BOUCLES ET BRANCHEMENTS

A. Primitives de branchement.

?BRANCH f ---
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.

?CONDITION f ---
Exécute un test d'erreur de parité lors de la définition des éléments des structures de contrôle.

BRANCH ---
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.

?<MARK --- f adr
Dépose un flag vrai et exécute <MARK.

?<RESOLVE f adr ---
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.

?>MARK --- f
Dépose un flag vrai et exécute >MARK.

?>RESOLVE f ---
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 ?

<MARK --- adr F83
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 ;
<RESOLVE adr --- F83
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.

>MARK --- adr
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 ;
>RESOLVE adr ---
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.

(+LOOP) n ---
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.

(?DO) n1 n2 ---
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.

(?LEAVE) f ---
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.

(DO) n1 n2 ---
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.

(LEAVE) ---
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.

(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é.

+LOOP n --- F83
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.

?DO --- imm
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.

?LEAVE --- imm
Mot d'exécution immédiate compilant le mot (?LEAVE).

DO --- imm F83
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é.

LEAVE --- imm
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.
LOOP --- imm F83
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 ;
I J K --- index
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.

BEGIN --- adr f imm F83
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.

WHILE --- imm F83
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.

REPEAT --- Imm
Mot d'exécution immédiate. Marque la fin d'une structure de contrôle de type BEGIN..WHILE..REPEAT.

UNTIL f --- imm F83
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 ;
AGAIN --- imm
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 ;
IF --- imm F83
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
ELSE --- imm F83
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 ;
THEN --- imm F83
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 ;
CASE ---
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
OF imm
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.

ENDOF imm
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.

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.

#TIMES --- adr
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.

MANY ---
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.

RECURSE --- imm
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.

RECURSIVE --- imm
Mot d'exécution immédiate. Autorise une auto-référence à la définition courante.

TIMES n ---
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 --