module Network.Anonymous.Tor.Protocol.Parser ( quotedString
, unquotedString
, reply
, key
, keyValue
, value
, token
, tokens ) where
import Control.Applicative ((*>), (<$>), (<*), (<*>),
(<|>))
import qualified Data.Attoparsec.ByteString as Atto
import qualified Data.Attoparsec.ByteString.Char8 as Atto8
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BS8
import Data.Word (Word8)
import qualified Network.Anonymous.Tor.Protocol.Parser.Ast as A
doubleQuote :: Word8
doubleQuote = 34
singleQuote :: Word8
singleQuote = 39
backslash :: Word8
backslash = 92
minus :: Word8
minus = 45
plus :: Word8
plus = 43
space :: Word8
space = 32
equals :: Word8
equals = 61
quotedString :: Atto.Parser BS.ByteString
quotedString =
let quoted :: Word8
-> Atto.Parser BS.ByteString
quoted c = (Atto.word8 c *> escaped c <* Atto.word8 c)
escaped :: Word8 -> Atto.Parser BS.ByteString
escaped c = BS8.concat <$> Atto8.many'
( Atto8.string (BS8.pack "\\\\")
<|> Atto8.string (BS.pack [backslash, c])
<|> (BS.singleton <$> Atto.satisfy (/= c)))
in quoted doubleQuote <|> quoted singleQuote
unquotedString :: Atto.Parser BS.ByteString
unquotedString =
Atto8.takeWhile1 (not . Atto8.isSpace)
reply :: Atto.Parser [A.Line]
reply = do
replies <- Atto.many' (replyLine minus <|> replyLine plus)
lastReply <- replyLine space
return (replies ++ [lastReply])
where
replyLine :: Word8 -> Atto.Parser A.Line
replyLine c = A.Line <$> Atto8.decimal <*> (Atto.word8 c *> tokens) <* Atto8.endOfLine
value :: Atto.Parser BS.ByteString
value =
quotedString <|> unquotedString
keyValue :: Atto.Parser A.Token
keyValue = do
A.Token k _ <- key
_ <- Atto.word8 equals
v <- value
return (A.Token k (Just v))
key :: Atto.Parser A.Token
key =
let isKeyEnd '=' = True
isKeyEnd c = Atto8.isSpace c
in flip A.Token Nothing <$> Atto8.takeWhile1 (not . isKeyEnd)
token :: Atto.Parser A.Token
token =
Atto.skipWhile Atto8.isHorizontalSpace *> (keyValue <|> key)
tokens :: Atto.Parser [A.Token]
tokens =
Atto.many' token