Skip to content

Commit 9e401bc

Browse files
committed
user input, file IO, and mycorand test
1 parent 10b097c commit 9e401bc

File tree

8 files changed

+265
-16
lines changed

8 files changed

+265
-16
lines changed

README.MD

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,5 @@ A standard-conforming Funge-98 interpreter.
2424
- [ROMA](https://catseye.tc/view/funge-98/library/ROMA.markdown)
2525

2626
### Additional goals
27-
- [ ] File IO
27+
- [x] File IO
2828
- [x] Concurrency

src/main/java/com/falsepattern/jfunge/Main.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,6 @@ public static void main(String[] args) throws IOException {
6767
} else {
6868
program = Files.readAllBytes(Paths.get(file));
6969
}
70-
System.exit(Interpreter.executeProgram(trefunge, args, program, maxIter, System.in, System.out));
70+
System.exit(Interpreter.executeProgram(trefunge, args, program, maxIter, System.in, System.out, Interpreter.DEFAULT_FILE_IO_SUPPLIER));
7171
}
7272
}

src/main/java/com/falsepattern/jfunge/interpreter/ExecutionContext.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
import com.falsepattern.jfunge.ip.InstructionPointer;
44
import com.falsepattern.jfunge.storage.FungeSpace;
55

6-
import java.io.InputStream;
76
import java.io.OutputStream;
87
import java.util.List;
98
import java.util.Map;
9+
import java.util.Scanner;
1010

1111
public interface ExecutionContext {
1212
InstructionPointer[] allIPs();
@@ -22,6 +22,8 @@ public interface ExecutionContext {
2222
List<String> args();
2323
Map<String, String> env();
2424
int paradigm();
25-
InputStream input();
25+
int input(boolean stagger);
2626
OutputStream output();
27+
byte[] readFile(String file);
28+
boolean writeFile(String file, byte[] data);
2729
}

src/main/java/com/falsepattern/jfunge/interpreter/Interpreter.java

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,42 @@
88
import lombok.Getter;
99
import lombok.experimental.Accessors;
1010
import lombok.val;
11+
import lombok.var;
1112

12-
import java.io.InputStream;
13-
import java.io.OutputStream;
13+
import java.io.*;
14+
import java.nio.file.Files;
15+
import java.nio.file.Paths;
1416
import java.util.*;
1517

1618
@Accessors(fluent = true)
1719
public class Interpreter implements ExecutionContext {
20+
public static final FileIOSupplier DEFAULT_FILE_IO_SUPPLIER = new FileIOSupplier() {
21+
@Override
22+
public byte[] readFile(String file) throws IOException {
23+
return Files.readAllBytes(Paths.get(file));
24+
}
25+
26+
@Override
27+
public boolean writeFile(String file, byte[] data) throws IOException {
28+
Files.write(Paths.get(file), data);
29+
return true;
30+
}
31+
};
1832
@Getter
1933
private final FungeSpace fungeSpace = new FungeSpace(' ');
2034

2135
private final List<InstructionPointer> IPs = new ArrayList<>();
2236

2337
private final InstructionManager baseInstructionManager = new InstructionManager();
2438

25-
@Getter
2639
private final InputStream input;
2740

41+
2842
@Getter
2943
private final OutputStream output;
3044

45+
private final FileIOSupplier fileIOSupplier;
46+
3147
@Getter
3248
private final List<String> args;
3349

@@ -42,8 +58,11 @@ public class Interpreter implements ExecutionContext {
4258

4359
private int nextUUID = 0;
4460

45-
public static int executeProgram(boolean trefunge, String[] args, byte[] program, long iterLimit, InputStream input, OutputStream output) {
46-
val interpreter = new Interpreter(trefunge, args, input, output);
61+
private int inputStagger;
62+
63+
64+
public static int executeProgram(boolean trefunge, String[] args, byte[] program, long iterLimit, InputStream input, OutputStream output, FileIOSupplier fileIOSupplier) {
65+
val interpreter = new Interpreter(trefunge, args, input, output, fileIOSupplier);
4766
interpreter.fungeSpace().loadFileAt(0, 0, 0, program, trefunge);
4867
if (iterLimit > 0) {
4968
long step = 0;
@@ -60,12 +79,13 @@ public static int executeProgram(boolean trefunge, String[] args, byte[] program
6079
return interpreter.exitCode();
6180
}
6281

63-
public Interpreter(boolean trefunge, String[] args, InputStream input, OutputStream output) {
82+
public Interpreter(boolean trefunge, String[] args, InputStream input, OutputStream output, FileIOSupplier fileIOSupplier) {
6483
this.args = Arrays.asList(args);
6584
dimensions = trefunge ? 3 : 2;
6685
baseInstructionManager.loadInstructionSet(Funge98.INSTANCE);
6786
this.input = input;
6887
this.output = output;
88+
this.fileIOSupplier = fileIOSupplier;
6989
val ip = new InstructionPointer();
7090
ip.UUID = nextUUID++;
7191
IPs.add(ip);
@@ -176,6 +196,48 @@ public int paradigm() {
176196
return 0;
177197
}
178198

199+
@Override
200+
public int input(boolean stagger) {
201+
var value = -1;
202+
if (inputStagger > 0) {
203+
value = inputStagger;
204+
inputStagger = -1;
205+
} else {
206+
try {
207+
value = input.read();
208+
} catch (IOException e) {
209+
value = -1;
210+
}
211+
}
212+
if (stagger) {
213+
inputStagger = value;
214+
}
215+
return value;
216+
}
217+
218+
@Override
219+
public byte[] readFile(String file) {
220+
try {
221+
return fileIOSupplier.readFile(file);
222+
} catch (IOException e) {
223+
return null;
224+
}
225+
}
226+
227+
@Override
228+
public boolean writeFile(String file, byte[] data) {
229+
try {
230+
return fileIOSupplier.writeFile(file, data);
231+
} catch (IOException e) {
232+
return false;
233+
}
234+
}
235+
236+
public interface FileIOSupplier {
237+
byte[] readFile(String file) throws IOException;
238+
boolean writeFile(String file, byte[] data) throws IOException;
239+
}
240+
179241
public void tick() {
180242
currentIP = null;
181243
for (int i = 0; i < IPs.size(); i++) {

src/main/java/com/falsepattern/jfunge/interpreter/instructions/Funge98.java

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ public static void sysInfo(ExecutionContext ctx) {
463463
//2 bpc
464464
s.push(4);
465465
//1 flags
466-
s.push(0b00000001);
466+
s.push(0b00000111);
467467
if (n > 0) {
468468
int curr = s.pick(n - 1);
469469
for (int i = s.size(); i >= tossSize; i--) {
@@ -485,6 +485,93 @@ public static void split(ExecutionContext ctx) {
485485
clone.delta.mul(-1);
486486
}
487487

488+
@Instr('i')
489+
public static void input(ExecutionContext ctx) {
490+
val s = ctx.IP().stackStack.TOSS();
491+
val filename = s.popString();
492+
val flags = s.pop();
493+
val pos = new Vector3i();
494+
if (ctx.dimensions() == 3) {
495+
s.pop3(pos);
496+
} else {
497+
pos.set(s.pop2(), 0);
498+
}
499+
pos.add(ctx.IP().storageOffset);
500+
val file = ctx.readFile(filename);
501+
if (file == null) {
502+
ctx.interpret('r');
503+
return;
504+
}
505+
val delta = ((flags & 1) == 1) ? ctx.fungeSpace().loadBinaryFileAt(pos.x, pos.y, pos.z, file) : ctx.fungeSpace().loadFileAt(pos.x, pos.y, pos.z, file, ctx.dimensions() == 3);
506+
pos.sub(ctx.IP().storageOffset);
507+
if (ctx.dimensions() == 3) {
508+
s.push3(delta);
509+
s.push3(pos);
510+
} else {
511+
s.push2(new Vector2i(delta.x, delta.y));
512+
s.push2(new Vector2i(pos.x, pos.y));
513+
}
514+
}
515+
516+
@Instr('o')
517+
public static void output(ExecutionContext ctx) {
518+
val s = ctx.IP().stackStack.TOSS();
519+
val filename = s.popString();
520+
val flags = s.pop();
521+
val pos = new Vector3i();
522+
val delta = new Vector3i();
523+
if (ctx.dimensions() == 3) {
524+
s.pop3(pos);
525+
s.pop3(delta);
526+
} else {
527+
pos.set(s.pop2(), 0);
528+
delta.set(s.pop2(), 1);
529+
}
530+
pos.add(ctx.IP().storageOffset);
531+
val data = ctx.fungeSpace().readDataAt(pos.x, pos.y, pos.z, delta.x, delta.y, delta.z, (flags & 1) == 1);
532+
if (!ctx.writeFile(filename, data)) {
533+
ctx.interpret('r');
534+
}
535+
}
536+
537+
@Instr('&')
538+
@SneakyThrows
539+
public static void readInt(ExecutionContext ctx) {
540+
ctx.output().flush();
541+
var counter = 0;
542+
var found = false;
543+
var next = 0;
544+
while ((next = ctx.input(true)) < '0' || next > '9') {
545+
ctx.input(false);
546+
}
547+
while (true) {
548+
next = ctx.input(true);
549+
if (next >= '0' && next <= '9' && (counter * 10 >= counter)) {
550+
found = true;
551+
counter *= 10;
552+
counter += next - '0';
553+
ctx.input(false);
554+
} else {
555+
if (next == '\n') {
556+
ctx.input(false);
557+
}
558+
break;
559+
}
560+
}
561+
if (found) {
562+
ctx.IP().stackStack.TOSS().push(counter);
563+
} else {
564+
ctx.interpret('r');
565+
}
566+
}
567+
568+
@Instr('~')
569+
@SneakyThrows
570+
public static void readChar(ExecutionContext ctx) {
571+
ctx.output().flush();
572+
ctx.IP().stackStack.TOSS().push(ctx.input(false));
573+
}
574+
488575
@Override
489576
public void load(ObjIntConsumer<Instruction> instructionSet) {
490577
InstructionSet.super.load(instructionSet);

src/main/java/com/falsepattern/jfunge/storage/FungeSpace.java

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import org.joml.Vector3i;
88
import org.joml.Vector3ic;
99

10+
import java.io.ByteArrayOutputStream;
11+
1012
import static com.falsepattern.jfunge.storage.Chunk.*;
1113

1214
@RequiredArgsConstructor
@@ -128,10 +130,18 @@ public void gc() {
128130
}
129131
}
130132

131-
public void loadFileAt(int x, int y, int z, byte[] data, boolean trefunge) {
133+
public Vector3i loadBinaryFileAt(int x, int y, int z, byte[] data) {
134+
for (int i = 0; i < data.length; i++) {
135+
set(x + i, y, z, Byte.toUnsignedInt(data[i]));
136+
}
137+
return new Vector3i(x + data.length - 1, y, z);
138+
}
139+
140+
public Vector3i loadFileAt(int x, int y, int z, byte[] data, boolean trefunge) {
132141
int X = x;
133142
int Y = y;
134143
int Z = z;
144+
val ret = new Vector3i(x, y, z);
135145
for (int i = 0; i < data.length; i++) {
136146
int c = Byte.toUnsignedInt(data[i]);
137147
switch (c) {
@@ -149,9 +159,48 @@ public void loadFileAt(int x, int y, int z, byte[] data, boolean trefunge) {
149159
}
150160
continue;
151161
}
152-
set(X, Y, Z, c);
162+
ret.x = Math.max(ret.x, X);
163+
ret.y = Math.max(ret.y, Y);
164+
ret.z = Math.max(ret.z, Z);
165+
if (c != defaultValue) {
166+
set(X, Y, Z, c);
167+
}
153168
X++;
154169
}
170+
return ret.sub(x - 1, y - 1, z - 1);
171+
}
172+
173+
public byte[] readDataAt(int x, int y, int z, int dX, int dY, int dZ, boolean textFile) {
174+
val out = new ByteArrayOutputStream();
175+
for (int accumulatedFormFeeds = 0, Z = z; Z < z + dZ; Z++, accumulatedFormFeeds++) {
176+
if (!textFile && accumulatedFormFeeds > 0) {
177+
out.write('\t');
178+
}
179+
for (int accumulatedLineFeeds = 0, Y = y; Y < y + dY; Y++, accumulatedLineFeeds++) {
180+
if (!textFile && accumulatedLineFeeds > 0) {
181+
out.write('\n');
182+
}
183+
for (int accumulatedSpaces = 0, X = x; X < x + dX; X++) {
184+
val c = get(X, Y, Z);
185+
if (textFile) {
186+
if (c == defaultValue) {
187+
accumulatedSpaces++;
188+
} else {
189+
for (; accumulatedFormFeeds > 0; accumulatedFormFeeds--)
190+
out.write('\f');
191+
for (; accumulatedLineFeeds > 0; accumulatedLineFeeds--)
192+
out.write('\n');
193+
for (; accumulatedSpaces > 0; accumulatedSpaces--)
194+
out.write(defaultValue);
195+
out.write(c);
196+
}
197+
} else {
198+
out.write(c);
199+
}
200+
}
201+
}
202+
}
203+
return out.toByteArray();
155204
}
156205

157206
public void wipe() {

0 commit comments

Comments
 (0)