Local Variables in Ficl

Forth Inspired Command Language 
Author: John Sadler (john_sadler@alum.mit.edu)
Created: 19 July 1997 
Revision 2.04: 20 May 2000

Local Variables

Locally scoped variables came late to Forth. Purists seem to feel that experienced Forth programmers can write supportable code using only anonymous stack variables and good factoring, but they complain that novices use global variables too much. Local variables cost little in terms of code size and execution speed, and are very convenient for OO programming, where stack effects are more complex. I use them a lot. 
Please refer to the Standard for more information on local variables.

Johns-Hopkins local variables

ANS Forth does not specify a complete and satisfying local variable facility. Instead it defines a foundation upon which to build one. Ficl comes with an adaptation of the Johns-Hopkins local variable syntax developed by John Hayes et al. This is my preferred form, and I've extended it to make OOP a bit simpler. Local variables can only be declared inside a definition, and are only visible in that definition. Here's the syntax of a JH local variable declaration:
{ <initialized-locals> | <cleared-locals> -- <ignored> }
The declaration is designed to look like a stack comment, but it uses curly braces instead of parens. The <initialized-locals> names get their initial values from the stack when the word executes. The <cleared-locals> names are (you guessed it) set to zero when the word executes, and any characters between -- and } are treated as a comment. The | and -- sections are optional, but they must appear in the order shown if they appear at all. 
Double cell locals (AKA 2locals): ordinarily, each local represents one cell. Local variable names prefixed with the character '2' in the declaration are double-cell locals. They behave the same as single cell locals in all other respects. I use 2locals quite a bit in Ficl's OO classes, because objects in Ficl are require two cells on the stack. You can modify the value of a double cell local with TO the same as you would a single cell local.
Following are some examples to illustrate usage (they are not intended to be good code otherwise). Try these out in FiclWin to get a feeling for how they work...
: local-demo  { a b | c -- }
    ." a = " a . cr
    ." b = " b . cr
    ." c = " c . cr ;
1 2 local-demo  ( you should see 1 2 0 )

: my2dup  { 2x }   x x ;  ( uses a 2local )
1 2 my2dup .s 
.( you should see 1 2 1 2 on the stack ) cr empty

: my2swap   { 2x 2y -- y x }   y x ;  ( note use of 2locals )
1 2 3 4 my2swap .s
.( you should see 3 4 1 2 on the stack ) cr empty

: totally-lame-swap  { x y | temp -- y x }
    y to temp
    x to y
    temp to x
    x y ;

The last definition introduces the use of TO applied to local variables. TO knows whether it's operating on a LOCAL, a 2LOCAL, or a VALUE, and does the right thing accordingly. 
 

Other local variable syntaxes (deprecated)

There are other syntaxes in use for local variables. You get the same compiled code regardless of which style of local declaration you choose, but the Johns-Hopkins syntax is more readable, more flexible, and supports 2LOCALs - if you agree, then skip this section. 

Ficl includes support for LOCALS and LOCALS EXT words (all three of them!). I've implemented both of the local variable syntaxes suggested in DPANS Appendix A.13. Examples: (By the way, Ficl implements -ROT as : -rot   2 -roll ; )

\ Using LOCALS| from LOCALS EXT
: -rot   ( a b c -- c a b )
   locals| c b a |
  c a b 
;
    \ Using LOCAL END-LOCAL
    : -rot   ( a b c -- c a b )
        local c
        local b
        local a
        end-locals
        c a b
    ;

Build Controls

Local variable support is optional because it adds a small amount of overhead to the outer interpreter. You can disable it by setting FICL_WANT_LOCALS to 0 in sysdep.h. Beware: Ficl's OOP code makes heavy use of local variables, so if you disable locals, you're going to lose other capabilities too. Local variables can make Forth code quite a bit easier to read, so I'd encourage you to experiment with them. 

The default maximum number of local variables is 16. It's controlled by FICL_MAX_LOCALS in sysdep.h. 

Release notes for local variables

Ficl 2.02 includes by default an implementation of the Johns Hopkins local syntax (as best I can determine it from examples on the web). This syntax lets you declare local variables that look very much like a stack comment. Variables in the declaration appear in the "correct" order for a stack comment. Everything after the -- is treated as a comment. In addition, you can insert a | before the -- to declare one or more zero-initialized locals. Example: 
:tuck0   { a b c | d -- 0 a b c }
    d a b c ;
The | and -- delimiters can appear at most once, and must appear in the order shown in the example to work correctly. The local declaration ends at the first occurrence of }. The declaration must all be on one line as presently implemented. 

Deprecated: Ficl 2.01 added yet another local syntax that models a stack comment. This one is not compiled in the release, but you can add it by editing softwords/softcore.bat to include the file ficllocal.fr. In this case, parameters are re-ordered so that the rightmost initialized param comes from the top of the stack. The syntax is: 

{{ <initialized params> -- <cleared params> }}
You can omit either the initialized or the cleared parameters. Parameters after the double dash are set to zero initially. Those to the left are initialized from the stack at execution time. Examples (lame ones, admittedly): 
 
: -rot   ( a b c -- c a b )
    {{ a b c }} 
    c a b  
; 

: tuck0  ( a b c -- 0 a b c ) 
    {{ a b c -- d }} 
    d a b c