Skip to content

Commit

Permalink
Implement val to zig converter.
Browse files Browse the repository at this point in the history
  • Loading branch information
wmedrano committed Aug 17, 2024
1 parent a26977b commit 4be304b
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 9 deletions.
81 changes: 80 additions & 1 deletion src/Environment.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ const Val = @import("val.zig").Val;
const ByteCode = @import("ByteCode.zig");
const Ir = @import("ir.zig").Ir;
const MemoryManager = @import("MemoryManager.zig");
const iter = @import("iter.zig");
const Compiler = @import("Compiler.zig");
const builtins = @import("builtins.zig");
const std = @import("std");
Expand Down Expand Up @@ -79,6 +78,65 @@ pub fn allocator(self: *const Environment) std.mem.Allocator {
return self.memory_manager.allocator;
}

/// Convert the val into a Zig value.
pub fn toZig(self: *const Environment, T: type, alloc: std.mem.Allocator, val: Val) !T {
if (T == void) {
if (!val.isNone()) return error.TypeError;
return;
}
if (T == bool) return val.asBool();
if (T == i64) return val.asInt();
if (T == f64) return val.asFloat();
if (T == []u8) {
switch (val) {
.string => |s| return try alloc.dupe(u8, s),
else => return error.TypeError,
}
}
switch (@typeInfo(T)) {
.Pointer => |info| {
switch (info.size) {
.One => @compileError("Val.toZig does not support pointers"),
.Slice => switch (val) {
.list => |lst| {
var ret = try alloc.alloc(info.child, lst.len);
var init_count: usize = 0;
errdefer toZigClean(T, alloc, ret[0..init_count]);
for (0..lst.len) |idx| {
ret[idx] = try self.toZig(info.child, alloc, lst[idx]);
init_count += 1;
}
return ret;
},
else => return error.TypeError,
},
.Many => @compileError("Val.toZig does not support Many pointers"),
.C => @compileError("Val.toZig does not support C pointers."),
}
},
else => {
@compileError("Val.toZig called with unsupported Zig type.");
},
}
}

fn toZigClean(T: type, alloc: std.mem.Allocator, v: T) void {
if (T == void or T == bool or T == i64 or T == f64) return;
if (T == []u8) {
alloc.free(v);
}
switch (@typeInfo(T)) {
.Pointer => |info| switch (info.size) {
.Slice => {
for (v) |item| toZigClean(info.child, alloc, item);
alloc.free(v);
},
else => @compileError("Unreachable"),
},
else => @compileError("Unreachable"),
}
}

/// Run the garbage collector to free up memory.
pub fn runGc(self: *Environment) !void {
var timer = try std.time.Timer.start();
Expand Down Expand Up @@ -285,3 +343,24 @@ fn executeImportModule(self: *Environment, module: *Module, module_path: []const
module_ok = true;
try module.setModuleAlias(self.allocator(), module_alias, new_module);
}

test "can convert to zig val" {
var env = try init(std.testing.allocator);
defer env.deinit();

try std.testing.expectEqual(false, env.toZig(bool, std.testing.allocator, .{ .boolean = false }));
try std.testing.expectEqual(42, env.toZig(i64, std.testing.allocator, .{ .int = 42 }));
try env.toZig(void, std.testing.allocator, .none);

const actual_str = try env.toZig([]u8, std.testing.allocator, .{ .string = "string" });
defer std.testing.allocator.free(actual_str);
try std.testing.expectEqualStrings("string", actual_str);

const actual_int_list = try env.toZig(
[]i64,
std.testing.allocator,
Val{ .list = @constCast(&[_]Val{ Val{ .int = 1 }, Val{ .int = 2 } }) },
);
defer std.testing.allocator.free(actual_int_list);
try std.testing.expectEqualDeep(&[_]i64{ 1, 2 }, actual_int_list);
}
9 changes: 1 addition & 8 deletions src/Vm.zig
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
const Vm = @This();

const Ast = @import("Ast.zig");
const ByteCode = @import("ByteCode.zig");
const Compiler = @import("Compiler.zig");
const Environment = @import("Environment.zig");
const Ir = @import("ir.zig").Ir;
const MemoryManager = @import("MemoryManager.zig");
const Module = @import("Module.zig");
const Val = @import("val.zig").Val;
const builtins = @import("builtins.zig");
const iter = @import("iter.zig");
const std = @import("std");

pub const Error = Environment.Error;
Expand All @@ -18,11 +13,9 @@ env: Environment,

/// Create a new virtual machine.
pub fn init(alloc: std.mem.Allocator) std.mem.Allocator.Error!Vm {
var vm = Vm{
return Vm{
.env = try Environment.init(alloc),
};
try builtins.registerAll(&vm.env);
return vm;
}

/// Deinitialize a virtual machine. Using self after calling deinit is invalid.
Expand Down
24 changes: 24 additions & 0 deletions src/val.zig
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,38 @@ pub const Val = union(enum) {
return @as(Tag, self);
}

/// Returns true if the value is none.
pub fn isNone(self: Val) bool {
return self.tag() == Tag.none;
}

/// Get the value as a bool.
pub fn asBool(self: Val) !bool {
switch (self) {
.boolean => |b| return b,
else => return error.TypeError,
}
}

/// Get the value as an int.
pub fn asInt(self: Val) !i64 {
switch (self) {
.int => |i| return i,
else => return error.TypeError,
}
}

/// Get the value as an f64. Val may be a float or an int.
pub fn asFloat(self: Val) !f64 {
switch (self) {
.float => |f| return f,
.int => |i| return @as(f64, @floatFromInt(i)),
else => return error.TypeError,
}
}
};

test "val size is ok" {
// TODO: Reduce the size of val to 2 words.
try std.testing.expectEqual(3 * @sizeOf(usize), @sizeOf(Val));
}

0 comments on commit 4be304b

Please sign in to comment.