aboutsummaryrefslogtreecommitdiffstats
path: root/src/common/console8.zig
blob: c7789557e0f413c233934e764ed9c105529009d1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
const std = @import("std");
const aoc = @import("aoc.zig");

pub const OpError = error{ InstructionPointerOOB, InvalidFormat, InstructionUnknown };
pub const OpFuncSig = fn (ctx: *Console, arg: i64) OpError!void;
pub const Instruction = struct { opcode: []const u8, opfunc: *const OpFuncSig, argval: i64 };

pub const Console = struct {
    accumulator: i64 = 0,
    instructptr: u64 = 0,

    jumpAddr: i65 = 0,

    code: []const u8,
    instlist: [][]const u8,
    allocator: std.mem.Allocator,
    const Self = @This();

    pub fn init(code: []const u8, allocator: std.mem.Allocator) !Self {
        var instvec = std.ArrayList([]const u8).init(allocator);
        errdefer instvec.deinit();

        var instit = std.mem.tokenize(u8, code, "\n");
        while (instit.next()) |inst| {
            try instvec.append(inst);
        }
        return Console{
            .code = code,
            .instlist = instvec.toOwnedSlice(),
            .allocator = allocator,
        };
    }

    pub fn deinit(self: *Self) void {
        self.allocator.free(self.instlist);
    }

    pub fn reset(self: *Self) void {
        self.accumulator = 0;
        self.instructptr = 0;
    }

    const instructionMap = std.ComptimeStringMap(*const OpFuncSig, .{
        .{ "jmp", jumpInstruction },
        .{ "acc", accInstruction },
        .{ "nop", nopInstruction },
    });

    pub fn jumpInstruction(self: *Self, arg: i64) OpError!void {
        self.jumpAddr = @intCast(i65, self.instructptr) + @intCast(i65, arg);
        if (self.jumpAddr < 0 or self.jumpAddr >= self.instlist.len)
            return error.InstructionPointerOOB;
        self.instructptr = @intCast(u64, self.jumpAddr);
    }

    pub fn accInstruction(self: *Self, arg: i64) OpError!void {
        self.accumulator += arg;
        self.instructptr += 1;
    }

    pub fn nopInstruction(self: *Self, arg: i64) OpError!void {
        _ = arg;
        self.instructptr += 1;
    }

    pub fn parseNext(self: *Self) !?Instruction {
        if (self.instructptr >= self.instlist.len)
            return null;

        const inststr = self.instlist[self.instructptr];
        const sep = std.mem.indexOfScalar(u8, inststr, ' ');
        if (sep == null) return OpError.InvalidFormat;

        const opcode = inststr[0..sep.?];
        if (instructionMap.get(opcode)) |opfunc| {
            const arg = inststr[sep.? + 1 ..];
            const val = std.fmt.parseInt(i64, arg, 10) catch {
                aoc.debugfmt("Failed to parse arg value: {s}\n", .{arg});
                return OpError.InvalidFormat;
            };
            return Instruction{ .opcode = opcode, .opfunc = opfunc, .argval = val };
        } else {
            aoc.debugfmt("Unknown instruction: {s}\n", .{inststr});
            return OpError.InstructionUnknown;
        }
    }

    pub fn exec(self: *Self, inst: *Instruction) !void {
        try inst.opfunc(self, inst.argval);
    }
};