| parser grammar CTFParser; |
| |
| options { |
| language = Java; |
| output = AST; |
| ASTLabelType = CommonTree; |
| tokenVocab = CTFLexer; |
| } |
| |
| tokens { |
| ROOT; |
| |
| EVENT; |
| STREAM; |
| TRACE; |
| ENV; |
| CLOCK; |
| |
| DECLARATION; |
| SV_DECLARATION; |
| TYPE_SPECIFIER_LIST; |
| TYPE_DECLARATOR_LIST; |
| TYPE_DECLARATOR; |
| |
| STRUCT; |
| STRUCT_NAME; |
| STRUCT_BODY; |
| ALIGN; |
| |
| CTF_EXPRESSION_TYPE; |
| CTF_EXPRESSION_VAL; |
| CTF_LEFT; |
| CTF_RIGHT; |
| |
| UNARY_EXPRESSION_STRING; |
| UNARY_EXPRESSION_STRING_QUOTES; |
| UNARY_EXPRESSION_DEC; |
| UNARY_EXPRESSION_HEX; |
| UNARY_EXPRESSION_OCT; |
| LENGTH; |
| |
| TYPEDEF; |
| |
| TYPEALIAS; |
| TYPEALIAS_TARGET; |
| TYPEALIAS_ALIAS; |
| |
| INTEGER; |
| STRING; |
| FLOATING_POINT; |
| |
| ENUM; |
| ENUM_CONTAINER_TYPE; |
| ENUM_ENUMERATOR; |
| ENUM_NAME; |
| ENUM_VALUE; |
| ENUM_VALUE_RANGE; |
| ENUM_BODY; |
| |
| VARIANT; |
| VARIANT_NAME; |
| VARIANT_TAG; |
| VARIANT_BODY; |
| |
| DECLARATOR; |
| LENGTH; |
| } |
| |
| /* |
| * Scope for the tracking of types. |
| * For now we just track the names (it's a simple Set), but |
| * later we will have to track the info about the target type. |
| */ |
| scope Symbols { |
| Set<String> types; |
| } |
| |
| @header { |
| package org.eclipse.linuxtools.ctf.parser; |
| import java.util.Set; |
| import java.util.HashSet; |
| } |
| |
| @members { |
| public CTFParser(TokenStream input, boolean verbose) { |
| this(input); |
| this.verbose = verbose; |
| } |
| |
| /* To disable automatic error recovery. When we have a mismatched token, simply throw an exception. */ |
| @Override |
| protected Object recoverFromMismatchedToken(IntStream input, int ttype, BitSet follow) throws RecognitionException |
| { |
| throw new MismatchedTokenException(ttype, input); |
| } |
| |
| /** |
| * Checks if a given name has been defined has a type. |
| * From: http://www.antlr.org/grammar/1153358328744/C.g |
| * |
| * @param name The name to check. |
| * @return True if is is a type, false otherwise. |
| */ |
| boolean isTypeName(String name) { |
| for (int i = Symbols_stack.size() - 1; i >= 0; i--) { |
| Symbols_scope scope = (Symbols_scope)Symbols_stack.get(i); |
| if (scope.types.contains(name)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void addTypeName(String name) { |
| $Symbols::types.add(name); |
| if (verbose) { |
| debug_print("New type: " + name); |
| } |
| } |
| |
| boolean _inTypedef = false; |
| |
| void typedefOn() { |
| debug_print("typedefOn"); |
| _inTypedef = true; |
| } |
| |
| void typedefOff() { |
| debug_print("typedefOff"); |
| _inTypedef = false; |
| } |
| |
| boolean inTypedef() { |
| return _inTypedef; |
| } |
| |
| boolean _inTypealiasAlias = false; |
| |
| void typealiasAliasOn() { |
| debug_print("typealiasAliasOn"); |
| _inTypealiasAlias = true; |
| } |
| |
| void typealiasAliasOff() { |
| debug_print("typealiasAliasOff"); |
| _inTypealiasAlias = false; |
| } |
| |
| boolean inTypealiasAlias() { |
| return _inTypealiasAlias; |
| } |
| |
| void print_tabs(int n) { |
| for (int i = 0; i < n; i++) { |
| System.out.print(" "); |
| } |
| } |
| |
| void enter(String name) { |
| if (verbose) { |
| if (state.backtracking == 0) { |
| print_tabs(depth); |
| debug_print("+ " + name); |
| depth++; |
| } |
| } |
| } |
| |
| void exit(String name) { |
| if (verbose) { |
| depth--; |
| print_tabs(depth); |
| debug_print("- " + name); |
| } |
| } |
| |
| void debug_print(String str) { |
| if (verbose) { |
| System.out.println(str); |
| } |
| } |
| |
| int depth = 0; |
| |
| /* Prints rule entry and exit while parsing */ |
| boolean verbose = false; |
| } |
| |
| /* To disable automatic error recovery. By default, the catch block of every rule simple rethrows the error. */ |
| @rulecatch { |
| catch (RecognitionException e) |
| { |
| throw e; |
| } |
| } |
| |
| /* The top-level rule. */ |
| parse |
| scope Symbols; |
| @init { |
| enter("parse"); |
| debug_print("Scope push " + Symbols_stack.size()); |
| $Symbols::types = new HashSet<String>(); |
| } |
| @after { |
| debug_print("Scope pop " + Symbols_stack.size()); |
| exit("parse"); |
| |
| debug_print("Final depth, should be 0: " + depth); |
| } |
| : |
| declaration+ EOF -> ^(ROOT declaration+) |
| ; |
| |
| numberLiteral |
| @init { |
| enter("numberLiteral"); |
| } |
| @after { |
| debug_print($numberLiteral.text); |
| exit("numberLiteral"); |
| } |
| : |
| SIGN* (HEX_LITERAL -> ^(UNARY_EXPRESSION_HEX HEX_LITERAL SIGN*) |
| | DECIMAL_LITERAL -> ^(UNARY_EXPRESSION_DEC DECIMAL_LITERAL SIGN*) |
| | OCTAL_LITERAL -> ^(UNARY_EXPRESSION_OCT OCTAL_LITERAL SIGN*)) |
| ; |
| |
| constant |
| @init { |
| enter("constant"); |
| } |
| @after { |
| exit("constant"); |
| } |
| : |
| numberLiteral |
| | enumConstant |
| | CHARACTER_LITERAL |
| ; |
| |
| primaryExpression |
| @init { |
| enter("primaryExpression"); |
| } |
| @after { |
| exit("primaryExpression"); |
| } |
| : |
| (IDENTIFIER) => IDENTIFIER { debug_print("IDENTIFIER: " + $IDENTIFIER.text);} -> ^(UNARY_EXPRESSION_STRING IDENTIFIER) |
| | (ctfKeyword) => ctfKeyword -> ^(UNARY_EXPRESSION_STRING ctfKeyword) |
| | (STRING_LITERAL) => STRING_LITERAL { debug_print("STRING_LITERAL: " + $STRING_LITERAL.text);} -> ^(UNARY_EXPRESSION_STRING_QUOTES STRING_LITERAL) |
| /*| (LPAREN unaryExpression RPAREN)*/ // Not supported yet |
| | constant |
| ; |
| |
| reference |
| @init { |
| enter("reference"); |
| } |
| @after { |
| debug_print($reference.text); |
| exit("reference"); |
| } |
| : |
| (ref=DOT | ref=ARROW) IDENTIFIER -> ^($ref ^(UNARY_EXPRESSION_STRING IDENTIFIER)) |
| ; |
| |
| postfixExpressionSuffix |
| @init { |
| enter("postfixExpressionSuffix"); |
| } |
| @after { |
| exit("postfixExpressionSuffix"); |
| } |
| : |
| (OPENBRAC unaryExpression CLOSEBRAC!) |
| | reference |
| ; |
| |
| postfixExpression |
| @init { |
| enter("postfixExpression"); |
| } |
| @after { |
| exit("postfixExpression"); |
| } |
| : |
| (primaryExpression) (postfixExpressionSuffix)*| |
| ((ctfSpecifierHead) (postfixExpressionSuffix)+)// added for ctfV1.8 |
| ; |
| |
| unaryExpression |
| @init { |
| enter("unaryExpression"); |
| } |
| @after { |
| exit("unaryExpression"); |
| } |
| : |
| /*((SIGN postfixExpression[true]) |
| | postfixExpression[false])*/ |
| postfixExpression |
| ; |
| |
| enumConstant |
| @init { |
| enter("enumConstant"); |
| } |
| @after { |
| debug_print($enumConstant.text); |
| exit("enumConstant"); |
| } |
| : |
| STRING_LITERAL -> ^(UNARY_EXPRESSION_STRING_QUOTES STRING_LITERAL) |
| | IDENTIFIER -> ^(UNARY_EXPRESSION_STRING IDENTIFIER) |
| | ctfKeyword -> ^(UNARY_EXPRESSION_STRING ctfKeyword) |
| ; |
| // 2.2 |
| |
| declaration |
| @init { |
| enter("declaration"); |
| } |
| @after { |
| exit("declaration"); |
| if (inTypedef()) |
| typedefOff(); |
| } |
| : |
| (declarationSpecifiers declaratorList? TERM) |
| |
| // When the declaration is completely parsed and was a typedef, we add the declarators to the symbol table. |
| -> {inTypedef()}? ^(DECLARATION ^(TYPEDEF declaratorList declarationSpecifiers)) |
| -> ^(DECLARATION declarationSpecifiers declaratorList?) |
| |
| | (ctfSpecifier TERM!) |
| ; |
| |
| declarationSpecifiers |
| @init { |
| enter("declarationSpecifiers"); |
| } |
| @after { |
| debug_print($declarationSpecifiers.text); |
| exit("declarationSpecifiers"); |
| } |
| : |
| ( |
| // We don't want to keep the typedef keyword in the specifier list. |
| // Instead, we keep track that we encountered a typedef in the declaration. |
| storageClassSpecifier |
| | typeQualifier |
| | typeSpecifier |
| )+ -> ^(TYPE_SPECIFIER_LIST typeQualifier* typeSpecifier*) |
| ; |
| |
| declaratorList |
| @init { |
| enter("declaratorList"); |
| } |
| @after { |
| exit("declaratorList"); |
| } |
| : |
| declarator (SEPARATOR declarator)* -> ^(TYPE_DECLARATOR_LIST declarator+) |
| ; |
| |
| abstractDeclaratorList |
| @init { |
| enter("abstractDeclaratorList"); |
| } |
| @after { |
| exit("abstractDeclaratorList"); |
| } |
| : |
| abstractDeclarator (SEPARATOR abstractDeclarator)* -> ^(TYPE_DECLARATOR_LIST abstractDeclarator+) |
| ; |
| |
| storageClassSpecifier : |
| TYPEDEFTOK {typedefOn();} |
| ; |
| |
| typeSpecifier |
| @init { |
| enter("typeSpecifier"); |
| } |
| @after { |
| debug_print($typeSpecifier.text); |
| exit("typeSpecifier"); |
| } |
| : |
| FLOATTOK |
| | INTTOK |
| | LONGTOK |
| | SHORTTOK |
| | SIGNEDTOK |
| | UNSIGNEDTOK |
| | CHARTOK |
| | DOUBLETOK |
| | VOIDTOK |
| | BOOLTOK |
| | COMPLEXTOK |
| | IMAGINARYTOK |
| | structSpecifier |
| | variantSpecifier |
| | enumSpecifier |
| | ctfTypeSpecifier |
| | {inTypealiasAlias() || isTypeName(input.LT(1).getText())}? => typedefName |
| ; |
| |
| typeQualifier |
| @init { |
| enter("typeQualifier"); |
| } |
| @after { |
| debug_print($typeQualifier.text); |
| exit("typeQualifier"); |
| } |
| : |
| CONSTTOK |
| ; |
| |
| alignAttribute : |
| ALIGNTOK LPAREN unaryExpression RPAREN -> ^(ALIGN unaryExpression) |
| ; |
| |
| // you can have an empty struct but not an empty variant |
| structBody |
| scope Symbols; |
| @init { |
| enter("structBody"); |
| debug_print("Scope push " + Symbols_stack.size()); |
| $Symbols::types = new HashSet<String>(); |
| } |
| @after { |
| debug_print("Scope pop " + Symbols_stack.size()); |
| exit("structBody"); |
| } |
| : |
| LCURL structOrVariantDeclarationList? RCURL -> ^(STRUCT_BODY structOrVariantDeclarationList?) |
| ; |
| |
| |
| |
| structSpecifier |
| @init { |
| enter("structSpecifier"); |
| } |
| @after { |
| exit("structSpecifier"); |
| } |
| : |
| STRUCTTOK |
| ( |
| // We have an IDENTIFIER after 'struct' |
| ( |
| structName |
| ( |
| alignAttribute |
| | |
| ( |
| structBody |
| ( /* structBody can return an empty tree, so we need those ? */ |
| alignAttribute |
| | |
| /* empty */ |
| ) |
| ) |
| | |
| /* empty */ |
| ) |
| ) |
| | |
| // We have a body after 'struct' |
| ( |
| structBody |
| ( |
| alignAttribute |
| | |
| /* empty */ |
| ) |
| ) |
| ) -> ^(STRUCT structName? structBody? alignAttribute?) |
| ; |
| |
| structName |
| @init { |
| enter("structName"); |
| } |
| @after { |
| debug_print($structName.text); |
| exit("structName"); |
| } |
| : |
| IDENTIFIER -> ^(STRUCT_NAME IDENTIFIER) |
| ; |
| |
| structOrVariantDeclarationList |
| @init { |
| enter("structOrVariantDeclarationList"); |
| } |
| @after { |
| exit("structOrVariantDeclarationList"); |
| } |
| : |
| structOrVariantDeclaration+ |
| ; |
| |
| structOrVariantDeclaration |
| @init { |
| enter("structOrVariantDeclaration"); |
| } |
| @after { |
| exit("structOrVariantDeclaration"); |
| } |
| : |
| ( |
| ( |
| declarationSpecifiers |
| ( |
| /* If we met a "typedef" */ |
| {inTypedef()}? => declaratorList {typedefOff();} |
| -> ^(TYPEDEF declaratorList declarationSpecifiers) |
| | structOrVariantDeclaratorList |
| -> ^(SV_DECLARATION declarationSpecifiers structOrVariantDeclaratorList) |
| ) |
| ) |
| | |
| // Lines 3 and 4 |
| typealiasDecl -> typealiasDecl |
| ) |
| TERM |
| ; |
| |
| specifierQualifierList |
| @init { |
| enter("specifierQualifierList"); |
| } |
| @after { |
| exit("specifierQualifierList"); |
| } |
| : |
| (typeQualifier | typeSpecifier)+ -> ^(TYPE_SPECIFIER_LIST typeQualifier* typeSpecifier*) |
| ; |
| |
| structOrVariantDeclaratorList |
| @init { |
| enter("structOrVariantDeclaratorList"); |
| } |
| @after { |
| exit("structOrVariantDeclaratorList"); |
| } |
| : |
| structOrVariantDeclarator (SEPARATOR structOrVariantDeclarator)* -> ^(TYPE_DECLARATOR_LIST structOrVariantDeclarator+) |
| ; |
| |
| structOrVariantDeclarator |
| @init { |
| enter("structOrVariantDeclarator"); |
| } |
| @after { |
| exit("structOrVariantDeclarator"); |
| } |
| : |
| /* Bitfields not supported yet */ |
| (declarator (COLON numberLiteral)?) -> declarator |
| /*| (COLON numberLiteral)*/ |
| ; |
| |
| variantSpecifier |
| @init { |
| enter("variantSpecifier"); |
| } |
| @after { |
| exit("variantSpecifier"); |
| } |
| : |
| VARIANTTOK |
| ( |
| ( |
| variantName |
| ( |
| ( |
| variantTag |
| ( |
| variantBody |
| | |
| /* empty */ |
| ) |
| ) |
| | |
| variantBody |
| ) |
| ) |
| | |
| (variantTag variantBody) |
| | |
| variantBody |
| ) -> ^(VARIANT variantName? variantTag? variantBody?) |
| ; |
| |
| variantName |
| @init { |
| enter("variantName"); |
| } |
| @after { |
| debug_print($variantName.text); |
| exit("variantName"); |
| } |
| : |
| IDENTIFIER -> ^(VARIANT_NAME IDENTIFIER) |
| ; |
| |
| variantBody |
| scope Symbols; |
| @init { |
| enter("variantBody"); |
| debug_print("Scope push " + Symbols_stack.size()); |
| $Symbols::types = new HashSet<String>(); |
| } |
| @after { |
| debug_print("Scope pop " + Symbols_stack.size()); |
| exit("variantBody"); |
| } |
| : |
| LCURL structOrVariantDeclarationList RCURL -> ^(VARIANT_BODY structOrVariantDeclarationList) |
| ; |
| |
| variantTag |
| @init { |
| enter("variantTag"); |
| } |
| @after { |
| debug_print($variantTag.text); |
| exit("variantTag"); |
| } |
| : |
| LT IDENTIFIER GT -> ^(VARIANT_TAG IDENTIFIER) |
| ; |
| |
| enumSpecifier |
| @init { |
| enter("enumSpecifier"); |
| } |
| @after { |
| exit("enumSpecifier"); |
| } |
| : |
| ENUMTOK |
| ( |
| // Lines 1 to 5, when we have "ENUMTOK IDENTIFIER". |
| ( |
| enumName |
| ( |
| enumContainerType enumBody |
| | |
| enumBody |
| | |
| // no enumDeclarator or enumBodym |
| ) |
| ) |
| | |
| // Lines 1, 2, 4, 5, when we have no IDENTIFIER. |
| ( |
| enumContainerType enumBody |
| | |
| enumBody |
| ) |
| ) -> ^(ENUM enumName? enumContainerType? enumBody?) |
| ; |
| |
| enumName |
| @init { |
| enter("enumName"); |
| } |
| @after { |
| debug_print($enumName.text); |
| exit("enumName"); |
| } |
| : |
| IDENTIFIER -> ^(ENUM_NAME IDENTIFIER) |
| ; |
| |
| enumBody |
| @init { |
| enter("enumBody"); |
| } |
| @after { |
| exit("enumBody"); |
| } |
| : |
| LCURL enumeratorList (SEPARATOR RCURL | RCURL) -> ^(ENUM_BODY enumeratorList) |
| ; |
| |
| enumContainerType |
| @init { |
| enter("enumContainerType"); |
| } |
| @after { |
| exit("enumContainerType"); |
| } |
| : |
| COLON declarationSpecifiers -> ^(ENUM_CONTAINER_TYPE declarationSpecifiers) |
| ; |
| |
| enumeratorList |
| @init { |
| enter("enumeratorList"); |
| } |
| @after { |
| exit("enumeratorList"); |
| } |
| : |
| enumerator (SEPARATOR enumerator)* -> (^(ENUM_ENUMERATOR enumerator))+ |
| ; |
| |
| enumerator |
| @init { |
| enter("enumerator"); |
| } |
| @after { |
| exit("enumerator"); |
| } |
| : |
| enumConstant enumeratorValue? |
| ; |
| |
| enumeratorValue |
| @init { |
| enter("enumeratorValue"); |
| } |
| @after { |
| exit("enumeratorValue"); |
| } |
| : |
| ASSIGNMENT e1=unaryExpression |
| ( |
| -> ^(ENUM_VALUE $e1) |
| | ELIPSES e2=unaryExpression -> ^(ENUM_VALUE_RANGE $e1 $e2) |
| ) |
| ; |
| |
| |
| declarator |
| @init { |
| enter("declarator"); |
| } |
| @after { |
| exit("declarator"); |
| } |
| : |
| pointer* directDeclarator -> ^(TYPE_DECLARATOR pointer* directDeclarator) |
| ; |
| |
| directDeclarator |
| @init { |
| enter("directDeclarator"); |
| } |
| @after { |
| exit("directDeclarator"); |
| } |
| : |
| ( |
| IDENTIFIER { if (inTypedef()) addTypeName($IDENTIFIER.text); } {debug_print($IDENTIFIER.text);} |
| /*| LPAREN declarator RPAREN*/ /* Not supported yet */ |
| ) |
| directDeclaratorSuffix* |
| ; |
| |
| directDeclaratorSuffix: |
| OPENBRAC directDeclaratorLength CLOSEBRAC -> ^(LENGTH directDeclaratorLength) |
| ; |
| |
| directDeclaratorLength : |
| unaryExpression |
| ; |
| |
| |
| abstractDeclarator |
| @init { |
| enter("abstractDeclarator"); |
| } |
| @after { |
| exit("abstractDeclarator"); |
| } |
| : |
| (pointer+ directAbstractDeclarator?) -> ^(TYPE_DECLARATOR pointer+ directAbstractDeclarator?) |
| | directAbstractDeclarator -> ^(TYPE_DECLARATOR directAbstractDeclarator) |
| ; |
| |
| /* |
| In the CTF grammar, direct-abstract-declarator can be empty (because of identifier-opt). |
| We take care of that by appending a '?' to each use of "abstractDeclaratorList". |
| */ |
| directAbstractDeclarator |
| @init { |
| enter("directAbstractDeclarator"); |
| } |
| @after { |
| debug_print($directAbstractDeclarator.text); |
| exit("directAbstractDeclarator"); |
| } |
| : |
| ( |
| IDENTIFIER |
| | (LPAREN abstractDeclarator RPAREN) |
| ) |
| ( |
| OPENBRAC unaryExpression? CLOSEBRAC |
| )? |
| ; |
| |
| pointer |
| @init { |
| enter("pointer"); |
| } |
| @after { |
| debug_print($pointer.text); |
| exit("pointer"); |
| } |
| : |
| POINTER typeQualifierList? -> ^(POINTER typeQualifierList?) |
| ; |
| |
| typeQualifierList : |
| typeQualifier+ |
| ; |
| |
| typedefName |
| @init { |
| enter("typedefName"); |
| } |
| @after { |
| debug_print("typedefName: " + $typedefName.text); |
| exit("typedefName"); |
| } |
| : |
| {inTypealiasAlias() || isTypeName(input.LT(1).getText())}? IDENTIFIER { if ((inTypedef() || inTypealiasAlias()) && !isTypeName($IDENTIFIER.text)) { addTypeName($IDENTIFIER.text); } } |
| ; |
| |
| /** |
| * What goes in the target part of a typealias. |
| * |
| * For example, the integer part in: |
| * typealias integer {...} := my_new_integer; |
| */ |
| typealiasTarget |
| @init { |
| enter("typealiasTarget"); |
| } |
| @after { |
| exit("typealiasTarget"); |
| } |
| : |
| declarationSpecifiers abstractDeclaratorList? |
| ; |
| |
| /** |
| * What goes in the alias part of a typealias. |
| * |
| * For example, the my_new_integer part in: |
| * typealias integer {...} := my_new_integer; |
| */ |
| typealiasAlias |
| @init { |
| enter("typealiasAlias"); |
| typealiasAliasOn(); |
| } |
| @after { |
| exit("typealiasAlias"); |
| typealiasAliasOff(); |
| } |
| : |
| ( |
| abstractDeclaratorList |
| | |
| (declarationSpecifiers abstractDeclaratorList?) |
| ) |
| ; |
| |
| typealiasDecl |
| @init { |
| enter("typealiasDecl"); |
| } |
| @after { |
| exit("typealiasDecl"); |
| } |
| : |
| TYPEALIASTOK typealiasTarget TYPE_ASSIGNMENT typealiasAlias |
| -> ^(TYPEALIAS ^(TYPEALIAS_TARGET typealiasTarget) ^(TYPEALIAS_ALIAS typealiasAlias)) |
| ; |
| |
| // 2.3 CTF stuff |
| |
| // TODO: Ajouter ceux qui manquent |
| ctfKeyword |
| @init { |
| enter("ctfKeyword"); |
| } |
| @after { |
| debug_print($ctfKeyword.text); |
| exit("ctfKeyword"); |
| } |
| : |
| ALIGNTOK |
| | EVENTTOK |
| | SIGNEDTOK |
| | STRINGTOK |
| ; |
| |
| ctfSpecifier |
| @init { |
| enter("ctfSpecifier"); |
| } |
| @after { |
| exit("ctfSpecifier"); |
| } |
| : |
| // event {...}, stream {...}, trace {...} |
| ctfSpecifierHead ctfBody -> ^(ctfSpecifierHead ctfBody) |
| | |
| // typealias |
| typealiasDecl -> ^(DECLARATION typealiasDecl) |
| ; |
| |
| ctfSpecifierHead |
| @init { |
| enter("ctfSpecifierHead"); |
| } |
| @after { |
| debug_print($ctfSpecifierHead.text); |
| exit("ctfSpecifierHead"); |
| } |
| : |
| EVENTTOK -> EVENT |
| | STREAMTOK -> STREAM |
| | TRACETOK -> TRACE |
| | ENVTOK -> ENV |
| | CLOCKTOK -> CLOCK |
| ; |
| |
| ctfTypeSpecifier |
| @init { |
| enter("ctfTypeSpecifier"); |
| } |
| @after { |
| exit("ctfTypeSpecifier"); |
| } |
| : |
| /* ctfBody can return an empty tree if the body is empty */ |
| FLOATINGPOINTTOK ctfBody -> ^(FLOATING_POINT ctfBody?) |
| | INTEGERTOK ctfBody -> ^(INTEGER ctfBody?) |
| | STRINGTOK ctfBody? -> ^(STRING ctfBody?) |
| ; |
| |
| ctfBody |
| scope Symbols; |
| @init { |
| enter("ctfBody"); |
| debug_print("Scope push " + + Symbols_stack.size()); |
| $Symbols::types = new HashSet<String>(); |
| } |
| @after { |
| debug_print("Scope pop " + + Symbols_stack.size()); |
| exit("ctfBody"); |
| } |
| : |
| LCURL ctfAssignmentExpressionList? RCURL -> ctfAssignmentExpressionList? |
| ; |
| |
| ctfAssignmentExpressionList : |
| (ctfAssignmentExpression TERM!)+ |
| ; |
| |
| |
| ctfAssignmentExpression |
| @init { |
| enter("ctfAssignmentExpression"); |
| } |
| @after { |
| if (inTypedef()) { |
| typedefOff(); |
| } |
| exit("ctfAssignmentExpression"); |
| } |
| : |
| ( |
| left=unaryExpression |
| ( |
| (assignment=ASSIGNMENT right1=unaryExpression) -> ^(CTF_EXPRESSION_VAL ^(CTF_LEFT $left) ^(CTF_RIGHT $right1)) |
| | (type_assignment=TYPE_ASSIGNMENT right2=typeSpecifier) -> ^(CTF_EXPRESSION_TYPE ^(CTF_LEFT $left) ^(CTF_RIGHT ^(TYPE_SPECIFIER_LIST $right2))) |
| ) |
| ) |
| |
| | |
| |
| (declarationSpecifiers {inTypedef()}? declaratorList) |
| -> ^(TYPEDEF declaratorList declarationSpecifiers) |
| | |
| |
| typealiasDecl |
| ; |