Description
Le stoicos est un langage de programmation fonctionnel, impératif et orienté objet. Il est interprété et dynamiquement typé, même si un typage progressif est envisagé. Côté syntaxe, il s'inspire de la famille des lisps, des langages connus pour leur abondance de parenthèses. C'est un langage que je développe depuis fin 2015 et qui est actuellement à sa 5e réécriture.
Voici un exemple d'app graphique réalisée avec le langage :

Historique
V1
La première version a été développée fin 2015 en ruby. On peut clairement parler de prototype. Dans cette version, les variables sont globales et on ne peut pas créer ses propres fonctions. Il existe toutefois un moyen de créer des procédures, qui sont un moyen de stocker du code pour l'exécuter plus tard.
Le code de résolution de fonction est un immense switch case. Autant dire que ça n'était pas fameux !
Malgré tout je suis assez fier de ce que j'ai pu accomplir sur cette première version, surtout compte tenu de ma faible expérience en développement.
V2
Entre temps, je découvre le langage de programmation OCaml et décide de réécrire mon langage avec. Cela m'impose de repenser une bonne partie de mon code, mais c'est pour le mieux ! Je m'arrête sur un interpréteur capable de lancer un hello world et reprend tout le code, à nouveau en ruby, pour faire une v2. Son implémentation est plus stable et certains points de syntaxe sont améliorés.
La version 2 introduit notamment la notion de blocs inspirés des blocs ruby, un objet contenant du code à application suspendue pouvant être exécuté une ou plusieurs fois. Le code est aussi plus objet, et moins procédural.
V3
Après deux versions simples mais qui montrèrent vite leurs limites, je décide fin 2017 de revoir en profondeur le code du langage afin de permettre la création de fonctions. Ce qui apporte aux développeurs qui utiliseraient le langage plus de souplesse dans leur code. Le langage introduit aussi les fonctionnalités suivantes :
- Ajouts de structure/module avec un accès en
struct.property - Ajout de l'accès aux arrays sous la syntaxe
mon_array[index] - Ajout de mini macros permettant de générer à la volée une expression
- Ajout d'un léger support de l'orienté objet avec la possibilité d'ajouter des méthodes dans des structures et de les appeler.
Voici un exemple de code à ce stade :
= :is_prime (function :nombre
{
(= :aux (function :x
{
(cond
[{(> (* x x) nombre)} {true}]
[{(== (% nombre x) 0)} {false}]
[true {(aux (+ x 1))}]
)
}
)
)
(aux 2)
})V4
La V3 est très intéressante au niveau des nouveautés qu'elle apporte mais possède quelques défauts :
- Le langage n'est disponible que sur Windows
- La gestion des variables n'est pas parfaite et cause parfois des bugs invisibles
- Le langage est particulièrement lent. Un hello world s'exécute en 3 secondes
Je décide de faire une 4e réécriture en ruby, qui ne verra pas le jour. Considérant le langage comme étant plus un jouet qu'un projet sérieux, je décide finalement de réécrire en typescript, et de l'intégrer dans une application de bureau à l'aide d'electron, puis de tauri. M'étant plus intéressé aux langages de la famille de lisp, en particulier racket, je décide d'adapter la syntaxe de mon langage en conséquence.
Le développement de cette version est étalé sur le temps et j'essaie de bien réfléchir à l'implémentation dans le but de ne plus enchaîner les refontes de 0. Je pose un correctif sur la gestion des closures et commence un test pour pouvoir typer ses variables, à l'aide d'un système de type résolu au runtime. Par exemple, pour définir le type Array[String], il faut utiliser la fonction Array.of et lui passer en paramètre le type String.type pour générer le type d'Array correspondant. Si l'expérimentation n'est pas allée très loin, j'ai bon espoir qu'un jour le langage puisse ainsi gérer des types plus complexes, comme les types dépendants.
Une ultime version
Pour finir, je réalise qu'un langage dans une interface customisée n'est pas très pertinent. Les utilisateurs préfèrent utiliser leur propre éditeur et le fait de passer par un interpréteur web limite pas mal les possibilités du langage. La tentative de créer un serveur web avec cela s'est, par exemple, révélée être un vrai casse-tête ! C'est pourquoi je produis une ultime version en haxe, qui cette fois marche en cli. En un peu plus d'une semaine, elle rattrape le travail effectué sur la version typescript et commence déjà à offrir plus de possibilités.
Exemples de code
; Le classique hello world
(println "hello world"); Exemple de server http
(import :Server)
(import :File)
(import :Struct)
(import :Json)
(def :mimes Server.mimes)
(def :data (Struct.make [
[:name "Paul Dupont"]
[:age 42]
[:old false]
]))
(Server.start [
["/" (fun [] {
(Server.response (File.get_content "./index.html") mimes.html)
})]
["/paul" (fun [] {
(Server.response "Hello Paul!" mimes.text)
})]
["/json" (fun [] {
(Server.response (Json.serialize data) mimes.json)
})]
])