diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -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.
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,7 @@
+#!/usr/bin/env runhaskell
+
+> module Main where
+> import Distribution.Simple
+
+> main :: IO ()
+> main = defaultMain
diff --git a/Text/Hamlet/Parse.hs b/Text/Hamlet/Parse.hs
new file mode 100644
--- /dev/null
+++ b/Text/Hamlet/Parse.hs
@@ -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)
diff --git a/Text/Hamlet/XML.hs b/Text/Hamlet/XML.hs
new file mode 100644
--- /dev/null
+++ b/Text/Hamlet/XML.hs
@@ -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 |]
diff --git a/Text/Shakespeare.hs b/Text/Shakespeare.hs
new file mode 100644
--- /dev/null
+++ b/Text/Shakespeare.hs
@@ -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
diff --git a/xml-hamlet.cabal b/xml-hamlet.cabal
new file mode 100644
--- /dev/null
+++ b/xml-hamlet.cabal
@@ -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
