37.3 Написание правил PEG

Важно знать при написании правил PEG, что они жадные (greedy). Правила, которые могут потреблять переменное количество текста, всегда будут потреблять максимально возможное количество, даже если это приведет к сбою правила, которое в противном случае могло бы совпасть – нет обратного отслеживания (backtracking). Например, это правило никогда не сработает:

(forest (+ "tree" (* [blank])) "tree" (eol))

Выражение PEX (+ "tree" (* [blank])) потребляет все повторения слова ‘tree’, не оставляя ни одного для совпадения с финальным ‘tree’.

В таких ситуациях желаемый результат можно получить, используя предикаты и гварды – а именно выражения not, if и guard – для ограничения поведения. Например:

(forest (+ "tree" (* [blank])) (not (eol)) "tree" (eol))

Операторы if и not принимают выражение парсинга и интерпретируют его как булево значение, не перемещая указатель. Содержимое оператора guard оценивается как обычный Lisp (а не PEX) и должно возвращать булево значение. Значение nil приводит к сбою совпадения.

Другое потенциально неожиданное поведение заключается в том, что парсинг будет перемещать указатель как можно дальше, даже если парсинг в конечном итоге завершится неудачей. Это правило:

(end-game "game" (eob))

при запуске в буфере, содержащем текст “game over” после указателя, переместит указатель сразу после “game”, а затем остановит парсинг, вернув nil. Успешный парсинг всегда вернет t или контексты стека парсинга.