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:
Mbuild.zig | 40+++++++++++++++++++++++-----------------
Asrc/common.zig | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/main.zig | 138-------------------------------------------------------------------------------
Asrc/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; + } +}