La structure de données de base de Forth est le « dictionnaire » qui mappe les « mots » au code exécutable ou aux structures de données nommées. Le dictionnaire est présenté en mémoire sous la forme d’un arbre de listes chaînées avec les liens allant du mot défini le plus récent (le plus récent) au plus ancien, jusqu’à ce qu’une valeur sentinelle, généralement un pointeur NUL, soit trouvée. Un changement de contexte provoque le démarrage d’une recherche de liste sur une feuille différente. Une recherche par liste chaînée se poursuit lorsque la branche fusionne dans le tronc principal menant finalement à la sentinelle, la racine.Il peut y avoir plusieurs dictionnaires. Dans de rares cas tels que la méta-compilation, un dictionnaire peut être isolé et autonome.L’effet ressemble à celui des espaces de noms d’imbrication et peut surcharger les mots-clés en fonction du contexte.
Un mot défini se compose généralement d’en-tête et de corps, l’en-tête étant constitué du champ nom (NF) et du champ lien (LF), et le corps étant constitué du champ code (CF) et du champ paramètre (PF).
La tête et le corps d’une entrée de dictionnaire sont traités séparément car ils peuvent ne pas être contigus. Par exemple, lorsqu’un quatrième programme est recompilé pour une nouvelle plate-forme, la tête peut rester sur l’ordinateur de compilation, tandis que le corps se rend sur la nouvelle plate-forme. Dans certains environnements (tels que les systèmes embarqués), les têtes occupent inutilement de la mémoire. Cependant, certains compilateurs croisés peuvent mettre des têtes dans la cible si la cible elle-même est censée prendre en charge un Forth interactif.
Entrée de dictionnaire
Le format exact d’une entrée de dictionnaire n’est pas prescrit et les implémentations varient. Cependant, certains composants sont presque toujours présents, bien que la taille et l’ordre exacts puissent varier. Décrite comme une structure, une entrée de dictionnaire peut ressembler à ceci:
structure byte: flag \ 3bit flags + length of word's name char-array: name \ name's runtime length isn't known at compile time address: previous \ link field, backward ptr to previous word address: codeword \ ptr to the code to execute this word any-array: parameterfield \ unknown length of data, words, or opcodes end-structure forthword
Le champ name commence par un préfixe donnant la longueur du nom du mot (généralement jusqu’à 32 octets), et plusieurs bits pour les drapeaux. La représentation des caractères du nom du mot suit alors le préfixe. Selon l’implémentation particulière de Forth, il peut y avoir un ou plusieurs octets NUL(‘\0’) pour l’alignement.
Le champ lien contient un pointeur vers le mot précédemment défini. Le pointeur peut être un déplacement relatif ou une adresse absolue qui pointe vers le frère le plus âgé suivant.
Le pointeur de champ de code sera soit l’adresse du mot qui exécutera le code ou les données dans le champ de paramètre, soit le début du code machine que le processeur exécutera directement. Pour les mots définis par deux-points, le pointeur de champ de code pointe vers le mot qui enregistrera le pointeur d’instruction Quatrième actuel (IP) sur la pile de retour et chargera l’IP avec la nouvelle adresse à partir de laquelle poursuivre l’exécution des mots. C’est la même chose que ce que font les instructions d’appel / retour d’un processeur.
Structure du compilerEdit
Le compilateur lui-même n’est pas un programme monolithique. Il se compose de quatre mots visibles par le système et utilisables par un programmeur. Cela permet à un programmeur de modifier les mots du compilateur à des fins spéciales.
L’indicateur « temps de compilation » dans le champ nom est défini pour les mots ayant un comportement « temps de compilation ». La plupart des mots simples exécutent le même code, qu’ils soient tapés sur une ligne de commande ou intégrés dans du code. Lors de la compilation de ceux-ci, le compilateur place simplement du code ou un pointeur fileté sur le mot.
Les exemples classiques de mots de compilation sont les structures de contrôle telles que IF
et WHILE
. Presque toutes les structures de contrôle de Forth et presque tous ses compilateurs sont implémentés en tant que mots de compilation. Mis à part certains mots de flux de contrôle rarement utilisés que l’on ne trouve que dans quelques implémentations, comme un retour conditionnel, tous les mots de flux de contrôle de Forth sont exécutés pendant la compilation pour compiler diverses combinaisons de mots primitifs avec leurs adresses de branche. Par exemple, IF
et WHILE
, et les mots qui correspondent à ceux-ci, configurent BRANCH
(branche inconditionnelle) et ?BRANCH
(efface une valeur de la pile et branche si elle est fausse). Les mots de flux de contrôle de boucle comptés fonctionnent de la même manière, mais configurent des combinaisons de mots primitifs qui fonctionnent avec un compteur, etc. Lors de la compilation, la pile de données est utilisée pour prendre en charge l’équilibrage de la structure de contrôle, l’imbrication et le rétro-correctif des adresses de branche. L’extrait de code :
... DUP 6 < IF DROP 5 ELSE 1 - THEN ...
serait compilé dans la séquence suivante à l’intérieur d’une définition :
... DUP LIT 6 < ?BRANCH 5 DROP LIT 5 BRANCH 3 LIT 1 - ...
Les nombres après BRANCH
représentent les adresses de saut relatives. LIT
est le mot primitif pour pousser un nombre « littéral » sur la pile de données.
État de compilation et d’interprétation stateEdit
Le mot :
(deux-points) analyse un nom en tant que paramètre, crée une entrée de dictionnaire (une définition de deux-points) et entre dans l’état de compilation. L’interpréteur continue de lire des mots délimités par des espaces à partir du dispositif d’entrée utilisateur. Si un mot est trouvé, l’interpréteur exécute la sémantique de compilation associée au mot, au lieu de la sémantique d’interprétation. La sémantique de compilation par défaut d’un mot consiste à ajouter sa sémantique d’interprétation à la définition actuelle.
Le mot ;
(point-virgule) termine la définition actuelle et revient à l’état d’interprétation. C’est un exemple de mot dont la sémantique de compilation diffère de la valeur par défaut. La sémantique d’interprétation de ;
(point-virgule), la plupart des mots du flux de contrôle et plusieurs autres mots ne sont pas définis dans ANS Forth, ce qui signifie qu’ils ne doivent être utilisés qu’à l’intérieur des définitions et non sur la ligne de commande interactive.
L’état de l’interpréteur peut être modifié manuellement avec les mots (parenthèse droite) qui entrent respectivement dans l’état d’interprétation ou l’état de compilation. Ces mots peuvent être utilisés avec le mot
LITERAL
pour calculer une valeur lors d’une compilation et pour insérer la valeur calculée dans la définition de deux points courante. LITERAL
a la sémantique de compilation pour prendre un objet de la pile de données et ajouter de la sémantique à la définition de deux points actuelle pour placer cet objet sur la pile de données.
Dans ANS Forth, l’état actuel de l’interpréteur peut être lu à partir de l’indicateur STATE
qui contient la valeur true en état de compilation et false sinon. Cela permet l’implémentation de mots dits intelligents d’état avec un comportement qui change en fonction de l’état actuel de l’interpréteur.
Immediate wordsEdit
Le mot IMMEDIATE
marque la définition du deux-points la plus récente comme un mot immédiat, remplaçant efficacement sa sémantique de compilation par sa sémantique d’interprétation. Les mots immédiats sont normalement exécutés pendant la compilation, pas compilés, mais cela peut être remplacé par le programmeur dans l’un ou l’autre état. ;
est un exemple de mot immédiat. Dans ANS Forth, le mot POSTPONE
prend un nom comme paramètre et ajoute la sémantique de compilation du mot nommé à la définition actuelle même si le mot a été marqué immédiat. Forth-83 a défini des mots séparés COMPILE
et pour forcer la compilation de mots non immédiats et immédiats, respectivement.
Mots sans nom et tokensEdit d’exécution
Dans ANS Forth, les mots sans nom peuvent être définis avec le mot :NONAME
qui compile les mots suivants jusqu’au suivant ;
(point-virgule) et laisse un jeton d’exécution sur la pile de données. Le jeton d’exécution fournit un handle opaque pour la sémantique compilée, similaire aux pointeurs de fonction du langage de programmation C.
Les jetons d’exécution peuvent être stockés dans des variables. Le mot EXECUTE
prend un jeton d’exécution de la pile de données et effectue la sémantique associée. Le mot COMPILE,
(compile-virgule) prend un jeton d’exécution de la pile de données et ajoute la sémantique associée à la définition actuelle.
Le mot '
(tick) prend le nom d’un mot comme paramètre et renvoie le jeton d’exécution associé à ce mot sur la pile de données. Dans l’état d’interprétation, ' RANDOM-WORD EXECUTE
est équivalent à RANDOM-WORD
.
Edit
Les mots :
(deux-points), POSTPONE
'
(tick) sont des exemples de mots d’analyse qui prennent leurs arguments du périphérique d’entrée utilisateur au lieu de la pile de données. Un autre exemple est le mot (
(paren) qui lit et ignore les mots suivants jusqu’à la parenthèse droite suivante et est utilisé pour placer des commentaires dans une définition deux-points. De même, le mot \
(barre oblique inverse) est utilisé pour les commentaires qui se poursuivent jusqu’à la fin de la ligne en cours. Pour être correctement analysées, (
(paren) et \
(barre oblique inverse) doivent être séparées par des espaces du texte de commentaire suivant.
Structure de codeEdit
Dans la plupart des systèmes Forth, le corps d’une définition de code est constitué soit d’un langage machine, soit d’une forme de code threadé. Le Forth original qui suit la norme FIG informelle (Forth Interest Group), est un TIL (Langage interprétatif fileté). Ceci est également appelé code à thread indirect, mais les Forths à thread direct et à thread sous-programme sont également devenus populaires à l’époque moderne. Les forts modernes les plus rapides, tels que SwiftForth, VFX Forth et iForth, compilent en code machine natif.
Objets de donnéesmodifier
Lorsqu’un mot est une variable ou un autre objet de données, le CF pointe vers le code d’exécution associé au mot de définition qui l’a créé. Un mot de définition a une caractéristique « comportement de définition » (création d’une entrée de dictionnaire plus éventuellement allocation et initialisation d’espace de données) et spécifie également le comportement d’une instance de la classe de mots construite par ce mot de définition. Les exemples incluent:
VARIABLE
Nomme un emplacement de mémoire non initialisé à une cellule. Le comportement de l’instance d’unVARIABLE
renvoie son adresse sur la pile.CONSTANT
Nomme une valeur (spécifiée comme argument pourCONSTANT
). Le comportement de l’instance renvoie la valeur.CREATE
Nomme un emplacement ; l’espace peut être alloué à cet emplacement, ou il peut être défini pour contenir une chaîne ou une autre valeur initialisée. Le comportement de l’instance renvoie l’adresse du début de cet espace.
Forth fournit également une facilité par laquelle un programmeur peut définir de nouveaux mots de définition spécifiques à une application, en spécifiant à la fois un comportement de définition personnalisé et un comportement d’instance. Quelques exemples incluent des tampons circulaires, des bits nommés sur un port d’E /S et des tableaux indexés automatiquement.
Les objets de données définis par ces mots et des mots similaires ont une portée globale. La fonction fournie par les variables locales dans d’autres langages est fournie par la pile de données dans Forth (bien que Forth ait également de vraies variables locales). Le style de programmation Forth utilise très peu d’objets de données nommés par rapport aux autres langages; en règle générale, ces objets de données sont utilisés pour contenir des données utilisées par un certain nombre de mots ou de tâches (dans une implémentation multitâche).
Forth n’impose pas la cohérence de l’utilisation du type de données; il est de la responsabilité du programmeur d’utiliser des opérateurs appropriés pour récupérer et stocker des valeurs ou effectuer d’autres opérations sur les données.