2009-12-18 5 views
192

Ich möchte mit ANTLR beginnen, aber nachdem ich ein paar Stunden damit verbracht habe, die Beispiele auf der antlr.org-Site zu überprüfen, kann ich immer noch kein klares Verständnis der Grammatik für den Java-Prozess bekommen.ANTLR: Gibt es ein einfaches Beispiel?

Gibt es ein einfaches Beispiel, etwa einen Rechner mit vier Operationen, der mit ANTLR implementiert wird und die Parserdefinition bis hin zum Java-Quellcode durchläuft?

+2

Dieses genaue Beispiel wird als Tutorial auf Antlr's Website verwendet, zuletzt habe ich überprüft. –

+1

@Cory Petosky: Können Sie den Link liefern? – Eli

+2

Ich teile auch Ihre Suche. –

Antwort

393

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.

+1

Woher kommen die Implementierungen von 'parser.eval()'? Das ist HIER oder im ANTLR3 Wiki nicht klar! –

+1

@Jarrod, äh, sorry, ich verstehe dich nicht wirklich. 'eval' ist eine Parser-Regel, die ein' double' zurückgibt. Also gibt es eine 'eval()' Methode, die Sie in einer Instanz eines 'ExpParser' aufrufen können, genau wie ich es in' ANTLRDemo.main (...) 'gezeigt habe. Nachdem Sie einen Lexer/Parser erzeugt haben, öffnen Sie einfach die Datei 'ExpParser.java' und Sie werden sehen, dass es eine' eval() 'Methode gibt, die ein' double' zurückgibt. –

+0

@Bart Ich habe dies seit einer Woche recherchiert - dies ist das erste Beispiel, das tatsächlich detailliert und vollständig genug war, um das erste Mal zu arbeiten und das ich glaube zu verstehen. Ich hatte fast aufgegeben. Vielen Dank! –

7

Für Antlr 4 die Codegenerierung Java ist unter: -

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

Aktualisieren Sie Ihr Glas Namen entsprechend in Classpath.

1

Unter https://github.com/BITPlan/com.bitplan.antlr finden Sie eine ANTLR-Java-Bibliothek mit einigen nützlichen Hilfsklassen und ein paar vollständigen Beispielen. Es ist bereit für die Verwendung mit Maven und wenn Sie Eclipse und Maven mögen.

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

ist eine einfache Expression Sprache, die mehrfach tun und Operationen hinzuzufügen. https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestExpParser.java hat die entsprechenden Unit-Tests dafür.

https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/main/antlr4/com/bitplan/iri/IRIParser.g4 ist ein IRI-Parser, der in die drei Teile aufgeteilt wurde:

  1. parser Grammar
  2. LEXER Grammatik
  3. importiert LexBasic Grammatik

https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestIRIParser.java hat die Einheit Tests für es.

Persönlich fand ich das der schwierigste Teil, um richtig zu kommen. Siehe http://wiki.bitplan.com/index.php/ANTLR_maven_plugin

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

enthält drei weitere Beispiele, die für ein Leistungsproblem von ANTLR4 in einer früheren Version erstellt wurden. In der Zwischenzeit wurden diese Probleme behoben, wie der Testfall https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestIssue994.java zeigt.

3

Für mich ist dieses Tutorial sehr hilfreich: https://tomassetti.me/antlr-mega-tutorial

Es hat Grammatik Beispiele, Beispiele für die Besucher in verschiedenen Sprachen (Java, JavaScript, C# und Python) und viele andere Dinge. Sehr empfehlenswert.