package parser import ( "golox/ast" "golox/errors" "golox/token" "testing" ) func TestExpressionParsing(t *testing.T) { tests := []struct { name string tokens []token.Token expected string }{ { name: "Simple expression", tokens: []token.Token{ {Type: token.NUMBER, Literal: 1}, {Type: token.PLUS, Lexeme: "+"}, {Type: token.NUMBER, Literal: 2}, {Type: token.SEMICOLON, Lexeme: ";"}, {Type: token.EOF}, }, expected: "(+ 1 2)", }, { name: "Unary expression", tokens: []token.Token{ {Type: token.MINUS, Lexeme: "-"}, {Type: token.NUMBER, Literal: 123}, {Type: token.SEMICOLON, Lexeme: ";"}, {Type: token.EOF}, }, expected: "(- 123)", }, { name: "Grouping expression", tokens: []token.Token{ {Type: token.LEFT_PAREN, Lexeme: "("}, {Type: token.NUMBER, Literal: 1}, {Type: token.PLUS, Lexeme: "+"}, {Type: token.NUMBER, Literal: 2}, {Type: token.RIGHT_PAREN, Lexeme: ")"}, {Type: token.SEMICOLON, Lexeme: ";"}, {Type: token.EOF}, }, expected: "(group (+ 1 2))", }, { name: "Comparison expression", tokens: []token.Token{ {Type: token.NUMBER, Literal: 1}, {Type: token.GREATER, Lexeme: ">"}, {Type: token.NUMBER, Literal: 2}, {Type: token.SEMICOLON, Lexeme: ";"}, {Type: token.EOF}, }, expected: "(> 1 2)", }, { name: "Equality expression", tokens: []token.Token{ {Type: token.NUMBER, Literal: 1}, {Type: token.EQUAL_EQUAL, Lexeme: "=="}, {Type: token.NUMBER, Literal: 2}, {Type: token.SEMICOLON, Lexeme: ";"}, {Type: token.EOF}, }, expected: "(== 1 2)", }, { name: "Parsing error - missing right parenthesis", tokens: []token.Token{ {Type: token.LEFT_PAREN, Lexeme: "("}, {Type: token.NUMBER, Literal: 1}, {Type: token.PLUS, Lexeme: "+"}, {Type: token.NUMBER, Literal: 2}, {Type: token.SEMICOLON, Lexeme: ";"}, {Type: token.EOF}, }, expected: "Expect ')' after expression.", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { parser := New(tt.tokens, errors.NewMockErrorLogger()) stmts := parser.Parse() if len(stmts) != 1 { t.Fatalf("expected 1 statement, got %d", len(stmts)) } stmt := stmts[0] var expr ast.Expr if es, ok := stmt.(*ast.ExpressionStmt); !ok { t.Errorf("expected ExprStmt, got %T", stmt) } else { expr = es.Expression } ap := ast.NewPrinter() s := ap.PrintExpr(expr) if s != tt.expected { t.Errorf("expected %v, got %v", tt.expected, s) } }) } } func TestParseExpressionStmt(t *testing.T) { tests := []struct { name string tokens []token.Token expected string }{ { name: "simple expression statement", tokens: []token.Token{ {Type: token.NUMBER, Lexeme: "42", Literal: 42}, {Type: token.SEMICOLON, Lexeme: ";"}, {Type: token.EOF}, }, expected: "42\n", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { parser := New(tt.tokens, errors.NewMockErrorLogger()) stmts := parser.Parse() if len(stmts) != 1 { t.Fatalf("expected 1 statement, got %d", len(stmts)) } ap := ast.NewPrinter() s := ap.PrintStmts(stmts) if s != tt.expected { t.Errorf("expected %v, got %v", tt.expected, s) } }) } } func TestParsePrintStmt(t *testing.T) { tests := []struct { name string tokens []token.Token expected string }{ { name: "simple print statement", tokens: []token.Token{ {Type: token.PRINT, Lexeme: "print"}, {Type: token.NUMBER, Lexeme: "42", Literal: 42}, {Type: token.SEMICOLON, Lexeme: ";"}, {Type: token.EOF}, }, expected: "(print 42)\n", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { parser := New(tt.tokens, errors.NewMockErrorLogger()) stmts := parser.Parse() if len(stmts) != 1 { t.Fatalf("expected 1 statement, got %d", len(stmts)) } ap := ast.NewPrinter() s := ap.PrintStmts(stmts) if s != tt.expected { t.Errorf("expected %v, got %v", tt.expected, s) } }) } } func TestParseVarStmt(t *testing.T) { tests := []struct { name string tokens []token.Token expected string }{ { name: "simple var statement", tokens: []token.Token{ {Type: token.VAR, Lexeme: "var"}, {Type: token.IDENTIFIER, Lexeme: "foo"}, {Type: token.EQUAL, Lexeme: "="}, {Type: token.NUMBER, Lexeme: "42", Literal: 42}, {Type: token.SEMICOLON, Lexeme: ";"}, {Type: token.EOF}, }, expected: "(var foo 42)\n", }, { name: "simple var statement", tokens: []token.Token{ {Type: token.VAR, Lexeme: "var"}, {Type: token.IDENTIFIER, Lexeme: "foo"}, {Type: token.SEMICOLON, Lexeme: ";"}, {Type: token.EOF}, }, expected: "(var foo)\n", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { parser := New(tt.tokens, errors.NewMockErrorLogger()) stmts := parser.Parse() if len(stmts) != 1 { t.Fatalf("expected 1 statement, got %d", len(stmts)) } ap := ast.NewPrinter() s := ap.PrintStmts(stmts) if s != tt.expected { t.Errorf("expected %v, got %v", tt.expected, s) } }) } }