init interpreter

This commit is contained in:
caandt 2025-06-08 12:28:01 -05:00
parent 3ad4054176
commit 91c1cfac42
5 changed files with 231 additions and 6 deletions

View file

@ -51,14 +51,16 @@ fn is_op(c: u8) bool {
};
}
pub fn op_kind(op: []const u8) TokenKind {
const v: u16 = switch (op.len) {
pub fn op_val(op: []const u8) u16 {
return switch (op.len) {
1 => op_map[op[0]],
2 => op_map[op[0]] + (@as(u16, op_map[op[1]]) << 5),
3 => op_map[op[0]] + (@as(u16, op_map[op[1]]) << 5) + (@as(u16, op_map[op[2]]) << 10),
else => @intFromEnum(TokenKind.invalid),
};
return @enumFromInt(v);
}
pub fn op_kind(op: []const u8) TokenKind {
return @enumFromInt(op_val(op));
}
const Operator = @import("parse.zig").Operator;

74
src/interp/compile.zig Normal file
View file

@ -0,0 +1,74 @@
const std = @import("std");
const parse = @import("../frontend/parse.zig");
const Expr = parse.Expr;
pub const sz_tag = enum {
int,
num,
bool,
str,
list,
err,
};
pub const sz_obj = struct {
obj: usize = 0,
obj2: usize = 0,
tag: sz_tag = .int,
};
pub const sz_inst = union(enum) {
push_const: u16,
push_var: u16,
pop,
pop_var: u16,
binop: u16,
unop: u16,
jump: u16,
ret,
};
pub const sz_code = struct {
consts: []sz_obj,
vars: []sz_obj,
bc: []sz_inst,
};
const sz_code_tmp = struct {
consts: std.ArrayList(sz_obj),
vars: std.ArrayList(sz_obj),
bc: std.ArrayList(sz_inst),
fn finalize(self: *sz_code_tmp) !sz_code {
return sz_code{
.consts = try self.consts.toOwnedSlice(),
.vars = try self.vars.toOwnedSlice(),
.bc = try self.bc.toOwnedSlice(),
};
}
fn append(self: *sz_code_tmp, src: []const u8, expr: *const Expr) !void {
switch (expr.*) {
.atom => |a| {
switch (a.kind) {
.number => try self.consts.append(.{ .obj = @bitCast(std.fmt.parseInt(isize, src[a.start..a.end], 0) catch 0) }),
else => unreachable,
}
try self.bc.append(.{ .push_const = @truncate(self.consts.items.len - 1) });
},
.binop => |b| {
try self.append(src, b.lhs);
try self.append(src, b.rhs);
try self.bc.append(.{ .binop = @intFromEnum(b.op.kind) });
},
.unop => |u| {
try self.append(src, u.expr);
try self.bc.append(.{ .unop = @intFromEnum(u.op.kind) });
},
}
}
};
pub fn compile(allocator: std.mem.Allocator, src: []const u8) !sz_code {
var p = try parse.Parser.init(allocator);
const expr = try p.parse(src);
var code = sz_code_tmp{ .consts = std.ArrayList(sz_obj).init(allocator), .vars = std.ArrayList(sz_obj).init(allocator), .bc = std.ArrayList(sz_inst).init(allocator) };
try code.append(src, expr);
try code.bc.append(.{ .pop = {} });
return code.finalize();
}

140
src/interp/vm.zig Normal file
View file

@ -0,0 +1,140 @@
const std = @import("std");
const op_val = @import("../frontend/token.zig").op_val;
const compile = @import("compile.zig");
const sz_tag = compile.sz_tag;
const sz_inst = compile.sz_inst;
const sz_code = compile.sz_code;
const sz_obj = compile.sz_obj;
pub const sz_vm = struct {
code: sz_code,
stack: std.ArrayList(sz_obj),
allocator: std.mem.Allocator,
pc: usize,
pub fn init(allocator: std.mem.Allocator, code: sz_code) sz_vm {
return sz_vm{ .code = code, .stack = std.ArrayList(sz_obj).init(allocator), .allocator = allocator, .pc = 0 };
}
pub fn step(self: *sz_vm) !void {
const inst = self.code.bc[self.pc];
switch (inst) {
.push_const => |i| try self.stack.append(self.code.consts[i]),
.push_var => |i| try self.stack.append(self.code.vars[i]),
.pop => {
const a = self.stack.pop().?;
switch (a.tag) {
.int => std.debug.print("{}\n", .{a.obj}),
.num => std.debug.print("{}\n", .{@as(f64, @bitCast(a.obj))}),
.str => std.debug.print("{s}\n", .{@as([*]u8, @ptrFromInt(a.obj))[0..a.obj2]}),
else => {},
}
},
.pop_var => |i| self.code.vars[i] = self.stack.pop().?,
.binop => |i| {
const rhs = self.stack.pop().?;
const lhs = self.stack.pop().?;
try self.stack.append(try self.binop(lhs, rhs, i));
},
.unop => |i| try self.stack.append(self.unop(self.stack.pop().?, i)),
else => unreachable,
}
self.pc += 1;
}
fn binop(self: *sz_vm, lhs: sz_obj, rhs: sz_obj, op: u16) !sz_obj {
if (lhs.tag == rhs.tag) {
switch (lhs.tag) {
.int => {
const lo: isize = @bitCast(lhs.obj);
const ro: isize = @bitCast(rhs.obj);
const obj = switch (op) {
op_val("+") => lo + ro,
op_val("-") => lo - ro,
op_val("*") => lo * ro,
op_val("/") => @divFloor(lo, ro),
else => return .{ .tag = .err },
};
return .{ .obj = @bitCast(obj) };
},
.num => {
const lo: f64 = @bitCast(lhs.obj);
const ro: f64 = @bitCast(rhs.obj);
const obj: f64 = switch (op) {
op_val("+") => lo + ro,
op_val("-") => lo - ro,
op_val("*") => lo * ro,
op_val("/") => lo / ro,
else => return .{ .tag = .err },
};
return .{ .tag = .num, .obj = @bitCast(obj) };
},
.bool => {
const lo = lhs.obj != 0;
const ro = rhs.obj != 0;
const obj: u64 = switch (op) {
op_val("&&") => if (lo and ro) 1 else 0,
op_val("||") => if (lo or ro) 1 else 0,
else => return .{ .tag = .err },
};
return .{ .tag = .num, .obj = obj };
},
.str => {
const lo = @as([*]u8, @ptrFromInt(lhs.obj))[0..lhs.obj2];
const ro = @as([*]u8, @ptrFromInt(rhs.obj))[0..rhs.obj2];
const obj = switch (op) {
op_val("+") => try std.mem.concat(self.allocator, u8, &[_][]u8{ lo, ro }),
else => return .{ .tag = .err },
};
return .{ .tag = .str, .obj = @intFromPtr(obj.ptr), .obj2 = lhs.obj2 + rhs.obj2 };
},
.list => {
const lo = @as([*]sz_obj, @ptrFromInt(lhs.obj))[0..lhs.obj2];
const ro = @as([*]sz_obj, @ptrFromInt(rhs.obj))[0..rhs.obj2];
const obj = switch (op) {
op_val("+") => try std.mem.concat(self.allocator, sz_obj, &[_][]sz_obj{ lo, ro }),
else => return .{ .tag = .err },
};
return .{ .tag = .list, .obj = @intFromPtr(obj.ptr), .obj2 = lhs.obj2 + rhs.obj2 };
},
.err => return lhs,
}
}
return switch (op) {
op_val("*") => switch (lhs.tag) {
.int => switch (rhs.tag) {
.str => .{ .tag = .str, .obj = @intFromPtr((try mul_slice(self.allocator, u8, @as([*]u8, @ptrFromInt(rhs.obj))[0..rhs.obj2], lhs.obj)).ptr), .obj2 = rhs.obj2 * lhs.obj },
.list => .{ .tag = .list, .obj = @intFromPtr((try mul_slice(self.allocator, sz_obj, @as([*]sz_obj, @ptrFromInt(rhs.obj))[0..rhs.obj2], lhs.obj)).ptr), .obj2 = rhs.obj2 * lhs.obj },
else => .{ .tag = .err },
},
.str => switch (rhs.tag) {
.int => .{ .tag = .str, .obj = @intFromPtr((try mul_slice(self.allocator, u8, @as([*]u8, @ptrFromInt(lhs.obj))[0..lhs.obj2], rhs.obj)).ptr), .obj2 = lhs.obj2 * rhs.obj },
else => .{ .tag = .err },
},
.list => switch (rhs.tag) {
.int => .{ .tag = .list, .obj = @intFromPtr((try mul_slice(self.allocator, sz_obj, @as([*]sz_obj, @ptrFromInt(lhs.obj))[0..lhs.obj2], rhs.obj)).ptr), .obj2 = lhs.obj2 * rhs.obj },
else => .{ .tag = .err },
},
else => .{ .tag = .err },
},
else => .{ .tag = .err },
};
}
fn unop(self: sz_vm, obj: sz_obj, op: u16) sz_obj {
_ = self;
return switch (obj.tag) {
.int => switch (op) {
op_val("+") => obj,
op_val("-") => .{ .obj = ~obj.obj + 1 },
else => .{ .tag = .err },
},
else => .{ .tag = .err },
};
}
};
fn mul_slice(allocator: std.mem.Allocator, comptime T: type, slice: []T, n: usize) ![]T {
var m = try allocator.alloc(T, slice.len * n);
for (0..n) |i| {
@memcpy(m[i * slice.len .. (i + 1) * slice.len], slice);
}
return m;
}

View file

@ -18,9 +18,16 @@ pub fn main() !void {
const content = try file.readToEndAlloc(allocator, std.math.maxInt(usize));
defer allocator.free(content);
var p = try lib.parse.Parser.init(allocator);
defer p.free();
std.debug.print("{}\n", .{(try p.parse(content)).eval(content)});
const code = try lib.compile.compile(allocator, content);
std.debug.print("{any}\n", .{code.bc});
var vm = lib.vm.sz_vm.init(allocator, code);
while (true) {
try vm.step();
for (vm.stack.items) |i| {
std.debug.print("{} ", .{i.obj});
}
std.debug.print("\n", .{});
}
}
test {

View file

@ -1,6 +1,8 @@
const std = @import("std");
pub const token = @import("frontend/token.zig");
pub const parse = @import("frontend/parse.zig");
pub const vm = @import("interp/vm.zig");
pub const compile = @import("interp/compile.zig");
test {
std.testing.refAllDecls(@This());