Antlr:Самый простой способ распознать даты и цифры?

StackOverflow https://stackoverflow.com/questions/141875

  •  02-07-2019
  •  | 
  •  

Вопрос

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

number: (MONTH|INT);

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

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

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

foo: STRING OP number -> ^(OP STRING number);
STRING: ('a'..'z')+;
OP: ('<'|'>');
Это было полезно?

Решение

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

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

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

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

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

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

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 */)}

Однако, когда вы делаете это, вы встраиваете в свою грамматику код, специфичный для конкретного языка, и он не будет переносимым между целевыми объектами.

Другие советы

Используя 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];
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top