javaloopsnested-loopsinterpreterbrainfuck

How do I implement the looping functionality in my BrainFuck Interpreter?


There's multiple questions here already, but I'll still proceed. This is a simple BrainFuck interpreter. I figured out all the other symbols, but I can't figure out how to implement loops. Can anyone help?

package com.lang.bfinterpreter;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;


import com.lang.exceptions.TapeSizeExceededException;


public class Interpreter {

    private Interpreter() {
        super();
    }

    private static String getCode(final String inputFile) throws IOException {
        String code = "";
        
        // store the entire code
        final BufferedReader br = new BufferedReader(new FileReader(inputFile));
        for (String line = br.readLine(); line != null; line = br.readLine()) {
            code += line;
        }
        br.close();

        return code;
    }
    
    public static void interpret(final String inputFile) throws IOException,TapeSizeExceededException,IndexOutOfBoundsException {
        // get the program as a string
        final String code = getCode(inputFile);

        // create the Turing tape (static size)
        Character[] tape = new Character[12000];
        Integer indexPointer = 0;
        for (int i = 0; i != 12000; i++) {
            switch (code.toCharArray()[i]) {
                case ',':
                    tape[indexPointer] = (char) System.in.read();
                    break;
                
                case '.':
                    System.out.println(tape[indexPointer]);
                    break;

                case '+':
                    tape[indexPointer]++;
                    break;

                case '-':
                    tape[indexPointer]--;
                    break;

                case '>':
                    if (indexPointer == 11999) {
                        throw new IndexOutOfBoundsException();
                    }
                    else {
                        indexPointer++;
                    }
                    break;

                case '<':
                    if (indexPointer == 0) {
                        throw new IndexOutOfBoundsException();
                    }
                    else {
                        indexPointer--;
                    }
                    break;
                    
                case '[':
                    // I have a feeling I'll need stack to store nested loops   
                    break;
                    
                    case ']':
                    // I have a feeling I'll need stack to store nested loops   
                    break;
                    
                default:
                    break;
            }
        }

    } 
}

I have a feeling that I will need to use Stack, but I just can't seem to figure out how. I have constructed expression evaluators before... will this require the same logic?


Solution

  • The most challenging part, I suppose, is finding the matching brackets. After you find where the matching bracket is, you can just check tape[indexPointer]'s value, and set i to the position after it, which should be rather easy to do.

    Given an opening bracket at index i in code, to find its matching close bracket, you just need to go to the right of i in code. You start with an stack with a single [ in it - this is the [ at i. Every time you encounter a new [, you push it onto the stack. Every time you encounter a ], you pop a [ from the stack - this ] you encountered matches the [ you popped! When you popped the last [ from the stack (i.e. when the stack becomes empty), you know you have found the matching close bracket of the open bracket at i.

    In code, you don't even need a Stack. You can just use an int to encode how many elements are in the stack - increment it when you push, decrement it when you pop.

    private static int findMatchingCloseBracketAfterOpenBracket(char[] code, int openBracketIndex) {
        // parameter validations omitted
        int stack = 1;
        for (int i = openBracketIndex + 1; i < code.length ; i++) {
            if (code[i] == '[') {
                stack++;
            } else if (code[i] == ']') {
                stack--;
            }
            if (stack == 0) {
                return i;
            }
        }
        return -1; // brackets not balanced!
    }
    

    To find the matching [ of a ], the idea is same, except you go the other direction, and reverse the push and pop actions.

    private static int findMatchingOpenBracketBeforeCloseBracket(char[] code, int closeBracketIndex) {
        // parameter validations omitted
        int stack = 1;
        for (int i = closeBracketIndex - 1; i >= 0 ; i--) {
            if (code[i] == '[') {
                stack--;
            } else if (code[i] == ']') {
                stack++;
            }
            if (stack == 0) {
                return i;
            }
        }
        return -1; // brackets not balanced!
    }
    

    (Refactoring the code duplication here is left as an exercise for the reader)