BOUCLES ET BRANCHEMENTS
A. Primitives de branchement.
Partie exécution compilée par une structure de saut conditionnel. Exécute
une rupture de séquence à l'adresse 16 bits qui suit immédiatement ?BRANCH
si le nombre 16 bits présent sur la pile est égal à zéro, sinon passe à
l'exécution du mot qui suit cette adresse. L'adresse en question est absolue
ou relative selon les systèmes, les nouvelles normes précisant l'adressage
absolu.
Note concernant ?BRANCH et BRANCH: ces deux mots ne sont pas destinés à être
utilisés directement dans une définition (ou alors à quoi sert-il de
disposer d'un langage structuré?) et rares sont les situations où les
structures existantes se révèlent insuffisantes. Exemple:
inventons un mot de pseudo-récursivité:
: ASSEZ?
COMPILE ?BRANCH LAST @ NAME> >BODY , ; IMMEDIATE
( compilation en adressage absolu)
Le mot ASSEZ? ( f ---) réexécute tout le mot en cours jusqu'à ce que le flag
disponible au sommet de la pile de données soit vrai. Pourtant, le mot
suivant:
: ATTENTE CR ." je m'ennuie de vous"
KEY? ASSEZ? CR ." enfin!" ;
serait compilé octet pour octet comme celui-ci:
: ATTENTE BEGIN CR ." je m'ennuie de vous"
KEY? UNTIL CR ." enfin!" ;
Pour être juste, signalons qu'on pourrait écrire IF..ASSEZ?..THEN alors
que BEGIN..IF..UNTIL..THEN ne serait pas autorisé. Rien n'est simple.
Exécute un test d'erreur de parité lors de la définition des éléments des
structures de contrôle.
Partie exécution compilée par une structure de saut inconditionnel. Pour
exemple, ce mot est compilé par des mots tels que ELSE, THEN ou AGAIN. Le
mot BRANCH est suivi, dans une définition compilée, d'une adresse 16 bits
désignant la destination du branchement. Cette adresse est absolue ou
relative selon les systèmes, les nouvelles normes précisant l'adressage
absolu.
B. Primitives de compilation de branchements.
Dépose un flag vrai et exécute <MARK.
Exécute <RESOLVE si le flag est vrai, sinon émet un message d'erreur. Le
couple ?<MARK..?<RESOLVE est équivalent au couple <MARK..<RESOLVE avec un
contrôle minimal du couplage.
Dépose un flag vrai et exécute >MARK.
Exécute >RESOLVE si le flag est vrai, sinon émet un message d'erreur. Le
couple ?>MARK..?>RESOLVE est équivalent au couple >MARK..>RESOLVE avec un
contrôle minimal du couplage.
Note: quand on a connu les contrôles de structures compilées des précédents
standards, on n'ose pas dire ce qu'on pense de ces quatre derniers mots.
C'est probablement parfois fatigant d'être génial, hein Mike ?
Elément d'une structure de compilation pour opérer une rupture de séquence
avec saut en arrière. Empile une adresse de retour utilisée par <RESOLVE pour
définir un branchement en arrière. Le couple <MARK ... <RESOLVE placé dans
deux mots immédiats de contrôle de compilation est indissociable. Exemple,
soit à ajouter aux structures déjà riches du FORTH une structure de type:
... FAIRE <mot1> ... <motn> <condition> TANT-QUE ...
qui réalisera une boucle <mot1> ... <motn> tant que la <condition> est vraie.
Sans faire appel à BEGIN..NOT UNTIL, il est possible d'écrire:
: FAIRE <MARK ; IMMEDIATE
: TANT-QUE COMPILE NOT COMPILE ?BRANCH <RESOLVE ; IMMEDIATE
qui pourront être utilisés sous une forme du type:
: PROMENADE ( ---)
FAIRE
UN-TOUR IL-FAIT-BEAU
TANT-QUE ;
Elément d'une structure de compilation pour opérer une rupture de séquence
avec saut en arrière. Utilise l'adresse laissée par <MARK pour compiler
l'argument du déplacement en arrière.
Elément d'une structure de compilation pour opérer une rupture de séquence
avec saut en avant. Empile une adresse où >RESOLVE placera l'argument d'un
branchement en avant. Le couple >MARK ... >RESOLVE placé dans deux mots
immédiats de contrôle de compilation est indissociable. Exemple, soit à
ajouter aux structures déjà riches du FORTH une structure de type:
<condition> SAUF <mot1> ... <motn> PUIS ...
qui réalisera une boucle <mot1> ... <motn> si la <condition> est fausse. Sans
faire appel à NOT IF..THEN il est possible d'écrire:
: SAUF COMPILE NOT COMPILE ?BRANCH >MARK ; IMMEDIATE
: PUIS >RESOLVE ; IMMEDIATE
qui pourront être utilisés sous une forme du type:
: PROMENADE ( ---)
IL-PLEUT
SAUF SORTIR PUIS SIESTE ;
Elément d'une structure de compilation pour opérer une rupture de séquence
avec saut en avant. Utilise l'adresse laissée par >MARK pour compiler
l'argument du déplacement en avant.
C. Boucles itératives.
Partie exécution compilée par le mot d'exécution immédiate +LOOP. Incrémente
le compteur de boucle avec la valeur déposée sur la pile de données et décide
s'il faut continuer ou non à boucler.
Mot compilé par le mot d'exécution immédiate ?DO. Dépose sur la pile de
retour l'adresse qui sera éventuellement utilisée par (LOOP) ou (+LOOP). La
différence entre ?DO et DO tient au fait que ?DO ne réalise aucune itération
si l'index initial est égal ou supérieur à l'index final.
Partie exécution compilée par le mot d'exécution immédiate ?LEAVE. Abandonne
la boucle en cours d'exécution si le flag booléen f est vrai. Dans le cas
contraire, l'exécution se poursuit.
Partie exécution compilée par DO. Dépose sur la pile de retour l'adresse qui
sera éventuellement utilisée par (LOOP) ou (+LOOP). Si l'index de boucle
initial est égal ou inférieur à l'index de boucle final, le contenu de la
boucle sera exécuté au moins une fois.
Mot compilé par le mot d'exécution immédiate LEAVE. Réalise une sortie
immédiate lors de l'exécution de la partie de définition contenue dans une
structure DO...LOOP, DO...+LOOP, ?DO...LOOP ou ?DO...+LOOP.
Mot compilé par le mot d'exécution immédiate LOOP. Réalise un branchement
arrière vers le début de la boucle s'il faut exécuter plusieurs itérations.
Le compteur de boucle, situé sur la pile de retour, est incrémenté.
Mot d'exécution immédiate, utilisé en compilation seulement. Il termine une
structure de type DO...+LOOP ou ?DO...+LOOP. Le mot +LOOP compile le mot
(+LOOP) et la valeur de branchement vers laquelle l'exécution doit se
poursuivre pour réaliser l'itération. L'index de boucle est incrémenté avec
la valeur déposée sur la pile de données. La valeur n peut être négative.
Exemple:
: BOUCLE
100 0 DO I . 3 +LOOP ;
L'exécution de BOUCLE affiche tous les nombres compris entre 0 et 100, ceci
de trois en trois.
Mot d'exécution immédiate marquant le début d'une structure de contrôle
itérative de type ?DO..LOOP ou ?DO..+LOOP. Son exécution compile le mot
(?DO). A la différence de DO, le contenu de la boucle n'est pas exécuté dans
le cas où les index de boucle sont identiques (DO..LOOP exécutant la boucle
65535 fois). Exemple:
: BOUCLE ?DO ." #" LOOP ;
4 0 BOUCLE affiche ####
4 4 BOUCLE n'affiche rien
L'utilisation d'index de boucles égaux avec DO plante le système. Un index
de boucle de départ inférieur à l'index de boucle d'extrémité avec ?DO plante
le système (plus exactement, exécute la boucle... à l'envers !!!). Cas type
d'erreur:
1 4 BOUCLE plante le système
Les boucles de type ?DO..LOOP et ?DO..+LOOP peuvent s'imbriquer. Exemple:
: BOUCLES ( ---)
20 10 ?DO I
20 10 ?DO DUP I AT ASCII # EMIT LOOP
DROP LOOP ;
Malheureusement, le standard 83 ne prévoit aucun test d'erreur dans le cas
de structures de contrôle dépareillées, comme ?DO.. IF.. LOOP.. THEN.. pour
exemple.
Mot d'exécution immédiate compilant le mot (?LEAVE).
Mot d'exécution immédiate et utilisé en compilation seulement, pour marquer
le début d'une structure de contrôle définie en tant que boucle du type:
n2 n1 DO ... LOOP forme no 1
n2 n1 DO ... n3 +LOOP forme no 2
Le corps de boucle est exécuté itérativement pour qu'un indice de boucle
présent sur la pile de retour, disponible ave I, J et K, parcoure
l'intervalle de n1 à n2, n2 non compris, par pas de 1 dans la forme no 1, ou
par pas de n3 dans la forme no 2.
L'exécution du mot DO compile le mot (DO) qui gère sur la pile de retour
l'adresse de fin de boucle et les indices de boucle pour LOOP ou +LOOP.
Quelque soient n1 et n2, la boucle est exécutée au moins une fois. Si n1 est
égal à n2, la boucle est exécutée 65536 fois.
La gestion de la pile de retour et des structures de contrôle couplées
autorise l'imbrication de boucles sans chevauchement. Exemple:
: CARRE
5 0 DO CR
5 0 DO ASCII * EMIT
LOOP
LOOP ;
et l'exécution de CARRE affiche 25 étoiles disposées en carré.
Mot d'exécution immédiate. Son exécution permet l'interrution du déroulement
d'une boucle de type DO..LOOP ou DO..+LOOP. Exemple:
VARIABLE TOTAL
: SIGMA ( n0 n9 --- ntotal si nn<>0)
0 TOTAL ! 10 0 DO
?DUP IF TOTAL +!
ELSE ." INTERROMPU" CR LEAVE THEN
LOOP ;
et en exécution:
1 2 3 4 5 6 7 8 9 10 SIGMA TOTAL ? affiche 55
par contre
1 3 7 0 4 6 8 5 2 1 SIGMA TOTAL ? affiche INTERROMPU 11
car la présence du nombre 0 a interrompu la boucle.
Mot d'exécution immédiate. Termine une structure de contrôle répétitive de
type DO..LOOP ou ?DO..LOOP. L'exécution de la boucle peut être interrompue
par LEAVE ou ?LEAVE. Exemple:
: BOUCLE ( ---)
10000 0 DO LOOP ;
L'index de la boucle peut être prélevé lors de l'exécution de la boucle à
l'aide des mots I, J ou K. Plusieurs boucles peuvent s'imbriquer. Exemple:
: TEST ( ---)
20 10 DO
15 5 DO
I J AT 127 EMIT
LOOP
LOOP ;
Déposent sur la pile l'index de boucle. Le mot I délivre celui de la boucle
courante, J celui de la boucle extérieure dans le cas de deux boucles
imbriquées, K celui de la boucle extérieure dans le cas de trois boucles
imbriquées. Exemple:
: BOUCLES ( ---)
10 0 DO
10 0 DO
10 0 DO I J K CR 3 .R 3 .R 3 .R
LOOP
LOOP
LOOP ;
D. Branchements et boucles indéfinies.
Mot d'exécution immédiate marquant le début d'une structure de contrôle et
correspondant à la compilation d'une boucle infinie ou indéfinie. Ce mot
laisse sur la pile une adresse de retour utilisée par le mot de fin de
boucle, ainsi qu'un flag booléeen de contrôle de structure. Trois structures
de boucles sont possibles à partir de BEGIN:
infinie BEGIN ... AGAIN
conditionnelles indéfinies BEGIN ... WHILE ... REPEAT
BEGIN ... UNTIL
Exemple:
: DACTYLO BEGIN KEY DUP EMIT CONTROL C UNTIL ;
Lors de l'exécution de DACTYLO, tous les caractères frappés au clavier sont
envoyés au terminal jusqu'à la frappe de CTRL-C.
Mot d'exécution immédiate. Utilisé dans la définition d'une structure de
contrôle de type BEGIN..WHILE..REPEAT. Exécute la partie de définition située
entre WHILE et REPEAT si le flag booléen qui précède son exécution est vrai,
puis réexécute la partie de définition depuis BEGIN. Dans le cas contraire,
l'exécution se poursuit après le mot REPEAT.
Mot d'exécution immédiate. Marque la fin d'une structure de contrôle de type
BEGIN..WHILE..REPEAT.
Compile un branchement arrière dans une structure de contrôle de type BEGIN..
UNTIL. Le contenu de la structure de contrôle est ré-exécuté si le flag
booléen situé sur la pile avant UNTIL est faux (f=0). Exemple:
: ARRET
BEGIN
... définition ...
KEY 3 = ( action CTRL-C)
UNTIL ;
Mot d'exécution immédiate compilant un branchement inconditionnel arrière
dans une structure de contrôle de type BEGIN..AGAIN. Il n'est pas possible
d'interrompre une boucle de ce type, à moins de provoquer une erreur.
Exemple:
: BOUCLE BEGIN AGAIN ;
Une fois lancé, le mot BOUCLE ne peut être interrompu. Il ne reste qu'à
retirer votre disquette et couper le courant. Voici un moyen d'éviter ce
désagrément:
: BOUCLE
BEGIN KEY? IF EXIT THEN AGAIN ;
Mot d'exécution immédiate. Marque le début d'une structure de contrôle de
type IF..THEN ou IF..ELSE..THEN. Lors de l'exécution, la partie de définition
située entre IF et THEN ou entre IF et ELSE est exécutée si le flag booléen
situé au sommet de la pile de données est vrai (f<>0). Dans le cas contraire,
si le flag booléen est faux (f=0), c'est la partie de définition située entre
ELSE et THEN qui sera exécutée. S'il n'y a pas de ELSE, l'exécution se
poursuit après THEN. Exemple:
: BEAU? ( fl ---)
IF ." Beau temps "
ELSE ." Temps couvert " THEN ;
1 BEAU? affiche Beau temps
0 BEAU? affiche Temps couvert
Mot d'exécution immédiate et utilisé en compilation seulement. Marque une
alternative dans une structure de contrôle du type:
(condition) IF ... ELSE ... THEN ...
A l'exécution, si la condition située sur la pile avant IF est fausse, il y
a rupture de séquence avec saut à la suite de ELSE, puis reprise en séquence
après THEN.
A la compilation, IF a déposé sur la pile une adresse de saut conditionnel
qui est résolue (voir >RESOLVE) par ELSE. En cas de structures IF.. ELSE..
THEN imbriquées, ELSE se rapporte à la dernière adresse non résolue, ce qui
interdit en principe les imbrications chevauchantes pour n'autoriser que des
structures du type:
IF ... IF ...
ELSE ... IF...
IF ... ou ELSE...
ELSE ... THEN ...
THEN ... ELSE ...
THEN ... THEN ...
Exemple:
: TEST CR ." Appuyez sur une touche " KEY
DUP 65 122 BETWEEN
IF CR 3 SPACES ." c'est une lettre "
ELSE DUP 48 57 BETWEEN
IF CR 3 SPACES ." c'est un chiffre "
ELSE CR 3 SPACES ." c'est un caractère spécial "
THEN
THEN DROP ;
Mot d'exécution immédiate utilisé en compilation seulement. Marque la fin
d'une structure de contrôle de type IF..THEN ou IF..ELSE..THEN. Exemple:
10 CONSTANT LIMITE
: TROP? ( n ---)
LIMITE >
IF CR ." Il y en a trop"
ELSE CR ." Mettez-en encore" THEN ;
Mot d'exécution immédiate. Marque le début d'une structure de contrôle de
type CASE.. OF.. ENDOF.. ENDCASE. Dans ce type de structure, la condition
est indiquée implicitement avant le mot OF. Si la condition est vérifiée, la
partie de définition située entre OF et ENDOF est exécutée, puis un
branchement s'exécute vers ENDCASE sans contrôle des conditions suivantes le
cas échéant. Exemple:
: JOUR ( n --- adr long)
7 MOD CASE
0 OF " DIMANCHE" ENDOF
1 OF " LUNDI" ENDOF
....
6 OF " SAMEDI" ENDOF
ENDCASE ;
L'exécution de JOUR teste un paramètre au modulo 7 (calendrier perpétuel)
comme suit:
0 JOUR TYPE affiche DIMANCHE
2 JOUR TYPE affiche MARDI
7 JOUR TYPE affiche DIMANCHE
Mot d'exécution immédiate. Marque le début d'une condition de type OF.. ENDOF
au sein d'une structure de contrôle de type CASE.. OF.. ENDOF.. ENDCASE. La
condition compilée automatiquement par OF est équivalente à:
n OVER = IF ... THEN
où n est la valeur à tester et s'écrit:
n OF ... ENDOF
L'avantage de la structure OF.. ENDOF est d'interrompre l'exécution des tests
suivants si la condition est vérifiée, en provoquant un branchement à
ENDCASE.
Mot d'exécution immédiate. Marque la fin d'une condition de type OF.. ENDOF
au sein d'une structure de contrôle de type CASE.. OF.. ENDOF.. ENDCASE.
Laisse sur la pile un branchement qui sera résolu ultérieurement par ENDCASE.
Mot d'exécution immédiate. Marque la fin d'une structure de contrôle de type
CASE.. OF.. ENDOF.. ENDCASE. Résout tous les branchements conditionnels
laissés sur la pile par les ENDOF successifs.
E. Structures semi-récursives et récursives.
Délivre l'adresse du contenu de la variable #TIMES. Ce contenu indique le
nombre de réexécutions s'étant produites sous le contrôle du mot TIMES.
Réexécute le contenu du flot d'entrée tant que l'utilisateur n'appuye pas
sur une touche du clavier. Exemple:
: POUR-EMBETER ( ---)
." Entrez votre commande FORTH:" QUERY MANY ;
et à l'exécution:
Entrez votre commande FORTH: BELL EMIT et appui sur <Return>
Le terminal sifflera tant que vous n'appuyerez pas sur une touche du clavier.
Rend le contenu d'une définition récursif. Ce mot est à utiliser à bon
escient. Une récursivit, mal contrôlée risque de saturer la pile de données
ou la pile de retour, ce qui vous obligerait à réinitialiser votre système.
Exemple:
: TITRE ( ---)
DARK ." MENU GENERAL"
KEY 32 = IF RECURSE ELSE EXIT THEN ;
L'exécution de RECURSE compile TITRE dans la séquence IF.. ELSE.. THEN citée
en exemple. Attention, si vous exécutez un trop grand nombre de fois une
définition récursive, vous risquez de surcharger la pile de retour.
Mot d'exécution immédiate. Autorise une auto-référence à la définition
courante.
Ré-exécute le contenu du flot d'entrée un certain nombre de fois. Le nombre
de répétitions déjà effectuées est controlé par le contenu de la variable
#TIMES.
-- hautdepage -- sommaire
-- page d'accueil --