def operatorPrecedence(base, operators):
    """
    This re-implements pyparsing's operatorPrecedence function.

    It gets rid of a few annoying bugs, like always putting operators inside
    a Group, and matching the whole grammar with Forward first (there may
    actually be a reason for that, but I couldn't find it). It doesn't
    support trinary expressions, but they should be easy to add if it turns
    out I need them.
    """

    # The full expression, used to provide sub-expressions
    expression = Forward()

    # The initial expression
    last = base | Suppress('(') + expression + Suppress(')')

    def parse_operator(expr, arity, association, action=None, extra=None):
        return expr, arity, association, action, extra

    for op in operators:
        # Use a function to default action to None
        expr, arity, association, action, extra = parse_operator(*op)

        # Check that the arity is valid
        if arity < 1 or arity > 2:
            raise Exception("Arity must be unary (1) or binary (2)")

        if association not in (opAssoc.LEFT, opAssoc.RIGHT):
            raise Exception("Association must be LEFT or RIGHT")

        # This will contain the expression
        this = Forward()

        # Create an expression based on the association and arity
        if association is opAssoc.LEFT:
            new_last = (last | extra) if extra else last
            if arity == 1:
                operator_expression = new_last + OneOrMore(expr)
            elif arity == 2:
                operator_expression = last + OneOrMore(expr + new_last)
        elif association is opAssoc.RIGHT:
            new_this = (this | extra) if extra else this
            if arity == 1:
                operator_expression = expr + new_this
            # Currently no operator uses this, so marking it nocover for now
            elif arity == 2:                                      # nocover
                operator_expression = last + OneOrMore(new_this)  # nocover

        # Set the parse action for the operator
        if action is not None:
            operator_expression.setParseAction(action)

        this <<= (operator_expression | last)
        last = this

    # Set the full expression and return it
    expression <<= last
    return expression
This re-implements pyparsing's operatorPrecedence function.

    It gets rid of a few annoying bugs, like always putting operators inside
    a Group, and matching the whole grammar with Forward first (there may
    actually be a reason for that, but I couldn't find it). It doesn't
    support trinary expressions, but they should be easy to add if it turns
    out I need them.