我已经在Haskell中编写了一个解析器,该解析器以字符串输入的形式解析公式,并生成由下面的BNF定义的Haskell data
类型。
formula ::= true
| false
| var
| formula & formula
| ∀ var . formula
| (formula)
var ::= letter { letter | digit }*
现在,我想创建一个Show
的实例,以便可以很好地打印由我的类型定义的公式(我不想使用deriving (Show)
)。我的问题是:如何定义函数,以便可以告诉何时需要括号?我不要太多,也不要太括号。
例如,给定公式∀ X . (X & Y) & (∀ Y . Y) & false
,该公式在解析后会产生数据结构
And (And (Forall "X" (And (Var "X") (Var "Y"))) (Forall "Y" (Var "Y"))) False
我们有
Too little parentheses: ∀ X . X & Y & ∀ Y . Y & false
Too much parentheses: (∀ X . (((X) & (Y)))) & (∀ Y . (Y)) & (false)
Just right: ∀ X . (X & Y) & (∀ Y . Y) & false
是否有一种方法可以估计需要多少个括号,以使语义永远不会模棱两可?我感谢任何反馈。
答案 0 :(得分:1)
未经测试的伪代码:
instance Show Formula where
showsPrec _p True = "True"
showsPrec _p False = "False"
showsPrec p (And f1 f2) = showParen (p > 5) $
showsPrec 5 f1 . (" & " ++) . showsPrec 5 f2
showsPrec p (Forall x f) = showParen (p > 8) $
("forall " ++ x ++) . showsPrec 8 f
...
(我可能应该使用showString
而不是上面的++
。我认为应该可以使用。)
以上,整数p
表示显示当前公式的上下文的优先级。例如,如果我们在f
中显示f & ...
,则p
的优先级为&
。
如果需要在优先级更高的上下文中打印符号,则需要添加括号。例如。如果f
是a | b
,我们就不能写a | b & ...
,否则它将被解释为a | (b & ...)
。我们需要在a | b
周围加上括号。这是由showParen (p > ...)
完成的。
递归时,我们会将符号的优先级传递给子项。
上面,我随机选择了优先级别。您需要根据自己的喜好调整它们。您还应该检查选择的级别是否与标准库一起播放。例如。打印Just someFormula
不应生成类似Just a & b
的内容,而应加上括号。