Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/unreleased/typed-by-sweep.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- The nonstandard-notation pass no longer walks every expression of a document looking for a `typed by` on a body-expression parameter (#1478) when the document spells none: the parser counts the spellings and the pass sweeps only while one is unreported. Loading a generated 1 000-element model allocates 58.5 MB instead of 61.2 MB, recovering most of what #1478 added over 0.11.0's 58.2 MB on the same toolchain; the remaining 0.3 MB is other work.
10 changes: 9 additions & 1 deletion internal/check/passes/nonstandard_notation.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,12 @@ func (NonstandardNotationPass) Run(ctx *Context, name string, root *ast.RootName
w.bodies = append(w.bodies, bodyFrame{members: root.Members, namespaceish: true})
w.walk(root.Members)
w.bodies = w.bodies[:0]
w.typedByInExpressions(root)
// The member walk reaches every `typed by` but a body expression's; the
// document counts its spellings, so the expression sweep runs only when
// one is left to find.
if w.typedByReported < root.TypedBy {
w.typedByInExpressions(root)
}
// A comment where no member may start is the parser's own warning, which
// strict conformance escalates; the escalated finding replaces the warning.
if ctx.Options.Conformance.IsStrict() {
Expand Down Expand Up @@ -100,6 +105,8 @@ type notationWalker struct {
// inKerMLDeclaration counts the enclosing declarations already reported as
// KerML notation; their members move with them, so are not reported again.
inKerMLDeclaration int
// typedByReported counts the `typed by` findings, against RootNamespace.TypedBy.
typedByReported int
// keywordName holds the offsets where the parser recovered a keyword written as
// a name, the only spans keywordAsName escalates.
keywordName map[int]bool
Expand Down Expand Up @@ -765,6 +772,7 @@ func (w *notationWalker) typedByRelationships(rels []*ast.Relationship) {
// typedBy reports a typing spelled `typed by` in a SysML file (SysML.xtext:395 admits
// `:` and `defined by`; KerML.xtext:600 `typed by`); the fix rewrites the keyword alone.
func (w *notationWalker) typedBy(span source.Span) {
w.typedByReported++
w.diags = append(w.diags, diag.Diagnostic{
Severity: w.severity,
Span: span,
Expand Down
2 changes: 2 additions & 0 deletions internal/syntax/ast/astcodec/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,7 @@ func (e *Encoder) encodeFields(node ast.Node) {
e.base(&n.NodeBase)
e.nodes(n.Members)
e.operatorExprs(n.UndefinedOperators)
e.w.Int(int64(n.TypedBy))
case *ast.SelectExpr:
e.base(&n.NodeBase)
e.node(n.Operand)
Expand Down Expand Up @@ -1514,6 +1515,7 @@ func (d *Decoder) decodeFields(node ast.Node) {
d.base(&n.NodeBase)
n.Members = d.nodes()
n.UndefinedOperators = d.operatorExprs()
n.TypedBy = int(d.r.Int())
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
Resolved
case *ast.SelectExpr:
d.base(&n.NodeBase)
n.Operand = d.node()
Expand Down
3 changes: 3 additions & 0 deletions internal/syntax/ast/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,9 @@ type RootNamespace struct {
// UndefinedOperators are the `~` operator expressions of the document in
// source order: KerML leaves DataFunctions::'~' undefined, so a tool warns at each.
UndefinedOperators []*OperatorExpr
// TypedBy counts the typings the document spells `typed by` (KerML.xtext:600),
// on declarations, prefix metadata and body-expression parameters alike.
TypedBy int
}

// PrefixMetadata records a `# QualifiedName` metadata annotation reference.
Expand Down
1 change: 1 addition & 0 deletions internal/syntax/parser/defusage.go
Original file line number Diff line number Diff line change
Expand Up @@ -4260,6 +4260,7 @@ func (p *Parser) typedBySpan(clauseTok lexer.Token) source.Span {
if clauseTok.Kind != lexer.Keyword || clauseTok.KeywordID != "typed" {
return source.Span{}
}
p.typedByCount++
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
Resolved
return p.spanFrom(clauseTok.Span.Offset)
}

Expand Down
6 changes: 6 additions & 0 deletions internal/syntax/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ type Parser struct {
// undefinedOps are the `~` operator expressions the parse built, in source
// order; ParseFile hands them to the root.
undefinedOps []*ast.OperatorExpr
// typedByCount counts the typings spelled `typed by`, for RootNamespace.TypedBy.
typedByCount int

// calcBodyDepth counts the calculation bodies being parsed, so a `return`
// reached in a statement position inside one is read as the result
Expand Down Expand Up @@ -172,6 +174,7 @@ type parseCheckpoint struct {
diagnosticLen int
warningLen int
undefinedOpLen int
typedByCount int
memberStartLen int
commentBodyLen int
attachedLen int
Expand Down Expand Up @@ -599,6 +602,7 @@ func (p *Parser) ParseFile() *ast.RootNamespace {
}
root.NodeSpan = p.spanFrom(start)
root.UndefinedOperators = p.undefinedOps
root.TypedBy = p.typedByCount
p.reportMisplacedComments()
return root
}
Expand Down Expand Up @@ -661,6 +665,7 @@ func (p *Parser) checkpoint() parseCheckpoint {
diagnosticLen: len(p.Diagnostics),
warningLen: len(p.Warnings),
undefinedOpLen: len(p.undefinedOps),
typedByCount: p.typedByCount,
memberStartLen: len(p.memberStarts),
commentBodyLen: len(p.commentBodies),
attachedLen: len(p.attachedStarts),
Expand All @@ -682,6 +687,7 @@ func (p *Parser) restore(cp parseCheckpoint) {
p.Diagnostics = p.Diagnostics[:cp.diagnosticLen]
p.Warnings = p.Warnings[:cp.warningLen]
p.undefinedOps = p.undefinedOps[:cp.undefinedOpLen]
p.typedByCount = cp.typedByCount
p.memberStarts = p.memberStarts[:cp.memberStartLen]
p.commentBodies = p.commentBodies[:cp.commentBodyLen]
p.attachedStarts = p.attachedStarts[:cp.attachedLen]
Expand Down
36 changes: 36 additions & 0 deletions internal/syntax/parser/typed_by_count_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package parser

import (
"testing"

"github.com/Open-MBEE/OpenSysML/internal/syntax/source"
)

// The document counts its `typed by` spellings (RootNamespace.TypedBy), on
// declarations, prefix metadata and body-expression parameters alike, so a
// pass that reports the spelling knows when it has found them all.
func TestRootNamespaceCountsTypedBy(t *testing.T) {
tests := []struct {
name string
src string
want int
}{
{"none", "package P { part def A; part a : A; }", 0},
{"typed_as_name", "package P { attribute typed; }", 0},
{"declaration", "package P { part def A; part a typed by A; }", 1},
{"two_declarations", "package P { part def A; part a typed by A; part b typed by A, A; }", 2},
{"prefix_metadata", "package P { metadata def M; @ m typed by M; }", 1},
{"body_parameter", "package P { part def A; calc c { in x; return y = x.{ in v typed by A; v }; } }", 1},
{"end_without_cross_feature", "package P { part def A; connection def C { end x typed by A; end y typed by A; } }", 2},
{"kerml_feature", "package P { classifier A; feature f typed by A; }", 1},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := New(source.New(tt.name+".sysml", []byte(tt.src)))
root := p.ParseFile()
if root.TypedBy != tt.want {
t.Errorf("RootNamespace.TypedBy = %d, want %d", root.TypedBy, tt.want)
}
})
}
}
2 changes: 1 addition & 1 deletion internal/workspace/libs/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ var stdlibSnapshot []byte
// whenever the stream layout does, so a stale blob is refused, not misread.
const (
snapshotMagic = "OpenSysML library snapshot\n"
snapshotFormatVersion = 26
snapshotFormatVersion = 27
)

// snapshotCRC checksums the index stream, so a damaged byte that still parses
Expand Down
2 changes: 1 addition & 1 deletion internal/workspace/libs/snapshot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func TestSnapshotIndexMatchesFreshLoad(t *testing.T) {

// snapshotKindDigest pins the SymbolKind and PseudostateKind numbering the snapshot
// stream persists under snapshotFormatVersion; a kind added or moved renumbers the ones after it.
const snapshotKindDigest = "19f8c6091c4acf8b"
const snapshotKindDigest = "e7b3ced9fc89d152"

func TestSnapshotFormatVersionPinsSymbolKinds(t *testing.T) {
var b strings.Builder
Expand Down
Binary file modified internal/workspace/libs/stdlib.snapshot
Binary file not shown.
Loading