Parse boolean expression containing and/or operators

def _parse_expression(s):
    """Parse boolean expression containing and/or operators"""

    # Converters for opeartor clauses
    operators = {
        'and': And,
        'or': Or,
        None: lambda *args: args[0]
    }

    # Pairing of end group symbols with start group symbols
    group_pairs = {
        ')': '(',
        ']': '['
    }

    scanner = re.compile(r'''
        (\s+) |              # space
        (\(|\[) |            # group_start
        (\)|\]) |            # group_end
        ((?:or|and)\b) |     # operator
        ([^\s\(\)\[\]]+) |   # variable
        (\Z) |               # end
        (.)                  # error
    ''', re.DOTALL | re.VERBOSE | re.UNICODE | re.IGNORECASE)

    # Parsed using two states and a stack of open clauses
    # At state 0 (not expect_operator): Expect variable, or parenthesis group
    #  start.
    # At state 1 (expect_operator): Expect operator, parenthesis group end, or
    #  end.
    expect_operator = False
    clause_stack = []
    current_clause = []
    clause_operator = None
    clause_symbol = None

    def close():
        prev_op, prev_symbol, prev_clause = clause_stack.pop()
        prev_clause.append(operators[clause_operator](*current_clause))
        return prev_op, prev_symbol, prev_clause

    for match in re.finditer(scanner, s):
        (space, group_start, group_end, operator, variable, end,
            error) = match.groups()

        if error is not None:
            raise ParseError('Invalid token in expression string: {}'.format(
                repr(match.group(0))), span=(match.start(), match.end()))
        elif space is not None:
            continue
        elif expect_operator and operator is not None:
            operator = operator.lower()
            if operator == 'and' and clause_operator != 'and':
                prev_term = current_clause.pop()
                clause_stack.append(
                    (clause_operator, clause_symbol, current_clause))
                current_clause = [prev_term]
            elif operator == 'or' and clause_operator == 'and':
                clause_operator, clause_symbol, current_clause = close()
            clause_operator = operator
            expect_operator = False
        elif expect_operator and group_end is not None:
            if clause_operator == 'and':
                clause_operator, clause_symbol, current_clause = close()
            if len(clause_stack) == 0:
                raise ParseError(
                    'Unbalanced parenthesis group in expression',
                    span=(match.start(), match.end()))
            if group_pairs[group_end] != clause_symbol:
                raise ParseError(
                    'Group started with {} ended with {}'.format(
                        clause_symbol, group_end),
                    span=(match.start(), match.end()))
            clause_operator, clause_symbol, current_clause = close()
        elif expect_operator and end is not None:
            if clause_operator == 'and':
                clause_operator, clause_symbol, current_clause = close()
        elif not expect_operator and variable is not None:
            current_clause.append(Variable(variable))
            expect_operator = True
        elif not expect_operator and group_start is not None:
            clause_stack.append(
                (clause_operator, clause_symbol, current_clause))
            current_clause = []
            clause_operator = None
            clause_symbol = group_start
        else:
            raise ParseError(
                'Invalid token in expression string: {!r}'.format(
                    match.group(0)),
                span=(match.start(), match.end()))

    if len(clause_stack) > 0:
        raise ParseError('Unbalanced parenthesis group in expression')

    expr = operators[clause_operator](*current_clause)
    return expr