diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/class.nom | 9 | ||||
| -rw-r--r-- | lib/collections.nom | 6 | ||||
| -rw-r--r-- | lib/control_flow.nom | 468 | ||||
| -rw-r--r-- | lib/metaprogramming.nom | 22 | ||||
| -rw-r--r-- | lib/operators.nom | 154 |
5 files changed, 357 insertions, 302 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 '*' - - 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" - - set %action = (%tokens -> 3) - if: %action is (nil) - lua do> "table.insert(\%fallthroughs, \%condition)" - do next %func_call - - 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) + 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 + + if: =lua "\%condition.type == 'Word' and \%condition.value == 'else'" + + <- %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) + + %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 '*' - - 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" - - set %action = (%tokens -> 3) - if: %action is (nil) - lua> "table.insert(\%fallthroughs, \%condition)" - do next %func_call - - 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) + %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 + + if: =lua "\%condition.type == 'Word' and \%condition.value == 'else'" + assume (not %is_first) or barf "'else' clause cannot be first in 'when % = ?' block" + <- %result + ".." + + 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: %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 + 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 - return table.concat(lhs, ", ").." = "..table.concat(rhs, ", ")..";"; + 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);" |
