3

我的目标是在 python 中找到循环语句的开始和结束的行号。

示例场景

#A.py
Line1: a=0                  
Line2: while a<5:           
Line3:    print a          
Line4:    a=a+1 

Desired output:
Start of a loop Line2 
End of a loop   Line4 

当前解析器代码

#parser.py
with open(a) as f:
    tree = ast.parse(f.read())
taskline=[]
for node in ast.walk(tree):
    if isinstance(node, (ast.For)) or isinstance(node,(ast.While)):                        
        print node.lineno-1  <-- This give line number on for the start of a loop              

我想实现上述输出。我使用 AST 来解析给定的文件并确定循环的发生。通过 AST 解析,我能够找到循环开始的行号,但循环结束的行号尚未确定。有什么办法可以解析整个循环语句并确定它的开始和结束行号?

4

4 回答 4

6

一个节点在它的列表While中有它的语句。node.body循环的最后一行while是列表的最后一个元素。我不知道你为什么要减去一个(除非你的文件a有一个你想假装不存在的评论):

$ cat a.py
a = 0 
while a < 5:
    print a
    a += 1
for i in (1, 2, 3): 
    pass
$ cat ast_ex.py
import ast

with open('a.py') as f:
    tree = ast.parse(f.read())

for node in ast.walk(tree):
    if isinstance(node, (ast.For, ast.While)):
        print 'node:', node, 'at line:', node.lineno
        print 'body of loop ends at:', node.body[-1].lineno
$ python ast_ex.py 
node: <_ast.While object at 0x8017a8e50> at line: 2
body of loop ends at: 4
node: <_ast.For object at 0x8017ac0d0> at line: 5
body of loop ends at: 6

循环中的第一行是 in body[0](这可能与循环body[-1]中只有一个语句相同)。

于 2013-07-30T11:13:02.743 回答
0

它可能很复杂,但您可以尝试以下算法。

1. Count the number of white spaces before while. say it ident(you can use something like this len(a) - len(a.lstrip()) )
2. countinue reading the next line and counting the white spaces before the line say currIdent.
3. when ever currIdent = ident, then end of loop is line before it.
于 2013-07-30T10:46:15.547 回答
0

我对这个ast模块不是很熟悉,但是下面的代码在一些测试示例中对我有用。它返回一个 2 元组列表,文件中的每个循环一个,每个元组看起来像(start_line, end_line).

def get_loop_boundaries(fname):
    boundaries = []

    with open(fname) as f:
        tree = ast.parse(f.read())

    for node in ast.walk(tree):
        if isinstance(node, (ast.For)) or isinstance(node,(ast.While)):
            loop_start = node.lineno

            # body attribute is a list of nodes, one for each line in the loop
            # the lineno of the last node will give us the ending line
            loop_end = node.body[-1].lineno

            # add 2-tuple of starting and ending lines for current loop to list
            boundaries.append((loop_start, loop_end))
    # return a list of starting and ending lines for all loops in fname file
    return boundaries

我刚刚意识到函数的主要逻辑可以更简洁地写成列表理解:

return [(node.lineno, node.body[-1].lineno) for node in ast.walk(tree) if isinstance(node, (ast.For, ast.While))]
于 2013-07-30T11:08:29.470 回答
0

Torek 的回答非常好,我自己也尝试在我的程序中使用它,但我们还有另一种方法可以做到。'ast' 类提供了一个名为 'end_lineno' 的功能,就像 lineno 一样。这可用于查找循环结束的 lineno。请参考文档

import ast
with open('a.py') as f:
     tree = ast.parse(f.read())

for node in ast.walk(tree):
    if isinstance(node, (ast.For, ast.While)):
       print 'node:', node, 'at line:', node.lineno
       print 'body of loop ends at:', node.end_lineno
于 2021-06-05T05:48:01.153 回答