diff --git a/code_obj.lua b/code_obj.lua index 5afca1d..a09608f 100644 --- a/code_obj.lua +++ b/code_obj.lua @@ -320,6 +320,10 @@ do return self:dirty() end, remove_free_vars = function(self, vars) + if vars == nil then + vars = nil + end + vars = vars or self:get_free_vars() if not (#vars > 0) then return end @@ -351,33 +355,34 @@ do end return self:dirty() end, + get_free_vars = function(self) + local vars, seen = { }, { } + local gather_from + gather_from = function(self) + local _list_0 = self.free_vars + for _index_0 = 1, #_list_0 do + local var = _list_0[_index_0] + if not (seen[var]) then + seen[var] = true + vars[#vars + 1] = var + end + end + local _list_1 = self.bits + for _index_0 = 1, #_list_1 do + local bit = _list_1[_index_0] + if not (type(bit) == 'string') then + gather_from(bit) + end + end + end + gather_from(self) + return vars + end, declare_locals = function(self, to_declare) if to_declare == nil then to_declare = nil end - if to_declare == nil then - local seen - to_declare, seen = { }, { } - local gather_from - gather_from = function(self) - local _list_0 = self.free_vars - for _index_0 = 1, #_list_0 do - local var = _list_0[_index_0] - if not (seen[var]) then - seen[var] = true - to_declare[#to_declare + 1] = var - end - end - local _list_1 = self.bits - for _index_0 = 1, #_list_1 do - local bit = _list_1[_index_0] - if not (type(bit) == 'string') then - gather_from(bit) - end - end - end - gather_from(self) - end + to_declare = to_declare or self:get_free_vars() if #to_declare > 0 then self:remove_free_vars(to_declare) self:prepend("local " .. tostring(concat(to_declare, ", ")) .. ";\n") diff --git a/code_obj.moon b/code_obj.moon index bdce010..9315264 100644 --- a/code_obj.moon +++ b/code_obj.moon @@ -179,7 +179,8 @@ class LuaCode extends Code seen[var] = true @dirty! - remove_free_vars: (vars)=> + remove_free_vars: (vars=nil)=> + vars or= @get_free_vars! return unless #vars > 0 removals = {} for var in *vars @@ -198,18 +199,21 @@ class LuaCode extends Code stack[#stack+1] = b @dirty! + get_free_vars: => + vars, seen = {}, {} + gather_from = => + for var in *@free_vars + unless seen[var] + seen[var] = true + vars[#vars+1] = var + for bit in *@bits + unless type(bit) == 'string' + gather_from bit + gather_from self + return vars + declare_locals: (to_declare=nil)=> - if to_declare == nil - to_declare, seen = {}, {} - gather_from = => - for var in *@free_vars - unless seen[var] - seen[var] = true - to_declare[#to_declare+1] = var - for bit in *@bits - unless type(bit) == 'string' - gather_from bit - gather_from self + to_declare or= @get_free_vars! if #to_declare > 0 @remove_free_vars to_declare @prepend "local #{concat to_declare, ", "};\n" diff --git a/containers.lua b/containers.lua index d4514af..418a819 100644 --- a/containers.lua +++ b/containers.lua @@ -251,18 +251,6 @@ List = function(t) return error("Unsupported List type: " .. type(t)) end end -local walk_items -walk_items = function(self, i) - i = i + 1 - local k, v = next(self.table, self.key) - if k ~= nil then - self.key = k - return i, Dict({ - key = k, - value = v - }) - end -end local _dict_mt = { __type = "Dict", __eq = function(self, other) @@ -321,12 +309,6 @@ local _dict_mt = { return _accum_0 end)(), ", ") .. "}" end, - __ipairs = function(self) - return walk_items, { - table = self, - key = nil - }, 0 - end, __band = function(self, other) return Dict((function() local _tbl_0 = { } @@ -339,30 +321,28 @@ local _dict_mt = { end)()) end, __bor = function(self, other) - local ret - do + local ret = Dict((function() local _tbl_0 = { } for k, v in pairs(self) do _tbl_0[k] = v end - ret = _tbl_0 - end + return _tbl_0 + end)()) for k, v in pairs(other) do if ret[k] == nil then ret[k] = v end end - return Dict(ret) + return ret end, __bxor = function(self, other) - local ret - do + local ret = Dict((function() local _tbl_0 = { } for k, v in pairs(self) do _tbl_0[k] = v end - ret = _tbl_0 - end + return _tbl_0 + end)()) for k, v in pairs(other) do if ret[k] == nil then ret[k] = v @@ -370,17 +350,16 @@ local _dict_mt = { ret[k] = nil end end - return Dict(ret) + return ret end, __add = function(self, other) - local ret - do + local ret = Dict((function() local _tbl_0 = { } for k, v in pairs(self) do _tbl_0[k] = v end - ret = _tbl_0 - end + return _tbl_0 + end)()) for k, v in pairs(other) do if ret[k] == nil then ret[k] = v @@ -388,17 +367,16 @@ local _dict_mt = { ret[k] = ret[k] + v end end - return Dict(ret) + return ret end, __sub = function(self, other) - local ret - do + local ret = Dict((function() local _tbl_0 = { } for k, v in pairs(self) do _tbl_0[k] = v end - ret = _tbl_0 - end + return _tbl_0 + end)()) for k, v in pairs(other) do if ret[k] == nil then ret[k] = -v @@ -406,7 +384,7 @@ local _dict_mt = { ret[k] = ret[k] - v end end - return Dict(ret) + return ret end } Dict = function(t) @@ -430,11 +408,6 @@ Dict = function(t) return error("Unsupported Dict type: " .. type(t)) end end -for i, entry in ipairs(Dict({ - x = 99 -})) do - assert(i == 1 and entry.key == "x" and entry.value == 99, "ipairs compatibility issue") -end do local reverse, upper, lower, find, byte, match, gmatch, gsub, sub, format, rep do @@ -502,9 +475,9 @@ do return (match(self, patt)) end, matching_groups = function(self, patt) - return { + return List({ match(self, patt) - } + }) end, [as_lua_id("* 1")] = function(self, n) return rep(self, n) diff --git a/containers.moon b/containers.moon index 26c1032..be0925b 100644 --- a/containers.moon +++ b/containers.moon @@ -106,13 +106,6 @@ List = (t)-> return l else error("Unsupported List type: "..type(t)) -walk_items = (i)=> - i = i + 1 - k, v = next(@table, @key) - if k != nil - @key = k - return i, Dict{key:k, value:v} - _dict_mt = __type: "Dict" __eq: (other)=> @@ -133,32 +126,31 @@ _dict_mt = "{"..concat(["#{as_nomsu(k)}: #{as_nomsu(v)}" for k,v in pairs @], ", ").."}" as_lua: => "Dict{"..concat(["[ #{as_lua(k)}]= #{as_lua(v)}" for k,v in pairs @], ", ").."}" - __ipairs: => walk_items, {table:@, key:nil}, 0 __band: (other)=> Dict{k,v for k,v in pairs(@) when other[k] != nil} __bor: (other)=> - ret = {k,v for k,v in pairs(@)} + ret = Dict{k,v for k,v in pairs(@)} for k,v in pairs(other) if ret[k] == nil then ret[k] = v - return Dict(ret) + return ret __bxor: (other)=> - ret = {k,v for k,v in pairs(@)} + ret = Dict{k,v for k,v in pairs(@)} for k,v in pairs(other) if ret[k] == nil then ret[k] = v else ret[k] = nil - return Dict(ret) + return ret __add: (other)=> - ret = {k,v for k,v in pairs(@)} + ret = Dict{k,v for k,v in pairs(@)} for k,v in pairs(other) if ret[k] == nil then ret[k] = v else ret[k] += v - return Dict(ret) + return ret __sub: (other)=> - ret = {k,v for k,v in pairs(@)} + ret = Dict{k,v for k,v in pairs(@)} for k,v in pairs(other) if ret[k] == nil then ret[k] = -v else ret[k] -= v - return Dict(ret) + return ret Dict = (t)-> if type(t) == 'table' return setmetatable(t, _dict_mt) @@ -171,8 +163,6 @@ Dict = (t)-> return d else error("Unsupported Dict type: "..type(t)) -for i,entry in ipairs(Dict({x:99})) - assert(i == 1 and entry.key == "x" and entry.value == 99, "ipairs compatibility issue") do {:reverse, :upper, :lower, :find, :byte, :match, :gmatch, :gsub, :sub, :format, :rep} = string @@ -204,7 +194,7 @@ do line_position_at: (i)=> select(3, line_at(@, i)) matches: (patt)=> match(@, patt) and true or false matching: (patt)=> (match(@, patt)) - matching_groups: (patt)=> {match(@, patt)} + matching_groups: (patt)=> List{match(@, patt)} [as_lua_id "* 1"]: (n)=> rep(@, n) all_matches_of: (patt)=> result = {} diff --git a/core/collections.nom b/core/collections.nom index 69685b8..6aac861 100644 --- a/core/collections.nom +++ b/core/collections.nom @@ -37,7 +37,6 @@ test: test: $dict = {.x = 1, .y = 2, .z = 3} assume (size of $dict) == 3 - assume [: for $ in {.x = 1}: add $] == [{.key = "x", .value = 1}] assume [: for $k = $v in {.x = 1}: add {.key = $k, .value = $v}] == [{.key = "x", .value = 1}] assume ({.x = 1, .y = 1} + {.y = 10, .z = 10}) == {.x = 1, .y = 11, .z = 10} diff --git a/core/control_flow.nom b/core/control_flow.nom index 16f8537..f066767 100644 --- a/core/control_flow.nom +++ b/core/control_flow.nom @@ -5,7 +5,6 @@ use "core/metaprogramming.nom" use "core/operators.nom" -use "core/errors.nom" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -448,32 +447,6 @@ test: end -- do ") -test: - $d = {} - try: - do: - $d.x = "bad" - barf - ..then always: - $d.x = "good" - assume ($d.x == "good") - -(do $action then always $final_action) compiles to: - define mangler - return - Lua (" - do - local \(mangle "fell_through") = false - local \(mangle "ok"), \(mangle "ret") = pcall(function() - \($action as lua) - \(mangle "fell_through") = true - end) - \($final_action as lua) - if not \(mangle "ok") then error(ret, 0) end - if not \(mangle "fell_through") then return ret end - end - ") - test: assume ((result of: return 99) == 99) diff --git a/core/errors.nom b/core/errors.nom index 6779922..0b63b5b 100644 --- a/core/errors.nom +++ b/core/errors.nom @@ -3,11 +3,12 @@ This file contains basic error reporting code use "core/metaprogramming.nom" +use "core/operators.nom" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -(barf $msg) compiles to - "error(\(=lua "\$msg and \($msg as lua expr) or 'nil'"), 0);" +(fail $msg) compiles to + "error(\(($msg as lua expr) if $msg else "nil"), 0);" (assume $condition) compiles to: lua> (" @@ -37,62 +38,86 @@ use "core/metaprogramming.nom" end ") -(assume $condition or barf $message) compiles to (" - if not \($condition as lua expr) then - error(\($message as lua expr), 0) - end -") - test: - try (barf) and if it succeeds: - barf "try failed." + try: fail $worked = (no) - try (barf) and if it barfs: + try: fail + ..if it fails: $worked = (yes) - assume $worked or barf "try/catch failed" - $x = 1 - try: - $x = 2 - do (barf) then always: $x = 3 - ..and if it barfs: - do nothing - assume ($x == 3) or barf "do/then always failed" - + ..if it succeeds: + fail "'try' incorrectly ran success case." + + unless $worked: + fail "'try' failed to recover from failure" # Try/except [ - 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 -] all compile to (" - do - local fell_through = false - local err, erred = nil, false - local ok, ret = xpcall(function() - \($action as lua) - fell_through = true - end, function(\(=lua "\$fallback and \($msg as lua expr) or ''")) - local ok, ret = pcall(function() - \((=lua "\$fallback or \$msg") as lua) - end) - if not ok then err, erred = ret, true end - end) - if ok then - \($success as lua) - if not fell_through then - return ret + try $action if it succeeds $success if it fails $fallback + try $action if it fails $fallback if it succeeds $success +] all compile to: + $success_lua = ($success as lua) + if ((#"\$success_lua") > 0): $success_lua, add "\n" + $success_lua, prepend "-- Success:\n" + $success_lua, add "if not _fell_through then return table.unpack(_result, 2) end" + $fallback_lua = ($fallback as lua) + if ((#"\$fallback_lua") > 0): + $fallback_lua, prepend "\nlocal function failure() return _result[2] end\n" + $fallback_lua, prepend "-- Failure:" + return + Lua (" + do + local _fell_through = false + local _result = {pcall(function() + \($action as lua) + _fell_through = true + end)} + if _result[1] then + \$success_lua + else + \$fallback_lua + end end - elseif erred then - error(err, 0) - end - end -") + ") ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (try $action) parses as - try $action and if it succeeds (do nothing) or if it barfs (do nothing) + try $action if it succeeds (do nothing) if it fails (do nothing) -(try $action and if it barfs $msg $fallback) parses as - try $action and if it succeeds (do nothing) or if it barfs $msg $fallback +(try $action if it fails $msg $fallback) parses as + try $action if it succeeds (do nothing) if it fails $msg $fallback -(try $action and if it succeeds $success) parses as - try $action and if it succeeds $success or if it barfs (do nothing) +(try $action if it succeeds $success) parses as + try $action if it succeeds $success if it fails (do nothing) + +(try $action if it fails $fallback if it succeeds $success) parses as + try $action if it succeeds $success if it fails $fallback + +test: + $success = (no) + try: + do: fail + ..then always: + $success = (yes) + ..if it succeeds: + fail "'try ... then always ...' didn't propagate failure" + + unless $success: + fail "'try ... then always ...' didn't execute the 'always' code" + +(do $action then always $final_action) compiles to (" + do -- do/then always + local _fell_through = false + local _results = {pcall(function() + \($action as lua) + _fell_through = true + end)} + \($final_action as lua) + if not _results[1] then error(_results[2], 0) end + if not _fell_through then return table.unpack(_results, 2) end + end +") + +~~~ + +(barf $) parses as (fail $) +(assume $1 or barf $2) parses as (unless $1: fail $2) diff --git a/core/metaprogramming.nom b/core/metaprogramming.nom index 2e1bf6b..b4297dd 100644 --- a/core/metaprogramming.nom +++ b/core/metaprogramming.nom @@ -308,11 +308,11 @@ externally ($ is $kind syntax tree) means ($tree with $t -> $replacement) compiles to (" \($tree as lua expr):map(function(\($t as lua expr)) \( - =lua (" - \$replacement.type == 'Block' and \($replacement as lua) or 'return '..\ - ..\($replacement as lua expr) - ") - ) + =lua (" + \$replacement.type == 'Block' and \($replacement as lua) or 'return '..\ + ..\($replacement as lua expr) + ") + ) end) ") diff --git a/core/operators.nom b/core/operators.nom index 8d646e9..6c089a6 100644 --- a/core/operators.nom +++ b/core/operators.nom @@ -3,7 +3,6 @@ This file contains definitions of operators like "+" and "and". use "core/metaprogramming.nom" -use "core/errors.nom" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/nomsu.6.peg b/nomsu.6.peg index 6b4686d..6a1725a 100644 --- a/nomsu.6.peg +++ b/nomsu.6.peg @@ -168,7 +168,11 @@ blank_text_lines <- {~ (%nl ((ws* -> '') (&%nl / !.) / (=curr_indent -> '') &[^%nl]))+ ~} text_interpolation <- - ("\" (indented_block (blank_lines =curr_indent "..")? / indented_expression)) + ({| + -- %indentation will backtrack and match the actual indentation of the current line + "\" {:curr_indent: %indentation :} + (indented_block (blank_lines =curr_indent "..")? / indented_expression) + |} -> unpack) / inline_text_interpolation diff --git a/nomsu_compiler.lua b/nomsu_compiler.lua index 9b8fc87..2b49112 100644 --- a/nomsu_compiler.lua +++ b/nomsu_compiler.lua @@ -304,7 +304,7 @@ local compile = setmetatable({ lua:add(",") end if lua:trailing_line_len() + #(entry_lua:text():match("^[\n]*")) > MAX_LINE then - lua:add("\n") + lua:add("\n ") elseif needs_comma then lua:add(" ") end @@ -417,6 +417,8 @@ local compile = setmetatable({ if tree[i].type == "Comment" then items_lua:add("\n") sep = '' + elseif items_lua:trailing_line_len() > MAX_LINE then + sep = ',\n ' else sep = ', ' end diff --git a/nomsu_compiler.moon b/nomsu_compiler.moon index 12a7529..45e863d 100644 --- a/nomsu_compiler.moon +++ b/nomsu_compiler.moon @@ -234,7 +234,7 @@ compile = setmetatable({ entry_lua\add as_lua(v) if needs_comma then lua\add "," if lua\trailing_line_len! + #(entry_lua\text!\match("^[\n]*")) > MAX_LINE - lua\add "\n" + lua\add "\n " elseif needs_comma lua\add " " lua\add entry_lua @@ -322,6 +322,8 @@ compile = setmetatable({ if tree[i].type == "Comment" items_lua\add "\n" sep = '' + elseif items_lua\trailing_line_len! > MAX_LINE + sep = ',\n ' else sep = ', ' i += 1 diff --git a/parser.lua b/parser.lua index 523e223..09386fa 100644 --- a/parser.lua +++ b/parser.lua @@ -26,6 +26,13 @@ do _with_0.unpack = unpack or table.unpack _with_0["nil"] = Cc(nil) _with_0.userdata = Carg(1) + _with_0.indentation = lpeg.Cmt(P(0), function(s, i) + local sub = string.sub + while i > 1 and sub(s, i - 1, i - 1) ~= '\n' do + i = i - 1 + end + return true, (s:match("^ *", i)) + end) _with_0.utf8_char = (R("\194\223") * R("\128\191") + R("\224\239") * R("\128\191") * R("\128\191") + R("\240\244") * R("\128\191") * R("\128\191") * R("\128\191")) _with_0.Tree = function(t, userdata) return userdata.make_tree(t, userdata) diff --git a/parser.moon b/parser.moon index 122b58c..3f36953 100644 --- a/parser.moon +++ b/parser.moon @@ -25,6 +25,13 @@ DEFS = with {} .unpack = unpack or table.unpack .nil = Cc(nil) .userdata = Carg(1) + -- Always match and capture the indentation (spaces only) of the current line + -- i.e. the leading space of the chunk of non-newline characters leading up to s[i] + .indentation = lpeg.Cmt P(0), + (s, i)-> + sub = string.sub + while i > 1 and sub(s,i-1,i-1) != '\n' do i -= 1 + return true, (s\match("^ *", i)) .utf8_char = ( R("\194\223")*R("\128\191") + R("\224\239")*R("\128\191")*R("\128\191") + diff --git a/tools/repl.nom b/tools/repl.nom index 983de5f..4240ddc 100755 --- a/tools/repl.nom +++ b/tools/repl.nom @@ -42,25 +42,44 @@ repeat: if ((size of $buff) == 0): stop $buff = ($buff, joined) - - # TODO: support local variables spoof file $buff try: - $ret = (run $buff) - ..and if it barfs $err: say $err - ..or if it succeeds: - if (type of $ret) is: - "nil": - do nothing - - "boolean": - say "= \("yes" if $ret else "no")" - - "table": - if $ret.as_nomsu: - say "= \($ret, as nomsu)" - ..else: + $tree = ($buff parsed) + ..and if it barfs $err: + say $err + do next + + unless $tree: + do next + + for $chunk in $tree: + try: + $lua = ($chunk as lua) + ..and if it barfs $err: say $err + + unless $lua: + do next + + # TODO: this is a bit hacky, it just defaults variables to global + so that stuff mostly works across multiple lines. It would be + nicer if local variables actually worked. + $lua, remove free vars + try: + $ret = (run $lua) + ..and if it barfs $err: say $err + ..or if it succeeds: + if (type of $ret) is: + "nil": + do nothing + + "boolean": + say "= \("yes" if $ret else "no")" + + "table": + if $ret.as_nomsu: + say "= \($ret, as nomsu)" + ..else: + say "= \$ret" + + else: say "= \$ret" - - else: - say "= \$ret"