2020-11-05 23:20:28 +00:00
|
|
|
-- CC0/Unlicense Emilia 2020
|
|
|
|
|
2020-11-01 02:55:54 +00:00
|
|
|
local tlang = {}
|
|
|
|
|
2020-11-05 19:11:17 +00:00
|
|
|
local prefix = ""
|
|
|
|
if minetest ~= nil then
|
|
|
|
prefix = minetest.get_modpath(minetest.get_current_modname()) .. "/"
|
|
|
|
end
|
|
|
|
|
2020-11-06 00:13:21 +00:00
|
|
|
local function merge_tables(l1, l2)
|
|
|
|
local out = {}
|
|
|
|
|
|
|
|
for k, v in pairs(l1) do
|
|
|
|
out[k] = v
|
|
|
|
end
|
|
|
|
|
|
|
|
for k, v in pairs(l2) do
|
|
|
|
out[k] = v
|
|
|
|
end
|
|
|
|
|
|
|
|
return out
|
|
|
|
end
|
|
|
|
|
|
|
|
local function load_api_file(file)
|
2020-12-11 02:59:40 +00:00
|
|
|
loadfile(prefix .. file)(tlang)
|
2020-11-06 00:13:21 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
load_api_file("tlang_lex.lua")
|
|
|
|
load_api_file("tlang_parse.lua")
|
|
|
|
load_api_file("tlang_vm.lua")
|
|
|
|
|
|
|
|
|
|
|
|
function tlang.combine_builtins(b1, b2)
|
|
|
|
return merge_tables(b1, b2)
|
|
|
|
end
|
|
|
|
|
|
|
|
function tlang.construct_builtins(builtins)
|
|
|
|
return merge_tables(tlang.builtins, builtins)
|
|
|
|
end
|
2020-11-01 02:55:54 +00:00
|
|
|
|
|
|
|
-- TODO
|
|
|
|
--[[
|
|
|
|
lexer should include line/character number in symbols
|
|
|
|
error messages
|
|
|
|
maps should be able to have out of order number indexes (like [1 2 3 10:"Out of order"])
|
|
|
|
map.key accessing syntax
|
2020-11-04 02:57:48 +00:00
|
|
|
parse as identifier, include . as identifier character, split on . and thats the indexing tree
|
2020-11-01 02:55:54 +00:00
|
|
|
--]]
|
|
|
|
|
|
|
|
function tlang.run(state)
|
|
|
|
while true do
|
|
|
|
local more = tlang.step(state)
|
|
|
|
if more == true or more == nil then
|
|
|
|
-- continue along
|
|
|
|
elseif type(more) == "string" then
|
|
|
|
print(more) -- error
|
|
|
|
elseif more == false then
|
|
|
|
return -- done
|
|
|
|
else
|
|
|
|
print("Unknown error, tlang.step returned: " .. tostring(more))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function tlang.get_state(code)
|
|
|
|
local lexed = tlang.lex(code)
|
|
|
|
local parsed = tlang.parse(lexed)
|
|
|
|
|
|
|
|
return {
|
|
|
|
locals = {{
|
2020-12-10 04:34:48 +00:00
|
|
|
pc = {sg = 1, pos = {"__ast__"}, elem = 1},
|
|
|
|
vars = {
|
|
|
|
__src__ = tlang.value_to_tlang(code),
|
|
|
|
__lex__ = tlang.value_to_tlang(lexed),
|
|
|
|
__ast__ = {type = "code", value = parsed}
|
|
|
|
}
|
|
|
|
}},
|
2020-11-01 02:55:54 +00:00
|
|
|
stack = {},
|
2020-11-03 07:06:59 +00:00
|
|
|
code_stack = {},
|
2020-11-08 18:26:01 +00:00
|
|
|
builtins = tlang.builtins
|
2020-11-01 02:55:54 +00:00
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
function tlang.exec(code)
|
|
|
|
local state = tlang.get_state(code)
|
|
|
|
tlang.run(state)
|
|
|
|
end
|
|
|
|
|
2020-11-07 19:27:25 +00:00
|
|
|
function tlang.pretty_pc(pc)
|
2020-12-11 02:59:40 +00:00
|
|
|
return tostring(pc.sg) .. ";" .. table.concat(pc.pos, ".") .. ";" .. tostring(pc.elem)
|
2020-11-07 19:27:25 +00:00
|
|
|
end
|
|
|
|
|
2020-12-10 04:34:48 +00:00
|
|
|
function tlang.format_table(t, depth, maxdepth)
|
|
|
|
depth = depth or 0
|
|
|
|
maxdepth = maxdepth or -1
|
|
|
|
|
|
|
|
if depth == maxdepth then
|
|
|
|
return "{...}"
|
|
|
|
end
|
|
|
|
|
|
|
|
local out = {}
|
|
|
|
out[1] = "{\n"
|
|
|
|
|
|
|
|
for k, v in pairs(t) do
|
|
|
|
local idx = k
|
|
|
|
if type(k) == "string" then
|
|
|
|
idx = '"' .. k .. '"'
|
|
|
|
elseif type(k) == "table" then
|
|
|
|
idx = "{...}"
|
|
|
|
end
|
|
|
|
|
|
|
|
out[#out + 1] = string.rep("\t", depth + 1) .. "[" .. idx .. "] = "
|
|
|
|
|
|
|
|
if type(v) == "table" then
|
|
|
|
out[#out + 1] = tlang.format_table(v, depth + 1, maxdepth)
|
|
|
|
elseif type(v) == "string" then
|
|
|
|
out[#out + 1] = '"' .. v .. '"'
|
|
|
|
else
|
|
|
|
out[#out + 1] = tostring(v)
|
|
|
|
end
|
|
|
|
|
|
|
|
out[#out + 1] = ",\n"
|
|
|
|
end
|
|
|
|
|
|
|
|
out[#out + 1] = string.rep("\t", depth) .. "}"
|
|
|
|
return table.concat(out)
|
|
|
|
end
|
|
|
|
|
|
|
|
function tlang.print_table(t, maxdepth)
|
|
|
|
print(tlang.format_table(t, nil, maxdepth))
|
|
|
|
end
|
2020-11-01 02:55:54 +00:00
|
|
|
|
2020-11-05 18:58:25 +00:00
|
|
|
local function test()
|
|
|
|
local complex = [[{dup *} `square =
|
|
|
|
-5.42 square
|
|
|
|
"Hello, world!" print
|
|
|
|
[1 2 3 str:"String"]
|
|
|
|
]]
|
|
|
|
|
|
|
|
local number = "-4.2123"
|
|
|
|
|
|
|
|
local simple = "{dup *}"
|
|
|
|
|
|
|
|
local map = "[this:2 that:3]"
|
|
|
|
|
|
|
|
local square = [[{dup *} `square =
|
|
|
|
5 square print]]
|
|
|
|
|
|
|
|
local square_run = "5 {dup *} run print"
|
|
|
|
|
|
|
|
local comment_test = "'asd' print # 'aft' print"
|
|
|
|
|
|
|
|
local forever_test = [[
|
|
|
|
5 # iteration count
|
|
|
|
{
|
|
|
|
dup # duplicate iter count
|
|
|
|
print # print countdown
|
|
|
|
-- # decrement
|
|
|
|
dup 0 == # check if TOS is 0
|
|
|
|
{break} if # break if TOS == 0
|
|
|
|
}
|
|
|
|
forever # run loop
|
|
|
|
]]
|
|
|
|
|
|
|
|
local local_test = [[
|
|
|
|
'outside' `var =
|
|
|
|
{
|
|
|
|
var print # should be 'outside'
|
|
|
|
'inside' `var =
|
|
|
|
var print # should be 'inside'
|
|
|
|
} run
|
|
|
|
var print # should be 'inside'
|
|
|
|
]]
|
|
|
|
|
|
|
|
local while_test = [[
|
|
|
|
5 `cur =
|
|
|
|
{
|
|
|
|
`cur --
|
|
|
|
cur
|
|
|
|
} {
|
|
|
|
"four times" print
|
|
|
|
} while
|
|
|
|
]]
|
|
|
|
|
|
|
|
local repeat_test = [[
|
|
|
|
{
|
|
|
|
"four times" print
|
|
|
|
} 4 repeat
|
|
|
|
{
|
|
|
|
i print
|
|
|
|
} 5 `i repeat
|
|
|
|
]]
|
|
|
|
|
|
|
|
local stack_test = "5 5 == print"
|
|
|
|
|
2020-11-06 00:54:06 +00:00
|
|
|
local args_test = [[
|
|
|
|
{ 0 `first `second args
|
|
|
|
first print
|
|
|
|
second print
|
|
|
|
} `test =
|
|
|
|
1 2 test
|
|
|
|
]]
|
|
|
|
|
2020-11-06 17:06:32 +00:00
|
|
|
local ifelse_test = [[
|
|
|
|
{
|
|
|
|
{
|
|
|
|
'if' print
|
|
|
|
} {
|
|
|
|
'else' print
|
|
|
|
} if
|
|
|
|
} `ifprint =
|
|
|
|
|
|
|
|
1 ifprint
|
|
|
|
0 ifprint
|
|
|
|
]]
|
|
|
|
|
|
|
|
local nest_run = [[
|
|
|
|
{
|
|
|
|
{
|
|
|
|
'innermost' print
|
|
|
|
} run
|
|
|
|
} run
|
|
|
|
'work' print
|
|
|
|
]]
|
|
|
|
|
2020-11-13 06:41:30 +00:00
|
|
|
local mapid_test = "this.that.2.here .81..wao.88912"
|
|
|
|
|
2020-11-13 06:57:54 +00:00
|
|
|
local paren_test = "('works' print) 'out' print"
|
|
|
|
|
2020-12-10 04:34:48 +00:00
|
|
|
local mapdot_test = [[
|
2020-12-10 04:45:58 +00:00
|
|
|
[1 a:5 b:[a:2 b:3] 3] `a =
|
2020-12-10 04:34:48 +00:00
|
|
|
4 `a.a =
|
2020-12-10 04:45:58 +00:00
|
|
|
a.1 print
|
|
|
|
a.2 print
|
2020-12-10 04:34:48 +00:00
|
|
|
a.a print
|
|
|
|
a.b.b print
|
|
|
|
]]
|
|
|
|
|
|
|
|
local stackdot_test = [[
|
|
|
|
[a:1 b:2]
|
|
|
|
.b print
|
|
|
|
6 `.a =
|
|
|
|
.a print
|
|
|
|
]]
|
|
|
|
|
2020-12-11 02:59:40 +00:00
|
|
|
local funcfunc_test = [[
|
|
|
|
{dup *} `square =
|
|
|
|
{dup square *} `cube =
|
|
|
|
5 cube print
|
|
|
|
]]
|
|
|
|
|
|
|
|
local test = funcfunc_test
|
2020-12-10 04:34:48 +00:00
|
|
|
|
|
|
|
--tlang.print_table(tlang.lex(test))
|
|
|
|
--tlang.print_table(tlang.parse(tlang.lex(test)))
|
|
|
|
tlang.exec(test)
|
2020-11-05 18:58:25 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
if minetest == nil then
|
|
|
|
test()
|
|
|
|
end
|
2020-11-01 02:55:54 +00:00
|
|
|
|
|
|
|
return tlang
|