Q: ANTLR 4 Грамматическое распознавание всего нечетного значения, а не только последней цифры - PullRequest
0 голосов
/ 08 ноября 2018

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

Например, это работает так: Если я поставлю 123, результат будет 123. Если я поставлю 1234, то результат будет 123, а ошибка распознавания токена: 4, но должна быть: 1234.

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

grammar G;
DIGIT:    ('0'..'9') * ('1' | '3' | '5' | '7'| '9');
operator : ('+' | '-' | '*' | ':');
result: DIGIT operator (DIGIT | result);

Я имею в виду, специально, чтобы 1234 должен быть признан ошибкой, а не только последней цифрой.

1 Ответ

0 голосов
/ 08 ноября 2018

Способ работы токенизации заключается в том, что он пытается найти самый длинный префикс ввода, который соответствует любому из ваших регулярных выражений, а затем создает соответствующий токен, используя этот префикс. Поэтому, когда вводом является 1234, он видит 123 как самый длинный префикс, соответствующий шаблону DIGIT (который на самом деле должен называться ODD_INT или что-то в этом роде), и генерирует соответствующий токен. Затем он видит оставшиеся 4 и выдает ошибку, потому что ни одно правило не соответствует ему.

Обратите внимание, что это не обязательно только последняя цифра, которая выдает ошибку. Для ввода 1324 он выдаст токен DIGIT для 13, а затем ошибку распознавания токена для 24.

Так как вы можете получить поведение, которое вы хотите? Один из подходов заключается в том, чтобы переписать ваш шаблон, чтобы он соответствовал всем последовательностям цифр, а затем использовать семантический предикат, чтобы убедиться, что число нечетное. Способ, которым семантические предикаты работают с правилами лексера, заключается в том, что сначала он берет самый длинный префикс, соответствующий шаблону (без учета предиката), а затем проверяет предикат. Если предикат имеет значение false, он переходит к другим шаблонам - он не пытается сопоставить этот же шаблон с меньшим входным значением, чтобы предикат возвращал значение true. Таким образом, для ввода 1234 шаблон будет соответствовать целому числу, а затем предикат вернет false. Затем он попробует другие шаблоны, ни один из которых не подходит, поэтому вы получите ошибку распознавания токена для полного числа.

ODD_INT: ('0'..'9') + { Integer.parseInt(getText()) % 2 == 1 }?;

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

Кроме того, вы можете просто распознать все целые числа в лексере - не только нечетные - и затем проверить, не являются ли они нечетными позже во время семантического анализа.

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

ODD_INT: ('0'..'9') * ('1' | '3' | '5' | '7'| '9');
EVEN_INT: ('0'..'9') * ('0' | '2' | '4' | '6'| '8');

Таким образом, для ввода типа 1234 самое длинное совпадение всегда будет 1234, а не 123. Просто это будет соответствовать шаблону EVEN_INT, а не ODD_INT. Таким образом, вы не получите ошибку распознавания токена, но, если вы будете последовательно использовать только ODD_INT в грамматике, вы получите сообщение о том, что ожидалось ODD_INT, но найдено EVEN_INT.

...