package parser import ( "testing" assert "git.sharkk.net/Go/Assert" "git.sharkk.net/Sharkk/Mako/types" ) func TestParserBasic(t *testing.T) { // Test basic variable assignment source := "x = 123" p := New(source) stmts := p.Parse() assert.Equal(t, 1, len(stmts)) assignStmt, ok := stmts[0].(types.AssignStmt) assert.True(t, ok) assert.Equal(t, "x", assignStmt.Name.Lexeme) literal, ok := assignStmt.Value.(types.LiteralExpr) assert.True(t, ok) assert.Equal(t, float64(123), literal.Value.(float64)) } func TestParserFunction(t *testing.T) { source := `fn add(a, b) return a + b end` p := New(source) stmts := p.Parse() assert.Equal(t, 1, len(stmts)) funcStmt, ok := stmts[0].(types.FunctionStmt) assert.True(t, ok) assert.Equal(t, "add", funcStmt.Name.Lexeme) assert.Equal(t, 2, len(funcStmt.Params)) assert.Equal(t, "a", funcStmt.Params[0].Lexeme) assert.Equal(t, "b", funcStmt.Params[1].Lexeme) assert.Equal(t, false, funcStmt.IsVariadic) assert.Equal(t, 1, len(funcStmt.Body)) } func TestParserVariadicFunction(t *testing.T) { source := `fn concat(first, ...) return first end` p := New(source) stmts := p.Parse() assert.Equal(t, 1, len(stmts)) funcStmt, ok := stmts[0].(types.FunctionStmt) assert.True(t, ok) assert.Equal(t, true, funcStmt.IsVariadic) assert.Equal(t, 1, len(funcStmt.Params)) } func TestParserIf(t *testing.T) { source := `if x > 10 then y = 20 elseif x < 5 then y = 10 else y = 15 end` p := New(source) stmts := p.Parse() assert.Equal(t, 1, len(stmts)) ifStmt, ok := stmts[0].(types.IfStmt) assert.True(t, ok) // Check condition binary, ok := ifStmt.Condition.(types.BinaryExpr) assert.True(t, ok) assert.Equal(t, types.GREATER, binary.Operator.Type) // Check then branch assert.Equal(t, 1, len(ifStmt.ThenBranch)) // Check elseif branch assert.Equal(t, 1, len(ifStmt.ElseIfs)) // Check else branch assert.Equal(t, 1, len(ifStmt.ElseBranch)) } func TestParserEchoStatement(t *testing.T) { source := `echo "Hello, world!"` p := New(source) stmts := p.Parse() assert.Equal(t, 1, len(stmts)) echoStmt, ok := stmts[0].(types.EchoStmt) assert.True(t, ok) literal, ok := echoStmt.Value.(types.LiteralExpr) assert.True(t, ok) assert.DeepEqual(t, "Hello, world!", literal.Value) } func TestParserExpression(t *testing.T) { source := "1 + 2 * 3" p := New(source) stmts := p.Parse() assert.Equal(t, 1, len(stmts)) exprStmt, ok := stmts[0].(types.ExpressionStmt) assert.True(t, ok) // Verify that * has higher precedence than + binary, ok := exprStmt.Expression.(types.BinaryExpr) assert.True(t, ok) assert.Equal(t, types.PLUS, binary.Operator.Type) leftLiteral, ok := binary.Left.(types.LiteralExpr) assert.True(t, ok) assert.Equal(t, float64(1), leftLiteral.Value.(float64)) rightBinary, ok := binary.Right.(types.BinaryExpr) assert.True(t, ok) assert.Equal(t, types.STAR, rightBinary.Operator.Type) } func TestParserFunctionCall(t *testing.T) { source := "add(1, 2)" p := New(source) stmts := p.Parse() assert.Equal(t, 1, len(stmts)) exprStmt, ok := stmts[0].(types.ExpressionStmt) assert.True(t, ok) call, ok := exprStmt.Expression.(types.CallExpr) assert.True(t, ok) variable, ok := call.Callee.(types.VariableExpr) assert.True(t, ok) assert.Equal(t, "add", variable.Name.Lexeme) assert.Equal(t, 2, len(call.Arguments)) } func TestParserAnonymousFunction(t *testing.T) { source := `fn(x) return x * x end` p := New(source) stmts := p.Parse() assert.Equal(t, 1, len(stmts)) exprStmt, ok := stmts[0].(types.ExpressionStmt) assert.True(t, ok) fnExpr, ok := exprStmt.Expression.(types.FunctionExpr) assert.True(t, ok) assert.Equal(t, 1, len(fnExpr.Params)) assert.Equal(t, "x", fnExpr.Params[0].Lexeme) assert.Equal(t, 1, len(fnExpr.Body)) } func TestParserComplex(t *testing.T) { source := ` fn fibonacci(n) if n < 2 then return n else return fibonacci(n - 1) + fibonacci(n - 2) end end echo fibonacci(10) ` p := New(source) stmts := p.Parse() assert.Equal(t, 2, len(stmts)) _, ok := stmts[0].(types.FunctionStmt) assert.True(t, ok) _, ok = stmts[1].(types.EchoStmt) assert.True(t, ok) } func TestParserErrorHandling(t *testing.T) { // Missing closing parenthesis source := "fn add(a, b" p := New(source) _ = p.Parse() assert.True(t, p.hadError) // Continue parsing after error source = ` x = 1 fn bad y = 2 ` p = New(source) stmts := p.Parse() assert.True(t, p.hadError) assert.Equal(t, 2, len(stmts)) // Should recover and parse the other statements } func TestParserComments(t *testing.T) { source := ` // This is a comment x = 1 // Inline comment // Another comment y = 2 ` p := New(source) stmts := p.Parse() assert.Equal(t, 2, len(stmts)) assert.False(t, p.hadError) }