%{ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /** * Thrift parser. * * This parser is used on a thrift definition file. * */ #define __STDC_LIMIT_MACROS #define __STDC_FORMAT_MACROS #include #include #include #include "main.h" #include "globals.h" #include "parse/t_program.h" #include "parse/t_scope.h" /** * This global variable is used for automatic numbering of field indices etc. * when parsing the members of a struct. Field values are automatically * assigned starting from -1 and working their way down. */ int y_field_val = -1; int g_arglist = 0; %} /** * This structure is used by the parser to hold the data types associated with * various parse nodes. */ %union { char* id; int64_t iconst; double dconst; bool tbool; t_doc* tdoc; t_type* ttype; t_base_type* tbase; t_typedef* ttypedef; t_enum* tenum; t_enum_value* tenumv; t_const* tconst; t_const_value* tconstv; t_struct* tstruct; t_service* tservice; t_function* tfunction; t_field* tfield; char* dtext; t_field::e_req ereq; t_annotation* tannot; } /** * Strings identifier */ %token tok_identifier %token tok_literal %token tok_doctext %token tok_st_identifier /** * Constant values */ %token tok_int_constant %token tok_dub_constant /** * Header keywords */ %token tok_include %token tok_namespace %token tok_cpp_namespace %token tok_cpp_include %token tok_cpp_type %token tok_php_namespace %token tok_py_module %token tok_perl_package %token tok_java_package %token tok_xsd_all %token tok_xsd_optional %token tok_xsd_nillable %token tok_xsd_namespace %token tok_xsd_attrs %token tok_ruby_namespace %token tok_smalltalk_category %token tok_smalltalk_prefix %token tok_cocoa_prefix %token tok_csharp_namespace /** * Base datatype keywords */ %token tok_void %token tok_bool %token tok_byte %token tok_string %token tok_binary %token tok_slist %token tok_senum %token tok_i16 %token tok_i32 %token tok_i64 %token tok_double /** * Complex type keywords */ %token tok_map %token tok_list %token tok_set /** * Function modifiers */ %token tok_oneway /** * Thrift language keywords */ %token tok_typedef %token tok_struct %token tok_xception %token tok_throws %token tok_extends %token tok_service %token tok_enum %token tok_const %token tok_required %token tok_optional /** * Grammar nodes */ %type BaseType %type ContainerType %type SimpleContainerType %type MapType %type SetType %type ListType %type Definition %type TypeDefinition %type Typedef %type DefinitionType %type TypeAnnotations %type TypeAnnotationList %type TypeAnnotation %type Field %type FieldIdentifier %type FieldRequiredness %type FieldType %type FieldValue %type FieldList %type Enum %type EnumDefList %type EnumDef %type Senum %type SenumDefList %type SenumDef %type Const %type ConstValue %type ConstList %type ConstListContents %type ConstMap %type ConstMapContents %type Struct %type Xception %type Service %type Function %type FunctionType %type FunctionList %type Throws %type Extends %type Oneway %type XsdAll %type XsdOptional %type XsdNillable %type XsdAttributes %type CppType %type CaptureDocText %% /** * Thrift Grammar Implementation. * * For the most part this source file works its way top down from what you * might expect to find in a typical .thrift file, i.e. type definitions and * namespaces up top followed by service definitions using those types. */ Program: HeaderList DefinitionList { pdebug("Program -> Headers DefinitionList"); /* TODO(dreiss): Decide whether full-program doctext is worth the trouble. if ($1 != NULL) { g_program->set_doc($1); } */ clear_doctext(); } CaptureDocText: { if (g_parse_mode == PROGRAM) { $$ = g_doctext; g_doctext = NULL; } else { $$ = NULL; } } /* TODO(dreiss): Try to DestroyDocText in all sorts or random places. */ DestroyDocText: { if (g_parse_mode == PROGRAM) { clear_doctext(); } } /* We have to DestroyDocText here, otherwise it catches the doctext on the first real element. */ HeaderList: HeaderList DestroyDocText Header { pdebug("HeaderList -> HeaderList Header"); } | { pdebug("HeaderList -> "); } Header: Include { pdebug("Header -> Include"); } | tok_namespace tok_identifier tok_identifier { pdebug("Header -> tok_namespace tok_identifier tok_identifier"); if (g_parse_mode == PROGRAM) { g_program->set_namespace($2, $3); } } /* TODO(dreiss): Get rid of this once everyone is using the new hotness. */ | tok_cpp_namespace tok_identifier { pwarning(1, "'cpp_namespace' is deprecated. Use 'namespace cpp' instead"); pdebug("Header -> tok_cpp_namespace tok_identifier"); if (g_parse_mode == PROGRAM) { g_program->set_namespace("cpp", $2); } } | tok_cpp_include tok_literal { pdebug("Header -> tok_cpp_include tok_literal"); if (g_parse_mode == PROGRAM) { g_program->add_cpp_include($2); } } | tok_php_namespace tok_identifier { pwarning(1, "'php_namespace' is deprecated. Use 'namespace php' instead"); pdebug("Header -> tok_php_namespace tok_identifier"); if (g_parse_mode == PROGRAM) { g_program->set_namespace("php", $2); } } /* TODO(dreiss): Get rid of this once everyone is using the new hotness. */ | tok_py_module tok_identifier { pwarning(1, "'py_module' is deprecated. Use 'namespace py' instead"); pdebug("Header -> tok_py_module tok_identifier"); if (g_parse_mode == PROGRAM) { g_program->set_namespace("py", $2); } } /* TODO(dreiss): Get rid of this once everyone is using the new hotness. */ | tok_perl_package tok_identifier { pwarning(1, "'perl_package' is deprecated. Use 'namespace perl' instead"); pdebug("Header -> tok_perl_namespace tok_identifier"); if (g_parse_mode == PROGRAM) { g_program->set_namespace("perl", $2); } } /* TODO(dreiss): Get rid of this once everyone is using the new hotness. */ | tok_ruby_namespace tok_identifier { pwarning(1, "'ruby_namespace' is deprecated. Use 'namespace rb' instead"); pdebug("Header -> tok_ruby_namespace tok_identifier"); if (g_parse_mode == PROGRAM) { g_program->set_namespace("rb", $2); } } /* TODO(dreiss): Get rid of this once everyone is using the new hotness. */ | tok_smalltalk_category tok_st_identifier { pwarning(1, "'smalltalk_category' is deprecated. Use 'namespace smalltalk.category' instead"); pdebug("Header -> tok_smalltalk_category tok_st_identifier"); if (g_parse_mode == PROGRAM) { g_program->set_namespace("smalltalk.category", $2); } } /* TODO(dreiss): Get rid of this once everyone is using the new hotness. */ | tok_smalltalk_prefix tok_identifier { pwarning(1, "'smalltalk_prefix' is deprecated. Use 'namespace smalltalk.prefix' instead"); pdebug("Header -> tok_smalltalk_prefix tok_identifier"); if (g_parse_mode == PROGRAM) { g_program->set_namespace("smalltalk.prefix", $2); } } /* TODO(dreiss): Get rid of this once everyone is using the new hotness. */ | tok_java_package tok_identifier { pwarning(1, "'java_package' is deprecated. Use 'namespace java' instead"); pdebug("Header -> tok_java_package tok_identifier"); if (g_parse_mode == PROGRAM) { g_program->set_namespace("java", $2); } } /* TODO(dreiss): Get rid of this once everyone is using the new hotness. */ | tok_cocoa_prefix tok_identifier { pwarning(1, "'cocoa_prefix' is deprecated. Use 'namespace cocoa' instead"); pdebug("Header -> tok_cocoa_prefix tok_identifier"); if (g_parse_mode == PROGRAM) { g_program->set_namespace("cocoa", $2); } } /* TODO(dreiss): Get rid of this once everyone is using the new hotness. */ | tok_xsd_namespace tok_literal { pwarning(1, "'xsd_namespace' is deprecated. Use 'namespace xsd' instead"); pdebug("Header -> tok_xsd_namespace tok_literal"); if (g_parse_mode == PROGRAM) { g_program->set_namespace("cocoa", $2); } } /* TODO(dreiss): Get rid of this once everyone is using the new hotness. */ | tok_csharp_namespace tok_identifier { pwarning(1, "'csharp_namespace' is deprecated. Use 'namespace csharp' instead"); pdebug("Header -> tok_csharp_namespace tok_identifier"); if (g_parse_mode == PROGRAM) { g_program->set_namespace("csharp", $2); } } Include: tok_include tok_literal { pdebug("Include -> tok_include tok_literal"); if (g_parse_mode == INCLUDES) { std::string path = include_file(std::string($2)); if (!path.empty()) { g_program->add_include(path, std::string($2)); } } } DefinitionList: DefinitionList CaptureDocText Definition { pdebug("DefinitionList -> DefinitionList Definition"); if ($2 != NULL && $3 != NULL) { $3->set_doc($2); } } | { pdebug("DefinitionList -> "); } Definition: Const { pdebug("Definition -> Const"); if (g_parse_mode == PROGRAM) { g_program->add_const($1); } $$ = $1; } | TypeDefinition { pdebug("Definition -> TypeDefinition"); if (g_parse_mode == PROGRAM) { g_scope->add_type($1->get_name(), $1); if (g_parent_scope != NULL) { g_parent_scope->add_type(g_parent_prefix + $1->get_name(), $1); } } $$ = $1; } | Service { pdebug("Definition -> Service"); if (g_parse_mode == PROGRAM) { g_scope->add_service($1->get_name(), $1); if (g_parent_scope != NULL) { g_parent_scope->add_service(g_parent_prefix + $1->get_name(), $1); } g_program->add_service($1); } $$ = $1; } TypeDefinition: Typedef { pdebug("TypeDefinition -> Typedef"); if (g_parse_mode == PROGRAM) { g_program->add_typedef($1); } } | Enum { pdebug("TypeDefinition -> Enum"); if (g_parse_mode == PROGRAM) { g_program->add_enum($1); } } | Senum { pdebug("TypeDefinition -> Senum"); if (g_parse_mode == PROGRAM) { g_program->add_typedef($1); } } | Struct { pdebug("TypeDefinition -> Struct"); if (g_parse_mode == PROGRAM) { g_program->add_struct($1); } } | Xception { pdebug("TypeDefinition -> Xception"); if (g_parse_mode == PROGRAM) { g_program->add_xception($1); } } Typedef: tok_typedef DefinitionType tok_identifier { pdebug("TypeDef -> tok_typedef DefinitionType tok_identifier"); t_typedef *td = new t_typedef(g_program, $2, $3); $$ = td; } CommaOrSemicolonOptional: ',' {} | ';' {} | {} Enum: tok_enum tok_identifier '{' EnumDefList '}' { pdebug("Enum -> tok_enum tok_identifier { EnumDefList }"); $$ = $4; $$->set_name($2); } EnumDefList: EnumDefList EnumDef { pdebug("EnumDefList -> EnumDefList EnumDef"); $$ = $1; $$->append($2); } | { pdebug("EnumDefList -> "); $$ = new t_enum(g_program); } EnumDef: CaptureDocText tok_identifier '=' tok_int_constant CommaOrSemicolonOptional { pdebug("EnumDef -> tok_identifier = tok_int_constant"); if ($4 < 0) { pwarning(1, "Negative value supplied for enum %s.\n", $2); } if ($4 > INT_MAX) { pwarning(1, "64-bit value supplied for enum %s.\n", $2); } $$ = new t_enum_value($2, $4); if ($1 != NULL) { $$->set_doc($1); } if (g_parse_mode == PROGRAM) { g_scope->add_constant($2, new t_const(g_type_i32, $2, new t_const_value($4))); if (g_parent_scope != NULL) { g_parent_scope->add_constant(g_parent_prefix + $2, new t_const(g_type_i32, $2, new t_const_value($4))); } } } | CaptureDocText tok_identifier CommaOrSemicolonOptional { pdebug("EnumDef -> tok_identifier"); $$ = new t_enum_value($2); if ($1 != NULL) { $$->set_doc($1); } } Senum: tok_senum tok_identifier '{' SenumDefList '}' { pdebug("Senum -> tok_senum tok_identifier { SenumDefList }"); $$ = new t_typedef(g_program, $4, $2); } SenumDefList: SenumDefList SenumDef { pdebug("SenumDefList -> SenumDefList SenumDef"); $$ = $1; $$->add_string_enum_val($2); } | { pdebug("SenumDefList -> "); $$ = new t_base_type("string", t_base_type::TYPE_STRING); $$->set_string_enum(true); } SenumDef: tok_literal CommaOrSemicolonOptional { pdebug("SenumDef -> tok_literal"); $$ = $1; } Const: tok_const FieldType tok_identifier '=' ConstValue CommaOrSemicolonOptional { pdebug("Const -> tok_const FieldType tok_identifier = ConstValue"); if (g_parse_mode == PROGRAM) { $$ = new t_const($2, $3, $5); validate_const_type($$); g_scope->add_constant($3, $$); if (g_parent_scope != NULL) { g_parent_scope->add_constant(g_parent_prefix + $3, $$); } } else { $$ = NULL; } } ConstValue: tok_int_constant { pdebug("ConstValue => tok_int_constant"); $$ = new t_const_value(); $$->set_integer($1); if ($1 < INT32_MIN || $1 > INT32_MAX) { pwarning(1, "64-bit constant \"%"PRIi64"\" may not work in all languages.\n", $1); } } | tok_dub_constant { pdebug("ConstValue => tok_dub_constant"); $$ = new t_const_value(); $$->set_double($1); } | tok_literal { pdebug("ConstValue => tok_literal"); $$ = new t_const_value($1); } | tok_identifier { pdebug("ConstValue => tok_identifier"); t_const* constant = g_scope->get_constant($1); if (constant != NULL) { $$ = constant->get_value(); } else { if (g_parse_mode == PROGRAM) { pwarning(1, "Constant strings should be quoted: %s\n", $1); } $$ = new t_const_value($1); } } | ConstList { pdebug("ConstValue => ConstList"); $$ = $1; } | ConstMap { pdebug("ConstValue => ConstMap"); $$ = $1; } ConstList: '[' ConstListContents ']' { pdebug("ConstList => [ ConstListContents ]"); $$ = $2; } ConstListContents: ConstListContents ConstValue CommaOrSemicolonOptional { pdebug("ConstListContents => ConstListContents ConstValue CommaOrSemicolonOptional"); $$ = $1; $$->add_list($2); } | { pdebug("ConstListContents =>"); $$ = new t_const_value(); $$->set_list(); } ConstMap: '{' ConstMapContents '}' { pdebug("ConstMap => { ConstMapContents }"); $$ = $2; } ConstMapContents: ConstMapContents ConstValue ':' ConstValue CommaOrSemicolonOptional { pdebug("ConstMapContents => ConstMapContents ConstValue CommaOrSemicolonOptional"); $$ = $1; $$->add_map($2, $4); } | { pdebug("ConstMapContents =>"); $$ = new t_const_value(); $$->set_map(); } Struct: tok_struct tok_identifier XsdAll '{' FieldList '}' TypeAnnotations { pdebug("Struct -> tok_struct tok_identifier { FieldList }"); $5->set_xsd_all($3); $$ = $5; $$->set_name($2); if ($7 != NULL) { $$->annotations_ = $7->annotations_; delete $7; } } XsdAll: tok_xsd_all { $$ = true; } | { $$ = false; } XsdOptional: tok_xsd_optional { $$ = true; } | { $$ = false; } XsdNillable: tok_xsd_nillable { $$ = true; } | { $$ = false; } XsdAttributes: tok_xsd_attrs '{' FieldList '}' { $$ = $3; } | { $$ = NULL; } Xception: tok_xception tok_identifier '{' FieldList '}' { pdebug("Xception -> tok_xception tok_identifier { FieldList }"); $4->set_name($2); $4->set_xception(true); $$ = $4; } Service: tok_service tok_identifier Extends '{' FlagArgs FunctionList UnflagArgs '}' { pdebug("Service -> tok_service tok_identifier { FunctionList }"); $$ = $6; $$->set_name($2); $$->set_extends($3); } FlagArgs: { g_arglist = 1; } UnflagArgs: { g_arglist = 0; } Extends: tok_extends tok_identifier { pdebug("Extends -> tok_extends tok_identifier"); $$ = NULL; if (g_parse_mode == PROGRAM) { $$ = g_scope->get_service($2); if ($$ == NULL) { yyerror("Service \"%s\" has not been defined.", $2); exit(1); } } } | { $$ = NULL; } FunctionList: FunctionList Function { pdebug("FunctionList -> FunctionList Function"); $$ = $1; $1->add_function($2); } | { pdebug("FunctionList -> "); $$ = new t_service(g_program); } Function: CaptureDocText Oneway FunctionType tok_identifier '(' FieldList ')' Throws CommaOrSemicolonOptional { $6->set_name(std::string($4) + "_args"); $$ = new t_function($3, $4, $6, $8, $2); if ($1 != NULL) { $$->set_doc($1); } } Oneway: tok_oneway { $$ = true; } | { $$ = false; } Throws: tok_throws '(' FieldList ')' { pdebug("Throws -> tok_throws ( FieldList )"); $$ = $3; if (g_parse_mode == PROGRAM && !validate_throws($$)) { yyerror("Throws clause may not contain non-exception types"); exit(1); } } | { $$ = new t_struct(g_program); } FieldList: FieldList Field { pdebug("FieldList -> FieldList , Field"); $$ = $1; if (!($$->append($2))) { yyerror("Field identifier %d for \"%s\" has already been used", $2->get_key(), $2->get_name().c_str()); exit(1); } } | { pdebug("FieldList -> "); y_field_val = -1; $$ = new t_struct(g_program); } Field: CaptureDocText FieldIdentifier FieldRequiredness FieldType tok_identifier FieldValue XsdOptional XsdNillable XsdAttributes CommaOrSemicolonOptional { pdebug("tok_int_constant : Field -> FieldType tok_identifier"); if ($2 < 0) { pwarning(1, "No field key specified for %s, resulting protocol may have conflicts or not be backwards compatible!\n", $5); if (g_strict >= 192) { yyerror("Implicit field keys are deprecated and not allowed with -strict"); exit(1); } } $$ = new t_field($4, $5, $2); $$->set_req($3); if ($6 != NULL) { validate_field_value($$, $6); $$->set_value($6); } $$->set_xsd_optional($7); $$->set_xsd_nillable($8); if ($1 != NULL) { $$->set_doc($1); } if ($9 != NULL) { $$->set_xsd_attrs($9); } } FieldIdentifier: tok_int_constant ':' { if ($1 <= 0) { pwarning(1, "Nonpositive value (%d) not allowed as a field key.\n", $1); $1 = y_field_val--; } $$ = $1; } | { $$ = y_field_val--; } FieldRequiredness: tok_required { if (g_arglist) { if (g_parse_mode == PROGRAM) { pwarning(1, "required keyword is ignored in argument lists.\n"); } $$ = t_field::T_OPT_IN_REQ_OUT; } else { $$ = t_field::T_REQUIRED; } } | tok_optional { if (g_arglist) { if (g_parse_mode == PROGRAM) { pwarning(1, "optional keyword is ignored in argument lists.\n"); } $$ = t_field::T_OPT_IN_REQ_OUT; } else { $$ = t_field::T_OPTIONAL; } } | { $$ = t_field::T_OPT_IN_REQ_OUT; } FieldValue: '=' ConstValue { if (g_parse_mode == PROGRAM) { $$ = $2; } else { $$ = NULL; } } | { $$ = NULL; } DefinitionType: BaseType { pdebug("DefinitionType -> BaseType"); $$ = $1; } | ContainerType { pdebug("DefinitionType -> ContainerType"); $$ = $1; } FunctionType: FieldType { pdebug("FunctionType -> FieldType"); $$ = $1; } | tok_void { pdebug("FunctionType -> tok_void"); $$ = g_type_void; } FieldType: tok_identifier { pdebug("FieldType -> tok_identifier"); if (g_parse_mode == INCLUDES) { // Ignore identifiers in include mode $$ = NULL; } else { // Lookup the identifier in the current scope $$ = g_scope->get_type($1); if ($$ == NULL) { yyerror("Type \"%s\" has not been defined.", $1); exit(1); } } } | BaseType { pdebug("FieldType -> BaseType"); $$ = $1; } | ContainerType { pdebug("FieldType -> ContainerType"); $$ = $1; } BaseType: tok_string { pdebug("BaseType -> tok_string"); $$ = g_type_string; } | tok_binary { pdebug("BaseType -> tok_binary"); $$ = g_type_binary; } | tok_slist { pdebug("BaseType -> tok_slist"); $$ = g_type_slist; } | tok_bool { pdebug("BaseType -> tok_bool"); $$ = g_type_bool; } | tok_byte { pdebug("BaseType -> tok_byte"); $$ = g_type_byte; } | tok_i16 { pdebug("BaseType -> tok_i16"); $$ = g_type_i16; } | tok_i32 { pdebug("BaseType -> tok_i32"); $$ = g_type_i32; } | tok_i64 { pdebug("BaseType -> tok_i64"); $$ = g_type_i64; } | tok_double { pdebug("BaseType -> tok_double"); $$ = g_type_double; } ContainerType: SimpleContainerType TypeAnnotations { pdebug("ContainerType -> SimpleContainerType TypeAnnotations"); $$ = $1; if ($2 != NULL) { $$->annotations_ = $2->annotations_; delete $2; } } SimpleContainerType: MapType { pdebug("SimpleContainerType -> MapType"); $$ = $1; } | SetType { pdebug("SimpleContainerType -> SetType"); $$ = $1; } | ListType { pdebug("SimpleContainerType -> ListType"); $$ = $1; } MapType: tok_map CppType '<' FieldType ',' FieldType '>' { pdebug("MapType -> tok_map "); $$ = new t_map($4, $6); if ($2 != NULL) { ((t_container*)$$)->set_cpp_name(std::string($2)); } } SetType: tok_set CppType '<' FieldType '>' { pdebug("SetType -> tok_set"); $$ = new t_set($4); if ($2 != NULL) { ((t_container*)$$)->set_cpp_name(std::string($2)); } } ListType: tok_list '<' FieldType '>' CppType { pdebug("ListType -> tok_list"); $$ = new t_list($3); if ($5 != NULL) { ((t_container*)$$)->set_cpp_name(std::string($5)); } } CppType: tok_cpp_type tok_literal { $$ = $2; } | { $$ = NULL; } TypeAnnotations: '(' TypeAnnotationList ')' { pdebug("TypeAnnotations -> ( TypeAnnotationList )"); $$ = $2; } | { $$ = NULL; } TypeAnnotationList: TypeAnnotationList TypeAnnotation { pdebug("TypeAnnotationList -> TypeAnnotationList , TypeAnnotation"); $$ = $1; $$->annotations_[$2->key] = $2->val; delete $2; } | { /* Just use a dummy structure to hold the annotations. */ $$ = new t_struct(g_program); } TypeAnnotation: tok_identifier '=' tok_literal CommaOrSemicolonOptional { pdebug("TypeAnnotation -> tok_identifier = tok_literal"); $$ = new t_annotation; $$->key = $1; $$->val = $3; } %%