Parsec如果找到匹配则抛出错误

时间:2016-08-20 02:20:55

标签: haskell parsec

我试图区分解析器中的Ints和浮点数。我有2个解析器,每个int和float一个。但是,我无法在#39 ;.#39;上遇到失败。我寻找否定并展望未来,似乎没有得到结果。

我希望我不会重复任何问题。

我让它看着下一个不是'的字符。'但这是一个丑陋的解决方案。

编辑:添加了更多代码。

--Int--------------------------------------------------------------------
findInt :: Parser String
findInt = plus <|> minus <|> number

number :: Parser String
number = many1 digit

plus :: Parser String
plus = char '+' *> number

minus :: Parser String
minus = char '-' <:> number

makeInt :: Parser Int
makeInt = prepareResult (findInt  <* many (noneOf ".")  <* endOfLine)
    where readInt = read :: String -> Int
          prepareResult = liftA readInt
makeInt2 :: Parser Int
makeInt2 = do
  numberFound <- (findInt  <* many (noneOf ".")  <* endOfLine)
  match <- char '.'
  return  (prepareResult numberFound)
  where readInt = read :: String -> Int
        prepareResult = readInt
--End Int----------------------------------------------------------------

3 个答案:

答案 0 :(得分:2)

我认为你最好将两个解析器组合成一个。尝试这样的事情:

import Text.Parsec.String (Parser)
import Control.Applicative ((<|>))
import Text.Parsec.Char (char,digit)
import Text.Parsec.Combinator (many1,optionMaybe)

makeIntOrFloat :: Parser (Either Int Float)
makeIntOrFloat = do
    sign <- optionMaybe (char '-' <|> char '+')
    n <- many1 digit
    m <- optionMaybe (char '.' *> many1 digit)
    return $ case (m,sign) of
        (Nothing, Just '-') -> Left (negate (read n))
        (Nothing, _)        -> Left (read n)
        (Just m, Just '-')  -> Right (negate (read n + read m / 10.0^(length m)))
        (Just m, _)         -> Right (read n + read m / 10.0^(length m))

ErikR有一个正确的解决方案,但使用try意味着parsec必须跟踪回溯的可能性(这有点效率低),而事实上在这种情况下是不必要的

这里的关键区别在于我们实际上可以告诉我们是否有浮动 - 如果我们没有浮动,optionMaybe中的char '.' *> many1 digit解析器会立即失败(没有消耗输入),所以没有必要考虑回溯。

在GHCi

ghci> import Text.Parsec.Prim
ghci> parseTest makeIntOrFloat "1234.012"
Right 1234.012
ghci> parseTest makeIntOrFloat "1234"
Left 1234

答案 1 :(得分:1)

我会使用notFollowedBy - 例如:

import Text.Parsec
import Text.Parsec.String
import Text.Parsec.Combinator

int :: Parser String
int = many1 digit <* notFollowedBy (char '.')

float :: Parser (String,String)
float = do whole <- many1 digit
           fracpart <- try (char '.' *> many digit) <|> (return "")
           return (whole, fracpart)

intOrFloat :: Parser (Either String (String,String))
intOrFloat = try (fmap Left int) <|> (fmap Right float)

test1 = parseTest (intOrFloat <* eof) "123"
test2 = parseTest (intOrFloat <* eof) "123.456"
test3 = parseTest (intOrFloat <* eof) "123."

答案 2 :(得分:0)

使用应用程序组合器来构建解析器通常最简单 - 这使得解析器更易于推理,并且通常不需要解析器的monadic和回溯函数。

例如,整数解析器可以这样写:

import Text.Parsec hiding ((<|>), optional)
import Text.Parsec.String 
import Numeric.Natural 
import Control.Applicative 
import Data.Foldable

natural :: Parser Natural 
natural = read <$> many1 digit

sign :: Num a => Parser (a -> a)
sign = asum [ id <$ char '+'
            , negate <$ char '-'
            , pure id 
            ] 

integer :: Parser Integer 
integer = sign <*> (fromIntegral <$> natural)

十进制数是一个整数,可选地后跟一个小数部分(&#39;。&#39;后跟另一个整数),它本身就是一个数字,所以你的解析器可以写成

decimalPart :: Parser Double   
decimalPart = read . ("0."++) <$> (char '.' *> many1 digit)

integerOrDecimal :: Parser (Either Integer Double)
integerOrDecimal = liftA2 cmb integer (optional decimalPart) where 
   cmb :: Integer -> Maybe Double -> Either Integer Double
   cmb x Nothing = Left x 
   cmb x (Just d) = Right (fromIntegral x + d) 

cmb的定义很明显 - 如果不是小数部分,则生成Integer,如果有,则生成Double,通过将整数部分添加到小数部分。

您还可以根据上述内容定义小数的解析器:

decimal :: Parser Double 
decimal = either fromIntegral id <$> integerOrDecimal 

请注意,上述解析器都不直接使用monadic函数(即>>=)或回溯 - 使它们变得简单而有效。