header { using StringBuilder = System.Text.StringBuilder; using FileInfo = System.IO.FileInfo; } options { language = "CSharp"; namespace = "Kunle.CSharpParser"; } /** [The "BSD licence"] Copyright (c) 2002-2005 Kunle Odutola All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /// /// A Parser for the C# language (including preprocessors directives). /// /// /// /// /// The Parser defined below is based on the "C# Language Specification" as documented in /// the ECMA-334 standard dated December 2001. /// /// /// /// One notable feature of this parser is that it can handle input that includes "normalized" /// C# preprocessing directives. In the simplest sense, normalized C# preprocessing directives /// are directives that can be safely deleted from a source file without triggering any parsing /// errors due to incomplete statements etc. /// /// /// /// The Abstract Syntax Tree that this parser constructs has special nodes that represents // all the C# preprocessor directives defined in the ECMA-334 standard. /// /// /// /// History /// /// /// /// 31-May-2002 kunle Started work in earnest /// 09-Feb-2003 kunle Separated Parser from the original combined Lexer/Parser grammar file
/// 20-Oct-2003 kunle Removed STMT_LIST from inside BLOCK nodes. A BLOCK node now directly contains /// a list of statements. Finding the AST nodes that correspond to a selection /// should now be easier.
///
/// ///
*/ class CSharpParser extends Parser; options { k = 2; // two tokens of lookahead importVocab = CSharpLexer; exportVocab = CSharp; buildAST = true; ASTLabelType = "ASTNode"; //codeGenMakeSwitchThreshold = 5; // Some optimizations //codeGenBitsetTestThreshold = 50; //defaultErrorHandler = false; defaultErrorHandler = true; } tokens { COMPILATION_UNIT; USING_DIRECTIVES; USING_ALIAS_DIRECTIVE; USING_NAMESPACE_DIRECTIVE; GLOBAL_ATTRIBUTE_SECTIONS; GLOBAL_ATTRIBUTE_SECTION; ATTRIBUTE_SECTIONS; ATTRIBUTE_SECTION; ATTRIBUTE; QUALIFIED_IDENTIFIER; POSITIONAL_ARGLIST; POSITIONAL_ARG; NAMED_ARGLIST; NAMED_ARG; ARG_LIST; FORMAL_PARAMETER_LIST; PARAMETER_FIXED; PARAMETER_ARRAY; ATTRIB_ARGUMENT_EXPR; UNARY_MINUS; UNARY_PLUS; CLASS_BASE; STRUCT_BASE; INTERFACE_BASE; ENUM_BASE; TYPE_BODY; MEMBER_LIST; CONST_DECLARATOR; CTOR_DECL; STATIC_CTOR_DECL; DTOR_DECL; FIELD_DECL; METHOD_DECL; PROPERTY_DECL; INDEXER_DECL; UNARY_OP_DECL; BINARY_OP_DECL; CONV_OP_DECL; TYPE; STARS; ARRAY_RANK; ARRAY_RANKS; ARRAY_INIT; VAR_INIT; VAR_INIT_LIST; VAR_DECLARATOR; LOCVAR_INIT; LOCVAR_INIT_LIST; LOCVAR_DECLS; LOCAL_CONST; EXPR; EXPR_LIST; MEMBER_ACCESS_EXPR; ELEMENT_ACCESS_EXPR; INVOCATION_EXPR; POST_INC_EXPR; POST_DEC_EXPR; PAREN_EXPR; OBJ_CREATE_EXPR; DLG_CREATE_EXPR; ARRAY_CREATE_EXPR; CAST_EXPR; PTR_ELEMENT_ACCESS_EXPR; PTR_INDIRECTION_EXPR; PTR_DECLARATOR; PTR_INIT; ADDRESS_OF_EXPR; MODIFIERS; NAMESPACE_BODY; BLOCK; STMT_LIST; EMPTY_STMT; LABEL_STMT; EXPR_STMT; FOR_INIT; FOR_COND; FOR_ITER; SWITCH_SECTION; SWITCH_LABELS; SWITCH_LABEL; PP_DIRECTIVES; PP_EXPR; PP_MESSAGE; PP_BLOCK; } { //--------------------------------------------------------------------- // PRIVATE DATA MEMBERS //--------------------------------------------------------------------- private FileInfo fileinfo_; private bool NotExcluded(CodeMaskEnums codeMask, CodeMaskEnums construct) { return ((codeMask & construct) != 0 ); } public override void setFilename(string filename) { base.setFilename(filename); fileinfo_ = new FileInfo(filename); ((ASTNodeFactory) astFactory).FileInfo = fileinfo_; } private bool SingleLinePPDirectiveIsPredictedByLA(int lookAheadDepth) { if ((LA(lookAheadDepth) == PP_WARNING) || (LA(lookAheadDepth) == PP_ERROR) || (LA(lookAheadDepth) == PP_LINE) || (LA(lookAheadDepth) == PP_UNDEFINE) || (LA(lookAheadDepth) == PP_DEFINE)) { return true; } return false; } private bool PPDirectiveIsPredictedByLA(int lookAheadDepth) { if ((LA(lookAheadDepth) == PP_REGION) || (LA(lookAheadDepth) == PP_COND_IF) || (LA(lookAheadDepth) == PP_WARNING) || (LA(lookAheadDepth) == PP_ERROR) || (LA(lookAheadDepth) == PP_LINE) || (LA(lookAheadDepth) == PP_UNDEFINE) || (LA(lookAheadDepth) == PP_DEFINE)) { return true; } return false; } private bool IdentifierRuleIsPredictedByLA(int lookAheadDepth) { if ((LA(lookAheadDepth) == IDENTIFIER) || (LA(lookAheadDepth) == LITERAL_add) || (LA(lookAheadDepth) == LITERAL_remove) || (LA(lookAheadDepth) == LITERAL_get) || (LA(lookAheadDepth) == LITERAL_set) || (LA(lookAheadDepth) == LITERAL_assembly) || (LA(lookAheadDepth) == LITERAL_field) || (LA(lookAheadDepth) == LITERAL_method) || (LA(lookAheadDepth) == LITERAL_module) || (LA(lookAheadDepth) == LITERAL_param) || (LA(lookAheadDepth) == LITERAL_property) || (LA(lookAheadDepth) == LITERAL_type)) { return true; } return false; } private bool TypeRuleIsPredictedByLA(int lookAheadDepth) { if ((LA(lookAheadDepth) == DOT) || (LA(lookAheadDepth) == VOID) || (LA(lookAheadDepth) == IDENTIFIER) || (LA(lookAheadDepth) == INT) || (LA(lookAheadDepth) == BOOL) || (LA(lookAheadDepth) == STRING) || (LA(lookAheadDepth) == OBJECT) || (LA(lookAheadDepth) == BYTE) || (LA(lookAheadDepth) == CHAR) || (LA(lookAheadDepth) == DECIMAL) || (LA(lookAheadDepth) == DOUBLE) || (LA(lookAheadDepth) == FLOAT) || (LA(lookAheadDepth) == LONG) || (LA(lookAheadDepth) == SBYTE) || (LA(lookAheadDepth) == SHORT) || (LA(lookAheadDepth) == UINT) || (LA(lookAheadDepth) == ULONG) || (LA(lookAheadDepth) == USHORT)) { return true; } return false; } } //============================================================================= // Start of RULES //============================================================================= // // C# LANGUAGE SPECIFICATION // // A.2 Syntactic grammar // // The start rule for this grammar is 'compilationUnit' // // // A.2.1 Basic concepts // nonKeywordLiterals : "add" | "remove" | "get" | "set" | "assembly" | "field" | "method" | "module" | "param" | "property" | "type" ; identifier : IDENTIFIER | n:nonKeywordLiterals { #n.setType(IDENTIFIER); } ; qualifiedIdentifier : identifier ( options { greedy = true; } : DOT^ qualifiedIdentifier )? ; /* // // A.2.2 Types // */ type! { ASTNode typeBase = null; ASTNode starsBase = #[STARS, "STARS"]; } : ( ( p:predefinedTypeName { typeBase = #p; } | q:qualifiedIdentifier { typeBase = #q; } ) // typeName ( s1:STAR // pointerType { #starsBase.addChildEx(#s1); } )* | v:VOID s2:STAR { #starsBase.addChildEx(#s2); typeBase = #v; } ) r:rankSpecifiers // arrayType := nonArrayType rankSpecifiers { ## = #( [TYPE, "TYPE"], typeBase, starsBase, r ); } ; integralType : ( SBYTE | BYTE | SHORT | USHORT | INT | UINT | LONG | ULONG | CHAR ) { ## = #( [TYPE, "TYPE"], ##, [STARS, "STARS"], [ARRAY_RANKS, "ARRAY_RANKS"] ); } ; classType : ( qualifiedIdentifier // typeName | OBJECT | STRING ) { ## = #( [TYPE, "TYPE"], ##, [STARS, "STARS"], [ARRAY_RANKS, "ARRAY_RANKS"] ); } ; /* // // A.2.4 Expressions // */ argumentList : argument ( COMMA! argument )* { ## = #( [ARG_LIST, "ARG_LIST"], #argumentList ); } ; argument : expression | REF^ expression | OUT^ expression ; constantExpression : expression ; booleanExpression : expression ; expressionList : expression ( COMMA! expression )* { ## = #( [EXPR_LIST, "EXPR_LIST"], #expressionList ); } ; /* //====================================== // 14.2.1 Operator precedence and associativity // // The following table summarizes all operators in order of precedence from lowest to highest: // // PRECEDENCE SECTION CATEGORY OPERATORS // lowest (14) 14.13 Assignment = *= /= %= += -= <<= >>= &= ^= |= // (13) 14.12 Conditional ?: // (12) 14.11 Conditional OR || // (11) 14.11 Conditional AND && // (10) 14.10 Logical OR | // ( 9) 14.10 Logical XOR ^ // ( 8) 14.10 Logical AND & // ( 7) 14.9 Equality == != // ( 6) 14.9 Relational and type-testing < > <= >= is as // ( 5) 14.8 Shift << >> // ( 4) 14.7 Additive +{binary} -{binary} // ( 3) 14.7 Multiplicative * / % // ( 2) 14.6 Unary +{unary} -{unary} ! ~ ++x --x (T)x // highest ( 1) 14.5 Primary x.y f(x) a[x] x++ x-- new // typeof checked unchecked // // NOTE: In accordance with lessons gleaned from the "java.g" file supplied with ANTLR, // I have applied the following pattern to the rules for expressions: // // thisLevelExpression : // nextHigherPrecedenceExpression (OPERATOR nextHigherPrecedenceExpression)* // // which is a standard recursive definition for a parsing an expression. // */ expression : assignmentExpression { #expression = #( #[EXPR,"EXPR"], #expression ); } ; assignmentExpression : conditionalExpression ( ( ASSIGN^ | PLUS_ASSIGN^ | MINUS_ASSIGN^ | STAR_ASSIGN^ | DIV_ASSIGN^ | MOD_ASSIGN^ | BIN_AND_ASSIGN^ | BIN_OR_ASSIGN^ | BIN_XOR_ASSIGN^ | SHIFTL_ASSIGN^ | SHIFTR_ASSIGN^ ) assignmentExpression )? ; conditionalExpression : conditionalOrExpression ( QUESTION^ assignmentExpression COLON! conditionalExpression )? ; conditionalOrExpression : conditionalAndExpression ( LOG_OR^ conditionalAndExpression )* ; conditionalAndExpression : inclusiveOrExpression ( LOG_AND^ inclusiveOrExpression )* ; inclusiveOrExpression : exclusiveOrExpression ( BIN_OR^ exclusiveOrExpression )* ; exclusiveOrExpression : andExpression ( BIN_XOR^ andExpression )* ; andExpression : equalityExpression ( BIN_AND^ equalityExpression )* ; equalityExpression : relationalExpression ( ( EQUAL^ | NOT_EQUAL^ ) relationalExpression )* ; relationalExpression : shiftExpression ( ( ( LTHAN^ | GTHAN^ | LTE^ | GTE^ ) shiftExpression )* | ( IS^ | AS^ ) type ) ; shiftExpression : additiveExpression ( ( SHIFTL^ | SHIFTR^ ) additiveExpression )* ; additiveExpression : multiplicativeExpression ( ( PLUS^ | MINUS^ ) multiplicativeExpression )* ; multiplicativeExpression : unaryExpression ( ( STAR^ | DIV^ | MOD^ ) unaryExpression )* ; unaryExpression : // castExpression // ( OPEN_PAREN type CLOSE_PAREN unaryExpression )=> o:OPEN_PAREN^ { #o.setType(CAST_EXPR); } type CLOSE_PAREN! unaryExpression | // preIncrementExpression // INC^ unaryExpression | // preDecrementExpression // DEC^ unaryExpression | p:PLUS^ { #p.setType(UNARY_PLUS ); } unaryExpression | m:MINUS^ { #m.setType(UNARY_MINUS ); } unaryExpression | LOG_NOT^ unaryExpression | BIN_NOT^ unaryExpression | // pointerIndirectionExpression // s:STAR^ { #s.setType(PTR_INDIRECTION_EXPR); } unaryExpression | // addressofExpression // b:BIN_AND^ { #b.setType(ADDRESS_OF_EXPR); } unaryExpression | primaryExpression ; basicPrimaryExpression // primaryNoArrayCreationExpression // : ( literal | identifier // simpleName | // parenthesizedExpression // o:OPEN_PAREN^ { #o.setType(PAREN_EXPR); } assignmentExpression CLOSE_PAREN! | THIS^ // thisAccess | BASE^ ( DOT! identifier // baseAccess | OPEN_BRACK! expressionList CLOSE_BRACK! // baseAccess ) | newExpression |! to_t:TYPEOF^ OPEN_PAREN! ( { ((LA(1) == VOID) && (LA(2) == CLOSE_PAREN)) }? to_v:voidAsType CLOSE_PAREN! // typeofExpression { ## = #( #to_t, #to_v ); } | to_typ:type CLOSE_PAREN! // typeofExpression { ## = #( #to_t, #to_typ ); } ) | SIZEOF^ OPEN_PAREN! qualifiedIdentifier CLOSE_PAREN! // sizeofExpression | CHECKED^ OPEN_PAREN! expression CLOSE_PAREN! // checkedExpression | UNCHECKED^ OPEN_PAREN! expression CLOSE_PAREN! // uncheckedExpression |! //-- // memberAccess ma_typ:predefinedType dt:DOT ma_id:identifier { #dt.setType(MEMBER_ACCESS_EXPR); ## = #( #dt, #ma_typ, #ma_id ); } ) ; primaryExpression! : bexpr:basicPrimaryExpression { ## = #bexpr; } ( options { greedy = true; } : ( // invocationExpression ::= primaryExpression OPEN_PAREN ( argumentList )? CLOSE_PAREN // op:OPEN_PAREN { #a = null; } ( a:argumentList )? CLOSE_PAREN! { #op.setType(INVOCATION_EXPR); ## = #( #op, #bexpr, #a ); } | // elementAccess ::= primaryNoArrayCreationExpression OPEN_BRACK expressionList CLOSE_BRACK // pointerElementAccess ::= primaryNoArrayCreationExpression OPEN_BRACK expression CLOSE_BRACK // ob:OPEN_BRACK elist:expressionList CLOSE_BRACK! { #ob.setType(ELEMENT_ACCESS_EXPR); ## = #( #ob, #bexpr, #elist ); } | // memberAccess ::= primaryExpression DOT identifier // dt:DOT ma_id:identifier { #dt.setType(MEMBER_ACCESS_EXPR); ## = #( #dt, #bexpr, #ma_id ); } | ic:INC // postIncrementExpression { #ic.setType(POST_INC_EXPR); ## = #( #ic, #bexpr ); } | dc:DEC // postDecrementExpression { #dc.setType(POST_DEC_EXPR); ## = #( #dc, #bexpr ); } | pm_deref:DEREF pm_id:identifier // pointerMemberAccess { ## = #( #pm_deref, #bexpr, #pm_id ); } ) { #bexpr = ##; } )* ; newExpression! : n:NEW typ:type ( // objectCreationExpression ::= NEW type OPEN_PAREN ( argumentList )? CLOSE_PAREN // delegateCreationExpression ::= NEW delegateType OPEN_PAREN expression CLOSE_PAREN // NOTE: Will ALSO match 'delegateCreationExpression' // OPEN_PAREN! ( arglist:argumentList )? CLOSE_PAREN! { #n.setType(OBJ_CREATE_EXPR); ## = #( #n, #typ, #arglist ); } | // arrayCreationExpression ::= NEW arrayType arrayInitializer // ar_init:arrayInitializer { #n.setType(ARRAY_CREATE_EXPR); ## = #( #n, #typ, #ar_init ); } | // arrayCreationExpression ::= NEW nonArrayType OPEN_BRACK expressionList CLOSE_BRACK ( rankSpecifiers )? ( arrayInitializer )? // OPEN_BRACK! elist:expressionList CLOSE_BRACK! ar_spec2:rankSpecifiers ( ar_init2:arrayInitializer )? { #n.setType(ARRAY_CREATE_EXPR); ## = #( #n, #typ, #elist, #ar_spec2, #ar_init2 ); } ) ; literal : TRUE // BOOLEAN_LITERAL | FALSE // BOOLEAN_LITERAL | INT_LITERAL | UINT_LITERAL | LONG_LITERAL | ULONG_LITERAL | DECIMAL_LITERAL | FLOAT_LITERAL | DOUBLE_LITERAL | CHAR_LITERAL | STRING_LITERAL | NULL // NULL_LITERAL ; predefinedType : ( BOOL | BYTE | CHAR | DECIMAL | DOUBLE | FLOAT | INT | LONG | OBJECT | SBYTE | SHORT | STRING | UINT | ULONG | USHORT ) { ## = #( [TYPE, "TYPE"], ##, [STARS, "STARS"], [ARRAY_RANKS, "ARRAY_RANKS"] ); } ; predefinedTypeName : BOOL | BYTE | CHAR | DECIMAL | DOUBLE | FLOAT | INT | LONG | OBJECT | SBYTE | SHORT | STRING | UINT | ULONG | USHORT ; /* // // A.2.5 Statements // */ statement : { (IdentifierRuleIsPredictedByLA(1) && (LA(2) == COLON)) }? labeledStatement | { ((LA(1) == CONST) && TypeRuleIsPredictedByLA(2) && IdentifierRuleIsPredictedByLA(3)) || (TypeRuleIsPredictedByLA(1) && IdentifierRuleIsPredictedByLA(2)) }? declarationStatement | ( ( CONST )? type identifier )=> declarationStatement | embeddedStatement | preprocessorDirective[CodeMaskEnums.Statements] ; embeddedStatement : block | //emptyStatement // s:SEMI { #s.setType(EMPTY_STMT); } | expressionStatement | selectionStatement | iterationStatement | jumpStatement | tryStatement | checkedStatement | uncheckedStatement | lockStatement | usingStatement | unsafeStatement | fixedStatement ; body : block | s:SEMI { #s.setType(EMPTY_STMT); } ; block : o:OPEN_CURLY^ { #o.setType(BLOCK); } ( statement )* CLOSE_CURLY ; statementList : ( statement )+ { #statementList = #( [STMT_LIST, "STMT_LIST"], #statementList ); } ; labeledStatement : id:identifier c:COLON^ { #c.setType(LABEL_STMT); } stmt:statement ; declarationStatement : localVariableDeclaration SEMI! | localConstantDeclaration SEMI! ; localVariableDeclaration : type localVariableDeclarators { ## = #( [LOCVAR_DECLS, "LOCVAR_DECLS"], ## ); } ; localVariableDeclarators : localVariableDeclarator ( COMMA! localVariableDeclarator )* ; localVariableDeclarator : identifier ( ASSIGN! localVariableInitializer )? { ## = #( [VAR_DECLARATOR, "VAR_DECLARATOR"], ## ); } ; localVariableInitializer : ( expression | arrayInitializer ) { #localVariableInitializer = #( [LOCVAR_INIT, "LOCVAR_INIT"], #localVariableInitializer ); } ; localConstantDeclaration : c:CONST^ { #c.setType(LOCAL_CONST); } type constantDeclarators ; constantDeclarators : constantDeclarator ( COMMA! constantDeclarator )* ; constantDeclarator : identifier a:ASSIGN^ { #a.setType(CONST_DECLARATOR); } constantExpression ; expressionStatement : statementExpression s:SEMI^ { #s.setType(EXPR_STMT); } ; statementExpression! : aexpr:assignmentExpression { ## = #aexpr; } /* : invocationExpression | objectCreationExpression | assignmentExpression | postIncrementExpression | postDecrementExpression | preIncrementExpression | preDecrementExpression */ ; selectionStatement : ifStatement | switchStatement ; ifStatement : IF^ OPEN_PAREN! booleanExpression CLOSE_PAREN! embeddedStatement ( options { greedy = true; } : elseStatement )? ; elseStatement : ELSE^ embeddedStatement ; switchStatement : SWITCH^ OPEN_PAREN! expression CLOSE_PAREN! switchBlock ; switchBlock : OPEN_CURLY^ ( switchSections )? CLOSE_CURLY ; switchSections : ( switchSection )+ ; switchSection! : lbl:switchLabels stmt:statementList { ## = #( [SWITCH_SECTION, "SWITCH_SECTION"], #lbl, #stmt ); } ; switchLabels : ( switchLabel )+ { #switchLabels = #( [SWITCH_LABELS, "SWITCH_LABELS"], #switchLabels ); } ; switchLabel : CASE^ constantExpression COLON! | DEFAULT^ COLON! ; iterationStatement : whileStatement | doStatement | forStatement | foreachStatement ; whileStatement : WHILE^ OPEN_PAREN! booleanExpression CLOSE_PAREN! embeddedStatement ; doStatement : DO^ embeddedStatement WHILE! OPEN_PAREN! booleanExpression CLOSE_PAREN! SEMI! ; forStatement : FOR^ OPEN_PAREN! forInitializer SEMI! forCondition SEMI! forIterator CLOSE_PAREN! embeddedStatement ; forInitializer : ( { (TypeRuleIsPredictedByLA(1) && IdentifierRuleIsPredictedByLA(2)) }? localVariableDeclaration | ( type identifier )=> localVariableDeclaration | statementExpressionList )? { #forInitializer = #( [FOR_INIT, "FOR_INIT"], #forInitializer ); } ; forCondition : ( booleanExpression )? { #forCondition = #( [FOR_COND, "FOR_COND"], #forCondition ); } ; forIterator : ( statementExpressionList )? { #forIterator = #( [FOR_ITER, "FOR_ITER"], #forIterator ); } ; statementExpressionList : statementExpression ( COMMA! statementExpression )* ; foreachStatement! : f:FOREACH OPEN_PAREN! t:type id:identifier IN! e:expression CLOSE_PAREN! s:embeddedStatement { ## = #( #f, #( [LOCVAR_DECLS], #t, #( [VAR_DECLARATOR], #id ) ), #e, #s ); } ; jumpStatement : breakStatement | continueStatement | gotoStatement | returnStatement | throwStatement ; breakStatement : BREAK^ SEMI! ; continueStatement : CONTINUE^ SEMI! ; gotoStatement : GOTO^ ( identifier SEMI! | CASE constantExpression SEMI! | DEFAULT SEMI! ) ; returnStatement : RETURN^ ( expression )? SEMI! ; throwStatement : THROW^ ( expression )? SEMI! ; tryStatement : TRY^ block ( finallyClause | catchClauses ( finallyClause )? ) ; catchClauses : ( options { greedy = true; } : specificCatchClause )+ ( generalCatchClause )? | generalCatchClause ; specificCatchClause! : c:CATCH^ OPEN_PAREN! ctype:classType ( id:identifier )? CLOSE_PAREN! b:block { if (#id == null) ## = #( #c, #b, #ctype ); else ## = #( #c, #b, #( [LOCVAR_DECLS], #ctype, #( [VAR_DECLARATOR], #id ) ) ); } ; generalCatchClause : CATCH^ block ; finallyClause : FINALLY^ block ; checkedStatement : CHECKED^ block ; uncheckedStatement : UNCHECKED^ block ; lockStatement : LOCK^ OPEN_PAREN! expression CLOSE_PAREN! embeddedStatement ; usingStatement : USING^ OPEN_PAREN! resourceAcquisition CLOSE_PAREN! embeddedStatement ; unsafeStatement : UNSAFE^ block ; resourceAcquisition : { (TypeRuleIsPredictedByLA(1) && IdentifierRuleIsPredictedByLA(2)) }? localVariableDeclaration | ( type identifier )=> localVariableDeclaration | expression ; compilationUnit : justPreprocessorDirectives usingDirectives globalAttributes namespaceMemberDeclarations EOF! { ## = #( [COMPILATION_UNIT, "COMPILATION_UNIT"], ## ); } ; usingDirectives : ( options { greedy = true; } : { !PPDirectiveIsPredictedByLA(1) }? usingDirective | ( preprocessorDirective[CodeMaskEnums.UsingDirectives] )=> preprocessorDirective[CodeMaskEnums.UsingDirectives] )* { #usingDirectives = #( [USING_DIRECTIVES, "USING_DIRECTIVES"], ## ); } ; usingDirective : u:USING^ ( // UsingAliasDirective { (IdentifierRuleIsPredictedByLA(1) && (LA(2) == ASSIGN)) }? identifier ASSIGN! qualifiedIdentifier SEMI! { #u.setType(USING_ALIAS_DIRECTIVE); } | // UsingNamespaceDirective qualifiedIdentifier SEMI! { #u.setType(USING_NAMESPACE_DIRECTIVE); } ) ; namespaceMemberDeclarations : ( options { greedy = true; } : { PPDirectiveIsPredictedByLA(1) }? preprocessorDirective[CodeMaskEnums.NamespaceMemberDeclarations] | namespaceMemberDeclaration )* ; namespaceMemberDeclaration : namespaceDeclaration | a:attributes! m:modifiers! typeDeclaration[#a, #m] ; typeDeclaration [AST attribs, AST modifiers] : classDeclaration[attribs, modifiers] | structDeclaration[attribs, modifiers] | interfaceDeclaration[attribs, modifiers] | enumDeclaration[attribs, modifiers] | delegateDeclaration[attribs, modifiers] ; namespaceDeclaration : NAMESPACE^ qualifiedIdentifier namespaceBody ( options { greedy = true; } : SEMI! )? ; namespaceBody : o:OPEN_CURLY^ { #o.setType(NAMESPACE_BODY); } usingDirectives namespaceMemberDeclarations CLOSE_CURLY ; modifiers : ( modifier )* { #modifiers = #( [MODIFIERS, "MODIFIERS"], #modifiers ); } ; modifier : ( ABSTRACT | NEW | OVERRIDE | PUBLIC | PROTECTED | INTERNAL | PRIVATE | SEALED | STATIC | VIRTUAL | EXTERN | READONLY | UNSAFE | VOLATILE ) ; // // A.2.6 Classes // classDeclaration! [AST attribs, AST modifiers] : cl:CLASS id:identifier ba:classBase bo:classBody ( options { greedy = true; } : SEMI! )? { ## = #( #cl, #attribs, #modifiers, #id, #ba, #bo ); } ; classBase : ( COLON! type ( COMMA! type )* )? { #classBase = #( [CLASS_BASE, "CLASS_BASE"], #classBase ); } ; classBody : o:OPEN_CURLY^ { #o.setType(TYPE_BODY); } classMemberDeclarations CLOSE_CURLY ; classMemberDeclarations : ( options { greedy = true; } : { PPDirectiveIsPredictedByLA(1) }? preprocessorDirective[CodeMaskEnums.ClassMemberDeclarations] | classMemberDeclaration )* { ## = #( [MEMBER_LIST, "MEMBER_LIST"], ## ); } ; classMemberDeclaration : a:attributes! m:modifiers! ( destructorDeclaration[#a, #m] | typeMemberDeclaration[#a, #m] ) ; typeMemberDeclaration [AST attribs, AST modifiers] { bool IsBinaryOp = false; AST OpParams = null; } :! // constantDeclaration c:CONST t:type cdecl:constantDeclarators SEMI! { ## = #( #c, #attribs, #modifiers, #t, #cdecl ); } |! // eventDeclaration ev:EVENT ev_typ:type ( { IdentifierRuleIsPredictedByLA(1) && (LA(2)==ASSIGN || LA(2)==SEMI ||LA(2)==COMMA) }? ev_v:variableDeclarators SEMI! { ## = #( #ev, #attribs, #modifiers, #ev_typ, #ev_v ); } | ev_id:qualifiedIdentifier OPEN_CURLY! ev_e:eventAccessorDeclarations ev_c:CLOSE_CURLY { ## = #( #ev, #attribs, #modifiers, #ev_typ, #ev_id, #ev_e, #ev_c ); } ) |! // constructorDeclaration cd_id:identifier OPEN_PAREN! ( cd_fp:formalParameterList )? CLOSE_PAREN! ( cd_ci:constructorInitializer )? cd_cb:constructorBody { if ( ((ASTNode) modifiers).GetFirstChildOfType(STATIC) == null ) { ## = #( [CTOR_DECL, "CTOR_DECL"], #attribs, #modifiers, #cd_id, #cd_cb, #cd_fp, #cd_ci ); ##.CopyPositionFrom( #cd_id ); } else { ## = #( [STATIC_CTOR_DECL, "STATIC_CTOR_DECL"], #attribs, #modifiers, #cd_id, #cd_cb ); ##.CopyPositionFrom( #cd_id ); } } |! // methodDeclaration { ((LA(1) == VOID) && (LA(2) != STAR)) }? mdv_rt:voidAsType mdv_mn:qualifiedIdentifier mdv_opn:OPEN_PAREN! ( mdv_fp:formalParameterList )? CLOSE_PAREN! mdv_mb:methodBody { ## = #( [METHOD_DECL, "METHOD_DECL"], #attribs, #modifiers, #mdv_rt, #mdv_mn, #mdv_mb, #mdv_fp ); ##.CopyPositionFrom( #mdv_mn ); } |! typ1:type ( // unaryOperatorDeclarator or binaryOperatorDeclarator OPERATOR! od_op:overloadableOperator OPEN_PAREN! od_f1:fixedOperatorParameter ( COMMA! od_f2:fixedOperatorParameter { IsBinaryOp = true; } )? { OpParams = #( [FORMAL_PARAMETER_LIST, "FORMAL_PARAMETER_LIST"], #od_f1, #od_f2 ); } CLOSE_PAREN! { if (!IsBinaryOp) { if (#od_op.Type == PLUS) #od_op.Type = UNARY_PLUS; else if (#od_op.Type == MINUS) #od_op.Type = UNARY_MINUS; ## = #( [UNARY_OP_DECL, "UNARY_OP_DECL"], #attribs, #modifiers, #typ1, #od_op, OpParams ); } else { ## = #( [BINARY_OP_DECL, "BINARY_OP_DECL"], #attribs, #modifiers, #typ1, #od_op, OpParams ); } ##.CopyPositionFrom( #od_op ); } od_body:operatorBody { ##.addChildEx(#od_body); } | // fieldDeclaration { IdentifierRuleIsPredictedByLA(1) && (LA(2)==ASSIGN || LA(2)==SEMI ||LA(2)==COMMA) }? fd_v:variableDeclarators SEMI! { ## = #( [FIELD_DECL, "FIELD_DECL"], #attribs, #modifiers, #typ1, #fd_v ); } | qid1:qualifiedIdentifier ( // propertyDeclaration OPEN_CURLY! pd_a:accessorDeclarations pd_c:CLOSE_CURLY { ## = #( [PROPERTY_DECL, "PROPERTY_DECL"], #attribs, #modifiers, #typ1, #qid1, #pd_a, #pd_c ); ##.CopyPositionFrom( #qid1 ); } | // methodDeclaration OPEN_PAREN! ( md_fp:formalParameterList )? CLOSE_PAREN! md_mb:methodBody { ## = #( [METHOD_DECL, "METHOD_DECL"], #attribs, #modifiers, #typ1, #qid1, #md_mb, #md_fp ); ##.CopyPositionFrom( #qid1 ); } | // indexerDeclaration DOT! ixq_t:THIS OPEN_BRACK! ixq_fp:formalParameterList CLOSE_BRACK! OPEN_CURLY! ixq_adecls:accessorDeclarations ixq_c:CLOSE_CURLY { ## = #( [INDEXER_DECL, "INDEXER_DECL"], #attribs, #modifiers, #typ1, #qid1, #ixq_t, #ixq_fp, #ixq_adecls, #ixq_c ); ##.CopyPositionFrom( #ixq_t ); } ) | // indexerDeclaration ix_t:THIS OPEN_BRACK! ix_fp:formalParameterList CLOSE_BRACK! OPEN_CURLY! ix_adecls:accessorDeclarations ix_c:CLOSE_CURLY { ## = #( [INDEXER_DECL, "INDEXER_DECL"], #attribs, #modifiers, #typ1, #qid1, #ix_t, #ix_fp, #ix_adecls, #ix_c ); ##.CopyPositionFrom( #ix_t ); } ) |! imp:IMPLICIT OPERATOR! imp_typ1:type OPEN_PAREN! imp_params:oneOperatorParameter CLOSE_PAREN! // conversionOperatorDeclarator { ## = #( [CONV_OP_DECL, "CONV_OP_DECL"], #attribs, #modifiers, #imp, #imp_typ1, #imp_params ); ##.CopyPositionFrom( #imp ); } imp_body:operatorBody { ##.addChildEx(#imp_body); } |! exp:EXPLICIT OPERATOR! exp_typ1:type OPEN_PAREN! exp_params:oneOperatorParameter CLOSE_PAREN! // conversionOperatorDeclarator { ## = #( [CONV_OP_DECL, "CONV_OP_DECL"], #attribs, #modifiers, #exp, #exp_typ1, #exp_params ); ##.CopyPositionFrom( #exp ); } exp_body:operatorBody { ##.addChildEx(#exp_body); } | typeDeclaration[attribs, modifiers] ; variableDeclarators : variableDeclarator ( COMMA! variableDeclarator )* ; variableDeclarator : identifier ( ASSIGN! variableInitializer )? { ## = #( [VAR_DECLARATOR, "VAR_DECLARATOR"], ## ); } ; variableInitializer : ( expression | arrayInitializer | stackallocInitializer ) { #variableInitializer = #( [VAR_INIT, "VAR_INIT"], #variableInitializer ); } ; returnType : { ((LA(1) == VOID) && (LA(2) != STAR)) }? voidAsType | type ; methodBody : b:body ; formalParameterList : fa:attributes! ( fixedParameters[#fa] ( COMMA! pa:attributes! parameterArray[#pa] )? | parameterArray[#fa] ) { ## = #( [FORMAL_PARAMETER_LIST, "FORMAL_PARAMETER_LIST"], ## ); } ; fixedParameters [AST attribs] : fixedParameter[attribs] ( options { greedy = true; } : COMMA! a:attributes! fixedParameter[#a] )* ; fixedParameter! [AST attribs] : ( mod:parameterModifier )? typ:type id:identifier { ## = #( [PARAMETER_FIXED, "PARAMETER_FIXED"], #attribs, #typ, #id, #mod ); ##.CopyPositionFrom( #id ); } ; parameterModifier : REF | OUT ; parameterArray! [AST attribs] // : PARAMS! arrayType identifier : p:PARAMS t:type id:identifier { ## = #( #p, #attribs, #t, #id ); } ; accessorDeclarations! : a1:attributes ( g1:getAccessorDeclaration[#a1] { ## = #g1; } ( a2:attributes s1:setAccessorDeclaration[#a2] { #g1.setNextSibling(#s1); #s1.setPreviousSibling(#g1); } )? | s2:setAccessorDeclaration[#a1] { ## = #s2; } ( a3:attributes g2:getAccessorDeclaration[#a3] { #s2.setNextSibling(#g2); #g2.setPreviousSibling(#s2); } )? ) ; getAccessorDeclaration! [AST attribs] : g:"get" abody:accessorBody { ## = #( #g, #attribs, #abody ); } ; setAccessorDeclaration! [AST attribs] : s:"set" abody:accessorBody { ## = #( #s, #attribs, #abody ); } ; accessorBody : body ; eventAccessorDeclarations! : a1:attributes ( add1:addAccessorDeclaration[#a1] a2:attributes rem1:removeAccessorDeclaration[#a2] { ## = #add1; #add1.setNextSibling(#rem1); #rem1.setPreviousSibling(#add1); } | rem2:removeAccessorDeclaration[#a1] a3:attributes add2:addAccessorDeclaration[#a3] { ## = #add2; #add2.setNextSibling(#rem2); #rem2.setPreviousSibling(#add2); } ) ; addAccessorDeclaration! [AST attribs] : a:"add" b:block { ## = #( #a, #attribs, #b ); } ; removeAccessorDeclaration! [AST attribs] : r:"remove" b:block { ## = #( #r, #attribs, #b ); } ; overloadableOperator // Unary-or-Binary Operators // : PLUS | MINUS // Unary-only Operators // | LOG_NOT | BIN_NOT | INC | DEC | TRUE //"true" | FALSE //"false" // Binary-only Operators // | STAR | DIV | MOD | BIN_AND | BIN_OR | BIN_XOR | SHIFTL | SHIFTR | EQUAL | NOT_EQUAL | GTHAN | LTHAN | GTE | LTE ; oneOperatorParameter : fixedOperatorParameter { ## = #( [FORMAL_PARAMETER_LIST, "FORMAL_PARAMETER_LIST"], ## ); } ; fixedOperatorParameter! : typ:type id:identifier { ## = #( [PARAMETER_FIXED, "PARAMETER_FIXED"], [ATTRIBUTE_SECTIONS, "ATTRIBUTE_SECTIONS"], #typ, #id ); ##.CopyPositionFrom( #id ); } ; operatorBody : b:body ; constructorInitializer : c:COLON! ( BASE^ OPEN_PAREN! ( argumentList )? CLOSE_PAREN! | THIS^ OPEN_PAREN! ( argumentList )? CLOSE_PAREN! ) ; constructorBody : b:body ; destructorDeclaration! [AST attribs, AST modifiers] : b:BIN_NOT! id:identifier OPEN_PAREN! CLOSE_PAREN! dbody:destructorBody { ## = #( [DTOR_DECL, "DTOR_DECL"], #attribs, #modifiers, #id, #dbody ); ##.CopyPositionFrom( #id ); } ; destructorBody : b:body ; // // A.2.7 Structs // structDeclaration! [AST attribs, AST modifiers] : st:STRUCT^ id:identifier si:structInterfaces sb:structBody ( options { greedy = true; } : SEMI! )? { ## = #( #st, #attribs, #modifiers, #id, #si, #sb ); } ; structInterfaces : ( COLON! type ( COMMA! type )* )? { ## = #( [STRUCT_BASE, "STRUCT_BASE"], #structInterfaces ); } ; structBody : o:OPEN_CURLY^ { #o.setType(TYPE_BODY); } structMemberDeclarations CLOSE_CURLY ; structMemberDeclarations : ( options { greedy = true; } : { PPDirectiveIsPredictedByLA(1) }? preprocessorDirective[CodeMaskEnums.StructMemberDeclarations] | structMemberDeclaration )* { ## = #( [MEMBER_LIST, "MEMBER_LIST"], ## ); } ; structMemberDeclaration : a:attributes! m:modifiers! typeMemberDeclaration[#a, #m] ; // // A.2.8 Arrays // nonArrayType : type ; rankSpecifiers : // CONFLICT: ANTLR says this about this line: // ECMA-CSharp.g:1295: warning: nondeterminism upon // ECMA-CSharp.g:1295: k==1:OPEN_BRACK // ECMA-CSharp.g:1295: k==2:COMMA,CLOSE_BRACK // ECMA-CSharp.g:1295: between alt 1 and exit branch of block // !FIXME! -- if possible, can't see the problem right now. ( options { greedy = true; } : rankSpecifier )* //( rankSpecifier )+ { #rankSpecifiers = #( [ARRAY_RANKS, "ARRAY_RANKS"], #rankSpecifiers ); } ; rankSpecifier : o:OPEN_BRACK^ { #o.setType(ARRAY_RANK); } ( options { greedy = true; } : COMMA )* CLOSE_BRACK! ; arrayInitializer : o:OPEN_CURLY^ { #o.setType(ARRAY_INIT); } ( CLOSE_CURLY | variableInitializerList (COMMA!)? CLOSE_CURLY ) ; variableInitializerList : variableInitializer ( options { greedy = true; } : COMMA! variableInitializer )* { #variableInitializerList = #( [VAR_INIT_LIST, "VAR_INIT_LIST"], #variableInitializerList ); } ; // // A.2.9 Interfaces // interfaceDeclaration! [AST attribs, AST modifiers] : iface:INTERFACE id:identifier ibase:interfaceBase ibody:interfaceBody ( options { greedy = true; } : SEMI! )? { ## = #( #iface, #attribs, #modifiers, #id, #ibase, #ibody ); } ; interfaceBase : ( COLON! type ( COMMA! type )* )? { ## = #( [INTERFACE_BASE, "INTERFACE_BASE"], #interfaceBase ); } ; interfaceBody : o:OPEN_CURLY^ { #o.setType(TYPE_BODY); } interfaceMemberDeclarations CLOSE_CURLY ; interfaceMemberDeclarations : ( options { greedy = true; } : { PPDirectiveIsPredictedByLA(1) }? preprocessorDirective[CodeMaskEnums.InterfaceMemberDeclarations] | interfaceMemberDeclaration )* { ## = #( [MEMBER_LIST, "MEMBER_LIST"], ## ); } ; interfaceMemberDeclaration { ASTNode modifiers = #[MODIFIERS, "MODIFIERS"]; } : attribs:attributes! ( n:NEW! { modifiers.addChildEx(#n); } )? (! // interfaceMethodDeclaration { ((LA(1) == VOID) && (LA(2) != STAR)) }? im1_rt:voidAsType im1_id:identifier OPEN_PAREN! ( im1_fp:formalParameterList )? CLOSE_PAREN! im1_s:SEMI { #im1_s.setType(EMPTY_STMT); } { ## = #( [METHOD_DECL, "METHOD_DECL"], #attribs, #modifiers, #im1_rt, #im1_id, #im1_s, #im1_fp ); ##.CopyPositionFrom( #im1_id ); } |! typ1:type ( // interfaceIndexerDeclaration ix_t:THIS OPEN_BRACK! ix_fp:formalParameterList CLOSE_BRACK! OPEN_CURLY! ix_acc:interfaceAccessors ix_c:CLOSE_CURLY { ## = #( [INDEXER_DECL, "INDEXER_DECL"], #attribs, #modifiers, #typ1, #ix_t, #ix_fp, #ix_acc, #ix_c ); ##.CopyPositionFrom( #ix_t ); } | id1:identifier ( // interfaceMethodDeclaration OPEN_PAREN! ( im2_fp:formalParameterList )? CLOSE_PAREN! im2_s:SEMI { #im2_s.setType(EMPTY_STMT); } { ## = #( [METHOD_DECL, "METHOD_DECL"], #attribs, #modifiers, #typ1, #id1, #im2_s, #im2_fp ); ##.CopyPositionFrom( #id1 ); } | // interfacePropertyDeclaration OPEN_CURLY! ip_acc:interfaceAccessors ip_c:CLOSE_CURLY { ## = #( [PROPERTY_DECL, "PROPERTY_DECL"], #attribs, #modifiers, #typ1, #id1, #ip_acc, #ip_c ); ##.CopyPositionFrom( #id1 ); } ) ) | // interfaceEventDeclaration ev:EVENT ev_typ:type ev_id:identifier SEMI! { ## = #( #ev, #attribs, #modifiers, #ev_typ, #( [VAR_DECLARATOR, "VAR_DECLARATOR"], #ev_id ) ); } ) ; interfaceAccessors! : a1:attributes ( g1:getAccessorDeclaration[#a1] { ## = #g1; } ( a2:attributes s1:setAccessorDeclaration[#a2] { #g1.setNextSibling(#s1); #s1.setPreviousSibling(#g1); } )? | s2:setAccessorDeclaration[#a1] { ## = #s2; } ( a3:attributes g2:getAccessorDeclaration[#a3] { #s2.setNextSibling(#g2); #g2.setPreviousSibling(#s2); } )? ) ; // // A.2.10 Enums // enumDeclaration! [AST attribs, AST modifiers] : en:ENUM id:identifier ebase:enumBase ebody:enumBody ( options { greedy = true; } : SEMI! )? { ## = #( #en, #attribs, #modifiers, #id, #ebase, #ebody ); } ; enumBase! { bool empty = true; } : ( c:COLON { #c.setType(ENUM_BASE); empty = false; } t:integralType )? { if (empty) { ## = #[ENUM_BASE, "ENUM_BASE"]; } else { ## = #( #c, #t ); } } ; enumBody : o:OPEN_CURLY^ { #o.setType(TYPE_BODY); } ( enumMemberDeclarations ( COMMA! )? )? CLOSE_CURLY ; enumMemberDeclarations : a1:attributes! enumMemberDeclaration[#a1] ( options { greedy = true; } : COMMA! a2:attributes! enumMemberDeclaration[#a2] )* { ## = #( [MEMBER_LIST, "MEMBER_LIST"], ## ); } ; enumMemberDeclaration! [AST attribs] : id:identifier ( ASSIGN! cexpr:constantExpression )? { ## = #( #id, #attribs, #cexpr ); } ; // // A.2.11 Delegates // delegateDeclaration! [AST attribs, AST modifiers] { AST typ = null; } : dlg:DELEGATE ( { ((LA(1) == VOID) && IdentifierRuleIsPredictedByLA(2)) }? typ1:voidAsType { typ = #typ1; } | typ2:type { typ = #typ2; } ) id:identifier OPEN_PAREN! ( fp:formalParameterList )? CLOSE_PAREN! SEMI! { ## = #( #dlg, #attribs, #modifiers, #typ, #id, #fp ); } ; // // A.2.12 Attributes // globalAttributes : ( options { greedy = true; } : { !PPDirectiveIsPredictedByLA(1) }? globalAttributeSection | ( preprocessorDirective[CodeMaskEnums.GlobalAttributes] )=> preprocessorDirective[CodeMaskEnums.GlobalAttributes] )* { #globalAttributes = #( [GLOBAL_ATTRIBUTE_SECTIONS, "GLOBAL_ATTRIBUTE_SECTIONS"], #globalAttributes ); } ; globalAttributeSection : o:OPEN_BRACK^ { #o.setType(GLOBAL_ATTRIBUTE_SECTION); } "assembly"! COLON! attributeList ( COMMA! )? CLOSE_BRACK! ; attributes : ( options { greedy = true; } : { !PPDirectiveIsPredictedByLA(1) }? attributeSection | ( preprocessorDirective[CodeMaskEnums.Attributes] )=> preprocessorDirective[CodeMaskEnums.Attributes] )* { #attributes = #( [ATTRIBUTE_SECTIONS, "ATTRIBUTE_SECTIONS"], #attributes ); } ; attributeSection : o:OPEN_BRACK^ { #o.setType(ATTRIBUTE_SECTION); } ( attributeTarget COLON! )? attributeList ( COMMA! )? CLOSE_BRACK! ; attributeTarget : "field" | EVENT | "method" | "module" | "param" | "property" | RETURN | "type" ; attributeList : attribute ( options { greedy = true; } : COMMA! attribute )* ; attribute : ( predefinedTypeName | qualifiedIdentifier ) ( attributeArguments )? { #attribute = #( [ATTRIBUTE, "ATTRIBUTE"], #attribute ); } ; attributeArguments : OPEN_PAREN! ( CLOSE_PAREN! | { (IdentifierRuleIsPredictedByLA(1) && (LA(2) == ASSIGN)) }? namedArgumentList CLOSE_PAREN! | positionalArgumentList ( COMMA! namedArgumentList )? CLOSE_PAREN! ) ; positionalArgumentList : positionalArgument ( // CONFLICT: ANTLR thinks this is ambiguous, because // in rule 'attributeArguments' a COMMA also // separates positionalArgument & namedArgument. // !FIXME! if possible. options { greedy = true; } : COMMA! positionalArgument )* { #positionalArgumentList = #( [POSITIONAL_ARGLIST, "POSITIONAL_ARGLIST"], #positionalArgumentList ); } ; positionalArgument : attributeArgumentExpression { #positionalArgument = #( [POSITIONAL_ARG, "POSITIONAL_ARG"], #positionalArgument ); } ; namedArgumentList : namedArgument ( COMMA! namedArgument )* { #namedArgumentList = #( [NAMED_ARGLIST, "NAMED_ARGLIST"], #namedArgumentList ); } ; namedArgument : identifier ASSIGN! attributeArgumentExpression { #namedArgument = #( [NAMED_ARG, "NAMED_ARG"], #namedArgument ); } ; attributeArgumentExpression : expression { #attributeArgumentExpression = #( [ATTRIB_ARGUMENT_EXPR, "ATTRIB_ARGUMENT_EXPR"], #attributeArgumentExpression ); } ; // // A.3 Grammar extensions for unsafe code // fixedStatement // : FIXED^ OPEN_PAREN! pointerType fixedPointerDeclarators CLOSE_PAREN! embeddedStatement : FIXED^ OPEN_PAREN! type fixedPointerDeclarators CLOSE_PAREN! embeddedStatement ; fixedPointerDeclarators : fixedPointerDeclarator ( COMMA! fixedPointerDeclarator )* ; fixedPointerDeclarator : identifier ASSIGN! fixedPointerInitializer { ## = #( [PTR_DECLARATOR, "PTR_DECLARATOR"], ## ); } ; fixedPointerInitializer : expression { ## = #( [PTR_INIT, "PTR_INIT"], ## ); } ; stackallocInitializer : STACKALLOC^ qualifiedIdentifier OPEN_BRACK! expression CLOSE_BRACK! ; // // A.1.10 Pre-processing directives // justPreprocessorDirectives : ( options { greedy = true; } : { SingleLinePPDirectiveIsPredictedByLA(1) }? singleLinePreprocessorDirective | ( preprocessorDirective[CodeMaskEnums.PreprocessorDirectivesOnly] )=> preprocessorDirective[CodeMaskEnums.PreprocessorDirectivesOnly] )* { ## = #( [PP_DIRECTIVES, "PP_DIRECTIVES"], ## ); } ; preprocessorDirective [CodeMaskEnums codeMask] : PP_DEFINE^ PP_IDENT | PP_UNDEFINE^ PP_IDENT | lineDirective | PP_ERROR^ ppMessage | PP_WARNING^ ppMessage | regionDirective[codeMask] | conditionalDirective[codeMask] ; singleLinePreprocessorDirective : PP_DEFINE^ PP_IDENT | PP_UNDEFINE^ PP_IDENT | lineDirective | PP_ERROR^ ppMessage | PP_WARNING^ ppMessage ; lineDirective : PP_LINE^ ( DEFAULT | PP_NUMBER ( PP_FILENAME )? ) ; regionDirective! [CodeMaskEnums codeMask] : reg:PP_REGION^ msg1:ppMessage { #reg.addChildEx(#msg1); } drtv:directiveBlock[codeMask] { #reg.addChildEx(#drtv); } endreg:PP_ENDREGION msg2:ppMessage { #endreg.addChildEx(#msg2); #reg.addChildEx(#endreg); } { ## = #reg; } ; conditionalDirective! [CodeMaskEnums codeMask] : hashIF:PP_COND_IF^ exprIF:preprocessExpression { #hashIF.addChildEx(#exprIF); } drtvIF:directiveBlock[codeMask] { #hashIF.addChildEx(#drtvIF); } ( hashELIF:PP_COND_ELIF exprELIF:preprocessExpression { #hashELIF.addChildEx(#exprELIF); } drtvELIF:directiveBlock[codeMask] { #hashELIF.addChildEx(#drtvELIF); } { #hashIF.addChildEx(#hashELIF); } )* ( hashELSE:PP_COND_ELSE drtvELSE:directiveBlock[codeMask] { #hashELSE.addChildEx(#drtvELSE); } { #hashIF.addChildEx(#hashELSE); } )? hashENDIF:PP_COND_ENDIF { #hashIF.addChildEx(#hashENDIF); } { ## = #hashIF; } ; directiveBlock [CodeMaskEnums codeMask] : ( options { greedy = true; } : preprocessorDirective[codeMask] | { NotExcluded(codeMask, CodeMaskEnums.UsingDirectives) }? usingDirective | { NotExcluded(codeMask, CodeMaskEnums.GlobalAttributes) }? globalAttributeSection | { NotExcluded(codeMask, CodeMaskEnums.Attributes) }? attributeSection | { NotExcluded(codeMask, CodeMaskEnums.NamespaceMemberDeclarations) }? namespaceMemberDeclaration | { NotExcluded(codeMask, CodeMaskEnums.ClassMemberDeclarations) }? classMemberDeclaration | { NotExcluded(codeMask, CodeMaskEnums.StructMemberDeclarations) }? structMemberDeclaration | { NotExcluded(codeMask, CodeMaskEnums.InterfaceMemberDeclarations) }? interfaceMemberDeclaration | { NotExcluded(codeMask, CodeMaskEnums.Statements) }? statement )* { ## = #( [PP_BLOCK, "PP_BLOCK"], ## ); } ; ppMessage : ( PP_IDENT | PP_STRING | PP_FILENAME | PP_NUMBER )* { ## = #( [PP_MESSAGE, "PP_MESSAGE"], ## ); } ; //====================================== // 14.2.1 Operator precedence and associativity // // The following table summarizes all PP operators in order of precedence from lowest to highest: // // PRECEDENCE SECTION CATEGORY OPERATORS // lowest ( 4) 14.11 Conditional OR || // ( 3) 14.11 Conditional AND && // ( 2) 14.9 Equality == != // highest ( 1) 14.5 Primary (x) !x // // NOTE: In accordance with lessons gleaned from the "java.g" file supplied with ANTLR, I have // applied the following pattern to the rules for expressions: // // thisLevelExpression : // nextHigherPrecedenceExpression (OPERATOR nextHigherPrecedenceExpression)* // // which is a standard recursive definition for a parsing an expression. // preprocessExpression : preprocessOrExpression { #preprocessExpression = #( #[PP_EXPR,"PP_EXPR"], #preprocessExpression ); } ; preprocessOrExpression : preprocessAndExpression ( LOG_OR^ preprocessAndExpression )* ; preprocessAndExpression : preprocessEqualityExpression ( LOG_AND^ preprocessEqualityExpression )* ; preprocessEqualityExpression : preprocessPrimaryExpression ( ( EQUAL^ | NOT_EQUAL^ ) preprocessPrimaryExpression )* ; preprocessPrimaryExpression : ( id:keywordExceptTrueAndFalse { #id.setType(PP_IDENT); } | PP_IDENT | TRUE | FALSE | LOG_NOT^ preprocessPrimaryExpression | o:OPEN_PAREN^ { #o.setType(PAREN_EXPR); } preprocessOrExpression CLOSE_PAREN! ) ; keywordExceptTrueAndFalse : ABSTRACT | AS | BASE | BOOL | BREAK | BYTE | CASE | CATCH | CHAR | CHECKED | CLASS | CONST | CONTINUE | DECIMAL | DEFAULT | DELEGATE | DO | DOUBLE | ELSE | ENUM | EVENT | EXPLICIT | EXTERN | FINALLY | FIXED | FLOAT | FOR | FOREACH | GOTO | IF | IMPLICIT | IN | INT | INTERFACE| INTERNAL | IS | LOCK | LONG | NAMESPACE| NEW | NULL | OBJECT | OPERATOR | OUT | OVERRIDE | PARAMS | PRIVATE | PROTECTED| PUBLIC | READONLY | REF | RETURN | SBYTE | SEALED | SHORT | SIZEOF | STACKALLOC | STATIC | STRING | STRUCT | SWITCH | THIS | THROW | TRY | TYPEOF | UINT | ULONG | UNCHECKED| UNSAFE | USHORT | USING | VIRTUAL | VOID | VOLATILE| WHILE ; voidAsType! : v:VOID { ## = #( [TYPE, "TYPE"], #v, [STARS, "STARS"], [ARRAY_RANKS, "ARRAY_RANKS"] ); } ;