eval.lua
Branches > main > 302c951 > files / org / faddle / lang / eval.lua
local LIST = require("org.faddle.lang.list")
local is_list = LIST.is_list
local cons = LIST.cons
local count = LIST.count
local first = LIST.first
local rest = LIST.rest
local apply = LIST.apply
local empty = LIST.empty
local symbol = require("org.faddle.lang.symbol")
local is_symbol = symbol.is_symbol
local intern = symbol.intern
local compiler = require("org.faddle.lang.compile")
local compile = compiler.compile
local execute = compiler.execute
local eval
-- Table of module names -> module tables?
-- Or var tables, idk.
-- Right now it's symbols -> objects and maybe var tracking should be separate idk
local modules = {}
-- So this should add both to a registry of vars, as well as the munged names mapping to the values in the module return table
-- And re-defining updates the var and calls its handler or whatever, or calls an update_var() idk, and it also updates the module table
local function def(var_name, args, env)
assert(is_symbol(var_name), "def must be called with a symbol")
assert(modules[env.current_module] ~= nil, "Cannot define var in non-existent module '" .. env.current_module .. "'")
local val = eval(first(args), env)
-- Doesn't add to the return table right now
-- Probably want to munge names so they're easier to use from Lua.
-- I'm not sure whether like 'foo?' and 'is_foo' should be the same symbol or just munged to produce the same thing.
print(env.current_module)
print(var_name)
print(env.user)
modules[env.current_module][var_name] = val
return var_name
end
local function module(mod_name, env)
if type(mod_name) ~= "string" then error("First argument to module must be a string!") end
-- Why not?
if package.loaded[mod_name] and not modules[mod_name] then
error("Cannot override already defined Lua module " .. mod_name)
else
-- This is not the return table but the module object...
local M = modules[mod_name]
if not M then
M = {}
modules[mod_name] = M
end
env.current_module = mod_name
return mod_name
end
end
-- Would be nice if these were real functions/macros?
-- And qualified as idk org.faddle.compiler:def that way you can safely shadow def in core and elsewhere.
local DEF = intern("def")
local MODULE = intern("module")
local QUOTE = intern("quote")
local FN = intern("fn")
-- Should eventually be in core/boot ofc they're just here now so I can test functions before figuring out modules
local temp_functions =
{
[intern("list")] = LIST.list,
[intern("plus")] = function(...)
local x = 0
for i = 1, select("#", ...) do
local n = select(i, ...)
if n == nil then n = 0 end
x = x + n
end
return x
end
}
-- Perhaps special forms should be looked up like anything else, and be shadowable?
-- If you lookup say 'def', it returns nil which is strange
local function lookup(sym, env)
return temp_functions[sym] or modules[env.current_module][sym]
end
-- Not quite sure how to do this...
-- Do tables need to be compiled into Lua?
-- Not simple ones, but others may benefit, I'm not sure.
local function eval_table(t, env)
local ret = {}
for k, v in pairs(t) do
-- Could end up with holes, but that's the user's fault
ret[eval(k, env)] = eval(v, env)
end
return ret
end
local function map_eval(lst, env)
if lst == empty then return lst end
local res = eval(first(lst), env)
return cons(res, map_eval(rest(lst), env))
end
local function second(lst) return first(rest(lst)) end
local function call(f, args)
if type(f) == "number" or type(f) == "string" then
local t = first(args)
local not_found = second(args)
if t == nil then
return not_found
else
local res = t[f]
if res == nil then return not_found else return res end
end
else
return apply(f, args)
end
end
-- So is this what loads core and alises?
local function create_env(mod_name)
local env = {current_module = mod_name}
module(mod_name, env)
return env
end
eval = function(form, env)
if is_list(form) then
if count(form) == 0 then return form end
local f = first(form)
if f == QUOTE then return second(form) end
if f == MODULE then return module(second(form), env) end
if f == DEF then return def(second(form), rest(rest(form)), env) end
if f == FN then return execute(compile(form), "eval_fn") end
form = map_eval(form, env)
return call(first(form), rest(form))
end
if is_symbol(form) then return lookup(form, env) end
if type(form) == "table" then return eval_table(form, env) end
return form -- string, number, function, thread, userdata, nil
end
return {eval = eval, create_env = create_env}