HCC!Forth
27 nov 2002


Definiërende woorden

Albert Nijhof
1. Rekenen in Forth
2. Programmeren in Forth
3. Definiërende woorden
4. Compilerende woorden

CREATE

Ieder Forth-woord bezit de volgende onderdelen:
  • Header - met gegevens waardoor het woord o.a. vindbaar is.
  • CF - Code Field, met een verwijzing naar de actie die het woord uitvoert.
  • PF - een Parameter Field, voor data.
Het basiswoord waarmee je in Forth een nieuw woord aanmaakt is CREATE.
 create PLEK
Nu bestaat PLEK, het heeft een header en het heeft een CF. De CF-actie van het woord is de minimale standaardactie, dat is: zet het dataveld-adres op stack. Dat dataveld is nog niet afgebakend, maar dat zou bijvoorbeeld zo kunnen:
 0 , 0 , 
Het dataveld beslaat nu 2 cellen, elk met een nul gevuld.

plek ( -- body ) Zet op stack het adres van een gebied dat 2 cellen bestrijkt.

Om dit verhaal leesbaar te houden gebruik ik de gangbare termen:

  • HEADER - de header omvat de naam en nog een paar ander attributen. De header weet waar het DOERVELD ligt.
  • DOERVELD - met de verwijzing naar de DOER,de eigenlijke actie van het woord. Van hieruit is bekend waar de BODY zich bevindt.
  • BODY - het dataveld(beginadres).

Het EXECUTEREN van een Forth-woord houdt in dat de DOER uitgevoerd wordt op de DATA in de BODY.

De colon-definitie

Geldt dat nu ook voor de dubbele-punt definitie?
Ja, ook hier is er sprake van een Doer die Data hanteert. Zo ziet het plaatje er uit voor hi-level Forth definities:


De Doer die DO: (of DOCOLON) heet voert de eigenlijke actie uit. Hij loopt door de gecompileerde lijst met woorden en zorgt er voor, dat zij op hun beurt uitgevoerd worden.

Hoe die gecompileerde lijst er uit ziet hangt natuurlijk samen met de actie van DO: en dat kan op verschillende manieren opgelost worden. In sommige Forth's wordt er een lijst van subroutines gecompileerd waardoor DO: zelfs een no-operation is geworden. Het Doerveld kan dan rechtstreeks verwijzen naar de Body. Dit laatste geldt ook voor de (meestal kleine) machinetaalwoordjes, de zogenaamde primitieven. De manier waarop men dit soort problemen opgelost heeft in een Forth noemt men het type "bedrading" van de Forth. De oervorm die ik hierboven gegeven heb heet "indirecte bedrading". De andere typen zoals directe bedrading, subroutine-bedrading en token-bedrading kun je beschouwen als (tempo)optimalisaties hiervan.

@ en !

In dit kopje, dat er zo alarmerend uitziet, staan de commando's die adressen uitlezen en beschrijven, zoals je dat met PEEK en POKE doet in BASIC.

@ (adres - x ) Lees het getal dat op «adres» staat, resultaat x.
! ( x adres -- ) Schrijf het getal x weg naar «adres». Dit toegepast op PLEK:

plek @ . [enter] 0  ok
2002 plek ! [enter] ok
-1 plek cell+ ! [enter] ok 

In de body van PLEK staan nu de getallen 2002 en -1.
plek @ plek cell+ @ + . [enter] 2001  ok

DOES>

Een van de simpelste defining words is CONSTANT. Hier volgt zijn definitie, want defining woorden moeten ook ooit gedefiniëerd zijn:
: CONSTANT ( x «name» -- ) create , does> @ ;
CONSTANT verwacht een getal op stack en een naam achter zich.
Het woord DOES> leidt de code van de doer in.
12 constant DOZIJN
DOZIJN zet niet alleen zijn body-adres op stack, hij voert daar ook nog een @ op uit.

dozijn ( -- 12 ) Zet 12 op stack.

Het spaarvarken

: SPAARPOT ( «naam» -- ) create 0 , does> +! ;
SPAARPOT verwacht alleen de naam van het nieuwe woord achter zich:
spaarpot VARKEN
De body van VARKEN is 1 cel groot en bevat het getal 0.

+! ( x adres -- ) Tel x op bij het getal dat in «adres» staat.

dus

varken ( x -- ) Tel x op bij het getal in de body van VARKEN.
Met andere woorden, iedere keer dat je VARKEN uitvoert, verandert zijn inhoud.

100 varken
300 varken
VARKEN zal nu 400 bevatten. Is dat te controleren?

Tick

Alle Forth-woorden zijn in Forth geschreven, of anders gezegd, Forth bestaat uit Forth-woorden. Er is geen duistere of geheimzinnige kern die het eigenlijke werk opknapt, alles wat er in Forth gebeurt is door de programmeur zichtbaar te maken en te gebruiken.
Zo ook het opzoeken van een woord.

' ( «naam» -- xt/error ) Zet de sleutel voor «naam» op stack; geef een foutmelding als «naam» niet te vinden is.
«xt» staat voor de sleutel (in het engels 'token') van een woord.
«'», apostrof, heet in het engels «tick».

De sleutel is een uniek getal dat bij een bepaald woord hoort, vergelijkbaar met een paspoortnummer.

Wat kun je doen met een sleutel?

  • Je kunt er EXECUTE op loslaten:
     ' varken execute 
    doet het zelfde als
     varken 
    De grens tussen code en data begint te vervagen.
    Bedenkt dat EXECUTE rampzalige gevolgen heeft als je het uitvoert op een willekeurig getal.

  • Je kunt via een sleutel de body bereiken:
     ' varken >body 
    geeft het adres van de body van VARKEN.

    >body ( xt -- body ) Bepaal het body-adres als de sleutel gegeven is.

Wat zit er in het varken?

Dus met een tick, maar zonder het varken kapot te slaan:
' varken >body @ . [enter] 400  ok
of in een definitie:
: @VARKEN ( -- x ) [ ' varken >body ] literal @ ;
Tussen de vierkante haken wordt het compileren even onderbroken om een getal te berekenen op stack. Daarna zet LITERAL het getal in de definitie (vroege binding), die er uiteindelijk zó uit komt te zien:
: @VARKEN 46656 @ ;
(er van uitgaande dat het berekende adres 46656 is)
@varken . [enter] 400  ok
Om het ingewikkeld te maken komt hier nog een variant:
: @VARKEN ['] varken >body @ ;
['] ( «naam» -- ) Onderbreek tijdelijk het compileren, bepaal de sleutel van «naam» en zet die als getal in de lopende definitie.

De definitie komt er uiteindelijk zó uit te zien:

: @VARKEN 1296 >body @ ;
(er van uitgaande dat de berekende sleutel 1296 is)

CASE

Een laatste voorbeeld.
: VERDELER ( «naam» -- ) create does> swap cells + @ execute ;
Toepassing:
: K0 @varken dup . negate varken ;
: K1 1 varken ;
: K2 10 varken ;
: K3 100 varken ;
verdeler KEUZE ' k0 , ' k1 , ' k2 , ' k3 ,
KEUZE heeft vier Forth-woorden in zijn Body.

keuze ( x -- ) Als x=0, 1, 2 of 3: voer de bijbehorende actie uit.
Voor andere waarden van x gebeurt er iets onvoorspelbaars, er zijn immers maar 4 acties ingevuld.

3 keuze [enter] ok
1 keuze [enter] ok
2 keuze [enter] ok
0 keuze [enter] 111  ok
0 keuze [enter] 0  ok
Dit voorbeeld laat zien dat je in Forth op eenvoudige wijze de meest complexe constructies kunt bouwen als je die nodigt denkt te hebben voor een applicatie. Forth is een uitgangspunt. Daarom is het niet zinvol om een Forth bij voorbaat vol te stoppen met allerlei extra features. Die kan de programmeur zelf beter maken want hij is het die precies weet wat hij nodig heeft. Hij hoeft zich dan ook niet meer bezig te houden met het onderdrukken van de ongewenste effecten van "voorgebakken" commando's.

1. Rekenen in Forth
2. Programmeren in Forth
3. Definiërende woorden
4. Compilerende woorden


© 2007 HCC!Forth