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

409 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 executeMako(code string) *vm.VM {
lex := lexer.New(code)
p := parser.New(lex)
program := p.ParseProgram()
bytecode := compiler.Compile(program)
virtualMachine := vm.New()
virtualMachine.Run(bytecode)
return virtualMachine
}
func TestBasicExecution(t *testing.T) {
vm := executeMako(`
// Variables and echo
x = 5;
y = 10;
sum = x + y;
`)
// Verify the sum variable
sum, found := vm.GetGlobal("sum")
assert.True(t, found)
assert.Equal(t, types.TypeNumber, sum.Type)
assert.Equal(t, 15.0, sum.Data.(float64))
// Verify x and y variables
x, found := vm.GetGlobal("x")
assert.True(t, found)
assert.Equal(t, 5.0, x.Data.(float64))
y, found := vm.GetGlobal("y")
assert.True(t, found)
assert.Equal(t, 10.0, y.Data.(float64))
}
func TestTableOperations(t *testing.T) {
vm := executeMako(`
// Table creation and access
person = {
name = "John",
age = 30,
isActive = true
};
nameValue = person["name"];
person["location"] = "New York";
locationValue = person["location"];
`)
// Verify table operations
nameValue, found := vm.GetGlobal("nameValue")
assert.True(t, found)
assert.Equal(t, types.TypeString, nameValue.Type)
assert.Equal(t, "John", nameValue.Data.(string))
locationValue, found := vm.GetGlobal("locationValue")
assert.True(t, found)
assert.Equal(t, types.TypeString, locationValue.Type)
assert.Equal(t, "New York", locationValue.Data.(string))
// Verify the person table exists
person, found := vm.GetGlobal("person")
assert.True(t, found)
assert.Equal(t, types.TypeTable, person.Type)
}
func TestConditionalExecution(t *testing.T) {
vm := executeMako(`
// If-else statements
x = 5;
if x < 10 then
result1 = "x is less than 10";
else
result1 = "x is not less than 10";
end
// Nested if-else
y = 20;
if x > y then
result2 = "x is greater than y";
elseif x < y then
result2 = "x is less than y";
else
result2 = "x equals y";
end
`)
// Verify conditional results
result1, found := vm.GetGlobal("result1")
assert.True(t, found)
assert.Equal(t, types.TypeString, result1.Type)
assert.Equal(t, "x is less than 10", result1.Data.(string))
result2, found := vm.GetGlobal("result2")
assert.True(t, found)
assert.Equal(t, types.TypeString, result2.Type)
assert.Equal(t, "x is less than y", result2.Data.(string))
}
func TestScopes(t *testing.T) {
vm := executeMako(`
// Global scope
x = 10;
globalX = x;
// Enter a new scope
{
// Local scope - variable shadowing
x = 20;
localX = x;
// New local variable
y = 30;
localY = y;
}
// Back to global scope
afterScopeX = x;
afterScopeY = y; // This should be null since y was only defined in local scope
`)
// Verify global scope values
globalX, found := vm.GetGlobal("globalX")
assert.True(t, found)
assert.Equal(t, types.TypeNumber, globalX.Type)
assert.Equal(t, 10.0, globalX.Data.(float64))
// Verify local scope values while in scope
localX, found := vm.GetGlobal("localX")
assert.True(t, found)
assert.Equal(t, types.TypeNumber, localX.Type)
assert.Equal(t, 20.0, localX.Data.(float64))
localY, found := vm.GetGlobal("localY")
assert.True(t, found)
assert.Equal(t, types.TypeNumber, localY.Type)
assert.Equal(t, 30.0, localY.Data.(float64))
// Verify variables after scope exit
afterScopeX, found := vm.GetGlobal("afterScopeX")
assert.True(t, found)
assert.Equal(t, types.TypeNumber, afterScopeX.Type)
assert.Equal(t, 10.0, afterScopeX.Data.(float64))
// y should be null since it was only defined in local scope
afterScopeY, found := vm.GetGlobal("afterScopeY")
assert.True(t, found)
assert.Equal(t, types.TypeNull, afterScopeY.Type)
}
func TestArithmeticOperations(t *testing.T) {
vm := executeMako(`
// Basic arithmetic
add_result = 5 + 10;
sub_result = 20 - 5;
mul_result = 4 * 5;
div_result = 20 / 4;
// Compound expressions
expr1 = (5 + 10) * 2;
expr2 = 5 + 10 * 2;
expr3 = -5 + 10;
`)
// Verify arithmetic results
add_result, found := vm.GetGlobal("add_result")
assert.True(t, found)
assert.Equal(t, types.TypeNumber, add_result.Type)
assert.Equal(t, 15.0, add_result.Data.(float64))
sub_result, found := vm.GetGlobal("sub_result")
assert.True(t, found)
assert.Equal(t, types.TypeNumber, sub_result.Type)
assert.Equal(t, 15.0, sub_result.Data.(float64))
mul_result, found := vm.GetGlobal("mul_result")
assert.True(t, found)
assert.Equal(t, types.TypeNumber, mul_result.Type)
assert.Equal(t, 20.0, mul_result.Data.(float64))
div_result, found := vm.GetGlobal("div_result")
assert.True(t, found)
assert.Equal(t, types.TypeNumber, div_result.Type)
assert.Equal(t, 5.0, div_result.Data.(float64))
// Verify compound expressions
expr1, found := vm.GetGlobal("expr1")
assert.True(t, found)
assert.Equal(t, types.TypeNumber, expr1.Type)
assert.Equal(t, 30.0, expr1.Data.(float64))
expr2, found := vm.GetGlobal("expr2")
assert.True(t, found)
assert.Equal(t, types.TypeNumber, expr2.Type)
assert.Equal(t, 25.0, expr2.Data.(float64))
expr3, found := vm.GetGlobal("expr3")
assert.True(t, found)
assert.Equal(t, types.TypeNumber, expr3.Type)
assert.Equal(t, 5.0, expr3.Data.(float64))
}
func TestComparisonOperations(t *testing.T) {
vm := executeMako(`
// Basic comparisons
eq_result = 5 == 5;
neq_result = 5 != 10;
lt_result = 5 < 10;
gt_result = 10 > 5;
lte_result = 5 <= 5;
gte_result = 5 >= 5;
// Compound comparisons
comp1 = 5 + 5 == 10;
comp2 = 5 * 2 != 15;
`)
// Verify comparison results
eq_result, found := vm.GetGlobal("eq_result")
assert.True(t, found)
assert.Equal(t, types.TypeBoolean, eq_result.Type)
assert.Equal(t, true, eq_result.Data.(bool))
neq_result, found := vm.GetGlobal("neq_result")
assert.True(t, found)
assert.Equal(t, types.TypeBoolean, neq_result.Type)
assert.Equal(t, true, neq_result.Data.(bool))
lt_result, found := vm.GetGlobal("lt_result")
assert.True(t, found)
assert.Equal(t, types.TypeBoolean, lt_result.Type)
assert.Equal(t, true, lt_result.Data.(bool))
gt_result, found := vm.GetGlobal("gt_result")
assert.True(t, found)
assert.Equal(t, types.TypeBoolean, gt_result.Type)
assert.Equal(t, true, gt_result.Data.(bool))
lte_result, found := vm.GetGlobal("lte_result")
assert.True(t, found)
assert.Equal(t, types.TypeBoolean, lte_result.Type)
assert.Equal(t, true, lte_result.Data.(bool))
gte_result, found := vm.GetGlobal("gte_result")
assert.True(t, found)
assert.Equal(t, types.TypeBoolean, gte_result.Type)
assert.Equal(t, true, gte_result.Data.(bool))
// Verify compound comparison results
comp1, found := vm.GetGlobal("comp1")
assert.True(t, found)
assert.Equal(t, types.TypeBoolean, comp1.Type)
assert.Equal(t, true, comp1.Data.(bool))
comp2, found := vm.GetGlobal("comp2")
assert.True(t, found)
assert.Equal(t, types.TypeBoolean, comp2.Type)
assert.Equal(t, true, comp2.Data.(bool))
}
func TestLogicalOperations(t *testing.T) {
vm := executeMako(`
// Logical operators
and_tt = true and true;
and_tf = true and false;
or_tf = true or false;
or_ff = false or false;
not_t = not true;
not_f = not false;
// Short-circuit evaluation
x = 5;
true_or_effect = true or (x = 10); // Should not change x
x_after_or = x; // Should still be 5
false_and_effect = false and (x = 15); // Should not change x
x_after_and = x; // Should still be 5
`)
// Verify logical operation results
and_tt, found := vm.GetGlobal("and_tt")
assert.True(t, found)
assert.Equal(t, types.TypeBoolean, and_tt.Type)
assert.Equal(t, true, and_tt.Data.(bool))
and_tf, found := vm.GetGlobal("and_tf")
assert.True(t, found)
assert.Equal(t, types.TypeBoolean, and_tf.Type)
assert.Equal(t, false, and_tf.Data.(bool))
or_tf, found := vm.GetGlobal("or_tf")
assert.True(t, found)
assert.Equal(t, types.TypeBoolean, or_tf.Type)
assert.Equal(t, true, or_tf.Data.(bool))
or_ff, found := vm.GetGlobal("or_ff")
assert.True(t, found)
assert.Equal(t, types.TypeBoolean, or_ff.Type)
assert.Equal(t, false, or_ff.Data.(bool))
not_t, found := vm.GetGlobal("not_t")
assert.True(t, found)
assert.Equal(t, types.TypeBoolean, not_t.Type)
assert.Equal(t, false, not_t.Data.(bool))
not_f, found := vm.GetGlobal("not_f")
assert.True(t, found)
assert.Equal(t, types.TypeBoolean, not_f.Type)
assert.Equal(t, true, not_f.Data.(bool))
// Verify short-circuit behavior
x_after_or, found := vm.GetGlobal("x_after_or")
assert.True(t, found)
assert.Equal(t, types.TypeNumber, x_after_or.Type)
assert.Equal(t, 5.0, x_after_or.Data.(float64))
x_after_and, found := vm.GetGlobal("x_after_and")
assert.True(t, found)
assert.Equal(t, types.TypeNumber, x_after_and.Type)
assert.Equal(t, 5.0, x_after_and.Data.(float64))
}
func TestComplexProgram(t *testing.T) {
vm := executeMako(`
// Define a table to store data
data = {
users = {
admin = {
name = "Admin User",
access = "full",
active = true
},
guest = {
name = "Guest User",
access = "limited",
active = true
},
blocked = {
name = "Blocked User",
access = "none",
active = false
}
},
settings = {
theme = "dark",
notifications = true,
language = "en"
}
};
// Get the user type from input (simulated)
userType = "admin";
// Check access and print message
if data["users"][userType]["active"] then
access = data["users"][userType]["access"];
if access == "full" then
message = "Welcome, Administrator!";
elseif access == "limited" then
message = "Welcome, Guest!";
else
message = "Access denied.";
end
else
message = "User account is inactive.";
end
// Update a setting
data["settings"]["theme"] = "light";
theme = data["settings"]["theme"];
// Toggle notifications
data["settings"]["notifications"] = not data["settings"]["notifications"];
notif_status = data["settings"]["notifications"];
`)
// Verify complex program results
message, found := vm.GetGlobal("message")
assert.True(t, found)
assert.Equal(t, types.TypeString, message.Type)
assert.Equal(t, "Welcome, Administrator!", message.Data.(string))
theme, found := vm.GetGlobal("theme")
assert.True(t, found)
assert.Equal(t, types.TypeString, theme.Type)
assert.Equal(t, "light", theme.Data.(string))
notif_status, found := vm.GetGlobal("notif_status")
assert.True(t, found)
assert.Equal(t, types.TypeBoolean, notif_status.Type)
assert.Equal(t, false, notif_status.Data.(bool))
}