pyparsing: вложенное выражение с простой арифметикой - PullRequest
0 голосов
/ 16 января 2019

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

(A, B, 2 * C, 3 * ( D, E, 2 * F, 3 *(G, H)), I )

Выход должен разворачивать арифметику:

( A, B, C, C, D, E, F, F, G, H, G, H, G, H, D, E, F, F, G, H, G, H, G, H, D, E, F, F, G, H, G, H, G, H, I )

Может ли кто-нибудь дать мне подсказку, как подойти к проблеме?

Я начал следующим образом: поскольку есть только операция умножения, я решил использовать символ '*' в качестве разделителя в несколько странном списке:

import pyparsing as pp

oddDelim = pp.Or([',', '*'])
weirdList = pp.Optional(',').suppress() + \
            pp.delimitedList(pp.Or([pp.alphas, pp.pyparsing_common.number]), delim = oddDelim, combine = False) + \
            pp.Optional('*').suppress()
nestedTest = pp.nestedExpr(content = weirdList)

Используя это выражение nestedTest, я получаю разумный результат:

[['A', 'B', 2, 'C', 3, ['D', 'E', 2, 'F', 3, ['G', 'H']], 'I']]

но я не знаю, как мне разобрать токены, чтобы правильно развернуть арифметику.

Вместо последовательной итерации по токенам в цикле FOR, в идеале я хотел бы начать разворачивать арифметику с наивысшей степени вложенности и постепенно снижаться. Но я не знаю, как ...

Является ли nestedExpr подходом? Или я должен изменить подход и использовать Forward или, возможно, infixNotation? Я очень новичок в pyparsing, я был бы очень признателен, если бы я получил некоторые советы / идеи по этому поводу.

Заранее большое спасибо за помощь!

Ура, Pau

1 Ответ

0 голосов
/ 16 января 2019

Если вы хотите использовать Forward () для прокрутки нашей собственной рекурсивной грамматики, лучше всего начать с написания BNF для вашей грамматики.Это поможет вам вначале подумать о проблемном пространстве, а затем потом заняться кодированием.

Вот примерный BNF для того, что вы опубликовали:

list_expr ::= '(' list_item [',' list_item]* ')'
list_item ::= term | mult_term | list_expr
mult_term ::= integer '*' list_item
term      ::= A-Z

То естькаждый список, заключенный в круглые скобки, содержит список элементов, разделенных запятыми, и каждый элемент может представлять собой односимвольный термин, выражение умножения для целого числа, '*' и элемент или вложенный список в другом наборе скобок.

Чтобы перевести это на pyparsing, работайте снизу вверх, чтобы определить каждое выражение.Например, определите термин, используя новый класс Char (который представляет собой один символ из строки разрешенных символов):

term = pp.Char("ABCDEFGHI... etc.")

Вам потребуется использовать Forward для list_item, так как для этого потребуются выражениякоторые еще не определены, поэтому Forward () дает вам заполнитель.Затем, когда вы определили term, mult_term и list_expr, используя «<< =», чтобы «вставить» определение в существующий заполнитель, например так: </p>

list_item <<= term | mult_term | list_expr

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

Группировка здесь проста, это делается с помощью (), что довольно стандартно, и infixNotation будет обрабатывать ихкак таковой по умолчанию.

Далее определите, что представляют собой операнды самого низкого уровня.У вас есть два типа операндов: целые числа и одиночные буквенные символы.

Два оператора: '*' для умножения и ',' для сложения.

Поскольку вы запрашивали только предложения, яЯ остановлюсь на этом и позволю вам самостоятельно решать следующие проблемы.

...