def parse_nestings(string, only_curl=False):
    r"""
    References:
        http://stackoverflow.com/questions/4801403/pyparsing-nested-mutiple-opener-clo

    CommandLine:
        python -m utool.util_gridsearch parse_nestings:1 --show

    Example:
        >>> # DISABLE_DOCTEST
        >>> from utool.util_gridsearch import *  # NOQA
        >>> import utool as ut
        >>> string = r'lambda u: sign(u) * abs(u)**3.0 * greater(u, 0)'
        >>> parsed_blocks = parse_nestings(string)
        >>> recombined = recombine_nestings(parsed_blocks)
        >>> print('PARSED_BLOCKS = ' + ut.repr3(parsed_blocks, nl=1))
        >>> print('recombined = %r' % (recombined,))
        >>> print('orig       = %r' % (string,))
        PARSED_BLOCKS = [
            ('nonNested', 'lambda u: sign'),
            ('paren', [('ITEM', '('), ('nonNested', 'u'), ('ITEM', ')')]),
            ('nonNested', '* abs'),
            ('paren', [('ITEM', '('), ('nonNested', 'u'), ('ITEM', ')')]),
            ('nonNested', '**3.0 * greater'),
            ('paren', [('ITEM', '('), ('nonNested', 'u, 0'), ('ITEM', ')')]),
        ]

    Example:
        >>> # DISABLE_DOCTEST
        >>> from utool.util_gridsearch import *  # NOQA
        >>> import utool as ut
        >>> string = r'\chapter{Identification \textbf{foobar} workflow}\label{chap:application}'
        >>> parsed_blocks = parse_nestings(string)
        >>> print('PARSED_BLOCKS = ' + ut.repr3(parsed_blocks, nl=1))
        PARSED_BLOCKS = [
            ('nonNested', '\\chapter'),
            ('curl', [('ITEM', '{'), ('nonNested', 'Identification \\textbf'), ('curl', [('ITEM', '{'), ('nonNested', 'foobar'), ('ITEM', '}')]), ('nonNested', 'workflow'), ('ITEM', '}')]),
            ('nonNested', '\\label'),
            ('curl', [('ITEM', '{'), ('nonNested', 'chap:application'), ('ITEM', '}')]),
        ]
    """
    import utool as ut  # NOQA
    import pyparsing as pp

    def as_tagged(parent, doctag=None):
        """Returns the parse results as XML. Tags are created for tokens and lists that have defined results names."""
        namedItems = dict((v[1], k) for (k, vlist) in parent._ParseResults__tokdict.items()
                          for v in vlist)
        # collapse out indents if formatting is not desired
        parentTag = None
        if doctag is not None:
            parentTag = doctag
        else:
            if parent._ParseResults__name:
                parentTag = parent._ParseResults__name
        if not parentTag:
            parentTag = "ITEM"
        out = []
        for i, res in enumerate(parent._ParseResults__toklist):
            if isinstance(res, pp.ParseResults):
                if i in namedItems:
                    child = as_tagged(res, namedItems[i])
                else:
                    child = as_tagged(res, None)
                out.append(child)
            else:
                # individual token, see if there is a name for it
                resTag = None
                if i in namedItems:
                    resTag = namedItems[i]
                if not resTag:
                    resTag = "ITEM"
                child = (resTag, pp._ustr(res))
                out += [child]
        return (parentTag, out)

    def combine_nested(opener, closer, content, name=None):
        r"""
        opener, closer, content = '(', ')', nest_body
        """
        import utool as ut  # NOQA
        ret1 = pp.Forward()
        _NEST = ut.identity
        #_NEST = pp.Suppress
        opener_ = _NEST(opener)
        closer_ = _NEST(closer)
        group = pp.Group(opener_ + pp.ZeroOrMore(content) + closer_)
        ret2 = ret1 << group
        if ret2 is None:
            ret2 = ret1
        else:
            pass
            #raise AssertionError('Weird pyparsing behavior. Comment this line if encountered. pp.__version__ = %r' % (pp.__version__,))
        if name is None:
            ret3 = ret2
        else:
            ret3 = ret2.setResultsName(name)
        assert ret3 is not None, 'cannot have a None return'
        return ret3

    # Current Best Grammar
    nest_body = pp.Forward()
    nestedParens   = combine_nested('(', ')', content=nest_body, name='paren')
    nestedBrackets = combine_nested('[', ']', content=nest_body, name='brak')
    nestedCurlies  = combine_nested('{', '}', content=nest_body, name='curl')

    nonBracePrintables = ''.join(c for c in pp.printables if c not in '(){}[]') + ' '
    nonNested = pp.Word(nonBracePrintables).setResultsName('nonNested')
    nonNested = nonNested.leaveWhitespace()

    # if with_curl and not with_paren and not with_brak:
    if only_curl:
        # TODO figure out how to chain |
        nest_body << (nonNested | nestedCurlies)
    else:
        nest_body << (nonNested | nestedParens | nestedBrackets | nestedCurlies)

    nest_body = nest_body.leaveWhitespace()
    parser = pp.ZeroOrMore(nest_body)

    debug_ = ut.VERBOSE

    if len(string) > 0:
        tokens = parser.parseString(string)
        if debug_:
            print('string = %r' % (string,))
            print('tokens List: ' + ut.repr3(tokens.asList()))
            print('tokens XML: ' + tokens.asXML())
        parsed_blocks = as_tagged(tokens)[1]
        if debug_:
            print('PARSED_BLOCKS = ' + ut.repr3(parsed_blocks, nl=1))
    else:
        parsed_blocks = []
    return parsed_blocks
def parse_nestings ( string , only_curl = False ) : import utool as ut import pyparsing as pp def as_tagged ( parent , doctag = None ) : namedItems = dict ( ( v [ 1 ] , k ) for ( k , vlist ) in parent . _ParseResults__tokdict . items ( ) for v in vlist ) parentTag = None if doctag is not None : parentTag = doctag else : if parent . _ParseResults__name : parentTag = parent . _ParseResults__name if not parentTag : parentTag = "ITEM" out = [ ] for i , res in enumerate ( parent . _ParseResults__toklist ) : if isinstance ( res , pp . ParseResults ) : if i in namedItems : child = as_tagged ( res , namedItems [ i ] ) else : child = as_tagged ( res , None ) out . append ( child ) else : resTag = None if i in namedItems : resTag = namedItems [ i ] if not resTag : resTag = "ITEM" child = ( resTag , pp . _ustr ( res ) ) out += [ child ] return ( parentTag , out ) def combine_nested ( opener , closer , content , name = None ) : import utool as ut ret1 = pp . Forward ( ) _NEST = ut . identity opener_ = _NEST ( opener ) closer_ = _NEST ( closer ) group = pp . Group ( opener_ + pp . ZeroOrMore ( content ) + closer_ ) ret2 = ret1 << group if ret2 is None : ret2 = ret1 else : pass if name is None : ret3 = ret2 else : ret3 = ret2 . setResultsName ( name ) assert ret3 is not None , 'cannot have a None return' return ret3 nest_body = pp . Forward ( ) nestedParens = combine_nested ( '(' , ')' , content = nest_body , name = 'paren' ) nestedBrackets = combine_nested ( '[' , ']' , content = nest_body , name = 'brak' ) nestedCurlies = combine_nested ( '{' , '}' , content = nest_body , name = 'curl' ) nonBracePrintables = '' . join ( c for c in pp . printables if c not in '(){}[]' ) + ' ' nonNested = pp . Word ( nonBracePrintables ) . setResultsName ( 'nonNested' ) nonNested = nonNested . leaveWhitespace ( ) if only_curl : nest_body << ( nonNested | nestedCurlies ) else : nest_body << ( nonNested | nestedParens | nestedBrackets | nestedCurlies ) nest_body = nest_body . leaveWhitespace ( ) parser = pp . ZeroOrMore ( nest_body ) debug_ = ut . VERBOSE if len ( string ) > 0 : tokens = parser . parseString ( string ) if debug_ : print ( 'string = %r' % ( string , ) ) print ( 'tokens List: ' + ut . repr3 ( tokens . asList ( ) ) ) print ( 'tokens XML: ' + tokens . asXML ( ) ) parsed_blocks = as_tagged ( tokens ) [ 1 ] if debug_ : print ( 'PARSED_BLOCKS = ' + ut . repr3 ( parsed_blocks , nl = 1 ) ) else : parsed_blocks = [ ] return parsed_blocks
r"""
    References:
        http://stackoverflow.com/questions/4801403/pyparsing-nested-mutiple-opener-clo

    CommandLine:
        python -m utool.util_gridsearch parse_nestings:1 --show

    Example:
        >>> # DISABLE_DOCTEST
        >>> from utool.util_gridsearch import *  # NOQA
        >>> import utool as ut
        >>> string = r'lambda u: sign(u) * abs(u)**3.0 * greater(u, 0)'
        >>> parsed_blocks = parse_nestings(string)
        >>> recombined = recombine_nestings(parsed_blocks)
        >>> print('PARSED_BLOCKS = ' + ut.repr3(parsed_blocks, nl=1))
        >>> print('recombined = %r' % (recombined,))
        >>> print('orig       = %r' % (string,))
        PARSED_BLOCKS = [
            ('nonNested', 'lambda u: sign'),
            ('paren', [('ITEM', '('), ('nonNested', 'u'), ('ITEM', ')')]),
            ('nonNested', '* abs'),
            ('paren', [('ITEM', '('), ('nonNested', 'u'), ('ITEM', ')')]),
            ('nonNested', '**3.0 * greater'),
            ('paren', [('ITEM', '('), ('nonNested', 'u, 0'), ('ITEM', ')')]),
        ]

    Example:
        >>> # DISABLE_DOCTEST
        >>> from utool.util_gridsearch import *  # NOQA
        >>> import utool as ut
        >>> string = r'\chapter{Identification \textbf{foobar} workflow}\label{chap:application}'
        >>> parsed_blocks = parse_nestings(string)
        >>> print('PARSED_BLOCKS = ' + ut.repr3(parsed_blocks, nl=1))
        PARSED_BLOCKS = [
            ('nonNested', '\\chapter'),
            ('curl', [('ITEM', '{'), ('nonNested', 'Identification \\textbf'), ('curl', [('ITEM', '{'), ('nonNested', 'foobar'), ('ITEM', '}')]), ('nonNested', 'workflow'), ('ITEM', '}')]),
            ('nonNested', '\\label'),
            ('curl', [('ITEM', '{'), ('nonNested', 'chap:application'), ('ITEM', '}')]),
        ]