An error experiment

Richard Suchenwirth 2003-07-29: Errors happen, and in Tcl they're less depressing and more educational than in most other languages. Also, one can try things like resuming at the position where the error happened - almost. Consider this code, where error is renamed.

rename error tcl::error

and redefined to a simple interactive prompt (a minimal debugger), where you can

  • check what went wrong ("p" to see the proc body where the error happened, and the arguments with which it was called),
  • maybe fix it, and decide to
  • "c"ontinue (i.e. resume after the place the error was raised) or
  • keep the error for the real handler ("x"):
proc error {code args} {
    puts $code/$args
    while 1 {
        puts -nonewline {% }
        flush stdout
        gets stdin line
        if {$line eq {x}} {tcl::error $code}
        if {$line eq {p}} {
            set self [info level -1]
            puts "$self {[info body [lindex $self 0]]}"
            continue
        }
        if {$line eq {c}} break
        catch {uplevel 1 $line} res
        puts $res
    }
}

The test code is heavily putsy so one sees what goes on. try makes two deliberate errors: one by just calling an error, one by referencing an undefined variable.

puts {before try}
proc try args {
    puts {in try}
    error intended
    puts x=$x
    puts {after error}
}
try a little bit
puts {after try}

Experimenting with this, I find the intended error triggers indeed, I can see where I am, and it lets me fix the "missing x" bug, and resume:

suchenwi@jaguar% tclsh error2.tcl
before try
in try
intended/
% p
try a little bit {
   puts {in try}
   error intended
   puts x=$x
   puts {after error}
}
% set x wow!
wow!
% c
x=wow!
after error
after try

but if I just let the intended error resume, missing x appears to be hard-wired to the original error, not my overloaded version:

suchenwi@jaguar% tclsh error2.tcl                                         
before try
in try
intended/
% c ;# continue without fixing
can't read "x": no such variable
   while executing
"puts x=$x"
   (procedure "try" line 4)
   invoked from within
"try"
   (file "error2.tcl" line 22)

So the usability of this is limited - you can only intercept errors that you explicitly raised yourself.

An interesting point is however the behavior of the bytecode compiler: it does not notice or complain about the fact that $x is used before defined - possibly because a preceding command (like my redefined "error") might have set it with upvar...

See Also

procstep
A script monitor that provides full control over the evalution of the script, making things like full restarts feasible to implement in pure Tcl.