commit 2325713c2f574aa08b5ad1bb91a3e4ee30b6b0b5
parent 7dc3033c862c30d8998ed3e4c98c056286f31401
Author: Drazen Borkovic <borkovic@users.noreply.github.com>
Date: Sat, 26 Aug 2017 16:19:50 -0700
Add "continue" builtin
Diffstat:
M | builtins.c | | | 19 | +++++++++++++++---- |
M | except.c | | | 9 | ++++++--- |
M | input.c | | | 4 | ++-- |
M | rc.h | | | 4 | ++-- |
M | trip.rc | | | 72 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | walk.c | | | 83 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------- |
6 files changed, 154 insertions(+), 37 deletions(-)
diff --git a/builtins.c b/builtins.c
@@ -20,7 +20,7 @@
#include "rlimit.h"
#include "sigmsgs.h"
-static void b_break(char **), b_cd(char **), b_eval(char **), b_exit(char **),
+static void b_break(char **), b_cd(char **), b_continue(char **), b_eval(char **), b_exit(char **),
b_newpgrp(char **), b_return(char **), b_shift(char **), b_umask(char **),
b_wait(char **), b_whatis(char **);
@@ -39,6 +39,7 @@ static struct {
{ b_break, "break" },
{ b_builtin, "builtin" },
{ b_cd, "cd" },
+ { b_continue, "continue" },
#if RC_ECHO
{ b_echo, "echo" },
#endif
@@ -83,8 +84,8 @@ extern void funcall(char **av) {
except(eVarstack, star, &e2);
walk(treecpy(fnlookup(*av), nalloc), TRUE);
varrm("*", TRUE);
- unexcept(); /* eVarstack */
- unexcept(); /* eReturn */
+ unexcept(eVarstack);
+ unexcept(eReturn);
}
static void arg_count(char *name) {
@@ -216,6 +217,16 @@ static void b_break(char **av) {
rc_raise(eBreak);
}
+/* raise a "continue" exception to finish early an iteration of 'for' and 'while' loops */
+
+static void b_continue(char **av) {
+ if (av[1] != NULL) {
+ arg_count("continue");
+ return;
+ }
+ rc_raise(eContinue);
+}
+
/* shift $* n places (default 1) */
static void b_shift(char **av) {
@@ -401,7 +412,7 @@ extern void b_dot(char **av) {
except(eVarstack, star, &e);
doit(TRUE);
varrm("*", TRUE);
- unexcept(); /* eVarstack */
+ unexcept(eVarstack);
interactive = old_i;
}
diff --git a/except.c b/except.c
@@ -22,13 +22,14 @@ extern void except(ecodes e, Edata data, Estack *ex) {
estack = ex;
estack->e = e;
estack->data = data;
- if (e == eError || e == eBreak || e == eReturn)
+ if (e == eError || e == eBreak || e == eReturn || e == eContinue)
estack->interactive = interactive;
}
/* remove an exception, restore last interactive value */
-extern void unexcept() {
+extern void unexcept(ecodes e) {
+ assert(e == estack->e);
switch (estack->e) {
default:
break;
@@ -66,8 +67,10 @@ extern void rc_raise(ecodes e) {
exit(1); /* child processes exit on an error/signal */
for (; estack != NULL; estack = estack->prev)
if (estack->e != e) {
- if (e == eBreak && estack->e != eArena && estack->e != eVarstack)
+ if (e == eBreak && (estack->e != eArena && estack->e != eVarstack && estack->e != eContinue))
rc_error("break outside of loop");
+ else if (e == eContinue && (estack->e != eVarstack))
+ rc_error("continue outside of loop");
else if (e == eReturn && estack->e == eError) /* can return from loops inside functions */
rc_error("return outside of function");
switch (estack->e) {
diff --git a/input.c b/input.c
@@ -314,10 +314,10 @@ extern Node *doit(bool clobberexecit) {
else if (dashex && dashen)
fprint(2, "%T\n", parsetree);
}
- unexcept(); /* eArena */
+ unexcept(eArena);
}
popinput();
- unexcept(); /* eError */
+ unexcept(eError);
return parsetree;
}
diff --git a/rc.h b/rc.h
@@ -41,7 +41,7 @@ typedef enum nodetype {
} nodetype;
typedef enum ecodes {
- eError, eBreak, eReturn, eVarstack, eArena, eFifo, eFd
+ eError, eBreak, eReturn, eVarstack, eArena, eFifo, eFd, eContinue
} ecodes;
typedef enum bool {
@@ -185,7 +185,7 @@ extern bool outstanding_cmdarg(void);
extern void pop_cmdarg(bool);
extern void rc_raise(ecodes);
extern void except(ecodes, Edata, Estack *);
-extern void unexcept(void);
+extern void unexcept(ecodes e);
extern void rc_error(char *);
extern void sigint(int);
diff --git a/trip.rc b/trip.rc
@@ -598,6 +598,78 @@ x=$tmpdir/qux*/foo
rm -rf $tmpdir
+#############################################################################
+## Check builtin continue in while loop.
+#############################################################################
+q=''''
+C=$q^while-continue$q
+L=()
+
+ten = a^(1 2 3 4 5 6 7 8 9 10)
+* = $ten
+for (n) {
+ if (~ $n a2 a3) {
+ continue
+ }
+ L=($L $n)
+}
+if (!~ $#L 8) {
+ fail Wrong length of list from $C: $#L
+}
+if (!~ $L(1) a1) {
+ fail First element of $C list is not a1: $L(1)
+}
+if (!~ $L(2) a4) {
+ fail Second element of $C list is not a4: $L(2)
+}
+C=() L=()
+
+#############################################################################
+## Check builtin continue in for loop.
+#############################################################################
+C=$q^for-continue$q
+L=()
+
+for (x in a b c d e f g) {
+ if (~ $x c f) {
+ continue
+ }
+ L=($L $x)
+}
+if (!~ $#L 5) {
+ fail Wrong length of list from $C
+}
+if (!~ $L(1) a) {
+ fail First element of $C list is not a: $L(1)
+}
+if (!~ $L(3) d) {
+ fail Third element of $C list is not d: $L(3)
+}
+if (!~ $L(5) g) {
+ fail Fifth element of $C list is not g: $L(5)
+}
+C=() L=()
+
+submatch continue 'rc: continue outside of loop' 'continue outside of loop'
+
+#############################################################################
+## check builtin continue in for loop (2)
+#############################################################################
+L=()
+for (x in a b c d e f g) {
+ if (~ $x b d) {
+ continue
+ }
+ L=($L $x)
+ if (~ $x f) {
+ break;
+ }
+}
+
+if (!~ $^L 'a c e f') {
+ fail List should be '(a c e f)', but is $L
+}
+
# test support for unquoted =
submatch 'echo foo = bar' 'foo = bar' 'unquoted equals 2'
submatch 'echo foo=bar' 'foo=bar' 'unquoted equals 2'
diff --git a/walk.c b/walk.c
@@ -17,6 +17,7 @@ static bool haspreredir(Node *);
static bool isallpre(Node *);
static bool dofork(bool);
static void dopipe(Node *);
+static void loop_body(Node* n);
/* Tail-recursive version of walk() */
@@ -101,52 +102,58 @@ top: sigchk();
WALK(true_cmd, parent);
}
case nWhile: {
- Jbwrap j;
- Edata jbreak;
- Estack e1, e2;
- bool testtrue, oldcond = cond;
+ Jbwrap break_jb;
+ Edata break_data;
+ Estack break_stack;
+ bool testtrue;
+ const bool oldcond = cond;
cond = TRUE;
if (!walk(n->u[0].p, TRUE)) { /* prevent spurious breaks inside test */
cond = oldcond;
break;
}
cond = oldcond;
- if (sigsetjmp(j.j, 1))
+ if (sigsetjmp(break_jb.j, 1))
break;
- jbreak.jb = &j;
- except(eBreak, jbreak, &e1);
+ break_data.jb = &break_jb;
+ except(eBreak, break_data, &break_stack);
+
+ cond = oldcond;
do {
- Edata block;
- block.b = newblock();
- except(eArena, block, &e2);
- walk(n->u[1].p, TRUE);
+ Edata iter_data;
+ Estack iter_stack;
+ iter_data.b = newblock();
+ except(eArena, iter_data, &iter_stack);
+ loop_body(n->u[1].p);
cond = TRUE;
testtrue = walk(n->u[0].p, TRUE);
cond = oldcond;
- unexcept(); /* eArena */
+ unexcept(eArena);
} while (testtrue);
cond = oldcond;
- unexcept(); /* eBreak */
+ unexcept(eBreak);
break;
}
case nForin: {
List *l, *var = glom(n->u[0].p);
- Jbwrap j;
- Estack e1, e2;
- Edata jbreak;
- if (sigsetjmp(j.j, 1))
+ Jbwrap break_jb;
+ Edata break_data;
+ Estack break_stack;
+ if (sigsetjmp(break_jb.j, 1))
break;
- jbreak.jb = &j;
- except(eBreak, jbreak, &e1);
+ break_data.jb = &break_jb;
+ except(eBreak, break_data, &break_stack);
+
for (l = listcpy(glob(glom(n->u[1].p)), nalloc); l != NULL; l = l->n) {
- Edata block;
+ Edata iter_data;
+ Estack iter_stack;
assign(var, word(l->w, NULL), FALSE);
- block.b = newblock();
- except(eArena, block, &e2);
- walk(n->u[2].p, TRUE);
- unexcept(); /* eArena */
+ iter_data.b = newblock();
+ except(eArena, iter_data, &iter_stack);
+ loop_body(n->u[2].p);
+ unexcept(eArena);
}
- unexcept(); /* eBreak */
+ unexcept(eBreak);
break;
}
case nSubshell:
@@ -240,7 +247,7 @@ top: sigchk();
except(eVarstack, var, &e);
walk(n->u[1].p, parent);
varrm(v->w, TRUE);
- unexcept(); /* eVarstack */
+ unexcept(eVarstack);
}
} else
panic("unexpected node in preredir section of walk");
@@ -364,3 +371,27 @@ static void dopipe(Node *n) {
setpipestatus(stats, i);
sigchk();
}
+
+/* From http://en.cppreference.com/w/c/program/setjmp
+ * According to the C standard setjmp() must appear only in the following 4 constructs:
+ * 1. switch (setjmp(args)) {statements}
+ * 2. if (setjmp(args) == Const) {statements} with any of
+ * operators: ==, !=, <, >, <=, >=
+ * 3. while (! setjmp(args)) {statements}
+ * 4. setjmp(args);
+*/
+static void loop_body(Node* nd)
+{
+ Node *volatile n = nd;
+ Jbwrap cont_jb;
+ Edata cont_data;
+ Estack cont_stack;
+
+ if (sigsetjmp(cont_jb.j, 1) == 0) {
+ cont_data.jb = &cont_jb;
+ except(eContinue, cont_data, &cont_stack);
+ walk(n, TRUE);
+ unexcept(eContinue);
+ }
+}
+