diff --git a/nomsu.lua b/nomsu.lua index 4d80357..ca007e6 100644 --- a/nomsu.lua +++ b/nomsu.lua @@ -235,7 +235,12 @@ setmetatable(NOMSU_DEFS, { __index = function(self, key) local make_node make_node = function(src, ...) - return Types[key](...) + local tree = Types[key](...) + insert(lpeg.userdata.depth_first_sources, { + src, + tree + }) + return tree end self[key] = make_node return make_node @@ -316,7 +321,8 @@ do indent_stack = { "" }, - errors = { } + errors = { }, + depth_first_sources = { } } local old_userdata old_userdata, lpeg.userdata = lpeg.userdata, userdata @@ -339,7 +345,25 @@ do end error(concat(errors, "\n\n"), 0) end - return tree + local src_map = { } + local src_i = 1 + local walk_tree + walk_tree = function(tree, path) + if tree.is_multi then + for i, v in ipairs(tree) do + if Types.is_node(v) then + walk_tree(v, Tuple(i, path)) + end + end + end + local src, t2 = unpack(userdata.depth_first_sources[src_i]) + src_i = src_i + 1 + assert(t2 == tree) + src_map[path] = src + end + walk_tree(tree, Tuple()) + assert(src_i == #userdata.depth_first_sources + 1) + return tree, src_map end, run = function(self, nomsu_code, compile_fn) if compile_fn == nil then @@ -441,7 +465,10 @@ do end return run_lua_fn() end, - tree_to_lua = function(self, tree) + tree_to_lua = function(self, tree, path) + if path == nil then + path = Tuple() + end local _exp_0 = tree.type if "Action" == _exp_0 then local stub = tree:get_stub() @@ -484,7 +511,7 @@ do if tok.type == "Word" then lua:append(tok.value) else - local tok_lua = self:tree_to_lua(tok) + local tok_lua = self:tree_to_lua(tok, Tuple(i, path)) if not (tok_lua.is_value) then error("non-expression value inside math expression: " .. tostring(colored.yellow(repr(tok)))) end @@ -507,7 +534,7 @@ do _continue_0 = true break end - local arg_lua = self:tree_to_lua(tok) + local arg_lua = self:tree_to_lua(tok, Tuple(i, path)) if not (arg_lua.is_value) then error("Cannot use:\n" .. tostring(colored.yellow(repr(tok))) .. "\nas an argument to " .. tostring(stub) .. ", since it's not an expression, it produces: " .. tostring(repr(arg_lua)), 0) end @@ -560,14 +587,14 @@ do end return t.type .. "(" .. table.concat(bits, ", ") .. ")" else - return t.type .. "(" .. make_tree(t.value) .. ")" + return t.type .. "(" .. make_tree(t[1]) .. ")" end end - return Lua.Value(nil, make_tree(tree.value)) + return Lua.Value(nil, make_tree(tree[1])) elseif "Block" == _exp_0 then local lua = Lua() for i, line in ipairs(tree) do - local line_lua = self:tree_to_lua(line) + local line_lua = self:tree_to_lua(line, Tuple(i, path)) if i > 1 then lua:append("\n") end @@ -577,10 +604,9 @@ do elseif "Text" == _exp_0 then local lua = Lua.Value() local string_buffer = "" - for _index_0 = 1, #tree do + for i, bit in ipairs(tree) do local _continue_0 = false repeat - local bit = tree[_index_0] if type(bit) == "string" then string_buffer = string_buffer .. bit _continue_0 = true @@ -593,7 +619,7 @@ do lua:append(repr(string_buffer)) string_buffer = "" end - local bit_lua = self:tree_to_lua(bit) + local bit_lua = self:tree_to_lua(bit, Tuple(i, path)) if not (bit_lua.is_value) then error("Cannot use " .. tostring(colored.yellow(repr(bit))) .. " as a string interpolation value, since it's not an expression.", 0) end @@ -624,7 +650,7 @@ do local lua = Lua.Value(nil, "{") local line_length = 0 for i, item in ipairs(tree) do - local item_lua = self:tree_to_lua(item) + local item_lua = self:tree_to_lua(item, Tuple(i, path)) if not (item_lua.is_value) then error("Cannot use " .. tostring(colored.yellow(repr(item))) .. " as a list item, since it's not an expression.", 0) end @@ -652,7 +678,7 @@ do local lua = Lua.Value(nil, "{") local line_length = 0 for i, entry in ipairs(tree) do - local entry_lua = self:tree_to_lua(entry) + local entry_lua = self:tree_to_lua(entry, Tuple(i, path)) lua:append(entry_lua) local entry_lua_str = tostring(entry_lua) local last_line = entry_lua_str:match("\n([^\n]*)$") @@ -675,11 +701,11 @@ do return lua elseif "DictEntry" == _exp_0 then local key, value = tree[1], tree[2] - local key_lua = self:tree_to_lua(key) + local key_lua = self:tree_to_lua(key, Tuple(1, path)) if not (key_lua.is_value) then error("Cannot use " .. tostring(colored.yellow(repr(key))) .. " as a dict key, since it's not an expression.", 0) end - local value_lua = value and self:tree_to_lua(value) or Lua.Value(nil, "true") + local value_lua = value and self:tree_to_lua(value, Tuple(2, path)) or Lua.Value(nil, "true") if not (value_lua.is_value) then error("Cannot use " .. tostring(colored.yellow(repr(value))) .. " as a dict value, since it's not an expression.", 0) end @@ -692,7 +718,7 @@ do return Lua(nil, "[", key_lua, "]=", value_lua) end elseif "IndexChain" == _exp_0 then - local lua = self:tree_to_lua(tree[1]) + local lua = self:tree_to_lua(tree[1], Tuple(1, path)) if not (lua.is_value) then error("Cannot index " .. tostring(colored.yellow(repr(tree[1]))) .. ", since it's not an expression.", 0) end @@ -702,7 +728,7 @@ do end for i = 2, #tree do local key = tree[i] - local key_lua = self:tree_to_lua(key) + local key_lua = self:tree_to_lua(key, Tuple(i, path)) if not (key_lua.is_value) then error("Cannot use " .. tostring(colored.yellow(repr(key))) .. " as an index, since it's not an expression.", 0) end @@ -822,7 +848,7 @@ do elseif "EscapedNomsu" == _exp_0 then local nomsu = self:tree_to_nomsu(tree.value, true) if nomsu == nil and not inline then - nomsu = self:tree_to_nomsu(tree.value) + nomsu = self:tree_to_nomsu(tree[1]) return nomsu and Nomsu(nil, "\\:\n ", nomsu) end return nomsu and Nomsu(nil, "\\(", nomsu, ")") @@ -1070,16 +1096,13 @@ do depth = 0 end coroutine.yield(tree, depth) - if not (Types.is_node(tree)) then - return - end if tree.is_multi then for _index_0 = 1, #tree do local v = tree[_index_0] - self:walk_tree(v, depth + 1) + if Types.is_node(v) then + self:walk_tree(v, depth + 1) + end end - else - return self:walk_tree(v, depth + 1) end end, initialize_core = function(self) diff --git a/nomsu.moon b/nomsu.moon index d8ae4e0..7648998 100755 --- a/nomsu.moon +++ b/nomsu.moon @@ -201,7 +201,9 @@ NOMSU_DEFS = with {} setmetatable(NOMSU_DEFS, {__index:(key)=> make_node = (src, ...)-> - Types[key](...) + tree = Types[key](...) + insert lpeg.userdata.depth_first_sources, {src, tree} + return tree self[key] = make_node return make_node }) @@ -327,6 +329,7 @@ class NomsuCompiler nomsu_code = Nomsu(filename, nomsu_code) userdata = { source_code:nomsu_code, indent_stack: {""}, errors: {}, + depth_first_sources: {}, } old_userdata, lpeg.userdata = lpeg.userdata, userdata @@ -340,8 +343,22 @@ class NomsuCompiler table.sort(keys) errors = [userdata.errors[k] for k in *keys] error(concat(errors, "\n\n"), 0) - - return tree + + src_map = {} + src_i = 1 + walk_tree = (tree, path)-> + if tree.is_multi + for i, v in ipairs tree + if Types.is_node(v) + walk_tree(v, Tuple(i, path)) + src, t2 = unpack(userdata.depth_first_sources[src_i]) + src_i += 1 + assert t2 == tree + src_map[path] = src + walk_tree tree, Tuple! + assert src_i == #userdata.depth_first_sources + 1 + + return tree, src_map run: (nomsu_code, compile_fn=nil)=> if #tostring(nomsu_code) == 0 then return nil @@ -410,7 +427,7 @@ class NomsuCompiler MAX_LINE = 80 -- For beautification purposes, try not to make lines much longer than this value math_expression = re.compile [[ ([+-] " ")* "%" (" " [*/^+-] (" " [+-])* " %")+ !. ]] - tree_to_lua: (tree)=> + tree_to_lua: (tree, path=Tuple!)=> switch tree.type when "Action" stub = tree\get_stub! @@ -420,6 +437,7 @@ class NomsuCompiler -- Force all compile-time actions to take a tree location args = [args[p-1] for p in *@environment.ARG_ORDERS[compile_action][stub]] -- Force Lua to avoid tail call optimization for debugging purposes + -- TODO: use tail call ret = compile_action(tree, unpack(args)) if not ret then error("Failed to produce any Lua") return ret @@ -433,7 +451,7 @@ class NomsuCompiler if tok.type == "Word" lua\append tok.value else - tok_lua = @tree_to_lua(tok) + tok_lua = @tree_to_lua(tok, Tuple(i, path)) unless tok_lua.is_value error("non-expression value inside math expression: #{colored.yellow repr(tok)}") if tok.type == "Action" @@ -446,7 +464,7 @@ class NomsuCompiler args = {} for i, tok in ipairs tree if tok.type == "Word" then continue - arg_lua = @tree_to_lua(tok) + arg_lua = @tree_to_lua(tok, Tuple(i, path)) unless arg_lua.is_value error "Cannot use:\n#{colored.yellow repr(tok)}\nas an argument to #{stub}, since it's not an expression, it produces: #{repr arg_lua}", 0 insert args, arg_lua @@ -471,13 +489,13 @@ class NomsuCompiler bits = [make_tree(bit) for bit in *t] return t.type.."("..table.concat(bits, ", ")..")" else - return t.type.."("..make_tree(t.value)..")" - Lua.Value nil, make_tree(tree.value) + return t.type.."("..make_tree(t[1])..")" + Lua.Value nil, make_tree(tree[1]) when "Block" lua = Lua! for i,line in ipairs tree - line_lua = @tree_to_lua(line) + line_lua = @tree_to_lua(line, Tuple(i, path)) if i > 1 lua\append "\n" lua\append line_lua\as_statements! @@ -486,7 +504,7 @@ class NomsuCompiler when "Text" lua = Lua.Value! string_buffer = "" - for bit in *tree + for i, bit in ipairs tree if type(bit) == "string" string_buffer ..= bit continue @@ -494,7 +512,7 @@ class NomsuCompiler if #lua.bits > 0 then lua\append ".." lua\append repr(string_buffer) string_buffer = "" - bit_lua = @tree_to_lua(bit) + bit_lua = @tree_to_lua(bit, Tuple(i, path)) unless bit_lua.is_value error "Cannot use #{colored.yellow repr(bit)} as a string interpolation value, since it's not an expression.", 0 if #lua.bits > 0 then lua\append ".." @@ -514,7 +532,7 @@ class NomsuCompiler lua = Lua.Value nil, "{" line_length = 0 for i, item in ipairs tree - item_lua = @tree_to_lua(item) + item_lua = @tree_to_lua(item, Tuple(i, path)) unless item_lua.is_value error "Cannot use #{colored.yellow repr(item)} as a list item, since it's not an expression.", 0 lua\append item_lua @@ -538,7 +556,7 @@ class NomsuCompiler lua = Lua.Value nil, "{" line_length = 0 for i, entry in ipairs tree - entry_lua = @tree_to_lua(entry) + entry_lua = @tree_to_lua(entry, Tuple(i, path)) lua\append entry_lua entry_lua_str = tostring(entry_lua) -- TODO: maybe make this more accurate? It's only a heuristic, so eh... @@ -559,10 +577,10 @@ class NomsuCompiler when "DictEntry" key, value = tree[1], tree[2] - key_lua = @tree_to_lua(key) + key_lua = @tree_to_lua(key, Tuple(1, path)) unless key_lua.is_value error "Cannot use #{colored.yellow repr(key)} as a dict key, since it's not an expression.", 0 - value_lua = value and @tree_to_lua(value) or Lua.Value(nil, "true") + value_lua = value and @tree_to_lua(value, Tuple(2, path)) or Lua.Value(nil, "true") unless value_lua.is_value error "Cannot use #{colored.yellow repr(value)} as a dict value, since it's not an expression.", 0 key_str = tostring(key_lua)\match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=]) @@ -577,7 +595,7 @@ class NomsuCompiler Lua nil, "[",key_lua,"]=",value_lua when "IndexChain" - lua = @tree_to_lua(tree[1]) + lua = @tree_to_lua(tree[1], Tuple(1, path)) unless lua.is_value error "Cannot index #{colored.yellow repr(tree[1])}, since it's not an expression.", 0 first_char = tostring(lua)\sub(1,1) @@ -586,7 +604,7 @@ class NomsuCompiler for i=2,#tree key = tree[i] - key_lua = @tree_to_lua(key) + key_lua = @tree_to_lua(key, Tuple(i, path)) unless key_lua.is_value error "Cannot use #{colored.yellow repr(key)} as an index, since it's not an expression.", 0 key_lua_str = tostring(key_lua) @@ -684,7 +702,7 @@ class NomsuCompiler when "EscapedNomsu" nomsu = @tree_to_nomsu(tree.value, true) if nomsu == nil and not inline - nomsu = @tree_to_nomsu(tree.value) + nomsu = @tree_to_nomsu(tree[1]) return nomsu and Nomsu nil, "\\:\n ", nomsu return nomsu and Nomsu nil, "\\(", nomsu, ")" @@ -866,12 +884,10 @@ class NomsuCompiler walk_tree: (tree, depth=0)=> coroutine.yield(tree, depth) - return unless Types.is_node(tree) if tree.is_multi for v in *tree - @walk_tree(v, depth+1) - else - @walk_tree(v, depth+1) + if Types.is_node(v) + @walk_tree(v, depth+1) initialize_core: => -- Sets up some core functionality diff --git a/nomsu_tree.lua b/nomsu_tree.lua index f4f10b2..4bacdb5 100644 --- a/nomsu_tree.lua +++ b/nomsu_tree.lua @@ -57,7 +57,7 @@ Tree = function(name, kind, methods) return _accum_0 end)(), ', ')) .. ")" end - methods.map = function(self, fn) + methods._map = function(self, fn) do local ret = fn(self) if ret then @@ -70,7 +70,7 @@ Tree = function(name, kind, methods) local _len_0 = 1 for _index_0 = 1, #self do local v = self[_index_0] - _accum_0[_len_0] = v.map and v:map(fn) or v + _accum_0[_len_0] = v._map and v:_map(fn) or v _len_0 = _len_0 + 1 end new_vals = _accum_0 @@ -82,7 +82,7 @@ Tree = function(name, kind, methods) methods.__tostring = function(self) return tostring(self.name) .. "(" .. tostring(repr(self.value)) .. ")" end - methods.map = function(self, fn) + methods._map = function(self, fn) return fn(self) or self end end @@ -96,6 +96,7 @@ Tree = function(name, kind, methods) end end Tree("Block", 'multi') +Tree("EscapedNomsu", 'multi') Tree("Text", 'multi') Tree("List", 'multi') Tree("Dict", 'multi') @@ -104,11 +105,6 @@ Tree("IndexChain", 'multi') Tree("Number", 'single') Tree("Word", 'single') Tree("Comment", 'single') -Tree("EscapedNomsu", 'single', { - map = function(self, fn) - return fn(self) or self:map(fn) - end -}) Tree("Var", 'single', { as_lua_id = function(self) return "_" .. (self.value:gsub("%W", function(c) diff --git a/nomsu_tree.moon b/nomsu_tree.moon index c15d29c..2f7c202 100644 --- a/nomsu_tree.moon +++ b/nomsu_tree.moon @@ -32,15 +32,15 @@ Tree = (name, kind, methods)-> return @_map(fn) if is_multi .__tostring = => "#{@name}(#{table.concat [repr(v) for v in *@], ', '})" - .map = (fn)=> + ._map = (fn)=> if ret = fn(@) return ret - new_vals = [v.map and v\map(fn) or v for v in *@] + new_vals = [v._map and v\_map(fn) or v for v in *@] ret = getmetatable(self)(unpack(new_vals)) return ret else .__tostring = => "#{@name}(#{repr(@value)})" - .map = (fn)=> + ._map = (fn)=> fn(@) or @ if is_multi @@ -49,6 +49,7 @@ Tree = (name, kind, methods)-> Types[name] = immutable {"value"}, methods Tree "Block", 'multi' +Tree "EscapedNomsu", 'multi' Tree "Text", 'multi' Tree "List", 'multi' Tree "Dict", 'multi' @@ -58,9 +59,6 @@ Tree "Number", 'single' Tree "Word", 'single' Tree "Comment", 'single' -Tree "EscapedNomsu", 'single', - map: (fn)=> fn(@) or @\map(fn) - Tree "Var", 'single', as_lua_id: => "_"..(@value\gsub("%W", (c)-> if c == "_" then "__" else ("_%x")\format(c\byte!)))