2016-08-20 49 views
1

我想區分分析器中的Ints和浮點數。我有2個分析器,每個int和float。但是,我無法進入'。'。我尋找否定和展望未來,似乎並沒有得到和成果。Parsec如果發現匹配,然後拋出錯誤

我希望我不會重複任何問題。

我讓它看着下一個不是''的字符。但這是一個醜陋的解決方案。

編輯:增加了更多的代碼。

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

findInt看起來像什麼(只是有助於解決某些問題)? – Alec

+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必須保持的回溯的可能性(這是一個有點低效率)的軌道時,其實是不必要在這案件。

這裏,關鍵的區別是,我們實際上可以告訴立即如果我們有一個浮動與否 - 如果我們沒有一定的浮動,該char '.' *> many1 digit解析器optionMaybe將立即失敗(不消耗輸入),所以有沒有必要考慮回溯。

在GHCI

ghci> import Text.Parsec.Prim 
ghci> parseTest makeIntOrFloat "1234.012" 
Right 1234.012 
ghci> parseTest makeIntOrFloat "1234" 
Left 1234 
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." 
0

它通常是最容易使用的應用性組合子來建立你的分析器 - 這使得您的解析器更容易推理,往往不需要解析器的一元和回溯功能。

例如,對於整數解析器可以寫成這樣:

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,加入INTEG呃部分到小數部分。

也可以在上述的條款限定用於小數解析器:

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

注意,沒有以上解析器直接使用一元函數(即>>=)或回溯 - 使它們簡單和有效的。