|
| ||||
|
CREATE |
Ieder Forth-woord bezit de volgende onderdelen:
create PLEKNu 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:
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.
plek @ . [enter] 0 ok 2002 plek ! [enter] ok -1 plek cell+ ! [enter] okIn 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 DOZIJNDOZIJN 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 VARKENDe 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.
100 varken 300 varkenVARKEN 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.
De sleutel is een uniek getal dat bij een bepaald woord hoort, vergelijkbaar met een paspoortnummer. Wat kun je doen met een sleutel?
|
Wat zit er in het varken? |
Dus met een tick, maar zonder het varken kapot te slaan:
' varken >body @ . [enter] 400 okof 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 okOm 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.
3 keuze [enter] ok 1 keuze [enter] ok 2 keuze [enter] ok 0 keuze [enter] 111 ok 0 keuze [enter] 0 okDit 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.
|