Search code examples
pythonnested-loops

How to decompress a nested loop in a file : Python


Input Example:

Blank  {Line0}
Loop 3 {Line1}
Blank  {Line1a}
Label2: Blank {Line2}
Blank  {Line3}
Loop 2 {Line4} 
Blank  {Line4a}      
Label1: Blank {Line5}       # I need to repeat the Jump statements as many times as mentioned in the Loop before the Label statement. Always The Loop statements precedes the immediate Jump statement. So here Line5, Lin6 and Line7 gets repeated twice. Jump to label1 so print line5, Line6 (since it has nothign going on) and Line7 is the end of Jump statement. Repeat it twice. Since Loop 2 {Line4} says do the following Jump Twice. 
Blank  {Line6}
Jump Label1 {Line7}
Blank  {Line8}
Jump Label2 {Line9} # This jumps back to Label2. Which repeats everything until this line including the expanded inner loop ... This is the nested loop.
Blank  {Line10}

Output Example:

Line0
Line1
Line1a
Line2
Line3
Line4
Line4a
Line5
Line6
Line7
Line5
Line6
Line7
Line8
Line9
Line2
Line3
Line4
Line4a
Line5
Line6
Line7
Line5
Line6
Line7
Line8
Line9
Line2
Line3
Line4
Line4a
Line5
Line6
Line7
Line5
Line6
Line7
Line8
Line9
Line10

I currently have a code that works for loops and repeated loops in a file. But breaks for the input file given above. I tried to implement @Samy Arous method from here: Using Python to Parse a File for Nested Loops but couldn't implement it. Moved this as a seperate question for clarity.

All the lines are just strings... It is a little complicated format so just gave a simple one. We can assume the Blank, JUMP, LOOP are all directing what needs to be done. Either just print it out or repeat based on the loop and Jump

My code:

import sys

inputtfile = sys.stdin
inputfileContents = open(r'path to input file')


def extract(line):
    return line[line.find('{') + 1: line.rfind('}')]

loopCount = 0
loopLines = []
inLoop = False
for line in inputfileContents:
    if line.startswith('Loop '):
        loopCount = int(line.split()[1])
        inLoop = True
    elif line.startswith('Jump '):
        loopLines.append(extract(line))
        for i in range(loopCount):
            for loopLine in loopLines:
                print loopLine

#reset state variables to track next possible loop
        loopCount = 0
        inLoop = False
        loopLines = []
    elif inLoop:
        loopLines.append(extract(line))
    else:
        print extract(line)

Solution

  • OK well if the input file is not yours, then you can't do much with the awful format. I think the easiest way to handle nesting is to use recursive functions.

    Few assumptions about weird input format:

    • The "looped lines" always start on the next line after "Loop".
    • I ignore the labels. For "jump", that just means "end loop"

    Anyway, the output I get seems to match your expected, but if my assumptions are incorrect, you'll need to modify this.

    with open(r'path to input file') as f:
        inputfileContents = f.readlines()
    
    def printLines(lines):
        nest_level = 0
        for i, line in enumerate(lines):
            print line.split('{')[-1].split('}')[0]
            if line.startswith('Jump '):
                nest_level -= 1
                if nest_level < 0:
                    return
            elif line.startswith('Loop '):
                nest_level += 1
                loopCount = int(line.split()[1])-1
                for cnt in range(loopCount):
                    printLines(lines[i+1:])
    
    printLines(inputfileContents)
    

    EDIT --> Here is a modified version that jumps to the label instead. Works with the current example input, but there's a lot of ways a bad input could break this.

    with open(r'path to input file') as f:
        inputfileContents = f.readlines()
    
    def printLines(lines):
        loop_cnt = []
        label_to_idx = {}
        for i, line in enumerate(lines):
            line = line.split('#')[0] #removing comments
            print line.split('{')[-1].split('}')[0]
            line_label = line.split('{')[0].strip()
            line_label = line_label.replace(':','') #I assume the colon is a typo?
            label_to_idx[line_label] = i
    
            if line.startswith('Jump '):
                if not loop_cnt:
                    return
                jump_label = line.split()[1]
                start = label_to_idx[jump_label]
                loopCount = loop_cnt.pop()
                for cnt in range(loopCount):
                    printLines(lines[start:])
            elif line.startswith('Loop '):
                loopCount = int(line.split()[1])-1
                loop_cnt.append(loopCount)
    
    printLines(inputfileContents)