// joycub - an interpeter for the programming language Joy
// 
// (c) 2004-2006 Sergey Zubkov 
// $Id: builtins_predicates.cc,v 1.1 2006/02/27 18:44:01 cubbi Exp $
#include <vector>
#include "program.h"
#include "builtins.h"
#include "exceptions.h"
#include "float.h"
#include "set.h"
#include "char.h"
#include "string.h"
#include "bool.h"
#include "op.h"
#include "symtab.h"

static char RCSID[] ="$Id: builtins_predicates.cc,v 1.1 2006/02/27 18:44:01 cubbi Exp $";

// logical      :  X  ->  B
// Tests whether X is a logical.
void op_logical(JC_Stack & STACK) {
	STACK.assert_depth(1,"logical");
	JC_objref* ref_x = STACK.pop();
	bool result=ref_x->get_type() == BOOL;
	STACK.push_back(new JC_objref(new JC_bool(result)));
}
//integer      :  X  ->  B
//Tests whether X is an integer.
void op_integer(JC_Stack & STACK) {
	STACK.assert_depth(1,"integer");
	JC_objref* ref_x = STACK.pop();
	bool result=ref_x->get_type() == INT;
	STACK.push_back(new JC_objref(new JC_bool(result)));
}
//char      :  X  ->  B
//Tests whether X is a character.
void op_char(JC_Stack & STACK) {
	STACK.assert_depth(1,"char");
	JC_objref* ref_x = STACK.pop();
	bool result=ref_x->get_type() == CHAR;
	STACK.push_back(new JC_objref(new JC_bool(result)));
}
//set      :  X  ->  B
//Tests whether X is a set.
void op_set(JC_Stack & STACK) {
	STACK.assert_depth(1,"set");
	JC_objref* ref_x = STACK.pop();
	bool result=ref_x->get_type() == SET;
	STACK.push_back(new JC_objref(new JC_bool(result)));
}
//string      :  X  ->  B
//Tests whether X is a string.
void op_string(JC_Stack & STACK) {
	STACK.assert_depth(1,"string");
	JC_objref* ref_x = STACK.pop();
	bool result=ref_x->get_type() == STRING;
	STACK.push_back(new JC_objref(new JC_bool(result)));
}
//list      :  X  ->  B
//Tests whether X is a list.
void op_list(JC_Stack & STACK) {
	STACK.assert_depth(1,"list");
	JC_objref* ref_x = STACK.pop();
	bool result=ref_x->get_type() == LIST;
	STACK.push_back(new JC_objref(new JC_bool(result)));
}
//leaf      :  X  ->  B
//Tests whether X is not a list.
void op_leaf(JC_Stack & STACK) {
	STACK.assert_depth(1,"leaf");
	JC_objref* ref_x = STACK.pop();
	bool result=ref_x->get_type() != LIST;
	STACK.push_back(new JC_objref(new JC_bool(result)));
}
//user      :  X  ->  B
//Tests whether X is a user-defined symbol.
extern JC_symtab * SYMTAB;
void op_user(JC_Stack & STACK) {
	STACK.assert_depth(1,"user");
	JC_objref* ref_x = STACK.pop();
	bool result;
	if (ref_x->get_type() != OPERATOR) result=false;
	else result=SYMTAB->is_userdef(ref_x->get_opnum());
	STACK.push_back(new JC_objref(new JC_bool(result)));
}
//float      :  R  ->  B
//Tests whether R is a float.
void op_float(JC_Stack & STACK) {
	STACK.assert_depth(1,"float");
	JC_objref* ref_x = STACK.pop();
	bool result=ref_x->get_type() == FLOAT;
	STACK.push_back(new JC_objref(new JC_bool(result)));
}
//file      :  F  ->  B
//Tests whether F is a file.
void op_file(JC_Stack & STACK) {
	STACK.assert_depth(1,"file");
	JC_objref* ref_x = STACK.pop();
	bool result=ref_x->get_type() == _FILE_;
	STACK.push_back(new JC_objref(new JC_bool(result)));
}

//>=      :  X Y  ->  B
//Either both X and Y are numeric or both are strings or symbols.
//Tests whether X greater than or equal to Y.  Also supports float.
void op_ge(JC_Stack & STACK) {
	STACK.assert_depth(2,">=");
	JC_objref* ref_y = STACK.pop();
	JC_objref* ref_x = STACK.pop();
	if(ref_x->get_type()==INT && ref_y->get_type()==INT)  { // result is INT
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->to_int() >= ref_y->to_int())));
	} else if(ref_x->get_type()==STRING && ref_y->get_type()==STRING) {
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->key() >= ref_y->key())));
	} else if(ref_x->get_type()==CHAR && ref_y->get_type()==CHAR) {
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->to_int() >= ref_y->to_int())));
	} else { // result is FLOAT or throw type error
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->to_double() >= ref_y->to_double())));
	}
	delete ref_x;
	delete ref_y;
}
//>      :  X Y  ->  B
//Either both X and Y are numeric or both are strings or symbols.
//Tests whether X greater than Y.  Also supports float.
void op_gt(JC_Stack & STACK) {
	STACK.assert_depth(2,">");
	int i = STACK.size()-1; // last element's index
	JC_objref* ref_x = STACK[i-1];
	JC_objref* ref_y = STACK[i];
	STACK.pop_back();
	STACK.pop_back();
	if(ref_x->get_type()==INT && ref_y->get_type()==INT)  { // result is INT
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->to_int() > ref_y->to_int())));
	} else if(ref_x->get_type()==STRING && ref_y->get_type()==STRING) {
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->key() > ref_y->key())));
	} else if(ref_x->get_type()==CHAR && ref_y->get_type()==CHAR) {
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->to_int() > ref_y->to_int())));
	} else { // result is FLOAT or throw type error
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->to_double() > ref_y->to_double())));
	}
	delete ref_x;
	delete ref_y;
}
//<=      :  X Y  ->  B
//Either both X and Y are numeric or both are strings or symbols.
//Tests whether X less than or equal to Y.  Also supports float.
void op_le(JC_Stack & STACK) {
	STACK.assert_depth(2,"<=");
	int i = STACK.size()-1; // last element's index
	JC_objref* ref_x = STACK[i-1];
	JC_objref* ref_y = STACK[i];
	STACK.pop_back();
	STACK.pop_back();
	if(ref_x->get_type()==INT && ref_y->get_type()==INT)  { // result is INT
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->to_int() <= ref_y->to_int())));
	} else if(ref_x->get_type()==STRING && ref_y->get_type()==STRING) {
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->key() <= ref_y->key())));
	} else if(ref_x->get_type()==CHAR && ref_y->get_type()==CHAR) {
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->to_int() <= ref_y->to_int())));
	} else { // result is FLOAT or throw type error
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->to_double() <= ref_y->to_double())));
	}
	delete ref_x;
	delete ref_y;
}
//<      :  X Y  ->  B
//Either both X and Y are numeric or both are strings or symbols.
//Tests whether X less than Y.  Also supports float.
void op_lt(JC_Stack & STACK) {
	STACK.assert_depth(2,"<");
	int i = STACK.size()-1; // last element's index
	JC_objref* ref_x = STACK[i-1];
	JC_objref* ref_y = STACK[i];
	STACK.pop_back();
	STACK.pop_back();
	if(ref_x->get_type()==INT && ref_y->get_type()==INT)  { // result is INT
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->to_int() < ref_y->to_int())));
	} else if(ref_x->get_type()==STRING && ref_y->get_type()==STRING) {
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->key() < ref_y->key())));
	} else if(ref_x->get_type()==CHAR && ref_y->get_type()==CHAR) {
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->to_int() < ref_y->to_int())));
	} else { // result is FLOAT or throw type error
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->to_double() < ref_y->to_double())));
	}
	delete ref_x;
	delete ref_y;
}
//!=      :  X Y  ->  B
//Either both X and Y are numeric or both are strings or symbols.
//Tests whether X not equal to Y.  Also supports float.
void op_ne(JC_Stack & STACK) {
	STACK.assert_depth(2,"!=");
	int i = STACK.size()-1; // last element's index
	JC_objref* ref_x = STACK[i-1];
	JC_objref* ref_y = STACK[i];
	STACK.pop_back();
	STACK.pop_back();
	if(ref_x->get_type()==INT && ref_y->get_type()==INT)  { // result is INT
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->to_int() != ref_y->to_int())));
	} else if(ref_x->get_type()==STRING && ref_y->get_type()==STRING) {
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->key() != ref_y->key())));
	} else if(ref_x->get_type()==CHAR && ref_y->get_type()==CHAR) {
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->to_int() != ref_y->to_int())));
	} else { // result is FLOAT or throw type error
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->to_double() != ref_y->to_double())));
	}
	delete ref_x;
	delete ref_y;
}
//=      :  X Y  ->  B
//Either both X and Y are numeric or both are strings or symbols.
//Tests whether X equal to Y.  Also supports float.
void op_eq(JC_Stack & STACK) {
	STACK.assert_depth(2,"=");
	int i = STACK.size()-1; // last element's index
	JC_objref* ref_x = STACK[i-1];
	JC_objref* ref_y = STACK[i];
	STACK.pop_back();
	STACK.pop_back();
	if(ref_x->get_type()==INT && ref_y->get_type()==INT)  { // result is INT
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->to_int() == ref_y->to_int())));
	} else if(ref_x->get_type()==STRING && ref_y->get_type()==STRING) {
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->key() == ref_y->key())));
	} else if(ref_x->get_type()==CHAR && ref_y->get_type()==CHAR) {
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->to_int() == ref_y->to_int())));
	} else if(ref_x->get_type()==OPERATOR && ref_y->get_type()==OPERATOR) {
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->key() == ref_y->key())));
	} else { // result is FLOAT or throw type error
		STACK.push_back(new JC_objref(new JC_bool(
			ref_x->to_double() == ref_y->to_double())));
	}
	delete ref_x;
	delete ref_y;
}


//null      :  X  ->  B
//Tests for empty aggregate X or zero numeric.
void op_null(JC_Stack & STACK) {
	STACK.assert_depth(1,"null");
	JC_objref* ref_m = STACK.pop();
	bool result=false;
	switch(ref_m->get_type()) {
		case CHAR:
		case INT: result = (ref_m->to_int()==0);
				  break;
		case STRING:
		case SET:
		case LIST: result = ref_m->to_agg().size() == 0;
			  break;
		default:  throw runtime_error("Different type needed for small.");
				  break;
	}
	delete ref_m;
	STACK.push_back(new JC_objref(new JC_bool(result)));
}


// PREDICATE
// small      :  X  ->  B
// Tests whether aggregate X has 0 or 1 members, or numeric 0 or 1.
void op_small(JC_Stack & STACK) {
	STACK.assert_depth(1,"small");
	JC_objref* ref_m = STACK.pop();
	bool result=false;
	switch(ref_m->get_type()) {
		case INT: result = (ref_m->to_int()<=1);
				  break;
		case SET: result = ref_m->to_agg().size() <= 1;
				  break;
		case LIST: result = ref_m->to_agg().size() <= 1;
				  break;
		case STRING: result = (ref_m->key().size() <=1);
				  break;
		default:  throw runtime_error("Different type needed for small.");
				  break;
	}
	delete ref_m;
	STACK.push_back(new JC_objref(new JC_bool(result)));
}

// equal      :  T U  ->  B
// (Recursively) tests whether trees T and U are identical.
void op_equal(JC_Stack & STACK) {
	STACK.assert_depth(2,"equal");
	JC_objref* ref_u = STACK.pop();
	JC_objref* ref_t = STACK.pop();
	STACK.push(new JC_bool(ref_u->key() == ref_t->key()));
	delete ref_u; delete ref_t;
}
