复杂的Parsec解析器

时间:2011-05-03 22:02:06

标签: haskell parsec

我不知道怎么回答。我想我需要一般指导。我有这样的事情:

expr = buildExpressionParser table term
    <?> "expression"

term = choice [
    (float >>= return . EDouble)
    , try (natural >>= return . EInteger)
    , try (stringLiteral >>= return . EString)
    , try (reserved "true" >> return (EBool True))
    , try (reserved "false" >> return (EBool False))
    , try assign
    , try ifelse
    , try lambda
    , try array
    , try eseq
    , parens expr
    ]
    <?> "simple expression"

当我测试该解析器时,我主要遇到问题......就像我尝试解析

时一样
 (a,b) -> "b"

它被lambda解析器接受,但expr解析器讨厌它。有时它甚至完全挂在永恒的规则中。

我已阅读Write Yourself a Scheme,但它只解析了Scheme的同类来源。

也许我一般都在想错误的方向。

编辑:这里是内部解析器:

assign = do
    i <- identifier
    reservedOp "="
    e <- expr
    return $ EAssign i e

ifelse = do
    reserved "if"
    e <- expr
    reserved "then"
    a <- expr
    reserved "else"
    b <- expr
    return $ EIfElse e a b

lambda = do
    ls <- parens $ commaSep identifier
    reservedOp "->"
    e <- expr
    return $ ELambda ls e

array = (squares $ commaSep expr) >>= return . EArray

eseq = do
    a <- expr
    semi <|> (newline >>= (\x -> return [x]))
    b <- expr
    return $ ESequence a b

table = [
        [binary "*" EMult AssocLeft, binary "/" EDiv AssocLeft, binary "%" EMod AssocLeft ],
        [binary "+" EPlus AssocLeft, binary "-" EMinus   AssocLeft ],
        [binary "~" EConcat AssocLeft],
        [prefixF "not" ENot],
        [binaryF "and" EAnd AssocLeft, binaryF "or" EAnd AssocLeft]
    ]

并且“讨厌它”我的意思是它告诉我它需要一个整数或一个浮点。

2 个答案:

答案 0 :(得分:6)

评论中的爱德华和我都想做的是精神上运行你的解析器,如果没有更多的解析器继续下去,这有点困难。我将在这里做一些猜测,也许他们会帮助你改进你的问题。

猜1):您已尝试GHCI> parse expr "(input)" "(a,b) -> \"b\"并已返回Left …。知道错误是什么会很有帮助。

猜猜2):您还尝试了GHCI> parse lambda "(input)" "(a,b) -> \"b\"并返回了Right …。基于这个爱德华,我已经推断出你的term解析器或者生成的expr解析器中的某个地方存在冲突。这是解析器的某些部分成功匹配开头的字符串并返回一个值,但剩下的不再有效。如果您尝试GHCI> parse term "(input)" "(a,b) -> \"b\"会很有帮助,因为这可以让我们知道问题是在term还是expr

猜3):字符串"(a,b)"本身就是编程时语法中的有效表达式。 (虽然可能不像你想要的来编程它;-)。尝试通过expr解析器发送它,看看会发生什么。

猜猜4):你的语法是递归的。这就是导致它被卡住并永远循环的原因。 Parsec是LL(k)解析器。如果您习惯于作为LR(1)或LR(k)解析器的Yacc和family,则递归规则正好相反。如果您不理解这最后一句话,那就行了,但请告诉我们。

猜猜5):表达式构建器中的代码看起来像是来自函数的文档。我想你可能也在某处发现了term表达式。如果是这种情况,您可以指出它来自何处。如果没有,你能用几句话解释你认为term应该如何运作。


一般建议:大量try语句最终(现在是a.k.a.)会让你感到悲伤。它们在某些情况下很有用,但也有点顽皮。如果下一个字符可以确定哪个选项应该成功,则不需要它们。如果你只是试图让一些东西运行大量的回溯会减少中间形式的数量,但它也会隐藏病态情况并使错误更加模糊。

答案 1 :(得分:4)

似乎有左递归,如果choice中的term到达eseq,则会导致解析器挂起:

expr - &gt; term - &gt; eseq - &gt; expr

term (a,b)不会解析为lambdaarray,因此会落入eseq循环。

我不明白为什么(a,b) -> "b"不能解析为expr,因为choice这个术语应该点击你所说的lambda,在到达eseq之前。解析错误中报告的位置是什么?