【发布时间】:2019-06-10 05:02:01
【问题描述】:
我正在尝试编写一个Augmented Backus-Naur form 解析器。但是,每当我尝试解析替代方案时,都会遇到堆栈溢出异常。以下是触发问题的示例:
#r @"..\packages\FParsec\lib\net40-client\FParsecCS.dll"
#r @"..\packages\FParsec\lib\net40-client\FParsec.dll"
open FParsec
type Parser<'t> = Parser<'t, unit>
type Element =
| Alternates of Element list
| ParsedString of string
let (pRuleElement, pRuleElementRef) : (Parser<Element> * Parser<Element> ref) = createParserForwardedToRef()
let pString =
pchar '"' >>. manyCharsTill (noneOf ['"']) (pchar '"')
|>> ParsedString
let pAlternates : Parser<_> =
sepBy1 pRuleElement (many (pchar ' ') >>. (pchar '/') >>. many (pchar ' ') )
|>> Alternates
do pRuleElementRef :=
choice
[
pString
pAlternates
]
"\"0\" / \"1\" / \"2\" / \"3\" / \"4\" / \"5\" / \"6\" / \"7\""
|> run (pRuleElement .>> (skipNewline <|> eof))
只需像这样重新排序 choice 即可轻松解决该问题:
do pRuleElementRef :=
choice
[
pAlternates
pString
]
但是,这会导致堆栈溢出,因为它会不断尝试解析新的替代序列而不消耗输入。此外,该方法会破坏 ABNF 优先级:
- 字符串,名称形成
- 评论
- 取值范围
- 重复
- 分组,可选
- 串联
- 另类
我的问题基本上可以归结为:如何结合对可以是元素序列或元素的单个实例的单个元素的解析?如果您需要任何说明/其他示例,请告诉我。
非常感谢您的帮助,谢谢!
编辑:
我可能应该提到还有各种其他类型的分组。一个序列组(element[s]) 和一个可选组[optional element[s]。其中element 可以是嵌套组/可选组/字符串/其他元素类型。下面是一个序列组解析的示例(为简单起见,不包括可选的组解析):
#r @"..\packages\FParsec\lib\net40-client\FParsecCS.dll"
#r @"..\packages\FParsec\lib\net40-client\FParsec.dll"
open FParsec
type Parser<'t> = Parser<'t, unit>
type Element =
| Alternates of Element list
| SequenceGroup of Element list
| ParsedString of string
let (pRuleElement, pRuleElementRef) : (Parser<Element> * Parser<Element> ref) = createParserForwardedToRef()
let pString =
pchar '"' >>. manyCharsTill (noneOf ['"']) (pchar '"')
|>> ParsedString
let pAlternates : Parser<_> =
pipe2
(pRuleElement .>> (many (pchar ' ') >>. (pchar '/') >>. many (pchar ' ')))
(sepBy1 pRuleElement (many (pchar ' ') >>. (pchar '/') >>. many (pchar ' ') ))
(fun first rest -> first :: rest)
|>> Alternates
let pSequenceGroup : Parser<_> =
between (pchar '(') (pchar ')') (sepBy1 pRuleElement (pchar ' '))
|>> SequenceGroup
do pRuleElementRef :=
choice
[
pAlternates
pSequenceGroup
pString
]
"\"0\" / ((\"1\" \"2\") / \"2\") / \"3\" / (\"4\" / \"5\") / \"6\" / \"7\""
|> run (pRuleElement .>> (skipNewline <|> eof))
如果我首先尝试解析备用/序列组,它会以 stack overflow 异常终止,因为它会尝试重复解析备用。
【问题讨论】:
标签: parsing recursion f# fparsec