aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--code_obj.lua32
-rw-r--r--code_obj.moon17
-rw-r--r--core/metaprogramming.nom34
-rw-r--r--core/operators.nom84
-rw-r--r--core/text.nom2
-rw-r--r--nomsu.lua663
-rwxr-xr-xnomsu.moon479
-rw-r--r--nomsu_tree.lua726
-rw-r--r--nomsu_tree.moon477
9 files changed, 1228 insertions, 1286 deletions
diff --git a/code_obj.lua b/code_obj.lua
index 2e1ce5e..ab0f1ac 100644
--- a/code_obj.lua
+++ b/code_obj.lua
@@ -201,6 +201,9 @@ do
local _parent_0 = Code
local _base_0 = {
add_free_vars = function(self, vars)
+ if not (#vars > 0) then
+ return
+ end
local seen
do
local _tbl_0 = { }
@@ -216,12 +219,7 @@ do
end
for _index_0 = 1, #vars do
local var = vars[_index_0]
- if type(var) == 'userdata' and var.type == "Var" then
- var = tostring(var:as_lua())
- elseif type(var) ~= 'string' then
- var = tostring(var)
- end
- assert(var:match("^[_a-zA-Z][_a-zA-Z0-9]*$"))
+ assert(type(var) == 'userdata' and var.type == "Var")
if not (seen[var]) then
self.free_vars[#self.free_vars + 1] = var
seen[var] = true
@@ -230,15 +228,13 @@ do
self.__str = nil
end,
remove_free_vars = function(self, vars)
+ if not (#vars > 0) then
+ return
+ end
local removals = { }
for _index_0 = 1, #vars do
local var = vars[_index_0]
- if type(var) == 'userdata' and var.type == "Var" then
- var = tostring(var:as_lua())
- elseif type(var) ~= 'string' then
- var = tostring(var)
- end
- assert(var:match("^[_a-zA-Z][_a-zA-Z0-9]*$"))
+ assert(type(var) == 'userdata' and var.type == "Var")
removals[var] = true
end
local stack = {
@@ -296,7 +292,6 @@ do
local var = _list_0[_index_0]
if not (seen[var]) then
seen[var] = true
- assert(var:match("^[_a-zA-Z][_a-zA-Z0-9]*$"))
to_declare[#to_declare + 1] = var
end
end
@@ -312,7 +307,16 @@ do
end
if #to_declare > 0 then
self:remove_free_vars(to_declare)
- self:prepend("local " .. tostring(concat(to_declare, ", ")) .. ";\n")
+ self:prepend("local " .. tostring(concat((function()
+ local _accum_0 = { }
+ local _len_0 = 1
+ for _index_0 = 1, #to_declare do
+ local v = to_declare[_index_0]
+ _accum_0[_len_0] = v:as_lua_id()
+ _len_0 = _len_0 + 1
+ end
+ return _accum_0
+ end)(), ", ")) .. ";\n")
end
return to_declare
end,
diff --git a/code_obj.moon b/code_obj.moon
index ec5bc46..fbe202c 100644
--- a/code_obj.moon
+++ b/code_obj.moon
@@ -134,26 +134,20 @@ class Lua extends Code
return lua
add_free_vars: (vars)=>
+ return unless #vars > 0
seen = {[v]:true for v in *@free_vars}
for var in *vars
- if type(var) == 'userdata' and var.type == "Var"
- var = tostring(var\as_lua!)
- elseif type(var) != 'string'
- var = tostring(var)
- assert(var\match("^[_a-zA-Z][_a-zA-Z0-9]*$"))
+ assert(type(var) == 'userdata' and var.type == "Var")
unless seen[var]
@free_vars[#@free_vars+1] = var
seen[var] = true
@__str = nil
remove_free_vars: (vars)=>
+ return unless #vars > 0
removals = {}
for var in *vars
- if type(var) == 'userdata' and var.type == "Var"
- var = tostring(var\as_lua!)
- elseif type(var) != 'string'
- var = tostring(var)
- assert(var\match("^[_a-zA-Z][_a-zA-Z0-9]*$"))
+ assert(type(var) == 'userdata' and var.type == "Var")
removals[var] = true
stack = {self}
@@ -185,7 +179,6 @@ class Lua extends Code
for var in *@free_vars
unless seen[var]
seen[var] = true
- assert(var\match("^[_a-zA-Z][_a-zA-Z0-9]*$"))
to_declare[#to_declare+1] = var
for bit in *@bits
if bit.__class == Lua
@@ -193,7 +186,7 @@ class Lua extends Code
gather_from self
if #to_declare > 0
@remove_free_vars to_declare
- @prepend "local #{concat to_declare, ", "};\n"
+ @prepend "local #{concat [v\as_lua_id! for v in *to_declare], ", "};\n"
return to_declare
__tostring: =>
diff --git a/core/metaprogramming.nom b/core/metaprogramming.nom
index 41491c7..9d6e1e6 100644
--- a/core/metaprogramming.nom
+++ b/core/metaprogramming.nom
@@ -20,12 +20,12 @@ immediately
lua:append("function(tree")
local args = {}
for i,tok in ipairs(\%actions[1]) do
- if tok.type == "Var" then args[#args+1] = tok:as_lua(nomsu) end
+ if tok.type == "Var" then args[#args+1] = tok end
end
for i, arg in ipairs(args) do
- lua:append(", ", arg)
+ lua:append(", ", nomsu:tree_to_lua(arg))
end
- local body_lua = \%lua:as_lua(nomsu):as_statements("return ")
+ local body_lua = nomsu:tree_to_lua(\%lua):as_statements("return ")
body_lua:remove_free_vars(args)
body_lua:declare_locals()
lua:append(")\n ", body_lua, "\nend);")
@@ -50,13 +50,13 @@ immediately
lua:append("function(")
local args = {}
for i,tok in ipairs(\%actions[1]) do
- if tok.type == "Var" then args[#args+1] = tok:as_lua(nomsu) end
+ if tok.type == "Var" then args[#args+1] = tok end
end
for i, arg in ipairs(args) do
- lua:append(arg)
+ lua:append(nomsu:tree_to_lua(arg))
if i < #args then lua:append(", ") end
end
- local body_lua = \%body:as_lua(nomsu):as_statements("return ")
+ local body_lua = nomsu:tree_to_lua(\%body):as_statements("return ")
body_lua:remove_free_vars(args)
body_lua:declare_locals()
lua:append(")\n ", body_lua, "\nend);")
@@ -81,7 +81,7 @@ immediately
local replacements = {}
for i,tok in ipairs(\%shorthand[1]) do
if tok.type == "Var" then
- local lua_var = tostring(tok:as_lua(nomsu))
+ local lua_var = tostring(nomsu:tree_to_lua(tok))
replacements[tok] = lua_var
lua:append(", ", lua_var)
end
@@ -104,7 +104,7 @@ immediately
lua:append([[)
local tree = ]], make_tree(\%longhand), [[
- return tree:as_lua(nomsu)
+ return nomsu:tree_to_lua(tree)
end);]])
return lua
@@ -119,27 +119,27 @@ action [remove action %stub]
immediately
action [%tree as nomsu]
- =lua "\%tree:as_nomsu()"
+ =lua "nomsu:tree_to_nomsu(\%tree)"
action [%tree as inline nomsu]
- =lua "\%tree:as_nomsu(true)"
+ =lua "nomsu:tree_to_nomsu(\%tree, true)"
action [%tree as lua]
- =lua "\%tree:as_lua(nomsu)"
+ =lua "nomsu:tree_to_lua(\%tree)"
action [%tree as lua expr]
lua> ".."
- local lua = \%tree:as_lua(nomsu)
+ local lua = nomsu:tree_to_lua(\%tree)
if not lua.is_value then
error("Invalid thing to convert to lua expr: "..\%tree)
end
return lua
action [%tree as lua statements]
- =lua "\%tree:as_lua(nomsu):as_statements()"
+ =lua "nomsu:tree_to_lua(\%tree):as_statements()"
action [%tree with vars %vars]
- =lua "nomsu:tree_with_replacements(\%tree, \%vars)"
+ =lua "\%tree:map(\%vars)"
compile [declare locals in %code] to
Lua value "\(%code as lua expr):declare_locals()"
@@ -168,7 +168,7 @@ immediately
immediately
compile [nomsu] to: Lua value "nomsu"
- compile [%var as lua identifier] to: Lua value "\(%var as lua expr):as_lua(nomsu)"
+ compile [%var as lua identifier] to: Lua value "nomsu:tree_to_lua(\(%var as lua expr))"
# Compiler tools
immediately
@@ -178,7 +178,7 @@ immediately
immediately
compile [show lua %block] to
lua> ".."
- local \%lua = \%block:as_lua(nomsu);
+ local \%lua = nomsu:tree_to_lua(\%block);
return Lua(nil, "print(", repr(tostring(\%lua)), ");");
immediately
@@ -202,7 +202,7 @@ immediately
compile [barf] to: Lua "error(nil, 0);"
compile [barf %msg] to: Lua "error(\(%msg as lua expr), 0);"
compile [assume %condition] to
- lua> "local \%assumption = 'Assumption failed: '..tostring(\%condition:as_nomsu());"
+ lua> "local \%assumption = 'Assumption failed: '..tostring(nomsu:tree_to_nomsu(\%condition));"
return
Lua ".."
if not \(%condition as lua expr) then
diff --git a/core/operators.nom b/core/operators.nom
index adfeeb5..62a3225 100644
--- a/core/operators.nom
+++ b/core/operators.nom
@@ -25,21 +25,21 @@ immediately
# TODO: optimize case of [%x,%y] = [1,2]
compile [%a is %b, %a = %b, %a == %b] to
lua> ".."
- local safe = {Text=true, Number=true};
- local a_lua, b_lua = \%a:as_lua(nomsu), \%b:as_lua(nomsu);
+ local safe = {Text=true, Number=true}
+ local a_lua, b_lua = \(%a as lua), \(%b as lua)
if safe[\%a.type] or safe[\%b.type] then
- return Lua.Value(nil, "(", a_lua, " == ", b_lua, ")");
+ return Lua.Value(nil, "(", a_lua, " == ", b_lua, ")")
else
- return Lua.Value(nil, "utils.equivalent(", a_lua, ", ", b_lua, ")");
+ return Lua.Value(nil, "utils.equivalent(", a_lua, ", ", b_lua, ")")
end
compile [%a isn't %b, %a is not %b, %a not= %b, %a != %b] to
lua> ".."
- local safe = {Text=true, Number=true};
- local a_lua, b_lua = \%a:as_lua(nomsu), \%b:as_lua(nomsu);
+ local safe = {Text=true, Number=true}
+ local a_lua, b_lua = \(%a as lua), \(%b as lua)
if safe[\%a.type] or safe[\%b.type] then
- return Lua.Value(nil, "(", a_lua, " ~= ", b_lua, ")");
+ return Lua.Value(nil, "(", a_lua, " ~= ", b_lua, ")")
else
- return Lua.Value(nil, "(not utils.equivalent(", a_lua, ", ", b_lua, "))");
+ return Lua.Value(nil, "(not utils.equivalent(", a_lua, ", ", b_lua, "))")
end
# For strict identity checking, use (%x's id) is (%y's id)
compile [%'s id, id of %] to: Lua value "nomsu.ids[\(% as lua expr)]"
@@ -47,14 +47,14 @@ immediately
# Variable assignment operator
immediately
compile [%var <- %value] to
- lua> "local \%var_lua = \%var:as_lua(nomsu);"
+ lua> "local \%var_lua = \(%var as lua);"
assume %var_lua.is_value or barf "Invalid target for assignment: \%var"
- lua> "local \%value_lua = \%value:as_lua(nomsu);"
+ lua> "local \%value_lua = \(%value as lua);"
assume %value_lua.is_value or barf "Invalid value for assignment: \%value"
lua> ".."
- local lua = Lua(nil, \%var_lua, ' = ', \%value_lua, ';');
+ local lua = Lua(nil, \%var_lua, ' = ', \%value_lua, ';')
if \%var.type == 'Var' then
- lua:add_free_vars({\%var});
+ lua:add_free_vars({\%var})
end
return lua;
@@ -64,24 +64,24 @@ immediately
assume ((%assignments' "type") is "Dict") or barf ".."
Expected a Dict for the assignments part of '<- %' statement, not \%assignments
lua> ".."
- local lhs, rhs = Lua(), Lua();
+ local lhs, rhs = Lua(), Lua()
for i, item in ipairs(\%assignments) do
- local target, value = item[1], item[2];
- local target_lua = target:as_lua(nomsu);
- if not target_lua.is_value then error("Invalid target for assignment: "..target:get_src()); end
- local value_lua = value:as_lua(nomsu);
- if not value_lua.is_value then error("Invalid value for assignment: "..value:get_src()); end
- if target.type == "Var" then
- lhs:add_free_vars({target});
+ local \%target, \%value = item[1], item[2]
+ local target_lua = \(%target as lua)
+ if not target_lua.is_value then error("Invalid target for assignment: "..\(%target as text)) end
+ local value_lua = \(%value as lua)
+ if not value_lua.is_value then error("Invalid value for assignment: "..\(%value as text)) end
+ if \%target.type == "Var" then
+ lhs:add_free_vars({\%target})
end
if i > 1 then
- lhs:append(", ");
- rhs:append(", ");
+ lhs:append(", ")
+ rhs:append(", ")
end
- lhs:append(target_lua);
- rhs:append(value_lua);
+ lhs:append(target_lua)
+ rhs:append(value_lua)
end
- return Lua(nil, lhs, " = ", rhs, ";");
+ return Lua(nil, lhs, " = ", rhs, ";")
immediately
compile [external %var <- %value] to
@@ -99,31 +99,31 @@ immediately
compile [with %assignments %body] to
%lua <- (%body as lua statements)
lua> ".."
- local lhs, rhs = Lua(), Lua();
- local vars = {};
+ local lhs, rhs = Lua(), Lua()
+ local vars = {}
for i, item in ipairs(\%assignments) do
- local target, value = item[1], item[2];
- if not target.type == "Var" then
- error("Invalid target for 'with' assignment: "..tostring(target));
+ local \%target, \%value = item[1], item[2]
+ if not \%target.type == "Var" then
+ error("Invalid target for 'with' assignment: "..tostring(\%target))
end
- local target_lua = target:as_lua(nomsu);
- local value_lua = value:as_lua(nomsu);
+ local target_lua = \(%target as lua)
+ local value_lua = \(%value as lua)
if not value_lua.is_value then
- error("Invalid value for assignment: "..tostring(value));
+ error("Invalid value for assignment: "..tostring(\%value))
end
- if target.type == "Var" then
- lhs:add_free_vars({target});
+ if \%target.type == "Var" then
+ lhs:add_free_vars({\%target})
end
if i > 1 then
- lhs:append(", ");
- rhs:append(", ");
+ lhs:append(", ")
+ rhs:append(", ")
end
- lhs:append(target_lua);
- rhs:append(value_lua);
- vars[i] = tostring(target_lua);
+ lhs:append(target_lua)
+ rhs:append(value_lua)
+ vars[i] = \%target
end
- \%lua:remove_free_vars(vars);
- \%lua:prepend("local ", lhs, " = ", rhs, ";\n");
+ \%lua:remove_free_vars(vars)
+ \%lua:prepend("local ", lhs, " = ", rhs, ";\n")
return
Lua ".."
do
diff --git a/core/text.nom b/core/text.nom
index 7b945cd..b7fd2cf 100644
--- a/core/text.nom
+++ b/core/text.nom
@@ -44,7 +44,7 @@ lua> ".."
local reset = "'"..colors["reset color"].."'";
nomsu:define_compile_action(name, function(tree) return Lua.Value(nil, color); end);
nomsu:define_compile_action(name.." %", function(\%)
- return Lua.Value(nil, color, "..", \%:as_lua(nomsu), "..", reset);
+ return Lua.Value(nil, color, "..", \(% as lua), "..", reset);
end);
end
end
diff --git a/nomsu.lua b/nomsu.lua
index f4dcb7e..2b0b6dc 100644
--- a/nomsu.lua
+++ b/nomsu.lua
@@ -249,7 +249,7 @@ end
local NomsuCompiler
do
local _class_0
- local stub_defs, stub_pattern, var_pattern, _nomsu_chunk_counter, _running_files
+ local stub_defs, stub_pattern, var_pattern, _nomsu_chunk_counter, _running_files, MAX_LINE, math_expression
local _base_0 = {
define_action = function(self, signature, fn, is_compile_action)
if is_compile_action == nil then
@@ -284,7 +284,7 @@ do
local _len_0 = 1
for _index_1 = 1, #stub_args do
local a = stub_args[_index_1]
- _accum_0[_len_0] = fn_arg_positions[Types.Var.as_lua_id(a)]
+ _accum_0[_len_0] = fn_arg_positions[Types.Var(a):as_lua_id()]
_len_0 = _len_0 + 1
end
arg_orders[stub] = _accum_0
@@ -341,7 +341,7 @@ do
end
local tree = self:parse(nomsu_code)
assert(tree, "Failed to parse: " .. tostring(nomsu_code))
- local lua = tree:as_lua(self):as_statements()
+ local lua = self:tree_to_lua(tree):as_statements()
lua:declare_locals()
lua:prepend("-- File: " .. tostring(nomsu_code.source or "") .. "\n")
if compile_fn then
@@ -432,11 +432,628 @@ do
end
return run_lua_fn()
end,
+ tree_to_lua = function(self, tree)
+ local _exp_0 = tree.type
+ if "Action" == _exp_0 then
+ local stub = tree:get_stub()
+ local compile_action = self.environment.COMPILE_ACTIONS[stub]
+ if compile_action then
+ local args
+ do
+ local _accum_0 = { }
+ local _len_0 = 1
+ for _index_0 = 1, #tree do
+ local arg = tree[_index_0]
+ if arg.type ~= "Word" then
+ _accum_0[_len_0] = arg
+ _len_0 = _len_0 + 1
+ end
+ end
+ args = _accum_0
+ end
+ do
+ local _accum_0 = { }
+ local _len_0 = 1
+ local _list_0 = self.environment.ARG_ORDERS[compile_action][stub]
+ for _index_0 = 1, #_list_0 do
+ local p = _list_0[_index_0]
+ _accum_0[_len_0] = args[p - 1]
+ _len_0 = _len_0 + 1
+ end
+ args = _accum_0
+ end
+ local ret = compile_action(tree, unpack(args))
+ if not ret then
+ error("Failed to produce any Lua")
+ end
+ return ret
+ end
+ local action = rawget(self.environment.ACTIONS, stub)
+ local lua = Lua.Value()
+ if not action and math_expression:match(stub) then
+ for i, tok in ipairs(tree) do
+ if tok.type == "Word" then
+ lua:append(tok.value)
+ else
+ local tok_lua = self:tree_to_lua(tok)
+ if not (tok_lua.is_value) then
+ error("non-expression value inside math expression: " .. tostring(colored.yellow(repr(tok))))
+ end
+ if tok.type == "Action" then
+ tok_lua:parenthesize()
+ end
+ lua:append(tok_lua)
+ end
+ if i < #tree then
+ lua:append(" ")
+ end
+ end
+ return lua
+ end
+ local args = { }
+ for i, tok in ipairs(tree) do
+ local _continue_0 = false
+ repeat
+ if tok.type == "Word" then
+ _continue_0 = true
+ break
+ end
+ local arg_lua = self:tree_to_lua(tok)
+ if not (arg_lua.is_value) then
+ error("Cannot use:\n" .. tostring(colored.yellow(repr(tok))) .. "\nas an argument to " .. tostring(stub) .. ", since it's not an expression, it produces: " .. tostring(repr(arg_lua)), 0)
+ end
+ insert(args, arg_lua)
+ _continue_0 = true
+ until true
+ if not _continue_0 then
+ break
+ end
+ end
+ if action then
+ do
+ local _accum_0 = { }
+ local _len_0 = 1
+ local _list_0 = self.environment.ARG_ORDERS[action][stub]
+ for _index_0 = 1, #_list_0 do
+ local p = _list_0[_index_0]
+ _accum_0[_len_0] = args[p]
+ _len_0 = _len_0 + 1
+ end
+ args = _accum_0
+ end
+ end
+ lua:append("ACTIONS[", repr(stub), "](")
+ for i, arg in ipairs(args) do
+ lua:append(arg)
+ if i < #args then
+ lua:append(", ")
+ end
+ end
+ lua:append(")")
+ return lua
+ elseif "EscapedNomsu" == _exp_0 then
+ local make_tree
+ make_tree = function(t)
+ if type(t) ~= 'userdata' then
+ return repr(t)
+ end
+ if t.is_multi then
+ 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
+ end
+ return t.type .. "(" .. table.concat(bits, ", ") .. ")"
+ else
+ return t.type .. "(" .. make_tree(t.value) .. ")"
+ end
+ end
+ return Lua.Value(nil, make_tree(tree.value))
+ elseif "Block" == _exp_0 then
+ local lua = Lua()
+ for i, line in ipairs(tree) do
+ local line_lua = self:tree_to_lua(line)
+ if i > 1 then
+ lua:append("\n")
+ end
+ lua:append(line_lua:as_statements())
+ end
+ return lua
+ elseif "Text" == _exp_0 then
+ local lua = Lua.Value()
+ local string_buffer = ""
+ for _index_0 = 1, #tree do
+ local _continue_0 = false
+ repeat
+ local bit = tree[_index_0]
+ if type(bit) == "string" then
+ string_buffer = string_buffer .. bit
+ _continue_0 = true
+ break
+ end
+ if string_buffer ~= "" then
+ if #lua.bits > 0 then
+ lua:append("..")
+ end
+ lua:append(repr(string_buffer))
+ string_buffer = ""
+ end
+ local bit_lua = self:tree_to_lua(bit)
+ if not (bit_lua.is_value) then
+ error("Cannot use " .. tostring(colored.yellow(repr(bit))) .. " as a string interpolation value, since it's not an expression.", 0)
+ end
+ if #lua.bits > 0 then
+ lua:append("..")
+ end
+ if bit.type ~= "Text" then
+ bit_lua = Lua.Value(nil, "stringify(", bit_lua, ")")
+ end
+ lua:append(bit_lua)
+ _continue_0 = true
+ until true
+ if not _continue_0 then
+ break
+ end
+ end
+ if string_buffer ~= "" or #lua.bits == 0 then
+ if #lua.bits > 0 then
+ lua:append("..")
+ end
+ lua:append(repr(string_buffer))
+ end
+ if #lua.bits > 1 then
+ lua:parenthesize()
+ end
+ return lua
+ elseif "List" == _exp_0 then
+ local lua = Lua.Value(nil, "{")
+ local line_length = 0
+ for i, item in ipairs(tree) do
+ local item_lua = self:tree_to_lua(item)
+ if not (item_lua.is_value) then
+ error("Cannot use " .. tostring(colored.yellow(repr(item))) .. " as a list item, since it's not an expression.", 0)
+ end
+ lua:append(item_lua)
+ local item_string = tostring(item_lua)
+ local last_line = item_string:match("[^\n]*$")
+ if item_string:match("\n") then
+ line_length = #last_line
+ else
+ line_length = line_length + #last_line
+ end
+ if i < #tree then
+ if line_length >= MAX_LINE then
+ lua:append(",\n ")
+ line_length = 0
+ else
+ lua:append(", ")
+ line_length = line_length + 2
+ end
+ end
+ end
+ lua:append("}")
+ return lua
+ elseif "Dict" == _exp_0 then
+ local lua = Lua.Value(nil, "{")
+ local line_length = 0
+ for i, entry in ipairs(tree) 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]*)$")
+ if last_line then
+ line_length = #last_line
+ else
+ line_length = line_length + #entry_lua_str
+ end
+ if i < #tree then
+ if line_length >= MAX_LINE then
+ lua:append(",\n ")
+ line_length = 0
+ else
+ lua:append(", ")
+ line_length = line_length + 2
+ end
+ end
+ end
+ lua:append("}")
+ return lua
+ elseif "DictEntry" == _exp_0 then
+ local key, value = tree[1], tree[2]
+ local key_lua = self:tree_to_lua(key)
+ if not (key_lua.is_value) then
+ error("Cannot use " .. tostring(colored.yellow(repr(key))) .. " as a dict key, since it's not an expression.", 0)
+ end
+ local value_lua = value and self:tree_to_lua(value) or Lua.Value(nil, "true")
+ if not (value_lua.is_value) then
+ error("Cannot use " .. tostring(colored.yellow(repr(value))) .. " as a dict value, since it's not an expression.", 0)
+ end
+ local key_str = tostring(key_lua):match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=])
+ if key_str then
+ return Lua(nil, key_str, "=", value_lua)
+ elseif tostring(key_lua):sub(1, 1) == "[" then
+ return Lua(nil, "[ ", key_lua, "]=", value_lua)
+ else
+ return Lua(nil, "[", key_lua, "]=", value_lua)
+ end
+ elseif "IndexChain" == _exp_0 then
+ local lua = self:tree_to_lua(tree[1])
+ if not (lua.is_value) then
+ error("Cannot index " .. tostring(colored.yellow(repr(tree[1]))) .. ", since it's not an expression.", 0)
+ end
+ local first_char = tostring(lua):sub(1, 1)
+ if first_char == "{" or first_char == '"' or first_char == "[" then
+ lua:parenthesize()
+ end
+ for i = 2, #tree do
+ local key = tree[i]
+ local key_lua = self:tree_to_lua(key)
+ if not (key_lua.is_value) then
+ error("Cannot use " .. tostring(colored.yellow(repr(key))) .. " as an index, since it's not an expression.", 0)
+ end
+ local key_lua_str = tostring(key_lua)
+ do
+ local lua_id = key_lua_str:match("^['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]$")
+ if lua_id then
+ lua:append("." .. tostring(lua_id))
+ elseif key_lua_str:sub(1, 1) == '[' then
+ lua:append("[ ", key_lua, " ]")
+ else
+ lua:append("[", key_lua, "]")
+ end
+ end
+ end
+ return lua
+ elseif "Number" == _exp_0 then
+ return Lua.Value(nil, tostring(tree.value))
+ elseif "Var" == _exp_0 then
+ return Lua.Value(nil, tree:as_lua_id())
+ elseif "Word" == _exp_0 then
+ return error("Cannot convert a Word to lua")
+ elseif "Comment" == _exp_0 then
+ return Lua(nil, "--" .. tree.value:gsub("\n", "\n--") .. "\n")
+ else
+ return error("Unknown type: " .. tostring(tree.type))
+ end
+ end,
+ tree_to_nomsu = function(self, tree, inline, can_use_colon)
+ if inline == nil then
+ inline = false
+ end
+ if can_use_colon == nil then
+ can_use_colon = false
+ end
+ local _exp_0 = tree.type
+ if "Action" == _exp_0 then
+ if inline then
+ local nomsu = Nomsu()
+ for i, bit in ipairs(tree) do
+ if bit.type == "Word" then
+ if i > 1 then
+ nomsu:append(" ")
+ end
+ nomsu:append(bit.value)
+ else
+ local arg_nomsu = self:tree_to_nomsu(bit, true)
+ if not (arg_nomsu) then
+ return nil
+ end
+ if not (i == 1) then
+ nomsu:append(" ")
+ end
+ if bit.type == "Action" or bit.type == "Block" then
+ arg_nomsu:parenthesize()
+ end
+ nomsu:append(arg_nomsu)
+ end
+ end
+ return nomsu
+ else
+ local nomsu = Nomsu()
+ local next_space = ""
+ local last_colon = nil
+ for i, bit in ipairs(tree) do
+ if bit.type == "Word" then
+ nomsu:append(next_space, bit.value)
+ next_space = " "
+ else
+ local arg_nomsu
+ if last_colon == i - 1 and bit.type == "Action" then
+ arg_nomsu = nil
+ elseif bit.type == "Block" then
+ arg_nomsu = nil
+ else
+ arg_nomsu = self:tree_to_nomsu(bit, true)
+ end
+ 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)
+ next_space = "\n.."
+ last_colon = i
+ else
+ nomsu:append(next_space, "(", arg_nomsu, ")")
+ next_space = " "
+ end
+ else
+ nomsu:append(next_space, arg_nomsu)
+ next_space = " "
+ end
+ else
+ arg_nomsu = self:tree_to_nomsu(bit, nil, true)
+ if not (nomsu) then
+ return nil
+ end
+ if bit.type ~= "List" and bit.type ~= "Dict" and bit.type ~= "Text" then
+ if i == 1 then
+ arg_nomsu = Nomsu(nil, "(..)\n ", arg_nomsu)
+ else
+ arg_nomsu = Nomsu(nil, "\n ", arg_nomsu)
+ end
+ end
+ if last_colon == i - 1 and (bit.type == "Action" or bit.type == "Block") then
+ next_space = ""
+ end
+ nomsu:append(next_space, arg_nomsu)
+ next_space = "\n.."
+ end
+ if next_space == " " and #(tostring(nomsu):match("[^\n]*$")) > MAX_LINE then
+ next_space = "\n.."
+ end
+ end
+ end
+ return nomsu
+ end
+ elseif "EscapedNomsu" == _exp_0 then
+ local nomsu = self:tree_to_nomsu(tree.value, true)
+ if nomsu == nil and not inline then
+ nomsu = self:tree_to_nomsu(tree.value)
+ return nomsu and Nomsu(nil, "\\:\n ", nomsu)
+ end
+ return nomsu and Nomsu(nil, "\\(", nomsu, ")")
+ elseif "Block" == _exp_0 then
+ if inline then
+ local nomsu = Nomsu()
+ for i, line in ipairs(self) do
+ if i > 1 then
+ nomsu:append("; ")
+ end
+ local line_nomsu = self:tree_to_nomsu(line, true)
+ if not (line_nomsu) then
+ return nil
+ end
+ nomsu:append(line_nomsu)
+ end
+ return nomsu
+ end
+ local nomsu = Nomsu()
+ for i, line in ipairs(self) do
+ line = assert(self:tree_to_nomsu(line, nil, true), "Could not convert line to nomsu")
+ nomsu:append(line)
+ if i < #self then
+ nomsu:append("\n")
+ if tostring(line):match("\n") then
+ nomsu:append("\n")
+ end
+ end
+ end
+ return nomsu
+ elseif "Text" == _exp_0 then
+ if inline then
+ local nomsu = Nomsu(nil, '"')
+ for _index_0 = 1, #tree do
+ local bit = tree[_index_0]
+ if type(bit) == 'string' then
+ nomsu:append((bit:gsub("\\", "\\\\"):gsub("\n", "\\n")))
+ else
+ local interp_nomsu = self:tree_to_nomsu(bit, true)
+ if interp_nomsu then
+ if bit.type ~= "Word" and bit.type ~= "List" and bit.type ~= "Dict" and bit.type ~= "Text" then
+ interp_nomsu:parenthesize()
+ end
+ nomsu:append("\\", interp_nomsu)
+ else
+ return nil
+ end
+ end
+ end
+ nomsu:append('"')
+ return nomsu
+ else
+ local inline_version = self:tree_to_nomsu(tree, true)
+ if inline_version and #inline_version <= MAX_LINE then
+ return inline_version
+ end
+ local nomsu = Nomsu(nil, '".."\n ')
+ for i, bit in ipairs(self) do
+ if type(bit) == 'string' then
+ nomsu:append((bit:gsub("\\", "\\\\"):gsub("\n", "\n ")))
+ else
+ local interp_nomsu = self:tree_to_nomsu(bit, true)
+ if interp_nomsu then
+ if bit.type ~= "Word" and bit.type ~= "List" and bit.type ~= "Dict" and bit.type ~= "Text" then
+ interp_nomsu:parenthesize()
+ end
+ nomsu:append("\\", interp_nomsu)
+ else
+ interp_nomsu = self:tree_to_nomsu(bit)
+ if not (interp_nomsu) then
+ return nil
+ end
+ nomsu:append("\\\n ", interp_nomsu)
+ if i < #self then
+ nomsu:append("\n ..")
+ end
+ end
+ end
+ end
+ return nomsu
+ end
+ elseif "List" == _exp_0 then
+ if inline then
+ local nomsu = Nomsu(nil, "[")
+ for i, item in ipairs(tree) do
+ local item_nomsu = self:tree_to_nomsu(item, true)
+ if not (item_nomsu) then
+ return nil
+ end
+ if i > 1 then
+ nomsu:append(", ")
+ end
+ nomsu:append(item_nomsu)
+ end
+ nomsu:append("]")
+ return nomsu
+ else
+ local inline_version = self:tree_to_nomsu(tree, true)
+ if inline_version and #inline_version <= MAX_LINE then
+ return inline_version
+ end
+ local nomsu = Nomsu(nil, "[..]")
+ local line = Nomsu(nil, "\n ")
+ for _index_0 = 1, #tree do
+ local item = tree[_index_0]
+ local item_nomsu = self:tree_to_nomsu(item, true)
+ if item_nomsu and #line + #", " + #item_nomsu <= MAX_LINE then
+ if #line.bits > 1 then
+ line:append(", ")
+ end
+ line:append(item_nomsu)
+ else
+ if not (item_nomsu) then
+ item_nomsu = self:tree_to_nomsu(item)
+ if not (item_nomsu) then
+ return nil
+ end
+ end
+ if #line.bits > 1 then
+ nomsu:append(line)
+ line = Nomsu(nil, "\n ")
+ end
+ line:append(item_nomsu)
+ end
+ end
+ if #line.bits > 1 then
+ nomsu:append(line)
+ end
+ return nomsu
+ end
+ elseif "Dict" == _exp_0 then
+ if inline then
+ local nomsu = Nomsu(nil, "{")
+ for i, entry in ipairs(tree) do
+ local entry_nomsu = self:tree_to_nomsu(entry, true)
+ if not (entry_nomsu) then
+ return nil
+ end
+ if i > 1 then
+ nomsu:append(", ")
+ end
+ nomsu:append(entry_nomsu)
+ end
+ nomsu:append("}")
+ return nomsu
+ else
+ local inline_version = self:tree_to_nomsu(tree, true)
+ if inline_version then
+ return inline_version
+ end
+ local nomsu = Nomsu(nil, "{..}")
+ local line = Nomsu(nil, "\n ")
+ for _index_0 = 1, #tree do
+ local entry = tree[_index_0]
+ local entry_nomsu = self:tree_to_nomsu(entry)
+ if not (entry_nomsu) then
+ return nil
+ end
+ if #line + #tostring(entry_nomsu) <= MAX_LINE then
+ if #line.bits > 1 then
+ line:append(", ")
+ end
+ line:append(entry_nomsu)
+ else
+ if #line.bits > 1 then
+ nomsu:append(line)
+ line = Nomsu(nil, "\n ")
+ end
+ line:append(entry_nomsu)
+ end
+ end
+ if #line.bits > 1 then
+ nomsu:append(line)
+ end
+ return nomsu
+ end
+ elseif "DictEntry" == _exp_0 then
+ local key, value = tree[1], tree[2]
+ local key_nomsu = self:tree_to_nomsu(key, true)
+ if not (key_nomsu) then
+ return nil
+ end
+ if key.type == "Action" or key.type == "Block" then
+ key_nomsu:parenthesize()
+ end
+ local value_nomsu
+ if value then
+ value_nomsu = self:tree_to_nomsu(value, true)
+ else
+ value_nomsu = Nomsu(nil, "")
+ end
+ if inline and not value_nomsu then
+ return nil
+ end
+ if not value_nomsu then
+ if inline then
+ return nil
+ end
+ value_nomsu = self:tree_to_nomsu(value)
+ if not (value_nomsu) then
+ return nil
+ end
+ end
+ return Nomsu(nil, key_nomsu, ":", value_nomsu)
+ elseif "IndexChain" == _exp_0 then
+ local nomsu = Nomsu()
+ for i, bit in ipairs(tree) do
+ if i > 1 then
+ nomsu:append(".")
+ end
+ local bit_nomsu = self:tree_to_nomsu(bit, true)
+ if not (bit_nomsu) then
+ return nil
+ end
+ if bit.type == "Action" or bit.type == "Block" then
+ bit_nomsu:parenthesize()
+ end
+ nomsu:append(bit_nomsu)
+ end
+ return nomsu
+ elseif "Number" == _exp_0 then
+ return Nomsu(nil, tostring(tree.value))
+ elseif "Var" == _exp_0 then
+ return Nomsu(nil, "%", tree.value)
+ elseif "Word" == _exp_0 then
+ return Nomsu(nil, tree.value)
+ elseif "Comment" == _exp_0 then
+ if inline then
+ return nil
+ end
+ return Nomsu(nil, "#", tree.value:gsub("\n", "\n "))
+ else
+ return error("Unknown type: " .. tostring(tree.type))
+ end
+ end,
tree_to_value = function(self, tree)
if tree.type == 'Text' and #tree == 1 and type(tree[1]) == 'string' then
return tree[1]
end
- local lua = Lua(nil, "return ", tree:as_lua(self), ";")
+ local lua = Lua(nil, "return ", self:tree_to_lua(tree), ";")
return self:run_lua(lua)
end,
walk_tree = function(self, tree, depth)
@@ -456,32 +1073,10 @@ do
return self:walk_tree(v, depth + 1)
end
end,
- tree_with_replacements = function(self, tree, replacements)
- if not (next(replacements)) then
- return tree
- end
- if next(replacements).type == "Var" then
- do
- local _tbl_0 = { }
- for k, v in pairs(replacements) do
- _tbl_0[tostring(k:as_lua(self))] = v
- end
- replacements = _tbl_0
- end
- end
- return tree:map(function(t)
- if t.type == "Var" then
- local id = tostring(t:as_lua(self))
- if replacements[id] ~= nil then
- return replacements[id]
- end
- end
- end)
- end,
initialize_core = function(self)
local nomsu = self
self:define_compile_action("immediately %block", function(self, _block)
- local lua = _block:as_lua(nomsu):as_statements()
+ local lua = nomsu:tree_to_lua(_block):as_statements()
lua:declare_locals()
nomsu:run_lua(lua)
return Lua(nil, "if IMMEDIATE then\n ", lua, "\nend")
@@ -489,7 +1084,7 @@ do
local add_lua_string_bits
add_lua_string_bits = function(lua, code)
if code.type ~= "Text" then
- lua:append(", ", code:as_lua(nomsu))
+ lua:append(", ", nomsu:tree_to_lua(code))
return
end
for _index_0 = 1, #code do
@@ -498,7 +1093,7 @@ do
if type(bit) == "string" then
lua:append(repr(bit))
else
- local bit_lua = bit:as_lua(nomsu)
+ local bit_lua = nomsu:tree_to_lua(bit)
if not (bit_lua.is_value) then
error("Cannot use " .. tostring(colored.yellow(repr(bit))) .. " as a string interpolation value, since it's not an expression.")
end
@@ -525,7 +1120,7 @@ do
if type(bit) == "string" then
lua:append(bit)
else
- local bit_lua = bit:as_lua(nomsu)
+ local bit_lua = nomsu:tree_to_lua(bit)
if not (bit_lua.is_value) then
error("Cannot use " .. tostring(colored.yellow(repr(bit))) .. " as a string interpolation value, since it's not an expression.", 0)
end
@@ -536,13 +1131,13 @@ do
end
self:define_compile_action("lua> %code", function(self, _code)
if _code.type ~= "Text" then
- return Lua(nil, "nomsu:run_lua(", _code:as_lua(nomsu), ");")
+ return Lua(nil, "nomsu:run_lua(", nomsu:tree_to_lua(_code), ");")
end
return add_lua_bits(Lua(), _code)
end)
self:define_compile_action("=lua %code", function(self, _code)
if _code.type ~= "Text" then
- return Lua.Value(nil, "nomsu:run_lua(", _code:as_lua(nomsu), ":as_statements('return '))")
+ return Lua.Value(nil, "nomsu:run_lua(", nomsu:tree_to_lua(_code), ":as_statements('return '))")
end
return add_lua_bits(Lua.Value(), _code)
end)
@@ -715,6 +1310,8 @@ do
var_pattern = re.compile("{| %space ((('%' {%varname}) / %word) %space)+ |}", stub_defs)
_nomsu_chunk_counter = 0
_running_files = { }
+ MAX_LINE = 80
+ math_expression = re.compile([[ ([+-] " ")* "%" (" " [*/^+-] (" " [+-])* " %")+ !. ]])
NomsuCompiler = _class_0
end
if arg and debug_getinfo(2).func ~= require then
@@ -953,7 +1550,7 @@ OPTIONS
elseif args.format then
for input_file in all_files(input) do
local tree = nomsu:parse(io.open(input_file):read("*a"))
- local formatted = tostring(tree:as_nomsu())
+ local formatted = tostring(self:tree_to_nomsu(tree))
if output_file then
output_file:write(formatted, "\n")
output_file:flush()
diff --git a/nomsu.moon b/nomsu.moon
index 7e60cfd..196bedf 100755
--- a/nomsu.moon
+++ b/nomsu.moon
@@ -306,7 +306,7 @@ class NomsuCompiler
stub = assert(stub_pattern\match(alias))
stub_args = assert(var_pattern\match(alias))
(is_compile_action and @environment.COMPILE_ACTIONS or @environment.ACTIONS)[stub] = fn
- arg_orders[stub] = [fn_arg_positions[Types.Var.as_lua_id(a)] for a in *stub_args]
+ arg_orders[stub] = [fn_arg_positions[Types.Var(a)\as_lua_id!] for a in *stub_args]
@environment.ARG_ORDERS[fn] = arg_orders
define_compile_action: (signature, fn)=>
@@ -341,7 +341,7 @@ class NomsuCompiler
if #tostring(nomsu_code) == 0 then return nil
tree = @parse(nomsu_code)
assert tree, "Failed to parse: #{nomsu_code}"
- lua = tree\as_lua(@)\as_statements!
+ lua = @tree_to_lua(tree)\as_statements!
lua\declare_locals!
lua\prepend "-- File: #{nomsu_code.source or ""}\n"
if compile_fn
@@ -401,12 +401,461 @@ class NomsuCompiler
line_numbered_lua = "1 |"..lua_string\gsub("\n", fn)
error("Failed to compile generated code:\n#{colored.bright colored.blue colored.onblack line_numbered_lua}\n\n#{err}", 0)
return run_lua_fn!
+
+ MAX_LINE = 80 -- For beautification purposes, try not to make lines much longer than this value
+ math_expression = re.compile [[ ([+-] " ")* "%" (" " [*/^+-] (" " [+-])* " %")+ !. ]]
+ tree_to_lua: (tree)=>
+ switch tree.type
+ when "Action"
+ stub = tree\get_stub!
+ compile_action = @environment.COMPILE_ACTIONS[stub]
+ if compile_action
+ args = [arg for arg in *tree when arg.type != "Word"]
+ -- Force all compile-time actions to take a tree location
+ args = [args[p-1] for p in *@environment.ARG_ORDERS[compile_action][stub]]
+ -- Force Lua to avoid tail call optimization for debugging purposes
+ ret = compile_action(tree, unpack(args))
+ if not ret then error("Failed to produce any Lua")
+ return ret
+ action = rawget(@environment.ACTIONS, stub)
+ lua = Lua.Value!
+ if not action and math_expression\match(stub)
+ -- This is a bit of a hack, but this code handles arbitrarily complex
+ -- math expressions like 2*x + 3^2 without having to define a single
+ -- action for every possibility.
+ for i,tok in ipairs tree
+ if tok.type == "Word"
+ lua\append tok.value
+ else
+ tok_lua = @tree_to_lua(tok)
+ unless tok_lua.is_value
+ error("non-expression value inside math expression: #{colored.yellow repr(tok)}")
+ if tok.type == "Action"
+ tok_lua\parenthesize!
+ lua\append tok_lua
+ if i < #tree
+ lua\append " "
+ return lua
+
+ args = {}
+ for i, tok in ipairs tree
+ if tok.type == "Word" then continue
+ arg_lua = @tree_to_lua(tok)
+ unless arg_lua.is_value
+ error "Cannot use:\n#{colored.yellow repr(tok)}\nas an argument to #{stub}, since it's not an expression, it produces: #{repr arg_lua}", 0
+ insert args, arg_lua
+
+ if action
+ args = [args[p] for p in *@environment.ARG_ORDERS[action][stub]]
+
+ -- Not really worth bothering with ACTIONS.foo(...) style since almost every action
+ -- has arguments, so it won't work
+ lua\append "ACTIONS[",repr(stub),"]("
+ for i, arg in ipairs args
+ lua\append arg
+ if i < #args then lua\append ", "
+ lua\append ")"
+ return lua
+
+ when "EscapedNomsu"
+ make_tree = (t)->
+ if type(t) != 'userdata'
+ return repr(t)
+ if t.is_multi
+ bits = [make_tree(bit) for bit in *t]
+ return t.type.."("..table.concat(bits, ", ")..")"
+ else
+ return t.type.."("..make_tree(t.value)..")"
+ Lua.Value nil, make_tree(tree.value)
+
+ when "Block"
+ lua = Lua!
+ for i,line in ipairs tree
+ line_lua = @tree_to_lua(line)
+ if i > 1
+ lua\append "\n"
+ lua\append line_lua\as_statements!
+ return lua
+
+ when "Text"
+ lua = Lua.Value!
+ string_buffer = ""
+ for bit in *tree
+ if type(bit) == "string"
+ string_buffer ..= bit
+ continue
+ if string_buffer ~= ""
+ if #lua.bits > 0 then lua\append ".."
+ lua\append repr(string_buffer)
+ string_buffer = ""
+ bit_lua = @tree_to_lua(bit)
+ unless bit_lua.is_value
+ error "Cannot use #{colored.yellow repr(bit)} as a string interpolation value, since it's not an expression.", 0
+ if #lua.bits > 0 then lua\append ".."
+ if bit.type != "Text"
+ bit_lua = Lua.Value(nil, "stringify(",bit_lua,")")
+ lua\append bit_lua
+
+ if string_buffer ~= "" or #lua.bits == 0
+ if #lua.bits > 0 then lua\append ".."
+ lua\append repr(string_buffer)
+
+ if #lua.bits > 1
+ lua\parenthesize!
+ return lua
+
+ when "List"
+ lua = Lua.Value nil, "{"
+ line_length = 0
+ for i, item in ipairs tree
+ item_lua = @tree_to_lua(item)
+ unless item_lua.is_value
+ error "Cannot use #{colored.yellow repr(item)} as a list item, since it's not an expression.", 0
+ lua\append item_lua
+ item_string = tostring(item_lua)
+ last_line = item_string\match("[^\n]*$")
+ if item_string\match("\n")
+ line_length = #last_line
+ else
+ line_length += #last_line
+ if i < #tree
+ if line_length >= MAX_LINE
+ lua\append ",\n "
+ line_length = 0
+ else
+ lua\append ", "
+ line_length += 2
+ lua\append "}"
+ return lua
+
+ when "Dict"
+ lua = Lua.Value nil, "{"
+ line_length = 0
+ for i, entry in ipairs tree
+ entry_lua = @tree_to_lua(entry)
+ 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]*)$")
+ if last_line
+ line_length = #last_line
+ else
+ line_length += #entry_lua_str
+ if i < #tree
+ if line_length >= MAX_LINE
+ lua\append ",\n "
+ line_length = 0
+ else
+ lua\append ", "
+ line_length += 2
+ lua\append "}"
+ return lua
+
+ when "DictEntry"
+ key, value = tree[1], tree[2]
+ key_lua = @tree_to_lua(key)
+ unless key_lua.is_value
+ error "Cannot use #{colored.yellow repr(key)} as a dict key, since it's not an expression.", 0
+ value_lua = value and @tree_to_lua(value) or Lua.Value(nil, "true")
+ unless value_lua.is_value
+ error "Cannot use #{colored.yellow repr(value)} as a dict value, since it's not an expression.", 0
+ key_str = tostring(key_lua)\match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=])
+ return if key_str
+ Lua nil, key_str,"=",value_lua
+ elseif tostring(key_lua)\sub(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"]
+ Lua nil, "[ ",key_lua,"]=",value_lua
+ else
+ Lua nil, "[",key_lua,"]=",value_lua
+
+ when "IndexChain"
+ lua = @tree_to_lua(tree[1])
+ unless lua.is_value
+ error "Cannot index #{colored.yellow repr(tree[1])}, since it's not an expression.", 0
+ first_char = tostring(lua)\sub(1,1)
+ if first_char == "{" or first_char == '"' or first_char == "["
+ lua\parenthesize!
+
+ for i=2,#tree
+ key = tree[i]
+ key_lua = @tree_to_lua(key)
+ unless key_lua.is_value
+ error "Cannot use #{colored.yellow repr(key)} as an index, since it's not an expression.", 0
+ key_lua_str = tostring(key_lua)
+ if lua_id = key_lua_str\match("^['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]$")
+ lua\append ".#{lua_id}"
+ elseif key_lua_str\sub(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"]
+ lua\append "[ ",key_lua," ]"
+ else
+ lua\append "[",key_lua,"]"
+ return lua
+
+ when "Number"
+ Lua.Value(nil, tostring(tree.value))
+
+ when "Var"
+ Lua.Value(nil, tree\as_lua_id!)
+
+ when "Word"
+ error("Cannot convert a Word to lua")
+
+ when "Comment"
+ Lua(nil, "--"..tree.value\gsub("\n","\n--").."\n")
+
+ else
+ error("Unknown type: #{tree.type}")
+
+
+ tree_to_nomsu: (tree, inline=false, can_use_colon=false)=>
+ switch tree.type
+ when "Action"
+ if inline
+ nomsu = Nomsu!
+ for i,bit in ipairs tree
+ if bit.type == "Word"
+ if i > 1
+ nomsu\append " "
+ nomsu\append bit.value
+ else
+ arg_nomsu = @tree_to_nomsu(bit,true)
+ return nil unless arg_nomsu
+ unless i == 1
+ nomsu\append " "
+ if bit.type == "Action" or bit.type == "Block"
+ arg_nomsu\parenthesize!
+ nomsu\append arg_nomsu
+ return nomsu
+ else
+ nomsu = Nomsu!
+ next_space = ""
+ -- TODO: track line length as we go and use 80-that instead of 80 for wrapping
+ last_colon = nil
+ for i,bit in ipairs tree
+ if bit.type == "Word"
+ nomsu\append next_space, bit.value
+ next_space = " "
+ else
+ arg_nomsu = if last_colon == i-1 and bit.type == "Action" then nil
+ elseif bit.type == "Block" then nil
+ else @tree_to_nomsu(bit,true)
+
+ 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
+ next_space = "\n.."
+ last_colon = i
+ else
+ nomsu\append next_space, "(", arg_nomsu, ")"
+ next_space = " "
+ else
+ nomsu\append next_space, arg_nomsu
+ next_space = " "
+ else
+ arg_nomsu = @tree_to_nomsu(bit, nil, true)
+ return nil unless nomsu
+ -- These types carry their own indentation
+ if bit.type != "List" and bit.type != "Dict" and bit.type != "Text"
+ if i == 1
+ arg_nomsu = Nomsu(nil, "(..)\n ", arg_nomsu)
+ else
+ arg_nomsu = Nomsu(nil, "\n ", arg_nomsu)
+
+ if last_colon == i-1 and (bit.type == "Action" or bit.type == "Block")
+ next_space = ""
+ nomsu\append next_space, arg_nomsu
+ next_space = "\n.."
+
+ if next_space == " " and #(tostring(nomsu)\match("[^\n]*$")) > MAX_LINE
+ next_space = "\n.."
+ return nomsu
+
+ when "EscapedNomsu"
+ nomsu = @tree_to_nomsu(tree.value, true)
+ if nomsu == nil and not inline
+ nomsu = @tree_to_nomsu(tree.value)
+ return nomsu and Nomsu nil, "\\:\n ", nomsu
+ return nomsu and Nomsu nil, "\\(", nomsu, ")"
+
+ when "Block"
+ if inline
+ nomsu = Nomsu!
+ for i,line in ipairs @
+ if i > 1
+ nomsu\append "; "
+ line_nomsu = @tree_to_nomsu(line,true)
+ return nil unless line_nomsu
+ nomsu\append line_nomsu
+ return nomsu
+ nomsu = Nomsu!
+ for i, line in ipairs @
+ line = assert(@tree_to_nomsu(line, nil, true), "Could not convert line to nomsu")
+ nomsu\append line
+ if i < #@
+ nomsu\append "\n"
+ if tostring(line)\match("\n")
+ nomsu\append "\n"
+ return nomsu
+
+ when "Text"
+ if inline
+ nomsu = Nomsu(nil, '"')
+ for bit in *tree
+ if type(bit) == 'string'
+ -- TODO: unescape better?
+ nomsu\append (bit\gsub("\\","\\\\")\gsub("\n","\\n"))
+ else
+ interp_nomsu = @tree_to_nomsu(bit, true)
+ if interp_nomsu
+ if bit.type != "Word" and bit.type != "List" and bit.type != "Dict" and bit.type != "Text"
+ interp_nomsu\parenthesize!
+ nomsu\append "\\", interp_nomsu
+ else return nil
+ nomsu\append '"'
+ return nomsu
+ else
+ inline_version = @tree_to_nomsu(tree, true)
+ if inline_version and #inline_version <= MAX_LINE
+ return inline_version
+ nomsu = Nomsu(nil, '".."\n ')
+ for i, bit in ipairs @
+ if type(bit) == 'string'
+ nomsu\append (bit\gsub("\\","\\\\")\gsub("\n","\n "))
+ else
+ interp_nomsu = @tree_to_nomsu(bit, true)
+ if interp_nomsu
+ if bit.type != "Word" and bit.type != "List" and bit.type != "Dict" and bit.type != "Text"
+ interp_nomsu\parenthesize!
+ nomsu\append "\\", interp_nomsu
+ else
+ interp_nomsu = @tree_to_nomsu(bit)
+ return nil unless interp_nomsu
+ nomsu\append "\\\n ", interp_nomsu
+ if i < #@
+ nomsu\append "\n .."
+ return nomsu
+
+ when "List"
+ if inline
+ nomsu = Nomsu(nil, "[")
+ for i, item in ipairs tree
+ item_nomsu = @tree_to_nomsu(item, true)
+ return nil unless item_nomsu
+ if i > 1
+ nomsu\append ", "
+ nomsu\append item_nomsu
+ nomsu\append "]"
+ return nomsu
+ else
+ inline_version = @tree_to_nomsu(tree, true)
+ if inline_version and #inline_version <= MAX_LINE
+ return inline_version
+ nomsu = Nomsu(nil, "[..]")
+ line = Nomsu(nil, "\n ")
+ for item in *tree
+ item_nomsu = @tree_to_nomsu(item, true)
+ if item_nomsu and #line + #", " + #item_nomsu <= MAX_LINE
+ if #line.bits > 1
+ line\append ", "
+ line\append item_nomsu
+ else
+ unless item_nomsu
+ item_nomsu = @tree_to_nomsu(item)
+ return nil unless item_nomsu
+ if #line.bits > 1
+ nomsu\append line
+ line = Nomsu(nil, "\n ")
+ line\append item_nomsu
+ if #line.bits > 1
+ nomsu\append line
+ return nomsu
+
+ when "Dict"
+ if inline
+ nomsu = Nomsu(nil, "{")
+ for i, entry in ipairs tree
+ entry_nomsu = @tree_to_nomsu(entry, true)
+ return nil unless entry_nomsu
+ if i > 1
+ nomsu\append ", "
+ nomsu\append entry_nomsu
+ nomsu\append "}"
+ return nomsu
+ else
+ inline_version = @tree_to_nomsu(tree, true)
+ if inline_version then return inline_version
+ nomsu = Nomsu(nil, "{..}")
+ line = Nomsu(nil, "\n ")
+ for entry in *tree
+ entry_nomsu = @tree_to_nomsu(entry)
+ return nil unless entry_nomsu
+ if #line + #tostring(entry_nomsu) <= MAX_LINE
+ if #line.bits > 1
+ line\append ", "
+ line\append entry_nomsu
+ else
+ if #line.bits > 1
+ nomsu\append line
+ line = Nomsu(nil, "\n ")
+ line\append entry_nomsu
+ if #line.bits > 1
+ nomsu\append line
+ return nomsu
+
+ when "DictEntry"
+ key, value = tree[1], tree[2]
+ key_nomsu = @tree_to_nomsu(key, true)
+ return nil unless key_nomsu
+ if key.type == "Action" or key.type == "Block"
+ key_nomsu\parenthesize!
+ value_nomsu = if value
+ @tree_to_nomsu(value, true)
+ else Nomsu(nil, "")
+ if inline and not value_nomsu then return nil
+ if not value_nomsu
+ return nil if inline
+ value_nomsu = @tree_to_nomsu(value)
+ return nil unless value_nomsu
+ return Nomsu nil, key_nomsu, ":", value_nomsu
+
+ when "IndexChain"
+ nomsu = Nomsu!
+ for i, bit in ipairs tree
+ if i > 1
+ nomsu\append "."
+ bit_nomsu = @tree_to_nomsu(bit, true)
+ return nil unless bit_nomsu
+ if bit.type == "Action" or bit.type == "Block"
+ bit_nomsu\parenthesize!
+ nomsu\append bit_nomsu
+ return nomsu
+
+ when "Number"
+ return Nomsu(nil, tostring(tree.value))
+
+ when "Var"
+ return Nomsu(nil, "%", tree.value)
+
+ when "Word"
+ return Nomsu(nil, tree.value)
+
+ when "Comment"
+ return nil if inline
+ return Nomsu(nil, "#", tree.value\gsub("\n", "\n "))
+
+ else
+ error("Unknown type: #{tree.type}")
tree_to_value: (tree)=>
-- Special case for text literals
if tree.type == 'Text' and #tree == 1 and type(tree[1]) == 'string'
return tree[1]
- lua = Lua(nil, "return ",tree\as_lua(@),";")
+ lua = Lua(nil, "return ",@tree_to_lua(tree),";")
return @run_lua(lua)
walk_tree: (tree, depth=0)=>
@@ -418,35 +867,25 @@ class NomsuCompiler
else
@walk_tree(v, depth+1)
- tree_with_replacements: (tree, replacements)=>
- return tree unless next(replacements)
- if next(replacements).type == "Var"
- replacements = {tostring(k\as_lua(@)),v for k,v in pairs(replacements)}
- tree\map (t)->
- if t.type == "Var"
- id = tostring(t\as_lua(self))
- if replacements[id] != nil
- return replacements[id]
-
initialize_core: =>
-- Sets up some core functionality
nomsu = self
@define_compile_action "immediately %block", (_block)=>
- lua = _block\as_lua(nomsu)\as_statements!
+ lua = nomsu\tree_to_lua(_block)\as_statements!
lua\declare_locals!
nomsu\run_lua(lua)
return Lua(nil, "if IMMEDIATE then\n ", lua, "\nend")
add_lua_string_bits = (lua, code)->
if code.type != "Text"
- lua\append ", ", code\as_lua(nomsu)
+ lua\append ", ", nomsu\tree_to_lua(code)
return
for bit in *code
lua\append ", "
if type(bit) == "string"
lua\append repr(bit)
else
- bit_lua = bit\as_lua(nomsu)
+ bit_lua = nomsu\tree_to_lua(bit)
unless bit_lua.is_value
error "Cannot use #{colored.yellow repr(bit)} as a string interpolation value, since it's not an expression."
lua\append bit_lua
@@ -468,7 +907,7 @@ class NomsuCompiler
if type(bit) == "string"
lua\append bit
else
- bit_lua = bit\as_lua(nomsu)
+ bit_lua = nomsu\tree_to_lua(bit)
unless bit_lua.is_value
error "Cannot use #{colored.yellow repr(bit)} as a string interpolation value, since it's not an expression.", 0
lua\append bit_lua
@@ -476,12 +915,12 @@ class NomsuCompiler
@define_compile_action "lua> %code", (_code)=>
if _code.type != "Text"
- return Lua nil, "nomsu:run_lua(", _code\as_lua(nomsu), ");"
+ return Lua nil, "nomsu:run_lua(", nomsu\tree_to_lua(_code), ");"
return add_lua_bits(Lua!, _code)
@define_compile_action "=lua %code", (_code)=>
if _code.type != "Text"
- return Lua.Value nil, "nomsu:run_lua(", _code\as_lua(nomsu), ":as_statements('return '))"
+ return Lua.Value nil, "nomsu:run_lua(", nomsu\tree_to_lua(_code), ":as_statements('return '))"
return add_lua_bits(Lua.Value!, _code)
@define_compile_action "use %path", (_path)=>
@@ -669,7 +1108,7 @@ OPTIONS
-- Auto-format
for input_file in all_files(input)
tree = nomsu\parse(io.open(input_file)\read("*a"))
- formatted = tostring(tree\as_nomsu!)
+ formatted = tostring(@tree_to_nomsu(tree))
if output_file
output_file\write(formatted, "\n")
output_file\flush!
diff --git a/nomsu_tree.lua b/nomsu_tree.lua
index 4d30f38..f4f10b2 100644
--- a/nomsu_tree.lua
+++ b/nomsu_tree.lua
@@ -19,6 +19,7 @@ Types.is_node = function(n)
end
local Tree
Tree = function(name, kind, methods)
+ methods = methods or { }
assert((kind == 'single') or (kind == 'multi'))
local is_multi = (kind == 'multi')
do
@@ -28,6 +29,21 @@ Tree = function(name, kind, methods)
methods.type = name
methods.name = name
methods.is_multi = is_multi
+ methods.map = function(self, fn)
+ if type(fn) == 'table' then
+ if not (next(fn)) then
+ return self
+ end
+ if type(next(fn)) == 'string' then
+ error("SHIT")
+ end
+ local _replacements = fn
+ fn = function(k)
+ return _replacements[k]
+ end
+ end
+ return self:_map(fn)
+ end
if is_multi then
methods.__tostring = function(self)
return tostring(self.name) .. "(" .. tostring(table.concat((function()
@@ -79,191 +95,32 @@ Tree = function(name, kind, methods)
}, methods)
end
end
+Tree("Block", 'multi')
+Tree("Text", 'multi')
+Tree("List", 'multi')
+Tree("Dict", 'multi')
+Tree("DictEntry", 'multi')
+Tree("IndexChain", 'multi')
+Tree("Number", 'single')
+Tree("Word", 'single')
+Tree("Comment", 'single')
Tree("EscapedNomsu", 'single', {
- as_lua = function(self, nomsu)
- local make_tree
- make_tree = function(t)
- if type(t) ~= 'userdata' then
- return repr(t)
- end
- if t.is_multi then
- 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
- end
- return t.type .. "(" .. table.concat(bits, ", ") .. ")"
- else
- return t.type .. "(" .. make_tree(t.value) .. ")"
- end
- end
- return Lua.Value(nil, make_tree(self.value))
- end,
- as_nomsu = function(self, inline)
- if inline == nil then
- inline = false
- end
- local nomsu = self.value:as_nomsu(true)
- if nomsu == nil and not inline then
- nomsu = self.value:as_nomsu()
- return nomsu and Nomsu(nil, "\\:\n ", nomsu)
- end
- return nomsu and Nomsu(nil, "\\(", nomsu, ")")
- end,
map = function(self, fn)
return fn(self) or self:map(fn)
end
})
-Tree("Block", 'multi', {
- as_lua = function(self, nomsu)
- local lua = Lua()
- for i, line in ipairs(self) do
- local line_lua = line:as_lua(nomsu)
- if i > 1 then
- lua:append("\n")
- end
- lua:append(line_lua:as_statements())
- end
- return lua
- end,
- as_nomsu = function(self, inline)
- if inline == nil then
- inline = false
- end
- if inline then
- local nomsu = Nomsu()
- for i, line in ipairs(self) do
- if i > 1 then
- nomsu:append("; ")
- end
- local line_nomsu = line:as_nomsu(true)
- if not (line_nomsu) then
- return nil
- end
- nomsu:append(line_nomsu)
- end
- return nomsu
- end
- local nomsu = Nomsu()
- for i, line in ipairs(self) do
- line = assert(line:as_nomsu(nil, true), "Could not convert line to nomsu")
- nomsu:append(line)
- if i < #self then
- nomsu:append("\n")
- if tostring(line):match("\n") then
- nomsu:append("\n")
- end
+Tree("Var", 'single', {
+ as_lua_id = function(self)
+ return "_" .. (self.value:gsub("%W", function(c)
+ if c == "_" then
+ return "__"
+ else
+ return ("_%x"):format(c:byte())
end
- end
- return nomsu
+ end))
end
})
-local math_expression = re.compile([[ ([+-] " ")* "%" (" " [*/^+-] (" " [+-])* " %")+ !. ]])
Tree("Action", 'multi', {
- as_lua = function(self, nomsu)
- local stub = self:get_stub()
- local compile_action = nomsu.environment.COMPILE_ACTIONS[stub]
- if compile_action then
- local args
- do
- local _accum_0 = { }
- local _len_0 = 1
- for _index_0 = 1, #self do
- local arg = self[_index_0]
- if arg.type ~= "Word" then
- _accum_0[_len_0] = arg
- _len_0 = _len_0 + 1
- end
- end
- args = _accum_0
- end
- do
- local _accum_0 = { }
- local _len_0 = 1
- local _list_0 = nomsu.environment.ARG_ORDERS[compile_action][stub]
- for _index_0 = 1, #_list_0 do
- local p = _list_0[_index_0]
- _accum_0[_len_0] = args[p - 1]
- _len_0 = _len_0 + 1
- end
- args = _accum_0
- end
- local ret = compile_action(self, unpack(args))
- if not ret then
- error("Failed to produce any Lua")
- end
- return ret
- end
- local action = rawget(nomsu.environment.ACTIONS, stub)
- local lua = Lua.Value()
- if not action and math_expression:match(stub) then
- for i, tok in ipairs(self) do
- if tok.type == "Word" then
- lua:append(tok.value)
- else
- local tok_lua = tok:as_lua(nomsu)
- if not (tok_lua.is_value) then
- error("non-expression value inside math expression: " .. tostring(colored.yellow(repr(tok))))
- end
- if tok.type == "Action" then
- tok_lua:parenthesize()
- end
- lua:append(tok_lua)
- end
- if i < #self then
- lua:append(" ")
- end
- end
- return lua
- end
- local args = { }
- for i, tok in ipairs(self) do
- local _continue_0 = false
- repeat
- if tok.type == "Word" then
- _continue_0 = true
- break
- end
- local arg_lua = tok:as_lua(nomsu)
- if not (arg_lua.is_value) then
- error("Cannot use:\n" .. tostring(colored.yellow(repr(tok))) .. "\nas an argument to " .. tostring(stub) .. ", since it's not an expression, it produces: " .. tostring(repr(arg_lua)), 0)
- end
- insert(args, arg_lua)
- _continue_0 = true
- until true
- if not _continue_0 then
- break
- end
- end
- if action then
- do
- local _accum_0 = { }
- local _len_0 = 1
- local _list_0 = nomsu.environment.ARG_ORDERS[action][stub]
- for _index_0 = 1, #_list_0 do
- local p = _list_0[_index_0]
- _accum_0[_len_0] = args[p]
- _len_0 = _len_0 + 1
- end
- args = _accum_0
- end
- end
- lua:append("ACTIONS[", repr(stub), "](")
- for i, arg in ipairs(args) do
- lua:append(arg)
- if i < #args then
- lua:append(", ")
- end
- end
- lua:append(")")
- return lua
- end,
get_stub = function(self, include_names)
if include_names == nil then
include_names = false
@@ -293,523 +150,6 @@ Tree("Action", 'multi', {
end
end
return concat(bits, " ")
- end,
- as_nomsu = function(self, inline, can_use_colon)
- if inline == nil then
- inline = false
- end
- if can_use_colon == nil then
- can_use_colon = false
- end
- if inline then
- local nomsu = Nomsu()
- for i, bit in ipairs(self) do
- if bit.type == "Word" then
- if i > 1 then
- nomsu:append(" ")
- end
- nomsu:append(bit.value)
- else
- local arg_nomsu = bit:as_nomsu(true)
- if not (arg_nomsu) then
- return nil
- end
- if not (i == 1) then
- nomsu:append(" ")
- end
- if bit.type == "Action" or bit.type == "Block" then
- arg_nomsu:parenthesize()
- end
- nomsu:append(arg_nomsu)
- end
- end
- return nomsu
- else
- local nomsu = Nomsu()
- local next_space = ""
- local last_colon = nil
- for i, bit in ipairs(self) do
- if bit.type == "Word" then
- nomsu:append(next_space, bit.value)
- next_space = " "
- else
- local arg_nomsu
- if last_colon == i - 1 and bit.type == "Action" then
- arg_nomsu = nil
- elseif bit.type == "Block" then
- arg_nomsu = nil
- else
- arg_nomsu = bit:as_nomsu(true)
- end
- 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)
- next_space = "\n.."
- last_colon = i
- else
- nomsu:append(next_space, "(", arg_nomsu, ")")
- next_space = " "
- end
- else
- nomsu:append(next_space, arg_nomsu)
- next_space = " "
- end
- else
- arg_nomsu = bit:as_nomsu(nil, true)
- if not (nomsu) then
- return nil
- end
- if bit.type ~= "List" and bit.type ~= "Dict" and bit.type ~= "Text" then
- if i == 1 then
- arg_nomsu = Nomsu(nil, "(..)\n ", arg_nomsu)
- else
- arg_nomsu = Nomsu(nil, "\n ", arg_nomsu)
- end
- end
- if last_colon == i - 1 and (bit.type == "Action" or bit.type == "Block") then
- next_space = ""
- end
- nomsu:append(next_space, arg_nomsu)
- next_space = "\n.."
- end
- if next_space == " " and #(tostring(nomsu):match("[^\n]*$")) > MAX_LINE then
- next_space = "\n.."
- end
- end
- end
- return nomsu
- end
- end
-})
-Tree("Text", 'multi', {
- as_lua = function(self, nomsu)
- local lua = Lua.Value()
- local string_buffer = ""
- for _index_0 = 1, #self do
- local _continue_0 = false
- repeat
- local bit = self[_index_0]
- if type(bit) == "string" then
- string_buffer = string_buffer .. bit
- _continue_0 = true
- break
- end
- if string_buffer ~= "" then
- if #lua.bits > 0 then
- lua:append("..")
- end
- lua:append(repr(string_buffer))
- string_buffer = ""
- end
- local bit_lua = bit:as_lua(nomsu)
- if not (bit_lua.is_value) then
- error("Cannot use " .. tostring(colored.yellow(repr(bit))) .. " as a string interpolation value, since it's not an expression.", 0)
- end
- if #lua.bits > 0 then
- lua:append("..")
- end
- if bit.type ~= "Text" then
- bit_lua = Lua.Value(nil, "stringify(", bit_lua, ")")
- end
- lua:append(bit_lua)
- _continue_0 = true
- until true
- if not _continue_0 then
- break
- end
- end
- if string_buffer ~= "" or #lua.bits == 0 then
- if #lua.bits > 0 then
- lua:append("..")
- end
- lua:append(repr(string_buffer))
- end
- if #lua.bits > 1 then
- lua:parenthesize()
- end
- return lua
- end,
- as_nomsu = function(self, inline)
- if inline == nil then
- inline = false
- end
- if inline then
- local nomsu = Nomsu(nil, '"')
- for _index_0 = 1, #self do
- local bit = self[_index_0]
- if type(bit) == 'string' then
- nomsu:append((bit:gsub("\\", "\\\\"):gsub("\n", "\\n")))
- else
- local interp_nomsu = bit:as_nomsu(true)
- if interp_nomsu then
- if bit.type ~= "Word" and bit.type ~= "List" and bit.type ~= "Dict" and bit.type ~= "Text" then
- interp_nomsu:parenthesize()
- end
- nomsu:append("\\", interp_nomsu)
- else
- return nil
- end
- end
- end
- nomsu:append('"')
- return nomsu
- else
- local inline_version = self:as_nomsu(true)
- if inline_version and #inline_version <= MAX_LINE then
- return inline_version
- end
- local nomsu = Nomsu(nil, '".."\n ')
- for i, bit in ipairs(self) do
- if type(bit) == 'string' then
- nomsu:append((bit:gsub("\\", "\\\\"):gsub("\n", "\n ")))
- else
- local interp_nomsu = bit:as_nomsu(true)
- if interp_nomsu then
- if bit.type ~= "Word" and bit.type ~= "List" and bit.type ~= "Dict" and bit.type ~= "Text" then
- interp_nomsu:parenthesize()
- end
- nomsu:append("\\", interp_nomsu)
- else
- interp_nomsu = bit:as_nomsu()
- if not (interp_nomsu) then
- return nil
- end
- nomsu:append("\\\n ", interp_nomsu)
- if i < #self then
- nomsu:append("\n ..")
- end
- end
- end
- end
- return nomsu
- end
- end
-})
-Tree("List", 'multi', {
- as_lua = function(self, nomsu)
- local lua = Lua.Value(nil, "{")
- local line_length = 0
- for i, item in ipairs(self) do
- local item_lua = item:as_lua(nomsu)
- if not (item_lua.is_value) then
- error("Cannot use " .. tostring(colored.yellow(repr(item))) .. " as a list item, since it's not an expression.", 0)
- end
- lua:append(item_lua)
- local item_string = tostring(item_lua)
- local last_line = item_string:match("[^\n]*$")
- if item_string:match("\n") then
- line_length = #last_line
- else
- line_length = line_length + #last_line
- end
- if i < #self then
- if line_length >= MAX_LINE then
- lua:append(",\n ")
- line_length = 0
- else
- lua:append(", ")
- line_length = line_length + 2
- end
- end
- end
- lua:append("}")
- return lua
- end,
- as_nomsu = function(self, inline)
- if inline == nil then
- inline = false
- end
- if inline then
- local nomsu = Nomsu(nil, "[")
- for i, item in ipairs(self) do
- local item_nomsu = item:as_nomsu(true)
- if not (item_nomsu) then
- return nil
- end
- if i > 1 then
- nomsu:append(", ")
- end
- nomsu:append(item_nomsu)
- end
- nomsu:append("]")
- return nomsu
- else
- local inline_version = self:as_nomsu(true)
- if inline_version and #inline_version <= MAX_LINE then
- return inline_version
- end
- local nomsu = Nomsu(nil, "[..]")
- local line = Nomsu(nil, "\n ")
- for _index_0 = 1, #self do
- local item = self[_index_0]
- local item_nomsu = item:as_nomsu(true)
- if item_nomsu and #line + #", " + #item_nomsu <= MAX_LINE then
- if #line.bits > 1 then
- line:append(", ")
- end
- line:append(item_nomsu)
- else
- if not (item_nomsu) then
- item_nomsu = item:as_nomsu()
- if not (item_nomsu) then
- return nil
- end
- end
- if #line.bits > 1 then
- nomsu:append(line)
- line = Nomsu(nil, "\n ")
- end
- line:append(item_nomsu)
- end
- end
- if #line.bits > 1 then
- nomsu:append(line)
- end
- return nomsu
- end
- end
-})
-Tree("Dict", 'multi', {
- as_lua = function(self, nomsu)
- local lua = Lua.Value(nil, "{")
- local line_length = 0
- for i, entry in ipairs(self) do
- local entry_lua = entry:as_lua(nomsu)
- lua:append(entry_lua)
- local entry_lua_str = tostring(entry_lua)
- local last_line = entry_lua_str:match("\n([^\n]*)$")
- if last_line then
- line_length = #last_line
- else
- line_length = line_length + #entry_lua_str
- end
- if i < #self then
- if line_length >= MAX_LINE then
- lua:append(",\n ")
- line_length = 0
- else
- lua:append(", ")
- line_length = line_length + 2
- end
- end
- end
- lua:append("}")
- return lua
- end,
- as_nomsu = function(self, inline)
- if inline == nil then
- inline = false
- end
- if inline then
- local nomsu = Nomsu(nil, "{")
- for i, entry in ipairs(self) do
- local entry_nomsu = entry:as_nomsu(true)
- if not (entry_nomsu) then
- return nil
- end
- if i > 1 then
- nomsu:append(", ")
- end
- nomsu:append(entry_nomsu)
- end
- nomsu:append("}")
- return nomsu
- else
- local inline_version = self:as_nomsu(true)
- if inline_version then
- return inline_version
- end
- local nomsu = Nomsu(nil, "{..}")
- local line = Nomsu(nil, "\n ")
- for _index_0 = 1, #self do
- local entry = self[_index_0]
- local entry_nomsu = entry:as_nomsu()
- if not (entry_nomsu) then
- return nil
- end
- if #line + #tostring(entry_nomsu) <= MAX_LINE then
- if #line.bits > 1 then
- line:append(", ")
- end
- line:append(entry_nomsu)
- else
- if #line.bits > 1 then
- nomsu:append(line)
- line = Nomsu(nil, "\n ")
- end
- line:append(entry_nomsu)
- end
- end
- if #line.bits > 1 then
- nomsu:append(line)
- end
- return nomsu
- end
- end
-})
-Tree("DictEntry", 'multi', {
- as_lua = function(self, nomsu)
- local key, value = self[1], self[2]
- local key_lua = key:as_lua(nomsu)
- if not (key_lua.is_value) then
- error("Cannot use " .. tostring(colored.yellow(repr(key))) .. " as a dict key, since it's not an expression.", 0)
- end
- local value_lua = value and value:as_lua(nomsu) or Lua.Value(nil, "true")
- if not (value_lua.is_value) then
- error("Cannot use " .. tostring(colored.yellow(repr(value))) .. " as a dict value, since it's not an expression.", 0)
- end
- local key_str = tostring(key_lua):match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=])
- if key_str then
- return Lua(nil, key_str, "=", value_lua)
- elseif tostring(key_lua):sub(1, 1) == "[" then
- return Lua(nil, "[ ", key_lua, "]=", value_lua)
- else
- return Lua(nil, "[", key_lua, "]=", value_lua)
- end
- end,
- as_nomsu = function(self, inline)
- if inline == nil then
- inline = true
- end
- local key, value = self[1], self[2]
- local key_nomsu = key:as_nomsu(true)
- if not (key_nomsu) then
- return nil
- end
- if key.type == "Action" or key.type == "Block" then
- key_nomsu:parenthesize()
- end
- local value_nomsu
- if value then
- value_nomsu = value:as_nomsu(true)
- else
- value_nomsu = Nomsu(nil, "")
- end
- if inline and not value_nomsu then
- return nil
- end
- if not value_nomsu then
- if inline then
- return nil
- end
- value_nomsu = value:as_nomsu()
- if not (value_nomsu) then
- return nil
- end
- end
- return Nomsu(nil, key_nomsu, ":", value_nomsu)
- end
-})
-Tree("IndexChain", 'multi', {
- as_lua = function(self, nomsu)
- local lua = self[1]:as_lua(nomsu)
- if not (lua.is_value) then
- error("Cannot index " .. tostring(colored.yellow(repr(self[1]))) .. ", since it's not an expression.", 0)
- end
- local first_char = tostring(lua):sub(1, 1)
- if first_char == "{" or first_char == '"' or first_char == "[" then
- lua:parenthesize()
- end
- for i = 2, #self do
- local key = self[i]
- local key_lua = key:as_lua(nomsu)
- if not (key_lua.is_value) then
- error("Cannot use " .. tostring(colored.yellow(repr(key))) .. " as an index, since it's not an expression.", 0)
- end
- local key_lua_str = tostring(key_lua)
- do
- local lua_id = key_lua_str:match("^['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]$")
- if lua_id then
- lua:append("." .. tostring(lua_id))
- elseif key_lua_str:sub(1, 1) == '[' then
- lua:append("[ ", key_lua, " ]")
- else
- lua:append("[", key_lua, "]")
- end
- end
- end
- return lua
- end,
- as_nomsu = function(self, inline)
- if inline == nil then
- inline = false
- end
- local nomsu = Nomsu()
- for i, bit in ipairs(self) do
- if i > 1 then
- nomsu:append(".")
- end
- local bit_nomsu = bit:as_nomsu(true)
- if not (bit_nomsu) then
- return nil
- end
- if bit.type == "Action" or bit.type == "Block" then
- bit_nomsu:parenthesize()
- end
- nomsu:append(bit_nomsu)
- end
- return nomsu
- end
-})
-Tree("Number", 'single', {
- as_lua = function(self, nomsu)
- return Lua.Value(nil, tostring(self.value))
- end,
- as_nomsu = function(self, inline)
- if inline == nil then
- inline = false
- end
- return Nomsu(nil, tostring(self.value))
- end
-})
-Tree("Var", 'single', {
- as_lua_id = function(v)
- return "_" .. (v:gsub("%W", function(c)
- if c == "_" then
- return "__"
- else
- return ("_%x"):format(c:byte())
- end
- end))
- end,
- as_lua = function(self, nomsu)
- return Lua.Value(nil, self.as_lua_id(self.value))
- end,
- as_nomsu = function(self, inline)
- if inline == nil then
- inline = false
- end
- return Nomsu(nil, "%", self.value)
- end
-})
-Tree("Word", 'single', {
- as_lua = function(self, nomsu)
- return error("Attempt to convert Word to lua")
- end,
- as_nomsu = function(self, inline)
- if inline == nil then
- inline = false
- end
- return Nomsu(nil, self.value)
- end
-})
-Tree("Comment", 'single', {
- as_lua = function(self, nomsu)
- return Lua(nil, "--" .. self.value:gsub("\n", "\n--") .. "\n")
- end,
- as_nomsu = function(self, inline)
- if inline == nil then
- inline = false
- end
- if inline then
- return nil
- end
- if self.value:match("\n") then
- return Nomsu(nil, "#..", self.value:gsub("\n", "\n "))
- else
- return Nomsu(nil, "#", self.value)
- end
end
})
return Types
diff --git a/nomsu_tree.moon b/nomsu_tree.moon
index 11fed94..c15d29c 100644
--- a/nomsu_tree.moon
+++ b/nomsu_tree.moon
@@ -14,6 +14,7 @@ Types.is_node = (n)->
-- Helper method:
Tree = (name, kind, methods)->
+ methods or= {}
assert((kind == 'single') or (kind == 'multi'))
is_multi = (kind == 'multi')
with methods
@@ -21,6 +22,14 @@ Tree = (name, kind, methods)->
.type = name
.name = name
.is_multi = is_multi
+ .map = (fn)=>
+ if type(fn) == 'table'
+ return @ unless next(fn)
+ if type(next(fn)) == 'string'
+ error("SHIT")
+ _replacements = fn
+ fn = (k)-> _replacements[k]
+ return @_map(fn)
if is_multi
.__tostring = => "#{@name}(#{table.concat [repr(v) for v in *@], ', '})"
.map = (fn)=>
@@ -31,476 +40,36 @@ Tree = (name, kind, methods)->
return ret
else
.__tostring = => "#{@name}(#{repr(@value)})"
- .map = (fn)=> fn(@) or @
+ .map = (fn)=>
+ fn(@) or @
if is_multi
Types[name] = immutable nil, methods
else
Types[name] = immutable {"value"}, methods
+Tree "Block", 'multi'
+Tree "Text", 'multi'
+Tree "List", 'multi'
+Tree "Dict", 'multi'
+Tree "DictEntry", 'multi'
+Tree "IndexChain", 'multi'
+Tree "Number", 'single'
+Tree "Word", 'single'
+Tree "Comment", 'single'
Tree "EscapedNomsu", 'single',
- as_lua: (nomsu)=>
- make_tree = (t)->
- if type(t) != 'userdata'
- return repr(t)
- if t.is_multi
- bits = [make_tree(bit) for bit in *t]
- return t.type.."("..table.concat(bits, ", ")..")"
- else
- return t.type.."("..make_tree(t.value)..")"
- Lua.Value nil, make_tree(@value)
-
- as_nomsu: (inline=false)=>
- nomsu = @value\as_nomsu(true)
- if nomsu == nil and not inline
- nomsu = @value\as_nomsu!
- return nomsu and Nomsu nil, "\\:\n ", nomsu
- return nomsu and Nomsu nil, "\\(", nomsu, ")"
-
map: (fn)=> fn(@) or @\map(fn)
-Tree "Block", 'multi',
- as_lua: (nomsu)=>
- lua = Lua!
- for i,line in ipairs @
- line_lua = line\as_lua(nomsu)
- if i > 1
- lua\append "\n"
- lua\append line_lua\as_statements!
- return lua
-
- as_nomsu: (inline=false)=>
- if inline
- nomsu = Nomsu!
- for i,line in ipairs @
- if i > 1
- nomsu\append "; "
- line_nomsu = line\as_nomsu(true)
- return nil unless line_nomsu
- nomsu\append line_nomsu
- return nomsu
- nomsu = Nomsu!
- for i, line in ipairs @
- line = assert(line\as_nomsu(nil, true), "Could not convert line to nomsu")
- nomsu\append line
- if i < #@
- nomsu\append "\n"
- if tostring(line)\match("\n")
- nomsu\append "\n"
- return nomsu
+Tree "Var", 'single',
+ as_lua_id: =>
+ "_"..(@value\gsub("%W", (c)-> if c == "_" then "__" else ("_%x")\format(c\byte!)))
-math_expression = re.compile [[ ([+-] " ")* "%" (" " [*/^+-] (" " [+-])* " %")+ !. ]]
Tree "Action", 'multi',
- as_lua: (nomsu)=>
- stub = @get_stub!
- compile_action = nomsu.environment.COMPILE_ACTIONS[stub]
- if compile_action
- args = [arg for arg in *@ when arg.type != "Word"]
- -- Force all compile-time actions to take a tree location
- args = [args[p-1] for p in *nomsu.environment.ARG_ORDERS[compile_action][stub]]
- -- Force Lua to avoid tail call optimization for debugging purposes
- ret = compile_action(self, unpack(args))
- if not ret then error("Failed to produce any Lua")
- return ret
- action = rawget(nomsu.environment.ACTIONS, stub)
- lua = Lua.Value!
- if not action and math_expression\match(stub)
- -- This is a bit of a hack, but this code handles arbitrarily complex
- -- math expressions like 2*x + 3^2 without having to define a single
- -- action for every possibility.
- for i,tok in ipairs @
- if tok.type == "Word"
- lua\append tok.value
- else
- tok_lua = tok\as_lua(nomsu)
- unless tok_lua.is_value
- error("non-expression value inside math expression: #{colored.yellow repr(tok)}")
- if tok.type == "Action"
- tok_lua\parenthesize!
- lua\append tok_lua
- if i < #@
- lua\append " "
- return lua
-
- args = {}
- for i, tok in ipairs @
- if tok.type == "Word" then continue
- arg_lua = tok\as_lua(nomsu)
- unless arg_lua.is_value
- error "Cannot use:\n#{colored.yellow repr(tok)}\nas an argument to #{stub}, since it's not an expression, it produces: #{repr arg_lua}", 0
- insert args, arg_lua
-
- if action
- args = [args[p] for p in *nomsu.environment.ARG_ORDERS[action][stub]]
-
- -- Not really worth bothering with ACTIONS.foo(...) style since almost every action
- -- has arguments, so it won't work
- lua\append "ACTIONS[",repr(stub),"]("
- for i, arg in ipairs args
- lua\append arg
- if i < #args then lua\append ", "
- lua\append ")"
- return lua
-
get_stub: (include_names=false)=>
bits = if include_names
[(t.type == "Word" and t.value or "%#{t.value}") for t in *@]
else [(t.type == "Word" and t.value or "%") for t in *@]
return concat(bits, " ")
- as_nomsu: (inline=false, can_use_colon=false)=>
- if inline
- nomsu = Nomsu!
- for i,bit in ipairs @
- if bit.type == "Word"
- if i > 1
- nomsu\append " "
- nomsu\append bit.value
- else
- arg_nomsu = bit\as_nomsu(true)
- return nil unless arg_nomsu
- unless i == 1
- nomsu\append " "
- if bit.type == "Action" or bit.type == "Block"
- arg_nomsu\parenthesize!
- nomsu\append arg_nomsu
- return nomsu
- else
- nomsu = Nomsu!
- next_space = ""
- -- TODO: track line length as we go and use 80-that instead of 80 for wrapping
- last_colon = nil
- for i,bit in ipairs @
- if bit.type == "Word"
- nomsu\append next_space, bit.value
- next_space = " "
- else
- arg_nomsu = if last_colon == i-1 and bit.type == "Action" then nil
- elseif bit.type == "Block" then nil
- else bit\as_nomsu(true)
-
- 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
- next_space = "\n.."
- last_colon = i
- else
- nomsu\append next_space, "(", arg_nomsu, ")"
- next_space = " "
- else
- nomsu\append next_space, arg_nomsu
- next_space = " "
- else
- arg_nomsu = bit\as_nomsu(nil, true)
- return nil unless nomsu
- -- These types carry their own indentation
- if bit.type != "List" and bit.type != "Dict" and bit.type != "Text"
- if i == 1
- arg_nomsu = Nomsu(nil, "(..)\n ", arg_nomsu)
- else
- arg_nomsu = Nomsu(nil, "\n ", arg_nomsu)
-
- if last_colon == i-1 and (bit.type == "Action" or bit.type == "Block")
- next_space = ""
- nomsu\append next_space, arg_nomsu
- next_space = "\n.."
-
- if next_space == " " and #(tostring(nomsu)\match("[^\n]*$")) > MAX_LINE
- next_space = "\n.."
- return nomsu
-
-Tree "Text", 'multi',
- as_lua: (nomsu)=>
- lua = Lua.Value!
- string_buffer = ""
- for bit in *@
- if type(bit) == "string"
- string_buffer ..= bit
- continue
- if string_buffer ~= ""
- if #lua.bits > 0 then lua\append ".."
- lua\append repr(string_buffer)
- string_buffer = ""
- bit_lua = bit\as_lua(nomsu)
- unless bit_lua.is_value
- error "Cannot use #{colored.yellow repr(bit)} as a string interpolation value, since it's not an expression.", 0
- if #lua.bits > 0 then lua\append ".."
- if bit.type != "Text"
- bit_lua = Lua.Value(nil, "stringify(",bit_lua,")")
- lua\append bit_lua
-
- if string_buffer ~= "" or #lua.bits == 0
- if #lua.bits > 0 then lua\append ".."
- lua\append repr(string_buffer)
-
- if #lua.bits > 1
- lua\parenthesize!
- return lua
-
- as_nomsu: (inline=false)=>
- if inline
- nomsu = Nomsu(nil, '"')
- for bit in *@
- if type(bit) == 'string'
- -- TODO: unescape better?
- nomsu\append (bit\gsub("\\","\\\\")\gsub("\n","\\n"))
- else
- interp_nomsu = bit\as_nomsu(true)
- if interp_nomsu
- if bit.type != "Word" and bit.type != "List" and bit.type != "Dict" and bit.type != "Text"
- interp_nomsu\parenthesize!
- nomsu\append "\\", interp_nomsu
- else return nil
- nomsu\append '"'
- return nomsu
- else
- inline_version = @as_nomsu(true)
- if inline_version and #inline_version <= MAX_LINE
- return inline_version
- nomsu = Nomsu(nil, '".."\n ')
- for i, bit in ipairs @
- if type(bit) == 'string'
- nomsu\append (bit\gsub("\\","\\\\")\gsub("\n","\n "))
- else
- interp_nomsu = bit\as_nomsu(true)
- if interp_nomsu
- if bit.type != "Word" and bit.type != "List" and bit.type != "Dict" and bit.type != "Text"
- interp_nomsu\parenthesize!
- nomsu\append "\\", interp_nomsu
- else
- interp_nomsu = bit\as_nomsu!
- return nil unless interp_nomsu
- nomsu\append "\\\n ", interp_nomsu
- if i < #@
- nomsu\append "\n .."
- return nomsu
-
-Tree "List", 'multi',
- as_lua: (nomsu)=>
- lua = Lua.Value nil, "{"
- line_length = 0
- for i, item in ipairs @
- item_lua = item\as_lua(nomsu)
- unless item_lua.is_value
- error "Cannot use #{colored.yellow repr(item)} as a list item, since it's not an expression.", 0
- lua\append item_lua
- item_string = tostring(item_lua)
- last_line = item_string\match("[^\n]*$")
- if item_string\match("\n")
- line_length = #last_line
- else
- line_length += #last_line
- if i < #@
- if line_length >= MAX_LINE
- lua\append ",\n "
- line_length = 0
- else
- lua\append ", "
- line_length += 2
- lua\append "}"
- return lua
-
- as_nomsu: (inline=false)=>
- if inline
- nomsu = Nomsu(nil, "[")
- for i, item in ipairs @
- item_nomsu = item\as_nomsu(true)
- return nil unless item_nomsu
- if i > 1
- nomsu\append ", "
- nomsu\append item_nomsu
- nomsu\append "]"
- return nomsu
- else
- inline_version = @as_nomsu(true)
- if inline_version and #inline_version <= MAX_LINE
- return inline_version
- nomsu = Nomsu(nil, "[..]")
- line = Nomsu(nil, "\n ")
- for item in *@
- item_nomsu = item\as_nomsu(true)
- if item_nomsu and #line + #", " + #item_nomsu <= MAX_LINE
- if #line.bits > 1
- line\append ", "
- line\append item_nomsu
- else
- unless item_nomsu
- item_nomsu = item\as_nomsu!
- return nil unless item_nomsu
- if #line.bits > 1
- nomsu\append line
- line = Nomsu(nil, "\n ")
- line\append item_nomsu
- if #line.bits > 1
- nomsu\append line
- return nomsu
-
-Tree "Dict", 'multi',
- as_lua: (nomsu)=>
- lua = Lua.Value nil, "{"
- line_length = 0
- for i, entry in ipairs @
- entry_lua = entry\as_lua(nomsu)
- 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]*)$")
- if last_line
- line_length = #last_line
- else
- line_length += #entry_lua_str
- if i < #@
- if line_length >= MAX_LINE
- lua\append ",\n "
- line_length = 0
- else
- lua\append ", "
- line_length += 2
- lua\append "}"
- return lua
-
- as_nomsu: (inline=false)=>
- if inline
- nomsu = Nomsu(nil, "{")
- for i, entry in ipairs @
- entry_nomsu = entry\as_nomsu(true)
- return nil unless entry_nomsu
- if i > 1
- nomsu\append ", "
- nomsu\append entry_nomsu
- nomsu\append "}"
- return nomsu
- else
- inline_version = @as_nomsu(true)
- if inline_version then return inline_version
- nomsu = Nomsu(nil, "{..}")
- line = Nomsu(nil, "\n ")
- for entry in *@
- entry_nomsu = entry\as_nomsu!
- return nil unless entry_nomsu
- if #line + #tostring(entry_nomsu) <= MAX_LINE
- if #line.bits > 1
- line\append ", "
- line\append entry_nomsu
- else
- if #line.bits > 1
- nomsu\append line
- line = Nomsu(nil, "\n ")
- line\append entry_nomsu
- if #line.bits > 1
- nomsu\append line
- return nomsu
-
-Tree "DictEntry", 'multi',
- as_lua: (nomsu)=>
- key, value = @[1], @[2]
- key_lua = key\as_lua(nomsu)
- unless key_lua.is_value
- error "Cannot use #{colored.yellow repr(key)} as a dict key, since it's not an expression.", 0
- value_lua = value and value\as_lua(nomsu) or Lua.Value(nil, "true")
- unless value_lua.is_value
- error "Cannot use #{colored.yellow repr(value)} as a dict value, since it's not an expression.", 0
- key_str = tostring(key_lua)\match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=])
- return if key_str
- Lua nil, key_str,"=",value_lua
- elseif tostring(key_lua)\sub(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"]
- Lua nil, "[ ",key_lua,"]=",value_lua
- else
- Lua nil, "[",key_lua,"]=",value_lua
-
- as_nomsu: (inline=true)=>
- key, value = @[1], @[2]
- key_nomsu = key\as_nomsu(true)
- return nil unless key_nomsu
- if key.type == "Action" or key.type == "Block"
- key_nomsu\parenthesize!
- value_nomsu = if value
- value\as_nomsu(true)
- else Nomsu(nil, "")
- if inline and not value_nomsu then return nil
- if not value_nomsu
- return nil if inline
- value_nomsu = value\as_nomsu!
- return nil unless value_nomsu
- return Nomsu nil, key_nomsu, ":", value_nomsu
-
-
-Tree "IndexChain", 'multi',
- as_lua: (nomsu)=>
- lua = @[1]\as_lua(nomsu)
- unless lua.is_value
- error "Cannot index #{colored.yellow repr(@[1])}, since it's not an expression.", 0
- first_char = tostring(lua)\sub(1,1)
- if first_char == "{" or first_char == '"' or first_char == "["
- lua\parenthesize!
-
- for i=2,#@
- key = @[i]
- key_lua = key\as_lua(nomsu)
- unless key_lua.is_value
- error "Cannot use #{colored.yellow repr(key)} as an index, since it's not an expression.", 0
- key_lua_str = tostring(key_lua)
- if lua_id = key_lua_str\match("^['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]$")
- lua\append ".#{lua_id}"
- elseif key_lua_str\sub(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"]
- lua\append "[ ",key_lua," ]"
- else
- lua\append "[",key_lua,"]"
- return lua
-
- as_nomsu: (inline=false)=>
- nomsu = Nomsu!
- for i, bit in ipairs @
- if i > 1
- nomsu\append "."
- bit_nomsu = bit\as_nomsu(true)
- return nil unless bit_nomsu
- if bit.type == "Action" or bit.type == "Block"
- bit_nomsu\parenthesize!
- nomsu\append bit_nomsu
- return nomsu
-
-Tree "Number", 'single',
- as_lua: (nomsu)=>
- Lua.Value(nil, tostring(@value))
-
- as_nomsu: (inline=false)=>
- return Nomsu(nil, tostring(@value))
-
-Tree "Var", 'single',
- as_lua_id: (v)->
- "_"..(v\gsub("%W", (c)-> if c == "_" then "__" else ("_%x")\format(c\byte!)))
-
- as_lua: (nomsu)=>
- Lua.Value(nil, self.as_lua_id(@value))
-
- as_nomsu: (inline=false)=>
- return Nomsu(nil, "%", @value)
-
-Tree "Word", 'single',
- as_lua: (nomsu)=>
- error("Attempt to convert Word to lua")
-
- as_nomsu: (inline=false)=>
- return Nomsu(nil, @value)
-
-Tree "Comment", 'single',
- as_lua: (nomsu)=>
- Lua(nil, "--"..@value\gsub("\n","\n--").."\n")
-
- as_nomsu: (inline=false)=>
- return nil if inline
- if @value\match("\n")
- return Nomsu(nil, "#..", @value\gsub("\n", "\n "))
- else
- return Nomsu(nil, "#", @value)
-
return Types