/**
 * Copyright 2019-present, GraphQL Foundation
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

%require "3"

%skeleton "lalr1.cc"

%defines
%define parser_class_name {GraphQLParserImpl}

%define api.token.prefix {TOK_}

%define parse.error verbose

%code requires
{
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <sstream>
#include <string>

#include "Ast.h"

using facebook::graphql::ast::Node;
using facebook::graphql::ast::Name;
using facebook::graphql::ast::Definition;
using facebook::graphql::ast::Document;
using facebook::graphql::ast::OperationDefinition;
using facebook::graphql::ast::VariableDefinition;
using facebook::graphql::ast::Variable;
using facebook::graphql::ast::SelectionSet;
using facebook::graphql::ast::Selection;
using facebook::graphql::ast::Field;
using facebook::graphql::ast::Argument;
using facebook::graphql::ast::FragmentSpread;
using facebook::graphql::ast::InlineFragment;
using facebook::graphql::ast::FragmentDefinition;
using facebook::graphql::ast::Value;
using facebook::graphql::ast::IntValue;
using facebook::graphql::ast::FloatValue;
using facebook::graphql::ast::StringValue;
using facebook::graphql::ast::BooleanValue;
using facebook::graphql::ast::NullValue;
using facebook::graphql::ast::EnumValue;
using facebook::graphql::ast::ListValue;
using facebook::graphql::ast::ObjectValue;
using facebook::graphql::ast::ObjectField;
using facebook::graphql::ast::Directive;
using facebook::graphql::ast::Type;
using facebook::graphql::ast::NamedType;
using facebook::graphql::ast::ListType;
using facebook::graphql::ast::NonNullType;

// Experimental schema support.
using facebook::graphql::ast::SchemaDefinition;
using facebook::graphql::ast::ScalarTypeDefinition;
using facebook::graphql::ast::ObjectTypeDefinition;
using facebook::graphql::ast::InterfaceTypeDefinition;
using facebook::graphql::ast::UnionTypeDefinition;
using facebook::graphql::ast::EnumTypeDefinition;
using facebook::graphql::ast::InputObjectTypeDefinition;
using facebook::graphql::ast::TypeExtensionDefinition;
using facebook::graphql::ast::DirectiveDefinition;
using facebook::graphql::ast::SchemaDefinition;
using facebook::graphql::ast::OperationTypeDefinition;
using facebook::graphql::ast::ScalarTypeDefinition;
using facebook::graphql::ast::ObjectTypeDefinition;
using facebook::graphql::ast::FieldDefinition;
using facebook::graphql::ast::InputValueDefinition;
using facebook::graphql::ast::InterfaceTypeDefinition;
using facebook::graphql::ast::UnionTypeDefinition;
using facebook::graphql::ast::EnumTypeDefinition;
using facebook::graphql::ast::EnumValueDefinition;
using facebook::graphql::ast::InputObjectTypeDefinition;
using facebook::graphql::ast::TypeExtensionDefinition;
using facebook::graphql::ast::DirectiveDefinition;

union yystype {                                         \
    const char *str;                                    \
    const char *heapStr;                                \
    Name *name;                                         \
    Definition *definition;                             \
    Document *document;                                 \
    OperationDefinition *operationDefinition;           \
    VariableDefinition *variableDefinition;             \
    Variable *variable;                                 \
    SelectionSet *selectionSet;                     \
    Selection *selection;                           \
    Field *field;                                   \
    Argument *argument;                             \
    FragmentSpread *fragmentSpread;                 \
    InlineFragment *inlineFragment;                 \
    FragmentDefinition *fragmentDefinition;         \
    Value *value;                                   \
    IntValue *intValue;                             \
    FloatValue *floatValue;                         \
    StringValue *stringValue;                       \
    BooleanValue *booleanValue;                     \
    NullValue *nullValue;                           \
    EnumValue *enumValue;                           \
    ListValue *arrayValue;                          \
    ObjectValue *objectValue;                       \
    ObjectField *objectField;                       \
    Directive *directive;                           \
    Type *type;                                     \
    NamedType *namedType;                           \
    ListType *listType;                             \
    NonNullType *nonNullType;                       \
    \
    std::vector<std::unique_ptr<Definition>> *definitionList;       \
    std::vector<std::unique_ptr<VariableDefinition>> *variableDefinitionList; \
    std::vector<std::unique_ptr<Selection>> *selectionList;         \
    std::vector<std::unique_ptr<Field>> *fieldList;                 \
    std::vector<std::unique_ptr<Argument>> *argumentList;           \
    std::vector<std::unique_ptr<Value>> *valueList;                 \
    std::vector<std::unique_ptr<ObjectField>> *objectFieldList;     \
    std::vector<std::unique_ptr<Directive>> *directiveList;         \
                                                                    \
    SchemaDefinition *schemaDefinition;                             \
    ScalarTypeDefinition *scalarTypeDefinition;                     \
    ObjectTypeDefinition *objectTypeDefinition;                     \
    InterfaceTypeDefinition *interfaceTypeDefinition;               \
    UnionTypeDefinition *unionTypeDefinition;                       \
    EnumTypeDefinition *enumTypeDefinition;                         \
    InputObjectTypeDefinition *inputObjectTypeDefinition;           \
    TypeExtensionDefinition *typeExtensionDefinition;               \
    DirectiveDefinition *directiveDefinition;                       \
    OperationTypeDefinition *operationTypeDefinition;               \
    InputValueDefinition *inputValueDefinition;                     \
    FieldDefinition *fieldDefinition;                               \
    EnumValueDefinition *enumValueDefinition;                       \
    \
    std::vector<std::unique_ptr<OperationTypeDefinition>> *operationTypeDefinitionList; \
    std::vector<std::unique_ptr<NamedType>> *typeNameList;          \
    std::vector<std::unique_ptr<InputValueDefinition>> *inputValueDefinitionList; \
    std::vector<std::unique_ptr<FieldDefinition>> *fieldDefinitionList; \
    std::vector<std::unique_ptr<Name>> *nameList;          \
    std::vector<std::unique_ptr<EnumValueDefinition>> *enumValueDefinitionList; \
};

#define YYSTYPE union yystype
#define YYLTYPE yy::location

}

%lex-param { void *scanner }
%parse-param { bool enableSchema } { Node **outAST } { const char **outError } { void *scanner }

%locations

%code
{
#include "lexer.h"
#include "syntaxdefs.h"
}

%token EOF 0
%token <str> DIRECTIVE "directive"
%token <str> ENUM "enum"
%token <str> EXTEND "extend"
%token <str> FALSE "false"
%token <str> FRAGMENT "fragment"
%token <str> IMPLEMENTS "implements"
%token <str> INPUT "input"
%token <str> INTERFACE "interface"
%token <str> MUTATION "mutation"
%token <str> NULL "null"
%token <str> QUERY "query"
%token <str> ON "on"
%token <str> SCALAR "scalar"
%token <str> SCHEMA "schema"
%token <str> SUBSCRIPTION "subscription"
%token <str> TRUE "true"
%token <str> TYPE "type"
%token <str> UNION "union"
%token BANG "!"
%token LPAREN "("
%token RPAREN ")"
%token ELLIPSIS "..."
%token COLON ":"
%token EQUAL "="
%token AT "@"
%token LBRACKET "["
%token RBRACKET "]"
%token LBRACE "{"
%token PIPE "|"
%token RBRACE "}"

%token <str> VARIABLE
%token <str> INTEGER
%token <str> FLOAT
%token <str> STRING
%token <str> IDENTIFIER

%type <variable> variable
%type <intValue> int_value
%type <floatValue> float_value
%type <stringValue> string_value

%type <document> start
%type <document> document
%type <name> fragment_name
%type <name> name
%type <name> name_opt

%type <definitionList> definition_list
%type <definition> definition
%type <definition> schema_gate

%type <operationDefinition> operation_definition
%type <variableDefinitionList> variable_definitions
%type <variableDefinitionList> variable_definition_list
%type <variableDefinition> variable_definition
%type <value> default_value_opt
%type <value> default_value
%type <selectionSet> selection_set
%type <selectionSet> selection_set_opt
%type <selectionList> selection_list
%type <selection> selection
%type <field> field
%type <argumentList> arguments_opt
%type <argumentList> arguments
%type <argumentList> argument_list
%type <argument> argument

%type <fragmentSpread> fragment_spread
%type <inlineFragment> inline_fragment
%type <fragmentDefinition> fragment_definition
%type <namedType> type_condition

%type <value> value
%type <value> value_const
%type <booleanValue> boolean_value
%type <nullValue> null_value
%type <enumValue> enum_value
%type <arrayValue> list_value
%type <arrayValue> list_value_const
%type <valueList> value_list
%type <valueList> value_const_list
%type <objectValue> object_value
%type <objectValue> object_value_const
%type <objectFieldList> object_field_list
%type <objectFieldList> object_field_const_list
%type <objectField> object_field
%type <objectField> object_field_const


%type <directiveList> directives
%type <directiveList> directives_opt
%type <directiveList> directive_list
%type <directive> directive

%type <type> type
%type <namedType> type_name
%type <listType> list_type
%type <nonNullType> non_null_type

%type <heapStr> operation_type

%type <schemaDefinition> schema_definition;
%type <scalarTypeDefinition> scalar_type_definition;
%type <objectTypeDefinition> object_type_definition;
%type <interfaceTypeDefinition> interface_type_definition;
%type <unionTypeDefinition> union_type_definition;
%type <enumTypeDefinition> enum_type_definition;
%type <inputObjectTypeDefinition> input_object_type_definition;
%type <typeExtensionDefinition> type_extension_definition;
%type <directiveDefinition> directive_definition;
%type <operationTypeDefinition> operation_type_definition;
%type <operationTypeDefinitionList> operation_type_definition_list;
%type <typeNameList> type_name_list;
%type <typeNameList> implements_interfaces_opt;
%type <typeNameList> union_members;
%type <fieldDefinition> field_definition;
%type <fieldDefinitionList> field_definition_list;
%type <inputValueDefinitionList> arguments_definition_opt; 
%type <inputValueDefinitionList> arguments_definition;
%type <inputValueDefinitionList> input_value_definition_list;
%type <inputValueDefinition> input_value_definition;
%type <enumValueDefinition> enum_value_definition;
%type <nameList> directive_locations;
%type <enumValueDefinitionList> enum_value_definition_list;

%destructor { } <str>
%destructor { free((void *)$$); } <heapStr>
%destructor { } <document>  /* we steal it and put it in outAST, don't free! */
%destructor { delete $$; } <*>

%printer { yyoutput << $$; } <str>

%%

start:          document  { *outAST = $1; }
        ;

/* All of the non-identifier tokens are to accommodate various flavors
   of name that don't include those tokens. */
fragment_name:  DIRECTIVE { $$ = new Name(@1, strdup($1)); }
        |       ENUM { $$ = new Name(@1, strdup($1)); }
        |       EXTEND { $$ = new Name(@1, strdup($1)); }
        |       FALSE { $$ = new Name(@1, strdup($1)); }
        |       FRAGMENT { $$ = new Name(@1, strdup($1)); }
        |       IDENTIFIER { $$ = new Name(@1, strdup($1)); }
        |       IMPLEMENTS { $$ = new Name(@1, strdup($1)); }
        |       INPUT { $$ = new Name(@1, strdup($1)); }
        |       INTERFACE { $$ = new Name(@1, strdup($1)); }
        |       MUTATION { $$ = new Name(@1, strdup($1)); }
        |       NULL { $$ = new Name(@1, strdup($1)); }
        |       QUERY { $$ = new Name(@1, strdup($1)); }
        |       SCALAR { $$ = new Name(@1, strdup($1)); }
        |       SCHEMA { $$ = new Name(@1, strdup($1)); }
        |       SUBSCRIPTION { $$ = new Name(@1, strdup($1)); }
        |       TRUE { $$ = new Name(@1, strdup($1)); }
        |       TYPE { $$ = new Name(@1, strdup($1)); }
        |       UNION { $$ = new Name(@1, strdup($1)); }
        ;

name: fragment_name
        |       ON { $$ = new Name(@1, strdup($1)); }
        ;

name_opt:
                %empty {$$ = nullptr;}
        |       name
        ;

/* 2.2 Document */

document:       definition_list { $$ = new Document(@$, $1); }
        ;
                
definition_list:definition { $$ = new std::vector<std::unique_ptr<Definition>>(); $$->emplace_back($1); }
        |       definition_list definition { $1->emplace_back($2); $$ = $1; }
        ;

definition:     operation_definition { $$ = static_cast<Definition *>($1); }
        |       fragment_definition { $$ = static_cast<Definition *>($1); }
        |       schema_gate {
            if (!enableSchema) {
              error(@$, "schema support disabled");
              // %destructor doesn't work with YYERROR. See
              // https://www.gnu.org/software/bison/manual/html_node/Destructor-Decl.html
              delete $$;
              YYERROR;
            }
            $$ = static_cast<Definition *>($1);
          }
        ;

schema_gate:    schema_definition { $$ = static_cast<Definition *>($1); }
        |       scalar_type_definition { $$ = static_cast<Definition *>($1); }
        |       object_type_definition { $$ = static_cast<Definition *>($1); }
        |       interface_type_definition { $$ = static_cast<Definition *>($1); }
        |       union_type_definition { $$ = static_cast<Definition *>($1); }
        |       enum_type_definition { $$ = static_cast<Definition *>($1); }
        |       input_object_type_definition { $$ = static_cast<Definition *>($1); }
        |       type_extension_definition { $$ = static_cast<Definition *>($1); }
        |       directive_definition { $$ = static_cast<Definition *>($1); }
        ;


/* 2.2.1 Operations */
operation_definition:
                selection_set { $$ = new OperationDefinition(@$, strdup("query"), nullptr, nullptr, nullptr, $1); }
        |       operation_type name_opt selection_set { $$ = new OperationDefinition(@$, $1, $2, nullptr, nullptr, $3); }
        |       operation_type name_opt variable_definitions selection_set { $$ = new OperationDefinition(@$, $1, $2, $3, nullptr, $4); }
        |       operation_type name_opt directives selection_set { $$ = new OperationDefinition(@$, $1, $2, nullptr, $3, $4); }
        |       operation_type name_opt variable_definitions directives selection_set { $$ = new OperationDefinition(@$, $1, $2, $3, $4, $5); }
        ;

operation_type: QUERY { $$ = strdup($1); }
        |       MUTATION { $$ = strdup($1); }
        |       SUBSCRIPTION { $$ = strdup($1); }
        ;

variable_definitions:
                "(" variable_definition_list ")" { $$ = $2; }
        ;

variable_definition_list:
                variable_definition { $$ = new std::vector<std::unique_ptr<VariableDefinition>>(); $$->emplace_back($1); }
        |       variable_definition_list variable_definition { $1->emplace_back($2); $$ = $1; }
        ;

variable:       VARIABLE { $$ = new Variable(@$, new Name(@1, strdup($1))); }
        ;

variable_definition:
                variable ":" type default_value_opt { $$ = new VariableDefinition(@$, $1, $3, $4); }
        ;

default_value_opt:
                %empty { $$ = nullptr; }
        |       default_value
        ;

default_value:  "=" value_const { $$ = $2; }
        ;

selection_set:
                  "{" selection_list "}" { $$ = new SelectionSet(@$, $2); }
        ;

selection_set_opt:
                %empty { $$ = nullptr; }
        |       selection_set
        ;
selection_list: selection { $$ = new std::vector<std::unique_ptr<Selection>>(); $$->emplace_back($1); }
        |       selection_list selection { $1->emplace_back($2); $$ = $1; }
        ;

selection:      field { $$ = static_cast<Selection *>($1); }
        |       fragment_spread { $$ = static_cast<Selection *>($1); }
        |       inline_fragment { $$ = static_cast<Selection *>($1); }
        ;

field:          name arguments_opt directives_opt selection_set_opt { $$ = new Field(@$, nullptr, $1, $2, $3, $4); }
        |       name ":" name arguments_opt directives_opt selection_set_opt { $$ = new Field(@$, $1, $3, $4, $5, $6); }
        ;

arguments:      "(" argument_list ")" { $$ = $2; }
        ;

arguments_opt:  %empty { $$ = nullptr; }
        |       arguments { $$ = $1; }
        ;

argument_list:  argument { $$ = new std::vector<std::unique_ptr<Argument>>(); $$->emplace_back($1); }
        |       argument_list argument { $1->emplace_back($2); $$ = $1; }
        ;

argument:       name ":" value { $$ = new Argument(@$, $1, $3); }
        ;

/* 2.2.6 Fragments */
fragment_spread:
                "..." fragment_name directives_opt { $$ = new FragmentSpread(@$, $2, $3); }
        ;

inline_fragment:
                "..." "on" type_condition directives_opt selection_set { $$ = new InlineFragment(@$, $3, $4, $5); }
        |       "..." directives_opt selection_set { $$ = new InlineFragment(@$, nullptr, $2, $3); }
        ;

fragment_definition:
                "fragment" fragment_name "on" type_condition directives_opt selection_set { $$ = new FragmentDefinition(@$, $2, $4, $5, $6); }
        ;

type_condition: type_name
        ;

/* 2.2.7 Input Values */
value:          variable { $$ = static_cast<Value *>($1); }
        |       int_value { $$ = static_cast<Value *>($1); }
        |       float_value { $$ = static_cast<Value *>($1); }
        |       string_value { $$ = static_cast<Value *>($1); }
        |       boolean_value { $$ = static_cast<Value *>($1); }
        |       null_value { $$ = static_cast<Value *>($1); }
        |       enum_value { $$ = static_cast<Value *>($1); }
        |       list_value { $$ = static_cast<Value *>($1); }
        |       object_value { $$ = static_cast<Value *>($1); }
        ;

int_value:      INTEGER { $$ = new IntValue(@$, strdup($1)); }
        ;

float_value:    FLOAT { $$ = new FloatValue(@$, strdup($1)); }
        ;

string_value:   STRING { $$ = new StringValue(@$, strdup($1)); }
        ;

value_const:    int_value { $$ = static_cast<Value *>($1); }
        |       float_value { $$ = static_cast<Value *>($1); }
        |       string_value { $$ = static_cast<Value *>($1); }
        |       boolean_value { $$ = static_cast<Value *>($1); }
        |       null_value { $$ = static_cast<Value *>($1); }
        |       enum_value { $$ = static_cast<Value *>($1); }
        |       list_value_const { $$ = static_cast<Value *>($1); }
        |       object_value_const { $$ = static_cast<Value *>($1); }
        ;

boolean_value:  TRUE { $$ = new BooleanValue(@$, true); }
        |       FALSE { $$ = new BooleanValue(@$, false); }
        ;

null_value:     NULL { $$ = new NullValue(@$); }
        ;

enum_value:     DIRECTIVE { $$ = new EnumValue(@$, strdup($1)); }
        |       ENUM { $$ = new EnumValue(@$, strdup($1)); }
        |       EXTEND { $$ = new EnumValue(@$, strdup($1)); }
        |       FRAGMENT { $$ = new EnumValue(@$, strdup($1)); }
        |       IDENTIFIER { $$ = new EnumValue(@$, strdup($1)); }
        |       IMPLEMENTS { $$ = new EnumValue(@$, strdup($1)); }
        |       INPUT { $$ = new EnumValue(@$, strdup($1)); }
        |       INTERFACE { $$ = new EnumValue(@$, strdup($1)); }
        |       MUTATION { $$ = new EnumValue(@$, strdup($1)); }
        |       ON { $$ = new EnumValue(@$, strdup($1)); }
        |       QUERY { $$ = new EnumValue(@$, strdup($1)); }
        |       SCALAR { $$ = new EnumValue(@$, strdup($1)); }
        |       SCHEMA { $$ = new EnumValue(@$, strdup($1)); }
        |       SUBSCRIPTION { $$ = new EnumValue(@$, strdup($1)); }
        |       TYPE { $$ = new EnumValue(@$, strdup($1)); }
        |       UNION { $$ = new EnumValue(@$, strdup($1)); }
        ;

/* 2.2.7.6 List Value */

/* REVIEW: the empty case is inefficient; consider implementing
   ListValue manually. Don't forget to also do list_value_const. */
list_value:    "[" "]" { $$ = new ListValue(@$, new std::vector<std::unique_ptr<Value>>()); }
        |       "[" value_list "]" { $$ = new ListValue(@$, $2); }
        ;

value_list:     value { $$ = new std::vector<std::unique_ptr<Value>>(); $$->emplace_back($1); }
        |       value_list value { $1->emplace_back($2); $$ = $1; }
        ;

list_value_const:
                "[" "]" { $$ = new ListValue(@$, new std::vector<std::unique_ptr<Value>>()); }
        |       "[" value_const_list "]" { $$ = new ListValue(@$, $2); }
        ;

value_const_list:
                value_const { $$ = new std::vector<std::unique_ptr<Value>>(); $$->emplace_back($1); }
        |       value_const_list value_const { $1->emplace_back($2); $$ = $1; }
        ;

/* 2.2.7.7 Object Value */
/* REVIEW: Inefficient, like ListValue. */
object_value:   "{" "}" { $$ = new ObjectValue(@$, new std::vector<std::unique_ptr<ObjectField>>()); }
        |       "{" object_field_list "}" { $$ = new ObjectValue(@$, $2); }
        ;

object_field_list:
                object_field { $$ = new std::vector<std::unique_ptr<ObjectField>>(); $$->emplace_back($1); }
        |       object_field_list object_field { $1->emplace_back($2); $$ = $1; }
        ;

object_field:   name ":" value { $$ = new ObjectField(@$, $1, $3); }
        ;

object_value_const:
                "{" "}" { $$ = new ObjectValue(@$, new std::vector<std::unique_ptr<ObjectField>>()); }
        |       "{" object_field_const_list "}" { $$ = new ObjectValue(@$, $2); }
        ;

object_field_const_list:
                object_field_const { $$ = new std::vector<std::unique_ptr<ObjectField>>(); $$->emplace_back($1); }
        |       object_field_const_list object_field_const { $1->emplace_back($2); $$ = $1; }
        ;

object_field_const: name ":" value_const { $$ = new ObjectField(@$, $1, $3); }
        ;

/* 2.2.10 Directives */

directives:     directive_list
        ;

directives_opt: %empty { $$ = nullptr; }
        |       directives
        ;

directive_list: directive { $$ = new std::vector<std::unique_ptr<Directive>>(); $$->emplace_back($1); }
        |       directive_list directive { $1->emplace_back($2); $$ = $1; }
        ;

directive:      "@" name arguments_opt { $$ = new Directive(@$, $2, $3); }
        ;

/* 2.2.9 Types */

type:           type_name { $$ = static_cast<Type *>($1); }
        |       list_type { $$ = static_cast<Type *>($1); }
        |       non_null_type { $$ = static_cast<Type *>($1); }
        ;

type_name:      name { $$ = new NamedType(@$, $1); }
        ;

list_type:      "[" type "]" { $$ = new ListType(@$, $2); }
        ;

non_null_type:  type_name "!" { $$ = new NonNullType(@$, $1); }
        |       list_type "!" { $$ = new NonNullType(@$, $1); }
        ;

/* Experimental schema parsing support. */

schema_definition: SCHEMA directives_opt "{" operation_type_definition_list "}" { $$ = new SchemaDefinition(@$, $2, $4); }
                ;

operation_type_definition_list:
                operation_type_definition { $$ = new std::vector<std::unique_ptr<OperationTypeDefinition>>(); $$->emplace_back($1); }
        |       operation_type_definition_list operation_type_definition { $1->emplace_back($2); $$ = $1; }
        ;

operation_type_definition:
                operation_type ":" type_name { $$ = new OperationTypeDefinition(@$, $1, $3); }
                ;

scalar_type_definition: SCALAR name directives_opt { $$ = new ScalarTypeDefinition(@$, $2, $3); }
                ;

object_type_definition: TYPE name implements_interfaces_opt directives_opt "{" field_definition_list "}" { $$ = new ObjectTypeDefinition(@$, $2, $3, $4, $6); }
                ;

implements_interfaces_opt: %empty { $$ = nullptr; }
        |       IMPLEMENTS type_name_list { $$ = $2; }
                ;

type_name_list: type_name { $$ = new std::vector<std::unique_ptr<NamedType>>(); $$->emplace_back($1); }
        |       type_name_list type_name { $1->emplace_back($2); $$ = $1; }
                ;

field_definition: name arguments_definition_opt ":" type directives_opt { $$ = new FieldDefinition(@$, $1, $2, $4, $5); }
                ;

field_definition_list:
                        field_definition { $$ = new std::vector<std::unique_ptr<FieldDefinition>>(); $$->emplace_back($1); }
        |       field_definition_list field_definition { $1->emplace_back($2); $$ = $1; }
        ;

arguments_definition_opt: %empty { $$ = nullptr; }
        |       arguments_definition { $$ = $1; }
                ;

arguments_definition: "(" input_value_definition_list ")" { $$ = $2; }
                ;

input_value_definition_list: input_value_definition { $$ = new std::vector<std::unique_ptr<InputValueDefinition>>(); $$->emplace_back($1); }
        |       input_value_definition_list input_value_definition { $1->emplace_back($2); $$ = $1; }
                ;

input_value_definition: name ":" type default_value_opt directives_opt { $$ = new InputValueDefinition(@$, $1, $3, $4, $5); }

interface_type_definition: INTERFACE name directives_opt "{" field_definition_list "}" { $$ = new InterfaceTypeDefinition(@$, $2, $3, $5); }
                ;

union_type_definition: UNION name directives_opt "=" union_members { $$ = new UnionTypeDefinition(@$, $2, $3, $5); }
                ;

union_members:  type_name { $$ = new std::vector<std::unique_ptr<NamedType>>();  $$->emplace_back($1); }
        |       union_members "|" type_name { $1->emplace_back($3); $$ = $1; }
        ;

enum_type_definition: ENUM name directives_opt "{" enum_value_definition_list "}" { $$ = new EnumTypeDefinition(@$, $2, $3, $5); }
                ;

enum_value_definition: name directives_opt { $$ = new EnumValueDefinition(@$, $1, $2); }
                ;

enum_value_definition_list:
                enum_value_definition { $$ = new std::vector<std::unique_ptr<EnumValueDefinition>>(); $$->emplace_back($1); }
        |       enum_value_definition_list enum_value_definition { $1->emplace_back($2); $$ = $1; }
        ;

input_object_type_definition: INPUT name directives_opt "{" input_value_definition_list "}" { $$ = new InputObjectTypeDefinition(@$, $2, $3, $5); }
                ;

type_extension_definition: EXTEND object_type_definition { $$ = new TypeExtensionDefinition(@$, $2); }
                ;

directive_definition: DIRECTIVE "@" name arguments_definition_opt ON directive_locations { $$ = new DirectiveDefinition(@$, $3, $4, $6); }
                ;

directive_locations:
                name { $$ = new std::vector<std::unique_ptr<Name>>(); $$->emplace_back($1); }
        |       directive_locations "|" name { $1->emplace_back($3); $$ = $1; }
        ;

%%

void yy::GraphQLParserImpl::error(const yy::location &loc, const std::string &str) {
  std::ostringstream out;
  out << loc << ": " << str;
  if (outError) {
    *outError = strdup(out.str().c_str());
  }
}

/* Workaround for syntax_error ctor being marked inline, which causes link
   errors if used from lexer.lpp. */
yy::GraphQLParserImpl::syntax_error make_error(const yy::location &loc, const std::string &str) {
  return yy::GraphQLParserImpl::syntax_error(loc, str);
}
