From 505fec2a61d2571317cc4bbc36ec0f4822a63f9d Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Fri, 2 Feb 2018 15:48:28 -0800 Subject: Restructured the nomsu files to group all the essentials into core/ and all the optionals into lib/. lib/core.nom and tests/all.nom are no longer needed now. --- lib/control_flow.nom | 467 --------------------------------------------------- 1 file changed, 467 deletions(-) delete mode 100644 lib/control_flow.nom (limited to 'lib/control_flow.nom') diff --git a/lib/control_flow.nom b/lib/control_flow.nom deleted file mode 100644 index 75cf6fd..0000000 --- a/lib/control_flow.nom +++ /dev/null @@ -1,467 +0,0 @@ -#.. - This file contains compile-time actions that define basic control flow structures - like "if" statements and loops. - -use "lib/metaprogramming.nom" -use "lib/text.nom" -use "lib/operators.nom" - -# No-Op -immediately: - compile [do nothing] to {statements:""} - -# Conditionals -immediately: - compile [if %condition %if_body] to: - %if_body <- (%if_body as lua) - return {..} - locals: %if_body's "locals" - statements:".." - if \(%condition as lua expr) then - \((%if_body's "statements") or "\(%if_body's "expr");") - end - parse [unless %condition %unless_body] as: if (not %condition) %unless_body - - compile [if %condition %if_body else %else_body, unless %condition %else_body else %if_body] to: - %if_body <- (%if_body as lua) - %else_body <- (%else_body as lua) - lua> ".." - local \%locals = {unpack(\%if_body.locals or {})}; - for i,loc in ipairs(\%else_body.locals or {}) do table.insert(\%locals, loc); end - utils.deduplicate(\%locals); - return {..} - locals:%locals - statements:".." - if \(%condition as lua expr) then - \((%if_body's "statements") or "\(%if_body's "expr");") - else - \((%else_body's "statements") or "\(%else_body's "expr");") - 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" -immediately - compile [..] - %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 - ..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: (%when_true_expr's "type") in {Text:yes, List:yes, Dict:yes, Number:yes} - return {..} - expr:"(\(%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 {..} - expr: ".." - (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 -immediately: - compile [=== %label ===, --- %label ---, *** %label ***] to {..} - statements:"::label_\(%label as lua identifier)::;" - compile [go to %label] to {..} - statements:"goto label_\(%label as lua identifier);" - -# Basic loop control -immediately: - compile [do next] to {statements:"continue;"} - compile [stop] to {statements:"break;"} - -# Helper function -immediately: - compile [if %tree has subtree %subtree where %condition %body] to: - %body_lua <- (%body as lua) - %body_statements <- ((%body_lua's "statements") or "\(%body_lua's "expr");") - return {..} - locals: %body_lua's "locals" - statements:".." - for \(%subtree as lua expr) in coroutine.wrap(function() nomsu:walk_tree(\(%tree as lua expr)) end) do - if type(\(%subtree as lua expr)) == 'table' and \(%subtree as lua expr).type then - if \(%condition as lua expr) then - \%body_statements - break; - end - end - end - -# While loops -immediately: - compile [do next repeat] to {statements:"goto continue_repeat;"} - compile [stop repeating] to {statements:"goto stop_repeat;"} - compile [repeat while %condition %body] to - %body_lua <- (%body as lua) - %body_statements <- ((%body_lua's "statements") or "\(%body_lua's "expr");") - if %body has subtree % where: - ((%'s "type") = "FunctionCall") and ((%'s "stub") is "do next repeat") - ..: %body_statments +<- "\n::continue_repeat::;" - %code <- ".." - while \(%condition as lua expr) do - \%body_statements - end --while-loop - if %body has subtree % where: - ((%'s "type") = "FunctionCall") and ((%'s "stub") is "stop repeating") - ..: - %code <- ".." - do -- scope of "stop repeating" label - \%code - ::stop_repeat::; - end -- end of "stop repeating" label scope - return {statements:%code, locals:%body_lua's "locals"} - 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: - %body_lua <- (%body as lua) - %body_statements <- ((%body_lua's "statements") or "\(%body_lua's "expr");") - if %body has subtree % where - ((%'s "type") = "FunctionCall") and ((%'s "stub") is "do next repeat") - ..: %body_statements +<- "\n::continue_repeat::;" - %code <- ".." - for i=1,\(%n as lua expr) do - \%body_statements - end --numeric for-loop - if %body has subtree % where: - ((%'s "type") = "FunctionCall") and ((%'s "stub") is "stop repeating") - ..: - %code <- ".." - do -- scope of "stop repeating" label - \%code - ::stop_repeat::; - end -- end of "stop repeating" label scope - return {statements:%code, locals:%body_lua's "locals"} - -# For loop control flow: -immediately: - compile [stop %var] to {..} - statements:"goto stop_\(%var as lua identifier);" - compile [do next %var] to {..} - statements:"goto continue_\(%var as lua identifier);" - -# Numeric range for loops -immediately: - compile [..] - for %var from %start to %stop by %step %body - for %var from %start to %stop via %step %body - ..to: - %body_lua <- (%body as lua) - %body_statements <- ((%body_lua's "statements") or "\(%body_lua's "expr");") - if %body has subtree % where: - ((%'s "type") = "FunctionCall") and - ((%'s "stub") is "do next %") and - ((3rd in (%'s "value"))'s "value") is (%var's "value") - ..: %body_statements +<- "\n::continue_\(%var as lua identifier)::;" - - # This uses Lua's approach of only allowing loop-scoped variables in a loop - assume ((%var's "type") is "Var") or barf "Loop expected variable, not: \(%var's source code)" - %code <- ".." - for \(%var as lua expr)=\(%start as lua expr),\(%stop as lua expr),\(%step as lua expr) do - \%body_statements - end --numeric for-loop - - if %body has subtree % where: - ((%'s "type") = "FunctionCall") and: - ((%'s "stub") is "stop %") and: - ((2nd in (%'s "value"))'s "value") is (%var's "value") - ..: - %code <- ".." - do -- scope for stopping for-loop - \%code - ::stop_\(%var as lua identifier)::; - end -- end of scope for stopping for-loop - - return {statements:%code, locals:%body_lua's "locals"} - -immediately: - parse [for %var from %start to %stop %body] as: for %var from %start to %stop via 1 %body - parse [..] - for all %start to %stop by %step %body - for all %start to %stop via %step %body - ..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 (lua's "ipairs()") -immediately: - compile [for %var in %iterable %body] to: - %body_lua <- (%body as lua) - %body_statements <- ((%body_lua's "statements") or "\(%body_lua's "expr");") - if %body has subtree % where: - ((%'s "type") = "FunctionCall") and - ((%'s "stub") is "do next %") and - ((3rd in (%'s "value"))'s "value") is (%var's "value") - ..: %body_statements +<- "\n::continue_\(%var as lua identifier)::;" - # This uses Lua's approach of only allowing loop-scoped variables in a loop - assume ((%var's "type") is "Var") or barf "Loop expected variable, not: \(%var's source code)" - %code <- ".." - for i,\(%var as lua expr) in ipairs(\(%iterable as lua expr)) do - \%body_statements - end --foreach-loop - if %body has subtree % where: - ((%'s "type") = "FunctionCall") and - ((%'s "stub") is "stop %") and - ((2nd in (%'s "value"))'s "value") is (%var's "value") - ..: - %code <- ".." - do -- scope for stopping for-loop - \%code - ::stop_\(%var as lua identifier)::; - end -- end of scope for stopping for-loop - return {statements:%code, locals:%body_lua's "locals"} - - parse [for all %iterable %body] as: for % in %iterable %body - -# Dict iteration (lua's "pairs()") -immediately: - compile [for %key = %value in %iterable %body] to: - %body_lua <- (%body as lua) - %body_statements <- ((%body_lua's "statements") or "\(%body_lua's "expr");") - if %body has subtree % where: - ((%'s "type") = "FunctionCall") and - ((%'s "stub") is "do next %") and - ((3rd in (%'s "value"))'s "value") is (%key's "value") - ..: %body_statements +<- "\n::continue_\(%key as lua identifier)::;" - - if %body has subtree % where: - ((%'s "type") = "FunctionCall") and - ((%'s "stub") is "do next %") and - ((3rd in (%'s "value"))'s "value") is (%value's "value") - ..: %body_statements +<- "\n::continue_\(%value as lua identifier)::;" - - # This uses Lua's approach of only allowing loop-scoped variables in a loop - assume ((%key's "type") is "Var") or barf "Loop expected variable, not: \(%key's source code)" - assume ((%value's "type") is "Var") or barf "Loop expected variable, not: \(%value's source code)" - %code <- ".." - for \(%key as lua expr),\(%value as lua expr) in pairs(\(%iterable as lua expr)) do - \%body_statements - end --foreach-loop - - %stop_labels <- "" - if %body has subtree % where: - ((%'s "type") = "FunctionCall") and - ((%'s "stub") is "stop %") and - ((2nd in (%'s "value"))'s "value") is (%key's "value") - ..: %stop_labels +<- "\n::stop_\(%key as lua identifier)::;" - - if %body has subtree % where: - ((%'s "type") = "FunctionCall") and - ((%'s "stub") is "stop %") and - ((2nd in (%'s "value"))'s "value") is (%value's "value") - ..: %stop_labels +<- "\n::stop_\(%value as lua identifier)::;" - - if: %stop_labels is not "" - %code <- ".." - do -- scope for stopping for % = % loop - \%code\%stop_labels - end - return {statements:%code, locals:%body_lua's "locals"} - -# Switch statement/multi-branch if -immediately: - compile [when %body] to: - %code <- "" - %fallthroughs <- [] - %locals <- [] - %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. - 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> "table.insert(\%fallthroughs, \(%condition as lua expr));" - do next %func_call - %action <- (%action as lua) - %action_statements <- ((%action's "statements") or "\(%action's "expr");") - for %local in ((%action's "locals") or []): - lua> "table.insert(\%locals, \%local);" - - if: =lua "\%condition.type == 'Word' and \%condition.value == 'else'" - assume (not %is_first) or barf "'else' clause cannot be first in 'when % = ?' block" - %code +<- ".." - - else - \%action_statements - %seen_else <- (yes) - ..else: - assume (not %seen_else) or barf "'else' clause needs to be last in 'when' block" - lua> "table.insert(\%fallthroughs, \(%condition as lua expr));" - %condition_code <- (%fallthroughs joined with " or ") - %code +<- ".." - - \("if" if %is_first else "elseif") \%condition_code then - \%action_statements - - %fallthroughs <- [] - %is_first <- (no) - - assume (%fallthroughs = []) or barf "Unfinished fallthrough conditions in 'when' block" - if: %code is not "" - %code +<- "\nend" - lua> "utils.deduplicate(\%locals);" - return {statements:%code, locals:%locals} - -# Switch statement -immediately: - compile [when %branch_value = ? %body, when %branch_value is ? %body] to: - %code <- "" - %fallthroughs <- [] - %locals <- [] - %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. - %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 as lua expr))" - do next %func_call - - %action <- (%action as lua) - %action_statements <- ((%action's "statements") or "\(%action's "expr");") - for %local in ((%action's "locals") or []): - lua> "table.insert(\%locals, \%local);" - - if: =lua "\%condition.type == 'Word' and \%condition.value == 'else'" - assume (not %is_first) or barf "'else' clause cannot be first in 'when % = ?' block" - %code +<- ".." - - else - \%action_statements - end - %seen_else <- (yes) - ..else: - assume (not %seen_else) or barf "'else' clause needs to be last in 'when % = ?' block" - lua> "table.insert(\%fallthroughs, \(%condition as lua expr));" - for %i = % in %fallthroughs - if: ((%'s "type") is "Text") or ((%'s "type") is "Number") - (%i'th in %fallthroughs) <- "branch_value == \%" - ..else - (%i'th in %fallthroughs) <- "utils.equivalent(branch_value, \%)" - %clause <- (%fallthroughs joined with " or ") - %code +<- ".." - - \("if" if %is_first else "elseif") \%clause then - \%action_statements - - %fallthroughs <- [] - %is_first <- (no) - - assume (%fallthroughs = []) or barf "Unfinished fallthrough conditions in 'when' block" - assume (%code is not "") or barf "No body for 'when % = ?' block!" - unless %seen_else - %code +<- "\nend" - %code <- ".." - do --when % = ? - local branch_value = \(%branch_value as lua expr);\ - ..\%code - end --when % = ? - lua> "utils.deduplicate(\%locals);" - return {statements:%code, locals:%locals} - -# Try/except -immediately: - compile [..] - try %action and if it succeeds %success or if it barfs %fallback - try %action and if it barfs %fallback or if it succeeds %success - ..to: - %locals <- [] - %action_lua <- (%action as lua) - %success_lua <- (%success as lua) - %fallback_lua <- (%fallback as lua) - %fallback <- (%fallback as lua) - for %block in [%action_lua, %success_lua, %fallback_lua]: - for %local in ((%block's "locals") or []): - lua> "table.insert(\%locals, \%local);" - lua> "utils.deduplicate(\%locals);" - return {..} - locals: %locals - statements: ".." - do - local fell_through = false; - local ok, ret = pcall(function() - \((%action_lua's "statements") or "\(%action_lua's "expr");") - fell_through = true; - end); - if ok then - \((%success_lua's "statements") or "\(%success_lua's "expr");") - end - if not ok then - \((%fallback_lua's "statements") or "\(%fallback_lua's "expr");") - elseif not fell_through then - return ret; - end - end - parse [try %action] as: - try %action and if it succeeds: do nothing - ..or if it barfs: do nothing - parse [try %action and if it barfs %fallback] as: - try %action and if it succeeds: do nothing - ..or if it barfs %fallback - parse [try %action and if it succeeds %success] as: - try %action and if it succeeds %success or if it barfs: do nothing - -# Do/finally: -immediately: - compile [do %action] to: - %action <- (%action as lua) - return {..} - locals: %action's "locals" - statements: ".." - do - \((%action's "statements") or "\(%action's "expr");") - end - - compile [do %action then always %final_action] to: - %action <- (%action as lua) - %final_action <- (%final_action as lua) - %locals <- [] - for %sub_locals in [%action's "locals", %final_action's "locals"]: - for %local in %sub_locals: - lua> "table.insert(\%locals, \%local);" - lua> "utils.deduplicate(\%locals);" - return {..} - locals: %locals - statements: ".." - do - local fell_through = false; - local ok, ret1 = pcall(function() - \((%action's "statements") or "\(%action's "expr");") - fell_through = true; - end); - local ok2, ret2 = pcall(function() - \((%final_action's "statements") or "\(%final_action's "expr");") - end); - if not ok then error(ret1); end - if not ok2 then error(ret2); end - if not fell_through then - return ret1; - end - end - -- cgit v1.2.3