Not : Bu cevap ANTLR3 içindir ! Bir ANTLR4 örneği arıyorsanız , bu soru ve cevap , ANTLR4 kullanarak basit bir ifade ayrıştırıcısının ve değerlendiricinin nasıl oluşturulacağını gösterir .
Önce bir dilbilgisi oluşturursunuz. Aşağıda, 4 temel matematik operatörü kullanılarak oluşturulan ifadeleri değerlendirmek için kullanabileceğiniz küçük bir dilbilgisi bulunmaktadır: +, -, * ve /. Ayrıca parantez kullanarak ifadeleri gruplandırabilirsiniz.
Bu dilbilgisinin çok basit bir dil olduğuna dikkat edin: yalnızca iki eksiklik belirtmek için tekli işleçleri (eksi: -1 + 9) veya .99 (ondalık sayı olmadan) ondalık sayıları işlemez. Bu sadece kendiniz üzerinde çalışabileceğiniz bir örnektir.
Exp.g gramer dosyasının içeriği :
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;}
;
(Ayrıştırıcı kuralları küçük harfle başlar ve lexer kuralları büyük harfle başlar)
Dilbilgisini oluşturduktan sonra, ondan bir ayrıştırıcı ve lexer oluşturmak isteyeceksiniz. ANTLR kavanozunu indirin ve dilbilgisi dosyanızla aynı dizinde saklayın.
Kabuk / komut isteminizde aşağıdaki komutu yürütün:
java -cp antlr-3.2.jar org.antlr.Tool Exp.g
Herhangi bir hata mesajı ve dosyaları üretmek olmamalıdır ExpLexer.java , ExpParser.java ve Exp.tokens şimdi oluşturulmalıdır.
Her şeyin düzgün çalışıp çalışmadığını görmek için şu test sınıfını oluşturun:
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();
}
}
ve derleyin:
// *nix/MacOS
javac -cp .:antlr-3.2.jar ANTLRDemo.java
// Windows
javac -cp .;antlr-3.2.jar ANTLRDemo.java
ve sonra çalıştırın:
// *nix/MacOS
java -cp .:antlr-3.2.jar ANTLRDemo
// Windows
java -cp .;antlr-3.2.jar ANTLRDemo
Her şey yolunda giderse, konsola hiçbir şey yazdırılmıyor. Bu, ayrıştırıcının herhangi bir hata bulamadığı anlamına gelir. Eğer değiştirdiğinizde "12*(5-6)"
içine "12*(5-6"
ve daha sonra yeniden derleme ve çalıştırın, aşağıdakileri orada basılmış edilmelidir:
line 0:-1 mismatched input '<EOF>' expecting ')'
Tamam, şimdi dilbilgisine biraz Java kodu eklemek istiyoruz, böylece ayrıştırıcı aslında yararlı bir şey yapıyor. Ekleme kod yerleştirilmesiyle yapılabilir {
ve }
bunun içindeki bazı düz Java koduyla dilbilgisi içeride.
Ama önce: dilbilgisi dosyasındaki tüm ayrıştırıcı kuralları ilkel bir çift değer döndürmelidir. returns [double value]
Her kuraldan sonra ekleyerek bunu yapabilirsiniz :
grammar Exp;
eval returns [double value]
: additionExp
;
additionExp returns [double value]
: multiplyExp
( '+' multiplyExp
| '-' multiplyExp
)*
;
// ...
Bu da çok az açıklama gerektirir: her kuralın iki kat değer döndürmesi beklenir. Şimdi double value
bir kod bloğunun içinden dönüş değeri (düz bir Java kod bloğu içinde DEĞİLDİR) ile "etkileşim kurmak" {...}
için, önüne bir dolar işareti eklemeniz gerekir 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); }
;
// ...
İşte dilbilgisi ama şimdi Java kodu eklendi:
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;}
;
ve eval
kuralımız şimdi bir çift döndürdüğünden, ANTLRDemo.java dosyanızı şu şekilde değiştirin:
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
}
}
Tekrar (yeniden) dilbilginizden (1) yeni bir lexer ve ayrıştırıcı oluşturun, tüm sınıfları (2) derleyin ve ANTLRDemo (3) komutunu çalıştırın:
// *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
ve şimdi 12*(5-6)
konsolunuza yazdırılan ifadenin sonucunu göreceksiniz !
Tekrar: Bu çok kısa bir açıklama. ANTLR wiki'ye göz atmanızı ve bazı eğiticileri okumanızı ve / veya az önce yayınladığım şeylerle biraz oynamanızı tavsiye ederim.
İyi şanslar!
DÜZENLE:
Bu yazı , yukarıdaki Map<String, Double>
ifadenin değişkenleri tutan bir sağlanabilmesi için yukarıdaki örneğin nasıl genişletileceğini gösterir .
Bu kodun Antlr'ın güncel bir sürümüyle (Haziran 2014) çalışmasını sağlamak için birkaç değişiklik yapmam gerekiyordu. ANTLRStringStream
olmak için gerekli ANTLRInputStream
, değişim için gerekli döndürülen değer parser.eval()
için parser.eval().value
, ben kaldırmak için gerekli WS
nitelik değerleri gibi çünkü sonunda maddesini $channel
artık lexer eylemlere görünmesine izin verilir.