Compare commits
3 commits
b193b80e40
...
47606bd082
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47606bd082 | ||
|
|
91c1cfac42 | ||
|
|
3ad4054176 |
|
|
@ -45,7 +45,8 @@ fn no_infix(_: *Parser, _: u32, _: *const Expr, _: Token) Error!*const Expr {
|
||||||
return Error.InvalidOp;
|
return Error.InvalidOp;
|
||||||
}
|
}
|
||||||
pub const Operator = struct {
|
pub const Operator = struct {
|
||||||
precedence: u32 = 0,
|
prefix_precedence: u32 = 0,
|
||||||
|
infix_precedence: u32 = 0,
|
||||||
parse_prefix: *const fn (*Parser, u32, Token) Error!*const Expr = no_prefix,
|
parse_prefix: *const fn (*Parser, u32, Token) Error!*const Expr = no_prefix,
|
||||||
parse_infix: *const fn (*Parser, u32, *const Expr, Token) Error!*const Expr = no_infix,
|
parse_infix: *const fn (*Parser, u32, *const Expr, Token) Error!*const Expr = no_infix,
|
||||||
};
|
};
|
||||||
|
|
@ -80,13 +81,13 @@ pub const Parser = struct {
|
||||||
fn parse_expr(self: *Parser, precedence: u32) Error!*const Expr {
|
fn parse_expr(self: *Parser, precedence: u32) Error!*const Expr {
|
||||||
const tok = self.tokenizer.next();
|
const tok = self.tokenizer.next();
|
||||||
const prefix = self.ops.get(tok.kind) orelse return Error.InvalidOp;
|
const prefix = self.ops.get(tok.kind) orelse return Error.InvalidOp;
|
||||||
var left = try prefix.parse_prefix(self, prefix.precedence, tok);
|
var left = try prefix.parse_prefix(self, prefix.prefix_precedence, tok);
|
||||||
var infix: Operator = undefined;
|
var infix: Operator = undefined;
|
||||||
while (w: {
|
while (w: {
|
||||||
infix = self.ops.get(self.tokenizer.peek().kind) orelse break :w false;
|
infix = self.ops.get(self.tokenizer.peek().kind) orelse break :w false;
|
||||||
break :w infix.precedence > precedence;
|
break :w infix.infix_precedence > precedence;
|
||||||
}) {
|
}) {
|
||||||
left = try infix.parse_infix(self, infix.precedence, left, self.tokenizer.next());
|
left = try infix.parse_infix(self, infix.infix_precedence, left, self.tokenizer.next());
|
||||||
}
|
}
|
||||||
return left;
|
return left;
|
||||||
}
|
}
|
||||||
|
|
@ -95,15 +96,17 @@ pub const Parser = struct {
|
||||||
ptr.* = try expr;
|
ptr.* = try expr;
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
fn register_unop(self: *Parser, op: []const u8, precedence: u32) !void {
|
fn register_unop(self: *Parser, op: []const u8, precedence: u32) Error!void {
|
||||||
const op_p = try self.ops.getOrPutValue(op_kind(op), .{ .precedence = precedence });
|
const op_p = try self.ops.getOrPutValue(op_kind(op), .{});
|
||||||
op_p.value_ptr.parse_prefix = prefix_unop;
|
op_p.value_ptr.parse_prefix = prefix_unop;
|
||||||
|
op_p.value_ptr.prefix_precedence = precedence;
|
||||||
}
|
}
|
||||||
fn register_binop(self: *Parser, op: []const u8, precedence: u32) !void {
|
fn register_binop(self: *Parser, op: []const u8, precedence: u32) Error!void {
|
||||||
const op_p = try self.ops.getOrPutValue(op_kind(op), .{ .precedence = precedence });
|
const op_p = try self.ops.getOrPutValue(op_kind(op), .{});
|
||||||
op_p.value_ptr.parse_infix = infix_binop;
|
op_p.value_ptr.parse_infix = infix_binop;
|
||||||
|
op_p.value_ptr.infix_precedence = precedence;
|
||||||
}
|
}
|
||||||
pub fn init(allocator: std.mem.Allocator) !Parser {
|
pub fn init(allocator: std.mem.Allocator) Error!Parser {
|
||||||
const ops = std.AutoHashMap(TokenKind, Operator).init(allocator);
|
const ops = std.AutoHashMap(TokenKind, Operator).init(allocator);
|
||||||
var p = Parser{ .tokenizer = undefined, .allocator = allocator, .ops = ops };
|
var p = Parser{ .tokenizer = undefined, .allocator = allocator, .ops = ops };
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,14 +51,16 @@ fn is_op(c: u8) bool {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn op_kind(op: []const u8) TokenKind {
|
pub fn op_val(op: []const u8) u16 {
|
||||||
const v: u16 = switch (op.len) {
|
return switch (op.len) {
|
||||||
1 => op_map[op[0]],
|
1 => op_map[op[0]],
|
||||||
2 => op_map[op[0]] + (@as(u16, op_map[op[1]]) << 5),
|
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),
|
3 => op_map[op[0]] + (@as(u16, op_map[op[1]]) << 5) + (@as(u16, op_map[op[2]]) << 10),
|
||||||
else => @intFromEnum(TokenKind.invalid),
|
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;
|
const Operator = @import("parse.zig").Operator;
|
||||||
|
|
|
||||||
99
src/interp/compile.zig
Normal file
99
src/interp/compile.zig
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
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 fn format(value: sz_obj, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
|
||||||
|
_ = fmt;
|
||||||
|
_ = options;
|
||||||
|
switch (value.tag) {
|
||||||
|
.int => try writer.print("i[{}]", .{@as(isize, @bitCast(value.obj))}),
|
||||||
|
.num => try writer.print("n[{}]", .{@as(f64, @bitCast(value.obj))}),
|
||||||
|
.bool => try writer.print("b[{}]", .{value.obj}),
|
||||||
|
.str => try writer.print("s[{s}]", .{@as([*]u8, @ptrFromInt(value.obj))[0..value.obj2]}),
|
||||||
|
.list => try writer.print("l[{s}]", .{@as([*]sz_obj, @ptrFromInt(value.obj))[0..value.obj2]}),
|
||||||
|
.err => try writer.print("err[]", .{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const sz_inst = union(enum) {
|
||||||
|
push_const: u16,
|
||||||
|
push_var: u16,
|
||||||
|
pop,
|
||||||
|
pop_var: u16,
|
||||||
|
binop: u16,
|
||||||
|
unop: u16,
|
||||||
|
jump: u16,
|
||||||
|
ret,
|
||||||
|
pub fn format(value: sz_inst, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
|
||||||
|
_ = fmt;
|
||||||
|
_ = options;
|
||||||
|
switch (value) {
|
||||||
|
inline else => |n| {
|
||||||
|
if (@TypeOf(n) == void) {
|
||||||
|
try writer.print("[{s}]", .{@tagName(value)});
|
||||||
|
} else {
|
||||||
|
try writer.print("[{s} {}]", .{@tagName(value), n});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
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;
|
||||||
|
}
|
||||||
10
src/main.zig
10
src/main.zig
|
|
@ -18,9 +18,13 @@ pub fn main() !void {
|
||||||
const content = try file.readToEndAlloc(allocator, std.math.maxInt(usize));
|
const content = try file.readToEndAlloc(allocator, std.math.maxInt(usize));
|
||||||
defer allocator.free(content);
|
defer allocator.free(content);
|
||||||
|
|
||||||
var p = try lib.parse.Parser.init(allocator);
|
const code = try lib.compile.compile(allocator, content);
|
||||||
defer p.free();
|
std.debug.print("{any}\n", .{code.bc});
|
||||||
std.debug.print("{}\n", .{(try p.parse(content)).eval(content)});
|
var vm = lib.vm.sz_vm.init(allocator, code);
|
||||||
|
while (true) {
|
||||||
|
try vm.step();
|
||||||
|
std.debug.print("{any}\n", .{vm.stack.items});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
pub const token = @import("frontend/token.zig");
|
pub const token = @import("frontend/token.zig");
|
||||||
pub const parse = @import("frontend/parse.zig");
|
pub const parse = @import("frontend/parse.zig");
|
||||||
|
pub const vm = @import("interp/vm.zig");
|
||||||
|
pub const compile = @import("interp/compile.zig");
|
||||||
|
|
||||||
test {
|
test {
|
||||||
std.testing.refAllDecls(@This());
|
std.testing.refAllDecls(@This());
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue