I wrote this about a year ago and am prompted to post it now by JCW's Trying FORTH in Tcl -- JBR 12/11/2002
Here FORTH is not nearly as faithfully reproduced as JCW's. Missing are the ability to get at the input stream with token (word in FORTH?) and I have no return stack (added later to hold loop indicies).
Code is eval'ed as a list and it makes the inner interpreter very fast. unknown is used there to push values onto the stack.
After RS made his comment (below) about the code's brevity I actually looked at it in the light of what I have learned from RS and the rest of the active Wiki authors and I've given it the once over.
Now I'd say its brief. I'm still stumped about a way to make pop a one liner. - RS: Easy if you have the famous K combinator ;-):
proc K {x y} {set x} proc pop {} {K [lindex $::S end] [set ::S [lrange $::S 0 end-1]]}
11dec02 jcw - Pesky types like me would say that this is still a two-liner. How about...
proc pop {} {lindex [list $::S [set ::S [lrange $::S 0 end-1]]] end}
Untested code ... (cool stuff, btw!) RS: pop is a one-liner, and K is a useful tool in the box for many cases ;-)
I knew that K would be the answer but I couldn't see that I should put the value I wanted to keep first. Code updated just so I can say that I've used the K (I'd never used end-1 either) Thanks -- JBR.
After jcw's comment (above), I deleted a little more code in the ":" proc and used a definition of pop similar to his, although now I might have to admit that I'm reaching for brevity at the expence of simplicity here.
I just had to add control structures. I got down my copy of starting FORTH and changed the ":" proc. Now we have a FORTH to tcl translator the inner loop executing FORTH words as compiled tcl. The definitions are not exactally right, I just noticed that LOOP should test at the bottom not the top, but this could be extended to include all of the FORTH control structures. -- JBR, evening 12/11/2002
# Stack Primitives # set S {} ;# Stack set R {} ;# Return Stack (for loop indicies) proc T { } { lindex $::S end } ;# Top value proc S { } { lindex $::S end-1 } ;# Second value proc R { } { lindex $::R end } ;# Top of return stack proc CR { } { puts "" } # Stack Ops # proc psh { x } { lappend ::S $x } ;# Push value stack proc pop { } { lindex [list [T] [set ::S [lrange $::S 0 end-1]]] 0 } ;# Pop value stack proc ! { } { set ::[pop] [pop] } ;# Set named reference top to value proc @ { } { psh [set ::[pop]] } ;# Get named reference top proc . { } { puts -nonewline "[pop] " } ;# Print top proc drop { } { pop } proc swap { } { set ::S [lreplace ::S end-1 end [T] [S]] } proc unknown { args } { psh $args } ;# Push anything thats not a proc (values) # Construct a set of useful binary operators # proc stkops { args } { foreach op $args { proc $op { } "set ::S \[lreplace \$::S end-1 end \[expr \[T] $op \[S]]]" } } stkops + - * / % | & ^ || == <= >= proc : { name list } { set code {} foreach word $list { switch -- $word { if { set word "if \{ \[pop] \} \{" } then { set word \} } else { set word "\} else \{" } do { set word "swap; >R; >R; \n\ while \{ \[lindex \$::R end] <= \[lindex \$::R end-1] \} \{" } loop { set word "set ::R \[lreplace \$::R end end \[expr \[R]+1]] \n\ \}; set ::R \[lrange \$::R 0 end-2]" } +loop { set word "set ::R \[lreplace \$::R end end \[expr \[R]+\[pop]]]\n\ \}; set ::R \[lrange \$::R 0 end-2]" } } lappend code $word } proc $name { } [join $code "\n"] } proc >R { } { lappend ::R [pop] } proc I { } { psh [lindex $::R end] } proc J { } { psh [lindex $::R end-2] } proc K { } { psh [lindex $::R end-4] } : Two 2 : setX { X ! } : dotX { X @ . } : Test { Two 4 * 45 + setX dotX CR } Test ;# --> 53 : BranchTest { if True . else False . then CR } : TestTrue { 1 BranchTest } : TestFalse { 0 BranchTest } TestTrue ;# True TestFalse ;# False : TestSwap { First Second swap . . CR } TestSwap ;# First then Second : TestLoop { 10 0 do I . loop } TestLoop : TestLoop2 { 3 1 do 5 1 do J I * . loop loop CR } TestLoop2 : Test+Loop2 { 3 1 do 6 1 do J I * . 2 +loop loop CR } Test+Loop2
RS: I really like this code for its brevity - and the unknown redirection ;-) My own attempts in that direction, also a little old, are at RPN in Tcl.