<commit_before>/*! \file uexpression.cc
 *******************************************************************************

 File: uexpression.cc\n
 Implementation of the UExpression class.

 This file is part of 
 %URBI Kernel, version __kernelversion__\n
 (c) <NAME>, 2004-2005.

 Permission to use, copy, modify, and redistribute this software for
 non-commercial use is hereby granted.

 This software is provided "as is" without warranty of any kind,
 either expressed or implied, including but not limited to the
 implied warranties of fitness for a particular purpose.

 For more information, comments, bug reports: http://www.urbiforge.net

 **************************************************************************** */

#include <math.h>
#include <stdio.h>
#include <sstream>
#ifdef _MSC_VER
#define snprintf _snprintf
#endif
#include "uexpression.h"
#include "ucommand.h"
#include "uconnection.h"

#include "userver.h"

MEMORY_MANAGER_INIT(UExpression);
// **************************************************************************
//! UExpression base constructor called by every specific constructor.
void UExpression::initialize()
{
  ADDOBJ(UExpression);

  expression1      = 0;
  expression2      = 0; 
  str              = 0;
  id               = 0;
  variablename     = 0;
  parameters       = 0;
  firsteval        = true;
  isconst          = false;
  issofttest       = false;
  staticcache      = 0;
  tmp_value        = 0;

  this->softtest_time = 0; 
  
  dataType  = DATA_UNKNOWN;
}
	
//! UExpression constructor for numeric value.
/*! The parameter 'type' is required here only for the sake of uniformity
    between all the different constructors.
*/
UExpression::UExpression(UExpressionType type, ufloat *val) 
{	
   initialize();
  
   this->val  = *val;
   delete val; //other steal pointer, we copy->destroy
   this->type = type; // should be EXPR_VALUE
   this->isconst = true;
  
   dataType   = DATA_NUM;
}
		
			
//! UExpression constructor for numeric value.
/*! The parameter 'type' is required here only for the sake of uniformity
    between all the different constructors.
*/
UExpression::UExpression(UExpressionType type, ufloat val) 
{	
   initialize();
  
   this->val  = val;
   this->type = type; // should be EXPR_VALUE
   this->isconst = true;
  
   dataType   = DATA_NUM;
}


//! UExpression constructor for string value.
/*! The parameter 'type' is required here only for the sake of uniformity
    between all the different constructors.
*/
UExpression::UExpression(UExpressionType type, UString *str) 
{	
  initialize();
  this->str  = str;
  this->type = type; // should be EXPR_VALUE
  this->isconst = true;
  dataType   = DATA_STRING;
}
	
//! UExpression constructor for numeric value.
/*! The parameter 'type' is required here only for the sake of uniformity
    between all the different constructors.
*/
UExpression::UExpression(UExpressionType type, UValue *v) 
{	
   initialize();
    
   this->type = type; // should be EXPR_VALUE
   this->isconst = true;
   dataType   = v->dataType;

   if (v->dataType == DATA_NUM) this->val  = v->val;
   else
     if (v->dataType == DATA_STRING) this->str = v->str->copy();
     else {
       tmp_value = v;
       dataType = DATA_UNKNOWN;  
     }
}
		
//! UExpression constructor for functors 
/*! The parameter 'type' is required here only for the sake of uniformity
    between all the different constructors.
*/
UExpression::UExpression(UExpressionType type, 
                         UString *oper, 
                         UVariableName *variablename) 
{	
  initialize();
  this->str          = oper;
  this->variablename = variablename;
  if (variablename)
    this->isconst    = variablename->isstatic;

  this->type       = type; // should be EXPR_PROPERTY
  dataType         = DATA_UNKNOWN;
}	       
		
//! UExpression constructor for variable
/*! The parameter 'type' is required here only for the sake of uniformity
    between all the different constructors.
*/
UExpression::UExpression(UExpressionType type, UVariableName* variablename) 
{	
  initialize();
  this->type     = type; // should be EXPR_VARIABLE or EXPR_ADDR_VARIABLE or EXPR_GROUPLIST
  /*if (variablename)
    dataType = variablename->dataType;
  */
  if (type == EXPR_ADDR_VARIABLE) dataType = DATA_STRING;
  this->variablename = variablename;
}
	
//! UExpression constructor for function
/*! The parameter 'type' is required here only for the sake of uniformity
    between all the different constructors.
*/
UExpression::UExpression(UExpressionType type,
                         UVariableName* variablename, 
                         UNamedParameters *parameters) 
{	
  initialize();
  this->type         = type; // should be EXPR_FUNCTION  
  dataType           = DATA_UNKNOWN;
  this->variablename = variablename;
  this->parameters   = parameters;
}	

//! UExpression constructor for function
/*! The parameter 'type' is required here only for the sake of uniformity
    between all the different constructors.
*/
UExpression::UExpression(UExpressionType type,                        
                         UNamedParameters *parameters) 
{	
  initialize();
  this->type         = type; // should be EXPR_LIST
  dataType           = DATA_LIST;
  this->parameters   = parameters;
}

//! UExpression constructor for composed operation
UExpression::UExpression(UExpressionType type, 
                         UExpression* expression1, 
                         UExpression* expression2)
{	
  initialize();
  this->type        = type;

  this->expression1 = expression1;
  this->expression2 = expression2;

  // Compile time calculus reduction

  if ((expression1) && (expression2))
    if (( expression1->type == EXPR_VALUE ) &&
        ( expression1->dataType == DATA_NUM ) &&
        ( expression2->type == EXPR_VALUE ) &&
        ( expression2->dataType == DATA_NUM ) && 
        ( (type == EXPR_PLUS) || (type == EXPR_MINUS) || 
          (type == EXPR_MULT) || (type == EXPR_DIV) ||
          (type == EXPR_EXP) ) &&
        ( ! ( (type == EXPR_DIV) && (expression2->val == 0)) ) ) {
      
      switch (type) {
      case EXPR_PLUS:  val = expression1->val + expression2->val; break;
      case EXPR_MINUS: val = expression1->val - expression2->val; break;
      case EXPR_MULT:  val = expression1->val * expression2->val; break;
      case EXPR_DIV:   val = expression1->val / expression2->val; break;
      case EXPR_EXP:   val = pow (expression1->val , expression2->val); break;    
      }

      this->type = EXPR_VALUE;
      this->isconst = true;
      dataType   = DATA_NUM;
      delete expression1; this->expression1 = 0;
      delete expression2; this->expression2 = 0;
    }

  if ( ( type == EXPR_NEG ) &&
       ( expression1 ) )
    if ( ( expression1->type == EXPR_VALUE ) &&
         ( expression1->dataType == DATA_NUM ) ) { 

      val = - expression1->val;

      this->type = EXPR_VALUE;
      this->isconst = true;
      dataType   = DATA_NUM;
      delete expression1; this->expression1 = 0;
    }   
}

//! UExpression destructor.
UExpression::~UExpression()
{ 
  FREEOBJ(UExpression);

  if (str) delete(str);
  if (id) delete(id);
  if (expression1)  delete expression1;
  if (expression2)  delete expression2;
  if (variablename) delete variablename;
  if (parameters)   delete parameters;
} 
 
//! UExpression hard copy function
UExpression*
UExpression::copy() 
{
  UExpression* ret = new UExpression(type,ufloat(0));  

  if (expression1)  ret->expression1 = expression1->copy(); 
  if (expression2)  ret->expression2 = expression2->copy(); 
  if (variablename) ret->variablename = variablename->copy(); 
  if (parameters)   ret->parameters = parameters->copy(); 
  if (str)          ret->str = new UString(str);
  if (id)           ret->id  = new UString(id);
  if (softtest_time) ret->softtest_time = softtest_time->copy();
  if (staticcache)  ret->staticcache = staticcache->copy();
  
  ret->dataType = dataType;
  ret->val      = val;
  ret->isconst  = isconst;
  ret->issofttest  = issofttest;
  ret->firsteval   = firsteval;
  ret->tmp_value   = tmp_value;

  return (ret);
}

//! Print the expression
/*! This function is for debugging purpose only. 
    It is not safe, efficient or crash proof. A better version will come later.
*/
void 
UExpression::print()
{
  ::urbiserver->debug("[Type:E%d ",type);
  if (isconst) ::urbiserver->debug("(const) ");
  if ((type == EXPR_VALUE) && (dataType == DATA_NUM)) {
    std::ostringstream tstr;
    tstr << "val="<<val<<" ";
    ::urbiserver->debug(tstr.str().c_str());
  }
  if (str) ::urbiserver->debug("str='%s' ",str->str());
  if (id) ::urbiserver->debug("id='%s' ",id->str());
  if (expression1) {::urbiserver->debug("expr1="); expression1->print(); ::urbiserver->debug(" ");}
  if (expression2) {::urbiserver->debug("expr2="); expression2->print(); ::urbiserver->debug(" ");}
  if (variablename){::urbiserver->debug("variablename="); variablename->print(); ::urbiserver->debug(" ");}
  if (parameters)  {::urbiserver->debug("parameters={"); parameters->print(); ::urbiserver->debug("} ");}
  ::urbiserver->debug("] ");  
}

//! UExpression evaluation.
/*! The connection parameter is necessary to access the variable hash table for
    expressions who contain variables. It is also used to display error 
    messages.
    The UCommand is used to retrieve a message tag if necessary.
*/
UValue*
UExpression::eval(UCommand *command, UConnection *connection, bool silent)
{
  const int errSize = 256;
  static char errorString[errSize]; // Max error message = 256 chars  
  UValue *e1;
  UValue *e2;
  UValue *e3;
  UValue *e4;
  UValue *ret;
  UValue *value;
  UVariable *variable;
  UString* method;
  UString* devicename;
  ufloat d1,d2;
  UCommand_EMIT* cmd;
  const char* vnamestr;
  UNamedParameters *pevent;
  UNamedParameters *pcatch;
  UString* funname;
  HMfunctiontab::iterator hmf; 
  HMgrouptab::iterator retr;
  list<UString*>::iterator it;
  UExpression *e;

  if ((issofttest) &&
      (softtest_time)) {
    
    UValue *ret = softtest_time->eval(command, connection);
    if (ret)
      softtest_time->val = ret->val;
    else 
      softtest_time->val = 0;
    delete ret;
  }

  switch (type) {

  case EXPR_LIST:

    ret = new UValue();
    ret->dataType = DATA_LIST;
    pevent = parameters;    
    e1 = ret;
    if (pevent) {
      e1->liststart = pevent->expression->eval(command, connection);
      e1 = e1->liststart;
      pevent = pevent->next;
    }
    
    while (pevent) {
      
      e1->next = pevent->expression->eval(command, connection);
      if (e1->next==0) {
        delete ret;
        return(0);
      }
      pevent = pevent->next;
      e1 = e1->next;
    }
    return(ret);

  case EXPR_GROUP: 
        
    ret = new UValue(); 
    retr = connection->server->grouptab.find(str->str());
    if (retr !=  connection->server->grouptab.end()) {
      
      ret->dataType = DATA_LIST;
      
      it = (*retr).second->members.begin();
      if (it !=  (*retr).second->members.end()) {

	e = new UExpression (EXPR_GROUP, (*it)->copy());
	e2 = e->eval(command, connection);
	delete e;
	if (e2->dataType == DATA_VOID) {
	  ret->liststart = new UValue((*it)->str());
	  delete e2;
	}
	else {
	  delete ret;
	  ret = e2;
	}
	  
	e1 = ret->liststart;
	while (e1->next) e1 = e1->next;
	it++;
      }
      
      while (it !=  (*retr).second->members.end()) {
	
	e = new UExpression (EXPR_GROUP, (*it)->copy());
	e2 = e->eval(command, connection);
	delete e;
	if (e2->dataType == DATA_VOID) {
	  e1->next = new UValue((*it)->str());
	  delete e2;
	  e1 = e1->next;
	}
	else {
	  e3 = e2;
	  e2 = e2->liststart;
	  while (e2) {
	    e1->next = e2->copy();
	    e1 = e1->next;
	    e2 = e2->next;
	  }
	  delete e3;
	}
	it++;
      }
    }  
    return(ret);
    
  case EXPR_VALUE:

    if (tmp_value) return tmp_value; // hack to be able to handle complex return types from function calls

    ret = new UValue();
    ret->dataType = dataType;
    if (dataType == DATA_NUM) ret->val = val; 
    if (dataType == DATA_STRING) ret->str = new UString(str);    
    return(ret);

  case EXPR_ADDR_VARIABLE:

    ret = new UValue();
    ret->dataType = DATA_STRING;    
    // hack here to be able to use objects pointeurs
    
    ret->str = new UString (variablename->buildFullname(command,connection)); 
    return(ret);

  case EXPR_VARIABLE: 

    variable = variablename->getVariable(command,connection); 
    if (!variablename->getFullname()) return (0); 
    method = variablename->getMethod();
    devicename = variablename->getDevice();
 	
    const char* varname; 
    if (!variable) {
    	
      varname = variablename->getFullname()->str();
      if (::urbiserver->eventtab.find(varname) !=
          ::urbiserver->eventtab.end()) {
        // this is an event
       
        ret = new UValue(ufloat(1));
        ret->val = 1;
        ret->eventid = ::urbiserver->eventtab[variablename->getFullname()->str()]->eventid;
        return(ret);
      }

      // virtual variables
      const char* devname = variablename->getDevice()->str();
      bool ambiguous;
      UVariable *vari = 0;
      HMobjtab::iterator itobj;
      if ((itobj = ::urbiserver->objtab.find(devname)) !=
	  ::urbiserver->objtab.end()) {
	vari = itobj->second->searchVariable(variablename->getMethod()->str(),
    	    ambiguous);
	if (ambiguous)  {  
	  snprintf(errorString,errSize,"!!! Ambiguous multiple inheritance on variable %s\n", variablename->getFullname()->str());
	  connection->send(errorString,command->tag->str()); 
          return new UValue();	            
	}
	
	variable = vari;     
	if (vari) {
  	  devicename->update(vari->method);
	  variablename->device->update(vari->method);
	  variablename->buildFullname(command,connection); 
	}
      }
    }
      
    if (!variable) {

      char* p = strstr(varname,"__");
      char* p2;
      if (p) { // could be a list index.... (dirty hack)
	p[0]=0;
	HMvariabletab::iterator hmv = ::urbiserver->variabletab.find(varname);
	if (hmv != ::urbiserver->variabletab.end()) {<commit_msg>lists can be passed as function arguments now<commit_after>/*! \file uexpression.cc
 *******************************************************************************

 File: uexpression.cc\n
 Implementation of the UExpression class.

 This file is part of 
 %URBI Kernel, version __kernelversion__\n
 (c) <NAME>, 2004-2005.

 Permission to use, copy, modify, and redistribute this software for
 non-commercial use is hereby granted.

 This software is provided "as is" without warranty of any kind,
 either expressed or implied, including but not limited to the
 implied warranties of fitness for a particular purpose.

 For more information, comments, bug reports: http://www.urbiforge.net

 **************************************************************************** */

#include <math.h>
#include <stdio.h>
#include <sstream>
#ifdef _MSC_VER
#define snprintf _snprintf
#endif
#include "uexpression.h"
#include "ucommand.h"
#include "uconnection.h"

#include "userver.h"

MEMORY_MANAGER_INIT(UExpression);
// **************************************************************************
//! UExpression base constructor called by every specific constructor.
void UExpression::initialize()
{
  ADDOBJ(UExpression);

  expression1      = 0;
  expression2      = 0; 
  str              = 0;
  id               = 0;
  variablename     = 0;
  parameters       = 0;
  firsteval        = true;
  isconst          = false;
  issofttest       = false;
  staticcache      = 0;
  tmp_value        = 0;

  this->softtest_time = 0; 
  
  dataType  = DATA_UNKNOWN;
}
	
//! UExpression constructor for numeric value.
/*! The parameter 'type' is required here only for the sake of uniformity
    between all the different constructors.
*/
UExpression::UExpression(UExpressionType type, ufloat *val) 
{	
   initialize();
  
   this->val  = *val;
   delete val; //other steal pointer, we copy->destroy
   this->type = type; // should be EXPR_VALUE
   this->isconst = true;
  
   dataType   = DATA_NUM;
}
		
			
//! UExpression constructor for numeric value.
/*! The parameter 'type' is required here only for the sake of uniformity
    between all the different constructors.
*/
UExpression::UExpression(UExpressionType type, ufloat val) 
{	
   initialize();
  
   this->val  = val;
   this->type = type; // should be EXPR_VALUE
   this->isconst = true;
  
   dataType   = DATA_NUM;
}


//! UExpression constructor for string value.
/*! The parameter 'type' is required here only for the sake of uniformity
    between all the different constructors.
*/
UExpression::UExpression(UExpressionType type, UString *str) 
{	
  initialize();
  this->str  = str;
  this->type = type; // should be EXPR_VALUE
  this->isconst = true;
  dataType   = DATA_STRING;
}
	
//! UExpression constructor for numeric value.
/*! The parameter 'type' is required here only for the sake of uniformity
    between all the different constructors.
*/
UExpression::UExpression(UExpressionType type, UValue *v) 
{	
   initialize();
    
   this->type = type; // should be EXPR_VALUE
   this->isconst = true;
   dataType   = v->dataType;

   if (v->dataType == DATA_NUM) this->val  = v->val;
   else
     if (v->dataType == DATA_STRING) this->str = v->str->copy();
     else {
       tmp_value = v;
       dataType = DATA_UNKNOWN;  
     }
}
		
//! UExpression constructor for functors 
/*! The parameter 'type' is required here only for the sake of uniformity
    between all the different constructors.
*/
UExpression::UExpression(UExpressionType type, 
                         UString *oper, 
                         UVariableName *variablename) 
{	
  initialize();
  this->str          = oper;
  this->variablename = variablename;
  if (variablename)
    this->isconst    = variablename->isstatic;

  this->type       = type; // should be EXPR_PROPERTY
  dataType         = DATA_UNKNOWN;
}	       
		
//! UExpression constructor for variable
/*! The parameter 'type' is required here only for the sake of uniformity
    between all the different constructors.
*/
UExpression::UExpression(UExpressionType type, UVariableName* variablename) 
{	
  initialize();
  this->type     = type; // should be EXPR_VARIABLE or EXPR_ADDR_VARIABLE or EXPR_GROUPLIST
  /*if (variablename)
    dataType = variablename->dataType;
  */
  if (type == EXPR_ADDR_VARIABLE) dataType = DATA_STRING;
  this->variablename = variablename;
}
	
//! UExpression constructor for function
/*! The parameter 'type' is required here only for the sake of uniformity
    between all the different constructors.
*/
UExpression::UExpression(UExpressionType type,
                         UVariableName* variablename, 
                         UNamedParameters *parameters) 
{	
  initialize();
  this->type         = type; // should be EXPR_FUNCTION  
  dataType           = DATA_UNKNOWN;
  this->variablename = variablename;
  this->parameters   = parameters;
}	

//! UExpression constructor for function
/*! The parameter 'type' is required here only for the sake of uniformity
    between all the different constructors.
*/
UExpression::UExpression(UExpressionType type,                        
                         UNamedParameters *parameters) 
{	
  initialize();
  this->type         = type; // should be EXPR_LIST
  dataType           = DATA_LIST;
  this->parameters   = parameters;
}

//! UExpression constructor for composed operation
UExpression::UExpression(UExpressionType type, 
                         UExpression* expression1, 
                         UExpression* expression2)
{	
  initialize();
  this->type        = type;

  this->expression1 = expression1;
  this->expression2 = expression2;

  // Compile time calculus reduction

  if ((expression1) && (expression2))
    if (( expression1->type == EXPR_VALUE ) &&
        ( expression1->dataType == DATA_NUM ) &&
        ( expression2->type == EXPR_VALUE ) &&
        ( expression2->dataType == DATA_NUM ) && 
        ( (type == EXPR_PLUS) || (type == EXPR_MINUS) || 
          (type == EXPR_MULT) || (type == EXPR_DIV) ||
          (type == EXPR_EXP) ) &&
        ( ! ( (type == EXPR_DIV) && (expression2->val == 0)) ) ) {
      
      switch (type) {
      case EXPR_PLUS:  val = expression1->val + expression2->val; break;
      case EXPR_MINUS: val = expression1->val - expression2->val; break;
      case EXPR_MULT:  val = expression1->val * expression2->val; break;
      case EXPR_DIV:   val = expression1->val / expression2->val; break;
      case EXPR_EXP:   val = pow (expression1->val , expression2->val); break;    
      }

      this->type = EXPR_VALUE;
      this->isconst = true;
      dataType   = DATA_NUM;
      delete expression1; this->expression1 = 0;
      delete expression2; this->expression2 = 0;
    }

  if ( ( type == EXPR_NEG ) &&
       ( expression1 ) )
    if ( ( expression1->type == EXPR_VALUE ) &&
         ( expression1->dataType == DATA_NUM ) ) { 

      val = - expression1->val;

      this->type = EXPR_VALUE;
      this->isconst = true;
      dataType   = DATA_NUM;
      delete expression1; this->expression1 = 0;
    }   
}

//! UExpression destructor.
UExpression::~UExpression()
{ 
  FREEOBJ(UExpression);

  if (str) delete(str);
  if (id) delete(id);
  if (expression1)  delete expression1;
  if (expression2)  delete expression2;
  if (variablename) delete variablename;
  if (parameters)   delete parameters;
} 
 
//! UExpression hard copy function
UExpression*
UExpression::copy() 
{
  UExpression* ret = new UExpression(type,ufloat(0));  

  if (expression1)  ret->expression1 = expression1->copy(); 
  if (expression2)  ret->expression2 = expression2->copy(); 
  if (variablename) ret->variablename = variablename->copy(); 
  if (parameters)   ret->parameters = parameters->copy(); 
  if (str)          ret->str = new UString(str);
  if (id)           ret->id  = new UString(id);
  if (softtest_time) ret->softtest_time = softtest_time->copy();
  if (staticcache)  ret->staticcache = staticcache->copy();
  
  ret->dataType = dataType;
  ret->val      = val;
  ret->isconst  = isconst;
  ret->issofttest  = issofttest;
  ret->firsteval   = firsteval;
  ret->tmp_value   = tmp_value;

  return (ret);
}

//! Print the expression
/*! This function is for debugging purpose only. 
    It is not safe, efficient or crash proof. A better version will come later.
*/
void 
UExpression::print()
{
  ::urbiserver->debug("[Type:E%d ",type);
  if (isconst) ::urbiserver->debug("(const) ");
  if ((type == EXPR_VALUE) && (dataType == DATA_NUM)) {
    std::ostringstream tstr;
    tstr << "val="<<val<<" ";
    ::urbiserver->debug(tstr.str().c_str());
  }
  if (str) ::urbiserver->debug("str='%s' ",str->str());
  if (id) ::urbiserver->debug("id='%s' ",id->str());
  if (expression1) {::urbiserver->debug("expr1="); expression1->print(); ::urbiserver->debug(" ");}
  if (expression2) {::urbiserver->debug("expr2="); expression2->print(); ::urbiserver->debug(" ");}
  if (variablename){::urbiserver->debug("variablename="); variablename->print(); ::urbiserver->debug(" ");}
  if (parameters)  {::urbiserver->debug("parameters={"); parameters->print(); ::urbiserver->debug("} ");}
  ::urbiserver->debug("] ");  
}

//! UExpression evaluation.
/*! The connection parameter is necessary to access the variable hash table for
    expressions who contain variables. It is also used to display error 
    messages.
    The UCommand is used to retrieve a message tag if necessary.
*/
UValue*
UExpression::eval(UCommand *command, UConnection *connection, bool silent)
{
  const int errSize = 256;
  static char errorString[errSize]; // Max error message = 256 chars  
  UValue *e1;
  UValue *e2;
  UValue *e3;
  UValue *e4;
  UValue *ret;
  UValue *value;
  UVariable *variable;
  UString* method;
  UString* devicename;
  ufloat d1,d2;
  UCommand_EMIT* cmd;
  const char* vnamestr;
  UNamedParameters *pevent;
  UNamedParameters *pcatch;
  UString* funname;
  HMfunctiontab::iterator hmf; 
  HMgrouptab::iterator retr;
  list<UString*>::iterator it;
  UExpression *e;

  if ((issofttest) &&
      (softtest_time)) {
    
    UValue *ret = softtest_time->eval(command, connection);
    if (ret)
      softtest_time->val = ret->val;
    else 
      softtest_time->val = 0;
    delete ret;
  }

  switch (type) {

  case EXPR_LIST:

    ret = new UValue();
    ret->dataType = DATA_LIST;
    pevent = parameters;    
    e1 = ret;
    if (pevent) {
      e1->liststart = pevent->expression->eval(command, connection);
      e1 = e1->liststart;
      pevent = pevent->next;
    }
    
    while (pevent) {
      
      e1->next = pevent->expression->eval(command, connection);
      if (e1->next==0) {
        delete ret;
        return(0);
      }
      pevent = pevent->next;
      e1 = e1->next;
    }
    return(ret);

  case EXPR_GROUP: 
        
    ret = new UValue(); 
    retr = connection->server->grouptab.find(str->str());
    if (retr !=  connection->server->grouptab.end()) {
      
      ret->dataType = DATA_LIST;
      
      it = (*retr).second->members.begin();
      if (it !=  (*retr).second->members.end()) {

	e = new UExpression (EXPR_GROUP, (*it)->copy());
	e2 = e->eval(command, connection);
	delete e;
	if (e2->dataType == DATA_VOID) {
	  ret->liststart = new UValue((*it)->str());
	  delete e2;
	}
	else {
	  delete ret;
	  ret = e2;
	}
	  
	e1 = ret->liststart;
	while (e1->next) e1 = e1->next;
	it++;
      }
      
      while (it !=  (*retr).second->members.end()) {
	
	e = new UExpression (EXPR_GROUP, (*it)->copy());
	e2 = e->eval(command, connection);
	delete e;
	if (e2->dataType == DATA_VOID) {
	  e1->next = new UValue((*it)->str());
	  delete e2;
	  e1 = e1->next;
	}
	else {
	  e3 = e2;
	  e2 = e2->liststart;
	  while (e2) {
	    e1->next = e2->copy();
	    e1 = e1->next;
	    e2 = e2->next;
	  }
	  delete e3;
	}
	it++;
      }
    }  
    return(ret);
    
  case EXPR_VALUE:

    if (tmp_value) return tmp_value; // hack to be able to handle complex return types from function calls

    ret = new UValue();
    ret->dataType = dataType;
    if (dataType == DATA_NUM) ret->val = val; 
    if (dataType == DATA_STRING) ret->str = new UString(str);    
    return(ret);

  case EXPR_ADDR_VARIABLE:

    ret = new UValue();
    ret->dataType = DATA_STRING;    
    // hack here to be able to use objects pointeurs
    
    ret->str = new UString (variablename->buildFullname(command,connection)); 
    return(ret);

  case EXPR_VARIABLE: 

    variable = variablename->getVariable(command,connection); 
    if (!variablename->getFullname()) return (0); 
    method = variablename->getMethod();
    devicename = variablename->getDevice();
 	
    const char* varname; 
    if (!variable) {
    	
      varname = variablename->getFullname()->str();
      if (::urbiserver->eventtab.find(varname) !=
          ::urbiserver->eventtab.end()) {
        // this is an event
       
        ret = new UValue(ufloat(1));
        ret->val = 1;
        ret->eventid = ::urbiserver->eventtab[variablename->getFullname()->str()]->eventid;
        return(ret);
      }

      // virtual variables
      const char* devname = variablename->getDevice()->str();
      bool ambiguous;
      UVariable *vari = 0;
      HMobjtab::iterator itobj;
      if ((itobj = ::urbiserver->objtab.find(devname)) !=
	  ::urbiserver->objtab.end()) {
	vari = itobj->second->searchVariable(variablename->getMethod()->str(),
    	    ambiguous);
	if (ambiguous)  {  
	  snprintf(errorString,errSize,"!!! Ambiguous multiple inheritance on variable %s\n", 
	      variablename->getFullname()->str());
	  connection->send(errorString,command->tag->str()); 
          return new UValue();	            
	}
	
	variable = vari;     
	if (vari) {
  	  devicename->update(vari->method);
	  variablename->device->update(vari->method);
	  variablename->buildFullname(command,connection); 
	}
      }
    }
      
    if (!variable) {

      char* p = strstr(varname,"__");
      char* pnext = p;
      while (pnext) {
	p = pnext;
	pnext = strstr(p+2,"__");
      }
      char* p2;
      if (p) { // could be a list index.... (dirty hack)
	p[0]=0;

	HMvariabletab::iterator hmv = ::urbiserver->variabletab.find(varname);
	if (hmv != ::urbiserver->variabletab.end()) {
	  
{'fraction_non_alphanumeric': 0.14954495555791636, 'fraction_numerical': 0.009171712879351252, 'mean_word_length': 2.8711446196024673, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 387, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7436801', 'n_tokens_mistral': 9241, 'n_tokens_neox': 8794, 'n_words': 2266}