代码之家  ›  专栏  ›  技术社区  ›  Sean Kearon

用fparsec解析分隔列表

  •  4
  • Sean Kearon  · 技术社区  · 6 年前

    我试图解析一些可能是项目列表的内容,或者只是一个项目。我想把结果输入一个du( Thing 以下)。

    我的方法如下,但它给了我一个列表,即使列表中只有一个东西。

    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
    

    上面的链接如下: http://share.linqpad.net/sd8tpj.linq

    1 回复  |  直到 6 年前
        1
  •  5
  •   rmunn    6 年前

    如果您不喜欢在解析后测试列表长度,那么您可以尝试切换 <|> 表达式以先测试单个项的大小写,然后使用 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 . 那是因为 documentation for the <|> parser 状态(强调原文):

    解析器 p1 <|> p2 首先应用解析器 p1 . 如果 P1 成功,结果 P1 返回。如果 P1 出现非_致命错误,并且 不更改分析器状态 ,解析器 p2 已应用。

    没有 尝试 在这里,“一两个”会首先尝试用 只有一个 ,它将使用“one”,然后在“or”上失败,但解析器状态将被更改。这个 尝试 在尝试解析器之前,Combinator基本上会生成解析器状态的“书签”,如果解析器失败,它会返回到“书签”。所以 <和gt; 之后将看到未更改的分析器状态 attempt oneThingOnly ,然后尝试 lotsOfThings .