aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Bennett <steveb@workware.net.au>2023-05-13 10:07:50 +1000
committerSteve Bennett <steveb@workware.net.au>2023-05-25 08:56:24 +1000
commit1b151f816f14b11f1c1ef10b171411e21b9a504e (patch)
tree7e4702015b4ae17082283504e1f1bdc99f62093a
parente0ea457f4f9e9dd8a7836e826cc9d2a5e607e698 (diff)
downloadjimtcl-1b151f816f14b11f1c1ef10b171411e21b9a504e.zip
jimtcl-1b151f816f14b11f1c1ef10b171411e21b9a504e.tar.gz
jimtcl-1b151f816f14b11f1c1ef10b171411e21b9a504e.tar.bz2
core: support multi-level while, break from loops
loop i 5 { loop j 6 { # This breaks out of both loops break 2 } } Signed-off-by: Steve Bennett <steveb@workware.net.au>
-rw-r--r--jim.c77
-rw-r--r--tests/jim.test22
2 files changed, 76 insertions, 23 deletions
diff --git a/jim.c b/jim.c
index 11f4efb..d960f54 100644
--- a/jim.c
+++ b/jim.c
@@ -12281,6 +12281,23 @@ static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar
return JIM_OK;
}
+/**
+ * All commands that support break, continue from a loop (while, loop, foreach, for)
+ * use this to check for returnLevel.
+ *
+ * If returnLevel is > 0, decrements the returnLevel and returns 1.
+ * Otherwise returns 0
+ */
+static int JimCheckLoopRetcode(Jim_Interp *interp, int retval)
+{
+ if (retval == JIM_BREAK || retval == JIM_CONTINUE) {
+ if (--interp->returnLevel > 0) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
/* [while] */
static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
@@ -12299,13 +12316,14 @@ static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar
break;
if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
+ if (JimCheckLoopRetcode(interp, retval)) {
+ return retval;
+ }
switch (retval) {
case JIM_BREAK:
goto out;
- break;
case JIM_CONTINUE:
continue;
- break;
default:
return retval;
}
@@ -12321,6 +12339,7 @@ static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv
{
int retval;
int boolean = 1;
+ int immediate = 0;
Jim_Obj *varNamePtr = NULL;
Jim_Obj *stopVarNamePtr = NULL;
@@ -12441,6 +12460,10 @@ static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv
/* Eval body */
retval = Jim_EvalObj(interp, argv[4]);
+ if (JimCheckLoopRetcode(interp, retval)) {
+ immediate++;
+ goto out;
+ }
if (retval == JIM_OK || retval == JIM_CONTINUE) {
retval = JIM_OK;
@@ -12477,6 +12500,10 @@ static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv
/* increment */
JIM_IF_OPTIM(evalnext:)
retval = Jim_EvalObj(interp, argv[3]);
+ if (JimCheckLoopRetcode(interp, retval)) {
+ immediate++;
+ goto out;
+ }
if (retval == JIM_OK || retval == JIM_CONTINUE) {
/* test */
JIM_IF_OPTIM(testcond:)
@@ -12492,9 +12519,11 @@ JIM_IF_OPTIM(out:)
Jim_DecrRefCount(interp, varNamePtr);
}
- if (retval == JIM_CONTINUE || retval == JIM_BREAK || retval == JIM_OK) {
- Jim_SetEmptyResult(interp);
- return JIM_OK;
+ if (!immediate) {
+ if (retval == JIM_CONTINUE || retval == JIM_BREAK || retval == JIM_OK) {
+ Jim_SetEmptyResult(interp);
+ return JIM_OK;
+ }
}
return retval;
@@ -12534,6 +12563,9 @@ static int Jim_LoopCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
while (((i < limit && incr > 0) || (i > limit && incr < 0)) && retval == JIM_OK) {
retval = Jim_EvalObj(interp, bodyObjPtr);
+ if (JimCheckLoopRetcode(interp, retval)) {
+ return retval;
+ }
if (retval == JIM_OK || retval == JIM_CONTINUE) {
Jim_Obj *objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
@@ -12688,7 +12720,11 @@ static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *arg
}
}
}
- switch (result = Jim_EvalObj(interp, script)) {
+ result = Jim_EvalObj(interp, script);
+ if (JimCheckLoopRetcode(interp, result)) {
+ goto err;
+ }
+ switch (result) {
case JIM_OK:
if (doMap) {
Jim_ListAppendElement(interp, resultObj, interp->result);
@@ -13828,24 +13864,33 @@ static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
return retcode;
}
-/* [break] */
-static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+static int JimBreakContinueHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int retcode)
{
- if (argc != 1) {
- Jim_WrongNumArgs(interp, 1, argv, "");
+ if (argc != 1 && argc != 2) {
+ Jim_WrongNumArgs(interp, 1, argv, "?level?");
return JIM_ERR;
}
- return JIM_BREAK;
+ if (argc == 2) {
+ long level;
+ int ret = Jim_GetLong(interp, argv[1], &level);
+ if (ret != JIM_OK) {
+ return ret;
+ }
+ interp->returnLevel = level;
+ }
+ return retcode;
+}
+
+/* [break] */
+static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+ return JimBreakContinueHelper(interp, argc, argv, JIM_BREAK);
}
/* [continue] */
static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
- if (argc != 1) {
- Jim_WrongNumArgs(interp, 1, argv, "");
- return JIM_ERR;
- }
- return JIM_CONTINUE;
+ return JimBreakContinueHelper(interp, argc, argv, JIM_CONTINUE);
}
/* [return] */
diff --git a/tests/jim.test b/tests/jim.test
index b2a9337..e6de376 100644
--- a/tests/jim.test
+++ b/tests/jim.test
@@ -2002,9 +2002,13 @@ test foreach-7.3 {continue tests} {
} {b}
test foreach-7.4 {continue tests} {catch {continue foo} msg} 1
test foreach-7.5 {continue tests} {
+ catch {continue foo blah} msg
+ set msg
+} {wrong # args: should be "continue ?level?"}
+test foreach-7.6 {continue tests} {
catch {continue foo} msg
set msg
-} {wrong # args: should be "continue"}
+} {expected integer but got "foo"}
# Check "break".
@@ -2019,9 +2023,13 @@ test foreach-8.2 {break tests} {
} {a b}
test foreach-8.3 {break tests} {catch {break foo} msg} 1
test foreach-8.4 {break tests} {
- catch {break foo} msg
+ catch {break foo blah} msg
+ set msg
+} {wrong # args: should be "break ?level?"}
+test foreach-8.6 {break tests} {
+ catch {break foo} msg
set msg
-} {wrong # args: should be "break"}
+} {expected integer but got "foo"}
# Test for incorrect "double evaluation" semantics
@@ -2792,9 +2800,9 @@ test for-1.15 {TclCompileForCmd: for command result} {
# Check "for" and "continue".
test for-2.1 {TclCompileContinueCmd: arguments after "continue"} {
- catch {continue foo} msg
+ catch {continue foo blah} msg
set msg
-} {wrong # args: should be "continue"}
+} {wrong # args: should be "continue ?level?"}
test for-2.2 {TclCompileContinueCmd: continue result} {
catch continue
} 4
@@ -2863,9 +2871,9 @@ test for-2.6 {continue tests, long command body} {
# Check "for" and "break".
test for-3.1 {TclCompileBreakCmd: arguments after "break"} {
- catch {break foo} msg
+ catch {break foo blah} msg
set msg
-} {wrong # args: should be "break"}
+} {wrong # args: should be "break ?level?"}
test for-3.2 {TclCompileBreakCmd: break result} {
catch break
} 3