diff --git a/code_obj.lua b/code_obj.lua index 6dbffca..de62c8f 100644 --- a/code_obj.lua +++ b/code_obj.lua @@ -98,26 +98,35 @@ do end self.__str = nil end, - concat_append = function(self, values, joiner) + concat_append = function(self, values, joiner, wrapping_joiner) + wrapping_joiner = wrapping_joiner or joiner local bits, indents = self.bits, self.indents local match = string.match + local line_len = 0 for i = 1, #values do local b = values[i] - assert(b) if i > 1 then - bits[#bits + 1] = joiner + if line_len > 80 then + bits[#bits + 1] = wrapping_joiner + line_len = 0 + else + bits[#bits + 1] = joiner + end end bits[#bits + 1] = b - if type(b) == 'string' then - do - local spaces = match(b, "\n([ ]*)[^\n]*$") - if spaces then - self.current_indent = #spaces - end - end - elseif self.current_indent ~= 0 then + if type(b) ~= 'string' and self.current_indent ~= 0 then indents[#bits] = self.current_indent end + local b_str = tostring(b) + local line, spaces = match(b_str, "\n(([ ]*)[^\n]*)$") + if spaces then + if type(b) == 'string' then + self.current_indent = #spaces + end + line_len = #line + else + line_len = line_len + #b + end end self.__str = nil end, diff --git a/code_obj.moon b/code_obj.moon index 9e2e324..73a1ba8 100644 --- a/code_obj.moon +++ b/code_obj.moon @@ -66,20 +66,30 @@ class Code indents[#bits] = @current_indent @__str = nil - concat_append: (values, joiner)=> + concat_append: (values, joiner, wrapping_joiner)=> + wrapping_joiner or= joiner bits, indents = @bits, @indents match = string.match + line_len = 0 for i=1,#values b = values[i] - assert(b) if i > 1 - bits[#bits+1] = joiner + if line_len > 80 + bits[#bits+1] = wrapping_joiner + line_len = 0 + else + bits[#bits+1] = joiner bits[#bits+1] = b - if type(b) == 'string' - if spaces = match(b, "\n([ ]*)[^\n]*$") - @current_indent = #spaces - elseif @current_indent != 0 + if type(b) != 'string' and @current_indent != 0 indents[#bits] = @current_indent + b_str = tostring(b) + line, spaces = match(b_str, "\n(([ ]*)[^\n]*)$") + if spaces + if type(b) == 'string' + @current_indent = #spaces + line_len = #line + else + line_len += #b @__str = nil prepend: (...)=> diff --git a/nomsu.lua b/nomsu.lua index f322abf..b8e5137 100644 --- a/nomsu.lua +++ b/nomsu.lua @@ -1,16 +1,10 @@ local lpeg = require('lpeg') local re = require('re') lpeg.setmaxstack(10000) -local P, R, V, S, Cg, C, Cp, B, Cmt, Carg -P, R, V, S, Cg, C, Cp, B, Cmt, Carg = lpeg.P, lpeg.R, lpeg.V, lpeg.S, lpeg.Cg, lpeg.C, lpeg.Cp, lpeg.B, lpeg.Cmt, lpeg.Carg local utils = require('utils') -local repr, stringify, min, max, equivalent, set, is_list, sum -repr, stringify, min, max, equivalent, set, is_list, sum = utils.repr, utils.stringify, utils.min, utils.max, utils.equivalent, utils.set, utils.is_list, utils.sum -local colors = setmetatable({ }, { - __index = function() - return "" - end -}) +local repr, stringify, equivalent +repr, stringify, equivalent = utils.repr, utils.stringify, utils.equivalent +colors = require('consolecolors') colored = setmetatable({ }, { __index = function(_, color) return (function(msg) @@ -35,6 +29,8 @@ do local _obj_0 = require("code_obj") NomsuCode, LuaCode, Source = _obj_0.NomsuCode, _obj_0.LuaCode, _obj_0.Source end +local AST = require("nomsu_tree") +local parse = require("parser") local STDIN, STDOUT, STDERR = "/dev/fd/0", "/dev/fd/1", "/dev/fd/2" string.as_lua_id = function(str) local argnum = 0 @@ -60,6 +56,11 @@ table.map = function(self, fn) end return _accum_0 end +table.fork = function(t, values) + return setmetatable(values or { }, { + __index = t + }) +end FILE_CACHE = setmetatable({ }, { __index = function(self, filename) local file = io.open(filename) @@ -103,12 +104,12 @@ end local line_counter = re.compile([[ lines <- {| line (%nl line)* |} line <- {} (!%nl .)* ]], { - nl = P("\r") ^ -1 * P("\n") + nl = lpeg.P("\r") ^ -1 * lpeg.P("\n") }) local get_lines = re.compile([[ lines <- {| line (%nl line)* |} line <- {[^%nl]*} ]], { - nl = P("\r") ^ -1 * P("\n") + nl = lpeg.P("\r") ^ -1 * lpeg.P("\n") }) LINE_STARTS = setmetatable({ }, { __mode = "k", @@ -127,7 +128,6 @@ LINE_STARTS = setmetatable({ }, { return line_starts end }) -local pos_to_line pos_to_line = function(str, pos) local line_starts = LINE_STARTS[str] local lo, hi = 1, #line_starts @@ -158,9 +158,8 @@ do end end end -local AST = require("nomsu_tree") local _list_mt = { - __eq = utils.equivalent, + __eq = equivalent, __tostring = function(self) return "[" .. concat((function() local _accum_0 = { } @@ -179,7 +178,7 @@ list = function(t) return setmetatable(t, _list_mt) end local _dict_mt = { - __eq = utils.equivalent, + __eq = equivalent, __tostring = function(self) return "{" .. concat((function() local _accum_0 = { } @@ -212,7 +211,6 @@ local NomsuCompiler = setmetatable({ }, { do NomsuCompiler._ENV = NomsuCompiler NomsuCompiler.nomsu = NomsuCompiler - local parse = require("parser") NomsuCompiler.parse = function(self, ...) return parse(...) end @@ -270,9 +268,6 @@ do NomsuCompiler.LuaCode = LuaCode NomsuCompiler.NomsuCode = NomsuCode NomsuCompiler.Source = Source - NomsuCompiler.ARG_ORDERS = setmetatable({ }, { - __mode = "k" - }) NomsuCompiler.ALIASES = setmetatable({ }, { __mode = "k" }) @@ -337,7 +332,7 @@ do end end NomsuCompiler.COMPILE_ACTIONS = setmetatable({ - compile_math_expr = function(self, tree, ...) + ["# compile math expr #"] = function(self, tree, ...) local lua = LuaCode.Value(tree.source) for i, tok in ipairs(tree) do if type(tok) == 'string' then @@ -393,19 +388,10 @@ do }, { __index = function(self, stub) if math_expression:match(stub) then - return self.compile_math_expr + return self["# compile math expr #"] end end }) - NomsuCompiler.fork = function(self) - return setmetatable({ - COMPILE_ACTIONS = setmetatable({ }, { - __index = self.COMPILE_ACTIONS - }) - }, { - __index = self - }) - end NomsuCompiler.run = function(self, to_run, source) if source == nil then source = nil @@ -453,10 +439,12 @@ do for filename in all_files(filename) do local _continue_0 = false repeat - if self.LOADED[filename] then + do ret = self.LOADED[filename] - _continue_0 = true - break + if ret then + _continue_0 = true + break + end end for i, running in ipairs(_running_files) do if running == filename then @@ -471,7 +459,7 @@ do loop = _accum_0 end insert(loop, filename) - error("Circular import, this loops forever: " .. tostring(concat(loop, " -> "))) + error("Circular import, this loops forever: " .. tostring(concat(loop, " -> ")) .. "...") end end insert(_running_files, filename) @@ -479,21 +467,24 @@ do local file = assert(FILE_CACHE[filename], "Could not find file: " .. tostring(filename)) ret = self:run_lua(file, Source(filename, 1, #file)) elseif match(filename, "%.nom$") or match(filename, "^/dev/fd/[012]$") then + local ran_lua if not self.skip_precompiled then local lua_filename = gsub(filename, "%.nom$", ".lua") - local file = FILE_CACHE[lua_filename] - if file then - ret = self:run_lua(file, Source(filename, 1, #file)) - remove(_running_files) - _continue_0 = true - break + do + local file = FILE_CACHE[lua_filename] + if file then + ret = self:run_lua(file, Source(lua_filename, 1, #file)) + ran_lua = true + end end end - local file = file or FILE_CACHE[filename] - if not file then - error("File does not exist: " .. tostring(filename), 0) + if not (ran_lua) then + local file = file or FILE_CACHE[filename] + if not file then + error("File does not exist: " .. tostring(filename), 0) + end + ret = self:run(file, Source(filename, 1, #file)) end - ret = self:run(file, Source(filename, 1, #file)) else error("Invalid filetype for " .. tostring(filename), 0) end @@ -515,13 +506,15 @@ do local lua_string = tostring(lua) local run_lua_fn, err = load(lua_string, nil and tostring(source or lua.source), "t", self) if not run_lua_fn then - local n = 1 - local fn - fn = function() - n = n + 1 - return ("\n%-3d|"):format(n) - end - local line_numbered_lua = "1 |" .. lua_string:gsub("\n", fn) + local line_numbered_lua = concat((function() + local _accum_0 = { } + local _len_0 = 1 + for i, line in ipairs(get_lines:match(lua_string)) do + _accum_0[_len_0] = format("%3d|%s", i, line) + _len_0 = _len_0 + 1 + end + return _accum_0 + end)(), "\n") error("Failed to compile generated code:\n" .. tostring(colored.bright(colored.blue(colored.onblack(line_numbered_lua)))) .. "\n\n" .. tostring(err), 0) end local source_key = tostring(source or lua.source) @@ -559,7 +552,6 @@ do return run_lua_fn() end NomsuCompiler.compile = function(self, tree) - assert(LuaCode) local _exp_0 = tree.type if "Action" == _exp_0 then local stub = tree.stub @@ -579,21 +571,6 @@ do end args = _accum_0 end - do - local arg_orders = self.ARG_ORDERS[stub] - if arg_orders then - do - local _accum_0 = { } - local _len_0 = 1 - for _index_0 = 1, #arg_orders do - local p = arg_orders[_index_0] - _accum_0[_len_0] = args[p] - _len_0 = _len_0 + 1 - end - args = _accum_0 - end - end - end local ret = compile_action(self, tree, unpack(args)) if not ret then self:compile_error(tree, "Compile-time action:\n%s\nfailed to produce any Lua") @@ -601,28 +578,7 @@ do return ret end end - local action = self['A' .. string.as_lua_id(stub)] - local lua = LuaCode.Value(tree.source) - if not action and math_expression:match(stub) then - for i, tok in ipairs(tree) do - if type(tok) == 'string' then - lua:append(tok) - else - local tok_lua = self:compile(tok) - if not (tok_lua.is_value) then - self:compile_error(tok, "Non-expression value inside math expression:\n%s") - end - if tok.type == "Action" then - tok_lua:parenthesize() - end - lua:append(tok_lua) - end - if i < #tree then - lua:append(" ") - end - end - return lua - end + local lua = LuaCode.Value(tree.source, "A", string.as_lua_id(stub), "(") local args = { } for i, tok in ipairs(tree) do local _continue_0 = false @@ -642,30 +598,7 @@ do break end end - if action then - do - local arg_orders = self.ARG_ORDERS[stub] - if arg_orders then - do - local _accum_0 = { } - local _len_0 = 1 - for _index_0 = 1, #arg_orders do - local p = arg_orders[_index_0] - _accum_0[_len_0] = args[p] - _len_0 = _len_0 + 1 - end - args = _accum_0 - end - end - end - end - lua:append("A", string.as_lua_id(stub), "(") - for i, arg in ipairs(args) do - lua:append(arg) - if i < #args then - lua:append(", ") - end - end + lua:concat_append(args, ", ") lua:append(")") return lua elseif "EscapedNomsu" == _exp_0 then @@ -690,13 +623,16 @@ do return LuaCode.Value(tree.source, make_tree(tree[1])) elseif "Block" == _exp_0 then local lua = LuaCode(tree.source) - for i, line in ipairs(tree) do - local line_lua = self:compile(line) - if i > 1 then - lua:append("\n") + lua:concat_append((function() + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #tree do + local line = tree[_index_0] + _accum_0[_len_0] = self:compile(line):as_statements() + _len_0 = _len_0 + 1 end - lua:append(line_lua:as_statements()) - end + return _accum_0 + end)(), "\n") return lua elseif "Text" == _exp_0 then local lua = LuaCode.Value(tree.source) @@ -747,55 +683,29 @@ do return lua elseif "List" == _exp_0 then local lua = LuaCode.Value(tree.source, "list{") - local line_length = 0 + local items = { } for i, item in ipairs(tree) do local item_lua = self:compile(item) if not (item_lua.is_value) then self:compile_error(item, "Cannot use:\n%s\nas a list item, since it's not an expression.") end - lua:append(item_lua) - local item_string = tostring(item_lua) - local last_line = match(item_string, "[^\n]*$") - if match(item_string, "\n") then - line_length = #last_line - else - line_length = line_length + #last_line - end - if i < #tree then - if line_length >= MAX_LINE then - lua:append(",\n ") - line_length = 0 - else - lua:append(", ") - line_length = line_length + 2 - end - end + items[i] = item_lua end + lua:concat_append(items, ", ", ",\n ") lua:append("}") return lua elseif "Dict" == _exp_0 then local lua = LuaCode.Value(tree.source, "dict{") - local line_length = 0 - for i, entry in ipairs(tree) do - local entry_lua = self:compile(entry) - lua:append(entry_lua) - local entry_lua_str = tostring(entry_lua) - local last_line = match(entry_lua_str, "\n([^\n]*)$") - if last_line then - line_length = #last_line - else - line_length = line_length + #entry_lua_str + lua:concat_append((function() + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #tree do + local e = tree[_index_0] + _accum_0[_len_0] = self:compile(e) + _len_0 = _len_0 + 1 end - if i < #tree then - if line_length >= MAX_LINE then - lua:append(",\n ") - line_length = 0 - else - lua:append(", ") - line_length = line_length + 2 - end - end - end + return _accum_0 + end)(), ", ", ",\n ") lua:append("}") return lua elseif "DictEntry" == _exp_0 then @@ -848,6 +758,8 @@ do return LuaCode.Value(tree.source, tostring(tree[1])) elseif "Var" == _exp_0 then return LuaCode.Value(tree.source, string.as_lua_id(tree[1])) + elseif "FileChunks" == _exp_0 then + return error("Cannot convert FileChunks to a single block of lua, since each chunk's " .. "compilation depends on the earlier chunks") else return error("Unknown type: " .. tostring(tree.type)) end @@ -860,7 +772,23 @@ do can_use_colon = false end local _exp_0 = tree.type - if "Action" == _exp_0 then + if "FileChunks" == _exp_0 then + if inline then + return nil + end + local nomsu = NomsuCode(tree.source) + nomsu:concat_append((function() + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #tree do + local c = tree[_index_0] + _accum_0[_len_0] = self:tree_to_nomsu(c) + _len_0 = _len_0 + 1 + end + return _accum_0 + end)(), "\n" .. tostring(("~"):rep(80)) .. "\n") + return nomsu + elseif "Action" == _exp_0 then if inline then local nomsu = NomsuCode(tree.source) for i, bit in ipairs(tree) do @@ -1221,7 +1149,6 @@ do end end if arg and debug_getinfo(2).func ~= require then - colors = require('consolecolors') local parser = re.compile([[ args <- {| (flag ";")* {:inputs: {| ({file} ";")* |} :} {:nomsu_args: {| ("--;" ({[^;]*} ";")*)? |} :} ";"? |} !. flag <- {:interactive: ("-i" -> true) :} @@ -1293,12 +1220,6 @@ OPTIONS return info end if info.short_src or info.source or info.linedefine or info.currentline then - do - local arg_orders = nomsu.ARG_ORDERS[info.func] - if arg_orders then - info.name = next(arg_orders) - end - end do local map = nomsu.source_map[info.source] if map then @@ -1383,14 +1304,7 @@ OPTIONS local file = FILE_CACHE[filename]:sub(tonumber(start), tonumber(stop)) local err_line = get_line(file, calling_fn.currentline):sub(1, -2) local offending_statement = colored.bright(colored.red(err_line:match("^[ ]*(.*)"))) - do - local arg_orders = nomsu.ARG_ORDERS[calling_fn.func] - if arg_orders then - name = "action '" .. tostring(next(arg_orders)) .. "'" - else - name = "main chunk" - end - end + name = "action '" .. tostring(calling_fn.name) .. "'" line = colored.yellow(tostring(filename) .. ":" .. tostring(calling_fn.currentline) .. " in " .. tostring(name) .. "\n " .. tostring(offending_statement)) else local file diff --git a/nomsu.moon b/nomsu.moon index 1e6a89f..230b4fb 100755 --- a/nomsu.moon +++ b/nomsu.moon @@ -13,17 +13,18 @@ lpeg = require 'lpeg' re = require 're' lpeg.setmaxstack 10000 -{:P,:R,:V,:S,:Cg,:C,:Cp,:B,:Cmt,:Carg} = lpeg utils = require 'utils' -{:repr, :stringify, :min, :max, :equivalent, :set, :is_list, :sum} = utils -colors = setmetatable({}, {__index:->""}) -export colored +{:repr, :stringify, :equivalent} = utils +export colors, colored +colors = require 'consolecolors' colored = setmetatable({}, {__index:(_,color)-> ((msg)-> colors[color]..tostring(msg or '')..colors.reset)}) {:insert, :remove, :concat} = table unpack or= table.unpack {:match, :sub, :rep, :gsub, :format, :byte, :match, :find} = string debug_getinfo = debug.getinfo {:NomsuCode, :LuaCode, :Source} = require "code_obj" +AST = require "nomsu_tree" +parse = require("parser") STDIN, STDOUT, STDERR = "/dev/fd/0", "/dev/fd/1", "/dev/fd/2" string.as_lua_id = (str)-> @@ -40,15 +41,11 @@ string.as_lua_id = (str)-> return '_'..str table.map = (fn)=> [fn(v) for _,v in ipairs(@)] +table.fork = (t, values)-> setmetatable(values or {}, {__index:t}) -- TODO: -- consider non-linear codegen, rather than doing thunks for things like comprehensions --- type checking? --- Add compiler options for optimization level (compile-fast vs. run-fast, etc.) --- Do a pass on all actions to enforce parameters-are-nouns heuristic --- Maybe do some sort of lazy definitions of actions that defer until they're used in code -- Add a ((%x foo %y) where {x:"asdf", y:"fdsa"}) compile-time action for substitution --- Maybe support some kind of regex action definitions like "foo %first (and %next)*"? -- Re-implement nomsu-to-lua comment translation? export FILE_CACHE @@ -84,11 +81,11 @@ all_files = (path)-> line_counter = re.compile([[ lines <- {| line (%nl line)* |} line <- {} (!%nl .)* -]], nl:P("\r")^-1 * P("\n")) +]], nl:lpeg.P("\r")^-1 * lpeg.P("\n")) get_lines = re.compile([[ lines <- {| line (%nl line)* |} line <- {[^%nl]*} -]], nl:P("\r")^-1 * P("\n")) +]], nl:lpeg.P("\r")^-1 * lpeg.P("\n")) -- Mapping from line number -> character offset export LINE_STARTS -- LINE_STARTS is a mapping from strings to a table that maps line number to character positions @@ -104,6 +101,7 @@ LINE_STARTS = setmetatable {}, { self[k] = line_starts return line_starts } +export pos_to_line pos_to_line = (str, pos)-> line_starts = LINE_STARTS[str] -- Binary search for line number of position @@ -127,17 +125,17 @@ do if type(i) == 'number' then return sub(@, i, i) elseif type(i) == 'table' then return sub(@, i[1], i[2]) -AST = require "nomsu_tree" - +-- List and Dict classes to provide basic equality/tostring functionality for the tables +-- used in Nomsu. This way, they retain a notion of whether they were originally lists or dicts. _list_mt = - __eq:utils.equivalent + __eq:equivalent -- Could consider adding a __newindex to enforce list-ness, but would hurt performance __tostring: => "["..concat([repr(b) for b in *@], ", ").."]" list = (t)-> setmetatable(t, _list_mt) _dict_mt = - __eq:utils.equivalent + __eq:equivalent __tostring: => "{"..concat(["#{repr(k)}: #{repr(v)}" for k,v in pairs @], ", ").."}" dict = (t)-> setmetatable(t, _dict_mt) @@ -147,7 +145,6 @@ NomsuCompiler = setmetatable({}, {__index: (k)=> if _self = rawget(@, "self") th with NomsuCompiler ._ENV = NomsuCompiler .nomsu = NomsuCompiler - parse = require("parser") .parse = (...)=> parse(...) -- Mapping from source string (e.g. "@core/metaprogramming.nom[1:100]") to a mapping @@ -170,7 +167,6 @@ with NomsuCompiler .LuaCode = LuaCode .NomsuCode = NomsuCode .Source = Source - .ARG_ORDERS = setmetatable({}, {__mode:"k"}) .ALIASES = setmetatable({}, {__mode:"k"}) .LOADED = {} .AST = AST @@ -226,7 +222,7 @@ with NomsuCompiler lua\append bit_lua .COMPILE_ACTIONS = setmetatable { - compile_math_expr: (tree, ...)=> + ["# compile math expr #"]: (tree, ...)=> lua = LuaCode.Value(tree.source) for i,tok in ipairs tree if type(tok) == 'string' @@ -234,8 +230,7 @@ with NomsuCompiler else tok_lua = @compile(tok) unless tok_lua.is_value - @compile_error tok, - "Non-expression value inside math expression:\n%s" + @compile_error tok, "Non-expression value inside math expression:\n%s" if tok.type == "Action" tok_lua\parenthesize! lua\append tok_lua @@ -274,17 +269,17 @@ with NomsuCompiler }, { __index: (stub)=> if math_expression\match(stub) - return @compile_math_expr + return @["# compile math expr #"] } - .fork = => - setmetatable({COMPILE_ACTIONS:setmetatable({}, {__index:@COMPILE_ACTIONS})}, {__index:@}) - .run = (to_run, source=nil)=> tree = if AST.is_syntax_tree(to_run) then tree else @parse(to_run, source or to_run.source) if tree == nil -- Happens if pattern matches, but there are no captures, e.g. an empty string return nil if tree.type == "FileChunks" + -- Each chunk's compilation is affected by the code in the previous chunks + -- (typically), so each chunk needs to compile and run before the next one + -- compiles. ret = nil all_lua = {} for chunk in *tree @@ -310,32 +305,31 @@ with NomsuCompiler return @LOADED[filename] ret = nil for filename in all_files(filename) - if @LOADED[filename] - ret = @LOADED[filename] + if ret = @LOADED[filename] continue + -- Check for circular import for i,running in ipairs _running_files if running == filename loop = [_running_files[j] for j=i,#_running_files] insert loop, filename - error("Circular import, this loops forever: #{concat loop, " -> "}") + error("Circular import, this loops forever: #{concat loop, " -> "}...") insert _running_files, filename if match(filename, "%.lua$") file = assert(FILE_CACHE[filename], "Could not find file: #{filename}") ret = @run_lua file, Source(filename, 1, #file) elseif match(filename, "%.nom$") or match(filename, "^/dev/fd/[012]$") - if not @skip_precompiled -- Look for precompiled version + ran_lua = if not @skip_precompiled -- Look for precompiled version lua_filename = gsub(filename, "%.nom$", ".lua") - file = FILE_CACHE[lua_filename] - if file - ret = @run_lua file, Source(filename, 1, #file) - remove _running_files - continue - file = file or FILE_CACHE[filename] - if not file - error("File does not exist: #{filename}", 0) - ret = @run file, Source(filename,1,#file) + if file = FILE_CACHE[lua_filename] + ret = @run_lua file, Source(lua_filename, 1, #file) + true + unless ran_lua + file = file or FILE_CACHE[filename] + if not file + error("File does not exist: #{filename}", 0) + ret = @run file, Source(filename,1,#file) else error("Invalid filetype for #{filename}", 0) @LOADED[filename] = ret or true @@ -348,11 +342,9 @@ with NomsuCompiler lua_string = tostring(lua) run_lua_fn, err = load(lua_string, nil and tostring(source or lua.source), "t", self) if not run_lua_fn - n = 1 - fn = -> - n = n + 1 - ("\n%-3d|")\format(n) - line_numbered_lua = "1 |"..lua_string\gsub("\n", fn) + line_numbered_lua = concat( + [format("%3d|%s",i,line) for i, line in ipairs get_lines\match(lua_string)], + "\n") error("Failed to compile generated code:\n#{colored.bright colored.blue colored.onblack line_numbered_lua}\n\n#{err}", 0) source_key = tostring(source or lua.source) unless @source_map[source_key] @@ -381,14 +373,11 @@ with NomsuCompiler return run_lua_fn! .compile = (tree)=> - assert(LuaCode) switch tree.type when "Action" stub = tree.stub if compile_action = @COMPILE_ACTIONS[stub] args = [arg for arg in *tree when type(arg) != "string"] - if arg_orders = @ARG_ORDERS[stub] - args = [args[p] for p in *arg_orders] -- Force Lua to avoid tail call optimization for debugging purposes -- TODO: use tail call? ret = compile_action(@, tree, unpack(args)) @@ -397,28 +386,7 @@ with NomsuCompiler "Compile-time action:\n%s\nfailed to produce any Lua" return ret - action = @['A'..string.as_lua_id(stub)] - - lua = LuaCode.Value(tree.source) - if not action and math_expression\match(stub) - -- This is a bit of a hack, but this code handles arbitrarily complex - -- math expressions like 2*x + 3^2 without having to define a single - -- action for every possibility. - for i,tok in ipairs tree - if type(tok) == 'string' - lua\append tok - else - tok_lua = @compile(tok) - unless tok_lua.is_value - @compile_error tok, - "Non-expression value inside math expression:\n%s" - if tok.type == "Action" - tok_lua\parenthesize! - lua\append tok_lua - if i < #tree - lua\append " " - return lua - + lua = LuaCode.Value(tree.source, "A",string.as_lua_id(stub),"(") args = {} for i, tok in ipairs tree if type(tok) == "string" then continue @@ -428,15 +396,7 @@ with NomsuCompiler "Cannot use:\n%s\nas an argument to %s, since it's not an expression, it produces: %s", stub, repr arg_lua insert args, arg_lua - - if action - if arg_orders = @ARG_ORDERS[stub] - args = [args[p] for p in *arg_orders] - - lua\append "A",string.as_lua_id(stub),"(" - for i, arg in ipairs args - lua\append arg - if i < #args then lua\append ", " + lua\concat_append args, ", " lua\append ")" return lua @@ -450,11 +410,7 @@ with NomsuCompiler when "Block" lua = LuaCode(tree.source) - for i,line in ipairs tree - line_lua = @compile(line) - if i > 1 - lua\append "\n" - lua\append line_lua\as_statements! + lua\concat_append([@compile(line)\as_statements! for line in *tree], "\n") return lua when "Text" @@ -489,49 +445,20 @@ with NomsuCompiler when "List" lua = LuaCode.Value tree.source, "list{" - line_length = 0 + items = {} for i, item in ipairs tree item_lua = @compile(item) unless item_lua.is_value @compile_error item, "Cannot use:\n%s\nas a list item, since it's not an expression." - lua\append item_lua - item_string = tostring(item_lua) - last_line = match(item_string, "[^\n]*$") - if match(item_string, "\n") - line_length = #last_line - else - line_length += #last_line - if i < #tree - if line_length >= MAX_LINE - lua\append ",\n " - line_length = 0 - else - lua\append ", " - line_length += 2 + items[i] = item_lua + lua\concat_append(items, ", ", ",\n ") lua\append "}" return lua when "Dict" lua = LuaCode.Value tree.source, "dict{" - line_length = 0 - for i, entry in ipairs tree - entry_lua = @compile(entry) - lua\append entry_lua - entry_lua_str = tostring(entry_lua) - -- TODO: maybe make this more accurate? It's only a heuristic, so eh... - last_line = match(entry_lua_str, "\n([^\n]*)$") - if last_line - line_length = #last_line - else - line_length += #entry_lua_str - if i < #tree - if line_length >= MAX_LINE - lua\append ",\n " - line_length = 0 - else - lua\append ", " - line_length += 2 + lua\concat_append([@compile(e) for e in *tree], ", ", ",\n ") lua\append "}" return lua @@ -545,6 +472,7 @@ with NomsuCompiler unless value_lua.is_value @compile_error tree[2], "Cannot use:\n%s\nas a dict value, since it's not an expression." + -- TODO: support arbitrary words here, like operators and unicode key_str = match(tostring(key_lua), [=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=]) return if key_str LuaCode tree.source, key_str,"=",value_lua @@ -588,12 +516,22 @@ with NomsuCompiler when "Var" LuaCode.Value(tree.source, string.as_lua_id(tree[1])) - + + when "FileChunks" + error("Cannot convert FileChunks to a single block of lua, since each chunk's ".. + "compilation depends on the earlier chunks") + else error("Unknown type: #{tree.type}") .tree_to_nomsu = (tree, inline=false, can_use_colon=false)=> switch tree.type + when "FileChunks" + return nil if inline + nomsu = NomsuCode(tree.source) + nomsu\concat_append [@tree_to_nomsu(c) for c in *tree], "\n#{("~")\rep(80)}\n" + return nomsu + when "Action" if inline nomsu = NomsuCode(tree.source) @@ -858,18 +796,9 @@ with NomsuCompiler error("Unknown type: #{tree.type}") - - - - -- Command line interface: - - - -- Only run this code if this file was run directly with command line arguments, and not require()'d: if arg and debug_getinfo(2).func != require - export colors - colors = require 'consolecolors' parser = re.compile([[ args <- {| (flag ";")* {:inputs: {| ({file} ";")* |} :} {:nomsu_args: {| ("--;" ({[^;]*} ";")*)? |} :} ";"? |} !. flag <- @@ -926,8 +855,7 @@ OPTIONS else debug_getinfo(thread,f,what) if not info or not info.func then return info if info.short_src or info.source or info.linedefine or info.currentline - if arg_orders = nomsu.ARG_ORDERS[info.func] - info.name = next(arg_orders) + -- TODO: get name properly if map = nomsu.source_map[info.source] if info.currentline info.currentline = assert(map[info.currentline]) @@ -981,10 +909,8 @@ OPTIONS file = FILE_CACHE[filename]\sub(tonumber(start),tonumber(stop)) err_line = get_line(file, calling_fn.currentline)\sub(1,-2) offending_statement = colored.bright(colored.red(err_line\match("^[ ]*(.*)"))) - if arg_orders = nomsu.ARG_ORDERS[calling_fn.func] - name = "action '#{next(arg_orders)}'" - else - name = "main chunk" + -- TODO: get name properly + name = "action '#{calling_fn.name}'" line = colored.yellow("#{filename}:#{calling_fn.currentline} in #{name}\n #{offending_statement}") else ok, file = pcall ->FILE_CACHE[calling_fn.short_src] diff --git a/parser.lua b/parser.lua index e995c3c..863e17e 100644 --- a/parser.lua +++ b/parser.lua @@ -127,7 +127,7 @@ do ident <- [a-zA-Z_][a-zA-Z0-9_]* comment <- "--" [^%nl]* ]]) - local nomsu_peg = peg_tidier:match(FILE_CACHE["nomsu.peg"]) + local nomsu_peg = peg_tidier:match(io.open("nomsu.peg"):read('*a')) NOMSU_PATTERN = re.compile(nomsu_peg, NOMSU_DEFS) end local parse diff --git a/parser.moon b/parser.moon index cac126c..ad9d0d0 100644 --- a/parser.moon +++ b/parser.moon @@ -67,7 +67,6 @@ NOMSU_DEFS = with {} err_line = colored.white(err_line\sub(1, i))..colored.bright(colored.red(err_line\sub(i+1,i+1)))..colored.dim(err_line\sub(i+2,-1)) err_msg ..= "\n#{err_line}\n#{colored.red pointer}" if #next_line > 0 then err_msg ..= "\n"..colored.dim(next_line) - --error(err_msg) seen_errors[start_pos] = err_msg return true @@ -98,7 +97,7 @@ NOMSU_PATTERN = do ident <- [a-zA-Z_][a-zA-Z0-9_]* comment <- "--" [^%nl]* ]] - nomsu_peg = peg_tidier\match(FILE_CACHE["nomsu.peg"]) + nomsu_peg = peg_tidier\match(io.open("nomsu.peg")\read('*a')) re.compile(nomsu_peg, NOMSU_DEFS) parse = (nomsu_code, source=nil)->