Разбор отдельных списков с помощью FParsec - PullRequest
0 голосов
/ 21 мая 2018

Я пытаюсь разобрать что-то, что может быть списком элементов или может быть только одним элементом.Я хочу поместить результаты в DU (Thing ниже).

Способ, которым я подхожу к этому, описан ниже, но он дает мне список вещей, даже если вlist.

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

type Thing =
    | OneThing of int
    | LotsOfThings of Thing list

let str s = pstringCI s .>> spaces 

let one = str "one" |>> fun x -> OneThing 1
let two = str "two" |>> fun x -> OneThing 2
let three = str "three" |>> fun x -> OneThing 3

let oneThing = (one <|> two <|> three)
let lotsOfThings = sepBy1 oneThing (str "or") |>> LotsOfThings

let lotsFirst = (lotsOfThings <|> oneThing)
test lotsFirst "one or two" // Success: LotsOfThings [OneThing 1; OneThing 2]
test lotsFirst "one" // Success: LotsOfThings [OneThing 1]

Как правильно вернуть OneThing, если в списке есть только один элемент?

Я могу это сделать, если проверю список перед возвратом, например:нижеприведенное.Но это на самом деле не «чувствует» правильно.

let lotsOfThings = sepBy1 oneThing (str "or") |>> fun l -> if l.Length = 1 then l.[0] else l |> LotsOfThings

LinqPad из вышеперечисленного здесь: http://share.linqpad.net/sd8tpj.linq

1 Ответ

0 голосов
/ 22 мая 2018

Если вам не нравится проверять длину списка после синтаксического анализа, вы можете попробовать переключить выражение <|>, чтобы сначала проверить случай с одним элементом, и использовать notFollowedBy, чтобы убедиться, что случай с одним элементом выиграл 'не соответствует списку:

let oneThing = (one <|> two <|> three)
let separator = str "or"
let lotsOfThings = sepBy1 oneThing separator |>> LotsOfThings

let oneThingOnly = oneThing .>> (notFollowedBy separator)
let lotsSecond = (attempt oneThingOnly) <|> lotsOfThings
test lotsSecond "one or two" // Success: LotsOfThings [OneThing 1; OneThing 2]
test lotsSecond "one" // Success: OneThing 1

Обратите внимание на использование синтаксического анализатора attempt с oneThingOnly.Это связано с тем, что документация для <|> синтаксического анализатора гласит (выделено в оригинале):

Анализатор p1 <|> p2 сначала применяет синтаксический анализатор p1.Если p1 завершается успешно, возвращается результат p1.Если p1 дает сбой с нефатальной ошибкой и без изменения состояния синтаксического анализатора , анализатор p2 применяется.

Без attempt в нем «один или два» сначала попытается выполнить синтаксический анализ с oneThingOnly, который будет использовать «один», а затем потерпеть неудачу на «или», носостояние парсера было бы измененоКомбинатор attempt в основном делает «закладку» состояния синтаксического анализатора, прежде чем пытаться выполнить анализатор, и, если этот синтаксический анализатор дает сбой, он возвращается к «закладке».Таким образом, <|> будет видеть неизмененное состояние анализатора после attempt oneThingOnly, а затем попытается lotsOfThings.

...