2024-09-12 23:01:54 +08:00
|
|
|
import { assertEquals } from "@std/assert/equals";
|
2024-10-09 01:53:09 +08:00
|
|
|
import {
|
|
|
|
LuaEnv,
|
|
|
|
LuaNativeJSFunction,
|
2024-10-20 21:06:23 +08:00
|
|
|
LuaStackFrame,
|
2024-10-09 01:53:09 +08:00
|
|
|
luaValueToJS,
|
|
|
|
singleResult,
|
|
|
|
} from "./runtime.ts";
|
2024-09-24 16:15:22 +08:00
|
|
|
import { parse } from "./parse.ts";
|
2024-09-27 15:11:03 +08:00
|
|
|
import type { LuaBlock, LuaFunctionCallStatement } from "./ast.ts";
|
|
|
|
import { evalExpression, evalStatement } from "./eval.ts";
|
2024-10-03 23:55:51 +08:00
|
|
|
import { luaBuildStandardEnv } from "$common/space_lua/stdlib.ts";
|
2024-09-12 23:01:54 +08:00
|
|
|
|
|
|
|
function evalExpr(s: string, e = new LuaEnv()): any {
|
2024-10-20 21:06:23 +08:00
|
|
|
const node = parse(`e(${s})`).statements[0] as LuaFunctionCallStatement;
|
|
|
|
const sf = new LuaStackFrame(e, node.ctx);
|
2024-09-30 18:50:54 +08:00
|
|
|
return evalExpression(
|
2024-10-20 21:06:23 +08:00
|
|
|
node.call.args[0],
|
2024-09-30 18:50:54 +08:00
|
|
|
e,
|
2024-10-20 21:06:23 +08:00
|
|
|
sf,
|
2024-09-30 18:50:54 +08:00
|
|
|
);
|
2024-09-12 23:01:54 +08:00
|
|
|
}
|
|
|
|
|
2024-09-27 15:11:03 +08:00
|
|
|
function evalBlock(s: string, e = new LuaEnv()): Promise<void> {
|
2024-10-20 21:06:23 +08:00
|
|
|
const node = parse(s) as LuaBlock;
|
|
|
|
const sf = new LuaStackFrame(e, node.ctx);
|
|
|
|
return evalStatement(node, e, sf);
|
2024-09-27 15:11:03 +08:00
|
|
|
}
|
|
|
|
|
2024-09-12 23:01:54 +08:00
|
|
|
Deno.test("Evaluator test", async () => {
|
2024-09-30 18:50:54 +08:00
|
|
|
const env = new LuaEnv();
|
|
|
|
env.set("test", new LuaNativeJSFunction((n) => n));
|
|
|
|
env.set("asyncTest", new LuaNativeJSFunction((n) => Promise.resolve(n)));
|
|
|
|
|
|
|
|
// Basic arithmetic
|
|
|
|
assertEquals(evalExpr(`1 + 2 + 3 - 3`), 3);
|
|
|
|
assertEquals(evalExpr(`4 // 3`), 1);
|
|
|
|
assertEquals(evalExpr(`4 % 3`), 1);
|
|
|
|
|
|
|
|
// Strings
|
|
|
|
assertEquals(evalExpr(`"a" .. "b"`), "ab");
|
|
|
|
|
|
|
|
// Logic
|
|
|
|
assertEquals(evalExpr(`true and false`), false);
|
|
|
|
assertEquals(evalExpr(`true or false`), true);
|
|
|
|
assertEquals(evalExpr(`not true`), false);
|
|
|
|
|
|
|
|
// Tables
|
|
|
|
const tbl = evalExpr(`{3, 1, 2}`);
|
|
|
|
assertEquals(tbl.get(1), 3);
|
|
|
|
assertEquals(tbl.get(2), 1);
|
|
|
|
assertEquals(tbl.get(3), 2);
|
2024-10-09 01:53:09 +08:00
|
|
|
assertEquals(luaValueToJS(tbl), [3, 1, 2]);
|
2024-09-30 18:50:54 +08:00
|
|
|
|
2024-10-09 01:53:09 +08:00
|
|
|
assertEquals(luaValueToJS(evalExpr(`{name=test("Zef"), age=100}`, env)), {
|
2024-09-30 18:50:54 +08:00
|
|
|
name: "Zef",
|
|
|
|
age: 100,
|
|
|
|
});
|
|
|
|
|
|
|
|
assertEquals(
|
2024-10-09 01:53:09 +08:00
|
|
|
luaValueToJS(await evalExpr(`{name="Zef", age=asyncTest(100)}`, env)),
|
2024-09-30 18:50:54 +08:00
|
|
|
{
|
|
|
|
name: "Zef",
|
|
|
|
age: 100,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
2024-10-09 01:53:09 +08:00
|
|
|
const result = evalExpr(`{[3+2]=1, ["a".."b"]=2}`);
|
|
|
|
assertEquals(result.get(5), 1);
|
|
|
|
assertEquals(result.get("ab"), 2);
|
2024-09-30 18:50:54 +08:00
|
|
|
|
|
|
|
assertEquals(evalExpr(`#{}`), 0);
|
|
|
|
assertEquals(evalExpr(`#{1, 2, 3}`), 3);
|
|
|
|
|
|
|
|
// Unary operators
|
|
|
|
assertEquals(await evalExpr(`-asyncTest(3)`, env), -3);
|
|
|
|
|
|
|
|
// Function calls
|
|
|
|
assertEquals(singleResult(evalExpr(`test(3)`, env)), 3);
|
|
|
|
assertEquals(singleResult(await evalExpr(`asyncTest(3) + 1`, env)), 4);
|
2024-10-03 23:55:51 +08:00
|
|
|
|
2024-10-04 00:52:44 +08:00
|
|
|
// Function expressions and table access
|
|
|
|
assertEquals(
|
|
|
|
await evalExpr(`(function() return {name="John"} end)().name`),
|
|
|
|
"John",
|
|
|
|
);
|
|
|
|
|
2024-10-03 23:55:51 +08:00
|
|
|
// Function definitions
|
|
|
|
const fn = evalExpr(`function(a, b) return a + b end`);
|
|
|
|
assertEquals(fn.body.parameters, ["a", "b"]);
|
2024-09-12 23:01:54 +08:00
|
|
|
});
|
2024-09-27 15:11:03 +08:00
|
|
|
|
|
|
|
Deno.test("Statement evaluation", async () => {
|
2024-09-30 18:50:54 +08:00
|
|
|
const env = new LuaEnv();
|
|
|
|
env.set("test", new LuaNativeJSFunction((n) => n));
|
|
|
|
env.set("asyncTest", new LuaNativeJSFunction((n) => Promise.resolve(n)));
|
|
|
|
|
|
|
|
assertEquals(undefined, await evalBlock(`a = 3`, env));
|
|
|
|
assertEquals(env.get("a"), 3);
|
|
|
|
assertEquals(undefined, await evalBlock(`b = test(3)`, env));
|
|
|
|
assertEquals(env.get("b"), 3);
|
|
|
|
|
|
|
|
await evalBlock(`c = asyncTest(3)`, env);
|
|
|
|
assertEquals(env.get("c"), 3);
|
|
|
|
|
|
|
|
// Multiple assignments
|
|
|
|
const env2 = new LuaEnv();
|
|
|
|
assertEquals(undefined, await evalBlock(`a, b = 1, 2`, env2));
|
|
|
|
assertEquals(env2.get("a"), 1);
|
|
|
|
assertEquals(env2.get("b"), 2);
|
|
|
|
|
|
|
|
// Other lvalues
|
|
|
|
const env3 = new LuaEnv();
|
|
|
|
await evalBlock(`tbl = {1, 2, 3}`, env3);
|
|
|
|
await evalBlock(`tbl[1] = 3`, env3);
|
2024-10-09 01:53:09 +08:00
|
|
|
assertEquals(luaValueToJS(env3.get("tbl")), [3, 2, 3]);
|
2024-09-30 18:50:54 +08:00
|
|
|
await evalBlock("tbl.name = 'Zef'", env3);
|
|
|
|
assertEquals(env3.get("tbl").get("name"), "Zef");
|
|
|
|
await evalBlock(`tbl[2] = {age=10}`, env3);
|
|
|
|
await evalBlock(`tbl[2].age = 20`, env3);
|
|
|
|
assertEquals(env3.get("tbl").get(2).get("age"), 20);
|
|
|
|
|
|
|
|
// Blocks and scopes
|
|
|
|
const env4 = new LuaEnv();
|
|
|
|
env4.set("print", new LuaNativeJSFunction(console.log));
|
|
|
|
await evalBlock(
|
|
|
|
`
|
2024-10-03 23:55:51 +08:00
|
|
|
a = 1
|
2024-09-27 15:11:03 +08:00
|
|
|
do
|
|
|
|
-- sets global a to 3
|
|
|
|
a = 3
|
|
|
|
print("The number is: " .. a)
|
|
|
|
end`,
|
2024-09-30 18:50:54 +08:00
|
|
|
env4,
|
|
|
|
);
|
|
|
|
assertEquals(env4.get("a"), 3);
|
2024-09-27 15:11:03 +08:00
|
|
|
|
2024-09-30 18:50:54 +08:00
|
|
|
const env5 = new LuaEnv();
|
|
|
|
env5.set("print", new LuaNativeJSFunction(console.log));
|
2024-09-27 15:11:03 +08:00
|
|
|
|
2024-09-30 18:50:54 +08:00
|
|
|
await evalBlock(
|
|
|
|
`
|
2024-09-27 15:11:03 +08:00
|
|
|
a = 1
|
|
|
|
if a > 0 then
|
|
|
|
a = 3
|
|
|
|
else
|
|
|
|
a = 0
|
|
|
|
end`,
|
2024-09-30 18:50:54 +08:00
|
|
|
env5,
|
|
|
|
);
|
|
|
|
assertEquals(env5.get("a"), 3);
|
2024-09-27 15:11:03 +08:00
|
|
|
|
2024-09-30 18:50:54 +08:00
|
|
|
await evalBlock(
|
|
|
|
`
|
2024-09-27 15:11:03 +08:00
|
|
|
if a < 0 then
|
|
|
|
a = -1
|
|
|
|
elseif a > 0 then
|
|
|
|
a = 1
|
|
|
|
else
|
|
|
|
a = 0
|
|
|
|
end`,
|
2024-09-30 18:50:54 +08:00
|
|
|
env5,
|
|
|
|
);
|
|
|
|
assertEquals(env5.get("a"), 1);
|
2024-09-27 15:11:03 +08:00
|
|
|
|
2024-09-30 18:50:54 +08:00
|
|
|
await evalBlock(
|
|
|
|
`
|
2024-09-27 15:11:03 +08:00
|
|
|
var = 1
|
|
|
|
do
|
|
|
|
local var
|
|
|
|
var = 2
|
|
|
|
end`,
|
2024-09-30 18:50:54 +08:00
|
|
|
env5,
|
|
|
|
);
|
|
|
|
assertEquals(env5.get("var"), 1);
|
|
|
|
|
|
|
|
// While loop
|
|
|
|
const env6 = new LuaEnv();
|
|
|
|
await evalBlock(
|
|
|
|
`
|
2024-09-27 15:11:03 +08:00
|
|
|
c = 0
|
|
|
|
while true do
|
|
|
|
c = c + 1
|
|
|
|
if c == 3 then
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
`,
|
2024-09-30 18:50:54 +08:00
|
|
|
env6,
|
|
|
|
);
|
|
|
|
assertEquals(env6.get("c"), 3);
|
|
|
|
|
|
|
|
// Repeat loop
|
|
|
|
const env7 = new LuaEnv();
|
|
|
|
await evalBlock(
|
|
|
|
`
|
2024-09-27 15:11:03 +08:00
|
|
|
c = 0
|
|
|
|
repeat
|
|
|
|
c = c + 1
|
|
|
|
if c == 3 then
|
|
|
|
break
|
|
|
|
end
|
|
|
|
until false
|
|
|
|
`,
|
2024-09-30 18:50:54 +08:00
|
|
|
env7,
|
|
|
|
);
|
|
|
|
assertEquals(env7.get("c"), 3);
|
|
|
|
|
|
|
|
// Function definition and calling
|
|
|
|
const env8 = new LuaEnv();
|
|
|
|
env8.set("print", new LuaNativeJSFunction(console.log));
|
|
|
|
await evalBlock(
|
|
|
|
`
|
2024-09-27 23:09:25 +08:00
|
|
|
function test(a)
|
|
|
|
return a + 1
|
|
|
|
end
|
|
|
|
print("3 + 1 = " .. test(3))
|
|
|
|
`,
|
2024-09-30 18:50:54 +08:00
|
|
|
env8,
|
|
|
|
);
|
2024-10-03 23:55:51 +08:00
|
|
|
|
|
|
|
// Local fucntion definition
|
|
|
|
const env9 = new LuaEnv();
|
|
|
|
env9.set("print", new LuaNativeJSFunction(console.log));
|
|
|
|
await evalBlock(
|
|
|
|
`
|
|
|
|
local function test(a)
|
|
|
|
return a + 1
|
|
|
|
end
|
|
|
|
print("3 + 1 = " .. test(3))
|
|
|
|
`,
|
|
|
|
env9,
|
|
|
|
);
|
|
|
|
|
|
|
|
// For loop over range
|
|
|
|
const env10 = new LuaEnv();
|
|
|
|
await evalBlock(
|
|
|
|
`
|
|
|
|
c = 0
|
|
|
|
for i = 1, 3 do
|
|
|
|
c = c + i
|
|
|
|
end
|
|
|
|
`,
|
|
|
|
env10,
|
|
|
|
);
|
|
|
|
assertEquals(env10.get("c"), 6);
|
|
|
|
|
|
|
|
// For loop over iterator
|
|
|
|
const env11 = new LuaEnv(luaBuildStandardEnv());
|
|
|
|
await evalBlock(
|
|
|
|
`
|
|
|
|
function fruits()
|
|
|
|
local list = { "apple", "banana", "cherry" }
|
|
|
|
-- Track index internally
|
|
|
|
local index = 0
|
|
|
|
|
|
|
|
return function()
|
|
|
|
index = index + 1
|
|
|
|
if list[index] then
|
|
|
|
return list[index]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
for fruit in fruits() do
|
|
|
|
print("Fruit: " .. fruit)
|
|
|
|
end
|
|
|
|
`,
|
|
|
|
env11,
|
|
|
|
);
|
|
|
|
|
|
|
|
await evalBlock(
|
|
|
|
`
|
|
|
|
for _, f in ipairs({ "apple", "banana", "cherry" }) do
|
|
|
|
print("Fruit: " .. f)
|
|
|
|
end`,
|
|
|
|
luaBuildStandardEnv(),
|
|
|
|
);
|
2024-09-27 15:11:03 +08:00
|
|
|
});
|