在Haskell中实现优先爬升

在Haskell中实现优先爬升,haskell,compiler-construction,abstract-syntax-tree,operator-precedence,recursive-descent,Haskell,Compiler Construction,Abstract Syntax Tree,Operator Precedence,Recursive Descent,我试图在Haskell中实现优先级提升,遵循以下特定算法: compute_expr(min_prec): result = compute_atom() while cur token is a binary operator with precedence >= min_prec: prec, assoc = precedence and associativity of current token if assoc is left: next_mi

我试图在Haskell中实现优先级提升,遵循以下特定算法:

compute_expr(min_prec):
  result = compute_atom()
  while cur token is a binary operator with precedence >= min_prec:
    prec, assoc = precedence and associativity of current token
    if assoc is left:
      next_min_prec = prec + 1
    else:
      next_min_prec = prec
    rhs = compute_expr(next_min_prec)
    result = compute operator(result, rhs)

  return result
在这个伪代码中,
compute\u atom
负责传递值,并处理自然优先级被括号覆盖的情况。 我的Haskell代码如下所示:

precedenceClimbing :: Tokens -> [Either MyException Tokens] -> Precedence -> (Expr, [Either MyException Tokens])
precedenceClimbing tok listOfTokens prec = 
 let result = returnPrecExpr tok listOfTokens
     nextTBinOp = getToken (snd result)
 in  if checkBinopTok nextTBinOp  -- checkRParTok nextTBinOp
     then let listOfTokensBinOp = shrinkTokenList (snd result)
              binOp = convertTokenToBinOp nextTBinOp
              binOpParsed = parseBinop nextTBinOp listOfTokensBinOp
              prec_cur = handleBinopsPrecedence binOp 
          in  if prec_cur >= prec
              then let newPrec = prec_cur + 1
                       nextTokAtom = getToken (snd binOpParsed)
                       listOfTokensAtom = shrinkTokenList (snd binOpParsed)
                       newCalc = precedenceClimbing nextTokAtom listOfTokensAtom newPrec
                       newBinExpr = ExprBinOp (fst binOpParsed) (fst result) (fst newCalc)
                       in (newBinExpr, snd newCalc)
              else result --This is the most unsure section
     else result

returnPrecExpr :: Tokens -> [Either MyException Tokens] -> (Expr, [Either MyException Tokens])
returnPrecExpr tok listOfTokens = 
     if checkLParTok tok
     then let nextTokValue = getToken listOfTokens
              listOfTokensNValue = shrinkTokenList listOfTokens
              result = precedenceClimbing nextTokValue listOfTokensNValue 1
              nextRPar = getToken (snd result)
              listOfTokensRPar = shrinkTokenList (snd result)
          in  if checkRParTok nextRPar
              then (fst result, listOfTokensRPar)
              else undefined --error handling
     else let token = convertTokenToValue tok
              result = returnTokValueData token
          in (result, listOfTokens)
returnprecxpr
在这种情况下代表了
compute\u atom
,我相信它可以达到它的目的。然而,主要功能不是,因为我的主要问题是我不能满足算法中while给出的所有标准。从技术上讲,这意味着我应该能够通过适当的优先级(在这种情况下,自定义数据类型
首选项
只是一个
Int
)并像现在一样以正确的方式调用
优先项
,当算法应该退出当前运算符时,我无法继续,因为它的优先级不等于或大于前一个运算符。这就是我的代码停止的地方。
有什么改进的建议吗?
编辑

通过一个具体的例子,让我们有一个表达式,它是2*3+5。这里,因为2*3的优先级高于3+5,所以算法应该返回,返回2*3作为结果(但在我的上下文中,不是6的结果,而是以这种形式,2*3),但在这个阶段,我的算法停止并返回2*3,因为我没有正确地实现算法中的while/递归。这是我希望得到任何帮助的特定部分。

我建议您在开始解析之前处理错误消息。我假设
getToken
只从列表中获取第一个令牌,而
shrinkTokenList
只从列表中删除第一个令牌。然后,您可以使用更短的名称和模式匹配使函数更为惯用,如下所示:

preferencegranning::[Token]->preference->(Expr,[Token])
先例
|checkBinopTok tok1&&curPrec>=prec
=let(op,toks2)=parseBinop tok1 toks1
(r,toks3)=超前爬升toks2(curPrec+1)
in(ExprBinOp op l r,toks3)
|否则=结果
哪里
结果@(l,tok1:toks1)=返回EXPR toks
curPrec=车把前座(ConvertTokentobino tok1)
returnPrecExpr::[Token]->(Expr[Token])
RETURNPROCEXPR(tok:toks)
|checkLParTok tok=如果checkRParTok tok'then(e,toks')else未定义——错误处理
|否则=(returnTokValueData(ConvertTokenToValueTok),toks)
其中(e,tok':toks')=toks 1

您甚至可以使用
State[Token]
monad使其更加完善,如果您想生成错误消息,那么您可以轻松地将其更改为
StateT[Token](MyException之一)

我建议您在开始解析之前处理错误消息。我假设
getToken
只从列表中获取第一个令牌,而
shrinkTokenList
只从列表中删除第一个令牌。然后,您可以使用更短的名称和模式匹配使函数更为惯用,如下所示:

preferencegranning::[Token]->preference->(Expr,[Token])
先例
|checkBinopTok tok1&&curPrec>=prec
=let(op,toks2)=parseBinop tok1 toks1
(r,toks3)=超前爬升toks2(curPrec+1)
in(ExprBinOp op l r,toks3)
|否则=结果
哪里
结果@(l,tok1:toks1)=返回EXPR toks
curPrec=车把前座(ConvertTokentobino tok1)
returnPrecExpr::[Token]->(Expr[Token])
RETURNPROCEXPR(tok:toks)
|checkLParTok tok=如果checkRParTok tok'then(e,toks')else未定义——错误处理
|否则=(returnTokValueData(ConvertTokenToValueTok),toks)
其中(e,tok':toks')=toks 1

您甚至可以使用
State[Token]
monad使这一点变得更好,如果您想生成错误消息,那么您可以轻松地将其更改为
StateT[Token](MyException之一)

如果代码没有缩进到函数参数的右侧,那么您的代码将更易于阅读。对不起,更正了,谢谢你的反馈!我已经对代码风格提出了一些建议,但我真的无法给出改进实际算法实现的建议。你能详细说明一下问题是什么吗。我特别不理解这部分:“当算法应该从当前运算符退出时,我无法继续”。是的,我将编辑我的帖子。如果代码没有缩进到函数参数的右侧,您的代码将更容易阅读。抱歉,更正了,感谢您的反馈!我已经对代码风格提出了一些建议,但我真的无法给出改进实际算法实现的建议。你能详细说明一下问题是什么吗。我特别不理解这一部分:“当算法应该从当前操作符中退出时,我无法继续”。是的,我将编辑我的帖子。谢谢!我对Haskell编程相当业余,从命令式的角度来看,我离实现更好的函数心态还有很长的路要走。谢谢!我对Haskell编程相当业余,从命令式的角度来看,我离实现更好的函数心态还有很长的路要走。