{-# LANGUAGE TemplateHaskell #-}

-- | QuasiQuoter for interpolated strings using Ruby syntax. Expressions inside #{} will have
-- 'show' called. Multi-line strings are supported. Escaping of '#' and '{' is done with backslash.
--
-- @
-- v :: String
-- v = [$istr| well \#{\"hello\" ++ \" there\"} \#{6*7}]
-- @
--
-- v will have the value \" well hello there 42\"
module Text.InterpolatedString.QQ (istr) where

import qualified Language.Haskell.TH as TH
import Language.Haskell.TH.Quote
import Language.Haskell.Meta.Parse

data StringPart = Literal String | AntiQuote String deriving Show

parseHaskell a []          = [Literal (reverse a)]
parseHaskell a ('\\':x:xs) = parseHaskell (x:a) xs
parseHaskell a ('\\':[])   = parseHaskell ('\\':a) []
parseHaskell a ('}':xs)    = AntiQuote (reverse a) : parseStr [] xs
parseHaskell a (x:xs)      = parseHaskell (x:a) xs

parseStr a []           = [Literal (reverse a)]
parseStr a ('\\':x:xs)  = parseStr (x:a) xs
parseStr a ('\\':[])    = parseStr ('\\':a) []
parseStr a ('#':'{':xs) = Literal (reverse a) : parseHaskell [] xs
parseStr a (x:xs)       = parseStr (x:a) xs

makeExpr [] = [| "" |]
makeExpr ((Literal a):xs)   = TH.appE [| (++) a |] (makeExpr xs)
makeExpr ((AntiQuote a):xs) = TH.appE [| (++) (trimQuotes (show $(reifyStringToHaskell a))) |] (makeExpr xs)

trimQuotes s = reverse $ dropWhile (== '"') $ reverse $ dropWhile (== '"') s

reifyStringToHaskell s = 
    case parseExp s of
        Left s  -> TH.report True s >> [| "" |]
        Right e ->  return e

rstrExp s = makeExpr $ parseStr [] $ filter (/= '\r') s

-- | QuasiQuoter for interpolating Haskell values into a string literal. The pattern portion is undefined.
istr :: QuasiQuoter
istr = QuasiQuoter rstrExp undefined