Continuation is a powerful language feature, that can help to solve in simple and elegant ways some kind of programming tasks... Tcl doesn't provide it yet, nor is it's current implementation "adapted" for an easy "addition" of it....
Several years ago, I implemented some sort of continuation in the Tcl 7.3 version. Some months later I found in the Internet an other implementation of continuation based on Tcl 7.5 called AgentTcl; I didn't try to understand how it was done, my impl. worked for me.
Some years later, I had a look at the Tcl 8.x versions and discovered the switch from "pure strings" to the "Tcl_Obj" and also the "byte-compiler"; this didn't inspire me much to "reimplement continuations"....
This summer (2006), I made a "real try", first beginning with a "pure tcl" way-of-doing, based on "code intrumentation" (but soon I realized that this wouldn't be satifying for a "strong" and "truly useable" feature), followed by an attempt to do it as a Tcl Extension. This was better looking from the "application side" (tcl scripting), but "too bad" regarding the C code (too much duplication of Tcl core code, no chance to be maintained...). Finally I came up with an impl. as Extension requiring some changes in the Tcl C core.
New (March 2008): First-class Continuation Objectsinit initialize (enable) the cnt context. reset reset the cnt context (forget all cnt objects). dbglevel set tracing level for debug purpose. disabled eval script with cnt disabled (for speedup). loaderstorer set the loaderstore for persistence. new create a cnt object (variable). update update a cnt object (store current execution state in it). resume resume execution at a cnt ('s last update state). current create a cnt object and update it (wrapper around new/update). goto goto a cnt (wrappper around resume).there are also some sub-namespaces implementing "higher-level" concepts, like:
new create a consumer object. put put something into the cons (called by producer). get get something from cons (called by the consumer).::cnt::gene (generator)
new: create a generator object. get: get next value from gene. yield: return value to get caller (resuming at next command when called again).and a sub-namespace ::cnt:dls (default loaderstorer) implementing "persistency":
save_frames_and_cnts save frames and cnt objects. save_namespace save namespace. run_file reload and run a (previously saved) script.
package require Cnt ::cnt::initusing the lowest-level procs script.
# do something like: set k 3; while 1 {puts "k=$k"; if !$k break; incr k -1} # create the cnt object set cnt [::cnt::new] # update it with the current context/continuation (the cc), return value 3 set k [cnt::update $cnt 3] puts "k=$k" # maybe resume it, with a new value if $k {cnt::resume $cnt [expr $k-1]} puts donethe goto example (syntaxic sugar) script.
# do something like: set k -2; while 1 {puts "k=$k"; incr k; if !$k break} set k -2 # create and update a cnt object set label [::cnt::current] puts "k=$k" incr k # maybe goto if $k [::cnt::goto $label] puts donethe producer/consumer example script.
# define the consumer proc (a standard function, no state machine) proc consume {a b c} { while 1 { set v [::cnt::cons::get] # work with $v puts "consuming v=$v" # ... } } # create the consumer object set cntc [::cnt::cons::new consume "a" "b" "c"] # produce ... call consumer for {set i 0} {$i < 30} {incr i} { # work # ... # call consume ::cnt::cons::put $cntc $i } # delete the consumer object unset cntc puts "consumer done"the generator example script.
# define the generator proc (standard proc, no state machine) proc generate {b s} { set i $b while 1 { ::cnt::gene::yield $i incr i $s } } # create the generator object set cntg [::cnt::gene::new generate 1 2] # use it. while 1 { set v [::cnt::gene::get $cntg] if {$v > 20} break puts "got v=$v" } # delete it unset cntgsaving/restoring the interp context script.
#define the loader-storer package ::cnt::loaderstorer ::cnt::dls # define the saving proc proc save {filename cnt rval} { ::cnt::disabled { ::cnt::dls::save_frames_and_cnts $cnt -rval $rval frames cnts restart puts stderr "save: dumping to $filename" set f [open $filename w] puts $f "# -*- mode: tcl -*-\n" puts $f "package require Cnt\n\n[::cnt::dls::save_namespace {}]" puts $f "\# frames\n\n::cnt::init disabled\n::cnt::loaderstorer ::cnt::dls\n\n$frames" puts $f "\# cnts\n\n$cnts\n\# restart\n\n$restart\n\# end" close $f } } # create a cnt object set cnt [::cnt::new] # update it set what [::cnt::update $cnt "save"] puts "$what" set filename /tmp/x.cnt if {$what == "save"} { # save, setting the restart cnt and value if [catch {save $filename $cnt "restarted"} err] { puts stderr "save: $err" exit 1 } } set n 4 puts "reloading/restarting in $n secs" exec sleep $n ::cnt::dls::run_file $filename
cd .../somewhere tar zxvf ..../cntTcl8.4.14-src.tgz tar zxvf ..../tclCnt1.1.14-src.tgzSince I do not know how to do it the right way, I arranged for tclInt.h to be found by cntTcl using symbolic links.
cd ..../somewhere/tclCnt1.1.14/generic ln -s ../../tcl8.4.14/generic/tclInt.h . ln -s ../../tcl8.4.14/generic/tclIntDecls.h .and also for tclCntObj.h to be found by Tcl.
cd ..../somewhere/tcl8.4.14/generic ln -s ../../tclCnt1.1.14/generic/tclCntObj.h .This should be ok (no need to redo) on a Unix system, (on WinX, to copy the files to the right place may be a solution).
cd .../somewhere/tcl8.4.14/unix # configure to take in account your somewhere dir (change/add options to suit your needs) ./configure --enable-64bit --enable-symbols --disable-shared --prefix=/usr/localtcl8.4.14 gmake gmake test sudo gmake install # do some tests to see if installed stuf works echo puts hello | /usr/localtcl8.4.14/bin/tclsh8.4 # should say hello
cd .../somewhere/tclCnt1.1.14 # configure ./configure --with-tcl=../tcl8.4.14/unix --enable-symbols --prefix=/usr/localtcl8.4.14 gmake sudo gmake install # check the installation echo 'puts [package require Cnt]' | /usr/localtcl8.4.14/bin/tclsh8.4 # should say 1.0.0
# run an example examples/lowlevel.tcl # and others examples/goto.tcl examples/prodcons.tcl examples/generator.tcl examples/saverest.tcl