/* *************************************************************************
                          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"
#include "nullgdl.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
bool IsEnabledGC(); // defined in GDLInterpreter.hpp with EnableGC(bool);
void EnableGC(bool);
}

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 ARRAYDEF_GENERALIZED_INDGENNode;
    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 bool CheckProExist( 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:
    bool InterruptEnable() const { return interruptEnable;}
    // procedure (searchForPro == true) or function (searchForPro == false)
    static bool CompileFile(const std::string& f, 
                            const std::string& untilPro="",
                            bool searchForPro=true); 
    static bool CompileSaveFile(RefDNode theAST); 
    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 heapIx;

    static EnvStackT  callStack; 
    static bool noInteractive;
    static bool InBatchProcedureAtMain;
    static DLong stepCount;
    static std::string MyProName;

// 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;
//     }

//   static bool IsEnabledGC() { return enable_GC;}
	static bool IsEnabledGC() { return true;}

    // the New... functions 'own' their BaseGDL*
    SizeT NewObjHeap( SizeT n=1, DStructGDL* var=NULL)
    {
        SizeT tmpIx=heapIx;
        for( SizeT i=0; i<n; i++)
        objHeap.insert( objHeap.end(),
            std::pair<SizeT, RefDStructGDL>( heapIx++, (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); 
        if (!NullGDL::IsNULLorNullGDL(del)) delete del; //avoid destroying !NULL
    }
    static void FreeObjHeap( DObj id)
    {
        if( id != 0)
        {       
            ObjHeapT::iterator it=objHeap.find( id);
            if  ( it != objHeap.end()) 
            { 
                FreeObjHeapDirect( id, it);
            }
        }
    }
    static void FreeHeapDirect( DPtr id, HeapT::iterator it)
    {
        BaseGDL* del = (*it).second.get();
        heap.erase( id); 
        if (!NullGDL::IsNULLorNullGDL(del)) delete del; //avoid destroying !NULL
    }
    static void FreeHeap( DPtr id)
    {
        if( id != 0)
            {
                HeapT::iterator it=heap.find( id);
                if( it != heap.end()) 
                    { 
                        FreeHeapDirect( id, it);
                    }
            }
    }
    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 DByteGDL* IsEnabledGC( DPtrGDL* p)
   {
		SizeT nEl=p->N_Elements();
		if( nEl == 0) return new DByteGDL( 0);
		DByteGDL* ret = new DByteGDL( p->Dim());
		Guard<DByteGDL> guard(ret);
		for( SizeT ix=0; ix < nEl; ix++)
		{
			DPtr id= (*p)[ix];
		   if( id != 0)
			   {
			   HeapT::iterator it=heap.find( id);
			   if( it != heap.end() and (*it).second.IsEnabledGC())
					(*ret)[ix] = 1;
			   }
		}
		return guard.release();
   }
   static DByteGDL* IsEnabledGCObj( DObjGDL* p)
   {
        SizeT nEl=p->N_Elements();
        if( nEl == 0) return new DByteGDL( 0);
        DByteGDL* ret = new DByteGDL( p->Dim());
        Guard<DByteGDL> guard(ret);
        for( SizeT ix=0; ix < nEl; ix++)
   {
            DObj id= (*p)[ix];
		   if( id != 0)
			   {
			   ObjHeapT::iterator it=objHeap.find( id);
			   if( it != objHeap.end() and (*it).second.IsEnabledGC())
					(*ret)[ix] = 1;
			   }
        }
        return guard.release();
   }

   static void EnableGC( DPtr id, bool set=true)
   {
       if( id != 0)
           {
		   HeapT::iterator it=heap.find( id);
		   if( it != heap.end()) (*it).second.EnableGC(set);
           }
   }
   static void EnableGC( DPtrGDL* p, bool set=true)
   {
        SizeT nEl=p->N_Elements();
        for( SizeT ix=0; ix < nEl; ix++)
        {
            DPtr id= (*p)[ix];
            EnableGC( id, set);
       }
   }
   static void EnableAllGC() {
        for( HeapT::iterator it=heap.begin(); it != heap.end(); ++it)
			it->second.EnableGC(true);
        for( ObjHeapT::iterator it=objHeap.begin(); it != objHeap.end(); ++it)
			it->second.EnableGC(true);
   }

   static void EnableGCObj( DObj id, bool set=true)
   {
       if( id != 0)
           {
		   ObjHeapT::iterator it=objHeap.find( id);
		   if( it != objHeap.end()) (*it).second.EnableGC(set);
           }
   }
   static void EnableGCObj( DObjGDL* p, bool set=true)
   {
        SizeT nEl=p->N_Elements();
        for( SizeT ix=0; ix < nEl; ix++)
        {
            DObj id= (*p)[ix];
            EnableGCObj( id, set);
       }
   }

   static void DecRef( DPtr id)
   {
       if( id != 0 and IsEnabledGC())
           {
#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() and (*it).second.IsEnabledGC() )
                           {
#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 and IsEnabledGC())
            {
#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() and (*it).second.IsEnabledGC() )
                           {
#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 and IsEnabledGC())
            {
#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 SizeT RefCountHeap( DPtr id)
    {
		SizeT result = 0;
        if( id != 0)
            {
                HeapT::iterator it=heap.find( id);
                if( it != heap.end()) result = (*it).second.Count();
			}
		return result;
	   }
   static SizeT RefCountHeapObj( DObj id)
    {
		SizeT result = 0;
        if( id != 0)
            {
                ObjHeapT::iterator it=objHeap.find( id);
                if( it != objHeap.end()) result = (*it).second.Count();
			}
		return result;
	   }
   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 and IsEnabledGC())
            {
#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 BaseGDL* GetHeapNoThrow( DPtr ID)
    {
        HeapT::iterator it=heap.find( ID);
        if( it == heap.end())  return NULL;
        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)
        {
           BaseGDL* del = (*it).second.get();
           if (!NullGDL::IsNULLorNullGDL(del)) delete del; //avoid destroying !NULL
           heap.erase( it->first); 
        }
        for( ObjHeapT::iterator it=objHeap.begin(); it != objHeap.end(); ++it)
        {
           BaseGDL* del = (*it).second.get();
           if (!NullGDL::IsNULLorNullGDL(del)) delete del; //avoid destroying !NULL
           heap.erase( it->first);
        }
// The counters are reset for easier human readability.
       heapIx = 1;
    }

    // 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 = callStack.back()->GetLineNumber(); //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);
        if (noInteractive) exit(EXIT_SUCCESS); //strangely, IDL exits on error when non interactive with 0 not 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;
        if (noInteractive) exit(EXIT_SUCCESS);
    }

    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;
	//NOTE: *** code below is not active ***
}
    : (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);
            _t = _retTree;
			
// 			if( retCode == RC_RETURN) 
			if( retCode >= RC_RETURN) 
			{
                res=returnValue;
                returnValue=NULL;
                // already done in RETFNode::Run() :
                // if( returnValueL != NULL)
                //     {
                //         callStack.back()->SetPtrToReturnValue( returnValueL);
                //         returnValueL = NULL;
                //     }
			break;
			}					
	}
	
	// default return value if none was set
	if( res == NULL) res = new DIntGDL( 0); 
	
	_retTree = _t;
	return res;
	//NOTE: *** code below is not active ***
}
    : (retCode=statement
        )*
    ;

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;
            }
    }
	
	// res must be defined here
	if( res == NULL)
        throw GDLException( in, "Function "+callStack.back()->GetProName()+
                            " must return a global left-value in this context.",
                            false,false);
	_retTree = _t;
	return res;
	//NOTE: *** code below is not active ***
}
    : (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;
	//NOTE: *** code below is not active ***
}
    : (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;
	//NOTE: *** code below is not active ***
}
    : (retCode=statement
        )+
    ;

statement returns[ RetCode retCode]
{

    assert( _t != NULL);
    ProgNodeP last;
    _retTree = _t;
}
	:  
        {
    //optimize speed: differentiate inner loops to avoid one externally implicit comparison on controlc for each loop (if possible)
    if (interruptEnable) { //will test for sigControlC
      {
        do {
          last = _retTree;
          callStack.back()->SetLineNumber(last->getLine()); // track actual line number
          retCode = last->Run(); // Run() sets _retTree
        } while (_retTree != NULL && retCode == RC_OK && !(sigControlC) && (debugMode <= DEBUG_RETURN)); //loops if debug_clear or debug_return
        if (_retTree != NULL) last = _retTree; //this is OK see https://github.com/gnudatalanguage/gdl/issues/1403#issuecomment-1326490113
        goto afterStatement;
      }
    } else { //will not test for sigControlC 
      {
        do {
          last = _retTree;
          callStack.back()->SetLineNumber(last->getLine()); // track actual line number
          retCode = last->Run(); // Run() sets _retTree
        } while (_retTree != NULL && retCode == RC_OK && (debugMode <= DEBUG_RETURN)); //loops if debug_clear or debug_return
        if (_retTree != NULL) last = _retTree; //this is OK see https://github.com/gnudatalanguage/gdl/issues/1403#issuecomment-1326490113
        goto afterStatement;
      }
    }
// original single loop with all checks    
//    {
//      do {
//        last = _retTree;
//        callStack.back()->SetLineNumber(last->getLine()); // track actual line number
//        retCode = last->Run(); // Run() sets _retTree
//      } while (_retTree != NULL && retCode == RC_OK && !(sigControlC && interruptEnable) && (debugMode <= DEBUG_RETURN)); //loops if debug_clear or debug_return
//      if (_retTree != NULL) last = _retTree; //this is OK see https://github.com/gnudatalanguage/gdl/issues/1403#issuecomment-1326490113
//      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 (interruptEnable && _retTree == NULL && (debugMode == DEBUG_RETURN || debugMode == DEBUG_OUT)) {
      if (debugMode == DEBUG_RETURN) {
        if (callStack.back()->GetProName() == MyProName) {
          DebugMsg(last, "Return encountered: ");
          debugMode = DEBUG_CLEAR;
          return NewInterpreterInstance(last->getLine()); //-1);
        }
      } else { //DEBUG_OUT --> just do an additional .step if we are at MyProName
         if (callStack.back()->GetProName() == MyProName) {
          debugMode = DEBUG_STEP;
          stepCount=1;
          return retCode; //continue 
        }
     } 
   } else if (debugMode != DEBUG_CLEAR) {
      if (debugMode == DEBUG_STOP) {
        DebugMsg(last, "Stop encountered: ");
        if (!interruptEnable) debugMode = DEBUG_PROCESS_STOP;
      } else if (debugMode == DEBUG_STOP_SILENT) {
        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_STEPOVER) {
        if (callStack.back()->GetProName() == MyProName) { //we count only in current level
          if (stepCount == 1) {
            stepCount = 0;
            DebugMsg(last, "Stepped to: ");

            debugMode = DEBUG_CLEAR;
            MyProName="";
            retCode = NewInterpreterInstance(last->getLine()); //-1);
          } else {
            --stepCount;
#ifdef GDL_DEBUG
            std::cout << "stepCount-- = " << stepCount << std::endl;
#endif
          }
        }
      } 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 
        DStructDesc* errorStateDesc = SysVar::Error_State()->Desc();   //MUST NOT BE STATIC, due to .reset 
        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);  //removed. for some reason dynamic_cast returns NULL on bona fide GDLIOException objects.
                // 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()); //probably false -- see ReportError, was obliged to replace e.getLine() by callStack.back()->GetLineNumber()

                    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;
    } // catch [ GDLException& e] 






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

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

   BaseGDL* e1;
   ProgNodeP evalExpr = _t->getFirstChild();
   if( NonCopyNode( evalExpr->getType()))
       {
           e1 = evalExpr->EvalNC();
       }
   else
       {
           BaseGDL** ref = evalExpr->EvalRefCheck(e1);
           if( ref == NULL)
               {
                   // use new env if set (during parameter parsing)
                   EnvBaseT* actEnv = DInterpreter::CallStackBack()->GetNewEnv();
                   if( actEnv == NULL) actEnv = DInterpreter::CallStackBack();
                   assert( actEnv != NULL);
                   // this is crucial, a guard does not work here as a temporary
                   // ptr will be cleaned up at return from this function
                   actEnv->DeleteAtExit( e1);
               }
           else
               e1 = *ref;
       }

    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;
	//NOTE: *** code below is not active ***
}
    : DEREF
    ;


// l expressions for DEC/INC ********************************
// l_decind_expr is the only function called from external

// l_decinc_expr is the only external function of the l_decinc_* expressions


// called from l_decinc_array_expr
l_decinc_indexable_expr [ BaseGDL*& res] returns [BaseGDL** e]
{
    e = _t->LEval();
    res = *e;
    if( res == NULL)
        throw GDLException( _t, "Variable is undefined: "+Name(e),true,false);
    return e;
	//NOTE: *** code below is not active ***
}
    : e=l_function_call_internal
    | e=l_deref 
    | e=l_defined_simple_var
    | e=l_sys_var
    ;



// called from l_decinc_expr
l_decinc_array_expr [int dec_inc, BaseGDL*& res] returns [BaseGDL** e]
{
	ArrayIndexListT* aL;
	ArrayIndexListGuard guard;
	
	if( _t->getType() == ARRAYEXPR)
	{
		ProgNodeP arrExNP = _t;
		// match(antlr::RefAST(_t),ARRAYEXPR);

		_t = _t->getFirstChild();

		BaseGDL** tmp =_t->LEval();// l_decinc_indexable_expr(_t, res);
        res = *tmp;
        if( res == NULL)
            throw GDLException( _t, "Variable is undefined: " + Name(tmp),
                                true,false);
		_t = _t->getNextSibling();

		aL=arrayindex_list(_t,!res->IsAssoc());

		_retTree = arrExNP->getNextSibling();
		
		guard.reset( aL); 
		aL->SetVariable( res);
		
		if( dec_inc == DECSTATEMENT) 
		{
            res->DecAt( aL); 
            return NULL;
		}
		if( dec_inc == INCSTATEMENT)
		{
            res->IncAt( aL);
            return NULL;
		}
		
		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);
		
        return NULL;
	}
    else
    {
		e =_t->LEval();// l_decinc_indexable_expr(_t, res);
        res = *e;
        if( res == NULL)
            throw GDLException( _t, "Variable is undefined: " + Name(e),
                                true,false);
		_retTree = _t->getNextSibling();
		// e=l_decinc_indexable_expr(_t, res);
		// _t = _retTree;
		
		if( dec_inc == DECSTATEMENT || dec_inc == DEC_REF_CHECK) 
		{
            res->Dec(); 
            return e;
		}
		if( dec_inc == INCSTATEMENT || dec_inc == INC_REF_CHECK)
		{
            res->Inc();
            return e;
		}
		
		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();
		
        return e;
	}
    assert(false);
	return e;

    // not used _______________________________________
    // ------------------------------------------------
	//NOTE: *** code below is not active ***
}
    : ARRAYEXPR 
    | e= l_decinc_indexable_expr[ res]
    ;

// struct assignment
// MAIN function: called from l_decinc_expr
l_decinc_dot_expr [int dec_inc] returns [BaseGDL* res]
{ 
	ProgNodeP dot = _t;
	//match(antlr::RefAST(_t),DOT);

	_t = _t->getFirstChild();
	
	SizeT nDot=dot->nDot;
	Guard<DotAccessDescT> aD( new DotAccessDescT(nDot+1));
	
	l_dot_array_expr(_t, aD.Get());
	_t = _retTree;

    while( _t != NULL)
        {
            tag_array_expr(_t, aD.Get());
			_t = _retTree;
		}

	_retTree = dot->getNextSibling();
	
	if( dec_inc == DECSTATEMENT) 
        {
            aD.Get()->Dec(); 
            return NULL;
        }   
	if( dec_inc == INCSTATEMENT)
        {
            aD.Get()->Inc();
            return NULL; 
        }
	
	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();

	return res;
	//NOTE: *** code below is not active ***
}
    : DOT
    ;

// 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=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;

                    res=l_decinc_dot_expr(_t, dec_inc);

                    _retTree = startNode->getNextSibling();
                    return refRet;
                }   
        }    
        // no exception caught
        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;
        }   
        )
    | res=l_decinc_dot_expr[dec_inc]
    | 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, "Heap variable is undefined: "+Name(res),true,false);
        }
   _retTree = _t->getNextSibling();
	return res;
	//NOTE: *** code below is not active ***
}
    : EXPR // 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 only from unused part of l_expr_internal
unused_l_array_expr [BaseGDL* right] returns [BaseGDL** res]
{
   ArrayIndexListT* aL;
}
   : #(ARRAYEXPR res=l_indexable_expr aL=arrayindex_list[ false]) 
    ;

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,!(*rP)->IsAssoc());

		_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;
	//NOTE: *** code below is not active ***
}
    : #(ARRAYEXPR rP=l_indexable_expr aL=arrayindex_list[ false])   
    | 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);
    _retTree = _t->getNextSibling();
    return res;
	//NOTE: *** code below is not active ***
    
    BaseGDL* e1; 
}
    : QUESTION
    | res=unused_l_array_expr[ right]
    | res=l_sys_var // sysvars cannot change their type
    | 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]
    | DOT
    | ASSIGN
    | ASSIGN_ARRAYEXPR_MFCALL
    | ASSIGN_REPLACE 
    | 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;
	//NOTE: *** code below is not active ***

}
    : 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;
	//NOTE: *** code below is not active ***

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

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

}
    : SYSVAR  
    ;

// 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;
	//NOTE: *** code below is not active ***

    BaseGDL** refRet; // not used
}
    : EXPR
    | ARRAYDEF
    | ARRAYDEF_GENERALIZED_INDGEN
    | 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;
	//NOTE: *** code below is not active ***
}
    : 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;
        // noAssoc==true, as assoc vars cannot be struct members
		aL=arrayindex_list(_t,true); 
		//_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;
	//NOTE: *** code below is not active ***
}
	: #(ARRAYEXPR tag_expr[ aD] aL=arrayindex_list[ true] /*{ aD->ADAddIx(aL);}*/ )
    | tag_expr[ aD] //{ aD->ADAddIx(NULL);} 
    ;

r_dot_indexable_expr [DotAccessDescT* aD] returns [BaseGDL* res] // 1st
{
    res=NULL;
	switch ( _t->getType()) {
	case EXPR:
	{
		ProgNodeP tIn = _t;
		_t = _t->getFirstChild();
		//res=expr(_t);
        res = _t->Eval();
		aD->SetOwner( true);
		_retTree = tIn->getNextSibling();
		break;
	}
	// DEREF was forgotten: cause of #812 and #26
    case DEREF:
    {
		BaseGDL** v=l_deref(_t);
			//_t = _retTree;
			res = *v;
			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;
	//NOTE: *** code below is not active ***

    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,!r->IsAssoc());
		_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: *** code below is not active ***
}
// 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[ false] /*{ guard.reset(aL);}*/ )   
    | r=r_dot_indexable_expr[ aD]
    ;

// 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;
	//NOTE: *** code below is not active ***
}
	: (QUESTION) // trinary operator
    | (ARRAYEXPR) //res=array_expr
    | DOT
    | 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;
	//NOTE: *** code below is not active ***

    BaseGDL** e2;
}
    : e2=l_defined_simple_var
    | SYSVAR
    | 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);
	//NOTE: *** code below is not active ***
}
    : res=tmp_expr
    | 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;
	//NOTE: *** code below is not active ***

    BaseGDL** e2;
} // tmp_expr
    : e2=l_deref 
	| (QUESTION) // trinary operator
    | (ARRAYEXPR) //res=array_expr
//    | res=array_expr
    | DOT
    | 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;
	//NOTE: *** code below is not active ***

    BaseGDL** l;
}
    : ASSIGN 
    | ASSIGN_ARRAYEXPR_MFCALL
    | ASSIGN_REPLACE 
    ;



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: "+_t->getText(),true,false);
        else // VARPTR
            throw GDLException( _t, "Common block variable is undefined.",true,false);
        }
	_retTree = _t->getNextSibling();
	return vData->Dup();
	//NOTE: *** code below is not active ***
}
    : VAR // DNode.varIx is index into functions/procedures environment
    | VARPTR // DNode.var   is ptr to common block variable
    ;

sys_var returns [BaseGDL* res]
{
    res = _t->Eval();
	_retTree = _t->getNextSibling();
	return res; // no ->Dup()
	//NOTE: *** code below is not active ***
}
    : SYSVAR
    ;

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;
	//NOTE: *** code below is not active ***
}
	: FCALL_LIB
    ;    

lib_function_call_retnew_internal returns[ BaseGDL* res]
{ 
    res = _t->Eval();
	_retTree = _t->getNextSibling();
	return res; //_t->cData->Dup(); 
	//NOTE: *** code below is not active ***
}
	: FCALL_LIB_RETNEW
    ;    


unused_function_call returns[ BaseGDL* res]
{res=NULL;}
    : MFCALL 
    | MFCALL_PARENT 
    | FCALL 
    | ARRAYEXPR_MFCALL
	;	



l_arrayexpr_mfcall [BaseGDL* right] returns [BaseGDL** res]
{ 
    // better than Guard: Guard 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;
	//NOTE: *** code below is not active ***
}
	: FCALL_LIB
    | MFCALL 
    | MFCALL_PARENT 
    | FCALL
	;	

// 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;
	//NOTE: *** code below is not active ***
}
    : KEYDEF_REF_EXPR IDENTIFIER
    ;



// 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;
	//NOTE: *** code below is not active ***
}
    : KEYDEF_REF IDENTIFIER
	;



// 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;
	//NOTE: *** code below is not active ***
}
    : KEYDEF_REF IDENTIFIER
	;



arrayindex_list[ bool 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();
	
    if( noAssoc)
        aL = ax->arrIxListNoAssoc;
    else
        aL = ax->arrIxList; 
	assert( aL != NULL);

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

        assert( _t != NULL);
        if( NonCopyNode( _t->getType()))
            {
                s = _t->EvalNC(); 
            }
        else
            {
                BaseGDL** ref =_t->EvalRefCheck( s); 
                if( ref == NULL)
                    cleanupList->push_back( s);
                else
                    s = *ref;
            }
			
        assert( s != NULL);
       if (s == NullGDL::GetSingleInstance()) { //return an empty arraylist, to be checked further as to not apply any posterior 'AssignAt' function.
	     aL->Init();                            //as an array assigment containing '!NULL' is to be ignored.
         aL->SetIgnore();
	     _retTree = ax->getNextSibling();//retTree;
	     return aL;
	   }
        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;
	//NOTE: *** code below is not active ***
}
	: ARRAYIX
    ;



// 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
            {
                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;

 	//NOTE: *** code below is not active ***
}
	: ARRAYIX
    ;
