Salut à tous!
Je viens vous présenter mon dernier projet en date (un qui marche cette fois), ArkScript!
Encore et toujours un langage, après les échecs de Kafe, j’ai voulu me remettre le pied à l’étrier en me lançant sur un petit langage lisp like, interprété. De là est né Ark (renommé plus tard en ArkScript, cf la discussion sur reddit). Puis je me suis dit "et si cette fois-ci, je le compilais vraiment ce langage?" et au lieu de me lancer dans la VM avant de faire le compilo, j’ai fait les choses proprement, et ça a marché!
ArkScript reprend la philosophie de Kafe "scripting de jeux vidéos", mais c’est tout, les autres points sont bien différents.
Concepts et objectifs
ArkScript est inspiré de Lisp, tout est immuable par défaut, et tout est passé par valeur. Cela représente un coût à l’exécution, coût que je suis prêt à payer (cependant toutes les fonctions travaillant sur des listes évitent le plus possible les copies, sans pour autant impliquer des références cachées !). L’objectif du projet n’est pas d’être plus performant que du C bien fait, mais de privilégier les features (même si j’ai travaillé un peu sur les performances pour ne pas rester en rade), de manière à être plus productif quand on code avec ce langage.
Je voulais que le langage tourne sur VM pour le gain de performance comparé à une interprétation d’un arbre, et pour la portabilité: on compile une fois un code Ark et il marche partout tant que la VM est installée.
Le lexing/parsing se veut très simple, étant un lisp like il n’y a pas grand chose à faire. La partie la plus complexe, c’est le couple compilateur/VM, car il faut faire pas mal de choses pour fournir un langage haut niveau. On a donc de l’élimination de dépendances cycliques au parsing, de la réduction d’expressions constantes par le compilateur ainsi que de l’élimination de dead code (au scope global uniquement).
Le langage est dynamiquement et fortement typé, et autorise les listes hétérogènes. De plus, il n’y a aucune référence cachée (ce que je trouve très important pour conserver cet aspect d’immuabilité quand on déclare une variable avec let
).
Enfin, ArkScript se veut concis et n’a que 10 mots clés à offrir: let
, mut
et set
pour les constantes/variables, begin
pour les blocks, if
et while
pour le contrôle de flux, del
pour vider la mémoire manuellement (ArkScript profite également du RRID du langage d’implémentation, le C++), import
qui permet d’importer du code Ark et des binaires qui utilisent une API spéciale (par exemple pour charger la SFML, manipuler la console…), quote
pour faire de l’exécution différée, et fun
pour déclarer une fonction ou closure (à capture explicite).
Le langage se veut petit pour être utilisable dans un projet sans plomber la taille de celui-ci, la machine virtuelle tient sur moins de 3000 lignes (en comptant tous les fichiers des dossiers VM/
).
Des exemples
Un petit exemple qui utilises les closures avec la capture explicite (via la notation &capture), le closure field reading (dot notation) pour simuler de l’orienté objet (sans possibilité de modifier l’objet de l’extérieur, tout est en lecture seule quand on n’est pas dans la closure):
(let create-human (fun (name age weight) {
# functions can be invoked in the closure scope
(let set-age (fun (new-age) (set age new-age)))
# this will be our "constructor"
(fun (&set-age &name &age &weight) ())
}))
(let bob (create-human "Bob" 0 144))
(let john (create-human "John" 12 15))
(print bob.age)
(bob.set-age 10)
(print bob.age)
(print john.age)
La fonction d’Ackermann Péter qui me sert pour mes benchmarks comme elle est récursive non primitive, donc pas optimisable par un compilateur, très pratique pour tester l’implémentation d’un langage:
(let ackermann (fun (m n) {
(if (> m 0)
# then
(if (= 0 n)
# then
(ackermann (- m 1) 1)
# else
(ackermann (- m 1) (ackermann m (- n 1))))
# else
(+ 1 n))
}))
(print (ackermann 3 6))
Les releases sont disponibles ici (la lib standard est fournie avec chaque release, ainsi que les modules en .arkm).