Как разобрать петли Python, используя скрипт Python?

Моя главная цель - разобрать циклы Python так, чтобы я мог вставить несколько операторов для моего анализа.

Normal code:
#A.py

[code Starts]
.
.
.
while [condition]:
    [statements]
    [statements]
    [statements]

.
.
.
[code ends]

Инструментированный код:

Normal code:
#A.py

[code Starts]
.
.
.
count =0                                    <---------- inserted code 
print "Entry of loop"                       <---------- inserted code
while [condition]:
    print "Iteration Number " + count++     <---------- inserted code
    [statements]
    [statements]
    [statements]
print "Exit of loop"                        <---------- inserted code
.
.
.
[code ends]

Моя цель - вставить вышеуказанные коды в соответствующие места с соответствующими отступами. Цикл также может быть циклом for. Для достижения вышеупомянутого инструментированного кода мне нужно проанализировать Loops в файле A.py и вставить этот код.

Есть хороший способ разобрать эти петли и получить номер строки петли, чтобы я мог инструмент?

Спасибо

3 ответа

pyparsing содержит образец файла, содержащий полный (?) синтаксический анализатор Python. В долгосрочной перспективе это может быть интересным вариантом, особенно если / когда ваш аналитический проект получит больше возможностей:

Разбор, как правило, трудная задача. Вы можете использовать Python-библиотеку Pygments, которая является библиотекой подсветки синтаксиса. Это может отличаться от того, что вы собираетесь делать, но это не так. В конце концов, раскраска кода в основном добавляет информацию о цвете в блоки кода.

Используя PythonLexer, вы можете извлекать токены для каждой строки и добавлять любые комментарии. Это пригодится, если вы хотите не просто работать с циклами while, но и с циклами for, если операторы...

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

Следующий код делает то, что вы хотите, но он совсем не устойчив:

def add_info_on_loops(iterable):
    in_loop = False
    for line in iterable:
        if not in_loop:
            if line.startswith('for ') or line.startswith('while '):
                in_loop = True
                yield 'count = 0\n'
                yield 'print "Entry of loop"\n'
                yield line
                yield '    print "Iteration Number:", count'
                yield '    count += 1\n'
            else:
                yield line
        else:
            if not line.startswith('    '):
                in_loop = False
                yield 'print "Exit of loop"\n'
            yield line

Использование:

>>> code = StringIO("""[code Starts]
... .
... .
... .
... while [condition]:
...     [statements]
...     [statements]
...     [statements]
... 
... .
... .
... .
... [code ends]""")
>>> print ''.join(add_info_on_loops(code))
[code Starts]
.
.
.
count = 0
print "Entry of loop"
while [condition]:
    print "Iteration Number:", count    count += 1
    [statements]
    [statements]
    [statements]
print "Exit of loop"

.
.
.
[code ends]

Подводные камни кода:

  1. Код обрабатывает только циклы на верхнем уровне. Что-то вроде if condition: for x in a: ... не распознается Это может быть решено путем удаления строк пробела перед проверкой, есть ли у нас цикл или нет (но вы должны учитывать различные уровни отступов и т. Д.)
  2. Код прерывается всякий раз, когда в цикле есть строка без отступа. Это произойдет, например, если вы "разделите" код пустой строкой, и среда IDE удалит пробел. Решением может быть ожидание непустой строки без отступа вместо строки без отступа.
  3. Код не обрабатывает вкладки для отступа (легко исправляется)
  4. Код не обрабатывает однострочные циклы (например, for x in a: print x). В этом случае вы получите неправильный вывод. Легко исправить, проверяя, есть ли что-то после :,
  5. Используя один count Переменная является проблематичной, если вы хотите добавить поддержку для вложенных циклов. Вы, вероятно, должны иметь где-то целочисленный идентификатор и использовать имена переменных, такие как count_0, count_1 с идентификатором, который увеличивается каждый раз, когда вы находите новый цикл.
  6. Код не обрабатывает выражения с круглыми скобками, которые не имеют пробелов на клавиатуре. например for(a,b) in x: не определяется как цикл, в то время как for (a,b) in x: обнаружен. Это может быть легко решено. Сначала вы проверяете, начинается ли строка с for а также while и следующий символ не должен быть буквой, цифрой, подчеркиванием (на самом деле в python3 вы также можете использовать символы Юникода, и это становится сложнее проверить, но это возможно).
  7. Код не обрабатывает исходный код, который заканчивается строкой с отступом цикла. например for x in a: indented_last_line_of_code() выход print не будет добавлено. (легко исправить добавив проверку на in_loop вне for функции, чтобы увидеть, есть ли у нас такая ситуация).

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

Другие вопросы по тегам