diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# mustache [![Travis Status](https://travis-ci.org/JustusAdam/mustache.svg?branch=master)](https://travis-ci.org/JustusAdam/mustache)
+# mustache [![Travis Status](https://travis-ci.org/JustusAdam/mustache.svg?branch=master)](https://travis-ci.org/JustusAdam/mustache) [![Hackage](https://img.shields.io/hackage/v/mustache.svg)](https://hackage.haskell.org/package/mustache) [![Join the chat at https://gitter.im/JustusAdam/mustache](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/JustusAdam/mustache?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 
 Haskell implementation of [mustache templates][mustache-homepage].
 
@@ -6,7 +6,7 @@
 
 ## Motivation
 
-The old Haskell implementation of mustache tempates [hastache][] seemed pretty abandoned to me. This implementation aims to be much easier to use and (fingers crossed) better maintained.
+The old Haskell implementation of mustache templates [hastache][] seemed pretty abandoned to me. This implementation aims to be much easier to use and (fingers crossed) better maintained.
 
 [hastache]: https://hackage.haskell.org/package/hastache
 
@@ -41,6 +41,6 @@
 - [x] Support for 'set delimiter'
 - [x] More efficiency using `Text` rather than `String`
 - [x] More efficient Text parsing
-- [ ] Full unittest coverage (partially done)
+- [x] Test coverage by the official [specs](https://github.com/mustache/spec)
 - [x] Haddock documentation
 - [ ] More instances for `ToMustache` typeclass
diff --git a/Setup.hs b/Setup.hs
--- a/Setup.hs
+++ b/Setup.hs
@@ -1,2 +1,2 @@
-import Distribution.Simple
+import           Distribution.Simple
 main = defaultMain
diff --git a/mustache.cabal b/mustache.cabal
--- a/mustache.cabal
+++ b/mustache.cabal
@@ -1,9 +1,11 @@
 name:                mustache
-version:             0.1.0.0
+version:             0.2.0.0
 synopsis:            A mustache template parser library.
 description:
   Allows parsing and rendering template files with mustache markup. See the
   mustache <http://mustache.github.io/mustache.5.html language reference>.
+
+  Implements the mustache spec version 1.1.3
 license:             BSD3
 license-file:        LICENSE
 author:              Justus Adam
@@ -24,7 +26,7 @@
   type:     git
   branch:   master
   location: git://github.com/JustusAdam/mustache.git
-  tag:      0.1.0.0
+  tag:      v1.0.0.0rc-0
 
 
 
@@ -52,7 +54,9 @@
                        filepath,
                        scientific,
                        conversion >= 1.2,
-                       conversion-text
+                       conversion-text,
+                       base-unicode-symbols,
+                       ja-base-extra >= 0.2.1
   hs-source-dirs:      src/lib
   default-language:    Haskell2010
   ghc-options:
@@ -70,7 +74,8 @@
                        aeson,
                        cmdargs,
                        text,
-                       filepath
+                       filepath,
+                       base-unicode-symbols
   default-language:    Haskell2010
   hs-source-dirs:      src/bin
 
@@ -82,6 +87,13 @@
                       hspec,
                       text,
                       mustache,
-                      aeson
+                      aeson,
+                      unordered-containers,
+                      yaml,
+                      filepath,
+                      process,
+                      temporary,
+                      directory,
+                      base-unicode-symbols
   hs-source-dirs:     test/unit
   default-language:   Haskell2010
diff --git a/src/bin/Main.hs b/src/bin/Main.hs
--- a/src/bin/Main.hs
+++ b/src/bin/Main.hs
@@ -4,88 +4,69 @@
 module Main (main) where
 
 
-import           Control.Applicative             ((<|>))
 import           Data.Aeson                      (Value, eitherDecode)
-import qualified Data.ByteString                 as B
-import qualified Data.ByteString.Lazy            as BS
-import           Data.Char                       (toLower)
-import           Data.Maybe                      (fromMaybe)
-import qualified Data.Text.IO                    as TIO
-import qualified Data.Yaml                       as Y
-import           System.Console.CmdArgs.Implicit
-import           System.FilePath
-import           Text.Mustache
+import qualified Data.ByteString                 as B (readFile)
+import qualified Data.ByteString.Lazy            as BS (readFile)
+import           Data.Foldable                   (for_)
+import qualified Data.Text.IO                    as TIO (putStrLn)
+import           Data.Yaml                       (decodeEither)
+import           Prelude.Unicode
+import           System.Console.CmdArgs.Implicit (Data, Typeable, argPos, args,
+                                                  cmdArgs, def, help, summary,
+                                                  typ, (&=))
+import           System.FilePath                 (takeExtension)
+import           Text.Mustache                   (automaticCompile, substitute,
+                                                  toMustache)
 
 
 data Arguments = Arguments
   { template     ∷ FilePath
   , templateDirs ∷ [FilePath]
-  , dataFile     ∷ FilePath
-  , outputFile   ∷ Maybe FilePath
-  , dataFormat   ∷ Maybe String
+  , dataFiles    ∷ [FilePath]
   } deriving (Show, Data, Typeable)
 
 
 commandArgs ∷ Arguments
 commandArgs = Arguments
-  { template  = def
+  { template = def
       &= argPos 0
       &= typ "TEMPLATE"
-  , dataFile  = def
-      &= argPos 1
-      &= typ "DATAFILE"
-  , outputFile = Nothing
-      &= help "Name of the resulting file"
-      &= typFile
+  , dataFiles = def
+      &= args
+      &= typ "DATA-FILES"
   , templateDirs = ["."]
       &= help "The directories in which to search for the templates"
       &= typ "DIRECTORIES"
-  , dataFormat = Nothing
-      &= help "Format for input data"
   } &= summary "Simple mustache template subtitution"
 
 
 readJSON ∷ FilePath → IO (Either String Value)
-readJSON = fmap eitherDecode . BS.readFile
+readJSON = fmap eitherDecode ∘ BS.readFile
 
 
 readYAML ∷ FilePath → IO (Either String Value)
-readYAML = fmap Y.decodeEither . B.readFile
-
-
-readers ∷ [(String, FilePath → IO (Either String Value))]
-readers =
-  [ ("yaml", readYAML)
-  , ("yml" , readYAML)
-  , ("json", readJSON)
-  ]
-
-
-getReader ∷ String → Maybe (FilePath → IO (Either String Value))
-getReader = flip lookup readers . map toLower
+readYAML = fmap decodeEither ∘ B.readFile
 
 
 main ∷ IO ()
 main = do
-  a@(Arguments { dataFormat, template, templateDirs, dataFile, outputFile })
-    ← cmdArgs commandArgs
+  (Arguments { template, templateDirs, dataFiles }) ← cmdArgs commandArgs
 
-  print a
-  eitherTemplate ← compileTemplate templateDirs template
+  eitherTemplate ← automaticCompile templateDirs template
 
   case eitherTemplate of
     Left err → print err
-    Right compiledTemplate → do
-
-      let decode = fromMaybe readJSON $
-            (dataFormat >>= getReader)
-            <|> getReader (drop 1 $ takeExtension dataFile)
+    Right compiledTemplate →
+      for_ dataFiles $ \file → do
 
-      let write = maybe TIO.putStrLn TIO.writeFile outputFile
-      decoded ← decode dataFile
+        let decoder =
+              case takeExtension file of
+                ".yml"  → readYAML
+                ".yaml" → readYAML
+                _       → readJSON
+        decoded ← decoder file
 
-      either
-        putStrLn
-        write
-        $ decoded >>=
-          substitute compiledTemplate . toMustache
+        either
+          putStrLn
+          (TIO.putStrLn ∘ substitute compiledTemplate ∘ toMustache)
+          decoded
diff --git a/src/lib/Text/Mustache.hs b/src/lib/Text/Mustache.hs
--- a/src/lib/Text/Mustache.hs
+++ b/src/lib/Text/Mustache.hs
@@ -6,6 +6,35 @@
 Maintainer  : development@justusadam.com
 Stability   : experimental
 Portability : POSIX
+
+* How to use this library
+
+This module exposes some of the most convenient functions for dealing with mustache
+templates.
+
+The easiest way of compiling a file and its potential includes (called partials)
+is by using the 'compileTemplate' function.
+@
+      -- the search space encompasses all directories in which the compiler should
+      -- search for the template source files
+      --
+      -- the search is conducted in order, from left to right.
+  let searchSpace = [".", "./templates"]
+      -- the templateName is the relative path of the template to any directory
+      -- of the search space
+      templateName = "main.mustache"
+
+    compiled <- automaticCompile searchSpace templateName
+    case compiled of
+      -- the compiler will throw errors if either the template is malformed
+      -- or the source file for a partial could not be found
+      Left err -> print err
+      Right template -> return () -- this is where you can start using it
+@
+
+Should your search space be only the current working directory, you can use
+'localAutomaticCompile'.
+
 -}
 {-# LANGUAGE LambdaCase #-}
 module Text.Mustache
@@ -13,10 +42,10 @@
   -- * Compiling
 
   -- ** Automatic
-    compileTemplate
+    automaticCompile, localAutomaticCompile
 
   -- ** Manually
-  , compileTemplateWithCache, parseTemplate, MustacheTemplate(..)
+  , compileTemplateWithCache, parseTemplate, Template(..)
 
   -- * Rendering
 
@@ -30,12 +59,6 @@
 
   -- ** Data Conversion
   , ToMustache, toMustache, object, (~>), (~=), (~~>), (~~=)
-
-  -- * Util
-
-  -- | These are functions used internally by the parser and renderer. Whether
-  -- these  will continue to be exposed is to be seen.
-  , getFile , getPartials , getPartials', toString, search, Context(..)
   ) where
 
 
diff --git a/src/lib/Text/Mustache/Compile.hs b/src/lib/Text/Mustache/Compile.hs
--- a/src/lib/Text/Mustache/Compile.hs
+++ b/src/lib/Text/Mustache/Compile.hs
@@ -1,26 +1,35 @@
 {-# LANGUAGE UnicodeSyntax #-}
-module Text.Mustache.Compile where
+module Text.Mustache.Compile
+  ( automaticCompile, localAutomaticCompile, TemplateCache, compileTemplateWithCache
+  , parseTemplate, cacheFromList, getPartials
+  ) where
 
 
-import           Control.Applicative
+import           Control.Arrow              ((&&&))
 import           Control.Monad
 import           Control.Monad.Except
+import           Control.Monad.State
 import           Control.Monad.Trans.Either
+import           Control.Monad.Unicode
 import           Data.Bool
-import           Data.Foldable              (fold)
-import           Data.List
-import           Data.Monoid
+import           Data.Function.JAExtra
+import           Data.HashMap.Strict        as HM
+import           Data.Monoid.Unicode        ((⊕), (∅))
 import           Data.Text                  hiding (concat, find, map, uncons)
 import qualified Data.Text.IO               as TIO
+import           Prelude.Unicode
 import           System.Directory
 import           System.FilePath
-import           Text.Mustache.Types
 import           Text.Mustache.Parser
+import           Text.Mustache.Types
 import           Text.Parsec.Error
 import           Text.Parsec.Pos
 import           Text.Printf
 
 
+type TemplateCache = HM.HashMap String Template
+
+
 {-|
   Compiles a mustache template provided by name including the mentioned partials.
 
@@ -32,56 +41,80 @@
   A reference to the included template will be found in each including templates
   'partials' section.
 -}
-compileTemplate ∷ [FilePath] → FilePath → IO (Either ParseError MustacheTemplate)
-compileTemplate searchSpace = compileTemplateWithCache searchSpace mempty
+automaticCompile ∷ [FilePath] → FilePath → IO (Either ParseError Template)
+automaticCompile searchSpace = compileTemplateWithCache searchSpace (∅)
 
 
+localAutomaticCompile ∷ FilePath → IO (Either ParseError Template)
+localAutomaticCompile = automaticCompile ["."]
+
+
 {-|
   Compile a mustache template providing a list of precompiled templates that do
   not have to be recompiled.
 -}
-compileTemplateWithCache ∷ [FilePath] → [MustacheTemplate] → FilePath → IO (Either ParseError MustacheTemplate)
-compileTemplateWithCache searchSpace = (runEitherT .) . compile'
+compileTemplateWithCache ∷ [FilePath]
+                         → TemplateCache
+                         → FilePath
+                         → IO (Either ParseError Template)
+compileTemplateWithCache searchSpace templates initName =
+  runEitherT $ evalStateT (compile' initName) $ flattenPartials templates
   where
-    compile' templates name' =
-      case find ((== name') . name) templates of
+    compile' :: FilePath
+             → StateT
+                (HM.HashMap String Template)
+                (EitherT ParseError IO)
+                Template
+    compile' name' = do
+      templates' ← get
+      case HM.lookup name' templates' of
         Just template → return template
         Nothing → do
-          rawSource ← getFile searchSpace name'
-          compiled@(MustacheTemplate { ast = mast }) ←
-            hoistEither $ parseTemplate name' rawSource
+          rawSource ← lift $ getFile searchSpace name'
+          compiled@(Template { ast = mAST }) ←
+            lift $ hoistEither $ parseTemplate name' rawSource
 
           foldM
-            (\st@(MustacheTemplate { partials = p }) partialName →
-              compile' (p <> templates) partialName >>=
-                \nt → return (st { partials = nt : p })
+            (\st@(Template { partials = p }) partialName → do
+              nt ← compile' partialName
+              modify (HM.insert partialName nt)
+              return (st { partials = HM.insert partialName nt p })
             )
             compiled
-            (getPartials mast)
+            (getPartials mAST)
 
 
-parseTemplate ∷ String → Text → Either ParseError MustacheTemplate
-parseTemplate name' = fmap (flip (MustacheTemplate name') mempty) . parse name'
+cacheFromList ∷ [Template] → TemplateCache
+cacheFromList = flattenPartials ∘ fromList ∘ fmap (name &&& id)
 
 
+parseTemplate ∷ String → Text → Either ParseError Template
+parseTemplate name' = fmap (flip (Template name') (∅)) ∘ parse name'
+
+
 {-|
   Find the names of all included partials in a mustache AST.
 
   Same as @join . fmap getPartials'@
 -}
-getPartials ∷ MustacheAST → [FilePath]
-getPartials = join . fmap getPartials'
+getPartials ∷ AST → [FilePath]
+getPartials = join ∘ fmap getPartials'
 
 
 {-|
-  Find partials in a single MustacheNode
+  Find partials in a single Node
 -}
-getPartials' ∷ MustacheNode Text → [FilePath]
-getPartials' (MustachePartial p) = return p
-getPartials' (MustacheSection _ n) = getPartials n
-getPartials' _ = mempty
+getPartials' ∷ Node Text → [FilePath]
+getPartials' (Partial _ p) = return p
+getPartials' (Section _ n) = getPartials n
+getPartials' (InvertedSection _ n) = getPartials n
+getPartials' _                     = (∅)
 
 
+flattenPartials ∷ TemplateCache → TemplateCache
+flattenPartials = stuffWith $ foldrWithKey $ insertWith discard
+
+
 {-|
   @getFile searchSpace file@ iteratively searches all directories in
   @searchSpace@ for a @file@ returning it if found or raising an error if none
@@ -93,7 +126,7 @@
 getFile ∷ [FilePath] → FilePath → EitherT ParseError IO Text
 getFile [] fp = throwError $ fileNotFound fp
 getFile (templateDir : xs) fp =
-  lift (doesFileExist filePath) >>=
+  lift (doesFileExist filePath) ≫=
     bool
       (getFile xs fp)
       (lift $ TIO.readFile filePath)
diff --git a/src/lib/Text/Mustache/Internal.hs b/src/lib/Text/Mustache/Internal.hs
--- a/src/lib/Text/Mustache/Internal.hs
+++ b/src/lib/Text/Mustache/Internal.hs
@@ -4,9 +4,9 @@
 
 
 #if MIN_VERSION_base(4,8,0)
-  import Data.List (uncons)
+import Data.List (uncons)
 #else
-  uncons ∷ [a] → Maybe (a, [a])
-  uncons [] = Nothing
-  uncons (x:xs) = return (x, xs)
+uncons ∷ [a] → Maybe (a, [a])
+uncons []     = Nothing
+uncons (x:xs) = return (x, xs)
 #endif
diff --git a/src/lib/Text/Mustache/Parser.hs b/src/lib/Text/Mustache/Parser.hs
--- a/src/lib/Text/Mustache/Parser.hs
+++ b/src/lib/Text/Mustache/Parser.hs
@@ -1,16 +1,10 @@
-{-|
-Module      : $Header$
-Description : Functions for parsing mustache templates
-Copyright   : (c) Justus Adam, 2015
-License     : LGPL-3
-Maintainer  : development@justusadam.com
-Stability   : experimental
-Portability : POSIX
--}
-{-# LANGUAGE LambdaCase        #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE TupleSections     #-}
-{-# LANGUAGE UnicodeSyntax     #-}
+{-# LANGUAGE FlexibleContexts      #-}
+{-# LANGUAGE LambdaCase            #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE NamedFieldPuns        #-}
+{-# LANGUAGE OverloadedStrings     #-}
+{-# LANGUAGE TupleSections         #-}
+{-# LANGUAGE UnicodeSyntax         #-}
 module Text.Mustache.Parser
   (
   -- * Generic parsing functions
@@ -19,16 +13,15 @@
 
   -- * Configurations
 
-  , MustacheConf, emptyConf, defaultConf
+  , MustacheConf, defaultConf
 
   -- * Parser
 
-  , MustacheParser
+  , Parser
 
   -- ** Components
 
-  , genParseTag, parseSection, parseTag, parseVariable, parsePartial, parseText
-  , parseInvertedSection, parseDelimiterChange, parseUnescapedVar, parseEnd
+  , parseText
 
   -- * Mustache Constants
 
@@ -39,199 +32,272 @@
 
 
 import           Control.Monad
-import           Data.Char           (isAlphaNum, isSpace)
-import           Data.Foldable       (fold)
-import           Data.Functor        ((<$>))
-import           Data.List           (nub)
-import           Data.Monoid         (mempty, (<>))
-import           Data.Text           as T
-import           Prelude             as Prel
+import           Control.Monad.Unicode
+import           Conversion                  (Conversion, convert)
+import           Conversion.Text             ()
+import           Data.Char                   (isAlphaNum, isSpace)
+import           Data.Functor                ((<$>))
+import           Data.List                   (nub)
+import           Data.Monoid.Unicode         ((∅), (⊕))
+import           Data.Text                   as T (Text, null, pack)
+import           Prelude                     as Prel
+import           Prelude.Unicode
 import           Text.Mustache.Types
-import           Text.Parsec         as P hiding (parse)
+import           Text.Parsec                 as P hiding (endOfLine, parse)
 
 
 data MustacheConf = MustacheConf
   { delimiters ∷ (String, String)
   }
 
+
+data MustacheState = MustacheState
+  { sDelimiters        ∷ (String, String)
+  , textStack          ∷ Text
+  , isBeginngingOfLine ∷ Bool
+  , currentSectionName ∷ Maybe DataIdentifier
+  }
+
+data ParseTagRes
+  = SectionBegin Bool DataIdentifier
+  | SectionEnd DataIdentifier
+  | Tag (Node Text)
+  | HandledTag
+
+
 -- | @#@
-sectionBegin ∷ String
-sectionBegin = "#"
+sectionBegin ∷ Char
+sectionBegin = '#'
 -- | @/@
-sectionEnd ∷ String
-sectionEnd = "/"
+sectionEnd ∷ Char
+sectionEnd = '/'
 -- | @>@
-partialBegin ∷ String
-partialBegin = ">"
+partialBegin ∷ Char
+partialBegin = '>'
 -- | @^@
-invertedSectionBegin ∷ String
-invertedSectionBegin = "^"
+invertedSectionBegin ∷ Char
+invertedSectionBegin = '^'
 -- | @{@ and @}@
-unescape2 ∷ (String, String)
-unescape2 = ("{", "}")
+unescape2 ∷ (Char, Char)
+unescape2 = ('{', '}')
 -- | @&@
-unescape1 ∷ String
-unescape1 = "&"
+unescape1 ∷ Char
+unescape1 = '&'
 -- | @=@
-delimiterChange ∷ String
-delimiterChange = "="
+delimiterChange ∷ Char
+delimiterChange = '='
 -- | @.@
-nestingSeparator ∷ String
-nestingSeparator = "."
+nestingSeparator ∷ Char
+nestingSeparator = '.'
+-- | @!@
+comment ∷ Char
+comment = '!'
+-- | @.@
+implicitIterator ∷ Char
+implicitIterator = '.'
 -- | Cannot be a letter, number or the nesting separation Character @.@
 isAllowedDelimiterCharacter ∷ Char → Bool
 isAllowedDelimiterCharacter =
-  not . Prel.or . sequence
-    [ isSpace, isAlphaNum, flip elem nestingSeparator ]
-allowedDelimiterCharacter ∷ MustacheParser Char
+  not ∘ Prel.or ∘ sequence
+    [ isSpace, isAlphaNum, (≡ nestingSeparator) ]
+allowedDelimiterCharacter ∷ Parser Char
 allowedDelimiterCharacter =
   satisfy isAllowedDelimiterCharacter
 
 
 -- | Empty configuration
-emptyConf ∷ MustacheConf
-emptyConf = MustacheConf ("", "")
+emptyState ∷ MustacheState
+emptyState = MustacheState ("", "") (∅) True Nothing
 
 
 -- | Default configuration (delimiters = ("{{", "}}"))
 defaultConf ∷ MustacheConf
-defaultConf = emptyConf { delimiters = ("{{", "}}") }
+defaultConf = MustacheConf ("{{", "}}")
 
 
-type MustacheParser = Parsec Text MustacheConf
-type MNodeParser = MustacheParser (MustacheNode Text)
+initState ∷ MustacheConf → MustacheState
+initState (MustacheConf { delimiters }) = emptyState { sDelimiters = delimiters }
 
 
+type Parser = Parsec Text MustacheState
+
+
+(<<) ∷ Monad m ⇒ m b → m a → m b
+(<<) = flip (≫)
+
+
+endOfLine ∷ Parser String
+endOfLine = do
+  r ← optionMaybe $ char '\r'
+  n ← char '\n'
+  return $ maybe id (:) r [n]
+
+
 {-|
   Runs the parser for a mustache template, returning the syntax tree.
 -}
-parse ∷ FilePath → Text → Either ParseError MustacheAST
+parse ∷ FilePath → Text → Either ParseError AST
 parse = parseWithConf defaultConf
 
 
-parseWithConf ∷ MustacheConf → FilePath → Text → Either ParseError MustacheAST
-parseWithConf = P.runParser (parseText Nothing)
+parseWithConf ∷ MustacheConf → FilePath → Text → Either ParseError AST
+parseWithConf = P.runParser parseText ∘ initState
 
 
-parseText ∷ Maybe [Text] → MustacheParser MustacheAST
-parseText
-  tagName = do
-    (MustacheConf { delimiters = ( start, _ )}) ← getState
-    let endOfText = try (void $ string start) <|> try eof
-    content ← pack <$> manyTill anyChar (lookAhead endOfText)
-    others ← parseTag tagName
-    return $ if T.null content
-              then others
-              else MustacheText content : others
+parseText ∷ Parser AST
+parseText = do
+  (MustacheState { isBeginngingOfLine }) ← getState
+  if isBeginngingOfLine
+    then parseLine
+    else continueLine
 
 
+appendTextStack ∷ Conversion t Text ⇒ t → Parser ()
+appendTextStack t = modifyState (\s → s { textStack = textStack s ⊕ convert t})
 
-parseTag ∷ Maybe [Text] → MustacheParser MustacheAST
-parseTag tagName = choice
-  [ parseEnd tagName >> return []
-  , parseSection >>= continue
-  , parseInvertedSection >>= continue
-  , parseUnescapedVar >>= continue
-  , parseDelimiterChange >> parseText tagName
-  , parsePartial >>= continue
-  , parseVariable >>= continue
-  , eof >> maybe (return []) (parserFail . ("Unclosed section " <>) . unpack . fold) tagName
-  ]
-  where
-    continue val = (val :) <$> parseText tagName
 
-parseSection ∷ MNodeParser
-parseSection = do
-  sectionName ← genParseTag sectionBegin mempty
-  MustacheSection sectionName <$> parseText (return sectionName)
+continueLine ∷ Parser AST
+continueLine = do
+  (MustacheState { sDelimiters = ( start, _ )}) ← getState
+  let forbidden = head start : "\n\r"
 
+  many (noneOf forbidden) ≫= appendTextStack
 
-parsePartial ∷ MNodeParser
-parsePartial = do
-  (MustacheConf { delimiters = ( start, end )}) <- getState
-  let pStart = start <> partialBegin
-      pEnd = end
-  void $ try $ string pStart
-  spaces
-  MustachePartial <$>
-    anyChar `manyTill` try (skipMany space >> string pEnd)
+  (try endOfLine ≫= appendTextStack ≫ modifyState (\s → s { isBeginngingOfLine = True }) ≫ parseLine)
+    <|> (try (string start) ≫ switchOnTag ≫= continueFromTag)
+    <|> (try eof ≫ finishFile)
+    <|> (anyChar ≫= appendTextStack ≫ continueLine)
 
 
-parseDelimiterChange ∷ MustacheParser ()
-parseDelimiterChange = do
-  (MustacheConf { delimiters = ( start, end )}) <- getState
-  void $ try $ string (start <> delimiterChange)
-  delim1 ← allowedDelimiterCharacter `manyTill` space
-  spaces
-  delim2 ← allowedDelimiterCharacter `manyTill` try (string $ delimiterChange <> end)
-  oldState ← getState
-  putState $ oldState { delimiters = (delim1, delim2) }
+flushText ∷ Parser AST
+flushText = do
+  s@(MustacheState { textStack = text }) ← getState
+  putState $ s { textStack = (∅) }
+  return $ if T.null text
+              then []
+              else [TextBlock text]
 
 
-parseInvertedSection ∷ MNodeParser
-parseInvertedSection = do
-  sectionName ← genParseTag invertedSectionBegin mempty
-  MustacheInvertedSection sectionName <$> parseText (return sectionName)
+finishFile ∷ Parser AST
+finishFile =
+  getState ≫= \case
+    (MustacheState {currentSectionName = Nothing}) → flushText
+    (MustacheState {currentSectionName = Just name}) →
+      parserFail $ "Unclosed section " ⊕ show name
 
 
-parseUnescapedVar ∷ MNodeParser
-parseUnescapedVar = MustacheVariable False <$>
-  (try (uncurry genParseTag unescape2) <|> genParseTag unescape1 mempty)
+parseLine ∷ Parser AST
+parseLine = do
+  (MustacheState { sDelimiters = ( start, _ ) }) ← getState
+  initialWhitespace ← many (oneOf " \t")
+  let handleStandalone = do
+        tag ← switchOnTag
+        let continueNoStandalone = do
+              appendTextStack initialWhitespace
+              modifyState (\s → s { isBeginngingOfLine = False })
+              continueFromTag tag
+            standaloneEnding = do
+              try (skipMany (oneOf " \t") ≫ (eof <|> void endOfLine))
+              modifyState (\s → s { isBeginngingOfLine = True })
+        case tag of
+          Tag (Partial _ name) →
+            ( standaloneEnding ≫
+              continueFromTag (Tag (Partial (Just (pack initialWhitespace)) name))
+            ) <|> continueNoStandalone
+          Tag _ → continueNoStandalone
+          _     →
+            ( standaloneEnding ≫
+              continueFromTag tag
+            ) <|> continueNoStandalone
+  (try (string start) ≫ handleStandalone)
+    <|> (try eof ≫ appendTextStack initialWhitespace ≫ finishFile)
+    <|> (appendTextStack initialWhitespace ≫ modifyState (\s → s { isBeginngingOfLine = False }) ≫ continueLine)
 
 
-parseVariable ∷ MNodeParser
-parseVariable = MustacheVariable True <$> genParseTag mempty mempty
+continueFromTag ∷ ParseTagRes → Parser AST
+continueFromTag (SectionBegin inverted name) = do
+  textNodes ← flushText
+  state@(MustacheState
+    { currentSectionName = previousSection }) ← getState
+  putState $ state { currentSectionName = return name }
+  innerSectionContent ← parseText
+  let sectionTag =
+        if inverted
+          then InvertedSection
+          else Section
+  modifyState $ \s → s { currentSectionName = previousSection }
+  outerSectionContent ← parseText
+  return (textNodes ⊕ [sectionTag name innerSectionContent] ⊕ outerSectionContent)
+continueFromTag (SectionEnd name) = do
+  (MustacheState
+    { currentSectionName }) ← getState
+  case currentSectionName of
+    Just name' | name' ≡ name → flushText
+    Just name' → parserFail $ "Expected closing sequence for \"" ⊕ show name ⊕ "\" got \"" ⊕ show name' ⊕ "\"."
+    Nothing → parserFail $ "Encountered closing sequence for \"" ⊕ show name ⊕ "\" which has never been opened."
+continueFromTag (Tag tag) = do
+  textNodes    ← flushText
+  furtherNodes ← parseText
+  return $ textNodes ⊕ return tag ⊕ furtherNodes
+continueFromTag HandledTag = parseText
 
 
-parseEnd ∷ Maybe [Text] -> MustacheParser ()
-parseEnd tagName = do
-  tag ← genParseTag sectionEnd mempty
-  unless (isSameSection tag) $
-    parserFail $
-      maybe
-        (unexpectedSection tag)
-        (`unexpectedClosingSequence` tag)
-        tagName
+switchOnTag ∷ Parser ParseTagRes
+switchOnTag = do
+  (MustacheState { sDelimiters = ( _, end )}) ← getState
+
+  choice
+    [ SectionBegin False <$> (try (char sectionBegin) ≫ genParseTagEnd (∅))
+    , SectionEnd
+        <$> (try (char sectionEnd) ≫ genParseTagEnd (∅))
+    , Tag ∘ Variable False
+        <$> (try (char unescape1) ≫ genParseTagEnd (∅))
+    , Tag ∘ Variable False
+        <$> (try (char (fst unescape2)) ≫ genParseTagEnd (return $ snd unescape2))
+    , Tag ∘ Partial Nothing
+        <$> (try (char partialBegin) ≫ spaces ≫ (noneOf (nub end) `manyTill` try (spaces ≫ string end)))
+    , return HandledTag
+        << (try (char delimiterChange) ≫ parseDelimChange)
+    , SectionBegin True
+        <$> (try (char invertedSectionBegin) ≫ genParseTagEnd (∅) ≫= \case
+              n@(NamedData _) → return n
+              _ → parserFail "Inverted Sections can not be implicit."
+            )
+    , return HandledTag << (try (char comment) ≫ manyTill anyChar (try $ string end))
+    , Tag . Variable True
+        <$> genParseTagEnd (∅)
+    ]
   where
-    isSameSection = maybe (const True) (==) tagName
+    parseDelimChange = do
+      (MustacheState { sDelimiters = ( _, end )}) ← getState
+      spaces
+      delim1 ← allowedDelimiterCharacter `manyTill` space
+      spaces
+      delim2 ← allowedDelimiterCharacter `manyTill` try (spaces ≫ string (delimiterChange : end))
+      when (delim1 ≡ (∅) ∨ delim2 ≡ (∅))
+        $ parserFail "Tags must contain more than 0 characters"
+      oldState ← getState
+      putState $ oldState { sDelimiters = (delim1, delim2) }
 
 
-genParseTag ∷ String → String → MustacheParser [Text]
-genParseTag smod emod = do
-  (MustacheConf { delimiters = ( start, end ) }) <- getState
+genParseTagEnd ∷ String → Parser DataIdentifier
+genParseTagEnd emod = do
+  (MustacheState { sDelimiters = ( start, end ) }) ← getState
 
-  let nStart = start <> smod
-      nEnd = emod <> end
-      disallowed = nub $ nestingSeparator <> start <> end
+  let nEnd = emod ⊕ end
+      disallowed = nub $ nestingSeparator : start ⊕ end
 
-      parseOne :: MustacheParser [Text]
+      parseOne :: Parser [Text]
       parseOne = do
-        spaces
 
         one ← noneOf disallowed
           `manyTill` lookAhead
-            (try (spaces >> void (string nEnd))
-            <|> try (void $ string nestingSeparator))
+            (try (spaces ≫ void (string nEnd))
+            <|> try (void $ char nestingSeparator))
 
-        others ← (string nestingSeparator >> parseOne)
-                  <|> (const mempty <$> (spaces >> string nEnd))
+        others ← (char nestingSeparator ≫ parseOne)
+                  <|> (const (∅) <$> (spaces ≫ string nEnd))
         return $ pack one : others
-
-  void $ try $ string nStart
-  parseOne
-
-
--- ERRORS
-
-sectionToString ∷ [Text] → String
-sectionToString = unpack . intercalate "."
-
-unexpectedSection ∷ [Text] → String
-unexpectedSection s = "No such section '" <> sectionToString s <> "'"
-unexpectedClosingSequence ∷ [Text] → [Text] → String
-unexpectedClosingSequence tag1 tag2 =
-  "Expected closing sequence for section '"
-  <> sectionToString tag1
-  <> "' got '"
-  <> sectionToString tag2
-  <> "'"
+  spaces
+  (try (char implicitIterator) ≫ spaces ≫ string nEnd ≫ return Implicit)
+    <|> (NamedData <$> parseOne)
diff --git a/src/lib/Text/Mustache/Render.hs b/src/lib/Text/Mustache/Render.hs
--- a/src/lib/Text/Mustache/Render.hs
+++ b/src/lib/Text/Mustache/Render.hs
@@ -19,19 +19,24 @@
   , toString
   ) where
 
---
-import           Control.Applicative    ((<|>), (<$>))
-import           Data.Foldable          (fold, find)
+
+import           Control.Applicative    ((<$>), (<|>))
+import           Control.Arrow          (first)
+import           Control.Monad
+import           Control.Monad.Unicode
+import           Data.Foldable          (fold)
 import           Data.HashMap.Strict    as HM hiding (map)
-import           Data.Monoid            (mempty, (<>))
-import           Data.Text              hiding (concat, find, map, uncons)
-import qualified Data.Text              as T
-import           Data.Traversable       (traverse)
+import           Data.Maybe             (fromMaybe)
+import           Data.Monoid.Unicode
+import           Data.Scientific        (floatingOrInteger)
+import           Data.Text              as T (Text, isSuffixOf, null, pack,
+                                             replace, stripSuffix)
 import qualified Data.Vector            as V
+import           Prelude                hiding (length, lines, unlines)
+import           Prelude.Unicode
 import           Text.HTML.TagSoup      (escapeHTML)
 import           Text.Mustache.Internal
 import           Text.Mustache.Types
-import           Text.Printf
 
 
 {-|
@@ -40,94 +45,134 @@
 
   Equivalent to @substituteValue . toMustache@.
 -}
-substitute ∷ ToMustache j ⇒ MustacheTemplate → j → Either String Text
-substitute t = substituteValue t . toMustache
+substitute ∷ ToMustache j ⇒ Template → j → Text
+substitute t = substituteValue t ∘ toMustache
 
 
 {-|
   Substitutes all mustache defined tokens (or tags) for values found in the
   provided data structure.
 -}
-substituteValue ∷ MustacheTemplate → Value → Either String Text
-substituteValue (MustacheTemplate { ast = cAst, partials = cPartials }) dataStruct =
-  joinSubstituted (substitute' (Context mempty dataStruct)) cAst
+substituteValue ∷ Template → Value → Text
+substituteValue (Template { ast = cAst, partials = cPartials }) dataStruct =
+  joinSubstituted (substitute' (Context (∅) dataStruct)) cAst
   where
-    joinSubstituted f = fmap fold . traverse f
+    joinSubstituted f = fold ∘ fmap f
 
     -- Main substitution function
-    substitute' ∷ Context Value → MustacheNode Text → Either String Text
+    substitute' ∷ Context Value → Node Text → Text
 
     -- subtituting text
-    substitute' _ (MustacheText t) = return t
+    substitute' _ (TextBlock t) = t
 
     -- substituting a whole section (entails a focus shift)
-    substitute' context@(Context parents focus) (MustacheSection secName secAST) =
+    substitute' (Context parents focus@(Array a)) (Section Implicit secAST)
+      | V.null a  = (∅)
+      | otherwise = flip joinSubstituted a $ \focus' →
+        let
+          newContext = Context (focus:parents) focus'
+        in
+          joinSubstituted (substitute' newContext) secAST
+    substitute' context@(Context _ (Object _)) (Section Implicit secAST) =
+      joinSubstituted (substitute' context) secAST
+    substitute' _ (Section Implicit _) = (∅)
+    substitute' context@(Context parents focus) (Section (NamedData secName) secAST) =
       case search context secName of
         Just arr@(Array arrCont) →
           if V.null arrCont
-            then return mempty
+            then (∅)
             else flip joinSubstituted arrCont $ \focus' →
               let
                 newContext = Context (arr:focus:parents) focus'
               in
                 joinSubstituted (substitute' newContext) secAST
-        Just (Bool b) | not b → return mempty
-        Just (Lambda l) → l context secAST >>= joinSubstituted (substitute' context)
-        Just focus' →
+        Just (Bool False) → (∅)
+        Just (Lambda l)   → joinSubstituted (substitute' context) (l context secAST)
+        Just focus'       →
           let
             newContext = Context (focus:parents) focus'
           in
             joinSubstituted (substitute' newContext) secAST
-        Nothing → return mempty
+        Nothing → (∅)
 
     -- substituting an inverted section
-    substitute' context (MustacheInvertedSection invSecName invSecAST) =
-      case search context invSecName of
-        Just (Bool False) → contents
+    substitute' _       (InvertedSection  Implicit           _        ) = (∅)
+    substitute' context (InvertedSection (NamedData secName) invSecAST) =
+      case search context secName of
+        Just (Bool False)         → contents
         Just (Array a) | V.null a → contents
-        Nothing → contents
-        _ → return mempty
+        Nothing                   → contents
+        _                         → (∅)
       where
         contents = joinSubstituted (substitute' context) invSecAST
 
     -- substituting a variable
-    substitute' context (MustacheVariable escaped varName) =
-      return $ maybe
-        mempty
+    substitute' (Context _ current) (Variable _ Implicit) = toString current
+    substitute' context (Variable escaped (NamedData varName)) =
+      maybe
+        (∅)
         (if escaped then escapeHTML else id)
         $ toString <$> search context varName
 
     -- substituting a partial
-    substitute' context (MustachePartial pName) =
+    substitute' context (Partial indent pName) =
       maybe
-        (Left $ printf "Could not find partial '%s'" pName)
-        (joinSubstituted (substitute' context) . ast)
-        $ find ((== pName) . name) cPartials
+        (∅)
+        (joinSubstituted (substitute' context) ∘ handleIndent indent ∘ ast)
+        $ HM.lookup pName cPartials
 
 
-search ∷ Context Value → [T.Text] → Maybe Value
+handleIndent ∷ Maybe Text → AST → AST
+handleIndent Nothing ast' = ast'
+handleIndent (Just indentation) ast' = preface ⊕ content
+  where
+    preface = if T.null indentation then [] else [TextBlock indentation]
+    content = if T.null indentation
+      then ast'
+      else
+        let
+          fullIndented = fmap (indentBy indentation) ast'
+          dropper (TextBlock t) = TextBlock $
+            if ("\n" ⊕ indentation) `isSuffixOf` t
+              then fromMaybe t $ stripSuffix indentation t
+              else t
+          dropper a = a
+        in
+          reverse $ fromMaybe [] (uncurry (:) ∘ first dropper <$> uncons (reverse fullIndented))
+
+
+search ∷ Context Value → [Text] → Maybe Value
 search _ [] = Nothing
 search (Context parents focus) val@(x:xs) =
   (
     ( case focus of
       (Object o) → HM.lookup x o
-      _ → Nothing
+      _          → Nothing
     )
-    <|> (do
-          (newFocus, newParents) <- uncons parents
+    <|> ( do
+          (newFocus, newParents) ← uncons parents
           search (Context newParents newFocus) val
         )
   )
-    >>= innerSearch xs
+    ≫= innerSearch xs
 
 
+indentBy ∷ Text → Node Text → Node Text
+indentBy indent p@(Partial (Just indent') name')
+  | T.null indent = p
+  | otherwise = Partial (Just (indent ⊕ indent')) name'
+indentBy indent (Partial Nothing name') = Partial (Just indent) name'
+indentBy indent (TextBlock t) = TextBlock $ replace "\n" ("\n" ⊕ indent) t
+indentBy _ a = a
 
-innerSearch ∷ [T.Text] → Value → Maybe Value
-innerSearch [] v = Just v
-innerSearch (y:ys) (Object o) = HM.lookup y o >>= innerSearch ys
-innerSearch _ _ = Nothing
 
+innerSearch ∷ [Text] → Value → Maybe Value
+innerSearch []     v          = Just v
+innerSearch (y:ys) (Object o) = HM.lookup y o ≫= innerSearch ys
+innerSearch _      _          = Nothing
 
+
 toString ∷ Value → Text
 toString (String t) = t
-toString e = pack $ show e
+toString (Number n) = either (pack ∘ show) (pack ∘ show) (floatingOrInteger n ∷ Either Double Integer)
+toString e          = pack $ show e
diff --git a/src/lib/Text/Mustache/Types.hs b/src/lib/Text/Mustache/Types.hs
--- a/src/lib/Text/Mustache/Types.hs
+++ b/src/lib/Text/Mustache/Types.hs
@@ -7,96 +7,106 @@
 Stability   : experimental
 Portability : POSIX
 -}
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE TupleSections     #-}
-{-# LANGUAGE UnicodeSyntax     #-}
-{-# LANGUAGE FlexibleContexts  #-}
+{-# LANGUAGE FlexibleContexts      #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE TupleSections         #-}
+{-# LANGUAGE UnicodeSyntax         #-}
 module Text.Mustache.Types
   (
   -- * Types for the Parser / Template
-    MustacheAST
-  , MustacheTemplate(..)
-  , MustacheNode(..)
+    AST
+  , Template(..)
+  , Node(..)
+  , DataIdentifier(..)
   -- * Types for the Substitution / Data
   , Value(..)
   -- ** Converting
   , object
-  , (~>), (~=), (~~>), (~~=)
-  , ToMustache, toMustache, toMustacheText, mFromJSON
+  , (~>), (↝), (~=), (⥱), (~~>), (~↝), (~~=), (~⥱)
+  , ToMustache, toMustache, toTextBlock, mFromJSON
   -- ** Representation
   , Array, Object
   , Context(..)
   ) where
 
 
+import           Conversion
+import           Conversion.Text     ()
 import qualified Data.Aeson          as Aeson
-import           Data.Functor        ((<$>))
 import           Data.HashMap.Strict as HM
 import           Data.Scientific
 import           Data.Text
 import qualified Data.Text.Lazy      as LT
 import qualified Data.Vector         as V
-import           Conversion
-import           Conversion.Text ()
-
+import           Prelude.Unicode
 
 -- | Abstract syntax tree for a mustache template
-type MustacheAST = [MustacheNode Text]
+type AST = [Node Text]
 
 
 -- | Basic values composing the AST
-data MustacheNode a
-  = MustacheText a
-  | MustacheSection [Text] MustacheAST
-  | MustacheInvertedSection [Text] MustacheAST
-  | MustacheVariable Bool [Text]
-  | MustachePartial FilePath
+data Node α
+  = TextBlock α
+  | Section DataIdentifier AST
+  | InvertedSection DataIdentifier AST
+  | Variable Bool DataIdentifier
+  | Partial (Maybe α) FilePath
   deriving (Show, Eq)
 
 
-type Array = V.Vector Value
+data DataIdentifier
+  = NamedData [Text]
+  | Implicit
+  deriving (Show, Eq)
+
+
+type Array  = V.Vector Value
 type Object = HM.HashMap Text Value
-type KeyValuePair = (Text, Value)
+type Pair   = (Text, Value)
 
 
 -- | Representation of stateful context for the substitution process
-data Context a = Context [a] a
+data Context α = Context [α] α
 
 
 -- | Internal value AST
 data Value
   = Object Object
-  | Array Array
+  | Array  Array
   | Number Scientific
   | String Text
-  | Lambda (Context Value → MustacheAST → Either String MustacheAST)
-  | Bool Bool
+  | Lambda (Context Value → AST → AST)
+  | Bool   Bool
   | Null
 
 
 instance Show Value where
-  show (Lambda _)  = "Lambda Context Value → MustacheAST → Either String MustacheAST"
-  show (Object o)  = show o
-  show (Array a)   = show a
-  show (String s)  = show s
-  show (Number n)  = show n
-  show (Bool b)    = show b
-  show Null        = "null"
+  show (Lambda _) = "Lambda Context Value → AST → Either String AST"
+  show (Object o) = show o
+  show (Array  a) = show a
+  show (String s) = show s
+  show (Number n) = show n
+  show (Bool   b) = show b
+  show Null       = "null"
 
 
 -- | Conversion class
-class ToMustache a where
-  toMustache ∷ a → Value
+class ToMustache ω where
+  toMustache ∷ ω → Value
 
 
+instance ToMustache Value where
+  toMustache = id
+
 instance ToMustache [Char] where
-  toMustache = String . pack
+  toMustache = String ∘ pack
 
 instance ToMustache Bool where
   toMustache = Bool
 
 instance ToMustache Char where
-  toMustache = String . pack . return
+  toMustache = String ∘ pack ∘ return
 
 instance ToMustache () where
   toMustache = const Null
@@ -105,57 +115,40 @@
   toMustache = String
 
 instance ToMustache LT.Text where
-  toMustache = String . LT.toStrict
+  toMustache = String ∘ LT.toStrict
 
 instance ToMustache Scientific where
   toMustache = Number
 
-instance ToMustache Value where
-  toMustache = id
-
-instance ToMustache m ⇒ ToMustache [m] where
-  toMustache = Array . V.fromList . fmap toMustache
+instance ToMustache ω ⇒ ToMustache [ω] where
+  toMustache = Array ∘ V.fromList ∘ fmap toMustache
 
-instance ToMustache m ⇒ ToMustache (V.Vector m) where
-  toMustache = Array . fmap toMustache
+instance ToMustache ω ⇒ ToMustache (V.Vector ω) where
+  toMustache = Array ∘ fmap toMustache
 
-instance ToMustache m ⇒ ToMustache (HM.HashMap Text m) where
-  toMustache = Object . fmap toMustache
+instance ToMustache ω ⇒ ToMustache (HM.HashMap Text ω) where
+  toMustache = Object ∘ fmap toMustache
 
-instance ToMustache (Context Value → MustacheAST → Either String MustacheAST) where
+instance ToMustache (Context Value → AST → AST) where
   toMustache = Lambda
 
-instance ToMustache (Context Value → MustacheAST → Either String Text) where
-  toMustache f = Lambda wrapper
-    where wrapper c lAST = return . MustacheText <$> f c lAST
-
-instance ToMustache (Context Value → MustacheAST → MustacheAST) where
-  toMustache f = Lambda wrapper
-    where wrapper c = Right . f c
-
-instance ToMustache (Context Value → MustacheAST → Text) where
+instance ToMustache (Context Value → AST → Text) where
   toMustache f = Lambda wrapper
-    where wrapper c = Right . return . MustacheText . f c
+    where wrapper c lAST = return ∘ TextBlock $ f c lAST
 
-instance ToMustache (Context Value → MustacheAST → String) where
+instance ToMustache (Context Value → AST → String) where
   toMustache f = toMustache wrapper
-    where wrapper c = pack . f c
+    where wrapper c = pack ∘ f c
 
-instance ToMustache (MustacheAST → Either String MustacheAST) where
+instance ToMustache (AST → AST) where
   toMustache f = Lambda $ const f
 
-instance ToMustache (MustacheAST → Either String Text) where
+instance ToMustache (AST → Text) where
   toMustache f = Lambda wrapper
-    where wrapper _ = fmap (return . MustacheText) . f
-
-instance ToMustache (MustacheAST → Either String String) where
-  toMustache f = toMustache (fmap pack . f)
-
-instance ToMustache (MustacheAST → Text) where
-  toMustache f = toMustache (Right . f ∷ MustacheAST -> Either String Text)
+    where wrapper _ = (return ∘ TextBlock) ∘ f
 
-instance ToMustache (MustacheAST → String) where
-  toMustache f = toMustache (pack . f)
+instance ToMustache (AST → String) where
+  toMustache f = toMustache (pack ∘ f)
 
 instance ToMustache Aeson.Value where
   toMustache (Aeson.Object o) = Object $ fmap toMustache o
@@ -163,9 +156,92 @@
   toMustache (Aeson.Number n) = Number n
   toMustache (Aeson.String s) = String s
   toMustache (Aeson.Bool   b) = Bool b
-  toMustache (Aeson.Null)     = Null
+  toMustache Aeson.Null       = Null
 
+instance (ToMustache α, ToMustache β) ⇒ ToMustache (α, β) where
+  toMustache (a, b) = toMustache [toMustache a, toMustache b]
 
+instance (ToMustache α, ToMustache β, ToMustache γ)
+         ⇒ ToMustache (α, β, γ) where
+  toMustache (a, b, c) = toMustache [toMustache a, toMustache b, toMustache c]
+
+instance (ToMustache α, ToMustache β, ToMustache γ, ToMustache δ)
+         ⇒ ToMustache (α, β, γ, δ) where
+  toMustache (a, b, c, d) = toMustache
+    [ toMustache a
+    , toMustache b
+    , toMustache c
+    , toMustache d
+    ]
+
+instance ( ToMustache α
+         , ToMustache β
+         , ToMustache γ
+         , ToMustache δ
+         , ToMustache ε
+         ) ⇒ ToMustache (α, β, γ, δ, ε) where
+  toMustache (a, b, c, d, e) = toMustache
+    [ toMustache a
+    , toMustache b
+    , toMustache c
+    , toMustache d
+    , toMustache e
+    ]
+
+instance ( ToMustache α
+         , ToMustache β
+         , ToMustache γ
+         , ToMustache δ
+         , ToMustache ε
+         , ToMustache ζ
+         ) ⇒ ToMustache (α, β, γ, δ, ε, ζ) where
+  toMustache (a, b, c, d, e, f) = toMustache
+    [ toMustache a
+    , toMustache b
+    , toMustache c
+    , toMustache d
+    , toMustache e
+    , toMustache f
+    ]
+
+instance ( ToMustache α
+         , ToMustache β
+         , ToMustache γ
+         , ToMustache δ
+         , ToMustache ε
+         , ToMustache ζ
+         , ToMustache η
+         ) ⇒ ToMustache (α, β, γ, δ, ε, ζ, η) where
+  toMustache (a, b, c, d, e, f, g) = toMustache
+    [ toMustache a
+    , toMustache b
+    , toMustache c
+    , toMustache d
+    , toMustache e
+    , toMustache f
+    , toMustache g
+    ]
+
+instance ( ToMustache α
+         , ToMustache β
+         , ToMustache γ
+         , ToMustache δ
+         , ToMustache ε
+         , ToMustache ζ
+         , ToMustache η
+         , ToMustache θ
+         ) ⇒ ToMustache (α, β, γ, δ, ε, ζ, η, θ) where
+  toMustache (a, b, c, d, e, f, g, h) = toMustache
+    [ toMustache a
+    , toMustache b
+    , toMustache c
+    , toMustache d
+    , toMustache e
+    , toMustache f
+    , toMustache g
+    , toMustache h
+    ]
+
 -- | Convenience function for creating Object values.
 --
 -- This function is supposed to be used in conjuction with the '~>' and '~=' operators.
@@ -190,48 +266,67 @@
 -- Here we can see that we can use the '~>' operator for values that have themselves
 -- a 'ToMustache' instance, or alternatively if they lack such an instance but provide
 -- an instance for the 'ToJSON' typeclass we can use the '~=' operator.
-object ∷ [(Text, Value)] → Value
-object = Object . HM.fromList
+object ∷ [Pair] → Value
+object = Object ∘ HM.fromList
 
 
 -- | Map keys to values that provide a 'ToMustache' instance
 --
 -- Recommended in conjunction with the `OverloadedStrings` extension.
-(~>) ∷ ToMustache m ⇒ Text → m → KeyValuePair
-(~>) t = (t, ) . toMustache
+(~>) ∷ ToMustache ω ⇒ Text → ω → Pair
+(~>) t = (t, ) ∘ toMustache
 
 
+-- | Unicode version of '~>'
+(↝) ∷ ToMustache ω ⇒ Text → ω → Pair
+(↝) = (~>)
+
+
 -- | Map keys to values that provide a 'ToJSON' instance
 --
 -- Recommended in conjunction with the `OverloadedStrings` extension.
-(~=) ∷ Aeson.ToJSON j ⇒ Text → j → KeyValuePair
-(~=) t = (t ~>) . Aeson.toJSON
+(~=) ∷ Aeson.ToJSON ι ⇒ Text → ι → Pair
+(~=) t = (t ~>) ∘ Aeson.toJSON
 
 
+-- | Unicode version of '~='
+(⥱) ∷ Aeson.ToJSON ι ⇒ Text → ι → Pair
+(⥱) = (~=)
+
 -- | Conceptually similar to '~>' but uses arbitrary String-likes as keys.
-(~~>) ∷ (Conversion t Text, ToMustache m) ⇒ t → m → KeyValuePair
-(~~>) = (~>) . convert
+(~~>) ∷ (Conversion ζ Text, ToMustache ω) ⇒ ζ → ω → Pair
+(~~>) = (~>) ∘ convert
 
 
+-- | Unicde version of '~~>'
+(~↝) ∷ (Conversion ζ Text, ToMustache ω) ⇒ ζ → ω → Pair
+(~↝) = (~~>)
+
 -- | Conceptually similar to '~=' but uses arbitrary String-likes as keys.
-(~~=) ∷ (Conversion t Text, Aeson.ToJSON j) ⇒ t → j → KeyValuePair
-(~~=) = (~=) . convert
+(~~=) ∷ (Conversion ζ Text, Aeson.ToJSON ι) ⇒ ζ → ι → Pair
+(~~=) = (~=) ∘ convert
 
 
+-- | Unicode version of '~~='
+(~⥱) ∷ (Conversion ζ Text, Aeson.ToJSON ι) ⇒ ζ → ι → Pair
+(~⥱) = (~~=)
+
+
 -- | Converts arbitrary String-likes to Values
-toMustacheText ∷ Conversion t Text ⇒ t → Value
-toMustacheText = String . convert
+toTextBlock ∷ Conversion ζ Text ⇒ ζ → Value
+toTextBlock = String ∘ convert
 
 
 -- | Converts a value that can be represented as JSON to a Value.
-mFromJSON ∷ Aeson.ToJSON j ⇒ j → Value
-mFromJSON = toMustache . Aeson.toJSON
+mFromJSON ∷ Aeson.ToJSON ι ⇒ ι → Value
+mFromJSON = toMustache ∘ Aeson.toJSON
 
 
 {-|
   A compiled Template with metadata.
 -}
-data MustacheTemplate = MustacheTemplate { name     ∷ String
-                                         , ast      ∷ MustacheAST
-                                         , partials ∷ [MustacheTemplate]
-                                         } deriving (Show)
+data Template = Template
+  { name     ∷ String
+  , ast      ∷ AST
+  , partials ∷ HashMap String Template
+  } deriving (Show)
diff --git a/test/unit/Spec.hs b/test/unit/Spec.hs
--- a/test/unit/Spec.hs
+++ b/test/unit/Spec.hs
@@ -1,16 +1,77 @@
+{-# LANGUAGE LambdaCase        #-}
+{-# LANGUAGE NamedFieldPuns    #-}
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards   #-}
+{-# LANGUAGE UnicodeSyntax     #-}
 module Main where
 
 
+import           Control.Applicative  ((<$>), (<*>))
+import           Control.Monad
 import           Data.Either
-import           Data.Monoid          (mempty)
+import           Data.Foldable        (for_)
+import qualified Data.HashMap.Strict  as HM (HashMap, elems, empty, lookup,
+                                             traverseWithKey)
+import           Data.List
+import           Data.Monoid          (mempty, (<>))
 import qualified Data.Text            as T
+import           Data.Yaml            as Y (FromJSON, Value (..), decodeFile,
+                                            parseJSON, (.!=), (.:), (.:?))
+import           Debug.Trace          (traceShowId)
+import           System.Directory
+import           System.FilePath
+import           System.IO.Temp
+import           System.Process
 import           Test.Hspec
 import           Text.Mustache
-import           Text.Mustache.Types
 import           Text.Mustache.Parser
+import           Text.Mustache.Types
 
 
+langspecDir = "spec-1.1.3"
+specDir = "specs"
+releaseFile = "langspec.tar.gz"
+releaseURL = "https://codeload.github.com/mustache/spec/tar.gz/v1.1.3"
+
+
+data LangSpecFile = LangSpecFile
+  { overview :: String
+  , tests    :: [LangSpecTest]
+  }
+
+
+data LangSpecTest = LangSpecTest
+  { name            :: String
+  , specDescription :: String
+  , specData        :: Y.Value
+  , template        :: T.Text
+  , expected        :: T.Text
+  , testPartials    :: HM.HashMap String T.Text
+  }
+
+
+instance FromJSON LangSpecFile where
+  parseJSON (Y.Object o) = LangSpecFile
+    <$> o .: "overview"
+    <*> o .: "tests"
+  parseJSON _ = mzero
+
+
+instance FromJSON LangSpecTest where
+  parseJSON (Y.Object o) = LangSpecTest
+    <$> o .: "name"
+    <*> o .: "desc"
+    <*> o .: "data"
+    <*> o .: "template"
+    <*> o .: "expected"
+    <*> o .:? "partials" .!= HM.empty
+  parseJSON _ = mzero
+
+
+(&) ∷ a → (a → b) → b
+(&) = flip ($)
+
+
 parserSpec :: Spec
 parserSpec =
   describe "mustacheParser" $ do
@@ -20,162 +81,208 @@
     let text = "test12356p0--=-34{}jnv,\n"
 
     it "parses text" $
-      lparse text `shouldBe` returnedOne (MustacheText text)
+      lparse text `shouldBe` returnedOne (TextBlock text)
 
     it "parses a variable" $
-      lparse "{{name}}" `shouldBe` returnedOne (MustacheVariable True ["name"])
+      lparse "{{name}}" `shouldBe` returnedOne (Variable True (NamedData ["name"]))
 
     it "parses a variable with whitespace" $
-      lparse "{{ name  }}" `shouldBe` returnedOne (MustacheVariable True ["name"])
+      lparse "{{ name  }}" `shouldBe` returnedOne (Variable True (NamedData ["name"]))
 
     it "allows '-' in variable names" $
       lparse "{{ name-name }}" `shouldBe`
-        returnedOne (MustacheVariable True ["name-name"])
+        returnedOne (Variable True (NamedData ["name-name"]))
 
     it "allows '_' in variable names" $
       lparse "{{ name_name }}" `shouldBe`
-        returnedOne (MustacheVariable True ["name_name"])
+        returnedOne (Variable True (NamedData ["name_name"]))
 
     it "parses a variable unescaped with {{{}}}" $
-      lparse "{{{name}}}" `shouldBe` returnedOne (MustacheVariable False ["name"])
+      lparse "{{{name}}}" `shouldBe` returnedOne (Variable False (NamedData ["name"]))
 
     it "parses a variable unescaped with {{{}}} with whitespace" $
       lparse "{{{  name     }}}" `shouldBe`
-        returnedOne (MustacheVariable False ["name"])
+        returnedOne (Variable False (NamedData ["name"]))
 
     it "parses a variable unescaped with &" $
-      lparse "{{&name}}" `shouldBe` returnedOne (MustacheVariable False ["name"])
+      lparse "{{&name}}" `shouldBe` returnedOne (Variable False (NamedData ["name"]))
 
     it "parses a variable unescaped with & with whitespace" $
       lparse "{{&  name  }}" `shouldBe`
-        returnedOne (MustacheVariable False ["name"])
+        returnedOne (Variable False (NamedData ["name"]))
 
     it "parses a partial" $
       lparse "{{>myPartial}}" `shouldBe`
-        returnedOne (MustachePartial "myPartial")
+        returnedOne (Partial (Just "") "myPartial")
 
     it "parses a partial with whitespace" $
       lparse "{{>  myPartial }}" `shouldBe`
-        returnedOne (MustachePartial "myPartial")
+        returnedOne (Partial (Just "") "myPartial")
 
     it "parses the an empty section" $
       lparse "{{#section}}{{/section}}" `shouldBe`
-        returnedOne (MustacheSection ["section"] mempty)
+        returnedOne (Section (NamedData ["section"]) mempty)
 
     it "parses the an empty section with whitespace" $
       lparse "{{#   section }}{{/     section }}" `shouldBe`
-        returnedOne (MustacheSection ["section"] mempty)
+        returnedOne (Section (NamedData ["section"]) mempty)
 
     it "parses a delimiter change" $
       lparse "{{=<< >>=}}<<var>>{{var}}" `shouldBe`
-        return [MustacheVariable True ["var"], MustacheText "{{var}}"]
+        return [Variable True (NamedData ["var"]), TextBlock "{{var}}"]
 
     it "parses a delimiter change with whitespace" $
       lparse "{{=<<   >>=}}<< var   >>{{var}}" `shouldBe`
-        return [MustacheVariable True ["var"], MustacheText "{{var}}"]
+        return [Variable True (NamedData ["var"]), TextBlock "{{var}}"]
 
     it "parses two subsequent delimiter changes" $
       lparse "{{=((  ))=}}(( var ))((=--  $-=))--#section$---/section$-" `shouldBe`
-        return [MustacheVariable True ["var"], MustacheSection ["section"] []]
+        return [Variable True (NamedData ["var"]), Section (NamedData ["section"]) []]
 
     it "propagates a delimiter change from a nested scope" $
       lparse "{{#section}}{{=<< >>=}}<</section>><<var>>" `shouldBe`
-        return [MustacheSection ["section"] [], MustacheVariable True ["var"]]
+        return [Section (NamedData ["section"]) [], Variable True (NamedData ["var"])]
 
     it "fails if the tag contains illegal characters" $
       lparse "{{#&}}" `shouldSatisfy` isLeft
 
     it "parses a nested variable" $
-      lparse "{{ name.val }}" `shouldBe` returnedOne (MustacheVariable True ["name", "val"])
+      lparse "{{ name.val }}" `shouldBe` returnedOne (Variable True (NamedData ["name", "val"]))
 
     it "parses a variable containing whitespace" $
-      lparse "{{ val space }}" `shouldBe` returnedOne (MustacheVariable True ["val space"])
+      lparse "{{ val space }}" `shouldBe` returnedOne (Variable True (NamedData ["val space"]))
 
 
 substituteSpec :: Spec
 substituteSpec =
   describe "substitute" $ do
 
-    let toTemplate ast' = MustacheTemplate "testsuite" ast' []
+    let toTemplate ast' = Template "testsuite" ast' mempty
 
     it "substitutes a html escaped value for a variable" $
       substitute
-        (toTemplate [MustacheVariable True ["name"]])
+        (toTemplate [Variable True (NamedData ["name"])])
         (object ["name" ~> ("<tag>" :: T.Text)])
-      `shouldBe` return "&lt;tag&gt;"
+      `shouldBe` "&lt;tag&gt;"
 
     it "substitutes raw value for an unescaped variable" $
       substitute
-        (toTemplate [MustacheVariable False ["name"]])
+        (toTemplate [Variable False (NamedData ["name"])])
         (object ["name" ~> ("<tag>" :: T.Text)])
-      `shouldBe` return "<tag>"
+      `shouldBe` "<tag>"
 
     it "substitutes a section when the key is present (and an empty object)" $
       substitute
-        (toTemplate [MustacheSection ["section"] [MustacheText "t"]])
+        (toTemplate [Section (NamedData ["section"]) [TextBlock "t"]])
         (object ["section" ~> object []])
-      `shouldBe` return "t"
+      `shouldBe` "t"
 
     it "substitutes a section when the key is present (and 'true')" $
       substitute
-        (toTemplate [MustacheSection ["section"] [MustacheText "t"]])
+        (toTemplate [Section (NamedData ["section"]) [TextBlock "t"]])
         (object ["section" ~> True])
-      `shouldBe` return "t"
+      `shouldBe` "t"
 
     it "substitutes a section once when the key is present and a singleton list" $
       substitute
-        (toTemplate [MustacheSection ["section"] [MustacheText "t"]])
+        (toTemplate [Section (NamedData ["section"]) [TextBlock "t"]])
         (object ["section" ~> ["True" :: T.Text]])
-      `shouldBe` return "t"
+      `shouldBe` "t"
 
     it "substitutes a section twice when the key is present and a list with two items" $
       substitute
-        (toTemplate [MustacheSection ["section"] [MustacheText "t"]])
+        (toTemplate [Section (NamedData ["section"]) [TextBlock "t"]])
         (object ["section" ~> (["True", "False"] :: [T.Text])])
-      `shouldBe` return "tt"
+      `shouldBe` "tt"
 
     it "substitutes a section twice when the key is present and a list with two\
     \ objects, changing the scope to each object" $
       substitute
-        (toTemplate [MustacheSection ["section"] [MustacheVariable True ["t"]]])
+        (toTemplate [Section (NamedData ["section"]) [Variable True (NamedData ["t"])]])
         (object
           [ "section" ~>
             [ object ["t" ~> ("var1" :: T.Text)]
             , object ["t" ~> ("var2" :: T.Text)]
             ]
           ])
-      `shouldBe` return "var1var2"
+      `shouldBe` "var1var2"
 
     it "does not substitute a section when the key is not present" $
       substitute
-        (toTemplate [MustacheSection ["section"] [MustacheText "t"]])
+        (toTemplate [Section (NamedData ["section"]) [TextBlock "t"]])
         (object [])
-      `shouldBe` return ""
+      `shouldBe` ""
 
     it "does not substitute a section when the key is present (and 'false')" $
       substitute
-        (toTemplate [MustacheSection ["section"] [MustacheText "t"]])
+        (toTemplate [Section (NamedData ["section"]) [TextBlock "t"]])
         (object ["section" ~> False])
-      `shouldBe` return ""
+      `shouldBe` ""
 
     it "does not substitute a section when the key is present (and empty list)" $
       substitute
-        (toTemplate [MustacheSection ["section"] [MustacheText "t"]])
+        (toTemplate [Section (NamedData ["section"]) [TextBlock "t"]])
         (object ["section" ~> ([] :: [T.Text])])
-      `shouldBe` return ""
+      `shouldBe` ""
 
     it "substitutes a nested section" $
       substitute
-        (toTemplate [MustacheVariable True ["outer", "inner"]])
+        (toTemplate [Variable True (NamedData ["outer", "inner"])])
         (object
           [ "outer" ~> object ["inner" ~> ("success" :: T.Text)]
           , "inner" ~> ("error" :: T.Text)
           ]
         )
-        `shouldBe` return "success"
+        `shouldBe` "success"
 
 
+getOfficialSpecRelease ∷ FilePath → IO ()
+getOfficialSpecRelease tempdir = do
+  currentDirectory ← getCurrentDirectory
+  setCurrentDirectory tempdir
+  createDirectory langspecDir
+  callProcess "curl" [releaseURL, "-o", releaseFile]
+  callProcess "tar" ["-xf", releaseFile]
+  getDirectoryContents "." >>= print
+  setCurrentDirectory currentDirectory
+
+
+testOfficialLangSpec ∷ FilePath → Spec
+testOfficialLangSpec dir = do
+  allFiles ← runIO $ getDirectoryContents dir
+  let testfiles = allFiles
+        & filter ((`elem` [".yml", ".yaml"]) . takeExtension)
+      -- Filters the lambda tests for now.
+        & filter (not . ("~" `isPrefixOf`) . takeFileName)
+  for_ testfiles $ \filename →
+    runIO (decodeFile (dir </> filename)) >>= \case
+      Nothing -> describe ("File: " <> takeFileName filename) $
+        it "loads the data file" $
+          expectationFailure "Data file could not be parsed"
+      Just (LangSpecFile { tests }) →
+        describe ("File: " <> takeFileName filename) $
+          for_ tests $ \(LangSpecTest { .. }) →
+            it ("Name: " <> name <> "  Description: " <> specDescription) $
+              let
+                compiled = do
+                  partials' <- HM.traverseWithKey parseTemplate testPartials
+                  template' <- parseTemplate name template
+                  return $ template' { partials = partials' }
+              in
+                case compiled of
+                  Left m → expectationFailure $ show m
+                  Right tmp →
+                    substituteValue tmp (toMustache specData) `shouldBe` expected
+
+
 main :: IO ()
-main = hspec $ do
-  parserSpec
-  substituteSpec
+main =
+  void $
+    withSystemTempDirectory
+      "mustache-test-resources"
+      $ \tempdir → do
+        getOfficialSpecRelease tempdir
+        hspec $ do
+          parserSpec
+          substituteSpec
+          testOfficialLangSpec (tempdir </> langspecDir </> specDir)
