// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

grammar IDL;

@header {
    //package com.eprosima.idl.parser.grammar;

    import com.eprosima.idl.context.Context;
    import com.eprosima.idl.generator.manager.TemplateManager;
    import com.eprosima.idl.generator.manager.TemplateGroup;
    import com.eprosima.idl.generator.manager.TemplateUtil;
    import com.eprosima.idl.parser.typecode.*;
    import com.eprosima.idl.parser.tree.*;
    import com.eprosima.idl.util.Pair;
    import com.eprosima.idl.parser.strategy.DefaultErrorStrategy;
    import com.eprosima.idl.parser.listener.DefaultErrorListener;
    import com.eprosima.idl.parser.exception.ParseException;

    import java.util.Vector;
}

@parser::members {
    private TemplateManager tmanager = null;
    private Context ctx = null;

    public Context getContext_()
    {
        return ctx;
    }
}

@lexer::members{
    Context ctx = null;

    public void setContext(Context _ctx)
    {
        ctx = _ctx;
    }
}

specification [Context context, TemplateManager templatemanager, TemplateGroup maintemplates] returns [Specification spec = null]
@init{
    //! Used to catch each definition grammar element in the whole IDL file.
    Pair<Vector<Definition>, TemplateGroup> dtg = null;
    List<Definition> specificationChildren = new ArrayList<Definition>();
    ctx = context;
    tmanager = templatemanager;

    // Set error handler
    DefaultErrorListener listener = new DefaultErrorListener(ctx);
    this.setErrorHandler(DefaultErrorStrategy.INSTANCE);
    // Select listener for errors.
    ((Lexer)this._input.getTokenSource()).removeErrorListeners();
    ((Lexer)this._input.getTokenSource()).addErrorListener(listener);
    this.removeErrorListeners();
    this.addErrorListener(listener);
}
    :   import_decl*
    (
        definition[null, null]
        {
            dtg=$definition.dtg;
            if (dtg!=null) {
                if(maintemplates != null && dtg.second() != null) { // Don't add templates for forward decl.
                    maintemplates.setAttribute("definitions", dtg.second());
                }
                for(int count = 0; count < dtg.first().size(); ++count)
                {
                    ctx.addDefinition(dtg.first().get(count));
                    specificationChildren.add(dtg.first().get(count));
                }
            }
        }
    )+
    {
        if(getNumberOfSyntaxErrors() == 0)
        {
            $spec = new Specification();
            $spec.setDefinitions(specificationChildren);
        }
    }
    ;

/*!
 * @brief This grammar element represents a global definition: type declaration, interface, module, etc...
 * @return A pair with the object that represents the definition (interface for other objects like module grammar element)
 * and the template group generated by the definition (in fact the template group generated by the element who inherits).
 */
definition [Vector<Annotation> annotations, ArrayList<Definition> defs] returns [Pair<Vector<Definition>, TemplateGroup> dtg = null]
@init {
    // TODO Cambiar esto. No me gusta la forma.

    Vector<Definition> vector = new Vector<Definition>();
    Pair<Vector<TypeDeclaration>, TemplateGroup> tdtg = null;
    Pair<ConstDeclaration, TemplateGroup> cdtg = null;
    Pair<com.eprosima.idl.parser.tree.Exception, TemplateGroup> etg = null;
    Pair<Interface, TemplateGroup> itg = null;
    Pair<com.eprosima.idl.parser.tree.Module, TemplateGroup> mtg = null;
    Pair<AnnotationDeclaration, TemplateGroup> atg = null;

    if(annotations == null) annotations = new Vector<Annotation>();
}
    :   type_decl[annotations, defs] SEMICOLON
        {
            tdtg=$type_decl.returnPair;
            if(tdtg!=null)
            {
                for(TypeDeclaration tydl : tdtg.first())
                    vector.add(tydl);
                $dtg = new Pair<Vector<Definition>, TemplateGroup>(vector, tdtg.second());
            }
        }  // Type Declaration
    |   const_decl[null] SEMICOLON { cdtg=$const_decl.returnPair; if(cdtg!=null){ vector.add(cdtg.first()); $dtg = new Pair<Vector<Definition>, TemplateGroup>(vector, cdtg.second());}} // Const Declaration
    |   except_decl SEMICOLON { etg=$except_decl.returnPair; if(etg!=null){ vector.add(etg.first()); $dtg = new Pair<Vector<Definition>, TemplateGroup>(vector, etg.second());}} // Exception.
    |   interface_or_forward_decl[annotations] SEMICOLON { itg=$interface_or_forward_decl.itg; if(itg!=null){ vector.add(itg.first()); $dtg = new Pair<Vector<Definition>, TemplateGroup>(vector, itg.second());}} // Interface
    |   module SEMICOLON { mtg=$module.returnPair; if(mtg!=null){ vector.add(mtg.first()); $dtg = new Pair<Vector<Definition>, TemplateGroup>(vector, mtg.second());}} // Module
    |   value SEMICOLON
    |   type_id_decl SEMICOLON
    |   type_prefix_decl SEMICOLON
    |   event SEMICOLON
    |   component SEMICOLON
    |   home_decl SEMICOLON
    |   annotation_decl SEMICOLON { atg=$annotation_decl.returnPair; if(atg!=null){ vector.add(atg.first()); $dtg = new Pair<Vector<Definition>, TemplateGroup>(vector, atg.second());}}
    |   annotation_appl
        {
            annotations.add($annotation_appl.annotation);
        }
        aux_definition[annotations, defs]{$dtg=$aux_definition.dtg;}
    ;

aux_definition [Vector<Annotation> annotations, ArrayList<Definition> defs] returns [Pair<Vector<Definition>, TemplateGroup> dtg = null]
    : definition[annotations, defs] {$dtg=$definition.dtg;}
    ;

/*!
 * @brief This grammar expression catches a module definition.
 * @return This grammar expression returns the Module object as DefinitionContainer to be stored.
 * Also the TemplateGroup of module is returned.
 */
module returns [Pair<com.eprosima.idl.parser.tree.Module, TemplateGroup> returnPair = null]
@init{
    com.eprosima.idl.parser.tree.Module moduleObject = null;
    TemplateGroup moduleTemplates = null;
    TemplateGroup tg = null;
    // Store old namespace.
    String name = null, old_scope = ctx.getScope();
    Token tk = null;
}
    :   ( KW_MODULE )
    {
        tk = _input.LT(1);
    }
    identifier
    {
        String error = ctx.checkIdentifier(Definition.Kind.MODULE, ctx.getScope(), $identifier.id);
        if (error != null)
        {
            throw new ParseException(null, "Illegal identifier: " + error);
        }
        name = ctx.removeEscapeCharacter($identifier.id);
        // Check if the module already was defined.
        moduleObject = ctx.existsModule(ctx.getScope() + "::" + name);

        if(moduleObject == null)
        {
            // Create the Module object.
            moduleObject = new com.eprosima.idl.parser.tree.Module(ctx.getScopeFile(), ctx.isInScopedFile(), ctx.getScope(), name, tk);
            //ctx.addPendingModule(moduleObject);
        }

        // Add the module to the context.
        ctx.addModule(moduleObject);

        if(ctx.isInScopedFile() || ctx.isScopeLimitToAll()) {
            if(tmanager != null) {
                moduleTemplates = tmanager.createTemplateGroup("module");
                moduleTemplates.setAttribute("ctx", ctx);
                // Set the module object to the TemplateGroup of the module.
                moduleTemplates.setAttribute("module", moduleObject);
            }
        }

        // Update to a new namespace.
        if(old_scope.isEmpty())
            ctx.setScope(name);
        else
            ctx.setScope(old_scope + "::" + name);
    }
    // Each definition is stored in the Module and each TemplateGroup is set as attribute in the TemplateGroup of the module.
    LEFT_BRACE
    definition_list[moduleObject]{ tg=$definition_list.dlTemplates; if(moduleTemplates!=null && tg!=null)moduleTemplates.setAttribute("definition_list", tg);}
    RIGHT_BRACE
    {
        // Set the old namespace.
        ctx.setScope(old_scope);
        // Create the returned data.
        $returnPair = new Pair<com.eprosima.idl.parser.tree.Module, TemplateGroup>(moduleObject, moduleTemplates);
    }
    ;


/*!
 * @brief This grammar expression catches a list of definitions.
 * @param dc An object that inherits from DefinitionContainer. This object could store all definitions.
 * @return This grammar expression returns the template group with all template groups of definitions.
 */
definition_list [DefinitionContainer dc] returns [TemplateGroup dlTemplates]
@init{
    Pair<Vector<Definition>, TemplateGroup> dtg = null;
    if(tmanager != null) {
        $dlTemplates = tmanager.createTemplateGroup("definition_list");
    }
}
    :   (
        definition[null, dc.getDefinitions()]
        {
            dtg=$definition.dtg;
            if(dtg!=null)
            {
                for(int count = 0; count < dtg.first().size(); ++count)
                    dc.add(dtg.first().get(count));

                if($dlTemplates != null && dtg.second() != null)
                {
                    // Set parent
                    dtg.second().setAttribute("parent", dc);
                    // Print template into definitions rule
                    $dlTemplates.setAttribute("definitions", dtg.second());
                }
            }
        }
        )+
    ;

/*!
 * @brief This grammar expression catches currently a interface declaration.
 * @return This grammar expression returns the interface object and its template group.
 */
interface_or_forward_decl [Vector<Annotation> annotations] returns [Pair<Interface, TemplateGroup> itg = null]
    :   interface_decl[annotations] {$itg = $interface_decl.returnPair;}
    |   forward_decl
    ;

interface_decl [Vector<Annotation> annotations] returns [Pair<Interface, TemplateGroup> returnPair = null]
@init {
    Token tk = null;
    Interface interfaceObject = null;
    TemplateGroup interfaceTemplates = null;
    TemplateGroup tg = null;
    String name = null, old_scope = ctx.getScope();
}
    :   ( ( KW_ABSTRACT | KW_LOCAL )? ( KW_INTERFACE )
        {
            tk = _input.LT(1);
        }
        identifier
        {
            String error = ctx.checkIdentifier(Definition.Kind.INTERFACE, ctx.getScope(), $identifier.id);
            if (error != null)
            {
                throw new ParseException(null, "Illegal identifier: " + error);
            }
            name = ctx.removeEscapeCharacter($identifier.id);
            // Retrieve from forward or Create the Interface object.
            interfaceObject = ctx.getInterface(name);
            if (interfaceObject == null)
            {
                interfaceObject = ctx.createInterface(name, tk);
            }

            // Add annotations.
            for(Annotation annotation : annotations)
            {
               interfaceObject.addAnnotation(ctx, annotation);
            }

            if(ctx.isInScopedFile() || ctx.isScopeLimitToAll())
            {
                if(tmanager != null) {
                    interfaceTemplates = tmanager.createTemplateGroup("interface");
                    interfaceTemplates.setAttribute("ctx", ctx);
                    // Set the the interface object to the TemplateGroup of the module.
                    interfaceTemplates.setAttribute("interface", interfaceObject);
                }
            }

            // Update to a new namespace.
            if(old_scope.isEmpty())
                ctx.setScope(name);
            else
                ctx.setScope(old_scope + "::" + name);
        }
        ( interface_inheritance_spec[interfaceObject] )?
        LEFT_BRACE interface_body[interfaceObject]{ tg=$interface_body.elTemplates; if(interfaceTemplates!=null && tg!=null)interfaceTemplates.setAttribute("export_list", tg);} RIGHT_BRACE )
        {
           // Set the old namespace.
           ctx.setScope(old_scope);
           // Create the returned data.
           $returnPair = new Pair<Interface, TemplateGroup>(interfaceObject, interfaceTemplates);
        }
    ;

forward_decl
@init {
    Token tk = null;
    TemplateGroup interfaceTemplates = null;
}
    :   ( KW_ABSTRACT | KW_LOCAL )? ( KW_INTERFACE )
        {
            tk = _input.LT(1);
        }
        identifier
        {
            // Create the Interface object.
            Interface interfaceObject = ctx.createInterface($identifier.id, tk);

            if(ctx.isInScopedFile() || ctx.isScopeLimitToAll())
            {
                if(tmanager != null)
                {
                    interfaceTemplates = tmanager.createTemplateGroup("interface");
                    interfaceTemplates.setAttribute("ctx", ctx);
                    // Set the the interface object to the TemplateGroup of the module.
                    interfaceTemplates.setAttribute("interface", interfaceObject);
                }
            }
        }
    ;


/*!
 * @brief This grammar expression catches a list of exports.
 * @param dc An object that inherits from ExportContainer. This object could store all exports.
 * @return This grammar expression returns the template group with all template groups of exports.
 */
interface_body [ExportContainer ec] returns [TemplateGroup elTemplates]
@init{
        Pair<Vector<Export>, TemplateGroup> etg = null;
        if(tmanager != null) {
            $elTemplates = tmanager.createTemplateGroup("export_list");
        }
}
    :   (
        export[null]
        {
            etg=$export.etg;
            if(etg!=null)
            {
                for(int count = 0; count < etg.first().size(); ++count)
                {
                    ec.add(etg.first().get(count));
                    etg.first().get(count).resolve(ctx);
                }

                if($elTemplates != null && etg.second() != null)
                {
                    // Add parent
                    etg.second().setAttribute("parent", ec);
                    // Print template into exports rule
                    $elTemplates.setAttribute("exports", etg.second());
                }
            }
        }
        )*
    ;

/*!
 * @brief This grammar element represents an export of a interface: type declaration, operation, etc...
 * @return A pair with the object that represents the export (interface for other objects like operation grammar element)
 * and the template group generated by the export (in fact the template group generated by the element who inherits).
 */
export [Vector<Annotation> annotations] returns [Pair<Vector<Export>, TemplateGroup> etg = null]
@init {
        // TODO Cambiar esto. No me gusta la forma.
        Vector<Export> vector = new Vector<Export>();
        Pair<Vector<TypeDeclaration>, TemplateGroup> tetg = null;
        Pair<ConstDeclaration, TemplateGroup> cetg = null;
        Pair<Operation, TemplateGroup> oetg = null;
        Pair<com.eprosima.idl.parser.tree.Exception, TemplateGroup> eetg = null;

        if(annotations == null) annotations = new Vector<Annotation>();
}
    :   type_decl[annotations, null] SEMICOLON { tetg=$type_decl.returnPair; if(tetg!=null){ for(TypeDeclaration tydl : tetg.first()) vector.add(tydl); $etg = new Pair<Vector<Export>, TemplateGroup>(vector, tetg.second());} }  // Type Declaration
    |   const_decl[null] SEMICOLON { cetg=$const_decl.returnPair; if(cetg!=null){ vector.add(cetg.first()); $etg = new Pair<Vector<Export>, TemplateGroup>(vector, cetg.second());}} // Const Declaration
    |   except_decl SEMICOLON { eetg=$except_decl.returnPair; if(eetg!=null){ vector.add(eetg.first()); $etg = new Pair<Vector<Export>, TemplateGroup>(vector, eetg.second());}}  // Exception
    |   attr_decl SEMICOLON
    { System.out.println("WARNING (File " + ctx.getFilename() + ", Line " + (_input.LT(1) != null ? _input.LT(1).getLine() - ctx.getCurrentIncludeLine() : "1") + "): Attribute declarations are not supported. Ignoring..."); }
    |   op_decl[annotations] SEMICOLON { oetg=$op_decl.returnPair; if(oetg!=null){ vector.add(oetg.first()); $etg = new Pair<Vector<Export>, TemplateGroup>(vector, oetg.second());}}  // Operation
    |   type_id_decl SEMICOLON
    |   type_prefix_decl SEMICOLON
    |   annotation_appl
        {
            annotations.add($annotation_appl.annotation);
        }
        aux_export[annotations]{$etg=$aux_export.etg;}
    ;

aux_export [Vector<Annotation> annotations] returns [Pair<Vector<Export>, TemplateGroup> etg = null]
    : export[annotations] {$etg=$export.etg;}
    ;

interface_inheritance_spec [Interface interfaceObject]
@init{
        Vector<Pair<String, Token>> iflist = null;
}
    :   COLON scoped_name_list { iflist=$scoped_name_list.retlist; }
    {
        for(Pair<String, Token> pair : iflist)
        {
            Interface base = ctx.getInterface(pair.first());

            if(base != null)
            {
                if(!$interfaceObject.addBase(base))
                    throw new ParseException(pair.second(), " is duplicated.");
            }
            else
            {
               throw new ParseException(pair.second(), "was not defined previously");
            }
        }
    }
    ;

interface_name
    :   scoped_name
    ;

scoped_name_list returns [Vector<Pair<String, Token>> retlist = null]
@init{
   $retlist = new Vector<Pair<String, Token>>();
   Pair<String, Token> pair = null;
}
    :    scoped_name{ pair=$scoped_name.pair; $retlist.add(pair);} (COMA scoped_name{ pair=$scoped_name.pair; $retlist.add(pair);})*
    ;

scoped_name returns [Pair<String, Token> pair = null]
@init{
    String literalStr = "";
    Token tk = _input.LT(1);
}
:   ( {literalStr += _input.LT(1).getText();} DOUBLE_COLON )?
      {literalStr += _input.LT(1).getText();} (ID /* identifier */ | KW_OBJECT )
    (
        /* An escaped KW may be part of the scoped_name */
        {literalStr += _input.LT(1).getText();} DOUBLE_COLON
            (identifier { literalStr+=$identifier.id; } | KW_OBJECT { literalStr+="Object"; } )
    )*
    {$pair = new Pair<String, Token>(literalStr, tk);}
    ;

value
@init{
    System.out.println("WARNING (File " + ctx.getFilename() + ", Line " + (_input.LT(1) != null ? _input.LT(1).getLine() - ctx.getCurrentIncludeLine() : "1") + "): ValueType declarations are not supported. Ignoring...");
}
    :   ( value_decl | value_abs_decl | value_box_decl | value_forward_decl )
    ;

value_forward_decl
    :   ( KW_ABSTRACT )? KW_VALUETYPE ID
    ;

value_box_decl
    :   KW_VALUETYPE ID type_spec[null]
    ;

value_abs_decl
    :   KW_ABSTRACT KW_VALUETYPE ID value_inheritance_spec LEFT_BRACE export[null]* RIGHT_BRACE
    ;

value_decl
    :   value_header LEFT_BRACE  value_element* RIGHT_BRACE
    ;

value_header
    :   ( KW_CUSTOM )? KW_VALUETYPE ID value_inheritance_spec
    ;

value_inheritance_spec
    :   ( COLON ( KW_TRUNCATABLE )? value_name ( COMA value_name )* )? ( KW_SUPPORTS interface_name ( COMA interface_name )* )?
    ;

value_name
    :   scoped_name
    ;

value_element
    :   ( export[null] |  state_member | init_decl )
    ;

state_member
    :   ( KW_PUBLIC | KW_PRIVATE ) type_spec[null] declarators SEMICOLON
    ;

init_decl
    :   KW_FACTORY ID LEFT_BRACKET ( init_param_decls )? RIGHT_BRACKET ( raises_expr )? SEMICOLON
    ;

init_param_decls
    :   init_param_decl ( COMA init_param_decl )*
    ;

init_param_decl
    :   init_param_attribute param_type_spec simple_declarator
    ;

init_param_attribute
    :   KW_IN
    ;

const_decl [AnnotationDeclaration annotation] returns [Pair<ConstDeclaration, TemplateGroup> returnPair = null]
@init {
    ConstDeclaration constDecl = null;
    TypeCode typecode = null;
    String constName = null, constValue = null;
    TemplateGroup constTemplates = null;
    if(tmanager != null) {
        constTemplates = tmanager.createTemplateGroup("const_decl");
    }
    Token tk = null;
}
    :   KW_CONST const_type[annotation] { typecode=$const_type.typecode; tk = _input.LT(1);} identifier
        {
            String error = ctx.checkIdentifier(Definition.Kind.CONST_DECLARATION, ctx.getScope(), $identifier.id);
            if (error != null)
            {
                throw new ParseException(null, "Illegal identifier: " + error);
            }
            constName = ctx.removeEscapeCharacter($identifier.id);
        }
        EQUAL const_exp { constValue=$const_exp.literalStr; }
    {
        if(typecode != null)
        {
            constDecl = new ConstDeclaration(ctx.getScopeFile(), ctx.isInScopedFile(), ctx.getScope(), constName, typecode, constValue, tk);

            if(constTemplates != null)
            {
                constTemplates.setAttribute("ctx", ctx);
                constTemplates.setAttribute("const", constDecl);
            }

            $returnPair = new Pair<ConstDeclaration, TemplateGroup>(constDecl, constTemplates);
       }
    }
    ;

// TODO Not supported fixed types: Show warning
const_type[AnnotationDeclaration annotation] returns [TypeCode typecode = null]
@init{
    Pair<String, Token> pair = null;
}
    :   integer_type { $typecode = $integer_type.typecode; }
    |   char_type { $typecode = $char_type.typecode; }
    |   wide_char_type { $typecode = $wide_char_type.typecode; }
    |   boolean_type { $typecode = $boolean_type.typecode; }
    |   floating_pt_type { $typecode = $floating_pt_type.typecode; }
    |   string_type { $typecode = $string_type.typecode; }
    |   wide_string_type { $typecode = $wide_string_type.typecode; }
    |   any_type { $typecode = $any_type.typecode; }
    |   fixed_pt_const_type
    |   scoped_name
        {
            pair = $scoped_name.pair;

            // Find first at annotation scope
            if ($annotation != null)
            {
                $typecode = $annotation.getTypeCode(pair.first());
            }

            // Find typecode in the global map if not found.
            if ($typecode == null)
            {
                $typecode = ctx.getTypeCode(pair.first());
            }

            if($typecode == null)
            {
                throw new ParseException(pair.second(), "was not defined previously");
            }
        }
    |   octet_type { $typecode = $octet_type.typecode; }
    ;

/*   EXPRESSIONS   */

const_exp returns [String literalStr = null]
@init{
    String aux = null;
}
    :   or_expr { $literalStr = $or_expr.literalStr; }
    ;

or_expr returns [String literalStr = null]
@init{
    String aux = null;
}
    :   xor_expr { $literalStr = $xor_expr.literalStr; }
    (
        {$literalStr += _input.LT(1).getText();}
        PIPE
        xor_expr
        { aux=$xor_expr.literalStr; $literalStr += aux;}
    )*
    ;

xor_expr returns [String literalStr = null]
@init{
    String aux = null;
}
    :   and_expr { $literalStr = $and_expr.literalStr; }
    (
        {$literalStr += _input.LT(1).getText();}
        CARET
        and_expr
        { aux=$and_expr.literalStr; $literalStr += aux;}
    )*
    ;

and_expr returns [String literalStr = null]
@init{
    String aux = null;
}
    :   shift_expr { $literalStr = $shift_expr.literalStr; }
    (
        {$literalStr += _input.LT(1).getText();}
        AMPERSAND
        shift_expr
        { aux=$shift_expr.literalStr; $literalStr += aux;}
    )*
    ;

shift_expr returns [String literalStr = null]
@init{
    String aux = null;
}
    :   add_expr { $literalStr = $add_expr.literalStr; }
    (
        {$literalStr += _input.LT(1).getText();}
        ( RIGHT_SHIFT | LEFT_SHIFT )
        add_expr
        { aux=$add_expr.literalStr; $literalStr += aux;}
    )*
    ;

add_expr returns [String literalStr = null]
@init{
    String aux = null;
}
   :   mult_expr { $literalStr = $mult_expr.literalStr; }
    (
        {$literalStr += _input.LT(1).getText();}
        ( PLUS | MINUS )
        mult_expr
        { aux=$mult_expr.literalStr; $literalStr += aux;}
    )*
    ;

mult_expr returns [String literalStr = null]
@init{
    String aux = null;
}
    :   unary_expr  { $literalStr = $unary_expr.literalStr; }
    (
        {$literalStr += _input.LT(1).getText();}
        ( '*' | SLASH | PERCENT )
        unary_expr
        { aux=$unary_expr.literalStr; $literalStr += aux;}
    )*
    ;

unary_expr returns [String literalStr = null]
@init{
    String aux = null;
}
    :
        {$literalStr = _input.LT(1).getText();}
        ( MINUS | PLUS | TILDE )
        primary_expr
        { aux=$primary_expr.literalStr; $literalStr += aux;}
    |   primary_expr { $literalStr = $primary_expr.literalStr; }
    ;

//unary_operator
//    :   ( MINUS | PLUS | TILDE )
//    ;

primary_expr  returns [String literalStr = null]
@init{
    String aux = null;
}
    :   scoped_name { $literalStr = $scoped_name.pair.first(); }
    |   literal  { $literalStr = $literal.pair.first(); }
    |   {$literalStr = _input.LT(1).getText();}
        LEFT_BRACKET
        const_exp
       { aux=$const_exp.literalStr; $literalStr += aux; $literalStr += _input.LT(1).getText();}
        RIGHT_BRACKET
    ;

literal returns [Pair<String, Token> pair = null]
@init{
    Token tk = _input.LT(1);
    String literalStr = tk.getText();
}
    :   ( HEX_LITERAL
    | INTEGER_LITERAL
    | STRING_LITERAL { literalStr = ctx.concatStringLiterals(literalStr); }
    | WIDE_STRING_LITERAL { literalStr = ctx.concatStringLiterals(literalStr); }
    | CHARACTER_LITERAL
    | WIDE_CHARACTER_LITERAL
    | FIXED_PT_LITERAL
    | FLOATING_PT_LITERAL
    | boolean_literal {literalStr = $boolean_literal.literalStr;} )
    {$pair = new Pair<String, Token>(literalStr, tk);}
    ;

boolean_literal returns [String literalStr = null]
    :   'TRUE'{ $literalStr = "true";}
    |   'FALSE'{ $literalStr = "false";}
    ;

positive_int_const returns [String literalStr = null]
    :   const_exp { $literalStr = $const_exp.literalStr; }
        {
           // TODO Calcular la expresion
           /*try {
               int value = Integer.parseInt($literalStr);

               if(value < 0)
                   throw new ParseException($literalStr, "expression is not supported. You must use a positive integer.");
           } catch(NumberFormatException e) {
           }*/
       }
    ;

type_decl [Vector<Annotation> annotations, ArrayList<Definition> defs] returns [Pair<Vector<TypeDeclaration>, TemplateGroup> returnPair = null]
@init {
    Pair<Vector<TypeCode>, TemplateGroup> ttg = null;
    Vector<TypeDeclaration> vector = null;
    Token tk = null;
    String fw_name = null;
}
    :   ( KW_TYPEDEF {tk = _input.LT(1);} type_declarator[null] { ttg=$type_declarator.returnPair; }
    |   struct_type { ttg=$struct_type.returnPair; fw_name = $struct_type.fw_name; }
    |   union_type[defs] { ttg=$union_type.returnPair; fw_name = $union_type.fw_name; }
    |   enum_type { ttg=$enum_type.returnPair; }
    |   bitset_type { ttg=$bitset_type.returnPair; }
    |   bitmask_type { ttg=$bitmask_type.returnPair; }
    |   KW_NATIVE { System.out.println("WARNING (File " + ctx.getFilename() + ", Line " + (_input.LT(1) != null ? _input.LT(1).getLine() - ctx.getCurrentIncludeLine() : "1") + "): Native declarations are not supported. Ignoring..."); } simple_declarator
    |   constr_forward_decl )
    {
        if(ttg!=null)
        {
            vector = new Vector<TypeDeclaration>();

            for(int count = 0; count < ttg.first().size(); ++count)
            {
                String name = null;
                if(ttg.first().get(count) instanceof MemberedTypeCode)
                    name = ((MemberedTypeCode)ttg.first().get(count)).getName();
                else if(ttg.first().get(count) instanceof AliasTypeCode)
                    name = ((AliasTypeCode)ttg.first().get(count)).getName();

                if (fw_name != null)
                {
                    if (ctx.getScope() != null && !ctx.getScope().isEmpty())
                    {
                        fw_name = ctx.getScope() + "::" + fw_name;
                    }
                }

                TypeDeclaration typedeclaration = (fw_name == null) ? new TypeDeclaration(ctx.getScopeFile(), ctx.isInScopedFile(), ctx.getScope(), name, ttg.first().get(count), tk) : ctx.getTypeDeclaration(fw_name);
                //System.out.println("Type ttg not null: " + name);

                // Add annotations
                for(Annotation annotation : annotations)
                {
                    if (annotation != null) // Some annotations may be ignored
                    {
                        typedeclaration.addAnnotation(ctx, annotation);
                    }
                }

                // Add type declaration to the map with all typedeclarations.
                if (fw_name == null)
                {
                    ctx.addTypeDeclaration(typedeclaration);
                }

                vector.add(typedeclaration);

                $returnPair = new Pair<Vector<TypeDeclaration>, TemplateGroup>(vector, ttg.second());
            }
        }
    }
    ;

type_declarator [AnnotationDeclaration annotation] returns [Pair<Vector<TypeCode>, TemplateGroup> returnPair = null]
@init {
    Vector<TypeCode> vector = null;
    AliasTypeCode typedefTypecode = null;
    TemplateGroup typedefTemplates =  null;
    if(tmanager != null) {
        typedefTemplates = tmanager.createTemplateGroup("typedef_decl");
    }
}
    :   type_spec[annotation] declarators
    {
       if($type_spec.typecode != null)
       {
           vector = new Vector<TypeCode>();

           for(int count = 0; count < $declarators.ret.size(); ++count)
           {
               typedefTypecode = new AliasTypeCode(ctx.getScope(), $declarators.ret.get(count).first().first());

               if($declarators.ret.get(count).second() != null)
               {
                   // Array declaration
                   $declarators.ret.get(count).second().setContentTypeCode($type_spec.typecode);
                   typedefTypecode.setContentTypeCode($declarators.ret.get(count).second());
               }
               else
               {
                   // Simple declaration
                   typedefTypecode.setContentTypeCode($type_spec.typecode);
               }

               if(typedefTemplates != null) {
                    typedefTemplates.setAttribute("typedefs", typedefTypecode);
               }

               vector.add(typedefTypecode);
           }

            if(typedefTemplates != null) {
                typedefTemplates.setAttribute("ctx", ctx);
            }

            $returnPair = new Pair<Vector<TypeCode>, TemplateGroup>(vector, typedefTemplates);
       }
    }
    ;

type_spec [AnnotationDeclaration annotation] returns [TypeCode typecode = null, Definition def = null]
    :   simple_type_spec[annotation] { $typecode=$simple_type_spec.typecode; $def=$simple_type_spec.def; }
    |   constr_type_spec
    ;

simple_type_spec [AnnotationDeclaration annotation] returns [TypeCode typecode = null, Definition def = null]
@init {
    Pair<String, Token> pair = null;
}
    :   base_type_spec { $typecode=$base_type_spec.typecode; }
    |   template_type_spec { $typecode=$template_type_spec.typecode; }
    |   scoped_name
        {
            pair=$scoped_name.pair;

            // Look for it first on annotation
            if ($annotation != null)
            {
                $typecode = $annotation.getTypeCode(pair.first());
            }

            // Find typecode in the global map.
            if($typecode == null)
            {
                $typecode = ctx.getTypeCode(pair.first());
            }

            if($typecode == null)
            {
                // Maybe an interface
                $def = ctx.getInterface(pair.first());

                if ($def == null)
                {
                    throw new ParseException(pair.second(), "was not defined previously");
                }
            }
        }
    ;

bitfield_type_spec returns [TypeCode typecode = null]
    :   integer_type { $typecode=$integer_type.typecode; }
    |   boolean_type { $typecode=$boolean_type.typecode; }
    |   octet_type { $typecode=$octet_type.typecode; }
    ;

base_type_spec returns [TypeCode typecode = null]
    :   floating_pt_type { $typecode=$floating_pt_type.typecode; }
    |   integer_type { $typecode=$integer_type.typecode; }
    |   char_type { $typecode=$char_type.typecode; }
    |   wide_char_type { $typecode=$wide_char_type.typecode; }
    |   boolean_type { $typecode=$boolean_type.typecode; }
    |   octet_type { $typecode=$octet_type.typecode; }
    |   any_type
    |   object_type { $typecode=$object_type.typecode; }
    |   value_base_type
    ;

template_type_spec returns [TypeCode typecode = null]
    :   sequence_type { $typecode=$sequence_type.typecode; }
	|   set_type { $typecode=$set_type.typecode; }
	|   map_type { $typecode=$map_type.typecode; }
    |   string_type { $typecode=$string_type.typecode; }
    |   wide_string_type { $typecode=$wide_string_type.typecode; }
    |   fixed_pt_type
    ;

constr_type_spec returns [Pair<Vector<TypeCode>, TemplateGroup> returnPair = null]
    :   struct_type
    |   union_type[null]
    |   enum_type
    |   bitset_type
    |   bitmask_type
    ;

declarators returns [Vector<Pair<Pair<String, Token>, ContainerTypeCode>> ret = new Vector<Pair<Pair<String, Token>, ContainerTypeCode>>()]
    :   declarator
        {
            if($declarator.ret != null)
                $ret.add($declarator.ret);
            else
                throw new ParseException(null, "Cannot parse type declarator");
        }
        ( COMA declarator
        {
            if($declarator.ret != null)
                $ret.add($declarator.ret);
            else
                throw new ParseException(null, "Cannot parse type declarator");
        }
        )*
    ;

simple_declarators returns [Vector<Pair<Pair<String, Token>, ContainerTypeCode>> ret = new Vector<Pair<Pair<String, Token>, ContainerTypeCode>>()]
    :   simple_declarator
        {
            if($simple_declarator.ret != null)
                $ret.add($simple_declarator.ret);
            else
                throw new ParseException(null, "Cannot parse type declarator");
        }
        ( COMA simple_declarator
        {
            if($simple_declarator.ret != null)
                $ret.add($simple_declarator.ret);
            else
                throw new ParseException(null, "Cannot parse type declarator");
        }
        )*
    ;

declarator returns [Pair<Pair<String, Token>, ContainerTypeCode> ret = null]
    :   simple_declarator { $ret=$simple_declarator.ret; }
    |   complex_declarator { $ret=$complex_declarator.ret; }
    ;

simple_declarator returns [Pair<Pair<String, Token>, ContainerTypeCode> ret = null]
@init
{
    Token tk = _input.LT(1);
}
    :  identifier
        {
            String error = ctx.checkIdentifier(Definition.Kind.TYPE_DECLARATION, ctx.getScope(), $identifier.id);
            if (error != null)
            {
                throw new ParseException(null, "Illegal identifier: " + error);
            }
            String name = ctx.removeEscapeCharacter($identifier.id);
            Pair<String, Token> p = new Pair<String, Token>(name, tk);
            $ret = new Pair<Pair<String, Token>, ContainerTypeCode>(p, null);
        }
    ;

complex_declarator returns [Pair<Pair<String, Token>, ContainerTypeCode> ret = null]
    :   array_declarator { $ret=$array_declarator.pair; }
    ;

floating_pt_type returns [TypeCode typecode = null]
    :   ( KW_FLOAT { $typecode = new PrimitiveTypeCode(Kind.KIND_FLOAT);}
    | KW_DOUBLE { $typecode = new PrimitiveTypeCode(Kind.KIND_DOUBLE);}
    | KW_LONG KW_DOUBLE { $typecode = new PrimitiveTypeCode(Kind.KIND_LONGDOUBLE);}
    )
    ;

integer_type returns [TypeCode typecode = null]
    :   signed_int { $typecode = $signed_int.typecode; }
    |   unsigned_int { $typecode = $unsigned_int.typecode; }
    ;

signed_int returns [TypeCode typecode = null]
    :   signed_short_int { $typecode = $signed_short_int.typecode; }
    |   signed_long_int { $typecode = $signed_long_int.typecode; }
    |   signed_longlong_int { $typecode = $signed_longlong_int.typecode; }
    |   signed_tiny_int { $typecode = $signed_tiny_int.typecode; }
    ;

signed_tiny_int returns [TypeCode typecode]
@init{
    $typecode = new PrimitiveTypeCode(Kind.KIND_INT8);
}
    :   KW_INT8
    ;

signed_short_int returns [TypeCode typecode]
@init{
    $typecode = new PrimitiveTypeCode(Kind.KIND_SHORT);
}
    :   KW_SHORT
    |   KW_INT16
    ;

signed_long_int returns [TypeCode typecode]
@init{
    $typecode = new PrimitiveTypeCode(Kind.KIND_LONG);
}
    :   KW_LONG
    |   KW_INT32
    ;

signed_longlong_int returns [TypeCode typecode]
@init{
    $typecode = new PrimitiveTypeCode(Kind.KIND_LONGLONG);
}
    :   KW_LONG KW_LONG
    |   KW_INT64
    ;

unsigned_int returns [TypeCode typecode = null]
    :   unsigned_short_int { $typecode = $unsigned_short_int.typecode; }
    |   unsigned_long_int { $typecode = $unsigned_long_int.typecode; }
    |   unsigned_longlong_int { $typecode = $unsigned_longlong_int.typecode; }
    |   unsigned_tiny_int { $typecode = $unsigned_tiny_int.typecode; }
    ;

unsigned_tiny_int returns [TypeCode typecode]
@init{
    $typecode = new PrimitiveTypeCode(Kind.KIND_UINT8);
}
    :   KW_UINT8
    ;

unsigned_short_int returns [TypeCode typecode]
@init{
    $typecode = new PrimitiveTypeCode(Kind.KIND_USHORT);
}
    :   KW_UNSIGNED KW_SHORT
    |   KW_UINT16
    ;

unsigned_long_int returns [TypeCode typecode]
@init{
    $typecode = new PrimitiveTypeCode(Kind.KIND_ULONG);
}
    :   KW_UNSIGNED KW_LONG
    |   KW_UINT32
    ;

unsigned_longlong_int returns [TypeCode typecode]
@init{
    $typecode = new PrimitiveTypeCode(Kind.KIND_ULONGLONG);
}
    :   KW_UNSIGNED KW_LONG KW_LONG
    |   KW_UINT64
    ;

char_type returns [TypeCode typecode]
@init{
    $typecode = new PrimitiveTypeCode(Kind.KIND_CHAR);
}
    :   KW_CHAR
    ;

wide_char_type returns [TypeCode typecode]
@init{
    $typecode = new PrimitiveTypeCode(Kind.KIND_WCHAR);
}
    :   KW_WCHAR
    ;

boolean_type returns [TypeCode typecode]
@init{
    $typecode = new PrimitiveTypeCode(Kind.KIND_BOOLEAN);
}
    :   KW_BOOLEAN
    ;

octet_type returns [TypeCode typecode]
@init{
    $typecode = new PrimitiveTypeCode(Kind.KIND_OCTET);
}
    :   KW_OCTET
    ;

any_type returns [TypeCode typecode]
@init{
    $typecode = new AnyTypeCode();
}
    :   KW_ANY
    ;

object_type returns [TypeCode typecode]
@init{
    Token tk = _input.LT(1);
}
    :   KW_OBJECT
    {
        // Find typecode in the global map.
        $typecode = ctx.getTypeCode("Object");

        if($typecode == null)
        {
            throw new ParseException(tk, ". Object type is not supported");
        }
    }
    ;

annotation_decl returns [Pair<AnnotationDeclaration, TemplateGroup> returnPair = null]
:
    annotation_def { $returnPair=$annotation_def.returnPair; }
    | annotation_forward_dcl
    ;

annotation_def returns [Pair<AnnotationDeclaration, TemplateGroup> returnPair = null]
@init
{
    TemplateGroup annotationTemplates = null;
}
    : annotation_header LEFT_BRACE annotation_body[$annotation_header.annotation] RIGHT_BRACE
    {
        if($annotation_header.annotation != null)
        {
            if(ctx.isInScopedFile() || ctx.isScopeLimitToAll())
            {
                if(tmanager != null)
                {
                    annotationTemplates = tmanager.createTemplateGroup("annotation");
                    annotationTemplates.setAttribute("ctx", ctx);
                    // Set the annotation object to the TemplateGroup of the annotation.
                    annotationTemplates.setAttribute("annotation", $annotation_header.annotation);
                }
            }

            $returnPair = new Pair<AnnotationDeclaration, TemplateGroup>($annotation_header.annotation, annotationTemplates);
        }
    }
    ;

annotation_header returns [AnnotationDeclaration annotation = null]
@init
{
    Token tk = null;
}
    : KW_AT_ANNOTATION
    {
        tk = _input.LT(1);
    }
    identifier
    {
        String error = ctx.checkIdentifier(Definition.Kind.ANNOTATION, ctx.getScope(), $identifier.id);
        if (error != null)
        {
            throw new ParseException(null, "Illegal identifier: " + error);
        }
        String name = ctx.removeEscapeCharacter($identifier.id);
        $annotation = ctx.createAnnotationDeclaration(name, tk);
    }
    ( annotation_inheritance_spec[$annotation] )?
    ;

annotation_inheritance_spec [AnnotationDeclaration annotation]
@init
{
    AnnotationDeclaration inhanno = null;
}
    :
    COLON scoped_name
    {
        if(annotation != null)
        {
            inhanno = ctx.getAnnotationDeclaration($scoped_name.pair.first());

            if(inhanno != null)
            {
                annotation.addMembers(inhanno);
            }
            else
            {
                System.out.println("WARNING (File " + ctx.getFilename() + ", Line " + (_input.LT(1) != null ? _input.LT(1).getLine() - ctx.getCurrentIncludeLine() : "1") + "): Annotation " + $scoped_name.pair.first() + " not supported. Ignoring...");
            }
        }
    }
    ;

annotation_body [AnnotationDeclaration annotation]
@init
{
    Pair<Vector<TypeCode>, TemplateGroup> pairtype = null;
    Pair<ConstDeclaration, TemplateGroup> pairconst = null;
}
:
    (
      annotation_member[annotation]
    | enum_type SEMICOLON
        {
            pairtype = $enum_type.returnPair;
            $annotation.addEnums(pairtype.first());
        }
    | const_decl[annotation] SEMICOLON
        {
            pairconst = $const_decl.returnPair;
            $annotation.addConstDecl(pairconst.first());
        }
    | KW_TYPEDEF type_declarator[annotation] SEMICOLON
        {
            pairtype = $type_declarator.returnPair;
            $annotation.addTypeDefs(pairtype.first());
        }
    )*
    ;

annotation_member [AnnotationDeclaration annotation]
@init
{
    String literalStr = null;
}
    :
    const_type[annotation] simple_declarator ( KW_DEFAULT const_exp { literalStr=$const_exp.literalStr; } )? SEMICOLON
    {
        if(!$annotation.addMember(new AnnotationMember($simple_declarator.ret.first().first(), $const_type.typecode, literalStr)))
        {
            throw new ParseException($simple_declarator.ret.first().second(), $simple_declarator.ret.first().first() + " was defined previously");
        }
    }
    ;

annotation_forward_dcl
:
    KW_AT_ANNOTATION scoped_name
    ;

bitset_type returns [Pair<Vector<TypeCode>, TemplateGroup> returnPair = null]
@init {
    String name = null;
    Vector<TypeCode> vector = null;
    BitsetTypeCode typecode = null;
    BitsetTypeCode superType = null;
    TemplateGroup bitsetTemplates = null;
} :     KW_BITSET
        identifier
        {
            String error = ctx.checkIdentifier(Definition.Kind.TYPE_DECLARATION, ctx.getScope(), $identifier.id);
            if (error != null)
            {
                throw new ParseException(null, "Illegal identifier: " + error);
            }
            name = ctx.removeEscapeCharacter($identifier.id);
            typecode = ctx.createBitsetTypeCode(name);
        }
        ( COLON scoped_name
            {
                TypeCode scopedType = ctx.getTypeCode($scoped_name.pair.first());
                if (scopedType instanceof BitsetTypeCode)
                {
                    superType = (BitsetTypeCode)scopedType;
                }
                else
                {
                    System.out.println("WARNING (File " + ctx.getFilename() + ", Line " + (_input.LT(1) != null ? _input.LT(1).getLine() - ctx.getCurrentIncludeLine() : "1") + "): Bitset only can inherit from other bitsets.");
                }
            }
        )?
        LEFT_BRACE bitfield[typecode] RIGHT_BRACE
        {
            if (superType != null) typecode.addParent(superType);

            if(ctx.isInScopedFile() || ctx.isScopeLimitToAll())
            {
                if(tmanager != null) {
                    bitsetTemplates = tmanager.createTemplateGroup("bitset_type");
                    bitsetTemplates.setAttribute("ctx", ctx);
                    bitsetTemplates.setAttribute("bitset", typecode);
                }
            }

            // Return the returned data.
            vector = new Vector<TypeCode>();
            vector.add(typecode);
            $returnPair = new Pair<Vector<TypeCode>, TemplateGroup>(vector, bitsetTemplates);
        }
    ;

bitfield [BitsetTypeCode owner]
    :   (
            (
                bitfield_spec simple_declarators SEMICOLON
                {
                    if($bitfield_spec.bitfieldType != null)
                    {
                        for(int count = 0; count < $simple_declarators.ret.size(); ++count)
                        {
                            Bitfield bitfield = null;

                            // Only simple declaration
                            bitfield = new Bitfield($owner, $bitfield_spec.bitfieldType, $simple_declarators.ret.get(count).first().first());

                            $owner.addBitfield(bitfield);

                            if(!$owner.addMember(bitfield))
                                throw new ParseException($simple_declarators.ret.get(count).first().second(), " was defined previously");
                        }
                    }
                }
            )
        |
            (
                bitfield_spec SEMICOLON
                {
                    if($bitfield_spec.bitfieldType != null)
                    {
                        Bitfield bitfield = null;

                        // Only simple declaration
                        bitfield = new Bitfield($owner, $bitfield_spec.bitfieldType, "");

                        $owner.addBitfield(bitfield);

                        if(!$owner.addMember(bitfield))
                            System.out.println("Empty space failed to be inserted.");
                    }
                }
            )
        )+
    ;

bitfield_spec returns [BitfieldSpec bitfieldType = null]
@init {
    TypeCode type = null;
    String bitsize = null;
}
    : KW_BITFIELD
        LEFT_ANG_BRACKET positive_int_const { bitsize=$positive_int_const.literalStr; } RIGHT_ANG_BRACKET
        {
            $bitfieldType = ctx.createBitfieldSpec(bitsize, null);
        }
    | KW_BITFIELD
        LEFT_ANG_BRACKET positive_int_const { bitsize=$positive_int_const.literalStr; } COMA bitfield_type_spec { type=$bitfield_type_spec.typecode; } RIGHT_ANG_BRACKET
        {
            $bitfieldType = ctx.createBitfieldSpec(bitsize, type);
        }
    ;

bitmask_type returns [Pair<Vector<TypeCode>, TemplateGroup> returnPair = null]
@init {
    String name = null;
    Vector<TypeCode> vector = null;
    BitmaskTypeCode typecode = null;
    TemplateGroup bitmaskTemplates = null;
}   :   KW_BITMASK
        identifier
        {
            String error = ctx.checkIdentifier(Definition.Kind.TYPE_DECLARATION, ctx.getScope(), $identifier.id);
            if (error != null)
            {
                throw new ParseException(null, "Illegal identifier: " + error);
            }
            name = ctx.removeEscapeCharacter($identifier.id);
            typecode = ctx.createBitmaskTypeCode(name);
        }
        LEFT_BRACE bit_values[typecode] RIGHT_BRACE
        {
            if(ctx.isInScopedFile() || ctx.isScopeLimitToAll())
            {
                if(tmanager != null) {
                    bitmaskTemplates = tmanager.createTemplateGroup("bitmask_type");
                    bitmaskTemplates.setAttribute("ctx", ctx);
                    bitmaskTemplates.setAttribute("bitmask", typecode);
                }
            }

            // Return the returned data.
            vector = new Vector<TypeCode>();
            vector.add(typecode);
            $returnPair = new Pair<Vector<TypeCode>, TemplateGroup>(vector, bitmaskTemplates);
        }
    ;

bit_values [BitmaskTypeCode owner]
@init
{
    Vector<Annotation> annots = new Vector<Annotation>();
}   :
        (annotation_appl { annots.add($annotation_appl.annotation); })* identifier
        {
            Bitmask bitmask_f = new Bitmask(owner, $identifier.id);
            for (Annotation ann : annots)
            {
                bitmask_f.addAnnotation(ctx, ann);
            }
            owner.addBitmask(bitmask_f);
            annots = new Vector<Annotation>();
        }
        (
            COMA (annotation_appl { annots.add($annotation_appl.annotation); })* identifier
            {
                Bitmask bitmask_o = new Bitmask(owner, $identifier.id);
                for (Annotation ann : annots)
                {
                    bitmask_o.addAnnotation(ctx, ann);
                }
                owner.addBitmask(bitmask_o);
                annots = new Vector<Annotation>();
            }
        )*
    ;

struct_type returns [Pair<Vector<TypeCode>, TemplateGroup> returnPair = null, String fw_name = null]
@init{
    String name = null;
    Vector<TypeCode> vector = null;
    StructTypeCode structTP = null;
    StructTypeCode parentStruct = null;
    TemplateGroup structTemplates = null;
    Boolean fw_declaration = false;
}
    :   KW_STRUCT
        identifier
        {
            String error = ctx.checkIdentifier(Definition.Kind.TYPE_DECLARATION, ctx.getScope(), $identifier.id);
            if (error != null)
            {
                throw new ParseException(null, "Illegal identifier: " + error);
            }
            name = ctx.removeEscapeCharacter($identifier.id);
            String fw_name = name;

            // Find typecode in the global map.
            if (ctx.getScope() != null && !ctx.getScope().isEmpty())
            {
                fw_name = ctx.getScope() + "::" + name;
            }

            TypeCode typecode = ctx.getTypeCode(fw_name);

            if(typecode != null)
            {
                if (typecode instanceof StructTypeCode)
                {
                    fw_declaration = true;
                    structTP = (StructTypeCode)typecode;
                    if (structTP.isDefined())
                    {
                        System.out.println("ERROR (File " + ctx.getFilename() + ", Line " + (_input.LT(1) != null ? _input.LT(1).getLine() - ctx.getCurrentIncludeLine() : "1") + "): Redefinition: " + fw_name);
                        throw new ParseException(_input.LT(1), fw_name + " redefinition");
                    }
                }
                else
                {
                    System.out.println("ERROR (File " + ctx.getFilename() + ", Line " + (_input.LT(1) != null ? _input.LT(1).getLine() - ctx.getCurrentIncludeLine() : "1") + "): Identifier already used.");
                }
            }
            else
            {
                structTP = ctx.createStructTypeCode(name);
            }
            structTP.setDefined();
        }
        (COLON scoped_name
            {
                TypeCode scopedType = ctx.getTypeCode($scoped_name.pair.first());
                if (scopedType instanceof StructTypeCode)
                {
                    parentStruct = (StructTypeCode)scopedType;
                }
                else
                {
                    System.out.println("WARNING (File " + ctx.getFilename() + ", Line " + (_input.LT(1) != null ? _input.LT(1).getLine() - ctx.getCurrentIncludeLine() : "1") + "): Structs only can inherit from other structs.");
                }
            }
        )?
        LEFT_BRACE member_list[structTP] RIGHT_BRACE
        {
            if(ctx.isInScopedFile() || ctx.isScopeLimitToAll())
            {
                if(tmanager != null) {
                    structTemplates = tmanager.createTemplateGroup("struct_type");
                    structTemplates.setAttribute("ctx", ctx);
                    structTemplates.setAttribute("struct", structTP);
                }
            }
            // Return the returned data.
            vector = new Vector<TypeCode>();
            structTP.setForwarded(fw_declaration);
            vector.add(structTP);
            if (parentStruct != null)
            {
                structTP.addInheritance(ctx, parentStruct);
            }
            $returnPair = new Pair<Vector<TypeCode>, TemplateGroup>(vector, structTemplates);
            $fw_name = (fw_declaration) ? name : null;
        }
    |
        KW_STRUCT
        identifier
        {
            // Forward declaration
            name=$identifier.id;
            structTP = ctx.createStructTypeCode(name);

            if(ctx.isInScopedFile() || ctx.isScopeLimitToAll())
            {
                if(tmanager != null) {
                    structTemplates = tmanager.createTemplateGroup("fwd_decl");
                    structTemplates.setAttribute("ctx", ctx);
                    structTemplates.setAttribute("type", structTP);
                }
            }

            // Return the returned data.
            vector = new Vector<TypeCode>();
            structTP.setForwarded(true);
            vector.add(structTP);
            $returnPair = new Pair<Vector<TypeCode>, TemplateGroup>(vector, structTemplates);
            $fw_name = null;
        }
    ;

member_list [StructTypeCode structTP]
    :   (
            member_def[structTP]
           {
               if($member_def.ret != null)
               {
                   for(Pair<Pair<String, Token>, Member> pair : $member_def.ret)
                   {
                       if(!$structTP.addMember(pair.second()))
                           throw new ParseException(pair.first().second(), pair.first().first() + " was defined previously");
                   }
               }
           }
        )*
    ;

member_def [StructTypeCode structTP] returns [Vector<Pair<Pair<String, Token>, Member>> ret = null]
    :   member[structTP] { $ret=$member.ret; }
    |   annotation_appl defret=member_def[structTP]
        {
            if($defret.ret != null)
            {
                $ret=$defret.ret;

                for(Pair<Pair<String, Token>, Member> pair : $ret)
                {
                    if(pair.second() != null)
                        pair.second().addAnnotation(ctx, $annotation_appl.annotation);
                }
            }
        }
    ;

member [StructTypeCode structTP] returns [Vector<Pair<Pair<String, Token>, Member>> ret = new Vector<Pair<Pair<String, Token>, Member>>()]
    :   type_spec[null] declarators SEMICOLON
        {
            if($type_spec.typecode!=null)
            {
                for(int count = 0; count < $declarators.ret.size(); ++count)
                {
                    Member member = null;

                    if($declarators.ret.get(count).second() != null)
                    {
                        // Array declaration
                        $declarators.ret.get(count).second().setContentTypeCode($type_spec.typecode);
                        member = new Member($declarators.ret.get(count).second(), $declarators.ret.get(count).first().first());

                    }
                    else
                    {
                        // Simple declaration
                        member = new Member($type_spec.typecode, $declarators.ret.get(count).first().first());
                    }

                    $ret.add(new Pair<Pair<String, Token>, Member>($declarators.ret.get(count).first(), member));
                }
            }
        }
    ;

union_type [ArrayList<Definition> defs] returns [Pair<Vector<TypeCode>, TemplateGroup> returnPair = null, String fw_name = null]
@init {
    String name = null;
    int line = 0;
    TypeCode dist_type = null;
    Vector<TypeCode> vector = null;
    UnionTypeCode unionTP = null;
    TemplateGroup unionTemplates = null;
    Boolean fw_decl = false;
}
    :   KW_UNION
        identifier
        {
            String error = ctx.checkIdentifier(Definition.Kind.TYPE_DECLARATION, ctx.getScope(), $identifier.id);
            if (error != null)
            {
                throw new ParseException(null, "Illegal identifier: " + error);
            }
            name = ctx.removeEscapeCharacter($identifier.id);
            String fw_name = name;

            // Find typecode in the global map.
            if (ctx.getScope() != null && !ctx.getScope().isEmpty())
            {
                fw_name = ctx.getScope() + "::" + name;
            }

            TypeCode typecode = ctx.getTypeCode(fw_name);

            if(typecode != null)
            {
                if (typecode instanceof UnionTypeCode)
                {
                    fw_decl = true;
                    unionTP = (UnionTypeCode)typecode;
                    if (unionTP.isDefined())
                    {
                        System.out.println("ERROR (File " + ctx.getFilename() + ", Line " + (_input.LT(1) != null ? _input.LT(1).getLine() - ctx.getCurrentIncludeLine() : "1") + "): Redefinition: " + fw_name);
                        throw new ParseException(_input.LT(1), fw_name + " redefinition");
                    }
                }
                else
                {
                    System.out.println("ERROR (File " + ctx.getFilename() + ", Line " + (_input.LT(1) != null ? _input.LT(1).getLine() - ctx.getCurrentIncludeLine() : "1") + "): Identifier already used.");
                }
            }
        }
        KW_SWITCH LEFT_BRACKET switch_type_spec { dist_type=$switch_type_spec.typecode; } RIGHT_BRACKET
        {
            // TODO Check supported types for discriminator: long, enumeration, etc...
            if (fw_decl)
            {
                unionTP.setDiscriminatorType(dist_type);
            }
            else
            {
                unionTP = new UnionTypeCode(ctx.getScope(), name, dist_type);
            }
            unionTP.setDefined();
            line= _input.LT(1) != null ? _input.LT(1).getLine() - ctx.getCurrentIncludeLine() : 1;
        }
        LEFT_BRACE switch_body[unionTP] RIGHT_BRACE
        {
            // Calculate default label.
            TemplateUtil.setUnionDefaultLabel(defs, unionTP, ctx.getScopeFile(), line);

            if(ctx.isInScopedFile() || ctx.isScopeLimitToAll())
            {
                if(tmanager != null) {
                    unionTemplates = tmanager.createTemplateGroup("union_type");
                    unionTemplates.setAttribute("ctx", ctx);
                    unionTemplates.setAttribute("union", unionTP);
                }
            }

            // Return the returned data.
            vector = new Vector<TypeCode>();
            unionTP.setForwarded(fw_decl);
            vector.add(unionTP);
            $returnPair = new Pair<Vector<TypeCode>, TemplateGroup>(vector, unionTemplates);
            $fw_name = (fw_decl) ? name : null;
        }
    |
        KW_UNION
        identifier
        {
            name=$identifier.id;
            // TODO Check supported types for discriminator: long, enumeration, etc...
            unionTP = new UnionTypeCode(ctx.getScope(), name);

            if(ctx.isInScopedFile() || ctx.isScopeLimitToAll())
            {
                if(tmanager != null) {
                    unionTemplates = tmanager.createTemplateGroup("fwd_decl");
                    unionTemplates.setAttribute("ctx", ctx);
                    unionTemplates.setAttribute("type", unionTP);
                }
            }

            // Return the returned data.
            vector = new Vector<TypeCode>();
            unionTP.setForwarded(true);
            vector.add(unionTP);
            $returnPair = new Pair<Vector<TypeCode>, TemplateGroup>(vector, unionTemplates);
            $fw_name = null;

        }
    ;

switch_type_spec returns [TypeCode typecode = null]
@init {
    Pair<String, Token> pair = null;
}
    :   integer_type { $typecode=$integer_type.typecode; }
    |   char_type { $typecode=$char_type.typecode; }
    |   wide_char_type { $typecode = $wide_char_type.typecode; }
    |   octet_type { $typecode=$octet_type.typecode; }
    |   boolean_type { $typecode=$boolean_type.typecode; }
    |   enum_type
    |   scoped_name
        {
           pair=$scoped_name.pair;
           // Find typecode in the global map.
           $typecode = ctx.getTypeCode(pair.first());
           if($typecode == null)
               throw new ParseException(pair.second(), "was not defined previously");
        }
    ;

switch_body [UnionTypeCode unionTP]
@init {
}
    :   case_stmt_list[unionTP]
    ;

case_stmt_list [UnionTypeCode unionTP]
@init {
}
    :  (case_stmt[unionTP])+
    ;

case_stmt [UnionTypeCode unionTP]
@init
{
    List<String> labels = new ArrayList<String>();
    boolean defaul = false;
    List<Annotation> annotations = new ArrayList<Annotation>();
}
    :    ( KW_CASE const_exp
        {
            labels.add(TemplateUtil.checkUnionLabel(unionTP.getDiscriminator(), $const_exp.literalStr, ctx.getScopeFile(), _input.LT(1) != null ? _input.LT(1).getLine() - ctx.getCurrentIncludeLine() : 1));
        } COLON
        | KW_DEFAULT { defaul = true; } COLON
        )+
        (annotation_appl { annotations.add($annotation_appl.annotation); })* element_spec[labels, defaul] SEMICOLON
        {
            if($element_spec.ret != null)
            {
                int ret = unionTP.addMember($element_spec.ret.second());

                if(ret == -1)
                {
                    throw new ParseException($element_spec.ret.first().second(), " is already defined.");
                }
                else if(ret == -2)
                {
                    throw new ParseException($element_spec.ret.first().second(), " is also a default attribute. Another was defined previously.");
                }

                for (Annotation ann : annotations)
                {
                    $element_spec.ret.second().addAnnotation(ctx, ann);
                }
            }
        }
    ;

//case_label
//    :   KW_CASE const_exp COLON
//    |   KW_DEFAULT COLON
//    ;

element_spec [List<String> labels, boolean isDefault] returns [Pair<Pair<String, Token>, UnionMember> ret = null]
    :   type_spec[null] declarator
        {
            if($type_spec.typecode != null)
            {
                UnionMember member = null;

                if($declarator.ret.second() != null)
                {
                    $declarator.ret.second().setContentTypeCode($type_spec.typecode);
                    member = new UnionMember($declarator.ret.second(), $declarator.ret.first().first(), labels, isDefault);
                }
                else
                {
                    member = new UnionMember($type_spec.typecode, $declarator.ret.first().first(), labels, isDefault);
                }

                $ret = new Pair<Pair<String, Token>, UnionMember>($declarator.ret.first(), member);
            }
        }
    ;

enum_type returns [Pair<Vector<TypeCode>, TemplateGroup> returnPair = null]
@init{
    String name = null;
    Vector<TypeCode> vector = null;
    EnumTypeCode enumTP = null;
    TemplateGroup enumTemplates = null;
}
    :   KW_ENUM
        identifier
        {
            String error = ctx.checkIdentifier(Definition.Kind.TYPE_DECLARATION, ctx.getScope(), $identifier.id);
            if (error != null)
            {
                throw new ParseException(null, "Illegal identifier: " + error);
            }
            name = ctx.removeEscapeCharacter($identifier.id);
            enumTP = new EnumTypeCode(ctx.getScope(), name);
        }
        LEFT_BRACE  enumerator_list[enumTP] RIGHT_BRACE
        {
            if(ctx.isInScopedFile() || ctx.isScopeLimitToAll())
            {
                if(tmanager != null) {
                    enumTemplates = tmanager.createTemplateGroup("enum_type");
                    enumTemplates.setAttribute("ctx", ctx);
                    enumTemplates.setAttribute("enum", enumTP);
                }
            }

            // Return the returned data.
            vector = new Vector<TypeCode>();
            vector.add(enumTP);
            $returnPair = new Pair<Vector<TypeCode>, TemplateGroup>(vector, enumTemplates);
        }
    ;

enumerator_list [EnumTypeCode enumTP]
    :    enumerator[enumTP] (COMA enumerator[enumTP])*
    ;

enumerator [EnumTypeCode enumTP]
@init{
    String name = null;
    ArrayList<Annotation> annotations = new ArrayList<Annotation>();
}
    :   (annotation_appl { annotations.add($annotation_appl.annotation); })* identifier
        {
            String error = ctx.checkIdentifier(Definition.Kind.TYPE_DECLARATION, ctx.getScope(), $identifier.id);
            if (error != null)
            {
                throw new ParseException(null, "Illegal identifier: " + error);
            }
            name = ctx.removeEscapeCharacter($identifier.id);
            EnumMember new_field = new EnumMember(name);
            for (Annotation ann : annotations)
            {
                new_field.addAnnotation(ctx, ann);
            }
            enumTP.addMember(new_field);
        }
    ;

sequence_type returns [SequenceTypeCode typecode = null]
@init {
    TypeCode type = null;
    String maxsize = null;
    Definition def = null;
}
    :   ( (KW_SEQUENCE)
        LEFT_ANG_BRACKET simple_type_spec[null] { type=$simple_type_spec.typecode; def=$simple_type_spec.def; } COMA positive_int_const { maxsize=$positive_int_const.literalStr; } RIGHT_ANG_BRACKET
    |   (KW_SEQUENCE)
        LEFT_ANG_BRACKET simple_type_spec[null] { type=$simple_type_spec.typecode; def=$simple_type_spec.def; } RIGHT_ANG_BRACKET )
        {
           if(type != null)
           {
               $typecode = new SequenceTypeCode(maxsize);
               $typecode.setContentTypeCode(type);
           }
           else if (def != null)
           {
               $typecode = new SequenceTypeCode(maxsize);
               $typecode.setContentDefinition(def);
           }
        }
    ;

set_type returns [SetTypeCode typecode = null]
@init {
    TypeCode type = null;
    String maxsize = null;
    Definition def = null;
} : ( KW_SET
		LEFT_ANG_BRACKET simple_type_spec[null] { type=$simple_type_spec.typecode; def=$simple_type_spec.def; } COMA positive_int_const { maxsize=$positive_int_const.literalStr; } RIGHT_ANG_BRACKET
    |   KW_SET
		LEFT_ANG_BRACKET simple_type_spec[null] { type=$simple_type_spec.typecode; def=$simple_type_spec.def; } RIGHT_ANG_BRACKET )
		{
	        $typecode = new SetTypeCode(maxsize);
            if (type != null)
            {
	            $typecode.setContentTypeCode(type);
            }
            else if (def != null)
            {
	            $typecode.setContentDefinition(def);
            }
	    }
    ;

map_type returns [MapTypeCode typecode = null]
@init {
    TypeCode keyType = null;
    TypeCode valueType = null;
    Definition keyDef = null;
    Definition valueDef = null;
    String maxsize = null;
}   :   KW_MAP
		LEFT_ANG_BRACKET simple_type_spec[null]
        {
            keyType=$simple_type_spec.typecode;
            keyDef=$simple_type_spec.def;
        }
        COMA simple_type_spec[null]
        {
            valueType=$simple_type_spec.typecode;
            valueDef=$simple_type_spec.def;
        }
        (COMA positive_int_const { maxsize=$positive_int_const.literalStr; } )?
        RIGHT_ANG_BRACKET
		{
	        $typecode = new MapTypeCode(maxsize);

            if (keyType != null)
            {
	            $typecode.setKeyTypeCode(keyType);
            }
            else if (keyDef != null)
            {
                $typecode.setKeyDefinition(keyDef);
            }

            if (valueType != null)
            {
	            $typecode.setValueTypeCode(valueType);
            }
            else if (valueDef != null)
            {
                $typecode.setValueDefinition(valueDef);
            }
	    }
    ;

string_type returns [TypeCode typecode = null]
@init{
    String maxsize = null;
}
    :   ( KW_STRING LEFT_ANG_BRACKET positive_int_const { maxsize=$positive_int_const.literalStr; } RIGHT_ANG_BRACKET
    |   KW_STRING )
       {$typecode = new StringTypeCode(Kind.KIND_STRING, maxsize);}
    ;

wide_string_type returns [TypeCode typecode = null]
@init
{
    String maxsize = null;
}
    :   ( KW_WSTRING LEFT_ANG_BRACKET positive_int_const { maxsize=$positive_int_const.literalStr; } RIGHT_ANG_BRACKET
    |   KW_WSTRING )
       {$typecode = new StringTypeCode(Kind.KIND_WSTRING, maxsize);}
    ;

array_declarator returns [Pair<Pair<String, Token>, ContainerTypeCode> pair = null]
@init
{
    Token tk = _input.LT(1);
    ArrayTypeCode typecode = new ArrayTypeCode();
}
    :   ID
        (
            fixed_array_size
            {
               typecode.addDimension($fixed_array_size.literalStr);
            }
        )+
        {
            Pair<String, Token> p = new Pair<String, Token>(tk.getText(), tk);
            $pair = new Pair<Pair<String, Token>, ContainerTypeCode>(p, typecode);
        }
    ;

fixed_array_size returns [String literalStr = null]
    :   LEFT_SQUARE_BRACKET
        positive_int_const { $literalStr=$positive_int_const.literalStr; }
        RIGHT_SQUARE_BRACKET
    ;

attr_decl returns [Vector<Pair<Pair<String, Token>, TypeCode>> ret = null, Vector<Pair<Pair<String, Token>, Definition>> retDef = null]
    :   readonly_attr_spec
    |   attr_spec { $ret=$attr_spec.ret; $retDef=$attr_spec.retDef; }
    ;

except_decl returns [Pair<com.eprosima.idl.parser.tree.Exception, TemplateGroup> returnPair = null]
@init {
    String name = null;
    com.eprosima.idl.parser.tree.Exception exceptionObject = null;
    TemplateGroup exTemplates = null;
    Token tk = null;
}
    :   KW_EXCEPTION
        {
            tk = _input.LT(1);
        }
        identifier
        {
            String error = ctx.checkIdentifier(Definition.Kind.TYPE_DECLARATION, ctx.getScope(), $identifier.id);
            if (error != null)
            {
                throw new ParseException(null, "Illegal identifier: " + error);
            }
            name = ctx.removeEscapeCharacter($identifier.id);
        }
        {
            // Create the Exception object.
            exceptionObject = ctx.createException(name, tk);

            if(ctx.isInScopedFile() || ctx.isScopeLimitToAll())
            {
                if(tmanager != null) {
                    exTemplates = tmanager.createTemplateGroup("exception");
                    exTemplates.setAttribute("ctx", ctx);
                    // Set the the exception object to the TemplateGroup of the module.
                    exTemplates.setAttribute("exception", exceptionObject);
                }
            }
            // Its a dependency.
            else
            {
                ctx.addIncludeDependency(ctx.getScopeFile());
            }
        }
        LEFT_BRACE opt_member_list[exceptionObject] RIGHT_BRACE
        {
            // Create the returned data.
            $returnPair = new Pair<com.eprosima.idl.parser.tree.Exception, TemplateGroup>(exceptionObject, exTemplates);
        }
    ;

opt_member_list [com.eprosima.idl.parser.tree.Exception exceptionObject]
    :  (
          member[null]
          {
              for(int count = 0; count < $member.ret.size(); ++count)
                  $exceptionObject.addMember($member.ret.get(count).second());
          }
       )*
    ;

 /*!
 * @brief This grammar expression catches a operation.
 * @return This grammar expression returns the operation object and its template group.
 */
op_decl [Vector<Annotation> annotations] returns [Pair<Operation, TemplateGroup> returnPair = null]
@init {
        Operation operationObject = null;
        TemplateGroup operationTemplates = null;
        if(tmanager != null) {
            operationTemplates = tmanager.createTemplateGroup("operation");
        }
        TemplateGroup tpl = null;
        String name = "";
        Token tk = null, tkoneway = null;
        TypeCode retType = null;
        Vector<Pair<String, Token>> exceptions = null;
}
    :   ( op_attribute { tkoneway=$op_attribute.token; } )?
        op_type_spec { retType=$op_type_spec.typecode; }
        {
            tk = _input.LT(1);
            name += tk.getText();
        }
        ID
        {
           // Create the Operation object.
           operationObject = ctx.createOperation(name, tk);

           // Add annotations.
           for(Annotation annotation : annotations)
               operationObject.addAnnotation(ctx, annotation);

           if(operationTemplates != null)
           {
               operationTemplates.setAttribute("ctx", ctx);
               // Set the the interface object to the TemplateGroup of the module.
               operationTemplates.setAttribute("operation", operationObject);
           }

           // Set return type
           operationObject.setRettype(retType);

           // Set oneway
           if(tkoneway != null)
           {
               operationObject.setOneway(true);

               if(retType != null)
               {
                   throw new ParseException(tkoneway, ". Oneway function cannot have a return type.");
               }
           }
        }
        parameter_decls[operationObject] { tpl=$parameter_decls.tpl; }
        (
           raises_expr { exceptions=$raises_expr.exlist; }
           {
              // Search global exceptions and add them to the operation.
              for(Pair<String, Token> pair : exceptions)
              {
                 com.eprosima.idl.parser.tree.Exception exception = ctx.getException(pair.first());

                 if(exception != null)
                    operationObject.addException(exception);
                 else
                    operationObject.addUnresolvedException(pair.first());
              }
           }
        )?
        ( context_expr )?
        {
            if(operationTemplates != null) {
                // Store the parameter list template group in the operation template group.
                operationTemplates.setAttribute("param_list", tpl);
            }
           // Create the returned data.
           $returnPair = new Pair<Operation, TemplateGroup>(operationObject, operationTemplates);
        }
    ;

op_attribute returns [Token token = null]
@init {
    Token tk = _input.LT(1);
}
    :   KW_ONEWAY { $token = tk;}
    ;

op_type_spec returns [TypeCode typecode = null, Definition def = null]
    :   param_type_spec { $typecode=$param_type_spec.typecode; $def=$param_type_spec.def; }
    |   KW_VOID
    ;

parameter_decls [Operation operation] returns [TemplateGroup tpl]
@init {
    if(tmanager != null) {
        $tpl = tmanager.createTemplateGroup("param_list");
    }
}
    :   LEFT_BRACKET (param_decl_list[operation, $tpl])? RIGHT_BRACKET
    ;

param_decl_list [Operation operation, TemplateGroup tpl]
@init {
        Pair<Param, TemplateGroup> ptg = null;
}
    :   param_decl { ptg=$param_decl.returnPair; }
        {
            if(ptg!=null) {
                operation.add(ptg.first());
                if(tpl != null) {
                    tpl.setAttribute("parameters", ptg.second());
                }
            }
        }
        (
        COMA param_decl { ptg=$param_decl.returnPair; }
        {
            if(ptg!=null) {
                operation.add(ptg.first());
                if(tpl != null) {
                    tpl.setAttribute("parameters", ptg.second());
                }
            }
        }
        )*
    ;

param_decl returns [Pair<Param, TemplateGroup> returnPair = null]
@init{
        TemplateGroup paramTemplate = null;
        if(tmanager != null) {
            paramTemplate = tmanager.createTemplateGroup("param");
        }
        TypeCode typecode = null;
        Definition definition = null;
        String literalStr = _input.LT(1).getText();
}
    :   ('in' | 'out' | 'inout')
        param_type_spec { typecode=$param_type_spec.typecode; definition=$param_type_spec.def; }
        simple_declarator
        {
            if(typecode != null)
            {
                Param param = null;
                if(literalStr.equals("in"))
                    param = ctx.createParam($simple_declarator.ret.first().first(), typecode, Param.Kind.IN_PARAM);
                else if(literalStr.equals("out"))
                    param = ctx.createParam($simple_declarator.ret.first().first(), typecode, Param.Kind.OUT_PARAM);
                else if(literalStr.equals("inout"))
                    param = ctx.createParam($simple_declarator.ret.first().first(), typecode, Param.Kind.INOUT_PARAM);

                if(paramTemplate != null) {
                    paramTemplate.setAttribute("parameter", param);
                }
                $returnPair = new Pair<Param, TemplateGroup>(param, paramTemplate);
            }
            else if (definition != null)
            {
                Param param = null;
                if(literalStr.equals("in"))
                    param = ctx.createParam($simple_declarator.ret.first().first(), definition, Param.Kind.IN_PARAM);
                else if(literalStr.equals("out"))
                    param = ctx.createParam($simple_declarator.ret.first().first(), definition, Param.Kind.OUT_PARAM);
                else if(literalStr.equals("inout"))
                    param = ctx.createParam($simple_declarator.ret.first().first(), definition, Param.Kind.INOUT_PARAM);

                if(paramTemplate != null) {
                    paramTemplate.setAttribute("parameter", param);
                }
                $returnPair = new Pair<Param, TemplateGroup>(param, paramTemplate);
            }
        }
    ;

//param_attribute
//    :   KW_IN
//    |   KW_OUT
//    |   KW_INOUT
//    ;

raises_expr returns [Vector<Pair<String, Token>> exlist = null]
    :   KW_RAISES LEFT_BRACKET scoped_name_list { $exlist=$scoped_name_list.retlist; } RIGHT_BRACKET
    ;

context_expr
@init {
    System.out.println("WARNING (File " + ctx.getFilename() + ", Line " + (_input.LT(1) != null ? _input.LT(1).getLine() - ctx.getCurrentIncludeLine() : "1") + "): Context declarations are not supported. Ignoring...");
}
    :   KW_CONTEXT LEFT_BRACKET STRING_LITERAL ( COMA STRING_LITERAL )* RIGHT_BRACKET
    ;

param_type_spec returns [TypeCode typecode = null, Definition def = null]
@init{
    Pair<String, Token> pair = null;
}
    :   base_type_spec { $typecode=$base_type_spec.typecode; }
    |   string_type { $typecode=$string_type.typecode; }
    |   wide_string_type { $typecode=$wide_string_type.typecode; }
    |   scoped_name
        {
            pair=$scoped_name.pair;
            // Find typecode in the global map.
            $typecode = ctx.getTypeCode(pair.first());

            if($typecode == null)
            {
                // Maybe an interface
                $def = ctx.getInterface(pair.first());

                if ($def == null)
                {
                    throw new ParseException(pair.second(), "was not defined previously");
                }
            }
        }
    ;

fixed_pt_type
@init{
    Token tk = _input.LT(1);
}
    :   KW_FIXED LEFT_ANG_BRACKET positive_int_const COMA positive_int_const RIGHT_ANG_BRACKET
    {throw new ParseException(tk, ". Fixed type is not supported");}
    ;

fixed_pt_const_type
@init{
    Token tk = _input.LT(1);
}
    :   KW_FIXED
    {throw new ParseException(tk, ". Fixed type is not supported");}
    ;

value_base_type
@init{
    Token tk = _input.LT(1);
}
    :   KW_VALUEBASE
    {throw new ParseException(tk, ". Value type is not supported");}
    ;

constr_forward_decl
    :   KW_STRUCT ID
    |   KW_UNION ID
    ;

import_decl
    :   KW_IMPORT
    {
        System.out.println("WARNING (File " + ctx.getFilename() + ", Line " + (_input.LT(1) != null ? _input.LT(1).getLine() - ctx.getCurrentIncludeLine() : "1") + "): Import declarations are not supported. Ignoring...");
    }
    imported_scope SEMICOLON
    ;

imported_scope
    :   scoped_name | STRING_LITERAL
    ;

type_id_decl
    :   KW_TYPEID
        {
            System.out.println("WARNING (File " + ctx.getFilename() + ", Line " + (_input.LT(1) != null ? _input.LT(1).getLine() - ctx.getCurrentIncludeLine() : "1") + "): TypeID declarations are not supported. Ignoring...");
        }
        scoped_name STRING_LITERAL
    ;

type_prefix_decl
    :   KW_TYPEPREFIX
        {
            System.out.println("WARNING (File " + ctx.getFilename() + ", Line " + (_input.LT(1) != null ? _input.LT(1).getLine() - ctx.getCurrentIncludeLine() : "1") + "): TypePrefix declarations are not supported. Ignoring...");
        }
        scoped_name STRING_LITERAL
    ;

readonly_attr_spec
    :   KW_READONLY KW_ATTRIBUTE param_type_spec readonly_attr_declarator
    ;

readonly_attr_declarator
    :   simple_declarator raises_expr
    |   simple_declarator ( COMA simple_declarator )*
    ;

attr_spec returns [Vector<Pair<Pair<String, Token>, TypeCode>> ret = new Vector<Pair<Pair<String, Token>, TypeCode>>(), Vector<Pair<Pair<String, Token>, Definition>> retDef = new Vector<Pair<Pair<String, Token>, Definition>>()]
@init {
    TypeCode typecode = null;
    Definition definition = null;
}
    :   KW_ATTRIBUTE param_type_spec { typecode=$param_type_spec.typecode; definition=$param_type_spec.def; } attr_declarator
        {
           if(typecode != null)
           {
               for(int count = 0; count < $attr_declarator.ret.size(); ++count)
               {
                   // attr_declarator always is a simple declarator. Not a complex (array):
                   // Simple declaration
                   $ret.add(new Pair<Pair<String, Token>, TypeCode>($attr_declarator.ret.get(count).first(), typecode));
               }
           }
           else if(definition != null)
           {
               for(int count = 0; count < $attr_declarator.ret.size(); ++count)
               {
                   // attr_declarator always is a simple declarator. Not a complex (array):
                   // Simple declaration
                   $retDef.add(new Pair<Pair<String, Token>, Definition>($attr_declarator.ret.get(count).first(), definition));
               }
           }
        }
    ;

attr_declarator returns [Vector<Pair<Pair<String, Token>, ContainerTypeCode>> ret]
@init {
    $ret = new Vector<Pair<Pair<String, Token>, ContainerTypeCode>>();
}
    :   simple_declarator {$ret.add($simple_declarator.ret);}
        ( attr_raises_expr
        | (COMA simple_declarator {$ret.add($simple_declarator.ret);})*
        )
    ;

attr_raises_expr
    :   get_excep_expr ( set_excep_expr )?
    |   set_excep_expr
    ;

get_excep_expr
    :   KW_GETRAISES exception_list
    ;

set_excep_expr
    :   KW_SETRAISES exception_list
    ;

exception_list
    :   LEFT_BRACKET scoped_name ( COMA scoped_name )* RIGHT_BRACKET
    ;

// Component Stuff

component
    :   component_decl
    |   component_forward_decl
    ;

component_forward_decl
    :   KW_COMPONENT
        {
            System.out.println("WARNING (File " + ctx.getFilename() + ", Line " + (_input.LT(1) != null ? _input.LT(1).getLine() - ctx.getCurrentIncludeLine() : "1") + "): Component declarations are not supported. Ignoring...");
        }
        ID
    ;

component_decl
    :   component_header LEFT_BRACE component_body RIGHT_BRACE
    ;

component_header
    :   KW_COMPONENT ID ( component_inheritance_spec )? ( supported_interface_spec )?
    ;

supported_interface_spec
    :   KW_SUPPORTS scoped_name ( COMA scoped_name )*
    ;

component_inheritance_spec
    :   COLON scoped_name
    ;

component_body
    :   component_export*
    ;

component_export
    :   provides_decl SEMICOLON
    |   uses_decl SEMICOLON
    |   emits_decl SEMICOLON
    |   publishes_decl SEMICOLON
    |   consumes_decl SEMICOLON
    |   attr_decl SEMICOLON
    ;

provides_decl
    :   KW_PROVIDES interface_type ID
    ;

interface_type
    :   scoped_name
    |   KW_OBJECT
    ;

uses_decl
    :   KW_USES ( KW_MULTIPLE )? interface_type ID
    ;

emits_decl
    :   KW_EMITS scoped_name ID
    ;

publishes_decl
    :   KW_PUBLISHES scoped_name ID
    ;

consumes_decl
    :   KW_CONSUMES scoped_name ID
    ;

home_decl
@init {
    System.out.println("WARNING (File " + ctx.getFilename() + ", Line " + (_input.LT(1) != null ? _input.LT(1).getLine() - ctx.getCurrentIncludeLine() : "1") + "): Home declarations are not supported. Ignoring...");
}
    :   home_header home_body
    ;

home_header
    :   KW_HOME ID ( home_inheritance_spec )? ( supported_interface_spec )? KW_MANAGES scoped_name ( primary_key_spec )?
    ;

home_inheritance_spec
    :   COLON scoped_name
    ;

primary_key_spec
    :   KW_PRIMARYKEY scoped_name
    ;

home_body
    :   LEFT_BRACE home_export* RIGHT_BRACE
    ;

home_export
    :   export[null]
    |   factory_decl SEMICOLON
    |   finder_decl SEMICOLON
    ;

factory_decl
    :   KW_FACTORY ID LEFT_BRACKET ( init_param_decls )? RIGHT_BRACKET ( raises_expr )?
    ;

finder_decl
    :   KW_FINDER ID LEFT_BRACKET ( init_param_decls )? RIGHT_BRACKET ( raises_expr )?
    ;

event
@init{
    System.out.println("WARNING (File " + ctx.getFilename() + ", Line " + (_input.LT(1) != null ? _input.LT(1).getLine() - ctx.getCurrentIncludeLine() : "1") + "): Event declarations are not supported. Ignoring...");
}
    :   ( event_decl | event_abs_decl | event_forward_decl)
    ;

event_forward_decl
    :   ( KW_ABSTRACT )? KW_EVENTTYPE ID
    ;

event_abs_decl
    :   KW_ABSTRACT KW_EVENTTYPE ID value_inheritance_spec LEFT_BRACE export[null]* RIGHT_BRACE
    ;

event_decl
    :   event_header LEFT_BRACE value_element* RIGHT_BRACE
    ;

event_header
    :   ( KW_CUSTOM )? KW_EVENTTYPE ID value_inheritance_spec
    ;

annotation_appl returns [Annotation annotation = null]
@init
{
    AnnotationDeclaration anndecl = null;
    String name;
}
    :
    AT
    (
        scoped_name { name = $scoped_name.pair.first(); }
        | KW_DEFAULT { name = "default"; }
    )
    {
        anndecl = ctx.getAnnotationDeclaration(name);
        if(anndecl == null)
        {
            System.out.println("WARNING (File " + ctx.getFilename() + ", Line " + (_input.LT(1) != null ? _input.LT(1).getLine() - ctx.getCurrentIncludeLine() : "1") + "): Annotation " + name + " not supported. Ignoring...");
        }
        else
        {
            $annotation = new Annotation(anndecl);
        }

    }
    ( LEFT_BRACKET ( annotation_appl_params[$annotation, name] )? RIGHT_BRACKET )?
    ;

// TODO Support several members in annotations.
annotation_appl_params [Annotation annotation, String tkannot]
    : const_exp
    {
        if(annotation != null && !annotation.addValue($const_exp.literalStr))
        {
            System.out.println("WARNING: " + tkannot + " doesn't has only one attribute.");
        }
    }
    | annotation_appl_param[annotation] ( COMA annotation_appl_param[annotation] )*
    ;

annotation_appl_param [Annotation annotation]
@init
{
    Token tk = _input.LT(1);
}
    : identifier EQUAL const_exp
    {
        if(annotation != null && !annotation.addValue($identifier.id, $const_exp.literalStr))
            throw new ParseException(tk, "is not an attribute of annotation " + annotation.getName());
    }
    ;

identifier returns [String id]
@init {
    $id = _input.LT(1).getText();
}
    :   ID
    ;


INTEGER_LITERAL : ('0' | '1'..'9' '0'..'9'*) INTEGER_TYPE_SUFFIX? ;

OCTAL_LITERAL : '0' ('0'..'7')+ INTEGER_TYPE_SUFFIX? ;

HEX_LITERAL : '0' ('x' | 'X') HEX_DIGIT+ INTEGER_TYPE_SUFFIX? ;

fragment
HEX_DIGIT : ( '0'..'9' | 'a'..'f' | 'A'..'F' ) ;

fragment
INTEGER_TYPE_SUFFIX : ('l' | 'L') ;

FLOATING_PT_LITERAL
    :   ('0'..'9')+ '.' ('0'..'9')* EXPONENT? FLOAT_TYPE_SUFFIX?
    |   '.' ('0'..'9')+ EXPONENT? FLOAT_TYPE_SUFFIX?
    |   ('0'..'9')+ EXPONENT FLOAT_TYPE_SUFFIX?
    |   ('0'..'9')+ EXPONENT? FLOAT_TYPE_SUFFIX
    ;

FIXED_PT_LITERAL
    :   FLOATING_PT_LITERAL
    ;

fragment
EXPONENT : ('e' | 'E') (PLUS|MINUS)? ('0'..'9')+ ;

fragment
FLOAT_TYPE_SUFFIX : ('f' | 'F' | 'd' | 'D') ;

WIDE_CHARACTER_LITERAL
    :   'L' CHARACTER_LITERAL
    ;

CHARACTER_LITERAL
    :   '\'' ( ESCAPE_SEQUENCE | ~('\'' | '\\') ) '\''
    ;

WIDE_STRING_LITERAL
    :   ('L' STRING_LITERAL(WS*))+
    ;

STRING_LITERAL
    :   ('"' ( ESCAPE_SEQUENCE | ~('\\' | '"') )* '"'(WS*))+
    ;

BOOLEAN_LITERAL
    :   'TRUE'
    |   'FALSE'
    ;

fragment
ESCAPE_SEQUENCE
    :   '\\' ('b' | 't' | 'n' | 'f' | 'r' | '\"' | '\'' | '\\' | 'v' | 'a' | '?')
    |   UNICODE_ESCAPE
    |   HEX_ESCAPE
    |   OCTAL_ESCAPE
    ;

fragment
OCTAL_ESCAPE
    :   '\\' ('0'..'3') ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7')
    ;

fragment
UNICODE_ESCAPE
    :   '\\' 'u' HEX_DIGIT? HEX_DIGIT? HEX_DIGIT? HEX_DIGIT
    ;

fragment
HEX_ESCAPE
    :   '\\' 'x' HEX_DIGIT? HEX_DIGIT? HEX_DIGIT
    ;

fragment
LETTER
    :   '\u0024'
    |   '\u0041'..'\u005a'
    |   '\u005f'
    |   '\u0061'..'\u007a'
    |   '\u00c0'..'\u00d6'
    |   '\u00d8'..'\u00f6'
    |   '\u00f8'..'\u00ff'
    |   '\u0100'..'\u1fff'
    |   '\u3040'..'\u318f'
    |   '\u3300'..'\u337f'
    |   '\u3400'..'\u3d2d'
    |   '\u4e00'..'\u9fff'
    |   '\uf900'..'\ufaff'
    ;

fragment
ID_DIGIT
    :   '\u0030'..'\u0039'
    |   '\u0660'..'\u0669'
    |   '\u06f0'..'\u06f9'
    |   '\u0966'..'\u096f'
    |   '\u09e6'..'\u09ef'
    |   '\u0a66'..'\u0a6f'
    |   '\u0ae6'..'\u0aef'
    |   '\u0b66'..'\u0b6f'
    |   '\u0be7'..'\u0bef'
    |   '\u0c66'..'\u0c6f'
    |   '\u0ce6'..'\u0cef'
    |   '\u0d66'..'\u0d6f'
    |   '\u0e50'..'\u0e59'
    |   '\u0ed0'..'\u0ed9'
    |   '\u1040'..'\u1049'
    ;

SEMICOLON:              ';';
COLON:                  ':';
COMA:                   ',';
LEFT_BRACE:             '{';
RIGHT_BRACE:            '}';
LEFT_BRACKET:           '(';
RIGHT_BRACKET:          ')';
LEFT_SQUARE_BRACKET:    '[';
RIGHT_SQUARE_BRACKET:   ']';
TILDE:                  '~';
SLASH:                  '/';
LEFT_ANG_BRACKET:       '<';
RIGHT_ANG_BRACKET:      '>';
STAR:                   '*';
PLUS:                   '+';
MINUS:                  '-';
CARET:                  '^';
AMPERSAND:              '&';
PIPE:                   '|';
EQUAL:                  '=';
PERCENT:                '%';
AT:                        '@';

DOUBLE_COLON:           '::';
RIGHT_SHIFT:            '>>';
LEFT_SHIFT:             '<<';

KW_SETRAISES:           'setraises';
KW_OUT:                 'out';
KW_EMITS:               'emits';
KW_STRING:              'string';
KW_SWITCH:              'switch';
KW_PUBLISHES:           'publishes';
KW_TYPEDEF:             'typedef';
KW_USES:                'uses';
KW_PRIMARYKEY:          'primarykey';
KW_CUSTOM:              'custom';
KW_OCTET:               'octet';
KW_SEQUENCE:            'sequence';
KW_IMPORT:              'import';
KW_STRUCT:              'struct';
KW_NATIVE:              'native';
KW_READONLY:            'readonly';
KW_FINDER:              'finder';
KW_RAISES:              'raises';
KW_VOID:                'void';
KW_PRIVATE:             'private';
KW_EVENTTYPE:           'eventtype';
KW_WCHAR:               'wchar';
KW_IN:                  'in';
KW_DEFAULT:             'default';
KW_PUBLIC:              'public';
KW_SHORT:               'short';
KW_LONG:                'long';
KW_ENUM:                'enum';
KW_WSTRING:             'wstring';
KW_CONTEXT:             'context';
KW_HOME:                'home';
KW_FACTORY:             'factory';
KW_EXCEPTION:           'exception';
KW_GETRAISES:           'getraises';
KW_CONST:               'const';
KW_VALUEBASE:           'ValueBase';
KW_VALUETYPE:           'valuetype';
KW_SUPPORTS:            'supports';
KW_MODULE:              'module';
KW_OBJECT:              'Object';
KW_TRUNCATABLE:         'truncatable';
KW_UNSIGNED:            'unsigned';
KW_FIXED:               'fixed';
KW_UNION:               'union';
KW_ONEWAY:              'oneway';
KW_ANY:                 'any';
KW_CHAR:                'char';
KW_CASE:                'case';
KW_FLOAT:               'float';
KW_BOOLEAN:             'boolean';
KW_MULTIPLE:            'multiple';
KW_ABSTRACT:            'abstract';
KW_INOUT:               'inout';
KW_PROVIDES:            'provides';
KW_CONSUMES:            'consumes';
KW_DOUBLE:              'double';
KW_TYPEPREFIX:          'typeprefix';
KW_TYPEID:              'typeid';
KW_ATTRIBUTE:           'attribute';
KW_LOCAL:               'local';
KW_MANAGES:             'manages';
KW_INTERFACE:           'interface';
KW_COMPONENT:           'component';
KW_SET:                 'set';
KW_MAP:                 'map';
KW_BITFIELD:            'bitfield';
KW_BITSET:              'bitset';
KW_BITMASK:             'bitmask';
KW_INT8:                'int8';
KW_UINT8:               'uint8';
KW_INT16:               'int16';
KW_UINT16:              'uint16';
KW_INT32:               'int32';
KW_UINT32:              'uint32';
KW_INT64:               'int64';
KW_UINT64:              'uint64';
KW_AT_ANNOTATION:       '@annotation';

ID
    :   LETTER (LETTER|ID_DIGIT)*
    ;

WS
    :   (' ' | '\r' | '\t' | '\u000C' | '\n') -> channel(HIDDEN)
    ;

PREPROC_DIRECTIVE
    :
    '#'
    (~'\n')* '\n'
    {
        ctx.processPreprocessorLine(new String(getText()), getLine());
        skip();
        //newline();
    }
    ;

COMMENT
    :   '/*' .*? '*/' -> channel(HIDDEN)
    ;

LINE_COMMENT
    :   '//' ~('\n' | '\r')* '\r'? '\n' -> channel(HIDDEN)
    ;

// [EOF] IDL.g
