def object_path_cmp(path1, path2):
    """
    Compare two object paths.

    :param path1: The first ObjectPath instance
    :param path2: The second ObjectPath instance
    :return: <0, 0, or >0 depending on whether the first arg is less, equal or
        greater than the second
    """
    if path1.object_type_name < path2.object_type_name:
        result = -1

    elif path1.object_type_name > path2.object_type_name:
        result = 1

    else:
        # I always thought of key and index path steps as separate.  The AST
        # lumps indices in with the previous key as a single path component.
        # The following splits the path components into individual comparable
        # values again.  Maybe I should not do this...
        path_vals1 = object_path_to_raw_values(path1)
        path_vals2 = object_path_to_raw_values(path2)
        result = iter_lex_cmp(
            path_vals1,
            path_vals2,
            object_path_component_cmp,
        )

    return result
Beispiel #2
0
    def __transform(self, ast):
        sorted_children = sorted(
            ast.operands,
            key=functools.cmp_to_key(observation_expression_cmp),
        )

        # Deduping only applies to ORs
        if ast.operator == "OR":
            deduped_children = [
                key.obj for key, _ in itertools.groupby(
                    sorted_children,
                    key=functools.cmp_to_key(observation_expression_cmp, ),
                )
            ]
        else:
            deduped_children = sorted_children

        changed = iter_lex_cmp(
            ast.operands,
            deduped_children,
            observation_expression_cmp,
        ) != 0

        ast.operands = deduped_children

        return ast, changed
    def __transform(self, ast):
        """
        Sort/dedupe children.  AND and OR can be treated identically.

        :param ast: The comparison expression AST
        :return: The same AST node, but with sorted children
        """
        sorted_children = sorted(
            ast.operands,
            key=functools.cmp_to_key(comparison_expression_cmp),
        )

        deduped_children = [
            # Apparently when using a key function, groupby()'s "keys" are the
            # key wrappers, not actual sequence values.  Obviously we don't
            # need key wrappers in our ASTs!
            k.obj for k, _ in itertools.groupby(
                sorted_children,
                key=functools.cmp_to_key(comparison_expression_cmp, ),
            )
        ]

        changed = iter_lex_cmp(
            ast.operands,
            deduped_children,
            comparison_expression_cmp,
        ) != 0

        ast.operands = deduped_children

        return ast, changed
Beispiel #4
0
def list_cmp(value1, value2):
    """
    Compare lists order-insensitively.

    Args:
        value1: The first ListConstant
        value2: The second ListConstant

    Returns:
        <0, 0, or >0 depending on whether the first arg is less, equal or
        greater than the second
    """

    # Achieve order-independence by sorting the lists first.
    sorted_value1 = sorted(
        value1.value,
        key=functools.cmp_to_key(constant_cmp),
    )

    sorted_value2 = sorted(
        value2.value,
        key=functools.cmp_to_key(constant_cmp),
    )

    result = iter_lex_cmp(sorted_value1, sorted_value2, constant_cmp)

    return result
def startstop_cmp(qual1, qual2):
    """
    Compare START/STOP qualifiers.  This lexicographically orders by start time,
    then stop time.
    """
    return iter_lex_cmp(
        (qual1.start_time, qual1.stop_time),
        (qual2.start_time, qual2.stop_time),
        generic_constant_cmp,
    )
Beispiel #6
0
def comparison_expression_cmp(expr1, expr2):
    """
    Compare two comparison expressions.  This is sensitive to the order of the
    expressions' sub-components.  To achieve an order-insensitive comparison,
    the sub-component ASTs must be ordered first.

    Args:
        expr1: The first comparison expression
        expr2: The second comparison expression

    Returns:
        <0, 0, or >0 depending on whether the first arg is less, equal or
        greater than the second
    """
    if isinstance(expr1, _ComparisonExpression) \
            and isinstance(expr2, _ComparisonExpression):
        result = simple_comparison_expression_cmp(expr1, expr2)

    # One is simple, one is compound.  Let's say... simple ones come first?
    elif isinstance(expr1, _ComparisonExpression):
        result = -1

    elif isinstance(expr2, _ComparisonExpression):
        result = 1

    # Both are compound: AND's before OR's?
    elif isinstance(expr1, AndBooleanExpression) \
            and isinstance(expr2, OrBooleanExpression):
        result = -1

    elif isinstance(expr1, OrBooleanExpression) \
            and isinstance(expr2, AndBooleanExpression):
        result = 1

    else:
        # Both compound, same boolean operator: sort according to contents.
        # This will order according to recursive invocations of this comparator,
        # on sub-expressions.
        result = iter_lex_cmp(
            expr1.operands,
            expr2.operands,
            comparison_expression_cmp,
        )

    return result
def observation_expression_cmp(expr1, expr2):
    """
    Compare two observation expression ASTs.  This is sensitive to the order of
    the expressions' sub-components.  To achieve an order-insensitive
    comparison, the ASTs must be canonically ordered first.

    Args:
        expr1: The first observation expression
        expr2: The second observation expression

    Returns:
        <0, 0, or >0 depending on whether the first arg is less, equal or
        greater than the second
    """
    type1 = type(expr1)
    type2 = type(expr2)

    type1_idx = _OBSERVATION_EXPRESSION_TYPE_ORDER.index(type1)
    type2_idx = _OBSERVATION_EXPRESSION_TYPE_ORDER.index(type2)

    if type1_idx != type2_idx:
        result = generic_cmp(type1_idx, type2_idx)

    # else, both exprs are of same type.

    # If they're simple, use contained comparison expression order
    elif type1 is ObservationExpression:
        result = comparison_expression_cmp(
            expr1.operand,
            expr2.operand,
        )

    elif isinstance(expr1, _CompoundObservationExpression):
        # Both compound, and of same type (and/or/followedby): sort according
        # to contents.
        result = iter_lex_cmp(
            expr1.operands,
            expr2.operands,
            observation_expression_cmp,
        )

    else:  # QualifiedObservationExpression
        # Both qualified.  Check qualifiers first; if they are the same,
        # use order of the qualified expressions.
        qual1_type = type(expr1.qualifier)
        qual2_type = type(expr2.qualifier)

        qual1_type_idx = _QUALIFIER_TYPE_ORDER.index(qual1_type)
        qual2_type_idx = _QUALIFIER_TYPE_ORDER.index(qual2_type)

        result = generic_cmp(qual1_type_idx, qual2_type_idx)

        if result == 0:
            # Same qualifier type; compare qualifier details
            qual_cmp = _QUALIFIER_COMPARATORS.get(qual1_type)
            if qual_cmp:
                result = qual_cmp(expr1.qualifier, expr2.qualifier)
            else:
                raise TypeError(
                    "Can't compare qualifier type: " + qual1_type.__name__, )

        if result == 0:
            # Same qualifier type and details; use qualified expression order
            result = observation_expression_cmp(
                expr1.observation_expression,
                expr2.observation_expression,
            )

    return result