Introduction à Xtext
L'ingénierie des modèles est une nouvelle façon de définir des
langages, des DSL. Dans l'environnement EMF, cela se fait via la
définition d'un méta-modèle en Ecore. Ensuite, on peut éditer des
modèles écrits dans ce DSL via l'éditeur générique de modèles XMI ou
les manipuler de manière programmatique avec du code Java ou ATL comme
cela a été fait dans les TP précédents.
L'éditeur par défaut d'EMF fonctionne bien mais n'est pas pratique
pour définir des modèles complexes. Vous avez pu le constater par
exemple avec les diagrammes de classes du TP précédent où le contenu
des modèles XMI est difficile à étudier et à modifier. Dans ce TP,
nous allons voir comment définir une syntaxe dédiée à un DSL créé via
un méta-modèle Ecore afin de faciliter l'édition de modèles. Dans le
monde EMF, il y a deux outils de référence pour cela :
- Xtext pour définir
des syntaxes textuelles
- Sirius pour définir
des syntaxes graphiques
Dans ce TP, nous allons voir comment créer un éditeur textuel avec
Xtext. Pour cela, nous allons repartir du DSL de processus défini dans
le TP 1. Pour reprendre l'exemple donné en
début de ce TP, le but est d'avoir un éditeur textuel qui affiche
ceci avec de la coloration syntaxique et de la complétion :
Processus {
activites {
Activite Intro_Generale {
suivante Cours_Meta
},
Activite Cours_Meta {
suivante TP_Meta
precedente Intro_Generale
},
Activite TP_Meta {
suivante TP_Kermeta
precedente Cours_Meta
},
Activite TP_Kermeta {
precedente TP_Kermeta
}
}
// on définit le début et la fin en respectant les invariants de cohérence en OCL
debut Intro_Generale
fin TP_Kermeta
}
Modification du méta-modèle du TP 1
Xtext a pour objectif de définir une grammaire EBNF et de faire le
lien avec un méta-modèle. Il y a deux façons de procéder : soit on
génère la grammaire à partir du méta-modèle Ecore, soit on génère le
méta-modèle à partir de la grammaire. Dans notre cas, le méta-modèle
Ecore existe, on va donc générer la grammaire Xtext qui ensuite
permettra d'éditer textuellement un modèle.
La grammaire Xtext va permettre de définir les éléments d'un
processus : la liste des activités, le début et la fin. Ces éléments
sont définis dans un ordre précis avec comme principe de référencer
des éléments définis préalablement. Ainsi, le début et la fin qui
référencent des activités sont définis après les activités. Dans notre
exemple, il y a une exception à cet ordre : dans une activité, on
référence la suivante qui sera définie plus bas dans le modèle (de
manière plus générale, les références suivantes et précédentes sont
indépendantes de l'ordre de définition de la liste des activités du
processus). Au niveau de l'éditeur, cela peut poser des problèmes
d'avoir ce type de référence croisée. Xtext propose une solution
techhnique pour référencer de n'importe où un élément : qu'il soit
identifié par un attribut intitulé "name" dans sa méta-classe.
Dans un premier temps, nous allons modifier notre méta-modèle pour
que les activités aient un attribut "name". La modification est simple
puisqu'il y a déjà un attribut "nom" qui joue le même rôle. En
pratique, il y a plusieurs étapes à effectuer :
- Dans le méta-modèle Ecore, modifiez l'attribut "nom" de la classe
"Activite" en "name".
- Modifiez au passage également le nsURI du package "LDP" pour y
mettre un adressage absolu comme "http://LDP/1.0".
- Editez le méta-modèle en version textuelle pour mettre à jour les
invariants OCL qui manipulaient l'attribut "nom" d'une activité.
- Il faut mettre à jour le fichier "genmodel" et regénérer le code
Java pour le méta-modèle : sélectionnez le fichier "LDP.genmodel" dans
l'arborescence puis clic droit -> Reload ... -> Next -> bouton Load
-> Next -> Finish. Ouvrez le fichier "LDP.genmodel", sélectionnez
le package "LDP" et via le menu contextuel, générez le code Java
(Generate Model Code).
- Suite à ces modifications, les modèles XMI existants ne sont plus
valides : il est possible de les éditer textuellement pour changer le
nsURI et l'attribut "nom" des activités. Le code Java de la classe
"LDPManipulation" doit aussi être modifié pour prendre en compte le
changement du nom d'attribut de la classe "Activite".
Génération de l'éditeur textuel
Génération de la grammaire Xtext
Plusieurs étapes sont nécessaires pour générer la grammaire Xtext à
partir du méta-modèle Ecore :
- Sélectionnez le nom du projet "tp1" dans l'arborescence
puis clic droit -> Configure -> Convert to Xtext Project.
- Via le menu principal d'Eclipse, créez un nouveau projet Xtext
: File -> New -> Other -> Xtext -> Xtext Project From Existing
Ecore Models. Cliquez sur Add... puis sélectionnez le
fichier "LDP.genmodel" du projet "tp1". Modifiez le champ "Entry
rule:" en bas pour qu'il contienne "Processus - LDP" : il faut
préciser à Xtext quel est l'élément racine du métamodèle, ici c'est le
processus. Cliquez sur Next et dans la fenêtre suivante, entrez
:
- Project name : xtext.ldp
- Language Name : xtext.Ldp
- Language Extensions : ldp
puis cliquez sur Finish. Ainsi, nous avons défini que
l'extension de nos fichiers contenant les modèles sera "ldp" au lieu
du "xmi" générique.
- A ce stade, Eclipse a créé plusieurs projets dont les noms
commencent par "xtext.ldp". Ouvrez celui qui s'appelle "xtext.ldp"
(tout court) et le fichier "Ldp.xtext" se trouvant dans le répertoire
"src/xtext". Vous devez avoir ceci, c'est la grammaire Xtext générée
automatiquement à partir du méta-modèle :
La grammaire fait le lien avec les classes du méta-modèle tout en
rajoutant des éléments de syntaxe pour définir textuellement les
instances de ces classes. Les élements de syntaxe sont écrits entre
quotes, par exemple 'Processus', 'activites' ou bien encore les
accolades ou une virgule. Une règle définit comment un élément du
méta-modèle est créé comme par exemple " ... returns Processus:" qui
instancie la méta-classe Processus. Ensuite, dans la règle, il est
précisé comment les attributs et les références de l'instance sont
positionnés. Pour Processus, on retrouve par exemple "activites" ou
"debut" qui font faire appel aux régles définies plus bas pour générer
ces éléments.
- Pour finaliser l'éditeur, sélectionnez le fichier
"GenerateLdp.mwe2" dans le même répertoire et clic droit -> Run as
-> MWE2 Workflow.
Edition d'un modèle
Pour lancer l'édition de l'éditeur, il faut lancer une nouvelle
instance d'Eclipse qui exécutera le projet Xtext :
- Sélectionnez le projet "xtext.ldp" dans l'arborescence
puis clic droit -> Run As -> Eclipse Application
- Une fois le nouveau Eclipse lancé, fermez la fenêtre "Welcome" et
créez un nouveau projet générique : File -> New -> Project ->
General -> Project. Pour information, le nouveau Eclipse se lance
en utilisant un workspace nommé "runtime-EclipseXtext".
- Dans le projet, créez un nouveau fichier que vous nommerez avec
l'extension "ldp". Cliquez sur "Yes" à la question sur la conversion
en un projet Xtext.
- Vous pouvez maintenant éditer votre modèle en respectant la
syntaxe Xtext. Les mots-clés du langage sont mis en gras et Bordeaux,
la complétion sur les noms d'activités fonctionne avec Ctrl+espace. Si
vous voulez utiliser des espaces dans les noms des activités, il faut
écrire les noms entre guillemets. Pendant l'édition, une croix rouge
vous indique une erreur de syntaxe et un triangle jaune un invariant
OCL qui n'est pas respecté. Pour l'exemple ci-dessus, le modèle doit
être le suivant :
.
- La syntaxe générée par défaut est un peu trop verbeuse notamment
avec la définition des débuts et fins où il y a des redondances ou des
mots-clés sans intérêt : fermez le nouveal Eclipse, revenez sur
l'Eclipse initial, modifiez la grammaire Xtext pour avoir une syntaxe
comme dans l'exemple donné en haut de la page, regénérez le workflow
MWE2 et relancez un Eclipse sur le projet Xtext pour appliquer vos
modifications.
Pour aller plus loin
Ce TP est une introduction à Xtext pour montrer la faisabilité de
la définition d'une syntaxe textuelle pour un DSL défini par un
méta-modèle Ecore. Les modèles définis pourront être ensuite utilisés
en entrée des transformations que vous aurez définies pour le
méta-modèle. Il est également possible de customiser l'éditeur pour
rajouter des règles d'édition ou générer des choses une fois le modèle
édité. Cela se fait soit en Java, soit en Xtend.
Vous pourrez trouver des informations sur cela dans le cours/TP de
Xtext que faisait M. Le
Goaer (avec des liens vers d'autres documentations)
: support
de cours
et sujet
de TP/tutoriel.