module Ideas.Encoding.DecoderJSON
( JSONDecoder, jsonDecoder
) where
import Control.Monad
import Data.Char
import Data.Maybe
import Ideas.Common.Library hiding (exerciseId, symbol)
import Ideas.Common.Traversal.Navigator
import Ideas.Encoding.Encoder
import Ideas.Service.State
import Ideas.Service.Types hiding (String)
import Ideas.Text.JSON
import qualified Ideas.Service.Types as Tp
type JSONDecoder a = Decoder a JSON
jsonDecoder :: TypedDecoder a JSON
jsonDecoder tp = decoderFor $ \json ->
case json of
Array xs -> decodeType tp // xs
_ -> fail "expecting an array"
decodeType :: Type a t -> Decoder a [JSON] t
decodeType tp =
case tp of
Tag _ t -> decodeType t
Iso p t -> from p <$> decodeType t
Pair t1 t2 -> do
a <- decodeType t1
b <- decodeType t2
return (a, b)
t1 :|: t2 ->
(Left <$> decodeType t1) `mplus`
(Right <$> decodeType t2)
Unit -> return ()
Const QCGen -> getQCGen
Const Script -> getScript
Const t -> symbol >>= \a -> decodeConst t // a
_ -> fail $ "No support for argument type: " ++ show tp
decodeConst :: Const a t -> JSONDecoder a t
decodeConst tp =
case tp of
State -> decodeState
Context -> decodeContext
Exercise -> getExercise
Environment -> decodeEnvironment
Location -> decodeLocation
Term -> decoderFor (return . jsonToTerm)
Int -> decoderFor fromJSON
Tp.String -> decoderFor fromJSON
Id -> decodeId
Rule -> decodeRule
_ -> fail $ "No support for argument type: " ++ show tp
decodeRule :: JSONDecoder a (Rule (Context a))
decodeRule = do
ex <- getExercise
decoderFor $ \json ->
case json of
String s -> getRule ex (newId s)
_ -> fail "expecting a string for rule"
decodeId :: JSONDecoder a Id
decodeId = decoderFor $ \json ->
case json of
String s -> return (newId s)
_ -> fail "expecting a string for id"
decodeLocation :: JSONDecoder a Location
decodeLocation = decoderFor $ \json ->
case json of
String s -> toLocation <$> readM s
_ -> fail "expecting a string for a location"
decodeState :: JSONDecoder a (State a)
decodeState = do
ex <- getExercise
decoderFor $ \json ->
case json of
Array [a] -> setInput a >> decodeState
Array (String _code : pref : term : jsonContext : rest) -> do
pts <- decodePaths // pref
a <- decodeExpression // term
env <- decodeEnvironment // jsonContext
let loc = envToLoc env
ctx = navigateTowards loc $ deleteRef locRef $
setEnvironment env $ inContext ex a
prfx = pts (strategy ex) ctx
case rest of
[] -> return $ makeState ex prfx ctx
[Array [String user, String session, String startterm]] ->
return (makeState ex prfx ctx)
{ stateUser = Just user
, stateSession = Just session
, stateStartTerm = Just startterm
}
_ -> fail $ "invalid state" ++ show json
_ -> fail $ "invalid state" ++ show json
envToLoc :: Environment -> Location
envToLoc env = toLocation $ fromMaybe [] $ locRef ? env >>= readM
locRef :: Ref String
locRef = makeRef "location"
decodePaths :: JSONDecoder a (LabeledStrategy (Context a) -> Context a -> Prefix (Context a))
decodePaths =
decoderFor $ \json ->
case json of
String p
| p ~= "noprefix" -> return (\_ _ -> noPrefix)
| otherwise -> replayPaths <$> readPaths p
_ -> fail "invalid prefixes"
where
x ~= y = filter isAlphaNum (map toLower x) == y
decodeEnvironment :: JSONDecoder a Environment
decodeEnvironment = decoderFor $ \json ->
case json of
String "" -> return mempty
Object xs -> foldM (flip add) mempty xs
_ -> fail $ "invalid context: " ++ show json
where
add (k, String s) = return . insertRef (makeRef k) s
add (k, Number n) = return . insertRef (makeRef k) (show n)
add _ = fail "invalid item in context"
decodeContext :: JSONDecoder a (Context a)
decodeContext = do
ex <- getExercise
inContext ex <$> decodeExpression
decodeExpression :: JSONDecoder a a
decodeExpression = withJSONTerm $ \b -> getExercise >>= decoderFor . f b
where
f True ex json =
case hasJSONView ex of
Just v -> matchM v json
Nothing -> fail "JSON encoding not supported by exercise"
f False ex json =
case json of
String s -> either fail return (parser ex s)
_ -> fail "Expecting a string when reading a term"