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)) }