Initial commit
This commit is contained in:
commit
c847863034
7
bon.lua
Normal file
7
bon.lua
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
local next_unique_id = 0
|
||||||
|
local function unique_name()
|
||||||
|
next_unique_id = next_unique_id + 1
|
||||||
|
return "__" .. "KY" .. next_unique_id
|
||||||
|
end
|
||||||
|
|
||||||
|
return {unique_name = unique_name}
|
81
des.lua
Normal file
81
des.lua
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
local unique_name = require"bon".unique_name
|
||||||
|
|
||||||
|
local function pass_destandardify(chu)
|
||||||
|
local function is_builtin_method(c)
|
||||||
|
if c.type ~= 'dot' then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if c.a.et and c.a.et.type == 'list' and c.b == 'pop' then
|
||||||
|
return {change_stmt = function(stmt)
|
||||||
|
assert(stmt.type == 'call')
|
||||||
|
|
||||||
|
local temp = {et = stmt.what.a.et, name = unique_name()}
|
||||||
|
|
||||||
|
return {
|
||||||
|
{
|
||||||
|
type = 'let',
|
||||||
|
var = temp,
|
||||||
|
expr = stmt.what.a
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type = 'assign',
|
||||||
|
dest = {
|
||||||
|
type = 'index',
|
||||||
|
what = {type = 'var', which = temp, et = temp.et},
|
||||||
|
idx = {type = 'lengthof', sub = {type = 'var', which = temp, et = temp.et}, et = {type = 'integer'}}
|
||||||
|
},
|
||||||
|
src = {type = 'null', et = {type = 'null'}}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
end}
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if chu.type == "chunk" then
|
||||||
|
for k, t in pairs(chu.types) do
|
||||||
|
for _, m in pairs(t.members) do
|
||||||
|
if m.type == "field" then
|
||||||
|
-- Do nothing.
|
||||||
|
elseif m.type == "static" then
|
||||||
|
pass_destandardify(m.value)
|
||||||
|
else
|
||||||
|
error("Invalid AST")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local stmtIdx = 1
|
||||||
|
while stmtIdx <= #chu.stmts do
|
||||||
|
local stmt = chu.stmts[stmtIdx]
|
||||||
|
|
||||||
|
local builtinmethod = stmt.type == 'call' and is_builtin_method(stmt.what)
|
||||||
|
if builtinmethod then
|
||||||
|
table.remove(chu.stmts, stmtIdx)
|
||||||
|
for p, o in ipairs(builtinmethod.change_stmt(stmt)) do
|
||||||
|
table.insert(chu.stmts, stmtIdx + p - 1, o)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
pass_destandardify(stmt)
|
||||||
|
stmtIdx = stmtIdx + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif chu.type == 'function' then
|
||||||
|
pass_destandardify(chu.chunk)
|
||||||
|
elseif chu.type == 'if' then
|
||||||
|
for i = 1, #chu do
|
||||||
|
pass_destandardify(chu[i].chu)
|
||||||
|
end
|
||||||
|
if chu.elsa then
|
||||||
|
pass_destandardify(chu.elsa)
|
||||||
|
end
|
||||||
|
elseif chu.type == 'fori' then
|
||||||
|
pass_destandardify(chu.chu)
|
||||||
|
elseif chu.type == 'loop' then
|
||||||
|
pass_destandardify(chu.chu)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return pass_destandardify
|
224
gen.lua
Normal file
224
gen.lua
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
return function(ast, out)
|
||||||
|
local function compile(chu)
|
||||||
|
if chu.type == "chunk" then
|
||||||
|
if chu.toplevel then
|
||||||
|
for k, import in pairs(chu.imports) do
|
||||||
|
out("local %s = require(%q)\n", import[#import], table.concat(import, "."))
|
||||||
|
end
|
||||||
|
for k, var in pairs(chu.vars) do
|
||||||
|
out("local %s\n", k)
|
||||||
|
end
|
||||||
|
for k, expr in pairs(chu.vars) do
|
||||||
|
if expr then
|
||||||
|
out("%s=", k)
|
||||||
|
compile(expr)
|
||||||
|
out(";")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for k, t in pairs(chu.types) do
|
||||||
|
out("local %s={}%s.__index=%s;setmetatable(%s, %s)", k, k, k, k, k)
|
||||||
|
for _, m in pairs(t.members) do
|
||||||
|
if m.type == "field" then
|
||||||
|
-- Do nothing.
|
||||||
|
elseif m.type == "static" then
|
||||||
|
out("%s.%s=", k, m.name)
|
||||||
|
compile(m.value)
|
||||||
|
out(";")
|
||||||
|
else
|
||||||
|
error("Invalid AST")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
out("return{")
|
||||||
|
for k,v in pairs(chu.vars) do
|
||||||
|
out("%s=%s,", k, k)
|
||||||
|
end
|
||||||
|
for k,v in pairs(chu.types) do
|
||||||
|
out("%s=%s,", k, k)
|
||||||
|
end
|
||||||
|
out("}")
|
||||||
|
else
|
||||||
|
for k, stmt in ipairs(chu.stmts) do
|
||||||
|
compile(stmt)
|
||||||
|
|
||||||
|
if stmt.type == 'call' then
|
||||||
|
out(";") --Safety delimiter.
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif chu.type == "function" then
|
||||||
|
out("function(%s%s)", chu.is_constructor and "_," or "", table.concat(chu.args, ","))
|
||||||
|
|
||||||
|
if SAFE_MODE then
|
||||||
|
for k, argname in pairs(chu.args) do
|
||||||
|
local et = chu.et.args[k]
|
||||||
|
if et then
|
||||||
|
local checks = {}
|
||||||
|
if et.type == 'integer' or et.type == 'number' or et.type == 'string' or et.type == 'boolean' then
|
||||||
|
if et.type == 'integer' then
|
||||||
|
table.insert(checks, 'type(' .. argname .. ')=="number"')
|
||||||
|
table.insert(checks, argname .. '%1==0')
|
||||||
|
else
|
||||||
|
table.insert(checks, 'type(' .. argname .. ')=="'..et.type..'"')
|
||||||
|
end
|
||||||
|
elseif et.type == 'list' then
|
||||||
|
table.insert(checks, 'type(' .. argname .. ')=="table"')
|
||||||
|
end
|
||||||
|
if #checks > 0 then
|
||||||
|
out("assert(%s, %q);", table.concat(checks, " and "), string.format("Invalid argument %q", argname))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if chu.is_constructor then
|
||||||
|
out("local self=setmetatable({}, %s);", chu.et.ret.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
compile(chu.chunk)
|
||||||
|
|
||||||
|
if chu.is_constructor then
|
||||||
|
out("return self;")
|
||||||
|
end
|
||||||
|
|
||||||
|
out("end")
|
||||||
|
elseif chu.type == "let" then
|
||||||
|
out("local %s", chu.var.name)
|
||||||
|
|
||||||
|
if chu.expr then
|
||||||
|
out("=")
|
||||||
|
compile(chu.expr)
|
||||||
|
end
|
||||||
|
|
||||||
|
out(";")
|
||||||
|
elseif chu.type == "num" then
|
||||||
|
out("%g", chu.value)
|
||||||
|
elseif chu.type == "var" then
|
||||||
|
out("%s", chu.which.name)
|
||||||
|
elseif chu.type == "binop" then
|
||||||
|
local paren
|
||||||
|
|
||||||
|
paren = chu.a.type == "binop" and chu.a.level > chu.level
|
||||||
|
if paren then out("(") end
|
||||||
|
compile(chu.a)
|
||||||
|
if paren then out(")") end
|
||||||
|
|
||||||
|
local op = chu.op
|
||||||
|
|
||||||
|
if op == "**" then
|
||||||
|
op = "^"
|
||||||
|
elseif op == "/" and (not chu.a.et or chu.a.et.type == 'integer') and (not chu.b.et or chu.b.et.type == 'integer') then
|
||||||
|
op = "//"
|
||||||
|
end
|
||||||
|
|
||||||
|
out("%s", op)
|
||||||
|
|
||||||
|
paren = chu.b.type == "binop" and chu.b.level > chu.level
|
||||||
|
if paren then out("(") end
|
||||||
|
compile(chu.b)
|
||||||
|
if paren then out(")") end
|
||||||
|
elseif chu.type == "lengthof" then
|
||||||
|
out("#(")
|
||||||
|
compile(chu.sub)
|
||||||
|
out(")")
|
||||||
|
elseif chu.type == "dot" then
|
||||||
|
compile(chu.a)
|
||||||
|
out("%s%s", chu.colon and ":" or ".", chu.b)
|
||||||
|
elseif chu.type == "index" then
|
||||||
|
out("(")
|
||||||
|
compile(chu.what)
|
||||||
|
out(")[")
|
||||||
|
compile(chu.idx)
|
||||||
|
out("]")
|
||||||
|
elseif chu.type == "call" then
|
||||||
|
local guard = chu.type == 'binop'
|
||||||
|
if guard then
|
||||||
|
out("(")
|
||||||
|
end
|
||||||
|
compile(chu.what)
|
||||||
|
if guard then
|
||||||
|
out(")")
|
||||||
|
end
|
||||||
|
out("(")
|
||||||
|
for k, v in pairs(chu.args) do
|
||||||
|
compile(v)
|
||||||
|
|
||||||
|
if k < #chu.args then
|
||||||
|
out(",")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
out(")")
|
||||||
|
elseif chu.type == "dict" then
|
||||||
|
out("{")
|
||||||
|
for k, v in pairs(chu.mappings) do
|
||||||
|
out("[")
|
||||||
|
compile(k)
|
||||||
|
out("]=")
|
||||||
|
compile(v)
|
||||||
|
out(",")
|
||||||
|
end
|
||||||
|
out("}")
|
||||||
|
elseif chu.type == "list" then
|
||||||
|
out("{")
|
||||||
|
for k, v in pairs(chu.values) do
|
||||||
|
out("[%i]=", k)
|
||||||
|
compile(v)
|
||||||
|
out(",")
|
||||||
|
end
|
||||||
|
out("}")
|
||||||
|
elseif chu.type == "string" then
|
||||||
|
out("%q", chu.value)
|
||||||
|
elseif chu.type == "null" then
|
||||||
|
out("nil")
|
||||||
|
elseif chu.type == "return" then
|
||||||
|
out("return ")
|
||||||
|
compile(chu.expr)
|
||||||
|
out(";")
|
||||||
|
elseif chu.type == "if" then
|
||||||
|
out("if ")
|
||||||
|
compile(chu[1].pred)
|
||||||
|
out(" then ")
|
||||||
|
compile(chu[1].chu)
|
||||||
|
|
||||||
|
for i = 2, #chu do
|
||||||
|
out("elseif ")
|
||||||
|
compile(chu[i].pred)
|
||||||
|
out("then ")
|
||||||
|
compile(chu[i].chu)
|
||||||
|
end
|
||||||
|
|
||||||
|
if chu.elsa then
|
||||||
|
out("else ")
|
||||||
|
compile(chu.elsa)
|
||||||
|
end
|
||||||
|
|
||||||
|
out("end;")
|
||||||
|
elseif chu.type == "fori" then
|
||||||
|
out("for " .. chu.varname .. "=")
|
||||||
|
compile(chu.from)
|
||||||
|
out(",")
|
||||||
|
compile(chu.to)
|
||||||
|
out("-1 do ")
|
||||||
|
compile(chu.chu)
|
||||||
|
out("end ")
|
||||||
|
elseif chu.type == "loop" then
|
||||||
|
out("while true do ")
|
||||||
|
compile(chu.chu)
|
||||||
|
out("end ")
|
||||||
|
elseif chu.type == "break" then
|
||||||
|
out("break ")
|
||||||
|
elseif chu.type == "noop" then
|
||||||
|
elseif chu.type == "exprstat" then
|
||||||
|
compile(chu.expr)
|
||||||
|
out(";")
|
||||||
|
elseif chu.type == "assign" then
|
||||||
|
compile(chu.dest)
|
||||||
|
out("=")
|
||||||
|
compile(chu.src)
|
||||||
|
out(";")
|
||||||
|
else
|
||||||
|
error(string.format("Invalid AST node (type %q)", chu.type))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return compile(ast)
|
||||||
|
end
|
215
lex.lua
Normal file
215
lex.lua
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
--[[
|
||||||
|
Titty has two main design goals:
|
||||||
|
1. Should look natural from the outside, Lua code's PoV, and
|
||||||
|
2. Should compile to Lua code that also appears natural from within
|
||||||
|
The second is less important than the first.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local inspect = require"inspect".inspect
|
||||||
|
|
||||||
|
local function codepoints(str)
|
||||||
|
local it, state, v1, v2 = utf8.codes(str)
|
||||||
|
local idx = 0
|
||||||
|
return function()
|
||||||
|
if not it then return nil end
|
||||||
|
|
||||||
|
v1, v2 = it(state, v1)
|
||||||
|
if v1 then
|
||||||
|
local r = idx
|
||||||
|
idx = idx + 1
|
||||||
|
return r, v2
|
||||||
|
else
|
||||||
|
it = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_digit(cp)
|
||||||
|
return cp and (cp >= 48 and cp <= 57)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_ident_start(cp)
|
||||||
|
return cp and ((cp >= 65 and cp <= 90) or (cp >= 97 and cp <= 122) or cp == 95)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_ident_nonstart(cp)
|
||||||
|
return cp and (is_ident_start(cp) or is_digit(cp))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function forall(tbl, f, startI)
|
||||||
|
startI = startI or 1
|
||||||
|
|
||||||
|
for k, v in pairs(tbl) do
|
||||||
|
if not f(v) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_whitespace(cp)
|
||||||
|
return cp and (cp == 32 or cp == 10 or cp == 9)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function clear(tbl)
|
||||||
|
for k in pairs(tbl) do
|
||||||
|
tbl[k] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function lex(cpgetraw)
|
||||||
|
local row, column, idx = 0, 0, 0
|
||||||
|
local pull
|
||||||
|
local function cpget()
|
||||||
|
if pull then
|
||||||
|
local ret = pull
|
||||||
|
pull = nil
|
||||||
|
return ret
|
||||||
|
else
|
||||||
|
local cp
|
||||||
|
idx, cp = cpgetraw()
|
||||||
|
column = column + 1
|
||||||
|
if cp == 10 then
|
||||||
|
column = 0
|
||||||
|
row = row + 1
|
||||||
|
end
|
||||||
|
return cp
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local buf = {}
|
||||||
|
|
||||||
|
return function()
|
||||||
|
local cp
|
||||||
|
|
||||||
|
while true do
|
||||||
|
cp = cpget()
|
||||||
|
|
||||||
|
if not is_whitespace(cp) then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not cp then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local rowStart, columnStart = row, column
|
||||||
|
|
||||||
|
if cp == 40 then
|
||||||
|
return "(", "(", rowStart, columnStart
|
||||||
|
elseif cp == 41 then
|
||||||
|
return ")", ")", rowStart, columnStart
|
||||||
|
elseif cp == 123 then
|
||||||
|
return "{", "{", rowStart, columnStart
|
||||||
|
elseif cp == 125 then
|
||||||
|
return "}", "}", rowStart, columnStart
|
||||||
|
elseif cp == 44 then
|
||||||
|
return ",", ",", rowStart, columnStart
|
||||||
|
elseif cp == 46 then
|
||||||
|
return ".", ".", rowStart, columnStart
|
||||||
|
elseif cp == 58 then
|
||||||
|
return ":", ":", rowStart, columnStart
|
||||||
|
elseif cp == 61 then
|
||||||
|
local after = cpget()
|
||||||
|
if after == 61 then
|
||||||
|
return "==", "==", rowStart, columnStart
|
||||||
|
else
|
||||||
|
pull = after
|
||||||
|
end
|
||||||
|
return "=", "=", rowStart, columnStart
|
||||||
|
elseif cp == 43 then
|
||||||
|
return "+", "+", rowStart, columnStart
|
||||||
|
elseif cp == 45 then
|
||||||
|
return "-", "-", rowStart, columnStart
|
||||||
|
elseif cp == 42 then
|
||||||
|
local after = cpget()
|
||||||
|
if after == 42 then
|
||||||
|
return "**", "**", rowStart, columnStart
|
||||||
|
else
|
||||||
|
pull = after
|
||||||
|
end
|
||||||
|
return "*", "*", rowStart, columnStart
|
||||||
|
elseif cp == 47 then
|
||||||
|
return "/", "/", rowStart, columnStart
|
||||||
|
elseif cp == 37 then
|
||||||
|
return "%", "%", rowStart, columnStart
|
||||||
|
elseif cp == 35 then
|
||||||
|
return "#", "#", rowStart, columnStart
|
||||||
|
elseif cp == 91 then
|
||||||
|
return "[", "[", rowStart, columnStart
|
||||||
|
elseif cp == 93 then
|
||||||
|
return "]", "]", rowStart, columnStart
|
||||||
|
elseif cp == 39 then
|
||||||
|
while true do
|
||||||
|
cp = cpget()
|
||||||
|
|
||||||
|
if cp ~= 39 then
|
||||||
|
buf[#buf + 1] = cp
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local ret = utf8.char(table.unpack(buf))
|
||||||
|
|
||||||
|
clear(buf)
|
||||||
|
|
||||||
|
return "string", ret, rowStart, columnStart
|
||||||
|
elseif is_ident_start(cp) then
|
||||||
|
buf[1] = cp
|
||||||
|
|
||||||
|
while true do
|
||||||
|
cp = cpget()
|
||||||
|
|
||||||
|
if is_ident_nonstart(cp) then
|
||||||
|
buf[#buf + 1] = cp
|
||||||
|
else
|
||||||
|
pull = cp
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local ret = utf8.char(table.unpack(buf))
|
||||||
|
|
||||||
|
clear(buf)
|
||||||
|
|
||||||
|
if ret == "func" or ret == "end" or ret == "if"
|
||||||
|
or ret == "while" or ret == "do" or ret == "then"
|
||||||
|
or ret == "elseif" or ret == "type" or ret == "interf"
|
||||||
|
or ret == "let" or ret == "return" or ret == "else"
|
||||||
|
or ret == "for" or ret == "import" or ret == "constr"
|
||||||
|
or ret == "loop" or ret == "break" or ret == "nil" then
|
||||||
|
|
||||||
|
return ret, ret, rowStart, columnStart
|
||||||
|
end
|
||||||
|
|
||||||
|
return "id", ret, rowStart, columnStart
|
||||||
|
elseif is_digit(cp) then
|
||||||
|
buf[1] = cp
|
||||||
|
|
||||||
|
local dotFound = false
|
||||||
|
|
||||||
|
while true do
|
||||||
|
cp = cpget()
|
||||||
|
if is_digit(cp) then
|
||||||
|
buf[#buf + 1] = cp
|
||||||
|
elseif cp == 46 and not dotFound then
|
||||||
|
dotFound = true
|
||||||
|
buf[#buf + 1] = cp
|
||||||
|
else
|
||||||
|
pull = cp
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local ret = utf8.char(table.unpack(buf))
|
||||||
|
clear(buf)
|
||||||
|
return "num", ret, rowStart, columnStart
|
||||||
|
else
|
||||||
|
error(string.format("%i:%i unknown character %q (code point %i)", row, column, utf8.char(cp), cp))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return {lex = lex, codepoints = codepoints}
|
16
main.lua
Normal file
16
main.lua
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
local Lexer = require"lex"
|
||||||
|
local Parser = require"parse"
|
||||||
|
local Des = require"des"
|
||||||
|
local Gen = require"gen"
|
||||||
|
|
||||||
|
local tokens = {}
|
||||||
|
|
||||||
|
for tt, ts, tr, tc in Lexer.lex(Lexer.codepoints(io.open(arg[1], "r"):read("*a"))) do
|
||||||
|
tokens[#tokens + 1] = {tt, ts, tr, tc}
|
||||||
|
end
|
||||||
|
|
||||||
|
local ast = Parser(tokens, false)
|
||||||
|
|
||||||
|
Des(ast)
|
||||||
|
|
||||||
|
Gen(ast, function(str, ...) io.write(string.format(str, ...)) end)
|
696
parse.lua
Normal file
696
parse.lua
Normal file
@ -0,0 +1,696 @@
|
|||||||
|
local unique_name = require"bon".unique_name
|
||||||
|
|
||||||
|
local function do_everything(Tokens, Skeleton)
|
||||||
|
local TokensIdx = 1
|
||||||
|
local IdxStack = {}
|
||||||
|
|
||||||
|
local function gettoken()
|
||||||
|
local r = Tokens[TokensIdx]
|
||||||
|
if r then
|
||||||
|
TokensIdx = TokensIdx + 1
|
||||||
|
end
|
||||||
|
return table.unpack(r)
|
||||||
|
end
|
||||||
|
local function peektoken()
|
||||||
|
local r = Tokens[TokensIdx]
|
||||||
|
if r then
|
||||||
|
return table.unpack(Tokens[TokensIdx])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local function maybe(t)
|
||||||
|
local a, b, c, d = peektoken()
|
||||||
|
if a == t then return gettoken() end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local function pushlexi()
|
||||||
|
table.insert(IdxStack, TokensIdx)
|
||||||
|
end
|
||||||
|
local function poplexi()
|
||||||
|
TokensIdx = table.remove(IdxStack)
|
||||||
|
end
|
||||||
|
local function forgetlexi()
|
||||||
|
table.remove(IdxStack)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function expect(t)
|
||||||
|
local a, b, c, d = gettoken()
|
||||||
|
if t ~= a then
|
||||||
|
error(string.format("%i:%i expected %s, got %s", c, d, t, a))
|
||||||
|
end
|
||||||
|
return a, b, c, d
|
||||||
|
end
|
||||||
|
|
||||||
|
local parse_chunk
|
||||||
|
|
||||||
|
local function new_parse_context(parent)
|
||||||
|
return setmetatable({["the parent"] = parent}, {
|
||||||
|
__index = function(self, key)
|
||||||
|
while true do
|
||||||
|
local r = rawget(self, key)
|
||||||
|
if r then
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
self = rawget(self, "the parent")
|
||||||
|
if not self then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
__newindex = function(self, key, value)
|
||||||
|
local z = self
|
||||||
|
local r = nil
|
||||||
|
while true do
|
||||||
|
r = rawget(z, key)
|
||||||
|
if r then break end
|
||||||
|
z = rawget(z, "the parent")
|
||||||
|
if not z then break end
|
||||||
|
end
|
||||||
|
rawset(z or self, key, value)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local ParseContext, ParseContextTypes
|
||||||
|
|
||||||
|
local function push_parse_context()
|
||||||
|
ParseContext = new_parse_context(ParseContext)
|
||||||
|
ParseContextTypes = new_parse_context(ParseContextTypes)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function pop_parse_context()
|
||||||
|
ParseContext = rawget(ParseContext, "the parent")
|
||||||
|
ParseContextTypes = rawget(ParseContextTypes, "the parent")
|
||||||
|
end
|
||||||
|
|
||||||
|
local Types = {}
|
||||||
|
function Types.castable(this, to)
|
||||||
|
return not to or (this and this.type == to.type)
|
||||||
|
end
|
||||||
|
function Types.is_number(this)
|
||||||
|
return not this or this.type == 'number' or this.type == 'integer'
|
||||||
|
end
|
||||||
|
function Types.can_do_arith(this)
|
||||||
|
return Types.is_number(this)
|
||||||
|
end
|
||||||
|
function Types.is_callable(this)
|
||||||
|
return not this or this.type == 'function'
|
||||||
|
end
|
||||||
|
function Types.is_indexable(this)
|
||||||
|
return not this or this.type == 'list'
|
||||||
|
end
|
||||||
|
function Types.has_field(this, field)
|
||||||
|
if not this then
|
||||||
|
return true, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if this.type == 'list' and field == 'pop' then
|
||||||
|
return true, {type = 'function', ret = false, args = {this}}
|
||||||
|
end
|
||||||
|
|
||||||
|
if this.type ~= 'structure' then
|
||||||
|
return false, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
for k, v in ipairs(this.members) do
|
||||||
|
if v.name == field then
|
||||||
|
return true, v.et
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parse_et()
|
||||||
|
local A
|
||||||
|
|
||||||
|
if peektoken() == "id" then
|
||||||
|
local name = select(2, gettoken())
|
||||||
|
if name == "number" then
|
||||||
|
A = {type = 'number'}
|
||||||
|
elseif name == "integer" then
|
||||||
|
A = {type = 'integer'}
|
||||||
|
elseif name == "string" then
|
||||||
|
A = {type = 'string'}
|
||||||
|
elseif name == "boolean" then
|
||||||
|
A = {type = 'boolean'}
|
||||||
|
elseif name == "list" then
|
||||||
|
A = {type = 'list'}
|
||||||
|
elseif name == "dict" then
|
||||||
|
A = {type = 'dict'}
|
||||||
|
else
|
||||||
|
A = ParseContextTypes[name]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not A then
|
||||||
|
error("Invalid type description")
|
||||||
|
end
|
||||||
|
|
||||||
|
if maybe("[") then
|
||||||
|
A.parametrization = {parse_et()}
|
||||||
|
|
||||||
|
expect("]")
|
||||||
|
end
|
||||||
|
|
||||||
|
return A
|
||||||
|
end
|
||||||
|
|
||||||
|
local parse_function
|
||||||
|
|
||||||
|
local function parse_function_arg()
|
||||||
|
local name = select(2, expect("id"))
|
||||||
|
local et
|
||||||
|
if maybe(":") then
|
||||||
|
et = parse_et()
|
||||||
|
end
|
||||||
|
return name, et
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parse_type()
|
||||||
|
expect("type")
|
||||||
|
|
||||||
|
local name = select(2, expect("id"))
|
||||||
|
|
||||||
|
local ret = {type = 'structure', members = {}, name = name}
|
||||||
|
|
||||||
|
while true do
|
||||||
|
if maybe("end") then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
if peektoken() == "constr" or peektoken() == "func" then
|
||||||
|
local isConstr = gettoken() == "constr"
|
||||||
|
local fname
|
||||||
|
|
||||||
|
if isConstr then
|
||||||
|
fname = "__call"
|
||||||
|
else
|
||||||
|
fname = select(2, expect("id"))
|
||||||
|
end
|
||||||
|
|
||||||
|
local argnames = {}
|
||||||
|
local argets = {}
|
||||||
|
|
||||||
|
if not isConstr then
|
||||||
|
table.insert(argnames, "self")
|
||||||
|
table.insert(argets, ret)
|
||||||
|
end
|
||||||
|
|
||||||
|
push_parse_context()
|
||||||
|
rawset(ParseContext, "self", {et = ret, name = "self"})
|
||||||
|
|
||||||
|
if maybe("(") then
|
||||||
|
if not maybe(")") then
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local name, et = parse_function_arg()
|
||||||
|
|
||||||
|
table.insert(argnames, name)
|
||||||
|
argets[#argnames] = et
|
||||||
|
|
||||||
|
rawset(ParseContext, name, {et = et, name = name})
|
||||||
|
|
||||||
|
if maybe(")") then
|
||||||
|
break
|
||||||
|
else
|
||||||
|
expect(",")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local fret
|
||||||
|
if isConstr then
|
||||||
|
fret = ret
|
||||||
|
else
|
||||||
|
if maybe(":") then
|
||||||
|
fret = parse_et()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local chu = parse_chunk(false)
|
||||||
|
|
||||||
|
local et = {type = "function", args = argets, ret = fret}
|
||||||
|
|
||||||
|
table.insert(ret.members, {
|
||||||
|
type = "static",
|
||||||
|
name = fname,
|
||||||
|
et = et,
|
||||||
|
value = {type = "function", et = et, args = argnames, chunk = chu, is_constructor = isConstr},
|
||||||
|
})
|
||||||
|
|
||||||
|
pop_parse_context()
|
||||||
|
|
||||||
|
expect("end")
|
||||||
|
else
|
||||||
|
local membname = select(2, expect("id"))
|
||||||
|
|
||||||
|
expect(":")
|
||||||
|
|
||||||
|
local et = parse_et()
|
||||||
|
|
||||||
|
table.insert(ret.members, {type = "field", name = membname, et = et})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return name, ret
|
||||||
|
end
|
||||||
|
|
||||||
|
parse_function = function()
|
||||||
|
expect("func")
|
||||||
|
|
||||||
|
local name = select(2, maybe("id"))
|
||||||
|
|
||||||
|
push_parse_context()
|
||||||
|
|
||||||
|
local argnames = {}
|
||||||
|
local argets = {}
|
||||||
|
|
||||||
|
if maybe("(") then
|
||||||
|
if not maybe(")") then
|
||||||
|
while true do
|
||||||
|
local name, et = parse_function_arg()
|
||||||
|
|
||||||
|
table.insert(argnames, name)
|
||||||
|
argets[#argnames] = et
|
||||||
|
|
||||||
|
rawset(ParseContext, name, {et = et, name = name})
|
||||||
|
|
||||||
|
if maybe(")") then
|
||||||
|
break
|
||||||
|
else
|
||||||
|
expect(",")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local retet
|
||||||
|
if maybe(":") then
|
||||||
|
retet = parse_et()
|
||||||
|
end
|
||||||
|
|
||||||
|
rawset(ParseContext, "the function name", name)
|
||||||
|
rawset(ParseContextTypes, "the function return type", retet)
|
||||||
|
|
||||||
|
local ret = {
|
||||||
|
type = "function",
|
||||||
|
et = {type = "function", args = argets, ret = retet},
|
||||||
|
chunk = parse_chunk(false),
|
||||||
|
args = argnames
|
||||||
|
}
|
||||||
|
|
||||||
|
pop_parse_context()
|
||||||
|
|
||||||
|
expect("end")
|
||||||
|
|
||||||
|
return ret, name
|
||||||
|
end
|
||||||
|
|
||||||
|
local active_stmt_list_stack = {}
|
||||||
|
|
||||||
|
BINOP_LEVELS = {["+"] = 3, ["-"] = 3, ["=="] = 4, ["/"] = 2, ["*"] = 2, ["%"] = 2, ["**"] = 1}
|
||||||
|
local function parse_expr(level)
|
||||||
|
level = level or 4
|
||||||
|
|
||||||
|
if level == 4 then
|
||||||
|
local A = parse_expr(level - 1)
|
||||||
|
|
||||||
|
if peektoken() == "==" then
|
||||||
|
local op = gettoken()
|
||||||
|
|
||||||
|
local B = parse_expr(level - 1)
|
||||||
|
|
||||||
|
A = {type = "binop", level = level, et = {type = 'boolean'}, op = op, a = A, b = B}
|
||||||
|
end
|
||||||
|
|
||||||
|
return A
|
||||||
|
elseif level == 3 then
|
||||||
|
local A = parse_expr(level - 1)
|
||||||
|
|
||||||
|
if peektoken() == "+" or peektoken() == "-" then
|
||||||
|
local op = gettoken()
|
||||||
|
|
||||||
|
local B = parse_expr(level - 1)
|
||||||
|
|
||||||
|
if not Types.can_do_arith(A.et) or not Types.can_do_arith(B.et) then
|
||||||
|
error(op .. " operator not supported for non-number types")
|
||||||
|
end
|
||||||
|
|
||||||
|
A = {type = "binop", level = level, et = A.et, op = op, a = A, b = B}
|
||||||
|
end
|
||||||
|
|
||||||
|
return A
|
||||||
|
elseif level == 2 then
|
||||||
|
local A = parse_expr(level - 1)
|
||||||
|
|
||||||
|
if peektoken() == "*" or peektoken() == "/" or peektoken() == "%" then
|
||||||
|
local op = gettoken()
|
||||||
|
|
||||||
|
local B = parse_expr(level - 1)
|
||||||
|
|
||||||
|
if not Types.can_do_arith(A.et) or not Types.can_do_arith(B.et) then
|
||||||
|
error(op .. " operator not supported for non-number types")
|
||||||
|
end
|
||||||
|
|
||||||
|
A = {type = "binop", level = level, et = A.et, op = op, a = A, b = B}
|
||||||
|
end
|
||||||
|
|
||||||
|
return A
|
||||||
|
elseif level == 1 then
|
||||||
|
local A = parse_expr(level - 1)
|
||||||
|
|
||||||
|
if peektoken() == "**" then
|
||||||
|
local op = gettoken()
|
||||||
|
|
||||||
|
local B = parse_expr(level - 1)
|
||||||
|
|
||||||
|
if not Types.can_do_arith(A.et) or not Types.can_do_arith(B.et) then
|
||||||
|
error(op .. " operator not supported for non-number types")
|
||||||
|
end
|
||||||
|
|
||||||
|
A = {type = "binop", level = level, et = A.et, op = op, a = A, b = B}
|
||||||
|
end
|
||||||
|
|
||||||
|
return A
|
||||||
|
elseif level == 0 then
|
||||||
|
local A
|
||||||
|
|
||||||
|
if maybe("#") then
|
||||||
|
A = {type = 'lengthof', et = {type = 'integer'}, sub = parse_expr(level)}
|
||||||
|
|
||||||
|
if A.sub.et and A.sub.et.type ~= 'list' then
|
||||||
|
error("# requires a list")
|
||||||
|
end
|
||||||
|
elseif maybe("(") then
|
||||||
|
A = parse_expr()
|
||||||
|
expect(")")
|
||||||
|
elseif peektoken() == "func" then
|
||||||
|
A = parse_function()
|
||||||
|
elseif peektoken() == "num" then
|
||||||
|
local lit = select(2, gettoken())
|
||||||
|
A = {type = "num", et = lit:find("%.") and {type = 'number'} or {type = 'integer'}, value = tonumber(lit)}
|
||||||
|
elseif peektoken() == "id" then
|
||||||
|
local name = select(2, gettoken())
|
||||||
|
local v = ParseContext[name]
|
||||||
|
if not v then
|
||||||
|
error(string.format("No value with name %s exists.", name))
|
||||||
|
end
|
||||||
|
A = {type = "var", which = v, et = v.et}
|
||||||
|
elseif maybe("{") then
|
||||||
|
A = {type = "dict", et = {type = "dict", parametrization = {}}, mappings = {}}
|
||||||
|
expect("}")
|
||||||
|
elseif maybe("[") then
|
||||||
|
A = {type = "list", et = {type = "list", parametrization = {}}, values = {}}
|
||||||
|
expect("]")
|
||||||
|
elseif peektoken() == "string" then
|
||||||
|
A = {type = "string", et = {type = 'string'}, value = select(2, gettoken())}
|
||||||
|
elseif maybe("nil") then
|
||||||
|
A = {type = 'null', et = {type = 'null'}}
|
||||||
|
else
|
||||||
|
error("Unexpected token " .. select(2, peektoken()))
|
||||||
|
end
|
||||||
|
|
||||||
|
while peektoken() == "[" or peektoken() == "(" or peektoken() == "." do
|
||||||
|
if maybe("[") then
|
||||||
|
if A.et and A.et.type ~= 'list' and A.et.type ~= 'dict' and A.et.type ~= 'string' then
|
||||||
|
error("Indexed value is not a list nor dict.")
|
||||||
|
end
|
||||||
|
|
||||||
|
local newet
|
||||||
|
if A.et.type == 'list' then
|
||||||
|
newet = A.et.parametrization and A.et.parametrization[1]
|
||||||
|
elseif A.et.type == 'dict' then
|
||||||
|
newet = A.et.parametrization and A.et.parametrization[2]
|
||||||
|
elseif A.et.type == 'string' then
|
||||||
|
newet = A.et
|
||||||
|
else
|
||||||
|
error()
|
||||||
|
end
|
||||||
|
|
||||||
|
local idx = parse_expr()
|
||||||
|
|
||||||
|
if A.et.type == 'string' then
|
||||||
|
local temp = {name = unique_name(), et = nil}
|
||||||
|
|
||||||
|
table.insert(active_stmt_list_stack[#active_stmt_list_stack], {
|
||||||
|
type = 'let',
|
||||||
|
var = temp,
|
||||||
|
expr = {type = 'binop', level = BINOP_LEVELS['+'], op = '+', a = idx, b = {type = 'num', value = 1, et = {type = 'integer'}}}
|
||||||
|
})
|
||||||
|
|
||||||
|
A = {
|
||||||
|
type = 'call',
|
||||||
|
what = {type = 'dot', colon = true, a = A, b = 'sub', et = nil},
|
||||||
|
args = {
|
||||||
|
{type = 'var', which = temp, et = temp.et},
|
||||||
|
{type = 'var', which = temp, et = temp.et}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if A.et.type == 'list' then
|
||||||
|
-- Switch to 1-based indexing
|
||||||
|
idx = {type = 'binop', level = BINOP_LEVELS['+'], op = '+', a = idx, b = {type = 'num', value = 1, et = {type = 'integer'}}}
|
||||||
|
end
|
||||||
|
|
||||||
|
A = {type = 'index', what = A, idx = idx, et = newet}
|
||||||
|
end
|
||||||
|
|
||||||
|
expect("]")
|
||||||
|
elseif maybe("(") then
|
||||||
|
if not Types.is_callable(A.et) then
|
||||||
|
error("Called value is not a function.")
|
||||||
|
end
|
||||||
|
|
||||||
|
local args = {}
|
||||||
|
|
||||||
|
if not maybe(")") then
|
||||||
|
while true do
|
||||||
|
table.insert(args, parse_expr())
|
||||||
|
|
||||||
|
if maybe(")") then break end
|
||||||
|
|
||||||
|
expect(",")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
A = {type = 'call', what = A, et = A.et and A.et.ret or nil, args = args}
|
||||||
|
elseif maybe(".") then
|
||||||
|
local B = select(2, expect("id"))
|
||||||
|
|
||||||
|
local has_field, field_type = Types.has_field(A.et, B)
|
||||||
|
|
||||||
|
if not has_field then
|
||||||
|
error(string.format("No such field %s", B))
|
||||||
|
end
|
||||||
|
|
||||||
|
A = {type = 'dot', a = A, b = B, et = field_type}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return A
|
||||||
|
end
|
||||||
|
|
||||||
|
error("Invalid expression")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parse_stmt()
|
||||||
|
if maybe("let") then
|
||||||
|
local name = select(2, expect("id"))
|
||||||
|
|
||||||
|
local et
|
||||||
|
if maybe(":") then
|
||||||
|
et = parse_et()
|
||||||
|
end
|
||||||
|
|
||||||
|
local expr
|
||||||
|
if maybe("=") then
|
||||||
|
expr = parse_expr()
|
||||||
|
end
|
||||||
|
|
||||||
|
if not et and not expr then
|
||||||
|
error(string.format("Cannot create name %s without a given type", name))
|
||||||
|
end
|
||||||
|
|
||||||
|
if not et then
|
||||||
|
et = expr.et
|
||||||
|
elseif expr and not Types.castable(expr.et, et) then
|
||||||
|
error(string.format("Cannot cast expression to type %s", et.type))
|
||||||
|
end
|
||||||
|
|
||||||
|
local newvar = {et = et, name = name}
|
||||||
|
|
||||||
|
ParseContext[name] = newvar
|
||||||
|
|
||||||
|
return {type = "let", var = newvar, expr = expr}
|
||||||
|
elseif maybe("if") then
|
||||||
|
local ret = {type = "if"}
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local expr = parse_expr()
|
||||||
|
if not et and not Types.castable(expr.et, {type = 'boolean'}) then
|
||||||
|
error("If predicate must be castable to boolean")
|
||||||
|
end
|
||||||
|
|
||||||
|
expect("then")
|
||||||
|
|
||||||
|
local chu = parse_chunk(false)
|
||||||
|
|
||||||
|
table.insert(ret, {pred = expr, chu = chu})
|
||||||
|
|
||||||
|
if maybe("else") then
|
||||||
|
ret.elsa = parse_chunk(false)
|
||||||
|
|
||||||
|
expect("end")
|
||||||
|
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
if maybe("end") then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
expect("elseif")
|
||||||
|
end
|
||||||
|
|
||||||
|
return ret
|
||||||
|
elseif maybe("for") then
|
||||||
|
local varname = select(2, expect("id"))
|
||||||
|
|
||||||
|
expect("=")
|
||||||
|
|
||||||
|
local from = parse_expr()
|
||||||
|
|
||||||
|
expect(",")
|
||||||
|
|
||||||
|
local to = parse_expr()
|
||||||
|
|
||||||
|
expect("do")
|
||||||
|
|
||||||
|
push_parse_context()
|
||||||
|
|
||||||
|
rawset(ParseContext, varname, {et = {type = 'integer'}, name = varname})
|
||||||
|
|
||||||
|
local chu = parse_chunk(false)
|
||||||
|
|
||||||
|
pop_parse_context()
|
||||||
|
|
||||||
|
expect("end")
|
||||||
|
|
||||||
|
return {type = "fori", varname = varname, from = from, to = to, chu = chu}
|
||||||
|
elseif maybe("loop") then
|
||||||
|
push_parse_context()
|
||||||
|
local chu = parse_chunk(false)
|
||||||
|
pop_parse_context()
|
||||||
|
expect("end")
|
||||||
|
|
||||||
|
return {type = "loop", chu = chu}
|
||||||
|
elseif maybe("break") then
|
||||||
|
return {type = "break"}
|
||||||
|
elseif maybe("return") then
|
||||||
|
local expr = parse_expr()
|
||||||
|
|
||||||
|
if expr.et and not Types.castable(expr.et, ParseContextTypes["the function return type"]) then
|
||||||
|
error(string.format("Function %s returns different type.", ParseContext["the function name"]))
|
||||||
|
end
|
||||||
|
|
||||||
|
return {type = "return", expr = expr}
|
||||||
|
else
|
||||||
|
local e = parse_expr()
|
||||||
|
|
||||||
|
if maybe("=") then
|
||||||
|
local src = parse_expr()
|
||||||
|
|
||||||
|
if not Types.castable(src.et, e.et) then
|
||||||
|
error("Cannot cast for assignment")
|
||||||
|
end
|
||||||
|
|
||||||
|
return {type = "assign", dest = e, src = src}
|
||||||
|
else
|
||||||
|
if e.type ~= "call" then
|
||||||
|
error("Expected statement.")
|
||||||
|
end
|
||||||
|
|
||||||
|
return e
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
error(string.format("Unexpected token %s", (select(2, peektoken()))))
|
||||||
|
end
|
||||||
|
|
||||||
|
local SAFE_MODE = true
|
||||||
|
|
||||||
|
parse_chunk = function(toplevel)
|
||||||
|
push_parse_context()
|
||||||
|
|
||||||
|
local ast = {type = "chunk", types = {}, vars = {}, stmts = {}, imports = {}, toplevel = toplevel}
|
||||||
|
|
||||||
|
table.insert(active_stmt_list_stack, ast.stmts)
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local tt, ts, tr, tc = peektoken()
|
||||||
|
|
||||||
|
if tt == "end" or tt == "else" or tt == "elseif" then
|
||||||
|
if toplevel then
|
||||||
|
error("Unexpected end in top-level chunk")
|
||||||
|
end
|
||||||
|
|
||||||
|
break
|
||||||
|
elseif tt == nil then
|
||||||
|
if not toplevel then
|
||||||
|
error("Unexpected EOF in non top-level chunk")
|
||||||
|
end
|
||||||
|
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
if toplevel and tt == "import" then
|
||||||
|
gettoken()
|
||||||
|
|
||||||
|
local path = {}
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local part = select(2, expect("id"))
|
||||||
|
table.insert(path, part)
|
||||||
|
|
||||||
|
if not maybe(".") then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
rawset(ParseContext, path[#path], {et = nil, name = path[#path]})
|
||||||
|
|
||||||
|
table.insert(ast.imports, path)
|
||||||
|
elseif tt == "type" then
|
||||||
|
local name, t = parse_type()
|
||||||
|
ast.types[name] = t
|
||||||
|
rawset(ParseContextTypes, name, t)
|
||||||
|
elseif tt == "func" then
|
||||||
|
local expr, name = parse_function()
|
||||||
|
ast.vars[name] = expr
|
||||||
|
rawset(ParseContext, name, {et = expr.et, name = name})
|
||||||
|
elseif not toplevel then
|
||||||
|
table.insert(ast.stmts, parse_stmt())
|
||||||
|
else
|
||||||
|
error(string.format("Unexpected %s in top-level chunk", tt))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
table.remove(active_stmt_list_stack)
|
||||||
|
|
||||||
|
pop_parse_context()
|
||||||
|
|
||||||
|
return ast
|
||||||
|
end
|
||||||
|
|
||||||
|
return parse_chunk(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
--local chu = parse_chunk(true)
|
||||||
|
--pass_destandardify(chu)
|
||||||
|
--compile(chu)
|
||||||
|
--print()
|
||||||
|
|
||||||
|
return do_everything
|
Loading…
Reference in New Issue
Block a user