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