为什么在使用Parsec的buildExpressionParser时只有第一个定义的中缀运算符解析?

时间:2012-12-03 13:01:55

标签: haskell parsec

我正在尝试使用Parsec为命题演算编写解析器。解析器使用Text.Parsec.Expr中的buildExpressionParser函数。这是我定义逻辑运算符的代码。

operators = [ [Prefix (string "~" >> return Negation)]
            , [binary "&" Conjunction]
            , [binary "|" Disjunction]
            , [binary "->" Conditional]
            , [binary "<->" Biconditional]
            ]

binary n c = Infix (spaces >> string n >> spaces >> return c) AssocRight

expr = buildExpressionParser operators term
    <?> "compound expression"

我省略了变量,术语和括号表达式的解析器,但如果您认为它们可能与问题相关,则可以阅读full source for the parser

解析器成功使用仅使用否定和连接的表达式,即唯一的前缀运算符和第一个中缀运算符。

*Data.Logic.Propositional.Parser2> runPT expr () "" "p & ~q"
Right (p ∧ ¬q)

使用任何其他运算符的表达式在运算符的第一个字符上失败,并出现如下错误:

*Data.Logic.Propositional.Parser2> runPT expr () "" "p | q"
Left (line 1, column 3):
unexpected "|"
expecting space or "&"

如果我注释掉为连词定义解析器的行,那么析取的解析器将起作用(但其余的仍然会失败)。将它们全部放入一个列表(即具有相同的优先级)也不起作用:同样的问题仍然存在。

有谁可以指出我做错了什么?非常感谢。


感谢Daniel Fischer提供了一个迅速而有用的答案。

为了使这个解析器正常工作,我还需要处理否定符号的重复应用,以便例如~~p会正确解析。 This SO answer向我展示了如何操作,并且我可以找到我对解析器所做的更改here

1 个答案:

答案 0 :(得分:8)

你的问题是那个

binary n c = Infix (spaces >> string n >> spaces >> return c) AssocRight

第一个尝试过的中缀运算符在失败之前会消耗一个空格,因此不会尝试后续的可能性。 (Parsec倾向于使用解析器,<|>只会尝试运行第二个解析器,如果第一个失败的没有消耗任何输入。)

要让其他中缀运算符在第一次失败时尝试,您可以将binary解析器包装在try

binary n c = Infix (try $ ...) AssocRight

这样当这样的解析器失败时,它不会消耗任何输入,或者更好,并且解决该问题的传统解决方案,从中删除初始spaces

binary n c = Infix (string n >> spaces >> return c) AssocRight

并让所有解析器在解析后的

之后使用空格
variable = do c <- letter
              spaces
              return $ Variable (Var c)
        <?> "variable"

parens p = do char '('
              spaces
              x <- p
              char ')'
              spaces
              return x
        <?> "parens"

当然,如果你有能够使用公共前缀解析运算符的解析器,你仍然需要将它们包装在try中,这样如果解析>=失败,>>=仍然可以尝试。

如上所述,模拟命题的数据类型并改变占用空间的行为,

*PropositionalParser Text.Parsec> head $ runPT expr () "" "p | q -> r & s"
Right (Conditional (Disjunction (Variable (Var 'p')) (Variable (Var 'q'))) (Conjunction (Variable (Var 'r')) (Variable (Var 's'))))

即使解析了更复杂的表达式。