import re


def parse_parentheticals(text, lparen='\(', rparen='\)'):
    """Parses the given text and returns a tree of parentheticals.

    Arguments:
        text (str): The input text.
        lparen (str): A regex for matching the left parenthetical delimiter.
        rparen (str): A regex for matching the right parenthetical delimiter.

    Returns:
        (dict | [str]): A dictionary representing the parse tree or a list of
        strings. Each node of the tree will have the following structure:

            {'parens': (l, r), 'text': []}

        where (l, r) are the parentheticals wrapping the text, and the list
        contains raw text and subnodes. For example, the following string

            'ab)c((d)ef)g()(hi'

        will return:

            {'parens': None,
             'text': ['ab',
                      ')',
                      'c',
                      {'parens': ('(', ')'),
                       'text': [{'parens': ('(', ')'), 'text': ['d']}, 'ef']},
                      'g',
                      {'parens': ('(', ')'), 'text': []},
                      {'parens': ('(', None), 'text': ['hi']}]}

        Unmatched lparens will be interpretted as regular text. Unmatched
        rparens will have None as their second parens tuple element. If there
        are no parentheticals, a list of text will be returned.
    """
    # Precompile regular expressions for ease of use.
    n_regex = re.compile(r'([^{}{}]*)'.format(lparen, rparen))
    l_regex = re.compile(r'({})'.format(lparen))
    r_regex = re.compile(r'({})'.format(rparen))

    # Build root of tree.
    tree = {'parens': (None, None),
            'text': []}

    context = [tree]
    rest = text

    # Keep parsing until nothing is left.
    while rest:
        node = context[0]

        # Match rparens.
        m = r_regex.match(rest)
        if m:
            if node['parens'] == (None, None):
                node['text'].append(m.group(1))
            else:
                node = context.pop(0)
                node['parens'] = (node['parens'][0], m.group(1))

            rest = rest[len(m.group(1)):]
            continue

        # Match lparens.
        m = l_regex.match(rest)
        if m:
            new_node = {'parens': (m.group(1), None),
                        'text': []}
            node['text'].append(new_node)
            context.insert(0, new_node)

            rest = rest[len(m.group(1)):]
            continue

        # Match text.
        m = n_regex.match(rest)
        if m:
            node['text'].append(m.group(1))

            rest = rest[len(m.group(1)):]

    # Remove highest level tree if whole string is parenthetical.
    if len(tree['text']) == 1:
        tree = [tree['text'][0]]

    return tree