Acceuil
Faqs
FORTH
CV
Liens
E-mail me
Acceuil info

Voici une presentation succinte d'un langage un peu particulier: FORTH



Forth a été conçu vers la fin des années 60 par Charles Moore, alors qu'il travaillait à l'observatoire américain de radioastronomie. Son but était de créer un langage de commande de processus, notamment pour la commande des télescopes, mais plutôt que de s'adresser à des informaticiens, qui en tant qu'intermédiaires ignorant en astronomie ne faisaient qu'introduire des contraintes stériles et agaçantes, il préféra s'y atteler lui-même. Il inventa donc un concept d'une simplicité et d'une efficacité étonnante. Moore fut tellement satisfait de son œuvre qu'il voulut l'appeler langage de quatrième génération (FOURTH). Mais, la machine sur laquelle il avait développé son langage, un IBM 1130, n'acceptait que des noms de programme de cinq lettres, c'est pourquoi il choisit FORTH.
A l'image de son génial inventeur, FORTH est un langage atypique, plus proche des métalangages, que des super charabia dont la pensée unique informatique inonde le monde. Sans chercher à avoir une approche exhaustive de tous les aspects de ce langage, je m'attacherai à en présenter les aspects et concepts fondamentaux, puis je terminerai en présentant un outil que peu de langage propose : le méta-compilateur du langage.

I_ Concepts généraux

Je ne peux m'en délivrer qu'en allant jusqu'au bout de cette histoire. Mais que trouverai-je en fin de parcours ? Vous n'êtes pas de celles qui la laissent ouverte, en vue d'en faire un conte infini. Votre histoire est une suite de portes qui s'ouvrent sur des territoires blancs et des labyrinthes qui tournent …

Tahar Ben Jelloun, La nuit sacrée

Comme tout langage de programmation, FORTH a ses adeptes et ses détracteurs. Toutefois, aux dires de mes sources, il est l'un des langages informatiques les plus performants. Les concepts qu'il manipule allient simplicité, efficacité et complexité ! Contrairement aux langages classiques, FORTH n'est pas limité par une liste finie d'instruction et de structure de données. Le programmeur FORTH a la possibilité de modeler ce dernier à sa convenance et de le spécialiser en fonction de l'application qu'il développe. Pour le forthien, les notions de procédures et de fonctions n'existent pas. De même, la notion de typage est quasi absente. Les instructions du langage s'appellent des mots. A l'instar des langues naturelles, FORTH organise les mots qui le composent en vocabulaire (ensemble de mots ayant un trait commun). Ce vocabulaire peu être étendu, c'est à dire qu'on peut lui apprendre de nouveaux mots, en les définissant à partir de mots existant. C'est d'ailleurs ainsi que l'on programme en FORTH, en enrichissant un vocabulaire existant, ou en en définissant un nouveau.

A_ L'élément centrale de tout système FORTH : la pile

FORTH est un langage structuré (pas de GOTO), basé sur l'utilisation d'une structure de donnée bien connue, la pile LIFO, sa syntaxe utilise la notation polonaise inversée. Il dispose d'un jeu d'instruction sur ses piles, et des noms d'opérations pour en créer de nouveaux.

Le langage est construit autour de 2 piles :

La pile de donnée (data stack)
La pile de retour (return stack)

L'utilisation de la notation polonaise inversée se fait de la façon suivante : les opérandes sont empilés au fur t à mesure de leur rencontre, les opérateurs sont exécutés sur les derniers opérandes empilés, qui sont dépilés et on empile le résultat.

Ex : pour calculer 2+(3*5),
2 3 5 * +
ou, 3 5 * 2 +

C'est là l'une des difficultés du langage : acquérir de nouveaux réflexes. Comme la pile est l'élément central de FORTH, il existe des mots pour modifier son contenu, dont voici quelques exemples :

DUP (x à xx) : duplique l'élément de sommet de pile, et l'empile Ex : pour calculer le carré d'un nombre on peut procéder ainsi,
2 DUP * DROP (x à g) : enlève l'élément de sommet de pile.
SWAP (x1x2 à x2x1) : échange les 2 éléments de sommet de pile.
etc…

D'autres mots sont plus " tordus " :

TUCK (x1x2 à x1x2x1) : copie l'élément de sommet de pile sous les 2 premiers.
PICK (x1x2 … xn à x1x2 … xnx1) : empile l'élément de rang de la pile.

B_ Les variables

Notion capitale en informatique, les variables sont également utilisées en FORTH, mais beaucoup moins que dans d'autres langages, du fait de l'utilisation intensive de la pile pour passer des données d'une partie du programme à une autre (d'un mot à l'autre en FORTH). En fait, on les utilise lorsque l'on a une gestion de la pile qui se complique trop à vouloir les éviter. Naturellement, cela se fait au détriment de la lisibilité du code produit.

Les variables doivent être déclarées, ainsi pour créer une variable V, on écrira : VARIABLE V, et V sera créée avec comme valeur par défaut 0. Pour intervenir sur la valeur d'une variable, le programmeur FORTH dispose de mots spécifiques : ! (affectation), @ (affichage).

Ex : VARIABLE note
note @. à afiche 0
18 note !.
note @. à afiche 18
Remarque:
Il est interressant de noter que si on tape, note.
FORTH affichera un nombre (ex: 15666) qui est l'adresse de la variable en mémoire.

Il existe aussi des pseudo variables appelées constantes, que l'on déclare à l'aide du mot CONSTANT, et l'utilisateur a également la possibilitée de déclarer des variables alpha-nulmériques, avec le mot STRING.
Le système FORTH utilise également des variables pour son fonctionnement. Par exemple il existe une variable prédéfinie BASE à laquelle il suffit d'affecter la valeur de la base numérique dans laquelle on veu travailler (par défaut le système est en base 10). Naturellement, il existe des mots qui permettent de changer de base numérique (OCTAL, HEX, …).

C_ Structure de contrôle et récursivitée

En programmation, toute séquence d'instruction peut-être rompue en exécutant un branchement ou une boucle. Comme tout langage informatique qui se respecte, FORTH dispose de toute une panoplie de ces branchements:

Si … alors … sinon
Répéter … jusqu'à
Pour i de 1 à n
Selon

Pour exemple prenons la structure IF … ELSE … THEN :

Le mot IF est un mot d'exécution immédiate, et compile un branchement conditionnel. Il n'est utilisable qu'en compilation.
Si le résultat du test effectué avant IF délivre un flag booléen faux, c'est la partie de code située entre ESLE et THEN qui sera exécutée, sinon se sera celle entre IF et ELSE. Un flag booléen peut être le résultat de divers opérations: résultat d'un calcul arithmétique, d'une poération logique, de la lecture d'une adresse mémoire …
Le mot IF test et consomme la valeur située au sommet de la pile de données, toutes valeur non nulle sera considérée comme vrai.

Les puristes, dont certaine de mes sources, affirment qu'un langage informatique est incomplet s'il ne peut traiter la récursivité, pour d'autres on peut s'en passer. FORTH va enthousiasmer les premiers, car il est équipé pour traiter ce type de situation.
Come il n'est pas possible de faire appel à un mot en cours de définition, on insère le mot RECURSE partout où on veut faire un appel récurssif.
Attention touefois, la récursivitée à ses limites : la capacité des piles de données et de retour. Une récursivité mal contrôlée sature les piles et bloque le système.

II_ Vocabulaire

Comme nous l'avons déjà vu, FORTH utilise un vocabulaire que l'on peut étendre en définissant de nouveaux mots. Je dvrais plutôt dire des vocabulaires, car il y en a plusieurs:

Le vocabulaire courrant, créé par l'utilisateur ( il est perdu s'il n'est pas sauvegarder)
Des vocabulaires créés et sauvegardés par l'utilisateur
Les vocabulaires du système FORTH
Toute nouvelle définition d'un mot se fait à partir des mots déjà présents, il est bien entendu possible de redéfinir un mot existant; Lorsque FORTH cherche un mot, il se réfèrera toujours à la définition la plus récente du vocabulaire courant. Il existe des mots pour manipuler les vocabulaires.

Ex: FORGET: efface les mots défini par l'utilisateur
HERE: renvoie l'adresse du premier emplacement libre du dictionnaire. C'est là que l'on commence vraiment à jouer avec FORTH car nous pouvons le faire se modifier lui - même.
FENCE: prmet de protéger une partie du dictionnaire

Lorsque que la définition d'un mot fait référence à un mot pré-existant du dictionnaire, l'adresse correspondante est exprimée en adressage absolu au sein de la nouvelle définition. Comme nous l'avons déjà vu, l'activitée de programmation consiste essentiellement à définir de nouveau mots.

Ex:
On souhaite ecrire un programme qui calcul la masse d'un sac de bille. En supposant que toutes les billes aient la même masse, il suffit de calculer la masse d'une bille, le nombre de billes et enfin de calculer la masse du sac.
En FORTH, on aurait quelque chose comme cela,

:SAC_BILLE
MASSE_1_BILLE
NOMBRE_BILLE
MASSE_SAC;
Si on soumet se programme au compilateur, il va répondre:

POIDS_BILLE inconnu Pour faire accepter le programme, il faut définir les mots qui le compose. Toutefois, rien n'interdit d'entrer une définition provisoire, et pourquoi pas celle du mot vide?

: MASSE_1_BILLE;
: NOMBRE_BILLE;
: MASSE_SAC;
: SAC_BILLE
MASSE_1_BILLE
NOMBRE_BILLE
MASSE_SAC;

Cette fois, le compilateur accepte le programme et réponds OK, en pur texan! Ce programme ne fait toujours rien, mais il le fait de façon structurée.

Un mot FORTH est constituer de plusieurs parties accolées les unes aux autres, et dont l'organisation est la suivante:

Un VFA(View Field Adress), adresse du champs de vue, qui est utilisée pour l'auto-documentation(commande HELP)

Un LFA(Link Field Adress),adresse du champs de lien, qui pointe vers le définition du mot précédent du dictionnaire

Un NFA(Name Field Adress), adresse du champs de nom, qui contient le nom du mot

Un CFA(Code Field Adress), adresse du champs de code, qui contient le code executable du mot

Un PFA(Parameters Field Adres), adresse du champs de paramètres, contient les adresses des mots qui constituent la définition du mot

III_ Application industrielle

Nous avons vu que pour définir un nouveau mot, FORTH utilise des mots existant. En décomposant de plus en plus loin, on finit par tomber sur les primitives du langage. Toutes les primitives sont rassemblées dans le noyau, qui est le programme de base, de toute petite taille.

Exemples de mots du noyau: @, !, |, 2!, …
Un bon noyau contient seulement quelque dizaines de primitives, qui servent à effectuer des opérations arithmétiques, aller checher ou stocker des valeurs en mémoire, ouvrir ou fermer des fichiers, … Bref, tout ce que sait faire un ordinateur sans problème. Un noyau déjà respectable a une taille de 20 Ko, il existe même des noyau de 1 à 2 Ko, soit le contenu d'une page imprimée… Cette petite taille du noyau faisait de FORTH un outil idéal pour la programmation des mini et des micro ordinateurs.
De nos jour, FORTH est toujours utilisé. Par exemple il est utiliser comme boot language sur les systèmes SPARC, PowerPC et intel x86. Autre application, indirecte cette fois, le langage postcript est un descendant directe du FORTH. De même, celui des calculatrice HP en est un dérivé.

Loin de ces mastodontes de l'informatique, un passionné s'est "bricolé" son propre micro-ordinateur, à partir d'un processeur 68030. JPB a ainsi réussi à crée un système de gestion de fichier plus performant que windows, inspiré d'UNIX. Pour ce faire, plutôt que d'utiliser l'assembleur, ce qui peut être fastidieux, il utilise FORTH car bien qu'il soit puissant, ses faibles besoins en mémoire (compilateur + interprtteur = 16 Ko) l'ont décidé. Pour lui, FORTH est l'un des langages évolués les plus agréable pour travailler à bas niveau. Ses instructions de base se révèlent parfaitement adaptées à une implémenntation matérielle au sein d'un micro-processeur.
Je ne peut me permettre d'aller plus avant sur ce sujet, mais j'incite le lecteur interressé à visiter le site internet de ce monsieur JPB, dont vous trouverez l'adresse plus loin, et à lire l'article le concernant dans le dernier Virus mag' (n° 18).

IV_ Interprétation / Compilation

Les langages compilés ont le défaut que l'on ne peut pas les exécuter directement et il faut recompiler le programme à chaque modification; par contre cela fait ils sont d'une très grande rapiditée d'exécution. Les langages interprétés sont eux exécutables directement, mais sont en contre partie beaucoup plus lents à l'exécution. Le langage FORTH est, là encore, un langage atypique car il intègre à la fois un interpréteur, un compilateur et un assembleur. La version FORTH83 dispose même de trois interpréteurs, dont deux sont écris en FORT§H et le dernier en assembleur.
Le compilateur travaille en une seule passe et sans gérer de table de référence. Son rôle est d'accroitre les fonctions disponibles dans le dictionnaire, de rajouter des mots à un vocabulaire existant ou a en créer un nouveau. Tout mot compilé dans le dictionnaire peut-être intégré à une nouvelle définition.
Il y a deux façon de compiler du code: à partir d'un fichier "classique", ou bien en mode interprété, mettant le code à compiler entre les mots : et ;.
Le compilateur travaille avec deux dictionnaires, un pour les noms et un autre pour le code. Le premier est souvent une liste chainée ou une table de hachage, ou tout autre mécanisme permettant une recherche dans une liste de nom. Le compilateur, ou l'interpréteur cherchent dans cette liste le nom auquel est associé le code exécutable voulu. Ces deux dictionnaire peuvent être mélangés ou séparé suivant les systèmes.
Comme nous l'avons déjà vu précédement, certain mots permettent de redéfinir les mots de base du langage. En fait cela va même encore plus loin, car FORTH dispose d'un outils permettant de redéfinir un système FORTH complet: son méta-compilateur.

V_ La méta-compilation

Les signes ne sont pas des preuves, puisque n'importe qui peut en produir des faux ou d'ambigus. de là à se rabattre, paradoxalement, sur la toute puissance du langage: puisque rien n'assure le langage, je tiendrai le langage pour la seule et dernière assurance: je ne croirai plus à l'interprétation.

Roland Barthes, ragments d'un discours amoureux

A_ Un autre exemple: CAML

La méta-compilation est sans doute le summum de la complexité, mais aussi d'intelligence en matière de programmation. Son but est de redéfinir à partir d'un système donné, l'intégralité d'un nouveau système. Peu de langages évolués sont suffisamment puissants et maniables pour disposer de ce concept.
Le seul autre exemple que j'ai pu trouver est celui du langage CAML. En effet, dans ce langage on parle d'auto-génération. Ce mécanisme est même général dans les systèmes CAML : ils sont tous auto-gènes, c'est à dire produit par eux-mêmes. Le compilateur est entièrement écrit en CAML, c'est donc un programme CAMLcommo les autres, compilable par le compilateur CAML, c'est à dire par lui-même.
L'auto-génération, ou bootstrap en anglais, est un mécanisme étrange, puisqu'il s'agit d'une sorte de " définition récursive du compilateur du langage ". Par quel miracle peut-il en sortir un système CAML qui tourne ? Et bien la récurrence s'arrête sur un compilateur de niveau 0, le compilateur initial, écrit dans un autre langage. Progressivement, certaines parties du compilateur initial sont réécrites dans le langage compilable par le compilateur, jusqu'à obtenir un compilateur entièrement écrit dans le langage du compilateur. La comparaison peut paraître osée, toutefois l'idée est la même en FORTH.

B_ Méta-compilation d'un système FORTH

Tout système FORTH, sauf peut-être les versions des toutes premières origines du langage, est lui-même conçu et écrit en FORTH. L'outils d'écriture d'un système FORTH est son méta compilateur, et il est d'usage de fournir à l'utilisateur les sources du méta-compilateur, aussi bien que les sources du langage lui-même méta-compilé. Pour l'utilisateur, disposer du méta-compilateur et des sources du langage signifie qu'il possède un outil de programmation extrêmement puissant, l'autorisant à redéfinir, selon ses propres besoins, l'outil même dont il se sert. Certains méta compilateurs ont étés écrit pour permettre de passer du système FORTH d'un processeur A à un processeur B.
Historiquement, TURBO-FORTH a été méta compilé en FORTH83 par Marc Petremann1, à l'aide du méta-compilateur " Phénix " du système FORTH83 (créé par Laxen et Perry). Au fils des générations, méta-compilateur et source méta-compilés ont été modifiés jusqu'au système actuel qui assure sa propre auto-génération intégrale, et rien n'empêche le forthien de continuer cette boucle étrange… Toutefois, toute entreprise de méta-compilation requiert selon moi de solides connaissances du langage, un QI confortable et beaucoup de rigueur et de patience pour appréhender le plus complexe de tous les concepts de FORTH.
Nous l'avons vu, l'auto-génération de CAML s'arrête sur un compilateur de niveau 0. Ici, ce n'est pas le cas, mais on reste assez proche de l'idée car la " récurrence " s'arrête sur un noyau (CORE) contenant les primitives minimales du langage pour le bon fonctionnement de tout système FORTH (selon la norme ANSI). Il faut savoir qu'à partir du moment ou un système supporte ce CORE, il peut compiler du code.
Attention, il ne faut pas confondre compilation et méta-compilation. Là où la compilation ne fait qu'étendre le dictionnaire d'un système, la méta-compilation créée un système cible entièrement nouveau, complet et autonome par rapport au système hôte. La cible n'est en aucun cas un sous-système du système hôte.

Conclusion

FORTH est un langage inoxydable, indémodable, du moins tant qu'il faudra faire passer des particules dans des portes logiques pour simuler une machine de Turing. Non que FORTH soit le seul langage qui possède cette aptitude, c'est simplement qu'il est relativement accessible aux non-informaticiens, s'ils sont disposés à apprendre comment fonctionne un ordinateur. FORTH est d'une efficacité qui humilie les applications ordinaires, ne coûte pratiquement rien, fonctionne à la perfection, ne consomme presque rien en mémoire et en disque, tourne sur à peu prés n'importe quel ordinateur et bénéficie du support quasi bénévole de quelques mordus. Et le fameux revers de la médaille ?
Il tient principalement dans les quelques dizaines heures à passer pour acquérir de nouveaux réflexes, qui plus est bien utiles dans d'autres domaine. De plus, l'utilisation de symboles de ponctuation comme instruction du langage peut rendre les codes sources illisibles, mais des commentaires et un peu de rigueur rendent le tout relativement digeste, plus en tout cas que certain script PERL.
Pour ma part, le concept de méta-compilation m'intrigue au plus haut point, et je continu à travailler sur les documents en anglais que j'ai réussi à trouver sur internet. Et pour finir, une maxime bien adaptée au FORTH :
small is beautiful ...

Voici un exemple de programme FORTH utilisant au maximum la pile pour le passage des données. Ce programme calculera la somme des éléments impairs d'une liste d'entiers. On supposera que celle-ci, ainsi que le nombre d'élément de la pile se trouve en sommet de pile au moment de l'appel de fonction.

:SOMIMP
0 SWAP 0
DO
SWAP DUP 2 MOD
IF +
ELSE DROP
THEN
LOOP
;

Si on étudie ce programme, on se rend contre qu'il est constituer de trois partie distinct : Soit L la liste sur laquelle on travaille et n son nombre d'élément.

:SOMIMP

/* == Première étape
On empile 0, puis on échange (swap) les deux éléments du sommet de pile et on empile à nouveau 0. Si on se souviens que lors de l'appel au programme le sommet de pile doit être : L n , on se retrouve après cela avec : L 0 n 0 == */

0 SWAP 0

/* == Deuxième étape
On entre maintenant dans une boucle du type pour i de 1 à n, en effet le mot DO dépiler les deux éléments de sommet de pile i et j (ici n et 0) et exécute la boucle i-j fois (ici n-0 fois, c'est à dire autant de fois qu'il y as d'élément dans la boucle. Invariant de l'itération : l'élément du sommet de pile est la somme S des éléments impaires de la liste, pour l'instant c'est 0 (le premier 0 que l'on a empilé) == */

DO

/* == Troisième étape
La pile se trouve dans cet état : L' S (L' : liste des élément de L non encore traités, S : somme des élément impaire de la partie déjà traitée de la liste)
Si e est le premier élément de L', la première ligne met la pile dans cet état : L'' S e e 2
Puis, MOD dépile les deux éléments de sommet de pile, calcule leur modulo m et empile le résultat : L'' S e m
Enfin le mot IF est exécuté : il dépile l'élément de sommet de pile, si celui-ci est différent de 0 (donc e était impaire),c'est le code entre IF et ELSE qui est exécuté : L'' S+e
Sinon, e était pair, on exécute le code entre ELSE et THEN : L'' S
Dans tous les cas l'invariant de l'itération est respecté. == */

SWAP DUP 2
MOD
IF +
ELSE DROP
THEN

LOOP
;

Exemple d'appel : 11 12 0 1 23 7 8 SOMIMP. Le résultat sera : 42.