diff --git a/src/frontend/token.zig b/src/frontend/token.zig index 6cd207c..4ccc156 100644 --- a/src/frontend/token.zig +++ b/src/frontend/token.zig @@ -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; diff --git a/src/interp/compile.zig b/src/interp/compile.zig new file mode 100644 index 0000000..7f918f4 --- /dev/null +++ b/src/interp/compile.zig @@ -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(); +} diff --git a/src/interp/vm.zig b/src/interp/vm.zig new file mode 100644 index 0000000..3babe81 --- /dev/null +++ b/src/interp/vm.zig @@ -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; +} diff --git a/src/main.zig b/src/main.zig index 39939e7..ade0589 100644 --- a/src/main.zig +++ b/src/main.zig @@ -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 { diff --git a/src/root.zig b/src/root.zig index 13fa572..e6244ea 100644 --- a/src/root.zig +++ b/src/root.zig @@ -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());