package de.fuberlin.projecta.analysis.ast;
import de.fuberlin.commons.lexer.TokenType;
import de.fuberlin.projecta.analysis.SemanticException;
import de.fuberlin.projecta.analysis.TypeChecker;
import de.fuberlin.projecta.codegen.LLVM;
public class BinaryOp extends Expression {
TokenType op;
public TokenType getOp() {
return op;
}
public BinaryOp(TokenType op) {
this.op = op;
}
@Override
public void checkSemantics() {
switch (this.getOp()) {
// TODO: think if you can find other cases, where semantics can be
// wrong/ambiguous
case OP_ASSIGN:
if (this.getChild(0) instanceof Id && LLVM.isInParams((Id)this.getChild(0))){
throw new SemanticException("Assining a new value to a vall-by-value parameter is impossible", this);
}
if (!((this.getChild(0) instanceof Id) || this.getChild(0) instanceof RecordVarCall || this.getChild(0) instanceof ArrayCall)) {
throw new SemanticException(
"Left side of an assignment has to be an identifier, but is "
+ this.getChild(0).toString(), this);
}
if (this.getChild(1) instanceof BinaryOp
&& (((BinaryOp) this.getChild(1)).getOp() == TokenType.OP_ASSIGN)) {
throw new SemanticException(
"Left side of an assignment cannot be an assignment.", this);
}
break;
case OP_DIV:
if (this.getChild(1) instanceof IntLiteral) {
int v = ((IntLiteral) this.getChild(1)).getValue();
if (v == 0)
throw new SemanticException("Division by Zero!", this);
}
else if (this.getChild(1) instanceof RealLiteral) {
double v = ((RealLiteral) this.getChild(1)).getValue();
if (v == 0.0)
throw new SemanticException("Division by Zero!", this);
}
}
// check all children, may throw
for (int i = 0; i < this.getChildrenCount(); i++) {
((AbstractSyntaxTree)this.getChild(i)).checkSemantics();
}
}
@Override
public String genCode() {
String ret = "";
Block block = getHighestBlock();
Expression t1 = (Expression) getLeftSide();
Expression t2 = (Expression) getRightSide();
// comparison operators
if (op == TokenType.OP_EQ || op == TokenType.OP_NE
|| op == TokenType.OP_LT || op == TokenType.OP_LE
|| op == TokenType.OP_GT || op == TokenType.OP_GE) {
// load both values into new memory addresses
ret += LLVM.loadType(t1);
ret += LLVM.loadType(t2);
int mem = block.getNewVar();
this.setValMemory(mem);
ret += "%" + mem + " = " + getIntOrReal(t1) + " " + getOpName(t1)
+ " " + t1.fromTypeStringToLLVMType() + " %";
ret += LLVM.getMem(t1) + ", %";
ret += LLVM.getMem(t2) + "\n";
}
// arithmetic operators
else if (op == TokenType.OP_ADD || op == TokenType.OP_MINUS
|| op == TokenType.OP_DIV || op == TokenType.OP_MUL) {
String type = "";
String mathOp = getMathOpName(t1);
// load both values into new memory addresses
ret += LLVM.loadType(t1);
ret += LLVM.loadType(t2);
int val = block.getNewVar();
this.setValMemory(val); // save currents computation in this node
type = t1.fromTypeStringToLLVMType();
ret += "%" + val + " = " + mathOp + " " + type + " %"
+ LLVM.getMem(t1) + ", %" + LLVM.getMem(t2) + "\n";
}
// assignment operator
else if (op == TokenType.OP_ASSIGN) {
Id id1 = null;
if (t1 instanceof RecordVarCall)
id1 = ((RecordVarCall) t1).getVarId();
else if(t1 instanceof ArrayCall)
id1 = ((ArrayCall) t1).getVarId();
else
id1 = (Id)t1;
if (getChild(1) instanceof StringLiteral) {
StringLiteral str = (StringLiteral) getChild(1);
/*
* if you look below, this is what is implemented (it's kind of
* sick) ;basic principle for saving a string to a char pointer
* %str3 %r3 = alloca [9 x i8] store [9 x i8] c"test1234\00", [9
* x i8]* %r3 %firstEl = getelementptr [9 x i8]* %r3, i8 0, i8 0
* store i8* %firstEl, i8** %str3
*/
int tmp1 = block.getNewVar();
int tmp2 = block.getNewVar();
int strLength = (str.getValue().length() + 1);
ret = "%" + tmp1 + " = alloca [" + strLength + " x i8]\n";
ret += "store [" + strLength + " x i8] c\"" + str.getValue()
+ "\\00\", [" + strLength + " x i8]* %" + tmp1 + "\n";
ret += "%" + tmp2 + " = getelementptr [" + strLength
+ " x i8]* %" + tmp1 + ", i8 0, i8 0 \n";
ret += "store i8* %" + tmp2 + ", i8** %" + id1.getValue();
} else if (t1 instanceof RecordVarCall) {
ret += LLVM.getRecordVarCallPointer((RecordVarCall)t1);
ret += LLVM.loadType(t2);
String t = t2.fromTypeStringToLLVMType();
ret += "store " + t + " %" + LLVM.getMem(t2) + ", " + t + "* "
+ "%" + LLVM.getMem(t1);
} else if(t1 instanceof ArrayCall){
ret += LLVM.getArrayCallPointer((ArrayCall) t1);
ret += LLVM.loadType(t2);
String t = t2.fromTypeStringToLLVMType();
ret += "store " + t + " %" + LLVM.getMem(t2) + ", " + t + "* %" + LLVM.getMem(t1);
} else {
ret += LLVM.loadType(t2);
String t = t2.fromTypeStringToLLVMType();
ret += "store " + t + " %" + LLVM.getMem(t2) + ", " + t + "* "
+ "%" + id1.getValue();
}
} else {
System.out.println("Unknown Binary OP: " + op);
}
return ret;
}
private String getIntOrReal(Expression expr) {
String ret = "";
if (expr != null) {
if ((expr.fromTypeStringToLLVMType()).equals("i64")) {
ret = "fcmp";
} else {
ret = "icmp";
}
}
return ret;
}
private AbstractSyntaxTree getLeftSide() {
return (AbstractSyntaxTree)getChild(0);
}
private AbstractSyntaxTree getRightSide() {
return (AbstractSyntaxTree)getChild(1);
}
private String getMathOpName(Expression expr) {
String mathOp = "";
if ((expr.fromTypeStringToLLVMType()).equals("i64"))
mathOp = "f"; // append f in front of math_op
switch (op) {
case OP_ADD:
mathOp += "add";
break;
case OP_MINUS:
mathOp += "sub";
break;
case OP_MUL:
mathOp += "mul";
break;
case OP_DIV:
mathOp += "sdiv";
break;
default:
assert(false); // should never happen!
}
if(expr.toTypeString().equals(BasicType.TYPE_REAL_STRING)){
return "f" + mathOp;
}
return mathOp;
}
private String getOpName(Expression expr) {
String ret = "";
if (expr != null) {
if ((expr.toTypeString().equals("double"))) {
switch (op) {
case OP_LT:
ret = "olt";
break;
case OP_LE:
ret = "ole";
break;
case OP_GT:
ret = "ogt";
break;
case OP_GE:
ret = "oge";
break;
case OP_EQ:
ret = "eq";
break;
case OP_NE:
ret = "ne";
break;
}
} else {
switch (op) {
case OP_LT:
ret = "slt";
break;
case OP_LE:
ret = "sle";
break;
case OP_GT:
ret = "sgt";
break;
case OP_GE:
ret = "sge";
break;
case OP_EQ:
ret = "eq";
break;
case OP_NE:
ret = "ne";
break;
}
}
}
return ret;
}
@Override
public void checkTypes() {
Expression leftChild = (Expression)getLeftSide();
Expression rightChild = (Expression)getRightSide();
String leftTypeString = leftChild.toTypeString();
String rightTypeString = rightChild.toTypeString();
if (leftTypeString.equals(rightTypeString)) {
switch (this.op) {
case OP_ADD:
case OP_MUL:
case OP_DIV:
case OP_MINUS:
if (!TypeChecker.isNumeric(leftTypeString)
|| !TypeChecker.isNumeric(rightTypeString)) {
throw new SemanticException(
"Can only perform arithmetics operations on numeric types", this);
}
case OP_AND:
case OP_OR:
case OP_NOT:
case OP_LT:
case OP_LE:
case OP_EQ:
case OP_GE:
case OP_GT:
case OP_NE:
case OP_ASSIGN:
return;
default:
throw new SemanticException("Undefined error in BinaryOp", this);
}
}
throw new SemanticException(
"Operands have to be of same type but are:\n Left operand: "
+ leftTypeString + "\nRight operand: "
+ rightTypeString, this);
}
@Override
public String toTypeString() {
// if both operands are not equal, checkTypes will catch this
switch (this.op) {
case OP_ADD:
case OP_MUL:
case OP_DIV:
case OP_MINUS:
return ((Expression) this.getChild(0)).toTypeString();
case OP_AND:
case OP_OR:
case OP_LT:
case OP_LE:
case OP_EQ:
case OP_GE:
case OP_GT:
case OP_NE:
case OP_NOT:
return BasicType.TYPE_BOOL_STRING;
default:
return BasicType.TYPE_VOID_STRING;
}
}
}