Introduction
La théorie des automates fournit les outils de formalisation des machines de calcul et trouve de nombreuses applications, notamment en théorie des langages. Enseignants, chercheurs et étudiants du domaine doivent fréquemment manipuler ces objets dont l’écriture mathématique est pour le moins fastidieuse. Il est plus facile, à des fins de compréhension et de communication de représenter les automates sous forme de dessins. Si un tel dessin présente toutes les informations nécessaires à quiconque le regarde, il ne présente pas le même formalisme qu’une définition précise de l’objet.
Un logiciel pourrait réconcilier ces deux représentations - l’une mathématique, l’autre graphique - en proposant de dessiner un automate à partir d’une description, ou en produisant une description à partir d’un dessin.
Nous avons entrepris la réalisation d’un tel logiciel à la demande et sous la supervision de M. Nicart, enseignant-chercheur à l’université de Rouen. L’objectif est de définir un langage capable de décrire un automate, puis de fournir un compilateur sur ce langage à même de produire le dessin correspondant.
Des solutions logicielles traitant des automates existent déjà, mais ce projet fait un usage exclusif des technologies Web. Proposer ces fonctionnalités directement dans le navigateur offre la garantie d’un logiciel portable, que le développeur peut aisément déployer et dont l’utilisateur peut facilement avoir accés. L’application tient d’ailleurs son nom du format de dessin vectoriel SVG utilisable directement dans une page Web.
Enfin, le département informatique de l’université de Rouen est interessé par un retour d’expérience afin d’évaluer la maturité des technologies Web pour ce genre d’entreprise. Ce projet sert alors également de preuve de concept.
Cahier des charges
Avant de débuter le développement, nous nous sommes rencontrés avec M. Nicart pour définir les fonctionnalités du logiciel. Le cahier des charges est donc un effort conjoint des développeurs et du client. Les premiers ont proposé de nombreuses fonctionnalités que le client a ensuite évalué pour leur assigner chacune une priorité.
1. Deux cas d’utilisation majeurs
Initialement, le projet consistait seulement à définir une représentation textuelle d’automate et à dessiner un automate en SVG à partir de cette dernière.
Après échange avec le client, il s’avère aussi intéressant de pouvoir manipuler la représentation graphique et d’en obtenir une représentation textuelle équivalente.
Essentiellement, l’application se décompose en deux cas d’utilisation majeurs : d’une part produire un dessin d’automate à partir d’une description textuelle, et d’autre part manipuler un dessin d’automate pour finalement le sauvegarder sous format textuel.
2. Rappel sur les automates
Un automate est définit par un quintuplet \((\Sigma, Q, I, F, \delta)\).
\(\Sigma\) est l’alphabet de l’automate. On appelle ses éléments des lettres ou des symboles.
\(Q\) est l’ensemble des états de l’automate. \(I\) et \(F\), sous-parties de \(Q\), sont les ensemble des états respectivement initiaux et terminaux.
\(\delta\) est l’ensemble des transitions. Une transition est définie par un triplet \((p, a, q)\) où \(p\) est dit état de départ ou état source de la transition, $a$ est dit lettre ou symbole de la transition, et \(q\) est dit état d’arrivée ou état cible de la transition.
Dans ce document, on parlera d’entité ou d’objet de l’automate pour désigner invariablement les états, symboles ou transitions.
3. Fonctionnalités détaillées
Ci-dessous, la liste des fonctionnalités par ordre de priorité :
-
Définition d’un langage de description d’automate, qui doit :
-
être saisissable dans un champ de texte sur la page Web
-
être intuitif
-
être concis - l’utilisateur doit pouvoir produire rapidement des automates
-
permettre de contrôler le rendu graphique, c’est-à-dire :
-
le positionnement des états et transitions
-
quelques options de style
-
-
-
Dessin d’automate à partir de la description, qui doit :
-
être affiché dans la page Web via une balise SVG
-
correspondre esthétiquement aux dessins d’automates habituels, c’est-à-dire
-
les états sont représentés par leur étiquette entourée d’une ellipse ; l’ellipse est un cercle si l’étiquette est courte
-
les états initaux sont identifiés par une petite flêche arrêtée au contour pointant vers le centre de l’ellipse
-
le contour des états terminaux est doublé
-
les transitions qui partagent le même état de départ et le même état d’arrivée par le même arc
-
les étiquettes des arcs (lettres des transitions) sont lisibles - elles ne sont pas rendues par dessus l’arc mais à côté, sans pour autant trop s’en éloigner
-
-
être satisfaisant sans nécessiter un positionnement manuel des différents composants, ce qui signifie notamment :
-
des états suffisamment espacés
-
le moins de croisement d’arcs possibles
-
une orientation "dans le sens" des transitions
-
-
-
Interface graphique associée au dessin, qui doit :
-
permettre le glisser-déposer des éléments du dessin pour les placer manuellement
-
permettre l’édition de la structure de l’automate
-
produire à la demande une description textuelle de l’automate dans le champ de saisi
-
optionellement, cette description textuelle doit être la plus proche possible de celle saisie par l’utilisateur
-
-
-
Fonction d’export, c’est-à-dire :
-
enregistrer la définition de l’automate dans un fichier sur la machine
-
enregistrer le code SVG généré dans un fichier sur la machine
-
-
Quelques opérations sur les automates
-
Architecture facilitant l’ajout de nouveaux types d’automates
Architecture
L’application se décompose naturellement en trois modules : le compilateur, le modèle et le dessinateur.
Le compilateur est chargé de fournir une instance d’automate à partir d’une description textuelle dans un langage que nous définirons. Le compilateur doit également être à même de décompiler, c’est-à-dire produire une représentation textuelle à partir d’une instance d’automate ; cette fonctionnalité n’a toutefois pas été implémentée.
Le modèle permet d’instancier un automate à partir d’un objet litéral Javascript (un enregistrement). Ce dernier doit répondre à un protocole que nous définirons. De plus, le modèle expose une API pour manipuler les instances d’automate. Il est alors possible de changer leur état de façon sûre.
Le dessinateur instancie une vue et un contrôleur et les lie à une instance d’automate. La vue dessine l’automate à l’aide d’une balise SVG tandis que le contrôleur manipule l’automate via l’API en réponse à certains évenements du DOM.
4. Compilateur
Le compilateur permet d’obtenir un automate à partir d’un langage d’entrée : CAL, pour Concise Automaton Language
La compilation se décompose en 3 étapes. Elles sont toutes mises en relation par un module appelé le superviseur.
4.1. Parseur
Le parseur est la partie du compilateur qui s’occupe de l’analyse lexicale et syntaxique de la représentation textuelle dans le langage CAL. Il retourne ce que nous appelons un descripteur, c’est-à-dire une structure de données formée des informations contenues dans la représentation textuelle.
Simultanément, le parseur produit et renvoit un second descripteur symétrique au premier. Au lieu de contenir les données associées aux tokens lus par le parseur, ce second descripteur fournit la position de ces tokens.
La création du parseur s’est faite à partir de sa grammaire à l’aide de PEGjs, une bibliothèque Javascript semblable à Lex/Bison. La syntaxe employée par PEGjs pour définir une grammaire est très proche de celle abordée en théorie des langages, l'extended Backus-Naur form.
A chaque régle de la grammaire peut être associé un bloc de code Javascript permettant ainsi de créer le descripteur sous la forme voulue.
Si une erreur syntaxique survient, le parseur la renvoit au lieu du descripteur. Cette erreur est capturée par le compilateur qui la renvoit à son tour.
4.2. Inférence
L’inférence est l’étape consistant compléter les informations du descripteur. Des états et des symboles sont alors créés à partir des informations présents dans les transitions. Cette étape est réalisé par le superviseur. Le descripteur ainsi complété est alors passé au modèle.
4.3. Appel au modèle
L’appel au modèle est l’étape qui consiste à passer du descripteur à un automate. Le constructeur d’automate exposé par le modèle prend comme paramétre un descripteur. Si celui-ci est bien spécifié, une instance est renvoyée. En cas d’erreur, le constructeur renvoit une exception. Le superviseur va associer cette exception à la position du token ayant causé l’erreur grâce au second descripteur produit par le parseur. Il est ainsi possible de localiser les erreurs sémantiques aussi bien que les erreurs syntaxiques.
4.4. Protocole
Plusieurs raisons nous aménent à utiliser un descripteur plutôt que de directement appeler les fonctions du modèle dans le parseur.
D’une part, cela assouplit la relation entre le langage et le modèle. On peut tout à fait changer la syntaxe de CAL ; en effet la présence d’un protocole assure que le modèle est capable de prendre en compte n’importe quel descripteur le respectant.
De plus, le code présent dans la grammaire reste aussi simple que possible. Il n’est dédié qu’à une tâche : la création du descripteur. Auncun code interagissant avec l’exterieur n’est présent, facilitant ainsi la lecture et la compréhension.
5. Modèle
Le modèle permet d’instancier et de manipuler les automates de façon sûre. Nous définissons un protocole pour instancier les automates ainsi qu’une API séparée en deux niveaux : l’une manipule l’automate en temps qu’objet mathématique tandis que l’autre prend en compte les données liées à sa représentation graphique.
5.1. Fsm
Le modèle expose une classe dénommée Fsm
(pour finite state machine).
Une instance de Fsm
représente un automate au sens mathématique.
5.1.1. Trois classes internes
La classe Fsm
n’expose aucune méthode. Elle offre seulement trois
attributs non mutables appelés symbols
, states
et transitions
qui
correspondent respectivement aux lettres, états et transitions de l’automate.
Ces attributs sont en réalité des classes. Elles exposent des méthodes statiques pour créer, obtenir et supprimer les objets correspondant, mais aucune méthode d’instance.
Ce patron de conception permet de simuler des espace de nommage
pour les méthodes. Par exemple, pour ajouter un état sur un Fsm
nommer
automaton
, on appelle la méthode automaton.states.add()
. En revanche,
si on veut ajouter une lettre, on appelle automaton.symbols.add()
.
5.1.2. Immutabilité des objets
Techniquement, les objets de l’automate sont mutables. On peut modifier à loisir les propriétés des lettres, états et transitions. Toutefois, le modèle dissimule les données qui lui sont utiles, si bien qu’il est impossible de changer par exemple les états pointées par une transition ou le fait qu’un état soit final. La seule solution est de supprimer l’objet puis de le recréer comme souhaité.
Pour obtenir des données spécifiques aux instances, les classes exposent
des getters Javascript. Ces getters sont des attributs qui à l’accès
éxecutent une méthode au lieu de renvoyer la valeur de l’attribut. Cela permet
de créer des champs non mutables depuis l’API, mais mutables pour le modèle.
Par exemple, pour savoir si un état etat
est initial, on appelle etat.initial
qui renvoit un booléen. Contrairement aux propriétés d’objet,
etat.initial = true
renverra une erreur.
Tous les objets de l’automate disposent d’un getter appelé struct
qui
renvoit l’automate en question.
5.1.3. Des mixins pour étendre les classes internes
Il est possible de passer au constructeur des mixins pour chacune des classes internes.
Une mixin est une fonction qui prend une classe comme paramètre et renvoit une autre classe. Les mixins ne devraient pas modifier la sémantique des superclasses pour ne pas invalider la spécification de l’API.
5.2. SketchFsm
Le modèle expose un seconde classe SketchFsm
qui hérite de Fsm
.
Son rôle et d’ajouter à Fsm
les méthodes nécessaires pour spécifier
ou obtenir des attributs graphiques comme la position ou la dimension
des entités.
SketchFsm
expose également une méthode pour calculer automatiquement
la position des entités.
5.2.1. "Héritage" via mixins
SketchFsm
n’est techniquement pas une sous-classe de Fsm
. Elle compose
Fsm
mais passe ses propres mixins à l’instanciation. Ce sont donc les
classes internes qui sont dérivées. Ces classes sont à leur tour exposées
de la même façon que Fsm
via les attributs de même nom.
L’API proposée par SketchFsm
est alors entièrement compatible avec celle de Fsm
.
5.2.2. Une quatrième entité : les arcs
Un dessin d’automate présente explicitement les états puisqu’ils sont chacun dessinés par une ellipse (ou un cercle). L’alphabet est quant à lui implicite puisqu’il n’apparait que via les transitions. Alphabet et symbole ne posent donc aucun problème de rendu.
La difficulté provient des transitions ; elles sont bien dessinées, mais n’ont pas toujours d’existence propre. En effet, si plusieurs transitions ne diffèrent que du symbole qu’elles portent, elles seront représentées par le même arc.
Comme SketchFsm
a pour object d’encapsuler les attributs graphiques des
entités, il est nécessaire d’en créer une nouvelle pour représenter les arcs.
Ces arcs dépendent des transitions. Un attribut edges
s’ajoute aussi
aux trois autres déjà définis par Fsm
.
5.2.3. Algorithme de positionnement des entités
SketchFsm
expose également une méthode layout()
. Cette méthode est chargée
de calculer une position par défaut pour les états, les arcs et leurs étiquettes.
Les méthodes d’instance des états et arcs permettant d’obtenir les positions
ne renvoient les positions calculées que si aucune position n’a été définie
manuellement. Cela permet une définition partielle des positions par l’utilisateur.
Toutefois, l’algorithme de positionnement ne prend pas en compte ces nouvelles
positions, ce qui peut conduire à des dessins peu satisfaisants.
Le placement automatique des objets d’un automate dans le plan se ramène au problème anologue pour les graphes orientés pondérés. Il s’agit toutefois d’un problème NP-complet ; seules des heuristiques parfois complexes permettent d’obtenir un resultant satisfaisant.
L’algorithme utilisé est inspiré des travaux de Gansner et. al [GKNV], eux-mêmes issus de ceux de Sugiyama [STT]. Hors, l’algoritihme utlisé par GraphViz - logiciel de dessin de graphes - est également inspiré do celui de Sugiyama. Ces algorithmes permettent de placer dans le plan les noeuds d’un graphe connexe. Nous utilisons directement l’implémentation de la bibliothèque dagre.
Le principe est simple : les étiquettes des arcs sont transformées en noeuds le temps de l’algorithme. Si des cycles sont présents dans le graphe, l’orientation de certains arcs sont inversés pour les briser. L’orientation d’origine est rétablie à la fin de l’éxécuction. Les noeuds sont ensuite placés dans une grille abstraite définie par un repère orthogonal. Une première étape appelée ranking répartit les noeuds dans les colonnes. La seconde étape appelée ordering ordonne les noeuds en ligne de façon à minimiser les croisement d’arc. Ces deux étapes reposent toutes deux sur heuristiques éprouvées. Enfin, la grille abstraite est remplacée par le système de coordonnées désiré (ici, une matrice de pixels de taille arbitraire)
6. Dessin
Le troisième composant majeur de l’application est le dessinateur.
Il est chargé de communiquer avec une instance de SketchFsm
pour
le représenter ou mettre à jour les attributs graphiques de ses entités.
Nous faisons ici usage de la bibliothèque D3 qui facilite la création et la manipulation d’éléments SVG.
La fonction de dessin appelée lors de la génération de l’automate est décomposée en quatres étapes.
6.1. Mesure
La première étape consiste à mettre à jour les attributs graphiques du modèle qui ne peuvent être déterminés qu’au moment du rendu. Ces attributs sont notamment la longeur et la largeur des étiquettes des états et arcs. En effet, le placement des entités dépendra de leur dimension ; leur dimension dépend à leur tour de la place qu’occupera le texte de l’étiquette. Comme les polices peuvent changer d’un client à l’autre, cette étape s’avère nécessaire.
La mesure s’effectue comme suit. D’abord, l’élément SVG est instancié puis injecté dans la page. Il sera conservé tout au long de la durée de vie du modèle.
Ensuite, les étiquettes sont ajoutées à l’élément SVG puis rendues.
Les dimensions des boîtes de contour sont alors calculées via la
primitive getBBox()
. Pour les étiquettes d’arcs trop larges, des sauts
de ligne sont manuellement insérés jusqu’à ce que la boîte de contour atteigne
une dimension satisfaisante.
Les dimensions calculées sont ensuite remises au modèle via les méthodes
d’instance height()
et width()
des entités concernées.
6.2. Positionnement
Cette étape consiste seulement à appeler la méthode layout()
du modèle.
Celle-ci appelle l’algorithme de positionnement automatique des entités.
Il fait naturellement usage des dimensions mesurées précédemment.
6.3. Rendu
Avant d’insérer les éléments de rendu, un marqueur triangulaire est ajouté
à l’élément SVG. Ce marqueur habite la balise defs
. Il sera référencé par
les chemins qui devront porter une flêche à leur extrémité.
La fonction itère ensuite sur les entités de l’automate et
insère dans l’élément SVG un groupe g
pour chacune d’entre elles.
Les éléments de rendu occupent le groupe approprié.
Les états sont représentés par des éléments text
pour les étiquettes et
des éléments ellipse
pour les contours. Les états terminaux disposent de
deux contours tandis que de petits lignes droites fléchées identifient les
états initiaux. L’identifiant de l’état dans la représentation textuelle
est préfixée de state_
pour obtenir l’attribut id
de l’élément g
.
Cette élément est par ailleurs doté de la classe state
.
Les arcs sont représentés par des éléments text
pour les étiquettes et
des éléments path
pour les branches. La position du marqueur triangulaire
sur le chemin est préalablement calculée. L’attribut id
de l’élément g
d’un arc est la chaîne de caractère edge_
suivi de l’identifiant de l’état
de départ, un tiret bas puis l’identifiant de l’état d’arrivée. L’élément
g
possède également la classe edge
.
6.4. Écoute
Enfin, on enregistre auprès des groupes des écouteurs. Ceux-ci peuvent modifier le modèle en réaction à des évenements levés par le DOM. Bien que l’application livrée n’en fasse pas grand usage, il est possible de repositionner les états par glisser-déposer. Le modèle est altéré en conséquence.
Il n’est pas nécessaire pour l’heure d’enregistrer des écouteurs auprès du modèle.
L’élément SVG est la seule vue susceptible de modifier l’instance de SketchFsm
en temps réel. Une modification de la représentation textuelle améne une
nouvelle exécution de la fonction de dessin. A terme, SketchFsm
devrait
exposer des méthodes pour écouter le modèle.
Organisation
Ce projet se déroule par équipe de 4 personnes. Il a donc fallu opter pour une organisation orientée vers la collaboration.
7. Outils
Quatre outils majeurs ont facilité la communication au sein de l’équipe et la modularité du code. Ils sont les suivants.
- npm
-
Le node package manager est un outil qui permet d’installer différents paquetages Javascript. La gestion des dépendances est automatique et permet donc un dépoiement facilité. Par conséquent, tous les membres de l’équipe disposaient du même environnement de développement.
- Brunch
-
Brunch prépare des sources déployables à partir des sources éditées. Cet un moteur de production adapté aux applications Web clients. Les sources Javascript sont enrobées d’une fonction anonyme puis concaténées dans un seul fichier cible. Le code est ainsi modularisé, donc aisément réutilisable, tout en étant facile à déployer. De plus, Brunch orchestre l’exécution de différents linters et transpileurs. Nous avons ainsi pu utiliser le dernier standard Javascript, ES2015, puisque les sources étaient ensuite traduites dans une version compatible avec le plus grand nombre de navigateurs.
- Git
-
Git est un système de gestion de version. Ses branches faciles d’emploi nous ont permis de travailler simultanément sur la même base de code sans altérer le travail des autres. Partager ce code en ligne fût très facile grâce à Github, un service d’hebergement publique de dépots Git.
- Google Hangouts
-
Google Hangouts est un logiciel de voix sur IP disposant d’une fonctionnalité de partage d’écran. Celle-ci nous a permis d’étendre nos plages horaires de travail car nous pouvions collaborer en direct indépendamment des horaires d’ouverture de la faculté. Partager son code via cette fonctionnalité est aussi un avantage lors du débogage car il permet d’apporter très rapidement un oeil neuf sur notre code.
8. Equipes
L’architecture du projet a très vite amené à une séparation en sous-équipes de 2 personnes. La première composée d’Achraf et de Thibaut était en charge du parseur et de la gestion des erreurs. La seconde équipe composée de Yacine et de Tom s’occupait du modèle et du dessin. Cette séparation a pu s’opérer très facilement. D’une part grâce à nos outils ; d’autre part grâce au protocole qui fût défini en amont par l’èquipe entière.
9. Rencontres
Dès le début du projet, nous avons planifié des réunions hebdomadiares. Ces rencontes permettaient de faire le bilan de la semaine passée. Nous fixions ensuite les objectifs de la semaine suivante selon ce qui avait déjà été fait. C’était aussi l’occasion de réfléchir ensemble à des choix concernant l’ensemble de l’application tel que le protocole. Enfin cela permettait de préparer la réunion avec notre tuteur, M. Nicart. Celle-ci nous donnait l’occasion de faire un retour sur notre avancement et d’obtenir l’avis du tuteur sur les points que nous allions aborder. C’était aussi un moyen de réviser les objectifs et de se concentrer sur l’essentiel. Nous avions aussi mis en place l’application sur un site accessible facilement afin d’obtenir un suivi optimal.
Bilan
Outre l’application commandée, il nous a été demandé de faire le point sur les technologies Web employées.
De plus, après 4 mois de développement, il est utile de faire le bilan de notre progression, aussi bien du point de vue technique qu’organisationnel.
10. Bilan technique
Certaines fonctionnalités n’ont pas été implémentées. Il a en en effet fallu prioriser les fonctionnalités et nous nous sommes focalisés sur le cas d’utilisation principal. L’application ne propose donc pas d’édition graphique. Cela aurait nécessiter un effort trop conséquent pour aboutir à un résultat satisfaisant. De plus, le modèle présente certains défauts d’implémentation qu’il aurait d’abord fallu corriger. Il est cependant déjà possible de modifier la position des états par glisser-déposer.
Le changement de la vue texte à partir du modèle n’a pas été implémenté non plus. Cela est du au fait que, sans édition graphique, aucun changement ne peut subvenir dans le modèle. Cette fonctionnalité n’aurait donc jamais servi.
Les fonctionnalités manquantes comme l’éxécution d’algorithmes sur les automates ou l’export dans d’autres formats s’accomodent toutefois très bien de l’architecture. Elles devraient être faciles à implémenter.
11. Retour d’expérience
Notre tuteur était particulièrement intéressé par notre expérience avec les technologies Web. Voici nos conclusions.
11.1. Un ecosystème riche
L’utilisation des technologies Web durant ce projet a donné accés à un écosystème très riche. C’est de cet écosystème que sont issues les principales dépendances du projet : D3, PEGjs et Dagre. Toutefois cette richesse peut s’avérer problématique. Il est parfois difficile de faire un choix puisqu’il évaluer les solutions disponibles.
Bien que les technologies Web côté client évoluent relativement lentement, les outils à disposition des développeurs sont nombreux et matures. L’environnement de développement que nous avons pu mettre en place en témoigne.
11.2. Un langage parfois handicapant
Le langage Javascript présente certains avantages. La manipulation aisée des fonctions permet de changer le comportements des objets durant l’éxécution. Cela facilite l’utilisation de techniques de programmation avancée tels que les mixins, le patron de conception de fabrique abstraite ou la métaprogrammation.
Malgrè cela, le Javascript souffre de certains inconvénients, notamment l’absence de typage. En conséquence, une partie conséquente du code est consacrée au travail de vérification afin de ne pas fragiliser l’application. Le code est aussi moins lisible. De plus, aucun compilateur ne peut venir en aide au programmeur pour prévenir certaines erreurs triviales. Sans une batterie de tests automatisés (qu’il faut également programmer), les bugs sont fréquents et viennent impacter la productivité.
Il est difficile et/ou peu efficace d’isoler des variables en Javascript car le langage ne propose pas d’attributs privés. Cela a rendu l’implémentation du modèle plus difficile ; la lisibilité du code en a donc été impactée.
Lors de l’implémentation des fonctions de dessin, nous aurions aimer pouvoir définir des fonction en notation infixe. Les portions du code relatives à la manipulation de vecteurs auraient été bien plus lisibles.
Le fait que la dernière version de Javascript ne soit pas intégralement implémentée par tous les navigateurs peut nuire à la compatibilité. Transpilateurs et polyfills mitigent ce problème. Naturellement, nous ne pouvons espérer des performances similaires à des langages compilés.
11.3. Une interface souple
Pour la programmation d’interfaces graphiques en dehors des applications Web, les développeurs doivent jongler entre des API très haut niveau et des API plutôt bas niveau. D’un côté, les API haut niveau permettent la création rapide de composants, mais ceux-ci présentent des formes et des comportement rigides. De l’autre côté, les API bas niveau n’ont de limite que l’imagination du développeur, mais au prix d’un effort de programmation très conséquent.
Malgré ses défauts, l’environnement graphique du navigateur nous semble atteindre le parfait compromis. Grâce au DOM et au CSS, il est facile de concevoir une vue, de la styliser et d’y enregistrer des écouteurs. Nous ne sommes toutefois pas cantonnés à des composants limités. Les boîtes à outils GTK3 et Qt5 tentent d’ailleurs de s’inspirer de ces technologies Web pour proposer les mêmes avantages.
12. Bilan humain
Nous avons remis en question nos choix pour l’application pendant trop longtemps. Il aurait été plus sage de commencer à développer plus tôt pour s’apercevoir des éventuelles problèmes à l’utilisation. Cela nous a d’ailleurs empéché de faire un véritable déploiement continu, puisque nous ne codions pas toujours, nous n’avions aucune nouvelle fonctionnalité à intégrer. Il aurait été bien de faire plus de tests pour faciliter l’intégration mais aussi compenser les faiblesses de Javascript. Cela aurait été aussi bénéfique pour définir notre API. En effet, les besoins seraient apparus plus rapidement.
13. Perspectives
La réalisation de ce projet laisse entrevoir différentes améliorations possibles.
Finaliser l’édition graphique requiert de revisiter la relation entre le modèle et l’algorithme de calcul des positions. Ils nous semblent pour le moment trop couplés, bien que des progrès ait déjà été effectués en ce sens. A terme, on souhaiterait pouvoir spécifier dynamiquement l’algorithme de positonnement automatique.
Après avoir utilisé le langage CAL sur nos exemples, nous nous sommes rendus compte qu’il est possible de le rendre plus concis encore. En effet, il est naturel de spécifier ensemble des transitions qui partagent les mêmes états de départ et d’arrivée.
Cela réglerait également le problème de positionnement des arcs, moyennant la définition de règles de précédence entre les transitions partageant le même arc.
La partie de dessin gagnerait également à définir des classes pour chaque type d’entité à dessiner. Ces composants seraient alors responsables de leur propre rendu et d’enregister leur écouteurs auprès du modèle. Pour l’heure, les fonctions de dessin sont codées dans un style très procédural.
Enfin, si le projet était à refaire, nous nous orienterons probablement vers un langage fonctionnel fortement typé. Un certain nombre propose des transpilateurs Javascript, comme PureScript, Elm ou Haskell.
Conclusion
L’issue de ce projet est une application Web côté client capable de dessiner un automate en SVG à partir d’une représentation textuelle. Cette représentation textuelle est défini à l’aide d’un nouveau langage appelé CAL, pour Concise Automaton Language.
Ce projet peut servir de point de départ pour une application plus poussée intégrant par exemple l’édition graphique. Cette expérience nous a été très profitable, tant au niveau technique qu’au niveau organisationnel. En effet nous avons pu employer nous exercer à des méthodes et à des outils de collaboration, comme Git. Non seulement le sujet porte sur la représentation d’objets issus de la théorie des langages, mais en plus nous avons fait usage de cette même théorie via la définition du langage CAL et l’implémentation de son compilateur. Nos connaissances dans le domaine ont donc été bien consolidées.
Nous retiendrons de nos succès et de nos difficultés de nombreux enseignements. D’une part, nous avons noté l’importance d’une bonne communication, qu’elle soit entre les membres de l’équipe, avec le tuteur ou avec le jury. D’autre part, la planification sérieuse apparait comme une nécessité. Le fait de fixer des objetifs modeste dans des délais courts nous aurait permis de conserver une motivation constante tout au long du projet.
Appendix A: Grammaire de CAL
Les diagrammes syntaxiques définissant la grammaire du langage CAL sont présentés dans cette annexe.