У меня есть парсер, который кажется достаточно простым. Я добавил этот подпарсер до конца, чтобы дать информацию об общих ошибках синтаксического анализа, так как все остальные подпарсеры потерпели неудачу -
/// Read the rest of a line as an error.
let readError =
parse {
let! restOfLineStr = restOfLine true
return makeViolation ("Read error on: " + restOfLineStr + ".") }
/// Read an expression.
do readExprRef :=
choice
[attempt readBoolean
attempt readCharacter
attempt readString
attempt readInt
attempt readError] // just now added this sub-parser, and get the issue
Однако, как только я добавляю readError в качестве выбора, я получаю страшную ошибку FParsec о потреблении потока во время выполнения - The combinator 'many' was applied to a parser that succeeds without consuming input and without changing the parser state in any other way.
Я не понимаю, почему я получаю это, так как я использую проанализированный остаток строки для создать структуру используемой ошибки (здесь «нарушение»).
Может кто-нибудь помочь мне понять это? Собираюсь ли я сообщать пользователю об ошибках парсера неправильно? Если нет, то как я могу это исправить?
Спасибо за помощь!
* Подробнее *
Вот еще немного кода, который может иметь отношение к делу -
/// The expression structure.
type Expr =
| Violation of Expr
| Boolean of bool
| Character of char
| String of string
| Int of int
/// Make a violation from a string.
let makeViolation str = Violation (String str)
/// Read whitespace character as a string.
let spaceAsStr = anyOf whitespaceChars |>> fun chr -> string chr
/// Read a line comment.
let lineComment = pchar lineCommentChar >>. restOfLine true
/// Read a multiline comment.
/// TODO: make multiline comments nest.
let multilineComment =
between
(pstring openMultilineCommentStr)
(pstring closeMultilineCommentStr)
(charsTillString closeMultilineCommentStr false System.Int32.MaxValue)
/// Read whitespace text.
let whitespace = lineComment <|> multilineComment <|> spaceAsStr
/// Skip any white space characters.
let skipWhitespace = skipMany whitespace
/// Skip at least one white space character.
let skipWhitespace1 = skipMany1 whitespace
/// Read a boolean.
let readBoolean =
parse {
do! skipWhitespace
let! booleanValue = readStr trueStr <|> readStr falseStr
return Boolean (booleanValue = trueStr) }
/// Read a character.
let readCharacter =
parse {
// TODO: enable reading of escaped chars
do! skipWhitespace
let! chr = between skipSingleQuote skipSingleQuote (manyChars (noneOf "\'"))
return Character chr.[0] }
/// Read a string.
let readString =
parse {
// TODO: enable reading of escaped chars
do! skipWhitespace
let! str = between skipDoubleQuote skipDoubleQuote (manyChars (noneOf "\""))
return String str }
/// Read an int.
let readInt =
parse {
do! skipWhitespace
let! value = pint32
let! _ = opt (skipString intSuffixStr)
do! notFollowedByLetterOrNameChar
do! notFollowedByDot
return Int value }
Я не знаю. Возможно, проблема в том, что он уже находится в конце потока, когда пытается запустить парсер readError. Будет ли это делать restOfLine не потреблять ввода, даже пробелы?
* Вывод *
Оказывается, что подход к отчёту об ошибках с парсером readError неверен. Правильный подход - использовать синтаксический анализатор «до конца», например, так:
/// Read the end of input.
let readEndOfInput = skipWhitespace >>. eof
// Read multiple exprs.
let readExprs = many readExpr
// Read exprs until the end of the input.
let readExprsTillEnd = readExprs .>> readEndOfInput
Теперь я просто запускаю readExprsTillEnd, когда мне нужно получить все выражения во входном потоке.
Еще раз спасибо, Густаво!