aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/control_flow.nom2
-rw-r--r--core/operators.nom2
-rw-r--r--nomsu.lua298
-rwxr-xr-xnomsu.moon97
-rw-r--r--nomsu.peg32
5 files changed, 239 insertions, 192 deletions
diff --git a/core/control_flow.nom b/core/control_flow.nom
index 4066a97..d40c349 100644
--- a/core/control_flow.nom
+++ b/core/control_flow.nom
@@ -88,7 +88,7 @@ immediately:
locals: %body_lua's "locals"
statements:".."
for \(%subtree as lua expr) in coroutine.wrap(function() nomsu:walk_tree(\(%tree as lua expr)) end) do
- if type(\(%subtree as lua expr)) == 'table' and \(%subtree as lua expr).type then
+ if Types.is_node(\(%subtree as lua expr)) then
if \(%condition as lua expr) then
\%body_statements
break;
diff --git a/core/operators.nom b/core/operators.nom
index d2929bf..7a6f535 100644
--- a/core/operators.nom
+++ b/core/operators.nom
@@ -79,7 +79,7 @@ immediately:
Expected a Dict for the assignments part of '<- %' statement, not \(%assignments' source code)
lua> ".."
for i, item in ipairs(\%assignments.value) do
- local target, value = item.dict_key, item.dict_value;
+ local target, value = item.key, item.value;
local target_lua = nomsu:tree_to_lua(target);
if not target_lua.expr then error("Invalid target for assignment: "..target:get_src()); end
local value_lua = nomsu:tree_to_lua(value);
diff --git a/nomsu.lua b/nomsu.lua
index 90188ad..0ea6078 100644
--- a/nomsu.lua
+++ b/nomsu.lua
@@ -3,6 +3,7 @@ local re = require('re')
local lpeg = require('lpeg')
local utils = require('utils')
local new_uuid = require('uuid')
+local immutable = require('immutable')
local repr, stringify, min, max, equivalent, set, is_list, sum
repr, stringify, min, max, equivalent, set, is_list, sum = utils.repr, utils.stringify, utils.min, utils.max, utils.equivalent, utils.set, utils.is_list, utils.sum
local colors = setmetatable({ }, {
@@ -22,6 +23,14 @@ do
local _obj_0 = table
insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat
end
+local _tuples = { }
+local Tuple
+Tuple = function(t)
+ if not _tuples[#t] then
+ _tuples[#t] = immutable(#t)
+ end
+ return _tuples[#t]:from_table(t)
+end
local cached
cached = function(fn)
local cache = setmetatable({ }, {
@@ -52,9 +61,46 @@ end
lpeg.setmaxstack(10000)
local P, R, V, S, Cg, C, Cp, B, Cmt
P, R, V, S, Cg, C, Cp, B, Cmt = lpeg.P, lpeg.R, lpeg.V, lpeg.S, lpeg.Cg, lpeg.C, lpeg.Cp, lpeg.B, lpeg.Cmt
+local Types = { }
+local _list_0 = {
+ "File",
+ "Nomsu",
+ "Block",
+ "List",
+ "FunctionCall",
+ "Text",
+ "Dict",
+ "Number",
+ "Word",
+ "Var",
+ "Comment"
+}
+for _index_0 = 1, #_list_0 do
+ local t = _list_0[_index_0]
+ Types[t] = immutable({
+ "id",
+ "value"
+ }, {
+ type = t,
+ name = t
+ })
+end
+Types.DictEntry = immutable({
+ "key",
+ "value"
+}, {
+ name = "DictEntry"
+})
+Types.is_node = function(n)
+ return type(n) == 'userdata' and n.type
+end
local NOMSU_DEFS
do
local _with_0 = { }
+ _with_0.Tuple = Tuple
+ _with_0.DictEntry = function(k, v)
+ return Types.DictEntry(k, v)
+ end
_with_0.nl = P("\r") ^ -1 * P("\n")
_with_0.ws = S(" \t")
_with_0.tonumber = tonumber
@@ -133,14 +179,16 @@ do
end
NOMSU_DEFS = _with_0
end
+local node_id = 0
setmetatable(NOMSU_DEFS, {
__index = function(self, key)
local make_node
make_node = function(start, value, stop)
- local node = {
- type = key,
- value = value
- }
+ node_id = node_id + 1
+ if type(value) == 'table' then
+ error(value)
+ end
+ local node = Types[key](node_id, value)
lpeg.userdata.tree_metadata[node] = {
start = start,
stop = stop,
@@ -490,7 +538,7 @@ do
expr_type = nil
end
assert(tree, "No tree provided to tree_to_nomsu.")
- assert(tree.type, "Invalid tree: " .. tostring(repr(tree)))
+ assert(Types.is_node(tree), "Invalid tree: " .. tostring(repr(tree)))
local join_lines
join_lines = function(lines)
for _index_0 = 1, #lines do
@@ -543,9 +591,9 @@ do
return buff
elseif "List" == _exp_0 then
local bits = { }
- local _list_0 = tok.value
- for _index_0 = 1, #_list_0 do
- local bit = _list_0[_index_0]
+ local _list_1 = tok.value
+ for _index_0 = 1, #_list_1 do
+ local bit = _list_1[_index_0]
local nomsu = inline_expression(bit)
if not (nomsu) then
return nil
@@ -555,22 +603,22 @@ do
return "[" .. concat(bits, ", ") .. "]"
elseif "Dict" == _exp_0 then
local bits = { }
- local _list_0 = tok.value
- for _index_0 = 1, #_list_0 do
- local bit = _list_0[_index_0]
+ local _list_1 = tok.value
+ for _index_0 = 1, #_list_1 do
+ local bit = _list_1[_index_0]
local key_nomsu
- if bit.dict_key.type == "Word" then
- key_nomsu = bit.dict_key.value
+ if bit.key.type == "Word" then
+ key_nomsu = bit.key.value
else
- key_nomsu = inline_expression(bit.dict_key)
+ key_nomsu = inline_expression(bit.key)
end
if not (key_nomsu) then
return nil
end
- if bit.dict_key.type == "FunctionCall" then
+ if bit.key.type == "FunctionCall" then
key_nomsu = "(" .. key_nomsu .. ")"
end
- local value_nomsu = inline_expression(bit.dict_value)
+ local value_nomsu = inline_expression(bit.value)
if not (value_nomsu) then
return nil
end
@@ -579,9 +627,9 @@ do
return "{" .. concat(bits, ", ") .. "}"
elseif "Text" == _exp_0 then
local buff = '"'
- local _list_0 = tok.value
- for _index_0 = 1, #_list_0 do
- local bit = _list_0[_index_0]
+ local _list_1 = tok.value
+ for _index_0 = 1, #_list_1 do
+ local bit = _list_1[_index_0]
if type(bit) == 'string' then
if bit:find("\n") then
return nil
@@ -627,9 +675,9 @@ do
local _exp_0 = tok.type
if "Block" == _exp_0 then
local buff = ":"
- local _list_0 = tok.value
- for _index_0 = 1, #_list_0 do
- local line = _list_0[_index_0]
+ local _list_1 = tok.value
+ for _index_0 = 1, #_list_1 do
+ local line = _list_1[_index_0]
nomsu = expression(line)
if not (nomsu) then
return nil
@@ -646,9 +694,9 @@ do
elseif "List" == _exp_0 then
local buff = "[..]"
local line = "\n "
- local _list_0 = tok.value
- for _index_0 = 1, #_list_0 do
- local bit = _list_0[_index_0]
+ local _list_1 = tok.value
+ for _index_0 = 1, #_list_1 do
+ local bit = _list_1[_index_0]
nomsu = inline_expression(bit)
if line ~= "\n " and #line + #", " + #nomsu > max_line then
buff = buff .. line
@@ -674,17 +722,17 @@ do
elseif "Dict" == _exp_0 then
local buff = "{..}"
local line = "\n "
- local _list_0 = tok.value
- for _index_0 = 1, #_list_0 do
- local bit = _list_0[_index_0]
- local key_nomsu = inline_expression(bit.dict_key)
+ local _list_1 = tok.value
+ for _index_0 = 1, #_list_1 do
+ local bit = _list_1[_index_0]
+ local key_nomsu = inline_expression(bit.key)
if not (key_nomsu) then
return nil
end
- if bit.dict_key.type == "FunctionCall" then
+ if bit.key.type == "FunctionCall" then
key_nomsu = "(" .. key_nomsu .. ")"
end
- local value_nomsu = inline_expression(bit.dict_value)
+ local value_nomsu = inline_expression(bit.value)
if value_nomsu and #key_nomsu + #value_nomsu < max_line then
line = line .. (key_nomsu .. "=" .. value_nomsu .. ",")
if #line >= max_line then
@@ -692,7 +740,7 @@ do
line = "\n "
end
else
- line = line .. (key_nomsu .. "=" .. expression(bit.dict_value))
+ line = line .. (key_nomsu .. "=" .. expression(bit.value))
buff = buff .. line
line = "\n "
end
@@ -703,9 +751,9 @@ do
return buff
elseif "Text" == _exp_0 then
local buff = '".."\n '
- local _list_0 = tok.value
- for _index_0 = 1, #_list_0 do
- local bit = _list_0[_index_0]
+ local _list_1 = tok.value
+ for _index_0 = 1, #_list_1 do
+ local bit = _list_1[_index_0]
if type(bit) == 'string' then
buff = buff .. bit:gsub("\\", "\\\\"):gsub("\n", "\n ")
else
@@ -793,9 +841,9 @@ do
return buff
elseif "File" == _exp_0 then
local lines = { }
- local _list_0 = tree.value
- for _index_0 = 1, #_list_0 do
- local line = _list_0[_index_0]
+ local _list_1 = tree.value
+ for _index_0 = 1, #_list_1 do
+ local line = _list_1[_index_0]
nomsu = expression(line)
if not (nomsu) then
local src = self:get_source_code(line)
@@ -861,7 +909,7 @@ do
end,
tree_to_lua = function(self, tree)
assert(tree, "No tree provided.")
- if not tree.type then
+ if not Types.is_node(tree) then
error("Invalid tree: " .. tostring(repr(tree)), 0)
end
local _exp_0 = tree.type
@@ -872,9 +920,9 @@ do
local declared_locals = { }
local lua_bits = { }
local line_no = 1
- local _list_0 = tree.value
- for _index_0 = 1, #_list_0 do
- local line = _list_0[_index_0]
+ local _list_1 = tree.value
+ for _index_0 = 1, #_list_1 do
+ local line = _list_1[_index_0]
local lua = self:tree_to_lua(line)
if not lua then
error("No lua produced by " .. tostring(repr(line)), 0)
@@ -884,9 +932,9 @@ do
do
local _accum_0 = { }
local _len_0 = 1
- local _list_1 = lua.locals
- for _index_1 = 1, #_list_1 do
- local l = _list_1[_index_1]
+ local _list_2 = lua.locals
+ for _index_1 = 1, #_list_2 do
+ local l = _list_2[_index_1]
if not declared_locals[l] then
_accum_0[_len_0] = l
_len_0 = _len_0 + 1
@@ -922,9 +970,9 @@ do
elseif "Block" == _exp_0 then
local lua_bits = { }
local locals = { }
- local _list_0 = tree.value
- for _index_0 = 1, #_list_0 do
- local arg = _list_0[_index_0]
+ local _list_1 = tree.value
+ for _index_0 = 1, #_list_1 do
+ local arg = _list_1[_index_0]
local lua = self:tree_to_lua(arg)
if #tree.value == 1 and lua.expr and not lua.statements then
return {
@@ -933,9 +981,9 @@ do
}
end
if lua.locals then
- local _list_1 = lua.locals
- for _index_1 = 1, #_list_1 do
- local l = _list_1[_index_1]
+ local _list_2 = lua.locals
+ for _index_1 = 1, #_list_2 do
+ local l = _list_2[_index_1]
table.insert(locals, l)
end
end
@@ -965,9 +1013,9 @@ do
do
local _accum_0 = { }
local _len_0 = 1
- local _list_0 = tree.value
- for _index_0 = 1, #_list_0 do
- local arg = _list_0[_index_0]
+ local _list_1 = tree.value
+ for _index_0 = 1, #_list_1 do
+ local arg = _list_1[_index_0]
if arg.type ~= "Word" then
_accum_0[_len_0] = arg
_len_0 = _len_0 + 1
@@ -980,9 +1028,9 @@ do
do
local _accum_0 = { }
local _len_0 = 1
- local _list_0 = metadata.arg_orders[stub]
- for _index_0 = 1, #_list_0 do
- local p = _list_0[_index_0]
+ local _list_1 = metadata.arg_orders[stub]
+ for _index_0 = 1, #_list_1 do
+ local p = _list_1[_index_0]
_accum_0[_len_0] = args[p]
_len_0 = _len_0 + 1
end
@@ -1008,9 +1056,9 @@ do
return lua
elseif not metadata and self.__class.math_patt:match(stub) then
local bits = { }
- local _list_0 = tree.value
- for _index_0 = 1, #_list_0 do
- local tok = _list_0[_index_0]
+ local _list_1 = tree.value
+ for _index_0 = 1, #_list_1 do
+ local tok = _list_1[_index_0]
if tok.type == "Word" then
insert(bits, tok.value)
else
@@ -1028,11 +1076,11 @@ do
}
end
local args = { }
- local _list_0 = tree.value
- for _index_0 = 1, #_list_0 do
+ local _list_1 = tree.value
+ for _index_0 = 1, #_list_1 do
local _continue_0 = false
repeat
- local tok = _list_0[_index_0]
+ local tok = _list_1[_index_0]
if tok.type == "Word" then
_continue_0 = true
break
@@ -1055,9 +1103,9 @@ do
do
local _accum_0 = { }
local _len_0 = 1
- local _list_1 = metadata.arg_orders[stub]
- for _index_0 = 1, #_list_1 do
- local p = _list_1[_index_0]
+ local _list_2 = metadata.arg_orders[stub]
+ for _index_0 = 1, #_list_2 do
+ local p = _list_2[_index_0]
_accum_0[_len_0] = args[p]
_len_0 = _len_0 + 1
end
@@ -1072,11 +1120,11 @@ do
elseif "Text" == _exp_0 then
local concat_parts = { }
local string_buffer = ""
- local _list_0 = tree.value
- for _index_0 = 1, #_list_0 do
+ local _list_1 = tree.value
+ for _index_0 = 1, #_list_1 do
local _continue_0 = false
repeat
- local bit = _list_0[_index_0]
+ local bit = _list_1[_index_0]
if type(bit) == "string" then
string_buffer = string_buffer .. bit
_continue_0 = true
@@ -1122,9 +1170,9 @@ do
end
elseif "List" == _exp_0 then
local items = { }
- local _list_0 = tree.value
- for _index_0 = 1, #_list_0 do
- local item = _list_0[_index_0]
+ local _list_1 = tree.value
+ for _index_0 = 1, #_list_1 do
+ local item = _list_1[_index_0]
local lua = self:tree_to_lua(item)
if not (lua.expr) then
local line = self:get_line_number(item)
@@ -1138,26 +1186,26 @@ do
}
elseif "Dict" == _exp_0 then
local items = { }
- local _list_0 = tree.value
- for _index_0 = 1, #_list_0 do
- local entry = _list_0[_index_0]
+ local _list_1 = tree.value
+ for _index_0 = 1, #_list_1 do
+ local entry = _list_1[_index_0]
local key_lua
- if entry.dict_key.type == "Word" then
+ if entry.key.type == "Word" then
key_lua = {
- expr = repr(entry.dict_key.value)
+ expr = repr(entry.key.value)
}
else
- key_lua = self:tree_to_lua(entry.dict_key)
+ key_lua = self:tree_to_lua(entry.key)
end
if not (key_lua.expr) then
- local line = self:get_line_number(entry.dict_key)
- local src = self:get_source_code(entry.dict_key)
+ local line = self:get_line_number(entry.key)
+ local src = self:get_source_code(entry.key)
error(tostring(line) .. ": Cannot use " .. tostring(colored.yellow(src)) .. " as a dict key, since it's not an expression.", 0)
end
- local value_lua = self:tree_to_lua(entry.dict_value)
+ local value_lua = self:tree_to_lua(entry.value)
if not (value_lua.expr) then
- local line = self:get_line_number(entry.dict_value)
- local src = self:get_source_code(entry.dict_value)
+ local line = self:get_line_number(entry.value)
+ local src = self:get_source_code(entry.value)
error(tostring(line) .. ": Cannot use " .. tostring(colored.yellow(src)) .. " as a dict value, since it's not an expression.", 0)
end
local key_str = key_lua.expr:match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=])
@@ -1189,22 +1237,22 @@ do
depth = 0
end
coroutine.yield(tree, depth)
- if type(tree) ~= 'table' or not tree.type then
+ if not (Types.is_node(tree)) then
return
end
local _exp_0 = tree.type
if "List" == _exp_0 or "File" == _exp_0 or "Block" == _exp_0 or "FunctionCall" == _exp_0 or "Text" == _exp_0 then
- local _list_0 = tree.value
- for _index_0 = 1, #_list_0 do
- local v = _list_0[_index_0]
+ local _list_1 = tree.value
+ for _index_0 = 1, #_list_1 do
+ local v = _list_1[_index_0]
self:walk_tree(v, depth + 1)
end
elseif "Dict" == _exp_0 then
- local _list_0 = tree.value
- for _index_0 = 1, #_list_0 do
- local e = _list_0[_index_0]
- self:walk_tree(e.dict_key, depth + 1)
- self:walk_tree(e.dict_value, depth + 1)
+ local _list_1 = tree.value
+ for _index_0 = 1, #_list_1 do
+ local e = _list_1[_index_0]
+ self:walk_tree(e.key, depth + 1)
+ self:walk_tree(e.value, depth + 1)
end
else
self:walk_tree(tree.value, depth + 1)
@@ -1216,10 +1264,10 @@ do
for node, depth in coroutine.wrap(function()
return self:walk_tree(tree)
end) do
- if type(node) ~= 'table' or not node.type then
- print((" "):rep(depth) .. repr(node))
- else
+ if Types.is_node(node) then
print(tostring((" "):rep(depth)) .. tostring(node.type) .. ":")
+ else
+ print((" "):rep(depth) .. repr(node))
end
end
return io.write(colors.reset)
@@ -1229,16 +1277,16 @@ do
for node, depth in coroutine.wrap(function()
return self:walk_tree(tree)
end) do
- if type(node) ~= 'table' or not node.type then
- insert(bits, ((" "):rep(depth) .. repr(node)))
- else
+ if Types.is_node(node) then
insert(bits, (tostring((" "):rep(depth)) .. tostring(node.type) .. ":"))
+ else
+ insert(bits, ((" "):rep(depth) .. repr(node)))
end
end
return concat(bits, "\n")
end,
tree_map = function(self, tree, fn)
- if type(tree) ~= 'table' then
+ if not (Types.is_node(tree)) then
return tree
end
local replacement = fn(tree)
@@ -1258,44 +1306,25 @@ do
end
end
if is_changed then
- local new_tree
- do
- local _tbl_0 = { }
- for k, v in pairs(tree) do
- _tbl_0[k] = v
- end
- new_tree = _tbl_0
- end
+ local new_tree = getmetatable(tree)(tree.id, Tuple(new_values))
self.tree_metadata[new_tree] = self.tree_metadata[tree]
- new_tree.value = new_values
return new_tree
end
elseif "Dict" == _exp_0 then
local new_values, is_changed = { }, false
for i, e in ipairs(tree.value) do
- local new_key = self:tree_map(e.dict_key, fn)
- local new_value = self:tree_map(e.dict_value, fn)
- if (new_key ~= nil and new_key ~= e.dict_key) or (new_value ~= nil and new_value ~= e.dict_value) then
+ local new_key = self:tree_map(e.key, fn)
+ local new_value = self:tree_map(e.value, fn)
+ if (new_key ~= nil and new_key ~= e.key) or (new_value ~= nil and new_value ~= e.value) then
is_changed = true
- new_values[i] = {
- dict_key = new_key,
- dict_value = new_value
- }
+ new_values[i] = DictEntry(new_key, new_value)
else
new_values[i] = e
end
end
if is_changed then
- local new_tree
- do
- local _tbl_0 = { }
- for k, v in pairs(tree) do
- _tbl_0[k] = v
- end
- new_tree = _tbl_0
- end
+ local new_tree = getmetatable(tree)(tree.id, Tuple(new_values))
self.tree_metadata[new_tree] = self.tree_metadata[tree]
- new_tree.value = new_values
return new_tree
end
elseif nil == _exp_0 then
@@ -1320,9 +1349,9 @@ do
return concat((function()
local _accum_0 = { }
local _len_0 = 1
- local _list_0 = tree.value
- for _index_0 = 1, #_list_0 do
- local t = _list_0[_index_0]
+ local _list_1 = tree.value
+ for _index_0 = 1, #_list_1 do
+ local t = _list_1[_index_0]
_accum_0[_len_0] = (t.type == "Word" and t.value or "%")
_len_0 = _len_0 + 1
end
@@ -1336,9 +1365,9 @@ do
return concat((function()
local _accum_0 = { }
local _len_0 = 1
- local _list_0 = tree.value
- for _index_0 = 1, #_list_0 do
- local t = _list_0[_index_0]
+ local _list_1 = tree.value
+ for _index_0 = 1, #_list_1 do
+ local t = _list_1[_index_0]
_accum_0[_len_0] = (t.type == "Word" and t.value or "%" .. tostring(t.value))
_len_0 = _len_0 + 1
end
@@ -1382,7 +1411,7 @@ do
return stub_args
end,
var_to_lua_identifier = function(self, var)
- if type(var) == 'table' and var.type == "Var" then
+ if Types.Var:is_instance(var) then
var = var.value
end
return "_" .. (var:gsub("%W", function(verboten)
@@ -1402,9 +1431,9 @@ do
local nomsu_string_as_lua
nomsu_string_as_lua = function(code)
local concat_parts = { }
- local _list_0 = code.value
- for _index_0 = 1, #_list_0 do
- local bit = _list_0[_index_0]
+ local _list_1 = code.value
+ for _index_0 = 1, #_list_1 do
+ local bit = _list_1[_index_0]
if type(bit) == "string" then
insert(concat_parts, bit)
else
@@ -1548,6 +1577,7 @@ do
end
})
self.environment.LOADED = { }
+ self.environment.Types = Types
return self:initialize_core()
end,
__base = _base_0,
diff --git a/nomsu.moon b/nomsu.moon
index df6d296..037368b 100755
--- a/nomsu.moon
+++ b/nomsu.moon
@@ -15,11 +15,18 @@ re = require 're'
lpeg = require 'lpeg'
utils = require 'utils'
new_uuid = require 'uuid'
+immutable = require 'immutable'
{:repr, :stringify, :min, :max, :equivalent, :set, :is_list, :sum} = utils
colors = setmetatable({}, {__index:->""})
colored = setmetatable({}, {__index:(_,color)-> ((msg)-> colors[color]..(msg or '')..colors.reset)})
{:insert, :remove, :concat} = table
+_tuples = {}
+Tuple = (t)->
+ if not _tuples[#t]
+ _tuples[#t] = immutable(#t)
+ return _tuples[#t]\from_table(t)
+
cached = (fn)->
cache = setmetatable({}, {__mode:"k"})
return (self, arg)->
@@ -53,8 +60,17 @@ do
lpeg.setmaxstack 10000 -- whoa
{:P,:R,:V,:S,:Cg,:C,:Cp,:B,:Cmt} = lpeg
+Types = {}
+for t in *{"File", "Nomsu", "Block", "List", "FunctionCall", "Text", "Dict", "Number", "Word", "Var", "Comment"}
+ Types[t] = immutable({"id","value"}, {type:t, name:t})
+Types.DictEntry = immutable({"key","value"}, {name:"DictEntry"})
+Types.is_node = (n)->
+ type(n) == 'userdata' and n.type
+
NOMSU_DEFS = with {}
-- Newline supports either windows-style CR+LF or unix-style LF
+ .Tuple = Tuple
+ .DictEntry = (k,v) -> Types.DictEntry(k,v)
.nl = P("\r")^-1 * P("\n")
.ws = S(" \t")
.tonumber = tonumber
@@ -114,9 +130,12 @@ NOMSU_DEFS = with {}
err_msg ..="\n#{prev_line}\n#{err_line}\n#{pointer}\n#{next_line}\n"
error(err_msg)
+node_id = 0
setmetatable(NOMSU_DEFS, {__index:(key)=>
make_node = (start, value, stop)->
- node = {type: key, :value}
+ node_id = node_id + 1
+ if type(value) == 'table' then error(value)-- = Tuple(value)
+ node = Types[key](node_id, value)
lpeg.userdata.tree_metadata[node] = {
:start,:stop,filename:lpeg.userdata.filename,source_code:lpeg.userdata.source_code
}
@@ -176,6 +195,7 @@ class NomsuCompiler
error("Attempt to run undefined action: #{key}", 0)
})
@environment.LOADED = {}
+ @environment.Types = Types
@initialize_core!
define_action: (signature, source, fn)=>
@@ -394,7 +414,7 @@ class NomsuCompiler
-- "inline" for code that cannot contain indented code or an end-of-line component
-- e.g. code that is meant to go inside parentheses
assert tree, "No tree provided to tree_to_nomsu."
- assert tree.type, "Invalid tree: #{repr(tree)}"
+ assert Types.is_node(tree), "Invalid tree: #{repr(tree)}"
join_lines = (lines)->
for line in *lines
if #indentation + #line > max_line
@@ -436,13 +456,13 @@ class NomsuCompiler
when "Dict"
bits = {}
for bit in *tok.value
- key_nomsu = if bit.dict_key.type == "Word"
- bit.dict_key.value
- else inline_expression bit.dict_key
+ key_nomsu = if bit.key.type == "Word"
+ bit.key.value
+ else inline_expression bit.key
return nil unless key_nomsu
- if bit.dict_key.type == "FunctionCall"
+ if bit.key.type == "FunctionCall"
key_nomsu = "("..key_nomsu..")"
- value_nomsu = inline_expression bit.dict_value
+ value_nomsu = inline_expression bit.value
return nil unless value_nomsu
insert bits, key_nomsu.."="..value_nomsu
return "{"..concat(bits, ", ").."}"
@@ -510,18 +530,18 @@ class NomsuCompiler
buff = "{..}"
line = "\n "
for bit in *tok.value
- key_nomsu = inline_expression bit.dict_key
+ key_nomsu = inline_expression bit.key
return nil unless key_nomsu
- if bit.dict_key.type == "FunctionCall"
+ if bit.key.type == "FunctionCall"
key_nomsu = "("..key_nomsu..")"
- value_nomsu = inline_expression bit.dict_value
+ value_nomsu = inline_expression bit.value
if value_nomsu and #key_nomsu + #value_nomsu < max_line
line ..= key_nomsu.."="..value_nomsu..","
if #line >= max_line
buff ..= line
line = "\n "
else
- line ..= key_nomsu.."="..expression(bit.dict_value)
+ line ..= key_nomsu.."="..expression(bit.value)
buff ..= line
line = "\n "
if line ~= "\n "
@@ -638,7 +658,7 @@ class NomsuCompiler
tree_to_lua: (tree)=>
-- Return <lua code for value>, <additional lua code>
assert tree, "No tree provided."
- if not tree.type
+ if not Types.is_node(tree)
error("Invalid tree: #{repr(tree)}", 0)
switch tree.type
when "File"
@@ -777,18 +797,18 @@ class NomsuCompiler
when "Dict"
items = {}
for entry in *tree.value
- key_lua = if entry.dict_key.type == "Word"
- {expr:repr(entry.dict_key.value)}
+ key_lua = if entry.key.type == "Word"
+ {expr:repr(entry.key.value)}
else
- @tree_to_lua entry.dict_key
+ @tree_to_lua entry.key
unless key_lua.expr
- line = @get_line_number(entry.dict_key)
- src = @get_source_code(entry.dict_key)
+ line = @get_line_number(entry.key)
+ src = @get_source_code(entry.key)
error "#{line}: Cannot use #{colored.yellow src} as a dict key, since it's not an expression.", 0
- value_lua = @tree_to_lua entry.dict_value
+ value_lua = @tree_to_lua entry.value
unless value_lua.expr
- line = @get_line_number(entry.dict_value)
- src = @get_source_code(entry.dict_value)
+ line = @get_line_number(entry.value)
+ src = @get_source_code(entry.value)
error "#{line}: Cannot use #{colored.yellow src} as a dict value, since it's not an expression.", 0
key_str = key_lua.expr\match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=])
if key_str
@@ -810,35 +830,34 @@ class NomsuCompiler
walk_tree: (tree, depth=0)=>
coroutine.yield(tree, depth)
- if type(tree) != 'table' or not tree.type
- return
+ return unless Types.is_node(tree)
switch tree.type
when "List", "File", "Block", "FunctionCall", "Text"
for v in *tree.value
@walk_tree(v, depth+1)
when "Dict"
for e in *tree.value
- @walk_tree(e.dict_key, depth+1)
- @walk_tree(e.dict_value, depth+1)
+ @walk_tree(e.key, depth+1)
+ @walk_tree(e.value, depth+1)
else @walk_tree(tree.value, depth+1)
return nil
print_tree: (tree)=>
io.write(colors.bright..colors.green)
for node,depth in coroutine.wrap(-> @walk_tree tree)
- if type(node) != 'table' or not node.type
- print((" ")\rep(depth)..repr(node))
- else
+ if Types.is_node(node)
print("#{(" ")\rep(depth)}#{node.type}:")
+ else
+ print((" ")\rep(depth)..repr(node))
io.write(colors.reset)
tree_to_str: (tree)=>
bits = {}
for node,depth in coroutine.wrap(-> @walk_tree tree)
- if type(node) != 'table' or not node.type
- insert bits, ((" ")\rep(depth)..repr(node))
- else
+ if Types.is_node(node)
insert bits, ("#{(" ")\rep(depth)}#{node.type}:")
+ else
+ insert bits, ((" ")\rep(depth)..repr(node))
return concat(bits, "\n")
@unescape_string: (str)=>
@@ -860,7 +879,7 @@ class NomsuCompiler
tree_map: (tree, fn)=>
-- Return a new tree with fn mapped to each node. If fn provides a replacement,
-- use that and stop recursing, otherwise recurse.
- if type(tree) != 'table' then return tree
+ unless Types.is_node(tree) then return tree
replacement = fn(tree)
if replacement != nil
return replacement
@@ -875,27 +894,25 @@ class NomsuCompiler
else
new_values[i] = old_value
if is_changed
- new_tree = {k,v for k,v in pairs(tree)}
+ new_tree = getmetatable(tree)(tree.id, Tuple(new_values))
-- TODO: Maybe generate new metadata?
@tree_metadata[new_tree] = @tree_metadata[tree]
- new_tree.value = new_values
return new_tree
when "Dict"
new_values, is_changed = {}, false
for i,e in ipairs tree.value
- new_key = @tree_map(e.dict_key, fn)
- new_value = @tree_map(e.dict_value, fn)
- if (new_key != nil and new_key != e.dict_key) or (new_value != nil and new_value != e.dict_value)
+ new_key = @tree_map(e.key, fn)
+ new_value = @tree_map(e.value, fn)
+ if (new_key != nil and new_key != e.key) or (new_value != nil and new_value != e.value)
is_changed = true
- new_values[i] = {dict_key:new_key, dict_value:new_value}
+ new_values[i] = DictEntry(new_key, new_value)
else
new_values[i] = e
if is_changed
- new_tree = {k,v for k,v in pairs(tree)}
+ new_tree = getmetatable(tree)(tree.id, Tuple(new_values))
-- TODO: Maybe generate new metadata?
@tree_metadata[new_tree] = @tree_metadata[tree]
- new_tree.value = new_values
return new_tree
when nil -- Raw table, probably from one of the .value of a multi-value tree (e.g. List)
error("Invalid tree: #{repr tree}")
@@ -953,7 +970,7 @@ class NomsuCompiler
var_to_lua_identifier: (var)=>
-- Converts arbitrary nomsu vars to valid lua identifiers by replacing illegal
-- characters with escape sequences
- if type(var) == 'table' and var.type == "Var"
+ if Types.Var\is_instance(var)
var = var.value
"_"..(var\gsub "%W", (verboten)->
if verboten == "_" then "__" else ("_%x")\format(verboten\byte!))
diff --git a/nomsu.peg b/nomsu.peg
index 6c12cc4..bfa9f0c 100644
--- a/nomsu.peg
+++ b/nomsu.peg
@@ -3,7 +3,7 @@ file (File):
(ignored_line %nl)*
statement (nodent statement)*
(%nl ignored_line)*
- (!. / (("" -> "Parse error") => error)?) |}
+ (!. / (("" -> "Parse error") => error)?) |} -> Tuple
shebang: "#!" [^%nl]* %nl
@@ -13,7 +13,7 @@ indented_block (Block):
{| (":" / "(..)")? indent
statement (nodent statement)*
(dedent / (("" -> "Error while parsing block") => error))
- |}
+ |} -> Tuple
inline_nomsu (Nomsu): "\" inline_expression
indented_nomsu (Nomsu): "\" expression
@@ -29,27 +29,27 @@ expression:
-- Function calls need at least one word in them
inline_functioncall (FunctionCall):
{| (inline_expression %ws*)* word (%ws* (inline_expression / word))*
- (%ws* ":" %ws* (inline_functioncall / inline_expression))?|}
+ (%ws* ":" %ws* (inline_functioncall / inline_expression))?|} -> Tuple
functioncall (FunctionCall):
- {| (expression (dotdot / %ws*))* word ((dotdot / %ws*) (expression / word))* |}
+ {| (expression (dotdot / %ws*))* word ((dotdot / %ws*) (expression / word))* |} -> Tuple
word (Word): { %operator / (!number plain_word) }
inline_text (Text):
!('".."' eol)
- '"' {|
+ '"' ({|
({~ (('\"' -> '"') / ('\\' -> '\') / %escaped_char / [^%nl\"])+ ~}
/ inline_text_interpolation)*
- |} '"'
+ |} -> Tuple) '"'
-- Have to use "%indent" instead of "indent" etc. to avoid messing up text lines that start with "#"
indented_text (Text):
- '".."' eol %nl {|
+ '".."' eol %nl ({|
{~ (%nl*) (%indent -> "") ~}
({~
(("\\" -> "\") / (("\" eol %nl+ %nodent "..") -> "")
/ (%nl+ {~ %nodent -> "" ~}) / [^%nl\])+
~} / text_interpolation)*
- |} (((!.) &%dedent) / (&(%nl %dedent)) / (("" -> "Error while parsing Text") => error))
+ |} -> Tuple) (((!.) &%dedent) / (&(%nl %dedent)) / (("" -> "Error while parsing Text") => error))
inline_text_interpolation:
"\" (
variable / inline_list / inline_dict / inline_text
@@ -73,11 +73,11 @@ variable (Var): "%" { plain_word? }
inline_list (List):
!('[..]')
- "[" %ws* {| (inline_list_item (comma inline_list_item)* comma?)? |} %ws* "]"
+ "[" %ws* ({| (inline_list_item (comma inline_list_item)* comma?)? |} -> Tuple) %ws* "]"
indented_list (List):
- "[..]" indent {|
+ "[..]" indent ({|
list_line (nodent list_line)*
- |}
+ |} -> Tuple)
(dedent / (("" -> "Error while parsing list") => error))
list_line:
((functioncall / expression) !comma)
@@ -86,17 +86,17 @@ inline_list_item: inline_functioncall / inline_expression
inline_dict (Dict):
!('{..}')
- "{" %ws* {| (inline_dict_item (comma inline_dict_item)* comma?)? |} %ws* "}"
+ "{" %ws* ({| (inline_dict_item (comma inline_dict_item)* comma?)? |} -> Tuple) %ws* "}"
indented_dict (Dict):
- "{..}" indent {|
+ "{..}" indent ({|
dict_line (nodent dict_line)*
- |}
+ |} -> Tuple)
(dedent / (("" -> "Error while parsing dict") => error))
dict_line:
- ({| {:dict_key: inline_expression / word :} %ws* ":" %ws* {:dict_value: functioncall / expression :} |} !comma)
+ (((inline_expression / word) %ws* ":" %ws* (functioncall / expression)) -> DictEntry !comma)
/ (inline_dict_item (comma dict_line?)?)
inline_dict_item:
- {| {:dict_key: inline_expression / word :} %ws* ":" %ws* {:dict_value: inline_functioncall / inline_expression :} |}
+ ((inline_expression / word) %ws* ":" %ws* (inline_functioncall / inline_expression)) -> DictEntry
block_comment(Comment): "#.." { [^%nl]* (%nl+ %indent [^%nl]* (%nl+ %nodent [^%nl]*)* %dedent)? }
line_comment(Comment): "#" { [^%nl]* }