diff --git a/src/main/java/com/falsepattern/jfunge/Globals.java b/src/main/java/com/falsepattern/jfunge/Globals.java index ccd0a64..330bb54 100644 --- a/src/main/java/com/falsepattern/jfunge/Globals.java +++ b/src/main/java/com/falsepattern/jfunge/Globals.java @@ -4,19 +4,5 @@ public class Globals { public static final String VERSION = "1.0.0"; public static final int FUNGE_VERSION = 1 * 256 * 256 + 0 * 256 + 0; public static final int HANDPRINT = 0xfa15e9a7; //"falsepat" - public static final String LICENSE = "JFunge - A standard-conforming Befunge-98 and Trefunge-98 interpreter\n" + - "Copyright (C) 2022 FalsePattern\n" + - "\n" + - "This program is free software: you can redistribute it and/or modify\n" + - "it under the terms of the GNU General Public License as published by\n" + - "the Free Software Foundation, either version 3 of the License, or\n" + - "(at your option) any later version.\n" + - "\n" + - "This program is distributed in the hope that it will be useful,\n" + - "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + - "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + - "GNU General Public License for more details.\n" + - "\n" + - "You should have received a copy of the GNU General Public License\n" + - "along with this program. If not, see ."; + public static final String LICENSE = "JFunge - A standard-conforming Befunge-98 and Trefunge-98 interpreter\n" + "Copyright (C) 2022 FalsePattern\n" + "\n" + "This program is free software: you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation, either version 3 of the License, or\n" + "(at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program. If not, see ."; } diff --git a/src/main/java/com/falsepattern/jfunge/Main.java b/src/main/java/com/falsepattern/jfunge/Main.java index 8b56ac1..696b462 100644 --- a/src/main/java/com/falsepattern/jfunge/Main.java +++ b/src/main/java/com/falsepattern/jfunge/Main.java @@ -4,7 +4,6 @@ import lombok.val; import lombok.var; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.file.Files; @@ -12,7 +11,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.stream.Stream; public class Main { public static void main(String[] args) throws IOException { @@ -61,7 +59,7 @@ public static void main(String[] args) throws IOException { var read = 0; val buf = new byte[4096]; while ((read = in.read(buf)) > 0) { - programBytes.write(buf, 0, read); + programBytes.write(buf, 0, read); } program = programBytes.toByteArray(); } else { diff --git a/src/main/java/com/falsepattern/jfunge/Releasable.java b/src/main/java/com/falsepattern/jfunge/Releasable.java index 1d8ca91..e2d0084 100644 --- a/src/main/java/com/falsepattern/jfunge/Releasable.java +++ b/src/main/java/com/falsepattern/jfunge/Releasable.java @@ -1,6 +1,6 @@ package com.falsepattern.jfunge; -public interface Releasable extends AutoCloseable{ +public interface Releasable extends AutoCloseable { /** * When called, it signals that this specific object is no longer referenced anywhere in the code, and can be freely deleted or even reused later by the provider. */ diff --git a/src/main/java/com/falsepattern/jfunge/interpreter/ExecutionContext.java b/src/main/java/com/falsepattern/jfunge/interpreter/ExecutionContext.java index 9bd6895..0162e96 100644 --- a/src/main/java/com/falsepattern/jfunge/interpreter/ExecutionContext.java +++ b/src/main/java/com/falsepattern/jfunge/interpreter/ExecutionContext.java @@ -6,23 +6,37 @@ import java.io.OutputStream; import java.util.List; import java.util.Map; -import java.util.Scanner; public interface ExecutionContext { InstructionPointer[] allIPs(); + InstructionPointer IP(); + InstructionPointer cloneIP(); + FungeSpace fungeSpace(); + int dimensions(); + boolean stopped(); + void stop(int exitCode); + int exitCode(); + void interpret(int code); + void step(InstructionPointer ip); + List args(); + Map env(); + int input(boolean stagger); + OutputStream output(); + byte[] readFile(String file); + boolean writeFile(String file, byte[] data); } diff --git a/src/main/java/com/falsepattern/jfunge/interpreter/Interpreter.java b/src/main/java/com/falsepattern/jfunge/interpreter/Interpreter.java index 026dc06..a81737c 100644 --- a/src/main/java/com/falsepattern/jfunge/interpreter/Interpreter.java +++ b/src/main/java/com/falsepattern/jfunge/interpreter/Interpreter.java @@ -10,7 +10,9 @@ import lombok.val; import lombok.var; -import java.io.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Paths; import java.util.*; @@ -61,6 +63,18 @@ public boolean writeFile(String file, byte[] data) throws IOException { private int inputStagger; + public Interpreter(boolean trefunge, String[] args, InputStream input, OutputStream output, FileIOSupplier fileIOSupplier) { + this.args = Arrays.asList(args); + dimensions = trefunge ? 3 : 2; + baseInstructionManager.loadInstructionSet(Funge98.INSTANCE); + this.input = input; + this.output = output; + this.fileIOSupplier = fileIOSupplier; + val ip = new InstructionPointer(); + ip.UUID = nextUUID++; + IPs.add(ip); + } + public static int executeProgram(boolean trefunge, String[] args, byte[] program, long iterLimit, InputStream input, OutputStream output, FileIOSupplier fileIOSupplier) { val interpreter = new Interpreter(trefunge, args, input, output, fileIOSupplier); interpreter.fungeSpace().loadFileAt(0, 0, 0, program, trefunge); @@ -70,7 +84,8 @@ public static int executeProgram(boolean trefunge, String[] args, byte[] program interpreter.tick(); step++; } - if (!interpreter.stopped()) throw new IllegalStateException("Program exceeded max iteration count!"); + if (!interpreter.stopped()) + throw new IllegalStateException("Program exceeded max iteration count!"); } else { while (!interpreter.stopped()) { interpreter.tick(); @@ -79,18 +94,6 @@ public static int executeProgram(boolean trefunge, String[] args, byte[] program return interpreter.exitCode(); } - public Interpreter(boolean trefunge, String[] args, InputStream input, OutputStream output, FileIOSupplier fileIOSupplier) { - this.args = Arrays.asList(args); - dimensions = trefunge ? 3 : 2; - baseInstructionManager.loadInstructionSet(Funge98.INSTANCE); - this.input = input; - this.output = output; - this.fileIOSupplier = fileIOSupplier; - val ip = new InstructionPointer(); - ip.UUID = nextUUID++; - IPs.add(ip); - } - @Override public InstructionPointer[] allIPs() { return IPs.toArray(new InstructionPointer[0]); @@ -137,7 +140,8 @@ public void interpret(int opcode) { if ((instr = IP().instructionManager.fetch(opcode)) != null || (instr = baseInstructionManager.fetch(opcode)) != null) { instr.process(this); } else { - if (opcode == 'r') throw new IllegalArgumentException("Language does not implement 'r' reflect instruction."); + if (opcode == 'r') + throw new IllegalArgumentException("Language does not implement 'r' reflect instruction."); interpret('r'); } } @@ -228,11 +232,6 @@ public boolean writeFile(String file, byte[] data) { } } - public interface FileIOSupplier { - byte[] readFile(String file) throws IOException; - boolean writeFile(String file, byte[] data) throws IOException; - } - public void tick() { currentIP = null; for (int i = 0; i < IPs.size(); i++) { @@ -248,8 +247,14 @@ public void tick() { clone = null; } } - for (val ip: IPs) { + for (val ip : IPs) { step(ip); } } + + public interface FileIOSupplier { + byte[] readFile(String file) throws IOException; + + boolean writeFile(String file, byte[] data) throws IOException; + } } diff --git a/src/main/java/com/falsepattern/jfunge/interpreter/instructions/Funge98.java b/src/main/java/com/falsepattern/jfunge/interpreter/instructions/Funge98.java index 8ce3bd2..dc68e72 100644 --- a/src/main/java/com/falsepattern/jfunge/interpreter/instructions/Funge98.java +++ b/src/main/java/com/falsepattern/jfunge/interpreter/instructions/Funge98.java @@ -68,10 +68,14 @@ public static void unloadFinger(ExecutionContext ctx) { } @Instr('>') - public static void east(ExecutionContext ctx) {ctx.IP().delta.set(1, 0, 0);} + public static void east(ExecutionContext ctx) { + ctx.IP().delta.set(1, 0, 0); + } @Instr('v') - public static void south(ExecutionContext ctx) {ctx.IP().delta.set(0, 1, 0);} + public static void south(ExecutionContext ctx) { + ctx.IP().delta.set(0, 1, 0); + } @Instr('h') public static void high(ExecutionContext ctx) { @@ -94,28 +98,52 @@ public static void away(ExecutionContext ctx) { var random = Math.abs(ctx.IP().nextRandom()); if (ctx.dimensions() == 3) { switch (random % 6) { - case 0: east(ctx); break; - case 1: south(ctx); break; - case 2: west(ctx); break; - case 3: north(ctx); break; - case 4: high(ctx); break; - case 5: low(ctx); break; + case 0: + east(ctx); + break; + case 1: + south(ctx); + break; + case 2: + west(ctx); + break; + case 3: + north(ctx); + break; + case 4: + high(ctx); + break; + case 5: + low(ctx); + break; } } else { switch (random % 4) { - case 0: east(ctx); break; - case 1: south(ctx); break; - case 2: west(ctx); break; - case 3: north(ctx); break; + case 0: + east(ctx); + break; + case 1: + south(ctx); + break; + case 2: + west(ctx); + break; + case 3: + north(ctx); + break; } } } @Instr('<') - public static void west(ExecutionContext ctx) {ctx.IP().delta.set(-1, 0, 0);} + public static void west(ExecutionContext ctx) { + ctx.IP().delta.set(-1, 0, 0); + } @Instr('^') - public static void north(ExecutionContext ctx) {ctx.IP().delta.set(0, -1, 0);} + public static void north(ExecutionContext ctx) { + ctx.IP().delta.set(0, -1, 0); + } @Instr('[') public static void turnLeft(ExecutionContext ctx) { @@ -130,7 +158,9 @@ public static void turnRight(ExecutionContext ctx) { } @Instr('#') - public static void trampoline(ExecutionContext ctx) {ctx.IP().position.add(ctx.IP().delta);} + public static void trampoline(ExecutionContext ctx) { + ctx.IP().position.add(ctx.IP().delta); + } @Instr('j') public static void jumpNTimes(ExecutionContext ctx) { @@ -180,22 +210,34 @@ public static void printChar(ExecutionContext ctx) { } @Instr('r') - public static void reflect(ExecutionContext ctx) {ctx.IP().delta.mul(-1);} + public static void reflect(ExecutionContext ctx) { + ctx.IP().delta.mul(-1); + } @Instr('@') - public static void die(ExecutionContext ctx) {ctx.IP().die();} + public static void die(ExecutionContext ctx) { + ctx.IP().die(); + } @Instr('$') - public static void pop(ExecutionContext ctx) {ctx.IP().stackStack.TOSS().pop();} + public static void pop(ExecutionContext ctx) { + ctx.IP().stackStack.TOSS().pop(); + } @Instr('n') - public static void clearStack(ExecutionContext ctx) {ctx.IP().stackStack.TOSS().clear();} + public static void clearStack(ExecutionContext ctx) { + ctx.IP().stackStack.TOSS().clear(); + } @Instr('_') - public static void branchEastWest(ExecutionContext ctx) {ctx.interpret(ctx.IP().stackStack.TOSS().pop() == 0 ? '>' : '<');} + public static void branchEastWest(ExecutionContext ctx) { + ctx.interpret(ctx.IP().stackStack.TOSS().pop() == 0 ? '>' : '<'); + } @Instr('|') - public static void branchNorthSouth(ExecutionContext ctx) {ctx.interpret(ctx.IP().stackStack.TOSS().pop() == 0 ? 'v' : '^');} + public static void branchNorthSouth(ExecutionContext ctx) { + ctx.interpret(ctx.IP().stackStack.TOSS().pop() == 0 ? 'v' : '^'); + } @Instr('m') public static void branchHighLow(ExecutionContext ctx) { @@ -234,25 +276,38 @@ public static void conditionalTurn(ExecutionContext ctx) { } @Instr('z') - public static void noop(ExecutionContext ctx) {} + public static void noop(ExecutionContext ctx) { + } @Instr('+') - public static void add(ExecutionContext ctx) {binop(ctx, Integer::sum);} + public static void add(ExecutionContext ctx) { + binop(ctx, Integer::sum); + } @Instr('-') - public static void sub(ExecutionContext ctx) {binop(ctx, (a, b) -> a - b);} + public static void sub(ExecutionContext ctx) { + binop(ctx, (a, b) -> a - b); + } @Instr('*') - public static void mul(ExecutionContext ctx) {binop(ctx, (a, b) -> a * b);} + public static void mul(ExecutionContext ctx) { + binop(ctx, (a, b) -> a * b); + } @Instr('`') - public static void greater(ExecutionContext ctx) {binop(ctx, (a, b) -> a > b ? 1 : 0);} + public static void greater(ExecutionContext ctx) { + binop(ctx, (a, b) -> a > b ? 1 : 0); + } @Instr('/') - public static void div(ExecutionContext ctx) {binop(ctx, (a, b) -> b == 0 ? 0 : a / b);} + public static void div(ExecutionContext ctx) { + binop(ctx, (a, b) -> b == 0 ? 0 : a / b); + } @Instr('%') - public static void mod(ExecutionContext ctx) {binop(ctx, (a, b) -> b == 0 ? 0 : a % b);} + public static void mod(ExecutionContext ctx) { + binop(ctx, (a, b) -> b == 0 ? 0 : a % b); + } @Instr('\\') public static void swap(ExecutionContext ctx) { @@ -293,7 +348,9 @@ public static void get(ExecutionContext ctx) { } @Instr('!') - public static void logicalNot(ExecutionContext ctx) {stack(ctx, (toss) -> toss.push(toss.pop() == 0 ? 1 : 0));} + public static void logicalNot(ExecutionContext ctx) { + stack(ctx, (toss) -> toss.push(toss.pop() == 0 ? 1 : 0)); + } @Instr(':') public static void duplicate(ExecutionContext ctx) { @@ -583,6 +640,17 @@ public static void sysCall(ExecutionContext ctx) { } } + public static void stack(ExecutionContext ctx, Consumer runner) { + runner.accept(ctx.IP().stackStack.TOSS()); + } + + public static void binop(ExecutionContext ctx, BinaryOperator op) { + val TOSS = ctx.IP().stackStack.TOSS(); + int b = TOSS.pop(); + int a = TOSS.pop(); + TOSS.push(op.op(a, b)); + } + @Override public void load(ObjIntConsumer instructionSet) { InstructionSet.super.load(instructionSet); @@ -596,23 +664,12 @@ public void load(ObjIntConsumer instructionSet) { } } - public static void stack(ExecutionContext ctx, Consumer runner) { - runner.accept(ctx.IP().stackStack.TOSS()); - } - - public static void binop(ExecutionContext ctx, BinaryOperator op) { - val TOSS = ctx.IP().stackStack.TOSS(); - int b = TOSS.pop(); - int a = TOSS.pop(); - TOSS.push(op.op(a, b)); + @Override + public void unload(IntConsumer instructionSet) { + throw new UnsupportedOperationException("Cannot unload the base syntax"); } public interface BinaryOperator { int op(int a, int b); } - - @Override - public void unload(IntConsumer instructionSet) { - throw new UnsupportedOperationException("Cannot unload the base syntax"); - } } diff --git a/src/main/java/com/falsepattern/jfunge/interpreter/instructions/InstructionManager.java b/src/main/java/com/falsepattern/jfunge/interpreter/instructions/InstructionManager.java index 1e6da37..88685df 100644 --- a/src/main/java/com/falsepattern/jfunge/interpreter/instructions/InstructionManager.java +++ b/src/main/java/com/falsepattern/jfunge/interpreter/instructions/InstructionManager.java @@ -1,7 +1,6 @@ package com.falsepattern.jfunge.interpreter.instructions; import com.falsepattern.jfunge.Copiable; -import com.falsepattern.jfunge.interpreter.instructions.fingerprints.Fingerprint; import gnu.trove.map.TIntObjectMap; import gnu.trove.map.hash.TIntObjectHashMap; import lombok.NoArgsConstructor; diff --git a/src/main/java/com/falsepattern/jfunge/interpreter/instructions/InstructionSet.java b/src/main/java/com/falsepattern/jfunge/interpreter/instructions/InstructionSet.java index abcd6be..8023f8e 100644 --- a/src/main/java/com/falsepattern/jfunge/interpreter/instructions/InstructionSet.java +++ b/src/main/java/com/falsepattern/jfunge/interpreter/instructions/InstructionSet.java @@ -20,14 +20,7 @@ default void load(ObjIntConsumer instructionSet) { val lookup = MethodHandles.lookup(); val methodType = MethodType.methodType(void.class, ExecutionContext.class); try { - val lambda = (Instruction) LambdaMetafactory.metafactory(lookup, - "process", - MethodType.methodType(Instruction.class), - methodType, - lookup.findStatic(clazz, method.getName(), methodType), - methodType) - .getTarget() - .invokeExact(); + val lambda = (Instruction) LambdaMetafactory.metafactory(lookup, "process", MethodType.methodType(Instruction.class), methodType, lookup.findStatic(clazz, method.getName(), methodType), methodType).getTarget().invokeExact(); val ann = method.getAnnotation(Instr.class); instructionSet.accept(lambda, ann.value()); } catch (Throwable e) { diff --git a/src/main/java/com/falsepattern/jfunge/interpreter/instructions/fingerprints/Fingerprint.java b/src/main/java/com/falsepattern/jfunge/interpreter/instructions/fingerprints/Fingerprint.java index 45d29d2..b32cc85 100644 --- a/src/main/java/com/falsepattern/jfunge/interpreter/instructions/fingerprints/Fingerprint.java +++ b/src/main/java/com/falsepattern/jfunge/interpreter/instructions/fingerprints/Fingerprint.java @@ -1,6 +1,5 @@ package com.falsepattern.jfunge.interpreter.instructions.fingerprints; -import com.falsepattern.jfunge.interpreter.instructions.Instruction; import com.falsepattern.jfunge.interpreter.instructions.InstructionSet; public interface Fingerprint extends InstructionSet { diff --git a/src/main/java/com/falsepattern/jfunge/interpreter/instructions/fingerprints/MODE.java b/src/main/java/com/falsepattern/jfunge/interpreter/instructions/fingerprints/MODE.java index ae33b91..6a1917b 100644 --- a/src/main/java/com/falsepattern/jfunge/interpreter/instructions/fingerprints/MODE.java +++ b/src/main/java/com/falsepattern/jfunge/interpreter/instructions/fingerprints/MODE.java @@ -36,11 +36,6 @@ public static void queueMode(ExecutionContext ctx) { ctx.IP().stackStack.queueMode(!ctx.IP().stackStack.queueMode()); } - @Override - public int code() { - return 0x4d4f4445; - } - private static void toggleMode(ExecutionContext ctx, InstructionSet set, int bit) { val ip = ctx.IP(); val state = ip.customStorage.get("mode"); @@ -54,6 +49,11 @@ private static void toggleMode(ExecutionContext ctx, InstructionSet set, int bit ip.customStorage.put("mode", state ^ bit); } + @Override + public int code() { + return 0x4d4f4445; + } + @NoArgsConstructor(access = AccessLevel.PRIVATE) public static final class HoverMode implements InstructionSet { @Instr('>') @@ -100,6 +100,7 @@ public static final class SwitchMode implements InstructionSet { private static void set(ExecutionContext ctx, int ch) { ctx.fungeSpace().set(ctx.IP().position, ch); } + @Instr('(') public static void loadFinger(ExecutionContext ctx) { Funge98.loadFinger(ctx); diff --git a/src/main/java/com/falsepattern/jfunge/interpreter/instructions/fingerprints/NULL.java b/src/main/java/com/falsepattern/jfunge/interpreter/instructions/fingerprints/NULL.java index 29e1cd9..a1a25b3 100644 --- a/src/main/java/com/falsepattern/jfunge/interpreter/instructions/fingerprints/NULL.java +++ b/src/main/java/com/falsepattern/jfunge/interpreter/instructions/fingerprints/NULL.java @@ -10,6 +10,7 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class NULL implements Fingerprint { public static final NULL INSTANCE = new NULL(); + @Override public void load(ObjIntConsumer instructionSet) { for (int i = 'A'; i <= 'Z'; i++) { diff --git a/src/main/java/com/falsepattern/jfunge/interpreter/instructions/fingerprints/ROMA.java b/src/main/java/com/falsepattern/jfunge/interpreter/instructions/fingerprints/ROMA.java index ad351b1..d5990e3 100644 --- a/src/main/java/com/falsepattern/jfunge/interpreter/instructions/fingerprints/ROMA.java +++ b/src/main/java/com/falsepattern/jfunge/interpreter/instructions/fingerprints/ROMA.java @@ -1,37 +1,47 @@ package com.falsepattern.jfunge.interpreter.instructions.fingerprints; import com.falsepattern.jfunge.interpreter.ExecutionContext; -import com.falsepattern.jfunge.interpreter.instructions.Instruction; import lombok.AccessLevel; import lombok.NoArgsConstructor; -import java.util.function.IntConsumer; -import java.util.function.ObjIntConsumer; - @NoArgsConstructor(access = AccessLevel.PRIVATE) public class ROMA implements Fingerprint { public static final ROMA INSTANCE = new ROMA(); @Instr('I') - public static void I(ExecutionContext ctx) {ctx.IP().stackStack.TOSS().push(1);} + public static void I(ExecutionContext ctx) { + ctx.IP().stackStack.TOSS().push(1); + } @Instr('V') - public static void V(ExecutionContext ctx) {ctx.IP().stackStack.TOSS().push(5);} + public static void V(ExecutionContext ctx) { + ctx.IP().stackStack.TOSS().push(5); + } @Instr('X') - public static void X(ExecutionContext ctx) {ctx.IP().stackStack.TOSS().push(10);} + public static void X(ExecutionContext ctx) { + ctx.IP().stackStack.TOSS().push(10); + } @Instr('L') - public static void L(ExecutionContext ctx) {ctx.IP().stackStack.TOSS().push(50);} + public static void L(ExecutionContext ctx) { + ctx.IP().stackStack.TOSS().push(50); + } @Instr('C') - public static void C(ExecutionContext ctx) {ctx.IP().stackStack.TOSS().push(100);} + public static void C(ExecutionContext ctx) { + ctx.IP().stackStack.TOSS().push(100); + } @Instr('D') - public static void D(ExecutionContext ctx) {ctx.IP().stackStack.TOSS().push(500);} + public static void D(ExecutionContext ctx) { + ctx.IP().stackStack.TOSS().push(500); + } @Instr('M') - public static void M(ExecutionContext ctx) {ctx.IP().stackStack.TOSS().push(1000);} + public static void M(ExecutionContext ctx) { + ctx.IP().stackStack.TOSS().push(1000); + } @Override public int code() { diff --git a/src/main/java/com/falsepattern/jfunge/ip/InstructionPointer.java b/src/main/java/com/falsepattern/jfunge/ip/InstructionPointer.java index 9c5af58..9144dcf 100644 --- a/src/main/java/com/falsepattern/jfunge/ip/InstructionPointer.java +++ b/src/main/java/com/falsepattern/jfunge/ip/InstructionPointer.java @@ -17,12 +17,9 @@ public class InstructionPointer implements Copiable { public final StackStack stackStack; public final InstructionManager instructionManager; public final TObjectIntMap customStorage; - + private final SecureRandom rng; public boolean stringMode = false; public int UUID; - - private final SecureRandom rng; - @Getter private boolean dead = false; diff --git a/src/main/java/com/falsepattern/jfunge/ip/Stack.java b/src/main/java/com/falsepattern/jfunge/ip/Stack.java index 38411f8..d4fe215 100644 --- a/src/main/java/com/falsepattern/jfunge/ip/Stack.java +++ b/src/main/java/com/falsepattern/jfunge/ip/Stack.java @@ -10,6 +10,7 @@ public class Stack implements Copiable { private final TIntList storage; public boolean invertMode; public boolean queueMode; + public Stack() { storage = new TIntArrayList(); } @@ -108,7 +109,7 @@ public void pushString(String text) { public String popString() { val sb = new StringBuilder(); char c; - while ((c = (char)pop()) != 0) { + while ((c = (char) pop()) != 0) { sb.append(c); } return sb.toString(); diff --git a/src/main/java/com/falsepattern/jfunge/ip/StackStack.java b/src/main/java/com/falsepattern/jfunge/ip/StackStack.java index 11c5725..a08cd3d 100644 --- a/src/main/java/com/falsepattern/jfunge/ip/StackStack.java +++ b/src/main/java/com/falsepattern/jfunge/ip/StackStack.java @@ -71,14 +71,15 @@ public int[] sizes() { val sizes = new int[stackStack.size() + 1]; sizes[0] = TOSS().size(); int i = 1; - for (val s: stackStack) { + for (val s : stackStack) { sizes[i++] = s.size(); } return sizes; } public boolean popStackStack() { - if (stackStack.size() == 0) return false; + if (stackStack.size() == 0) + return false; TOSS(stackStack.pop()); return true; } diff --git a/src/main/java/com/falsepattern/jfunge/storage/Bounds.java b/src/main/java/com/falsepattern/jfunge/storage/Bounds.java index adf6337..b481435 100644 --- a/src/main/java/com/falsepattern/jfunge/storage/Bounds.java +++ b/src/main/java/com/falsepattern/jfunge/storage/Bounds.java @@ -4,7 +4,8 @@ import lombok.NoArgsConstructor; import lombok.experimental.Accessors; -@Accessors(fluent = true, chain = true) +@Accessors(fluent = true, + chain = true) @Data @NoArgsConstructor public class Bounds implements BoundsC { diff --git a/src/main/java/com/falsepattern/jfunge/storage/BoundsC.java b/src/main/java/com/falsepattern/jfunge/storage/BoundsC.java index d83824d..fc99c56 100644 --- a/src/main/java/com/falsepattern/jfunge/storage/BoundsC.java +++ b/src/main/java/com/falsepattern/jfunge/storage/BoundsC.java @@ -5,10 +5,15 @@ public interface BoundsC> extends Copiable { int xMin(); + int yMin(); + int zMin(); + int xMax(); + int yMax(); + int zMax(); diff --git a/src/main/java/com/falsepattern/jfunge/storage/Chunk.java b/src/main/java/com/falsepattern/jfunge/storage/Chunk.java index c8a3245..bf27747 100644 --- a/src/main/java/com/falsepattern/jfunge/storage/Chunk.java +++ b/src/main/java/com/falsepattern/jfunge/storage/Chunk.java @@ -84,6 +84,10 @@ public static int fromChunkZ(int cPos) { return cPos * CHUNK_EDGE_SIZE_Z; } + private static int toIndex(int x, int y, int z) { + return (z * CHUNK_EDGE_SIZE_Y + y) * CHUNK_EDGE_SIZE_X + x; + } + public boolean isEmpty() { return populatedCells == 0; } @@ -95,10 +99,6 @@ public void release() { } } - private static int toIndex(int x, int y, int z) { - return (z * CHUNK_EDGE_SIZE_Y + y) * CHUNK_EDGE_SIZE_X + x; - } - public int get(int x, int y, int z) { return storage[toIndex(x, y, z)]; } @@ -173,12 +173,6 @@ public Chunk deepCopy() { } private interface Getter { - int toIndex(int a, int b, int c); - - int sa(); - int sb(); - int sc(); - Getter gX = new Getter() { public int toIndex(int a, int b, int c) { return Chunk.toIndex(a, b, c); @@ -239,5 +233,13 @@ public int sc() { return CHUNK_EDGE_SIZE_Y; } }; + + int toIndex(int a, int b, int c); + + int sa(); + + int sb(); + + int sc(); } } diff --git a/src/main/java/com/falsepattern/jfunge/storage/FungeSpace.java b/src/main/java/com/falsepattern/jfunge/storage/FungeSpace.java index eee4e11..b209f39 100644 --- a/src/main/java/com/falsepattern/jfunge/storage/FungeSpace.java +++ b/src/main/java/com/falsepattern/jfunge/storage/FungeSpace.java @@ -3,7 +3,9 @@ import com.falsepattern.jfunge.Copiable; import gnu.trove.map.TIntObjectMap; import gnu.trove.map.hash.TIntObjectHashMap; -import lombok.*; +import lombok.RequiredArgsConstructor; +import lombok.val; +import lombok.var; import org.joml.Vector3i; import org.joml.Vector3ic; @@ -16,12 +18,10 @@ public class FungeSpace implements Copiable { private final TIntObjectMap>> storage = new TIntObjectHashMap<>(); private final Vector3i cachePos = new Vector3i(); - private Chunk cacheChunk; - - private boolean boundsRecheck = false; private final Bounds bounds = new Bounds(); - private final int defaultValue; + private Chunk cacheChunk; + private boolean boundsRecheck = false; private FungeSpace(FungeSpace original) { original.storage.forEachEntry((z, oPlane) -> { @@ -46,6 +46,17 @@ private FungeSpace(FungeSpace original) { cacheChunk = null; } + private static void minMax(int[] arr, int[] buf) { + int max = Integer.MIN_VALUE; + int min = Integer.MAX_VALUE; + for (int i = 0; i < arr.length; i++) { + max = Math.max(arr[i], max); + min = Math.min(arr[i], min); + } + buf[0] = min; + buf[1] = max; + } + public int get(int x, int y, int z) { val cX = toChunkX(x); val cY = toChunkY(y); @@ -91,7 +102,8 @@ public void set(int x, int y, int z, int value) { } var chunk = row.get(cX); if (chunk == null) { - if (value == defaultValue) return; + if (value == defaultValue) + return; chunk = Chunk.allocate(defaultValue); row.put(cX, chunk); } @@ -107,13 +119,13 @@ public void set(Vector3ic v, int value) { public void gc() { cacheChunk = null; val planes = storage.keys(); - for (val iPlane: planes) { + for (val iPlane : planes) { val plane = storage.get(iPlane); val rows = plane.keys(); - for (val iRow: rows) { + for (val iRow : rows) { val row = plane.get(iRow); val chunks = row.keys(); - for (val iChunk: chunks) { + for (val iChunk : chunks) { val chunk = row.get(iChunk); if (chunk.isEmpty()) { row.remove(iChunk); @@ -146,7 +158,8 @@ public Vector3i loadFileAt(int x, int y, int z, byte[] data, boolean trefunge) { int c = Byte.toUnsignedInt(data[i]); switch (c) { case '\r': - if (i < data.length - 1 && data[i + 1] == '\n') continue; + if (i < data.length - 1 && data[i + 1] == '\n') + continue; case '\n': X = x; Y++; @@ -215,7 +228,8 @@ public BoundsC bounds() { } public void recheckBounds() { - if (!boundsRecheck) return; + if (!boundsRecheck) + return; gc(); boundsRecheck = false; if (storage.size() == 0) { @@ -259,7 +273,7 @@ public void recheckBounds() { } } zMaxFinal = fromChunkZ(cZMax) + best; - for (var cZ: cZArr) { + for (var cZ : cZArr) { plane = storage.get(cZ); int yMin; int yMax; @@ -289,7 +303,7 @@ public void recheckBounds() { yMax = fromChunkY(cYMax) + best; yMinFinal = Math.min(yMinFinal, yMin); yMaxFinal = Math.max(yMaxFinal, yMax); - for (val cY: cYArr) { + for (val cY : cYArr) { row = plane.get(cY); int xMin; int xMax; @@ -306,17 +320,6 @@ public void recheckBounds() { bounds.set(xMinFinal, yMinFinal, zMinFinal, xMaxFinal, yMaxFinal, zMaxFinal); } - private static void minMax(int[] arr, int[] buf) { - int max = Integer.MIN_VALUE; - int min = Integer.MAX_VALUE; - for (int i = 0; i < arr.length; i++) { - max = Math.max(arr[i], max); - min = Math.min(arr[i], min); - } - buf[0] = min; - buf[1] = max; - } - @Override public FungeSpace deepCopy() { return new FungeSpace(this); diff --git a/src/test/java/com/falsepattern/jfunge/storage/TestChunk.java b/src/test/java/com/falsepattern/jfunge/storage/TestChunk.java index d785000..c2e5a22 100644 --- a/src/test/java/com/falsepattern/jfunge/storage/TestChunk.java +++ b/src/test/java/com/falsepattern/jfunge/storage/TestChunk.java @@ -13,7 +13,8 @@ public class TestChunk { @Test public void testSetGet() { val rngSeed = System.nanoTime(); - @Cleanup val chunk = Chunk.allocate(0); + @Cleanup + val chunk = Chunk.allocate(0); var rng = new Random(rngSeed); for (int z = 0; z < Chunk.CHUNK_EDGE_SIZE_Z; z++) @@ -31,7 +32,8 @@ public void testSetGet() { @Test public void testDefaultValue() { for (int i = -10; i < 10; i++) { - @Cleanup val chunk = Chunk.allocate(i); + @Cleanup + val chunk = Chunk.allocate(i); for (int z = 0; z < Chunk.CHUNK_EDGE_SIZE_Z; z++) for (int y = 0; y < Chunk.CHUNK_EDGE_SIZE_Y; y++) for (int x = 0; x < Chunk.CHUNK_EDGE_SIZE_X; x++) @@ -41,7 +43,8 @@ public void testDefaultValue() { @Test public void testEmpty() { - @Cleanup val chunk = Chunk.allocate(0); + @Cleanup + val chunk = Chunk.allocate(0); Assertions.assertTrue(chunk.isEmpty()); chunk.set(1, 1, 0, 3); Assertions.assertFalse(chunk.isEmpty()); diff --git a/src/test/java/com/falsepattern/jfunge/storage/TestFungeSpace.java b/src/test/java/com/falsepattern/jfunge/storage/TestFungeSpace.java index 830d44d..5f02085 100644 --- a/src/test/java/com/falsepattern/jfunge/storage/TestFungeSpace.java +++ b/src/test/java/com/falsepattern/jfunge/storage/TestFungeSpace.java @@ -1,11 +1,9 @@ package com.falsepattern.jfunge.storage; import lombok.val; -import org.joml.Vector4i; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import java.awt.*; import java.nio.charset.StandardCharsets; import static com.falsepattern.jfunge.storage.Chunk.*; @@ -15,6 +13,7 @@ private static int toPos(int fragment, int es) { int sign = -((fragment >>> 1) & 1); return sign + (1 + 2 * sign) * ((fragment & 1) * es); } + @Test public void testSetGet() { val fungeSpace = new FungeSpace(0); diff --git a/src/test/java/com/falsepattern/jfunge/storage/TestMycology.java b/src/test/java/com/falsepattern/jfunge/storage/TestMycology.java index ea36423..586c4e0 100644 --- a/src/test/java/com/falsepattern/jfunge/storage/TestMycology.java +++ b/src/test/java/com/falsepattern/jfunge/storage/TestMycology.java @@ -6,42 +6,19 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; public class TestMycology { - @Test - public void testMycology() { - val input = new ByteArrayInputStream(new byte[0]); - val output = new ByteArrayOutputStream(); - val program = new ByteArrayOutputStream(); - Assertions.assertDoesNotThrow(() -> { - val reader = TestMycology.class.getResourceAsStream("/mycology.b98"); - Assertions.assertNotNull(reader); - var read = 0; - val b = new byte[4096]; - while ((read = reader.read(b)) > 0) { - program.write(b, 0, read); - } - }); - val returnCode = Assertions.assertDoesNotThrow(() -> Interpreter.executeProgram(false, new String[]{"mycology.b98"}, program.toByteArray(), 300000, input, output, fakeSupplier)); - val txt = output.toString(); - Assertions.assertTrue(Arrays.stream(txt.split("\n")).noneMatch((line) -> { - if (line.startsWith("BAD")) { - System.out.println(line); - return true; - } else { - return false; - } - })); - Assertions.assertEquals(15, returnCode); - } - private static final Interpreter.FileIOSupplier fakeSupplier = new Interpreter.FileIOSupplier() { private final Map files = new HashMap<>(); + @Override public byte[] readFile(String file) throws IOException { if (files.containsKey(file)) { @@ -70,4 +47,31 @@ public boolean writeFile(String file, byte[] data) throws IOException { return true; } }; + + @Test + public void testMycology() { + val input = new ByteArrayInputStream(new byte[0]); + val output = new ByteArrayOutputStream(); + val program = new ByteArrayOutputStream(); + Assertions.assertDoesNotThrow(() -> { + val reader = TestMycology.class.getResourceAsStream("/mycology.b98"); + Assertions.assertNotNull(reader); + var read = 0; + val b = new byte[4096]; + while ((read = reader.read(b)) > 0) { + program.write(b, 0, read); + } + }); + val returnCode = Assertions.assertDoesNotThrow(() -> Interpreter.executeProgram(false, new String[]{"mycology.b98"}, program.toByteArray(), 300000, input, output, fakeSupplier)); + val txt = output.toString(); + Assertions.assertTrue(Arrays.stream(txt.split("\n")).noneMatch((line) -> { + if (line.startsWith("BAD")) { + System.out.println(line); + return true; + } else { + return false; + } + })); + Assertions.assertEquals(15, returnCode); + } }