Pregunta

¿Cuál es la forma más simple (más corta, menos reglas y sin advertencias) de analizar fechas y números válidos en la misma gramática? Mi problema es que una regla lexer que coincida con un mes válido (1-12) coincidirá con cualquier ocurrencia de 1-12. Entonces, si solo quiero hacer coincidir un número, necesito una regla de análisis como:

number: (MONTH|INT);

Solo se vuelve más complejo cuando agrego reglas de lexer para el día y el año. Quiero una regla de análisis para una fecha como esta:

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

No me importa si mes, día y amp; Los años son reglas de análisis o lectura, siempre y cuando termine con la misma estructura de árbol. También necesito poder reconocer números en otro lugar, por ejemplo:

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

Solución

El problema es que parece querer realizar comprobaciones sintácticas y semánticas en su lexer y / o su analizador. Es un error común, y algo que solo es posible en lenguajes muy simples.

Lo que realmente necesita hacer es aceptar de forma más amplia en el lexer y el analizador, y luego realizar verificaciones semánticas. Qué tan estricto sea su nivel de flexibilidad depende de usted, pero tiene dos opciones básicas, dependiendo de si necesita o no aceptar los ceros antes de los días del mes: 1) Estar realmente aceptando para sus INT, 2) definir DATENUM para solo acepta aquellos tokens que son días válidos, pero no INT válidos. Recomiendo el segundo porque habrá menos controles semánticos necesarios más adelante en el código (ya que los INT serán verificables en el nivel de sintaxis y solo tendrá que realizar controles semánticos en sus fechas. El primer enfoque:

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

El segundo enfoque:

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

Después de aceptar el uso de estas reglas en el lexer, su campo de fecha sería:

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

o:

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

Después de eso, realizarías una ejecución semántica sobre tu AST para asegurarte de que tus fechas son válidas.

Sin embargo, si está totalmente decidido a realizar verificaciones semánticas en su gramática, ANTLR permite predicados semánticos en el analizador, por lo que podría hacer un campo de fecha que verifique los valores de esta manera:

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

Sin embargo, cuando haces esto, estás incrustando un código específico de idioma en tu gramática, y no será portátil para todos los objetivos.

Otros consejos

Usando ANTLR4, aquí hay una simple gramática combinada que utilicé. Hace uso del lexer para hacer coincidir tokens simples, dejando las reglas del analizador para interpretar las fechas frente a los 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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top