#include "eparserinterpreter.h"

#include "eparser.h"
#include "evarcommon.h"
#include "logger.h"
#include "evar.h"
#include "eiostream.h"
#include "edistcomp.h"
#include "edir.h"

#ifdef _MSC_VER
#include <conio.h>
#endif

#ifdef EUTILS_HAVE_READLINE_H
 #include <readline/readline.h>
 #include <readline/history.h>
#endif

#include "etimer.h"
#include "einterpret_atom.h"
#include "efunccode.h"

#ifndef MIN
#define MIN(a,b) (a)<(b)?(a):(b)
#endif

void skip_blank(const estr& str,long& i);

bool isOp(const estr& str);
bool split_atoms2(const estr& str,estrarray& sa);

void assign(eatom*,evar&,estrhashof<evar>&,stopExecutionStruct&,const estr& name,ebasicarray<eatom_base*>&,evararray&);
void assignref(eatom*,evar&,estrhashof<evar>&,stopExecutionStruct&,const estr& name,ebasicarray<eatom_base*>&,evararray&);
void objprop(eatom*,evar&,estrhashof<evar>&,stopExecutionStruct&,const estr& name,ebasicarray<eatom_base*>& args,evararray&);
void objop(eatom*,evar&,estrhashof<evar>&,stopExecutionStruct&,const estr& name,ebasicarray<eatom_base*>& args,evararray&);
void objcall(eatom*,evar&,estrhashof<evar>&,stopExecutionStruct&,const estr& name,ebasicarray<eatom_base*>& args,evararray&);

const type_info& checkassign(eatom_base*,evar&,estrhashof<evar>&,stopExecutionStruct&,const estr& name,ebasicarray<eatom_base*>&,evararray&);
const type_info& checkassignref(eatom_base*,evar&,estrhashof<evar>&,stopExecutionStruct&,const estr& name,ebasicarray<eatom_base*>&,evararray&);
const type_info& checkobjprop(eatom_base*,evar&,estrhashof<evar>&,stopExecutionStruct&,const estr& name,ebasicarray<eatom_base*>& args,evararray&);
const type_info& checkobjop(eatom_base*,evar&,estrhashof<evar>&,stopExecutionStruct&,const estr& name,ebasicarray<eatom_base*>& args,evararray&);
const type_info& checkobjcall(eatom_base*,evar&,estrhashof<evar>&,stopExecutionStruct&,const estr& name,ebasicarray<eatom_base*>& args,evararray&);

ecodeAtom* newCodeAtomByType(unsigned int catype);

eatom_base::eatom_base(int _type): type(_type), evald(false), remote(0) {}
eatom_base::~eatom_base()
{
  int i;
  for (i=0; i<args.size(); ++i)
    delete args[i];
  args.clear();
}

void eatom_base::setRemote()
{
  remote=1;
  for (int i=0; i<args.size(); ++i)
    args[i]->setRemote();
}

void eatom_base::remoteExecute(estrhashof<evar>& env)
{
  estr s;
  print(s);
  ldinfo("remoteExecute: command tree: "+s);
  
  rvalue.set(getDistComp().executeAtom(exechost,this,env));
// do not know why this is here, it is a problem when a remote call is executed in a loop
//  remote=0;
  evald=true;
}


void eatom_value::serial(estrhashof<evar>& env,estr& data)
{
  serialuint(remote,data);
  if (remote==0){
    value.serial(data); // remote value, so just send the name of the value/variable
    stopExecutionStruct stopExecution;
    //BUG: this causes eatom_values such as "size" in arr.size() to be looked up as a variable, thus creating a variable named "size"
    make(env,stopExecution).serial(data); // value computed on local computer to be sent to remote
    return;
  }
  value.serial(data); // remote value, so just send the name of the value/variable
  rvalue.serial(data); // added to handle evarRemote cases
}

size_t eatom_value::unserial(const estr& data,size_t ip)
{
  ip=unserialuint(remote,data,ip);
  if (remote==0){
    ip=value.unserial(data,ip);
//    cerr << "eatom_value: " << value << " ip: " << ip << "/" << data.len() << endl;
    ip=rvalue.unserial(data,ip);  // variable sent from remote computer
//    cerr << rvalue << endl;
//    cerr << "eatom_value rvalue: " << rvalue << " ip: " << ip << "/" << data.len() << endl;
    evald=true;
    return(ip);
  }
  remote=0;
  ip=value.unserial(data,ip);
  ip=rvalue.unserial(data,ip);
  if (rvalue.isTypeid(typeid(evarRemote))){
    evarRemote &rvar(rvalue.get<evarRemote>());
    if (rvar.exechost==getSystem().getHostname() && rvar.execpid==getSystem().getPID()){
      if (getParser().tmpVars.exists(rvar.rvid))
        rvalue.set(getParser().tmpVars[rvar.rvid]);
      else
        lerror("remote value not found: "+estr(rvar.rvid));
    }
  }
//  cerr << "eatom_value: " << value << " ip: " << ip << "/" << data.len() << endl;
  return(ip);
}

void eatom_value::clear()
{
//  eprofile p("eatom_value::clear");
  if (constant) return; // constant values do not need to be reset
  rvalue.clear();
  evald=false;
}

eatom_value::eatom_value(const estr& str): eatom_base(2),value(str),constant(false)
{
/*
  ldinfo("eatom_value::make()");
  if (evald && rvalue.isNull() && env.exists(value)) // environment variables are not stored in rvalue
    return(env[value]);
  if (!rvalue.isNull() || evald) return(rvalue);

  evald=true;

  if (!value.len()){
    lderror("empty atom value");
    return(rvalue);
  }

  if (value.is_int()){
    rvalue.set(value.i());
    return(rvalue);
  }

  if (value.is_float()){
    rvalue.set(value.f());
    return(rvalue);
  }

  if (value.is_hex()){
    rvalue.set(value.h());
    return(rvalue);
  }
  if (value[0]=='@'){
    int i;
    i=value.find(":");
    if (i==-1){
      lderror("remote variable missing :");
      return(rvalue);
    }
    estr tmphost(value.substr(1,i-1));
    estr tmpvar(value.substr(i+1));
    exechost=tmphost;
    rvalue.set(getDistComp().var(tmphost,tmpvar));
    return(rvalue);
  }

  if (value[0]=='"' && value[value.len()-1]=='"'){
    estr tmps;
    tmps=value.substr(1,value.len()-2);
    tmps.replace("\\\"","\"");
    tmps.replace("\\n","\n");
    tmps.replace("\\r","\r");
    tmps.replace("\\t","\t");
    tmps.replace("\\\\","\\");
    rvalue.set(new estr(tmps));
    return(rvalue);
  }

  if (getClassNames().exists(value)){
    rvalue.set(getClassNames()[value]->create.at(0));
    return(rvalue);
  }

  if (env.exists(value))
    return(env[value]);

  if (parser->funcs.exists(value)){
    rvalue.set(parser->funcs[value].at(0));
    return(rvalue);
  }

  if (&env==&getParser()->objects) lwarn("creating variable \""+value+"\"");
  env.add(value,evar());
  return(env[value]);
*/
}

const type_info& eatom_value::check(estrhashof<evar>& env,stopExecutionStruct& stopExecution)
{
  if (stopExecution.flag) { return(typeid(void)); }

  ldinfo(estr("eatom_value::make(): ")+value+" ("+rvalue.getClass()+")");
  if (env.exists(value)) // environment variables are not stored in rvalue
    return(env[value].getTypeid());

  if (!value.len()){
    return(typeid(void));
  }

  if (value.is_int()){
    return(typeid(int));
  }

  if (value.is_float()){
    return(typeid(double));
  }

  if (value.is_hex()){
    return(typeid(int));
  }
  if (value[0]=='@'){
    return(typeid(void));
/*    int i;
    i=value.find(":");
    if (i==-1){
      lderror("remote variable missing :");
      return(rvalue);
    }
    estr tmphost(value.substr(1,i-1));
    estr tmpvar(value.substr(i+1));
    exechost=tmphost;
    value=tmpvar;
//    rvalue.set(getDistComp().var(tmphost,tmpvar));
    remote=1;
    return(rvalue);
*/
  }

  if (value[0]=='"' && value[value.len()-1]=='"'){
    return(typeid(estr));
  }

  if (value[0]=='/' || (value.size()>1 && value[1]=='/' && (value[0]=='~' || value[0]=='.')) || (value.size()>2 && value[2]=='/' && value[1]=='.' && value[0]=='.')){
    return(typeid(efile));
  }

  if (getClassNames().exists(value) && getClassNames()[value]->create.size()){
    return(getClassNames()[value]->getTypeid());
  }

  if (getParser().funcs.exists(value)){
    return(*getParser().funcs[value].at(0).fReturn);
  }

  return(typeid(void));
}

evar& eatom_value::make(estrhashof<evar>& env,stopExecutionStruct& stopExecution)
{
  if (stopExecution.flag) return(rvalue);

//  eprofile p(getProfiler("eatom_value::make"));
  ldinfo(estr("eatom_value::make(): ")+value+" ("+rvalue.getClass()+")");
  if (value=="continue" || value=="break" || value=="return"){
    constant=true;
    return(rvalue);
  }

  if (evald && rvalue.isNull() && env.exists(value)) // environment variables are not stored in rvalue
  {
    evar& v(env[value]);
    if (!v.isNull()){
      constant=true;
      if (v.isTypeid(typeid(evarRemote))){
        remote=1;
        constant=false;
        exechost=evarRemoteExecuteHost(v.var);
//        return(rvalue);
        rvalue.set(v);
        return(rvalue);
      }
    }
    return(env[value]);
  }
  if (!rvalue.isNull() || evald) return(rvalue);

  evald=true;

  if (!value.len()){
    constant=true;
    stopExecution.flag=true;
    stopExecution.line=line;
    stopExecution.error="empty atom value";
    return(rvalue);
  }

  if (value.is_int()){
    rvalue.set(value.l());
    constant=true;
    return(rvalue);
  }

  if (value.is_float()){
    constant=true;
    rvalue.set(value.d());
    return(rvalue);
  }

  if (value.is_hex()){
    constant=true;
    rvalue.set(value.h());
    return(rvalue);
  }
  if (remote==1)      // fix for remote variables not being looked up twice due to removal of trailing @ in the following code
    return(rvalue);

  if (value[0]=='@'){
    constant=false;
    int i;
    i=value.find(":");
    if (i==-1){
      stopExecution.flag=true;
      stopExecution.line=line;
      stopExecution.error="remote variable missing :";
      return(rvalue);
    }
    estr tmphost(value.substr(1,i-1));
    estr tmpvar(value.substr(i+1));
    exechost=tmphost;
    value=tmpvar;
//    rvalue.set(getDistComp().var(tmphost,tmpvar));
    remote=1;
    return(rvalue);
  }

  if (value[0]=='"' && value[value.len()-1]=='"'){
    constant=true;
    estr tmps;
    tmps=value.substr(1,value.len()-2);
    tmps.replace("\\\"","\"");
    tmps.replace("\\n","\n");
    tmps.replace("\\r","\r");
    tmps.replace("\\t","\t");
    tmps.replace("\\\\","\\");
    rvalue.set(tmps);
    return(rvalue);
  }

  if (value[0]=='/' || (value.size()>1 && value[1]=='/' && (value[0]=='~' || value[0]=='.')) || (value.size()>2 && value[2]=='/' && value[1]=='.' && value[0]=='.')){
    constant=false;
    rvalue.set(efile(value));
    return(rvalue);
  }

  if (getClassNames().exists(value) && getClassNames()[value]->create.size()){
    constant=false;
    rvalue.set(&getClassNames()[value]->create.at(0));
    return(rvalue);
  }

  if (env.exists(value)){
    evar& v(env[value]);
    constant=false;
    if (v.isTypeid(typeid(evarRemote))){
      remote=1;
      rvalue.set(v);
      exechost=evarRemoteExecuteHost(v.var);
    }
    return(v);
  }

  if (getParser().funcs.exists(value)){
    constant=true;
    rvalue.set(&getParser().funcs[value].at(0));
    return(rvalue);
  }

  constant=false;
  if (&env==&getParser().objects) linfo("creating variable \""+value+"\"");
  env.add(value,evar());
  return(env[value]);
}

void eatom_value::print(estr& s)
{
  if (remote==1)
    s+="R";

  ldinfo("eatom_value::print");
  if (!value.len()){
    lderror("empty atom value");
    s += "(empty) ";
    return;
  }

  if (value.is_int()){
    s += "("+value+") ";
    return;
  }

  if (value.is_float()){
    s += "("+value+") ";
    return;
  }

  if (value.is_hex()){
    s += "("+value+") ";
    return;
  }

  if (value[0]=='"' && value[value.len()-1]=='"'){
    s += "\""+value.substr(1,value.len()-2)+"\" ";
    return;
  }


  if (getParser().objects.exists(value)){
    s += value + " "; //+ "(" + *parser->objects[value]<<") ";
    return;
  }

  s += "<n/a>"+value+" ";
  return;
}

void eatom::serial(estrhashof<evar>& env,estr& data)
{
  serialuint(remote,data);
  if (remote==0) {
//    value.serial(data); // remote value, so just send the name of the value/variable
    //BUG: this causes eatom_values such as "size" in arr.size() to be looked up as a variable, thus creating a variable named "size"
    stopExecutionStruct stopExecution;
    make(env,stopExecution).serial(data); // value computed on local computer to be sent to remote
//    rvalue.serial(data);
    return;
  }
  name.serial(data);
  serialuint(args.size(),data);
  int i;
  for (i=0; i<args.size(); ++i){
    serialuint(args[i]->type,data);
    args[i]->serial(env,data);
  }
}

eatom_base *newAtomByType(unsigned int type)
{
  switch(type) {
    case 1: return(new eatom);
    case 2: return(new eatom_value);
  }
  return(0x00);
}

size_t eatom::unserial(const estr& data,size_t ip)
{
  ip=unserialuint(remote,data,ip);
//  cerr << "eatom remote: " << remote << " ip: " << ip << "/" << data.len() << endl;
  if (remote==0){ // precomputed value sent to this computer
    ip=rvalue.unserial(data,ip);
//    cerr << rvalue << endl;
//    cerr << "eatom rvalue: " << rvalue << " ip: " << ip << "/" << data.len() << endl;
    evald=true;
    return(ip);
  }

  remote=0;
  ip=name.unserial(data,ip);
//  cerr << "eatom: " << name << " ip: " << ip << "/" << data.len() << endl;
  unsigned int count,type;
  int i;
  ip=unserialuint(count,data,ip);
//  cerr << "eatom args: " << count << " ip: " << ip << "/" << data.len() << endl;
  if (ip==-1) return(ip);
  for (i=0; i<count; ++i){
    ip=unserialuint(type,data,ip);
//    cerr << "eatom arg: " << i << " type: " << type << " ip: " << ip << "/" << data.len() << endl;
    if (ip==-1) return(ip);
    eatom_base *tmpatom=newAtomByType(type);
    args.add(tmpatom);
    ldieif(tmpatom==0x00,"Unknown atom type");
    ip=tmpatom->unserial(data,ip);
    if (ip==-1) return(ip);
/*
    if (type==1){
      eatom *tmpatom=new eatom;
      ip=tmpatom->unserial(data,ip);
      args.add(tmpatom);
//      cerr << "eatom arg: " << i << " type: " << type << " ip: " << ip << "/" << data.len() << endl;
    } else if (type==2){
      eatom_value *tmpatom=new eatom_value;
      ip=tmpatom->unserial(data,ip);
      args.add(tmpatom);
//      cerr << "eatom arg: " << i << " type: " << type << " ip: " << ip << "/" << data.len() << endl;
    }
    if (ip==-1) return(ip);
*/
  }
  vargs.init(count-1);
  return(ip);
}

efunc* findFunc(earray<efunc>& farr,const evararray& arr)
{
  int i,j;
  ldieif(farr.size()==0,"looking for overloaded function in empty function array!");

  if (farr.size()==1)
    return(&farr[0]);

  efunc *f;
  int bmatch=0;
  int bmatchi=0;
  int match;
  int def=-1; // TODO: this is a hack in case a function taking an evararray exists, then if no better function is found use that.
              
  ldinfo(" args: (");
  for (int j=0; j<arr.size(); ++j)
    ldinfo(getClassName(&arr[j].getTypeid()));
  ldinfo(")");

  // TODO: Improve overloaded function matching. we can check if conversion is possible
  for (i=0; i<farr.size(); ++i){
    f=&farr[i];
    match=2;
    if (f->fArgs.size()==1 && *f->fArgs[0]==typeid(evararray))
      def=i;
    
    for (j=0; j<arr.size() && j<f->fArgs.size(); ++j){
      if (arr[j].getTypeid() != *f->fArgs[j]){
        match=1;
        if (!arr[j].isConvertible(*f->fArgs[j]))
          match=0;
        break;
      }
    }
    
//    cout << "match: "<<match<<" arr: "<<arr.size()<<" fArgs: "<<f->fArgs.size()<<" defargs: "<<f->defargs.size()<<endl;
    ldinfo(estr("match: ")+match+" arr: "+arr.size()+" fArgs: "+f->fArgs.size()+" defargs: "+f->defargs.size());
    ldinfo((f->fReturn?estr(getClassName(f->fReturn)):estr("void"))+" function(");
    for (int j=0; j<f->fArgs.size(); ++j)
      ldinfo(getClassName(f->fArgs.at(j)));
    ldinfo(")");

    if (match==2 && arr.size() == f->fArgs.size())
      return(&farr[i]);
    else if (match==2 && arr.size() <= f->fArgs.size() && arr.size() + f->defargs.size() >= f->fArgs.size()){
      lwarn("overloaded function with ambiguous argument set, using arbitrary function");
//      cout << farr[i] << endl;
      return(&farr[i]);
    }
    if (match>bmatch && ((arr.size() == f->fArgs.size()) || (arr.size() + f->defargs.size() >= f->fArgs.size())) ){ bmatch=match; bmatchi=i; }
  }
  if (def!=-1) // pass all the parameters in a single evararray to the default function
    return(&farr[def]);
  if (bmatch>0){
    return(&farr[bmatchi]);
  }
  lwarn("overloaded function with ambiguous argument set, using first function");
  return(&farr[0]);
}

void eatom::clear()
{
//  eprofile p(getProfiler("eatom::clear"));
  int i;
  for (i=0; i<args.size(); ++i)
    args[i]->clear();
  if (name=="function") return; // TODO: this is a hack, function objects in a line are stored in rvalue and not generated at make time, so they can not be cleared
  rvalue.clear();
//  vargs.clear();
  evald=false;
}

const type_info& eatom::check(estrhashof<evar>& env,stopExecutionStruct& stopExecution)
{
  if (stopExecution.flag) return(typeid(void));

  if (name.len()==0 && args.size()==1){
    if (args[0]->remote==1){
      remote=1;
    }
    return(args[0]->check(env,stopExecution));
  }

  if (name.len()==0){
    stopExecution.flag=true;
    stopExecution.line=line;
    stopExecution.error="no name in eatom";
    return(typeid(void));
  }
//  lderrorif(name.len()==0,"no name in eatom, args.size="+estr(args.size()));

  if (args.size()<1){
    return(typeid(void));
  }

  if (name=="="){
    return(checkassign(this,rvalue,env,stopExecution,name,args,vargs));
  }else if (name=="=&"){
    return(checkassignref(this,rvalue,env,stopExecution,name,args,vargs));
  }else if (name=="."){
    return(checkobjprop(this,rvalue,env,stopExecution,name,args,vargs));
  }else if (name=="()"){
    estr fname;
    if (args[0]->type==2){
      // TODO: check if it is a remote function and call executeRemote if it is the case
      fname=static_cast<eatom_value*>(args[0])->value;

      if (fname.len() && fname[0]=='@'){
        int i;
        i=fname.find(":");
        if (i==-1){
          stopExecution.flag=true;
          stopExecution.line=line;
          stopExecution.error="remote variable missing :";
          return(typeid(void));
        }
        estr tmphost(fname.substr(1,i-1));
        estr tmpvar(fname.substr(i+1));
        args[0]->exechost=tmphost;
        static_cast<eatom_value*>(args[0])->value=tmpvar;
        args[0]->remote=1;
        exechost=tmphost;
        remote=1;
        return(typeid(void));
      }


      if (getClassNames().exists(fname) && getClassNames()[fname]->create.size()){ // constructor function
        int i;
        for (i=1; i<args.size(); ++i){
          args[i]->make(env,stopExecution);
          if (args[i]->remote==1){
            if (args[i]->rvalue.isNull())
              args[i]->remoteExecute(env);
            evarRemoteValue(args[i]->rvalue);
          }
        }
        for (i=1; i<args.size(); ++i)
          vargs[i-1].set(args[i]->make(env,stopExecution));
//          vargs.add(args[i]->make(env));
        efcall=findFunc(getClassNames()[fname]->create,vargs);
        return(*efcall->fReturn);
      }else if (getParser().funcs.exists(fname)){ // registered function
        int i;
        for (i=1; i<args.size(); ++i){
          args[i]->make(env,stopExecution);
          if (args[i]->remote==1){
            if (args[i]->rvalue.isNull())
              args[i]->remoteExecute(env);
            evarRemoteValue(args[i]->rvalue);
          }
        }
        for (i=1; i<args.size(); ++i)
          vargs[i-1].set(args[i]->make(env,stopExecution));
//          vargs.add(args[i]->make(env));
        efcall=findFunc(getParser().funcs[fname],vargs);
        return(*efcall->fReturn);
      }else if (env.exists(fname)){ // function stored in a variable
        return(checkobjop(this,rvalue,env,stopExecution,name,args,vargs));
      }else{
        stopExecution.flag=true;
        stopExecution.line=line;
        stopExecution.error="unknown function or object: "+fname;
        return(typeid(void));
      }
    } else if (args[0]->type==1){
      if (static_cast<eatom*>(args[0])->name=="."){
        return(checkobjcall(this,rvalue,env,stopExecution,name,args,vargs));
      }else if (!args[0]->make(env,stopExecution).isNull()) {
        return(checkobjop(this,rvalue,env,stopExecution,name,args,vargs));
      } else {
        stopExecution.flag=true;
        stopExecution.line=line;
        stopExecution.error="left side of operator() does not resolve to an object";
        return(typeid(void));
      }
    }
    return(typeid(void));
  }else{  //if (isOp(name) || name=="[]" || name=="++prefix" || name=="--prefix"){
    return(checkobjop(this,rvalue,env,stopExecution,name,args,vargs));
  }
  return(typeid(void));
}

/*
void call_func(eatom_base *atom,evar& rvalue,estrhashof<evar>& env,const estr& name,ebasicarray<eatom_base*>& args,evararray& vargs,efunc& f)
{
  f->call(rvalue,vargs);
}
*/

evar& eatom::make(estrhashof<evar>& env,stopExecutionStruct& stopExecution)
{
  if (stopExecution.flag) return(rvalue);
//  eprofile p(getProfiler("eatom::make"));

  ldinfo("eatom::make, name: "+name);
  if (!rvalue.isNull() || evald)
    return(rvalue);

  evald=true;

/*
  // optimize calls in reused code such as loop conditionals and loop code
  if (fcall){
    fcall(this,rvalue,env,name,args,vargs);
    return(rvalue);
  }else if (efcall){
    int i;
    for (i=1; i<args.size(); ++i)
      vargs[i-1].set(args[i]->make(env));
    efcall->call(rvalue,vargs);
    return(rvalue);
  }
*/

  if (name.len()==0 && args.size()==1){
    if (args[0]->remote==1){
      remote=1;
      exechost=args[0]->exechost;
      if (rvalue.isNull())
        remoteExecute(env);
      evarRemoteValue(rvalue);
//      return(rvalue);
    }
    rvalue.set(args[0]->make(env,stopExecution));
    return(rvalue);
  }
  if (name.len()==0){
    stopExecution.flag=true;
    stopExecution.line=line;
    stopExecution.error="no name in eatom";
    return(rvalue);
  }

  lderrorif(name.len()==0,"no name in eatom, args.size="+estr(args.size()));

  if (args.size()<1){
    stopExecution.flag=true;
    stopExecution.line=line;
    stopExecution.error="missing argument: "+name;
    return(rvalue);
  }

  if (name=="="){
    fcall=&assign;
    assign(this,rvalue,env,stopExecution,name,args,vargs);
    return(rvalue);
  }else if (name=="=&"){
    fcall=&assignref;
    assignref(this,rvalue,env,stopExecution,name,args,vargs);
    return(rvalue);
  }else if (name=="."){
    fcall=&objprop;
    objprop(this,rvalue,env,stopExecution,name,args,vargs);
    return(rvalue);
  }else if (name=="()"){
    estr fname;
    if (args[0]->type==2){
      // TODO: check if it is a remote function and call executeRemote if it is the case
      fname=static_cast<eatom_value*>(args[0])->value;

      if (remote==1){ // means that this is a remote block and code below has already run once..., which removes the trailing @ and prevents it from running a second time. This is a fix for that, not very elegant approach but should work
        return(rvalue);
      }
      if (fname.len() && fname[0]=='@'){
        int i;
        i=fname.find(":");
        if (i==-1){
          stopExecution.flag=true;
          stopExecution.line=line;
          stopExecution.error="remote variable missing ':'";
          return(rvalue);
        }
        estr tmphost(fname.substr(1,i-1));
        estr tmpvar(fname.substr(i+1));
        args[0]->exechost=tmphost;
        static_cast<eatom_value*>(args[0])->value=tmpvar;
        args[0]->remote=1;
        exechost=tmphost;
        remote=1;
        return(rvalue);
      }


      if (getClassNames().exists(fname) && getClassNames()[fname]->create.size()){ // constructor function
        int i;
        for (i=1; i<args.size(); ++i){
          args[i]->make(env,stopExecution);
          if (args[i]->remote==1){
            if (args[i]->rvalue.isNull())
              args[i]->remoteExecute(env);
            evarRemoteValue(args[i]->rvalue);
          }
        }
        for (i=1; i<args.size(); ++i)
          vargs[i-1].set(args[i]->make(env,stopExecution));
//          vargs.add(args[i]->make(env));
        efcall=findFunc(getClassNames()[fname]->create,vargs);
        efcall->call(rvalue,vargs);
        return(rvalue);
      }else if (getParser().funcs.exists(fname)){ // registered function
        int i;
        for (i=1; i<args.size(); ++i){
          args[i]->make(env,stopExecution);
          if (args[i]->remote==1){
            if (args[i]->rvalue.isNull())
              args[i]->remoteExecute(env);
            evarRemoteValue(args[i]->rvalue);
          }
        }
        for (i=1; i<args.size(); ++i)
          vargs[i-1].set(args[i]->make(env,stopExecution));
//          vargs.add(args[i]->make(env));
        efcall=findFunc(getParser().funcs[fname],vargs);
        efcall->call(rvalue,vargs);
        return(rvalue);
      }else if (env.exists(fname)){ // function stored in a variable
        fcall=objop;
        objop(this,rvalue,env,stopExecution,name,args,vargs);
        return(rvalue);
      }else{
        stopExecution.flag=true;
        stopExecution.line=line;
        stopExecution.error="unknown function or object: "+fname;
        return(rvalue);
      }
    } else if (args[0]->type==1){
      if (static_cast<eatom*>(args[0])->name=="."){
        fcall=objcall;
        objcall(this,rvalue,env,stopExecution,name,args,vargs);
        return(rvalue);
      }else if (!args[0]->make(env,stopExecution).isNull()) {
        fcall=objop;
        objop(this,rvalue,env,stopExecution,name,args,vargs);
        return(rvalue);
      } else {
        stopExecution.flag=true;
        stopExecution.line=line;
        stopExecution.error="left side of operator() does not resolve to an object";
        return(rvalue);
      }
    }
    return(rvalue);
  }else{  //if (isOp(name) || name=="[]" || name=="++prefix" || name=="--prefix"){
    fcall=objop;
    objop(this,rvalue,env,stopExecution,name,args,vargs);
    return(rvalue);
  }

  stopExecution.flag=true;
  stopExecution.line=line;
  stopExecution.error="unknown operator: "+name;
  return(rvalue);
}

void eatom::print(estr& s)
{
  ldinfo("eatom::print "+name+" args.size: "+estr(args.size()));
  if (remote==1)
    s+="R";

  s += name+" { ";

  int i;
  for (i=0; i<args.size(); ++i)
    { args[i]->print(s); s += ", "; }
  if (args.size()>0)
    s.del(-2);

  s += "} ";
}


eatom_base* newFuncAtom(const estr& name,const estrarray& sa)
{
  estrarray tmpsa;
  split_atoms2(sa[0],tmpsa);
  eatom* atom=new eatom(tmpsa);
  atom->name = name;
  return(atom);
}


eatom_base* newAtom(const estrarray& sa)
{
  if (!sa.size()) return(new eatom(sa));

  if (sa.size()>1)
    return(new eatom(sa));

  estrarray tmpsa;
  if (sa[0].len() && sa[0][0]=='(')
    split_atoms2(sa[0].substr(1,-2).trim(),tmpsa);
  else
    split_atoms2(sa[0],tmpsa);
  if (tmpsa.size()==1 && tmpsa[0].len() && tmpsa[0][0]=='['){
    tmpsa[0]="("+tmpsa[0].substr(1,-2)+")";
    tmpsa.insert(0,"evararray");
    return(new eatom(tmpsa));
  }else if (tmpsa.size()==1)
    return(new eatom_value(tmpsa[0]));

  return(new eatom(tmpsa));
}

/*
eatom_base* newAtom(const estrarray& sa){
  estrarray tmpsa(sa);
  return(newAtom(tmpsa));
}
*/

int find_min_assoc_op(const estrarray& sa);

eatom::eatom(const estrarray& sa): eatom_base(1),fcall(0x00),efcall(0x00),mcall(0x00)
{
  if (!sa.size()){ ldinfo("eatom(): empty array"); return; }

  int i;
  if (sa[0][0]=='$'){
    // external executable call
    name="()";
    args.add(newAtom("exec"));
    args.add(newAtom("\""+sa[0].substr(1)+"\""));
    for (i=1; i<sa.size(); ++i)
      args.add(newAtom("\""+sa[i]+"\""));
    vargs.init(args.size()-1);
    return;
  }

  if (sa.size()==1){
    if (!sa[0].len())
      return;
    ldinfo("eatom(): 1 element: "+estr(sa));
    if (sa[0]=="continue" || sa[0]=="break" || sa[0]=="return")
      name=sa[0];
    else
      args.add(newAtom(sa));
    return;
  }

  i=find_min_assoc_op(sa);
  if (i==-1){
    lerror("eatom(): unprocessed event: "+estr(sa));
    return;
  }
   
  if (sa[i][0]=='('){
    ldinfo("eatom(): operator found: "+sa[i]+" in: "+estr(sa));
    name="()";
    args.add(newAtom(sa.subset(0,i)));
    estrarray tmpsa;
    split_atoms2(sa[i].substr(1,-2).trim(),tmpsa);

    int ti,e;
    bool options=false;
    for (ti=0,e=tmpsa.find(","); ti<tmpsa.size(); ti=e+1,e=tmpsa.find(",",ti)){
      if (e==-1) e=tmpsa.size();
      if (ti+1<tmpsa.size() && tmpsa[ti+1]=="=") { options=true; break; } // start of options array
      args.add(newAtom(tmpsa.subset(ti,e-ti)));
    }
    if (options){
      if (sa[0]!="evararray"){ /* handles options at end of function argument list */
        estrarray tmpsa2;
        tmpsa2.add("evarhash");
        estr tmpstr="(";
        for (; ti<tmpsa.size(); ti=e+1,e=tmpsa.find(",",ti)){
          if (e==-1) e=tmpsa.size();
          if (ti+1>=tmpsa.size() || tmpsa[ti+1]!="=") { lerror("missing name in evarhash"); return; }
          tmpstr+="\""+tmpsa[ti]+"\"";  // key
          tmpstr+=",";
          tmpstr+=tmpsa.subset(ti+2,e-ti-2).join("",""); // value
          if (e<tmpsa.size()) tmpstr+=",";
        }
        tmpstr+=")";
        tmpsa2.add(tmpstr);
        args.add(newAtom(tmpsa2));
      }else{ /* handles [a="test",b="fdsfs",...] evarhash dictionaries */
        delete args[0];
        args.clear();
        args.add(newAtom("evarhash"));
//        sa[0]="evarhash";
        for (; ti<tmpsa.size(); ti=e+1,e=tmpsa.find(",",ti)){
          if (e==-1) e=tmpsa.size();
          if (ti+1>=tmpsa.size() || tmpsa[ti+1]!="=") { lerror("missing name in evarhash"); return; }
          args.add(newAtom("\""+tmpsa[ti]+"\""));  // key
          args.add(newAtom(tmpsa.subset(ti+2,e-ti-2).join("",""))); // value
        }
      }
    }
/*
    e=tmpsa.find(",");
    if (e!=-1){
      i=0;
      while (e!=-1) {
        args.add(newAtom(tmpsa.subset(i,e-i)));
        i=e+1;
        e=tmpsa.find(",",i);
      }
      args.add(newAtom(tmpsa.subset(i)));
    }else if (tmpsa.size() && tmpsa[0].len())
      args.add(newAtom(tmpsa));
*/
  } else if (sa[i][0]=='['){
    ldinfo("eatom(): operator found: "+sa[i]+" in: "+estr(sa));
    name="[]";
    args.add(newAtom(sa.subset(0,i)));

    estrarray tmpsa;
    split_atoms2(sa[i].substr(1,-2).trim(),tmpsa);
    args.add(newAtom(tmpsa));
  }else if (sa[i][0]=='{'){
    if (i<2){
      lerror("eatom(): not enough atoms for function: "+estr(sa));
      return;
    }
    if (sa[i-2]!="function"){
      lerror("eatom(): code block in single line without function keyword: "+estr(sa));
      return;
    }
    if (sa[i-1][0]!='('){
      lerror("eatom(): function missing arguments?: "+estr(sa));
      return;
    }
//    cout << "function code: " << sa[i].substr(1,-2) << endl;
//    cout << "function args: " << sa[i-1].substr(1,-2) << endl;
    name="function";

    ecodeParser cparser;
    ecodeAtomBlock *batom = new ecodeAtomBlock;
    batom->parse(cparser,sa[i].substr(1,-2));
    batom->type=CA_CODE;
    efuncCode *funcCode = new efuncCode;
    funcCode->exec=batom;
    funcCode->code=sa[i].substr(1,-2);
    funcCode->args=sa[i-1].substr(1,-2);
    efunc *func=new efunc;
    func->setFunc(funcCode);
    rvalue.set(func);
//    cout << "rvalue: " << rvalue.isNull() << endl;
    evald=true;
//    cout << "finished function code: " << sa[i].substr(1,-2) << endl;
//    cout << sa << endl;
    return;
  }else{
    ldinfo("eatom(): operator found: "+sa[i]+" in: "+estr(sa));
    name=sa[i];
    if (name=="++"){
      if (i==0) {
        name="++prefix";
        args.add(newAtom(sa.subset(i+1)));
      } else
        args.add(newAtom(sa.subset(0,i)));
    }else if (name=="--"){
      if (i==0) {
        name="--prefix";
        args.add(newAtom(sa.subset(i+1)));
      } else
        args.add(newAtom(sa.subset(0,i)));
    }else if (name=="-" && i==0){
      name="-unary";
      args.add(newAtom(sa.subset(i+1)));
    }else if (name=="!"){
      args.add(newAtom(sa.subset(i+1)));
    }else if (i==0 && (name=="/" || name==".")){
      if (sa.size()>1){
        estrarray tmpsa(sa);
        tmpsa[1]=tmpsa[0]+tmpsa[1];
        args.add(newAtom(tmpsa.subset(1)));
      }else{
        args.add(newAtom(sa));
      }
    }else if (name=="&" && i>0 && sa[i-1]=="="){
      name="=&";
      args.add(newAtom(sa.subset(0,i-1)));
      args.add(newAtom(sa.subset(i+1)));
    }else{
      args.add(newAtom(sa.subset(0,i)));
      args.add(newAtom(sa.subset(i+1)));
    }
  }
  vargs.init(args.size()-1);
}

estrarray ops=estr(".:++:--:!:*:%:/:-:+:>>:<<:,:=:+=:-=:==:!=:>=:<=:>:<:&:^:|:&&:||: ").explode(":");
estrarray opsall=estr(".:++:--:!:*:%:/:-:+:>>:<<:,:=:+=:-=:==:!=:>=:<=:>:<:&:^:|:&&:||: :(:):{:}:[:]:,").explode(":");
//estrarray ops=estr(".:++:--:*:%:/:-:+:>>:<<:,:=:+=:-=:==:!=:>=:<=:>:<:&:^:|:&&:||").explode(":");
//estrarray ops=estr("-=:+=:=:.:,:*:%:/:-:+:<<:>>:<:>:<=:>=:!=:==").explode(":");
//==:!=:>=:<=:>:<:>>:<<:+:-:/:%:*:,:.").explode(":");

int find_min_assoc_op(const estrarray& sa)
{
  long i,j;
  long imin,ipos;
  imin=-1;
  ipos=-1;
  for (i=0; i<sa.size(); ++i){
    j=ops.find(sa[i]);
    if (sa[i][0]=='(') j=0;
    else if (sa[i][0]=='{') j=0;
    else if (sa[i]==".") j=0;
    else if (sa[i][0]=='[') j=0;
    else if (j==-1) continue;

//    if (j>imin && ipos!=i || ((j==0 || j==5) && imin==0)){ imin=j; ipos=i; } // we look for the operator that should be evaluated last, in the case of the "." it is the last "." found in the string the "/" is also in the same case
    if (j>imin || (j==0 && imin==0) || ((j==6 || j==4) && (imin==6 || imin==4)) || (j==8 && imin==8)){ imin=j; ipos=i; } // we look for the operator that should be evaluated last, in the case of the "." it is the last "." found in the string the "/" is also in the same case
  }
//  cout << "div: "<<ipos<<" op: "<<ops[
  return(ipos);
}

void skip_string(const estr& str,long& i)
{
  ++i;
  for (;i<str.len() && str[i]!='"'; ++i){ if (str[i]=='\\') ++i; }
}

void skip_comment(const estr& str,long& i)
{
  for (;i<str.len() && str[i]!='\n'; ++i);
  if (i<str.len()) ++i;
}
void skip_longcomment(const estr& str,long& i)
{
  i+=2;
  for (;i+1<str.len() && (str[i]!='*' || str[i+1]!='/'); ++i);
  if (i==str.len()-1) i=str.len();
  if (i+1<str.len()) i+=2;
}
void skip_comment(const estr& str,long& i,int& line)
{
  for (;i<str.len() && str[i]!='\n'; ++i);
  ++line;
  if (i<str.len()) ++i;
}
void skip_longcomment(const estr& str,long& i,int& line)
{
  i+=2;
  for (;i+1<str.len() && (str[i]!='*' || str[i+1]!='/'); ++i) if (str[i]=='\n') ++line;
  if (i==str.len()-1) i=str.len();
  if (i+1<str.len()) i+=2;
}



bool isFile(const estr& str){
  bool res=true;
  long i=0;
  if (str[i]=='/' || (i+1<str.len() && str[i+1]=='/' && (str[i]=='~' || str[i]=='.')) || (i+2<str.len() && str[i+2]=='/' && str[i]=='.' && str[i+1]=='.')){
    for (;i<str.len() && str[i]!=','; ++i);
    if (i==str.len()) return(true);
  }
  return(false);
}


long find_ops(const estr& str,const estr& op,bool start,long i=0)
{
  long j;
//  bool start;
//  start=true;
  for (; i<str.len(); ++i){
//    if (start && str[i]>='0' && str[i]<='9' || str[i]=='-' && i+1<str.len() && str[i+1]>='0' && str[i+1]<='9'){
    if (start && str[i]>='0' && str[i]<='9'){
      ++i;
      for (;i<str.len() && ((str[i]>='0' && str[i]<='9') || str[i]=='.' || str[i]=='e' || str[i]=='E' || str[i]=='x' || ((str[i]=='+' || str[i]=='-') && (str[i-1]=='e' || str[i-1]=='E')) ); ++i);
      start=false;
    }else if (start && str[i]=='"'){
      skip_string(str,i);
      start=false;
    }else if (start && str[i]=='@'){
      for (;i<str.len() && str[i]!=':'; ++i);
      start=true;
    }
/*     else if (start && (str[i]=='/' || (i+1<str.len() && str[i+1]=='/' && (str[i]=='~' || str[i]=='.')) || (i+2<str.len() && str[i+2]=='/' && str[i]=='.' && str[i+1]=='.'))){
      for (;i<str.len() && str[i]!=','; ++i);
      start=false;
    }
*/
    else
      start=false;

    if (!start && str[i]==op[0]){
      j=0;
      for (;i+j<str.len() && j<op.len() && str[i+j]==op[j]; ++j);
      if (j==op.len()) return(i);
    }
  }
  return(-1);
}

void split_ops(const estr& str, estrarray& strarr, bool& start, const estrarray& ops)
{
  long i2;
  long io;
  long iopos;
  long i;
//  int e;

  long j;

  i=0;
  while(i<str.len()){
    skip_blank(str,i);
    io=0; iopos=find_ops(str,ops[0],start,i);
    if (iopos==-1) iopos=str.len();
    for (j=0;j<ops.size(); ++j){
      i2=find_ops(str,ops[j],start,i);
      if (i2 < iopos && i2!=-1) { iopos=i2; io=j; }
      else if (i2 != -1 && i2 == iopos && ops[j].size() == 2){ io=j; }
    }
    if (iopos==str.len() ) break;

    if (iopos-i)
      strarr += str.substr(i,iopos-i).trim();
    if (ops[io]!=" ") { // spaces should only split arguments not be added as operators
      strarr += ops[io];
      start=true;
    }
    i = iopos + ops[io].len();
  }

  if (i!=str.len())
    strarr += str.substr(i).trim();
}

void split_ops_all(const estr& str, estrarray& strarr, const estrarray& ops)
{
  bool start=true;
  long i2;
  long io;
  long iopos;
  long i;
//  int e;

  long j;

  i=0;
  while(i<str.len()){
    skip_blank(str,i);
    io=0; iopos=find_ops(str,ops[0],start,i);
    if (iopos==-1) iopos=str.len();
    for (j=0;j<ops.size(); ++j){
      i2=find_ops(str,ops[j],start,i);
      if (i2 < iopos && i2!=-1) { iopos=i2; io=j; }
      else if (i2 != -1 && i2 == iopos && ops[j].size() == 2){ io=j; }
    }
    if (iopos==str.len() ) break;

    if (iopos-i)
      strarr += str.substr(i,iopos-i).trim();
    if (ops[io]!=" ") { // spaces should only split arguments not be added as operators
      strarr += ops[io];
      start=true;
    }
    i = iopos + ops[io].len();
  }

  if (i!=str.len())
    strarr += str.substr(i).trim();
}




bool isOpAll(const estr& str)
{
  if (opsall.find(str)!=-1) return(true);
  return(false);
}

bool isOp(const estr& str)
{
  if (ops.find(str)!=-1) return(true);
  return(false);
}

inline bool streq(const estr& str,const estr& m,int i)
{
  if (str.len()-i<m.len()) return(false);

  int j;
  for (j=0; j<m.len(); ++j)
    if (str[j+i]!=m[j]) return(false);

  return(true);
}


bool find_blocks(const estr& str,const estr& blockleft,const estr& blockright,long& i,long& e)
{
  long j;
  long entry;
  for (;i<str.len(); ++i){
    if (str[i]=='"')
      skip_string(str,i);
    else if (str[i]=='/' && i+1<str.len() && str[i+1]=='/')
      skip_comment(str,i);
    else if (str[i]=='/' && i+1<str.len() && str[i+1]=='*')
      skip_longcomment(str,i);
    else{
      for (j=0; j<blockleft.size(); ++j){
        if (str[i]==blockleft[j]){     // for left,right block endings bigger than 1 char:  && streq(str,blockleft[j],i)){
          entry=1;
          e=i+1;
          for (;e<str.size();++e){
            if (str[i]=='"')
              skip_string(str,i);
            else if (str[i]=='/' && i+1<str.len() && str[i+1]=='/')
              skip_comment(str,i);
            else if (str[i]=='/' && i+1<str.len() && str[i+1]=='*')
              skip_longcomment(str,i);
             else if (str[e]==blockleft[j])  // && streq(str,blockleft
              ++entry;
            else if (str[e]==blockright[j]){
              --entry;
              if (!entry) return(true);
            }
          }
          if (e>=str.size()){
            lwarn("closing string \""+blockright.substr(j,1)+"\" missing in \""+str.substr(i)+"\"");
            i=str.size();
            return(false);
          }
        }
      }
    }
  }
  e=i;
//  if (i==str.size()) return(false);
  return(true);
}

/*
void setup_parse_table()
{
  pstate['+']=0x01;

  pstate['0']=0x01;
  pstate['1']=0x01;
  pstate['2']=0x01;
  pstate['3']=0x01;
  pstate['4']=0x01;
  pstate['5']=0x01;
  pstate['6']=0x01;
  pstate['7']=0x01;
  pstate['8']=0x01;
  pstate['9']=0x01;

  pstate[' ']=0x00;
}

void split_atoms3(const estr& str)
{
  int s=0x00;
  for (i=0; i<str.len(); ++i){
    s=pstate[str[i]];

  }
}
*/

bool split_atoms2(const estr& str, estrarray& strarr)
{
  estr left="([{";
  estr right=")]}";
  
  long i,i2,e;

  e=0;
  i=0;
  i2=0;
  bool start=true;
  bool err=false;

  while (i2<str.len() && (err=find_blocks(str,left,right,i2,e))){
    ldinfo(estr(i)+","+estr(i2)+","+e+" --- "+str.substr(i,i2-i) + " --- " + str.substr(i2,e-i2+1) + " --- " + str.substr(e+1));
    split_ops(str.substr(i,i2-i),strarr,start,ops);
    if (e==str.len()) {
      i=i2;
      break;
    }
    strarr += str.substr(i2,e-i2+1).trim();
    start=false;
    i=e+1;
    i2=i;
  }
  if (i!=str.len())
    split_ops(str.substr(i),strarr,start,ops);

  if (!err)
    strarr.clear();

  return(err);
}



bool isPrimitive(const type_info& tinfo)
{
  if (tinfo==typeid(int) || tinfo==typeid(unsigned int) || tinfo==typeid(char) || tinfo==typeid(unsigned char) || tinfo==typeid(short) || tinfo==typeid(unsigned short) || tinfo==typeid(float) || tinfo==typeid(double) || tinfo==typeid(long) || tinfo==typeid(unsigned long) || tinfo==typeid(bool))
    return(true);
  return(false);
}



void objcall(eatom* atom,evar& rvalue,estrhashof<evar>& env,stopExecutionStruct& stopExecution,const estr& name,ebasicarray<eatom_base*>& args,evararray& vargs)
{
  if (stopExecution.flag) return;

//  eprofile p("objcall");
  int i;
  for (i=1; i<args.size(); ++i)
    args[i]->make(env,stopExecution);

  evar& v(args[0]->args[0]->make(env,stopExecution));
  if (args[0]->args[0]->remote==1){
    args[0]->remote=1;
    args[0]->exechost=args[0]->args[0]->exechost;
    atom->remote=1;
    atom->exechost=args[0]->args[0]->exechost;
    return;
  }
  for (i=1; i<args.size(); ++i){
    if (args[i]->remote==1){
      if (args[i]->rvalue.isNull())
        args[i]->remoteExecute(env);
      evarRemoteValue(args[i]->rvalue);
    }
  }

  for (i=1; i<args.size(); ++i){
    vargs[i-1].set(args[i]->make(env,stopExecution));
    if (vargs[i-1].isNull()) {
      stopExecution.flag=true;
//      stopExecution.line=line;
      stopExecution.error="null argument: "+estr(i);
      return;
    }
  }

  ldinfo("vargs: "+estr(vargs.size()));
  v.call(rvalue, static_cast<eatom_value*>(args[0]->args[1])->value , vargs);
}

/*
void objop2(evar& rvalue,estrhashof<evar>& env,const estr& name,ebasicarray<eatom_base*>& args,evararray& vargs)
{
  int i;
  for (i=1; i<args.size(); ++i){
    vargs[i-1].set(args[i]->make(env));
    if (vargs[i-1].isNull()) { lwarn("argument is null: "+estr(i-1)); return; }
  }
  ldinfo("objop: "+estr(args.size()));
  args[0]->make(env).call( rvalue, name, vargs);
}
*/

void objop(eatom* atom,evar& rvalue,estrhashof<evar>& env,stopExecutionStruct& stopExecution,const estr& name,ebasicarray<eatom_base*>& args,evararray& vargs)
{
  if (stopExecution.flag) return;
  //TODO: speed up interpreter by moving the code from evar::call and caching it in the AST
//  eprofile p("objop:"+name);
  int i;

  evar& v(args[0]->make(env,stopExecution));
  for (i=1; i<args.size(); ++i)
    args[i]->make(env,stopExecution);

  if (args[0]->remote==1){
    atom->remote=1;
    atom->exechost=args[0]->exechost;
    return;
  }
  for (i=1; i<args.size(); ++i){
    if (args[i]->remote==1){
      if (args[i]->rvalue.isNull())
        args[i]->remoteExecute(env);
      evarRemoteValue(args[i]->rvalue);
    }
  }

  if (name=="()" && v.getTypeid()==typeid(efunc)){
//    eprofile prf("objop:"+name+" func call");
    for (i=1; i<args.size(); ++i){
      vargs[i-1].set(args[i]->make(env,stopExecution));
      if (vargs[i-1].isNull()) {
        stopExecution.flag=true;
//        stopExecution.line=line;
        stopExecution.error="null argument: "+estr(i);
        return;
      }
    }
    ldinfo("efunc call: "+estr(args.size()));
    v.get<efunc>().call(rvalue,vargs);
    return;
  }
  else if (isPrimitive(v.getTypeid())){
//    eprofile prp("objop:"+name+" isprimitive");
    // operators of primitive types need to be passed the object as the first argument
    while (vargs.size()+1<args.size()) vargs.add(evar());
//    vargs[0].set(v);
    for (i=1; i<args.size(); ++i){
      vargs[i-1].set(args[i]->make(env,stopExecution));
      if (vargs[i-1].isNull()) {
        stopExecution.flag=true;
//        stopExecution.line=line;
        stopExecution.error="null argument: "+estr(i);
        return;
      }
    }
    ldinfo("objop: "+estr(args.size()));
    if (atom->mcall==0x00) 
      atom->mcall=v.getCallMethod(name,vargs);
    ldieif(atom->mcall==0x00,"mcall==0x00: "+name+" class: "+v.getClass());
    rvalue.set((*atom->mcall)(v.var,vargs));
//    v.call(rvalue,name,vargs);
    return;
  }

//  eprofile prnp("objop:"+name+" !isprimitive");
  for (i=1; i<args.size(); ++i){
    vargs[i-1].set(args[i]->make(env,stopExecution));
    if (vargs[i-1].isNull()) {
      stopExecution.flag=true;
//      stopExecution.line=line;
      stopExecution.error="null argument: "+estr(i);
      return;
    }
  }
  ldinfo("objop: "+estr(args.size()));

//  if (atom->mcall==0x00) 
//    atom->mcall=v.getCallMethod(name,vargs);
//  ldieif(atom->mcall==0x00,"mcall==0x00: "+name);
//  rvalue.set((*atom->mcall)(v.var,vargs));
  v.call( rvalue, name, vargs);

  return;
}

void objprop(eatom* atom,evar& rvalue,estrhashof<evar>& env,stopExecutionStruct& stopExecution,const estr& name,ebasicarray<eatom_base*>& args,evararray& vargs)
{
  if (stopExecution.flag) return;

//  eprofile p("objprop");
  evar obj;
  if (args.size()==2){
    if (args[1]->type!=2) { 
      stopExecution.flag=true;
//      stopExecution.line=line;
      stopExecution.error="invalid property name: not a string";
      return;
    }

    evar& v(args[0]->make(env,stopExecution));
    if (args[0]->remote==1){
      atom->remote=1;
      atom->exechost=args[0]->exechost;
      return;
    }

    obj.set(v);
    estr prop(static_cast<eatom_value*>(args[1])->value);
   
    if (obj.hasProperty(prop))
      rvalue.set(obj.property(prop));
    else if (obj.hasMethod(prop))
      rvalue.set(obj.property(prop));
    else{
      stopExecution.flag=true;
//      stopExecution.line=line;
      stopExecution.error="no property or method found: "+prop;
    }
  }else{
    stopExecution.flag=true;
//    stopExecution.line=line;
    stopExecution.error="objprop argument size mismatch: "+estr(args.size());
  }
}

void assign(eatom* atom,evar& rvalue,estrhashof<evar>& env,stopExecutionStruct& stopExecution,const estr& name,ebasicarray<eatom_base*>& args,evararray& vargs)
{
  if (stopExecution.flag) return;

//  eprofile p("assign");
  evar& v(args[0]->make(env,stopExecution));
  args[1]->make(env,stopExecution);
  if (args[0]->remote==1){
    atom->remote=1;
    atom->exechost=args[0]->exechost;
    return;
  }
  if (args[1]->remote==1){
    if (args[1]->rvalue.isNull())
      args[1]->remoteExecute(env);
    evarRemoteValue(args[1]->rvalue);
  }

  ldinfo("assigning value");
  if (v.isNull())
    v.copy(args[1]->make(env,stopExecution));
  else
    v=args[1]->make(env,stopExecution);
  rvalue.set(v);
}

void assignref(eatom* atom,evar& rvalue,estrhashof<evar>& env,stopExecutionStruct& stopExecution,const estr& name,ebasicarray<eatom_base*>& args,evararray& vargs)
{
  if (stopExecution.flag) return;

//  eprofile p("assignref");
  evar& v(args[0]->make(env,stopExecution));
  args[1]->make(env,stopExecution);
  if (args[0]->remote==1){
    atom->remote=1;
    atom->exechost=args[0]->exechost;
    return;
  }
  if (args[1]->remote==1)
    args[1]->remoteExecute(env);

  ldinfo("assigning reference");
  v.set(args[1]->make(env,stopExecution));
  rvalue.set(v);
}



const type_info& checkobjcall(eatom_base* atom,evar& rvalue,estrhashof<evar>& env,stopExecutionStruct& stopExecution,const estr& name,ebasicarray<eatom_base*>& args,evararray& vargs)
{
  if (stopExecution.flag) return(typeid(void));

  int i;
  for (i=1; i<args.size(); ++i)
    args[i]->check(env,stopExecution);

  args[0]->args[0]->check(env,stopExecution);
  if (args[0]->args[0]->remote==1){
    args[0]->remote=1;
    args[0]->exechost=args[0]->args[0]->exechost;
    atom->remote=1;
    atom->exechost=args[0]->args[0]->exechost;
    return(typeid(void));
  }
/*
  for (i=1; i<args.size(); ++i){
    if (args[i]->remote==1)
      args[i]->remoteExecute(env);
  }
*/

  for (i=1; i<args.size(); ++i){
    vargs[i-1].set(args[i]->make(env,stopExecution));
    if (vargs[i-1].isNull()) {
      stopExecution.flag=true;
//      stopExecution.line=line;
      stopExecution.error="null argument: "+estr(i-1);
      return(typeid(void));
    }
  }
  args[0]->args[0]->make(env,stopExecution).call(rvalue, static_cast<eatom_value*>(args[0]->args[1])->value , vargs);
  return(rvalue.getTypeid());
}

const type_info& checkobjop(eatom_base* atom,evar& rvalue,estrhashof<evar>& env,stopExecutionStruct& stopExecution,const estr& name,ebasicarray<eatom_base*>& args,evararray& vargs)
{
  if (stopExecution.flag) return(typeid(void));

  int i;

  args[0]->make(env,stopExecution);
  for (i=1; i<args.size(); ++i)
    args[i]->make(env,stopExecution);

  if (args[0]->remote==1){
    atom->remote=1;
    atom->exechost=args[0]->exechost;
    return(typeid(void));
  }
  for (i=1; i<args.size(); ++i){
    if (args[i]->remote==1){
      if (args[i]->rvalue.isNull())
        args[i]->remoteExecute(env);
      evarRemoteValue(args[i]->rvalue);
    }
  }

  if (name=="()" && args[0]->make(env,stopExecution).getTypeid()==typeid(efunc)){
    for (i=1; i<args.size(); ++i){
      vargs[i-1].set(args[i]->make(env,stopExecution));
      if (vargs[i-1].isNull()) {
        stopExecution.flag=true;
//        stopExecution.line=line;
        stopExecution.error="null argument: "+estr(i);
        return(typeid(void));
      }
    }
    ldinfo("efunc call: "+estr(args.size()));
    args[0]->make(env,stopExecution).get<efunc>().call(rvalue,vargs);
    return(*args[0]->make(env,stopExecution).get<efunc>().fReturn);
  }else if (!isPrimitive(args[0]->make(env,stopExecution).getTypeid())){
    for (i=1; i<args.size(); ++i){
      vargs[i-1].set(args[i]->make(env,stopExecution));
      if (vargs[i-1].isNull()) {
        stopExecution.flag=true;
//        stopExecution.line=line;
        stopExecution.error="null argument: "+estr(i);
        return(typeid(void));
      }
    }
    ldinfo("objop: "+estr(args.size()));
    args[0]->make(env,stopExecution).call( rvalue, name, vargs);
    return(rvalue.getTypeid());
  }

  while (vargs.size()+1<args.size()) vargs.add(evar());
  for (i=1; i<args.size(); ++i){
    vargs[i-1].set(args[i]->make(env,stopExecution));
    if (vargs[i-1].isNull()) {
      stopExecution.flag=true;
//      stopExecution.line=line;
      stopExecution.error="null argument: "+estr(i);
      return(typeid(void));
    }
  }
  ldinfo("objop: "+estr(args.size()));
  args[0]->make(env,stopExecution).call( rvalue, name, vargs);
  return(rvalue.getTypeid());
}

const type_info& checkobjprop(eatom_base* atom,evar& rvalue,estrhashof<evar>& env,stopExecutionStruct& stopExecution,const estr& name,ebasicarray<eatom_base*>& args,evararray& vargs)
{
  if (stopExecution.flag) return(typeid(void));
  evar obj;
  if (args.size()==2){
    if (args[1]->type!=2) {
      stopExecution.flag=true;
//      stopExecution.line=line;
      stopExecution.error="invalid property name: not a string";
      return(typeid(void));
    }

    args[0]->make(env,stopExecution);
    if (args[0]->remote==1){
      atom->remote=1;
      atom->exechost=args[0]->exechost;
      return(typeid(void));
    }

    obj.set(args[0]->make(env,stopExecution));
    estr prop(static_cast<eatom_value*>(args[1])->value);
   
    if (obj.hasProperty(prop))
      rvalue.set(obj.property(prop));
    else if (obj.hasMethod(prop))
      rvalue.set(obj.property(prop));
    else{
      stopExecution.flag=true;
//      stopExecution.line=line;
      stopExecution.error="no property or method found: "+prop;
    }
  }else{
    stopExecution.flag=true;
//    stopExecution.line=line;
    stopExecution.error="objprop argument size mismatch: "+estr(args.size());
  }
  return(rvalue.getTypeid());
}

const type_info& checkassign(eatom_base* atom,evar& rvalue,estrhashof<evar>& env,stopExecutionStruct& stopExecution,const estr& name,ebasicarray<eatom_base*>& args,evararray& vargs)
{
  if (stopExecution.flag) return(typeid(void));

  args[0]->make(env,stopExecution);
  args[1]->make(env,stopExecution);
  if (args[0]->remote==1){
    atom->remote=1;
    atom->exechost=args[0]->exechost;
    return(typeid(void));
  }
  if (args[1]->remote==1){
    if (args[1]->rvalue.isNull())
      args[1]->remoteExecute(env);
    evarRemoteValue(args[1]->rvalue);
  }

  ldinfo("assigning value");
  if (args[0]->make(env,stopExecution).isNull())
    args[0]->make(env,stopExecution).copy(args[1]->make(env,stopExecution));
  else
    args[0]->make(env,stopExecution)=args[1]->make(env,stopExecution);
  rvalue.set(args[0]->make(env,stopExecution));
  return(rvalue.getTypeid());
}

const type_info& checkassignref(eatom_base* atom,evar& rvalue,estrhashof<evar>& env,stopExecutionStruct& stopExecution,const estr& name,ebasicarray<eatom_base*>& args,evararray& vargs)
{
  if (stopExecution.flag) return(typeid(void));

  args[0]->make(env,stopExecution);
  args[1]->make(env,stopExecution);
  if (args[0]->remote==1){
    atom->remote=1;
    atom->exechost=args[0]->exechost;
    return(typeid(void));
  }
  if (args[1]->remote==1){
    args[1]->remoteExecute(env);
  }

  ldinfo("assigning value");
  args[0]->make(env,stopExecution).set(args[1]->make(env,stopExecution));
  rvalue.set(args[0]->make(env,stopExecution));
  return(rvalue.getTypeid());
}



// TODO: Make sure code Atoms are all deleted after parsing!!!





void find_prefix(estr& prefix,const estr& str){
  int i=MIN(str.len(),prefix.len())-1;
  for (i=0; i<str.len() && i<prefix.len() && str[i]==prefix[i]; ++i);
  prefix.del(i);
}


ecodeParser::ecodeParser(): error(CP_ERROR_NONE) {}

earray<estr> ecodeParser::autocomplete(estr& line,estrhashof<evar>& env){
  earray<estr> res;
  estrarray arr;
  split_ops_all(line,arr,opsall);
//  cout << arr << endl;
  if (arr.size()>1 && (arr[arr.size()-1]=="." || arr[arr.size()-2]==".")){
    estr filter,vname(arr[arr.size()-2]);
    if (arr.size()>2 && arr[arr.size()-2]==".") {
      filter=arr[arr.size()-1];
      vname=arr[arr.size()-3];
    }
    if (getParser().objects.exists(vname)){
      evar var(getParser().objects[vname]);
      if (getClasses().exists(var.getClass())){
        eclassBase& c(getClasses().values(var.getClass()));
        for (int i=0; i<c.properties.size(); ++i){
          estr &tmps(c.properties.keys(i));
          if (filter.len()==0 || tmps.substr(0,filter.len())==filter)
            res.add(tmps);
        }
        for (int i=0; i<c.parents.size(); ++i){
          eclassBase& cp(*c.parents.values(i));
          for (int j=0; j<cp.properties.size(); ++j){
            estr &tmps(cp.properties.keys(j));
            if (filter.len()==0 || tmps.substr(0,filter.len())==filter)
              res.add(tmps);
          }
        }
        for (int i=0; i<c.methods.size(); ++i){
          for (int k=0; k<c.methods.values(i).size(); ++k){
            eclassMethodBase &method(*c.methods.values(i).at(k));
            estr &tmps(c.methods.keys(i));
            if (filter.len()==0 || tmps.substr(0,filter.len())==filter)
              res.add(tmps);
           }
        }
        for (int i=0; i<c.parents.size(); ++i){
          eclassBase& cp(*c.parents.values(i));
          for (int l=0; l<cp.methods.size(); ++l){
            for (int k=0; k<cp.methods.values(l).size(); ++k){
              eclassMethodBase &method(*cp.methods.values(l).at(k));
              estr &tmps(cp.methods.keys(l));
              if (filter.len()==0 || tmps.substr(0,filter.len())==filter)
                res.add(tmps);
            }
          }
        }
      }
      if (res.size()==0)
        return(res);
  
      estr prefix=res[0];
      for (int j=1; j<res.size(); ++j)
        find_prefix(prefix,res[j]);
      line=line.substr(0,-filter.len()-1)+prefix;
      return(res);
    }
  }
  if (line.len()==0 || arr.size()==0 || isOpAll(arr[arr.size()-1])){
    for (int i=0; i<getParser().objects.size(); ++i)
      res.add(getParser().objects.keys(i));
    for (int i=0; i<getParser().funcs.size(); ++i)
      res.add(getParser().funcs.keys(i));
    return(res);
  }
  estr filter(arr[arr.size()-1]);
  if (filter[0]=='@'){
    edistcomp &dc(getDistComp());
    int i=filter.find(":");
    if (i==-1){
      if (filter.len()==1){
        for (int j=0; j<dc.hosts.size(); ++j)
          res.add("@"+dc.hosts.keys(j));
      }else{
        for (int j=0; j<dc.hosts.size(); ++j){
          estr tmp("@"+dc.hosts.keys(j));
          if (filter.len()<=tmp.len() && filter==tmp.substr(0,filter.len()))
            res.add("@"+dc.hosts.keys(j));
        }
      }
    }else{
      estr cmd(filter.substr(i+1));
      estr host(filter.substr(1,i-1));
      filter=cmd;
      res=dc.autocomplete(host,cmd);
    }
    if (res.size()==0)
      return(res);

    estr prefix=res[0];
    for (int j=1; j<res.size(); ++j)
      find_prefix(prefix,res[j]);
    line=line.substr(0,-filter.len()-1)+prefix;
    return(res);
  } else if (isFile(filter) || filter[0]=='"'){
    if (filter[0]=='"') filter.del(0,1);
    efile f(filter);
    estr dirname(f.dirname());
    estr bfname(f.basename());
    edir fdir(ls(dirname));
//    cout << "File autocomplete: "<< filter << " :: " << dirname << " :: " << bfname << endl;
    if (bfname.len()==0){
      for (int j=0; j<fdir.dirs.size(); ++j)
        res.add(dirname + "/" + fdir.dirs.keys(j));
      for (int j=0; j<fdir.files.size(); ++j)
        res.add(dirname + "/" + fdir.files.keys(j));
      return(res);
    }
 
    for (int j=0; j<fdir.dirs.size(); ++j){
      estr& tmp(fdir.dirs.keys(j));
      if (bfname.len()<=tmp.len() && bfname==tmp.substr(0,bfname.len()))
        res.add(dirname+"/"+tmp);
    }
    for (int j=0; j<fdir.files.size(); ++j){
      estr& tmp(fdir.files.keys(j));
      if (bfname.len()<=tmp.len() && bfname==tmp.substr(0,bfname.len()))
        res.add(dirname+"/"+tmp);
    }
    if (res.size()==0)
      return(res);

    estr prefix=res[0];
    for (int j=1; j<res.size(); ++j)
      find_prefix(prefix,res[j]);
    line=line.substr(0,-filter.len()-1)+prefix;
    return(res);
  }
//  cout << "Variable/function autocomplete: "<< filter << endl;
  for (int j=0; j<getParser().objects.size(); ++j){
    estr& tmp(getParser().objects.keys(j));
    if (filter.len()<=tmp.len() && filter==tmp.substr(0,filter.len()))
      res.add(tmp);
  }
  for (int j=0; j<getParser().funcs.size(); ++j){
    estr& tmp(getParser().funcs.keys(j));
    if (filter.len()<=tmp.len() && filter==tmp.substr(0,filter.len()))
      res.add(tmp);
  }
  if (res.size()==0)
    return(res);
  estr prefix=res[0];
  for (int j=1; j<res.size(); ++j)
    find_prefix(prefix,res[j]);
//  cout << "prefix: " << prefix << endl;
  line=line.substr(0,-filter.len()-1)+prefix;
  return(res);
}

ecodeAtomBlock* ecodeParser::parse(const estr& code)
{
  if (!code.len()) return(0x00);
/*
  estr normcode(code);
  long i,ip;
  for (i=0,ip=0; i<code.len(); ++i,++ip){
    if (code[i]=='/' && i+1<code.len() && code[i+1]=='/'){
      skip_comment(code,i);
      --i;
      --ip;
      continue;
    }else if (code[i]=='/' && i+1<code.len() && code[i+1]=='*'){
      skip_longcomment(code,i);
      --i;
      --ip;
      continue;
    }
    if (i==ip) continue;
    normcode[ip]=code[i];
  }
  normcode.del(ip);
  ecodeAtomBlock *catom=new ecodeAtomBlock;
  catom->parse(*this,normcode);
*/
  ecodeAtomBlock *catom=new ecodeAtomBlock;
  catom->parse(*this,code);
  if (error==0) return(catom);
  delete catom;
  return(0x00);
}

void ecodeParser::setError(int errcode){ error=errcode; } //cerr<<"setError: " << errcode << endl;


size_t unserialAtom(eatom_base* &batom,const estr& data,size_t ip)
{
  unsigned int ctype;
  ip=unserialuint(ctype,data,ip);
  if (ip==-1) return(ip);
  batom=newAtomByType(ctype);
  ip=batom->unserial(data,ip); 
  return(ip);
}

size_t unserialCodeAtom(ecodeAtom* &catom,const estr& data,size_t ip)
{
  unsigned int ctype;
  ip=unserialuint(ctype,data,ip);
  if (ip==-1) return(ip);
  catom=newCodeAtomByType(ctype);
  ip=catom->unserial(data,ip); 
  return(ip);
}



void ecodeAtomBlock::clear()
{
  int i;
  for (i=0; i<exec.size(); ++i)
    delete exec[i];
  exec.clear();
}

ecodeAtomBlock::~ecodeAtomBlock()
{
  clear();
}

estr ecodeAtomBlock::check(estrhashof<evar>& env,stopExecutionStruct& stopExecution)
{
//  eprofile p(getProfiler("ecodeAtomBlock::interpret"));

//  if (exechost.len())
//    return(getDistComp().check(exechost,code));

  int i;
  for (i=0; i+1<exec.size(); ++i)
    exec[i]->check(env,stopExecution);
  if (exec.size())
    return(exec[i]->check(env,stopExecution));
  return("");
}

evar ecodeAtomBlock::interpret(estrhashof<evar>& env,stopExecutionStruct &stopExecution,int& loopControl)
{
  if (stopExecution.flag || loopControl) return(evar());

//  eprofile p(getProfiler("ecodeAtomBlock::interpret"));

  if (exechost.len()){
//    rvalue.set(getDistComp().executeAtom(exechost,this,env));
//    return(getDistComp().interpret(exechost,code));
    for (int i=0; i+1<exec.size(); ++i){
//      exec[i]->exechost=exechost;
      getDistComp().executeCodeAtom(exechost,exec[i],env);
    }
    if (exec.size()){
//      exec[exec.size()-1]->exechost=exechost;
      return(getDistComp().executeCodeAtom(exechost,exec[exec.size()-1],env));
    }
    return(evar());
  }

  for (int i=0; i+1<exec.size() && !stopExecution.flag && !loopControl; ++i)
    exec[i]->interpret(env,stopExecution,loopControl);
  if (exec.size() && !stopExecution.flag && !loopControl)
    return(exec[exec.size()-1]->interpret(env,stopExecution,loopControl));
  return(evar());
}
void ecodeAtomBlock::parse(ecodeParser& cparser,const estr& str)
{
  if (!str.len()) return;
  if (exechost.len()){
    code=str;
    return;
  }

  exec.clear();
  ecodeAtom *atom;
  long i;
  int line;

  i=0;
  line=0;
  while (i<str.len()){
    atom=cparser.getatom(str,i,line);
    if (atom)
      exec.add(atom);
  }
}

void ecodeAtomBlock::serial(estrhashof<evar>& env,estr& data)
{
  serialuint(CA_BLOCK,data);
  serialuint(exec.size(),data);
  for (int i=0; i<exec.size(); ++i)
    exec[i]->serial(env,data);
}

size_t ecodeAtomBlock::unserial(const estr& data,size_t ip)
{
//  cerr << "eatom: " << name << " ip: " << ip << "/" << data.len() << endl;
  unsigned int count;
  ip=unserialuint(count,data,ip);
//  cerr << "eatom args: " << count << " ip: " << ip << "/" << data.len() << endl;
//  if (ip==-1) return(ip);

  for (int i=0; i<count && ip!=-1; ++i){
    ecodeAtom *catom=0x00;
    ip=unserialCodeAtom(catom,data,ip);
    if (catom)
      exec.add(catom);
  }
  return(ip);
}



evar interpret_line(estrhashof<evar>& env,stopExecutionStruct& stopExecution,const estr& str);

class ecodeAtomArg : public ecodeAtom
{
 public:
  estr cond;
  virtual evar interpret(estrhashof<evar>& env,stopExecutionStruct& stopExecution,int& loopControl);
  void serial(estrhashof<evar>& env,estr& data);
  size_t unserial(const estr& data,size_t ip);
};
evar ecodeAtomArg::interpret(estrhashof<evar>& env,stopExecutionStruct& stopExecution,int& loopControl)
{
  if (stopExecution.flag) return(evar());

  ldinfo("ecodeAtomArg::interpret");
  return(interpret_line(env,stopExecution,cond));
}

void ecodeAtomArg::serial(estrhashof<evar>& env,estr& data)
{
  cond.serial(data);
}

size_t ecodeAtomArg::unserial(const estr& data,size_t ip)
{
  ip=cond.unserial(data,ip);
  return(ip);
}

class ecodeAtomSingle : public ecodeAtom
{
 public:
  eatom *root;
//  estr exec;
  bool showReturn;
  bool parse(const estr& code);
  virtual estr check(estrhashof<evar>& env,stopExecutionStruct& stopExecution);
  virtual evar interpret(estrhashof<evar>& env,stopExecutionStruct& stopExecution,int& loopControl);
  void serial(estrhashof<evar>& env,estr& data);
  size_t unserial(const estr& data,size_t ip);

  ecodeAtomSingle(): root(0x00) {}
  ~ecodeAtomSingle() { if (root) delete root; }
};

bool ecodeAtomSingle::parse(const estr& code)
{
  estrarray sa;
  if (!split_atoms2(code,sa)) return(false);

  root=new eatom(sa);
  estr s;
  root->print(s);
  ldinfo("interpret_line: command tree: "+s);
  return(true);
}

estr ecodeAtomSingle::check(estrhashof<evar>& env,stopExecutionStruct& stopExecution)
{
  ldinfo("ecodeAtomSingle::interpret");

  if (root==0x00) return("");

  root->clear();
  estr res(root->check(env,stopExecution).name());
  if (root->remote==1){
//    root->remoteExecute(env);
//    res.set(root->make(env));
  }
  return(res);
}


evar ecodeAtomSingle::interpret(estrhashof<evar>& env,stopExecutionStruct& stopExecution,int& loopControl)
{
  if (stopExecution.flag || loopControl) return(evar());
//  eprofile p(getProfiler("ecodeAtomSingle::interpret"));
  ldinfo("ecodeAtomSingle::interpret");

/*
  if (!exec.len()) return(evar());

  estrarray sa;
  if (split_atoms2(exec,sa)){
    eatom root(sa);
    estr s;
    root.print(s);
    ldinfo("interpret_line: command tree: "+s);
    evar res(root.make(env));
    if (!res.isNull() && showReturn)
      cout << res << endl;
    return(res);
  }
  return(evar());
*/

  if (root==0x00) return(evar());

  root->clear();
  evar res;
  try {
    res.set(root->make(env,stopExecution));
  }
  catch (const std::out_of_range& oor) {
    stopExecution.line=line;
    stopExecution.flag=true;
    stopExecution.error="Out of Range exception";
    res.clear();
    return(evar());
  }
/*
  catch (...) {
    stopExecution=true;
    lerror("Exception error");
    res.clear();
    return(evar());
  }
*/
//  cout << "condition value, remote: " << root->remote <<  endl;
  if (root->remote==1){
    if (root->rvalue.isNull())
      root->remoteExecute(env);
    evarRemoteValue(root->rvalue);
//    res.set(root->make(env));
//    cout << "condition value: " << root->rvalue.isNull() << " - " << root->rvalue << " - " << root->rvalue.getClass() <<  endl;
    res.set(root->rvalue);
//    cout << "condition value: " << res.isNull() << " - " << res << " - " << res.getClass() <<  endl;
  }
  if (!res.isNull() && showReturn)
    cout << res << endl;
  return(res);
}

void ecodeAtomSingle::serial(estrhashof<evar>& env,estr& data)
{
  serialuint(CA_SINGLE,data);
  root->setRemote();
  root->serial(env,data);
}

size_t ecodeAtomSingle::unserial(const estr& data,size_t ip)
{
  root=new eatom;
  ip=root->unserial(data,ip);
  return(ip);
}

class ecodeAtomReturn : public ecodeAtom
{
 public:
  ecodeAtomReturn() {}
  ~ecodeAtomReturn() {}
  virtual evar interpret(estrhashof<evar>& env,stopExecutionStruct& stopExecution,int& loopControl);
  void serial(estrhashof<evar>& env,estr& data);
  size_t unserial(const estr& data,size_t ip);
};
evar ecodeAtomReturn::interpret(estrhashof<evar>& env,stopExecutionStruct &stopExecution,int& loopControl)
{
  if (stopExecution.flag || loopControl) return(evar());
  loopControl=-100;
// retVar=value in front of return  
  return(evar());
}
void ecodeAtomReturn::serial(estrhashof<evar>& env,estr& data)
{
  serialuint(CA_RETURN,data);
}
size_t ecodeAtomReturn::unserial(const estr& data,size_t ip)
{
  return(ip);
}

class ecodeAtomContinue : public ecodeAtom
{
 public:
  ecodeAtomContinue() {}
  ~ecodeAtomContinue() {}
  virtual evar interpret(estrhashof<evar>& env,stopExecutionStruct& stopExecution,int& loopControl);
  void serial(estrhashof<evar>& env,estr& data);
  size_t unserial(const estr& data,size_t ip);
};
evar ecodeAtomContinue::interpret(estrhashof<evar>& env,stopExecutionStruct &stopExecution,int& loopControl)
{
  if (stopExecution.flag || loopControl) return(evar());
  loopControl=1;
  return(evar());
}
void ecodeAtomContinue::serial(estrhashof<evar>& env,estr& data)
{
  serialuint(CA_CONTINUE,data);
}
size_t ecodeAtomContinue::unserial(const estr& data,size_t ip)
{
  return(ip);
}

class ecodeAtomBreak : public ecodeAtom
{
 public:
  ecodeAtomBreak() {}
  ~ecodeAtomBreak() {}
  virtual evar interpret(estrhashof<evar>& env,stopExecutionStruct& stopExecution,int& loopControl);
  void serial(estrhashof<evar>& env,estr& data);
  size_t unserial(const estr& data,size_t ip);
};
evar ecodeAtomBreak::interpret(estrhashof<evar>& env,stopExecutionStruct &stopExecution,int& loopControl)
{
  if (stopExecution.flag || loopControl) return(evar());
  loopControl=-1;
  return(evar());
}
void ecodeAtomBreak::serial(estrhashof<evar>& env,estr& data)
{
  serialuint(CA_BREAK,data);
}
size_t ecodeAtomBreak::unserial(const estr& data,size_t ip)
{
  return(ip);
}

class ecodeAtomIf : public ecodeAtom
{
 public:
//  estr cond;
//  estr exectrue;
//  estr execfalse;

  ecodeAtom *cond;
  ecodeAtom *exectrue;
  ecodeAtom *execfalse;
  ecodeAtomIf(): cond(0x00),exectrue(0x00),execfalse(0x00) {}
  ~ecodeAtomIf() { if (cond) delete cond; if (exectrue) delete exectrue; if (execfalse) delete execfalse; }
  virtual evar interpret(estrhashof<evar>& env,stopExecutionStruct& stopExecution,int& loopControl);
  void serial(estrhashof<evar>& env,estr& data);
  size_t unserial(const estr& data,size_t ip);
};
evar ecodeAtomIf::interpret(estrhashof<evar>& env,stopExecutionStruct &stopExecution,int& loopControl)
{
  if (stopExecution.flag || loopControl) return(evar());

//  evar condval;
  bool condval;
  evar v(cond->interpret(env,stopExecution,loopControl));
  if (v.isNull()){
    stopExecution.flag=true;
    stopExecution.line=line;
    stopExecution.error="condition is null";
    return(evar());
  }else if (v.getTypeid()==typeid(bool))
    condval=v.get<bool>();
  else if (v.getTypeid()==typeid(int))
    condval=v.get<int>();
  else if (v.getTypeid()==typeid(long))
    condval=v.get<long>();
  else {
    stopExecution.flag=true;
    stopExecution.line=line;
    stopExecution.error="condition is not boolean or int";
    return(evar());
  }

  if (condval)
    exectrue->interpret(env,stopExecution,loopControl);
  else if (execfalse)
    execfalse->interpret(env,stopExecution,loopControl);

  return(evar());
}
void ecodeAtomIf::serial(estrhashof<evar>& env,estr& data)
{
  serialuint(CA_IF,data);
  cond->serial(env,data);
  exectrue->serial(env,data);
  serialuint((execfalse?1:0),data);
  if (execfalse)
    execfalse->serial(env,data);
}

size_t ecodeAtomIf::unserial(const estr& data,size_t ip)
{
  ip=unserialCodeAtom(cond,data,ip);
  ip=unserialCodeAtom(exectrue,data,ip);
  unsigned int exfalse=0;
  ip=unserialuint(exfalse,data,ip);
  if (exfalse)
    ip=unserialCodeAtom(execfalse,data,ip);
  return(ip);
}


class ecodeAtomFor : public ecodeAtom
{
 public:
  ecodeAtom *init;
  ecodeAtom *loop;
  ecodeAtom *cond;

  ecodeAtom *exec;

  ecodeAtomFor(): init(0x00),loop(0x00),cond(0x00),exec(0x00) {}
  ~ecodeAtomFor(){ if (init) delete init; if (loop) delete loop; if (cond) delete cond; if (exec) delete exec; }
  virtual evar interpret(estrhashof<evar>& env,stopExecutionStruct& stopExecution,int& loopControl);
  void serial(estrhashof<evar>& env,estr& data);
  size_t unserial(const estr& data,size_t ip);
};
evar ecodeAtomFor::interpret(estrhashof<evar>& env,stopExecutionStruct& stopExecution,int& loopControl)
{
  if (stopExecution.flag || loopControl) return(evar());

  init->interpret(env,stopExecution,loopControl);

  bool condval;
  evar v;

  v.set(cond->interpret(env,stopExecution,loopControl));
  if (v.isNull()){
    stopExecution.flag=true;
    stopExecution.line=line;
    stopExecution.error="condition is null";
    return(evar());
  }else if (v.getTypeid()==typeid(bool))
    condval=v.get<bool>();
  else if (v.getTypeid()==typeid(int))
    condval=v.get<int>();
  else if (v.getTypeid()==typeid(long))
    condval=v.get<long>();
  else {
    stopExecution.flag=true;
    stopExecution.line=line;
    stopExecution.error="condition is not boolean or int";
    return(evar());
  }

  while(condval && !stopExecution.flag){
    exec->interpret(env,stopExecution,loopControl);
    if (loopControl<0) break;
    loopControl=0;
    loop->interpret(env,stopExecution,loopControl);
    v.set(cond->interpret(env,stopExecution,loopControl));
    if (v.isNull()){
      stopExecution.flag=true;
      stopExecution.line=line;
      stopExecution.error="condition is null";
      return(evar());
    }else if (v.getTypeid()==typeid(bool))
      condval=v.get<bool>();
    else if (v.getTypeid()==typeid(int))
      condval=v.get<int>();
    else if (v.getTypeid()==typeid(long))
      condval=v.get<long>();
    else {
      stopExecution.flag=true;
      stopExecution.line=line;
      stopExecution.error="condition is not boolean or int";
      return(evar());
    }
  }
  if (loopControl<0) ++loopControl;
//  loopControl=0;
  return(evar());
}
void ecodeAtomFor::serial(estrhashof<evar>& env,estr& data)
{
  serialuint(CA_FOR,data);
  init->serial(env,data);
  loop->serial(env,data);
  cond->serial(env,data);
  exec->serial(env,data);
}
size_t ecodeAtomFor::unserial(const estr& data,size_t ip)
{
  ip=unserialCodeAtom(init,data,ip);
  ip=unserialCodeAtom(loop,data,ip);
  ip=unserialCodeAtom(cond,data,ip);
  ip=unserialCodeAtom(exec,data,ip);
  return(ip);
}


class ecodeAtomWhile : public ecodeAtom
{
 public:
  ecodeAtom *cond;
  ecodeAtom *exec;

  ecodeAtomWhile(): cond(0x00),exec(0x00) {}
  ~ecodeAtomWhile() { if (cond) delete cond; if (exec) delete exec; }
  virtual evar interpret(estrhashof<evar>& env,stopExecutionStruct& stopExecution,int& loopControl);
  void serial(estrhashof<evar>& env,estr& data);
  size_t unserial(const estr& data,size_t ip);
};
evar ecodeAtomWhile::interpret(estrhashof<evar>& env,stopExecutionStruct& stopExecution,int& loopControl)
{
  if (stopExecution.flag || loopControl) return(evar());

  evar v;
  bool condval;

  v.set(cond->interpret(env,stopExecution,loopControl));
  if (v.isNull()){
    stopExecution.flag=true;
    stopExecution.line=line;
    stopExecution.error="condition is null";
    return(evar());
  }else if (v.getTypeid()==typeid(bool))
    condval=v.get<bool>();
  else if (v.getTypeid()==typeid(int))
    condval=v.get<int>();
  else if (v.getTypeid()==typeid(long))
    condval=v.get<long>();
  else {
    stopExecution.flag=true;
    stopExecution.line=line;
    stopExecution.error="condition is not boolean or int";
    return(evar());
  }

  while(condval && !stopExecution.flag){
    exec->interpret(env,stopExecution,loopControl);
    if (loopControl<0) break;
    loopControl=0;
    v.set(cond->interpret(env,stopExecution,loopControl));
    if (v.isNull()){
      stopExecution.flag=true;
      stopExecution.line=line;
      stopExecution.error="condition is null";
      return(evar());
    }else if (v.getTypeid()==typeid(bool))
      condval=v.get<bool>();
    else if (v.getTypeid()==typeid(int))
      condval=v.get<int>();
    else if (v.getTypeid()==typeid(long))
      condval=v.get<long>();
    else {
      stopExecution.flag=true;
      stopExecution.line=line;
      stopExecution.error="condition is not boolean or int";
      return(evar());
    }
  }
  if (loopControl<0)
    ++loopControl;
  return(evar());
}
void ecodeAtomWhile::serial(estrhashof<evar>& env,estr& data)
{
  serialuint(CA_WHILE,data);
  cond->serial(env,data);
  exec->serial(env,data);
}
size_t ecodeAtomWhile::unserial(const estr& data,size_t ip)
{
  ip=unserialCodeAtom(cond,data,ip);
  ip=unserialCodeAtom(exec,data,ip);
  return(ip);
}




class ecodeAtomDo : public ecodeAtom
{
 public:
  ecodeAtom *cond;
  ecodeAtom *exec;
  ecodeAtomDo(): cond(0x00),exec(0x00) {}
  ~ecodeAtomDo() { if (cond) delete cond; if (exec) delete exec; }
  virtual evar interpret(estrhashof<evar>& env,stopExecutionStruct& stopExecution,int& loopControl);
  void serial(estrhashof<evar>& env,estr& data);
  size_t unserial(const estr& data,size_t ip);
};
evar ecodeAtomDo::interpret(estrhashof<evar>& env,stopExecutionStruct& stopExecution,int& loopControl)
{
  if (stopExecution.flag || loopControl) return(evar());

  bool condval;
  evar v;

  do{
    exec->interpret(env,stopExecution,loopControl);
    if (loopControl<0) break;
    loopControl=0;
    v.set(cond->interpret(env,stopExecution,loopControl));
    if (v.isNull()){
      stopExecution.flag=true;
      stopExecution.line=line;
      stopExecution.error="condition is null";
      return(evar());
    }else if (v.getTypeid()==typeid(bool))
      condval=v.get<bool>();
    else if (v.getTypeid()==typeid(int))
      condval=v.get<int>();
    else if (v.getTypeid()==typeid(long))
      condval=v.get<long>();
    else {
      stopExecution.flag=true;
      stopExecution.line=line;
      stopExecution.error="condition is not boolean or int";
      return(evar());
    }
  }while(condval && !stopExecution.flag);
  if (loopControl<0)
    ++loopControl;
  return(evar());
}
void ecodeAtomDo::serial(estrhashof<evar>& env,estr& data)
{
  serialuint(CA_DO,data);
  cond->serial(env,data);
  exec->serial(env,data);
}
size_t ecodeAtomDo::unserial(const estr& data,size_t ip)
{
  ip=unserialCodeAtom(cond,data,ip);
  ip=unserialCodeAtom(exec,data,ip);
  return(ip);
}

/*
size_t ecodeAtomDo::unserial(const estr& data,size_t i)
{
  cond=ecodeAtom::unserial(data,i);
  exec=ecodeAtom::unserial(data,i);
  return(i);
}
*/



//#include "efuncbase.h"



class ecodeAtomFunction : public ecodeAtom
{
 public:
  estr name;
  ecodeAtom *args;
  ecodeAtom *exec;
  estr code;
//  ecodeAtomFunction(): args(0x00),exec(0x00) {}
//  ~ecodeAtomFunction(){ if (args) delete args; if (exec) delete exce; }
  virtual evar interpret(estrhashof<evar>& env,stopExecutionStruct& stopExecution,int& loopControl);
  void serial(estrhashof<evar>& env,estr& data);
  size_t unserial(const estr& data,size_t ip);
};
evar ecodeAtomFunction::interpret(estrhashof<evar>& env,stopExecutionStruct &stopExecution,int& loopControl)
{
  if (stopExecution.flag || loopControl) return(evar());

  if (name.len() && !getParser().funcs.exists(name))
    getParser().funcs.add(name,earray<efunc>());

  efuncCode *funcCode = new efuncCode;
  funcCode->exec=exec;
  funcCode->code=code;
  if (args->type==CA_ARG){
    funcCode->args=static_cast<ecodeAtomArg*>(args)->cond;
  }else{
    stopExecution.flag=true;
    stopExecution.line=line;
    stopExecution.error="argument block of function definition is not (argument) type";
    return(evar());
  }
  efunc func;
//  func.func=funcCode;
//  ++funcCode->pcount;
  func.setFunc(funcCode);

  if (name.len())
    getParser().funcs[name].add(func);
  // register function
  return(func);
}
void ecodeAtomFunction::serial(estrhashof<evar>& env,estr& data)
{
  serialuint(CA_FUNCTION,data);
  code.serial(data);
  args->serial(env,data);
  exec->serial(env,data);
}
size_t ecodeAtomFunction::unserial(const estr& data,size_t ip)
{
  ip=code.unserial(data,ip);
  ip=unserialCodeAtom(args,data,ip);
  ip=unserialCodeAtom(exec,data,ip);
  return(ip);
}


ecodeAtom* newCodeAtomByType(unsigned int catype)
{
  switch (catype){
    case CA_BLOCK:
      return(new ecodeAtomBlock);
    case CA_SINGLE:
      return(new ecodeAtomSingle);
    case CA_ARG:
      return(new ecodeAtomArg);
    case CA_IF:
      return(new ecodeAtomIf);
    case CA_FOR:
      return(new ecodeAtomFor);
    case CA_WHILE:
      return(new ecodeAtomWhile);
    case CA_DO:
      return(new ecodeAtomDo);
    case CA_FUNCTION:
      return(new ecodeAtomFunction);
    case CA_CONTINUE:
      return(new ecodeAtomContinue);
    case CA_BREAK:
      return(new ecodeAtomBreak);
   }
  return(0x00);
}


/*
void getstratom(const estr& str,int ind,ecodeAtom& catom)
{
  int i;

  for (i=ind+1; i<str.len() && str[i]!='"'; ++i){
    if (str[i]=='\\') ++i;
  }
  catom.text=str.substr(ind,1+i-ind);
  catom.type=CA_STR;
}

void getnumatom(const estr& str,int ind,ecodeAtom& catom)
{
  int i;
  bool dot;
  bool exp;
  bool hex;

  dot=false; exp=false; hex=false;

  i=ind;
  if (str[i]=='-') ++i;
  for (; i<str.len() && (str[i]>=0 && str[i]<=9 || !dot  && str[i]=='.' || !exp && (str[i]=='e'||str[i]=='E') || !hex && str[i]=='x' || hex && (str[i]>='A' && str[i]<='F' || str[i]>='a' && str[i]<='f')); ++i){
    if (str[i]=='.') dot=true;
    if (str[i]=='e' || str[i]=='E'){
      exp=true;
      if (i+1<str.len() && str[i+1]=='-') ++i;
    }
    if (str[i]=='x')
      hex=true;
  }
  catom.text=str.substr(ind,i-ind);
  catom.type=CA_NUM;
}
*/


void skip_comment(const estr& str,long& i,int& line);
void skip_longcomment(const estr& str,long& i,int& line);

void skip_blank(const estr& str,long& ind){
  while (ind<str.len() && (str[ind]==' ' || str[ind]=='\t' || str[ind]=='\r' || str[ind]=='\n' || (str[ind]=='/' && ind+1<str.len() && (str[ind+1]=='/' || str[ind+1]=='*')))) {
    if (str[ind]=='/' && ind+1<str.len()) {
      if (str[ind+1]=='/') {
        skip_comment(str,ind);
        continue;
      } else if (str[ind+1]=='*') {
        skip_longcomment(str,ind);
        continue;
      }
    }
    ++ind;
  }
//  for (;i<str.len() && str[i]==' '; ++i);
}

void skipblanks(const estr& str,long& ind,int& line)
{
  while (ind<str.len() && (str[ind]==' ' || str[ind]=='\t' || str[ind]=='\r' || str[ind]=='\n' || (str[ind]=='/' && ind+1<str.len() && (str[ind+1]=='/' || str[ind+1]=='*')))) {
    if (str[ind]=='\n') ++line;
    else if (str[ind]=='/' && ind+1<str.len()) {
      if (str[ind+1]=='/') {
        skip_comment(str,ind,line);
        continue;
      } else if (str[ind+1]=='*') {
        skip_longcomment(str,ind,line);
        continue;
      }
    }
    ++ind;
  }
}

void skipstr(const estr& str,long& ind)
{
  for (++ind; ind<str.len() && str[ind]!='"'; ++ind){
    if (str[ind]=='\\') ++ind;
  }
}


ecodeAtom *ecodeParser::getargatom(const estr& str,long& ind,int& line)
{
  int count;

  skipblanks(str,ind,line);

  int i=ind;
  if (str[ind]!='(') return(0x00);

  count=1;
  for (++ind ; ind<str.len() && count; ++ind){
    if (str[ind]=='"')
      skipstr(str,ind);
    if (str[ind]=='(') ++count;
    if (str[ind]==')') --count;
  }
//  cerr << "getargatom: " << count << endl;
  if (count) {
    setError(CP_ERROR_MISSING_PARENTESIS);
    return(0x00);
  }

  ecodeAtomArg *aatom = new ecodeAtomArg;
  aatom->line=line;
  aatom->cond=str.substr(i+1,ind-i-2);
//  cerr << "arg string: " << aatom->cond << endl;
  aatom->type=CA_ARG;
//  ++ind;
  ldinfo("code arg atom: "+aatom->cond);
  return(aatom);
}

ecodeAtomBlock *ecodeParser::getblockatom(const estr& str,long& ind,int& line)
{
  int i=ind;
  int count;

  if (str[ind]!='{') { lderror("first character is not a {"); return(0x00); }

  ecodeAtomBlock *batom = new ecodeAtomBlock;
  batom->line=line;
  count=1;
  for (++ind ; ind<str.len() && count; ++ind){
    if (str[ind]=='"')
      skipstr(str,ind);
    if (str[ind]=='{') ++count;
    if (str[ind]=='}') --count;
  }
//  cerr << "getblockatom count: " << count << endl;
  if (count){
    setError(CP_ERROR_MISSING_PARENTESIS);
    return(0x00);
  }
  batom->parse(*this,str.substr(i+1,ind-i-2));
  batom->type=CA_CODE;
//  cerr << "block string: " << str.substr(i+1,ind-i-2) << endl;
  ldinfo("code block atom: "+str.substr(i+1,ind-i-2));
//  ++ind;
  return(batom);
}

void ecodeParser::getcontrolstr(const estr& str,long& ind,int& line,estr& control)
{
  skipblanks(str,ind,line);
  int i=ind;
  for (; ind<str.len() && (str[ind]=='@' || str[ind]==':' || (str[ind]>='a' && str[ind]<='z') || (str[ind]>='A' && str[ind]<='Z') || (str[ind]>='0' && str[ind]<='9') || str[ind]=='_'); ++ind);
  control=str.substr(i,ind-i);
}

ecodeAtom *ecodeParser::getcodeatom(const estr& str,long& ind,int& line)
{
  estr control;

  int i;
  i=ind;

  getcontrolstr(str,ind,line,control); 
  if (control=="if")
    return(getcodeifatom(str,ind,line));
  else if (control=="for")
    return(getcodeforatom(str,ind,line));
  else if (control=="while")
    return(getcodewhileatom(str,ind,line));
  else if (control=="do")
    return(getcodedoatom(str,ind,line));
  else if (control=="return")
    return(new ecodeAtomReturn);
   else if (control=="continue")
    return(new ecodeAtomContinue);
  else if (control=="break")
    return(new ecodeAtomBreak);
  else if (control=="function")
    return(getcodefunctionatom(str,ind,line));
  else if (control.len() && control[0]=='@' && ind<str.len() && str[ind]=='{'){
    ecodeAtomBlock *catom=getblockatom(str,ind,line);
    if (catom==0x00) return(catom);
    catom->exechost=control.substr(1);
    if (catom->exechost[catom->exechost.len()-1]==':')
      catom->exechost.del(-1);
    return(catom);
  }else{ // not a statement control keyword, use line evaluator
    ind=i;
    return(getsingleatom(str,ind,line));
  }
}

ecodeAtom *ecodeParser::getsingleatom(const estr& str,long& ind,int& line)
{
  long i=ind;

  int count=0;
  for ( ;ind<str.len() && (str[ind]!=';' || count); ++ind){
    if (str[ind]=='"')
      skipstr(str,ind);
    else if (str[ind]=='{')
      ++count;
    else if (str[ind]=='}')
      --count;
  }
//  cerr << "getsingleatom, count: " << count << endl;
  if (count){
//    cerr << "error: getsingleatom: " << str.substr(i,ind-i) << endl;
    setError(CP_ERROR_MISSING_PARENTESIS);
    return(0x00);
  }

  ecodeAtomSingle* satom = new ecodeAtomSingle();
  satom->line=line;

  int trimleft=0; // remove spaces between last code and ";"
  if (ind>0 && str[ind-1]==';') trimleft=1;
  while (ind-1-trimleft>i && (str[ind-1-trimleft]==' ' || str[ind-1-trimleft]=='\t' || str[ind-1-trimleft]=='\n')) ++trimleft;

  estr tmpstr(str.substr(i,ind-i-trimleft));
//  cerr << "getsingleatom: " << tmpstr << endl;
  satom->parse(tmpstr);
//  satom->exec=tmpstr;
  satom->type=CA_CODE;
  satom->showReturn=false;
  if (ind>=str.len()) satom->showReturn=true;

  ++ind;
  ldinfo("single atom: "+tmpstr);
  return(satom);
}

ecodeAtom *ecodeParser::getatom(const estr& str,long& ind,int& line)
{
/*  if (str[ind]='"')
    getstratom(str,ind,catom);
  else if (str[ind]>='0' && str[ind]<='9' || str[ind]=='-')
    getnumatom(str,ind,catom);*/

  skipblanks(str,ind,line);
  if (ind>=str.len()) return(0x00);

  if (str[ind]=='"' || (str[ind]>='0' && str[ind]<='9') || str[ind]=='-')
    return(getsingleatom(str,ind,line)); // single statements
//  else if (str[ind]=='(')
//    return(getargatom(str,ind));
  else if (str[ind]=='{')
    return(getblockatom(str,ind,line)); // get block code
  else
    return(getcodeatom(str,ind,line)); // if, while, for, funciton, control statements
}

ecodeAtom *ecodeParser::getcodeifatom(const estr& str,long& i,int& line)
{
  ecodeAtomIf *ifatom = new ecodeAtomIf;
  ifatom->line=line;

  ifatom->cond=getargatom(str,i,line);
  if (!ifatom->cond){
    setError(CP_ERROR_MISSING_IF_ARG);
    return(0x00);
  } else if (ifatom->cond->type!=CA_ARG){
    setError(CP_ERROR_WRONG_IF_ARG);
    return(0x00);
  }

    
//   ifatom->cond->type!=CA_ARG,"\"if\" missing condition: "+str.substr(i),(0x00));
//  }

  ifatom->exectrue=getatom(str,i,line);
  if (!ifatom->exectrue){
    setError(CP_ERROR_MISSING_IF_EXEC);
    return(0x00);
  } else if (ifatom->exectrue->type==CA_ARG){
    setError(CP_ERROR_WRONG_IF_EXEC);
    return(0x00);
  }

//  lerrorifr(!ifatom->exectrue || ifatom->exectrue->type==CA_ARG,"condition found where statement was expected",(0x00));

  ifatom->execfalse=0x00;

  estr control;
  int itmp;
  itmp=i;
  getcontrolstr(str,i,line,control);
//  cout << " > if : getcontrolstr = "<<control<<endl;
  if (control=="else"){
    ifatom->execfalse = getatom(str,i,line);
//    lerrorifr(!ifatom->execfalse || ifatom->execfalse->type==CA_ARG,"condition found where statement was expected",(0x00));
    if (!ifatom->execfalse){
      setError(CP_ERROR_MISSING_ELSE_EXEC);
      return(0x00);
    } else if (ifatom->execfalse->type==CA_ARG){
      setError(CP_ERROR_WRONG_ELSE_EXEC);
      return(0x00);
    }

  }else
   i=itmp;

  return(ifatom);
}

ecodeAtom *ecodeParser::getcodeforatom(const estr& str,long& i,int& line)
{
  ecodeAtomFor *foratom = new ecodeAtomFor;
  foratom->line=line;

  ecodeAtom *tmpatom;

  tmpatom=getargatom(str,i,line);
  if (!tmpatom){
    setError(CP_ERROR_MISSING_FOR_ARG);
    return(0x00);
  } else if (tmpatom->type!=CA_ARG){
    setError(CP_ERROR_WRONG_FOR_ARG);
    return(0x00);
  }
//  lerrorifr(!tmpatom || tmpatom->type!=CA_ARG,"\"for\" missing condition",(0x00));

  long tmpi;

  tmpi=0;
  foratom->init=getatom(((ecodeAtomArg*)tmpatom)->cond,tmpi,line);
  lerrorifr(!foratom->init || foratom->init->type==CA_ARG,"\"for\" missing init part",(0x00));

  foratom->cond=getatom(((ecodeAtomArg*)tmpatom)->cond,tmpi,line);
  lerrorifr(!foratom->cond || foratom->cond->type==CA_ARG,"\"for\" missing condition part",(0x00));
   
  foratom->loop=getatom(((ecodeAtomArg*)tmpatom)->cond+";",tmpi,line);
  lerrorifr(!foratom->loop || foratom->loop->type==CA_ARG,"\"for\" missing loop part",(0x00));

  delete tmpatom;
 
  foratom->exec=getatom(str,i,line);
  lerrorifr(!foratom->exec || foratom->exec->type==CA_ARG,"condition found where statement was expected",(0x00));

  return(foratom);
}

ecodeAtom *ecodeParser::getcodewhileatom(const estr& str,long& i,int& line)
{
  ecodeAtomWhile *whileatom = new ecodeAtomWhile;
  whileatom->line=line;

  whileatom->cond=getargatom(str,i,line);
  lerrorifr(!whileatom->cond || whileatom->cond->type!=CA_ARG,"\"while\" missing condition",(0x00));

  whileatom->exec=getatom(str,i,line);
  lerrorifr(!whileatom->exec || whileatom->exec->type==CA_ARG,"condition found where statement was expected",(0x00));

  return(whileatom);
}

ecodeAtom *ecodeParser::getcodedoatom(const estr& str,long& i,int& line)
{
  ecodeAtomDo *doatom = new ecodeAtomDo;
  doatom->line=line;

  doatom->exec=getatom(str,i,line);
  lerrorifr(!doatom->exec || doatom->exec->type==CA_ARG,"\"do\" condition found where statement was expected",(0x00));

  estr control;
  getcontrolstr(str,i,line,control);
  lerrorifr(control!="while","\"do\" missing \"while\" keyword",(0x00));

  doatom->cond=getargatom(str,i,line);
  lerrorifr(!doatom->cond || doatom->cond->type!=CA_ARG,"\"do\" statement found where condition was expected",(0x00));
 
  return(doatom);
}

ecodeAtom *ecodeParser::getcodefunctionatom(const estr& str,long& i,int& line)
{
  ecodeAtomFunction *funcatom = new ecodeAtomFunction;
  funcatom->line=line;

  getcontrolstr(str,i,line,funcatom->name);
  
  funcatom->args=getargatom(str,i,line);
  lerrorifr(!funcatom->args || funcatom->args->type!=CA_ARG,"\"function\" statement found where args were expected",(0x00));

  int b=i;
  funcatom->exec=getatom(str,i,line);
  funcatom->code=str.substr(b,i-b);
  lerrorifr(!funcatom->exec || funcatom->exec->type==CA_ARG,"\"function\" args found where statement was expected",(0x00));

  return(funcatom);
}

evar code_interpret(estrhashof<evar>& env,const estr& str)
{
  if (!str.len()) return(evar());

  ecodeParser cparser;
  ecodeAtomBlock *code;
  ldinfo("code_interpret");
  
  code=cparser.parse(str);
  if (code==0x00 || cparser.error!=0){
    lderror("cparser error: "+estr(cparser.error));
    return(evar());
  }
  stopExecutionStruct stopExecution;
  int loopControl=0;
  evar res(code->interpret(env,stopExecution,loopControl));
  if (stopExecution.flag){
    cerr << "Runtime error! line: " << stopExecution.line << " - " << stopExecution.error << endl;
  }
  delete code;
  return(res);
}

evar interpret_line(estrhashof<evar>& env,stopExecutionStruct& stopExecution,const estr& str)
{
  if (!str.len()) return(evar());
  estrarray sa;
  if (split_atoms2(str,sa)){
    eatom root(sa);
    estr s;
    root.print(s);
    ldinfo("interpret_line: command tree: "+s);
    evar res;
    root.make(env,stopExecution);
    if (root.remote==1){
      if (root.rvalue.isNull())
        root.remoteExecute(env);
      evarRemoteValue(root.rvalue);
//    res.set(root->make(env));
      res.set(root.rvalue);
    }else
      res.copy(root.make(env,stopExecution));
    return(res);
  }
  return(evar());
}

#ifdef EUTILS_HAVE_READLINE_H
void my_rlhandler(char* line);
#endif


estr exechost;

evar epinterpret(const estr& str)
{
  estr tmpstr(str);
  estr tmpexechost;
  int i;

  tmpstr.trim();
  if (!tmpstr.len()) return(evar());

//  cout << tmpstr << endl;

/*
  if (tmpstr[0]=='@'){
    ldinfo("remote command");
    i=tmpstr.find(":");
    if (i==-1){
      tmpstr.del(0,1);
      if (tmpstr.len() && !getDistComp().clients.exists(tmpstr))
        { lderror("host not found: "+tmpstr); return(evar()); }
      ldinfo("setting exec host to: "+tmpstr);
      exechost=tmpstr;

      #ifdef EUTILS_HAVE_READLINE_H
      estr cmdprompt="easyc++";
      if (exechost.len()) cmdprompt+="@"+exechost;
      cmdprompt+="> ";
      rl_callback_handler_install(cmdprompt._str, &my_rlhandler);
      #endif
      return(evar());
    }

    tmpexechost=tmpstr.substr(1,i-1);
    tmpstr.del(0,i+1);
    return(getDistComp().interpret(tmpexechost,tmpstr));
  }else if (exechost.len()){
    ldinfo("remote command: "+exechost);
    return(getDistComp().interpret(exechost,tmpstr));
  }
*/
  return(code_interpret(getParser().objects,tmpstr));
}

#include "efile.h"
#include "esystem.h"


estr histfile;


#ifdef EUTILS_HAVE_READLINE_H
 #include "edir.h"
 


void my_rlhandler(char* line)
{
  if (line==NULL){
    cout << endl;
    rl_callback_handler_remove();
    lerrorif(write_history(histfile._str)!=0,"unable to write history to file: "+histfile);
    exit(0);
    // Ctrl-D will allow us to exit nicely
  }else{
    if(*line!=0){
      // If line wasn't empty, store it so that uparrow retrieves it
      epinterpret(line);
      add_history(line);
    }
//    printf("Your input was:\n%s\n", line);
    free(line);
  }
}

char *dupstr(char *s)
{
  char *r;
  r = new char[strlen(s)+1];
  strcpy(r,s);
  return(r);
}

char *eparser_command_generator(const char *text,int state)
{
  static int list_index, len;
  char *name;

  /* If this is a new word to complete, initialize now.  This includes
     saving the length of TEXT for efficiency, and initializing the index
     variable to 0. */
  if (!state) {
    list_index=0;
    len=strlen(text);
  }

  /* Return the next name which partially matches from the command list. */
  for (; list_index<getParser().funcs.size(); ++list_index){
    name = getParser().funcs.keys(list_index)._str;
    if (strncmp(name,text,len) == 0){
      ++list_index;
      return(dupstr(name));
    }
  }
  for (; list_index-getParser().funcs.size()<getParser().objects.size(); ++list_index){
    name = getParser().objects.keys(list_index-getParser().funcs.size())._str;
    if (strncmp(name,text,len) == 0){
      ++list_index;
      return(dupstr(name));
    }
  }
  return((char*)NULL);
}

char **eparser_completion(const char *text,int start,int end)
{
  char **matches;
  matches = (char **)NULL;
  if (start == 0)
    matches = rl_completion_matches(text, eparser_command_generator);
  return (matches);
}
#endif

void interpretGotInput()
{
#ifdef EUTILS_HAVE_READLINE_H
  rl_callback_read_char();
#else
  estr cmd;
  efile input(stdin);
  if (exechost.len())
    cout << "easyc++@"<< exechost << "> ";
  else
    cout << "easyc++> ";
  flush(cout);
  input.readln(cmd);
  if (cmd[0]==0x00) exit(0);
  if (cmd[cmd.size()-1]==0x0A)
    cmd.del(-1);
  epinterpret(cmd);
#endif
}

/*
void epruninterpret()
{
  epregisterFunctions();

#ifdef EUTILS_HAVE_READLINE_H
  using_history();

//  histfile=env()["HOME"]+"/."+efile(argv[0]).basename()+"_history";
//  lerrorif(read_history(histfile._str)!=0,"unable to read history from "+histfile);

  rl_callback_handler_install("easyc++> ", &my_rlhandler);
  rl_attempted_completion_function = (char**(*)(const char*,int,int))eparser_completion;  
#endif

  getSystem().addReadCallback(0,interpretGotInput,evararray());
  getSystem().run();

#ifdef EUTILS_HAVE_READLINE_H
  rl_callback_handler_remove();
#endif
  cout << endl;
}
*/

esystemCallback *parseInputCB=0x00;

stopExecutionStruct stopExecution;

estr bufferline;

void doParseInput(){
//  if (parsing) return;
//  parsing=true;

#ifdef _MSC_VER
	static HANDLE stdinHandle;
	// Get the IO handles
	// getc(stdin);
	stdinHandle = GetStdHandle(STD_INPUT_HANDLE);

	if (!_kbhit()) // _kbhit() always returns immediately // some sort of other events , we need to clear it from the queue
	{
		// clear events
		INPUT_RECORD r[512];
		DWORD read;
		ReadConsoleInput(stdinHandle, r, 512, &read);
		return;
//		cerr << "mouse event" << endl;
	}
#endif

  parseInputCB->disableRead();

  estr line;
  efile f(stdin);
  f.blocking=true;
  while (f.readln(line)){
    if (line.len()==0) continue;
    ecodeParser cparser;
    ecodeAtomBlock *code=0x00;
    ldinfo("code_interpret");
  
    estr tmpline;
    if (bufferline.len())
      tmpline=bufferline+"\n"+line;
    else
      tmpline=line;
    code=cparser.parse(tmpline);
    if (cparser.error==0){
      int loopControl=0;
      stopExecution.flag=false;
      code->interpret(getParser().objects,stopExecution,loopControl);
      if (stopExecution.flag){
        cerr << "Runtime error on line: " << stopExecution.line << " - " << stopExecution.error << endl;
      }
      delete code;
      bufferline.clear();
    } else if (cparser.error==1)
      bufferline=tmpline;
    else
      cout << "Parsing error: " << cparser.error << endl;
#ifdef _MSC_VER /* break if on windows to make sure we do not get a blocked read */
   	break;
#endif
  }
  if (f.eof())
    exit(0);
  parseInputCB->enableRead();
//  parsing=false;
}



#ifdef EUTILS_HAVE_LIBNCURSES
#include "etermviewer.h"

earray<estr> histarr;
int histarrPos=-1;
estr histtmp;

bool controlChar=false;
int windowPos=0;
estrarrayof<earray<estr> > windows;
estr windowLine;


bool interpretRun=true;
econdsig interpretSignal;
emutex interpretMutex;
ecodeAtomBlock *interpretCode=0x00;

void doInterpretThread()
{
  while (interpretRun){
    // wait for code
    interpretMutex.lock();
    while (interpretCode==0x00 && interpretRun) interpretSignal.wait(interpretMutex);
    ecodeAtomBlock *tmpCode=interpretCode;
    interpretMutex.unlock();

    // run code
    if (tmpCode){
      int loopControl=0;
      stopExecution.flag=false;
      tmpCode->interpret(getParser().objects,stopExecution,loopControl);
      if (stopExecution.flag)
        cerr << "Runtime error on line: " << stopExecution.line << " - " << stopExecution.error << endl;
      delete tmpCode;
    }

    // ready for next code
    interpretMutex.lock();
    interpretCode=0x00;
    interpretMutex.unlock();
  }
}


void doInterpret(etermviewer& tviewer,const estr& line)
{
  windows[0].add("> "+line);
  tviewer.line.clear();

  if (line.len()>0){
    ecodeParser cparser;
    ecodeAtomBlock *tmpcode=0x00;
    ldinfo("code_interpret");
  
    estr tmpline;
    if (windowLine.len())
      tmpline=windowLine+"\n"+line;
    else
      tmpline=line;
    if (tmpline.len() && tmpline[0]=='@' && tmpline.find(":")==-1){
      exechost=tmpline.substr(1);
    }else{
      tmpcode=cparser.parse(tmpline);
      if (exechost.len())
        tmpcode->exechost=exechost;
  //    cout << "codeParser error: " << cparser.error << endl;
      if (cparser.error==0){

//#ifndef __APPLE__
#ifdef MTPARSE /* Causes problems with new remote variable code, induces crashes, must be a race condition somewhere */
        interpretMutex.lock();
        if (interpretCode==0x00){
          interpretCode=tmpcode;
          tmpcode=0x00;
        }
        interpretSignal.signal();
        interpretMutex.unlock();
#else /* OSX requires UI and other function calls to be performed on the main thread, which complicates things for the parser when using multiple threads */
        stopExecution.flag=false;
        int loopControl=0;
        tmpcode->interpret(getParser().objects,stopExecution,loopControl);
        if (stopExecution.flag)
          cerr << "Runtime error on line: " << stopExecution.line << " - " << stopExecution.error << endl;
        delete tmpcode;
        tmpcode=0x00;
#endif
        if (tmpcode==0x00){
          windowLine.clear();
          histarr.add(tmpline);
          histarrPos=-1;
          histtmp.clear();
        }else{
          delete tmpcode;
          tmpcode=0x00;
        }
      } else if (cparser.error==1) {
        windowLine=tmpline;
      } else
        cout << "codeParser error: " << cparser.error << endl;
    }
  }
  tviewer.doDraw();
}


int lastchar=0x00;

bool doKeyPress(etermviewer& tviewer,int key)
{
//  char tmpsz[255];
//  sprintf(tmpsz,"key: %x\n",key);
//  tviewer.status=tmpsz;
  lastchar=key;

  if (controlChar){
    switch (key){
      case 'n':
      case 'N':{
        windowPos=(windowPos+1)%windows.size();
        if (windows.keys(windowPos)=="debug")
          getLogger().getDebugText(windows[windowPos]);
        tviewer.setText(&windows[windowPos]);
        estr status;
        for (int i=0; i<windows.size(); ++i){
          if (i==windowPos)
            status+="["+windows.keys(i)+"]";
          else
            status+=" "+windows.keys(i)+" ";
        }
        tviewer.status=status;
        tviewer.doDraw();
      }break;
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':{
        windowPos=key-'1';
        if (key=='0') windowPos=10;
        if (windowPos>=windows.size()) windowPos=windows.size()-1;
        if (windows.keys(windowPos)=="debug")
          getLogger().getDebugText(windows[windowPos]);
        tviewer.setText(&windows[windowPos]);
        estr status;
        for (int i=0; i<windows.size(); ++i){
          if (i==windowPos)
            status+="["+windows.keys(i)+"]";
          else
            status+=" "+windows.keys(i)+" ";
        }
        tviewer.status=status;
 
      }break;
    }
    controlChar=false;
    return(false);
  }

  switch(key){
    case 0x01:{
      controlChar=true;
    }break;
    case KEY_UP:{
      if (histarr.size()==0) return(false);
      if (histarrPos==-1) histtmp=tviewer.line;
      ++histarrPos;
      if (histarrPos>=histarr.size()) histarrPos=histarr.size()-1;
      tviewer.line=histarr[histarr.size()-histarrPos-1];
      tviewer.cpos=tviewer.line.len();
      return(false);
    }break;
    case KEY_END:{
      tviewer.cpos=tviewer.line.len();
      return(false);
    }break;
    case KEY_HOME:{
      tviewer.cpos=0;
      return(false);
    }break;
    case KEY_DOWN:{
      if (histarr.size()==0) return(false);
      --histarrPos;
      if (histarrPos<0) { histarrPos=-1; tviewer.line=histtmp; return(false); }
      tviewer.line=histarr[histarr.size()-histarrPos-1];
      tviewer.cpos=tviewer.line.len();
      return(false);
    }break;
    case 0x09:{   //TAB
      if (lastchar==0x09){
//        if (tviewer.line.len()>0){
          ecodeParser cparser;
//          ecodeAtomBlock *code=0x00;
//          ldinfo("code_check");
          // cut the string from cursor until the first ;(,. and use the characters found to filter the suggestion list
          // context can be retrieved if the previous character is a (,.  for (, the function should be looked up and the argument type can be used as a filter
          // for . the object should be looked up and the suggestion should be to filter properties and methods
  
          estr tmpline;
          if (windowLine.len())
            tmpline=windowLine+"\n"+tviewer.line;
          else
            tmpline=tviewer.line;

//          tviewer.add(cparser.autocomplete(tviewer.line,getParser().objects));
          estr tmpline2(tviewer.line.substr(0,tviewer.cpos));
          tviewer.add(cparser.autocomplete(tmpline2,getParser().objects));
          tviewer.line=tmpline2+tviewer.line.substr(tviewer.cpos);
          tviewer.cpos=tmpline2.len();
/*
          code=cparser.parse(tmpline);
          if (cparser.error==0){
            tviewer.add(code->check(getParser().objects));
          }else
            cout << "codeParser error: " << cparser.error << endl;
*/
//        }

/*
        const char *name;
        for (int i=0; i<getParser().funcs.size(); ++i){
          name=getParser().funcs.keys(i)._str;
          if (strncmp(name,tviewer.line._str,tviewer.line.len()) == 0)
            tviewer.add(name);
        }
        for (int i=0; i<getParser().objects.size(); ++i){
          name=getParser().objects.keys(i)._str;
          if (strncmp(name,tviewer.line._str,tviewer.line.len()) == 0)
            tviewer.add(name);
        }
*/
        tviewer.doDraw();
        lastchar=0x00;
      }
    }break;
    case 0x107:{  //Ctrl+H

    }break;
    case 0x10f:
    case 0x10e:
    case 0x10d:
    case 0x10c:
    case 0x10b:
    case 0x10a:
    case 0x109:{
      windowPos=key-0x109;
      if (windowPos>=windows.size()) windowPos=windows.size()-1;
      if (windows.keys(windowPos)=="debug")
        getLogger().getDebugText(windows[windowPos]);
      tviewer.setText(&windows[windowPos]);
      estr status;
      for (int i=0; i<windows.size(); ++i){
        if (i==windowPos)
          status+="["+windows.keys(i)+"]";
        else
          status+=" "+windows.keys(i)+" ";
      }
      tviewer.status=status;
      tviewer.doDraw();
    }break;
  }
  return(true);
}

void doParseOutput(etermviewer& tviewer)
{
  estr line;
//  efile f;
//  f.open(tviewer.outputfd,"r");
  while (!tviewer.outputf.eof() && tviewer.outputf.readln(line))
    windows[0].add(line);
  if (windowPos==0)
    tviewer.update();
//    tviewer.doDraw();
}

void eploadHistory(const efile& f)
{
  estr line;
  f.open();
  while (!f.eof() && f.readln(line)){
    if (line.len()==0) continue;
    histarr.add(line);
  }
  f.close();
}

void epsaveHistory(const efile& f)
{
  f.open();
  for (int i=0; i<histarr.size(); ++i)
    f.write(histarr[i]+"\n");
  f.close();
}

etermviewer *tviewer=0x00;

void doExit()
{
  interpretMutex.lock();
  interpretRun=false;
  interpretSignal.signal();
  interpretMutex.unlock();
  delete tviewer;
  epsaveHistory(efile(histfile,"w"));
}

//bool parsing=false;
ethreadFunc runInterpretThread;


void doParseInput2() {
  estr line;
  efile f(stdin);
  f.blocking=true;
  while (f.readln(line)){
    if (line.len()==0) continue;

    ecodeParser cparser;
    ecodeAtomBlock *tmpcode=0x00;
    ldinfo("code_interpret");
  
    estr tmpline;
    if (windowLine.len())
      tmpline=windowLine+"\n"+line;
    else
      tmpline=line;
    tmpcode=cparser.parse(tmpline);
//    cout << "codeParser error: " << cparser.error << endl;
    if (cparser.error==0){
      interpretMutex.lock();
      while (interpretCode!=0x00) interpretSignal.wait(interpretMutex);
      interpretCode=tmpcode;
      tmpcode=0x00;
      interpretSignal.signal();
      interpretMutex.unlock();
//      code.interpret(getParser().objects);
      if (tmpcode==0x00){
        windowLine.clear();
        histarr.add(tmpline);
        histarrPos=-1;
        histtmp.clear();
      }else{
        delete tmpcode;
        tmpcode=0x00;
      }
    } else if (cparser.error==1)
      windowLine=tmpline;
    else
      cout << "codeParser error: " << cparser.error << endl;
  }
  if (f.eof())
    exit(0);
 
}

void handleInterruptSignal(int signal,siginfo_t *si,void *data)
{
  stopExecution.flag=true;
  stopExecution.line=-1;
  stopExecution.error="[Interrupt signal]";
}

void setupInterpret()
{
  epregisterFunctions();
  struct sigaction sa;
  sigemptyset(&sa.sa_mask);
  sigaddset(&sa.sa_mask,SIGINT);
  sa.sa_flags=SA_SIGINFO;
  sa.sa_sigaction=&handleInterruptSignal;
  sigaction(SIGINT,&sa,0x00);

  if (isatty(0) && isatty(1)){
    histfile=(getParser().args.size()>0?env()["HOME"]+"/."+efile(getParser().args[0]).basename()+"_history":".esh_history");
    eploadHistory(efile(histfile,"r"));
    atexit(doExit);
 
//#ifndef __APPLE__  /* only run multithreaded for non-osx systems */
#ifdef MTPARSE  /* only run multithreaded for non-osx systems */
    runInterpretThread.run(doInterpretThread);
#endif

    tviewer=new etermviewer;
    tviewer->status="[main] debug";
    windows.add("main",earray<estr>());
    windows.add("debug",earray<estr>());
    tviewer->init();
//    tviewer->onExit=doExit;
    tviewer->onEnter=doInterpret;
    tviewer->onKeyPress=doKeyPress;
    getSystem().addReadCallback(tviewer->outputfd,doParseOutput,evararray(tviewer));
    tviewer->setText(&windows.values(0));
  }else{
//    runInterpretThread.run(doInterpretThread);
    efile f(stdin);
    f.disableBuffer();
    f.setNonBlocking();
    parseInputCB=getSystem().addReadCallback(fileno(stdin),doParseInput,evararray());
  }
}
#else
void setupInterpret()
{
  epregisterFunctions();

  efile f(stdin);
  f.disableBuffer();
  f.setNonBlocking();
  parseInputCB=getSystem().addReadCallback(fileno(stdin),doParseInput,evararray());
}
#endif

void epruninterpret()
{
  setupInterpret();
  getSystem().run();

/*
#ifdef EUTILS_HAVE_READLINE_H
  using_history();

  histfile=env()["HOME"]+"/."+efile(argv[0]).basename()+"_history";
  lerrorif(read_history(histfile._str)!=0,"unable to read history from "+histfile);

  rl_callback_handler_install("easyc++> ", &my_rlhandler);
  rl_attempted_completion_function = (char**(*)(const char*,int,int))eparser_completion;  
#endif

  getSystem().addReadCallback(0,interpretGotInput,evararray());
  getSystem().run();

#ifdef EUTILS_HAVE_READLINE_H
  rl_callback_handler_remove();
#endif
*/
  cout << endl;
}

//#include "eregexp.h"

void include(const estr& file)
{
  if (efile(file).exists()){
    epinterpretfile(file);
    return;
  } 

  if (efile(file+".esh").exists()){
    epinterpretfile(file+".esh");
    return;
  } 

  if (efile(estr(INCLUDEPATH)+"/"+file+".esh").exists()){
    epinterpretfile(estr(INCLUDEPATH)+"/"+file+".esh");
    return;
  } 

  estrarray env_vars(env());
  if (env_vars.findkey("EUTILS_INCLUDE_PATH")!=-1){
    estrarray paths=env_vars["EUTILS_INCLUDE_PATH"].explode(":");
    for (int i=0; i<paths.size(); ++i){
      if (efile(paths[i]+"/"+file+".esh").exists()){
        epinterpretfile(paths[i]+"/"+file+".esh");
        return;
      }
    }
  }
}

void epinterpretfile(const estr& file)
{
  epregisterFunctions();

  efile f(file);
  estr data(f.data());
  f.close();
  // remove first line if it is a esh script
  if (data.len()>=2 && data[0]=='#' && data[1]=='!') {
    int i=data.find("\n");
    if (i==-1) i=data.len();
    data.del(0,i);
  }

  ecodeParser cparser;
  ecodeAtomBlock *code=0x00;
  code=cparser.parse(data);
  int loopControl=0;
  stopExecution.flag=false;
  if (cparser.error==0)
    code->interpret(getParser().objects,stopExecution,loopControl);
  else
    cout << "codeParser error: " << cparser.error << endl;
  if (stopExecution.flag)
    cerr << "Runtime error on line: " << stopExecution.line << " - " << stopExecution.error << endl;
  f.close();
}

