Question

Je voudrais commencer avec ANTLR, mais après avoir passé quelques heures à examiner les exemples à la antlr.org site, je ne peux toujours pas une compréhension claire de la grammaire à traiter Java.

est-il un exemple simple, quelque chose comme une calculatrice quatre opérations mises en œuvre avec ANTLR en passant par la définition de l'analyseur et tout le chemin vers le code source Java?

Était-ce utile?

La solution

Remarque : cette réponse est antlr3 ! Si vous êtes à la recherche d'un ANTLR4 par exemple, cette Q & a montre comment créer un analyseur d'expression simple et évaluateur en utilisant ANTLR4 .


Vous devez d'abord créer une grammaire. Ci-dessous une petite grammaire que vous pouvez utiliser pour évaluer les expressions qui sont construites à l'aide des 4 opérateurs mathématiques de base: +, -, * et /. Vous pouvez également les expressions de groupe en utilisant les parenthèses.

Notez que cette grammaire est juste un très basique: il ne gère pas les opérateurs unaires (le moins en: -1 + 9) ou décimaux comme .99 (sans numéro de premier plan), pour ne citer que deux défauts. Ceci est juste un exemple, 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;}
    ;

(règles de Parser commencent par une lettre minuscule, et les règles commencent par une analyseurs lexicaux lettre majuscule)

Après avoir créé la grammaire, vous aurez envie de générer un analyseur et lexer de celui-ci. Télécharger le pot ANTLR et le stocker dans le même répertoire que votre fichier de grammaire.

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

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

Il ne doit pas produire un message d'erreur, et les fichiers ExpLexer.java , ExpParser.java et Exp.tokens devrait maintenant être générée .

Pour voir si tout fonctionne correctement, créer 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

et puis exécutez:

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

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

Si tout va bien, rien n'est imprimé à la console. Cela signifie que l'analyseur n'a pas trouvé d'erreur. Lorsque vous modifiez "12*(5-6)" dans "12*(5-6" puis recompiler et l'exécuter, il devrait y imprimer les éléments suivants:

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

D'accord, maintenant, nous voulons ajouter un peu de code Java à la grammaire afin que l'analyseur ne fait quelque chose d'utile. Ajout de code peut être fait en plaçant { et } dans votre grammaire avec un code Java clair à l'intérieur.

Mais d'abord: toutes les règles de l'analyseur dans le fichier de grammaire doit retourner 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
         )* 
    ;

// ...

qui a besoin de peu d'explications: chaque règle devrait retourner une valeur double. Maintenant, pour « interagir » avec la valeur de retour double value (ce qui est à l'intérieur d'un {...} bloc plaine de code Java) à l'intérieur d'un bloc de code, vous aurez besoin d'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 de eval retourne maintenant un double, changer votre ANTLRDemo.java dans 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érer un nouveau et analyseur de lexer votre grammaire (1), compiler 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: cela est une explication très brève. Je vous invite à naviguer sur le ANTLR wiki et lire quelques tutoriels et / ou jouer un peu avec ce que je viens de publier.

Bonne chance!

EDIT:

Cette post montre comment étendre l'exemple ci-dessus de telle sorte qu'une Map<String, Double> peut être fournie qui contient les variables de l'expression fournie.

Pour obtenir ce code de travail dont je avais besoin avec une version actuelle de Antlr (Juin 2014) pour faire quelques changements. ANTLRStringStream nécessaires pour devenir ANTLRInputStream, la retouvaleur RNED nécessaire pour passer de parser.eval() à parser.eval().value, et je devais supprimer la clause de WS à la fin, parce que les valeurs d'attributs tels que $channel ne sont plus autorisés à apparaître dans les actions analyseurs lexicaux.

Autres conseils

ANTLR méga tutoriel par Gabriele Tomassetti est très utile

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

EDIT: d'autres articles utiles par Gabriele Tomassetti sur ANTLR

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

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

Mettre à jour le nom de votre pot dans classpath en conséquence.

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 simple langage d'expression qui peut faire se multiplier et 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 pour elle.

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

  1. grammaire analyseur
  2. grammaire lexer
  3. grammaire LexBasic importés

https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestIRIParser.java a les tests unitaires pour elle.

Personnellement, je trouve cela la partie la plus difficile à obtenir le droit. 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 qui ont été créés pour un problème de performance de ANTLR4 dans une version antérieure. En attendant ces questions a été fixé comme testcase https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestIssue994.java montre.

Version

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

import org.antlr.v4.runtime.*;

pour le segment principal - noter les CharStreams:

CharStream in = CharStreams.fromString("12*(5-6)");
ExpLexer lexer = new ExpLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ExpParser parser = new ExpParser(tokens);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top