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 直接使用单子函数(即 >>=
)或回溯 - 使它们简单高效。
我试图在解析器中区分整数和浮点数。我有 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 直接使用单子函数(即 >>=
)或回溯 - 使它们简单高效。