compile.lua
Branches > main > adbfe9f > files / org / faddle / lang / compile.lua
local LIST = require("org.faddle.lang.list")
local is_list = LIST.is_list
local count = LIST.count
local first = LIST.first
local rest = LIST.rest
local symbol = require("org.faddle.lang.symbol")
local is_symbol = symbol.is_symbol
local intern = symbol.intern
local munge = symbol.munge
local module = symbol.module
local lisp_print = require("org.faddle.lang.print").print
-- Still bad :p
local FN = intern("fn")
local LET = intern("let")
local DO = intern("do")
local compile
local gensym_number = 0
local function gensym()
gensym_number = gensym_number + 1
return "G__" .. gensym_number
end
local function ret(statement)
return statement and "return " or ""
end
local function compile_fn(form, statement)
local args = rest(form)
local fn_args = first(args)
local body = rest(args)
assert(#fn_args == 0)
assert(count(body) == 1)
return ret(statement) .. "function() " .. compile(first(body), true) .. " end"
end
-- If I'm just using the same lexical stuff as Lua, how do builtin Lua modules work?
-- In Lua you can just say `io`, but I'd like to wrap these libraries in something like `lua.io`
-- to make it clear where they're coming from...
-- Not sure how I could do that
-- I also want to check locals and fail during compilation if you reference a non-existent local
-- Well `io` is just something you'd have to import in Faddle, since the compiler can just look at what
-- modules have been imported, and `io` won't be one of them.
-- So then `lua.io` or whatever can be defined by Faddle, and map to `io`.
local function compile_let(form, statement)
local args = rest(form)
local bindings = first(args)
local body = rest(args)
assert(type(bindings) == "table" and #bindings % 2 == 0, "Let requires an even number of binding forms")
assert(#bindings == 2)
assert(count(body) == 1)
local sym = bindings[1]
local binding = bindings[2]
if statement then
return "local " .. munge(sym) .. "=" .. compile(binding) .. ";" .. compile(first(body), true)
else
return "return (function()" .. compile_let(form, not statement) .. " end)()"
end
end
--- Returns a string of Lua source code from the form
-- TODO needs to compile returns vs not returns, I think...
-- So this may need to encode the context of whether this is in an expression or statement context
compile = function(form, statement)
if is_list(form) then
-- if count(form) == 0 then return "__rt.list()" end
if count(form) == 0 then return ret(statement) .. "require('org.faddle.lang.rt').list()" end
-- If it's a literal number or string you can compile directly to a lookup
-- Symbols still need to be checked whether they resolve to a string or number though
if first(form) == FN then return compile_fn(form, statement) end
if first(form) == LET then return compile_let(form, statement) end
print(lisp_print(form))
assert(count(form) == 1, "uhhh")
return ret(statement) .. compile(first(form)) .. "()"
-- macro expansion :D
-- core functions
-- vars that are local, however you're supposed to know that :p
elseif is_symbol(form) then
assert(not module(form), "can't compile symbols with modules yet :(")
return ret(statement) .. munge(form)
else
local t = type(form)
if t == "number" or t == "nil" or t == "boolean" then
return ret(statement) .. tostring(form)
elseif t == "string" then
return ret(statement) .. string.format("%q", form)
else
error("Cannot compile form of type '" .. t .. "'")
end
end
end
local loadfn = loadstring or load
local function execute(lua_source, chunk_name)
-- Really, needs to just have all of the std library loaded :p
print(lua_source)
local chunk, err = loadfn("local __rt = require('org.faddle.lang.rt');" .. lua_source, chunk_name)
if not chunk then
error(err)
else
return chunk()
end
end
return {compile = compile, execute = execute}