local U = require "alicorn-utils"
local metalanguage = require "metalanguage"
local eval
local function eval_passhandler(env, val)
return true, val, env
end
local eval
local function Eval(syntax, matcher, environment)
return U.notail(eval(syntax, environment))
end
local evaluates = metalanguage.reducer(Eval, "evaluates")
local function eval_pairhandler(env, a, b)
local ok, combiner, _ = a:match({
evaluates(eval_passhandler, env),
}, metalanguage.failure_handler, env)
if not ok then
return false, combiner
end
local ok, val, newenv = combiner:apply(b, env)
return ok, val, newenv
end
local function symbolenvhandler(env, name)
local res = env:get(name)
if res ~= nil then
return true, res
else
return false, "environment does not contain a binding for " .. name
end
end
local function SymbolInEnvironment(syntax, environment)
return U.notail(syntax:match({
metalanguage.issymbol(symbolenvhandler),
}, metalanguage.failure_handler, environment))
end
local symbol_in_environment = metalanguage.reducer(SymbolInEnvironment, "symbol in env")
function eval(syntax, environment)
return U.notail(syntax:match({
symbol_in_environment(eval_passhandler, environment),
metalanguage.isvalue(eval_passhandler),
metalanguage.ispair(eval_pairhandler),
}, metalanguage.failure_handler, environment))
end
local function syntax_args_val_handler(_, val, newenv)
return true, val, newenv
end
local function syntax_args_nil_handler(data)
return true, false
end
local function syntax_args_pair_handler(env, a, b)
local ok, val, _ = a:match({
evaluates(syntax_args_val_handler, env),
}, metalanguage.failure_handler, nil)
return true, true, val, b
end
local function EvalArgs(syntax, matcher, environment)
local args = {}
local ok, ispair, val, tail = true, true, nil, nil
while ok and ispair do
ok, ispair, val, tail = syntax:match({
metalanguage.ispair(syntax_args_pair_handler),
metalanguage.isnil(syntax_args_nil_handler),
}, metalanguage.failure_handler, environment)
if not ok then
return false, ispair
end
if ispair then
args[#args + 1] = val
syntax = tail
end
end
return true, args
end
local evalargs = metalanguage.reducer(EvalArgs, "evalargs")
local primitive_applicative_mt = {
__index = {
apply = function(self, ops, env)
local ok, args = ops:match({
evalargs(metalanguage.accept_handler, env),
}, metalanguage.failure_handler, nil)
local res = self.fn(table.unpack(args))
return true, U.notail(metalanguage.value(nil, nil, res)), env
end,
},
}
local function primitive_applicative(fn)
return setmetatable({ fn = fn }, primitive_applicative_mt)
end
local primitive_operative_mt = {
__index = {
apply = function(self, ops, env)
return U.notail(self.fn(ops, env))
end,
},
}
local function primitive_operative(fn)
return setmetatable({ fn = fn }, primitive_operative_mt)
end
return {
eval = eval,
evalargs = evalargs,
evaluates = evaluates,
primitive_applicative = primitive_applicative,
primitive_operative = primitive_operative,
}