diff options
| author | Bruce Hill <bitbucket@bruce-hill.com> | 2017-09-12 21:38:54 -0700 |
|---|---|---|
| committer | Bruce Hill <bitbucket@bruce-hill.com> | 2017-09-12 21:38:54 -0700 |
| commit | 0615d127b5fb8aa4b031cb9754b29654e432f641 (patch) | |
| tree | 1f0b3cc12f1503b8486424cf0a81795d9c2b5530 /nomic.lua | |
| parent | 7bcdfdbc1f45a756b257c97b9fd201a412267cda (diff) | |
Added moonc compiled versions of files.
Diffstat (limited to 'nomic.lua')
| -rw-r--r-- | nomic.lua | 847 |
1 files changed, 847 insertions, 0 deletions
diff --git a/nomic.lua b/nomic.lua new file mode 100644 index 0000000..a2cd6c3 --- /dev/null +++ b/nomic.lua @@ -0,0 +1,847 @@ +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 + local _ = [==[ lua_thunk, err = loadstring(code) + if not lua_thunk + error("Failed to compile generated code:\n#{code}\n\n#{err}") + action = lua_thunk! + if @debug + print("Running...") + return action(self, {}) + ]==] + 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 |
