/************************************************
  Team: Jon Gregory, Gor Nishanov
  Project #2 Part 2
    for RPI course 66-648 Compiler Design Theory

  expr.h:

    repository of Expression classes.
    Later we will add issueCode and check methods
    to every class and hopefully it solves Project #3


    *****/


class Node : public QuickNew {
public:
	int op;
public:
	Node(int Op):op(Op) {}
	int getOp() {	return op; }
	virtual void print(ostream& o) const {
		o << "Nodes is not supposed to be here !!!!" << endl;
		assert(false);
	}
	static void safePrint(ostream& o, const Node* e) {
		if(e == 0)
			o << "<null>";
		else o << *e;
	}
	friend ostream& operator << (ostream& o, const Node& n) {
		n.print(o);
		return o;
	}
	friend ostream& operator << (ostream& o, const Node* n) {
		Node::safePrint(o, n);
		return o;
	}
	void printOpName(ostream& o) const { o << ('(' + opNames[op] + ' '); }
};


class Expression : public Node {
public:
	Type* type;
public:
	Expression(int op, Type* typ):Node(op),type(typ),nextInOwner(0) {}

	virtual bool isConstant() {	return false;	}
        virtual Identifier toIdent() const {return Identifier::crazy;}

	virtual bool firstConstructor() {	return false;	}
	virtual void print(ostream& o) const {
		o << opNames[op];	}

	Expression * nextInOwner;
};
typedef List<Expression> ExprList;

class ConstantExpression : public Expression {
public:
	ConstantExpression(int op, Type* type):Expression(op, type){}
	bool isConstant() {	return true;}
};


class UnaryExpression: public Expression {
protected:
	Expression* right;
public:
	UnaryExpression(int op, Type* type, Expression* r):
		Expression(op, type),right(r) {}

	virtual void print(ostream& o) const {
		o << '(' << opNames[op] << ' ' << right << ')';
	}
};

class NaryExpression : public UnaryExpression {
protected:
	Expression * args; //[]
public:

	NaryExpression(int op, Type *type, Expression *right, Expression *arg) :
		UnaryExpression(op, type, right),args(arg) {}

	virtual void print(ostream& o) const {
		o << '(' << opNames[op];
		if (right != NULL) {
			o << " ";
			right->print(o);
		}
		Expression *e = args;
		if(e == 0) o << "<null>";
		while(e != 0) {
			o << ' ' << e;
			e = e->nextInOwner;
		}
	        o << ')';
	}
};

class ArrayExpression : public NaryExpression {
public:
	ArrayExpression(Expression* args):
		NaryExpression(ARRAY, Type::Error, NULL, args){}
};

class BinaryExpression : public UnaryExpression {
protected:
	Expression* left;
public:
	BinaryExpression(int op, Type* type, Expression* Left, Expression* right):
		UnaryExpression(op, type, right),left(Left){}

	virtual void print(ostream& o) const {
		o << '(' << opNames[op] << ' ';
		safePrint(o, left);
		o << ' ';
		safePrint(o, right);
		o << ')';
	}
};

class BinaryAssignExpression : public BinaryExpression {
public:
	BinaryAssignExpression(int op, Expression *left, Expression *right):
		BinaryExpression(op, left->type, left, right){}
};

class AssignExpression : public BinaryAssignExpression {
public:
	AssignExpression(Expression* left, Expression* right):
		BinaryAssignExpression(ASSIGN, left, right){}
};

class ArrayAccessExpression : public UnaryExpression {
	Expression* index;
public:
	ArrayAccessExpression(Expression* right, Expression* ndx):
		UnaryExpression(ARRAYACCESS, Type::Error, right),index(ndx){}

	virtual void print(ostream& o) const {
		o << '(' << opNames[op] << ' ';
		safePrint(o, right);
    o << ' ' << index << ')';
	}
};

class ArrayGenerateExpression : public UnaryExpression {
	Expression* index;
public:
	ArrayGenerateExpression(Expression* right, Expression* ndx):
		UnaryExpression(ARRAYACCESS, Type::Error, right),index(ndx){}

	virtual void print(ostream& o) const {
    safePrint(o, right);
    o << '['; if(index != 0) o << index; o << ']';
	}
};

class BinaryArithmeticExpression : public BinaryExpression {
public:
	BinaryArithmeticExpression(int op, Expression *left, Expression *right):
		BinaryExpression(op, left->type, left, right){}
};

class BinaryCompareExpression : public BinaryExpression {
public:
	BinaryCompareExpression(int op, Expression *left, Expression *right):
		BinaryExpression(op, Type::Bool, left, right){ }
};

class BinaryBitExpression : public BinaryExpression {
public:
	BinaryBitExpression(int op, Expression *left, Expression *right):
		BinaryExpression(op, left->type, left, right) {}
};

class BinaryEqualityExpression : public BinaryExpression {
public:
	BinaryEqualityExpression(int op, Expression *left, Expression *right):
		BinaryExpression(op, Type::Bool, left, right) {}
};

class BinaryLogicalExpression: public BinaryExpression {
public:
	BinaryLogicalExpression(int op, Expression *left, Expression *right):
		BinaryExpression(op, Type::Bool, left, right) {}
};


class BoolExpression : public ConstantExpression {
	bool value;
public:
  BoolExpression(bool val) :
		ConstantExpression(BOOLEANVAL, Type::Bool),value(val){}

	virtual void print(ostream& o) const {
		o << ((value) ? "true" : "false");}
};

class IntegerExpression : public ConstantExpression {
public:
	int value;
IntegerExpression(int op, Type* type, int val):
	ConstantExpression(op, type),value(val) {}

//	asm.add(opc_ldc, new Integer(value));
};

class ByteExpression : public IntegerExpression {
public:
	ByteExpression(int value):
		IntegerExpression(BYTEVAL, Type::Byte, value){}

	virtual void print(ostream& o) const {
		 o << value << 'b'; }
};

class CastExpression : public BinaryExpression {
public:
	CastExpression(Expression *left, Expression *right):
		BinaryExpression(CAST, left->type, left, right){}

	virtual void print(ostream& o) const {
		o << '(' << opNames[op] << ' ' << type << ' ';
		safePrint(o, right); o << ')';
	}
};

class CharExpression : public IntegerExpression {
public:
	CharExpression(char value):
		IntegerExpression(CHARVAL, Type::Char, value) {}

	virtual void print(ostream& o) const {
		o << '\'' << (char)value << '\'';
	}
};

class CommaExpression : public BinaryExpression {
public:
	CommaExpression(Expression* left, Expression* right):
		BinaryExpression(COMMA,
			(right != null) ? right->type : Type::Void, left, right) {}
};

class ConditionalExpression : public BinaryExpression {
	Expression* cond;
public:
	ConditionalExpression(Expression *c, Expression *left, Expression *right):
		BinaryExpression(COND, Type::Error, left, right), cond(c) {}

	virtual void print(ostream& o) const {
		o << '(' << opNames[op] << ' ';
		safePrint(o, cond); o << ' ';
		safePrint(o, left); o << ' ';
		safePrint(o, right); o << ')';
	}
};

class ConvertExpression : public UnaryExpression {
public: ConvertExpression(Type* type, Expression* right):
	UnaryExpression(CONVERT, type, right){}

	virtual void print(ostream& o) const {
		o << '(' << opNames[op] << ' ' << type << ' ' << right << ')'; }
};

class DoubleExpression : public ConstantExpression {
	double value;
public:
	DoubleExpression(double val):
		ConstantExpression(DOUBLEVAL, Type::Double),value(val){}

//	asm.add(where, opc_ldc2_w, new Double(value));

	virtual void print(ostream& o) const {
		o << value << "D";}
};

class ExprExpression : public UnaryExpression {
public:
	ExprExpression(Expression* right):
		UnaryExpression(EXPR, right->type, right){}
};

class FieldExpression : public UnaryExpression {
	Identifier id;
public:
	FieldExpression(Expression* right, Identifier Id):
		UnaryExpression(FIELD, Type::Error, right),id(Id) {}

	virtual void print(ostream& o) const {
		o << '(' << right << '.' << id  << ')';
	}
       virtual Identifier toIdent() const {
         char a[100];
         char* ptr = (right->toIdent()).copyTo(a);
         *ptr = '/'; id.copyTo(++ptr);
          return Identifier(a);
       }
};

class FloatExpression : public ConstantExpression {
	float value;

public:
	FloatExpression(float val):
		ConstantExpression(FLOATVAL, Type::Float),value(val){}
	virtual void print(ostream& o) const {
		o << value << 'F' ;}
};

class PreIncDecExpression : public UnaryExpression {
public:
	PreIncDecExpression(int op, Expression* right):
		UnaryExpression(op, right->type, right){}
};

class PostIncDecExpression : public UnaryExpression {
public:
  PostIncDecExpression(int op, Expression* right):
    UnaryExpression(op + POSTINC - PREINC, right->type, right){}
  virtual void print(ostream& o) const {
    o << '(' << right << opNames[op] << ')';
  }  
};

class IdentifierExpression : public Expression {
	Identifier id;

public: 
  IdentifierExpression(Identifier Id):
  Expression(IDENT, Type::Error),id(Id){}

  virtual Identifier toIdent() const {return id;}
  virtual void print(ostream& o) const {o << id;}
};

class IntExpression : public  IntegerExpression {
public: IntExpression(int val):IntegerExpression(INTVAL, Type::Int, val){}
	virtual void print(ostream& o) const { o << value; }
};

class LengthExpression : public UnaryExpression {
public:
	LengthExpression(Expression* right):
	UnaryExpression(LENGTH, Type::Int, right){}
};

class InstanceOfExpression : public BinaryExpression {
public:
	InstanceOfExpression(Expression* left, Expression* right):
		BinaryExpression(INSTANCEOF, Type::Bool, left, right){}

	virtual void print(ostream& o) const {
		printOpName(o);
		o << left << ' ';
		if (right && right->op == TYPE)
			o << right->type;
		else
			o << right;
		o << ')';
	}
};

class MethodExpression : public NaryExpression {
	Identifier id;
//	ClassDefinition clazz;
//	FieldDefinition field;

public:
	MethodExpression(Expression* right, Identifier Id, Expression* args):
		NaryExpression(METHOD, Type::Error, right, args),id(Id){}

  MethodExpression(Expression* right, Expression* args): // !! need to fix
		NaryExpression(METHOD, Type::Error, right, args),id(Identifier::nullstr){}

	virtual bool firstConstructor() {return id == Identifier::init;}

	virtual void print(ostream& o) const {
		o << '(' << opNames[op];
		if (right != null) {
			o << ' ' << right;
		}
		o << " " << id;
		Expression * e = args;
		while(e != 0) {
			o << ' ' << e;
			e = e -> nextInOwner;
		}
		o << ')';
	}
};

class LongExpression : public ConstantExpression {
	long value;
public:
	LongExpression(long val):
		ConstantExpression(LONGVAL, Type::Long), value(val){}

	virtual void print(ostream& o) const {
		o << value << 'L';}
};

class NotExpression : public UnaryExpression {
public:
	NotExpression(Expression* right):
		UnaryExpression(NOT, Type::Bool, right) {}
};

class NegPosExpression : public UnaryExpression {
public:
  NegPosExpression(int code, Expression* right):
    UnaryExpression(code + POS - ADD, right->type, right){}
//	right.codeValue(env, ctx, asm);
//	asm.add(opc_ineg + type.getTypeCodeOffset());
};

class NullExpression : public ConstantExpression {
public:
  NullExpression():ConstantExpression(NULL_E, Type::Null){}
//	asm.add(opc_aconst_null);
	virtual void print(ostream& o) const {
		o << "null";	}
};

class NewArrayExpression : public NaryExpression {
public:
	NewArrayExpression(Expression* right, Expression* args):
		NaryExpression(NEWARRAY, Type::Error, right, args){}
};

class NewInstanceExpression : public NaryExpression {
//	FieldDefinition field;
public:
  NewInstanceExpression(Type* typ, Expression* args):
    NaryExpression(NEWINSTANCE, typ, 0, args){}
};

class ShortExpression : public IntegerExpression {
public:
	ShortExpression(short val):
		IntegerExpression(SHORTVAL, Type::Short, val){}
	virtual void print(ostream& o) const {
		o << value << 's';}
};

class BinaryShiftExpression : public BinaryExpression {
public:
	BinaryShiftExpression(int op, Expression* left, Expression* right):
		BinaryExpression(op, left->type, left, right){}
};

class StringExpression : public ConstantExpression {
	Identifier value;
public:
  StringExpression(char * val):
		ConstantExpression(STRINGVAL, Type::String),value(val){}

//	asm.add(opc_ldc, this);
	virtual void print(ostream& o) const {
		o << '"' << value << '"'; }
};

class SuperExpression : public Expression {
//   LocalField field;
public:
	SuperExpression():Expression(SUPER, Type::Object){}
};

class ThisExpression : public Expression {
//  LocalField field;
public:
	ThisExpression():  Expression(THIS, Type::Object){}
	virtual void print(ostream& o) const {
		o << "this";
	}
};

class TypeExpression : public Expression {
public:
  TypeExpression(Type* type):Expression(TYPE, type){}
  virtual Identifier toIdent() const {return type->typeSig; }

  virtual void print(ostream& o) const {
		o << type; }
};
