2008-09-26 14 views
5

Jaka jest najprostsza (najkrótsza, najmniejsza liczba reguł i brak ostrzeżeń) sposób na przeanalizowanie obu ważnych dat i liczb w tej samej gramatyce? Mój problem polega na tym, że reguła lexera pasująca do poprawnego miesiąca (1-12) będzie pasować do każdego wystąpienia 1-12. Więc jeśli po prostu chcesz dopasować liczbę, muszę regułę parsującej jak:Antlr: Najprostszy sposób rozpoznawania dat i liczb?

number: (MONTH|INT); 

To tylko staje się bardziej skomplikowana, gdy dodam zasady Lexer za dnia i roku. Chcę regułę przetwarza na bieżąco tak:

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

I nie obchodzi mnie, czy miesiąc, dzień & lat są zasady analizowanie składni i Lexer, tylko tak długo, jak skończyć z tej samej strukturze drzewa. Ja również muszą być w stanie rozpoznać numery gdzie indziej, np .:

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

Odpowiedz

5

Problemem jest to, że wydają się chce wykonać zarówno syntaktyczną i semantyczną sprawdzanie w lexer i/lub swojej parsera. To powszechny błąd i coś, co jest możliwe tylko w bardzo prostych językach.

To, co naprawdę należy zrobić, to zaakceptować szerzej w lexer i parserze, a następnie przeprowadzić kontrole semantyczne. Jak ścisłe są twoje leksykony, zależy tylko od ciebie, ale masz dwie podstawowe opcje, w zależności od tego, czy musisz zaakceptować zera poprzedzające twoje dni w miesiącu: 1) bądź naprawdę akceptujący dla swoich INT, 2) określ DATENUM na akceptuj tylko te żetony, które są ważnymi dniami, ale nie są ważnymi INTs. Polecam drugi, ponieważ później będzie potrzeba mniejszej ilości sprawdzeń semantycznych w kodzie (ponieważ INTs będzie można zweryfikować na poziomie składni, a będziesz musiał tylko wykonać kontrole semantyczne w swoich datach. Pierwsze podejście:

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

drugie podejście:

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

Po zaakceptowaniu stosując te zasady w lexer, data field będzie albo:

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

lub:

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

Następnie wykonasz semantyczne przejazdy AST, aby upewnić się, że daty są prawidłowe.

Jeśli jesteś nagonce na przeprowadzenie kontroli semantyczne w gramatyce jednak ANTLR pozwala semantyczne predykaty w parsera, więc można zrobić pole daty, który sprawdza wartości tak:

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

Kiedy robisz to jednak, osadzasz kod językowy w swojej gramatyce i nie będzie on przenośny między celami.

0

Używając ANTLR4, przedstawiam prostą kombinowaną gramatykę, której użyłem. Używa lexera, aby dopasować tylko proste tokeny, pozostawiając zasady parsera interpretujące daty w porównaniu do liczb.

// 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];