From c1c32688a4afc43f6addb99b8b5fa878944a70e3 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 14 Jan 2019 15:42:48 -0800 Subject: Overhaul in progress, mostly working. Moved all the nomsu packages into lib/, including core/*. Changes to how nomsu environments and importing work. --- core/control_flow.nom | 489 -------------------------------------------------- 1 file changed, 489 deletions(-) delete mode 100644 core/control_flow.nom (limited to 'core/control_flow.nom') diff --git a/core/control_flow.nom b/core/control_flow.nom deleted file mode 100644 index b0c4f27..0000000 --- a/core/control_flow.nom +++ /dev/null @@ -1,489 +0,0 @@ -#!/usr/bin/env nomsu -V6.14 -# - This file contains compile-time actions that define basic control flow structures - like "if" statements and loops. - -use "core/metaprogramming" -use "core/operators" - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -# No-Op -test: - do nothing -(do nothing) compiles to "" - -# Conditionals -test: - if (no): - fail "conditional fail" - -(if $condition $if_body) compiles to (" - if \($condition as lua expr) then - \($if_body as lua) - end -") - -test: - unless (yes): - fail "conditional fail" - -(unless $condition $unless_body) parses as (if (not $condition) $unless_body) -[ - if $condition $if_body else $else_body, unless $condition $else_body else $if_body -] all compile to (" - if \($condition as lua expr) then - \($if_body as lua) - else - \($else_body as lua) - end -") - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -# 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) - -[ - $when_true_expr if $condition else $when_false_expr - $when_true_expr if $condition otherwise $when_false_expr - $when_false_expr unless $condition else $when_true_expr - $when_false_expr unless $condition then $when_true_expr -] all compile to: - # 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, .List, .Dict, .Number}.($when_true_expr.type): - return - Lua (" - (\($condition as lua expr) and \($when_true_expr as lua expr) or \ - ..\($when_false_expr as lua expr)) - ") - ..else: - # Otherwise, need to do an anonymous inline function (yuck, too bad lua - doesn't have a proper ternary operator!) - To see why this is necessary consider: (random()<.5 and false or 99) - return - Lua (" - ((function() - if \($condition as lua expr) then - return \($when_true_expr as lua expr) - else - return \($when_false_expr as lua expr) - end - end)()) - ") - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -# GOTOs -test: - $i = 0 - --- $loop --- - $i += 1 - unless ($i == 10): - go to $loop - assume ($i == 10) - --- (Loop) --- - $i -= 1 - unless ($i == 0): - go to (Loop) - assume ($i == 0) - -(--- $label ---) compiles to (" - ::label_\( - ($label.stub, as lua id) if ($label.type == "Action") else - $label as lua identifier - ):: -") - -(go to $label) compiles to (" - goto label_\( - ($label.stub, as lua id) if ($label.type == "Action") else - $label as lua identifier - ) -") - -# Basic loop control -(stop $var) compiles to: - if $var: - return (Lua "goto stop_\($var as lua identifier)") - ..else: - return (Lua "break") - -(do next $var) compiles to: - if $var: - return (Lua "goto continue_\($var as lua identifier)") - ..else: - return (Lua "goto continue") - -(---stop $var ---) compiles to "::stop_\($var as lua identifier)::" -(---next $var ---) compiles to "::continue_\($var as lua identifier)::" - -# While loops -test: - $x = 0 - repeat while ($x < 10): $x += 1 - assume ($x == 10) - repeat while ($x < 20): stop - assume ($x == 10) - repeat while ($x < 20): - $x += 1 - if (yes): - do next - fail "Failed to 'do next'" - assume ($x == 20) - -(repeat while $condition $body) compiles to: - $lua = - Lua (" - while \($condition as lua expr) do - \($body as lua) - ") - - if ($body has subtree \(do next)): - $lua, add "\n ::continue::" - - $lua, add "\nend --while-loop" - return $lua - -(repeat $body) parses as (repeat while (yes) $body) -(repeat until $condition $body) parses as (repeat while (not $condition) $body) - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -test: - $nums = [] - for $x in 1 to 5: - $nums, add $x - assume ($nums == [1, 2, 3, 4, 5]) - $nums = [] - for $x in 1 to 5 via 2: - $nums, add $x - assume ($nums == [1, 3, 5]) - $nums = [] - for $outer in 1 to 100: - for $inner in $outer to ($outer + 2): - if ($inner == 2): - $nums, add -2 - do next $inner - $nums, add $inner - if ($inner == 5): - stop $outer - assume ($nums == [1, -2, 3, -2, 3, 4, 3, 4, 5]) - -# Numeric range for loops -[ - for $var in $start to $stop by $step $body - for $var in $start to $stop via $step $body -] all compile to: - # This uses Lua's approach of only allowing loop-scoped variables in a loop - $lua = - Lua (" - for \($var as lua identifier)=\($start as lua expr),\($stop as lua expr),\ - ..\($step as lua expr) do - ") - $lua, add "\n " ($body as lua) - if ($body has subtree \(do next)): - $lua, add "\n ::continue::" - - if ($body has subtree \(do next $var)): - $lua, add "\n " (\(---next $var ---) as lua) - - $lua, add "\nend -- numeric for " ($var as lua identifier) " loop" - if ($body has subtree \(stop $var)): - $lua = - Lua (" - do -- scope for (stop \($var as lua identifier)) - \$lua - \(\(---stop $var ---) as lua) - end -- scope for (stop \($var as lua identifier)) - ") - return $lua - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -(for $var in $start to $stop $body) parses as - for $var in $start to $stop via 1 $body - -test: - $x = 0 - repeat 5 times: - $x += 1 - assume $x == 5 - -(repeat $n times $body) parses as (for (=lua "_XXX_") in 1 to $n $body) -test: - $a = [10, 20, 30, 40, 50] - $b = [] - for $x in $a: - $b, add $x - assume ($a == $b) - $b = [] - for $x in $a: - if ($x == 10): - do next $x - - if ($x == 50): - stop $x - - $b, add $x - assume ($b == [20, 30, 40]) - -# For-each loop (lua's "ipairs()") -(for $var in $iterable at $i $body) compiles to: - # This uses Lua's approach of only allowing loop-scoped variables in a loop - $lua = - Lua (" - for \($i as lua identifier),\($var as lua identifier) in ipairs(\($iterable as lua expr)) do - \; - ") - $lua, add ($body as lua) - if ($body has subtree \(do next)): - $lua, add "\n ::continue::" - - if ($body has subtree \(do next $var)): - $lua, add "\n " (\(---next $var ---) as lua) - - $lua, add "\nend --for \($var as lua identifier) loop" - if ($body has subtree \(stop $var)): - $inner_lua = $lua - $lua = (Lua "do -- scope for stopping for-loop\n ") - $lua, add $inner_lua "\n " - $lua, add (\(---stop $var ---) as lua) - $lua, add "\nend -- end of scope for stopping for-loop" - return $lua - -(for $var in $iterable $body) parses as - for $var in $iterable at (=lua "__") $body - -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 - - $result, add "\$k = \$v" - assume (($result sorted) == ["c = 30", "d = 40", "e = 50"]) - -# Dict iteration (lua's "pairs()") -[for $key = $value in $iterable $body, for $key $value in $iterable $body] -..all compile to: - $lua = - Lua (" - for \($key as lua identifier),\($value as lua identifier) in pairs(\ - ..\($iterable as lua expr)) do - ") - $lua, add "\n " ($body as lua) - if ($body has subtree \(do next)): - $lua, add "\n ::continue::" - - if ($body has subtree \(do next $key)): - $lua, add "\n " (\(---next $key ---) as lua) - - if ($body has subtree \(do next $value)): - $lua, add "\n " (\(---next $value ---) as lua) - - $lua, add "\nend --foreach-loop" - $stop_labels = (Lua "") - if ($body has subtree \(stop $key)): - $stop_labels, add "\n" (\(---stop $key ---) as lua) - - if ($body has subtree \(stop $value)): - $stop_labels, add "\n" (\(---stop $value ---) as lua) - - if ((size of "\$stop_labels") > 0): - $inner_lua = $lua - $lua = (Lua "do -- scope for stopping for $ = $ loop\n ") - $lua, add $inner_lua $stop_labels "\nend" - - return $lua - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -test: - when: - (1 == 2) (100 < 0): - fail "bad conditional" - (1 == 0) (1 == 1) $not_a_variable.x: do nothing - (1 == 1): - fail "bad conditional" - - (1 == 2): - fail "bad conditional" - - else: - fail "bad conditional" - -# Multi-branch conditional (if..elseif..else) -(when $body) compiles to: - $code = (Lua "") - $clause = "if" - $else_allowed = (yes) - unless ($body.type is "Block"): - compile error at $body "'if' expected a Block, but got a \($body.type)." - "Perhaps you forgot to put a ':' after 'if'?" - - for $line in $body: - unless - (($line.type is "Action") and ((size of $line) >= 2)) and - $line.(size of $line) is "Block" syntax tree - ..: - compile error at $line "Invalid line for the body of an 'if' block." (" - Each line should contain one or more conditional expressions followed by a block, or "else"\ - .. followed by a block. - ") - $action = $line.(size of $line) - if (($line.1 is "else") and ((size of $line) == 2)): - unless $else_allowed: - compile error at $line "You can't have two 'else' blocks." - "Merge all of the 'else' blocks together." - - unless ((size of "\$code") > 0): - compile error at $line - .."You can't have an 'else' block without a preceding condition" (" - If you want the code in this block to always execute, you don't need a conditional block \ - ..around it. Otherwise, make sure the 'else' block comes last. - ") - - $code, add "\nelse\n " ($action as lua) - $else_allowed = (no) - ..else: - $code, add $clause " " - for $i in 1 to ((size of $line) - 1): - if ($i > 1): - $code, add " or " - $code, add ($line.$i as lua expr) - $code, add " then\n " ($action as lua) - $clause = "\nelseif" - - if ((size of "\$code") == 0): - compile error at $body "'if' block has an empty body." - "This means nothing would happen, so the 'if' block should be deleted." - - $code, add "\nend --when" - return $code - -test: - if 5 is: - 1 2 3: - fail "bad switch statement" - - 4 5: - do nothing - - 5 6: - fail "bad switch statement" - - else: - fail "bad switch statement" - -# Switch statement -[if $branch_value is $body, when $branch_value is $body] all compile to: - $code = (Lua "") - $clause = "if" - $else_allowed = (yes) - define mangler - unless ($body.type is "Block"): - compile error at $body "'if' expected a Block, but got a \($body.type)" - "Perhaps you forgot to put a ':' after the 'is'?" - - for $line in $body: - unless - (($line.type is "Action") and ((size of $line) >= 2)) and - $line.(size of $line) is "Block" syntax tree - ..: - compile error at $line "Invalid line for 'if' block." (" - Each line should contain expressions followed by a block, or "else" followed by a block - ") - $action = $line.(size of $line) - if (($line.1 is "else") and ((size of $line) == 2)): - unless $else_allowed: - compile error at $line "You can't have two 'else' blocks." - "Merge all of the 'else' blocks together." - - unless ((size of "\$code") > 0): - compile error at $line - .."You can't have an 'else' block without a preceding condition" (" - If you want the code in this block to always execute, you don't need a conditional block \ - ..around it. Otherwise, make sure the 'else' block comes last. - ") - - $code, add "\nelse\n " ($action as lua) - $else_allowed = (no) - ..else: - $code, add $clause " " - for $i in 1 to ((size of $line) - 1): - if ($i > 1): - $code, add " or " - $code, add "\(mangle "branch value") == " ($line.$i as lua expr) - $code, add " then\n " ($action as lua) - $clause = "\nelseif" - - if ((size of "\$code") == 0): - compile error at $body "'if' block has an empty body." - "This means nothing would happen, so the 'if' block should be deleted." - - $code, add "\nend --when" - return - Lua (" - do --if $ is... - local \(mangle "branch value") = \($branch_value as lua expr) - \$code - end -- if $ is... - ") - -# Do/finally -(do $action) compiles to (" - do - \($action as lua) - end -- do -") - -test: - assume ((result of: return 99) == 99) - -# Inline thunk: -(result of $body) compiles to "\(\(-> $body) as lua)()" -test: - $t = [1, [2, [[3], 4], 5, [[[6]]]]] - $flat = [] - for $ in recursive $t: - if ((lua type of $) is "table"): - for $2 in $: - recurse $ on $2 - ..else: - $flat, add $ - assume (sorted $flat) == [1, 2, 3, 4, 5, 6] - -# Recurion control flow -(recurse $v on $x) compiles to - Lua "table.insert(_stack_\($v as lua expr), \($x as lua expr))" -(for $var in recursive $structure $body) compiles to: - $lua = - Lua (" - do - local _stack_\($var as lua expr) = List{\($structure as lua expr)} - while #_stack_\($var as lua expr) > 0 do - \($var as lua expr) = table.remove(_stack_\($var as lua expr), 1) - \($body as lua) - ") - - if ($body has subtree \(do next)): - $lua, add "\n ::continue::" - - if ($body has subtree \(do next $var)): - $lua, add "\n \(\(---next $var ---) as lua)" - - $lua, add "\n end -- Recursive loop" - if ($body has subtree \(stop $var)): - $lua, add "\n \(\(---stop $var ---) as lua)" - $lua, add "\nend -- Recursive scope" - return $lua -- cgit v1.2.3