#include <math.h>
#include "efuns.h"
#include "list.h"
#include "array.h"
#include "main.h"
#include "stralloc.h"
#include "opcodes.h"
#include "simulate.h"
#include "operators.h"
INLINE int is_eq(const struct svalue *a,const struct svalue *b)
{
if (a->type != b->type) return 0;
switch(a->type)
{
case T_REGEXP:
case T_NUMBER:
case T_LIST:
case T_POINTER:
case T_OBJECT:
case T_MAPPING:
case T_ALIST_PART:
return a->u.number == b->u.number;
case T_STRING:
return a->u.string == b->u.string;
case T_FUNCTION:
return (a->subtype==b->subtype && a->u.ob==b->u.ob);
case T_FLOAT:
return a->u.fnum == b->u.fnum;
default:
fatal("Unknown type %x\n",a->type);
return 0; /* make gcc happy */
}
}
struct processing
{
struct processing *next;
struct vector *v;
};
int low_is_equal(const struct svalue *a,const struct svalue *b,struct processing *p)
{
struct svalue *ap,*bp;
int e;
struct processing curr;
if(a->type!=b->type) return 0;
if(is_eq(a,b)) return 1;
switch(a->type)
{
case T_REGEXP:
case T_NUMBER:
case T_STRING:
case T_OBJECT:
case T_FLOAT:
case T_FUNCTION:
return 0;
case T_POINTER:
case T_ALIST_PART:
case T_MAPPING:
case T_LIST:
if((e=a->u.vec->size)!=b->u.vec->size) return 0;
curr.next=p;
curr.v=a->u.vec;
for(;p;p=p->next) if(p->v==a->u.vec) return 1;
ap=a->u.vec->item;
bp=b->u.vec->item;
for(;e>0;e--) if(!is_equal(ap++,bp++)) return 0;
break;
default:
fatal("Unknown type in is_equal.\n");
}
return 1; /* survived */
}
INLINE int is_equal(const struct svalue *a,const struct svalue *b)
{
return low_is_equal(a,b,0);
}
INLINE int is_gt(const struct svalue *a,const struct svalue *b)
{
if (a->type != b->type)
{
error("Cannot compare different types.\n");
}
switch(a->type)
{
default:
error("Bad argument 1 tp '<'.\n");
case T_NUMBER:
return a->u.number > b->u.number;
case T_STRING:
return my_strcmp(a,b)>0;
case T_FLOAT:
return a->u.fnum > b->u.fnum;
}
}
INLINE int is_lt(const struct svalue *a,const struct svalue *b)
{
if (a->type != b->type)
{
error("Cannot compare different types.\n");
}
switch(a->type)
{
default:
error("Bad arg 1 to '<='.\n");
case T_NUMBER:
return a->u.number < b->u.number;
case T_STRING:
return my_strcmp(a, b)<0;
case T_FLOAT:
return a->u.fnum < b->u.fnum;
}
}
INLINE void f_eq()
{
int i;
i=is_eq(sp-1,sp);
pop_stack();
pop_stack();
push_number(i);
}
INLINE void f_ne()
{
int i;
i=!is_eq(sp-1,sp);
pop_stack();
pop_stack();
push_number(i);
}
INLINE void f_lt()
{
int i;
i=is_lt(sp-1,sp);
pop_stack();
pop_stack();
push_number(i);
}
INLINE void f_gt()
{
int i;
i=is_gt(sp-1,sp);
pop_stack();
pop_stack();
push_number(i);
}
INLINE void f_ge()
{
int i;
i=!is_lt(sp-1,sp);
pop_stack();
pop_stack();
push_number(i);
}
INLINE void f_le()
{
int i;
i=!is_gt(sp-1,sp);
pop_stack();
pop_stack();
push_number(i);
}
#ifdef ADD_CACHE_SIZE
struct add_cache_entry add_cache[ADD_CACHE_SIZE];
void clean_add_cache(void)
{
int e;
for(e=0;e<ADD_CACHE_SIZE;e++)
{
if(add_cache[e].hits) continue;
if(!add_cache[e].a) continue;
free_string(add_cache[e].a);
free_string(add_cache[e].b);
free_string(add_cache[e].sum);
add_cache[e].a=0;
add_cache[e].b=0;
add_cache[e].sum=0;
add_cache[e].hits=0;
}
}
void free_add_cache(void)
{
int e;
for(e=0;e<ADD_CACHE_SIZE;e++)
{
if(!add_cache[e].a) continue;
free_string(add_cache[e].a);
free_string(add_cache[e].b);
free_string(add_cache[e].sum);
add_cache[e].a=0;
add_cache[e].b=0;
add_cache[e].sum=0;
add_cache[e].hits=0;
}
}
#endif
void f_sum(int num_arg,struct svalue *argp)
{
int e,size;
unsigned short types;
for(types=e=0;e<num_arg;e++) types|=1<<argp[e].type;
switch(types)
{
default:
if(num_arg)
if(argp[0].type==T_OBJECT || argp[e].type==T_NUMBER)
bad_arg(1,F_SUM);
error("Incompatible types to sum() or +\n");
return; /* compiler hint */
case BT_STRING:
{
char *buf,*str;
#ifdef ADD_CACHE_SIZE
struct add_cache_entry *h;
#endif
if(num_arg==1) return;
for(size=e=0;e<num_arg;e++) size+=my_strlen(argp+e);
#ifdef ADD_CACHE_SIZE
if(num_arg==2 && size<=ADD_CACHE_MAX_LENGTH)
{
/* oh my, it looks like lisp! */
h=add_cache+( (((unsigned int)strptr(argp))+(((unsigned int)(strptr(argp+1)))>>2)) % ADD_CACHE_SIZE );
if(h->a==strptr(argp) && h->b==strptr(argp+1))
{
h->hits++;
buf=copy_shared_string(h->sum);
}else{
if(h->a)
{
free_string(h->a);
free_string(h->b);
free_string(h->sum);
}
buf=begin_shared_string(size);
MEMCPY(buf,strptr(argp),my_strlen(argp));
MEMCPY(buf+my_strlen(argp),strptr(argp+1),my_strlen(argp+1));
buf=end_shared_string(buf);
h->a=copy_shared_string(strptr(argp));
h->b=copy_shared_string(strptr(argp+1));
h->sum=copy_shared_string(buf);
h->hits=0;
}
}else{
#endif
buf=str=begin_shared_string(size);
for(e=0;e<num_arg;e++)
{
MEMCPY(buf,strptr(argp+e),my_strlen(argp+e));
buf+=my_strlen(argp+e);
}
buf=end_shared_string(str);
#ifdef ADD_CACHE_SIZE
}
#endif
for(e=0;e<num_arg;e++)
{
free_string(strptr(argp+e));
#ifdef WARN
argp[e].type=2000;
#endif
}
sp-=num_arg;
push_shared_string(buf);
break;
}
case BT_STRING | BT_NUMBER:
case BT_STRING | BT_FLOAT:
case BT_STRING | BT_FLOAT | BT_NUMBER:
{
char *buf,*str;
for(size=e=0;e<num_arg;e++)
{
switch(argp[e].type)
{
case T_STRING:
size+=my_strlen(argp+e);
break;
case T_INT:
size+=14;
break;
case T_FLOAT:
size+=22;
break;
}
}
str=buf=malloc(size+1);
size=0;
for(size=e=0;e<num_arg;e++)
{
switch(argp[e].type)
{
case T_STRING:
MEMCPY(buf,strptr(argp+e),my_strlen(argp+e));
buf+=my_strlen(argp+e);
break;
case T_INT:
sprintf(buf,"%d",argp[e].u.number);
buf+=strlen(buf);
break;
case T_FLOAT:
sprintf(buf,"%f",argp[e].u.fnum);
buf+=strlen(buf);
break;
}
}
buf=make_shared_binary_string(str,buf-str);
free(str);
pop_n_elems(num_arg);
push_shared_string(buf);
break;
}
case BT_NUMBER:
for(size=e=0;e<num_arg;e++) size+=argp[e].u.number;
sp-=num_arg-1;
sp->u.number=size;
break;
case BT_FLOAT:
{
float sum;
sum=0.0;
for(e=0;e<num_arg;e++) sum+=argp[e].u.fnum;
sp-=num_arg-1;
sp->u.fnum=sum;
}
break;
case BT_POINTER:
{
struct vector *v;
for(size=e=0;e<num_arg;e++)
{
check_vector_for_destruct(argp[e].u.vec);
size+=argp[e].u.vec->size;
}
if(argp[0].u.vec->ref==1)
{
e=argp[0].u.vec->size;
v=resize_array(argp[0].u.vec,size);
SET_TO_ZERO(argp[0]);
size=e;
e=1;
}else{
v=allocate_array_no_init(size,0);
v->types=0;
e=size=0;
}
for(;e<num_arg;e++)
{
v->types|=argp[e].u.vec->types;
copy_svalues_raw(v->item+size,argp[e].u.vec->item,argp[e].u.vec->size);
size+=argp[e].u.vec->size;
}
pop_n_elems(num_arg);
push_vector(v);
v->ref--;
}
break;
case BT_LIST:
{
struct vector *v,*w;
struct svalue *r;
int d;
for(size=e=0;e<num_arg;e++)
{
check_alist_for_destruct(argp[e].u.vec);
size+=argp[e].u.vec->size;
}
if(num_arg==2)
{
w = do_array_surgery(argp[0].u.vec,argp[1].u.vec,OPER_ADD,T_LIST);
}else{
v=allocate_array_no_init(size,0);
for(size=e=0;e<num_arg;e++)
{
r=argp[e].u.vec->item[0].u.vec->item;
for(d=0;d<argp[e].u.vec->size;d++)
{
assign_svalue_raw(v->item+size,r+d);
size++;
}
}
w=allocate_array_no_init(1,0);
w->item[0].u.vec=v;
w->item[0].type=T_ALIST_PART;
order_alist(w);
}
pop_n_elems(num_arg);
push_list(w);
w->ref--;
}
break;
case BT_MAPPING:
{
struct vector *v,*values,*w;
struct svalue *r,*rr;
int d;
for(size=e=0;e<num_arg;e++)
{
check_alist_for_destruct(argp[e].u.vec);
size+=argp[e].u.vec->size;
}
if(num_arg==2)
{
w = do_array_surgery(argp[0].u.vec,argp[1].u.vec,OPER_ADD,T_MAPPING);
}else{
v=allocate_array_no_init(size,0);
values=allocate_array_no_init(size,0);
for(size=e=0;e<num_arg;e++)
{
r=argp[e].u.vec->item[0].u.vec->item;
rr=argp[e].u.vec->item[1].u.vec->item;
for(d=0;d<argp[e].u.vec->size;d++)
{
assign_svalue_raw(v->item+size,r+d);
assign_svalue_raw(values->item+size,rr+d);
size++;
}
}
w=allocate_array_no_init(2,0);
w->item[0].u.vec=v;
w->item[0].type=T_ALIST_PART;
w->item[1].u.vec=values;
w->item[1].type=T_ALIST_PART;
order_alist(w);
}
pop_n_elems(num_arg);
push_mapping(w);
w->ref--;
}
break;
}
}
INLINE void f_add() { f_sum(2,sp-1); }
void f_subtract()
{
if ((sp-1)->type != sp->type )
error("Subtract on different types.\n");
switch(sp->type)
{
case T_POINTER:
{
extern struct vector *subtract_array
PROT((struct vector *,struct vector*));
struct vector *v;
/* subtract_array already takes care of destructed objects */
check_vector_for_destruct(sp[-1].u.vec);
check_vector_for_destruct(sp[0].u.vec);
if(sp[0].u.vec->types & sp[-1].u.vec->types)
{
v = subtract_array((sp-1)->u.vec, sp->u.vec);
}else{
v=slice_array(sp[-1].u.vec,0,sp[-1].u.vec->size-1);
}
pop_stack();
pop_stack();
push_vector(v);
v->ref--;
return;
}
case T_LIST:
case T_MAPPING:
{
struct vector *v;
int type=sp->type;
v=do_array_surgery((sp-1)->u.vec,sp->u.vec,OPER_SUB,type);
pop_stack();
pop_stack();
push_vector(v);
v->ref--;
sp->type=type;
return;
}
case T_FLOAT:
sp--;
sp->u.fnum=sp[0].u.fnum-sp[1].u.fnum;
return;
case T_NUMBER:
sp--;
sp->u.number = sp[0].u.number - sp[1].u.number;
return;
case T_STRING:
push_svalue(&const_empty_string);
f_replace(3,sp-2);
return;
default:
bad_arg(1,F_SUBTRACT);
}
}
void f_and()
{
if(sp->type == (sp-1)->type &&
IS_TYPE(*sp,BT_POINTER | BT_LIST | BT_MAPPING))
{
struct vector *v;
int type=sp->type;
check_vector_for_destruct(sp[-1].u.vec);
check_vector_for_destruct(sp[0].u.vec);
if(sp[-1].u.vec->types & sp[0].u.vec->types)
{
v=do_array_surgery((sp-1)->u.vec,sp->u.vec,OPER_AND,type);
}else{
v=allocate_array(0);
}
pop_stack();
pop_stack();
push_vector(v);
v->ref--;
sp->type=type;
return;
}
if ((sp-1)->type != T_NUMBER) bad_arg(1,F_AND);
if (sp->type != T_NUMBER) bad_arg(2,F_AND);
sp--;
sp->u.number = sp[0].u.number & sp[1].u.number;
}
void f_or()
{
if(sp->type == (sp-1)->type &&
IS_TYPE(*sp,BT_POINTER | BT_LIST | BT_MAPPING))
{
struct vector *v;
int type=sp->type;
check_vector_for_destruct(sp[-1].u.vec);
check_vector_for_destruct(sp[0].u.vec);
if(sp[-1].u.vec->types & sp[0].u.vec->types)
{
v=do_array_surgery((sp-1)->u.vec,sp->u.vec,OPER_OR,type);
}else{
f_add();
return;
}
pop_stack();
pop_stack();
push_vector(v);
v->ref--;
sp->type=type;
return;
}
if ((sp-1)->type != T_NUMBER) bad_arg(1,F_OR);
if (sp->type != T_NUMBER) bad_arg(2,F_OR);
sp--;
sp->u.number = sp[0].u.number | sp[1].u.number;
}
void f_xor()
{
if(sp->type == (sp-1)->type &&
IS_TYPE(*sp,BT_POINTER | BT_LIST | BT_MAPPING ))
{
struct vector *v;
int type=sp->type;
check_vector_for_destruct(sp[-1].u.vec);
check_vector_for_destruct(sp[0].u.vec);
if(sp[-1].u.vec->types & sp[0].u.vec->types)
{
v=do_array_surgery((sp-1)->u.vec,sp->u.vec,OPER_XOR,type);
}else{
f_add();
return;
}
pop_stack();
pop_stack();
push_vector(v);
v->ref--;
sp->type=type;
return;
}
if ((sp-1)->type != T_NUMBER) bad_arg(1,F_XOR);
if (sp->type != T_NUMBER) bad_arg(2,F_XOR);
sp--;
sp->u.number = sp[0].u.number ^ sp[1].u.number;
}
void f_lsh()
{
if ((sp-1)->type != T_NUMBER) bad_arg(1,F_LSH);
if (sp->type != T_NUMBER) bad_arg(2,F_LSH);
sp--;
sp->u.number = sp[0].u.number << sp[1].u.number;
}
void f_rsh()
{
if ((sp-1)->type != T_NUMBER) bad_arg(1,F_RSH);
if (sp->type != T_NUMBER) bad_arg(2,F_RSH);
sp--;
sp->u.number = sp[0].u.number >> sp[1].u.number;
}
void f_multiply()
{
switch(sp[-1].type)
{
case T_POINTER:
if(sp->type!=T_STRING) bad_arg(2,F_MULTIPLY);
f_implode(2,sp-1);
return;
case T_FLOAT:
if(sp->type!=T_FLOAT) bad_arg(2,F_MULTIPLY);
sp--;
sp->u.fnum=sp[0].u.fnum*sp[1].u.fnum;
return;
case T_INT:
if(sp->type!=T_INT) bad_arg(2,F_MULTIPLY);
sp--;
sp->u.number=sp[0].u.number*sp[1].u.number;
return;
default:
bad_arg(1,F_MULTIPLY);
}
}
void f_divide()
{
if(sp[-1].type!=sp[0].type) bad_arg(2,F_DIVIDE);
switch(sp[0].type)
{
case T_STRING:
f_explode(2,sp-1);
return;
case T_FLOAT:
if(sp->u.fnum==0.0)
error("Division by zero.\n");
sp--;
sp->u.fnum = sp[0].u.fnum / sp[1].u.fnum;
return;
case T_NUMBER:
if (sp->u.number == 0)
error("Division by zero\n");
sp--;
sp->u.number = sp[0].u.number / sp[1].u.number;
return;
default:
bad_arg(1,F_DIVIDE);
}
}
void f_mod()
{
if(sp[-1].type!=sp[0].type) bad_arg(2,F_MOD);
if (sp->type == T_FLOAT)
{
float foo;
if(sp->u.fnum==0.0)
error("Modulo by zero.\n");
foo=(sp-1)->u.fnum / sp->u.fnum;
foo=(sp-1)->u.fnum-sp->u.fnum*floor(foo);
sp--;
sp->u.fnum=foo;
return;
}
if (sp->type != T_NUMBER) bad_arg(1,F_MOD);
if (sp->u.number == 0)
error("Modulus by zero.\n");
sp--;
sp->u.number = sp[0].u.number % sp[1].u.number;
}
void f_not()
{
if(sp->type==T_FLOAT)
{
pop_push_conditional(sp->u.fnum==0.0);
return;
}
pop_push_conditional(sp->type == T_NUMBER && sp->u.number == 0);
}
void f_compl()
{
if (sp->type != T_NUMBER)
error("Bad argument to ~\n");
sp->u.number = ~ sp->u.number;
}
void f_negate()
{
if(sp->type==T_FLOAT)
{
sp->u.fnum=-sp->u.fnum;
return;
}
if (sp->type != T_NUMBER)
error("Bad argument to unary minus\n");
sp->u.number = - sp->u.number;
}
void f_inc()
{
int *i;
if (sp->type != T_LVALUE) error("Bad argument to ++\n");
i=lvalue_to_intp(sp->u.lvalue,"++ of non-integer variable.\n");
if(i)
{
i[0]++;
pop_stack();
push_number(*i);
}else{
sp++;
lvalue_to_svalue_no_free(sp,sp[-1].u.lvalue);
push_number(1);
f_add();
f_assign();
}
}
void f_inc_and_pop() { f_inc(); pop_stack(); }
void f_dec()
{
int *i;
if (sp->type != T_LVALUE) error("Bad argument to --\n");
i=lvalue_to_intp(sp->u.lvalue,"-- of non-integer variable.\n");
if(i)
{
i[0]--;
pop_stack();
push_number(*i);
}else{
sp++;
lvalue_to_svalue_no_free(sp,sp[-1].u.lvalue);
push_number(-1);
f_add();
f_assign();
}
}
void f_dec_and_pop() { f_dec(); pop_stack(); }
void f_post_inc()
{
int *i;
if (sp->type != T_LVALUE) error("Bad argument to ++\n");
i=lvalue_to_intp(sp->u.lvalue,"++ of non-integer variable.\n");
if(i)
{
int t;
t=i[0]++;
pop_stack();
push_number(t);
}else{
sp++;
lvalue_to_svalue_no_free(sp,sp[-1].u.lvalue);
f_swap();
sp++;
assign_svalue_no_free(sp,sp-2);
push_number(1);
f_add();
f_assign_and_pop();
}
}
void f_post_dec()
{
int *i;
if (sp->type != T_LVALUE) error("Bad argument to --\n");
i=lvalue_to_intp(sp->u.lvalue,"-- of non-integer variable.\n");
if(i)
{
int t;
t=i[0]--;
pop_stack();
push_number(t);
}else{
sp++;
lvalue_to_svalue_no_free(sp,sp[-1].u.lvalue);
f_swap();
sp++;
assign_svalue_no_free(sp,sp-2);
push_number(-1);
f_add();
f_assign_and_pop();
}
}
void f_assign()
{
#ifdef DEBUG
if (sp[-1].type != T_LVALUE)
error("Bad argument to F_ASSIGN\n");
#endif
assign(sp[-1].u.lvalue,sp);
free_svalue(sp-1);
sp[-1]=*sp;
SET_TO_ZERO(*sp);
sp--;
}
void f_assign_and_pop()
{
#ifdef DEBUG
if (sp[-1].type != T_LVALUE)
error("Bad argument to F_ASSIGN\n");
#endif
assign(sp[-1].u.lvalue,sp);
pop_stack();
pop_stack();
}
void f_is_equal(int num_arg,struct svalue *argp)
{
int i;
i=is_equal(argp,argp+1);
pop_stack();
pop_stack();
push_number(i);
}