commit dc9ac95d0cb79567bce37c0b500f35cb1b180c29
parent 9eb0256d8f18f61ec8467d4d7a9b653861e4d915
Author: Walther Chen <walther.chen@gmail.com>
Date: Mon, 7 Nov 2022 21:15:49 -0500
refactor to have common lib and then bin per stage
Diffstat:
M | build.zig | | | 40 | +++++++++++++++++++++++----------------- |
A | src/common.zig | | | 73 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
D | src/main.zig | | | 138 | ------------------------------------------------------------------------------- |
A | src/og.zig | | | 81 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
4 files changed, 177 insertions(+), 155 deletions(-)
diff --git a/build.zig b/build.zig
@@ -11,24 +11,30 @@ pub fn build(b: *std.build.Builder) void {
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const mode = b.standardReleaseOptions();
- const exe = b.addExecutable("brainz", "src/main.zig");
- exe.setTarget(target);
- exe.setBuildMode(mode);
- exe.install();
-
- const run_cmd = exe.run();
- run_cmd.step.dependOn(b.getInstallStep());
- if (b.args) |args| {
- run_cmd.addArgs(args);
- }
+ const stages = [_][]const u8{
+ "og",
+ };
- const run_step = b.step("run", "Run the app");
- run_step.dependOn(&run_cmd.step);
+ const test_step = b.step("test", "Run unit tests");
- const exe_tests = b.addTest("src/main.zig");
- exe_tests.setTarget(target);
- exe_tests.setBuildMode(mode);
+ inline for (stages) |stage| {
+ const exe = b.addExecutable(stage, "src/" ++ stage ++ ".zig");
+ exe.setTarget(target);
+ exe.setBuildMode(mode);
+ exe.install();
- const test_step = b.step("test", "Run unit tests");
- test_step.dependOn(&exe_tests.step);
+ const run_cmd = exe.run();
+ run_cmd.step.dependOn(b.getInstallStep());
+ if (b.args) |args| {
+ run_cmd.addArgs(args);
+ }
+
+ const run_step = b.step(stage, "Run " ++ stage);
+ run_step.dependOn(&run_cmd.step);
+
+ const exe_tests = b.addTest("src/" ++ stage ++ ".zig");
+ exe_tests.setTarget(target);
+ exe_tests.setBuildMode(mode);
+ test_step.dependOn(&exe_tests.step);
+ }
}
diff --git a/src/common.zig b/src/common.zig
@@ -0,0 +1,73 @@
+// Each program will have it's own `interpret` fn. This module holds all the other setup
+// for running a bf program.
+
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const ArrayList = std.ArrayList;
+
+const MEMORY_SIZE = 30000;
+
+pub fn runInterpreter(interpret: anytype) anyerror!void {
+ var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
+ defer arena.deinit();
+ const alloc = arena.allocator();
+
+ var args = std.process.args();
+ _ = args.skip();
+ const source_path = args.next() orelse return error.MissingSourcePathArg;
+ const f = try std.fs.cwd().openFile(source_path, .{});
+ defer f.close();
+
+ const src = try f.readToEndAlloc(alloc, 1024 * 20);
+
+ var stdin = std.io.getStdIn();
+ var stdout = std.io.getStdOut();
+ var memory = [_]u8{0} ** MEMORY_SIZE;
+
+ const program = try parse(src, alloc);
+ defer program.deinit();
+
+ try interpret(program, &memory, stdin.reader(), stdout.writer());
+}
+
+fn parse(src: []const u8, alloc: std.mem.Allocator) !Program {
+ var instructions = ArrayList(u8).init(alloc);
+
+ for (src) |c| {
+ switch (c) {
+ '>', '<', '+', '-', '.', ',', '[', ']' => try instructions.append(c),
+ else => {},
+ }
+ }
+ return .{ .instructions = instructions.toOwnedSlice(), .alloc = alloc };
+}
+
+pub const Program = struct {
+ alloc: Allocator,
+ instructions: []const u8,
+
+ fn deinit(self: Program) void {
+ self.alloc.free(self.instructions);
+ }
+};
+
+pub fn testHelloWorld(interpret: anytype) anyerror!void {
+ const expectEqualSlices = std.testing.expectEqualSlices;
+
+ // Doesn't test parsing stage (so this input cannot have comments)
+ const hello_world = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.";
+
+ var list = ArrayList(u8).init(std.testing.allocator);
+ defer list.deinit();
+
+ var in_buf: [0]u8 = undefined;
+ var empty_in = std.io.fixedBufferStream(&in_buf);
+
+ var memory = [_]u8{0} ** 30000;
+ var rdr = empty_in.reader();
+ var wtr = list.writer();
+
+ const program = Program{ .instructions = hello_world, .alloc = std.testing.allocator };
+ try interpret(program, &memory, rdr, wtr);
+ try expectEqualSlices(u8, "Hello World!\n", list.items);
+}
diff --git a/src/main.zig b/src/main.zig
@@ -1,138 +0,0 @@
-const std = @import("std");
-const ArrayList = std.ArrayList;
-const expectEqualSlices = std.testing.expectEqualSlices;
-
-const MEMORY_SIZE = 30000;
-
-pub fn main() anyerror!void {
- var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena.deinit();
- const alloc = arena.allocator();
-
- var args = std.process.args();
- _ = args.skip();
- const source_path = args.next() orelse return error.MissingSourcePathArg;
- const f = try std.fs.cwd().openFile(source_path, .{});
- defer f.close();
-
- const src = try f.readToEndAlloc(alloc, 1024 * 20);
-
- var stdin = std.io.getStdIn();
- var stdout = std.io.getStdOut();
- var memory = [_]u8{0} ** MEMORY_SIZE;
-
- const program = try parse(src, alloc);
- defer program.deinit();
- try interpret(program, &memory, stdin.reader(), stdout.writer());
-}
-
-fn parse(src: []const u8, alloc: std.mem.Allocator) !Program {
- var instructions = ArrayList(u8).init(alloc);
-
- for (src) |c| {
- switch (c) {
- '>', '<', '+', '-', '.', ',', '[', ']' => try instructions.append(c),
- else => {},
- }
- }
- return .{ .instructions = instructions.toOwnedSlice(), .alloc = alloc };
-}
-
-fn interpret(program: Program, memory: []u8, rdr: anytype, wtr: anytype) !void {
- var pc: usize = 0;
- var dataptr: usize = 0;
-
- while (pc < program.instructions.len) {
- const instructions = program.instructions;
- const instruction = instructions[pc];
-
- switch (instruction) {
- '>' => dataptr += 1,
- '<' => dataptr -= 1,
- '+' => memory[dataptr] += 1,
- '-' => memory[dataptr] -= 1,
- ',' => memory[dataptr] = try rdr.readByte(),
- '.' => try wtr.writeByte(memory[dataptr]),
- // jumps to next matching ']' if curr_data == 0
- '[' => blk: {
- if (memory[dataptr] != 0) {
- break :blk;
- }
-
- var bracket_nesting: usize = 1;
- var saved_pc = pc; // used for error message only
-
- while (bracket_nesting != 0 and pc < instructions.len - 1) {
- pc += 1;
-
- if (instructions[pc] == ']') {
- bracket_nesting -= 1;
- } else if (instructions[pc] == '[') {
- bracket_nesting += 1;
- }
- }
-
- if (bracket_nesting != 0) {
- std.debug.print("unmatched '[' at pc={}", .{saved_pc});
- }
- },
- // jumps to previous matching ']' if curr data != 0
- ']' => blk: {
- if (memory[dataptr] == 0) {
- break :blk;
- }
-
- var bracket_nesting: usize = 1;
- var saved_pc = pc; // used for error message only
-
- while (bracket_nesting != 0 and pc > 0) {
- pc -= 1;
-
- if (instructions[pc] == '[') {
- bracket_nesting -= 1;
- } else if (instructions[pc] == ']') {
- bracket_nesting += 1;
- }
- }
-
- if (bracket_nesting != 0) {
- std.debug.print("unmatched ']' at pc={}", .{saved_pc});
- }
- },
- else => {
- return error.unreachableChar;
- },
- }
-
- pc += 1;
- }
-}
-
-const Program = struct {
- alloc: std.mem.Allocator,
- instructions: []const u8,
-
- fn deinit(self: Program) void {
- self.alloc.free(self.instructions);
- }
-};
-
-test "interpret hello world" {
- // Doesn't test parsing stage (so this input cannot have comments)
-
- const hello_world = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.";
-
- var list = ArrayList(u8).init(std.testing.allocator);
- defer list.deinit();
-
- var in_buf: [0]u8 = undefined;
- var empty_in = std.io.fixedBufferStream(&in_buf);
-
- var memory = [_]u8{0} ** 30000;
- var rdr = empty_in.reader();
- var wtr = list.writer();
-
- const program = Program{ .instructions = hello_world, .alloc = std.testing.allocator };
- try interpret(program, &memory, rdr, wtr);
- try expectEqualSlices(u8, "Hello World!\n", list.items);
-}
diff --git a/src/og.zig b/src/og.zig
@@ -0,0 +1,81 @@
+const std = @import("std");
+const common = @import("common.zig");
+const Program = common.Program;
+
+pub fn main() anyerror!void {
+ try common.runInterpreter(interpret);
+}
+
+test "og: interpret hello world" {
+ try common.testHelloWorld(interpret);
+}
+
+fn interpret(program: Program, memory: []u8, rdr: anytype, wtr: anytype) !void {
+ var pc: usize = 0;
+ var dataptr: usize = 0;
+
+ while (pc < program.instructions.len) {
+ const instructions = program.instructions;
+ const instruction = instructions[pc];
+
+ switch (instruction) {
+ '>' => dataptr += 1,
+ '<' => dataptr -= 1,
+ '+' => memory[dataptr] += 1,
+ '-' => memory[dataptr] -= 1,
+ ',' => memory[dataptr] = try rdr.readByte(),
+ '.' => try wtr.writeByte(memory[dataptr]),
+ // jumps to next matching ']' if curr_data == 0
+ '[' => blk: {
+ if (memory[dataptr] != 0) {
+ break :blk;
+ }
+
+ var bracket_nesting: usize = 1;
+ var saved_pc = pc; // used for error message only
+
+ while (bracket_nesting != 0 and pc < instructions.len - 1) {
+ pc += 1;
+
+ if (instructions[pc] == ']') {
+ bracket_nesting -= 1;
+ } else if (instructions[pc] == '[') {
+ bracket_nesting += 1;
+ }
+ }
+
+ if (bracket_nesting != 0) {
+ std.debug.print("unmatched '[' at pc={}", .{saved_pc});
+ }
+ },
+ // jumps to previous matching ']' if curr data != 0
+ ']' => blk: {
+ if (memory[dataptr] == 0) {
+ break :blk;
+ }
+
+ var bracket_nesting: usize = 1;
+ var saved_pc = pc; // used for error message only
+
+ while (bracket_nesting != 0 and pc > 0) {
+ pc -= 1;
+
+ if (instructions[pc] == '[') {
+ bracket_nesting -= 1;
+ } else if (instructions[pc] == ']') {
+ bracket_nesting += 1;
+ }
+ }
+
+ if (bracket_nesting != 0) {
+ std.debug.print("unmatched ']' at pc={}", .{saved_pc});
+ }
+ },
+ else => {
+ return error.unreachableChar;
+ },
+ }
+
+ pc += 1;
+ }
+}