[INTERPRETER]
* NEW: "_operators" is a new interface that allows any native class to be used with most of arithmetic operators: +, -, *, /, Abs, =, <> at the moment. * NEW: The "_convert" interface now allows a class to be converted to any datatype, not just other classes. * NEW: Str() and Print uses the "_convert" interface to print a localized string conversion of any object. [GB.GSL] * NEW: Use the new "_operators" interface so that complex numbers can be handled by standard arithmetic operators. * NEW: The Complex class now implements the "_convert" interface. git-svn-id: svn://localhost/gambas/trunk@4908 867c0c6c-44f3-4631-809d-bfa615b0a4ec
This commit is contained in:
parent
2c631a0874
commit
7bd330aa00
@ -38,20 +38,228 @@
|
||||
|
||||
#define THIS ((GSLCOMPLEX *)_object)
|
||||
|
||||
//---- Complex number creation ----------------------------------------------
|
||||
|
||||
/***********************************
|
||||
Complex Utility Methods
|
||||
***********************************/
|
||||
GSLCOMPLEX *GSLComplex_create()
|
||||
GSLCOMPLEX *COMPLEX_create()
|
||||
{
|
||||
static GB_CLASS _klass = (GB_CLASS)NULL;
|
||||
|
||||
GSLCOMPLEX *c;
|
||||
c = (GSLCOMPLEX *)GB.New(GB.FindClass("Complex"), NULL, NULL);
|
||||
|
||||
if (!_klass)
|
||||
_klass = GB.FindClass("Complex");
|
||||
|
||||
c = (GSLCOMPLEX *)GB.New(_klass, NULL, NULL);
|
||||
c->number.dat[0] = 0.0;
|
||||
c->number.dat[1] = 0.0;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
//---- Arithmetic operators -------------------------------------------------
|
||||
|
||||
static GSLCOMPLEX *_addf(GSLCOMPLEX *a, double f)
|
||||
{
|
||||
GSLCOMPLEX *r = COMPLEX_create();
|
||||
r->number = gsl_complex_add_real(a->number, f);
|
||||
return r;
|
||||
}
|
||||
|
||||
static GSLCOMPLEX *_add(GSLCOMPLEX *a, GSLCOMPLEX *b)
|
||||
{
|
||||
GSLCOMPLEX *r = COMPLEX_create();
|
||||
r->number = gsl_complex_add(a->number, b->number);
|
||||
return r;
|
||||
}
|
||||
|
||||
static GSLCOMPLEX *_subf(GSLCOMPLEX *a, double f)
|
||||
{
|
||||
GSLCOMPLEX *r = COMPLEX_create();
|
||||
r->number = gsl_complex_sub_real(a->number, f);
|
||||
return r;
|
||||
}
|
||||
|
||||
static GSLCOMPLEX *_sub(GSLCOMPLEX *a, GSLCOMPLEX *b)
|
||||
{
|
||||
GSLCOMPLEX *r = COMPLEX_create();
|
||||
r->number = gsl_complex_sub(a->number, b->number);
|
||||
return r;
|
||||
}
|
||||
|
||||
static GSLCOMPLEX *_mulf(GSLCOMPLEX *a, double f)
|
||||
{
|
||||
GSLCOMPLEX *r = COMPLEX_create();
|
||||
r->number = gsl_complex_mul_real(a->number, f);
|
||||
return r;
|
||||
}
|
||||
|
||||
static GSLCOMPLEX *_mul(GSLCOMPLEX *a, GSLCOMPLEX *b)
|
||||
{
|
||||
GSLCOMPLEX *r = COMPLEX_create();
|
||||
r->number = gsl_complex_mul(a->number, b->number);
|
||||
return r;
|
||||
}
|
||||
|
||||
static GSLCOMPLEX *_divf(GSLCOMPLEX *a, double f)
|
||||
{
|
||||
gsl_complex c = gsl_complex_div_real(a->number, f);
|
||||
|
||||
if (isfinite(c.dat[0]) && isfinite(c.dat[1]))
|
||||
{
|
||||
GSLCOMPLEX *r = COMPLEX_create();
|
||||
r->number = c;
|
||||
return r;
|
||||
}
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GSLCOMPLEX *_idivf(GSLCOMPLEX *a, double f)
|
||||
{
|
||||
GSLCOMPLEX *r = COMPLEX_create();
|
||||
r->number = gsl_complex_mul_real(gsl_complex_inverse(a->number), f);
|
||||
return r;
|
||||
}
|
||||
|
||||
static GSLCOMPLEX *_div(GSLCOMPLEX *a, GSLCOMPLEX *b)
|
||||
{
|
||||
gsl_complex c = gsl_complex_div(a->number, b->number);
|
||||
|
||||
if (isfinite(c.dat[0]) && isfinite(c.dat[1]))
|
||||
{
|
||||
GSLCOMPLEX *r = COMPLEX_create();
|
||||
r->number = c;
|
||||
return r;
|
||||
}
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _equal(GSLCOMPLEX *a, GSLCOMPLEX *b)
|
||||
{
|
||||
return a->number.dat[0] == b->number.dat[0] && a->number.dat[1] == b->number.dat[1];
|
||||
}
|
||||
|
||||
static int _equalf(GSLCOMPLEX *a, double f)
|
||||
{
|
||||
return a->number.dat[0] == f && a->number.dat[1] == 0.0;
|
||||
}
|
||||
|
||||
static GSLCOMPLEX *_neg(GSLCOMPLEX *a)
|
||||
{
|
||||
GSLCOMPLEX *r = COMPLEX_create();
|
||||
r->number = gsl_complex_negative(a->number);
|
||||
return r;
|
||||
}
|
||||
|
||||
static double _abs(GSLCOMPLEX *a)
|
||||
{
|
||||
return gsl_complex_abs(a->number);
|
||||
}
|
||||
|
||||
static GB_OPERATOR_DESC _operators =
|
||||
{
|
||||
add: (void *)_add,
|
||||
addf: (void *)_addf,
|
||||
sub: (void *)_sub,
|
||||
subf: (void *)_subf,
|
||||
mul: (void *)_mul,
|
||||
mulf: (void *)_mulf,
|
||||
div: (void *)_div,
|
||||
divf: (void *)_divf,
|
||||
idivf: (void *)_idivf,
|
||||
equal: (void *)_equal,
|
||||
equalf: (void *)_equalf,
|
||||
abs: (void *)_abs,
|
||||
neg: (void *)_neg
|
||||
};
|
||||
|
||||
//---- Conversions ----------------------------------------------------------
|
||||
|
||||
static char *_to_string(GSLCOMPLEX *_object, bool local)
|
||||
{
|
||||
char buffer[64];
|
||||
char *p;
|
||||
char *str;
|
||||
int len;
|
||||
double real, imag;
|
||||
|
||||
if (!THIS)
|
||||
return NULL;
|
||||
|
||||
real = THIS->number.dat[0];
|
||||
imag = THIS->number.dat[1];
|
||||
|
||||
if (real == 0.0 && imag == 0.0)
|
||||
return GB.NewString("0", 1);
|
||||
|
||||
p = buffer;
|
||||
|
||||
if (real != 0.0)
|
||||
{
|
||||
GB.NumberToString(local, real, NULL, &str, &len);
|
||||
strncpy(p, str, len);
|
||||
p += len;
|
||||
}
|
||||
|
||||
if (imag != 0.0)
|
||||
{
|
||||
if (imag < 0.0)
|
||||
{
|
||||
*p++ = '-';
|
||||
imag = (-imag);
|
||||
}
|
||||
else if (p != buffer)
|
||||
*p++ = '+';
|
||||
|
||||
if (imag != 1.0)
|
||||
{
|
||||
GB.NumberToString(local, imag, NULL, &str, &len);
|
||||
strncpy(p, str, len);
|
||||
p += len;
|
||||
}
|
||||
*p++ = 'i';
|
||||
}
|
||||
|
||||
return GB.NewString(buffer, p - buffer);
|
||||
}
|
||||
|
||||
static bool _convert(GSLCOMPLEX *a, GB_TYPE type, GB_VALUE *conv)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GB_T_FLOAT:
|
||||
conv->_float.value = gsl_complex_abs(a->number);
|
||||
return FALSE;
|
||||
|
||||
case GB_T_SINGLE:
|
||||
conv->_single.value = gsl_complex_abs(a->number);
|
||||
return FALSE;
|
||||
|
||||
case GB_T_INTEGER:
|
||||
case GB_T_SHORT:
|
||||
case GB_T_BYTE:
|
||||
conv->_integer.value = gsl_complex_abs(a->number);
|
||||
return FALSE;
|
||||
|
||||
case GB_T_LONG:
|
||||
conv->_long.value = gsl_complex_abs(a->number);
|
||||
return FALSE;
|
||||
|
||||
case GB_T_STRING:
|
||||
case GB_T_CSTRING:
|
||||
conv->_string.value.addr = _to_string(a, type == GB_T_CSTRING);
|
||||
conv->_string.value.start = 0;
|
||||
conv->_string.value.len = GB.StringLength(conv->_string.value.addr);
|
||||
return FALSE;
|
||||
|
||||
default:
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
BEGIN_METHOD(GslComplex_new, GB_FLOAT real; GB_FLOAT imag)
|
||||
|
||||
THIS->number.dat[0] = VARGOPT(real, 0.0);
|
||||
@ -62,7 +270,7 @@ END_METHOD
|
||||
|
||||
BEGIN_METHOD(GslComplex_call, GB_FLOAT real; GB_FLOAT imag)
|
||||
|
||||
GSLCOMPLEX *c = GSLComplex_create();
|
||||
GSLCOMPLEX *c = COMPLEX_create();
|
||||
|
||||
c->number.dat[0] = VARG(real);
|
||||
c->number.dat[1] = VARG(imag);
|
||||
@ -73,7 +281,7 @@ END_METHOD
|
||||
|
||||
BEGIN_METHOD_VOID(GslComplex_Copy)
|
||||
|
||||
GSLCOMPLEX *c = GSLComplex_create();
|
||||
GSLCOMPLEX *c = COMPLEX_create();
|
||||
|
||||
c->number = THIS->number;
|
||||
GB.ReturnObject(c);
|
||||
@ -110,15 +318,11 @@ BEGIN_METHOD_VOID(GslComplex_ToString)
|
||||
{
|
||||
if (imag < 0.0)
|
||||
{
|
||||
*p++ = ' ';
|
||||
*p++ = '-';
|
||||
*p++ = ' ';
|
||||
imag = (-imag);
|
||||
}
|
||||
else if (p != buffer)
|
||||
*p++ = ' ';
|
||||
*p++ = '+';
|
||||
*p++ = ' ';
|
||||
|
||||
if (imag != 1.0)
|
||||
{
|
||||
@ -233,7 +437,7 @@ BEGIN_METHOD(GslComplex_Add, GB_OBJECT x)
|
||||
return;
|
||||
|
||||
// Create new object
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
// Add two complex numbers
|
||||
obj->number = gsl_complex_add(THIS->number, x->number);
|
||||
@ -251,7 +455,7 @@ BEGIN_METHOD(GslComplex_Sub, GB_OBJECT x)
|
||||
if (GB.CheckObject(x))
|
||||
return;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_sub(THIS->number, x->number);
|
||||
|
||||
@ -268,7 +472,7 @@ BEGIN_METHOD(GslComplex_Mul, GB_OBJECT x)
|
||||
if (GB.CheckObject(x))
|
||||
return;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_mul(THIS->number, x->number);
|
||||
|
||||
@ -285,7 +489,7 @@ BEGIN_METHOD(GslComplex_Div, GB_OBJECT x)
|
||||
if (GB.CheckObject(x))
|
||||
return;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_div(THIS->number, x->number);
|
||||
|
||||
@ -301,7 +505,7 @@ BEGIN_METHOD(GslComplex_Add_Real, GB_FLOAT x)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_add_real(THIS->number, VARG(x));
|
||||
|
||||
@ -314,7 +518,7 @@ BEGIN_METHOD(GslComplex_Sub_Real, GB_FLOAT x)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_sub_real(THIS->number, VARG(x));
|
||||
|
||||
@ -327,7 +531,7 @@ BEGIN_METHOD(GslComplex_Mul_Real, GB_FLOAT x)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_mul_real(THIS->number, VARG(x));
|
||||
|
||||
@ -340,7 +544,7 @@ BEGIN_METHOD(GslComplex_Div_Real, GB_FLOAT x)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_div_real(THIS->number, VARG(x));
|
||||
|
||||
@ -356,7 +560,7 @@ BEGIN_METHOD(GslComplex_Add_Imag, GB_FLOAT x)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_add_imag(THIS->number, VARG(x));
|
||||
|
||||
@ -369,7 +573,7 @@ BEGIN_METHOD(GslComplex_Sub_Imag, GB_FLOAT x)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_sub_imag(THIS->number, VARG(x));
|
||||
|
||||
@ -382,7 +586,7 @@ BEGIN_METHOD(GslComplex_Mul_Imag, GB_FLOAT x)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_mul_imag(THIS->number, VARG(x));
|
||||
|
||||
@ -395,7 +599,7 @@ BEGIN_METHOD(GslComplex_Div_Imag, GB_FLOAT x)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_div_imag(THIS->number, VARG(x));
|
||||
|
||||
@ -408,7 +612,7 @@ BEGIN_METHOD_VOID(GslComplex_Conjugate)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_conjugate(THIS->number);
|
||||
|
||||
@ -420,7 +624,7 @@ BEGIN_METHOD_VOID(GslComplex_Inverse)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_inverse(THIS->number);
|
||||
|
||||
@ -433,7 +637,7 @@ BEGIN_METHOD_VOID(GslComplex_Negative)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_negative(THIS->number);
|
||||
|
||||
@ -449,7 +653,7 @@ BEGIN_METHOD_VOID(GslComplex_Sqrt)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_sqrt(THIS->number);
|
||||
|
||||
@ -462,7 +666,7 @@ BEGIN_METHOD(GslComplex_SqrtReal, GB_FLOAT x;)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_sqrt_real(VARG(x));
|
||||
|
||||
@ -479,7 +683,7 @@ BEGIN_METHOD(GslComplex_Pow, GB_OBJECT x;)
|
||||
if (GB.CheckObject(x))
|
||||
return;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_pow(THIS->number, x->number);
|
||||
|
||||
@ -492,7 +696,7 @@ BEGIN_METHOD(GslComplex_PowReal, GB_FLOAT x;)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_pow_real(THIS->number, VARG(x));
|
||||
|
||||
@ -505,7 +709,7 @@ BEGIN_METHOD_VOID(GslComplex_Exp)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_exp(THIS->number);
|
||||
|
||||
@ -518,7 +722,7 @@ BEGIN_METHOD_VOID(GslComplex_Log)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_log(THIS->number);
|
||||
|
||||
@ -531,7 +735,7 @@ BEGIN_METHOD_VOID(GslComplex_Log10)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_log10(THIS->number);
|
||||
|
||||
@ -548,7 +752,7 @@ BEGIN_METHOD(GslComplex_Log_b, GB_OBJECT x;)
|
||||
if (GB.CheckObject(x))
|
||||
return;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_log_b(THIS->number, x->number);
|
||||
|
||||
@ -566,7 +770,7 @@ BEGIN_METHOD_VOID(GslComplex_Sin)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_sin(THIS->number);
|
||||
|
||||
@ -579,7 +783,7 @@ BEGIN_METHOD_VOID(GslComplex_Cos)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_cos(THIS->number);
|
||||
|
||||
@ -592,7 +796,7 @@ BEGIN_METHOD_VOID(GslComplex_Tan)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_tan(THIS->number);
|
||||
|
||||
@ -605,7 +809,7 @@ BEGIN_METHOD_VOID(GslComplex_Sec)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_sec(THIS->number);
|
||||
|
||||
@ -618,7 +822,7 @@ BEGIN_METHOD_VOID(GslComplex_Csc)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_csc(THIS->number);
|
||||
|
||||
@ -631,7 +835,7 @@ BEGIN_METHOD_VOID(GslComplex_Cot)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_cot(THIS->number);
|
||||
|
||||
@ -648,7 +852,7 @@ END_METHOD
|
||||
BEGIN_METHOD_VOID(GslComplex_Arcsin)
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_arcsin(THIS->number);
|
||||
|
||||
@ -661,7 +865,7 @@ BEGIN_METHOD(GslComplex_Arcsin_Real, GB_FLOAT x;)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_arcsin_real(VARG(x));
|
||||
|
||||
@ -673,7 +877,7 @@ END_METHOD
|
||||
BEGIN_METHOD_VOID(GslComplex_Arccos)
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_arccos(THIS->number);
|
||||
|
||||
@ -686,7 +890,7 @@ BEGIN_METHOD(GslComplex_Arccos_Real, GB_FLOAT x;)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_arccos_real(VARG(x));
|
||||
|
||||
@ -698,7 +902,7 @@ END_METHOD
|
||||
BEGIN_METHOD_VOID(GslComplex_Arctan)
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_arctan(THIS->number);
|
||||
|
||||
@ -710,7 +914,7 @@ END_METHOD
|
||||
BEGIN_METHOD_VOID(GslComplex_Arcsec)
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_arcsec(THIS->number);
|
||||
|
||||
@ -723,7 +927,7 @@ BEGIN_METHOD(GslComplex_Arcsec_Real, GB_FLOAT x;)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_arcsec_real(VARG(x));
|
||||
|
||||
@ -735,7 +939,7 @@ END_METHOD
|
||||
BEGIN_METHOD_VOID(GslComplex_Arccsc)
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_arccsc(THIS->number);
|
||||
|
||||
@ -748,7 +952,7 @@ BEGIN_METHOD(GslComplex_Arccsc_Real, GB_FLOAT x;)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_arccsc_real(VARG(x));
|
||||
|
||||
@ -760,7 +964,7 @@ END_METHOD
|
||||
BEGIN_METHOD_VOID(GslComplex_Arccot)
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_arccot(THIS->number);
|
||||
|
||||
@ -777,7 +981,7 @@ END_METHOD
|
||||
BEGIN_METHOD_VOID(GslComplex_Sinh)
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_sinh(THIS->number);
|
||||
|
||||
@ -789,7 +993,7 @@ END_METHOD
|
||||
BEGIN_METHOD_VOID(GslComplex_Cosh)
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_cosh(THIS->number);
|
||||
|
||||
@ -801,7 +1005,7 @@ END_METHOD
|
||||
BEGIN_METHOD_VOID(GslComplex_Tanh)
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_tanh(THIS->number);
|
||||
|
||||
@ -813,7 +1017,7 @@ END_METHOD
|
||||
BEGIN_METHOD_VOID(GslComplex_Sech)
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_sech(THIS->number);
|
||||
|
||||
@ -825,7 +1029,7 @@ END_METHOD
|
||||
BEGIN_METHOD_VOID(GslComplex_Csch)
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_csch(THIS->number);
|
||||
|
||||
@ -837,7 +1041,7 @@ END_METHOD
|
||||
BEGIN_METHOD_VOID(GslComplex_Coth)
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_coth(THIS->number);
|
||||
|
||||
@ -854,7 +1058,7 @@ END_METHOD
|
||||
BEGIN_METHOD_VOID(GslComplex_Arcsinh)
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_arcsinh(THIS->number);
|
||||
|
||||
@ -866,7 +1070,7 @@ END_METHOD
|
||||
BEGIN_METHOD_VOID(GslComplex_Arccosh)
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_arccosh(THIS->number);
|
||||
|
||||
@ -879,7 +1083,7 @@ BEGIN_METHOD(GslComplex_Arccosh_Real, GB_FLOAT x;)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_arccosh_real(VARG(x));
|
||||
|
||||
@ -891,7 +1095,7 @@ END_METHOD
|
||||
BEGIN_METHOD_VOID(GslComplex_Arctanh)
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_arctanh(THIS->number);
|
||||
|
||||
@ -904,7 +1108,7 @@ BEGIN_METHOD(GslComplex_Arctanh_Real, GB_FLOAT x;)
|
||||
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_arctanh_real(VARG(x));
|
||||
|
||||
@ -917,7 +1121,7 @@ END_METHOD
|
||||
BEGIN_METHOD_VOID(GslComplex_Arcsech)
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_arcsech(THIS->number);
|
||||
|
||||
@ -929,7 +1133,7 @@ END_METHOD
|
||||
BEGIN_METHOD_VOID(GslComplex_Arccsch)
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_arccsch(THIS->number);
|
||||
|
||||
@ -941,7 +1145,7 @@ END_METHOD
|
||||
BEGIN_METHOD_VOID(GslComplex_Arccoth)
|
||||
GSLCOMPLEX *obj;
|
||||
|
||||
obj = GSLComplex_create();
|
||||
obj = COMPLEX_create();
|
||||
|
||||
obj->number = gsl_complex_arccoth(THIS->number);
|
||||
|
||||
@ -967,15 +1171,17 @@ GB_DESC CComplexDesc[] =
|
||||
GB_METHOD("Rect", NULL, GslComplex_Rect, "[(Real)f(Imag)f]"),
|
||||
GB_METHOD("Polar", NULL, GslComplex_Polar, "[(Real)f(Imag)f]"),
|
||||
GB_METHOD("Arg", "f", GslComplex_Arg, NULL),
|
||||
|
||||
GB_INTERFACE("_operators", &_operators),
|
||||
GB_INTERFACE("_convert", &_convert),
|
||||
|
||||
GB_METHOD("Abs", "f", GslComplex_Abs, NULL),
|
||||
GB_METHOD("Abs2", "f", GslComplex_Abs2, NULL),
|
||||
GB_METHOD("LogAbs", "f", GslComplex_LogAbs, NULL),
|
||||
|
||||
|
||||
// Properties
|
||||
GB_PROPERTY("Real", "f", GslComplex_Real),
|
||||
GB_PROPERTY("Imag", "f", GslComplex_Imagined),
|
||||
|
||||
|
||||
/* Operations on gsl_complex */
|
||||
// Elementary Math Functions
|
||||
|
@ -1,25 +1,25 @@
|
||||
/***************************************************************************
|
||||
|
||||
c_complex.h
|
||||
c_complex.h
|
||||
|
||||
gb.gsl component
|
||||
gb.gsl component
|
||||
|
||||
(c) 2012 Randall Morgan <rmorgan62@gmail.com>
|
||||
(c) 2012 Randall Morgan <rmorgan62@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 1, or (at your option)
|
||||
any later version.
|
||||
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 1, or (at your option)
|
||||
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.
|
||||
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., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02110-1301, USA.
|
||||
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., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02110-1301, USA.
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
@ -38,14 +38,13 @@ GB_INTERFACE GB EXPORT;
|
||||
extern GB_DESC CComplexDesc[];
|
||||
|
||||
typedef
|
||||
struct __GSLCOMPLEX
|
||||
{
|
||||
GB_BASE ob;
|
||||
gsl_complex number;
|
||||
}
|
||||
GSLCOMPLEX;
|
||||
|
||||
GSLCOMPLEX *GSLComplex_create();
|
||||
struct
|
||||
{
|
||||
GB_BASE ob;
|
||||
gsl_complex number;
|
||||
}
|
||||
GSLCOMPLEX;
|
||||
|
||||
GSLCOMPLEX *COMPLEX_create();
|
||||
|
||||
#endif /* __C_GSL_COMPLEX_H */
|
||||
|
@ -228,7 +228,7 @@ BEGIN_METHOD_VOID(CComplexPolynomial_ComplexSolve)
|
||||
for(i = 0; i < THIS->len-1; i++)
|
||||
{
|
||||
printf ("z%d = %+.18f %+.18f\n", i, GSL_REAL(z[i]), GSL_IMAG(z[i]));
|
||||
cx = GSLComplex_create();
|
||||
cx = COMPLEX_create();
|
||||
if(cx)
|
||||
{
|
||||
GSLCOMPLEX *elt;
|
||||
|
@ -1400,15 +1400,15 @@ static void error_array_convert()
|
||||
OBJECT_UNREF(_converted_array, "error_array_convert");
|
||||
}
|
||||
|
||||
static void *array_convert(CARRAY *src, CLASS *class)
|
||||
static bool array_convert(CARRAY *src, CLASS *class, VALUE *conv)
|
||||
{
|
||||
CARRAY *array;
|
||||
int i;
|
||||
void *data;
|
||||
VALUE temp;
|
||||
|
||||
if (!CLASS_inherits(class, CLASS_Array))
|
||||
return NULL;
|
||||
if (!TYPE_is_pure_object((TYPE)class) || !CLASS_inherits(class, CLASS_Array))
|
||||
return TRUE;
|
||||
|
||||
_converted_array = array = OBJECT_create(class, NULL, NULL, 0);
|
||||
|
||||
@ -1429,7 +1429,9 @@ static void *array_convert(CARRAY *src, CLASS *class)
|
||||
}
|
||||
END_ERROR
|
||||
|
||||
return array;
|
||||
conv->_object.object = array;
|
||||
OBJECT_REF(array, "array_convert");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#else
|
||||
|
@ -1266,6 +1266,8 @@ void *CLASS_auto_create(CLASS *class, int nparam)
|
||||
|
||||
void CLASS_search_special(CLASS *class)
|
||||
{
|
||||
int sym;
|
||||
|
||||
class->special[SPEC_NEW] = CLASS_get_symbol_index_kind(class, "_new", CD_METHOD, 0);
|
||||
class->special[SPEC_FREE] = CLASS_get_symbol_index_kind(class, "_free", CD_METHOD, 0);
|
||||
class->special[SPEC_GET] = CLASS_get_symbol_index_kind(class, "_get", CD_METHOD, CD_STATIC_METHOD);
|
||||
@ -1279,7 +1281,15 @@ void CLASS_search_special(CLASS *class)
|
||||
class->special[SPEC_ATTACH] = CLASS_get_symbol_index_kind(class, "_attach", CD_METHOD, 0);
|
||||
|
||||
class->special[SPEC_CONVERT] = CLASS_get_symbol_index_kind(class, "_@_convert", CD_CONSTANT, 0);
|
||||
class->has_convert = class->special[SPEC_CONVERT] != NO_SYMBOL;
|
||||
|
||||
sym = CLASS_get_symbol_index_kind(class, "_@_operators", CD_CONSTANT, 0);
|
||||
if (sym != NO_SYMBOL)
|
||||
{
|
||||
class->has_operators = TRUE;
|
||||
class->operators = CLASS_get_desc(class, sym)->constant.value._pointer;
|
||||
}
|
||||
|
||||
if (class->special[SPEC_NEXT] != NO_SYMBOL)
|
||||
class->enum_static = CLASS_DESC_get_type(CLASS_get_desc(class, class->special[SPEC_NEXT])) == CD_STATIC_METHOD;
|
||||
if (class->special[SPEC_UNKNOWN] != NO_SYMBOL)
|
||||
|
@ -238,6 +238,28 @@ enum
|
||||
CS_READY = 2
|
||||
};
|
||||
|
||||
enum {
|
||||
CO_EQUAL,
|
||||
CO_EQUALF,
|
||||
CO_COMP,
|
||||
CO_COMPF,
|
||||
CO_ADD,
|
||||
CO_ADDF,
|
||||
CO_SUB,
|
||||
CO_SUBF,
|
||||
CO_MUL,
|
||||
CO_MULF,
|
||||
CO_DIV,
|
||||
CO_DIVF,
|
||||
CO_IDIVF,
|
||||
CO_NEG,
|
||||
CO_POW,
|
||||
CO_POWF,
|
||||
CO_ABS,
|
||||
CO_MAX,
|
||||
CO_MIN
|
||||
};
|
||||
|
||||
typedef
|
||||
struct _CLASS { // 32b 64b
|
||||
struct _CLASS *class; // 4 8 Points at the 'Class' class !
|
||||
@ -271,9 +293,11 @@ typedef
|
||||
unsigned init_dynamic : 1; // If there is a special function to call at instanciation
|
||||
unsigned must_check : 1; // The class has a check function
|
||||
unsigned has_child : 1; // The class has an inherited child class
|
||||
unsigned unknown_static : 1; // if _unknown is static
|
||||
unsigned unknown_static : 1; // if _unknown is static
|
||||
unsigned property_static : 1; // if _property is static
|
||||
unsigned _reserved : 3; // 24 36
|
||||
unsigned has_convert : 1; // if the _convert interface is implemented
|
||||
unsigned has_operators : 1; // if the _operators interface is implemented
|
||||
unsigned _reserved : 1; // 24 36
|
||||
|
||||
short n_desc; // 26 38 number of descriptions
|
||||
short n_event; // 28 40 number of events
|
||||
@ -309,17 +333,18 @@ typedef
|
||||
struct _CLASS *astruct_class; // 112 176 array of struct class
|
||||
|
||||
void *instance; // 116 184 automatically created instance
|
||||
void *operators; // 120 192 arithmetic interface
|
||||
|
||||
COMPONENT *component; // 120 192 The component the class belongs to
|
||||
COMPONENT *component; // 124 200 The component the class belongs to
|
||||
|
||||
struct _CLASS *override; // 124 200 The overridden class
|
||||
struct _CLASS *override; // 128 208 The overridden class
|
||||
|
||||
struct _CLASS *next; // 128 208 next class
|
||||
struct _CLASS *next; // 132 216 next class
|
||||
|
||||
void (**jit_functions)(void); // 132 216 array of jit functions
|
||||
void (**jit_functions)(void); // 136 224 array of jit functions
|
||||
}
|
||||
CLASS;
|
||||
|
||||
|
||||
typedef
|
||||
struct {
|
||||
SYMBOL sym;
|
||||
|
@ -73,6 +73,8 @@ static void _SUBR_sub(ushort code);
|
||||
static void _SUBR_mul(ushort code);
|
||||
static void _SUBR_div(ushort code);
|
||||
|
||||
//---- Subroutine dispatch table --------------------------------------------
|
||||
|
||||
static void *SubrTable[] =
|
||||
{
|
||||
/* 28 */ NULL, _SUBR_compn, _SUBR_compi, _SUBR_compi,
|
||||
@ -181,6 +183,139 @@ static void *SubrTable[] =
|
||||
};
|
||||
|
||||
|
||||
//---- Object arithmetic helpers --------------------------------------------
|
||||
|
||||
#define OP_NONE 0
|
||||
#define OP_OBJECT_FLOAT 1
|
||||
#define OP_FLOAT_OBJECT 2
|
||||
#define OP_OBJECT_CONV 3
|
||||
#define OP_CONV_OBJECT 4
|
||||
#define OP_OBJECT 5
|
||||
|
||||
static int check_operators(VALUE *P1, VALUE *P2)
|
||||
{
|
||||
if (TYPE_is_number(P1->type) && TYPE_is_object(P2->type))
|
||||
{
|
||||
if (OBJECT_class(P2->_object.object)->has_operators)
|
||||
{
|
||||
//*dynamic = P2->type == T_OBJECT;
|
||||
return OP_FLOAT_OBJECT;
|
||||
}
|
||||
}
|
||||
else if (TYPE_is_number(P2->type) && TYPE_is_object(P1->type))
|
||||
{
|
||||
if (OBJECT_class(P1->_object.object)->has_operators)
|
||||
{
|
||||
//*dynamic = P1->type == T_OBJECT;
|
||||
return OP_OBJECT_FLOAT;
|
||||
}
|
||||
}
|
||||
else if (TYPE_is_object(P1->type) && TYPE_is_object(P2->type))
|
||||
{
|
||||
CLASS *class1 = OBJECT_class(P1->_object.object);
|
||||
CLASS *class2 = OBJECT_class(P2->_object.object);
|
||||
|
||||
//*dynamic = P1->type == T_OBJECT || P2->type = T_OBJECT;
|
||||
|
||||
if (class1 && class1->has_operators)
|
||||
{
|
||||
if (class1 == class2)
|
||||
return OP_OBJECT;
|
||||
|
||||
if (class2 && class2->has_operators)
|
||||
return OP_OBJECT_CONV;
|
||||
}
|
||||
}
|
||||
|
||||
return OP_NONE;
|
||||
}
|
||||
|
||||
static void operator_object_float(VALUE *P1, VALUE *P2, uchar op)
|
||||
{
|
||||
void *(*func)(void *, double) = (void *(*)(void *, double))((void **)(OBJECT_class(P1->_object.object)->operators))[op];
|
||||
VALUE_conv_float(P2);
|
||||
void *result = (*func)(P1->_object.object, P2->_float.value);
|
||||
OBJECT_REF(result, "operator_object_float");
|
||||
OBJECT_UNREF(P1->_object.object, "operator_object_float");
|
||||
P1->_object.object = result;
|
||||
}
|
||||
|
||||
static void operator_float_object(VALUE *P1, VALUE *P2, uchar op)
|
||||
{
|
||||
void *(*func)(void *, double) = (void *(*)(void *, double))((void **)(OBJECT_class(P2->_object.object)->operators))[op];
|
||||
VALUE_conv_float(P1);
|
||||
void *result = (*func)(P2->_object.object, P1->_float.value);
|
||||
OBJECT_REF(result, "operator_float_object");
|
||||
P1->_object.class = P2->_object.class;
|
||||
OBJECT_UNREF(P2->_object.object, "operator_float_object");
|
||||
P1->_object.object = result;
|
||||
}
|
||||
|
||||
static void operator_object(VALUE *P1, VALUE *P2, uchar op)
|
||||
{
|
||||
void *(*func)(void *, void *) = (void *(*)(void *, void *))((void **)(OBJECT_class(P1->_object.object)->operators))[op];
|
||||
void *result = (*func)(P1->_object.object, P2->_object.object);
|
||||
OBJECT_REF(result, "operator_object");
|
||||
OBJECT_UNREF(P1->_object.object, "operator_object");
|
||||
OBJECT_UNREF(P2->_object.object, "operator_object");
|
||||
P1->_object.object = result;
|
||||
}
|
||||
|
||||
static void operator_object_conv(VALUE *P1, VALUE *P2, char op)
|
||||
{
|
||||
VALUE_conv(P2, (TYPE)P1->_object.class);
|
||||
operator_object(P1, P2, op);
|
||||
}
|
||||
|
||||
static void operator_conv_object(VALUE *P1, VALUE *P2, char op)
|
||||
{
|
||||
VALUE_conv(P1, (TYPE)P2->_object.class);
|
||||
operator_object(P1, P2, op);
|
||||
}
|
||||
|
||||
static int comparator_object_float(VALUE *P1, VALUE *P2, uchar op)
|
||||
{
|
||||
int (*func)(void *, double) = (int (*)(void *, double))((void **)(OBJECT_class(P1->_object.object)->operators))[op];
|
||||
VALUE_conv_float(P2);
|
||||
int result = (*func)(P1->_object.object, P2->_float.value);
|
||||
OBJECT_UNREF(P1->_object.object, "comparator_object_float");
|
||||
return result;
|
||||
}
|
||||
|
||||
static int comparator_float_object(VALUE *P1, VALUE *P2, uchar op)
|
||||
{
|
||||
int (*func)(void *, double) = (int (*)(void *, double))((void **)(OBJECT_class(P2->_object.object)->operators))[op];
|
||||
VALUE_conv_float(P1);
|
||||
int result = (*func)(P2->_object.object, P1->_float.value);
|
||||
OBJECT_UNREF(P2->_object.object, "comparator_float_object");
|
||||
return result;
|
||||
}
|
||||
|
||||
static int comparator_object(VALUE *P1, VALUE *P2, uchar op)
|
||||
{
|
||||
int (*func)(void *, void *) = (int (*)(void *, void *))((void **)(OBJECT_class(P1->_object.object)->operators))[op];
|
||||
int result = (*func)(P1->_object.object, P2->_object.object);
|
||||
OBJECT_UNREF(P1->_object.object, "comparator_object");
|
||||
OBJECT_UNREF(P2->_object.object, "comparator_object");
|
||||
return result;
|
||||
}
|
||||
|
||||
static int comparator_object_conv(VALUE *P1, VALUE *P2, char op)
|
||||
{
|
||||
VALUE_conv(P2, (TYPE)P1->_object.class);
|
||||
return comparator_object(P1, P2, op);
|
||||
}
|
||||
|
||||
static int comparator_conv_object(VALUE *P1, VALUE *P2, char op)
|
||||
{
|
||||
VALUE_conv(P1, (TYPE)P2->_object.class);
|
||||
return comparator_object(P1, P2, op);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---- Main interpreter loop ------------------------------------------------
|
||||
|
||||
void EXEC_loop(void)
|
||||
{
|
||||
static const void *jump_table[256] =
|
||||
@ -1707,14 +1842,14 @@ _ADD_QUICK:
|
||||
if (LIKELY(type <= T_POINTER))
|
||||
goto *_aq_jump[type];
|
||||
|
||||
__AQ_VOID:
|
||||
|
||||
THROW(E_NRETURN);
|
||||
|
||||
__AQ_BOOLEAN:
|
||||
|
||||
THROW(E_TYPE, "Number", TYPE_get_name(type));
|
||||
|
||||
__AQ_VOID:
|
||||
|
||||
THROW(E_NRETURN);
|
||||
|
||||
__AQ_BYTE:
|
||||
|
||||
P1->_integer.value = (unsigned char)(P1->_integer.value + value);
|
||||
@ -2041,9 +2176,10 @@ _SUBR_LEN:
|
||||
|
||||
_SUBR_COMP:
|
||||
{
|
||||
static void *jump[17] = {
|
||||
static void *jump[] = {
|
||||
&&__SC_VARIANT, &&__SC_BOOLEAN, &&__SC_BYTE, &&__SC_SHORT, &&__SC_INTEGER, &&__SC_LONG, &&__SC_SINGLE, &&__SC_FLOAT, &&__SC_DATE,
|
||||
&&__SC_STRING, &&__SC_STRING, &&__SC_POINTER, &&__SC_ERROR, &&__SC_ERROR, &&__SC_ERROR, &&__SC_NULL, &&__SC_OBJECT
|
||||
&&__SC_STRING, &&__SC_STRING, &&__SC_POINTER, &&__SC_ERROR, &&__SC_ERROR, &&__SC_ERROR, &&__SC_NULL, &&__SC_OBJECT,
|
||||
&&__SC_OBJECT_FLOAT, &&__SC_FLOAT_OBJECT, &&__SC_OBJECT_CONV, &&__SC_CONV_OBJECT, &&__SC_OBJECT_OBJECT
|
||||
};
|
||||
|
||||
char NO_WARNING(result);
|
||||
@ -2137,6 +2273,31 @@ _SUBR_COMP:
|
||||
//RELEASE_OBJECT(P2);
|
||||
goto __SC_END_RELEASE;
|
||||
|
||||
__SC_OBJECT_FLOAT:
|
||||
|
||||
result = comparator_object_float(P1, P2, CO_EQUALF);
|
||||
goto __SC_END;
|
||||
|
||||
__SC_FLOAT_OBJECT:
|
||||
|
||||
result = comparator_float_object(P1, P2, CO_EQUALF);
|
||||
goto __SC_END;
|
||||
|
||||
__SC_OBJECT_CONV:
|
||||
|
||||
result = comparator_object_conv(P1, P2, CO_EQUAL);
|
||||
goto __SC_END;
|
||||
|
||||
__SC_CONV_OBJECT:
|
||||
|
||||
result = comparator_conv_object(P1, P2, CO_EQUAL);
|
||||
goto __SC_END;
|
||||
|
||||
__SC_OBJECT_OBJECT:
|
||||
|
||||
result = comparator_object(P1, P2, CO_EQUAL);
|
||||
goto __SC_END;
|
||||
|
||||
__SC_VARIANT:
|
||||
|
||||
{
|
||||
@ -2155,6 +2316,15 @@ _SUBR_COMP:
|
||||
variant = TRUE;
|
||||
}
|
||||
|
||||
code = check_operators(P1, P2);
|
||||
if (code)
|
||||
{
|
||||
code += T_OBJECT;
|
||||
if (!(variant || P1->type == T_OBJECT || P2->type == T_OBJECT))
|
||||
*PC |= code;
|
||||
goto *jump[code];
|
||||
}
|
||||
|
||||
type = Max(P1->type, P2->type);
|
||||
|
||||
if (TYPE_is_object_null(P1->type) && TYPE_is_object_null(P2->type))
|
||||
@ -2345,9 +2515,10 @@ __END:
|
||||
|
||||
static void _SUBR_compn(ushort code)
|
||||
{
|
||||
static void *jump[17] = {
|
||||
static void *jump[] = {
|
||||
&&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE,
|
||||
&&__STRING, &&__STRING, &&__POINTER, &&__ERROR, &&__ERROR, &&__ERROR, &&__NULL, &&__OBJECT
|
||||
&&__STRING, &&__STRING, &&__POINTER, &&__ERROR, &&__ERROR, &&__ERROR, &&__NULL, &&__OBJECT,
|
||||
&&__OBJECT_FLOAT, &&__FLOAT_OBJECT, &&__OBJECT_CONV, &&__CONV_OBJECT, &&__OBJECT_OBJECT
|
||||
};
|
||||
|
||||
//static void *test[] = { &&__EQ, &&__NE, &&__GT, &&__LE, &&__LT, &&__GE };
|
||||
@ -2443,6 +2614,31 @@ __OBJECT:
|
||||
//RELEASE_OBJECT(P2);
|
||||
goto __END_RELEASE;
|
||||
|
||||
__OBJECT_FLOAT:
|
||||
|
||||
result = comparator_object_float(P1, P2, CO_EQUALF);
|
||||
goto __END;
|
||||
|
||||
__FLOAT_OBJECT:
|
||||
|
||||
result = comparator_float_object(P1, P2, CO_EQUALF);
|
||||
goto __END;
|
||||
|
||||
__OBJECT_CONV:
|
||||
|
||||
result = comparator_object_conv(P1, P2, CO_EQUAL);
|
||||
goto __END;
|
||||
|
||||
__CONV_OBJECT:
|
||||
|
||||
result = comparator_conv_object(P1, P2, CO_EQUAL);
|
||||
goto __END;
|
||||
|
||||
__OBJECT_OBJECT:
|
||||
|
||||
result = comparator_object(P1, P2, CO_EQUAL);
|
||||
goto __END;
|
||||
|
||||
__VARIANT:
|
||||
|
||||
{
|
||||
@ -2461,6 +2657,15 @@ __VARIANT:
|
||||
variant = TRUE;
|
||||
}
|
||||
|
||||
code = check_operators(P1, P2);
|
||||
if (code)
|
||||
{
|
||||
code += T_OBJECT;
|
||||
if (!(variant || P1->type == T_OBJECT || P2->type == T_OBJECT))
|
||||
*PC |= code;
|
||||
goto *jump[code];
|
||||
}
|
||||
|
||||
type = Max(P1->type, P2->type);
|
||||
|
||||
if (TYPE_is_object_null(P1->type) && TYPE_is_object_null(P2->type))
|
||||
@ -2493,7 +2698,7 @@ __END:
|
||||
P1->_boolean.value = result - 1; // ? 0 : -1;
|
||||
}
|
||||
|
||||
#define sgn(_x) \
|
||||
/*#define sgn(_x) \
|
||||
({ \
|
||||
int x = _x; \
|
||||
int minusOne = x >> 31; \
|
||||
@ -2501,13 +2706,653 @@ __END:
|
||||
int plusOne = (int)(negateX >> 31); \
|
||||
int result = minusOne | plusOne; \
|
||||
result; \
|
||||
})
|
||||
})*/
|
||||
|
||||
static void my_VALUE_class_constant(CLASS *class, VALUE *value, int ind)
|
||||
{
|
||||
VALUE_class_constant_inline(class, value, ind);
|
||||
}
|
||||
|
||||
#define STATIC_SUBR
|
||||
#include "gbx_subr_common.h"
|
||||
#define MANAGE_VARIANT_OBJECT(_func) \
|
||||
({ \
|
||||
type = Max(P1->type, P2->type); \
|
||||
if (TYPE_is_void(P1->type) || TYPE_is_void(P2->type)) \
|
||||
THROW(E_NRETURN); \
|
||||
\
|
||||
if (TYPE_is_number_date(type)) \
|
||||
{ \
|
||||
*PC |= type; \
|
||||
goto *jump[type]; \
|
||||
} \
|
||||
\
|
||||
code = check_operators(P1, P2); \
|
||||
if (code) \
|
||||
{ \
|
||||
code += T_DATE; \
|
||||
if (!(P1->type == T_OBJECT || P2->type == T_OBJECT)) \
|
||||
*PC |= code; \
|
||||
goto *jump[code]; \
|
||||
} \
|
||||
\
|
||||
VARIANT_undo(P1); \
|
||||
VARIANT_undo(P2); \
|
||||
\
|
||||
if (TYPE_is_string(P1->type)) \
|
||||
VALUE_conv_float(P1); \
|
||||
\
|
||||
if (TYPE_is_string(P2->type)) \
|
||||
VALUE_conv_float(P2); \
|
||||
\
|
||||
if (TYPE_is_null(P1->type) || TYPE_is_null(P2->type)) \
|
||||
type = T_NULL; \
|
||||
else \
|
||||
type = Max(P1->type, P2->type); \
|
||||
\
|
||||
if (TYPE_is_number_date(type)) \
|
||||
{ \
|
||||
(_func)(code | type); \
|
||||
VALUE_conv_variant(P1); \
|
||||
return; \
|
||||
} \
|
||||
\
|
||||
code = check_operators(P1, P2); \
|
||||
if (code) \
|
||||
{ \
|
||||
(_func)(code + T_DATE); \
|
||||
VALUE_conv_variant(P1); \
|
||||
return; \
|
||||
} \
|
||||
})
|
||||
|
||||
#define MANAGE_VARIANT_POINTER_OBJECT(_func) \
|
||||
({ \
|
||||
type = Max(P1->type, P2->type); \
|
||||
if (TYPE_is_void(P1->type) || TYPE_is_void(P2->type)) \
|
||||
THROW(E_NRETURN); \
|
||||
\
|
||||
if (TYPE_is_number_date(type) || TYPE_is_pointer(type)) \
|
||||
{ \
|
||||
*PC |= type; \
|
||||
goto *jump[type]; \
|
||||
} \
|
||||
\
|
||||
code = check_operators(P1, P2); \
|
||||
if (code) \
|
||||
{ \
|
||||
code += T_POINTER; \
|
||||
*PC |= code; \
|
||||
goto *jump[code]; \
|
||||
} \
|
||||
\
|
||||
VARIANT_undo(P1); \
|
||||
VARIANT_undo(P2); \
|
||||
\
|
||||
if (TYPE_is_string(P1->type)) \
|
||||
VALUE_conv_float(P1); \
|
||||
\
|
||||
if (TYPE_is_string(P2->type)) \
|
||||
VALUE_conv_float(P2); \
|
||||
\
|
||||
if (TYPE_is_null(P1->type) || TYPE_is_null(P2->type)) \
|
||||
type = T_NULL; \
|
||||
else \
|
||||
type = Max(P1->type, P2->type); \
|
||||
\
|
||||
if (TYPE_is_number_date(type) || TYPE_is_pointer(type)) \
|
||||
{ \
|
||||
(_func)(code | type); \
|
||||
VALUE_conv_variant(P1); \
|
||||
return; \
|
||||
} \
|
||||
\
|
||||
code = check_operators(P1, P2); \
|
||||
if (code) \
|
||||
{ \
|
||||
(_func)(code + T_POINTER); \
|
||||
VALUE_conv_variant(P1); \
|
||||
return; \
|
||||
} \
|
||||
})
|
||||
|
||||
|
||||
static void _SUBR_add(ushort code)
|
||||
{
|
||||
static void *jump[] = {
|
||||
&&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, NULL, NULL, &&__POINTER,
|
||||
&&__OBJECT_FLOAT, &&__FLOAT_OBJECT, &&__OBJECT_CONV, &&__CONV_OBJECT, &&__OBJECT
|
||||
};
|
||||
|
||||
TYPE type;
|
||||
VALUE *P1, *P2;
|
||||
|
||||
P1 = SP - 2;
|
||||
P2 = P1 + 1;
|
||||
|
||||
type = code & 0x0F;
|
||||
goto *jump[type];
|
||||
|
||||
__BOOLEAN:
|
||||
|
||||
P1->type = T_BOOLEAN;
|
||||
P1->_integer.value = P1->_integer.value | P2->_integer.value; goto __END;
|
||||
|
||||
__BYTE:
|
||||
|
||||
P1->type = T_BYTE;
|
||||
P1->_integer.value = (unsigned char)(P1->_integer.value + P2->_integer.value); goto __END;
|
||||
|
||||
__SHORT:
|
||||
|
||||
P1->type = T_SHORT;
|
||||
P1->_integer.value = (short)(P1->_integer.value + P2->_integer.value); goto __END;
|
||||
|
||||
__INTEGER:
|
||||
|
||||
P1->type = T_INTEGER;
|
||||
P1->_integer.value += P2->_integer.value; goto __END;
|
||||
|
||||
__LONG:
|
||||
|
||||
VALUE_conv(P1, T_LONG);
|
||||
VALUE_conv(P2, T_LONG);
|
||||
|
||||
P1->_long.value += P2->_long.value; goto __END;
|
||||
|
||||
__SINGLE:
|
||||
|
||||
VALUE_conv(P1, T_SINGLE);
|
||||
VALUE_conv(P2, T_SINGLE);
|
||||
|
||||
P1->_single.value += P2->_single.value; goto __END;
|
||||
|
||||
__DATE:
|
||||
__FLOAT:
|
||||
|
||||
VALUE_conv_float(P1);
|
||||
VALUE_conv_float(P2);
|
||||
|
||||
P1->_float.value += P2->_float.value;
|
||||
//fprintf(stderr, "+: %.24g\n", P1->_float.value);
|
||||
goto __END;
|
||||
|
||||
__POINTER:
|
||||
|
||||
VALUE_conv(P1, T_POINTER);
|
||||
VALUE_conv(P2, T_POINTER);
|
||||
|
||||
P1->_pointer.value += (intptr_t)P2->_pointer.value; goto __END;
|
||||
|
||||
__OBJECT_FLOAT:
|
||||
|
||||
operator_object_float(P1, P2, CO_ADDF);
|
||||
goto __END;
|
||||
|
||||
__FLOAT_OBJECT:
|
||||
|
||||
operator_float_object(P1, P2, CO_ADDF);
|
||||
goto __END;
|
||||
|
||||
__OBJECT_CONV:
|
||||
|
||||
operator_object_conv(P1, P2, CO_ADD);
|
||||
goto __END;
|
||||
|
||||
__CONV_OBJECT:
|
||||
|
||||
operator_conv_object(P1, P2, CO_ADD);
|
||||
goto __END;
|
||||
|
||||
__OBJECT:
|
||||
|
||||
operator_object(P1, P2, CO_ADD);
|
||||
goto __END;
|
||||
|
||||
__VARIANT:
|
||||
|
||||
MANAGE_VARIANT_POINTER_OBJECT(_SUBR_add);
|
||||
goto __ERROR;
|
||||
|
||||
__ERROR:
|
||||
|
||||
THROW(E_TYPE, "Number", TYPE_get_name(type));
|
||||
|
||||
__END:
|
||||
|
||||
SP--;
|
||||
}
|
||||
|
||||
static void _SUBR_sub(ushort code)
|
||||
{
|
||||
static void *jump[] = {
|
||||
&&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, NULL, NULL, &&__POINTER,
|
||||
&&__OBJECT_FLOAT, &&__FLOAT_OBJECT, &&__OBJECT_CONV, &&__CONV_OBJECT, &&__OBJECT
|
||||
};
|
||||
|
||||
TYPE type;
|
||||
VALUE *P1, *P2;
|
||||
|
||||
P1 = SP - 2;
|
||||
P2 = P1 + 1;
|
||||
|
||||
type = code & 0x0F;
|
||||
goto *jump[type];
|
||||
|
||||
__BOOLEAN:
|
||||
|
||||
P1->type = T_BOOLEAN;
|
||||
P1->_integer.value = P1->_integer.value ^ P2->_integer.value; goto __END;
|
||||
|
||||
__BYTE:
|
||||
|
||||
P1->type = T_BYTE;
|
||||
P1->_integer.value = (unsigned char)(P1->_integer.value - P2->_integer.value); goto __END;
|
||||
|
||||
__SHORT:
|
||||
|
||||
P1->type = T_SHORT;
|
||||
P1->_integer.value = (short)(P1->_integer.value - P2->_integer.value); goto __END;
|
||||
|
||||
__INTEGER:
|
||||
|
||||
P1->type = T_INTEGER;
|
||||
P1->_integer.value -= P2->_integer.value; goto __END;
|
||||
|
||||
__LONG:
|
||||
|
||||
VALUE_conv(P1, T_LONG);
|
||||
VALUE_conv(P2, T_LONG);
|
||||
|
||||
P1->_long.value -= P2->_long.value; goto __END;
|
||||
|
||||
__SINGLE:
|
||||
|
||||
VALUE_conv(P1, T_SINGLE);
|
||||
VALUE_conv(P2, T_SINGLE);
|
||||
|
||||
P1->_single.value -= P2->_single.value; goto __END;
|
||||
|
||||
__DATE:
|
||||
__FLOAT:
|
||||
|
||||
VALUE_conv_float(P1);
|
||||
VALUE_conv_float(P2);
|
||||
|
||||
P1->_float.value -= P2->_float.value; goto __END;
|
||||
|
||||
__POINTER:
|
||||
|
||||
VALUE_conv(P1, T_POINTER);
|
||||
VALUE_conv(P2, T_POINTER);
|
||||
|
||||
P1->_pointer.value -= (intptr_t)P2->_pointer.value; goto __END;
|
||||
|
||||
__OBJECT_FLOAT:
|
||||
|
||||
operator_object_float(P1, P2, CO_SUBF);
|
||||
goto __END;
|
||||
|
||||
__FLOAT_OBJECT:
|
||||
|
||||
operator_float_object(P1, P2, CO_SUBF);
|
||||
goto __END;
|
||||
|
||||
__OBJECT_CONV:
|
||||
|
||||
operator_object_conv(P1, P2, CO_SUB);
|
||||
goto __END;
|
||||
|
||||
__CONV_OBJECT:
|
||||
|
||||
operator_conv_object(P1, P2, CO_SUB);
|
||||
goto __END;
|
||||
|
||||
__OBJECT:
|
||||
|
||||
operator_object(P1, P2, CO_SUB);
|
||||
goto __END;
|
||||
|
||||
__VARIANT:
|
||||
|
||||
MANAGE_VARIANT_POINTER_OBJECT(_SUBR_sub);
|
||||
goto __ERROR;
|
||||
|
||||
__ERROR:
|
||||
|
||||
THROW(E_TYPE, "Number", TYPE_get_name(type));
|
||||
|
||||
__END:
|
||||
|
||||
SP--;
|
||||
}
|
||||
|
||||
static void _SUBR_mul(ushort code)
|
||||
{
|
||||
static void *jump[] = {
|
||||
&&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__ERROR,
|
||||
&&__OBJECT_FLOAT, &&__FLOAT_OBJECT, &&__OBJECT_CONV, &&__CONV_OBJECT, &&__OBJECT
|
||||
};
|
||||
|
||||
TYPE type;
|
||||
VALUE *P1, *P2;
|
||||
|
||||
P1 = SP - 2;
|
||||
P2 = P1 + 1;
|
||||
|
||||
type = code & 0x0F;
|
||||
goto *jump[type];
|
||||
|
||||
__BOOLEAN:
|
||||
|
||||
P1->type = T_BOOLEAN;
|
||||
P1->_integer.value = P1->_integer.value & P2->_integer.value; goto __END;
|
||||
|
||||
__BYTE:
|
||||
|
||||
P1->type = T_BYTE;
|
||||
P1->_integer.value = (unsigned char)(P1->_integer.value * P2->_integer.value); goto __END;
|
||||
|
||||
__SHORT:
|
||||
|
||||
P1->type = T_SHORT;
|
||||
P1->_integer.value = (short)(P1->_integer.value * P2->_integer.value); goto __END;
|
||||
|
||||
__INTEGER:
|
||||
|
||||
P1->type = T_INTEGER;
|
||||
P1->_integer.value *= P2->_integer.value; goto __END;
|
||||
|
||||
__LONG:
|
||||
|
||||
VALUE_conv(P1, T_LONG);
|
||||
VALUE_conv(P2, T_LONG);
|
||||
|
||||
P1->_long.value *= P2->_long.value; goto __END;
|
||||
|
||||
__SINGLE:
|
||||
|
||||
VALUE_conv(P1, T_SINGLE);
|
||||
VALUE_conv(P2, T_SINGLE);
|
||||
|
||||
P1->_single.value *= P2->_single.value; goto __END;
|
||||
|
||||
__FLOAT:
|
||||
|
||||
VALUE_conv_float(P1);
|
||||
VALUE_conv_float(P2);
|
||||
|
||||
P1->_float.value *= P2->_float.value;
|
||||
//fprintf(stderr, "*: %.24g\n", P1->_float.value);
|
||||
goto __END;
|
||||
|
||||
__OBJECT_FLOAT:
|
||||
|
||||
operator_object_float(P1, P2, CO_MULF);
|
||||
goto __END;
|
||||
|
||||
__FLOAT_OBJECT:
|
||||
|
||||
operator_float_object(P1, P2, CO_MULF);
|
||||
goto __END;
|
||||
|
||||
__OBJECT_CONV:
|
||||
|
||||
operator_object_conv(P1, P2, CO_MUL);
|
||||
goto __END;
|
||||
|
||||
__CONV_OBJECT:
|
||||
|
||||
operator_conv_object(P1, P2, CO_MUL);
|
||||
goto __END;
|
||||
|
||||
__OBJECT:
|
||||
|
||||
operator_object(P1, P2, CO_MUL);
|
||||
goto __END;
|
||||
|
||||
__VARIANT:
|
||||
|
||||
MANAGE_VARIANT_OBJECT(_SUBR_mul);
|
||||
goto __ERROR;
|
||||
|
||||
__ERROR:
|
||||
|
||||
THROW(E_TYPE, "Number", TYPE_get_name(type));
|
||||
|
||||
__END:
|
||||
|
||||
SP--;
|
||||
}
|
||||
|
||||
static void _SUBR_div(ushort code)
|
||||
{
|
||||
static void *jump[] = {
|
||||
&&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__ERROR,
|
||||
&&__OBJECT_FLOAT, &&__FLOAT_OBJECT, &&__OBJECT_CONV, &&__CONV_OBJECT, &&__OBJECT
|
||||
};
|
||||
|
||||
TYPE type;
|
||||
VALUE *P1, *P2;
|
||||
|
||||
P1 = SP - 2;
|
||||
P2 = P1 + 1;
|
||||
|
||||
type = code & 0x0F;
|
||||
goto *jump[type];
|
||||
|
||||
__BOOLEAN:
|
||||
__BYTE:
|
||||
__SHORT:
|
||||
__INTEGER:
|
||||
__LONG:
|
||||
__SINGLE:
|
||||
__FLOAT:
|
||||
|
||||
VALUE_conv_float(P1);
|
||||
VALUE_conv_float(P2);
|
||||
|
||||
P1->_float.value /= P2->_float.value;
|
||||
if (isfinite(P1->_float.value))
|
||||
goto __END;
|
||||
|
||||
THROW(E_ZERO);
|
||||
|
||||
__OBJECT_FLOAT:
|
||||
|
||||
operator_object_float(P1, P2, CO_DIVF);
|
||||
goto __CHECK_OBJECT;
|
||||
|
||||
__FLOAT_OBJECT:
|
||||
|
||||
operator_float_object(P1, P2, CO_IDIVF);
|
||||
goto __CHECK_OBJECT;
|
||||
|
||||
__OBJECT_CONV:
|
||||
|
||||
operator_object_conv(P1, P2, CO_DIV);
|
||||
goto __CHECK_OBJECT;
|
||||
|
||||
__CONV_OBJECT:
|
||||
|
||||
operator_conv_object(P1, P2, CO_DIV);
|
||||
goto __CHECK_OBJECT;
|
||||
|
||||
__OBJECT:
|
||||
|
||||
operator_object(P1, P2, CO_DIV);
|
||||
goto __CHECK_OBJECT;
|
||||
|
||||
__VARIANT:
|
||||
|
||||
MANAGE_VARIANT_OBJECT(_SUBR_div);
|
||||
goto __ERROR;
|
||||
|
||||
__ERROR:
|
||||
|
||||
THROW(E_TYPE, "Number", TYPE_get_name(type));
|
||||
|
||||
__CHECK_OBJECT:
|
||||
|
||||
if (P1->_object.object == NULL)
|
||||
THROW(E_ZERO);
|
||||
|
||||
__END:
|
||||
|
||||
SP--;
|
||||
}
|
||||
|
||||
static void _SUBR_compi(ushort code)
|
||||
{
|
||||
static void *jump[17] = {
|
||||
&&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE,
|
||||
&&__STRING, &&__STRING, &&__POINTER, &&__ERROR, &&__ERROR, &&__ERROR, &&__NULL, &&__OBJECT
|
||||
};
|
||||
|
||||
static void *test[] = { &&__GT, &&__LE, &&__LT, &&__GE };
|
||||
|
||||
char NO_WARNING(result);
|
||||
VALUE *P1;
|
||||
VALUE *P2;
|
||||
TYPE type;
|
||||
|
||||
P1 = SP - 2;
|
||||
P2 = P1 + 1;
|
||||
|
||||
type = code & 0x1F;
|
||||
goto *jump[type];
|
||||
|
||||
__BOOLEAN:
|
||||
__BYTE:
|
||||
__SHORT:
|
||||
__INTEGER:
|
||||
|
||||
result = P1->_integer.value > P2->_integer.value ? 1 : P1->_integer.value < P2->_integer.value ? -1 : 0;
|
||||
goto __END;
|
||||
|
||||
__LONG:
|
||||
|
||||
VALUE_conv(P1, T_LONG);
|
||||
VALUE_conv(P2, T_LONG);
|
||||
|
||||
result = P1->_long.value > P2->_long.value ? 1 : P1->_long.value < P2->_long.value ? -1 : 0;
|
||||
goto __END;
|
||||
|
||||
__DATE:
|
||||
|
||||
VALUE_conv(P1, T_DATE);
|
||||
VALUE_conv(P2, T_DATE);
|
||||
|
||||
result = DATE_comp_value(P1, P2);
|
||||
goto __END;
|
||||
|
||||
__NULL:
|
||||
__STRING:
|
||||
|
||||
VALUE_conv_string(P1);
|
||||
VALUE_conv_string(P2);
|
||||
|
||||
result = STRING_compare(P1->_string.addr + P1->_string.start, P1->_string.len, P2->_string.addr + P2->_string.start, P2->_string.len);
|
||||
|
||||
RELEASE_STRING(P1);
|
||||
RELEASE_STRING(P2);
|
||||
goto __END;
|
||||
|
||||
__SINGLE:
|
||||
|
||||
VALUE_conv(P1, T_SINGLE);
|
||||
VALUE_conv(P2, T_SINGLE);
|
||||
|
||||
result = P1->_single.value > P2->_single.value ? 1 : P1->_single.value < P2->_single.value ? -1 : 0;
|
||||
goto __END;
|
||||
|
||||
__FLOAT:
|
||||
|
||||
VALUE_conv_float(P1);
|
||||
VALUE_conv_float(P2);
|
||||
|
||||
result = P1->_float.value > P2->_float.value ? 1 : P1->_float.value < P2->_float.value ? -1 : 0;
|
||||
goto __END;
|
||||
|
||||
__POINTER:
|
||||
|
||||
VALUE_conv(P1, T_POINTER);
|
||||
VALUE_conv(P2, T_POINTER);
|
||||
|
||||
result = P1->_pointer.value > P2->_pointer.value ? 1 : P1->_pointer.value < P2->_pointer.value ? -1 : 0;
|
||||
goto __END;
|
||||
|
||||
__OBJECT:
|
||||
|
||||
result = OBJECT_comp_value(P1, P2);
|
||||
//RELEASE_OBJECT(P1);
|
||||
//RELEASE_OBJECT(P2);
|
||||
goto __END_RELEASE;
|
||||
|
||||
__VARIANT:
|
||||
|
||||
{
|
||||
bool variant = FALSE;
|
||||
|
||||
if (TYPE_is_variant(P1->type))
|
||||
{
|
||||
VARIANT_undo(P1);
|
||||
variant = TRUE;
|
||||
}
|
||||
|
||||
if (TYPE_is_variant(P2->type))
|
||||
{
|
||||
VARIANT_undo(P2);
|
||||
variant = TRUE;
|
||||
}
|
||||
|
||||
type = Max(P1->type, P2->type);
|
||||
|
||||
if (type == T_NULL || TYPE_is_string(type))
|
||||
{
|
||||
TYPE typem = Min(P1->type, P2->type);
|
||||
if (!TYPE_is_string(typem))
|
||||
THROW(E_TYPE, TYPE_get_name(typem), TYPE_get_name(type));
|
||||
}
|
||||
else if (TYPE_is_object(type))
|
||||
goto __ERROR;
|
||||
else if (TYPE_is_void(type))
|
||||
THROW(E_NRETURN);
|
||||
|
||||
if (!variant)
|
||||
*PC |= type;
|
||||
|
||||
goto *jump[type];
|
||||
}
|
||||
|
||||
__ERROR:
|
||||
|
||||
THROW(E_TYPE, "Number, Date or String", TYPE_get_name(type));
|
||||
|
||||
__END_RELEASE:
|
||||
|
||||
RELEASE(P1);
|
||||
RELEASE(P2);
|
||||
|
||||
__END:
|
||||
|
||||
P1->type = T_BOOLEAN;
|
||||
SP--;
|
||||
|
||||
goto *test[(code >> 8) - (C_GT >> 8)];
|
||||
|
||||
__GT:
|
||||
P1->_boolean.value = result > 0 ? -1 : 0;
|
||||
return;
|
||||
|
||||
__GE:
|
||||
P1->_boolean.value = result >= 0 ? -1 : 0;
|
||||
return;
|
||||
|
||||
__LT:
|
||||
P1->_boolean.value = result < 0 ? -1 : 0;
|
||||
return;
|
||||
|
||||
__LE:
|
||||
P1->_boolean.value = result <= 0 ? -1 : 0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ typedef
|
||||
#define OBJECT_is(_object, _class) (OBJECT_class(_object) == _class)
|
||||
#define OBJECT_is_class(_object) OBJECT_is(_object, CLASS_Class)
|
||||
#define OBJECT_class(_object) ((_object) ? ((OBJECT *)_object)->class : NULL)
|
||||
#define OBJECT_count(_object) ((_object) ? ((OBJECT *)_object)->ref : 0)
|
||||
#define OBJECT_count(_object) (((OBJECT *)_object)->ref)
|
||||
|
||||
void *OBJECT_new(CLASS *class, const char *name, OBJECT *parent);
|
||||
void OBJECT_attach(OBJECT *ob, OBJECT *parent, const char *name);
|
||||
|
@ -52,6 +52,30 @@
|
||||
#include "gbx_subr_math_temp.h"
|
||||
|
||||
|
||||
static bool check_operators(VALUE *P1)
|
||||
{
|
||||
return (TYPE_is_object(P1->type) && OBJECT_class(P1->_object.object)->has_operators);
|
||||
}
|
||||
|
||||
static void operator_object_abs(VALUE *P1)
|
||||
{
|
||||
double (*func)(void *) = (double (*)(void *))((void **)(OBJECT_class(P1->_object.object)->operators))[CO_ABS];
|
||||
double result = (*func)(P1->_object.object);
|
||||
OBJECT_UNREF(P1->_object.object, "operator_object_abs");
|
||||
P1->type = T_FLOAT;
|
||||
P1->_float.value = result;
|
||||
}
|
||||
|
||||
static void operator_object(VALUE *P1, uchar op)
|
||||
{
|
||||
void *(*func)(void *) = (void *(*)(void *))((void **)(OBJECT_class(P1->_object.object)->operators))[op];
|
||||
void *result = (*func)(P1->_object.object);
|
||||
OBJECT_REF(result, "operator_object");
|
||||
OBJECT_UNREF(P1->_object.object, "operator_object");
|
||||
P1->_object.object = result;
|
||||
}
|
||||
|
||||
|
||||
void SUBR_pi(ushort code)
|
||||
{
|
||||
SUBR_ENTER();
|
||||
@ -135,7 +159,6 @@ void SUBR_round(ushort code)
|
||||
|
||||
void SUBR_math(ushort code)
|
||||
{
|
||||
|
||||
static void *jump[] = {
|
||||
NULL, &&__FRAC, &&__LOG, &&__EXP, &&__SQRT, &&__SIN, &&__COS, &&__TAN, &&__ATAN, &&__ASIN, &&__ACOS,
|
||||
&&__DEG, &&__RAD, &&__LOG10, &&__SINH, &&__COSH, &&__TANH, &&__ASINH, &&__ACOSH, &&__ATANH,
|
||||
@ -493,6 +516,41 @@ __END:
|
||||
} \
|
||||
})
|
||||
|
||||
#define MANAGE_VARIANT_OBJECT(_func) \
|
||||
({ \
|
||||
type = P1->type; \
|
||||
\
|
||||
if (TYPE_is_number_date(type)) \
|
||||
{ \
|
||||
*PC |= type; \
|
||||
goto *jump[type]; \
|
||||
} \
|
||||
\
|
||||
if (check_operators(P1)) \
|
||||
{ \
|
||||
*PC |= T_DATE + 1; \
|
||||
goto *jump[T_DATE + 1]; \
|
||||
} \
|
||||
\
|
||||
if (TYPE_is_variant(type)) \
|
||||
{ \
|
||||
VARIANT_undo(P1); \
|
||||
type = P1->type; \
|
||||
if (TYPE_is_number_date(type)) \
|
||||
{ \
|
||||
(_func)(code | type); \
|
||||
VALUE_conv_variant(P1); \
|
||||
return; \
|
||||
} \
|
||||
if (check_operators(P1)) \
|
||||
{ \
|
||||
(_func)(T_DATE + 1); \
|
||||
VALUE_conv_variant(P1); \
|
||||
return; \
|
||||
} \
|
||||
} \
|
||||
})
|
||||
|
||||
|
||||
void SUBR_sgn(ushort code)
|
||||
{
|
||||
@ -550,7 +608,7 @@ __END:
|
||||
void SUBR_neg(ushort code)
|
||||
{
|
||||
static void *jump[] = {
|
||||
&&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__ERROR
|
||||
&&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__ERROR, &&__OBJECT
|
||||
};
|
||||
|
||||
VALUE *P1;
|
||||
@ -590,9 +648,14 @@ __FLOAT:
|
||||
|
||||
P1->_float.value = (-P1->_float.value); return;
|
||||
|
||||
__OBJECT:
|
||||
|
||||
operator_object(P1, CO_NEG);
|
||||
return;
|
||||
|
||||
__VARIANT:
|
||||
|
||||
MANAGE_VARIANT(SUBR_neg);
|
||||
MANAGE_VARIANT_OBJECT(SUBR_neg);
|
||||
|
||||
__ERROR:
|
||||
|
||||
@ -603,7 +666,7 @@ __ERROR:
|
||||
void SUBR_abs(ushort code)
|
||||
{
|
||||
static void *jump[] = {
|
||||
&&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__ERROR
|
||||
&&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__ERROR, &&__OBJECT
|
||||
};
|
||||
|
||||
VALUE *P1;
|
||||
@ -643,9 +706,14 @@ __FLOAT:
|
||||
|
||||
P1->_float.value = fabs(P1->_float.value); return;
|
||||
|
||||
__OBJECT:
|
||||
|
||||
operator_object_abs(P1);
|
||||
return;
|
||||
|
||||
__VARIANT:
|
||||
|
||||
MANAGE_VARIANT(SUBR_abs);
|
||||
MANAGE_VARIANT_OBJECT(SUBR_abs);
|
||||
|
||||
__ERROR:
|
||||
|
||||
|
@ -728,7 +728,13 @@ __OBJECT:
|
||||
if (type == T_VARIANT)
|
||||
goto __2v;
|
||||
|
||||
goto __N;
|
||||
if (value->type == T_OBJECT)
|
||||
class = OBJECT_class(value->_object.object);
|
||||
else
|
||||
class = value->_object.class;
|
||||
|
||||
goto __CONVERT;
|
||||
//goto __N;
|
||||
}
|
||||
|
||||
if (!TYPE_is_object(value->type))
|
||||
@ -771,13 +777,7 @@ __OBJECT:
|
||||
goto __TYPE;
|
||||
|
||||
if (value->type == T_OBJECT)
|
||||
{
|
||||
/*if (value->_object.object == NULL)
|
||||
goto __TYPE;*/
|
||||
|
||||
class = OBJECT_class(value->_object.object);
|
||||
/* on continue */
|
||||
}
|
||||
else
|
||||
class = value->_object.class;
|
||||
|
||||
@ -799,14 +799,14 @@ __RETRY:
|
||||
goto __RETRY;
|
||||
}
|
||||
|
||||
if (class->special[SPEC_CONVERT] != NO_SYMBOL)
|
||||
__CONVERT:
|
||||
|
||||
if (class->has_convert)
|
||||
{
|
||||
void *conv = ((void *(*)())(CLASS_get_desc(class, class->special[SPEC_CONVERT])->constant.value._pointer))(value->_object.object, type);
|
||||
if (conv)
|
||||
void *unref = value->_object.object;
|
||||
if (!((bool (*)())(CLASS_get_desc(class, class->special[SPEC_CONVERT])->constant.value._pointer))(value->_object.object, type, value))
|
||||
{
|
||||
OBJECT_REF(conv, "VALUE_conv");
|
||||
OBJECT_UNREF(value->_object.object, "VALUE_conv");
|
||||
value->_object.object = conv;
|
||||
OBJECT_UNREF(unref, "VALUE_conv");
|
||||
goto __TYPE;
|
||||
}
|
||||
}
|
||||
@ -1175,12 +1175,29 @@ __STRING:
|
||||
|
||||
__OBJECT:
|
||||
|
||||
if (VALUE_is_null(value))
|
||||
goto __NULL;
|
||||
{
|
||||
CLASS *class;
|
||||
|
||||
if (VALUE_is_null(value))
|
||||
goto __NULL;
|
||||
|
||||
*len = sprintf(COMMON_buffer, "(%s %p)", OBJECT_class(value->_object.object)->name, value->_object.object);
|
||||
*addr = COMMON_buffer;
|
||||
return;
|
||||
class = OBJECT_class(value->_object.object);
|
||||
if (class->has_convert)
|
||||
{
|
||||
VALUE temp;
|
||||
if (!((bool (*)())(CLASS_get_desc(class, class->special[SPEC_CONVERT])->constant.value._pointer))(value->_object.object, T_CSTRING, &temp))
|
||||
{
|
||||
*addr = temp._string.addr + temp._string.start;
|
||||
*len = temp._string.len;
|
||||
STRING_free_later(*addr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
*len = sprintf(COMMON_buffer, "(%s %p)", class->name, value->_object.object);
|
||||
*addr = COMMON_buffer;
|
||||
return;
|
||||
}
|
||||
|
||||
__POINTER:
|
||||
|
||||
|
@ -297,6 +297,7 @@ typedef
|
||||
GB_BOOLEAN _boolean;
|
||||
GB_INTEGER _integer;
|
||||
GB_LONG _long;
|
||||
GB_SINGLE _single;
|
||||
GB_FLOAT _float;
|
||||
GB_DATE _date;
|
||||
GB_STRING _string;
|
||||
@ -314,6 +315,7 @@ typedef
|
||||
#define GB_ERR_NPROPERTY ((char *)17)
|
||||
#define GB_ERR_ARG ((char *)20)
|
||||
#define GB_ERR_BOUND ((char *)21)
|
||||
#define GB_ERR_ZERO ((char *)26)
|
||||
|
||||
|
||||
/* Gambas description start macro */
|
||||
@ -813,6 +815,34 @@ typedef
|
||||
|
||||
#define POINTER(_pointer) (void **)(void *)_pointer
|
||||
|
||||
/* For classes that implements arithmetic operators (e.g. complex numbers...) */
|
||||
|
||||
typedef
|
||||
struct {
|
||||
int (*equal)(void *, void *);
|
||||
int (*equalf)(void *, double);
|
||||
int (*comp)(void *, void *);
|
||||
int (*compf)(void *, double);
|
||||
void *(*add)(void *, void *);
|
||||
void *(*addf)(void *, double);
|
||||
void *(*sub)(void *, void *);
|
||||
void *(*subf)(void *, double);
|
||||
void *(*mul)(void *, void *);
|
||||
void *(*mulf)(void *, double);
|
||||
void *(*div)(void *, void *);
|
||||
void *(*divf)(void *, double);
|
||||
void *(*idivf)(void *, double);
|
||||
void *(*neg)(void *);
|
||||
void *(*pow)(void *, void *);
|
||||
void *(*powf)(void *, double);
|
||||
void *(*abs)(void *);
|
||||
void *(*max)(void *, void *);
|
||||
void *(*min)(void *, void *);
|
||||
}
|
||||
PACKED
|
||||
GB_OPERATOR_DESC;
|
||||
|
||||
|
||||
/* Gambas Application Programming Interface */
|
||||
|
||||
typedef
|
||||
|
@ -26,6 +26,87 @@
|
||||
#define STATIC_SUBR static
|
||||
#endif
|
||||
|
||||
#define OP_OBJECT_FLOAT (T_POINTER + 1)
|
||||
#define OP_FLOAT_OBJECT (T_POINTER + 2)
|
||||
#define OP_OBJECT_CONV (T_POINTER + 3)
|
||||
#define OP_CONV_OBJECT (T_POINTER + 4)
|
||||
#define OP_OBJECT (T_POINTER + 5)
|
||||
|
||||
static int check_operators(VALUE *P1, VALUE *P2)
|
||||
{
|
||||
if (TYPE_is_number(P1->type))
|
||||
{
|
||||
if (OBJECT_class(P2->_object.object)->operators)
|
||||
return OP_FLOAT_OBJECT;
|
||||
}
|
||||
else if (TYPE_is_number(P2->type))
|
||||
{
|
||||
if (OBJECT_class(P1->_object.object)->operators)
|
||||
return OP_OBJECT_FLOAT;
|
||||
}
|
||||
else
|
||||
{
|
||||
CLASS *class1 = OBJECT_class(P1->_object.object);
|
||||
CLASS *class2 = OBJECT_class(P2->_object.object);
|
||||
|
||||
if (class1->operators)
|
||||
{
|
||||
if (class1 == class2)
|
||||
return OP_OBJECT;
|
||||
|
||||
if (class2->operators)
|
||||
{
|
||||
if (class1->operators->strength > class2->operators->strength)
|
||||
return OP_OBJECT_CONV;
|
||||
else
|
||||
return OP_CONV_OBJECT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void operator_object_float(VALUE *P1, VALUE *P2, uchar op)
|
||||
{
|
||||
void *(*func)(void *, double) = (void *(*)(void *, double))((void **)(OBJECT_class(P1->_object.object)->operators))[op];
|
||||
VALUE_conv_float(P2);
|
||||
void *result = (*func)(P1->_object.object, P2->_float.value);
|
||||
OBJECT_unref(P1->_object.object);
|
||||
P1->_object.object = result;
|
||||
}
|
||||
|
||||
static void operator_float_object(VALUE *P1, VALUE *P2, uchar op)
|
||||
{
|
||||
void *(*func)(void *, double) = (void *(*)(void *, double))((void **)(OBJECT_class(P2->_object.object)->operators))[op];
|
||||
VALUE_conv_float(P1);
|
||||
void *result = (*func)(P2->_object.object, P1->_float.value);
|
||||
P1->_object.class = P2->_object.class;
|
||||
OBJECT_unref(P2->_object.object);
|
||||
P1->_object.object = result;
|
||||
}
|
||||
|
||||
static void operator_object(VALUE *P1, VALUE *P2, uchar op)
|
||||
{
|
||||
void *(*func)(void *, void *) = (void *(*)(void *, void *))((void **)(OBJECT_class(P2->_object.object)->operators))[op];
|
||||
void *result = (*func)(P1->_object.object, P2->_object.object);
|
||||
OBJECT_unref(P1->_object.object);
|
||||
OBJECT_unref(P2->_object.object);
|
||||
P1->_object.object = result;
|
||||
}
|
||||
|
||||
static void operator_object_conv(VALUE *P1, VALUE *P2, char op)
|
||||
{
|
||||
VALUE_conv(P2, (TYPE)P1->_object.class);
|
||||
operator_object(P1, P2, op);
|
||||
}
|
||||
|
||||
static void operator_conv_object(VALUE *P1, VALUE *P2, char op)
|
||||
{
|
||||
VALUE_conv(P1, (TYPE)P2->_object.class);
|
||||
operator_object(P1, P2, op);
|
||||
}
|
||||
|
||||
#define MANAGE_VARIANT(_func) \
|
||||
({ \
|
||||
type = Max(P1->type, P2->type); \
|
||||
@ -95,10 +176,57 @@
|
||||
})
|
||||
|
||||
|
||||
#define MANAGE_VARIANT_POINTER_OBJECT(_func) \
|
||||
({ \
|
||||
type = Max(P1->type, P2->type); \
|
||||
if (TYPE_is_void(P1->type) || TYPE_is_void(P2->type)) \
|
||||
THROW(E_NRETURN); \
|
||||
\
|
||||
if (TYPE_is_number_date(type) || TYPE_is_pointer(type)) \
|
||||
{ \
|
||||
*PC |= type; \
|
||||
goto *jump[type]; \
|
||||
} \
|
||||
\
|
||||
VARIANT_undo(P1); \
|
||||
VARIANT_undo(P2); \
|
||||
\
|
||||
if (TYPE_is_string(P1->type)) \
|
||||
VALUE_conv_float(P1); \
|
||||
\
|
||||
if (TYPE_is_string(P2->type)) \
|
||||
VALUE_conv_float(P2); \
|
||||
\
|
||||
if (TYPE_is_null(P1->type) || TYPE_is_null(P2->type)) \
|
||||
type = T_NULL; \
|
||||
else \
|
||||
type = Max(P1->type, P2->type); \
|
||||
\
|
||||
if (TYPE_is_number_date(type) || TYPE_is_pointer(type)) \
|
||||
{ \
|
||||
(_func)(code | type); \
|
||||
VALUE_conv_variant(P1); \
|
||||
return; \
|
||||
} \
|
||||
\
|
||||
if (TYPE_is_object(type)) \
|
||||
{ \
|
||||
type = check_operators(P1, P2); \
|
||||
if (type) \
|
||||
{ \
|
||||
*PC |= type; \
|
||||
goto *jump[type]; \
|
||||
} \
|
||||
} \
|
||||
})
|
||||
|
||||
|
||||
STATIC_SUBR void _SUBR_add(ushort code)
|
||||
{
|
||||
static void *jump[] = {
|
||||
&&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, NULL, NULL, &&__POINTER
|
||||
&&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT,
|
||||
&&__DATE, NULL, NULL, &&__POINTER,
|
||||
&&__OBJECT_FLOAT, &&__FLOAT_OBJECT, &&__OBJECT_CONV, &&__CONV_OBJECT, &&__OBJECT
|
||||
};
|
||||
|
||||
TYPE type;
|
||||
@ -161,11 +289,36 @@ __POINTER:
|
||||
|
||||
P1->_pointer.value += (intptr_t)P2->_pointer.value; goto __END;
|
||||
|
||||
__OBJECT_FLOAT:
|
||||
|
||||
operator_object_float(P1, P2, CO_ADDF);
|
||||
goto __END;
|
||||
|
||||
__FLOAT_OBJECT:
|
||||
|
||||
operator_float_object(P1, P2, CO_ADDF);
|
||||
goto __END;
|
||||
|
||||
__OBJECT_CONV:
|
||||
|
||||
operator_object_conv(P1, P2, CO_ADDF);
|
||||
goto __END;
|
||||
|
||||
__CONV_OBJECT:
|
||||
|
||||
operator_conv_object(P1, P2, CO_ADDF);
|
||||
goto __END;
|
||||
|
||||
__OBJECT:
|
||||
|
||||
operator_object(P1, P2, CO_ADDF);
|
||||
goto __END;
|
||||
|
||||
__VARIANT:
|
||||
|
||||
MANAGE_VARIANT_POINTER(_SUBR_add);
|
||||
MANAGE_VARIANT_POINTER_OBJECT(_SUBR_add);
|
||||
goto __ERROR;
|
||||
|
||||
|
||||
__ERROR:
|
||||
|
||||
THROW(E_TYPE, "Number", TYPE_get_name(type));
|
||||
|
Loading…
x
Reference in New Issue
Block a user