code: def split_expr(expr, op):
      
      res = []
  
      def rec(subexpr):
          if subexpr.__class__ is tuple and subexpr[0] is op:
              rec(subexpr[1])
              rec(subexpr[2])
          else:
              res.append(subexpr)
  
      rec(expr)
      return res
docs: Returns a list containing the top-level AND or OR operands in the
      expression 'expr', in the same (left-to-right) order as they appear in
      the expression.
  
      This can be handy e.g. for splitting (weak) reverse dependencies
      from 'select' and 'imply' into individual selects/implies.
  
      op:
        Either AND to get AND operands, or OR to get OR operands.
  
        (Having this as an operand might be more future-safe than having two
        hardcoded functions.)
  
  
      Pseudo-code examples:
  
        split_expr( A                    , OR  )  ->  [A]
        split_expr( A && B               , OR  )  ->  [A && B]
        split_expr( A || B               , OR  )  ->  [A, B]
        split_expr( A || B               , AND )  ->  [A || B]
        split_expr( A || B || (C && D)   , OR  )  ->  [A, B, C && D]
  
        # Second || is not at the top level
        split_expr( A || (B && (C || D)) , OR )  ->  [A, B && (C || D)]
  
        # Parentheses don't matter as long as we stay at the top level (don't
        # encounter any non-'op' nodes)
        split_expr( (A || B) || C        , OR )  ->  [A, B, C]
        split_expr( A || (B || C)        , OR )  ->  [A, B, C]