pyparsing: грамматика для списка словарей (erlang)

Я пытаюсь создать грамматику для анализа списка кортежей с тегами Erlang и сопоставить его с Dict в pyparsing. У меня проблемы, когда у меня есть список Dicts. Грамматика работает, если Dict имеет только один элемент, но когда я добавляю второй, теперь не могу заставить его анализировать.

Текущий (упрощенный код грамматики (я убрал ненужные в данном случае биты языка):

#!/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()

Это дает:

[['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

то есть working работает, но broken не анализируется как два списка.

Любые идеи?

Редактировать: изменен тестовый пример, чтобы более точно указывать ожидаемый результат.


person DaveR    schedule 26.02.2014    source источник
comment
Можете ли вы добавить код, который фактически печатает ваш вывод?   -  person Nathaniel Waisbrot    schedule 27.02.2014
comment
У меня есть - см. нижнюю часть примера (print erlangValue.parseString(working).dump())   -  person DaveR    schedule 27.02.2014


Ответы (2)


Итак, я никогда раньше не работал с pyparsing, так что извините, если мое решение не имеет смысла. Вот так:

Насколько я понимаю, вам нужны три основные структуры. Самой распространенной ошибкой, которую вы совершали, была группировка списков с разделителями. Они уже сгруппированы, поэтому у вас есть проблема двойной группировки. Вот мои определения:

для {а, "б"}:

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

для [{a,"b"}, {c,"d"}]:

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

что касается прочего:

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

Итак, мое исправление для вашего кода:

#!/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()

Что дает вывод:

[['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

Надеюсь, это поможет, ура!

person user3387819    schedule 07.03.2014
comment
Это здорово - спасибо! Все выглядит хорошо с небольшим тестовым случаем; надеюсь, я смогу понять, как расширить это до полной грамматики. Награда заслуженная :) - person DaveR; 08.03.2014
comment
Я рад, что смог помочь :) - person user3387819; 08.03.2014

Я не могу понять, почему он не работает, потому что ваш код очень похож на пример JSON, который прекрасно обрабатывает вложенные списки.

Но проблема, кажется, происходит в этой строке

erlangElements = delimitedList( erlangValue )

где, если erlangValue являются списками, они добавляются вместо cons'd. Вы можете обходить это с помощью

erlangElements = delimitedList( Group(erlangValue) )

который добавляет дополнительный слой списка вокруг самого верхнего элемента, но предотвращает слияние ваших подсписков.

person Nathaniel Waisbrot    schedule 27.02.2014
comment
Итак, если я это сделаю, то pyparsing группирует (а не объединяет списки), но словарь все равно не анализируется: `[[[['foo', 'bar'], ['baz', 'bar2'] ], [['фу', 'боб'], ['баз', 'фес']]]]` - person DaveR; 27.02.2014
comment
Он разбирает для меня. Однако у вас нет ни одного словаря. Вы получаете список, содержащий список из двух словарей. Итак, result[0][0] и result[0][1] — ваши два словаря. (Вы ожидаете чего-то другого?) - person Nathaniel Waisbrot; 27.02.2014
comment
Это может сработать для меня (придется перепроверить большую грамматику), но добавление дополнительного уровня списков означает, что структура данных Erlang не соответствует чистому Python, что проблематично. Дело в том, что я не понимаю, почему текущий код работает неправильно - в нем уже есть Group() в определении erlangList... - person DaveR; 28.02.2014
comment
Итак, я вернулся к примеру JSON, но он страдает точно такой же проблемой - см. stackoverflow.com/questions/22081239/ Итак, либо мы что-то полностью упускаем, либо это что-то фундаментальное в pyparsing... - person DaveR; 28.02.2014