diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2016, cutsea110
+
+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 cutsea110 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.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/heredocs.cabal b/heredocs.cabal
new file mode 100644
--- /dev/null
+++ b/heredocs.cabal
@@ -0,0 +1,41 @@
+-- Initial heredoc.cabal generated by cabal init.  For further 
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name:                heredocs
+version:             0.1.0.0
+synopsis:            heredocument
+description:         heredocument
+homepage:            http://github.com/cutsea110/heredoc.git
+license:             BSD3
+license-file:        LICENSE
+author:              cutsea110
+maintainer:          cutsea110@gmail.com
+-- copyright:           
+category:            Text
+build-type:          Simple
+-- extra-source-files:  
+cabal-version:       >=1.10
+
+library
+  exposed-modules:     
+                     Text.Heredoc
+  -- other-modules:       
+  other-extensions:    
+                     TemplateHaskell
+                     QuasiQuotes
+  build-depends:       base >=4.8 && <4.9
+                     , doctest >= 0.11.0
+                     , parsec >=3.1.9
+                     , template-haskell >=2.10.0.0
+  hs-source-dirs:      src
+  default-language:    Haskell2010
+
+test-suite doctest
+  type:              exitcode-stdio-1.0
+  ghc-options:       -threaded
+  main-is:           doctests.hs
+  build-depends:     base
+                   , doctest >= 0.11.0
+                   , heredocs
+  hs-source-dirs:      test
+  default-language:    Haskell2010
diff --git a/src/Text/Heredoc.hs b/src/Text/Heredoc.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/Heredoc.hs
@@ -0,0 +1,447 @@
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# OPTIONS_GHC -fno-warn-missing-fields #-}
+module Text.Heredoc ( heredoc
+                    , heredocFile
+                    ) where
+
+import Control.Applicative ((<$>), (<*>))
+import Control.Arrow ((***))
+import Data.Function (on)
+import Data.Monoid ((<>))
+import Text.ParserCombinators.Parsec hiding (Line)
+import Text.ParserCombinators.Parsec.Error
+import Language.Haskell.TH
+import Language.Haskell.TH.Quote
+
+heredoc :: QuasiQuoter
+heredoc = QuasiQuoter { quoteExp = heredocFromString }
+
+heredocFile :: FilePath -> Q Exp
+heredocFile fp = do
+  content <- runIO $ readFile fp
+  heredocFromString content
+
+-- | C# code gen
+heredocFromString :: String -> Q Exp
+heredocFromString
+    = either err (concatToQExp . arrange) . parse doc "heredoc" . (<>"\n")
+    where
+      err = infixE <$> Just . pos <*> pure (varE '(++)) <*> Just . msg
+      pos = litE <$> (stringL <$> show . errorPos)
+      msg = litE <$> (stringL <$> concatMap messageString . errorMessages)
+
+type Indent = Int
+type Line' = (Indent, Line)
+type ChildBlock = [Line']
+type AltFlag = Bool
+
+data InLine = Raw String
+            | Quoted [Expr]
+              deriving Show
+
+data Line = CtrlForall [Expr] [Expr] ChildBlock
+          | CtrlMaybe AltFlag [Expr] [Expr] ChildBlock ChildBlock
+          | CtrlNothing
+          | CtrlIf AltFlag [Expr] ChildBlock ChildBlock
+          | CtrlElse
+          | CtrlCase [Expr] [([Expr], ChildBlock)]
+          | CtrlOf [Expr]
+          | CtrlLet [Expr] [Expr] ChildBlock
+          | Normal [InLine]
+            deriving Show
+
+data Expr = S String
+          | I Integer
+          | W
+          | A String Expr
+          | V String
+          | V' String
+          | C String
+          | O String
+          | O' String
+          | E [Expr]
+          | T [[Expr]]
+          | L [[Expr]]
+          | N
+            deriving Show
+
+eol :: Parser String
+eol =     try (string "\n\r")
+      <|> try (string "\r\n")
+      <|> string "\n"
+      <|> string "\r"
+      <?> fail "end of line"
+
+spaceTabs :: Parser String
+spaceTabs = many (oneOf " \t")
+
+doc :: Parser [(Indent, Line)]
+doc = line `endBy` eol
+
+line :: Parser (Indent, Line)
+line = (,) <$> indent <*> contents
+
+indent :: Parser Indent
+indent = fmap sum $
+         many ((char ' ' >> pure 1) <|>
+               (char '\t' >> fail "Tabs are not allowed in indentation"))
+
+contents :: Parser Line
+contents = try ctrlForall <|>
+           try ctrlMaybe <|>
+           try ctrlNothing <|>
+           try ctrlIf <|>
+           try ctrlElse <|>
+           try ctrlCase <|>
+           try ctrlOf <|>
+           try ctrlLet <|>
+           normal
+
+ctrlForall :: Parser Line
+ctrlForall = CtrlForall <$> bindVal <*> expr <*> pure []
+    where
+      bindVal = string "$forall" *> spaceTabs *>
+                binding
+                <* spaceTabs <* string "<-" <* spaceTabs
+
+ctrlMaybe :: Parser Line
+ctrlMaybe = CtrlMaybe <$> pure False <*> bindVal <*> expr <*> pure [] <*> pure []
+    where
+      bindVal = string "$maybe" *> spaceTabs *>
+                binding
+                <* spaceTabs <* string "<-" <* spaceTabs
+
+ctrlNothing :: Parser Line
+ctrlNothing = string "$nothing" *> spaceTabs >> pure CtrlNothing
+
+ctrlIf :: Parser Line
+ctrlIf = CtrlIf <$> pure False <*> (string "$if" *> spaceTabs *> expr <* spaceTabs) <*> pure [] <*> pure []
+
+ctrlElse :: Parser Line
+ctrlElse = string "$else" *> spaceTabs >> pure CtrlElse
+
+ctrlCase :: Parser Line
+ctrlCase = CtrlCase <$> (string "$case" *> spaceTabs *> expr <* spaceTabs) <*> pure []
+
+ctrlOf :: Parser Line
+ctrlOf = CtrlOf <$> bindVal
+    where
+      bindVal = string "$of" *> spaceTabs *>
+                binding
+                <* spaceTabs
+
+ctrlLet :: Parser Line
+ctrlLet = CtrlLet <$> bindVal <*> expr <*> pure []
+    where
+      bindVal = string "$let" *> spaceTabs *>
+                binding
+                <* spaceTabs <* string "=" <* spaceTabs
+
+binding :: Parser [Expr]
+binding = spaceTabs *> many1 (try (A <$> var <* char '@' <*> term) <|>
+                              term)
+    where
+      term :: Parser Expr
+      term = (C <$> con <|>
+              T <$> tuple <|>
+              (try (nil >> pure N) <|>
+               try (L <$> list) <|>
+               try (O <$> string ":")) <|> -- only pattern operator
+              (try (V <$> ((++) <$> wild <*> many1 (alphaNum <|> oneOf "_'"))) <|>
+               try (wild >> pure W) <|>
+               V <$> var)) <* spaceTabs
+
+expr :: Parser [Expr]
+expr = spaceTabs *> many1 (try (A <$> var <* char '@' <*> term) <|>
+                           term)
+    where
+      term :: Parser Expr
+      term = (S  <$> str <|>
+              T  <$> tuple <|>
+              (try (nil >> pure N) <|>
+               try (L <$> list) <|>
+               try (O <$> op)) <|>
+              (try (O' <$> op') <|> try (E  <$> subexp)) <|>
+              C  <$> con <|>
+              I  <$> integer <|>
+              V' <$> var' <|>
+              (try (V <$> ((++) <$> wild <*> many1 (alphaNum <|> oneOf "_'"))) <|>
+               try (wild >> pure W) <|>
+               V <$> var)) <* spaceTabs
+
+
+tuple :: Parser [[Expr]]
+tuple = char '(' *> sepBy expr (char ',')  <* char ')'
+
+list :: Parser [[Expr]]
+list = char '[' *> sepBy expr (char ',') <* char ']'
+
+integer :: Parser Integer
+integer = read <$> many1 digit
+
+str :: Parser String
+str = char '"' *> many quotedChar <* char '"'
+    where
+      quotedChar :: Parser Char
+      quotedChar = noneOf "\\\"" <|> try (string "\\\"" >> pure '"')
+
+subexp :: Parser [Expr]
+subexp = char '(' *> expr <* char ')'
+
+var :: Parser String
+var = (:) <$> lower <*> many (alphaNum <|> oneOf "_'")
+
+var' :: Parser String
+var' = char '`' *> var <* char '`'
+
+wild :: Parser String
+wild = string "_"
+
+nil :: Parser String
+nil = string "[]"
+
+con :: Parser String
+con = (:) <$> upper <*> many (alphaNum <|> oneOf "_'")
+
+op :: Parser String
+op = many1 (oneOf ":!#$%&*+./<=>?@\\^|-~")
+
+op' :: Parser String
+op' = char '(' *> op <* char ')'
+
+normal :: Parser Line
+normal = Normal <$> many (try quoted <|> try raw' <|> try raw)
+
+quoted :: Parser InLine
+quoted = Quoted <$> (string "${" *> expr <* string "}")
+
+raw' :: Parser InLine
+raw' = Raw <$> ((:) <$> (char '$')
+                <*> ((:) <$> noneOf "{" <*> many (noneOf "$\n\r")))
+
+raw :: Parser InLine
+raw = Raw <$> many1 (noneOf "$\n\r")
+
+----
+arrange :: [(Indent, Line)] -> [(Indent, Line)]
+arrange = norm . rev . foldl (flip push) []
+    where
+      isCtrlNothing (_, CtrlNothing) = True
+      isCtrlNothing _ = False
+      isCtrlElse (_, CtrlElse) = True
+      isCtrlElse _ = False
+      isCtrlOf (_, CtrlOf _) = True
+      isCtrlOf _ = False
+
+      push :: Line' -> [Line'] -> [Line']
+      push x [] = x:[]
+      push x ss'@((_, Normal _):_) = x:ss'
+
+      push x@(i, _) ss'@((j, CtrlForall b e body):ss)
+          | i > j = (j, CtrlForall b e (push x body)):ss
+          | otherwise = x:ss'
+
+      push x@(i, _) ss'@((j, CtrlLet b e body):ss)
+          | i > j = (j, CtrlLet b e (push x body)):ss
+          | otherwise = x:ss'
+
+      push x@(i, _) ss'@((j, CtrlMaybe flg b e body alt):ss)
+          | i > j = if flg
+                    then (j, CtrlMaybe flg b e body (push x alt)):ss
+                    else (j, CtrlMaybe flg b e (push x body) alt):ss
+          | i == j && isCtrlNothing x
+              = if flg
+                then error "too many $nothing found"
+                else (j, CtrlMaybe True b e body alt):ss
+          | otherwise = x:ss'
+      push x ((j, CtrlNothing):_) = error "orphan $nothing found"
+
+      push x@(i, _) ss'@((j, CtrlIf flg e body alt):ss)
+          | i > j = if flg
+                    then (j, CtrlIf flg e body (push x alt)):ss
+                    else (j, CtrlIf flg e (push x body) alt):ss
+          | i == j && isCtrlElse x
+              = if flg
+                then error "too many $else found"
+                else (j, CtrlIf True e body alt):ss
+          | otherwise = x:ss'
+      push x ((j, CtrlElse):_) = error "orphan $else found"
+
+      push x@(i, _) ss'@((j, CtrlCase e alts):ss)
+          | i > j = (j, CtrlCase e (push' x alts)):ss
+          | otherwise
+            = if isCtrlOf x
+              then error "orphan $of found"
+              else x:ss'
+      push x ((j, CtrlOf _):_) = error "orphan $of found"
+
+      push' x@(i, CtrlOf e) alts = (e, []):alts
+      push' x [] = error "$of not found"
+      push' x ((e, body):alts) = (e, (push x body)):alts
+
+      rev :: [Line'] -> [Line']
+      rev = foldr (\x xs -> xs ++ [rev' x]) []
+      rev' :: Line' -> Line'
+      rev' x@(_, Normal _) = x
+      rev' (i, CtrlForall b e body)
+          = (i, CtrlForall b e (rev body))
+      rev' (i, CtrlLet b e body)
+          = (i, CtrlLet b e (rev body))
+      rev' (i, CtrlMaybe flg b e body alt)
+          = (i, CtrlMaybe flg b e (rev body) (rev alt))
+      rev' (i, CtrlIf flg e body alt)
+          = (i, CtrlIf flg e (rev body) (rev alt))
+      rev' (i, CtrlCase e alts)
+          = (i, CtrlCase e (map (id *** rev) $ reverse alts))
+
+      norm :: [Line'] -> [Line']
+      norm = foldr (\x xs -> norm' x:xs) []
+      norm' :: Line' -> Line'
+      norm' x@(_, Normal _) = x
+      norm' (i, CtrlForall b e body)
+          = (i, CtrlForall b e (normsub i body))
+      norm' (i, CtrlLet b e body)
+          = (i, CtrlLet b e (normsub i body))
+      norm' (i, CtrlMaybe flg b e body alt)
+          = (i, CtrlMaybe flg b e (normsub i body) (normsub i alt))
+      norm' (i, CtrlNothing) = error "orphan $nothing found"
+      norm' (i, CtrlIf flg e body alt)
+          = (i, CtrlIf flg e (normsub i body) (normsub i alt))
+      norm' (i, CtrlElse) = error "orphan $else found"
+      norm' (i, CtrlCase e alts)
+          = (i, CtrlCase e (map (id *** normsub i) alts))
+      norm' (i, CtrlOf _) = error "orphan $of found"
+
+      normsub :: Indent -> [Line'] -> [Line']
+      normsub i body = let j = minimum $ map fst body
+                           deIndent n = i+(n-j)
+                       in norm $ map (deIndent *** id) body
+
+class ToQPat a where
+    toQPat :: a -> Q Pat
+    concatToQPat :: [a] -> Q Pat
+
+instance ToQPat Expr where
+    toQPat (S s) = litP (stringL s)
+    toQPat (I i) = litP (integerL i)
+    toQPat W     = wildP
+    toQPat (V v) = varP (mkName v)
+    toQPat (O o) = varP (mkName o)
+    toQPat (E e) = concatToQPat e
+    toQPat (C c) = conP (mkName c) []
+    toQPat (T t) = tupP $ map concatToQPat t
+    toQPat (A a e) = asP (mkName a) $ toQPat e
+
+    -- special case for list
+    concatToQPat (x:O ":":xs) = infixP (toQPat x)
+                                       (mkName ":")
+                                       (concatToQPat xs)
+    concatToQPat ((C c):args) = conP (mkName c) $ map toQPat args
+    concatToQPat ((V v):args) = varP (mkName v) -- OK?
+    concatToQPat (p@(T t):[]) = toQPat p -- OK?
+    concatToQPat (W:[]) = wildP -- OK?
+    concatToQPat (p@(A _ _):[]) = toQPat p
+    concatToQPat _ = error "don't support this pattern"
+
+class ToQExp a where
+    toQExp :: a -> Q Exp
+    concatToQExp :: [a] -> Q Exp
+
+instance ToQExp Expr where
+    toQExp (S s) = litE (stringL s)
+    toQExp (I i) = litE (integerL i)
+    toQExp W     = error "wildcard is NOT legal expression"
+    toQExp (V v) = varE (mkName v)
+    toQExp (O o) = varE (mkName o)
+    toQExp (E e) = concatToQExp e
+    toQExp (C c) = conE (mkName c)
+    toQExp (T t) = tupE $ map concatToQExp t
+    toQExp N     = listE []
+    toQExp (L l) = listE $ map concatToQExp l
+
+    concatToQExp xs = concatToQ' Nothing xs
+        where
+          concatToQ' (Just acc) [] = acc
+          concatToQ' Nothing  [x] = toQExp x
+          concatToQ' Nothing (x:xs) = concatToQ' (Just (toQExp x)) xs
+          -- spacial case for list
+          concatToQ' (Just acc) ((O ":"):xs)
+              = infixE (Just acc)
+                       (conE (mkName ":"))
+                       (Just (concatToQExp xs))
+          concatToQ' (Just acc) ((O o):xs)
+              = infixE (Just acc)
+                       (varE (mkName o))
+                       (Just (concatToQExp xs))
+          concatToQ' (Just acc) ((V' v'):xs)
+              = infixE (Just acc)
+                       (varE (mkName v'))
+                       (Just (concatToQExp xs))
+          concatToQ' (Just acc) (x:xs)
+              = concatToQ' (Just (appE acc (toQExp x))) xs
+
+instance ToQExp InLine where
+    toQExp (Raw s) = litE (stringL s)
+    toQExp (Quoted expr) = concatToQExp expr
+
+    concatToQExp [] = litE (stringL "")
+    concatToQExp (x:xs) = infixE (Just (toQExp x))
+                                 (varE '(++))
+                                 (Just (concatToQExp xs))
+
+instance ToQExp Line where
+    toQExp (CtrlForall b e body)
+        = appE (appE (appE (varE 'foldr)
+                           (lamE [concatToQPat b]
+                                 (infixE (Just (concatToQExp body))
+                                         (varE '(++))
+                                         Nothing)))
+                (litE (stringL "")))
+          (concatToQExp e)
+    toQExp (CtrlMaybe flg b e body alt)
+        = caseE (concatToQExp e)
+                [ match (conP 'Just [concatToQPat b])
+                        (normalB (concatToQExp body))
+                        []
+                , match (conP 'Nothing [])
+                        (normalB (concatToQExp alt))
+                         []
+                ]
+    toQExp (CtrlIf flg e body alt)
+        = condE (concatToQExp e) (concatToQExp body) (concatToQExp alt)
+    toQExp CtrlElse = error "illegal $else found"
+    toQExp (CtrlCase e alts)
+        = caseE (concatToQExp e) (map mkMatch alts)
+        where
+          mkMatch (e', body) = match (concatToQPat e')
+                                     (normalB (concatToQExp body))
+                                     []
+    toQExp (CtrlOf e) = error "illegal $of found"
+    toQExp (CtrlLet b e body)
+        = letE [valD (concatToQPat b) (normalB $ concatToQExp e) []]
+               (concatToQExp body)
+    toQExp (Normal xs) = concatToQExp xs
+
+    concatToQExp (x:[]) = toQExp x
+    concatToQExp (x:xs) = infixE (Just (toQExp x))
+                                 (varE '(++))
+                                 (Just (concatToQExp xs))
+
+instance ToQExp Line' where
+    toQExp (n, x@(Normal _))
+        = infixE (Just (litE (stringL (replicate n ' '))))
+                 (varE '(++))
+                 (Just (toQExp x))
+    toQExp (n, x) =  toQExp x -- Ctrl*
+
+    concatToQExp [] = litE (stringL "")
+    concatToQExp (x@(_, Normal _):xs)
+        = infixE (Just (infixE (Just (toQExp x))
+                               (varE '(++))
+                               (Just (litE (stringL "\n")))))
+                 (varE '(++))
+                 (Just (concatToQExp xs))
+    concatToQExp (x:xs) = infixE (Just (toQExp x))
+                                 (varE '(++))
+                                 (Just (concatToQExp xs))
diff --git a/test/doctests.hs b/test/doctests.hs
new file mode 100644
--- /dev/null
+++ b/test/doctests.hs
@@ -0,0 +1,6 @@
+module Main where
+
+import Test.DocTest
+
+main :: IO ()
+main = doctest ["-isrc", "src/Text/Heredoc.hs", "test/Test.hs"]
