예제 #1
0
        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;
        }