Способ работы токенизации заключается в том, что он пытается найти самый длинный префикс ввода, который соответствует любому из ваших регулярных выражений, а затем создает соответствующий токен, используя этот префикс. Поэтому, когда вводом является 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
.