{-# LANGUAGE TemplateHaskell #-}
module Unison.Sqlite.Sql
( Sql (..),
sql,
Param (..),
ParsedLump (..),
internalParseSql,
)
where
import Control.Monad.Trans.State.Strict qualified as State
import Data.Char qualified as Char
import Data.List.NonEmpty qualified as List (NonEmpty)
import Data.List.NonEmpty qualified as List.NonEmpty
import Data.Text qualified as Text
import Database.SQLite.Simple qualified as Sqlite.Simple
import Database.SQLite.Simple.ToField qualified as Sqlite.Simple
import Language.Haskell.TH qualified as TH
import Language.Haskell.TH.Quote qualified as TH
import Language.Haskell.TH.Syntax qualified as TH
import Text.Megaparsec qualified as Megaparsec
import Text.Megaparsec.Char qualified as Megaparsec
import TextBuilder (TextBuilder)
import TextBuilder qualified
import Unison.Prelude
import Prelude hiding (unzip)
data Sql = Sql
{ Sql -> Text
query :: Text,
Sql -> [SQLData]
params :: [Sqlite.Simple.SQLData]
}
deriving stock (Int -> Sql -> ShowS
[Sql] -> ShowS
Sql -> String
(Int -> Sql -> ShowS)
-> (Sql -> String) -> ([Sql] -> ShowS) -> Show Sql
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Sql -> ShowS
showsPrec :: Int -> Sql -> ShowS
$cshow :: Sql -> String
show :: Sql -> String
$cshowList :: [Sql] -> ShowS
showList :: [Sql] -> ShowS
Show)
query__ :: Sql -> Text
query__ :: Sql -> Text
query__ (Sql Text
x [SQLData]
_) = Text
x
params__ :: Sql -> [Sqlite.Simple.SQLData]
params__ :: Sql -> [SQLData]
params__ (Sql Text
_ [SQLData]
x) = [SQLData]
x
sql :: TH.QuasiQuoter
sql :: QuasiQuoter
sql = (String -> Q Exp)
-> (String -> Q Pat)
-> (String -> Q Type)
-> (String -> Q [Dec])
-> QuasiQuoter
TH.QuasiQuoter String -> Q Exp
sqlQQ String -> Q Pat
forall a. HasCallStack => a
undefined String -> Q Type
forall a. HasCallStack => a
undefined String -> Q [Dec]
forall a. HasCallStack => a
undefined
sqlQQ :: String -> TH.Q TH.Exp
sqlQQ :: String -> Q Exp
sqlQQ String
input =
case Text -> Either String [ParsedLump]
internalParseSql (String -> Text
Text.pack String
input) of
Left String
err -> String -> Q Exp
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
err
Right [ParsedLump]
lumps -> do
(sqlPieces, paramsPieces) <- [(Exp, Exp)] -> ([Exp], [Exp])
forall (f :: * -> *) a b. Functor f => f (a, b) -> (f a, f b)
unzip ([(Exp, Exp)] -> ([Exp], [Exp]))
-> Q [(Exp, Exp)] -> Q ([Exp], [Exp])
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [ParsedLump] -> (ParsedLump -> Q (Exp, Exp)) -> Q [(Exp, Exp)]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
t a -> (a -> f b) -> f (t b)
for [ParsedLump]
lumps ParsedLump -> Q (Exp, Exp)
unlump
[|
Sql
(mconcat $(pure (TH.ListE sqlPieces)))
(mconcat $(pure (TH.ListE paramsPieces)))
|]
where
unlump :: ParsedLump -> TH.Q (TH.Exp, TH.Exp)
unlump :: ParsedLump -> Q (Exp, Exp)
unlump = \case
ParsedOuterLump Text
s [Param]
params -> Text -> [Param] -> Q (Exp, Exp)
outerLump Text
s [Param]
params
ParsedInnerLump Text
s -> Text -> Q (Exp, Exp)
innerLump Text
s
ParsedInLump Text
s -> Text -> Q (Exp, Exp)
inLump Text
s
ParsedValuesLump Text
s -> Text -> Q (Exp, Exp)
valuesLump Text
s
outerLump :: Text -> [Param] -> TH.Q (TH.Exp, TH.Exp)
outerLump :: Text -> [Param] -> Q (Exp, Exp)
outerLump Text
s [Param]
params =
(,) (Exp -> Exp -> (Exp, Exp)) -> Q Exp -> Q (Exp -> (Exp, Exp))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> Q Exp
forall t (m :: * -> *). (Lift t, Quote m) => t -> m Exp
forall (m :: * -> *). Quote m => Text -> m Exp
TH.lift Text
s Q (Exp -> (Exp, Exp)) -> Q Exp -> Q (Exp, Exp)
forall a b. Q (a -> b) -> Q a -> Q b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> [|mconcat $([Exp] -> Exp
TH.ListE ([Exp] -> Exp) -> Q [Exp] -> Q Exp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Param] -> (Param -> Q Exp) -> Q [Exp]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
t a -> (a -> f b) -> f (t b)
for [Param]
params Param -> Q Exp
paramToSqlData)|]
where
paramToSqlData :: Param -> TH.Q TH.Exp
paramToSqlData :: Param -> Q Exp
paramToSqlData = \case
FieldParam Text
var ->
String -> Q (Maybe Name)
TH.lookupValueName (Text -> String
Text.unpack Text
var) Q (Maybe Name) -> (Maybe Name -> Q Exp) -> Q Exp
forall a b. Q a -> (a -> Q b) -> Q b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Maybe Name
Nothing -> String -> Q Exp
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String
"Not in scope: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Text -> String
Text.unpack Text
var)
Just Name
name -> [|[Sqlite.Simple.toField $(Name -> Q Exp
forall (m :: * -> *). Quote m => Name -> m Exp
TH.varE Name
name)]|]
RowParam Text
var Int
_count ->
String -> Q (Maybe Name)
TH.lookupValueName (Text -> String
Text.unpack Text
var) Q (Maybe Name) -> (Maybe Name -> Q Exp) -> Q Exp
forall a b. Q a -> (a -> Q b) -> Q b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Maybe Name
Nothing -> String -> Q Exp
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String
"Not in scope: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Text -> String
Text.unpack Text
var)
Just Name
name -> [|Sqlite.Simple.toRow $(Name -> Q Exp
forall (m :: * -> *). Quote m => Name -> m Exp
TH.varE Name
name)|]
innerLump :: Text -> TH.Q (TH.Exp, TH.Exp)
innerLump :: Text -> Q (Exp, Exp)
innerLump Text
var =
String -> Q (Maybe Name)
TH.lookupValueName (Text -> String
Text.unpack Text
var) Q (Maybe Name) -> (Maybe Name -> Q (Exp, Exp)) -> Q (Exp, Exp)
forall a b. Q a -> (a -> Q b) -> Q b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Maybe Name
Nothing -> String -> Q (Exp, Exp)
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String
"Not in scope: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Text -> String
Text.unpack Text
var)
Just Name
name -> (,) (Exp -> Exp -> (Exp, Exp)) -> Q Exp -> Q (Exp -> (Exp, Exp))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [|query__ $(Name -> Q Exp
forall (m :: * -> *). Quote m => Name -> m Exp
TH.varE Name
name)|] Q (Exp -> (Exp, Exp)) -> Q Exp -> Q (Exp, Exp)
forall a b. Q (a -> b) -> Q a -> Q b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> [|params__ $(Name -> Q Exp
forall (m :: * -> *). Quote m => Name -> m Exp
TH.varE Name
name)|]
inLump :: Text -> TH.Q (TH.Exp, TH.Exp)
inLump :: Text -> Q (Exp, Exp)
inLump Text
var =
String -> Q (Maybe Name)
TH.lookupValueName (Text -> String
Text.unpack Text
var) Q (Maybe Name) -> (Maybe Name -> Q (Exp, Exp)) -> Q (Exp, Exp)
forall a b. Q a -> (a -> Q b) -> Q b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Maybe Name
Nothing -> String -> Q (Exp, Exp)
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String
"Not in scope: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Text -> String
Text.unpack Text
var)
Just Name
name -> (,) (Exp -> Exp -> (Exp, Exp)) -> Q Exp -> Q (Exp -> (Exp, Exp))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [|inSql $(Name -> Q Exp
forall (m :: * -> *). Quote m => Name -> m Exp
TH.varE Name
name)|] Q (Exp -> (Exp, Exp)) -> Q Exp -> Q (Exp, Exp)
forall a b. Q (a -> b) -> Q a -> Q b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> [|map Sqlite.Simple.toField $(Name -> Q Exp
forall (m :: * -> *). Quote m => Name -> m Exp
TH.varE Name
name)|]
valuesLump :: Text -> TH.Q (TH.Exp, TH.Exp)
valuesLump :: Text -> Q (Exp, Exp)
valuesLump Text
var =
String -> Q (Maybe Name)
TH.lookupValueName (Text -> String
Text.unpack Text
var) Q (Maybe Name) -> (Maybe Name -> Q (Exp, Exp)) -> Q (Exp, Exp)
forall a b. Q a -> (a -> Q b) -> Q b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Maybe Name
Nothing -> String -> Q (Exp, Exp)
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String
"Not in scope: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Text -> String
Text.unpack Text
var)
Just Name
name -> (,) (Exp -> Exp -> (Exp, Exp)) -> Q Exp -> Q (Exp -> (Exp, Exp))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [|valuesSql $(Name -> Q Exp
forall (m :: * -> *). Quote m => Name -> m Exp
TH.varE Name
name)|] Q (Exp -> (Exp, Exp)) -> Q Exp -> Q (Exp, Exp)
forall a b. Q (a -> b) -> Q a -> Q b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> [|foldMap Sqlite.Simple.toRow $(Name -> Q Exp
forall (m :: * -> *). Quote m => Name -> m Exp
TH.varE Name
name)|]
inSql :: (Sqlite.Simple.ToField a) => [a] -> Text
inSql :: forall a. ToField a => [a] -> Text
inSql [a]
scalars =
TextBuilder -> Text
TextBuilder.toText (TextBuilder
"IN (" TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> [TextBuilder] -> TextBuilder
b_commaSep ((a -> TextBuilder) -> [a] -> [TextBuilder]
forall a b. (a -> b) -> [a] -> [b]
map (\a
_ -> TextBuilder
b_qmark) [a]
scalars) TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> TextBuilder
b_rparen)
valuesSql :: (Sqlite.Simple.ToRow a) => List.NonEmpty a -> Text
valuesSql :: forall a. ToRow a => NonEmpty a -> Text
valuesSql NonEmpty a
values =
TextBuilder -> Text
TextBuilder.toText (TextBuilder -> Text) -> TextBuilder -> Text
forall a b. (a -> b) -> a -> b
$
TextBuilder
"VALUES " TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> [TextBuilder] -> TextBuilder
b_commaSep (Int -> TextBuilder -> [TextBuilder]
forall a. Int -> a -> [a]
replicate (NonEmpty a -> Int
forall a. NonEmpty a -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length NonEmpty a
values) (Int -> TextBuilder
valueSql Int
columns))
where
columns :: Int
columns :: Int
columns =
[SQLData] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length (a -> [SQLData]
forall a. ToRow a => a -> [SQLData]
Sqlite.Simple.toRow (NonEmpty a -> a
forall a. NonEmpty a -> a
List.NonEmpty.head NonEmpty a
values))
valueSql :: Int -> TextBuilder
valueSql :: Int -> TextBuilder
valueSql Int
columns =
TextBuilder
b_lparen TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> [TextBuilder] -> TextBuilder
b_commaSep (Int -> TextBuilder -> [TextBuilder]
forall a. Int -> a -> [a]
replicate Int
columns TextBuilder
b_qmark) TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> TextBuilder
b_rparen
data ParsedLump
= ParsedOuterLump !Text ![Param]
| ParsedInnerLump !Text
| ParsedInLump !Text
| ParsedValuesLump !Text
deriving stock (ParsedLump -> ParsedLump -> Bool
(ParsedLump -> ParsedLump -> Bool)
-> (ParsedLump -> ParsedLump -> Bool) -> Eq ParsedLump
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ParsedLump -> ParsedLump -> Bool
== :: ParsedLump -> ParsedLump -> Bool
$c/= :: ParsedLump -> ParsedLump -> Bool
/= :: ParsedLump -> ParsedLump -> Bool
Eq, Int -> ParsedLump -> ShowS
[ParsedLump] -> ShowS
ParsedLump -> String
(Int -> ParsedLump -> ShowS)
-> (ParsedLump -> String)
-> ([ParsedLump] -> ShowS)
-> Show ParsedLump
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ParsedLump -> ShowS
showsPrec :: Int -> ParsedLump -> ShowS
$cshow :: ParsedLump -> String
show :: ParsedLump -> String
$cshowList :: [ParsedLump] -> ShowS
showList :: [ParsedLump] -> ShowS
Show)
internalParseSql :: Text -> Either String [ParsedLump]
internalParseSql :: Text -> Either String [ParsedLump]
internalParseSql Text
input =
case P () -> Text -> Either (ParseErrorBundle Text Void) ((), [Lump])
forall a.
P a -> Text -> Either (ParseErrorBundle Text Void) (a, [Lump])
runP (P ()
parser P () -> P () -> P ()
forall a b.
StateT [Lump] (Parsec Void Text) a
-> StateT [Lump] (Parsec Void Text) b
-> StateT [Lump] (Parsec Void Text) a
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* P ()
forall e s (m :: * -> *). MonadParsec e s m => m ()
Megaparsec.eof) (Text -> Text
Text.strip Text
input) of
Left ParseErrorBundle Text Void
err -> String -> Either String [ParsedLump]
forall a b. a -> Either a b
Left (ParseErrorBundle Text Void -> String
forall s e.
(VisualStream s, TraversableStream s, ShowErrorComponent e) =>
ParseErrorBundle s e -> String
Megaparsec.errorBundlePretty ParseErrorBundle Text Void
err)
Right ((), [Lump]
lumps) -> [ParsedLump] -> Either String [ParsedLump]
forall a b. b -> Either a b
Right ((Lump -> ParsedLump) -> [Lump] -> [ParsedLump]
forall a b. (a -> b) -> [a] -> [b]
map Lump -> ParsedLump
unlump ([Lump] -> [Lump]
forall a. [a] -> [a]
reverse [Lump]
lumps))
where
unlump :: Lump -> ParsedLump
unlump = \case
OuterLump TextBuilder
sql [Param]
params -> Text -> [Param] -> ParsedLump
ParsedOuterLump (TextBuilder -> Text
TextBuilder.toText TextBuilder
sql) ([Param] -> [Param]
forall a. [a] -> [a]
reverse [Param]
params)
InnerLump Text
query -> Text -> ParsedLump
ParsedInnerLump Text
query
InLump Text
param -> Text -> ParsedLump
ParsedInLump Text
param
ValuesLump Text
param -> Text -> ParsedLump
ParsedValuesLump Text
param
data Lump
= OuterLump !TextBuilder ![Param]
| InnerLump !Text
| InLump !Text
| ValuesLump !Text
data Param
= FieldParam !Text
| RowParam !Text !Int
deriving stock (Param -> Param -> Bool
(Param -> Param -> Bool) -> (Param -> Param -> Bool) -> Eq Param
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Param -> Param -> Bool
== :: Param -> Param -> Bool
$c/= :: Param -> Param -> Bool
/= :: Param -> Param -> Bool
Eq, Int -> Param -> ShowS
[Param] -> ShowS
Param -> String
(Int -> Param -> ShowS)
-> (Param -> String) -> ([Param] -> ShowS) -> Show Param
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Param -> ShowS
showsPrec :: Int -> Param -> ShowS
$cshow :: Param -> String
show :: Param -> String
$cshowList :: [Param] -> ShowS
showList :: [Param] -> ShowS
Show)
type P a =
State.StateT [Lump] (Megaparsec.Parsec Void Text) a
runP :: P a -> Text -> Either (Megaparsec.ParseErrorBundle Text Void) (a, [Lump])
runP :: forall a.
P a -> Text -> Either (ParseErrorBundle Text Void) (a, [Lump])
runP P a
p =
Parsec Void Text (a, [Lump])
-> String
-> Text
-> Either (ParseErrorBundle Text Void) (a, [Lump])
forall e s a.
Parsec e s a -> String -> s -> Either (ParseErrorBundle s e) a
Megaparsec.runParser (P a -> [Lump] -> Parsec Void Text (a, [Lump])
forall s (m :: * -> *) a. StateT s m a -> s -> m (a, s)
State.runStateT P a
p []) String
""
parser :: P ()
parser :: P ()
parser = do
P Fragment
fragmentParser P Fragment -> (Fragment -> P ()) -> P ()
forall a b.
StateT [Lump] (Parsec Void Text) a
-> (a -> StateT [Lump] (Parsec Void Text) b)
-> StateT [Lump] (Parsec Void Text) b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Fragment
Comment -> P ()
parser
NonParam TextBuilder
fragment -> TextBuilder -> ([Param] -> P [Param]) -> P ()
outer TextBuilder
fragment [Param] -> P [Param]
forall a. a -> StateT [Lump] (Parsec Void Text) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure
AtParam TextBuilder
param ->
TextBuilder -> ([Param] -> P [Param]) -> P ()
outer
TextBuilder
b_qmark
let param1 :: Text
param1 = TextBuilder -> Text
TextBuilder.toText TextBuilder
param
in if Text -> Bool
Text.null Text
param1
then \case
RowParam Text
name Int
count : [Param]
params -> do
let !count' :: Int
count' = Int
count Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1
[Param] -> P [Param]
forall a. a -> StateT [Lump] (Parsec Void Text) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Text -> Int -> Param
RowParam Text
name Int
count' Param -> [Param] -> [Param]
forall a. a -> [a] -> [a]
: [Param]
params)
[Param]
_ -> String -> P [Param]
forall a. String -> StateT [Lump] (Parsec Void Text) a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String
"Invalid query: encountered unnamed-@ without a preceding named-@, like `@foo`")
else \[Param]
params -> [Param] -> P [Param]
forall a. a -> StateT [Lump] (Parsec Void Text) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Text -> Int -> Param
RowParam Text
param1 Int
1 Param -> [Param] -> [Param]
forall a. a -> [a] -> [a]
: [Param]
params)
ColonParam TextBuilder
param -> TextBuilder -> ([Param] -> P [Param]) -> P ()
outer TextBuilder
b_qmark \[Param]
params -> [Param] -> P [Param]
forall a. a -> StateT [Lump] (Parsec Void Text) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Text -> Param
FieldParam (TextBuilder -> Text
TextBuilder.toText TextBuilder
param) Param -> [Param] -> [Param]
forall a. a -> [a] -> [a]
: [Param]
params)
DollarParam TextBuilder
param -> do
([Lump] -> [Lump]) -> P ()
forall (m :: * -> *) s. Monad m => (s -> s) -> StateT s m ()
State.modify' (Text -> Lump
InnerLump (TextBuilder -> Text
TextBuilder.toText TextBuilder
param) Lump -> [Lump] -> [Lump]
forall a. a -> [a] -> [a]
:)
P ()
parser
InParam TextBuilder
param -> do
([Lump] -> [Lump]) -> P ()
forall (m :: * -> *) s. Monad m => (s -> s) -> StateT s m ()
State.modify' (Text -> Lump
InLump (TextBuilder -> Text
TextBuilder.toText TextBuilder
param) Lump -> [Lump] -> [Lump]
forall a. a -> [a] -> [a]
:)
P ()
parser
ValuesParam TextBuilder
param -> do
([Lump] -> [Lump]) -> P ()
forall (m :: * -> *) s. Monad m => (s -> s) -> StateT s m ()
State.modify' (Text -> Lump
ValuesLump (TextBuilder -> Text
TextBuilder.toText TextBuilder
param) Lump -> [Lump] -> [Lump]
forall a. a -> [a] -> [a]
:)
P ()
parser
Fragment
Whitespace -> TextBuilder -> ([Param] -> P [Param]) -> P ()
outer (Char -> TextBuilder
TextBuilder.char Char
' ') [Param] -> P [Param]
forall a. a -> StateT [Lump] (Parsec Void Text) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure
Fragment
EndOfInput -> () -> P ()
forall a. a -> StateT [Lump] (Parsec Void Text) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
where
outer :: TextBuilder -> ([Param] -> P [Param]) -> P ()
outer :: TextBuilder -> ([Param] -> P [Param]) -> P ()
outer TextBuilder
s [Param] -> P [Param]
g = do
StateT [Lump] (Parsec Void Text) [Lump]
forall (m :: * -> *) s. Monad m => StateT s m s
State.get StateT [Lump] (Parsec Void Text) [Lump] -> ([Lump] -> P ()) -> P ()
forall a b.
StateT [Lump] (Parsec Void Text) a
-> (a -> StateT [Lump] (Parsec Void Text) b)
-> StateT [Lump] (Parsec Void Text) b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
OuterLump TextBuilder
sql [Param]
params : [Lump]
lumps -> do
let !sql' :: TextBuilder
sql' = TextBuilder
sql TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> TextBuilder
s
params' <- [Param] -> P [Param]
g [Param]
params
State.put (OuterLump sql' params' : lumps)
[Lump]
lumps -> do
params <- [Param] -> P [Param]
g []
State.put (OuterLump s params : lumps)
P ()
parser
data Fragment
=
| NonParam !TextBuilder
| AtParam !TextBuilder
| ColonParam !TextBuilder
| DollarParam !TextBuilder
| InParam !TextBuilder
| ValuesParam !TextBuilder
| Whitespace
| EndOfInput
fragmentParser :: P Fragment
fragmentParser :: P Fragment
fragmentParser =
[P Fragment] -> P Fragment
forall (t :: * -> *) (f :: * -> *) a.
(Foldable t, Alternative f) =>
t (f a) -> f a
asum
[ Fragment
Whitespace Fragment -> P () -> P Fragment
forall a b.
a
-> StateT [Lump] (Parsec Void Text) b
-> StateT [Lump] (Parsec Void Text) a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ P ()
whitespaceP,
TextBuilder -> Fragment
NonParam (TextBuilder -> Fragment)
-> StateT [Lump] (Parsec Void Text) TextBuilder -> P Fragment
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> Char -> StateT [Lump] (Parsec Void Text) TextBuilder
betwixt String
"string" Char
'\'',
TextBuilder -> Fragment
NonParam (TextBuilder -> Fragment)
-> StateT [Lump] (Parsec Void Text) TextBuilder -> P Fragment
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> Char -> StateT [Lump] (Parsec Void Text) TextBuilder
betwixt String
"identifier" Char
'"',
TextBuilder -> Fragment
NonParam (TextBuilder -> Fragment)
-> StateT [Lump] (Parsec Void Text) TextBuilder -> P Fragment
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> Char -> StateT [Lump] (Parsec Void Text) TextBuilder
betwixt String
"identifier" Char
'`',
TextBuilder -> Fragment
NonParam (TextBuilder -> Fragment)
-> StateT [Lump] (Parsec Void Text) TextBuilder -> P Fragment
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> StateT [Lump] (Parsec Void Text) TextBuilder
bracketedIdentifierP,
Fragment
Comment Fragment -> P () -> P Fragment
forall a b.
a
-> StateT [Lump] (Parsec Void Text) b
-> StateT [Lump] (Parsec Void Text) a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ P ()
lineCommentP,
Fragment
Comment Fragment -> P () -> P Fragment
forall a b.
a
-> StateT [Lump] (Parsec Void Text) b
-> StateT [Lump] (Parsec Void Text) a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ P ()
blockCommentP,
TextBuilder -> Fragment
ColonParam (TextBuilder -> Fragment)
-> StateT [Lump] (Parsec Void Text) TextBuilder -> P Fragment
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> StateT [Lump] (Parsec Void Text) TextBuilder
colonParamP,
TextBuilder -> Fragment
AtParam (TextBuilder -> Fragment)
-> StateT [Lump] (Parsec Void Text) TextBuilder -> P Fragment
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> StateT [Lump] (Parsec Void Text) TextBuilder
atParamP,
TextBuilder -> Fragment
DollarParam (TextBuilder -> Fragment)
-> StateT [Lump] (Parsec Void Text) TextBuilder -> P Fragment
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> StateT [Lump] (Parsec Void Text) TextBuilder
dollarParamP,
TextBuilder -> Fragment
InParam (TextBuilder -> Fragment)
-> StateT [Lump] (Parsec Void Text) TextBuilder -> P Fragment
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> StateT [Lump] (Parsec Void Text) TextBuilder
inParamP,
TextBuilder -> Fragment
ValuesParam (TextBuilder -> Fragment)
-> StateT [Lump] (Parsec Void Text) TextBuilder -> P Fragment
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> StateT [Lump] (Parsec Void Text) TextBuilder
valuesParamP,
TextBuilder -> Fragment
NonParam (TextBuilder -> Fragment)
-> StateT [Lump] (Parsec Void Text) TextBuilder -> P Fragment
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> StateT [Lump] (Parsec Void Text) TextBuilder
unstructuredP,
Fragment
EndOfInput Fragment -> P () -> P Fragment
forall a b.
a
-> StateT [Lump] (Parsec Void Text) b
-> StateT [Lump] (Parsec Void Text) a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ P ()
forall e s (m :: * -> *). MonadParsec e s m => m ()
Megaparsec.eof
]
where
bracketedIdentifierP :: P TextBuilder
bracketedIdentifierP :: StateT [Lump] (Parsec Void Text) TextBuilder
bracketedIdentifierP = do
x <- Char -> StateT [Lump] (Parsec Void Text) TextBuilder
char Char
'['
ys <- Megaparsec.takeWhile1P (Just "identifier") (/= ']')
z <- char ']'
pure (x <> TextBuilder.text ys <> z)
lineCommentP :: P ()
lineCommentP :: P ()
lineCommentP = do
_ <- Tokens Text -> StateT [Lump] (Parsec Void Text) (Tokens Text)
forall e s (m :: * -> *).
MonadParsec e s m =>
Tokens s -> m (Tokens s)
Megaparsec.string Tokens Text
"--"
_ <- Megaparsec.takeWhileP (Just "comment") (/= '\n')
whitespaceP
blockCommentP :: P ()
blockCommentP :: P ()
blockCommentP = do
_ <- Tokens Text -> StateT [Lump] (Parsec Void Text) (Tokens Text)
forall e s (m :: * -> *).
MonadParsec e s m =>
Tokens s -> m (Tokens s)
Megaparsec.string Tokens Text
"/*"
let loop = do
_ <- Maybe String
-> (Token Text -> Bool)
-> StateT [Lump] (Parsec Void Text) (Tokens Text)
forall e s (m :: * -> *).
MonadParsec e s m =>
Maybe String -> (Token s -> Bool) -> m (Tokens s)
Megaparsec.takeWhileP (String -> Maybe String
forall a. a -> Maybe a
Just String
"comment") (Token Text -> Token Text -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
Token Text
'*')
Megaparsec.string "*/" <|> (Megaparsec.anySingle >> loop)
_ <- loop
whitespaceP
unstructuredP :: P TextBuilder
unstructuredP :: StateT [Lump] (Parsec Void Text) TextBuilder
unstructuredP = do
x <- StateT [Lump] (Parsec Void Text) Char
StateT [Lump] (Parsec Void Text) (Token Text)
forall e s (m :: * -> *). MonadParsec e s m => m (Token s)
Megaparsec.anySingle
xs <-
Megaparsec.takeWhileP
(Just "sql")
\Token Text
c ->
Bool -> Bool
not (Char -> Bool
Char.isSpace Char
Token Text
c)
Bool -> Bool -> Bool
&& Char
Token Text
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'\''
Bool -> Bool -> Bool
&& Char
Token Text
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'"'
Bool -> Bool -> Bool
&& Char
Token Text
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
':'
Bool -> Bool -> Bool
&& Char
Token Text
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'@'
Bool -> Bool -> Bool
&& Char
Token Text
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'$'
Bool -> Bool -> Bool
&& Char
Token Text
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'`'
Bool -> Bool -> Bool
&& Char
Token Text
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'['
Bool -> Bool -> Bool
&& Char
Token Text
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'-'
Bool -> Bool -> Bool
&& Char
Token Text
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'/'
Bool -> Bool -> Bool
&& Char
Token Text
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'I'
Bool -> Bool -> Bool
&& Char
Token Text
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'V'
pure (TextBuilder.char x <> TextBuilder.text xs)
atParamP :: P TextBuilder
atParamP :: StateT [Lump] (Parsec Void Text) TextBuilder
atParamP = do
_ <- Token Text -> StateT [Lump] (Parsec Void Text) (Token Text)
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
Token s -> m (Token s)
Megaparsec.char Char
Token Text
'@'
haskellVariableP <|> pure mempty
colonParamP :: P TextBuilder
colonParamP :: StateT [Lump] (Parsec Void Text) TextBuilder
colonParamP = do
_ <- Token Text -> StateT [Lump] (Parsec Void Text) (Token Text)
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
Token s -> m (Token s)
Megaparsec.char Char
Token Text
':'
haskellVariableP
dollarParamP :: P TextBuilder
dollarParamP :: StateT [Lump] (Parsec Void Text) TextBuilder
dollarParamP = do
_ <- Token Text -> StateT [Lump] (Parsec Void Text) (Token Text)
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
Token s -> m (Token s)
Megaparsec.char Char
Token Text
'$'
haskellVariableP
inParamP :: P TextBuilder
inParamP :: StateT [Lump] (Parsec Void Text) TextBuilder
inParamP = do
StateT [Lump] (Parsec Void Text) TextBuilder
-> StateT [Lump] (Parsec Void Text) TextBuilder
forall a.
StateT [Lump] (Parsec Void Text) a
-> StateT [Lump] (Parsec Void Text) a
forall e s (m :: * -> *) a. MonadParsec e s m => m a -> m a
Megaparsec.try do
_ <- Tokens Text -> StateT [Lump] (Parsec Void Text) (Tokens Text)
forall e s (m :: * -> *).
MonadParsec e s m =>
Tokens s -> m (Tokens s)
Megaparsec.string Tokens Text
"IN"
whitespaceP
colonParamP
valuesParamP :: P TextBuilder
valuesParamP :: StateT [Lump] (Parsec Void Text) TextBuilder
valuesParamP = do
StateT [Lump] (Parsec Void Text) TextBuilder
-> StateT [Lump] (Parsec Void Text) TextBuilder
forall a.
StateT [Lump] (Parsec Void Text) a
-> StateT [Lump] (Parsec Void Text) a
forall e s (m :: * -> *) a. MonadParsec e s m => m a -> m a
Megaparsec.try do
_ <- Tokens Text -> StateT [Lump] (Parsec Void Text) (Tokens Text)
forall e s (m :: * -> *).
MonadParsec e s m =>
Tokens s -> m (Tokens s)
Megaparsec.string Tokens Text
"VALUES"
whitespaceP
colonParamP
haskellVariableP :: P TextBuilder
haskellVariableP :: StateT [Lump] (Parsec Void Text) TextBuilder
haskellVariableP = do
x <- (Token Text -> Bool)
-> StateT [Lump] (Parsec Void Text) (Token Text)
forall e s (m :: * -> *).
MonadParsec e s m =>
(Token s -> Bool) -> m (Token s)
Megaparsec.satisfy (\Token Text
c -> Char -> Bool
Char.isAlpha Char
Token Text
c Bool -> Bool -> Bool
|| Char
Token Text
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'_')
xs <- Megaparsec.takeWhileP (Just "parameter") \Token Text
c -> Char -> Bool
Char.isAlphaNum Char
Token Text
c Bool -> Bool -> Bool
|| Char
Token Text
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'_' Bool -> Bool -> Bool
|| Char
Token Text
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'\''
pure (TextBuilder.char x <> TextBuilder.text xs)
whitespaceP :: P ()
whitespaceP :: P ()
whitespaceP = do
StateT [Lump] (Parsec Void Text) (Tokens Text) -> P ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (Maybe String
-> (Token Text -> Bool)
-> StateT [Lump] (Parsec Void Text) (Tokens Text)
forall e s (m :: * -> *).
MonadParsec e s m =>
Maybe String -> (Token s -> Bool) -> m (Tokens s)
Megaparsec.takeWhile1P (String -> Maybe String
forall a. a -> Maybe a
Just String
"whitepsace") Char -> Bool
Token Text -> Bool
Char.isSpace)
betwixt :: String -> Char -> P TextBuilder
betwixt :: String -> Char -> StateT [Lump] (Parsec Void Text) TextBuilder
betwixt String
name Char
quote = do
startQuote <- StateT [Lump] (Parsec Void Text) TextBuilder
quoteP
let loop TextBuilder
sofar = do
content <- Maybe String
-> (Token Text -> Bool)
-> StateT [Lump] (Parsec Void Text) (Tokens Text)
forall e s (m :: * -> *).
MonadParsec e s m =>
Maybe String -> (Token s -> Bool) -> m (Tokens s)
Megaparsec.takeWhileP (String -> Maybe String
forall a. a -> Maybe a
Just String
name) (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
quote)
Megaparsec.notFollowedBy Megaparsec.eof
let escapedQuoteAndMore = do
escapedQuote <- StateT [Lump] (Parsec Void Text) TextBuilder
-> StateT [Lump] (Parsec Void Text) TextBuilder
forall a.
StateT [Lump] (Parsec Void Text) a
-> StateT [Lump] (Parsec Void Text) a
forall e s (m :: * -> *) a. MonadParsec e s m => m a -> m a
Megaparsec.try (TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
(<>) (TextBuilder -> TextBuilder -> TextBuilder)
-> StateT [Lump] (Parsec Void Text) TextBuilder
-> StateT [Lump] (Parsec Void Text) (TextBuilder -> TextBuilder)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> StateT [Lump] (Parsec Void Text) TextBuilder
quoteP StateT [Lump] (Parsec Void Text) (TextBuilder -> TextBuilder)
-> StateT [Lump] (Parsec Void Text) TextBuilder
-> StateT [Lump] (Parsec Void Text) TextBuilder
forall a b.
StateT [Lump] (Parsec Void Text) (a -> b)
-> StateT [Lump] (Parsec Void Text) a
-> StateT [Lump] (Parsec Void Text) b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> StateT [Lump] (Parsec Void Text) TextBuilder
quoteP)
loop (sofar <> TextBuilder.text content <> escapedQuote)
let allDone = do
endQuote <- StateT [Lump] (Parsec Void Text) TextBuilder
quoteP
pure (sofar <> TextBuilder.text content <> endQuote)
escapedQuoteAndMore <|> allDone
loop startQuote
where
quoteP :: StateT [Lump] (Parsec Void Text) TextBuilder
quoteP =
Char -> StateT [Lump] (Parsec Void Text) TextBuilder
char Char
quote
char :: Char -> P TextBuilder
char :: Char -> StateT [Lump] (Parsec Void Text) TextBuilder
char Char
c =
Token Text -> StateT [Lump] (Parsec Void Text) (Token Text)
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
Token s -> m (Token s)
Megaparsec.char Char
Token Text
c StateT [Lump] (Parsec Void Text) Char
-> TextBuilder -> StateT [Lump] (Parsec Void Text) TextBuilder
forall (f :: * -> *) a b. Functor f => f a -> b -> f b
$> Char -> TextBuilder
TextBuilder.char Char
c
b_qmark :: TextBuilder
b_qmark :: TextBuilder
b_qmark = Char -> TextBuilder
TextBuilder.char Char
'?'
b_lparen :: TextBuilder
b_lparen :: TextBuilder
b_lparen = Char -> TextBuilder
TextBuilder.char Char
'('
b_rparen :: TextBuilder
b_rparen :: TextBuilder
b_rparen = Char -> TextBuilder
TextBuilder.char Char
')'
b_commaSep :: [TextBuilder] -> TextBuilder
b_commaSep :: [TextBuilder] -> TextBuilder
b_commaSep =
TextBuilder -> [TextBuilder] -> TextBuilder
forall (f :: * -> *).
Foldable f =>
TextBuilder -> f TextBuilder -> TextBuilder
TextBuilder.intercalate (Text -> TextBuilder
TextBuilder.text Text
", ")