init interpreter
This commit is contained in:
parent
3ad4054176
commit
91c1cfac42
|
|
@ -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
74
src/interp/compile.zig
Normal 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
140
src/interp/vm.zig
Normal 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;
|
||||
}
|
||||
13
src/main.zig
13
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 {
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
Loading…
Reference in a new issue