/*
BOIL (Brunni's Own Interpreter Language)
(c) 1998 netEstate (http://www.netestate.de/, info@netestate.de),
Michael Brunnbauer & Markus Hendel, Gesellschaft fuer Netzwerkkommunikation bR
Am Bluetenanger 8, 80995 Muenchen, Germany

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "general.h"
#include "handles.cc"
#include "boil.h"
#include "boil.tab.h"
#include "libs.h"
#ifdef WITH_RPC
#include "rpc.h"
#endif

int autocast(type_value& value,int type,bool force)
{
  if (value.type==type) { return 1; };

  if (type==TYPE_VALUE_DOUBLE)
    {
      if (value.type==TYPE_VALUE_LONG)
        {
          value.type=TYPE_VALUE_DOUBLE;
          value.val.doubleval=(double) value.val.longval;
          return 1;
        }
      else if (value.type==TYPE_VALUE_ULONG)
        {
          value.type=TYPE_VALUE_DOUBLE;
          value.val.doubleval=(double) value.val.ulongval;
          return 1;
        };
    }
  else if (type==TYPE_VALUE_LONG)
    {
      if (value.type==TYPE_VALUE_ULONG)
        {
          if (!force && ((long) value.val.ulongval) < 0) { return 0; };
          value.type=TYPE_VALUE_LONG;
          value.val.longval=(long) value.val.ulongval;
          return 1;
        };
    }
  else if (type==TYPE_VALUE_ULONG)
    {
      if (value.type==TYPE_VALUE_LONG)
        {
          if (!force && value.val.longval < 0) { return 0; };
          value.type=TYPE_VALUE_ULONG;
          value.val.ulongval=(unsigned long) value.val.longval;
          return 1;
        };
    };

  return 0;
}

#define CONVERT if (!autocast(arg1,arg2.type,0) && \
!autocast(arg2,arg1.type,0)) { autocast(arg1,arg2.type,1);\
autocast(arg2,arg1.type,1); };

#define TINT if (arg1.type!=TYPE_VALUE_LONG && arg1.type!=TYPE_VALUE_ULONG) \
{ back.stringval="integer type required for operator" ; return back; }

#define NUMERIC1 if (arg1.type==TYPE_VALUE_STRING || \
arg1.type==TYPE_VALUE_VOID) { back.stringval="numeric type required for \
operator" ; return back; }

#define NUMERIC2 if (arg2.type==TYPE_VALUE_STRING || \
arg2.type==TYPE_VALUE_VOID) { back.stringval="numeric type required for \
operator" ; return back; }

#define SAMETYPE if (arg1.type!=arg2.type) { back.stringval="same types \
required for operator" ; return back; }

#define ZERO2 if ((arg2.type==TYPE_VALUE_LONG && !arg2.val.longval) || \
(arg2.type==TYPE_VALUE_DOUBLE && !arg2.val.doubleval) || \
(arg2.type==TYPE_VALUE_ULONG && !arg2.val.ulongval)) { \
back.stringval="division by zero" ; return back; }

#define ISLONG(a) if (arg1.type==TYPE_VALUE_LONG) { (a); return back; }

#define ISULONG(a) if (arg1.type==TYPE_VALUE_ULONG) { (a); return back; }

#define ISDOUBLE(a) if (arg1.type==TYPE_VALUE_DOUBLE) { (a); return back; }

#define ISSTRING(a) if (arg1.type==TYPE_VALUE_STRING) { if \
(defined(arg1.stringval) && defined(arg2.stringval)) { (a); } else { \
back.type=TYPE_VALUE_VOID; back.stringval="string operand undefined"; \
return back; }; }

#define ISSTRING1(a) if (arg1.type==TYPE_VALUE_STRING) { if \
(defined(arg1.stringval) && defined(arg2.stringval)) { (a); return back; } \
else { back.type=TYPE_VALUE_VOID; back.stringval="string operand undefined"; \
return back; }; }

type_value::type_value() { return; }

type_value::~type_value() { return; }

type_value::type_value(type_value& src)
{
  type=src.type;
  if (src.type==TYPE_VALUE_STRING || src.type==TYPE_VALUE_VOID)
    {
      stringval=src.stringval;
    }
  else
    {
      val=src.val;
    };
  return;
}

type_value& type_value::operator=(type_value src)
{
  type=src.type;
  if (src.type==TYPE_VALUE_STRING || src.type==TYPE_VALUE_VOID)
    {
      stringval=src.stringval;
    }
  else
    {
      val=src.val;
    };
  return *this;
}

type_value operator-(type_value& arg1)
{
  type_value back;
  back.type=TYPE_VALUE_VOID;
  NUMERIC1;

  back.type=arg1.type;
  ISLONG(back.val.longval=(-arg1.val.longval));
  ISULONG(back.val.ulongval=(-arg1.val.ulongval));
  ISDOUBLE(back.val.doubleval=(-arg1.val.doubleval));
  return back;
}

type_value operator!(type_value& arg1)
{
  type_value back;
  back.type=TYPE_VALUE_VOID;
  NUMERIC1;

  back.type=arg1.type;
  ISLONG(back.val.longval=(!arg1.val.longval));
  ISULONG(back.val.ulongval=(!arg1.val.ulongval));
  ISDOUBLE(back.val.doubleval=(!arg1.val.doubleval));
  return back;
}

type_value operator~(type_value& arg1)
{
  type_value back;
  back.type=TYPE_VALUE_VOID;
  TINT;

  back.type=arg1.type;
  ISLONG(back.val.longval=(~arg1.val.longval));
  ISULONG(back.val.ulongval=(~arg1.val.ulongval));
  return back;
}

type_value operator*(type_value& arg1,type_value& arg2)
{
  type_value back;
  back.type=TYPE_VALUE_VOID;
  CONVERT;
  SAMETYPE;
  NUMERIC1;NUMERIC2;

  back.type=arg1.type;
  ISLONG(back.val.longval=arg1.val.longval*arg2.val.longval);
  ISULONG(back.val.ulongval=arg1.val.ulongval*arg2.val.ulongval);
  ISDOUBLE(back.val.doubleval=arg1.val.doubleval*arg2.val.doubleval);
  return back;
}

type_value operator/(type_value& arg1,type_value& arg2)
{
  type_value back;
  back.type=TYPE_VALUE_VOID;
  CONVERT;
  SAMETYPE;
  NUMERIC1;NUMERIC2;
  ZERO2;

  back.type=arg1.type;
  ISLONG(back.val.longval=arg1.val.longval/arg2.val.longval);
  ISULONG(back.val.ulongval=arg1.val.ulongval/arg2.val.ulongval);
  ISDOUBLE(back.val.doubleval=arg1.val.doubleval/arg2.val.doubleval);
  return back;
}

type_value operator%(type_value& arg1,type_value& arg2)
{
  type_value back;
  back.type=TYPE_VALUE_VOID;
  CONVERT;
  SAMETYPE;
  TINT;
  ZERO2;

  back.type=arg1.type;
  ISLONG(back.val.longval=arg1.val.longval%arg2.val.longval);
  ISULONG(back.val.ulongval=arg1.val.ulongval%arg2.val.ulongval);
  return back;
}

type_value operator+(type_value& arg1,type_value& arg2)
{
  type_value back;
  back.type=TYPE_VALUE_VOID;
  CONVERT;
  SAMETYPE;

  back.type=arg1.type;
  ISSTRING1(back.stringval=arg1.stringval+arg2.stringval);
  ISLONG(back.val.longval=arg1.val.longval+arg2.val.longval);
  ISULONG(back.val.ulongval=arg1.val.ulongval+arg2.val.ulongval);
  ISDOUBLE(back.val.doubleval=arg1.val.doubleval+arg2.val.doubleval);
  return back;
}

type_value operator-(type_value& arg1,type_value& arg2)
{
  type_value back;
  back.type=TYPE_VALUE_VOID;
  CONVERT;
  SAMETYPE;
  NUMERIC1;NUMERIC2;

  back.type=arg1.type;
  ISLONG(back.val.longval=arg1.val.longval-arg2.val.longval);
  ISULONG(back.val.ulongval=arg1.val.ulongval-arg2.val.ulongval);
  ISDOUBLE(back.val.doubleval=arg1.val.doubleval-arg2.val.doubleval);
  return back;
}

type_value operator<<(type_value& arg1,type_value& arg2)
{
  type_value back;
  back.type=TYPE_VALUE_VOID;
  CONVERT; /* FIXME: converting was deactivated for bit-operators, why ? */
  SAMETYPE;
  TINT;

  back.type=arg1.type;
  ISLONG(back.val.longval=arg1.val.longval<<arg2.val.longval);
  ISULONG(back.val.ulongval=arg1.val.ulongval<<arg2.val.ulongval);
  return back;
}

type_value operator>>(type_value& arg1,type_value& arg2)
{
  type_value back;
  back.type=TYPE_VALUE_VOID;
  CONVERT; /* FIXME: converting was deactivated for bit-operators, why ? */
  SAMETYPE;
  TINT;

  back.type=arg1.type;
  ISLONG(back.val.longval=arg1.val.longval>>arg2.val.longval);
  ISULONG(back.val.ulongval=arg1.val.ulongval>>arg2.val.ulongval);
  return back;
}

type_value operator<(type_value& arg1,type_value& arg2)
{
  type_value back;
  back.type=TYPE_VALUE_VOID;
  CONVERT;
  SAMETYPE;
  NUMERIC1;NUMERIC2;

  back.type=arg1.type;
  ISLONG(back.val.longval=arg1.val.longval<arg2.val.longval);
  ISULONG(back.val.ulongval=arg1.val.ulongval<arg2.val.ulongval);
  ISDOUBLE(back.val.doubleval=arg1.val.doubleval<arg2.val.doubleval);
  return back;
}

type_value operator>(type_value& arg1,type_value& arg2)
{
  type_value back;
  back.type=TYPE_VALUE_VOID;
  CONVERT;
  SAMETYPE;
  NUMERIC1;NUMERIC2;

  back.type=arg1.type;
  ISLONG(back.val.longval=arg1.val.longval>arg2.val.longval);
  ISULONG(back.val.ulongval=arg1.val.ulongval>arg2.val.ulongval);
  ISDOUBLE(back.val.doubleval=arg1.val.doubleval>arg2.val.doubleval);
  return back;
}

type_value operator<=(type_value& arg1,type_value& arg2)
{
  type_value back;
  back.type=TYPE_VALUE_VOID;
  CONVERT;
  SAMETYPE;
  NUMERIC1;NUMERIC2;

  back.type=arg1.type;
  ISLONG(back.val.longval=arg1.val.longval<=arg2.val.longval);
  ISULONG(back.val.ulongval=arg1.val.ulongval<=arg2.val.ulongval);
  ISDOUBLE(back.val.doubleval=arg1.val.doubleval<=arg2.val.doubleval);
  return back;
}

type_value operator>=(type_value& arg1,type_value& arg2)
{
  type_value back;
  back.type=TYPE_VALUE_VOID;
  CONVERT;
  SAMETYPE;
  NUMERIC1;NUMERIC2;

  back.type=arg1.type;
  ISLONG(back.val.longval=arg1.val.longval>=arg2.val.longval);
  ISULONG(back.val.ulongval=arg1.val.ulongval>=arg2.val.ulongval);
  ISDOUBLE(back.val.doubleval=arg1.val.doubleval>=arg2.val.doubleval);
  return back;
}

type_value operator==(type_value& arg1,type_value& arg2)
{
  type_value back;
  back.type=TYPE_VALUE_VOID;
  CONVERT;
  SAMETYPE;

  back.type=arg1.type;
  ISSTRING(back.type=TYPE_VALUE_ULONG);
  ISSTRING1(back.val.ulongval=(arg1.stringval==arg2.stringval));
  ISLONG(back.val.longval=(arg1.val.longval==arg2.val.longval));
  ISULONG(back.val.ulongval=(arg1.val.ulongval==arg2.val.ulongval));
  ISDOUBLE(back.val.doubleval=(arg1.val.doubleval==arg2.val.doubleval));
  return back;
}

type_value operator!=(type_value& arg1,type_value& arg2)
{
  type_value back;
  back.type=TYPE_VALUE_VOID;
  CONVERT;
  SAMETYPE;

  back.type=arg1.type;
  ISSTRING(back.type=TYPE_VALUE_ULONG);
  ISSTRING1(back.val.ulongval=(arg1.stringval!=arg2.stringval));
  ISLONG(back.val.longval=(arg1.val.longval!=arg2.val.longval));
  ISULONG(back.val.ulongval=(arg1.val.ulongval!=arg2.val.ulongval));
  ISDOUBLE(back.val.doubleval=(arg1.val.doubleval!=arg2.val.doubleval));
  return back;
}

type_value operator&(type_value& arg1,type_value& arg2)
{
  type_value back;
  back.type=TYPE_VALUE_VOID;
  CONVERT; /* FIXME: converting was deactivated for bit-operators, why ? */
  SAMETYPE;
  TINT;

  back.type=arg1.type;
  ISLONG(back.val.longval=arg1.val.longval&arg2.val.longval);
  ISULONG(back.val.ulongval=arg1.val.ulongval&arg2.val.ulongval);
  return back;
}

type_value operator^(type_value& arg1,type_value& arg2)
{
  type_value back;
  back.type=TYPE_VALUE_VOID;
  CONVERT; /* FIXME: converting was deactivated for bit-operators, why ? */
  SAMETYPE;
  TINT;

  back.type=arg1.type;
  ISLONG(back.val.longval=arg1.val.longval^arg2.val.longval);
  ISULONG(back.val.ulongval=arg1.val.ulongval^arg2.val.ulongval);
  return back;
}

type_value operator|(type_value& arg1,type_value& arg2)
{
  type_value back;
  back.type=TYPE_VALUE_VOID;
  CONVERT; /* FIXME: converting was deactivated for bit-operators, why ? */
  SAMETYPE;
  TINT;

  back.type=arg1.type;
  ISLONG(back.val.longval=arg1.val.longval|arg2.val.longval);
  ISULONG(back.val.ulongval=arg1.val.ulongval|arg2.val.ulongval);
  return back;
}

/* NOT USED DUE TO OPTIMIZATIONS

type_value operator&&(type_value& arg1,type_value& arg2)
{
  type_value back;
  back.type=TYPE_VALUE_VOID;
  CONVERT;
  SAMETYPE;
  NUMERIC1;NUMERIC2;

  back.type=arg1.type;
  ISLONG(back.val.longval=arg1.val.longval&&arg2.val.longval);
  ISULONG(back.val.ulongval=arg1.val.ulongval&&arg2.val.ulongval);
  ISDOUBLE(back.val.doubleval=arg1.val.doubleval&&arg2.val.doubleval);
  return back;
}

type_value operator||(type_value& arg1,type_value& arg2)
{
  type_value back;
  back.type=TYPE_VALUE_VOID;
  CONVERT;
  SAMETYPE;
  NUMERIC1;NUMERIC2;

  back.type=arg1.type;
  ISLONG(back.val.longval=arg1.val.longval||arg2.val.longval);
  ISULONG(back.val.ulongval=arg1.val.ulongval||arg2.val.ulongval);
  ISDOUBLE(back.val.doubleval=arg1.val.doubleval||arg2.val.doubleval);
  return back;
}

*/
