typeLinkingExpr db imps su expr@(Binary op exprL exprR _)
    |   length typeLs /= 1 || length typeRs /= 1 = []
    |   elem op ["+"] = case (typeL, typeR) of
                            (TypeVoid, _) -> typeLinkingFailure "Binary + TypeVoid"
                            (_, TypeVoid) -> typeLinkingFailure "Binary + TypeVoid"
                            ((Object (Name ["java", "lang", "String"])), _) -> [(Object (Name ["java", "lang", "String"]))]
                            (_, (Object (Name ["java", "lang", "String"]))) -> [(Object (Name ["java", "lang", "String"]))]
                            (_, _) -> if typeLInt && typeRInt then typeLR' else typeLinkingFailure "Binary +"
    |   elem op ["-", "*", "/", "%"] = if typeLInt && typeRInt then typeLR' else typeLinkingFailure "Binary arithematic op"
    |   elem op ["<", ">", "<=", ">="] = if typeLInt && typeRInt then [TypeBoolean] else typeLinkingFailure "Binary comparision op"
    |   elem op ["&&", "||", "&", "|"] = if typeLBool && typeRBool then [TypeBoolean] else typeLinkingFailure "Binary logical op"
    |   elem op ["==", "!="] = if null equality then typeLinkingFailure $ "Binary " ++ (show typeL) ++ op ++ (show typeR) else [TypeBoolean]
    |   elem op ["="] = if assignRL && (finalArrayLength db imps su exprL) then [typeL] else typeLinkingFailure $ "Binary =" ++ (show typeL) ++ (show typeR) ++ (show $ assignConversion db typeR typeL)
    where
        typeLs = case filter filterNonFunction $ typeLinkingExpr db imps su exprL of
                            [] -> typeLinkingFailure $ "Binary typeL no match: type(left) " ++ op ++ " type(right)" ++ (show exprL) ++ (show exprR)
                            [l] -> [l]
                            a -> typeLinkingFailure $ "Binary typeL multi match: type(left) " ++ op ++ " type(right)" ++ (show exprL) ++ (show exprR) ++ (show a)
        [typeL] = typeLs
        typeRs = case filter filterNonFunction $ typeLinkingExpr db imps su exprR of
                            [] -> typeLinkingFailure $ "Binary typeR no match: type(left) " ++ op ++ " type(right)" ++ (show exprL) ++ (show exprR)
                            [r] -> [r]
                            a -> typeLinkingFailure $ "Binary typeR multi match: type(left) " ++ op ++ " type(right)" ++ (show exprL) ++ (show exprR) ++ (show a)
        [typeR] = typeRs
        
        assignRL = not . null $ assignConversion db typeR typeL
        typeLR = case (null $ castConversion db typeL typeR, null $ castConversion db typeR typeL) of
                    (True, True) -> []
                    (False, _) -> [typeR]
                    (_, False) -> [typeL]
        typeLR' = map widenType typeLR
        typeLInt = not . null $ castConversion db typeL TypeInt
        typeRInt = not . null $ castConversion db typeR TypeInt
        typeLBool= not . null $ castConversion db typeL TypeBoolean
        typeRBool = not . null $ castConversion db typeR TypeBoolean
        equality = equalityCheck db typeL typeR
typeLinkingExpr db imps su expr@(Binary op exprL exprR _)
    |   length typeLs /= 1 || length typeRs /= 1 = []
    |   elem op ["+"] = case (typeL, typeR) of
                            (TypeVoid, _) -> typeLinkingFailure "Binary + TypeVoid"
                            (_, TypeVoid) -> typeLinkingFailure "Binary + TypeVoid"
                            ((Object (Name ["java", "lang", "String"])), _) -> [(Object (Name ["java", "lang", "String"]))]
                            (_, (Object (Name ["java", "lang", "String"]))) -> [(Object (Name ["java", "lang", "String"]))]
                            (_, _) -> if typeLInt && typeRInt then typeLR' else typeLinkingFailure "Binary +"
    |   elem op ["-", "*", "/", "%"] = if typeLInt && typeRInt then typeLR' else typeLinkingFailure "Binary arithematic op"
    |   elem op ["<", ">", "<=", ">="] = if typeLInt && typeRInt then [TypeBoolean] else typeLinkingFailure "Binary comparision op"
    |   elem op ["&&", "||", "&", "|"] = if typeLBool && typeRBool then [TypeBoolean] else typeLinkingFailure "Binary logical op"
    |   elem op ["==", "!="] = if null equality then typeLinkingFailure $ "Binary " ++ (show typeL) ++ op ++ (show typeR) else [TypeBoolean]
    |   elem op ["="] = if assignRL && (finalArrayLength db imps su exprL) then [typeL] else typeLinkingFailure $ "Binary =" ++ (show typeL) ++ (show typeR) ++ (show $ assignConversion db typeR typeL)
    where
        typeLs = case filter filterNonFunction $ typeLinkingExpr db imps su exprL of
                            [] -> typeLinkingFailure $ "Binary typeL no match: type(left) " ++ op ++ " type(right)" ++ (show exprL) ++ (show exprR)
                            [l] -> [l]
                            a -> typeLinkingFailure $ "Binary typeL multi match: type(left) " ++ op ++ " type(right)" ++ (show exprL) ++ (show exprR) ++ (show a)
        [typeL] = typeLs
        typeRs = case filter filterNonFunction $ typeLinkingExpr db imps su exprR of
                            [] -> typeLinkingFailure $ "Binary typeR no match: type(left) " ++ op ++ " type(right)" ++ (show exprL) ++ (show exprR)
                            [r] -> [r]
                            a -> typeLinkingFailure $ "Binary typeR multi match: type(left) " ++ op ++ " type(right)" ++ (show exprL) ++ (show exprR) ++ (show a)
        [typeR] = typeRs
        
        assignRL = not . null $ assignConversion db typeR typeL
        typeLR = case (null $ castConversion db typeL typeR, null $ castConversion db typeR typeL) of
                    (True, True) -> []
                    (False, _) -> [typeR]
                    (_, False) -> [typeL]
        typeLR' = map widenType typeLR
        typeLInt = not . null $ castConversion db typeL TypeInt
        typeRInt = not . null $ castConversion db typeR TypeInt
        typeLBool= not . null $ castConversion db typeL TypeBoolean
        typeRBool = not . null $ castConversion db typeR TypeBoolean
        equality = equalityCheck db typeL typeR
typeLinkingExpr db imps su expr@(Binary op exprL exprR _)
    |   length typeLs /= 1 || length typeRs /= 1 = []
    |   elem op ["+"] = case (typeL, typeR) of
                            (TypeVoid, _) -> typeLinkingFailure "Binary + TypeVoid"
                            (_, TypeVoid) -> typeLinkingFailure "Binary + TypeVoid"
                            ((Object (Name ["java", "lang", "String"])), _) -> [(Object (Name ["java", "lang", "String"]))]
                            (_, (Object (Name ["java", "lang", "String"]))) -> [(Object (Name ["java", "lang", "String"]))]
                            (_, _) -> if typeLInt && typeRInt then typeLR' else typeLinkingFailure "Binary +"
    |   elem op ["-", "*", "/", "%"] = if typeLInt && typeRInt then typeLR' else typeLinkingFailure "Binary arithematic op"
    |   elem op ["<", ">", "<=", ">="] = if typeLInt && typeRInt then [TypeBoolean] else typeLinkingFailure "Binary comparision op"
    |   elem op ["&&", "||", "&", "|"] = if typeLBool && typeRBool then [TypeBoolean] else typeLinkingFailure "Binary logical op"
    |   elem op ["==", "!="] = if null equality then typeLinkingFailure $ "Binary " ++ (show typeL) ++ op ++ (show typeR) else [TypeBoolean]
    |   elem op ["="] = if assignRL && (finalArrayLength db imps su exprL) then [typeL] else typeLinkingFailure $ "Binary =" ++ (show typeL) ++ (show typeR) ++ (show $ assignConversion db typeR typeL)
    where
        typeLs = case filter filterNonFunction $ typeLinkingExpr db imps su exprL of
                            [] -> typeLinkingFailure $ "Binary typeL no match: type(left) " ++ op ++ " type(right)" ++ (show exprL) ++ (show exprR)
                            [l] -> [l]
                            a -> typeLinkingFailure $ "Binary typeL multi match: type(left) " ++ op ++ " type(right)" ++ (show exprL) ++ (show exprR) ++ (show a)
        [typeL] = typeLs
        typeRs = case filter filterNonFunction $ typeLinkingExpr db imps su exprR of
                            [] -> typeLinkingFailure $ "Binary typeR no match: type(left) " ++ op ++ " type(right)" ++ (show exprL) ++ (show exprR)
                            [r] -> [r]
                            a -> typeLinkingFailure $ "Binary typeR multi match: type(left) " ++ op ++ " type(right)" ++ (show exprL) ++ (show exprR) ++ (show a)
        [typeR] = typeRs
        
        assignRL = not . null $ assignConversion db typeR typeL
        typeLR = case (null $ castConversion db typeL typeR, null $ castConversion db typeR typeL) of
                    (True, True) -> []
                    (False, _) -> [typeR]
                    (_, False) -> [typeL]
        typeLR' = map widenType typeLR
        typeLInt = not . null $ castConversion db typeL TypeInt
        typeRInt = not . null $ castConversion db typeR TypeInt
        typeLBool= not . null $ castConversion db typeL TypeBoolean
        typeRBool = not . null $ castConversion db typeR TypeBoolean
        equality = equalityCheck db typeL typeR