При выполнения вашего примера, FParsec генерирует исключение со следующим сообщением:
Дополнительной информации: (Ln: 2, Col: 8): комбинатор 'много' был примененной к синтаксическому анализатору удается без использования ввода и без изменить состояние анализатора любым другим способом. (Если исключение не было поднята, комбинатор вероятно вошел бы в бесконечный цикл.)
Проблема заключается в том, что ваш part
анализатор всегда удается, даже если он может только разобрать пустую строку. Вы можете решить эту проблему, заменив manyChars
в определении part
на many1Chars
.
Если вы ищете, например, «Применяется для синтаксического анализатора, который преуспевает, не потребляя вход» вы найдете несколько обсуждений подобных ошибок в Интернете, в том числе один в руководстве пользователя FParse в: http://www.quanttec.com/fparsec/users-guide/parsing-sequences.html#the-many-parser
Update: Вот простой определение парсер, который работает:
let sepPart = skipNewline
>>? (skipMany1SatisfyL ((=) '-') "'-'"
>>. (skipNewline <|> eof))
let part = many1CharsTill anyChar sepPart
let parser = many part
Обратите внимание, что я использую >>?
в определении sepPart
, чтобы позволить этому анализатору возвратиться к началу, если новая строка не следует тир. В качестве альтернативы вы также можете использовать attempt (skipNewline >>. ...)
, который также будет возвращаться к ошибкам после начальной тире. В документации для many[Chars]Till p endp
указывается эквивалентность с many (notFollowedBy endp >>. p) .>> endp
, что не является строгим, потому что many[Chars]Till
не отступает, как notFollowedBy
. Я уточню документацию.
Это лучше для производительности, если вы избегаете обратного отсчета с использованием many[Chars]Till
или notFollowedBy
, где это возможно. Например, вы могли бы также разобрать свои куски строк следующим образом:
let id = manyMinMaxSatisfyL 2 2 isUpper "id (two capital letters)"
let line = id .>>. (pchar ' ' >>. restOfLine true)
let separator = many1SatisfyL ((=) '-') "dash separator"
>>. (skipNewline <|> eof)
let chunk = many1 line
let parser = sepEndBy1 chunk separator
Обратите внимание, что эта реализация не требует последней порции должна быть завершена сепаратором. Если вы хотите, что вы могли бы использовать вместо:
let chunk = many line .>> separator
let parser = many chunk
Если вы хотите, чтобы пустые куски с sepEndBy
определения, вы можете использовать:
let chunk = many1 line <|> (notFollowedByEof >>% [])
let parser = sepEndBy1 chunk separator
Это делает подавить ошибку, однако она не возвращается правильный результат.Если вы посмотрите, что возвращается первым анализом {test part s}, вы увидите, что результат изменится, когда manyChars будет изменен на many1Chars в парсере деталей. – JonnyBoats
Я не вижу разницы, за исключением того, что 'part' возвращает строку и' many part' возвращает строку в списке. Не могли бы вы быть более конкретными и объяснить, какой результат вы ожидаете? Ваш парсер никогда не пропускает 'sepPart', поэтому, возможно, вам нужно что-то вроде' many (part. >> sepPart) '. Если вы хотите совместить один или несколько тире для разделителя, вам также нужно будет использовать что-то вроде 'skipMany1 (pstring" - ")' или 'skipMany1SatisfyL ((=) '-')" '-' "' в определение 'sepPart'. –
Я отредактировал вопрос, чтобы лучше описать ожидаемый результат. Я утверждаю, что не стал более понятным для начала. – JonnyBoats