Close

MOGWAI (langage)

MOGWAI est une bibliothèque développée par GUYZMO permettant de doter les applications d’un moteur d’exécution de scripts. Il devient ainsi possible de modifier une partie du code sans recompiler ni republier. Cette bibliothèque est écrite en .NET Standard et existe aussi pour iOS (écrite en SWIFT). Une version native Android est prévue.

Pour le moment (mais ça va évoluer rapidement) MOGWAI tourne donc sous Windows PC, tablette, phone (RIP) et aussi sous Raspberry PI 3 (grâce à Windows 10 IoT), et sous iOS.

Pourquoi créer un moteur d’exécution ?

Chez GUYZMO nous codons énormément d’applications utilisant le Bluetooth Low Energy (BLE). Suivant le type de périphérique auquel nous nous connectons, il faut effectuer une série d’opérations pour se mettre par exemple dans un état particulier de fonctionnement puis lancer une série de tests centrés sur les échanges BLE. Ce type de manipulations est très courant pendant le développement de l’application pour le client.

Sans ce type de moteur, il faudrait créer pour chaque projet une application de tests permettant d’effectuer toutes les opérations.

Avec ce type de moteur, l’application de tests native reste très générique en effectuant toutes les opérations nécessaires sous la directive d’un code qui est modifiable à volonté, en temps réel, sans recompilation, car c’est le code du moteur de script qui va s’occuper de toute la partie « logique » des tests. Il suffira de stocker pour chaque périphérique un jeu de scripts adapté à son mode de fonctionnement et adapté aux tests à réaliser. La souplesse obtenue est énorme !

Nous avons d’ailleurs créé un outil dans ce sens en utilisant MOGWAI. Un article complet lui est consacré sur ce blog.

Pile et notation polonaise inversée

Pour être efficace et permettre des temps de parsing et d’exécution les plus rapides possibles il était impératif d’utiliser une syntaxe simple évitant au maximum les complexes étapes d’analyse de l’arbre syntaxique et toutes les joyeusetés qui vont avec.

MOGWAI est un langage qui utilise une pile et la notation polonaise inversée (comme le langage Forth). La syntaxe, un peu déroutante au début, est souvent dans l’ordre inverse des langages classiques.

Par exemple, pour demander un nombre aléatoire compris entre 0 et 1, le multiplier par 100, ne garder que la partie entière et le stocker dans la variable « x » on va écrire en MOGWAI :

rand 100 * ->int 'x' sto

et non

x = int(rand() * 100)

Le parent spirituel de MOGWAI est le RPL

Cette manière de coder est très fortement inspirée du langage RPL des calculatrices scientifiques HP. Ce langage est utilisé par HP depuis plusieurs décennies, et s’avère à l’usage efficace et puissant.

Calculatrices HP 48 SX et GX

Le fait que le RPL soit un langage utilisant une pile avec une syntaxe très simple en a fait tout naturellement une source d’inspiration pour créer MOGWAI. Pour ne rien vous cacher, j’ai eu dans ma jeunesse beaucoup de satisfaction à utiliser le RPL pour créer des programmes sympathiques sur ma HP 48 SX (que j’ai toujours et qui fonctionne comme au 1er jour) à l’époque où je passait beaucoup de temps à coder sur tout et n’importe quoi avec tous les langages qui me tombaient sous la main (dit comme ça, on dirait un névrosé qui parle, ça fait peur).

Pile et notation polonaise inversée

La base de tout MOGWAI est là. Quand on sait manipuler la pile et que l’on est à l’aise avec la notation polonaise inversée (RPN, à ne pas confondre avec le langage RPL), coder en MOGWAI s’avère très simple.

Si vous voulez ajouter 2 nombres, il suffit de les placer sur la pile, et d’invoquer la fonction d’addition. Cette fonction va prendre 2 paramètres sur la pile, les ajouter et placer le résultat… sur la pile, tout simplement.

la séquence suivante ajoute les nombres 5 et 9 et place le résultat (14) sur la pile :

5 9 +

Si maintenant je veux multiplier par 10 le résultat :

10 *

Il reste sur la pile la valeur 140

Si on regarde l’état de la pile à chaque étape ça donne ceci :

Etape 1, on pose 5 sur la pile

  • Niveau 1 = 5
  • Niveau 2 = Vide

Etape 2, on pose 9 sur le pile

  • Niveau 1 = 9
  • Niveau 2 = 5
  • Niveau 3 = Vide

Etape 3, on appelle la fonction +

  • Niveau 1 = 14
  • Niveau 2 = Vide

Etape 4, on place 10 sur la pile

  • Niveau 1 = 10
  • Niveau 2 = 14
  • Niveau 3 = Vide

Etape 5, on appelle la fonction *

  • Niveau 1 = 140
  • Niveau 2 = Vide

Cette manière de faire permet de se passer des parenthèses pour indiquer l’ordre des opérations.

Pour calculer (5 + 7) * (17 – 12) * 5 il suffit d’écrire en MOGWAI :

5 7 + 17 12 - * 5 *

Et on obtient sur la pile, au 1er niveau, la valeur 300

Ca marche aussi avec d’autre choses que des nombres :

"bonjou" "r" +

Pose « bonjour » sur la pile (le résultat de la somme de « bonjou » et « r »)

Les paramètres en premier, la fonction en dernier

L’usage systématique de la pile pour passer les paramètres impose d’avoir en 1er les paramètres puis la fonction qui va les utiliser. Comme on l’a vu par exemple pour les fonctions + ou *

2 5 +

et non 2+5

10 20 *

et non 10*20

Quelques points à savoir

Avant de continuer, il faut savoir que :

  • Les noms (pour nommer les variables par exemple) sont écrits avec des quotes (ex ‘a’ ‘toto’ ‘essai’)
  • Les variables posées sur la pile sont directement évaluées (remplacées par leur valeur) et son écrites sans les quotes
  • Les bocks de code sont écrit entre {  }
  • Les fonctions de conversion commencent par -> (ex ->int pour convertir en entier)
  • Qu’au bout d’un moment on s’y fait

Pour stocker une valeur dans une variable on utilise la fonction sto

50 'a' sto

stocke 50 dans la variable a

"HELLO" 'message' sto

stocke « HELLO » dans la variable message

Pour tester si une valeur est plus grande qu’une autre on utilise la fonction >

30 50 >

pose false sur la pile car 30 n’est pas plus grand que 50

30 30 ==

pose true sur la pile car 30 est bien égal à 30

Comment faire un simple IF ?

Pour effectuer un test du genre (en syntaxe type basic) : if a > 50 then b = 30 il faut penser pile+RPN ce qui donne :

test { code à exécuter si le test est positif } IF

en MOGWAI ça donne :

a 50 > { 30 'b' sto }  IF

Ce qui je vous l’accorde n’est pas très facile à lire, mais nous allons voir un peu plus loin que ça va s’arranger.

Comment se passe l’exécution de ce code au niveau de la pile ?

Etape 1, on pose sur la pile a (on va dire que dans a il y a la valeur 90, c’est donc 90 qui est posé sur la pile)

  • Niveau 1 = 90
  • Niveau 2 = Vide

Etape 2, on pose 50 sur la pile

  • Niveau 1 = 50
  • Niveau 2 = 90
  • Niveau 3 = Vide

Etape 3, on appelle la fonction > qui va prendre 2 éléments de la pile et tester si le 1er est > au 2ème et placer le résultat (true ou false) sur la pile. Comme 90 est bien > à 50 donc true est posé sur la pile :

  • Niveau 1 = true
  • Niveau 2 = Vide

Etape 4, le bloc de code à exécuter si le test est vrai est placé sur la pile :

  • Niveau 1 = { 30 ‘b’ sto }
  • Niveau 2 = true

Etape 5, on appelle la fonction IF qui va prendre le bloc de code et la valeur booléenne de la pile et exécuter le bloc de code uniquement si la valeur booléenne est vraie (ce qui est le cas ici) :

  • Niveau 1 = Vide

Etape 6, le bloc que code est exécuté, on place 30 sur la pile :

  • Niveau 1 = 30
  • Niveau 2 = Vide

Etape 7, on place le nom ‘b’ sur la pile :

  • Niveau 1 = ‘b’
  • Niveau 2 = 30

Etape 8, on appelle la fonction sto qui va donc stocker 30 dans la variable b. La pile termine vide :

  • Niveau 1 = Vide

Simplification de certaines expressions

Comme vous avez pu le voir avec l’exemple du simple IF, la syntaxe 100% polonaise inversée n’est pas très naturelle. Heureusement MOGWAI possède quelques mécanismes qui permettent (pour certaines expressions) une écriture plus classique.

Ainsi pour le IF, au lieu d’utiliser directement la fonction IF 100% RPN, on peut utiliser l’expression if qui se construit de la manière suivante :

if { bloc de code servant à effectuer le test } then { bloc de code à exécuter si le test est vrai } :

if { a 50 > } then { 30 'b' sto }

Ce qui est plus facile à lire.

La version avec ELSE existe aussi :

if { a 50 > } then { 30 'b' sto } else { 10 'b' sto }

En plaçant les instructions sur plusieurs lignes on retrouve une syntaxe plus habituelle :

if { a 50 > } then
{
    30 'b' sto
}
else
{
    20 'b' sto
}

Les expressions suivantes bénéficient du système de simplification et s’écrivent avec une syntaxe plus classique :

  • if
  • if else
  • repeat
  • for
  • for step
  • while
  • foreach
  • try catch
  • onEvent
  • timer

Nous verrons en détails chacune de ces instructions tout au long des différents articles qui seront consacrés à MOGWAI .

Les listes

Les listes sont des collections pouvant contenir n’import quel type de base. Elles sont notées avec des parenthèses et chaque élément est séparé par un espace :

( 1 3 4 "hello" ( true "Guyzmo" 156.7 ) "x" "y" 'somme' maVariable )

Celle liste contient 9 éléments différents (3 nombres, 1 chaine, une autre liste, 2 chaines, un nom, un mot)

Il est possible de manipuler les listes avec toute une série de fonctions (qui seront détaillées plus tard).

Le code suivant permet de créer une liste composées de 10 nombres aléatoires compris entre 0 et 99 :

( )

10 repeat 
{
    rand 100 * ->int +
}

Pour résumer, ce code place sur la pile une liste vide, puis 10 fois de suite ajoute à la liste présente sur la pile un nombre aléatoire compris entre 0 et 99.

Si on exécute ce code lui-même 10 fois on obtiendra 10 listes de 10 éléments aléatoires compris entre 0 et 99.

10 repeat { ( ) 10 repeat { rand 100 * ->int + } }

Il y a un moyen d’aller encore plus vite (au niveau vitesse d’exécution) en utilisant massivement la pile pour composer une liste :

10 repeat { rand 100 * ->int } 10 ->list

Je vous laisse méditer sur ce petit bout de code qui crée une seule liste de 10 éléments bien plus vite.

Les types manipulables par MOGWAI

MOGWAI sait manipuler les types suivants :

  • Nombres (décimaux, hexadécimaux et binaires)
  • Chaînes de caractères
  • Dates et durées
  • Listes (on vient d’en parler)
  • Noms
  • Mots
  • Evénements
  • Tâches
  • Timers
  • Enregistrements (dictionnaires)
  • Clés (pour les enregistrements principalement)
  • Tableaux d’octets

Les fonctions actuellement disponibles

MOGWAI propose un grand nombre de fonctions de base. Il est possible de l’étendre en utilisant une communication basée sur l’échange avec son hôte via un système d’interfaces et de délégués.

Voici la liste actuelle des fonctions disponibles, certaines vous paraitrons évidentes, d’autres moins et c’est bien normal.

Dans de prochains articles nous regarderons de plus prêt certaines d’entres elles.

+
-
*
/
rand 
sin 
cos 
tan 
asin 
acos 
atan 
sqr 
PI 
max 
min 
sum 
mean 
abs 
floor 
ceil 
->deg 
->rad 
sleep 
halt 
return 
reset 
exit 
raise 
->int 
->str 
->num 
->key 
->name 
->list 
->date 
->time 
->rec 
->bytes 
eval 
type 
tron 
troff 
clr 
dup 
drop 
over 
rot 
swap 
pick 
depth 
lsto 
lsto+ 
lsto- 
lsto* 
lsto/ 
sto 
sto+ 
sto- 
sto* 
sto/ 
rcl 
purge 
vars 
save 
load 
< 
<= 
> 
>= 
== 
!= 
isnull 
->not 
and 
or 
IF 
IFELSE 
REPEAT 
FOR 
FORSTEP 
WHILE 
FOREACH 
DURING 
break 
print 
input 
trap 
error 
clrError 
debug 
clrDebug 
TRYCATCH 
size 
get 
put 
rem 
keys 
last 
first 
sub 
copy 
now 
day 
month 
year 
hour 
minute 
second 
time 
days 
hours 
minutes 
seconds 
AFTER 
EVERY 
DI 
EI 
timer.purge 
timer.suspend 
timer.resume 
TASK
task.wait
task.run 
task.retval 
task.running 
task.purge 
ONEVENT 
event.purge 
event.raise 
&
| 
^ 
>> 
<<

Conclusion

Voici une première présentation de MOGWAI. Prochainement je vous présenterai MOGWAI Studio et je vous détaillerai d’autres éléments du langage tels que :

  • Les enregistrements
  • La gestion des variables locales et globales
  • La récursivité
  • Les expressions for, while et foreach
  • L’affichage d’un message
  • La saisie d’une valeur

Alors à très bientôt pour la suite de la présentation de MOGWAI et n’hésitez pas à poster des commentaires si le sujet vous intéresse.

Stéphane SIBUE CTO GUYZMO

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *