diff options
author | Steve Bennett <steveb@workware.net.au> | 2021-08-01 15:15:15 +1000 |
---|---|---|
committer | Steve Bennett <steveb@workware.net.au> | 2022-04-14 07:51:29 +1000 |
commit | 5b967d60f4f5277e0f843d15fc54c0cb906a1bc2 (patch) | |
tree | 3c6e9e7b8b49cd48b01334b552e91e40dbac4a6d | |
parent | b66b2a731be6bbc680f3f2c69d972050686d6720 (diff) | |
download | jimtcl-5b967d60f4f5277e0f843d15fc54c0cb906a1bc2.zip jimtcl-5b967d60f4f5277e0f843d15fc54c0cb906a1bc2.tar.gz jimtcl-5b967d60f4f5277e0f843d15fc54c0cb906a1bc2.tar.bz2 |
try: add support for trap
In addition to "on codes ..."
it is now possible to trap on errorcode with
"trap sublist ..."
e.g.
try {
...
} trap CHILDSTATUS {msg opts} {
...
}
Fixes #204
Signed-off-by: Steve Bennett <steveb@workware.net.au>
-rw-r--r-- | jim.c | 98 | ||||
-rw-r--r-- | jim_tcl.txt | 37 | ||||
-rw-r--r-- | tests/try.test | 44 |
3 files changed, 132 insertions, 47 deletions
@@ -14518,7 +14518,7 @@ static int JimCatchTryHelper(Jim_Interp *interp, int istry, int argc, Jim_Obj *c { static const char * const wrongargs_catchtry[2] = { "?-?no?code ... --? script ?resultVarName? ?optionVarName?", - "?-?no?code ... --? script ?on codes vars script? ... ?finally script?" + "?-?no?code ... --? script ?on|trap codes vars script? ... ?finally script?" }; int exitCode = 0; int i; @@ -14527,7 +14527,8 @@ static int JimCatchTryHelper(Jim_Interp *interp, int istry, int argc, Jim_Obj *c Jim_Obj *finallyScriptObj = NULL; Jim_Obj *msgVarObj = NULL; Jim_Obj *optsVarObj = NULL; - Jim_Obj *onScriptObj = NULL; + Jim_Obj *handlerScriptObj = NULL; + Jim_Obj *errorCodeObj; int idx; /* Which return codes are ignored (passed through)? By default, only exit, eval and signal */ @@ -14606,9 +14607,11 @@ wrongargs: } interp->signal_level -= sig; - /* For try, we need to find both a matching return code and finally (if they exist) + errorCodeObj = Jim_GetGlobalVariableStr(interp, "errorCode", JIM_NONE); + + /* For try, we need to find both a matching return or trap code and finally (if they exist) * Set: finallyScriptObj - * onScriptObj + * handlerScriptObj * msgVarObj * optsVarObj * Any of these can be NULL; @@ -14616,31 +14619,60 @@ wrongargs: 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; + int option; + int ret; + static const char * const try_options[] = { "on", "trap", "finally", NULL }; + enum { TRY_ON, TRY_TRAP, TRY_FINALLY, }; + + if (Jim_GetEnum(interp, argv[idx], try_options, &option, "handler", JIM_ERRMSG) != JIM_OK) { + return JIM_ERR; } - else { - goto wrongargs; + switch (option) { + case TRY_ON: + case TRY_TRAP: + if (idx + 4 > argc) { + goto wrongargs; + } + if (option == TRY_ON) { + ret = JimMatchReturnCodes(interp, argv[idx + 1], exitCode); + if (ret > JIM_OK) { + goto wrongargs; + } + } + else if (errorCodeObj) { + int len = Jim_ListLength(interp, argv[idx + 1]); + int i; + + ret = JIM_OK; + /* Try to match the sublist against errorcode */ + for (i = 0; i < len; i++) { + Jim_Obj *matchObj = Jim_ListGetIndex(interp, argv[idx + 1], i); + Jim_Obj *objPtr = Jim_ListGetIndex(interp, errorCodeObj, i); + if (Jim_StringCompareObj(interp, matchObj, objPtr, 0) != 0) { + ret = -1; + break; + } + } + } + else { + /* No errorCode, so no match for trap */ + ret = -1; + } + /* Save the details of the first match */ + if (ret == JIM_OK && handlerScriptObj == NULL) { + msgVarObj = Jim_ListGetIndex(interp, argv[idx + 2], 0); + optsVarObj = Jim_ListGetIndex(interp, argv[idx + 2], 1); + handlerScriptObj = argv[idx + 3]; + } + idx += 4; + break; + case TRY_FINALLY: + if (idx + 2 != argc) { + goto wrongargs; + } + finallyScriptObj = argv[idx + 1]; + idx += 2; + break; } } } @@ -14690,24 +14722,22 @@ wrongargs: 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) { + if (errorCodeObj) { Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorcode", -1)); - Jim_ListAppendElement(interp, optListObj, errorCode); + Jim_ListAppendElement(interp, optListObj, errorCodeObj); } } if (Jim_SetVariable(interp, optsVarObj, optListObj) != JIM_OK) { ok = 0; } } - if (ok && onScriptObj) { + if (ok && handlerScriptObj) { /* Execute the on script. Any return code replaces the original. */ - exitCode = Jim_EvalObj(interp, onScriptObj); + exitCode = Jim_EvalObj(interp, handlerScriptObj); } if (finallyScriptObj) { diff --git a/jim_tcl.txt b/jim_tcl.txt index 9aad71e..f84101f 100644 --- a/jim_tcl.txt +++ b/jim_tcl.txt @@ -66,6 +66,7 @@ Changes between 0.80 and 0.81 9. Add support for `lsort -stride` 10. `open` now supports POSIX-style access arguments 11. TIP 526, `expr` now only allows a single argument (unless --compat is enabled) +12. `try` now supports trap to match on errorcode Changes between 0.79 and 0.80 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -4570,29 +4571,35 @@ Time is measured in elapsed time, not CPU time. try ~~~ -+*try* '?catchopts? tryscript' ?*on* 'returncodes {?resultvar? ?optsvar?} handlerscript \...'? ?*finally* 'finalscript'?+ ++*try* '?catchopts? tryscript' ?*on|trap* 'match {?resultvar? ?optsvar?} handlerscript \...'? ?*finally* 'finalscript'?+ The `try` command is provided as a convenience for exception handling. This interpeter first evaluates +'tryscript'+ under the effect of the catch options +'catchopts'+ (e.g. +-signal -noexit --+, see `catch`). -It then evaluates the script for the first matching 'on' handler -(there many be zero or more) based on the return code from the `try` -section. For example a normal +JIM_ERR+ error will be matched by -an 'on error' handler. +It then evaluates the script for the first matching 'on' or 'trap' +handler (there many be zero or more) based on the return code and +'errorcode' from the `try` section. For example a normal +JIM_ERR+ +error will be matched by an 'on error' handler. Finally, any +'finalscript'+ is evaluated. -The result of this command is the result of +'tryscript'+, except in the -case where an exception occurs in a matching 'on' handler script or the 'finally' script, -in which case the result is this new exception. +The result of this command is the result of +'tryscript'+, except +in the case where an exception occurs in a matching 'on' handler +script or the 'finally' script, in which case the result is this +new exception. -The specified +'returncodes'+ is a list of return codes either as names ('ok', 'error', 'break', etc.) -or as integers. +For the +'on'+ handler, 'match' is a list of return codes either +as names ('ok', 'error', 'break', etc.) or as integers. -If +'resultvar'+ and +'optsvar'+ are specified, they are set as for `catch` before evaluating -the matching handler. +For the +'trap'+ handler, 'match' is a list to match against +'errorcode'. For example, if the errorcode was ++'\{CHILDKILLED 10627 SIGTERM\}'+, then a 'match' value of +\{CHILDKILLED\}+ +would match. + +If +'resultvar'+ and +'optsvar'+ are specified, they are set as for +`catch` before evaluating the matching handler. For example: @@ -4602,6 +4609,9 @@ For example: process $f } on {continue break} {} { error "Unexpected break/continue" + } trap CHILDKILLED {msg opts} { + puts "A child process died" + return {*}$opts $msg } on error {msg opts} { puts "Dealing with error" return {*}$opts $msg @@ -4613,7 +4623,8 @@ For example: ---- If break, continue or error are raised, they are dealt with by the matching -handler. +handler. If an error occurred with an errorcode of +CHILDKILLED+, the trap +handler will be evaluated as it is specified before the 'on error' handler. In any case, the file will be closed via the 'finally' clause. diff --git a/tests/try.test b/tests/try.test index a2bb38a..0d76865 100644 --- a/tests/try.test +++ b/tests/try.test @@ -97,6 +97,50 @@ test try-1.9 "return -code from within try" { list [catch a msg] $msg } {3 text} +test try-2.1 "try ... trap" -body { + proc a {} { + return -code error -errorcode {CUSTOM RESULT} "custom errorcode" + } + try { + a + } trap CUSTOM {msg opts} { + list $msg $opts(-code) $opts(-errorcode) + } +} -result {{custom errorcode} 1 {CUSTOM RESULT}} + +test try-2.2 "trap single match" { + try { + apply {{} {return -code error -errorcode {FOO BAR} failed}} + } trap FOO {msg opts} { + list trapped + } +} trapped + +test try-2.3 "trap two matches" { + try { + apply {{} {return -code error -errorcode {FOO BAR} failed}} + } trap {FOO BAR} {msg opts} { + list trapped + } +} trapped + +test try-2.4 "trap no match" -body { + try { + apply {{} {return -code error -errorcode {FOO BAR} failed}} + } trap BAZ {msg opts} { + list trapped + } +} -returnCodes error -result failed + +test try-2.5 "trap match first but not second" -body { + try { + apply {{} {return -code error -errorcode {FOO BAR} failed}} + } trap {FOO BAZ} {msg opts} { + list trapped + } +} -returnCodes error -result failed + + proc c {} { try { error here |