我是Haskell和Parsec的新手.在阅读了第16章使用真实世界Haskell的Parsec之后,我脑子里出现了一个问题:为什么以及何时Parsec比Yacc/Bison/Antlr等其他解析器生成器更好?
我的理解是Parsec创建了一个很好的编写解析器的DSL,而Haskell使它非常容易和富有表现力.但解析是一种标准/流行的技术,值得使用自己的语言,输出到多种目标语言.那么我们何时应该使用Parsec而不是从Bison/Antlr生成Haskell代码呢?
这个问题可能会超越技术,进入行业实践领域.从头开始编写解析器时,与Bison/Antlr或类似的东西相比,拾取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"
Run Code Online (Sandbox Code Playgroud)
我省略了变量,术语和括号表达式的解析器,但如果您认为它们可能与问题相关,则可以阅读解析器的完整源代码.
解析器成功使用仅使用否定和连接的表达式,即唯一的前缀运算符和第一个中缀运算符.
*Data.Logic.Propositional.Parser2> runPT expr () "" "p & ~q"
Right (p ? ¬q)
Run Code Online (Sandbox Code Playgroud)
使用任何其他运算符的表达式在运算符的第一个字符上失败,出现如下错误:
*Data.Logic.Propositional.Parser2> runPT expr () "" "p | …Run Code Online (Sandbox Code Playgroud) Parsec.Expr.buildExpressionParser的文档说:
相同优先级的前缀和后缀运算符只能出现一次(如果 - 是前缀否定,则不允许--2).
但是,我想解析这样的字符串.
具体来说,请考虑以下语法:
sentence:
| identifier
| "~" sentence
| sentence & sentence
| "!" sentence
Run Code Online (Sandbox Code Playgroud)
运算符优先级为:"~"绑定强于"&"绑定强于"!"
例如,我想要这句话
! ~a & b
Run Code Online (Sandbox Code Playgroud)
被解析为
! ( (~a) & b )
Run Code Online (Sandbox Code Playgroud)
而且这句话
~ ! a & b
Run Code Online (Sandbox Code Playgroud)
如
~( ! ( a & b) )
Run Code Online (Sandbox Code Playgroud)
Parsec允许我这样做(并指定运算符优先级),但是,我希望能够链接前缀,例如~ ~ ! ~ a.Parsec不允许这样做.我找到了链接前缀的解决方案,但是这个解决方案不允许我为不同的前缀运算符指定不同的运算符优先级("〜"和"!"绑定比"&"更强,或者它们都没有)
有人有解决方案吗?
编辑:
部分解决方案使操作员绑定正确,但不允许链接:http: //lpaste.net/143362
带链接的部分解决方案,但对"〜"运算符有错误的绑定:http: //lpaste.net/143364
编辑:有关最新答案的更多说明.
我其实想要&联想.左或右无所谓.左与右关联性仅在具有相同优先级的运算符之间起作用.对于您的示例,通过注意&绑定强于! …
我一直在尝试使用buildExpressionParser来解析一种语言,而我几乎拥有它.感谢Parsec.Expr重复的Prefix/Postfix运算符不支持解决我的一个大问题.
这段代码片段说明了(我希望是什么)我的最后一个难题:
import Text.Parsec.Expr
import Text.Parsec
data Expr = Lit Char | A1 Expr | A2 Expr | B Expr Expr
deriving (Show)
expr :: Parsec String () Expr
expr = buildExpressionParser table (fmap Lit digit)
prefix p = Prefix . chainl1 p $ return (.)
table =
[ [prefix $ char ',' >> return A1]
, [Infix (char '*' >> return B) AssocNone]
, [prefix $ char '.' >> return A2]]
Run Code Online (Sandbox Code Playgroud)
这成功地(并正确)解析,,0,..0,.,0 …