4

I am attempting to implement an interpreter for brainfuck and as of now, I am just using a series of if/elif statements.

if(i == ">"):
    ...
elif(i == "<"):
    ...
elif(i == "+"):
    ...
elif(i == "-"):
    ...

However, this seems very clunky and un-pythonic to me. Is there a better (cleaner/faster/more aesthetically pleasing) way to implement this?

cjm
  • 814
  • 1
  • 11
  • 26
  • Study parsers and lexers... – Robert Harvey Jul 31 '13 at 17:25
  • 2
    Wrong approach. Look for: Lexers, Tokenizers, Parsers – Nadh Jul 31 '13 at 17:26
  • 2
    A dictionary that translates tokens to actions, for one... – StoryTeller - Unslander Monica Jul 31 '13 at 17:26
  • 1
    Possible duplicate of http://stackoverflow.com/questions/60208/replacements-for-switch-statement-in-python – Markus Unterwaditzer Jul 31 '13 at 17:29
  • 2
    So funny that people complain when a perfectly reasonable question is asked and judge without better knowledge... indeed, many brainf*ck interpreters are such if-else loops... and the question is quite valid if perhaps duplicate. +1 from me – Antti Haapala -- Слава Україні Jul 31 '13 at 17:39
  • 4
    As a side note, don't put extra parens around your `if` conditions in Python. It makes your code less Pythonic and harder to read. (An experienced Python dev will assume you've got the parens there for a reason—overriding precedence, or multi-line continuation, whatever—and waste mental effort looking for that reason.) – abarnert Jul 31 '13 at 17:46
  • @mipadi: Well, the parser is just an iterable over the tokens, the tokenizer is just an iterable over the lexemes, the lexer is just an iterable over the source characters, and the source is a string, which is already an iterable over the characters/lexemes/tokens, so `for ch in source:` already _is_ all of the above, and isn't overkill. :) But I know what you meant. – abarnert Jul 31 '13 at 17:49
  • Bad comments @Nadh, RobertHarvey. This has nothing to do with "proper" lexers/parsers and your advice is akin to commending sledgehammer for shelling peanuts – Nas Banov Mar 31 '16 at 13:34

1 Answers1

6

I have a quick implementation of a Brainfuck interpreter for Python in a GitHub repo. In a nutshell, though, you could keep a dictionary, where the keys are the Brainfuck characters and the values are function (or method) objects, and then dispatch on that. Something like this:

instructions = {
  '+': increment,
  '-': decrement,
  # Other functions
}

def run(tape):
  ch = next_token(tape)
  if ch in instructions:
    instructions[ch]()

(Not an actual implementation, just a quick illustration.)

mipadi
  • 398,885
  • 90
  • 523
  • 479
  • 2
    +1. As a general rule, almost any chain of more than a handful of `if foo=='a':` `elif foo=='b':` lines is better written this way, using a dict to dispatch to functions. However, you might want to use `instructions.get(ch, default_func)` instead of the explicit `in` check. (Whether `default_func` is a NOP, an error handler, or something different depends on what you're writing, of course.) – abarnert Jul 31 '13 at 17:45
  • woahhhhhhhh are you telling me that `instructions[ch]()` works like that??? – Stephan Jul 31 '13 at 17:48
  • 1
    @Stephan: If `instructions` is a dict whose values are callable, then `instructions[ch]` is a callable, so `instructions[ch]()` is a call expression. (Functions, bound methods, and other callables are first-class values in Python, a fact that's worth learning early and using regularly!) – abarnert Jul 31 '13 at 17:50
  • @abarnert [woah](http://googlegif.com/wp-content/uploads/2012/08/1620742673_532dd66d_mind_blown_xlarge1.jpeg) – Stephan Jul 31 '13 at 17:53