Only very lightly tested (and the output includes white space). As with @Marius' answer (and the general rule about paren matching requiring a PDA), I use a stack. However, I have a little extra paranoia built in to mine.
def paren_matcher(string, opens, closes):
"""Yield (in order) the parts of a string that are contained
in matching parentheses. That is, upon encounting an "open
parenthesis" character (one in <opens>), we require a
corresponding "close parenthesis" character (the corresponding
one from <closes>) to close it.
If there are embedded <open>s they increment the count and
also require corresponding <close>s. If an <open> is closed
by the wrong <close>, we raise a ValueError.
"""
stack = []
if len(opens) != len(closes):
raise TypeError("opens and closes must have the same length")
# could make sure that no closes[i] is present in opens, but
# won't bother here...
result = []
for char in string:
# If it's an open parenthesis, push corresponding closer onto stack.
pos = opens.find(char)
if pos >= 0:
if result and not stack: # yield accumulated pre-paren stuff
yield ''.join(result)
result = []
result.append(char)
stack.append(closes[pos])
continue
result.append(char)
# If it's a close parenthesis, match it up.
pos = closes.find(char)
if pos >= 0:
if not stack or stack[-1] != char:
raise ValueError("unbalanced parentheses: %s" %
''.join(result))
stack.pop()
if not stack: # final paren closed
yield ''.join(result)
result = []
if stack:
raise ValueError("unclosed parentheses: %s" % ''.join(result))
if result:
yield ''.join(result)
print list(paren_matcher('(([a] b) c ) [d] (e) f', '([', ')]'))
print list(paren_matcher('foo (bar (baz))', '(', ')'))