aboutsummaryrefslogtreecommitdiff
path: root/lib/control_flow.nom
diff options
context:
space:
mode:
Diffstat (limited to 'lib/control_flow.nom')
-rw-r--r--lib/control_flow.nom468
1 files changed, 241 insertions, 227 deletions
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
-