Domanda

Mi piacerebbe iniziare con ANTLR, ma dopo aver trascorso alcune ore a rivedere gli esempi su antlr.org site, non riesco ancora ad avere una chiara comprensione della grammatica del processo Java.

Esiste qualche semplice esempio, qualcosa come un calcolatore a quattro operazioni implementato con ANTLR che passa attraverso la definizione del parser e arriva fino al codice sorgente Java?

È stato utile?

Soluzione

Nota:questa risposta è per ANTLR3!Se stai cercando un ANTLR4 esempio, allora questo Domande e risposte dimostra come creare un semplice parser di espressioni e un valutatore utilizzando ANTLR4.


Per prima cosa crei una grammatica.Di seguito è riportata una breve grammatica che puoi utilizzare per valutare le espressioni costruite utilizzando i 4 operatori matematici di base:+, -, * e /.Puoi anche raggruppare le espressioni utilizzando le parentesi.

Nota che questa grammatica è molto semplice:non gestisce gli operatori unari (il meno in:-1+9) o decimali come .99 (senza numero iniziale), per citare solo due difetti.Questo è solo un esempio su cui puoi lavorare su te stesso.

Ecco il contenuto del file di grammatica Esp.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;}
    ;

(Le regole del parser iniziano con una lettera minuscola e le regole del lexer iniziano con una lettera maiuscola)

Dopo aver creato la grammatica, ti consigliamo di generare da essa un parser e un lexer.Scarica il Vaso ANTLR e memorizzalo nella stessa directory del file di grammatica.

Esegui il seguente comando sulla shell/prompt dei comandi:

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

Non dovrebbe produrre alcun messaggio di errore e i file ExpLexer.java, ExpParser.java E Gettoni esp ora dovrebbe essere generato.

Per vedere se tutto funziona correttamente, crea questa classe di 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();
    }
}

e compilarlo:

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

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

e poi eseguilo:

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

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

Se tutto va bene, non viene stampato nulla sulla console.Ciò significa che il parser non ha trovato alcun errore.Quando cambi "12*(5-6)" in "12*(5-6" e quindi ricompilarlo ed eseguirlo, dovrebbe essere stampato quanto segue:

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

Ok, ora vogliamo aggiungere un po' di codice Java alla grammatica in modo che il parser faccia effettivamente qualcosa di utile.L'aggiunta del codice può essere eseguita inserendo { E } all'interno della tua grammatica con del semplice codice Java al suo interno.

Ma prima:tutte le regole del parser nel file di grammatica dovrebbero restituire un valore double primitivo.Puoi farlo aggiungendo returns [double value] dopo ogni regola:

grammar Exp;

eval returns [double value]
    :    additionExp
    ;

additionExp returns [double value]
    :    multiplyExp 
         ( '+' multiplyExp 
         | '-' multiplyExp
         )* 
    ;

// ...

che ha bisogno di poche spiegazioni:ci si aspetta che ogni regola restituisca un valore doppio.Ora dobbiamo "interagire" con il valore restituito double value (che NON è all'interno di un semplice blocco di codice Java {...}) dall'interno di un blocco di codice, dovrai aggiungere il simbolo del dollaro davanti a 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); }
    ;

// ...

Ecco la grammatica ma ora con il codice Java aggiunto:

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;}
    ;

e poiché il ns eval rule ora restituisce un double, cambia il tuo ANTLRDemo.java in questo:

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
    }
}

Ancora una volta (ri)genera un nuovo lexer e parser dalla tua grammatica (1), compila tutte le classi (2) ed esegui 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

e ora vedrai il risultato dell'espressione 12*(5-6) stampato sulla tua console!

Ancora:questa è una spiegazione molto breve.Ti incoraggio a sfogliare il Wiki dell'ANTLR e leggere alcuni tutorial e/o giocare un po' con quello che ho appena pubblicato.

Buona fortuna!

MODIFICARE:

Questo post mostra come estendere l'esempio precedente in modo che a Map<String, Double> può essere fornito che contenga variabili nell'espressione fornita.

Per far funzionare questo codice con una versione corrente di Antlr (giugno 2014) avevo bisogno di apportare alcune modifiche. ANTLRStringStream necessario diventare ANTLRInputStream, il valore restituito da cui cambiare parser.eval() A parser.eval().value, e avevo bisogno di rimuovere il file WS clausola alla fine, perché valori di attributo come $channel non possono più apparire nelle azioni del lexer.

Altri suggerimenti

ANTLR mega tutorial di Gabriele Tomassetti è molto utile

Ha esempi grammaticali, esempi di visitatori in diverse lingue (Java, JavaScript, C # e Python) e molte altre cose. Altamente raccomandato.

EDIT: altri articoli utili di Gabriele Tomassetti su ANTLR

Per Antlr 4 il processo di generazione di codice Java è qui di seguito: -

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

Aggiorna il tuo nome jar in classpath di conseguenza.

https://github.com/BITPlan/com.bitplan.antlr troverete una libreria ANTLR Java con alcune classi di supporto utili e alcuni esempi completi. E 'pronto per essere utilizzato con Maven e se vi piace Eclipse e Maven.

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

è un semplice linguaggio di espressione che può fare si moltiplicano e aggiungere le operazioni. https: //github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestExpParser.java ha i test di unità corrispondenti per esso.

https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/main/antlr4/com/bitplan/iri/IRIParser.g4 è un parser IRI che è stato diviso in tre parti:

  1. parser grammatica
  2. lexer grammatica
  3. importati grammatica LexBasic

https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestIRIParser.java ha i test di unità per esso.

Personalmente ho trovato questo la parte più difficile da ottenere. Vedere http://wiki.bitplan.com/index.php/ANTLR_maven_plugin

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

contiene altre tre esempi che sono stati creati per un problema di prestazioni di ANTLR4 in una versione precedente. Nel frattempo questi problemi è stato risolto come il testcase https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestIssue994.java spettacoli.

Versione 4.7.1 era leggermente diverso: per l'importazione:

import org.antlr.v4.runtime.*;

per il segmento principale - notare le CharStreams:

CharStream in = CharStreams.fromString("12*(5-6)");
ExpLexer lexer = new ExpLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ExpParser parser = new ExpParser(tokens);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top