Anatomie de MOGWAI — les propriétés fondamentales d’un langage concatenatif moderne

Introduction

Il existe des milliers de langages de programmation. La plupart partagent une même grammaire mentale : des expressions, des fonctions qui prennent des arguments entre parenthèses, des variables assignées avec =. Cette grammaire est tellement dominante qu’on finit par la confondre avec la programmation elle-même.

MOGWAI n’appartient pas à cette famille.

C’est un langage concatenatif, stack-based, homoïconique et réflexif, conçu comme moteur de scripting embarqué pour .NET. Son ADN remonte directement aux langages Forth et RPL — les langages des calculatrices HP 28 et HP 48 — tout en intégrant des concepts issus de la programmation fonctionnelle et prototypale. Le résultat est un langage qui pense différemment, et qui fait penser différemment.

Cet article explore les cinq propriétés fondamentales qui définissent l’identité de MOGWAI, ce qu’elles impliquent concrètement, et pourquoi elles forment un tout cohérent plutôt qu’une collection de fonctionnalités disparates.


1. Un langage concatenatif et stack-based

La pile comme unique convention d’appel

Dans la plupart des langages, appeler une fonction ressemble à ceci :

double résultat = Math.Sqrt(addition(3, 4));

L’arbre syntaxique est implicite dans l’imbrication des parenthèses. Pour évaluer cette expression, le compilateur doit d’abord résoudre l’imbrication, puis passer les résultats intermédiaires de fonction en fonction — via des mécanismes invisibles (registres, pile d’appel, conventions d’appel spécifiques à la plateforme).

MOGWAI fonctionne autrement. Il n’y a qu’une seule structure de données partagée entre toutes les opérations : la pile. Les valeurs y sont empilées, les primitives les consomment et y déposent leurs résultats.

3 4 + sqrt

Pas de parenthèses, pas de variables intermédiaires, pas d’arbre syntaxique. Chaque token est évalué séquentiellement, de gauche à droite : 3 est empilé, puis 4, puis + consomme les deux et dépose 7, puis sqrt consomme 7 et dépose 2.6457…

Ce style d’écriture s’appelle la notation polonaise inversée (RPN). Elle est à la fois la syntaxe du langage et son modèle d’exécution.

La concaténation comme composition

Ce qui rend ce modèle particulièrement élégant, c’est qu’il transforme la composition de fonctions en simple concaténation de texte. Dans un langage classique, composer deux fonctions croît vers l’intérieur et devient vite illisible. En MOGWAI, composer deux comportements c’est simplement les écrire l’un après l’autre :

« 2 * » -> 'DOUBLE'
« 1 + » -> 'INC'

5 !DOUBLE !INC # → 11

DOUBLE INC c’est la composition de deux blocs — pas d’opérateur, pas de syntaxe spéciale, juste la juxtaposition. C’est la propriété qui donne son nom au paradigme : un programme concatenatif est un programme dont les parties peuvent être juxtaposées pour former un programme plus grand. La composition n’est pas un opérateur — c’est le mode d’existence naturel du code.

L’héritage Forth/RPL

Cette approche n’est pas nouvelle. Forth, créé par Charles Moore dans les années 1970, a été l’un des premiers langages à exploiter systématiquement ce modèle. RPL, développé par HP pour ses calculatrices scientifiques haut de gamme (HP 28, HP 48), en a proposé une variante plus riche, avec des types de données structurés et une syntaxe de bloc délimitée par « ».

MOGWAI hérite directement de RPL, y compris dans ses choix syntaxiques : les blocs de code utilisent les mêmes guillemets « », la pile est le même citoyen de première classe, et la logique de travail reste fondamentalement la même — empiler, transformer, dépiler.

Ce qui change, c’est le contexte : là où RPL vivait dans la mémoire contrainte d’une calculatrice des années 80, MOGWAI s’exécute sur le runtime .NET, avec accès à l’écosystème C#, aux bibliothèques modernes, et aux outils contemporains.


2. Un langage homoïconique

Quand le code est une donnée comme les autres

La plupart des langages opèrent une distinction nette entre le code — ce qui s’exécute — et les données — ce qui est manipulé. Un entier, une chaîne, une liste : ce sont des données. Une fonction, une classe, un module : c’est du code. Cette frontière est si naturelle qu’on ne la questionne généralement pas.

MOGWAI, comme Lisp ou RPL avant lui, efface cette frontière. Un bloc de code « » est une valeur ordinaire : il peut être empilé, stocké dans une variable, passé à une primitive, retourné comme résultat, ou exécuté à la demande. Le code et les données partagent le même espace, la même pile, le même traitement.

Cette propriété s’appelle l’homoïconicité — du grec homos (même) et icon (représentation) : le programme se représente lui-même dans sa propre structure de données.

En pratique

« 2 * » -> 'DOUBLE'
« 1 + » -> 'INC'

5 !DOUBLE !INC # → 11

DOUBLE et INC ne sont pas des fonctions au sens classique — ce sont des variables qui contiennent des blocs. Le sigil ! les exécute automatiquement quand on y accède. Rien n’oblige à les exécuter immédiatement : on peut les stocker, les combiner, les passer à une autre primitive.

L’homoïconicité transparaît aussi dans la mécanique du if. La forme lisible :

if (a 10 >) then { "grand" ? } else { "petit" ? }

est traduite par le parser en sa forme canonique :

{! a 10 >} { "grand" ? } { "petit" ? } IFELSE

Trois blocs empilés, consommés par IFELSE. Même le branchement conditionnel est du code qu’on empile comme des données ordinaires.

Ce que ça change concrètement

L’homoïconicité n’est pas un détail esthétique — elle a des conséquences profondes :

  • Les primitives d’ordre supérieur sont naturelles. foreach transform, les boucles paramétrées par un bloc, le branchement conditionnel — tout cela s’écrit sans mécanisme spécial, parce que passer du code à une primitive c’est juste empiler une valeur.
  • La méta-programmation est triviale. Un programme peut construire un bloc dynamiquement, le modifier, puis l’exécuter — sans avoir besoin d’un mécanisme eval exotique ou d’une API de réflexion.
  • L’interpréteur est simple. Puisque code et données ont la même représentation, l’interpréteur n’a pas besoin de gérer deux mondes séparés.

C’est cette propriété qui rend possible tout ce qui suit : la réflexivité, le paradigme prototypal du bag, et la composabilité naturelle du langage.


3. Un langage réflexif

Inspecter et modifier ce qui s’exécute

Un langage est dit réflexif quand un programme peut, à l’exécution, observer et modifier sa propre structure ou son propre comportement. C’est une propriété rare et puissante — et chez MOGWAI, elle découle naturellement de l’homoïconicité. Puisque le code est une donnée, le programme peut le manipuler comme n’importe quelle autre valeur.

La réflexivité de MOGWAI s’exprime à trois niveaux distincts.

Niveau 1 — L’évaluation dynamique

Le mécanisme le plus direct : un bloc peut être construit ou obtenu à runtime, puis exécuté. Le sigil ! (auto-eval) en est la forme la plus concise — une variable qui contient un bloc est évaluée automatiquement au lieu d’être simplement poussée sur la pile.

« 2 * » -> 'OP'
5 !OP # → 10

C’est l’équivalent d’un eval dynamique, mais sans rien d’exotique — juste une conséquence logique du fait que le code est une valeur.

Niveau 2 — L’introspection et la mutation de l’environnement

MOGWAI permet à un programme d’agir sur ses propres variables pendant l’exécution. L’assignation dynamique permet de calculer le nom d’une variable à runtime :

'compteur' -> 'NOM'
100 -> NOM # place 100 dans 'compteur'

Le programme ne sait pas à l’avance quelle variable il va modifier — il le calcule. C’est de la réflexivité comportementale : le programme s’adapte à lui-même.

Niveau 3 — Le bag, réflexivité structurelle

Le bag — introduit dans MOGWAI 8.5 — pousse la réflexivité à son niveau le plus élevé. C’est une primitive qui pousse sur la pile le conteneur (record ou liste) du bloc ou de la fonction en cours d’exécution, permettant à un bloc de référencer son propre environnement à runtime.

[x: 10 y: 50 s: «! bag x: get bag y: get + »] -> 'POINT'
POINT s: get ? # → 60

bag dans le bloc fait référence au record qui le contient — l’équivalent d’un this implicite. Le slot s peut ainsi accéder aux autres slots de son propre conteneur sans connaître le nom de la variable externe. bag retourne null si le code s’exécute hors de tout conteneur.

Une réflexivité cohérente, pas cosmétique

Ce qui distingue la réflexivité de MOGWAI d’un simple mécanisme eval ajouté après coup, c’est qu’elle est structurelle : elle découle directement des propriétés 1 et 2. Parce que le langage est concatenatif, le code circule sur la pile. Parce qu’il est homoïconique, ce code est une donnée manipulable. La réflexivité n’est pas une fonctionnalité ajoutée — c’est une conséquence inévitable de la conception.


4. Un langage multi-paradigme

Au-delà du paradigme concatenatif

MOGWAI est fondamentalement concatenatif — mais cette identité de base n’est pas une contrainte. Elle est le socle à partir duquel plusieurs paradigmes coexistent naturellement, sans friction et sans compromis syntaxique.

Paradigme fonctionnel

Les blocs étant des valeurs de première classe, MOGWAI supporte nativement les idiomes de la programmation fonctionnelle : fonctions d’ordre supérieur, composition, application partielle.

(1 2 3 4 5) foreach 'item' transform { item 2 * }   # → (2 4 6 8 10)

foreach transform itère sur la liste et applique le bloc à chaque élément — l’équivalent d’un map fonctionnel, sans infrastructure particulière. C’est une conséquence directe de l’homoïconicité.

Paradigme impératif

MOGWAI dispose de variables, de boucles, de conditions et d’une gestion explicite de l’état — ce qui le rend parfaitement à l’aise pour des scripts procéduraux classiques.

0 -> 'SOMME'
1 -> 'I'

while (I 10 <=) do
{
SOMME I + -> 'SOMME'
I 1 + -> 'I'
}

SOMME ? # → 55

Rien d’exotique ici pour un développeur .NET : c’est de l’impératif classique, simplement écrit en RPN.

Paradigme prototypal

C’est le paradigme le plus récent dans MOGWAI, apporté par le bag. Là où les langages orientés objet classiques définissent des comportements dans des classes, MOGWAI permet de les attacher directement à des structures de données à runtime.

[x: 10 y: 50 s: «! bag x: get bag y: get + »] -> 'POINT'
POINT s: get ? # → 60

Pas de classe, pas de schéma déclaré à l’avance — la structure et le comportement émergent pendant l’exécution. C’est le modèle de JavaScript ou de Self, appliqué à un langage concatenatif.

Paradigme orienté objet

MOGWAI dispose d’un système de classes complet, intentionnellement simple : pas d’héritage, pas de ramasse-miettes. La gestion du cycle de vie est explicite et sous le contrôle total du développeur.

Une classe regroupe des propriétés typées et des méthodes dans deux sections : private: (accessible uniquement depuis l’intérieur de la classe) et public: (accessible depuis l’extérieur).

class 'Counter' do
{
private:
{
_step: .number
}

public:
{
value: .number

onInit:
{
[step: (.number 1)] ->params
self->reset:
step self<-_step:
}

increment:
{
self->value: self->_step: + self<-value:
}

reset:
{
0 self<-value:
}
}
}

[step: 5] 'Counter' new -> 'C'
C->increment:
C->increment:
C->value: ? # → 10
C free

onInit: est appelé automatiquement à la création de l’instance, onFree: juste avant sa destruction. À l’intérieur d’une méthode, self référence l’instance courante. La notation compacte -> lit une propriété, <- l’écrit.

Chaque instance reçoit un handle unique (par exemple §1) qui devient invalide dès que l’instance est détruite avec free.

Une cohérence plutôt qu’un assemblage

Ce qui est remarquable, c’est que ces quatre paradigmes ne coexistent pas par accumulation de fonctionnalités — ils émergent tous du même mécanisme central : la pile et les blocs comme valeurs. Le fonctionnel utilise les blocs comme fonctions d’ordre supérieur. L’impératif utilise la pile comme registre d’état. Le prototypal utilise les blocs comme méthodes attachées à des conteneurs. L’orienté objet structure données et comportements dans des classes à cycle de vie explicite. C’est le même outil, vu sous quatre angles différents.


5. Un citoyen .NET natif

Pas un langage isolé dans sa tour

Un langage embarqué n’a de valeur que s’il s’intègre naturellement dans son environnement hôte. MOGWAI n’est pas un interpréteur autonome qu’on appelle depuis .NET via un pont fragile — il est conçu depuis le départ pour vivre à l’intérieur d’une application .NET, comme un composant de première classe.

Intégration directe dans une app .NET

L’interpréteur MOGWAI s’instancie en quelques lignes de C# et expose immédiatement un environnement de scripting complet à l’application hôte. Les variables, les primitives personnalisées, et les événements peuvent être partagés entre le monde C# et le monde MOGWAI de manière bidirectionnelle.

Un développeur .NET peut :

  • Exposer des primitives C# à MOGWAI
  • Lire et écrire des variables MOGWAI depuis C#
  • Exécuter des scripts MOGWAI à la demande, de manière synchrone ou asynchrone

C’est le cas d’usage fondamental : MOGWAI comme moteur de règles métier scriptable, embarqué dans une application existante sans en perturber l’architecture.

Extensibilité par plugins (en cours)

MOGWAI dispose d’une architecture de plugins basée sur une interface simple : MOGWAI.Interfaces.IPlugin. Une bibliothèque externe peut exposer de nouvelles primitives à l’interpréteur sans modifier le cœur du langage — de l’extérieur, rien ne distingue une primitive built-in d’une primitive plugin.

Trois plugins internes illustrent déjà cette extensibilité : communication série, accès SQLite, et Bluetooth Low Energy. Le système est fonctionnel, mais son exposition publique est encore en cours de finalisation.

Compatibilité NativeAOT

MOGWAI est compatible avec la compilation NativeAOT de .NET (IsAotCompatible), avec sérialisation JSON par source generation et suppression des avertissements de réflexion. Concrètement, cela signifie qu’un programme embarquant MOGWAI peut être compilé en binaire natif autonome — sans runtime .NET installé sur la machine cible, avec un temps de démarrage minimal et une empreinte mémoire réduite.

C’est une propriété rare pour un interpréteur de langage dynamique, et elle ouvre MOGWAI à des contextes où le runtime .NET complet n’est pas disponible : systèmes embarqués, outils CLI distribués, environnements contraints.

Le playground WebAssembly

La compatibilité .NET va jusqu’à Blazor WebAssembly : un playground MOGWAI tourne entièrement dans le navigateur, sans serveur, via compilation WASM. C’est le même interpréteur, le même langage, exécuté dans un contexte radicalement différent — ce qui illustre mieux que n’importe quel argument la neutralité de plateforme que .NET rend possible.

Un langage en production

MOGWAI n’est pas un projet expérimental — il est déjà embarqué dans des applications .NET MAUI industrielles et des bancs de test WinForms en production, où il joue son rôle de moteur de scripting dans des contextes réels et contraints.

Une extension pour VS Code permet de coder pour MOGWAI.

Un IDE dédié, MOGWAI STUDIO, est par ailleurs en cours de développement, avec pour ambition d’offrir un environnement complet : édition, débogage, et exploration interactive de la pile.


Conclusion

Un tout cohérent

Ce qui frappe, en examinant MOGWAI propriété par propriété, c’est l’absence de coutures. Les cinq caractéristiques décrites dans cet article ne sont pas des fonctionnalités ajoutées indépendamment les unes des autres — elles forment une chaîne causale.

Parce que MOGWAI est concatenatif, le code circule sur la pile comme n’importe quelle valeur. Parce qu’il est homoïconique, ce code est une donnée manipulable à l’égal d’un entier ou d’une chaîne. Parce que le code est une donnée, le langage est naturellement réflexif — un programme peut observer et modifier sa propre structure à runtime. Cette réflexivité, combinée à la liberté de la pile, rend le langage multi-paradigme sans effort : fonctionnel, impératif, prototypal et orienté objet coexistent sans friction. Et tout cela s’exécute sur .NET, ce qui en fait un citoyen natif de l’écosystème Microsoft — extensible, compilable en natif, et déployable jusqu’au navigateur via WebAssembly.

Un héritage assumé, une identité propre

MOGWAI assume pleinement sa filiation avec Forth et RPL. Ce n’est pas de la nostalgie — c’est le constat que ces langages avaient trouvé des solutions élégantes à des problèmes que les langages mainstream ont souvent résolus de manière plus lourde. La pile comme convention d’appel universelle, le code comme donnée, la composition par juxtaposition : ces idées ont cinquante ans et elles n’ont pas pris une ride.

Ce que MOGWAI apporte, c’est leur projection dans l’écosystème .NET contemporain — avec les outils, les performances et les contraintes d’aujourd’hui.

Pour aller plus loin

MOGWAI est open source. Le code source, la documentation et le playground WebAssembly sont disponibles sur GitHub. Si cet article a éveillé votre curiosité, la meilleure façon de continuer est d’ouvrir le playground et d’empiler quelques valeurs.

Laisser un commentaire

En savoir plus sur CODING 4 PHONE

Abonnez-vous pour poursuivre la lecture et avoir accès à l’ensemble des archives.

Poursuivre la lecture