229 lines
4.7 KiB
Go
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)
|
|
}
|