Deterministic standalone Go library for linting EARS requirement sentences.
This library only does:
- EARS shell parsing and pattern classification
- boolean clause parsing inside
While/When/Where/If - deterministic catalog matching
- machine-readable diagnostics
go get github.com/labeth/ears-lint-gofunc LintEars(text string, catalog Catalog, options *Options) LintResult
func LintEarsBatch(items [][2]string, catalog Catalog, options *Options) []LintResult
func LintCatalogCoverage(items [][2]string, catalog Catalog, options *Options) []Diagnostic
func LintCatalogCoverageFromResults(results []LintResult, catalog Catalog, options *Options) []DiagnosticLintEarsBatch input item format:
[2]string{ id, text }
type Options struct {
Mode Mode
CommaAsAnd bool
VagueTerms []string
}Defaults:
Mode:strictCommaAsAnd:falseVagueTerms:appropriate,sufficient,as needed
type Mode string
const (
ModeStrict Mode = "strict"
ModeGuided Mode = "guided"
)Mode behavior:
strict: structural parse failures and certain validation failures are errorsguided: structural parse failures are downgraded to warnings where applicable
type Catalog struct {
Systems []CatalogEntry
Actors []CatalogEntry
Events []CatalogEntry
States []CatalogEntry
Features []CatalogEntry
Modes []CatalogEntry
Conditions []CatalogEntry
DataTerms []CatalogEntry
}
type CatalogEntry struct {
ID string
Name string
Aliases []string
}Matching policy (deterministic only):
- exact canonical name
- exact alias
- ambiguous (multiple)
- unresolved (none)
No fuzzy or semantic matching is used.
type LintResult struct {
ID string
Valid bool
Pattern Pattern
AST *EarsAST
References []ReferenceMatch
Diagnostics []Diagnostic
}Valid is computed only from diagnostics severity:
falseif at least oneerrortrueotherwise
type Pattern string
const (
PatternUbiquitous Pattern = "ubiquitous"
PatternStateDriven Pattern = "state-driven"
PatternEventDriven Pattern = "event-driven"
PatternOptionalFeature Pattern = "optional-feature"
PatternUnwantedBehavior Pattern = "unwanted-behaviour"
PatternComplex Pattern = "complex"
)type EarsAST struct {
Pattern Pattern
Preconditions *ClauseExpr
Trigger *ClauseExpr
Feature *ClauseExpr
System TermMatch
Responses []string
Raw string
}type ClauseExpr struct {
Kind string // term | and | or | not | group | free-text
Span *Span
Term *TermMatch
Text string
Items []ClauseExpr
Item *ClauseExpr
}type TermMatch struct {
Raw string
Role TermRole
Matched *CatalogRef
Ambiguous []CatalogRef
Unresolved bool
ViaAlias bool
}type CatalogRef struct {
Group string
ID string
Name string
}type TermRole string
const (
RoleSystem TermRole = "system"
RoleActor TermRole = "actor"
RoleEvent TermRole = "event"
RoleState TermRole = "state"
RoleFeature TermRole = "feature"
RoleMode TermRole = "mode"
RoleCondition TermRole = "condition"
RoleDataTerm TermRole = "data-term"
)type ReferenceMatch struct {
Clause string // system | preconditions | trigger | feature
Text string
Role TermRole
Matched *CatalogRef
Ambiguous []CatalogRef
Unresolved bool
ViaAlias bool
Span *Span
}type Diagnostic struct {
Code string
Severity Severity
Message string
Span *Span
}
type Severity string
const (
SeverityError Severity = "error"
SeverityWarning Severity = "warning"
SeverityInfo Severity = "info"
)type Span struct {
Start int // 0-based start offset in input text
End int // 0-based exclusive end offset
}The <system> shall <response>While <expr>, the <system> shall <response>Where <expr>, the <system> shall <response>When <expr>, the <system> shall <response>If <expr>, then the <system> shall <response>- complex combinations from these shell clauses
Current accepted order:
While* -> Where* -> When* -> If* -> the <system> shall ...
Violations can produce ears.invalid_clause_order.
Inside clause bodies, parser supports:
andornot- parentheses
- comma as
andonly whenCommaAsAnd=true
Operator precedence:
not>and>or
Shell keywords are matched case-insensitively (while, WHEN, If, THEN all parse).
The response text after shall is split by semicolon (;) into Responses.
This is the full set of codes currently emitted by implementation.
ears.no_matchears.invalid_clause_orderears.missing_systemears.missing_shallears.multiple_shallears.invalid_if_then_form
expr.unbalanced_parenthesesexpr.invalid_operator_sequenceexpr.empty_subexpressionexpr.operator_precedence_warningexpr.mixed_unresolved_termsexpr.ambiguous_termexpr.unknown_term
Generated as:
catalog.<role>_unresolvedcatalog.<role>_ambiguouscatalog.term_unreferenced(from catalog coverage lint in strict mode)
Roles currently emitted from this parser pipeline:
systemstateeventfeature
Concrete examples:
catalog.system_unresolvedcatalog.system_ambiguouscatalog.state_unresolvedcatalog.state_ambiguouscatalog.event_unresolvedcatalog.event_ambiguouscatalog.feature_unresolvedcatalog.feature_ambiguous
lint.multiple_responseslint.vague_responselint.unparsed_taillint.alias_usedlint.suspicious_text_shape
severityByMode(strict) = errorseverityByMode(guided) = warning
Applied to structural errors and selected expression diagnostics.
Catalog severity notes:
- unresolved/ambiguous
systemuses mode severity - unresolved/ambiguous non-system clause terms are warnings
- No external calls
- No random behavior
- Diagnostics are stably sorted by span, code, message, severity
- Batch output order matches input order
package main
import (
"fmt"
earslint "github.com/labeth/ears-lint-go"
)
func main() {
catalog := earslint.Catalog{
Systems: []earslint.CatalogEntry{{ID: "SYS-ENGINE", Name: "engine control system"}},
States: []earslint.CatalogEntry{{ID: "STATE-GROUND", Name: "aircraft is on ground"}},
Events: []earslint.CatalogEntry{{ID: "EVT-REV", Name: "reverse thrust is commanded"}},
}
res := earslint.LintEars(
"While aircraft is on ground, when reverse thrust is commanded, the engine control system shall enable reverse thrust.",
catalog,
&earslint.Options{Mode: earslint.ModeStrict},
)
fmt.Println(res.Valid, res.Pattern, len(res.Diagnostics))
}Runnable example that prints raw API output as JSON:
- program:
examples/dump_batch_json/main.go - run:
go run ./examples/dump_batch_json > output.jsonMIT. See LICENSE.
See CONTRIBUTING.md.