#include "expression.h" #include #include #include "symbol_table.h" extern int line_count; char type_name_string[][30] = { "", "integer", "double", "string", "animation block" }; char opname[][10] = { "","+","-","/","*","%", "&&","||","==",">",">=","<", "<=","!=","-","!","", "sin","cos","tan","asin", "acos","atan","sqrt","abs", "floor","random","", "touches","near" }; #define OP_ADD 0x01 #define OP_SUB 0x02 #define OP_DIV 0x03 #define OP_MUL 0x04 #define OP_MOD 0x05 #define OP_AND 0x06 #define OP_OR 0x07 #define OP_EQU 0x08 #define OP_GR 0x09 #define OP_GEQ 0x0A #define OP_LE 0x0B #define OP_LEQ 0x0C #define OP_NEQ 0x0D #define OP_NEG 0x0E #define OP_NOT 0x0F #define MATH_SIN 0x11 #define MATH_COS 0x12 #define MATH_TAN 0x13 #define MATH_ASIN 0x14 #define MATH_ACOS 0x15 #define MATH_ATAN 0x16 #define MATH_SQRT 0x17 #define MATH_ABS 0x18 #define MATH_FLOOR 0x19 #define MATH_RANDOM 0x1A #define OP_TOUCHES 0x21 #define OP_NEAR 0x22 #define MAX(x,y) ((x)>(y)?(x):(y)) #define EXISTS(t1,t2,type) ((t1)&&(t2)? (type) : 0) #define EXIST(t1,type) ((t1)? (type) : 0) int Expression::type_check(int op, int ltype, int rtype) { line = line_count; int rbad = 1; int rv = 0; switch (op) { case OP_GR: case OP_LE: case OP_LEQ: case OP_GEQ: case OP_EQU: case OP_NEQ: // Logical operators on int,double,sring rv= EXISTS(ltype,rtype,TYPE_INT); break; case OP_NOT: // logical ! rv= EXIST(ltype, TYPE_INT); break; case OP_AND: case OP_OR: // con/disjunction. rv= (ltype<=TYPE_DOUBLE && rtype <= TYPE_DOUBLE)? TYPE_INT : 0; if (!rv && ltype>TYPE_DOUBLE) rbad = 0; break; case OP_ADD: rv= (ltype<=TYPE_STRING && rtype <= TYPE_STRING)? MAX(ltype,rtype) : 0; break; case OP_SUB: case OP_MUL: case OP_DIV: rv= (ltype<=TYPE_DOUBLE && rtype <= TYPE_DOUBLE)? MAX(ltype,rtype) : 0; if (!rv && ltype>TYPE_DOUBLE) rbad = 0; break; case OP_MOD: rv= (ltype==TYPE_INT && rtype == TYPE_INT)? TYPE_INT : 0; break; case OP_NEG: //unary - rv= ltype<=TYPE_DOUBLE? ltype : 0; break; case MATH_SIN: case MATH_COS: case MATH_TAN: case MATH_ACOS: case MATH_ASIN: case MATH_ATAN: case MATH_SQRT: rv= ltype<=TYPE_DOUBLE? TYPE_DOUBLE : 0; break; case MATH_ABS: rv= ltype<=TYPE_DOUBLE? ltype : 0; break; case MATH_FLOOR: case MATH_RANDOM: rv= ltype<=TYPE_DOUBLE? TYPE_INT : 0; break; } if (rv) return rv; if (rbad) Error::error(Error::INVALID_RIGHT_OPERAND_TYPE, string(opname[op])); else Error::error(Error::INVALID_LEFT_OPERAND_TYPE, string(opname[op])); return 0; // bad type } double Expression::int_to_double(long in) { return in; } string *Expression::int_to_string(long in) { char buffer[90]; sprintf(buffer, "%ld", in); return new string(buffer); } string *Expression::double_to_string(double in) { char buffer[90]; sprintf(buffer, "%lg", in); return new string(buffer); } // TODO: Expressions do type check at construct. /* double Expression::as_float(void) { // Unimplemented; assert(0); } string *Expression::as_string(void) { assert(0); } long Expression::as_int(void) { assert(0); } */ long MathExpr::as_int(void) { Gpl_union result; switch (op) { case MATH_ABS: switch (my_type) { case TYPE_INT: result.t_int = abs(operand->as_int()); break; /*case TYPE_DOUBLE: result.t_float = abs(operand->as_float()); break;*/ default: assert(0); } break; case MATH_FLOOR: result.t_int = (long)floor(operand->as_float()); break; case MATH_RANDOM: result.t_int = rand()%((int)floor(operand->as_float())); break; default: assert(0); //typeerror; no other int-returning math funcs } return result.t_int; } string *MathExpr::as_string(void) { return double_to_string(as_float()); // thank you life's small mercies } double MathExpr::as_float(void) { Gpl_union result; switch (op) { case MATH_SIN: result.t_float = sin(radians(operand->as_float())); break; case MATH_TAN: result.t_float = tan(radians(operand->as_float())); break; case MATH_COS: result.t_float = cos(radians(operand->as_float())); break; case MATH_ASIN: result.t_float = degrees(asin(operand->as_float())); break; case MATH_ACOS: result.t_float = degrees(acos(operand->as_float())); break; case MATH_ATAN: result.t_float = degrees(atan(operand->as_float())); break; case MATH_FLOOR: result.t_float = floor(operand->as_float()); break; case MATH_RANDOM: result.t_float = rand()%((int)floor(operand->as_float())); //FIXME break; case MATH_SQRT: result.t_float = sqrt(operand->as_float()); break; case MATH_ABS: result.t_float = fabs(operand->as_float()); break; } return result.t_float; } long UnaryExpr::as_int(void) { switch (op) { case OP_NEG: return -operand->as_int(); case OP_NOT: return !operand->as_int(); } return 0; } double UnaryExpr::as_float(void) { switch (op) { case OP_NEG: return -operand->as_float(); case OP_NOT: return !operand->as_int(); } return 0.0; } string *UnaryExpr::as_string(void) { // FIXME return NULL; } long BinaryExpr::as_int(void) { // make type error if this isn't an int expression /* New algorithm for this - 1. convert all arguments to the final type (mytype) 2. execute the operation with homogeneous arguments. 3. make a cast from mytype to the desired type if possible. */ Gpl_union left, right, result; int tmptype; int rtype; switch (tmptype=MAX(left_operand->type(), right_operand->type())) { case TYPE_INT: left.t_int = left_operand->as_int(); right.t_int = right_operand->as_int(); break; case TYPE_DOUBLE: left.t_float = left_operand->as_float(); right.t_float = right_operand->as_float(); break; case TYPE_STRING: left.t_string = left_operand->as_string(); right.t_string = right_operand->as_string(); break; } // I really wish there were a safe way to ignore the type of the union for this // but i guess it needs that info to compile it logically // did i mention how much i hate static typing by hand? i wish it were ML or // else python switch (op) { case OP_ADD: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int + right.t_int; break; /* Since this is as_int anything else is a type error */ default: assert(0); } rtype = tmptype; break; case OP_SUB: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int - right.t_int; break; /* Since this is as_int anything else is a type error */ default: assert(0); } rtype = tmptype; break; case OP_MUL: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int * right.t_int; break; /* Since this is as_int anything else is a type error */ default: assert(0); } rtype = tmptype; break; case OP_DIV: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int / right.t_int; break; /* Since this is as_int anything else is a type error */ default: assert(0); } rtype = tmptype; break; case OP_MOD: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int % right.t_int; break; /* Since this is as_int anything else is a type error */ default: assert(0); } rtype = TYPE_INT; break; case OP_AND: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int && right.t_int; break; case TYPE_DOUBLE: result.t_int = left.t_float && right.t_float; break; /* Since this is as_int anything else is a type error */ default: cerr << "!!!!!" << line < right.t_int; break; case TYPE_DOUBLE: result.t_int = left.t_float > right.t_float; break; case TYPE_STRING: result.t_int = *left.t_string > *right.t_string; break; default: assert(0); } rtype = TYPE_INT; break; case OP_LE: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int < right.t_int; break; case TYPE_DOUBLE: result.t_int = left.t_float < right.t_float; break; case TYPE_STRING: result.t_int = *left.t_string < *right.t_string; break; default: assert(0); } rtype = TYPE_INT; break; case OP_GEQ: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int >= right.t_int; break; case TYPE_DOUBLE: result.t_int = left.t_float >= right.t_float; break; case TYPE_STRING: result.t_int = *left.t_string >= *right.t_string; break; default: assert(0); } rtype = TYPE_INT; break; case OP_LEQ: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int <= right.t_int; break; case TYPE_DOUBLE: result.t_int = left.t_float <= right.t_float; break; case TYPE_STRING: result.t_int = *left.t_string <= *right.t_string; break; default: assert(0); } rtype = TYPE_INT; break; case OP_NEQ: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int != right.t_int; break; case TYPE_DOUBLE: result.t_int = left.t_float != right.t_float; break; case TYPE_STRING: result.t_int = *left.t_string != *right.t_string; break; default: assert(0); } rtype = TYPE_INT; break; } assert(rtype == TYPE_INT); return result.t_int; } /* in retrospect this is horrible and I see how it should be refactored, but I am really just rushing to get it done now. */ string *BinaryExpr::as_string(void) { // make type error if this isn't an int expression /* New algorithm for this - 1. convert all arguments to the final type (mytype) 2. execute the operation with homogeneous arguments. 3. make a cast from mytype to the desired type if possible. */ Gpl_union left, right, result; int tmptype; int rtype = 0; switch (tmptype=MAX(left_operand->type(), right_operand->type())) { case TYPE_INT: left.t_int = left_operand->as_int(); right.t_int = right_operand->as_int(); break; case TYPE_DOUBLE: left.t_float = left_operand->as_float(); right.t_float = right_operand->as_float(); break; case TYPE_STRING: left.t_string = left_operand->as_string(); right.t_string = right_operand->as_string(); break; } // I really wish there were a safe way to ignore the type of the union for this // but i guess it needs that info to compile it logically // did i mention how much i hate static typing by hand? i wish it were ML or // else python switch (op) { case OP_ADD: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int + right.t_int; break; case TYPE_DOUBLE: result.t_float = left.t_float + right.t_float; break; case TYPE_STRING: result.t_string = new string(*left.t_string + *right.t_string); break; default: assert(0); } rtype = tmptype; break; case OP_SUB: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int - right.t_int; break; case TYPE_DOUBLE: result.t_float = left.t_float - right.t_float; break; default: assert(0); } rtype = tmptype; break; case OP_MOD: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int % right.t_int; default: assert(0); } rtype = TYPE_INT; break; case OP_MUL: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int * right.t_int; break; case TYPE_DOUBLE: result.t_float = left.t_float * right.t_float; break; /* Since this is as_int anything else is a type error */ default: assert(0); } rtype = tmptype; break; case OP_DIV: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int / right.t_int; break; case TYPE_DOUBLE: result.t_float = left.t_float / right.t_float; break; default: assert(0); } rtype = tmptype; break; case OP_AND: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int && right.t_int; break; case TYPE_DOUBLE: result.t_int = left.t_float && right.t_float; break; /* Since this is as_int anything else is a type error */ default: assert(0); } rtype = TYPE_INT; break; case OP_OR: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int || right.t_int; break; case TYPE_DOUBLE: result.t_int = left.t_float || right.t_float; break; default: assert(0); } rtype = TYPE_INT; break; case OP_EQU: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int == right.t_int; break; case TYPE_DOUBLE: result.t_int = left.t_float == right.t_float; break; case TYPE_STRING: result.t_int = *left.t_string == *right.t_string; break; default: assert(0); } rtype = TYPE_INT; break; case OP_GR: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int > right.t_int; break; case TYPE_DOUBLE: result.t_int = left.t_float > right.t_float; break; case TYPE_STRING: result.t_int = *left.t_string > *right.t_string; break; default: assert(0); } rtype = TYPE_INT; break; case OP_LE: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int < right.t_int; break; case TYPE_DOUBLE: result.t_int = left.t_float < right.t_float; break; case TYPE_STRING: result.t_int = *left.t_string < *right.t_string; break; default: assert(0); } rtype = TYPE_INT; break; case OP_GEQ: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int >= right.t_int; break; case TYPE_DOUBLE: result.t_int = left.t_float >= right.t_float; break; case TYPE_STRING: result.t_int = *left.t_string >= *right.t_string; break; default: assert(0); } rtype = TYPE_INT; break; case OP_LEQ: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int <= right.t_int; break; case TYPE_DOUBLE: result.t_int = left.t_float <= right.t_float; break; case TYPE_STRING: result.t_int = *left.t_string <= *right.t_string; break; default: assert(0); } rtype = TYPE_INT; break; case OP_NEQ: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int != right.t_int; break; case TYPE_DOUBLE: result.t_int = left.t_float != right.t_float; break; case TYPE_STRING: result.t_int = *left.t_string != *right.t_string; break; default: assert(0); } rtype = TYPE_INT; break; } switch (rtype) { case TYPE_STRING: return result.t_string; case TYPE_DOUBLE: return double_to_string(result.t_float); case TYPE_INT: return int_to_string(result.t_int); } assert(0); return NULL; // for the compiler } /* string *BinaryExpr::as_string(void) { string *tmp = new string(""); switch (op) { case OP_ADD: //switch (my_type) { // i hate static typing so much // case //} *tmp = *left_operand->as_string(); *tmp += *right_operand->as_string(); break; case OP_EQU: //FIXME semantic errors throughout; we DONT CONVERNT TO STRING UNTIL AFTER DUH *tmp = (*left_operand->as_string() == *right_operand->as_string()); break; case OP_GR: *tmp = (*left_operand->as_string() > *right_operand->as_string()); break; case OP_LE: *tmp = (*left_operand->as_string() < *right_operand->as_string()); break; case OP_GEQ: *tmp = (*left_operand->as_string() >= *right_operand->as_string()); break; case OP_LEQ: *tmp = (*left_operand->as_string() <= *right_operand->as_string()); break; case OP_NEQ: *tmp = (*left_operand->as_string() != *right_operand->as_string()); break; } //cout << "+++++++" << *tmp << endl; return tmp; }*/ /* double BinaryExpr::as_float(void) { // make type error if this isn't an int expression - maybe an // exception can percolate up from constants? // type errors will be generaqted by the as functions switch (op) { case OP_ADD: return left_operand->as_float() + right_operand->as_float(); case OP_SUB: return left_operand->as_float() - right_operand->as_float(); case OP_MUL: return left_operand->as_float() * right_operand->as_float(); case OP_DIV: return left_operand->as_float() / right_operand->as_float(); case OP_AND: return left_operand->as_float() && right_operand->as_float(); case OP_OR: return left_operand->as_float() || right_operand->as_float(); case OP_EQU: return left_operand->as_float() == right_operand->as_float(); case OP_GR: return left_operand->as_float() > right_operand->as_float(); case OP_LE: return left_operand->as_float() < right_operand->as_float(); case OP_GEQ: return left_operand->as_float() >= right_operand->as_float(); case OP_LEQ: return left_operand->as_float() <= right_operand->as_float(); case OP_NEQ: return left_operand->as_float() != right_operand->as_float(); } return -1.0; } */ double BinaryExpr::as_float(void) { // make type error if this isn't an int expression /* New algorithm for this - 1. convert all arguments to the final type (mytype) 2. execute the operation with homogeneous arguments. 3. make a cast from mytype to the desired type if possible. */ Gpl_union left, right, result; int tmptype; switch (tmptype=MAX(left_operand->type(), right_operand->type())) { case TYPE_INT: left.t_int = left_operand->as_int(); right.t_int = right_operand->as_int(); break; case TYPE_DOUBLE: left.t_float = left_operand->as_float(); right.t_float = right_operand->as_float(); break; default: assert(0); // if it comes out to be a string, we can't make it float. } // I really wish there were a safe way to ignore the type of the union for this // but i guess it needs that info to compile it logically // did i mention how much i hate static typing by hand? i wish it were ML or // else python switch (op) { case OP_ADD: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int + right.t_int; break; case TYPE_DOUBLE: result.t_float = left.t_float + right.t_float; break; default: assert(0); } break; case OP_SUB: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int - right.t_int; break; case TYPE_DOUBLE: result.t_float = left.t_float - right.t_float; break; default: assert(0); } break; case OP_MUL: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int * right.t_int; break; case TYPE_DOUBLE: result.t_float = left.t_float * right.t_float; break; /* Since this is as_int anything else is a type error */ default: assert(0); } break; case OP_DIV: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int / right.t_int; break; case TYPE_DOUBLE: result.t_float = left.t_float / right.t_float; break; default: assert(0); } break; case OP_MOD: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int / right.t_int; break; default: assert(0); } break; case OP_AND: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int && right.t_int; break; case TYPE_DOUBLE: result.t_int = left.t_float && right.t_float; break; /* Since this is as_int anything else is a type error */ default: assert(0); } break; case OP_OR: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int || right.t_int; break; case TYPE_DOUBLE: result.t_int = left.t_float || right.t_float; break; default: assert(0); } break; case OP_EQU: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int == right.t_int; break; case TYPE_DOUBLE: result.t_int = left.t_float == right.t_float; break; case TYPE_STRING: result.t_int = *left.t_string == *right.t_string; break; default: assert(0); } break; case OP_GR: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int > right.t_int; break; case TYPE_DOUBLE: result.t_int = left.t_float > right.t_float; break; case TYPE_STRING: result.t_int = *left.t_string > *right.t_string; break; default: assert(0); } break; case OP_LE: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int < right.t_int; break; case TYPE_DOUBLE: result.t_int = left.t_float < right.t_float; break; case TYPE_STRING: result.t_int = *left.t_string < *right.t_string; break; default: assert(0); } break; case OP_GEQ: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int >= right.t_int; break; case TYPE_DOUBLE: result.t_int = left.t_float >= right.t_float; break; case TYPE_STRING: result.t_int = *left.t_string >= *right.t_string; break; default: assert(0); } break; case OP_LEQ: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int <= right.t_int; break; case TYPE_DOUBLE: result.t_int = left.t_float <= right.t_float; break; case TYPE_STRING: result.t_int = *left.t_string <= *right.t_string; break; default: assert(0); } break; case OP_NEQ: switch (tmptype) { case TYPE_INT: result.t_int = left.t_int != right.t_int; break; case TYPE_DOUBLE: result.t_int = left.t_float != right.t_float; break; case TYPE_STRING: result.t_int = *left.t_string != *right.t_string; break; default: assert(0); } break; } switch (tmptype) { case TYPE_DOUBLE: return result.t_float; case TYPE_INT: return int_to_double(result.t_int); default: assert(0);//return result.t_string; } return -421337; } BinaryExpr::BinaryExpr(int op, Expression *left, Expression *right) { this->op = op; left_operand = left; right_operand = right; my_type = type_check(op, left_operand->type(), right_operand->type()); if (!my_type) { left_operand= new IntConstantExpr(0); right_operand = new IntConstantExpr(0); my_type = TYPE_INT; } } /* CONSTANT EXPRESSIONS: int */ long IntConstantExpr::as_int(void) { return value; } double IntConstantExpr::as_float(void) { return (double)value; } string *IntConstantExpr::as_string(void) { char buffer[80]; sprintf(buffer, "%ld", value); return new string(buffer); } /* CONSTANT EXPRESSIONS: float */ long FloatConstantExpr::as_int(void) { /* TYPE ERROR */ return (long)value; } double FloatConstantExpr::as_float(void) { return value; } string *FloatConstantExpr::as_string(void) { char buffer[80]; sprintf(buffer, "%lg", value); return new string(buffer); } /* CONSTANT EXPRESSIONS: string */ long StringConstantExpr::as_int(void) { /* TYPE ERROR */ //long tmp; //sscanf(value->c_str(), "%ld", &tmp); return 0; } double StringConstantExpr::as_float(void) { /* TYPE ERROR */ //double tmp; //sscanf(value->c_str(), "%lg", &tmp); return 0;//tmp; } string *StringConstantExpr::as_string(void) { return value; } VariableExpr::VariableExpr(string *tid, string *atr, Expression *index){ this->index = index; this->name = tid ; this->member = atr; this->my_type = 0; Symbol_table *symbols = Symbol_table::instance(); if (index == NULL && !symbols->has_symbol(tid)) { Error::error(Error::UNDECLARED_VARIABLE, *tid); } if (index != NULL && index->type() != TYPE_INT) { Error::error(Error::ARRAY_INDEX_MUST_BE_AN_INTEGER, *tid, string("A ") + type_name_string[index->type()] + string(" expression")); this->index = new IntConstantExpr(0); } } int VariableExpr::type(void) { Symbol_table *symbols = Symbol_table::instance(); return symbols->type(name); } // maybe i should have gone through with the class hierarchy thing // and made variablexpr, arrayexpr, memberxprr, maybe with multiple // inheritance that would have been a hoot long VariableExpr::as_int(void) { Symbol_table *symbols = Symbol_table::instance(); // FIXME: needs to support indexing, members| //if (symbols->type(name) != TYPE_INT) assert(0); if (this->index == NULL) { if (symbols->has_symbol(name)) return symbols->as_int(name); else return 0; } else { char buffer[90]; sprintf(buffer, "%s[%ld]", name->c_str(), index->as_int()); if (symbols->has_symbol(new string(buffer))) { return symbols->as_int(new string(buffer)); } else { Error::error(Error::ARRAY_INDEX_OUT_OF_BOUNDS, *name, *index->as_string()); return symbols->as_int(new string(*name + "[0]")); } } } double VariableExpr::as_float(void) { Symbol_table *symbols = Symbol_table::instance(); if (this->index == NULL) { if (symbols->has_symbol(name)) return symbols->as_float(name); else return 0.0; } else { char buffer[90]; sprintf(buffer, "%s[%ld]", name->c_str(), index->as_int()); return symbols->as_float(new string(buffer)); } } string *VariableExpr::as_string(void) { Symbol_table *symbols = Symbol_table::instance(); if (this->index == NULL) { if (symbols->has_symbol(name)) return symbols->as_string(name); else return new string(""); } else { char buffer[90]; sprintf(buffer, "%s[%ld]", name->c_str(), index->as_int()); return symbols->as_string(new string(buffer)); } } MathExpr::MathExpr(int op, Expression *operand) { this->op = op; this->operand = operand; my_type = type_check(op, operand->type(), 0); if (!my_type) { this->operand = new IntConstantExpr(1); my_type = TYPE_INT; } } UnaryExpr::UnaryExpr(int op, Expression *operand) { this->op = op; this->operand = operand; my_type = type_check(op, operand->type(), 0); if (!my_type) { this->operand = new IntConstantExpr(0); my_type = TYPE_INT; } };