/**************************************************************
 *
 *	CRISP - Custom Reduced Instruction Set Programmers Editor
 *
 *	(C) Paul Fox, 1989
 *
 *    Please See COPYRIGHT notice.
 *
 **************************************************************/
# include	"list.h"

extern ref_t *halt_list;
void	mult_string PROTO((int, int));

void
com_equ(op)
int	op;
{
	com_equ1(op, (SYMBOL *) argv[1].l_sym);
}
void
com_equ1(op, sp)
int	op;
register SYMBOL *sp;
{	long	rvalue = 0;
	OPCODE type = argv[2].l_flags;
	char	buf[32];
	
	if (sp->s_type != F_LIST && type == F_NULL && (sp->s_flag & SF_POLY) == 0) {
		ewprintf("Missing assignment value.");
		return;
		}
	if (sp->s_type != type) {
		if (op == NOOP && sp->s_flag & SF_POLY) {
			switch (sp->s_type) {
			  case F_STR:
				r_dec(sp->s_obj);
				break;
			  case F_LIST:
				r_dec(sp->s_obj);
				if (type == F_NULL) {
					sp->s_obj = r_inc(halt_list);
					acc_assign_null();
					return;
					}
				break;
			  default:
			  	break;
			  }
			sp->s_obj = NULL;
			sp->s_type = type == F_RLIST ? F_LIST : 
				     type == F_LIT ? F_STR : type;
			goto ok_check;
			}
		if (sp->s_type == F_STR) {
			switch (type) {
			  case F_RSTR:
			  case F_LIT:
				break;
			  case F_FLOAT:
			  	sprintf(buf, "%g", argv[2].l_float);
				argv[2].l_str = buf;
				argv[2].l_flags = F_STR;
				type = F_STR;
				break;
			  case F_INT:
			  	sprintf(buf, "%ld", argv[2].l_int);
				argv[2].l_str = buf;
				argv[2].l_flags = F_STR;
				type = F_STR;
				break;
			  default:
			  	goto bad_asgn;
			  }
			goto ok_check;
			}
		if (type == F_NULL)
			;
		else if (sp->s_type == F_INT && type == F_FLOAT) {
			/***********************************************/
			/*   Cast float value to an int.	       */
			/***********************************************/
			rvalue = (long) argv[2].l_float;
			argv[2].l_int = rvalue;
			type = F_INT;
			}
		else if (sp->s_type == F_FLOAT && type == F_INT) {
			/***********************************************/
			/*   Cast int value to a float.		       */
			/***********************************************/
			argv[2].l_float = (double) argv[2].l_int;
			type = F_FLOAT;
			}
		else if (sp->s_type == F_LIST && op == PLUS) {
			append();
			return;
			}
		else if (sp->s_type == F_LIST && type == F_RLIST) {
			/***********************************************/
			/*   nothing to do here.		       */
			/***********************************************/
			}
		else {
bad_asgn:
			ewprintf("Mixed types in assignment: %s", sp->s_name);
			return;
			}
		}
ok_check:
	if (type == F_INT)
		rvalue = argv[2].l_int;
	switch (op) {
	  case NOOP:	
  		switch (type) {
		  case F_INT:
			sp->s_int = rvalue;
			break;
		  case F_FLOAT:
			sym_assign_fval(sp, argv[2].l_float);
			return;
		  case F_RSTR:
			str_rassign(sp, argv[2].l_ref);
			acc_assign_str(argv[2].l_ref->r_ptr, argv[2].l_ref->r_used);
			sp->s_type = F_STR;
			return;
		  case F_LIT:
		  case F_STR: {
			char *cp = get_str(2);
			str_assign(sp, cp);
			acc_assign_ref(sp->s_obj);
			return;
			}
		  case F_NULL:
			if (sp->s_obj) {
				r_dec(sp->s_obj);
				sp->s_obj = r_inc(halt_list);
				}
			acc_assign_null();
			return;
		  case F_RLIST:
		  	ref_assign(sp, argv[2].l_ref);
			return;
		  case F_LIST: {
		  	LIST *lp = argv[2].l_list;
		  	int len_lp = length_of_list_in_bytes(lp);
			list_assign(sp, copy_list(lp, 0), len_lp);
			acc_assign_list(lp, len_lp);
			return;
			}
		  default:
		  	panic("com_equ: what do i do?");
		  }
		break;
	  case PLUS:
	  	switch (type) {
		  case F_LIT:
		  case F_STR:	
		  case F_RSTR:
			sp->s_obj = r_cat(sp->s_obj, get_str(2));
			trace_sym(sp);
			acc_assign_ref(sp->s_obj);
			return;
		  case F_FLOAT:
			sym_assign_fval(sp, sp->s_float + argv[2].l_float);
			return;
		  case F_LIST:
		  	append();
			return;
		  default:
			sp->s_int += rvalue; 
		  }
		break;
	  case MINUS:
	  	if (type == F_FLOAT) {
			sym_assign_fval(sp, sp->s_float - argv[2].l_float);
			return;
			}
		else
	  		sp->s_int -= rvalue; 
		break;
	  case MULTIPLY:	
	  	if (type == F_FLOAT) {
			sym_assign_fval(sp, sp->s_float * argv[2].l_float);
			return;
			}
		else
	  		sp->s_int *= rvalue; 
		break;
	  case DIVIDE:	
	  	if (type == F_FLOAT) {
			if (argv[2].l_float != 0.0)
				sym_assign_fval(sp, sp->s_float / argv[2].l_float);
			return;
			}
		else {
			if (rvalue == 0)
				rvalue = 1;
			sp->s_int /= rvalue; 
			}
		break;
	  case MODULO:	
		if (rvalue == 0)
			rvalue = 1;
		sp->s_int %= rvalue; 
		break;
	  case BAND:	sp->s_int &= rvalue; break;
	  case BOR:	sp->s_int |= rvalue; break;
	  case BXOR:	sp->s_int ^= rvalue; break;
	  case LSHIFT:
	  	sp->s_int <<= rvalue;
	  	break;
	  case RSHIFT:
	  	sp->s_int >>= rvalue;
	  	break;
	  }
	acc_assign_int(sp->s_int);
	trace_sym(sp);
}
void
minusminus()
{
	if (argv[1].l_sym->s_type == F_INT)
		acc_assign_int(--argv[1].l_sym->s_int);
	else
		acc_assign_float(--argv[1].l_sym->s_float);
}
void
plusplus()
{
	if (argv[1].l_sym->s_type == F_INT)
		acc_assign_int(++argv[1].l_sym->s_int);
	else
		acc_assign_float(++argv[1].l_sym->s_float);
}
void
post_minusminus()
{
	if (argv[1].l_sym->s_type == F_INT)
		acc_assign_int(argv[1].l_sym->s_int--);
	else
		acc_assign_float(argv[1].l_sym->s_float--);
}
void
post_plusplus()
{
	if (argv[1].l_sym->s_type == F_INT)
		acc_assign_int(argv[1].l_sym->s_int++);
	else
		acc_assign_float(argv[1].l_sym->s_float++);
}
void
lnot()
{	
	acc_assign_int((long) !argv[1].l_int);
}
void
com_op(op)
int	op;
{	long	op1 = argv[1].l_int,
		op2 = argv[2].l_int;
	OPCODE	type = 0;
	extern char *command_name;
	long	val = 0;
	char	buf[64];

	/***********************************************/
	/*   Perform basic type co-oercion here.       */
	/***********************************************/
	if (op == BNOT)
		argv[2].l_flags = F_INT;
	switch (argv[1].l_flags) {
	  case F_INT:
	  	switch (argv[2].l_flags) {
		  case F_INT:
			type = F_INT;
			break;
		  case F_FLOAT:
			argv[1].l_float = (double) argv[1].l_int;
			type = F_FLOAT;
			break;
		  case F_LIT:
		  case F_STR:
		  case F_RSTR:
		  	if (op != MULTIPLY) {
			  	sprintf(buf, "%ld", argv[1].l_int);
				argv[1].l_str = buf;
				argv[1].l_flags = F_STR;
				}
		  	type = F_STR;
		  	break;
		  case F_RLIST:
		  case F_LIST: {
		  	LISTV	t;
		  	type = F_LIST;
			t = argv[1];
			argv[1] = argv[2];
			argv[2] = t;
			break;
			}
		  default:
		  	goto DEFAULT;
		  }
		break;
	  case F_FLOAT:
	  	switch (argv[2].l_flags) {
		  case F_INT:
			argv[2].l_float = (double) argv[2].l_int;
			type = F_FLOAT;
			break;
		  case F_FLOAT:
			type = F_FLOAT;
		  	break;
		  case F_LIT:
		  case F_STR:
		  case F_RSTR:
		  	sprintf(buf, "%g", argv[1].l_float);
			argv[1].l_str = buf;
			argv[1].l_flags = F_STR;
		  	type = F_STR;
		  	break;
		  case F_RLIST:
		  case F_LIST: {
		  	LISTV	t;
		  	type = F_LIST;
			t = argv[1];
			argv[1] = argv[2];
			argv[2] = t;
			break;
			}
		  default:
		  	goto DEFAULT;
		  }
		break;
	  case F_RLIST:
	  case F_LIST:
	  	if (op != PLUS)
			goto DEFAULT;
		type = F_LIST;
		break;
	  case F_LIT:
	  case F_STR:
	  case F_RSTR:
	  	switch (argv[2].l_flags) {
		  case F_LIT:
		  case F_STR:
		  case F_RSTR:
			type = F_STR;
			break;
		  case F_INT:
		  	if (op != MULTIPLY) {
			  	sprintf(buf, "%ld", argv[2].l_int);
				argv[2].l_str = buf;
				argv[2].l_flags = F_STR;
				}
		  	type = F_STR;
		  	break;
		  case F_FLOAT:
		  	sprintf(buf, "%g", argv[2].l_float);
			argv[2].l_str = buf;
			argv[2].l_flags = F_STR;
		  	type = F_STR;
		  	break;
		  case F_RLIST:
		  case F_LIST: {
		  	LISTV	t;
		  	type = F_LIST;
			t = argv[1];
			argv[1] = argv[2];
			argv[2] = t;
			break;
			}
		  default:
		  	goto DEFAULT;
		  }
		break;
	  default:
	  DEFAULT:
		if (op != BNOT) {
			ewprintf("%s: invalid parameters.", command_name);
			return;
			}
		}

	switch (op) {
	  case	PLUS:
	  	switch (type) {
		  case F_INT:
			val = op1 + op2;
			break;
		  case F_FLOAT:
			acc_assign_float(argv[1].l_float + argv[2].l_float);
		  	return;
		  case F_LIST: {
		  	LIST *new_list;
			int	newlen;
			if (argv[1].l_flags == F_LIST)
				new_list = append_list(argv[1].l_list, -1,
					&argv[2], &newlen);
			else
				new_list = append_list((LIST *) argv[1].l_ref->r_ptr,
					argv[1].l_ref->r_used,
					&argv[2], &newlen);
			acc_donate_list(new_list, newlen);
		  	return;
			}
		  default: {
			char *str1 = get_str(1);
			char *str2 = get_str(2);
			int len_1 = get_len(1);
			int len_2 = get_len(2);
			acc_assign_str(str1, len_1 + len_2);
			str1 = acc_get_sval();
			memcpy(str1 + len_1, str2, len_2);
			return;
			}
		  }
		break;
	  case	MINUS:
	  	switch (type) {
		  case F_INT:
			val = op1 - op2; 
			break;
		  case F_FLOAT:
			acc_assign_float(argv[1].l_float - argv[2].l_float);
		  	return;
		  default:
		  	break;
		  }
		break;
	  case	MULTIPLY:	
	  	switch (type) {
		  case F_INT:
			val = op1 * op2; 
			break;
		  case F_FLOAT:
			acc_assign_float(argv[1].l_float * argv[2].l_float);
		  	return;
		  case F_STR:
		  	if (argv[1].l_flags == F_INT) {
				mult_string(1, 2);
				return;
				}
			if (argv[2].l_flags == F_INT) {
				mult_string(2, 1);
				return;
				}
			ewprintf("*: attempt to multiply two strings.");
		  	break;
		  default:
		  	break;
		  }
		break;
	  case	DIVIDE:	
	  	switch (type) {
		  case F_INT:
		  	if (op2 == 0)
				val = op1;
			else
				val = op1 / op2; 
			break;
		  case F_FLOAT:
		  	if (argv[2].l_float)
				acc_assign_float(argv[1].l_float / argv[2].l_float);
		  	return;
		  default:
		  	break;
		  }
		break;
	  case	MODULO:	
		val = op1 % (op2 ? op2 : 1); 
		break;
	  case	EQ:
	  	switch (type) {
		  case F_INT:
			val = op1 == op2; 
			break;
		  case F_FLOAT:
			val = argv[1].l_float == argv[2].l_float;
			break;
		  default:
			val = strcmp(get_str(1), get_str(2)) == 0;
		  }
		break;
	  case	NE:	
	  	switch (type) {
		  case F_INT:
			val = op1 != op2; 
			break;
		  case F_FLOAT:
			val = argv[1].l_float != argv[2].l_float;
			break;
		  default:
			val = strcmp(get_str(1), get_str(2)) != 0;
		  }
		break;
	  case	LT:	
	  	switch (type) {
		  case F_INT:
			val = op1 < op2; 
			break;
		  case F_FLOAT:
			val = argv[1].l_float < argv[2].l_float;
			break;
		  default:
			val = strcmp(get_str(1), get_str(2)) < 0;
		  }
		break;
	  case	LE:	
	  	switch (type) {
		  case F_INT:
			val = op1 <= op2; 
			break;
		  case F_FLOAT:
			val = argv[1].l_float <= argv[2].l_float;
			break;
		  default:
			val = strcmp(get_str(1), get_str(2)) <= 0;
		  }
		break;
	  case	GT:	
	  	switch (type) {
		  case F_INT:
			val = op1 > op2; 
			break;
		  case F_FLOAT:
			val = argv[1].l_float > argv[2].l_float;
			break;
		  default:
			val = strcmp(get_str(1), get_str(2)) > 0;
		  }
		break;
	  case	GE:
	  	switch (type) {
		  case F_INT:
			val = op1 >= op2; 
			break;
		  case F_FLOAT:
			val = argv[1].l_float >= argv[2].l_float;
			break;
		  default:
			val = strcmp(get_str(1), get_str(2)) >= 0;
		  }
		break;
	  case	ABOVE:
	  	switch (type) {
		  case F_INT:
			val = (unsigned long) op1 > (unsigned long) op2; 
			break;
		  case F_FLOAT:
			val = argv[1].l_float > argv[2].l_float;
			break;
		  default:
			val = strcmp(get_str(1), get_str(2)) > 0;
		  }
		break;
	  case	ABOVE_EQ:
	  	switch (type) {
		  case F_INT:
			val = (unsigned long) op1 >= (unsigned long) op2; 
			break;
		  case F_FLOAT:
			val = argv[1].l_float >= argv[2].l_float;
			break;
		  default:
			val = strcmp(get_str(1), get_str(2)) >= 0;
		  }
		break;
	  case	BELOW:
	  	switch (type) {
		  case F_INT:
			val = (unsigned long) op1 < (unsigned long) op2; 
			break;
		  case F_FLOAT:
			val = argv[1].l_float < argv[2].l_float;
			break;
		  default:
			val = strcmp(get_str(1), get_str(2)) < 0;
		  }
		break;
	  case	BELOW_EQ:
	  	switch (type) {
		  case F_INT:
			val = (unsigned long) op1 <= (unsigned long) op2; 
			break;
		  case F_FLOAT:
			val = argv[1].l_float <= argv[2].l_float;
			break;
		  default:
			val = strcmp(get_str(1), get_str(2)) <= 0;
		  }
		break;
	  case	BAND:
		val = argv[1].l_int & argv[2].l_int;
		break;
	  case	BOR:
		val = argv[1].l_int | argv[2].l_int;
		break;
	  case	BXOR:
		val = argv[1].l_int ^ argv[2].l_int;
		break;
	  case	BNOT:
		val = ~argv[1].l_int;
		break;
	  case	LSHIFT:
		val = argv[1].l_int << argv[2].l_int;
		break;
	  case	RSHIFT:
		val = argv[1].l_int >> argv[2].l_int;
		break;
	  }
	acc_assign_int(val);
}
/*******************************************************************/
/*   Return  length  of  string  argument  or  length  of longest  */
/*   string in a list.						   */
/*******************************************************************/
void
do_strlen()
{	int	step = 1;
	int	i;
	int	longest_length = 0;
	int	len;
	LIST	*lp;

	if (argv[1].l_flags != F_LIST && argv[1].l_flags != F_RLIST) {
		acc_assign_int((long) get_len(1));
		return;
		}
	/***********************************************/
	/*   See if the step argument was specified.   */
	/***********************************************/
	if (argv[2].l_flags == F_INT)
		step = (int) argv[2].l_int;
	if (step <= 0)
		step = 1;
		
	for (lp = get_list(1); lp && *lp != F_HALT; ) {
		switch (*lp) {
		  case F_STR:
		  case F_LIT:
		  	len = strlen((char *) LGET32(lp));
			break;
		  case F_ID:
			len = strlen(builtin[LGET16(lp)].name);
			break;
		  case F_RSTR: {
			ref_t *rp = (ref_t *) LGET32(lp);
			len = rp->r_used;
			break;
			}
		  default:
			len = 0;
		  	break;
		  }
		if (len > longest_length)
			longest_length = len;
		for (i = 0; i < step && lp; i++) {
			lp = next_atom(lp);
			}
		}
	acc_assign_int((long) longest_length);
}
void
do_atoi()
{	unsigned char *cp = (unsigned char *) get_str(1);
	if (argv[2].l_flags == F_NULL || argv[2].l_int)
		acc_assign_int((long) atoi((char *) cp));
	else
		acc_assign_int((long) *cp);
}
void
string_count()
{	register char	*str1 = get_str(1);
	register char	*str2 = get_str(2);
	char *strchr();
	long	val = 0;

	while (*str1) {
		if (strchr(str2, *str1++))
			val++;
		}
	acc_assign_int(val);
}
void
do_index()
{
	extern	char	*instr();
	char	*cp;
	char	*cp2 = get_str(2);
	char	*str1 = get_str(1);
	long	val = 0;

	if (*cp2 == NULL)
		val = strlen(str1) + 1;
	else if (cp = instr(str1, cp2))
		val = cp - str1 + 1;
	acc_assign_int(val);
	
}
void
do_rindex()
{
	char	*cp;
	char	*str1 = get_str(1);
	char	*str2 = get_str(2);
	int	len = get_len(2);
	long	val = 0;

	for (cp = str1+strlen(str1)-1; cp >= str1; cp--)
		if (strncmp(cp, str2, len) == 0) {
			val = cp - str1 + 1;
			break;
			}
	acc_assign_int(val);
}
void
substr()
{	char *str1 = get_str(1);
	int	len1 = get_len(1);
	int	offset = argv[2].l_flags == F_INT ? argv[2].l_int - 1 : 0;
	char	*cp;

	if (offset < 0)
		offset = 0;
	else if (offset > len1)
		offset = len1;
	cp = str1 + offset;
	len1 = argv[3].l_flags == F_NULL ? strlen(cp) : (int) argv[3].l_int;
	if (len1 < 0)
		len1 = 0;
	acc_assign_str(cp, argv[2].l_int ? len1 : 0);
}
void
compress()
{	register char	*cp;
	register char	*cp1;
	char	*start;
	int	trim_flag = argv[2].l_flags == F_INT;
	
	acc_assign_str(get_str(1), -1);
	start = acc_get_sval();
	cp = start;
	if (trim_flag)
		while (isspace(*cp))
			strcpy(cp, cp+1);
	while (*cp) {
		for (cp1 = cp; isspace(*cp1) || *cp1 == '\n'; )
			cp1++;
		if (*cp1 != *cp) {
			strcpy(cp+1, cp1);
			*cp = ' ';
			}
		cp++;
		}
	if (!trim_flag)
		return;
	while (cp > start && isspace(cp[-1]))
		*--cp = NULL;
}
void
trim()
{	register char *cp, *start;
	int len = get_len(1);
	char	*trim_str = " \t\n";

	acc_assign_str(get_str(1), len);
	if (argv[2].l_flags != F_NULL)
		trim_str = get_str(2);
	start = acc_get_sval();
	cp = start + len - 1;
	while (cp >= start && strchr(trim_str, *cp))
		*cp-- = NULL;
}
void
ltrim()
{	register char *cp = get_str(1);
	char	*trim_str = " \t\n";

	if (argv[2].l_flags != F_NULL)
		trim_str = get_str(2);
	while (*cp && strchr(trim_str, *cp))
		cp++;
	acc_assign_str(cp, -1);
}
void
andand()
{	LISTV	result;

	acc_assign_int(0L);
	if (argv[1].l_int == 0)
		return;
	if (eval(argv[2].l_list, &result) != F_INT)
		return;
	acc_assign_int(result.l_int);
}
void
oror()
{	LISTV	result;

	acc_assign_int(1L);
	if (argv[1].l_int)
		return;
	if (eval(argv[2].l_list, &result) != F_INT)
		return;
	acc_assign_int(result.l_int);
}
/**********************************************************************/
/*   Function to replicate a string 'n' times.			      */
/**********************************************************************/
void
mult_string(n, m)
int	n;
int	m;
{	char	*cp = get_str(m);
	char	*str;
	int	i, len;
	int	j = argv[n].l_int;

	if (j <= 0) {
		acc_assign_str("", 1);
		return;
		}
	/***********************************************/
	/*   Allocate  memory  --  return null string  */
	/*   if too big.			       */
	/***********************************************/
	len = strlen(cp);
	str = chk_alloc(j * len + 1);
	if (str == NULL) {	
		acc_assign_str("", 1);
		return;
		}
	for (i = 0; j-- > 0; i += len) {
		memcpy(str + i, cp, len);
		}
	str[i] = NULL;
	acc_assign_str(str, i);
}
