xml-hamlet (empty) → 0.0.0
raw patch · 6 files changed
+672/−0 lines, 6 filesdep +HUnitdep +basedep +hspecsetup-changed
Dependencies added: HUnit, base, hspec, parsec, template-haskell, text, xml-enumerator
Files
- LICENSE +30/−0
- Setup.lhs +7/−0
- Text/Hamlet/Parse.hs +282/−0
- Text/Hamlet/XML.hs +96/−0
- Text/Shakespeare.hs +225/−0
- xml-hamlet.cabal +32/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c)2011, Michael Snoyman++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Michael Snoyman nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.lhs view
@@ -0,0 +1,7 @@+#!/usr/bin/env runhaskell++> module Main where+> import Distribution.Simple++> main :: IO ()+> main = defaultMain
+ Text/Hamlet/Parse.hs view
@@ -0,0 +1,282 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE CPP #-}+module Text.Hamlet.Parse+ ( Result (..)+ , Content (..)+ , Doc (..)+ , parseDoc+ )+ where++import Text.Shakespeare+import Control.Applicative ((<$>), Applicative (..))+import Control.Monad+import Data.Data+import Text.ParserCombinators.Parsec hiding (Line)++data Result v = Error String | Ok v+ deriving (Show, Eq, Read, Data, Typeable)+instance Monad Result where+ return = Ok+ Error s >>= _ = Error s+ Ok v >>= f = f v+ fail = Error+instance Functor Result where+ fmap = liftM+instance Applicative Result where+ pure = return+ (<*>) = ap++data Content = ContentRaw String+ | ContentVar Deref+ | ContentEmbed Deref+ deriving (Show, Eq, Read, Data, Typeable)++data Line = LineForall Deref Ident+ | LineIf Deref+ | LineElseIf Deref+ | LineElse+ | LineWith [(Deref, Ident)]+ | LineMaybe Deref Ident+ | LineNothing+ | LineTag+ { _lineTagName :: String+ , _lineAttr :: [(Maybe Deref, String, [Content])]+ , _lineContent :: [Content]+ }+ | LineContent [Content]+ deriving (Eq, Show, Read)++parseLines :: String -> Result [(Int, Line)]+parseLines s =+ case parse (many parseLine) s s of+ Left e -> Error $ show e+ Right x -> Ok x++parseLine :: Parser (Int, Line)+parseLine = do+ ss <- fmap sum $ many ((char ' ' >> return 1) <|>+ (char '\t' >> return 4))+ x <- comment <|>+ htmlComment <|>+ backslash <|>+ controlIf <|>+ controlElseIf <|>+ (try (string "$else") >> spaceTabs >> eol >> return LineElse) <|>+ controlMaybe <|>+ (try (string "$nothing") >> spaceTabs >> eol >> return LineNothing) <|>+ controlForall <|>+ controlWith <|>+ angle <|>+ (eol' >> return (LineContent [])) <|>+ (do+ cs <- content InContent+ isEof <- (eof >> return True) <|> return False+ if null cs && ss == 0 && isEof+ then fail "End of Hamlet template"+ else return $ LineContent cs)+ return (ss, x)+ where+ eol' = (char '\n' >> return ()) <|> (string "\r\n" >> return ())+ eol = eof <|> eol'+ spaceTabs = many $ oneOf " \t"+ comment = do+ _ <- try $ string "$#"+ _ <- many $ noneOf "\r\n"+ eol+ return $ LineContent []+ htmlComment = do+ _ <- try $ string "<!--"+ _ <- manyTill anyChar $ try $ string "-->"+ x <- many nonComments+ eol+ return $ LineContent [ContentRaw $ concat x] -- FIXME handle variables?+ nonComments = (many1 $ noneOf "\r\n<") <|> (do+ _ <- char '<'+ (do+ _ <- try $ string "!--"+ _ <- manyTill anyChar $ try $ string "-->"+ return "") <|> return "<")+ backslash = do+ _ <- char '\\'+ (eol >> return (LineContent [ContentRaw "\n"]))+ <|> (LineContent <$> content InContent)+ controlIf = do+ _ <- try $ string "$if"+ spaces+ x <- parseDeref+ _ <- spaceTabs+ eol+ return $ LineIf x+ controlElseIf = do+ _ <- try $ string "$elseif"+ spaces+ x <- parseDeref+ _ <- spaceTabs+ eol+ return $ LineElseIf x+ binding = do+ y <- ident+ spaces+ _ <- string "<-"+ spaces+ x <- parseDeref+ _ <- spaceTabs+ return (x,y)+ bindingSep = char ',' >> spaceTabs+ controlMaybe = do+ _ <- try $ string "$maybe"+ spaces+ (x,y) <- binding+ eol+ return $ LineMaybe x y+ controlForall = do+ _ <- try $ string "$forall"+ spaces+ (x,y) <- binding+ eol+ return $ LineForall x y+ controlWith = do+ _ <- try $ string "$with"+ spaces+ bindings <- (binding `sepBy` bindingSep) `endBy` eol+ return $ LineWith $ concat bindings -- concat because endBy returns a [[(Deref,Ident)]]+ content cr = do+ x <- many $ content' cr+ case cr of+ InQuotes -> char '"' >> return ()+ NotInQuotes -> return ()+ NotInQuotesAttr -> return ()+ InContent -> eol+ return $ cc x+ where+ cc [] = []+ cc (ContentRaw a:ContentRaw b:c) = cc $ ContentRaw (a ++ b) : c+ cc (a:b) = a : cc b+ content' cr = contentHash <|> contentCaret <|> contentReg cr+ contentHash = do+ x <- parseHash+ case x of+ Left str -> return $ ContentRaw str+ Right deref -> return $ ContentVar deref+ contentCaret = do+ x <- parseCaret+ case x of+ Left str -> return $ ContentRaw str+ Right deref -> return $ ContentEmbed deref+ contentReg InContent = (ContentRaw . return) <$> noneOf "#@^\r\n"+ contentReg NotInQuotes = (ContentRaw . return) <$> noneOf "@^#. \t\n\r>"+ contentReg NotInQuotesAttr = (ContentRaw . return) <$> noneOf "@^ \t\n\r>"+ contentReg InQuotes = (ContentRaw . return) <$> noneOf "#@^\\\"\n\r>"+ tagAttribValue notInQuotes = do+ cr <- (char '"' >> return InQuotes) <|> return notInQuotes+ content cr+ tagCond = do+ _ <- char ':'+ d <- parseDeref+ _ <- char ':'+ tagAttrib (Just d)+ tagAttrib cond = do+ s <- many1 $ noneOf " \t=\r\n>"+ v <- (do+ _ <- char '='+ s' <- tagAttribValue NotInQuotesAttr+ return s') <|> return []+ return $ TagAttrib (cond, s, v)+ tag' = foldr tag'' ("div", [])+ tag'' (TagName s) (_, y) = (s, y)+ tag'' (TagAttrib s) (x, y) = (x, s : y)+ ident = Ident <$> many1 (alphaNum <|> char '_' <|> char '\'')+ angle = do+ _ <- char '<'+ name' <- many $ noneOf " \t.#\r\n!>"+ let name = if null name' then "div" else name'+ xs <- many $ try ((many $ oneOf " \t") >>+ (tagCond <|> tagAttrib Nothing))+ _ <- many $ oneOf " \t"+ c <- (eol >> return []) <|> (do+ _ <- char '>'+ c <- content InContent+ return c)+ let (tn, attr) = tag' $ TagName name : xs+ return $ LineTag tn attr c++data TagPiece = TagName String+ | TagAttrib (Maybe Deref, String, [Content])+ deriving Show++data ContentRule = InQuotes | NotInQuotes | NotInQuotesAttr | InContent++data Nest = Nest Line [Nest]++nestLines :: [(Int, Line)] -> [Nest]+nestLines [] = []+nestLines ((i, l):rest) =+ let (deeper, rest') = span (\(i', _) -> i' > i) rest+ in Nest l (nestLines deeper) : nestLines rest'++data Doc = DocForall Deref Ident [Doc]+ | DocWith [(Deref,Ident)] [Doc]+ | DocCond [(Deref, [Doc])] (Maybe [Doc])+ | DocMaybe Deref Ident [Doc] (Maybe [Doc])+ | DocTag String [(Maybe Deref, String, [Content])] [Doc]+ | DocContent Content+ -- FIXME PIs+ deriving (Show, Eq, Read, Data, Typeable)++nestToDoc :: [Nest] -> Result [Doc]+nestToDoc [] = Ok []+nestToDoc (Nest (LineForall d i) inside:rest) = do+ inside' <- nestToDoc inside+ rest' <- nestToDoc rest+ Ok $ DocForall d i inside' : rest'+nestToDoc (Nest (LineWith dis) inside:rest) = do+ inside' <- nestToDoc inside+ rest' <- nestToDoc rest+ Ok $ DocWith dis inside' : rest'+nestToDoc (Nest (LineIf d) inside:rest) = do+ inside' <- nestToDoc inside+ (ifs, el, rest') <- parseConds ((:) (d, inside')) rest+ rest'' <- nestToDoc rest'+ Ok $ DocCond ifs el : rest''+nestToDoc (Nest (LineMaybe d i) inside:rest) = do+ inside' <- nestToDoc inside+ (nothing, rest') <-+ case rest of+ Nest LineNothing ninside:x -> do+ ninside' <- nestToDoc ninside+ return (Just ninside', x)+ _ -> return (Nothing, rest)+ rest'' <- nestToDoc rest'+ Ok $ DocMaybe d i inside' nothing : rest''+nestToDoc (Nest (LineTag tn attrs content) inside:rest) = do+ inside' <- nestToDoc inside+ rest' <- nestToDoc rest+ Ok $ (DocTag tn attrs $ map DocContent content ++ inside') : rest'+nestToDoc (Nest (LineContent content) inside:rest) = do+ inside' <- nestToDoc inside+ rest' <- nestToDoc rest+ Ok $ map DocContent content ++ inside' ++ rest'+nestToDoc (Nest (LineElseIf _) _:_) = Error "Unexpected elseif"+nestToDoc (Nest LineElse _:_) = Error "Unexpected else"+nestToDoc (Nest LineNothing _:_) = Error "Unexpected nothing"++parseDoc :: String -> Result [Doc]+parseDoc s = do+ ls <- parseLines s+ let notEmpty (_, LineContent []) = False+ notEmpty _ = True+ let ns = nestLines $ filter notEmpty ls+ ds <- nestToDoc ns+ return ds++parseConds :: ([(Deref, [Doc])] -> [(Deref, [Doc])])+ -> [Nest]+ -> Result ([(Deref, [Doc])], Maybe [Doc], [Nest])+parseConds front (Nest LineElse inside:rest) = do+ inside' <- nestToDoc inside+ Ok $ (front [], Just inside', rest)+parseConds front (Nest (LineElseIf d) inside:rest) = do+ inside' <- nestToDoc inside+ parseConds (front . (:) (d, inside')) rest+parseConds front rest = Ok (front [], Nothing, rest)
+ Text/Hamlet/XML.hs view
@@ -0,0 +1,96 @@+{-# LANGUAGE TemplateHaskell #-}+module Text.Hamlet.XML+ ( xml+ , xmlFile+ ) where++import Language.Haskell.TH.Syntax+import Language.Haskell.TH.Quote+import qualified Data.Text.Lazy as TL+import Control.Monad ((<=<))+import Text.Hamlet.Parse+import Text.Shakespeare (readUtf8File, derefToExp, Scope, Deref (DerefIdent), Ident (Ident))+import Data.Text (pack, unpack)+import qualified Data.Text as T+import qualified Text.XML.Enumerator.Resolved as X+import Data.String (fromString)+import qualified Data.Foldable as F+import Data.Maybe (fromMaybe)++xml :: QuasiQuoter+xml = QuasiQuoter { quoteExp = strToExp }++xmlFile :: FilePath -> Q Exp+xmlFile = strToExp . TL.unpack <=< qRunIO . readUtf8File++strToExp :: String -> Q Exp+strToExp s =+ case parseDoc s of+ Error e -> error e+ Ok x -> docsToExp [] x++docsToExp :: Scope -> [Doc] -> Q Exp+docsToExp scope docs = [| concat $(fmap ListE $ mapM (docToExp scope) docs) |]++docToExp :: Scope -> Doc -> Q Exp+docToExp scope (DocTag name attrs cs) =+ [| [ X.NodeElement (X.Element ($(liftName name)) $(mkAttrs scope attrs) $(docsToExp scope cs))+ ] |]+docToExp _ (DocContent (ContentRaw s)) = [| [ X.NodeContent (pack $(lift s)) ] |]+docToExp scope (DocContent (ContentVar d)) = [| [ X.NodeContent $(return $ derefToExp scope d) ] |]+docToExp scope (DocContent (ContentEmbed d)) = return $ derefToExp scope d+docToExp scope (DocForall deref ident@(Ident ident') inside) = do+ let list' = derefToExp scope deref+ name <- newName ident'+ let scope' = (ident, VarE name) : scope+ inside' <- docsToExp scope' inside+ let lam = LamE [VarP name] inside'+ [| F.concatMap $(return lam) $(return list') |]+docToExp scope (DocWith [] inside) = docsToExp scope inside+docToExp scope (DocWith ((deref, ident@(Ident name)):dis) inside) = do+ let deref' = derefToExp scope deref+ name' <- newName name+ let scope' = (ident, VarE name') : scope+ inside' <- docToExp scope' (DocWith dis inside)+ let lam = LamE [VarP name'] inside'+ return $ lam `AppE` deref'+docToExp scope (DocMaybe deref ident@(Ident name) just nothing) = do+ let deref' = derefToExp scope deref+ name' <- newName name+ let scope' = (ident, VarE name') : scope+ inside' <- docsToExp scope' just+ let inside'' = LamE [VarP name'] inside'+ nothing' <-+ case nothing of+ Nothing -> [| [] |]+ Just n -> docsToExp scope n+ [| maybe $(return nothing') $(return inside'') $(return deref') |]+docToExp scope (DocCond conds final) = do+ unit <- [| () |]+ body <- fmap GuardedB $ mapM go $ conds ++ [(DerefIdent $ Ident "otherwise", fromMaybe [] final)]+ return $ CaseE unit [Match (TupP []) body []]+ where+ go (deref, inside) = do+ inside' <- docsToExp scope inside+ return (NormalG $ derefToExp scope deref, inside')++mkAttrs :: Scope -> [(Maybe Deref, String, [Content])] -> Q Exp+mkAttrs _ [] = [| [] |]+mkAttrs scope ((mderef, name, value):rest) = do+ rest' <- mkAttrs scope rest+ this <- [| ($(liftName name), T.concat $(fmap ListE $ mapM go value)) |]+ let with = [| $(return this) : $(return rest') |]+ case mderef of+ Nothing -> with+ Just deref -> [| if $(return $ derefToExp scope deref) then $(with) else $(return rest') |]+ where+ go (ContentRaw s) = [| pack $(lift s) |]+ go (ContentVar d) = return $ derefToExp scope d+ go ContentEmbed{} = error "Cannot use embed interpolation in attribute value"++liftName :: String -> Q Exp+liftName s = do+ X.Name local mns _ <- return $ fromString s+ case mns of+ Nothing -> [| X.Name (pack $(lift $ unpack local)) Nothing Nothing |]+ Just ns -> [| X.Name (pack $(lift $ unpack local)) (Just $ pack $(lift $ unpack ns)) Nothing |]
+ Text/Shakespeare.hs view
@@ -0,0 +1,225 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE CPP #-}+-- | General parsers, functions and datatypes for all three languages.+module Text.Shakespeare+ ( Deref (..)+ , Ident (..)+ , Scope+ , parseDeref+ , parseHash+ , parseVar+ , parseAt+ , parseUrl+ , parseCaret+ , parseUnder+ , parseInt+ , derefToExp+ , flattenDeref+ , readUtf8File+ ) where++import Language.Haskell.TH.Syntax+import Language.Haskell.TH (appE)+import Data.Char (isUpper)+import Text.ParserCombinators.Parsec+import Data.List (intercalate)+import Data.Ratio (Ratio, numerator, denominator, (%))+import Data.Data (Data)+import Data.Typeable (Typeable)+import qualified Data.Text.Lazy as TL+import qualified System.IO as SIO+import qualified Data.Text.Lazy.IO as TIO++newtype Ident = Ident String+ deriving (Show, Eq, Read, Data, Typeable)++type Scope = [(Ident, Exp)]++data Deref = DerefModulesIdent [String] Ident+ | DerefIdent Ident+ | DerefIntegral Integer+ | DerefRational Rational+ | DerefString String+ | DerefBranch Deref Deref+ deriving (Show, Eq, Read, Data, Typeable)++instance Lift Ident where+ lift (Ident s) = [|Ident|] `appE` lift s+instance Lift Deref where+ lift (DerefModulesIdent v s) = do+ dl <- [|DerefModulesIdent|]+ v' <- lift v+ s' <- lift s+ return $ dl `AppE` v' `AppE` s'+ lift (DerefIdent s) = do+ dl <- [|DerefIdent|]+ s' <- lift s+ return $ dl `AppE` s'+ lift (DerefBranch x y) = do+ x' <- lift x+ y' <- lift y+ db <- [|DerefBranch|]+ return $ db `AppE` x' `AppE` y'+ lift (DerefIntegral i) = [|DerefIntegral|] `appE` lift i+ lift (DerefRational r) = do+ n <- lift $ numerator r+ d <- lift $ denominator r+ per <- [|(%) :: Int -> Int -> Ratio Int|]+ dr <- [|DerefRational|]+ return $ dr `AppE` (InfixE (Just n) per (Just d))+ lift (DerefString s) = [|DerefString|] `appE` lift s++parseDeref :: Parser Deref+parseDeref = do+ skipMany $ oneOf " \t"+ x <- derefSingle+ res <- deref' $ (:) x+ skipMany $ oneOf " \t"+ return res+ where+ delim = (many1 (char ' ') >> return())+ <|> lookAhead (char '(' >> return ())+ derefOp = try $ do+ _ <- char '('+ x <- many1 $ noneOf " \t\n\r()"+ _ <- char ')'+ return $ DerefIdent $ Ident x+ derefParens = between (char '(') (char ')') parseDeref+ derefSingle = derefOp <|> derefParens <|> numeric <|> strLit<|> ident+ deref' lhs =+ dollar <|> derefSingle'+ <|> return (foldl1 DerefBranch $ lhs [])+ where+ dollar = do+ _ <- try $ delim >> char '$'+ rhs <- parseDeref+ let lhs' = foldl1 DerefBranch $ lhs []+ return $ DerefBranch lhs' rhs+ derefSingle' = do+ x <- try $ delim >> derefSingle+ deref' $ lhs . (:) x+ numeric = do+ n <- (char '-' >> return "-") <|> return ""+ x <- many1 digit+ y <- (char '.' >> fmap Just (many1 digit)) <|> return Nothing+ return $ case y of+ Nothing -> DerefIntegral $ read' "Integral" $ n ++ x+ Just z -> DerefRational $ toRational+ (read' "Rational" $ n ++ x ++ '.' : z :: Double)+ strLit = do+ _ <- char '"'+ chars <- many quotedChar+ _ <- char '"'+ return $ DerefString chars+ quotedChar = (char '\\' >> escapedChar) <|> noneOf "\""+ escapedChar =+ (char 'n' >> return '\n') <|>+ (char 'r' >> return '\r') <|>+ (char 'b' >> return '\b') <|>+ (char 't' >> return '\t') <|>+ (char '\\' >> return '\\') <|>+ (char '"' >> return '"') <|>+ (char '\'' >> return '\'')+ ident = do+ mods <- many modul+ func <- many1 (alphaNum <|> char '_' <|> char '\'')+ let func' = Ident func+ return $+ if null mods+ then DerefIdent func'+ else DerefModulesIdent mods func'+ modul = try $ do+ c <- upper+ cs <- many (alphaNum <|> char '_')+ _ <- char '.'+ return $ c : cs++read' :: Read a => String -> String -> a+read' t s =+ case reads s of+ (x, _):_ -> x+ [] -> error $ t ++ " read failed: " ++ s++expType :: Ident -> Name -> Exp+expType (Ident (c:_)) = if isUpper c || c == ':' then ConE else VarE+expType (Ident "") = error "Bad Ident"++derefToExp :: Scope -> Deref -> Exp+derefToExp s (DerefBranch x y) = derefToExp s x `AppE` derefToExp s y+derefToExp _ (DerefModulesIdent mods i@(Ident s)) =+ expType i $ Name (mkOccName s) (NameQ $ mkModName $ intercalate "." mods)+derefToExp scope (DerefIdent i@(Ident s)) =+ case lookup i scope of+ Just e -> e+ Nothing -> expType i $ mkName s+derefToExp _ (DerefIntegral i) = LitE $ IntegerL i+derefToExp _ (DerefRational r) = LitE $ RationalL r+derefToExp _ (DerefString s) = LitE $ StringL s++-- FIXME shouldn't we use something besides a list here?+flattenDeref :: Deref -> Maybe [String]+flattenDeref (DerefIdent (Ident x)) = Just [x]+flattenDeref (DerefBranch (DerefIdent (Ident x)) y) = do+ y' <- flattenDeref y+ Just $ y' ++ [x]+flattenDeref _ = Nothing++parseHash :: Parser (Either String Deref)+parseHash = parseVar '#'++parseVar :: Char -> Parser (Either String Deref)+parseVar c = do+ _ <- char c+ (char '\\' >> return (Left [c])) <|> (do+ _ <- char '{'+ deref <- parseDeref+ _ <- char '}'+ return $ Right deref) <|> (do+ -- Check for hash just before newline+ _ <- lookAhead (oneOf "\r\n" >> return ()) <|> eof+ return $ Left ""+ ) <|> return (Left [c])++parseAt :: Parser (Either String (Deref, Bool))+parseAt = parseUrl '@' '?'++parseUrl :: Char -> Char -> Parser (Either String (Deref, Bool))+parseUrl c d = do+ _ <- char c+ (char '\\' >> return (Left [c])) <|> (do+ x <- (char d >> return True) <|> return False+ (do+ _ <- char '{'+ deref <- parseDeref+ _ <- char '}'+ return $ Right (deref, x))+ <|> return (Left $ if x then [c, d] else [c]))++parseCaret :: Parser (Either String Deref)+parseCaret = parseInt '^'++parseInt :: Char -> Parser (Either String Deref)+parseInt c = do+ _ <- char c+ (char '\\' >> return (Left [c])) <|> (do+ _ <- char '{'+ deref <- parseDeref+ _ <- char '}'+ return $ Right deref) <|> return (Left [c])++parseUnder :: Parser (Either String Deref)+parseUnder = do+ _ <- char '_'+ (char '\\' >> return (Left "_")) <|> (do+ _ <- char '{'+ deref <- parseDeref+ _ <- char '}'+ return $ Right deref) <|> return (Left "_")++readUtf8File :: FilePath -> IO TL.Text+readUtf8File fp = do+ h <- SIO.openFile fp SIO.ReadMode+ SIO.hSetEncoding h SIO.utf8_bom+ TIO.hGetContents h
+ xml-hamlet.cabal view
@@ -0,0 +1,32 @@+Name: xml-hamlet+Version: 0.0.0+Synopsis: Hamlet-style quasiquoter for XML content+Homepage: http://www.yesodweb.com/+License: BSD3+License-file: LICENSE+Author: Michael Snoyman+Maintainer: michael@snoyman.com+Category: Text+Build-type: Simple++Cabal-version: >=1.8++Library+ Exposed-modules: Text.Hamlet.XML+ Other-modules: Text.Hamlet.Parse+ Text.Shakespeare+ + Build-depends: base >= 4 && < 5+ , xml-enumerator >= 0.3.4 && < 0.4+ , text >= 0.10 && < 1.0+ , template-haskell+ , parsec >= 2.0 && < 3.2++ Ghc-options: -Wall++test-suite runtests+ main-is: runtests.hs+ type: exitcode-stdio-1.0+ ghc-options: -Wall+ build-depends: hspec >= 0.6 && < 0.7+ , HUnit >= 1.2 && < 1.3