我想解析F#类型的语法。我开始写一个[F]秒差距语法,遇到了问题,所以我简化the grammar到这一点:Parsec:回溯不工作
type ::= identifier | type -> type
identifier ::= [A-Za-z0-9.`]+
运行与FParsec问题之后,我切换到秒差距,因为我有一个full chapter of a book dedicated to explaining it。我给这家文法代码是
typeP = choice [identP, arrowP]
identP = do
id <- many1 (digit <|> letter <|> char '.' <|> char '`')
-- more complicated code here later
return id
arrowP = do
domain <- typeP
string "->"
range <- typeP
return $ "("++domain++" -> "++range++")"
run = parse (do t <- typeP
eof
return t) "F# type syntax"
的问题是,秒差距默认不走回头路,所以
> run "int"
Right "int"
-- works!
> run "int->int"
Left "F# type syntax"
unexpected "-"
expecting digit, letter, ".", "`" or end of input
-- doesn't work!
我想的第一件事就是重新安排typeP:
typeP = choice [arrowP, identP]
但是这个堆栈溢出了,因为语法是左递归的 - typeP永远不会尝试identP
,因为它一直试图反复尝试arrowP
。接下来,我在不同的地方尝试try
,例如:
typeP = choice [try identP, arrowP]
但没有我这样做似乎改变(1)栈溢出或(2)不承认的基本行为 - 一个标识符下面的“>”。
对于成功编写Parsec语法的人来说,我的错误可能是很明显的。有人可以指出吗?
很好的解释。正如你注意到的那样,问题的根源在于你需要打破箭头P可以下降到typeP的循环,而typeP本身可以下降到typeP。我认为你的'parens'例子特别具有启发性。 – kvb 2010-03-08 20:41:07
因此,Parsec语法与LR(1)语法具有基本相同的非组成问题,因为您必须规划整个语法,以便每条规则的左边最终重写为明确的文字。哦,我想我应该知道比假设帕塞克是魔术更好。 – 2010-03-09 15:46:22