Last active
August 5, 2024 16:05
-
-
Save srijan-paul/da9367aa4ae067af8b9a3d3c34ccc532 to your computer and use it in GitHub Desktop.
Simple stack based interpreter.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const std = @import("std"); | |
const Opcode = enum(u8) { | |
push, | |
add, | |
print, | |
exit, | |
eq, // pushes 1 if stack[-1] == stack[-2], 0 otherwise | |
jumpif_1, | |
jump, | |
load_var, | |
store_var, | |
}; | |
const CodeBlock = struct { | |
instructions: []const u8, | |
constants: []const i64, | |
}; | |
const Interpreter = struct { | |
stack: [32_000]i64 = undefined, | |
stack_pos: u64 = 0, | |
blocks: []const CodeBlock, | |
current_block: *const CodeBlock = undefined, | |
pc: usize = 0, | |
pub fn init(blocks: []const CodeBlock) Interpreter { | |
return .{ | |
.blocks = blocks, | |
.current_block = &blocks[0], | |
}; | |
} | |
inline fn loadConst(self: *Interpreter) i64 { | |
const index = self.current_block.instructions[self.pc]; | |
const constant = self.current_block.constants[index]; | |
self.pc += 1; | |
return constant; | |
} | |
inline fn top(self: *Interpreter) i64 { | |
return self.stack[self.stack_pos - 1]; | |
} | |
inline fn pop(self: *Interpreter) i64 { | |
self.stack_pos -= 1; | |
return self.stack[self.stack_pos]; | |
} | |
inline fn push(self: *Interpreter, value: i64) void { | |
self.stack[self.stack_pos] = value; | |
self.stack_pos += 1; | |
} | |
inline fn nextOp(self: *Interpreter) u8 { | |
const op = self.current_block.instructions[self.pc]; | |
self.pc += 1; | |
return op; | |
} | |
pub fn run(self: *Interpreter) !void { | |
while (self.pc < self.current_block.instructions.len) { | |
const instr: Opcode = @enumFromInt(self.nextOp()); | |
switch (instr) { | |
.push => self.push(self.loadConst()), | |
.add => { | |
const a = self.pop(); | |
const b = self.pop(); | |
self.push(a + b); | |
}, | |
.print => { | |
std.debug.print("{d}\n", .{self.pop()}); | |
}, | |
.exit => { | |
const exit_code: u8 = @intCast(self.pop()); | |
std.process.exit(exit_code); | |
}, | |
.eq => { | |
const a = self.pop(); | |
const b = self.pop(); | |
self.push(if (a == b) 1 else 0); | |
}, | |
.jump => self.jump(), | |
.jumpif_1 => { | |
if (self.pop() == 1) { | |
self.jump(); | |
} else { | |
self.pc += 1; | |
} | |
}, | |
.load_var => { | |
const index = self.nextOp(); | |
self.push(self.stack[index]); | |
}, | |
.store_var => { | |
// [value, index] | |
const index = self.nextOp(); | |
self.stack[index] = self.pop(); | |
}, | |
} | |
} | |
} | |
fn jump(self: *Interpreter) void { | |
const block_idx = self.nextOp(); | |
self.current_block = &self.blocks[block_idx]; | |
self.pc = 0; | |
} | |
}; | |
pub inline fn Op(o: Opcode) u8 { | |
return @intFromEnum(o); | |
} | |
pub fn main() !void { | |
// 0 is x, 1 is i | |
const b0 = CodeBlock{ | |
.instructions = &[_]u8{ | |
Op(.push), 0, // x = 0 | |
Op(.push), 0, // i = 0 | |
Op(.jump), 1, // jump to while loop (block 1 | |
}, | |
.constants = &[_]i64{0}, | |
}; | |
const b1 = CodeBlock{ | |
.instructions = &[_]u8{ | |
// while i < 1_000_000 | |
Op(.load_var), 1, // load i | |
Op(.push), 0, // load 1_000_000 | |
Op(.eq), | |
Op(.jumpif_1), 2, // jump to exit if i == 1_000_000 | |
// x = x + 1 | |
Op(.load_var), 0, // load x | |
Op(.load_var), 1, // load i | |
Op(.add), | |
Op(.store_var), 0, // x = x + i | |
// i += 1 | |
Op(.load_var), 1, // load i | |
Op(.push), 1, // load 1 | |
Op(.add), | |
Op(.store_var), 1, // i = i + 1 | |
Op(.jump), 1, // jump to start | |
}, | |
.constants = &[_]i64{ 1_00_0001, 1 }, | |
}; | |
const b2 = CodeBlock{ | |
.instructions = &[_]u8{ | |
Op(.load_var), 0, // load x | |
Op(.print), | |
Op(.push), 0, // load 0 | |
Op(.exit), | |
}, | |
.constants = &[_]i64{0}, | |
}; | |
const program = [_]CodeBlock{ b0, b1, b2 }; | |
var interpreter = Interpreter.init(&program); | |
try interpreter.run(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment