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
- prefixes with other functions ( +TO INCR DECR ADR *TO … )
- prefixes for other word types ( 2VALUE STRINGVALUE … )
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
- xt @ of a value is characteristic for values (WORD-TYPE).
- 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 ) \ <><>