[ 
  treedl.language = "java";
  treedl.visitor = "Java5Visitor"; 
]
tree com.unitesk.java5.Java5;

/**
 * All nodes have start/end positions.
 * Lines and columns count from 1.
 * Column of end position points after node, not at the last char.
 */
abstract node BaseNode : Node
{
    attribute abstract late noset <com.unitesk.atp.text.location.Position> startPosition;
    attribute abstract late noset <com.unitesk.atp.text.location.Position> endPosition;
}

/**
 * One-token node gets start/end positions from one token.
 */
abstract node TokenNode : BaseNode
{
    attribute <antlr.Token> token;

    attribute custom late noset <com.unitesk.atp.text.location.Position> startPosition
    get { startPosition = TokenUtils.getPosition( token ); };
    
    attribute custom late noset <com.unitesk.atp.text.location.Position> endPosition
    get { endPosition = TokenUtils.getEndPosition( token ); };
}

/**
 * Node with start/end tokens used to calculate start/end positions.
 */
abstract node TokenRangeNode : BaseNode
{
    attribute <antlr.Token> startToken;
    attribute <antlr.Token> endToken;
    
    attribute custom late noset <com.unitesk.atp.text.location.Position> startPosition
    get { startPosition = TokenUtils.getPosition( startToken ); };
    
    attribute custom late noset <com.unitesk.atp.text.location.Position> endPosition
    get { endPosition = TokenUtils.getEndPosition( endToken ); };
}

root node CompilationUnit
{
    child PackageDeclaration? optPackageDeclaration;
    child ImportDeclaration* optImportDeclarationList;
    child TypeDeclaration* optTypeDeclarationList;
}

node PackageDeclaration : TokenRangeNode
{
    child Annotation* optAnnotationList;
    child QualifiedIdentifier name;
}

abstract node ImportDeclaration : TokenRangeNode
{
    child QualifiedIdentifier name;
}

node SingleTypeImportDeclaration : ImportDeclaration {}

node TypeImportOnDemandDeclaration : ImportDeclaration {}

node SingleStaticImportDeclaration : ImportDeclaration {}

node StaticImportOnDemandDeclaration : ImportDeclaration {}

flags Modifiers
{
    PUBLIC, PROTECTED, PRIVATE, ABSTRACT, 
    STATIC, STRICTFP, FINAL, TRANSIENT,
    VOLATILE, SYNCHRONIZED, NATIVE

    // Interface modifiers: PUBLIC, PROTECTED, PRIVATE, ABSTRACT, 
    //                      STATIC, STRICTFP
    //
    // Class modifiers: PUBLIC, PROTECTED, PRIVATE, ABSTRACT, 
    //                  STATIC, STRICTFP, FINAL
    //
    // Field modifiers: PUBLIC, PROTECTED, PRIVATE, STATIC,
    //                  FINAL, TRANSIENT, VOLATILE
    //
    // Method modifiers: PUBLIC, PROTECTED, PRIVATE, ABSTRACT, STATIC,
    //                   FINAL, SYNCHRONIZED, NATIVE, STRICTFP
    //
    // Constructor modifiers: PUBLIC, PROTECTED, PRIVATE
    //
    // Constant modifiers: PUBLIC, STATIC, FINAL
    //
    // Abstract method modifiers: PUBLIC, ABSTRACT
}

[ java_translate.static = true; ]
operation string toStringModifiers( Modifiers modifiers )
{
    case( modifiers ):
    {
        StringBuffer sb = new StringBuffer();
        if( modifiers.check( Modifiers.PUBLIC ) )       sb.append( "public " );
        if( modifiers.check( Modifiers.PROTECTED ) )    sb.append( "protected " );
        if( modifiers.check( Modifiers.PRIVATE ) )      sb.append( "private " );
        if( modifiers.check( Modifiers.ABSTRACT ) )     sb.append( "abstract " );
        if( modifiers.check( Modifiers.STATIC ) )       sb.append( "static " );
        if( modifiers.check( Modifiers.STRICTFP ) )     sb.append( "strictfp " );
        if( modifiers.check( Modifiers.FINAL ) )        sb.append( "final " );
        if( modifiers.check( Modifiers.TRANSIENT ) )    sb.append( "transient " );
        if( modifiers.check( Modifiers.VOLATILE ) )     sb.append( "volatile " );
        if( modifiers.check( Modifiers.SYNCHRONIZED ) ) sb.append( "synchronized " );
        if( modifiers.check( Modifiers.NATIVE       ) ) sb.append( "native " );
        if( sb.length() > 0 ) sb.setLength( sb.length() - 1 ); // trim last space
        return sb.toString();
    }
}

/**
 * Type declaration is member declaration
 */
abstract node TypeDeclaration : MemberDeclaration
{
    child MemberDeclaration* optMemberList;
}

//=====  Qualification of type  ================================================

abstract node OptionalType
{
}

node VoidType : OptionalType
{
    attribute <antlr.Token> token;

    attribute custom late noset <com.unitesk.atp.text.location.Position> startPosition
    get { startPosition = TokenUtils.getPosition( token ); };
    
    attribute custom late noset <com.unitesk.atp.text.location.Position> endPosition
    get { endPosition = TokenUtils.getEndPosition( token ); };
}

abstract node Type : OptionalType
{
}

abstract node PrimitiveType : Type
{
    attribute <antlr.Token> token;

    attribute custom late noset <com.unitesk.atp.text.location.Position> startPosition
    get { startPosition = TokenUtils.getPosition( token ); };
    
    attribute custom late noset <com.unitesk.atp.text.location.Position> endPosition
    get { endPosition = TokenUtils.getEndPosition( token ); };
}

enum NumericTypeKind
{
    BYTE, SHORT, INT, LONG, CHAR, FLOAT, DOUBLE
}

[ java_translate.static = true; ]
operation string toStringNumericTypeKind( virtual NumericTypeKind kind )
{
    case( BYTE   ): { return "byte";   }
    case( SHORT  ): { return "short";  }
    case( INT    ): { return "int";    }
    case( LONG   ): { return "long";   }
    case( CHAR   ): { return "char";   }
    case( FLOAT  ): { return "float";  }
    case( DOUBLE ): { return "double"; }
}

abstract node NumericType : PrimitiveType
{
}

node IntegralType : NumericType
{
    /**
     * Allowed values are:
     *   BYTE, SHORT, INT, LONG, CHAR
     */
    attribute NumericTypeKind kind;
    
    constructor
    {
        if( kind == NumericTypeKind.FLOAT || kind == NumericTypeKind.DOUBLE )
        {
            // TODO: refine exception type and message
            throw new IllegalArgumentException("Non integral type");
        }
    }
}

node FloatingPointType : NumericType
{
    /**
     * Allowed values are:
     *   FLOAT, DOUBLE
     */
    attribute NumericTypeKind kind;

    constructor
    {
        if( kind != NumericTypeKind.FLOAT && kind != NumericTypeKind.DOUBLE )
        {
            // TODO: refine exception type and message
            throw new IllegalArgumentException("Non floating point type");
        }
    }
}

node BooleanType : PrimitiveType {}

abstract node ReferenceType : Type
{
}

node QualifiedParameterizedIdentifier : ReferenceType
{
    child ParameterizedIdentifier+ parameterizedIdentifierList;
}

node ArrayType : ReferenceType
{
    child Type type;
    attribute int dim;
}

node ParameterizedIdentifier
{
    child Identifier id;
    child ActualTypeArgument* optArgumentList;
}

abstract node ActualTypeArgument
{
}

node ReferenceTypeActualTypeArgument : ActualTypeArgument
{
    child ReferenceType type;
}

node WildcardActualTypeArgument : ActualTypeArgument
{
}

node ExtendsWildcardActualTypeArgument : WildcardActualTypeArgument 
{
    child ReferenceType type;
}

node SuperWildcardActualTypeArgument : WildcardActualTypeArgument
{
    child ReferenceType type;
}

//=====  Class declaration  ====================================================

/**
 *
 * changes:     child QualifiedParameterizedIdentifier? optSuperClass 
 *          --> child Type? optSuperClass
 *              child QualifiedParameterizedIdentifier* optInterfaceList
 *          --> child Type* optInterfaceList
 *         (in the correspondence with JLS and JAVAC)
 *
**/
node ClassDeclaration : TypeDeclaration
{
    child Annotation* optAnnotationList;

    /**
     * Allowed values are:
     *   PUBLIC, PROTECTED, PRIVATE, ABSTRACT, 
     *   STATIC, STRICTFP, FINAL
     */
    attribute Modifiers modifiers;
    child Identifier name;
    child TypeParameter* optTypeParameterList;
    child QualifiedParameterizedIdentifier? optSuperClass;
    child QualifiedParameterizedIdentifier* optInterfaceList;
    // TODO: Check member declaration type
}

node TypeParameter
{
    child Identifier typeVariable; // TODO: check identifier being type variable
    child QualifiedParameterizedIdentifier? optClassOrInterfaceType;
    child QualifiedParameterizedIdentifier* optAdditionalBoundInterfaceList;
    /* two last children depend on each other
     * they can be replaced by
     * child ClassOrInterfaceType* optClassOrInterfaceType;
     * but it requires additional check that all list elements
     * except the first one are of InterfaceType
     */

    constructor
    {
        if( getOptClassOrInterfaceType() == null // TypeBound skipped
            && sizeOptAdditionalBoundInterfaceList() > 0  // but AdditionalBounds exist
          )
        {
            // TODO: refine exception type and message
            throw new IllegalArgumentException("There is no type bound (but additional bounds exists)");
        }
    }
}

//=====  Enum declaration  =====================================================

/**
 *
 * changes:     child QualifiedParameterizedIdentifier* optInterfaceList
 *          --> child Type* optInterfaceList
 *         (in the correspondence with JLS and JAVAC)
 *
**/
node EnumDeclaration : TypeDeclaration
{
    child Annotation* optAnnotationList;
    
    /**
     * Allowed values are:
     *   PUBLIC, PROTECTED, PRIVATE, ABSTRACT,
     *   STATIC, STRICTFP, FINAL
     */
    attribute Modifiers modifiers;
    child Identifier name;
    child QualifiedParameterizedIdentifier* optInterfaceList;
    child EnumConstant* optConstantList;
    // TODO: Check member declaration type
}

//=====  Interface declaration  ================================================

/**
 *
 * changes:     child QualifiedParameterizedIdentifier* optInterfaceList
 *          --> child Type* optInterfaceList
 *         (in the correspondence with JLS and JAVAC)
 *
**/
node InterfaceDeclaration : TypeDeclaration
{
    child Annotation* optAnnotationList;
    
    /**
     * Allowed values are:
     *   PUBLIC, PROTECTED, PRIVATE,
     *   ABSTRACT, STATIC, STRICTFP
     */
    attribute Modifiers modifiers;
    child Identifier name;
    child TypeParameter* optTypeParameterList;
    child QualifiedParameterizedIdentifier* optInterfaceList;
    // TODO: Check member declaration type
}

//=====  Annotation type declaration  ==========================================

node AnnotationTypeDeclaration : TypeDeclaration
{
    child Annotation* optAnnotationList;
    
    /**
     * Allowed values are:
     *   PUBLIC, PROTECTED, PRIVATE,
     *   ABSTRACT, STATIC, STRICTFP
     */
    attribute Modifiers modifiers;
    child Identifier name;
    // TODO: Check member declaration type
}

//=====  Annotations  ==========================================================

abstract node ElementValue {}

/**
 * MarkerAnnotation and SingleElementAnnotation 
 * are shorthands for NormalAnnotation
 */
abstract node Annotation : ElementValue
{
    child QualifiedIdentifier typeName;
}

node MarkerAnnotation : Annotation
{
}

node SingleElementAnnotation : Annotation
{
    child ElementValue value;
}

node NormalAnnotation : Annotation
{
    child ElementValuePair* optElementValuePairList;
}

node ConditionalExpressionElementValue : ElementValue
{
    child ConditionalExpression expression;
}

node ElementValueArrayInitializer : ElementValue
{
    child ElementValue* optElementValueList;
}

node ElementValuePair
{
    child Identifier id;
    child ElementValue value;
}

//=====  Member declarations  ==================================================

/**
 * Union of all type declaration members
 */
abstract node MemberDeclaration : TokenRangeNode {}

//=====  class members ==================================================

node FieldDeclaration : MemberDeclaration
{
    child Annotation* optAnnotationList;
    
    /**
     * Allowed values are:
     *   PUBLIC, PROTECTED, PRIVATE, STATIC,
     *   FINAL, TRANSIENT, VOLATILE
     */
    attribute Modifiers modifiers;
    child Type type;
    child VariableDeclarator+ declaratorList;
}

node VariableDeclarator
{
    child VariableDeclaratorId name;
    child VariableInitializer? optInitializer;
}

node VariableDeclaratorId
{
    child Identifier name;
    attribute int dim;
}

abstract node VariableInitializer {}

node ExpressionVariableInitializer : VariableInitializer
{
    child Expression expression;
}

node ArrayInitializer : VariableInitializer
{
    child VariableInitializer* optInitializerList;
}

node MethodDeclaration : MemberDeclaration
{
    child Annotation* optAnnotationList;
    
    /**
     * Allowed values are: 
     *   PUBLIC, PROTECTED, PRIVATE, ABSTRACT, STATIC,
     *   FINAL, SYNCHRONIZED, NATIVE, STRICTFP
     */
    attribute Modifiers modifiers;
    child MethodHeader @header;
    child Block? optBody;
}

node MethodHeader
{
    child TypeParameter* optTypeParameterList;
    child OptionalType resultType;
    child Identifier name;
    child FormalParameter* optFormalParameterList;
    
    /**
     * dimension stored in resultType
     */
    attribute bool variableArity; // "..."
    
    /**
     * Class types or type variables
     */
    child QualifiedParameterizedIdentifier* optExceptionList;
}

node FormalParameter
{
    child Annotation* optAnnotationList;
    attribute bool isFinal;
    
    /**
     * if "..." then ArrayType, variableArity is "true" in a parent node
     */
    attribute bool variableArity;
    child Type type;
    child Identifier name;
}

node InstanceInitializer : MemberDeclaration
{
    child Block block;
}

node StaticInitializer : MemberDeclaration
{
    child Block block;
}

node ConstructorDeclaration : MemberDeclaration
{
    child Annotation* optAnnotationList;
    
    /**
     * Allowed values are:
     *   PUBLIC, PROTECTED, PRIVATE
     */
    attribute Modifiers modifiers;
    child TypeParameter* optTypeParameterList;
    child Identifier  name;
    child FormalParameter* optFormalParameterList;
    attribute bool variableArity; // "..."
    child QualifiedParameterizedIdentifier* optExceptionList;
    child ExplicitConstructorInvocation? optExplicitConstructorInvocation;
    child BlockStatement* optBlockStatementList; 
}

abstract node ExplicitConstructorInvocation
{
    child ReferenceType* optTypeArgumentList;
    child Expression* optArgumentList;
}

node ThisExplicitConstructorInvocation : ExplicitConstructorInvocation {}

node SuperExplicitConstructorInvocation : ExplicitConstructorInvocation
{
    child Primary? optQualification;
}

//=====  enum class members  ==================================================

node EnumConstant : TokenRangeNode
{
    child Annotation* optAnnotationList;
    child Identifier name;
    child Expression* optArgumentList;
    child MemberDeclaration* optMemberDeclarationList;
}

//=====  interface members  ==================================================

node ConstantDeclaration : MemberDeclaration
{
    child Annotation* optAnnotationList;
    
    /**
     * Allowed values are:
     *   PUBLIC, STATIC, FINAL
     */
    attribute Modifiers modifiers;
    child Type type;
    child VariableDeclarator+ declaratorList;
}

node AbstractMethodDeclaration : MemberDeclaration
{
    child Annotation* optAnnotationList;
    
    /**
     * Allowed values are:
     *   PUBLIC, ABSTRACT
     */
    attribute Modifiers modifiers;
    child MethodHeader methodHeader;
}

//=====  member of annotation types  ==================================================

node AnnotationTypeElementDeclaration : MemberDeclaration
{
    child Annotation* optAnnotationList;

    /**
     * Allowed values are:
     *   PUBLIC, ABSTRACT
     */
    attribute Modifiers modifiers;
    child Type type;
    child Identifier name;
    child ElementValue? optDefaultValue;
}

//=====  Statements  ==================================================

node Block : Statement
{
    attribute <antlr.Token> startToken;
    attribute <antlr.Token> endToken;
    child BlockStatement* optStatementList;

    attribute custom late noset <com.unitesk.atp.text.location.Position> startPosition
    get { startPosition = TokenUtils.getPosition( startToken ); };
    
    attribute custom late noset <com.unitesk.atp.text.location.Position> endPosition
    get { endPosition = TokenUtils.getEndPosition( endToken ); };
}

abstract node BlockStatement {}

node LocalVariableDeclarationStatement : BlockStatement
{
    child LocalVariableDeclaration declaration;
}

node LocalVariableDeclaration
{
    child Annotation* optAnnotationList;
    attribute bool isFinal;
    child Type type;
    child VariableDeclarator+ declaratorList;
}

node ClassDeclarationStatement : BlockStatement
{
    child ClassDeclaration declaration;
}

node EnumDeclarationStatement : BlockStatement
{
    child EnumDeclaration declaration;
}

abstract node Statement : BlockStatement {}

/**
 * Required for statements like "for(;;);"
 *
 * BNF: EmptyStatement
 */
node EmptyStatement : Statement {}

node LabeledStatement : Statement
{
    child Identifier label;
    child Statement statement;
}

node ExpressionStatement : Statement
{
    // TODO: check expression type
    child Expression statementExpression;
}

node IfStatement : Statement
{
    child Expression condition;
    child Statement  thenStatement;
    child Statement? optElseStatement;
}

node AssertStatement : Statement
{
    child Expression condition;
    child Expression? optMessage;
}

node SwitchStatement : Statement
{
    child Expression condition;
    child SwitchElement* optElementList;
}

abstract node SwitchElement {}

node StatementSwitchElement : SwitchElement
{
    child BlockStatement+ statementList;
}

node SwitchLabel : SwitchElement
{
    child Expression label;
    // TODO: child should be either constant expression
    //       or an identifier, which is an enum constant
    //       reference, in the last case it will be expression name
}

node DefaultSwitchLabel : SwitchElement {}

node WhileStatement : Statement
{
    child Expression condition;
    child Statement @body;
}

node DoStatement : Statement
{
    child Statement @body;
    child Expression condition;
}

abstract node ForStatement : Statement 
{
    child Statement @body;
}

node BasicForStatement : ForStatement
{
    child ForInit? optInit;
    child Expression? optCondition;
    // TODO: check expression type
    child Expression* optForUpdateStatementExpressionList;
}

abstract node ForInit {}

node StatementExpressionListForInit : ForInit
{
    // TODO: check expression type
    child Expression+ statementExpressionList;
}

node LocalVariableDeclarationForInit : ForInit
{
    child LocalVariableDeclaration declaration;
}

node EnhancedForStatement : ForStatement
{
    child FormalParameter parameter;
    child Expression expression;
}

node BreakStatement : Statement
{
    child Identifier? optLabel;
}

node ContinueStatement : Statement
{
    child Identifier? optLabel;
}

node ReturnStatement : Statement
{
    child Expression? optExpression;
}

node ThrowStatement : Statement
{
    child Expression expression;
}

node SynchronizedStatement : Statement
{
    child Expression expression;
    child Block block;
}

node TryStatement : Statement
{
    child Block block;
    child CatchClause* optCatchClauseList;
    child Block? optFinallyBlock;

    constructor
    {
        if( sizeOptCatchClauseList() == 0 && getOptFinallyBlock() == null )
        {
            // TODO: refine exception type and message
            throw new IllegalArgumentException("There is neither catch clause nor finally block");
        }
    }
}

node CatchClause
{
   child FormalParameter parameter;
   child Block block;
}

//=====  Expressions  ==================================================

abstract node Primary : PostfixExpression {}

abstract node PrimaryNoNewArray : Primary {}

abstract node Literal : PrimaryNoNewArray 
{
    attribute <antlr.Token> token;
    
    attribute string value;

    attribute custom late noset <com.unitesk.atp.text.location.Position> startPosition
    get { startPosition = TokenUtils.getPosition( token ); };
    
    attribute custom late noset <com.unitesk.atp.text.location.Position> endPosition
    get { endPosition = TokenUtils.getEndPosition( token ); };
}

node IntegerLiteral : Literal
{
}

node FloatingPointLiteral : Literal
{
}

node CharacterLiteral : Literal
{
}

node StringLiteral : Literal
{
}

node BooleanLiteral : Literal
{
}

node NullLiteral : Literal
{
}

node ClassLiteral : PrimaryNoNewArray
{
    child OptionalType type;
}

node ThisAccessExpression : PrimaryNoNewArray
{
    child QualifiedIdentifier? optClassName;
}

node ParenthesizedExpression : PrimaryNoNewArray
{
    child Expression expression;
}

abstract node ClassInstanceCreationExpression : PrimaryNoNewArray
{
    child ReferenceType* optConstructorTypeParameterList;
    child Expression*    optArgumentList;
    child MemberDeclaration* optMemberDeclarationList;
}

node SimpleClassInstanceCreationExpression : ClassInstanceCreationExpression
{
    child QualifiedParameterizedIdentifier type;
}

node PrimaryClassInstanceCreationExpression : ClassInstanceCreationExpression
{
    child Primary qualification;
    child Identifier type;
    child ReferenceType* optTypeParameterList;
}

abstract node FieldAccess : PrimaryNoNewArray
{
    child Identifier name;
}

node PrimaryFieldAccess : FieldAccess
{
    child Primary expression;
}

node SuperFieldAccess : FieldAccess
{
    child QualifiedIdentifier? optClassName;
}

abstract node MethodInvocation : PrimaryNoNewArray 
{
    child Expression* optArgumentList;
}

node SimpleNameMethodInvocation : MethodInvocation
{
    child QualifiedIdentifier name;
}

abstract node IdMethodInvocation : MethodInvocation
{
    child ReferenceType* optTypeArgumentList;
    child Identifier name;
}

node ParameterizedNameMethodInvocation : IdMethodInvocation
{
    child QualifiedIdentifier? optTypeName;
}

node PrimaryMethodInvocation : IdMethodInvocation
{
    child Primary expression;
}

node SuperMethodInvocation : IdMethodInvocation
{
    child QualifiedIdentifier? optClassName;
}

abstract node ArrayAccess : PrimaryNoNewArray 
{
    /*
     * TODO: may be list of indexes?
     */
    child Expression index;
}

node ExpressionNameArrayAccess : ArrayAccess
{
    child QualifiedIdentifier expressionName;
}

// PrimaryNoNewArray changed to Primary
// because javac allows expressions like as "new int[]{}[2]"
node PrimaryArrayAccess : ArrayAccess
{
    child Primary expression;
}

abstract node ArrayCreationExpression : Primary
{
    child Type type;
    attribute int dim;
}

node ExprArrayCreationExpression : ArrayCreationExpression
{
    child Expression+ exprList;
}

node InitArrayCreationExpression : ArrayCreationExpression
{
    child ArrayInitializer initializer;
}

abstract node PostfixExpression : UnaryExpressionNotPlusMinus {}

// ExpressionName : PostfixExpression
// --> ExpressionName : Primary
node ExpressionName : Primary
{
    child QualifiedIdentifier name;
}

node PostIncrementExpression : PostfixExpression
{
    child PostfixExpression expression;
}

node PostDecrementExpression : PostfixExpression
{
    child PostfixExpression expression;
}

abstract node UnaryExpression : MultiplicativeExpression {}

node PreIncrementExpression : UnaryExpression
{
    child UnaryExpression expression;
}

node PreDecrementExpression : UnaryExpression
{
    child UnaryExpression expression;
}

node UnaryPlusExpression : UnaryExpression 
{
    child UnaryExpression expression;
}

node UnaryMinusExpression : UnaryExpression 
{
    child UnaryExpression expression;
}

abstract node UnaryExpressionNotPlusMinus : UnaryExpression {}

node BitwiseComplementExpression : UnaryExpressionNotPlusMinus
{
    child UnaryExpression expression;
}

node LogicalComplementExpression : UnaryExpressionNotPlusMinus
{
    child UnaryExpression expression;
}

abstract node CastExpression : UnaryExpressionNotPlusMinus {}

node PrimitiveTypeCastExpression : CastExpression
{
    child PrimitiveType type;
    attribute int dim;
    child UnaryExpression expression;
}

node ReferenceTypeCastExpression : CastExpression
{
    child ReferenceType type;
    child UnaryExpressionNotPlusMinus expression;
}

abstract node MultiplicativeExpression : AdditiveExpression {}

enum MultiplicativeSign
{
    MULT, DIV, REMAINDER
}

[ java_translate.static = true; ]
operation string toStringMultiplicativeSign( virtual MultiplicativeSign sign )
{
    case( MULT ):      { return "*"; }
    case( DIV ):       { return "/"; }
    case( REMAINDER ): { return "%"; }
}

node StrictMultiplicativeExpression : MultiplicativeExpression
{
    child MultiplicativeExpression left;
    attribute MultiplicativeSign sign;
    child UnaryExpression right;
}

abstract node AdditiveExpression : ShiftExpression {}

enum AdditiveSign
{
    PLUS, MINUS
}

[ java_translate.static = true; ]
operation string toStringAdditiveSign( virtual AdditiveSign sign )
{
    case( PLUS ):  { return "+"; }
    case( MINUS ): { return "-"; }
}

node StrictAdditiveExpression : AdditiveExpression
{
    child AdditiveExpression left;
    attribute AdditiveSign sign;
    child MultiplicativeExpression right;
}

abstract node ShiftExpression : RelationalExpression {}

enum ShiftSign
{
    LEFT, RIGHT, URIGHT
}

[ java_translate.static = true; ]
operation string toStringShiftSign( virtual ShiftSign sign )
{
    case( LEFT ):   { return "<<"; }
    case( RIGHT ):  { return ">>"; }
    case( URIGHT ): { return ">>>"; }
}

node StrictShiftExpression : ShiftExpression
{
    child ShiftExpression left;
    attribute ShiftSign sign;
    child AdditiveExpression right;
}

abstract node RelationalExpression : EqualityExpression {}

enum RelationalSign
{
    LT, GT, LE, GE
}

[ java_translate.static = true; ]
operation string toStringRelationalSign( virtual RelationalSign sign )
{
    case( LT ): { return "<"; }
    case( GT ): { return ">"; }
    case( LE ): { return "<="; }
    case( GE ): { return ">="; }
}

node StrictRelationalExpression : RelationalExpression
{
    child RelationalExpression left;
    attribute RelationalSign sign;
    child ShiftExpression right;
}

node InstanceOfRelationalExpression : RelationalExpression
{
    child RelationalExpression expression;
    child ReferenceType type;
}

abstract node EqualityExpression : AndExpression {}

enum EqualitySign
{
    EQ, NE
}

[ java_translate.static = true; ]
operation string toStringEqualitySign( virtual EqualitySign sign )
{
    case( EQ ): { return "=="; }
    case( NE ): { return "!="; }
}

node StrictEqualityExpression : EqualityExpression
{
    child EqualityExpression left;
    attribute EqualitySign sign;
    child RelationalExpression right;
}

abstract node AndExpression : ExclusiveOrExpression {}

node StrictAndExpression : AndExpression
{
    child AndExpression left;
    child EqualityExpression right;
}

abstract node ExclusiveOrExpression : InclusiveOrExpression {}

node StrictExclusiveOrExpression : ExclusiveOrExpression
{
    child ExclusiveOrExpression left;
    child AndExpression right; 
}

abstract node InclusiveOrExpression : ConditionalAndExpression {}

node StrictInclusiveOrExpression : InclusiveOrExpression
{
    child InclusiveOrExpression left;
    child ExclusiveOrExpression right; 
}

abstract node ConditionalAndExpression : ConditionalOrExpression {}

node StrictConditionalAndExpression : ConditionalAndExpression
{
    child ConditionalAndExpression left;
    child InclusiveOrExpression right; 
}

abstract node ConditionalOrExpression : ConditionalExpression {}

node StrictConditionalOrExpression : ConditionalOrExpression
{
    child ConditionalOrExpression left;
    child ConditionalAndExpression right; 
}

abstract node ConditionalExpression : Expression {}

node StrictConditionalExpression : ConditionalExpression
{
    child ConditionalOrExpression condition;
    child Expression thenExpr; 
    child ConditionalExpression elseExpr; 
}

abstract node Expression {}

enum AssignmentOperator
{ 
   ASSIGN, ASSIGN_PLUS, ASSIGN_MINUS, ASSIGN_MULTIPLY,
   ASSIGN_DIVIDE, ASSIGN_REMAINDER, ASSIGN_AND,
   ASSIGN_OR, ASSIGN_XOR, ASSIGN_LSHIFT, ASSIGN_RSHIFT,
   ASSIGN_URSHIFT 
} 

[ java_translate.static = true; ]
operation string toStringAssignmentOperator( virtual AssignmentOperator op )
{
    case( ASSIGN ):           { return "="; }
    case( ASSIGN_PLUS ):      { return "+="; }
    case( ASSIGN_MINUS ):     { return "-="; }
    case( ASSIGN_MULTIPLY ):  { return "*="; }
    case( ASSIGN_DIVIDE ):    { return "/="; }
    case( ASSIGN_REMAINDER ): { return "%="; }
    case( ASSIGN_AND ):       { return "&="; }
    case( ASSIGN_OR ):        { return "|="; }
    case( ASSIGN_XOR ):       { return "^="; }
    case( ASSIGN_LSHIFT ):    { return "<<="; }
    case( ASSIGN_RSHIFT ):    { return ">>="; }
    case( ASSIGN_URSHIFT ):   { return ">>>="; }
}

node Assignment : Expression
{
    child Expression left;
    attribute AssignmentOperator operator;
    child Expression right; 
}

//=====  Common declarations  ==================================================

node QualifiedIdentifier : BaseNode
{
    child Identifier+ identifierList;

    attribute custom late noset <com.unitesk.atp.text.location.Position> startPosition
    get { startPosition = getIdentifierList(0).getStartPosition(); };
    
    attribute custom late noset <com.unitesk.atp.text.location.Position> endPosition
    get { endPosition = getIdentifierList(sizeIdentifierList()-1).getEndPosition(); };
}

node Identifier : TokenNode
{
    attribute string value;
}