/* *************************************************************************
                          gdlc.i.g 
the GDL interpreter
interprets the output of the treeparser/compiler
                             -------------------
    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.                                   *
 *                                                                         *
 ***************************************************************************/

header "pre_include_cpp" {
    // gets inserted before the antlr generated includes in the cpp file
#include "includefirst.hpp"
}

header "post_include_cpp" {
    // gets inserted after the antlr generated includes in the cpp file
#include "dinterpreter.hpp"
#include "prognodeexpr.hpp"

#include <cassert>

// tweaking ANTLR
#define ASTNULL          NULLProgNodeP
#define ProgNodeP( xxx ) NULL             /* ProgNodeP(antlr::nullAST) */
#define RefAST( xxx)     ConvertAST( xxx) /* antlr::RefAST( Ref type)  */
#define match( a, b)     /* remove from source */

using namespace std;
}

header {
    // antlr header

    // make sure it gets included before the 'tweak'
#include "GDLParser.hpp" 
#include "GDLTreeParser.hpp" 

#include <map>
#include <iomanip>
//#include <exception>

#include "datatypes.hpp"
#include "objects.hpp"
#include "dpro.hpp"
#include "accessdesc.hpp"
#include "initsysvar.hpp"
#include "gdljournal.hpp"

//class ProgNode;
//typedef ProgNode* ProgNodeP;

// tweaking ANTLR
#define RefAST( xxx)     ConvertAST( xxx) /* antlr::RefAST( Ref type)  */

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

}

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 GDLInterpreter extends TreeParser;

options {
  importVocab = GDL;	// use vocab generated by lexer
  buildAST = false;     // no modifying of AST anymore
    // no AST is created in the interpreter, hence we don't need ref counting
//  ASTLabelType = "RefDNode"; 
  ASTLabelType = "ProgNodeP"; 
//  defaultErrorHandler = true;
  defaultErrorHandler = false;
//  codeGenMakeSwitchThreshold = 2;
//  codeGenBitsetTestThreshold = 32;
}

{
private:
    // ASTNULL replacement
    static ProgNode  NULLProgNode;
    static ProgNodeP NULLProgNodeP;

    friend class BaseGDL;
    friend class ProgNode;
    friend class ARRAYDEFNode;
    friend class STRUCNode;
    friend class NSTRUCNode;
    friend class NSTRUC_REFNode;
    friend class ASSIGNNode;
    friend class ASSIGN_ARRAYEXPR_MFCALLNode;
    friend class ASSIGN_REPLACENode;
    friend class PCALL_LIBNode;//: public CommandNode
    friend class MPCALLNode;//: public CommandNode
    friend class MPCALL_PARENTNode;//: public CommandNode
    friend class PCALLNode;//: public CommandNode
    friend class RETFNode;
    friend class RETPNode;
    friend class FORNode;
    friend class FOR_LOOPNode;
    friend class FOREACHNode;
    friend class FOREACH_LOOPNode;
    friend class FOREACH_INDEXNode;
    friend class FOREACH_INDEX_LOOPNode;
    friend class FOR_STEPNode;
    friend class FOR_STEP_LOOPNode;
    friend class KEYDEFNode;
    friend class KEYDEF_REFNode;
    friend class KEYDEF_REF_CHECKNode;
    friend class KEYDEF_REF_EXPRNode;
    friend class REFNode;
    friend class REF_CHECKNode;
    friend class REF_EXPRNode;
    friend class ParameterNode;
    friend class REFVNNode;
    friend class REF_CHECKVNNode;
    friend class REF_EXPRVNNode;
    friend class ParameterVNNode;
    friend class WRAPPED_FUNNode;
    friend class WRAPPED_PRONode;

public: 

//     RetCode returnCode;    
    ProgNodeP GetNULLProgNodeP() const { return NULLProgNodeP;}


    void SetRetTree( ProgNodeP rT)
    {
        this->_retTree = rT;
    }
    ProgNodeP GetRetTree() const
    {
        return this->_retTree;
    }
//     void SetReturnCode( RetCode rC)
//     {
//         this->returnCode = rC;
//     }
    
//     enum RetCode {
//         RC_OK=0,
//         RC_BREAK,
//         RC_CONTINUE,
//         RC_RETURN, 
//         RC_ABORT, // checked as retCode >= RC_RETURN
//     };  

    // code in: dinterpreter.cpp
    // procedure (searchForPro == true) or function (searchForPro == false)
    static bool SearchCompilePro(const std::string& pro, bool searchForPro); 
    static int GetFunIx( ProgNodeP);
    static int GetFunIx( const std::string& subName);
    static int GetProIx( ProgNodeP);//const std::string& subName);
    static int GetProIx( const std::string& subName);
    DStructGDL* ObjectStruct( DObjGDL* self, ProgNodeP mp);
    void SetRootR( ProgNodeP tt, DotAccessDescT* aD, BaseGDL* r, ArrayIndexListT* aL);
    void SetRootL( ProgNodeP tt, DotAccessDescT* aD, BaseGDL* r, ArrayIndexListT* aL);
    // DStructGDL* ObjectStructCheckAccess( DObjGDL* self, ProgNodeP mp);
    // DStructDesc* GDLObjectDesc( DObjGDL* self, ProgNodeP mp);

    // code in: dinterpreter.cpp
    static void SetFunIx( ProgNodeP f); // triggers read/compile


private: 

    static void SetProIx( ProgNodeP f); // triggers read/compile
    static void AdjustTypes( BaseGDL*&, BaseGDL*&);


protected:
    std::istringstream executeLine; // actual interactive executed line

//     std::vector<BaseGDL*> tmpList;
//     void ClearTmpList()
//     {
//         std::vector<BaseGDL*>::iterator i;
//         for(i = tmpList.begin(); i != tmpList.end(); ++i) 
//             { delete *i;}
//         tmpList.clear();
//     }

    class RetAllException 
    {
        public:
        enum ExCode {
            NONE=0 // normal RETALL
            ,RUN   // RETALL from .RUN command
            ,RESET // RETALL from .RESET command
            ,FULL_RESET // RETALL from .FULL_RESET command
        };  

        private:
        ExCode code;

        public:
        RetAllException( ExCode code_=NONE): code( code_) {}

        ExCode Code() { return code;}
    };
    
    // code in: dinterpreter.cpp
//    static bool CompleteFileName(std::string& fn); -> str.cpp

    BaseGDL*  returnValue;  // holding the return value for functions
    BaseGDL** returnValueL; // holding the return value for l_functions

    bool interruptEnable;

public:
    // procedure (searchForPro == true) or function (searchForPro == false)
    static bool CompileFile(const std::string& f, 
                            const std::string& untilPro="",
                            bool searchForPro=true); 

    typedef RefHeap<BaseGDL> RefBaseGDL;
    typedef RefHeap<DStructGDL> RefDStructGDL;

    typedef std::map<SizeT, RefBaseGDL> HeapT;
    typedef std::map<SizeT, RefDStructGDL> ObjHeapT;

protected:
//     typedef std::map<SizeT, BaseGDL*> HeapT;
//     typedef std::map<SizeT, DStructGDL*> ObjHeapT;

    // the following must be all static because several interpreter might be active
    // the heap for all dynamic variables
    // ease the handling, no memory leaks, gc possible
    static HeapT     heap; 
    static ObjHeapT  objHeap; 

    // index for newly allocated heap variables
    static SizeT objHeapIx;
    static SizeT heapIx;

    static EnvStackT  callStack; 

    static DLong stepCount;


// smuggle optimizations in
//#include "GDLInterpreterOptimized.inc"


public:
    // triggers read/compile/interpret
    DStructDesc* GetStruct(const std::string& name, const ProgNodeP cN); 

//     bool Called( std::string proName)
//     {
//         for( EnvStackT::reverse_iterator env = callStack.rbegin();
//             env != callStack.rend();
//             ++env)
//             {
//                 //std::cout <<  (*env)->GetPro()->ObjectFileName() << std::endl;
//                 if( proName == (*env)->GetPro()->ObjectFileName()) return true;
//             }
//         return false;
//     }

    // the New... functions 'own' their BaseGDL*
    SizeT NewObjHeap( SizeT n=1, DStructGDL* var=NULL)
    {
        SizeT tmpIx=objHeapIx;
        for( SizeT i=0; i<n; i++)
        objHeap.insert( objHeap.end(),
            std::pair<SizeT, RefDStructGDL>( objHeapIx++, (DStructGDL*)var));
        return tmpIx;
    }
    SizeT NewHeap( SizeT n=1, BaseGDL* var=NULL)
    {
        SizeT tmpIx=heapIx;
        for( SizeT i=0; i<n; i++)
        heap.insert( heap.end(),
            std::pair<SizeT, RefBaseGDL>( heapIx++, var));
        return tmpIx;
    }
    static void FreeObjHeapDirect( DObj id, ObjHeapT::iterator it)
    {
        BaseGDL* del = (*it).second.get();
        objHeap.erase( id); 
        delete del;
        // delete (*it).second.get();
        // objHeap.erase( id);
    }
    static void FreeObjHeap( DObj id)
    {
        if( id != 0)
        {       
            ObjHeapT::iterator it=objHeap.find( id);
            if  ( it != objHeap.end()) 
            { 
                FreeObjHeapDirect( id, it);
                // delete (*it).second.get();
                // objHeap.erase( id);
            }
        }
    }
    static void FreeHeapDirect( DPtr id, HeapT::iterator it)
    {
        BaseGDL* del = (*it).second.get();
        heap.erase( id); 
        delete del;
        // delete (*it).second.get();
        // // useless because of next: (*it).second.get() = NULL;
        // heap.erase( id); 
    }
    static void FreeHeap( DPtr id)
    {
        if( id != 0)
            {
                HeapT::iterator it=heap.find( id);
                if( it != heap.end()) 
                    { 
                        FreeHeapDirect( id, it);
                        // delete (*it).second.get();
                        // heap.erase( id); 
                    }
            }
    }
    static void HeapErase( DPtr id) // for LIST
    {
        if( id != 0)
            {
              heap.erase( id); 
            }
    }

   static void FreeHeap( DPtrGDL* p)
    {
        SizeT nEl=p->N_Elements();
        for( SizeT ix=0; ix < nEl; ix++)
        {
            DPtr id= (*p)[ix];
            FreeHeap( id);
       }
    }

   static void DecRef( DPtr id)
   {
       if( id != 0)
           {
#ifdef GDL_DEBUG_HEAP
               std::cout << "-- <PtrHeapVar" << id << ">" << std::endl; 
#endif
               HeapT::iterator it=heap.find( id);
               if( it != heap.end()) 
                   { 
                       if( (*it).second.Dec())
                           {
#ifdef GDL_DEBUG_HEAP
                               std::cout << "Out of scope (garbage collected): <PtrHeapVar" << id 
                                         << ">"
                                         << " at: " << callStack.back()->GetProName()
                                         << "  line: " << callStack.back()->GetLineNumber()
                                         << std::endl; 
#endif
                               FreeHeapDirect( id, it);
                           }
#ifdef GDL_DEBUG_HEAP
                       else
                           std::cout << "<PtrHeapVar" << id << "> = " << (*it).second.Count() << std::endl; 
#endif
                   }
           }
   }
   static void DecRef( DPtrGDL* p)
    {
        SizeT nEl=p->N_Elements();
        for( SizeT ix=0; ix < nEl; ix++)
        {
            DPtr id= (*p)[ix];
            DecRef( id);
       }
    }
   static void DecRefObj( DObj id)
    {
        if( id != 0)
            {
#ifdef GDL_DEBUG_HEAP
std::cout << "-- <ObjHeapVar" << id << ">" << std::endl; 
#endif
                ObjHeapT::iterator it=objHeap.find( id);
                if( it != objHeap.end()) 
                    { 
                       if( (*it).second.Dec())
                           {
#ifdef GDL_DEBUG_HEAP
                               std::cout << "Out of scope (garbage collected): <ObjHeapVar" << id 
                                         << ">"
                                         << " at: " << callStack.back()->GetProName()
                                         << "  line: " << callStack.back()->GetLineNumber()
                                         << std::endl; 
#endif
                               callStack.back()->ObjCleanup( id);
//                             FreeObjHeapDirect( id, it);
                           }
#ifdef GDL_DEBUG_HEAP
                        else
std::cout << "<ObjHeapVar" << id << "> = " << (*it).second.Count() << std::endl; 
#endif
                        
                     }
            }
    }
   static void DecRefObj( DObjGDL* p)
    {
        SizeT nEl=p->Size();//N_Elements();
        for( SizeT ix=0; ix < nEl; ix++)
        {
            DObj id= (*p)[ix];
            DecRefObj( id);
       }
    }
   static void IncRef( DPtr id)
    {
        if( id != 0)
            {
#ifdef GDL_DEBUG_HEAP
std::cout << "++ <PtrHeapVar" << id << ">" << std::endl; 
#endif
                HeapT::iterator it=heap.find( id);
                if( it != heap.end()) 
                    { 
                        (*it).second.Inc(); 
#ifdef GDL_DEBUG_HEAP
std::cout << "<PtrHeapVar" << id << "> = " << (*it).second.Count() << std::endl; 
#endif
                    }
            }
    }
   static void AddRef( DPtr id, SizeT add)
    {
        if( id != 0)
            {
#ifdef GDL_DEBUG_HEAP
std::cout << add << " + <PtrHeapVar" << id << ">" << std::endl; 
#endif
                HeapT::iterator it=heap.find( id);
                if( it != heap.end()) 
                    { 
                        (*it).second.Add(add); 
                    }
            }
    }
   static void IncRef( DPtrGDL* p)
    {
        SizeT nEl=p->N_Elements();
        for( SizeT ix=0; ix < nEl; ix++)
        {
            DPtr id= (*p)[ix];
            IncRef( id);
       }
    }
   static void IncRefObj( DObj id)
    {
        if( id != 0)
            {
#ifdef GDL_DEBUG_HEAP
std::cout << "++ <ObjHeapVar" << id << ">" << std::endl; 
#endif
                ObjHeapT::iterator it=objHeap.find( id);
                if( it != objHeap.end()) 
                    { 
                        (*it).second.Inc(); 
                    }
            }
    }
   static void AddRefObj( DObj id, SizeT add)
    {
        if( id != 0)
            {
#ifdef GDL_DEBUG_HEAP
std::cout << add << " + <ObjHeapVar" << id << ">" << std::endl; 
#endif
                ObjHeapT::iterator it=objHeap.find( id);
                if( it != objHeap.end()) 
                    { 
                        (*it).second.Add(add); 
                    }
            }
    }
   static void IncRefObj( DObjGDL* p)
    {
        SizeT nEl=p->Size();//N_Elements();
        for( SizeT ix=0; ix < nEl; ix++)
        {
            DObj id= (*p)[ix];
            IncRefObj( id);
       }
    }

    class HeapException {};

    static BaseGDL*& GetHeap( DPtr ID)
    {
        HeapT::iterator it=heap.find( ID);
        if( it == heap.end()) 
            throw HeapException();
        return it->second.get();
    }
    static DStructGDL*& GetObjHeap( DObj ID)
    {
        ObjHeapT::iterator it=objHeap.find( ID);
        if( it == objHeap.end()) 
            throw HeapException();
        return it->second.get();
    }

    // for overload functions
    static DSubUD* GetObjHeapOperator( DObj ID, int opIx)
    {
        if( ID == 0) return NULL;
        ObjHeapT::iterator it=objHeap.find( ID);
        if( it == objHeap.end()) return NULL;
        return it->second.get()->Desc()->GetOperator( opIx);
    }
    static DStructGDL* GetObjHeapNoThrow( DObj ID)
    {
        ObjHeapT::iterator it=objHeap.find( ID);
        if( it == objHeap.end()) return NULL;
        return it->second.get();
    }
//     static DStructGDL*& GetObjHeap( DObj ID, ObjHeapT::iterator& it)
//     {
// //         ObjHeapT::iterator it=objHeap.find( ID);
//         it=objHeap.find( ID);
//         if( it == objHeap.end()) throw HeapException();
//         return it->second.get();
//     }

    static bool PtrValid( DPtr ID)
    {
        HeapT::iterator it=heap.find( ID);
        return  (it != heap.end());
    }

    static SizeT HeapSize()
    {
        return heap.size();
    }

    static DPtr FindInHeap( BaseGDL** p)
    {
        for( HeapT::iterator it=heap.begin(); it != heap.end(); ++it)
        {
            if( &it->second.get() == p)
                return it->first;
        }
        return 0;
    }
    // static BaseGDL** GetPtrToHeap( BaseGDL* p)
    // {
    //     for( HeapT::iterator it=heap.begin(); it != heap.end(); ++it)
    //     {
    //         if( it->second.get() == p)
    //             return &it->second.get();
    //     }
    //     return NULL;
    // }
    static DPtrGDL* GetAllHeap()
    {
        SizeT nEl = heap.size();
        if( nEl == 0) return new DPtrGDL( 0);
        DPtrGDL* ret = new DPtrGDL( dimension( &nEl, 1), BaseGDL::NOZERO);
        SizeT i=0;
        for( HeapT::iterator it=heap.begin(); it != heap.end(); ++it)
        {
            IncRef( it->first);
            (*ret)[ i++] = it->first;
        }
        return ret;
    }

    // no ref counting here
    static std::vector<DPtr>* GetAllHeapSTL()
    {
        SizeT nEl = heap.size();
        if( nEl == 0) return new std::vector<DPtr>();
        std::vector<DPtr>* ret = new std::vector<DPtr>( nEl);
        SizeT i=0;
        for( HeapT::iterator it=heap.begin(); it != heap.end(); ++it)
        {
            (*ret)[ i++] = it->first;
        }
        return ret;
    }

    static bool ObjValid( DObj ID)
    {
        ObjHeapT::iterator it=objHeap.find( ID);
        return  (it != objHeap.end());
    }

    static SizeT ObjHeapSize()
    {
        return objHeap.size();
    }

//     static DObj FindInObjHeap( BaseGDL** p)
//     {
//         for( ObjHeapT::iterator it=objHeap.begin(); it != objHeap.end(); ++it)
//         {
//             if( &it->second == reinterpret_cast<DStructGDL**>(p))
//                 return it->first;
//         }
//         return 0;
//     }
    static DObjGDL* GetAllObjHeap()
    {
        SizeT nEl = objHeap.size();
        if( nEl == 0) return new DObjGDL( 0);
        DObjGDL* ret = new DObjGDL( dimension( &nEl, 1), BaseGDL::NOZERO);
        SizeT i=0;
        for( ObjHeapT::iterator it=objHeap.begin(); it != objHeap.end(); ++it)
        {
            IncRefObj( it->first);
            (*ret)[ i++] = it->first;
        }
        return ret;
    }

    // no ref counting here
    static std::vector<DObj>* GetAllObjHeapSTL()
    {
        SizeT nEl = objHeap.size();
        if( nEl == 0) return new std::vector<DObj>();
        std::vector<DObj>* ret = new std::vector<DObj>( nEl);
        SizeT i=0;
        for( ObjHeapT::iterator it=objHeap.begin(); it != objHeap.end(); ++it)
        {
            (*ret)[ i++] = it->first;
        }
        return ret;
    }


    static void ResetHeap() // purges both heaps
    {
        for( HeapT::iterator it=heap.begin(); it != heap.end(); ++it)
        {
           delete (*it).second.get();
           heap.erase( it->first); 
        }
        for( ObjHeapT::iterator it=objHeap.begin(); it != objHeap.end(); ++it)
        {
            delete (*it).second.get();
            objHeap.erase( it->first); 
        }
    }

    // name of data
    static const std::string Name( BaseGDL* p) // const
    {
        return callStack.back()->GetString( p);
    }

    static const std::string Name( BaseGDL** p) // const
    {
        assert( *p == NULL);
        DPtr h = FindInHeap( p);
        if( h != 0) return std::string("<PtrHeapVar")+i2s(h)+">";
//         DObj o = FindInObjHeap( p);
//         if( o != 0) return std::string("<ObjHeapVar")+i2s(o)+">";
        return "<(ptr to undefined expression not found on the heap)>";
    }




    // compiler (lexer, parser, treeparser) def in dinterpreter.cpp
    static void ReportCompileError( GDLException& e, const std::string& file = "");

    // interpreter
    static void ReportError( GDLException& e, const std::string emsg, 
                             bool dumpStack=true)
    {
        DString msgPrefix = SysVar::MsgPrefix();

        std::cout << std::flush;
        if( dumpStack)
        if( e.Prefix())
        {
            std::cerr << msgPrefix << e.toString() << std::endl;
            lib::write_journal_comment(msgPrefix+e.toString());
        }
        else
        {
            std::cerr << e.toString() << std::endl;
            lib::write_journal_comment(e.toString());
        }

        std::cerr << msgPrefix << emsg << " " << 
        std::left << std::setw(16) << callStack.back()->GetProName();
        std::string file=callStack.back()->GetFilename();
        if( file != "")
        {
            SizeT line = e.getLine();
            if( line != 0)
            {       
                std::cerr << std::right << std::setw(6) << line;
            }
            else
            {
                std::cerr << std::right << std::setw(6) << "";
            }
            std::cerr << std::left << " " << file;
        }
        std::cerr << std::endl;
        
        if( dumpStack) DumpStack( emsg.size() + 1);
    }
    
    static void DumpStack( SizeT w)
    {
        DString msgPrefix = SysVar::MsgPrefix();

        // EnvStackT::reverse_iterator upEnv = callStack.rbegin();
        // //EnvStackT::reverse_iterator env = upEnv++;
        // upEnv++;
        // for(; 
        //     upEnv != callStack.rend();
        //     ++upEnv /*,++env*/)
        
        long actIx = callStack.size() - 2;
        for( ; actIx >= 0; --actIx)
        {
            EnvStackT::pointer_type upEnv = callStack[ actIx]; 

            std::cerr << msgPrefix << std::right << std::setw( w) << "";
            std::cerr << std::left << std::setw(16) << upEnv->GetProName();

            std::string file = upEnv->GetFilename();
            if( file != "")
            {              
//                 ProgNodeP cNode= (*env)->CallingNode();
//                 if( cNode != NULL)
//                 {       
//                     std::cerr << std::right << std::setw(6) << cNode->getLine();
//                 }
//                 else
//                 {
//                     std::cerr << std::right << std::setw(6) << "";
//                 }                
//                 ProgNodeP cNode= (*env)->CallingNode();
//                 if( cNode != NULL && cNode->getLine() != 0)
//                 {       
//                     (*upEnv)->SetLineNumber( cNode->getLine());
//                 }

                int lineNumber = upEnv->GetLineNumber();
                if( lineNumber != 0)
                {       
                    std::cerr << std::right << std::setw(6) << lineNumber;
                }
                else
                {
                    std::cerr << std::right << std::setw(6) << "";
                }
                std::cerr << std::left << " " << file;
            }
            std::cerr << std::endl;
        }
    }

    static void DebugMsg( ProgNodeP _t, const std::string& msg)
    {    
        DString msgPrefix = SysVar::MsgPrefix();

        std::cout << std::flush;
        std::cerr << msgPrefix << msg
        << std::left << std::setw(16) << callStack.back()->GetProName();
        std::string file=callStack.back()->GetFilename();
        if( file != "")
        {
            ProgNodeP eNode = _t;
            if( eNode != NULL)
            {       
                std::cerr << std::right << std::setw(6) << eNode->getLine();
            }
            else
            {
                std::cerr << std::right << std::setw(6) << "";
            }
            std::cerr << std::left << " " << file;
        }
        std::cerr << std::endl;
    }

    static void RetAll( RetAllException::ExCode c=RetAllException::NONE)    
    {
        throw RetAllException( c);
    }

    static EnvStackT& CallStack() { return callStack;} // the callstack
//    static EnvBaseT*  CallStackBack() { return callStack.back();} 
    static EnvUDT*  CallStackBack() { return callStack.back();} 
    
    std::string GetClearActualLine()
    {
        std::string ret = executeLine.str();
        executeLine.str("");
        return ret;
    }

    RetCode NewInterpreterInstance(SizeT lineOffset); // code in dinterpreter.cpp

    ~GDLInterpreter()
    {
    }
}

//***********************************************************************
// interpreter functions ************************************************
//***********************************************************************

// intercative usage
interactive returns[ RetCode retCode]
// {
// 	return interactive_statement_list(_t);
// }    
//     : retCode=statement_list
//     ;
// // used from interactive nulls line number
// interactive_statement_list returns[ RetCode retCode]
{
	for (; _t != NULL;) {

        //_t->setLine(0);
		retCode=statement(_t);
		_t = _retTree;
			
		if( retCode != RC_OK) break; // break out if non-regular
	}
	_retTree = _t;
	return retCode;
}
    : (retCode=statement
            {
                if( retCode != RC_OK) break; // break out if non-regular
            }
        )+
    ;

// execute statement
execute returns[ RetCode retCode]
{
//    RetCode retCode;
    ValueGuard<bool> guard( interruptEnable);
    interruptEnable = false;

	return statement_list(_t);
}
    : retCode=statement_list
    ;

// used to call functions
// same as statement list, but different behaviour for returncodes
call_fun returns[ BaseGDL* res]
{

    res = NULL;
    assert(returnValue == NULL);
    RetCode retCode;

	for (; _t != NULL;) {

			retCode=statement(_t);
			
// 			if( retCode == RC_RETURN) 
			if( retCode >= RC_RETURN) 
			{
                res=returnValue;
                returnValue=NULL;
                if( returnValueL != NULL)
                    {
                        callStack.back()->SetPtrToReturnValue( returnValueL);
                        returnValueL = NULL;
                    }
			break;
			}					

		_t = _retTree;
	}
	
	// default return value if none was set
	if( res == NULL) res = new DIntGDL( 0); 
	
	_retTree = _t;
	return res;
}
    : (retCode=statement
            {
                if( retCode >= RC_RETURN) 
                {
                    res=returnValue;
                    returnValue=NULL;
                    
                    break;
                }
            }
        )*
        {
            // default return value if none was set
            if( res == NULL) res = new DIntGDL( 0); 
        }
    ;

call_lfun returns[ BaseGDL** res]
{
    res = NULL;
    assert(returnValueL == NULL);
    RetCode retCode;

	ProgNodeP in = _t;

	for (; _t != NULL;) 
    {
        retCode=statement(_t);
        _t = _retTree;
			
        if( retCode >= RC_RETURN) 
			{
                res=returnValueL;
                returnValueL=NULL;
                break;
            }
    }
	
	// default return value if none was set
	if( res == NULL)
        throw GDLException( in, "Function "+callStack.back()->GetProName()+
                            " must return a left-value in this context.",
                            false,false);
	_retTree = _t;
	return res;
}
    : (retCode=statement
        )*
    ;

// used to call procedures
call_pro
{
    RetCode retCode;

	for (; _t != NULL;) {
			retCode=statement(_t);
			_t = _retTree;
			
			// added RC_ABORT here
			if( retCode >= RC_RETURN) break;
	}
	_retTree = _t;
    return;
}
    : (retCode=statement
        )*
    ;


// used on many occasions
statement_list returns[ RetCode retCode]
{
	for (; _t != NULL;) {

		retCode=statement(_t);
		_t = _retTree;
			
		if( retCode != RC_OK) break; // break out if non-regular
	}
	_retTree = _t;
	return retCode;
}
    : (retCode=statement
        )+
    ;

statement returns[ RetCode retCode]
{
//    ProgNodeP& actPos = statement_AST_in;
    assert( _t != NULL);
    ProgNodeP last;
    _retTree = _t;
//  if( callStack.back()->GetLineNumber() == 0) 
//  if( _t->getLine() != 0) 
//      callStack.back()->SetLineNumber( _t->getLine());
}
	:  
        {
            do {
//                 if( _t->getLine() != 0) 
//                     callStack.back()->SetLineNumber( _t->getLine());
                
                last = _retTree;

                // track actual line number
                callStack.back()->SetLineNumber( last->getLine());

                retCode = last->Run(); // Run() sets _retTree
                        
            }
            while( 
                    _retTree != NULL && 
                    retCode == RC_OK && 
                    !(sigControlC && interruptEnable) && 
                    (debugMode == DEBUG_CLEAR));

            // commented out, because we are only at the last statement
            // if( _retTree != NULL) 
            //     last = _retTree;

            goto afterStatement;
}
        (
            // note: assignment must take care to update the owner of lvalue
            // a real copy must be performed (creating a new BaseGDL)  
            ASSIGN            
        |   ASSIGN_ARRAYEXPR_MFCALL
        |   ASSIGN_REPLACE            
        |   PCALL_LIB
        |   MPCALL
        |   MPCALL_PARENT
        |   PCALL
        |   DEC
        |   INC
        |   FOR
        |   FOR_LOOP
        |   FOREACH
        |   FOREACH_LOOP
        |   FOREACH_INDEX
        |   FOREACH_INDEX_LOOP
        |   FOR_STEP
        |   FOR_STEP_LOOP
        |   REPEAT
        |   REPEAT_LOOP
        |   WHILE
        |   IF
        |   IF_ELSE
        |   CASE
        |   SWITCH
        |   BLOCK
        |   LABEL
        |   ON_IOERROR_NULL
        |   ON_IOERROR
        |   BREAK
        |   CONTINUE
        |   GOTO
        |   RETF 
        |   RETP
        )

        // control-c and debugging
        {
           afterStatement:;

           // possible optimization: make sigControlC a debugMode 
           if( interruptEnable && sigControlC)
            {
                DebugMsg( last, "Interrupted at: "); 

                sigControlC = false;

                retCode = NewInterpreterInstance( last->getLine());//-1);
            }
           else if( debugMode != DEBUG_CLEAR)
            {
                if( debugMode == DEBUG_STOP)
                {
                    DebugMsg( last, "Stop encountered: ");
                    if( !interruptEnable)
                        debugMode = DEBUG_PROCESS_STOP;
                }

                if( debugMode == DEBUG_STEP)
                    {
                        if( stepCount == 1)
                            {
                                stepCount = 0;
                                DebugMsg( last, "Stepped to: ");
                                
                                debugMode = DEBUG_CLEAR;
                
                                retCode = NewInterpreterInstance( last->getLine());//-1);
                            }
                        else
                            {
                            --stepCount;
#ifdef GDL_DEBUG
                            std::cout << "stepCount-- = " << stepCount << std::endl;
#endif
                            }
                    }
                // else if( debugMode == DEBUG_SKIP)
                //     {
                //         if( last != NULL)
                //             {
                //                 last = last->getNextSibling();
                //                 DebugMsg( last, "Skipped to: ");
                //             }
                //         else
                //             DebugMsg( last, "Cannot SKIP fro here");

                //         debugMode = DEBUG_CLEAR;
                //         retCode = RC_OK;
                //     }
                else if( interruptEnable)
                {
                    if( debugMode == DEBUG_PROCESS_STOP)
                    {
                        DebugMsg( last, "Stepped to: ");
                    }

                    debugMode = DEBUG_CLEAR;
                
                    retCode = NewInterpreterInstance( last->getLine());//-1);
                }   
                else
                {
                    retCode = RC_ABORT;
                }
            }
           return retCode;
        }
	;
    exception 
    catch [ GDLException& e] 
    { 
        // reset _retTree to last statement
        // (might otherwise be inside an expression in which case 
        // .CONTINUE does not work)
        _retTree = last; 

        if( last->IsWrappedNode())
            throw e; // WRAPPED_... nodes should not stop inside

        // set !ERROR_STATE sys var 
        static DStructDesc* errorStateDesc = SysVar::Error_State()->Desc();
        static unsigned nameTag = errorStateDesc->TagIndex( "NAME");
        static unsigned codeTag = errorStateDesc->TagIndex( "CODE");
        static unsigned msgTag = errorStateDesc->TagIndex( "MSG");

        if( e.IsIOException())
            {
                assert( dynamic_cast< GDLIOException*>( &e) != NULL);
                // set the jump target - also logs the jump
                ProgNodeP onIOErr = 
                    static_cast<EnvUDT*>(callStack.back())->GetIOError();
                if( onIOErr != NULL)
                    {
        DStructGDL* errorState = SysVar::Error_State();
        (*static_cast<DStringGDL*>( errorState->GetTag( nameTag)))[0] = 
            "IDL_M_FAILURE";
        (*static_cast<DLongGDL*>( errorState->GetTag( codeTag)))[0] = 
            e.ErrorCode();
        SysVar::SetErrError( e.ErrorCode());
        (*static_cast<DStringGDL*>( errorState->GetTag( msgTag)))[0] = 
            e.getMessage();
        SysVar::SetErr_String( e.getMessage());

                        _retTree = onIOErr;
                        return RC_OK;
                    }
            }

        // handle CATCH
        ProgNodeP catchNode = callStack.back()->GetCatchNode();
        if( catchNode != NULL)
            {
        DStructGDL* errorState = SysVar::Error_State();
        (*static_cast<DStringGDL*>( errorState->GetTag( nameTag)))[0] = 
            "IDL_M_FAILURE";
        (*static_cast<DLongGDL*>( errorState->GetTag( codeTag)))[0] = 
            e.ErrorCode();
        SysVar::SetErrError( e.ErrorCode());
        (*static_cast<DStringGDL*>( errorState->GetTag( msgTag)))[0] = 
            e.getMessage();
        SysVar::SetErr_String( e.getMessage());

                BaseGDL** catchVar = callStack.back()->GetCatchVar();
                GDLDelete(*catchVar);
                *catchVar = new DLongGDL( e.ErrorCode());
                _retTree = catchNode;
                return RC_OK;
            }

        EnvUDT* targetEnv = e.GetTargetEnv();
        if( targetEnv == NULL)
        {
            // initial exception, set target env
            
        // set !ERROR_STATE here
        DStructGDL* errorState = SysVar::Error_State();
        (*static_cast<DStringGDL*>( errorState->GetTag( nameTag)))[0] = 
            "IDL_M_FAILURE";
        (*static_cast<DLongGDL*>( errorState->GetTag( codeTag)))[0] = 
            e.ErrorCode();
        SysVar::SetErrError( e.ErrorCode());
        (*static_cast<DStringGDL*>( errorState->GetTag( msgTag)))[0] = 
            e.getMessage();
        SysVar::SetErr_String( e.getMessage());

            // look if ON_ERROR is set somewhere
            // for( EnvStackT::reverse_iterator i = callStack.rbegin();
            //     i != callStack.rend(); ++i)
            for( long ix = callStack.size() - 1; ix>=0; --ix)
            {
                EnvUDT** i = &callStack[ ix];
                DLong oE = -1;
                EnvUDT* envUD = dynamic_cast<EnvUDT*>(*i);
                if( envUD != NULL)
                    oE = envUD->GetOnError();
                
                if( oE != -1) 
                { // oE was set
                    
                    // 0 -> stop here
                    if( oE == 0) 
                    targetEnv = static_cast<EnvUDT*>(callStack.back()); 
                    // 1 -> $MAIN$
                    else if( oE == 1) 
                    {
                        EnvUDT* cS_begin = 
                        static_cast<EnvUDT*>(callStack[0]);
                        // static_cast<EnvUDT*>(*callStack.begin());
                        targetEnv = cS_begin;  
                    }
                    // 2 -> caller of routine which called ON_ERROR
                    else if( oE == 2)
                    {
                        // set to caller, handle nested
                        while( ix > 0 && static_cast<EnvUDT*>(callStack[--ix])->GetOnError() == 2)
                            ; // just set ix

                        EnvUDT* iUDT = static_cast<EnvUDT*>(callStack[ix]);
                        targetEnv = iUDT;


                        // while( static_cast<EnvUDT*>(*(++i))->GetOnError() == 2 
                        //        && i != callStack.rend());
                        // if( i == callStack.rend())
                        // {
                        //     EnvUDT* cS_begin = 
                        //     static_cast<EnvUDT*>(*callStack.begin());
                        //     targetEnv = cS_begin;
                        // }
                        // else
                        // {
                        //     EnvUDT* iUDT = static_cast<EnvUDT*>(*i);
                        //     targetEnv = iUDT;
                        // }
                    }   
                    // 3 -> routine which called ON_ERROR
                    else if( oE == 3)
                    {
                        EnvUDT* iUDT = static_cast<EnvUDT*>(callStack[ix]);
                        // EnvUDT* iUDT = static_cast<EnvUDT*>(*i);
                        targetEnv = iUDT;
                    }
                    
                    
                    // State where error occured
//                     if( e.getLine() == 0 && _t != NULL)
//                         e.SetLine( _t->getLine());
//                     if( e.getLine() == 0 && _retTree != NULL)
//                         e.SetLine( _retTree->getLine());
                    if( e.getLine() == 0 && last != NULL)
                        e.SetLine( last->getLine());

                    if( interruptEnable)
                        ReportError(e, "Error occurred at:");

                    // remeber where to stop
                    e.SetTargetEnv( targetEnv);
                    
                    if( targetEnv->GetLineNumber() != 0)
                        e.SetLine( targetEnv->GetLineNumber());                    

//                     ProgNodeP errorNodeP = targetEnv->CallingNode();
//                     e.SetErrorNodeP( errorNodeP);

                    // break on first occurence of set oE
                    break;
                }
            }
        }
        
        if( targetEnv != NULL && targetEnv != callStack.back())
        {
            throw e; // rethrow
        }
        lib::write_journal( GetClearActualLine());

        // many low level routines don't have errorNode info
        // set line number here in this case
//         if( e.getLine() == 0 && _t != NULL)
//             e.SetLine( _t->getLine());
//         if( e.getLine() == 0 && _retTree != NULL)
//             e.SetLine( _retTree->getLine());
//        if( e.getLine() == 0 && actPos != NULL)
//            e.SetLine( actPos->getLine());

        if( interruptEnable)
            {
                if( e.getLine() == 0 && last != NULL)
                    e.SetLine( last->getLine());

                // tell where we are
                ReportError(e, "Execution halted at:", targetEnv == NULL); 

                retCode = NewInterpreterInstance(e.getLine());//-1);
            }    
        else
            {

                DString msgPrefix = SysVar::MsgPrefix();
                if( e.Prefix())
                    {
                        std::cerr << msgPrefix << e.toString() << std::endl;
                        lib::write_journal_comment(msgPrefix+e.toString());
                    }
                else
                    {
                        std::cerr << e.toString() << std::endl;
                        lib::write_journal_comment(e.toString());
                    }

                retCode = RC_ABORT;
            }

        return retCode;
    }





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

l_deref returns [BaseGDL** res]
{
	ProgNodeP retTree = _t->getNextSibling();

    EnvBaseT* actEnv = callStack.back()->GetNewEnv();
    if( actEnv == NULL) actEnv = callStack.back();

    assert( actEnv != NULL);

    Guard<BaseGDL> e1_guard;
    BaseGDL* e1;
    ProgNodeP evalExpr = _t->getFirstChild();
    if( NonCopyNode( evalExpr->getType()))
        {
            e1 = evalExpr->EvalNC();
        }
    else if( evalExpr->getType() ==  GDLTokenTypes::FCALL_LIB)
        {
            e1=lib_function_call_internal(evalExpr);

            if( e1 == NULL) // ROUTINE_NAMES
                throw GDLException( evalExpr, "Undefined return value", true, false);
            
            if( callStack.back()->GetPtrToReturnValueNull() == NULL) 
                {
                    //                if( actEnv != NULL)
                    actEnv->DeleteAtExit( e1); 
                    //                else
                    //                    e1_guard.reset( e1);
                }
        }
    else
        {
            e1 = evalExpr->Eval();

            //      if( actEnv != NULL)
            actEnv->DeleteAtExit( e1); 
            //      else
            //          e1_guard.reset(e1);
        }

    if( e1 == NULL || e1->Type() != GDL_PTR)
        throw GDLException( evalExpr, "Pointer type required"
                            " in this context: "+Name(e1),true,false);

    DPtrGDL* ptr=static_cast<DPtrGDL*>(e1);

    DPtr sc; 
    if( !ptr->Scalar(sc))
        throw GDLException( _t, "Expression must be a "
                            "scalar in this context: "+Name(e1),true,false);
    if( sc == 0)
        throw GDLException( _t, "Unable to dereference"
                            " NULL pointer: "+Name(e1),true,false);
    
    try
        {
            res = &GetHeap(sc);
        }
    catch( HeapException)
        {
            throw GDLException( _t, "Invalid pointer: "+Name(e1),true,false);
        }
    
	_retTree = retTree;
	return res;
}
    : (DEREF //e1=expr 
        )
    ;

// return value from functions when used as l var
// used only from jump_statement and within itself
l_ret_expr returns [BaseGDL** res]
{
    BaseGDL*       e1;
}
    : res=l_deref
    | #(QUESTION e1=expr
            { 
                Guard<BaseGDL> e1_guard(e1);
                if( e1->True())
                {
                    res=l_ret_expr(_t);
                }
                else
                {
                    _t=_t->GetNextSibling(); // jump over 1st expression
                    res=l_ret_expr(_t);
                }
            }
        ) // trinary operator
//    | #(EXPR res=l_ret_expr) // does not exist anymore
    | res=l_arrayexpr_mfcall_as_mfcall
    | res=l_function_call_internal 
        { // here a local to the actual environment could be returned
            if( callStack.back()->IsLocalKW( res))
            throw GDLException( _t, 
                "Attempt to return indirectly a local variable "
                "from left-function.",true,false);
        }
    | varPtr:VARPTR // DNode.var   is ptr to common block variable
        {
            res=&varPtr->var->Data(); // returns BaseGDL* of var (DVar*) 
        }
    | var:VAR // DNode.varIx is index into functions/procedures environment
        {     // check if variable is non-local 
              // (because it will be invalid after return otherwise)
            if( !callStack.back()->GlobalKW(var->varIx))
            throw GDLException( _t, 
                "Attempt to return a non-global variable "
                "from left-function.",true,false);
            
            res=&callStack.back()->GetKW(var->varIx); 
        }
    | // here ASSIGN and ASSIGN_REPLACE are identical
      #(ASSIGN // can it occur at all?
            { 
                Guard<BaseGDL> r_guard;
            } 
            ( e1=tmp_expr
                {
                    r_guard.Init( e1);
                }
            | e1=lib_function_call_internal
                {
                    // if( !callStack.back()->Contains( e1)) 
                    if( callStack.back()->GetPtrToReturnValueNull() == NULL) 
                        r_guard.Init( e1);
                }
            )
            res=l_ret_expr
            {
                if( e1 != (*res))
                    {
                    delete *res;
                    *res = e1;
                    }
                r_guard.release();
            }
        )
    | #(ASSIGN_ARRAYEXPR_MFCALL // here as return value of l_function
            { 
                Guard<BaseGDL> r_guard;
            } 
            ( e1=tmp_expr
                {
                    r_guard.Init( e1);
                }
            | e1=lib_function_call_internal
                {
                    // if( !callStack.back()->Contains( e1)) 
                    if( callStack.back()->GetPtrToReturnValueNull() == NULL) 
                        r_guard.Init( e1);
                }
            )
            res=l_arrayexpr_mfcall_as_mfcall
            {
                if( e1 != (*res))
                    {
                    delete *res;
                    *res = e1;
                    }
                r_guard.release();
            }
        )
    | #(ASSIGN_REPLACE 
            { 
                Guard<BaseGDL> r_guard;
            } 
            ( e1=tmp_expr
                {
                    r_guard.Init( e1);
                }
            | e1=lib_function_call_internal
                {
                    // if( !callStack.back()->Contains( e1)) 
                    if( callStack.back()->GetPtrToReturnValueNull() == NULL) 
                        r_guard.Init( e1);
                }
            )
            res=l_ret_expr
            {
                if( e1 != (*res))
                    {
                    delete *res;
                    *res = e1;
                    }
                r_guard.release();
            }
        )

    // the following are forbiden    
    | #(ARRAYEXPR
            {
                throw GDLException( _t, 
                    "Indexed expression not allowed as left-function"
                    " return value.",true,false);
            }
        )
    | #(DOT 
            {
                throw GDLException( _t, 
                    "Struct expression not allowed as left-function"
                    " return value.",true,false);
            }
        )
    | SYSVAR
        {
            throw GDLException( _t, 
                "System variable not allowed as left-function"
                " return value.",true,false);
        }
    | e1=r_expr
        {
            delete e1;
            throw GDLException( _t, 
                "Expression not allowed as left-function return value.",true,false);
        }
    | CONSTANT //e1=constant_nocopy
        {
            throw GDLException( _t, 
                "Constant not allowed as left-function return value.",true,false);
        }
    ;

// l expressions for DEC/INC ********************************
// called from l_decinc_array_expr
l_decinc_indexable_expr [int dec_inc, BaseGDL*& res] returns [BaseGDL** e]
{
    e = NULL;
}
//     : #(EXPR e = l_expr_internal[ NULL])                       
//         {
//             res = *e;
//             if( res == NULL)
//             throw GDLException( _t, "Variable is undefined: "+Name(e));
//         }
//     | e=l_function_call_internal
    : e=l_function_call_internal
        {
            res = *e;
            if( res == NULL)
            throw GDLException( _t, "Variable is undefined: "+Name(e),true,false);
        }
    | e=l_deref 
        {
            res = *e;
            if( res == NULL)
            throw GDLException( _t, "Variable is undefined: "+Name(e),true,false);
        }
    | e=l_defined_simple_var { res = *e; } // no Dup here
    | e=l_sys_var { res = *e; }            // no Dup here
    ;

// called from l_decinc_expr
l_decinc_array_expr [int dec_inc, BaseGDL*& res] returns [BaseGDL** e]
{
    e = NULL;

    ArrayIndexListT* aL;
    ArrayIndexListGuard guard;

    BaseGDL** ignore;
}
    : #(ARRAYEXPR 
            ignore=l_decinc_indexable_expr[dec_inc, res]   
            aL=arrayindex_list )
        {
            guard.reset( aL); 
            aL->SetVariable( res);

            if( dec_inc == DECSTATEMENT) 
            {
                res->DecAt( aL); 
                break;
            }
            if( dec_inc == INCSTATEMENT)
            {
                res->IncAt( aL);
                break;
            }

            if( dec_inc == DEC || dec_inc == DEC_REF_CHECK) 
                res->DecAt( aL); 
            else if( dec_inc == INC || dec_inc == INC_REF_CHECK) 
                res->IncAt( aL);

            BaseGDL* resBefore = res;
            res = resBefore->Index( aL);

            if( dec_inc == POSTDEC) resBefore->DecAt( aL);
            else if( dec_inc == POSTINC) resBefore->IncAt( aL);
        }
    | e=l_decinc_indexable_expr[dec_inc, res]
        {
            if( dec_inc == DECSTATEMENT || dec_inc == DEC_REF_CHECK) 
            {
                res->Dec(); 
                break;
            }
            if( dec_inc == INCSTATEMENT || dec_inc == INC_REF_CHECK)
            {
                res->Inc();
                break;
            }

            if( dec_inc == DEC) res->Dec();
            else if( dec_inc == INC) res->Inc();
  //          
            BaseGDL* resBefore = res;
            res = resBefore->Dup();
            
            if( dec_inc == POSTDEC) resBefore->Dec();
            else if( dec_inc == POSTINC) resBefore->Inc();
        }
    ;

// struct assignment
// MAIN function: called from l_decinc_expr
l_decinc_dot_expr [int dec_inc, BaseGDL*& res] returns [BaseGDL** refRet]
{ 
    refRet = NULL; 
}
    : #(dot:DOT 
            { 
                SizeT nDot=dot->nDot;
                Guard<DotAccessDescT> aD( new DotAccessDescT(nDot+1));
            } 
            l_dot_array_expr[ aD.Get()] 
            (tag_array_expr[ aD.Get()] /* nDot times*/ )+ 
        )         
        {
            if( dec_inc == DECSTATEMENT) 
            {
                aD.Get()->Dec(); 
            }
            else if( dec_inc == INCSTATEMENT)
            {
                aD.Get()->Inc();
            }
            else
            {
                if( dec_inc == DEC || dec_inc == DEC_REF_CHECK) 
                    aD.Get()->Dec(); //*** aD->Assign( dec_inc);
                else if( dec_inc == INC || dec_inc == INC_REF_CHECK) 
                    aD.Get()->Inc();
//                
                res=aD.Get()->ADResolve();
                
                if( dec_inc == POSTDEC) aD.Get()->Dec();
                else if( dec_inc == POSTINC) aD.Get()->Inc();
            }
        }
    ;

// l_decinc_expr is only used in dec/inc statements and within itself
l_decinc_expr [int dec_inc, BaseGDL*& res] returns [BaseGDL** refRet]
{
    refRet = NULL;

    BaseGDL*       e1;
    ProgNodeP startNode = _t;
}
    : #(QUESTION e1=expr
            { 
                Guard<BaseGDL> e1_guard(e1);

                if( e1->True())
                {
                    refRet=l_decinc_expr(_t, dec_inc, res);
                }
                else
                {
                    _t=_t->GetNextSibling(); // jump over 1st expression
                    refRet=l_decinc_expr(_t, dec_inc, res);
                }
            }
        ) // trinary operator
    | #(ASSIGN 
            { 
                Guard<BaseGDL> r_guard;
            } 
//             ( e1=tmp_expr
//                 {
//                     r_guard.reset( e1);
//                 }
//             | e1=lib_function_call_internal
//                 {
//                     if( !callStack.back()->Contains( e1)) 
//                         r_guard.reset( e1);
//                 }
//             )
            ( e1=indexable_expr
            | e1=indexable_tmp_expr { r_guard.Init( e1);}
            | e1=lib_function_call_internal
                {
                    // if( !callStack.back()->Contains( e1)) 
                    if( callStack.back()->GetPtrToReturnValueNull() == NULL) 
                        r_guard.Init( e1); // guard if no global data
                }
            )
            { 
                ProgNodeP l = _t;

                BaseGDL** tmp;
            } 
            tmp=l_expr_internal[ e1] // assign
            {
                _t = l;
            }
            refRet=l_decinc_expr[dec_inc, res]
        )
    | #(ASSIGN_ARRAYEXPR_MFCALL
            { 
                Guard<BaseGDL> r_guard;
            } 
            ( e1=indexable_expr
            | e1=indexable_tmp_expr { r_guard.Init( e1);}
            | e1=lib_function_call_internal
                {
                    // if( !callStack.back()->Contains( e1)) 
                    if( callStack.back()->GetPtrToReturnValueNull() == NULL) 
                        r_guard.Init( e1); // guard if no global data
                }
            )
            { 
                ProgNodeP l = _t;

                BaseGDL** tmp;

                // try MFCALL
                try
                {
    
                    tmp=l_arrayexpr_mfcall_as_mfcall(l);
    
                    if( e1 != (*tmp))
                    {
                        delete *tmp;

                        if( r_guard.Get() == e1)
                            *tmp = r_guard.release();
                        else          
                            *tmp = e1->Dup();
                    }

                    refRet=l_decinc_expr( l, dec_inc, res);
                }
                catch( GDLException& ex)
                {
                // try ARRAYEXPR
                    try
	                {
                        tmp=l_arrayexpr_mfcall_as_arrayexpr(l, e1);
	                }
                    catch( GDLException& ex2)
                    {
                        throw GDLException(ex.toString() + " or "+ex2.toString());
                    }

                    refRet=l_decinc_expr( l, dec_inc, res);
                }
            }
        )
    | #(ASSIGN_REPLACE 
            { 
                Guard<BaseGDL> r_guard;
            } 
            ( e1=tmp_expr
                {
                    r_guard.Init( e1);
                }
            | e1=lib_function_call_internal
                {
                    // if( !callStack.back()->Contains( e1)) 
                    if( callStack.back()->GetPtrToReturnValueNull() == NULL) 
                        r_guard.Init( e1);
                }
            )
            { 
                ProgNodeP l = _t;

                BaseGDL** tmp;
            } 
//            tmp=l_expr_internal[ e1] // assign
            (
              tmp=l_function_call_internal   // FCALL_LIB, MFCALL, MFCALL_PARENT, FCALL
            | tmp=l_deref           // DEREF
            | tmp=l_simple_var      // VAR, VARPTR
            )
        {
            if( e1 != (*tmp))
            {
                delete *tmp;

                if( r_guard.Get() == e1)
                  *tmp = r_guard.release();
                else  
                  *tmp = e1->Dup();
            }
        }
            {
                _t = l;
            }
            refRet=l_decinc_expr[dec_inc, res]
        )
    | refRet=l_decinc_array_expr[dec_inc, res]
    | #(ARRAYEXPR_MFCALL
        {
            ProgNodeP mark = _t;
            _t = _t->getNextSibling(); // step over DOT

            BaseGDL* self;
        }            
        //BaseGDL** e = l_arrayexpr_mfcall_as_mfcall( _t);
        self=expr mp2:IDENTIFIER
        {  
                Guard<BaseGDL> self_guard(self);
        
                EnvUDT* newEnv;

                DObjGDL* selfObj = NULL;
                if( self->Type() == GDL_OBJ)
                    selfObj = static_cast<DObjGDL*>( self);
                try {
                    newEnv=new EnvUDT( selfObj, mp2, "", EnvUDT::LFUNCTION);
                    self_guard.release();
                }
                catch( GDLException& ex)
                {
                    _t = mark;

                    refRet=l_decinc_dot_expr(_t, dec_inc, res);

                    _retTree = startNode->getNextSibling();
                    return refRet;
                }   
        }    
        parameter_def[ newEnv]
        {
            // push environment onto call stack
            callStack.push_back(newEnv);
            
            // make the call
            BaseGDL** ee=call_lfun(static_cast<DSubUD*>(
                                  newEnv->GetPro())->GetTree());

            BaseGDL* e = *ee;
            if( e == NULL)
                throw GDLException( _t, "Variable is undefined: "+Name(ee),true,false);

            if( dec_inc == DECSTATEMENT || dec_inc == DEC_REF_CHECK) 
                {
                    e->Dec(); 
                    _retTree = startNode->getNextSibling();
                    return ee;
                }
            if( dec_inc == INCSTATEMENT || dec_inc == INC_REF_CHECK)
                {
                    e->Inc();
                    _retTree = startNode->getNextSibling();
                    return ee;
                }

            if( dec_inc == DEC) e->Dec();
            else if( dec_inc == INC) e->Inc();
  //          
            res = e->Dup();
            
            if( dec_inc == POSTDEC) e->Dec();
            else if( dec_inc == POSTINC) e->Inc();

            _retTree = startNode->getNextSibling();
            return ee;
        }   
        )
    | refRet=l_decinc_dot_expr[dec_inc, res]
    | e1=r_expr
        {
            delete e1;
            throw GDLException( _t, 
                "Expression not allowed with decrement/increment operator.",true,false);
        }
    | CONSTANT //e1=constant_nocopy
        {
            throw GDLException( _t, 
                "Constant not allowed with decrement/increment operator.",true,false);
        }
    ;

// l expressions for assignment *************************

// an indexable expression must be defined
l_indexable_expr returns [BaseGDL** res]
{
    res = _t->LEval();
    if( *res == NULL) 
        {
            // check not needed for SYSVAR 
            assert( _t->getType() != SYSVAR);
            if( _t->getType() == VARPTR)
                throw GDLException( _t, "Common block variable is undefined: "+
                                    callStack.back()->GetString( *res),true,false);
            if( _t->getType() == VAR)
                throw GDLException( _t, "Variable is undefined: "+
                               callStack.back()->GetString(_t->varIx),true,false);
            throw GDLException( _t, "Variable is undefined: "+Name(res),true,false);
        }
   _retTree = _t->getNextSibling();
	return res;
}
    : #(EXPR res=l_expr_internal[ NULL]) // for l_dot_array_expr
    | res=l_function_call_internal
    | res=l_arrayexpr_mfcall_as_mfcall 
    | res=l_deref
    | res=l_defined_simple_var                         
    | res=l_sys_var 
    ;

// called from l_expr_internal
unused_l_array_expr [BaseGDL* right] returns [BaseGDL** res]
//{
//    ArrayIndexListT* aL;
//}
//    : #(ARRAYEXPR res=l_indexable_expr aL=arrayindex_list) 
    : ARRAYEXPR
    ;

l_dot_array_expr [DotAccessDescT* aD] // 1st
{
    ArrayIndexListT* aL;
    BaseGDL**        rP;
	
	if( _t->getType() == ARRAYEXPR)
	{
		rP=l_indexable_expr(_t->getFirstChild());
		aL=arrayindex_list(_retTree);

		_retTree = _t->getNextSibling();
        
        SetRootL( _t, aD, *rP, aL); 
	}
    else
	// case ARRAYEXPR_MFCALL:
	// case DEREF:
	// case EXPR:
	// case FCALL:
	// case FCALL_LIB:
	// case MFCALL:
	// case MFCALL_PARENT:
	// case SYSVAR:
	// case VAR:
	// case VARPTR:
	{
		rP=l_indexable_expr(_t);

        SetRootL( _t, aD, *rP, NULL); 
	}
    return;
//	_retTree = _t;
}
    : #(ARRAYEXPR rP=l_indexable_expr aL=arrayindex_list)   
    | rP=l_indexable_expr
    ;


// l_expr_internal is only used in assignment and within itself
l_expr_internal [BaseGDL* right] returns [BaseGDL** res]
{
    res = _t->LExpr( right);
    SetRetTree( _t->getNextSibling());
    return res;
    
    BaseGDL* e1; 
}
    : #(QUESTION e1=expr
        ) // trinary operator
    | res=unused_l_array_expr[ right]
    | res=l_sys_var // sysvars cannot change their type
    | // can be called via QUESTION
       ( res=l_function_call_internal   // FCALL_LIB, MFCALL, MFCALL_PARENT, FCALL
       | res=l_deref           // DEREF
       | res=l_simple_var      // VAR, VARPTR
       )
    | res=l_arrayexpr_mfcall[ right]
//  | res=l_dot_expr[ right]
    | #(dot:DOT  // struct assignment
            l_dot_array_expr[ NULL] //aD.get()] 
           // (tag_array_expr[ aD.get()] /* nDot times*/ )+ 
        )         
    | #(ASSIGN //???
            // just calculate because of side effects
            ( e1=indexable_expr
            | e1=indexable_tmp_expr { delete e1;}
            | e1=lib_function_call_internal
                // {
                //     if( !callStack.back()->Contains( e1)) 
                //         delete e1; // guard if no global data
                // }
            )
            // here the only relevant assingment is done
            res=l_expr_internal[ right]
        )
    | #(ASSIGN_ARRAYEXPR_MFCALL
            ( e1=indexable_expr
            | e1=indexable_tmp_expr //{ delete e1;}
            | e1=lib_function_call_internal
            )
        )
    | #(ASSIGN_REPLACE //???e1=expr
            ( e1=tmp_expr
            | e1=lib_function_call_internal
            )
            (
              res=l_function_call_internal   // FCALL_LIB, MFCALL, MFCALL_PARENT, FCALL
            | res=l_deref           // DEREF
            | res=l_simple_var      // VAR, VARPTR
            )
        )
//    | { right == NULL}? res=l_function_call_internal
    | e1=r_expr // illegal
    | CONSTANT // illegal
    ;

// used in l_expr_internal but also in parameter_def
l_simple_var returns [BaseGDL** res]
{
	assert( _t != NULL);
    res = _t->LEval();    
    _retTree = _t->getNextSibling();
    return res;

}
    : VAR // DNode.varIx is index into functions/procedures environment
    | VARPTR // DNode.var   is ptr to common block variable
    ;

l_defined_simple_var returns [BaseGDL** res]
{
	assert( _t != NULL);
    res = _t->LEval();
    _retTree = _t->getNextSibling();
	if( *res == NULL)
        {
            if( _t->getType() == VAR)
                throw GDLException( _t, "Variable is undefined: "+
                                    callStack.back()->GetString(_t->varIx),true,false);
            else
                throw GDLException( _t, "Common block variable is undefined: "+
                                    callStack.back()->GetString( *res),true,false);
        }
    return res;

}
    : VAR // DNode.varIx is index into functions/procedures environment
//         {
//             res=&callStack.back()->GetKW(var->varIx); 
//             if( *res == NULL)
//             throw GDLException( _t, "Variable is undefined: "+
//                 callStack.back()->GetString(var->varIx),true,false);
//         }
    | VARPTR // DNode.var   is ptr to common block variable
//         {
//             res=&varPtr->var->Data(); // returns BaseGDL* of var (DVar*) 
//             if( *res == NULL)
//             throw GDLException( _t, "Variable is undefined: "+
//                 callStack.back()->GetString( *res),true,false);
//         }
    ;

l_sys_var returns [BaseGDL** res]
{
	ProgNodeP sysVar = _t;
	//match(antlr::RefAST(_t),SYSVAR);
	res=sysVar->LEval();
	_retTree = sysVar->getNextSibling();
	return res;

}
    : SYSVAR  
        // {
        //     res=sysVar->LEval();
        //     _retTree = sysVar->getNextSibling();
        // }
    ;

// right expressions **********************************************************
// expressions which always only return a value
// expecting to delete any sub-expressions
r_expr returns [BaseGDL* res]
{
    res=_t->Eval();
	_retTree = _t->getNextSibling();
	return res;

    BaseGDL** refRet; // not used
}
    : EXPR
    | ARRAYDEF
    | STRUC
    | NSTRUC
    | NSTRUC_REF
    |	#(DEC refRet=l_decinc_expr[ DEC, res])
    |	#(INC refRet=l_decinc_expr[ INC, res])
    |	#(POSTDEC refRet=l_decinc_expr[ POSTDEC, res])
    |	#(POSTINC refRet=l_decinc_expr[ POSTINC, res])
    ;


// for l and r expr
tag_expr [DotAccessDescT* aD] // 2nd...
{
    BaseGDL* e;
	
	if( _t->getType() == EXPR)
	{
		ProgNodeP tIn = _t;
		_t = _t->getFirstChild();
		e=expr(_t);
		
		Guard<BaseGDL> e_guard(e);
		
		SizeT tagIx;
		int ret=e->Scalar2Index(tagIx);
		if( ret < 1) // this is a return code, not the index
            throw GDLException( tIn, "Expression must be a scalar"
                                " >= 0 in this context: "+Name(e),true,false);
		aD->ADAdd( tagIx);

		_retTree = tIn->getNextSibling();
	}
    else
	// case IDENTIFIER:
	{
        assert( _t->getType() == IDENTIFIER);
		std::string tagName=_t->getText();

		aD->ADAdd( tagName);

		_retTree = _t->getNextSibling();		
	}
    return;
}
    : #(EXPR e=expr
        )                       
    | IDENTIFIER
    ;

// for l and r expr
tag_array_expr  [DotAccessDescT* aD] // 2nd...
{
    ArrayIndexListT* aL;
	
	if( _t->getType() == ARRAYEXPR)
	{
		ProgNodeP tIn = _t;
		_t = _t->getFirstChild();
		tag_expr(_t, aD);
		_t = _retTree;
		aL=arrayindex_list(_t);
		_t = _retTree;
		aD->ADAddIx(aL);
		_retTree = tIn->getNextSibling();
	}
    else
	// case EXPR:
	// case IDENTIFIER:
	{
		tag_expr(_t, aD);
		//_t = _retTree;
		aD->ADAddIx(NULL);
	}
	//_retTree = _t;
    return;
}
	: #(ARRAYEXPR tag_expr[ aD] aL=arrayindex_list /*{ aD->ADAddIx(aL);}*/ )
    | tag_expr[ aD] //{ aD->ADAddIx(NULL);} 
    ;

r_dot_indexable_expr [DotAccessDescT* aD] returns [BaseGDL* res] // 1st
{
	switch ( _t->getType()) {
	case EXPR:
	{
		ProgNodeP tIn = _t;
		_t = _t->getFirstChild();
		//res=expr(_t);
        res = _t->Eval();
		aD->SetOwner( true);
		_retTree = tIn->getNextSibling();
		break;
	}
	case VAR:
	case VARPTR:
	{
		BaseGDL** v=l_defined_simple_var(_t);
		//_t = _retTree;
		res = *v;
		break;
	}
	case SYSVAR:
	{
        res = _t->EvalNC();
        _retTree = _t->getNextSibling();
		//res=sys_var_nocopy(_t);
		//_t = _retTree;
		break;
	}
	}
	//_retTree = _t;
	return res;

    BaseGDL** e;
}
    : #(EXPR res=expr { aD->SetOwner( true);}) // ({tag:0}).tag should work 
    | e=l_defined_simple_var 
    |   // we cant use l_sys_var here because of copy protection
        // could use sysvar and SetOwner( true), but this is quicker
        SYSVAR //res=sys_var_nocopy // system variables are always defined    
    ;

r_dot_array_expr [DotAccessDescT* aD] // 1st
{
    BaseGDL*         r;
    ArrayIndexListT* aL;
	
	if( _t->getType() == ARRAYEXPR)
	{
		//match(antlr::RefAST(_t),ARRAYEXPR);
		r=r_dot_indexable_expr(_t->getFirstChild(), aD);
		aL=arrayindex_list(_retTree);
		_retTree = _t->getNextSibling();
		
		// check here for object and get struct
		SetRootR( _t, aD, r, aL); 
	}
    else
	{
		r=r_dot_indexable_expr(_t, aD);
		//_t = _retTree;
		
		// check here for object and get struct
		SetRootR( _t, aD, r, NULL); 
	}
    return;
}
// NOTE: r is owned by aD or a l_... (r must not be deleted here)
    : #(ARRAYEXPR r=r_dot_indexable_expr[ aD] 
            aL=arrayindex_list /*{ guard.reset(aL);}*/ )   
        {
            // check here for object and get struct
            SetRootR( _t, aD, r, aL); 
        }
    | r=r_dot_indexable_expr[ aD]
        {
            // check here for object and get struct
            SetRootR( _t, aD, r, NULL); 
        }
    ;

// Entry function for struct access
//#(DOT array_expr (tag_array_expr)+)                     
dot_expr_unused returns [BaseGDL* res]
{
    res = _t->Eval();
    _retTree = _t->getNextSibling();
    return res;
}
    : #(DOT 
            r_dot_array_expr[ NULL]  
            (tag_array_expr[ NULL] /* nDot times*/ )+ 
        )         
    ;

// indexable expressions are used to minimize copying of data
// ( via Dup())
// owned by caller
indexable_tmp_expr returns [BaseGDL* res]
{
     res = _t->Eval(); //lib_function_call_retnew_internal(_t);
	_retTree = _t->getNextSibling();
    return res;
//    BaseGDL*  e1;
//    BaseGDL* res;
//    ProgNodeP q = ProgNodeP(antlr::nullAST);
//    ProgNodeP a = ProgNodeP(antlr::nullAST);
    
    switch ( _t->getType()) {
      case QUESTION:
      // {
	  //     res = _t->Eval();
	  //     _retTree = _t->getNextSibling();
	  //     break;
      // }
      case ARRAYEXPR:
      // {
	  //     res = _t->Eval();
	  //     _retTree = _t->getNextSibling();
	  //     break;
      // }
      case ARRAYEXPR_MFCALL:
      case FCALL:
      case MFCALL:
      case MFCALL_PARENT:
  //     {
  //         res=_t->Eval(); //function_call(_t);
  //         _retTree = _t->getNextSibling();
  // //	    _t = _retTree;
  //         break;
  //     }
      case ARRAYDEF:
      case EXPR:
      case NSTRUC:
      case NSTRUC_REF:
      case POSTDEC:
      case POSTINC:
      case STRUC:
      case DEC:
      case INC:
  //     {
  //         res=_t->Eval(); //r_expr(_t);
  //         _retTree = _t->getNextSibling();
  // //	    _t = _retTree;
  //         break;
  //     }
      case DOT:
  //     {
  //         res=_t->Eval(); //dot_expr(_t);
  //         _retTree = _t->getNextSibling();
  // //	    _t = _retTree;
  //         break;
  //     }
      case ASSIGN:
      case ASSIGN_REPLACE:
      case ASSIGN_ARRAYEXPR_MFCALL:
  //     {
  //         res=_t->Eval(); //assign_expr(_t);
  //         _retTree = _t->getNextSibling();
  // //	    _t = _retTree;
  //         break;
  //     }
      case FCALL_LIB_RETNEW:
      {
	      res=_t->Eval(); //lib_function_call_retnew_internal(_t);
	      _retTree = _t->getNextSibling();
  //	    _t = _retTree;
	      break;
      }
    }
//    _retTree = _t;
    return res;
}
	: #(q:QUESTION { res=q->Eval();}) // trinary operator
    | (ARRAYEXPR) //res=array_expr
    | res=dot_expr_unused
    | res=assign_expr
    | res=unused_function_call
    | res=r_expr
    | res=lib_function_call_retnew_internal 
    ;

// not owned by caller 
indexable_expr returns [BaseGDL* res]
{
    res = _t->EvalNC();
    _retTree = _t->getNextSibling();
    return res;

    BaseGDL** e2;
}
    : e2=l_defined_simple_var
        {
            res = *e2;
        }
    | res=sys_var_nocopy
    | CONSTANT //res=constant_nocopy
    | e2=l_deref 
    ;


// l_expr_internal used as r_expr and true r_expr
expr returns [BaseGDL* res]
{
	assert( _t != NULL);

    res = _t->Eval();
    _retTree = _t->getNextSibling();
    return res; //tmp_expr(_t);

EnvT*   newEnv;
}
    : res=tmp_expr
    | res=lib_function_call_internal
    ;    


// check_expr returns [BaseGDL* res]
//     : res=lib_function_call_internal 
//     ;

// l_expr_internal used as r_expr and true r_expr
tmp_expr returns [BaseGDL* res]
{
 	res = _t->Eval();
 	_retTree = _t->getNextSibling();
    return res;

    BaseGDL** e2;
} // tmp_expr
    : e2=l_deref 
	| #(q:QUESTION {res=q->Eval();}) // trinary operator
    | (ARRAYEXPR) //res=array_expr
//    | res=array_expr
    | res=dot_expr_unused
    | res=assign_expr
    | res=unused_function_call
    | res=r_expr
    | CONSTANT
        // *********
    | res=simple_var                         
    | res=sys_var 
    | res=lib_function_call_retnew_internal 
    ;

assign_expr returns [BaseGDL* res]
{
    res = _t->Eval();
	_retTree = _t->getNextSibling();
    return res;

    BaseGDL** l;
}
    : #(ASSIGN 
            ( res=tmp_expr
            | res=lib_function_call_internal
            )
            l=l_expr_internal[ res]
        )
    | #(ASSIGN_ARRAYEXPR_MFCALL
            ( res=tmp_expr
            | res=lib_function_call_internal
            )
            l=l_expr_internal[ res]
        )
    | #(ASSIGN_REPLACE 
            ( res=tmp_expr
            | res=lib_function_call_internal
            )
            (
              l=l_function_call_internal   // FCALL_LIB, MFCALL, MFCALL_PARENT, FCALL
            | l=l_deref           // DEREF
            | l=l_simple_var      // VAR, VARPTR
            )
        )
    ;



simple_var returns [BaseGDL* res]
{
	assert( _t != NULL);
    BaseGDL* vData = _t->EvalNC();
	if( vData == NULL)
        {
        if( _t->getType() == VAR)
            throw GDLException( _t, "Variable is undefined: "+var->getText(),true,false);
        else // VARPTR
            throw GDLException( _t, "Common block variable is undefined.",true,false);
        }
	_retTree = _t->getNextSibling();
	return vData->Dup();
}
    : var:VAR // DNode.varIx is index into functions/procedures environment
    | varPtr:VARPTR // DNode.var   is ptr to common block variable
    ;

sys_var returns [BaseGDL* res]
{
    res = _t->Eval();
	_retTree = _t->getNextSibling();
	return res; // no ->Dup()
}
    : res=sys_var_nocopy 
    ;

sys_var_nocopy returns [BaseGDL* res]
{
    res = _t->EvalNC();
	_retTree = _t->getNextSibling();
	return res; // no ->Dup()
}
    : SYSVAR 
    ;

// constant returns [BaseGDL* res]
// {
//     res = _t->Eval();
// 	_retTree = _t->getNextSibling();
// 	return res; //_t->cData->Dup(); 
// }
//   : CONSTANT
// //         {
// //             res=c->cData->Dup(); 
// //         }
//   ;


lib_function_call_internal returns[ BaseGDL* res]
{ 
    assert( _t->getType() == FCALL_LIB);
 	BaseGDL** retValPtr;
 	res = static_cast<FCALL_LIBNode*>(_t)->EvalFCALL_LIB(retValPtr); 
 	_retTree = _t->getNextSibling();

    callStack.back()->SetPtrToReturnValue( retValPtr); 
    return res;

    EnvT*   newEnv;
}
	: #(FCALL_LIB //fll:IDENTIFIER
            parameter_def[ newEnv]
        )
    ;    

lib_function_call_retnew_internal returns[ BaseGDL* res]
{ 
    res = _t->Eval();
	_retTree = _t->getNextSibling();
	return res; //_t->cData->Dup(); 

    EnvT*   newEnv;
}
	: #(FCALL_LIB_RETNEW //fll:IDENTIFIER
            parameter_def[ newEnv]
        )
    ;    


unused_function_call returns[ BaseGDL* res]
{ 
    // better than auto_ptr: auto_ptr wouldn't remove newEnv from the stack
    StackGuard<EnvStackT> guard(callStack);
    BaseGDL *self;
    EnvUDT*  newEnv;
    ProgNodeP startNode = _t;
    ProgNodeP mark;
}
    : ( 
          #(MFCALL 
                self=expr mp:IDENTIFIER
                parameter_def[ newEnv]
            )
        | #(MFCALL_PARENT 
                self=expr parent:IDENTIFIER p:IDENTIFIER
                parameter_def[ newEnv]
            )
        | #(f:FCALL //f:IDENTIFIER
                parameter_def[ newEnv]
            )
        | #(ARRAYEXPR_MFCALL
            self=expr mp2:IDENTIFIER
            parameter_def[ newEnv]
            )
        )        
	;	



l_arrayexpr_mfcall [BaseGDL* right] returns [BaseGDL** res]
{ 
    // better than auto_ptr: auto_ptr wouldn't remove newEnv from the stack
    StackGuard<EnvStackT> guard(callStack);
    BaseGDL *self;
    EnvUDT*  newEnv;
    ProgNodeP startNode = _t;
}
    : #(ARRAYEXPR_MFCALL
        {
            ProgNodeP mark = _t;
            _t = _t->getNextSibling(); // skip DOT
        }

        self=expr mp2:IDENTIFIER

        {  
            Guard<BaseGDL> self_guard(self);
        
            try {
                newEnv=new EnvUDT( self, mp2, "", EnvUDT::LFUNCTION);
                self_guard.release();
            }
            catch( GDLException& ex)
            {
                goto tryARRAYEXPR;
            }
        }    

            parameter_def[ newEnv]
        )
        {
            // push environment onto call stack
            callStack.push_back(newEnv);
            
            // make the call
            res=call_lfun(static_cast<DSubUD*>(
                    newEnv->GetPro())->GetTree());

            _retTree = startNode->getNextSibling();
            return res;

            tryARRAYEXPR:;
            _t = mark;
        }   
        #(dot:DOT  // struct assignment
            { 
                SizeT nDot=dot->nDot;
                Guard<DotAccessDescT> aD( new DotAccessDescT(nDot+1));
            } 
            l_dot_array_expr[ aD.Get()] 
            (tag_array_expr[ aD.Get()] /* nDot times*/ )+ 
        )
        {
            if( right == NULL)
            throw GDLException( _t, 
                                "Struct expression not allowed in this context.",
                                true,false);
            
            aD.Get()->ADAssign( right);

            res=NULL;

            _retTree = startNode->getNextSibling();
            return res;
        }
    ;



l_arrayexpr_mfcall_as_arrayexpr [BaseGDL* right] returns [BaseGDL** res]
    : #(ARRAYEXPR_MFCALL
            #(dot:DOT  // struct assignment
            { 
                SizeT nDot=dot->nDot;
                Guard<DotAccessDescT> aD( new DotAccessDescT(nDot+1));
            } 
            l_dot_array_expr[ aD.Get()] 
            (tag_array_expr[ aD.Get()] /* nDot times*/ )+ 
            )         
        )
        {
            if( right == NULL)
            throw GDLException( _t, 
                                "Struct expression not allowed in this context.",
                                true,false);
            
            aD.Get()->ADAssign( right);

            res=NULL;
        }
    ;

// function call can be l_values (within (#EXPR ...) only)
l_arrayexpr_mfcall_as_mfcall returns[ BaseGDL** res]
{ 
    // better than auto_ptr: auto_ptr wouldn't remove newEnv from the stack
    StackGuard<EnvStackT> guard(callStack);
    BaseGDL *self;
    EnvUDT*   newEnv;
}
    : #(ARRAYEXPR_MFCALL 
                {
                   _t = _t->getNextSibling(); // skip DOT
                }
 
                self=expr mp2:IDENTIFIER
                {  
                    Guard<BaseGDL> self_guard(self);
                    
                    newEnv=new EnvUDT( self, mp2, "", EnvUDT::LFUNCTION);

                    self_guard.release();
                }
                parameter_def[ newEnv]
        )
        {
            // push environment onto call stack
            callStack.push_back(newEnv);
            
            // make the call
            res=call_lfun(static_cast<DSubUD*>(
                    newEnv->GetPro())->GetTree());
        }   
	;	

// function call can be l_values (within (#EXPR ...) only)
l_function_call_internal returns[ BaseGDL** res]
{ 
    res = _t->LEval();
    _retTree = _t->getNextSibling();
    return res;

    BaseGDL *self;
    EnvUDT*   newEnv;
}

	: #(FCALL_LIB //fl:IDENTIFIER
            parameter_def[ newEnv]
        )
    |
        (
        ( #(MFCALL 
                self=expr IDENTIFIER
                parameter_def[ newEnv]
            )
        | #(MFCALL_PARENT 
                self=expr IDENTIFIER IDENTIFIER
                parameter_def[ newEnv]
            )

        | #(FCALL //f:IDENTIFIER
                parameter_def[ newEnv]
            )
        )
        )
	;	

// for N_ELEMENTS() the environment is (as well) not on the callstack
// needed because N_ELEMENTS must handle undefined variables different
parameter_def_n_elements [EnvBaseT* actEnv] 
{
    Guard<EnvBaseT> guard(actEnv); 
    _retTree = _t;
//     bool interruptEnableIn = interruptEnable;
    if( _retTree != NULL)
        {
            int nPar = _retTree->GetNParam();
            //int nSub = actEnv->GetPro()->NPar();
            assert(  actEnv->GetPro()->NPar() == 1); // N_ELEMENTS
            // fixed number of parameters
            if( nPar > 1)//nSub) // check here
                {
                    throw GDLException( _t, actEnv->GetProName() +
                                        ": Incorrect number of arguments.",
                                        false, false);
                }

            if( _retTree->getType() == REF ||
                _retTree->getType() == REF_EXPR ||
                _retTree->getType() == REF_CHECK ||
                _retTree->getType() == PARAEXPR)
                {
                    try
                        {
                            //                     interruptEnable = false;
                            static_cast<ParameterNode*>(_retTree)->Parameter( actEnv);
                            //                     interruptEnable = interruptEnableIn;
                        } 
                    catch( GDLException& e)
                        {
                            // an error occured -> parameter is undefined 
                            //                         interruptEnable = interruptEnableIn;
                            if( actEnv->NParam() == 0) // not set yet
                                {
                                    BaseGDL* nullP = NULL;
                                    actEnv->SetNextPar( nullP);
                                }
                        }
                }
            else // used for error handling: keywords are checked only here in Parameter()
                {
                    try
                        {
                            // as N_ELEMENTS has no keywords this should throw always
                            static_cast<ParameterNode*>(_retTree)->Parameter( actEnv);
                            assert( 0);
                        }
                    catch( GDLException& e)
                        {
                            // update line number, currently set to caller->CallingNode()
                            // because actEnv is not on the stack yet, 
                            // report caller->Pro()'s name is ok, because we are not inside
                            // the call yet
                            e.SetErrorNodeP( actEnv->CallingNode());
                            throw e;
                        }
                }
            // actEnv->Extra(); // expand _EXTRA
        } // if( _retTree != NULL)

	guard.release();
	
    return;
}
    : #(KEYDEF_REF_EXPR IDENTIFIER //ref_parameter
        )             
;

// the environment is not on the callstack
parameter_def [EnvBaseT* actEnv] 
{
    // as actEnv is not on the stack guard it here
    Guard<EnvBaseT> guard(actEnv); 

    EnvBaseT* callerEnv = callStack.back();
    EnvBaseT* oldNewEnv = callerEnv->GetNewEnv();

    callerEnv->SetNewEnv( actEnv);

    try{

        _retTree = _t;
        if( _retTree != NULL)
            {
                int nPar = _retTree->GetNParam();
                int nSub = actEnv->GetPro()->NPar();
                // // variable number of parameters
                // if( nSub == -1)
                //     {
                //         // _retTree != NULL, save one check
                //         static_cast<ParameterNode*>(_retTree)->Parameter( actEnv);
                //         while(_retTree != NULL) 
                //             static_cast<ParameterNode*>(_retTree)->Parameter( actEnv);
                //     }
                // // fixed number of parameters
                if( nSub != -1 && nPar > nSub) // check here
                    {
                    throw GDLException( _t, actEnv->GetProName() +
                                        ": Incorrect number of arguments.",
                                        false, false);
                    }
                else
                    {
                        // _retTree != NULL, save one check
                        static_cast<ParameterNode*>(_retTree)->Parameter( actEnv);
                        // Parameter does no checking
                        while(_retTree != NULL) 
                            static_cast<ParameterNode*>(_retTree)->Parameter( actEnv);
                    }    
                actEnv->ResolveExtra(); // expand _EXTRA        
            }
    } 
    catch( GDLException& e)
        {
            callerEnv->SetNewEnv( oldNewEnv);
            // update line number, currently set to caller->CallingNode()
            // because actEnv is not on the stack yet, 
            // report caller->Pro()'s name is ok, because we are not inside
            // the call yet
            e.SetErrorNodeP( actEnv->CallingNode());
            throw e;
        }
    callerEnv->SetNewEnv( oldNewEnv);

	guard.release();
	
    return;
}
    : (  #(KEYDEF_REF IDENTIFIER //ref_parameter
            )
        )
	;

// the environment is not on the callstack
// for library subroutines, their number of parameters is already checked in the compiler
parameter_def_nocheck [EnvBaseT* actEnv] 
{
    Guard<EnvBaseT> guard(actEnv); 

    EnvBaseT* callerEnv = callStack.back();
    EnvBaseT* oldNewEnv = callerEnv->GetNewEnv();

    callerEnv->SetNewEnv( actEnv);

    try{

        if( _t != NULL)
            {
                _retTree = _t;
                // _retTree != NULL, save one check // 'if' is needed already for Extra()
                static_cast<ParameterNode*>(_retTree)->Parameter( actEnv);
                // Parameter does no checking
                while(_retTree != NULL) 
                     static_cast<ParameterNode*>(_retTree)->Parameter( actEnv);

                actEnv->ResolveExtra(); // expand _EXTRA        
            }
    } 
    catch( GDLException& e)
        {
            callerEnv->SetNewEnv( oldNewEnv);
            // update line number, currently set to caller->CallingNode()
            // because actEnv is not on the stack yet, 
            // report caller->Pro()'s name is ok, because we are not inside
            // the call yet
            e.SetErrorNodeP( actEnv->CallingNode());
            throw e;
        }
    callerEnv->SetNewEnv( oldNewEnv);

	guard.release();
	
    return;
}
    : (  #(KEYDEF_REF IDENTIFIER //ref_parameter
            )
        )
	;

arrayindex_list returns [ArrayIndexListT* aL]
{
    // IxExprListT      cleanupList; // for cleanup
    IxExprListT      ixExprList;
    SizeT nExpr;
    BaseGDL* s;
	
//	ProgNodeP retTree = _t->getNextSibling();
	ProgNodeP ax = _t;
// 	match(antlr::RefAST(_t),ARRAYIX);
	_t = _t->getFirstChild();
	
	aL = ax->arrIxList; // vs. ax->arrIxListNoAssoc
	assert( aL != NULL);

	nExpr = aL->NParam();
	if( nExpr == 0)
	{
        aL->Init();
        _retTree = ax->getNextSibling();//retTree;
        return aL;
	}
	
	while( true) {
        IxExprListT* cleanupList = aL->GetCleanupIx(); // for cleanup

        assert( _t != NULL);
        if( NonCopyNode( _t->getType()))
            {
                s= _t->EvalNC(); //indexable_expr(_t);
                //_t = _retTree;
            }
        else if( _t->getType() ==  GDLTokenTypes::FCALL_LIB)
            {
                s=lib_function_call_internal(_t);
                //_t = _retTree;
                // if( !callStack.back()->Contains( s)) 
                if( callStack.back()->GetPtrToReturnValueNull() == NULL) 
                    cleanupList->push_back( s);
            }				
        else
            {
                s=_t->Eval(); //indexable_tmp_expr(_t);
                //_t = _retTree;
                cleanupList->push_back( s);
            }
			
        assert( s != NULL);
        ixExprList.push_back( s);
        if( ixExprList.size() == nExpr)
            break; // allows some manual tuning

        _t = _t->getNextSibling();
	}

	aL->Init( ixExprList);//, &cleanupList);
	
	_retTree = ax->getNextSibling();//retTree;
	return aL;
}
	: #(ARRAYIX
            (
                ( s=indexable_expr
                | s=lib_function_call_internal
                | s=indexable_tmp_expr
                )
            )*
        )
    ;
/*
arrayindex_list_noassoc returns [ArrayIndexListT* aL]
{
    IxExprListT      cleanupList; // for cleanup
    IxExprListT      ixExprList;
    SizeT nExpr;
    BaseGDL* s;
	
//	ProgNodeP retTree = _t->getNextSibling();
	ProgNodeP ax = _t;
// 	match(antlr::RefAST(_t),ARRAYIX);
	_t = _t->getFirstChild();
	
	aL = ax->arrIxListNoAssoc;
	assert( aL != NULL);
	
	nExpr = aL->NParam();
	if( nExpr == 0)
	{
        aL->Init();
        _retTree = ax->getNextSibling();//retTree;
        return aL;
	}
	
	while( true) {
        assert( _t != NULL);
        if( NonCopyNode( _t->getType()))
            {
                s= _t->EvalNC(); //indexable_expr(_t);
                //_t = _retTree;
            }
        else if( _t->getType() ==  GDLTokenTypes::FCALL_LIB)
            {
                s=lib_function_call_internal(_t);
                //_t = _retTree;
                if( !callStack.back()->Contains( s)) 
                    cleanupList.push_back( s);
            }				
        else
            {
                s=_t->Eval(); //indexable_tmp_expr(_t);
                //_t = _retTree;
                cleanupList.push_back( s);
            }
			
        ixExprList.push_back( s);
        if( ixExprList.size() == nExpr)
            break; // allows some manual tuning

        _t = _t->getNextSibling();
	}

	aL->Init( ixExprList, &cleanupList);
	
	_retTree = ax->getNextSibling();//retTree;
	return aL;
}
	: #(ARRAYIX
            (
                ( s=indexable_expr
                | s=lib_function_call_internal
                | s=indexable_tmp_expr
                )
            )*
        )
    ;
*/
// for _overloadBracketsLeftSide/_overloadBracketsRightSide
arrayindex_list_overload [IxExprListT& indexList]
{
    ArrayIndexListT* aL;
    // IxExprListT      cleanupList; // for cleanup
    IxExprListT      ixExprList;
    SizeT nExpr;
    BaseGDL* s;
	
//	ProgNodeP retTree = _t->getNextSibling();
	ProgNodeP ax = _t;
// 	match(antlr::RefAST(_t),ARRAYIX);
	_t = _t->getFirstChild();
	
	aL = ax->arrIxListNoAssoc;
	assert( aL != NULL);

	nExpr = aL->NParam();
	if( nExpr == 0)
	{
        aL->InitAsOverloadIndex( ixExprList, /* NULL,*/ indexList);
        _retTree = ax->getNextSibling();//retTree;
        return;
	}
	
    IxExprListT* cleanupList = aL->GetCleanupIx();
	
	while( true) {
        assert( _t != NULL);
        if( NonCopyNode( _t->getType()))
            {
                s= _t->EvalNCNull(); // in this case (overload) NULL is ok
                //_t = _retTree;
            }
         // else if( _t->getType() ==  GDLTokenTypes::FCALL_LIB)
         //     {
         //         // s=lib_function_call_internal(_t);
         //         BaseGDL** retValPtr;
         //         s = static_cast<FCALL_LIBNode*>(_t)->EvalFCALL_LIB(retValPtr); 
         //         //_t = _retTree;
         //         // if( !callStack.back()->Contains( s)) 
         //         if( retValPtr == NULL) 
         //             cleanupList->push_back( s);
         //     }				
        else
            {
                BaseGDL** ref=_t->EvalRefCheck( s); //indexable_tmp_expr(_t);
                //_t = _retTree;
                if( ref == NULL)
		  cleanupList->push_back( s);
		else
		  s = *ref;
            }
			
        ixExprList.push_back( s);
        if( ixExprList.size() == nExpr)
            break; // allows some manual tuning

        _t = _t->getNextSibling();
	}

    aL->InitAsOverloadIndex( ixExprList, /*&cleanupList,*/ indexList);
	
	_retTree = ax->getNextSibling();//retTree;
	return;
}
	: #(ARRAYIX
            (
                ( s=indexable_expr
                | s=lib_function_call_internal
                | s=indexable_tmp_expr
                )
            )*
        )
    ;

