diff --git a/code_obj.lua b/code_obj.lua index 47e08cb..39f6c95 100644 --- a/code_obj.lua +++ b/code_obj.lua @@ -187,7 +187,7 @@ do for _index_0 = 1, #vars do local var = vars[_index_0] assert(var.type == "Var") - removals[var.value] = true + removals[var[1]] = true end local stack = { self @@ -196,7 +196,7 @@ do local lua lua, stack[#stack] = stack[#stack], nil for i = #lua.free_vars, 1, -1 do - if removals[lua.free_vars[i].value] then + if removals[lua.free_vars[i][1]] then remove(lua.free_vars, i) end end @@ -264,7 +264,7 @@ do local _len_0 = 1 for _index_0 = 1, #to_declare do local v = to_declare[_index_0] - _accum_0[_len_0] = string.as_lua_id(v.value) + _accum_0[_len_0] = string.as_lua_id(v[1]) _len_0 = _len_0 + 1 end return _accum_0 diff --git a/code_obj.moon b/code_obj.moon index 45df615..dae3f52 100644 --- a/code_obj.moon +++ b/code_obj.moon @@ -109,13 +109,13 @@ class Lua extends Code removals = {} for var in *vars assert(var.type == "Var") - removals[var.value] = true + removals[var[1]] = true stack = {self} while #stack > 0 lua, stack[#stack] = stack[#stack], nil for i=#lua.free_vars,1,-1 - if removals[lua.free_vars[i].value] + if removals[lua.free_vars[i][1]] remove lua.free_vars, i for b in *lua.bits if type(b) != 'string' @@ -147,7 +147,7 @@ class Lua extends Code gather_from self if #to_declare > 0 @remove_free_vars to_declare - @prepend "local #{concat [string.as_lua_id(v.value) for v in *to_declare], ", "};\n" + @prepend "local #{concat [string.as_lua_id(v[1]) for v in *to_declare], ", "};\n" return to_declare __tostring: => diff --git a/compile_lib.sh b/compile_lib.sh index fa932b3..d7df5f8 100755 --- a/compile_lib.sh +++ b/compile_lib.sh @@ -6,11 +6,11 @@ moonc *.moon rm -f core/*.lua lib/*.lua for file in core/*.nom; do printf "Compiling $file ..." - lua ./nomsu.lua -O -o "core/$(basename $file .nom).lua" $file + luajit ./nomsu.lua -O -o "core/$(basename $file .nom).lua" $file echo "done." done for file in lib/*.nom; do printf "Compiling $file ..." - lua ./nomsu.lua -O -o "lib/$(basename $file .nom).lua" $file + luajit ./nomsu.lua -O -o "lib/$(basename $file .nom).lua" $file echo "done." done diff --git a/core/control_flow.nom b/core/control_flow.nom index 73a0f43..f0238a4 100644 --- a/core/control_flow.nom +++ b/core/control_flow.nom @@ -188,14 +188,14 @@ immediately %body has subtree % where (%.type = "Action") and (%.stub is "do next %") and - %.3.value = %var.value + %.3.(1) = %var.(1) ..: to %lua write (Lua "\n ::continue_\(%var as lua identifier)::") to %lua write "\nend --foreach-loop" if %body has subtree % where (%.type = "Action") and (%.stub is "stop %") and - %.2.value = %var.value + %.2.(1) = %var.(1) .. %lua <- Lua ".." @@ -222,14 +222,14 @@ immediately %body has subtree % where (%.type = "Action") and (%.stub is "do next %") and - %.3.value = %key.value + %.3.(1) = %key.(1) ..: to %lua write (Lua "\n ::continue_\(%key as lua identifier)::") if %body has subtree % where (%.type = "Action") and (%.stub is "do next %") and - %.3.value = %value.value + %.3.(1) = %value.(1) ..: to %lua write (Lua "\n ::continue_\(%value as lua identifier)::") to %lua write "\nend --foreach-loop" @@ -238,14 +238,14 @@ immediately %body has subtree % where (%.type = "Action") and (%.stub is "stop %") and - %.2.value = %key.value + %.2.(1) = %key.(1) ..: to %stop_labels write "\n::stop_\(%key as lua identifier)::" if %body has subtree % where (%.type = "Action") and (%.stub is "stop %") and - %.2.value = %value.value + %.2.(1) = %value.(1) ..: to %stop_labels write "\n::stop_\(%value as lua identifier)::" if: (length of %stop_labels) > 0 diff --git a/core/metaprogramming.nom b/core/metaprogramming.nom index ec65f3e..e413191 100644 --- a/core/metaprogramming.nom +++ b/core/metaprogramming.nom @@ -82,7 +82,7 @@ immediately for i,tok in ipairs(\%shorthand[1]) do if tok.type == "Var" then local lua_var = tostring(nomsu:tree_to_lua(tok)) - replacements[tok.value] = lua_var + replacements[tok[1]] = lua_var lua:append(", ", lua_var) end end @@ -90,12 +90,10 @@ immediately local function make_tree(t) if type(t) ~= 'table' and type(t) ~= 'userdata' then return repr(t) - elseif t.type == 'Var' and replacements[t.value] then - return replacements[t.value] + elseif t.type == 'Var' and replacements[t[1]] then + return replacements[t[1]] elseif t.type == 'Var' then - return t.type.."("..repr(tostring(t.source))..", "..repr(t.value.."#"..tostring(MANGLE_INDEX))..")" - elseif t.value then - return t.type.."("..repr(tostring(t.source))..", "..repr(t.value)..")" + return t.type.."("..repr(tostring(t.source))..", "..repr(t[1].."#"..tostring(MANGLE_INDEX))..")" else local bits = {repr(tostring(t.source))} for i, entry in ipairs(t) do diff --git a/core/operators.nom b/core/operators.nom index 4ec8ff1..555a636 100644 --- a/core/operators.nom +++ b/core/operators.nom @@ -160,8 +160,7 @@ immediately # Unary operators compile [- %] to: Lua value "(- \(% as lua expr))" compile [not %] to: Lua value "(not \(% as lua expr))" - # Using custom "len()" instead of Lua's "#" operator for compatibility with luajit. - compile [length of %list] to: Lua value "len(\(%list as lua expr))" + compile [length of %list] to: Lua value "(#\(%list as lua expr))" # Update operators immediately diff --git a/nomsu.lua b/nomsu.lua index e9d394f..4f61c29 100644 --- a/nomsu.lua +++ b/nomsu.lua @@ -1,33 +1,10 @@ local _pairs, _ipairs = pairs, ipairs if jit then package.cpath = "./luajit_lpeg/?.so;" .. package.cpath - lpeg = require('lpeg') + package.path = "./luajit_lpeg/?.lua;" .. package.path bit32 = require('bit') - pairs = function(x) - do - local mt = getmetatable(x) - if mt then - if mt.__pairs then - return mt.__pairs(x) - end - end - end - return _pairs(x) - end - ipairs = function(x) - do - local mt = getmetatable(x) - if mt then - if mt.__ipairs then - return mt.__ipairs(x) - end - end - end - return _ipairs(x) - end -else - lpeg = require('lpeg') end +lpeg = require('lpeg') re = require('re') lpeg.setmaxstack(10000) local P, R, V, S, Cg, C, Cp, B, Cmt, Carg @@ -53,6 +30,11 @@ do local _obj_0 = table insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat end +local match, sub, rep, gsub, format, byte +do + local _obj_0 = string + match, sub, rep, gsub, format, byte, match = _obj_0.match, _obj_0.sub, _obj_0.rep, _obj_0.gsub, _obj_0.format, _obj_0.byte, _obj_0.match +end local debug_getinfo = debug.getinfo local Nomsu, Lua, Source do @@ -61,11 +43,11 @@ do end local STDIN, STDOUT, STDERR = "/dev/fd/0", "/dev/fd/1", "/dev/fd/2" string.as_lua_id = function(str) - return "_" .. (str:gsub("%W", function(c) + return "_" .. (gsub(str, "%W", function(c) if c == "_" then return "__" else - return ("_%x"):format(c:byte()) + return format("_%x", byte(c)) end end)) end @@ -91,10 +73,13 @@ iterate_single = function(item, prev) end local all_files all_files = function(path) - if path:match("%.nom$") or path:match("%.lua$") or path:match("^/dev/fd/[012]$") then + if match(path, "%.nom$") or match(path, "%.lua$") or match(path, "^/dev/fd/[012]$") then return iterate_single, path end - path = path:gsub("\\", "\\\\"):gsub("`", ""):gsub('"', '\\"'):gsub("$", "") + path = gsub(path, "\\", "\\\\") + path = gsub(path, "`", "") + path = gsub(path, '"', '\\"') + path = gsub(path, "$", "") return coroutine.wrap(function() local f = io.popen('find -L "' .. path .. '" -not -path "*/\\.*" -type f -name "*.nom"') for line in f:lines() do @@ -153,9 +138,9 @@ do return ret end if type(i) == 'number' then - return string.sub(self, i, i) + return sub(self, i, i) elseif type(i) == 'table' then - return string.sub(self, i[1], i[2]) + return sub(self, i[1], i[2]) end end end @@ -187,19 +172,19 @@ do _with_0.utf8_char = (R("\194\223") * R("\128\191") + R("\224\239") * R("\128\191") * R("\128\191") + R("\240\244") * R("\128\191") * R("\128\191") * R("\128\191")) _with_0.ident_char = R("az", "AZ", "09") + P("_") + _with_0.utf8_char _with_0.indent = Cmt(Carg(1), function(self, start, userdata) - if #self:match("^[ ]*", start) >= userdata.indent + 4 then + if #match(self, "^[ ]*", start) >= userdata.indent + 4 then userdata.indent = userdata.indent + 4 return start + userdata.indent end end) _with_0.dedent = Cmt(Carg(1), function(self, start, userdata) - if #self:match("^[ ]*", start) <= userdata.indent - 4 then + if #match(self, "^[ ]*", start) <= userdata.indent - 4 then userdata.indent = userdata.indent - 4 return start end end) _with_0.nodent = Cmt(Carg(1), function(self, start, userdata) - if #self:match("^[ ]*", start) >= userdata.indent then + if #match(self, "^[ ]*", start) >= userdata.indent then return start + userdata.indent end end) @@ -403,12 +388,12 @@ do end end insert(_running_files, filename) - if filename:match("%.lua$") then + if match(filename, "%.lua$") then local file = assert(FILE_CACHE[filename], "Could not find file: " .. tostring(filename)) ret = self:run_lua(Lua(Source(filename, 1, #file), file)) - elseif filename:match("%.nom$") or filename:match("^/dev/fd/[012]$") then + elseif match(filename, "%.nom$") or match(filename, "^/dev/fd/[012]$") then if not self.skip_precompiled then - local lua_filename = filename:gsub("%.nom$", ".lua") + local lua_filename = gsub(filename, "%.nom$", ".lua") local file = FILE_CACHE[lua_filename] if file then ret = self:run_lua(Lua(Source(filename, 1, #file), file)) @@ -439,7 +424,7 @@ do run_lua = function(self, lua) assert(type(lua) ~= 'string', "Attempt to run lua string instead of Lua (object)") local lua_string = tostring(lua) - local run_lua_fn, err = load(lua_string, nil and tostring(lua.source), "t", self.environment) + local run_lua_fn, err = load(lua_string, tostring(lua.source), "t", self.environment) if not run_lua_fn then local n = 1 local fn @@ -590,22 +575,18 @@ do if not (AST.is_syntax_tree(t)) then return repr(t) end - if t.value then - return t.type .. "(" .. repr(tostring(t.source)) .. ", " .. repr(t.value) .. ")" - else - local bits - do - local _accum_0 = { } - local _len_0 = 1 - for _index_0 = 1, #t do - local bit = t[_index_0] - _accum_0[_len_0] = make_tree(bit) - _len_0 = _len_0 + 1 - end - bits = _accum_0 + local bits + do + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #t do + local bit = t[_index_0] + _accum_0[_len_0] = make_tree(bit) + _len_0 = _len_0 + 1 end - return t.type .. "(" .. repr(tostring(t.source)) .. ", " .. table.concat(bits, ", ") .. ")" + bits = _accum_0 end + return t.type .. "(" .. repr(tostring(t.source)) .. ", " .. table.concat(bits, ", ") .. ")" end return Lua.Value(tree.source, make_tree(tree[1])) elseif "Block" == _exp_0 then @@ -638,7 +619,7 @@ do end local bit_lua = self:tree_to_lua(bit) if not (bit_lua.is_value) then - local src = ' ' .. tostring(self:tree_to_nomsu(bit)):gsub('\n', '\n ') + local src = ' ' .. gsub(tostring(self:tree_to_nomsu(bit)), '\n', '\n ') local line = tostring(bit.source.filename) .. ":" .. tostring(pos_to_line(FILE_CACHE[bit.source.filename], bit.source.start)) compile_error(bit, "Cannot use:\n%s\nas a string interpolation value, since it's not an expression.") end @@ -675,8 +656,8 @@ do end lua:append(item_lua) local item_string = tostring(item_lua) - local last_line = item_string:match("[^\n]*$") - if item_string:match("\n") then + local last_line = match(item_string, "[^\n]*$") + if match(item_string, "\n") then line_length = #last_line else line_length = line_length + #last_line @@ -700,7 +681,7 @@ do local entry_lua = self:tree_to_lua(entry) lua:append(entry_lua) local entry_lua_str = tostring(entry_lua) - local last_line = entry_lua_str:match("\n([^\n]*)$") + local last_line = match(entry_lua_str, "\n([^\n]*)$") if last_line then line_length = #last_line else @@ -728,10 +709,10 @@ do if not (value_lua.is_value) then compile_error(tree[2], "Cannot use:\n%s\nas a dict value, since it's not an expression.") end - local key_str = tostring(key_lua):match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=]) + local key_str = match(tostring(key_lua), [=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=]) if key_str then return Lua(tree.source, key_str, "=", value_lua) - elseif tostring(key_lua):sub(1, 1) == "[" then + elseif sub(tostring(key_lua), 1, 1) == "[" then return Lua(tree.source, "[ ", key_lua, "]=", value_lua) else return Lua(tree.source, "[", key_lua, "]=", value_lua) @@ -741,7 +722,7 @@ do if not (lua.is_value) then compile_error(tree[1], "Cannot index:\n%s\nsince it's not an expression.") end - local first_char = tostring(lua):sub(1, 1) + local first_char = sub(tostring(lua), 1, 1) if first_char == "{" or first_char == '"' or first_char == "[" then lua:parenthesize() end @@ -753,10 +734,10 @@ do end local key_lua_str = tostring(key_lua) do - local lua_id = key_lua_str:match("^['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]$") + local lua_id = match(key_lua_str, "^['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]$") if lua_id then lua:append("." .. tostring(lua_id)) - elseif key_lua_str:sub(1, 1) == '[' then + elseif sub(key_lua_str, 1, 1) == '[' then lua:append("[ ", key_lua, " ]") else lua:append("[", key_lua, "]") @@ -765,9 +746,9 @@ do end return lua elseif "Number" == _exp_0 then - return Lua.Value(tree.source, tostring(tree.value)) + return Lua.Value(tree.source, tostring(tree[1])) elseif "Var" == _exp_0 then - return Lua.Value(tree.source, string.as_lua_id(tree.value)) + return Lua.Value(tree.source, string.as_lua_id(tree[1])) else return error("Unknown type: " .. tostring(tree.type)) end @@ -824,7 +805,7 @@ do if arg_nomsu and #arg_nomsu < MAX_LINE then if bit.type == "Action" then if can_use_colon and i > 1 then - nomsu:append(next_space:match("[^ ]*"), ": ", arg_nomsu) + nomsu:append(match(next_space, "[^ ]*"), ": ", arg_nomsu) next_space = "\n.." last_colon = i else @@ -853,7 +834,7 @@ do nomsu:append(next_space, arg_nomsu) next_space = "\n.." end - if next_space == " " and #(tostring(nomsu):match("[^\n]*$")) > MAX_LINE then + if next_space == " " and #(match(tostring(nomsu), "[^\n]*$")) > MAX_LINE then next_space = "\n.." end end @@ -888,7 +869,7 @@ do nomsu:append(line) if i < #tree then nomsu:append("\n") - if tostring(line):match("\n") then + if match(tostring(line), "\n") then nomsu:append("\n") end end @@ -900,7 +881,7 @@ do for _index_0 = 1, #tree do local bit = tree[_index_0] if type(bit) == 'string' then - nomsu:append((bit:gsub("\\", "\\\\"):gsub("\n", "\\n"))) + nomsu:append((gsub(gsub(bit, "\\", "\\\\"), "\n", "\\n"))) else local interp_nomsu = self:tree_to_nomsu(bit, true) if interp_nomsu then @@ -923,7 +904,7 @@ do local nomsu = Nomsu(tree.source, '".."\n ') for i, bit in ipairs(self) do if type(bit) == 'string' then - nomsu:append((bit:gsub("\\", "\\\\"):gsub("\n", "\n "))) + nomsu:append((gsub(gsub(bit, "\\", "\\\\"), "\n", "\\n"))) else local interp_nomsu = self:tree_to_nomsu(bit, true) if interp_nomsu then @@ -1085,9 +1066,9 @@ do end return nomsu elseif "Number" == _exp_0 then - return Nomsu(tree.source, tostring(tree.value)) + return Nomsu(tree.source, tostring(tree[1])) elseif "Var" == _exp_0 then - return Nomsu(tree.source, "%", tree.value) + return Nomsu(tree.source, "%", tree[1]) else return error("Unknown type: " .. tostring(tree.type)) end @@ -1268,60 +1249,11 @@ do math = math, io = io, load = load, + pairs = pairs, + ipairs = ipairs, list = list, dict = dict } - if jit then - self.environment.len = function(x) - do - local mt = getmetatable(x) - if mt then - if mt.__len then - return mt.__len(x) - end - end - end - return #x - end - else - self.environment.len = (function(x) - return #x - end) - end - self.environment.ipairs = function(x) - if type(x) == 'function' then - return coroutine.wrap(x) - elseif type(x) == 'thread' then - return coroutine.resume, x, nil - else - do - local mt = getmetatable(x) - if mt then - if mt.__ipairs then - return mt.__ipairs(x) - end - end - end - end - return _ipairs(x) - end - self.environment.pairs = function(x) - if type(x) == 'function' then - return coroutine.wrap(x) - elseif type(x) == 'thread' then - return coroutine.resume, x, nil - else - do - local mt = getmetatable(x) - if mt then - if mt.__pairs then - return mt.__pairs(x) - end - end - end - end - return _pairs(x) - end for k, v in pairs(AST) do self.environment[k] = v end diff --git a/nomsu.moon b/nomsu.moon index bef479b..5aaf683 100755 --- a/nomsu.moon +++ b/nomsu.moon @@ -14,27 +14,12 @@ export lpeg, re _pairs, _ipairs = pairs, ipairs if jit package.cpath = "./luajit_lpeg/?.so;"..package.cpath - --package.path = "./LPegLJ/src/?.lua;"..package.path - --lpeg = require "lpeglj" - lpeg = require 'lpeg' + package.path = "./luajit_lpeg/?.lua;"..package.path export bit32 bit32 = require('bit') - export pairs, ipairs - pairs = (x)-> - if mt = getmetatable(x) - if mt.__pairs - return mt.__pairs(x) - return _pairs(x) - ipairs = (x)-> - if mt = getmetatable(x) - if mt.__ipairs - return mt.__ipairs(x) - return _ipairs(x) -else - lpeg = require 'lpeg' - +lpeg = require 'lpeg' re = require 're' lpeg.setmaxstack 10000 {:P,:R,:V,:S,:Cg,:C,:Cp,:B,:Cmt,:Carg} = lpeg @@ -45,12 +30,13 @@ colors = setmetatable({}, {__index:->""}) export colored colored = setmetatable({}, {__index:(_,color)-> ((msg)-> colors[color]..tostring(msg or '')..colors.reset)}) {:insert, :remove, :concat} = table +{:match, :sub, :rep, :gsub, :format, :byte, :match} = string debug_getinfo = debug.getinfo {:Nomsu, :Lua, :Source} = require "code_obj" STDIN, STDOUT, STDERR = "/dev/fd/0", "/dev/fd/1", "/dev/fd/2" string.as_lua_id = (str)-> - "_"..(str\gsub("%W", (c)-> if c == "_" then "__" else ("_%x")\format(c\byte!))) + "_"..(gsub(str, "%W", (c)-> if c == "_" then "__" else format("_%x", byte(c)))) -- TODO: -- consider non-linear codegen, rather than doing thunks for things like comprehensions @@ -77,10 +63,13 @@ FILE_CACHE = setmetatable {}, { iterate_single = (item, prev) -> if item == prev then nil else item all_files = (path)-> -- Sanitize path - if path\match("%.nom$") or path\match("%.lua$") or path\match("^/dev/fd/[012]$") + if match(path, "%.nom$") or match(path, "%.lua$") or match(path, "^/dev/fd/[012]$") return iterate_single, path -- TODO: improve sanitization - path = path\gsub("\\","\\\\")\gsub("`","")\gsub('"','\\"')\gsub("$","") + path = gsub(path,"\\","\\\\") + path = gsub(path,"`","") + path = gsub(path,'"','\\"') + path = gsub(path,"$","") return coroutine.wrap -> f = io.popen('find -L "'..path..'" -not -path "*/\\.*" -type f -name "*.nom"') for line in f\lines! @@ -128,8 +117,8 @@ do STRING_METATABLE.__index = (i)=> ret = string[i] if ret != nil then return ret - if type(i) == 'number' then return string.sub(@, i, i) - elseif type(i) == 'table' then return string.sub(@, i[1], i[2]) + if type(i) == 'number' then return sub(@, i, i) + elseif type(i) == 'table' then return sub(@, i[1], i[2]) AST = require "nomsu_tree" @@ -153,19 +142,19 @@ NOMSU_DEFS = with {} -- If the line begins with #indent+4 spaces, the pattern matches *those* spaces -- and adds them to the stack (not any more). .indent = Cmt Carg(1), (start, userdata)=> - if #@match("^[ ]*", start) >= userdata.indent + 4 + if #match(@, "^[ ]*", start) >= userdata.indent + 4 userdata.indent += 4 return start + userdata.indent -- If the number of leading space characters is <= the number of space on the top of the -- stack minus 4, this pattern matches and pops off the top of the stack exactly once. .dedent = Cmt Carg(1), (start, userdata)=> - if #@match("^[ ]*", start) <= userdata.indent - 4 + if #match(@, "^[ ]*", start) <= userdata.indent - 4 userdata.indent -= 4 return start -- If the number of leading space characters is >= the number on the top of the -- stack, this pattern matches and does not modify the stack. .nodent = Cmt Carg(1), (start, userdata)=> - if #@match("^[ ]*", start) >= userdata.indent + if #match(@, "^[ ]*", start) >= userdata.indent return start + userdata.indent .userdata = Carg(1) @@ -178,8 +167,6 @@ NOMSU_DEFS = with {} seen_errors[start_pos+1] = colored.bright colored.yellow colored.onred "Too many errors, canceling parsing..." return #src+1 err_pos = start_pos - --if src\sub(err_pos,err_pos)\match("[\r\n]") - -- err_pos += #src\match("[ \t\n\r]*", err_pos) line_no = pos_to_line(src, err_pos) src = FILE_CACHE[userdata.source.filename] line_starts = LINE_STARTS[src] @@ -276,34 +263,10 @@ class NomsuCompiler :error, :package, :os, :require, :tonumber, :tostring, :string, :xpcall, :module, :print, :loadfile, :rawset, :_VERSION, :collectgarbage, :rawget, :bit32, :rawlen, :table, :assert, :dofile, :loadstring, :type, :select, :debug, :math, :io, :load, + :pairs, :ipairs, -- Nomsu types: :list, :dict, } - @environment.len = if jit - (x)-> - if mt = getmetatable(x) - if mt.__len - return mt.__len(x) - return #x - else ((x) -> #x) - @environment.ipairs = (x)-> - if type(x) == 'function' - return coroutine.wrap(x) - elseif type(x) == 'thread' - return coroutine.resume, x, nil - elseif mt = getmetatable(x) - if mt.__ipairs - return mt.__ipairs(x) - return _ipairs(x) - @environment.pairs = (x)-> - if type(x) == 'function' - return coroutine.wrap(x) - elseif type(x) == 'thread' - return coroutine.resume, x, nil - elseif mt = getmetatable(x) - if mt.__pairs - return mt.__pairs(x) - return _pairs(x) for k,v in pairs(AST) do @environment[k] = v @environment.Lua = Lua @environment.Nomsu = Nomsu @@ -400,12 +363,12 @@ class NomsuCompiler error("Circular import, this loops forever: #{concat loop, " -> "}") insert _running_files, filename - if filename\match("%.lua$") + if match(filename, "%.lua$") file = assert(FILE_CACHE[filename], "Could not find file: #{filename}") ret = @run_lua(Lua(Source(filename, 1, #file), file)) - elseif filename\match("%.nom$") or filename\match("^/dev/fd/[012]$") + elseif match(filename, "%.nom$") or match(filename, "^/dev/fd/[012]$") if not @skip_precompiled -- Look for precompiled version - lua_filename = filename\gsub("%.nom$", ".lua") + lua_filename = gsub(filename, "%.nom$", ".lua") file = FILE_CACHE[lua_filename] if file ret = @run_lua(Lua(Source(filename, 1, #file), file)) @@ -426,7 +389,7 @@ class NomsuCompiler run_lua: (lua)=> assert(type(lua) != 'string', "Attempt to run lua string instead of Lua (object)") lua_string = tostring(lua) - run_lua_fn, err = load(lua_string, nil and tostring(lua.source), "t", @environment) + run_lua_fn, err = load(lua_string, tostring(lua.source), "t", @environment) if not run_lua_fn n = 1 fn = -> @@ -526,11 +489,8 @@ class NomsuCompiler make_tree = (t)-> unless AST.is_syntax_tree(t) return repr(t) - if t.value - return t.type.."("..repr(tostring t.source)..", "..repr(t.value)..")" - else - bits = [make_tree(bit) for bit in *t] - return t.type.."("..repr(tostring t.source)..", "..table.concat(bits, ", ")..")" + bits = [make_tree(bit) for bit in *t] + return t.type.."("..repr(tostring t.source)..", "..table.concat(bits, ", ")..")" Lua.Value tree.source, make_tree(tree[1]) when "Block" @@ -555,7 +515,7 @@ class NomsuCompiler string_buffer = "" bit_lua = @tree_to_lua(bit) unless bit_lua.is_value - src = ' '..tostring(@tree_to_nomsu(bit))\gsub('\n','\n ') + src = ' '..gsub(tostring(@tree_to_nomsu(bit)), '\n','\n ') line = "#{bit.source.filename}:#{pos_to_line(FILE_CACHE[bit.source.filename], bit.source.start)}" compile_error bit, "Cannot use:\n%s\nas a string interpolation value, since it's not an expression." @@ -582,8 +542,8 @@ class NomsuCompiler "Cannot use:\n%s\nas a list item, since it's not an expression." lua\append item_lua item_string = tostring(item_lua) - last_line = item_string\match("[^\n]*$") - if item_string\match("\n") + last_line = match(item_string, "[^\n]*$") + if match(item_string, "\n") line_length = #last_line else line_length += #last_line @@ -605,7 +565,7 @@ class NomsuCompiler lua\append entry_lua entry_lua_str = tostring(entry_lua) -- TODO: maybe make this more accurate? It's only a heuristic, so eh... - last_line = entry_lua_str\match("\n([^\n]*)$") + last_line = match(entry_lua_str, "\n([^\n]*)$") if last_line line_length = #last_line else @@ -630,10 +590,10 @@ class NomsuCompiler unless value_lua.is_value compile_error tree[2], "Cannot use:\n%s\nas a dict value, since it's not an expression." - key_str = tostring(key_lua)\match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=]) + key_str = match(tostring(key_lua), [=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=]) return if key_str Lua tree.source, key_str,"=",value_lua - elseif tostring(key_lua)\sub(1,1) == "[" + elseif sub(tostring(key_lua),1,1) == "[" -- NOTE: this *must* use a space after the [ to avoid freaking out -- Lua's parser if the inner expression is a long string. Lua -- parses x[[[y]]] as x("[y]"), not as x["y"] @@ -646,7 +606,7 @@ class NomsuCompiler unless lua.is_value compile_error tree[1], "Cannot index:\n%s\nsince it's not an expression." - first_char = tostring(lua)\sub(1,1) + first_char = sub(tostring(lua),1,1) if first_char == "{" or first_char == '"' or first_char == "[" lua\parenthesize! @@ -657,9 +617,9 @@ class NomsuCompiler compile_error key, "Cannot use:\n%s\nas an index, since it's not an expression." key_lua_str = tostring(key_lua) - if lua_id = key_lua_str\match("^['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]$") + if lua_id = match(key_lua_str, "^['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]$") lua\append ".#{lua_id}" - elseif key_lua_str\sub(1,1) == '[' + elseif sub(key_lua_str,1,1) == '[' -- NOTE: this *must* use a space after the [ to avoid freaking out -- Lua's parser if the inner expression is a long string. Lua -- parses x[[[y]]] as x("[y]"), not as x["y"] @@ -669,10 +629,10 @@ class NomsuCompiler return lua when "Number" - Lua.Value(tree.source, tostring(tree.value)) + Lua.Value(tree.source, tostring(tree[1])) when "Var" - Lua.Value(tree.source, string.as_lua_id(tree.value)) + Lua.Value(tree.source, string.as_lua_id(tree[1])) else error("Unknown type: #{tree.type}") @@ -713,7 +673,7 @@ class NomsuCompiler if arg_nomsu and #arg_nomsu < MAX_LINE if bit.type == "Action" if can_use_colon and i > 1 - nomsu\append next_space\match("[^ ]*"), ": ", arg_nomsu + nomsu\append match(next_space,"[^ ]*"), ": ", arg_nomsu next_space = "\n.." last_colon = i else @@ -737,7 +697,7 @@ class NomsuCompiler nomsu\append next_space, arg_nomsu next_space = "\n.." - if next_space == " " and #(tostring(nomsu)\match("[^\n]*$")) > MAX_LINE + if next_space == " " and #(match(tostring(nomsu),"[^\n]*$")) > MAX_LINE next_space = "\n.." return nomsu @@ -764,7 +724,7 @@ class NomsuCompiler nomsu\append line if i < #tree nomsu\append "\n" - if tostring(line)\match("\n") + if match(tostring(line), "\n") nomsu\append "\n" return nomsu @@ -774,7 +734,7 @@ class NomsuCompiler for bit in *tree if type(bit) == 'string' -- TODO: unescape better? - nomsu\append (bit\gsub("\\","\\\\")\gsub("\n","\\n")) + nomsu\append (gsub(gsub(bit,"\\","\\\\"),"\n","\\n")) else interp_nomsu = @tree_to_nomsu(bit, true) if interp_nomsu @@ -791,7 +751,8 @@ class NomsuCompiler nomsu = Nomsu(tree.source, '".."\n ') for i, bit in ipairs @ if type(bit) == 'string' - nomsu\append (bit\gsub("\\","\\\\")\gsub("\n","\n ")) + -- TODO: unescape better? + nomsu\append (gsub(gsub(bit,"\\","\\\\"),"\n","\\n")) else interp_nomsu = @tree_to_nomsu(bit, true) if interp_nomsu @@ -902,10 +863,10 @@ class NomsuCompiler return nomsu when "Number" - return Nomsu(tree.source, tostring(tree.value)) + return Nomsu(tree.source, tostring(tree[1])) when "Var" - return Nomsu(tree.source, "%", tree.value) + return Nomsu(tree.source, "%", tree[1]) else error("Unknown type: #{tree.type}") diff --git a/nomsu.peg b/nomsu.peg index 2de5292..2654f21 100644 --- a/nomsu.peg +++ b/nomsu.peg @@ -98,11 +98,11 @@ text_interpolation: inline_text_interpolation / ("\" indented_expression nodent "..") -number (Number): {| {:value: (("-"? (([0-9]+ "." [0-9]+) / ("." [0-9]+) / ([0-9]+)))-> tonumber) :} |} +number (Number): {| (("-"? (([0-9]+ "." [0-9]+) / ("." [0-9]+) / ([0-9]+)))-> tonumber) |} -- Variables can be nameless (i.e. just %) and can't contain operators like apostrophe -- which is a hack to allow %'s to parse as "%" and "' s" separately -variable (Var): "%" {| {:value: (%ident_char+ ((!"'" %operator_char+) / %ident_char+)*)? :} |} +variable (Var): "%" {| {(%ident_char+ ((!"'" %operator_char+) / %ident_char+)*)?} |} inline_list (List): !('[..]') diff --git a/nomsu_tree.lua b/nomsu_tree.lua index 5aeefe8..96e5031 100644 --- a/nomsu_tree.lua +++ b/nomsu_tree.lua @@ -12,17 +12,18 @@ AST.is_syntax_tree = function(n) return type(n) == 'table' and getmetatable(n) and AST[n.type] == getmetatable(n) end local Tree -Tree = function(name, leaf_or_branch, methods) +Tree = function(name, methods) local cls = methods or { } - local is_multi = leaf_or_branch == 'branch' do cls.type = name + cls.__class = cls + cls.__name = name cls.is_instance = function(self, x) return getmetatable(x) == self end cls.__index = cls cls.__tostring = function(self) - return tostring(self.name) .. "(#{@value and repr(@value) or table.concat([repr(v) for v in *@]), ', '})" + return tostring(self.name) .. "(#{table.concat([repr(v) for v in *@]), ', '})" end cls.map = function(self, fn) do @@ -31,21 +32,26 @@ Tree = function(name, leaf_or_branch, methods) return replacement end end - if self.value then + local made_changes, new_vals = false, { } + for i, v in ipairs(self) do + if AST.is_syntax_tree(v) then + do + local replacement = v:map(fn) + if replacement then + if replacement ~= v then + made_changes = true + v = replacement + end + end + end + end + new_vals[i] = v + end + if not (made_changes) then return self end - local new_vals - do - local _accum_0 = { } - 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 - _len_0 = _len_0 + 1 - end - new_vals = _accum_0 - end - return getmetatable(self)(self.source, unpack(new_vals)) + local replacement = getmetatable(self)(self.source, unpack(new_vals)) + return replacement end end AST[name] = setmetatable(cls, { @@ -57,18 +63,10 @@ Tree = function(name, leaf_or_branch, methods) source = Source:from_string(source) end assert(Source:is_instance(source)) - local inst - if is_multi then - inst = { - source = source, - ... - } - else - inst = { - source = source, - value = ... - } - end + local inst = { + source = source, + ... + } setmetatable(inst, self) if inst.__init then inst:__init() @@ -77,16 +75,16 @@ Tree = function(name, leaf_or_branch, methods) end }) end -Tree("Number", 'leaf') -Tree("Var", 'leaf') -Tree("Block", 'branch') -Tree("EscapedNomsu", 'branch') -Tree("Text", 'branch') -Tree("List", 'branch') -Tree("Dict", 'branch') -Tree("DictEntry", 'branch') -Tree("IndexChain", 'branch') -Tree("Action", 'branch', { +Tree("Number") +Tree("Var") +Tree("Block") +Tree("EscapedNomsu") +Tree("Text") +Tree("List") +Tree("Dict") +Tree("DictEntry") +Tree("IndexChain") +Tree("Action", { __init = function(self) local stub_bits do @@ -107,7 +105,7 @@ Tree("Action", 'branch', { local _len_0 = 1 for _index_0 = 1, #self do local a = self[_index_0] - _accum_0[_len_0] = type(a) == "string" and a or "%" .. tostring(a.value) + _accum_0[_len_0] = type(a) == "string" and a or "%" .. tostring(a[1]) _len_0 = _len_0 + 1 end return _accum_0 diff --git a/nomsu_tree.moon b/nomsu_tree.moon index fe1c4f4..315571a 100644 --- a/nomsu_tree.moon +++ b/nomsu_tree.moon @@ -9,19 +9,28 @@ AST.is_syntax_tree = (n)-> type(n) == 'table' and getmetatable(n) and AST[n.type] == getmetatable(n) -- Helper method: -Tree = (name, leaf_or_branch, methods)-> +Tree = (name, methods)-> cls = methods or {} - is_multi = leaf_or_branch == 'branch' with cls .type = name + .__class = cls + .__name = name .is_instance = (x)=> getmetatable(x) == @ .__index = cls - .__tostring = => "#{@name}(#{@value and repr(@value) or table.concat([repr(v) for v in *@]), ', '})" + .__tostring = => "#{@name}(#{table.concat([repr(v) for v in *@]), ', '})" .map = (fn)=> if replacement = fn(@) then return replacement - if @value then return @ - new_vals = [v.map and v\map(fn) or v for v in *@] - return getmetatable(self)(@source, unpack(new_vals)) + made_changes, new_vals = false, {} + for i,v in ipairs @ + if AST.is_syntax_tree(v) + if replacement = v\map(fn) + if replacement ~= v + made_changes = true + v = replacement + new_vals[i] = v + return @ unless made_changes + replacement = getmetatable(self)(@source, unpack(new_vals)) + return replacement AST[name] = setmetatable cls, __tostring: => @name @@ -29,25 +38,25 @@ Tree = (name, leaf_or_branch, methods)-> if type(source) == 'string' source = Source\from_string(source) assert(Source\is_instance(source)) - inst = if is_multi then {:source, ...} else {:source, value:...} + inst = {:source, ...} setmetatable(inst, @) if inst.__init then inst\__init! return inst -Tree "Number", 'leaf' -Tree "Var", 'leaf' -Tree "Block", 'branch' -Tree "EscapedNomsu", 'branch' -Tree "Text", 'branch' -Tree "List", 'branch' -Tree "Dict", 'branch' -Tree "DictEntry", 'branch' -Tree "IndexChain", 'branch' -Tree "Action", 'branch', +Tree "Number" +Tree "Var" +Tree "Block" +Tree "EscapedNomsu" +Tree "Text" +Tree "List" +Tree "Dict" +Tree "DictEntry" +Tree "IndexChain" +Tree "Action", __init: => stub_bits = [type(a) == 'string' and a or '%' for a in *@] @stub = concat stub_bits, " " get_spec: => - concat [type(a) == "string" and a or "%#{a.value}" for a in *@], " " + concat [type(a) == "string" and a or "%#{a[1]}" for a in *@], " " return AST diff --git a/tests/control_flow.nom b/tests/control_flow.nom index 886b59c..5553811 100644 --- a/tests/control_flow.nom +++ b/tests/control_flow.nom @@ -193,14 +193,15 @@ assume return %n ..= 6 -%nums <- [] -for % in - values - -> 4 - -> 5 - -> 6 -.. - add % to %nums +# + %nums <- [] + for % in + values + -> 4 + -> 5 + -> 6 + .. + add % to %nums -assume (%nums = [4,5,6]) or barf "Coroutine iteration failed" + assume (%nums = [4,5,6]) or barf "Coroutine iteration failed"