Reworked io functions to take a configurable reader or writer
This commit is contained in:
parent
35c2f5e626
commit
aa56929ff7
4 changed files with 68 additions and 61 deletions
|
@ -13,19 +13,19 @@ pub const Interpreter = struct {
|
|||
self.field.deinit();
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
pub fn init(allocator: std.mem.Allocator, reader: anytype, ioFunctions: io.Functions, args: []const []const u8) !*Interpreter {
|
||||
pub fn init(allocator: std.mem.Allocator, fileReader: anytype, args: []const []const u8) !*Interpreter {
|
||||
var i = try allocator.create(Interpreter);
|
||||
errdefer allocator.destroy(i);
|
||||
i.allocator = allocator;
|
||||
i.field = try field.Field.init_from_reader(allocator, reader);
|
||||
i.field = try field.Field.init_from_reader(allocator, fileReader);
|
||||
errdefer i.field.deinit();
|
||||
i.pointer = try pointer.Pointer.init(std.testing.allocator, i.field, ioFunctions, args);
|
||||
i.pointer = try pointer.Pointer.init(std.testing.allocator, i.field, args);
|
||||
errdefer i.pointer.deinit();
|
||||
return i;
|
||||
}
|
||||
pub fn run(self: *Interpreter) !i64 {
|
||||
pub fn run(self: *Interpreter, ioContext: anytype) !i64 {
|
||||
while (true) {
|
||||
if (try self.pointer.exec()) |ret| {
|
||||
if (try self.pointer.exec(ioContext)) |ret| {
|
||||
if (ret.code) |code| {
|
||||
return code;
|
||||
} else {
|
||||
|
|
65
src/io.zig
65
src/io.zig
|
@ -1,42 +1,37 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub const IOErrors = error{
|
||||
IOError,
|
||||
NotImplemented,
|
||||
};
|
||||
|
||||
pub const Functions = struct {
|
||||
characterInput: fn () IOErrors!i64,
|
||||
decimalInput: fn () IOErrors!i64,
|
||||
characterOutput: fn (i64) IOErrors!void,
|
||||
decimalOutput: fn (i64) IOErrors!void,
|
||||
};
|
||||
|
||||
pub const defaultFunctions = Functions{
|
||||
.characterInput = characterInput,
|
||||
.decimalInput = decimalInput,
|
||||
.characterOutput = characterOutput,
|
||||
.decimalOutput = decimalOutput,
|
||||
};
|
||||
|
||||
fn characterInput() IOErrors!i64 {
|
||||
// TODO
|
||||
return error.NotImplemented;
|
||||
pub fn Context(readerType: anytype, writerType: anytype) type {
|
||||
return struct {
|
||||
reader: readerType,
|
||||
writer: writerType,
|
||||
pub fn characterInput(self: @This()) !i64 {
|
||||
var buffer = [_]u8{0};
|
||||
var n = try self.reader.read(buffer[0..]);
|
||||
if (n == 1) {
|
||||
return buffer[0];
|
||||
}
|
||||
return error.IOError;
|
||||
}
|
||||
pub fn decimalInput(self: @This()) !i64 {
|
||||
_ = self;
|
||||
return error.NotImplemented;
|
||||
}
|
||||
pub fn characterOutput(self: @This(), v: i64) !void {
|
||||
try self.writer.print("{c}", .{@intCast(u8, v)});
|
||||
return;
|
||||
}
|
||||
pub fn decimalOutput(self: @This(), v: i64) !void {
|
||||
try self.writer.print("{d}", .{v});
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn decimalInput() IOErrors!i64 {
|
||||
// TODO
|
||||
return error.NotImplemented;
|
||||
}
|
||||
|
||||
fn characterOutput(v: i64) IOErrors!void {
|
||||
std.debug.print("{c}", .{@intCast(u8, v)});
|
||||
return;
|
||||
}
|
||||
|
||||
fn decimalOutput(v: i64) IOErrors!void {
|
||||
std.debug.print("{d}", .{v});
|
||||
return;
|
||||
pub fn context(reader: anytype, writer: anytype) Context(@TypeOf(reader), @TypeOf(writer)) {
|
||||
return .{
|
||||
.reader = reader,
|
||||
.writer = writer,
|
||||
};
|
||||
}
|
||||
|
||||
test "all" {
|
||||
|
|
|
@ -15,10 +15,10 @@ pub fn main() anyerror!void {
|
|||
var file = try std.fs.cwd().openFile("mycology/sanity.bf", .{});
|
||||
defer file.close();
|
||||
|
||||
var i = try interpreter.Interpreter.init(gpa.allocator(), file.reader(), io.defaultFunctions, args);
|
||||
var i = try interpreter.Interpreter.init(gpa.allocator(), file.reader(), args);
|
||||
defer i.deinit();
|
||||
|
||||
std.os.exit(@intCast(u8, try i.run()));
|
||||
std.os.exit(@intCast(u8, try i.run(io.context(std.io.getStdIn().reader(), std.io.getStdOut().writer()))));
|
||||
}
|
||||
|
||||
test "all" {
|
||||
|
@ -28,7 +28,7 @@ test "sanity" {
|
|||
var file = try std.fs.cwd().openFile("mycology/sanity.bf", .{});
|
||||
defer file.close();
|
||||
const args = [_][]const u8{"sanity"};
|
||||
var i = try interpreter.Interpreter.init(std.testing.allocator, file.reader(), io.defaultFunctions, args[0..]);
|
||||
var i = try interpreter.Interpreter.init(std.testing.allocator, file.reader(), args[0..]);
|
||||
defer i.deinit();
|
||||
try std.testing.expectEqual(try i.run(), 0);
|
||||
try std.testing.expectEqual(try i.run(io.context(std.io.getStdIn().reader(), std.io.getStdOut().writer())), 0);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ pub const Pointer = struct {
|
|||
stringMode: bool = false, // string mode flags
|
||||
lastCharWasSpace: bool = false,
|
||||
ss: *stackStack.StackStack,
|
||||
ioFunctions: io.Functions,
|
||||
argv: []const []const u8,
|
||||
rand: *std.rand.Random,
|
||||
|
||||
|
@ -31,7 +30,7 @@ pub const Pointer = struct {
|
|||
self.ss.deinit();
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
fn eval(p: *Pointer, c: i64) pointerErrorType!?pointerReturn {
|
||||
fn eval(p: *Pointer, ioContext: anytype, c: i64) pointerErrorType!?pointerReturn {
|
||||
// Returns non nil if the pointer terminated, and a return code if
|
||||
// the program should terminate completely
|
||||
switch (c) {
|
||||
|
@ -67,9 +66,9 @@ pub const Pointer = struct {
|
|||
p.x = x;
|
||||
p.y = y;
|
||||
if (v != ' ' and v != ';') {
|
||||
if (v == 'q' or v == '@') return try p.eval(v);
|
||||
if (v == 'q' or v == '@') return try p.eval(ioContext, v);
|
||||
var i: usize = 0;
|
||||
while (i < n) : (i += 1) _ = try p.eval(v);
|
||||
while (i < n) : (i += 1) _ = try p.eval(ioContext, v);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -168,11 +167,25 @@ pub const Pointer = struct {
|
|||
const n = p.ss.toss.pop();
|
||||
try p.field.set(v[0] + p.sox, v[1] + p.soy, n);
|
||||
},
|
||||
'.' => try p.ioFunctions.decimalOutput(p.ss.toss.pop()),
|
||||
',' => try p.ioFunctions.characterOutput(p.ss.toss.pop()),
|
||||
'&' => try p.ss.toss.push(try p.ioFunctions.decimalInput()),
|
||||
'~' => try p.ss.toss.push(try p.ioFunctions.characterInput()),
|
||||
// TODO
|
||||
'.' => ioContext.decimalOutput(p.ss.toss.pop()) catch {
|
||||
p.reverse();
|
||||
return null;
|
||||
},
|
||||
',' => ioContext.characterOutput(p.ss.toss.pop()) catch p.reverse(),
|
||||
'&' => {
|
||||
const n = ioContext.decimalInput() catch {
|
||||
p.reverse();
|
||||
return null;
|
||||
};
|
||||
p.ss.toss.push(n) catch p.reverse();
|
||||
},
|
||||
'~' => {
|
||||
const n = ioContext.characterInput() catch {
|
||||
p.reverse();
|
||||
return null;
|
||||
};
|
||||
p.ss.toss.push(n) catch p.reverse();
|
||||
},
|
||||
'y' => return error.NotImplemented,
|
||||
'(' => {
|
||||
const n = p.ss.toss.pop();
|
||||
|
@ -208,7 +221,7 @@ pub const Pointer = struct {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
pub fn exec(self: *Pointer) !?pointerReturn {
|
||||
pub fn exec(self: *Pointer, ioContext: anytype) !?pointerReturn {
|
||||
// Advances to the next instruction of the field and executes it
|
||||
// Returns non nil if the pointer terminated, and a return code if
|
||||
// the program should terminate completely
|
||||
|
@ -233,18 +246,17 @@ pub const Pointer = struct {
|
|||
if (c == ';') jumpingMode = !jumpingMode;
|
||||
c = self.stepAndGet();
|
||||
}
|
||||
result = try self.eval(c);
|
||||
result = try self.eval(ioContext, c);
|
||||
}
|
||||
self.step();
|
||||
return result;
|
||||
}
|
||||
pub fn init(allocator: std.mem.Allocator, f: *field.Field, ioFunctions: io.Functions, argv: []const []const u8) !*Pointer {
|
||||
pub fn init(allocator: std.mem.Allocator, f: *field.Field, argv: []const []const u8) !*Pointer {
|
||||
var p = try allocator.create(Pointer);
|
||||
errdefer allocator.destroy(p);
|
||||
p.allocator = allocator;
|
||||
p.field = f;
|
||||
p.ss = try stackStack.StackStack.init(allocator);
|
||||
p.ioFunctions = ioFunctions;
|
||||
p.argv = argv;
|
||||
p.x = 0;
|
||||
p.y = 0;
|
||||
|
@ -328,16 +340,16 @@ test "minimal" {
|
|||
var f = try field.Field.init_from_reader(std.testing.allocator, minimal);
|
||||
defer f.deinit();
|
||||
const argv = [_][]const u8{"minimal"};
|
||||
var p = try Pointer.init(std.testing.allocator, f, io.defaultFunctions, argv[0..]);
|
||||
var p = try Pointer.init(std.testing.allocator, f, argv[0..]);
|
||||
defer p.deinit();
|
||||
try std.testing.expectEqual(p.exec(), pointerReturn{});
|
||||
try std.testing.expectEqual(p.exec(io.context(std.io.getStdIn().reader(), std.io.getStdOut().writer())), pointerReturn{});
|
||||
}
|
||||
test "almost minimal" {
|
||||
const minimal = std.io.fixedBufferStream(" @").reader();
|
||||
var f = try field.Field.init_from_reader(std.testing.allocator, minimal);
|
||||
defer f.deinit();
|
||||
const argv = [_][]const u8{"minimal"};
|
||||
var p = try Pointer.init(std.testing.allocator, f, io.defaultFunctions, argv[0..]);
|
||||
var p = try Pointer.init(std.testing.allocator, f, argv[0..]);
|
||||
defer p.deinit();
|
||||
try std.testing.expectEqual(p.exec(), pointerReturn{});
|
||||
try std.testing.expectEqual(p.exec(io.context(std.io.getStdIn().reader(), std.io.getStdOut().writer())), pointerReturn{});
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue