Le développement de mon langage de script à typage statique Umka est entré dans la phase où il était nécessaire de tester les capacités du langage en utilisant des exemples plus complexes que des scripts sur quelques dizaines de lignes. Pour cela, j'ai décidé d'implémenter l' interpréteur Lisp dans ma langue . J'ai été inspiré par une expérience pédagogique de Rob Pike, l'un des créateurs du langage Go. Pike a récemment publié un petit interpréteur Lisp dans Go . J'ai été particulièrement impressionné par la remarque de Pike selon laquelle la description de l'interprète se trouvait sur une page 13 de l'ancien manuel Lisp 1.5.... Compte tenu de l'affinité syntaxique d'Umka et Go, il était difficile de résister à la tentation de construire un tel interpréteur dans Umka, non pas en transférant littéralement le code de Pike, mais complètement à partir de zéro. J'espère que les connaisseurs de Lisp et des langages fonctionnels me pardonneront l'étonnement naïf d'être en contact avec la beauté.
Pour les non-initiés, Lisp peut être au plus près du choc. Où est la frontière entre le code et les données? Où sont les boucles? Où est la pile? La seule structure de données est un arbre. Il peut également représenter une liste. Il devient également un arbre de syntaxe abstraite lorsque le programme est analysé. Il remplace également la pile lors de l'évaluation des expressions. N'importe quel arbre peut être essayé pour être exécuté en tant que code ou utilisé comme données. Au lieu de boucles - récursivité. Il n'y a même pas d'arithmétique au cœur du langage. Pourtant, c'est un langage complet de Turing qui peut être infiniment étendu et saupoudré de sucre syntaxique.
Définir un interpréteur Lisp minimal prend vraiment moins d'une page. Sur un bout droit, bien sûr: il utilise des fonctions définies sur plusieurs pages précédentes. Le créateur de Lisp John McCarthy semble avoir fait tout son possible pour se surpasser dans la concision et a finalement publié un micro-manuel Lisp contenant la définition de la langue ainsi que la source de l'interprète - deux pages de magazine au total. Certes, il a ajouté au titre: " Pas toute la vérité ".
Le noyau de la langue (nous parlons ici des dialectes les plus anciens et les plus simples) nécessite cinq fonctions élémentaires, quatre mots-clés et deux constantes qui ne peuvent être exprimées au moyen de la langue elle-même.
Constructions de langage de base pour ceux qui ne les connaissent pas
(car x)
- mise en évidence de la tête de listex
(cdr x)
—x
(cons x y)
—x
y
(atom x)
—x
(eq x y)
—x
y
(cond (a x) (b y))
—x
y
a
b
(quote x)
—x
,((lambda (x) a) y)
—a
,x
y
((label ff (lambda (x) a)) y)
—ff
t
—nil
—
, . , , , 6:
((label fac (lambda (n) (cond ((eq n 0) 1) ((quote t) (mul n (fac (sub n 1))))))) 6)
Lisp, . Lisp 1.5 13 , . . , REPL . , , , label
defn
, , . Lisp .
Puisque l'interpréteur entier est criblé de récursivité et de traitement d'arborescence, il a servi de test excellent pour de nombreuses fonctionnalités du langage Umka, de l'empilement au ramassage des ordures. Je pense qu'Umka a bien réussi le test. Nous n'avons dû corriger que deux ou trois bugs mineurs liés à l'exportation des noms et des déclarations de type avancées. L'ensemble du code d'interprétation faisait moins de 400 lignes.
Pour ceux qui veulent jouer avec mon interprète et passer l'inspiration de Rob Pike par le bâton, je recommande de prendre le dossier avec des exemples de la branche master , et non de la dernière version .