-- | Raw string literals, implemented using Template Haskell's quasiquotation
-- feature.
module Text.RawString.QQ (r, rQ)
       where

import Language.Haskell.TH
import Language.Haskell.TH.Quote

{-|

A quasiquoter for raw string literals - that is, string literals that don't
recognise the standard escape sequences (such as @\'\\n\'@). Basically, they
make your code more readable by freeing you from the responsibility to escape
backslashes. They are useful when working with regular expressions, DOS/Windows
paths and markup languages (such as XML).

Don't forget the @LANGUAGE QuasiQuotes@ pragma if you're using this
module in your code.

Usage:

@
    ghci> :set -XQuasiQuotes
    ghci> import Text.RawString.QQ
    ghci> let s = [r|\\w+\@[a-zA-Z_]+?\\.[a-zA-Z]{2,3}|]
    ghci> s
    \"\\\\w+\@[a-zA-Z_]+?\\\\.[a-zA-Z]{2,3}\"
    ghci> [r|C:\\Windows\\SYSTEM|] ++ [r|\\user32.dll|]
    \"C:\\\\Windows\\\\SYSTEM\\\\user32.dll\"
@

Multiline raw string literals are also supported:

@
    multiline :: String
    multiline = [r|\<HTML\>
    \<HEAD\>
    \<TITLE\>Auto-generated html formated source\</TITLE\>
    \<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=windows-1252\"\>
    \</HEAD\>
    \<BODY LINK=\"#0000ff\" VLINK=\"#800080\" BGCOLOR=\"#ffffff\"\>
    \<P\> \</P\>
    \<PRE\>|]
@

Caveat: since the @\"|]\"@ character sequence is used to terminate the
quasiquotation, you can't use it inside the raw string literal. Use 'rQ' if you
want to embed that character sequence inside the raw string.

For more on raw strings, see e.g.
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2053.html>

For more on quasiquotation, see
<http://www.haskell.org/haskellwiki/Quasiquotation>

-}
r :: QuasiQuoter
r :: QuasiQuoter
r = QuasiQuoter {
    -- Extracted from dead-simple-json.
    quoteExp :: String -> Q Exp
quoteExp  = Exp -> Q Exp
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return (Exp -> Q Exp) -> (String -> Exp) -> String -> Q Exp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Lit -> Exp
LitE (Lit -> Exp) -> (String -> Lit) -> String -> Exp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Lit
StringL (String -> Lit) -> (String -> String) -> String -> Lit
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
normaliseNewlines,

    quotePat :: String -> Q Pat
quotePat  = \String
_ -> String -> Q Pat
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"illegal raw string QuasiQuote \
                           \(allowed as expression only, used as a pattern)",
    quoteType :: String -> Q Type
quoteType = \String
_ -> String -> Q Type
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"illegal raw string QuasiQuote \
                           \(allowed as expression only, used as a type)",
    quoteDec :: String -> Q [Dec]
quoteDec  = \String
_ -> String -> Q [Dec]
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"illegal raw string QuasiQuote \
                           \(allowed as expression only, used as a declaration)"
}

{-| A variant of 'r' that interprets the @\"|~]\"@ sequence as @\"|]\"@,
@\"|~~]\"@ as @\"|~]\"@ and, in general, @\"|~^n]\"@ as @\"|~^(n-1)]\"@
for n >= 1.

Usage:

@
    ghci> [rQ||~]|~]|]
    \"|]|]\"
    ghci> [rQ||~~]|]
    \"|~]\"
    ghci> [rQ||~~~~]|]
    \"|~~~]\"
@
-}
rQ :: QuasiQuoter
rQ :: QuasiQuoter
rQ = QuasiQuoter {
    quoteExp :: String -> Q Exp
quoteExp  = Exp -> Q Exp
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return (Exp -> Q Exp) -> (String -> Exp) -> String -> Q Exp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Lit -> Exp
LitE (Lit -> Exp) -> (String -> Lit) -> String -> Exp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Lit
StringL (String -> Lit) -> (String -> String) -> String -> Lit
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
escape_rQ (String -> String) -> (String -> String) -> String -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
normaliseNewlines,

    quotePat :: String -> Q Pat
quotePat  = \String
_ -> String -> Q Pat
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"illegal raw string QuasiQuote \
                           \(allowed as expression only, used as a pattern)",
    quoteType :: String -> Q Type
quoteType = \String
_ -> String -> Q Type
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"illegal raw string QuasiQuote \
                           \(allowed as expression only, used as a type)",
    quoteDec :: String -> Q [Dec]
quoteDec  = \String
_ -> String -> Q [Dec]
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"illegal raw string QuasiQuote \
                           \(allowed as expression only, used as a declaration)"
}

escape_rQ :: String -> String
escape_rQ :: String -> String
escape_rQ [] = []
escape_rQ (Char
'|':Char
'~':String
xs) =
  let (String
tildas, String
rest) = (Char -> Bool) -> String -> (String, String)
forall a. (a -> Bool) -> [a] -> ([a], [a])
span (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'~') String
xs
  in case String
rest of
    []       -> Char
'|'Char -> String -> String
forall a. a -> [a] -> [a]
:Char
'~'Char -> String -> String
forall a. a -> [a] -> [a]
:String
tildas
    (Char
']':String
rs) -> Char
'|'Char -> String -> String
forall a. a -> [a] -> [a]
:String
tildas String -> String -> String
forall a. [a] -> [a] -> [a]
++ Char
']'Char -> String -> String
forall a. a -> [a] -> [a]
:String -> String
escape_rQ String
rs
    String
rs       -> Char
'|'Char -> String -> String
forall a. a -> [a] -> [a]
:Char
'~'Char -> String -> String
forall a. a -> [a] -> [a]
:String
tildas String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
escape_rQ String
rs
escape_rQ (Char
x:String
xs) = Char
x Char -> String -> String
forall a. a -> [a] -> [a]
: String -> String
escape_rQ String
xs

-- See https://github.com/23Skidoo/raw-strings-qq/issues/1 and
-- https://ghc.haskell.org/trac/ghc/ticket/11215.
normaliseNewlines :: String -> String
normaliseNewlines :: String -> String
normaliseNewlines []             = []
normaliseNewlines (Char
'\r':Char
'\n':String
cs) = Char
'\n'Char -> String -> String
forall a. a -> [a] -> [a]
:String -> String
normaliseNewlines String
cs
normaliseNewlines (Char
c:String
cs)         = Char
cChar -> String -> String
forall a. a -> [a] -> [a]
:String -> String
normaliseNewlines String
cs