Move AST to AST.Document

This commit is contained in:
Eugen Wissner 2019-12-28 07:07:58 +01:00
parent 78ee76f9d5
commit fdf5914626
7 changed files with 232 additions and 188 deletions

View File

@ -11,9 +11,11 @@ and this project adheres to
- AST for the GraphQL schema. - AST for the GraphQL schema.
### Changed ### Changed
- Rename `AST.Definition` into `AST.ExecutableDefinition`. - Rename `AST.Definition` into `AST.Document.ExecutableDefinition`.
`AST.TypeSystemDefinition` and `AST.TypeSystemExtension` can also be `AST.Document.TypeSystemDefinition` and `AST.Document.TypeSystemExtension`
definitions. can also be definitions.
- Move all AST data to `AST.Document` and reexport them.
- Rename `AST.OperationSelectionSet` to `AST.Document.SelectionSet`.
### Removed ### Removed
- `AST.Field`, `AST.InlineFragment` and `AST.FragmentSpread`. - `AST.Field`, `AST.InlineFragment` and `AST.FragmentSpread`.

View File

@ -1,157 +1,6 @@
-- | Target AST for Parser. -- | Target AST for Parser.
module Language.GraphQL.AST module Language.GraphQL.AST
( Alias ( module Language.GraphQL.AST.Document
, Argument(..)
, ExecutableDefinition(..)
, Directive(..)
, FragmentDefinition(..)
, Name
, NonNullType(..)
, ObjectField(..)
, OperationDefinition(..)
, OperationType(..)
, Selection(..)
, SelectionSet
, SelectionSetOpt
, Type(..)
, TypeCondition
, Value(..)
, VariableDefinition(..)
) where ) where
import Data.Int (Int32) import Language.GraphQL.AST.Document
import Data.List.NonEmpty (NonEmpty)
import Data.Text (Text)
-- | Name
type Name = Text
-- | Directive.
data Directive = Directive Name [Argument] deriving (Eq, Show)
-- * Operations
-- | Top-level definition of a document, either an operation or a fragment.
data ExecutableDefinition
= DefinitionOperation OperationDefinition
| DefinitionFragment FragmentDefinition
deriving (Eq, Show)
-- | Operation definition.
data OperationDefinition
= OperationSelectionSet SelectionSet
| OperationDefinition OperationType
(Maybe Name)
[VariableDefinition]
[Directive]
SelectionSet
deriving (Eq, Show)
-- | GraphQL has 3 operation types: queries, mutations and subscribtions.
--
-- Currently only queries and mutations are supported.
data OperationType = Query | Mutation deriving (Eq, Show)
-- * Selections
-- | "Top-level" selection, selection on an operation or fragment.
type SelectionSet = NonEmpty Selection
-- | Field selection.
type SelectionSetOpt = [Selection]
-- | Single GraphQL field.
--
-- The only required property of a field is its name. Optionally it can also
-- have an alias, arguments or a list of subfields.
--
-- Given the following query:
--
-- @
-- {
-- zuck: user(id: 4) {
-- id
-- name
-- }
-- }
-- @
--
-- * "user", "id" and "name" are field names.
-- * "user" has two subfields, "id" and "name".
-- * "zuck" is an alias for "user". "id" and "name" have no aliases.
-- * "id: 4" is an argument for "user". "id" and "name" don't have any
-- arguments.
data Selection
= Field (Maybe Alias) Name [Argument] [Directive] SelectionSetOpt
| FragmentSpread Name [Directive]
| InlineFragment (Maybe TypeCondition) [Directive] SelectionSet
deriving (Eq, Show)
-- | Alternative field name.
--
-- @
-- {
-- smallPic: profilePic(size: 64)
-- bigPic: profilePic(size: 1024)
-- }
-- @
--
-- Here "smallPic" and "bigPic" are aliases for the same field, "profilePic",
-- used to distinquish between profile pictures with different arguments
-- (sizes).
type Alias = Name
-- | Single argument.
--
-- @
-- {
-- user(id: 4) {
-- name
-- }
-- }
-- @
--
-- Here "id" is an argument for the field "user" and its value is 4.
data Argument = Argument Name Value deriving (Eq,Show)
-- | Fragment definition.
data FragmentDefinition
= FragmentDefinition Name TypeCondition [Directive] SelectionSet
deriving (Eq, Show)
-- * Inputs
-- | Input value.
data Value = Variable Name
| Int Int32
| Float Double
| String Text
| Boolean Bool
| Null
| Enum Name
| List [Value]
| Object [ObjectField]
deriving (Eq, Show)
-- | Key-value pair.
--
-- A list of 'ObjectField's represents a GraphQL object type.
data ObjectField = ObjectField Name Value deriving (Eq, Show)
-- | Variable definition.
data VariableDefinition = VariableDefinition Name Type (Maybe Value)
deriving (Eq, Show)
-- | Type condition.
type TypeCondition = Name
-- | Type representation.
data Type = TypeNamed Name
| TypeList Type
| TypeNonNull NonNullType
deriving (Eq, Show)
-- | Helper type to represent Non-Null types and lists of such types.
data NonNullType = NonNullTypeNamed Name
| NonNullTypeList Type
deriving (Eq, Show)

View File

@ -1,34 +1,46 @@
-- | This module defines an abstract syntax tree for the @GraphQL@ language. It -- | This module defines an abstract syntax tree for the @GraphQL@ language. It
-- follows closely the structure given in the specification. Please refer to -- follows closely the structure given in the specification. Please refer to
-- <https://facebook.github.io/graphql/ Facebook's GraphQL Specification>. -- <https://facebook.github.io/graphql/ Facebook's GraphQL Specification>.
-- for more information. -- for more information.
module Language.GraphQL.AST.Document module Language.GraphQL.AST.Document
( Definition(..) ( Alias
, Argument(..)
, Definition(ExecutableDefinition)
, Directive(..)
, Document , Document
, ExecutableDefinition(..) , ExecutableDefinition(..)
, FragmentDefinition(..)
, Name
, NonNullType(..)
, ObjectField(..)
, OperationDefinition(..)
, OperationType(..)
, Selection(..)
, SelectionSet
, SelectionSetOpt
, Type(..)
, TypeCondition
, Value(..)
, VariableDefinition(..)
) where ) where
import Data.Int (Int32)
import Data.List.NonEmpty (NonEmpty) import Data.List.NonEmpty (NonEmpty)
import Data.Text (Text) import Data.Text (Text)
import Language.GraphQL.AST
( ExecutableDefinition(..)
, Directive
, Name
, OperationType
, Type
, Value
)
import Language.GraphQL.AST.DirectiveLocation import Language.GraphQL.AST.DirectiveLocation
-- * Language -- * Language
-- ** Source Text
-- | Name.
type Name = Text
-- ** Document -- ** Document
-- | GraphQL document. -- | GraphQL document.
type Document = NonEmpty Definition type Document = NonEmpty Definition
type NamedType = Name
-- | All kinds of definitions that can occur in a GraphQL document. -- | All kinds of definitions that can occur in a GraphQL document.
data Definition data Definition
= ExecutableDefinition ExecutableDefinition = ExecutableDefinition ExecutableDefinition
@ -36,12 +48,190 @@ data Definition
| TypeSystemExtension TypeSystemExtension | TypeSystemExtension TypeSystemExtension
deriving (Eq, Show) deriving (Eq, Show)
-- | Top-level definition of a document, either an operation or a fragment.
data ExecutableDefinition
= DefinitionOperation OperationDefinition
| DefinitionFragment FragmentDefinition
deriving (Eq, Show)
-- ** Operations
-- | Operation definition.
data OperationDefinition
= SelectionSet SelectionSet
| OperationDefinition
OperationType
(Maybe Name)
[VariableDefinition]
[Directive]
SelectionSet
deriving (Eq, Show)
-- | GraphQL has 3 operation types:
--
-- * query - a read-only fetch.
-- * mutation - a write operation followed by a fetch.
-- * subscription - a long-lived request that fetches data in response to
-- source events.
--
-- Currently only queries and mutations are supported.
data OperationType = Query | Mutation deriving (Eq, Show)
-- ** Selection Sets
-- | "Top-level" selection, selection on an operation or fragment.
type SelectionSet = NonEmpty Selection
-- | Field selection.
type SelectionSetOpt = [Selection]
-- | Selection is a single entry in a selection set. It can be a single field,
-- fragment spread or inline fragment.
--
-- The only required property of a field is its name. Optionally it can also
-- have an alias, arguments, directives and a list of subfields.
--
-- In the following query "user" is a field with two subfields, "id" and "name":
--
-- @
-- {
-- user {
-- id
-- name
-- }
-- }
-- @
--
-- A fragment spread refers to a fragment defined outside the operation and is
-- expanded at the execution time.
--
-- @
-- {
-- user {
-- ...userFragment
-- }
-- }
--
-- fragment userFragment on UserType {
-- id
-- name
-- }
-- @
--
-- Inline fragments are similar but they don't have any name and the type
-- condition ("on UserType") is optional.
--
-- @
-- {
-- user {
-- ... on UserType {
-- id
-- name
-- }
-- }
-- @
data Selection
= Field (Maybe Alias) Name [Argument] [Directive] SelectionSetOpt
| FragmentSpread Name [Directive]
| InlineFragment (Maybe TypeCondition) [Directive] SelectionSet
deriving (Eq, Show)
-- ** Arguments
-- | Single argument.
--
-- @
-- {
-- user(id: 4) {
-- name
-- }
-- }
-- @
--
-- Here "id" is an argument for the field "user" and its value is 4.
data Argument = Argument Name Value deriving (Eq,Show)
-- ** Field Alias
-- | Alternative field name.
--
-- @
-- {
-- smallPic: profilePic(size: 64)
-- bigPic: profilePic(size: 1024)
-- }
-- @
--
-- Here "smallPic" and "bigPic" are aliases for the same field, "profilePic",
-- used to distinquish between profile pictures with different arguments
-- (sizes).
type Alias = Name
-- ** Fragments
-- | Fragment definition.
data FragmentDefinition
= FragmentDefinition Name TypeCondition [Directive] SelectionSet
deriving (Eq, Show)
-- | Type condition.
type TypeCondition = Name
-- ** Input Values
-- | Input value.
data Value
= Variable Name
| Int Int32
| Float Double
| String Text
| Boolean Bool
| Null
| Enum Name
| List [Value]
| Object [ObjectField]
deriving (Eq, Show)
-- | Key-value pair.
--
-- A list of 'ObjectField's represents a GraphQL object type.
data ObjectField = ObjectField Name Value deriving (Eq, Show)
-- ** Variables
-- | Variable definition.
data VariableDefinition = VariableDefinition Name Type (Maybe Value)
deriving (Eq, Show)
-- ** Type References
-- | Type representation.
data Type
= TypeNamed Name
| TypeList Type
| TypeNonNull NonNullType
deriving (Eq, Show)
type NamedType = Name
-- | Helper type to represent Non-Null types and lists of such types.
data NonNullType
= NonNullTypeNamed Name
| NonNullTypeList Type
deriving (Eq, Show)
-- ** Directives
-- | Directive.
data Directive = Directive Name [Argument] deriving (Eq, Show)
-- * Type System -- * Type System
data TypeSystemDefinition data TypeSystemDefinition
= SchemaDefinition [Directive] RootOperationTypeDefinitions = SchemaDefinition [Directive] RootOperationTypeDefinitions
| TypeDefinition TypeDefinition | TypeDefinition TypeDefinition
| DirectiveDefinition Description Name ArgumentsDefinition DirectiveLocation | DirectiveDefinition
Description Name ArgumentsDefinition DirectiveLocation
deriving (Eq, Show) deriving (Eq, Show)
-- ** Type System Extensions -- ** Type System Extensions
@ -109,7 +299,8 @@ newtype ImplementsInterfaces = ImplementsInterfaces (NonEmpty NamedType)
newtype ImplementsInterfacesOpt = ImplementsInterfacesOpt [NamedType] newtype ImplementsInterfacesOpt = ImplementsInterfacesOpt [NamedType]
deriving (Eq, Show) deriving (Eq, Show)
data FieldDefinition = FieldDefinition Description Name ArgumentsDefinition Type data FieldDefinition
= FieldDefinition Description Name ArgumentsDefinition Type
deriving (Eq, Show) deriving (Eq, Show)
newtype ArgumentsDefinition = ArgumentsDefinition [InputValueDefinition] newtype ArgumentsDefinition = ArgumentsDefinition [InputValueDefinition]

View File

@ -66,7 +66,7 @@ definition formatter x
= fragmentDefinition formatter fragment = fragmentDefinition formatter fragment
operationDefinition :: Formatter -> Full.OperationDefinition -> Lazy.Text operationDefinition :: Formatter -> Full.OperationDefinition -> Lazy.Text
operationDefinition formatter (Full.OperationSelectionSet sels) operationDefinition formatter (Full.SelectionSet sels)
= selectionSet formatter sels = selectionSet formatter sels
operationDefinition formatter (Full.OperationDefinition Full.Query name vars dirs sels) operationDefinition formatter (Full.OperationDefinition Full.Query name vars dirs sels)
= "query " <> node formatter name vars dirs sels = "query " <> node formatter name vars dirs sels

View File

@ -25,13 +25,16 @@ definition = DefinitionOperation <$> operationDefinition
<?> "definition error!" <?> "definition error!"
operationDefinition :: Parser OperationDefinition operationDefinition :: Parser OperationDefinition
operationDefinition = OperationSelectionSet <$> selectionSet operationDefinition = SelectionSet <$> selectionSet
<|> OperationDefinition <$> operationType <|> operationDefinition'
<*> optional name <?> "operationDefinition error"
<*> opt variableDefinitions where
<*> opt directives operationDefinition'
<*> selectionSet = OperationDefinition <$> operationType
<?> "operationDefinition error" <*> optional name
<*> opt variableDefinitions
<*> opt directives
<*> selectionSet
operationType :: Parser OperationType operationType :: Parser OperationType
operationType = Query <$ symbol "query" operationType = Query <$ symbol "query"

View File

@ -58,13 +58,13 @@ operations operations' = do
lift . lift $ NonEmpty.nonEmpty coreOperations lift . lift $ NonEmpty.nonEmpty coreOperations
operation :: Full.OperationDefinition -> TransformT Core.Operation operation :: Full.OperationDefinition -> TransformT Core.Operation
operation (Full.OperationSelectionSet sels) = operation (Full.SelectionSet sels)
operation $ Full.OperationDefinition Full.Query mempty mempty mempty sels = operation $ Full.OperationDefinition Full.Query mempty mempty mempty sels
-- TODO: Validate Variable definitions with substituter -- TODO: Validate Variable definitions with substituter
operation (Full.OperationDefinition Full.Query name _vars _dirs sels) = operation (Full.OperationDefinition Full.Query name _vars _dirs sels)
Core.Query name <$> appendSelection sels = Core.Query name <$> appendSelection sels
operation (Full.OperationDefinition Full.Mutation name _vars _dirs sels) = operation (Full.OperationDefinition Full.Mutation name _vars _dirs sels)
Core.Mutation name <$> appendSelection sels = Core.Mutation name <$> appendSelection sels
-- * Selection -- * Selection

View File

@ -35,8 +35,7 @@ spec = do
it "indents block strings in arguments" $ it "indents block strings in arguments" $
let arguments = [Argument "message" (String "line1\nline2")] let arguments = [Argument "message" (String "line1\nline2")]
field = Field Nothing "field" arguments [] [] field = Field Nothing "field" arguments [] []
set = OperationSelectionSet $ pure field operation = DefinitionOperation $ SelectionSet $ pure field
operation = DefinitionOperation set
in definition pretty operation `shouldBe` [r|{ in definition pretty operation `shouldBe` [r|{
field(message: """ field(message: """
line1 line1