-- The JSDict data type and JSDictM monad {-# LANGUAGE GADTs #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleContexts #-} module JSDict ( JSDictM , shareFun , buildFun2 , buildStmt2 , buildStmt3 , newSM , expStmt , Def (..) , runJSDictM ) where import Param import JS import Memo import Language.HJavaScript.Syntax import Control.Monad.Identity import Control.Monad.Trans import Control.Monad.Trans.Writer import Control.Monad.Trans.State --------------------------------------------------------------- data Def where SMDef :: String -> {-n :-}Int -> ([String]{-Vec n String-} -> Memo String) -> Def JSDef :: String -> Def type JSDictM = WriterT [Def] (StateT Int Identity) runJSDictM :: JSDictM a -> ((a, [Def]), Int) runJSDictM = runIdentity . flip runStateT (1 :: Int) . runWriterT data JSM x r where Ret :: r -> JSM () r Bind :: JSM () k -> (k -> JSM x r) -> JSM x r VDA :: Exp t -> JSM () (Exp t) ES :: Exp t -> JSM () () type JSMB r = JSM r () instance Monad (JSM ()) where return = Ret (>>=) = Bind expStmt :: Exp t -> JSM () () expStmt = ES runJSM :: JSM r () -> JSDictM (Block r) runJSM = fmap fst . runJSM' infixl 1 |+| (|+|) :: forall t. Block () -> Stmt t -> Block t (|+|) = Sequence runJSM' :: JSM r a -> JSDictM (Block r, a) runJSM' (Ret r) = return (EmptyBlock, r) runJSM' (VDA e) = do i <- newId let v = 'd': i return (EmptyBlock |+| VarDeclAssign v e, JConst v) runJSM' (ES e) = return (EmptyBlock |+| ExpStmt e, ()) runJSM' (Bind m f) = do (a, x) <- runJSM' m (b, y) <- runJSM' $ f x return (a .+ b, y) (.+) :: Block () -> Block a -> Block a a .+ EmptyBlock = a a .+ Sequence b s = Sequence (a .+ b) s newId :: JSDictM String newId = do i <- lift get lift $ put $ i + 1 return $ show i newVar :: JSDictM (Var a) newVar = fmap (JParam . ('v':)) newId newFunName :: JSDictM String newFunName = fmap ('f':) newId tellJS :: (Show a) => a -> JSDictM () tellJS f = tell [JSDef $ show f] tellSM :: String -> Int -> ([String] -> Memo String) -> JSDictM () tellSM name i f = tell [SMDef name i f] getPre :: Exp String -> Exp (String -> ()) -> Stmt () getPre = curry $ ExpStmt . -- JI . JCall (JConst "getPre") infixr 5 `strPlus` strPlus :: Exp String -> Exp String -> Exp String strPlus x y = JBinOp x Plus y (|=|) :: String -> Exp String -> Exp String t |=| x = JString (t ++ "=") `strPlus` encodeURIComponent x encodeURIComponent :: Exp String -> Exp String encodeURIComponent = JCall $ JConst "encodeURIComponent" infixr 3 |&| (|&|) :: Exp String -> Exp String -> Exp String a |&| b = a `strPlus` JString "&" `strPlus` b newSM :: forall t1 t2 t3 a . (FromString t1, FromString t2, ToString t3) => (t1 -> t2 -> Memo t3) -> JSDictM ( Exp t1 -> Exp t2 -> (Exp t3 -> Exp a) -> Exp ()) newSM body = do name <- newFunName (w1 :: Var String) <- newVar (w2 :: Var String) <- newVar (w3 :: Var (String -> ())) <- newVar (v1 :: Var t3) <- newVar tellJS $ JFunction (Just name) (w1, w2, w3) $ EmptyBlock |+| getPre ( "sm" |=| JString name |&| "v1" |=| val w1 |&| "v2" |=| val w2) (val w3) tellSM name 2 $ \[x, y] -> fmap ts $ body (fs x) (fs y) return $ \w1 w2 w3f -> JCall (JConst name) (w1, w2, JFunction Nothing v1 $ EmptyBlock |+| ExpStmt (w3f $ val v1)) buildFun :: forall t r . ParamType t => (Exp t -> Block r) -> JSDictM (Exp t -> Exp r) buildFun body = do name <- newFunName v <- newVar tellJS $ JFunction (Just name) v $ body $ val v return $ JCall $ JConst name shareFun :: forall t r . ParamType t => (Exp t -> Exp r) -> JSDictM (Exp t -> Exp r) shareFun f = buildFun $ Sequence EmptyBlock . Return . f buildFun2 :: forall t1 t2 r . ParamType (t1,t2) => (Exp t1 -> Exp t2 -> JSMB r) -> JSDictM (Exp t1 -> Exp t2 -> Exp r) buildFun2 body = do name <- newFunName v1 <- newVar v2 <- newVar b <- runJSM $ body (val v1) (val v2) tellJS $ JFunction (Just name) (v1,v2) b return $ \v1 v2 -> JCall (JConst name) (v1,v2) buildStmt2 :: forall t1 t2 r . ParamType (t1,t2) => (Exp t1 -> Exp t2 -> JSMB r) -> JSDictM (Exp t1 -> Exp t2 -> Stmt ()) buildStmt2 f = fmap (\f a b -> ExpStmt $ f a b) $ buildFun2 f buildFun3 :: forall t1 t2 t3 r . ParamType (t1,t2,t3) => (Exp t1 -> Exp t2 -> Exp t3 -> JSMB r) -> JSDictM (Exp t1 -> Exp t2 -> Exp t3 -> Exp r) buildFun3 body = do name <- newFunName v1 <- newVar v2 <- newVar v3 <- newVar b <- runJSM $ body (val v1) (val v2) (val v3) tellJS $ JFunction (Just name) (v1,v2,v3) b return $ \v1 v2 v3 -> JCall (JConst name) (v1,v2,v3) buildStmt3 :: forall t1 t2 t3 r . ParamType (t1,t2,t3) => (Exp t1 -> Exp t2 -> Exp t3 -> JSMB ()) -> JSDictM (Exp t1 -> Exp t2 -> Exp t3 -> Stmt ()) buildStmt3 f = fmap (\f t1 t2 t3 -> ExpStmt $ f t1 t2 t3) $ buildFun3 f