/* *************************************************************************
                                gdlc.tree.g 
the GDL tree parser
used after the lexer/parser (gdlc.g)
calls the compiler (dcompiler.cpp)
put out trees suitable to be interpreted (gdlc.i.g)
                             -------------------
    begin                : July 22 2002
    copyright            : (C) 2002 by Marc Schellens
    email                : m_schellens@hotmail.com
 ***************************************************************************/

/* *************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

// possible source of errors:
// #id and id (as label) are not the same because tree generation is ON
// #id refers to the generated (output) tree id to the input tree

header "pre_include_cpp" {
#include "includefirst.hpp"
}

header "post_include_cpp" {
#include <memory>

#include <cstdlib>

// ****
#include "print_tree.hpp"

using namespace std;

// print out AST tree
//#define GDL_DEBUG
//#undef GDL_DEBUG

}

header {
#include "objects.hpp"
#include "dcompiler.hpp"
#include "dnodefactory.hpp"
#include "arrayindexlistt.hpp"
}

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 TreeParser  ****************************************
class GDLTreeParser extends TreeParser;

options {
	importVocab = GDL;	// use vocab generated by lexer
	buildAST = true;
  	ASTLabelType = "RefDNode";
//    defaultErrorHandler = true;
    defaultErrorHandler = false;
}
{
    private:
//    ProgNodeP       returnProgNodeP;
    
    DCompiler       comp; // each tree parser has its own compiler
    
    IDList          loopVarStack;
    
    bool LoopVar( RefDNode& lN)
    {
        int lT = lN->getType();
        if( lT == VAR || lT == VARPTR)
        return (FindInIDList( loopVarStack, lN->getText()) != -1);
        return false;
    }

public:    
    static bool IsREF_CHECK(int t)
    {
        return    t == FCALL_LIB 
            // || t == MFCALL_LIB  
            // //t == FCALL_LIB_N_ELEMENTS ||
            // || t == MFCALL_PARENT_LIB 
            || t == QUESTION 
            // TODO: These are ref check as well, but parameter nodes need to know
            || t == FCALL || t == MFCALL || t == MFCALL_PARENT
            || t == ARRAYEXPR_FCALL
            || t == ARRAYEXPR_MFCALL
            ;
    }

private:
    // Replaces ASSIGN with ASSIGN_REPLACE if appropiate
    void AssignReplace( RefDNode& lN, RefDNode& aN)
    {
        if( LoopVar( lN))
            {
                Warning( "Warning: Assignment to loop variable detected.");
            }
        int lT = lN->getType();
        if( lT == FCALL || lT == MFCALL || lT == MFCALL_PARENT ||
            lT == FCALL_LIB ||
//            lT == FCALL_LIB_N_ELEMENTS ||
            lT == FCALL_LIB_RETNEW || 
            lT == FCALL_LIB_DIRECT || 
            lT == MFCALL_LIB || 
            lT == MFCALL_LIB_RETNEW || 
            lT == MFCALL_PARENT_LIB ||
            lT == MFCALL_PARENT_LIB_RETNEW ||
            lT == DEREF || lT == VAR || lT == VARPTR 
            )
        {
            aN->setType( ASSIGN_REPLACE);
            aN->setText( "r=");
        }
        if( lT == ARRAYEXPR_MFCALL) // is MFCALL or DOT 
        {
            aN->setType( ASSIGN_ARRAYEXPR_MFCALL);
            aN->setText( "?=");
        }

    }
    
    bool SelfAssignment( RefDNode& lN, RefDNode& rN)
    {
        int lT = lN->getType();
        int rT = rN->getType();
        if( 
            rT == VARPTR && lT == VARPTR)
        {
            return  (lN->GetVar() == rN->GetVar());
        }
        if( 
            rT == VAR && lT == VAR)
        {
            return  (lN->GetVarIx() == rN->GetVarIx());
        }
        return false;
        
    }

  RefDNode RemoveNextSibling( RefDNode l)
  {
    RefDNode newNode = RefDNode(astFactory->dupTree( antlr::RefAST(l)));
    newNode->SetLine( l->getLine());
    return newNode;
//    newNode->setNextSibling( RefDNode(antlr::nullAST));
  }
  public:
  // constructor with processed file
  GDLTreeParser(const std::string& f, const std::string& sub)
    : antlr::TreeParser(), comp(f, NULL, sub)
    {
        //       setTokenNames(_tokenNames);
        //       setASTNodeFactory( DNode::factory );
        initializeASTFactory( DNodeFactory);
        setASTFactory( &DNodeFactory );
    }
  // constructor for command line/execute
  GDLTreeParser( EnvBaseT* env)
    : antlr::TreeParser(), comp( "", env, "")
    {
        initializeASTFactory( DNodeFactory);
        setASTFactory( &DNodeFactory );
    }

  bool ActiveProCompiled() const { return comp.ActiveProCompiled();} 
  int NCompileErrors() const { return comp.NCompileErrors();} 
}

// file parsing
translation_unit
{
//    returnProgNodeP = _t;
    bool mainStarted = false;
}
	: (   options {greedy=true;}: procedure_def
    | function_def
    | forward_function
    |   {
            EnvBaseT* envBefore = comp.GetEnv();
            if( !mainStarted)
                {
                    comp.StartPro( "$MAIN$");                        
                    mainStarted = true;
                }
            comp.ContinueMainPro();
        }
        common_block
        {
            comp.EndInteractiveStatement();
            comp.SetEnv( envBefore);
        }
    )*
    
    (
        {
            if( !mainStarted)
                {
                    comp.StartPro( "$MAIN$");                        
                    mainStarted = true;
                }
            comp.ContinueMainPro();
        }
        retAST:statement_list
        {
            comp.SetTree( #retAST);
            comp.EndPro();

#ifdef GDL_DEBUG
  cout << "TreeParser output:" << endl;
  antlr::print_tree pt;
  pt.pr_tree(static_cast<antlr::RefAST>(retAST));
  cout << "CompileFile: TreeParser end." << endl;
#endif
           
        }
    )?

        // optional main program
//         ( main_program)?
        
        exception 
        catch [ GDLException& e] 
        { 
            throw;
        }
        catch [ antlr::NoViableAltException& e] 
        {
            // SYNTAX ERROR
            throw GDLException( e.getLine(), e.getColumn(), "Compiler syntax error: "+e.getMessage());
        }
        catch [ antlr::RecognitionException& e] 
        {
            // SYNTAX ERROR
            throw GDLException( e.getLine(), e.getColumn(), "General syntax error: "+e.getMessage());
        }
	;

// intercative usage
interactive
    : (statement { comp.EndInteractiveStatement();})+
        exception 
        catch [ GDLException& e] 
        { 
            throw;
        }
        catch [ antlr::NoViableAltException& e] 
        {
            // SYNTAX ERROR
            throw GDLException( e.getLine(), e.getColumn(), "Compiler syntax error: "+e.getMessage());
        }
        catch [ antlr::RecognitionException& e] 
        {
            // SYNTAX ERROR
            throw GDLException( e.getLine(), e.getColumn(), "General syntax error: "+e.getMessage());
        }
  ;


forward_function!
	: #(FORWARD 
	  (id:IDENTIFIER
		{
		  comp.ForwardFunction(id->getText());
		}	
	  )+
	)
	;

parameter_declaration!
  : #(PARADECL 
	  (id:IDENTIFIER 
		{
		  comp.AddPar(id->getText());
		}
	  | keyword_declaration
	  )+
	)
  ;

keyword_declaration!
	: #(KEYDECL key:IDENTIFIER val:IDENTIFIER)
	{
	  comp.AddKey(key->getText(),val->getText());
	}
	;

procedure_def!
    : #(p:PRO 
            name:IDENTIFIER
            (METHOD obj:IDENTIFIER
                {
                    comp.StartPro(name->getText(),#p->GetCompileOpt(),obj->getText());
                }
            |
                {
                    comp.StartPro(name->getText(),#p->GetCompileOpt());
                }
            ) 
            (parameter_declaration)?
            (statement_list
                {      
                    comp.SetTree( returnAST);
                }
            )?
            {
                if( NCompileErrors() > 0)
                    throw GDLException( i2s(NCompileErrors()) + " compilation error(s) in module " + name->getText() + ".");
               
                comp.EndPro();
            }
        )
    ;

function_def!
    : #(f:FUNCTION 
            name:IDENTIFIER
            (METHOD obj:IDENTIFIER
                {
                    comp.StartFun(name->getText(),#f->GetCompileOpt(),obj->getText());
                }
            |
                {
                    comp.StartFun(name->getText(),#f->GetCompileOpt());
                }
            ) 
            (parameter_declaration)? 
            (statement_list
                {
                    comp.SetTree( returnAST);
                }
            )?
            {
                if( NCompileErrors() > 0)
                    throw GDLException( i2s(NCompileErrors()) + " compilation error(s) in module " + name->getText() + ".");
               
                comp.EndFun();
            }
        )
    ;

common_block!//
  : #(COMMONDEF id:IDENTIFIER
	  {
		DCommonBase* actCommon=comp.CommonDef(id->getText());
	  }
	  (	cv:IDENTIFIER
		{
		  comp.CommonVar(actCommon,cv->getText());
		}
	  )+		
	)
  | #(COMMONDECL id2:IDENTIFIER
	  {
		comp.CommonDecl(id2->getText());
	  }
	)
  ;		

// more than one ELSE is allowed: first is executed, *all*
// (including expr) later branches are ignored
// labeled_expr
//     : ex:expr
//         {
//          if( #ex->getType() != EXPR)   
//             #labeled_expr = #([EXPR, "expr"],#labeled_expr);
//         }
//     ;


caseswitch_body 
	: #(b:BLOCK { #b->setType(CSBLOCK);#b->setText("csblock");} expr //labeled_expr 
            (statement_list)? 
        )
	| #(ELSEBLK 
            (statement_list)?
        )
	;	

switch_statement
{
    int labelStart = comp.NDefLabel();
}
	: #(s:SWITCH expr (caseswitch_body)*)
        {
            #s->SetLabelRange( labelStart, comp.NDefLabel());
        }
	;

case_statement
{
    int labelStart = comp.NDefLabel();
}
	: #(c:CASE expr (caseswitch_body)*)
        {
            #c->SetLabelRange( labelStart, comp.NDefLabel());
        }
	;

block
{
//    int labelStart = comp.NDefLabel();
}
	: #(b:BLOCK (statement_list)?)
        {
//            #b->SetLabelRange( labelStart, comp.NDefLabel());
        }
	;

unblock!
	: #(b:BLOCK 
            ( s:statement_list { #unblock = #s;}
            | { #unblock = #b;}
            )
        )
	;

unblock_empty!
	: #(b:BLOCK 
            ( s:statement_list { #unblock_empty = #s;}
            | 
            )
        )
	;

statement_list // note: proper syntax is provided already by the parser
    : ( statement | label )+
    ;

statement
    : assign_expr_statement
//    | assign_expr
    | comp_assign_expr   
	| procedure_call
	| for_statement
	| foreach_statement  
	| repeat_statement
	| while_statement
	| jump_statement
	| if_statement
	| case_statement
	| switch_statement
    | forward_function
	| common_block
	| block
    | #(DEC expr) //unbrace_expr)
    | #(INC expr) //unbrace_expr)
	| BREAK    // only in loops or case/switch_statement
	| CONTINUE // only in loops
	;

repeat_statement!
{
    int labelStart = comp.NDefLabel();
}
	: #(r:REPEAT b:unblock_empty e:expr)
        {
            #r->SetLabelRange( labelStart, comp.NDefLabel());
            #repeat_statement=#( r, e, b);
            if( #b == static_cast<RefDNode>(antlr::nullAST))
            Warning( "Warning: Empty REPEAT UNTIL loop detected.");
        }
	;

while_statement!
{
    int labelStart = comp.NDefLabel();
}
	: #(w:WHILE e:expr s:statement)
        {
            #w->SetLabelRange( labelStart, comp.NDefLabel());

            // swap e <-> s for easier access in interpreter
            #while_statement=#( w, e, s);
        }
	;

// for_block!
// : u:unblock
// {
//     #for_block=#( u, [ENDFOR,"endfor"]);
// }
// ;


for_statement //!
{
    StackSizeGuard<IDList> guard( loopVarStack);
    int labelStart = comp.NDefLabel();
}
	: #(f:FOR i:IDENTIFIER 
        	{ 
                #i->setType(VAR);
                comp.Var(#i);	

                loopVarStack.push_back(#i->getText());
            }
            expr expr 
            (expr
                { 
                #f->setType(FOR_STEP);
                #f->setText("for_step");
                }
            )? 
//           for_block
           unblock_empty
        )
        {
        #f->SetLabelRange( labelStart, comp.NDefLabel());
        }
	;

foreach_statement //!
{
    StackSizeGuard<IDList> guard( loopVarStack);
    int labelStart = comp.NDefLabel();
}
	: #(f:FOREACH i:IDENTIFIER 
        	{ 
                #i->setType(VAR);
                comp.Var(#i);	
                loopVarStack.push_back(#i->getText());
            }
            expr
            (l:IDENTIFIER
                { 
                #l->setType(VAR);
                comp.Var(#l);	
                loopVarStack.push_back(#l->getText());

                #f->setType(FOREACH_INDEX);
                #f->setText("foreach_index");
                }
            )? 

//           for_block
           unblock_empty
//           unblock
        )
        {
        #f->SetLabelRange( labelStart, comp.NDefLabel());
        }
	;

label!
  : #(i:IDENTIFIER COLON)
	{ 
	  #label=#[LABEL,i->getText()];
      #label->SetLine( i->getLine());
	  comp.Label(#label); 
	}	
  ;

jump_statement!//
  : #(g:GOTO i1:IDENTIFIER)
	{ 
	  #jump_statement=astFactory->create(GOTO,i1->getText());
      #jump_statement->SetLine(g->getLine()); 
//	  #jump_statement=#[GOTO,i1->getText()]; // doesn't work
//	  comp.Goto(#jump_statement); 
	}	
  | #(r:RETURN {bool exprThere=false;} (e:expr {exprThere=true;})?)
	{
	  if( comp.IsFun())
	  	{
		if( !exprThere)	throw GDLException(	r, 
                    "Return statement in functions "
                    "must have 1 value.");

//         // wrong: this is only true for l_function as return value
//         // a ARRAYEXPR_MFCALL can only be a MFCALL here -> change tree
//         if( #e->getType() == ARRAYEXPR_MFCALL)
//             {
//                 #e->setType( MFCALL);
//                 #e->setText( "mfcall");
//                 RefDNode mfc;
//                 mfc = #e->getFirstChild();
//                 mfc = mfc->getNextSibling();
//                 #e->setFirstChild( antlr::RefAST(mfc));             
//             }

		#jump_statement=#([RETF,"retf"],e);
        #jump_statement->SetLine(r->getLine()); 
		}
	  else
	  	{
		if( exprThere) throw GDLException(	_t, 
                    "Return statement in "
                    "procedures cannot have values.");
		#jump_statement=#[RETP,"retp"]; // astFactory.create(RETP,"retp");
      #jump_statement->SetLine(r->getLine()); 
	  	}
	}
  | #(o:ON_IOERROR i2:IDENTIFIER)
	{
      if( i2->getText() == "NULL")
            {
                #jump_statement=astFactory->create(ON_IOERROR_NULL,
                                                   "on_ioerror_null");
      #jump_statement->SetLine(o->getLine()); 
            }
      else
            {
                #jump_statement=astFactory->create(ON_IOERROR,i2->getText());
      #jump_statement->SetLine(o->getLine()); 
//	            #jump_statement=#[ON_IOERROR,i2->getText()];
//              comp.Goto(#jump_statement); // same handling		 
            }
	}
  ;

if_statement!//
{
//     int labelStart = comp.NDefLabel();
    RefDNode block;
}
	: #(i:IF e:expr s1:statement 
            (
                {
//         #i->SetLabelRange( labelStart, comp.NDefLabel());
                #if_statement=#(i,e,s1);
                }
            | s2:statement
                {
                #i->setText( "if_else");
                #i->setType( IF_ELSE);
//         #i->SetLabelRange( labelStart, comp.NDefLabel());
                if( #s1->getType() != BLOCK)
                    {
                        #block = astFactory->create(BLOCK,"block");
                        #block->SetLine( #s1->getLine());
                        #if_statement=#(i,e, #(block, s1), s2);
                    }
                else
                    #if_statement=#(i,e,s1,s2);
                }
            )
        )
//        {
//        #i->SetLabelRange( labelStart, comp.NDefLabel());
//        }
	;

procedure_call
	: #(MPCALL expr IDENTIFIER parameter_def[ false]
        )
	| #(MPCALL_PARENT expr IDENTIFIER
            IDENTIFIER parameter_def[ false]
        )
	| #(p:PCALL id:IDENTIFIER
            {
                // first search library procedures
                int i=LibProIx(#id->getText());
            }
            para:parameter_def[ i != -1 && libProList[ i]->NPar() == -1]
            {
                if( i != -1)
                {
                    int nParam = 0;
                    if( #para != RefDNode(antlr::nullAST))
                        nParam = #para->GetNParam();

                    int libParam = libProList[i]->NPar();
                    int libParamMin = libProList[i]->NParMin();
                    if( libParam != -1 && nParam > libParam)
                        throw GDLException(	p, libProList[i]->Name() + ": Too many arguments.");
                    if( libParam != -1 && nParam < libParamMin)
                        throw GDLException(	p, libProList[i]->Name() + ": Too few arguments.");

                    #p->setType(PCALL_LIB);
                    #p->setText("pcall_lib");
                    #id->SetLibPro( libProList[i]);
                }
                else
                {
                    // then search user defined procedures
                    i=ProIx(#id->getText());
                    #id->SetProIx(i);
                }
            }
        )
	;	    

parameter_def [bool varNum]
{
 // count positional parameters
 int nKey = 0;
 int nPar = 0;
}

    : 
    ( key_parameter
            {
                ++nKey;
            }
    | pos_parameter[ varNum]
            {
                ++nPar;
            }
    )*
        {
            if( nPar > 0 || nKey > 0)
                {
                    RefDNode(currentAST.root)->SetNParam( nPar);
                }
        }
    ;

key_parameter!//
{
    RefDNode variable;
}
	: #(d:KEYDEF i:IDENTIFIER k:expr //unbrace_expr
            {
                variable=comp.ByReference(#k);
                if( variable != static_cast<RefDNode>(antlr::nullAST))
                {
                    int vT = variable->getType();
                    if( IsREF_CHECK(vT))
                    {
                        #d=#[KEYDEF_REF_CHECK,"keydef_ref_check"];
                        #key_parameter=#(d,i,k);
                    }
                    else if( variable == #k)
                    {
                        #d=#[KEYDEF_REF,"keydef_ref"];
                        #key_parameter=#(d,i,variable);
                    }
                    else
                    {
                        #d=#[KEYDEF_REF_EXPR,"keydef_ref_expr"];
                        #key_parameter=#(d,i,k,variable);
                    }
                }
                else 
                {
                    int t = #k->getType();
                    // Note: Right now there are no MFCALL_LIB or MFCALL_PARENT_LIB nodes
                    if( IsREF_CHECK(t))
//                            t  == FCALL_LIB 
//                         || t == MFCALL_LIB  // || t == FCALL_LIB_N_ELEMENTS 
//                         || t == MFCALL_PARENT_LIB  
//                         || t == QUESTION 
//                         || t == FCALL || t == MFCALL || t == MFCALL_PARENT
//                         || t == ARRAYEXPR_FCALL
//                         || t == ARRAYEXPR_MFCALL
// //                 t == FCALL_LIB_RETNEW || t == MFCALL_LIB_RETNEW || 
// //                 t == MFCALL_PARENT_LIB_RETNEW //||
// //                 t == ARRARYEXPR_MFCALL_LIB // MFCALL_LIB or VAR or DEREF 
                      // // )
                    {
                        #d=#[KEYDEF_REF_CHECK,"keydef_ref_check"];
                        #key_parameter=#(d,i,k);
                    }
//                     else if( t == FCALL_LIB_RETNEW || t == MFCALL_LIB_RETNEW) 
//                     {
// //                         #d=#[KEYDEF_REF,"keydef_ref"];
//                         #key_parameter=#(d,i,k);
//                     }
                    else
                    {
                        #key_parameter=#(d,i,k);
                    }
                }
            }
        )
    ;

pos_parameter! [bool varNum] // varNum: is variable number of parameters subroutine (e. g. PRINT)
{
    RefDNode variable;
}
	: e:expr //unbrace_expr
        {
            variable=comp.ByReference(#e);
            if( variable != static_cast<RefDNode>(antlr::nullAST))
            {
                    int vT = variable->getType();
                    if( IsREF_CHECK(vT))
                    {
                        if( varNum)
                            #pos_parameter=#([REF_CHECK_VN,"ref_check_vn"],e);
                        else
                            #pos_parameter=#([REF_CHECK,"ref_check"],e);
                    }
                    else if( variable == #e)
                    {
                        if( varNum)
                            #pos_parameter=#([REF_VN,"ref_vn"],variable);
                        else
                            #pos_parameter=#([REF,"ref"],variable);
                    }
                    else
                    {
                        if( varNum)
                            #pos_parameter=#([REF_EXPR_VN,"ref_expr_vn"],e,variable);
                        else
                            #pos_parameter=#([REF_EXPR,"ref_expr"],e,variable);
                    }
            }
            else 
            {
                int t = #e->getType();
                // Note: Right now there are no MFCALL_LIB or MFCALL_PARENT_LIB nodes
                if( IsREF_CHECK(t))
//                 if( t == FCALL_LIB 
//                     || t == MFCALL_LIB  //t == FCALL_LIB_N_ELEMENTS ||
//                     || t == MFCALL_PARENT_LIB 
//                     || t == QUESTION 
// // TODO: These are ref check as well, but parameter nodes need to know
//                     || t == FCALL || t == MFCALL || t == MFCALL_PARENT
//                     || t == ARRAYEXPR_FCALL
//                     || t == ARRAYEXPR_MFCALL
// //                      t == FCALL_LIB_RETNEW || t == MFCALL_LIB_RETNEW || 
// //                      t == MFCALL_PARENT_LIB_RETNEW
// //                      t == ARRARYEXPR_MFCALL_LIB // MFCALL_LIB or VAR or DEREF 
//                     ) 
                {
                    // something like: CALLAPRO,reform(a,/OVERWRITE)
                    if( varNum)
                        #pos_parameter=#([REF_CHECK_VN,"ref_check_vn"],e);
                    else
                        #pos_parameter=#([REF_CHECK,"ref_check"],e);
                }
                else
                {
                    if( varNum)
                        #pos_parameter= #([PARAEXPR_VN,"paraexpr_vn"], e);
                    else
                        #pos_parameter= #([PARAEXPR,"paraexpr"], e);
                }
            }
        }
	;

// counts the [[[ ]]]
// 0 -> [ ]   (add dim if scalar)
// 1 -> [[ ]] ...
array_def returns [int depth]
{
    RefDNode sPos;
}
	: #(a:ARRAYDEF {sPos=_t;} (/*e:*/expr {/*check for constant expression*/})*)
        {
            // better check here as then CONSTANT is propagated
            depth=0;
//             bool constant = true;
//             bool depth0 = false;
            for( RefDNode e=sPos; 
                e != static_cast<RefDNode>(antlr::nullAST);
                e=e->getNextSibling())
            {
//                 if( e->getType() != CONSTANT)
//                     constant = false;
//                 if( !depth0)
                if( (e->getType() != ARRAYDEF) && (e->getType() != ARRAYDEF_CONST))
                {
                    depth=0;
                    break;
//                     depth0 = true;
                }
                else
                {
                    int act=array_def(e); // recursive call
                    act=act+1;
                    if( depth == 0)
                    {
                        depth=act;
                    }
                    else
                    {
                        if( depth > act) depth=act;
                    }
                }   
            }
            #a->SetArrayDepth(depth);
        }
	| #(aa:ARRAYDEF_CONST {sPos=_t;} (expr)*)
        {
            depth=0;
            for( RefDNode e=sPos; 
                e != static_cast<RefDNode>(antlr::nullAST);
                e=e->getNextSibling())
            {
                if( (e->getType() != ARRAYDEF) && (e->getType() != ARRAYDEF_CONST))
                {
                    depth=0;
                    break;
                }
                else
                {
                    int act=array_def(e); // recursive call
                    act=act+1;
                    if( depth == 0)
                    {
                        depth=act;
                    }
                    else
                    {
                        if( depth > act) depth=act;
                    }
                }   
            }
            #aa->SetArrayDepth(depth);
        }
	;

struct_def
{
    bool noTagName = false;
}
	: #(n:NSTRUC_REF // parser delivers always nstruct_ref 
            IDENTIFIER 
            ((expr {noTagName = true;} | IDENTIFIER expr | INHERITS IDENTIFIER)+
                {   
                    // set to nstruct if defined here
                    #n->setType(NSTRUC); 
                    #n->setText("nstruct");
                    #n->DefinedStruct( noTagName);
                }
            )?
        )
	| #(STRUC (tag_def)+)
	;

tag_def
	: IDENTIFIER expr
	;	

arrayindex! [ArrayIndexVectorT* ixList]
{
    BaseGDL *c1, *c2, *c3, *c4;
}
	: ( #(ARRAYIX  
                ( ALL
                     { 
                         ixList->push_back( new ArrayIndexAll());
                     }
                | ( e1:expr // 0 or 2
                {
                    // in ConstantIndex the cData of all nodes is stolen
                    // (in case it succeeds)
                    // therefore we build here the new constant node with
                    // proper cData. See below (e2)
                    c1 = comp.ConstantIndex( #e1);
                    if( c1 != NULL)
                        {    
                            int e1Line = #e1->getLine();
                            #e1 = #[CONSTANT,"CONST_IX"];
                            #e1->ResetCData( c1);
                            #e1->SetLine( e1Line);
                        }
                }
                    ( // empty  
                    {
                        bool    constantOK = false;

                        if( c1 != NULL)
                            {    
                                DType dType = c1->Type();
                                int typeCheck = DTypeOrder[ dType];
                                if( dType == GDL_STRING || typeCheck >= 100)
                                    {
                                        //delete c1;
                                    }
                                else
                                    {

                                        try {
                                            // ATTENTION: These two grab c1 (all others don't)
                                            // a bit unclean, but for maximum efficiency
                                            if( c1->Rank() == 0)
                                                ixList->push_back( new CArrayIndexScalar( c1));
                                            else
                                                ixList->push_back( new CArrayIndexIndexed( c1));

                                            // prevent c1 from being deleted
                                            #e1->StealCData(); // ok, as #e1 is not used anymore

                                            constantOK = true;
                                        }
                                        catch( GDLException& e) {
                                            //delete c1; // owned by #e1
                                        }  
                                    }
                            }

                        if( !constantOK)
                            {
                                if( LoopVar( #e1))
                                    {
                                        if( #e1->getType() == VAR)
                                            ixList->push_back( new ArrayIndexScalar( #e1));
                                        else
                                            ixList->push_back( new ArrayIndexScalarVP( #e1));
                                    }
                                else
                                    {
                                        ## = #e1;
                                        ixList->push_back( new ArrayIndexIndexed());
                                    }
                            }
                    }
                        | ALL
                            ( // empty
                            {
                                    //c1 = comp.ConstantIndex( #e1); 
                                    if( c1 != NULL)
                                    {
                                        ixList->push_back( new CArrayIndexORange( c1));
                                    }
                                    else
                                    {
                                        ## = #e1;
                                        ixList->push_back( new ArrayIndexORange());
                                    }
                                }
                            | e2:expr
                {
                    // in ConstantIndex the cData of all nodes is stolen
                    // (in case it succeeds)
                    // therefore we build here the new constant node with
                    // proper cData. This is crucial because if e. g. only e1 but not e2
                    // is constant, #e1 is put to the output tree (with stolen cData) -> crash
                    c2 = comp.ConstantIndex( #e2);
                    if( c2 != NULL)
                        {    
                            int e2Line = #e2->getLine();
                            #e2 = #[CONSTANT,"CONST_IX"];
                            #e2->ResetCData( c2);
                            #e2->SetLine( e2Line);
                        }
                }
                                { 
                                    //c1 = comp.ConstantIndex( #e1); 
                                    //c2 = comp.ConstantIndex( #e2); 
                                    if( c1 != NULL && c2 != NULL)
                                    {
                                        ixList->push_back( new 
                                            CArrayIndexORangeS( c1, c2));
                                    }
                                    else
                                    {
                                        //delete c1;
                                        //delete c2;
                                        ## = #( NULL, e1, e2);
                                        ixList->push_back( new 
                                            ArrayIndexORangeS());
                                    }
                                }
                            )
                        | e3:expr
                {
                    // see above (#e2)
                    c3 = comp.ConstantIndex( #e3);
                    if( c3 != NULL)
                        {    
                            int e3Line = #e3->getLine();
                            #e3 = #[CONSTANT,"CONST_IX"];
                            #e3->ResetCData( c3);
                            #e3->SetLine( e3Line);
                        }
                }
                            ( // empty
                                { 
                                    //c1 = comp.ConstantIndex( #e1); 
                                    //c3 = comp.ConstantIndex( #e3); 
                                    if( c1 != NULL && c3 != NULL)
                                    {
                                        ixList->push_back( new 
                                            CArrayIndexRange( c1, c3));
                                    }
                                    else
                                    {
                                        //delete c1;
                                        //delete c3;
                                        ## = #( NULL, e1, e3);
                                        ixList->push_back( new ArrayIndexRange());
                                    }
                                }
                            | e4:expr
                {
                    // see above (#e2)
                    c4 = comp.ConstantIndex( #e4);
                    if( c4 != NULL)
                        {    
                            int e4Line = #e4->getLine();
                            #e4 = #[CONSTANT,"CONST_IX"];
                            #e4->ResetCData( c4);
                            #e4->SetLine( e4Line);
                        }
                }
                                { 
                                    //c1 = comp.ConstantIndex( #e1); 
                                    //c3 = comp.ConstantIndex( #e3); 
                                    //c4 = comp.ConstantIndex( #e4); 
                                    if( c1 != NULL && c3 != NULL && c4 != NULL)
                                    {
                                        ixList->push_back( new CArrayIndexRangeS( c1, c3, c4));
                                    }
                                    else
                                    {
                                        //delete c1;
                                        //delete c3;
                                        //delete c4;
                                        ## = #( NULL, e1, e3, e4);
                                        ixList->push_back( new ArrayIndexRangeS());
                                    }
                                }
                            )
                        )
                    )
                )
            )
        )
    ;

arrayindex_list
{
//std::auto_ptr< ArrayIndexListT> ixList( new ArrayIndexListT()); // compile_opt
ArrayIndexVectorT* ixList = new ArrayIndexVectorT();
PtrGuard< ArrayIndexVectorT> guard( ixList);
}
	: ( arrayindex[ ixList])+
        {
            #arrayindex_list = #([ARRAYIX,"[...]"], arrayindex_list);

//            ixList->Freeze(); // do all initial one-time settings
//             if( ixList->NDim() == 1)
//                 #arrayindex_list = #([ARRAYIX1,"[ix]"], arrayindex_list);
//             else
//                 {
//                     #arrayindex_list = #([ARRAYIX,"[...]"], arrayindex_list);
//                     ixList->Freeze(); // do all initial one-time settings
//                 }

            guard.Release();
            ArrayIndexListT* arrayIndexNoAssoc;
            ArrayIndexListT* arrayIndex;
            MakeArrayIndex( ixList, &arrayIndex, &arrayIndexNoAssoc);
            delete ixList;
            #arrayindex_list->SetArrayIndexList( arrayIndex, arrayIndexNoAssoc);
        }
	;	

// arrayindex
// 	: #(ax:ARRAYIX 
// 			( ALL 
//                 { 
//                     #ax->setType(ARRAYIX_ALL); // 1
//                     #ax->setText("*");
//                 }
// 			| ( expr // 0 or 2
//                     (   (  !ALL
//                             { 
//                             #ax->setType(ARRAYIX_ORANGE); // 3
//                             #ax->setText("s:*");
//                             }
//                         )
//                         ( expr
//                             { 
//                             #ax->setType(ARRAYIX_ORANGE_S); // 5
//                             #ax->setText("s:*:s");
//                             }
//                         )?
//                       | expr
//                             { 
//                             #ax->setType(ARRAYIX_RANGE); // 4
//                             #ax->setText("s:e");
//                             }
//                         ( expr
//                             { 
//                             #ax->setType(ARRAYIX_RANGE_S); // 6
//                             #ax->setText("s:e:s");
//                             }
//                         )?
//                     )?
//                 )
// 			)
// 		)
// 	;

// removes last pair of braces
// for non functions
lassign_expr!//
	: ex:expr
		{
            // remove last pair of braces
			if( #ex->getType()==EXPR)
            {
//                 int cT = #ex->getFirstChild()->getType();
//                 if( cT != FCALL && 
//                     cT != MFCALL && 
//                     cT != MFCALL_PARENT &&
//                     cT != FCALL_LIB && 
//                     cT != MFCALL_LIB && 
//                     cT != MFCALL_PARENT_LIB)
                        #ex=#ex->getFirstChild();
            }

            if( #ex->getType()==ASSIGN)
            throw GDLException(	_t, "Assign expression is not allowed as "
                                    "l-expression in assignment");

            #lassign_expr= #ex; //#( NULL, ex);
		}
    ;

assign_expr!
	: #(a:ASSIGN l:lassign_expr r:expr)
        {
            if( !SelfAssignment( #l, #r))
            {
                
                AssignReplace( #l, #a);
                
                // int lT = #l->getType();
                // if( lT == FCALL || lT == MFCALL || lT == MFCALL_PARENT ||
                // lT == FCALL_LIB || lT == MFCALL_LIB || lT == MFCALL_PARENT_LIB ||
                // lT == DEREF || lT == VAR || lT == VARPTR)
                // #a->setType( ASSIGN_REPLACE);
                #assign_expr=#(a,r,l);  
            }
            else
            {
                #assign_expr=#(l);
            }
        }
    ;

// different return tree with self assignment
assign_expr_statement!
	: #(a:ASSIGN l:lassign_expr r:expr)
        {
            if( !SelfAssignment( #l, #r))
            {
                
                AssignReplace( #l, #a);
                
                // int lT = #l->getType();
                // if( lT == FCALL || lT == MFCALL || lT == MFCALL_PARENT ||
                // lT == FCALL_LIB || lT == MFCALL_LIB || lT == MFCALL_PARENT_LIB ||
                // lT == DEREF || lT == VAR || lT == VARPTR)
                // #a->setType( ASSIGN_REPLACE);
                #assign_expr_statement=#(a,r,l);  
            }
            else
            {
                #assign_expr_statement=#([BLOCK,"block"]);                
            }
        }
    ;

/// *** to be done (ASSIGN_REPLACE)
// +=, *=, ...
// ATTENTION: This gives a different result than IDL on something like:
// a=intarr(3)&i=1&a[[i++]]+=1&print,i,a
comp_assign_expr!//
{
RefDNode l;
}
    : 
    (
      #(a1:AND_OP_EQ l1:lassign_expr r1:expr) 
        { l=#l1; #comp_assign_expr=#([ASSIGN,":="],([AND_OP,"and"],l1,r1),RemoveNextSibling(l1));}
    | #(a2:ASTERIX_EQ l2:lassign_expr r2:expr) 
        { l=#l2; #comp_assign_expr=#([ASSIGN,":="],([ASTERIX,"*"],l2,r2),RemoveNextSibling(l2));} 
    | #(a3:EQ_OP_EQ l3:lassign_expr r3:expr) 
        { l=#l3; #comp_assign_expr=#([ASSIGN,":="],([EQ_OP,"eq"],l3,r3),RemoveNextSibling(l3));} 
    | #(a4:GE_OP_EQ l4:lassign_expr r4:expr) 
        { l=#l4; #comp_assign_expr=#([ASSIGN,":="],([GE_OP,"ge"],l4,r4),RemoveNextSibling(l4));}
    | #(a5:GTMARK_EQ l5:lassign_expr r5:expr) 
        { l=#l5; #comp_assign_expr=#([ASSIGN,":="],([GTMARK,">"],l5,r5),RemoveNextSibling(l5));}
    | #(a6:GT_OP_EQ l6:lassign_expr r6:expr) 
        { l=#l6; #comp_assign_expr=#([ASSIGN,":="],([GT_OP,"gt"],l6,r6),RemoveNextSibling(l6));}
    | #(a7:LE_OP_EQ l7:lassign_expr r7:expr) 
        { l=#l7; #comp_assign_expr=#([ASSIGN,":="],([LE_OP,"le"],l7,r7),RemoveNextSibling(l7));}
    | #(a8:LTMARK_EQ l8:lassign_expr r8:expr) 
        { l=#l8; #comp_assign_expr=#([ASSIGN,":="],([LTMARK,"<"],l8,r8),RemoveNextSibling(l8));}
    | #(a9:LT_OP_EQ l9:lassign_expr r9:expr) 
        { l=#l9; #comp_assign_expr=#([ASSIGN,":="],([LT_OP,"lt"],l9,r9),RemoveNextSibling(l9));}
    | #(a10:MATRIX_OP1_EQ l10:lassign_expr r10:expr) 
        { l=#l10; #comp_assign_expr=#([ASSIGN,":="],([MATRIX_OP1,"#"],l10,r10),RemoveNextSibling(l10));}
    | #(a11:MATRIX_OP2_EQ l11:lassign_expr r11:expr) 
        { l=#l11; #comp_assign_expr=#([ASSIGN,":="],([MATRIX_OP2,"##"],l11,r11),RemoveNextSibling(l11));}
    | #(a12:MINUS_EQ l12:lassign_expr r12:expr) 
        { l=#l12; #comp_assign_expr=#([ASSIGN,":="],([MINUS,"-"],l12,r12),RemoveNextSibling(l12));}
    | #(a13:MOD_OP_EQ l13:lassign_expr r13:expr) 
        { l=#l13; #comp_assign_expr=#([ASSIGN,":="],([MOD_OP,"mod"],l13,r13),RemoveNextSibling(l13));}
    | #(a14:NE_OP_EQ l14:lassign_expr r14:expr) 
        { l=#l14; #comp_assign_expr=#([ASSIGN,":="],([NE_OP,"ne"],l14,r14),RemoveNextSibling(l14));}
    | #(a15:OR_OP_EQ l15:lassign_expr r15:expr) 
        { l=#l15; #comp_assign_expr=#([ASSIGN,":="],([OR_OP,"or"],l15,r15),RemoveNextSibling(l15));}
    | #(a16:PLUS_EQ l16:lassign_expr r16:expr) 
        { l=#l16; #comp_assign_expr=#([ASSIGN,":="],([PLUS,"+"],l16,r16),RemoveNextSibling(l16));}
    | #(a17:POW_EQ l17:lassign_expr r17:expr) 
        { l=#l17; #comp_assign_expr=#([ASSIGN,":="],([POW,"^"],l17,r17),RemoveNextSibling(l17));}
    | #(a18:SLASH_EQ l18:lassign_expr r18:expr) 
        { l=#l18; #comp_assign_expr=#([ASSIGN,":="],([SLASH,"/"],l18,r18),RemoveNextSibling(l18));}
    | #(a19:XOR_OP_EQ l19:lassign_expr r19:expr) 
        { l=#l19; #comp_assign_expr=#([ASSIGN,":="],([XOR_OP,"xor"],l19,r19),RemoveNextSibling(l19));} 
    )
        {
            AssignReplace( l, #comp_assign_expr);
        }
    ;



// NEW for correct behaviour
// comp_assign_expr!//
// {
// RefDNode l;
// }
//     : 
//       #(a1:AND_OP_EQ l1:lassign_expr r1:expr) 
//         { #comp_assign_expr=#([ASSIGN_INPLACE,"op="],a1,r1,l1);}
//     | #(a2:ASTERIX_EQ l2:lassign_expr r2:expr) 
//         { #comp_assign_expr=#([ASSIGN_INPLACE,"op="],a2,r2,l2);}
//     | #(a3:EQ_OP_EQ l3:lassign_expr r3:expr) 
//         { #comp_assign_expr=#([ASSIGN_INPLACE,"op="],a3,r3,l3);}
//     | #(a4:GE_OP_EQ l4:lassign_expr r4:expr) 
//         { #comp_assign_expr=#([ASSIGN_INPLACE,"op="],a4,r4,l4);}
//     | #(a5:GTMARK_EQ l5:lassign_expr r5:expr) 
//         { #comp_assign_expr=#([ASSIGN_INPLACE,"op="],a5,r5,l5);}
//     | #(a6:GT_OP_EQ l6:lassign_expr r6:expr) 
//         { #comp_assign_expr=#([ASSIGN_INPLACE,"op="],a6,r6,l6);}
//     | #(a7:LE_OP_EQ l7:lassign_expr r7:expr) 
//         { #comp_assign_expr=#([ASSIGN_INPLACE,"op="],a7,r7,l7);}
//     | #(a8:LTMARK_EQ l8:lassign_expr r8:expr) 
//         { #comp_assign_expr=#([ASSIGN_INPLACE,"op="],a8,r8,l8);}
//     | #(a9:LT_OP_EQ l9:lassign_expr r9:expr) 
//         { #comp_assign_expr=#([ASSIGN_INPLACE,"op="],a9,r9,l9);}
//     | #(a10:MATRIX_OP1_EQ l10:lassign_expr r10:expr) 
//         { #comp_assign_expr=#([ASSIGN_INPLACE,"op="],a10,r10,l10);}
//     | #(a11:MATRIX_OP2_EQ l11:lassign_expr r11:expr) 
//         { #comp_assign_expr=#([ASSIGN_INPLACE,"op="],a11,r11,l11);}
//     | #(a12:MINUS_EQ l12:lassign_expr r12:expr) 
//         { #comp_assign_expr=#([ASSIGN_INPLACE,"op="],a12,r12,l12);}
//     | #(a13:MOD_OP_EQ l13:lassign_expr r13:expr) 
//         { #comp_assign_expr=#([ASSIGN_INPLACE,"op="],a13,r13,l13);}
//     | #(a14:NE_OP_EQ l14:lassign_expr r14:expr) 
//         { #comp_assign_expr=#([ASSIGN_INPLACE,"op="],a14,r14,l14);}
//     | #(a15:OR_OP_EQ l15:lassign_expr r15:expr) 
//         { #comp_assign_expr=#([ASSIGN_INPLACE,"op="],a15,r15,l15);}
//     | #(a16:PLUS_EQ l16:lassign_expr r16:expr) 
//         { #comp_assign_expr=#([ASSIGN_INPLACE,"op="],a16,r15,l16);}
//     | #(a17:POW_EQ l17:lassign_expr r17:expr) 
//         { #comp_assign_expr=#([ASSIGN_INPLACE,"op="],a17,r17,l17);}
//     | #(a18:SLASH_EQ l18:lassign_expr r18:expr) 
//         { #comp_assign_expr=#([ASSIGN_INPLACE,"op="],a18,r18,l18);}
//     | #(a19:XOR_OP_EQ l19:lassign_expr r19:expr) 
//         { #comp_assign_expr=#([ASSIGN_INPLACE,"op="],a19,r19,l19);}
//     ;



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

// system variables have as variable ptr NULL initially
sysvar!//
  : #(s:SYSVAR i:SYSVARNAME)
	{ 
      std::string sysVarName = i->getText();
      // here we create the real sysvar node      
	  #sysvar=astFactory->create(SYSVAR, sysVarName.substr(1));
      #sysvar->SetLine(s->getLine());
//	  #sysvar=#[SYSVAR,i->getText()];
	  comp.SysVar(#sysvar); // sets var to NULL
	}	
  ;

// variables are converted to:
// VAR    with index into functions/procedures variable list or
// VARPTR for common block variables with a ptr to the
//        variable in the common block
var!//
  : #(v:VAR i:IDENTIFIER)
	{ 
	  #var=astFactory->create(VAR,i->getText());
      #var->SetLine(v->getLine());
//	  #var=#[VAR,i->getText()];
	  comp.Var(#var);	
	}
  ;

arrayindex_list_to_parameter_list! [bool varNum]// ???
{
    int nPar = 0;
}
    : (options {greedy=true;}: #(ARRAYIX e:pos_parameter[ varNum])
            {
                #arrayindex_list_to_parameter_list=
                    #(NULL, arrayindex_list_to_parameter_list, e);
                ++nPar;
            }
        )+
        {
            arrayindex_list_to_parameter_list_AST->SetNParam( nPar);
        }
  ;

// for function calls the arrayindex list is properly converted
arrayexpr_fn!//
{
    std::string id_text;
    bool isVar;
    RefDNode mark, va2, vaAlt, fn, arrayindex_listAST;
}   
  	: #(aIn:ARRAYEXPR_FCALL 
            // always here: #(VAR IDENTIFIER)
            #(va:VAR id:IDENTIFIER)
            { 
                mark = _t;

                id_text = #id->getText(); 
                
                // IsVar is not needed, we must emit an ARRAYEXPR_FCALL even if the variable is known
                // (rule: Accessible functions always override variables
                //isVar = comp.IsVar( id_text); 
                // isVar == true -> VAR for sure 
                // (== false: maybe VAR nevertheless)

                int libIx = LibFunIx(id_text);

            }
            (   
                el:arrayindex_list_to_parameter_list[ libIx != -1 && libFunList[ libIx]->NPar() == -1]
            )
            { 
                // first search library functions
                if( libIx != -1)
                    {
                        int nParam = 0;
                        if( #el != RefDNode(antlr::nullAST))
                            nParam = #el->GetNParam();

                        int libParam = libFunList[libIx]->NPar();
                        int libParamMin = libFunList[libIx]->NParMin();
                        if( libParam != -1 && nParam > libParam)
                            throw GDLException(	aIn, libFunList[libIx]->Name() + ": Too many arguments.");
                        if( libParam != -1 && nParam < libParamMin)
                            throw GDLException(	aIn, libFunList[libIx]->Name() + ": Too few arguments.");

                        #id->SetLibFun( libFunList[libIx]);
                        if( libFunList[ libIx]->RetNew())
                            {
                                if( libFunList[ libIx]->Name() == "N_ELEMENTS")
                                    #id->setType( FCALL_LIB_N_ELEMENTS);
                                else if( libFunList[ libIx]->DirectCall())
                                        #id->setType( FCALL_LIB_DIRECT);
                                    else
                                        #id->setType( FCALL_LIB_RETNEW);
                                #arrayexpr_fn = #( id, el);
//                              #([/*FCALL_LIB_RETNEW,"fcall_lib_retnew"],*/ id, el);
                            }
                        else
                            {
                                #id->setType( FCALL_LIB);
                                #arrayexpr_fn = #( id, el);
//                              #(/*[FCALL_LIB,"fcall_lib"],*/ id, el);
                            }
                    }
                // then search user defined functions
                else
                    {

                        int funIx=FunIx( id_text);

                        // we use #id for the FCALL part
                        #id->setType( FCALL);
                        #id->SetFunIx( funIx);

                        if( funIx != -1) // found -> FCALL
                            {
                                #arrayexpr_fn = #( id, el); 
                                // #(/*[FCALL,"fcall"],*/ id, el);
                            }
                        else // function not found -> still ambiguous
                            {
                                // _t = mark; // rewind to parse again 
                                arrayindex_list( mark);
                                //_t = _retTree;
                                arrayindex_listAST = returnAST;
                

                                #va2=astFactory->create( VAR, id_text);
                                // #va=#[VAR,id->getText()];
                                comp.Var( #va2); // we declare the variable here!
                                // if IsVar() still would be used this would lead to surprising behavior: 
                                // e. g.: function_call(42) & function_call(43)  
                                // The first (42) would be an ARRAYEXPR_FCALL the 2nd (43) an ARRAYEXPR
                                // if then at runtime function "function_call" is known,
                                // it will be called only at the first appearance of the call.
                                // that's why we cannot allow unambiguous VAR here 

                                #vaAlt = #([ARRAYEXPR,"arrayexpr"], va2, arrayindex_listAST);
                                #fn = #( id, el);    

                                #arrayexpr_fn = #( aIn, vaAlt, fn); 
                            }
                    }

//                 // not valid s. a. (kept for reference): unambiguous VAR
//                 {   // variable -> arrayexpr                    
//                     // make var
//                     #va=astFactory->create(VAR,#id->getText());
// //                    #va=#[VAR,id->getText()];
//                     comp.Var(#va);
//                     #arrayexpr_fn=
//                     #([ARRAYEXPR,"arrayexpr"], va, al);
//                 }
            }
        )  
    ;



arrayexpr_mfcall!//
    : #(a:ARRAYEXPR_MFCALL
            {
                RefDNode mark = _t; // mark
            }
            e:expr i:IDENTIFIER //al:arrayindex_list
            { 
                bool success = true;
                RefDNode #first;
	            try {
                    arrayindex_list(_t);

                    RefDNode al = returnAST;
                    RefDNode #ae = #([ARRAYEXPR,"arrayexpr"], #i, al);

                    if( e->getType() == DOT)
                    {
                        int nDot = #e->GetNDot();
                        #e->SetNDot( ++nDot);

                        #e->addChild( #ae);

                        #first = #e;
                    }
                    else
                    {
                        RefDNode #dot = #([DOT,"."], e, ae);
                        #dot->SetNDot( 1);

                        #first = #dot;
                    }
                }
                catch( GDLException& ex)
                {
                    Message( "Ambiguity resolved: member function call "
                             "due to invalid array index.");

                    success = false;

                    #a->setType( MFCALL);
                    #a->setText( "mfcall");
                }

                _t = mark; // rewind to parse again 
            }
            e2:expr i2:IDENTIFIER a2:arrayindex_list_to_parameter_list[ false]
            {
                if( success)
                    #arrayexpr_mfcall = #(a, first, e2, i2, a2);
                else
                    #arrayexpr_mfcall = #(a, e2, i2, a2);
            } 
            
        )
    ;

// only here a function call is ok also
primary_expr
{
int dummy;
RefDNode mark;
}
    : assign_expr
    | comp_assign_expr
    | arrayexpr_mfcall 
	| #(MFCALL expr IDENTIFIER parameter_def[ false]
        )
	| #(MFCALL_PARENT expr IDENTIFIER
            IDENTIFIER parameter_def[ false]
        )
//     | #(nEl:FCALL_LIB_N_ELEMENTS id2:IDENTIFIER! parameter_def
//         )
	| #(f:FCALL id:IDENTIFIER!
            {
                // first search library functions
                int i=LibFunIx(id->getText());
            }
            p:parameter_def[  i != -1 && libFunList[ i]->NPar() == -1]
            {
                if( i != -1)
                {
                    // N_ELEMENTS must handle exceptions during parameter evaluation
//                     if( StrUpCase( #id->getText()) == "N_ELEMENTS")
//                     {
//                     #f->setType(FCALL_LIB_N_ELEMENTS);
//                     #f->setText(#id->getText());
//                     #f->SetLibFun( libFunList[i]);
//                         //                    #id->SetFunIx(i);
//                     }
//                     else
                    {
                    int nParam = 0;
                    if( #p != RefDNode(antlr::nullAST))
                        nParam = #p->GetNParam();

                    int libParam = libFunList[i]->NPar();
                    int libParamMin = libFunList[i]->NParMin();
                    if( libParam != -1 && nParam > libParam)
                        throw GDLException(	f, libFunList[i]->Name() + ": Too many arguments.");
                    if( libParam != -1 && nParam < libParamMin)
                        throw GDLException(	f, libFunList[i]->Name() + ": Too few arguments.");
                    if( libFunList[ i]->RetNew())
                    {
                        if( libFunList[ i]->Name() == "N_ELEMENTS")
                            #f->setType( FCALL_LIB_N_ELEMENTS);
                        else if( libFunList[ i]->DirectCall())
                                #f->setType( FCALL_LIB_DIRECT);
                            else
                                #f->setType(FCALL_LIB_RETNEW);
                        #f->setText(#id->getText());
                        #f->SetLibFun( libFunList[i]);
                        //                    #id->SetFunIx(i);
                    }
                    else
                    {
                        #f->setType(FCALL_LIB);
                        #f->setText(#id->getText());
                        #f->SetLibFun( libFunList[i]);
                        //                    #id->SetFunIx(i);
                    }
                    }
                }
                else
                {
                    // then search user defined functions
                    #f->setType(FCALL);
                    #f->setText(#id->getText());
                    i=FunIx(#id->getText());
                    #f->SetFunIx(i);
                }
            }
        ) 	
  	| arrayexpr_fn // converts fo FCALL(_LIB) or ARRAYEXPR_FCALL
	| CONSTANT
	| dummy=array_def
	| struct_def
    | g:GDLNULL
        {
            #g->setType(SYSVAR);
            #g->setText("NULL");
            comp.SysVar(#g); // sets var to NULL
        }
	;

op_expr
    :	#(QUESTION expr expr expr)	// trinary operator
	|	#(AND_OP expr expr)			// binary/unary operators...
	|	#(OR_OP expr expr)
	|	#(XOR_OP expr expr)
	|	#(LOG_AND expr expr)	
	|	#(LOG_OR expr expr)
	|	#(EQ_OP expr expr)
	|	#(NE_OP expr expr)
	|	#(LE_OP expr expr)
	|	#(LT_OP expr expr)
	|	#(GE_OP expr expr)
	|	#(GT_OP expr expr)
	|	#(NOT_OP expr)
	|	#(PLUS expr expr)
	|	#(MINUS expr expr)
	|	#(LTMARK expr expr)
	|	#(GTMARK expr expr)
//	|	#(UPLUS e:expr { #op_expr=#e;}) // elimintated
	|	uminus
	|	#(LOG_NEG expr)
	|	#(ASTERIX expr expr)
	|	#(MATRIX_OP1 expr expr)
	|	#(MATRIX_OP2 expr expr)
	|	#(SLASH expr expr)
	|	#(MOD_OP expr expr)
	|	#(POW expr expr)
	|	#(DEC expr) //unbrace_expr)
	|	#(INC expr) //unbrace_expr)
	|	#(POSTDEC expr) //unbrace_expr)
	|	#(POSTINC expr) //unbrace_expr)
	|   primary_expr
	;

uminus
    : #(u:UMINUS e:expr)
        {
            // eliminate (pre-calculate) uminus for constants
            if( #e->getType() == CONSTANT)
            {
                #e->ResetCData( #e->CData()->UMinus());
                #e->setText( "-"+#e->getText());
                #uminus = #e;
            }
//             else
//             {
//                 #uminus = #( [UMINUS,"u-"], e);
//             }
        }
    ;

// remove multiple braces
brace_expr!//
	: #(e:EXPR ex:expr)
		{
            while( #ex->getType()==EXPR) 
                #ex=#ex->getFirstChild();
            #brace_expr=#(e, ex);
		}
	;

// removes ALL braces
unbrace_expr!// 
	: #(EXPR ex:expr)
		{
            while( #ex->getType()==EXPR) 
                #ex=#ex->getFirstChild();
            #unbrace_expr=#ex;
		}
	;

// array and struct accessing
indexable_expr // only used by array_expr
	: var
	| sysvar
//    | brace_expr
    | unbrace_expr
    ;
array_expr // only used by expr
	: #(ARRAYEXPR indexable_expr arrayindex_list)
	| indexable_expr
	;

tag_indexable_expr // only used by tag_array_expr_1st
	: var
	| sysvar
    | brace_expr
    ;
tag_array_expr_1st // only used by expr
	: #(ARRAYEXPR tag_indexable_expr arrayindex_list)
	| tag_indexable_expr
	;

tag_expr
    : brace_expr
    | IDENTIFIER
    ;
tag_array_expr
	: #(ARRAYEXPR tag_expr arrayindex_list)
    | tag_expr
    ;

// the everywhere used expression
expr
	: array_expr
    | #(DOT tag_array_expr_1st (tag_array_expr)+)
	| #(DEREF expr)    // deref
	| op_expr
	;
