diff --git a/executables/APITests.hs b/executables/APITests.hs
new file mode 100644
--- /dev/null
+++ b/executables/APITests.hs
@@ -0,0 +1,26 @@
+{-# OPTIONS_GHC -F -pgmF htfpp #-}
+
+import BasePrelude
+import Test.Framework
+import NeatInterpolation
+
+
+main = htfMain $ htf_thisModulesTests
+
+
+test_demo =
+  assertEqual
+    "function(){\n  function(){\n    {\n      indented line\n      indented line\n    }\n  }\n  return {\n    indented line\n    indented line\n  }\n}\n"
+    (template a a)
+  where
+    template a b = 
+      [string|
+        function(){
+          function(){
+            $a
+          }
+          return $b
+        }
+      |]
+    a = "{\n  indented line\n  indented line\n}"
+
diff --git a/library/NeatInterpolation.hs b/library/NeatInterpolation.hs
new file mode 100644
--- /dev/null
+++ b/library/NeatInterpolation.hs
@@ -0,0 +1,123 @@
+{-# OPTIONS_GHC -fno-warn-missing-fields #-} 
+-- | 
+-- NeatInterpolation provides a quasiquoter for producing strings 
+-- with a simple interpolation of input values. 
+-- It removes the excessive indentation from the input and 
+-- accurately manages the indentation of all lines of interpolated variables. 
+-- But enough words, the code shows it better.
+-- 
+-- Consider the following declaration:
+-- 
+-- > {-# LANGUAGE QuasiQuotes, OverloadedStrings #-}
+-- > 
+-- > import NeatInterpolation
+-- > 
+-- > f :: String -> String -> String
+-- > f a b = 
+-- >   [string|
+-- >     function(){
+-- >       function(){
+-- >         $a
+-- >       }
+-- >       return $b
+-- >     }
+-- >   |]
+-- 
+-- Executing the following:
+-- 
+-- > main = putStrLn $ f "1" "2"
+-- 
+-- will produce this (notice the reduced indentation compared to how it was
+-- declared):
+-- 
+-- > function(){
+-- >   function(){
+-- >     1
+-- >   }
+-- >   return 2
+-- > }
+-- 
+-- Now let's test it with multiline string parameters:
+-- 
+-- > main = putStrLn $ f 
+-- >   "{\n  indented line\n  indented line\n}" 
+-- >   "{\n  indented line\n  indented line\n}" 
+--
+-- We get
+--
+-- > function(){
+-- >   function(){
+-- >     {
+-- >       indented line
+-- >       indented line
+-- >     }
+-- >   }
+-- >   return {
+-- >     indented line
+-- >     indented line
+-- >   }
+-- > }
+-- 
+-- See how it neatly preserved the indentation levels of lines the 
+-- variable placeholders were at?  
+module NeatInterpolation (string, indentQQPlaceholder) where
+
+import BasePrelude
+
+import Language.Haskell.TH
+import Language.Haskell.TH.Quote
+
+import NeatInterpolation.String
+import NeatInterpolation.Parsing
+
+
+-- |
+-- The quasiquoter.
+string :: QuasiQuoter
+string = QuasiQuoter {quoteExp = quoteExprExp}
+
+-- |
+-- A function used internally by quasiquoter. Just ignore it.
+indentQQPlaceholder :: Int -> String -> String
+indentQQPlaceholder indent text = case lines text of
+  head:tail -> intercalate "\n" $ head : map (replicate indent ' ' ++) tail
+  [] -> text 
+
+
+quoteExprExp :: String -> Q Exp
+quoteExprExp input = 
+  case parseLines $ normalizeQQInput input of
+    Left e -> fail $ show e
+    Right lines -> appE [|unlines|] $ linesExp lines
+
+linesExp :: [Line] -> Q Exp
+linesExp [] = [|([] :: [String])|]
+linesExp (head : tail) = 
+  (binaryOpE [|(:)|])
+    (lineExp head)
+    (linesExp tail)
+
+lineExp :: Line -> Q Exp
+lineExp (Line indent contents) = 
+  msumExps $ map (contentExp $ fromIntegral indent) contents
+
+
+
+contentExp :: Integer -> LineContent -> Q Exp
+contentExp _ (LineContentText text) = stringE text
+contentExp indent (LineContentIdentifier name) = do
+  valueName <- lookupValueName name
+  case valueName of
+    Just valueName -> do
+      Just indentQQPlaceholderName <- lookupValueName "indentQQPlaceholder"
+      appE
+        (appE (varE indentQQPlaceholderName) $ litE $ integerL indent)
+        (varE valueName)
+    Nothing -> fail $ "Value `" ++ name ++ "` is not in scope"
+
+msumExps :: [Q Exp] -> Q Exp
+msumExps = foldr (binaryOpE mappendE) memptyE
+memptyE = [|mempty|]
+mappendE = [|mappend|]
+
+binaryOpE e = \a b -> e `appE` a `appE` b
diff --git a/library/NeatInterpolation/Parsing.hs b/library/NeatInterpolation/Parsing.hs
new file mode 100644
--- /dev/null
+++ b/library/NeatInterpolation/Parsing.hs
@@ -0,0 +1,35 @@
+module NeatInterpolation.Parsing where
+
+import BasePrelude hiding (try, (<|>), many)
+import Text.Parsec hiding (Line)
+
+
+data Line = 
+  Line {lineIndent :: Int, lineContents :: [LineContent]}
+  deriving (Show)
+
+data LineContent = 
+  LineContentText [Char] |
+  LineContentIdentifier [Char]
+  deriving (Show)
+
+
+parseLines :: [Char] -> Either ParseError [Line]
+parseLines = parse lines "NeatInterpolation.Parsing.parseLines"
+  where
+    lines = sepBy line newline <* eof
+    line = Line <$> countIndent <*> many content
+    countIndent = fmap length $ try $ lookAhead $ many $ char ' '
+    content = try identifier <|> contentText
+    identifier = fmap LineContentIdentifier $ 
+      string "$" *> many1 (alphaNum <|> char '\'' <|> char '_')
+    contentText = do
+      text <- manyTill anyChar end
+      if null text
+        then fail "Empty text"
+        else return $ LineContentText $ text
+      where
+        end = 
+          (void $ try $ lookAhead identifier) <|> 
+          (void $ try $ lookAhead newline) <|> 
+          eof
diff --git a/library/NeatInterpolation/String.hs b/library/NeatInterpolation/String.hs
new file mode 100644
--- /dev/null
+++ b/library/NeatInterpolation/String.hs
@@ -0,0 +1,46 @@
+module NeatInterpolation.String where
+
+import BasePrelude
+
+
+normalizeQQInput :: [Char] -> [Char]
+normalizeQQInput = trim . unindent' . tabsToSpaces
+  where
+    unindent' :: [Char] -> [Char]
+    unindent' s =
+      case lines s of
+        head:tail -> 
+          let 
+            unindentedHead = dropWhile (== ' ') head 
+            minimumTailIndent = minimumIndent . unlines $ tail
+            unindentedTail = case minimumTailIndent of
+              Just indent -> map (drop indent) tail
+              Nothing -> tail
+          in unlines $ unindentedHead : unindentedTail
+        [] -> []
+
+trim :: [Char] -> [Char]
+trim = dropWhileRev isSpace . dropWhile isSpace
+
+dropWhileRev :: (a -> Bool) -> [a] -> [a]
+dropWhileRev p = foldr (\x xs -> if p x && null xs then [] else x:xs) []
+
+unindent :: [Char] -> [Char]
+unindent s =
+  case minimumIndent s of
+    Just indent -> unlines . map (drop indent) . lines $ s
+    Nothing -> s
+
+tabsToSpaces :: [Char] -> [Char]
+tabsToSpaces ('\t':tail) = "    " ++ tabsToSpaces tail
+tabsToSpaces (head:tail) = head : tabsToSpaces tail
+tabsToSpaces [] = []
+
+minimumIndent :: [Char] -> Maybe Int
+minimumIndent = 
+  listToMaybe . sort . map lineIndent 
+    . filter (not . null . dropWhile isSpace) . lines
+
+-- | Amount of preceding spaces on first line
+lineIndent :: [Char] -> Int
+lineIndent = length . takeWhile (== ' ')
diff --git a/neat-interpolation.cabal b/neat-interpolation.cabal
--- a/neat-interpolation.cabal
+++ b/neat-interpolation.cabal
@@ -1,7 +1,7 @@
 name:
   neat-interpolation
 version:
-  0.2.0
+  0.2.1
 synopsis:
   A quasiquoter for neat and simple multiline text interpolation
 description:
@@ -40,94 +40,37 @@
 
 library
   hs-source-dirs:
-    src
+    library
   exposed-modules:
     NeatInterpolation
   other-modules:
-    NeatInterpolation.Prelude
     NeatInterpolation.Parsing
     NeatInterpolation.String
   build-depends:
-    parsec,
-    template-haskell,
-    base >= 4.5 && < 5
+    parsec >= 3 && < 3.2,
+    template-haskell >= 2.8 && < 2.10,
+    base-prelude == 0.1.*,
+    base >= 4.5 && < 4.8
+  ghc-options:
+    -funbox-strict-fields
   default-extensions:
-    NoImplicitPrelude
-    PatternGuards
-    MultiWayIf
-    LambdaCase
-    Arrows
-    MultiParamTypeClasses
-    NoMonomorphismRestriction
-    FlexibleInstances
-    FlexibleContexts
-    GADTs
-    EmptyDataDecls
-    StandaloneDeriving
-    ConstraintKinds
-    RankNTypes
-    TypeFamilies
-    TypeOperators
-    DataKinds
-    ImpredicativeTypes
-    LiberalTypeSynonyms
-    OverloadedStrings
-    TemplateHaskell
-    QuasiQuotes
-    GeneralizedNewtypeDeriving
-    DeriveDataTypeable
-    DeriveGeneric
-    DefaultSignatures
-    ScopedTypeVariables
-    BangPatterns
-    RecordWildCards
-    TupleSections
+    Arrows, BangPatterns, ConstraintKinds, DataKinds, DefaultSignatures, DeriveDataTypeable, DeriveFunctor, DeriveGeneric, EmptyDataDecls, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GADTs, GeneralizedNewtypeDeriving, ImpredicativeTypes, LambdaCase, LiberalTypeSynonyms, MagicHash, MultiParamTypeClasses, MultiWayIf, NoImplicitPrelude, NoMonomorphismRestriction, OverloadedStrings, PatternGuards, ParallelListComp, QuasiQuotes, RankNTypes, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, TemplateHaskell, TupleSections, TypeFamilies, TypeOperators, UnboxedTuples
   default-language:
     Haskell2010
 
 
-executable neat-interpolation-demo
+test-suite api-tests
+  type:
+    exitcode-stdio-1.0
   hs-source-dirs:
-    src
+    executables
   main-is:
-    Demo.hs
-  ghc-options:
-    -threaded 
-    "-with-rtsopts=-N"
+    APITests.hs
   build-depends:
-    parsec,
-    template-haskell,
-    base >= 4.5 && < 5
+    neat-interpolation,
+    HTF == 0.11.*,
+    base-prelude == 0.1.*
   default-extensions:
-    NoImplicitPrelude
-    PatternGuards
-    MultiWayIf
-    LambdaCase
-    Arrows
-    MultiParamTypeClasses
-    NoMonomorphismRestriction
-    FlexibleInstances
-    FlexibleContexts
-    GADTs
-    EmptyDataDecls
-    StandaloneDeriving
-    ConstraintKinds
-    RankNTypes
-    TypeFamilies
-    TypeOperators
-    DataKinds
-    ImpredicativeTypes
-    LiberalTypeSynonyms
-    OverloadedStrings
-    TemplateHaskell
-    QuasiQuotes
-    GeneralizedNewtypeDeriving
-    DeriveDataTypeable
-    DeriveGeneric
-    DefaultSignatures
-    ScopedTypeVariables
-    BangPatterns
-    RecordWildCards
-    TupleSections
+    Arrows, BangPatterns, ConstraintKinds, DataKinds, DefaultSignatures, DeriveDataTypeable, DeriveFunctor, DeriveGeneric, EmptyDataDecls, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GADTs, GeneralizedNewtypeDeriving, ImpredicativeTypes, LambdaCase, LiberalTypeSynonyms, MagicHash, MultiParamTypeClasses, MultiWayIf, NoImplicitPrelude, NoMonomorphismRestriction, OverloadedStrings, PatternGuards, ParallelListComp, QuasiQuotes, RankNTypes, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, TemplateHaskell, TupleSections, TypeFamilies, TypeOperators, UnboxedTuples
   default-language:
     Haskell2010
diff --git a/src/Demo.hs b/src/Demo.hs
deleted file mode 100644
--- a/src/Demo.hs
+++ /dev/null
@@ -1,26 +0,0 @@
-
-import NeatInterpolation.Prelude
-import NeatInterpolation
-
-main = do
-  let a' = [string| 
-    function startsWith( start, string ){
-      return string.lastIndexOf( start ) == 0
-    }
-  |]
-  let pattern = [string| {
-      
-
-      $a'
-
-      single inlining: ( $a' )
-
-
-      {
-        multiple inlining: ( $a' ) ( $a' )
-      }
-
-
-    } 
-    |]
-  putStrLn pattern
diff --git a/src/NeatInterpolation.hs b/src/NeatInterpolation.hs
deleted file mode 100644
--- a/src/NeatInterpolation.hs
+++ /dev/null
@@ -1,123 +0,0 @@
-{-# OPTIONS_GHC -fno-warn-missing-fields #-} 
--- | 
--- NeatInterpolation provides a quasiquoter for producing strings 
--- with a simple interpolation of input values. 
--- It removes the excessive indentation from the input and 
--- accurately manages the indentation of all lines of interpolated variables. 
--- But enough words, the code shows it better.
--- 
--- Consider the following declaration:
--- 
--- > {-# LANGUAGE QuasiQuotes, OverloadedStrings #-}
--- > 
--- > import NeatInterpolation
--- > 
--- > f :: String -> String -> String
--- > f a b = 
--- >   [string|
--- >     function(){
--- >       function(){
--- >         $a
--- >       }
--- >       return $b
--- >     }
--- >   |]
--- 
--- Executing the following:
--- 
--- > main = putStrLn $ f "1" "2"
--- 
--- will produce this (notice the reduced indentation compared to how it was
--- declared):
--- 
--- > function(){
--- >   function(){
--- >     1
--- >   }
--- >   return 2
--- > }
--- 
--- Now let's test it with multiline string parameters:
--- 
--- > main = putStrLn $ f 
--- >   "{\n  indented line\n  indented line\n}" 
--- >   "{\n  indented line\n  indented line\n}" 
---
--- We get
---
--- > function(){
--- >   function(){
--- >     {
--- >       indented line
--- >       indented line
--- >     }
--- >   }
--- >   return {
--- >     indented line
--- >     indented line
--- >   }
--- > }
--- 
--- See how it neatly preserved the indentation levels of lines the 
--- variable placeholders were at?  
-module NeatInterpolation (string, indentQQPlaceholder) where
-
-import NeatInterpolation.Prelude
-
-import Language.Haskell.TH
-import Language.Haskell.TH.Quote
-
-import NeatInterpolation.String
-import NeatInterpolation.Parsing
-
-
--- |
--- The quasiquoter.
-string :: QuasiQuoter
-string = QuasiQuoter {quoteExp = quoteExprExp}
-
--- |
--- A function used internally by quasiquoter. Just ignore it.
-indentQQPlaceholder :: Int -> String -> String
-indentQQPlaceholder indent text = case lines text of
-  head:tail -> intercalate "\n" $ head : map (replicate indent ' ' ++) tail
-  [] -> text 
-
-
-quoteExprExp :: String -> Q Exp
-quoteExprExp input = 
-  case parseLines $ normalizeQQInput input of
-    Left e -> fail $ show e
-    Right lines -> appE [|unlines|] $ linesExp lines
-
-linesExp :: [Line] -> Q Exp
-linesExp [] = [|([] :: [String])|]
-linesExp (head : tail) = 
-  (binaryOpE [|(:)|])
-    (lineExp head)
-    (linesExp tail)
-
-lineExp :: Line -> Q Exp
-lineExp (Line indent contents) = 
-  msumExps $ map (contentExp $ fromIntegral indent) contents
-
-
-
-contentExp :: Integer -> LineContent -> Q Exp
-contentExp _ (LineContentText text) = stringE text
-contentExp indent (LineContentIdentifier name) = do
-  valueName <- lookupValueName name
-  case valueName of
-    Just valueName -> do
-      Just indentQQPlaceholderName <- lookupValueName "indentQQPlaceholder"
-      appE
-        (appE (varE indentQQPlaceholderName) $ litE $ integerL indent)
-        (varE valueName)
-    Nothing -> fail $ "Value `" ++ name ++ "` is not in scope"
-
-msumExps :: [Q Exp] -> Q Exp
-msumExps = foldr (binaryOpE mappendE) memptyE
-memptyE = [|mempty|]
-mappendE = [|mappend|]
-
-binaryOpE e = \a b -> e `appE` a `appE` b
diff --git a/src/NeatInterpolation/Parsing.hs b/src/NeatInterpolation/Parsing.hs
deleted file mode 100644
--- a/src/NeatInterpolation/Parsing.hs
+++ /dev/null
@@ -1,35 +0,0 @@
-module NeatInterpolation.Parsing where
-
-import NeatInterpolation.Prelude hiding (try, (<|>), many)
-import Text.Parsec hiding (Line)
--- import qualified Data.Attoparsec.Text as AP
-
-data Line = 
-  Line {lineIndent :: Int, lineContents :: [LineContent]}
-  deriving (Show)
-
-data LineContent = 
-  LineContentText [Char] |
-  LineContentIdentifier [Char]
-  deriving (Show)
-
-
-parseLines :: [Char] -> Either ParseError [Line]
-parseLines = parse lines "NeatInterpolation.Parsing.parseLines"
-  where
-    lines = sepBy line newline <* eof
-    line = Line <$> countIndent <*> many content
-    countIndent = fmap length $ try $ lookAhead $ many $ char ' '
-    content = try identifier <|> contentText
-    identifier = fmap LineContentIdentifier $ 
-      string "$" *> many1 (alphaNum <|> char '\'' <|> char '_')
-    contentText = do
-      text <- manyTill anyChar end
-      if null text
-        then fail "Empty text"
-        else return $ LineContentText $ text
-      where
-        end = 
-          (void $ try $ lookAhead identifier) <|> 
-          (void $ try $ lookAhead newline) <|> 
-          eof
diff --git a/src/NeatInterpolation/Prelude.hs b/src/NeatInterpolation/Prelude.hs
deleted file mode 100644
--- a/src/NeatInterpolation/Prelude.hs
+++ /dev/null
@@ -1,52 +0,0 @@
-module NeatInterpolation.Prelude 
-  ( 
-    module Exports,
-    (?:),
-    traceM,
-  )
-  where
-
--- base
-import Prelude as Exports hiding (concat, foldr, mapM_, sequence_, foldl1, maximum, minimum, product, sum, all, and, any, concatMap, elem, foldl, foldr1, notElem, or, mapM, sequence, FilePath, id, (.))
-import Control.Monad as Exports hiding (mapM_, sequence_, forM_, msum, mapM, sequence, forM)
-import Control.Applicative as Exports
-import Control.Arrow as Exports hiding (left, right)
-import Control.Category as Exports
-import Data.Monoid as Exports
-import Data.Foldable as Exports
-import Data.Traversable as Exports hiding (for)
-import Data.Maybe as Exports
-import Data.List as Exports hiding (concat, foldr, foldl1, maximum, minimum, product, sum, all, and, any, concatMap, elem, foldl, foldr1, notElem, or, find, maximumBy, minimumBy, mapAccumL, mapAccumR, foldl')
-import Data.Tuple as Exports
-import Data.Ord as Exports (Down(..))
-import Data.String as Exports
-import Data.Int as Exports
-import Data.Word as Exports
-import Data.Ratio as Exports
-import Data.Fixed as Exports
-import Data.Ix as Exports
-import Data.Data as Exports
-import Text.Read as Exports (readMaybe, readEither)
-import Control.Exception as Exports hiding (tryJust)
-import Control.Concurrent as Exports hiding (yield)
-import System.Timeout as Exports
-import System.Exit as Exports
-import System.IO.Unsafe as Exports
-import System.IO as Exports (Handle, hClose)
-import System.IO.Error as Exports
-import Unsafe.Coerce as Exports
-import GHC.Exts as Exports (groupWith, sortWith)
-import GHC.Generics as Exports (Generic)
-import GHC.IO.Exception as Exports
-import Debug.Trace as Exports
-import Data.IORef as Exports
-import Data.STRef as Exports
-import Control.Monad.ST as Exports
-
-
-(?:) :: Maybe a -> a -> a
-maybeA ?: b = fromMaybe b maybeA
-{-# INLINE (?:) #-}
-
-traceM :: (Monad m) => String -> m ()
-traceM s = trace s $ return ()
diff --git a/src/NeatInterpolation/String.hs b/src/NeatInterpolation/String.hs
deleted file mode 100644
--- a/src/NeatInterpolation/String.hs
+++ /dev/null
@@ -1,47 +0,0 @@
-module NeatInterpolation.String where
-
-import NeatInterpolation.Prelude
-import Data.Char
-
-
-normalizeQQInput :: [Char] -> [Char]
-normalizeQQInput = trim . unindent' . tabsToSpaces
-  where
-    unindent' :: [Char] -> [Char]
-    unindent' s =
-      case lines s of
-        head:tail -> 
-          let 
-            unindentedHead = dropWhile (== ' ') head 
-            minimumTailIndent = minimumIndent . unlines $ tail
-            unindentedTail = case minimumTailIndent of
-              Just indent -> map (drop indent) tail
-              Nothing -> tail
-          in unlines $ unindentedHead : unindentedTail
-        [] -> []
-
-trim :: [Char] -> [Char]
-trim = dropWhileRev isSpace . dropWhile isSpace
-
-dropWhileRev :: (a -> Bool) -> [a] -> [a]
-dropWhileRev p = foldr (\x xs -> if p x && null xs then [] else x:xs) []
-
-unindent :: [Char] -> [Char]
-unindent s =
-  case minimumIndent s of
-    Just indent -> unlines . map (drop indent) . lines $ s
-    Nothing -> s
-
-tabsToSpaces :: [Char] -> [Char]
-tabsToSpaces ('\t':tail) = "    " ++ tabsToSpaces tail
-tabsToSpaces (head:tail) = head : tabsToSpaces tail
-tabsToSpaces [] = []
-
-minimumIndent :: [Char] -> Maybe Int
-minimumIndent = 
-  listToMaybe . sort . map lineIndent 
-    . filter (not . null . dropWhile isSpace) . lines
-
--- | Amount of preceding spaces on first line
-lineIndent :: [Char] -> Int
-lineIndent = length . takeWhile (== ' ')
