Синтаксический анализ комментариев к блоку Megaparsec с использованием символов начала и конца

Я хочу проанализировать текст, похожий на этот в Haskell, используя Megaparsec.

# START SKIP
def foo(a,b):
    c = 2*a # Foo 
    return a + b
# END SKIP

, где # START SKIP а также # END SKIP отмечает начало и конец блока текста для анализа.

По сравнению с skipBlockComment я хочу, чтобы парсер возвращал строки между маркером начала и конца.

Это мой парсер.

skip :: Parser String
skip = s >> manyTill anyChar e
  where s = string "# START SKIP"
        e = string "# END SKIP"

skip Парсер работает как задумано.

Чтобы разрешить переменное количество пустого пространства в начале и конце маркера, например # START SKIP Я пробовал следующее:

skip' :: Parser String
skip' = s >> manyTill anyChar e
  where s = symbol "#" >> symbol "START" >> symbol "SKIP"
        e = symbol "#" >> symbol "END" >> symbol "SKIP"

С помощью skip' для разбора вышеприведенного текста выдается следующая ошибка.

3:15:
unexpected 'F'
expecting "END", space, or tab

Я хотел бы понять причину этой ошибки и как я могу это исправить.

1 ответ

Решение

Как уже заметил Алек, проблема в том, что как только e встречи '#', он считается потребляемым персонажем. И способ работы parsec и его производных заключается в том, что как только вы используете любые символы, вы переходите на эту ветвь синтаксического анализа - т.е. manyTill anyChar альтернатива тогда не рассматривается, даже если e в конечном итоге терпит неудачу здесь.

Вы можете легко запросить возврат, хотя, оборачивая конечный разделитель в try:

skip' :: Parser String
skip' = s >> manyTill anyChar e
  where s = symbol "#" >> symbol "START" >> symbol "SKIP"
        e = try $ symbol "#" >> symbol "END" >> symbol "SKIP"

Это тогда будет, прежде чем потреблять '#' установить "контрольную точку", и когда e не удается позже (в вашем примере, в "Foo"), он будет действовать так, как если бы ни один символ не совпадал вообще.

На самом деле, традиционный парсек даст такое же поведение и для skip, Просто потому, что поиск строки и ее успешное выполнение только в том случае, если она полностью совпадает, является такой распространенной задачей, мегапарсек string реализован как try . string То есть, если сбой происходит в пределах этой фиксированной строки, он всегда будет возвращаться назад.

Однако составные парсеры по-прежнему не возвращаются по умолчанию, как в attoparsec. Основная причина заключается в том, что если что-то может вернуться к какой-либо точке, вы не сможете получить четкую точку сбоя в сообщении об ошибке.

Другие вопросы по тегам