Local Variables in Ficl

Local Variables

Named locally scoped variables came late to Forth. Purists feel that experienced Forth programmers can (and should) 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 (maybe I'm a weenie). 
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 characters "2:" in the declaration are double-cell locals. The prefix is not part of the local variable's name, only part of the declaration. 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 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. Also see softwords/string.fr for an example of use of locals in OO code.
: 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  { 2:x }   x x ;  ( uses a 2local )
1 2 my2dup .s 
.( you should see 1 2 1 2 on the stack ) cr empty

: my2swap   { 2:x 2:y -- 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.05 adds 2LOCALS using the "2:" prefix

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