-1

我想解析这样的表达式:a().x. 它应该看起来像EAttrRef (EFuncCall (EVarRef "a") []) "x"。不幸的是,我的表达式解析器停止得太快了,它只会解析a()然后停止。

1:4:
  |
1 | a().x
  |    ^
unexpected '.'
expecting end of input

代码:

pExpr :: Parser Expr
pExpr = lexeme p & dbg "pExpr" <?> "expression"
  where
    pTerm = try pVarRef <|> pELit
    p = makeExprParser pTerm exprTable
    exprTable = [[Postfix opIndexRef], [InfixL opAttrRef], [Postfix opFuncCall]]
    opAttrRef :: Parser (Expr -> Expr -> Expr)
    opAttrRef = do
      symbol "." & dbg "opAttrRef symbol \".\""
      return r
      where
        r x (EVarRef y) = EAttrRef x y
        r x y = error [qq|opAttrRef got unexpected right operand $y (left operand was $x)|]
    opFuncCall :: Parser (Expr -> Expr)
    opFuncCall = do
      symbol "("
      args <- sepBy pExpr (symbol ",")
      symbol ")" & dbg "opFuncCall symbol \")\""
      return $ \funcExpr -> EFuncCall funcExpr args
    opIndexRef = do
      symbol "["
      e <- pExpr
      symbol "]" & dbg "opIndexRef symbol \"]\""
      return $ \obj -> EIndexRef obj e

调试输出:

opAttrRef symbol "."> IN: "().x"
opAttrRef symbol "."> MATCH (EERR): <EMPTY>
opAttrRef symbol "."> ERROR:
opAttrRef symbol "."> offset=1:
opAttrRef symbol "."> unexpected '('
opAttrRef symbol "."> expecting '.'

pExpr> IN: ").x"
pExpr> MATCH (EERR): <EMPTY>
pExpr> ERROR:
pExpr> offset=2:
pExpr> unexpected ").x"
pExpr> expecting "false", "null", "true", '"', '+', '-', '[', digit, identifier, or integer

opFuncCall symbol ")"> IN: ").x"
opFuncCall symbol ")"> MATCH (COK): ')'
opFuncCall symbol ")"> VALUE: ")"

pExpr> IN: "a().x"
pExpr> MATCH (COK): "a()"
pExpr> VALUE: EFuncCall (EVarRef "a") []

在我看来,这makeExprParser不是opFuncCall第二次调用(与索引访问调试输出的外观相比),但我不知道为什么不调用。

当我降低opAttrRef优先级时它会解析,但随后会产生错误的树(例如,正确的操作数x.a()a()不正确的,应该是a,然后整个认为应该在函数调用中),所以我不能使用它(我很确保当前优先级是正确的,因为它基于该语言的参考)。

4

1 回答 1

0

您当前的表达式解析器类似于以下 BNF:

expr = funcOp ;
funcOp = attrOp , { "(" , expr, ")" } ;
attrOp = attrOp , "." , indexOp | indexOp ;
indexOp = term , { "[", expr, "]" } ;

一旦完成解析funcCall,它就不会返回到运算符表中并解析任何attrRefor indexRef

降低 的 优先级的问题opAttrRef是分别解析点的左侧和右侧,当您似乎希望解析器从左到右读取,并且能够混合任何funcCall,attrRefindexRef. 因此,如果您希望能够解析类似的内容a[b](c).d(e)[f],我建议opAttrRef从中缀更改为后缀,并将运算符表展平为:

exprTable = [[Postfix opIndexRef, PostFix opAttrRef, Postfix opFuncCall]]

此时,解析器变为:

expr = term , { indexRef | attrRef | funcCall } ;

如果您需要允许多个后缀运算符,您可以像这样重写表达式解析器:

p = liftM2 (foldl (flip ($))) pTerm (many (opAttrRef <|> opIndexRef <|> opFuncCall))

如果要添加算术、逻辑和其他常用运算符,则p解析器可以用作术语解析器。makeExprParser

于 2020-02-18T16:25:46.020 回答