~ntgg/PGLang

ref: 5b64a42f5b965a902bb3d349ffb583b4738b8dac PGLang/src/vm.zig -rw-r--r-- 2.2 KiB View raw
5b64a42fNoah Graff moved VM to seperate file 3 months ago
                                                                                
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
const std = @import("std");
const Allocator = std.mem.Allocator;
const parse = @import("parse.zig").parse;
const Instruction = @import("parse.zig").Instruction;

extern fn readByte() u8;
extern fn writeByte(byte: u8) void;

pub const VM = struct {
    const Self = @This();

    allocator: *Allocator,
    instructions: std.ArrayList(Instruction),
    current_instr: usize,
    memory: []u8,
    current_cell: usize,

    pub fn init(allocator: *Allocator, source: []const u8) !Self {
        var memory = try allocator.alloc(u8, 30000);
        std.mem.secureZero(u8, memory);
        return Self{
            .allocator = allocator,
            .instructions = try parse(allocator, source),
            .current_instr = 0,
            .memory = memory,
            .current_cell = 0,
        };
    }

    pub fn deinit(vm: *Self) void {
        vm.instructions.deinit();
        vm.allocator.free(vm.memory);
    }

    pub fn run(vm: *Self) !void {
        while (try vm.step()) {}
    }

    // false if should finish
    pub fn step(vm: *Self) !bool {
        if (vm.current_instr >= vm.instructions.len) return false;

        if (vm.current_cell > vm.memory.len) {
            vm.memory = try vm.allocator.realloc(vm.memory, std.math.max(vm.memory.len * 2, vm.current_cell));
        }

        const instr = vm.instructions.toSliceConst()[vm.current_instr];
        const cell = &vm.memory[vm.current_cell];
        switch (instr) {
            .Incr => |by| {
                cell.* +%= by;
            },
            .Decr => |by| cell.* -%= by,
            .Next => |cells| vm.current_cell += cells,
            .Prev => |cells| {
                if (vm.current_cell < cells) return error.InvalidPointer;
                vm.current_cell -= cells;
            },
            .Output => {
                writeByte(cell.*);
            },
            .Input => {
                cell.* = readByte();
            },
            .LoopStart => |end| if (cell.* == 0) {
                // parse returns only vaild code, so .? is safe
                vm.current_instr = end.?;
            },
            .LoopEnd => |start| if (cell.* != 0) {
                vm.current_instr = start;
            },
        }

        vm.current_instr += 1;
        return true;
    }
};