Как разобрать последовательность слов, разделенных двойными пробелами, с помощью fparsec?

Учитывая ввод:

alpha beta gamma  one two three

Как я могу разобрать это ниже?

[["alpha"; "beta"; "gamma"]; ["one"; "two"; "three"]]

Я могу написать это, когда есть лучший разделитель (например, __), как тогда

sepBy (sepBy word (pchar ' ')) (pstring "__")

работает, но в случае двойного пробела pchar в первом sepBy потребляет первый пробел, а затем синтаксический анализатор дает сбой.


person Simon P    schedule 19.09.2018    source источник


Ответы (2)


В руководстве FParsec говорится, что в sepBy p sep, если sep успешно и последующий p терпит неудачу (без изменения состояния), вся sepBy тоже терпит неудачу. Следовательно, ваша цель:

  1. сделать разделитель сбойным, если он встречает более одного пробела;
  2. для возврата, чтобы "внутренний" sepBy цикл благополучно закрылся и передал управление "внешнему" sepBy циклу.

Вот как это сделать:

// this is your word parser; it can be different of course,
// I just made it as simple as possible;
let pWord = many1Satisfy isAsciiLetter

// this is the Inner separator to separate individual words
let pSepInner =
    pchar ' '
    .>> notFollowedBy (pchar ' ') // guard rule to prevent 2nd space
    |> attempt                    // a wrapper that would fail NON-fatally

// this is the Outer separator
let pSepOuter =
    pchar ' '
    |> many1  // loop

// this is the parser that would return String list list
let pMain =
    pWord
    |> sepBy <| pSepInner         // the Inner loop
    |> sepBy <| pSepOuter         // the Outer loop

Использовать:

run pMain "alpha beta gamma  one two three"
Success: [["alpha"; "beta"; "gamma"]; ["one"; "two"; "three"]]
person bytebuster    schedule 19.09.2018

Я бы рекомендовал заменить sepBy word (pchar ' ') чем-то вроде этого:

let pOneSpace = pchar ' ' .>> notFollowedBy (pchar ' ')
let pTwoSpaces = pstring "  "
// Or if two spaces are allowed as separators but *not* three spaces...
let pTwoSpaces = pstring "  " .>> notFollowedBy (pchar ' ')
sepBy (sepBy word pOneSpace) pTwoSpaces

Примечание: не проверял (поскольку у меня сейчас нет времени), просто ввел в поле ответа. Так что проверь, вдруг я где-то ошибся.

person rmunn    schedule 19.09.2018
comment
Я думаю, что для этого нужна попытка, использованная выше, иначе вы получите ошибку. - person Simon P; 22.09.2018