Working trimming but module loop and formatting req.

This commit is contained in:
samgd 2016-07-24 15:10:12 +02:00
parent a04f722eb1
commit ca7b78ee42
No known key found for this signature in database
GPG key ID: E69F2FF86041ADB3
5 changed files with 261 additions and 133 deletions

View file

@ -117,7 +117,7 @@ Library
Hakyll.Web.Paginate Hakyll.Web.Paginate
Hakyll.Web.Template Hakyll.Web.Template
Hakyll.Web.Template.Internal Hakyll.Web.Template.Internal
Hakyll.Web.Template.Canonicalize Hakyll.Web.Template.Trim
Hakyll.Web.Template.Context Hakyll.Web.Template.Context
Hakyll.Web.Template.List Hakyll.Web.Template.List

View file

@ -1,86 +0,0 @@
--------------------------------------------------------------------------------
-- | TODO
module Hakyll.Web.Template.Canonicalize
( canonicalize
) where
--------------------------------------------------------------------------------
import Hakyll.Web.Template.Internal
--------------------------------------------------------------------------------
--
-- Some initial implementation notes. Note: Not valid syntax etc.
--
--
-- Top level ONLY:
-- [TrimL, t, TrimR] = [t]
--
-- Dedupe:
--
-- List:
--
-- [t1, TrimR, TrimR, t2] = [t1, TrimR, t2]
--
-- [t1, TrimL, TrimL, t2] = [t1, TrimL, t2]
--
-- If:
--
-- [t1, TrimR, If ex [TrimR, t] e, t2] = [t1, If ex [TrimR, t] e, t2]
--
-- [t1, If ex t [e, TrimL], TrimL, t2] = [t1, If ex t [e, TrimL], t2]
--
-- [t1, If ex [t, TrimL] Nothing, TrimL, t2] = [t1, If ex [t, TrimL] Nothing, t2]
--
-- For:
--
-- [t1, TrimR, For e [TrimR, b] sep, t2] = [t1, For e [TrimR, b] sep, t2]
--
-- [t1, For e b [sep, TrimL], TrimL, t2] = [t1, For e b [sep, TrimL], t2]
--
-- [t1, For e [b, TrimL] Nothing, TrimL, t2] = [t1, For e [b, TrimL] Nothing, t2]
--
--
-- Sink:
--
-- If:
--
-- [t1, TrimR, If ex t e, t2] = [t1, If ex [TrimR, t] e, t2]
--
-- [t1, If ex t e, TrimL, t2] = if isJust e
-- then [t1, If ex t [e, TrimL], t2]
-- else [t1, If ex [t, TrimL] e, t2]
--
-- For:
--
-- [t1, TrimR, For e b sep, t2] = [t1, For e [TrimR, b] sep, t2]
--
-- [t1, For e b sep, TrimL, t2] = if isJust sep
-- then [t1, For e b [sep, TrimL], t2]
-- else [t1, For e [b, TrimL] sep, t2]
--
--
-- Shift/Lift:
--
-- If:
--
-- If ex [t1, TrimR] (Just e) = If ex t1 [TrimR, e]
--
-- If ex [t1, TrimR] Nothing = [If ex t1 Nothing, TrimR]
--
-- If ex t [TrimL, e] = If ex [t, TrimL] e
--
--
-- For:
--
-- For e [t1, TrimR] (Just sep) = For e t1 [TrimR, sep]
--
-- For e [t1, TrimR] Nothing = For e t1 [TrimR, sep]
--
-- For e b [TrimL, sep] = For e [b, TrimL] sep
--
--
--
canonicalize :: Template -> Template
canonicalize = undefined

View file

@ -129,6 +129,20 @@ instance Binary TemplateExpr where
_ -> error "Hakyll.Web.Template.Internal: Error reading cached template" _ -> error "Hakyll.Web.Template.Internal: Error reading cached template"
--------------------------------------------------------------------------------
(.~) :: [TemplateElement] -> Template -> Template
ts .~ (Template t) = Template (ts ++ t)
infixr 6 .~
--------------------------------------------------------------------------------
(~.) :: Template -> [TemplateElement] -> Template
(Template t) ~. ts = Template (t ++ ts)
infixl 5 ~.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
readTemplate :: String -> Template readTemplate :: String -> Template
readTemplate input = case P.parse template "" input of readTemplate input = case P.parse template "" input of
@ -159,10 +173,7 @@ expr = P.try $ do
trimLExpr <- trimOpen trimLExpr <- trimOpen
e <- expr' e <- expr'
trimRExpr <- trimClose trimRExpr <- trimClose
return $ Template $ mconcat [ [TrimL | trimLExpr] return $ [TrimL | trimLExpr] .~ Template [Expr e] ~. [TrimR | trimRExpr]
, [Expr e]
, [TrimR | trimRExpr]
]
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -194,73 +205,87 @@ trimClose = do
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
conditional :: P.Parser Template conditional :: P.Parser Template
conditional = P.try $ do conditional = P.try $ do
-- if
trimLIf <- trimOpen trimLIf <- trimOpen
void $ P.string "if(" void $ P.string "if("
e <- expr' e <- expr'
void $ P.char ')' void $ P.char ')'
trimRIf <- trimClose trimRIf <- trimClose
-- then
thenBranch <- template thenBranch <- template
-- else
elseBranch <- P.optionMaybe $ P.try $ do elseParse <- opt "else"
trimLElse <- trimOpen -- endif
void $ P.string "else"
trimRElse <- trimClose
elseBody <- template
pure $ mconcat $ concat [ [Template [TrimL] | trimLElse]
, [Template [TrimR] | trimRElse]
, [elseBody]
]
trimLEnd <- trimOpen trimLEnd <- trimOpen
void $ P.string "endif" void $ P.string "endif"
trimREnd <- trimClose trimREnd <- trimClose
pure $ Template $ mconcat [ [TrimL | trimLIf] -- As else is optional we need to sort out where any Trim_s need to go.
, [TrimR | trimRIf] let (thenBody, elseBody) = maybe (thenNoElse, Nothing) thenElse elseParse
, [If e thenBranch elseBranch] where thenNoElse =
, [TrimL | trimLEnd] [TrimR | trimRIf] .~ thenBranch ~. [TrimL | trimLEnd]
, [TrimR | trimREnd]
] thenElse (trimLElse, elseBranch, trimRElse) = (thenB, elseB)
where thenB = [TrimR | trimRIf]
.~ thenBranch
~. [TrimL | trimLElse]
elseB = Just $ [TrimR | trimRElse]
.~ elseBranch
~. [TrimL | trimLEnd]
pure $ [TrimL | trimLIf]
.~ Template [If e thenBody elseBody]
~. [TrimR | trimREnd]
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
for :: P.Parser Template for :: P.Parser Template
for = P.try $ do for = P.try $ do
-- for
trimLFor <- trimOpen trimLFor <- trimOpen
void $ P.string "for(" void $ P.string "for("
e <- expr' e <- expr'
void $ P.char ')' void $ P.char ')'
trimRFor <- trimClose trimRFor <- trimClose
-- body
body <- template bodyBranch <- template
sep <- P.optionMaybe $ P.try (P.string "$sep$") >> template -- sep
sepParse <- opt "sep"
-- endfor
trimLEnd <- trimOpen trimLEnd <- trimOpen
void $ P.string "endfor" void $ P.string "endfor"
trimREnd <- trimClose trimREnd <- trimClose
pure $ Template $ mconcat [ [TrimL | trimLFor] -- As sep is optional we need to sort out where any Trim_s need to go.
, [TrimR | trimRFor] let (forBody, sepBody) = maybe (forNoSep, Nothing) forSep sepParse
, [For e body sep] where forNoSep =
, [TrimL | trimLEnd] [TrimR | trimRFor] .~ bodyBranch ~. [TrimL | trimLEnd]
, [TrimR | trimREnd]
] forSep (trimLSep, sepBranch, trimRSep) = (forB, sepB)
where forB = [TrimR | trimRFor]
.~ bodyBranch
~. [TrimL | trimLSep]
sepB = Just $ [TrimR | trimRSep]
.~ sepBranch
~. [TrimL | trimLEnd]
pure $ [TrimL | trimLFor]
.~ Template [For e forBody sepBody]
~. [TrimR | trimREnd]
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
partial :: P.Parser Template partial :: P.Parser Template
partial = P.try $ do partial = P.try $ do
trimLPartial <- trimOpen trimLPart <- trimOpen
void $ P.string "partial(" void $ P.string "partial("
e <- expr' e <- expr'
void $ P.char ')' void $ P.char ')'
trimRPartial <- trimClose trimRPart <- trimClose
pure $ Template $ mconcat [ [TrimL | trimLPartial] pure $ [TrimL | trimLPart] .~ Template [Partial e] ~. [TrimR | trimRPart]
, [Partial e]
, [TrimR | trimRPartial]
]
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -294,3 +319,14 @@ stringLiteral = do
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
key :: P.Parser TemplateKey key :: P.Parser TemplateKey
key = TemplateKey <$> metadataKey key = TemplateKey <$> metadataKey
--------------------------------------------------------------------------------
opt :: String -> P.Parser (Maybe (Bool, Template, Bool))
opt clause = P.optionMaybe $ P.try $ do
trimL <- trimOpen
void $ P.string clause
trimR <- trimClose
branch <- template
pure (trimL, branch, trimR)

View file

@ -0,0 +1,178 @@
--------------------------------------------------------------------------------
-- | TODO
module Hakyll.Web.Template.Internal.Trim
( trim
) where
--------------------------------------------------------------------------------
import Data.Char (isSpace)
import Data.List (dropWhileEnd)
--------------------------------------------------------------------------------
import Hakyll.Web.Template.Internal
--------------------------------------------------------------------------------
trim :: Template -> Template
trim = cleanse . canonicalize
--------------------------------------------------------------------------------
cleanse :: Template -> Template
cleanse = tmap (recurse cleanse . process)
where process [] = []
process (TrimR:Chunk str:ts) = Chunk (lstrip str):process ts
process (Chunk str:TrimL:ts) = Chunk (rstrip str):process ts
process (t:ts) = t:process ts
lstrip = dropWhile isSpace
rstrip = dropWhileEnd isSpace
--------------------------------------------------------------------------------
--
-- Invariant: Every TrimL should have a Chunk to its Left
-- Every TrimR should have a Chunk to its Right
--
--
-- Some initial implementation notes. Note: Not valid syntax etc.
--
--
--
--
--------------------------------------------------------------------------------
canonicalize :: Template -> Template
canonicalize = go
where go t = let t' = redundant . swap . dedupe $ sink t
in if t == t' then t else go t'
--------------------------------------------------------------------------------
-- | 'redundant' removes the redundant 'TrimR's and 'TrimL's from the
-- 'Template's list of 'TemplateExpr's. It does _not_ recurse down the AST.
--
-- Note: Should _only_ be used on the top level 'Template'.
--
redundant :: Template -> Template
redundant = tmap (recurse redundant . process)
where -- Remove the leading 'TrimL's.
process (TrimL:ts) = process ts
-- Remove trailing 'TrimR's.
process ts = foldr trailing [] ts
where trailing TrimR [] = []
trailing t ts = t:ts
--------------------------------------------------------------------------------
-- Commute:
--
-- List:
--
-- [t1, TrimR, TrimL, t2] = [t1, TrimL, TrimR, t2]
--
-- Rest should come for free provided Trim's are Sunk/Shifted etc I think.
--
swap :: Template -> Template
swap = tmap (recurse swap . process)
where process [] = []
process (TrimR:TrimL:ts) = TrimL:process (TrimR:ts)
process (t:ts) = t:process ts
--------------------------------------------------------------------------------
--
-- Dedupe:
--
-- List:
--
-- [t1, TrimR, TrimR, t2] = [t1, TrimR, t2]
--
-- [t1, TrimL, TrimL, t2] = [t1, TrimL, t2]
--
-- If: Should come for free after Trim_'s have been sunk.
--
-- [t1, TrimR, If ex [TrimR, t] e, t2] = [t1, If ex [TrimR, t] e, t2]
--
-- [t1, If ex t [e, TrimL], TrimL, t2] = [t1, If ex t [e, TrimL], t2]
--
-- [t1, If ex [t, TrimL] Nothing, TrimL, t2] = [t1, If ex [t, TrimL] Nothing, t2]
--
-- For:
--
-- [t1, TrimR, For e [TrimR, b] sep, t2] = [t1, For e [TrimR, b] sep, t2]
--
-- [t1, For e b [sep, TrimL], TrimL, t2] = [t1, For e b [sep, TrimL], t2]
--
-- [t1, For e [b, TrimL] Nothing, TrimL, t2] = [t1, For e [b, TrimL] Nothing, t2]
--
dedupe :: Template -> Template
dedupe = tmap (recurse dedupe . process)
where process [] = []
process (TrimR:TrimR:ts) = process (TrimR:ts)
process (TrimL:TrimL:ts) = process (TrimL:ts)
process (t:ts) = t:process ts
--------------------------------------------------------------------------------
--
-- Sink:
--
-- If:
--
-- [t1, TrimR, If ex t e, t2] = [t1, If ex [TrimR, t] e, t2]
--
-- [t1, If ex t e, TrimL, t2] = if isJust e
-- then [t1, If ex t [e, TrimL], t2]
-- else [t1, If ex [t, TrimL] e, t2]
--
-- For:
--
-- [t1, TrimR, For e b sep, t2] = [t1, For e [TrimR, b] sep, t2]
--
-- [t1, For e b sep, TrimL, t2] = if isJust sep
-- then [t1, For e b [sep, TrimL], t2]
-- else [t1, For e [b, TrimL] sep, t2]
--
--
sink :: Template -> Template
sink = tmap (recurse sink . process)
where process [] = []
-- Right sink TrimR into If thenbody.
process (TrimR:If e (Template tb) eb:ts)
= If e (Template (TrimR:tb)) eb:process ts
-- Left sink TrimL into If thenbody.
process (If e (Template tb) Nothing:TrimL:ts)
= If e (Template (tb ++ [TrimL])) Nothing:process ts
-- Left sink TrimL into If elsebody.
process (If e tb (Just (Template eb)):TrimL:ts)
= If e tb (Just (Template (eb ++ [TrimL]))):process ts
-- Right sink TrimR into For body.
process (TrimR:For e (Template b) sep:ts)
= For e (Template (TrimR:b)) sep:process ts
-- Left sink TrimL into For body.
process (For e (Template b) Nothing:TrimL:ts)
= For e (Template (b ++ [TrimL])) Nothing:process ts
-- Left sink TrimL into For sep.
process (For e b (Just (Template sep)):TrimL:ts)
= For e b (Just (Template (sep ++ [TrimL]))):process ts
-- Otherwise move on.
process (t:ts) = t:process ts
--------------------------------------------------------------------------------
tmap :: ([TemplateElement] -> [TemplateElement]) -> Template -> Template
tmap f (Template t) = Template (f t)
--------------------------------------------------------------------------------
recurse :: (Template -> Template) -> [TemplateElement] -> [TemplateElement]
recurse f [] = []
recurse f (x:xs) = process x:recurse f xs
where process x = case x of
If e tb eb -> If e (f tb) (f <$> eb)
For e t s -> For e (f t) (f <$> s)
_ -> x
--------------------------------------------------------------------------------

View file

@ -41,30 +41,30 @@ tests = testGroup "Hakyll.Core.Template.Tests" $ concat
-- 'If' trim check. -- 'If' trim check.
, Template , Template
[ TrimL [ TrimL
, TrimR
, If (Ident (TemplateKey "body")) , If (Ident (TemplateKey "body"))
(Template [ Chunk "\n" (Template [ TrimR
, Chunk "\n"
, Expr (Ident (TemplateKey "body")) , Expr (Ident (TemplateKey "body"))
, Chunk "\n" , Chunk "\n"
, TrimL
]) ])
(Just (Template [ TrimL (Just (Template [ TrimR
, TrimR
, Chunk "\n" , Chunk "\n"
, Expr (Ident (TemplateKey "body")) , Expr (Ident (TemplateKey "body"))
, Chunk "\n" , Chunk "\n"
, TrimL
])) ]))
, TrimL
, TrimR , TrimR
] ]
@=? readTemplate "$-if(body)-$\n$body$\n$-else-$\n$body$\n$-endif-$" @=? readTemplate "$-if(body)-$\n$body$\n$-else-$\n$body$\n$-endif-$"
-- 'For' trim check. -- 'For' trim check.
, Template , Template
[ TrimL [ TrimL
, TrimR
, For (Ident (TemplateKey "authors")) , For (Ident (TemplateKey "authors"))
(Template [Chunk "\n body \n"]) (Template [ TrimR
, Chunk "\n body \n"
, TrimL])
Nothing Nothing
, TrimL
, TrimR , TrimR
] ]
@=? readTemplate "$-for(authors)-$\n body \n$-endfor-$" @=? readTemplate "$-for(authors)-$\n body \n$-endfor-$"