Skip to content

Instantly share code, notes, and snippets.

@Pyeroh
Created December 14, 2018 05:46
Show Gist options
  • Save Pyeroh/1b2903e53d71e0662d105dc0d6aedbdf to your computer and use it in GitHub Desktop.
Save Pyeroh/1b2903e53d71e0662d105dc0d6aedbdf to your computer and use it in GitHub Desktop.
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Optional;
@SuppressWarnings("ConstantConditions") // For IntelliJ to stop complaining about method "fail"
public class WhitespaceInterpreter {
// transforms space characters to ['s','t','n'] chars;
public static String unbleach(String code) {
return code != null ? code.replace(' ', 's').replace('\t', 't').replace('\n', 'n') : null;
}
public static String execute(String code, InputStream input) {
return execute(code, input, null);
}
public static String execute(String code, InputStream input, OutputStream stream) {
try {
return execute0(0,
code,
input,
stream,
new StringBuilder(),
new LinkedList<>(),
new HashMap<>(),
new HashMap<>(),
new HashMap<>()).value;
} finally {
if (stream != null) {
safeRun(stream::flush);
}
}
}
private static DecodedValue<String> execute0(int startingIndex,
String code,
InputStream input,
OutputStream stream,
StringBuilder output,
LinkedList<Integer> stack,
Map<Integer, Integer> heap,
Map<String, Integer> labels,
Map<String, Integer> labelsCount) {
code = code == null ? "" : code;
boolean exitSignal = false;
if (stream != null) {
safeRun(stream::flush);
}
if (code.isEmpty()) {
return fail("no code");
}
if (startingIndex == 0) {
Arrays.stream(code.split("[ \n\t]+"))
.filter(s -> !s.isEmpty())
.map("====="::concat)
.forEach(System.out::println);
code = unbleach(code.replaceAll("[^ \n\t]+", ""));
System.out.println(code);
detectLabels(code, labels, labelsCount);
}
BufferedReader lineInput = input == null ? null : new BufferedReader(new InputStreamReader(input));
int i = startingIndex;
for (; i < code.length(); i++) {
char imp1 = code.charAt(i);
PointerIncrementer pi;
if (imp1 == 's') {
pi = stackManipulation(code.substring(++i), stack);
} else if (imp1 == 't') {
char imp2 = code.charAt(++i);
if (imp2 == 's') {
pi = arithmetic(code.substring(++i), stack);
} else if (imp2 == 't') {
pi = heapAccess(code.substring(++i), stack, heap);
} else {
pi = inputOutput(code.substring(++i), stack, heap, lineInput, output, stream);
}
} else {
pi = flowControl(code.substring(++i), i - 1, labels, stack, labelsCount);
if (((SubExecutor) pi).shouldExitProgram) {
exitSignal = true;
break;
}
if (((SubExecutor) pi).shouldExitSubRoutine) {
if (startingIndex != 0) {
exitSignal = true;
i = -1;
break;
} else {
fail("Why using return when not in a subroutine ?");
}
}
if (((SubExecutor) pi).subroutineStartingIndex != -1) {
DecodedValue<String> executed = execute0(((SubExecutor) pi).subroutineStartingIndex + 1, code, input, stream, output, stack, heap, labels, labelsCount);
if (executed.pointerIncrement != -1) {
pi = executed;
}
}
}
i += pi.pointerIncrement;
}
if (startingIndex != 0 && i == code.length()) {
fail("No return for a subroutine");
}
if (!exitSignal && i == code.length()) {
fail("Unclean termination");
}
if (stream != null) {
safeRun(stream::flush);
}
System.out.println(output.toString());
return new DecodedValue<>(output.toString(), i);
}
private static void detectLabels(String code, Map<String, Integer> labels, Map<String, Integer> labelsCount) {
for (int i = 0; i < code.length(); i++) {
char imp1 = code.charAt(i);
if (imp1 == 's') {
if (code.charAt(++i) == 'n') {
i++;
} else {
i = code.indexOf('n', i);
}
} else if (imp1 == 't') {
char imp2 = code.charAt(++i);
if (imp2 == 's' || imp2 == 'n') {
i += 2;
} else {
i++;
}
} else {
char imp2 = code.charAt(++i);
if (imp2 == 's') {
char imp3 = code.charAt(++i);
if (imp3 == 's') {
DecodedValue<String> decoded = decodeLabel(code.substring(++i));
i += decoded.pointerIncrement;
labels.putIfAbsent(decoded.value, i);
labelsCount.put(decoded.value, labelsCount.getOrDefault(decoded.value, 0) + 1);
} else {
i = code.indexOf('n', ++i);
}
} else if (imp2 == 't') {
if (code.charAt(++i) != 'n') {
i = code.indexOf('n', ++i);
}
} else {
i++;
}
}
}
}
private static PointerIncrementer arithmetic(String code, Deque<Integer> stack) {
int i = -1;
char imp3 = code.charAt(++i);
Integer a = stack.pop();
Integer b = stack.pop();
if (imp3 == 's') {
char imp4 = code.charAt(++i);
if (imp4 == 's') {
stack.push(b + a);
} else if (imp4 == 't') {
stack.push(b - a);
} else {
stack.push(b * a);
}
} else if (imp3 == 't') {
char imp4 = code.charAt(++i);
if (imp4 == 's') {
stack.push(Math.floorDiv(b, a));
} else if (imp4 == 't') {
stack.push(Math.floorMod(b, a));
} else {
failUnexpected("tst", imp4);
}
} else {
failUnexpected("ts", imp3);
}
return new PointerIncrementer(i);
}
private static PointerIncrementer heapAccess(String code, Deque<Integer> stack, Map<Integer, Integer> heap) {
int i = -1;
char imp3 = code.charAt(++i);
Integer a = stack.pop();
if (imp3 == 's') {
Integer b = stack.pop();
heap.put(b, a);
} else if (imp3 == 't') {
stack.push(heap.get(a));
} else {
failUnexpected("tt", imp3);
}
return new PointerIncrementer(i);
}
private static PointerIncrementer inputOutput(String code, Deque<Integer> stack, Map<Integer, Integer> heap, BufferedReader lineInput, StringBuilder output, OutputStream stream) {
int i = -1;
char imp3 = code.charAt(++i);
if (imp3 == 's') {
char imp4 = code.charAt(++i);
int firstValue = stack.pop();
if (imp4 == 's') {
appendToOutput((char) firstValue, output, stream);
} else if (imp4 == 't') {
appendToOutput(firstValue, output, stream);
} else {
failUnexpected("tns", imp4);
}
} else if (imp3 == 't') {
char imp4 = code.charAt(++i);
Integer a = null;
if (imp4 == 's') {
a = safeGet(lineInput::read);
if (a == -1) {
fail("End of input");
}
} else if (imp4 == 't') {
a = safeGet(() -> Integer.valueOf(lineInput.readLine()));
} else {
failUnexpected("tnt", imp4);
}
int b = stack.pop();
heap.put(b, a);
} else {
failUnexpected("tn", imp3);
}
return new PointerIncrementer(i);
}
private static SubExecutor flowControl(String code, int startingIndex, Map<String, Integer> labels, Deque<Integer> stack, Map<String, Integer> labelsCount) {
int i = 0;
int subroutineStartingIndex = -1;
boolean exitSub = false;
boolean exitMain = false;
char imp2 = code.charAt(i++);
if (imp2 == 's') {
char imp3 = code.charAt(i++);
if (imp3 == 's') {
DecodedValue<String> decoded = decodeLabel(code.substring(i++));
i += decoded.pointerIncrement;
int count = labelsCount.get(decoded.value);
if (count == 0 || count > 1) {
fail("Already existent label : \"" + decoded.value + "\"");
}
} else if (imp3 == 't') {
DecodedValue<String> decoded = decodeLabel(code.substring(i++));
i += decoded.pointerIncrement;
subroutineStartingIndex = labels.get(decoded.value);
} else {
DecodedValue<String> decoded = decodeLabel(code.substring(i));
i = labels.get(decoded.value) - startingIndex;
}
} else if (imp2 == 't') {
char imp3 = code.charAt(i++);
if (imp3 == 's') {
DecodedValue<String> decoded = decodeLabel(code.substring(i++));
i += decoded.pointerIncrement;
if (stack.pop() == 0) {
i = labels.get(decoded.value) - startingIndex;
}
} else if (imp3 == 't') {
DecodedValue<String> decoded = decodeLabel(code.substring(i++));
i += decoded.pointerIncrement;
if (stack.pop() < 0) {
i = labels.get(decoded.value) - startingIndex;
}
} else {
exitSub = true;
}
} else {
char imp3 = code.charAt(i++);
if (imp3 == 'n') {
i = code.length();
exitMain = true;
} else {
failUnexpected("nn", imp3);
}
}
return new SubExecutor(i - 1, subroutineStartingIndex, exitSub, exitMain);
}
private static PointerIncrementer stackManipulation(String code, LinkedList<Integer> stack) {
int i = 0;
char imp2 = code.charAt(i++);
if (imp2 == 's') {
DecodedValue<Integer> integerDecodedValue = decodeNumber(code.substring(i++));
i += integerDecodedValue.pointerIncrement;
stack.push(integerDecodedValue.value);
} else if (imp2 == 't') {
char imp3 = code.charAt(i++);
if (imp3 == 's') {
DecodedValue<Integer> integerDecodedValue = decodeNumber(code.substring(i++));
i += integerDecodedValue.pointerIncrement;
stack.push(stack.get(integerDecodedValue.value));
} else if (imp3 == 'n') {
DecodedValue<Integer> decoded = decodeNumber(code.substring(i++));
i += decoded.pointerIncrement;
if (decoded.value < 0 || decoded.value >= stack.size()) {
stack.subList(1, stack.size()).clear();
} else {
stack.subList(1, decoded.value + 1).clear();
}
} else {
failUnexpected("st", imp3);
}
} else if (imp2 == 'n') {
char imp3 = code.charAt(i++);
if (imp3 == 's') {
stack.push(stack.getFirst());
} else if (imp3 == 't') {
Integer firstValue = stack.pop();
Integer secondValue = stack.pop();
stack.push(firstValue);
stack.push(secondValue);
} else {
stack.pop();
}
}
return new PointerIncrementer(i - 1);
}
private static DecodedValue<Integer> decodeNumber(String code) {
int i = 0;
char sign = code.charAt(i++);
if (sign == 't') {
sign = '-';
} else if (sign == 's') {
sign = '+';
} else {
fail("Unknown sign");
}
StringBuilder number = new StringBuilder().append(sign).append('0');
char bit;
while ((bit = code.charAt(i++)) != 'n') {
number.append(bit == 's' ? '0' : (bit == 't' ? '1' : (char) fail("Invalid bit")));
}
return new DecodedValue<>(Integer.valueOf(number.toString(), 2), i - 1);
}
private static DecodedValue<String> decodeLabel(String code) {
int i = 0;
StringBuilder label = new StringBuilder();
char bit;
while ((bit = code.charAt(i++)) != 'n') {
label.append(bit == 's' ? '0' : (bit == 't' ? '1' : (char) fail("Invalid bit")));
}
return new DecodedValue<>(Optional.of(label.toString())
.filter(s -> !s.isEmpty())
.map(s -> Integer.valueOf(s, 2))
.map(String::valueOf)
.orElse(""), i - 1);
}
private static void appendToOutput(char c, StringBuilder outputSB, OutputStream outputStream) {
outputSB.append(c);
if (outputStream != null) {
safeRun(() -> outputStream.write((int) c));
}
}
private static void appendToOutput(int i, StringBuilder outputSB, OutputStream outputStream) {
outputSB.append(i);
if (outputStream != null) {
String.valueOf(i).chars().forEach(c -> safeRun(() -> outputStream.write(c)));
}
}
private static <T> T fail(String message) {
if (message != null) {
throw new RuntimeException(message);
} else {
throw new RuntimeException();
}
}
private static void failUnexpected(String mainInstruction, char subInstruction) {
fail(String.format("Unexpected instruction after %s : %s", mainInstruction, subInstruction));
}
private static <T> T safeGet(CheckedSupplier<T> supplier) {
try {
return supplier.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static void safeRun(CheckedRunnable runnable) {
try {
runnable.run();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@FunctionalInterface
interface CheckedSupplier<T> {
T get() throws Exception;
}
@FunctionalInterface
interface CheckedRunnable {
void run() throws Exception;
}
static class PointerIncrementer {
final int pointerIncrement;
PointerIncrementer(int pointerIncrement) {
this.pointerIncrement = pointerIncrement;
}
}
static class SubExecutor extends PointerIncrementer {
final int subroutineStartingIndex;
final boolean shouldExitSubRoutine;
final boolean shouldExitProgram;
SubExecutor(int pointerIncrement, int subroutineStartingIndex, boolean shouldExitSubRoutine, boolean shouldExitProgram) {
super(pointerIncrement);
this.subroutineStartingIndex = subroutineStartingIndex;
this.shouldExitSubRoutine = shouldExitSubRoutine;
this.shouldExitProgram = shouldExitProgram;
}
}
static class DecodedValue<T> extends PointerIncrementer {
final T value;
DecodedValue(T value, int pointerIncrement) {
super(pointerIncrement);
this.value = value;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment