S01E15 — 12-08-2024.
La décomposition.
jour 15.
Hey frère ! Un langage moderne est obligé de permettre de faire la décomposition (destructuring dans la langue de Big Ben). Si ton langage ne permet pas de le faire, soyons clairs, c’est de la merde. Ne t’en fais pas, cette fatalité peut être surmontée. Je suis moi-même passer par là, j’avais un parser qui n’était même pas foutu de comprendre la décomposition. Un langage de merde quoi ! Ce temps est révolu, laisse-moi t’expliquer l’approche pour donner à ton compilateur le super pouvoir de la décomposition.
En général, un nom — qu’il soit pour une variable, une fonction, un type, etc. est souvent représenté par un identifiant dans un langage formel. Dès que le tokenizer rencontre un nom il va le transformer en jeton de type identifiant. Et c’est la bonne façon de faire.
Ensuite, dans la représentation de l’arbre de syntaxe abstraite, l’identifiant est lui aussi transformé en nœud de type identifiant. Et c’est là qu’est le problème. Ce n’est pas une mauvaise approche je rappelle qu’on veut créer des langages modernes, c’est-à-dire les langages du turfu. L’approche la plus commune est limitante. Elle ne donne aucune flexibilité à la décomposition. En scrutant le compilateur Rust. D’ailleurs, je t’invite à explorer le bijou qu’est le compilateur Rust. C’est ton devoir en tant que curieux. C’est un logiciel qui est écrit par les meilleurs ingénieurs du monde. Ils savent ce qu’ils font, de plus, pour apprendre le Rust c’est une excellente référence.
Pour en revenir à nos moutons, en scrutant le compilateur Rust. Je voulais comprendre leur approche puisque dans le cadre de mon langage je souhaitais pouvoir décomposer :
Les tableaux
imu [x, y, z]: float[] = [0.0, 1.0, 0.0];
Les tuples
imu (x, y, z): (float, float, float) = (0.0, 1.0, 0.0);
Comme la plupart d’entre nous, avant, j’avais un nœud Ident pour chaque identifiant dans le code. Rust suit une autre approche avec un nœud Pat celui-ci peut être représenté sous plusieurs formes via PatKind. Cette énumération possède une variante pour l’identifiant PatKind::Ident(..). Tu commences à capter le délire ? C’est vraiment cool comme approche, car tu peux ainsi avoir plus de contrôle. Par exemple en Rust, la déclaration d’une variable se fait comme ceci :
let foobar = 0;
Ici, foobar n’est pas juste un simple identifiant. C’est en réalité un pattern de type identifiant. Ce qui permet d’entrevoir clairement le champ des possibles. Si je souhaite supporter la décomposition pour les tableaux, je vais facilement rajouter une variante de type PatKind::Array(..), la même chose pour les tuples PatKind::Tuple(..). Ensuite, côté parser à moi de faire le taf dans une fonction parse_pattern pour retourner le pattern qui va bien en fonction du jeton courant.
- Si le jeton est un identifiant — je retourne un
Patde typePatKind::Ident(..). - Si le jeton est un délimiteur crochet ouvert
[— je parse ce qu’il faut et retourne unPatde typePatKind::Array(..). - Et ainsi de suite, selon les besoins de mon langage.
Rien de plus simple, pour comprendre le jutsu de la décomposition. Il me reste encore à comprendre comment cela doit s’articuler au niveau de l’interprétation, mais je suis confiant.
Vasy frérot, maintenant à toi de jouer. Rejoins-nous dans le turfu des langages modernes.