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"])
我正在尝试解析资产类型列表,其中每种资产类型可能都有一个名称。列表完成后,我想继续解析资产类型的属性列表,所有资产类型的一个列表。
我尝试解析的字符串如下所示:
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 如 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"])