Table of Contents

Prefix TO

A simple forth implementation of TO for values,
extendable with other prefixes,
extendable for other word types.
(an 20dec2022)

VARIABLE & VALUE, pros & cons

Style

VARIABLE with @ ! and +!    = nice pure forth style
VALUE with TO and +TO       = typical non forth style

Psychology

• VARIABLE
The name provides the location of the variable. ! and @ produce programming noise that easily causes errors. For pointers you need even the combinations @ @ and @ ! .
• VALUE
The name gives the value itself, which is very readable.

Efficiency

Generally variables or values are more often read than written. With values you get rid of many fetch commands.
( BASE @ STATE @ >IN @ )

Programming technique

Variables and values allow nearly the same programming techniques. The weak point of values is that sometimes the address is needed. For example if you want to access a value from assembler or in factorisations like this:

AN and WO are variables:

 : EXCHANGE  ( an wo -- ) 2>r 2r@ @ swap @ r> ! r> ! ;

That's not possible with values. AN and WO are values:

 an wo   to an to wo

We solved that (in noForth) with the ADR prefix. ADR before a value returns the location of the value. That gives VALUE at least all the technical capabilities of VARIABLE.

Extendable

With the code below you can define

About the code

PREFIX stands for the global prefix (symbolic, a very small number). PFX stands for the real action in a specific situation. A PREFIX searches the PFX-LIST for the appropriate PFX.

Two steps in the code are not portable:

: WORD-TYPE ( token -- word-type ) @ ;
: DATA-ADDR ( token -- data-addr ) >body ;

Example:

10 value TEST

Given the token of TEST we need a function WORD-TYPE that can determine that TEST is a value. For indirect threading this could simply be a @ assuming that the token = CFA and that CFA @ gives DOVALUE . In some forth implementations this may be problematic.

In the code below we assume that

  1. xt @ of a value is characteristic for values (WORD-TYPE).
  2. the body of a value contains the data.

The code

\ words:  PFX-LINK   PFX   FIND-PFX   PREFIX

variable PFX-LINK       \ Linked list for pfx's
here 0 , pfx-link !

\ Put a pfx in the pfx-list
: PFX ( 'pfx <prefix-name> <name-of-data-word> -- ) \ See examples below.
    here >r pfx-link @ ,
        ' >body @   \ prefix#
        ' @         \ word-type
        + ,         \ pfx-id ( = word-type + prefix# )
        ,           \ xt of pfx-action
    r> pfx-link ! ;
\ An element in the pfx-list consists of three cells: | link | pfx-id | 'pfx |

: FIND-PFX ( pfx-id -- link )   \ pfx-id = word-type + prefix#
    pfx-link
    begin dup @             \ continue if not end of list
    while 2dup cell+ @ <>   \ continue while not found
    while @                 \ next element in the list
    repeat then
    nip ;

\ Define a (global) prefix
: PREFIX ( n <name> -- )
    create ,  immediate     ( prefix# <prefix-name> -- )
    does>                   ( <name-of-a-data-word> -- )
    @ >r                    \ prefix#
    ' dup >body swap        \ data-address 'data-word
    @                       \ word-type
    r> +                    \ pfx-id ( = word-type + prefix# )
    find-pfx
    dup @ 0= abort" Prefix not allowed "
    cell+ cell+ @           \ jump over link and pfx-id and get xt of pfx
    execute ;

The two user words

PREFIX ( n <name> -- )

This word is used to define a global prefix. It takes a (very small) number and a name as input. The number serves as the identifier for the prefix. The name is the symbolic name that will be used in your forth code. The word PREFIX creates an immediate word with the provided name that, when invoked, searches for the appropriate pfx action in the pfx-list based on the combination of the prefix number and the word type of the next word in the input stream.

PFX ( 'pfx <prefix-name> <name-of-data-word> -- )

This word is used to add a new entry to the pfx-list. It takes as input: the pfx action execution token, the symbolic (global) prefix name and the name of a data word (an instance of the word type which must be made accessible through the prefix).

Applying PREFIX and PFX

\ ----- Defining global prefixes -----
0 prefix TO
1 prefix +TO
2 prefix ADR
\ etc.

\ ----- value -----
: VALUE ( x name -- ) create , does> @ ;

\ pfx's for VALUE (internal words)
: TO.V  ( x -- ) state @ if postpone literal postpone ! exit then ! ;
: +TO.V ( x -- ) state @ if postpone literal postpone +! exit then +! ;
: ADR.V ( -- x ) state @ if postpone literal then ;

\ Put the pfx's for VALUE in pfx-list. We have to create a value to achieve this.
7 value AMSTERDAM
' to-v  pfx   to amsterdam
' +to.v pfx  +to amsterdam
' adr.v pfx  adr amsterdam

\ Better: define !() in assembler (msp430 example)
\ code !() ( x -- )  \ store x in inline following address
\    ip )+ day mov   \ read inline address
\    tos day ) mov   \ store x in that address
\    sp )+ tos mov
\    next end-code
\ Then          postpone literal postpone !
\ becomes       postpone !() ,

\ ----- 2value -----
: 2VALUE ( dx name -- ) create , , does> 2@ ;
: TO.2V  ( x -- ) state @ if postpone literal postpone 2! exit then 2! ;

\ Put pfx's for 2VALUE in pfx-list
77777. 2value ROTTERDAM
' to.2v pfx    to rotterdam
' adr.v pfx   adr rotterdam    \ Same action as for VALUE

Testing

\ ----- value test -----
amsterdam                 .
1 to amsterdam  amsterdam .
1 +to amsterdam amsterdam .
adr amsterdam   @         .

10 value OSLO        oslo .
: T1 1  to oslo ; t1 oslo .
: T2 1 +to oslo ; t2 oslo .
: T3 adr oslo ;      t3 @ .

\ ----- 2value test -----
rotterdam                      d.
11111. to rotterdam  rotterdam d.
adr rotterdam 2@               d.

: T4 to rotterdam ;
22222. t4            rotterdam d.
: T5 adr rotterdam 2@ ;     t5 d.
55555. +to rotterdam    ( this should cause an error )
\ <><>

Alternative Implementations