Domanda

Qual è il modo più semplice (il più breve, il minor numero di regole e nessun avviso) per analizzare sia date che numeri validi nella stessa grammatica? Il mio problema è che una regola lexer per abbinare un mese valido (1-12) corrisponderà a qualsiasi occorrenza di 1-12. Quindi, se voglio solo abbinare un numero, ho bisogno di una regola di analisi come:

number: (MONTH|INT);

Diventa più complesso solo quando aggiungo regole lexer per giorno e anno. Voglio una regola di analisi per data come questa:

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

Non mi interessa se mese, giorno e amp; l'anno sono le regole di analisi o lexer, purché finisca con la stessa struttura ad albero. Devo anche essere in grado di riconoscere numeri altrove, ad esempio:

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

Soluzione

Il problema è che sembra che tu voglia eseguire controlli sia sintattici che semantici nel tuo lexer e / o nel tuo parser. È un errore comune e qualcosa che è possibile solo in lingue molto semplici.

Quello che devi veramente fare è accettare più ampiamente nel lexer e nel parser e quindi eseguire controlli semantici. Quanto sei severo nel tuo lexing dipende da te, ma hai due opzioni di base, a seconda che tu debba o meno accettare gli zero che precedono i tuoi giorni del mese: 1) Accettare davvero i tuoi INT, 2) definire DATENUM per accetta solo quei token che sono giorni validi, ma non INT validi. Raccomando il secondo perché ci saranno meno controlli semantici necessari più avanti nel codice (poiché gli INT saranno quindi verificabili a livello di sintassi e dovrai solo eseguire controlli semantici nelle tue date. Il primo approccio:

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

Il secondo approccio:

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

Dopo aver accettato di usare queste regole nel lexer, il tuo campo data sarà:

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

o

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

Successivamente, eseguirai una corsa semantica sul tuo AST per assicurarti che le tue date siano valide.

Se sei impegnato a eseguire controlli semantici nella tua grammatica, tuttavia, ANTLR consente predicati semantici nel parser, quindi potresti creare un campo data che controlla i valori in questo modo:

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

Quando lo fai, tuttavia, stai incorporando un codice specifico della lingua nella tua grammatica e non sarà trasportabile su più destinazioni.

Altri suggerimenti

Usando ANTLR4, ecco una semplice grammatica combinata che ho usato. Utilizza il lexer per abbinare solo token semplici, lasciando le regole del parser per interpretare date e numeri.

// 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];
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top