com.github.bloodshura.sparkium.brainfxck.BrainfuckInterpreter Maven / Gradle / Ivy
Show all versions of sparkium-brainfxck Show documentation
package com.github.bloodshura.sparkium.brainfxck;
import com.github.bloodshura.ignitium.activity.logging.Logger;
import com.github.bloodshura.ignitium.activity.logging.XLogger;
import com.github.bloodshura.ignitium.activity.scanning.Scanner;
import com.github.bloodshura.ignitium.activity.scanning.XScanner;
import com.github.bloodshura.ignitium.charset.TextBuilder;
import com.github.bloodshura.ignitium.collection.store.impl.XQueue;
import com.github.bloodshura.ignitium.resource.Resource;
import com.github.bloodshura.sparkium.brainfxck.action.ActionSet;
import com.github.bloodshura.sparkium.brainfxck.action.impl.DefaultAction;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
/**
* This class is responsible for interpreting Brainfuck code, executing them, and maintaining the necessary resources, like the {@link BrainfuckMemory} instance, the output logger, the input scanner, etc.
*/
public class BrainfuckInterpreter {
private final ActionSet actionSet;
private final XQueue buffer;
private Logger logger;
private final BrainfuckMemory memory;
private Scanner scanner;
/**
* Constructs a new instance of the Brainfuck interpreter, using the default {@link ActionSet} and {@link BrainfuckMemory}.
* Note that, this interpreter automatically creates a console logger and console scanner.
*
* @see #BrainfuckInterpreter(ActionSet, BrainfuckMemory)
*/
public BrainfuckInterpreter() {
this(new ActionSet(new DefaultAction()), new BrainfuckMemory());
}
/**
* Constructs a new instance of the Brainfuck interpreter, using the specified {@link ActionSet} and the default {@link BrainfuckMemory}.
*
Note that, this interpreter automatically creates a console logger and console scanner.
*
* @param actionSet The {@link ActionSet} to be used
* @see #BrainfuckInterpreter(ActionSet, BrainfuckMemory)
*/
public BrainfuckInterpreter(@Nonnull ActionSet actionSet) {
this(actionSet, new BrainfuckMemory());
}
/**
* Constructs a new instance of the Brainfuck interpreter, using the default {@link ActionSet} and the specified {@link BrainfuckMemory}.
*
Note that, this interpreter automatically creates a console logger and console scanner.
*
* @param memory The {@link BrainfuckMemory} to be used
* @see #BrainfuckInterpreter(ActionSet, BrainfuckMemory)
*/
public BrainfuckInterpreter(@Nonnull BrainfuckMemory memory) {
this(new ActionSet(new DefaultAction()), memory);
}
/**
* Constructs a new instance of the Brainfuck interpreter, using the specified {@link ActionSet} and {@link BrainfuckMemory}.
*
Note that, this interpreter automatically creates a console logger and console scanner.
*
* @param actionSet The {@link ActionSet} to be used
* @param memory The {@link BrainfuckMemory} to be used
* @see #setLogger(Logger)
* @see #setScanner(Scanner)
*/
public BrainfuckInterpreter(@Nonnull ActionSet actionSet, @Nonnull BrainfuckMemory memory) {
this.actionSet = actionSet;
this.buffer = new XQueue<>();
this.memory = memory;
setLogger(null);
setScanner(null);
}
/**
* Returns the unmodifiable {@link ActionSet} used by this interpreter.
*
* @return The action set
*/
@Nonnull
public ActionSet getActionSet() {
return actionSet;
}
/**
* Returns the logger used by this interpreter, where code execution output goes.
*
* @return The output logger
* @see #setLogger(Logger)
*/
@Nonnull
public Logger getLogger() {
return logger;
}
/**
* Returns the instance responsible for memory management of the Brainfuck code ran by this interpreter.
*
* @return The memory manager
*/
@Nonnull
public BrainfuckMemory getMemory() {
return memory;
}
/**
* Returns the scanner used by this interpreter, where code execution input comes from.
*
* @return The input scanner
* @see #setScanner(Scanner)
*/
@Nonnull
public Scanner getScanner() {
return scanner;
}
/**
* Interprets the Brainfuck code passed by parammeter.
*
* @param input The Brainfuck code
* @throws IOException If an I/O exception occurs when interpreting the code; for example, I/O fails on the logger and/or scanner
*/
public void interpret(@Nonnull CharSequence input) throws IOException {
BrainfuckContext context = new BrainfuckContext(this);
input = removeComments(input);
for (int i = 0; !Thread.interrupted() && i < input.length(); i++) {
context.setCurrentIndex(i);
context.interpret(input.charAt(i));
if (context.getCurrentIndex() != i) {
i = context.getCurrentIndex() - 1;
}
}
}
/**
* Interprets the resource passed by parammeter, passing it to a String and then calling the method {@link #interpret(CharSequence)}.
*
* @param resource The code resource
* @throws IOException If an I/O exception occurs when trying to read String from the resource parammeter
* @throws IOException If an I/O exception occurs when interpreting the code; for example, I/O fails on the logger and/or scanner
* @see #interpret(CharSequence)
*/
public void interpret(@Nonnull Resource resource) throws IOException {
interpret(resource.readString());
}
/**
* A convenient method to read an unique character from the input scanner.
*
In this default implementation, it's used the {@link Scanner} specified on this interpreter; therefore, depending the input source, it will only be processed after a line feed is inserted.
*
Because of those cases, an internal buffer is maintained, where all the characters are stored into and, on each call to this method, a single character is polled.
*
* @return The character read
*/
public char readChar() {
if (!buffer.isEmpty()) {
return buffer.poll();
}
String line = getScanner().scan();
for (int i = 1; i < line.length(); i++) {
buffer.push(line.charAt(i));
}
return line.charAt(0);
}
/**
* Changes the logger used by this interpreter.
*
If the logger parameter is null, it will be assigned {@link XLogger#DEFAULT}.
*
* @param logger The output logger
*/
public void setLogger(@Nullable Logger logger) {
this.logger = logger != null ? logger : XLogger.DEFAULT;
}
/**
* Changes the scanner used by this interpreter.
*
If the scanner parameter is null, it will be assigned {@link XScanner#DEFAULT}.
*
* @param scanner The input scanner
*/
public void setScanner(@Nullable Scanner scanner) {
this.scanner = scanner != null ? scanner : XScanner.DEFAULT;
}
/**
* Removes all comments present in the specified Brainfuck code.
*
Comments start with a single quote ('), and end with a line feed (\n).
*
* @param The CharSequence class type
* @param sequence The Brainfuck code, as a character sequence
* @return The stripped Brainfuck code, without comments
*/
@Nonnull
public static E removeComments(@Nonnull E sequence) {
TextBuilder builder = new TextBuilder();
boolean removing = false;
for (int i = 0; i < sequence.length(); i++) {
char ch = sequence.charAt(i);
if (ch == '\'') {
removing = true;
} else if (ch == '\n') {
removing = false;
} else if (!removing) {
builder.append(ch);
}
}
return (E) builder.get(sequence.getClass());
}
}