Version 5 of TclForth

Updated 2012-10-31 09:08:57 by wej

wej - Several wiki pages describe how to handle Forth in Tcl ( RPN in Tcl, Trying FORTH in Tcl, A different Forth, f4t ). This page describes a different approach that, following f4t, aims "to integrate Forth and Tcl as harmonically as possible". TclForth is a working Forth system that uses Tcl as its native language. It has the following features.

Code words - The code words are Tcl procs without formal arguments. TclForth replaces the formal argument by a stack diagram. In normal Forth systems the stack notation is a comment that is ignored by the compiler. In TclForth the compiler uses the diagram to add the appropriate stack handling.

Example: The Forth word

     code int ( n1 -- n2 )  
          set n2 [expr int($n1)]  

is compiled to

      proc int {} {
          set n1 [pop];  set n2 [expr int($n1)]; push $n2 ;
     }

: (colon) words - TclForth is subroutine threaded, like most modern Forth systems. As a consequence, the high level colon words are also implemented as Tcl procedures. Here is an example from the Forth console.

: Prompt ( -- )
     depth 0> withStack and
     if   "$::stack ok>"
     else "ok>"
     then Console append update
;

is compiled to

proc Prompt {} {
     depth ;  0> ; push $::withStack ;  and ;
     if [pop]  {
          push "$::stack ok>" ;
     } else {
          push "ok>" ;
     }
     $::Console insert end [pop] ; update ;
}

The code procs call Tcl commands, the colon procs call Forth words.

The Forth words are collected in a dictionary, which is used by the compiler (and would be used by the interpreter, but in TclForth everything is compiled before being executed). The dictionary is implemented as the array Words(). The indices in the array are the Forth word names, the values are individual compiling scripts. For code and colon words the script inserts the name with a following ";" .

Compiler words handle the cases where you can't just insert a name and a semicolon. Foremost the group of flow control words. Prompt illustrates the compiling action of if, else and then. In similar ways TclForth implements the remaining standard Forth flow words and includes foreach and switch (as a case-of-endof-endcase construct).

All Tcl commands are thus available in Forth, in principle. Example:

Compiler doafter
     appendcode " after \[pop\] \[pop\] ;  "

If you miss a feature, add a compiler word.

Data words - We want to address data elements in postfix. Here is an example with three data types, string, variable and list.

: SaveComline ( comline -- )
     comline "" !=
     if   comline  comhistory append      
          comhistory length  comindex set     
     then
;

TclForth converts this to

proc SaveComline {} {
     set comline [pop] ; push $comline ; push "" ;  != ;
     if [pop]  {
          push $comline ; lappend ::comhistory [pop] ; push [llength $::comhistory] ; set ::comindex [pop] ; 
     }
} 

The datatypes are implemented as objecttypes with separate message and method. For the data types in SaveComline the objecttypes are defined as (reduced to the messages used in the example)

objecttype variable 
     {}      {push $obj}
     set     {set obj [pop]}
     ...      ...

objecttype string  
     {}      {push $obj}
     ...      ...

objecttype list  
     append  {lappend obj [pop]}
     length  {push [llength $obj]}
     ...      ...

An objecttype is compiled as an array with the messages as names and the methods as scripts. When an object is used in a definition the compiler substitutes "obj" in the method code by the actual object's name. The default message {} is activated when the word following an object is not a valid message of this object. The object then delivers its value. This lets you write A B + C set. Or A B + C ! in the standard Forth way. @ and ! are included in the message list, of course.

Stack Diagram - The stack diagram also defines the stack-independent local variables for a definition. The syntax is ( inputs | locals -- results ) with any number of inputs, locals and results. The local variables are initially compiled as objecttype variable and can then be casted to other types.

Example: : word ( text -- )  cast text string  ...  

Inner interpreter - Since the Forth words compile to Tcl procedures, there is no need for a Forth inner interpreter. The Tcl run time system is the inner interpreter of TclForth.

Tk is the true object of desire for Forth programmers. Several attempts to use Tk widgets from a Forth system have been reported, TkForth is still the best documented. Tk has clear syntax that can be handled in TclForth code words. There is no pressing need to invent a Forth syntax. However, when packed in TclForth objecttypes the widgets can be handled also in colon words.

Example: The objecttype tktext

objecttype tktext
     instance {uplevel #0 {set obj [pop]; text $obj} }
     {}       {push $obj}
     config   {eval $obj [concat configure [pop]]}
     set      {eval $obj [concat config [pop]]}
     pack     {eval [concat pack $obj [pop]]}
     tkwait   {tkwait window $obj}
     bind     {eval [concat bind $obj [pop]]}
     append   {$obj insert end [pop] ; $obj mark set insert end}
     yview    {$obj yview moveto [pop]}
     end      {push [$obj index end]}
     insert   {push [$obj index insert]}
     delete   {swap ; $obj delete [pop] [pop]}

is used in the Forth console as

: ConsoleWindows  ( -- )
     "TclForth" title
     ".forth" tktext Console
     "-padx 10 -pady 20 -relief sunken -border 1 -highlightcolor white" Console config  
     "-expand 1 -fill both" Console pack
     ".code" tktext CodeWindow
     "-height 4 -pady 10 -padx 10 -relief sunken -border 1 -highlightcolor white"  CodeWindow config 
     "-expand 0 -fill both" CodeWindow pack
;

TclForth is available as a starkit at [L1 ].