Salut Zeste de Savoir ! Je viens vous partager un projet sur lequel je travaille depuis avril 2019, ArkScript.
Pour la petite histoire : 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 voilà où nous en sommes.
ArkScript reprend la philosophie originelle de Kafe "scripting de jeux vidéos", mais c’est tout, les autres points sont bien différents. Petit à petit cette philosophie a évolué en "un langage facile à utiliser pour scripter n’importe quel projet en C++".
Concepts et objectifs
En bref :
- un langage immuable par défaut (passage par valeur),
- un langage fortement et dynamiquement typé,
- pas de références cachées à la Python avec les listes,
- très peu de mots clés (9),
- des closures à capture explicite,
- privilégier les features avant les performances,
- élimination de dépendance cycliques,
- processeur de macros Turing Complete,
- élimination de dead code,
- de l’asynchrone avec n’importe quelle fonction,
ArkScript est inspiré de Lisp (entre autre pour la facilité à le parser), 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 comme le ferait Python). 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 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) et des expression statements (ce qui permet de ne pas polluer la VM avec des valeurs inutilisées). Le tout est saupoudré d’un processeur de macros Turing Complete (on peut faire des macros récursives, co-récursives, etc).
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 que l’utilisateur peut manipuler, ce que je trouve très important pour conserver cet aspect d’immuabilité quand on déclare une variable avec let
. En interne, côté VM, des références sont utilisées mais n’atteindront jamais l’utilisateur telle quelle et verront leur valeur copiée au moment voulu (copy on write) uniquement.
Enfin, ArkScript se veut concis et n’a que 9 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…), 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/
). Voir ce benchmark d’octobre 2020 qui compare ArkScript 3.0.13 à d’autres langages dont ChaiScript, Lua et Python.
Un écosystème de plugins
Le langage se veut extensible, et pour ça il expose une API pour créer facilement un plugin C++, qui peut être chargé par la machine virtuelle, par exemple pour :
- faire des appels HTTP
- jouer avec les couleurs du terminal
- parser du JSON
- communiquer avec une base SQLite3
- et bien plus encore
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)
(if (= 0 n)
(ackermann (- m 1) 1)
(ackermann (- m 1) (ackermann m (- n 1))))
(+ 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).
Voici le repo GitHub du projet https://github.com/ArkScript-lang/Ark. N’hésitez pas à passer faire un tour sur le site du projet et sa documentation ! Enfin si vous avez besoin d’aide, passez faire un tour sur le discord communautaire, nous nous ferons un plaisir de vous aider.