-- | -- Module : Robotics.ROS.Msg.Parser -- Copyright : Alexander Krupenkin 2016 -- License : BSD3 -- -- Maintainer : mail@akru.me -- Stability : experimental -- Portability : POSIX / WIN32 -- -- Parser components for the ROS message description language (@msg@ -- files). See http://wiki.ros.org/msg for reference. -- module Robotics.ROS.Msg.Parser ( -- * Attoparsec re-export Result(..) , parse -- * The ROS message language parser , rosmsg ) where import Data.Text (Text, pack, toLower) import Data.Char (isDigit, isAlpha) import Data.Attoparsec.Text.Lazy import Control.Arrow ((&&&)) import Data.Either (rights) import qualified Data.Text as T import Robotics.ROS.Msg.Types -- | Show simple type showType :: Show a => a -> Text showType = toLower . T.drop 1 . pack . show -- | All of simple type and its text representation list simpleAssoc :: [(SimpleType, Text)] simpleAssoc = (id &&& showType) <$> enumFrom RBool -- | Line getter takeLine :: Parser Text takeLine = pack <$> manyTill anyChar (eitherP endOfLine endOfInput) -- | Valid ROS identifier parser identifier :: Parser Text identifier = takeWhile1 validChar where validChar c = any ($ c) [isDigit, isAlpha, (== '_'), (== '/')] -- | ROS message comments parser comment :: Parser () comment = skipSpace *> char '#' *> takeLine *> pure () -- | Parse fields defined in the message variableParser :: Parser FieldDefinition variableParser = do typeIdent <- choice [simpleField, customField] mkField <- choice [flat, array, fixedArray] return (Variable $ mkField typeIdent) where -- Build-in type parser simpleField = Simple . fst <$> choice (mapM string <$> simpleAssoc) -- User type parser customField = Custom . dropPkgSpec <$> identifier -- Drop package spec from user type dropPkgSpec = last . T.split (== '/') -- Flat type is no array flat = do name <- space *> skipSpace *> identifier <* takeLine return $ flip (,) name -- Variable lenght array array = do name <- skipSpace *> string "[]" *> skipSpace *> identifier <* takeLine return $ flip (,) name . Array -- Fixed lenght array fixedArray = do len <- skipSpace *> char '[' *> decimal <* char ']' name <- skipSpace *> identifier <* takeLine return $ flip (,) name . FixedArray len -- | Parse constants defined in the message constantParser :: Parser FieldDefinition constantParser = choice (go <$> enumFrom RBool) where go t = do name <- string (showType t) *> skipSpace *> identifier <* space value <- skipSpace *> char '=' *> skipSpace *> takeLine return $ Constant (Simple t, name) $ -- String constants are parsed somewhat differently from numeric -- constants. For numerical constants, we drop comments and trailing -- spaces. For strings, we take the whole line (so comments aren't -- stripped). case t of RString -> value _ -> T.takeWhile (/= '#') value -- | The ROS message language parser rosmsg :: Parser MsgDefinition rosmsg = rights <$> many' (eitherP junk field) where field = choice [constantParser, variableParser] junk = choice [comment, endOfLine]