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
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

View File

@ -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: =>

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

174
nomsu.lua
View File

@ -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

View File

@ -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}")

View File

@ -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):
!('[..]')

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)
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

View File

@ -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

View File

@ -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"