I'm creating a Brainfuck parser (in a BASIC dialect) ultimately to create an interpreter but i've realise it's not as straight forward as i first thought. My problem is that i need a way to accurately parse the matching loop operators within a Brainfuck program. This is an example program:
,>,>++++++++[<------<------>>-]
<<[>[>+>+<<-]>>[<<+>>-]<<<-]
>>>++++++[<++++++++>-],<.>.
'[' = start of loop
']' = end of loop
I need to record the start and end point of each matching loop operator so i can jump around the source as needed. Some loops are alone, some are nested.
What would be the best way to parse this? I was thinking maybe move through the source file creating a 2D array (or such like) recording the start and end positions of each matching operator, but this seems like a lot of 'to'ing and fro'ing' through the source. Is this the best way to do it?
More info: Brainfuck homepage
EDIT: Sample code in any language greatly appreciated.
Python 3.0 example of the stack algorithm described by the other posters:
(Well, to be honest, this only finds matching brackets. I don't know brainf*ck, so what to do next, I have no idea.)
Each time you find a '[', push the current position (or another "marker" token or a "context") on a stack. When you come accross a ']', you're at the end of the loop, and you can pop the marker token from the stack.
Since in BF the '[' already checks for a condition and may need jump past the ']', you may want to have a flag indicating that instructions shall be skipped in the current loop context.
I don't have sample code, but.
I might try using a stack, along with an algorithm like this:
It looks like this question has become a "post your bf interpreter" poll.
So here's mine that I just got working:
Interesting enough, just a couple days ago, I was writing a brainf*ck interpreter in Java.
One of the issues I was having was that the explanation of the commands at the official page was insufficient, and did not mention the part about nested loops. The Wikipedia page on Brainf*ck has a Commands subsection which describes the correct behavior.
Basically to summarize the problem, the official page says when an instruction is a
[
and the current memory location is0
, then jump to the next]
. The correct behavior is to jump to the corresponding]
, not the next one.One way to achieve this behavior is to keep track of the level of nesting. I ended up implementing this by having a counter which kept track of the nesting level.
The following is part of the interpreter's main loop:
Here is the legend for the variable names:
memory
-- the memory cells for the data.p
-- pointer to the current memory cell location.inst
-- an array holding the instructions.pc
-- program counter; points to the current instruction.nesting
-- level of the nesting of the current loop.nesting
of0
means that the current location is not in a nested loop.Basically, when a loop opening
[
is encountered, the current memory location is checked to see if the value is0
. If that is the case, awhile
loop is entered to jump to the corresponding]
.The way the nesting is handled is as follows:
If an
[
is encountered while seeking for the corresponding loop closing]
, then thenesting
variable is incremented by1
in order to indicate that we have entered a nested loop.If an
]
is encountered, and:a. If the
nesting
variable is greater than0
, then thenesting
variable is decremented by1
to indicate that we've left a nested loop.b. If the
nesting
variable is0
, then we know that the end of the loop has been encountered, so seeking the end of the loop in thewhile
loop is terminated by executing abreak
statement.Now, the next part is to handle the closing of the loop by
]
. Similar to the opening of the loop, it will use thenesting
counter in order to determine the current nesting level of the loop, and try to find the corresponding loop opening[
.This method may not be the most elegant way to do things, but it seems like it is resource-friendly because it only requires one extra variable to use as a counter for the current nesting level.
(Of course, "resource-friendly" is ignoring the fact that this interpreter was written in Java -- I just wanted to write some quick code and Java just happened to be what I wrote it in.)
This should work. You need to modify it somewhat. That is actually standard example how brainfuck interpreter works. I modified it to use in my app, brackets are handled there: