fengari
Version:
A Lua VM written in JS ES6 targeting the browser
436 lines (333 loc) • 10.1 kB
JavaScript
;
const lua = require('../src/lua.js');
const lauxlib = require('../src/lauxlib.js');
const lualib = require('../src/lualib.js');
const {to_luastring} = require("../src/fengaricore.js");
test('print', () => {
let L = lauxlib.luaL_newstate();
if (!L) throw Error("failed to create lua state");
let luaCode = `
print("hello", "world", 123)
`;
{
lualib.luaL_openlibs(L);
expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
lua.lua_call(L, 0, -1);
}
});
test('setmetatable, getmetatable', () => {
let L = lauxlib.luaL_newstate();
if (!L) throw Error("failed to create lua state");
let luaCode = `
local mt = {
__index = function ()
return "hello"
end
}
local t = {}
setmetatable(t, mt);
return t[1], getmetatable(t)
`;
{
lualib.luaL_openlibs(L);
expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
lua.lua_call(L, 0, -1);
}
expect(lua.lua_tojsstring(L, -2))
.toBe("hello");
expect(lua.lua_istable(L, -1)).toBe(true);
});
test('rawequal', () => {
let L = lauxlib.luaL_newstate();
if (!L) throw Error("failed to create lua state");
let luaCode = `
local mt = {
__eq = function ()
return true
end
}
local t1 = {}
local t2 = {}
setmetatable(t1, mt);
return rawequal(t1, t2), t1 == t2
`;
{
lualib.luaL_openlibs(L);
expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
lua.lua_call(L, 0, -1);
}
expect(lua.lua_toboolean(L, -2)).toBe(false);
expect(lua.lua_toboolean(L, -1)).toBe(true);
});
test('rawset, rawget', () => {
let L = lauxlib.luaL_newstate();
if (!L) throw Error("failed to create lua state");
let luaCode = `
local mt = {
__newindex = function (table, key, value)
rawset(table, key, "hello")
end
}
local t = {}
setmetatable(t, mt);
t["yo"] = "bye"
rawset(t, "yoyo", "bye")
return rawget(t, "yo"), t["yo"], rawget(t, "yoyo"), t["yoyo"]
`;
{
lualib.luaL_openlibs(L);
expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
lua.lua_call(L, 0, -1);
}
expect(lua.lua_tojsstring(L, -4))
.toBe("hello");
expect(lua.lua_tojsstring(L, -3))
.toBe("hello");
expect(lua.lua_tojsstring(L, -2))
.toBe("bye");
expect(lua.lua_tojsstring(L, -1))
.toBe("bye");
});
test('type', () => {
let L = lauxlib.luaL_newstate();
if (!L) throw Error("failed to create lua state");
let luaCode = `
return type(1), type(true), type("hello"), type({}), type(nil)
`;
{
lualib.luaL_openlibs(L);
expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
lua.lua_call(L, 0, -1);
}
expect(lua.lua_tojsstring(L, -5))
.toBe("number");
expect(lua.lua_tojsstring(L, -4))
.toBe("boolean");
expect(lua.lua_tojsstring(L, -3))
.toBe("string");
expect(lua.lua_tojsstring(L, -2))
.toBe("table");
expect(lua.lua_tojsstring(L, -1))
.toBe("nil");
});
test('error', () => {
let L = lauxlib.luaL_newstate();
if (!L) throw Error("failed to create lua state");
let luaCode = `
error("you fucked up")
`;
{
lualib.luaL_openlibs(L);
expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
expect(() => {
lua.lua_call(L, 0, -1);
}).toThrow(/you fucked up/);
}
});
test('error, protected', () => {
let L = lauxlib.luaL_newstate();
if (!L) throw Error("failed to create lua state");
let luaCode = `
error("you fucked up")
`;
{
lualib.luaL_openlibs(L);
expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
lua.lua_pcall(L, 0, -1, 0);
}
expect(lua.lua_tojsstring(L, -1)).toMatch(/you fucked up/);
});
test('pcall', () => {
let L = lauxlib.luaL_newstate();
if (!L) throw Error("failed to create lua state");
let luaCode = `
local willFail = function ()
error("you fucked up")
end
return pcall(willFail)
`;
{
lualib.luaL_openlibs(L);
expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
lua.lua_call(L, 0, -1);
}
expect(lua.lua_tojsstring(L, -1)).toMatch(/you fucked up/);
});
test('xpcall', () => {
let L = lauxlib.luaL_newstate();
if (!L) throw Error("failed to create lua state");
let luaCode = `
local willFail = function ()
error("you fucked up")
end
local msgh = function (err)
return "Something's wrong: " .. err
end
return xpcall(willFail, msgh)
`;
{
lualib.luaL_openlibs(L);
expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
lua.lua_call(L, 0, -1);
}
expect(lua.lua_tojsstring(L, -1)).toMatch(/Something's wrong: .*you fucked up/);
});
test('ipairs', () => {
let L = lauxlib.luaL_newstate();
if (!L) throw Error("failed to create lua state");
let luaCode = `
local t = {1, 2, 3, 4, 5, ['yo'] = 'lo'}
local sum = 0
for i, v in ipairs(t) do
sum = sum + v
end
return sum
`;
{
lualib.luaL_openlibs(L);
expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
lua.lua_call(L, 0, -1);
}
expect(lua.lua_tointeger(L, -1)).toBe(15);
});
test('select', () => {
let L = lauxlib.luaL_newstate();
if (!L) throw Error("failed to create lua state");
let luaCode = `
return {select('#', 1, 2, 3)}, {select(2, 1, 2, 3)}, {select(-2, 1, 2, 3)}
`;
{
lualib.luaL_openlibs(L);
expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
lua.lua_call(L, 0, -1);
}
expect([...lua.lua_topointer(L, -3).strong.entries()].map(e => e[1].value.value))
.toEqual([3]);
expect([...lua.lua_topointer(L, -2).strong.entries()].map(e => e[1].value.value).sort())
.toEqual([2, 3]);
expect([...lua.lua_topointer(L, -1).strong.entries()].map(e => e[1].value.value).sort())
.toEqual([2, 3]);
});
test('tonumber', () => {
let L = lauxlib.luaL_newstate();
if (!L) throw Error("failed to create lua state");
let luaCode = `
return tonumber('foo'),
tonumber('123'),
tonumber('12.3'),
tonumber('az', 36),
tonumber('10', 2)
`;
{
lualib.luaL_openlibs(L);
expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
lua.lua_call(L, 0, -1);
}
expect(lua.lua_isnil(L, -5)).toBe(true);
expect(lua.lua_tonumber(L, -4)).toBe(123);
expect(lua.lua_tonumber(L, -3)).toBe(12.3);
expect(lua.lua_tonumber(L, -2)).toBe(395);
expect(lua.lua_tonumber(L, -1)).toBe(2);
});
test('assert', () => {
let L = lauxlib.luaL_newstate();
if (!L) throw Error("failed to create lua state");
let luaCode = `
assert(1 < 0, "this doesn't makes sense")
`;
{
lualib.luaL_openlibs(L);
expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
lua.lua_pcall(L, 0, -1, 0);
}
expect(lua.lua_tojsstring(L, -1)).toMatch(/this doesn't makes sense/);
});
test('rawlen', () => {
let L = lauxlib.luaL_newstate();
if (!L) throw Error("failed to create lua state");
let luaCode = `
return rawlen({1, 2, 3}), rawlen('hello')
`;
{
lualib.luaL_openlibs(L);
expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
lua.lua_call(L, 0, -1);
}
expect(lua.lua_tonumber(L, -2)).toBe(3);
expect(lua.lua_tonumber(L, -1)).toBe(5);
});
test('next', () => {
let L = lauxlib.luaL_newstate();
if (!L) throw Error("failed to create lua state");
let luaCode = `
local total = 0
local t = {
1,
two = 2,
3,
four = 4
}
for k,v in next, t, nil do
total = total + v
end
return total
`;
{
lualib.luaL_openlibs(L);
expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
lua.lua_call(L, 0, -1);
}
expect(lua.lua_tonumber(L, -1)).toBe(10);
});
test('pairs', () => {
let L = lauxlib.luaL_newstate();
if (!L) throw Error("failed to create lua state");
let luaCode = `
local total = 0
local t = {
1,
two = 2,
3,
four = 4
}
for k,v in pairs(t) do
total = total + v
end
return total
`;
{
lualib.luaL_openlibs(L);
expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
lua.lua_call(L, 0, -1);
}
expect(lua.lua_tonumber(L, -1)).toBe(10);
});
test('pairs with __pairs', () => {
let L = lauxlib.luaL_newstate();
if (!L) throw Error("failed to create lua state");
let luaCode = `
local total = 0
local mt = {
__pairs = function(t)
return next, {5, 6, 7, 8}, nil
end
}
local t = {
1,
two = 2,
3,
four = 4
}
setmetatable(t, mt)
for k,v in pairs(t) do
total = total + v
end
return total
`;
{
lualib.luaL_openlibs(L);
expect(lauxlib.luaL_loadstring(L, to_luastring(luaCode))).toBe(lua.LUA_OK);
lua.lua_call(L, 0, -1);
}
expect(lua.lua_tonumber(L, -1)).toBe(26);
});