Vous créez d'abord une grammaire. Ci-dessous une petite grammaire que vous pouvez utiliser pour évaluer les expressions qui sont construites en utilisant les 4 de base en mathématiques les opérateurs: +, -, * et /. Vous pouvez également grouper des expressions à l'aide de parenthèses.
Notez que cette grammaire est juste un très basique: il ne gère pas les opérateurs unaires (moins de: -1+9) ou décimales comme .99 (sans numéro), pour ne citer que deux lacunes. C'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;}
;
(Analyseur règles de commencer avec une lettre minuscule, et lexer règles de commencer avec une lettre majuscule)
Après la création de la grammaire, vous aurez envie de générer un analyseur syntaxique et l'analyseur lexical. Télécharger le ANTLR pot et de 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 devrait pas avoir de message d'erreur, et les fichiers ExpLexer.java, ExpParser.java et Exp.les jetons doivent maintenant être généré.
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 de l'exécuter:
// *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é sur la console. Cela signifie que l'analyseur n'a trouvé aucune erreur. Lorsque vous modifiez "12*(5-6)"
en "12*(5-6"
puis de recompiler et de l'exécuter, il doit être imprimé suivantes:
line 0:-1 mismatched input '<EOF>' expecting ')'
Bon, maintenant, nous voulons ajouter un peu de code Java à la grammaire, de sorte que l'analyseur ne fait quelque chose d'utile. L'ajout de code peut être fait en plaçant {
et }
à l'intérieur de votre grammaire avec certains plaine de code Java à l'intérieur.
Mais d'abord: tous analyseur de règles dans le fichier de grammaire doit retourner une primitive valeur double. 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'explication: chaque règle est prévu pour renvoyer une valeur de type double. Maintenant, à "interagir" avec la valeur de retour double value
(ce qui n'est PAS à l'intérieur d'une plaine de code Java bloc {...}
) de l'intérieur d'un bloc de code, vous aurez besoin d'ajouter un signe de dollar en face de l' 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 depuis notre eval
règle renvoie un double, changer votre ANTLRDemo.java dans ce:
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
}
}
De nouveau (re) générer une nouvelle analyseur lexical et l'analyseur de votre grammaire (1), de compiler toutes les catégories (2) et de l'exécution 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 allez maintenant voir le résultat de l'expression 12*(5-6)
imprimé sur votre console!
Encore une fois: c'est une très brève explication. Je vous encourage à parcourir le ANTLR wiki et de lire quelques tutoriels et/ou jouer un peu avec ce que je viens de poster.
Bonne chance!
EDIT:
Ce post montre comment étendre l'exemple ci-dessus de sorte qu'un Map<String, Double>
que détient des variables dans l'expression.
Et ce Q&A montre comment créer un simple analyseur d'expression, et par l'évaluateur à l'aide de ANTLR4.
Pour obtenir ce code de travail avec une version actuelle de Antlr (juin 2014), j'ai besoin de faire quelques changements. ANTLRStringStream
nécessaires pour devenir ANTLRInputStream
, la valeur retournée besoin de changer de parser.eval()
de parser.eval().value
, et que je devais enlever l' WS
clause à la fin, parce que les valeurs d'attribut comme $channel
ne sont plus autorisés à apparaître dans lexer actions.