====== 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 -- ) \ 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 -- )
create , immediate ( prefix# -- )
does> ( -- )
@ >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 -- )
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 -- )
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