/* *************************************************************************
                          gdlc.g  -  gdl lexer/parser
                             -------------------
    begin                : July 22 2002
    copyright            : (C) 2002 by Marc Schellens
    email                : m_schellens@users.sf.net
 ***************************************************************************/

/* *************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

header "pre_include_cpp" {
#include "includefirst.hpp"
}
header "post_include_cpp" {
#include <errno.h>

#include <cstdlib>
}

header {
#include <fstream>

#include "GDLParser.hpp"

#include "str.hpp"
#include "dnodefactory.hpp"
#include "objects.hpp"
#include "initsysvar.hpp"

#include <antlr/TokenStreamSelector.hpp>

#include <antlr/SemanticException.hpp>
#include <antlr/NoViableAltForCharException.hpp>
#include <antlr/TokenStreamIOException.hpp>
#include <antlr/CharInputBuffer.hpp>

// GD: set to 1 to traceout what the Parser does.
#define debugParser 0
//#include "dinterpreter.hpp"

// defintion in dinterpreter.cpp
//void SetActualCompileOpt( unsigned int cOpt);
}

options {
    language="Cpp";
    genHashLines = false;
    namespaceStd="std";         // cosmetic option to get rid of long defines
    namespaceAntlr="antlr";     // cosmetic option to get rid of long defines
}    

// the GDL Parser *********************************************
class GDLParser extends Parser;

options {
    exportVocab = GDL;    // use vocab generated by lexer
    buildAST = true;
      ASTLabelType = "RefDNode";
    k=2;
    defaultErrorHandler = false;
//    defaultErrorHandler = true;
}

// if something is changed here
// identifier below probably needs to change as well
// also, imperative to change values of _TokenSet_xxx farther on in the file.
tokens {
    ALL;        // arrayindex (*, e.g. [1:*])
    ASSIGN;
    ASSIGN_INPLACE;
    ASSIGN_REPLACE;
    ASSIGN_ARRAYEXPR_MFCALL;
    ARRAYDEF;
    ARRAYDEF_CONST;
    ARRAYDEF_GENERALIZED_INDGEN;
    ARRAYIX;
    ARRAYIX_ALL;
    ARRAYIX_ORANGE;
    ARRAYIX_RANGE;
    ARRAYIX_ORANGE_S; // with stride
    ARRAYIX_RANGE_S;
    ARRAYEXPR;
    ARRAYEXPR_FCALL;
    ARRAYEXPR_MFCALL;
    BLOCK;
    BREAK;
    CSBLOCK;
    CONTINUE;
    COMMONDECL;
    COMMONDEF;
    CONSTANT;
    DEREF;
    ELSEBLK;
    EXPR;
    FOR;
    FOR_STEP; // for with step
    FOREACH;
    FOREACH_INDEX; // foreach with index (hash) variable
    FOR_LOOP;
    FOR_STEP_LOOP; // for with step
    FOREACH_LOOP;
    FOREACH_INDEX_LOOP;
    FCALL;
    FCALL_LIB; // library function call
     FCALL_LIB_DIRECT; // direct call
     FCALL_LIB_N_ELEMENTS; // N_ELEMENTS
    FCALL_LIB_RETNEW; // library function call always return newly allocated data
    GDLNULL;
    IF_ELSE;
    KEYDECL;
    KEYDEF;
    KEYDEF_REF; // keyword passed by reference
    KEYDEF_REF_CHECK; // keyword maybe passed by reference
    KEYDEF_REF_EXPR;  // keyword with assign/inc/dec passed by reference
      LABEL;
    MPCALL;
    MPCALL_PARENT; // explicit call to parent 
    MFCALL;
    MFCALL_LIB;
    MFCALL_LIB_RETNEW;
    MFCALL_PARENT; // explicit call to parent
    MFCALL_PARENT_LIB;
    MFCALL_PARENT_LIB_RETNEW;
      NOP;     // no operation
    NSTRUC;     // named struct
    NSTRUC_REF; // named struct reference
    ON_IOERROR_NULL;
    PCALL;
    PCALL_LIB; // libraray procedure call
    PARADECL;
    PARAEXPR;  // parameter
    PARAEXPR_VN; // _VN Variable Number of parameters version
    DEC_REF_CHECK; // called from EvalRefCheck() (no temporary needed then)
    INC_REF_CHECK; // called from EvalRefCheck() (no temporary needed then)
    POSTDEC;  //post-decrement : i--
    POSTINC; // Post-increment : i++
    DECSTATEMENT; // as a statement
    INCSTATEMENT; // as a statement
    REF;        // expr pass by reference
    REF_VN;        // expr pass by reference
    REF_CHECK;  // expr maybe be passed by reference
    REF_CHECK_VN;  // expr maybe be passed by reference
    REF_EXPR;   // assign/dec/inc expr passed by reference
    REF_EXPR_VN;   // assign/dec/inc expr passed by reference
    REPEAT;
    REPEAT_LOOP;
    RETURN;  // unspecified return (replaced by tree parser with RETF/RETP)
      RETF;    // return from function (return argument)
      RETP;    // return from procedure (no return argument)
    STRUC;  // struct
    SYSVAR;
//    UPLUS;
    UMINUS;
    VAR;     // variable, referenced through index
    VARPTR;  // variable, referenced through pointer
    WHILE;
}

{
    public:
    enum CompileOpt {
        NONE=0,
        DEFINT32=1,
        HIDDEN=2,
        OBSOLETE=4,
        STRICTARR=8,
        LOGICAL_PREDICATE=16, // *** functionality not implemeted yet
        IDL2=DEFINT32 | STRICTARR,
        STRICTARRSUBS=32,
        STATIC=64,
        NOSAVE=128,
        GDL_HIDDEN=256 //flag to avoid writing "Compiled module" at compilation. 
                       //Not the same as HIDDEN, that can be set in the procdure code and hides also the procedure from the HELP. 
    };

    void SetCompileOpt( unsigned int cOpt)
    {
        this->compileOpt = cOpt;
    }
    
    private:
    void AddCompileOpt( const std::string &opt)
    {
        if(      opt == "DEFINT32")          compileOpt |= DEFINT32;
        else if( opt == "HIDDEN")            compileOpt |= HIDDEN;
        else if( opt == "OBSOLETE")          compileOpt |= OBSOLETE;
        else if( opt == "STRICTARR")         compileOpt |= STRICTARR;
        else if( opt == "LOGICAL_PREDICATE") compileOpt |= LOGICAL_PREDICATE;
        else if( opt == "IDL2")              compileOpt |= IDL2;
        else if( opt == "STRICTARRSUBS")     compileOpt |= STRICTARRSUBS;
        else if( opt == "STATIC")            compileOpt |= STATIC;
        else if( opt == "NOSAVE")            compileOpt |= NOSAVE;
        else throw GDLException("Unrecognised COMPILE_OPT option: "+opt);
//        SetActualCompileOpt( compileOpt);
    }

    std::string subName; // name of procedure function to be compiled ("" -> all file)
    bool   searchForPro; // true -> procedure subName, false -> function subName 
    bool   subReached; 
    unsigned int compileOpt;

    bool ConstantExprNode( int t)
    {
        return (t == CONSTANT) || 
               (t == ARRAYDEF_CONST);
    }

    public:
    GDLParser(antlr::TokenStream& selector, 
              const std::string& sName, 
              bool searchPro, // true -> search for procedure sName, false -> for function
              unsigned int compileOptIn):
    antlr::LLkParser(selector,2), subName(sName), searchForPro( searchPro), 
    subReached(false), compileOpt(compileOptIn)
    { 
        //        setTokenNames(_tokenNames);
    }
}

// 'reverse' identifier
// allows reserved words as identifiers
// needed for keyword abbreviations
// if you change some keywords here you probably need to change
// the reserved word list above
identifier
    : IDENTIFIER
    | a:AND_OP { #a->setType( IDENTIFIER);}
    | b:BEGIN  { #b->setType( IDENTIFIER);}
    | c:CASE { #c->setType( IDENTIFIER);}
    | co:COMMON { #co->setType( IDENTIFIER);}
    | com:COMPILE_OPT { #com->setType( IDENTIFIER);}
    | d:DO { #d->setType( IDENTIFIER);}
    | e:ELSE { #e->setType( IDENTIFIER);}
    | en:END { #en->setType( IDENTIFIER);}
    | end:ENDCASE { #end->setType( IDENTIFIER);}
    | ende:ENDELSE { #ende->setType( IDENTIFIER);}
    | endf:ENDFOR { #endf->setType( IDENTIFIER);}
    | endfe:ENDFOREACH { #endf->setType( IDENTIFIER);}
    | endi:ENDIF { #endi->setType( IDENTIFIER);}
    | endr:ENDREP { #endr->setType( IDENTIFIER);}
    | ends:ENDSWITCH { #ends->setType( IDENTIFIER);}
    | endw:ENDWHILE { #endw->setType( IDENTIFIER);}
    | eq:EQ_OP { #eq->setType( IDENTIFIER);}
    | f:FOR { #f->setType( IDENTIFIER);}
    | fe:FOREACH { #f->setType( IDENTIFIER);}
    | fo:FORWARD { #fo->setType( IDENTIFIER);}
    | fu:FUNCTION { #fu->setType( IDENTIFIER);}
    | g:GE_OP { #g->setType( IDENTIFIER);}
    | go:GOTO { #go->setType( IDENTIFIER);}
    | gt:GT_OP { #gt->setType( IDENTIFIER);}
    | i:IF { #i->setType( IDENTIFIER);}
    | in:INHERITS { #in->setType( IDENTIFIER);}
    | l:LE_OP { #l->setType( IDENTIFIER);}
    | lt:LT_OP { #lt->setType( IDENTIFIER);}
    | m:MOD_OP { #m->setType( IDENTIFIER);}
    | n:NE_OP { #n->setType( IDENTIFIER);}
    | no:NOT_OP { #no->setType( IDENTIFIER);}
    | o:OF { #o->setType( IDENTIFIER);}
    | on:ON_IOERROR { #on->setType( IDENTIFIER);}
    | o_:OR_OP { #o_->setType( IDENTIFIER);}
    | p:PRO { #p->setType( IDENTIFIER);}
    | r:REPEAT { #r->setType( IDENTIFIER);}
    | s:SWITCH { #s->setType( IDENTIFIER);}
    | t:THEN { #t->setType( IDENTIFIER);}
    | u:UNTIL { #u->setType( IDENTIFIER);}
    | w:WHILE { #w->setType( IDENTIFIER);}
    | x:XOR_OP { #x->setType( IDENTIFIER);}
    ;


// file parsing
translation_unit
{ 
    subReached=false;
    compileOpt=NONE; // reset compileOpt    
    if (debugParser) std::cout << " translation_unit" << std::endl;
}
    :   ( options {greedy=true;}: end_unit
        | forward_function end_unit
        | procedure_def 
            { 
                compileOpt=NONE; // reset compileOpt    
                if( subReached) goto bailOut;
            }
        | function_def  
            { 
                compileOpt=NONE; // reset compileOpt    
                if( subReached) goto bailOut;
            }
        | common_block
        )* // optional - only main program is also ok

        ( statement_list END! (end_unit)? )? // $MAIN$ program

        (EOF!)   // braces necessary because goto crosses initialization otherwise
        { bailOut:;} // bailout jump label
        // catch lexer exceptions also
        exception 
        catch [ GDLException& e] 
        { 
            throw;
        }
        catch [ antlr::NoViableAltException& e] 
        {
            // PARSER SYNTAX ERROR
            throw GDLException( e.getLine(), e.getColumn(), "Parser syntax error: "+e.getMessage());
        }
        catch [ antlr::NoViableAltForCharException& e] 
        {
            // LEXER SYNTAX ERROR
            throw GDLException( e.getLine(), e.getColumn(), "Lexer syntax error: "+e.getMessage());
        }
        catch [ antlr::RecognitionException& e] 
        {
            // SYNTAX ERROR
            throw GDLException( e.getLine(), e.getColumn(), "Lexer/Parser syntax error: "+e.getMessage());
        }
        catch [ antlr::TokenStreamIOException& e] 
        {
            // IO ERROR
            throw GDLException( returnAST, "Input/Output error: "+e.getMessage());
        }
        catch [ antlr::TokenStreamException& e] 
        {
            throw GDLException( returnAST, "Token stream error: "+e.getMessage());
        }
    ;

// to give a more precise error message
// interactive compilation is not allowed
interactive_compile!
{    if (debugParser) std::cout << " interactive_compile! " << std::endl; }
    : (FUNCTION | PRO)
        IDENTIFIER 
        {
            throw GDLException( "Programs can't be compiled from "
                "single statement mode.");
        }
        (METHOD IDENTIFIER)?
        (COMMA parameter_declaration)? 
        end_unit
    ;

// interactive usage
interactive
{    if (debugParser) std::cout << " interactive " << std::endl; }

    :   ( end_unit (end_mark)? 
        | interactive_statement
        | interactive_compile
        )+
        // catch lexer exceptions also
        exception 
        catch [ GDLException& e] 
        { 
            throw;
        }
        catch [ antlr::NoViableAltException& e] 
        {
            // PARSER SYNTAX ERROR
            throw GDLException( e.getLine(), e.getColumn(), "Parser syntax error: "+
                e.getMessage());
        }
        catch [ antlr::NoViableAltForCharException& e] 
        {
            // LEXER SYNTAX ERROR
            throw GDLException( e.getLine(), e.getColumn(), "Lexer syntax error: "+
                e.getMessage());
        }
        catch [ antlr::RecognitionException& e] 
        {
            // SYNTAX ERROR
            throw GDLException( e.getLine(), e.getColumn(), 
                "Lexer/Parser syntax error: "+e.getMessage());
        }
        catch [ antlr::TokenStreamIOException& e] 
        {
            // IO ERROR
            throw GDLException( returnAST, "Input/Output error: "+e.getMessage());
        }
        catch [ antlr::TokenStreamException& e] 
        {
            throw GDLException( returnAST, "Token stream error: "+e.getMessage());
        }
    ;

// compound statements in the original don't care about the specific end_mark
// in interactive mode end need not to be there and labels are ignored
interactive_statement
{    if (debugParser) std::cout << " interactive_statement " << std::endl; }
  :  (BEGIN! | IDENTIFIER! COLON!)* 
    statement end_unit
  ;


// idl allows more than one ELSE: first is executed, *all*
// (including expr) later branches are ignored (case) or 
// executed (switch)
switch_statement
{
    int numBranch=0;
    if (debugParser) std::cout  << " switch_statement " << std::endl; 
}
    : SWITCH^ expr OF! (end_unit)? 
        (switch_body
            {
                numBranch++;
            }
        )*
        endswitch_mark
        {
        #SWITCH->SetNumBranch(numBranch);
        }
    ;

switch_body
{    if (debugParser) std::cout  << " switch_body " << std::endl; }
    : expr COLON! 
        ( statement
        | BEGIN! statement_list endswitch_mark)? end_unit
        { #switch_body = #([BLOCK, "block"], #switch_body); if (debugParser) std::cout<<std::endl;}
    | ELSE! COLON! 
        ( statement
        | BEGIN! statement_list endswitchelse_mark)? end_unit
        { #switch_body = #([ELSEBLK, "elseblk"], #switch_body); if (debugParser) std::cout<<std::endl;}
    ;    

case_statement
{
    int numBranch=0;
    if (debugParser) std::cout  << " case_statement " << std::endl; 
}
    : CASE^ expr OF! (end_unit)? 
        (case_body
            {
                numBranch++;
            }
        )*
        endcase_mark
        {
        #CASE->SetNumBranch(numBranch); if (debugParser) std::cout<<std::endl;
        }
    ;

case_body
{    if (debugParser) std::cout  << " case_body " << std::endl; }
    : expr COLON! 
        (statement
        | BEGIN! statement_list endcase_mark)? end_unit
        { #case_body = #([BLOCK, "block"], #case_body); if (debugParser) std::cout<<std::endl;}
    | ELSE! COLON! 
        (statement
        | BEGIN! statement_list endcaseelse_mark)? end_unit
        { #case_body = #([ELSEBLK, "elseblk"], #case_body); if (debugParser) std::cout<<std::endl;}
    ;

// whereever one END_U is there might be more
// end_unit is syntactical necessary, but not for the AST
end_unit!
{    if (debugParser) std::cout  << " end_unit!" << std::endl; }
    : (options {greedy=true;}: END_U)+
    ;


forward_function
{    if (debugParser) std::cout  << " forward_function -> " /* << std::endl */; }
  : FORWARD^ identifier_list
  ;


parameter_declaration
{    if (debugParser) std::cout  << " parameter_declaration -> " /* << std::endl */; }
    : (IDENTIFIER | keyword_declaration) 
        (COMMA! (IDENTIFIER | keyword_declaration))*
        { #parameter_declaration = 
            #([PARADECL,"paradecl"], #parameter_declaration); if (debugParser) std::cout<<std::endl;}
    ;
    
    
keyword_declaration
{    if (debugParser) std::cout  << " keyword_declaration -> " /* << std::endl */; }
    : IDENTIFIER EQUAL! IDENTIFIER
        { #keyword_declaration =
            #([KEYDECL,"keydecl"], #keyword_declaration); if (debugParser) std::cout<<std::endl;}
    ;

protected
object_name! returns [std::string name] // !//
{    if (debugParser) std::cout  << " object_name! -> " /* << std::endl */; }
      : i1:IDENTIFIER m:METHOD i2:IDENTIFIER
        { 
        // here we translate IDL_OBECT to GDL_OBJECT for source code compatibility
        {
            if( #i1->getText() == "IDL_OBJECT")
                #i1->setText(GDL_OBJECT_NAME);
            else if( #i1->getText() == "IDL_CONTAINER")
                #i1->setText(GDL_CONTAINER_NAME);
        }

            #object_name = #(NULL, i2, m, i1); // NULL -> no root
            name= std::string( i1->getText()+"__"+i2->getText()); if (debugParser) std::cout<<std::endl;
        }
      ;    

procedure_def
{
    std::string name;
    if (debugParser) std::cout  << " procedure_def -> " /* << std::endl */; 
}
    : p:PRO^
        ( n:IDENTIFIER { name=n->getText(); }
        | name=object_name  
        )
        (COMMA! parameter_declaration)? end_unit
        (statement_list)? END!
        { 
            if( subName == name && searchForPro == true) subReached=true;
            #p->SetCompileOpt( compileOpt); if (debugParser) std::cout<<std::endl;
        }
  ;

function_def
{
    std::string name;
    if (debugParser) std::cout  << " function_def -> " /* << std::endl */; 
}
    : f:FUNCTION^
        ( n:IDENTIFIER { name=n->getText(); }
        | name=object_name
        )
        (COMMA! parameter_declaration)? end_unit
        (statement_list)? END!
        { 
            if( subName == name && searchForPro == false) subReached=true;
            #f->SetCompileOpt( compileOpt); if (debugParser) std::cout<<std::endl;
        }
    ;

// change defaultbehaviour of the compiling
compile_opt!
{    if (debugParser) std::cout  << " compile_opt! -> " /* << std::endl */; }
    : COMPILE_OPT i:IDENTIFIER 
        {
            AddCompileOpt( i->getText());
        }
        (COMMA ii:IDENTIFIER
            {
                AddCompileOpt( ii->getText());
            }
        )*
    ;

common_block
{    if (debugParser) std::cout  << " common_block -> " /* << std::endl */; }
    : COMMON! IDENTIFIER 
        (
            { #common_block = #([COMMONDECL,"commondecl"], #common_block); if (debugParser) std::cout<<std::endl;}
        | COMMA! identifier_list
            { #common_block = #([COMMONDEF,"commondef"], #common_block); if (debugParser) std::cout<<std::endl;}
        )
    ;

identifier_list
{    if (debugParser) std::cout  << " identifier_list -> " /* << std::endl */; }
    : IDENTIFIER (COMMA! IDENTIFIER)*
    ;

// no ASTs for end marks
end_mark!
{    if (debugParser) std::cout  << " end_mark! -> " /* << std::endl */; }
    : END
    | ENDIF
    | ENDELSE
    | ENDCASE
    | ENDSWITCH
    | ENDFOR
    | ENDFOREACH
    | ENDWHILE
    | ENDREP
    ;

endforeach_mark!
{    if (debugParser) std::cout  << " endforeach_mark! " << std::endl; }
    : ENDFOREACH | END
    ;

endfor_mark!
{    if (debugParser) std::cout  << " endfor_mark! " << std::endl; }
    : ENDFOR | END
    ;

endrep_mark!
{    if (debugParser) std::cout  << " endrep_mark! " << std::endl; }
    : ENDREP | END
    ;

endwhile_mark!
{    if (debugParser) std::cout  << " endwhile_mark! " << std::endl; }
    : ENDWHILE | END
    ;

endif_mark!
{    if (debugParser) std::cout  << " endif_mark! " << std::endl; }
    : ENDIF    | END
    ;

endelse_mark!
{    if (debugParser) std::cout  << " endelse_mark! " << std::endl; }
    : ENDELSE | END
    ;

endcase_mark!
{    if (debugParser) std::cout  << " endcase_mark! " << std::endl; }
    : ENDCASE | END
    ;

endcaseelse_mark!
{    if (debugParser) std::cout  << " endcaseelse_mark! " << std::endl; }
    : endcase_mark | ENDELSE
    ;

endswitch_mark!
{    if (debugParser) std::cout  << " endswitch_mark! " << std::endl; }
    : ENDSWITCH | END
    ;

endswitchelse_mark!
{    if (debugParser) std::cout  << " endswitchelse_mark! " << std::endl; }
    : endswitch_mark | ENDELSE
    ;

statement_list
{    if (debugParser) std::cout  << " statement_list -> " /* << std::endl */; }
    : (end_unit 
        | compound_statement end_unit 
        | label_statement end_unit)+
    ;

label
{    if (debugParser) std::cout  << " label -> " /* << std::endl */; }
      : IDENTIFIER^ COLON
      ;

label_statement
{    if (debugParser) std::cout  << " label_statement -> " /* << std::endl */; }
    : (label)+ (compound_statement)?
    ;

// compound statements don't care about the specific end_mark
compound_statement
{    if (debugParser) std::cout  << " compound_statement -> " /* << std::endl */; }
    : statement
    | BEGIN! statement_list end_mark
        { #compound_statement = #([BLOCK, "block"], #compound_statement); if (debugParser) std::cout<<std::endl;}
    ;

baseclass_method
{    if (debugParser) std::cout  << " baseclass_method -> " /* << std::endl */; }
    : s:IDENTIFIER METHOD!
        // here we translate IDL_OBECT to GDL_OBJECT for source code compatibility
        {
            if( #s->getText() == "IDL_OBJECT")
                #s->setText(GDL_OBJECT_NAME);
            else if( #s->getText() == "IDL_CONTAINER")
                #s->setText(GDL_CONTAINER_NAME);
        }
    ;

statement
// assignment and member_procedure_call starting with deref_expr
{
    bool parent=false;
    if (debugParser) std::cout << " statement -> " /* << std::endl */; 
}
    : (assign_expr)=> assign_expr (DEC^ | INC^)?
    | (deref_dot_expr_keeplast IDENTIFIER COMMA)=>
        d1:deref_dot_expr_keeplast formal_procedure_call
                { 
                        #statement = #([MPCALL, "mpcall"], #statement);
                        #statement->SetLine( #d1->getLine()); if (debugParser) std::cout<<" statement : \""<<LT(0)->getText()<<"\""<<std::endl;

                }
    | (deref_dot_expr_keeplast baseclass_method)=>
        d2:deref_dot_expr_keeplast baseclass_method formal_procedure_call
                { 
                        #statement = #([MPCALL_PARENT, "mpcall::"], 
                                        #statement);
                        #statement->SetLine( #d2->getLine()); if (debugParser) std::cout<<" statement : \""<<LT(0)->getText()<<"\""<<std::endl;
                }
    | ( deref_expr
                ( EQUAL
                | AND_OP_EQ^ 
                | ASTERIX_EQ^ 
                | EQ_OP_EQ^ 
                | GE_OP_EQ^
                | GTMARK_EQ^
                | GT_OP_EQ^
                | LE_OP_EQ^
                | LTMARK_EQ^
                | LT_OP_EQ^
                | MATRIX_OP1_EQ^
                | MATRIX_OP2_EQ^
                | MINUS_EQ^
                | MOD_OP_EQ^
                | NE_OP_EQ^
                | OR_OP_EQ^
                | PLUS_EQ^
                | POW_EQ^
                | SLASH_EQ^
                | XOR_OP_EQ^
                | DEC^  
                | INC^ // no POSTDEC/POSTINC for statements            
                | MEMBER! // member procedure call 
                )
      )=>
        deref_expr
            (EQUAL! expr             
                { #statement = #([ASSIGN,":="], #statement); if (debugParser) std::cout<<" statement : \""<<LT(0)->getText()<<"\""<<std::endl;}
            |   ( AND_OP_EQ^ 
                | ASTERIX_EQ^ 
                | EQ_OP_EQ^ 
                | GE_OP_EQ^
                | GTMARK_EQ^
                | GT_OP_EQ^
                | LE_OP_EQ^
                | LTMARK_EQ^
                | LT_OP_EQ^
                | MATRIX_OP1_EQ^
                | MATRIX_OP2_EQ^
                | MINUS_EQ^
                | MOD_OP_EQ^
                | NE_OP_EQ^
                | OR_OP_EQ^
                | PLUS_EQ^
                | POW_EQ^
                | SLASH_EQ^
                | XOR_OP_EQ^) expr
            | (DEC^ | INC^) // no POSTDEC/POSTINC for statements            
            | MEMBER! // member procedure call 
                (baseclass_method { parent=true; })? 
                formal_procedure_call
                { 
                    if( parent)
                        #statement = #([MPCALL_PARENT, "mpcall::"], 
                                        #statement); 
                    else
                        #statement = #([MPCALL, "mpcall"], #statement);
		if (debugParser) std::cout<<"statement : \""<<LT(0)->getText()<<"\""<<std::endl;
                }
            )
    | d3:deref_dot_expr_keeplast formal_procedure_call
                { 
                    #statement = #([MPCALL, "mpcall"], #statement);
                    #statement->SetLine( #d3->getLine()); if (debugParser) std::cout<<" statement : \""<<LT(0)->getText()<<"\""<<std::endl;
                }
    | (DEC^ | INC^) expr
    | procedure_call // next two handled by procedure_call also
//    | BREAK     // only valid in loops and switch_statement
//    | CONTINUE  // only valid in loops
    | for_statement 
    | foreach_statement 
    | repeat_statement
    | while_statement
    | jump_statement
    | if_statement
    | case_statement
    | switch_statement
    | forward_function
    | common_block
    | compile_opt
    ;


repeat_statement
{    if (debugParser) std::cout << " repeat_statement " << std::endl; }
    : REPEAT^ 
        repeat_block
        UNTIL! expr
    ;


repeat_block
{    if (debugParser) std::cout << " repeat_block " << std::endl; }
    : st:statement
        { #repeat_block = #([BLOCK, "block"], #st); if (debugParser) std::cout<<std::endl;}
    | BEGIN! stl:statement_list endrep_mark
        { #repeat_block = #([BLOCK, "block"], #stl); if (debugParser) std::cout<<std::endl;}
    ;


while_statement
{    if (debugParser) std::cout << " while_statement " << std::endl; }
    : WHILE^
        expr DO! 
        while_block
    ;


while_block
{    if (debugParser) std::cout << " while_block " << std::endl; }
    : statement 
    | BEGIN! statement_list endwhile_mark
        { #while_block = #([BLOCK, "block"], #while_block); if (debugParser) std::cout<<std::endl;}
    ;


for_statement
{    if (debugParser) std::cout << " for_statement " << std::endl; }
    : FOR^ IDENTIFIER EQUAL! expr COMMA! expr 
        (COMMA! expr)? DO!
        for_block
    ;

// GD reverted (below) to historical version as the following, while permitting #52 creates #1599 and #1608
//for_block
//{    if (debugParser) std::cout << " for_block " << std::endl; }
//    :
//      (BEGIN statement)=> (BEGIN! stb:statement) { #for_block = #([BLOCK, "block"], #stb); if (debugParser) std::cout<<std::endl;}
//    |  BEGIN! stl:statement_list endfor_mark { #for_block = #([BLOCK, "block"], #stl); if (debugParser) std::cout<<std::endl;}
//    |  st:statement { #for_block = #([BLOCK, "block"], #st); if (debugParser) std::cout<<std::endl;}
//    ;    

for_block
{    if (debugParser) std::cout << " for_block " << std::endl; }
    :  st:statement { #for_block = #([BLOCK, "block"], #st); if (debugParser) std::cout<<std::endl;}
    |  BEGIN! stl:statement_list endfor_mark { #for_block = #([BLOCK, "block"], #stl); if (debugParser) std::cout<<std::endl;}
    ;    

foreach_statement
{    if (debugParser) std::cout << " foreach_statement " << std::endl; }
    : FOREACH^ IDENTIFIER COMMA! expr (COMMA! IDENTIFIER)? DO!
        foreach_block
    ;

foreach_block
{    if (debugParser) std::cout << " foreach_block " << std::endl; }
    : st:statement
        { #foreach_block = #([BLOCK, "block"], #st); if (debugParser) std::cout<<std::endl;}
    | BEGIN! stl:statement_list endforeach_mark
        { #foreach_block = #([BLOCK, "block"], #stl); if (debugParser) std::cout<<std::endl;}
    ;    

jump_statement
{    if (debugParser) std::cout << " jump_statement " << std::endl; }
    : GOTO^ COMMA! IDENTIFIER
// now handled as a procedure_call because RETURN is no reserved word
//    | RETURN^ (COMMA! expr)?
    | ON_IOERROR^ COMMA! IDENTIFIER
    ;

// the classical greedy case (match ELSE as soon as possible)
if_statement
{    if (debugParser) std::cout << " if_statement " << std::endl; }
    : IF^ expr THEN!
        if_block
        ( options {greedy=true;}: ELSE! 
            else_block
        )?
    ;


if_block
{    if (debugParser) std::cout << " if_block " << std::endl; }
    : statement 
    | BEGIN! statement_list endif_mark
        { #if_block = #([BLOCK, "block"], #if_block); if (debugParser) std::cout<<std::endl;}
    ;


else_block
{    if (debugParser) std::cout << " else_block " << std::endl; }
    : statement
    | BEGIN! statement_list endelse_mark
        { #else_block = #([BLOCK, "block"], #else_block); if (debugParser) std::cout<<std::endl;}
    ;

formal_procedure_call
{    if (debugParser) std::cout << " formal_procedure_call -> " /* << std::endl */; }
    : IDENTIFIER (COMMA! parameter_def_list)?
    ;    

// must handle RETURN, BREAK, CONTINUE also
procedure_call!//
{    if (debugParser) std::cout << " procedure_call! -> " /* << std::endl */; }
// was:
// formal_procedure_call
    : id:IDENTIFIER 
        ( {id->getText() == "RETURN"}?
            (COMMA! e:expr)?
            { 
                #id->setType(RETURN); // text is already "return"
                #procedure_call = #( #id, #e); // make root
             if (debugParser) std::cout<<" procedure_call : \""<<LT(0)->getText()<<"\""<<std::endl;}
        | {id->getText() == "BREAK"}?
            {
                #id->setType(BREAK); // text is already "break"
                #procedure_call = #id; if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;
            }
        | {id->getText() == "CONTINUE"}?
            {
                #id->setType(CONTINUE); // text is already "continue"
                #procedure_call = #id; if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;
            }
        | (COMMA! pa:parameter_def_list)? 
        { 
            #procedure_call = #([PCALL, "pcall"], #id, #pa);
            #procedure_call->SetLine(id->getLine()); if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;
        }
        )
    ;    

// ambiguity with arrays 
// but as arrays got priority, only real function calls need
// to be handled here
formal_function_call
{    if (debugParser) std::cout << " formal_function_call -> " /* << std::endl */; }
    : IDENTIFIER LBRACE! (parameter_def_list)? RBRACE!
    ;

parameter_def
{    if (debugParser) std::cout << " parameter_def -> " /* << std::endl */; }
//    : IDENTIFIER EQUAL! expr
    : identifier EQUAL! expr
        { #parameter_def = #([KEYDEF,"!=!"], #parameter_def); if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;}
    | expr
//    | SLASH! id:IDENTIFIER
    | SLASH! id:identifier
        {
            RefDNode c=static_cast<RefDNode>( #[CONSTANT,"1"]);
            c->Text2Int(10);
            c->SetLine( #id->getLine());
            #parameter_def = #([KEYDEF,"!=!"], id, c); if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;
        }
    ;

parameter_def_list
{    if (debugParser) std::cout << " parameter_def_list -> " /* << std::endl */; }
    : parameter_def ( COMMA! parameter_def)*
    ;

// [expr,...]
array_def
{
bool constant = true;
int flexible_array_def_count=1;
    if (debugParser) std::cout << " array_def -> " /* << std::endl */; 
}
    : LSQUARE! e:expr {if( !ConstantExprNode( #e->getType())) constant = false;}
      (
        (COMMA! ee:expr {if( !ConstantExprNode( #ee->getType())) constant = false;} )* RSQUARE!
            { 
              if( constant)
              #array_def = #([ARRAYDEF_CONST, "array_def_const"], #array_def);
              else
              #array_def = #([ARRAYDEF, "array_def"], #array_def); if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;
            }
        | (COLON! eee:expr {flexible_array_def_count++;})+ RSQUARE!
          {
            if (flexible_array_def_count>3 || flexible_array_def_count<2) throw GDLException( "Illegal array creation syntax.");
            #array_def = #([ARRAYDEF_GENERALIZED_INDGEN, "array_def_generalized_indgen"], #array_def); if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;
          } 
      )
    ;

struct_identifier
{    if (debugParser) std::cout << " struct_identifier -> " /* << std::endl */; }
    :   ( IDENTIFIER 
        | s:SYSVARNAME  { #s->setType( IDENTIFIER);}  
        | e:EXCLAMATION { #e->setType( IDENTIFIER);}  
        | i:INHERITS    { #i->setType( IDENTIFIER);}  
        ) 
        // fake IDENTIFIER (struct tag can also be "!" or "!XXXX")
        // no additional subtype is needed here, as struct_def already creates
        // the appropriate node (ie. there is no ambiguity in the parser output)
    ;

struct_name
{    if (debugParser) std::cout << " struct_name -> " /* << std::endl */; }
    :   s:struct_identifier
        // here we translate IDL_OBECT to GDL_OBJECT for source code compatibility
        {
            if( #s->getText() == "IDL_OBJECT")
                #s->setText(GDL_OBJECT_NAME);
            else if( #s->getText() == "IDL_CONTAINER")
                #s->setText(GDL_CONTAINER_NAME);
        }
    ;

struct_def
{    if (debugParser) std::cout << " struct_def -> " /* << std::endl */; }
    : LCURLY! 
        (struct_name (COMMA! named_tag_def_list)? RCURLY!
            { #struct_def = 
                #([NSTRUC_REF, "nstruct_ref"], #struct_def); if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;}
        | tag_def_list RCURLY!
            { #struct_def = 
                #([STRUC, "struct"], #struct_def); if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;}
        )
    ;

tag_def
{    if (debugParser) std::cout << " tag_def -> " /* << std::endl */; }
    : struct_identifier COLON! expr    
    ;    

tag_def_list
{    if (debugParser) std::cout << " tag_def_list -> " /* << std::endl */; }
    : tag_def (options {greedy=true;} : COMMA! tag_def)*
    ;    

ntag_def
{    if (debugParser) std::cout << " ntag_def -> " /* << std::endl */; }
    : tag_def
    | expr // for named structs, just the definition is ok
    ;    

ntag_defs
{    if (debugParser) std::cout << " ntag_defs -> " /* << std::endl */; }
    : ntag_def (options {greedy=true;} : COMMA! ntag_def)*
    ;    

named_tag_def_entry
{    if (debugParser) std::cout << " named_tag_def_entry -> " /* << std::endl */; }
    :   ( (INHERITS) => INHERITS struct_name
        | ntag_def
        )
    ;

named_tag_def_list
{    if (debugParser) std::cout << " named_tag_def_list -> " /* << std::endl */; }
    : named_tag_def_entry ( COMMA! named_tag_def_entry)*
    ;    

numeric_constant!//
{    if (debugParser) std::cout << " numeric_constant. " << std::endl; }
    :  ( c1:CONSTANT_HEX_BYTE    
        { #numeric_constant=#[CONSTANT,c1->getText()];
          #numeric_constant->Text2Byte(16);    
          #numeric_constant->SetLine( c1->getLine());    
        }  
    | c2:CONSTANT_HEX_LONG 
        { #numeric_constant=#[CONSTANT,c2->getText()];
          #numeric_constant->Text2Long(16);    
          #numeric_constant->SetLine( c2->getLine());    
        }  
    | c3:CONSTANT_HEX_LONG64 
        { #numeric_constant=#[CONSTANT,c3->getText()];
          #numeric_constant->Text2Long64(16);    
          #numeric_constant->SetLine( c3->getLine());    
        }  
    | c4:CONSTANT_HEX_INT 
        { #numeric_constant=#[CONSTANT,c4->getText()];
          #numeric_constant->Text2Int(16);    
          #numeric_constant->SetLine( c4->getLine());    
        }  
    | c44:CONSTANT_HEX_I 
        // DEFINT32
        { #numeric_constant=#[CONSTANT,c44->getText()];
            if( compileOpt & DEFINT32)
                #numeric_constant->Text2Long(16,true);    
            else
                #numeric_constant->Text2Int(16,true);    
          #numeric_constant->SetLine( c44->getLine());    
        }  
    | c5:CONSTANT_HEX_ULONG 
        { #numeric_constant=#[CONSTANT,c5->getText()];
          #numeric_constant->Text2ULong(16);    
          #numeric_constant->SetLine( c5->getLine());    
        }  
    | c6:CONSTANT_HEX_ULONG64 
        { #numeric_constant=#[CONSTANT,c6->getText()];
          #numeric_constant->Text2ULong64(16);    
          #numeric_constant->SetLine( c6->getLine());    
        }  
    | c77:CONSTANT_HEX_UI
        // DEFINT32
        { #numeric_constant=#[CONSTANT,c77->getText()];
            if( compileOpt & DEFINT32)
                #numeric_constant->Text2ULong(16,true);    
            else
                #numeric_constant->Text2UInt(16,true);    
          #numeric_constant->SetLine( c77->getLine());    
        }  
    | c7:CONSTANT_HEX_UINT
        { #numeric_constant=#[CONSTANT,c7->getText()];
          #numeric_constant->Text2UInt(16);    
          #numeric_constant->SetLine( c7->getLine());    
        }  
    | c8:CONSTANT_BYTE  
        { #numeric_constant=#[CONSTANT,c8->getText()];
          #numeric_constant->Text2Byte(10);    
          #numeric_constant->SetLine( c8->getLine());    
        }  
    | c9:CONSTANT_LONG 
        { #numeric_constant=#[CONSTANT,c9->getText()];
          #numeric_constant->Text2Long(10);    
          #numeric_constant->SetLine( c9->getLine());    
        }  
    | c10:CONSTANT_LONG64 
        { #numeric_constant=#[CONSTANT,c10->getText()];
          #numeric_constant->Text2Long64(10);    
          #numeric_constant->SetLine( c10->getLine());    
        }  
    | c11:CONSTANT_INT
        { #numeric_constant=#[CONSTANT,c11->getText()];
          #numeric_constant->Text2Int(10);    
          #numeric_constant->SetLine( c11->getLine());    
        }  
    | c111:CONSTANT_I
        // DEFINT32
        { #numeric_constant=#[CONSTANT,c111->getText()];
            if( compileOpt & DEFINT32)
                #numeric_constant->Text2Long(10,true);    
            else
                #numeric_constant->Text2Int(10,true);    
          #numeric_constant->SetLine( c111->getLine());    
        }  
    | c12:CONSTANT_ULONG 
        { #numeric_constant=#[CONSTANT,c12->getText()];
          #numeric_constant->Text2ULong(10);    
          #numeric_constant->SetLine( c12->getLine());    
        }  
    | c13:CONSTANT_ULONG64 
        { #numeric_constant=#[CONSTANT,c13->getText()];
          #numeric_constant->Text2ULong64(10);    
          #numeric_constant->SetLine( c13->getLine());    
        }  
    | c144:CONSTANT_UI
        // DEFINT32
        { #numeric_constant=#[CONSTANT,c144->getText()];
            if( compileOpt & DEFINT32)
                #numeric_constant->Text2ULong(10,true);    
            else
                #numeric_constant->Text2UInt(10,true);    
          #numeric_constant->SetLine( c144->getLine());    
        }  
    | c14:CONSTANT_UINT
        { #numeric_constant=#[CONSTANT,c14->getText()];
          #numeric_constant->Text2UInt(10);    
          #numeric_constant->SetLine( c14->getLine());    
        }  
    | c15:CONSTANT_OCT_BYTE  
        { #numeric_constant=#[CONSTANT,c15->getText()];
          #numeric_constant->Text2Byte(8);    
          #numeric_constant->SetLine( c15->getLine());    
        }  
    | c16:CONSTANT_OCT_LONG 
        { #numeric_constant=#[CONSTANT,c16->getText()];
          #numeric_constant->Text2Long(8);    
          #numeric_constant->SetLine( c16->getLine());    
        }  
    | c17:CONSTANT_OCT_LONG64 
        { #numeric_constant=#[CONSTANT,c17->getText()];
          #numeric_constant->Text2Long64(8);    
          #numeric_constant->SetLine( c17->getLine());    
        }  
    | c18:CONSTANT_OCT_INT
        { #numeric_constant=#[CONSTANT,c18->getText()];
          #numeric_constant->Text2Int(8);    
          #numeric_constant->SetLine( c18->getLine());    
        }  
    | c188:CONSTANT_OCT_I
        // DEFINT32
        { #numeric_constant=#[CONSTANT,c188->getText()];
            if( compileOpt & DEFINT32)
                #numeric_constant->Text2Long(8,true);    
            else
                #numeric_constant->Text2Int(8,true);    
          #numeric_constant->SetLine( c188->getLine());    
        }  
    | c19:CONSTANT_OCT_ULONG 
        { #numeric_constant=#[CONSTANT,c19->getText()];
          #numeric_constant->Text2ULong(8);    
          #numeric_constant->SetLine( c19->getLine());    
        }  
    | c20:CONSTANT_OCT_ULONG64 
        { #numeric_constant=#[CONSTANT,c20->getText()];
          #numeric_constant->Text2ULong64(8);    
          #numeric_constant->SetLine( c20->getLine());    
        }  
    | c211:CONSTANT_OCT_UI
        // DEFINT32
        { #numeric_constant=#[CONSTANT,c211->getText()];
            if( compileOpt & DEFINT32)
                #numeric_constant->Text2ULong(8,true);    
            else
                #numeric_constant->Text2UInt(8,true);    
          #numeric_constant->SetLine( c211->getLine());    
        }  
    | c21:CONSTANT_OCT_UINT
        { #numeric_constant=#[CONSTANT,c21->getText()];
          #numeric_constant->Text2UInt(8);    
          #numeric_constant->SetLine( c21->getLine());    
        }  
    | c22:CONSTANT_FLOAT     
        { #numeric_constant=#[CONSTANT,c22->getText()];
          #numeric_constant->Text2Float();    
          #numeric_constant->SetLine( c22->getLine());    
        }  
    | c23:CONSTANT_DOUBLE
        { #numeric_constant=#[CONSTANT,c23->getText()];
          #numeric_constant->Text2Double();    
          #numeric_constant->SetLine( c23->getLine());    
        }  
    | c24:CONSTANT_BIN_BYTE  
        { #numeric_constant=#[CONSTANT,c24->getText()];
          #numeric_constant->Text2Byte(2);    
          #numeric_constant->SetLine( c24->getLine());    
        }  
    | c25:CONSTANT_BIN_LONG 
        { #numeric_constant=#[CONSTANT,c25->getText()];
          #numeric_constant->Text2Long(2);    
          #numeric_constant->SetLine( c25->getLine());    
        }  
    | c26:CONSTANT_BIN_LONG64 
        { #numeric_constant=#[CONSTANT,c26->getText()];
          #numeric_constant->Text2Long64(2);    
          #numeric_constant->SetLine( c26->getLine());    
        }  
    | c27:CONSTANT_BIN_INT
        { #numeric_constant=#[CONSTANT,c27->getText()];
          #numeric_constant->Text2Int(2);    
          #numeric_constant->SetLine( c27->getLine());    
        }  
    | c277:CONSTANT_BIN_I
        // DEFINT32
        { #numeric_constant=#[CONSTANT,c277->getText()];
            if( compileOpt & DEFINT32)
                #numeric_constant->Text2Long(2,true);    
            else
                #numeric_constant->Text2Int(2,true);    
          #numeric_constant->SetLine( c277->getLine());    
        }  
    | c28:CONSTANT_BIN_ULONG 
        { #numeric_constant=#[CONSTANT,c28->getText()];
          #numeric_constant->Text2ULong(2);    
          #numeric_constant->SetLine( c28->getLine());    
        }  
    | c29:CONSTANT_BIN_ULONG64 
        { #numeric_constant=#[CONSTANT,c29->getText()];
          #numeric_constant->Text2ULong64(2);    
          #numeric_constant->SetLine( c29->getLine());    
        }  
    | c300:CONSTANT_BIN_UI
        // DEFINT32
        { #numeric_constant=#[CONSTANT,c300->getText()];
            if( compileOpt & DEFINT32)
                #numeric_constant->Text2ULong(2,true);    
            else
                #numeric_constant->Text2UInt(2,true);    
          #numeric_constant->SetLine( c300->getLine());    
        }  
    | c30:CONSTANT_BIN_UINT
        { #numeric_constant=#[CONSTANT,c30->getText()];
          #numeric_constant->Text2UInt(2);    
          #numeric_constant->SetLine( c30->getLine());    
        }
	) {if (debugParser) std::cout<<" numeric_constant : \""<<LT(0)->getText()<<"\""<<std::endl;}
    ;

arrayindex_list
{        
    int rank = 1;
    if (debugParser) std::cout << " arrayindex_list -> " /* << std::endl */; 
}
    : LSQUARE! arrayindex ({++rank <= MAXRANK}? COMMA! arrayindex)* RSQUARE!
    | { IsRelaxed()}? LBRACE! arrayindex ({++rank <= MAXRANK}? COMMA! arrayindex)* RBRACE!
    ;    

all_elements!
{    if (debugParser) std::cout << " all_elements! -> " /* << std::endl */; }
    : ASTERIX { #all_elements = #([ALL,"*"]); if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;}
    ;

// used only from arrayindex_list
arrayindex
{    if (debugParser) std::cout << " arrayindex -> " /* << std::endl */; }
  : ((ASTERIX (COMMA|{ IsRelaxed()}? RBRACE|RSQUARE))=> all_elements
    | expr
         (COLON! 
              (
                (ASTERIX (COMMA|{ IsRelaxed()}? RBRACE|RSQUARE|COLON))=> all_elements
              | expr
              )
              (COLON! 
                  (
                    (ASTERIX (COMMA|{ IsRelaxed()}? RBRACE|RSQUARE))=> ASTERIX!
                      {
                      throw  GDLException( "n:n:* subscript form not allowed.");
                      }
                  | expr
                  )
              )?
         )?
    )
    { #arrayindex = #([ARRAYIX,"arrayix"], #arrayindex); if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;}
    ;

// the expressions *************************************

// system variable name
sysvar
{    if (debugParser) std::cout << " sysvar -> " /* << std::endl */; }
  : SYSVARNAME
    { #sysvar = #([SYSVAR,"SYSVAR"],sysvar); if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;}
      ;

// variable name
var!
{    if (debugParser) std::cout << " var: " /* << std::endl */; }
    :   ( id:IDENTIFIER
            {
                #var = #([VAR,"VAR"],id); if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;
            }
        | ih:INHERITS 
            { 
                #ih->setType( IDENTIFIER);
                #var = #([VAR,"VAR"],ih); if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;
            }  
        // fake IDENTIFIER (variable name can be "INHERITS")
        )
    ;

// this is SYNTATICALLY ok as an lvalue, but if one try to assign
// something to an non-var an error is raised
brace_expr
    :  LBRACE! expr RBRACE!       
        { #brace_expr = 
            #([EXPR,"expr"], #brace_expr); if (debugParser) std::cout<<"brace_expr: \""<<LT(0)->getText()<<"\""<<std::endl;
                }
    ;

// only used in deref_expr
// sysvar or expr (first in struct access - therefore the name)
array_expr_1st_sub
{    if (debugParser) std::cout << " array_expr_1st_sub -> " /* << std::endl */; }
    // a variable MUST be already defined here
    :  var 
    |  sysvar 
    |  brace_expr
    ;

array_expr_1st!
{    if (debugParser) std::cout << " array_expr_1st! -> " /* << std::endl */; }
// a variable MUST be already defined here
    : e:array_expr_1st_sub
        ( al:arrayindex_list
            { #array_expr_1st = 
                #([ARRAYEXPR,"arrayexpr"], #e, #al); if (debugParser) std::cout<<" array_expr_1st: \""<<LT(0)->getText()<<"\""<<std::endl;}
        | // empty
            { #array_expr_1st = #e; if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;}
        )
    ;    

array_expr_nth_sub
{    if (debugParser) std::cout << " array_expr_nth_sub -> " /* << std::endl */; }
    : IDENTIFIER
    | brace_expr
    ;

// expr
array_expr_nth!
{    if (debugParser) std::cout << " array_expr_nth! -> " /* << std::endl */; }
    : e:array_expr_nth_sub
        ( al:arrayindex_list
            { #array_expr_nth = 
                #([ARRAYEXPR,"arrayexpr"], #e, #al); if (debugParser) std::cout<<"array_expr_nth: \""<<LT(0)->getText()<<"\""<<std::endl;}
        | // empty
            { #array_expr_nth = #e; if (debugParser) std::cout<<"array_expr_nth: \""<<LT(0)->getText()<<"\""<<std::endl;}
        )
    ;    

tag_array_expr_nth_sub
{    if (debugParser) std::cout << " tag_array_expr_nth_sub -> " /* << std::endl */; }
    : IDENTIFIER
    | s:SYSVARNAME  
        { #s->setType( IDENTIFIER); /* #s->setText( "!" + #s->getText()); */}  
    | e:EXCLAMATION { #e->setType( IDENTIFIER);}  
    | brace_expr
    ;

tag_array_expr_nth!
{    if (debugParser) std::cout << " tag_array_expr_nth! -> " /* << std::endl */; }
    : e:tag_array_expr_nth_sub
        ( al:arrayindex_list
            { #tag_array_expr_nth = 
                #([ARRAYEXPR,"arrayexpr"], #e, #al); if (debugParser) std::cout<<"tag_array_expr_nth: \""<<LT(0)->getText()<<"\""<<std::endl;}
        | // empty
            { #tag_array_expr_nth = #e; if (debugParser) std::cout<<"tag_array_expr_nth: \""<<LT(0)->getText()<<"\""<<std::endl;}
        )
    ;    

protected
tag_access_keeplast returns [int nDot]
{
    int t;
    bool parent = false;
    nDot=1;
    if (debugParser) std::cout << " tag_access_keeplast -> " /* << std::endl */; 
}
    : DOT!
        (
            (tag_array_expr_nth DOT)=>
// not working: (tag_array_expr_nth tag_access_keeplast)=>
                    (tag_array_expr_nth t=tag_access_keeplast { nDot += t;})
        |   //(tag_array_expr_nth DOT tag_array_expr_nth)=> 
            //        (tag_array_expr_nth)
        )
    ;


deref_dot_expr_keeplast
{
    RefDNode dot;
    int nDot;
    if (debugParser) std::cout << " deref_dot_expr_keeplast -> " /* << std::endl */; 
}
    : a1:array_expr_1st 
        (// (tag_access_keeplast)=>
        nDot=tag_access_keeplast
            { 
                if( --nDot > 0)
                    {
                        dot=#[DOT,"."];
                        dot->SetNDot( nDot);    
                        dot->SetLine( #a1->getLine());
                        #deref_dot_expr_keeplast = #(dot, #deref_dot_expr_keeplast); if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;
                    }
            }        
//      |   { #deref_dot_expr_keeplast = #a1; if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;}
        )
    | ASTERIX! deref_dot_expr_keeplast
        { #deref_dot_expr_keeplast = 
            #([DEREF,"deref"], #deref_dot_expr_keeplast); if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;}
    ;

protected
tag_access returns [SizeT nDot]
{
    nDot=0;
    if (debugParser) std::cout << " tag_access -> " /* << std::endl */; 
}
    : (options {greedy=true;}: DOT! { ++nDot;} tag_array_expr_nth)+
    ;

deref_dot_expr
{
    RefDNode dot;
    SizeT nDot;
    if (debugParser) std::cout << " deref_dot_expr -> " /* << std::endl */; 
}
//    : array_expr_1st (DOT array_expr_nth)*
    : a1:array_expr_1st 
        (nDot=tag_access
            { 

                dot=#[DOT,"."];
                dot->SetNDot( nDot);    
                dot->SetLine( #a1->getLine());

                #deref_dot_expr = #(dot, #deref_dot_expr); if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;
            }        
//        |   { #deref_expr = #a1;}
        )
    | ASTERIX! deref_dot_expr
        { #deref_dot_expr = 
            #([DEREF,"deref"], #deref_dot_expr); if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;}
    ;

deref_expr
{
    RefDNode dot;
    SizeT nDot;
    if (debugParser) std::cout << " deref_expr -> " /* << std::endl */; 
}
//    : array_expr_1st (DOT array_expr_nth)*
    : a1:array_expr_1st 
        // ((tag_access)=> nDot=tag_access
        ((DOT)=> nDot=tag_access
            { 

                dot=#[DOT,"."];
                dot->SetNDot( nDot);    
                dot->SetLine( #a1->getLine());

                #deref_expr = #(dot, #deref_expr); if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;
            }        
        |   { #deref_expr = #a1; if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;}
        )
    | ASTERIX! deref_expr
        { #deref_expr = 
            #([DEREF,"deref"], #deref_expr); if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;}
    ;


protected
member_function_call returns [bool parent]
{    if (debugParser) std::cout << " member_function_call -> " /* << std::endl */; }
    : { parent = false;} MEMBER! 
        (s:IDENTIFIER METHOD! 
            { 
        // here we translate IDL_OBECT to GDL_OBJECT for source code compatibility
        {
            if( #s->getText() == "IDL_OBJECT")
                #s->setText(GDL_OBJECT_NAME);
            else if( #s->getText() == "IDL_CONTAINER")
                #s->setText(GDL_CONTAINER_NAME);
        }
                parent = true;
            } )? formal_function_call
      ;
member_function_call_dot
{    if (debugParser) std::cout << " member_function_call_dot -> " /* << std::endl */; }
    :  DOT! (s:IDENTIFIER METHOD!
        // here we translate IDL_OBECT to GDL_OBJECT for source code compatibility
        {
            if( #s->getText() == "IDL_OBJECT")
                #s->setText(GDL_OBJECT_NAME);
            else if( #s->getText() == "IDL_CONTAINER")
                #s->setText(GDL_CONTAINER_NAME);
        }
        ) formal_function_call
      ;

assign_expr
{    if (debugParser) std::cout << " assign_expr -> " /* << std::endl */; }
    : LBRACE! deref_expr EQUAL! expr RBRACE! // assignment
        { #assign_expr = #([ASSIGN,":="], #assign_expr); if (debugParser) std::cout<<" assign_expr : \""<<LT(0)->getText()<<"\""<<std::endl;}
    ;

// arrayexpr_mfcall_last
//     : (IDENTIFIER^ arrayindex_list) 
//     ;

// only used for production in primary_expr
arrayexpr_mfcall!
{
    RefDNode dot;
    RefDNode tag;
    int nDot;
    if (debugParser) std::cout << " arrayexpr_mfcall! -> " /* << std::endl */; 
}
    : a1:array_expr_1st 
        (   // this rule is only for production // (tag_access_keeplast)=>
            nDot=t1:tag_access_keeplast
            { 
                if( --nDot > 0)
                    {
                        dot=#[DOT,"DOT_A_MF"];
                        dot->SetNDot( nDot);    
                        dot->SetLine( #a1->getLine());
                        tag = #(dot, #a1, #t1);
                    }
           }        
        )
        id:IDENTIFIER al:arrayindex_list
        {
            if( nDot > 0)
                #arrayexpr_mfcall = #([ARRAYEXPR_MFCALL,"arrayexpr_mfcall"], #tag, #id, #al);
            else
                #arrayexpr_mfcall = #([ARRAYEXPR_MFCALL,"arrayexpr_mfcall"], #a1, #id, #al);
         if (debugParser) std::cout<<"arrayexpr_mfcall : \""<<LT(0)->getText()<<"\""<<std::endl;
	 }
    | ASTERIX deref_arrayexpr_mfcall:arrayexpr_mfcall
        { #arrayexpr_mfcall = 
            #([DEREF,"deref"], #deref_arrayexpr_mfcall); if (debugParser) std::cout<<" deref_arrayexpr_mfcall : \""<<LT(0)->getText()<<"\""<<std::endl;}
    ;

primary_expr_tail
{
    bool parent;
    if (debugParser) std::cout << " -> primary_expr_tail -> ";
    }
    : 
         // a member function call starts with a deref_expr 
         (deref_dot_expr)=>
        // same parsing as (deref_expr)=> see below
        deref_expr 
        ( parent=member_function_call
            {
                if( parent)
                {
                    #primary_expr_tail = #([MFCALL_PARENT, "mfcall::"], #primary_expr_tail);
		    if (debugParser) std::cout << " (deref_dot_expr)=>deref_expr ( parent=true) -> mfcall" << std::endl;
                } 
                else
                {
                    #primary_expr_tail = #([MFCALL, "mfcall"], #primary_expr_tail);
		    if (debugParser) std::cout << " (deref_dot_expr)=>deref_expr -> primary_expr ->  mfcall" << std::endl;
                }
            }
        | { if (debugParser) std::cout << " | empty -> array expression -> "/* << std::endl */;}  // empty -> array expression
        )
    |   
        // ambiguity (arrayexpr or fcall)
        (IDENTIFIER LBRACE expr (COMMA expr)* RBRACE)=>
        (

            // already known function 
            // (could be reordered, but this is conform to original)
            { IsFun(LT(1))}? formal_function_call
            { 
             if (debugParser) std::cout << " (IDENTIFIER LBRACE expr (COMMA expr)* RBRACE)=> formal_function_call : primary_expr : " /* << std::endl */;
                   #primary_expr_tail = #([FCALL, "fcall"], #primary_expr_tail); if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;
            }
        | 
            // still ambiguity (arrayexpr or fcall)
        (var arrayindex_list)=> var arrayindex_list     // array_expr_fn
            { 
             if (debugParser) std::cout << "(var arrayindex_list)=> var arrayindex_list -> primary_expr : " /* << std::endl */;

                #primary_expr_tail = #([ARRAYEXPR_FCALL,"arrayexpr_fcall"], #primary_expr_tail); if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;
	    }
        |   // if arrayindex_list failed (due to to many indices)
            // this must be a function call
            formal_function_call
            {  if (debugParser) std::cout << " (IDENTIFIER LBRACE expr (COMMA expr)* RBRACE)=>formal_function_call -> primary_expr : " /* << std::endl */;
                   #primary_expr_tail = #([FCALL, "fcall"], #primary_expr_tail); if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;
            }
        )
    |   // not the above => keyword parameter (or no args) => function call
         (formal_function_call)=> formal_function_call
         { if (debugParser) std::cout << " (formal_function_call)=> formal_function_call -> primary_expr :" << std::endl; #primary_expr_tail = #([FCALL, "fcall"], #primary_expr_tail);}

    |   // a member function call starts with a deref_expr 
        // deref_dot_expr already failed
         (deref_expr)=>
        deref_expr 
        ( parent=member_function_call
            { 
                if( parent)
                {
                    if (debugParser) std::cout << " (deref_expr)=> deref_expr ( parent=true) -> mfcall :" /* << std::endl */;
                    #primary_expr_tail = #([MFCALL_PARENT, "mfcall::"], #primary_expr_tail); if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;
                }
                else
                {
                    if (debugParser) std::cout << " (deref_expr)=> deref_expr ( parent=false) -> mfcall :" /* << std::endl */;
                    #primary_expr_tail = #([MFCALL, "mfcall"], #primary_expr_tail); if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;
                }
            }
        |{ if (debugParser) std::cout << " (deref_expr)=> deref_expr | empty -> array expression No 2! -> "/* << std::endl */;}  // empty -> array expression
        )

    | assign_expr 	   
    | array_def 	   
    | struct_def 	   
    | ! ls:LSQUARE !RSQUARE
        { #primary_expr_tail=#[GDLNULL,"GDLNULL[]"];
            #primary_expr_tail->SetLine( #ls->getLine()); if (debugParser) std::cout << "NULL" << std::endl;
        }  
    | ! lc:LCURLY !RCURLY
        { #primary_expr_tail=#[GDLNULL,"GDLNULL{}"];
            #primary_expr_tail->SetLine( #lc->getLine()); if (debugParser) std::cout << "NULL" << std::endl;
        }  
    ;

primary_expr_deref
{
// the following needs to be updated if the symbols are rearranged (e. g. a symbol is inserted)
// (it is taken from GDLParser.cpp: const antlr::BitSet GDLParser::_tokenSet_XX)
const unsigned long _tokenSet_4_data_[] = { 0UL, 0UL, 268435456UL, 1048576UL, 536870912UL, 4UL, 4096UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL };
// IDENTIFIER "inherits" LBRACE SYSVARNAME ASTERIX 
const antlr::BitSet _tokenSet_4(_tokenSet_4_data_,16);
const unsigned long _tokenSet_5_data_[] = { 0UL, 0UL, 268435456UL, 34603008UL, 536871296UL, 4294967253UL, 5013503UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL };
// IDENTIFIER "inherits" "not" DEC INC LBRACE LSQUARE SYSVARNAME LCURLY 
// CONSTANT_HEX_BYTE CONSTANT_HEX_LONG CONSTANT_HEX_LONG64 CONSTANT_HEX_INT 
// CONSTANT_HEX_I CONSTANT_HEX_ULONG CONSTANT_HEX_ULONG64 CONSTANT_HEX_UI 
// CONSTANT_HEX_UINT CONSTANT_BYTE CONSTANT_LONG CONSTANT_LONG64 CONSTANT_INT 
// CONSTANT_I CONSTANT_ULONG CONSTANT_ULONG64 CONSTANT_UI CONSTANT_UINT 
// CONSTANT_OCT_BYTE CONSTANT_OCT_LONG CONSTANT_OCT_LONG64 CONSTANT_OCT_INT 
// CONSTANT_OCT_I CONSTANT_OCT_ULONG CONSTANT_OCT_ULONG64 CONSTANT_OCT_UI 
// CONSTANT_OCT_UINT CONSTANT_FLOAT CONSTANT_DOUBLE CONSTANT_BIN_BYTE CONSTANT_BIN_LONG 
// CONSTANT_BIN_LONG64 CONSTANT_BIN_INT CONSTANT_BIN_I CONSTANT_BIN_ULONG 
// CONSTANT_BIN_ULONG64 CONSTANT_BIN_UI CONSTANT_BIN_UINT ASTERIX DOT STRING_LITERAL 
// PLUS MINUS LOG_NEG 
const antlr::BitSet _tokenSet_5(_tokenSet_5_data_,16);
const unsigned long _tokenSet_23_data_[] = { 0UL, 0UL, 268435456UL, 1048576UL, 536870912UL, 21UL, 4096UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL };
// IDENTIFIER "inherits" LBRACE LSQUARE SYSVARNAME LCURLY ASTERIX 
const antlr::BitSet _tokenSet_23(_tokenSet_23_data_,16);
const unsigned long _tokenSet_24_data_[] = { 2UL, 0UL, 805306368UL, 2549424140UL, 4026532283UL, 4294967295UL, 67108863UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL };
// EOF IDENTIFIER "and" "do" "else" "eq" "ge" "gt" "inherits" "le" "lt" 
// "mod" "ne" "not" "of" "or" "then" "until" "xor" COMMA COLON END_U DEC 
// INC MEMBER LBRACE RBRACE SLASH LSQUARE RSQUARE SYSVARNAME EXCLAMATION 
// LCURLY RCURLY CONSTANT_HEX_BYTE CONSTANT_HEX_LONG CONSTANT_HEX_LONG64 
// CONSTANT_HEX_INT CONSTANT_HEX_I CONSTANT_HEX_ULONG CONSTANT_HEX_ULONG64 
// CONSTANT_HEX_UI CONSTANT_HEX_UINT CONSTANT_BYTE CONSTANT_LONG CONSTANT_LONG64 
// CONSTANT_INT CONSTANT_I CONSTANT_ULONG CONSTANT_ULONG64 CONSTANT_UI 
// CONSTANT_UINT CONSTANT_OCT_BYTE CONSTANT_OCT_LONG CONSTANT_OCT_LONG64 
// CONSTANT_OCT_INT CONSTANT_OCT_I CONSTANT_OCT_ULONG CONSTANT_OCT_ULONG64 
// CONSTANT_OCT_UI CONSTANT_OCT_UINT CONSTANT_FLOAT CONSTANT_DOUBLE CONSTANT_BIN_BYTE 
// CONSTANT_BIN_LONG CONSTANT_BIN_LONG64 CONSTANT_BIN_INT CONSTANT_BIN_I 
// CONSTANT_BIN_ULONG CONSTANT_BIN_ULONG64 CONSTANT_BIN_UI CONSTANT_BIN_UINT 
// ASTERIX DOT STRING_LITERAL POW MATRIX_OP1 MATRIX_OP2 PLUS MINUS LTMARK 
// GTMARK LOG_NEG LOG_AND LOG_OR QUESTION 
const antlr::BitSet _tokenSet_24(_tokenSet_24_data_,16);
    bool parent;

    bool skip;
    int markIn = mark();

	inputState->guessing++;

    bool tailLa1La2 = (_tokenSet_23.member(LA(1))) && (_tokenSet_24.member(LA(2)));
    bool derefLa1La2 = (_tokenSet_4.member(LA(1))) && (_tokenSet_5.member(LA(2))); 

    if ( derefLa1La2) 
    {
        skip = false;
        try {
            deref_dot_expr_keeplast();
        }
        catch (antlr::RecognitionException& pe) {
            skip = true;
        }
    }
    else
        skip = true;

    if( skip && tailLa1La2)
        {
            rewind( markIn);
            inputState->guessing--;

            primary_expr_tail();
            if (inputState->guessing==0) {
                astFactory->addASTChild(currentAST, antlr::RefAST(returnAST));
            }
            primary_expr_deref_AST = RefDNode(currentAST.root);
            returnAST = primary_expr_deref_AST;
            return;
        }

    bool arrayexpr_mfcallParse = false;
    bool function_callParse = false;

    int mark2nd = mark();

    bool baseclass_methodParse = true;
    try {
        {
        baseclass_method();
        }
    }
    catch (antlr::RecognitionException& pe) {
        baseclass_methodParse = false;
    }

    rewind( mark2nd);

    if( !baseclass_methodParse)
    {
        int mark3rd = mark();

        arrayexpr_mfcallParse = true;
        try {
                {
    			match(IDENTIFIER);
    			match(LBRACE);
    			expr();
    			{ // ( ... )*
    			for (;;) {
    				if ((LA(1) == COMMA)) {
    					match(COMMA);
    					expr();
    				}
    				else {
                        break;
    				}
                }
    			} // ( ... )*
    			match(RBRACE);
                }
            }
        catch (antlr::RecognitionException& pe) {
            arrayexpr_mfcallParse = false;
        }

        rewind( mark3rd);

        if( !arrayexpr_mfcallParse)
        {
            function_callParse = true;
            try {
                {
                formal_function_call();
                }
            }
            catch (antlr::RecognitionException& pe) {
                 function_callParse = false;
            }

            if( !function_callParse && tailLa1La2)
            {
                rewind( markIn);
                inputState->guessing--;

                primary_expr_tail();
                if (inputState->guessing==0) {
                    astFactory->addASTChild(currentAST, antlr::RefAST(returnAST));
                }
                primary_expr_deref_AST = RefDNode(currentAST.root);
                returnAST = primary_expr_deref_AST;
                return; 
            }
        }
   }

   rewind( markIn);
   inputState->guessing--;
}
   :
        // with METHOD

//        (deref_dot_expr_keeplast baseclass_method)=>
        {baseclass_methodParse}?
        deref_dot_expr_keeplast baseclass_method formal_function_call
        {
            if (debugParser) std::cout << " d1:deref_dot_expr_keeplast baseclass_method formal_function_call "<< std::endl;
            #primary_expr_deref = #([MFCALL_PARENT, "mfcall::"], #primary_expr_deref);
            if (debugParser) std::cout<<"\""<<LT(0)->getText()<<"\""<<std::endl;
        }   
    | 
        // ambiguity (arrayexpr or mfcall)
        {arrayexpr_mfcallParse}?
       arrayexpr_mfcall
       {if (debugParser) std::cout << " deref_dot_expr_keeplast (IDENTIFIER LBRACE expr (COMMA expr)* RBRACE))=> arrayexpr_mfcall -> " /*<< std::endl */;}
    | 
        // not the above -> unambigous mfcall (or unambigous array expr handled below)

  //      (deref_dot_expr_keeplast formal_function_call)=> 
        {function_callParse}?
       deref_dot_expr_keeplast 
            // here it is impossible to decide about function call
            // as we do not know the object type/struct tag
            formal_function_call
            { #primary_expr_deref = #([MFCALL, "mfcall"], #primary_expr_deref);
              if (debugParser) std::cout << "  (deref_dot_expr_keeplast formal_function_call)=> d3:deref_dot_expr_keeplast formal_function_call -> " << std::endl; }
     |
        primary_expr_tail
    ;



// only here a function call is ok also (all other places must be an array)
primary_expr 
{    if (debugParser) std::cout << " -> primary_expr -> ";}
    : 
	 ! sl:STRING_LITERAL // also a CONSTANT
		{ #primary_expr=#[CONSTANT,sl->getText()];
            #primary_expr->Text2String();	
            #primary_expr->SetLine( #sl->getLine());
		}  
	| numeric_constant
    | primary_expr_deref
	;

// only one INC/DEC allowed per target
decinc_expr
//{    if (debugParser) std::cout << " -> decinc_expr "; }
    : primary_expr 
        ( i:INC^ { #i->setType( POSTINC); #i->setText( "_++");if (debugParser) std::cout << "++" <<std::endl;} 
        | d:DEC^ { #d->setType( POSTDEC); #d->setText( "_--");if (debugParser) std::cout << "--" <<std::endl;} 
        | // empty
        )
    | INC^ primary_expr
    | DEC^ primary_expr
    ;

exponential_expr
//{    if (debugParser) std::cout << " -> exponential_expr" ; }
    : decinc_expr 
        (POW^ decinc_expr 
        )*
    ;


multiplicative_expr
//{    if (debugParser) std::cout << " -> multiplicative_expr "; }
    : exponential_expr
        (
            ( ASTERIX^
            | MATRIX_OP1^
            | MATRIX_OP2^
            | SLASH^ 
            | MOD_OP^
            | AND_OP_EQ^ 
            | ASTERIX_EQ^ 
            | EQ_OP_EQ^ 
            | GE_OP_EQ^
            | GTMARK_EQ^
            | GT_OP_EQ^
            | LE_OP_EQ^
            | LTMARK_EQ^
            | LT_OP_EQ^
            | MATRIX_OP1_EQ^
            | MATRIX_OP2_EQ^
            | MINUS_EQ^
            | MOD_OP_EQ^
            | NE_OP_EQ^
            | OR_OP_EQ^
            | PLUS_EQ^
            | POW_EQ^
            | SLASH_EQ^
            | XOR_OP_EQ^
            ) exponential_expr
        )*
    ;

// only one allowed per target
signed_multiplicative_expr
//{    if (debugParser) std::cout << " -> signed_multiplicative_expr "; }
    : PLUS! multiplicative_expr
    | m:MINUS^ multiplicative_expr
        { 
        #m->setType( UMINUS); 
        #m->setText( "u-"); 
        } 
    | multiplicative_expr
    ;

additive_expr
//{    if (debugParser) std::cout << " -> additive_expr "; }
    : (signed_multiplicative_expr | neg_expr)
        ( 
            ( PLUS^
            | MINUS^
            | LTMARK^
            | GTMARK^
            ) (multiplicative_expr | neg_expr)
        )*
// //    | NOT_OP^ additive_expr // multiple allowed
//     | NOT_OP^ multiplicative_expr // multiple not allowed
// // true precedence of ~ operator
//     | LOG_NEG^ multiplicative_expr // multiple not allowed
    ;

neg_expr
//{    if (debugParser) std::cout << " -> neg_expr "; }
    : NOT_OP^ multiplicative_expr
// true precedence of ~ operator
    | LOG_NEG^ multiplicative_expr
      ;


relational_expr
//{    if (debugParser) std::cout << " -> relational_expr "; }
    : additive_expr
        (
            ( EQ_OP^
            | NE_OP^
            | LE_OP^
            | LT_OP^
            | GE_OP^
            | GT_OP^
            ) additive_expr
        )*
    ;

boolean_expr
//{    if (debugParser) std::cout << " -> boolean_expr" ; }
    : relational_expr
        ( 
            ( AND_OP^ 
            | OR_OP^ 
            | XOR_OP^ 
            ) relational_expr
        )*
    ;

logical_expr
//{    if (debugParser) std::cout << " -> logical_expr "; }
    : boolean_expr
        ( 
            ( LOG_AND^ 
            | LOG_OR^ 
            ) boolean_expr
        )*
    ;

expr
{    if (debugParser) std::cout << " expr-> "; }
  : logical_expr
    (
      QUESTION^ expr
      COLON! expr
    )?
  ;

// the GDL Lexer *********************************************
class GDLLexer extends Lexer;

options {
    charVocabulary = '\3'..'\377';
    caseSensitive=false;
    testLiterals =false;
    caseSensitiveLiterals=false;
    exportVocab=GDL;
    k=3;
    defaultErrorHandler = false;
//    defaultErrorHandler = true;
//      analyzerDebug=true;
}

// the reserved words
tokens {
    AND_OP="and"; 
    BEGIN="begin";
//    BREAK="break";
    CASE="case"; 
    COMMON="common";
    COMPILE_OPT="compile_opt";
//    CONTINUE="continue";
    DO="do";
    ELSE="else";
    END="end";
    ENDCASE="endcase";
    ENDELSE="endelse";
    ENDFOR="endfor";
    ENDFOREACH="endforeach";
    ENDIF="endif";
    ENDREP="endrep";
    ENDSWITCH="endswitch";
    ENDWHILE="endwhile";
    EQ_OP="eq";
    FOR="for";
    FOREACH="foreach";
    FORWARD="forward_function";
    FUNCTION="function";
    GE_OP="ge";
    GOTO="goto";
    GT_OP="gt";
    IF="if";
    INHERITS="inherits";
    LE_OP="le";
    LT_OP="lt";
    MOD_OP="mod";
    NE_OP="ne";
    NOT_OP="not";
    OF="of";
    ON_IOERROR="on_ioerror";
    OR_OP="or";
    PRO="pro";
    REPEAT="repeat";
//    RETURN="return";
    SWITCH="switch";
    THEN="then";
    UNTIL="until";
    WHILE="while";
    XOR_OP="xor";
}
{
  // Stuff for include files (@filename)
  private:
    std::unique_ptr<std::ifstream>    inputFile; // stores ifsteam* and deletes 
                                     // it when it is deleted itself
    antlr::TokenStreamSelector*     selector; 
    GDLLexer*                       mainLexerPtr;
    GDLParser*                      parserPtr;

    int                             lineContinuation;

  public:
    ~GDLLexer() 
    {
        if( mainLexerPtr != this)
            selector->pop(); // return to old lexer/stream
        else
        {
            delete parserPtr;
            delete selector;
        }
    }

    // main lexer constructor
    GDLLexer( std::istream& in, const std::string &f, unsigned int compileOptIn,
        const std::string &pro="", bool searchForPro=true) 
    : antlr::CharScanner(new antlr::CharBuffer(in),false),
      lineContinuation( 0)
//    : antlr::CharScanner(in)
    {
        setCaseSensitive(false);
        initLiterals();
  
        selector=     new antlr::TokenStreamSelector();
        mainLexerPtr= this;
        parserPtr=    new GDLParser( *selector, pro, searchForPro, compileOptIn);

        parserPtr->setFilename(f);
        parserPtr->initializeASTFactory( DNodeFactory);
        parserPtr->setASTFactory( &DNodeFactory );
//        parserPtr->setASTNodeFactory( DNode::factory );
        
        selector->addInputStream(this, f);
        selector->select(f); // start with main lexer
        
        // set line number to 0 in interactive mode
        if( f == "")
            { 
                setLine(0);
            }
//        p=parserPtr;
    }

    // sublexer constructor
    GDLLexer( std::ifstream& in, const std::string& name,
        GDLLexer* parent)
    : antlr::CharScanner(new antlr::CharBuffer(in),false),
      inputFile( &in)
    //    : antlr::CharScanner(new antlr::CharInputBuffer(in))
    //    : antlr::CharScanner(new antlr::CharBuffer(in))
    {
        setCaseSensitive(false);
        initLiterals();
        
        selector=     parent->selector;
        mainLexerPtr= parent->mainLexerPtr;
        parserPtr=    parent->parserPtr;
        
//        inputFile.Reset( &in); // make sure file 
//                               // gets deleted (closed) 
//                                    // when lexer finish

        // make sure errors are reported in right file
        setFilename(name);
        parserPtr->setFilename(name);
        selector->push(this);
    }
 
    GDLParser& Parser()
    {
        return *parserPtr;
    }
    
    int LineContinuation()
    {
        int lC = lineContinuation;
        lineContinuation = 0;
        return lC;
    }

  void uponEOF() /*throws TokenStreamException, CharStreamException*/ 
  {
  if ( selector->getCurrentStream() != mainLexerPtr ) {
  //if( this != mainLexerPtr ) {
      
      // make copy as we delete 'this'
      antlr::TokenStreamSelector* sel=selector; 

      // make sure errors are reported in right file
      parserPtr->setFilename(
        static_cast<GDLLexer*>(selector->getCurrentStream())->getFilename());

 //GD: see issue #1632 -- deletion must be here and not before previous line!           
      // here 'this' is deleted (pops selector)
      delete sel->getCurrentStream();

      // don't allow EOF until main lexer.  Force the
      // selector to retry for another token.
      sel->retry();
    }    
  }
}

protected
STRING
    : ( ~('\n' | '\r' ))*
    ;

INCLUDE!
      :    '@' f:STRING
        {
        ANTLR_USING_NAMESPACE(std)
        // create lexer to handle include
        std::string name = f->getText();

        // find comments on the same line
        size_t pos = name.find_first_of(';', 0);   
        if( pos != std::string::npos) // remove them  
            name = name.substr(0, pos);

          StrTrim(name);

          std::string appName=name;
          AppendIfNeeded(appName,".pro");

        errno = 0; // zero it to detect errors

        bool found = CompleteFileName( appName);
        if( found) 
            name = appName;
        else
            found = CompleteFileName( name);
            
        if( !found)
            {
                if( errno == EMFILE)
                    throw GDLException( "Too many open files "
                    "(recursive use of '@'?): " + name);
                else 
                    throw GDLException( "File not found: " + name);
           }

        std::ifstream* input = new std::ifstream(name.c_str());
        if (!*input) 
            {
              delete input;
            throw GDLException( "Error opening file. File: " + name);
              cerr << SysVar::MsgPrefix() << "Error opening file. File: " << name << endl;
            }

          if( *input) 
              {
            new GDLLexer(*input,name,this);
            selector->retry(); // throws TokenStreamRetryException
            }
        }
    ;

AND_OP_EQ: { LA(4) == '='}? "and="; 
ASTERIX_EQ:"*=";
EQ_OP_EQ:"eq=";
GE_OP_EQ:"ge=";
GTMARK_EQ:">=";
GT_OP_EQ:"gt=";
LE_OP_EQ:"le=";
LTMARK_EQ:"<=";
LT_OP_EQ:"lt=";
MATRIX_OP1_EQ:"#=";
MATRIX_OP2_EQ:"##=";
MINUS_EQ:"-=";
MOD_OP_EQ: { LA(4) == '='}? "mod=";
NE_OP_EQ:"ne=";
OR_OP_EQ:"or=";
PLUS_EQ:"+=";
POW_EQ:"^=";
SLASH_EQ:"/=";
XOR_OP_EQ: { LA(4) == '='}? "xor=";

MATRIX_OP1:'#';
MATRIX_OP2:"##";
METHOD:"::";
MEMBER:"->";
COMMA:',';
COLON:':';
EQUAL:'=';
LCURLY:'{';
RCURLY:'}';
LSQUARE:'[';
RSQUARE:']';
LBRACE:'(';
RBRACE:')';
QUESTION:'?';
EXCLAMATION:'!';
POW:'^';
ASTERIX:'*';
SLASH:'/';
MINUS:'-';
PLUS:'+';
INC:"++";
DEC:"--";
GTMARK:'>';
LTMARK:'<';
LOG_AND:"&&";
LOG_OR:"||";
LOG_NEG:'~';

protected
END_U:;

protected
EOL
    :     ( ("\r\n")=> "\r\n" // WINDOOF
//        | ("\n\r")=> "\n\r" // ???    
        | '\n'               // Unix
        | '\r'               // macintosh
        ) { newline(); }
    ;

protected     
W
//    : ( '\003'..'\010' | '\t' | '\r' | '\013' | '\f' | '\016'.. '\037' | ' ' )
    : (' ' | '\t' | '\014') // 014=FF 
    ;

protected
D
    : ('0'..'9')
    ;

protected
L
    : ('a'..'z'|'_')
    ;

protected
H 
    : ('a'..'f'|'0'..'9')
    ;

protected
O
    : ('0'..'7')
    ;
protected
B
    : ('0'..'1')
    ;

protected
EXP
    : ('e' (('+'|'-')? (D)+)? )
    ;

protected 
DBL_E
    : 'd' { $setText( "E");}
    ;

protected
DBL
    : (DBL_E (('+'|'-')? (D)+)? )
    ;

protected
CONSTANT_HEX_BYTE:;
protected
CONSTANT_HEX_LONG:;
protected
CONSTANT_HEX_LONG64:;
protected
CONSTANT_HEX_I:; // integer or larger
protected
CONSTANT_HEX_INT:;
protected
CONSTANT_HEX_ULONG:;
protected
CONSTANT_HEX_ULONG64:;
protected
CONSTANT_HEX_UI:;
protected
CONSTANT_HEX_UINT:;
protected
CONSTANT_BYTE:;
protected
CONSTANT_LONG:;
protected
CONSTANT_LONG64:;
protected
CONSTANT_I:; // integer or larger if necessary
protected
CONSTANT_INT:;
protected
CONSTANT_ULONG:;
protected
CONSTANT_ULONG64:;
protected
CONSTANT_UI:;
protected
CONSTANT_UINT:;
protected
CONSTANT_OCT_BYTE:;
protected
CONSTANT_OCT_LONG:;
protected
CONSTANT_OCT_LONG64:;
protected
CONSTANT_OCT_I:; // integer or larger if necessary
protected
CONSTANT_OCT_INT:;
protected
CONSTANT_OCT_ULONG:;
protected
CONSTANT_OCT_ULONG64:;
protected
CONSTANT_OCT_UI:;
protected
CONSTANT_OCT_UINT:;
protected
CONSTANT_FLOAT:;
protected
CONSTANT_DOUBLE:;
protected
STRING_LITERAL:;
protected
DOT:;

CONSTANT_OR_STRING_LITERAL
    // returns everything 'cleaned', ready to use
    // could be a string, but octals have priority
    // but "012345" is a string because of ending \"
    :
        ("0x"(H)+ ( 's' | 'l' | 'u' )?) =>  //NOT 'b' as B is part of (H) : ex: 0x3BAFB 
      ("0x"! (H)+          { _ttype=CONSTANT_HEX_I; } // DEFINT32
            ( 's'!        { _ttype=CONSTANT_HEX_INT; }
        | 'u'!        { _ttype=CONSTANT_HEX_UI; }   // DEFINT32
        | "us"!        { _ttype=CONSTANT_HEX_UINT; } 
        | "ub"!        { _ttype=CONSTANT_HEX_BYTE; }
        | 'l'!            { _ttype=CONSTANT_HEX_LONG; }
        | "ll"!        { _ttype=CONSTANT_HEX_LONG64; }
        | "ul"!           { _ttype=CONSTANT_HEX_ULONG; }
        | "ull"!    { _ttype=CONSTANT_HEX_ULONG64; }
            )?)
    |('\"'(O)+ ( 'b' | 's' | 'l' | 'u' | "\"")?) => 
        ('\"'! (O)+        { _ttype=CONSTANT_OCT_I; }  // DEFINT32
            ( 's'!        { _ttype=CONSTANT_OCT_INT; }
            | 'b'!        { _ttype=CONSTANT_OCT_BYTE; }
            | 'u'!         { _ttype=CONSTANT_OCT_UI; }   // DEFINT32
            | "us"!        { _ttype=CONSTANT_OCT_UINT; } 
            | "ub"!        { _ttype=CONSTANT_OCT_BYTE; }
            | 'l'!         { _ttype=CONSTANT_OCT_LONG; }
            | "ll"!        { _ttype=CONSTANT_OCT_LONG64; }
            | "ul"!        { _ttype=CONSTANT_OCT_ULONG; }
            | "ull"!    { _ttype=CONSTANT_OCT_ULONG64; }
            | "\""!            { _ttype=STRING_LITERAL; }
            )?)
    | ('\''(H)+'\'' ( 'x' | "xs" | "xb" | "xl" | "xu" | "xus" | "xub" | "xul" )) =>
        ('\''! (H)+ '\''! 'x'!
          (                  { _ttype=CONSTANT_HEX_I; } // DEFINT32
            | 's'!        { _ttype=CONSTANT_HEX_INT; }
            | 'b'!        { _ttype=CONSTANT_HEX_BYTE; }
            | 'u'!        { _ttype=CONSTANT_HEX_UI; }   // DEFINT32
            | "us"!        { _ttype=CONSTANT_HEX_UINT; } 
                    | "ub"!        { _ttype=CONSTANT_HEX_BYTE; }
            | 'l'!            { _ttype=CONSTANT_HEX_LONG; }
            | "ll"!        { _ttype=CONSTANT_HEX_LONG64; }
            | "ul"!           { _ttype=CONSTANT_HEX_ULONG; }
              | "ull"!    { _ttype=CONSTANT_HEX_ULONG64; }
            ))
    | ('\''(O)+'\''    ( 'o' | "os" | "ol" | "ou" | "oul")) =>
        ('\''! (O)+ '\''! 'o'!
            (           { _ttype=CONSTANT_OCT_I; } // DEFINT32
            | 's'!         { _ttype=CONSTANT_OCT_INT; }
            | 'b'!         { _ttype=CONSTANT_OCT_BYTE; }
            | 'u'!         { _ttype=CONSTANT_OCT_UI; }   // DEFINT32
            | "us"!        { _ttype=CONSTANT_OCT_UINT; } 
            | "ub"!        { _ttype=CONSTANT_OCT_BYTE; }
            | 'l'!         { _ttype=CONSTANT_OCT_LONG; }
            | "ll"!     { _ttype=CONSTANT_OCT_LONG64; }
            | "ul"!        { _ttype=CONSTANT_OCT_ULONG; }
            | "ull"!    { _ttype=CONSTANT_OCT_ULONG64; }
            ))
    | ('\''(B)+'\''    ( 'b' | "bs" | "bl" | "bu" | "bul" )) =>
        ('\''! (B)+ '\''! 'b'!
            (           { _ttype=CONSTANT_BIN_I; } // DEFINT32
            | 's'!         { _ttype=CONSTANT_BIN_INT; }
            | 'b'!         { _ttype=CONSTANT_BIN_BYTE; }
            | 'u'!         { _ttype=CONSTANT_BIN_UI; }   // DEFINT32
            | "us"!        { _ttype=CONSTANT_BIN_UINT; } 
            | "ub"!        { _ttype=CONSTANT_BIN_BYTE; }
            | 'l'!         { _ttype=CONSTANT_BIN_LONG; }
            | "ll"!         { _ttype=CONSTANT_BIN_LONG64; }
            | "ul"!        { _ttype=CONSTANT_BIN_ULONG; }
            | "ull"!    { _ttype=CONSTANT_BIN_ULONG64; }
            ))
    // strings in the original do not need trailing " or '    
    | '\"'! (~('\"'|'\r'|'\n')| '\"' '\"'! )* 
        ( '\"'!
        |         
        )                  { _ttype=STRING_LITERAL; }
    | '\''! (~('\''|'\r'|'\n')| '\'' '\''!  )* 
        ( '\''!
        |         
        )                  { _ttype=STRING_LITERAL; }
    | (((D)+ (DBL | '.'(D)*(DBL))) | '.'(D)+(DBL)) =>
        (
            (
                (D)+ 
                ( DBL 
                | '.'(D)*(DBL)
                )
            ) 
        | '.'(D)+(DBL)) 
                        { _ttype=CONSTANT_DOUBLE; }
    | (((D)+ (EXP | '.'(D)*(EXP)?)) | '.'(D)+(EXP)?) =>
        (
            (
                (D)+ 
                ( EXP 
                | '.'(D)*(EXP)?
                )
            ) 
        | '.'(D)+(EXP)?) 
        { _ttype=CONSTANT_FLOAT; }
    | '.'                 { _ttype=DOT; }
    | (D)+                   { _ttype=CONSTANT_I; }
        ( 's'!          { _ttype=CONSTANT_INT; }
        | 'b'!            { _ttype=CONSTANT_BYTE; }
        | 'u'!('s'!)?    { _ttype=CONSTANT_UINT; }
        | "ub"!            { _ttype=CONSTANT_BYTE; }
        | 'l'!            { _ttype=CONSTANT_LONG; }
        | "ll"!            { _ttype=CONSTANT_LONG64; }
        | "ul"!            { _ttype=CONSTANT_ULONG; }
        | "ull"!           { _ttype=CONSTANT_ULONG64; }
        )?    
    ;    

COMMENT
  : ';' (options {greedy=true;}: ~('\r'|'\n'))* {_ttype=antlr::Token::SKIP;}
  ;

// look here for reserved words
IDENTIFIER
options
{
    testLiterals = true;
}
    : (L)(L|D|'$')*
    { 
      std::string s=StrUpCase( $getText);
      $setText( s); 
    }
    ;

SYSVARNAME
    : ('!') (L|D|'$')+
    { 
      std::string s=StrUpCase( $getText);
      $setText( s); 
    }
    ;

END_MARKER
  : '&' { _ttype=END_U; }
  ;

WHITESPACE
  : (W)+
    { _ttype=antlr::Token::SKIP; }
  ;

// this subrule eats lines to skip
// 1. comment only lines
// 2. blank lines
protected
SKIP_LINES
  : ( COMMENT
    | W
    | EOL
    )*
  ;

// IDL ignores everything on the line after the $
CONT_STATEMENT
    : '$' (~('\r'|'\n'))* EOL
        SKIP_LINES
        { 
            ++lineContinuation;
            _ttype=antlr::Token::SKIP; 
        }
    ;

END_OF_LINE 
  : EOL
    SKIP_LINES
    { _ttype=END_U; }
  ;

// just to know how many tokens are there
protected
MAX_TOKEN_NUMBER :;
