Правило Lexer распознается там, где оно не нужно

пытаясь использовать ANTLR 4 для создания простой грамматики для некоторых операторов Select в Oracle DB. И столкнулся с небольшой проблемой. У меня есть следующая грамматика:

Grammar & Lexer

column
: (tableAlias '.')? IDENT ((AS)? colAlias)?
| expression ((AS)? colAlias)?
| caseWhenClause ((AS)? colAlias)?
| rankAggregate ((AS)? colAlias)?
| rankAnalytic colAlias
;

colAlias
: '"' IDENT '"'
| IDENT
;

rankAnalytic
: RANK '(' ')' OVER '(' queryPartitionClause orderByClause ')'
;

RANK: R A N K;
fragment A:('a'|'A');
fragment N:('n'|'N');
fragment R:('r'|'R');
fragment K:('k'|'K');

Самая важная часть находится в объявлении COLUMN, часть rankAnalytic. Я объявил, что после оператора Rank должен быть colAlias, но в случае, если этот colAlias ​​называется как "rank" (без кавычек), он распознается как правило лексера RANK, а не как colAlias.

Так, например, если у меня есть следующий текст:

 SELECT fulfillment_bundle_id, SKU, SKU_ACTIVE, PARENT_SKU, SKU_NAME, LAST_MODIFIED_DATE,
 RANK() over (PARTITION BY fulfillment_bundle_id, SKU, PARENT_SKU 
 order by ACTIVE DESC NULLS LAST,SKU_NAME) rank

Псевдоним "rank" будет подчеркнут и помечен как ошибочный со следующей ошибкой:
несоответствующий ввод "rank", ожидающий {'"', IDENT}
Но дело в том, что я не не хочу, чтобы оно распознавалось как слово лексера RANK, а только как псевдоним для столбца.
Открыто для ваших предложений :)


person Maks Karashchuk    schedule 18.10.2017    source источник


Ответы (1)


Правило RANK, по-видимому, появляется над правилом IDENT, поэтому строка «rank» никогда не будет выдаваться лексером как токен IDENT.

Простое исправление — изменить правило colAlias:

colAlias
    : '"' ( IDENT | RANK ) '"'
    | ( IDENT | RANK ) 
    ;

ОП добавил:

Хорошо, но если у меня есть не только RANK в качестве правила лексера, но и весь список (> 100) таких ключевых слов... Что мне делать?

Если colAlias может быть буквально чем угодно, то пусть это будет:

colAlias
    : '"' .+? '"'    // must quote if multiple
    | .              // one token
    ;

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

colAlias
    : '"' m+=.+? '"' { check($m) }?  // multiple
    | o=.            { check($o) }?  // one 
    ;

Функционально предикат — это просто еще один элемент субправила.

person GRosenberg    schedule 18.10.2017
comment
Хорошо, но если у меня есть не только RANK как правило лексера, но и весь список (>100) таких ключевых слов... Что мне делать? Перечислите их все, как показано выше? Я думаю, что в таком случае список будет очень длинным, и мне нужно будет обновлять его каждый раз, когда добавляется новый лексер. - person Maks Karashchuk; 19.10.2017
comment
Обновленный ответ. - person GRosenberg; 19.10.2017