package org.faabtech.brainfuck; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; /** * The {@link BrainfuckEngine} class is an implementation of the original <code>brainfuck</code> * language. * * @author Fabian M. */ public class BrainfuckEngine { /** * The memory thats available for this <code>brainfuck</code> program. */ protected byte[] data; /** * The data pointer that points to the current index in the {@link BrainfuckEngine#data} memory array. */ protected int dataPointer = 0; /** * The character pointer that points to the current index of the character array * of value of its file or string. */ protected int charPointer = 0; /** * The {@link BrainfuckEngine#fileReader} allows use to read from a file if one is specified. */ protected BufferedReader fileReader; /** * The {@link BrainfuckEngine#consoleReader} allows us to read from the console for the ',' keyword. */ protected InputStreamReader consoleReader; /** * The {@link BrainfuckEngine#outWriter} allows us to write to the console. */ protected OutputStream outWriter; /** * The current line the engine is at. */ protected int lineCount = 0; /** * The current column the engine is at. */ protected int columnCount = 0; /** * The {@link Token} class contains tokens in <code>brainfuck</code>. * * @author Fabian M. */ protected static class Token { public final static char NEXT = '>'; public final static char PREVIOUS = '<'; public final static char PLUS = '+'; public final static char MINUS = '-'; public final static char OUTPUT = '.'; public final static char INPUT = ','; public final static char BRACKET_LEFT = '['; public final static char BRACKET_RIGHT = ']'; } /** * Constructs a new {@link BrainfuckEngine} instance. * * @param cells * The amount of memory cells. */ public BrainfuckEngine(int cells) { this(cells, new PrintStream(System.out), System.in); } /** * Constructs a new {@link BrainfuckEngine} instance. * * @param cells * The amount of mem interpret(content);ory cells. * @param out The outputstream of this program. */ public BrainfuckEngine(int cells, OutputStream out) { this(cells, out, System.in); } /** * Constructs a new {@link BrainfuckEngine} instance. * * @param cells * The amount of memory cells. * @param out The printstream of this program. * @param in The outputstream of this program. */ public BrainfuckEngine(int cells, OutputStream out, InputStream in) { initate(cells); outWriter = out; consoleReader = new InputStreamReader(in); } /** * Initiate this instance. */ protected void initate(int cells) { data = new byte[cells]; dataPointer = 0; charPointer = 0; } /** * Interprets the given file. * * @param file * The file to interpret. * @throws Exception */ public void interpret(File file) throws Exception { fileReader = new BufferedReader(new FileReader(file)); String content = ""; String line = ""; while((line = fileReader.readLine()) != null) { content += line; lineCount++; } interpret(content); } /** * Interprets the given string. * * @param str * The string to interpret. * @throws Exception */ public void interpret(String str) throws Exception { for (; charPointer < str.length(); charPointer++) interpret(str.charAt(charPointer), str.toCharArray()); initate(data.length); } /** * Interprets the given char * * @param c * The char to interpret. * @throws Exception */ protected void interpret(char c, char[] chars) throws Exception { switch (c) { case Token.NEXT: // increment the data pointer (to point to the next cell to the // right). if ((dataPointer + 1) > data.length) { throw new Exception("Error on line " + lineCount + ", column " + columnCount + ":" + "data pointer (" + dataPointer + ") on postion " + charPointer + "" + " out of range."); } dataPointer++; break; case Token.PREVIOUS: // decrement the data pointer (to point to the next cell to the // left). if ((dataPointer - 1) < 0) { throw new Exception("Error on line " + lineCount + ", column " + columnCount + ":" + "data pointer (" + dataPointer + ") on postion " + charPointer + " " + "negative."); } dataPointer--; break; case Token.PLUS: // increment (increase by one) the byte at the data pointer. if ((data[dataPointer] + 1) > Integer.MAX_VALUE) { throw new Exception("Error on line " + lineCount + ", column " + columnCount + ":" + "byte value at data pointer (" + dataPointer + ") " + " on postion " + charPointer + " higher than byte max value."); } data[dataPointer]++; break; case Token.MINUS: // decrement (decrease by one) the byte at the data pointer. /*if ((data[dataPointer] - 1) < 0) { throw new Exception("Error on line " + lineCount + ", column " + columnCount + ":" + "at data pointer " + dataPointer + " on postion " + charPointer + ": Value can not be lower than zero."); }*/ data[dataPointer]--; break; case Token.OUTPUT: // Output the byte at the current index in a character. outWriter.write((byte) data[dataPointer]); break; case Token.INPUT: // accept one byte of input, storing its value in the byte at the data pointer. data[dataPointer] = (byte) consoleReader.read(); break; case Token.BRACKET_LEFT: if (data[dataPointer] == 0) { int i = 1; while (i > 0) { char c2 = chars[++charPointer]; if (c2 == Token.BRACKET_LEFT) i++; else if (c2 == Token.BRACKET_RIGHT) i--; } } break; case Token.BRACKET_RIGHT: int i = 1; while (i > 0) { char c2 = chars[--charPointer]; if (c2 == Token.BRACKET_LEFT) i--; else if (c2 == Token.BRACKET_RIGHT) i++; } charPointer--; break; } columnCount++; } }