on master help sign in
Working trimming but module loop and formatting req.
commitca7b78ee428a43bb307ecefb65529d5106751192 authorsamgd <sam@samgd.com> dateSun 24 Jul 2016, 15:10 (10 years ago) parenta04f722
5 files changed+261−133
hakyll.cabal+1−1 src/Hakyll/Web/Template/Canonicalize.hs−86 src/Hakyll/Web/Template/Internal.hs+74−38 src/Hakyll/Web/Template/Trim.hs+178 tests/Hakyll/Web/Template/Tests.hs+8−8
hakyll.cabal+1−1
@@ -117,7 +117,7 @@ Library
117117 Hakyll.Web.Paginate
118118 Hakyll.Web.Template
119119 Hakyll.Web.Template.Internal
120− Hakyll.Web.Template.Canonicalize
120+ Hakyll.Web.Template.Trim
121121 Hakyll.Web.Template.Context
122122 Hakyll.Web.Template.List
123123
src/Hakyll/Web/Template/Canonicalize.hsdeleted−86
@@ -1,86 +0,0 @@
1−--------------------------------------------------------------------------------
2−-- | TODO
3−module Hakyll.Web.Template.Canonicalize
4− ( canonicalize
5− ) where
6−
7−
8−--------------------------------------------------------------------------------
9−import Hakyll.Web.Template.Internal
10−
11−
12−--------------------------------------------------------------------------------
13−--
14−-- Some initial implementation notes. Note: Not valid syntax etc.
15−--
16−--
17−-- Top level ONLY:
18−-- [TrimL, t, TrimR] = [t]
19−--
20−-- Dedupe:
21−--
22−-- List:
23−--
24−-- [t1, TrimR, TrimR, t2] = [t1, TrimR, t2]
25−--
26−-- [t1, TrimL, TrimL, t2] = [t1, TrimL, t2]
27−--
28−-- If:
29−--
30−-- [t1, TrimR, If ex [TrimR, t] e, t2] = [t1, If ex [TrimR, t] e, t2]
31−--
32−-- [t1, If ex t [e, TrimL], TrimL, t2] = [t1, If ex t [e, TrimL], t2]
33−--
34−-- [t1, If ex [t, TrimL] Nothing, TrimL, t2] = [t1, If ex [t, TrimL] Nothing, t2]
35−--
36−-- For:
37−--
38−-- [t1, TrimR, For e [TrimR, b] sep, t2] = [t1, For e [TrimR, b] sep, t2]
39−--
40−-- [t1, For e b [sep, TrimL], TrimL, t2] = [t1, For e b [sep, TrimL], t2]
41−--
42−-- [t1, For e [b, TrimL] Nothing, TrimL, t2] = [t1, For e [b, TrimL] Nothing, t2]
43−--
44−--
45−-- Sink:
46−--
47−-- If:
48−--
49−-- [t1, TrimR, If ex t e, t2] = [t1, If ex [TrimR, t] e, t2]
50−--
51−-- [t1, If ex t e, TrimL, t2] = if isJust e
52−-- then [t1, If ex t [e, TrimL], t2]
53−-- else [t1, If ex [t, TrimL] e, t2]
54−--
55−-- For:
56−--
57−-- [t1, TrimR, For e b sep, t2] = [t1, For e [TrimR, b] sep, t2]
58−--
59−-- [t1, For e b sep, TrimL, t2] = if isJust sep
60−-- then [t1, For e b [sep, TrimL], t2]
61−-- else [t1, For e [b, TrimL] sep, t2]
62−--
63−--
64−-- Shift/Lift:
65−--
66−-- If:
67−--
68−-- If ex [t1, TrimR] (Just e) = If ex t1 [TrimR, e]
69−--
70−-- If ex [t1, TrimR] Nothing = [If ex t1 Nothing, TrimR]
71−--
72−-- If ex t [TrimL, e] = If ex [t, TrimL] e
73−--
74−--
75−-- For:
76−--
77−-- For e [t1, TrimR] (Just sep) = For e t1 [TrimR, sep]
78−--
79−-- For e [t1, TrimR] Nothing = For e t1 [TrimR, sep]
80−--
81−-- For e b [TrimL, sep] = For e [b, TrimL] sep
82−--
83−--
84−--
85−canonicalize :: Template -> Template
86−canonicalize = undefined
src/Hakyll/Web/Template/Internal.hs+74−38
@@ -129,6 +129,20 @@ instance Binary TemplateExpr where
129129 _ -> error "Hakyll.Web.Template.Internal: Error reading cached template"
130130
131131
132+--------------------------------------------------------------------------------
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+
132146--------------------------------------------------------------------------------
133147readTemplate :: String -> Template
134148readTemplate input = case P.parse template "" input of
@@ -159,10 +173,7 @@ expr = P.try $ do
159173 trimLExpr <- trimOpen
160174 e <- expr'
161175 trimRExpr <- trimClose
162− return $ Template $ mconcat [ [TrimL | trimLExpr]
163− , [Expr e]
164− , [TrimR | trimRExpr]
165− ]
176+ return $ [TrimL | trimLExpr] .~ Template [Expr e] ~. [TrimR | trimRExpr]
166177
167178
168179--------------------------------------------------------------------------------
@@ -194,73 +205,87 @@ trimClose = do
194205--------------------------------------------------------------------------------
195206conditional :: P.Parser Template
196207conditional = P.try $ do
208+ -- if
197209 trimLIf <- trimOpen
198210 void $ P.string "if("
199211 e <- expr'
200212 void $ P.char ')'
201213 trimRIf <- trimClose
202−
214+ -- then
203215 thenBranch <- template
204−
205− elseBranch <- P.optionMaybe $ P.try $ do
206− trimLElse <- trimOpen
207− void $ P.string "else"
208− trimRElse <- trimClose
209− elseBody <- template
210− pure $ mconcat $ concat [ [Template [TrimL] | trimLElse]
211− , [Template [TrimR] | trimRElse]
212− , [elseBody]
213− ]
214−
216+ -- else
217+ elseParse <- opt "else"
218+ -- endif
215219 trimLEnd <- trimOpen
216220 void $ P.string "endif"
217221 trimREnd <- trimClose
218222
219− pure $ Template $ mconcat [ [TrimL | trimLIf]
220− , [TrimR | trimRIf]
221− , [If e thenBranch elseBranch]
222− , [TrimL | trimLEnd]
223− , [TrimR | trimREnd]
224− ]
223+ -- As else is optional we need to sort out where any Trim_s need to go.
224+ let (thenBody, elseBody) = maybe (thenNoElse, Nothing) thenElse elseParse
225+ where thenNoElse =
226+ [TrimR | trimRIf] .~ thenBranch ~. [TrimL | trimLEnd]
227+
228+ thenElse (trimLElse, elseBranch, trimRElse) = (thenB, elseB)
229+ where thenB = [TrimR | trimRIf]
230+ .~ thenBranch
231+ ~. [TrimL | trimLElse]
232+
233+ elseB = Just $ [TrimR | trimRElse]
234+ .~ elseBranch
235+ ~. [TrimL | trimLEnd]
236+
237+ pure $ [TrimL | trimLIf]
238+ .~ Template [If e thenBody elseBody]
239+ ~. [TrimR | trimREnd]
225240
226241
227242--------------------------------------------------------------------------------
228243for :: P.Parser Template
229244for = P.try $ do
245+ -- for
230246 trimLFor <- trimOpen
231247 void $ P.string "for("
232248 e <- expr'
233249 void $ P.char ')'
234250 trimRFor <- trimClose
235−
236− body <- template
237− sep <- P.optionMaybe $ P.try (P.string "$sep$") >> template
238−
251+ -- body
252+ bodyBranch <- template
253+ -- sep
254+ sepParse <- opt "sep"
255+ -- endfor
239256 trimLEnd <- trimOpen
240257 void $ P.string "endfor"
241258 trimREnd <- trimClose
242259
243− pure $ Template $ mconcat [ [TrimL | trimLFor]
244− , [TrimR | trimRFor]
245− , [For e body sep]
246− , [TrimL | trimLEnd]
247− , [TrimR | trimREnd]
248− ]
260+ -- As sep is optional we need to sort out where any Trim_s need to go.
261+ let (forBody, sepBody) = maybe (forNoSep, Nothing) forSep sepParse
262+ where forNoSep =
263+ [TrimR | trimRFor] .~ bodyBranch ~. [TrimL | trimLEnd]
264+
265+ forSep (trimLSep, sepBranch, trimRSep) = (forB, sepB)
266+ where forB = [TrimR | trimRFor]
267+ .~ bodyBranch
268+ ~. [TrimL | trimLSep]
269+
270+ sepB = Just $ [TrimR | trimRSep]
271+ .~ sepBranch
272+ ~. [TrimL | trimLEnd]
273+
274+ pure $ [TrimL | trimLFor]
275+ .~ Template [For e forBody sepBody]
276+ ~. [TrimR | trimREnd]
249277
250278
251279--------------------------------------------------------------------------------
252280partial :: P.Parser Template
253281partial = P.try $ do
254− trimLPartial <- trimOpen
282+ trimLPart <- trimOpen
255283 void $ P.string "partial("
256284 e <- expr'
257285 void $ P.char ')'
258− trimRPartial <- trimClose
286+ trimRPart <- trimClose
259287
260− pure $ Template $ mconcat [ [TrimL | trimLPartial]
261− , [Partial e]
262− , [TrimR | trimRPartial]
263− ]
288+ pure $ [TrimL | trimLPart] .~ Template [Partial e] ~. [TrimR | trimRPart]
264289
265290
266291--------------------------------------------------------------------------------
@@ -294,3 +319,14 @@ stringLiteral = do
294319--------------------------------------------------------------------------------
295320key :: P.Parser TemplateKey
296321key = TemplateKey <$> metadataKey
322+
323+
324+--------------------------------------------------------------------------------
325+opt :: String -> P.Parser (Maybe (Bool, Template, Bool))
326+opt clause = P.optionMaybe $ P.try $ do
327+ trimL <- trimOpen
328+ void $ P.string clause
329+ trimR <- trimClose
330+ branch <- template
331+ pure (trimL, branch, trimR)
332+
src/Hakyll/Web/Template/Trim.hsnew file+178
@@ -0,0 +1,178 @@
1+--------------------------------------------------------------------------------
2+-- | TODO
3+module Hakyll.Web.Template.Internal.Trim
4+ ( trim
5+ ) where
6+
7+
8+--------------------------------------------------------------------------------
9+import Data.Char (isSpace)
10+import Data.List (dropWhileEnd)
11+
12+
13+--------------------------------------------------------------------------------
14+import Hakyll.Web.Template.Internal
15+
16+
17+--------------------------------------------------------------------------------
18+trim :: Template -> Template
19+trim = cleanse . canonicalize
20+
21+
22+--------------------------------------------------------------------------------
23+cleanse :: Template -> Template
24+cleanse = tmap (recurse cleanse . process)
25+ where process [] = []
26+ process (TrimR:Chunk str:ts) = Chunk (lstrip str):process ts
27+ process (Chunk str:TrimL:ts) = Chunk (rstrip str):process ts
28+ process (t:ts) = t:process ts
29+
30+ lstrip = dropWhile isSpace
31+ rstrip = dropWhileEnd isSpace
32+
33+--------------------------------------------------------------------------------
34+--
35+-- Invariant: Every TrimL should have a Chunk to its Left
36+-- Every TrimR should have a Chunk to its Right
37+--
38+--
39+-- Some initial implementation notes. Note: Not valid syntax etc.
40+--
41+--
42+--
43+--
44+--------------------------------------------------------------------------------
45+canonicalize :: Template -> Template
46+canonicalize = go
47+ where go t = let t' = redundant . swap . dedupe $ sink t
48+ in if t == t' then t else go t'
49+
50+
51+--------------------------------------------------------------------------------
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)
59+ where -- Remove the leading 'TrimL's.
60+ process (TrimL:ts) = process ts
61+ -- Remove trailing 'TrimR's.
62+ process ts = foldr trailing [] ts
63+ where trailing TrimR [] = []
64+ trailing t ts = t:ts
65+
66+
67+--------------------------------------------------------------------------------
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)
78+ where process [] = []
79+ process (TrimR:TrimL:ts) = TrimL:process (TrimR:ts)
80+ process (t:ts) = t:process ts
81+
82+
83+--------------------------------------------------------------------------------
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)
111+ where process [] = []
112+ process (TrimR:TrimR:ts) = process (TrimR:ts)
113+ process (TrimL:TrimL:ts) = process (TrimL:ts)
114+ process (t:ts) = t:process ts
115+
116+
117+--------------------------------------------------------------------------------
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 [] = []
171+recurse f (x:xs) = process x:recurse f xs
172+ where process x = case x of
173+ If e tb eb -> If e (f tb) (f <$> eb)
174+ For e t s -> For e (f t) (f <$> s)
175+ _ -> x
176+
177+
178+--------------------------------------------------------------------------------
tests/Hakyll/Web/Template/Tests.hs+8−8
@@ -41,30 +41,30 @@ tests = testGroup "Hakyll.Core.Template.Tests" $ concat
4141 -- 'If' trim check.
4242 , Template
4343 [ TrimL
44− , TrimR
4544 , If (Ident (TemplateKey "body"))
46− (Template [ Chunk "\n"
45+ (Template [ TrimR
46+ , Chunk "\n"
4747 , Expr (Ident (TemplateKey "body"))
4848 , Chunk "\n"
49+ , TrimL
4950 ])
50− (Just (Template [ TrimL
51− , TrimR
51+ (Just (Template [ TrimR
5252 , Chunk "\n"
5353 , Expr (Ident (TemplateKey "body"))
5454 , Chunk "\n"
55+ , TrimL
5556 ]))
56− , TrimL
5757 , TrimR
5858 ]
5959 @=? readTemplate "$-if(body)-$\n$body$\n$-else-$\n$body$\n$-endif-$"
6060 -- 'For' trim check.
6161 , Template
6262 [ TrimL
63− , TrimR
6463 , For (Ident (TemplateKey "authors"))
65− (Template [Chunk "\n body \n"])
64+ (Template [ TrimR
65+ , Chunk "\n body \n"
66+ , TrimL])
6667 Nothing
67− , TrimL
6868 , TrimR
6969 ]
7070 @=? readTemplate "$-for(authors)-$\n body \n$-endfor-$"