4
\$\begingroup\$

I have this obsession with esoteric programming languages. So I decided to spiff up my previous Brainfuck interpreter.

# Simple BrainF*** interpreter

# Class that stores lang variables
class Lang(object):
    step = 0
    cell = [0] * 30000
    test_cell = [0] * 30000
    pos = 0
    test_pos = 0
    loop = False
    loop_ret = 0


# Main interpreter function
def interpreter():

    code_input = raw_input('Code: ')
    steps = len(code_input)

    while Lang.step < steps:

        if code_input[Lang.step] == '+':
            Lang.cell[Lang.pos] += 1

        elif code_input[Lang.step] == '-': 
            Lang.cell[Lang.pos] -= 1

        elif code_input[Lang.step] == '>':
            if Lang.pos < 30000:
                Lang.pos += 1
            elif Lang.pos > 30000:
                Lang.pos = 0

        elif code_input[Lang.step] == '<':
            if Lang.pos > 0:
                Lang.cell_pos -= 1
            elif Lang.pos < 0:
                Lang.pos = 30000

        elif code_input[Lang.step] == '[':
            if Lang.loop == False:
                Lang.loop_ret = Lang.step
                Lang.loop = True

        elif code_input[Lang.step] == ']':
            if Lang.cell[Lang.pos] != 0:
                Lang.step = Lang.loop_ret
            elif Lang.cell[Lang.pos] == 0:
                Lang.loop = False

        elif code_input[Lang.step] == '.':
            print str(chr(Lang.cell[Lang.pos]))

        elif code_input[Lang.step] == ',':
            Lang.cell[Lang.pos] = int(raw_input())

        elif code_input[Lang.step] == ":":
            Lang.test_cell[Lang.test_pos] += 1

        elif code_input[Lang.step] == ";":
            Lang.test_cell[Lang.test_pos] -= 1

        elif code_input[Lang.step] == "}":
            if Lang.test_pos > 30000:
                Lang.test_pos += 1
            elif Lang.test_pos < 30000:
                Lang.test_pos = 0

        elif code_input[Lang.step] == "{":
            if Lang.test_pos > 0:
                Lang.test_pos -= 1
            elif Lang.test_pos < 0:
                Lang.test_pos = 30000

        elif code_input[Lang.step] == "$":
            if Lang.test_cell[Lang.test_pos] == Lang.cell[Lang.pos]:
                print True 
            elif Lang.test_cell[Lang.test_pos] != Lang.cell[Lang.pos]:
                print False 

        Lang.step += 1


# Running the program
if __name__ == "__main__":
    interpreter()

If there are any issues, please mention them. All I'm looking for is any general improvements.

\$\endgroup\$
5
\$\begingroup\$

In a word: dictionaries.

You have a class that only has class attributes and lacks any methods; that could just be a dictionary:

lang = dict(
    step = 0,
    cell = [0] * 30000,
    test_cell = [0] * 30000,
    pos = 0,
    test_pos = 0,
    loop = False,
    loop_ret = 0
)

(Alternatively, if you really want attribute (foo.bar) rather than key (foo['bar']) access to the values, look into collections.namedtuple.)

You have a whole bunch of elifs; that could also be a dictionary (with some judiciously-named functions):

commands = {
    "+": increment_byte,
    "-": decrement_byte,
    ...
}

This makes your interpreter loop:

def interpreter():

    lang = dict(...)
    commands = {...}

    code_input = raw_input('Code: ')    
    steps = len(code_input)

    while lang['step'] < steps:           
        command = code_input[lang['step']]
        if command in commands:
            commands[command](lang)    
        lang['step'] += 1

along with e.g.:

def increment_byte(lang):
    """Increment the byte at the data pointer."""
    val = lang['cell'][lang['pos']]
    lang['cell'][lang['pos']] = ((val + 1) % 256)

(Note use of % per @user50399's answer.)

This has two advantages:

  • very simple loop in interpreter; and
  • commands acts as a syntax guide (covering @Dagg's comment).

You could also add some input validation:

def accept_input(lang):
    """Accept one char of input, storing its value in the byte at the data pointer."""
    while True:
        try:
            i = ord(raw_input("Enter char: "))
        except TypeError:
            pass
        else:
            if i in range(256):
                lang['cell'][lang['pos']] = i
                break
        print("Not a valid input.")

(Note switch to ord per @user50399's answer.)

\$\endgroup\$
4
\$\begingroup\$

You haven't implemented the language properly.

Nested loops are ignored. This makes it impossible to implement a large class of Brainfuck programs. It's hard enough to write Brainfuck programs even without this handicap.

The output routine incorrectly appends a newline to every character it prints.

The input routine incorrectly expects a string rather than a single character, and incorrectly parses the string as a number rather than extracting the character's ASCII code.

Your support for circular memory tape is wrong.

  • If Lang.pos = 29999 and the > command is encountered, then Lang.pos = 30000. Suppose you try a + operation then — that's an IndexError.
  • If Lang.pos = 30000 and the > command is encountered, the pointer doesn't move. That also makes the elif branch unreachable.
  • Analogous bugs exist for the < command. By the way, Python automatically wraps negative array indices for you anyway: Lang.cell[-1] is the last element of the list.

It's odd that interpreter() is a standalone function rather than a member of the Lang class. Also, why call the class Lang, when you could name it BrainfuckInterpreter?

You have implemented the non-wrapping dialect of Brainfuck. The original language treats each cell as an unsigned byte, doing all calculations modulo 256.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.