{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE NoPolyKinds #-}
{- | Description : quasiquoter inspired by -XNamedFieldPuns -}
module Data.HList.RecordPuns (
    -- $ex
    pun

    ) where

import Language.Haskell.TH
import Language.Haskell.TH.Quote
import Data.HList.Record
import Data.HList.FakePrelude
import Data.List
import Data.HList.HList

{- $ex

>>> :set -XQuasiQuotes -XViewPatterns

[@patterns@]

>>> let y = Label :: Label "y"
>>> let x = Label :: Label "x"
>>> [pun| x y |] <- return (x .=. 3 .*. y .=. "hi" .*. emptyRecord)
>>> print (x,y)
(3,"hi")

[@expressions@]

Compare with the standard way to construct records above

>>> let x = 3; y = "hi"
>>> [pun|x y|]
Record{x=3,y="hi"}

[@nesting@]

Nesting is supported. Variables inside
@{ }@ and @( )@ are one level deeper, like the built-in syntax.
Furthermore the outer @{ }@ can be left out because @[pun|{x}|]@ is more
cluttered than @[pun|x|]@.
More concretely the pattern:


> let [pun| ab@{ a b } y z c{d} |] = x

is short for:

> let ab = x.ab
>     a = x.ab.a
>     b = x.ab.b
>     y = x.y
>     z = x.z
>     -- c is not bound
>     d = x.c.d

Where here `.` is a left-associative field lookup (as it is in other languages).

The pun quasiquoter can also be used in an expression context:

> let mkX ab a b y z d = [pun| ab@{ a b } y z c{d} |]
>     x = mkX ab b y z d

Here `mkX` includes @ab a b y z d@. @ab@ needs to be a record, and if it has
fields called @a@ or @b@ they are overridden by the values of @a@ and @b@ (via
'hLeftUnion' = '.<++.') . In other words,

> let mkX ab_ a b y z d = let ab = [pun| a b |] .<++. ab_
>                               in [pun| ab y z c{d} |]

For patterns, any order and additional fields are allowed if @{ }@ is used,
just as in built-in record syntax. But it is often necessary to restrict the
order and number of fields, such as if the record is a 'hRearrange' of a 'hLeftUnion'.
So use @( )@ instead:

> let [pun| (x _ y{}) |] = list
> -- desugars to something like:
> Record ((Tagged x :: Tagged "x" s1) `HCons`
>         (Tagged _ :: Tagged t   s2) `HCons`
>         (Tagged _ :: Tagged "y" s3) `HCons`
>          HNil) = list

Note that this also introduces the familiar wild card pattern (@_@),
and shows again how to ensure a label is present but not bind a variable
to it.

For comparison, here are three equivalent ways to define variables `x` and `y`

> let [pun| x y{} |] = r
> let [pun|{ x y{} }|] = r -- or this
> let x = r .!. (Label :: Label "x")
>     y = constrainType (r .!. (Label :: Label "y"))
>     constrainType :: Record t -> Record t
>     constrainType = id

See also @examples/pun.hs@. In @{}@ patterns, @pun@ can work with
'Variant' too.

-}


-- | requires labels to be promoted strings (kind Symbol), as provided by
-- "Data.HList.Label6" (ie. the label for foo is @Label :: Label \"foo\"@),
-- or "Data.HList.Labelable"
pun :: QuasiQuoter
pun :: QuasiQuoter
pun = QuasiQuoter {
    quotePat :: String -> Q Pat
quotePat = forall {t}. (Tree -> t) -> Tree -> t
suppressWarning Tree -> Q Pat
mp forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Tree
parseRec,
    quoteExp :: String -> Q Exp
quoteExp = forall {t}. (Tree -> t) -> Tree -> t
suppressWarning Tree -> Q Exp
me forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Tree
parseRec,
    quoteDec :: String -> Q [Dec]
quoteDec  = forall a. HasCallStack => String -> a
error String
"Data.HList.RecordPuns.quoteDec",
    quoteType :: String -> Q Type
quoteType = forall a. HasCallStack => String -> a
error String
"Data.HList.RecordPuns.quoteType"
 }


-- | the warning about @implicit {} added@ doesn't
-- make sense at top level (but it does if you say
-- have  [pun| x @ y |]
suppressWarning :: (Tree -> t) -> Tree -> t
suppressWarning Tree -> t
f (V String
a) = Tree -> t
f ([Tree] -> Tree
C [String -> Tree
V String
a])
suppressWarning Tree -> t
f Tree
x = Tree -> t
f Tree
x

-- extracts ["x1","x2"] becomes \x -> (x .!. x1, x .!. x2),
-- where x1 = Label :: Label "x1"
extracts :: [String] -> m Exp
extracts [String]
xs = do
    Name
record <- forall (m :: * -> *). Quote m => String -> m Name
newName String
"record"
    -- to fix #5 I could comment out the ensureLength below
    forall (m :: * -> *). Quote m => [m Pat] -> m Exp -> m Exp
lamE [forall (m :: * -> *). Quote m => Name -> m Pat
varP Name
record] forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *). Quote m => [m Exp] -> m Exp
tupE
            [ [| $(varE record) .!. $label  |]
                | String
x <- [String]
xs,
                let label :: m Exp
label = [| Label :: Label $(litT (strTyLit x)) |],
                String
x forall a. Eq a => a -> a -> Bool
/= String
"_"
                ]

mkPair :: String -> ExpQ -> ExpQ
mkPair :: String -> Q Exp -> Q Exp
mkPair String
x Q Exp
xe = [| (Label :: Label $(litT (strTyLit x))) .=. $xe |]



me :: Tree -> ExpQ
me :: Tree -> Q Exp
me (C [Tree]
as) = forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (\(String
l,Q Exp
e) Q Exp
acc -> [| $(mkPair l e) .*. $acc |]) [| emptyRecord |] ([Tree] -> [(String, Q Exp)]
mes [Tree]
as)
me (D [Tree]
_as) = forall a. HasCallStack => String -> a
error String
"Data.HList.RecordPuns.mp impossible"
me Tree
a = do
    String -> Q ()
reportWarning forall a b. (a -> b) -> a -> b
$ String
"Data.HList.RecordPuns.mp implicit {} added around:" forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show Tree
a
    Tree -> Q Exp
me ([Tree] -> Tree
C [Tree
a])

mes :: [Tree] -> [(String, ExpQ)]
mes :: [Tree] -> [(String, Q Exp)]
mes (V String
a : V String
"@": Tree
b : [Tree]
c) = (String
a, [| $(me b) `hLeftUnion` $(dyn a) |]) forall a. a -> [a] -> [a]
: [Tree] -> [(String, Q Exp)]
mes [Tree]
c
mes (V String
a : C [Tree]
b : [Tree]
c)      = (String
a, Tree -> Q Exp
me ([Tree] -> Tree
C [Tree]
b)) forall a. a -> [a] -> [a]
: [Tree] -> [(String, Q Exp)]
mes [Tree]
c
mes (V String
a : D [Tree]
b : [Tree]
c)      = (String
a, Tree -> Q Exp
me ([Tree] -> Tree
C [Tree]
b)) forall a. a -> [a] -> [a]
: [Tree] -> [(String, Q Exp)]
mes [Tree]
c
mes (V String
a : [Tree]
b)            = (String
a, forall (m :: * -> *). Quote m => Name -> m Exp
varE (String -> Name
mkName String
a)) forall a. a -> [a] -> [a]
: [Tree] -> [(String, Q Exp)]
mes [Tree]
b
mes [] = []
mes [Tree]
inp = forall a. HasCallStack => String -> a
error forall a b. (a -> b) -> a -> b
$ String
"Data.HList.RecordPuns.mes: cannot translate remaining:" forall a. [a] -> [a] -> [a]
++
                        forall a. Show a => a -> String
show (forall a b. (a -> b) -> [a] -> [b]
map Tree -> String
ppTree [Tree]
inp)

mp :: Tree -> PatQ
mp :: Tree -> Q Pat
mp (C [Tree]
as) =
    let extractPats :: [(String, Q Pat)]
extractPats = [Tree] -> [(String, Q Pat)]
mps [Tree]
as
        tupleP :: Q Pat
tupleP = forall (m :: * -> *). Quote m => [m Pat] -> m Pat
tupP [ Q Pat
p | (String
binding, Q Pat
p) <- [(String, Q Pat)]
extractPats, String
binding forall a. Eq a => a -> a -> Bool
/= String
"_" ]
    in forall (m :: * -> *). Quote m => m Exp -> m Pat -> m Pat
viewP (forall {m :: * -> *}. Quote m => [String] -> m Exp
extracts (forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> a
fst [(String, Q Pat)]
extractPats)) Q Pat
tupleP


mp (D [Tree]
as) = forall (m :: * -> *). Quote m => Name -> [m Pat] -> m Pat
conP 'Record
  [forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr ( \ (String
n,Q Pat
p) Q Pat
xs -> forall (m :: * -> *). Quote m => Name -> [m Pat] -> m Pat
conP 'HCons
                [ let ty :: Q Exp
ty
                          | String
n forall a. Eq a => a -> a -> Bool
== String
"_"  = [| undefined :: Tagged anyLabel t |]
                          | Bool
otherwise = [| undefined :: Tagged $(litT (strTyLit n)) t |]
                  in forall (m :: * -> *). Quote m => m Exp -> m Pat -> m Pat
viewP [| \x -> x `asTypeOf` $ty |]
                      (forall (m :: * -> *). Quote m => Name -> [m Pat] -> m Pat
conP 'Tagged [Q Pat
p]),
                Q Pat
xs])
          (forall (m :: * -> *). Quote m => Name -> [m Pat] -> m Pat
conP 'HNil [])
          ([Tree] -> [(String, Q Pat)]
mps [Tree]
as)]
mp Tree
a = do
    String -> Q ()
reportWarning forall a b. (a -> b) -> a -> b
$ String
"Data.HList.RecordPuns.mp implicit {} added around:" forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show Tree
a
    Tree -> Q Pat
mp ([Tree] -> Tree
C [Tree
a])

mps :: [Tree] -> [(String, PatQ)]
mps :: [Tree] -> [(String, Q Pat)]
mps (V String
a : V String
"@" : Tree
b : [Tree]
c) = (String
a, forall (m :: * -> *). Quote m => Name -> m Pat -> m Pat
asP (String -> Name
mkName String
a) (Tree -> Q Pat
mp Tree
b)) forall a. a -> [a] -> [a]
:  [Tree] -> [(String, Q Pat)]
mps [Tree]
c
mps (V String
a : C [Tree]
b : [Tree]
c) = (String
a, Tree -> Q Pat
mp ([Tree] -> Tree
C [Tree]
b)) forall a. a -> [a] -> [a]
: [Tree] -> [(String, Q Pat)]
mps [Tree]
c
mps (V String
a : D [Tree]
b : [Tree]
c) = (String
a, Tree -> Q Pat
mp ([Tree] -> Tree
D [Tree]
b)) forall a. a -> [a] -> [a]
: [Tree] -> [(String, Q Pat)]
mps [Tree]
c
mps (V String
"_" : [Tree]
b) = (String
"_", forall (m :: * -> *). Quote m => m Pat
wildP) forall a. a -> [a] -> [a]
: [Tree] -> [(String, Q Pat)]
mps [Tree]
b
mps (V String
a : [Tree]
b) = (String
a, forall (m :: * -> *). Quote m => Name -> m Pat
varP (String -> Name
mkName String
a)) forall a. a -> [a] -> [a]
: [Tree] -> [(String, Q Pat)]
mps [Tree]
b
mps [] = []
mps [Tree]
inp = forall a. HasCallStack => String -> a
error forall a b. (a -> b) -> a -> b
$ String
"Data.HList.RecordPuns.mps: cannot translate remaining pattern:" forall a. [a] -> [a] -> [a]
++
                        forall a. Show a => a -> String
show (forall a b. (a -> b) -> [a] -> [b]
map Tree -> String
ppTree [Tree]
inp)

data Tree = C [Tree] -- ^ curly @{ }@
          | D [Tree] -- ^ @(  )@
          | V String -- ^ variable
  deriving Int -> Tree -> ShowS
[Tree] -> ShowS
Tree -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Tree] -> ShowS
$cshowList :: [Tree] -> ShowS
show :: Tree -> String
$cshow :: Tree -> String
showsPrec :: Int -> Tree -> ShowS
$cshowsPrec :: Int -> Tree -> ShowS
Show

{- |

>>> parseRec "{ a b c {d e f}  } d"
C [C [V "a",V "b",V "c",C [V "d",V "e",V "f"]],V "d"]

>>> ppTree $ parseRec "{a b c {d e {} f @ g}}"
"{a b c {d e {} f @ g}}"

>>> ppTree $ parseRec "a b c {d e {} f @ g}"
"{a b c {d e {} f @ g}}"

>>> ppTree $ parseRec "(a b { (d) e } )"
"(a b {(d) e})"

-}
parseRec :: String -> Tree
parseRec :: String -> Tree
parseRec String
str = case Int -> Int -> [[Tree]] -> [String] -> [Tree]
parseRec' Int
0 Int
0 [[]] forall a b. (a -> b) -> a -> b
$ String -> [String]
lexing String
str of
    [Tree
x] -> Tree
x -- avoid adding another layer if possible
    [Tree]
x -> [Tree] -> Tree
C (forall a. [a] -> [a]
reverse [Tree]
x)

parseRec' :: Int -> Int -> [[Tree]] -> [String] -> [Tree]
parseRec' :: Int -> Int -> [[Tree]] -> [String] -> [Tree]
parseRec' Int
n Int
m [[Tree]]
accum  (String
"{" : [String]
rest)  = Int -> Int -> [[Tree]] -> [String] -> [Tree]
parseRec' (Int
nforall a. Num a => a -> a -> a
+Int
1) Int
m ([] forall a. a -> [a] -> [a]
: [[Tree]]
accum) [String]
rest
parseRec' Int
n Int
m [[Tree]]
accum  (String
"(" : [String]
rest)  = Int -> Int -> [[Tree]] -> [String] -> [Tree]
parseRec'  Int
n (Int
mforall a. Num a => a -> a -> a
+Int
1) ([] forall a. a -> [a] -> [a]
: [[Tree]]
accum) [String]
rest
parseRec' Int
n Int
m ([Tree]
a:[Tree]
b:[[Tree]]
c) (String
"}" : [String]
rest) = Int -> Int -> [[Tree]] -> [String] -> [Tree]
parseRec' (Int
nforall a. Num a => a -> a -> a
-Int
1) Int
m (([Tree] -> Tree
C (forall a. [a] -> [a]
reverse [Tree]
a) forall a. a -> [a] -> [a]
: [Tree]
b) forall a. a -> [a] -> [a]
: [[Tree]]
c)  [String]
rest
parseRec' Int
n Int
m ([Tree]
a:[Tree]
b:[[Tree]]
c) (String
")" : [String]
rest) = Int -> Int -> [[Tree]] -> [String] -> [Tree]
parseRec' Int
n (Int
mforall a. Num a => a -> a -> a
-Int
1) (([Tree] -> Tree
D (forall a. [a] -> [a]
reverse [Tree]
a) forall a. a -> [a] -> [a]
: [Tree]
b) forall a. a -> [a] -> [a]
: [[Tree]]
c)  [String]
rest
parseRec' Int
n Int
m ([Tree]
b:[[Tree]]
c) (String
a   : [String]
rest)
         | String
a forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [String
"{",String
"}",String
"(",String
")"] = Int -> Int -> [[Tree]] -> [String] -> [Tree]
parseRec' Int
n Int
m   ((String -> Tree
V String
a forall a. a -> [a] -> [a]
: [Tree]
b) forall a. a -> [a] -> [a]
: [[Tree]]
c) [String]
rest
parseRec' Int
0 Int
0 ([Tree]
a:[[Tree]]
_) []             = [Tree]
a
parseRec' Int
_ Int
_ [[Tree]]
accum [String]
e              = forall a. HasCallStack => String -> a
error (String
"Data.HList.RecordPuns.parseRec' unexpected: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show [String]
e
                                            forall a. [a] -> [a] -> [a]
++ String
"\n parsed:" forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show (forall a. [a] -> [a]
reverse [[Tree]]
accum))

ppTree :: Tree -> String
ppTree :: Tree -> String
ppTree (C [Tree]
ts) = String
"{" forall a. [a] -> [a] -> [a]
++ [String] -> String
unwords (forall a b. (a -> b) -> [a] -> [b]
map Tree -> String
ppTree [Tree]
ts) forall a. [a] -> [a] -> [a]
++ String
"}"
ppTree (D [Tree]
ts) = String
"(" forall a. [a] -> [a] -> [a]
++ [String] -> String
unwords (forall a b. (a -> b) -> [a] -> [b]
map Tree -> String
ppTree [Tree]
ts) forall a. [a] -> [a] -> [a]
++ String
")"
ppTree (V String
x)  = String
x

lexing :: String -> [String]
lexing = forall b a. (b -> Maybe (a, b)) -> b -> [a]
unfoldr (\String
v -> case ReadS String
lex String
v of
                    (String
"", String
"") : [(String, String)]
_ -> forall a. Maybe a
Nothing
                    (String, String)
e : [(String, String)]
_ -> forall a. a -> Maybe a
Just (String, String)
e
                    [(String, String)]
_ -> forall a. Maybe a
Nothing)