vendor/webonyx/graphql-php/src/Language/Parser.php line 521

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace GraphQL\Language;
  4. use GraphQL\Error\SyntaxError;
  5. use GraphQL\Language\AST\ArgumentNode;
  6. use GraphQL\Language\AST\BooleanValueNode;
  7. use GraphQL\Language\AST\DefinitionNode;
  8. use GraphQL\Language\AST\DirectiveDefinitionNode;
  9. use GraphQL\Language\AST\DirectiveNode;
  10. use GraphQL\Language\AST\DocumentNode;
  11. use GraphQL\Language\AST\EnumTypeDefinitionNode;
  12. use GraphQL\Language\AST\EnumTypeExtensionNode;
  13. use GraphQL\Language\AST\EnumValueDefinitionNode;
  14. use GraphQL\Language\AST\EnumValueNode;
  15. use GraphQL\Language\AST\ExecutableDefinitionNode;
  16. use GraphQL\Language\AST\FieldDefinitionNode;
  17. use GraphQL\Language\AST\FieldNode;
  18. use GraphQL\Language\AST\FloatValueNode;
  19. use GraphQL\Language\AST\FragmentDefinitionNode;
  20. use GraphQL\Language\AST\FragmentSpreadNode;
  21. use GraphQL\Language\AST\InlineFragmentNode;
  22. use GraphQL\Language\AST\InputObjectTypeDefinitionNode;
  23. use GraphQL\Language\AST\InputObjectTypeExtensionNode;
  24. use GraphQL\Language\AST\InputValueDefinitionNode;
  25. use GraphQL\Language\AST\InterfaceTypeDefinitionNode;
  26. use GraphQL\Language\AST\InterfaceTypeExtensionNode;
  27. use GraphQL\Language\AST\IntValueNode;
  28. use GraphQL\Language\AST\ListTypeNode;
  29. use GraphQL\Language\AST\ListValueNode;
  30. use GraphQL\Language\AST\Location;
  31. use GraphQL\Language\AST\NamedTypeNode;
  32. use GraphQL\Language\AST\NameNode;
  33. use GraphQL\Language\AST\Node;
  34. use GraphQL\Language\AST\NodeList;
  35. use GraphQL\Language\AST\NonNullTypeNode;
  36. use GraphQL\Language\AST\NullValueNode;
  37. use GraphQL\Language\AST\ObjectFieldNode;
  38. use GraphQL\Language\AST\ObjectTypeDefinitionNode;
  39. use GraphQL\Language\AST\ObjectTypeExtensionNode;
  40. use GraphQL\Language\AST\ObjectValueNode;
  41. use GraphQL\Language\AST\OperationDefinitionNode;
  42. use GraphQL\Language\AST\OperationTypeDefinitionNode;
  43. use GraphQL\Language\AST\ScalarTypeDefinitionNode;
  44. use GraphQL\Language\AST\ScalarTypeExtensionNode;
  45. use GraphQL\Language\AST\SchemaDefinitionNode;
  46. use GraphQL\Language\AST\SchemaTypeExtensionNode;
  47. use GraphQL\Language\AST\SelectionNode;
  48. use GraphQL\Language\AST\SelectionSetNode;
  49. use GraphQL\Language\AST\StringValueNode;
  50. use GraphQL\Language\AST\TypeExtensionNode;
  51. use GraphQL\Language\AST\TypeNode;
  52. use GraphQL\Language\AST\TypeSystemDefinitionNode;
  53. use GraphQL\Language\AST\UnionTypeDefinitionNode;
  54. use GraphQL\Language\AST\UnionTypeExtensionNode;
  55. use GraphQL\Language\AST\ValueNode;
  56. use GraphQL\Language\AST\VariableDefinitionNode;
  57. use GraphQL\Language\AST\VariableNode;
  58. use function count;
  59. use function sprintf;
  60. /**
  61.  * Parses string containing GraphQL query or [type definition](type-system/type-language.md) to Abstract Syntax Tree.
  62.  *
  63.  * Those magic functions allow partial parsing:
  64.  *
  65.  * @method static NameNode name(Source|string $source, bool[] $options = [])
  66.  * @method static DocumentNode document(Source|string $source, bool[] $options = [])
  67.  * @method static ExecutableDefinitionNode|TypeSystemDefinitionNode definition(Source|string $source, bool[] $options = [])
  68.  * @method static ExecutableDefinitionNode executableDefinition(Source|string $source, bool[] $options = [])
  69.  * @method static OperationDefinitionNode operationDefinition(Source|string $source, bool[] $options = [])
  70.  * @method static string operationType(Source|string $source, bool[] $options = [])
  71.  * @method static NodeList<VariableDefinitionNode> variableDefinitions(Source|string $source, bool[] $options = [])
  72.  * @method static VariableDefinitionNode variableDefinition(Source|string $source, bool[] $options = [])
  73.  * @method static VariableNode variable(Source|string $source, bool[] $options = [])
  74.  * @method static SelectionSetNode selectionSet(Source|string $source, bool[] $options = [])
  75.  * @method static mixed selection(Source|string $source, bool[] $options = [])
  76.  * @method static FieldNode field(Source|string $source, bool[] $options = [])
  77.  * @method static NodeList<ArgumentNode> arguments(Source|string $source, bool[] $options = [])
  78.  * @method static NodeList<ArgumentNode> constArguments(Source|string $source, bool[] $options = [])
  79.  * @method static ArgumentNode argument(Source|string $source, bool[] $options = [])
  80.  * @method static ArgumentNode constArgument(Source|string $source, bool[] $options = [])
  81.  * @method static FragmentSpreadNode|InlineFragmentNode fragment(Source|string $source, bool[] $options = [])
  82.  * @method static FragmentDefinitionNode fragmentDefinition(Source|string $source, bool[] $options = [])
  83.  * @method static NameNode fragmentName(Source|string $source, bool[] $options = [])
  84.  * @method static BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|ListValueNode|NullValueNode|ObjectValueNode|StringValueNode|VariableNode valueLiteral(Source|string $source, bool[] $options = [])
  85.  * @method static BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|ListValueNode|NullValueNode|ObjectValueNode|StringValueNode constValueLiteral(Source|string $source, bool[] $options = [])
  86.  * @method static StringValueNode stringLiteral(Source|string $source, bool[] $options = [])
  87.  * @method static BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|StringValueNode constValue(Source|string $source, bool[] $options = [])
  88.  * @method static BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|ListValueNode|ObjectValueNode|StringValueNode|VariableNode variableValue(Source|string $source, bool[] $options = [])
  89.  * @method static ListValueNode array(Source|string $source, bool[] $options = [])
  90.  * @method static ListValueNode constArray(Source|string $source, bool[] $options = [])
  91.  * @method static ObjectValueNode object(Source|string $source, bool[] $options = [])
  92.  * @method static ObjectValueNode constObject(Source|string $source, bool[] $options = [])
  93.  * @method static ObjectFieldNode objectField(Source|string $source, bool[] $options = [])
  94.  * @method static ObjectFieldNode constObjectField(Source|string $source, bool[] $options = [])
  95.  * @method static NodeList<DirectiveNode> directives(Source|string $source, bool[] $options = [])
  96.  * @method static NodeList<DirectiveNode> constDirectives(Source|string $source, bool[] $options = [])
  97.  * @method static DirectiveNode directive(Source|string $source, bool[] $options = [])
  98.  * @method static DirectiveNode constDirective(Source|string $source, bool[] $options = [])
  99.  * @method static ListTypeNode|NamedTypeNode|NonNullTypeNode typeReference(Source|string $source, bool[] $options = [])
  100.  * @method static NamedTypeNode namedType(Source|string $source, bool[] $options = [])
  101.  * @method static TypeSystemDefinitionNode typeSystemDefinition(Source|string $source, bool[] $options = [])
  102.  * @method static StringValueNode|null description(Source|string $source, bool[] $options = [])
  103.  * @method static SchemaDefinitionNode schemaDefinition(Source|string $source, bool[] $options = [])
  104.  * @method static OperationTypeDefinitionNode operationTypeDefinition(Source|string $source, bool[] $options = [])
  105.  * @method static ScalarTypeDefinitionNode scalarTypeDefinition(Source|string $source, bool[] $options = [])
  106.  * @method static ObjectTypeDefinitionNode objectTypeDefinition(Source|string $source, bool[] $options = [])
  107.  * @method static NodeList<NamedTypeNode> implementsInterfaces(Source|string $source, bool[] $options = [])
  108.  * @method static NodeList<FieldDefinitionNode> fieldsDefinition(Source|string $source, bool[] $options = [])
  109.  * @method static FieldDefinitionNode fieldDefinition(Source|string $source, bool[] $options = [])
  110.  * @method static NodeList<InputValueDefinitionNode> argumentsDefinition(Source|string $source, bool[] $options = [])
  111.  * @method static InputValueDefinitionNode inputValueDefinition(Source|string $source, bool[] $options = [])
  112.  * @method static InterfaceTypeDefinitionNode interfaceTypeDefinition(Source|string $source, bool[] $options = [])
  113.  * @method static UnionTypeDefinitionNode unionTypeDefinition(Source|string $source, bool[] $options = [])
  114.  * @method static NodeList<NamedTypeNode> unionMemberTypes(Source|string $source, bool[] $options = [])
  115.  * @method static EnumTypeDefinitionNode enumTypeDefinition(Source|string $source, bool[] $options = [])
  116.  * @method static NodeList<EnumValueDefinitionNode> enumValuesDefinition(Source|string $source, bool[] $options = [])
  117.  * @method static EnumValueDefinitionNode enumValueDefinition(Source|string $source, bool[] $options = [])
  118.  * @method static InputObjectTypeDefinitionNode inputObjectTypeDefinition(Source|string $source, bool[] $options = [])
  119.  * @method static NodeList<InputValueDefinitionNode> inputFieldsDefinition(Source|string $source, bool[] $options = [])
  120.  * @method static TypeExtensionNode typeExtension(Source|string $source, bool[] $options = [])
  121.  * @method static SchemaTypeExtensionNode schemaTypeExtension(Source|string $source, bool[] $options = [])
  122.  * @method static ScalarTypeExtensionNode scalarTypeExtension(Source|string $source, bool[] $options = [])
  123.  * @method static ObjectTypeExtensionNode objectTypeExtension(Source|string $source, bool[] $options = [])
  124.  * @method static InterfaceTypeExtensionNode interfaceTypeExtension(Source|string $source, bool[] $options = [])
  125.  * @method static UnionTypeExtensionNode unionTypeExtension(Source|string $source, bool[] $options = [])
  126.  * @method static EnumTypeExtensionNode enumTypeExtension(Source|string $source, bool[] $options = [])
  127.  * @method static InputObjectTypeExtensionNode inputObjectTypeExtension(Source|string $source, bool[] $options = [])
  128.  * @method static DirectiveDefinitionNode directiveDefinition(Source|string $source, bool[] $options = [])
  129.  * @method static NodeList<NameNode> directiveLocations(Source|string $source, bool[] $options = [])
  130.  * @method static NameNode directiveLocation(Source|string $source, bool[] $options = [])
  131.  */
  132. class Parser
  133. {
  134.     /**
  135.      * Given a GraphQL source, parses it into a `GraphQL\Language\AST\DocumentNode`.
  136.      * Throws `GraphQL\Error\SyntaxError` if a syntax error is encountered.
  137.      *
  138.      * Available options:
  139.      *
  140.      * noLocation: boolean,
  141.      *   (By default, the parser creates AST nodes that know the location
  142.      *   in the source that they correspond to. This configuration flag
  143.      *   disables that behavior for performance or testing.)
  144.      *
  145.      * allowLegacySDLEmptyFields: boolean
  146.      *   If enabled, the parser will parse empty fields sets in the Schema
  147.      *   Definition Language. Otherwise, the parser will follow the current
  148.      *   specification.
  149.      *
  150.      *   This option is provided to ease adoption of the final SDL specification
  151.      *   and will be removed in a future major release.
  152.      *
  153.      * allowLegacySDLImplementsInterfaces: boolean
  154.      *   If enabled, the parser will parse implemented interfaces with no `&`
  155.      *   character between each interface. Otherwise, the parser will follow the
  156.      *   current specification.
  157.      *
  158.      *   This option is provided to ease adoption of the final SDL specification
  159.      *   and will be removed in a future major release.
  160.      *
  161.      * experimentalFragmentVariables: boolean,
  162.      *   (If enabled, the parser will understand and parse variable definitions
  163.      *   contained in a fragment definition. They'll be represented in the
  164.      *   `variableDefinitions` field of the FragmentDefinitionNode.
  165.      *
  166.      *   The syntax is identical to normal, query-defined variables. For example:
  167.      *
  168.      *     fragment A($var: Boolean = false) on T  {
  169.      *       ...
  170.      *     }
  171.      *
  172.      *   Note: this feature is experimental and may change or be removed in the
  173.      *   future.)
  174.      *
  175.      * @param Source|string $source
  176.      * @param bool[]        $options
  177.      *
  178.      * @return DocumentNode
  179.      *
  180.      * @throws SyntaxError
  181.      *
  182.      * @api
  183.      */
  184.     public static function parse($source, array $options = [])
  185.     {
  186.         $parser = new self($source$options);
  187.         return $parser->parseDocument();
  188.     }
  189.     /**
  190.      * Given a string containing a GraphQL value (ex. `[42]`), parse the AST for
  191.      * that value.
  192.      * Throws `GraphQL\Error\SyntaxError` if a syntax error is encountered.
  193.      *
  194.      * This is useful within tools that operate upon GraphQL Values directly and
  195.      * in isolation of complete GraphQL documents.
  196.      *
  197.      * Consider providing the results to the utility function: `GraphQL\Utils\AST::valueFromAST()`.
  198.      *
  199.      * @param Source|string $source
  200.      * @param bool[]        $options
  201.      *
  202.      * @return BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|ListValueNode|ObjectValueNode|StringValueNode|VariableNode
  203.      *
  204.      * @api
  205.      */
  206.     public static function parseValue($source, array $options = [])
  207.     {
  208.         $parser = new Parser($source$options);
  209.         $parser->expect(Token::SOF);
  210.         $value $parser->parseValueLiteral(false);
  211.         $parser->expect(Token::EOF);
  212.         return $value;
  213.     }
  214.     /**
  215.      * Given a string containing a GraphQL Type (ex. `[Int!]`), parse the AST for
  216.      * that type.
  217.      * Throws `GraphQL\Error\SyntaxError` if a syntax error is encountered.
  218.      *
  219.      * This is useful within tools that operate upon GraphQL Types directly and
  220.      * in isolation of complete GraphQL documents.
  221.      *
  222.      * Consider providing the results to the utility function: `GraphQL\Utils\AST::typeFromAST()`.
  223.      *
  224.      * @param Source|string $source
  225.      * @param bool[]        $options
  226.      *
  227.      * @return ListTypeNode|NamedTypeNode|NonNullTypeNode
  228.      *
  229.      * @api
  230.      */
  231.     public static function parseType($source, array $options = [])
  232.     {
  233.         $parser = new Parser($source$options);
  234.         $parser->expect(Token::SOF);
  235.         $type $parser->parseTypeReference();
  236.         $parser->expect(Token::EOF);
  237.         return $type;
  238.     }
  239.     /**
  240.      * Parse partial source by delegating calls to the internal parseX methods.
  241.      *
  242.      * @param bool[] $arguments
  243.      *
  244.      * @throws SyntaxError
  245.      */
  246.     public static function __callStatic(string $name, array $arguments)
  247.     {
  248.         $parser = new Parser(...$arguments);
  249.         $parser->expect(Token::SOF);
  250.         switch ($name) {
  251.             case 'arguments':
  252.             case 'valueLiteral':
  253.             case 'array':
  254.             case 'object':
  255.             case 'objectField':
  256.             case 'directives':
  257.             case 'directive':
  258.                 $type $parser->{'parse' $name}(false);
  259.                 break;
  260.             case 'constArguments':
  261.                 $type $parser->parseArguments(true);
  262.                 break;
  263.             case 'constValueLiteral':
  264.                 $type $parser->parseValueLiteral(true);
  265.                 break;
  266.             case 'constArray':
  267.                 $type $parser->parseArray(true);
  268.                 break;
  269.             case 'constObject':
  270.                 $type $parser->parseObject(true);
  271.                 break;
  272.             case 'constObjectField':
  273.                 $type $parser->parseObjectField(true);
  274.                 break;
  275.             case 'constDirectives':
  276.                 $type $parser->parseDirectives(true);
  277.                 break;
  278.             case 'constDirective':
  279.                 $type $parser->parseDirective(true);
  280.                 break;
  281.             default:
  282.                 $type $parser->{'parse' $name}();
  283.         }
  284.         $parser->expect(Token::EOF);
  285.         return $type;
  286.     }
  287.     /** @var Lexer */
  288.     private $lexer;
  289.     /**
  290.      * @param Source|string $source
  291.      * @param bool[]        $options
  292.      */
  293.     public function __construct($source, array $options = [])
  294.     {
  295.         $sourceObj   $source instanceof Source $source : new Source($source);
  296.         $this->lexer = new Lexer($sourceObj$options);
  297.     }
  298.     /**
  299.      * Returns a location object, used to identify the place in
  300.      * the source that created a given parsed object.
  301.      */
  302.     private function loc(Token $startToken) : ?Location
  303.     {
  304.         if (! ($this->lexer->options['noLocation'] ?? false)) {
  305.             return new Location($startToken$this->lexer->lastToken$this->lexer->source);
  306.         }
  307.         return null;
  308.     }
  309.     /**
  310.      * Determines if the next token is of a given kind
  311.      */
  312.     private function peek(string $kind) : bool
  313.     {
  314.         return $this->lexer->token->kind === $kind;
  315.     }
  316.     /**
  317.      * If the next token is of the given kind, return true after advancing
  318.      * the parser. Otherwise, do not change the parser state and return false.
  319.      */
  320.     private function skip(string $kind) : bool
  321.     {
  322.         $match $this->lexer->token->kind === $kind;
  323.         if ($match) {
  324.             $this->lexer->advance();
  325.         }
  326.         return $match;
  327.     }
  328.     /**
  329.      * If the next token is of the given kind, return that token after advancing
  330.      * the parser. Otherwise, do not change the parser state and return false.
  331.      *
  332.      * @throws SyntaxError
  333.      */
  334.     private function expect(string $kind) : Token
  335.     {
  336.         $token $this->lexer->token;
  337.         if ($token->kind === $kind) {
  338.             $this->lexer->advance();
  339.             return $token;
  340.         }
  341.         throw new SyntaxError(
  342.             $this->lexer->source,
  343.             $token->start,
  344.             sprintf('Expected %s, found %s'$kind$token->getDescription())
  345.         );
  346.     }
  347.     /**
  348.      * If the next token is a keyword with the given value, advance the lexer.
  349.      * Otherwise, throw an error.
  350.      *
  351.      * @throws SyntaxError
  352.      */
  353.     private function expectKeyword(string $value) : void
  354.     {
  355.         $token $this->lexer->token;
  356.         if ($token->kind !== Token::NAME || $token->value !== $value) {
  357.             throw new SyntaxError(
  358.                 $this->lexer->source,
  359.                 $token->start,
  360.                 'Expected "' $value '", found ' $token->getDescription()
  361.             );
  362.         }
  363.         $this->lexer->advance();
  364.     }
  365.     /**
  366.      * If the next token is a given keyword, return "true" after advancing
  367.      * the lexer. Otherwise, do not change the parser state and return "false".
  368.      */
  369.     private function expectOptionalKeyword(string $value) : bool
  370.     {
  371.         $token $this->lexer->token;
  372.         if ($token->kind === Token::NAME && $token->value === $value) {
  373.             $this->lexer->advance();
  374.             return true;
  375.         }
  376.         return false;
  377.     }
  378.     private function unexpected(?Token $atToken null) : SyntaxError
  379.     {
  380.         $token $atToken ?? $this->lexer->token;
  381.         return new SyntaxError($this->lexer->source$token->start'Unexpected ' $token->getDescription());
  382.     }
  383.     /**
  384.      * Returns a possibly empty list of parse nodes, determined by
  385.      * the parseFn. This list begins with a lex token of openKind
  386.      * and ends with a lex token of closeKind. Advances the parser
  387.      * to the next lex token after the closing token.
  388.      *
  389.      * @throws SyntaxError
  390.      */
  391.     private function any(string $openKind, callable $parseFnstring $closeKind) : NodeList
  392.     {
  393.         $this->expect($openKind);
  394.         $nodes = [];
  395.         while (! $this->skip($closeKind)) {
  396.             $nodes[] = $parseFn($this);
  397.         }
  398.         return new NodeList($nodes);
  399.     }
  400.     /**
  401.      * Returns a non-empty list of parse nodes, determined by
  402.      * the parseFn. This list begins with a lex token of openKind
  403.      * and ends with a lex token of closeKind. Advances the parser
  404.      * to the next lex token after the closing token.
  405.      *
  406.      * @throws SyntaxError
  407.      */
  408.     private function many(string $openKind, callable $parseFnstring $closeKind) : NodeList
  409.     {
  410.         $this->expect($openKind);
  411.         $nodes = [$parseFn($this)];
  412.         while (! $this->skip($closeKind)) {
  413.             $nodes[] = $parseFn($this);
  414.         }
  415.         return new NodeList($nodes);
  416.     }
  417.     /**
  418.      * Converts a name lex token into a name parse node.
  419.      *
  420.      * @throws SyntaxError
  421.      */
  422.     private function parseName() : NameNode
  423.     {
  424.         $token $this->expect(Token::NAME);
  425.         return new NameNode([
  426.             'value' => $token->value,
  427.             'loc'   => $this->loc($token),
  428.         ]);
  429.     }
  430.     /**
  431.      * Implements the parsing rules in the Document section.
  432.      *
  433.      * @throws SyntaxError
  434.      */
  435.     private function parseDocument() : DocumentNode
  436.     {
  437.         $start $this->lexer->token;
  438.         return new DocumentNode([
  439.             'definitions' => $this->many(
  440.                 Token::SOF,
  441.                 function () {
  442.                     return $this->parseDefinition();
  443.                 },
  444.                 Token::EOF
  445.             ),
  446.             'loc'         => $this->loc($start),
  447.         ]);
  448.     }
  449.     /**
  450.      * @return ExecutableDefinitionNode|TypeSystemDefinitionNode
  451.      *
  452.      * @throws SyntaxError
  453.      */
  454.     private function parseDefinition() : DefinitionNode
  455.     {
  456.         if ($this->peek(Token::NAME)) {
  457.             switch ($this->lexer->token->value) {
  458.                 case 'query':
  459.                 case 'mutation':
  460.                 case 'subscription':
  461.                 case 'fragment':
  462.                     return $this->parseExecutableDefinition();
  463.                 // Note: The schema definition language is an experimental addition.
  464.                 case 'schema':
  465.                 case 'scalar':
  466.                 case 'type':
  467.                 case 'interface':
  468.                 case 'union':
  469.                 case 'enum':
  470.                 case 'input':
  471.                 case 'extend':
  472.                 case 'directive':
  473.                     // Note: The schema definition language is an experimental addition.
  474.                     return $this->parseTypeSystemDefinition();
  475.             }
  476.         } elseif ($this->peek(Token::BRACE_L)) {
  477.             return $this->parseExecutableDefinition();
  478.         } elseif ($this->peekDescription()) {
  479.             // Note: The schema definition language is an experimental addition.
  480.             return $this->parseTypeSystemDefinition();
  481.         }
  482.         throw $this->unexpected();
  483.     }
  484.     /**
  485.      * @throws SyntaxError
  486.      */
  487.     private function parseExecutableDefinition() : ExecutableDefinitionNode
  488.     {
  489.         if ($this->peek(Token::NAME)) {
  490.             switch ($this->lexer->token->value) {
  491.                 case 'query':
  492.                 case 'mutation':
  493.                 case 'subscription':
  494.                     return $this->parseOperationDefinition();
  495.                 case 'fragment':
  496.                     return $this->parseFragmentDefinition();
  497.             }
  498.         } elseif ($this->peek(Token::BRACE_L)) {
  499.             return $this->parseOperationDefinition();
  500.         }
  501.         throw $this->unexpected();
  502.     }
  503.     // Implements the parsing rules in the Operations section.
  504.     /**
  505.      * @throws SyntaxError
  506.      */
  507.     private function parseOperationDefinition() : OperationDefinitionNode
  508.     {
  509.         $start $this->lexer->token;
  510.         if ($this->peek(Token::BRACE_L)) {
  511.             return new OperationDefinitionNode([
  512.                 'operation'           => 'query',
  513.                 'name'                => null,
  514.                 'variableDefinitions' => new NodeList([]),
  515.                 'directives'          => new NodeList([]),
  516.                 'selectionSet'        => $this->parseSelectionSet(),
  517.                 'loc'                 => $this->loc($start),
  518.             ]);
  519.         }
  520.         $operation $this->parseOperationType();
  521.         $name null;
  522.         if ($this->peek(Token::NAME)) {
  523.             $name $this->parseName();
  524.         }
  525.         return new OperationDefinitionNode([
  526.             'operation'           => $operation,
  527.             'name'                => $name,
  528.             'variableDefinitions' => $this->parseVariableDefinitions(),
  529.             'directives'          => $this->parseDirectives(false),
  530.             'selectionSet'        => $this->parseSelectionSet(),
  531.             'loc'                 => $this->loc($start),
  532.         ]);
  533.     }
  534.     /**
  535.      * @throws SyntaxError
  536.      */
  537.     private function parseOperationType() : string
  538.     {
  539.         $operationToken $this->expect(Token::NAME);
  540.         switch ($operationToken->value) {
  541.             case 'query':
  542.                 return 'query';
  543.             case 'mutation':
  544.                 return 'mutation';
  545.             case 'subscription':
  546.                 return 'subscription';
  547.         }
  548.         throw $this->unexpected($operationToken);
  549.     }
  550.     private function parseVariableDefinitions() : NodeList
  551.     {
  552.         return $this->peek(Token::PAREN_L)
  553.             ? $this->many(
  554.                 Token::PAREN_L,
  555.                 function () : VariableDefinitionNode {
  556.                     return $this->parseVariableDefinition();
  557.                 },
  558.                 Token::PAREN_R
  559.             )
  560.             : new NodeList([]);
  561.     }
  562.     /**
  563.      * @throws SyntaxError
  564.      */
  565.     private function parseVariableDefinition() : VariableDefinitionNode
  566.     {
  567.         $start $this->lexer->token;
  568.         $var   $this->parseVariable();
  569.         $this->expect(Token::COLON);
  570.         $type $this->parseTypeReference();
  571.         return new VariableDefinitionNode([
  572.             'variable'     => $var,
  573.             'type'         => $type,
  574.             'defaultValue' => $this->skip(Token::EQUALS)
  575.                 ? $this->parseValueLiteral(true)
  576.                 : null,
  577.             'directives'   => $this->parseDirectives(true),
  578.             'loc'          => $this->loc($start),
  579.         ]);
  580.     }
  581.     /**
  582.      * @throws SyntaxError
  583.      */
  584.     private function parseVariable() : VariableNode
  585.     {
  586.         $start $this->lexer->token;
  587.         $this->expect(Token::DOLLAR);
  588.         return new VariableNode([
  589.             'name' => $this->parseName(),
  590.             'loc'  => $this->loc($start),
  591.         ]);
  592.     }
  593.     private function parseSelectionSet() : SelectionSetNode
  594.     {
  595.         $start $this->lexer->token;
  596.         return new SelectionSetNode(
  597.             [
  598.                 'selections' => $this->many(
  599.                     Token::BRACE_L,
  600.                     function () : SelectionNode {
  601.                         return $this->parseSelection();
  602.                     },
  603.                     Token::BRACE_R
  604.                 ),
  605.                 'loc'        => $this->loc($start),
  606.             ]
  607.         );
  608.     }
  609.     /**
  610.      *  Selection :
  611.      *   - Field
  612.      *   - FragmentSpread
  613.      *   - InlineFragment
  614.      */
  615.     private function parseSelection() : SelectionNode
  616.     {
  617.         return $this->peek(Token::SPREAD)
  618.             ? $this->parseFragment()
  619.             : $this->parseField();
  620.     }
  621.     /**
  622.      * @throws SyntaxError
  623.      */
  624.     private function parseField() : FieldNode
  625.     {
  626.         $start       $this->lexer->token;
  627.         $nameOrAlias $this->parseName();
  628.         if ($this->skip(Token::COLON)) {
  629.             $alias $nameOrAlias;
  630.             $name  $this->parseName();
  631.         } else {
  632.             $alias null;
  633.             $name  $nameOrAlias;
  634.         }
  635.         return new FieldNode([
  636.             'alias'        => $alias,
  637.             'name'         => $name,
  638.             'arguments'    => $this->parseArguments(false),
  639.             'directives'   => $this->parseDirectives(false),
  640.             'selectionSet' => $this->peek(Token::BRACE_L) ? $this->parseSelectionSet() : null,
  641.             'loc'          => $this->loc($start),
  642.         ]);
  643.     }
  644.     /**
  645.      * @throws SyntaxError
  646.      */
  647.     private function parseArguments(bool $isConst) : NodeList
  648.     {
  649.         $parseFn $isConst
  650.             ? function () : ArgumentNode {
  651.                 return $this->parseConstArgument();
  652.             }
  653.             : function () : ArgumentNode {
  654.                 return $this->parseArgument();
  655.             };
  656.         return $this->peek(Token::PAREN_L)
  657.             ? $this->many(Token::PAREN_L$parseFnToken::PAREN_R)
  658.             : new NodeList([]);
  659.     }
  660.     /**
  661.      * @throws SyntaxError
  662.      */
  663.     private function parseArgument() : ArgumentNode
  664.     {
  665.         $start $this->lexer->token;
  666.         $name  $this->parseName();
  667.         $this->expect(Token::COLON);
  668.         $value $this->parseValueLiteral(false);
  669.         return new ArgumentNode([
  670.             'name'  => $name,
  671.             'value' => $value,
  672.             'loc'   => $this->loc($start),
  673.         ]);
  674.     }
  675.     /**
  676.      * @throws SyntaxError
  677.      */
  678.     private function parseConstArgument() : ArgumentNode
  679.     {
  680.         $start $this->lexer->token;
  681.         $name  $this->parseName();
  682.         $this->expect(Token::COLON);
  683.         $value $this->parseConstValue();
  684.         return new ArgumentNode([
  685.             'name'  => $name,
  686.             'value' => $value,
  687.             'loc'   => $this->loc($start),
  688.         ]);
  689.     }
  690.     // Implements the parsing rules in the Fragments section.
  691.     /**
  692.      * @return FragmentSpreadNode|InlineFragmentNode
  693.      *
  694.      * @throws SyntaxError
  695.      */
  696.     private function parseFragment() : SelectionNode
  697.     {
  698.         $start $this->lexer->token;
  699.         $this->expect(Token::SPREAD);
  700.         $hasTypeCondition $this->expectOptionalKeyword('on');
  701.         if (! $hasTypeCondition && $this->peek(Token::NAME)) {
  702.             return new FragmentSpreadNode([
  703.                 'name'       => $this->parseFragmentName(),
  704.                 'directives' => $this->parseDirectives(false),
  705.                 'loc'        => $this->loc($start),
  706.             ]);
  707.         }
  708.         return new InlineFragmentNode([
  709.             'typeCondition' => $hasTypeCondition $this->parseNamedType() : null,
  710.             'directives'    => $this->parseDirectives(false),
  711.             'selectionSet'  => $this->parseSelectionSet(),
  712.             'loc'           => $this->loc($start),
  713.         ]);
  714.     }
  715.     /**
  716.      * @throws SyntaxError
  717.      */
  718.     private function parseFragmentDefinition() : FragmentDefinitionNode
  719.     {
  720.         $start $this->lexer->token;
  721.         $this->expectKeyword('fragment');
  722.         $name $this->parseFragmentName();
  723.         // Experimental support for defining variables within fragments changes
  724.         // the grammar of FragmentDefinition:
  725.         //   - fragment FragmentName VariableDefinitions? on TypeCondition Directives? SelectionSet
  726.         $variableDefinitions null;
  727.         if (isset($this->lexer->options['experimentalFragmentVariables'])) {
  728.             $variableDefinitions $this->parseVariableDefinitions();
  729.         }
  730.         $this->expectKeyword('on');
  731.         $typeCondition $this->parseNamedType();
  732.         return new FragmentDefinitionNode([
  733.             'name'                => $name,
  734.             'variableDefinitions' => $variableDefinitions,
  735.             'typeCondition'       => $typeCondition,
  736.             'directives'          => $this->parseDirectives(false),
  737.             'selectionSet'        => $this->parseSelectionSet(),
  738.             'loc'                 => $this->loc($start),
  739.         ]);
  740.     }
  741.     /**
  742.      * @throws SyntaxError
  743.      */
  744.     private function parseFragmentName() : NameNode
  745.     {
  746.         if ($this->lexer->token->value === 'on') {
  747.             throw $this->unexpected();
  748.         }
  749.         return $this->parseName();
  750.     }
  751.     // Implements the parsing rules in the Values section.
  752.     /**
  753.      * Value[Const] :
  754.      *   - [~Const] Variable
  755.      *   - IntValue
  756.      *   - FloatValue
  757.      *   - StringValue
  758.      *   - BooleanValue
  759.      *   - NullValue
  760.      *   - EnumValue
  761.      *   - ListValue[?Const]
  762.      *   - ObjectValue[?Const]
  763.      *
  764.      * BooleanValue : one of `true` `false`
  765.      *
  766.      * NullValue : `null`
  767.      *
  768.      * EnumValue : Name but not `true`, `false` or `null`
  769.      *
  770.      * @return BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|StringValueNode|VariableNode|ListValueNode|ObjectValueNode|NullValueNode
  771.      *
  772.      * @throws SyntaxError
  773.      */
  774.     private function parseValueLiteral(bool $isConst) : ValueNode
  775.     {
  776.         $token $this->lexer->token;
  777.         switch ($token->kind) {
  778.             case Token::BRACKET_L:
  779.                 return $this->parseArray($isConst);
  780.             case Token::BRACE_L:
  781.                 return $this->parseObject($isConst);
  782.             case Token::INT:
  783.                 $this->lexer->advance();
  784.                 return new IntValueNode([
  785.                     'value' => $token->value,
  786.                     'loc'   => $this->loc($token),
  787.                 ]);
  788.             case Token::FLOAT:
  789.                 $this->lexer->advance();
  790.                 return new FloatValueNode([
  791.                     'value' => $token->value,
  792.                     'loc'   => $this->loc($token),
  793.                 ]);
  794.             case Token::STRING:
  795.             case Token::BLOCK_STRING:
  796.                 return $this->parseStringLiteral();
  797.             case Token::NAME:
  798.                 if ($token->value === 'true' || $token->value === 'false') {
  799.                     $this->lexer->advance();
  800.                     return new BooleanValueNode([
  801.                         'value' => $token->value === 'true',
  802.                         'loc'   => $this->loc($token),
  803.                     ]);
  804.                 }
  805.                 if ($token->value === 'null') {
  806.                     $this->lexer->advance();
  807.                     return new NullValueNode([
  808.                         'loc' => $this->loc($token),
  809.                     ]);
  810.                 } else {
  811.                     $this->lexer->advance();
  812.                     return new EnumValueNode([
  813.                         'value' => $token->value,
  814.                         'loc'   => $this->loc($token),
  815.                     ]);
  816.                 }
  817.                 break;
  818.             case Token::DOLLAR:
  819.                 if (! $isConst) {
  820.                     return $this->parseVariable();
  821.                 }
  822.                 break;
  823.         }
  824.         throw $this->unexpected();
  825.     }
  826.     private function parseStringLiteral() : StringValueNode
  827.     {
  828.         $token $this->lexer->token;
  829.         $this->lexer->advance();
  830.         return new StringValueNode([
  831.             'value' => $token->value,
  832.             'block' => $token->kind === Token::BLOCK_STRING,
  833.             'loc'   => $this->loc($token),
  834.         ]);
  835.     }
  836.     /**
  837.      * @return BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|StringValueNode|VariableNode
  838.      *
  839.      * @throws SyntaxError
  840.      */
  841.     private function parseConstValue() : ValueNode
  842.     {
  843.         return $this->parseValueLiteral(true);
  844.     }
  845.     /**
  846.      * @return BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|ListValueNode|ObjectValueNode|StringValueNode|VariableNode
  847.      */
  848.     private function parseVariableValue() : ValueNode
  849.     {
  850.         return $this->parseValueLiteral(false);
  851.     }
  852.     private function parseArray(bool $isConst) : ListValueNode
  853.     {
  854.         $start   $this->lexer->token;
  855.         $parseFn $isConst
  856.             ? function () {
  857.                 return $this->parseConstValue();
  858.             }
  859.             : function () {
  860.                 return $this->parseVariableValue();
  861.             };
  862.         return new ListValueNode(
  863.             [
  864.                 'values' => $this->any(Token::BRACKET_L$parseFnToken::BRACKET_R),
  865.                 'loc'    => $this->loc($start),
  866.             ]
  867.         );
  868.     }
  869.     private function parseObject(bool $isConst) : ObjectValueNode
  870.     {
  871.         $start $this->lexer->token;
  872.         $this->expect(Token::BRACE_L);
  873.         $fields = [];
  874.         while (! $this->skip(Token::BRACE_R)) {
  875.             $fields[] = $this->parseObjectField($isConst);
  876.         }
  877.         return new ObjectValueNode([
  878.             'fields' => new NodeList($fields),
  879.             'loc'    => $this->loc($start),
  880.         ]);
  881.     }
  882.     private function parseObjectField(bool $isConst) : ObjectFieldNode
  883.     {
  884.         $start $this->lexer->token;
  885.         $name  $this->parseName();
  886.         $this->expect(Token::COLON);
  887.         return new ObjectFieldNode([
  888.             'name'  => $name,
  889.             'value' => $this->parseValueLiteral($isConst),
  890.             'loc'   => $this->loc($start),
  891.         ]);
  892.     }
  893.     // Implements the parsing rules in the Directives section.
  894.     /**
  895.      * @throws SyntaxError
  896.      */
  897.     private function parseDirectives(bool $isConst) : NodeList
  898.     {
  899.         $directives = [];
  900.         while ($this->peek(Token::AT)) {
  901.             $directives[] = $this->parseDirective($isConst);
  902.         }
  903.         return new NodeList($directives);
  904.     }
  905.     /**
  906.      * @throws SyntaxError
  907.      */
  908.     private function parseDirective(bool $isConst) : DirectiveNode
  909.     {
  910.         $start $this->lexer->token;
  911.         $this->expect(Token::AT);
  912.         return new DirectiveNode([
  913.             'name'      => $this->parseName(),
  914.             'arguments' => $this->parseArguments($isConst),
  915.             'loc'       => $this->loc($start),
  916.         ]);
  917.     }
  918.     // Implements the parsing rules in the Types section.
  919.     /**
  920.      * Handles the Type: TypeName, ListType, and NonNullType parsing rules.
  921.      *
  922.      * @return ListTypeNode|NamedTypeNode|NonNullTypeNode
  923.      *
  924.      * @throws SyntaxError
  925.      */
  926.     private function parseTypeReference() : TypeNode
  927.     {
  928.         $start $this->lexer->token;
  929.         if ($this->skip(Token::BRACKET_L)) {
  930.             $type $this->parseTypeReference();
  931.             $this->expect(Token::BRACKET_R);
  932.             $type = new ListTypeNode([
  933.                 'type' => $type,
  934.                 'loc'  => $this->loc($start),
  935.             ]);
  936.         } else {
  937.             $type $this->parseNamedType();
  938.         }
  939.         if ($this->skip(Token::BANG)) {
  940.             return new NonNullTypeNode([
  941.                 'type' => $type,
  942.                 'loc'  => $this->loc($start),
  943.             ]);
  944.         }
  945.         return $type;
  946.     }
  947.     private function parseNamedType() : NamedTypeNode
  948.     {
  949.         $start $this->lexer->token;
  950.         return new NamedTypeNode([
  951.             'name' => $this->parseName(),
  952.             'loc'  => $this->loc($start),
  953.         ]);
  954.     }
  955.     // Implements the parsing rules in the Type Definition section.
  956.     /**
  957.      * TypeSystemDefinition :
  958.      *   - SchemaDefinition
  959.      *   - TypeDefinition
  960.      *   - TypeExtension
  961.      *   - DirectiveDefinition
  962.      *
  963.      * TypeDefinition :
  964.      *   - ScalarTypeDefinition
  965.      *   - ObjectTypeDefinition
  966.      *   - InterfaceTypeDefinition
  967.      *   - UnionTypeDefinition
  968.      *   - EnumTypeDefinition
  969.      *   - InputObjectTypeDefinition
  970.      *
  971.      * @throws SyntaxError
  972.      */
  973.     private function parseTypeSystemDefinition() : TypeSystemDefinitionNode
  974.     {
  975.         // Many definitions begin with a description and require a lookahead.
  976.         $keywordToken $this->peekDescription()
  977.             ? $this->lexer->lookahead()
  978.             : $this->lexer->token;
  979.         if ($keywordToken->kind === Token::NAME) {
  980.             switch ($keywordToken->value) {
  981.                 case 'schema':
  982.                     return $this->parseSchemaDefinition();
  983.                 case 'scalar':
  984.                     return $this->parseScalarTypeDefinition();
  985.                 case 'type':
  986.                     return $this->parseObjectTypeDefinition();
  987.                 case 'interface':
  988.                     return $this->parseInterfaceTypeDefinition();
  989.                 case 'union':
  990.                     return $this->parseUnionTypeDefinition();
  991.                 case 'enum':
  992.                     return $this->parseEnumTypeDefinition();
  993.                 case 'input':
  994.                     return $this->parseInputObjectTypeDefinition();
  995.                 case 'extend':
  996.                     return $this->parseTypeExtension();
  997.                 case 'directive':
  998.                     return $this->parseDirectiveDefinition();
  999.             }
  1000.         }
  1001.         throw $this->unexpected($keywordToken);
  1002.     }
  1003.     private function peekDescription() : bool
  1004.     {
  1005.         return $this->peek(Token::STRING) || $this->peek(Token::BLOCK_STRING);
  1006.     }
  1007.     private function parseDescription() : ?StringValueNode
  1008.     {
  1009.         if ($this->peekDescription()) {
  1010.             return $this->parseStringLiteral();
  1011.         }
  1012.         return null;
  1013.     }
  1014.     /**
  1015.      * @throws SyntaxError
  1016.      */
  1017.     private function parseSchemaDefinition() : SchemaDefinitionNode
  1018.     {
  1019.         $start $this->lexer->token;
  1020.         $this->expectKeyword('schema');
  1021.         $directives $this->parseDirectives(true);
  1022.         $operationTypes $this->many(
  1023.             Token::BRACE_L,
  1024.             function () : OperationTypeDefinitionNode {
  1025.                 return $this->parseOperationTypeDefinition();
  1026.             },
  1027.             Token::BRACE_R
  1028.         );
  1029.         return new SchemaDefinitionNode([
  1030.             'directives'     => $directives,
  1031.             'operationTypes' => $operationTypes,
  1032.             'loc'            => $this->loc($start),
  1033.         ]);
  1034.     }
  1035.     /**
  1036.      * @throws SyntaxError
  1037.      */
  1038.     private function parseOperationTypeDefinition() : OperationTypeDefinitionNode
  1039.     {
  1040.         $start     $this->lexer->token;
  1041.         $operation $this->parseOperationType();
  1042.         $this->expect(Token::COLON);
  1043.         $type $this->parseNamedType();
  1044.         return new OperationTypeDefinitionNode([
  1045.             'operation' => $operation,
  1046.             'type'      => $type,
  1047.             'loc'       => $this->loc($start),
  1048.         ]);
  1049.     }
  1050.     /**
  1051.      * @throws SyntaxError
  1052.      */
  1053.     private function parseScalarTypeDefinition() : ScalarTypeDefinitionNode
  1054.     {
  1055.         $start       $this->lexer->token;
  1056.         $description $this->parseDescription();
  1057.         $this->expectKeyword('scalar');
  1058.         $name       $this->parseName();
  1059.         $directives $this->parseDirectives(true);
  1060.         return new ScalarTypeDefinitionNode([
  1061.             'name'        => $name,
  1062.             'directives'  => $directives,
  1063.             'loc'         => $this->loc($start),
  1064.             'description' => $description,
  1065.         ]);
  1066.     }
  1067.     /**
  1068.      * @throws SyntaxError
  1069.      */
  1070.     private function parseObjectTypeDefinition() : ObjectTypeDefinitionNode
  1071.     {
  1072.         $start       $this->lexer->token;
  1073.         $description $this->parseDescription();
  1074.         $this->expectKeyword('type');
  1075.         $name       $this->parseName();
  1076.         $interfaces $this->parseImplementsInterfaces();
  1077.         $directives $this->parseDirectives(true);
  1078.         $fields     $this->parseFieldsDefinition();
  1079.         return new ObjectTypeDefinitionNode([
  1080.             'name'        => $name,
  1081.             'interfaces'  => $interfaces,
  1082.             'directives'  => $directives,
  1083.             'fields'      => $fields,
  1084.             'loc'         => $this->loc($start),
  1085.             'description' => $description,
  1086.         ]);
  1087.     }
  1088.     /**
  1089.      * ImplementsInterfaces :
  1090.      *   - implements `&`? NamedType
  1091.      *   - ImplementsInterfaces & NamedType
  1092.      */
  1093.     private function parseImplementsInterfaces() : NodeList
  1094.     {
  1095.         $types = [];
  1096.         if ($this->expectOptionalKeyword('implements')) {
  1097.             // Optional leading ampersand
  1098.             $this->skip(Token::AMP);
  1099.             do {
  1100.                 $types[] = $this->parseNamedType();
  1101.             } while ($this->skip(Token::AMP) ||
  1102.                 // Legacy support for the SDL?
  1103.                 (($this->lexer->options['allowLegacySDLImplementsInterfaces'] ?? false) && $this->peek(Token::NAME))
  1104.             );
  1105.         }
  1106.         return new NodeList($types);
  1107.     }
  1108.     /**
  1109.      * @throws SyntaxError
  1110.      */
  1111.     private function parseFieldsDefinition() : NodeList
  1112.     {
  1113.         // Legacy support for the SDL?
  1114.         if (($this->lexer->options['allowLegacySDLEmptyFields'] ?? false)
  1115.             && $this->peek(Token::BRACE_L)
  1116.             && $this->lexer->lookahead()->kind === Token::BRACE_R
  1117.         ) {
  1118.             $this->lexer->advance();
  1119.             $this->lexer->advance();
  1120.             /** @phpstan-var NodeList<FieldDefinitionNode&Node> $nodeList */
  1121.             $nodeList = new NodeList([]);
  1122.         } else {
  1123.             /** @phpstan-var NodeList<FieldDefinitionNode&Node> $nodeList */
  1124.             $nodeList $this->peek(Token::BRACE_L)
  1125.                 ? $this->many(
  1126.                     Token::BRACE_L,
  1127.                     function () : FieldDefinitionNode {
  1128.                         return $this->parseFieldDefinition();
  1129.                     },
  1130.                     Token::BRACE_R
  1131.                 )
  1132.                 : new NodeList([]);
  1133.         }
  1134.         return $nodeList;
  1135.     }
  1136.     /**
  1137.      * @throws SyntaxError
  1138.      */
  1139.     private function parseFieldDefinition() : FieldDefinitionNode
  1140.     {
  1141.         $start       $this->lexer->token;
  1142.         $description $this->parseDescription();
  1143.         $name        $this->parseName();
  1144.         $args        $this->parseArgumentsDefinition();
  1145.         $this->expect(Token::COLON);
  1146.         $type       $this->parseTypeReference();
  1147.         $directives $this->parseDirectives(true);
  1148.         return new FieldDefinitionNode([
  1149.             'name'        => $name,
  1150.             'arguments'   => $args,
  1151.             'type'        => $type,
  1152.             'directives'  => $directives,
  1153.             'loc'         => $this->loc($start),
  1154.             'description' => $description,
  1155.         ]);
  1156.     }
  1157.     /**
  1158.      * @throws SyntaxError
  1159.      */
  1160.     private function parseArgumentsDefinition() : NodeList
  1161.     {
  1162.         /** @var NodeList<InputValueDefinitionNode&Node> $nodeList */
  1163.         $nodeList $this->peek(Token::PAREN_L)
  1164.             ? $this->many(
  1165.                 Token::PAREN_L,
  1166.                 function () : InputValueDefinitionNode {
  1167.                     return $this->parseInputValueDefinition();
  1168.                 },
  1169.                 Token::PAREN_R
  1170.             )
  1171.             : new NodeList([]);
  1172.         return $nodeList;
  1173.     }
  1174.     /**
  1175.      * @throws SyntaxError
  1176.      */
  1177.     private function parseInputValueDefinition() : InputValueDefinitionNode
  1178.     {
  1179.         $start       $this->lexer->token;
  1180.         $description $this->parseDescription();
  1181.         $name        $this->parseName();
  1182.         $this->expect(Token::COLON);
  1183.         $type         $this->parseTypeReference();
  1184.         $defaultValue null;
  1185.         if ($this->skip(Token::EQUALS)) {
  1186.             $defaultValue $this->parseConstValue();
  1187.         }
  1188.         $directives $this->parseDirectives(true);
  1189.         return new InputValueDefinitionNode([
  1190.             'name'         => $name,
  1191.             'type'         => $type,
  1192.             'defaultValue' => $defaultValue,
  1193.             'directives'   => $directives,
  1194.             'loc'          => $this->loc($start),
  1195.             'description'  => $description,
  1196.         ]);
  1197.     }
  1198.     /**
  1199.      * @throws SyntaxError
  1200.      */
  1201.     private function parseInterfaceTypeDefinition() : InterfaceTypeDefinitionNode
  1202.     {
  1203.         $start       $this->lexer->token;
  1204.         $description $this->parseDescription();
  1205.         $this->expectKeyword('interface');
  1206.         $name       $this->parseName();
  1207.         $interfaces $this->parseImplementsInterfaces();
  1208.         $directives $this->parseDirectives(true);
  1209.         $fields     $this->parseFieldsDefinition();
  1210.         return new InterfaceTypeDefinitionNode([
  1211.             'name'        => $name,
  1212.             'directives'  => $directives,
  1213.             'interfaces'  => $interfaces,
  1214.             'fields'      => $fields,
  1215.             'loc'         => $this->loc($start),
  1216.             'description' => $description,
  1217.         ]);
  1218.     }
  1219.     /**
  1220.      * UnionTypeDefinition :
  1221.      *   - Description? union Name Directives[Const]? UnionMemberTypes?
  1222.      *
  1223.      * @throws SyntaxError
  1224.      */
  1225.     private function parseUnionTypeDefinition() : UnionTypeDefinitionNode
  1226.     {
  1227.         $start       $this->lexer->token;
  1228.         $description $this->parseDescription();
  1229.         $this->expectKeyword('union');
  1230.         $name       $this->parseName();
  1231.         $directives $this->parseDirectives(true);
  1232.         $types      $this->parseUnionMemberTypes();
  1233.         return new UnionTypeDefinitionNode([
  1234.             'name'        => $name,
  1235.             'directives'  => $directives,
  1236.             'types'       => $types,
  1237.             'loc'         => $this->loc($start),
  1238.             'description' => $description,
  1239.         ]);
  1240.     }
  1241.     /**
  1242.      * UnionMemberTypes :
  1243.      *   - = `|`? NamedType
  1244.      *   - UnionMemberTypes | NamedType
  1245.      */
  1246.     private function parseUnionMemberTypes() : NodeList
  1247.     {
  1248.         $types = [];
  1249.         if ($this->skip(Token::EQUALS)) {
  1250.             // Optional leading pipe
  1251.             $this->skip(Token::PIPE);
  1252.             do {
  1253.                 $types[] = $this->parseNamedType();
  1254.             } while ($this->skip(Token::PIPE));
  1255.         }
  1256.         return new NodeList($types);
  1257.     }
  1258.     /**
  1259.      * @throws SyntaxError
  1260.      */
  1261.     private function parseEnumTypeDefinition() : EnumTypeDefinitionNode
  1262.     {
  1263.         $start       $this->lexer->token;
  1264.         $description $this->parseDescription();
  1265.         $this->expectKeyword('enum');
  1266.         $name       $this->parseName();
  1267.         $directives $this->parseDirectives(true);
  1268.         $values     $this->parseEnumValuesDefinition();
  1269.         return new EnumTypeDefinitionNode([
  1270.             'name'        => $name,
  1271.             'directives'  => $directives,
  1272.             'values'      => $values,
  1273.             'loc'         => $this->loc($start),
  1274.             'description' => $description,
  1275.         ]);
  1276.     }
  1277.     /**
  1278.      * @throws SyntaxError
  1279.      */
  1280.     private function parseEnumValuesDefinition() : NodeList
  1281.     {
  1282.         /** @var NodeList<EnumValueDefinitionNode&Node> $nodeList */
  1283.         $nodeList $this->peek(Token::BRACE_L)
  1284.             ? $this->many(
  1285.                 Token::BRACE_L,
  1286.                 function () : EnumValueDefinitionNode {
  1287.                     return $this->parseEnumValueDefinition();
  1288.                 },
  1289.                 Token::BRACE_R
  1290.             )
  1291.             : new NodeList([]);
  1292.         return $nodeList;
  1293.     }
  1294.     /**
  1295.      * @throws SyntaxError
  1296.      */
  1297.     private function parseEnumValueDefinition() : EnumValueDefinitionNode
  1298.     {
  1299.         $start       $this->lexer->token;
  1300.         $description $this->parseDescription();
  1301.         $name        $this->parseName();
  1302.         $directives  $this->parseDirectives(true);
  1303.         return new EnumValueDefinitionNode([
  1304.             'name'        => $name,
  1305.             'directives'  => $directives,
  1306.             'loc'         => $this->loc($start),
  1307.             'description' => $description,
  1308.         ]);
  1309.     }
  1310.     /**
  1311.      * @throws SyntaxError
  1312.      */
  1313.     private function parseInputObjectTypeDefinition() : InputObjectTypeDefinitionNode
  1314.     {
  1315.         $start       $this->lexer->token;
  1316.         $description $this->parseDescription();
  1317.         $this->expectKeyword('input');
  1318.         $name       $this->parseName();
  1319.         $directives $this->parseDirectives(true);
  1320.         $fields     $this->parseInputFieldsDefinition();
  1321.         return new InputObjectTypeDefinitionNode([
  1322.             'name'        => $name,
  1323.             'directives'  => $directives,
  1324.             'fields'      => $fields,
  1325.             'loc'         => $this->loc($start),
  1326.             'description' => $description,
  1327.         ]);
  1328.     }
  1329.     /**
  1330.      * @throws SyntaxError
  1331.      */
  1332.     private function parseInputFieldsDefinition() : NodeList
  1333.     {
  1334.         /** @var NodeList<InputValueDefinitionNode&Node> $nodeList */
  1335.         $nodeList $this->peek(Token::BRACE_L)
  1336.             ? $this->many(
  1337.                 Token::BRACE_L,
  1338.                 function () : InputValueDefinitionNode {
  1339.                     return $this->parseInputValueDefinition();
  1340.                 },
  1341.                 Token::BRACE_R
  1342.             )
  1343.             : new NodeList([]);
  1344.         return $nodeList;
  1345.     }
  1346.     /**
  1347.      * TypeExtension :
  1348.      *   - ScalarTypeExtension
  1349.      *   - ObjectTypeExtension
  1350.      *   - InterfaceTypeExtension
  1351.      *   - UnionTypeExtension
  1352.      *   - EnumTypeExtension
  1353.      *   - InputObjectTypeDefinition
  1354.      *
  1355.      * @throws SyntaxError
  1356.      */
  1357.     private function parseTypeExtension() : TypeExtensionNode
  1358.     {
  1359.         $keywordToken $this->lexer->lookahead();
  1360.         if ($keywordToken->kind === Token::NAME) {
  1361.             switch ($keywordToken->value) {
  1362.                 case 'schema':
  1363.                     return $this->parseSchemaTypeExtension();
  1364.                 case 'scalar':
  1365.                     return $this->parseScalarTypeExtension();
  1366.                 case 'type':
  1367.                     return $this->parseObjectTypeExtension();
  1368.                 case 'interface':
  1369.                     return $this->parseInterfaceTypeExtension();
  1370.                 case 'union':
  1371.                     return $this->parseUnionTypeExtension();
  1372.                 case 'enum':
  1373.                     return $this->parseEnumTypeExtension();
  1374.                 case 'input':
  1375.                     return $this->parseInputObjectTypeExtension();
  1376.             }
  1377.         }
  1378.         throw $this->unexpected($keywordToken);
  1379.     }
  1380.     /**
  1381.      * @throws SyntaxError
  1382.      */
  1383.     private function parseSchemaTypeExtension() : SchemaTypeExtensionNode
  1384.     {
  1385.         $start $this->lexer->token;
  1386.         $this->expectKeyword('extend');
  1387.         $this->expectKeyword('schema');
  1388.         $directives     $this->parseDirectives(true);
  1389.         $operationTypes $this->peek(Token::BRACE_L)
  1390.             ? $this->many(
  1391.                 Token::BRACE_L,
  1392.                 [$this'parseOperationTypeDefinition'],
  1393.                 Token::BRACE_R
  1394.             )
  1395.             : new NodeList([]);
  1396.         if (count($directives) === && count($operationTypes) === 0) {
  1397.             $this->unexpected();
  1398.         }
  1399.         return new SchemaTypeExtensionNode([
  1400.             'directives' => $directives,
  1401.             'operationTypes' => $operationTypes,
  1402.             'loc' => $this->loc($start),
  1403.         ]);
  1404.     }
  1405.     /**
  1406.      * @throws SyntaxError
  1407.      */
  1408.     private function parseScalarTypeExtension() : ScalarTypeExtensionNode
  1409.     {
  1410.         $start $this->lexer->token;
  1411.         $this->expectKeyword('extend');
  1412.         $this->expectKeyword('scalar');
  1413.         $name       $this->parseName();
  1414.         $directives $this->parseDirectives(true);
  1415.         if (count($directives) === 0) {
  1416.             throw $this->unexpected();
  1417.         }
  1418.         return new ScalarTypeExtensionNode([
  1419.             'name'       => $name,
  1420.             'directives' => $directives,
  1421.             'loc'        => $this->loc($start),
  1422.         ]);
  1423.     }
  1424.     /**
  1425.      * @throws SyntaxError
  1426.      */
  1427.     private function parseObjectTypeExtension() : ObjectTypeExtensionNode
  1428.     {
  1429.         $start $this->lexer->token;
  1430.         $this->expectKeyword('extend');
  1431.         $this->expectKeyword('type');
  1432.         $name       $this->parseName();
  1433.         $interfaces $this->parseImplementsInterfaces();
  1434.         $directives $this->parseDirectives(true);
  1435.         $fields     $this->parseFieldsDefinition();
  1436.         if (count($interfaces) === &&
  1437.             count($directives) === &&
  1438.             count($fields) === 0
  1439.         ) {
  1440.             throw $this->unexpected();
  1441.         }
  1442.         return new ObjectTypeExtensionNode([
  1443.             'name'       => $name,
  1444.             'interfaces' => $interfaces,
  1445.             'directives' => $directives,
  1446.             'fields'     => $fields,
  1447.             'loc'        => $this->loc($start),
  1448.         ]);
  1449.     }
  1450.     /**
  1451.      * @throws SyntaxError
  1452.      */
  1453.     private function parseInterfaceTypeExtension() : InterfaceTypeExtensionNode
  1454.     {
  1455.         $start $this->lexer->token;
  1456.         $this->expectKeyword('extend');
  1457.         $this->expectKeyword('interface');
  1458.         $name       $this->parseName();
  1459.         $interfaces $this->parseImplementsInterfaces();
  1460.         $directives $this->parseDirectives(true);
  1461.         $fields     $this->parseFieldsDefinition();
  1462.         if (count($interfaces) === 0
  1463.             && count($directives) === 0
  1464.             && count($fields) === 0
  1465.         ) {
  1466.             throw $this->unexpected();
  1467.         }
  1468.         return new InterfaceTypeExtensionNode([
  1469.             'name'       => $name,
  1470.             'directives' => $directives,
  1471.             'interfaces' => $interfaces,
  1472.             'fields'     => $fields,
  1473.             'loc'        => $this->loc($start),
  1474.         ]);
  1475.     }
  1476.     /**
  1477.      * UnionTypeExtension :
  1478.      *   - extend union Name Directives[Const]? UnionMemberTypes
  1479.      *   - extend union Name Directives[Const]
  1480.      *
  1481.      * @throws SyntaxError
  1482.      */
  1483.     private function parseUnionTypeExtension() : UnionTypeExtensionNode
  1484.     {
  1485.         $start $this->lexer->token;
  1486.         $this->expectKeyword('extend');
  1487.         $this->expectKeyword('union');
  1488.         $name       $this->parseName();
  1489.         $directives $this->parseDirectives(true);
  1490.         $types      $this->parseUnionMemberTypes();
  1491.         if (count($directives) === && count($types) === 0) {
  1492.             throw $this->unexpected();
  1493.         }
  1494.         return new UnionTypeExtensionNode([
  1495.             'name'       => $name,
  1496.             'directives' => $directives,
  1497.             'types'      => $types,
  1498.             'loc'        => $this->loc($start),
  1499.         ]);
  1500.     }
  1501.     /**
  1502.      * @throws SyntaxError
  1503.      */
  1504.     private function parseEnumTypeExtension() : EnumTypeExtensionNode
  1505.     {
  1506.         $start $this->lexer->token;
  1507.         $this->expectKeyword('extend');
  1508.         $this->expectKeyword('enum');
  1509.         $name       $this->parseName();
  1510.         $directives $this->parseDirectives(true);
  1511.         $values     $this->parseEnumValuesDefinition();
  1512.         if (count($directives) === &&
  1513.             count($values) === 0
  1514.         ) {
  1515.             throw $this->unexpected();
  1516.         }
  1517.         return new EnumTypeExtensionNode([
  1518.             'name'       => $name,
  1519.             'directives' => $directives,
  1520.             'values'     => $values,
  1521.             'loc'        => $this->loc($start),
  1522.         ]);
  1523.     }
  1524.     /**
  1525.      * @throws SyntaxError
  1526.      */
  1527.     private function parseInputObjectTypeExtension() : InputObjectTypeExtensionNode
  1528.     {
  1529.         $start $this->lexer->token;
  1530.         $this->expectKeyword('extend');
  1531.         $this->expectKeyword('input');
  1532.         $name       $this->parseName();
  1533.         $directives $this->parseDirectives(true);
  1534.         $fields     $this->parseInputFieldsDefinition();
  1535.         if (count($directives) === &&
  1536.             count($fields) === 0
  1537.         ) {
  1538.             throw $this->unexpected();
  1539.         }
  1540.         return new InputObjectTypeExtensionNode([
  1541.             'name'       => $name,
  1542.             'directives' => $directives,
  1543.             'fields'     => $fields,
  1544.             'loc'        => $this->loc($start),
  1545.         ]);
  1546.     }
  1547.     /**
  1548.      * DirectiveDefinition :
  1549.      *   - Description? directive @ Name ArgumentsDefinition? `repeatable`? on DirectiveLocations
  1550.      *
  1551.      * @throws SyntaxError
  1552.      */
  1553.     private function parseDirectiveDefinition() : DirectiveDefinitionNode
  1554.     {
  1555.         $start       $this->lexer->token;
  1556.         $description $this->parseDescription();
  1557.         $this->expectKeyword('directive');
  1558.         $this->expect(Token::AT);
  1559.         $name       $this->parseName();
  1560.         $args       $this->parseArgumentsDefinition();
  1561.         $repeatable $this->expectOptionalKeyword('repeatable');
  1562.         $this->expectKeyword('on');
  1563.         $locations $this->parseDirectiveLocations();
  1564.         return new DirectiveDefinitionNode([
  1565.             'name'        => $name,
  1566.             'description' => $description,
  1567.             'arguments'   => $args,
  1568.             'repeatable'  => $repeatable,
  1569.             'locations'   => $locations,
  1570.             'loc'         => $this->loc($start),
  1571.         ]);
  1572.     }
  1573.     /**
  1574.      * @throws SyntaxError
  1575.      */
  1576.     private function parseDirectiveLocations() : NodeList
  1577.     {
  1578.         // Optional leading pipe
  1579.         $this->skip(Token::PIPE);
  1580.         $locations = [];
  1581.         do {
  1582.             $locations[] = $this->parseDirectiveLocation();
  1583.         } while ($this->skip(Token::PIPE));
  1584.         return new NodeList($locations);
  1585.     }
  1586.     /**
  1587.      * @throws SyntaxError
  1588.      */
  1589.     private function parseDirectiveLocation() : NameNode
  1590.     {
  1591.         $start $this->lexer->token;
  1592.         $name  $this->parseName();
  1593.         if (DirectiveLocation::has($name->value)) {
  1594.             return $name;
  1595.         }
  1596.         throw $this->unexpected($start);
  1597.     }
  1598. }