ANTLR соответствует идентификатору, но не зарезервированным ключевым словам

#java #parsing #antlr4 #identifier

#java #синтаксический анализ #antlr4 #идентификатор

Вопрос:

Я пытаюсь сопоставить комплексные числа, используя разные обозначения, одно из которых использует cis функцию как таковую: cis ФАЗА МОДУЛЯ

Проблема в том, что мое правило идентификатора соответствует cis , а также началу следующего за ним числа, и поскольку оно больше, чем сам CIS токен, оно всегда возвращает тип токена идентификатора. Как я мог этого избежать?

Вот грамматика :

 grammar Sandbox;

input : number? CIS UNSIGNED 
    | IDENTIFIER
    ;

number : FLOAT
    | UFLOAT 
    | UINT
    | INT
    ;

fragment DIGIT : [0-9] ;

UFLOAT : UINT (DOT UINT? | 'f') ;
FLOAT : SUB UFLOAT ;
UINT : DIGITS ;
INT : SUB UINT ;
UNSIGNED : UFLOAT 
    | UINT 
    ;
DIGITS : DIGIT  ;

// Specific lexer rules
CIS : 'cis' ;
SUB : '-' ; 
DOT : '.' ;
WS : [ t]  -> skip ;
NEWLINE : 'r'? 'n' ;

IDENTIFIER : [a-zA-Z_] [a-zA-Z0-9_]* ;  // has to be after complex so i or cis doesn't match this first
 

Редактировать:
Входные данные, которые я пытался проанализировать, являются сложными 1 i , но используют их соответствующий модуль и фазу следующим образом : 1.4142135623730951cis0.7853981633974483

И моя реальная проблема заключается в том, что правило ИДЕНТИФИКАТОРА совпадает cis0 , а не просто соответствует правилу CIS lexer, даже если оно определено перед ним.

Я смутно знаю, что ANTLR выбирает правило на основе наибольшего совпадения, но в этом случае я хочу избежать этого =o .

Комментарии:

1. Не главная проблема, но в [a-zA-Z_] [a-zA-Z0-9_]* этом нет необходимости.

2. Ах, это действительно так, поскольку только первый символ имеет это ограничение, верно? = O Спасибо, я это исправлю

3. В MODULUS cis PHASE MODULUS и PHASE идентификаторы или числа? Покажите фактический пример ввода, который анализируется неправильно. Кроме того, я считаю, что в вашей грамматике много проблем. Например, IMAGINARY : IM UNSIGNED? совпадения, например i 3 .

4. да, точно, в основном они могут быть либо числами с плавающей запятой, либо целыми числами, но единственное отличие состоит в том, что если это мнимая часть, она должна быть без знака, чтобы /- обрабатывался до этого (я думаю). Вот один пример, который я хотел проанализировать: 1.4142135623730951cis0.7853981633974483 это комплекс 1 i , поэтому модуль и аргументы являются этими двумя значениями соответственно

5. Я удалил все остальные правила лексера и синтаксического анализатора, за исключением правила cis удаления примера

Ответ №1:

Я вижу здесь два решения:

  1. Сделайте комплексное число правилом одного лексера:
 COMPLEX:  (FLOAT | UFLOAT | UINT | INT) WS* CIS WS* UNSIGNED;
 

который будет длиннее идентификатора или ключевого слова pur CIS (и, следовательно, соответствует первому).

  1. Второе cis слово — это ключевое слово, когда оно следует за цифрой (с необязательными пробелами между ними), верно? Итак, вы можете выполнить обратный просмотр ( LA(-1) в вашем предикате, чтобы отклонить cis как идентификатор, если это условие истинно.

Я бы предпочел решение 1, потому что соглашение заключается в том, что отдельные объекты (а комплексное число, такое как число с плавающей запятой или строка, является одним логическим объектом) полностью совпадают в правиле лексера, а не в правиле синтаксического анализатора.

Комментарии:

1. Ага, это хороший трюк, я обязательно подумаю об этом, если у меня когда-нибудь возникнут проблемы с приоритетом правил. Я пометил это как ответ, но для второго подхода меня все еще интересует обратная связь, это то же самое, что и для регулярного выражения? Есть ли ссылка, где я тоже могу прочитать об этом = o

2. В ANTLR не используется и не поддерживается регулярное выражение. Но да, оглядываться назад — это как смотреть вперед, только с отрицательным смещением. LA() В лексере есть функция, которую вы можете использовать для проверки символов во входном потоке относительно текущей позиции. Аналогичная функция LT() (для токена look ahead) существует в анализаторе для получения определенного токена относительно текущей позиции токена.

3. Я прочитал справочник ANTLR, и на данный момент я дошел только до страниц 210 ~, поэтому я предполагаю, что это произойдет после ха-ха. Я изучу LA , и LT поскольку это то, что я действительно хотел иметь возможность использовать, хе-хе. Большое спасибо за помощь информация!

Ответ №2:

Я просто помещаю это здесь, потому что я думаю, что это может быть потенциальным решением, хотя я бы предпочел не использовать семантические предикаты, потому что это привязывает мою грамматику к целевому / конкретному языку =/ (я никогда не использовал их раньше, поэтому я не уверен, есть ли какие-либо другие предостережения):

 IDENTIFIER: [a-zA-Z_][a-zA-Z0-9_]* { identifierIsNotReserved() }?;
 

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

Семантический предикат представляет собой блок произвольного кода на целевом языке, окруженный символом {…}?, который принимает логическое значение. Если возвращаемое значение равно false, правило лексера пропускается.

Редактировать: забыл добавить ссылку на то, где я это нашел, вот она: https://riptutorial.com/antlr/example/11237/actions-and-semantic-predicates

Комментарии:

1. Выбор такого решения означал бы, что вы бы отказались от создания любого идентификатора, начинающегося с ключевого слова (вы собираетесь отклонить cis0 , верно?). Нет ли случаев, когда cis0 (или аналогичных) является допустимым идентификатором?

2. Хотел опубликовать ответ, но я вижу, что Майк уже это сделал (и я бы ответил на что-то похожее на его вариант 1).