on master help sign in
Fix module layout
commit43c969f326082d29d8e340ee865414deb87b8ac5 authorsamgd <sam@samgd.com> dateSun 24 Jul 2016, 17:30 (10 years ago) parentca7b78e
4 files changed+139−252
src/Hakyll/Web/Template.hs+37−9 src/Hakyll/Web/Template/Internal.hs+36−77 src/Hakyll/Web/Template/Trim.hs+27−118 tests/Hakyll/Web/Template/Tests.hs+39−48
src/Hakyll/Web/Template.hs+37−9
@@ -115,7 +115,8 @@
115115-- That is, calling @$partial$@ is equivalent to just copying and pasting
116116-- template code.
117117--
118−{-# LANGUAGE ScopedTypeVariables #-}
118+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
119+{-# LANGUAGE ScopedTypeVariables #-}
119120module Hakyll.Web.Template
120121 ( Template
121122 , templateBodyCompiler
@@ -128,9 +129,11 @@ module Hakyll.Web.Template
128129
129130
130131--------------------------------------------------------------------------------
131−import Control.Monad (liftM)
132132import Control.Monad.Except (MonadError (..))
133+import Data.Binary (Binary)
133134import Data.List (intercalate)
135+import Data.Typeable (Typeable)
136+import GHC.Exts (IsString (..))
134137import Prelude hiding (id)
135138
136139
@@ -138,8 +141,33 @@ import Prelude hiding (id)
138141import Hakyll.Core.Compiler
139142import Hakyll.Core.Identifier
140143import Hakyll.Core.Item
144+import Hakyll.Core.Writable
141145import Hakyll.Web.Template.Context
142146import Hakyll.Web.Template.Internal
147+import Hakyll.Web.Template.Trim
148+
149+
150+--------------------------------------------------------------------------------
151+-- | Datatype used for template substitutions.
152+newtype Template = Template
153+ { unTemplate :: [TemplateElement]
154+ } deriving (Show, Eq, Binary, Typeable)
155+
156+
157+--------------------------------------------------------------------------------
158+instance Writable Template where
159+ -- Writing a template is impossible
160+ write _ _ = return ()
161+
162+
163+--------------------------------------------------------------------------------
164+instance IsString Template where
165+ fromString = readTemplate
166+
167+
168+--------------------------------------------------------------------------------
169+readTemplate :: String -> Template
170+readTemplate = Template . trim . readTemplateElems
143171
144172
145173--------------------------------------------------------------------------------
@@ -163,23 +191,23 @@ applyTemplate :: Template -- ^ Template
163191 -> Item a -- ^ Page
164192 -> Compiler (Item String) -- ^ Resulting item
165193applyTemplate tpl context item = do
166− body <- applyTemplate' tpl context item
194+ body <- applyTemplate' (unTemplate tpl) context item
167195 return $ itemSetBody body item
168196
169197
170198--------------------------------------------------------------------------------
171199applyTemplate'
172200 :: forall a.
173− Template -- ^ Template
174− -> Context a -- ^ Context
175− -> Item a -- ^ Page
176− -> Compiler String -- ^ Resulting item
177−applyTemplate' tpl context x = go tpl
201+ [TemplateElement] -- ^ Unwrapped Template
202+ -> Context a -- ^ Context
203+ -> Item a -- ^ Page
204+ -> Compiler String -- ^ Resulting item
205+applyTemplate' tes context x = go tes
178206 where
179207 context' :: String -> [String] -> Item a -> Compiler ContextField
180208 context' = unContext (context `mappend` missingField)
181209
182− go = liftM concat . mapM applyElem . unTemplate
210+ go = fmap concat . mapM applyElem
183211
184212 ---------------------------------------------------------------------------
185213
src/Hakyll/Web/Template/Internal.hs+36−77
@@ -1,13 +1,12 @@
11--------------------------------------------------------------------------------
22-- | Module containing the template data structure
3−{-# LANGUAGE DeriveDataTypeable #-}
43{-# LANGUAGE GeneralizedNewtypeDeriving #-}
54module Hakyll.Web.Template.Internal
6− ( Template (..)
7− , TemplateKey (..)
5+ ( TemplateKey (..)
86 , TemplateExpr (..)
97 , TemplateElement (..)
10− , readTemplate
8+ , templateElems
9+ , readTemplateElems
1110 ) where
1211
1312
@@ -25,31 +24,6 @@ import qualified Text.Parsec.String as P
2524
2625--------------------------------------------------------------------------------
2726import Hakyll.Core.Util.Parser
28−import Hakyll.Core.Writable
29−
30−
31−--------------------------------------------------------------------------------
32−-- | Datatype used for template substitutions.
33−newtype Template = Template
34− { unTemplate :: [TemplateElement]
35− } deriving (Show, Eq, Binary, Typeable)
36−
37−
38−--------------------------------------------------------------------------------
39−instance Monoid Template where
40− mempty = Template []
41− (Template xs) `mappend` (Template ys) = Template (xs `mappend` ys)
42−
43−
44−--------------------------------------------------------------------------------
45−instance Writable Template where
46− -- Writing a template is impossible
47− write _ _ = return ()
48−
49−
50−--------------------------------------------------------------------------------
51−instance IsString Template where
52− fromString = readTemplate
5327
5428
5529--------------------------------------------------------------------------------
@@ -68,9 +42,12 @@ data TemplateElement
6842 = Chunk String
6943 | Expr TemplateExpr
7044 | Escaped
71− | If TemplateExpr Template (Maybe Template) -- expr, then, else
72− | For TemplateExpr Template (Maybe Template) -- expr, body, separator
73− | Partial TemplateExpr -- filename
45+ -- expr, then, else
46+ | If TemplateExpr [TemplateElement] (Maybe [TemplateElement])
47+ -- expr, body, separator
48+ | For TemplateExpr [TemplateElement] (Maybe [TemplateElement])
49+ -- filename
50+ | Partial TemplateExpr
7451 | TrimL
7552 | TrimR
7653 deriving (Show, Eq, Typeable)
@@ -130,36 +107,22 @@ instance Binary TemplateExpr where
130107
131108
132109--------------------------------------------------------------------------------
133−(.~) :: [TemplateElement] -> Template -> Template
134−ts .~ (Template t) = Template (ts ++ t)
135−
136−infixr 6 .~
137−
138−
139−--------------------------------------------------------------------------------
140−(~.) :: Template -> [TemplateElement] -> Template
141−(Template t) ~. ts = Template (t ++ ts)
142−
143−infixl 5 ~.
144−
145−
146−--------------------------------------------------------------------------------
147−readTemplate :: String -> Template
148−readTemplate input = case P.parse template "" input of
110+readTemplateElems :: String -> [TemplateElement]
111+readTemplateElems input = case P.parse templateElems "" input of
149112 Left err -> error $ "Cannot parse template: " ++ show err
150113 Right t -> t
151114
152115
153116--------------------------------------------------------------------------------
154−template :: P.Parser Template
155−template = mconcat <$> P.many (P.choice [ lift chunk
117+templateElems :: P.Parser [TemplateElement]
118+templateElems = mconcat <$> P.many (P.choice [ lift chunk
156119 , lift escaped
157120 , conditional
158121 , for
159122 , partial
160123 , expr
161124 ])
162− where lift = fmap (Template . (:[]))
125+ where lift = fmap (:[])
163126
164127
165128--------------------------------------------------------------------------------
@@ -168,12 +131,12 @@ chunk = Chunk <$> P.many1 (P.noneOf "$")
168131
169132
170133--------------------------------------------------------------------------------
171−expr :: P.Parser Template
134+expr :: P.Parser [TemplateElement]
172135expr = P.try $ do
173136 trimLExpr <- trimOpen
174137 e <- expr'
175138 trimRExpr <- trimClose
176− return $ [TrimL | trimLExpr] .~ Template [Expr e] ~. [TrimR | trimRExpr]
139+ return $ [TrimL | trimLExpr] ++ [Expr e] ++ [TrimR | trimRExpr]
177140
178141
179142--------------------------------------------------------------------------------
@@ -203,7 +166,7 @@ trimClose = do
203166
204167
205168--------------------------------------------------------------------------------
206−conditional :: P.Parser Template
169+conditional :: P.Parser [TemplateElement]
207170conditional = P.try $ do
208171 -- if
209172 trimLIf <- trimOpen
@@ -212,7 +175,7 @@ conditional = P.try $ do
212175 void $ P.char ')'
213176 trimRIf <- trimClose
214177 -- then
215− thenBranch <- template
178+ thenBranch <- templateElems
216179 -- else
217180 elseParse <- opt "else"
218181 -- endif
@@ -223,24 +186,22 @@ conditional = P.try $ do
223186 -- As else is optional we need to sort out where any Trim_s need to go.
224187 let (thenBody, elseBody) = maybe (thenNoElse, Nothing) thenElse elseParse
225188 where thenNoElse =
226− [TrimR | trimRIf] .~ thenBranch ~. [TrimL | trimLEnd]
189+ [TrimR | trimRIf] ++ thenBranch ++ [TrimL | trimLEnd]
227190
228191 thenElse (trimLElse, elseBranch, trimRElse) = (thenB, elseB)
229192 where thenB = [TrimR | trimRIf]
230− .~ thenBranch
231− ~. [TrimL | trimLElse]
193+ ++ thenBranch
194+ ++ [TrimL | trimLElse]
232195
233196 elseB = Just $ [TrimR | trimRElse]
234− .~ elseBranch
235− ~. [TrimL | trimLEnd]
197+ ++ elseBranch
198+ ++ [TrimL | trimLEnd]
236199
237− pure $ [TrimL | trimLIf]
238− .~ Template [If e thenBody elseBody]
239− ~. [TrimR | trimREnd]
200+ pure $ [TrimL | trimLIf] ++ [If e thenBody elseBody] ++ [TrimR | trimREnd]
240201
241202
242203--------------------------------------------------------------------------------
243−for :: P.Parser Template
204+for :: P.Parser [TemplateElement]
244205for = P.try $ do
245206 -- for
246207 trimLFor <- trimOpen
@@ -249,7 +210,7 @@ for = P.try $ do
249210 void $ P.char ')'
250211 trimRFor <- trimClose
251212 -- body
252− bodyBranch <- template
213+ bodyBranch <- templateElems
253214 -- sep
254215 sepParse <- opt "sep"
255216 -- endfor
@@ -260,24 +221,22 @@ for = P.try $ do
260221 -- As sep is optional we need to sort out where any Trim_s need to go.
261222 let (forBody, sepBody) = maybe (forNoSep, Nothing) forSep sepParse
262223 where forNoSep =
263− [TrimR | trimRFor] .~ bodyBranch ~. [TrimL | trimLEnd]
224+ [TrimR | trimRFor] ++ bodyBranch ++ [TrimL | trimLEnd]
264225
265226 forSep (trimLSep, sepBranch, trimRSep) = (forB, sepB)
266227 where forB = [TrimR | trimRFor]
267− .~ bodyBranch
268− ~. [TrimL | trimLSep]
228+ ++ bodyBranch
229+ ++ [TrimL | trimLSep]
269230
270231 sepB = Just $ [TrimR | trimRSep]
271− .~ sepBranch
272− ~. [TrimL | trimLEnd]
232+ ++ sepBranch
233+ ++ [TrimL | trimLEnd]
273234
274− pure $ [TrimL | trimLFor]
275− .~ Template [For e forBody sepBody]
276− ~. [TrimR | trimREnd]
235+ pure $ [TrimL | trimLFor] ++ [For e forBody sepBody] ++ [TrimR | trimREnd]
277236
278237
279238--------------------------------------------------------------------------------
280−partial :: P.Parser Template
239+partial :: P.Parser [TemplateElement]
281240partial = P.try $ do
282241 trimLPart <- trimOpen
283242 void $ P.string "partial("
@@ -285,7 +244,7 @@ partial = P.try $ do
285244 void $ P.char ')'
286245 trimRPart <- trimClose
287246
288− pure $ [TrimL | trimLPart] .~ Template [Partial e] ~. [TrimR | trimRPart]
247+ pure $ [TrimL | trimLPart] ++ [Partial e] ++ [TrimR | trimRPart]
289248
290249
291250--------------------------------------------------------------------------------
@@ -322,11 +281,11 @@ key = TemplateKey <$> metadataKey
322281
323282
324283--------------------------------------------------------------------------------
325−opt :: String -> P.Parser (Maybe (Bool, Template, Bool))
284+opt :: String -> P.Parser (Maybe (Bool, [TemplateElement], Bool))
326285opt clause = P.optionMaybe $ P.try $ do
327286 trimL <- trimOpen
328287 void $ P.string clause
329288 trimR <- trimClose
330− branch <- template
289+ branch <- templateElems
331290 pure (trimL, branch, trimR)
332291
src/Hakyll/Web/Template/Trim.hs+27−118
@@ -1,6 +1,6 @@
11--------------------------------------------------------------------------------
2−-- | TODO
3−module Hakyll.Web.Template.Internal.Trim
2+-- | Module for trimming whitespace.
3+module Hakyll.Web.Template.Trim
44 ( trim
55 ) where
66
@@ -15,13 +15,13 @@ import Hakyll.Web.Template.Internal
1515
1616
1717--------------------------------------------------------------------------------
18−trim :: Template -> Template
18+trim :: [TemplateElement] -> [TemplateElement]
1919trim = cleanse . canonicalize
2020
2121
2222--------------------------------------------------------------------------------
23−cleanse :: Template -> Template
24−cleanse = tmap (recurse cleanse . process)
23+cleanse :: [TemplateElement] -> [TemplateElement]
24+cleanse = recurse cleanse . process
2525 where process [] = []
2626 process (TrimR:Chunk str:ts) = Chunk (lstrip str):process ts
2727 process (Chunk str:TrimL:ts) = Chunk (rstrip str):process ts
@@ -31,83 +31,43 @@ cleanse = tmap (recurse cleanse . process)
3131 rstrip = dropWhileEnd isSpace
3232
3333--------------------------------------------------------------------------------
34+-- | Enforce the invariant that:
3435--
35−-- Invariant: Every TrimL should have a Chunk to its Left
36−-- Every TrimR should have a Chunk to its Right
36+-- * Every 'TrimL' has a 'Chunk' to its left.
37+-- * Every 'TrimR' has a 'Chunk' to its right.
3738--
38−--
39−-- Some initial implementation notes. Note: Not valid syntax etc.
40−--
41−--
42−--
43−--
44−--------------------------------------------------------------------------------
45−canonicalize :: Template -> Template
39+canonicalize :: [TemplateElement] -> [TemplateElement]
4640canonicalize = go
47− where go t = let t' = redundant . swap . dedupe $ sink t
41+ where go t = let t' = redundant . swap $ dedupe t
4842 in if t == t' then t else go t'
4943
5044
5145--------------------------------------------------------------------------------
52−-- | 'redundant' removes the redundant 'TrimR's and 'TrimL's from the
53−-- 'Template's list of 'TemplateExpr's. It does _not_ recurse down the AST.
54−--
55−-- Note: Should _only_ be used on the top level 'Template'.
56−--
57−redundant :: Template -> Template
58−redundant = tmap (recurse redundant . process)
46+-- | Remove the 'TrimR' and 'TrimL's that are no-ops.
47+redundant :: [TemplateElement] -> [TemplateElement]
48+redundant = recurse redundant . process
5949 where -- Remove the leading 'TrimL's.
6050 process (TrimL:ts) = process ts
6151 -- Remove trailing 'TrimR's.
6252 process ts = foldr trailing [] ts
6353 where trailing TrimR [] = []
64− trailing t ts = t:ts
54+ trailing x xs = x:xs
6555
6656
6757--------------------------------------------------------------------------------
68−-- Commute:
69−--
70−-- List:
71−--
72−-- [t1, TrimR, TrimL, t2] = [t1, TrimL, TrimR, t2]
73−--
74−-- Rest should come for free provided Trim's are Sunk/Shifted etc I think.
75−--
76−swap :: Template -> Template
77−swap = tmap (recurse swap . process)
58+-- >>> swap $ [TrimR, TrimL]
59+-- [TrimL, TrimR]
60+swap :: [TemplateElement] -> [TemplateElement]
61+swap = recurse swap . process
7862 where process [] = []
7963 process (TrimR:TrimL:ts) = TrimL:process (TrimR:ts)
8064 process (t:ts) = t:process ts
8165
8266
8367--------------------------------------------------------------------------------
84−--
85−-- Dedupe:
86−--
87−-- List:
88−--
89−-- [t1, TrimR, TrimR, t2] = [t1, TrimR, t2]
90−--
91−-- [t1, TrimL, TrimL, t2] = [t1, TrimL, t2]
92−--
93−-- If: Should come for free after Trim_'s have been sunk.
94−--
95−-- [t1, TrimR, If ex [TrimR, t] e, t2] = [t1, If ex [TrimR, t] e, t2]
96−--
97−-- [t1, If ex t [e, TrimL], TrimL, t2] = [t1, If ex t [e, TrimL], t2]
98−--
99−-- [t1, If ex [t, TrimL] Nothing, TrimL, t2] = [t1, If ex [t, TrimL] Nothing, t2]
100−--
101−-- For:
102−--
103−-- [t1, TrimR, For e [TrimR, b] sep, t2] = [t1, For e [TrimR, b] sep, t2]
104−--
105−-- [t1, For e b [sep, TrimL], TrimL, t2] = [t1, For e b [sep, TrimL], t2]
106−--
107−-- [t1, For e [b, TrimL] Nothing, TrimL, t2] = [t1, For e [b, TrimL] Nothing, t2]
108−--
109−dedupe :: Template -> Template
110−dedupe = tmap (recurse dedupe . process)
68+-- | Remove 'TrimR' and 'TrimL' duplication.
69+dedupe :: [TemplateElement] -> [TemplateElement]
70+dedupe = recurse dedupe . process
11171 where process [] = []
11272 process (TrimR:TrimR:ts) = process (TrimR:ts)
11373 process (TrimL:TrimL:ts) = process (TrimL:ts)
@@ -115,64 +75,13 @@ dedupe = tmap (recurse dedupe . process)
11575
11676
11777--------------------------------------------------------------------------------
118−--
119−-- Sink:
120−--
121−-- If:
122−--
123−-- [t1, TrimR, If ex t e, t2] = [t1, If ex [TrimR, t] e, t2]
124−--
125−-- [t1, If ex t e, TrimL, t2] = if isJust e
126−-- then [t1, If ex t [e, TrimL], t2]
127−-- else [t1, If ex [t, TrimL] e, t2]
128−--
129−-- For:
130−--
131−-- [t1, TrimR, For e b sep, t2] = [t1, For e [TrimR, b] sep, t2]
132−--
133−-- [t1, For e b sep, TrimL, t2] = if isJust sep
134−-- then [t1, For e b [sep, TrimL], t2]
135−-- else [t1, For e [b, TrimL] sep, t2]
136−--
137−--
138−sink :: Template -> Template
139−sink = tmap (recurse sink . process)
140− where process [] = []
141− -- Right sink TrimR into If thenbody.
142− process (TrimR:If e (Template tb) eb:ts)
143− = If e (Template (TrimR:tb)) eb:process ts
144− -- Left sink TrimL into If thenbody.
145− process (If e (Template tb) Nothing:TrimL:ts)
146− = If e (Template (tb ++ [TrimL])) Nothing:process ts
147− -- Left sink TrimL into If elsebody.
148− process (If e tb (Just (Template eb)):TrimL:ts)
149− = If e tb (Just (Template (eb ++ [TrimL]))):process ts
150− -- Right sink TrimR into For body.
151− process (TrimR:For e (Template b) sep:ts)
152− = For e (Template (TrimR:b)) sep:process ts
153− -- Left sink TrimL into For body.
154− process (For e (Template b) Nothing:TrimL:ts)
155− = For e (Template (b ++ [TrimL])) Nothing:process ts
156− -- Left sink TrimL into For sep.
157− process (For e b (Just (Template sep)):TrimL:ts)
158− = For e b (Just (Template (sep ++ [TrimL]))):process ts
159− -- Otherwise move on.
160− process (t:ts) = t:process ts
161−
162−
163−--------------------------------------------------------------------------------
164−tmap :: ([TemplateElement] -> [TemplateElement]) -> Template -> Template
165−tmap f (Template t) = Template (f t)
166−
167−
168−--------------------------------------------------------------------------------
169−recurse :: (Template -> Template) -> [TemplateElement] -> [TemplateElement]
170−recurse f [] = []
78+recurse :: ([TemplateElement] -> [TemplateElement])
79+ -> [TemplateElement]
80+ -> [TemplateElement]
81+recurse _ [] = []
17182recurse f (x:xs) = process x:recurse f xs
172− where process x = case x of
83+ where process y = case y of
17384 If e tb eb -> If e (f tb) (f <$> eb)
17485 For e t s -> For e (f t) (f <$> s)
175− _ -> x
86+ _ -> y
17687
177−
178−--------------------------------------------------------------------------------
tests/Hakyll/Web/Template/Tests.hs+39−48
@@ -31,57 +31,48 @@ tests = testGroup "Hakyll.Core.Template.Tests" $ concat
3131 ]
3232
3333 , fromAssertions "readTemplate"
34− [ Template [Chunk "Hello ", Expr (Call "guest" [])]
35− @=? readTemplate "Hello $guest()$"
36− , Template
37− [If (Call "a" [StringLiteral "bar"])
38− (Template [Chunk "foo"])
39− Nothing]
40− @=? readTemplate "$if(a(\"bar\"))$foo$endif$"
34+ [ [Chunk "Hello ", Expr (Call "guest" [])]
35+ @=? readTemplateElems "Hello $guest()$"
36+ , [If (Call "a" [StringLiteral "bar"]) [Chunk "foo"] Nothing]
37+ @=? readTemplateElems "$if(a(\"bar\"))$foo$endif$"
4138 -- 'If' trim check.
42− , Template
43− [ TrimL
44− , If (Ident (TemplateKey "body"))
45− (Template [ TrimR
46− , Chunk "\n"
47− , Expr (Ident (TemplateKey "body"))
48− , Chunk "\n"
49− , TrimL
50− ])
51− (Just (Template [ TrimR
52− , Chunk "\n"
53− , Expr (Ident (TemplateKey "body"))
54− , Chunk "\n"
55− , TrimL
56− ]))
57− , TrimR
58− ]
59− @=? readTemplate "$-if(body)-$\n$body$\n$-else-$\n$body$\n$-endif-$"
39+ , [ TrimL
40+ , If (Ident (TemplateKey "body"))
41+ [ TrimR
42+ , Chunk "\n"
43+ , Expr (Ident (TemplateKey "body"))
44+ , Chunk "\n"
45+ , TrimL
46+ ]
47+ (Just [ TrimR
48+ , Chunk "\n"
49+ , Expr (Ident (TemplateKey "body"))
50+ , Chunk "\n"
51+ , TrimL
52+ ])
53+ , TrimR
54+ ]
55+ @=? readTemplateElems "$-if(body)-$\n$body$\n$-else-$\n$body$\n$-endif-$"
6056 -- 'For' trim check.
61− , Template
62− [ TrimL
63− , For (Ident (TemplateKey "authors"))
64− (Template [ TrimR
65− , Chunk "\n body \n"
66− , TrimL])
67− Nothing
68− , TrimR
69− ]
70− @=? readTemplate "$-for(authors)-$\n body \n$-endfor-$"
57+ , [ TrimL
58+ , For (Ident (TemplateKey "authors"))
59+ [TrimR, Chunk "\n body \n", TrimL]
60+ Nothing
61+ , TrimR
62+ ]
63+ @=? readTemplateElems "$-for(authors)-$\n body \n$-endfor-$"
7164 -- 'Partial' trim check.
72− , Template
73− [ TrimL
74− , Partial (StringLiteral "path")
75− , TrimR
76− ]
77− @=? readTemplate "$-partial(\"path\")-$"
65+ , [ TrimL
66+ , Partial (StringLiteral "path")
67+ , TrimR
68+ ]
69+ @=? readTemplateElems "$-partial(\"path\")-$"
7870 -- 'Expr' trim check.
79− , Template
80− [ TrimL
81− , Expr (Ident (TemplateKey "foo"))
82− , TrimR
83− ]
84− @=? readTemplate "$-foo-$"
71+ , [ TrimL
72+ , Expr (Ident (TemplateKey "foo"))
73+ , TrimR
74+ ]
75+ @=? readTemplateElems "$-foo-$"
8576 ]
8677 ]
8778
@@ -126,4 +117,4 @@ testApplyJoinTemplateList = do
126117 where
127118 i1 = Item "item1" "Hello"
128119 i2 = Item "item2" "World"
129− tpl = Template [Chunk "<b>", Expr (Ident "body"), Chunk "</b>"]
120+ tpl = readTemplate "<b>$body$</b>"