Introduction au langage Forth

par Éric Marsden emarsden@mail.dotcom.fr


Pour gagner en connaissances, ajouter des éléments chaque jour.
Pour gagner en sagesse, retirer des éléments chaque jour.
le philosophe Chinois Lao Tzu

Introduction

Forth est à la fois un langage extensible et un environnement de développement interactif, surtout utilisé dans des domaines scientifiques et techniques tels l'instrumentation, la robotique et les systèmes embarqués. C'est un langage intéressant par sa simplicité (qui fait qu'un noyau peut tenir dans 4 Ko) ainsi que par sa souplesse et sa puissance.

À un niveau superficiel, Forth est un langage exécutable pour machine virtuelle à base de pile. L'évaluation sur la pile utilise la notation Polonaise postfixée, bien connue des utilisateurs de calculettes HP ; par exemple, la phrase suivante entrée à la ligne de commande

2 3 + 4 * .  20

place les nombres 2 puis 3 sur la pile, les dépile pour prendre leur somme, empile le résultat, empile le nombre 4 et finalement dépile le 4 puis le 5 pour empiler leur produit. Le point . affiche à l'écran l'élément en sommet de la pile (les réponses de l'interprète sont matérialisées en caractères gras). La pile est beaucoup utilisée en Forth, et il existe un grand nombre d'opérateurs pour la manipuler dont

 dup ( n -- n n )       \ dupliquer le sommet de pile
 drop ( n -- )          \ supprimer le sommet de pile
 swap ( a b -- b a )    \ échanger
 rot ( a b c -- b c a ) \ rotation
 nip ( a b -- b ) 

Les commentaires entre parenthèses indiquent l'effet qu'a l'opérateur (les Fortheurs parlent plutôt de mot) sur la pile, qui par convention croît vers la droite. Ces mots sont dits primitifs ; écrire un programme en Forth c'est définir de nouveaux mots, afin de construire un vocabulaire qui permettra de résoudre un problème.

Voici la définition d'un mot qui élève au carré l'entier en sommet de pile :

 : auCarré ( n -- n*n ) dup * ;

Le : crée une définition pour auCarré dans le dictionnaire. Les adresses des mots suivants (jusqu'au ; qui termine la définition) sont ajoutés au dictionnaire. Aussitôt définit, le mot peut être testé :

 5 auCarré . 25

Cette factorisation en mots réutilisables fait que le Forth produit un code très compact et rend le programmeur très productif. Les mots les plus utilisés peuvent être réécrits en assembleur, pour maximiser la vitesse d'exécution.

Forth permet bien entendu des tests conditionnels et des boucles. Par exemple, pour calculer itérativement le n-ième nombre de Fibonnaci :

   : fib    ( n -- nfib )
            dup 2 < IF 
               drop 1
            ELSE 
               1 1 rot 1 DO tuck + LOOP nip
            ENDIF 
   ;

Le test n 2 < met sur la pile un booléen, qui est lu (et dépilé) par le IF. Lorsque n est supérieur à 2 on effectue une boucle de 1 jusqu'à n, calculant le nouveau nombre de Fibonnaci en sommant les deux précédents (les deux premiers nombres de la série, 1 et 1, ayant été empilés avant la boucle). La primitive \texttt{tuck}, qui a l'effet ( a b - - b a b ), est équivalente à la phrase dup rot swap

.

On peut également utiliser la récursivité. Ainsi l'algorithme d'Euclid pour trouver le plus grand diviseur commun de deux entiers strictement positifs s'écrit :

 : pgcd  ( a b -- pgcd ) 
         dup 0<> IF tuck mod recurse ELSE drop ENDIF 
 ; 
 784 48 pgcd . 16

mod est l'opération modulo et recurse enclenche l'appel récursif. La récursion se termine lorsque b est nul, en laissant a sur la pile.

On peut aussi définir des constantes et des variables (même si elles sont moins employées que dans d'autres langages : on préfère un style de programmation fonctionnel avec communication par la pile à l'utilisation d'effets de bord) :

 666 constant foo  \ définit une constante
 variable bar      \ définit une variable
 foo 10 mod . 6    \ utilisation d'une constante
 123 bar !         \ affecte 123 à la variable bar
 bar @ . 123       \ récupère le contenu de la variable bar

La syntaxe de Forth est tellement simple qu'elle nécessite ni analyse syntaxique, ni analyse lexicale. En fait un système Forth a deux états possibles. L'état par défault est le mode interactif, qui correspond à la boucle suivante :

Le deuxième mode de fonctionnement du système est le mode compilation, qui permet d'ajouter de nouvelles définitions au dictionnaire. On a déjà vu le mot spécial :, qui permet de rentrer en mode compilation. Ainsi la phrase : auCarre dup * ; crée une nouvelle entrée auCarre dans le dictionnaire et y met les adresses des mots dup et *. Le ; permet de revenir en mode interactif.

CREATE .. DOES>

La vraie puissance de Forth vient du couple CREATE et DOES>, qui permet de définir de nouveaux types de mots qui auront un comportement différent à la compilation et à l'exécution. Le mot spécial variable vu précédemment pourrait être défini par

   : constant CREATE , DOES> @ ;

Quand on écrit 5 constant five le compilateur crée un nouveau mot five, et execute les instructions suivant le CREATE (ici le mot , alloue une cellule mémoire à la position courante dans le dictionnaire et y place la valeur 5 qui est en sommet de pile). Quand ensuite on écrira five, l'interprète empilera l'adresse où il a précedemment alloué une cellule, et exécutera les instructions suivant le DOES> (ici il cherche le contenu de de la cellule qu'on a allouée, et trouve la valeur 5).

De la même manière, on peut définir des tableaux :

: def-tableau CREATE ( taille -- )
              cells allot  \ alloue le tableau
              DOES> ( indice -- addr )
              cells + \ calcule l'offset dans le tableau
;
100 def-tableau tab \ crée un tableau de 100 céllules
5 tab 25 !          \ met 25 dans le 5eme élément de tab
66 tab @            \ récupère le 66eme élément de tab

Cet approche est très souple : on aurait par exemple pu initialiser le tableau dans la partie CREATE, et effectuer des tests de validité d'indice dans la partie DOES>. Ces mots permettent la définition de mots servant à la programmation orientée objet en Forth, concepte qui n'existait pas au moment de la conception du langage : on voit l'interêt que présente l'extensibilité.

Histoire

Forth a été inventé par le programmeur Charles Moore à la fin des années 1960, lorsqu'il travaillait dans un observatoire en Arizona. Son système dirigeait le télescope, contrôlait l'acquisition des données et leur enregistrement sur bande magnétique, et gérait un terminal graphique permettant à l'astronome d'analyser les données. Le système était multitâche, notion très avancée pour l'époque. Pour la petite histoire, le nom Forth était censé signifier langage de quatrième génération.

Forth s'est répandu rapidement dans la communauté scientifique concernée par la gestion de processus en temps réel, et a été utilisé dans plusieurs satellites. Le système était populaire sur les premiers micro-ordinateurs, où il tenait souvent office de système d'exploitation. On trouve aujourd'hui des systèmes Forth pour processeurs allant du microcontrôleur 8 bits au Cray, et il existe même des puces implantant la machine virtuelle de Forth. Sous Linux on peut conseiller PFE et GForth, le Forth du projet GNU. Ces systèmes (sous GPL) peuvent accéder à la libc et on peut leur ajouter des primitives en C.

Le Forth a servi de modèle au Postscript (qui comporte lui aussi une pile et un dictionnaire), et le concept de machine virtuelle a été repris dans de nombreux langages tels Java, Emacs Lisp et Caml. Le Forth a été quelque peu éclipsé par la montée en puissance du C, mais son code compact et son interactivité font qu'il résiste bien dans le secteur des systèmes embarquées, ainsi que pour les applications ROMables (cf les BIOS de Sun et de Apple).

Conclusion

Comme le laisse entendre la citation en début d'article, le Forth privilégie la simplicité et l'économie d'expression. Le programmeur peut comprendre le fonctionnement de chaque partie du système, et peut en modifier le comportement pour tel ou telle application. Cela permet au programmeur Forth d'aborder des problèmes complexes de manière plus naturelle qu'avec un langage impératif classique.

Bibliographie