使用Haskell Parsec

时间:2018-09-22 18:38:08

标签: haskell parsec

我正在编写一个算术解析器来处理“ 1 + 2-3”之类的表达式。我使用this blog post作为参考。为了处理左关联性和优先级,我根据此BNF(来自博客文章)使用Parsec编写了一个解析器。

<exp> ::= <term> { ("+" | "-") <term> }
<term> ::= <factor> { ("*" | "/") <factor> }
<factor> ::= "(" <exp> ")" | <unary_op> <factor> | <int>

这是我的解析器代码。

parseExp :: Parser Exp
parseExp = do
  t1 <- parseTerm
  loop t1
  where termSuffix t1 = do
          op <- lexeme $ oneOf "+-"
          t2 <- parseTerm
          case op of
            '+' -> termSuffix (Binary Plus t1 t2)
            '-' -> termSuffix (Binary Minus t1 t2)
        loop t = termSuffix t <|> return t

parseTerm :: Parser Exp
parseTerm = do
  f1 <- parseFactor
  loop f1
  where factorSuffix f1 = do
          op <- lexeme $ oneOf "*/"
          f2 <- parseFactor
          case op of
            '*' -> factorSuffix (Binary Mul f1 f2)
            '/' -> factorSuffix (Binary Div f1 f2)
        loop t = factorSuffix t <|> return t

parseFactor :: Parser Exp
parseFactor = parseConst <|> parseParen <|> parseUnary

parseParen = do
  void $ lexeme $ char '('
  e <- parseExp
  void $ lexeme $ char ')'
  return e

parseUnary :: Parser Exp
parseUnary = do
  op <- lexeme $ oneOf "!~-"
  f <- parseFactor
  case op of
    '!' -> return $ Unary LogNeg f
    '~' -> return $ Unary BitCompl f
    '-' -> return $ Unary ArithNeg f

parseConst :: Parser Exp
parseConst = do
  i <- many1 digit
  return (Const $ read i)

我还使用了本教程代码作为参考。 tutorial

simpleExpr7 :: Parser SimpleExpr
simpleExpr7 = do
    -- first parse a term
    e <- term7
    -- then see if it is followed by an '+ expr' suffix
    maybeAddSuffix e
  where
    -- this function takes an expression, and parses a
    -- '+ expr' suffix, returning an Add expression
    -- it recursively calls itself via the maybeAddSuffix function
    addSuffix e0 = do
        void $ lexeme $ char '+'
        e1 <- term7
        maybeAddSuffix (Add e0 e1)
    -- this is the wrapper for addSuffix, which adapts it so that if
    -- addSuffix fails, it returns just the original expression
    maybeAddSuffix e = addSuffix e <|> return e

我的代码不起作用。这段代码是这样的。

*Main CodeGen Parser> parseWithEof parseExp "-2"
Right (Unary ArithNeg (Const 2))
*Main CodeGen Parser> parseWithEof parseExp "(2)"
Right (Const 2)
*Main CodeGen Parser> parseWithEof parseExp "-!(((2)))"
Right (Unary ArithNeg (Unary LogNeg (Const 2)))
*Main CodeGen Parser> parseWithEof parseExp "1+2"
Left (line 1, column 4):
unexpected end of input
expecting digit
*Main CodeGen Parser> parseWithEof parseExp "1+2+3"
Left (line 1, column 6):
unexpected end of input
expecting digit
*Main CodeGen Parser> parseWithEof parseExp "1+2*3"
Left (line 1, column 6):
unexpected end of input
expecting digit

我不明白为什么会产生unexpected end of input

1 个答案:

答案 0 :(得分:0)

请考虑解析1+2。在parseExp中,将1解析为t1 = Const 1,然后进入循环loop (Const 1)。循环尝试第一个替代项termSuffix (Const 1),该替代项成功地解析运算符+,下一个项t2 = Const 2,然后循环回到termSuffix (Binary Plus (Const 1) (Const 2)),后者期望一个+-。解析失败。除了应循环回到termSuffix之外,还应循环回到loop,以允许在第一个+之后使用单个术语:

parseExp :: Parser Exp
parseExp = do
  t1 <- parseTerm
  loop t1
  where termSuffix t1 = do
          op <- lexeme $ oneOf "+-"
          t2 <- parseTerm
          case op of
            -- *** use `loop` here, not `termSuffix` ***
            '+' -> loop (Binary Plus t1 t2)
            '-' -> loop (Binary Minus t1 t2)
        loop t = termSuffix t <|> return t

parseTerm进行类似的更改后,您的测试用例都可以正常工作。