346 lines
8.5 KiB
Go
346 lines
8.5 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|