2013-05-19 2 views
3

У меня есть эта тестовая программа:FParsec неудача на многих

open FParsec 

let test p str = 
    match run p str with 
    | Success(result, _, _) -> printfn "Success: %A" result 
    | Failure(errorMsg, _, _) -> printfn "Failure: %s" errorMsg 

let str s = pstring s 

let sepPart = skipNewline >>. pstring "-" 

let part = manyChars (notFollowedBy sepPart >>. anyChar) 

[<EntryPoint>] 
let main argv = 
    let s = "AA 12345\nBB 6789\n-----\nCC 9876\nDD 54321\n-----" 
    test part s 
    test (many part) s 

    0 // return an integer exit code 

Линия {часть тест s} работает, как ожидался, но следующая строка {теста (много части) s} терпит неудачу, и я не понимаю что я делаю неправильно.

EDIT:

Чтобы уточнить, что я пытаюсь сделать, это {тест (много части) s} возвращение [ "AA 12345 \ NBB 6789"; «CC 9876 \ nD 54321»]. На словах, у меня есть входная строка, состоящая из «парсов» или «кусков», разделенных линиями со всеми тире. Для вывода я хочу массив, где каждый элемент является одной из частей, а строки с тире просто отбрасываются.

ответ

8

При выполнения вашего примера, 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 
+0

Это делает подавить ошибку, однако она не возвращается правильный результат.Если вы посмотрите, что возвращается первым анализом {test part s}, вы увидите, что результат изменится, когда manyChars будет изменен на many1Chars в парсере деталей. – JonnyBoats

+0

Я не вижу разницы, за исключением того, что 'part' возвращает строку и' many part' возвращает строку в списке. Не могли бы вы быть более конкретными и объяснить, какой результат вы ожидаете? Ваш парсер никогда не пропускает 'sepPart', поэтому, возможно, вам нужно что-то вроде' many (part. >> sepPart) '. Если вы хотите совместить один или несколько тире для разделителя, вам также нужно будет использовать что-то вроде 'skipMany1 (pstring" - ")' или 'skipMany1SatisfyL ((=) '-')" '-' "' в определение 'sepPart'. –

+0

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

Смежные вопросы