API Documentation¶
Complete API reference for OpenSysML packages.
Overview¶
OpenSysML is organized into core packages under internal/core/, with frontends in internal/lsp/ and internal/repl/.
Package Organization:
github.com/Open-MBEE/OpenSysML
├── internal/core/ # Core language implementation
│ ├── source/ # Source files and position tracking
│ ├── lexer/ # Tokenization
│ ├── parser/ # Parsing to AST
│ ├── ast/ # Abstract Syntax Tree
│ ├── symbols/ # Symbol tables and scopes
│ ├── resolve/ # Name resolution
│ ├── semantics/ # Type system and semantic queries
│ ├── passes/ # Validation passes
│ ├── lower/ # AST → execution IR (ActionGraph/StateGraph)
│ ├── runtime/ # Execution runtime
│ ├── model/ # Workspace and document management
│ └── libs/ # Standard library handling
├── internal/lsp/ # Language Server Protocol
├── internal/grpc/ # gRPC service implementation
└── internal/repl/ # Interactive REPL
Core Packages¶
internal/core/source¶
Source file management and position tracking.
Key Types:
SourceFile— Represents a source file with content and line indexingName() string— File pathContent() []byte— Raw bytesLineCount() int— Number of lines-
Line(n int) string— Get line content -
Span— Position range in source (offset-based) Start, End int— Byte offsetsContains(pos int) boolOverlaps(other Span) bool
Usage:
src := source.New("example.sysml", []byte("part Wheel;"))
span := source.Span{Start: 0, End: 4} // "part"
internal/core/lexer¶
Tokenization of SysML v2 textual notation.
Key Types:
Lexer— Scanner for SysML v2 tokensNext() Token— Get next token-
Peek() Token— Look ahead without consuming -
Token— Single token with position Kind TokenKind— Token type (keyword, identifier, literal, operator)Span source.Span— Position in source-
Text string— Raw text -
TokenKind— Enum of all token types - Keywords:
KwPackage,KwPart,KwAttribute,KwAction, etc. (~200 keywords) - Literals:
LitInteger,LitReal,LitString,LitBool - Operators:
OpPlus,OpMinus,OpEq, etc. - Structure:
LBrace,RBrace,Semicolon,Comma, etc.
Usage:
lex := lexer.New(source.New("test", []byte("part Wheel { }")))
for tok := lex.Next(); tok.Kind != lexer.EOF; tok = lex.Next() {
fmt.Println(tok.Kind, tok.Text)
}
internal/core/parser¶
Recursive-descent parser producing AST.
Entry Points:
New(src *source.SourceFile) *Parser— Create parser(*Parser).ParseFile() *ast.RootNamespace— Parse complete file
Key Functions:
parseDefinition()— Parse def (part, attribute, action, etc.)parseUsage()— Parse usageparseExpression()— Parse expressionsparseActionBody()— Parse behavioral action body (Phase C3)
Error Recovery:
Parser always produces a complete tree. Errors result in ast.ErrorNode placeholders.
Usage:
p := parser.New(source.New("test.sysml", content))
root := p.ParseFile()
// root is always non-nil, check root.Errors for diagnostics
internal/core/ast¶
Abstract Syntax Tree nodes (syntax-only, immutable).
Node Interface:
All AST nodes implement:
Key Types:
Namespace & Elements:
- RootNamespace — Top-level (one per file)
- Package — Package declaration
- Import — Import statement
Definitions & Usages:
- Definition — Base for all defs (part, attribute, action, etc.)
- .Kind DefinitionKind — Type of definition
- .Ident *Identifier — Name
- .Members []Node — Body contents
- .Specializations []*QualifiedName — Generalization edges
- .Visibility VisibilityKind
Usage— Base for all usages.Kind UsageKind.Ident *Identifier.Members []Node.Multiplicity *MultiplicityExpr.Value Node— Default value expression
Expressions:
- LiteralInteger, LiteralReal, LiteralBool, LiteralString
- FeatureReference — Reference to feature by name
- FeatureChainExpr — Dot notation (x.y.z)
- UnaryExpr, BinaryExpr, ConditionalExpr
- InvocationExpr — Function/calc invocation
- SequenceExpr, CollectExpr, SelectExpr — Collection operations
Behavioral Nodes (Phase C3):
- InitialNode, FinalNode — Control flow start/end
- ForkNode, JoinNode — Concurrency
- MergeNode, DecisionNode — Branching
- ActionExecutionNode — Action invocation
- SuccessionEdge — Flow between nodes
- .Source, .Target *QualifiedName
- .Guard Node — Optional guard expression
Architecture Rule: AST is immutable after parsing. All semantic information lives in side tables.
internal/core/symbols¶
Symbol tables and scope trees.
Key Types:
Symbol— Represents a declared nameName string— IdentifierKind SymbolKind— Type of symbolDecl ast.Node— AST node that declares itScope *Scope— Child scope (if compound)OwnerScope *Scope— Parent scope-
Visibility ast.VisibilityKind -
Scope— Lexical scope Parent() *ScopeNode() ast.NodeChildren() []*ScopeLookupLocal(name string) (*Symbol, bool)— Local lookup onlyLookupLocalAll(name string) []*Symbol— All with that name (short+primary)-
MemberNames() []string— All declared names in order -
Index— Global symbol index DocumentRoot(name string) *Scope— Get document root scope
Usage:
idx := symbols.NewIndex()
idx.AddDocument("example.sysml", root) // root is *ast.RootNamespace
scope := idx.DocumentRoot("example.sysml")
sym, ok := scope.LookupLocal("Wheel")
Important: Short name + primary name alias the same *Symbol. Dedupe by pointer when iterating.
internal/core/resolve¶
Name resolution (lazy, memoized).
Key Types:
Resolver— Name resolverResolveQualified(scope *symbols.Scope, qn *ast.QualifiedName) (*symbols.Symbol, bool)ResolveUnqualified(scope *symbols.Scope, name string) (*symbols.Symbol, bool)ResolveImport(importNode *ast.Import) []*symbols.Symbol
Usage:
Results are memoized internally.
internal/core/semantics¶
Type system, conformance, semantic queries.
Key Type: Model
Central semantic query engine. Built from resolver:
Methods:
Type relationships:
- DirectSupertypes(sym *symbols.Symbol) []*symbols.Symbol — Immediate generalizations
- AllSupertypes(sym *symbols.Symbol) []*symbols.Symbol — Transitive closure
- Conforms(a, b *symbols.Symbol) bool — Type conformance check
- HasSpecializationCycle(sym *symbols.Symbol) bool — Cycle detection
Members:
- MembersOf(sym *symbols.Symbol) []*symbols.Symbol — All members (local + inherited with masking)
- LookupMember(sym *symbols.Symbol, name string) (*symbols.Symbol, bool)
Multiplicity:
- MultiplicityOf(sym *symbols.Symbol) (Range, bool) — Extract multiplicity bounds
- Range{Lower, Upper Bound}
- Bound{Value int64, Infinite bool, Known bool}
Constant evaluation:
- Eval(n ast.Node) (Value, bool) — Constant-folder for literals and operators
- Value{Kind ValueKind, Int int64, Real float64, Bool bool}
- ValueKind ∈ {ValInt, ValReal, ValBool, ValInfinity, ValInvalid}
Note: Eval() is a constant-folder only. For full runtime evaluation, see internal/core/runtime.
internal/core/passes¶
Pluggable validation passes.
Architecture:
Validation runs in tiers: 1. Syntax — Checks for ErrorNodes 2. Name Resolution — Validates all names resolve 3. Type Checking — Type conformance 4. Constraints — Deep semantic rules
Higher tiers skip if lower tier fails.
Key Types:
Pass— Validation pass interfaceLevel() PassLevel— Which tier-
Run(ctx *Context, name string, root *ast.RootNamespace) []Diagnostic -
Context— Provides access to resolver and model Resolver() *resolve.Resolver-
Model() *semantics.Model -
Diagnostic— Error/warning Level DiagnosticLevel— Error, Warning, InfoSpan source.SpanMessage string
Usage:
// Analyze runs the default pass registry internally.
diagnostics := passes.Analyze("example.sysml", root, parseDiags, idx)
internal/core/runtime¶
Execution runtime (Tiers 1-5: instances, expressions, behaviors).
Key Types:
Value System:
Value— Runtime valueKind ValueKind— Type tagConst semantics.Value— Integer/real/bool (for ValConst)-
Str string,Instance int64,Sequence *Sequence,Set *Set -
ValueKind— Enum ValConst— Integer/real/bool (stored in Const field)ValNull,ValString,ValInstance,ValSequence,ValSet
Instance Model:
Instance— Runtime instanceID int64— Unique instance IDType *symbols.Symbol— Type symbolFeatureValues map[string]*FeatureValue— Feature values by feature name
Execution Context:
Context— Runtime execution contextInstantiate(sym *symbols.Symbol) (*Instance, error)— Create instanceEval(expr ast.Node, env map[*symbols.Symbol]Value) (Value, error)— Evaluate expressionInvokeCalc(sym *symbols.Symbol, args []Value, scope *symbols.Scope) (Value, error)— Invoke calculationEvaluateConstraint(sym *symbols.Symbol, scope *symbols.Scope) (bool, error)— Evaluate constraintEvaluateRequirement(sym *symbols.Symbol, scope *symbols.Scope) (bool, error)— Evaluate requirementExecuteAction(sym *symbols.Symbol) (map[string]Value, error)— Execute action to completionExecuteState(sym *symbols.Symbol) (map[string]Value, error)— Execute state machine until final/suspendedCreateActionExecutor(sym *symbols.Symbol) (*ActionExecutor, error)— Create action executor for debuggingCreateStateExecutor(sym *symbols.Symbol) (*StateExecutor, error)— Create state executor for debugging
Behavioral Execution (Tier 5):
Token— Control token for action execution, carrying no values of its ownID int64— Unique token ID-
Location ast.Node— Current node (InitialNode, ActionExecutionNode, etc.) -
ActionExecutor— Petri-net token-flow execution engine Step() error— Advance all tokens one stepRunToCompletion() error— Execute until StateCompleted (max 10k steps)Tokens() []Token— Get active tokens (copy)State() ExecutionState— Current execution state (Ready/Running/Completed/Suspended)Results() map[string]Value— Get results after completionData() map[string]Value— The action's live feature space, shared by every tokenSetBreakpoint(nodeName string)— Set breakpoint on nodeClearBreakpoints()— Clear all breakpoints-
ActionSymbol() *symbols.Symbol— Get action symbol -
StateExecutor— Event-driven state machine execution ProcessNextEvent() error— Process next event from queueCurrentState() ast.Node— Get current StateNodeStateStack() []*ast.StateNode— Get active configuration (hierarchical states)StateData() map[string]Value— Get state machine variablesEventQueue() *EventQueue— Get event queueCurrentTime() float64— Get simulation timeState() ExecutionState— Get execution state-
StateMachineSymbol() *symbols.Symbol— Get state machine symbol -
ExecutionState— Enum StateReady— Initialized, not startedStateRunning— ExecutingStateCompleted— Finished (final node/state reached)-
StateSuspended— Paused (waiting for events) -
Event— State machine event ID int64— Unique event IDType EventType— Time/Change/Accept/CallTimestamp float64— Event timestamp (for TimeEvent)Payload map[string]Value— Event data
Built-in Functions:
Registered KerML builtins:
- Arithmetic: +, -, *, /, %, **
- Comparison: ==, !=, <, >, <=, >=
- Boolean: and, or, xor, not, implies
- Collections: size, isEmpty, ->select, ->collect
- String: + (concat), size, substring
Usage:
Tier 1-3 (Instances & Expressions):
// Honour the SYSML_MAX_* budgets instead of the defaults with:
// budgets, err := runtime.BudgetsFromEnv()
// err = ctx.SetBudgets(budgets)
ctx := runtime.NewContext(model, resolver, runtime.DefaultMaxSteps)
inst, _ := ctx.Instantiate(wheelSym)
fv, _ := inst.GetFeatureValue(ctx, "diameter")
result, _ := ctx.InvokeCalc(addSym, []Value{v1, v2}, scope)
Tier 5 (Actions):
// Execute action to completion
results, err := ctx.ExecuteAction(myActionSym)
if err != nil { /* handle error */ }
result := results["result"]
// Or debug step-by-step
exec, _ := ctx.CreateActionExecutor(myActionSym)
exec.Initialize()
for exec.State() != StateCompleted {
exec.Step()
tokens := exec.Tokens()
// inspect tokens
}
Tier 5 (State Machines):
// Execute state machine
stateData, err := ctx.ExecuteState(stateMachineSym)
if err != nil { /* handle error */ }
// Or debug with events
exec, _ := ctx.CreateStateExecutor(stateMachineSym)
exec.Initialize()
for exec.State() != StateCompleted {
exec.ProcessNextEvent()
fmt.Printf("State: %s, Time: %f\n", exec.CurrentState(), exec.CurrentTime())
}
internal/core/model¶
Workspace and document management.
Key Types:
Workspace— Multi-file workspaceAddDocument(name string, src *source.SourceFile) *DocumentGetDocument(name string) (*Document, bool)RemoveDocument(name string)Index() *symbols.Index— Global symbol index-
Diagnostics(name string) []passes.Diagnostic -
Document— Single source file Name() stringSource() *source.SourceFileRoot() *ast.RootNamespaceScope() *symbols.ScopeVersion() int— Increments on update
Usage:
ws := model.NewWorkspace()
doc := ws.AddDocument("example.sysml", src)
diagnostics := ws.Diagnostics("example.sysml")
internal/core/libs¶
Standard library bundling and caching.
Key Functions:
Load(name string) (*source.SourceFile, error)— Load stdlib fileListFiles() []string— All stdlib files
Standard library is embedded in the binary using Go embed.FS.
Frontend Packages¶
internal/lsp¶
Language Server Protocol implementation.
Key Type:
Server— LSP serverRun(ctx context.Context, conn io.ReadWriteCloser) error
Transport: stdio only. sysml-lsp also accepts --stdio/-stdio as an explicit no-op, because
standard language clients (including the bundled VS Code extension) name the transport on the command
line. Any other unknown flag is still rejected with exit status 2.
Lifecycle (LSP 3.17): shutdown is answered, after which every request other than exit is answered
InvalidRequest (-32600) and non-exit notifications are dropped. exit makes Run return and the
process terminate — status 0 after a preceding shutdown, 1 otherwise.
Current Capabilities: - Document synchronization (open/change/close) - Diagnostics (syntax + semantic errors) - Hover (type info) - Go to definition - References - Document symbols - Workspace symbols - Completion
Usage:
ws := model.NewWorkspace()
srv := lsp.NewServer(ws)
srv.Run(ctx, stdio{}) // stdio implements io.ReadWriteCloser
internal/repl¶
Interactive REPL implementation.
Key Types:
Session— REPL session state- Accumulates declarations across inputs
- Tracks runtime context and instances
Entry Point:
Loop(reader LineReader, out io.Writer, session *Session) error
LineReader Interface:
Meta Commands:
- %help, %list, %clear, %load <file>
- %search <substring> — List the declared and library symbols whose qualified name contains the substring, with the kind of each
- %builtins — List the library functions the runtime implements directly
- %instantiate <name>, %features <name>, %instances
- %eval <expr>
- %calc <name> [args...] — Invoke calculation with arguments
- %constraint <name> — Evaluate constraint
- %requirement <name> — Evaluate requirement
- %satisfy [name] — Evaluate satisfaction assertions (assert satisfy <requirement> by <part>;), every one in the model or the ones a named element states
Usage:
SysML v2 API & Services Query¶
The gRPC service implements the query surface the SysML v2 API & Services
standard defines, so a client that speaks that API — the
SysML-v2-API-Java-Client,
the SysML v2 API Cookbook notebooks, MATLAB System Composer's executeQuery —
can filter a model OpenSysML parsed. The standard's schema is authoritative:
api/openapi.yaml in the Java client, components Query, Constraint,
PrimitiveConstraint, CompositeConstraint.
Implementation: internal/grpc/query.go (Service.Query), reported from
GetServerInfo as the query capability. Python: model.query(...)
(python/opensysml/query.py).
The query model¶
Query scope[] elements to consider; empty is the whole loaded model
select[] properties to report; empty reports every one
where one Constraint; absent selects the whole scope
Constraint PrimitiveConstraint | CompositeConstraint (a protobuf oneof)
PrimitiveConstraint property, operator (= > <), value[], inverse
CompositeConstraint operator (and | or), constraint[] (nests arbitrarily)
The RPC takes the same shape, with the standard's JSON names preserved, so translation from the standard's JSON is mechanical:
A cookbook payload, sent verbatim through the Python client:
model = opensysml.load("examples/vehicle.sysml")
model.query({"@type": "Query", "where": {
"@type": "PrimitiveConstraint",
"operator": "=", "property": "@type", "value": ["PartUsage"]}})
Each answered element is @id (its qualified name), @type and the selected
properties it has. A property an element does not have is absent, not empty.
An element with no qualified identity — an unnamed doc, an anonymous usage, an
anonymous connect — is not answered: its qualified name has an empty
segment (Demo::), so it is neither unique nor a name a scope could use. The
standard identifies an element by @id, and such an element has none
(TestQueryOmitsElementsWithNoQualifiedIdentity).
Neither is one declared inside an action body — a branch of an if, a loop body —
since the body is owned by no element and so names its declarations only locally
(step, not Demo::Drive::step): that name identifies no element and could not
be used as a scope (TestQueryOmitsBodyLocalDeclarations). An answered @id is
always a qualified name that the model resolves back to that element.
Queryable properties¶
The set is closed and is the single source of truth in
queryProperties (internal/grpc/query.go). A property outside it is an
INVALID_ARGUMENT error listing the ones that exist — never a silently empty
answer.
| Property | Reports | Ordered |
|---|---|---|
@id |
The element's qualified name, which is also how scope names it |
|
qualifiedName |
Same as @id |
|
@type |
The element's metamodel type (table below) | |
name |
The element's own name, the last segment of its qualified name | |
declaredName |
name, absent when the name is an effective name borrowed from a referenced feature |
|
owner |
Qualified name of the owning element; absent for a top-level element, whose owner is the document root | |
isAbstract |
true/false for a definition or usage; absent for anything else, and for a standard-library element restored from cache, which carries no declaration |
|
type |
Qualified name of the resolved type of a typed feature; absent when untyped or unresolved | |
multiplicityLower |
Declared lower bound | ✅ |
multiplicityUpper |
Declared upper bound, * when unbounded |
✅ |
@type — symbol kind → metamodel type¶
Mapping OpenSysML's symbol kinds onto the standard's metamodel type names is the
substantive design decision here; metamodelTypeNames
(internal/grpc/query.go) is the single source of truth, and
TestMetamodelTypeNameCoversEveryKind keeps it total over every kind a parsed
declaration can have. A standard-library element restored from cache may carry no
kind at all, and then reports no @type: it is answered, but never matches a
@type = comparison (and is kept by the inverse of one).
| OpenSysML kind | @type |
|---|---|
package, namespace |
Package, Namespace |
partDef / partUsage |
PartDefinition / PartUsage |
attributeDef / attributeUsage |
AttributeDefinition / AttributeUsage |
itemDef / itemUsage |
ItemDefinition / ItemUsage |
occurrenceDef / occurrenceUsage |
OccurrenceDefinition / OccurrenceUsage |
portDef / portUsage |
PortDefinition / PortUsage |
interfaceDef / interfaceUsage |
InterfaceDefinition / InterfaceUsage |
connectionDef / connectionUsage |
ConnectionDefinition / ConnectionUsage |
flowDef / flowUsage, allocationDef / allocationUsage |
FlowDefinition / FlowUsage, AllocationDefinition / AllocationUsage |
actionDef / actionUsage, stateDef / stateUsage |
ActionDefinition / ActionUsage, StateDefinition / StateUsage |
calcDef / calcUsage |
CalculationDefinition / CalculationUsage |
constraintDef / constraintUsage, requirementDef / requirementUsage |
ConstraintDefinition / ConstraintUsage, RequirementDefinition / RequirementUsage |
caseDef / caseUsage and the analysis / verification / use-case forms |
CaseDefinition / CaseUsage, AnalysisCase…, VerificationCase…, UseCase… |
viewDef, viewpointDef, renderingDef, concernDef and their usages |
ViewDefinition, ViewpointDefinition, RenderingDefinition, ConcernDefinition and …Usage |
enumerationDef / enumerationUsage, metadataDef / metadataUsage, metaclass |
EnumerationDefinition / EnumerationUsage, MetadataDefinition / MetadataUsage, Metaclass |
comment, documentation, textualRepresentation, dependency |
Comment, Documentation, TextualRepresentation, Dependency |
Three kinds have no distinct metamodel type, and report the closest one that exists — documented as approximations rather than hidden:
| OpenSysML kind | @type |
Why |
|---|---|---|
individualDef / individualUsage |
OccurrenceDefinition / OccurrenceUsage |
An individual is an occurrence with isIndividual set, not a type of its own |
connectorEnd |
Feature |
A connector end is a Feature with isEnd set |
alias |
Membership |
An alias is a named membership, not an element |
Comparison semantics¶
Where the standard is vague, these are the choices this implementation makes:
=compares the property's value as text, and matches if it equals any of the listed values. The standard writes one value and its clients write a list for@type; one value is the degenerate case of the same rule.>and<compare numbers, and require exactly one operand and an ordered property (themultiplicity*pair). A non-ordered property, more than one operand, or an operand that is not a number is anINVALID_ARGUMENTerror, not a false verdict.*parses as infinity, somultiplicityUpper > 1holds for an unbounded feature.- An element that simply has no value for the property fails the comparison — that is a fact about the element, not a fault in the query.
inversenegates the verdict of its own constraint, so a constraint and its inverse partition the scope.and/orcombine nested verdicts, short-circuiting once one is decisive.- The whole
wheretree is judged before any element is read, so a fault in it — an unknown property, a missing operator, an empty composite,>on an unordered property — is reported whatever the scope holds, including a model that declares nothing (TestQueryFaultIsReportedWithNoElementsToConsider), and wherever it sits, including under an already-decisive sibling. scopeconsiders each named element and everything nested inside it, in declaration order, parents first; a name the model does not have is an error. An empty scope enumerates the parsed document (not the standard library, which is reachable by naming a library element as a scope).
Not supported — by design of the standard¶
The standard's query model is deliberately weak, and this is an interop surface, not OpenSysML's expressive query story:
- No graph traversal and no transitive closure. There is no "all elements
under X", no "everything that specializes Y", no path expressions and no joins.
Containment is expressible only as a
scope; specialization is not expressible at all, even thoughsemantics.Modelcan answer it. - No
owningProject/@idon the query resource (single-model service, no project or commit store), no derived or computed properties, and no ordering or paging of results — elements come back in declaration order.
Usage Examples¶
Parse a File¶
import (
"github.com/Open-MBEE/OpenSysML/internal/core/source"
"github.com/Open-MBEE/OpenSysML/internal/core/parser"
)
src := source.New("example.sysml", []byte(`
part Wheel {
attribute diameter : Real;
}
`))
p := parser.New(src)
root := p.ParseFile()
// root is always non-nil, check root.Errors for parse errors
Build Symbol Table¶
import (
"github.com/Open-MBEE/OpenSysML/internal/core/symbols"
)
idx := symbols.NewIndex()
idx.AddDocument("example.sysml", root)
scope := idx.DocumentRoot("example.sysml")
sym, ok := scope.LookupLocal("Wheel")
Resolve Names¶
import (
"github.com/Open-MBEE/OpenSysML/internal/core/resolve"
)
res := resolve.New(idx)
sym, ok := res.ResolveQualified(scope, qualifiedName)
Type Queries¶
import (
"github.com/Open-MBEE/OpenSysML/internal/core/semantics"
)
model := semantics.NewModel(res)
members := model.MembersOf(wheelSym)
conforms := model.Conforms(wheelSym, vehiclePartSym)
Run Validation¶
import (
"github.com/Open-MBEE/OpenSysML/internal/core/passes"
)
// Analyze wires up the default pass registry and context internally.
diagnostics := passes.Analyze("example.sysml", root, parseDiags, idx)
Execute Runtime¶
import (
"github.com/Open-MBEE/OpenSysML/internal/core/runtime"
)
rtCtx := runtime.NewContext(model, resolver, runtime.DefaultMaxSteps)
inst, _ := rtCtx.Instantiate(wheelSym)
diameter, _ := inst.GetFeatureValue(rtCtx, "diameter")
fmt.Println(diameter.Value) // Value{Kind: ValConst, Real: 16.0}
Architecture Principles¶
1. Immutable AST¶
AST nodes are syntax-only and immutable after parsing. All semantic information (types, resolved references, instance values) lives in side tables keyed by ast.Node or *symbols.Symbol.
2. Lazy & Memoized¶
- Name resolution: computed on-demand, cached
- Semantic queries: computed on-demand, cached
- Passes: run only when diagnostics requested
3. Incremental Analysis¶
Documents can be updated individually. Symbol index and caches invalidate only affected documents.
4. Separation of Concerns¶
Source → Lexer → Parser → AST
↓
Symbols (side table)
↓
Resolve (side table)
↓
Semantics (side table)
↓
Passes (diagnostics)
↓
Runtime (values, instances)
Each layer is independent and testable.
Testing¶
All packages have comprehensive test coverage:
go test ./internal/core/parser # Parser tests
go test ./internal/core/symbols # Symbol table tests
go test ./internal/core/semantics # Semantic tests
go test ./internal/core/runtime # Runtime tests
go test ./... # All tests
Test fixtures in testdata/*.sysml.
Further Reading¶
- ARCHITECTURE.md — System architecture and design decisions
- the guide — Getting started guide
- OMG SysML v2.1 Beta 1 Spec — Language specification (2026-05 release)