diff --git a/lib/control_flow.nom b/lib/control_flow.nom index b1df621..460d4dc 100644 --- a/lib/control_flow.nom +++ b/lib/control_flow.nom @@ -3,137 +3,99 @@ require "lib/operators.nom" require "lib/utils.nom" # Conditionals -macro statement [if %condition %if_body] =: - ".."|if \%condition as lua\ then - | \(lua expr "vars.if_body.value") as lua\ - |end +parse (if %condition %if_body) as lua code ".." + |if \(%condition) then + | \(lua expr "vars.if_body.value") + |end -macro statement [if %condition %if_body else %else_body] =: - ".."|if \%condition as lua\ then - | \(lua expr "vars.if_body.value") as lua\ - |else - | \(lua expr "vars.else_body.value") as lua\ - |end +parse (if %condition %if_body else %else_body) as lua code ".." + |if \(%condition) then + | \(lua expr "vars.if_body.value") + |else + | \(lua expr "vars.else_body.value") + |end # Return -macro statement [return] =: "do return end" -macro block [return %return-value] =: ".." - |return \%return-value as lua\ - -macro [do %action] =: ".." - |(\%action as lua\)(nomsu, setmetatable({}, {__index=vars})) +parse (return) as lua code "do return end" +parse (return %return-value) as lua code "do return \(%return-value)" +parse (do %action) as lua expr ".." + |(\(%action))(nomsu, setmetatable({}, {__index=vars})) # GOTOs -macro statement [-> %label] =: ".." - |::label_\nomsu "var_to_lua_identifier" [%label]\:: -macro statement [go to %label] =: ".." - |goto label_\nomsu "var_to_lua_identifier" [%label]\ +parse (-> %label) as lua code ".." + |::label_\(nomsu "var_to_lua_identifier" [%label]):: +parse (go to %label) as lua code ".." + |goto label_\(nomsu "var_to_lua_identifier" [%label]) # Loop control flow -macro statement [stop, stop loop, break] =: "break" -macro statement [stop for, stop for-loop, break for] =: "goto break_for" -macro statement [stop repeat, stop repeat-loop, break repeat] =: "goto break_repeat" -macro statement [stop %var, break %var] =: ".." - |goto break_\nomsu "var_to_lua_identifier" [%var]\ +parse (stop; stop loop; break) as lua code "break" +parse (stop for; stop for-loop; break for) as lua code "goto break_for" +parse (stop repeat; stop repeat-loop; break repeat) as lua code "goto break_repeat" +parse (stop %var; break %var) as lua code ".." + |goto break_\(nomsu "var_to_lua_identifier" [%var]) -macro statement [continue, continue loop] =: "continue" -macro statement [continue for, continue for-loop] =: "goto continue_for" -macro statement [continue repeat, continue repeat-loop] =: "goto continue_repeat" -macro statement [continue %var, go to next %var, on to the next %var] =: ".." - |goto continue_\nomsu "var_to_lua_identifier" [%var]\ +parse (continue; continue loop) as lua code "continue" +parse (continue for; continue for-loop) as lua code "goto continue_for" +parse (continue repeat; continue repeat-loop) as lua code "goto continue_repeat" +parse (continue %var; go to next %var; on to the next %var) as lua code ".." + |goto continue_\(nomsu "var_to_lua_identifier" [%var]) # While loops -macro block [repeat %body] =: - ".."|while true do - | \(lua expr "vars.body.value") as lua\ - | ::continue_repeat:: - |end - |::break_repeat:: -macro block [repeat while %condition %body] =: - ".."|while \%condition as lua\ do - | \(lua expr "vars.body.value") as lua\ - | ::continue_repeat:: - |end - |::break_repeat:: -macro block [repeat until %condition %body] =: - ".."|while not (\%condition as lua\) do - | \(lua expr "vars.body.value") as lua\ - | ::continue_repeat:: - |end - |::break_repeat:: +parse (repeat %body) as lua block ".." + |while true do + | \(lua expr "vars.body.value") + | ::continue_repeat:: + |end + |::break_repeat:: +parse (repeat while %condition %body) as lua block ".." + |while \(%condition) do + | \(lua expr "vars.body.value") + | ::continue_repeat:: + |end + |::break_repeat:: +parse (repeat until %condition %body) as lua block ".." + |while not (\(%condition)) do + | \(lua expr "vars.body.value") + | ::continue_repeat:: + |end + |::break_repeat:: # Numeric range for loops -macro block [for %var from %start to %stop by %step %body] =: - %var-type =: lua expr "vars.var.type" - assert (%var-type == "Var") ".." - |For loop has the wrong type for the loop variable. Expected Var, but got: \%var-type\ +parse: + for %var from %start to %stop by %step %body + for %var from %start to %stop via %step %body +..as lua block ".." + |for i=\(%start),\(%stop),\(%step) do # This trashes the loop variables, just like in Python. - ".." - |for i=\%start as lua\,\%stop as lua\,\%step as lua\ do - | \%var as lua\ = i - | \(lua expr "vars.body.value") as lua\ - | ::continue_for:: - | ::continue_\nomsu "var_to_lua_identifier" [%var]\:: - |end - |::break_for:: - |::break_\nomsu "var_to_lua_identifier" [%var]\:: -macro block [for %var from %start to %stop %body] =: - %thunk =: :for %var from %start to %stop by 1 %body - lua block ".." - |for i,x in ipairs(vars.thunk.value) do - | if x.type == 'Var' then vars.thunk.type == vars[x.value] end - |end - |return compiler:run_macro(vars.thunk, 'Statement') + | \(%var) = i + | \(lua expr "vars.body.value") + | ::continue_for:: + | ::continue_\(nomsu "var_to_lua_identifier" [%var]):: + |end + |::break_for:: + |::break_\(nomsu "var_to_lua_identifier" [%var]):: +parse (for %var from %start to %stop %body) as: for %var from %start to %stop via 1 %body +parse: + for all %start to %stop by %step %body + for all %start to %stop via %step %body +..as: for % from %start to %stop via %step %body +parse (for all %start to %stop %body) as: for all %start to %stop via 1 %body -macro block [for all %start to %stop by %step %body] =: +parse (for %var in %iterable %body) as lua block ".." + |for i,value in ipairs(\(%iterable)) do # This trashes the loop variables, just like in Python. - ".." - |for i=\%start as lua\,\%stop as lua\,\%step as lua\ do - | vars[''] = i - | \(lua expr "vars.body.value") as lua\ - | ::continue_for:: - | ::continue_\nomsu "var_to_lua_identifier" [%]\:: - |end - |::break_for:: - |::break_\nomsu "var_to_lua_identifier" [%]\:: -macro block [for %var from %start to %stop %body] =: - %thunk =: :for %var from %start to %stop by 1 %body - lua block ".." - |for i,x in ipairs(vars.thunk.value) do - | if x.type == 'Var' then vars.thunk.type == vars[x.value] end - |end - |return compiler:run_macro(vars.thunk, 'Statement') - -macro block [for %var in %iterable %body] =: - %var-type =: lua expr "vars.var.type" - assert (%var-type == "Var") ".." - |For loop has the wrong type for the loop variable. Expected Var, but got: \%var-type\ - # This trashes the loop variables, just like in Python. - ".." - |for i,value in ipairs(\%iterable as lua\) do - | \%var as lua\ = value - | \(lua expr "vars.body.value") as lua\ - | ::continue_for:: - | ::continue_\nomsu "var_to_lua_identifier" [%var]\:: - |end - |::break_for:: - |::break_\nomsu "var_to_lua_identifier" [%var]\:: - -macro block [for all %iterable %body] =: - pass # TODO: fix compiler bug - # This trashes the loop variables, just like in Python. - ".."|for i,value in ipairs(\%iterable as lua\) do - | vars[''] = value - | \(lua expr "vars.body.value") as lua\ - | ::continue_for:: - | ::continue_\nomsu "var_to_lua_identifier" [%]\:: - |end - |::break_for:: - |::break_\nomsu "var_to_lua_identifier" [%]\:: + | \(%var) = value + | \(lua expr "vars.body.value") + | ::continue_for:: + | ::continue_\(nomsu "var_to_lua_identifier" [%var]):: + |end + |::break_for:: + |::break_\(nomsu "var_to_lua_identifier" [%var]):: +parse (for all %iterable %body) as: for % in %iterable %body # Switch statement/multi-branch if -macro block [when %body] =: +parse (when %body) as lua block: %result =: "" %fallthroughs =: [] for %statement in (lua expr "vars.body.value.value"): @@ -156,15 +118,18 @@ macro block [when %body] =: go to next %statement if (lua expr "vars.condition.type == 'Word' and vars.condition.value == 'else'"): - %result join=: ".."| + %result join=: ".." + | |do ..else: %condition =: %condition as lua - for all %fallthroughs: %condition join=: ".."| or \% as lua\ - %result join=: ".."| - |if \%condition\ then - %result join=: ".."| - | \(lua expr "vars.thunk.value") as lua\ + for all %fallthroughs: %condition join= " or \(%)" + %result join=: ".." + | + |if \(%condition) then + %result join=: ".." + | + | \(lua expr "vars.thunk.value") | goto finished_when |end @@ -174,8 +139,8 @@ macro block [when %body] =: %result # Switch statement -macro block [when %branch-value == ? %body] =: - %result =: ".."|local branch_value = \%branch-value as lua\ +parse (when %branch-value == ? %body) as lua block: + %result =: "local branch_value = \(%branch-value)" %fallthroughs =: [] for %statement in (lua expr "vars.body.value.value"): %func-call =: lua expr "vars.statement.value" @@ -197,15 +162,18 @@ macro block [when %branch-value == ? %body] =: go to next %statement if (lua expr "vars.condition.type == 'Word' and vars.condition.value == 'else'"): - %result join=: ".."| + %result join=: ".." + | |do ..else: - %condition =: ".."|branch_value == (\%condition as lua\) - for all %fallthroughs: %condition join=: ".."| or (branch_value == \% as lua\) - %result join=: ".."| + %condition =: "branch_value == (\(%condition))" + for all %fallthroughs: %condition join= " or (branch_value == \(%))" + %result join=: ".." + | |if \%condition\ then - %result join=: ".."| - | \(lua expr "vars.thunk.value") as lua\ + %result join=: ".." + | + | \((lua expr "vars.thunk.value")) | goto finished_when |end @@ -213,4 +181,3 @@ macro block [when %branch-value == ? %body] =: %result join=: "\n::finished_when::" %result - diff --git a/lib/metaprogramming.nom b/lib/metaprogramming.nom index e60a235..301d74b 100644 --- a/lib/metaprogramming.nom +++ b/lib/metaprogramming.nom @@ -27,7 +27,6 @@ lua code ".." |end |nomsu:def("parse nomsu %shorthand as nomsu %longhand", parse_as) parse nomsu \(parse %shorthand as %longhand) as nomsu \(parse nomsu \%shorthand as nomsu \%longhand) -parse (foo %x) as (baz %x) lua code ".." |nomsu:defmacro("lua expr %code", function(nomsu, vars) @@ -71,17 +70,12 @@ parse (lua block %block) as: parse (nomsu) as: lua expr "nomsu" parse (nomsu's %key) as: - lua expr "nomsu[\(%key as lua)]" + lua expr "nomsu['\(%key)']" parse (nomsu %method %args) as: - lua block ".." - |local args = {"nomsu"} - |for _,arg in ipairs(vars.args.value) do - | table.insert(args, nomsu:tree_to_lua(arg)) - |end - |local method_name = nomsu:repr(nomsu:tree_to_value(vars.method, vars)) - ..with value ".." - |("nomsu[%s](%s)"):format(method_name, table.concat(args, ", ")) + lua expr "nomsu['\(%method)'](nomsu, unpack(\(%args)))" parse (repr %) as: nomsu "repr" [%] +repr 5 + # Get the source code for a function rule (help %rule) =: @@ -111,3 +105,18 @@ parse (source code %body) as: source code from tree \%body parse (parse tree %code) as: repr (nomsu "tree_to_str" [\%code]) +parse (parse %code as lua code %lua) as: + parse nomsu \%code as nomsu: + nomsu "replaced_vars" [\(lua code %lua), lua expr "vars"] + +parse (parse %code as lua expr %lua) as: + parse nomsu \%code as nomsu: + nomsu "replaced_vars" [\(lua expr %lua), lua expr "vars"] + +parse (parse %code as lua block %lua) as: + parse nomsu \%code as nomsu: + nomsu "replaced_vars" [\(lua block %lua), lua expr "vars"] + +parse (pass) as lua code: "" + +pass diff --git a/lib/operators.nom b/lib/operators.nom index f2bb7c2..4e7bd59 100644 --- a/lib/operators.nom +++ b/lib/operators.nom @@ -1,25 +1,25 @@ require "lib/metaprogramming.nom" # Literals -macro [true, yes] =: "true" -macro [false, no] =: "false" -macro [nil, null] =: "nil" -macro [inf, infinity] =: "math.huge" -macro [nan, NaN, not a number] =: "(0/0)" -macro statement [nop, pass] =: "" +parse (true; yes) as lua expr "true" +parse (false; no) as lua expr "false" +parse (nil; null) as lua expr "nil" +parse (inf; infinity) as lua expr "math.huge" +parse (nan; NaN; not a number) as lua expr "(0/0)" +parse (nop; pass) as lua code "" # Ternary operator -macro [%if_expr if %condition else %else_expr] =: - pass # TODO: Fix compiler bug that doesn't parse right here +parse (%if_expr if %condition else %else_expr) as lua expr ".." + |(function(nomsu, vars) + # TODO: fix compiler bug that breaks this code if comments immediately follow ".." #.. Note: this uses a function instead of (condition and if_expr or else_expr) because that breaks if %if_expr is falsey. - ".."|(function(nomsu, vars) - | if \%condition as lua\ then - | return \%if_expr as lua\ - | else - | return \%else_expr as lua\ - | end - |end)(nomsu, vars) + | if \(%condition) then + | return \(%if_expr) + | else + | return \(%else_expr) + | end + |end)(nomsu, vars) # Variable assignment operator, and += type versions lua block ".." @@ -64,11 +64,11 @@ lua block ".." | end | nomsu:defmacro("%a "..nomsu_alias.." %b", (function(nomsu, vars, kind) | return "("..nomsu:tree_to_lua(vars.a).." "..op.." "..nomsu:tree_to_lua(vars.b)..")" - | end), [[".."|(\\%a as lua\\ ]]..op..[[ \\%b as lua\\)]]) + | end), [["(\\(%a) ]]..op..[[ \\(%b))"]]) |end # == and != do equivalence checking, rather than identity checking -macro [%a == %b] =: ".."|nomsu.utils.equivalent(\%a as lua\, \%b as lua\) -macro [%a != %b] =: ".."|(not nomsu.utils.equivalent(\%a as lua\, \%b as lua\)) +parse (%a == %b) as lua expr "nomsu.utils.equivalent(\(%a), \(%b))" +parse (%a != %b) as lua expr "(not nomsu.utils.equivalent(\(%a), \(%b)))" # Commutative Operators defined for up to 8 operands # TODO: work out solution for commutative operators using more clever macros @@ -125,5 +125,5 @@ lua block ".." |end # Unary operators -macro [- %a] =: ".."|-(\%a as lua\) -macro [not %a] =: ".."|not (\%a as lua\) +parse (- %a) as lua expr "-(\(%a))" +parse (not %a) as lua expr "not (\(%a))" diff --git a/lib/utils.nom b/lib/utils.nom index 1afc33a..11596f1 100644 --- a/lib/utils.nom +++ b/lib/utils.nom @@ -1,73 +1,66 @@ require "lib/metaprogramming.nom" # Error functions -rule [error!, panic!, fail!, abort!] =: - nomsu "error"[] -rule [error %msg] =: +rule (error!; panic!; fail!; abort!) =: + nomsu "error" [] +rule (error %msg) =: nomsu "error"[%msg] -macro statement [assert %condition] =: ".." - |if not (\%condition as lua\) then +parse (assert %condition) as: lua code ".." + |if not (\(%condition)) then | nomsu:error() |end -macro statement [assert %condition %msg] =: ".." - |if not (\%condition as lua\) then - | nomsu:error(\%msg as lua\) +parse (assert %condition %msg) as: lua code ".." + |if not (\(%condition)) then + | nomsu:error(\(%msg)) |end -macro statement [show generated lua %block] =: ".." - |nomsu:writeln(\lua expr "nomsu:repr(nomsu:tree_to_lua(vars.block.value))"\) +parse (show generated lua %block) as: lua code ".." + |nomsu:writeln(\(repr (nomsu "tree_to_lua" [%block]))) # String functions -rule [join %strs] =: +rule (join %strs) =: lua block ".." |local str_bits = {} |for i,bit in ipairs(vars.strs) do str_bits[i] = nomsu.utils.repr_if_not_string(bit) end |return table.concat(str_bits) -rule [join %strs with glue %glue] =: +rule (join %strs with glue %glue) =: lua block ".." |local str_bits = {} |for i,bit in ipairs(vars.strs) do str_bits[i] = nomsu.utils.repr_if_not_string(bit) end |return table.concat(str_bits, vars.glue) -macro [capitalize %str, %str capitalized] =: ".." - |(\%str as lua\):gsub("%l", string.upper, 1) +parse (capitalize %str; %str capitalized) as: lua expr "(\(%str)):gsub('%l', string.upper, 1)" -macro [repr %obj] =: - ".."|nomsu:repr(\%obj as lua\) - -macro [%obj as string] =: - ".."|nomsu.utils.repr_if_not_string(\%obj as lua\) - -macro [say %str] =: - ".."|nomsu:writeln(nomsu.utils.repr_if_not_string(\%str as lua\)) +parse (say %str) as: lua block ".." + |nomsu:writeln(nomsu.utils.repr_if_not_string(\(%str))) # Number ranges -macro [%start to %stop] =: ".." - |nomsu.utils.range(\%start as lua\, \%stop as lua\) -macro [%start to %stop by %step, %start to %stop via %step] =: ".." - |nomsu.utils.range(\%start as lua\, \%stop as lua\, \%step as lua\) +parse (%start to %stop) as: lua expr ".." + |nomsu.utils.range(\(%start), \(%stop)) +parse (%start to %stop by %step; %start to %stop via %step) as: lua expr ".." + |nomsu.utils.range(\(%start), \(%stop), \(%step)) # Common utility functions -macro [random number, random, rand] =: "math.random()" -macro [random int %n, random integer %n, randint %n] =: ".."|math.random(\%n as lua\) -macro [random from %low to %high, random number from %low to %high, rand %low %high] =: ".." - |math.random(\%low as lua\, \%high as lua\) -rule [random choice from %elements, random choice %elements, random %elements] =: - lua expr ".."|vars.elements[math.random(#vars.elements)] -macro [sum of %items, sum %items] =: ".."|nomsu.utils.sum(\%items as lua\) -macro [product of %items, product %items] =: ".."|nomsu.utils.product(\%items as lua\) -macro [all of %items] =: ".."|nomsu.utils.all(\%items as lua\) -macro [any of %items] =: ".."|nomsu.utils.any(\%items as lua\) +parse (random number; random; rand) as: lua expr "math.random()" +parse (random int %n; random integer %n; randint %n) as: lua expr "math.random(\(%n))" +parse (random from %low to %high; random number from %low to %high; rand %low %high) as: + lua expr "math.random(\(%low), \(%high))" +rule (random choice from %elements; random choice %elements; random %elements) =: + lua expr "vars.elements[math.random(#vars.elements)]" +parse (sum of %items; sum %items) as: lua expr "nomsu.utils.sum(\(%items))" +parse (product of %items; product %items) as: lua expr "nomsu.utils.product(\(%items))" +parse (all of %items) as: lua expr "nomsu.utils.all(\(%items))" +parse (any of %items) as: lua expr "nomsu.utils.any(\(%items))" # This is a rule, not a macro so we can use vars.items twice without running it twice. -rule [avg of %items, average of %items] =: - lua expr ".."|(nomsu.utils.sum(vars.items)/#vars.items) -macro [min of %items, smallest of %items, lowest of %items] =: - ".."|nomsu.utils.min(\%items as lua\) -macro [max of %items, biggest of %items, largest of %items, highest of %items] =: - ".."|nomsu.utils.min(\%items as lua\) -macro [min of %items with respect to %keys] =: - ".."|nomsu.utils.min(\%items as lua\, \%keys as lua\) -macro [max of %items with respect to %keys] =: - ".."|nomsu.utils.max(\%items as lua\, \%keys as lua\) +rule (avg of %items; average of %items) =: + lua expr "(nomsu.utils.sum(vars.items)/#vars.items)" +parse (min of %items; smallest of %items; lowest of %items) as: + lua expr "nomsu.utils.min(\(%items))" +parse (max of %items; biggest of %items; largest of %items; highest of %items) as: + lua expr "nomsu.utils.max(\(%items))" +parse (min of %items with respect to %keys) as: + lua expr "nomsu.utils.min(\(%items), \(%keys))" +parse (max of %items with respect to %keys) as: + lua expr "nomsu.utils.max(\(%items), \(%keys))" diff --git a/nomsu.lua b/nomsu.lua index 9d79c3a..8bfb83e 100644 --- a/nomsu.lua +++ b/nomsu.lua @@ -7,10 +7,6 @@ do local _obj_0 = table insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat end -local pcall -pcall = function(fn, ...) - return true, fn(...) -end lpeg.setmaxstack(10000) local P, V, S, Cg, C, Cp, B, Cmt P, V, S, Cg, C, Cp, B, Cmt = lpeg.P, lpeg.V, lpeg.S, lpeg.Cg, lpeg.C, lpeg.Cp, lpeg.B, lpeg.Cmt @@ -65,13 +61,13 @@ local nomsu = [=[ file <- ({ {| shebang? inline_block <- ({ {| "(" inline_statements ")" |} }) -> Block eol_block <- ({ {| ":" %ws? noeol_statements eol |} }) -> Block indented_block <- ({ {| (":" / "(..)") indent - statements + statements (nodent statements)* (dedent / (({.+} ("" -> "Error while parsing block")) => error)) |} }) -> Block - inline_nomsu <- ({ ("\" inline_block ) }) -> Nomsu - eol_nomsu <- ({ ("\" eol_block ) }) -> Nomsu - indented_nomsu <- ({ ("\" {indented_block} ) }) -> Nomsu + inline_nomsu <- ({ ("\" inline_expression) }) -> Nomsu + eol_nomsu <- ({ ("\" noeol_expression) }) -> Nomsu + indented_nomsu <- ({ ("\" expression) }) -> Nomsu inline_expression <- number / variable / inline_string / inline_list / inline_block / inline_nomsu noeol_expression <- indented_string / indented_block / indented_nomsu / indented_list / inline_expression @@ -192,27 +188,15 @@ do return self:write("\n") end, def = function(self, invocation, thunk, src) - if type(invocation) ~= 'string' then - self:error("Invocation should be string, not: " .. tostring(repr(invocation))) - end + local stub, arg_names = self:get_stub(invocation) + assert(stub, "NO STUB FOUND: " .. tostring(repr(invocation))) if self.debug then - self:writeln("Defining rule: " .. tostring(repr(invocation))) + self:writeln("Defining rule: " .. tostring(repr(stub)) .. " with args " .. tostring(repr(arg_names))) end - local stub = invocation:gsub("'", " '"):gsub("%%%S+", "%%"):gsub("%s+", " ") - local args - do - local _accum_0 = { } - local _len_0 = 1 - for arg in invocation:gmatch("%%(%S[^%s']*)") do - _accum_0[_len_0] = arg - _len_0 = _len_0 + 1 - end - args = _accum_0 - end - for i = 1, #args - 1 do - for j = i + 1, #args do - if args[i] == args[j] then - self:error("Duplicate argument in function def: " .. tostring(args[i])) + for i = 1, #arg_names - 1 do + for j = i + 1, #arg_names do + if arg_names[i] == arg_names[j] then + self:error("Duplicate argument in function " .. tostring(stub) .. ": '" .. tostring(arg_names[i]) .. "'") end end end @@ -220,11 +204,11 @@ do local _with_0 = { thunk = thunk, invocation = invocation, - args = args, + arg_names = arg_names, src = src, is_macro = false } - self.defs[invocation] = _with_0 + self.defs[stub] = _with_0 local _ = nil return _with_0 end @@ -236,30 +220,31 @@ do return _with_0 end end, - call = function(self, alias, ...) - local def = self.defs[alias] + call = function(self, stub, ...) + local def = self.defs[stub] if def == nil then - self:error("Attempt to call undefined function: " .. tostring(alias)) + self:error("Attempt to call undefined function: " .. tostring(stub)) end if def.is_macro and self.callstack[#self.callstack] ~= "#macro" then - self:error("Attempt to call macro at runtime: " .. tostring(alias) .. "\nThis can be caused by using a macro in a function that is defined before the macro.") + self:error("Attempt to call macro at runtime: " .. tostring(stub) .. "\nThis can be caused by using a macro in a function that is defined before the macro.") end if not (self:check_permission(def)) then - self:error("You do not have the authority to call: " .. tostring(alias)) + self:error("You do not have the authority to call: " .. tostring(stub)) end - local thunk, args - thunk, args = def.thunk, def.args + local thunk, arg_names + thunk, arg_names = def.thunk, def.arg_names + local args do local _tbl_0 = { } - for i, name in ipairs(args) do + for i, name in ipairs(arg_names) do _tbl_0[name] = select(i, ...) end args = _tbl_0 end if self.debug then - self:writeln("Calling " .. tostring(repr(alias)) .. " with args: " .. tostring(repr(args))) + self:writeln("Calling " .. tostring(repr(stub)) .. " with args: " .. tostring(repr(args))) end - insert(self.callstack, alias) + insert(self.callstack, stub) local rets = { thunk(self, args) } @@ -270,10 +255,9 @@ do if kind == nil then kind = "Expression" end - local args, alias - alias, args = self:get_alias(tree) + local stub, args = self:get_stub(tree) insert(self.callstack, "#macro") - local expr, statement = self:call(alias, unpack(args)) + local expr, statement = self:call(stub, unpack(args)) remove(self.callstack) return expr, statement end, @@ -319,13 +303,17 @@ do run = function(self, src, filename) local tree = self:parse(src, filename) assert(tree, "Tree failed to compile: " .. tostring(src)) - assert(tree.type == "File") + assert(tree.type == "File", "Attempt to run non-file: " .. tostring(tree.type)) local buffer = { } local vars = { } local return_value = nil local _list_0 = tree.value for _index_0 = 1, #_list_0 do local statement = _list_0[_index_0] + if self.debug then + self:writeln("RUNNING TREE:") + self:print_tree(statement) + end local ok, expr, statements = pcall(self.tree_to_lua, self, statement) if not ok then self:writeln("Error occurred in statement:\n" .. tostring(statement.src)) @@ -350,7 +338,7 @@ do end if not ok then self:writeln("Error occurred in statement:\n" .. tostring(statement.src)) - self:error(return_value) + self:error(repr(return_value)) end insert(buffer, tostring(statements or '') .. "\n" .. tostring(expr and "ret = " .. tostring(expr) or '')) end @@ -372,6 +360,7 @@ do tree_to_lua = function(self, tree) assert(tree, "No tree provided.") if not tree.type then + self:writeln(debug.traceback()) self:error("Invalid tree: " .. tostring(repr(tree))) end local _exp_0 = tree.type @@ -379,15 +368,12 @@ do return error("Should not be converting File to lua through this function.") elseif "Nomsu" == _exp_0 then return repr(tree.value), nil - elseif "Block" == _exp_0 then + elseif "Thunk" == _exp_0 then local lua_bits = { } - local _list_0 = tree.value + local _list_0 = tree.value.value for _index_0 = 1, #_list_0 do local arg = _list_0[_index_0] local expr, statement = self:tree_to_lua(arg) - if expr and not statement and #tree.value == 1 then - return expr, nil - end if statement then insert(lua_bits, statement) end @@ -395,18 +381,34 @@ do insert(lua_bits, "ret = " .. tostring(expr)) end end - return ([[ function(nomsu, vars) + return ([[ (function(nomsu, vars) local ret %s return ret - end]]):format(concat(lua_bits, "\n")) + end)]]):format(concat(lua_bits, "\n")) + elseif "Block" == _exp_0 then + if #tree.value == 0 then + return "nil", nil + end + if #tree.value == 1 then + local expr, statement = self:tree_to_lua(tree.value[1]) + if not statement then + return expr, nil + end + end + local thunk_lua = self:tree_to_lua({ + type = "Thunk", + value = tree, + src = tree.src + }) + return ("%s(nomsu, vars)"):format(thunk_lua), nil elseif "FunctionCall" == _exp_0 then - local alias = self:get_alias(tree) - if self.defs[alias] and self.defs[alias].is_macro then + local stub = self:get_stub(tree) + if self.defs[stub] and self.defs[stub].is_macro then return self:run_macro(tree, "Expression") end local args = { - repr(alias) + repr(stub) } local _list_0 = tree.value for _index_0 = 1, #_list_0 do @@ -460,6 +462,9 @@ do if string_buffer ~= "" then insert(concat_parts, repr(string_buffer)) end + if #concat_parts == 0 then + return "''", nil + end return "(" .. tostring(concat(concat_parts, "..")) .. ")", nil elseif "List" == _exp_0 then local items = { } @@ -481,25 +486,49 @@ do return self:error("Unknown/unimplemented thingy: " .. tostring(tree.type)) end end, - print_tree = function(self, tree, ind) - if ind == nil then - ind = "" + walk_tree = function(self, tree, depth) + if depth == nil then + depth = 0 end + coroutine.yield(tree, depth) if type(tree) ~= 'table' or not tree.type then - self:writeln(tostring(ind) .. tostring(repr(tree))) return end - self:writeln(tostring(ind) .. tostring(tree.type) .. ":") local _exp_0 = tree.type - if "List" == _exp_0 or "File" == _exp_0 or "Block" == _exp_0 or "FunctionCall" == _exp_0 or "String" == _exp_0 then + if "List" == _exp_0 or "File" == _exp_0 or "Nomsu" == _exp_0 or "Block" == _exp_0 or "FunctionCall" == _exp_0 or "String" == _exp_0 then local _list_0 = tree.value for _index_0 = 1, #_list_0 do local v = _list_0[_index_0] - self:print_tree(v, ind .. " ") + self:walk_tree(v, depth + 1) end else - return self:print_tree(tree.value, ind .. " ") + self:walk_tree(tree.value, depth + 1) end + return nil + end, + print_tree = function(self, tree) + for node, depth in coroutine.wrap(function() + return self:walk_tree(tree) + end) do + if type(node) ~= 'table' or not node.type then + self:writeln((" "):rep(depth) .. repr(node)) + else + self:writeln(tostring((" "):rep(depth)) .. tostring(node.type) .. ":") + end + end + end, + tree_to_str = function(self, tree) + local bits = { } + 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 + insert(bits, (tostring((" "):rep(depth)) .. tostring(node.type) .. ":")) + end + end + return concat(bits, "\n") end, replaced_vars = function(self, tree, vars) if type(tree) ~= 'table' then @@ -510,8 +539,8 @@ do if vars[tree.value] then tree = vars[tree.value] end - elseif "File" == _exp_0 or "Thunk" == _exp_0 or "Statement" == _exp_0 or "Block" == _exp_0 or "List" == _exp_0 or "FunctionCall" == _exp_0 or "String" == _exp_0 then - local new_value = self:replaced_vars(tree.value) + elseif "File" == _exp_0 or "Nomsu" == _exp_0 or "Thunk" == _exp_0 or "Block" == _exp_0 or "List" == _exp_0 or "FunctionCall" == _exp_0 or "String" == _exp_0 then + local new_value = self:replaced_vars(tree.value, vars) if new_value ~= tree.value then do local _tbl_0 = { } @@ -526,7 +555,7 @@ do local new_values = { } local any_different = false for k, v in pairs(tree) do - new_values[k] = self:replaced_vars(v) + new_values[k] = self:replaced_vars(v, vars) any_different = any_different or (new_values[k] ~= tree[k]) end if any_different then @@ -535,84 +564,47 @@ do end return tree end, - get_alias = function(self, x) + get_stub = function(self, x) if not x then - self:error("Nothing to get alias from") + self:error("Nothing to get stub from") end if type(x) == 'string' then - local alias = x:gsub("'", " '"):gsub("%%%S+", "%%"):gsub("%s+", " ") + local stub = x:gsub("'", " '"):gsub("%%%S+", "%%"):gsub("%s+", " ") local args do local _accum_0 = { } local _len_0 = 1 - for arg in x:gmatch("%%(%S[^%s']*)") do + for arg in x:gmatch("%%([^%s']*)") do _accum_0[_len_0] = arg _len_0 = _len_0 + 1 end args = _accum_0 end - return alias, args + return stub, args end local _exp_0 = x.type if "String" == _exp_0 then - return self:get_alias(x.value) - elseif "Statement" == _exp_0 then - return self:get_alias(x.value) + return self:get_stub(x.value) elseif "FunctionCall" == _exp_0 then - local alias, args = { }, { }, { } + local stub, args = { }, { }, { } local _list_0 = x.value for _index_0 = 1, #_list_0 do local token = _list_0[_index_0] local _exp_1 = token.type if "Word" == _exp_1 then - insert(alias, token.value) + insert(stub, token.value) elseif "Var" == _exp_1 then - insert(alias, "%") + insert(stub, "%") insert(args, token.value) else - insert(alias, "%") + insert(stub, "%") insert(args, token) end end - return concat(alias, " "), args - end - end, - get_aliases = function(self, x) - if not x then - self:error("Nothing to get aliases from") - end - if type(x) == 'string' then - local alias, args = self:get_alias(x) - return { - [alias] = args - } - end - local _exp_0 = x.type - if "String" == _exp_0 then - return self:get_aliases({ - x.value - }) - elseif "Statement" == _exp_0 then - return self:get_aliases({ - x.value - }) - elseif "FunctionCall" == _exp_0 then - return self:get_aliases({ - x - }) - elseif "List" == _exp_0 then - x = x.value + return concat(stub, " "), args elseif "Block" == _exp_0 then - x = x.value - end - do - local _with_0 = { } - for _index_0 = 1, #x do - local y = x[_index_0] - local alias, args = self:get_alias(y) - _with_0[alias] = args - end - return _with_0 + self:writeln(debug.traceback()) + return self:error("Please pass in a single line from a block, not the whole thing:\n" .. tostring(self:tree_to_str(x))) end end, var_to_lua_identifier = function(self, var) @@ -629,7 +621,9 @@ do end, error = function(self, ...) self:writeln("ERROR!") - self:writeln(...) + if select(1, ...) then + self:writeln(...) + end self:writeln("Callstack:") for i = #self.callstack, 1, -1 do self:writeln(" " .. tostring(self.callstack[i])) @@ -639,17 +633,22 @@ do return error() end, initialize_core = function(self) - self:defmacro("lua code %statements with value %value", function(self, vars) - local inner_vars = setmetatable({ }, { - __index = function(_, key) - return "vars[" .. tostring(repr(key)) .. "]" - end - }) - local statements = self:tree_to_value(vars.statements, inner_vars) - local value = self:tree_to_value(vars.value, inner_vars) - return value, statements - end) - self:def("require %filename", function(self, vars) + local lua_code + lua_code = function(self, vars) + local inner_vars = vars + local lua = self:tree_to_value(vars.code, inner_vars) + return nil, lua + end + self:defmacro("lua code %code", lua_code) + local lua_value + lua_value = function(self, vars) + local inner_vars = vars + local lua = self:tree_to_value(vars.code, inner_vars) + return lua, nil + end + self:defmacro("lua value %code", lua_value) + local _require + _require = function(self, vars) if not self.loaded_files[vars.filename] then local file = io.open(vars.filename) if not file then @@ -658,14 +657,17 @@ do self.loaded_files[vars.filename] = (self:run(file:read('*a'), vars.filename)) or true end return self.loaded_files[vars.filename] - end) - return self:def("run file %filename", function(self, vars) + end + self:def("require %filename", _require) + local run_file + run_file = function(self, vars) local file = io.open(vars.filename) if not file then self:error("File does not exist: " .. tostring(vars.filename)) end return self:run(file:read('*a'), vars.filename) - end) + end + return self:def("run file %filename", run_file) end } _base_0.__index = _base_0 @@ -726,7 +728,6 @@ do end if arg and arg[1] then local c = NomsuCompiler() - c.debug = true local input = io.open(arg[1]):read("*a") local _write = c.write if arg[2] == "-" then diff --git a/nomsu.moon b/nomsu.moon index 497bda1..9ba3856 100755 --- a/nomsu.moon +++ b/nomsu.moon @@ -70,7 +70,7 @@ nomsu = [=[ inline_block <- ({ {| "(" inline_statements ")" |} }) -> Block eol_block <- ({ {| ":" %ws? noeol_statements eol |} }) -> Block indented_block <- ({ {| (":" / "(..)") indent - statements + statements (nodent statements)* (dedent / (({.+} ("" -> "Error while parsing block")) => error)) |} }) -> Block @@ -329,6 +329,8 @@ class NomsuCompiler end)]])\format(concat lua_bits, "\n") when "Block" + if #tree.value == 0 + return "nil",nil if #tree.value == 1 expr,statement = @tree_to_lua tree.value[1] if not statement @@ -367,6 +369,8 @@ class NomsuCompiler if string_buffer ~= "" insert concat_parts, repr(string_buffer) + if #concat_parts == 0 + return "''", nil return "(#{concat(concat_parts, "..")})", nil when "List" @@ -502,13 +506,13 @@ class NomsuCompiler initialize_core: => -- Sets up some core functionality lua_code = (vars)=> - inner_vars = setmetatable({}, {__index:(_,key)-> "vars[#{repr(key)}]"}) + inner_vars = vars-- setmetatable({}, {__index:(_,key)-> "vars[#{repr(key)}]"}) lua = @tree_to_value(vars.code, inner_vars) return nil, lua @defmacro "lua code %code", lua_code lua_value = (vars)=> - inner_vars = setmetatable({}, {__index:(_,key)-> "vars[#{repr(key)}]"}) + inner_vars = vars--setmetatable({}, {__index:(_,key)-> "vars[#{repr(key)}]"}) lua = @tree_to_value(vars.code, inner_vars) return lua, nil @defmacro "lua value %code", lua_value diff --git a/utils.lua b/utils.lua index 84a0b54..2f6f25a 100644 --- a/utils.lua +++ b/utils.lua @@ -1,6 +1,9 @@ local utils utils = { is_list = function(t) + if type(t) ~= 'table' then + return false + end local i = 1 for _ in pairs(t) do if t[i] == nil then