[PL]: I'm just starting up this page. It's intended to explain the low-level details of terminating scripts, as regards termination categories (ok, error, return, break, continue), result values (valid data vs error messages), and handling exceptional termination. ***Kinds of script termination*** %|Kind|Numeric code|Symbolic code|Returns to|Result value treated by convention as...|% &|Normal|0|`ok`|Caller|Valid data|& &|Error|1|error|Handler|Error message|& &|Return|2|return|Caller's caller|Valid data (see below)|& &|Break|3|break|Special, see below|No convention exists|& &|Continue|4|continue|Special, see below|No convention exists|& "By convention" means just that: while most if not all Tcl programs handle the result values in this manner, there is nothing to actually enforce this usage or to stop you from having an error message at normal termination or valid data at error termination. It would just be a case of obfuscated Tcl. Consider a script with three commands (the comment after each command shows the command's result value): === ''commandA'' ;# -> ''resultA'' ''commandB'' ;# -> ''resultB'' ''commandC'' ;# -> ''resultC'' === The evaluation of this script goes like this: `''commandA''` is invoked, its side-effects (if any) happen, and its result (`''resultA''`) is produced and discarded (because it isn't assigned to anything). The same thing happens to `''commandB''` and `''commandC''`, but as the script terminates after `''commandC''`, `''resultC''` isn't discarded: instead it becomes the result of the evaluation of the script. This is a ''normal termination'', where the result is valid data. Now, if `''commandB''` is a ''terminating command'', evaluation goes differently. Commands `''commandA''` and `''commandB''` are invoked and their side-effects happen, but `''commandC''` doesn't get invoked at all and its side-effect doesn't happen. The result of `''commandB''` becomes the result of the evaluation of the script instead. This can still be a normal termination, but it can also be some kind of ''exceptional termination''. In the latter case, `''resultB''` isn't valid data but an error message. If instead an error occurs during execution of `''commandB''`, the situation is similar to the last paragraph. Commands `''commandA''` and `''commandB''` are invoked, but only `''commandA''` is completed successfully. The side-effect of `''commandB''` may or may not happen, or happen partially. The normal result of `''commandB''` is replaced by an error message, which becomes the result of the evaluation of the script. This is always ''exceptional termination''. Now, I've talked about the script having a result value, but where does it go? Every script is evaluated in a ''call stack frame'', a tier in a multi-layered data structure which is managed by the Tcl interpreter and mostly invisible to the programmer. The executing command always resides in the top frame in the call stack. If that command ''calls'' another command, a new stack frame is added to the call stack, and the called command can be executed in that frame. When a command terminates by normal termination, the stack is ''unwound'', i.e. the command's frame is removed and the command in the previous stack frame becomes active again ("control returns to the caller"). As part of the procedure to switch stack frames, the interpreter transfers the terminating command's result value to the caller's frame where it might be copied into a variable and stored. If `''commandA''`, with the variables `''a''`, `''b''`, and `''c''`, executes `set b [[commandB]]`, and `''commandB''` produces the result value `"abc"` the call stack might look something like these three steps (text in parenthesis is a stack frame with command name and variable names; the >> symbol shows the caller relationship; the text after -> is the command's return value): === ( ''commandA'' ''a'' ''b'' ''c'' ) ( ''commandA'' ''a'' ''b'' ''c'' ) >> ( ''commandB'' -> "abc" ) ( ''commandA'' ''a'' ''b'' = "abc" ''c'' ) === In the case of exceptional termination, the stack is again unwound, but the unwinding doesn't necessarily stop at the caller's frame. There are four different kinds of exceptional termination in Tcl: error, return, break, and continue. I'll deal with them one after another. ***Error termination*** In the error kind of exceptional termination, the stack is unwound until a ''handler'' is found. This is either the `[try]` command or the `[catch]` command being executed in a frame of the call stack. The handler gets the result value from the terminating command, and also a ''return code'' which designates the kind of termination that happened (1 for error termination: it's 0 for normal termination and 2, 3, and 4 for return, break, and continue, respectively). If `''commandA''` calls `try`, which calls `''commandB''`, which calls `''commandC''`, which terminates with an error, the call stack might look something like this (the stack frame for `try` has two fictional variables, ''_C'' and ''_R'': they are just a way to show that the return code for the termination and the result value of the terminating command both end up inside `try`'s stack frame): === ( ''commandA'' ) ( ''commandA'' ) >> ( `try` ''_C'' ''_R'' ) ( ''commandA'' ) >> ( `try` ''_C'' ''_R'' ) >> ( ''commandB'' ) ( ''commandA'' ) >> ( `try` ''_C'' ''_R'' ) >> ( ''commandB'' ) >> ( ''commandC'' -> "xyz" ) ( ''commandA'' ) >> ( `try` ''_C'' = 1 ''_R'' = "xyz" ) === If neither `''commandC''` nor `''commandB''` had terminated with an error, the call stack might have looked like this: === ( ''commandA'' ) ( ''commandA'' ) >> ( `try` ''_C'' ''_R'' ) ( ''commandA'' ) >> ( `try` ''_C'' ''_R'' ) >> ( ''commandB'' ) ( ''commandA'' ) >> ( `try` ''_C'' ''_R'' ) >> ( ''commandB'' ) >> ( ''commandC'' -> "abc" ) ( ''commandA'' ) >> ( `try` ''_C'' ''_R'' ) >> ( ''commandB'' -> "def" ) ( ''commandA'' ) >> ( `try` ''_C'' = 0 ''_R'' = "def" ) === The return code makes it possible for `try` to know if the result value is valid data or an error message: in the previous example, `"def"` is data, while in the example before that, `"xyz"` is an error message. The commands `[throw]` and `[error]` cause error termination, and so does `[return]` if invoked like `return -code error`, and `[unknown]` if it can't find a command to execute. Many other commands terminate with an error if they can't complete their tasks, such as `expr {1/0}` or `open {this file doesn't exist I promise} "r"`. ***Return termination*** A return termination unwinds exactly two stack frames and leaves its result value in the third ("control returns to the caller's caller"). If `''commandA''` calls `set a [[commandB]]`, and `''commandB''` calls `''commandC''`, which executes `return -code return theResult`, the stack looks like this: === ( ''commandA'' a ) ( ''commandA'' a ) >> ( ''commandB'' ) ( ''commandA'' a ) >> ( ''commandB'' ) >> ( ''commandC'' -> "theResult" ) ( ''commandA'' a = "theResult" ) === There is a twist, though. The termination reaches the caller's caller as a normal termination. Therefore, it could also be described like this: === ( ''commandA'' a ) ( ''commandA'' a ) >> ( ''commandB'' ) ( ''commandA'' a ) >> ( ''commandB'' ) >> ( ''commandC'' -> (3) "theResult" ) ( ''commandA'' a ) >> ( ''commandB'' -> (0) "theResult" ) ( ''commandA'' a = "theResult" ) === But note that there is no actual corresponding `return` command in `''commandB''`! The only way to cause a return termination is to invoke the command `[return] -code return`. ***Break termination*** A break termination unwinds one stack frame and expects to find a handler there. Specifically, a handler that was called as part of a command implementing a loop construct. ***Continue termination*** (I'll be back soon -- [PL]) ====== proc caller {} { set result [callee] } ====== === control { ''script'' } ;# -> ''result of control might be result of script'' === <>Tutorial