/* * PUBLIC DOMAIN PCCTS-BASED C++ GRAMMAR (cplusplus.g, stat.g, expr.g) * * Authors: Sumana Srinivasan, NeXT Inc.; sumana_srinivasan@next.com * Terence Parr, Parr Research Corporation; parrt@parr-research.com * Russell Quong, Purdue University; quong@ecn.purdue.edu * * VERSION 1.2 * * SOFTWARE RIGHTS * * This file is a part of the ANTLR-based C++ grammar and is free * software. We do not reserve any LEGAL rights to its use or * distribution, but you may NOT claim ownership or authorship of this * grammar or support code. An individual or company may otherwise do * whatever they wish with the grammar distributed herewith including the * incorporation of the grammar or the output generated by ANTLR into * commerical software. You may redistribute in source or binary form * without payment of royalties to us as long as this header remains * in all source distributions. * * We encourage users to develop parsers/tools using this grammar. * In return, we ask that credit is given to us for developing this * grammar. By "credit", we mean that if you incorporate our grammar or * the generated code into one of your programs (commercial product, * research project, or otherwise) that you acknowledge this fact in the * documentation, research report, etc.... In addition, you should say nice * things about us at every opportunity. * * As long as these guidelines are kept, we expect to continue enhancing * this grammar. Feel free to send us enhancements, fixes, bug reports, * suggestions, or general words of encouragement at parrt@parr-research.com. * * NeXT Computer Inc. * 900 Chesapeake Dr. * Redwood City, CA 94555 * 12/02/1994 * * Restructured for public consumption by Terence Parr late February, 1995. * * Requires PCCTS 1.32b7 or higher to get past ANTLR. * * DISCLAIMER: we make no guarantees that this grammar works, makes sense, * or can be used to do anything useful. */ #header << #include "CPPDictionary.h" #include "AToken.h" typedef ANTLRCommonToken ANTLRToken; >> /* These token names are unnecessary in that the regular expressions * could easily have been placed into the grammar rules themselves. * However, if SORCERER is to be used to do any sort of translation * labels must be assigned for each token type. Hence, I've left * the token label definitions here. */ #token LCURLYBRACE "\{" #token RCURLYBRACE "\}" #token LSQUAREBRACKET "\[" #token RSQUAREBRACKET "\]" #token LPARENTHESIS "\(" #token RPARENTHESIS "\)" #token COLON ":" #token SEMICOLON ";" #token COMMA "," #token QUESTIONMARK "?" #token ELLIPSIS "..." #token ASSIGNEQUAL "=" #token TIMESEQUAL "\*=" #token DIVIDEEQUAL "/=" #token MODEQUAL "%=" #token PLUSEQUAL "\+=" #token MINUSEQUAL "\-=" #token SHIFTLEFTEQUAL "\<\<=" #token SHIFTRIGHTEQUAL "\>\>=" #token BITWISEANDEQUAL "&=" #token BITWISEXOREQUAL "^=" #token BITWISEOREQUAL "\|=" #token OR "\|\|" #token AND "&&" #token BITWISEOR "\|" #token BITWISEXOR "^" #token AMPERSAND "&" #token EQUAL "==" #token NOTEQUAL "!=" #token LESSTHAN "<" #token GREATERTHAN ">" #token LESSTHANOREQUALTO "<=" #token GREATERTHANOREQUALTO ">=" #token SHIFTLEFT "\<\<" #token SHIFTRIGHT "\>\>" #token PLUS "\+" #token MINUS "\-" #token STAR "\*" #token DIVIDE "/" #token MOD "%" #token PLUSPLUS "\+\+" #token MINUSMINUS "\-\-" #token TILDE "\~" #token NOT "!" #token DOT "." #token POINTERTO "\->" #token AUTO "auto" #token BREAK "break" #token CASE "case" #token CHAR "char" #token CONST "const" #token CONTINUE "continue" #token DEFAULT "default" #token DO "do" #token DOUBLE "double" #token ELSE "else" #token ENUM "enum" #token EXTERN "extern" #token FLOAT "float" #token FOR "for" #token GOTO "goto" #token IF "if" #token INT "int" #token LONG "long" #token REGISTER "register" #token RETURN "return" #token SHORT "short" #token SIGNED "signed" #token SIZEOF "sizeof" #token STATIC "static" #token STRUCT "struct" #token CLASS "class" #token SWITCH "switch" #token TYPEDEF "typedef" #token UNION "union" #token UNSIGNED "unsigned" #token VOID "void" #token VOLATILE "volatile" #token WHILE "while" #token SCOPE "::" #token OPERATOR "operator" #token "/\*" << mode (COMMENT); skip (); >> #token "[\t\ ]+" << skip (); >> #token "[\n\r]" << newline(); skip(); >> #token "// ~[\n]* \n" << newline(); skip(); >> #token "#pragma ~[\n]* \n" << newline(); skip(); >> // line number and file stuff from preprocessor #token "#[\ \t]* [0-9]+ {[\ \t]* \"~[\"]+\" [\ \t]* [0-9]* [\ \t]* [0-9]*} \n" << _line = atoi(begexpr()+1); skip(); >> #token Eof "@" #lexclass COMMENT #token "[\n\r]" << skip(); newline(); >> #token "\*/" << mode (START); skip (); >> #token "\*~[/]" << skip (); >> #token "~[\*\n\r]+" << skip (); >> #lexclass STRINGS #token STRING "\"" << mode (START); >> #token "\\n" << replchar ((char) 0x0A); more (); >> #token "\\t" << replchar ((char) 0x09); more (); >> #token "\\v" << replchar ((char) 0x0B); more (); >> #token "\\b" << replchar ((char) 0x08); more (); >> #token "\\r" << replchar ((char) 0x0D); more (); >> #token "\\f" << replchar ((char) 0x0C); more (); >> #token "\\a" << replchar ((char) 0x07); more (); >> #token "\\\\" << replchar ((char) 0x5C); more (); >> #token "\\?" << replchar ((char) 0x3F); more (); >> #token "\\'" << replchar ((char) 0x27); more (); >> #token "\\\"" << replchar ((char) 0x22); more (); >> #token "\\0[0-7]*" << replchar ((char) strtol (begexpr(), NULL, 8)); more (); >> #token "\\[1-9][0-9]*" << replchar ((char) strtol (begexpr(), NULL, 10)); more (); >> #token "\\(0x|0X)[0-9a-fA-F]+" << replchar ((char) strtol (begexpr(), NULL, 16)); more (); >> #token "[\n\r]" << newline(); more (); >> #token "~[\"\n\r\\]+" << more (); >> #lexclass CHARACTERS #token CHARACTER "'" << mode (START); >> #token "\\n" << replchar ((char) 0x0A); more (); mode (DONE); >> #token "\\t" << replchar ((char) 0x09); more (); mode (DONE); >> #token "\\v" << replchar ((char) 0x0B); more (); mode (DONE); >> #token "\\b" << replchar ((char) 0x08); more (); mode (DONE); >> #token "\\r" << replchar ((char) 0x0D); more (); mode (DONE); >> #token "\\f" << replchar ((char) 0x0C); more (); mode (DONE); >> #token "\\a" << replchar ((char) 0x07); more (); mode (DONE); >> #token "\\\\" << replchar ((char) 0x5C); more (); mode (DONE); >> #token "\\?" << replchar ((char) 0x3F); more (); mode (DONE); >> #token "\\'" << replchar ((char) 0x27); more (); mode (DONE); >> #token "\\\"" << replchar ((char) 0x22); more (); mode (DONE); >> #token "\\0[0-7]*" << replchar ((char) strtol (begexpr(), NULL, 8)); more (); mode (DONE); >> #token "\\[1-9][0-9]*" << replchar ((char) strtol (begexpr(), NULL, 10)); more (); mode (DONE); >> #token "\\(0x|0X)[0-9a-fA-F]+" << replchar ((char) strtol (begexpr(), NULL, 16)); more (); mode (DONE); >> #token "[\n\r]" << newline(); more (); >> #token "~['\n\r\\]" << more (); mode (DONE); >> #lexclass DONE #token CHARACTER "'" << mode (START); >> #lexclass START << //typedef ANTLRCommonToken ANTLRToken; >> class CPPParser { << public: #define CPPParser_MaxQualifiedItemSize 500 // can't bitwise-OR enum elements together, this must be an int; damn! typedef unsigned long TypeSpecifier; // note: must be > 16bits #define tsInvalid 0 #define tsVOID 0x1 #define tsCHAR 0x2 #define tsSHORT 0x4 #define tsINT 0x8 #define tsLONG 0x10 #define tsFLOAT 0x20 #define tsDOUBLE 0x40 #define tsSIGNED 0x80 #define tsUNSIGNED 0x100 #define tsTYPEID 0x200 #define tsSTRUCT 0x400 #define tsENUM 0x800 #define tsUNION 0x1000 #define tsCLASS 0x2000 enum TypeQualifier { tqInvalid=0, tqCONST=1, tqVOLATILE }; enum StorageClass { scInvalid=0, scAUTO=1, scREGISTER, scSTATIC, scEXTERN, scTYPEDEF }; enum DeclSpecifier { dsInvalid=0, dsVIRTUAL, dsINLINE, dsFRIEND }; enum QualifiedItem { qiInvalid=0, qiType=1, // includes enum, class, typedefs qiDtor, qiCtor, qiOperator, qiPtrMember, qiID // not a type, but could be a var, func... }; protected: // Symbol table management stuff CPPDictionary *symbols; StorageClass _sc; TypeQualifier _tq; TypeSpecifier _ts; unsigned char functionDefinition; int traceIndentLevel, doTracing; char qualifierPrefix[CPPParser_MaxQualifiedItemSize+1]; int externalScope; // global variables int templateParameterScope; char *enclosingClass; void tracein(char *r); void traceout(char *r); QualifiedItem qualifiedItemIs(int lookahead_offset=0); char *qualified(char *); int scopedItem(int k=1); int finalQualifier(int k=1); public: void init() { ANTLRParser::init(); symbols = new CPPDictionary(4001, 200, 800000); traceIndentLevel = 0; doTracing = 0; qualifierPrefix[0] = '\0'; templateParameterScope = symbols->getCurrentScopeIndex(); symbols->saveScope(); externalScope = symbols->getCurrentScopeIndex(); enclosingClass = ""; } void traceOn() { doTracing=1; } protected: // Semantic interface; subclass and redefine these functions // so you don't have to mess with the grammar itself. virtual int isTypeName(char *s); virtual int isClassName(char *s); virtual void enterNewLocalScope(); virtual void exitLocalScope(); virtual void enterExternalScope(); virtual void exitExternalScope(); // Aggregate stuff virtual void beginClassDefinition(TypeSpecifier, char *); virtual void endClassDefinition(); virtual void classForwardDeclaration(TypeSpecifier, DeclSpecifier, char *); // Declaration stuff virtual void beginDeclaration(); virtual void endDeclaration(); virtual void beginFunctionDefinition(); virtual void endFunctionDefinition(); virtual void functionParameterList(); virtual void functionEndParameterList(); virtual void beginConstructorDefinition(char *); virtual void endConstructorDefinition(); virtual void beginConstructorDeclaration(char *); virtual void endConstructorDeclaration(); virtual void beginDestructorDefinition(char *); virtual void endDestructorDefinition(); virtual void beginDestructorDeclaration(char *); virtual void endDestructorDeclaration(); virtual void beginParameterDeclaration(); virtual void beginFieldDeclaration(); virtual void declarationSpecifier(StorageClass, TypeQualifier, TypeSpecifier); virtual void beginEnumDefinition(char *); virtual void endEnumDefinition(); virtual void enumElement(char *); // Declarator stuff virtual void declaratorPointerTo(); virtual void declaratorID(char *); virtual void declaratorArray(); virtual void declaratorParameterList(int def); virtual void declaratorEndParameterList(int def); // template stuff virtual void templateTypeParameter(char *); virtual void beginTemplateDefinition(); virtual void endTemplateDefinition(); virtual void beginTemplateParameterList(); virtual void endTemplateParameterList(); // exception stuff virtual void exceptionBeginHandler(); virtual void exceptionEndHandler(); virtual void panic(char *); >> translation_unit : <> (external_declaration)+ Eof <> ; external_declaration : // Class definition (templates too) // This is separated out otherwise the next alternative // would look for "class A { ... } f() {...}" which is // an acceptable level of backtracking. ( {TYPEDEF | template_head } class_head LCURLYBRACE )? { template_head } declaration | // Enum definition (don't want to backtrack over this in other alts) ( "enum" {ID} "\{" )? enum_specifier { init_declarator_list } ";" // Destructor DEFINITION (templated or non-templated) | ( {template_head} dtor_ctor_decl_spec dtor_declarator[1] "\{" )? dtor_definition // Constructor DEFINITION (non-templated) /* | (ID)? => <>? ( dtor_ctor_decl_spec ctor_declarator[1] { ctor_initializer } "\{" )? ctor_definition */ // User-defined type cast | ( scope_override conversion_function_decl_or_def )? | // Function definition ( ID )? => <<1>>? <<;>> // don't hoist preds from decl_specs as we will backtrack ( {declaration_specifiers} function_declarator "\{" )? function_definition // templated forward class decl, init/decl of static member in template | <> (template_head declaration_specifiers { init_declarator_list } ";")? <> // Can only be templated function definition here; regular funcs // matched above. Templated CONSTRUCTORS also matched here | <> template_head ( ( dtor_ctor_decl_spec ctor_declarator[1] { ctor_initializer } "\{" )? ctor_definition | {declaration_specifiers} function_declarator ( compound_statement | ";" ) ) <> /* // stupid, but must allow "a;" | init_declarator_list ";" */ // everything else (except templates) | declaration // believe it or not, this is a valid decl | ";" ; function_definition : // don't want next action as an init-action due to (...)? caller <> ( declaration_specifiers function_declarator compound_statement | function_declarator compound_statement ) <> ; linkage_specification : "extern" STRING ( "\{" (external_declaration)* "\}" #pragma approx {";"} | declaration ) ; declaration : <<;>> <> declaration_specifiers { init_declarator_list } ";" <> | linkage_specification ; declaration_specifiers : << TypeSpecifier ts = tsInvalid, ts2; TypeQualifier tq = tqInvalid; StorageClass sc = scInvalid; DeclSpecifier ds = dsInvalid; >> #pragma approx ( storage_class_specifier>[sc] | type_specifier[ds]>[ts2] <> | type_qualifier>[tq] | "inline" <> | "virtual" <> | "friend" <> )+ <> ; type_specifier[DeclSpecifier ds] > [CPPParser::TypeSpecifier ts] : simple_type_specifier > [$ts] | class_specifier[$ds] > [$ts] | enum_specifier <<$ts=tsENUM;>> ; simple_type_specifier > [CPPParser::TypeSpecifier ts] : ( builtin_type_specifier > [$ts] | qualified_type <<$ts=tsTYPEID;>> ) ; /* Match the A::B::C:: or nothing */ scope_override > [char *s] : << static char sitem[CPPParser_MaxQualifiedItemSize+1]; sitem[0]='\0'; >> {"::" <> } #pragma approx ( <>? id:ID {"<" template_argument_list ">"} "::" << strcat(sitem,$id->getText()); strcat(sitem,"::"); >> )* <<$s = sitem;>> ; /* This matches a generic qualified identifier ::T::B::foo * (including "operator"). * It might be a good idea to put T::~dtor in here * as well, but id_expression in expr.g puts it in manually. * Maybe not, 'cause many people use this assuming only A::B. * How about a 'qualified_complex_id'? */ qualified_id > [char *q] : << char *so; static char qitem[CPPParser_MaxQualifiedItemSize+1]; >> scope_override > [so] <> ( <> id2:ID { "<" template_argument_list ">" } <getText());>> | "operator" optor > [op] <> ) <<$q = qitem;>> ; /* Match A::B::* */ ptr_to_member : (ID)? => <>? scope_override "\*" ; qualified_type > [char *q] : <> <>? scope_override > [s] id:ID #pragma approx {"<" template_argument_list ">"} << strcpy(qitem, s); strcat(qitem, $id->getText()); $q = qitem; >> ; type_qualifier > [CPPParser::TypeQualifier tq] : CONST <<$tq = tqCONST;>> | VOLATILE <<$tq = tqVOLATILE;>> ; storage_class_specifier > [CPPParser::StorageClass sc] : AUTO <<$sc = scAUTO;>> | REGISTER <<$sc = scREGISTER;>> | STATIC <<$sc = scSTATIC;>> | EXTERN <<$sc = scEXTERN;>> | TYPEDEF <<$sc = scTYPEDEF;>> ; builtin_type_specifier > [CPPParser::TypeSpecifier ts] : VOID <<$ts = tsVOID;>> | CHAR <<$ts = tsCHAR;>> | SHORT <<$ts = tsSHORT;>> | INT <<$ts = tsINT;>> | LONG <<$ts = tsLONG;>> | FLOAT <<$ts = tsFLOAT;>> | DOUBLE <<$ts = tsDOUBLE;>> | SIGNED <<$ts = tsSIGNED;>> | UNSIGNED <<$ts = tsUNSIGNED;>> ; typeID : <getText())>>? ID ; init_declarator_list : init_declarator (COMMA init_declarator)* ; init_declarator : declarator { "=" initializer | "\(" expression_list "\)" } ; class_head : ( STRUCT | UNION | CLASS ) {ID { base_clause } } ; class_specifier[DeclSpecifier ds] > [CPPParser::TypeSpecifier ts] : <> ( STRUCT <<$ts=tsSTRUCT;>> | UNION <<$ts=tsUNION;>> | CLASS <<$ts=tsCLASS;>> ) #pragma approx // indicate that ambig between alts 1 and 3 is ok ( id:ID <strdup($id->getText()); >> { base_clause } LCURLYBRACE <getText());>> (member_declaration)* <> RCURLYBRACE <> | LCURLYBRACE <> <> (member_declaration)* <> RCURLYBRACE <> | tag:ID #pragma approx {"<" template_argument_list ">"} <getText());>> ) ; base_clause : ":" base_specifier ("," base_specifier)* ; base_specifier : ( "virtual" {access_specifier} ID | access_specifier {"virtual"} ID | ID ) {"<" template_argument_list ">"} ; access_specifier : "public" | "protected" | "private" ; member_declaration : // Class definition // This is separated out otherwise the next alternative // would look for "class A { ... } f() {...}" which is // an acceptable level of backtracking. ( {TYPEDEF} class_head LCURLYBRACE )? declaration | // Enum definition (don't want to backtrack over this in other alts) ( "enum" {ID} "\{" )? enum_specifier {member_declarator_list} ";" // Destructor DEFINITION | ( dtor_ctor_decl_spec dtor_declarator[1] "\{" )? dtor_definition | // Destructor DECLARATION (ambig with func def, but ok) ( ("inline"|"virtual")* "\~" )? dtor_ctor_decl_spec <getText());>> simple_dtor_declarator[0] ";" <> // Constructor DEFINITION (must have same name as enclosing class) | (ID)? => <getText())==0>>? ( dtor_ctor_decl_spec ctor_declarator[1] { ctor_initializer } "\{" )? ctor_definition // Constructor DECLARATION (must have same name as enclosing class) | (ID)? => <getText())==0>>? ( <<;>> <getText());>> dtor_ctor_decl_spec ctor_declarator[0] ";" <> )? | // Function definition <> ( {declaration_specifiers} function_declarator "\{" )? function_definition // User-defined type cast | conversion_function_decl_or_def // Hack to handle decls like "superclass::member" | ( qualified_id ";" )? /* // stupid, but must allow member "a;" | member_declarator_list ";" */ // Member with a type or just a type def // A::T a(), ::T a, ::B a, void a, E a (where E is the enclosing class) | (ID|SCOPE)? => <>? <> declaration_specifiers {member_declarator_list} ";" // Member without a type (I guess it can only be a function decl) | ( ID )? => <<1>>? <> function_declarator ";" | access_specifier ":" | ";" ; member_declarator_list : member_declarator { "=" OCTALINT } // The value must be 0 (pure virt.) ( "," member_declarator { "=" OCTALINT } )* ; member_declarator : declarator ; conversion_function_decl_or_def : "operator" declaration_specifiers {"\*"|"&"} "\(" { parameter_list } "\)" {type_qualifier} ( compound_statement | ";" ) ; enum_specifier : ENUM ( "\{" enumerator_list "\}" | id:ID <getText());>> { "\{" enumerator_list "\}" } <> ) ; enumerator_list : enumerator (COMMA enumerator)* ; enumerator : id:ID { "=" constant_expression } <getText());>> ; /* I think something is weird with the context-guards for predicates; * as a result I manually hoist the appropriate pred from ptr_to_member */ ptr_operator : "&" cv_qualifier_seq | "\*" cv_qualifier_seq | (ID)? => <>? <<;>> ptr_to_member cv_qualifier_seq ; /* Linear-approximate lookahead is sufficient to handle the {...}, but * ANTLR tries full LL(k) because is couldn't be sure. The #pragma * forces ANTLR not to attempt this *expensive* operation. */ cv_qualifier_seq : "const" | "const" "volatile" | "volatile" | "volatile" "const" | /* empty */ ; declarator : ptr_operator declarator <> | ( ID )? => <<1>>? // counteract qiPtrMember pred in ptr_operator direct_declarator ; direct_declarator : <> qualified_id > [id] <> declarator_suffixes | "\~" dtor:ID <getText());>> declarator_suffixes | LPARENTHESIS declarator RPARENTHESIS declarator_suffixes ; declarator_suffixes : ( LSQUAREBRACKET { constant_expression } RSQUAREBRACKET )+ <> | ("\(" ID)? => <>? "\(" <> { parameter_list } "\)" <> { type_qualifier } | ; /* I think something is weird with the context-guards for predicates; * as a result I manually hoist the appropriate pred from ptr_to_member * * TER: warning: seems that "ID::" will always bypass and go to 2nd alt :( */ function_declarator : (ID)? => <>? <<;>> ptr_operator function_declarator <> | function_direct_declarator ; function_direct_declarator : <> /* predicate indicate that plain ID is ok here; this counteracts any * other predicate that gets hoisted (along with this one) that * indicates that an ID is a type or whatever. E.g., * another rule testing isTypeName() alone, implies that the * the ID *MUST* be a type name. Combining isTypeName() and * this predicate in an OR situation like this one: * ( declaration_specifiers ... | function_declarator ... ) * would imply that ID can be a type name OR a plain ID. */ ( ID )? => <<1>>? qualified_id>[q] << declaratorID(q); functionParameterList(); >> "\(" { parameter_list } "\)" { type_qualifier } { "=" OCTALINT } // The value of the octal must be 0 <> ; dtor_ctor_decl_spec : "inline" | "virtual" | "virtual" "inline" | "inline" "virtual" | ; dtor_definition : <<;>> <getText());>> {template_head} dtor_ctor_decl_spec dtor_declarator[1] compound_statement <> ; ctor_definition : <<;>> <getText());>> dtor_ctor_decl_spec ctor_declarator[1] { ctor_initializer } compound_statement <> ; dtor_declarator[int definition] : scope_override simple_dtor_declarator[$definition] ; simple_dtor_declarator[int definition] : "\~" ID <> "\(" { parameter_list } "\)" <> ; ctor_declarator[int definition] : qualified_id <> "\(" { parameter_list } "\)" <> ; ctor_initializer : ":" superclass_init ("," superclass_init)* ; superclass_init : qualified_id "\(" {expression_list} "\)" ; parameter_list : parameter_declaration_list { {","} "..." } | "..." ; parameter_declaration_list : parameter_declaration ( COMMA parameter_declaration )* ; parameter_declaration : <<;>> <> declaration_specifiers ( (declarator)? // if arg name given | abstract_declarator // if arg name not given ) {"=" assignment_expression} ; initializer : assignment_expression | LCURLYBRACE initializer (COMMA initializer)* RCURLYBRACE ; type_name : declaration_specifiers abstract_declarator ; /* This rule looks a bit weird because (...) can happen in two * places within the declaration such as "void (*)()" (ptr to * function returning nothing). However, the () of a function * can only occur after having seen either a (abstract_declarator) * and not after a [..] or simple '*'. These are the only two * valid () func-groups: * int (*)(); // ptr to func * int (*[])(); // array of ptr to func */ abstract_declarator : ptr_operator abstract_declarator <> | LPARENTHESIS abstract_declarator RPARENTHESIS (abstract_declarator_suffix)+ | ( LSQUAREBRACKET { constant_expression } RSQUAREBRACKET <> )+ | /* empty */ ; abstract_declarator_suffix : LSQUAREBRACKET { constant_expression } RSQUAREBRACKET <> | "\(" <> { parameter_list } "\)" <> ; template_head : "template" <> "<" template_parameter_list ">" <> ; /* template_declaration : <<;>> <> template_head declaration <> ; */ template_parameter_list : template_parameter ( "," template_parameter )* ; /* Rule requires >2 lookahead tokens. The ambiguity is resolved * correctly, however. According to the manual ''...A template argument * that can be interpreted either as a parameter-declaration or a * type-argument (because its identifier is the name of an * already exisitng class) is taken as type-argument.'' * Therefore, any "class ID" that is seen on the input, should * match the first alternative here (it should be a type-argument). */ template_parameter : #pragma approx // make ANTLR shut up about this ambiguity ( "class" id:ID <getText());>> | parameter_declaration ) ; /* This rule refers to an instance of a template class or function */ template_id : ID "<" template_argument_list ">" ; template_argument_list : template_argument ( "," template_argument )* ; /* Here assignment_expression was changed to shift_expression to rule out * x< 1<2 > which causes ambiguities. As a result, these can be used only * by enclosing parentheses x<(1<2)>. This is true for x<1+2> ==> bad, * x<(1+2)>==>ok. */ template_argument : type_name | shift_expression ; }