//brainf const std = @import("std"); const io = std.io; const fs = std.fs; const mem = std.mem; const process = std.process; const testing = std.testing; const ArrayList = std.ArrayList; const Level1 = union(enum) { // think about it... i hope inc: i16, shift: i16, // u8 because why would we be reading to/writing from the same cell 256 times in a row // well, might as well use the extra space (union is bigger than its biggest member) in: u16, out: u16, loop_start, loop_end, const Self = @This(); fn pointless(self: Self) bool { return switch (self) { .inc, .shift => |n| n == 0, .in, .out => |n| n == 0, else => false, }; } }; const Level2 = union(enum) { inc: i16, shift: i16, in: u16, out: u16, // only difference // u24s because i arbitrarily chose 2^24 as the limit // maybe u32 would be more efficient idk this union should fit in a u32 // hm we can probably go way lower bc level1 is so much more compact than prog_unfiltered // shouldn't be necessary loop_start: u24, loop_end: u24, }; // we'll do the discarding of comment chars here fn compileLevel1(allocator: mem.Allocator, src: []const u8) !ArrayList(Level1) { var cur: ?Level1 = null; var out = ArrayList(Level1).init(allocator); for (src) |b| { switch (b) { '+', '-' => { const n: i16 = if (b == '+') 1 else -1; if (cur) |*ins| { switch (ins.*) { .inc => |*ns| ns.* += n, else => { if (!ins.pointless()) try out.append(ins.*); cur = .{ .inc = n }; }, } } else { cur = .{ .inc = n }; } }, '>', '<' => { const n: i16 = if (b == '>') 1 else -1; if (cur) |*ins| { switch (ins.*) { .shift => |*ns| ns.* += n, else => { if (!ins.pointless()) try out.append(ins.*); cur = .{ .shift = n }; }, } } else { cur = .{ .shift = n }; } }, '[', ']' => { if (cur) |ins| { if (!ins.pointless()) try out.append(ins); cur = null; } try out.append(if (b == '[') .loop_start else .loop_end); }, ',' => { if (cur) |*ins| { switch (ins.*) { .in => |*ns| ns.* += 1, else => { if (!ins.pointless()) try out.append(ins.*); cur = .{ .in = 1 }; }, } } else { cur = .{ .in = 1 }; } }, '.' => { if (cur) |*ins| { switch (ins.*) { .out => |*ns| ns.* += 1, else => { if (!ins.pointless()) try out.append(ins.*); cur = .{ .out = 1 }; }, } } else { cur = .{ .out = 1 }; } }, else => {}, } } if (cur) |ins| { if (!ins.pointless()) try out.append(ins); } return out; } test "level 1" { const src = "+++-- [ >><<< +- ] ...,,,"; const level1 = try compileLevel1(testing.allocator, src); defer level1.deinit(); std.debug.print("{any}\n", .{level1.items}); const should_be = [_]Level1{ .{ .inc = 1 }, .loop_start, .{ .shift = -1 }, .loop_end, .{ .out = 3 }, .{ .in = 3 }, }; try testing.expectEqual(should_be.len, level1.items.len); for ( level1.items, &should_be, ) |a, b| { try testing.expectEqual(a, b); } } pub fn main() !void { var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); var allocator = arena.allocator(); defer arena.deinit(); var out = io.getStdOut().writer(); var in = io.getStdIn().reader(); var args = process.args(); defer args.deinit(); _ = args.skip(); const prog_path = args.next() orelse { try io.getStdErr().writer().writeAll("i need a program path :(\n"); process.exit(1); }; var prog_file = try fs.cwd().openFile(prog_path, .{}); const prog_unfiltered = try prog_file.readToEndAlloc(allocator, 1 << 24); var prog = try allocator.alloc(u8, prog_unfiltered.len); var prog_len: u32 = 0; for (prog_unfiltered) |c| { if (c == '+' or c == '-' or c == '>' or c == '<' or c == '[' or c == ']' or c == '.' or c == ',') { prog[prog_len] = c; prog_len += 1; } } var memory = try allocator.alloc(u8, 1 << 16); for (memory) |*m| { m.* = 0; } var mp: u16 = 0; var ip: u32 = 0; while (ip < prog_len) { switch (prog[ip]) { '+' => memory[mp] +%= 1, '-' => memory[mp] -%= 1, '>' => mp +%= 1, '<' => mp -%= 1, '[' => { if (memory[mp] == 0) { var depth: i16 = 1; ip += 1; while (depth > 0) : (ip += 1) { depth += switch (prog[ip]) { ']' => -1, '[' => 1, else => 0, }; } ip -= 1; } }, ']' => { if (memory[mp] != 0) { var depth: i16 = 1; ip -= 1; while (depth > 0) : (ip -= 1) { depth += switch (prog[ip]) { '[' => -1, ']' => 1, else => 0, }; } ip += 1; } }, ',' => memory[mp] = in.readByte() catch 0, '.' => try out.writeByte(memory[mp]), else => unreachable, } ip += 1; } }