Local variable support

For programming a hosted Forth with a GUI interface and for other significant styles of programming, the ANS Forth specification of local variables is inadequate. VFX Forth and other modern Forth systems provide an alternative notation with more functionality and better readability. A subset of this notation became the basis of the Forth200x local variables proposal. The ANS locals mechanism is supported in VFX Forth for backwards compatibility.

Extended locals notation

The MPE extended local syntax provides a number of significant benefits to the ANS standard.

In this implementation, locals are allocated as a frame on the return stack. Note that the word's return address is no longer available.

The following example shows a code extract from a WINPROC, there are the traditional 4 inputs, a local array storing a temporary structure and one output.


: WndProc    {: hWnd uMsg wParam lParam | clientrect[ RECT ] -- res :}
  uMessage WM_SIZE =
  if
    hWnd clientrect[ GetClientRect drop    \ Get client rect
    hWndChild @                            \ useto resize child
    #0
    #0
    clientrect[ RECT.right @
    clientrect[ RECT.bottom @
    TRUE MoveWindow drop
    0 exit
  then

  ...... Other Messages ....

  hWnd uMessage wParam lParam DefWindowProc      \ Msg default.
;

The following syntax for named inputs and local variables is used.

The sequence:


{: ni1 ni2 ... | lv1 lv2 ... -- o1 o2 :}

defines named inputs, local variables, and outputs. The named inputs are automatically copied from the data stack on entry. Named inputs and local variables can be referenced by name within the word during compilation. The output names are dummies to allow a complete stack comment to be generated.

For compatibility with previous implementations, { is accepted in place of {: and } in place of :}. The change to {: ... :} took place as a result of the Forth200x standard.

Named inputs and locals return their values when referenced, and must be preceded by -> or TO to perform a store, or by ADDR to return the address.

Arrays may be defined in the form:


arr[ n ]

Any name ending in the '[' character will be treated as an array, the expression up to the terminating ']' will be interpreted to provide the size of the array. Arrays only return their base address, all operators are ignored.

In the example below, a and b are named inputs, a+b and a*b are local variables, and arr[ is a 10 byte array.


: foo    {: a b | a+b a*b arr[ 10 ] -- :}
  a b + -> a+b
  a b * -> a*b
  cr a+b .  a*b .
;

Floating point arguments (inputs) and temporaries are declared by placing F: before the name, but not for arrays of floats, which should be declared as above. Floating point locals use the CPU's native FP (80x87) stack, and so are most suitable for use with the %lib%/x86/ndp387.fth floating point package. Floating point locals are stored in the extended 80 bit (10 byte) format. This is the default for the %lib%/x86/ndp387.fth code. The default action of an FP local is to return its value. The following operators can be applied:


: foo2 {: a f: f1 b f: f2 | f: f3 f: f4 c d e -- :}
  ...
;

The arguments a and b above are integer arguments taken from the Forth data stack. The arguments f1 and f2 are FP arguments taken from the floating point unit. Local values f3 and f4 are FP locals and the others are integer locals. An example of using FP locals follows:


: foo3  {: f: f1 | f: f2 f: f3 -- :}
  0e0 -> f2  10e0 -> f3  ( noop )
  f1 add f2  f1 sub f3  ( noop )
  f2 f.  f3 f.
;

: {             \ --
The start of the traditional brace notation { ... }.

: {:            \ --
The Forth200x name to start the extended local variable notation. Use in the form:

{: ni1 ni2 ... | lv1 lv2 ... -- o1 o2 :}

ANS local definitions

The ANS locals definitions are provided for use with ANS standard compliant code. The ANS locals system offers limited functionality.

: (LOCAL)       \ Comp: c-addr u -- ; Exec: -- x
When executed during compilation, defines a local variable whose name is given by c-addr/u. If u is zero, c-addr is ignored and compilation of local variables is assumed to finish. When the word containing the local variable executes, the local variable is initialised from the stack. When the local variable executes, its value is returned. The local variable may be written to by preceding its name with TO. The word (LOCAL) is intended for the construction of user-defined local variable notations. It is only provided for ANS compatibility.

: LOCALS|       \ "<name1> ... <namen> |" --
Create named local variables <name1> to <namen>. At run time the stack effect is ( xn..x1 -- ), such that <name1> is initialised to x1 and <namen> is initialised to xn. Note that this means that the order of declaration is the reverse of the order used in stack comments! When referenced, a local variable returns its value. To write to a local, precede its name with TO. All locals created by LOCALS| are single-cell integers. In the example below, a and b are named inputs.


: foo      \ a b --
  locals| b a |
  a b +  cr .
  a b *  cr .
;

Local variable construction tools

variable LVCOUNT        \ -- addr
Holds the offset in the frame for the next local integer variable.

: FRADJUST      \ size -- offset
Adjust the size of the current local values frame. Used by words that create additional local variables outside a LOCALS| ... | or { ... } notation.