module Gdbmi.Representation
(
Command(..),
Token,
Operation,
Option(..),
Parameter(..),
Output(..),
ResultRecord(..),
OutOfBandRecord(..),
AsyncRecord(..),
ExecAsyncOutput(..),
StatusAsyncOutput(..),
NotifyAsyncOutput(..),
AsyncOutput(..),
ResultClass(..),
AsyncClass(..),
Result(..),
Variable,
Value(..), Const, Tuple(..), List(..),
StreamRecord(..),
ConsoleStreamOutput(..),
TargetStreamOutput(..),
LogStreamOutput(..),
CString,
Response(..),
Notification(..), NotificationClass(..),
Stream(..), StreamClass(..),
render_command,
parse_output,
output_response,
output_notification,
output_stream,
GetToken(..),
asConst, asTuple, asList,
parameter_valid
) where
import Control.Applicative ((<$>), (<*>), (<*))
import Data.Char (isAscii)
import Data.List (find)
import Data.Maybe (isNothing)
import Text.ParserCombinators.Parsec hiding (token)
data Command
= CLICommand (Maybe Token) String
| MICommand (Maybe Token) Operation [Option] [Parameter]
type Operation = String
data Option = Option Parameter (Maybe Parameter)
data Parameter
= RawString String
| QuotedString String
render_command :: Command -> String
render_command cmd = r_command cmd ""
r_command :: Command -> ShowS
r_command (CLICommand tok str) = maybe id r_token tok . showString str . showString "\n"
r_command (MICommand tok operation options parameters) =
maybe id shows tok
. showString "-" . r_operation operation
. foldl (\f o -> f . showString " " . r_option o) id options
. (if null parameters
then id
else showString " --" . foldl (\f p -> f . showString " " . r_parameter p) id parameters)
. showString "\n"
r_operation :: Operation -> ShowS
r_operation op = (op++)
r_option :: Option -> ShowS
r_option (Option p p') =
r_parameter p
. maybe id (\x -> showString " " . r_parameter x) p'
r_parameter :: Parameter -> ShowS
r_parameter (RawString s) = showString s
r_parameter (QuotedString s) = shows s
r_token :: Token -> ShowS
r_token = shows
data Output
= Output [OutOfBandRecord] (Maybe ResultRecord)
deriving Show
data ResultRecord
= ResultRecord (Maybe Token) ResultClass [Result]
deriving Show
data OutOfBandRecord
= OOBAsyncRecord AsyncRecord
| OOBStreamRecord StreamRecord
deriving Show
data AsyncRecord
= ARExecAsyncOutput ExecAsyncOutput
| ARStatusAsyncOutput StatusAsyncOutput
| ARNotifyAsyncOutput NotifyAsyncOutput
deriving Show
data ExecAsyncOutput
= ExecAsyncOutput (Maybe Token) AsyncOutput
deriving Show
data StatusAsyncOutput
= StatusAsyncOutput (Maybe Token) AsyncOutput
deriving Show
data NotifyAsyncOutput
= NotifyAsyncOutput (Maybe Token) AsyncOutput
deriving Show
data AsyncOutput
= AsyncOutput AsyncClass [Result]
deriving Show
data ResultClass
= RCDone
| RCRunning
| RCConnected
| RCError
| RCExit
deriving (Show, Eq)
data AsyncClass
= ACStop
| ACThreadGroupAdded
| ACThreadGroupStarted
| ACThreadCreated
| ACRunning
| ACLibraryLoaded
| ACThreadExited
| ACThreadGroupExited
| ACBreakpointModified
deriving (Show, Eq)
data Result
= Result {
resVariable :: Variable
, resValue :: Value
}
deriving Show
type Variable = String
data Value
= VConst Const
| VTuple Tuple
| VList List
deriving Show
type Const = CString
data Tuple
= Tuple {
tupleResults :: [Result]
}
deriving Show
data List
= EmptyList
| ValueList [Value]
| ResultList [Result]
deriving Show
data StreamRecord
= SRConsoleStreamOutput ConsoleStreamOutput
| SRTargetStreamOutput TargetStreamOutput
| SRLogStreamOutput LogStreamOutput
deriving Show
data ConsoleStreamOutput
= ConsoleStreamOutput CString
deriving Show
data TargetStreamOutput
= TargetStreamOutput CString
deriving Show
data LogStreamOutput
= LogStreamOutput CString
deriving Show
type CString = String
parse_output :: String -> Output
parse_output str = case parse p_output "gdb" str of
Left pe -> error $ "parse failed: " ++ show pe
Right o -> o
p_output :: Parser Output
p_output = do
oob <- many p_outOfBandRecord
rr <- optionMaybe p_resultRecord
oob' <- many p_outOfBandRecord
string "(gdb) " >> newline >> eof
return $ Output (oob ++ oob') rr
p_resultRecord :: Parser ResultRecord
p_resultRecord =
ResultRecord <$> optionMaybe p_token <* char '^' <*> p_resultClass <*> many (char ',' >> p_result) <* newline
p_outOfBandRecord :: Parser OutOfBandRecord
p_outOfBandRecord =
try (p_asyncRecord >>= return . OOBAsyncRecord)
<|> (p_streamRecord >>= return . OOBStreamRecord)
p_asyncRecord :: Parser AsyncRecord
p_asyncRecord =
(p_execAsyncOutput >>= return . ARExecAsyncOutput)
<|> (p_statusAsyncOutput >>= return . ARStatusAsyncOutput)
<|> (p_notifyAsyncOutput >>= return . ARNotifyAsyncOutput)
p_execAsyncOutput :: Parser ExecAsyncOutput
p_execAsyncOutput =
ExecAsyncOutput <$> optionMaybe p_token <* char '*' <*> p_asyncOutput
p_statusAsyncOutput :: Parser StatusAsyncOutput
p_statusAsyncOutput =
StatusAsyncOutput <$> optionMaybe p_token <* char '+' <*> p_asyncOutput
p_notifyAsyncOutput :: Parser NotifyAsyncOutput
p_notifyAsyncOutput =
NotifyAsyncOutput <$> optionMaybe p_token <* char '=' <*> p_asyncOutput
p_asyncOutput :: Parser AsyncOutput
p_asyncOutput =
AsyncOutput <$> p_asyncClass <*> many (char ',' >> p_result) <* newline
p_resultClass :: Parser ResultClass
p_resultClass =
try (string "done" >> return RCDone)
<|> try (string "running" >> return RCRunning)
<|> try (string "connected" >> return RCConnected)
<|> try (string "error" >> return RCError)
<|> (string "exit" >> return RCExit)
p_asyncClass :: Parser AsyncClass
p_asyncClass =
try (string "stopped" >> return ACStop)
<|> try (string "thread-group-added" >> return ACThreadGroupAdded)
<|> try (string "thread-group-started" >> return ACThreadGroupStarted)
<|> try (string "thread-created" >> return ACThreadCreated)
<|> try (string "running" >> return ACRunning)
<|> try (string "thread-exited" >> return ACThreadExited)
<|> try (string "thread-group-exited" >> return ACThreadGroupExited)
<|> try (string "breakpoint-modified" >> return ACBreakpointModified)
<|> (string "library-loaded" >> return ACLibraryLoaded)
p_result :: Parser Result
p_result =
Result <$> p_variable <* char '=' <*> p_value
p_variable :: Parser Variable
p_variable = many1 (letter <|> digit <|> oneOf "_-")
p_value :: Parser Value
p_value =
(p_const >>= return . VConst)
<|> (p_tuple >>= return . VTuple)
<|> (p_list >>= return . VList)
p_const :: Parser Const
p_const = p_cString
p_tuple :: Parser Tuple
p_tuple = try p_emptyTuple <|> p_filledTuple
where
p_emptyTuple = string "{}" >> return (Tuple [])
p_filledTuple = do
_ <- char '{'
first <- p_result
rest <- many (char ',' >> p_result)
_ <- char '}'
return $ Tuple (first:rest)
p_list :: Parser List
p_list = try p_emptyList <|> try p_valueList <|> p_resultList
where
p_emptyList = string "[]" >> return EmptyList
p_valueList = do
_ <- char '['
first <- p_value
rest <- many (char ',' >> p_value)
_ <- char ']'
return $ ValueList (first:rest)
p_resultList = do
_ <- char '['
first <- p_result
rest <- many (char ',' >> p_result)
_ <- char ']'
return $ ResultList (first:rest)
p_streamRecord :: Parser StreamRecord
p_streamRecord = do
sr <- anyStreamRecord
_ <- newline
return sr
where
anyStreamRecord =
(p_consoleStreamOutput >>= return . SRConsoleStreamOutput)
<|> (p_targetStreamOutput >>= return . SRTargetStreamOutput)
<|> (p_logStreamOutput >>= return . SRLogStreamOutput)
p_consoleStreamOutput :: Parser ConsoleStreamOutput
p_consoleStreamOutput = char '~' >> p_cString >>= return . ConsoleStreamOutput
p_targetStreamOutput :: Parser TargetStreamOutput
p_targetStreamOutput = char '@' >> p_cString >>= return . TargetStreamOutput
p_logStreamOutput :: Parser LogStreamOutput
p_logStreamOutput = char '&' >> p_cString >>= return . LogStreamOutput
p_cString :: Parser CString
p_cString = between (char '"') (char '"') (many p_cchar)
where
p_cchar = p_cbackslash
<|> noneOf "\""
p_cbackslash = do
_ <- char '\\'
c <- anyChar
case c of
'\\' -> return '\\'
'n' -> return '\n'
'"' -> return '"'
_ -> fail $ "unknown backslash escape: " ++ show c
p_token :: Parser Token
p_token = many1 digit >>= return . read
data Response
= Response {
respClass :: ResultClass
, respResults :: [Result]
}
deriving (Show)
data Notification
= Notification {
notiClass :: NotificationClass
, notiAsyncClass :: AsyncClass
, notiResults :: [Result]
}
deriving Show
data NotificationClass
= Exec
| Status
| Notify
deriving (Show, Eq)
data Stream
= Stream StreamClass String
deriving Show
data StreamClass
= Console
| Target
| Log
deriving Show
output_response :: Output -> Maybe Response
output_response (Output _ Nothing) = Nothing
output_response (Output _ (Just (ResultRecord _ rc rs))) = Just $ Response rc rs
output_notification :: Output -> [Notification]
output_notification (Output oobs _) = map (notification . unp) $ filter isNotification oobs
where
isNotification (OOBAsyncRecord _) = True
isNotification _ = False
unp (OOBAsyncRecord x) = x
unp x = error $ "unexpected parameter: " ++ show x
notification (ARExecAsyncOutput (ExecAsyncOutput _ (AsyncOutput ac rs))) = Notification Exec ac rs
notification (ARStatusAsyncOutput (StatusAsyncOutput _ (AsyncOutput ac rs))) = Notification Status ac rs
notification (ARNotifyAsyncOutput (NotifyAsyncOutput _ (AsyncOutput ac rs))) = Notification Notify ac rs
output_stream :: Output -> [Stream]
output_stream (Output oobs _) = map (stream . unp) $ filter isStream oobs
where
isStream (OOBStreamRecord _) = True
isStream _ = False
unp (OOBStreamRecord x) = x
unp x = error $ "unexpected parameter: " ++ show x
stream (SRConsoleStreamOutput (ConsoleStreamOutput s)) = Stream Console s
stream (SRTargetStreamOutput (TargetStreamOutput s)) = Stream Target s
stream (SRLogStreamOutput (LogStreamOutput s)) = Stream Log s
asConst :: Value -> Maybe Const
asConst (VConst x) = Just x
asConst _ = Nothing
asTuple :: Value -> Maybe Tuple
asTuple (VTuple x) = Just x
asTuple _ = Nothing
asList :: Value -> Maybe List
asList (VList x) = Just x
asList _ = Nothing
type Token = Int
class GetToken a where
get_token :: a -> Maybe Token
instance GetToken ResultRecord where
get_token (ResultRecord token _ _) = token
instance GetToken Command where
get_token (CLICommand token _) = token
get_token (MICommand token _ _ _) = token
instance GetToken Output where
get_token (Output _ (Just r)) = get_token r
get_token _ = Nothing
instance GetToken OutOfBandRecord where
get_token (OOBAsyncRecord r) = get_token r
get_token (OOBStreamRecord _) = Nothing
instance GetToken AsyncRecord where
get_token (ARExecAsyncOutput x) = get_token x
get_token (ARStatusAsyncOutput x) = get_token x
get_token (ARNotifyAsyncOutput x) = get_token x
instance GetToken ExecAsyncOutput where
get_token (ExecAsyncOutput token _) = token
instance GetToken StatusAsyncOutput where
get_token (StatusAsyncOutput token _) = token
instance GetToken NotifyAsyncOutput where
get_token (NotifyAsyncOutput token _) = token
parameter_valid :: Parameter -> Bool
parameter_valid (RawString s) = validParam s
parameter_valid (QuotedString s) = validParam s
validParam :: String -> Bool
validParam param
| null param = False
| isCString param = isNothing $ find (not . isAscii) param
| otherwise = isNothing $ find isSpecial param
where
isCString ('"':rest) = last rest == '"'
isCString _ = False
isSpecial ' ' = True
isSpecial '-' = True
isSpecial '\n' = True
isSpecial '"' = True
isSpecial _ = False