2011-12-17 3 views
5

У меня есть парсер, который кажется достаточно прямым. Я добавил этот суб-парсер до конца, чтобы дать информацию об общих ошибок синтаксического анализа, так как все остальные суб-парсеры не удалось -Как разрешить ошибку FParsec «Комбинатор« многие »был применен к парсеру, который преуспевает, не потребляя ...»

/// 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 

Однако, как только я добавляю Ошибка чтения как выбор, я получаю страшились 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 не потреблять вход, даже не пробелы?

* Заключение *

Оказывается, что подход к ошибкам отчетов с анализатором Ошибка чтения является неправильным. Правильный подход заключается в использовании «до конца» парсер, как так -

/// 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, когда мне нужно, чтобы получить все exprs во входном потоке.

Еще раз спасибо, Густаво!

+0

Вы можете разместить больше кода? В частности, тело функции makeViolation. – Gustavo

+0

Готово. BTW, Густаво, я большой поклонник вашего блога. Надеюсь, что Дон Симе смотрит на работу, которую вы сделали - получение функторов, аппликаций и монадов без HK или TC огромно! Интересно, можем ли мы также получить от вас стрелы в какой-то форме;) –

+0

Спасибо Брайан за ваши отзывы о моей работе с Typeclasses. Я также надеюсь, что люди из команды F # могут добавить дополнительную поддержку для этой техники, иначе они будут зависеть от команды CLR для реализации реальной поддержки Typeclasses на уровне .NET. Что касается стрелок, вы посмотрели http://stackoverflow.com/questions/4034802/how-would-i-translate-a-haskell-type-class-into-f? В проекте содержится больше информации о стрелках на http://code.google.com/p/fsharp-typeclasses. – Gustavo

ответ

1

Спасибо за дополнительный код, который вы опубликовали, к сожалению, я не смог воспроизвести ошибку. Но почему вы не пытаетесь удалить последние attempt? Я думаю, что это не имеет смысла и, возможно, вызывает проблемы.

do readExprRef := 
    choice 
     [attempt readBoolean 
     attempt readCharacter 
     attempt readString 
     attempt readInt 
     readError] 

Я не эксперт FParsec, но я думаю, что последний парсер выбора не должен быть попыткой.

UPDATE:

readError анализатор не преуспевает даже потребляя никакого ввода, если в какой-то момент у вас есть вызов readExpr в качестве параметра в many он никогда не заканчивается. Я имею в виду, если вы звоните

run (many readError) "" ;; 

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

Посмотрите на функцию функции restOfLine по адресу http://www.quanttec.com/fparsec/reference/charparsers.html#members.restOfLine, это предупреждает вас об этом.

Теперь есть много способов решить эту проблему, но я бы сказал, что вам придется пересмотреть то, как вы обрабатываете ошибки парсера.

Одна вещь, которую вы можете сделать, это взять на себя функцию readError, а затем при вызове readExpr парсер вы называете это таким образом

let readExprs = many readExpr .>> eof 

таким образом вы соблюдение ВФ и если есть что-то не управляется парсеров в выборе перед eof, FParsec автоматически генерирует приятное сообщение об ошибке для вас.

И если вы хотите обработать эту ошибку, посмотрим на http://www.quanttec.com/fparsec/users-guide/customizing-error-messages.html

+0

Я удалил последнюю «попытку», и она по-прежнему имеет такое же поведение :(Не могли бы вы лично рассказать об этом? Если нет, пожалуйста, внесите мне свою контактную информацию на [email protected] Cheers! –

+0

Да, это работает! Я разработал окончательное решение в ответ, используя вашу большую помощь! Спасибо вам огромное! –