Reputation: 3245
I have the following code:
$ cat ~/tmp/2.tcl
set zero 0
proc p1 {} {
if {[catch {expr 1/$zero} err]} {
puts "errorCode=$errorCode"
puts "errorInfo=$errorInfo"
}
}
p1
When I source it, I get error accessing errorCode:
$ tclsh ~/tmp/2.tcl
can't read "errorCode": no such variable
while executing
"puts "errorCode=$errorCode""
(procedure "p1" line 3)
invoked from within
"p1"
(file "~/tmp/2.tcl" line 9)
I tried changing to $::errorCode, but did not help.
Can you see what is wrong?
Upvotes: 0
Views: 1138
Reputation: 76
The options dictionary from the result of a [catch ...]
seem to contain various elements in a curious pattern depending on the -code
and -level
of what threw the exception inside the [catch]
. A few examples follow:
rc=0 catch.rslt=XXX catch.opts=-code 0 -level 0
rc=1 catch.rslt=XXX catch.opts=-code 1 -level 0 -errorinfo {A B C foo rvdebug.cfg 223} -errorcode {1 2 C}
rc=2 catch.rslt=XXX catch.opts=-code 0 -level 0
rc=3 catch.rslt=XXX catch.opts=-code 3 -level 0
rc=4 catch.rslt=XXX catch.opts=-code 4 -level 0
rc=0 catch.rslt=XXX catch.opts=-code 0 -level 0
rc=1 catch.rslt=XXX catch.opts=-code 1 -level 0 -errorinfo {A B C} -errorcode {1 2 C}
rc=2 catch.rslt=XXX catch.opts=-code 0 -level 0
rc=3 catch.rslt=XXX catch.opts=-code 3 -level 0
rc=4 catch.rslt=XXX catch.opts=-code 4 -level 0
rc=2 catch.rslt=XXX catch.opts=-code 0 -level 1
rc=2 catch.rslt=XXX catch.opts=-code 1 -level 1
rc=2 catch.rslt=XXX catch.opts=-code 2 -level 1
rc=2 catch.rslt=XXX catch.opts=-code 3 -level 1
rc=2 catch.rslt=XXX catch.opts=-code 4 -level 1
For example, a throwable procedure ...
proc foo {c lvl} {
return -code $c \
-errorinfo [list A B C] \
-errorcode [list 1 2 C] \
-level $lvl XXX \
}
... is called by a catchable handler ...
set rc [catch {foo 0 0} rslt opts]
... and its results examined as
echo "rc=$rc catch.rslt=$rslt catch.opts=$opts"
To summarize,
return
allows -errorcode
only when -code
is 1 (TCL_ERROR) and -level
is 0 (TRACE_TOP), otherwise syntax errorcatch
opts always contain the two elements -code
and -level
catch
opts has -errorinfo
suffixed with func,file,line only when -code
is 1 (TCL_ERROR) and -level
is 0 (TRACE_TOP)catch
opts has -errorinfo
un-suffixed only when -code
is 1 (TCL_ERROR) and -level
is 1 (TRACE_SUBTOP)catch
opts sets -code
as 0 only when -code
is 2 (TCL_RETURN) and -level
is 0 or 1 (TRACE_TOP or TRACE_SUBTOP)catch
always returns 2 (TCL_RETURN) when -level
is more than 1 (below TRACE_SUBTOP)catch
returns -code
when -level
is 0 or 1 (TRACE_TOP or TRACE_SUBTOP), and is not set to 0 at -code
2 (TCL_RETURN)catch
returns rslt unless proc is called with or has "wrong # args", a message returned instead along with -code
set to 1 (TCL_ERROR)where
# -code TCL_OK | TCL_ERROR | TCL_RETURN | TCL_BREAK | TCL_CONTINUE
# -level trace-depth-of-call-stack -- non-negative number
# -errorinfo [list ... ... ...] -- trio of elements meaning, e.g., proc, file, linenum
# -errorcode [list ...] -- arbitrary information, alphanumeric, one or more
# -errorinfo in catch opts only when -code is 1 (TCL_ERROR) and -level is 0 (TRACE_TOP) or 1 (TRACE_SUBTOP), value is dflt-trace unless -errorinfo specified --> ::errorInfo
# -errorcode in catch opts only when -code is 1 (TCL_ERROR) and -level is 0 (TRACE_TOP) or 1 (TRACE_SUBTOP), value is NONE unless -errorcode specified --> ::errorCode
and
# TCL Return Codes: TCL_OK 0 TCL_ERROR 1 TCL_RETURN 2 TCL_BREAK 3 TCL_CONTINUE 4
# TCL Trace Levels: TRACE_TOP 0 TRACE_SUBTOP 1 ... ...
In my particular case of OpenOCD presumably a simpler and/or older version of jim-tcl is used, where the return
allows only four arguments:
# return ?-code code? ?-errorinfo stacktrace? ?-level level? ?result?
Thus, neither -errorstack [list ...]
nor -options [dict create ... ...]
are supported for any -code
or -level
.
The MWE which created this output is shown below.
proc foo {c lvl} { return -code $c -errorinfo [list A B C] -errorcode [list 1 2 C] -level $lvl XXX} set rc [catch {foo 0 0} rslt opts] ; echo "rc=$rc catch.rslt=$rslt catch.opts=$opts" set rc [catch {foo 1 0} rslt opts] ; echo "rc=$rc catch.rslt=$rslt catch.opts=$opts" set rc [catch {foo 2 0} rslt opts] ; echo "rc=$rc catch.rslt=$rslt catch.opts=$opts" set rc [catch {foo 3 0} rslt opts] ; echo "rc=$rc catch.rslt=$rslt catch.opts=$opts" set rc [catch {foo 4 0} rslt opts] ; echo "rc=$rc catch.rslt=$rslt catch.opts=$opts"
set rc [catch {foo 0 1} rslt opts] ; echo "rc=$rc catch.rslt=$rslt catch.opts=$opts" set rc [catch {foo 1 1} rslt opts] ; echo "rc=$rc catch.rslt=$rslt catch.opts=$opts" set rc [catch {foo 2 1} rslt opts] ; echo "rc=$rc catch.rslt=$rslt catch.opts=$opts" set rc [catch {foo 3 1} rslt opts] ; echo "rc=$rc catch.rslt=$rslt catch.opts=$opts" set rc [catch {foo 4 1} rslt opts] ; echo "rc=$rc catch.rslt=$rslt catch.opts=$opts"
set rc [catch {foo 0 2} rslt opts] ; echo "rc=$rc catch.rslt=$rslt catch.opts=$opts" set rc [catch {foo 1 2} rslt opts] ; echo "rc=$rc catch.rslt=$rslt catch.opts=$opts" set rc [catch {foo 2 2} rslt opts] ; echo "rc=$rc catch.rslt=$rslt catch.opts=$opts" set rc [catch {foo 3 2} rslt opts] ; echo "rc=$rc catch.rslt=$rslt catch.opts=$opts" set rc [catch {foo 4 2} rslt opts] ; echo "rc=$rc catch.rslt=$rslt catch.opts=$opts"
Upvotes: 0
Reputation: 76
Sergei's answer is very good. Using the dictionary returned by the second optional parameter of catch [...]
is spot on. However, I find slightly different entries in this dictionary and -errorinfo is not one of them.
There are only two dictionary entries in opts. What works for me is
puts "errorCode=[dict get $opts -code]"
puts "errorLevel=[dict get $opts -level]"
Perhaps I have an older or more specialized version of Tcl/Tk?
Upvotes: 0
Reputation: 611
Starting from Tcl 8.5 (As Donal has pointed out, it still does, so they can be accessed as [catch]
doesn't set the errorCode
and errorInfo
global variables.$::errorCode
and $::errorInfo
). And in addition it puts their values into a dictionary which name is to be specified as the third argument. The following code
#!/usr/bin/tclsh
set zero 0
proc p1 {} {
if {[catch {expr 1/$zero} err opts] == 1} {
puts "errorCode=[dict get $opts -errorcode]"
puts "errorInfo=[dict get $opts -errorinfo]"
}
}
p1
prints
errorCode=NONE
errorInfo=can't read "zero": no such variable
while executing
"expr 1/$zero"
in Tcl 8.5.19, and
errorCode=TCL READ VARNAME
errorInfo=can't read "zero": no such variable
while executing
"expr 1/$zero"
in Tcl8.6.6.
You'd probably want to use $::zero
in the division after which the result would be
errorCode=ARITH DIVZERO {divide by zero}
errorInfo=divide by zero
while executing
"expr 1/$::zero"
Upvotes: 4
Reputation: 137767
The errorInfo
and errorCode
variables are globals. You should either use the global
command to bring them into scope or use their fully-qualified names (i.e., precede with ::
).
It might be easier to pick the information out of the result options dictionary (a new feature in 8.5).
Upvotes: 3