pyparsing, forward и recursion - PullRequest
       6

pyparsing, forward и recursion

9 голосов
/ 10 ноября 2010

Я использую pyparsing для анализа файлов vcd (дамп изменения значения). По сути, я хочу прочитать в файлах, разобрать его во внутренний словарь и манипулировать значениями.

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

В файлах vcd у вас есть «области», которые включают провода и, возможно, некоторые более глубокие (вложенные) области. Думайте о них как об уровнях.

Итак, в моем файле:

$scope module toplevel $end
$scope module midlevel $end
$var wire a $end
$var wire b $end
$upscope $end
$var wire c $end
$var wire d $end
$var wire e $end
$scope module extralevel $end
$var wire f $end
$var wire g $end
$upscope $end
$var wire h $end
$var wire i $end
$upscope $end

Таким образом, «верхний уровень» охватывает все (a - i), «средний уровень» имеет (a - b), «extralevel» имеет (f - g) и т. Д.

Вот мой код (фрагмент) для разбора этого раздела:

scope_header = Group(Literal('$scope') + Word(alphas) + Word(alphas) + \
                     Literal('$end'))

wire_map = Group(Literal('$var') + Literal('wire') + Word(alphas) + \
                 Literal('$end'))

scope_footer = Group(Literal('$upscope') + Literal('$end'))

scope = Forward()
scope << (scope_header + ZeroOrMore(wire_map) + ZeroOrMore(scope) + \
          ZeroOrMore(wire_map) + scope_footer)

Теперь, что я думал, что случается так, что, когда он попадает в каждую область, он отслеживает каждый «уровень», и я получаю структуру, содержащую вложенные области. Тем не менее, это ошибки на

$scope module extralevel $end

сказав, что ожидает '$ upscope'.

Так что я знаю, что неправильно использую рекурсию. Кто-нибудь может мне помочь? Дайте мне знать, если мне нужно предоставить больше информации.

Спасибо !!!!

Ответы [ 3 ]

9 голосов
/ 10 ноября 2010

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

Если у синтаксического анализатора есть режим отладки, в котором он печатает свое дерево синтаксического анализа, вы сможете увидеть это немедленно. Но вкратце, вы говорите, что существует ноль или более карт, за которыми следует ноль или более областей, а затем ноль или более карт, поэтому, если есть область действия, а затем карта, вы уже прошли поле области действия, любые другие области являются недействительными. Если язык, используемый pyparsing, поддерживает «или», вы можете использовать:

scope << (scope_header + ZeroOrMore((wire_map | scope)) + scope_footer)
6 голосов
/ 10 ноября 2010

Пожалуйста, выберите правильный ответ @ ZackBloom, он сразу его интуитивно понял, даже не зная синтаксиса pyparsing.

Всего несколько комментариев / предложений по вашей грамматике:

С ответом, опубликованным выше, вы можете визуализировать вложение, используя pprint и метод pyparsing asList() в ParseResults:

res = scope.parseString(vcd)

from pprint import pprint
pprint(res.asList())

Предоставление:

[[['$scope', 'module', 'toplevel', '$end'],
  [['$scope', 'module', 'midlevel', '$end'],
   ['$var', 'wire', 'a', '$end'],
   ['$var', 'wire', 'b', '$end'],
   ['$upscope', '$end']],
  ['$var', 'wire', 'c', '$end'],
  ['$var', 'wire', 'd', '$end'],
  ['$var', 'wire', 'e', '$end'],
  [['$scope', 'module', 'extralevel', '$end'],
   ['$var', 'wire', 'f', '$end'],
   ['$var', 'wire', 'g', '$end'],
   ['$upscope', '$end']],
  ['$var', 'wire', 'h', '$end'],
  ['$var', 'wire', 'i', '$end'],
  ['$upscope', '$end']]]

Так что теперь у вас есть хорошо структурированные результаты. Но вы можете немного навести порядок. Во-первых, теперь, когда у вас есть структура, вам не нужны все эти токены $scope, $end и т. Д. Конечно, вы можете просто перешагнуть через них при навигации по проанализированным результатам, но вы также можете использовать пиапарсинг, просто отбросив их из проанализированного вывода (поскольку результаты теперь структурированы, вы ничего не теряете). Измените определения вашего синтаксического анализатора на:

SCOPE, VAR, UPSCOPE, END = map(Suppress, 
                                 "$scope $var $upscope $end".split())
MODULE, WIRE = map(Literal, "module wire".split())

scope_header = Group(SCOPE + MODULE + Word(alphas) + END)
wire_map = Group(VAR + WIRE + Word(alphas) + END) 
scope_footer = (UPSCOPE + END) 

(Нет необходимости группировать scope_footer - все в этом выражении подавлено, поэтому Group просто даст вам пустой список.)

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

[[['module', 'toplevel'],
  [['module', 'midlevel'], ['wire', 'a'], ['wire', 'b']],
  ['wire', 'c'],
  ['wire', 'd'],
  ['wire', 'e'],
  [['module', 'extralevel'], ['wire', 'f'], ['wire', 'g']],
  ['wire', 'h'],
  ['wire', 'i']]]

На риск слишком большого количества группировок, я бы также предложил Group содержание вашего выражения scope, например:

scope << Group(scope_header + 
               Group(ZeroOrMore((wire_map | scope))) + 
               scope_footer) 

, который дает эти результаты:

[[['module', 'toplevel'],
  [[['module', 'midlevel'], [['wire', 'a'], ['wire', 'b']]],
   ['wire', 'c'],
   ['wire', 'd'],
   ['wire', 'e'],
   [['module', 'extralevel'], [['wire', 'f'], ['wire', 'g']]],
   ['wire', 'h'],
   ['wire', 'i']]]]

Теперь каждый результат области действия имеет 2 предсказуемых элемента: заголовок модуля и список проводов или подскопов. Эта предсказуемость значительно упростит написание рекурсивного кода, который будет перемещаться по результатам:

res = scope.parseString(vcd)
def dumpScope(parsedTokens, indent=''):
    module,contents = parsedTokens
    print indent + '- ' + module[1]
    for item in contents:
        if item[0]=='wire':
            print indent + '  wire: ' + item[1]
        else:
            dumpScope(item, indent+'  ')
dumpScope(res[0])

который выглядит как:

- toplevel
  - midlevel
    wire: a
    wire: b
  wire: c
  wire: d
  wire: e
  - extralevel
    wire: f
    wire: g
  wire: h
  wire: i

Хороший первый вопрос, добро пожаловать в SO и pyparsing!

1 голос
/ 06 февраля 2013

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

Verilog_VCD 1.0

...