Antlr: Самый простой способ узнать даты и цифры? - PullRequest
5 голосов
/ 27 сентября 2008

Каков самый простой (самые короткие, наименьшие правила и без предупреждений) способ разбора как действительных дат, так и чисел в одной и той же грамматике? Моя проблема заключается в том, что правило лексера, совпадающее с действительным месяцем (1-12), будет соответствовать любому вхождению 1-12. Поэтому, если я просто хочу сопоставить число, мне нужно правило разбора, например:

number: (MONTH|INT);

Все становится сложнее, когда я добавляю правила лексера для дня и года. Я хочу разобрать правило для даты, как это:

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

Мне все равно, являются ли месяц, день и год правилами синтаксического анализа или лексера, лишь бы я в конечном итоге имел ту же древовидную структуру. Мне также нужно иметь возможность распознавать номера в другом месте, например ::100100

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

Ответы [ 2 ]

5 голосов
/ 27 сентября 2008

Проблема в том, что вы, похоже, хотите выполнять синтаксическую и семантическую проверку в вашем лексере и / или в вашем анализаторе. Это распространенная ошибка, которая возможна только на очень простых языках.

Что вам действительно нужно сделать, так это принять более широко в лексере и парсере, а затем выполнить семантические проверки. Насколько вы строгие в своем лексизме, зависит от вас, но у вас есть два основных варианта, в зависимости от того, нужно ли вам принимать нули, предшествующие вашим дням месяца: 1) действительно принимайте за INT, 2) определяйте DATENUM принимайте только те токены, которые являются действительными днями, но не являются действительными INT. Я рекомендую второй, потому что в коде будет меньше семантических проверок, необходимых позже (поскольку INT будут проверяться на уровне синтаксиса, и вам нужно будет только выполнять семантические проверки ваших дат. Первый подход:

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

Второй подход:

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

После принятия этих правил в лексере поле даты будет либо:

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

или

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

После этого вы выполняете семантическую проверку вашего AST, чтобы убедиться, что ваши даты действительны.

Если вы не можете выполнять семантические проверки в своей грамматике, ANTLR разрешает семантические предикаты в синтаксическом анализаторе, поэтому вы можете создать поле даты, которое проверяет такие значения:

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

Однако, когда вы делаете это, вы встраиваете в язык грамматику, зависящую от языка, и она не переносится на цели.

0 голосов
/ 30 июля 2015

Используя ANTLR4, вот простая комбинированная грамматика, которую я использовал. Он использует лексер для сопоставления только простых токенов, оставляя правила синтаксического анализатора для интерпретации дат и чисел.

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