aboutsummaryrefslogtreecommitdiff
path: root/core/control_flow.nom
diff options
context:
space:
mode:
Diffstat (limited to 'core/control_flow.nom')
-rw-r--r--core/control_flow.nom221
1 files changed, 154 insertions, 67 deletions
diff --git a/core/control_flow.nom b/core/control_flow.nom
index 63d7900..3492b68 100644
--- a/core/control_flow.nom
+++ b/core/control_flow.nom
@@ -43,6 +43,10 @@ compile [..]
# Conditional expression (ternary operator)
# Note: this uses a function instead of "(condition and if_expr or else_expr)"
because that breaks if %if_expr is falsey, e.g. "x < 5 and false or 99"
+test:
+ assume ((1 if (yes) else 2) == 1)
+ assume ((1 if (no) else 2) == 2)
+
compile [..]
%when_true_expr if %condition else %when_false_expr
%when_true_expr if %condition otherwise %when_false_expr
@@ -74,31 +78,43 @@ compile [..]
# GOTOs
+test:
+ %i = 0
+ === %loop ===
+ %i += 1
+ unless (%i == 10): go to %loop
+ assume (%i == 10)
+
compile [=== %label ===, --- %label ---, *** %label ***] to (..)
Lua "::label_\(%label as lua identifier)::"
compile [go to %label] to (Lua "goto label_\(%label as lua identifier)")
# Basic loop control
-compile [do next] to (Lua "continue")
+compile [do next] to (Lua "goto continue")
compile [stop] to (Lua "break")
-# Helper function
-#TODO: do "using % compile %" instead so it's actually a helper function
-compile [%tree has subtree %subtree where %condition] to (..)
- Lua value ".."
- (function()
- for \(%subtree as lua expr) in coroutine.wrap(function() \(%tree as lua expr):map(coroutine.yield) end) do
- if \(%condition as lua expr) then
- return true
- end
- end
- return false
- end)()
-
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
# While loops
+test:
+ %x = 0
+ repeat while (%x < 10): %x += 1
+ assume (%x == 10)
+ repeat while (%x < 20): stop
+ repeat while (%x < 20): stop repeating
+ assume (%x == 10)
+ repeat while (%x < 20):
+ %x += 1
+ if (yes): do next
+ barf "Failed to 'do next'"
+
+ assume (%x == 20)
+ repeat while (%x < 30):
+ %x += 1
+ if (yes): do next repeat
+ barf "Failed to 'do next repeat'"
+
+ assume (%x == 30)
+
compile [do next repeat] to (Lua "goto continue_repeat")
compile [stop repeating] to (Lua "goto stop_repeat")
compile [repeat while %condition %body] to:
@@ -107,15 +123,14 @@ compile [repeat while %condition %body] to:
while \(%condition as lua expr) do
\(%body as lua statements)
- if (..)
- %body has subtree % where ((%.type == "Action") and (%.stub is "do next repeat"))
- ..:
+ if (%body has subtree \(do next)):
+ to %lua write "\n ::continue::"
+
+ if (%body has subtree \(do next repeat)):
to %lua write "\n ::continue_repeat::"
-
+
to %lua write "\nend --while-loop"
- if (..)
- %body has subtree % where ((%.type == "Action") and (%.stub is "stop repeating"))
- ..:
+ if (%body has subtree \(stop repeating)):
%lua = (..)
Lua ".."
do -- scope of "stop repeating" label
@@ -127,21 +142,26 @@ compile [repeat while %condition %body] to:
parse [repeat %body] as (repeat while (yes) %body)
parse [repeat until %condition %body] as (repeat while (not %condition) %body)
+
+test:
+ %x = 0
+ repeat 10 times: %x += 1
+ assume (%x == 10)
+
compile [repeat %n times %body] to:
%lua = (..)
Lua ".."
for i=1,\(%n as lua expr) do
\(%body as lua statements)
- if (..)
- %body has subtree % where ((%.type == "Action") and (%.stub is "do next repeat"))
- ..:
+ if (%body has subtree \(do next)):
+ to %lua write "\n ::continue::"
+
+ if (%body has subtree \(do next repeat)):
to %lua write "\n ::continue_repeat::"
to %lua write "\nend --numeric for-loop"
- if (..)
- %body has subtree % where ((%.type == "Action") and (%.stub is "stop repeating"))
- ..:
+ if (%body has subtree \(stop repeating)):
%lua = (..)
Lua ".."
do -- scope of "stop repeating" label
@@ -151,7 +171,6 @@ compile [repeat %n times %body] to:
return %lua
-
# For loop control flow
compile [stop %var] to (Lua "goto stop_\(%var as lua identifier)")
compile [do next %var] to (Lua "goto continue_\(%var as lua identifier)")
@@ -163,6 +182,23 @@ compile [===next %var ===, ---next %var ---, ***next %var ***] to (..)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+test:
+ %nums = []
+ for %x in 1 to 5: add %x to %nums
+ assume (%nums == [1,2,3,4,5])
+ %nums = []
+ for %x in 1 to 5 via 2: add %x to %nums
+ assume (%nums == [1,3,5])
+ %nums = []
+ for %outer in 1 to 100:
+ for %inner in %outer to (%outer + 2):
+ if (%inner == 2):
+ add -2 to %nums
+ do next %inner
+ add %inner to %nums
+ if (%inner == 5): stop %outer
+ assume (%nums == [1,-2,3,-2,3,4,3,4,5])
+
# Numeric range for loops
compile [..]
for %var in %start to %stop by %step %body
@@ -179,18 +215,15 @@ compile [..]
%step as lua expr
.. do
\(%body as lua statements)
+
+ if (%body has subtree \(do next)):
+ to %lua write "\n ::continue::"
- if (..)
- %body has subtree % where (..)
- (%.type == "Action") and ((%.stub is "do next %") and (%.(3).1 == %var.1))
- ..:
+ if (%body has subtree (\(do next %v) with vars {v:%var})):
to %lua write "\n \(compile as (===next %var ===))"
to %lua write "\nend --numeric for-loop"
- if (..)
- %body has subtree % where (..)
- (%.type == "Action") and ((%.stub is "stop %") and (%.(2).1 == %var.1))
- ..:
+ if (%body has subtree (\(stop %v) with vars {v:%var})):
%lua = (..)
Lua ".."
do -- scope for stopping for-loop
@@ -205,6 +238,17 @@ compile [..]
parse [for %var in %start to %stop %body] as (..)
for %var in %start to %stop via 1 %body
+test:
+ %a = [10,20,30,40,50]
+ %b = []
+ for %x in %a: add %x to %b
+ assume (%a == %b)
+ %b = []
+ for %x in %a:
+ if (%x == 10): do next %x
+ if (%x == 50): stop %x
+ add %x to %b
+ assume (%b == [20,30,40])
# For-each loop (lua's "ipairs()")
compile [for %var in %iterable %body] to:
@@ -218,17 +262,14 @@ compile [for %var in %iterable %body] to:
for i,\(%var as lua identifier) in ipairs(\(%iterable as lua expr)) do
\(%body as lua statements)
- if (..)
- %body has subtree % where (..)
- (%.type == "Action") and ((%.stub is "do next %") and (%.(3).1 == %var.1))
- ..:
+ if (%body has subtree \(do next)):
+ to %lua write "\n ::continue::"
+
+ if (%body has subtree (\(do next %v) with vars {v:%var})):
to %lua write (Lua "\n\(compile as (===next %var ===))")
to %lua write "\nend --foreach-loop"
- if (..)
- %body has subtree % where (..)
- (%.type == "Action") and ((%.stub is "stop %") and (%.(2).1 == %var.1))
- ..:
+ if (%body has subtree (\(stop %v) with vars {v:%var})):
%lua = (..)
Lua ".."
do -- scope for stopping for-loop
@@ -238,6 +279,14 @@ compile [for %var in %iterable %body] to:
return %lua
+test:
+ %d = {a:10, b:20, c:30, d:40, e:50}
+ %result = []
+ for %k = %v in %d:
+ if (%k == "a"): do next %k
+ if (%v == 20): do next %v
+ add "\%k = \%v" to %result
+ assume ((%result sorted) == ["c = 30", "d = 40", "e = 50"])
# Dict iteration (lua's "pairs()")
compile [..]
@@ -257,31 +306,22 @@ compile [..]
%iterable as lua expr
..) do
\(%body as lua statements)
+
+ if (%body has subtree \(do next)):
+ to %lua write "\n ::continue::"
- if (..)
- %body has subtree % where (..)
- (%.type == "Action") and ((%.stub is "do next %") and (%.(3).1 == %key.1))
- ..:
+ if (%body has subtree (\(do next %v) with vars {v:%key})):
to %lua write (Lua "\n\(compile as (===next %key ===))")
- if (..)
- %body has subtree % where (..)
- (%.type == "Action") and ((%.stub is "do next %") and (%.(3).1 == %value.1))
- ..:
+ if (%body has subtree (\(do next %v) with vars {v:%value})):
to %lua write (Lua "\n\(compile as (===next %value ===))")
to %lua write "\nend --foreach-loop"
%stop_labels = (Lua "")
- if (..)
- %body has subtree % where (..)
- (%.type == "Action") and ((%.stub is "stop %") and (%.(2).1 == %key.1))
- ..:
+ if (%body has subtree (\(stop %v) with vars {v:%key})):
to %stop_labels write "\n\(compile as (===stop %key ===))"
- if (..)
- %body has subtree % where (..)
- (%.type == "Action") and ((%.stub is "stop %") and (%.(2).1 == %value.1))
- ..:
+ if (%body has subtree (\(stop %v) with vars {v:%value})):
to %stop_labels write "\n\(compile as (===stop %value ===))"
if ((length of "\%stop_labels") > 0):
@@ -295,6 +335,14 @@ compile [..]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+test:
+ if:
+ (1 == 2) (100 < 0): barf "bad conditional"
+ (1 == 0) (1 == 1) (%not_a_variable.x): do nothing
+ (1 == 1): barf "bad conditional"
+ (1 == 2): barf "bad conditional"
+ else: barf "bad conditional"
+
# Multi-branch conditional (if..elseif..else)
compile [if %body, when %body] to:
%code = (Lua "")
@@ -352,6 +400,12 @@ compile [if %body, when %body] to:
to %code write "\nend --when"
return %code
+test:
+ if 5 is:
+ 1 2 3: barf "bad switch statement"
+ 4 5: do nothing
+ 5 6: barf "bad switch statement"
+ else: barf "bad switch statement"
# Switch statement
compile [if %branch_value is %body, when %branch_value is %body] to:
@@ -423,6 +477,17 @@ compile [do %action] to (..)
\(%action as lua statements)
end --do
+test:
+ %d = {}
+ try:
+ do:
+ %d.x = "bad"
+ barf
+ ..then always:
+ %d.x = "good"
+ ..and if it barfs: do nothing
+ assume (%d.x == "good")
+
compile [do %action then always %final_action] to (..)
Lua ".."
do
@@ -436,24 +501,46 @@ compile [do %action then always %final_action] to (..)
if not fell_through then return ret end
end
+test:
+ assume ((result of: return 99) == 99)
# Inline thunk:
compile [result of %body] to (Lua value "(\(compile as ([] -> %body)))()")
+test:
+ %t = [1, [2, [[3], 4], 5, [[[6]]]]]
+ %flat = []
+ for % in recursive %t:
+ if ((type of %) is "table"):
+ for %2 in %: recurse % on %2
+ ..else: add % to %flat
+
+ assume ((sorted %flat) == [1, 2, 3, 4, 5, 6])
+
# Recurion control flow
compile [for %var in recursive %structure %body] to (..)
with local compile actions:
compile [recurse %v on %x] to (..)
Lua "table.insert(stack\(%v as lua id), \(%x as lua expr))"
- return (..)
+ %lua = (..)
Lua ".."
do
local stack\(%var as lua id) = list{\(%structure as lua expr)}
while #stack\(%var as lua id) > 0 do
\(%var as lua expr) = table.remove(stack\(%var as lua id), 1)
\(%body as lua statements)
- \(compile as (===next %var ===))
- end
- \(compile as (===stop %var ===))
- end \ No newline at end of file
+
+ if (%body has subtree \(do next)):
+ to %lua write "\n ::continue::"
+
+ if (%body has subtree (\(do next %v) with vars {v:%var})):
+ to %lua write "\n \(compile as (===next %var ===))"
+
+ to %lua write "\n end -- Recursive loop"
+ if (%body has subtree (\(stop %v) with vars {v:%var})):
+ to %lua write "\n \(compile as (===stop %var ===))"
+
+ to %lua write "\nend -- Recursive scope"
+
+ return %lua