371 lines
9.3 KiB
Go
371 lines
9.3 KiB
Go
package parser_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"git.sharkk.net/Sharkk/Mako/parser"
|
|
)
|
|
|
|
func TestProgram(t *testing.T) {
|
|
input := `x = 42
|
|
y = "hello"
|
|
z = true + false`
|
|
|
|
l := parser.NewLexer(input)
|
|
p := parser.NewParser(l)
|
|
program := p.ParseProgram()
|
|
checkParserErrors(t, p)
|
|
|
|
if len(program.Statements) != 3 {
|
|
t.Fatalf("expected 3 statements, got %d", len(program.Statements))
|
|
}
|
|
|
|
expectedIdentifiers := []string{"x", "y", "z"}
|
|
for i, expectedIdent := range expectedIdentifiers {
|
|
stmt, ok := program.Statements[i].(*parser.Assignment)
|
|
if !ok {
|
|
t.Fatalf("statement %d: expected AssignStatement, got %T", i, program.Statements[i])
|
|
}
|
|
|
|
ident, ok := stmt.Target.(*parser.Identifier)
|
|
if !ok {
|
|
t.Fatalf("statement %d: expected Identifier for Name, got %T", i, stmt.Target)
|
|
}
|
|
|
|
if ident.Value != expectedIdent {
|
|
t.Errorf("statement %d: expected identifier %s, got %s", i, expectedIdent, ident.Value)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMixedStatements(t *testing.T) {
|
|
input := `x = 42
|
|
if x then
|
|
y = "hello"
|
|
if true then
|
|
z = {1, 2, 3}
|
|
end
|
|
end
|
|
arr = {a = 1, b = 2}`
|
|
|
|
l := parser.NewLexer(input)
|
|
p := parser.NewParser(l)
|
|
program := p.ParseProgram()
|
|
checkParserErrors(t, p)
|
|
|
|
if len(program.Statements) != 3 {
|
|
t.Fatalf("expected 3 statements, got %d", len(program.Statements))
|
|
}
|
|
|
|
// First statement: assignment
|
|
stmt1, ok := program.Statements[0].(*parser.Assignment)
|
|
if !ok {
|
|
t.Fatalf("statement 0: expected AssignStatement, got %T", program.Statements[0])
|
|
}
|
|
ident1, ok := stmt1.Target.(*parser.Identifier)
|
|
if !ok {
|
|
t.Fatalf("expected Identifier for Name, got %T", stmt1.Target)
|
|
}
|
|
if ident1.Value != "x" {
|
|
t.Errorf("expected identifier 'x', got %s", ident1.Value)
|
|
}
|
|
|
|
// Second statement: if statement
|
|
stmt2, ok := program.Statements[1].(*parser.IfStatement)
|
|
if !ok {
|
|
t.Fatalf("statement 1: expected IfStatement, got %T", program.Statements[1])
|
|
}
|
|
if len(stmt2.Body) != 2 {
|
|
t.Errorf("expected 2 body statements, got %d", len(stmt2.Body))
|
|
}
|
|
|
|
// Third statement: table assignment
|
|
stmt3, ok := program.Statements[2].(*parser.Assignment)
|
|
if !ok {
|
|
t.Fatalf("statement 2: expected AssignStatement, got %T", program.Statements[2])
|
|
}
|
|
table, ok := stmt3.Value.(*parser.TableLiteral)
|
|
if !ok {
|
|
t.Fatalf("expected TableLiteral value, got %T", stmt3.Value)
|
|
}
|
|
if table.IsArray() {
|
|
t.Error("expected hash table, got array")
|
|
}
|
|
}
|
|
|
|
func TestMemberAccessProgram(t *testing.T) {
|
|
input := `table = {key = "value", nested = {inner = 42}}
|
|
table.key = "new value"
|
|
table["key"] = "bracket syntax"
|
|
table.nested.inner = 100
|
|
echo table[table.key]`
|
|
|
|
l := parser.NewLexer(input)
|
|
p := parser.NewParser(l)
|
|
program := p.ParseProgram()
|
|
checkParserErrors(t, p)
|
|
|
|
if len(program.Statements) != 5 {
|
|
t.Fatalf("expected 5 statements, got %d", len(program.Statements))
|
|
}
|
|
|
|
// Second statement: dot assignment
|
|
stmt2, ok := program.Statements[1].(*parser.Assignment)
|
|
if !ok {
|
|
t.Fatalf("statement 1: expected AssignStatement, got %T", program.Statements[1])
|
|
}
|
|
_, ok = stmt2.Target.(*parser.DotExpression)
|
|
if !ok {
|
|
t.Fatalf("expected DotExpression for assignment target, got %T", stmt2.Target)
|
|
}
|
|
|
|
// Third statement: bracket assignment
|
|
stmt3, ok := program.Statements[2].(*parser.Assignment)
|
|
if !ok {
|
|
t.Fatalf("statement 2: expected AssignStatement, got %T", program.Statements[2])
|
|
}
|
|
_, ok = stmt3.Target.(*parser.IndexExpression)
|
|
if !ok {
|
|
t.Fatalf("expected IndexExpression for assignment target, got %T", stmt3.Target)
|
|
}
|
|
|
|
// Fourth statement: chained dot assignment
|
|
stmt4, ok := program.Statements[3].(*parser.Assignment)
|
|
if !ok {
|
|
t.Fatalf("statement 3: expected AssignStatement, got %T", program.Statements[3])
|
|
}
|
|
_, ok = stmt4.Target.(*parser.DotExpression)
|
|
if !ok {
|
|
t.Fatalf("expected DotExpression for assignment target, got %T", stmt4.Target)
|
|
}
|
|
|
|
// Fifth statement: echo with nested access
|
|
stmt5, ok := program.Statements[4].(*parser.EchoStatement)
|
|
if !ok {
|
|
t.Fatalf("statement 4: expected EchoStatement, got %T", program.Statements[4])
|
|
}
|
|
_, ok = stmt5.Value.(*parser.IndexExpression)
|
|
if !ok {
|
|
t.Fatalf("expected IndexExpression in echo, got %T", stmt5.Value)
|
|
}
|
|
}
|
|
|
|
func TestNestedConditionals(t *testing.T) {
|
|
input := `if a then
|
|
if b then
|
|
x = 1
|
|
elseif c then
|
|
x = 2
|
|
else
|
|
x = 3
|
|
end
|
|
y = 4
|
|
elseif d then
|
|
z = 5
|
|
else
|
|
w = 6
|
|
end`
|
|
|
|
l := parser.NewLexer(input)
|
|
p := parser.NewParser(l)
|
|
program := p.ParseProgram()
|
|
checkParserErrors(t, p)
|
|
|
|
if len(program.Statements) != 1 {
|
|
t.Fatalf("expected 1 statement, got %d", len(program.Statements))
|
|
}
|
|
|
|
outerIf, ok := program.Statements[0].(*parser.IfStatement)
|
|
if !ok {
|
|
t.Fatalf("expected IfStatement, got %T", program.Statements[0])
|
|
}
|
|
|
|
// Outer if should have 2 body statements: nested if and assignment
|
|
if len(outerIf.Body) != 2 {
|
|
t.Fatalf("expected 2 body statements, got %d", len(outerIf.Body))
|
|
}
|
|
|
|
// First body statement should be nested if
|
|
nestedIf, ok := outerIf.Body[0].(*parser.IfStatement)
|
|
if !ok {
|
|
t.Fatalf("expected nested IfStatement, got %T", outerIf.Body[0])
|
|
}
|
|
|
|
// Nested if should have 1 elseif and 1 else
|
|
if len(nestedIf.ElseIfs) != 1 {
|
|
t.Errorf("expected 1 elseif in nested if, got %d", len(nestedIf.ElseIfs))
|
|
}
|
|
|
|
if len(nestedIf.Else) != 1 {
|
|
t.Errorf("expected 1 else statement in nested if, got %d", len(nestedIf.Else))
|
|
}
|
|
|
|
// Outer if should have 1 elseif and 1 else
|
|
if len(outerIf.ElseIfs) != 1 {
|
|
t.Errorf("expected 1 elseif in outer if, got %d", len(outerIf.ElseIfs))
|
|
}
|
|
|
|
if len(outerIf.Else) != 1 {
|
|
t.Errorf("expected 1 else statement in outer if, got %d", len(outerIf.Else))
|
|
}
|
|
}
|
|
|
|
func TestComplexExpressions(t *testing.T) {
|
|
input := `result = (0xFF + 0b1010) * 1e2
|
|
if result then
|
|
table = {
|
|
hex = 0xDEAD,
|
|
bin = 0b1111,
|
|
sci = 1.5e-3,
|
|
str = [[multiline
|
|
string]]
|
|
}
|
|
end`
|
|
|
|
l := parser.NewLexer(input)
|
|
p := parser.NewParser(l)
|
|
program := p.ParseProgram()
|
|
checkParserErrors(t, p)
|
|
|
|
if len(program.Statements) != 2 {
|
|
t.Fatalf("expected 2 statements, got %d", len(program.Statements))
|
|
}
|
|
|
|
// First statement: complex expression assignment
|
|
stmt1, ok := program.Statements[0].(*parser.Assignment)
|
|
if !ok {
|
|
t.Fatalf("expected AssignStatement, got %T", program.Statements[0])
|
|
}
|
|
|
|
// Should be a complex infix expression
|
|
_, ok = stmt1.Value.(*parser.InfixExpression)
|
|
if !ok {
|
|
t.Fatalf("expected InfixExpression, got %T", stmt1.Value)
|
|
}
|
|
|
|
// Second statement: if with table assignment
|
|
stmt2, ok := program.Statements[1].(*parser.IfStatement)
|
|
if !ok {
|
|
t.Fatalf("expected IfStatement, got %T", program.Statements[1])
|
|
}
|
|
|
|
if len(stmt2.Body) != 1 {
|
|
t.Fatalf("expected 1 body statement, got %d", len(stmt2.Body))
|
|
}
|
|
|
|
bodyStmt, ok := stmt2.Body[0].(*parser.Assignment)
|
|
if !ok {
|
|
t.Fatalf("expected AssignStatement in body, got %T", stmt2.Body[0])
|
|
}
|
|
|
|
table, ok := bodyStmt.Value.(*parser.TableLiteral)
|
|
if !ok {
|
|
t.Fatalf("expected TableLiteral, got %T", bodyStmt.Value)
|
|
}
|
|
|
|
if len(table.Pairs) != 4 {
|
|
t.Errorf("expected 4 table pairs, got %d", len(table.Pairs))
|
|
}
|
|
}
|
|
|
|
func TestMixedStatementsWithEcho(t *testing.T) {
|
|
input := `x = 42
|
|
echo x
|
|
if x then
|
|
echo "found x"
|
|
end
|
|
echo {result = x}`
|
|
|
|
l := parser.NewLexer(input)
|
|
p := parser.NewParser(l)
|
|
program := p.ParseProgram()
|
|
checkParserErrors(t, p)
|
|
|
|
if len(program.Statements) != 4 {
|
|
t.Fatalf("expected 4 statements, got %d", len(program.Statements))
|
|
}
|
|
|
|
// First: assignment
|
|
_, ok := program.Statements[0].(*parser.Assignment)
|
|
if !ok {
|
|
t.Fatalf("statement 0: expected AssignStatement, got %T", program.Statements[0])
|
|
}
|
|
|
|
// Second: echo
|
|
echo1, ok := program.Statements[1].(*parser.EchoStatement)
|
|
if !ok {
|
|
t.Fatalf("statement 1: expected EchoStatement, got %T", program.Statements[1])
|
|
}
|
|
testIdentifier(t, echo1.Value, "x")
|
|
|
|
// Third: if statement with echo in body
|
|
ifStmt, ok := program.Statements[2].(*parser.IfStatement)
|
|
if !ok {
|
|
t.Fatalf("statement 2: expected IfStatement, got %T", program.Statements[2])
|
|
}
|
|
|
|
if len(ifStmt.Body) != 1 {
|
|
t.Fatalf("expected 1 body statement, got %d", len(ifStmt.Body))
|
|
}
|
|
|
|
bodyEcho, ok := ifStmt.Body[0].(*parser.EchoStatement)
|
|
if !ok {
|
|
t.Fatalf("if body: expected EchoStatement, got %T", ifStmt.Body[0])
|
|
}
|
|
testStringLiteral(t, bodyEcho.Value, "found x")
|
|
|
|
// Fourth: echo with table
|
|
echo2, ok := program.Statements[3].(*parser.EchoStatement)
|
|
if !ok {
|
|
t.Fatalf("statement 3: expected EchoStatement, got %T", program.Statements[3])
|
|
}
|
|
|
|
_, ok = echo2.Value.(*parser.TableLiteral)
|
|
if !ok {
|
|
t.Fatalf("expected TableLiteral in echo, got %T", echo2.Value)
|
|
}
|
|
}
|
|
|
|
func TestModuloOperator(t *testing.T) {
|
|
input := `result = 10 % 3`
|
|
|
|
l := parser.NewLexer(input)
|
|
p := parser.NewParser(l)
|
|
program := p.ParseProgram()
|
|
checkParserErrors(t, p)
|
|
|
|
if len(program.Statements) != 1 {
|
|
t.Fatalf("expected 1 statement, got %d", len(program.Statements))
|
|
}
|
|
|
|
stmt, ok := program.Statements[0].(*parser.Assignment)
|
|
if !ok {
|
|
t.Fatalf("expected AssignStatement, got %T", program.Statements[0])
|
|
}
|
|
|
|
infix, ok := stmt.Value.(*parser.InfixExpression)
|
|
if !ok {
|
|
t.Fatalf("expected InfixExpression, got %T", stmt.Value)
|
|
}
|
|
|
|
if infix.Operator != "%" {
|
|
t.Errorf("expected operator '%%', got %s", infix.Operator)
|
|
}
|
|
|
|
leftLit, ok := infix.Left.(*parser.NumberLiteral)
|
|
if !ok {
|
|
t.Fatalf("expected NumberLiteral on left, got %T", infix.Left)
|
|
}
|
|
if leftLit.Value != 10.0 {
|
|
t.Errorf("expected left value 10, got %f", leftLit.Value)
|
|
}
|
|
|
|
rightLit, ok := infix.Right.(*parser.NumberLiteral)
|
|
if !ok {
|
|
t.Fatalf("expected NumberLiteral on right, got %T", infix.Right)
|
|
}
|
|
if rightLit.Value != 3.0 {
|
|
t.Errorf("expected right value 3, got %f", rightLit.Value)
|
|
}
|
|
}
|