package parser_test import ( "testing" "git.sharkk.net/Sharkk/Mako/parser" ) func TestProgram(t *testing.T) { input := `x = 42 y = "hello" z = true + false` l := parser.NewLexer(input) p := parser.NewParser(l) program := p.ParseProgram() checkParserErrors(t, p) if len(program.Statements) != 3 { t.Fatalf("expected 3 statements, got %d", len(program.Statements)) } expectedIdentifiers := []string{"x", "y", "z"} for i, expectedIdent := range expectedIdentifiers { stmt, ok := program.Statements[i].(*parser.Assignment) if !ok { t.Fatalf("statement %d: expected AssignStatement, got %T", i, program.Statements[i]) } ident, ok := stmt.Target.(*parser.Identifier) if !ok { t.Fatalf("statement %d: expected Identifier for Name, got %T", i, stmt.Target) } if ident.Value != expectedIdent { t.Errorf("statement %d: expected identifier %s, got %s", i, expectedIdent, ident.Value) } } } func TestMixedStatements(t *testing.T) { input := `x = 42 if x then y = "hello" if true then z = {1, 2, 3} end end arr = {a = 1, b = 2}` l := parser.NewLexer(input) p := parser.NewParser(l) program := p.ParseProgram() checkParserErrors(t, p) if len(program.Statements) != 3 { t.Fatalf("expected 3 statements, got %d", len(program.Statements)) } // First statement: assignment stmt1, ok := program.Statements[0].(*parser.Assignment) if !ok { t.Fatalf("statement 0: expected AssignStatement, got %T", program.Statements[0]) } ident1, ok := stmt1.Target.(*parser.Identifier) if !ok { t.Fatalf("expected Identifier for Name, got %T", stmt1.Target) } if ident1.Value != "x" { t.Errorf("expected identifier 'x', got %s", ident1.Value) } // Second statement: if statement stmt2, ok := program.Statements[1].(*parser.IfStatement) if !ok { t.Fatalf("statement 1: expected IfStatement, got %T", program.Statements[1]) } if len(stmt2.Body) != 2 { t.Errorf("expected 2 body statements, got %d", len(stmt2.Body)) } // Third statement: table assignment stmt3, ok := program.Statements[2].(*parser.Assignment) if !ok { t.Fatalf("statement 2: expected AssignStatement, got %T", program.Statements[2]) } table, ok := stmt3.Value.(*parser.TableLiteral) if !ok { t.Fatalf("expected TableLiteral value, got %T", stmt3.Value) } if table.IsArray() { t.Error("expected hash table, got array") } } func TestMemberAccessProgram(t *testing.T) { input := `table = {key = "value", nested = {inner = 42}} table.key = "new value" table["key"] = "bracket syntax" table.nested.inner = 100 echo table[table.key]` l := parser.NewLexer(input) p := parser.NewParser(l) program := p.ParseProgram() checkParserErrors(t, p) if len(program.Statements) != 5 { t.Fatalf("expected 5 statements, got %d", len(program.Statements)) } // Second statement: dot assignment stmt2, ok := program.Statements[1].(*parser.Assignment) if !ok { t.Fatalf("statement 1: expected AssignStatement, got %T", program.Statements[1]) } _, ok = stmt2.Target.(*parser.DotExpression) if !ok { t.Fatalf("expected DotExpression for assignment target, got %T", stmt2.Target) } // Third statement: bracket assignment stmt3, ok := program.Statements[2].(*parser.Assignment) if !ok { t.Fatalf("statement 2: expected AssignStatement, got %T", program.Statements[2]) } _, ok = stmt3.Target.(*parser.IndexExpression) if !ok { t.Fatalf("expected IndexExpression for assignment target, got %T", stmt3.Target) } // Fourth statement: chained dot assignment stmt4, ok := program.Statements[3].(*parser.Assignment) if !ok { t.Fatalf("statement 3: expected AssignStatement, got %T", program.Statements[3]) } _, ok = stmt4.Target.(*parser.DotExpression) if !ok { t.Fatalf("expected DotExpression for assignment target, got %T", stmt4.Target) } // Fifth statement: echo with nested access stmt5, ok := program.Statements[4].(*parser.EchoStatement) if !ok { t.Fatalf("statement 4: expected EchoStatement, got %T", program.Statements[4]) } _, ok = stmt5.Value.(*parser.IndexExpression) if !ok { t.Fatalf("expected IndexExpression in echo, got %T", stmt5.Value) } } func TestNestedConditionals(t *testing.T) { input := `if a then if b then x = 1 elseif c then x = 2 else x = 3 end y = 4 elseif d then z = 5 else w = 6 end` l := parser.NewLexer(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)) } outerIf, ok := program.Statements[0].(*parser.IfStatement) if !ok { t.Fatalf("expected IfStatement, got %T", program.Statements[0]) } // Outer if should have 2 body statements: nested if and assignment if len(outerIf.Body) != 2 { t.Fatalf("expected 2 body statements, got %d", len(outerIf.Body)) } // First body statement should be nested if nestedIf, ok := outerIf.Body[0].(*parser.IfStatement) if !ok { t.Fatalf("expected nested IfStatement, got %T", outerIf.Body[0]) } // Nested if should have 1 elseif and 1 else if len(nestedIf.ElseIfs) != 1 { t.Errorf("expected 1 elseif in nested if, got %d", len(nestedIf.ElseIfs)) } if len(nestedIf.Else) != 1 { t.Errorf("expected 1 else statement in nested if, got %d", len(nestedIf.Else)) } // Outer if should have 1 elseif and 1 else if len(outerIf.ElseIfs) != 1 { t.Errorf("expected 1 elseif in outer if, got %d", len(outerIf.ElseIfs)) } if len(outerIf.Else) != 1 { t.Errorf("expected 1 else statement in outer if, got %d", len(outerIf.Else)) } } func TestComplexExpressions(t *testing.T) { input := `result = (0xFF + 0b1010) * 1e2 if result then table = { hex = 0xDEAD, bin = 0b1111, sci = 1.5e-3, str = [[multiline string]] } end` l := parser.NewLexer(input) p := parser.NewParser(l) program := p.ParseProgram() checkParserErrors(t, p) if len(program.Statements) != 2 { t.Fatalf("expected 2 statements, got %d", len(program.Statements)) } // First statement: complex expression assignment stmt1, ok := program.Statements[0].(*parser.Assignment) if !ok { t.Fatalf("expected AssignStatement, got %T", program.Statements[0]) } // Should be a complex infix expression _, ok = stmt1.Value.(*parser.InfixExpression) if !ok { t.Fatalf("expected InfixExpression, got %T", stmt1.Value) } // Second statement: if with table assignment stmt2, ok := program.Statements[1].(*parser.IfStatement) if !ok { t.Fatalf("expected IfStatement, got %T", program.Statements[1]) } if len(stmt2.Body) != 1 { t.Fatalf("expected 1 body statement, got %d", len(stmt2.Body)) } bodyStmt, ok := stmt2.Body[0].(*parser.Assignment) if !ok { t.Fatalf("expected AssignStatement in body, got %T", stmt2.Body[0]) } table, ok := bodyStmt.Value.(*parser.TableLiteral) if !ok { t.Fatalf("expected TableLiteral, got %T", bodyStmt.Value) } if len(table.Pairs) != 4 { t.Errorf("expected 4 table pairs, got %d", len(table.Pairs)) } } func TestMixedStatementsWithEcho(t *testing.T) { input := `x = 42 echo x if x then echo "found x" end echo {result = x}` l := parser.NewLexer(input) p := parser.NewParser(l) program := p.ParseProgram() checkParserErrors(t, p) if len(program.Statements) != 4 { t.Fatalf("expected 4 statements, got %d", len(program.Statements)) } // First: assignment _, ok := program.Statements[0].(*parser.Assignment) if !ok { t.Fatalf("statement 0: expected AssignStatement, got %T", program.Statements[0]) } // Second: echo echo1, ok := program.Statements[1].(*parser.EchoStatement) if !ok { t.Fatalf("statement 1: expected EchoStatement, got %T", program.Statements[1]) } testIdentifier(t, echo1.Value, "x") // Third: if statement with echo in body ifStmt, ok := program.Statements[2].(*parser.IfStatement) if !ok { t.Fatalf("statement 2: expected IfStatement, got %T", program.Statements[2]) } if len(ifStmt.Body) != 1 { t.Fatalf("expected 1 body statement, got %d", len(ifStmt.Body)) } bodyEcho, ok := ifStmt.Body[0].(*parser.EchoStatement) if !ok { t.Fatalf("if body: expected EchoStatement, got %T", ifStmt.Body[0]) } testStringLiteral(t, bodyEcho.Value, "found x") // Fourth: echo with table echo2, ok := program.Statements[3].(*parser.EchoStatement) if !ok { t.Fatalf("statement 3: expected EchoStatement, got %T", program.Statements[3]) } _, ok = echo2.Value.(*parser.TableLiteral) if !ok { t.Fatalf("expected TableLiteral in echo, got %T", echo2.Value) } } func TestModuloOperator(t *testing.T) { input := `result = 10 % 3` l := parser.NewLexer(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 AssignStatement, got %T", program.Statements[0]) } infix, ok := stmt.Value.(*parser.InfixExpression) if !ok { t.Fatalf("expected InfixExpression, got %T", stmt.Value) } if infix.Operator != "%" { t.Errorf("expected operator '%%', got %s", infix.Operator) } leftLit, ok := infix.Left.(*parser.NumberLiteral) if !ok { t.Fatalf("expected NumberLiteral on left, got %T", infix.Left) } if leftLit.Value != 10.0 { t.Errorf("expected left value 10, got %f", leftLit.Value) } rightLit, ok := infix.Right.(*parser.NumberLiteral) if !ok { t.Fatalf("expected NumberLiteral on right, got %T", infix.Right) } if rightLit.Value != 3.0 { t.Errorf("expected right value 3, got %f", rightLit.Value) } }