web-dev-qa-db-fra.com

ANTLR: Y a-t-il un exemple simple?

J'aimerais commencer à utiliser ANTLR, mais après avoir passé quelques heures à examiner les exemples du site antlr.org , je ne parviens toujours pas à comprendre clairement le processus de grammaire en Java.

Existe-t-il un exemple simple, par exemple un calculateur à quatre opérations implémenté avec ANTLR passant par la définition de l’analyseur jusqu’au code source Java?

211
Eli

Vous créez d'abord une grammaire. Vous trouverez ci-dessous une petite grammaire que vous pouvez utiliser pour évaluer des expressions construites à l'aide des 4 opérateurs mathématiques de base: +, -, * et /. Vous pouvez également regrouper des expressions à l'aide de parenthèses.

Notez que cette grammaire est très basique: elle ne gère pas les opérateurs unaires (le moins dans: -1 + 9) ni les décimales comme .99 (sans le premier chiffre), pour ne nommer que deux lacunes. Ceci est juste un exemple que vous pouvez travailler sur vous-même.

Voici le contenu du fichier de grammaire Exp.g:

grammar Exp;

/* This will be the entry point of our parser. */
eval
    :    additionExp
    ;

/* Addition and subtraction have the lowest precedence. */
additionExp
    :    multiplyExp 
         ( '+' multiplyExp 
         | '-' multiplyExp
         )* 
    ;

/* Multiplication and division have a higher precedence. */
multiplyExp
    :    atomExp
         ( '*' atomExp 
         | '/' atomExp
         )* 
    ;

/* An expression atom is the smallest part of an expression: a number. Or 
   when we encounter parenthesis, we're making a recursive call back to the
   rule 'additionExp'. As you can see, an 'atomExp' has the highest precedence. */
atomExp
    :    Number
    |    '(' additionExp ')'
    ;

/* A number: can be an integer value, or a decimal value */
Number
    :    ('0'..'9')+ ('.' ('0'..'9')+)?
    ;

/* We're going to ignore all white space characters */
WS  
    :   (' ' | '\t' | '\r'| '\n') {$channel=HIDDEN;}
    ;

(Les règles de l'analyseur commencent par une lettre minuscule et les règles de lexer par une majuscule)

Après avoir créé la grammaire, vous souhaiterez générer un analyseur et un lexer. Téléchargez le ANTLR jar et stockez-le dans le même répertoire que votre fichier de grammaire.

Exécutez la commande suivante sur votre invite de commande/Shell:

Java -cp antlr-3.2.jar org.antlr.Tool Exp.g

Il ne doit générer aucun message d'erreur et les fichiers ExpLexer.Java, ExpParser.Java et Exp.tokens doivent maintenant être générés.

Pour voir si tout fonctionne correctement, créez cette classe de test:

import org.antlr.runtime.*;

public class ANTLRDemo {
    public static void main(String[] args) throws Exception {
        ANTLRStringStream in = new ANTLRStringStream("12*(5-6)");
        ExpLexer lexer = new ExpLexer(in);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        ExpParser parser = new ExpParser(tokens);
        parser.eval();
    }
}

et le compiler:

// *nix/MacOS
javac -cp .:antlr-3.2.jar ANTLRDemo.Java

// Windows
javac -cp .;antlr-3.2.jar ANTLRDemo.Java

puis lancez-le:

// *nix/MacOS
Java -cp .:antlr-3.2.jar ANTLRDemo

// Windows
Java -cp .;antlr-3.2.jar ANTLRDemo

Si tout se passe bien, rien n’est imprimé sur la console. Cela signifie que l'analyseur n'a trouvé aucune erreur. Lorsque vous modifiez "12*(5-6)" en "12*(5-6", puis que vous le recompilez et l'exécutez, les éléments suivants doivent être imprimés:

line 0:-1 mismatched input '<EOF>' expecting ')'

D'accord, nous souhaitons maintenant ajouter un peu de code Java à la grammaire afin que l'analyseur effectue réellement quelque chose d'utile. L'ajout de code peut être effectué en plaçant { et } dans votre grammaire, avec du code Java simple.

Mais d’abord, toutes les règles de l’analyseur du fichier de grammaire doivent renvoyer une double valeur primitive. Vous pouvez le faire en ajoutant returns [double value] après chaque règle:

grammar Exp;

eval returns [double value]
    :    additionExp
    ;

additionExp returns [double value]
    :    multiplyExp 
         ( '+' multiplyExp 
         | '-' multiplyExp
         )* 
    ;

// ...

ce qui nécessite peu d'explications: chaque règle doit renvoyer une valeur double. Maintenant, pour "interagir" avec la valeur de retour double value (qui ne se trouve pas dans un bloc de code Java clair {...}) depuis un bloc de code, vous devez ajouter un signe dollar devant value:

grammar Exp;

/* This will be the entry point of our parser. */
eval returns [double value]                                                  
    :    additionExp { /* plain code block! */ System.out.println("value equals: "+$value); }
    ;

// ...

Voici la grammaire mais maintenant avec le code Java ajouté:

grammar Exp;

eval returns [double value]
    :    exp=additionExp {$value = $exp.value;}
    ;

additionExp returns [double value]
    :    m1=multiplyExp       {$value =  $m1.value;} 
         ( '+' m2=multiplyExp {$value += $m2.value;} 
         | '-' m2=multiplyExp {$value -= $m2.value;}
         )* 
    ;

multiplyExp returns [double value]
    :    a1=atomExp       {$value =  $a1.value;}
         ( '*' a2=atomExp {$value *= $a2.value;} 
         | '/' a2=atomExp {$value /= $a2.value;}
         )* 
    ;

atomExp returns [double value]
    :    n=Number                {$value = Double.parseDouble($n.text);}
    |    '(' exp=additionExp ')' {$value = $exp.value;}
    ;

Number
    :    ('0'..'9')+ ('.' ('0'..'9')+)?
    ;

WS  
    :   (' ' | '\t' | '\r'| '\n') {$channel=HIDDEN;}
    ;

et puisque notre règle eval renvoie maintenant un double, changez votre ANTLRDemo.Java en ceci:

import org.antlr.runtime.*;

public class ANTLRDemo {
    public static void main(String[] args) throws Exception {
        ANTLRStringStream in = new ANTLRStringStream("12*(5-6)");
        ExpLexer lexer = new ExpLexer(in);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        ExpParser parser = new ExpParser(tokens);
        System.out.println(parser.eval()); // print the value
    }
}

Encore une fois (re) générez un lexer et un analyseur syntaxique frais à partir de votre grammaire (1), compilez toutes les classes (2) et exécutez ANTLRDemo (3):

// *nix/MacOS
Java -cp antlr-3.2.jar org.antlr.Tool Exp.g   // 1
javac -cp .:antlr-3.2.jar ANTLRDemo.Java      // 2
Java -cp .:antlr-3.2.jar ANTLRDemo            // 3

// Windows
Java -cp antlr-3.2.jar org.antlr.Tool Exp.g   // 1
javac -cp .;antlr-3.2.jar ANTLRDemo.Java      // 2
Java -cp .;antlr-3.2.jar ANTLRDemo            // 3

et vous verrez maintenant le résultat de l'expression 12*(5-6) imprimé sur votre console!

Encore une fois: ceci est une explication très brève. Je vous encourage à parcourir le ANTLR wiki et à lire des tutoriels et/ou à jouer un peu avec ce que je viens de publier.

Bonne chance!

MODIFIER:

This post montre comment étendre l'exemple ci-dessus afin de pouvoir fournir un Map<String, Double> contenant des variables dans l'expression fournie.

Et ce Q & A montre comment créer un analyseur d'expression simple et un évaluateur utilisant ANTLR4.

Pour que ce code fonctionne avec une version actuelle de Antlr (juin 2014), je devais apporter quelques modifications. ANTLRStringStream devait devenir ANTLRInputStream, la valeur renvoyée nécessaire pour passer de parser.eval() à parser.eval().value et je devais supprimer la clause WS à la fin, car les valeurs d'attribut telles que $channel ne sont plus autorisées dans les actions lexer.

428
Bart Kiers

Pour moi, ce tutoriel a été très utile: https://tomassetti.me/antlr-mega-tutorial

Il contient des exemples de grammaire, des exemples de visiteurs dans différentes langues (Java, JavaScript, C # et Python) et bien d’autres choses encore. Hautement recommandé.

9
solo

Pour Antlr 4, le processus de génération de code Java est présenté ci-dessous: -

Java -cp antlr-4.5.3-complete.jar org.antlr.v4.Tool Exp.g

Mettez à jour votre nom de jar dans classpath en conséquence. 

7
Abhishek K

Sur https://github.com/BITPlan/com.bitplan.antlr , vous trouverez une bibliothèque Java ANTLR avec quelques classes d’aide utiles et quelques exemples complets. Il est prêt à être utilisé avec Maven et si vous aimez Eclipse et Maven.

https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/main/antlr4/com/bitplan/exp/Exp.g4

est un langage d'expression simple capable de multiplier et d'ajouter des opérations . https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/Java/com/bitplan/antlr/ TestExpParser.Java a les tests unitaires correspondants.

https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/main/antlr4/com/bitplan/iri/IRIParser.g4 est un analyseur syntaxique IRI qui a été divisé en trois les pièces:

  1. grammaire de l'analyseur
  2. la grammaire lexer
  3. grammaire LexBasic importée

https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/Java/com/bitplan/antlr/TestIRIParser.Java a les tests unitaires correspondants.

Personnellement, j'ai trouvé que c'était la partie la plus difficile à résoudre. Voir http://wiki.bitplan.com/index.php/ANTLR_maven_plugin

https://github.com/BITPlan/com.bitplan.antlr/tree/master/src/main/antlr4/com/bitplan/expr

contient trois autres exemples créés pour un problème de performances d'ANTLR4 dans une version antérieure. En attendant, ce problème a été résolu en tant que testcase https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/Java/com/bitplan/antlr/TestIssue994.Java spectacles.

1
Wolfgang Fahl

la version 4.7.1 était légèrement différente: pour l'importation:

import org.antlr.v4.runtime.*;

pour le segment principal - notez les CharStreams:

CharStream in = CharStreams.fromString("12*(5-6)");
ExpLexer lexer = new ExpLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ExpParser parser = new ExpParser(tokens);
0
user1562431