Pergunta

O que é o mais simples (, regras mais curtos menor número, e sem avisos) maneira de analisar as duas datas válidas e números na mesma gramática? Meu problema é que uma regra lexer para corresponder a um mês válido (1-12) irá corresponder a qualquer ocorrência de 1-12. Então, se eu só quero corresponder a um número, eu preciso de uma regra de análise como:

number: (MONTH|INT);

Ele só se torna mais complexa quando eu adicionar regras lexer para o dia e ano. Quero uma regra de análise para a data como esta:

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

Eu não me importo se mês, dia e ano são regras sintáticas ou lexer, contanto que eu acabar com a mesma estrutura de árvore. Eu também preciso ser capaz de reconhecer os números em outros lugares, por exemplo:.

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

Solução

O problema é que você parece querer executar tanto a verificação sintática e semântica em sua lexer e / ou o seu analisador. É um erro comum, e algo que só é possível em linguagens muito simples.

O que você realmente precisa fazer é aceitar de forma mais ampla no lexer e analisador, e em seguida, executar verificações de semântica. Como estrita você está no seu léxico é com você, mas você tem duas opções básicas, dependendo se ou não você precisa aceitar zeros precedentes seus dias do mês: 1) ser realmente aceitar para seus INTs, 2) definir DATENUM para só aceitam esses símbolos que são dias válidos, ainda não INTs válidos. Eu recomendo o segundo, porque haverá verificações menos semânticas necessárias mais tarde no código (desde INTs, então, ser verificáveis ??em nível sintaxe e você só vai precisar para realizar verificações de semântica sobre as datas A primeira abordagem:.

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

A segunda abordagem:

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

Depois de aceitar usando essas regras no lexer, o seu campo de data iria ser:

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

ou

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

Depois disso, você iria realizar uma corrida semântica sobre sua AST ter certeza de que suas datas são válidas.

Se você está morto conjunto sobre a realização de verificações de semântica na sua gramática, no entanto, ANTLR permite predicados semânticos no analisador, de modo que você pode fazer um campo de data que verifica os valores como este:

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

Quando você fizer isso, no entanto, você está embutindo o código específico linguagem em sua gramática, e não vai ser portátil através de alvos.

Outras dicas

Usando ANTLR4, aqui é um simples gramática combinado que eu usei. Ele faz uso do lexer para coincidir com símbolos simples somente, deixando as regras do analisador para interpretar datas vs números.

// 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];
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top