|
local remove |
|
remove = table.remove |
|
local insert |
|
insert = function(t, i, o) |
|
if not (o) then |
|
o, i = i, #t + 1 |
|
end |
|
table.insert(t, i, o) |
|
return t |
|
end |
|
local inspect = require("inspect") |
|
local t_str |
|
t_str = function(obj) |
|
return type(obj) == "string" |
|
end |
|
local t_tbl |
|
t_tbl = function(obj) |
|
return type(obj) == "table" |
|
end |
|
local noop |
|
noop = function(x) |
|
return x |
|
end |
|
local cp_tbl |
|
cp_tbl = function(t) |
|
local ret = { } |
|
for i, j in pairs(t) do |
|
if t_tbl(j) then |
|
ret[i] = cp_tbl(j) |
|
else |
|
ret[i] = j |
|
end |
|
end |
|
do |
|
local _M = getmetatable(t) |
|
if _M then |
|
setmetatable(ret, _M) |
|
end |
|
end |
|
return ret |
|
end |
|
local strmguard |
|
strmguard = function(str, t) |
|
for i = 1, #t, 2 do |
|
if not (t_str(t[i]) or type(t[i + 1]) == "function") then |
|
error("strmguard failed") |
|
end |
|
if str:match(t[i]) then |
|
return t[i + 1]() |
|
end |
|
end |
|
if t.default then |
|
return t.default() |
|
end |
|
end |
|
local funstack, builtin, eval_exp, eval_lambda, eval_apply, eval_block, eval |
|
funstack = { |
|
pushcresume = function(self, fun) |
|
return coroutine.resume((insert(self, { |
|
coroutine.create(fun) |
|
}))[#self][1]) |
|
end, |
|
stopregret = function(self, ret) |
|
return coroutine.yield((insert(self[#self], ret))[#self][1]) |
|
end, |
|
pop = function(self) |
|
return remove(self) |
|
end |
|
} |
|
builtin = { |
|
["+"] = function(pair) |
|
return pair[1] + pair[2] |
|
end, |
|
["-"] = function(pair) |
|
return pair[1] - pair[2] |
|
end, |
|
["*"] = function(pair) |
|
return pair[1] * pair[2] |
|
end, |
|
["/"] = function(pair) |
|
return pair[1] / pair[2] |
|
end, |
|
left = function(pair) |
|
return pair[1] |
|
end, |
|
right = function(pair) |
|
return pair[2] |
|
end, |
|
print = function(a) |
|
return (function(x) |
|
return print(x) |
|
end)((inspect(a)):gsub("{(.*)}", "<%1>"):gsub("function", "lambda"):gsub("nil", "undefined")) |
|
end |
|
} |
|
eval_exp = function(exp, env, k) |
|
local _exp_0 = type(exp) |
|
if "string" == _exp_0 then |
|
return k(strmguard(exp, { |
|
"^%d", |
|
function() |
|
return tonumber(exp) |
|
end, |
|
"^%.%d", |
|
function() |
|
return tonumber(exp) |
|
end, |
|
"^[_a-zA-Z]", |
|
function() |
|
return env[exp] |
|
end |
|
})) |
|
elseif "table" == _exp_0 then |
|
local _exp_1 = exp.label |
|
if "pair" == _exp_1 then |
|
return eval_exp(exp[1], env, function(left) |
|
return eval_exp(exp[2], env, function(right) |
|
return k({ |
|
left, |
|
right |
|
}) |
|
end) |
|
end) |
|
elseif "apply" == _exp_1 then |
|
return eval_apply(exp, env, function(exp) |
|
return k(exp) |
|
end) |
|
elseif "lambda" == _exp_1 then |
|
return eval_lambda(exp, env, k) |
|
end |
|
else |
|
return exp |
|
end |
|
end |
|
eval_lambda = function(lambda, env, k) |
|
local nenv = cp_tbl(env) |
|
return k(function(arg) |
|
nenv[lambda[1]] = arg |
|
funstack:pushcresume((function() |
|
return eval(lambda[2], nenv) |
|
end)) |
|
return funstack:pop()[2] |
|
end) |
|
end |
|
eval_apply = function(line, env, k) |
|
local fun = remove(line, 1) |
|
if t_str(fun) then |
|
fun = env[fun] |
|
end |
|
return eval_exp((remove(line, 1)), env, function(exp) |
|
local _exp_0 = type(fun) |
|
if "function" == _exp_0 then |
|
return line[1] and (eval_apply((insert(line, 1, fun(exp))), env, k)) or (k(fun(exp))) |
|
elseif "table" == _exp_0 then |
|
local _exp_1 = fun.label |
|
if "apply" == _exp_1 then |
|
return eval_apply(fun, env, function(f) |
|
return line[1] and (eval_apply((insert(line, 1, f)), env, k)) or (k(f(exp))) |
|
end) |
|
elseif "lambda" == _exp_1 then |
|
return eval_lambda(fun, env, function(f) |
|
return line[1] and (eval_apply((insert(line, 1, f)), env, k)) or (k(f(exp))) |
|
end) |
|
end |
|
end |
|
end) |
|
end |
|
eval_block = function(stat, env, k) |
|
local _exp_0 = stat.label |
|
if "var" == _exp_0 then |
|
return k(eval_exp(stat[2], env, function(exp) |
|
env[stat[1]] = exp |
|
end)) |
|
elseif "apply" == _exp_0 then |
|
return eval_apply(stat, env, k) |
|
elseif "return" == _exp_0 then |
|
return eval_exp(stat[1], env, function(exp) |
|
return funstack:stopregret(exp) |
|
end) |
|
end |
|
end |
|
eval = function(syntaxtree, env, k) |
|
if k == nil then |
|
k = noop |
|
end |
|
syntaxtree = cp_tbl(syntaxtree) |
|
if not (syntaxtree[1]) then |
|
return k(env or { }) |
|
else |
|
env = setmetatable((env or { }), { |
|
__index = builtin |
|
}) |
|
return eval_block((remove(syntaxtree, 1)), env, function() |
|
return eval(syntaxtree, env, k) |
|
end) |
|
end |
|
end |
|
return eval |