Вы хотите использовать 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
завершится.
В конечном счете, запись восстанавливаемых синтаксических анализаторов является сложной задачей.