Version 8 of Goto in Tcl

Updated 2002-10-26 12:16:06

Richard Suchenwirth -- Of course one shouldn't use a sort of GOTO command in Tcl (see "Go To Statement Considered Harmful", Edsger Dijkstra, http://www.acm.org/classics/oct95/ ), and few of us ever felt the need. But the question occasionally comes up in news:comp.lang.tcl : Can we do GOTO in Tcl? Why Tcl has no GOTO command

Summarizing what several folks have written about state machines in news:comp.lang.tcl , here's what I have now:

 proc states body {
    proc goto {id} {uplevel set goto $id; return -code continue}
    uplevel set goto [lindex $body 0]
    set tmp [lindex $body 0]
    foreach {cmd label} [lrange $body 1 end] {
        if {$label==""} {set label default}
        lappend tmp "$cmd; goto [list $label]" $label
    }
    lappend tmp break ;# to match last "default" label
    uplevel while 1 "{switch -- \$goto [list $tmp]}"
    rename goto "" ;# so now Tcl has no GOTO command any more
 }

And here comes a usage example:

 set n 0
 states {
    1    {puts 1:$n; incr n}
    2    {puts 2:$n; if {$n>5} {goto hell}}
    3    {puts 3:$n; goto 1}
    hell {puts hell!}
 }

Looks good enough, runs good enough even if I put the limit at 50000. break terminates the state engine, as would an undefined label (goto exit). If there is no goto at end of a state body, control continues with the next in line. The last state body gets a break appended that fires if there's no goto in front of it. Label 3 is redundant here. Now comes DKF's example of a state machine from Why Tcl has no GOTO command:

 states {
    1 {set x 1}
    2 {incr x $x}
    3 {if {$x < 10} {goto 2}}
 }

I further use states as interpreter for Basic in Tcl ;-)


A much more minimal state machine is used in Playing Assembler, where of course you do lots of jumping around:

 while {pc>0} {
    eval $mem($pc)
    incr pc
 }

which assumes you have the state code in an array with integer keys. A goto (or rather jump) is then implemented like this:

 proc JMP where {uplevel 1 set pc [expr $where-1]} 

Bryan Oakley writes in news:comp.lang.tcl in reply to Cameron Laird who mentioned an earlier attempt in pure Tcl:

I started to do that as well, just to prove a point. I'm slightly curious (in a rhetorical kind of way) what your solution looked like. I was thinking along the lines of the following:

 evalGOTOBlock {
    100 {
        <random bits of code>
    }
    200 {
        <random bits of code>
    }
    ... and so on
 }

... patterned after the switch statement. The theory being, in order to support goto, all code had to be labeled in some manner. In evalGOTOBlock, each block would be eval'd in turn, but a simple "goto N" statement would change the sequencing by breaking out of the block (possibly by throwing a magic error that evalGOTOBlock is looking for) and then resetting the execution order to begin again at the requested block.

Never got beyond that, though. In my heart, I know it's possible. This would make for an interesting challenge at the next tcl conference. Useful for porting those old BASIC programs to tcl :-)


Andreas Otto adds in news:comp.lang.tcl :

i like GOTO too, usually i use

  while {1} {
     ... some code ... or break
     ... some code ... or break
     ...
     ...
     break
  }

to get a goto like behavior.


Here's Cameron Laird's approach:

My scheme looked more like assembler or, if you prefer, BASIC. It had labels, so that code looked like

  if $something {
    do a
    do b
    goto end_stuff
  }
  do c

 LABEL end_stuff
  do d

or

  if $something {
    do a
    do b
    goto end_stuff
  }
  do c

 end_stuff:
  do d

In the second, [unknown] took anything with a trailing colon as a NOP-acting label. In the first, of course, results could quickly become hilarious with (un)suitable redefinition of "proc LABEL ..."

The rest of it involved:

 1.  getting a handle on the current script
 2.  scanning enough Tcl to be able to skip commands
 3.  creation of the right execution context to resume command interpretation

Exception-handling seemed to be correct automatically.

That's as much as I remember.


One thing which CL calls a "forward goto", jumping to the end of a code block, is easily done with a break in a foreach loop that iterates only once, e.g.

 foreach _ _ {
    # some processing
    if {$condition} break
    # some more processing
 }

Arjen Markus I once read about an alternative to GOTO, the COMEFROM construct. Want to try that in pure Tcl? RS: A "man page" for 1973 FORTRAN is at [L1 ] A better reference on comefrom might be intercal [L2 ] -- in 1973, COME FROM had been documented as a part of the language.

Arjen Markus I knew it was something with FORTRAN. Glad you could turn up the reference. (The famous paper by E. Dijkstra has engendered a whole bunch of papers pro and contra the claim).


Arts and crafts of Tcl-Tk programming