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 eval
aturan 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. ANTLRStringStream
diperlukan untuk menjadi ANTLRInputStream
, nilai yang dikembalikan perlu diubah dari parser.eval()
ke parser.eval().value
, dan saya perlu menghapus WS
klausa di akhir, karena nilai atribut seperti $channel
tidak lagi diizinkan untuk muncul dalam tindakan lexer.