Ваше использование not
является избыточным.Метод |
реализует упорядоченный выбор ;вторая вещь пробуется, только если первая потерпела неудачу.Это должно сработать:
def directive: Parser[Directive] =
( '$' ~>
( '{' ~> javaStuff <~ '}'
| "include" ~> includeDirective
| "def" ~> defDirective
| "val" ~> valDirective
| javaDirective
)
| htmlDirective
)
def templateFile: Parser[List[Directive]] = (directive <~ '\n').*
Для более быстрого разбора и более качественных сообщений об ошибках вы должны "фиксировать" ваши парсеры как можно чаще.Я думаю, что это то, что вы пытались получить, когда использовали not('{')
.
Прямо сейчас, если приведенный выше парсер видит '$'
, за которым следует '{'
, а затем нет см. javaStuff
, он вернется и рассмотрит каждую из четырех оставшихся '$'
-альтернатив в порядке (include
, def
, val
и, наконец, javaDirective
), а затем вернется кдо '$'
, чтобы попробовать htmlDirective
, до сбоя с ошибочным сообщением об ошибке.Но если мы видим '{'
, мы знаем, что ни одна из других альтернатив не может быть успешной, так почему мы должны их проверять?Аналогично, строка, начинающаяся с '$'
, никогда не может быть htmlDirective
.
Мы хотим, чтобы такие вещи, как '{'
, были точками без возврата;если синтаксический анализатор after- '{'
завершается с ошибкой и хочет вернуться назад, мы должны остановить его на своем пути и передать сбой, вызывающий возврат, непосредственно пользователю как ошибку.
Способ сделать это с помощью commit
.Эта функция / комбинатор, когда применяется к парсеру p
, смотрит на ParseResult
, выходящий из p
, и изменяет его на Error
(сигнал отказа от цели), если он изначально был Failure
(сигнал возврата), в противном случае он остается неизменным.При правильном использовании commit
синтаксический анализатор directive
становится:
def directive: Parser[Directive] =
( '$' ~> commit( '{' ~> commit(javaStuff <~ '}')
| "include" ~> commit(includeDirective)
| "def" ~> commit(defDirective)
| "val" ~> commit(valDirective
| javaDirective
)
| htmlDirective
)
Когда я впервые научился использовать библиотеку синтаксического анализа, я нашел действительно полезным взглянуть на исходный код для Parsers
;это делает некоторые вещи более понятными.
(Несколько других советов: цель append
и ParseResult#append
состоит в том, чтобы решить, какой сбой из последовательности разбора альтернатив следует распространить на пользователя. Просто пока проигнорируйте их. Также я бы не сталне беспокойтесь слишком о >>
/ flatMap
/ into
, пока не начнете больше практиковаться, когда придет время, прочитайте объяснение Даниэля Собрала . Наконец, мне никогда не приходилось использовать|||
, и вы, вероятно, тоже не будете. Удачного разбора!)
Надеюсь, это поможет.