package parser_test import ( "testing" "git.sharkk.net/Sharkk/Mako/parser" ) func TestAssignStatements(t *testing.T) { tests := []struct { input string expectedIdentifier string expectedValue any isExpression bool }{ {"x = 42", "x", 42.0, false}, {"name = \"test\"", "name", "test", false}, {"flag = true", "flag", true, false}, {"ptr = nil", "ptr", nil, false}, {"result = 3 + 4", "result", "(3.00 + 4.00)", true}, } for _, tt := range tests { t.Run(tt.input, func(t *testing.T) { l := parser.NewLexer(tt.input) p := parser.NewParser(l) program := p.ParseProgram() checkParserErrors(t, p) if len(program.Statements) != 1 { t.Fatalf("expected 1 statement, got %d", len(program.Statements)) } stmt, ok := program.Statements[0].(*parser.Assignment) if !ok { t.Fatalf("expected Assignment, got %T", program.Statements[0]) } // Check that Target is an Identifier ident, ok := stmt.Target.(*parser.Identifier) if !ok { t.Fatalf("expected Identifier for Target, got %T", stmt.Target) } if ident.Value != tt.expectedIdentifier { t.Errorf("expected identifier %s, got %s", tt.expectedIdentifier, ident.Value) } if tt.isExpression { if stmt.Value.String() != tt.expectedValue.(string) { t.Errorf("expected expression %s, got %s", tt.expectedValue.(string), stmt.Value.String()) } } else { switch expected := tt.expectedValue.(type) { case float64: testNumberLiteral(t, stmt.Value, expected) case string: testStringLiteral(t, stmt.Value, expected) case bool: testBooleanLiteral(t, stmt.Value, expected) case nil: testNilLiteral(t, stmt.Value) } } }) } } func TestMemberAccessAssignment(t *testing.T) { tests := []struct { input string expected string description string }{ {"table.key = 42", "table.key = 42.00", "dot notation assignment"}, {"arr[1] = \"hello\"", "arr[1.00] = \"hello\"", "bracket notation assignment"}, {"obj.nested.deep = true", "obj.nested.deep = true", "chained dot assignment"}, {"matrix[1][2] = 3.14", "matrix[1.00][2.00] = 3.14", "chained bracket assignment"}, {"data[\"key\"].value = nil", "data[\"key\"].value = nil", "mixed access assignment"}, } for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { l := parser.NewLexer(tt.input) p := parser.NewParser(l) program := p.ParseProgram() checkParserErrors(t, p) if len(program.Statements) != 1 { t.Fatalf("expected 1 statement, got %d", len(program.Statements)) } stmt, ok := program.Statements[0].(*parser.Assignment) if !ok { t.Fatalf("expected Assignment, got %T", program.Statements[0]) } if stmt.String() != tt.expected { t.Errorf("expected %s, got %s", tt.expected, stmt.String()) } }) } } func TestMemberAccessExpressions(t *testing.T) { tests := []struct { input string expected string description string }{ {"echo table.key", "echo table.key", "dot access"}, {"echo arr[1]", "echo arr[1.00]", "bracket access"}, {"echo obj.nested.deep", "echo obj.nested.deep", "chained dot access"}, {"echo matrix[1][2]", "echo matrix[1.00][2.00]", "chained bracket access"}, {"echo data[\"key\"].value", "echo data[\"key\"].value", "mixed access"}, {"echo table.key + arr[0]", "echo (table.key + arr[0.00])", "member access in expression"}, } for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { l := parser.NewLexer(tt.input) p := parser.NewParser(l) program := p.ParseProgram() checkParserErrors(t, p) if len(program.Statements) != 1 { t.Fatalf("expected 1 statement, got %d", len(program.Statements)) } if program.Statements[0].String() != tt.expected { t.Errorf("expected %s, got %s", tt.expected, program.Statements[0].String()) } }) } } func TestTableAssignments(t *testing.T) { tests := []struct { input string identifier string pairCount int isArray bool description string }{ {"arr = {1, 2, 3}", "arr", 3, true, "array assignment"}, {"hash = {x = 1, y = 2}", "hash", 2, false, "hash assignment"}, {"empty = {}", "empty", 0, true, "empty table assignment"}, } for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { l := parser.NewLexer(tt.input) p := parser.NewParser(l) program := p.ParseProgram() checkParserErrors(t, p) if len(program.Statements) != 1 { t.Fatalf("expected 1 statement, got %d", len(program.Statements)) } stmt, ok := program.Statements[0].(*parser.Assignment) if !ok { t.Fatalf("expected Assignment, got %T", program.Statements[0]) } // Check that Target is an Identifier ident, ok := stmt.Target.(*parser.Identifier) if !ok { t.Fatalf("expected Identifier for Target, got %T", stmt.Target) } if ident.Value != tt.identifier { t.Errorf("expected identifier %s, got %s", tt.identifier, ident.Value) } table, ok := stmt.Value.(*parser.TableLiteral) if !ok { t.Fatalf("expected TableLiteral, got %T", stmt.Value) } if len(table.Pairs) != tt.pairCount { t.Errorf("expected %d pairs, got %d", tt.pairCount, len(table.Pairs)) } if table.IsArray() != tt.isArray { t.Errorf("expected IsArray() = %t, got %t", tt.isArray, table.IsArray()) } }) } }