我正在尝试编写一个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
异常终止,因为它随后尝试重复解析替代项。
答案 0 :(得分:2)
问题是,当您在输入上运行pRuleElement
解析器时,它会正确解析一个字符串,留下一些未使用的输入,但是稍后会在choice
之外失败,从而回溯。 / p>
您可以在主要输入上运行pAlternates
解析器,该解析器实际上是有效的:
"\"0\" / \"1\" / \"2\" / \"3\" / \"4\" / \"5\" / \"6\" / \"7\""
|> run (pAlternates .>> (skipNewline <|> eof))
我怀疑您可能可以做到这一点-pAlternates
解析器即使在单个字符串上也可以正常工作-它将仅返回包含一个单例列表的Alternates
。
答案 1 :(得分:0)
该解决方案似乎只是在解析备选方案时根本不尝试解析备选方案,以避免无限循环导致栈溢出。我的问题中发布的代码的有效版本如下:
#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 (pNotAlternatives, pNotAlternativesRef) : (Parser<Element> * Parser<Element> ref) = createParserForwardedToRef()
let pString =
pchar '"' >>. manyCharsTill (noneOf ['"']) (pchar '"')
|>> ParsedString
let pAlternates : Parser<_> =
pipe2
(pNotAlternatives .>>? (many (pchar ' ') >>? (pchar '/') >>. many (pchar ' ')))
(sepBy1 pNotAlternatives (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
]
do pNotAlternativesRef :=
choice
[
pSequenceGroup
pString
]
"\"0\" / (\"1\" \"2\") / \"3\" / (\"4\" / \"5\") / \"6\" / \"7\""
|> run (pRuleElement .>> (skipNewline <|> eof))
除了添加pNotAlternatives
之外,我还对其进行了修改,以使其在无法解析替代分隔符/
时回溯,从而使它在“意识到”不是毕竟是替代品列表。