Le but de ce TP est d'introduire la notion de parser
en ANTLR4.
Un parser représente la partie d'un compilateur qui reconnaît des jetons (tokens) ordonnés et spécifiés par un ensemble de règles d'analyse. Cet ensemble de règles décrit un “langage” source reçu par le parser sous la forme d'instructions séquentielles du programme source, des entrées de la ligne de commande, de balises ou d'autres.
L'analyseur généré fait correspondre les jetons d'entrée (tokens définis par le lexer) aux règles, émettant un arbre de syntaxe abstraite (AST), dont on va discuter plus dans les TPs suivants. Cet arbre représente la sortie de l'analyseur.
La règle la plus simple est juste un nom de règle suivi d'une seule alternative terminée par un point-virgule, comme on l'a vu dans le TP précédent:
grammar Alf; start:;
Lorsqu'une règle se compose de plusieurs aternatives, on utilise le séparateur |
pour les différencier:
grammar Alf; start: expression ';' ; expression: addition | subtraction | multiplication | division ;
Les actions représentent des blocs de texte écrits dans le langage de programmation cible et délimités par des accolades {}
. Elles sont déclenchées selon leur position dans la grammaire.
grammar Alf; declaration: type VARIABLE ';' { System.out.println("This is a declaration statement!"); } ; type : 'String ' | 'number ' | 'boolean ' ; VARIABLE: ([a-zA-Z]+);
Dans la plupart des cas, les actions sont utilisées pour accéder aux attributs des tokens et aux références des regles qu'on a définies. Par exemple, dans la grammaire suivant, on va afficher pour chaque type d'expression la valeur numérique des opérants et le token pour l'opérateur:
grammar Alf; start: expression ; expression: left=expression op=MUL right=expression {if(!$left.text.isEmpty() && !$op.text.isEmpty() && !$right.text.isEmpty()) System.out.println($left.text + " " + $op.text + " " + $right.text + " ");} | left=expression op=DIV right=expression {if(!$left.text.isEmpty() && !$op.text.isEmpty() && !$right.text.isEmpty()) System.out.println($left.text + " " + $op.text + " " + $right.text + " ");} | left=expression op=PLUS right=expression {if(!$left.text.isEmpty() && !$op.text.isEmpty() && !$right.text.isEmpty()) System.out.println($left.text + " " + $op.text + " " + $right.text + " ");} | left=expression op=MINUS right=expression {if(!$left.text.isEmpty() && !$op.text.isEmpty() && !$right.text.isEmpty()) System.out.println($left.text + " " + $op.text + " " + $right.text + " ");} | INT ; WS : ([' ']+) -> skip; /* Skip whitespaces */ NEWLINE : ([\r\n]+); INT : ([0-9]+) ; MUL : ('*'); DIV : ('/'); PLUS : ('+'); MINUS : ('-');
Tous les jetons (tokens) incluent un ensemble d'attributs prédéfinis, read-only, qui décrivent des propriétés utiles pour les tokens et auxquels les actions peuvent accéder a travers l'instruction $label.attribute
, où label représente le nom du jeton/de la règle et attribute le nom de l'attribut.
getText()
. Exemple: $ID.text.getType()
. Exemple: $ID.type. getLine()
. Exemple: $ID.line.togetCharPositionInLine()
. Example: $ID.pos.getTokenIndex()
. Example: $ID.index.Integer.valueOf(text-of-token)
. Example: $INT.int.
Après avoir décrit une grammaire dans un fichier .g4
avec des jetons et des règles, il faut faire clic-droit sur le fichier et appuyer sur la commande Generate ANTLR Recognizer. (Pour configurer les fichiers, voir TP3)
Cette commande va générer les fichiers .java
correspondants, dont on va se servir pour diviser un texte en jetons et pour l'analyser a l'aide du parser.
Un exemple d'utilisation du parser dans un fichier Main.java
:
// Import generated files import com.Alf.parser.AlfLexer; import com.Alf.parser.AlfParser; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; public class Main { public static void main(String[] args) { String input = "1+2*4/5"; AlfLexer lexer = new AlfLexer(CharStreams.fromString(input)); AlfParser parser = new AlfParser(new CommonTokenStream(lexer)); // Parse the input, where `start` is whatever entry point you defined AlfParser.StartContext tree = parser.start(); } }
Pour la visualisation de l'arbre d'analyse, on utilise la fontion du plug-in ANTLRv4 ANTLR Preview
. Clic-drot sur la premiere regle definie dans la grammaire et ecrivez en gauche le texte qu'on doit analyser. En droit, on peut voir l'arbre genere. Pour plus d'informations, voir TP3.
Pour chaque exercise, faites une capture d'ecran de l'arbre d'analyse et mettez-le dans le directoire ParseTree
qui se trouve dans le Github repository.
int _var1 = 2; float _var2 = 5.55; String _var3 = "alf";
Suivez les étapes nécessaires pour visualiser l'arbre d'analyse et testez chaque règle à l'aide du fichier texte pour les données.
2+5/10-7
. (3p)_var1
. Vérifiez la correctitude de la grammaire en ajoutant l'expression 15/_var1-5;
au fichier texte pour les données.(1p)(2+5)/3
au fichier texte pour les données. (2p);
, mais aussi par des lignes vides. Modifiez le contenu du fichier texte pour les données en ajoutant les expressions suivantes et visualisez l'arbre d'analyse: (2p) String _var1 = "alf"; (2+7)/5*3;
int _var1 = 2 + 3 * 5;