const std = @import("std"); const Instruction = enum(u32) { PSH, ADD, POP, SET, HLT, }; const default_program = [_]u32{ @enumToInt(Instruction.PSH), 5, @enumToInt(Instruction.PSH), 6, @enumToInt(Instruction.ADD), @enumToInt(Instruction.POP), @enumToInt(Instruction.HLT), }; const Stack = std.ArrayList(u32); const VM = struct { program: []const u32, stack: Stack, instruction_pointer: usize = 0, stack_pointer: usize = 0, running: bool = false, const Self = @This(); pub fn init(allocator: *std.mem.Allocator, program: []const u32) !Self { return Self{ .program = program, .stack = try Stack.initCapacity(allocator, 256), .instruction_pointer = 0, }; } pub fn deinit(self: Self) void { self.stack.deinit(); } pub fn fetch(self: Self) u32 { return self.program[self.instruction_pointer]; } pub fn runOne(self: *Self, instruction_as_number: u32) !void { const instruction: Instruction = try std.meta.intToEnum(Instruction, instruction_as_number); std.log.info("running {}", .{instruction}); switch (instruction) { .PSH => { self.instruction_pointer += 1; const value = self.fetch(); self.stack.appendAssumeCapacity(value); }, .POP => { const value = self.stack.pop(); std.log.info("stack: popped {}", .{value}); }, .ADD => { const value_a = self.stack.pop(); const value_b = self.stack.pop(); self.stack.appendAssumeCapacity(value_b + value_a); }, .HLT => self.running = false, else => unreachable, } } }; pub fn main() anyerror!void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer { _ = gpa.deinit(); } const allocator = &gpa.allocator; var vm = try VM.init(allocator, &default_program); defer vm.deinit(); vm.running = true; while (vm.running) { try vm.runOne(vm.fetch()); vm.instruction_pointer += 1; } }