Как сообщить о нескольких ошибках, используя megaparse c? - PullRequest
2 голосов
/ 08 января 2020

За мегапарсный c документы, "Начиная с версии 8, одновременная отчетность по нескольким ошибкам синтаксического анализа стала намного проще." Мне не удалось найти ни одного примера этого. Единственное, что я нахожу, это это . Однако он показывает только, как анализировать игрушечный язык с разделителями новой строки, а также не показывает, как объединить несколько ошибок в ParseErrorBundle. Это ТАКОЕ обсуждение не является окончательным.

1 Ответ

1 голос
/ 10 января 2020

Вы хотите использовать withRecovery для восстановления от сгенерированных Megaparse c ошибок в сочетании с registerParseError (или registerFailure или registerFancyFailure), чтобы "зарегистрировать" эти ошибки (или ваши собственные сгенерированные ошибки) для отложенная обработка.

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

Вот очень простой пример, который анализирует разделенный запятыми список чисел :

import Data.Void
import Text.Megaparsec
import Text.Megaparsec.Char

type Parser = Parsec Void String

numbers :: Parser [Int]
numbers = sepBy number comma <* eof
  where number = read <$> some digitChar
        comma  = recover $ char ','
        -- recover to next comma
        recover = withRecovery $ \e -> do
          registerParseError e
          some (anySingleBut ',')
          char ','

При правильном вводе:

> parseTest numbers "1,2,3,4,5"
[1,2,3,4,5]

и при вводе с множественными ошибками:

> parseTest numbers "1.2,3e5,4,5x"
1:2:
  |
1 | 1.2,3e5,4,5x
  |  ^
unexpected '.'
expecting ','

1:6:
  |
1 | 1.2,3e5,4,5x
  |      ^
unexpected 'e'
expecting ','

1:12:
  |
1 | 1.2,3e5,4,5x
  |            ^
unexpected 'x'
expecting ',', digit, or end of input

Здесь есть некоторые тонкости. В последующем обрабатывается только первая ошибка синтаксического анализа:

> parseTest numbers "1,2,e,4,5x"
1:5:
  |
1 | 1,2,e,4,5x
  |     ^
unexpected 'e'
expecting digit

, и вам нужно внимательно изучить синтаксический анализатор, чтобы понять, почему. sepBy успешно применяет синтаксический анализатор number и comma в чередующейся последовательности для анализа "1,2,". Когда он достигает e, он применяет синтаксический анализатор number, который выходит из строя (потому что для some digitChar требуется хотя бы один символ di git). Это невосстановленная ошибка, поэтому синтаксический анализ немедленно прекращается без регистрации других ошибок, поэтому печатается только одна ошибка.

Кроме того, если вы отбросили <* eof из определения numbers (например, для сделав его частью более крупного анализатора), вы обнаружите, что:

> parseTest numbers "1,2,3.4,5"

дает ошибку разбора периода, но:

> parseTest numbers "1,2,3.4"

разбирает нормально. С другой стороны:

> parseTest numbers "1,2,3.4\n hundreds of lines without commas\nfinal line, with comma"

выдает ошибки разбора периода и запятой в конце файла.

Проблема в том, что синтаксический анализатор comma используется sepBy чтобы определить, когда закончился список номеров через запятую. Если синтаксический анализатор преуспеет (что он может сделать с помощью восстановления, сожрав сотни строк до следующей запятой), sepBy попытается продолжить работу; если синтаксический анализатор завершится неудачно (как изначально, так и из-за того, что код восстановления не может найти запятую после сканирования всего файла), sepBy завершится.

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

...