package tests import ( "testing" assert "git.sharkk.net/Go/Assert" "git.sharkk.net/Sharkk/Mako/lexer" "git.sharkk.net/Sharkk/Mako/parser" ) func TestParseVariableStatement(t *testing.T) { input := `x = 5;` lex := lexer.New(input) p := parser.New(lex) program := p.ParseProgram() assert.Equal(t, 0, len(p.Errors())) assert.Equal(t, 1, len(program.Statements)) stmt, ok := program.Statements[0].(*parser.VariableStatement) assert.True(t, ok) assert.Equal(t, "x", stmt.Name.Value) // Test the expression value numLit, ok := stmt.Value.(*parser.NumberLiteral) assert.True(t, ok) assert.Equal(t, 5.0, numLit.Value) } func TestParseEchoStatement(t *testing.T) { input := `echo "hello";` lex := lexer.New(input) p := parser.New(lex) program := p.ParseProgram() assert.Equal(t, 0, len(p.Errors())) assert.Equal(t, 1, len(program.Statements)) stmt, ok := program.Statements[0].(*parser.EchoStatement) assert.True(t, ok) strLit, ok := stmt.Value.(*parser.StringLiteral) assert.True(t, ok) assert.Equal(t, "hello", strLit.Value) } func TestParseTableLiteral(t *testing.T) { input := `table = { name = "John", age = 30 };` lex := lexer.New(input) p := parser.New(lex) program := p.ParseProgram() assert.Equal(t, 0, len(p.Errors())) assert.Equal(t, 1, len(program.Statements)) stmt, ok := program.Statements[0].(*parser.VariableStatement) assert.True(t, ok) assert.Equal(t, "table", stmt.Name.Value) tableLit, ok := stmt.Value.(*parser.TableLiteral) assert.True(t, ok) assert.Equal(t, 2, len(tableLit.Pairs)) // Check that the table has the expected keys and values // We need to find the entries in the pairs map foundName := false foundAge := false for key, value := range tableLit.Pairs { if ident, ok := key.(*parser.Identifier); ok && ident.Value == "name" { foundName = true strLit, ok := value.(*parser.StringLiteral) assert.True(t, ok) assert.Equal(t, "John", strLit.Value) } if ident, ok := key.(*parser.Identifier); ok && ident.Value == "age" { foundAge = true numLit, ok := value.(*parser.NumberLiteral) assert.True(t, ok) assert.Equal(t, 30.0, numLit.Value) } } assert.True(t, foundName) assert.True(t, foundAge) } func TestParseIndexExpression(t *testing.T) { input := `table["key"];` lex := lexer.New(input) p := parser.New(lex) program := p.ParseProgram() assert.Equal(t, 0, len(p.Errors())) assert.Equal(t, 1, len(program.Statements)) exprStmt, ok := program.Statements[0].(*parser.ExpressionStatement) assert.True(t, ok) indexExpr, ok := exprStmt.Expression.(*parser.IndexExpression) assert.True(t, ok) ident, ok := indexExpr.Left.(*parser.Identifier) assert.True(t, ok) assert.Equal(t, "table", ident.Value) strLit, ok := indexExpr.Index.(*parser.StringLiteral) assert.True(t, ok) assert.Equal(t, "key", strLit.Value) } func TestParseInfixExpression(t *testing.T) { tests := []struct { input string leftValue float64 operator string rightValue float64 }{ {"5 + 5;", 5, "+", 5}, {"5 - 5;", 5, "-", 5}, {"5 * 5;", 5, "*", 5}, {"5 / 5;", 5, "/", 5}, {"5 < 5;", 5, "<", 5}, {"5 > 5;", 5, ">", 5}, {"5 == 5;", 5, "==", 5}, {"5 != 5;", 5, "!=", 5}, {"5 <= 5;", 5, "<=", 5}, {"5 >= 5;", 5, ">=", 5}, } for _, tt := range tests { lex := lexer.New(tt.input) p := parser.New(lex) program := p.ParseProgram() assert.Equal(t, 0, len(p.Errors())) assert.Equal(t, 1, len(program.Statements)) exprStmt, ok := program.Statements[0].(*parser.ExpressionStatement) assert.True(t, ok) infixExpr, ok := exprStmt.Expression.(*parser.InfixExpression) assert.True(t, ok) leftLit, ok := infixExpr.Left.(*parser.NumberLiteral) assert.True(t, ok) assert.Equal(t, tt.leftValue, leftLit.Value) assert.Equal(t, tt.operator, infixExpr.Operator) rightLit, ok := infixExpr.Right.(*parser.NumberLiteral) assert.True(t, ok) assert.Equal(t, tt.rightValue, rightLit.Value) } } func TestParsePrefixExpression(t *testing.T) { tests := []struct { input string operator string value float64 }{ {"-5;", "-", 5}, } for _, tt := range tests { lex := lexer.New(tt.input) p := parser.New(lex) program := p.ParseProgram() assert.Equal(t, 0, len(p.Errors())) assert.Equal(t, 1, len(program.Statements)) exprStmt, ok := program.Statements[0].(*parser.ExpressionStatement) assert.True(t, ok) prefixExpr, ok := exprStmt.Expression.(*parser.PrefixExpression) assert.True(t, ok) assert.Equal(t, tt.operator, prefixExpr.Operator) rightLit, ok := prefixExpr.Right.(*parser.NumberLiteral) assert.True(t, ok) assert.Equal(t, tt.value, rightLit.Value) } } func TestParseBooleanLiteral(t *testing.T) { tests := []struct { input string value bool }{ {"true;", true}, {"false;", false}, } for _, tt := range tests { lex := lexer.New(tt.input) p := parser.New(lex) program := p.ParseProgram() assert.Equal(t, 0, len(p.Errors())) assert.Equal(t, 1, len(program.Statements)) exprStmt, ok := program.Statements[0].(*parser.ExpressionStatement) assert.True(t, ok) boolLit, ok := exprStmt.Expression.(*parser.BooleanLiteral) assert.True(t, ok) assert.Equal(t, tt.value, boolLit.Value) } } func TestParseIfExpression(t *testing.T) { input := `if x < 10 then echo "x is less than 10"; else echo "x is not less than 10"; end` lex := lexer.New(input) p := parser.New(lex) program := p.ParseProgram() assert.Equal(t, 0, len(p.Errors())) assert.Equal(t, 1, len(program.Statements)) exprStmt, ok := program.Statements[0].(*parser.ExpressionStatement) assert.True(t, ok) ifExpr, ok := exprStmt.Expression.(*parser.IfExpression) assert.True(t, ok) // Check condition condition, ok := ifExpr.Condition.(*parser.InfixExpression) assert.True(t, ok) assert.Equal(t, "<", condition.Operator) // Check consequence assert.Equal(t, 1, len(ifExpr.Consequence.Statements)) consEchoStmt, ok := ifExpr.Consequence.Statements[0].(*parser.EchoStatement) assert.True(t, ok) consStrLit, ok := consEchoStmt.Value.(*parser.StringLiteral) assert.True(t, ok) assert.Equal(t, "x is less than 10", consStrLit.Value) // Check alternative assert.NotNil(t, ifExpr.Alternative) assert.Equal(t, 1, len(ifExpr.Alternative.Statements)) altEchoStmt, ok := ifExpr.Alternative.Statements[0].(*parser.EchoStatement) assert.True(t, ok) altStrLit, ok := altEchoStmt.Value.(*parser.StringLiteral) assert.True(t, ok) assert.Equal(t, "x is not less than 10", altStrLit.Value) } func TestParseElseIfExpression(t *testing.T) { input := `if x < 10 then echo "x is less than 10"; elseif x < 20 then echo "x is less than 20"; else echo "x is not less than 20"; end` lex := lexer.New(input) p := parser.New(lex) program := p.ParseProgram() assert.Equal(t, 0, len(p.Errors())) assert.Equal(t, 1, len(program.Statements)) exprStmt, ok := program.Statements[0].(*parser.ExpressionStatement) assert.True(t, ok) ifExpr, ok := exprStmt.Expression.(*parser.IfExpression) assert.True(t, ok) // Check that we have an alternative block assert.NotNil(t, ifExpr.Alternative) assert.Equal(t, 1, len(ifExpr.Alternative.Statements)) // The alternative should contain another IfExpression (the elseif) altExprStmt, ok := ifExpr.Alternative.Statements[0].(*parser.ExpressionStatement) assert.True(t, ok) nestedIfExpr, ok := altExprStmt.Expression.(*parser.IfExpression) assert.True(t, ok) // Check nested if condition condition, ok := nestedIfExpr.Condition.(*parser.InfixExpression) assert.True(t, ok) assert.Equal(t, "<", condition.Operator) rightLit, ok := condition.Right.(*parser.NumberLiteral) assert.True(t, ok) assert.Equal(t, 20.0, rightLit.Value) // Check that the nested if has an alternative (the else) assert.NotNil(t, nestedIfExpr.Alternative) } func TestParseLogicalOperators(t *testing.T) { tests := []struct { input string operator string }{ {"true and false;", "and"}, {"true or false;", "or"}, {"not true;", "not"}, } for _, tt := range tests { lex := lexer.New(tt.input) p := parser.New(lex) program := p.ParseProgram() assert.Equal(t, 0, len(p.Errors())) assert.Equal(t, 1, len(program.Statements)) exprStmt, ok := program.Statements[0].(*parser.ExpressionStatement) assert.True(t, ok) if tt.operator == "not" { // Test prefix NOT prefixExpr, ok := exprStmt.Expression.(*parser.PrefixExpression) assert.True(t, ok) assert.Equal(t, tt.operator, prefixExpr.Operator) } else { // Test infix AND/OR infixExpr, ok := exprStmt.Expression.(*parser.InfixExpression) assert.True(t, ok) assert.Equal(t, tt.operator, infixExpr.Operator) } } }