| %{ | 
 | // Copyright (c) 2006- Facebook | 
 | // Distributed under the Thrift Software License | 
 | // | 
 | // See accompanying file LICENSE or visit the Thrift site at: | 
 | // http://developers.facebook.com/thrift/ | 
 |  | 
 | /** | 
 |  * Thrift parser. | 
 |  * | 
 |  * This parser is used on a thrift definition file. | 
 |  * | 
 |  * @author Mark Slee <mcslee@facebook.com> | 
 |  */ | 
 |  | 
 | #define __STDC_LIMIT_MACROS | 
 | #define __STDC_FORMAT_MACROS | 
 | #include <stdio.h> | 
 | #include <inttypes.h> | 
 | #include <limits.h> | 
 | #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<id>     tok_identifier | 
 | %token<id>     tok_literal | 
 | %token<dtext>  tok_doctext | 
 | %token<id>     tok_st_identifier | 
 |  | 
 | /** | 
 |  * Constant values | 
 |  */ | 
 | %token<iconst> tok_int_constant | 
 | %token<dconst> 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_async | 
 |  | 
 | /** | 
 |  * 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<ttype>     BaseType | 
 | %type<ttype>     ContainerType | 
 | %type<ttype>     SimpleContainerType | 
 | %type<ttype>     MapType | 
 | %type<ttype>     SetType | 
 | %type<ttype>     ListType | 
 |  | 
 | %type<tdoc>      Definition | 
 | %type<ttype>     TypeDefinition | 
 |  | 
 | %type<ttypedef>  Typedef | 
 | %type<ttype>     DefinitionType | 
 |  | 
 | %type<ttype>     TypeAnnotations | 
 | %type<ttype>     TypeAnnotationList | 
 | %type<tannot>    TypeAnnotation | 
 |  | 
 | %type<tfield>    Field | 
 | %type<iconst>    FieldIdentifier | 
 | %type<ereq>      FieldRequiredness | 
 | %type<ttype>     FieldType | 
 | %type<tconstv>   FieldValue | 
 | %type<tstruct>   FieldList | 
 |  | 
 | %type<tenum>     Enum | 
 | %type<tenum>     EnumDefList | 
 | %type<tenumv>    EnumDef | 
 |  | 
 | %type<ttypedef>  Senum | 
 | %type<tbase>     SenumDefList | 
 | %type<id>        SenumDef | 
 |  | 
 | %type<tconst>    Const | 
 | %type<tconstv>   ConstValue | 
 | %type<tconstv>   ConstList | 
 | %type<tconstv>   ConstListContents | 
 | %type<tconstv>   ConstMap | 
 | %type<tconstv>   ConstMapContents | 
 |  | 
 | %type<tstruct>   Struct | 
 | %type<tstruct>   Xception | 
 | %type<tservice>  Service | 
 |  | 
 | %type<tfunction> Function | 
 | %type<ttype>     FunctionType | 
 | %type<tservice>  FunctionList | 
 |  | 
 | %type<tstruct>   Throws | 
 | %type<tservice>  Extends | 
 | %type<tbool>     Async | 
 | %type<tbool>     XsdAll | 
 | %type<tbool>     XsdOptional | 
 | %type<tbool>     XsdNillable | 
 | %type<tstruct>   XsdAttributes | 
 | %type<id>        CppType | 
 |  | 
 | %type<dtext>     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 | 
 |     { | 
 |       pdebug("Header -> tok_php_namespace tok_identifier"); | 
 |       if (g_parse_mode == PROGRAM) { | 
 |         g_program->set_php_namespace($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); | 
 |       } | 
 |     } | 
 | | tok_xsd_namespace tok_literal | 
 |     { | 
 |       pdebug("Header -> tok_xsd_namespace tok_literal"); | 
 |       if (g_parse_mode == PROGRAM) { | 
 |         g_program->set_xsd_namespace($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; | 
 |       } | 
 |       y_field_val = -1; | 
 |     } | 
 |  | 
 | 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; | 
 |       y_field_val = -1; | 
 |     } | 
 |  | 
 | 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 Async 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); | 
 |       } | 
 |       y_field_val = -1; | 
 |     } | 
 |  | 
 | Async: | 
 |   tok_async | 
 |     { | 
 |       $$ = 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 (!($$->validate_field($2))) { | 
 |         yyerror("Field identifier %d for \"%s\" has already been used", $2->get_key(), $2->get_name().c_str()); | 
 |         exit(1); | 
 |       } | 
 |       $$->append($2); | 
 |     } | 
 | | | 
 |     { | 
 |       pdebug("FieldList -> "); | 
 |       $$ = 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(2, "No field key specified for %s, resulting protocol may have conflicts or not be backwards compatible!\n", $5); | 
 |       } | 
 |       $$ = 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 <FieldType, FieldType>"); | 
 |       $$ = 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<FieldType>"); | 
 |       $$ = new t_set($4); | 
 |       if ($2 != NULL) { | 
 |         ((t_container*)$$)->set_cpp_name(std::string($2)); | 
 |       } | 
 |     } | 
 |  | 
 | ListType: | 
 |   tok_list '<' FieldType '>' CppType | 
 |     { | 
 |       pdebug("ListType -> tok_list<FieldType>"); | 
 |       $$ = 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; | 
 |     } | 
 |  | 
 | %% |