Lots of optimizations and simplifications, especially towards getting

better performance on luajit.
This commit is contained in:
Bruce Hill 2018-06-12 18:04:18 -07:00
parent b5fb8933af
commit 3c510e4ee5
12 changed files with 188 additions and 290 deletions

View File

@ -187,7 +187,7 @@ do
for _index_0 = 1, #vars do for _index_0 = 1, #vars do
local var = vars[_index_0] local var = vars[_index_0]
assert(var.type == "Var") assert(var.type == "Var")
removals[var.value] = true removals[var[1]] = true
end end
local stack = { local stack = {
self self
@ -196,7 +196,7 @@ do
local lua local lua
lua, stack[#stack] = stack[#stack], nil lua, stack[#stack] = stack[#stack], nil
for i = #lua.free_vars, 1, -1 do 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) remove(lua.free_vars, i)
end end
end end
@ -264,7 +264,7 @@ do
local _len_0 = 1 local _len_0 = 1
for _index_0 = 1, #to_declare do for _index_0 = 1, #to_declare do
local v = to_declare[_index_0] 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 _len_0 = _len_0 + 1
end end
return _accum_0 return _accum_0

View File

@ -109,13 +109,13 @@ class Lua extends Code
removals = {} removals = {}
for var in *vars for var in *vars
assert(var.type == "Var") assert(var.type == "Var")
removals[var.value] = true removals[var[1]] = true
stack = {self} stack = {self}
while #stack > 0 while #stack > 0
lua, stack[#stack] = stack[#stack], nil lua, stack[#stack] = stack[#stack], nil
for i=#lua.free_vars,1,-1 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 remove lua.free_vars, i
for b in *lua.bits for b in *lua.bits
if type(b) != 'string' if type(b) != 'string'
@ -147,7 +147,7 @@ class Lua extends Code
gather_from self gather_from self
if #to_declare > 0 if #to_declare > 0
@remove_free_vars to_declare @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 return to_declare
__tostring: => __tostring: =>

View File

@ -6,11 +6,11 @@ moonc *.moon
rm -f core/*.lua lib/*.lua rm -f core/*.lua lib/*.lua
for file in core/*.nom; do for file in core/*.nom; do
printf "Compiling $file ..." 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." echo "done."
done done
for file in lib/*.nom; do for file in lib/*.nom; do
printf "Compiling $file ..." 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." echo "done."
done done

View File

@ -188,14 +188,14 @@ immediately
%body has subtree % where %body has subtree % where
(%.type = "Action") and (%.type = "Action") and
(%.stub is "do next %") 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 (Lua "\n ::continue_\(%var as lua identifier)::")
to %lua write "\nend --foreach-loop" to %lua write "\nend --foreach-loop"
if if
%body has subtree % where %body has subtree % where
(%.type = "Action") and (%.type = "Action") and
(%.stub is "stop %") and (%.stub is "stop %") and
%.2.value = %var.value %.2.(1) = %var.(1)
.. ..
%lua <- %lua <-
Lua ".." Lua ".."
@ -222,14 +222,14 @@ immediately
%body has subtree % where %body has subtree % where
(%.type = "Action") and (%.type = "Action") and
(%.stub is "do next %") and (%.stub is "do next %") and
%.3.value = %key.value %.3.(1) = %key.(1)
..: to %lua write (Lua "\n ::continue_\(%key as lua identifier)::") ..: to %lua write (Lua "\n ::continue_\(%key as lua identifier)::")
if if
%body has subtree % where %body has subtree % where
(%.type = "Action") and (%.type = "Action") and
(%.stub is "do next %") 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 (Lua "\n ::continue_\(%value as lua identifier)::")
to %lua write "\nend --foreach-loop" to %lua write "\nend --foreach-loop"
@ -238,14 +238,14 @@ immediately
%body has subtree % where %body has subtree % where
(%.type = "Action") and (%.type = "Action") and
(%.stub is "stop %") and (%.stub is "stop %") and
%.2.value = %key.value %.2.(1) = %key.(1)
..: to %stop_labels write "\n::stop_\(%key as lua identifier)::" ..: to %stop_labels write "\n::stop_\(%key as lua identifier)::"
if if
%body has subtree % where %body has subtree % where
(%.type = "Action") and (%.type = "Action") and
(%.stub is "stop %") and (%.stub is "stop %") and
%.2.value = %value.value %.2.(1) = %value.(1)
..: to %stop_labels write "\n::stop_\(%value as lua identifier)::" ..: to %stop_labels write "\n::stop_\(%value as lua identifier)::"
if: (length of %stop_labels) > 0 if: (length of %stop_labels) > 0

View File

@ -82,7 +82,7 @@ immediately
for i,tok in ipairs(\%shorthand[1]) do for i,tok in ipairs(\%shorthand[1]) do
if tok.type == "Var" then if tok.type == "Var" then
local lua_var = tostring(nomsu:tree_to_lua(tok)) local lua_var = tostring(nomsu:tree_to_lua(tok))
replacements[tok.value] = lua_var replacements[tok[1]] = lua_var
lua:append(", ", lua_var) lua:append(", ", lua_var)
end end
end end
@ -90,12 +90,10 @@ immediately
local function make_tree(t) local function make_tree(t)
if type(t) ~= 'table' and type(t) ~= 'userdata' then if type(t) ~= 'table' and type(t) ~= 'userdata' then
return repr(t) return repr(t)
elseif t.type == 'Var' and replacements[t.value] then elseif t.type == 'Var' and replacements[t[1]] then
return replacements[t.value] return replacements[t[1]]
elseif t.type == 'Var' then elseif t.type == 'Var' then
return t.type.."("..repr(tostring(t.source))..", "..repr(t.value.."#"..tostring(MANGLE_INDEX))..")" return t.type.."("..repr(tostring(t.source))..", "..repr(t[1].."#"..tostring(MANGLE_INDEX))..")"
elseif t.value then
return t.type.."("..repr(tostring(t.source))..", "..repr(t.value)..")"
else else
local bits = {repr(tostring(t.source))} local bits = {repr(tostring(t.source))}
for i, entry in ipairs(t) do for i, entry in ipairs(t) do

View File

@ -160,8 +160,7 @@ immediately
# Unary operators # Unary operators
compile [- %] to: Lua value "(- \(% as lua expr))" compile [- %] to: Lua value "(- \(% as lua expr))"
compile [not %] to: Lua value "(not \(% 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 "(#\(%list as lua expr))"
compile [length of %list] to: Lua value "len(\(%list as lua expr))"
# Update operators # Update operators
immediately immediately

174
nomsu.lua
View File

@ -1,33 +1,10 @@
local _pairs, _ipairs = pairs, ipairs local _pairs, _ipairs = pairs, ipairs
if jit then if jit then
package.cpath = "./luajit_lpeg/?.so;" .. package.cpath package.cpath = "./luajit_lpeg/?.so;" .. package.cpath
lpeg = require('lpeg') package.path = "./luajit_lpeg/?.lua;" .. package.path
bit32 = require('bit') 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 end
lpeg = require('lpeg')
re = require('re') re = require('re')
lpeg.setmaxstack(10000) lpeg.setmaxstack(10000)
local P, R, V, S, Cg, C, Cp, B, Cmt, Carg local P, R, V, S, Cg, C, Cp, B, Cmt, Carg
@ -53,6 +30,11 @@ do
local _obj_0 = table local _obj_0 = table
insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat
end 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 debug_getinfo = debug.getinfo
local Nomsu, Lua, Source local Nomsu, Lua, Source
do do
@ -61,11 +43,11 @@ do
end end
local STDIN, STDOUT, STDERR = "/dev/fd/0", "/dev/fd/1", "/dev/fd/2" local STDIN, STDOUT, STDERR = "/dev/fd/0", "/dev/fd/1", "/dev/fd/2"
string.as_lua_id = function(str) string.as_lua_id = function(str)
return "_" .. (str:gsub("%W", function(c) return "_" .. (gsub(str, "%W", function(c)
if c == "_" then if c == "_" then
return "__" return "__"
else else
return ("_%x"):format(c:byte()) return format("_%x", byte(c))
end end
end)) end))
end end
@ -91,10 +73,13 @@ iterate_single = function(item, prev)
end end
local all_files local all_files
all_files = function(path) 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 return iterate_single, path
end end
path = path:gsub("\\", "\\\\"):gsub("`", ""):gsub('"', '\\"'):gsub("$", "") path = gsub(path, "\\", "\\\\")
path = gsub(path, "`", "")
path = gsub(path, '"', '\\"')
path = gsub(path, "$", "")
return coroutine.wrap(function() return coroutine.wrap(function()
local f = io.popen('find -L "' .. path .. '" -not -path "*/\\.*" -type f -name "*.nom"') local f = io.popen('find -L "' .. path .. '" -not -path "*/\\.*" -type f -name "*.nom"')
for line in f:lines() do for line in f:lines() do
@ -153,9 +138,9 @@ do
return ret return ret
end end
if type(i) == 'number' then if type(i) == 'number' then
return string.sub(self, i, i) return sub(self, i, i)
elseif type(i) == 'table' then elseif type(i) == 'table' then
return string.sub(self, i[1], i[2]) return sub(self, i[1], i[2])
end end
end 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.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.ident_char = R("az", "AZ", "09") + P("_") + _with_0.utf8_char
_with_0.indent = Cmt(Carg(1), function(self, start, userdata) _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 userdata.indent = userdata.indent + 4
return start + userdata.indent return start + userdata.indent
end end
end) end)
_with_0.dedent = Cmt(Carg(1), function(self, start, userdata) _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 userdata.indent = userdata.indent - 4
return start return start
end end
end) end)
_with_0.nodent = Cmt(Carg(1), function(self, start, userdata) _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 return start + userdata.indent
end end
end) end)
@ -403,12 +388,12 @@ do
end end
end end
insert(_running_files, filename) 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)) local file = assert(FILE_CACHE[filename], "Could not find file: " .. tostring(filename))
ret = self:run_lua(Lua(Source(filename, 1, #file), file)) 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 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] local file = FILE_CACHE[lua_filename]
if file then if file then
ret = self:run_lua(Lua(Source(filename, 1, #file), file)) ret = self:run_lua(Lua(Source(filename, 1, #file), file))
@ -439,7 +424,7 @@ do
run_lua = function(self, lua) run_lua = function(self, lua)
assert(type(lua) ~= 'string', "Attempt to run lua string instead of Lua (object)") assert(type(lua) ~= 'string', "Attempt to run lua string instead of Lua (object)")
local lua_string = tostring(lua) 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 if not run_lua_fn then
local n = 1 local n = 1
local fn local fn
@ -590,22 +575,18 @@ do
if not (AST.is_syntax_tree(t)) then if not (AST.is_syntax_tree(t)) then
return repr(t) return repr(t)
end end
if t.value then local bits
return t.type .. "(" .. repr(tostring(t.source)) .. ", " .. repr(t.value) .. ")" do
else local _accum_0 = { }
local bits local _len_0 = 1
do for _index_0 = 1, #t do
local _accum_0 = { } local bit = t[_index_0]
local _len_0 = 1 _accum_0[_len_0] = make_tree(bit)
for _index_0 = 1, #t do _len_0 = _len_0 + 1
local bit = t[_index_0]
_accum_0[_len_0] = make_tree(bit)
_len_0 = _len_0 + 1
end
bits = _accum_0
end end
return t.type .. "(" .. repr(tostring(t.source)) .. ", " .. table.concat(bits, ", ") .. ")" bits = _accum_0
end end
return t.type .. "(" .. repr(tostring(t.source)) .. ", " .. table.concat(bits, ", ") .. ")"
end end
return Lua.Value(tree.source, make_tree(tree[1])) return Lua.Value(tree.source, make_tree(tree[1]))
elseif "Block" == _exp_0 then elseif "Block" == _exp_0 then
@ -638,7 +619,7 @@ do
end end
local bit_lua = self:tree_to_lua(bit) local bit_lua = self:tree_to_lua(bit)
if not (bit_lua.is_value) then 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)) 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.") compile_error(bit, "Cannot use:\n%s\nas a string interpolation value, since it's not an expression.")
end end
@ -675,8 +656,8 @@ do
end end
lua:append(item_lua) lua:append(item_lua)
local item_string = tostring(item_lua) local item_string = tostring(item_lua)
local last_line = item_string:match("[^\n]*$") local last_line = match(item_string, "[^\n]*$")
if item_string:match("\n") then if match(item_string, "\n") then
line_length = #last_line line_length = #last_line
else else
line_length = line_length + #last_line line_length = line_length + #last_line
@ -700,7 +681,7 @@ do
local entry_lua = self:tree_to_lua(entry) local entry_lua = self:tree_to_lua(entry)
lua:append(entry_lua) lua:append(entry_lua)
local entry_lua_str = tostring(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 if last_line then
line_length = #last_line line_length = #last_line
else else
@ -728,10 +709,10 @@ do
if not (value_lua.is_value) then 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.") compile_error(tree[2], "Cannot use:\n%s\nas a dict value, since it's not an expression.")
end 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 if key_str then
return Lua(tree.source, key_str, "=", value_lua) 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) return Lua(tree.source, "[ ", key_lua, "]=", value_lua)
else else
return Lua(tree.source, "[", key_lua, "]=", value_lua) return Lua(tree.source, "[", key_lua, "]=", value_lua)
@ -741,7 +722,7 @@ do
if not (lua.is_value) then if not (lua.is_value) then
compile_error(tree[1], "Cannot index:\n%s\nsince it's not an expression.") compile_error(tree[1], "Cannot index:\n%s\nsince it's not an expression.")
end 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 if first_char == "{" or first_char == '"' or first_char == "[" then
lua:parenthesize() lua:parenthesize()
end end
@ -753,10 +734,10 @@ do
end end
local key_lua_str = tostring(key_lua) local key_lua_str = tostring(key_lua)
do 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 if lua_id then
lua:append("." .. tostring(lua_id)) 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, " ]") lua:append("[ ", key_lua, " ]")
else else
lua:append("[", key_lua, "]") lua:append("[", key_lua, "]")
@ -765,9 +746,9 @@ do
end end
return lua return lua
elseif "Number" == _exp_0 then 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 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 else
return error("Unknown type: " .. tostring(tree.type)) return error("Unknown type: " .. tostring(tree.type))
end end
@ -824,7 +805,7 @@ do
if arg_nomsu and #arg_nomsu < MAX_LINE then if arg_nomsu and #arg_nomsu < MAX_LINE then
if bit.type == "Action" then if bit.type == "Action" then
if can_use_colon and i > 1 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.." next_space = "\n.."
last_colon = i last_colon = i
else else
@ -853,7 +834,7 @@ do
nomsu:append(next_space, arg_nomsu) nomsu:append(next_space, arg_nomsu)
next_space = "\n.." next_space = "\n.."
end 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.." next_space = "\n.."
end end
end end
@ -888,7 +869,7 @@ do
nomsu:append(line) nomsu:append(line)
if i < #tree then if i < #tree then
nomsu:append("\n") nomsu:append("\n")
if tostring(line):match("\n") then if match(tostring(line), "\n") then
nomsu:append("\n") nomsu:append("\n")
end end
end end
@ -900,7 +881,7 @@ do
for _index_0 = 1, #tree do for _index_0 = 1, #tree do
local bit = tree[_index_0] local bit = tree[_index_0]
if type(bit) == 'string' then if type(bit) == 'string' then
nomsu:append((bit:gsub("\\", "\\\\"):gsub("\n", "\\n"))) nomsu:append((gsub(gsub(bit, "\\", "\\\\"), "\n", "\\n")))
else else
local interp_nomsu = self:tree_to_nomsu(bit, true) local interp_nomsu = self:tree_to_nomsu(bit, true)
if interp_nomsu then if interp_nomsu then
@ -923,7 +904,7 @@ do
local nomsu = Nomsu(tree.source, '".."\n ') local nomsu = Nomsu(tree.source, '".."\n ')
for i, bit in ipairs(self) do for i, bit in ipairs(self) do
if type(bit) == 'string' then if type(bit) == 'string' then
nomsu:append((bit:gsub("\\", "\\\\"):gsub("\n", "\n "))) nomsu:append((gsub(gsub(bit, "\\", "\\\\"), "\n", "\\n")))
else else
local interp_nomsu = self:tree_to_nomsu(bit, true) local interp_nomsu = self:tree_to_nomsu(bit, true)
if interp_nomsu then if interp_nomsu then
@ -1085,9 +1066,9 @@ do
end end
return nomsu return nomsu
elseif "Number" == _exp_0 then elseif "Number" == _exp_0 then
return Nomsu(tree.source, tostring(tree.value)) return Nomsu(tree.source, tostring(tree[1]))
elseif "Var" == _exp_0 then elseif "Var" == _exp_0 then
return Nomsu(tree.source, "%", tree.value) return Nomsu(tree.source, "%", tree[1])
else else
return error("Unknown type: " .. tostring(tree.type)) return error("Unknown type: " .. tostring(tree.type))
end end
@ -1268,60 +1249,11 @@ do
math = math, math = math,
io = io, io = io,
load = load, load = load,
pairs = pairs,
ipairs = ipairs,
list = list, list = list,
dict = dict 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 for k, v in pairs(AST) do
self.environment[k] = v self.environment[k] = v
end end

View File

@ -14,27 +14,12 @@ export lpeg, re
_pairs, _ipairs = pairs, ipairs _pairs, _ipairs = pairs, ipairs
if jit if jit
package.cpath = "./luajit_lpeg/?.so;"..package.cpath package.cpath = "./luajit_lpeg/?.so;"..package.cpath
--package.path = "./LPegLJ/src/?.lua;"..package.path package.path = "./luajit_lpeg/?.lua;"..package.path
--lpeg = require "lpeglj"
lpeg = require 'lpeg'
export bit32 export bit32
bit32 = require('bit') bit32 = require('bit')
export pairs, ipairs lpeg = require 'lpeg'
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'
re = require 're' re = require 're'
lpeg.setmaxstack 10000 lpeg.setmaxstack 10000
{:P,:R,:V,:S,:Cg,:C,:Cp,:B,:Cmt,:Carg} = lpeg {:P,:R,:V,:S,:Cg,:C,:Cp,:B,:Cmt,:Carg} = lpeg
@ -45,12 +30,13 @@ colors = setmetatable({}, {__index:->""})
export colored export colored
colored = setmetatable({}, {__index:(_,color)-> ((msg)-> colors[color]..tostring(msg or '')..colors.reset)}) colored = setmetatable({}, {__index:(_,color)-> ((msg)-> colors[color]..tostring(msg or '')..colors.reset)})
{:insert, :remove, :concat} = table {:insert, :remove, :concat} = table
{:match, :sub, :rep, :gsub, :format, :byte, :match} = string
debug_getinfo = debug.getinfo debug_getinfo = debug.getinfo
{:Nomsu, :Lua, :Source} = require "code_obj" {:Nomsu, :Lua, :Source} = require "code_obj"
STDIN, STDOUT, STDERR = "/dev/fd/0", "/dev/fd/1", "/dev/fd/2" STDIN, STDOUT, STDERR = "/dev/fd/0", "/dev/fd/1", "/dev/fd/2"
string.as_lua_id = (str)-> 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: -- TODO:
-- consider non-linear codegen, rather than doing thunks for things like comprehensions -- 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 iterate_single = (item, prev) -> if item == prev then nil else item
all_files = (path)-> all_files = (path)->
-- Sanitize 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 return iterate_single, path
-- TODO: improve sanitization -- TODO: improve sanitization
path = path\gsub("\\","\\\\")\gsub("`","")\gsub('"','\\"')\gsub("$","") path = gsub(path,"\\","\\\\")
path = gsub(path,"`","")
path = gsub(path,'"','\\"')
path = gsub(path,"$","")
return coroutine.wrap -> return coroutine.wrap ->
f = io.popen('find -L "'..path..'" -not -path "*/\\.*" -type f -name "*.nom"') f = io.popen('find -L "'..path..'" -not -path "*/\\.*" -type f -name "*.nom"')
for line in f\lines! for line in f\lines!
@ -128,8 +117,8 @@ do
STRING_METATABLE.__index = (i)=> STRING_METATABLE.__index = (i)=>
ret = string[i] ret = string[i]
if ret != nil then return ret if ret != nil then return ret
if type(i) == 'number' then return string.sub(@, i, i) if type(i) == 'number' then return sub(@, i, i)
elseif type(i) == 'table' then return string.sub(@, i[1], i[2]) elseif type(i) == 'table' then return sub(@, i[1], i[2])
AST = require "nomsu_tree" AST = require "nomsu_tree"
@ -153,19 +142,19 @@ NOMSU_DEFS = with {}
-- If the line begins with #indent+4 spaces, the pattern matches *those* spaces -- If the line begins with #indent+4 spaces, the pattern matches *those* spaces
-- and adds them to the stack (not any more). -- and adds them to the stack (not any more).
.indent = Cmt Carg(1), (start, userdata)=> .indent = Cmt Carg(1), (start, userdata)=>
if #@match("^[ ]*", start) >= userdata.indent + 4 if #match(@, "^[ ]*", start) >= userdata.indent + 4
userdata.indent += 4 userdata.indent += 4
return start + userdata.indent return start + userdata.indent
-- If the number of leading space characters is <= the number of space on the top of the -- 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. -- stack minus 4, this pattern matches and pops off the top of the stack exactly once.
.dedent = Cmt Carg(1), (start, userdata)=> .dedent = Cmt Carg(1), (start, userdata)=>
if #@match("^[ ]*", start) <= userdata.indent - 4 if #match(@, "^[ ]*", start) <= userdata.indent - 4
userdata.indent -= 4 userdata.indent -= 4
return start return start
-- If the number of leading space characters is >= the number on the top of the -- 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. -- stack, this pattern matches and does not modify the stack.
.nodent = Cmt Carg(1), (start, userdata)=> .nodent = Cmt Carg(1), (start, userdata)=>
if #@match("^[ ]*", start) >= userdata.indent if #match(@, "^[ ]*", start) >= userdata.indent
return start + userdata.indent return start + userdata.indent
.userdata = Carg(1) .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..." seen_errors[start_pos+1] = colored.bright colored.yellow colored.onred "Too many errors, canceling parsing..."
return #src+1 return #src+1
err_pos = start_pos 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) line_no = pos_to_line(src, err_pos)
src = FILE_CACHE[userdata.source.filename] src = FILE_CACHE[userdata.source.filename]
line_starts = LINE_STARTS[src] line_starts = LINE_STARTS[src]
@ -276,34 +263,10 @@ class NomsuCompiler
:error, :package, :os, :require, :tonumber, :tostring, :string, :xpcall, :module, :error, :package, :os, :require, :tonumber, :tostring, :string, :xpcall, :module,
:print, :loadfile, :rawset, :_VERSION, :collectgarbage, :rawget, :bit32, :rawlen, :print, :loadfile, :rawset, :_VERSION, :collectgarbage, :rawget, :bit32, :rawlen,
:table, :assert, :dofile, :loadstring, :type, :select, :debug, :math, :io, :load, :table, :assert, :dofile, :loadstring, :type, :select, :debug, :math, :io, :load,
:pairs, :ipairs,
-- Nomsu types: -- Nomsu types:
:list, :dict, :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 for k,v in pairs(AST) do @environment[k] = v
@environment.Lua = Lua @environment.Lua = Lua
@environment.Nomsu = Nomsu @environment.Nomsu = Nomsu
@ -400,12 +363,12 @@ class NomsuCompiler
error("Circular import, this loops forever: #{concat loop, " -> "}") error("Circular import, this loops forever: #{concat loop, " -> "}")
insert _running_files, filename insert _running_files, filename
if filename\match("%.lua$") if match(filename, "%.lua$")
file = assert(FILE_CACHE[filename], "Could not find file: #{filename}") file = assert(FILE_CACHE[filename], "Could not find file: #{filename}")
ret = @run_lua(Lua(Source(filename, 1, #file), file)) 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 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] file = FILE_CACHE[lua_filename]
if file if file
ret = @run_lua(Lua(Source(filename, 1, #file), file)) ret = @run_lua(Lua(Source(filename, 1, #file), file))
@ -426,7 +389,7 @@ class NomsuCompiler
run_lua: (lua)=> run_lua: (lua)=>
assert(type(lua) != 'string', "Attempt to run lua string instead of Lua (object)") assert(type(lua) != 'string', "Attempt to run lua string instead of Lua (object)")
lua_string = tostring(lua) 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 if not run_lua_fn
n = 1 n = 1
fn = -> fn = ->
@ -526,11 +489,8 @@ class NomsuCompiler
make_tree = (t)-> make_tree = (t)->
unless AST.is_syntax_tree(t) unless AST.is_syntax_tree(t)
return repr(t) return repr(t)
if t.value bits = [make_tree(bit) for bit in *t]
return t.type.."("..repr(tostring t.source)..", "..repr(t.value)..")" return t.type.."("..repr(tostring t.source)..", "..table.concat(bits, ", ")..")"
else
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]) Lua.Value tree.source, make_tree(tree[1])
when "Block" when "Block"
@ -555,7 +515,7 @@ class NomsuCompiler
string_buffer = "" string_buffer = ""
bit_lua = @tree_to_lua(bit) bit_lua = @tree_to_lua(bit)
unless bit_lua.is_value 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)}" line = "#{bit.source.filename}:#{pos_to_line(FILE_CACHE[bit.source.filename], bit.source.start)}"
compile_error bit, compile_error bit,
"Cannot use:\n%s\nas a string interpolation value, since it's not an expression." "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." "Cannot use:\n%s\nas a list item, since it's not an expression."
lua\append item_lua lua\append item_lua
item_string = tostring(item_lua) item_string = tostring(item_lua)
last_line = item_string\match("[^\n]*$") last_line = match(item_string, "[^\n]*$")
if item_string\match("\n") if match(item_string, "\n")
line_length = #last_line line_length = #last_line
else else
line_length += #last_line line_length += #last_line
@ -605,7 +565,7 @@ class NomsuCompiler
lua\append entry_lua lua\append entry_lua
entry_lua_str = tostring(entry_lua) entry_lua_str = tostring(entry_lua)
-- TODO: maybe make this more accurate? It's only a heuristic, so eh... -- 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 if last_line
line_length = #last_line line_length = #last_line
else else
@ -630,10 +590,10 @@ class NomsuCompiler
unless value_lua.is_value unless value_lua.is_value
compile_error tree[2], compile_error tree[2],
"Cannot use:\n%s\nas a dict value, since it's not an expression." "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 return if key_str
Lua tree.source, key_str,"=",value_lua 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 -- NOTE: this *must* use a space after the [ to avoid freaking out
-- Lua's parser if the inner expression is a long string. Lua -- Lua's parser if the inner expression is a long string. Lua
-- parses x[[[y]]] as x("[y]"), not as x["y"] -- parses x[[[y]]] as x("[y]"), not as x["y"]
@ -646,7 +606,7 @@ class NomsuCompiler
unless lua.is_value unless lua.is_value
compile_error tree[1], compile_error tree[1],
"Cannot index:\n%s\nsince it's not an expression." "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 == "[" if first_char == "{" or first_char == '"' or first_char == "["
lua\parenthesize! lua\parenthesize!
@ -657,9 +617,9 @@ class NomsuCompiler
compile_error key, compile_error key,
"Cannot use:\n%s\nas an index, since it's not an expression." "Cannot use:\n%s\nas an index, since it's not an expression."
key_lua_str = tostring(key_lua) 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}" 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 -- NOTE: this *must* use a space after the [ to avoid freaking out
-- Lua's parser if the inner expression is a long string. Lua -- Lua's parser if the inner expression is a long string. Lua
-- parses x[[[y]]] as x("[y]"), not as x["y"] -- parses x[[[y]]] as x("[y]"), not as x["y"]
@ -669,10 +629,10 @@ class NomsuCompiler
return lua return lua
when "Number" when "Number"
Lua.Value(tree.source, tostring(tree.value)) Lua.Value(tree.source, tostring(tree[1]))
when "Var" when "Var"
Lua.Value(tree.source, string.as_lua_id(tree.value)) Lua.Value(tree.source, string.as_lua_id(tree[1]))
else else
error("Unknown type: #{tree.type}") error("Unknown type: #{tree.type}")
@ -713,7 +673,7 @@ class NomsuCompiler
if arg_nomsu and #arg_nomsu < MAX_LINE if arg_nomsu and #arg_nomsu < MAX_LINE
if bit.type == "Action" if bit.type == "Action"
if can_use_colon and i > 1 if can_use_colon and i > 1
nomsu\append next_space\match("[^ ]*"), ": ", arg_nomsu nomsu\append match(next_space,"[^ ]*"), ": ", arg_nomsu
next_space = "\n.." next_space = "\n.."
last_colon = i last_colon = i
else else
@ -737,7 +697,7 @@ class NomsuCompiler
nomsu\append next_space, arg_nomsu nomsu\append next_space, arg_nomsu
next_space = "\n.." 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.." next_space = "\n.."
return nomsu return nomsu
@ -764,7 +724,7 @@ class NomsuCompiler
nomsu\append line nomsu\append line
if i < #tree if i < #tree
nomsu\append "\n" nomsu\append "\n"
if tostring(line)\match("\n") if match(tostring(line), "\n")
nomsu\append "\n" nomsu\append "\n"
return nomsu return nomsu
@ -774,7 +734,7 @@ class NomsuCompiler
for bit in *tree for bit in *tree
if type(bit) == 'string' if type(bit) == 'string'
-- TODO: unescape better? -- TODO: unescape better?
nomsu\append (bit\gsub("\\","\\\\")\gsub("\n","\\n")) nomsu\append (gsub(gsub(bit,"\\","\\\\"),"\n","\\n"))
else else
interp_nomsu = @tree_to_nomsu(bit, true) interp_nomsu = @tree_to_nomsu(bit, true)
if interp_nomsu if interp_nomsu
@ -791,7 +751,8 @@ class NomsuCompiler
nomsu = Nomsu(tree.source, '".."\n ') nomsu = Nomsu(tree.source, '".."\n ')
for i, bit in ipairs @ for i, bit in ipairs @
if type(bit) == 'string' if type(bit) == 'string'
nomsu\append (bit\gsub("\\","\\\\")\gsub("\n","\n ")) -- TODO: unescape better?
nomsu\append (gsub(gsub(bit,"\\","\\\\"),"\n","\\n"))
else else
interp_nomsu = @tree_to_nomsu(bit, true) interp_nomsu = @tree_to_nomsu(bit, true)
if interp_nomsu if interp_nomsu
@ -902,10 +863,10 @@ class NomsuCompiler
return nomsu return nomsu
when "Number" when "Number"
return Nomsu(tree.source, tostring(tree.value)) return Nomsu(tree.source, tostring(tree[1]))
when "Var" when "Var"
return Nomsu(tree.source, "%", tree.value) return Nomsu(tree.source, "%", tree[1])
else else
error("Unknown type: #{tree.type}") error("Unknown type: #{tree.type}")

View File

@ -98,11 +98,11 @@ text_interpolation:
inline_text_interpolation / inline_text_interpolation /
("\" indented_expression nodent "..") ("\" 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 -- 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 -- 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): inline_list (List):
!('[..]') !('[..]')

View File

@ -12,17 +12,18 @@ AST.is_syntax_tree = function(n)
return type(n) == 'table' and getmetatable(n) and AST[n.type] == getmetatable(n) return type(n) == 'table' and getmetatable(n) and AST[n.type] == getmetatable(n)
end end
local Tree local Tree
Tree = function(name, leaf_or_branch, methods) Tree = function(name, methods)
local cls = methods or { } local cls = methods or { }
local is_multi = leaf_or_branch == 'branch'
do do
cls.type = name cls.type = name
cls.__class = cls
cls.__name = name
cls.is_instance = function(self, x) cls.is_instance = function(self, x)
return getmetatable(x) == self return getmetatable(x) == self
end end
cls.__index = cls cls.__index = cls
cls.__tostring = function(self) 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 end
cls.map = function(self, fn) cls.map = function(self, fn)
do do
@ -31,21 +32,26 @@ Tree = function(name, leaf_or_branch, methods)
return replacement return replacement
end end
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 return self
end end
local new_vals local replacement = getmetatable(self)(self.source, unpack(new_vals))
do return replacement
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))
end end
end end
AST[name] = setmetatable(cls, { AST[name] = setmetatable(cls, {
@ -57,18 +63,10 @@ Tree = function(name, leaf_or_branch, methods)
source = Source:from_string(source) source = Source:from_string(source)
end end
assert(Source:is_instance(source)) assert(Source:is_instance(source))
local inst local inst = {
if is_multi then source = source,
inst = { ...
source = source, }
...
}
else
inst = {
source = source,
value = ...
}
end
setmetatable(inst, self) setmetatable(inst, self)
if inst.__init then if inst.__init then
inst:__init() inst:__init()
@ -77,16 +75,16 @@ Tree = function(name, leaf_or_branch, methods)
end end
}) })
end end
Tree("Number", 'leaf') Tree("Number")
Tree("Var", 'leaf') Tree("Var")
Tree("Block", 'branch') Tree("Block")
Tree("EscapedNomsu", 'branch') Tree("EscapedNomsu")
Tree("Text", 'branch') Tree("Text")
Tree("List", 'branch') Tree("List")
Tree("Dict", 'branch') Tree("Dict")
Tree("DictEntry", 'branch') Tree("DictEntry")
Tree("IndexChain", 'branch') Tree("IndexChain")
Tree("Action", 'branch', { Tree("Action", {
__init = function(self) __init = function(self)
local stub_bits local stub_bits
do do
@ -107,7 +105,7 @@ Tree("Action", 'branch', {
local _len_0 = 1 local _len_0 = 1
for _index_0 = 1, #self do for _index_0 = 1, #self do
local a = self[_index_0] 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 _len_0 = _len_0 + 1
end end
return _accum_0 return _accum_0

View File

@ -9,19 +9,28 @@ AST.is_syntax_tree = (n)->
type(n) == 'table' and getmetatable(n) and AST[n.type] == getmetatable(n) type(n) == 'table' and getmetatable(n) and AST[n.type] == getmetatable(n)
-- Helper method: -- Helper method:
Tree = (name, leaf_or_branch, methods)-> Tree = (name, methods)->
cls = methods or {} cls = methods or {}
is_multi = leaf_or_branch == 'branch'
with cls with cls
.type = name .type = name
.__class = cls
.__name = name
.is_instance = (x)=> getmetatable(x) == @ .is_instance = (x)=> getmetatable(x) == @
.__index = cls .__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)=> .map = (fn)=>
if replacement = fn(@) then return replacement if replacement = fn(@) then return replacement
if @value then return @ made_changes, new_vals = false, {}
new_vals = [v.map and v\map(fn) or v for v in *@] for i,v in ipairs @
return getmetatable(self)(@source, unpack(new_vals)) 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, AST[name] = setmetatable cls,
__tostring: => @name __tostring: => @name
@ -29,25 +38,25 @@ Tree = (name, leaf_or_branch, methods)->
if type(source) == 'string' if type(source) == 'string'
source = Source\from_string(source) source = Source\from_string(source)
assert(Source\is_instance(source)) assert(Source\is_instance(source))
inst = if is_multi then {:source, ...} else {:source, value:...} inst = {:source, ...}
setmetatable(inst, @) setmetatable(inst, @)
if inst.__init then inst\__init! if inst.__init then inst\__init!
return inst return inst
Tree "Number", 'leaf' Tree "Number"
Tree "Var", 'leaf' Tree "Var"
Tree "Block", 'branch' Tree "Block"
Tree "EscapedNomsu", 'branch' Tree "EscapedNomsu"
Tree "Text", 'branch' Tree "Text"
Tree "List", 'branch' Tree "List"
Tree "Dict", 'branch' Tree "Dict"
Tree "DictEntry", 'branch' Tree "DictEntry"
Tree "IndexChain", 'branch' Tree "IndexChain"
Tree "Action", 'branch', Tree "Action",
__init: => __init: =>
stub_bits = [type(a) == 'string' and a or '%' for a in *@] stub_bits = [type(a) == 'string' and a or '%' for a in *@]
@stub = concat stub_bits, " " @stub = concat stub_bits, " "
get_spec: => 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 return AST

View File

@ -193,14 +193,15 @@ assume
return %n return %n
..= 6 ..= 6
%nums <- [] #
for % in %nums <- []
values for % in
-> 4 values
-> 5 -> 4
-> 6 -> 5
.. -> 6
add % to %nums ..
add % to %nums
assume (%nums = [4,5,6]) or barf "Coroutine iteration failed" assume (%nums = [4,5,6]) or barf "Coroutine iteration failed"