From 02af19153eff6dad434d2c1c527e33d078e3dfab Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Thu, 8 Feb 2018 16:22:57 -0800 Subject: Moved over to have tree metadata stored outside the tree. This paves the way for programmatically generating trees more easily. --- nomsu.lua | 243 +++++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 161 insertions(+), 82 deletions(-) (limited to 'nomsu.lua') 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 -- cgit v1.2.3