我正在编写一些代码来解析来自中定义的简单命令语言的命令 编程语言理论(Reynolds,1998)。
我有一个词法分析器模块,如果它是一个有效的语言表达式,给定一个字符串从中提取标记,然后我将该标记列表传递给解析器,该解析器应构建命令的内部表示(定义为代数数据类型) )。
这些是我的代币:
--Tokens for the parser
data Token = Kw Keyword
| Num Int
| Op Operator
| Str String
| Sym Symbol
deriving Show
我遇到二元运算符问题。我将举例说明总和,但它与所有这些相同,无论是布尔值还是整数。
例如,如果我运行程序解析“x:= 2 + 3”
我应该从词法分析器中获得以下令牌列表
[Str“x”,Op Colon,Op Equal,Num 2,OP,Plus,Num 3]
这实际上是我得到的。
然后解析器应该返回命令
分配“x”(Ibin Plus(Const 2)(Const 3)
这是命令的正确表示。但不是说我得到以下表示:
分配“x”(Const 2)
我想我在 pIntExpr 函数中的某个位置搞砸了它,因为赋值的变量标识符和:= 被解析好并且它没有解析最后一个元素。以下是此示例的相关解析器,以查看是否有人可以根据我的错误来定位我。
-- Integer expressions
data IntExpr = Const Int
| Var Iden --Iden=String
| Neg IntExpr
| IBin OpInt IntExpr IntExpr
deriving Show
type TParser = Parsec [Token] ()
--Internal representation of the commands
data Comm = Skip
| Assign Iden IntExpr
| If Assert Comm Comm
| Seq Comm Comm
| While Assert Comm
| Newvar Iden IntExpr Comm
deriving Show
--Parser for non sequential commands
pComm' :: TParser Comm
pComm' = choice [pif,pskip,pAssign,pwhile,pNewVar]
--Parser for the assignment command
pAssign :: TParser Comm
pAssign = do v <- pvar
_ <- silentOp Colon
_ <- silentOp Equal
e <- pIntExp
return $ Assign v e
-- Integer expressions parser
pIntExp :: TParser IntExpr
pIntExp = choice [ var' --An intexp is either a variable
, num --Or a numeric constant
, pMul --Or <intexp>x<intexp>
, pSum --Or <intexp>+<intexp>
, pRes --Or <intexp>-<intexp>
, pDiv --Division
, pMod --Modulus
, pNeg --Unary "-"
]
-- Parser for <intexp>+<intexp>
pSum :: TParser IntExpr
pSum = do
e <- pIntExp
_ <- silentOp Lexer.Plus
e' <- pIntExp
return $ IBin Lang.Plus e e'
更新进入帐户AndrewC的回答
不幸的是,在选项列表中向下移动 var'解析器不起作用,它会产生相同的结果。但我考虑了AndrewC的答案,并尝试“手动”跟踪执行(我不熟悉ghci的调试器,最终做了很多单步并最终迷路)。
这就是我的理由:
我从词法分析器得到了这个令牌列表: [Str“x”,Op Colon,Op Equal,Num 2,OP Plus,Num 3]
因此, pComm'解析器使用 pif 和 pskip 失败,但是使用 pAssign 成功,消耗< strong> Str“x”,Op Colon和Op Equal 并尝试解析 [Num 2,OP Plus,Num 3] 与 pIntExp (!!)
pIntExp 解析器然后尝试 var'解析器并失败,但是使用 num 解析器消耗了 Num 2 < / strong>令牌因此返回错误结果指定“x”(Const 2)。
因此,考虑到AndrewC关于 choice 的建议,我也将 num 解析器移到了列表中。为简单起见,我将 pIntExp 视为 选择[pSum,num,var'] ,这与这个特定的例子有关。
推理的第一部分保持不变。所以我会从(!!)重新开始
[Num 2,Op Plus,Num 3] 由 pIntExp解析
pIntExp 现在首先尝试使用 pSum ,然后再次“调用” pIntExp ,
它将再次尝试 pSum ,因此程序挂起。我尝试了它,它确实挂了,永远不会结束。
所以我想知道是否有一个表格可以使 pSum 解析器“超前”用于 Op Plus 令牌,然后解析相应的表达式?
更新2:在“google搜索”之后,我已经确定了问题,我发现组合解析器chainl1和/或chainl可能正是我需要的。 我会玩这些,如果我在解决方案之后解决这个问题
答案 0 :(得分:4)
选择函数按照它们在列表中的顺序尝试解析器。
由于变量的yoiur解析器出现在解析器之前,用于更复杂的加法表达式,因此它会在尝试另一个之前成功。
要解决此问题,请将之后的变量解析器放在以变量开头的任何表达式中(并在使用choice时考虑任何其他子字符串匹配问题。
类似的问题包括3 - 4 + 1评估为-2。人们期望在没有其他优先事项的情况下留下关联(所以总和而不是期限 - 总和)。
你也可能不希望1 + 10 * 5到55,所以如果你想实现运算符优先级,你必须小心+和*等。你可以通过解析一个由乘法作为一个术语组成的表达式,然后将一个加法表达式作为一个术语的总和来实现。