2016-08-20 1 views
1

J'essaie de distinguer entre Ints et flottants dans un analyseur. J'ai 2 parseurs un pour chaque int et float. Cependant, j'ai du mal à entrer dans un '.'. J'ai cherché négation et regarder en avant et n'a pas semblé obtenir et fruits.Parsec si un match trouvé, puis lancer l'erreur

J'espère ne pas avoir de questions. Je l'ai fait en regardant le prochain caractère qui n'est pas un '.' mais c'est une solution laide.

EDIT: Ajout de plus de code.

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

À quoi ressemble 'findInt' (aide simplement à avoir quelque chose au travail)? – Alec

+0

Ajouté. Désolé je ne pensais pas quand j'ai mis le code. –

Répondre

2

Je pense que vous êtes mieux loti combinant en fait les deux parseurs en un seul. Essayez quelque chose comme ceci:

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 a une solution correcte, mais l'utilisation de try signifie que parsec doit garder une trace de la possibilité de faire marche arrière (ce qui est un peu inefficace) alors qu'en fait, ce qui est inutile dans ce Cas.

Ici, la principale différence est que nous pouvons effectivement dire tout de suite si nous avons un flotteur ou non - si nous ne disposons pas d'un flotteur, l'analyseur char '.' *> many1 digit dans optionMaybe échouera immédiatement (sans entrée consommer), donc il n'est pas nécessaire d'envisager un retour en arrière.

A GHCi

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

J'utiliser notFollowedBy - .: par exemple

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

Il est généralement plus facile à utiliser pour construire des combinateurs applicatives vos parseurs - ce qui rend votre parseurs plus facile de raisonner sur et souvent vous n'avez pas besoin de fonctions monadiques et de l'analyseur de traçage.

Par exemple, un analyseur syntaxique pour les entiers pourraient être écrits en tant que tel:

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) 

Un nombre décimal est un nombre entier éventuellement suivie d'une partie décimale (a suivi d'un autre nombre entier « »), qui est lui-même un nombre approprié, de sorte que votre analyseur peut être écrit comme

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) 

la définition de cmb est évidente - si le fait pas partie décimale, produire un Integer, et s'il y a, produire un Double, en ajoutant la Integ partie à la partie décimale.

Vous pouvez également définir un analyseur syntaxique pour décimaux en termes de ce qui précède:

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

Notez qu'aucun des parseurs ci-dessus utilisent directement les fonctions monadiques (à savoir >>=) ou retours en arrière - ce qui les rend simple et efficace.