ANTLR'de anlamsal yüklem nedir ?
ANTLR'de anlamsal yüklem nedir ?
Yanıtlar:
ANTLR 4'teki tahminler için, bu yığın taşması Soru ve Cevaplarına göz atın :
Bir semantik yüklem yalın kodu kullanarak dilbilgisi eylemleri üzerine ekstra (semantik) kuralları uygulamak için bir yoldur.
3 tür anlamsal yüklem vardır:
Diyelim ki beyaz boşlukları göz ardı ederek, yalnızca virgülle ayrılmış sayılardan oluşan bir metin bloğunuz var. Numaraların en fazla 3 basamaklı "uzun" (en fazla 999) olduğundan emin olarak bu girişi ayrıştırmak istersiniz. Aşağıdaki gramer ( Numbers.g
) böyle bir şey yapar:
grammar Numbers;
// entry point of this parser: it parses an input string consisting of at least
// one number, optionally followed by zero or more comma's and numbers
parse
: number (',' number)* EOF
;
// matches a number that is between 1 and 3 digits long
number
: Digit Digit Digit
| Digit Digit
| Digit
;
// matches a single digit
Digit
: '0'..'9'
;
// ignore spaces
WhiteSpace
: (' ' | '\t' | '\r' | '\n') {skip();}
;
Dilbilgisi aşağıdaki sınıfla test edilebilir:
import org.antlr.runtime.*;
public class Main {
public static void main(String[] args) throws Exception {
ANTLRStringStream in = new ANTLRStringStream("123, 456, 7 , 89");
NumbersLexer lexer = new NumbersLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
NumbersParser parser = new NumbersParser(tokens);
parser.parse();
}
}
Sözcük ve ayrıştırıcı oluşturarak, tüm .java
dosyaları derleyerek ve Main
sınıfı çalıştırarak test edin :
java -cp antlr-3.2.jar org.antlr.Tool Numbers.g javac -cp antlr-3.2.jar * .java java -cp.: antlr-3.2.jar Ana
Bunu yaparken, konsola hiçbir şey yazdırılmaz, bu da hiçbir şeyin yanlış gitmediğini gösterir. Değiştirmeyi deneyin:
ANTLRStringStream in = new ANTLRStringStream("123, 456, 7 , 89");
içine:
ANTLRStringStream in = new ANTLRStringStream("123, 456, 7777 , 89");
ve testi tekrar yapın: dizeden hemen sonra konsolda görünen bir hata göreceksiniz 777
.
Bu bizi anlamsal yüklemlere getirir. 1-10 basamaklı sayıları ayrıştırmak istediğinizi varsayalım. Şöyle bir kural:
number
: Digit Digit Digit Digit Digit Digit Digit Digit Digit Digit
| Digit Digit Digit Digit Digit Digit Digit Digit Digit
/* ... */
| Digit Digit Digit
| Digit Digit
| Digit
;
hantal hale gelirdi. Anlamsal yüklemler bu tür bir kuralı basitleştirmeye yardımcı olabilir.
Bir doğrulama anlamsal yüklem bir soru işareti gelen kod bloğu başka bir şey değildir:
RULE { /* a boolean expression in here */ }?
Doğrulayıcı bir
anlamsal yüklem kullanarak yukarıdaki sorunu çözmek number
için dilbilgisindeki kuralı şu şekilde değiştirin :
number
@init { int N = 0; }
: (Digit { N++; } )+ { N <= 10 }?
;
Parçalar { int N = 0; }
ve { N++; }
ilk ayrıştırıcı "girer" ne zaman başlatılır olan düz Java ifadelerdir number
kuralı. Asıl dayanak şudur: { N <= 10 }?
çözümleyicinin, FailedPredicateException
bir sayı 10 basamaktan uzun olduğunda bir atmasına neden olur
.
Aşağıdakileri kullanarak test edin ANTLRStringStream
:
// all equal or less than 10 digits
ANTLRStringStream in = new ANTLRStringStream("1,23,1234567890");
bu bir istisna oluşturmaz, ancak aşağıdaki durum bir istisna oluşturmaz:
// '12345678901' is more than 10 digits
ANTLRStringStream in = new ANTLRStringStream("1,23,12345678901");
Bir kapı semantik yüklem bir benzer doğrulama semantik yüklem , sadece kapı sürümü yerine bir sözdizim hatası üretir FailedPredicateException
.
Geçitli bir anlamsal yüklemin sözdizimi şöyledir:
{ /* a boolean expression in here */ }?=> RULE
Bunun yerine yukarıdaki sorunu 10 haneye kadar olan sayıları eşleştirmek için geçitli tahminler kullanarak çözmek için şunu yazarsınız:
number
@init { int N = 1; }
: ( { N <= 10 }?=> Digit { N++; } )+
;
Her ikisiyle de tekrar test edin:
// all equal or less than 10 digits
ANTLRStringStream in = new ANTLRStringStream("1,23,1234567890");
ve:
// '12345678901' is more than 10 digits
ANTLRStringStream in = new ANTLRStringStream("1,23,12345678901");
ve sonuncunun bir hata atacağını göreceksiniz.
Son yüklem türü, belirsizliği ortadan kaldıran bir anlamsal yüklemdir , bu biraz doğrulayıcı yüklem ( {boolean-expression}?
) gibi görünür , ancak daha çok kapılı bir anlamsal yüklem gibi davranır (boole ifadesi değerlendirildiğinde istisna atılmaz false
). Bir kuralın bazı özelliklerini kontrol etmek ve ayrıştırıcının söz konusu kuralla eşleşip eşleşmemesini sağlamak için bir kuralın başlangıcında bunu kullanabilirsiniz.
Örnek dilbilgisinin Number
0..999 aralığındaki sayılarla eşleşecek jetonlar (ayrıştırıcı kuralı yerine sözcük kuralı) oluşturduğunu varsayalım. Şimdi ayrıştırıcıda, düşük ve yüksek sayılar arasında bir ayrım yapmak istersiniz (düşük: 0..500, yüksek: 501..999). Bu, düşük mü yoksa yüksek mi olduğunu kontrol etmek için akışta ( ) sonraki belirteci incelediğiniz, belirsizliği ortadan kaldıran bir anlamsal tahmin kullanılarak yapılabilir input.LT(1)
.
Bir demo:
grammar Numbers;
parse
: atom (',' atom)* EOF
;
atom
: low {System.out.println("low = " + $low.text);}
| high {System.out.println("high = " + $high.text);}
;
low
: {Integer.valueOf(input.LT(1).getText()) <= 500}? Number
;
high
: Number
;
Number
: Digit Digit Digit
| Digit Digit
| Digit
;
fragment Digit
: '0'..'9'
;
WhiteSpace
: (' ' | '\t' | '\r' | '\n') {skip();}
;
Şimdi dizeyi ayrıştırırsanız "123, 999, 456, 700, 89, 0"
, aşağıdaki çıktıyı görürsünüz:
low = 123
high = 999
low = 456
high = 700
low = 89
low = 0
input.LT(1)
için getCurrentToken()
artık :-)
Wincent.com'daki ANTLR tahminlerine kısa referansı her zaman kılavuzum olarak kullandım.