From 6d11354b3f7226a9a417ae89f53de7a225434647 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 23 Jan 2018 19:22:20 -0800 Subject: [PATCH] Added implicit local declarations and new syntax of %var <- %value. --- lib/class.nom | 9 +- lib/collections.nom | 6 +- lib/control_flow.nom | 458 +++++++++++++++++++++------------------- lib/metaprogramming.nom | 22 +- lib/operators.nom | 154 ++++++++------ nomsu.lua | 78 +++++-- nomsu.moon | 51 +++-- 7 files changed, 442 insertions(+), 336 deletions(-) diff --git a/lib/class.nom b/lib/class.nom index 06f55d8..b871798 100644 --- a/lib/class.nom +++ b/lib/class.nom @@ -12,7 +12,7 @@ compile [@%var] to end return "_me["..key_lua.."]"; -compile [set @%var = %val] to code +compile [@%var <- %val] to code lua> ".." local val_lua = \(%val as lua); local key_lua = repr(\%var.value); @@ -26,11 +26,10 @@ compile [set @%var = %val] to code return "_me["..key_lua.."] = "..val_lua..";"; compile [object %classname %class_body] to - local [%methods, %class_identifier] - set %class_identifier = (=lua "nomsu:var_to_lua_identifier(\(%classname as value)):sub(2,-1)") + %class_identifier <- (=lua "nomsu:var_to_lua_identifier(\(%classname as value)):sub(2,-1)") if: %class_identifier is "" - set %class_identifier = "class" - set %methods = [] + %class_identifier <- "class" + %methods <- [] for %line in (%class_body's "value") if: (%line's "type") is "Comment" do next %line diff --git a/lib/collections.nom b/lib/collections.nom index 90bd08c..10dae75 100644 --- a/lib/collections.nom +++ b/lib/collections.nom @@ -11,7 +11,7 @@ use "lib/operators.nom" # Indexing parse [..] %index st in %list, %index nd in %list, %index rd in %list - %index th in %list, %index in %list + %index th in %list ..as: %list -> %index compile [..] %index st to last in %list, %index nd to last in %list, %index rd to last in %list @@ -51,7 +51,7 @@ compile [length of %list, size of %list, size %list, number of %list, len %list] compile [%list ->* %indices] to assume ((%indices's "type") is "List") or barf ".." Expected List for chained lookup, not \(%indices's "type") - set %ret = "\(%list as lua)" + %ret <- "\(%list as lua)" for %index in (%indices's "value") %ret join= "[\(%index as lua)]" return "\%ret" @@ -75,7 +75,7 @@ compile [remove index %index from %list] to action [flatten %lists] - set %flat = [] + %flat <- [] for %list in %lists for %item in %list add %item to %flat diff --git a/lib/control_flow.nom b/lib/control_flow.nom index b05862c..7347a29 100644 --- a/lib/control_flow.nom +++ b/lib/control_flow.nom @@ -40,10 +40,9 @@ immediately %when_false_expr unless %condition else %when_true_expr %when_false_expr unless %condition then %when_true_expr ..to - local %safe #.. If %when_true_expr is guaranteed to be truthy, we can use Lua's idiomatic equivalent of a conditional expression: (cond and if_true or if_false) - if: {Text:yes, List:yes, Dict:yes, Number:yes}->(%when_true_expr's "type") + if: (%when_true_expr's "type") in {Text:yes, List:yes, Dict:yes, Number:yes} return "(\(%condition as lua) and \(%when_true_expr as lua) or \(%when_false_expr as lua))" ..else #.. Otherwise, need to do an anonymous inline function (yuck, too bad lua @@ -58,9 +57,10 @@ immediately end end)() + # GOTOs immediately - compile [-> %label] to code ".." + compile [=== %label ===, --- %label ---] to code ".." ::label_\(nomsu "var_to_lua_identifier" [%label])::; compile [go to %label] to code ".." goto label_\(nomsu "var_to_lua_identifier" [%label]); @@ -72,46 +72,77 @@ immediately # Helper function immediately - action [tree %tree has function call %call] - lua> ".." - local target = (\%call).stub; - for subtree,depth in coroutine.wrap(function() nomsu:walk_tree(\%tree); end) do - if type(subtree) == 'table' and subtree.type == "FunctionCall" - and subtree.stub == target then - return true; + compile [for subtree %subtree where %condition in %tree %body] to code ".." + for \(%subtree as lua) in coroutine.wrap(function() nomsu:walk_tree(\(%tree as lua)) end) do + if type(\(%subtree as lua)) == 'table' and \(%subtree as lua).type then + if \(%condition as lua) then + \(%body as lua statements) end end - return false; + end # While loops immediately - compile [do next repeat-loop] to code "goto continue_repeat;" - compile [stop repeat-loop] to code "goto stop_repeat;" + compile [do next repetition] to code "goto continue_repeat;" + compile [stop repeating] to code "goto stop_repeat;" compile [repeat while %condition %body] to code - set %continue_labels = - "\n::continue_repeat::;" - ..if (tree %body has function call \(do next repeat-loop)) else "" - set %code = ".." + %continue_labels <- "" + for subtree % where + ((%'s "type") = "FunctionCall") and ((%'s "stub") == "do next repetition") + ..in %body + %continue_labels <- "\n::continue_repeat::;" + stop + %code <- ".." while \(%condition as lua) do \(%body as lua statements)\ ..\%continue_labels end --while-loop - if: tree %body has function call \(stop repeat-loop) - return ".." - do --while-loop label scope + for subtree % where + ((%'s "type") = "FunctionCall") and ((%'s "stub") == "stop repeating") + ..in %body + %code <- ".." + do -- scope of "stop repeating" label \%code ::stop_repeat::; - end --while-loop label scope + end -- end of "stop repeating" label scope + %continue_labels <- "\n::continue_repeat::;" + stop return %code parse [repeat %body] as: repeat while (yes) %body parse [repeat until %condition %body] as: repeat while (not %condition) %body + compile [..] + repeat %n times %body + ..to code + %continue_labels <- "" + for subtree % where + ((%'s "type") = "FunctionCall") and ((%'s "stub") == "do next repetition") + ..in %body + %continue_labels <- "\n::continue_repeat::;" + stop + # This trashes the loop variables, just like in Python. + %code <- ".." + for i=1,\(%n as lua) do + \(%body as lua statements)\ + ..\%continue_labels + end --numeric for-loop + %stop_labels <- "" + for subtree % where + ((%'s "type") = "FunctionCall") and ((%'s "stub") == "stop repeating") + ..in %body + %code <- ".." + do -- scope of "stop repeating" label + \%code + ::stop_repeat::; + end -- end of "stop repeating" label scope + %continue_labels <- "\n::continue_repeat::;" + stop + return %code + # For loop control flow: immediately - compile [stop for-loop] to code "goto stop_for;" compile [stop %var] to code ".." goto stop_\(nomsu "var_to_lua_identifier" [%var]); - compile [do next for-loop] to code "goto continue_for;" compile [do next %var] to code ".." goto continue_\(nomsu "var_to_lua_identifier" [%var]); @@ -121,37 +152,42 @@ immediately for %var from %start to %stop by %step %body for %var from %start to %stop via %step %body ..to code - local [%continue_labels, %code, %stop_labels, %loop_var, %loop_var_shim] - set %continue_labels = "" - if: tree %body has function call \(do next for-loop) - %continue_labels join= "\n::continue_for::;" - if: tree %body has function call (tree \(do next %) with {"":%var}) - %continue_labels join= "\n::continue_\(nomsu "var_to_lua_identifier" [%var])::;" + %continue_labels <- "" + for subtree % where + ((%'s "type") = "FunctionCall") and + ((%'s "stub") == "do next %") and + ((3rd in (%'s "value"))'s "src") == (%var's "src") + ..in %body + %continue_labels <- "\n::continue_\(nomsu "var_to_lua_identifier" [%var])::;" + stop + if: (%var's "type") is "Var" - set %loop_var = (%var as lua) - set %loop_var_shim = "" + %loop_var <- (%var as lua) + %loop_var_shim <- "" ..else - set %loop_var = "i" - set %loop_var_shim = "\n\(%var as lua) = i;" + %loop_var <- "i" + %loop_var_shim <- "\n\(%var as lua) = i;" # This trashes the loop variables, just like in Python. - set %code = ".." + %code <- ".." for \(%loop_var)=\(%start as lua),\(%stop as lua),\(%step as lua) do\ ..\%loop_var_shim \(%body as lua statements)\ ..\%continue_labels end --numeric for-loop - set %stop_labels = "" - if: tree %body has function call \(stop for-loop) - %stop_labels join= "\n::stop_for::;" - if: tree %body has function call (tree \(stop %) with {"":%var}) - %stop_labels join= "\n::stop_\(nomsu "var_to_lua_identifier" [%var])::;" - return - ".." - do --for-loop label scope - \%code\ - ..\%stop_labels - end --for-loop label scope - ..if (%stop_labels is not "") else %code + + for subtree % where + ((%'s "type") = "FunctionCall") and + ((%'s "stub") == "stop %") and + ((2nd in (%'s "value"))'s "src") == (%var's "src") + ..in %body + %code <- ".." + do -- scope for stopping for-loop + \%code + ::stop_\(nomsu "var_to_lua_identifier" [%var])::; + end -- end of scope for stopping for-loop + stop + + return %code immediately parse [for %var from %start to %stop %body] as: for %var from %start to %stop via 1 %body @@ -161,207 +197,209 @@ immediately ..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 +# For-each loop immediately compile [for %var in %iterable %body] to code - local [%continue_labels, %code, %stop_labels, %loop_var, %loop_var_shim] - set %continue_labels = "" - if: tree %body has function call \(do next for-loop) - %continue_labels join= "\n::continue_for::;" - if: tree %body has function call (tree \(do next %) with {"":%var}) - %continue_labels join= "\n::continue_\(nomsu "var_to_lua_identifier" [%var])::;" + %continue_labels <- "" + for subtree % where + ((%'s "type") = "FunctionCall") and + ((%'s "stub") == "do next %") and + ((3rd in (%'s "value"))'s "src") == (%var's "src") + ..in %body + %continue_labels <- "\n::continue_\(nomsu "var_to_lua_identifier" [%var])::;" + stop if: (%var's "type") is "Var" - set %loop_var = (%var as lua) - set %loop_var_shim = "" + %loop_var <- (%var as lua) + %loop_var_shim <- "" ..else - set %loop_var = "value" - set %loop_var_shim = "\n\(%var as lua) = value;" + %loop_var <- "value" + %loop_var_shim <- "\n\(%var as lua) = value;" # This trashes the loop variables, just like in Python. - set %code = ".." + %code <- ".." for i,\%loop_var in ipairs(\(%iterable as lua)) do\ ..\%loop_var_shim \(%body as lua statements)\ ..\%continue_labels end --foreach-loop - set %stop_labels = "" - if: tree %body has function call \(stop for-loop) - %stop_labels join= "\n::stop_for::;" - if: tree %body has function call (tree \(stop %) with {"":%var}) - %stop_labels join= "\n::stop_\(nomsu "var_to_lua_identifier" [%var])::;" - if: %stop_labels is not "" - set %code = ".." - do --for-loop label scope - \%code\%stop_labels - end --for-loop label scope + for subtree % where + ((%'s "type") = "FunctionCall") and + ((%'s "stub") == "stop %") and + ((2nd in (%'s "value"))'s "src") == (%var's "src") + ..in %body + %code <- ".." + do -- scope for stopping for-loop + \%code + ::stop_\(nomsu "var_to_lua_identifier" [%var])::; + end -- end of scope for stopping for-loop + stop return %code parse [for all %iterable %body] as: for % in %iterable %body -immediately - compile [..] - repeat %n times %body, repeat %n x %body - ..to code - local [%continue_labels, %code, %stop_labels] - set %continue_labels = "" - if: tree %body has function call \(do next repeat-loop) - %continue_labels join= "\n::continue_repeat::;" - # This trashes the loop variables, just like in Python. - set %code = ".." - for i=1,\(%n as lua) do - \(%body as lua statements)\ - ..\%continue_labels - end --numeric for-loop - set %stop_labels = "" - if: tree %body has function call \(stop repeat-loop) - %stop_labels join= "\n::stop_repeat::;" - return - ".." - do --repeat-loop label scope - \%code\ - ..\%stop_labels - end --repeat-loop label scope - ..if (%stop_labels is not "") else %code - # Dict iteration (lua's "pairs()") immediately compile [for %key = %value in %iterable %body] to code - local [..] - %continue_labels, %code, %stop_labels, %key_loop_var, %key_loop_var_shim, - %value_loop_var, %value_loop_var_shim - set %continue_labels = "" - if: tree %body has function call \(do next for-loop) - %continue_labels join= "\n::continue_for::;" - if: tree %body has function call (tree \(do next %) with {"":%key}) - %continue_labels join= "\n::continue_\(nomsu "var_to_lua_identifier" [%key])::;" - if: tree %body has function call (tree \(do next %) with {"":%value}) - %continue_labels join= "\n::continue_\(nomsu "var_to_lua_identifier" [%value])::;" + %continue_labels <- "" + for subtree % where + ((%'s "type") = "FunctionCall") and + ((%'s "stub") == "do next %") and + ((3rd in (%'s "value"))'s "src") == (%key's "src") + ..in %body + <- %continue_labels + "\n::continue_\(nomsu "var_to_lua_identifier" [%key])::;" + stop + + for subtree % where + ((%'s "type") = "FunctionCall") and + ((%'s "stub") == "do next %") and + ((3rd in (%'s "value"))'s "src") == (%value's "src") + ..in %body + <- %continue_labels + "\n::continue_\(nomsu "var_to_lua_identifier" [%value])::;" + stop + if: (%key's "type") is "Var" - set %key_loop_var = (%key as lua) - set %key_loop_var_shim = "" + %key_loop_var <- (%key as lua) + %key_loop_var_shim <- "" ..else - set %key_loop_var = "key" - set %key_loop_var_shim = "\n\(%key as lua) = key;" + %key_loop_var <- "key" + %key_loop_var_shim <- "\n\(%key as lua) = key;" if: (%value's "type") is "Var" - set %value_loop_var = (%value as lua) - set %value_loop_var_shim = "" + %value_loop_var <- (%value as lua) + %value_loop_var_shim <- "" ..else - set %value_loop_var = "value" - set %value_loop_var_shim = "\n\(%value as lua) = value;" + %value_loop_var <- "value" + %value_loop_var_shim <- "\n\(%value as lua) = value;" # This trashes the loop variables, just like in Python. - set %code = ".." + %code <- ".." for \%key_loop_var,\%value_loop_var in pairs(\(%iterable as lua)) do\ ..\%key_loop_var_shim\%value_loop_var_shim \(%body as lua statements)\ ..\%continue_labels end --foreach-loop - set %stop_labels = "" - if: tree %body has function call \(stop for-loop) - %stop_labels join= "\n::stop_for::;" - if: tree %body has function call (tree \(stop %) with {"":%key}) - %stop_labels join= "\n::stop_\(nomsu "var_to_lua_identifier" [%key])::;" - if: tree %body has function call (tree \(stop %) with {"":%value}) - %stop_labels join= "\n::stop_\(nomsu "var_to_lua_identifier" [%value])::;" - return - ".." - do --for-loop label scope - \%code\ - ..\%stop_labels - end --for-loop label scope - ..if (%stop_labels is not "") else %code + %stop_labels <- "" + for subtree % where + ((%'s "type") = "FunctionCall") and + ((%'s "stub") == "stop %") and + ((2nd in (%'s "value"))'s "src") == (%key's "src") + ..in %body + <- %stop_labels + "\n::stop_\(nomsu "var_to_lua_identifier" [%key])::;" + stop + + for subtree % where + ((%'s "type") = "FunctionCall") and + ((%'s "stub") == "stop %") and + ((2nd in (%'s "value"))'s "src") == (%value's "src") + ..in %body + <- %stop_labels + "\n::stop_\(nomsu "var_to_lua_identifier" [%value])::;" + stop + + if: %stop_labels is not "" + %code <- ".." + do -- scope for stopping for % = % loop + \%code\%stop_labels + end + return %code # Switch statement/multi-branch if immediately compile [when %body] to code - local [%result, %fallthroughs, %first] - set %result = "" - set %fallthroughs = [] - set %first = (yes) + %result <- "" + %fallthroughs <- [] + %is_first <- (yes) for %func_call in (%body's "value") - local [%tokens, %star, %condition, %action] assume ((%func_call's "type") is "FunctionCall") or barf ".." Invalid format for 'when' statement. Only '*' blocks are allowed. - set %tokens = (%func_call's "value") - set %star = (%tokens -> 1) - assume (=lua "\%star and \%star.type == 'Word' and \%star.value == '*'") or barf ".." - Invalid format for 'when' statement. Lines must begin with '*' + with [..] + %tokens <- (%func_call's "value") + %star <- (1st in %tokens) + %condition <- (2nd in %tokens) + %action <- (3rd in %tokens) + .. + assume (=lua "\%star and \%star.type == 'Word' and \%star.value == '*'") or barf ".." + Invalid format for 'when' statement. Lines must begin with '*' + assume %condition or barf ".." + Invalid format for 'when' statement. Lines must begin with '*' and have a condition or the word "else" + if: %action is (nil) + lua do> "table.insert(\%fallthroughs, \%condition)" + do next %func_call - set %condition = (%tokens -> 2) - assume %condition or barf ".." - Invalid format for 'when' statement. Lines must begin with '*' and have a condition or the word "else" + if: =lua "\%condition.type == 'Word' and \%condition.value == 'else'" - set %action = (%tokens -> 3) - if: %action is (nil) - lua do> "table.insert(\%fallthroughs, \%condition)" - do next %func_call + <- %result + ".." + + else + \(%action as lua statements) + stop + ..else + %condition <- (%condition as lua) + for all %fallthroughs + <- %condition + " or \(% as lua)" + <- %result + ".." + + \("if" if %is_first else "elseif") \%condition then + \(%action as lua statements) - if: =lua "\%condition.type == 'Word' and \%condition.value == 'else'" - %result join= ".." - - else - \(%action as lua statements) - stop for-loop - ..else - set %condition = (%condition as lua) - for all %fallthroughs - %condition join= " or \(% as lua)" - %result join= ".." - - \("if" if %first else "elseif") \%condition then - \(%action as lua statements) - - set %fallthroughs = [] - set %first = (no) + %fallthroughs <- [] + %is_first <- (no) if: %result is not "" - %result join= "\nend" + <- %result + "\nend" return %result # Switch statement immediately compile [when %branch_value = ? %body, when %branch_value is ? %body] to code - set %result = "" - set %fallthroughs = [] - set %first = (yes) + %result <- "" + %fallthroughs <- [] + %is_first <- (yes) + %seen_else <- (no) for %func_call in (%body's "value") assume ((%func_call's "type") is "FunctionCall") or barf ".." Invalid format for 'when' statement. Only '*' blocks are allowed. - set %tokens = (%func_call's "value") - set %star = (%tokens -> 1) - assume (=lua "\%star and \%star.type == 'Word' and \%star.value == '*'") or barf ".." - Invalid format for 'when' statement. Lines must begin with '*' + %tokens <- (%func_call's "value") + with [%star<-(1st in %tokens), %condition<-(2nd in %tokens), %action<-(3rd in %tokens)] + assume (=lua "\%star and \%star.type == 'Word' and \%star.value == '*'") or barf ".." + Invalid format for 'when' statement. Lines must begin with '*' + assume %condition or barf ".." + Invalid format for 'when' statement. Lines must begin with '*' and have a condition or the word "else" + if: %action is (nil) + lua> "table.insert(\%fallthroughs, \%condition)" + do next %func_call - set %condition = (%tokens -> 2) - assume %condition or barf ".." - Invalid format for 'when' statement. Lines must begin with '*' and have a condition or the word "else" + if: =lua "\%condition.type == 'Word' and \%condition.value == 'else'" + assume (not %is_first) or barf "'else' clause cannot be first in 'when % = ?' block" + <- %result + ".." - set %action = (%tokens -> 3) - if: %action is (nil) - lua> "table.insert(\%fallthroughs, \%condition)" - do next %func_call + else + \(%action as lua statements) + end + %seen_else <- (yes) + ..else + assume (not %seen_else) or barf "'else' clause needs to be last in 'when % = ?' block" + %clause <- "" + if: ((%condition's "type") is "Text") or ((%condition's "type") is "Number") + %clause <- "branch_value == (\(%condition as lua))" + ..else + %clause <- "utils.equivalent(branch_value, \(%condition as lua))" + for all %fallthroughs + if: ((%'s "type") is "Text") or ((%'s "type") is "Number") + <- %clause + " or branch_value == (\(%condition as lua))" + ..else + <- %clause + " or utils.equivalent(branch_value, \(%condition as lua))" + <- %result + ".." + + \("if" if %is_first else "elseif") \%clause then + \(%action as lua statements) - if: =lua "\%condition.type == 'Word' and \%condition.value == 'else'" - %result join= ".." - - else - \(%action as lua statements) - stop for-loop - ..else - set %condition = "branch_value == (\(%condition as lua))" - for all %fallthroughs - %condition join= " or (branch_value == \(% as lua))" - %result join= ".." - - \("if" if %first else "elseif") \%condition then - \(%action as lua statements) - - set %fallthroughs = [] - set %first = (no) - - if: %result is not "" - set %result = ".." - do --when % = ? - local branch_value = \(%branch_value as lua);\ - ..\%result - end - end --when % = ? + %fallthroughs <- [] + %is_first <- (no) + + assume (%result is not "") or barf "No body for 'when % = ?' block!" + unless %seen_else + <- %result + "\nend" + %result <- ".." + do --when % = ? + local branch_value = \(%branch_value as lua);\ + ..\%result + end --when % = ? return %result # Try/except @@ -417,27 +455,3 @@ immediately end end -immediately - compile [with %assignments %action] to code - local [%lua, %olds, %old_vals, %new_vals] - set {%temp_vars:[], %old_vals:[], %new_vals:[]} - for %i=%assignment in (%assignments' "value") - set (%temp_vars->%i) = "temp\%i" - set (%old_vals->%i) = ((%assignment's "dict_key") as lua) - set (%new_vals->%i) = ((%assignment's "dict_value") as lua) - return ".." - do - local \(join %temp_vars with ", ") = \(join %old_vals with ", "); - \(join %old_vals with ", ") = \(join %new_vals with ", "); - local fell_through = false; - local ok, ret = pcall(function() - do - \(%action as lua statements) - end - fell_through = true; - end); - \(join %old_vals with ", ") = \(join %temp_vars with ", "); - if not ok then error(ret, 0); end - if not fell_through then return ret end - end - diff --git a/lib/metaprogramming.nom b/lib/metaprogramming.nom index 5ff1ab3..eec99a0 100644 --- a/lib/metaprogramming.nom +++ b/lib/metaprogramming.nom @@ -21,7 +21,6 @@ immediately return names, args; else local alias = nomsu:tree_to_value(spec); - print("ALIAS!!! "..repr(alias).." from "..repr(spec)); local junk, arg_names, junk = nomsu:get_stub(alias); local args = {}; for i, a in ipairs(arg_names) do args[i] = nomsu:var_to_lua_identifier(a); end @@ -37,7 +36,10 @@ immediately local names, args = nomsu:parse_spec(\%names); names, args = repr(names), table.concat(args, ", "); local body_lua = nomsu:tree_to_lua(\%body); - body_lua = body_lua.statements or ("return "..body_lua.expr..";"); + local body_code = body_lua.statements or ("return "..body_lua.expr..";"); + if body_lua.locals and #body_lua.locals > 0 then + body_code = "local "..table.concat(body_lua.locals, ", ")..";\\n"..body_code; + end local lua = ([[ do local function compile_action(%s) @@ -45,7 +47,7 @@ immediately end local function compile_action_wrapper(%s) return {expr=compile_action(%s)}; end nomsu:define_compile_action(%s, %s, compile_action_wrapper, %s); - end]]):format(args, body_lua, args, args, names, repr(\%names:get_line_no()), + end]]):format(args, body_code, args, args, names, repr(\%names:get_line_no()), repr(("compile %s\\n..to %s"):format(\%names.src, \%body.src))); return {statements=lua}; end, \(__src__ 1)); @@ -55,7 +57,10 @@ immediately local names, args = nomsu:parse_spec(\%names); names, args = repr(names), table.concat(args, ", "); local body_lua = nomsu:tree_to_lua(\%body); - body_lua = body_lua.statements or ("return "..body_lua.expr..";"); + local body_code = body_lua.statements or ("return "..body_lua.expr..";"); + if body_lua.locals and #body_lua.locals > 0 then + body_code = "local "..table.concat(body_lua.locals, ", ")..";\\n"..body_code; + end local lua = ([[ do local function compile_action(%s) @@ -63,7 +68,7 @@ immediately end local function compile_action_wrapper(%s) return {statements=compile_action(%s)}; end nomsu:define_compile_action(%s, %s, compile_action_wrapper, %s); - end]]):format(args, body_lua, args, args, names, repr(\%names:get_line_no()), + end]]):format(args, body_code, args, args, names, repr(\%names:get_line_no()), repr(("compile %s\\n..to code %s"):format(\%names.src, \%body.src))); return {statements=lua}; end, \(__src__ 1)); @@ -75,12 +80,15 @@ immediately local names, args = nomsu:parse_spec(\%names); names, args = repr(names), table.concat(args, ", "); local body_lua = nomsu:tree_to_lua(\%body); - body_lua = body_lua.statements or ("return "..body_lua.expr..";"); + local body_code = body_lua.statements or ("return "..body_lua.expr..";"); + if body_lua.locals and #body_lua.locals > 0 then + body_code = "local "..table.concat(body_lua.locals, ", ")..";\\n"..body_code; + end local src = nomsu:dedent(nomsu:source_code(0)); local def_lua = ([[ nomsu:define_action(%s, \(__line_no__), function(%s) %s - end, %s);]]):format(names, args, body_lua, repr(src)); + end, %s);]]):format(names, args, body_code, repr(src)); return def_lua; # Macro to make nomsu macros: diff --git a/lib/operators.nom b/lib/operators.nom index 91410be..4f43dfd 100644 --- a/lib/operators.nom +++ b/lib/operators.nom @@ -11,7 +11,10 @@ immediately It's also critical to have parens around %obj, otherwise Lua is too dumb to realize that {x=1}["x"] is the same as ({x=1})["x"] or that {x=1}.x is the same as ({x=1}).x - compile [%obj'%key, %obj's %key, %obj -> %key] to + compile [..] + %obj' %key, %obj's %key, %key in %obj, %key'th in %obj, %key of %obj, + %key st in %obj, %key nd in %obj, %key rd in %obj, %key th in %obj, + ..to lua> ".." local obj_lua = \(%obj as lua); if not obj_lua:sub(-1,-1):match("[a-zA-Z)]") then @@ -56,70 +59,101 @@ immediately # Variable assignment operator, and += type versions immediately - compile [local %vars] to code - lua> ".." - local locals = \%vars.type == "List" and \%vars.value or {\%vars}; - local identifiers = {}; - for i,x in ipairs(locals) do - identifiers[i] = nomsu:tree_to_lua(x).expr; + lua> ".." + nomsu:define_compile_action("%var <- %value", \(__line_no__), function(\%var, \%value) + local lua = {}; + lua.statements = ("%s = %s;"):format( + assert(nomsu:tree_to_lua(\%var).expr, "Invalid target for assignment: "..\%var.src), + assert(nomsu:tree_to_lua(\%value).expr, "Invalid value for assignment: "..\%value.src)); + if \%var.type == "Var" then + lua.locals = {nomsu:tree_to_lua(\%var).expr}; end - return "local "..table.concat(identifiers, ", "); - compile [set %var = %val] to code "\(%var as lua) = \(%val as lua);" - compile [set %assignments] to code - assume ((%assignments' "type") is "Dict") or barf "Expected Dict, but got \(%assignments' "type")" - lua> ".." - local lhs, rhs = {}, {}; - for i,entry in ipairs(\%assignments.value) do - lhs[i] = nomsu:tree_to_lua(entry.dict_key).expr; - rhs[i] = nomsu:tree_to_lua(entry.dict_value).expr; + return lua; + end, \(__src__ 1)); + lua> ".." + nomsu:define_compile_action("with %assignments %body", \(__line_no__), function(\%assignments, \%body) + local body_lua = nomsu:tree_to_lua(\%body); + local declarations = ""; + local leftover_locals = {}; + for _, body_local in ipairs(body_lua.locals or {}) do + leftover_locals[body_local] = true; end - return table.concat(lhs, ", ").." = "..table.concat(rhs, ", ")..";"; + assert(\%assignments.type == "List", + "Expected a List for the assignments part of 'with' statement, not "..\%assignments.src); + for i, item in ipairs(\%assignments.value) do + if item.type == "Var" then + local var = nomsu:tree_to_lua(item).expr; + leftover_locals[var] = nil; + declarations = declarations.."local "..var..";\\n "; + else + assert(item.type == "FunctionCall" and #item.value == 3 and item.value[2].src == "<-", + "'with' statement expects entries of the form: '%var <- %value', not: "..item.src); + local target, value = item.value[1], item.value[3]; + if target.type == "Var" then + local var = nomsu:tree_to_lua(target).expr; + leftover_locals[var] = nil; + declarations = declarations..(("local %s = %s;\\n "):format( + var, assert(nomsu:tree_to_lua(value).expr, "Invalid value for assignment: "..value.src))); + else + declarations = declarations..(("%s = %s;\\n "):format( + assert(nomsu:tree_to_lua(target).expr, "Invalid target for assignment: "..target.src), + assert(nomsu:tree_to_lua(value).expr, "Invalid value for assignment: "..value.src))); + end + end + end + local code = ([[ + do + %s%s + end]]):format(declarations, body_lua.statements or (body_lua.expr..";")); + return {statements=code, locals=utils.keys(leftover_locals)}; + end, \(__src__ 1)); -# Update assignment operators -compile [%var += %val] to code "\(%var as lua) = \(%var as lua) + \(%val as lua);" -compile [%var -= %val] to code "\(%var as lua) = \(%var as lua) - \(%val as lua);" -compile [%var *= %val] to code "\(%var as lua) = \(%var as lua) * \(%val as lua);" -compile [%var /= %val] to code "\(%var as lua) = \(%var as lua) / \(%val as lua);" -compile [%var ^= %val] to code "\(%var as lua) = \(%var as lua) ^ \(%val as lua);" -compile [%var and= %val] to code "\(%var as lua) = \(%var as lua) and\(%val as lua);" -compile [%var or= %val] to code "\(%var as lua) = \(%var as lua) or \(%val as lua);" -compile [%var join= %val] to code "\(%var as lua) = \(%var as lua) .. \(%val as lua);" -compile [wrap %var around %val] to code "\(%var as lua) = \(%var as lua) % \(%val as lua);" +immediately + # Math Operators + compile [%x + %y] to "(\(%x as lua) + \(%y as lua))" + compile [%x - %y] to "(\(%x as lua) - \(%y as lua))" + compile [%x * %y] to "(\(%x as lua) * \(%y as lua))" + compile [%x / %y] to "(\(%x as lua) / \(%y as lua))" + compile [%x ^ %y] to "(\(%x as lua) ^ \(%y as lua))" + compile [%x wrapped around %y, %x mod %y] to "(\(%x as lua) % \(%y as lua))" -# Math Operators -compile [%x + %y] to "(\(%x as lua) + \(%y as lua))" -compile [%x - %y] to "(\(%x as lua) - \(%y as lua))" -compile [%x * %y] to "(\(%x as lua) * \(%y as lua))" -compile [%x / %y] to "(\(%x as lua) / \(%y as lua))" -compile [%x ^ %y] to "(\(%x as lua) ^ \(%y as lua))" -compile [%x wrapped around %y, %x mod %y] to "(\(%x as lua) % \(%y as lua))" + # 3-part chained comparisons + # (uses a lambda to avoid re-evaluating middle value, while still being an expression) + parse [%x < %y < %z] as: =lua "(function(x,y,z) return x < y and y < z; end)(\%x,\%y,\%z)" + parse [%x <= %y < %z] as: =lua "(function(x,y,z) return x <= y and y < z; end)(\%x,\%y,\%z)" + parse [%x < %y <= %z] as: =lua "(function(x,y,z) return x < y and y <= z; end)(\%x,\%y,\%z)" + parse [%x <= %y <= %z] as: =lua "(function(x,y,z) return x <= y and y <= z; end)(\%x,\%y,\%z)" + parse [%x > %y > %z] as: =lua "(function(x,y,z) return x > y and y > z; end)(\%x,\%y,\%z)" + parse [%x >= %y > %z] as: =lua "(function(x,y,z) return x >= y and y > z; end)(\%x,\%y,\%z)" + parse [%x > %y >= %z] as: =lua "(function(x,y,z) return x > y and y >= z; end)(\%x,\%y,\%z)" + parse [%x >= %y >= %z] as: =lua "(function(x,y,z) return x >= y and y >= z; end)(\%x,\%y,\%z)" + # TODO: optimize for common case where x,y,z are all either variables or number literals -# 3-part chained comparisons -# (uses a lambda to avoid re-evaluating middle value, while still being an expression) -parse [%x < %y < %z] as: =lua "(function(x,y,z) return x < y and y < z; end)(\%x,\%y,\%z)" -parse [%x <= %y < %z] as: =lua "(function(x,y,z) return x <= y and y < z; end)(\%x,\%y,\%z)" -parse [%x < %y <= %z] as: =lua "(function(x,y,z) return x < y and y <= z; end)(\%x,\%y,\%z)" -parse [%x <= %y <= %z] as: =lua "(function(x,y,z) return x <= y and y <= z; end)(\%x,\%y,\%z)" -parse [%x > %y > %z] as: =lua "(function(x,y,z) return x > y and y > z; end)(\%x,\%y,\%z)" -parse [%x >= %y > %z] as: =lua "(function(x,y,z) return x >= y and y > z; end)(\%x,\%y,\%z)" -parse [%x > %y >= %z] as: =lua "(function(x,y,z) return x > y and y >= z; end)(\%x,\%y,\%z)" -parse [%x >= %y >= %z] as: =lua "(function(x,y,z) return x >= y and y >= z; end)(\%x,\%y,\%z)" -# TODO: optimize for common case where x,y,z are all either variables or number literals + # Boolean Operators + compile [%x and %y] to "(\(%x as lua) and \(%y as lua))" + compile [%x or %y] to "(\(%x as lua) or \(%y as lua))" -# Boolean Operators -compile [%x and %y] to "(\(%x as lua) and \(%y as lua))" -compile [%x or %y] to "(\(%x as lua) or \(%y as lua))" + # Bitwise Operators + compile [%a OR %b, %a | %b] to "bit32.bor(\(%a as lua), \(%b as lua))" + compile [%a XOR %b] to "bit32.bxor(\(%a as lua), \(%b as lua))" + compile [%a AND %b, %a & %b] to "bit32.band(\(%a as lua), \(%b as lua))" + compile [NOT %, ~ %] to "bit32.bnot(\(% as lua))" + compile [%x LSHIFT %shift, %x << %shift] to "bit32.lshift(\(%x as lua), \(%shift as lua))" + compile [%x RSHIFT %shift, %x >>> %shift] to "bit32.rshift(\(%x as lua), \(%shift as lua))" + compile [%x ARSHIFT %shift, %x >> %shift] to "bit32.arshift(\(%x as lua), \(%shift as lua))" + # TODO: implement OR, XOR, AND for multiple operands? -# Bitwise Operators -compile [%a OR %b, %a | %b] to "bit32.bor(\(%a as lua), \(%b as lua))" -compile [%a XOR %b] to "bit32.bxor(\(%a as lua), \(%b as lua))" -compile [%a AND %b, %a & %b] to "bit32.band(\(%a as lua), \(%b as lua))" -compile [NOT %, ~ %] to "bit32.bnot(\(% as lua))" -compile [%x LSHIFT %shift, %x << %shift] to "bit32.lshift(\(%x as lua), \(%shift as lua))" -compile [%x RSHIFT %shift, %x >>> %shift] to "bit32.rshift(\(%x as lua), \(%shift as lua))" -compile [%x ARSHIFT %shift, %x >> %shift] to "bit32.arshift(\(%x as lua), \(%shift as lua))" -# TODO: implement OR, XOR, AND for multiple operands? + # Unary operators + compile [- %] to "(- \(% as lua))" + compile [not %] to "(not \(% as lua))" -# Unary operators -compile [- %] to "-(\(% as lua))" -compile [not %] to "not (\(% as lua))" +# Update operators +immediately + parse [<- %var + %] as: %var <- (%var + %) + parse [<- %var - %] as: %var <- (%var - %) + parse [<- %var * %] as: %var <- (%var * %) + parse [<- %var / %] as: %var <- (%var / %) + parse [<- %var ^ %] as: %var <- (%var ^ %) + parse [<- %var and %] as: %var <- (%var and %) + parse [<- %var or %] as: %var <- (%var or %) + parse [wrap %var around %] as: "\(%var as lua) = \(%var as lua) % \(% as lua);" diff --git a/nomsu.lua b/nomsu.lua index 8042e9b..ba6a834 100644 --- a/nomsu.lua +++ b/nomsu.lua @@ -21,6 +21,12 @@ do local _obj_0 = table insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat end +do + local STRING_METATABLE = getmetatable("") + STRING_METATABLE.__add = function(self, other) + return self .. stringify(other) + end +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 @@ -416,6 +422,10 @@ do assert(tree.type == "File", "Attempt to run non-file: " .. tostring(tree.type)) local lua = self:tree_to_lua(tree) local lua_code = lua.statements or (lua.expr .. ";") + local locals = lua_code.locals or { } + if #locals > 0 then + lua_code = "local " .. concat(locals, ", ") .. ";\n" .. lua_code + end lua_code = "-- File: " .. tostring(filename) .. "\n" .. lua_code local ret = self:run_lua(lua_code) if max_operations then @@ -875,6 +885,7 @@ do if #tree.value == 1 then return self:tree_to_lua(tree.value[1]) end + local declared_locals = { } local lua_bits = { } local _list_0 = tree.value for _index_0 = 1, #_list_0 do @@ -883,10 +894,32 @@ do if not lua then error("No lua produced by " .. tostring(repr(line))) end + if lua.locals then + local new_locals + 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] + if not declared_locals[l] then + _accum_0[_len_0] = l + _len_0 = _len_0 + 1 + end + end + new_locals = _accum_0 + end + if #new_locals > 0 then + insert(lua_bits, "local " .. tostring(concat(new_locals, ", ")) .. ";") + for _index_1 = 1, #new_locals do + local l = new_locals[_index_1] + declared_locals[l] = true + end + end + end if lua.statements then insert(lua_bits, lua.statements) - end - if lua.expr then + elseif lua.expr then insert(lua_bits, tostring(lua.expr) .. ";") end end @@ -903,6 +936,7 @@ 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] @@ -912,15 +946,22 @@ do expr = lua.expr } end + if lua.locals then + local _list_1 = lua.locals + for _index_1 = 1, #_list_1 do + local l = _list_1[_index_1] + locals[l] = true + end + end if lua.statements then insert(lua_bits, lua.statements) - end - if lua.expr then + elseif lua.expr then insert(lua_bits, tostring(lua.expr) .. ";") end end return { - statements = concat(lua_bits, "\n") + statements = concat(lua_bits, "\n"), + locals = (next(locals) and utils.keys(locals) or nil) } elseif "FunctionCall" == _exp_0 then insert(self.compilestack, tree) @@ -981,7 +1022,7 @@ do insert(bits, tok.value) else local lua = self:tree_to_lua(tok) - assert(lua.statements == nil, "non-expression value inside math expression") + assert(lua.expr, "non-expression value inside math expression: " .. tostring(tok.src)) insert(bits, lua.expr) end end @@ -1001,7 +1042,7 @@ do break end local lua = self:tree_to_lua(tok) - assert(lua.expr, "Cannot use " .. tostring(tok.src) .. " as an argument, since it's not an expression, it produces: " .. tostring(repr(lua))) + assert(lua.expr, tostring(tree:get_line_no()) .. ": Cannot use:\n" .. tostring(colored.yellow(tok.src)) .. "\nas an argument to " .. tostring(tree.stub) .. ", since it's not an expression, it produces: " .. tostring(repr(lua))) insert(args, lua.expr) _continue_0 = true until true @@ -1051,9 +1092,7 @@ do self:print_tree(bit) self:writeln(tostring(colored.bright("EXPR:")) .. " " .. tostring(lua.expr) .. ", " .. tostring(colored.bright("STATEMENT:")) .. " " .. tostring(lua.statements)) end - if lua.statements then - error("Cannot use [[" .. tostring(bit.src) .. "]] as a string interpolation value, since it's not an expression.") - end + assert(lua.expr, "Cannot use [[" .. tostring(bit.src) .. "]] as a string interpolation value, since it's not an expression.") insert(concat_parts, "stringify(" .. tostring(lua.expr) .. ")") _continue_0 = true until true @@ -1083,9 +1122,7 @@ do for _index_0 = 1, #_list_0 do local item = _list_0[_index_0] local lua = self:tree_to_lua(item) - if lua.statements then - error("Cannot use [[" .. tostring(item.src) .. "]] as a list item, since it's not an expression.") - end + assert(lua.expr, "Cannot use [[" .. tostring(item.src) .. "]] as a list item, since it's not an expression.") insert(items, lua.expr) end return { @@ -1104,13 +1141,9 @@ do else key_lua = self:tree_to_lua(entry.dict_key) end - if key_lua.statements then - error("Cannot use [[" .. tostring(entry.dict_key.src) .. "]] as a dict key, since it's not an expression.") - end + assert(key_lua.expr, "Cannot use [[" .. tostring(entry.dict_key.src) .. "]] as a dict key, since it's not an expression.") local value_lua = self:tree_to_lua(entry.dict_value) - if value_lua.statements then - error("Cannot use [[" .. tostring(entry.dict_value.src) .. "]] as a dict value, since it's not an expression.") - end + assert(value_lua.expr, "Cannot use [[" .. tostring(entry.dict_value.src) .. "]] as a dict value, since it's not an expression.") local key_str = key_lua.expr:match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=]) if key_str then insert(items, tostring(key_str) .. "=" .. tostring(value_lua.expr)) @@ -1338,9 +1371,7 @@ do insert(concat_parts, bit) else local lua = nomsu:tree_to_lua(bit) - if lua.statements then - error("Cannot use [[" .. tostring(bit.src) .. "]] as a string interpolation value, since it's not an expression.") - end + assert(lua.expr, "Cannot use [[" .. tostring(bit.src) .. "]] as a string interpolation value, since it's not an expression.") insert(concat_parts, lua.expr) end end @@ -1352,7 +1383,8 @@ do lua_code = "-- Immediately:\n" .. lua_code nomsu:run_lua(lua_code) return { - statements = lua_code + statements = lua_code, + locals = lua.locals } end) self:define_compile_action("lua> %code", "nomsu.moon", function(_code) diff --git a/nomsu.moon b/nomsu.moon index 2fb5249..2ef3c6c 100755 --- a/nomsu.moon +++ b/nomsu.moon @@ -19,6 +19,12 @@ colors = setmetatable({}, {__index:->""}) colored = setmetatable({}, {__index:(_,color)-> ((msg)-> colors[color]..(msg or '')..colors.reset)}) {:insert, :remove, :concat} = table +-- Use + operator for string coercive concatenation (note: "asdf" + 3 == "asdf3") +-- Note: This globally affects all strings in this instance of Lua! +do + STRING_METATABLE = getmetatable("") + STRING_METATABLE.__add = (other)=> @ .. stringify(other) + -- TODO: -- consider non-linear codegen, rather than doing thunks for things like comprehensions -- improve indentation of generated lua code @@ -301,6 +307,9 @@ class NomsuCompiler lua = @tree_to_lua(tree) lua_code = lua.statements or (lua.expr..";") + locals = lua_code.locals or {} + if #locals > 0 + lua_code = "local "..concat(locals, ", ")..";\n"..lua_code lua_code = "-- File: #{filename}\n"..lua_code ret = @run_lua(lua_code) if max_operations @@ -616,13 +625,19 @@ class NomsuCompiler when "File" if #tree.value == 1 return @tree_to_lua(tree.value[1]) + declared_locals = {} lua_bits = {} for line in *tree.value lua = @tree_to_lua line if not lua error "No lua produced by #{repr line}" + if lua.locals + new_locals = [l for l in *lua.locals when not declared_locals[l]] + if #new_locals > 0 + insert lua_bits, "local #{concat new_locals, ", "};" + for l in *new_locals do declared_locals[l] = true if lua.statements then insert lua_bits, lua.statements - if lua.expr then insert lua_bits, "#{lua.expr};" + elseif lua.expr then insert lua_bits, "#{lua.expr};" return statements:concat(lua_bits, "\n") when "Comment" @@ -633,13 +648,16 @@ class NomsuCompiler when "Block" lua_bits = {} + locals = {} for arg in *tree.value lua = @tree_to_lua arg if #tree.value == 1 and lua.expr and not lua.statements return expr:lua.expr + if lua.locals + for l in *lua.locals do locals[l] = true if lua.statements then insert lua_bits, lua.statements - if lua.expr then insert lua_bits, "#{lua.expr};" - return statements:concat(lua_bits, "\n") + elseif lua.expr then insert lua_bits, "#{lua.expr};" + return statements:concat(lua_bits, "\n"), locals:(next(locals) and utils.keys(locals) or nil) when "FunctionCall" insert @compilestack, tree @@ -668,7 +686,7 @@ class NomsuCompiler insert bits, tok.value else lua = @tree_to_lua(tok) - assert(lua.statements == nil, "non-expression value inside math expression") + assert(lua.expr, "non-expression value inside math expression: #{tok.src}") insert bits, lua.expr remove @compilestack return expr:"(#{concat bits, " "})" @@ -677,7 +695,8 @@ class NomsuCompiler for tok in *tree.value if tok.type == "Word" then continue lua = @tree_to_lua(tok) - assert(lua.expr, "Cannot use #{tok.src} as an argument, since it's not an expression, it produces: #{repr lua}") + assert lua.expr, + "#{tree\get_line_no!}: Cannot use:\n#{colored.yellow tok.src}\nas an argument to #{tree.stub}, since it's not an expression, it produces: #{repr lua}" insert args, lua.expr if metadata and metadata.arg_orders @@ -702,8 +721,8 @@ class NomsuCompiler @writeln (colored.bright "INTERP:") @print_tree bit @writeln "#{colored.bright "EXPR:"} #{lua.expr}, #{colored.bright "STATEMENT:"} #{lua.statements}" - if lua.statements - error "Cannot use [[#{bit.src}]] as a string interpolation value, since it's not an expression." + assert lua.expr, + "Cannot use [[#{bit.src}]] as a string interpolation value, since it's not an expression." insert concat_parts, "stringify(#{lua.expr})" if string_buffer ~= "" @@ -719,8 +738,8 @@ class NomsuCompiler items = {} for item in *tree.value lua = @tree_to_lua item - if lua.statements - error "Cannot use [[#{item.src}]] as a list item, since it's not an expression." + assert lua.expr, + "Cannot use [[#{item.src}]] as a list item, since it's not an expression." insert items, lua.expr return expr:@@comma_separated_items("{", items, "}") @@ -731,11 +750,11 @@ class NomsuCompiler {expr:repr(entry.dict_key.value)} else @tree_to_lua entry.dict_key - if key_lua.statements - error "Cannot use [[#{entry.dict_key.src}]] as a dict key, since it's not an expression." + assert key_lua.expr, + "Cannot use [[#{entry.dict_key.src}]] as a dict key, since it's not an expression." value_lua = @tree_to_lua entry.dict_value - if value_lua.statements - error "Cannot use [[#{entry.dict_value.src}]] as a dict value, since it's not an expression." + assert value_lua.expr, + "Cannot use [[#{entry.dict_value.src}]] as a dict value, since it's not an expression." key_str = key_lua.expr\match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=]) if key_str insert items, "#{key_str}=#{value_lua.expr}" @@ -888,8 +907,8 @@ class NomsuCompiler insert concat_parts, bit else lua = nomsu\tree_to_lua bit - if lua.statements - error "Cannot use [[#{bit.src}]] as a string interpolation value, since it's not an expression." + assert lua.expr, + "Cannot use [[#{bit.src}]] as a string interpolation value, since it's not an expression." insert concat_parts, lua.expr return concat(concat_parts) @@ -898,7 +917,7 @@ class NomsuCompiler lua_code = lua.statements or (lua.expr..";") lua_code = "-- Immediately:\n"..lua_code nomsu\run_lua(lua_code) - return statements:lua_code + return statements:lua_code, locals:lua.locals @define_compile_action "lua> %code", "nomsu.moon", (_code)-> lua = nomsu_string_as_lua(_code)