Почему FParsec не использует символы, разбирающие разделитель списка? - PullRequest
0 голосов
/ 01 июня 2018

Фактический сценарий ниже составлен.Цель этого вопроса - понять, что здесь делает FParsec.

Я анализирую список строк (w) и (x), разделенных одним или несколькими пробелами ' '.

Анализатор для моего списка xs использует sepBy с анализатором-разделителем isSeparator.

isSeparator основан на manySatisfy и, похоже, правильно использует пробелы.Я полагаю, что это можно увидеть в тестовом выводе ниже, когда он анализирует два начальных пробела, он заканчивается в позиции 3.

Однако он не работает, когда я использую его в xs, как показано ниже.

Почему это терпит неудачу и каков будет хороший подход для работы с разделителем, который может быть одним или несколькими пробелами?

open FParsec

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

let str s = pstringCI s

let w = str "(w)"
let z = str "(z)"

let woz = w <|> z

let isSeparator = manySatisfy (fun c -> c = ' ')
let xs = sepBy woz isSeparator

test isSeparator "  (w)" // Success: "  " position = (Ln: 1, Col: 3)
test xs "(z) (w)"        // Failure: Error in Ln: 1 Col: 8
                         // (z) (w)
                         //        ^
                         // Note: The error occurred at the end of the input stream.                         
                         // Expecting: '(w)' (case-insensitive) or '(z)' (case-insensitive)

1 Ответ

0 голосов
/ 01 июня 2018

Это происходит потому, что manySatisfy соответствует нулю или более символов, которые удовлетворяют данному предикату, ключевое слово равно "нулю".Это означает, что в самом конце ввода isSeparator фактически успешно, даже если он не потребляет никаких символов.И поскольку isSeparator успешно выполняется, sepBy ожидает найти еще один экземпляр woz после разделителя.Но экземпляров больше нет, поэтому sepBy возвращает ошибку.

Чтобы убедиться в этом, попробуйте выполнить синтаксический анализ ввода без пробелов между w и z: test xs "(z)(w)".Это должно вывести «Success», потому что с пустым разделителем все в порядке.

Чтобы isSeparator всегда занимал хотя бы один символ и завершился ошибкой, если пробелы не найдены, используйте many1Satisfy вместо manySatisfy:

let isSeparator = many1Satisfy (fun c -> c = ' ')
let xs = sepBy woz isSeparator
...