From c3f4d8f465fa700a17ebf95222dfdedb1985c4e2 Mon Sep 17 00:00:00 2001 From: Martino Ferrari Date: Fri, 30 Jan 2026 01:01:47 +0100 Subject: [PATCH] Variable reference from $VAR to @VAR to avoid object conflict --- docs/CONFIGURATION_GUIDE.md | 6 +++--- internal/builder/builder.go | 2 +- internal/index/index.go | 2 +- internal/lsp/server.go | 2 +- internal/parser/lexer.go | 14 ++++++++++++++ internal/parser/parser.go | 2 +- internal/validator/validator.go | 6 +++--- test/formatter_variables_test.go | 13 ++++++------- test/lsp_app_test_repro_test.go | 4 ++-- test/lsp_binary_test.go | 2 +- test/lsp_diagnostics_app_test.go | 8 ++++---- test/lsp_hover_variable_test.go | 6 +++--- test/lsp_variable_refs_test.go | 8 ++++---- test/operators_test.go | 6 +++--- test/regex_variable_test.go | 2 +- test/validator_variable_usage_test.go | 4 ++-- test/variables_test.go | 4 ++-- 17 files changed, 52 insertions(+), 39 deletions(-) diff --git a/docs/CONFIGURATION_GUIDE.md b/docs/CONFIGURATION_GUIDE.md index 859651c..0ddc6ce 100644 --- a/docs/CONFIGURATION_GUIDE.md +++ b/docs/CONFIGURATION_GUIDE.md @@ -173,10 +173,10 @@ You can define variables using `#var`. The type expression supports CUE syntax. ``` ### Usage -Reference a variable using `$`: +Reference a variable using `@`: ```marte -Field = $MyVar +Field = @MyVar ``` ### Expressions @@ -187,7 +187,7 @@ You can use operators in field values. Supported operators: ```marte Field1 = 10 + 20 * 2 // 50 Field2 = "Hello " .. "World" -Field3 = $MyVar + 5 +Field3 = @MyVar + 5 ``` ### Build Override diff --git a/internal/builder/builder.go b/internal/builder/builder.go index 4435845..7164900 100644 --- a/internal/builder/builder.go +++ b/internal/builder/builder.go @@ -235,7 +235,7 @@ func (b *Builder) collectVariables(tree *index.ProjectTree) { func (b *Builder) evaluate(val parser.Value) parser.Value { switch v := val.(type) { case *parser.VariableReferenceValue: - name := strings.TrimPrefix(v.Name, "$") + name := strings.TrimPrefix(v.Name, "@") if res, ok := b.variables[name]; ok { return b.evaluate(res) } diff --git a/internal/index/index.go b/internal/index/index.go index ed2faa0..1c237ba 100644 --- a/internal/index/index.go +++ b/internal/index/index.go @@ -400,7 +400,7 @@ func (pt *ProjectTree) indexValue(file string, val parser.Value) { }) case *parser.VariableReferenceValue: pt.References = append(pt.References, Reference{ - Name: strings.TrimPrefix(v.Name, "$"), + Name: strings.TrimPrefix(v.Name, "@"), Position: v.Position, File: file, IsVariable: true, diff --git a/internal/lsp/server.go b/internal/lsp/server.go index dc7c706..e25ce5b 100644 --- a/internal/lsp/server.go +++ b/internal/lsp/server.go @@ -611,7 +611,7 @@ func HandleHover(params HoverParams) *Hover { } else if res.Reference.TargetVariable != nil { v := res.Reference.TargetVariable targetName = v.Name - fullInfo = fmt.Sprintf("**Variable**: `%s`\nType: `%s`", v.Name, v.TypeExpr) + fullInfo = fmt.Sprintf("**Variable**: `@%s`\nType: `%s`", v.Name, v.TypeExpr) if v.DefaultValue != nil { fullInfo += fmt.Sprintf("\nDefault: `%s`", valueToString(v.DefaultValue)) } diff --git a/internal/parser/lexer.go b/internal/parser/lexer.go index 313bc9e..2bbead7 100644 --- a/internal/parser/lexer.go +++ b/internal/parser/lexer.go @@ -36,6 +36,7 @@ const ( TokenCaret TokenAmpersand TokenConcat + TokenVariableReference ) type Token struct { @@ -184,6 +185,8 @@ func (l *Lexer) NextToken() Token { return l.lexString() case '#': return l.lexHashIdentifier() + case '@': + return l.lexVariableReference() case '$': return l.lexObjectIdentifier() } @@ -311,3 +314,14 @@ func (l *Lexer) lexHashIdentifier() Token { } return l.emit(TokenIdentifier) } + +func (l *Lexer) lexVariableReference() Token { + for { + r := l.next() + if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' || r == '-' { + continue + } + l.backup() + return l.emit(TokenVariableReference) + } +} diff --git a/internal/parser/parser.go b/internal/parser/parser.go index afbbe88..69e6fa4 100644 --- a/internal/parser/parser.go +++ b/internal/parser/parser.go @@ -297,7 +297,7 @@ func (p *Parser) parseAtom() (Value, bool) { true case TokenIdentifier: return &ReferenceValue{Position: tok.Position, Value: tok.Value}, true - case TokenObjectIdentifier: + case TokenVariableReference: return &VariableReferenceValue{Position: tok.Position, Name: tok.Value}, true case TokenLBrace: arr := &ArrayValue{Position: tok.Position} diff --git a/internal/validator/validator.go b/internal/validator/validator.go index ad284a3..6f8741b 100644 --- a/internal/validator/validator.go +++ b/internal/validator/validator.go @@ -223,7 +223,7 @@ func (v *Validator) valueToInterface(val parser.Value, ctx *index.ProjectNode) i case *parser.ReferenceValue: return t.Value case *parser.VariableReferenceValue: - name := strings.TrimPrefix(t.Name, "$") + name := strings.TrimPrefix(t.Name, "@") if info := v.Tree.ResolveVariable(ctx, name); info != nil { if info.Def.DefaultValue != nil { return v.valueToInterface(info.Def.DefaultValue, ctx) @@ -525,7 +525,7 @@ func (v *Validator) getFieldValue(f *parser.Field, ctx *index.ProjectNode) strin case *parser.BoolValue: return strconv.FormatBool(val.Value) case *parser.VariableReferenceValue: - name := strings.TrimPrefix(val.Name, "$") + name := strings.TrimPrefix(val.Name, "@") if info := v.Tree.ResolveVariable(ctx, name); info != nil { if info.Def.DefaultValue != nil { return v.getFieldValue(&parser.Field{Value: info.Def.DefaultValue}, ctx) @@ -1126,7 +1126,7 @@ func (v *Validator) CheckVariables() { if ref.IsVariable && ref.TargetVariable == nil { v.Diagnostics = append(v.Diagnostics, Diagnostic{ Level: LevelError, - Message: fmt.Sprintf("Unresolved variable reference: '$%s'", ref.Name), + Message: fmt.Sprintf("Unresolved variable reference: '@%s'", ref.Name), Position: ref.Position, File: ref.File, }) diff --git a/test/formatter_variables_test.go b/test/formatter_variables_test.go index 76d7d5d..a8379bf 100644 --- a/test/formatter_variables_test.go +++ b/test/formatter_variables_test.go @@ -15,8 +15,8 @@ func TestFormatterVariables(t *testing.T) { #var MyStr: string | "A" = "default" +Obj = { - Field1 = $MyInt - Field2 = $MyStr + Field1 = @MyInt + Field2 = @MyStr } ` p := parser.NewParser(content) @@ -35,11 +35,10 @@ func TestFormatterVariables(t *testing.T) { t.Errorf("Variable MyInt formatted incorrectly. Got:\n%s", output) } // Note: parser adds space after each token in TypeExpr - // string | "A" -> "string | \"A\"" + // string | "A" -> "string | \"A\"" if !strings.Contains(output, "#var MyStr: string | \"A\" = \"default\"") { t.Errorf("Variable MyStr formatted incorrectly. Got:\n%s", output) } - if !strings.Contains(output, "Field1 = $MyInt") { - t.Errorf("Variable reference $MyInt formatted incorrectly. Got:\n%s", output) - } -} + if !strings.Contains(output, "Field1 = @MyInt") { + t.Errorf("Variable reference @MyInt formatted incorrectly. Got:\n%s", output) + }} diff --git a/test/lsp_app_test_repro_test.go b/test/lsp_app_test_repro_test.go index ca9bf66..8eca44a 100644 --- a/test/lsp_app_test_repro_test.go +++ b/test/lsp_app_test_repro_test.go @@ -38,7 +38,7 @@ func TestLSPAppTestRepro(t *testing.T) { A = { DataSource = DDB Type = uint32 - Value = $Value + Value = @Value } } OutputSignals = { @@ -75,7 +75,7 @@ func TestLSPAppTestRepro(t *testing.T) { output := buf.String() // Check Unresolved Variable - if !strings.Contains(output, "Unresolved variable reference: '$Value'") { + if !strings.Contains(output, "Unresolved variable reference: '@Value'") { t.Error("LSP missing unresolved variable error") } diff --git a/test/lsp_binary_test.go b/test/lsp_binary_test.go index a44a258..e304aa6 100644 --- a/test/lsp_binary_test.go +++ b/test/lsp_binary_test.go @@ -151,7 +151,7 @@ diags := params["diagnostics"].([]interface{}) foundOrdering = true t.Log("Found Ordering error") } - if strings.Contains(m, "Unresolved variable reference: '$Value'") { + if strings.Contains(m, "Unresolved variable reference: '@Value'") { foundVariable = true t.Log("Found Variable error") } diff --git a/test/lsp_diagnostics_app_test.go b/test/lsp_diagnostics_app_test.go index cead575..677092d 100644 --- a/test/lsp_diagnostics_app_test.go +++ b/test/lsp_diagnostics_app_test.go @@ -41,7 +41,7 @@ func TestLSPDiagnosticsAppTest(t *testing.T) { A = { DataSource = DDB Type = uint32 - Value = $Value + Value = @Value } } OutputSignals = { @@ -87,9 +87,9 @@ func TestLSPDiagnosticsAppTest(t *testing.T) { t.Fatal("LSP did not publish diagnostics") } - // 1. Check Unresolved Variable Error ($Value) - if !strings.Contains(output, "Unresolved variable reference: '$Value'") { - t.Error("Missing diagnostic for unresolved variable '$Value'") + // 1. Check Unresolved Variable Error (@Value) + if !strings.Contains(output, "Unresolved variable reference: '@Value'") { + t.Error("Missing diagnostic for unresolved variable '@Value'") } // 2. Check INOUT Ordering Error (Signal A consumed but not produced) diff --git a/test/lsp_hover_variable_test.go b/test/lsp_hover_variable_test.go index 1b35646..944ee1f 100644 --- a/test/lsp_hover_variable_test.go +++ b/test/lsp_hover_variable_test.go @@ -16,7 +16,7 @@ func TestLSPHoverVariable(t *testing.T) { content := ` #var MyInt: int = 123 +Obj = { - Field = $MyInt + Field = @MyInt } ` uri := "file://hover_var.marte" @@ -47,8 +47,8 @@ func TestLSPHoverVariable(t *testing.T) { t.Errorf("Hover def missing default value. Got: %s", contentDef) } - // 2. Hover on Reference ($MyInt) - // Line 4 (index 3). $MyInt is at col 12. + // 2. Hover on Reference (@MyInt) + // Line 4 (index 3). @MyInt is at col 12. paramsRef := lsp.HoverParams{ TextDocument: lsp.TextDocumentIdentifier{URI: uri}, Position: lsp.Position{Line: 3, Character: 12}, diff --git a/test/lsp_variable_refs_test.go b/test/lsp_variable_refs_test.go index fa44975..3b8b3c1 100644 --- a/test/lsp_variable_refs_test.go +++ b/test/lsp_variable_refs_test.go @@ -15,7 +15,7 @@ func TestLSPVariableRefs(t *testing.T) { content := ` #var MyVar: int = 1 +Obj = { - Field = $MyVar + Field = @MyVar } ` uri := "file://vars.marte" @@ -29,11 +29,11 @@ func TestLSPVariableRefs(t *testing.T) { lsp.Tree.ResolveReferences() // 1. Definition from Usage - // Line 4: " Field = $MyVar" - // $ is at col 12 (0-based) ? + // Line 4: " Field = @MyVar" + // @ is at col 12 (0-based) ? // " Field = " is 4 + 6 + 3 = 13 chars? // 4 spaces. Field (5). " = " (3). 4+5+3 = 12. - // So $ is at 12. + // So @ is at 12. paramsDef := lsp.DefinitionParams{ TextDocument: lsp.TextDocumentIdentifier{URI: uri}, Position: lsp.Position{Line: 3, Character: 12}, diff --git a/test/operators_test.go b/test/operators_test.go index c74ecd7..8df7ae1 100644 --- a/test/operators_test.go +++ b/test/operators_test.go @@ -17,9 +17,9 @@ func TestOperators(t *testing.T) { #var S2: string = "World" +Obj = { - Math = $A + $B - Precedence = $A + $B * 2 - Concat = $S1 .. " " .. $S2 + Math = @A + @B + Precedence = @A + @B * 2 + Concat = @S1 .. " " .. @S2 } ` // Check Parser diff --git a/test/regex_variable_test.go b/test/regex_variable_test.go index 3e88d09..c957bdc 100644 --- a/test/regex_variable_test.go +++ b/test/regex_variable_test.go @@ -15,7 +15,7 @@ func TestRegexVariable(t *testing.T) { #var BadIP: string & =~"^[0-9.]+$" = "abc" +Obj = { - IP = $IP + IP = @IP } ` // Test Validator diff --git a/test/validator_variable_usage_test.go b/test/validator_variable_usage_test.go index 0d43951..84381b1 100644 --- a/test/validator_variable_usage_test.go +++ b/test/validator_variable_usage_test.go @@ -44,7 +44,7 @@ func TestVariableValidation(t *testing.T) { #var MyStr: string = "hello" +MyPID = { Class = PIDGAM - Kp = $MyStr + Kp = @MyStr Ki = 0.0 Kd = 0.0 } @@ -79,7 +79,7 @@ func TestVariableValidation(t *testing.T) { #var MyGain: float = 1.5 +MyPID = { Class = PIDGAM - Kp = $MyGain + Kp = @MyGain Ki = 0.0 Kd = 0.0 } diff --git a/test/variables_test.go b/test/variables_test.go index 2b0637c..ad2794a 100644 --- a/test/variables_test.go +++ b/test/variables_test.go @@ -16,8 +16,8 @@ func TestVariables(t *testing.T) { +Obj = { Class = Test - Field1 = $MyInt - Field2 = $MyStr + Field1 = @MyInt + Field2 = @MyStr } ` // Test Parsing