Skip to content

Commit

Permalink
Make binaryOp more similar to clox
Browse files Browse the repository at this point in the history
It grew way to big in the process
  • Loading branch information
zlw committed Nov 16, 2023
1 parent 5b14dff commit 76e5b94
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 118 deletions.
6 changes: 3 additions & 3 deletions src/table.zig
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub const Table = struct {
const entry = findEntry(self.entries, key);
const isNew = entry.key == null;

if (isNew and entry.val.isNil()) {
if (isNew and entry.val.isA(.nil)) {
self.count += 1;
}

Expand Down Expand Up @@ -91,7 +91,7 @@ pub const Table = struct {
const entry = &self.entries[index];
if (entry.key == null) {
// Stop if we find an empty non-tombstone entry.
if (entry.val.isNil()) return null;
if (entry.val.isA(.nil)) return null;
} else if (entry.key.?.chars.len == chars.len
and entry.key.?.hash == hash
and std.mem.eql(u8, entry.key.?.chars, chars)) {
Expand All @@ -112,7 +112,7 @@ pub const Table = struct {
const entry = &entries[index];

if (entry.key == null) {
if (entry.val.isNil()) {
if (entry.val.isA(.nil)) {
// not a tombstone
return tombstone orelse entry;
} else {
Expand Down
13 changes: 12 additions & 1 deletion src/value.zig
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
const std = @import("std");
const Object = @import("./object.zig").Object;

pub const Value = union(enum) {
pub const ValueType = enum {
nil,
boolean,
number,
object,
};

pub const Value = union(ValueType) {
const Self = @This();

nil,
Expand Down Expand Up @@ -29,6 +36,10 @@ pub const Value = union(enum) {
return self == Value.nil;
}

pub inline fn isA(self: Self, valueType: ValueType) bool {
return @as(ValueType, self) == valueType;
}

pub fn equal(self: Self, other: Self) bool {
return switch (self) {
.nil => other == .nil,
Expand Down
163 changes: 49 additions & 114 deletions src/vm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,19 @@ pub const Vm = struct {
}
},
.op_not => self.push(Value.BooleanValue(isFalsey(self.pop()))),
.op_add => self.binaryOp(.add),
.op_add => {
if (Object.isA(self.peek(0), .String) and Object.isA(self.peek(1), .String)) {
self.concatenate();
} else if (self.peek(0).isA(.number) and self.peek(1).isA(.number)) {
const rhs = self.pop().number;
const lhs = self.pop().number;

self.push(Value.NumberValue(lhs + rhs));
} else {
self.runtimeError("Operands must be two numbers or two strings", .{});
return InterpretError.RuntimeError;
}
},
.op_subtract => self.binaryOp(.sub),
.op_multiply => self.binaryOp(.mul),
.op_divide => self.binaryOp(.div),
Expand Down Expand Up @@ -311,7 +323,7 @@ pub const Vm = struct {
return self.stack[self.stack_top - 1 - back];
}

inline fn callValue(self: *Self, callee: Value, argCount: u8) bool {
fn callValue(self: *Self, callee: Value, argCount: u8) bool {
switch (callee) {
.object => |object| {
switch (object.objectType) {
Expand Down Expand Up @@ -378,7 +390,7 @@ pub const Vm = struct {
}
}

inline fn call(self: *Self, closure: *Object.Closure, argCount: u8) bool {
fn call(self: *Self, closure: *Object.Closure, argCount: u8) bool {
const function = closure.function;

if (function.arity != argCount) {
Expand All @@ -401,7 +413,7 @@ pub const Vm = struct {
return true;
}

inline fn callNative(self: *Self, function: *Object.NativeFunction, argCount: u8) bool {
fn callNative(self: *Self, function: *Object.NativeFunction, argCount: u8) bool {
const args = self.stack[self.stack_top - argCount - 1];
const result = function.function(args);
self.stack_top -= argCount + 1;
Expand All @@ -410,7 +422,7 @@ pub const Vm = struct {
return true;
}

inline fn captureUpvalue(self: *Self, local: *Value) *Object.Upvalue {
fn captureUpvalue(self: *Self, local: *Value) *Object.Upvalue {
var prevUpvalue: ?*Object.Upvalue = null;
var maybeUpvalue = self.openUpvalues;

Expand Down Expand Up @@ -526,129 +538,52 @@ pub const Vm = struct {
}

inline fn binaryOp(self: *Self, op: BinaryOp) InterpretError!void {
const boxed_rhs = self.peek(0);
const boxed_lhs = self.peek(1);

switch (boxed_lhs) {
.boolean, .nil => {
_ = self.pop();
_ = self.pop();
if (!self.peek(0).isA(.number) or !self.peek(1).isA(.number)) {
self.runtimeError("Operands must be numbers", .{});
return InterpretError.RuntimeError;
}

self.runtimeError("Operands must be two numbers or two strings", .{});
return InterpretError.RuntimeError;
},
.number => |lhs| {
switch (boxed_rhs) {
.boolean, .nil => {
_ = self.pop();
_ = self.pop();
const rhs = self.pop().number;
const lhs = self.pop().number;

self.runtimeError("Operands must be two numbers or two strings", .{});
return InterpretError.RuntimeError;
},
.object => {
_ = self.pop();
_ = self.pop();
const result = switch(op) {
.add => lhs + rhs,
.sub => lhs - rhs,
.mul => lhs * rhs,
.div => lhs / rhs,
};

self.runtimeError("Operands must be numbers", .{});
return InterpretError.RuntimeError;
},
.number => |rhs| {
const result = switch (op) {
.add => lhs + rhs,
.sub => lhs - rhs,
.mul => lhs * rhs,
.div => lhs / rhs,
};
self.push(Value.NumberValue(result));
}

_ = self.pop();
_ = self.pop();
fn concatenate(self: *Self) void {
const rhs = self.peek(0).object.asString();
const lhs = self.peek(1).object.asString();

self.push(Value.NumberValue(result));
},
}
},
.object => |lhs| {
switch (boxed_rhs) {
.boolean, .nil => {
_ = self.pop();
_ = self.pop();
const heap = std.mem.concat(self.allocator, u8, &[_][]const u8{ lhs.chars, rhs.chars }) catch unreachable;
const obj = Object.String.take(self, heap);

self.runtimeError("Operands must be two numbers or two strings", .{});
return InterpretError.RuntimeError;
},
.number => {
_ = self.pop();
_ = self.pop();
_ = self.pop();
_ = self.pop();

self.runtimeError("Operands must be numbers", .{});
return InterpretError.RuntimeError;
},
.object => |rhs| {
switch (lhs.objectType) {
.Function, .NativeFunction, .Closure, .Upvalue, .Class, .Instance, .BoundMethod => {
_ = self.pop();
_ = self.pop();

self.runtimeError("Operands must be two numbers or two strings", .{});
return InterpretError.RuntimeError;
},
.String => switch (rhs.objectType) {
.Function, .NativeFunction, .Closure, .Upvalue, .Class, .Instance, .BoundMethod => {
_ = self.pop();
_ = self.pop();

self.runtimeError("Operands must be two numbers or two strings", .{});
return InterpretError.RuntimeError;
},
.String => {
switch (op) {
.add => {
const heap = std.mem.concat(self.allocator, u8, &[_][]const u8{ lhs.asString().chars, rhs.asString().chars }) catch unreachable;
const obj = Object.String.take(self, heap);

_ = self.pop();
_ = self.pop();

self.push(Value.ObjectValue(&obj.object));
},
else => unreachable,
}
},
},
}
},
}
},
}
self.push(Value.ObjectValue(&obj.object));
}

inline fn compOp(self: *Self, op: CompOp) InterpretError!void {
const boxed_rhs = self.pop();
const boxed_lhs = self.pop();

switch (boxed_lhs) {
.boolean, .nil, .object => {
self.runtimeError("Operands must be numbers", .{});
return InterpretError.RuntimeError;
},
.number => |lhs| {
switch (boxed_rhs) {
.boolean, .nil, .object => {
self.runtimeError("Operands must be numbers", .{});
return InterpretError.RuntimeError;
},
.number => |rhs| {
const result = switch (op) {
.gt => lhs > rhs,
.lt => lhs < rhs,
};

self.push(Value.BooleanValue(result));
},
}
},
if (boxed_lhs.asNumber() == null or boxed_rhs.asNumber() == null) {
self.runtimeError("Operands must be numbers", .{});
return InterpretError.RuntimeError;
}

const result = switch (op) {
.gt => boxed_lhs.number > boxed_rhs.number,
.lt => boxed_lhs.number < boxed_rhs.number,
};

self.push(Value.BooleanValue(result));
}
};

Expand Down

0 comments on commit 76e5b94

Please sign in to comment.