Sie erstellen zuerst eine Grammatik. Im Folgenden finden Sie eine kleine Grammatik, mit der Sie Ausdrücke auswerten können, die mit den vier mathematischen Grundfunktionen erstellt wurden: +, -, * und /. Sie können Ausdrücke auch mithilfe von Klammern gruppieren.
Beachten Sie, dass diese Grammatik nur eine sehr einfache ist: es behandelt nicht unäre Operatoren (das Minus in: -1 + 9) oder Dezimalzahlen wie .99 (ohne eine führende Zahl), um nur zwei Mängel zu nennen. Dies ist nur ein Beispiel, an dem Sie selbst arbeiten können.
Hier den Inhalt der Grammatikdatei 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;}
;
(Parser-Regeln mit einem Kleinbuchstaben beginnen und Lexer Regeln beginnen mit einem Großbuchstaben)
Nach Wenn Sie die Grammatik erstellen, sollten Sie einen Parser und einen Lexer daraus generieren. Laden Sie das ANTLR jar herunter und speichern Sie es im selben Verzeichnis wie Ihre Grammatikdatei.
Führen Sie den folgenden Befehl an der Shell/Eingabeaufforderung:
java -cp antlr-3.2.jar org.antlr.Tool Exp.g
Es keine Fehlermeldung erzeugen sollte, und die Dateien ExpLexer.java, ExpParser.java und Exp.tokens sollte jetzt generiert werden.
Um zu sehen, ob alles richtig, erstellen Sie diese Test-Klasse funktioniert:
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();
}
}
und kompilieren:
// *nix/MacOS
javac -cp .:antlr-3.2.jar ANTLRDemo.java
// Windows
javac -cp .;antlr-3.2.jar ANTLRDemo.java
und dann laufen:
// *nix/MacOS
java -cp .:antlr-3.2.jar ANTLRDemo
// Windows
java -cp .;antlr-3.2.jar ANTLRDemo
Wenn alles klappt Nun, nichts wird auf die Konsole gedruckt. Dies bedeutet, dass der Parser keinen Fehler gefunden hat. Wenn Sie "12*(5-6)"
in "12*(5-6"
ändern und dann neu kompilieren und ausführen, sollte es gedruckt werden die folgenden:
line 0:-1 mismatched input '<EOF>' expecting ')'
Okay, jetzt wollen wir ein bisschen von Java-Code auf die Grammatik hinzuzufügen, so dass der Parser tut tatsächlich etwas Nützliches . Hinzufügen von Code kann getan werden, indem {
und }
innerhalb Ihrer Grammatik mit einigen einfachen Java-Code in ihm platziert.
Aber zuerst: alle Parser-Regeln in der Grammatikdatei sollten einen primitiven doppelten Wert zurückgeben.Sie können das tun, indem returns [double value]
nach jeder Regel hinzufügen:
grammar Exp;
eval returns [double value]
: additionExp
;
additionExp returns [double value]
: multiplyExp
('+' multiplyExp
| '-' multiplyExp
)*
;
// ...
die wenig Erklärung braucht: jede Regel einen doppelten Wert zurückgeben soll. Nun zu „interagieren“ mit dem Rückgabewert double value
(die nicht innerhalb einer Ebene Java-Code-Block ist {...}
) aus dem Inneren eines Codeblocks, müssen Sie ein Dollarzeichen vor value
hinzuzufügen:
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); }
;
// ...
Hier die Grammatik aber jetzt mit dem Java-Code hinzugefügt:
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;}
;
und da unsere eval
Regel jetzt eine doppelte zurückgibt, ändern Sie Ihre ANTLRDemo.java in diese:
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
}
}
Wieder (re) einen frischen Lexer und Parser von Grammatik (1), kompilieren alle Klassen (2) erzeugen und ANTLRDemo (3) laufen:
// *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
und Sie sehen jetzt das Ergebnis des Ausdrucks 12*(5-6)
gedruckt auf Ihrer Konsole!
Nochmal: Dies ist eine sehr kurze Erklärung. Ich ermutige Sie, die ANTLR wiki zu durchsuchen und einige Tutorials zu lesen und/oder ein bisschen mit dem zu spielen, was ich gerade gepostet habe.
Viel Glück!
EDIT:
This post zeigt, wie das obige Beispiel zu erweitern, so dass ein Map<String, Double>
vorgesehen werden, die Variablen in dem mitgelieferten Ausdruck gilt.
Und diese Q&A zeigt, wie Sie einen einfachen Ausdruck Parser und Evaluator mit ANTLR4 erstellen.
Um diesen Code mit einer aktuellen Version von Antlr (Juni 2014) arbeiten zu lassen, musste ich ein paar Änderungen vornehmen. ANTLRStringStream
benötigt ANTLRInputStream
, der zurückgegebene Wert von parser.eval()
zu parser.eval().value
geändert werden, und ich musste die WS
-Klausel am Ende entfernen, weil Attributwerte wie $channel
nicht mehr in Lexer Aktionen angezeigt werden dürfen.
Dieses genaue Beispiel wird als Tutorial auf Antlr's Website verwendet, zuletzt habe ich überprüft. –
@Cory Petosky: Können Sie den Link liefern? – Eli
Ich teile auch Ihre Suche. –