module Control.Monad.FreeMarker where
import Data.Monoid
import Control.Applicative
import Data.String
import Data.List
import Text.Blaze.Internal (preEscapedString)
import qualified Text.Blaze.Html.Renderer.Pretty as BR
import qualified Text.Blaze.Html5 as B5
litH :: B5.Html -> FM ()
litH h = lit $ BR.renderHtml h
type VarCounter = Int
data FM a = FM (VarCounter -> (String, VarCounter, a))
data FEX a b = FEX String
data ENum
data EString
data EBool
data EList a
data EDate
data EHash
data EName
data E
true :: FEX E EBool
true = FEX "true"
false :: FEX E EBool
false = FEX "false"
now :: FEX E EDate
now = FEX ".now"
usF :: String -> FEX a b -> FEX c d
usF s e = FEX ( s ++ parShow e )
usM :: FEX a b -> String -> FEX c d
usM e s = FEX ( parShow e ++ s )
parShow :: FEX a b -> String
parShow a = "(" ++ show a ++ ")"
(>~) :: FEX a b -> FEX c b -> FEX E EBool
a >~ b = FEX ( parShow a ++ " > " ++ parShow b )
(>=~) :: FEX a b -> FEX c b -> FEX E EBool
a >=~ b = FEX ( parShow a ++ " >= " ++ parShow b )
(<~) :: FEX a b -> FEX c b -> FEX E EBool
a <~ b = FEX ( parShow a ++ " < " ++ parShow b )
(<=~) :: FEX a b -> FEX c b -> FEX E EBool
a <=~ b = FEX ( parShow a ++ " <= " ++ parShow b )
(=~) :: FEX a b -> FEX c b -> FEX E EBool
a =~ b = FEX ( parShow a ++ " == " ++ parShow b )
(&~) :: FEX a EBool -> FEX b EBool -> FEX E EBool
a &~ b = FEX ( parShow a ++ " && " ++ parShow b )
instance Show (FEX a b) where
show (FEX s) = s
renderFM :: FM a -> String
renderFM f = renderFMW f 0
renderFMW :: FM a -> VarCounter -> String
renderFMW (FM a) v = let (s,_,_) = a v in s
instance IsString (FM a) where
fromString s = FM (\v -> (s, v, undefined))
lit :: String -> FM ()
lit s = FM (\v -> (s, v, ()))
lite :: String -> FEX a b
lite s = FEX s
size :: FEX a (EList b) -> FEX E ENum
size e = usM e "?size"
has :: FEX a EHash -> String -> FEX E EBool
has e n = usM (FEX ( parShow e ++ "." ++ n )) "??"
data LC b = forall a. LC (FEX a b)
instance Show (LC b) where
show (LC a) = show a
type LitLET a = FEX E (EList a)
litLEmpty :: FEX E (EList a)
litLEmpty = FEX "[]"
litL :: [LC b] -> FEX E (EList b)
litL l = FEX ( "["++ i ++"]" )
where
i = concat $ intersperse "," (map show l)
litNL :: (Num a, Show a) => [a] -> FEX E (EList ENum)
litNL l = litL $ map (LC . litN) l
litSL :: [String] -> FEX E (EList EString)
litSL l = litL $ map (LC . litS) l
litBL :: [Bool] -> FEX E (EList EBool)
litBL l = litL $ map (LC . litB) l
(>:) :: FEX a b -> [LC b] -> [LC b]
e >: l = ( LC e ):l
infixr 1 >:
exlitl = do
foo <- dec $ litS "afds"
D$< (litL [ LC $ litS "asd" , LC $ foo ])
D$< (litL ( litS "asd" >:
litS "lol" >: [] ))
list :: FEX a (EList lt) -> (FEX EName lt -> FM b) -> FM ()
list l b = FM (\v ->
let
i = FEX ("var" ++ (show v))
startList = "[#list "++ show l ++" as "++ show i ++"]\n"
endList = "\n[/#list]"
code = startList ++ renderFMW (b i) v ++ endList
in
(code, succ v, ())
)
if_ :: FEX a EBool -> FM b -> FM ()
if_ (FEX cond) b1 = FM (\v ->
let ns = (si cond) ++ renderFMW b1 v ++ ei in
(ns,v,())
)
where
si x = "[#if "++x++"]"
ei = "\n[/#if]"
ifel :: FEX a EBool -> FM b -> FM c -> FM ()
ifel (FEX cond) b1 b2 = FM (\v ->
let ns = (si cond) ++ renderFMW b1 v ++ eli ++ renderFMW b2 v ++ ei in
(ns, v, ())
)
where
si x = "[#if "++x++"]\n"
eli = "\n[#else]\n"
ei = "\n[/#if]"
getVarCounter :: FM VarCounter
getVarCounter = FM ( \v -> ("",v,v) )
dec :: FEX a b -> FM (FEX EName b)
dec ex = namedDec "var" ex
namedDec :: String -> FEX a b -> FM (FEX EName b)
namedDec n ex = do
vc <- getVarCounter
unsafeNamedDeclaration (n ++ show vc) ex
und :: String -> FEX a b -> FM (FEX EName b)
und n ex = unsafeNamedDeclaration n ex
unsafeNamedDeclaration :: String -> FEX a b -> FM (FEX EName b)
unsafeNamedDeclaration n ex = FM (\v ->
let
name = n
code = "[#assign " ++ name ++ " = " ++ show ex ++ "/]"
in
(code, (succ v), (FEX name))
)
assign :: FEX EName a -> FEX b a -> FM ()
assign (FEX name) ex = FM (\v -> (code, v, ()))
where
code = "[#assign " ++ name ++ " = " ++ show ex ++ "/]"
urlEncode :: FEX a EString -> FEX E EString
urlEncode p = FEX $ parShow p ++ "?url(\"UTF-8\")"
def :: FEX a b -> FEX a b -> FEX a b
def a b = FEX ( parShow a ++ "!" ++ parShow b )
data D = D
data R = R
($<) :: D -> FEX a b -> FM ()
y $< x = FM ( \v -> ( ("${" ++ show x ++ "}"), v, () ) )
($<<) :: R -> FEX a b -> B5.Html
y $<< x = preEscapedString $ renderFM $ FM ( \v -> ( ("${" ++ show x ++ "}"), v, () ) )
instance Functor FM where
fmap f (FM o) = FM (\v -> let (s,v2,a) = o v in (s,v2,f a))
instance Applicative FM where
pure = return
(FM f1) <*> (FM f2) = r
where
r = FM (\v -> let
(s2,v2,a2) = f1 v
(s3,v3,a3) = f2 v in
( s2 ++ s3 , v2 + v3 , a2 a3 ))
instance Monad FM where
return x = FM (\v -> ("", v, x) )
(FM fm1) >>= f = FM (\v ->
let
(s,v2,a1) = fm1 v
(FM fm2) = f a1
(s2,v3,a2) = fm2 v2
in
( s ++ "\n" ++ s2 , v3, a2 )
)
(-~) :: FEX a ENum -> FEX a ENum -> FEX E ENum
f1 -~ f2 = FEX ( parShow f1 ++ " - " ++ parShow f2 )
(+~) :: FEX a ENum -> FEX a ENum -> FEX E ENum
f1 +~ f2 = FEX ( parShow f1 ++ " + " ++ parShow f2 )
litN :: (Show a, Num a) => a -> FEX E ENum
litN x = FEX $ show x
litS :: String -> FEX E EString
litS x = FEX ( "\"" ++ x ++ "\"" )
litB :: Bool -> FEX E EBool
litB True = FEX "true"
litB False = FEX "false"
test = do
foo <- dec $ litB True
bar <- dec $ litN 123
assign foo true
ifel ( litN (1) =~ bar )
(do
"foo :("
)
(do
"foo is : "
(D$< foo)
)
ifel foo
"t2"
"t3"
"test"
D$< (urlEncode $ litS "<lol>\"\"")