FParsec 带有可选内容的解析列表,无法继续下一个解析器

FParsec Parsing list with optional content, fails on continuing with next parser

我正在尝试解析资产类型列表,其中每种资产类型可能都有一个名称。列表完成后,我想继续解析资产类型的属性列表,所有资产类型的一个列表。

我尝试解析的字符串如下所示: converter named a023, signaltower, powerunit named 23 attributes power, temperature 解析器签名看起来像 Parser<((Asset * AssetName option) list * Attribute liste),unit> 我分别解析了资产名称和属性,当我将两者结合起来并完成列表时,问题就出现了,然后它在属性字符串上失败,说明 Expecting: 'named'

对我来说,它似乎正在尝试在属性字符串上失败的 opt assetname 解析器,但我不确定如何忽略它并在列表为 "done" 时继续前进(毕竟资产名称部分是可选的)..

type AssetName = AssetName of string
let named = str "named" >>. spaces1 >>. word
let assetName = spaces1 >>. (named |>> AssetName)

type Asset = | Converter | Signaltower | Powerunit 
let assetType = ["converter"; "signaltower"; "powerunit";] |> Seq.map pstring |> choice
let findAsset = function
| "converter" -> Converter
| "signaltower" -> Signaltower
| "powerunit" -> Powerunit
| _ -> raise <| Exception "Invalid asset type"
let asset = (assetType |>> findAsset) .>>. opt assetName 

type Attribute = Attribute of string
let attribute = word |>> Attribute
let attributes = spaces1 >>. str "attributes" >>. spaces1 >>. sepBy attribute commaMaybeSpace

let p = sepBy asset (pchar ',' >>. spaces) .>>. attributes
let r input = run p3 input
r "converter named a023, signaltower, powerunit named 23 attributes power, temperature"

Edit and also hinted at in the documentation 中所述,如果 p 在更改其状态后失败,则可选解析器 opt p 失败。相反,应该使用回溯(恢复状态),或者使用 attempt 解析器,或者使用回溯运算符之一。

记住 opt (attempt (p >>. q)) 等同于 opt (p >>? q),试试

let assetName = spaces1 >>? (named |>> AssetName)

不确定它是否与问题有关,但示例中的解析器类型与您描述的语法不匹配。我想你想要:

asset-type : | Converter | Signaltower | Powerunit

named-asset : | asset-type | asset-type named ident

asset-list : named-asset,...,named-asset

attributed-asset-list : asset-list attributes ident-list

以下行不重复 named-asset 的解析器。

let p = asset .>>. attributes
// val p : Parser<((Asset * AssetName option) * Attribute list),unit>

您可以将其替换为

let p = sepBy asset (pchar ',' >>. spaces) .>>. attributes
// val p : Parser<((Asset * AssetName option) list * Attribute list),unit>

"converter named a023, signaltower, powerunit named 23 attributes power, temperature"
|> run p |> printfn "%A" 
// Success: ([(Converter, Some (AssetName "a023")); (Signaltower, null);
//   (Powerunit, Some (AssetName "23"))],
//  [Attribute "power"; Attribute "temperature"])