ANTLR: Apakah ada contoh sederhana?

230

Saya ingin memulai dengan ANTLR, tetapi setelah menghabiskan beberapa jam meninjau contoh-contoh di situs antlr.org , saya masih tidak bisa mendapatkan pemahaman yang jelas tentang tata bahasa untuk proses Java.

Apakah ada beberapa contoh sederhana, sesuatu seperti kalkulator empat operasi diimplementasikan dengan ANTLR melalui definisi parser dan semua jalan ke kode sumber Java?

Eli
sumber
2
Contoh yang tepat digunakan sebagai tutorial di situs Antlr, terakhir saya periksa.
Cory Petosky
1
@Cory Petosky: dapatkah Anda menyediakan tautannya?
Eli,
Saya baru saja memposting bagian pertama tutorial video di ANTLR. Lihat javadude.com/articles/antlr3xtut Semoga Anda merasa terbantu !
Scott Stanchfield
2
Saya juga membagikan pencarian Anda.
Paul Draper
1
Jawaban terbaik untuk ANTLR 4 adalah membeli buku Parr "The Definitive ANTLR 4 Reference."
james.garriss

Jawaban:

448

Catatan : jawaban ini untuk ANTLR3 ! Jika Anda mencari contoh ANTLR4 , maka T&J ini menunjukkan cara membuat pengurai ekspresi sederhana, dan evaluator menggunakan ANTLR4 .


Anda pertama kali membuat tata bahasa. Di bawah ini adalah tata bahasa kecil yang dapat Anda gunakan untuk mengevaluasi ekspresi yang dibangun menggunakan 4 operator matematika dasar: +, -, * dan /. Anda juga dapat mengelompokkan ekspresi menggunakan tanda kurung.

Perhatikan bahwa tata bahasa ini hanya yang sangat mendasar: ia tidak menangani operator yang unary (minus dalam: -1 + 9) atau desimal seperti 0,99 (tanpa nomor utama), untuk menyebutkan hanya dua kekurangan. Ini hanyalah contoh yang bisa Anda kerjakan sendiri.

Berikut isi file tata bahasa 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;}
    ;

(Aturan Parser mulai dengan huruf kecil, dan aturan lexer dimulai dengan huruf kapital)

Setelah membuat tata bahasa, Anda ingin menghasilkan parser dan lexer darinya. Unduh tabung ANTLR dan simpan di direktori yang sama dengan file tata bahasa Anda.

Jalankan perintah berikut pada shell / command prompt Anda:

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

Seharusnya tidak menghasilkan pesan kesalahan, dan file ExpLexer.java , ExpParser.java dan Exp.tokens sekarang harus dihasilkan.

Untuk melihat apakah semuanya berfungsi dengan baik, buat kelas tes ini:

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

dan kompilasi:

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

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

dan kemudian jalankan:

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

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

Jika semuanya berjalan dengan baik, tidak ada yang dicetak ke konsol. Ini berarti parser tidak menemukan kesalahan. Ketika Anda mengubah "12*(5-6)"ke "12*(5-6"dan kemudian mengkompilasi ulang dan menjalankannya, ada harus dicetak berikut:

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

Oke, sekarang kita ingin menambahkan sedikit kode Java ke tata bahasa sehingga parser benar-benar melakukan sesuatu yang bermanfaat. Menambahkan kode dapat dilakukan dengan menempatkan {dan }di dalam tata bahasa Anda dengan beberapa kode Java sederhana di dalamnya.

Tapi pertama-tama: semua aturan parser dalam file tata bahasa harus mengembalikan nilai ganda primitif. Anda dapat melakukannya dengan menambahkan returns [double value]setelah setiap aturan:

grammar Exp;

eval returns [double value]
    :    additionExp
    ;

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

// ...

yang perlu sedikit penjelasan: setiap aturan diharapkan menghasilkan nilai ganda. Sekarang untuk "berinteraksi" dengan nilai kembali double value(yang TIDAK di dalam blok kode Java biasa {...}) dari dalam blok kode, Anda harus menambahkan tanda dolar di depan 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); }
    ;

// ...

Inilah tata bahasa tetapi sekarang dengan kode Java ditambahkan:

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

dan karena evalaturan kami sekarang menghasilkan dua kali lipat, ubah ANTLRDemo.java Anda menjadi ini:

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

Sekali lagi (re) hasilkan lexer dan parser baru dari tata bahasa Anda (1), kompilasi semua kelas (2) dan jalankan 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

dan sekarang Anda akan melihat hasil dari ekspresi yang 12*(5-6)dicetak ke konsol Anda!

Sekali lagi: ini adalah penjelasan yang sangat singkat. Saya mendorong Anda untuk menelusuri wiki ANTLR dan membaca beberapa tutorial dan / atau bermain sedikit dengan apa yang baru saja saya posting.

Semoga berhasil!

EDIT:

Posting ini menunjukkan cara memperluas contoh di atas sehingga Map<String, Double>dapat disediakan yang menampung variabel dalam ekspresi yang disediakan.

Agar kode ini berfungsi dengan versi Antlr saat ini (Juni 2014), saya perlu melakukan beberapa perubahan. ANTLRStringStreamdiperlukan untuk menjadi ANTLRInputStream, nilai yang dikembalikan perlu diubah dari parser.eval()ke parser.eval().value, dan saya perlu menghapus WSklausa di akhir, karena nilai atribut seperti $channeltidak lagi diizinkan untuk muncul dalam tindakan lexer.

Bart Kiers
sumber
1
Di mana implementasi dari parser.eval()terjadi? Itu tidak jelas DI SINI atau di Wiki ANTLR3!
1
@Jarrod, err, maaf, saya tidak benar-benar mengerti Anda. evaladalah aturan parser yang mengembalikan a double. Jadi ada eval()metode yang dapat Anda gunakan pada instance dari ExpParser, sama seperti yang saya contohkan di ANTLRDemo.main(...). Setelah membuat lexer / parser, cukup buka file ExpParser.javadan Anda akan melihat bahwa ada eval()metode mengembalikan a double.
Bart Kiers
@ Bart Saya sudah meneliti ini selama seminggu - ini adalah contoh pertama yang sebenarnya cukup rinci dan lengkap untuk bekerja pertama kali dan saya pikir saya mengerti. Saya hampir menyerah. Terima kasih!
Vineel Shah
13

ANTLR mega tutorial oleh Gabriele Tomassetti sangat membantu

Ini memiliki contoh tata bahasa, contoh pengunjung dalam berbagai bahasa (Java, JavaScript, C # dan Python) dan banyak hal lainnya. Sangat dianjurkan.

EDIT: artikel bermanfaat lainnya oleh Gabriele Tomassetti di ANTLR

solo
sumber
Tutorial yang luar biasa!
Manish Patel
Antlr sekarang memiliki cpp juga sebagai bahasa target. Apakah ada tutorial dengan contoh tentang cpp?
vineeshvs
Orang yang sama membuat tutorial untuk ANTLR di C ++ tomassetti.me/getting-started-antlr-cpp Saya kira Anda akan menemukan apa yang Anda cari di sini atau di mega tutorial
solo
7

Untuk Antlr 4 proses pembuatan kode java di bawah ini: -

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

Perbarui nama jar Anda di classpath.

Abhishek K
sumber
2

Di https://github.com/BITPlan/com.bitplan.antlr Anda akan menemukan perpustakaan java ANTLR dengan beberapa kelas pembantu yang bermanfaat dan beberapa contoh lengkap. Ini siap digunakan dengan pakar dan jika Anda suka gerhana dan pakar.

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

adalah bahasa Ekspresi sederhana yang dapat melakukan banyak dan menambah operasi. https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestExpParser.java memiliki unit test yang sesuai untuk itu.

https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/main/antlr4/com/bitplan/iri/IRIParser.g4 adalah parser IRI yang telah dipecah menjadi tiga bagian:

  1. tata bahasa parser
  2. tata bahasa lexer
  3. tata bahasa LexBasic yang diimpor

https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestIRIParser.java memiliki unit test untuknya.

Secara pribadi saya menemukan ini bagian yang paling sulit untuk diperbaiki. Lihat http://wiki.bitplan.com/index.php/ANTLR_maven_plugin

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

berisi tiga contoh lagi yang telah dibuat untuk masalah kinerja ANTLR4 di versi sebelumnya. Sementara itu masalah ini telah diperbaiki seperti yang ditampilkan dalam testcase https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestIssue994.java .

Wolfgang Fahl
sumber
2

versi 4.7.1 sedikit berbeda: untuk impor:

import org.antlr.v4.runtime.*;

untuk segmen utama - perhatikan CharStreams:

CharStream in = CharStreams.fromString("12*(5-6)");
ExpLexer lexer = new ExpLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ExpParser parser = new ExpParser(tokens);
pengguna1562431
sumber