pyparsing: grammar for list of Dictionaries (erlang)
Question: I'm trying to build a grammar to parse an Erlang tagged tuple list, and map
this to a Dict in pyparsing. I'm having problems when I have a list of Dicts.
The grammar works if the Dict has just one element, but when I add a second
can't work out now to get it to parse.

Current (simplified grammar code (I removed the bits of the language not
necessary in this case):

    
    
    #!/usr/bin/env python2.7
    
    from pyparsing import *
    
    # Erlang config file definition:
    erlangAtom = Word( alphas + '_')
    erlangString = dblQuotedString.setParseAction( removeQuotes )
    
    erlangValue = Forward()
    erlangList = Forward()
    
    erlangElements = delimitedList( erlangValue )
    erlangCSList = Suppress('[') + erlangElements + Suppress(']')
    erlangList <<= Group( erlangCSList )
    erlangTaggedTuple = Group( Suppress('{') + erlangAtom + Suppress(',') +
                               erlangValue + Suppress('}') )
    erlangDict = Dict( Suppress('[') + delimitedList( erlangTaggedTuple ) + 
                       Suppress(']') )
    
    erlangValue <<= ( erlangAtom | erlangString |
                      erlangTaggedTuple |
                      erlangDict | erlangList )
    
    if __name__ == "__main__":
        working = """
    [{foo,"bar"}, {baz, "bar2"}]
    """
    
        broken = """
    [
        [{foo,"bar"}, {baz, "bar2"}],
        [{foo,"bob"}, {baz, "fez"}]
    ]
    """
        w = erlangValue.parseString(working)
        print w.dump()
    
        b = erlangValue.parseString(broken)
        print "b[0]:", b[0].dump()
        print "b[1]:", b[1].dump()
    

This gives:

    
    
    [['foo', 'bar'], ['baz', 'bar2']]
    - baz: bar2
    - foo: bar
    
    b[0]: [['foo', 'bar'], ['baz', 'bar2'], ['foo', 'bob'], ['baz', 'fez']]
    - baz: fez
    - foo: bob
    
    b[1]:
    Traceback (most recent call last):
      File "./erl_testcase.py", line 39, in <module>
        print "b[1]:", b[1].dump()
      File "/Library/Python/2.7/site-packages/pyparsing.py", line 317, in __getitem__
        return self.__toklist[i]
    IndexError: list index out of range
    

i.e. `working` works, but `broken` doesn't parse as two lists.

Any ideas?

**Edit:** Tweaked testcase to be more explicit about expected output.


Answer: Ok, so I have never worked with pyparsing before, so excuse me if my solution
does not make sense. Here we go:

As far as I understand what you need is three main structures. The most common
mistake you made was grouping delimitedLists. They are already grouped, so you
have an issue of double grouping. Here are my definitions:

for {a,"b"}:

    
    
    erlangTaggedTuple = Dict(Group(Suppress('{') + erlangAtom + Suppress(',') + erlangValue + Suppress('}') ))
    

for [{a,"b"}, {c,"d"}]:

    
    
    erlangDict = Suppress('[') + delimitedList( erlangTaggedTuple ) + Suppress(']')
    

for the rest:

    
    
    erlangList <<= Suppress('[') + delimitedList( Group(erlangDict|erlangList) ) + Suppress(']')
    

So my fix for your code is:

    
    
    #!/usr/bin/env python2.7
    
    from pyparsing import *
    
    # Erlang config file definition:
    erlangAtom = Word( alphas + '_')
    erlangString = dblQuotedString.setParseAction( removeQuotes )
    
    erlangValue = Forward()
    erlangList = Forward()
    
    erlangTaggedTuple = Dict(Group(Suppress('{') + erlangAtom + Suppress(',') +
                               erlangValue + Suppress('}') ))
    erlangDict = Suppress('[') + delimitedList( erlangTaggedTuple ) + Suppress(']') 
    erlangList <<= Suppress('[') + delimitedList( Group(erlangDict|erlangList) ) + Suppress(']')
    
    erlangValue <<= ( erlangAtom | erlangString |
                      erlangTaggedTuple |
                      erlangDict| erlangList )
    
    if __name__ == "__main__":
        working = """
    [{foo,"bar"}, {baz, "bar2"}]
    """
    
        broken = """
    [
        [{foo,"bar"}, {baz, "bar2"}],
        [{foo,"bob"}, {baz, "fez"}]
    ]
    """
        w = erlangValue.parseString(working)
        print w.dump()
    
        b = erlangValue.parseString(broken)
        print "b[0]:", b[0].dump()
        print "b[1]:", b[1].dump()
    

Which gives the output:

    
    
    [['foo', 'bar'], ['baz', 'bar2']]
    - baz: bar2
    - foo: bar
    b[0]: [['foo', 'bar'], ['baz', 'bar2']]
    - baz: bar2
    - foo: bar
    b[1]: [['foo', 'bob'], ['baz', 'fez']]
    - baz: fez
    - foo: bob
    

Hope that helps, cheers!

