Bonjour,
Je vous suis depuis un petit moment sur ces ateliers mini-langages, et je crois que vous m'avez convaincu d'une chose avec ce topic: mieux vaut commencer par la VM que de s'embourber avec le parsing.
En tout cas c'est sur le parsing que je me suis déjà cassé les dents plusieurs fois, et du coup j'ai bien envie de partir sur une VM en premier, pour mon prochain essai de mini-langage.
Par contre j'ai plusieurs questions :
Question n°1: A quoi ça sert d'avoir des tables de constantes et de symboles ?
Quel avantage cela nous procure par rapport à encoder directement les données dans le bytecode ?
Par exemple, si on suppose que l'opcode LOAD CONST = 0xA0, pourquoi ne pas faire directement ceci pour charger la valeur 200 :
A0 00 00 00 C8
Au lieu de
A0 01, en supposant que la constante 00 00 00 C8 se trouve dans le slot 01 de la table des constantes ?
Est-ce que l'intérêt est uniquement de gagner de la place au cas où on utilise beaucoup de constantes / plusieurs fois la même constante, ou est-ce que ça cache autre chose de plus intéressant ?
Ou bien c'est juste « parce que CPython fait comme ça » ?
Pour la table des symboles, quel est son intérêt si on admet que notre mini-langage possède maintenant un type string, et des instructions du type
LOAD CONST "Hello"
STORE "myvar"
équivalent à une expression myvar = "Hello"; évidemment
Là encore ce ne serait qu'une question de place ?
Question n°2: je pige plus ou moins le truc avec les environnements qu'on va empiler et dépiler quand on entre/sort d'une fonction; l'environnement stockera en gros les variables locales. Mais alors comment ça va marcher pour les closures ?
Chaque function va avoir un environnement privé qui n'est pas remis à zéro à chaque nouvel appel ? Comment cet environnement privé va être capturé ? IL va donc falloir que chaque environnement possède une référence vers son environnement parent (au cas où les closures s'empilent) ?
Question n°3: Je vois dans votre liste d'instructions l'opcode ROT_2, qui sert à intervertir les deux éléments au sommet de la pile. A priori ça me semble effectivement assez utile.
Par contre à quoi va servir ROT_3 qui permute les trois premiers éléments de la pile ? JE n'arrive pas à imaginer d'utilité concrète.
Question n°4, plus générale: pourquoi avoir choisi une VM à pile plutôt qu'une VM à registres ?
Est-ce que c'est beaucoup plus simple à implémenter ? Ma première intuition me répond oui, mais je ne n'en suis pas certain, y-a-til d'autres critères qui ont mené à ce choix ?