~ntgg/PGLang

ref: 97fab4ca046e0180ccfa8653858c19fa6dc6cdd3 PGLang/src/vm.zig -rw-r--r-- 2.8 KiB View raw
97fab4caNoah Graff moved to stream based io, removed wasm temporarily 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
78
79
80
81
82
83
84
const std = @import("std");
const Allocator = std.mem.Allocator;
const parse = @import("parse.zig").parse;
const Instruction = @import("parse.zig").Instruction;

pub fn VM(comptime ReadError: type, comptime WriteError: type) type {
    return struct {
        const Self = @This();

        const InStream = std.io.InStream(ReadError);
        const OutStream = std.io.OutStream(WriteError);

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

        pub fn init(allocator: *Allocator, source: []const u8, in_stream: *InStream, out_stream: *OutStream) !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,
                .in_stream = in_stream,
                .out_stream = out_stream,
            };
        }

        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 => {
                    try vm.out_stream.print("{}", .{&[_]u8{cell.*}});
                },
                .Input => {
                    cell.* = try vm.in_stream.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;
        }
    };
}