Question

Quel est le moyen le plus simple (le plus court, le moins de règles et aucun avertissement) d’analyser les dates et les nombres valides dans la même grammaire? Mon problème est qu'une règle de lexer pour correspondre à un mois valide (1-12) correspondra à toute occurrence de 1-12. Donc, si je veux juste faire correspondre un nombre, j'ai besoin d'une règle d'analyse comme:

number: (MONTH|INT);

Cela devient de plus en plus complexe lorsque j'ajoute des règles de lexer pour le jour et l'année. Je veux une règle d’analyse de date comme celle-ci:

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

Je me fiche de savoir si mois, jour et heure; year sont des règles d’analyse ou de lexer, du moment que j’ai la même arborescence. Je dois également pouvoir reconnaître les numéros ailleurs, par exemple:

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

La solution

Le problème est que vous semblez vouloir effectuer à la fois une vérification syntaxique et sémantique dans votre lexer et / ou votre analyseur. C'est une erreur commune, et quelque chose qui n'est possible que dans des langages très simples.

Ce que vous devez vraiment faire est d’accepter plus largement le lexer et l’analyseur, puis d’effectuer des contrôles sémantiques. La rigidité de votre lexing dépend de vous, mais vous avez deux options de base, selon que vous devez ou non accepter les zéros précédant vos jours du mois: 1) Soyez vraiment réceptif pour vos INT, 2) définissez DATENUM comme suit: n'acceptez que les jetons qui sont des jours valides, mais pas des INT valides. Je recommande le second car il y aura moins de contrôles sémantiques nécessaires plus tard dans le code (puisque les INT seront alors vérifiables au niveau de la syntaxe et que vous ne devrez effectuer que des contrôles sémantiques sur vos dates. La première approche:

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

La deuxième approche:

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

Après avoir accepté d'utiliser ces règles dans le lexer, votre champ de date serait soit:

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

ou:

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

Ensuite, vous effectuerez une analyse sémantique sur votre AST pour vous assurer de la validité de vos dates.

Si vous êtes prêt à effectuer des vérifications sémantiques dans votre grammaire, ANTLR autorise les prédicats sémantiques dans l'analyseur. Vous pouvez ainsi créer un champ de date vérifiant les valeurs comme suit:

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

Ce faisant, vous intégrez du code spécifique à une langue dans votre grammaire, qui ne sera pas portable entre les cibles.

Autres conseils

En utilisant ANTLR4, voici une grammaire combinée simple que j'ai utilisée. Il utilise le lexer pour ne faire correspondre que des jetons simples, laissant les règles de l'analyseur pour interpréter les dates par rapport aux nombres.

// 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];
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top