public override AstNode Visit(BinaryOperation node) { // Get the expressions. Expression left = node.GetLeftExpression(); Expression right = node.GetRightExpression(); // Make sure they are linked, used by operator overloading. if(left.GetNext() != null || right.GetNext() != null) Error(node, "binary operation arguments linked."); left.SetNext(right); // Visit the expressions. left.Accept(this); right.Accept(this); // Check the types. IChelaType leftType = left.GetNodeType(); IChelaType rightType = right.GetNodeType(); IChelaType nullRef = ChelaType.GetNullType(); // Dereference and de-const types. bool leftConstant = leftType.IsConstant(); leftType = DeConstType(leftType); // Required for string constant. if(leftType != nullRef && leftType.IsReference()) { leftType = DeReferenceType(leftType); leftConstant = leftConstant || leftType.IsConstant(); leftType = DeConstType(leftType); leftType = DeReferenceType(leftType); } leftConstant = leftConstant || leftType.IsConstant(); leftType = DeConstType(leftType); // De-reference and de-const the right type. bool rightConstant = rightType.IsConstant(); rightType = DeConstType(rightType); if(rightType != nullRef && rightType.IsReference()) { rightType = DeReferenceType(rightType); rightConstant = rightConstant || rightType.IsConstant(); rightType = DeConstType(rightType); rightType = DeReferenceType(rightType); } rightType = DeConstType(rightType); // Is this a constant. bool isConstant = leftConstant && rightConstant; // Copy the deref and deconst types. IChelaType actualLeftType = leftType; IChelaType actualRightType = rightType; // Check for operator overloading. string opName = BinaryOperation.GetOpName(node.GetOperation()); Function overload = null; if((leftType.IsStructure() || leftType.IsClass()) && opName != null) { // Find the operation function. Structure building = (Structure)leftType; FunctionGroup opGroup = (FunctionGroup)building.FindMember(opName); if(opGroup != null) { // Pick the function from the group. overload = PickFunction(node, opGroup, true, null, left, true); } } // Now check at the right type. if((rightType.IsStructure() || rightType.IsClass()) && opName != null && overload == null) { // Find the operation function. Structure building = (Structure)rightType; FunctionGroup opGroup = (FunctionGroup)building.FindMember(opName); if(opGroup != null) { // Pick the function from the group. overload = PickFunction(node, opGroup, true, null, left, true); } } // Use the found overload. if(overload != null) { // Set the operator overload. FunctionType overloadType = overload.GetFunctionType(); node.SetNodeType(overloadType.GetReturnType()); node.SetOverload(overload); return node; } // Check for pointer arithmetic. int op = node.GetOperation(); if((leftType.IsPointer() || rightType.IsPointer()) && (op == BinaryOperation.OpAdd || op == BinaryOperation.OpSub)) { // Only addition and subtraction are accepted. if(leftType.IsPointer() && rightType.IsPointer()) { if(op == BinaryOperation.OpAdd) Error(node, "cannot add two pointers."); // Set the coercion type and pointer arithmetic flags. node.SetNodeType(ChelaType.GetLongType()); node.SetCoercionType(leftType); node.SetSecondCoercion(rightType); node.SetPointerArithmetic(true); } else { // Check type compatibility. if(op == BinaryOperation.OpSub && !leftType.IsPointer()) Error(node, "cannot perform integer - pointer subtraction."); else if((leftType.IsPointer() && !rightType.IsInteger()) || (!leftType.IsInteger() && rightType.IsPointer())) Error(node, "only pointers and integers can be used in pointer arithmetic."); // Set the coercion type and pointer arithmetic flags. node.SetNodeType(leftType.IsPointer() ? leftType : rightType); node.SetCoercionType(leftType); node.SetSecondCoercion(rightType); node.SetPointerArithmetic(true); } // There aren't intermediate operations. node.SetOperationType(node.GetNodeType()); return node; } else { // Read the types again. leftType = left.GetNodeType(); rightType = right.GetNodeType(); } // Perform coercion. IChelaType destType = Coerce(node, leftType, rightType, right.GetNodeValue()); if(destType == null) destType = Coerce(node, rightType, leftType, left.GetNodeValue()); // Set the node coercion type. node.SetCoercionType(destType); node.SetSecondCoercion(destType); // Checks for matrix multiplication. if(BinaryOperation.OpMul == op) { // Use the actual types. leftType = actualLeftType; rightType = actualRightType; if(leftType.IsMatrix() && rightType.IsMatrix()) { // Cast the matrix. MatrixType leftMatrix = (MatrixType)leftType; MatrixType rightMatrix = (MatrixType)rightType; // The number of first matrix columns must be equal // to the number of the second matrix rows. if(leftMatrix.GetNumColumns() != rightMatrix.GetNumRows()) Error(node, "not matching number of matrix column and rows"); // Coerce the primitive types. IChelaType primCoercion = Coerce(leftMatrix.GetPrimitiveType(), rightMatrix.GetPrimitiveType()); if(primCoercion == null) Error(node, "cannot multiply a {0} by a{1}", leftMatrix.GetDisplayName(), rightMatrix.GetDisplayName()); node.SetCoercionType(MatrixType.Create(primCoercion, leftMatrix.GetNumRows(), leftMatrix.GetNumColumns())); node.SetSecondCoercion(MatrixType.Create(primCoercion, rightMatrix.GetNumRows(), rightMatrix.GetNumColumns())); // Create the destination type. destType = MatrixType.Create(primCoercion, leftMatrix.GetNumRows(), rightMatrix.GetNumColumns()); node.SetMatrixMul(true); } else if(leftType.IsMatrix() && rightType.IsVector()) { // Cast the matrix and the vector. MatrixType leftMatrix = (MatrixType)leftType; VectorType rightVector = (VectorType)rightType; // Check the dimensions. if(leftMatrix.GetNumColumns() != rightVector.GetNumComponents()) Error(node, "not matching number of matrix column and rows"); // Coerce the primitive types. IChelaType primCoercion = Coerce(leftMatrix.GetPrimitiveType(), rightVector.GetPrimitiveType()); if(primCoercion == null) Error(node, "cannot multiply a {0} by a {1}", leftMatrix.GetDisplayName(), rightVector.GetDisplayName()); node.SetCoercionType(MatrixType.Create(primCoercion, leftMatrix.GetNumRows(), leftMatrix.GetNumColumns())); node.SetSecondCoercion(VectorType.Create(primCoercion, rightVector.GetNumComponents())); // Create the destination type. destType = VectorType.Create(primCoercion, leftMatrix.GetNumRows()); node.SetMatrixMul(true); } else if(rightType.IsMatrix() && leftType.IsVector()) { // Cast the matrix and the vector. VectorType leftVector = (VectorType)leftType; MatrixType rightMatrix = (MatrixType)rightType; // Check the dimensions. if(leftVector.GetNumComponents() != rightMatrix.GetNumRows()) Error(node, "not matching number of matrix column and rows"); // Coerce the primitive types. IChelaType primCoercion = Coerce(leftVector.GetPrimitiveType(), rightMatrix.GetPrimitiveType()); if(primCoercion == null) Error(node, "cannot multiply a {0} by a {1}", leftVector.GetDisplayName(), rightMatrix.GetDisplayName()); node.SetCoercionType(VectorType.Create(primCoercion, leftVector.GetNumComponents())); node.SetSecondCoercion(MatrixType.Create(primCoercion, rightMatrix.GetNumRows(), rightMatrix.GetNumColumns())); // Create the destination type. destType = VectorType.Create(primCoercion, rightMatrix.GetNumColumns()); node.SetMatrixMul(true); } } // Check for special operations if(destType == null) { // Use the actual types. leftType = actualLeftType; rightType = actualRightType; // Check for external operations. if(leftType.IsVector() && (op == BinaryOperation.OpDiv || op == BinaryOperation.OpMul)) { // Select the types. VectorType vectorType = (VectorType)leftType; IChelaType primitiveType = rightType; // Coerce the primitive type. IChelaType secondCoercion = vectorType.GetPrimitiveType(); if(primitiveType != secondCoercion && Coerce(secondCoercion, primitiveType) != secondCoercion) Error(node, "failed to perform scalar operation."); // Store the new coercion types. destType = vectorType; node.SetCoercionType(vectorType); node.SetSecondCoercion(secondCoercion); } else if(rightType.IsVector() && op == BinaryOperation.OpMul) { // Select the types. IChelaType primitiveType = leftType; VectorType vectorType = (VectorType)rightType; // Coerce the primitive type. IChelaType firstCoercion = vectorType.GetPrimitiveType(); if(primitiveType != firstCoercion && Coerce(firstCoercion, primitiveType) != firstCoercion) Error(node, "failed to perform scalar operation."); // Store the new coercion types. destType = vectorType; node.SetCoercionType(firstCoercion); node.SetSecondCoercion(vectorType); } else Error(node, "failed to perform binary operation, invalid operands of type {0} and {1}.", leftType.GetDisplayName(), rightType.GetDisplayName()); } // De-const the type. destType = DeConstType(destType); // Dest type must be integer, float or boolean. IChelaType operationType = destType; if(!destType.IsNumber() && !destType.IsPointer() && !BinaryOperation.IsEquality(op)) { // Try to operate with the unboxed types. // Only structures are box holders. if(!destType.IsStructure()) Error(node, "cannot perform binary operations with type {0}.", destType.GetDisplayName()); // Use the structure associated type, or use his enum type. Structure building = (Structure)destType; IChelaType assocType = currentModule.GetAssociatedClassPrimitive(building); if(assocType == null) { // The structure can be an enumeration. Class enumClass = currentModule.GetEnumClass(); if(!building.IsDerivedFrom(enumClass)) Error(node, "cannot perform operation {0} with object of types {1}.", BinaryOperation.GetOpErrorName(op), building.GetDisplayName()); } // Unbox the structure. FieldVariable valueField = building.FindMember("__value") as FieldVariable; if(valueField == null) valueField = building.FindMember("m_value") as FieldVariable; if(valueField == null) Error(node, "structure {0} is not boxing a primitive.", building.GetDisplayName()); operationType = valueField.GetVariableType(); node.SetCoercionType(operationType); node.SetSecondCoercion(operationType); } // Use integers of at least 4 bytes. if(!operationType.IsPlaceHolderType() && operationType.IsInteger() && operationType.GetSize() < 4 && BinaryOperation.IsArithmetic(op)) { destType = ChelaType.GetIntType(); operationType = destType; IChelaType coercionType = destType; if(isConstant) coercionType = ConstantType.Create(destType); node.SetCoercionType(coercionType); node.SetSecondCoercion(coercionType); } // Perform validation switch(op) { case BinaryOperation.OpAdd: case BinaryOperation.OpMul: case BinaryOperation.OpDiv: case BinaryOperation.OpMod: if(operationType == ChelaType.GetBoolType()) Error(node, "Arithmetic operations aren't available with boolean values."); break; case BinaryOperation.OpSub: if(operationType == ChelaType.GetBoolType()) Error(node, "Arithmetic operations aren't available with boolean values."); // Use signed integer when subtracting. if(operationType.IsInteger() && operationType.IsUnsigned()) { // Use a new dest type and coercion type. IntegerType intType = (IntegerType)operationType; operationType = intType.GetSignedVersion(); IChelaType newCoercion = destType; if(isConstant) newCoercion = ConstantType.Create(intType); // Override the result types. node.SetCoercionType(newCoercion); node.SetSecondCoercion(newCoercion); destType = operationType; } break; case BinaryOperation.OpBitAnd: case BinaryOperation.OpBitOr: case BinaryOperation.OpBitXor: case BinaryOperation.OpBitLeft: case BinaryOperation.OpBitRight: if(operationType.IsFloatingPoint() || operationType == ChelaType.GetBoolType()) Error(node, "Bitwise operations aren't available with floating point and boolean values."); break; case BinaryOperation.OpLT: case BinaryOperation.OpGT: case BinaryOperation.OpEQ: case BinaryOperation.OpNEQ: case BinaryOperation.OpLEQ: case BinaryOperation.OpGEQ: // Boolean result. operationType = ChelaType.GetBoolType(); destType = operationType; break; case BinaryOperation.OpLAnd: case BinaryOperation.OpLOr: // Left and right must be boolean. if(operationType != ChelaType.GetBoolType()) Error(node, "Expected boolean arguments."); break; default: Error(node, "Compiler bug, unknown binary operation."); break; } // Set the destination type. if(isConstant) { node.SetOperationType(ConstantType.Create(operationType)); node.SetNodeType(ConstantType.Create(destType)); } else { node.SetOperationType(operationType); node.SetNodeType(destType); } return node; }