	Salut Godefroy,

	Les annotations de prsentation sont incluses entre crochets, et
il n'y a que l qu'on trouve des crochets d'ailleurs.
	[TEXTE] -> dbut d'une zone normale, tu formates au kilomtre
	[LISTING] -> dbut d'une zone de programme GFA, a afficher avec
		l'allure d'un listing.
	[DP] -> dbut de paragraphe forc, en gros, commencer une nouvelle
		ligne.

	Allez,  la prochaine!
	Toujours pas reu le XXL... L'article contient un (1) dans le
titre car je compte bien en faire un (2) pour complter.

	Guillaume

-----------------------------------------------

[TEXTE]

	UN INTERPRTEUR FORTH EN GFA! (1)
	*********************************

	Rien de tel que de programmer soi-mme un interprteur pour en
comprendre toutes les subtilits. Comme la majeur partie d'entre vous doit
disposer d'un GFA, c'est ce langage qu'on utilisera. Il se trouve que
c'est ainsi que j'ai commenc ma premire mouture du FORTH. Quand
l'histoire vous rattrape...

LES VARIABLES
-------------

	Il nous faut d'abord une pile de donnes pour toutes les
oprations arithmtiques et les passages de paramtres, c'est le tableau
PILE%(). Ensuite une pile des retours pour conserver les infos de toutes
les structures telles que les boucles, les IF-THEN, les sous-programmes,
c'est le tableau RETOUR%().
	Pour chacune de ces deux piles, un pointeur qui indiquera vers
quel lment du tableau on pointe  chaque instant: PILPTR% pour les
donnes et RETPTR% pour les retours.
	Finalement, il faut un tableau de chaines conservant l'ensemble
des ordres (ou fonctions) disponibles, c'est MOT$(). Le nombre total
d'ordres est conserv dans O%.
	Voici la premire partie, les pointeurs de piles sont mis  zro,
le tableau de mots est rempli et ses lments compts. La chaine "---"
sert de critre de fin:

[LISTING]

OPTION BASE 1
DIM pile%(100),retour%(100),mot$(20)
RESTORE
o%=0
DO
  READ a$
  EXIT IF a$="---"
  INC o%
  mot$(o%)=a$
LOOP
pileptr%=0
retptr%=0
DATA +,-,*,/,.,dup,swap,drop,system,ndo,nloop,timer,cr
DATA "---"

[TEXTE]

LE MOTEUR
---------

	Bte comme chou, on lit une chaine au clavier qu'on fournit
ensuite  l'interprteur. Seul le mot "system" met la variable FIN! 
TRUE, ce qui permet de terminer le programme.

[LISTING]

fin!=0
REPEAT
  PRINT
  INPUT "Mini FORTH > ",a$
  GOSUB interprete(a$)
UNTIL fin!
END

[TEXTE]

L'INTERPRTATION D'UNE CHAINE
-----------------------------

	Pour interprter une chaine, il suffit de la lire mot  mot et
d'excuter la procdure attache  chacun d'eux. Le sparateur est
l'espace, j'ajoute donc un caractre blanc en fin de chaine pour forcer la
limite du dernier mot. La valeur STRPTR% correspond  la position de
lecture dans la chaine:

[LISTING]

PROCEDURE interprete(b$)
  LOCAL strptr%
  b$=b$+" "
  strptr%=1

[TEXTE]
  	Pour trouver le mot suivant, on cherche l'espace  partir de la
position courante: si l'espace est  cette mme position, il faut avancer
d'un cran:

[LISTING]

  DO
    i%=INSTR(strptr%,b$," ")
    IF i%=strptr%
      INC strptr%

[TEXTE]
     	Sinon c'est qu'on tient un mot, il est copi dans mot$ et compar
 notre liste. Si il est trouv, la valeur I% ne dpassera pas O% dans la
boucle FOR-NEXT, donc selon la valeur I% on saute  la procdure voulue:

[LISTING]

    ELSE
      mot$=MID$(b$,strptr%,i%-strptr%)
      strptr%=i%+1
      FOR i%=1 TO o%
        EXIT IF mot$=mot$(i%)
      NEXT i%
      IF i%<=o%
        ON i% GOSUB plus,moins,fois,divise,point,duplique,permute,jette,quitte,ndo,nloop,chrono,cr

[TEXTE]
	Si le mot est inconnu, on considre que c'est en fait une valeur
numrique  ajouter sur la pile. Aprs avoir incrment le dit pointeur,
on range dans le tableau PILE%() la valeur de ce mot:

[LISTING]

      ELSE
        INC pileptr%
        pile%(pileptr%)=VAL(mot$)
      ENDIF
    ENDIF

[TEXTE]
    	L'interprtation s'arrte si le drapeau FIN! est mis (par l'ordre
system) ou si le pointeur de chaine STRPTR% a atteint la fin de la chaine:

[LISTING]

    EXIT IF fin!
    EXIT IF strptr%>=LEN(b$)
  LOOP
RETURN

[TEXTE]
	Considrer qu'un mot inconnu est forcment une valeur numrique
peut entrainer des erreurs, en effet, si je tape "drp" au lieu de "drop",
mon mot sera inconnu. Mais il se trouve que la fonction val("drp") ne
provoque pas d'erreur! Elle renvoit simplement zro. Il faudrait vrifier
que tous les caractres du mot sont composs de chiffres, si ce n'est pas
le cas, signaler l'erreur.

A CHAQUE MOT, SON EFFET
-----------------------

	Par exemple, le mot "+" prend les deux entiers au sommet de
la pile puis les remplace par leur somme. I% est d'abord la valeur au
sommet, ensuite on descend d'un cran et on ajoute  I% la seconde valeur,
c'est cette dernire qui est range dans le nouveau sommet:
[LISTING]

PROCEDURE plus
  i%=pile%(pileptr%)
  DEC pileptr%
  i%=i%+pile%(pileptr%)
  pile%(pileptr%)=i%
RETURN

[TEXTE]

	Mme chose pour "-", "*" et "/":

[LISTING]

PROCEDURE moins
  i%=pile%(pileptr%)
  DEC pileptr%
  i%=pile%(pileptr%)-i%
  pile%(pileptr%)=i%
RETURN
PROCEDURE fois
  i%=pile%(pileptr%)
  DEC pileptr%
  i%=i%*pile%(pileptr%)
  pile%(pileptr%)=i%
RETURN
PROCEDURE divise
  i%=pile%(pileptr%)
  DEC pileptr%
  i%=pile%(pileptr%)/i%
  pile%(pileptr%)=i%
RETURN

[TEXTE]
	De son ct, le point sert  afficher le sommet de la pile, valeur
qui disparat ensuite:

[LISTING]

PROCEDURE point
  PRINT pile%(pileptr%);" ";
  DEC pileptr%
RETURN

[TEXTE]
	Viennent ensuite les mots "dup", "swap" et "drop" qui permettent
respectivement de dupliquer, permuter ou jeter des valeurs:

[LISTING]

PROCEDURE duplique
  i%=pile%(pileptr%)
  INC pileptr%
  pile%(pileptr%)=i%
RETURN
PROCEDURE permute
  SWAP pile%(pileptr%),pile%(pileptr%-1)
RETURN
PROCEDURE jette
  DEC pileptr%
RETURN

[TEXTE]
	Le mot "system" dont il est question depuis tout  l'heure met
simplement le drapeau FIN!  TRUE, ce qui entrane l'arrt du programme:

[LISTING]

PROCEDURE quitte
  fin!=TRUE
RETURN

[TEXTE]
	Voici la seule structure implante pour l'instant, il s'agit de la
boucle NDO-NLOOP qui rpte un certain nombre de fois les mots qu'elle
contient. Ds que NDO est rencontr, la valeur de STRPTR% est conserve
sur la pile des retours, en effet: c'est  cet endroit de la chaine, juste
aprs NDO, qu'on reviendra  chaque itration. On y dplace galement la
valeur au sommet de la pile qui va servir de compteur.

[LISTING]

PROCEDURE ndo
  INC retptr%
  retour%(retptr%)=strptr%
  INC retptr%
  retour%(retptr%)=pile%(pileptr%)
  DEC pileptr%
RETURN

[TEXTE]
	Lorsque NLOOP est rencontr, on dcrmente de 1 la valeur au
sommet de la pile des retours, c'est  dire le compteur! Si le compteur
est devenu nul, c'est que le bouclage se termine, on dpile les deux
valeurs ranges par NDO et on sort. Par contre, si cette valeur n'est pas
nulle, on la range et on reprend l'ancienne valeur de STRPTR% pour
remonter en arrire dans la chaine afin de boucler:

[LISTING]

PROCEDURE nloop
  i%=retour%(retptr%)-1
  IF i%=0
    DEC retptr%
    DEC retptr%
  ELSE
    retour%(retptr%)=i%
    strptr%=retour%(retptr%-1)
  ENDIF
RETURN

[TEXTE]
	Le fait que ce soit une pile qui conserve ces informations de
boucle permet justement d'imbriquer  l'infini de telles structures
(enfin, dans la limite de la pile des retours).

	Le mot "timer" est reli au chrono qui prend la valeur de
l'horloge  200 Hz du ST. J'ai mis a si vous voulez chronomtrer vos
crations ou vos amliorations de cet interprteur:

[LISTING]

PROCEDURE chrono
  INC pileptr%
  pile%(pileptr%)=TIMER
RETURN

[TEXTE]
	Le mot "cr" ralise un saut de ligne (carriage return):

[LISTING]

PROCEDURE cr
  PRINT
RETURN

[TEXTE]

QUELQUES ESSAIS
---------------

	Lancez le programme et tapez ceci, en sparant bien avec les
espaces:

[DP]	1 3 + .

[DP]	Vous obtenez un 4 qui est bien le rsultat de 1+3 (magnifique!).
Essayez galement ceci:

[DP]	1 10 ndo 2 * nloop .

[DP]	Vous aurez multipli dix fois par deux le nombre 1, on obtient
deux puissance dix, c'est  dire 1024. On peut faire plus fort pour
vrifier l'imbrication des boucles:

[DP]	0 5 ndo 5 ndo 1 + dup . nloop cr nloop drop

[DP]	Finalement, l'utilisation du timer doit ressembler  ceci:

[DP]	timer 1000 ndo nloop timer swap - 2 / .

[DP]	Pour chronomtrer les mille boucles vides on dpose la
valeur du timer avant et aprs la boucle. Ces deux valeurs, aprs
permutation pour cause de signe, sont soustraites. Le rsultat est divis
par deux pour passer des 1/200 aux 1/100 de seconde. Mon STE me donne
quelque chose comme 481, c'est  dire 4.81 sec ou presque 208 boucles  la
seconde.

LA PROCHAINE FOIS
-----------------

	On ajoutera les fonctions de comparaison =, >, < et <> ainsi que
la structure IF-THEN. On s'attaquera aussi  la dfinition de mots par
l'utilisateur.
	Mais, d'ici l, rien ne vous empche de complter vous-mmes cet
interprteur: ajoutez le mot  la fin de la ligne DATA, ajoutez le nom de
la procdure  la fin de la ligne ON-GOSUB et crez cette procdure, c'est
tout. Essayez par exemple de programmer le mot "and" qui ralise un "et"
logique sur les deux valeurs au sommet de la pile et qui les remplace par
ce rsultat. Essayer aussi de programmer la fonction "abs" (valeur
absolue) ou "h." qui affiche un nombre en hexadcimal.

A QUOI CA SERT?
---------------

	Outre le fait de faire un interprteur FORTH, ce genre de petit
programme peut tre gnralis  un interprteur de commandes quelconque.
Imaginez que tous les softs proposent cette option en plus des entres de
menu cliquables: imaginez vous dans un programme de cration en 3D capable
de faire des rotations, des mouvements de camra, des zooms etc... On
basculerait alors dans l'interprteur et on pourrait taper:
[DP]	36 ndo 10 ROTATION 1.1 ZOOM AFFICHE nloop
[DP]	Qui permetrait de tourner autour de l'objet par pas de 10 degrs
en mme temps que la camra zoome de 1.1. Ce systme peut galement tre
inclus dans une base de donnes pour la rendre programmable, et pourquoi
pas dans un traitement de textes pour raliser des remplacements ou des
statistiques de manire trs pointue et trs personnalise.
	Il me semble qu'on peut dcupler les possibilits d'un logiciel
sans rien ajouter aux fonctions de base sauf un interprteur qui permet de
les combiner  l'infini.

	Sur cette note d'ternit, je vous dis  la prochaine!

	Guillaume TELLO
	gtello@wanadoo.fr

