/** * 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 #include #include #include #include #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> *definitionList; \ std::vector> *variableDefinitionList; \ std::vector> *selectionList; \ std::vector> *fieldList; \ std::vector> *argumentList; \ std::vector> *valueList; \ std::vector> *objectFieldList; \ std::vector> *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> *operationTypeDefinitionList; \ std::vector> *typeNameList; \ std::vector> *inputValueDefinitionList; \ std::vector> *fieldDefinitionList; \ std::vector> *nameList; \ std::vector> *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 DIRECTIVE "directive" %token ENUM "enum" %token EXTEND "extend" %token FALSE "false" %token FRAGMENT "fragment" %token IMPLEMENTS "implements" %token INPUT "input" %token INTERFACE "interface" %token MUTATION "mutation" %token NULL "null" %token QUERY "query" %token ON "on" %token SCALAR "scalar" %token SCHEMA "schema" %token SUBSCRIPTION "subscription" %token TRUE "true" %token TYPE "type" %token 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 VARIABLE %token INTEGER %token FLOAT %token STRING %token IDENTIFIER %type variable %type int_value %type float_value %type string_value %type start %type document %type fragment_name %type name %type name_opt %type definition_list %type definition %type schema_gate %type operation_definition %type variable_definitions %type variable_definition_list %type variable_definition %type default_value_opt %type default_value %type selection_set %type selection_set_opt %type selection_list %type selection %type field %type arguments_opt %type arguments %type argument_list %type argument %type fragment_spread %type inline_fragment %type fragment_definition %type type_condition %type value %type value_const %type boolean_value %type null_value %type enum_value %type list_value %type list_value_const %type value_list %type value_const_list %type object_value %type object_value_const %type object_field_list %type object_field_const_list %type object_field %type object_field_const %type directives %type directives_opt %type directive_list %type directive %type type %type type_name %type list_type %type non_null_type %type operation_type %type schema_definition; %type scalar_type_definition; %type object_type_definition; %type interface_type_definition; %type union_type_definition; %type enum_type_definition; %type input_object_type_definition; %type type_extension_definition; %type directive_definition; %type operation_type_definition; %type operation_type_definition_list; %type type_name_list; %type implements_interfaces_opt; %type union_members; %type field_definition; %type field_definition_list; %type arguments_definition_opt; %type arguments_definition; %type input_value_definition_list; %type input_value_definition; %type enum_value_definition; %type directive_locations; %type enum_value_definition_list; %destructor { } %destructor { free((void *)$$); } %destructor { } /* we steal it and put it in outAST, don't free! */ %destructor { delete $$; } <*> %printer { yyoutput << $$; } %% 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>(); $$->emplace_back($1); } | definition_list definition { $1->emplace_back($2); $$ = $1; } ; definition: operation_definition { $$ = static_cast($1); } | fragment_definition { $$ = static_cast($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($1); } ; schema_gate: schema_definition { $$ = static_cast($1); } | scalar_type_definition { $$ = static_cast($1); } | object_type_definition { $$ = static_cast($1); } | interface_type_definition { $$ = static_cast($1); } | union_type_definition { $$ = static_cast($1); } | enum_type_definition { $$ = static_cast($1); } | input_object_type_definition { $$ = static_cast($1); } | type_extension_definition { $$ = static_cast($1); } | directive_definition { $$ = static_cast($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>(); $$->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>(); $$->emplace_back($1); } | selection_list selection { $1->emplace_back($2); $$ = $1; } ; selection: field { $$ = static_cast($1); } | fragment_spread { $$ = static_cast($1); } | inline_fragment { $$ = static_cast($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>(); $$->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($1); } | int_value { $$ = static_cast($1); } | float_value { $$ = static_cast($1); } | string_value { $$ = static_cast($1); } | boolean_value { $$ = static_cast($1); } | null_value { $$ = static_cast($1); } | enum_value { $$ = static_cast($1); } | list_value { $$ = static_cast($1); } | object_value { $$ = static_cast($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($1); } | float_value { $$ = static_cast($1); } | string_value { $$ = static_cast($1); } | boolean_value { $$ = static_cast($1); } | null_value { $$ = static_cast($1); } | enum_value { $$ = static_cast($1); } | list_value_const { $$ = static_cast($1); } | object_value_const { $$ = static_cast($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>()); } | "[" value_list "]" { $$ = new ListValue(@$, $2); } ; value_list: value { $$ = new std::vector>(); $$->emplace_back($1); } | value_list value { $1->emplace_back($2); $$ = $1; } ; list_value_const: "[" "]" { $$ = new ListValue(@$, new std::vector>()); } | "[" value_const_list "]" { $$ = new ListValue(@$, $2); } ; value_const_list: value_const { $$ = new std::vector>(); $$->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>()); } | "{" object_field_list "}" { $$ = new ObjectValue(@$, $2); } ; object_field_list: object_field { $$ = new std::vector>(); $$->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>()); } | "{" object_field_const_list "}" { $$ = new ObjectValue(@$, $2); } ; object_field_const_list: object_field_const { $$ = new std::vector>(); $$->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>(); $$->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($1); } | list_type { $$ = static_cast($1); } | non_null_type { $$ = static_cast($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>(); $$->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>(); $$->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>(); $$->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>(); $$->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>(); $$->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>(); $$->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>(); $$->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); }