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

Parsec if a match it found then throw error

我试图在解析器中区分整数和浮点数。我有 2 个解析器,每个解析器一个用于 int 和 float。但是,我无法在“.”上失败。我寻找否定和展望似乎并没有得到结果。

我希望我没有重复任何问题。

我用它来查看下一个不是“.”的字符。但这是一个丑陋的解决方案。

编辑:添加了更多代码。

--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----------------------------------------------------------------

我会使用 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."

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

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

通常最简单的方法是使用应用组合器来构建您的解析器 - 这使您的解析器更易于推理,而且通常您不需要解析器的单子函数和回溯函数。

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

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)

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

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 

请注意,上述解析器中的 none 直接使用单子函数(即 >>=)或回溯 - 使它们简单高效。