我正在尝试编写一个 增强型巴克斯-诺尔范式 解析器。然而,每当我尝试解析替代方案时,都会遇到堆栈溢出异常。下面是一个触发此问题的示例:
#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))
如果我首先尝试解析替代/序列组,它会因为不断地尝试解析替代而导致堆栈溢出异常发生。
pString
和pSequenceGroup
将会很好,因为这两者在它们的第一个字符上不同 - 即如果不是这两者之一,则解析器将失败。然而,pAlternates
仍然需要在顶层运行,以便它可以消耗一个接一个的东西 - 所以我认为你需要两个解析器 - 一个用于任何可以通过第一个字符确定的内容,另一个用于通过交替迭代这些内容。 - Tomas Petricek