def evil(expr, lookup, operators, cast, reducer, tokenizer):
    """evil evaluates an expression according to the eval description given.

    :param expr: An expression to evaluate.
    :param lookup: A callable which takes a single pattern argument and returns
                   a set of results. The pattern can be anything that is not an
                   operator token or round brackets.
    :param operators: A precedence-ordered dictionary of (function, side)
                      tuples keyed on the operator token.
    :param reducer: A callable which takes a sequential list of values (from
                    operations or lookups) and combines them into a result.
                    Typical behaviour is that of the + operator. The return
                    type should be the same as cast.
    :param cast: A callable which transforms the results of the lookup into
                 the type expected by the operators and the type of the result.
    :param tokenizer: A callable which will break the query into tokens for
                      evaluation per the lookup and operators. Defaults to
                      setquery.query_tokenizer.
    :raises: SyntaxError
    :returns:

    """
    operators = OrderedDict((op[0], op[1:]) for op in operators)
    if "(" in operators or ")" in operators:
        raise ValueError("( and ) are reserved operators")

    operator_tokens = ["(", ")"] + operators.keys()
    tokens = iter(tokenizer(expr, operator_tokens))
    levels = [[]]

    while True:
        # Token evaluation and pattern lookups

        expr = levels.pop()           # The currently-constructed expression
        new_level = False             # We should step into a subexpression
        first_token = len(expr) == 0  # The first (sub)exp. token

        prev_op_side = None           # The side of the last-seen operator
        try:
            # Try to get the side of the last operator from an expression
            # which we are going to continue constructing.
            prev_op_side = operators[expr[-1]][1]
        except:
            pass

        for token in tokens:

            if token == "(":
                new_level = True
                break
            elif token == ")":
                break
            elif token in operators:
                op_side = operators[token][1]
                if first_token and op_side & OP_LEFT:
                    raise SyntaxError("Operators which act on expressions to "
                                      "their left or both sides cannot be at "
                                      "the beginning of an expression.")
                if prev_op_side is not None:
                    if prev_op_side & OP_RIGHT and op_side & OP_LEFT:
                        raise SyntaxError("Operators cannot be beside one "
                                          "another if they act on expressions "
                                          "facing one-another.")
                expr.append(token)
                prev_op_side = op_side
                continue
            else:
                expr.append(cast(lookup(token)))
                prev_op_side = None

            first_token = False

        if new_level:
            levels.append(expr)
            levels.append([])
            continue
        elif prev_op_side is not None and prev_op_side & OP_RIGHT:
            raise SyntaxError("Operators which act on expressions to their "
                              "right or both sides cannot be at the end of "
                              "an expression.")

        # Operator evaluation

        explen = len(expr)
        for op, (op_eval, op_side) in operators.iteritems():
            if op_side is OP_RIGHT:

                # Apply right-sided operators. We loop from the end backward so
                # that multiple such operators next to noe another are resolved
                # in the correct order
                t = explen - 1
                while t >= 0:
                    if expr[t] == op:
                        expr[t] = op_eval(expr[t + 1])
                        del expr[t + 1]
                        explen -= 1
                    t -= 1

            else:

                # Apply left- and both-sided operators. We loop forward so that
                # that multiple such operators next to one another are resolved
                # in the correct order.
                t = 0
                while t < explen:
                    if expr[t] == op:
                        # Apply left- or both-sided operators
                        if op_side is OP_LEFT:
                            expr[t] = op_eval(expr[t - 1])
                            del expr[t - 1]
                            t -= 1
                            explen -= 1
                        elif op_side is OP_BOTH:
                            expr[t] = op_eval(expr[t - 1], expr[t + 1])
                            del expr[t + 1], expr[t - 1]
                            t -= 1
                            explen -= 2
                    t += 1

        if len(levels) > 0:
            levels[-1].append(reducer(expr))
        else:
            break

    return reducer(expr)
evil evaluates an expression according to the eval description given.

    :param expr: An expression to evaluate.
    :param lookup: A callable which takes a single pattern argument and returns
                   a set of results. The pattern can be anything that is not an
                   operator token or round brackets.
    :param operators: A precedence-ordered dictionary of (function, side)
                      tuples keyed on the operator token.
    :param reducer: A callable which takes a sequential list of values (from
                    operations or lookups) and combines them into a result.
                    Typical behaviour is that of the + operator. The return
                    type should be the same as cast.
    :param cast: A callable which transforms the results of the lookup into
                 the type expected by the operators and the type of the result.
    :param tokenizer: A callable which will break the query into tokens for
                      evaluation per the lookup and operators. Defaults to
                      setquery.query_tokenizer.
    :raises: SyntaxError
    :returns: