module Ros.Internal.Util.ArgRemapping (parseRemappings, FromParam(..),
ParamVal) where
import Control.Applicative ((<$>))
import Control.Monad.Identity (Identity)
import Data.Either (partitionEithers, lefts, rights)
import Text.Parsec.Language (emptyDef)
import Text.Parsec.Token
import Text.Parsec (letter, char, alphaNum, (<|>))
import Text.Parsec.Combinator (choice)
import Text.Parsec.Prim (Parsec, runParser)
data ParamVal = PInt Int
| PBool Bool
| PString String
| PDouble Double
| PList [ParamVal]
| PUnknown
deriving Show
class FromParam a where
fromParam :: ParamVal -> a
instance FromParam Int where
fromParam (PInt x) = x
fromParam x = error $ "Parameter is not an Int: " ++ show x
instance FromParam Bool where
fromParam (PBool x) = x
fromParam x = error $ "Parameter is not a Bool: " ++ show x
instance FromParam String where
fromParam (PString x) = x
fromParam x = error $ "Parameter is not a String: " ++ show x
instance FromParam Double where
fromParam (PDouble x) = x
fromParam x = error $ "Parameter is not a Double: " ++ show x
instance FromParam [Int] where
fromParam (PList xs) = map fromParam xs
fromParam x = error $ "Parameter is not a List: " ++ show x
instance FromParam [Bool] where
fromParam (PList xs) = map fromParam xs
fromParam x = error $ "Parameter is not a List: " ++ show x
instance FromParam [Double] where
fromParam (PList xs) = map fromParam xs
fromParam x = error $ "Parameter is not a List: " ++ show x
type Names = [(String, String)]
type Params = [(String, ParamVal)]
lexer :: GenTokenParser String u Identity
lexer = makeTokenParser $
emptyDef { reservedNames = ["true", "false"]
, identStart = letter <|> char '_' <|> char '/'
, identLetter = alphaNum <|> char '_' <|> char '/' }
data Sign = Positive | Negative
sign :: Parsec String () Sign
sign = (char '-' >> return Negative)
<|> (char '+' >> return Positive)
<|> return Positive
applySign :: Num a => Sign -> a -> a
applySign Positive = id
applySign Negative = negate
intOrFloat' :: Parsec String () (Either Int Double)
intOrFloat' = do s <- sign
num <- naturalOrFloat lexer
case num of
Left x -> return . Left $ applySign s (fromIntegral x)
Right x -> return . Right $ applySign s x
parseVal :: Parsec String () ParamVal
parseVal = choice [ either PInt PDouble <$> intOrFloat'
, const (PBool True) <$> reserved lexer "true"
, const (PBool False) <$> reserved lexer "false"
, PString <$> (stringLiteral lexer <|> identifier lexer)
, PList <$> brackets lexer (commaSep lexer parseVal) ]
parseBinding :: Parsec String () (Either (String, String) (String, ParamVal))
parseBinding = do name <- identifier lexer
_ <- symbol lexer ":="
case head name of
'_' -> do val <- parseVal
return $ Right (name, val)
_ -> do val <- identifier lexer
return $ Left (name, val)
parseRemappings :: [String] -> (Names, Params)
parseRemappings args = if null errors
then partitionEithers (rights remaps)
else error $ "Couldn't parse remapping "++ show errors
where remaps = map (runParser parseBinding () "") args
errors = lefts remaps