package org.faabtech.brainfuck.impl;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import org.faabtech.brainfuck.BrainfuckEngine;
/**
* The {@link TrollScriptEngine} is an implementation for the
* <code>brainfuck<code> dialect
* <code>TrollScript</code>.
*
* @author Fabian M.
*/
public class TrollScriptEngine extends BrainfuckEngine {
/**
* The default length of a token.
*/
protected int defaultTokenLength = 3;
/**
* The {@link Token} class contains tokens in <code>TrollScript</code>.
*
* @author Fabian M.
*/
protected static class Token {
public final static String START = "tro";
public final static String NEXT = "ooo";
public final static String PREVIOUS = "ool";
public final static String PLUS = "olo";
public final static String MINUS = "oll";
public final static String OUTPUT = "loo";
public final static String INPUT = "lol";
public final static String BRACKET_LEFT = "llo";
public final static String BRACKET_RIGHT = "lll";
public final static String END = "ll.";
}
/**
* Constructs a new {@link TrollScriptEngine} instance.
*
* @param cells
* The amount of memory cells.
*/
public TrollScriptEngine(int cells) {
this(cells, new PrintStream(System.out), System.in);
}
/**
* Constructs a new {@link TrollScriptEngine} instance.
*
* @param cells
* The amount of memory cells.
* @param out
* The outputstream of this program.
*/
public TrollScriptEngine(int cells, OutputStream out) {
this(cells, out, System.in);
}
/**
* Constructs a new {@link TrollScriptEngine} instance.
*
* @param cells
* The amount of memory cells.
* @param out
* The printstream of this program.
* @param in
* The outputstream of this program.
*/
public TrollScriptEngine(int cells, OutputStream out, InputStream in) {
super(cells, out, in);
}
/**
* Interprets the given string.
*
* @param str
* The string to interpret.
* @throws Exception
*/
@Override
public void interpret(String str) throws Exception {
// Is this program already started?
boolean started = false;
// List with tokens.defaultTokenLenght
List<String> tokens = new ArrayList<String>();
// It fine that all TrollScript tokens are 3 characters long :)
// So we aren't going to loop through all characters.
for (; charPointer < str.length(); ) {
String token = "";
if (charPointer + defaultTokenLength <= str.length())
// The string we found.
token = str.substring(charPointer, charPointer + defaultTokenLength);
else
token = str.substring(charPointer, charPointer
+ (str.length() - charPointer));
// Is it a token?
if (isValidToken(token)) {
if (token.equalsIgnoreCase(Token.START))
started = true;
else if (token.equalsIgnoreCase(Token.END))
break;
else if (started)
tokens.add(token);
charPointer += defaultTokenLength;
} else if (charPointer + defaultTokenLength > str.length()) {
charPointer += (str.length() - charPointer);
} else {
charPointer++;
}
}
// Loop through all tokens.
for (int tokenPointer = 0; tokenPointer < tokens.size(); ) {
String token = tokens.get(tokenPointer);
if (token.equalsIgnoreCase(Token.NEXT)) {
// increment the data pointer (to point to the next cell
// to the
// right).
dataPointer = (dataPointer == data.length - 1 ? 0 : dataPointer + 1);
}
if (token.equalsIgnoreCase(Token.PREVIOUS)) {
// decrement the data pointer (to point to the next cell
// to the
// left).
dataPointer = (dataPointer == 0 ? data.length - 1 : dataPointer - 1);
}
if (token.equalsIgnoreCase(Token.PLUS)) {
// increment (increase by one) the byte at the data
// pointer.
data[dataPointer]++;
}
if (token.equalsIgnoreCase(Token.MINUS)) {
// decrement (decrease by one) the byte at the data
// pointer.
data[dataPointer]--;
}
if (token.equalsIgnoreCase(Token.OUTPUT)) {
// Output the byte at the current index in a character.
outWriter.write((char) data[dataPointer]);
// Flush the outputstream.
outWriter.flush();
}
if (token.equalsIgnoreCase(Token.INPUT)) {
// accept one byte of input, storing its value in the
// byte at the data pointer.
data[dataPointer] = (byte) consoleReader.read();
}
if (token.equalsIgnoreCase(Token.BRACKET_LEFT)) {
if (data[dataPointer] == 0) {
int level = 1;
while (level > 0) {
tokenPointer++;
if (tokens.get(tokenPointer).equalsIgnoreCase(Token.BRACKET_LEFT))
level++;
else if (tokens.get(tokenPointer).equalsIgnoreCase(Token.BRACKET_RIGHT))
level--;
}
}
}
if (token.equalsIgnoreCase(Token.BRACKET_RIGHT)) {
if (data[dataPointer] != 0) {
int level = 1;
while (level > 0) {
tokenPointer--;
if (tokens.get(tokenPointer).equalsIgnoreCase(Token.BRACKET_LEFT))
level--;
else if (tokens.get(tokenPointer).equalsIgnoreCase(Token.BRACKET_RIGHT))
level++;
}
}
}
tokenPointer++;
}
// Clear all data.
initate(data.length);
}
/**
* Is the given token a valid <code>TrollScript</code> token.
*
* @param token
* The token to check.
* @return <code>true</code> if the given token is a valid
* <code>TrollScript</code> token, <code>false</code> otherwise.
*/
protected boolean isValidToken(String token) {
if (token.equalsIgnoreCase(Token.START) || token.equalsIgnoreCase(Token.NEXT)
|| token.equalsIgnoreCase(Token.PREVIOUS) || token.equalsIgnoreCase(Token.PLUS)
|| token.equalsIgnoreCase(Token.MINUS) || token.equalsIgnoreCase(Token.OUTPUT)
|| token.equalsIgnoreCase(Token.INPUT)
|| token.equalsIgnoreCase(Token.BRACKET_LEFT)
|| token.equalsIgnoreCase(Token.BRACKET_RIGHT)
|| token.equalsIgnoreCase(Token.END)) {
return true;
}
return false;
}
}