Skip to content
Browse files

Remove return, break, add new escape function

	Escape is an escape continuation support, basically just a prettier use of exceptions.
	Return was very ugly, break had dynamic instead of lexical scope.
	With escape continuations, we have none of this problem.
  • Loading branch information...
1 parent b0286de commit d3a0da7755fe8bf01de6271ef8f29e1372071bc6 @frytvm committed Mar 21, 2010
Showing with 71 additions and 107 deletions.
  1. +1 −37 eval.cxx
  2. +35 −41 initial.xs
  3. +4 −3 test.xs
  4. +19 −22 xs.1
  5. +12 −4 xs_tests/control.xs
View
38 eval.cxx
@@ -310,41 +310,12 @@ extern const List *eval(const List* list, Binding* binding, int flags) {
break;
case nLambda:
{
- Tree* tree = cp->tree;
+ Tree* tree = cp->tree;
- /* define a return function */
-
- static unsigned int retid = 0;
-
- const char *id = str("%ud", retid++);
- Term *id_term = gcnew(Term);
- id_term->str = id;
- id_term->closure = NULL;
-
- List *id_def = gcnew(List);
- id_def->term = id_term;
- id_def->next = NULL;
-
- static Term return_term = { "return", NULL };
- List *return_def = gcnew(List);
- return_def->term = &return_term;
- return_def->next = id_def;
-
- static Term throw_term = { "throw", NULL };
- List *throw_def = gcnew(List);
- throw_def->term = &throw_term;
- throw_def->next = return_def;
- assert(termeq(&return_term, "return"));
- assert(return_def->next);
- assert(termeq(return_def->next->term, id_term->str));
-
- try {
Binding* context = bindargs(tree->u[0].p,
list->next,
cp->binding);
- context = mkbinding("fn-return", throw_def, context);
-
#define WALKFN walk(tree->u[1].p, context, flags)
if (funcname) {
Dyvar p("0",
@@ -355,13 +326,6 @@ extern const List *eval(const List* list, Binding* binding, int flags) {
list = WALKFN;
} else list = WALKFN;
#undef WALKFN
- } catch (List *e) {
- if (termeq(e->term, "return") && e->next && termeq(e->next->term, id)) {
- list = e->next->next;
- goto done;
- }
- throw e;
- }
}
break;
case nList: {
View
76 initial.xs
@@ -63,7 +63,6 @@
fn-. = $&dot
fn-access = $&access
-fn-break = $&break
fn-catch = $&catch
fn-echo = $&echo
fn-exec = $&exec
@@ -95,12 +94,10 @@ fn-false = result 1
# These functions just generate exceptions for control-flow
-# constructions. The for command and the while builtin both
-# catch the break exception.
+# constructions.
# The interpreter main() routine (and nothing else)
# catches the exit exception.
-fn-break = throw break
fn-exit = throw exit
@@ -112,6 +109,20 @@ fn-if = { |condition action else actions|
})
}
+let (nextid = 0) {
+ fn-escape = { |body|
+ nextid = `($nextid + 1)
+ let (id = _escape$nextid) {
+ catch { |e val|
+ (if {!~ $e $id} { throw $e }
+ else { result $val })
+ } {
+ $body { |val| throw $id $val }
+ }
+ }
+ }
+}
+
# unwind-protect is a simple wrapper around catch that is used
# to ensure that some cleanup code is run after running a code
# fragment. This function must be written with care to make
@@ -185,49 +196,36 @@ fn-whatis = { |args|
# The while function is implemented with the forever looping primitive.
# While uses to indicate that, while it is a lambda, it
-# does not catch the return exception. It does, however, catch break.
+# does not catch the return exception.
-fn-while = { |cond body|
- catch { |e value|
- if {!~ $e break} {
- throw $e $value
- }
- result $value
- } {
- let (result = <=true)
- forever {
- if {!$cond} {
- throw break $result
- } else {
- result = <=$body
- }
+fn-while = { |cond body| escape { |fn-return|
+ let (result = <=true)
+ forever {
+ if {!$cond} {
+ return $result
+ } else {
+ result = <=$body
}
- }
-}
+ }
+}}
fn-until = { |cond body|
while { ! $cond } $body
}
-fn-switch = { |value args|
+fn-switch = { |value args| escape { |fn-return|
if {~ $args ()} {
throw error switch 'usage: switch value [case1 action1] [case2 action2]...default'
}
- catch { |e value|
- if {!~ $e break} {
- throw $e $value
- }
- result $value
- } {
- for (cond action) $args {
- if {~ $action ()} {
- # Code for default action
- break <={$cond}
- }
- ~ $value $cond && break <={$action}
+ for (cond action) $args {
+ if {~ $action ()} {
+ # Code for default action
+ result <={$cond}
+ } else {
+ ~ $value $cond && return <={$action}
}
}
-}
+}}
# Somewhat like bash's alias, but simpler
# Create's new method named aliasname which
@@ -714,18 +712,14 @@ if {~ <=$&primitives execfailure} {fn-%exec-failure = $&execfailure}
# Other than eof, %interactive-loop does not exit on exceptions,
# where %batch-loop does.
#
-# The looping construct forever is used rather than while, because
-# while catches the break exception, which would make it difficult
-# to print ``break outside of loop'' errors.
-#
# The parsed code is executed only if it is non-empty, because otherwise
# result gets set to zero when it should not be.
fn-%parse = $&parse
fn-%batch-loop = $&batchloop
fn-%is-interactive = $&isinteractive
-fn %interactive-loop {
+fn %interactive-loop { escape |fn-return| {
let (result = <=true) {
catch { |e type msg|
(switch $e
@@ -752,7 +746,7 @@ fn %interactive-loop {
}
}
}
-}
+}}
# These functions are potentially passed to a REPL as the %dispatch
# function. (For %eval-noprint, note that an empty list prepended
View
7 test.xs
@@ -47,7 +47,7 @@ let (passes = 0
exit $fails
}
}
-fn conds { |requirements|
+fn conds { |requirements| escape { |fn-return|
for req $requirements {
if {! eval $req} {
log $req failed
@@ -56,9 +56,10 @@ fn conds { |requirements|
}
}
pass
-}
+}}
+
fn expect-success { ||
- return $RETURNVALUE
+ result $RETURNVALUE
}
fn expect-failure {
! expect-success
View
41 xs.1
@@ -360,14 +360,14 @@ Since lists can span multiple lines without explicit line
continuations, they are ideal for long commands. For example:
.Ds
.Cr "switch $x \e"
-.Cr " error { return 1 } \e"
+.Cr " error { result 1 } \e"
.Cr " warning { break } \e"
.Cr " good { echo no problem }"
.De
.PP is the same as
.Ds
.Cr "switch $x ("
-.Cr " error { break }"
+.Cr " error { result 1 }"
.Cr " warning { break }"
.Cr " good { echo no problem }"
.Cr ")"
@@ -1444,9 +1444,7 @@ Exceptions in
are used for many forms of non-structured control flow,
notably error reporting, signals, and flow of control
constructs such as
-.Cr break
-and
-.Cr return .
+.Cr escape .
.PP
Exceptions are passed up the call chain to catching routines.
A catcher may decide to intercept an exception,
@@ -1459,10 +1457,6 @@ The first word of an exception is, by convention, the type of exception
being raised.
The following exceptions are known:
.TP
-.Cr "break \fIvalue\fP"
-Exit from a loop or a switch.
-The return value of the loop/switch is the argument to the exception.
-.TP
.Cr eof
Raised by
.Cr %parse
@@ -1485,14 +1479,6 @@ causes the body of the
.Cr catch
clause to be run again.
.TP
-.Cr "return \fIvalue\fP"
-Causes the lexically-matching function to exit, with
-.I value
-as the return value (exit status). This function is somewhat special
-in that it exists only in the scope of it's matching function.
-Also, while it is implemented with exceptions, return is lexically-scoped
-as far as the user is concerned.
-.TP
.Cr "signal \fIsigname\fP"
Raised when the shell itself receives a signal,
and the signal is listed in the variable
@@ -1926,6 +1912,22 @@ then all other arguments are echoed literally;
this is used for echoing a literal
.Cr "\-n" .
.TP
+.Cr "escape \fIlambda\fP"
+Run lambda with one argument, an escape block
+which when evaulated will return to the point after this escape.
+This is more formally refered to as an escape continuation.
+In fact, it's behaviour is a simple subset of exceptions, and is implemented
+fairly simply using catch.
+Escape is useful to replace return/break like constructs; for example
+.Ds
+.Cr "fn f { escape |fn-return| {
+.Cr " ...; return 0;"
+.Cr " ..."
+.Cr "}"
+.De
+.TP
+will exit the function with result 0 when it reached the return.
+.TP
.Cr "eval \fIlist\fP"
Concatenates the elements of
.I list
@@ -2124,11 +2126,6 @@ This is
.IR xs 's
identity function.
.TP
-.Cr "return \fIvalue\fP"
-Causes the current function to exit,
-returning the named
-.IR value .
-.TP
.Cr "switch \fIvalue\fP \fI\fR[\fPcase1 action1\fR]\fP\fP...\fI\fR[\fPdefault-action\fR]\fP\fP"
Go through the list of cases,
testing if they are equal to
View
16 xs_tests/control.xs
@@ -13,10 +13,18 @@ run 'Simple until test' {
}
conds expect-success { match good }
-run 'Lexical return' {
- fn a { |ars| $ars }
- fn b { || a {return}; exit 1 }
- b
+run 'Escape escapes' {
+ escape { |e|
+ $e
+ exit 1
+ }
exit 0
}
conds expect-success
+
+run 'Escape val' {
+ echo <={escape { |e|
+ $e 5
+ }}
+}
+conds {match 5}

0 comments on commit d3a0da7

Please sign in to comment.
Something went wrong with that request. Please try again.