{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE TemplateHaskell #-}
module Preql.QuasiQuoter.Raw.TH where
import Preql.QuasiQuoter.Common
import Preql.QuasiQuoter.Raw.Lex (Token(..), parseQuery, unLex)
import Preql.Wire (Query)
import Data.String (IsString(..))
import Language.Haskell.TH
import Language.Haskell.TH.Quote
makeQuery :: String -> Q Exp
makeQuery :: String -> Q Exp
makeQuery String
string = [e|(fromString string :: Query $(VarT <$> (newName "n"))) |]
sql :: QuasiQuoter
sql :: QuasiQuoter
sql = String -> (String -> Q Exp) -> QuasiQuoter
expressionOnly String
"sql " ((String -> Q Exp) -> QuasiQuoter)
-> (String -> Q Exp) -> QuasiQuoter
forall a b. (a -> b) -> a -> b
$ \String
raw -> do
Loc
loc <- Q Loc
location
let e_ast :: Either String [Token]
e_ast = String -> String -> Either String [Token]
parseQuery (Loc -> String
forall a. Show a => a -> String
show Loc
loc) String
raw
case Either String [Token]
e_ast of
Right [Token]
parsed -> do
let
positionalCount :: Word
positionalCount = [Token] -> Word
maxParam [Token]
parsed
(String
rewritten, [String]
haskellExpressions) = Word -> [Token] -> (String, [String])
numberAntiquotes Word
positionalCount [Token]
parsed
antiNames :: [Name]
antiNames = (String -> Name) -> [String] -> [Name]
forall a b. (a -> b) -> [a] -> [b]
map String -> Name
mkName [String]
haskellExpressions
Exp
query <- String -> Q Exp
makeQuery String
rewritten
case Word
positionalCount of
Word
0 ->
Exp -> Q Exp
forall (m :: * -> *) a. Monad m => a -> m a
return (Exp -> Q Exp) -> Exp -> Q Exp
forall a b. (a -> b) -> a -> b
$ [Exp] -> Exp
tupleE [Exp
query, [Name] -> Exp
tupleOrSingle [Name]
antiNames]
Word
1 -> do
Name
patternName <- String -> Q Name
newName String
"c"
Exp -> Q Exp
forall (m :: * -> *) a. Monad m => a -> m a
return (Exp -> Q Exp) -> Exp -> Q Exp
forall a b. (a -> b) -> a -> b
$ [Pat] -> Exp -> Exp
LamE [Name -> Pat
VarP Name
patternName]
([Exp] -> Exp
tupleE [Exp
query, [Name] -> Exp
tupleOrSingle (Name
patternName Name -> [Name] -> [Name]
forall a. a -> [a] -> [a]
: [Name]
antiNames)])
Word
_ -> do
[Name]
patternNames <- Char -> Int -> Q [Name]
cNames Char
'q' (Word -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word
positionalCount)
Exp -> Q Exp
forall (m :: * -> *) a. Monad m => a -> m a
return (Exp -> Q Exp) -> Exp -> Q Exp
forall a b. (a -> b) -> a -> b
$ [Pat] -> Exp -> Exp
LamE
[[Pat] -> Pat
TupP ((Name -> Pat) -> [Name] -> [Pat]
forall a b. (a -> b) -> [a] -> [b]
map Name -> Pat
VarP [Name]
patternNames)]
([Exp] -> Exp
tupleE [Exp
query, [Name] -> Exp
tupleOrSingle ([Name]
patternNames [Name] -> [Name] -> [Name]
forall a. [a] -> [a] -> [a]
++ [Name]
antiNames)])
Left String
err -> String -> Q Exp
forall a. HasCallStack => String -> a
error String
err
maxParam :: [Token] -> Word
maxParam :: [Token] -> Word
maxParam = (Token -> Word -> Word) -> Word -> [Token] -> Word
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr Token -> Word -> Word
nextParam Word
0 where
nextParam :: Token -> Word -> Word
nextParam Token
token Word
maxSoFar =
case Token
token of
NumberedParam Word
i -> Word -> Word -> Word
forall a. Ord a => a -> a -> a
max Word
i Word
maxSoFar
Token
_ -> Word
maxSoFar
numberAntiquotes :: Word -> [Token] -> (String, [String])
numberAntiquotes :: Word -> [Token] -> (String, [String])
numberAntiquotes Word
mp [Token]
tokens = ([String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [String]
sqlStrings, [String]
variableNames) where
([String]
sqlStrings, [String]
variableNames) = Word -> [Token] -> ([String], [String])
go Word
mp [Token]
tokens
go :: Word -> [Token] -> ([String], [String])
go Word
_maxSoFar [] = ([], [])
go Word
maxSoFar (Token
token : [Token]
ts) =
case Token
token of
HaskellParam String
name -> let
newParam :: Word
newParam = Word
maxSoFar Word -> Word -> Word
forall a. Num a => a -> a -> a
+ Word
1
([String]
ss, [String]
ns) = Word -> [Token] -> ([String], [String])
go Word
newParam [Token]
ts
in (Token -> String
unLex (Word -> Token
NumberedParam Word
newParam) String -> [String] -> [String]
forall a. a -> [a] -> [a]
: [String]
ss, String
name String -> [String] -> [String]
forall a. a -> [a] -> [a]
: [String]
ns)
Token
EOF -> Word -> [Token] -> ([String], [String])
go Word
maxSoFar [Token]
ts
Token
_ -> let ([String]
ss, [String]
ns) = Word -> [Token] -> ([String], [String])
go Word
maxSoFar [Token]
ts in (Token -> String
unLex Token
token String -> [String] -> [String]
forall a. a -> [a] -> [a]
: [String]
ss, [String]
ns)