Frage

Ich möchte mit ANTLR zu beginnen, aber nach ein paar Stunden verbringen die Beispiele in der Überprüfung antlr.org Website, kann ich noch nicht ein klares Verständnis der Grammatik zu Java-Prozess bekommen.

Gibt es ein einfaches Beispiel, so etwas wie eine Vier-Operationen implementiert Rechner mit ANTLR durch die Parser Definition gehen und die ganzen Weg zu dem Java-Quellcode?

War es hilfreich?

Lösung

Hinweis : Diese Antwort ist für antlr3 ! Wenn Sie suchen ein ANTLR4 Beispiel, dann ist diese Q & A zeigt, wie ein einfacher Ausdruck Parser erstellen, und Auswerter mit ANTLR4 .


Sie erstellen zunächst eine Grammatik. Im Folgenden finden Sie eine kleine Grammatik, dass Sie Ausdrücke auswerten können, die die vier grundlegenden mathematischen Operatoren gebaut verwenden: +, -, * und /. Sie können auch Gruppen-Ausdrücke mit Klammern.

Beachten Sie, dass diese Grammatik ist nur ein sehr einfacher ein: es ist nicht unäre Operatoren (das Minus in: 1 + 9) nicht verarbeitet oder Dezimalzahlen wie .99 (ohne führende Zahl), um nur zwei Mängel. Dies ist nur ein Beispiel, das Sie kann auf sich selbst arbeiten.

Hier ist der 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 der Grammatik zu schaffen, möchten Sie einen Parser und Lexer daraus generieren. Laden Sie die ANTLR jar und speichern Sie sie im gleichen Verzeichnis wie die Grammatikdatei.

Führen Sie den folgenden Befehl an der Shell / Eingabeaufforderung:

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

Es sollte keine Fehlermeldung erzeugen und die Dateien ExpLexer.java , ExpParser.java und Exp.tokens sollte nun erzeugt 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 kompiliert es:

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

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

und führen Sie es:

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

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

Wenn alles gut geht, wird nichts auf die Konsole ausgegeben werden. Das bedeutet, der Parser keinen Fehler finden. Wenn Sie "12*(5-6)" in "12*(5-6" ändern und dann neu zu kompilieren und ausführen, sollte die folgende gedruckt werden:

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 durch Platzieren { und } in Ihrer Grammatik mit einigem Klar Java-Code in sie geschehen.

Aber zuerst: alle Parser-Regeln in der Grammatik-Datei sollten einen primitiven Doppel Wert zurück. 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 wird erwartet, einen doppelten Wert zurückzukehren. Jetzt auf „interact“ mit dem Rückgabewert double value (die Java-Codeblock {...} NICHT in einer Ebene 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 ist 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) erzeugt einen frischen Lexer und Parser von Grammatik (1), kompiliert alle Klassen (2) und führt 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

und Sie werden nun das Ergebnis des Ausdrucks 12*(5-6) gedruckt auf der Konsole sehen!

Auch hier: das ist eine sehr kurze Erklärung. Ich ermutige Sie, die ANTLR Wiki zu durchsuchen und einige lesen Tutorials und / oder spielen ein bisschen mit dem, was ich gerade geschrieben.

Viel Glück!

EDIT:

Dieser Beitrag zeigt, wie das Beispiel erstrecken sich über so dass ein Map<String, Double> werden kann, vorausgesetzt, die Variablen in dem mitgelieferten Ausdruck gilt.

Um diesen Code zu erhalten, die Arbeit mit einer aktuellen Version von Antlr (Juni 2014) Ich brauchte ein paar Änderungen vorzunehmen. ANTLRStringStream benötigt ANTLRInputStream zu werden, die returned Wert benötigt, um Wechsel von parser.eval() zu parser.eval().value, und ich brauchte die WS Klausel am Ende zu entfernen, weil Attributwerte wie $channel nicht mehr in Lexer Aktionen erlaubt erscheinen.

Andere Tipps

ANTLR mega Tutorial von Gabriele Tomassetti ist sehr hilfreich

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

EDIT: andere nützliche Artikel von Gabriele Tomassetti auf ANTLR

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

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

Aktualisieren Sie Ihre jar Namen in Classpath entsprechend.

https://github.com/BITPlan/com.bitplan.antlr erhalten Sie eine ANTLR Java-Bibliothek mit einigen nützlichen Helfern Klassen und ein paar kompletten Beispielen finden. Es ist bereit, mit Maven verwendet werden soll, und wenn Sie wie Eclipse und maven.

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

ist eine einfache Ausdruckssprache, 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 für sie.

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

  1. Parser Grammatik
  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 Unit-Tests für sie.

Ich persönlich fand dies den heikelsten Teil richtig zu machen. 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 dieser Probleme wurde als Testfall https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestIssue994.java zeigt.

Version 4.7.1 war etwas anders: für den Import:

import org.antlr.v4.runtime.*;

für das Hauptsegment - man beachte die CharStreams:

CharStream in = CharStreams.fromString("12*(5-6)");
ExpLexer lexer = new ExpLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ExpParser parser = new ExpParser(tokens);
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top