Mako/tests/edge_test.go
2025-05-06 17:01:47 -05:00

439 lines
11 KiB
Go

package tests
import (
"testing"
assert "git.sharkk.net/Go/Assert"
"git.sharkk.net/Sharkk/Mako/compiler"
"git.sharkk.net/Sharkk/Mako/lexer"
"git.sharkk.net/Sharkk/Mako/parser"
"git.sharkk.net/Sharkk/Mako/types"
"git.sharkk.net/Sharkk/Mako/vm"
)
// Helper function to execute Mako code
func executeMakoWithErrors(code string) (*vm.VM, []string) {
lex := lexer.New(code)
p := parser.New(lex)
program := p.ParseProgram()
errors := p.Errors()
bytecode := compiler.Compile(program)
virtualMachine := vm.New()
virtualMachine.Run(bytecode)
return virtualMachine, errors
}
func TestTypeConversions(t *testing.T) {
// Test automatic type conversions in operations
vm, errors := executeMakoWithErrors(`
// String concatenation
result1 = "Hello " + "World"; // Should work
// Using boolean in a condition
if true then
result2 = "Boolean works in condition";
end
// Numeric conditions
if 1 then
result3 = "Numeric 1 is truthy";
end
if 0 then
result4 = "This should not execute";
else
result4 = "Numeric 0 is falsy";
end
`)
assert.Equal(t, 0, len(errors))
// Verify results
result1, found := vm.GetGlobal("result1")
assert.True(t, found)
assert.Equal(t, types.TypeString, result1.Type)
assert.Equal(t, "Hello World", result1.Data.(string))
result2, found := vm.GetGlobal("result2")
assert.True(t, found)
assert.Equal(t, types.TypeString, result2.Type)
assert.Equal(t, "Boolean works in condition", result2.Data.(string))
result3, found := vm.GetGlobal("result3")
assert.True(t, found)
assert.Equal(t, types.TypeString, result3.Type)
assert.Equal(t, "Numeric 1 is truthy", result3.Data.(string))
result4, found := vm.GetGlobal("result4")
assert.True(t, found)
assert.Equal(t, types.TypeString, result4.Type)
assert.Equal(t, "Numeric 0 is falsy", result4.Data.(string))
}
func TestEdgeCases(t *testing.T) {
// Test edge cases that might cause issues
vm, errors := executeMakoWithErrors(`
// Deep nesting
table = {
level1 = {
level2 = {
level3 = {
level4 = {
value = "Deep nesting"
}
}
}
}
};
result1 = table["level1"]["level2"]["level3"]["level4"]["value"];
// Empty tables
emptyTable = {};
result2 = emptyTable["nonexistent"]; // Should return null
// Table with invalid access
someTable = { key = "value" };
someTable[123] = "numeric key"; // Should work
result3 = someTable[123]; // Should retrieve the value
`)
assert.Equal(t, 0, len(errors))
// Verify deep nesting result
result1, found := vm.GetGlobal("result1")
assert.True(t, found)
assert.Equal(t, types.TypeString, result1.Type)
assert.Equal(t, "Deep nesting", result1.Data.(string))
// Verify empty table result
result2, found := vm.GetGlobal("result2")
assert.True(t, found)
assert.Equal(t, types.TypeNull, result2.Type)
// Verify numeric key result
result3, found := vm.GetGlobal("result3")
assert.True(t, found)
assert.Equal(t, types.TypeString, result3.Type)
assert.Equal(t, "numeric key", result3.Data.(string))
}
func TestErrorHandling(t *testing.T) {
// Test error handling in the parser
_, errors := executeMakoWithErrors(`
// Invalid syntax - missing semicolon
x = 5
y = 10;
`)
// Should have at least one error
assert.True(t, len(errors) > 0)
// Test parser recovery
_, errors = executeMakoWithErrors(`
// Missing end keyword
if x < 10 then
echo "x is less than 10";
// end - missing
`)
// Should have at least one error
assert.True(t, len(errors) > 0)
}
func TestNestedScopes(t *testing.T) {
// Test nested scopes and variable shadowing
vm, errors := executeMakoWithErrors(`
x = "global";
{
echo x; // Should be "global"
x = "outer";
echo x; // Should be "outer"
{
echo x; // Should be "outer"
x = "inner";
echo x; // Should be "inner"
{
echo x; // Should be "inner"
x = "innermost";
echo x; // Should be "innermost"
}
echo x; // Should be "inner" again
}
echo x; // Should be "outer" again
}
echo x; // Should be "global" again
result = x;
`)
assert.Equal(t, 0, len(errors))
// Verify that x returns to its global value
result, found := vm.GetGlobal("result")
assert.True(t, found)
assert.Equal(t, types.TypeString, result.Type)
assert.Equal(t, "global", result.Data.(string))
// Also verify x directly
x, found := vm.GetGlobal("x")
assert.True(t, found)
assert.Equal(t, types.TypeString, x.Type)
assert.Equal(t, "global", x.Data.(string))
}
func TestComplexExpressions(t *testing.T) {
// Test complex expressions with multiple operators
vm, errors := executeMakoWithErrors(`
// Arithmetic precedence
result1 = 5 + 10 * 2; // Should be 25, not 30
// Parentheses override precedence
result2 = (5 + 10) * 2; // Should be 30
// Combined comparison and logical operators
x = 5;
y = 10;
z = 15;
result3 = x < y and y < z; // Should be true
result4 = x > y or y < z; // Should be true
result5 = not (x > y); // Should be true
// Complex conditional
if x < y and y < z then
result6 = "Condition passed";
else
result6 = "Condition failed";
end
`)
assert.Equal(t, 0, len(errors))
// Verify arithmetic precedence
result1, found := vm.GetGlobal("result1")
assert.True(t, found)
assert.Equal(t, types.TypeNumber, result1.Type)
assert.Equal(t, 25.0, result1.Data.(float64))
// Verify parentheses precedence
result2, found := vm.GetGlobal("result2")
assert.True(t, found)
assert.Equal(t, types.TypeNumber, result2.Type)
assert.Equal(t, 30.0, result2.Data.(float64))
// Verify logical operators
result3, found := vm.GetGlobal("result3")
assert.True(t, found)
assert.Equal(t, types.TypeBoolean, result3.Type)
assert.Equal(t, true, result3.Data.(bool))
result4, found := vm.GetGlobal("result4")
assert.True(t, found)
assert.Equal(t, types.TypeBoolean, result4.Type)
assert.Equal(t, true, result4.Data.(bool))
result5, found := vm.GetGlobal("result5")
assert.True(t, found)
assert.Equal(t, types.TypeBoolean, result5.Type)
assert.Equal(t, true, result5.Data.(bool))
// Verify complex conditional
result6, found := vm.GetGlobal("result6")
assert.True(t, found)
assert.Equal(t, types.TypeString, result6.Type)
assert.Equal(t, "Condition passed", result6.Data.(string))
}
func TestNestedTables(t *testing.T) {
// Test nested tables and complex access patterns
vm, errors := executeMakoWithErrors(`
// Define a nested table
config = {
server = {
host = "localhost",
port = 8080,
settings = {
timeout = 30,
retries = 3
}
},
database = {
host = "db.example.com",
port = 5432,
credentials = {
username = "admin",
password = "secret"
}
}
};
// Access nested values
result1 = config["server"]["host"];
result2 = config["server"]["settings"]["timeout"];
result3 = config["database"]["credentials"]["username"];
// Update nested values
config["server"]["settings"]["timeout"] = 60;
result4 = config["server"]["settings"]["timeout"];
// Add new nested values
config["logging"] = {
level = "info",
file = "app.log"
};
result5 = config["logging"]["level"];
`)
assert.Equal(t, 0, len(errors))
// Verify nested table access
result1, found := vm.GetGlobal("result1")
assert.True(t, found)
assert.Equal(t, types.TypeString, result1.Type)
assert.Equal(t, "localhost", result1.Data.(string))
result2, found := vm.GetGlobal("result2")
assert.True(t, found)
assert.Equal(t, types.TypeNumber, result2.Type)
assert.Equal(t, 30.0, result2.Data.(float64))
result3, found := vm.GetGlobal("result3")
assert.True(t, found)
assert.Equal(t, types.TypeString, result3.Type)
assert.Equal(t, "admin", result3.Data.(string))
// Verify nested table update
result4, found := vm.GetGlobal("result4")
assert.True(t, found)
assert.Equal(t, types.TypeNumber, result4.Type)
assert.Equal(t, 60.0, result4.Data.(float64))
// Verify adding new nested values
result5, found := vm.GetGlobal("result5")
assert.True(t, found)
assert.Equal(t, types.TypeString, result5.Type)
assert.Equal(t, "info", result5.Data.(string))
}
func TestTableAsArguments(t *testing.T) {
// Test using tables as arguments
vm, errors := executeMakoWithErrors(`
// Define a table
person = {
name = "John",
age = 30
};
// Use as index
lookup = {
John = "Developer",
Jane = "Designer"
};
// Access using value from another table
role = lookup[person["name"]];
result1 = role; // Should print "Developer"
`)
assert.Equal(t, 0, len(errors))
// Verify table as argument
result1, found := vm.GetGlobal("result1")
assert.True(t, found)
assert.Equal(t, types.TypeString, result1.Type)
assert.Equal(t, "Developer", result1.Data.(string))
// Test complex table indexing - this is more advanced and might not work yet
complexVM, complexErrors := executeMakoWithErrors(`
// Test with table as key - might not work in current implementation
matrix = {};
// Instead use a string representation
matrix["0,0"] = "origin";
result2 = matrix["0,0"];
`)
// For now, we expect no errors with the string-based approach
assert.Equal(t, 0, len(complexErrors))
result2, found := complexVM.GetGlobal("result2")
assert.True(t, found)
assert.Equal(t, types.TypeString, result2.Type)
assert.Equal(t, "origin", result2.Data.(string))
}
func TestTableEquality(t *testing.T) {
// Test table equality and using tables as keys
vm, errors := executeMakoWithErrors(`
// Test table equality
t1 = { x = 1, y = 2 };
t2 = { x = 1, y = 2 };
t3 = { x = 1, y = 3 };
// Test basic equality
eq_result1 = t1 == t2; // Should be true
eq_result2 = t1 == t3; // Should be false
eq_result3 = t2 == t3; // Should be false
// Test using tables as keys
lookup = {};
lookup[t1] = "first table";
lookup[t3] = "third table";
// Try to retrieve values using table keys
result1 = lookup[t1]; // Should be "first table"
result2 = lookup[t2]; // Should also be "first table" because t1 and t2 are equal
result3 = lookup[t3]; // Should be "third table"
// Test with nested tables
nested1 = { table = t1, name = "nested1" };
nested2 = { table = t2, name = "nested2" };
nested3 = { table = t3, name = "nested3" };
// Check equality with nested tables
neq_result1 = nested1 == nested2; // Should be false (different names)
neq_result2 = nested1 == nested3; // Should be false (different tables and names)
`)
assert.Equal(t, 0, len(errors))
// Verify basic table equality results
eq_result1, found := vm.GetGlobal("eq_result1")
assert.True(t, found)
assert.Equal(t, types.TypeBoolean, eq_result1.Type)
assert.Equal(t, true, eq_result1.Data.(bool))
eq_result2, found := vm.GetGlobal("eq_result2")
assert.True(t, found)
assert.Equal(t, types.TypeBoolean, eq_result2.Type)
assert.Equal(t, false, eq_result2.Data.(bool))
// Verify table as key results
result1, found := vm.GetGlobal("result1")
assert.True(t, found)
assert.Equal(t, types.TypeString, result1.Type)
assert.Equal(t, "first table", result1.Data.(string))
result2, found := vm.GetGlobal("result2")
assert.True(t, found)
assert.Equal(t, types.TypeString, result2.Type)
assert.Equal(t, "first table", result2.Data.(string))
result3, found := vm.GetGlobal("result3")
assert.True(t, found)
assert.Equal(t, types.TypeString, result3.Type)
assert.Equal(t, "third table", result3.Data.(string))
// Verify nested table equality
neq_result1, found := vm.GetGlobal("neq_result1")
assert.True(t, found)
assert.Equal(t, types.TypeBoolean, neq_result1.Type)
assert.Equal(t, false, neq_result1.Data.(bool))
}