module Data.Rison.Parser where
import Control.Applicative ( (<|>) )
import Data.Aeson ( Value(..) )
import qualified Data.Attoparsec.ByteString as A
import Data.Attoparsec.ByteString.Char8 ( Parser
, scientific )
import qualified Data.ByteString as BS ( cons )
import qualified Data.HashMap.Strict as H
import Data.Monoid ( (<>) )
import Data.Strings ( byteToChar )
import qualified Data.Text as T
import Data.Text.Encoding ( decodeUtf8 )
import qualified Data.Vector as V
import Data.Word ( Word8 )
#define BACKSLASH 92
#define CLOSE_CURLY 125
#define CLOSE_SQUARE 93
#define CLOSE_BRACKET 41
#define COMMA 44
#define DOUBLE_QUOTE 34
#define SINGLE_QUOTE 39
#define OPEN_CURLY 123
#define OPEN_SQUARE 91
#define OPEN_BRACKET 40
#define COLON 58
#define EXCLAMATION 33
#define C_0 48
#define C_9 57
#define C_A 65
#define C_F 70
#define C_a 97
#define C_f 102
#define C_n 110
#define C_t 116
rison :: Parser Value
rison = value
value :: Parser Value
value = object <|> array <|> rstring <|> number <|> boolean <|> nulll
A.<?> "unsupported value type"
object :: Parser Value
object = do
m <- (A.word8 OPEN_BRACKET *> objectValues <* A.word8 CLOSE_BRACKET)
A.<?> "object"
return $ Object m
objectValues :: Parser (H.HashMap T.Text Value)
objectValues = do
w <- A.peekWord8'
if w == CLOSE_BRACKET
then return H.empty
else loop H.empty
where
loop m0 = do
ident <- identifier <* A.word8 COLON
A.<?> "object property identifier"
v <- value
A.<?> "object property value"
let !m = H.insert ident v m0
ch <- A.peekWord8'
if ch == COMMA
then A.word8 COMMA *> loop m
else return m
array :: Parser Value
array = do
v <- A.word8 EXCLAMATION *>
A.word8 OPEN_BRACKET *> arrayValues <* A.word8 CLOSE_BRACKET
return $ Array v
arrayValues :: Parser (V.Vector Value)
arrayValues = do
w <- A.peekWord8'
if w == CLOSE_BRACKET
then return V.empty
else loop []
where
loop acc = do
v <- value A.<?> "array value"
ch <- A.peekWord8'
if ch == COMMA
then A.word8 COMMA *> loop (v:acc)
else return (V.reverse (V.fromList (v:acc)))
boolean :: Parser Value
boolean = true <|> false
A.<?> "boolean"
where
true = do
A.word8 EXCLAMATION
A.word8 C_t
return $ Bool True
false = do
A.word8 EXCLAMATION
A.word8 C_f
return $ Bool False
nulll :: Parser Value
nulll = do
A.word8 EXCLAMATION
A.word8 C_n
return Null
rstring :: Parser Value
rstring = do
let sq = SINGLE_QUOTE
s <- identifier <|>
A.word8 sq *> rstring_ sq <* A.word8 sq
return $ String s
rstring_ :: Word8 -> Parser T.Text
rstring_ endCh = loop "" A.<?> "rstring_"
where
loop acc = do
w <- A.peekWord8'
if w == endCh
then return acc
else do
ch <- if w == EXCLAMATION || w == BACKSLASH
then A.word8 w *> A.anyWord8
else A.anyWord8
loop $ T.snoc acc $ byteToChar ch
number :: Parser Value
number = Number <$> scientific
A.<?> "number"
identifier :: Parser T.Text
identifier = do
ch1 <- A.satisfy $ A.inClass "a-zA-Z_"
rest <- A.takeWhile $ A.inClass "0-9a-zA-Z/_-"
return $ decodeUtf8 (ch1 `BS.cons` rest)