From 0c9973ff0363e400d3d284339b77197c40c3f60c Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 12 Jun 2018 13:56:15 -0700 Subject: [PATCH] Tidying up exceptions and error reporting. Also simplified the grammar a tiny bit. --- core/control_flow.nom | 42 +++++++++++++------ core/metaprogramming.nom | 18 +++++++-- core/scopes.nom | 4 +- nomsu.lua | 87 +++++++++++++++------------------------- nomsu.moon | 81 +++++++++++++++++++------------------ nomsu.peg | 28 +++++++------ nomsu_tree.lua | 27 ++++--------- nomsu_tree.moon | 19 +++------ 8 files changed, 150 insertions(+), 156 deletions(-) diff --git a/core/control_flow.nom b/core/control_flow.nom index f5c61e3..73a0f43 100644 --- a/core/control_flow.nom +++ b/core/control_flow.nom @@ -49,13 +49,13 @@ immediately To see why this is necessary consider: (random()<.5 and false or 99) return Lua value ".." - (function() + ((function() if \(%condition as lua expr) then return \(%when_true_expr as lua expr) else return \(%when_false_expr as lua expr) end - end)() + end)()) # GOTOs immediately @@ -361,31 +361,46 @@ immediately # Try/except immediately compile [..] - try %action and if it succeeds %success or if it barfs %fallback - try %action and if it barfs %fallback or if it succeeds %success + try %action and if it succeeds %success or if it barfs %msg %fallback + try %action and if it barfs %msg %fallback or if it succeeds %success ..to Lua ".." do local fell_through = false - local ok, ret = pcall(function() + local err, erred = nil, false + local ok, ret = xpcall(function() \(%action as lua statements) fell_through = true + end, function(\(%msg as lua expr)) + local ok, ret = pcall(function() + \(%fallback as lua statements) + end) + if not ok then err, erred = ret, true end end) if ok then \(%success as lua statements) if not fell_through then return ret end - else - \(%fallback as lua statements) + elseif erred then + error(err, 0) end end +immediately + parse [..] + try %action and if it succeeds %success or if it barfs %fallback + try %action and if it barfs %fallback or if it succeeds %success + ..as: try %action and if it succeeds %success or if it barfs (=lua "") %fallback +immediately parse [try %action] as try %action and if it succeeds: do nothing ..or if it barfs: do nothing parse [try %action and if it barfs %fallback] as try %action and if it succeeds: do nothing ..or if it barfs %fallback + parse [try %action and if it barfs %msg %fallback] as + try %action and if it succeeds: do nothing + ..or if it barfs %msg %fallback parse [try %action and if it succeeds %success] as try %action and if it succeeds %success or if it barfs: do nothing @@ -406,17 +421,20 @@ immediately fell_through = true end) \(%final_action as lua statements) - if not ok then error(ret) end + if not ok then error(ret, 0) end if not fell_through then return ret end end # Inline thunk: immediately compile [result of %body] to - Lua value ".." - (function() - \(%body as lua statements) - end)() + %body <- (%body as lua statements) + declare locals in %body + return + Lua value ".." + ((function() + \%body + end)()) # Coroutines: immediately diff --git a/core/metaprogramming.nom b/core/metaprogramming.nom index 8ad9448..83293c1 100644 --- a/core/metaprogramming.nom +++ b/core/metaprogramming.nom @@ -132,15 +132,25 @@ immediately lua> ".." local lua = nomsu:tree_to_lua(\%tree) if not lua.is_value then - error("Invalid thing to convert to lua expr: "..tostring(\%tree)) + compile_error(\%tree, "Invalid thing to convert to lua expr:\n%s") end return lua action [%tree as lua statements] =lua "nomsu:tree_to_lua(\%tree):as_statements()" + + action [%tree as lua return] + =lua "nomsu:tree_to_lua(\%tree):as_statements('return ')" - action [%tree with vars %vars] - =lua "\%tree:map(\%vars)" +immediately + compile [%tree with vars %vars] to + barf "Deprecated" + + compile [%tree with %t -> %replacement] to + Lua value ".." + \(%tree as lua expr):map(function(\(%t as lua expr)) + \(%replacement as lua return) + end) compile [declare locals in %code] to Lua value "\(%code as lua expr):declare_locals()" @@ -202,6 +212,8 @@ immediately # Error functions immediately + compile [traceback] to: Lua value "debug.traceback()" + compile [traceback %] to: Lua value "debug.traceback('', \(% as lua expr))" compile [barf] to: Lua "error(nil, 0);" compile [barf %msg] to: Lua "error(\(%msg as lua expr), 0);" compile [assume %condition] to diff --git a/core/scopes.nom b/core/scopes.nom index 72631c0..02ad73d 100644 --- a/core/scopes.nom +++ b/core/scopes.nom @@ -30,7 +30,7 @@ compile [using %definitions %body, using %definitions do %body] to \%setup_lua if not ok then ACTIONS, COMPILE_ACTIONS, ARG_ORDERS = old_actions, old_compile_actions, old_arg_orders - error(ret) + error(ret, 0) end if not fell_through then ACTIONS, COMPILE_ACTIONS, ARG_ORDERS = old_actions, old_compile_actions, old_arg_orders @@ -42,7 +42,7 @@ compile [using %definitions %body, using %definitions do %body] to \%body_lua ACTIONS, COMPILE_ACTIONS, ARG_ORDERS = old_actions, old_compile_actions, old_arg_orders if not ok then - error(ret) + error(ret, 0) end if not fell_through then return ret diff --git a/nomsu.lua b/nomsu.lua index dc1a5d8..646b9af 100644 --- a/nomsu.lua +++ b/nomsu.lua @@ -278,7 +278,7 @@ end local NomsuCompiler do local _class_0 - local stub_pattern, var_pattern, _running_files, _report_error, MAX_LINE, math_expression + local compile_error, stub_pattern, var_pattern, _running_files, MAX_LINE, math_expression local _base_0 = { define_action = function(self, signature, fn, is_compile_action) if is_compile_action == nil then @@ -304,12 +304,13 @@ do end fn_arg_positions = _tbl_0 end + local actions = (is_compile_action and self.environment.COMPILE_ACTIONS or self.environment.ACTIONS) local arg_orders = { } for _index_0 = 1, #signature do local alias = signature[_index_0] local stub = concat(assert(stub_pattern:match(alias)), ' ') - local stub_args = assert(var_pattern:match(alias)); - (is_compile_action and self.environment.COMPILE_ACTIONS or self.environment.ACTIONS)[stub] = fn + local stub_args = assert(var_pattern:match(alias)) + actions[stub] = fn do local _accum_0 = { } local _len_0 = 1 @@ -507,10 +508,11 @@ do end args = _accum_0 end + local arg_orders = self.environment.ARG_ORDERS[compile_action] do local _accum_0 = { } local _len_0 = 1 - local _list_0 = self.environment.ARG_ORDERS[compile_action][stub] + local _list_0 = arg_orders[stub] for _index_0 = 1, #_list_0 do local p = _list_0[_index_0] _accum_0[_len_0] = args[p - 1] @@ -520,9 +522,7 @@ do end local ret = compile_action(tree, unpack(args)) if not ret then - _report_error(tree, function(src) - return "Compile-time action:\n" .. tostring(src) .. "\nfailed to produce any Lua" - end) + compile_error(tree, "Compile-time action:\n%s\nfailed to produce any Lua") end return ret end @@ -535,9 +535,7 @@ do else local tok_lua = self:tree_to_lua(tok) if not (tok_lua.is_value) then - _report_error(tok, function(src) - return "Non-expression value inside math expression:\n" .. tostring(src) - end) + compile_error(tok, "Non-expression value inside math expression:\n%s") end if tok.type == "Action" then tok_lua:parenthesize() @@ -560,9 +558,7 @@ do end local arg_lua = self:tree_to_lua(tok) if not (arg_lua.is_value) then - _report_error(tok, function(src) - return "Cannot use:\n" .. tostring(src) .. "\nas an argument to " .. tostring(stub) .. ", since it's not an expression, it produces: " .. tostring(repr(arg_lua)), 0 - end) + compile_error(tok, "Cannot use:\n%s\nas an argument to %s, since it's not an expression, it produces: %s", stub, repr(arg_lua)) end insert(args, arg_lua) _continue_0 = true @@ -649,9 +645,7 @@ do if not (bit_lua.is_value) then local src = ' ' .. tostring(self:tree_to_nomsu(bit)):gsub('\n', '\n ') local line = tostring(bit.source.filename) .. ":" .. tostring(pos_to_line(FILE_CACHE[bit.source.filename], bit.source.start)) - _report_error(bit, function(src) - return "Cannot use:\n" .. tostring(src) .. "\nas a string interpolation value, since it's not an expression." - end) + compile_error(bit, "Cannot use:\n%s\nas a string interpolation value, since it's not an expression.") end if #lua.bits > 0 then lua:append("..") @@ -682,9 +676,7 @@ do for i, item in ipairs(tree) do local item_lua = self:tree_to_lua(item) if not (item_lua.is_value) then - _report_error(item, function(src) - return "Cannot use:\n" .. tostring(src) .. "\nas a list item, since it's not an expression." - end) + compile_error(item, "Cannot use:\n%s\nas a list item, since it's not an expression.") end lua:append(item_lua) local item_string = tostring(item_lua) @@ -735,15 +727,11 @@ do local key, value = tree[1], tree[2] local key_lua = self:tree_to_lua(key) if not (key_lua.is_value) then - _report_error(tree[1], function(src) - return "Cannot use:\n" .. tostring(src) .. "\nas a dict key, since it's not an expression." - end) + compile_error(tree[1], "Cannot use:\n%s\nas a dict key, since it's not an expression.") end local value_lua = value and self:tree_to_lua(value) or Lua.Value(key.source, "true") if not (value_lua.is_value) then - _report_error(tree[2], function(src) - return "Cannot use:\n" .. tostring(src) .. "\nas a dict value, since it's not an expression." - end) + compile_error(tree[2], "Cannot use:\n%s\nas a dict value, since it's not an expression.") end local key_str = tostring(key_lua):match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=]) if key_str then @@ -756,9 +744,7 @@ do elseif "IndexChain" == _exp_0 then local lua = self:tree_to_lua(tree[1]) if not (lua.is_value) then - _report_error(tree[1], function(src) - return "Cannot index:\n" .. tostring(src) .. "\nsince it's not an expression." - end) + compile_error(tree[1], "Cannot index:\n%s\nsince it's not an expression.") end local first_char = tostring(lua):sub(1, 1) if first_char == "{" or first_char == '"' or first_char == "[" then @@ -768,9 +754,7 @@ do local key = tree[i] local key_lua = self:tree_to_lua(key) if not (key_lua.is_value) then - _report_error(key, function(src) - return "Cannot use:\n" .. tostring(key) .. "\nas an index, since it's not an expression." - end) + compile_error(key, "Cannot use:\n%s\nas an index, since it's not an expression.") end local key_lua_str = tostring(key_lua) do @@ -1135,9 +1119,7 @@ do else local bit_lua = nomsu:tree_to_lua(bit) if not (bit_lua.is_value) then - _report_error(bit, function(src) - return "Cannot use:\n" .. tostring(src) .. "\nas a string interpolation value, since it's not an expression." - end) + compile_error(bit, "Cannot use:\n%s\nas a string interpolation value, since it's not an expression.") end lua:append(bit_lua) end @@ -1164,9 +1146,7 @@ do else local bit_lua = nomsu:tree_to_lua(bit) if not (bit_lua.is_value) then - _report_error(bit, function(src) - return "Cannot use:\n" .. tostring(src) .. "\nas a string interpolation value, since it's not an expression." - end) + compile_error(bit, "Cannot use:\n%s\nas a string interpolation value, since it's not an expression.") end lua:append(bit_lua) end @@ -1186,12 +1166,10 @@ do return add_lua_bits(Lua.Value(self.source), _code) end) return self:define_compile_action("use %path", function(self, _path) - local path - if _path.type == 'Text' and #_path == 1 and type(_path[1]) == 'string' then - path = _path[1] - else - path = nomsu:run_lua(Lua(_path.source, "return ", nomsu:tree_to_lua(_path))) + if not (_path.type == 'Text' and #_path == 1 and type(_path[1]) == 'string') then + return Lua(_path.source, "nomsu:run_file(" .. tostring(nomsu:tree_to_lua(_path)) .. ");") end + local path = _path[1] nomsu:run_file(path) return Lua(_path.source, "nomsu:run_file(" .. tostring(repr(path)) .. ");") end) @@ -1260,6 +1238,7 @@ do utils = utils, lpeg = lpeg, re = re, + compile_error = compile_error, next = next, unpack = unpack, setmetatable = setmetatable, @@ -1382,6 +1361,18 @@ do }) _base_0.__class = _class_0 local self = _class_0 + compile_error = function(tok, err_format_string, ...) + local file = FILE_CACHE[tok.source.filename] + local line_no = pos_to_line(file, tok.source.start) + local line_start = LINE_STARTS[file][line_no] + local src = colored.dim(file:sub(line_start, tok.source.start - 1)) + src = src .. colored.underscore(colored.bright(colored.red(file:sub(tok.source.start, tok.source.stop - 1)))) + local end_of_line = (LINE_STARTS[file][pos_to_line(file, tok.source.stop) + 1] or 0) - 1 + src = src .. colored.dim(file:sub(tok.source.stop, end_of_line - 1)) + src = ' ' .. src:gsub('\n', '\n ') + local err_msg = err_format_string:format(src, ...) + return error(tostring(tok.source.filename) .. ":" .. tostring(line_no) .. ": " .. err_msg, 0) + end local stub_defs do stub_defs = { @@ -1394,18 +1385,6 @@ do ]=], stub_defs) var_pattern = re.compile("{| ((('%' {%varname}) / %word) ([ ])*)+ !. |}", stub_defs) _running_files = { } - _report_error = function(tok, fn) - local file = FILE_CACHE[tok.source.filename] - local line_no = pos_to_line(file, tok.source.start) - local line_start = LINE_STARTS[file][line_no] - local src = colored.dim(file:sub(line_start, tok.source.start - 1)) - src = src .. colored.underscore(colored.bright(colored.red(file:sub(tok.source.start, tok.source.stop - 1)))) - local end_of_line = (LINE_STARTS[file][pos_to_line(file, tok.source.stop) + 1] or 0) - 1 - src = src .. colored.dim(file:sub(tok.source.stop, end_of_line - 1)) - src = ' ' .. src:gsub('\n', '\n ') - local err_msg = fn(src) - return error(tostring(tok.source.filename) .. ":" .. tostring(line_no) .. ": " .. err_msg, 0) - end MAX_LINE = 80 math_expression = re.compile([[ ([+-] " ")* "%" (" " [*/^+-] (" " [+-])* " %")+ !. ]]) NomsuCompiler = _class_0 diff --git a/nomsu.moon b/nomsu.moon index b47b32a..1196b37 100755 --- a/nomsu.moon +++ b/nomsu.moon @@ -230,6 +230,17 @@ NOMSU_PATTERN = do re.compile(nomsu_peg, NOMSU_DEFS) class NomsuCompiler + compile_error = (tok, err_format_string, ...)-> + file = FILE_CACHE[tok.source.filename] + line_no = pos_to_line(file, tok.source.start) + line_start = LINE_STARTS[file][line_no] + src = colored.dim(file\sub(line_start, tok.source.start-1)) + src ..= colored.underscore colored.bright colored.red(file\sub(tok.source.start, tok.source.stop-1)) + end_of_line = (LINE_STARTS[file][pos_to_line(file, tok.source.stop) + 1] or 0) - 1 + src ..= colored.dim(file\sub(tok.source.stop, end_of_line-1)) + src = ' '..src\gsub('\n', '\n ') + err_msg = err_format_string\format(src, ...) + error("#{tok.source.filename}:#{line_no}: "..err_msg, 0) new: => -- Weak-key mapping from objects to randomly generated unique IDs NaN_surrogate = {} @@ -259,6 +270,7 @@ class NomsuCompiler @environment = { -- Discretionary/convenience stuff nomsu:self, repr:repr, stringify:stringify, utils:utils, lpeg:lpeg, re:re, + :compile_error -- Lua stuff: :next, :unpack, :setmetatable, :coroutine, :rawequal, :getmetatable, :pcall, :error, :package, :os, :require, :tonumber, :tostring, :string, :xpcall, :module, @@ -329,11 +341,12 @@ class NomsuCompiler fn_info = debug_getinfo(fn, "u") assert(not fn_info.isvararg, "Vararg functions aren't supported. Sorry, use a list instead.") fn_arg_positions = {debug.getlocal(fn, i), i for i=1,fn_info.nparams} + actions = (is_compile_action and @environment.COMPILE_ACTIONS or @environment.ACTIONS) arg_orders = {} for alias in *signature stub = concat(assert(stub_pattern\match(alias)), ' ') stub_args = assert(var_pattern\match(alias)) - (is_compile_action and @environment.COMPILE_ACTIONS or @environment.ACTIONS)[stub] = fn + actions[stub] = fn arg_orders[stub] = [fn_arg_positions[string.as_lua_id a] for a in *stub_args] @environment.ARG_ORDERS[fn] = arg_orders @@ -411,17 +424,6 @@ class NomsuCompiler loaded[filename] = ret or true return ret - _report_error = (tok, fn)-> - file = FILE_CACHE[tok.source.filename] - line_no = pos_to_line(file, tok.source.start) - line_start = LINE_STARTS[file][line_no] - src = colored.dim(file\sub(line_start, tok.source.start-1)) - src ..= colored.underscore colored.bright colored.red(file\sub(tok.source.start, tok.source.stop-1)) - end_of_line = (LINE_STARTS[file][pos_to_line(file, tok.source.stop) + 1] or 0) - 1 - src ..= colored.dim(file\sub(tok.source.stop, end_of_line-1)) - src = ' '..src\gsub('\n', '\n ') - err_msg = fn(src) - error("#{tok.source.filename}:#{line_no}: "..err_msg, 0) run_lua: (lua)=> assert(type(lua) != 'string', "Attempt to run lua string instead of Lua (object)") lua_string = tostring(lua) @@ -469,13 +471,14 @@ class NomsuCompiler if compile_action args = [arg for arg in *tree when type(arg) != "string"] -- Force all compile-time actions to take a tree location - args = [args[p-1] for p in *@environment.ARG_ORDERS[compile_action][stub]] + arg_orders = @environment.ARG_ORDERS[compile_action] + args = [args[p-1] for p in *arg_orders[stub]] -- Force Lua to avoid tail call optimization for debugging purposes -- TODO: use tail call ret = compile_action(tree, unpack(args)) if not ret - _report_error tree, (src)-> - "Compile-time action:\n#{src}\nfailed to produce any Lua" + compile_error tree, + "Compile-time action:\n%s\nfailed to produce any Lua" return ret action = rawget(@environment.ACTIONS, stub) lua = Lua.Value(tree.source) @@ -489,8 +492,8 @@ class NomsuCompiler else tok_lua = @tree_to_lua(tok) unless tok_lua.is_value - _report_error tok, (src)-> - "Non-expression value inside math expression:\n#{src}" + compile_error tok, + "Non-expression value inside math expression:\n%s" if tok.type == "Action" tok_lua\parenthesize! lua\append tok_lua @@ -503,8 +506,9 @@ class NomsuCompiler if type(tok) == "string" then continue arg_lua = @tree_to_lua(tok) unless arg_lua.is_value - _report_error tok, (src)-> - "Cannot use:\n#{src}\nas an argument to #{stub}, since it's not an expression, it produces: #{repr arg_lua}", 0 + compile_error tok, + "Cannot use:\n%s\nas an argument to %s, since it's not an expression, it produces: %s", + stub, repr arg_lua insert args, arg_lua if action @@ -554,8 +558,8 @@ class NomsuCompiler unless bit_lua.is_value src = ' '..tostring(@tree_to_nomsu(bit))\gsub('\n','\n ') line = "#{bit.source.filename}:#{pos_to_line(FILE_CACHE[bit.source.filename], bit.source.start)}" - _report_error bit, (src)-> - "Cannot use:\n#{src}\nas a string interpolation value, since it's not an expression." + compile_error bit, + "Cannot use:\n%s\nas a string interpolation value, since it's not an expression." if #lua.bits > 0 then lua\append ".." if bit.type != "Text" bit_lua = Lua.Value(bit.source, "stringify(",bit_lua,")") @@ -575,8 +579,8 @@ class NomsuCompiler for i, item in ipairs tree item_lua = @tree_to_lua(item) unless item_lua.is_value - _report_error item, (src)-> - "Cannot use:\n#{src}\nas a list item, since it's not an expression." + compile_error item, + "Cannot use:\n%s\nas a list item, since it's not an expression." lua\append item_lua item_string = tostring(item_lua) last_line = item_string\match("[^\n]*$") @@ -621,12 +625,12 @@ class NomsuCompiler key, value = tree[1], tree[2] key_lua = @tree_to_lua(key) unless key_lua.is_value - _report_error tree[1], (src)-> - "Cannot use:\n#{src}\nas a dict key, since it's not an expression." + compile_error tree[1], + "Cannot use:\n%s\nas a dict key, since it's not an expression." value_lua = value and @tree_to_lua(value) or Lua.Value(key.source, "true") unless value_lua.is_value - _report_error tree[2], (src)-> - "Cannot use:\n#{src}\nas a dict value, since it's not an expression." + compile_error tree[2], + "Cannot use:\n%s\nas a dict value, since it's not an expression." key_str = tostring(key_lua)\match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=]) return if key_str Lua tree.source, key_str,"=",value_lua @@ -641,8 +645,8 @@ class NomsuCompiler when "IndexChain" lua = @tree_to_lua(tree[1]) unless lua.is_value - _report_error tree[1], (src)-> - "Cannot index:\n#{src}\nsince it's not an expression." + compile_error tree[1], + "Cannot index:\n%s\nsince it's not an expression." first_char = tostring(lua)\sub(1,1) if first_char == "{" or first_char == '"' or first_char == "[" lua\parenthesize! @@ -651,8 +655,8 @@ class NomsuCompiler key = tree[i] key_lua = @tree_to_lua(key) unless key_lua.is_value - _report_error key, (src)-> - "Cannot use:\n#{key}\nas an index, since it's not an expression." + compile_error key, + "Cannot use:\n%s\nas an index, since it's not an expression." key_lua_str = tostring(key_lua) if lua_id = key_lua_str\match("^['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]$") lua\append ".#{lua_id}" @@ -927,8 +931,8 @@ class NomsuCompiler else bit_lua = nomsu\tree_to_lua(bit) unless bit_lua.is_value - _report_error bit, (src)-> - "Cannot use:\n#{src}\nas a string interpolation value, since it's not an expression." + compile_error bit, + "Cannot use:\n%s\nas a string interpolation value, since it's not an expression." lua\append bit_lua @define_compile_action "Lua %code", (_code)=> @@ -950,8 +954,8 @@ class NomsuCompiler else bit_lua = nomsu\tree_to_lua(bit) unless bit_lua.is_value - _report_error bit, (src)-> - "Cannot use:\n#{src}\nas a string interpolation value, since it's not an expression." + compile_error bit, + "Cannot use:\n%s\nas a string interpolation value, since it's not an expression." lua\append bit_lua return lua @@ -966,10 +970,9 @@ class NomsuCompiler return add_lua_bits(Lua.Value(@source), _code) @define_compile_action "use %path", (_path)=> - path = if _path.type == 'Text' and #_path == 1 and type(_path[1]) == 'string' - _path[1] - else - nomsu\run_lua Lua(_path.source, "return ",nomsu\tree_to_lua(_path)) + unless _path.type == 'Text' and #_path == 1 and type(_path[1]) == 'string' + return Lua(_path.source, "nomsu:run_file(#{nomsu\tree_to_lua(_path)});") + path = _path[1] nomsu\run_file(path) return Lua(_path.source, "nomsu:run_file(#{repr path});") diff --git a/nomsu.peg b/nomsu.peg index cab5dfb..ec40211 100644 --- a/nomsu.peg +++ b/nomsu.peg @@ -16,7 +16,7 @@ noindex_inline_expression: number / variable / inline_text / inline_list / inline_dict / inline_nomsu / ( "(" %ws* (inline_block / inline_action / inline_expression) %ws* - (comma %ws* (inline_block / inline_action / inline_expression) %ws*)* + (%ws* ',' %ws* (inline_block / inline_action / inline_expression) %ws*)* (")" / (({} ((!. / &%nl) -> 'Line ended without finding a closing )-parenthesis') %userdata) => error) / (({} ([^%nl]* -> 'Unexpected character while parsing subexpression') %userdata) => error) @@ -47,17 +47,21 @@ indented_nomsu (EscapedNomsu): index_chain (IndexChain): {| noindex_inline_expression ("." (text_word / noindex_inline_expression))+ |} --- Actions need at least one word in them +-- Actions need either at least 1 word, or at least 2 tokens inline_action (Action): {| - (inline_expression %ws*)* word (%ws* (inline_expression / word))* + ( (inline_expression (%ws* (inline_expression / word))+) + / (word (%ws* (inline_expression / word))*)) (%ws* ":" %ws* (inline_block / inline_action / inline_expression / (({} ('' -> "Missing expression after the ':'") %userdata) => error)))? |} action (Action): - {| (expression (dotdot? %ws*))* word ((dotdot? %ws*) (expression / word))* |} + {| + (expression ((nodent "..")? %ws* (expression / word))+) + / (word ((nodent "..")? %ws* (expression / word))*) + |} -word: { %operator_char+ / (!number %ident_char+) } +word: !number { %operator_char+ / %ident_char+ } text_word (Text): {| word |} @@ -84,7 +88,7 @@ inline_text_interpolation: variable / inline_list / inline_dict / inline_text / ("(" %ws* (inline_block / inline_action / inline_expression) %ws* - (comma %ws* (inline_block / inline_action / inline_expression) %ws*)* + (%ws* ',' %ws* (inline_block / inline_action / inline_expression) %ws*)* (")" / (({} (&%nl -> 'Line ended without finding a closing )-parenthesis') %userdata) => error) / (({} ([^%nl]* -> 'Unexpected character while parsing Text interpolation') %userdata) => error)) @@ -103,7 +107,7 @@ variable (Var): "%" { (%ident_char+ ((!"'" %operator_char+) / %ident_char+)*)? } inline_list (List): !('[..]') "[" %ws* - {| (inline_list_item (comma inline_list_item)* comma?)? |} %ws* + {| (inline_list_item (%ws* ',' %ws* inline_list_item)* (%ws* ',')?)? |} %ws* ("]" / (","? ( (({} (eol->"Line ended before finding a closing ]-bracket") %userdata) => error) /(({} ([^%nl]*->"Unexpected character while parsing List") %userdata) => error) @@ -113,14 +117,14 @@ indented_list (List): {| list_line (nodent list_line)* |} (dedent / ((","? {} (non_dedent_error -> "Unexpected character while parsing List") %userdata) => error)) list_line: - ((action / expression) !comma) - / (inline_list_item (comma list_line?)?) + ((action / expression) !(%ws* ',')) + / (inline_list_item ((%ws* ',' %ws*) list_line?)?) inline_list_item: inline_block / inline_action / inline_expression inline_dict (Dict): !('{..}') "{" %ws* - {| (inline_dict_entry (comma inline_dict_entry)*)? |} %ws* + {| (inline_dict_entry (%ws* ',' %ws* inline_dict_entry)*)? |} %ws* ("}" / (","? ( (({} (%ws* eol->"Line ended before finding a closing }-brace") %userdata) => error) / (({} ([^%nl]*->"Unexpected character while parsing Dictionary") %userdata) => error) @@ -130,7 +134,7 @@ indented_dict (Dict): {| dict_line (nodent dict_line)* |} (dedent / ((","? {} (non_dedent_error -> "Unexpected character while parsing Dictionary") %userdata) => error)) dict_line: - (dict_entry !comma) / (inline_dict_entry (comma dict_line?)?) + (dict_entry !(%ws* ',')) / (inline_dict_entry (%ws* ',' %ws dict_line?)?) dict_entry(DictEntry): {| dict_key (%ws* ":" %ws* (action / expression))? |} inline_dict_entry(DictEntry): @@ -147,5 +151,3 @@ indent: eol (%nl ignored_line)* %nl %indent (comment (%nl ignored_line)* nodent) nodent: eol (%nl ignored_line)* %nl %nodent dedent: eol (%nl ignored_line)* (((!.) %dedent) / (&(%nl %dedent))) non_dedent_error: (!dedent .)* eol (%nl ignored_line)* (!. / &%nl) -comma: %ws* "," %ws* -dotdot: nodent ".." diff --git a/nomsu_tree.lua b/nomsu_tree.lua index de021eb..f1b6489 100644 --- a/nomsu_tree.lua +++ b/nomsu_tree.lua @@ -36,18 +36,6 @@ Tree = function(name, fields, methods) return source, ... end methods.is_multi = is_multi - methods.map = function(self, fn) - if type(fn) == 'table' then - if not (next(fn)) then - return self - 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() @@ -61,11 +49,11 @@ Tree = function(name, fields, methods) return _accum_0 end)(), ', ')) .. ")" end - methods._map = function(self, fn) + methods.map = function(self, fn) do - local ret = fn(self) - if ret then - return ret + local replacement = fn(self) + if replacement then + return replacement end end local new_vals @@ -74,19 +62,18 @@ Tree = function(name, fields, methods) local _len_0 = 1 for _index_0 = 1, #self do local v = self[_index_0] - _accum_0[_len_0] = v._map and v:_map(fn) or v + _accum_0[_len_0] = v.map and v:map(fn) or v _len_0 = _len_0 + 1 end new_vals = _accum_0 end - local ret = getmetatable(self)(self.source, unpack(new_vals)) - return ret + return getmetatable(self)(self.source, unpack(new_vals)) end else methods.__tostring = function(self) return tostring(self.name) .. "(" .. tostring(repr(self.value)) .. ")" end - methods._map = function(self, fn) + methods.map = function(self, fn) return fn(self) or self end end diff --git a/nomsu_tree.moon b/nomsu_tree.moon index fe365c2..3339606 100644 --- a/nomsu_tree.moon +++ b/nomsu_tree.moon @@ -27,23 +27,16 @@ Tree = (name, fields, methods)-> --assert Source\is_instance(source) return source, ... .is_multi = is_multi - .map = (fn)=> - if type(fn) == 'table' - return @ unless next(fn) - _replacements = fn - fn = (k)-> _replacements[k] - return @_map(fn) if is_multi .__tostring = => "#{@name}(#{table.concat [repr(v) for v in *@], ', '})" - ._map = (fn)=> - if ret = fn(@) - return ret - new_vals = [v._map and v\_map(fn) or v for v in *@] - ret = getmetatable(self)(@source, unpack(new_vals)) - return ret + .map = (fn)=> + if replacement = fn(@) + return replacement + new_vals = [v.map and v\map(fn) or v for v in *@] + return getmetatable(self)(@source, unpack(new_vals)) else .__tostring = => "#{@name}(#{repr(@value)})" - ._map = (fn)=> + .map = (fn)=> fn(@) or @ Types[name] = immutable fields, methods