3

My objective is to find the line numbers of the start and the end of a loop statement in python.

Example scenario

#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 

Current parser code

#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              

I wanted to achieve the above output. I use AST to parse a given file and determine the occurrence of loops. With AST parsing i am able to find line number for the start of the loop but the line number for ending of the loop is yet to be determined. Is there any way i could parse an entire loop statement and determine its starting and ending line number ?

4 Answers 4

6

A While node has its statements in its node.body list. The last line of the while loop is the last element of the list. I don't know why you are subtracting one (unless your file a has a comment that you want to pretend does not exist):

$ 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

The first line in the loop is in body[0] (which may be the same as body[-1] if there is only one statement in the loop).

Sign up to request clarification or add additional context in comments.

Comments

0

It could be complecated but you can try following algorithm.

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.

Comments

0

I am not super familiar with the ast module, but the code below worked for me on a few test examples. It returns a list of 2-tuples, one for each loop in the file, where each tuple looks like (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

I just realized the main logic of the function can be written much more concisely as a list comprehension:

return [(node.lineno, node.body[-1].lineno) for node in ast.walk(tree) if isinstance(node, (ast.For, ast.While))]

1 Comment

torek's answer clarified some things regarding the ast module for me, so I improved my function.
0

Torek's answer is really good and I myself tried to use it in my program but there's another way we can do it. 'ast' class provides a functionality named 'end_lineno' just like lineno. This can be used to find lineno of end of loop. Please refer to docs

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.