haskell parsec的(< |>)运算符的行为?

时间:2015-02-17 09:16:22

标签: haskell parsec

我想使用RPN解释器练习使用Parsec,这是我的代码:

module RPN where
import Control.Applicative hiding((<|>))
import Text.ParserCombinators.Parsec 
data RPNVal = Add|Sub|Mul|Div|Num Double deriving(Show)
a <:> b  = (:) <$> a <*> b
a <++> b = (++) <$> a <*> b
opSymbol = oneOf "+-*/"
number   = many1 digit :: Parser String
plus     = char '+' *> number
minus    = char '-' <:> number
integer  = (number <|> minus <|> plus) :: Parser String
float    = integer <++> decimal <++> exponent
    where decimal  = option "" $ char '.' <:> number
          exponent = option "" $ oneOf "eE" <:> number
parseOp :: Parser RPNVal
parseOp = do
    op <- opSymbol
    case op of 
         '+' -> return Add 
         '-' -> return Sub 
         '*' -> return Mul 
         '/' -> return Div
parseNum :: Parser RPNVal
parseNum = liftA (read::String->Double) float >>= return.Num
parseRPNVal :: Parser RPNVal 
-- parseRPNVal = parseOp <|> parseNum
-- parseRPNVal = parseNum <|> parseOp
parseRPNVal = try parseNum <|> parseOp
parseExpr :: Parser [RPNVal]
parseExpr = parseRPNVal `sepBy1` spaces
readExpr :: String->[RPNVal]
readExpr input = case parse parseExpr "RPN" input of
                      Left errMsg -> error $ show errMsg
                      Right val   -> val

parseRPNVal的定义中,我发现如果我使用parseRPNVal = parseOp <|> parseNum,那么解析器运行良好,但是,如果我使用parseRPNVal = parseNum <|> parseOp,那么该函数只能解析数字操作员会导致错误。

Real World Haskell中,它说:

  

此运算符的行为如下:它将首先尝试解析器   剩下。如果它没有消耗输入[35],它将尝试右侧的解析器

所以有人可以向我解释parseOpparseNum对我的行为吗?

1 个答案:

答案 0 :(得分:2)

就像真实世界的哈斯克尔所说的那样。

parseNum首先解析integer,但integer - s可以加上+-作为前缀。

因此,在解析parseNum <|> parseOp时,如果下一个字符是+-,并且我们实际拥有的是运算符,parseNum会读取该字符并失败之后才开始。

使用parseOp <|> parseNum时,不需要回溯,因为parseOp只需要一个字符预测。