diff options
| -rw-r--r-- | core/metaprogramming.nom | 4 | ||||
| -rw-r--r-- | lua_obj.lua | 243 | ||||
| -rw-r--r-- | lua_obj.moon | 139 | ||||
| -rw-r--r-- | nomsu.lua | 83 | ||||
| -rwxr-xr-x | nomsu.moon | 74 | ||||
| -rw-r--r-- | nomsu_tree.moon | 2 |
6 files changed, 393 insertions, 152 deletions
diff --git a/core/metaprogramming.nom b/core/metaprogramming.nom index 1b65890..011d44d 100644 --- a/core/metaprogramming.nom +++ b/core/metaprogramming.nom @@ -78,13 +78,13 @@ immediately: for i, line in ipairs(\%longhand.value) do lines[i] = line.source:get_text(); end template = repr(table.concat(lines, "\\n")); else - template = repr(\%longhand.source:get_text()); + template = repr(tostring(\%longhand.source:get_text())); end local replacements = {}; for i, a in ipairs(args) do replacements[i] = a.."="..a; end replacements = "{"..table.concat(replacements, ", ").."}"; lua:append([[) - local template = nomsu:parse(]]..template..[[, ]]..repr(tree.source.filename)..[[); + local template = nomsu:parse(Nomsu(]]..repr(tree.source)..[[, ]]..template..[[)); local replacement = nomsu:tree_with_replaced_vars(template, ]]..replacements..[[); return replacement:as_lua(nomsu); end); diff --git a/lua_obj.lua b/lua_obj.lua index ba8277c..eca0bf1 100644 --- a/lua_obj.lua +++ b/lua_obj.lua @@ -4,23 +4,30 @@ do insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat end local immutable = require('immutable') -local Lua, Location -Location = immutable({ +local Lua, Source +Source = immutable({ "filename", "start", "stop" }, { - name = "Location", + name = "Source", __new = function(self, filename, start, stop) - return filename, start, stop or start + if stop then + assert(start <= stop, "Invalid range: " .. tostring(start) .. ", " .. tostring(stop)) + end + return filename, start, stop end, __tostring = function(self) - return "Location(\"" .. tostring(self.filename) .. "\", " .. tostring(self.start) .. ", " .. tostring(self.stop) .. ")" + if self.stop then + return "Source(\"" .. tostring(self.filename) .. "\", " .. tostring(self.start) .. ", " .. tostring(self.stop) .. ")" + else + return "Source(\"" .. tostring(self.filename) .. "\", " .. tostring(self.start) .. ")" + end end, __lt = function(self, other) assert(self.filename == other.filename, "Cannot compare sources from different files") if self.start == other.start then - return self.stop < other.stop + return (self.stop or self.start) < (other.stop or other.start) else return self.start < other.start end @@ -28,11 +35,36 @@ Location = immutable({ __le = function(self, other) assert(self.filename == other.filename, "Cannot compare sources from different files") if self.start == other.start then - return self.stop <= other.stop + return (self.stop or self.start) <= (other.stop or other.start) else return self.start <= other.start end end, + __add = function(self, offset) + if type(self) == 'number' then + offset, self = self, offset + else + assert(type(offset) == 'number', "Cannot add Source and " .. tostring(type(offset))) + end + return Source(self.filename, self.start + offset, self.stop) + end, + sub = function(self, start, stop) + if not self.stop then + assert(not stop, "cannot subscript non-range with range") + assert(start > 0, "cannot subscript non-range with negative index") + return Source(self.filename, self.start + (start or 0)) + else + start = start or 1 + if start < 0 then + start = self.stop + start + 1 + end + stop = stop or -1 + if stop < 0 then + stop = self.stop + stop + 1 + end + return Source(self.filename, self.start + start - 1, self.start + stop - 1) + end + end, get_text = function(self) return FILE_CACHE[self.filename]:sub(self.start, self.stop) end, @@ -61,19 +93,88 @@ Location = immutable({ end end }) +local Code do local _class_0 local _base_0 = { clone = function(self) - local copy = Lua(self.source, { - unpack(self.bits) - }) + local cls = self.__class + local copy = cls(self.source, unpack(self.bits)) copy.is_value = self.is_value for k, v in pairs(self.free_vars) do copy.free_vars[k] = v end return copy end, + __tostring = function(self) + local buff = { } + for i, b in ipairs(self.bits) do + buff[#buff + 1] = tostring(b) + end + local ret = concat(buff, "") + return ret + end, + __len = function(self) + local len = 0 + local _list_0 = self.bits + for _index_0 = 1, #_list_0 do + local b = _list_0[_index_0] + len = len + #b + end + return len + end, + sub = function(self, start, stop) + local str = tostring(self):sub(start, stop) + local cls = self.__class + return cls(self.source:sub(start - self.source.start + 1, stop - self.source.stop + 1), str) + end, + append = function(self, ...) + local n = select("#", ...) + local bits = self.bits + for i = 1, n do + bits[#bits + 1] = select(i, ...) + end + end, + prepend = function(self, ...) + local n = select("#", ...) + local bits = self.bits + for i = #bits + n, n + 1, -1 do + bits[i] = bits[i - n] + end + for i = 1, n do + bits[i] = select(i, ...) + end + end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function(self, source, ...) + self.source = source + if type(self.source) == 'string' then + local filename, start, stop = self.source:match("^(.-)[(%d+):(%d+)]$") + self.source = Source(filename, tonumber(start), tonumber(stop)) + end + self.bits = { + ... + } + end, + __base = _base_0, + __name = "Code" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + Code = _class_0 +end +do + local _class_0 + local _parent_0 = Code + local _base_0 = { add_free_vars = function(self, ...) local seen do @@ -150,9 +251,6 @@ do local buff = { } for i, b in ipairs(self.bits) do buff[#buff + 1] = tostring(b) - if i < #self.bits and type(b) ~= 'string' and not b.is_value then - buff[#buff + 1] = "\n" - end end local ret = concat(buff, "") return ret @@ -166,30 +264,33 @@ do end return len end, - __add = function(self, other) - return Lua(nil, self, other) - end, - __concat = function(self, other) - return Lua(nil, self, other) - end, append = function(self, ...) local n = select("#", ...) local bits = self.bits for i = 1, n do - bits[#bits + 1] = select(i, ...) + local bit = select(i, ...) + bits[#bits + 1] = bit + if type(bit) ~= 'string' and not bit.is_value and #self.bits > 0 then + bits[#bits + 1] = "\n" + end end end, prepend = function(self, ...) local n = select("#", ...) local bits = self.bits - for i = #bits + n, n + 1, -1 do - bits[i] = bits[i - n] - end + local insert_index = 1 for i = 1, n do - bits[i] = select(i, ...) + local bit = select(i, ...) + insert(bits, insert_index, bit) + insert_index = insert_index + 1 + if type(bit) ~= 'string' and not bit.is_value and insert_index < #self.bits + 1 then + insert(bits, insert_index, "\n") + insert_index = insert_index + 1 + end end end, - make_offset_table = function(self, lua_chunkname) + make_offset_table = function(self) + local lua_chunkname = tostring(self.source) .. ".lua" local lua_str = tostring(self) local metadata = { nomsu_filename = self.source.filename, @@ -211,7 +312,7 @@ do walk(b) end local lua_stop = lua_offset - local nomsu_src, lua_src = lua.source, Location(lua_chunkname, lua_start, lua_stop) + local nomsu_src, lua_src = lua.source, Source(lua_chunkname, lua_start, lua_stop) metadata.lua_to_nomsu[lua_src] = nomsu_src metadata.nomsu_to_lua[nomsu_src] = lua_src end @@ -229,23 +330,28 @@ do end } _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) _class_0 = setmetatable({ - __init = function(self, source, ...) - self.source = source - if type(self.source) == 'string' then - local filename, start, stop = self.source:match("^(.-)[(%d+):(%d+)]$") - self.source = Location(filename, tonumber(start), tonumber(stop)) - end - self.bits = { - ... - } + __init = function(self, ...) + _class_0.__parent.__init(self, ...) self.free_vars = { } self.is_value = false end, __base = _base_0, - __name = "Lua" + __name = "Lua", + __parent = _parent_0 }, { - __index = _base_0, + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, __call = function(cls, ...) local _self_0 = setmetatable({}, _base_0) cls.__init(_self_0, ...) @@ -259,9 +365,70 @@ do lua.is_value = true return lua end + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end Lua = _class_0 end +local Nomsu +do + local _class_0 + local _parent_0 = Code + local _base_0 = { + __tostring = function(self) + local buff = { } + for i, b in ipairs(self.bits) do + buff[#buff + 1] = tostring(b) + end + local ret = concat(buff, "") + return ret + end, + __len = function(self) + local len = 0 + local _list_0 = self.bits + for _index_0 = 1, #_list_0 do + local b = _list_0[_index_0] + len = len + #b + end + return len + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, ...) + return _class_0.__parent.__init(self, ...) + end, + __base = _base_0, + __name = "Nomsu", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + Nomsu = _class_0 +end return { + Code = Code, + Nomsu = Nomsu, Lua = Lua, - Location = Location + Source = Source } diff --git a/lua_obj.moon b/lua_obj.moon index ab5b5a3..6ee1970 100644 --- a/lua_obj.moon +++ b/lua_obj.moon @@ -1,25 +1,46 @@ {:insert, :remove, :concat} = table immutable = require 'immutable' -local Lua, Location +local Lua, Source export LINE_STARTS -Location = immutable {"filename","start","stop"}, { - name:"Location" +Source = immutable {"filename","start","stop"}, { + name:"Source" __new: (filename, start, stop)=> - --assert(type(filename) == 'string' and type(start) == 'number' and type(stop) == 'number') - return filename, start, stop or start - __tostring: => "Location(\"#{@filename}\", #{@start}, #{@stop})" + if stop then assert(start <= stop, "Invalid range: #{start}, #{stop}") + return filename, start, stop + __tostring: => + if @stop + "Source(\"#{@filename}\", #{@start}, #{@stop})" + else + "Source(\"#{@filename}\", #{@start})" __lt: (other)=> assert(@filename == other.filename, "Cannot compare sources from different files") return if @start == other.start - @stop < other.stop + (@stop or @start) < (other.stop or other.start) else @start < other.start __le: (other)=> assert(@filename == other.filename, "Cannot compare sources from different files") return if @start == other.start - @stop <= other.stop + (@stop or @start) <= (other.stop or other.start) else @start <= other.start - get_text: => FILE_CACHE[@filename]\sub(@start,@stop) + __add: (offset)=> + if type(self) == 'number' + offset, self = self, offset + else assert(type(offset) == 'number', "Cannot add Source and #{type(offset)}") + return Source(@filename, @start+offset, @stop) + sub: (start, stop)=> + if not @stop + assert(not stop, "cannot subscript non-range with range") + assert(start > 0, "cannot subscript non-range with negative index") + return Source(@filename, @start + (start or 0)) + else + start or= 1 + if start < 0 then start = @stop + start + 1 + stop or= -1 + if stop < 0 then stop = @stop + stop + 1 + return Source(@filename, @start + start - 1, @start + stop - 1) + get_text: => + FILE_CACHE[@filename]\sub(@start,@stop) get_line_number: => -- TODO: do a binary search if this is actually slow, which I doubt src = FILE_CACHE[@filename] @@ -39,12 +60,56 @@ Location = immutable {"filename","start","stop"}, { else "#{@filename}:#{start_line}-#{stop_line}" } -class Lua +class Code new: (@source, ...)=> if type(@source) == 'string' filename,start,stop = @source\match("^(.-)[(%d+):(%d+)]$") - @source = Location(filename, tonumber(start), tonumber(stop)) + @source = Source(filename, tonumber(start), tonumber(stop)) @bits = {...} + + clone: => + cls = @__class + copy = cls(@source, unpack(@bits)) + copy.is_value = @is_value + for k,v in pairs @free_vars + copy.free_vars[k] = v + return copy + + __tostring: => + buff = {} + for i,b in ipairs @bits + buff[#buff+1] = tostring(b) + ret = concat(buff, "") + return ret + + __len: => + len = 0 + for b in *@bits + len += #b + return len + + sub: (start,stop)=> + str = tostring(self)\sub(start,stop) + cls = @__class + return cls(@source\sub(start-@source.start+1,stop-@source.stop+1), str) + + append: (...)=> + n = select("#",...) + bits = @bits + for i=1,n + bits[#bits+1] = select(i, ...) + + prepend: (...)=> + n = select("#",...) + bits = @bits + for i=#bits+n,n+1,-1 + bits[i] = bits[i-n] + for i=1,n + bits[i] = select(i, ...) + +class Lua extends Code + new: (...)=> + super ... @free_vars = {} @is_value = false @@ -53,13 +118,6 @@ class Lua lua.is_value = true return lua - clone: => - copy = Lua(@source, {unpack(@bits)}) - copy.is_value = @is_value - for k,v in pairs @free_vars - copy.free_vars[k] = v - return copy - add_free_vars: (...)=> seen = {[v]:true for v in *@free_vars} for i=1,select("#",...) @@ -90,8 +148,6 @@ class Lua buff = {} for i,b in ipairs @bits buff[#buff+1] = tostring(b) - if i < #@bits and type(b) != 'string' and not b.is_value - buff[#buff+1] = "\n" ret = concat(buff, "") return ret @@ -100,29 +156,32 @@ class Lua for b in *@bits len += #b return len - - __add: (other)=> - Lua(nil, self, other) - - __concat: (other)=> - Lua(nil, self, other) append: (...)=> n = select("#",...) bits = @bits for i=1,n - bits[#bits+1] = select(i, ...) + bit = select(i, ...) + bits[#bits+1] = bit + if type(bit) != 'string' and not bit.is_value and #@bits > 0 + bits[#bits+1] = "\n" prepend: (...)=> n = select("#",...) bits = @bits - for i=#bits+n,n+1,-1 - bits[i] = bits[i-n] + insert_index = 1 + -- TODO: optimize? for i=1,n - bits[i] = select(i, ...) + bit = select(i, ...) + insert bits, insert_index, bit + insert_index += 1 + if type(bit) != 'string' and not bit.is_value and insert_index < #@bits + 1 + insert bits, insert_index, "\n" + insert_index += 1 - make_offset_table: (lua_chunkname)=> + make_offset_table: => -- Return a mapping from output (lua) character number to input (nomsu) character number + lua_chunkname = tostring(@source)..".lua" lua_str = tostring(self) metadata = { nomsu_filename:@source.filename @@ -138,7 +197,7 @@ class Lua for b in *lua.bits walk b lua_stop = lua_offset - nomsu_src, lua_src = lua.source, Location(lua_chunkname, lua_start, lua_stop) + nomsu_src, lua_src = lua.source, Source(lua_chunkname, lua_start, lua_stop) metadata.lua_to_nomsu[lua_src] = nomsu_src metadata.nomsu_to_lua[nomsu_src] = lua_src walk self @@ -151,4 +210,18 @@ class Lua else error "Cannot parenthesize lua statements" -return {:Lua, :Location} +class Nomsu extends Code + __tostring: => + buff = {} + for i,b in ipairs @bits + buff[#buff+1] = tostring(b) + ret = concat(buff, "") + return ret + + __len: => + len = 0 + for b in *@bits + len += #b + return len + +return {:Code, :Nomsu, :Lua, :Source} @@ -30,10 +30,10 @@ do insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat end local debug_getinfo = debug.getinfo -local Lua, Location +local Nomsu, Lua, Source do local _obj_0 = require("lua_obj") - Lua, Location = _obj_0.Lua, _obj_0.Location + Nomsu, Lua, Source = _obj_0.Nomsu, _obj_0.Lua, _obj_0.Source end FILE_CACHE = setmetatable({ }, { __index = function(self, filename) @@ -41,10 +41,16 @@ FILE_CACHE = setmetatable({ }, { if not (file) then return nil end - local source = file:read("a"):sub(1, -2) + local code = file:read("a"):sub(1, -2) file:close() - self[filename] = source - return source + local source = Source(filename, 1, #code) + if filename:match("%.nom$") then + code = Nomsu(source, code) + elseif filename:match("%.lua$") then + code = Lua(source, code) + end + self[filename] = code + return code end }) local line_counter = re.compile([[ lines <- {| line (%nl line)* |} @@ -55,7 +61,7 @@ local line_counter = re.compile([[ lines <- {| line (%nl line)* |} LINE_STARTS = setmetatable({ }, { __mode = "k", __index = function(self, k) - local line_starts = line_counter:match(k) + local line_starts = line_counter:match(tostring(k)) self[k] = line_starts return line_starts end @@ -149,17 +155,17 @@ do end end) _with_0.error = function(src, pos, err_msg) - if lpeg.userdata.source_code:sub(pos, pos):match("[\r\n]") then - pos = pos + #lpeg.userdata.source_code:match("[ \t\n\r]*", pos) + if src:sub(pos, pos):match("[\r\n]") then + pos = pos + #src:match("[ \t\n\r]*", pos) end local line_no = 1 - local text_loc = Location(lpeg.userdata.filename, pos) + local text_loc = lpeg.userdata.source_code.source:sub(pos, pos) line_no = text_loc:get_line_number() local prev_line = src:sub(LINE_STARTS[src][line_no - 1] or 1, LINE_STARTS[src][line_no] - 1) local err_line = src:sub(LINE_STARTS[src][line_no], (LINE_STARTS[src][line_no + 1] or 0) - 1) local next_line = src:sub(LINE_STARTS[src][line_no + 1] or -1, (LINE_STARTS[src][line_no + 2] or 0) - 1) local pointer = ("-"):rep(pos - LINE_STARTS[src][line_no]) .. "^" - err_msg = (err_msg or "Parse error") .. " in " .. tostring(lpeg.userdata.filename) .. " on line " .. tostring(line_no) .. ":\n" + err_msg = (err_msg or "Parse error") .. " in " .. tostring(lpeg.userdata.source_code.source.filename) .. " on line " .. tostring(line_no) .. ":\n" 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 @@ -172,8 +178,8 @@ setmetatable(NOMSU_DEFS, { if type(value) == 'table' then error("Not a tuple: " .. tostring(repr(value))) end - local loc = Location(lpeg.userdata.filename, start, stop) - local node = Types[key](value, loc) + local source = lpeg.userdata.source_code.source:sub(start, stop) + local node = Types[key](value, source) return node end self[key] = make_node @@ -197,7 +203,7 @@ end local NomsuCompiler do local _class_0 - local _chunk_counter, stub_defs, stub_pattern, var_pattern + local stub_defs, stub_pattern, var_pattern local _base_0 = { define_action = function(self, signature, source, fn) if type(fn) ~= 'function' then @@ -310,34 +316,35 @@ do end return code:gsub("\n", "\n" .. (" "):rep(levels)) end, - parse = function(self, nomsu_code, filename) - assert(type(filename) == "string", "Bad filename type: " .. tostring(type(filename))) + parse = function(self, nomsu_code) local userdata = { source_code = nomsu_code, - filename = filename, indent_stack = { "" } } local old_userdata old_userdata, lpeg.userdata = lpeg.userdata, userdata - local tree = NOMSU_PATTERN:match(nomsu_code) + local tree = NOMSU_PATTERN:match(tostring(nomsu_code)) lpeg.userdata = old_userdata assert(tree, "In file " .. tostring(colored.blue(filename)) .. " failed to parse:\n" .. tostring(colored.onyellow(colored.black(nomsu_code)))) return tree end, - run = function(self, nomsu_code, filename) + run = function(self, nomsu_code, source) + if type(source) == 'string' then + source = Source(source, 1, #nomsu_code) + end if nomsu_code == "" then - return nil, "" + return nil end - local tree = self:parse(nomsu_code, filename) + local tree = self:parse(nomsu_code, source) assert(tree, "Failed to parse: " .. tostring(nomsu_code)) assert(tree.type == "File", "Attempt to run non-file: " .. tostring(tree.type)) local lua = self:tree_to_lua(tree) lua:convert_to_statements() lua:declare_locals() - lua:prepend("-- File: " .. tostring(filename) .. "\n") - return self:run_lua(lua, filename .. ".lua") + lua:prepend("-- File: " .. tostring(source) .. "\n") + return self:run_lua(lua) end, run_file = function(self, filename) local file_attributes = assert(lfs.attributes(filename), "File not found: " .. tostring(filename)) @@ -396,25 +403,17 @@ do end return loaded[filename] end, - run_lua = function(self, lua, filename) - if filename == nil then - filename = nil - end - if filename == nil then - if type(lua) == 'string' then - _chunk_counter = _chunk_counter + 1 - filename = "<lua chunk #" .. tostring(_chunk_counter) .. ">" - else - filename = lua.source.filename .. ".lua" - end - FILE_CACHE[filename] = tostring(lua) + run_lua = function(self, lua) + assert(type(lua) ~= 'string', "Attempt to run lua string instead of Lua (object)") + local lua_string, metadata = lua:make_offset_table() + LUA_METADATA[metadata.lua_filename] = metadata + if rawget(FILE_CACHE, lua.source.filename) == nil then + FILE_CACHE[lua.source.filename] = lua_string end - if type(lua) ~= 'string' then - local metadata - lua, metadata = lua:make_offset_table(filename) - LUA_METADATA[lua] = metadata + if rawget(FILE_CACHE, lua.source) == nil then + FILE_CACHE[lua.source] = lua_string end - local run_lua_fn, err = load(lua, filename, "t", self.environment) + local run_lua_fn, err = load(lua_string, filename, "t", self.environment) if not run_lua_fn then local n = 1 local fn @@ -422,7 +421,7 @@ do n = n + 1 return ("\n%-3d|"):format(n) end - local line_numbered_lua = "1 |" .. lua:gsub("\n", fn) + local line_numbered_lua = "1 |" .. lua_string:gsub("\n", fn) error("Failed to compile generated code:\n" .. tostring(colored.bright(colored.blue(colored.onblack(line_numbered_lua)))) .. "\n\n" .. tostring(err), 0) end return run_lua_fn() @@ -1204,7 +1203,8 @@ do end self.environment.Tuple = Tuple self.environment.Lua = Lua - self.environment.Location = Location + self.environment.Nomsu = Nomsu + self.environment.Source = Source self.environment.ACTIONS = setmetatable({ }, { __index = function(self, key) return error("Attempt to run undefined action: " .. tostring(key), 0) @@ -1226,7 +1226,6 @@ do }) _base_0.__class = _class_0 local self = _class_0 - _chunk_counter = 0 self.unescape_string = function(self, str) return Cs(((P("\\\\") / "\\") + (P("\\\"") / '"') + NOMSU_DEFS.escaped_char + P(1)) ^ 0):match(str) end @@ -25,7 +25,7 @@ colors = setmetatable({}, {__index:->""}) colored = setmetatable({}, {__index:(_,color)-> ((msg)-> colors[color]..tostring(msg or '')..colors.reset)}) {:insert, :remove, :concat} = table debug_getinfo = debug.getinfo -{:Lua, :Location} = require "lua_obj" +{:Nomsu, :Lua, :Source} = require "lua_obj" -- TODO: -- consider non-linear codegen, rather than doing thunks for things like comprehensions @@ -45,10 +45,15 @@ FILE_CACHE = setmetatable {}, { __index: (filename)=> file = io.open(filename) return nil unless file - source = file\read("a")\sub(1,-2) -- Lua appends trailing newline for no apparent reason. + code = file\read("a")\sub(1,-2) -- Lua appends trailing newline for no apparent reason. file\close! - self[filename] = source - return source + source = Source(filename, 1, #code) + if filename\match("%.nom$") + code = Nomsu(source, code) + elseif filename\match("%.lua$") + code = Lua(source, code) + self[filename] = code + return code } line_counter = re.compile([[ @@ -60,7 +65,7 @@ export LINE_STARTS LINE_STARTS = setmetatable {}, { __mode:"k" __index: (k)=> - line_starts = line_counter\match(k) + line_starts = line_counter\match(tostring(k)) self[k] = line_starts return line_starts } @@ -142,24 +147,24 @@ NOMSU_DEFS = with {} return start + #nodent .error = (src,pos,err_msg)-> - if lpeg.userdata.source_code\sub(pos,pos)\match("[\r\n]") - pos += #lpeg.userdata.source_code\match("[ \t\n\r]*", pos) + if src\sub(pos,pos)\match("[\r\n]") + pos += #src\match("[ \t\n\r]*", pos) line_no = 1 - text_loc = Location(lpeg.userdata.filename, pos) + text_loc = lpeg.userdata.source_code.source\sub(pos,pos) line_no = text_loc\get_line_number! prev_line = src\sub(LINE_STARTS[src][line_no-1] or 1, LINE_STARTS[src][line_no]-1) err_line = src\sub(LINE_STARTS[src][line_no], (LINE_STARTS[src][line_no+1] or 0)-1) next_line = src\sub(LINE_STARTS[src][line_no+1] or -1, (LINE_STARTS[src][line_no+2] or 0)-1) pointer = ("-")\rep(pos-LINE_STARTS[src][line_no]) .. "^" - err_msg = (err_msg or "Parse error").." in #{lpeg.userdata.filename} on line #{line_no}:\n" + err_msg = (err_msg or "Parse error").." in #{lpeg.userdata.source_code.source.filename} on line #{line_no}:\n" err_msg ..="\n#{prev_line}\n#{err_line}\n#{pointer}\n#{next_line}\n" error(err_msg) setmetatable(NOMSU_DEFS, {__index:(key)=> make_node = (start, value, stop)-> if type(value) == 'table' then error("Not a tuple: #{repr value}")-- = Tuple(value) - loc = Location(lpeg.userdata.filename, start, stop) - node = Types[key](value, loc) + source = lpeg.userdata.source_code.source\sub(start,stop) + node = Types[key](value, source) return node self[key] = make_node return make_node @@ -212,7 +217,8 @@ class NomsuCompiler for k,v in pairs(Types) do @environment[k] = v @environment.Tuple = Tuple @environment.Lua = Lua - @environment.Location = Location + @environment.Nomsu = Nomsu + @environment.Source = Source @environment.ACTIONS = setmetatable({}, {__index:(key)=> error("Attempt to run undefined action: #{key}", 0) }) @@ -279,30 +285,30 @@ class NomsuCompiler indent: (code, levels=1)=> return code\gsub("\n","\n"..(" ")\rep(levels)) - parse: (nomsu_code, filename)=> - assert type(filename) == "string", "Bad filename type: #{type filename}" - + parse: (nomsu_code)=> userdata = { - source_code:nomsu_code, :filename, indent_stack: {""} + source_code:nomsu_code, indent_stack: {""} } old_userdata, lpeg.userdata = lpeg.userdata, userdata - tree = NOMSU_PATTERN\match(nomsu_code) + tree = NOMSU_PATTERN\match(tostring(nomsu_code)) lpeg.userdata = old_userdata assert tree, "In file #{colored.blue filename} failed to parse:\n#{colored.onyellow colored.black nomsu_code}" return tree - run: (nomsu_code, filename)=> - if nomsu_code == "" then return nil, "" - tree = @parse(nomsu_code, filename) + run: (nomsu_code, source)=> + if type(source) == 'string' + source = Source(source,1,#nomsu_code) + if nomsu_code == "" then return nil + tree = @parse(nomsu_code, source) assert tree, "Failed to parse: #{nomsu_code}" assert tree.type == "File", "Attempt to run non-file: #{tree.type}" lua = @tree_to_lua(tree) lua\convert_to_statements! lua\declare_locals! - lua\prepend "-- File: #{filename}\n" - return @run_lua(lua, filename..".lua") + lua\prepend "-- File: #{source}\n" + return @run_lua(lua) run_file: (filename)=> file_attributes = assert(lfs.attributes(filename), "File not found: #{filename}") @@ -342,26 +348,22 @@ class NomsuCompiler loaded[filename] = @run_file(filename) or true return loaded[filename] - _chunk_counter = 0 - run_lua: (lua, filename=nil)=> - if filename == nil - filename = if type(lua) == 'string' - _chunk_counter += 1 - "<lua chunk ##{_chunk_counter}>" - else - lua.source.filename..".lua" - FILE_CACHE[filename] = tostring(lua) - if type(lua) != 'string' - lua, metadata = lua\make_offset_table(filename) - LUA_METADATA[lua] = metadata + run_lua: (lua)=> + assert(type(lua) != 'string', "Attempt to run lua string instead of Lua (object)") + lua_string, metadata = lua\make_offset_table! + LUA_METADATA[metadata.lua_filename] = metadata + if rawget(FILE_CACHE, lua.source.filename) == nil + FILE_CACHE[lua.source.filename] = lua_string + if rawget(FILE_CACHE, lua.source) == nil + FILE_CACHE[lua.source] = lua_string - run_lua_fn, err = load(lua, filename, "t", @environment) + run_lua_fn, err = load(lua_string, filename, "t", @environment) if not run_lua_fn n = 1 fn = -> n = n + 1 ("\n%-3d|")\format(n) - line_numbered_lua = "1 |"..lua\gsub("\n", fn) + line_numbered_lua = "1 |"..lua_string\gsub("\n", fn) error("Failed to compile generated code:\n#{colored.bright colored.blue colored.onblack line_numbered_lua}\n\n#{err}", 0) return run_lua_fn! diff --git a/nomsu_tree.moon b/nomsu_tree.moon index 9b00d85..d327e2e 100644 --- a/nomsu_tree.moon +++ b/nomsu_tree.moon @@ -50,7 +50,7 @@ Tree "File", Tree "Nomsu", as_lua: (nomsu)=> - Lua.Value(@source, "nomsu:parse(",repr(@source\get_text!),", ",repr(@source.filename),")") + Lua.Value(@source, "nomsu:parse(Nomsu(",repr(@source),", ",repr(@source\get_text!),"))") Tree "Block", as_lua: (nomsu)=> |
