diff --git a/code_obj.lua b/code_obj.lua index 4fd01d6..376ee0c 100644 --- a/code_obj.lua +++ b/code_obj.lua @@ -110,9 +110,23 @@ do return self:text() end, as_lua = function(self) - return tostring(self.__class.__name) .. "(" .. tostring(concat({ - tostring(self.source):as_lua(), - unpack((function() + if self.source then + return tostring(self.__class.__name) .. ":from(" .. tostring(concat({ + tostring(self.source):as_lua(), + unpack((function() + local _accum_0 = { } + local _len_0 = 1 + local _list_0 = self.bits + for _index_0 = 1, #_list_0 do + local b = _list_0[_index_0] + _accum_0[_len_0] = b:as_lua() + _len_0 = _len_0 + 1 + end + return _accum_0 + end)()) + }, ", ")) .. ")" + else + return tostring(self.__class.__name) .. "(" .. tostring(concat((function() local _accum_0 = { } local _len_0 = 1 local _list_0 = self.bits @@ -122,8 +136,8 @@ do _len_0 = _len_0 + 1 end return _accum_0 - end)()) - }, ", ")) .. ")" + end)(), ", ")) .. ")" + end end, __len = function(self) return #self:text() @@ -246,12 +260,8 @@ do } _base_0.__index = _base_0 _class_0 = setmetatable({ - __init = function(self, source, ...) - self.source = source + __init = function(self, ...) self.bits = { } - if type(self.source) == 'string' then - self.source = Source:from_string(self.source) - end return self:append(...) end, __base = _base_0, @@ -266,6 +276,14 @@ do }) _base_0.__class = _class_0 local self = _class_0 + self.from = function(self, source, ...) + local inst = self(...) + if type(source) == 'string' then + source = Source:from_string(source) + end + inst.source = source + return inst + end self.is_instance = function(self, x) return type(x) == 'table' and x.__class == self end @@ -370,27 +388,8 @@ do end return to_declare end, - as_statements = function(self, prefix, suffix) - if prefix == nil then - prefix = "" - end - if suffix == nil then - suffix = ";" - end - if self:text():matches(";$") or self:text() == "" then - return self - end - local statements = LuaCode(self.source) - if prefix ~= "" then - statements:append(prefix) - end - statements:append(self) - if suffix ~= "" then - statements:append(suffix) - end - return statements - end, make_offset_table = function(self) + assert(self.source, "This code doesn't have a source") local lua_to_nomsu, nomsu_to_lua = { }, { } local walk walk = function(lua, pos) diff --git a/code_obj.moon b/code_obj.moon index fcf2217..e7fb5f3 100644 --- a/code_obj.moon +++ b/code_obj.moon @@ -43,13 +43,17 @@ class Source class Code is_code: true - new: (@source, ...)=> + new: (...)=> @bits = {} - if type(@source) == 'string' - @source = Source\from_string(@source) - --assert(@source and Source\is_instance(@source), "Source has the wrong type") @append(...) + @from: (source, ...)=> + inst = self(...) + if type(source) == 'string' + source = Source\from_string(source) + inst.source = source + return inst + @is_instance: (x)=> type(x) == 'table' and x.__class == @ text: => @@ -71,7 +75,10 @@ class Code __tostring: => @text! as_lua: => - "#{@__class.__name}(#{concat {tostring(@source)\as_lua!, unpack([b\as_lua! for b in *@bits])}, ", "})" + if @source + "#{@__class.__name}:from(#{concat {tostring(@source)\as_lua!, unpack([b\as_lua! for b in *@bits])}, ", "})" + else + "#{@__class.__name}(#{concat [b\as_lua! for b in *@bits], ", "})" __len: => #@text! @@ -214,18 +221,8 @@ class LuaCode extends Code @prepend "local #{concat to_declare, ", "};\n" return to_declare - as_statements: (prefix="", suffix=";")=> - if @text!\matches(";$") or @text! == "" - return self - statements = LuaCode(@source) - if prefix != "" - statements\append prefix - statements\append self - if suffix != "" - statements\append suffix - return statements - make_offset_table: => + assert @source, "This code doesn't have a source" -- Return a mapping from output (lua) character number to input (nomsu) character number lua_to_nomsu, nomsu_to_lua = {}, {} walk = (lua, pos)-> diff --git a/compatibility/compatibility.nom b/compatibility/compatibility.nom index 184ca56..4e22dbc 100644 --- a/compatibility/compatibility.nom +++ b/compatibility/compatibility.nom @@ -79,13 +79,13 @@ externally [..] if ((%ver as list) > (%end_version as list)): stop %ver if %ACTION_UPGRADES.%ver: %tree = (..) - %tree with % -> (..) + %tree with % ->: if ((% is "Action" syntax tree) and %ACTION_UPGRADES.%ver.(%.stub)): %with_upgraded_args = (..) %k = (%v upgraded from %start_version to %end_version) for %k = %v in % set %with_upgraded_args 's metatable to (% 's metatable) return (..) - call %ACTION_UPGRADES.%ver.(%.stub) with [%with_upgraded_args, %end_version] + %ACTION_UPGRADES.%ver.(%.stub) %with_upgraded_args %end_version if %UPGRADES.%ver: %with_upgraded_args = (..) diff --git a/containers.lua b/containers.lua index 17d37a2..f33c2ff 100644 --- a/containers.lua +++ b/containers.lua @@ -210,19 +210,21 @@ local _list_mt = { end, from_1_to = function(self, start, stop) local n = #self - if n < 0 then + if start < 0 then start = (n + 1 - start) end - if n < 0 then + if stop < 0 then stop = (n + 1 - stop) end - local _accum_0 = { } - local _len_0 = 1 - for i = start, stop do - _accum_0[_len_0] = self[i] - _len_0 = _len_0 + 1 - end - return _accum_0 + return List((function() + local _accum_0 = { } + local _len_0 = 1 + for i = start, stop do + _accum_0[_len_0] = self[i] + _len_0 = _len_0 + 1 + end + return _accum_0 + end)()) end }, __newindex = function(self, k, v) diff --git a/containers.moon b/containers.moon index 4cafdaf..990a741 100644 --- a/containers.moon +++ b/containers.moon @@ -85,9 +85,9 @@ _list_mt = return nil from_1_to: (start, stop)=> n = #@ - start = (n+1-start) if n < 0 - stop = (n+1-stop) if n < 0 - return [@[i] for i=start,stop] + start = (n+1-start) if start < 0 + stop = (n+1-stop) if stop < 0 + return List[@[i] for i=start,stop] -- TODO: remove this safety check to get better performance? __newindex: (k,v)=> assert type(k) == 'number', "List indices must be numbers" diff --git a/core/control_flow.nom b/core/control_flow.nom index cba0777..ff1a7c1 100644 --- a/core/control_flow.nom +++ b/core/control_flow.nom @@ -4,7 +4,6 @@ like "if" statements and loops. use "core/metaprogramming.nom" -use "core/text.nom" use "core/operators.nom" use "core/errors.nom" diff --git a/core/io.nom b/core/io.nom index edeeab4..2df0999 100644 --- a/core/io.nom +++ b/core/io.nom @@ -9,17 +9,17 @@ use "core/metaprogramming.nom" (say %message) compiles to: lua> "\ ..if \%message.type == "Text" then - return LuaCode(tree.source, "print(", \(%message as lua expr), ");"); + return LuaCode("print(", \(%message as lua expr), ");"); else - return LuaCode(tree.source, "print(tostring(", \(%message as lua expr), "));"); + return LuaCode("print(tostring(", \(%message as lua expr), "));"); end" (ask %prompt) compiles to: lua> "\ ..if \%prompt.type == "Text" then - return LuaCode(tree.source, "(io.write(", \(%prompt as lua expr), ") and io.read())"); + return LuaCode("(io.write(", \(%prompt as lua expr), ") and io.read())"); else - return LuaCode(tree.source, "(io.write(tostring(", \(..) + return LuaCode("(io.write(tostring(", \(..) %prompt as lua expr .., ")) and io.read())"); end" diff --git a/core/math.nom b/core/math.nom index 1c7306e..8951721 100644 --- a/core/math.nom +++ b/core/math.nom @@ -85,7 +85,7 @@ externally [all of %items, all %items] all mean: return (yes) [all of %items, all %items] all compile to: unless (%items.type is "List"): - return %tree + return \(all of %items) if ((size of %items) == 0): return (Lua "true") %lua = (Lua "(") %lua::add ((% as lua expr) for % in %items) joined with " and " @@ -99,7 +99,7 @@ externally [any of %items, any %items] all mean: return (no) [any of %items, any %items] all compile to: unless (%items.type is "List"): - return %tree + return \(any of %items) if ((size of %items) == 0): return (Lua "false") %lua = (Lua "(") %lua::add ((% as lua expr) for % in %items) joined with " or " @@ -114,7 +114,7 @@ externally [sum of %items, sum %items] all mean: return %total [sum of %items, sum %items] all compile to: unless (%items.type is "List"): - return %tree + return \(sum of %items) if ((size of %items) == 0): return (Lua "0") %lua = (Lua "(") %lua::add ((% as lua expr) for % in %items) joined with " + " @@ -127,7 +127,7 @@ externally [product of %items, product %items] all mean: return %prod [product of %items, product %items] all compile to: unless (%items.type is "List"): - return %tree + return \(product of %items) if ((size of %items) == 0): return (Lua "1") %lua = (Lua "(") %lua::add ((% as lua expr) for % in %items) joined with " * " diff --git a/core/metaprogramming.nom b/core/metaprogramming.nom index 1f787c2..fc81e15 100644 --- a/core/metaprogramming.nom +++ b/core/metaprogramming.nom @@ -17,13 +17,13 @@ lua> "\ end end end - compile.action["define mangler"] = function(compile, tree) - return LuaCode(tree.source, "local mangle = mangler()") + compile.action["define mangler"] = function(compile) + return LuaCode("local mangle = mangler()") end" lua> "\ - ..compile.action["1 ->"] = function(compile, tree, \%args, \%body) - local lua = LuaCode(tree.source, "(function(") + ..compile.action["1 ->"] = function(compile, \%args, \%body) + local lua = LuaCode("(function(") if SyntaxTree:is_instance(\%args) and \%args.type == "Action" then \%args = \%args:get_args() end local lua_args = table.map(\%args, function(a) return SyntaxTree:is_instance(a) and compile(a):text() or a end) lua:concat_append(lua_args, ", ") @@ -36,11 +36,10 @@ lua> "\ end" lua> "\ - ..compile.action["what 1 compiles to"] = function(compile, tree, \%action) - local lua = LuaCode(tree.source, "compile.action[", \%action.stub:as_lua(), "](") + ..compile.action["what 1 compiles to"] = function(compile, \%action) + local lua = LuaCode("compile.action[", \%action.stub:as_lua(), "](") local lua_args = table.map(\%action:get_args(), function(a) return compile(a) end) table.insert(lua_args, 1, "compile") - table.insert(lua_args, 2, "tree") lua:concat_append(lua_args, ", ") lua:append(")") return lua @@ -66,12 +65,12 @@ test: asdf assume (%tmp is (nil)) or barf "compile to is leaking variables" lua> "\ - ..compile.action["1 compiles to"] = function(compile, tree, \%action, \%body) - local \%args = List{\(\%compile), \(\%tree), unpack(\%action:get_args())} + ..compile.action["1 compiles to"] = function(compile, \%action, \%body) + local \%args = List{\(\%compile), unpack(\%action:get_args())} if \%body.type == "Text" then \%body = SyntaxTree{source=\%body.source, type="Action", "Lua", \%body} end - return LuaCode(tree.source, "compile.action[", \%action.stub:as_lua(), + return LuaCode("compile.action[", \%action.stub:as_lua(), "] = ", \(what (%args -> %body) compiles to)) end" @@ -82,11 +81,11 @@ lua> "\ ..if \%actions.type ~= "List" then compile_error(\%actions, "This should be a list of actions.") end - local lua = LuaCode(tree.source, \(what (%actions.1 compiles to %body) compiles to)) - local \%args = List{\(\%compile), \(\%tree), unpack(\%actions[1]:get_args())} + local lua = \(what (%actions.1 compiles to %body) compiles to) + local \%args = List{\(\%compile), unpack(\%actions[1]:get_args())} for i=2,#\%actions do local alias = \%actions[i] - local \%alias_args = List{\(\%compile), \(\%tree), unpack(alias:get_args())} + local \%alias_args = List{\(\%compile), unpack(alias:get_args())} lua:append("\\ncompile.action[", alias.stub:as_lua(), "] = ") if \%alias_args == \%args then lua:append("compile.action[", \%actions[1].stub:as_lua(), "]") @@ -100,7 +99,7 @@ lua> "\ (call %fn with %args) compiles to: lua> "\ - ..local lua = LuaCode(tree.source, compile(\%fn), "(") + ..local lua = LuaCode(compile(\%fn), "(") if \%args.type == 'List' then lua:concat_append(table.map(\%args, function(a) return compile(a) end), ", ") else @@ -126,14 +125,14 @@ test: lua> "\ ..local fn_name = \%action.stub:as_lua_id() local \%args = \%action:get_args() - local lua = LuaCode(tree.source, fn_name, " = ", \(what (%args -> %body) compiles to)) + local lua = LuaCode(fn_name, " = ", \(what (%args -> %body) compiles to)) lua:add_free_vars({fn_name}) return lua" (%actions all mean %body) compiles to: lua> "\ ..local fn_name = \%actions[1].stub:as_lua_id() local \%args = List(\%actions[1]:get_args()) - local lua = LuaCode(tree.source, \(what (%actions.1 means %body) compiles to)) + local lua = \(what (%actions.1 means %body) compiles to) for i=2,#\%actions do local alias = \%actions[i] local alias_name = alias.stub:as_lua_id() @@ -222,7 +221,7 @@ test: return t:as_lua() end end - local \%new_body = LuaCode(\%body.source, + local \%new_body = LuaCode:from(\%body.source, "local mangle = mangler()", "\\nreturn ", make_tree(\%body)) local ret = \(what (%actions all compile to %new_body) compiles to) diff --git a/core/operators.nom b/core/operators.nom index 38fc9a8..19a61d7 100644 --- a/core/operators.nom +++ b/core/operators.nom @@ -31,7 +31,7 @@ test: lua> "\ ..local \%var_lua = \(%var as lua expr) local \%value_lua = \(%value as lua expr) - local lua = LuaCode(tree.source, \%var_lua, ' = ', \%value_lua, ';') + local lua = LuaCode(\%var_lua, ' = ', \%value_lua, ';') if \%var.type == 'Var' then lua:add_free_vars({compile(\%var):text()}) end @@ -49,7 +49,7 @@ test: assume (%assignments.type is "Dict") or barf "\ ..Expected a Dict for the assignments part of '<- %' statement, not \%assignments" lua> "\ - ..local lhs, rhs = LuaCode(tree.source), LuaCode(tree.source) + ..local lhs, rhs = LuaCode(), LuaCode() for i, item in ipairs(\%assignments) do local \%target, \%value = item[1], item[2] \%value = \%value:map(function(t) @@ -69,7 +69,7 @@ test: lhs:append(target_lua) rhs:append(value_lua) end - return LuaCode(tree.source, lhs, " = ", rhs, ";")" + return LuaCode(lhs, " = ", rhs, ";")" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -111,7 +111,7 @@ test: (with %assignments %body) compiles to: %lua = (%body as lua) lua> "\ - ..local lhs, rhs = LuaCode(tree.source), LuaCode(tree.source) + ..local lhs, rhs = LuaCode(), LuaCode() local vars = {} for i, item in ipairs(\%assignments) do local \%target, \%value = item[1], item[2] @@ -233,8 +233,7 @@ test: test: assume ((size of [1, 2, 3]) == 3) -[size of %list, size of %list, size of %list, size of %list] all compile to (..) - "(#\(%list as lua expr))" +(size of %list) compiles to "(#\(%list as lua expr))" (%list is empty) compiles to "(#\(%list as lua expr) == 0)" diff --git a/core/text.nom b/core/text.nom index 2d72f01..d4cd227 100644 --- a/core/text.nom +++ b/core/text.nom @@ -4,6 +4,8 @@ color codes. use "core/metaprogramming.nom" +use "core/operators.nom" +use "core/control_flow.nom" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -47,17 +49,9 @@ test: assume "\n" == (newline) # Text literals -lua> "\ - ..do - local escapes = { - nl="\\\\n", newline="\\\\n", tab="\\\\t", bell="\\\\a", cr="\\\\r", - ["carriage return"]="\\\\r", backspace="\\\\b", ["form feed"]="\\\\f", - formfeed="\\\\f", ["vertical tab"]="\\\\v", - }; - for name, e in pairs(escapes) do - local lua = "'"..e.."'" - compile.action[name] = function(compile, tree) - return LuaCode(tree.source, lua) - end - end - end" +%escapes = {..} + nl:"\n", newline:"\n", tab:"\t", bell:"\a", cr:"\r", "carriage return":"\r", + backspace:"\b", "form feed":"\f", formfeed:"\f", "vertical tab":"\v" +for %name = %str in %escapes: + with {%lua: Lua (quote %str)}: + %compile.action.%name = ([]-> %lua) diff --git a/lib/consolecolor.nom b/lib/consolecolor.nom index d8b078f..1016c0b 100644 --- a/lib/consolecolor.nom +++ b/lib/consolecolor.nom @@ -17,7 +17,7 @@ for %name = %colornum in %colors: #(=lua "COMPILE_ACTIONS").%name = (..) [%nomsu, %tree] -> (Lua "'\\027[\(%colornum)m'") %compile.action.%name = (..) - [%nomsu, %tree, %text] ->: + [%nomsu, %text] ->: if %text: return (Lua "('\\027[\(%colornum)m'..\(%text as lua expr)..'\\027[0m')") ..else: return (Lua "'\\027[\(%colornum)m'") diff --git a/lib/object.nom b/lib/object.nom index 2af5412..bb2de3a 100644 --- a/lib/object.nom +++ b/lib/object.nom @@ -56,7 +56,7 @@ test: ..local fn_name = \%actions[1].stub:as_lua_id() local \%args = List(\%actions[1]:get_args()) table.insert(\%args, 1, \(\%me)) - local lua = LuaCode(tree.source, "class.", fn_name, " = ", \(..) + local lua = LuaCode("class.", fn_name, " = ", \(..) what (%args -> %body) compiles to ..) for i=2,#\%actions do diff --git a/lib/things.nom b/lib/things.nom index 4f15e68..50c7aaa 100644 --- a/lib/things.nom +++ b/lib/things.nom @@ -60,7 +60,7 @@ test: lua> "\ ..local fn_name = \%actions[1].stub:as_lua_id() local \%args = List{\(\%its), unpack(\%actions[1]:get_args())} - local lua = LuaCode(tree.source, "class.", fn_name, " = ", \(..) + local lua = LuaCode("class.", fn_name, " = ", \(..) what (%args -> %body) compiles to ..) for i=2,#\%actions do diff --git a/nomsu.lua b/nomsu.lua index 82c0d02..9010227 100644 --- a/nomsu.lua +++ b/nomsu.lua @@ -181,7 +181,7 @@ run = function() if args.check_syntax then local code = Files.read(filename) local source = Source(filename, 1, #code) - nomsu_environment._1_parsed(NomsuCode(source, code)) + nomsu_environment._1_parsed(NomsuCode:from(source, code)) print("Parse succeeded: " .. tostring(filename)) elseif args.compile then local output @@ -192,7 +192,7 @@ run = function() end local code = Files.read(filename) local source = Source(filename, 1, #code) - code = NomsuCode(source, code) + code = NomsuCode:from(source, code) local tree = nomsu_environment._1_parsed(code) if not (tree.type == 'FileChunks') then tree = { @@ -214,7 +214,7 @@ run = function() elseif args.verbose then local code = Files.read(filename) local source = Source(filename, 1, #code) - code = NomsuCode(source, code) + code = NomsuCode:from(source, code) local tree = nomsu_environment._1_parsed(code) if not (tree.type == 'FileChunks') then tree = { diff --git a/nomsu.moon b/nomsu.moon index 8825ba5..35726e7 100755 --- a/nomsu.moon +++ b/nomsu.moon @@ -117,7 +117,7 @@ run = -> -- Check syntax code = Files.read(filename) source = Source(filename, 1, #code) - nomsu_environment._1_parsed(NomsuCode(source, code)) + nomsu_environment._1_parsed(NomsuCode\from(source, code)) print("Parse succeeded: #{filename}") elseif args.compile -- Compile .nom files into .lua @@ -125,7 +125,7 @@ run = -> else io.open(filename\gsub("%.nom$", ".lua"), "w") code = Files.read(filename) source = Source(filename, 1, #code) - code = NomsuCode(source, code) + code = NomsuCode\from(source, code) tree = nomsu_environment._1_parsed(code) tree = {tree} unless tree.type == 'FileChunks' for chunk in *tree @@ -139,7 +139,7 @@ run = -> elseif args.verbose code = Files.read(filename) source = Source(filename, 1, #code) - code = NomsuCode(source, code) + code = NomsuCode\from(source, code) tree = nomsu_environment._1_parsed(code) tree = {tree} unless tree.type == 'FileChunks' for chunk in *tree diff --git a/nomsu_compiler.lua b/nomsu_compiler.lua index d293d99..f262dd8 100644 --- a/nomsu_compiler.lua +++ b/nomsu_compiler.lua @@ -63,30 +63,11 @@ do tree_to_nomsu, tree_to_inline_nomsu = _obj_0.tree_to_nomsu, _obj_0.tree_to_inline_nomsu end local math_expression = re.compile([[ (([*/^+-] / [0-9]+) " ")* [*/^+-] !. ]]) -local compile_math_expression -compile_math_expression = function(compile, tree, ...) - local lua = LuaCode(tree.source) - for i, tok in ipairs(tree) do - if type(tok) == 'string' then - lua:append(tok) - else - local tok_lua = compile(tok) - 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 MAX_LINE = 80 local compile = setmetatable({ action = Importer({ - [""] = function(compile, tree, fn, ...) - local lua = LuaCode(tree.source) + [""] = function(compile, fn, ...) + local lua = LuaCode() local fn_lua = compile(fn) lua:append(fn_lua) if not (fn_lua:text():match("^%(.*%)$") or fn_lua:text():match("^[_a-zA-Z][_a-zA-Z0-9.]*$")) then @@ -102,9 +83,9 @@ local compile = setmetatable({ lua:append(")") return lua end, - ["Lua"] = function(compile, tree, code) + ["Lua"] = function(compile, code) if code.type ~= "Text" then - return LuaCode(code.source, "LuaCode(", tostring(code.source):as_lua(), ", ", compile(code), ")") + return LuaCode("LuaCode:from(", tostring(code.source):as_lua(), ", ", compile(code), ")") end local add_bit_lua add_bit_lua = function(lua, bit_lua) @@ -114,7 +95,7 @@ local compile = setmetatable({ end local operate_on_text operate_on_text = function(text) - local lua = LuaCode(text.source, "LuaCode(", tostring(text.source):as_lua()) + local lua = LuaCode:from(text.source, "LuaCode:from(", tostring(text.source):as_lua()) for _index_0 = 1, #text do local bit = text[_index_0] if type(bit) == "string" then @@ -130,13 +111,13 @@ local compile = setmetatable({ end return operate_on_text(code) end, - ["lua >"] = function(compile, tree, code) + ["lua >"] = function(compile, code) if code.type ~= "Text" then - return tree + return code end local operate_on_text operate_on_text = function(text) - local lua = LuaCode(text.source) + local lua = LuaCode:from(text.source) for _index_0 = 1, #text do local bit = text[_index_0] if type(bit) == "string" then @@ -151,18 +132,18 @@ local compile = setmetatable({ end return operate_on_text(code) end, - ["= lua"] = function(compile, tree, code) - return compile.action["lua >"](compile, tree, code) + ["= lua"] = function(compile, code) + return compile.action["lua >"](compile, code) end, - ["use"] = function(compile, tree, path) - return LuaCode(tree.source, "run_file_1_in(" .. tostring(compile(path)) .. ", _ENV)") + ["use"] = function(compile, path) + return LuaCode("run_file_1_in(" .. tostring(compile(path)) .. ", _ENV)") end, - ["tests"] = function(compile, tree) - return LuaCode(tree.source, "TESTS") + ["tests"] = function(compile) + return LuaCode("TESTS") end, - ["test"] = function(compile, tree, body) + ["test"] = function(compile, body) if not (body.type == 'Block') then - compile_error(tree, "This should be a Block") + compile_error(body, "This should be a Block") end local test_nomsu = body:get_source_code():match(":[ ]*(.*)") do @@ -171,16 +152,16 @@ local compile = setmetatable({ test_nomsu = test_nomsu:gsub("\n" .. indent, "\n") end end - return LuaCode(tree.source, "TESTS[" .. tostring(tostring(tree.source):as_lua()) .. "] = ", test_nomsu:as_lua()) + return LuaCode("TESTS[" .. tostring(tostring(body.source):as_lua()) .. "] = ", test_nomsu:as_lua()) end, - ["is jit"] = function(compile, tree, code) - return LuaCode(tree.source, "jit") + ["is jit"] = function(compile, code) + return LuaCode("jit") end, - ["Lua version"] = function(compile, tree, code) - return LuaCode(tree.source, "_VERSION") + ["Lua version"] = function(compile, code) + return LuaCode("_VERSION") end, - ["nomsu environment"] = function(compile, tree) - return LuaCode(tree.source, "_ENV") + ["nomsu environment"] = function(compile) + return LuaCode("_ENV") end }) }, { @@ -191,25 +172,27 @@ local compile = setmetatable({ if force_value == nil then force_value = false end - if tree.version then - do - local get_version = compile.action[("Nomsu version"):as_lua_id()] - if get_version then - do - local upgrade = compile.action[("1 upgraded from 2 to"):as_lua_id()] - if upgrade then - tree = upgrade(tree, tree.version, get_version()) - end - end - end - end - end local _exp_0 = tree.type if "Action" == _exp_0 then local stub = tree.stub local compile_action = compile.action[stub] if not compile_action and math_expression:match(stub) then - compile_action = compile_math_expression + local lua = LuaCode:from(tree.source) + for i, tok in ipairs(tree) do + if type(tok) == 'string' then + lua:append(tok) + else + local tok_lua = compile(tok) + 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 if compile_action and not tree.target then local args @@ -225,20 +208,21 @@ local compile = setmetatable({ end args = _accum_0 end - local ret = compile_action(compile, tree, unpack(args)) + local ret = compile_action(compile, unpack(args)) if ret == nil then local info = debug.getinfo(compile_action, "S") local filename = Source:from_string(info.source).filename compile_error(tree, "The compile-time action here (" .. tostring(stub) .. ") failed to return any value.", "Look at the implementation of (" .. tostring(stub) .. ") in " .. tostring(filename) .. ":" .. tostring(info.linedefined) .. " and make sure it's returning something.") end if not (SyntaxTree:is_instance(ret)) then + ret.source = ret.source or tree.source return ret end if ret ~= tree then return compile(ret) end end - local lua = LuaCode(tree.source) + local lua = LuaCode:from(tree.source) if tree.target then local target_lua = compile(tree.target) local target_text = target_lua:text() @@ -269,7 +253,7 @@ local compile = setmetatable({ lua:append(")") return lua elseif "EscapedNomsu" == _exp_0 then - local lua = LuaCode(tree.source, "SyntaxTree{") + local lua = LuaCode:from(tree.source, "SyntaxTree{") local needs_comma, i = false, 1 local as_lua as_lua = function(x) @@ -304,7 +288,7 @@ local compile = setmetatable({ return lua elseif "Block" == _exp_0 then if not force_value then - local lua = LuaCode(tree.source) + local lua = LuaCode:from(tree.source) lua:concat_append((function() local _accum_0 = { } local _len_0 = 1 @@ -317,7 +301,7 @@ local compile = setmetatable({ end)(), "\n") return lua else - local lua = LuaCode(tree.source) + local lua = LuaCode:from(tree.source) lua:append("((function()") for i, line in ipairs(tree) do lua:append("\n ", compile(line)) @@ -326,7 +310,7 @@ local compile = setmetatable({ return lua end elseif "Text" == _exp_0 then - local lua = LuaCode(tree.source) + local lua = LuaCode:from(tree.source) local string_buffer = "" for i, bit in ipairs(tree) do local _continue_0 = false @@ -348,7 +332,7 @@ local compile = setmetatable({ lua:append("..") end if bit.type ~= "Text" then - bit_lua = LuaCode(bit.source, "tostring(", bit_lua, ")") + bit_lua = LuaCode:from(bit.source, "tostring(", bit_lua, ")") end lua:append(bit_lua) _continue_0 = true @@ -368,7 +352,7 @@ local compile = setmetatable({ end return lua elseif "List" == _exp_0 or "Dict" == _exp_0 then - local lua = LuaCode(tree.source, tostring(tree.type) .. "{") + local lua = LuaCode:from(tree.source, tostring(tree.type) .. "{") local i = 1 local sep = '' while i <= #tree do @@ -389,32 +373,38 @@ local compile = setmetatable({ end lua:append("}") if i <= #tree then - lua = LuaCode(tree.source, "(function()\n local it = ", lua) + lua = LuaCode:from(tree.source, "(function()\n local comprehension = ", lua) + if tree.type == "List" then + lua:append("\n local function add(x) comprehension[#comprehension+1] = x end") + else + lua:append("\n local function " .. tostring(("add 1 ="):as_lua_id()) .. "(k, v) comprehension[k] = v end") + end while i <= #tree do lua:append("\n ") if tree[i].type == 'Block' or tree[i].type == 'Comment' then lua:append(compile(tree[i])) elseif tree[i].type == "DictEntry" then - lua:append("it[ ", compile(tree[i][1]), "] = ", (tree[i][2] and compile(tree[i][2]) or "true")) + local entry_lua = compile(tree[i]) + lua:append((entry_lua:text():sub(1, 1) == '[' and "comprehension" or "comprehension."), entry_lua) else - lua:append("it:add(", compile(tree[i]), ")") + lua:append("comprehension[#comprehension+1] = ", compile(tree[i])) end i = i + 1 end - lua:append("\n return it\nend)()") + lua:append("\n return comprehension\nend)()") end return lua elseif "DictEntry" == _exp_0 then local key, value = tree[1], tree[2] local key_lua = compile(key) - local value_lua = value and compile(value) or LuaCode(key.source, "true") + local value_lua = value and compile(value) or LuaCode:from(key.source, "true") local key_str = match(key_lua:text(), [=[^["']([a-zA-Z_][a-zA-Z0-9_]*)['"]$]=]) if key_str and key_str:is_lua_id() then - return LuaCode(tree.source, key_str, "=", value_lua) + return LuaCode:from(tree.source, key_str, "=", value_lua) elseif sub(key_lua:text(), 1, 1) == "[" then - return LuaCode(tree.source, "[ ", key_lua, "]=", value_lua) + return LuaCode:from(tree.source, "[ ", key_lua, "]=", value_lua) else - return LuaCode(tree.source, "[", key_lua, "]=", value_lua) + return LuaCode:from(tree.source, "[", key_lua, "]=", value_lua) end elseif "IndexChain" == _exp_0 then local lua = compile(tree[1]) @@ -437,13 +427,13 @@ local compile = setmetatable({ end return lua elseif "Number" == _exp_0 then - return LuaCode(tree.source, tostring(tree[1])) + return LuaCode:from(tree.source, tostring(tree[1])) elseif "Var" == _exp_0 then - return LuaCode(tree.source, (tree[1]):as_lua_id()) + return LuaCode:from(tree.source, (tree[1]):as_lua_id()) elseif "FileChunks" == _exp_0 then return error("Can't convert FileChunks to a single block of lua, since each chunk's " .. "compilation depends on the earlier chunks") elseif "Comment" == _exp_0 then - return LuaCode(tree.source, "") + return LuaCode:from(tree.source, "") elseif "Error" == _exp_0 then return error("Can't compile errors") else diff --git a/nomsu_compiler.moon b/nomsu_compiler.moon index 847da21..97cb0a7 100644 --- a/nomsu_compiler.moon +++ b/nomsu_compiler.moon @@ -40,23 +40,12 @@ compile_error = (tree, err_msg, hint=nil)-> -- math expressions like 2*x + 3^2 without having to define a single -- action for every possibility. math_expression = re.compile [[ (([*/^+-] / [0-9]+) " ")* [*/^+-] !. ]] -compile_math_expression = (compile, tree, ...)-> - lua = LuaCode(tree.source) - for i,tok in ipairs tree - if type(tok) == 'string' - lua\append tok - else - tok_lua = compile(tok) - tok_lua\parenthesize! if tok.type == "Action" - lua\append tok_lua - lua\append " " if i < #tree - return lua MAX_LINE = 80 -- For beautification purposes, try not to make lines much longer than this value compile = setmetatable({ action: Importer{ - [""]: (compile, tree, fn, ...)-> - lua = LuaCode(tree.source) + [""]: (compile, fn, ...)-> + lua = LuaCode! fn_lua = compile(fn) lua\append fn_lua unless fn_lua\text!\match("^%(.*%)$") or fn_lua\text!\match("^[_a-zA-Z][_a-zA-Z0-9.]*$") @@ -68,15 +57,15 @@ compile = setmetatable({ lua\append ")" return lua - ["Lua"]: (compile, tree, code)-> + ["Lua"]: (compile, code)-> if code.type != "Text" - return LuaCode(code.source, "LuaCode(", tostring(code.source)\as_lua!, ", ", compile(code), ")") + return LuaCode("LuaCode:from(", tostring(code.source)\as_lua!, ", ", compile(code), ")") add_bit_lua = (lua, bit_lua)-> bit_leading_len = #(bit_lua\match("^[^\n]*")) lua\append(lua\trailing_line_len! + bit_leading_len > MAX_LINE and ",\n " or ", ") lua\append(bit_lua) operate_on_text = (text)-> - lua = LuaCode(text.source, "LuaCode(", tostring(text.source)\as_lua!) + lua = LuaCode\from(text.source, "LuaCode:from(", tostring(text.source)\as_lua!) for bit in *text if type(bit) == "string" add_bit_lua(lua, bit\as_lua!) @@ -88,11 +77,11 @@ compile = setmetatable({ return lua return operate_on_text code - ["lua >"]: (compile, tree, code)-> + ["lua >"]: (compile, code)-> if code.type != "Text" - return tree + return code operate_on_text = (text)-> - lua = LuaCode(text.source) + lua = LuaCode\from(text.source) for bit in *text if type(bit) == "string" lua\append bit @@ -103,47 +92,53 @@ compile = setmetatable({ return lua return operate_on_text code - ["= lua"]: (compile, tree, code)-> compile.action["lua >"](compile, tree, code) + ["= lua"]: (compile, code)-> compile.action["lua >"](compile, code) - ["use"]: (compile, tree, path)-> + ["use"]: (compile, path)-> --if path.type == 'Text' and #path == 1 and type(path[1]) == 'string' -- unless import_to_1_from(compile, path[1]) -- compile_error tree, "Could not find anything to import for #{path}" - return LuaCode(tree.source, "run_file_1_in(#{compile(path)}, _ENV)") + return LuaCode("run_file_1_in(#{compile(path)}, _ENV)") - ["tests"]: (compile, tree)-> LuaCode(tree.source, "TESTS") - ["test"]: (compile, tree, body)-> + ["tests"]: (compile)-> LuaCode("TESTS") + ["test"]: (compile, body)-> unless body.type == 'Block' - compile_error(tree, "This should be a Block") + compile_error(body, "This should be a Block") test_nomsu = body\get_source_code!\match(":[ ]*(.*)") if indent = test_nomsu\match("\n([ ]*)") test_nomsu = test_nomsu\gsub("\n"..indent, "\n") - LuaCode tree.source, "TESTS[#{tostring(tree.source)\as_lua!}] = ", test_nomsu\as_lua! + return LuaCode "TESTS[#{tostring(body.source)\as_lua!}] = ", test_nomsu\as_lua! - ["is jit"]: (compile, tree, code)-> LuaCode(tree.source, "jit") - ["Lua version"]: (compile, tree, code)-> LuaCode(tree.source, "_VERSION") - ["nomsu environment"]: (compile, tree)-> LuaCode(tree.source, "_ENV") + ["is jit"]: (compile, code)-> LuaCode("jit") + ["Lua version"]: (compile, code)-> LuaCode("_VERSION") + ["nomsu environment"]: (compile)-> LuaCode("_ENV") } }, { __import: (other)=> import_to_1_from(@action, other.action) return __call: (compile, tree, force_value=false)-> - if tree.version - if get_version = compile.action[("Nomsu version")\as_lua_id!] - if upgrade = compile.action[("1 upgraded from 2 to")\as_lua_id!] - tree = upgrade(tree, tree.version, get_version!) switch tree.type when "Action" stub = tree.stub compile_action = compile.action[stub] if not compile_action and math_expression\match(stub) - compile_action = compile_math_expression + lua = LuaCode\from(tree.source) + for i,tok in ipairs tree + if type(tok) == 'string' + lua\append tok + else + tok_lua = compile(tok) + tok_lua\parenthesize! if tok.type == "Action" + lua\append tok_lua + lua\append " " if i < #tree + return lua + if compile_action and not tree.target args = [arg for arg in *tree when type(arg) != "string"] -- Force Lua to avoid tail call optimization for debugging purposes -- TODO: use tail call? - ret = compile_action(compile, tree, unpack(args)) + ret = compile_action(compile, unpack(args)) if ret == nil info = debug.getinfo(compile_action, "S") filename = Source\from_string(info.source).filename @@ -151,11 +146,12 @@ compile = setmetatable({ "The compile-time action here (#{stub}) failed to return any value.", "Look at the implementation of (#{stub}) in #{filename}:#{info.linedefined} and make sure it's returning something." unless SyntaxTree\is_instance(ret) + ret.source or= tree.source return ret if ret != tree return compile(ret) - lua = LuaCode(tree.source) + lua = LuaCode\from(tree.source) if tree.target -- Method call target_lua = compile tree.target target_text = target_lua\text! @@ -174,7 +170,7 @@ compile = setmetatable({ return lua when "EscapedNomsu" - lua = LuaCode tree.source, "SyntaxTree{" + lua = LuaCode\from tree.source, "SyntaxTree{" needs_comma, i = false, 1 as_lua = (x)-> if type(x) == 'number' @@ -201,11 +197,11 @@ compile = setmetatable({ when "Block" if not force_value - lua = LuaCode(tree.source) + lua = LuaCode\from(tree.source) lua\concat_append([compile(line) for line in *tree], "\n") return lua else - lua = LuaCode(tree.source) + lua = LuaCode\from(tree.source) lua\append("((function()") for i, line in ipairs(tree) lua\append "\n ", compile(line) @@ -213,7 +209,7 @@ compile = setmetatable({ return lua when "Text" - lua = LuaCode(tree.source) + lua = LuaCode\from(tree.source) string_buffer = "" for i, bit in ipairs tree if type(bit) == "string" @@ -226,7 +222,7 @@ compile = setmetatable({ bit_lua = compile(bit) if #lua.bits > 0 then lua\append ".." if bit.type != "Text" - bit_lua = LuaCode(bit.source, "tostring(",bit_lua,")") + bit_lua = LuaCode\from(bit.source, "tostring(",bit_lua,")") lua\append bit_lua if string_buffer ~= "" or #lua.bits == 0 @@ -238,7 +234,7 @@ compile = setmetatable({ return lua when "List", "Dict" - lua = LuaCode tree.source, "#{tree.type}{" + lua = LuaCode\from tree.source, "#{tree.type}{" i = 1 sep = '' while i <= #tree @@ -255,20 +251,26 @@ compile = setmetatable({ sep = ', ' i += 1 lua\append "}" + -- List/dict comprehenstion if i <= #tree - lua = LuaCode tree.source, "(function()\n local it = ", lua + lua = LuaCode\from tree.source, "(function()\n local comprehension = ", lua + if tree.type == "List" + lua\append "\n local function add(x) comprehension[#comprehension+1] = x end" + else + lua\append "\n local function #{("add 1 =")\as_lua_id!}(k, v) comprehension[k] = v end" while i <= #tree lua\append "\n " if tree[i].type == 'Block' or tree[i].type == 'Comment' lua\append compile(tree[i]) elseif tree[i].type == "DictEntry" - lua\append "it[ ", compile(tree[i][1]), "] = ", (tree[i][2] and compile(tree[i][2]) or "true") + entry_lua = compile(tree[i]) + lua\append (entry_lua\text!\sub(1,1) == '[' and "comprehension" or "comprehension."), entry_lua else - lua\append "it:add(", compile(tree[i]), ")" + lua\append "comprehension[#comprehension+1] = ", compile(tree[i]) i += 1 - lua\append "\n return it\nend)()" + lua\append "\n return comprehension\nend)()" return lua - --lua = LuaCode tree.source, "#{tree.type}{" + --lua = LuaCode\from tree.source, "#{tree.type}{" --lua\concat_append([compile(e) for e in *tree when e.type != 'Comment'], ", ", ",\n ") --lua\append "}" --return lua @@ -276,17 +278,17 @@ compile = setmetatable({ when "DictEntry" key, value = tree[1], tree[2] key_lua = compile(key) - value_lua = value and compile(value) or LuaCode(key.source, "true") + value_lua = value and compile(value) or LuaCode\from(key.source, "true") key_str = match(key_lua\text!, [=[^["']([a-zA-Z_][a-zA-Z0-9_]*)['"]$]=]) return if key_str and key_str\is_lua_id! - LuaCode tree.source, key_str,"=",value_lua + LuaCode\from tree.source, key_str,"=",value_lua elseif sub(key_lua\text!,1,1) == "[" -- NOTE: this *must* use a space after the [ to avoid freaking out -- Lua's parser if the inner expression is a long string. Lua -- parses x[[[y]]] as x("[y]"), not as x["y"] - LuaCode tree.source, "[ ",key_lua,"]=",value_lua + LuaCode\from tree.source, "[ ",key_lua,"]=",value_lua else - LuaCode tree.source, "[",key_lua,"]=",value_lua + LuaCode\from tree.source, "[",key_lua,"]=",value_lua when "IndexChain" lua = compile(tree[1]) @@ -311,10 +313,10 @@ compile = setmetatable({ return lua when "Number" - return LuaCode(tree.source, tostring(tree[1])) + return LuaCode\from(tree.source, tostring(tree[1])) when "Var" - return LuaCode(tree.source, (tree[1])\as_lua_id!) + return LuaCode\from(tree.source, (tree[1])\as_lua_id!) when "FileChunks" error("Can't convert FileChunks to a single block of lua, since each chunk's ".. @@ -322,7 +324,7 @@ compile = setmetatable({ when "Comment" -- TODO: implement? - return LuaCode(tree.source, "") + return LuaCode\from(tree.source, "") when "Error" error("Can't compile errors") diff --git a/nomsu_decompiler.lua b/nomsu_decompiler.lua index 8d0aa7c..a3d7f41 100644 --- a/nomsu_decompiler.lua +++ b/nomsu_decompiler.lua @@ -47,7 +47,7 @@ local tree_to_inline_nomsu tree_to_inline_nomsu = function(tree) local _exp_0 = tree.type if "Action" == _exp_0 then - local nomsu = NomsuCode(tree.source) + local nomsu = NomsuCode:from(tree.source) if tree.target then local inline_target = tree_to_inline_nomsu(tree.target) if tree.target.type == "Action" then @@ -88,9 +88,9 @@ tree_to_inline_nomsu = function(tree) if not (tree[1].type == "List" or tree[1].type == "Dict" or tree[1].type == "Var") then inner_nomsu:parenthesize() end - return NomsuCode(tree.source, "\\", inner_nomsu) + return NomsuCode:from(tree.source, "\\", inner_nomsu) elseif "Block" == _exp_0 then - local nomsu = NomsuCode(tree.source, ":") + local nomsu = NomsuCode:from(tree.source, ":") for i, line in ipairs(tree) do nomsu:append(i == 1 and " " or "; ") nomsu:append(tree_to_inline_nomsu(line)) @@ -119,11 +119,11 @@ tree_to_inline_nomsu = function(tree) end end end - local nomsu = NomsuCode(tree.source) + local nomsu = NomsuCode:from(tree.source) add_text(nomsu, tree) - return NomsuCode(tree.source, '"', nomsu, '"') + return NomsuCode:from(tree.source, '"', nomsu, '"') elseif "List" == _exp_0 or "Dict" == _exp_0 then - local nomsu = NomsuCode(tree.source, (tree.type == "List" and "[" or "{")) + local nomsu = NomsuCode:from(tree.source, (tree.type == "List" and "[" or "{")) for i, item in ipairs(tree) do if i > 1 then nomsu:append(", ") @@ -136,7 +136,7 @@ tree_to_inline_nomsu = function(tree) local key, value = tree[1], tree[2] local nomsu if key.type == "Text" and #key == 1 and is_identifier(key[1]) then - nomsu = NomsuCode(key.source, key[1]) + nomsu = NomsuCode:from(key.source, key[1]) else nomsu = tree_to_inline_nomsu(key) end @@ -154,7 +154,7 @@ tree_to_inline_nomsu = function(tree) end return nomsu elseif "IndexChain" == _exp_0 then - local nomsu = NomsuCode(tree.source) + local nomsu = NomsuCode:from(tree.source) for i, bit in ipairs(tree) do if i > 1 then nomsu:append(".") @@ -173,9 +173,9 @@ tree_to_inline_nomsu = function(tree) end return nomsu elseif "Number" == _exp_0 then - return NomsuCode(tree.source, tostring(tree[1])) + return NomsuCode:from(tree.source, tostring(tree[1])) elseif "Var" == _exp_0 then - return NomsuCode(tree.source, "%", tree[1]) + return NomsuCode:from(tree.source, "%", tree[1]) elseif "FileChunks" == _exp_0 then return error("Can't inline a FileChunks") elseif "Comment" == _exp_0 then @@ -188,7 +188,7 @@ tree_to_inline_nomsu = function(tree) end local tree_to_nomsu tree_to_nomsu = function(tree) - local nomsu = NomsuCode(tree.source) + local nomsu = NomsuCode:from(tree.source) local recurse recurse = function(t) local space = MAX_LINE - nomsu:trailing_line_len() @@ -214,7 +214,7 @@ tree_to_nomsu = function(tree) local indented = tree_to_nomsu(t) if t.type == "Action" then if indented:is_multiline() then - return NomsuCode(t.source, "(..)\n ", indented) + return NomsuCode:from(t.source, "(..)\n ", indented) else indented:parenthesize() end @@ -304,7 +304,7 @@ tree_to_nomsu = function(tree) nomsu:append(line_nomsu:match('\n[^\n]*\n') and "\n\n" or "\n") end end - return NomsuCode(tree.source, ":\n ", nomsu) + return NomsuCode:from(tree.source, ":\n ", nomsu) elseif "Text" == _exp_0 then local max_line = math.floor(1.25 * MAX_LINE) local add_text @@ -357,7 +357,7 @@ tree_to_nomsu = function(tree) end end add_text(tree) - return NomsuCode(tree.source, '"\\\n ..', nomsu, '"') + return NomsuCode:from(tree.source, '"\\\n ..', nomsu, '"') elseif "List" == _exp_0 or "Dict" == _exp_0 then if #tree == 0 then nomsu:append(tree.type == "List" and "[]" or "{}") @@ -376,14 +376,14 @@ tree_to_nomsu = function(tree) end end if tree.type == "List" then - return NomsuCode(tree.source, "[..]\n ", nomsu) + return NomsuCode:from(tree.source, "[..]\n ", nomsu) else - return NomsuCode(tree.source, "{..}\n ", nomsu) + return NomsuCode:from(tree.source, "{..}\n ", nomsu) end elseif "DictEntry" == _exp_0 then local key, value = tree[1], tree[2] if key.type == "Text" and #key == 1 and is_identifier(key[1]) then - nomsu = NomsuCode(key.source, key[1]) + nomsu = NomsuCode:from(key.source, key[1]) else nomsu = tree_to_inline_nomsu(key) end diff --git a/nomsu_decompiler.moon b/nomsu_decompiler.moon index df1c4d6..828df06 100644 --- a/nomsu_decompiler.moon +++ b/nomsu_decompiler.moon @@ -31,7 +31,7 @@ escape = (s)-> tree_to_inline_nomsu = (tree)-> switch tree.type when "Action" - nomsu = NomsuCode(tree.source) + nomsu = NomsuCode\from(tree.source) if tree.target inline_target = tree_to_inline_nomsu(tree.target) if tree.target.type == "Action" @@ -61,10 +61,10 @@ tree_to_inline_nomsu = (tree)-> inner_nomsu = tree_to_inline_nomsu(tree[1]) unless tree[1].type == "List" or tree[1].type == "Dict" or tree[1].type == "Var" inner_nomsu\parenthesize! - return NomsuCode(tree.source, "\\", inner_nomsu) + return NomsuCode\from(tree.source, "\\", inner_nomsu) when "Block" - nomsu = NomsuCode(tree.source, ":") + nomsu = NomsuCode\from(tree.source, ":") for i,line in ipairs tree nomsu\append(i == 1 and " " or "; ") nomsu\append tree_to_inline_nomsu(line) @@ -86,12 +86,12 @@ tree_to_inline_nomsu = (tree)-> elseif bit.type == "Var" and type(tree[i+1]) == 'string' and not match(tree[i+1], "^[ \n\t,.:;#(){}[%]]") interp_nomsu\parenthesize! nomsu\append "\\", interp_nomsu - nomsu = NomsuCode(tree.source) + nomsu = NomsuCode\from(tree.source) add_text(nomsu, tree) - return NomsuCode(tree.source, '"', nomsu, '"') + return NomsuCode\from(tree.source, '"', nomsu, '"') when "List", "Dict" - nomsu = NomsuCode(tree.source, (tree.type == "List" and "[" or "{")) + nomsu = NomsuCode\from(tree.source, (tree.type == "List" and "[" or "{")) for i, item in ipairs tree nomsu\append ", " if i > 1 nomsu\append tree_to_inline_nomsu(item) @@ -101,7 +101,7 @@ tree_to_inline_nomsu = (tree)-> when "DictEntry" key, value = tree[1], tree[2] nomsu = if key.type == "Text" and #key == 1 and is_identifier(key[1]) - NomsuCode(key.source, key[1]) + NomsuCode\from(key.source, key[1]) else tree_to_inline_nomsu(key) nomsu\parenthesize! if key.type == "Action" or key.type == "Block" assert(value.type != "Block", "Didn't expect to find a Block as a value in a dict") @@ -113,7 +113,7 @@ tree_to_inline_nomsu = (tree)-> return nomsu when "IndexChain" - nomsu = NomsuCode(tree.source) + nomsu = NomsuCode\from(tree.source) for i, bit in ipairs tree nomsu\append "." if i > 1 local bit_nomsu @@ -127,10 +127,10 @@ tree_to_inline_nomsu = (tree)-> return nomsu when "Number" - return NomsuCode(tree.source, tostring(tree[1])) + return NomsuCode\from(tree.source, tostring(tree[1])) when "Var" - return NomsuCode(tree.source, "%", tree[1]) + return NomsuCode\from(tree.source, "%", tree[1]) when "FileChunks" error("Can't inline a FileChunks") @@ -146,7 +146,7 @@ tree_to_inline_nomsu = (tree)-> error("Unknown type: #{tree.type}") tree_to_nomsu = (tree)-> - nomsu = NomsuCode(tree.source) + nomsu = NomsuCode\from(tree.source) -- For concision: recurse = (t)-> @@ -166,7 +166,7 @@ tree_to_nomsu = (tree)-> indented = tree_to_nomsu(t) if t.type == "Action" if indented\is_multiline! - return NomsuCode(t.source, "(..)\n ", indented) + return NomsuCode\from(t.source, "(..)\n ", indented) else indented\parenthesize! return indented @@ -234,7 +234,7 @@ tree_to_nomsu = (tree)-> if i < #tree -- number of lines > 2 (TODO: improve this) nomsu\append(line_nomsu\match('\n[^\n]*\n') and "\n\n" or "\n") - return NomsuCode(tree.source, ":\n ", nomsu) + return NomsuCode\from(tree.source, ":\n ", nomsu) when "Text" -- Multi-line text has more generous wrap margins @@ -274,7 +274,7 @@ tree_to_nomsu = (tree)-> if interp_nomsu\is_multiline! nomsu\append "\n.." add_text(tree) - return NomsuCode(tree.source, '"\\\n ..', nomsu, '"') + return NomsuCode\from(tree.source, '"\\\n ..', nomsu, '"') when "List", "Dict" if #tree == 0 @@ -290,14 +290,14 @@ tree_to_nomsu = (tree)-> if i < #tree nomsu\append((item_nomsu\is_multiline! or nomsu\trailing_line_len! + #tostring(item_nomsu) >= MAX_LINE) and '\n' or ', ') return if tree.type == "List" then - NomsuCode(tree.source, "[..]\n ", nomsu) + NomsuCode\from(tree.source, "[..]\n ", nomsu) else - NomsuCode(tree.source, "{..}\n ", nomsu) + NomsuCode\from(tree.source, "{..}\n ", nomsu) when "DictEntry" key, value = tree[1], tree[2] nomsu = if key.type == "Text" and #key == 1 and is_identifier(key[1]) - NomsuCode(key.source, key[1]) + NomsuCode\from(key.source, key[1]) else tree_to_inline_nomsu(key) nomsu\parenthesize! if key.type == "Block" value_nomsu = tree_to_nomsu(value) diff --git a/nomsu_environment.lua b/nomsu_environment.lua index e72eb20..dd8b3ec 100644 --- a/nomsu_environment.lua +++ b/nomsu_environment.lua @@ -52,6 +52,7 @@ do tree_to_nomsu, tree_to_inline_nomsu = _obj_0.tree_to_nomsu, _obj_0.tree_to_inline_nomsu end local compile = require('nomsu_compiler') +local _currently_running_files = List({ }) local nomsu_environment = Importer({ NOMSU_COMPILER_VERSION = 12, NOMSU_SYNTAX_VERSION = max_parser_version, @@ -113,7 +114,7 @@ local nomsu_environment = Importer({ _1_parsed = function(nomsu_code) if type(nomsu_code) == 'string' then local filename = Files.spoof(nomsu_code) - nomsu_code = NomsuCode(Source(filename, 1, #nomsu_code), nomsu_code) + nomsu_code = NomsuCode:from(Source(filename, 1, #nomsu_code), nomsu_code) end local source = nomsu_code.source nomsu_code = tostring(nomsu_code) @@ -186,7 +187,7 @@ local nomsu_environment = Importer({ run_1_in = function(to_run, environment) if type(to_run) == 'string' then local filename = Files.spoof(to_run) - to_run = NomsuCode(Source(filename, 1, #to_run), to_run) + to_run = NomsuCode:from(Source(filename, 1, #to_run), to_run) local ret = environment.run_1_in(to_run, environment) return ret elseif NomsuCode:is_instance(to_run) then @@ -277,6 +278,14 @@ local nomsu_environment = Importer({ import_to_1_from(environment, environment.FILE_CACHE[path]) return end + if _currently_running_files:has(path) then + local i = _currently_running_files:index_of(path) + _currently_running_files:add(path) + local circle = _currently_running_files:from_1_to(i, -1) + print(_currently_running_files, path) + error("Circular import detected:\n " .. circle:joined_with("\n..imports ")) + end + _currently_running_files:add(path) local mod = _1_forked(environment) assert(mod._1_parsed) mod._ENV = mod @@ -291,10 +300,10 @@ local nomsu_environment = Importer({ local code if optimization ~= 0 and Files.read(lua_filename) then local file = Files.read(lua_filename) - code = LuaCode(Source(filename, 1, #file), file) + code = LuaCode:from(Source(filename, 1, #file), file) else local file = Files.read(filename) - code = NomsuCode(Source(filename, 1, #file), file) + code = NomsuCode:from(Source(filename, 1, #file), file) end environment.run_1_in(code, mod) _continue_0 = true @@ -305,6 +314,7 @@ local nomsu_environment = Importer({ end import_to_1_from(environment, mod) environment.FILE_CACHE[path] = mod + return _currently_running_files:remove() end, compile_error_at = function(tree, err_msg, hint) if hint == nil then diff --git a/nomsu_environment.moon b/nomsu_environment.moon index b216242..bfc5edc 100644 --- a/nomsu_environment.moon +++ b/nomsu_environment.moon @@ -32,6 +32,7 @@ for version=1,999 {:tree_to_nomsu, :tree_to_inline_nomsu} = require "nomsu_decompiler" compile = require('nomsu_compiler') +_currently_running_files = List{} -- Used to check for circular imports in run_file_1_in nomsu_environment = Importer{ NOMSU_COMPILER_VERSION: 12, NOMSU_SYNTAX_VERSION: max_parser_version -- Lua stuff: @@ -57,7 +58,7 @@ nomsu_environment = Importer{ _1_parsed: (nomsu_code)-> if type(nomsu_code) == 'string' filename = Files.spoof(nomsu_code) - nomsu_code = NomsuCode(Source(filename, 1, #nomsu_code), nomsu_code) + nomsu_code = NomsuCode\from(Source(filename, 1, #nomsu_code), nomsu_code) source = nomsu_code.source nomsu_code = tostring(nomsu_code) version = nomsu_code\match("^#![^\n]*nomsu[ ]+-V[ ]*([0-9.]+)") @@ -88,7 +89,7 @@ nomsu_environment = Importer{ run_1_in: (to_run, environment)-> if type(to_run) == 'string' filename = Files.spoof(to_run) - to_run = NomsuCode(Source(filename, 1, #to_run), to_run) + to_run = NomsuCode\from(Source(filename, 1, #to_run), to_run) ret = environment.run_1_in(to_run, environment) return ret elseif NomsuCode\is_instance(to_run) @@ -151,6 +152,13 @@ nomsu_environment = Importer{ if environment.FILE_CACHE[path] import_to_1_from(environment, environment.FILE_CACHE[path]) return + if _currently_running_files\has(path) + i = _currently_running_files\index_of(path) + _currently_running_files\add path + circle = _currently_running_files\from_1_to(i, -1) + print(_currently_running_files, path) + error("Circular import detected:\n "..circle\joined_with("\n..imports ")) + _currently_running_files\add path mod = _1_forked(environment) assert mod._1_parsed mod._ENV = mod @@ -160,13 +168,14 @@ nomsu_environment = Importer{ -- TODO: don't automatically use precompiled version? code = if optimization != 0 and Files.read(lua_filename) file = Files.read(lua_filename) - LuaCode(Source(filename, 1, #file), file) + LuaCode\from(Source(filename, 1, #file), file) else file = Files.read(filename) - NomsuCode(Source(filename, 1, #file), file) + NomsuCode\from(Source(filename, 1, #file), file) environment.run_1_in(code, mod) import_to_1_from(environment, mod) environment.FILE_CACHE[path] = mod + _currently_running_files\remove! compile_error_at: (tree, err_msg, hint=nil)-> err_str = pretty_error{