module WASH.HTML.HTMLTemplates where
import WASH.HTML.HTMLBase
import WASH.Utility.SHA1
import WASH.Utility.JavaScript
import qualified WASH.Utility.Base32 as Base32
import Monad (unless)
import List ((\\))
data ST s a = ST { unST :: s -> (a, s) }
instance Monad (ST s) where
return x = ST (\s -> (x, s))
m >>= f = ST (\s -> let (x', s') = unST m s
in unST (f x') s')
runST :: s -> ST s a -> a
runST s m = fst (unST m s)
getST :: ST s s
getST = ST (\s -> (s, s))
setST :: s -> ST s ()
setST s = ST (const ((), s))
data Names = Names { ntable :: [(String, Int)], nseen :: [Int] }
----------------------------------------------------------------------
-- template generation
----------------------------------------------------------------------
showTemplatified :: ELEMENT_ -> ShowS
showTemplatified = showTemplate . analyze
showTemplate :: (Template, Mt) -> ShowS
showTemplate (tis, mt) =
let ts = closed mt
ndefs = length ts
at = actuals mt
st = buildStringTableArgs at
cst = cleanupStringTable ndefs st
shownargs = runST (Names { ntable = cst, nseen = [] })
(showActualArgs at)
in
showDefinitions ndefs ts .
showString "\n"
showDefinitions n [] =
let scriptName = "STRINGBASE"
in
showString "\n"
showDefinitions n ((scriptName, (nr, nformals, t1)) :ts) =
showString "\n" .
showDefinitions (n-1) ts
showsIdent n = showString (identlist !! n)
charlist = (['A'..'Z'] ++ ['a'..'z'])
alphanumlist = ['0'..'9'] ++ charlist
identlist = ([ [c] | c <- charlist ]
++ [ [c,d] | c <- charlist, d <- alphanumlist ])
\\ ["S","o","v"]
showBody [] =
id
showBody [ti] =
showStatement ti
showBody (ti:tis) =
showBody tis .
showString "," .
showStatement ti
showStatement (TOut str) =
showString (jsShow str)
showStatement (TVar n) =
showsIdent n
showStatement (TCall fname args) =
error "this should not happen"
showFormals 0 =
id
showFormals n =
(if n>1 then showFormals (n-1) . showString "," else id) .
showsIdent n
showActual (TOut str) =
do names <- getST
let cst = ntable names
seen = nseen names
case lookup str cst of
Nothing ->
return $ showString (jsShow str)
Just strident ->
if strident `elem` seen
then return $ showsIdent strident
else
do setST (names { nseen = strident : seen })
return $
(showsIdent strident . showChar '=' . showString (jsShow str))
showActual (TVar n) =
error "this should not happen"
showActual (TCall fname []) =
return $ showsIdent fname
showActual (TCall fname args) =
do saa <- showActualArgs args
return (showsIdent fname . showString "(" .
saa .
showString ")")
showActualArgs [] =
return id
showActualArgs [arg] =
showActualArg arg
showActualArgs (arg: args) =
do x1 <- showActualArgs args
x2 <- showActualArg arg
return (x1 . showString "," . x2)
showActualArg [x] =
showActual x
showActualArg xs =
do hxs <- h xs
return (showString "[" . hxs . showString "]")
where
h [] = return id
h [x] = showActual x
h (x:xs) = do sax <- showActual x
hxs <- h xs
return (sax . showChar ',' . hxs)
showStringTable =
foldr g id
where
g (str, n) showrest =
showString "var " . showsIdent n . showString "=" . showString (jsShow str) .
showString ";\n" . showrest
cleanupStringTable ndefs st =
zip [str | (str, n) <- st, n > 3 || length str > 2 && n > 1]
[(ndefs+1) ..]
unite st1 st2 =
foldr g st1 st2
where g p@(str,i) st1 =
case lookup str st1 of
Nothing -> p:st1
Just j -> (str, i+j):[ p | (p@(str',_)) <- st1, str' /= str ]
buildStringTableArgs =
foldr unite [] . map buildStringTableArg
buildStringTableArg =
foldr unite [] . map buildStringTableActual
buildStringTableActual (TOut str) =
[(str, 1)]
buildStringTableActual (TVar n) =
error "this should not happen"
buildStringTableActual (TCall fname args) =
buildStringTableArgs args
----------------------------------------------------------------------
-- template analysis
----------------------------------------------------------------------
analyze :: ELEMENT_ -> (Template, Mt)
analyze e = unM (collect e STATIC []) mt0
data Mt = Mt
{ open :: Templates
, closed :: [(String, (Int, Int, Template))]
, dynamics :: [Templates]
, actuals :: Templates
, count :: Int
}
deriving Show
mt0 = Mt { open = []
, closed = []
, dynamics = []
, actuals = []
, count = 0
}
type Templates = [Template]
type Template = [TemplateItem]
data TemplateItem
= TOut String
| TVar Int
| TCall Int [Template]
deriving Show
tout :: String -> [TemplateItem] -> [TemplateItem]
tout s (TOut s' : rest) = (TOut (s'++s) : rest)
tout s tis = TOut s : tis
data M a = M { unM :: Mt -> (a, Mt) }
instance Monad M where
return a = M (\t -> (a, t))
m >>= f = M (\t -> let (a,t') = unM m t in unM (f a) t')
pushOpen :: Template -> M ()
pushOpen t = M (\mt -> ((), mt { open = t : open mt
, dynamics = actuals mt : dynamics mt
, actuals = []
} ))
popOpen :: M Template
popOpen = M (\mt -> (head (open mt), mt { open = tail (open mt)
, actuals = head (dynamics mt)
, dynamics = tail (dynamics mt)
}))
pushClosed :: Template -> M Int
pushClosed tis =
M (\mt ->
let defs = closed mt
name = Base32.encode (sha1 (show tis))
in
case lookup name defs of
Nothing ->
let next = length (closed mt) + 1 in
(next, mt { closed = (name, (next, length (actuals mt), tis)) : defs })
Just (last, nargs, _) ->
(last, mt))
pushActuals :: Template -> M Int
pushActuals tis =
M (\mt -> (length (actuals mt) + 1, mt { actuals = tis : actuals mt }))
getActuals :: M Templates
getActuals = M (\mt -> (actuals mt, mt))
get :: (Mt -> x) -> M x
get f = M (\mt -> (f mt, mt))
mergeActuals :: Template -> M ()
mergeActuals tis =
M (\mt ->
let act0 : acts = actuals mt in
((), mt { actuals = (act0 ++ tis) : actuals mt }))
maybePushActuals :: Template -> Template -> M Template
maybePushActuals cur tis =
case cur of
TVar _ : _ ->
do mergeActuals tis
return cur
_ ->
do vname <- pushActuals tis
return (TVar vname : cur)
-- |collect takes an element, a list of open templates, a list of finished
-- templates, and returns a pair (open templates, finished templates).
collect :: ELEMENT_ -> BT -> Template -> M Template
collect (EMPTY_ bt tag atts) cbt cur =
let t1 = TOut ('<' : tag)
t2 = tout "/>"
in
case bt of
TOPLEVEL ->
do pushOpen cur
ts <- collectAttrs atts STATIC [t1]
fname <- pushClosed (t2 ts)
parms <- get actuals
cur' <- popOpen
maybePushActuals cur' [TCall fname parms]
DYNAMIC ->
case cbt of
STATIC ->
do ts <- collectAttrs atts DYNAMIC [t1]
maybePushActuals cur (t2 ts)
DYNAMIC ->
do ts <- collectAttrs atts DYNAMIC (t1 : cur)
return (t2 ts)
STATIC ->
case cbt of
STATIC ->
do ts <- collectAttrs atts STATIC (t1 : cur)
return (t2 ts)
DYNAMIC ->
do pushOpen cur
ts <- collectAttrs atts STATIC [t1]
fname <- pushClosed (t2 ts)
parms <- get actuals
cur' <- popOpen
maybePushActuals cur' [TCall fname parms]
collect (ELEMENT_ bt tag atts elems) cbt cur =
let t1 = TOut ('<' : tag)
t1' = tout ('<' : tag)
t2 = tout ">"
t3 = tout ("" ++ tag ++ ">")
in
case bt of
TOPLEVEL ->
do pushOpen cur
ts1 <- collectAttrs atts STATIC [t1]
ts2 <- collectElems elems STATIC (t2 ts1)
fname <- pushClosed (t3 ts2)
parms <- get actuals
cur' <- popOpen
maybePushActuals cur' [TCall fname parms]
DYNAMIC ->
case cbt of
STATIC ->
do ts1 <- collectAttrs atts DYNAMIC [t1]
ts2 <- collectElems elems DYNAMIC (t2 ts1)
maybePushActuals cur (t3 ts2)
DYNAMIC ->
do ts1 <- collectAttrs atts DYNAMIC (t1' cur)
ts2 <- collectElems elems DYNAMIC (t2 ts1)
return (t3 ts2)
STATIC ->
case cbt of
STATIC ->
do ts1 <- collectAttrs atts STATIC (t1' cur)
ts2 <- collectElems elems STATIC (t2 ts1)
return (t3 ts2)
DYNAMIC ->
do pushOpen cur
ts1 <- collectAttrs atts STATIC [t1]
ts2 <- collectElems elems STATIC (t2 ts1)
fname <- pushClosed (t3 ts2)
parms <- get actuals
cur' <- popOpen
maybePushActuals cur' [TCall fname parms]
collect (DOCTYPE_ bt strs elems) cbt cur =
let t1 = TOut (" showChar ' ' . showString str . f) id strs .
showString ">")
"")
in
case bt of
TOPLEVEL ->
do pushOpen cur
ts2 <- collectElems elems STATIC [t1]
fname <- pushClosed ts2
parms <- get actuals
cur' <- popOpen
maybePushActuals cur' [TCall fname parms]
DYNAMIC ->
case cbt of
STATIC ->
do ts2 <- collectElems elems DYNAMIC [t1]
maybePushActuals cur ts2
DYNAMIC ->
do collectElems elems DYNAMIC (t1 : cur)
STATIC ->
case cbt of
STATIC ->
do collectElems elems STATIC (t1 : cur)
DYNAMIC ->
do pushOpen cur
ts2 <- collectElems elems STATIC [t1]
fname <- pushClosed ts2
parms <- get actuals
cur' <- popOpen
maybePushActuals cur' [TCall fname parms]
collect (CDATA_ bt str) cbt cur =
let t0 = TOut str
t0' = tout str in
case bt of
TOPLEVEL ->
do pushOpen cur
fname <- pushClosed [t0]
parms <- get actuals
cur' <- popOpen
maybePushActuals cur' [TCall fname parms]
DYNAMIC ->
case cbt of
STATIC ->
do maybePushActuals cur [t0]
DYNAMIC ->
do return [t0]
STATIC ->
case cbt of
STATIC ->
do return (t0' cur)
DYNAMIC ->
do pushOpen cur
fname <- pushClosed [t0]
parms <- get actuals
cur' <- popOpen
maybePushActuals cur' [TCall fname parms]
collect (COMMENT_ bt str) cbt cur =
let t0 = TOut str
t0' = tout str in
case bt of
TOPLEVEL ->
do pushOpen cur
fname <- pushClosed [t0]
parms <- get actuals
cur' <- popOpen
maybePushActuals cur' [TCall fname parms]
DYNAMIC ->
case cbt of
STATIC ->
do maybePushActuals cur [t0]
DYNAMIC ->
do return [t0]
STATIC ->
case cbt of
STATIC ->
do return (t0' cur)
DYNAMIC ->
do pushOpen cur
fname <- pushClosed [t0]
parms <- get actuals
cur' <- popOpen
maybePushActuals cur' [TCall fname parms]
collectElems [] cbt cur =
return cur
collectElems (at:ats) cbt cur =
do ts <- collectElems ats cbt cur
collect at cbt ts
collectAttrs [] cbt cur =
return cur
collectAttrs (at:ats) cbt cur =
do ts <- collectAttr at cbt cur
collectAttrs ats cbt ts
collectAttr
ATTR_ { attr_BT = bt
, attr_value_BT = vbt
, attr_name = aname
, attr_value = aval
}
cbt
cur = let { t1 = TOut (' ' : aname ++ "=\"") ; t2 = TOut "\"";
t1' = tout (' ' : aname ++ "=\"") ; t2' = tout "\""; } in
case bt of
TOPLEVEL ->
do pushOpen cur
ts <- collectAttrValue aval vbt STATIC [t1]
fname <- pushClosed (t2' ts)
parms <- get actuals
cur' <- popOpen
maybePushActuals cur' [TCall fname parms]
DYNAMIC ->
case cbt of
STATIC ->
do ts <- collectAttrValue aval vbt DYNAMIC [t1]
maybePushActuals cur (t2' ts)
DYNAMIC ->
do ts <- collectAttrValue aval vbt DYNAMIC (t1' cur)
return (t2' ts)
STATIC ->
case cbt of
STATIC ->
do ts <- collectAttrValue aval vbt STATIC (t1' cur)
return (t2' ts)
DYNAMIC ->
do pushOpen cur
ts <- collectAttrValue aval vbt STATIC [t1]
fname <- pushClosed (t2' ts)
parms <- get actuals
cur' <- popOpen
maybePushActuals cur' [TCall fname parms]
collectAttrValue aval bt cbt cur =
let encVal = (htmlAttr aval "")
t0 = TOut encVal
t0' = tout encVal in
case bt of
TOPLEVEL ->
do pushOpen cur
fname <- pushClosed [t0]
parms <- get actuals
cur' <- popOpen
maybePushActuals cur' [TCall fname parms]
DYNAMIC ->
case cbt of
STATIC ->
do maybePushActuals cur [t0]
DYNAMIC ->
do return [t0]
STATIC ->
case cbt of
STATIC ->
do return (t0' cur)
DYNAMIC ->
do pushOpen cur
fname <- pushClosed [t0]
parms <- get actuals
cur' <- popOpen
maybePushActuals cur' [TCall fname parms]
-- Local Variables:
-- haskell-prog-switches: ("/home/thiemann/src/haskell/Utility/sha1lib.o")
-- End: