|
Packit |
9a2dfb |
{-# LANGUAGE BangPatterns #-}
|
|
Packit |
9a2dfb |
{-# LANGUAGE CPP #-}
|
|
Packit |
9a2dfb |
{-# LANGUAGE FlexibleInstances #-}
|
|
Packit |
9a2dfb |
{-# LANGUAGE NamedFieldPuns #-}
|
|
Packit |
9a2dfb |
{-# LANGUAGE NoImplicitPrelude #-}
|
|
Packit |
9a2dfb |
{-# LANGUAGE UndecidableInstances #-}
|
|
Packit |
9a2dfb |
#if __GLASGOW_HASKELL__ >= 800
|
|
Packit |
9a2dfb |
-- a) THQ works on cross-compilers and unregisterised GHCs
|
|
Packit |
9a2dfb |
-- b) may make compilation faster as no dynamic loading is ever needed (not sure about this)
|
|
Packit |
9a2dfb |
-- c) removes one hindrance to have code inferred as SafeHaskell safe
|
|
Packit |
9a2dfb |
{-# LANGUAGE TemplateHaskellQuotes #-}
|
|
Packit |
9a2dfb |
#else
|
|
Packit |
9a2dfb |
{-# LANGUAGE TemplateHaskell #-}
|
|
Packit |
9a2dfb |
#endif
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
#include "incoherent-compat.h"
|
|
Packit |
9a2dfb |
#include "overlapping-compat.h"
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
{-|
|
|
Packit |
9a2dfb |
Module: Data.Aeson.TH
|
|
Packit |
9a2dfb |
Copyright: (c) 2011-2016 Bryan O'Sullivan
|
|
Packit |
9a2dfb |
(c) 2011 MailRank, Inc.
|
|
Packit |
9a2dfb |
License: BSD3
|
|
Packit |
9a2dfb |
Stability: experimental
|
|
Packit |
9a2dfb |
Portability: portable
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
Functions to mechanically derive 'ToJSON' and 'FromJSON' instances. Note that
|
|
Packit |
9a2dfb |
you need to enable the @TemplateHaskell@ language extension in order to use this
|
|
Packit |
9a2dfb |
module.
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
An example shows how instances are generated for arbitrary data types. First we
|
|
Packit |
9a2dfb |
define a data type:
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
@
|
|
Packit |
9a2dfb |
data D a = Nullary
|
|
Packit |
9a2dfb |
| Unary Int
|
|
Packit |
9a2dfb |
| Product String Char a
|
|
Packit |
9a2dfb |
| Record { testOne :: Double
|
|
Packit |
9a2dfb |
, testTwo :: Bool
|
|
Packit |
9a2dfb |
, testThree :: D a
|
|
Packit |
9a2dfb |
} deriving Eq
|
|
Packit |
9a2dfb |
@
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
Next we derive the necessary instances. Note that we make use of the
|
|
Packit |
9a2dfb |
feature to change record field names. In this case we drop the first 4
|
|
Packit |
9a2dfb |
characters of every field name. We also modify constructor names by
|
|
Packit |
9a2dfb |
lower-casing them:
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
@
|
|
Packit |
9a2dfb |
$('deriveJSON' 'defaultOptions'{'fieldLabelModifier' = 'drop' 4, 'constructorTagModifier' = map toLower} ''D)
|
|
Packit |
9a2dfb |
@
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
Now we can use the newly created instances.
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
@
|
|
Packit |
9a2dfb |
d :: D 'Int'
|
|
Packit |
9a2dfb |
d = Record { testOne = 3.14159
|
|
Packit |
9a2dfb |
, testTwo = 'True'
|
|
Packit |
9a2dfb |
, testThree = Product \"test\" \'A\' 123
|
|
Packit |
9a2dfb |
}
|
|
Packit |
9a2dfb |
@
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
>>> fromJSON (toJSON d) == Success d
|
|
Packit |
9a2dfb |
> True
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
This also works for data family instances, but instead of passing in the data
|
|
Packit |
9a2dfb |
family name (with double quotes), we pass in a data family instance
|
|
Packit |
9a2dfb |
constructor (with a single quote):
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
@
|
|
Packit |
9a2dfb |
data family DF a
|
|
Packit |
9a2dfb |
data instance DF Int = DF1 Int
|
|
Packit |
9a2dfb |
| DF2 Int Int
|
|
Packit |
9a2dfb |
deriving Eq
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
$('deriveJSON' 'defaultOptions' 'DF1)
|
|
Packit |
9a2dfb |
-- Alternatively, one could pass 'DF2 instead
|
|
Packit |
9a2dfb |
@
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
Please note that you can derive instances for tuples using the following syntax:
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
@
|
|
Packit |
9a2dfb |
-- FromJSON and ToJSON instances for 4-tuples.
|
|
Packit |
9a2dfb |
$('deriveJSON' 'defaultOptions' ''(,,,))
|
|
Packit |
9a2dfb |
@
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-}
|
|
Packit |
9a2dfb |
module Data.Aeson.TH
|
|
Packit |
9a2dfb |
(
|
|
Packit |
9a2dfb |
-- * Encoding configuration
|
|
Packit |
9a2dfb |
Options(..)
|
|
Packit |
9a2dfb |
, SumEncoding(..)
|
|
Packit |
9a2dfb |
, defaultOptions
|
|
Packit |
9a2dfb |
, defaultTaggedObject
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- * FromJSON and ToJSON derivation
|
|
Packit |
9a2dfb |
, deriveJSON
|
|
Packit |
9a2dfb |
, deriveJSON1
|
|
Packit |
9a2dfb |
, deriveJSON2
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
, deriveToJSON
|
|
Packit |
9a2dfb |
, deriveToJSON1
|
|
Packit |
9a2dfb |
, deriveToJSON2
|
|
Packit |
9a2dfb |
, deriveFromJSON
|
|
Packit |
9a2dfb |
, deriveFromJSON1
|
|
Packit |
9a2dfb |
, deriveFromJSON2
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
, mkToJSON
|
|
Packit |
9a2dfb |
, mkLiftToJSON
|
|
Packit |
9a2dfb |
, mkLiftToJSON2
|
|
Packit |
9a2dfb |
, mkToEncoding
|
|
Packit |
9a2dfb |
, mkLiftToEncoding
|
|
Packit |
9a2dfb |
, mkLiftToEncoding2
|
|
Packit |
9a2dfb |
, mkParseJSON
|
|
Packit |
9a2dfb |
, mkLiftParseJSON
|
|
Packit |
9a2dfb |
, mkLiftParseJSON2
|
|
Packit |
9a2dfb |
) where
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
import Prelude ()
|
|
Packit |
9a2dfb |
import Prelude.Compat hiding (exp)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
import Control.Applicative ((<|>))
|
|
Packit |
9a2dfb |
import Data.Aeson (Object, (.=), (.:), FromJSON(..), FromJSON1(..), FromJSON2(..), ToJSON(..), ToJSON1(..), ToJSON2(..))
|
|
Packit |
9a2dfb |
import Data.Aeson.Types (Options(..), Parser, SumEncoding(..), Value(..), defaultOptions, defaultTaggedObject)
|
|
Packit |
9a2dfb |
import Data.Aeson.Types.Internal ((), JSONPathElement(Key))
|
|
Packit |
9a2dfb |
import Data.Aeson.Types.FromJSON (parseOptionalFieldWith)
|
|
Packit |
9a2dfb |
import Control.Monad (liftM2, unless, when)
|
|
Packit |
9a2dfb |
import Data.Foldable (foldr')
|
|
Packit |
9a2dfb |
#if MIN_VERSION_template_haskell(2,8,0) && !MIN_VERSION_template_haskell(2,10,0)
|
|
Packit |
9a2dfb |
import Data.List (nub)
|
|
Packit |
9a2dfb |
#endif
|
|
Packit |
9a2dfb |
import Data.List (foldl', genericLength, intercalate, partition, union)
|
|
Packit |
9a2dfb |
import Data.List.NonEmpty ((<|), NonEmpty((:|)))
|
|
Packit |
9a2dfb |
import Data.Map (Map)
|
|
Packit |
9a2dfb |
import Data.Maybe (catMaybes, fromMaybe, mapMaybe)
|
|
Packit |
9a2dfb |
import Data.Set (Set)
|
|
Packit |
9a2dfb |
#if MIN_VERSION_template_haskell(2,8,0)
|
|
Packit |
9a2dfb |
import Language.Haskell.TH hiding (Arity)
|
|
Packit |
9a2dfb |
#else
|
|
Packit |
9a2dfb |
import Language.Haskell.TH
|
|
Packit |
9a2dfb |
#endif
|
|
Packit |
9a2dfb |
import Language.Haskell.TH.Datatype
|
|
Packit |
9a2dfb |
#if MIN_VERSION_template_haskell(2,7,0) && !(MIN_VERSION_template_haskell(2,8,0))
|
|
Packit |
9a2dfb |
import Language.Haskell.TH.Lib (starK)
|
|
Packit |
9a2dfb |
#endif
|
|
Packit |
9a2dfb |
#if MIN_VERSION_template_haskell(2,8,0) && !(MIN_VERSION_template_haskell(2,10,0))
|
|
Packit |
9a2dfb |
import Language.Haskell.TH.Syntax (mkNameG_tc)
|
|
Packit |
9a2dfb |
#endif
|
|
Packit |
9a2dfb |
import Text.Printf (printf)
|
|
Packit |
9a2dfb |
import qualified Data.Aeson as A
|
|
Packit |
9a2dfb |
import qualified Data.Aeson.Encoding.Internal as E
|
|
Packit |
9a2dfb |
import qualified Data.Foldable as F (all)
|
|
Packit |
9a2dfb |
import qualified Data.HashMap.Strict as H (lookup, toList)
|
|
Packit |
9a2dfb |
import qualified Data.List.NonEmpty as NE (length, reverse)
|
|
Packit |
9a2dfb |
import qualified Data.Map as M (fromList, keys, lookup , singleton, size)
|
|
Packit |
9a2dfb |
import qualified Data.Semigroup as Semigroup (Option(..))
|
|
Packit |
9a2dfb |
import qualified Data.Set as Set (empty, insert, member)
|
|
Packit |
9a2dfb |
import qualified Data.Text as T (Text, pack, unpack)
|
|
Packit |
9a2dfb |
import qualified Data.Vector as V (unsafeIndex, null, length, create, empty)
|
|
Packit |
9a2dfb |
import qualified Data.Vector.Mutable as VM (unsafeNew, unsafeWrite)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
{-# ANN module "Hlint: ignore Reduce duplication" #-}
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
--------------------------------------------------------------------------------
|
|
Packit |
9a2dfb |
-- Convenience
|
|
Packit |
9a2dfb |
--------------------------------------------------------------------------------
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Generates both 'ToJSON' and 'FromJSON' instance declarations for the given
|
|
Packit |
9a2dfb |
-- data type or data family instance constructor.
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- This is a convienience function which is equivalent to calling both
|
|
Packit |
9a2dfb |
-- 'deriveToJSON' and 'deriveFromJSON'.
|
|
Packit |
9a2dfb |
deriveJSON :: Options
|
|
Packit |
9a2dfb |
-- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name
|
|
Packit |
9a2dfb |
-- ^ Name of the type for which to generate 'ToJSON' and 'FromJSON'
|
|
Packit |
9a2dfb |
-- instances.
|
|
Packit |
9a2dfb |
-> Q [Dec]
|
|
Packit |
9a2dfb |
deriveJSON = deriveJSONBoth deriveToJSON deriveFromJSON
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Generates both 'ToJSON1' and 'FromJSON1' instance declarations for the given
|
|
Packit |
9a2dfb |
-- data type or data family instance constructor.
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- This is a convienience function which is equivalent to calling both
|
|
Packit |
9a2dfb |
-- 'deriveToJSON1' and 'deriveFromJSON1'.
|
|
Packit |
9a2dfb |
deriveJSON1 :: Options
|
|
Packit |
9a2dfb |
-- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name
|
|
Packit |
9a2dfb |
-- ^ Name of the type for which to generate 'ToJSON1' and 'FromJSON1'
|
|
Packit |
9a2dfb |
-- instances.
|
|
Packit |
9a2dfb |
-> Q [Dec]
|
|
Packit |
9a2dfb |
deriveJSON1 = deriveJSONBoth deriveToJSON1 deriveFromJSON1
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Generates both 'ToJSON2' and 'FromJSON2' instance declarations for the given
|
|
Packit |
9a2dfb |
-- data type or data family instance constructor.
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- This is a convienience function which is equivalent to calling both
|
|
Packit |
9a2dfb |
-- 'deriveToJSON2' and 'deriveFromJSON2'.
|
|
Packit |
9a2dfb |
deriveJSON2 :: Options
|
|
Packit |
9a2dfb |
-- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name
|
|
Packit |
9a2dfb |
-- ^ Name of the type for which to generate 'ToJSON2' and 'FromJSON2'
|
|
Packit |
9a2dfb |
-- instances.
|
|
Packit |
9a2dfb |
-> Q [Dec]
|
|
Packit |
9a2dfb |
deriveJSON2 = deriveJSONBoth deriveToJSON2 deriveFromJSON2
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
--------------------------------------------------------------------------------
|
|
Packit |
9a2dfb |
-- ToJSON
|
|
Packit |
9a2dfb |
--------------------------------------------------------------------------------
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
{-
|
|
Packit |
9a2dfb |
TODO: Don't constrain phantom type variables.
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
data Foo a = Foo Int
|
|
Packit |
9a2dfb |
instance (ToJSON a) ⇒ ToJSON Foo where ...
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
The above (ToJSON a) constraint is not necessary and perhaps undesirable.
|
|
Packit |
9a2dfb |
-}
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Generates a 'ToJSON' instance declaration for the given data type or
|
|
Packit |
9a2dfb |
-- data family instance constructor.
|
|
Packit |
9a2dfb |
deriveToJSON :: Options
|
|
Packit |
9a2dfb |
-- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name
|
|
Packit |
9a2dfb |
-- ^ Name of the type for which to generate a 'ToJSON' instance
|
|
Packit |
9a2dfb |
-- declaration.
|
|
Packit |
9a2dfb |
-> Q [Dec]
|
|
Packit |
9a2dfb |
deriveToJSON = deriveToJSONCommon toJSONClass
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Generates a 'ToJSON1' instance declaration for the given data type or
|
|
Packit |
9a2dfb |
-- data family instance constructor.
|
|
Packit |
9a2dfb |
deriveToJSON1 :: Options
|
|
Packit |
9a2dfb |
-- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name
|
|
Packit |
9a2dfb |
-- ^ Name of the type for which to generate a 'ToJSON1' instance
|
|
Packit |
9a2dfb |
-- declaration.
|
|
Packit |
9a2dfb |
-> Q [Dec]
|
|
Packit |
9a2dfb |
deriveToJSON1 = deriveToJSONCommon toJSON1Class
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Generates a 'ToJSON2' instance declaration for the given data type or
|
|
Packit |
9a2dfb |
-- data family instance constructor.
|
|
Packit |
9a2dfb |
deriveToJSON2 :: Options
|
|
Packit |
9a2dfb |
-- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name
|
|
Packit |
9a2dfb |
-- ^ Name of the type for which to generate a 'ToJSON2' instance
|
|
Packit |
9a2dfb |
-- declaration.
|
|
Packit |
9a2dfb |
-> Q [Dec]
|
|
Packit |
9a2dfb |
deriveToJSON2 = deriveToJSONCommon toJSON2Class
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
deriveToJSONCommon :: JSONClass
|
|
Packit |
9a2dfb |
-- ^ The ToJSON variant being derived.
|
|
Packit |
9a2dfb |
-> Options
|
|
Packit |
9a2dfb |
-- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name
|
|
Packit |
9a2dfb |
-- ^ Name of the type for which to generate an instance.
|
|
Packit |
9a2dfb |
-> Q [Dec]
|
|
Packit |
9a2dfb |
deriveToJSONCommon = deriveJSONClass [ (ToJSON, \jc _ -> consToValue Value jc)
|
|
Packit |
9a2dfb |
, (ToEncoding, \jc _ -> consToValue Encoding jc)
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Generates a lambda expression which encodes the given data type or
|
|
Packit |
9a2dfb |
-- data family instance constructor as a 'Value'.
|
|
Packit |
9a2dfb |
mkToJSON :: Options -- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name -- ^ Name of the type to encode.
|
|
Packit |
9a2dfb |
-> Q Exp
|
|
Packit |
9a2dfb |
mkToJSON = mkToJSONCommon toJSONClass
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Generates a lambda expression which encodes the given data type or
|
|
Packit |
9a2dfb |
-- data family instance constructor as a 'Value' by using the given encoding
|
|
Packit |
9a2dfb |
-- function on occurrences of the last type parameter.
|
|
Packit |
9a2dfb |
mkLiftToJSON :: Options -- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name -- ^ Name of the type to encode.
|
|
Packit |
9a2dfb |
-> Q Exp
|
|
Packit |
9a2dfb |
mkLiftToJSON = mkToJSONCommon toJSON1Class
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Generates a lambda expression which encodes the given data type or
|
|
Packit |
9a2dfb |
-- data family instance constructor as a 'Value' by using the given encoding
|
|
Packit |
9a2dfb |
-- functions on occurrences of the last two type parameters.
|
|
Packit |
9a2dfb |
mkLiftToJSON2 :: Options -- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name -- ^ Name of the type to encode.
|
|
Packit |
9a2dfb |
-> Q Exp
|
|
Packit |
9a2dfb |
mkLiftToJSON2 = mkToJSONCommon toJSON2Class
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
mkToJSONCommon :: JSONClass -- ^ Which class's method is being derived.
|
|
Packit |
9a2dfb |
-> Options -- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name -- ^ Name of the encoded type.
|
|
Packit |
9a2dfb |
-> Q Exp
|
|
Packit |
9a2dfb |
mkToJSONCommon = mkFunCommon (\jc _ -> consToValue Value jc)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Generates a lambda expression which encodes the given data type or
|
|
Packit |
9a2dfb |
-- data family instance constructor as a JSON string.
|
|
Packit |
9a2dfb |
mkToEncoding :: Options -- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name -- ^ Name of the type to encode.
|
|
Packit |
9a2dfb |
-> Q Exp
|
|
Packit |
9a2dfb |
mkToEncoding = mkToEncodingCommon toJSONClass
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Generates a lambda expression which encodes the given data type or
|
|
Packit |
9a2dfb |
-- data family instance constructor as a JSON string by using the given encoding
|
|
Packit |
9a2dfb |
-- function on occurrences of the last type parameter.
|
|
Packit |
9a2dfb |
mkLiftToEncoding :: Options -- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name -- ^ Name of the type to encode.
|
|
Packit |
9a2dfb |
-> Q Exp
|
|
Packit |
9a2dfb |
mkLiftToEncoding = mkToEncodingCommon toJSON1Class
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Generates a lambda expression which encodes the given data type or
|
|
Packit |
9a2dfb |
-- data family instance constructor as a JSON string by using the given encoding
|
|
Packit |
9a2dfb |
-- functions on occurrences of the last two type parameters.
|
|
Packit |
9a2dfb |
mkLiftToEncoding2 :: Options -- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name -- ^ Name of the type to encode.
|
|
Packit |
9a2dfb |
-> Q Exp
|
|
Packit |
9a2dfb |
mkLiftToEncoding2 = mkToEncodingCommon toJSON2Class
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
mkToEncodingCommon :: JSONClass -- ^ Which class's method is being derived.
|
|
Packit |
9a2dfb |
-> Options -- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name -- ^ Name of the encoded type.
|
|
Packit |
9a2dfb |
-> Q Exp
|
|
Packit |
9a2dfb |
mkToEncodingCommon = mkFunCommon (\jc _ -> consToValue Encoding jc)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Helper function used by both 'deriveToJSON' and 'mkToJSON'. Generates
|
|
Packit |
9a2dfb |
-- code to generate a 'Value' or 'Encoding' of a number of constructors. All
|
|
Packit |
9a2dfb |
-- constructors must be from the same type.
|
|
Packit |
9a2dfb |
consToValue :: ToJSONFun
|
|
Packit |
9a2dfb |
-- ^ The method ('toJSON' or 'toEncoding') being derived.
|
|
Packit |
9a2dfb |
-> JSONClass
|
|
Packit |
9a2dfb |
-- ^ The ToJSON variant being derived.
|
|
Packit |
9a2dfb |
-> Options
|
|
Packit |
9a2dfb |
-- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> [Type]
|
|
Packit |
9a2dfb |
-- ^ The types from the data type/data family instance declaration
|
|
Packit |
9a2dfb |
-> [ConstructorInfo]
|
|
Packit |
9a2dfb |
-- ^ Constructors for which to generate JSON generating code.
|
|
Packit |
9a2dfb |
-> Q Exp
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
consToValue _ _ _ _ [] = error $ "Data.Aeson.TH.consToValue: "
|
|
Packit |
9a2dfb |
++ "Not a single constructor given!"
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
consToValue target jc opts vars cons = do
|
|
Packit |
9a2dfb |
value <- newName "value"
|
|
Packit |
9a2dfb |
tjs <- newNameList "_tj" $ arityInt jc
|
|
Packit |
9a2dfb |
tjls <- newNameList "_tjl" $ arityInt jc
|
|
Packit |
9a2dfb |
let zippedTJs = zip tjs tjls
|
|
Packit |
9a2dfb |
interleavedTJs = interleave tjs tjls
|
|
Packit |
9a2dfb |
lastTyVars = map varTToName $ drop (length vars - arityInt jc) vars
|
|
Packit |
9a2dfb |
tvMap = M.fromList $ zip lastTyVars zippedTJs
|
|
Packit |
9a2dfb |
lamE (map varP $ interleavedTJs ++ [value]) $
|
|
Packit |
9a2dfb |
caseE (varE value) (matches tvMap)
|
|
Packit |
9a2dfb |
where
|
|
Packit |
9a2dfb |
matches tvMap = case cons of
|
|
Packit |
9a2dfb |
-- A single constructor is directly encoded. The constructor itself may be
|
|
Packit |
9a2dfb |
-- forgotten.
|
|
Packit |
9a2dfb |
[con] | not (tagSingleConstructors opts) -> [argsToValue target jc tvMap opts False con]
|
|
Packit |
9a2dfb |
_ | allNullaryToStringTag opts && all isNullary cons ->
|
|
Packit |
9a2dfb |
[ match (conP conName []) (normalB $ conStr target opts conName) []
|
|
Packit |
9a2dfb |
| con <- cons
|
|
Packit |
9a2dfb |
, let conName = constructorName con
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
| otherwise -> [argsToValue target jc tvMap opts True con | con <- cons]
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Name of the constructor as a quoted 'Value' or 'Encoding'.
|
|
Packit |
9a2dfb |
conStr :: ToJSONFun -> Options -> Name -> Q Exp
|
|
Packit |
9a2dfb |
conStr Value opts = appE [|String|] . conTxt opts
|
|
Packit |
9a2dfb |
conStr Encoding opts = appE [|E.text|] . conTxt opts
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Name of the constructor as a quoted 'Text'.
|
|
Packit |
9a2dfb |
conTxt :: Options -> Name -> Q Exp
|
|
Packit |
9a2dfb |
conTxt opts = appE [|T.pack|] . stringE . conString opts
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Name of the constructor.
|
|
Packit |
9a2dfb |
conString :: Options -> Name -> String
|
|
Packit |
9a2dfb |
conString opts = constructorTagModifier opts . nameBase
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | If constructor is nullary.
|
|
Packit |
9a2dfb |
isNullary :: ConstructorInfo -> Bool
|
|
Packit |
9a2dfb |
isNullary ConstructorInfo { constructorVariant = NormalConstructor
|
|
Packit |
9a2dfb |
, constructorFields = tys } = null tys
|
|
Packit |
9a2dfb |
isNullary _ = False
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Wrap fields of a non-record constructor. See 'sumToValue'.
|
|
Packit |
9a2dfb |
opaqueSumToValue :: ToJSONFun -> Options -> Bool -> Bool -> Name -> ExpQ -> ExpQ
|
|
Packit |
9a2dfb |
opaqueSumToValue target opts multiCons nullary conName value =
|
|
Packit |
9a2dfb |
sumToValue target opts multiCons nullary conName
|
|
Packit |
9a2dfb |
value
|
|
Packit |
9a2dfb |
pairs
|
|
Packit |
9a2dfb |
where
|
|
Packit |
9a2dfb |
pairs contentsFieldName = listE [toPair target contentsFieldName value]
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Wrap fields of a record constructor. See 'sumToValue'.
|
|
Packit |
9a2dfb |
recordSumToValue :: ToJSONFun -> Options -> Bool -> Bool -> Name -> ExpQ -> ExpQ
|
|
Packit |
9a2dfb |
recordSumToValue target opts multiCons nullary conName pairs =
|
|
Packit |
9a2dfb |
sumToValue target opts multiCons nullary conName
|
|
Packit |
9a2dfb |
(objectExp target pairs)
|
|
Packit |
9a2dfb |
(const pairs)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Wrap fields of a constructor.
|
|
Packit |
9a2dfb |
sumToValue
|
|
Packit |
9a2dfb |
:: ToJSONFun
|
|
Packit |
9a2dfb |
-- ^ The method being derived.
|
|
Packit |
9a2dfb |
-> Options
|
|
Packit |
9a2dfb |
-- ^ Deriving options.
|
|
Packit |
9a2dfb |
-> Bool
|
|
Packit |
9a2dfb |
-- ^ Does this type have multiple constructors.
|
|
Packit |
9a2dfb |
-> Bool
|
|
Packit |
9a2dfb |
-- ^ Is this constructor nullary.
|
|
Packit |
9a2dfb |
-> Name
|
|
Packit |
9a2dfb |
-- ^ Constructor name.
|
|
Packit |
9a2dfb |
-> ExpQ
|
|
Packit |
9a2dfb |
-- ^ Fields of the constructor as a 'Value' or 'Encoding'.
|
|
Packit |
9a2dfb |
-> (String -> ExpQ)
|
|
Packit |
9a2dfb |
-- ^ Representation of an 'Object' fragment used for the 'TaggedObject'
|
|
Packit |
9a2dfb |
-- variant; of type @[(Text,Value)]@ or @[Encoding]@, depending on the method
|
|
Packit |
9a2dfb |
-- being derived.
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- - For non-records, produces a pair @"contentsFieldName":value@,
|
|
Packit |
9a2dfb |
-- given a @contentsFieldName@ as an argument. See 'opaqueSumToValue'.
|
|
Packit |
9a2dfb |
-- - For records, produces the list of pairs corresponding to fields of the
|
|
Packit |
9a2dfb |
-- encoded value (ignores the argument). See 'recordSumToValue'.
|
|
Packit |
9a2dfb |
-> ExpQ
|
|
Packit |
9a2dfb |
sumToValue target opts multiCons nullary conName value pairs
|
|
Packit |
9a2dfb |
| multiCons =
|
|
Packit |
9a2dfb |
case sumEncoding opts of
|
|
Packit |
9a2dfb |
TwoElemArray ->
|
|
Packit |
9a2dfb |
array target [conStr target opts conName, value]
|
|
Packit |
9a2dfb |
TaggedObject{tagFieldName, contentsFieldName} ->
|
|
Packit |
9a2dfb |
-- TODO: Maybe throw an error in case
|
|
Packit |
9a2dfb |
-- tagFieldName overwrites a field in pairs.
|
|
Packit |
9a2dfb |
let tag = toPair target tagFieldName (conStr target opts conName)
|
|
Packit |
9a2dfb |
content = pairs contentsFieldName
|
|
Packit |
9a2dfb |
in objectExp target $
|
|
Packit |
9a2dfb |
if nullary then listE [tag] else infixApp tag [|(:)|] content
|
|
Packit |
9a2dfb |
ObjectWithSingleField ->
|
|
Packit |
9a2dfb |
object target [(conString opts conName, value)]
|
|
Packit |
9a2dfb |
UntaggedValue | nullary -> conStr target opts conName
|
|
Packit |
9a2dfb |
UntaggedValue -> value
|
|
Packit |
9a2dfb |
| otherwise = value
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Generates code to generate the JSON encoding of a single constructor.
|
|
Packit |
9a2dfb |
argsToValue :: ToJSONFun -> JSONClass -> TyVarMap -> Options -> Bool -> ConstructorInfo -> Q Match
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- Polyadic constructors with special case for unary constructors.
|
|
Packit |
9a2dfb |
argsToValue target jc tvMap opts multiCons
|
|
Packit |
9a2dfb |
ConstructorInfo { constructorName = conName
|
|
Packit |
9a2dfb |
, constructorVariant = NormalConstructor
|
|
Packit |
9a2dfb |
, constructorFields = argTys } = do
|
|
Packit |
9a2dfb |
argTys' <- mapM resolveTypeSynonyms argTys
|
|
Packit |
9a2dfb |
let len = length argTys'
|
|
Packit |
9a2dfb |
args <- newNameList "arg" len
|
|
Packit |
9a2dfb |
let js = case [ dispatchToJSON target jc conName tvMap argTy
|
|
Packit |
9a2dfb |
`appE` varE arg
|
|
Packit |
9a2dfb |
| (arg, argTy) <- zip args argTys'
|
|
Packit |
9a2dfb |
] of
|
|
Packit |
9a2dfb |
-- Single argument is directly converted.
|
|
Packit |
9a2dfb |
[e] -> e
|
|
Packit |
9a2dfb |
-- Zero and multiple arguments are converted to a JSON array.
|
|
Packit |
9a2dfb |
es -> array target es
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
match (conP conName $ map varP args)
|
|
Packit |
9a2dfb |
(normalB $ opaqueSumToValue target opts multiCons (null argTys') conName js)
|
|
Packit |
9a2dfb |
[]
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- Records.
|
|
Packit |
9a2dfb |
argsToValue target jc tvMap opts multiCons
|
|
Packit |
9a2dfb |
info@ConstructorInfo { constructorName = conName
|
|
Packit |
9a2dfb |
, constructorVariant = RecordConstructor fields
|
|
Packit |
9a2dfb |
, constructorFields = argTys } =
|
|
Packit |
9a2dfb |
case (unwrapUnaryRecords opts, not multiCons, argTys) of
|
|
Packit |
9a2dfb |
(True,True,[_]) -> argsToValue target jc tvMap opts multiCons
|
|
Packit |
9a2dfb |
(info{constructorVariant = NormalConstructor})
|
|
Packit |
9a2dfb |
_ -> do
|
|
Packit |
9a2dfb |
argTys' <- mapM resolveTypeSynonyms argTys
|
|
Packit |
9a2dfb |
args <- newNameList "arg" $ length argTys'
|
|
Packit |
9a2dfb |
let pairs | omitNothingFields opts = infixApp maybeFields
|
|
Packit |
9a2dfb |
[|(++)|]
|
|
Packit |
9a2dfb |
restFields
|
|
Packit |
9a2dfb |
| otherwise = listE $ map pureToPair argCons
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
argCons = zip3 (map varE args) argTys' fields
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
maybeFields = [|catMaybes|] `appE` listE (map maybeToPair maybes)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
restFields = listE $ map pureToPair rest
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
(maybes0, rest0) = partition isMaybe argCons
|
|
Packit |
9a2dfb |
(options, rest) = partition isOption rest0
|
|
Packit |
9a2dfb |
maybes = maybes0 ++ map optionToMaybe options
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
maybeToPair = toPairLifted True
|
|
Packit |
9a2dfb |
pureToPair = toPairLifted False
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
toPairLifted lifted (arg, argTy, field) =
|
|
Packit |
9a2dfb |
let toValue = dispatchToJSON target jc conName tvMap argTy
|
|
Packit |
9a2dfb |
fieldName = fieldLabel opts field
|
|
Packit |
9a2dfb |
e arg' = toPair target fieldName (toValue `appE` arg')
|
|
Packit |
9a2dfb |
in if lifted
|
|
Packit |
9a2dfb |
then do
|
|
Packit |
9a2dfb |
x <- newName "x"
|
|
Packit |
9a2dfb |
infixApp (lam1E (varP x) (e (varE x))) [|(<$>)|] arg
|
|
Packit |
9a2dfb |
else e arg
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
match (conP conName $ map varP args)
|
|
Packit |
9a2dfb |
(normalB $ recordSumToValue target opts multiCons (null argTys) conName pairs)
|
|
Packit |
9a2dfb |
[]
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- Infix constructors.
|
|
Packit |
9a2dfb |
argsToValue target jc tvMap opts multiCons
|
|
Packit |
9a2dfb |
ConstructorInfo { constructorName = conName
|
|
Packit |
9a2dfb |
, constructorVariant = InfixConstructor
|
|
Packit |
9a2dfb |
, constructorFields = argTys } = do
|
|
Packit |
9a2dfb |
[alTy, arTy] <- mapM resolveTypeSynonyms argTys
|
|
Packit |
9a2dfb |
al <- newName "argL"
|
|
Packit |
9a2dfb |
ar <- newName "argR"
|
|
Packit |
9a2dfb |
match (infixP (varP al) conName (varP ar))
|
|
Packit |
9a2dfb |
( normalB
|
|
Packit |
9a2dfb |
$ opaqueSumToValue target opts multiCons False conName
|
|
Packit |
9a2dfb |
$ array target
|
|
Packit |
9a2dfb |
[ dispatchToJSON target jc conName tvMap aTy
|
|
Packit |
9a2dfb |
`appE` varE a
|
|
Packit |
9a2dfb |
| (a, aTy) <- [(al,alTy), (ar,arTy)]
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
[]
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
isMaybe :: (a, Type, b) -> Bool
|
|
Packit |
9a2dfb |
isMaybe (_, AppT (ConT t) _, _) = t == ''Maybe
|
|
Packit |
9a2dfb |
isMaybe _ = False
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
isOption :: (a, Type, b) -> Bool
|
|
Packit |
9a2dfb |
isOption (_, AppT (ConT t) _, _) = t == ''Semigroup.Option
|
|
Packit |
9a2dfb |
isOption _ = False
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
optionToMaybe :: (ExpQ, b, c) -> (ExpQ, b, c)
|
|
Packit |
9a2dfb |
optionToMaybe (a, b, c) = ([|Semigroup.getOption|] `appE` a, b, c)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
(<^>) :: ExpQ -> ExpQ -> ExpQ
|
|
Packit |
9a2dfb |
(<^>) a b = infixApp a [|(E.><)|] b
|
|
Packit |
9a2dfb |
infixr 6 <^>
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
(<:>) :: ExpQ -> ExpQ -> ExpQ
|
|
Packit |
9a2dfb |
(<:>) a b = a <^> [|E.colon|] <^> b
|
|
Packit |
9a2dfb |
infixr 5 <:>
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
(<%>) :: ExpQ -> ExpQ -> ExpQ
|
|
Packit |
9a2dfb |
(<%>) a b = a <^> [|E.comma|] <^> b
|
|
Packit |
9a2dfb |
infixr 4 <%>
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Wrap a list of quoted 'Value's in a quoted 'Array' (of type 'Value').
|
|
Packit |
9a2dfb |
array :: ToJSONFun -> [ExpQ] -> ExpQ
|
|
Packit |
9a2dfb |
array Encoding [] = [|E.emptyArray_|]
|
|
Packit |
9a2dfb |
array Value [] = [|Array V.empty|]
|
|
Packit |
9a2dfb |
array Encoding es = [|E.wrapArray|] `appE` foldr1 (<%>) es
|
|
Packit |
9a2dfb |
array Value es = do
|
|
Packit |
9a2dfb |
mv <- newName "mv"
|
|
Packit |
9a2dfb |
let newMV = bindS (varP mv)
|
|
Packit |
9a2dfb |
([|VM.unsafeNew|] `appE`
|
|
Packit |
9a2dfb |
litE (integerL $ fromIntegral (length es)))
|
|
Packit |
9a2dfb |
stmts = [ noBindS $
|
|
Packit |
9a2dfb |
[|VM.unsafeWrite|] `appE`
|
|
Packit |
9a2dfb |
varE mv `appE`
|
|
Packit |
9a2dfb |
litE (integerL ix) `appE`
|
|
Packit |
9a2dfb |
e
|
|
Packit |
9a2dfb |
| (ix, e) <- zip [(0::Integer)..] es
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
ret = noBindS $ [|return|] `appE` varE mv
|
|
Packit |
9a2dfb |
[|Array|] `appE`
|
|
Packit |
9a2dfb |
(varE 'V.create `appE`
|
|
Packit |
9a2dfb |
doE (newMV:stmts++[ret]))
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Wrap an associative list of keys and quoted values in a quoted 'Object'.
|
|
Packit |
9a2dfb |
object :: ToJSONFun -> [(String, ExpQ)] -> ExpQ
|
|
Packit |
9a2dfb |
object target = wrapObject target . catPairs target . fmap (uncurry (toPair target))
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- |
|
|
Packit |
9a2dfb |
-- - When deriving 'ToJSON', map a list of quoted key-value pairs to an
|
|
Packit |
9a2dfb |
-- expression of the list of pairs.
|
|
Packit |
9a2dfb |
-- - When deriving 'ToEncoding', map a list of quoted 'Encoding's representing
|
|
Packit |
9a2dfb |
-- key-value pairs to a comma-separated 'Encoding' of them.
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- > catPairs Value [ [|(k0,v0)|], [|(k1,v1)|] ] = [| [(k0,v0), (k1,v1)] |]
|
|
Packit |
9a2dfb |
-- > catPairs Encoding [ [|"\"k0\":v0"|], [|"\"k1\":v1"|] ] = [| "\"k0\":v0,\"k1\":v1" |]
|
|
Packit |
9a2dfb |
catPairs :: ToJSONFun -> [ExpQ] -> ExpQ
|
|
Packit |
9a2dfb |
catPairs Value = listE
|
|
Packit |
9a2dfb |
catPairs Encoding = foldr1 (<%>)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- |
|
|
Packit |
9a2dfb |
-- - When deriving 'ToJSON', wrap a quoted list of key-value pairs in an 'Object'.
|
|
Packit |
9a2dfb |
-- - When deriving 'ToEncoding', wrap a quoted list of encoded key-value pairs
|
|
Packit |
9a2dfb |
-- in an encoded 'Object'.
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- > objectExp Value [| [(k0,v0), (k1,v1)] |] = [| Object (fromList [(k0,v0), (k1,v1)]) |]
|
|
Packit |
9a2dfb |
-- > objectExp Encoding [| ["\"k0\":v0", "\"k1\":v1"] |] = [| "{\"k0\":v0,\"k1\":v1}" |]
|
|
Packit |
9a2dfb |
objectExp :: ToJSONFun -> ExpQ -> ExpQ
|
|
Packit |
9a2dfb |
objectExp target = wrapObject target . catPairsExp target
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Counterpart of 'catPairsExp' when the list of pairs is already quoted.
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- > objectExp Value [| [(k0,v0), (k1,v1)] |] = [| [(k0,v0), (k1,v1)] |]
|
|
Packit |
9a2dfb |
-- > objectExp Encoding [| ["\"k0\":v0", "\"k1\":v1"] |] = [| "\"k0\":v0,\"k1\":v1" |]
|
|
Packit |
9a2dfb |
catPairsExp :: ToJSONFun -> ExpQ -> ExpQ
|
|
Packit |
9a2dfb |
catPairsExp Value e = e
|
|
Packit |
9a2dfb |
catPairsExp Encoding e = [|commaSep|] `appE` e
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Create (an encoding of) a key-value pair.
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- > toPair Value "k" [|v|] = [|("k",v)|] -- The quoted string is actually Text.
|
|
Packit |
9a2dfb |
-- > toPair Encoding "k" [|"v"|] = [|"\"k\":v"|]
|
|
Packit |
9a2dfb |
toPair :: ToJSONFun -> String -> ExpQ -> ExpQ
|
|
Packit |
9a2dfb |
toPair Value k v = infixApp [|T.pack k|] [|(.=)|] v
|
|
Packit |
9a2dfb |
toPair Encoding k v = [|E.string k|] <:> v
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Map an associative list in an 'Object'.
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- > wrapObject Value [| [(k0,v0), (k1,v1)] |] = [| Object (fromList [(k0,v0), (k1,v1)]) |]
|
|
Packit |
9a2dfb |
-- > wrapObject Encoding [| "\"k0\":v0,\"k1\":v1" |] = [| "{\"k0\":v0,\"k1\":v1}" |]
|
|
Packit |
9a2dfb |
wrapObject :: ToJSONFun -> ExpQ -> ExpQ
|
|
Packit |
9a2dfb |
wrapObject Value e = [|A.object|] `appE` e
|
|
Packit |
9a2dfb |
wrapObject Encoding e = [|E.wrapObject|] `appE` e
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Separate 'Encoding's by commas.
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- > commaSep ["a","b","c"] = "a,b,c"
|
|
Packit |
9a2dfb |
commaSep :: [E.Encoding] -> E.Encoding
|
|
Packit |
9a2dfb |
commaSep [] = E.empty
|
|
Packit |
9a2dfb |
commaSep [x] = x
|
|
Packit |
9a2dfb |
commaSep (x : xs) = x E.>< E.comma E.>< commaSep xs
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
--------------------------------------------------------------------------------
|
|
Packit |
9a2dfb |
-- FromJSON
|
|
Packit |
9a2dfb |
--------------------------------------------------------------------------------
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Generates a 'FromJSON' instance declaration for the given data type or
|
|
Packit |
9a2dfb |
-- data family instance constructor.
|
|
Packit |
9a2dfb |
deriveFromJSON :: Options
|
|
Packit |
9a2dfb |
-- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name
|
|
Packit |
9a2dfb |
-- ^ Name of the type for which to generate a 'FromJSON' instance
|
|
Packit |
9a2dfb |
-- declaration.
|
|
Packit |
9a2dfb |
-> Q [Dec]
|
|
Packit |
9a2dfb |
deriveFromJSON = deriveFromJSONCommon fromJSONClass
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Generates a 'FromJSON1' instance declaration for the given data type or
|
|
Packit |
9a2dfb |
-- data family instance constructor.
|
|
Packit |
9a2dfb |
deriveFromJSON1 :: Options
|
|
Packit |
9a2dfb |
-- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name
|
|
Packit |
9a2dfb |
-- ^ Name of the type for which to generate a 'FromJSON1' instance
|
|
Packit |
9a2dfb |
-- declaration.
|
|
Packit |
9a2dfb |
-> Q [Dec]
|
|
Packit |
9a2dfb |
deriveFromJSON1 = deriveFromJSONCommon fromJSON1Class
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Generates a 'FromJSON2' instance declaration for the given data type or
|
|
Packit |
9a2dfb |
-- data family instance constructor.
|
|
Packit |
9a2dfb |
deriveFromJSON2 :: Options
|
|
Packit |
9a2dfb |
-- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name
|
|
Packit |
9a2dfb |
-- ^ Name of the type for which to generate a 'FromJSON3' instance
|
|
Packit |
9a2dfb |
-- declaration.
|
|
Packit |
9a2dfb |
-> Q [Dec]
|
|
Packit |
9a2dfb |
deriveFromJSON2 = deriveFromJSONCommon fromJSON2Class
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
deriveFromJSONCommon :: JSONClass
|
|
Packit |
9a2dfb |
-- ^ The FromJSON variant being derived.
|
|
Packit |
9a2dfb |
-> Options
|
|
Packit |
9a2dfb |
-- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name
|
|
Packit |
9a2dfb |
-- ^ Name of the type for which to generate an instance.
|
|
Packit |
9a2dfb |
-- declaration.
|
|
Packit |
9a2dfb |
-> Q [Dec]
|
|
Packit |
9a2dfb |
deriveFromJSONCommon = deriveJSONClass [(ParseJSON, consFromJSON)]
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Generates a lambda expression which parses the JSON encoding of the given
|
|
Packit |
9a2dfb |
-- data type or data family instance constructor.
|
|
Packit |
9a2dfb |
mkParseJSON :: Options -- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name -- ^ Name of the encoded type.
|
|
Packit |
9a2dfb |
-> Q Exp
|
|
Packit |
9a2dfb |
mkParseJSON = mkParseJSONCommon fromJSONClass
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Generates a lambda expression which parses the JSON encoding of the given
|
|
Packit |
9a2dfb |
-- data type or data family instance constructor by using the given parsing
|
|
Packit |
9a2dfb |
-- function on occurrences of the last type parameter.
|
|
Packit |
9a2dfb |
mkLiftParseJSON :: Options -- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name -- ^ Name of the encoded type.
|
|
Packit |
9a2dfb |
-> Q Exp
|
|
Packit |
9a2dfb |
mkLiftParseJSON = mkParseJSONCommon fromJSON1Class
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Generates a lambda expression which parses the JSON encoding of the given
|
|
Packit |
9a2dfb |
-- data type or data family instance constructor by using the given parsing
|
|
Packit |
9a2dfb |
-- functions on occurrences of the last two type parameters.
|
|
Packit |
9a2dfb |
mkLiftParseJSON2 :: Options -- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name -- ^ Name of the encoded type.
|
|
Packit |
9a2dfb |
-> Q Exp
|
|
Packit |
9a2dfb |
mkLiftParseJSON2 = mkParseJSONCommon fromJSON2Class
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
mkParseJSONCommon :: JSONClass -- ^ Which class's method is being derived.
|
|
Packit |
9a2dfb |
-> Options -- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name -- ^ Name of the encoded type.
|
|
Packit |
9a2dfb |
-> Q Exp
|
|
Packit |
9a2dfb |
mkParseJSONCommon = mkFunCommon consFromJSON
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Helper function used by both 'deriveFromJSON' and 'mkParseJSON'. Generates
|
|
Packit |
9a2dfb |
-- code to parse the JSON encoding of a number of constructors. All constructors
|
|
Packit |
9a2dfb |
-- must be from the same type.
|
|
Packit |
9a2dfb |
consFromJSON :: JSONClass
|
|
Packit |
9a2dfb |
-- ^ The FromJSON variant being derived.
|
|
Packit |
9a2dfb |
-> Name
|
|
Packit |
9a2dfb |
-- ^ Name of the type to which the constructors belong.
|
|
Packit |
9a2dfb |
-> Options
|
|
Packit |
9a2dfb |
-- ^ Encoding options
|
|
Packit |
9a2dfb |
-> [Type]
|
|
Packit |
9a2dfb |
-- ^ The types from the data type/data family instance declaration
|
|
Packit |
9a2dfb |
-> [ConstructorInfo]
|
|
Packit |
9a2dfb |
-- ^ Constructors for which to generate JSON parsing code.
|
|
Packit |
9a2dfb |
-> Q Exp
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
consFromJSON _ _ _ _ [] = error $ "Data.Aeson.TH.consFromJSON: "
|
|
Packit |
9a2dfb |
++ "Not a single constructor given!"
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
consFromJSON jc tName opts vars cons = do
|
|
Packit |
9a2dfb |
value <- newName "value"
|
|
Packit |
9a2dfb |
pjs <- newNameList "_pj" $ arityInt jc
|
|
Packit |
9a2dfb |
pjls <- newNameList "_pjl" $ arityInt jc
|
|
Packit |
9a2dfb |
let zippedPJs = zip pjs pjls
|
|
Packit |
9a2dfb |
interleavedPJs = interleave pjs pjls
|
|
Packit |
9a2dfb |
lastTyVars = map varTToName $ drop (length vars - arityInt jc) vars
|
|
Packit |
9a2dfb |
tvMap = M.fromList $ zip lastTyVars zippedPJs
|
|
Packit |
9a2dfb |
lamE (map varP $ interleavedPJs ++ [value]) $ lamExpr value tvMap
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
where
|
|
Packit |
9a2dfb |
checkExi tvMap con = checkExistentialContext jc tvMap
|
|
Packit |
9a2dfb |
(constructorContext con)
|
|
Packit |
9a2dfb |
(constructorName con)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
lamExpr value tvMap = case cons of
|
|
Packit |
9a2dfb |
[con]
|
|
Packit |
9a2dfb |
| not (tagSingleConstructors opts)
|
|
Packit |
9a2dfb |
-> checkExi tvMap con $ parseArgs jc tvMap tName opts con (Right value)
|
|
Packit |
9a2dfb |
_ | sumEncoding opts == UntaggedValue
|
|
Packit |
9a2dfb |
-> parseUntaggedValue tvMap cons value
|
|
Packit |
9a2dfb |
| otherwise
|
|
Packit |
9a2dfb |
-> caseE (varE value) $
|
|
Packit |
9a2dfb |
if allNullaryToStringTag opts && all isNullary cons
|
|
Packit |
9a2dfb |
then allNullaryMatches
|
|
Packit |
9a2dfb |
else mixedMatches tvMap
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
allNullaryMatches =
|
|
Packit |
9a2dfb |
[ do txt <- newName "txt"
|
|
Packit |
9a2dfb |
match (conP 'String [varP txt])
|
|
Packit |
9a2dfb |
(guardedB $
|
|
Packit |
9a2dfb |
[ liftM2 (,) (normalG $
|
|
Packit |
9a2dfb |
infixApp (varE txt)
|
|
Packit |
9a2dfb |
[|(==)|]
|
|
Packit |
9a2dfb |
(conTxt opts conName)
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
([|pure|] `appE` conE conName)
|
|
Packit |
9a2dfb |
| con <- cons
|
|
Packit |
9a2dfb |
, let conName = constructorName con
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
++
|
|
Packit |
9a2dfb |
[ liftM2 (,)
|
|
Packit |
9a2dfb |
(normalG [|otherwise|])
|
|
Packit |
9a2dfb |
( [|noMatchFail|]
|
|
Packit |
9a2dfb |
`appE` litE (stringL $ show tName)
|
|
Packit |
9a2dfb |
`appE` ([|T.unpack|] `appE` varE txt)
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
[]
|
|
Packit |
9a2dfb |
, do other <- newName "other"
|
|
Packit |
9a2dfb |
match (varP other)
|
|
Packit |
9a2dfb |
(normalB $ [|noStringFail|]
|
|
Packit |
9a2dfb |
`appE` litE (stringL $ show tName)
|
|
Packit |
9a2dfb |
`appE` ([|valueConName|] `appE` varE other)
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
[]
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
mixedMatches tvMap =
|
|
Packit |
9a2dfb |
case sumEncoding opts of
|
|
Packit |
9a2dfb |
TaggedObject {tagFieldName, contentsFieldName} ->
|
|
Packit |
9a2dfb |
parseObject $ parseTaggedObject tvMap tagFieldName contentsFieldName
|
|
Packit |
9a2dfb |
UntaggedValue -> error "UntaggedValue: Should be handled already"
|
|
Packit |
9a2dfb |
ObjectWithSingleField ->
|
|
Packit |
9a2dfb |
parseObject $ parseObjectWithSingleField tvMap
|
|
Packit |
9a2dfb |
TwoElemArray ->
|
|
Packit |
9a2dfb |
[ do arr <- newName "array"
|
|
Packit |
9a2dfb |
match (conP 'Array [varP arr])
|
|
Packit |
9a2dfb |
(guardedB
|
|
Packit |
9a2dfb |
[ liftM2 (,) (normalG $ infixApp ([|V.length|] `appE` varE arr)
|
|
Packit |
9a2dfb |
[|(==)|]
|
|
Packit |
9a2dfb |
(litE $ integerL 2))
|
|
Packit |
9a2dfb |
(parse2ElemArray tvMap arr)
|
|
Packit |
9a2dfb |
, liftM2 (,) (normalG [|otherwise|])
|
|
Packit |
9a2dfb |
([|not2ElemArray|]
|
|
Packit |
9a2dfb |
`appE` litE (stringL $ show tName)
|
|
Packit |
9a2dfb |
`appE` ([|V.length|] `appE` varE arr))
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
[]
|
|
Packit |
9a2dfb |
, do other <- newName "other"
|
|
Packit |
9a2dfb |
match (varP other)
|
|
Packit |
9a2dfb |
( normalB
|
|
Packit |
9a2dfb |
$ [|noArrayFail|]
|
|
Packit |
9a2dfb |
`appE` litE (stringL $ show tName)
|
|
Packit |
9a2dfb |
`appE` ([|valueConName|] `appE` varE other)
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
[]
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
parseObject f =
|
|
Packit |
9a2dfb |
[ do obj <- newName "obj"
|
|
Packit |
9a2dfb |
match (conP 'Object [varP obj]) (normalB $ f obj) []
|
|
Packit |
9a2dfb |
, do other <- newName "other"
|
|
Packit |
9a2dfb |
match (varP other)
|
|
Packit |
9a2dfb |
( normalB
|
|
Packit |
9a2dfb |
$ [|noObjectFail|]
|
|
Packit |
9a2dfb |
`appE` litE (stringL $ show tName)
|
|
Packit |
9a2dfb |
`appE` ([|valueConName|] `appE` varE other)
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
[]
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
parseTaggedObject tvMap typFieldName valFieldName obj = do
|
|
Packit |
9a2dfb |
conKey <- newName "conKey"
|
|
Packit |
9a2dfb |
doE [ bindS (varP conKey)
|
|
Packit |
9a2dfb |
(infixApp (varE obj)
|
|
Packit |
9a2dfb |
[|(.:)|]
|
|
Packit |
9a2dfb |
([|T.pack|] `appE` stringE typFieldName))
|
|
Packit |
9a2dfb |
, noBindS $ parseContents tvMap conKey (Left (valFieldName, obj)) 'conNotFoundFailTaggedObject
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
parseUntaggedValue tvMap cons' conVal =
|
|
Packit |
9a2dfb |
foldr1 (\e e' -> infixApp e [|(<|>)|] e')
|
|
Packit |
9a2dfb |
(map (\x -> parseValue tvMap x conVal) cons')
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
parseValue _tvMap
|
|
Packit |
9a2dfb |
ConstructorInfo { constructorName = conName
|
|
Packit |
9a2dfb |
, constructorVariant = NormalConstructor
|
|
Packit |
9a2dfb |
, constructorFields = [] }
|
|
Packit |
9a2dfb |
conVal = do
|
|
Packit |
9a2dfb |
str <- newName "str"
|
|
Packit |
9a2dfb |
caseE (varE conVal)
|
|
Packit |
9a2dfb |
[ match (conP 'String [varP str])
|
|
Packit |
9a2dfb |
(guardedB
|
|
Packit |
9a2dfb |
[ liftM2 (,) (normalG $ infixApp (varE str) [|(==)|] (conTxt opts conName)
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
([|pure|] `appE` conE conName)
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
[]
|
|
Packit |
9a2dfb |
, matchFailed tName conName "String"
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
parseValue tvMap con conVal =
|
|
Packit |
9a2dfb |
checkExi tvMap con $ parseArgs jc tvMap tName opts con (Right conVal)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
parse2ElemArray tvMap arr = do
|
|
Packit |
9a2dfb |
conKey <- newName "conKey"
|
|
Packit |
9a2dfb |
conVal <- newName "conVal"
|
|
Packit |
9a2dfb |
let letIx n ix =
|
|
Packit |
9a2dfb |
valD (varP n)
|
|
Packit |
9a2dfb |
(normalB ([|V.unsafeIndex|] `appE`
|
|
Packit |
9a2dfb |
varE arr `appE`
|
|
Packit |
9a2dfb |
litE (integerL ix)))
|
|
Packit |
9a2dfb |
[]
|
|
Packit |
9a2dfb |
letE [ letIx conKey 0
|
|
Packit |
9a2dfb |
, letIx conVal 1
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
(caseE (varE conKey)
|
|
Packit |
9a2dfb |
[ do txt <- newName "txt"
|
|
Packit |
9a2dfb |
match (conP 'String [varP txt])
|
|
Packit |
9a2dfb |
(normalB $ parseContents tvMap
|
|
Packit |
9a2dfb |
txt
|
|
Packit |
9a2dfb |
(Right conVal)
|
|
Packit |
9a2dfb |
'conNotFoundFail2ElemArray
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
[]
|
|
Packit |
9a2dfb |
, do other <- newName "other"
|
|
Packit |
9a2dfb |
match (varP other)
|
|
Packit |
9a2dfb |
( normalB
|
|
Packit |
9a2dfb |
$ [|firstElemNoStringFail|]
|
|
Packit |
9a2dfb |
`appE` litE (stringL $ show tName)
|
|
Packit |
9a2dfb |
`appE` ([|valueConName|] `appE` varE other)
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
[]
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
parseObjectWithSingleField tvMap obj = do
|
|
Packit |
9a2dfb |
conKey <- newName "conKey"
|
|
Packit |
9a2dfb |
conVal <- newName "conVal"
|
|
Packit |
9a2dfb |
caseE ([e|H.toList|] `appE` varE obj)
|
|
Packit |
9a2dfb |
[ match (listP [tupP [varP conKey, varP conVal]])
|
|
Packit |
9a2dfb |
(normalB $ parseContents tvMap conKey (Right conVal) 'conNotFoundFailObjectSingleField)
|
|
Packit |
9a2dfb |
[]
|
|
Packit |
9a2dfb |
, do other <- newName "other"
|
|
Packit |
9a2dfb |
match (varP other)
|
|
Packit |
9a2dfb |
(normalB $ [|wrongPairCountFail|]
|
|
Packit |
9a2dfb |
`appE` litE (stringL $ show tName)
|
|
Packit |
9a2dfb |
`appE` ([|show . length|] `appE` varE other)
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
[]
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
parseContents tvMap conKey contents errorFun =
|
|
Packit |
9a2dfb |
caseE (varE conKey)
|
|
Packit |
9a2dfb |
[ match wildP
|
|
Packit |
9a2dfb |
( guardedB $
|
|
Packit |
9a2dfb |
[ do g <- normalG $ infixApp (varE conKey)
|
|
Packit |
9a2dfb |
[|(==)|]
|
|
Packit |
9a2dfb |
([|T.pack|] `appE`
|
|
Packit |
9a2dfb |
conNameExp opts con)
|
|
Packit |
9a2dfb |
e <- checkExi tvMap con $
|
|
Packit |
9a2dfb |
parseArgs jc tvMap tName opts con contents
|
|
Packit |
9a2dfb |
return (g, e)
|
|
Packit |
9a2dfb |
| con <- cons
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
++
|
|
Packit |
9a2dfb |
[ liftM2 (,)
|
|
Packit |
9a2dfb |
(normalG [e|otherwise|])
|
|
Packit |
9a2dfb |
( varE errorFun
|
|
Packit |
9a2dfb |
`appE` litE (stringL $ show tName)
|
|
Packit |
9a2dfb |
`appE` listE (map ( litE
|
|
Packit |
9a2dfb |
. stringL
|
|
Packit |
9a2dfb |
. constructorTagModifier opts
|
|
Packit |
9a2dfb |
. nameBase
|
|
Packit |
9a2dfb |
. constructorName
|
|
Packit |
9a2dfb |
) cons
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
`appE` ([|T.unpack|] `appE` varE conKey)
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
[]
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
parseNullaryMatches :: Name -> Name -> [Q Match]
|
|
Packit |
9a2dfb |
parseNullaryMatches tName conName =
|
|
Packit |
9a2dfb |
[ do arr <- newName "arr"
|
|
Packit |
9a2dfb |
match (conP 'Array [varP arr])
|
|
Packit |
9a2dfb |
(guardedB
|
|
Packit |
9a2dfb |
[ liftM2 (,) (normalG $ [|V.null|] `appE` varE arr)
|
|
Packit |
9a2dfb |
([|pure|] `appE` conE conName)
|
|
Packit |
9a2dfb |
, liftM2 (,) (normalG [|otherwise|])
|
|
Packit |
9a2dfb |
(parseTypeMismatch tName conName
|
|
Packit |
9a2dfb |
(litE $ stringL "an empty Array")
|
|
Packit |
9a2dfb |
(infixApp (litE $ stringL "Array of length ")
|
|
Packit |
9a2dfb |
[|(++)|]
|
|
Packit |
9a2dfb |
([|show . V.length|] `appE` varE arr)
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
[]
|
|
Packit |
9a2dfb |
, matchFailed tName conName "Array"
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
parseUnaryMatches :: JSONClass -> TyVarMap -> Type -> Name -> [Q Match]
|
|
Packit |
9a2dfb |
parseUnaryMatches jc tvMap argTy conName =
|
|
Packit |
9a2dfb |
[ do arg <- newName "arg"
|
|
Packit |
9a2dfb |
match (varP arg)
|
|
Packit |
9a2dfb |
( normalB $ infixApp (conE conName)
|
|
Packit |
9a2dfb |
[|(<$>)|]
|
|
Packit |
9a2dfb |
(dispatchParseJSON jc conName tvMap argTy
|
|
Packit |
9a2dfb |
`appE` varE arg)
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
[]
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
parseRecord :: JSONClass
|
|
Packit |
9a2dfb |
-> TyVarMap
|
|
Packit |
9a2dfb |
-> [Type]
|
|
Packit |
9a2dfb |
-> Options
|
|
Packit |
9a2dfb |
-> Name
|
|
Packit |
9a2dfb |
-> Name
|
|
Packit |
9a2dfb |
-> [Name]
|
|
Packit |
9a2dfb |
-> Name
|
|
Packit |
9a2dfb |
-> ExpQ
|
|
Packit |
9a2dfb |
parseRecord jc tvMap argTys opts tName conName fields obj =
|
|
Packit |
9a2dfb |
foldl' (\a b -> infixApp a [|(<*>)|] b)
|
|
Packit |
9a2dfb |
(infixApp (conE conName) [|(<$>)|] x)
|
|
Packit |
9a2dfb |
xs
|
|
Packit |
9a2dfb |
where
|
|
Packit |
9a2dfb |
x:xs = [ [|lookupField|]
|
|
Packit |
9a2dfb |
`appE` dispatchParseJSON jc conName tvMap argTy
|
|
Packit |
9a2dfb |
`appE` litE (stringL $ show tName)
|
|
Packit |
9a2dfb |
`appE` litE (stringL $ constructorTagModifier opts $ nameBase conName)
|
|
Packit |
9a2dfb |
`appE` varE obj
|
|
Packit |
9a2dfb |
`appE` ( [|T.pack|] `appE` stringE (fieldLabel opts field)
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
| (field, argTy) <- zip fields argTys
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
getValField :: Name -> String -> [MatchQ] -> Q Exp
|
|
Packit |
9a2dfb |
getValField obj valFieldName matches = do
|
|
Packit |
9a2dfb |
val <- newName "val"
|
|
Packit |
9a2dfb |
doE [ bindS (varP val) $ infixApp (varE obj)
|
|
Packit |
9a2dfb |
[|(.:)|]
|
|
Packit |
9a2dfb |
([|T.pack|] `appE`
|
|
Packit |
9a2dfb |
litE (stringL valFieldName))
|
|
Packit |
9a2dfb |
, noBindS $ caseE (varE val) matches
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
matchCases :: Either (String, Name) Name -> [MatchQ] -> Q Exp
|
|
Packit |
9a2dfb |
matchCases (Left (valFieldName, obj)) = getValField obj valFieldName
|
|
Packit |
9a2dfb |
matchCases (Right valName) = caseE (varE valName)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Generates code to parse the JSON encoding of a single constructor.
|
|
Packit |
9a2dfb |
parseArgs :: JSONClass -- ^ The FromJSON variant being derived.
|
|
Packit |
9a2dfb |
-> TyVarMap -- ^ Maps the last type variables to their decoding
|
|
Packit |
9a2dfb |
-- function arguments.
|
|
Packit |
9a2dfb |
-> Name -- ^ Name of the type to which the constructor belongs.
|
|
Packit |
9a2dfb |
-> Options -- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> ConstructorInfo -- ^ Constructor for which to generate JSON parsing code.
|
|
Packit |
9a2dfb |
-> Either (String, Name) Name -- ^ Left (valFieldName, objName) or
|
|
Packit |
9a2dfb |
-- Right valName
|
|
Packit |
9a2dfb |
-> Q Exp
|
|
Packit |
9a2dfb |
-- Nullary constructors.
|
|
Packit |
9a2dfb |
parseArgs _ _ _ _
|
|
Packit |
9a2dfb |
ConstructorInfo { constructorName = conName
|
|
Packit |
9a2dfb |
, constructorVariant = NormalConstructor
|
|
Packit |
9a2dfb |
, constructorFields = [] }
|
|
Packit |
9a2dfb |
(Left _) =
|
|
Packit |
9a2dfb |
[|pure|] `appE` conE conName
|
|
Packit |
9a2dfb |
parseArgs _ _ tName _
|
|
Packit |
9a2dfb |
ConstructorInfo { constructorName = conName
|
|
Packit |
9a2dfb |
, constructorVariant = NormalConstructor
|
|
Packit |
9a2dfb |
, constructorFields = [] }
|
|
Packit |
9a2dfb |
(Right valName) =
|
|
Packit |
9a2dfb |
caseE (varE valName) $ parseNullaryMatches tName conName
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- Unary constructors.
|
|
Packit |
9a2dfb |
parseArgs jc tvMap _ _
|
|
Packit |
9a2dfb |
ConstructorInfo { constructorName = conName
|
|
Packit |
9a2dfb |
, constructorVariant = NormalConstructor
|
|
Packit |
9a2dfb |
, constructorFields = [argTy] }
|
|
Packit |
9a2dfb |
contents = do
|
|
Packit |
9a2dfb |
argTy' <- resolveTypeSynonyms argTy
|
|
Packit |
9a2dfb |
matchCases contents $ parseUnaryMatches jc tvMap argTy' conName
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- Polyadic constructors.
|
|
Packit |
9a2dfb |
parseArgs jc tvMap tName _
|
|
Packit |
9a2dfb |
ConstructorInfo { constructorName = conName
|
|
Packit |
9a2dfb |
, constructorVariant = NormalConstructor
|
|
Packit |
9a2dfb |
, constructorFields = argTys }
|
|
Packit |
9a2dfb |
contents = do
|
|
Packit |
9a2dfb |
argTys' <- mapM resolveTypeSynonyms argTys
|
|
Packit |
9a2dfb |
let len = genericLength argTys'
|
|
Packit |
9a2dfb |
matchCases contents $ parseProduct jc tvMap argTys' tName conName len
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- Records.
|
|
Packit |
9a2dfb |
parseArgs jc tvMap tName opts
|
|
Packit |
9a2dfb |
ConstructorInfo { constructorName = conName
|
|
Packit |
9a2dfb |
, constructorVariant = RecordConstructor fields
|
|
Packit |
9a2dfb |
, constructorFields = argTys }
|
|
Packit |
9a2dfb |
(Left (_, obj)) = do
|
|
Packit |
9a2dfb |
argTys' <- mapM resolveTypeSynonyms argTys
|
|
Packit |
9a2dfb |
parseRecord jc tvMap argTys' opts tName conName fields obj
|
|
Packit |
9a2dfb |
parseArgs jc tvMap tName opts
|
|
Packit |
9a2dfb |
info@ConstructorInfo { constructorName = conName
|
|
Packit |
9a2dfb |
, constructorVariant = RecordConstructor fields
|
|
Packit |
9a2dfb |
, constructorFields = argTys }
|
|
Packit |
9a2dfb |
(Right valName) =
|
|
Packit |
9a2dfb |
case (unwrapUnaryRecords opts,argTys) of
|
|
Packit |
9a2dfb |
(True,[_])-> parseArgs jc tvMap tName opts
|
|
Packit |
9a2dfb |
(info{constructorVariant = NormalConstructor})
|
|
Packit |
9a2dfb |
(Right valName)
|
|
Packit |
9a2dfb |
_ -> do
|
|
Packit |
9a2dfb |
obj <- newName "recObj"
|
|
Packit |
9a2dfb |
argTys' <- mapM resolveTypeSynonyms argTys
|
|
Packit |
9a2dfb |
caseE (varE valName)
|
|
Packit |
9a2dfb |
[ match (conP 'Object [varP obj]) (normalB $
|
|
Packit |
9a2dfb |
parseRecord jc tvMap argTys' opts tName conName fields obj) []
|
|
Packit |
9a2dfb |
, matchFailed tName conName "Object"
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- Infix constructors. Apart from syntax these are the same as
|
|
Packit |
9a2dfb |
-- polyadic constructors.
|
|
Packit |
9a2dfb |
parseArgs jc tvMap tName _
|
|
Packit |
9a2dfb |
ConstructorInfo { constructorName = conName
|
|
Packit |
9a2dfb |
, constructorVariant = InfixConstructor
|
|
Packit |
9a2dfb |
, constructorFields = argTys }
|
|
Packit |
9a2dfb |
contents = do
|
|
Packit |
9a2dfb |
argTys' <- mapM resolveTypeSynonyms argTys
|
|
Packit |
9a2dfb |
matchCases contents $ parseProduct jc tvMap argTys' tName conName 2
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Generates code to parse the JSON encoding of an n-ary
|
|
Packit |
9a2dfb |
-- constructor.
|
|
Packit |
9a2dfb |
parseProduct :: JSONClass -- ^ The FromJSON variant being derived.
|
|
Packit |
9a2dfb |
-> TyVarMap -- ^ Maps the last type variables to their decoding
|
|
Packit |
9a2dfb |
-- function arguments.
|
|
Packit |
9a2dfb |
-> [Type] -- ^ The argument types of the constructor.
|
|
Packit |
9a2dfb |
-> Name -- ^ Name of the type to which the constructor belongs.
|
|
Packit |
9a2dfb |
-> Name -- ^ 'Con'structor name.
|
|
Packit |
9a2dfb |
-> Integer -- ^ 'Con'structor arity.
|
|
Packit |
9a2dfb |
-> [Q Match]
|
|
Packit |
9a2dfb |
parseProduct jc tvMap argTys tName conName numArgs =
|
|
Packit |
9a2dfb |
[ do arr <- newName "arr"
|
|
Packit |
9a2dfb |
-- List of: "parseJSON (arr `V.unsafeIndex` <IX>)"
|
|
Packit |
9a2dfb |
let x:xs = [ dispatchParseJSON jc conName tvMap argTy
|
|
Packit |
9a2dfb |
`appE`
|
|
Packit |
9a2dfb |
infixApp (varE arr)
|
|
Packit |
9a2dfb |
[|V.unsafeIndex|]
|
|
Packit |
9a2dfb |
(litE $ integerL ix)
|
|
Packit |
9a2dfb |
| (argTy, ix) <- zip argTys [0 .. numArgs - 1]
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
match (conP 'Array [varP arr])
|
|
Packit |
9a2dfb |
(normalB $ condE ( infixApp ([|V.length|] `appE` varE arr)
|
|
Packit |
9a2dfb |
[|(==)|]
|
|
Packit |
9a2dfb |
(litE $ integerL numArgs)
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
( foldl' (\a b -> infixApp a [|(<*>)|] b)
|
|
Packit |
9a2dfb |
(infixApp (conE conName) [|(<$>)|] x)
|
|
Packit |
9a2dfb |
xs
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
( parseTypeMismatch tName conName
|
|
Packit |
9a2dfb |
(litE $ stringL $ "Array of length " ++ show numArgs)
|
|
Packit |
9a2dfb |
( infixApp (litE $ stringL "Array of length ")
|
|
Packit |
9a2dfb |
[|(++)|]
|
|
Packit |
9a2dfb |
([|show . V.length|] `appE` varE arr)
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
[]
|
|
Packit |
9a2dfb |
, matchFailed tName conName "Array"
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
--------------------------------------------------------------------------------
|
|
Packit |
9a2dfb |
-- Parsing errors
|
|
Packit |
9a2dfb |
--------------------------------------------------------------------------------
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
matchFailed :: Name -> Name -> String -> MatchQ
|
|
Packit |
9a2dfb |
matchFailed tName conName expected = do
|
|
Packit |
9a2dfb |
other <- newName "other"
|
|
Packit |
9a2dfb |
match (varP other)
|
|
Packit |
9a2dfb |
( normalB $ parseTypeMismatch tName conName
|
|
Packit |
9a2dfb |
(litE $ stringL expected)
|
|
Packit |
9a2dfb |
([|valueConName|] `appE` varE other)
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
[]
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
parseTypeMismatch :: Name -> Name -> ExpQ -> ExpQ -> ExpQ
|
|
Packit |
9a2dfb |
parseTypeMismatch tName conName expected actual =
|
|
Packit |
9a2dfb |
foldl appE
|
|
Packit |
9a2dfb |
[|parseTypeMismatch'|]
|
|
Packit |
9a2dfb |
[ litE $ stringL $ nameBase conName
|
|
Packit |
9a2dfb |
, litE $ stringL $ show tName
|
|
Packit |
9a2dfb |
, expected
|
|
Packit |
9a2dfb |
, actual
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
class LookupField a where
|
|
Packit |
9a2dfb |
lookupField :: (Value -> Parser a) -> String -> String
|
|
Packit |
9a2dfb |
-> Object -> T.Text -> Parser a
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
instance OVERLAPPABLE_ LookupField a where
|
|
Packit |
9a2dfb |
lookupField = lookupFieldWith
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
instance INCOHERENT_ LookupField (Maybe a) where
|
|
Packit |
9a2dfb |
lookupField pj _ _ = parseOptionalFieldWith pj
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
instance INCOHERENT_ LookupField (Semigroup.Option a) where
|
|
Packit |
9a2dfb |
lookupField pj tName rec obj key =
|
|
Packit |
9a2dfb |
fmap Semigroup.Option
|
|
Packit |
9a2dfb |
(lookupField (fmap Semigroup.getOption . pj) tName rec obj key)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
lookupFieldWith :: (Value -> Parser a) -> String -> String
|
|
Packit |
9a2dfb |
-> Object -> T.Text -> Parser a
|
|
Packit |
9a2dfb |
lookupFieldWith pj tName rec obj key =
|
|
Packit |
9a2dfb |
case H.lookup key obj of
|
|
Packit |
9a2dfb |
Nothing -> unknownFieldFail tName rec (T.unpack key)
|
|
Packit |
9a2dfb |
Just v -> pj v Key key
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
unknownFieldFail :: String -> String -> String -> Parser fail
|
|
Packit |
9a2dfb |
unknownFieldFail tName rec key =
|
|
Packit |
9a2dfb |
fail $ printf "When parsing the record %s of type %s the key %s was not present."
|
|
Packit |
9a2dfb |
rec tName key
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
noArrayFail :: String -> String -> Parser fail
|
|
Packit |
9a2dfb |
noArrayFail t o = fail $ printf "When parsing %s expected Array but got %s." t o
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
noObjectFail :: String -> String -> Parser fail
|
|
Packit |
9a2dfb |
noObjectFail t o = fail $ printf "When parsing %s expected Object but got %s." t o
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
firstElemNoStringFail :: String -> String -> Parser fail
|
|
Packit |
9a2dfb |
firstElemNoStringFail t o = fail $ printf "When parsing %s expected an Array of 2 elements where the first element is a String but got %s at the first element." t o
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
wrongPairCountFail :: String -> String -> Parser fail
|
|
Packit |
9a2dfb |
wrongPairCountFail t n =
|
|
Packit |
9a2dfb |
fail $ printf "When parsing %s expected an Object with a single tag/contents pair but got %s pairs."
|
|
Packit |
9a2dfb |
t n
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
noStringFail :: String -> String -> Parser fail
|
|
Packit |
9a2dfb |
noStringFail t o = fail $ printf "When parsing %s expected String but got %s." t o
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
noMatchFail :: String -> String -> Parser fail
|
|
Packit |
9a2dfb |
noMatchFail t o =
|
|
Packit |
9a2dfb |
fail $ printf "When parsing %s expected a String with the tag of a constructor but got %s." t o
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
not2ElemArray :: String -> Int -> Parser fail
|
|
Packit |
9a2dfb |
not2ElemArray t i = fail $ printf "When parsing %s expected an Array of 2 elements but got %i elements" t i
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
conNotFoundFail2ElemArray :: String -> [String] -> String -> Parser fail
|
|
Packit |
9a2dfb |
conNotFoundFail2ElemArray t cs o =
|
|
Packit |
9a2dfb |
fail $ printf "When parsing %s expected a 2-element Array with a tag and contents element where the tag is one of [%s], but got %s."
|
|
Packit |
9a2dfb |
t (intercalate ", " cs) o
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
conNotFoundFailObjectSingleField :: String -> [String] -> String -> Parser fail
|
|
Packit |
9a2dfb |
conNotFoundFailObjectSingleField t cs o =
|
|
Packit |
9a2dfb |
fail $ printf "When parsing %s expected an Object with a single tag/contents pair where the tag is one of [%s], but got %s."
|
|
Packit |
9a2dfb |
t (intercalate ", " cs) o
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
conNotFoundFailTaggedObject :: String -> [String] -> String -> Parser fail
|
|
Packit |
9a2dfb |
conNotFoundFailTaggedObject t cs o =
|
|
Packit |
9a2dfb |
fail $ printf "When parsing %s expected an Object with a tag field where the value is one of [%s], but got %s."
|
|
Packit |
9a2dfb |
t (intercalate ", " cs) o
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
parseTypeMismatch' :: String -> String -> String -> String -> Parser fail
|
|
Packit |
9a2dfb |
parseTypeMismatch' conName tName expected actual =
|
|
Packit |
9a2dfb |
fail $ printf "When parsing the constructor %s of type %s expected %s but got %s."
|
|
Packit |
9a2dfb |
conName tName expected actual
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
--------------------------------------------------------------------------------
|
|
Packit |
9a2dfb |
-- Shared ToJSON and FromJSON code
|
|
Packit |
9a2dfb |
--------------------------------------------------------------------------------
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Functionality common to 'deriveJSON', 'deriveJSON1', and 'deriveJSON2'.
|
|
Packit |
9a2dfb |
deriveJSONBoth :: (Options -> Name -> Q [Dec])
|
|
Packit |
9a2dfb |
-- ^ Function which derives a flavor of 'ToJSON'.
|
|
Packit |
9a2dfb |
-> (Options -> Name -> Q [Dec])
|
|
Packit |
9a2dfb |
-- ^ Function which derives a flavor of 'FromJSON'.
|
|
Packit |
9a2dfb |
-> Options
|
|
Packit |
9a2dfb |
-- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name
|
|
Packit |
9a2dfb |
-- ^ Name of the type for which to generate 'ToJSON' and 'FromJSON'
|
|
Packit |
9a2dfb |
-- instances.
|
|
Packit |
9a2dfb |
-> Q [Dec]
|
|
Packit |
9a2dfb |
deriveJSONBoth dtj dfj opts name =
|
|
Packit |
9a2dfb |
liftM2 (++) (dtj opts name) (dfj opts name)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Functionality common to @deriveToJSON(1)(2)@ and @deriveFromJSON(1)(2)@.
|
|
Packit |
9a2dfb |
deriveJSONClass :: [(JSONFun, JSONClass -> Name -> Options -> [Type]
|
|
Packit |
9a2dfb |
-> [ConstructorInfo] -> Q Exp)]
|
|
Packit |
9a2dfb |
-- ^ The class methods and the functions which derive them.
|
|
Packit |
9a2dfb |
-> JSONClass
|
|
Packit |
9a2dfb |
-- ^ The class for which to generate an instance.
|
|
Packit |
9a2dfb |
-> Options
|
|
Packit |
9a2dfb |
-- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name
|
|
Packit |
9a2dfb |
-- ^ Name of the type for which to generate a class instance
|
|
Packit |
9a2dfb |
-- declaration.
|
|
Packit |
9a2dfb |
-> Q [Dec]
|
|
Packit |
9a2dfb |
deriveJSONClass consFuns jc opts name = do
|
|
Packit |
9a2dfb |
info <- reifyDatatype name
|
|
Packit |
9a2dfb |
case info of
|
|
Packit |
9a2dfb |
DatatypeInfo { datatypeContext = ctxt
|
|
Packit |
9a2dfb |
, datatypeName = parentName
|
|
Packit |
9a2dfb |
, datatypeVars = vars
|
|
Packit |
9a2dfb |
, datatypeVariant = variant
|
|
Packit |
9a2dfb |
, datatypeCons = cons
|
|
Packit |
9a2dfb |
} -> do
|
|
Packit |
9a2dfb |
(instanceCxt, instanceType)
|
|
Packit |
9a2dfb |
<- buildTypeInstance parentName jc ctxt vars variant
|
|
Packit |
9a2dfb |
(:[]) <$> instanceD (return instanceCxt)
|
|
Packit |
9a2dfb |
(return instanceType)
|
|
Packit |
9a2dfb |
(methodDecs parentName vars cons)
|
|
Packit |
9a2dfb |
where
|
|
Packit |
9a2dfb |
methodDecs :: Name -> [Type] -> [ConstructorInfo] -> [Q Dec]
|
|
Packit |
9a2dfb |
methodDecs parentName vars cons = flip map consFuns $ \(jf, jfMaker) ->
|
|
Packit |
9a2dfb |
funD (jsonFunValName jf (arity jc))
|
|
Packit |
9a2dfb |
[ clause []
|
|
Packit |
9a2dfb |
(normalB $ jfMaker jc parentName opts vars cons)
|
|
Packit |
9a2dfb |
[]
|
|
Packit |
9a2dfb |
]
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
mkFunCommon :: (JSONClass -> Name -> Options -> [Type] -> [ConstructorInfo] -> Q Exp)
|
|
Packit |
9a2dfb |
-- ^ The function which derives the expression.
|
|
Packit |
9a2dfb |
-> JSONClass
|
|
Packit |
9a2dfb |
-- ^ Which class's method is being derived.
|
|
Packit |
9a2dfb |
-> Options
|
|
Packit |
9a2dfb |
-- ^ Encoding options.
|
|
Packit |
9a2dfb |
-> Name
|
|
Packit |
9a2dfb |
-- ^ Name of the encoded type.
|
|
Packit |
9a2dfb |
-> Q Exp
|
|
Packit |
9a2dfb |
mkFunCommon consFun jc opts name = do
|
|
Packit |
9a2dfb |
info <- reifyDatatype name
|
|
Packit |
9a2dfb |
case info of
|
|
Packit |
9a2dfb |
DatatypeInfo { datatypeContext = ctxt
|
|
Packit |
9a2dfb |
, datatypeName = parentName
|
|
Packit |
9a2dfb |
, datatypeVars = vars
|
|
Packit |
9a2dfb |
, datatypeVariant = variant
|
|
Packit |
9a2dfb |
, datatypeCons = cons
|
|
Packit |
9a2dfb |
} -> do
|
|
Packit |
9a2dfb |
-- We force buildTypeInstance here since it performs some checks for whether
|
|
Packit |
9a2dfb |
-- or not the provided datatype's kind matches the derived method's
|
|
Packit |
9a2dfb |
-- typeclass, and produces errors if it can't.
|
|
Packit |
9a2dfb |
!_ <- buildTypeInstance parentName jc ctxt vars variant
|
|
Packit |
9a2dfb |
consFun jc parentName opts vars cons
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
dispatchFunByType :: JSONClass
|
|
Packit |
9a2dfb |
-> JSONFun
|
|
Packit |
9a2dfb |
-> Name
|
|
Packit |
9a2dfb |
-> TyVarMap
|
|
Packit |
9a2dfb |
-> Bool -- True if we are using the function argument that works
|
|
Packit |
9a2dfb |
-- on lists (e.g., [a] -> Value). False is we are using
|
|
Packit |
9a2dfb |
-- the function argument that works on single values
|
|
Packit |
9a2dfb |
-- (e.g., a -> Value).
|
|
Packit |
9a2dfb |
-> Type
|
|
Packit |
9a2dfb |
-> Q Exp
|
|
Packit |
9a2dfb |
dispatchFunByType _ jf _ tvMap list (VarT tyName) =
|
|
Packit |
9a2dfb |
varE $ case M.lookup tyName tvMap of
|
|
Packit |
9a2dfb |
Just (tfjExp, tfjlExp) -> if list then tfjlExp else tfjExp
|
|
Packit |
9a2dfb |
Nothing -> jsonFunValOrListName list jf Arity0
|
|
Packit |
9a2dfb |
dispatchFunByType jc jf conName tvMap list (SigT ty _) =
|
|
Packit |
9a2dfb |
dispatchFunByType jc jf conName tvMap list ty
|
|
Packit |
9a2dfb |
dispatchFunByType jc jf conName tvMap list (ForallT _ _ ty) =
|
|
Packit |
9a2dfb |
dispatchFunByType jc jf conName tvMap list ty
|
|
Packit |
9a2dfb |
dispatchFunByType jc jf conName tvMap list ty = do
|
|
Packit |
9a2dfb |
let tyCon :: Type
|
|
Packit |
9a2dfb |
tyArgs :: [Type]
|
|
Packit |
9a2dfb |
tyCon :| tyArgs = unapplyTy ty
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
numLastArgs :: Int
|
|
Packit |
9a2dfb |
numLastArgs = min (arityInt jc) (length tyArgs)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
lhsArgs, rhsArgs :: [Type]
|
|
Packit |
9a2dfb |
(lhsArgs, rhsArgs) = splitAt (length tyArgs - numLastArgs) tyArgs
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
tyVarNames :: [Name]
|
|
Packit |
9a2dfb |
tyVarNames = M.keys tvMap
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
itf <- isTyFamily tyCon
|
|
Packit |
9a2dfb |
if any (`mentionsName` tyVarNames) lhsArgs
|
|
Packit |
9a2dfb |
|| itf && any (`mentionsName` tyVarNames) tyArgs
|
|
Packit |
9a2dfb |
then outOfPlaceTyVarError jc conName
|
|
Packit |
9a2dfb |
else if any (`mentionsName` tyVarNames) rhsArgs
|
|
Packit |
9a2dfb |
then appsE $ varE (jsonFunValOrListName list jf $ toEnum numLastArgs)
|
|
Packit |
9a2dfb |
: zipWith (dispatchFunByType jc jf conName tvMap)
|
|
Packit |
9a2dfb |
(cycle [False,True])
|
|
Packit |
9a2dfb |
(interleave rhsArgs rhsArgs)
|
|
Packit |
9a2dfb |
else varE $ jsonFunValOrListName list jf Arity0
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
dispatchToJSON
|
|
Packit |
9a2dfb |
:: ToJSONFun -> JSONClass -> Name -> TyVarMap -> Type -> Q Exp
|
|
Packit |
9a2dfb |
dispatchToJSON target jc n tvMap =
|
|
Packit |
9a2dfb |
dispatchFunByType jc (targetToJSONFun target) n tvMap False
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
dispatchParseJSON
|
|
Packit |
9a2dfb |
:: JSONClass -> Name -> TyVarMap -> Type -> Q Exp
|
|
Packit |
9a2dfb |
dispatchParseJSON jc n tvMap = dispatchFunByType jc ParseJSON n tvMap False
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
--------------------------------------------------------------------------------
|
|
Packit |
9a2dfb |
-- Utility functions
|
|
Packit |
9a2dfb |
--------------------------------------------------------------------------------
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- For the given Types, generate an instance context and head.
|
|
Packit |
9a2dfb |
buildTypeInstance :: Name
|
|
Packit |
9a2dfb |
-- ^ The type constructor or data family name
|
|
Packit |
9a2dfb |
-> JSONClass
|
|
Packit |
9a2dfb |
-- ^ The typeclass to derive
|
|
Packit |
9a2dfb |
-> Cxt
|
|
Packit |
9a2dfb |
-- ^ The datatype context
|
|
Packit |
9a2dfb |
-> [Type]
|
|
Packit |
9a2dfb |
-- ^ The types to instantiate the instance with
|
|
Packit |
9a2dfb |
-> DatatypeVariant
|
|
Packit |
9a2dfb |
-- ^ Are we dealing with a data family instance or not
|
|
Packit |
9a2dfb |
-> Q (Cxt, Type)
|
|
Packit |
9a2dfb |
buildTypeInstance tyConName jc dataCxt varTysOrig variant = do
|
|
Packit |
9a2dfb |
-- Make sure to expand through type/kind synonyms! Otherwise, the
|
|
Packit |
9a2dfb |
-- eta-reduction check might get tripped up over type variables in a
|
|
Packit |
9a2dfb |
-- synonym that are actually dropped.
|
|
Packit |
9a2dfb |
-- (See GHC Trac #11416 for a scenario where this actually happened.)
|
|
Packit |
9a2dfb |
varTysExp <- mapM resolveTypeSynonyms varTysOrig
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
let remainingLength :: Int
|
|
Packit |
9a2dfb |
remainingLength = length varTysOrig - arityInt jc
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
droppedTysExp :: [Type]
|
|
Packit |
9a2dfb |
droppedTysExp = drop remainingLength varTysExp
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
droppedStarKindStati :: [StarKindStatus]
|
|
Packit |
9a2dfb |
droppedStarKindStati = map canRealizeKindStar droppedTysExp
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- Check there are enough types to drop and that all of them are either of
|
|
Packit |
9a2dfb |
-- kind * or kind k (for some kind variable k). If not, throw an error.
|
|
Packit |
9a2dfb |
when (remainingLength < 0 || elem NotKindStar droppedStarKindStati) $
|
|
Packit |
9a2dfb |
derivingKindError jc tyConName
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
let droppedKindVarNames :: [Name]
|
|
Packit |
9a2dfb |
droppedKindVarNames = catKindVarNames droppedStarKindStati
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- Substitute kind * for any dropped kind variables
|
|
Packit |
9a2dfb |
varTysExpSubst :: [Type]
|
|
Packit |
9a2dfb |
varTysExpSubst = map (substNamesWithKindStar droppedKindVarNames) varTysExp
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
remainingTysExpSubst, droppedTysExpSubst :: [Type]
|
|
Packit |
9a2dfb |
(remainingTysExpSubst, droppedTysExpSubst) =
|
|
Packit |
9a2dfb |
splitAt remainingLength varTysExpSubst
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- All of the type variables mentioned in the dropped types
|
|
Packit |
9a2dfb |
-- (post-synonym expansion)
|
|
Packit |
9a2dfb |
droppedTyVarNames :: [Name]
|
|
Packit |
9a2dfb |
droppedTyVarNames = freeVariables droppedTysExpSubst
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- If any of the dropped types were polykinded, ensure that they are of kind *
|
|
Packit |
9a2dfb |
-- after substituting * for the dropped kind variables. If not, throw an error.
|
|
Packit |
9a2dfb |
unless (all hasKindStar droppedTysExpSubst) $
|
|
Packit |
9a2dfb |
derivingKindError jc tyConName
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
let preds :: [Maybe Pred]
|
|
Packit |
9a2dfb |
kvNames :: [[Name]]
|
|
Packit |
9a2dfb |
kvNames' :: [Name]
|
|
Packit |
9a2dfb |
-- Derive instance constraints (and any kind variables which are specialized
|
|
Packit |
9a2dfb |
-- to * in those constraints)
|
|
Packit |
9a2dfb |
(preds, kvNames) = unzip $ map (deriveConstraint jc) remainingTysExpSubst
|
|
Packit |
9a2dfb |
kvNames' = concat kvNames
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- Substitute the kind variables specialized in the constraints with *
|
|
Packit |
9a2dfb |
remainingTysExpSubst' :: [Type]
|
|
Packit |
9a2dfb |
remainingTysExpSubst' =
|
|
Packit |
9a2dfb |
map (substNamesWithKindStar kvNames') remainingTysExpSubst
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- We now substitute all of the specialized-to-* kind variable names with
|
|
Packit |
9a2dfb |
-- *, but in the original types, not the synonym-expanded types. The reason
|
|
Packit |
9a2dfb |
-- we do this is a superficial one: we want the derived instance to resemble
|
|
Packit |
9a2dfb |
-- the datatype written in source code as closely as possible. For example,
|
|
Packit |
9a2dfb |
-- for the following data family instance:
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- data family Fam a
|
|
Packit |
9a2dfb |
-- newtype instance Fam String = Fam String
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- We'd want to generate the instance:
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- instance C (Fam String)
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- Not:
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- instance C (Fam [Char])
|
|
Packit |
9a2dfb |
remainingTysOrigSubst :: [Type]
|
|
Packit |
9a2dfb |
remainingTysOrigSubst =
|
|
Packit |
9a2dfb |
map (substNamesWithKindStar (droppedKindVarNames `union` kvNames'))
|
|
Packit |
9a2dfb |
$ take remainingLength varTysOrig
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
isDataFamily :: Bool
|
|
Packit |
9a2dfb |
isDataFamily = case variant of
|
|
Packit |
9a2dfb |
Datatype -> False
|
|
Packit |
9a2dfb |
Newtype -> False
|
|
Packit |
9a2dfb |
DataInstance -> True
|
|
Packit |
9a2dfb |
NewtypeInstance -> True
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
remainingTysOrigSubst' :: [Type]
|
|
Packit |
9a2dfb |
-- See Note [Kind signatures in derived instances] for an explanation
|
|
Packit |
9a2dfb |
-- of the isDataFamily check.
|
|
Packit |
9a2dfb |
remainingTysOrigSubst' =
|
|
Packit |
9a2dfb |
if isDataFamily
|
|
Packit |
9a2dfb |
then remainingTysOrigSubst
|
|
Packit |
9a2dfb |
else map unSigT remainingTysOrigSubst
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
instanceCxt :: Cxt
|
|
Packit |
9a2dfb |
instanceCxt = catMaybes preds
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
instanceType :: Type
|
|
Packit |
9a2dfb |
instanceType = AppT (ConT $ jsonClassName jc)
|
|
Packit |
9a2dfb |
$ applyTyCon tyConName remainingTysOrigSubst'
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- If the datatype context mentions any of the dropped type variables,
|
|
Packit |
9a2dfb |
-- we can't derive an instance, so throw an error.
|
|
Packit |
9a2dfb |
when (any (`predMentionsName` droppedTyVarNames) dataCxt) $
|
|
Packit |
9a2dfb |
datatypeContextError tyConName instanceType
|
|
Packit |
9a2dfb |
-- Also ensure the dropped types can be safely eta-reduced. Otherwise,
|
|
Packit |
9a2dfb |
-- throw an error.
|
|
Packit |
9a2dfb |
unless (canEtaReduce remainingTysExpSubst' droppedTysExpSubst) $
|
|
Packit |
9a2dfb |
etaReductionError instanceType
|
|
Packit |
9a2dfb |
return (instanceCxt, instanceType)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Attempt to derive a constraint on a Type. If successful, return
|
|
Packit |
9a2dfb |
-- Just the constraint and any kind variable names constrained to *.
|
|
Packit |
9a2dfb |
-- Otherwise, return Nothing and the empty list.
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- See Note [Type inference in derived instances] for the heuristics used to
|
|
Packit |
9a2dfb |
-- come up with constraints.
|
|
Packit |
9a2dfb |
deriveConstraint :: JSONClass -> Type -> (Maybe Pred, [Name])
|
|
Packit |
9a2dfb |
deriveConstraint jc t
|
|
Packit |
9a2dfb |
| not (isTyVar t) = (Nothing, [])
|
|
Packit |
9a2dfb |
| hasKindStar t = (Just (applyCon (jcConstraint Arity0) tName), [])
|
|
Packit |
9a2dfb |
| otherwise = case hasKindVarChain 1 t of
|
|
Packit |
9a2dfb |
Just ns | jcArity >= Arity1
|
|
Packit |
9a2dfb |
-> (Just (applyCon (jcConstraint Arity1) tName), ns)
|
|
Packit |
9a2dfb |
_ -> case hasKindVarChain 2 t of
|
|
Packit |
9a2dfb |
Just ns | jcArity == Arity2
|
|
Packit |
9a2dfb |
-> (Just (applyCon (jcConstraint Arity2) tName), ns)
|
|
Packit |
9a2dfb |
_ -> (Nothing, [])
|
|
Packit |
9a2dfb |
where
|
|
Packit |
9a2dfb |
tName :: Name
|
|
Packit |
9a2dfb |
tName = varTToName t
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
jcArity :: Arity
|
|
Packit |
9a2dfb |
jcArity = arity jc
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
jcConstraint :: Arity -> Name
|
|
Packit |
9a2dfb |
jcConstraint = jsonClassName . JSONClass (direction jc)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
{-
|
|
Packit |
9a2dfb |
Note [Kind signatures in derived instances]
|
|
Packit |
9a2dfb |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
It is possible to put explicit kind signatures into the derived instances, e.g.,
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
instance C a => C (Data (f :: * -> *)) where ...
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
But it is preferable to avoid this if possible. If we come up with an incorrect
|
|
Packit |
9a2dfb |
kind signature (which is entirely possible, since Template Haskell doesn't always
|
|
Packit |
9a2dfb |
have the best track record with reifying kind signatures), then GHC will flat-out
|
|
Packit |
9a2dfb |
reject the instance, which is quite unfortunate.
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
Plain old datatypes have the advantage that you can avoid using any kind signatures
|
|
Packit |
9a2dfb |
at all in their instances. This is because a datatype declaration uses all type
|
|
Packit |
9a2dfb |
variables, so the types that we use in a derived instance uniquely determine their
|
|
Packit |
9a2dfb |
kinds. As long as we plug in the right types, the kind inferencer can do the rest
|
|
Packit |
9a2dfb |
of the work. For this reason, we use unSigT to remove all kind signatures before
|
|
Packit |
9a2dfb |
splicing in the instance context and head.
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
Data family instances are trickier, since a data family can have two instances that
|
|
Packit |
9a2dfb |
are distinguished by kind alone, e.g.,
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
data family Fam (a :: k)
|
|
Packit |
9a2dfb |
data instance Fam (a :: * -> *)
|
|
Packit |
9a2dfb |
data instance Fam (a :: *)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
If we dropped the kind signatures for C (Fam a), then GHC will have no way of
|
|
Packit |
9a2dfb |
knowing which instance we are talking about. To avoid this scenario, we always
|
|
Packit |
9a2dfb |
include explicit kind signatures in data family instances. There is a chance that
|
|
Packit |
9a2dfb |
the inferred kind signatures will be incorrect, but if so, we can always fall back
|
|
Packit |
9a2dfb |
on the mk- functions.
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
Note [Type inference in derived instances]
|
|
Packit |
9a2dfb |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
Type inference is can be tricky to get right, and we want to avoid recreating the
|
|
Packit |
9a2dfb |
entirety of GHC's type inferencer in Template Haskell. For this reason, we will
|
|
Packit |
9a2dfb |
probably never come up with derived instance contexts that are as accurate as
|
|
Packit |
9a2dfb |
GHC's. But that doesn't mean we can't do anything! There are a couple of simple
|
|
Packit |
9a2dfb |
things we can do to make instance contexts that work for 80% of use cases:
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
1. If one of the last type parameters is polykinded, then its kind will be
|
|
Packit |
9a2dfb |
specialized to * in the derived instance. We note what kind variable the type
|
|
Packit |
9a2dfb |
parameter had and substitute it with * in the other types as well. For example,
|
|
Packit |
9a2dfb |
imagine you had
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
data Data (a :: k) (b :: k)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
Then you'd want to derived instance to be:
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
instance C (Data (a :: *))
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
Not:
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
instance C (Data (a :: k))
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
2. We naïvely come up with instance constraints using the following criteria:
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
(i) If there's a type parameter n of kind *, generate a ToJSON n/FromJSON n
|
|
Packit |
9a2dfb |
constraint.
|
|
Packit |
9a2dfb |
(ii) If there's a type parameter n of kind k1 -> k2 (where k1/k2 are * or kind
|
|
Packit |
9a2dfb |
variables), then generate a ToJSON1 n/FromJSON1 n constraint, and if
|
|
Packit |
9a2dfb |
k1/k2 are kind variables, then substitute k1/k2 with * elsewhere in the
|
|
Packit |
9a2dfb |
types. We must consider the case where they are kind variables because
|
|
Packit |
9a2dfb |
you might have a scenario like this:
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
newtype Compose (f :: k2 -> *) (g :: k1 -> k2) (a :: k1)
|
|
Packit |
9a2dfb |
= Compose (f (g a))
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
Which would have a derived ToJSON1 instance of:
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
instance (ToJSON1 f, ToJSON1 g) => ToJSON1 (Compose f g) where ...
|
|
Packit |
9a2dfb |
(iii) If there's a type parameter n of kind k1 -> k2 -> k3 (where k1/k2/k3 are
|
|
Packit |
9a2dfb |
* or kind variables), then generate a ToJSON2 n/FromJSON2 n constraint
|
|
Packit |
9a2dfb |
and perform kind substitution as in the other cases.
|
|
Packit |
9a2dfb |
-}
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
checkExistentialContext :: JSONClass -> TyVarMap -> Cxt -> Name
|
|
Packit |
9a2dfb |
-> Q a -> Q a
|
|
Packit |
9a2dfb |
checkExistentialContext jc tvMap ctxt conName q =
|
|
Packit |
9a2dfb |
if (any (`predMentionsName` M.keys tvMap) ctxt
|
|
Packit |
9a2dfb |
|| M.size tvMap < arityInt jc)
|
|
Packit |
9a2dfb |
&& not (allowExQuant jc)
|
|
Packit |
9a2dfb |
then existentialContextError conName
|
|
Packit |
9a2dfb |
else q
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
{-
|
|
Packit |
9a2dfb |
Note [Matching functions with GADT type variables]
|
|
Packit |
9a2dfb |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
When deriving ToJSON2, there is a tricky corner case to consider:
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
data Both a b where
|
|
Packit |
9a2dfb |
BothCon :: x -> x -> Both x x
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
Which encoding functions should be applied to which arguments of BothCon?
|
|
Packit |
9a2dfb |
We have a choice, since both the function of type (a -> Value) and of type
|
|
Packit |
9a2dfb |
(b -> Value) can be applied to either argument. In such a scenario, the
|
|
Packit |
9a2dfb |
second encoding function takes precedence over the first encoding function, so the
|
|
Packit |
9a2dfb |
derived ToJSON2 instance would be something like:
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
instance ToJSON2 Both where
|
|
Packit |
9a2dfb |
liftToJSON2 tj1 tj2 p (BothCon x1 x2) = Array $ create $ do
|
|
Packit |
9a2dfb |
mv <- unsafeNew 2
|
|
Packit |
9a2dfb |
unsafeWrite mv 0 (tj1 x1)
|
|
Packit |
9a2dfb |
unsafeWrite mv 1 (tj2 x2)
|
|
Packit |
9a2dfb |
return mv
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
This is not an arbitrary choice, as this definition ensures that
|
|
Packit |
9a2dfb |
liftToJSON2 toJSON = liftToJSON for a derived ToJSON1 instance for
|
|
Packit |
9a2dfb |
Both.
|
|
Packit |
9a2dfb |
-}
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- A mapping of type variable Names to their encoding/decoding function Names.
|
|
Packit |
9a2dfb |
-- For example, in a ToJSON2 declaration, a TyVarMap might look like
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- { a ~> (tj1, tjl1)
|
|
Packit |
9a2dfb |
-- , b ~> (tj2, tjl2) }
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- where a and b are the last two type variables of the datatype, tj1 and tjl1 are
|
|
Packit |
9a2dfb |
-- the function arguments of types (a -> Value) and ([a] -> Value), and tj2 and tjl2
|
|
Packit |
9a2dfb |
-- are the function arguments of types (b -> Value) and ([b] -> Value).
|
|
Packit |
9a2dfb |
type TyVarMap = Map Name (Name, Name)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Returns True if a Type has kind *.
|
|
Packit |
9a2dfb |
hasKindStar :: Type -> Bool
|
|
Packit |
9a2dfb |
hasKindStar VarT{} = True
|
|
Packit |
9a2dfb |
#if MIN_VERSION_template_haskell(2,8,0)
|
|
Packit |
9a2dfb |
hasKindStar (SigT _ StarT) = True
|
|
Packit |
9a2dfb |
#else
|
|
Packit |
9a2dfb |
hasKindStar (SigT _ StarK) = True
|
|
Packit |
9a2dfb |
#endif
|
|
Packit |
9a2dfb |
hasKindStar _ = False
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- Returns True is a kind is equal to *, or if it is a kind variable.
|
|
Packit |
9a2dfb |
isStarOrVar :: Kind -> Bool
|
|
Packit |
9a2dfb |
#if MIN_VERSION_template_haskell(2,8,0)
|
|
Packit |
9a2dfb |
isStarOrVar StarT = True
|
|
Packit |
9a2dfb |
isStarOrVar VarT{} = True
|
|
Packit |
9a2dfb |
#else
|
|
Packit |
9a2dfb |
isStarOrVar StarK = True
|
|
Packit |
9a2dfb |
#endif
|
|
Packit |
9a2dfb |
isStarOrVar _ = False
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- Generate a list of fresh names with a common prefix, and numbered suffixes.
|
|
Packit |
9a2dfb |
newNameList :: String -> Int -> Q [Name]
|
|
Packit |
9a2dfb |
newNameList prefix len = mapM newName [prefix ++ show n | n <- [1..len]]
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | @hasKindVarChain n kind@ Checks if @kind@ is of the form
|
|
Packit |
9a2dfb |
-- k_0 -> k_1 -> ... -> k_(n-1), where k0, k1, ..., and k_(n-1) can be * or
|
|
Packit |
9a2dfb |
-- kind variables.
|
|
Packit |
9a2dfb |
hasKindVarChain :: Int -> Type -> Maybe [Name]
|
|
Packit |
9a2dfb |
hasKindVarChain kindArrows t =
|
|
Packit |
9a2dfb |
let uk = uncurryKind (tyKind t)
|
|
Packit |
9a2dfb |
in if (NE.length uk - 1 == kindArrows) && F.all isStarOrVar uk
|
|
Packit |
9a2dfb |
then Just (concatMap freeVariables uk)
|
|
Packit |
9a2dfb |
else Nothing
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | If a Type is a SigT, returns its kind signature. Otherwise, return *.
|
|
Packit |
9a2dfb |
tyKind :: Type -> Kind
|
|
Packit |
9a2dfb |
tyKind (SigT _ k) = k
|
|
Packit |
9a2dfb |
tyKind _ = starK
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Extract Just the Name from a type variable. If the argument Type is not a
|
|
Packit |
9a2dfb |
-- type variable, return Nothing.
|
|
Packit |
9a2dfb |
varTToNameMaybe :: Type -> Maybe Name
|
|
Packit |
9a2dfb |
varTToNameMaybe (VarT n) = Just n
|
|
Packit |
9a2dfb |
varTToNameMaybe (SigT t _) = varTToNameMaybe t
|
|
Packit |
9a2dfb |
varTToNameMaybe _ = Nothing
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Extract the Name from a type variable. If the argument Type is not a
|
|
Packit |
9a2dfb |
-- type variable, throw an error.
|
|
Packit |
9a2dfb |
varTToName :: Type -> Name
|
|
Packit |
9a2dfb |
varTToName = fromMaybe (error "Not a type variable!") . varTToNameMaybe
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
interleave :: [a] -> [a] -> [a]
|
|
Packit |
9a2dfb |
interleave (a1:a1s) (a2:a2s) = a1:a2:interleave a1s a2s
|
|
Packit |
9a2dfb |
interleave _ _ = []
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Fully applies a type constructor to its type variables.
|
|
Packit |
9a2dfb |
applyTyCon :: Name -> [Type] -> Type
|
|
Packit |
9a2dfb |
applyTyCon = foldl' AppT . ConT
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Is the given type a variable?
|
|
Packit |
9a2dfb |
isTyVar :: Type -> Bool
|
|
Packit |
9a2dfb |
isTyVar (VarT _) = True
|
|
Packit |
9a2dfb |
isTyVar (SigT t _) = isTyVar t
|
|
Packit |
9a2dfb |
isTyVar _ = False
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Is the given type a type family constructor (and not a data family constructor)?
|
|
Packit |
9a2dfb |
isTyFamily :: Type -> Q Bool
|
|
Packit |
9a2dfb |
isTyFamily (ConT n) = do
|
|
Packit |
9a2dfb |
info <- reify n
|
|
Packit |
9a2dfb |
return $ case info of
|
|
Packit |
9a2dfb |
#if MIN_VERSION_template_haskell(2,11,0)
|
|
Packit |
9a2dfb |
FamilyI OpenTypeFamilyD{} _ -> True
|
|
Packit |
9a2dfb |
#else
|
|
Packit |
9a2dfb |
FamilyI (FamilyD TypeFam _ _ _) _ -> True
|
|
Packit |
9a2dfb |
#endif
|
|
Packit |
9a2dfb |
#if MIN_VERSION_template_haskell(2,9,0)
|
|
Packit |
9a2dfb |
FamilyI ClosedTypeFamilyD{} _ -> True
|
|
Packit |
9a2dfb |
#endif
|
|
Packit |
9a2dfb |
_ -> False
|
|
Packit |
9a2dfb |
isTyFamily _ = return False
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Peel off a kind signature from a Type (if it has one).
|
|
Packit |
9a2dfb |
unSigT :: Type -> Type
|
|
Packit |
9a2dfb |
unSigT (SigT t _) = t
|
|
Packit |
9a2dfb |
unSigT t = t
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Are all of the items in a list (which have an ordering) distinct?
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- This uses Set (as opposed to nub) for better asymptotic time complexity.
|
|
Packit |
9a2dfb |
allDistinct :: Ord a => [a] -> Bool
|
|
Packit |
9a2dfb |
allDistinct = allDistinct' Set.empty
|
|
Packit |
9a2dfb |
where
|
|
Packit |
9a2dfb |
allDistinct' :: Ord a => Set a -> [a] -> Bool
|
|
Packit |
9a2dfb |
allDistinct' uniqs (x:xs)
|
|
Packit |
9a2dfb |
| x `Set.member` uniqs = False
|
|
Packit |
9a2dfb |
| otherwise = allDistinct' (Set.insert x uniqs) xs
|
|
Packit |
9a2dfb |
allDistinct' _ _ = True
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Does the given type mention any of the Names in the list?
|
|
Packit |
9a2dfb |
mentionsName :: Type -> [Name] -> Bool
|
|
Packit |
9a2dfb |
mentionsName = go
|
|
Packit |
9a2dfb |
where
|
|
Packit |
9a2dfb |
go :: Type -> [Name] -> Bool
|
|
Packit |
9a2dfb |
go (AppT t1 t2) names = go t1 names || go t2 names
|
|
Packit |
9a2dfb |
go (SigT t _k) names = go t names
|
|
Packit |
9a2dfb |
#if MIN_VERSION_template_haskell(2,8,0)
|
|
Packit |
9a2dfb |
|| go _k names
|
|
Packit |
9a2dfb |
#endif
|
|
Packit |
9a2dfb |
go (VarT n) names = n `elem` names
|
|
Packit |
9a2dfb |
go _ _ = False
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Does an instance predicate mention any of the Names in the list?
|
|
Packit |
9a2dfb |
predMentionsName :: Pred -> [Name] -> Bool
|
|
Packit |
9a2dfb |
#if MIN_VERSION_template_haskell(2,10,0)
|
|
Packit |
9a2dfb |
predMentionsName = mentionsName
|
|
Packit |
9a2dfb |
#else
|
|
Packit |
9a2dfb |
predMentionsName (ClassP n tys) names = n `elem` names || any (`mentionsName` names) tys
|
|
Packit |
9a2dfb |
predMentionsName (EqualP t1 t2) names = mentionsName t1 names || mentionsName t2 names
|
|
Packit |
9a2dfb |
#endif
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Split an applied type into its individual components. For example, this:
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- @
|
|
Packit |
9a2dfb |
-- Either Int Char
|
|
Packit |
9a2dfb |
-- @
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- would split to this:
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- @
|
|
Packit |
9a2dfb |
-- [Either, Int, Char]
|
|
Packit |
9a2dfb |
-- @
|
|
Packit |
9a2dfb |
unapplyTy :: Type -> NonEmpty Type
|
|
Packit |
9a2dfb |
unapplyTy = NE.reverse . go
|
|
Packit |
9a2dfb |
where
|
|
Packit |
9a2dfb |
go :: Type -> NonEmpty Type
|
|
Packit |
9a2dfb |
go (AppT t1 t2) = t2 <| go t1
|
|
Packit |
9a2dfb |
go (SigT t _) = go t
|
|
Packit |
9a2dfb |
go (ForallT _ _ t) = go t
|
|
Packit |
9a2dfb |
go t = t :| []
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Split a type signature by the arrows on its spine. For example, this:
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- @
|
|
Packit |
9a2dfb |
-- forall a b. (a ~ b) => (a -> b) -> Char -> ()
|
|
Packit |
9a2dfb |
-- @
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- would split to this:
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- @
|
|
Packit |
9a2dfb |
-- (a ~ b, [a -> b, Char, ()])
|
|
Packit |
9a2dfb |
-- @
|
|
Packit |
9a2dfb |
uncurryTy :: Type -> (Cxt, NonEmpty Type)
|
|
Packit |
9a2dfb |
uncurryTy (AppT (AppT ArrowT t1) t2) =
|
|
Packit |
9a2dfb |
let (ctxt, tys) = uncurryTy t2
|
|
Packit |
9a2dfb |
in (ctxt, t1 <| tys)
|
|
Packit |
9a2dfb |
uncurryTy (SigT t _) = uncurryTy t
|
|
Packit |
9a2dfb |
uncurryTy (ForallT _ ctxt t) =
|
|
Packit |
9a2dfb |
let (ctxt', tys) = uncurryTy t
|
|
Packit |
9a2dfb |
in (ctxt ++ ctxt', tys)
|
|
Packit |
9a2dfb |
uncurryTy t = ([], t :| [])
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Like uncurryType, except on a kind level.
|
|
Packit |
9a2dfb |
uncurryKind :: Kind -> NonEmpty Kind
|
|
Packit |
9a2dfb |
#if MIN_VERSION_template_haskell(2,8,0)
|
|
Packit |
9a2dfb |
uncurryKind = snd . uncurryTy
|
|
Packit |
9a2dfb |
#else
|
|
Packit |
9a2dfb |
uncurryKind (ArrowK k1 k2) = k1 <| uncurryKind k2
|
|
Packit |
9a2dfb |
uncurryKind k = k :| []
|
|
Packit |
9a2dfb |
#endif
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
createKindChain :: Int -> Kind
|
|
Packit |
9a2dfb |
createKindChain = go starK
|
|
Packit |
9a2dfb |
where
|
|
Packit |
9a2dfb |
go :: Kind -> Int -> Kind
|
|
Packit |
9a2dfb |
go k 0 = k
|
|
Packit |
9a2dfb |
#if MIN_VERSION_template_haskell(2,8,0)
|
|
Packit |
9a2dfb |
go k !n = go (AppT (AppT ArrowT StarT) k) (n - 1)
|
|
Packit |
9a2dfb |
#else
|
|
Packit |
9a2dfb |
go k !n = go (ArrowK StarK k) (n - 1)
|
|
Packit |
9a2dfb |
#endif
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Makes a string literal expression from a constructor's name.
|
|
Packit |
9a2dfb |
conNameExp :: Options -> ConstructorInfo -> Q Exp
|
|
Packit |
9a2dfb |
conNameExp opts = litE
|
|
Packit |
9a2dfb |
. stringL
|
|
Packit |
9a2dfb |
. constructorTagModifier opts
|
|
Packit |
9a2dfb |
. nameBase
|
|
Packit |
9a2dfb |
. constructorName
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Extracts a record field label.
|
|
Packit |
9a2dfb |
fieldLabel :: Options -- ^ Encoding options
|
|
Packit |
9a2dfb |
-> Name
|
|
Packit |
9a2dfb |
-> String
|
|
Packit |
9a2dfb |
fieldLabel opts = fieldLabelModifier opts . nameBase
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | The name of the outermost 'Value' constructor.
|
|
Packit |
9a2dfb |
valueConName :: Value -> String
|
|
Packit |
9a2dfb |
valueConName (Object _) = "Object"
|
|
Packit |
9a2dfb |
valueConName (Array _) = "Array"
|
|
Packit |
9a2dfb |
valueConName (String _) = "String"
|
|
Packit |
9a2dfb |
valueConName (Number _) = "Number"
|
|
Packit |
9a2dfb |
valueConName (Bool _) = "Boolean"
|
|
Packit |
9a2dfb |
valueConName Null = "Null"
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
applyCon :: Name -> Name -> Pred
|
|
Packit |
9a2dfb |
applyCon con t =
|
|
Packit |
9a2dfb |
#if MIN_VERSION_template_haskell(2,10,0)
|
|
Packit |
9a2dfb |
AppT (ConT con) (VarT t)
|
|
Packit |
9a2dfb |
#else
|
|
Packit |
9a2dfb |
ClassP con [VarT t]
|
|
Packit |
9a2dfb |
#endif
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Checks to see if the last types in a data family instance can be safely eta-
|
|
Packit |
9a2dfb |
-- reduced (i.e., dropped), given the other types. This checks for three conditions:
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- (1) All of the dropped types are type variables
|
|
Packit |
9a2dfb |
-- (2) All of the dropped types are distinct
|
|
Packit |
9a2dfb |
-- (3) None of the remaining types mention any of the dropped types
|
|
Packit |
9a2dfb |
canEtaReduce :: [Type] -> [Type] -> Bool
|
|
Packit |
9a2dfb |
canEtaReduce remaining dropped =
|
|
Packit |
9a2dfb |
all isTyVar dropped
|
|
Packit |
9a2dfb |
&& allDistinct droppedNames -- Make sure not to pass something of type [Type], since Type
|
|
Packit |
9a2dfb |
-- didn't have an Ord instance until template-haskell-2.10.0.0
|
|
Packit |
9a2dfb |
&& not (any (`mentionsName` droppedNames) remaining)
|
|
Packit |
9a2dfb |
where
|
|
Packit |
9a2dfb |
droppedNames :: [Name]
|
|
Packit |
9a2dfb |
droppedNames = map varTToName dropped
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-------------------------------------------------------------------------------
|
|
Packit |
9a2dfb |
-- Expanding type synonyms
|
|
Packit |
9a2dfb |
-------------------------------------------------------------------------------
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
applySubstitutionKind :: Map Name Kind -> Type -> Type
|
|
Packit |
9a2dfb |
#if MIN_VERSION_template_haskell(2,8,0)
|
|
Packit |
9a2dfb |
applySubstitutionKind = applySubstitution
|
|
Packit |
9a2dfb |
#else
|
|
Packit |
9a2dfb |
applySubstitutionKind _ t = t
|
|
Packit |
9a2dfb |
#endif
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
substNameWithKind :: Name -> Kind -> Type -> Type
|
|
Packit |
9a2dfb |
substNameWithKind n k = applySubstitutionKind (M.singleton n k)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
substNamesWithKindStar :: [Name] -> Type -> Type
|
|
Packit |
9a2dfb |
substNamesWithKindStar ns t = foldr' (`substNameWithKind` starK) t ns
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-------------------------------------------------------------------------------
|
|
Packit |
9a2dfb |
-- Error messages
|
|
Packit |
9a2dfb |
-------------------------------------------------------------------------------
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Either the given data type doesn't have enough type variables, or one of
|
|
Packit |
9a2dfb |
-- the type variables to be eta-reduced cannot realize kind *.
|
|
Packit |
9a2dfb |
derivingKindError :: JSONClass -> Name -> Q a
|
|
Packit |
9a2dfb |
derivingKindError jc tyConName = fail
|
|
Packit |
9a2dfb |
. showString "Cannot derive well-kinded instance of form ‘"
|
|
Packit |
9a2dfb |
. showString className
|
|
Packit |
9a2dfb |
. showChar ' '
|
|
Packit |
9a2dfb |
. showParen True
|
|
Packit |
9a2dfb |
( showString (nameBase tyConName)
|
|
Packit |
9a2dfb |
. showString " ..."
|
|
Packit |
9a2dfb |
)
|
|
Packit |
9a2dfb |
. showString "‘\n\tClass "
|
|
Packit |
9a2dfb |
. showString className
|
|
Packit |
9a2dfb |
. showString " expects an argument of kind "
|
|
Packit |
9a2dfb |
. showString (pprint . createKindChain $ arityInt jc)
|
|
Packit |
9a2dfb |
$ ""
|
|
Packit |
9a2dfb |
where
|
|
Packit |
9a2dfb |
className :: String
|
|
Packit |
9a2dfb |
className = nameBase $ jsonClassName jc
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | One of the last type variables cannot be eta-reduced (see the canEtaReduce
|
|
Packit |
9a2dfb |
-- function for the criteria it would have to meet).
|
|
Packit |
9a2dfb |
etaReductionError :: Type -> Q a
|
|
Packit |
9a2dfb |
etaReductionError instanceType = fail $
|
|
Packit |
9a2dfb |
"Cannot eta-reduce to an instance of form \n\tinstance (...) => "
|
|
Packit |
9a2dfb |
++ pprint instanceType
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | The data type has a DatatypeContext which mentions one of the eta-reduced
|
|
Packit |
9a2dfb |
-- type variables.
|
|
Packit |
9a2dfb |
datatypeContextError :: Name -> Type -> Q a
|
|
Packit |
9a2dfb |
datatypeContextError dataName instanceType = fail
|
|
Packit |
9a2dfb |
. showString "Can't make a derived instance of ‘"
|
|
Packit |
9a2dfb |
. showString (pprint instanceType)
|
|
Packit |
9a2dfb |
. showString "‘:\n\tData type ‘"
|
|
Packit |
9a2dfb |
. showString (nameBase dataName)
|
|
Packit |
9a2dfb |
. showString "‘ must not have a class context involving the last type argument(s)"
|
|
Packit |
9a2dfb |
$ ""
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | The data type mentions one of the n eta-reduced type variables in a place other
|
|
Packit |
9a2dfb |
-- than the last nth positions of a data type in a constructor's field.
|
|
Packit |
9a2dfb |
outOfPlaceTyVarError :: JSONClass -> Name -> a
|
|
Packit |
9a2dfb |
outOfPlaceTyVarError jc conName = error
|
|
Packit |
9a2dfb |
. showString "Constructor ‘"
|
|
Packit |
9a2dfb |
. showString (nameBase conName)
|
|
Packit |
9a2dfb |
. showString "‘ must only use its last "
|
|
Packit |
9a2dfb |
. shows n
|
|
Packit |
9a2dfb |
. showString " type variable(s) within the last "
|
|
Packit |
9a2dfb |
. shows n
|
|
Packit |
9a2dfb |
. showString " argument(s) of a data type"
|
|
Packit |
9a2dfb |
$ ""
|
|
Packit |
9a2dfb |
where
|
|
Packit |
9a2dfb |
n :: Int
|
|
Packit |
9a2dfb |
n = arityInt jc
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | The data type has an existential constraint which mentions one of the
|
|
Packit |
9a2dfb |
-- eta-reduced type variables.
|
|
Packit |
9a2dfb |
existentialContextError :: Name -> a
|
|
Packit |
9a2dfb |
existentialContextError conName = error
|
|
Packit |
9a2dfb |
. showString "Constructor ‘"
|
|
Packit |
9a2dfb |
. showString (nameBase conName)
|
|
Packit |
9a2dfb |
. showString "‘ must be truly polymorphic in the last argument(s) of the data type"
|
|
Packit |
9a2dfb |
$ ""
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-------------------------------------------------------------------------------
|
|
Packit |
9a2dfb |
-- Class-specific constants
|
|
Packit |
9a2dfb |
-------------------------------------------------------------------------------
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | A representation of the arity of the ToJSON/FromJSON typeclass being derived.
|
|
Packit |
9a2dfb |
data Arity = Arity0 | Arity1 | Arity2
|
|
Packit |
9a2dfb |
deriving (Enum, Eq, Ord)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Whether ToJSON(1)(2) or FromJSON(1)(2) is being derived.
|
|
Packit |
9a2dfb |
data Direction = To | From
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | A representation of which typeclass method is being spliced in.
|
|
Packit |
9a2dfb |
data JSONFun = ToJSON | ToEncoding | ParseJSON
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | A refinement of JSONFun to [ToJSON, ToEncoding].
|
|
Packit |
9a2dfb |
data ToJSONFun = Value | Encoding
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
targetToJSONFun :: ToJSONFun -> JSONFun
|
|
Packit |
9a2dfb |
targetToJSONFun Value = ToJSON
|
|
Packit |
9a2dfb |
targetToJSONFun Encoding = ToEncoding
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | A representation of which typeclass is being derived.
|
|
Packit |
9a2dfb |
data JSONClass = JSONClass { direction :: Direction, arity :: Arity }
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
toJSONClass, toJSON1Class, toJSON2Class,
|
|
Packit |
9a2dfb |
fromJSONClass, fromJSON1Class, fromJSON2Class :: JSONClass
|
|
Packit |
9a2dfb |
toJSONClass = JSONClass To Arity0
|
|
Packit |
9a2dfb |
toJSON1Class = JSONClass To Arity1
|
|
Packit |
9a2dfb |
toJSON2Class = JSONClass To Arity2
|
|
Packit |
9a2dfb |
fromJSONClass = JSONClass From Arity0
|
|
Packit |
9a2dfb |
fromJSON1Class = JSONClass From Arity1
|
|
Packit |
9a2dfb |
fromJSON2Class = JSONClass From Arity2
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
jsonClassName :: JSONClass -> Name
|
|
Packit |
9a2dfb |
jsonClassName (JSONClass To Arity0) = ''ToJSON
|
|
Packit |
9a2dfb |
jsonClassName (JSONClass To Arity1) = ''ToJSON1
|
|
Packit |
9a2dfb |
jsonClassName (JSONClass To Arity2) = ''ToJSON2
|
|
Packit |
9a2dfb |
jsonClassName (JSONClass From Arity0) = ''FromJSON
|
|
Packit |
9a2dfb |
jsonClassName (JSONClass From Arity1) = ''FromJSON1
|
|
Packit |
9a2dfb |
jsonClassName (JSONClass From Arity2) = ''FromJSON2
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
jsonFunValName :: JSONFun -> Arity -> Name
|
|
Packit |
9a2dfb |
jsonFunValName ToJSON Arity0 = 'toJSON
|
|
Packit |
9a2dfb |
jsonFunValName ToJSON Arity1 = 'liftToJSON
|
|
Packit |
9a2dfb |
jsonFunValName ToJSON Arity2 = 'liftToJSON2
|
|
Packit |
9a2dfb |
jsonFunValName ToEncoding Arity0 = 'toEncoding
|
|
Packit |
9a2dfb |
jsonFunValName ToEncoding Arity1 = 'liftToEncoding
|
|
Packit |
9a2dfb |
jsonFunValName ToEncoding Arity2 = 'liftToEncoding2
|
|
Packit |
9a2dfb |
jsonFunValName ParseJSON Arity0 = 'parseJSON
|
|
Packit |
9a2dfb |
jsonFunValName ParseJSON Arity1 = 'liftParseJSON
|
|
Packit |
9a2dfb |
jsonFunValName ParseJSON Arity2 = 'liftParseJSON2
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
jsonFunListName :: JSONFun -> Arity -> Name
|
|
Packit |
9a2dfb |
jsonFunListName ToJSON Arity0 = 'toJSONList
|
|
Packit |
9a2dfb |
jsonFunListName ToJSON Arity1 = 'liftToJSONList
|
|
Packit |
9a2dfb |
jsonFunListName ToJSON Arity2 = 'liftToJSONList2
|
|
Packit |
9a2dfb |
jsonFunListName ToEncoding Arity0 = 'toEncodingList
|
|
Packit |
9a2dfb |
jsonFunListName ToEncoding Arity1 = 'liftToEncodingList
|
|
Packit |
9a2dfb |
jsonFunListName ToEncoding Arity2 = 'liftToEncodingList2
|
|
Packit |
9a2dfb |
jsonFunListName ParseJSON Arity0 = 'parseJSONList
|
|
Packit |
9a2dfb |
jsonFunListName ParseJSON Arity1 = 'liftParseJSONList
|
|
Packit |
9a2dfb |
jsonFunListName ParseJSON Arity2 = 'liftParseJSONList2
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
jsonFunValOrListName :: Bool -- e.g., toJSONList if True, toJSON if False
|
|
Packit |
9a2dfb |
-> JSONFun -> Arity -> Name
|
|
Packit |
9a2dfb |
jsonFunValOrListName False = jsonFunValName
|
|
Packit |
9a2dfb |
jsonFunValOrListName True = jsonFunListName
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
arityInt :: JSONClass -> Int
|
|
Packit |
9a2dfb |
arityInt = fromEnum . arity
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
allowExQuant :: JSONClass -> Bool
|
|
Packit |
9a2dfb |
allowExQuant (JSONClass To _) = True
|
|
Packit |
9a2dfb |
allowExQuant _ = False
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-------------------------------------------------------------------------------
|
|
Packit |
9a2dfb |
-- StarKindStatus
|
|
Packit |
9a2dfb |
-------------------------------------------------------------------------------
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Whether a type is not of kind *, is of kind *, or is a kind variable.
|
|
Packit |
9a2dfb |
data StarKindStatus = NotKindStar
|
|
Packit |
9a2dfb |
| KindStar
|
|
Packit |
9a2dfb |
| IsKindVar Name
|
|
Packit |
9a2dfb |
deriving Eq
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Does a Type have kind * or k (for some kind variable k)?
|
|
Packit |
9a2dfb |
canRealizeKindStar :: Type -> StarKindStatus
|
|
Packit |
9a2dfb |
canRealizeKindStar t = case t of
|
|
Packit |
9a2dfb |
_ | hasKindStar t -> KindStar
|
|
Packit |
9a2dfb |
#if MIN_VERSION_template_haskell(2,8,0)
|
|
Packit |
9a2dfb |
SigT _ (VarT k) -> IsKindVar k
|
|
Packit |
9a2dfb |
#endif
|
|
Packit |
9a2dfb |
_ -> NotKindStar
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Returns 'Just' the kind variable 'Name' of a 'StarKindStatus' if it exists.
|
|
Packit |
9a2dfb |
-- Otherwise, returns 'Nothing'.
|
|
Packit |
9a2dfb |
starKindStatusToName :: StarKindStatus -> Maybe Name
|
|
Packit |
9a2dfb |
starKindStatusToName (IsKindVar n) = Just n
|
|
Packit |
9a2dfb |
starKindStatusToName _ = Nothing
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Concat together all of the StarKindStatuses that are IsKindVar and extract
|
|
Packit |
9a2dfb |
-- the kind variables' Names out.
|
|
Packit |
9a2dfb |
catKindVarNames :: [StarKindStatus] -> [Name]
|
|
Packit |
9a2dfb |
catKindVarNames = mapMaybe starKindStatusToName
|