Mako/parser/parser_test.go
2025-05-07 09:08:51 -05:00

229 lines
4.7 KiB
Go

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)
}