diff options
author | Steve Bennett <steveb@workware.net.au> | 2020-12-26 10:37:42 +1000 |
---|---|---|
committer | Steve Bennett <steveb@workware.net.au> | 2020-12-26 18:10:16 +1000 |
commit | 8a095f94f167fa1e497660bf80141f07b93d8dea (patch) | |
tree | be38840e2d017420f858ca9942b23020a68b12e4 | |
parent | 0127e81ddcc824d2985e5c63dd2942689b5d39e3 (diff) | |
download | jimtcl-8a095f94f167fa1e497660bf80141f07b93d8dea.zip jimtcl-8a095f94f167fa1e497660bf80141f07b93d8dea.tar.gz jimtcl-8a095f94f167fa1e497660bf80141f07b93d8dea.tar.bz2 |
try: reimplement in C
For speed and size, since it shares a lot of the same code with catch
Signed-off-by: Steve Bennett <steveb@workware.net.au>
-rw-r--r-- | jim.c | 186 | ||||
-rw-r--r-- | tclcompat.tcl | 66 |
2 files changed, 150 insertions, 102 deletions
@@ -14240,18 +14240,46 @@ static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg return JIM_EXIT; } -/* [catch] */ -static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +static int JimMatchReturnCodes(Jim_Interp *interp, Jim_Obj *retcodeListObj, int rc) { + int len = Jim_ListLength(interp, retcodeListObj); + int i; + for (i = 0; i < len; i++) { + int returncode; + if (Jim_GetReturnCode(interp, Jim_ListGetIndex(interp, retcodeListObj, i), &returncode) != JIM_OK) { + return JIM_ERR; + } + if (rc == returncode) { + return JIM_OK; + } + } + return -1; +} + +/* Implements both [try] and [catch] */ +static int JimCatchTryHelper(Jim_Interp *interp, int istry, int argc, Jim_Obj *const *argv) +{ + static const char * const wrongargs_catchtry[2] = { + "?-?no?code ... --? script ?resultVarName? ?optionVarName?", + "?-?no?code ... --? script ?on codes vars script? ... ?finally script?" + }; int exitCode = 0; int i; int sig = 0; + int ok; + Jim_Obj *finallyScriptObj = NULL; + Jim_Obj *msgVarObj = NULL; + Jim_Obj *optsVarObj = NULL; + Jim_Obj *onScriptObj = NULL; + int idx; /* Which return codes are ignored (passed through)? By default, only exit, eval and signal */ jim_wide ignore_mask = (1 << JIM_EXIT) | (1 << JIM_EVAL) | (1 << JIM_SIGNAL); static const int max_ignore_code = sizeof(ignore_mask) * 8; - /* Reset the error code before catch. + JimPanic((istry != 0 && istry != 1, "wrong args to JimCatchTryHelper")); + + /* Reset the error code before catch/try. * Note that this is not strictly correct. */ Jim_SetGlobalVariableStr(interp, "errorCode", Jim_NewStringObj(interp, "NONE", -1)); @@ -14297,14 +14325,13 @@ static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar } } - argc -= i; - if (argc < 1 || argc > 3) { - wrongargs: - Jim_WrongNumArgs(interp, 1, argv, - "?-?no?code ... --? script ?resultVarName? ?optionVarName?"); + idx = i; + + if (argc - idx < 1) { +wrongargs: + Jim_WrongNumArgs(interp, 1, argv, wrongargs_catchtry[istry]); return JIM_ERR; } - argv += i; if ((ignore_mask & (1 << JIM_SIGNAL)) == 0) { sig++; @@ -14316,15 +14343,67 @@ static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar exitCode = JIM_SIGNAL; } else { - exitCode = Jim_EvalObj(interp, argv[0]); + exitCode = Jim_EvalObj(interp, argv[idx]); /* Don't want any caught error included in a later stack trace */ interp->errorFlag = 0; } interp->signal_level -= sig; + /* For try, we need to find both a matching return code and finally (if they exist) + * Set: finallyScriptObj + * onScriptObj + * msgVarObj + * optsVarObj + * Any of these can be NULL; + */ + idx++; + if (istry) { + while (idx < argc) { + if (Jim_CompareStringImmediate(interp, argv[idx], "on")) { + int ret; + if (idx + 4 > argc) { + goto wrongargs; + } + ret = JimMatchReturnCodes(interp, argv[idx + 1], exitCode); + if (ret > JIM_OK) { + goto wrongargs; + } + if (ret == JIM_OK) { + msgVarObj = Jim_ListGetIndex(interp, argv[idx + 2], 0); + optsVarObj = Jim_ListGetIndex(interp, argv[idx + 2], 1); + onScriptObj = argv[idx + 3]; + } + idx += 4; + } + else if (Jim_CompareStringImmediate(interp, argv[idx], "finally")) { + if (idx + 2 != argc) { + goto wrongargs; + } + finallyScriptObj = argv[idx + 1]; + idx += 2; + } + else { + goto wrongargs; + } + } + } + else { + if (argc - idx >= 1) { + msgVarObj = argv[idx]; + idx++; + if (argc - idx >= 1) { + optsVarObj = argv[idx]; + idx++; + } + } + } + /* Catch or pass through? Only the first 32/64 codes can be passed through */ if (exitCode >= 0 && exitCode < max_ignore_code && (((unsigned jim_wide)1 << exitCode) & ignore_mask)) { /* Not caught, pass it up */ + if (finallyScriptObj) { + Jim_EvalObj(interp, finallyScriptObj); + } return exitCode; } @@ -14333,43 +14412,77 @@ static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar if (interp->signal_set_result) { interp->signal_set_result(interp, interp->sigmask); } - else { + else if (!istry) { Jim_SetResultInt(interp, interp->sigmask); } interp->sigmask = 0; } - if (argc >= 2) { - if (Jim_SetVariable(interp, argv[1], Jim_GetResult(interp)) != JIM_OK) { - return JIM_ERR; + ok = 1; + if (msgVarObj && Jim_Length(msgVarObj)) { + if (Jim_SetVariable(interp, msgVarObj, Jim_GetResult(interp)) != JIM_OK) { + ok = 0; } - if (argc == 3) { - Jim_Obj *optListObj = Jim_NewListObj(interp, NULL, 0); + } + if (ok && optsVarObj && Jim_Length(optsVarObj)) { + Jim_Obj *optListObj = Jim_NewListObj(interp, NULL, 0); - Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-code", -1)); - Jim_ListAppendElement(interp, optListObj, - Jim_NewIntObj(interp, exitCode == JIM_RETURN ? interp->returnCode : exitCode)); - Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-level", -1)); - Jim_ListAppendElement(interp, optListObj, Jim_NewIntObj(interp, interp->returnLevel)); - if (exitCode == JIM_ERR) { - Jim_Obj *errorCode; - Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorinfo", - -1)); - Jim_ListAppendElement(interp, optListObj, interp->stackTrace); + Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-code", -1)); + Jim_ListAppendElement(interp, optListObj, + Jim_NewIntObj(interp, exitCode == JIM_RETURN ? interp->returnCode : exitCode)); + Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-level", -1)); + Jim_ListAppendElement(interp, optListObj, Jim_NewIntObj(interp, interp->returnLevel)); + if (exitCode == JIM_ERR) { + Jim_Obj *errorCode; + Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorinfo", + -1)); + Jim_ListAppendElement(interp, optListObj, interp->stackTrace); - errorCode = Jim_GetGlobalVariableStr(interp, "errorCode", JIM_NONE); - if (errorCode) { - Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorcode", -1)); - Jim_ListAppendElement(interp, optListObj, errorCode); - } - } - if (Jim_SetVariable(interp, argv[2], optListObj) != JIM_OK) { - return JIM_ERR; + errorCode = Jim_GetGlobalVariableStr(interp, "errorCode", JIM_NONE); + if (errorCode) { + Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorcode", -1)); + Jim_ListAppendElement(interp, optListObj, errorCode); } } + if (Jim_SetVariable(interp, optsVarObj, optListObj) != JIM_OK) { + ok = 0; + } } - Jim_SetResultInt(interp, exitCode); - return JIM_OK; + if (ok && onScriptObj) { + /* Execute the on script. Any return code replaces the original. */ + exitCode = Jim_EvalObj(interp, onScriptObj); + } + + if (finallyScriptObj) { + /* Execute the on script. If OK, restore previous resul/exitcode */ + Jim_Obj *prevResultObj = Jim_GetResult(interp); + Jim_IncrRefCount(prevResultObj); + int ret = Jim_EvalObj(interp, finallyScriptObj); + if (ret == JIM_OK) { + Jim_SetResult(interp, prevResultObj); + } + else { + exitCode = ret; + } + Jim_DecrRefCount(interp, prevResultObj); + } + if (!istry) { + Jim_SetResultInt(interp, exitCode); + exitCode = JIM_OK; + } + return exitCode; +} + +/* [catch] */ +static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + return JimCatchTryHelper(interp, 0, argc, argv); +} + +/* [try] */ +static int Jim_TryCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + return JimCatchTryHelper(interp, 1, argc, argv); } #if defined(JIM_REFERENCES) && !defined(JIM_BOOTSTRAP) @@ -15736,6 +15849,7 @@ static const struct { {"time", Jim_TimeCoreCommand}, {"exit", Jim_ExitCoreCommand}, {"catch", Jim_CatchCoreCommand}, + {"try", Jim_TryCoreCommand}, #ifdef JIM_REFERENCES {"ref", Jim_RefCoreCommand}, {"getref", Jim_GetrefCoreCommand}, diff --git a/tclcompat.tcl b/tclcompat.tcl index 432c744..e0f4070 100644 --- a/tclcompat.tcl +++ b/tclcompat.tcl @@ -181,72 +181,6 @@ local proc pid {{channelId {}}} { return $pids } -# try/on/finally conceptually similar to Tcl 8.6 -# -# Usage: try ?catchopts? script ?onclause ...? ?finallyclause? -# -# Where: -# catchopts is: options for catch such as -nobreak, -signal -# onclause is: on codes {?resultvar? ?optsvar?} script -# codes is: a list of return codes (ok, error, etc. or integers), or * for any -# finallyclause is: finally script -proc try {args} { - set catchopts {} - while {[string match -* [lindex $args 0]]} { - set args [lassign $args opt] - if {$opt eq "--"} { - break - } - lappend catchopts $opt - } - if {[llength $args] == 0} { - return -code error {wrong # args: should be "try ?options? script ?argument ...?"} - } - set args [lassign $args script] - set code [catch -eval {*}$catchopts {uplevel 1 $script} msg opts] - - set handled 0 - - foreach {on codes vars script} $args { - switch -- $on \ - on { - if {!$handled && ($codes eq "*" || [info returncode $code] in $codes)} { - lassign $vars msgvar optsvar - if {$msgvar ne ""} { - upvar $msgvar hmsg - set hmsg $msg - } - if {$optsvar ne ""} { - upvar $optsvar hopts - set hopts $opts - } - # Override any body result - set code [catch {uplevel 1 $script} msg opts] - incr handled - } - } \ - finally { - set finalcode [catch {uplevel 1 $codes} finalmsg finalopts] - if {$finalcode} { - # Override any body or handler result - set code $finalcode - set msg $finalmsg - set opts $finalopts - } - break - } \ - default { - return -code error "try: expected 'on' or 'finally', got '$on'" - } - } - - if {$code} { - incr opts(-level) - return {*}$opts $msg - } - return $msg -} - # Generates an exception with the given code (ok, error, etc. or an integer) # and the given message proc throw {code {msg ""}} { |