aboutsummaryrefslogtreecommitdiff
path: root/nomsub.lua
diff options
context:
space:
mode:
Diffstat (limited to 'nomsub.lua')
-rw-r--r--nomsub.lua839
1 files changed, 839 insertions, 0 deletions
diff --git a/nomsub.lua b/nomsub.lua
new file mode 100644
index 0000000..f111fc7
--- /dev/null
+++ b/nomsub.lua
@@ -0,0 +1,839 @@
+local re = require('re')
+local lpeg = require('lpeg')
+local utils = require('utils')
+local INDENT = " "
+lpeg.setmaxstack(10000)
+local P, V, S, Cg, C, Cp, B, Cmt
+P, V, S, Cg, C, Cp, B, Cmt = lpeg.P, lpeg.V, lpeg.S, lpeg.Cg, lpeg.C, lpeg.Cp, lpeg.B, lpeg.Cmt
+local wordchar = P(1) - S(' \t\n\r%:;,.{}[]()"')
+local comment = re.compile([[comment <- "(#" (comment / ((! "#)") .))* "#)"]])
+local whitespace = (S(" \t") + comment) ^ 1
+local nl = P("\n")
+local blank_line = whitespace ^ -1 * nl
+local get_line_indentation
+get_line_indentation = function(line)
+ local indent_amounts = {
+ [" "] = 1,
+ ["\t"] = 4
+ }
+ do
+ local sum = 0
+ local leading_space = line:gsub("([\t ]*).*", "%1")
+ for c in leading_space:gmatch("[\t ]") do
+ sum = sum + indent_amounts[c]
+ end
+ return sum
+ end
+end
+local make_parser
+make_parser = function(lingo, extra_definitions)
+ local indent_stack = {
+ 0
+ }
+ local push
+ push = function(n)
+ return table.insert(indent_stack, n)
+ end
+ local pop
+ pop = function()
+ return table.remove(indent_stack)
+ end
+ local check_indent
+ check_indent = function(subject, end_pos, spaces)
+ local num_spaces = get_line_indentation(spaces)
+ if num_spaces <= indent_stack[#indent_stack] then
+ return nil
+ end
+ push(num_spaces)
+ return end_pos
+ end
+ local check_dedent
+ check_dedent = function(subject, end_pos, spaces)
+ local num_spaces = get_line_indentation(spaces)
+ if num_spaces >= indent_stack[#indent_stack] then
+ return nil
+ end
+ pop()
+ return end_pos
+ end
+ local check_nodent
+ check_nodent = function(subject, end_pos, spaces)
+ local num_spaces = get_line_indentation(spaces)
+ if num_spaces ~= indent_stack[#indent_stack] then
+ return nil
+ end
+ return end_pos
+ end
+ local defs = {
+ wordchar = wordchar,
+ nl = nl,
+ ws = whitespace,
+ comment = comment,
+ eol = #nl + (P("") - P(1)),
+ word_boundary = whitespace + B(P("..")) + B(S("\";)]")) + #S("\":([") + #((whitespace + nl) ^ 0 * P("..")),
+ indent = #(nl * blank_line ^ 0 * Cmt(whitespace ^ -1, check_indent)),
+ dedent = #(nl * blank_line ^ 0 * Cmt(whitespace ^ -1, check_dedent)),
+ new_line = nl * blank_line ^ 0 * Cmt(whitespace ^ -1, check_nodent),
+ error_handler = function(src, pos, errors)
+ local line_no = 1
+ for _ in src:sub(1, -#errors):gmatch("\n") do
+ line_no = line_no + 1
+ end
+ local err_pos = #src - #errors + 1
+ if errors:sub(1, 1) == "\n" then
+ err_pos = err_pos + #errors:match("[ \t]*", 2)
+ end
+ local start_of_err_line = err_pos
+ while src:sub(start_of_err_line, start_of_err_line) ~= "\n" do
+ start_of_err_line = start_of_err_line - 1
+ end
+ local start_of_prev_line = start_of_err_line - 1
+ while src:sub(start_of_prev_line, start_of_prev_line) ~= "\n" do
+ start_of_prev_line = start_of_prev_line - 1
+ end
+ local prev_line, err_line, next_line = src:match("([^\n]*)\n([^\n]*)\n([^\n]*)", start_of_prev_line + 1)
+ local pointer = ("-"):rep(err_pos - start_of_err_line + 0) .. "^"
+ return error("\nParse error on line " .. tostring(line_no) .. ":\n\n" .. tostring(prev_line) .. "\n" .. tostring(err_line) .. "\n" .. tostring(pointer) .. "\n" .. tostring(next_line) .. "\n")
+ end
+ }
+ if extra_definitions then
+ for k, v in pairs(extra_definitions) do
+ defs[k] = v
+ end
+ end
+ setmetatable(defs, {
+ __index = function(t, key)
+ local fn
+ fn = function(src, value, errors)
+ local token = {
+ type = key,
+ src = src,
+ value = value,
+ errors = errors
+ }
+ return token
+ end
+ t[key] = fn
+ return fn
+ end
+ })
+ return re.compile(lingo, defs)
+end
+local Compiler
+do
+ local _class_0
+ local _base_0 = {
+ call = function(self, fn_name, ...)
+ local fn_info = self.defs[fn_name]
+ if fn_info == nil then
+ self:error("Attempt to call undefined function: " .. tostring(fn_name))
+ end
+ if fn_info.is_macro then
+ self:error("Attempt to call macro at runtime: " .. tostring(fn_name))
+ end
+ if not (self:check_permission(fn_name)) then
+ self:error("You do not have the authority to call: " .. tostring(fn_name))
+ end
+ table.insert(self.callstack, fn_name)
+ local fn, arg_names
+ fn, arg_names = fn_info.fn, fn_info.arg_names
+ local args
+ do
+ local _tbl_0 = { }
+ for i, name in ipairs(arg_names) do
+ _tbl_0[name] = select(i, ...)
+ end
+ args = _tbl_0
+ end
+ if self.debug then
+ print("Calling " .. tostring(fn_name) .. " with args: " .. tostring(utils.repr(args)))
+ end
+ local ret = fn(self, args)
+ table.remove(self.callstack)
+ return ret
+ end,
+ check_permission = function(self, fn_name)
+ local fn_info = self.defs[fn_name]
+ if fn_info == nil then
+ self:error("Undefined function: " .. tostring(fn_name))
+ end
+ if fn_info.whiteset == nil then
+ return true
+ end
+ local _list_0 = self.callstack
+ for _index_0 = 1, #_list_0 do
+ local caller = _list_0[_index_0]
+ if fn_info.whiteset[caller] then
+ return true
+ end
+ end
+ return false
+ end,
+ def = function(self, spec, fn)
+ if self.debug then
+ print("Defining rule: " .. tostring(spec))
+ end
+ local invocations, arg_names = self:get_invocations(spec)
+ local fn_info = {
+ fn = fn,
+ arg_names = arg_names,
+ invocations = invocations,
+ is_macro = false
+ }
+ for _index_0 = 1, #invocations do
+ local invocation = invocations[_index_0]
+ self.defs[invocation] = fn_info
+ end
+ end,
+ get_invocations = function(self, text)
+ if type(text) == 'string' then
+ text = {
+ text
+ }
+ end
+ local invocations = { }
+ local arg_names
+ for _index_0 = 1, #text do
+ local _text = text[_index_0]
+ local invocation = _text:gsub("%%%S+", "%%")
+ local _arg_names
+ do
+ local _accum_0 = { }
+ local _len_0 = 1
+ for arg in _text:gmatch("%%(%S+)") do
+ _accum_0[_len_0] = arg
+ _len_0 = _len_0 + 1
+ end
+ _arg_names = _accum_0
+ end
+ table.insert(invocations, invocation)
+ if arg_names then
+ if not utils.equivalent(utils.set(arg_names), utils.set(_arg_names)) then
+ self:error("Conflicting argument names " .. tostring(utils.repr(arg_names)) .. " and " .. tostring(utils.repr(_arg_names)) .. " for " .. tostring(utils.repr(text)))
+ end
+ else
+ arg_names = _arg_names
+ end
+ end
+ return invocations, arg_names
+ end,
+ defmacro = function(self, spec, lua_gen_fn)
+ local invocations, arg_names = self:get_invocations(spec)
+ local fn_info = {
+ fn = lua_gen_fn,
+ arg_names = arg_names,
+ invocations = invocations,
+ is_macro = true
+ }
+ for _index_0 = 1, #invocations do
+ local invocation = invocations[_index_0]
+ self.defs[invocation] = fn_info
+ end
+ end,
+ run = function(self, text)
+ if self.debug then
+ print("RUNNING TEXT:\n" .. tostring(text))
+ end
+ local code = self:compile(text)
+ if self.debug then
+ print("\nGENERATED LUA CODE:\n" .. tostring(code))
+ end
+ return code
+ end,
+ parse = function(self, str)
+ if self.debug then
+ print("PARSING:\n" .. tostring(str))
+ end
+ local lingo = [=[ file <- ({ {| %ws? %new_line? {:body: block :} %new_line? %ws? (errors)? |} }) -> File
+ errors <- (({.+}) => error_handler)
+ block <- ({ {| statement (%new_line statement)* |} }) -> Block
+ statement <- ({ (functioncall / expression) }) -> Statement
+ one_liner <- ({ {|
+ (({
+ (({ {|
+ (expression (%word_boundary fn_bit)+) / (word (%word_boundary fn_bit)*)
+ |} }) -> FunctionCall)
+ / (expression)
+ }) -> Statement)
+ |} }) -> Block
+
+ functioncall <- ({ {| (expression %word_boundary fn_bits) / (word (%word_boundary fn_bits)?) |} }) -> FunctionCall
+ fn_bit <- (expression / word)
+ fn_bits <-
+ ((".." %ws? (%indent %new_line indented_fn_bits %dedent) (%new_line ".." %ws? fn_bits)?)
+ / (%new_line ".." fn_bit (%word_boundary fn_bits)?)
+ / (fn_bit (%word_boundary fn_bits)?))
+ indented_fn_bits <-
+ fn_bit ((%new_line / %word_boundary) indented_fn_bits)?
+
+ thunk <-
+ ({ ":" %ws?
+ ((%indent %new_line block ((%dedent (%new_line "..")?) / errors))
+ / (one_liner (%ws? (%new_line? ".."))?)) }) -> Thunk
+
+ word <- ({ !number {%wordchar+} }) -> Word
+ expression <- ({ (longstring / string / number / variable / list / thunk / subexpression) }) -> Expression
+
+ string <- ({ (!longstring) '"' {(("\" .) / [^"])*} '"' }) -> String
+ longstring <- ({ '".."' %ws? %indent {(%new_line "|" [^%nl]*)+} ((%dedent (%new_line '..')?) / errors) }) -> Longstring
+ number <- ({ {'-'? [0-9]+ ("." [0-9]+)?} }) -> Number
+ variable <- ({ ("%" {%wordchar+}) }) -> Var
+
+ subexpression <-
+ (!%comment "(" %ws? (functioncall / expression) %ws? ")")
+ / ("(..)" %ws? %indent %new_line ((({ {| indented_fn_bits |} }) -> FunctionCall) / expression) %dedent (%new_line "..")?)
+
+ list <- ({ {|
+ ("[..]" %ws? %indent %new_line indented_list ","? ((%dedent (%new_line "..")?) / errors))
+ / ("[" %ws? (list_items ","?)? %ws?"]")
+ |} }) -> List
+ list_items <- ((functioncall / expression) (list_sep list_items)?)
+ list_sep <- %ws? "," %ws?
+ indented_list <-
+ (functioncall / expression) (((list_sep %new_line?) / %new_line) indented_list)?
+ ]=]
+ lingo = make_parser(lingo)
+ local tree = lingo:match(str:gsub("\r", "") .. "\n")
+ if self.debug then
+ print("\nPARSE TREE:")
+ self:print_tree(tree)
+ end
+ assert(tree, "Failed to parse: " .. tostring(str))
+ return tree
+ end,
+ tree_to_value = function(self, tree)
+ local code = "return (function(compiler, vars)\nreturn " .. tostring(self:tree_to_lua(tree)) .. "\nend)"
+ local lua_thunk, err = load(code)
+ if not lua_thunk then
+ error("Failed to compile generated code:\n" .. tostring(code) .. "\n\n" .. tostring(err))
+ end
+ return (lua_thunk())(self, { })
+ end,
+ tree_to_lua = function(self, tree, kind)
+ if kind == nil then
+ kind = "Expression"
+ end
+ assert(tree, "No tree provided.")
+ local indent = ""
+ local buffer = { }
+ local to_lua
+ to_lua = function(t, kind)
+ local ret = self:tree_to_lua(t, kind)
+ return ret
+ end
+ local add
+ add = function(code)
+ return table.insert(buffer, code)
+ end
+ local _exp_0 = tree.type
+ if "File" == _exp_0 then
+ add([[return (function(compiler, vars)
+ local ret]])
+ local vars = { }
+ local _list_0 = tree.value.body.value
+ for _index_0 = 1, #_list_0 do
+ local statement = _list_0[_index_0]
+ local code = to_lua(statement)
+ local lua_thunk, err = load("return (function(compiler, vars)\n" .. tostring(code) .. "\nend)")
+ if not lua_thunk then
+ error("Failed to compile generated code:\n" .. tostring(code) .. "\n\n" .. tostring(err))
+ end
+ local ok
+ ok, err = pcall(lua_thunk)
+ if not ok then
+ error(err)
+ end
+ ok, err = pcall(err, self, vars)
+ if not ok then
+ self:error(err)
+ end
+ add(code)
+ end
+ add([[ return ret
+ end)
+ ]])
+ elseif "Block" == _exp_0 then
+ local _list_0 = tree.value
+ for _index_0 = 1, #_list_0 do
+ local statement = _list_0[_index_0]
+ add(to_lua(statement))
+ end
+ elseif "Thunk" == _exp_0 then
+ assert(tree.value.type == "Block", "Non-block value in Thunk")
+ add([[ (function(compiler, vars)
+ local ret]])
+ add(to_lua(tree.value))
+ add([[ return ret
+ end)
+ ]])
+ elseif "Statement" == _exp_0 then
+ if tree.value.type == "FunctionCall" then
+ local name = self:fn_name_from_tree(tree.value)
+ if self.defs[name] and self.defs[name].is_macro then
+ add(self:run_macro(tree.value, "Statement"))
+ else
+ add("ret = " .. (to_lua(tree.value):match("%s*(.*)")))
+ end
+ else
+ add("ret = " .. (to_lua(tree.value):match("%s*(.*)")))
+ end
+ elseif "Expression" == _exp_0 then
+ add(to_lua(tree.value))
+ elseif "FunctionCall" == _exp_0 then
+ local name = self:fn_name_from_tree(tree)
+ if self.defs[name] and self.defs[name].is_macro then
+ add(self:run_macro(tree, "Expression"))
+ else
+ local args
+ do
+ local _accum_0 = { }
+ local _len_0 = 1
+ local _list_0 = tree.value
+ for _index_0 = 1, #_list_0 do
+ local a = _list_0[_index_0]
+ if a.type ~= "Word" then
+ _accum_0[_len_0] = to_lua(a)
+ _len_0 = _len_0 + 1
+ end
+ end
+ args = _accum_0
+ end
+ table.insert(args, 1, utils.repr(name, true))
+ add(self.__class:comma_separated_items("compiler:call(", args, ")"))
+ end
+ elseif "String" == _exp_0 then
+ local escapes = {
+ n = "\n",
+ t = "\t",
+ b = "\b",
+ a = "\a",
+ v = "\v",
+ f = "\f",
+ r = "\r"
+ }
+ local unescaped = tree.value:gsub("\\(.)", (function(c)
+ return escapes[c] or c
+ end))
+ add(utils.repr(unescaped, true))
+ elseif "Longstring" == _exp_0 then
+ local result
+ do
+ local _accum_0 = { }
+ local _len_0 = 1
+ for line in tree.value:gmatch("[ \t]*|([^\n]*)") do
+ _accum_0[_len_0] = line
+ _len_0 = _len_0 + 1
+ end
+ result = _accum_0
+ end
+ add(utils.repr(table.concat(result, "\n"), true))
+ elseif "Number" == _exp_0 then
+ add(tree.value)
+ elseif "List" == _exp_0 then
+ if #tree.value == 0 then
+ add("{}")
+ elseif #tree.value == 1 then
+ add("{" .. tostring(to_lua(tree.value[1])) .. "}")
+ else
+ add(self.__class:comma_separated_items("{", (function()
+ local _accum_0 = { }
+ local _len_0 = 1
+ local _list_0 = tree.value
+ for _index_0 = 1, #_list_0 do
+ local item = _list_0[_index_0]
+ _accum_0[_len_0] = to_lua(item)
+ _len_0 = _len_0 + 1
+ end
+ return _accum_0
+ end)(), "}"))
+ end
+ elseif "Var" == _exp_0 then
+ add("vars[" .. tostring(utils.repr(tree.value, true)) .. "]")
+ else
+ error("Unknown/unimplemented thingy: " .. tostring(tree.type))
+ end
+ buffer = table.concat(buffer, "\n")
+ return buffer
+ end,
+ fn_name_from_tree = function(self, tree)
+ assert(tree.type == "FunctionCall", "Attempt to get fn name from non-functioncall tree: " .. tostring(tree.type))
+ local name_bits = { }
+ local _list_0 = tree.value
+ for _index_0 = 1, #_list_0 do
+ local token = _list_0[_index_0]
+ table.insert(name_bits, (function()
+ if token.type == "Word" then
+ return token.value
+ else
+ return "%"
+ end
+ end)())
+ end
+ return table.concat(name_bits, " ")
+ end,
+ run_macro = function(self, tree, kind)
+ if kind == nil then
+ kind = "Expression"
+ end
+ local name = self:fn_name_from_tree(tree)
+ if not (self.defs[name] and self.defs[name].is_macro) then
+ self:error("Macro not found: " .. tostring(name))
+ end
+ if not (self:check_permission(name)) then
+ self:error("You do not have the authority to call: " .. tostring(name))
+ end
+ local fn, arg_names
+ do
+ local _obj_0 = self.defs[name]
+ fn, arg_names = _obj_0.fn, _obj_0.arg_names
+ end
+ local args
+ do
+ local _accum_0 = { }
+ local _len_0 = 1
+ local _list_0 = tree.value
+ for _index_0 = 1, #_list_0 do
+ local a = _list_0[_index_0]
+ if a.type ~= "Word" then
+ _accum_0[_len_0] = a
+ _len_0 = _len_0 + 1
+ end
+ end
+ args = _accum_0
+ end
+ do
+ local _tbl_0 = { }
+ for i, name in ipairs(arg_names) do
+ _tbl_0[name] = args[i]
+ end
+ args = _tbl_0
+ end
+ table.insert(self.callstack, name)
+ local ret, manual_mode = fn(self, args, kind)
+ table.remove(self.callstack)
+ if not ret then
+ self:error("No return value for macro: " .. tostring(name))
+ end
+ if kind == "Statement" and not manual_mode then
+ ret = "ret = " .. ret
+ end
+ return ret
+ end,
+ _yield_tree = function(self, tree, indent_level)
+ if indent_level == nil then
+ indent_level = 0
+ end
+ local ind
+ ind = function(s)
+ return INDENT:rep(indent_level) .. s
+ end
+ local _exp_0 = tree.type
+ if "File" == _exp_0 then
+ coroutine.yield(ind("File:"))
+ self:_yield_tree(tree.value.body, indent_level + 1)
+ elseif "Errors" == _exp_0 then
+ coroutine.yield(ind("Error:\n" .. tostring(tree.value)))
+ elseif "Block" == _exp_0 then
+ local _list_0 = tree.value
+ for _index_0 = 1, #_list_0 do
+ local chunk = _list_0[_index_0]
+ self:_yield_tree(chunk, indent_level)
+ end
+ elseif "Thunk" == _exp_0 then
+ coroutine.yield(ind("Thunk:"))
+ self:_yield_tree(tree.value, indent_level + 1)
+ elseif "Statement" == _exp_0 then
+ self:_yield_tree(tree.value, indent_level)
+ elseif "Expression" == _exp_0 then
+ self:_yield_tree(tree.value, indent_level)
+ elseif "FunctionCall" == _exp_0 then
+ local name = self:fn_name_from_tree(tree)
+ local args
+ do
+ local _accum_0 = { }
+ local _len_0 = 1
+ local _list_0 = tree.value
+ for _index_0 = 1, #_list_0 do
+ local a = _list_0[_index_0]
+ if a.type ~= "Word" then
+ _accum_0[_len_0] = a
+ _len_0 = _len_0 + 1
+ end
+ end
+ args = _accum_0
+ end
+ if #args == 0 then
+ coroutine.yield(ind("Call [" .. tostring(name) .. "]!"))
+ else
+ coroutine.yield(ind("Call [" .. tostring(name) .. "]:"))
+ for _index_0 = 1, #args do
+ local a = args[_index_0]
+ self:_yield_tree(a, indent_level + 1)
+ end
+ end
+ elseif "String" == _exp_0 then
+ coroutine.yield(ind(utils.repr(tree.value, true)))
+ elseif "Longstring" == _exp_0 then
+ coroutine.yield(ind(utils.repr(tree.value, true)))
+ elseif "Number" == _exp_0 then
+ coroutine.yield(ind(tree.value))
+ elseif "List" == _exp_0 then
+ if #tree.value == 0 then
+ coroutine.yield(ind("<Empty List>"))
+ else
+ coroutine.yield(ind("List:"))
+ local _list_0 = tree.value
+ for _index_0 = 1, #_list_0 do
+ local item = _list_0[_index_0]
+ self:_yield_tree(item, indent_level + 1)
+ end
+ end
+ elseif "Var" == _exp_0 then
+ coroutine.yield(ind("Var[" .. tostring(utils.repr(tree.value)) .. "]"))
+ else
+ error("Unknown/unimplemented thingy: " .. tostring(tree.type))
+ end
+ return nil
+ end,
+ print_tree = function(self, tree)
+ for line in coroutine.wrap(function()
+ return self:_yield_tree(tree)
+ end) do
+ print(line)
+ end
+ end,
+ stringify_tree = function(self, tree)
+ local result = { }
+ for line in coroutine.wrap(function()
+ return self:_yield_tree(tree)
+ end) do
+ table.insert(result, line)
+ end
+ return table.concat(result, "\n")
+ end,
+ compile = function(self, src, output_file)
+ if output_file == nil then
+ output_file = nil
+ end
+ if self.debug then
+ print("COMPILING:\n" .. tostring(src))
+ end
+ local tree = self:parse(src)
+ assert(tree, "Tree failed to compile: " .. tostring(src))
+ local code = self:tree_to_lua(tree)
+ if output_file then
+ local output = io.open(output_file, "w")
+ output:write(code)
+ end
+ return code
+ end,
+ error = function(self, ...)
+ print(...)
+ print("Callstack:")
+ for i = #self.callstack, 1, -1 do
+ print(" " .. tostring(self.callstack[i]))
+ end
+ return error()
+ end,
+ test = function(self, src, expected)
+ local i = 1
+ while i ~= nil do
+ local start, stop = src:find("\n\n", i)
+ local test = src:sub(i, start)
+ i = stop
+ start, stop = test:find("===")
+ if not start or not stop then
+ self:error("WHERE'S THE ===? in:\n" .. tostring(test))
+ end
+ local test_src
+ test_src, expected = test:sub(1, start - 1), test:sub(stop + 1, -1)
+ expected = expected:match('[\n]*(.*[^\n])')
+ local tree = self:parse(test_src)
+ local got = self:stringify_tree(tree.value.body)
+ if got ~= expected then
+ self:error("TEST FAILED!\nSource:\n" .. tostring(test_src) .. "\nExpected:\n" .. tostring(expected) .. "\n\nGot:\n" .. tostring(got))
+ end
+ end
+ end,
+ initialize_core = function(self)
+ local as_lua_code
+ as_lua_code = function(self, str)
+ local _exp_0 = str.type
+ if "String" == _exp_0 then
+ local escapes = {
+ n = "\n",
+ t = "\t",
+ b = "\b",
+ a = "\a",
+ v = "\v",
+ f = "\f",
+ r = "\r"
+ }
+ local unescaped = str.value:gsub("\\(.)", (function(c)
+ return escapes[c] or c
+ end))
+ return unescaped
+ elseif "Longstring" == _exp_0 then
+ local result
+ do
+ local _accum_0 = { }
+ local _len_0 = 1
+ for line in str.value:gmatch("[ \t]*|([^\n]*)") do
+ _accum_0[_len_0] = line
+ _len_0 = _len_0 + 1
+ end
+ result = _accum_0
+ end
+ return table.concat(result, "\n")
+ else
+ return self:tree_to_lua(str)
+ end
+ end
+ self:defmacro([[lua block %lua_code]], function(self, vars, kind)
+ if kind == "Expression" then
+ error("Expected to be in statement.")
+ end
+ local lua_code = vars.lua_code.value
+ local _exp_0 = lua_code.type
+ if "List" == _exp_0 then
+ return table.concat((function()
+ local _accum_0 = { }
+ local _len_0 = 1
+ local _list_0 = lua_code.value
+ for _index_0 = 1, #_list_0 do
+ local i = _list_0[_index_0]
+ _accum_0[_len_0] = as_lua_code(self, i.value)
+ _len_0 = _len_0 + 1
+ end
+ return _accum_0
+ end)()), true
+ else
+ return as_lua_code(self, lua_code), true
+ end
+ end)
+ self:defmacro([[lua expr %lua_code]], function(self, vars, kind)
+ local lua_code = vars.lua_code.value
+ local _exp_0 = lua_code.type
+ if "List" == _exp_0 then
+ return table.concat((function()
+ local _accum_0 = { }
+ local _len_0 = 1
+ local _list_0 = lua_code.value
+ for _index_0 = 1, #_list_0 do
+ local i = _list_0[_index_0]
+ _accum_0[_len_0] = as_lua_code(self, i.value)
+ _len_0 = _len_0 + 1
+ end
+ return _accum_0
+ end)())
+ else
+ return as_lua_code(self, lua_code)
+ end
+ end)
+ self:def("rule %spec %body", function(self, vars)
+ return self:def(vars.spec, vars.body)
+ end)
+ self:defmacro([[macro %spec %body]], function(self, vars, kind)
+ if kind == "Expression" then
+ error("Macro definitions cannot be used as expressions.")
+ end
+ self:defmacro(self:tree_to_value(vars.spec), self:tree_to_value(vars.body))
+ return "", true
+ end)
+ self:defmacro([[macro block %spec %body]], function(self, vars, kind)
+ if kind == "Expression" then
+ error("Macro definitions cannot be used as expressions.")
+ end
+ local invocation = self:tree_to_value(vars.spec)
+ local fn = self:tree_to_value(vars.body)
+ self:defmacro(invocation, (function(self, vars, kind)
+ if kind == "Expression" then
+ error("Macro: " .. tostring(invocation) .. " was defined to be a block, not an expression.")
+ end
+ return fn(self, vars, kind), true
+ end))
+ return "", true
+ end)
+ return self:def("run file %filename", function(self, vars)
+ local file = io.open(vars.filename)
+ return self:run(file:read('*a'))
+ end)
+ end
+ }
+ _base_0.__index = _base_0
+ _class_0 = setmetatable({
+ __init = function(self, parent)
+ self.defs = setmetatable({ }, {
+ __index = parent and parent.defs
+ })
+ self.callstack = { }
+ self.debug = false
+ return self:initialize_core()
+ end,
+ __base = _base_0,
+ __name = "Compiler"
+ }, {
+ __index = _base_0,
+ __call = function(cls, ...)
+ local _self_0 = setmetatable({}, _base_0)
+ cls.__init(_self_0, ...)
+ return _self_0
+ end
+ })
+ _base_0.__class = _class_0
+ local self = _class_0
+ self.comma_separated_items = function(self, open, items, close)
+ return utils.accumulate("\n", function()
+ local buffer = open
+ local so_far = 0
+ for i, item in ipairs(items) do
+ if i < #items then
+ item = item .. ", "
+ end
+ if so_far + #item >= 80 and #buffer > 0 then
+ coroutine.yield(buffer)
+ so_far = so_far - #buffer
+ buffer = item
+ else
+ so_far = so_far + #item
+ buffer = buffer .. item
+ end
+ end
+ buffer = buffer .. close
+ return coroutine.yield(buffer)
+ end)
+ end
+ Compiler = _class_0
+end
+if arg[1] then
+ local c = Compiler()
+ local input = io.open(arg[1]):read("*a")
+ local _print = print
+ local _io_write = io.write
+ if arg[2] == "-" then
+ local nop
+ nop = function() end
+ print, io.write = nop, nop
+ end
+ local code = c:run(input)
+ if arg[2] then
+ local output
+ if arg[2] == "-" then
+ print, io.write = _print, _io_write
+ output = io.output()
+ else
+ output = io.open(arg[2], 'w')
+ end
+ output:write([[ local load = function()
+ ]])
+ output:write(code)
+ output:write([[
+ end
+ local utils = require('utils')
+ local Compiler = require('nomic')
+ local c = Compiler(require('core'))
+ load()(c, {})
+ ]])
+ end
+end
+return Compiler