-1

Trying to make a brainfuck interpreter in python from scratch just for fun, I'm almost done with the code however this small error is coming up whenever I try interpreting ",[>++++<]" Which is supposed to input a number, and multiply it by 4. It stops and gets this error

File ".\bfInterpreter.py", line 42, in endWhile
    if self.stack[self.pointer]: self.place = self.inWhile.pop(-1)
IndexError: pop from empty list

Any ideas?

here is the code that the error is in:

import sys
import time

class Operator:
    def __init__(self) -> None:
        self.pointer = 0
        self.place = 0
        self.inWhile = []
        self.stack = [0]

    def plus(self):
        self.stack[self.pointer]+=1

    def minus(self):
        self.stack[self.pointer]-=1

    def left(self):
        if self.pointer > 0: self.pointer -= 1
        else: raise IndexError

    def right(self):
        self.pointer += 1
        try: self.stack[self.pointer] = 0
        except: self.stack.append(0)

    def output(self):
        print(ascii(self.stack[self.pointer])) 

    def inp(self):
        val = input("Num: ")
        try: self.stack[self.pointer] = int(val)
        except: 
            print("Please input a number")
            self.inp()

    def startWhile(self):
        self.inWhile.append(self.place)
        print(self.inWhile)

    def endWhile(self):
        print(self.inWhile)
        if self.stack[self.pointer]: self.place = self.inWhile.pop(-1)

    def interpret(self, bf):
        self.place = 0
        while self.place < len(bf):
            if bf[self.place] == "+": self.plus()
            elif bf[self.place] == "-": self.minus()
            elif bf[self.place] == "<": self.left()
            elif bf[self.place] == ">": self.right()
            elif bf[self.place] == "[": self.startWhile(bf)
            elif bf[self.place] == "]": self.endWhile()
            elif bf[self.place] == ",": self.inp()
            elif bf[self.place] == ".": self.output()
            else: raise SyntaxError
            self.place += 1
        print(self.stack)           

def main():
    start = time.time()
    op = Operator()
    op.interpret(",[>++++<]")
    print(f"Ended in {time.time() - start} second(s)")

if __name__ == "__main__": main()

Edit: self.stack is basically the memory that brainfuck is allowed to access and edit, so the "[" starts a while loop, checking if a certain spot in the stack has reached 0 every cycle, and if it's not 0, it repeats the code in the brackets

  • How is `self.stack` maintained? It's not clear why the code checks `self.stack` and then pops from `self.inWhile`. – sj95126 Nov 17 '21 at 17:38
  • There's not enough code here for us to reproduce the error, you either need to provide the missing code for the rest of the class or (Much preferable!) change the functions so that we can just run the functions with the given input to make a [mcve] . In general, the error is telling you that `inWhile` is empty at the time you try to `pop` from it, so likely your loop is running `endwhile()` one too many times or something else emptied the list already – G. Anderson Nov 17 '21 at 17:48
  • Edited again, added the rest of the code, couldn't figure out how to just use those particular functions without the rest of the code to make the minimal reproducible example like you mentioned – Tristen Gordon Nov 17 '21 at 18:04
  • 1
    Is this your actual code? If I run it, I get the following error: `TypeError: startWhile() takes 1 positional argument but 2 were given` because of obvious programming errors in the script. – wovano Nov 17 '21 at 19:01
  • maybe first use `print()` to see what you really have in variables before line which makes problem. You could also use `print()` to see what you have in variables in other moments and which part of code is executed. It's called `"print debuging"`. It should help you see where is the problem. – furas Nov 17 '21 at 20:17
  • 1
    to make code more readable: don't put code in the same line as `if ...:` but in next line. The same with `elif`, `else`, `try`, `except`. See more in [PEP 8 -- Style Guide for Python Code](https://www.python.org/dev/peps/pep-0008/) – furas Nov 17 '21 at 20:18

1 Answers1

0

The main problem is self.inWhile.pop(-1) in command ] - it removes value from list and in next loop this list is empty - so pop(-1) raises your error message.

But there is more mistakes and problems in [ and ].

All commands runs self.place += 1 but ] shouldn't do this because this way it jump to first command after [ but it should run again [. Maybe if it would run again [ then it could add again value to inWhile and self.inWhile.pop(-1) whouldn't make problem.

But there is other problem with [ - it should check if value is 0 and jump to first command after ]. It would need another list to remember position of ] but it seems more complicated - because if at start value is 0 then it couldn't yet run ] and it couldn't add position to list. Better is to search matching ] in string with commands. The same method I used to search [ (instead of using inWhile).

Other problem is > you always try to set stack[place] = 0 but if you run > and next < and again > then stack[place] may already exists and it may have some value and stack[place] = 0 may remove this value. I rather use while loop with pointer >= len(stack) to append values only if stack is really too short.


Other problem is that program in branfuck runs loop forever because it doesn't have - to decreate inputed value. It needs also >. after loop to display result.

,[->++++<]>.

My version without inWhile but with loop which search matching [ or matching ].

import sys
import time

class Operator:
    
    def __init__(self):
        self.place = 0    # command place
        self.pointer = 0  # stack pointer
        self.stack = [0]  

    def input(self):
        #print('[DEBUG] input')
        while True:
            val = input("Number: ")
            try:
                self.stack[self.pointer] = int(val)
                break
            except: 
                print("Please input a integer number")

    def output(self):
        #print('[DEBUG] output')
        print(ascii(self.stack[self.pointer])) 

    def plus(self):
        #print('[DEBUG] plus')
        self.stack[self.pointer] += 1

    def minus(self):
        #print('[DEBUG] minus')
        self.stack[self.pointer] -= 1

    def left(self):
        #print('[DEBUG] left')
        if self.pointer > 0:
            self.pointer -= 1
        else:
            raise IndexError

    def right(self):
        #print('[DEBUG] right')
        self.pointer += 1
        while len(self.stack) <= self.pointer:
            self.stack.append(0)

    def start_loop(self):
        #print('[DEBUG] start_loop')
        #print('self.place:', self.place)

        if self.stack[self.pointer] == 0:
            # find matching `]` to jump to next command after `]`
            counter = 1
            while counter > 0:
                self.place += 1
                char = self.bf[self.place]
                if char == '[':
                    counter += 1
                elif char == ']':
                    counter -= 1
                #print('counter:', counter)    

    def end_loop(self):
        #print('[DEBUG] end_loop')
        #print('self.place:', self.place)

        # find matching `[` to run again `[` and all loop
        counter = 1
        while counter > 0:
            self.place -= 1
            char = self.bf[self.place]
            if char == '[':
                counter -= 1
            elif char == ']':
                counter += 1
            #print('counter:', counter)    

    def interpret(self, bf):
        self.bf = bf
        self.place = 0
        while self.place < len(bf):

            char = self.bf[self.place]

            #print('[DEBUG] interpret:', self.place, bf[self.place])

            if   char == ",":
                self.input()
                self.place += 1
            elif char == ".":
                self.output()
                self.place += 1
          
            elif char == "+":
                self.plus()
                self.place += 1
            elif char == "-":
                self.minus()
                self.place += 1
                
            elif char == "<":
                self.left()
                self.place += 1
            elif char == ">":
                self.right()
                self.place += 1
                
            elif char == "[":
                self.start_loop()
                self.place += 1
                
            elif char == "]":
                self.end_loop()
                # it has to be without `self.place += 1`
                
            else:
                raise SyntaxError

        print('stack:', self.stack)           

def main():
    start = time.time()

    op = Operator()
    op.interpret(",[->++++<]>.")  # input*4
    #op.interpret(",[->+>++>+++>++++<<<<]>.>.>.>.")  # input*1, input*2, input*3, input*4
    #op.interpret(",[->+<>>++<<>>>+++<<<>>>>++++<<<<]>.>.>.>.")  # input*1, input*2, input*3, input*4

    end = time.time()
    print(f"Ended in {end - start:.2f} second(s)")

if __name__ == "__main__":
    main()
furas
  • 134,197
  • 12
  • 106
  • 148