import type { Maybe } from '../jsutils/Maybe' ; import type { GraphQLError } from '../error/GraphQLError' ; import { syntaxError } from '../error/syntaxError' ; import type { ArgumentNode , BooleanValueNode , ConstArgumentNode , ConstDirectiveNode , ConstListValueNode , ConstObjectFieldNode , ConstObjectValueNode , ConstValueNode , DefinitionNode , DirectiveDefinitionNode , DirectiveNode , DocumentNode , EnumTypeDefinitionNode , EnumTypeExtensionNode , EnumValueDefinitionNode , EnumValueNode , FieldDefinitionNode , FieldNode , FloatValueNode , FragmentDefinitionNode , FragmentSpreadNode , InlineFragmentNode , InputObjectTypeDefinitionNode , InputObjectTypeExtensionNode , InputValueDefinitionNode , InterfaceTypeDefinitionNode , InterfaceTypeExtensionNode , IntValueNode , ListTypeNode , ListValueNode , NamedTypeNode , NameNode , NonNullTypeNode , NullValueNode , ObjectFieldNode , ObjectTypeDefinitionNode , ObjectTypeExtensionNode , ObjectValueNode , OperationDefinitionNode , OperationTypeDefinitionNode , ScalarTypeDefinitionNode , ScalarTypeExtensionNode , SchemaDefinitionNode , SchemaExtensionNode , SelectionNode , SelectionSetNode , StringValueNode , Token , TypeNode , TypeSystemExtensionNode , UnionTypeDefinitionNode , UnionTypeExtensionNode , ValueNode , VariableDefinitionNode , VariableNode , } from './ast' ; import { Location , OperationTypeNode } from './ast' ; import { DirectiveLocation } from './directiveLocation' ; import { Kind } from './kinds' ; import { isPunctuatorTokenKind , Lexer } from './lexer' ; import { isSource , Source } from './source' ; import { TokenKind } from './tokenKind' ; /**
 * Configuration options to control parser behavior
 */ export interface ParseOptions { /**
     * By default, the parser creates AST nodes that know the location
     * in the source that they correspond to. This configuration flag
     * disables that behavior for performance or testing.
     */ noLocation ? : boolean ; deprecated allowLegacyFragmentVariables ? : boolean ; } /**
 * Given a GraphQL source, parses it into a Document.
 * Throws GraphQLError if a syntax error is encountered.
 */ export function parse ( source , options ? ) { const parser = new Parser ( source , options ) ; return parser . parseDocument ( ) ; } /**
 * Given a string containing a GraphQL value (ex. `[42]`), parse the AST for
 * that value.
 * Throws GraphQLError if a syntax error is encountered.
 *
 * This is useful within tools that operate upon GraphQL Values directly and
 * in isolation of complete GraphQL documents.
 *
 * Consider providing the results to the utility function: valueFromAST().
 */ export function parseValue ( source , options ? ) { const parser = new Parser ( source , options ) ; parser . expectToken ( TokenKind . SOF ) ; const value = parser . parseValueLiteral ( false ) ; parser . expectToken ( TokenKind . EOF ) ; return value ; } /**
 * Similar to parseValue(), but raises a parse error if it encounters a
 * variable. The return type will be a constant value.
 */ export function parseConstValue ( source , options ? ) { const parser = new Parser ( source , options ) ; parser . expectToken ( TokenKind . SOF ) ; const value = parser . parseConstValueLiteral ( ) ; parser . expectToken ( TokenKind . EOF ) ; return value ; } /**
 * Given a string containing a GraphQL Type (ex. `[Int!]`), parse the AST for
 * that type.
 * Throws GraphQLError if a syntax error is encountered.
 *
 * This is useful within tools that operate upon GraphQL Types directly and
 * in isolation of complete GraphQL documents.
 *
 * Consider providing the results to the utility function: typeFromAST().
 */ export function parseType ( source , options ? ) { const parser = new Parser ( source , options ) ; parser . expectToken ( TokenKind . SOF ) ; const type = parser . parseTypeReference ( ) ; parser . expectToken ( TokenKind . EOF ) ; return type ; } internal export class Parser { protected _options : Maybe < ParseOptions > ; protected _lexer : Lexer ; constructor ( source , options ? ) { const sourceObj = isSource ( source ) ? source : new Source ( source ) ; this . _lexer = new Lexer ( sourceObj ) ; this . _options = options ; } /**
     * Converts a name lex token into a name parse node.
     */ parseName ( ) { const token = this . expectToken ( TokenKind . NAME ) ; return this . node < NameNode > ( token , { kind : Kind . NAME , value : token . value , } ) ; } /**
     * Document : Definition+
     */ parseDocument ( ) { return this . node < DocumentNode > ( this . _lexer . token , { kind : Kind . DOCUMENT , definitions : this . many ( TokenKind . SOF , this . parseDefinition , TokenKind . EOF ) , } ) ; } /**
     * Definition :
     *   - ExecutableDefinition
     *   - TypeSystemDefinition
     *   - TypeSystemExtension
     *
     * ExecutableDefinition :
     *   - OperationDefinition
     *   - FragmentDefinition
     *
     * TypeSystemDefinition :
     *   - SchemaDefinition
     *   - TypeDefinition
     *   - DirectiveDefinition
     *
     * TypeDefinition :
     *   - ScalarTypeDefinition
     *   - ObjectTypeDefinition
     *   - InterfaceTypeDefinition
     *   - UnionTypeDefinition
     *   - EnumTypeDefinition
     *   - InputObjectTypeDefinition
     */ parseDefinition ( ) { if ( this . peek ( TokenKind . BRACE_L ) ) { return this . parseOperationDefinition ( ) ; } const hasDescription = this . peekDescription ( ) ; const keywordToken = hasDescription ? this . _lexer . lookahead ( ) : this . _lexer . token ; if ( keywordToken . kind === TokenKind . NAME ) { switch ( keywordToken . value ) { case 'schema' : return this . parseSchemaDefinition ( ) ; case 'scalar' : return this . parseScalarTypeDefinition ( ) ; case 'type' : return this . parseObjectTypeDefinition ( ) ; case 'interface' : return this . parseInterfaceTypeDefinition ( ) ; case 'union' : return this . parseUnionTypeDefinition ( ) ; case 'enum' : return this . parseEnumTypeDefinition ( ) ; case 'input' : return this . parseInputObjectTypeDefinition ( ) ; case 'directive' : return this . parseDirectiveDefinition ( ) ; } if ( hasDescription ) { throw syntaxError ( this . _lexer . source , this . _lexer . token . start , 'Unexpected description, descriptions are supported only on type definitions.' ) ; } switch ( keywordToken . value ) { case 'query' : case 'mutation' : case 'subscription' : return this . parseOperationDefinition ( ) ; case 'fragment' : return this . parseFragmentDefinition ( ) ; case 'extend' : return this . parseTypeSystemExtension ( ) ; } } throw this . unexpected ( keywordToken ) ; } /**
     * OperationDefinition :
     *  - SelectionSet
     *  - OperationType Name? VariableDefinitions? Directives? SelectionSet
     */ parseOperationDefinition ( ) { const start = this . _lexer . token ; if ( this . peek ( TokenKind . BRACE_L ) ) { return this . node < OperationDefinitionNode > ( start , { kind : Kind . OPERATION_DEFINITION , operation : OperationTypeNode . QUERY , name : undefined , variableDefinitions : [ ] , directives : [ ] , selectionSet : this . parseSelectionSet ( ) , } ) ; } const operation = this . parseOperationType ( ) ; let name ; if ( this . peek ( TokenKind . NAME ) ) { name = this . parseName ( ) ; } return this . node < OperationDefinitionNode > ( start , { kind : Kind . OPERATION_DEFINITION , operation , name , variableDefinitions : this . parseVariableDefinitions ( ) , directives : this . parseDirectives ( false ) , selectionSet : this . parseSelectionSet ( ) , } ) ; } /**
     * OperationType : one of query mutation subscription
     */ parseOperationType ( ) { const operationToken = this . expectToken ( TokenKind . NAME ) ; switch ( operationToken . value ) { case 'query' : return OperationTypeNode . QUERY ; case 'mutation' : return OperationTypeNode . MUTATION ; case 'subscription' : return OperationTypeNode . SUBSCRIPTION ; } throw this . unexpected ( operationToken ) ; } /**
     * VariableDefinitions : ( VariableDefinition+ )
     */ parseVariableDefinitions ( ) { return this . optionalMany ( TokenKind . PAREN_L , this . parseVariableDefinition , TokenKind . PAREN_R ) ; } /**
     * VariableDefinition : Variable : Type DefaultValue? Directives[Const]?
     */ parseVariableDefinition ( ) { return this . node < VariableDefinitionNode > ( this . _lexer . token , { kind : Kind . VARIABLE_DEFINITION , variable : this . parseVariable ( ) , type : ( this . expectToken ( TokenKind . COLON ) , this . parseTypeReference ( ) ) , defaultValue : this . expectOptionalToken ( TokenKind . EQUALS ) ? this . parseConstValueLiteral ( ) : undefined , directives : this . parseConstDirectives ( ) , } ) ; } /**
     * Variable : $ Name
     */ parseVariable ( ) { const start = this . _lexer . token ; this . expectToken ( TokenKind . DOLLAR ) ; return this . node < VariableNode > ( start , { kind : Kind . VARIABLE , name : this . parseName ( ) , } ) ; } /**
     * ```
     * SelectionSet : { Selection+ }
     * ```
     */ parseSelectionSet ( ) { return this . node < SelectionSetNode > ( this . _lexer . token , { kind : Kind . SELECTION_SET , selections : this . many ( TokenKind . BRACE_L , this . parseSelection , TokenKind . BRACE_R ) , } ) ; } /**
     * Selection :
     *   - Field
     *   - FragmentSpread
     *   - InlineFragment
     */ parseSelection ( ) { return this . peek ( TokenKind . SPREAD ) ? this . parseFragment ( ) : this . parseField ( ) ; } /**
     * Field : Alias? Name Arguments? Directives? SelectionSet?
     *
     * Alias : Name :
     */ parseField ( ) { const start = this . _lexer . token ; const nameOrAlias = this . parseName ( ) ; let alias ; let name ; if ( this . expectOptionalToken ( TokenKind . COLON ) ) { alias = nameOrAlias ; name = this . parseName ( ) ; } else { name = nameOrAlias ; } return this . node < FieldNode > ( start , { kind : Kind . FIELD , alias , name , arguments : this . parseArguments ( false ) , directives : this . parseDirectives ( false ) , selectionSet : this . peek ( TokenKind . BRACE_L ) ? this . parseSelectionSet ( ) : undefined , } ) ; } /**
     * Arguments[Const] : ( Argument[?Const]+ )
     */ parseArguments ( isConst ) ; parseArguments ( isConst ) ; parseArguments ( isConst ) { const item = isConst ? this . parseConstArgument : this . parseArgument ; return this . optionalMany ( TokenKind . PAREN_L , item , TokenKind . PAREN_R ) ; } /**
     * Argument[Const] : Name : Value[?Const]
     */ parseArgument ( isConst ) ; parseArgument ( isConst ? ) ; parseArgument ( isConst = false ) { const start = this . _lexer . token ; const name = this . parseName ( ) ; this . expectToken ( TokenKind . COLON ) ; return this . node < ArgumentNode > ( start , { kind : Kind . ARGUMENT , name , value : this . parseValueLiteral ( isConst ) , } ) ; } parseConstArgument ( ) { return this . parseArgument ( true ) ; } /**
     * Corresponds to both FragmentSpread and InlineFragment in the spec.
     *
     * FragmentSpread : ... FragmentName Directives?
     *
     * InlineFragment : ... TypeCondition? Directives? SelectionSet
     */ parseFragment ( ) { const start = this . _lexer . token ; this . expectToken ( TokenKind . SPREAD ) ; const hasTypeCondition = this . expectOptionalKeyword ( 'on' ) ; if ( ! hasTypeCondition && this . peek ( TokenKind . NAME ) ) { return this . node < FragmentSpreadNode > ( start , { kind : Kind . FRAGMENT_SPREAD , name : this . parseFragmentName ( ) , directives : this . parseDirectives ( false ) , } ) ; } return this . node < InlineFragmentNode > ( start , { kind : Kind . INLINE_FRAGMENT , typeCondition : hasTypeCondition ? this . parseNamedType ( ) : undefined , directives : this . parseDirectives ( false ) , selectionSet : this . parseSelectionSet ( ) , } ) ; } /**
     * FragmentDefinition :
     *   - fragment FragmentName on TypeCondition Directives? SelectionSet
     *
     * TypeCondition : NamedType
     */ parseFragmentDefinition ( ) { const start = this . _lexer . token ; this . expectKeyword ( 'fragment' ) ; if ( this . _options ?. allowLegacyFragmentVariables === true ) { return this . node < FragmentDefinitionNode > ( start , { kind : Kind . FRAGMENT_DEFINITION , name : this . parseFragmentName ( ) , variableDefinitions : this . parseVariableDefinitions ( ) , typeCondition : ( this . expectKeyword ( 'on' ) , this . parseNamedType ( ) ) , directives : this . parseDirectives ( false ) , selectionSet : this . parseSelectionSet ( ) , } ) ; } return this . node < FragmentDefinitionNode > ( start , { kind : Kind . FRAGMENT_DEFINITION , name : this . parseFragmentName ( ) , typeCondition : ( this . expectKeyword ( 'on' ) , this . parseNamedType ( ) ) , directives : this . parseDirectives ( false ) , selectionSet : this . parseSelectionSet ( ) , } ) ; } /**
     * FragmentName : Name but not `on`
     */ parseFragmentName ( ) { if ( this . _lexer . token . value === 'on' ) { throw this . unexpected ( ) ; } return this . parseName ( ) ; } /**
     * Value[Const] :
     *   - [~Const] Variable
     *   - IntValue
     *   - FloatValue
     *   - StringValue
     *   - BooleanValue
     *   - NullValue
     *   - EnumValue
     *   - ListValue[?Const]
     *   - ObjectValue[?Const]
     *
     * BooleanValue : one of `true` `false`
     *
     * NullValue : `null`
     *
     * EnumValue : Name but not `true`, `false` or `null`
     */ parseValueLiteral ( isConst ) ; parseValueLiteral ( isConst ) ; parseValueLiteral ( isConst ) { const token = this . _lexer . token ; switch ( token . kind ) { case TokenKind . BRACKET_L : return this . parseList ( isConst ) ; case TokenKind . BRACE_L : return this . parseObject ( isConst ) ; case TokenKind . INT : this . _lexer . advance ( ) ; return this . node < IntValueNode > ( token , { kind : Kind . INT , value : token . value , } ) ; case TokenKind . FLOAT : this . _lexer . advance ( ) ; return this . node < FloatValueNode > ( token , { kind : Kind . FLOAT , value : token . value , } ) ; case TokenKind . STRING : case TokenKind . BLOCK_STRING : return this . parseStringLiteral ( ) ; case TokenKind . NAME : this . _lexer . advance ( ) ; switch ( token . value ) { case 'true' : return this . node < BooleanValueNode > ( token , { kind : Kind . BOOLEAN , value : true , } ) ; case 'false' : return this . node < BooleanValueNode > ( token , { kind : Kind . BOOLEAN , value : false , } ) ; case 'null' : return this . node < NullValueNode > ( token , { kind : Kind . NULL } ) ; default : return this . node < EnumValueNode > ( token , { kind : Kind . ENUM , value : token . value , } ) ; } case TokenKind . DOLLAR : if ( isConst ) { this . expectToken ( TokenKind . DOLLAR ) ; if ( this . _lexer . token . kind === TokenKind . NAME ) { const varName = this . _lexer . token . value ; throw syntaxError ( this . _lexer . source , token . start , `Unexpected variable "$${ varName }" in constant value.` ) ; } else { throw this . unexpected ( token ) ; } } return this . parseVariable ( ) ; default : throw this . unexpected ( ) ; } } parseConstValueLiteral ( ) { return this . parseValueLiteral ( true ) ; } parseStringLiteral ( ) { const token = this . _lexer . token ; this . _lexer . advance ( ) ; return this . node < StringValueNode > ( token , { kind : Kind . STRING , value : token . value , block : token . kind === TokenKind . BLOCK_STRING , } ) ; } /**
     * ListValue[Const] :
     *   - [ ]
     *   - [ Value[?Const]+ ]
     */ parseList ( isConst ) ; parseList ( isConst ) ; parseList ( isConst ) { const item = ( ) => this . parseValueLiteral ( isConst ) ; return this . node < ListValueNode > ( this . _lexer . token , { kind : Kind . LIST , values : this . any ( TokenKind . BRACKET_L , item , TokenKind . BRACKET_R ) , } ) ; } /**
     * ```
     * ObjectValue[Const] :
     *   - { }
     *   - { ObjectField[?Const]+ }
     * ```
     */ parseObject ( isConst ) ; parseObject ( isConst ) ; parseObject ( isConst ) { const item = ( ) => this . parseObjectField ( isConst ) ; return this . node < ObjectValueNode > ( this . _lexer . token , { kind : Kind . OBJECT , fields : this . any ( TokenKind . BRACE_L , item , TokenKind . BRACE_R ) , } ) ; } /**
     * ObjectField[Const] : Name : Value[?Const]
     */ parseObjectField ( isConst ) ; parseObjectField ( isConst ) ; parseObjectField ( isConst ) { const start = this . _lexer . token ; const name = this . parseName ( ) ; this . expectToken ( TokenKind . COLON ) ; return this . node < ObjectFieldNode > ( start , { kind : Kind . OBJECT_FIELD , name , value : this . parseValueLiteral ( isConst ) , } ) ; } /**
     * Directives[Const] : Directive[?Const]+
     */ parseDirectives ( isConst ) ; parseDirectives ( isConst ) ; parseDirectives ( isConst ) { const directives = [ ] ; while ( this . peek ( TokenKind . AT ) ) { directives . push ( this . parseDirective ( isConst ) ) ; } return directives ; } parseConstDirectives ( ) { return this . parseDirectives ( true ) ; } /**
     * ```
     * Directive[Const] : @ Name Arguments[?Const]?
     * ```
     */ parseDirective ( isConst ) ; parseDirective ( isConst ) ; parseDirective ( isConst ) { const start = this . _lexer . token ; this . expectToken ( TokenKind . AT ) ; return this . node < DirectiveNode > ( start , { kind : Kind . DIRECTIVE , name : this . parseName ( ) , arguments : this . parseArguments ( isConst ) , } ) ; } /**
     * Type :
     *   - NamedType
     *   - ListType
     *   - NonNullType
     */ parseTypeReference ( ) { const start = this . _lexer . token ; let type ; if ( this . expectOptionalToken ( TokenKind . BRACKET_L ) ) { const innerType = this . parseTypeReference ( ) ; this . expectToken ( TokenKind . BRACKET_R ) ; type = this . node < ListTypeNode > ( start , { kind : Kind . LIST_TYPE , type : innerType , } ) ; } else { type = this . parseNamedType ( ) ; } if ( this . expectOptionalToken ( TokenKind . BANG ) ) { return this . node < NonNullTypeNode > ( start , { kind : Kind . NON_NULL_TYPE , type , } ) ; } return type ; } /**
     * NamedType : Name
     */ parseNamedType ( ) { return this . node < NamedTypeNode > ( this . _lexer . token , { kind : Kind . NAMED_TYPE , name : this . parseName ( ) , } ) ; } peekDescription ( ) { return this . peek ( TokenKind . STRING ) || this . peek ( TokenKind . BLOCK_STRING ) ; } /**
     * Description : StringValue
     */ parseDescription ( ) { if ( this . peekDescription ( ) ) { return this . parseStringLiteral ( ) ; } } /**
     * ```
     * SchemaDefinition : Description? schema Directives[Const]? { OperationTypeDefinition+ }
     * ```
     */ parseSchemaDefinition ( ) { const start = this . _lexer . token ; const description = this . parseDescription ( ) ; this . expectKeyword ( 'schema' ) ; const directives = this . parseConstDirectives ( ) ; const operationTypes = this . many ( TokenKind . BRACE_L , this . parseOperationTypeDefinition , TokenKind . BRACE_R ) ; return this . node < SchemaDefinitionNode > ( start , { kind : Kind . SCHEMA_DEFINITION , description , directives , operationTypes , } ) ; } /**
     * OperationTypeDefinition : OperationType : NamedType
     */ parseOperationTypeDefinition ( ) { const start = this . _lexer . token ; const operation = this . parseOperationType ( ) ; this . expectToken ( TokenKind . COLON ) ; const type = this . parseNamedType ( ) ; return this . node < OperationTypeDefinitionNode > ( start , { kind : Kind . OPERATION_TYPE_DEFINITION , operation , type , } ) ; } /**
     * ScalarTypeDefinition : Description? scalar Name Directives[Const]?
     */ parseScalarTypeDefinition ( ) { const start = this . _lexer . token ; const description = this . parseDescription ( ) ; this . expectKeyword ( 'scalar' ) ; const name = this . parseName ( ) ; const directives = this . parseConstDirectives ( ) ; return this . node < ScalarTypeDefinitionNode > ( start , { kind : Kind . SCALAR_TYPE_DEFINITION , description , name , directives , } ) ; } /**
     * ObjectTypeDefinition :
     *   Description?
     *   type Name ImplementsInterfaces? Directives[Const]? FieldsDefinition?
     */ parseObjectTypeDefinition ( ) { const start = this . _lexer . token ; const description = this . parseDescription ( ) ; this . expectKeyword ( 'type' ) ; const name = this . parseName ( ) ; const interfaces = this . parseImplementsInterfaces ( ) ; const directives = this . parseConstDirectives ( ) ; const fields = this . parseFieldsDefinition ( ) ; return this . node < ObjectTypeDefinitionNode > ( start , { kind : Kind . OBJECT_TYPE_DEFINITION , description , name , interfaces , directives , fields , } ) ; } /**
     * ImplementsInterfaces :
     *   - implements `&`? NamedType
     *   - ImplementsInterfaces & NamedType
     */ parseImplementsInterfaces ( ) { return this . expectOptionalKeyword ( 'implements' ) ? this . delimitedMany ( TokenKind . AMP , this . parseNamedType ) : [ ] ; } /**
     * ```
     * FieldsDefinition : { FieldDefinition+ }
     * ```
     */ parseFieldsDefinition ( ) { return this . optionalMany ( TokenKind . BRACE_L , this . parseFieldDefinition , TokenKind . BRACE_R ) ; } /**
     * FieldDefinition :
     *   - Description? Name ArgumentsDefinition? : Type Directives[Const]?
     */ parseFieldDefinition ( ) { const start = this . _lexer . token ; const description = this . parseDescription ( ) ; const name = this . parseName ( ) ; const args = this . parseArgumentDefs ( ) ; this . expectToken ( TokenKind . COLON ) ; const type = this . parseTypeReference ( ) ; const directives = this . parseConstDirectives ( ) ; return this . node < FieldDefinitionNode > ( start , { kind : Kind . FIELD_DEFINITION , description , name , arguments : args , type , directives , } ) ; } /**
     * ArgumentsDefinition : ( InputValueDefinition+ )
     */ parseArgumentDefs ( ) { return this . optionalMany ( TokenKind . PAREN_L , this . parseInputValueDef , TokenKind . PAREN_R ) ; } /**
     * InputValueDefinition :
     *   - Description? Name : Type DefaultValue? Directives[Const]?
     */ parseInputValueDef ( ) { const start = this . _lexer . token ; const description = this . parseDescription ( ) ; const name = this . parseName ( ) ; this . expectToken ( TokenKind . COLON ) ; const type = this . parseTypeReference ( ) ; let defaultValue ; if ( this . expectOptionalToken ( TokenKind . EQUALS ) ) { defaultValue = this . parseConstValueLiteral ( ) ; } const directives = this . parseConstDirectives ( ) ; return this . node < InputValueDefinitionNode > ( start , { kind : Kind . INPUT_VALUE_DEFINITION , description , name , type , defaultValue , directives , } ) ; } /**
     * InterfaceTypeDefinition :
     *   - Description? interface Name Directives[Const]? FieldsDefinition?
     */ parseInterfaceTypeDefinition ( ) { const start = this . _lexer . token ; const description = this . parseDescription ( ) ; this . expectKeyword ( 'interface' ) ; const name = this . parseName ( ) ; const interfaces = this . parseImplementsInterfaces ( ) ; const directives = this . parseConstDirectives ( ) ; const fields = this . parseFieldsDefinition ( ) ; return this . node < InterfaceTypeDefinitionNode > ( start , { kind : Kind . INTERFACE_TYPE_DEFINITION , description , name , interfaces , directives , fields , } ) ; } /**
     * UnionTypeDefinition :
     *   - Description? union Name Directives[Const]? UnionMemberTypes?
     */ parseUnionTypeDefinition ( ) { const start = this . _lexer . token ; const description = this . parseDescription ( ) ; this . expectKeyword ( 'union' ) ; const name = this . parseName ( ) ; const directives = this . parseConstDirectives ( ) ; const types = this . parseUnionMemberTypes ( ) ; return this . node < UnionTypeDefinitionNode > ( start , { kind : Kind . UNION_TYPE_DEFINITION , description , name , directives , types , } ) ; } /**
     * UnionMemberTypes :
     *   - = `|`? NamedType
     *   - UnionMemberTypes | NamedType
     */ parseUnionMemberTypes ( ) { return this . expectOptionalToken ( TokenKind . EQUALS ) ? this . delimitedMany ( TokenKind . PIPE , this . parseNamedType ) : [ ] ; } /**
     * EnumTypeDefinition :
     *   - Description? enum Name Directives[Const]? EnumValuesDefinition?
     */ parseEnumTypeDefinition ( ) { const start = this . _lexer . token ; const description = this . parseDescription ( ) ; this . expectKeyword ( 'enum' ) ; const name = this . parseName ( ) ; const directives = this . parseConstDirectives ( ) ; const values = this . parseEnumValuesDefinition ( ) ; return this . node < EnumTypeDefinitionNode > ( start , { kind : Kind . ENUM_TYPE_DEFINITION , description , name , directives , values , } ) ; } /**
     * ```
     * EnumValuesDefinition : { EnumValueDefinition+ }
     * ```
     */ parseEnumValuesDefinition ( ) { return this . optionalMany ( TokenKind . BRACE_L , this . parseEnumValueDefinition , TokenKind . BRACE_R ) ; } /**
     * EnumValueDefinition : Description? EnumValue Directives[Const]?
     */ parseEnumValueDefinition ( ) { const start = this . _lexer . token ; const description = this . parseDescription ( ) ; const name = this . parseEnumValueName ( ) ; const directives = this . parseConstDirectives ( ) ; return this . node < EnumValueDefinitionNode > ( start , { kind : Kind . ENUM_VALUE_DEFINITION , description , name , directives , } ) ; } /**
     * EnumValue : Name but not `true`, `false` or `null`
     */ parseEnumValueName ( ) { if ( this . _lexer . token . value === 'true' || this . _lexer . token . value === 'false' || this . _lexer . token . value === 'null' ) { throw syntaxError ( this . _lexer . source , this . _lexer . token . start , `${ getTokenDesc ( this . _lexer . token ) } is reserved and cannot be used for an enum value.` ) ; } return this . parseName ( ) ; } /**
     * InputObjectTypeDefinition :
     *   - Description? input Name Directives[Const]? InputFieldsDefinition?
     */ parseInputObjectTypeDefinition ( ) { const start = this . _lexer . token ; const description = this . parseDescription ( ) ; this . expectKeyword ( 'input' ) ; const name = this . parseName ( ) ; const directives = this . parseConstDirectives ( ) ; const fields = this . parseInputFieldsDefinition ( ) ; return this . node < InputObjectTypeDefinitionNode > ( start , { kind : Kind . INPUT_OBJECT_TYPE_DEFINITION , description , name , directives , fields , } ) ; } /**
     * ```
     * InputFieldsDefinition : { InputValueDefinition+ }
     * ```
     */ parseInputFieldsDefinition ( ) { return this . optionalMany ( TokenKind . BRACE_L , this . parseInputValueDef , TokenKind . BRACE_R ) ; } /**
     * TypeSystemExtension :
     *   - SchemaExtension
     *   - TypeExtension
     *
     * TypeExtension :
     *   - ScalarTypeExtension
     *   - ObjectTypeExtension
     *   - InterfaceTypeExtension
     *   - UnionTypeExtension
     *   - EnumTypeExtension
     *   - InputObjectTypeDefinition
     */ parseTypeSystemExtension ( ) { const keywordToken = this . _lexer . lookahead ( ) ; if ( keywordToken . kind === TokenKind . NAME ) { switch ( keywordToken . value ) { case 'schema' : return this . parseSchemaExtension ( ) ; case 'scalar' : return this . parseScalarTypeExtension ( ) ; case 'type' : return this . parseObjectTypeExtension ( ) ; case 'interface' : return this . parseInterfaceTypeExtension ( ) ; case 'union' : return this . parseUnionTypeExtension ( ) ; case 'enum' : return this . parseEnumTypeExtension ( ) ; case 'input' : return this . parseInputObjectTypeExtension ( ) ; } } throw this . unexpected ( keywordToken ) ; } /**
     * ```
     * SchemaExtension :
     *  - extend schema Directives[Const]? { OperationTypeDefinition+ }
     *  - extend schema Directives[Const]
     * ```
     */ parseSchemaExtension ( ) { const start = this . _lexer . token ; this . expectKeyword ( 'extend' ) ; this . expectKeyword ( 'schema' ) ; const directives = this . parseConstDirectives ( ) ; const operationTypes = this . optionalMany ( TokenKind . BRACE_L , this . parseOperationTypeDefinition , TokenKind . BRACE_R ) ; if ( directives . length === 0 && operationTypes . length === 0 ) { throw this . unexpected ( ) ; } return this . node < SchemaExtensionNode > ( start , { kind : Kind . SCHEMA_EXTENSION , directives , operationTypes , } ) ; } /**
     * ScalarTypeExtension :
     *   - extend scalar Name Directives[Const]
     */ parseScalarTypeExtension ( ) { const start = this . _lexer . token ; this . expectKeyword ( 'extend' ) ; this . expectKeyword ( 'scalar' ) ; const name = this . parseName ( ) ; const directives = this . parseConstDirectives ( ) ; if ( directives . length === 0 ) { throw this . unexpected ( ) ; } return this . node < ScalarTypeExtensionNode > ( start , { kind : Kind . SCALAR_TYPE_EXTENSION , name , directives , } ) ; } /**
     * ObjectTypeExtension :
     *  - extend type Name ImplementsInterfaces? Directives[Const]? FieldsDefinition
     *  - extend type Name ImplementsInterfaces? Directives[Const]
     *  - extend type Name ImplementsInterfaces
     */ parseObjectTypeExtension ( ) { const start = this . _lexer . token ; this . expectKeyword ( 'extend' ) ; this . expectKeyword ( 'type' ) ; const name = this . parseName ( ) ; const interfaces = this . parseImplementsInterfaces ( ) ; const directives = this . parseConstDirectives ( ) ; const fields = this . parseFieldsDefinition ( ) ; if ( interfaces . length === 0 && directives . length === 0 && fields . length === 0 ) { throw this . unexpected ( ) ; } return this . node < ObjectTypeExtensionNode > ( start , { kind : Kind . OBJECT_TYPE_EXTENSION , name , interfaces , directives , fields , } ) ; } /**
     * InterfaceTypeExtension :
     *  - extend interface Name ImplementsInterfaces? Directives[Const]? FieldsDefinition
     *  - extend interface Name ImplementsInterfaces? Directives[Const]
     *  - extend interface Name ImplementsInterfaces
     */ parseInterfaceTypeExtension ( ) { const start = this . _lexer . token ; this . expectKeyword ( 'extend' ) ; this . expectKeyword ( 'interface' ) ; const name = this . parseName ( ) ; const interfaces = this . parseImplementsInterfaces ( ) ; const directives = this . parseConstDirectives ( ) ; const fields = this . parseFieldsDefinition ( ) ; if ( interfaces . length === 0 && directives . length === 0 && fields . length === 0 ) { throw this . unexpected ( ) ; } return this . node < InterfaceTypeExtensionNode > ( start , { kind : Kind . INTERFACE_TYPE_EXTENSION , name , interfaces , directives , fields , } ) ; } /**
     * UnionTypeExtension :
     *   - extend union Name Directives[Const]? UnionMemberTypes
     *   - extend union Name Directives[Const]
     */ parseUnionTypeExtension ( ) { const start = this . _lexer . token ; this . expectKeyword ( 'extend' ) ; this . expectKeyword ( 'union' ) ; const name = this . parseName ( ) ; const directives = this . parseConstDirectives ( ) ; const types = this . parseUnionMemberTypes ( ) ; if ( directives . length === 0 && types . length === 0 ) { throw this . unexpected ( ) ; } return this . node < UnionTypeExtensionNode > ( start , { kind : Kind . UNION_TYPE_EXTENSION , name , directives , types , } ) ; } /**
     * EnumTypeExtension :
     *   - extend enum Name Directives[Const]? EnumValuesDefinition
     *   - extend enum Name Directives[Const]
     */ parseEnumTypeExtension ( ) { const start = this . _lexer . token ; this . expectKeyword ( 'extend' ) ; this . expectKeyword ( 'enum' ) ; const name = this . parseName ( ) ; const directives = this . parseConstDirectives ( ) ; const values = this . parseEnumValuesDefinition ( ) ; if ( directives . length === 0 && values . length === 0 ) { throw this . unexpected ( ) ; } return this . node < EnumTypeExtensionNode > ( start , { kind : Kind . ENUM_TYPE_EXTENSION , name , directives , values , } ) ; } /**
     * InputObjectTypeExtension :
     *   - extend input Name Directives[Const]? InputFieldsDefinition
     *   - extend input Name Directives[Const]
     */ parseInputObjectTypeExtension ( ) { const start = this . _lexer . token ; this . expectKeyword ( 'extend' ) ; this . expectKeyword ( 'input' ) ; const name = this . parseName ( ) ; const directives = this . parseConstDirectives ( ) ; const fields = this . parseInputFieldsDefinition ( ) ; if ( directives . length === 0 && fields . length === 0 ) { throw this . unexpected ( ) ; } return this . node < InputObjectTypeExtensionNode > ( start , { kind : Kind . INPUT_OBJECT_TYPE_EXTENSION , name , directives , fields , } ) ; } /**
     * ```
     * DirectiveDefinition :
     *   - Description? directive @ Name ArgumentsDefinition? `repeatable`? on DirectiveLocations
     * ```
     */ parseDirectiveDefinition ( ) { const start = this . _lexer . token ; const description = this . parseDescription ( ) ; this . expectKeyword ( 'directive' ) ; this . expectToken ( TokenKind . AT ) ; const name = this . parseName ( ) ; const args = this . parseArgumentDefs ( ) ; const repeatable = this . expectOptionalKeyword ( 'repeatable' ) ; this . expectKeyword ( 'on' ) ; const locations = this . parseDirectiveLocations ( ) ; return this . node < DirectiveDefinitionNode > ( start , { kind : Kind . DIRECTIVE_DEFINITION , description , name , arguments : args , repeatable , locations , } ) ; } /**
     * DirectiveLocations :
     *   - `|`? DirectiveLocation
     *   - DirectiveLocations | DirectiveLocation
     */ parseDirectiveLocations ( ) { return this . delimitedMany ( TokenKind . PIPE , this . parseDirectiveLocation ) ; } parseDirectiveLocation ( ) { const start = this . _lexer . token ; const name = this . parseName ( ) ; if ( Object . prototype . hasOwnProperty . call ( DirectiveLocation , name . value ) ) { return name ; } throw this . unexpected ( start ) ; } /**
     * Returns a node that, if configured to do so, sets a "loc" field as a
     * location object, used to identify the place in the source that created a
     * given parsed object.
     */ node < T extends { loc ? : Location ; } > ( startToken , node ) { if ( this . _options ?. noLocation !== true ) { node . loc = new Location ( startToken , this . _lexer . lastToken , this . _lexer . source ) ; } return node ; } /**
     * Determines if the next token is of a given kind
     */ peek ( kind ) { return this . _lexer . token . kind === kind ; } /**
     * If the next token is of the given kind, return that token after advancing the lexer.
     * Otherwise, do not change the parser state and throw an error.
     */ expectToken ( kind ) { const token = this . _lexer . token ; if ( token . kind === kind ) { this . _lexer . advance ( ) ; return token ; } throw syntaxError ( this . _lexer . source , token . start , `Expected ${ getTokenKindDesc ( kind ) }, found ${ getTokenDesc ( token ) }.` ) ; } /**
     * If the next token is of the given kind, return "true" after advancing the lexer.
     * Otherwise, do not change the parser state and return "false".
     */ expectOptionalToken ( kind ) { const token = this . _lexer . token ; if ( token . kind === kind ) { this . _lexer . advance ( ) ; return true ; } return false ; } /**
     * If the next token is a given keyword, advance the lexer.
     * Otherwise, do not change the parser state and throw an error.
     */ expectKeyword ( value ) { const token = this . _lexer . token ; if ( token . kind === TokenKind . NAME && token . value === value ) { this . _lexer . advance ( ) ; } else { throw syntaxError ( this . _lexer . source , token . start , `Expected "${ value }", found ${ getTokenDesc ( token ) }.` ) ; } } /**
     * If the next token is a given keyword, return "true" after advancing the lexer.
     * Otherwise, do not change the parser state and return "false".
     */ expectOptionalKeyword ( value ) { const token = this . _lexer . token ; if ( token . kind === TokenKind . NAME && token . value === value ) { this . _lexer . advance ( ) ; return true ; } return false ; } /**
     * Helper function for creating an error when an unexpected lexed token is encountered.
     */ unexpected ( atToken ? ) { const token = atToken ?? this . _lexer . token ; return syntaxError ( this . _lexer . source , token . start , `Unexpected ${ getTokenDesc ( token ) }.` ) ; } /**
     * Returns a possibly empty list of parse nodes, determined by the parseFn.
     * This list begins with a lex token of openKind and ends with a lex token of closeKind.
     * Advances the parser to the next lex token after the closing token.
     */ any < T > ( openKind , parseFn , closeKind ) { this . expectToken ( openKind ) ; const nodes = [ ] ; while ( ! this . expectOptionalToken ( closeKind ) ) { nodes . push ( parseFn . call ( this ) ) ; } return nodes ; } /**
     * Returns a list of parse nodes, determined by the parseFn.
     * It can be empty only if open token is missing otherwise it will always return non-empty list
     * that begins with a lex token of openKind and ends with a lex token of closeKind.
     * Advances the parser to the next lex token after the closing token.
     */ optionalMany < T > ( openKind , parseFn , closeKind ) { if ( this . expectOptionalToken ( openKind ) ) { const nodes = [ ] ; do { nodes . push ( parseFn . call ( this ) ) ; } while ( ! this . expectOptionalToken ( closeKind ) ) ; return nodes ; } return [ ] ; } /**
     * Returns a non-empty list of parse nodes, determined by the parseFn.
     * This list begins with a lex token of openKind and ends with a lex token of closeKind.
     * Advances the parser to the next lex token after the closing token.
     */ many < T > ( openKind , parseFn , closeKind ) { this . expectToken ( openKind ) ; const nodes = [ ] ; do { nodes . push ( parseFn . call ( this ) ) ; } while ( ! this . expectOptionalToken ( closeKind ) ) ; return nodes ; } /**
     * Returns a non-empty list of parse nodes, determined by the parseFn.
     * This list may begin with a lex token of delimiterKind followed by items separated by lex tokens of tokenKind.
     * Advances the parser to the next lex token after last item in the list.
     */ delimitedMany < T > ( delimiterKind , parseFn ) { this . expectOptionalToken ( delimiterKind ) ; const nodes = [ ] ; do { nodes . push ( parseFn . call ( this ) ) ; } while ( this . expectOptionalToken ( delimiterKind ) ) ; return nodes ; } } /**
 * A helper function to describe a token as a string for debugging.
 */ function getTokenDesc ( token ) { const value = token . value ; return getTokenKindDesc ( token . kind ) + ( value != null ? ` "${ value }"` : '' ) ; } /**
 * A helper function to describe a token kind as a string for debugging.
 */ function getTokenKindDesc ( kind ) { return isPunctuatorTokenKind ( kind ) ? `"${ kind }"` : kind ; }