2008-09-26 15 views
5

Aynı gramerdeki geçerli tarihleri ​​ve sayıları ayrıştırmanın en basit yolu (en kısa, en az kural ve uyarı yok) yolu nedir? Benim sorunum, geçerli bir ayı (1-12) eşleştirmek için bir lexer kuralının 1-12 arasındaki herhangi bir oluşumla eşleşmesidir.Antlr: Tarih ve sayıları tanımanın en kolay yolu?

number: (MONTH|INT); 

ben gün ve yıl boyunca lexer kuralları eklediğinizde Sadece daha karmaşık bir hal alır: Sadece bir sayı eşleştirmek istiyorsanız Yani, böyle bir ayrıştırma kuralı gerekir. ay, gün & yıl ayrıştırma veya lexer kuralları ise

date: month '/' day ('/' year)? -> ^('DATE' year month day); 

Ben sadece çok uzun zaman aynı ağaç yapısı ile bitirmek gibi umurumda değil: Böyle tarih için bir ayrıştırma kuralı istiyorum. Ayrıca, sorun size lexer ve/veya ayrıştırıcı sözdizimsel ve semantik hem denetimi gerçekleştirmek istediğiniz gibi görünüyor olmasıdır .:

foo: STRING OP number -> ^(OP STRING number); 
STRING: ('a'..'z')+; 
OP: ('<'|'>'); 

cevap

5

, başka yerde örneğin sayıları tanımak gerekiyor. Bu yaygın bir hatadır ve sadece basit dillerde mümkün olan bir şeydir.

Gerçekten yapmanız gereken, lexer ve ayrıştırıcıda daha geniş kapsamlı kabul edilmeli ve sonra anlamsal denetimler gerçekleştirilmelidir. Lexing'inizde ne kadar katı olduğunuz size bağlıdır, ancak ayınızın öncesindeki sıfırları kabul etmeniz gerekip gerekmediğine bağlı olarak iki temel seçeneğiniz vardır: 1) INT'leriniz için gerçekten kabul edin, 2) DATENUM'u Yalnızca geçerli günler olan ancak geçerli INT'ler olmayan jetonları kabul edin. Orada daha sonra kodda (IntS sonra sözdizimi düzeyindeki doğrulanabilir olacağından daha az anlamsal kontroller gerekli olacaktır ve size tarihlerde anlamsal denetimleri gerçekleştirmek için gereken tek olacak çünkü ikinci önereceğimiz ilk yaklaşım.

INT: '0'..'9'+; 

ikinci yaklaşım:

DATENUM: '0' '1'..'9'; 
INT: '0' | SIGN? '1'..'9' '0'..'9'*; 

lexer bu kuralları kullanarak kabul ettikten sonra, sizin tarih alanı olmak ya olacaktır:

date: INT '/' INT ('/' INT)? 

ya:

date: (INT | DATENUM) '/' (INT | DATENUM) ('/' (INT | DATENUM))? 

Bundan sonra, tarihlerinizin geçerli olduğundan emin olmak için AST'niz üzerinde anlamsal bir işlem gerçekleştirirsiniz. Eğer dilbilgisi içinde anlamsal kontroller ölü seti iseniz

Ancak ANTLR çözümleyici anlamsal yüklemleri sağlar, böylece bu gibi değerleri kontrol eden bir tarih alanını yapabiliriz:

date: month=INT '/' day=INT (year='/' INT)? { year==null ? (/* First check /*) : (/* Second check */)} 

Ancak bunu yaparsınız, dilinize özel bir kod yerleştiriyorsunuz ve hedefler arasında taşınabilir olmayacak. ANTLR4 kullanma

0

, burada kullandığım basit bir kombine gramer olduğunu. Sadece basit jetonları eşleştirmek için lexer'ı kullanır, ayrıştırıcı kurallarını tarihlerle sayıları yorumlamak için bırakır.

// parser rules 

date 
    : INT SEPARATOR month SEPARATOR INT 
    | INT SEPARATOR month SEPARATOR INT4 
    | INT SEPARATOR INT SEPARATOR INT4; 

month : JAN | FEB | MAR | APR | MAY | JUN | JUL | AUG | SEP | OCT | NOV | DEC ; 

number : FLOAT | INT | INT4 ; 

// lexer rules 

FLOAT : DIGIT+ '.' DIGIT+ ; 

INT4 : DIGIT DIGIT DIGIT DIGIT; 
INT : DIGIT+; 

JAN : [Jj][Aa][Nn] ; 
FEB : [Ff][Ee][Bb] ; 
MAR : [Mm][Aa][Rr] ; 
APR : [Aa][Pp][Rr] ; 
MAY : [Mm][Aa][Yy] ; 
JUN : [Jj][Uu][Nn] ; 
JUL : [Jj][Uu][Ll] ; 
AUG : [Aa][Uu][Gg] ; 
SEP : [Ss][Ee][Pp] ; 
OCT : [Oo][Cc][Tt] ; 
NOV : [Nn][Oo][Vv] ; 
DEC : [Dd][Ee][Cc] ; 

SEPARATOR : [/\\\-] ; 

fragment DIGIT : [0-9];