From 02af19153eff6dad434d2c1c527e33d078e3dfab Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Thu, 8 Feb 2018 16:22:57 -0800 Subject: [PATCH] Moved over to have tree metadata stored outside the tree. This paves the way for programmatically generating trees more easily. --- core/control_flow.nom | 24 ++-- core/metaprogramming.nom | 93 +++++++++------ lib/object.nom | 22 ++-- lib/object2.nom | 22 ++-- nomsu.lua | 243 ++++++++++++++++++++++++++------------- nomsu.moon | 160 +++++++++++++++++--------- 6 files changed, 363 insertions(+), 201 deletions(-) diff --git a/core/control_flow.nom b/core/control_flow.nom index 315c4ef..4066a97 100644 --- a/core/control_flow.nom +++ b/core/control_flow.nom @@ -104,14 +104,14 @@ immediately: %body_lua <- (%body as lua) %body_statements <- ((%body_lua's "statements") or "\(%body_lua's "expr");") if %body has subtree % where: - ((%'s "type") = "FunctionCall") and ((%'s "stub") is "do next repeat") + ((%'s "type") = "FunctionCall") and ((%'s stub) is "do next repeat") ..: %body_statments +<- "\n::continue_repeat::;" %code <- ".." while \(%condition as lua expr) do \%body_statements end --while-loop if %body has subtree % where: - ((%'s "type") = "FunctionCall") and ((%'s "stub") is "stop repeating") + ((%'s "type") = "FunctionCall") and ((%'s stub) is "stop repeating") ..: %code <- ".." do -- scope of "stop repeating" label @@ -128,14 +128,14 @@ immediately: %body_lua <- (%body as lua) %body_statements <- ((%body_lua's "statements") or "\(%body_lua's "expr");") if %body has subtree % where - ((%'s "type") = "FunctionCall") and ((%'s "stub") is "do next repeat") + ((%'s "type") = "FunctionCall") and ((%'s stub) is "do next repeat") ..: %body_statements +<- "\n::continue_repeat::;" %code <- ".." for i=1,\(%n as lua expr) do \%body_statements end --numeric for-loop if %body has subtree % where: - ((%'s "type") = "FunctionCall") and ((%'s "stub") is "stop repeating") + ((%'s "type") = "FunctionCall") and ((%'s stub) is "stop repeating") ..: %code <- ".." do -- scope of "stop repeating" label @@ -161,7 +161,7 @@ immediately: %body_statements <- ((%body_lua's "statements") or "\(%body_lua's "expr");") if %body has subtree % where: ((%'s "type") = "FunctionCall") and - ((%'s "stub") is "do next %") and + ((%'s stub) is "do next %") and ((3rd in (%'s "value"))'s "value") is (%var's "value") ..: %body_statements +<- "\n::continue_\(%var as lua identifier)::;" @@ -174,7 +174,7 @@ immediately: if %body has subtree % where: ((%'s "type") = "FunctionCall") and: - ((%'s "stub") is "stop %") and: + ((%'s stub) is "stop %") and: ((2nd in (%'s "value"))'s "value") is (%var's "value") ..: %code <- ".." @@ -200,7 +200,7 @@ immediately: %body_statements <- ((%body_lua's "statements") or "\(%body_lua's "expr");") if %body has subtree % where: ((%'s "type") = "FunctionCall") and - ((%'s "stub") is "do next %") and + ((%'s stub) is "do next %") and ((3rd in (%'s "value"))'s "value") is (%var's "value") ..: %body_statements +<- "\n::continue_\(%var as lua identifier)::;" # This uses Lua's approach of only allowing loop-scoped variables in a loop @@ -211,7 +211,7 @@ immediately: end --foreach-loop if %body has subtree % where: ((%'s "type") = "FunctionCall") and - ((%'s "stub") is "stop %") and + ((%'s stub) is "stop %") and ((2nd in (%'s "value"))'s "value") is (%var's "value") ..: %code <- ".." @@ -230,13 +230,13 @@ immediately: %body_statements <- ((%body_lua's "statements") or "\(%body_lua's "expr");") if %body has subtree % where: ((%'s "type") = "FunctionCall") and - ((%'s "stub") is "do next %") and + ((%'s stub) is "do next %") and ((3rd in (%'s "value"))'s "value") is (%key's "value") ..: %body_statements +<- "\n::continue_\(%key as lua identifier)::;" if %body has subtree % where: ((%'s "type") = "FunctionCall") and - ((%'s "stub") is "do next %") and + ((%'s stub) is "do next %") and ((3rd in (%'s "value"))'s "value") is (%value's "value") ..: %body_statements +<- "\n::continue_\(%value as lua identifier)::;" @@ -251,13 +251,13 @@ immediately: %stop_labels <- "" if %body has subtree % where: ((%'s "type") = "FunctionCall") and - ((%'s "stub") is "stop %") and + ((%'s stub) is "stop %") and ((2nd in (%'s "value"))'s "value") is (%key's "value") ..: %stop_labels +<- "\n::stop_\(%key as lua identifier)::;" if %body has subtree % where: ((%'s "type") = "FunctionCall") and - ((%'s "stub") is "stop %") and + ((%'s stub) is "stop %") and ((2nd in (%'s "value"))'s "value") is (%value's "value") ..: %stop_labels +<- "\n::stop_\(%value as lua identifier)::;" diff --git a/core/metaprogramming.nom b/core/metaprogramming.nom index b56f0e9..fa589bd 100644 --- a/core/metaprogramming.nom +++ b/core/metaprogramming.nom @@ -6,12 +6,16 @@ immediately: lua> ".." nomsu:define_compile_action("compile %actions to %lua", \(!! code location !!), function(\%actions, \%lua) - local signature = {}; - for i, action in ipairs(\%actions.value) do signature[i] = action:get_src(); end - local stubs = nomsu:get_stubs_from_signature(signature); - local stub_args = nomsu:get_args_from_signature(signature); + local stubs = {}; + for i, action in ipairs(\%actions.value) do + stubs[i] = nomsu:tree_to_named_stub(action); + end + local args = {}; + for i,tok in ipairs(\%actions.value[1].value) do + if tok.type == "Var" then args[#args+1] = nomsu:var_to_lua_identifier(tok.value); end + end local arg_set = {}; - for i, arg in ipairs(stub_args[1]) do arg_set[arg] = true; end + for i, arg in ipairs(args) do arg_set[arg] = true; end if \%lua.type == "Text" then error("Invalid type for 'compile % to %', expected a dict with expr/statements, but got text.", 0); end @@ -26,11 +30,11 @@ immediately: if #undeclared_locals > 0 then body_code = "local "..table.concat(undeclared_locals, ", ")..";\\n"..body_code; end - local lua_fn_args = table.concat(stub_args[1], ", "); - local def_tree = nomsu.compilestack[#nomsu.compilestack]; - local code_location = ("%s:%s,%s"):format(def_tree.filename, def_tree.start, def_tree.stop); + local lua_fn_args = table.concat(args, ", "); + local def_metadata = nomsu.tree_metadata[nomsu.compilestack[#nomsu.compilestack]]; + local code_location = ("%s:%s,%s"):format(def_metadata.filename, def_metadata.start, def_metadata.stop); return {statements=([[ - nomsu:define_compile_action(]]..repr(signature)..[[, ]]..repr(code_location)..[[, function(]]..lua_fn_args..[[) + nomsu:define_compile_action(]]..repr(stubs)..[[, ]]..repr(code_location)..[[, function(]]..lua_fn_args..[[) ]]..body_code.."\\n"..[[ end); ]])}; @@ -40,12 +44,16 @@ immediately: immediately: compile [action %actions %body] to: lua> ".." - local signature = {}; - for i, action in ipairs(\%actions.value) do signature[i] = action:get_src(); end - local stubs = nomsu:get_stubs_from_signature(signature); - local stub_args = nomsu:get_args_from_signature(signature); + local stubs = {}; + for i, action in ipairs(\%actions.value) do + stubs[i] = nomsu:tree_to_named_stub(action); + end + local args = {}; + for i,tok in ipairs(\%actions.value[1].value) do + if tok.type == "Var" then args[#args+1] = nomsu:var_to_lua_identifier(tok.value); end + end local arg_set = {}; - for i, arg in ipairs(stub_args[1]) do arg_set[arg] = true; end + for i, arg in ipairs(args) do arg_set[arg] = true; end local body_lua = nomsu:tree_to_lua(\%body); local body_code = body_lua.statements or ("return "..body_lua.expr..";"); local undeclared_locals = {}; @@ -57,11 +65,12 @@ immediately: if #undeclared_locals > 0 then body_code = "local "..table.concat(undeclared_locals, ", ")..";\\n"..body_code; end - local lua_fn_args = table.concat(stub_args[1], ", "); - local def_tree = nomsu.compilestack[#nomsu.compilestack]; - local code_location = ("%s:%s,%s"):format(def_tree.filename, def_tree.start, def_tree.stop); + local lua_fn_args = table.concat(args, ", "); + local def_metadata = nomsu.tree_metadata[nomsu.compilestack[#nomsu.compilestack]]; + assert(def_metadata, "No metadata found for: "..tostring(nomsu.compilestack[#nomsu.compilestack])); + local code_location = ("%s:%s,%s"):format(def_metadata.filename, def_metadata.start, def_metadata.stop); return {statements=[[ - nomsu:define_action(]]..repr(signature)..[[, ]]..repr(code_location)..[[, function(]]..lua_fn_args..[[) + nomsu:define_action(]]..repr(stubs)..[[, ]]..repr(code_location)..[[, function(]]..lua_fn_args..[[) ]]..body_code.."\\n"..[[ end); ]]}; @@ -70,27 +79,31 @@ immediately: immediately: compile [parse %shorthand as %longhand] to: lua> ".." - local signature = {}; - for i, action in ipairs(\%shorthand.value) do signature[i] = action:get_src(); end - local stubs = nomsu:get_stubs_from_signature(signature); - local stub_args = nomsu:get_args_from_signature(signature); - local lua_fn_args = table.concat(stub_args[1], ", "); + local stubs = {}; + for i, action in ipairs(\%shorthand.value) do + stubs[i] = nomsu:tree_to_named_stub(action); + end + local args = {}; + for i,tok in ipairs(\%shorthand.value[1].value) do + if tok.type == "Var" then args[#args+1] = nomsu:var_to_lua_identifier(tok.value); end + end + local lua_fn_args = table.concat(args, ", "); local template; if \%longhand.type == "Block" then local lines = {}; - for i, line in ipairs(\%longhand.value) do lines[i] = nomsu:dedent(line:get_src()); end + for i, line in ipairs(\%longhand.value) do lines[i] = nomsu:get_source_code(line); end template = repr(table.concat(lines, "\\n")); else - template = repr(nomsu:dedent(\%longhand:get_src())); + template = repr(nomsu:get_source_code(\%longhand)); end local replacements = {}; - for i, a in ipairs(stub_args[1]) do replacements[i] = a.."="..a; end + for i, a in ipairs(args) do replacements[i] = a.."="..a; end replacements = "{"..table.concat(replacements, ", ").."}"; - local def_tree = nomsu.compilestack[#nomsu.compilestack]; - local code_location = ("%s:%s,%s"):format(def_tree.filename, def_tree.start, def_tree.stop); + local def_metadata = nomsu.tree_metadata[nomsu.compilestack[#nomsu.compilestack]]; + local code_location = ("%s:%s,%s"):format(def_metadata.filename, def_metadata.start, def_metadata.stop); return {statements=[[ - nomsu:define_compile_action(]]..repr(signature)..[[, ]]..repr(code_location)..[[, function(]]..lua_fn_args..[[) - local template = nomsu:parse(]]..template..[[, ]]..repr(def_tree.filename)..[[); + nomsu:define_compile_action(]]..repr(stubs)..[[, ]]..repr(code_location)..[[, function(]]..lua_fn_args..[[) + local template = nomsu:parse(]]..template..[[, ]]..repr(def_metadata.filename)..[[); local replacement = nomsu:tree_with_replaced_vars(template, ]]..replacements..[[); return nomsu:tree_to_lua(replacement); end); @@ -99,7 +112,7 @@ immediately: action [remove action %stub]: lua> ".." local fn = ACTIONS[\%stub]; - local metadata = ACTION_METADATA[fn]; + local metadata = nomsu.action_metadata[fn]; for i=#metadata.aliases,1,-1 do metadata.arg_orders[metadata.aliases[i]] = nil; table.remove(metadata.aliases, i); @@ -114,7 +127,7 @@ immediately: lua> ".." local lua = nomsu:tree_to_lua(\%tree); if lua.locals or not lua.expr then - error("Invalid thing to convert to lua expr: "..\%tree:get_src()); + error("Invalid thing to convert to lua expr: "..nomsu:get_source_code(\%tree)); end return lua.expr; @@ -130,8 +143,11 @@ immediately: action [%tree as value]: =lua "nomsu:tree_to_value(\%tree)" + action [%tree's stub]: + =lua "nomsu:tree_to_stub(\%tree)" + immediately: - compile [%tree's source code, %tree' source code] to {expr:"\(%tree as lua expr):get_src()"} + compile [%tree's source code, %tree' source code] to {expr:"nomsu:get_source_code(\(%tree as lua expr))"} compile [repr %obj] to {expr:"repr(\(%obj as lua expr))"} compile [type of %obj] to {expr:"type(\(%obj as lua expr))"} @@ -141,7 +157,7 @@ immediately: compile [%var as lua identifier] to {expr:"nomsu:var_to_lua_identifier(\(%var as lua expr))"} action [action %names metadata]: - =lua "ACTION_METADATA[ACTIONS[\%names]]" + =lua "nomsu.action_metadata[ACTIONS[\%names]]" # Get the source code for a function action [help %action]: @@ -155,7 +171,12 @@ action [help %action]: # Compiler tools immediately: - compile [run %code] to {expr: "nomsu:run(\(%code as lua expr), '\(!! code location !!)')"} + #local def_metadata = nomsu.tree_metadata[nomsu.compilestack[#nomsu.compilestack]]; + compile [run %code] to {..} + expr: ".." + nomsu:run(\(%code as lua expr), '\ + =lua "nomsu.tree_metadata[nomsu.compilestack[#nomsu.compilestack]].filename" + ..') parse [enable debugging] as: lua> "nomsu.debug = true;" parse [disable debugging] as: lua> "nomsu.debug = false;" @@ -186,7 +207,7 @@ immediately: compile [barf] to {statements:"error(nil, 0);"} compile [barf %msg] to {statements:"error(\(%msg as lua expr), 0);"} compile [assume %condition] to: - lua> "local \%assumption = 'Assumption failed: '..\%condition:get_src();" + lua> "local \%assumption = 'Assumption failed: '..nomsu:get_source_code(\%condition);" return {..} statements:".." if not \(%condition as lua expr) then diff --git a/lib/object.nom b/lib/object.nom index 2f2f3ff..ca664b5 100644 --- a/lib/object.nom +++ b/lib/object.nom @@ -33,17 +33,21 @@ compile [define object %classname %class_body] to: for %line in (%class_body's "value"): if: (%line's "type") is "Comment" do next %line - assume (((%line's "type") == "FunctionCall") and ((%line's "stub") == "action % %")) + assume (((%line's "type") == "FunctionCall") and ((%line's stub) == "action % %")) ..or barf "Only action definitions are supported inside 'define object % %', not \(%line's "src")" %actions <- (2nd in (%line's "value")) %body <- (3rd in (%line's "value")) lua> ".." - local signature = {}; - for i, action in ipairs(\%actions.value) do signature[i] = action:get_src(); end - local stubs = nomsu:get_stubs_from_signature(signature); - local stub_args = nomsu:get_args_from_signature(signature); + local stubs = {}; + for i, action in ipairs(\%actions.value) do + stubs[i] = nomsu:tree_to_named_stub(action); + end + local args = {}; + for i,tok in ipairs(\%actions.value[1].value) do + if tok.type == "Var" then args[#args+1] = nomsu:var_to_lua_identifier(tok.value); end + end local arg_set = {}; - for i, arg in ipairs(stub_args[1]) do arg_set[arg] = true; end + for i, arg in ipairs(args) do arg_set[arg] = true; end local body_lua = nomsu:tree_to_lua(\%body); local body_code = body_lua.statements or ("return "..body_lua.expr..";"); local undeclared_locals = {}; @@ -55,12 +59,12 @@ compile [define object %classname %class_body] to: if #undeclared_locals > 0 then body_code = "local "..table.concat(undeclared_locals, ", ")..";\\n"..body_code; end - local lua_fn_args = table.concat(stub_args[1], ", "); + local lua_fn_args = table.concat(args, ", "); local def_tree = nomsu.compilestack[#nomsu.compilestack]; local code_location = ("%s:%s,%s"):format(def_tree.filename, def_tree.start, def_tree.stop); local compiled_args = {}; - for i, arg in ipairs(stub_args[1]) do + for i, arg in ipairs(args) do compiled_args[i] = "nomsu:tree_to_lua("..arg..").expr"; end compiled_args = table.concat(compiled_args, "..', '.."); @@ -75,7 +79,7 @@ compile [define object %classname %class_body] to: ]==]):format( \%class_identifier, repr(stubs[1]), lua_fn_args, body_code, - repr(signature), repr(code_location), lua_fn_args, + repr(stubs), repr(code_location), lua_fn_args, repr(repr(stubs[1])), compiled_args, \%class_identifier, repr(stubs[1]), repr(stubs[1]))); diff --git a/lib/object2.nom b/lib/object2.nom index d27bbc0..0e8c6fb 100644 --- a/lib/object2.nom +++ b/lib/object2.nom @@ -53,7 +53,7 @@ compile [define object %classname %class_body] to: for %line in (%class_body's "value"): if: (%line's "type") is "Comment" do next %line - if: ((%line's "type") is "FunctionCall") and ((%line's "stub") is "slots %") + if: ((%line's "type") is "FunctionCall") and ((%line's stub) is "slots %") %slot_index_clauses <- [] %slot_newindex_clauses <- [] %slots <- ("value" in (2nd in (%line's "value"))) @@ -78,17 +78,21 @@ compile [define object %classname %class_body] to: error("Attempt to store data in "..repr(key)..", which is not a valid slot on "..tostring(self.class)); end do next %line - assume (((%line's "type") is "FunctionCall") and ((%line's "stub") is "action % %")) + assume (((%line's "type") is "FunctionCall") and ((%line's stub) is "action % %")) ..or barf "Only action definitions are supported inside 'define object % %', not \(%line's "src")" %actions <- (2nd in (%line's "value")) %body <- (3rd in (%line's "value")) lua> ".." - local signature = {}; - for i, action in ipairs(\%actions.value) do signature[i] = action:get_src(); end - local stubs = nomsu:get_stubs_from_signature(signature); - local stub_args = nomsu:get_args_from_signature(signature); + local stubs = {}; + for i, action in ipairs(\%actions.value) do + stubs[i] = nomsu:tree_to_named_stub(action); + end + local args = {}; + for i,tok in ipairs(\%actions.value[1].value) do + if tok.type == "Var" then args[#args+1] = nomsu:var_to_lua_identifier(tok.value); end + end local arg_set = {}; - for i, arg in ipairs(stub_args[1]) do arg_set[arg] = true; end + for i, arg in ipairs(args) do arg_set[arg] = true; end local body_lua = nomsu:tree_to_lua(\%body); local body_code = body_lua.statements or ("return "..body_lua.expr..";"); local undeclared_locals = {}; @@ -100,12 +104,12 @@ compile [define object %classname %class_body] to: if #undeclared_locals > 0 then body_code = "local "..table.concat(undeclared_locals, ", ")..";\\n"..body_code; end - local lua_fn_args = table.concat({"self", unpack(stub_args[1])}, ", "); + local lua_fn_args = table.concat({"self", unpack(args)}, ", "); local def_tree = nomsu.compilestack[#nomsu.compilestack]; local code_location = ("%s:%s,%s"):format(def_tree.filename, def_tree.start, def_tree.stop); local compiled_args = {}; - for i, arg in ipairs(stub_args[1]) do + for i, arg in ipairs(args) do compiled_args[i] = "nomsu:tree_to_lua("..arg..").expr"; end compiled_args = table.concat(compiled_args, "..', '.."); diff --git a/nomsu.lua b/nomsu.lua index 82465d1..096bf9e 100644 --- a/nomsu.lua +++ b/nomsu.lua @@ -22,6 +22,18 @@ do local _obj_0 = table insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat end +local cached +cached = function(fn) + local cache = setmetatable({ }, { + __mode = "k" + }) + return function(self, arg) + if not (cache[arg]) then + cache[arg] = fn(self, arg) + end + return cache[arg] + end +end do local STRING_METATABLE = getmetatable("") STRING_METATABLE.__add = function(self, other) @@ -119,44 +131,23 @@ do err_msg = err_msg .. "\n" .. tostring(prev_line) .. "\n" .. tostring(err_line) .. "\n" .. tostring(pointer) .. "\n" .. tostring(next_line) .. "\n" return error(err_msg) end - _with_0.FunctionCall = function(start, value, stop) - local stub = concat((function() - local _accum_0 = { } - local _len_0 = 1 - for _index_0 = 1, #value do - local t = value[_index_0] - _accum_0[_len_0] = (t.type == "Word" and t.value or "%") - _len_0 = _len_0 + 1 - end - return _accum_0 - end)(), " ") - local src = lpeg.userdata.source_code:sub(start, stop - 1) - return { - type = "FunctionCall", - start = start, - stop = stop, - value = value, - stub = stub, - filename = lpeg.userdata.filename, - get_line_no = lpeg.userdata.get_line_no, - get_src = lpeg.userdata.get_src - } - end NOMSU_DEFS = _with_0 end setmetatable(NOMSU_DEFS, { __index = function(self, key) local make_node make_node = function(start, value, stop) - return { + local node = { type = key, + value = value + } + lpeg.userdata.tree_metadata[node] = { start = start, stop = stop, - value = value, filename = lpeg.userdata.filename, - get_src = lpeg.userdata.get_src, - get_line_no = lpeg.userdata.get_line_no + source_code = lpeg.userdata.source_code } + return node end self[key] = make_node return make_node @@ -233,7 +224,7 @@ do arg_orders[stub] = arg_positions end end - self.environment.ACTION_METADATA[fn] = { + self.action_metadata[fn] = { fn = fn, source = source, aliases = stubs, @@ -244,7 +235,7 @@ do end, define_compile_action = function(self, signature, source, fn, src) self:define_action(signature, source, fn) - self.environment.ACTION_METADATA[fn].compile_time = true + self.action_metadata[fn].compile_time = true end, serialize_defs = function(self, scope, after) if scope == nil then @@ -301,36 +292,53 @@ do end return code:gsub("\n", "\n" .. (" "):rep(levels)) end, + get_line_number = cached(function(self, tree) + local metadata = self.tree_metadata[tree] + if not (metadata) then + error("Failed to find metatdata for tree: " .. tostring(tree), 0) + end + if not (self.file_metadata[metadata.filename]) then + error("Failed to find file metatdata for file: " .. tostring(metadata.filename), 0) + end + local line_starts = self.file_metadata[metadata.filename].line_starts + local first_line = 1 + while first_line < #line_starts and line_starts[first_line + 1] < metadata.start do + first_line = first_line + 1 + end + local last_line = first_line + while last_line < #line_starts and line_starts[last_line + 1] < metadata.stop do + last_line = last_line + 1 + end + return tostring(metadata.filename) .. ":" .. tostring(first_line) + end), + get_source_code = function(self, tree) + local metadata = self.tree_metadata[tree] + if not (metadata) then + return self:tree_to_nomsu(tree) + end + return self:dedent(metadata.source_code:sub(metadata.start, metadata.stop - 1)) + end, parse = function(self, nomsu_code, filename) assert(type(filename) == "string", "Bad filename type: " .. tostring(type(filename))) if self.debug then print(tostring(colored.bright("PARSING:")) .. "\n" .. tostring(colored.yellow(nomsu_code))) end - local userdata - do - local _with_0 = { + if not (self.file_metadata[filename]) then + self.file_metadata[filename] = { source_code = nomsu_code, filename = filename, - indent_stack = { - "" - } + line_starts = line_counter:match(nomsu_code) } - _with_0.get_src = function(self) - return nomsu_code:sub(self.start, self.stop - 1) - end - _with_0.line_starts = line_counter:match(_with_0.source_code) - _with_0.get_line_no = function(self) - if not (self._line_no) then - local line_no = 1 - while line_no < #_with_0.line_starts and _with_0.line_starts[line_no + 1] < self.start do - line_no = line_no + 1 - end - self._line_no = tostring(_with_0.filename) .. ":" .. tostring(line_no) - end - return self._line_no - end - userdata = _with_0 end + local userdata = { + source_code = nomsu_code, + filename = filename, + indent_stack = { + "" + }, + tree_metadata = self.tree_metadata, + line_starts = self.file_metadata[filename].line_starts + } local old_userdata old_userdata, lpeg.userdata = lpeg.userdata, userdata local tree = NOMSU:match(nomsu_code) @@ -789,7 +797,10 @@ do for _index_0 = 1, #_list_0 do local line = _list_0[_index_0] nomsu = expression(line) - assert(nomsu, "Failed to produce output for:\n" .. tostring(colored.yellow(line:get_src()))) + if not (nomsu) then + local src = self:get_source_code(line) + error("Failed to produce output for:\n" .. tostring(colored.yellow(src)), 0) + end insert(lines, nomsu) end return concat(lines, "\n") @@ -853,6 +864,9 @@ do if not tree.type then error("Invalid tree: " .. tostring(repr(tree)), 0) end + if not (self.tree_metadata[tree]) then + error("??? tree: " .. tostring(repr(tree)), 0) + end local _exp_0 = tree.type if "File" == _exp_0 then if #tree.value == 1 then @@ -906,7 +920,7 @@ do } elseif "Nomsu" == _exp_0 then return { - expr = "nomsu:parse(" .. tostring(repr(tree.value:get_src())) .. ", " .. tostring(repr(tree:get_line_no())) .. ").value[1]" + expr = "nomsu:parse(" .. tostring(repr(self:get_source_code(tree.value))) .. ", " .. tostring(repr(self:get_line_number(tree.value))) .. ").value[1]" } elseif "Block" == _exp_0 then local lua_bits = { } @@ -941,13 +955,14 @@ do } elseif "FunctionCall" == _exp_0 then insert(self.compilestack, tree) + local stub = self:tree_to_stub(tree) local ok, fn = pcall(function() - return self.environment.ACTIONS[tree.stub] + return self.environment.ACTIONS[stub] end) if not ok then fn = nil end - local metadata = self.environment.ACTION_METADATA[fn] + local metadata = self.action_metadata[fn] if metadata and metadata.compile_time then local args do @@ -968,7 +983,7 @@ do do local _accum_0 = { } local _len_0 = 1 - local _list_0 = metadata.arg_orders[tree.stub] + local _list_0 = metadata.arg_orders[stub] for _index_0 = 1, #_list_0 do local p = _list_0[_index_0] _accum_0[_len_0] = args[p] @@ -979,22 +994,22 @@ do args = new_args end if self.debug then - print(tostring(colored.bright("RUNNING MACRO")) .. " " .. tostring(colored.underscore(colored.magenta(tree.stub))) .. " ") - print(tostring(colored.bright("WITH ARGS:")) .. " " .. tostring(colored.dim(repr((function() + print(tostring(colored.bright("RUNNING MACRO")) .. " " .. tostring(colored.underscore(colored.magenta(stub))) .. " ") + print(tostring(colored.bright("WITH ARGS:")) .. " " .. tostring(colored.dim(concat((function() local _accum_0 = { } local _len_0 = 1 for _index_0 = 1, #args do local a = args[_index_0] - _accum_0[_len_0] = (repr(a)):sub(1, 50) + _accum_0[_len_0] = (repr(a)):sub(1, 100) _len_0 = _len_0 + 1 end return _accum_0 - end)())))) + end)(), ", ")))) end local lua = fn(unpack(args)) remove(self.compilestack) return lua - elseif not metadata and self.__class.math_patt:match(tree.stub) then + elseif not metadata and self.__class.math_patt:match(stub) then local bits = { } local _list_0 = tree.value for _index_0 = 1, #_list_0 do @@ -1003,7 +1018,10 @@ do insert(bits, tok.value) else local lua = self:tree_to_lua(tok) - assert(lua.expr, "non-expression value inside math expression: " .. tostring(tok:get_src())) + if not (lua.expr) then + local src = self:get_source_code(tok) + error("non-expression value inside math expression: " .. tostring(colored.yellow(src))) + end insert(bits, lua.expr) end end @@ -1023,7 +1041,11 @@ do break end local lua = self:tree_to_lua(tok) - assert(lua.expr, tostring(tree:get_line_no()) .. ": Cannot use:\n" .. tostring(colored.yellow(tok:get_src())) .. "\nas an argument to " .. tostring(tree.stub) .. ", since it's not an expression, it produces: " .. tostring(repr(lua))) + if not (lua.expr) then + local line = self:get_line_number(tok) + local src = self:get_source_code(tok) + error(tostring(line) .. ": Cannot use:\n" .. tostring(colored.yellow(src)) .. "\nas an argument to " .. tostring(stub) .. ", since it's not an expression, it produces: " .. tostring(repr(lua)), 0) + end insert(args, lua.expr) _continue_0 = true until true @@ -1036,7 +1058,7 @@ do do local _accum_0 = { } local _len_0 = 1 - local _list_1 = metadata.arg_orders[tree.stub] + local _list_1 = metadata.arg_orders[stub] for _index_0 = 1, #_list_1 do local p = _list_1[_index_0] _accum_0[_len_0] = args[p] @@ -1048,7 +1070,7 @@ do end remove(self.compilestack) return { - expr = self.__class:comma_separated_items("ACTIONS[" .. tostring(repr(tree.stub)) .. "](", args, ")") + expr = self.__class:comma_separated_items("ACTIONS[" .. tostring(repr(stub)) .. "](", args, ")") } elseif "Text" == _exp_0 then local concat_parts = { } @@ -1073,7 +1095,11 @@ do self:print_tree(bit) print(tostring(colored.bright("EXPR:")) .. " " .. tostring(lua.expr) .. ", " .. tostring(colored.bright("STATEMENT:")) .. " " .. tostring(lua.statements)) end - assert(lua.expr, "Cannot use [[" .. tostring(bit:get_src()) .. "]] as a string interpolation value, since it's not an expression.") + if not (lua.expr) then + local line = self:get_line_number(bit) + local src = self:get_source_code(bit) + error(tostring(line) .. ": Cannot use " .. tostring(colored.yellow(bit)) .. " as a string interpolation value, since it's not an expression.", 0) + end insert(concat_parts, "stringify(" .. tostring(lua.expr) .. ")") _continue_0 = true until true @@ -1103,7 +1129,11 @@ do for _index_0 = 1, #_list_0 do local item = _list_0[_index_0] local lua = self:tree_to_lua(item) - assert(lua.expr, "Cannot use [[" .. tostring(item:get_src()) .. "]] as a list item, since it's not an expression.") + if not (lua.expr) then + local line = self:get_line_number(item) + local src = self:get_source_code(item) + error(tostring(line) .. ": Cannot use " .. tostring(colored.yellow(src)) .. " as a list item, since it's not an expression.", 0) + end insert(items, lua.expr) end return { @@ -1122,9 +1152,17 @@ do else key_lua = self:tree_to_lua(entry.dict_key) end - assert(key_lua.expr, "Cannot use [[" .. tostring(entry.dict_key:get_src()) .. "]] as a dict key, since it's not an expression.") + if not (key_lua.expr) then + local line = self:get_line_number(entry.dict_key) + local src = self:get_source_code(entry.dict_key) + error(tostring(line) .. ": Cannot use " .. tostring(colored.yellow(src)) .. " as a dict key, since it's not an expression.", 0) + end local value_lua = self:tree_to_lua(entry.dict_value) - assert(value_lua.expr, "Cannot use [[" .. tostring(entry.dict_value:get_src()) .. "]] as a dict value, since it's not an expression.") + if not (value_lua.expr) then + local line = self:get_line_number(entry.dict_value) + local src = self:get_source_code(entry.dict_value) + error(tostring(line) .. ": Cannot use " .. tostring(colored.yellow(src)) .. " as a dict value, since it's not an expression.", 0) + end local key_str = key_lua.expr:match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=]) if key_str then insert(items, tostring(key_str) .. "=" .. tostring(value_lua.expr)) @@ -1215,13 +1253,16 @@ do elseif "File" == _exp_0 or "Nomsu" == _exp_0 or "Block" == _exp_0 or "List" == _exp_0 or "FunctionCall" == _exp_0 or "Text" == _exp_0 then local new_value = self:tree_with_replaced_vars(tree.value, replacements) if new_value ~= tree.value then + local new_tree do local _tbl_0 = { } for k, v in pairs(tree) do _tbl_0[k] = v end - tree = _tbl_0 + new_tree = _tbl_0 end + self.tree_metadata[new_tree] = self.tree_metadata[tree] + tree = new_tree tree.value = new_value end elseif "Dict" == _exp_0 then @@ -1237,13 +1278,16 @@ do } end if dirty then + local new_tree do local _tbl_0 = { } for k, v in pairs(tree) do _tbl_0[k] = v end - tree = _tbl_0 + new_tree = _tbl_0 end + self.tree_metadata[new_tree] = self.tree_metadata[tree] + tree = new_tree tree.value = replacements end elseif nil == _exp_0 then @@ -1259,6 +1303,38 @@ do end return tree end, + tree_to_stub = cached(function(self, tree) + if tree.type ~= "FunctionCall" then + error("Tried to get stub from non-functioncall tree: " .. tostring(tree.type), 0) + end + return concat((function() + local _accum_0 = { } + local _len_0 = 1 + local _list_0 = tree.value + for _index_0 = 1, #_list_0 do + local t = _list_0[_index_0] + _accum_0[_len_0] = (t.type == "Word" and t.value or "%") + _len_0 = _len_0 + 1 + end + return _accum_0 + end)(), " ") + end), + tree_to_named_stub = cached(function(self, tree) + if tree.type ~= "FunctionCall" then + error("Tried to get stub from non-functioncall tree: " .. tostring(tree.type), 0) + end + return concat((function() + local _accum_0 = { } + local _len_0 = 1 + local _list_0 = tree.value + for _index_0 = 1, #_list_0 do + local t = _list_0[_index_0] + _accum_0[_len_0] = (t.type == "Word" and t.value or "%" .. tostring(t.value)) + _len_0 = _len_0 + 1 + end + return _accum_0 + end)(), " ") + end), get_stubs_from_signature = function(self, signature) if type(signature) ~= 'table' or signature.type then error("Invalid signature: " .. tostring(repr(signature)), 0) @@ -1307,12 +1383,6 @@ do end end)) end, - source_code = function(self, level) - if level == nil then - level = 0 - end - return self:dedent(self.compilestack[#self.compilestack - level]:get_src()) - end, initialize_core = function(self) local get_line_no get_line_no = function() @@ -1330,7 +1400,9 @@ do else local lua = nomsu:tree_to_lua(bit) if not (lua.expr) then - error("Cannot use [[" .. tostring(bit:get_src()) .. "]] as a string interpolation value, since it's not an expression.", 0) + local line = self:get_line_number(bit) + local src = self:get_source_code(bit) + error(tostring(line) .. ": Cannot use " .. tostring(colored.yellow(src)) .. " as a string interpolation value, since it's not an expression.", 0) end insert(concat_parts, lua.expr) end @@ -1369,8 +1441,9 @@ do end) self:define_compile_action("!! code location !!", get_line_no(), function() local tree = nomsu.compilestack[#nomsu.compilestack - 1] + local metadata = self.tree_metadata[tree] return { - expr = repr(tostring(tree.filename) .. ":" .. tostring(tree.start) .. "," .. tostring(tree.stop)) + expr = repr(tostring(metadata.filename) .. ":" .. tostring(metadata.start) .. "," .. tostring(metadata.stop)) } end) self:define_action("run file %filename", get_line_no(), function(_filename) @@ -1405,6 +1478,15 @@ do }) self.use_stack = { } self.compilestack = { } + self.file_metadata = setmetatable({ }, { + __mode = "k" + }) + self.tree_metadata = setmetatable({ }, { + __mode = "k" + }) + self.action_metadata = setmetatable({ }, { + __mode = "k" + }) self.debug = false self.environment = { nomsu = self, @@ -1455,9 +1537,6 @@ do return error("Attempt to run undefined action: " .. tostring(key), 0) end }) - self.environment.ACTION_METADATA = setmetatable({ }, { - __mode = "k" - }) self.environment.LOADED = { } return self:initialize_core() end, @@ -1623,7 +1702,7 @@ if arg and debug.getinfo(2).func ~= require then end local line = nil do - local metadata = nomsu.environment.ACTION_METADATA[calling_fn.func] + local metadata = nomsu.action_metadata[calling_fn.func] if metadata then local filename, start, stop = metadata.source:match("([^:]*):([0-9]*),([0-9]*)") if filename then diff --git a/nomsu.moon b/nomsu.moon index ce4e5e2..10e57c4 100755 --- a/nomsu.moon +++ b/nomsu.moon @@ -20,6 +20,13 @@ colors = setmetatable({}, {__index:->""}) colored = setmetatable({}, {__index:(_,color)-> ((msg)-> colors[color]..(msg or '')..colors.reset)}) {:insert, :remove, :concat} = table +cached = (fn)-> + cache = setmetatable({}, {__mode:"k"}) + return (self, arg)-> + unless cache[arg] + cache[arg] = fn(self, arg) + return cache[arg] + -- Use + operator for string coercive concatenation (note: "asdf" + 3 == "asdf3") -- Use [] for accessing string characters, or s[{3,4}] for s:sub(3,4) -- Note: This globally affects all strings in this instance of Lua! @@ -107,20 +114,13 @@ NOMSU_DEFS = with {} err_msg ..="\n#{prev_line}\n#{err_line}\n#{pointer}\n#{next_line}\n" error(err_msg) - .FunctionCall = (start, value, stop)-> - stub = concat([(t.type == "Word" and t.value or "%") for t in *value], " ") - src = lpeg.userdata.source_code\sub(start,stop-1) - return { - type: "FunctionCall", :start, :stop, :value, :stub, filename:lpeg.userdata.filename, - get_line_no:lpeg.userdata.get_line_no, get_src:lpeg.userdata.get_src, - } - setmetatable(NOMSU_DEFS, {__index:(key)=> make_node = (start, value, stop)-> - { - type: key, :start, :stop, :value, filename:lpeg.userdata.filename, - get_src:lpeg.userdata.get_src, get_line_no:lpeg.userdata.get_line_no, + node = {type: key, :value} + lpeg.userdata.tree_metadata[node] = { + :start,:stop,filename:lpeg.userdata.filename,source_code:lpeg.userdata.source_code } + return node self[key] = make_node return make_node }) @@ -157,6 +157,9 @@ class NomsuCompiler }) @use_stack = {} @compilestack = {} + @file_metadata = setmetatable({}, {__mode:"k"}) + @tree_metadata = setmetatable({}, {__mode:"k"}) + @action_metadata = setmetatable({}, {__mode:"k"}) @debug = false @environment = { @@ -172,7 +175,6 @@ class NomsuCompiler @environment.ACTIONS = setmetatable({}, {__index:(key)=> error("Attempt to run undefined action: #{key}", 0) }) - @environment.ACTION_METADATA = setmetatable({}, {__mode:"k"}) @environment.LOADED = {} @initialize_core! @@ -205,14 +207,14 @@ class NomsuCompiler error("Mismatch in args between lua function's #{repr fn_arg_positions} and stub's #{repr args} for #{repr stub}", 0) arg_orders[stub] = arg_positions - @environment.ACTION_METADATA[fn] = { + @action_metadata[fn] = { :fn, :source, aliases:stubs, :arg_orders, arg_positions:fn_arg_positions, def_number:@@def_number, } define_compile_action: (signature, source, fn, src)=> @define_action(signature, source, fn) - @environment.ACTION_METADATA[fn].compile_time = true + @action_metadata[fn].compile_time = true serialize_defs: (scope=nil, after=nil)=> -- TODO: repair @@ -239,6 +241,30 @@ class NomsuCompiler indent: (code, levels=1)=> return code\gsub("\n","\n"..(" ")\rep(levels)) + get_line_number: cached (tree)=> + metadata = @tree_metadata[tree] + unless metadata + error "Failed to find metatdata for tree: #{tree}", 0 + unless @file_metadata[metadata.filename] + error "Failed to find file metatdata for file: #{metadata.filename}", 0 + line_starts = @file_metadata[metadata.filename].line_starts + first_line = 1 + while first_line < #line_starts and line_starts[first_line+1] < metadata.start + first_line += 1 + last_line = first_line + while last_line < #line_starts and line_starts[last_line+1] < metadata.stop + last_line += 1 + --return first_line == last_line and "#{metadata.filename}:#{first_line}" or "#{metadata.filename}:#{first_line}-#{last_line}" + return "#{metadata.filename}:#{first_line}" + + get_source_code: (tree)=> + -- Return the (dedented) source code of a tree, or construct some if the tree was + -- dynamically generated. + metadata = @tree_metadata[tree] + unless metadata + return @tree_to_nomsu(tree) + return @dedent metadata.source_code\sub(metadata.start, metadata.stop-1) + line_counter = re.compile([[ lines <- {| line (%nl line)* |} line <- {} (!%nl .)* @@ -248,16 +274,14 @@ class NomsuCompiler if @debug print "#{colored.bright "PARSING:"}\n#{colored.yellow nomsu_code}" - userdata = with {source_code:nomsu_code, :filename, indent_stack: {""}} - .get_src = => nomsu_code\sub(@start, @stop-1) - .line_starts = line_counter\match(.source_code) - .get_line_no = => - unless @_line_no - line_no = 1 - while line_no < #.line_starts and .line_starts[line_no+1] < @start - line_no += 1 - @_line_no = "#{.filename}:#{line_no}" - return @_line_no + unless @file_metadata[filename] + @file_metadata[filename] = { + source_code:nomsu_code, :filename, line_starts:line_counter\match(nomsu_code) + } + userdata = { + source_code:nomsu_code, :filename, indent_stack: {""}, tree_metadata:@tree_metadata, + line_starts:@file_metadata[filename].line_starts, + } old_userdata, lpeg.userdata = lpeg.userdata, userdata tree = NOMSU\match(nomsu_code) @@ -569,7 +593,9 @@ class NomsuCompiler lines = {} for line in *tree.value nomsu = expression(line) - assert nomsu, "Failed to produce output for:\n#{colored.yellow line\get_src!}" + unless nomsu + src = @get_source_code line + error "Failed to produce output for:\n#{colored.yellow src}", 0 insert lines, nomsu return concat lines, "\n" @@ -614,6 +640,8 @@ class NomsuCompiler assert tree, "No tree provided." if not tree.type error("Invalid tree: #{repr(tree)}", 0) + unless @tree_metadata[tree] + error "??? tree: #{repr tree}", 0 switch tree.type when "File" if #tree.value == 1 @@ -638,7 +666,7 @@ class NomsuCompiler return statements:"--"..tree.value\gsub("\n","\n--") when "Nomsu" - return expr:"nomsu:parse(#{repr tree.value\get_src!}, #{repr tree\get_line_no!}).value[1]" + return expr:"nomsu:parse(#{repr @get_source_code(tree.value)}, #{repr @get_line_number(tree.value)}).value[1]" when "Block" lua_bits = {} @@ -657,22 +685,23 @@ class NomsuCompiler when "FunctionCall" insert @compilestack, tree - ok, fn = pcall(-> @environment.ACTIONS[tree.stub]) + stub = @tree_to_stub tree + ok, fn = pcall(-> @environment.ACTIONS[stub]) if not ok then fn = nil - metadata = @environment.ACTION_METADATA[fn] + metadata = @action_metadata[fn] if metadata and metadata.compile_time args = [arg for arg in *tree.value when arg.type != "Word"] if metadata and metadata.arg_orders - new_args = [args[p] for p in *metadata.arg_orders[tree.stub]] + new_args = [args[p] for p in *metadata.arg_orders[stub]] args = new_args if @debug - print "#{colored.bright "RUNNING MACRO"} #{colored.underscore colored.magenta(tree.stub)} " - print "#{colored.bright "WITH ARGS:"} #{colored.dim repr [(repr a)\sub(1,50) for a in *args]}" + print "#{colored.bright "RUNNING MACRO"} #{colored.underscore colored.magenta(stub)} " + print "#{colored.bright "WITH ARGS:"} #{colored.dim concat([(repr a)\sub(1,100) for a in *args], ", ")}" lua = fn(unpack(args)) remove @compilestack return lua - elseif not metadata and @@math_patt\match(tree.stub) + elseif not metadata and @@math_patt\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. @@ -682,7 +711,9 @@ class NomsuCompiler insert bits, tok.value else lua = @tree_to_lua(tok) - assert(lua.expr, "non-expression value inside math expression: #{tok\get_src!}") + unless lua.expr + src = @get_source_code(tok) + error("non-expression value inside math expression: #{colored.yellow src}") insert bits, lua.expr remove @compilestack return expr:"(#{concat bits, " "})" @@ -691,16 +722,18 @@ class NomsuCompiler for tok in *tree.value if tok.type == "Word" then continue lua = @tree_to_lua(tok) - assert lua.expr, - "#{tree\get_line_no!}: Cannot use:\n#{colored.yellow tok\get_src!}\nas an argument to #{tree.stub}, since it's not an expression, it produces: #{repr lua}" + unless lua.expr + line = @get_line_number(tok) + src = @get_source_code(tok) + error "#{line}: Cannot use:\n#{colored.yellow src}\nas an argument to #{stub}, since it's not an expression, it produces: #{repr lua}", 0 insert args, lua.expr if metadata and metadata.arg_orders - new_args = [args[p] for p in *metadata.arg_orders[tree.stub]] + new_args = [args[p] for p in *metadata.arg_orders[stub]] args = new_args remove @compilestack - return expr:@@comma_separated_items("ACTIONS[#{repr tree.stub}](", args, ")") + return expr:@@comma_separated_items("ACTIONS[#{repr stub}](", args, ")") when "Text" concat_parts = {} @@ -717,8 +750,10 @@ class NomsuCompiler print(colored.bright "INTERP:") @print_tree bit print "#{colored.bright "EXPR:"} #{lua.expr}, #{colored.bright "STATEMENT:"} #{lua.statements}" - assert lua.expr, - "Cannot use [[#{bit\get_src!}]] as a string interpolation value, since it's not an expression." + unless lua.expr + line = @get_line_number(bit) + src = @get_source_code(bit) + error "#{line}: Cannot use #{colored.yellow bit} as a string interpolation value, since it's not an expression.", 0 insert concat_parts, "stringify(#{lua.expr})" if string_buffer ~= "" @@ -734,8 +769,10 @@ class NomsuCompiler items = {} for item in *tree.value lua = @tree_to_lua item - assert lua.expr, - "Cannot use [[#{item\get_src!}]] as a list item, since it's not an expression." + unless lua.expr + line = @get_line_number(item) + src = @get_source_code(item) + error "#{line}: Cannot use #{colored.yellow src} as a list item, since it's not an expression.", 0 insert items, lua.expr return expr:@@comma_separated_items("{", items, "}") @@ -746,11 +783,15 @@ class NomsuCompiler {expr:repr(entry.dict_key.value)} else @tree_to_lua entry.dict_key - assert key_lua.expr, - "Cannot use [[#{entry.dict_key\get_src!}]] as a dict key, since it's not an expression." + unless key_lua.expr + line = @get_line_number(entry.dict_key) + src = @get_source_code(entry.dict_key) + error "#{line}: Cannot use #{colored.yellow src} as a dict key, since it's not an expression.", 0 value_lua = @tree_to_lua entry.dict_value - assert value_lua.expr, - "Cannot use [[#{entry.dict_value\get_src!}]] as a dict value, since it's not an expression." + unless value_lua.expr + line = @get_line_number(entry.dict_value) + src = @get_source_code(entry.dict_value) + error "#{line}: Cannot use #{colored.yellow src} as a dict value, since it's not an expression.", 0 key_str = key_lua.expr\match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=]) if key_str insert items, "#{key_str}=#{value_lua.expr}" @@ -828,7 +869,10 @@ class NomsuCompiler when "File", "Nomsu", "Block", "List", "FunctionCall", "Text" new_value = @tree_with_replaced_vars tree.value, replacements if new_value != tree.value - tree = {k,v for k,v in pairs(tree)} + new_tree = {k,v for k,v in pairs(tree)} + -- TODO: Maybe generate new metadata? + @tree_metadata[new_tree] = @tree_metadata[tree] + tree = new_tree tree.value = new_value when "Dict" dirty = false @@ -839,7 +883,10 @@ class NomsuCompiler dirty or= new_key != e.dict_key or new_value != e.dict_value replacements[i] = {dict_key:new_key, dict_value:new_value} if dirty - tree = {k,v for k,v in pairs(tree)} + new_tree = {k,v for k,v in pairs(tree)} + -- TODO: Maybe generate new metadata? + @tree_metadata[new_tree] = @tree_metadata[tree] + tree = new_tree tree.value = replacements when nil -- Raw table, probably from one of the .value of a multi-value tree (e.g. List) new_values = {} @@ -851,6 +898,13 @@ class NomsuCompiler tree = new_values return tree + tree_to_stub: cached (tree)=> + if tree.type != "FunctionCall" then error "Tried to get stub from non-functioncall tree: #{tree.type}", 0 + return concat([(t.type == "Word" and t.value or "%") for t in *tree.value], " ") + + tree_to_named_stub: cached (tree)=> + if tree.type != "FunctionCall" then error "Tried to get stub from non-functioncall tree: #{tree.type}", 0 + return concat([(t.type == "Word" and t.value or "%#{t.value}") for t in *tree.value], " ") stub_defs = { space:(P(' ') + P('\n..'))^0 @@ -893,9 +947,6 @@ class NomsuCompiler "_"..(var\gsub "%W", (verboten)-> if verboten == "_" then "__" else ("_%x")\format(verboten\byte!)) - source_code: (level=0)=> - @dedent @compilestack[#@compilestack-level]\get_src! - initialize_core: => -- Sets up some core functionality get_line_no = -> "nomsu.moon:#{debug.getinfo(2).currentline}" @@ -908,7 +959,9 @@ class NomsuCompiler else lua = nomsu\tree_to_lua bit unless lua.expr - error("Cannot use [[#{bit\get_src!}]] as a string interpolation value, since it's not an expression.", 0) + line = @get_line_number(bit) + src = @get_source_code(bit) + error "#{line}: Cannot use #{colored.yellow src} as a string interpolation value, since it's not an expression.", 0 insert concat_parts, lua.expr return concat(concat_parts) @@ -933,7 +986,8 @@ class NomsuCompiler @define_compile_action "!! code location !!", get_line_no!, -> tree = nomsu.compilestack[#nomsu.compilestack-1] - return expr: repr("#{tree.filename}:#{tree.start},#{tree.stop}") + metadata = @tree_metadata[tree] + return expr: repr("#{metadata.filename}:#{metadata.start},#{metadata.stop}") @define_action "run file %filename", get_line_no!, (_filename)-> return nomsu\run_file(_filename) @@ -1035,7 +1089,7 @@ if arg and debug.getinfo(2).func != require name = calling_fn.name if name == "run_lua_fn" then continue line = nil - if metadata = nomsu.environment.ACTION_METADATA[calling_fn.func] + if metadata = nomsu.action_metadata[calling_fn.func] filename, start, stop = metadata.source\match("([^:]*):([0-9]*),([0-9]*)") if filename file = io.open(filename)\read("*a")