Bon, essayons de partir sur des bases très simples, et on corrigera/enrichira ça quand on rencontrera des problèmes ou quand on voudra faire des trucs plus alambiqués.
Donc, pour moi ce qui me semble le plus facile à coder c'est une simple VM à pile. C'est-à-dire pas de registre, on pop et on push des valeurs sur une pile pour réaliser des opérations.
Définissons 1 type d'objet natif : les entiers (un seul type, disons i64 en interne).
Puis quelques opérations. J'appellerai TS
(top of the stack) le sommet de la pile, TS1
l'élément juste en dessous, TS2
le troisième élément de la pile, etc. :
ADD
: TS := TS1 + TS
MUL
: TS := TS1 * TS
DIV
: TS := TS1 / TS
SUB
: TS := TS1 - TS
POW
: TS := TS1 ** TS
Maintenant il faut pouvoir peupler cette pile :
CONST(n)
: pousse l'entier constant n
sur la pile
Voilà, nous avons une calculette polonaise.
Ajoutons des variables.
Il faut rajouter à la VM la notion d'environnement. Pour moi, vieux pythoniste, un environnement est une pile de scopes :
- La base de la pile est le scope global (un dictionnaire, tout simple)
- Lorsque l'on entre dans un scope lexical, on empile un nouveau dictionnaire
- Lorsque l'on sort du scope, on dépile un scope de l'environnement
Du coup :
NEW_ENV
: pousse un nouveau scope vide dans l'environnement,
POP_ENV
: dépile le sommet de l'environnement
LET (s)
: crée une entrée (variable) s
dans le scope local (le sommet de l'environnement), dépile TS
et associe cette valeur à s
.
LOAD (s)
: effectue une recherche pour le symbole s
dans l'environnement et place la valeur au sommet de la stack.
STORE (s)
: recherche un scope qui contient la clé s
et associe TS
à la clé s
dans le scope qu'on a trouvé.
Ne réfléchissons pas tout de suite aux fonctions, closures, etc. on y viendra après avoir un premier code qui marche. Essayons déjà de partir sur cette base, et de voir où ça nous mène.
Accessoirement on peut peut-être s'ajouter quelques opcodes pour afficher le sommet de la stack, etc., pour nous aider au debug, mais je pense pas que ce soit nécessaire de spécifier ça tout de suite.
Qu'en pensez-vous ?
Edit : Après ça il faudra sûrement spécifier des instructions comme des sauts, pour réaliser des conditions, des boucles, etc. Donc introduire la notion de code pointer dans la VM, mais je vous propose de le garder pour la phase 2. Pour moi, tout ce qui est branchements (if/else
) et boucles while
, finalement, c'est pas tellement différent de rajouter des procédures, c'est-à-dire faire des sauts dans le bytecode.