Skip to content

Commit

Permalink
Add clUnwrapChoice.
Browse files Browse the repository at this point in the history
  • Loading branch information
fubark committed Sep 2, 2024
1 parent 33fdcf1 commit d5f4e68
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 13 deletions.
12 changes: 12 additions & 0 deletions src/capi.zig
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,14 @@ pub const ZVM = struct {
c.clReset(@ptrCast(self));
}

pub fn evalMust(self: *ZVM, src: []const u8, out: *Value) void {
const res = self.eval(src, out);
if (res != Success) {
const summary = self.newLastErrorSummary();
std.debug.panic("{s}", .{summary});
}
}

pub fn eval(self: *ZVM, src: []const u8, out: *Value) ResultCode {
return c.clEval(@ptrCast(self), toStr(src), out);
}
Expand Down Expand Up @@ -360,6 +368,10 @@ pub const ZVM = struct {
return c.clGetField(@ptrCast(self), rec, toStr(name));
}

pub fn unwrapChoice(self: *ZVM, choice: Value, name: []const u8) Value {
return c.clUnwrapChoice(@ptrCast(self), choice, toStr(name));
}

pub fn expandTemplateType(self: *ZVM, template: Sym, args: []const Value, res: *Type) bool {
return c.clExpandTemplateType(@ptrCast(self), template, args.ptr, args.len, res);
}
Expand Down
6 changes: 3 additions & 3 deletions src/include/cyber.h
Original file line number Diff line number Diff line change
Expand Up @@ -577,17 +577,17 @@ CLValue clNewHostObject(CLVM* vm, CLType typeId, size_t n);
void* clNewHostObjectPtr(CLVM* vm, CLType typeId, size_t n);

// Returns a new instance of `type` with a list of field initializers.
// If the instance could not be created, the VM panics and `CL_INTERRUPT` is returned.
// This assumes that the field initializers are the correct type.
CLValue clNewInstance(CLVM* vm, CLType type, const CLFieldInit* fields, size_t nfields);

// Returns the type of boxed value.
CLType clGetType(CLValue val);

// Returns the field value of the receiver object `rec`.
// If the field can not be retrieved, the VM panics and `CL_INTERRUPT` is returned.
CLValue clGetField(CLVM* vm, CLValue rec, CLStr name);

// Returns the unwrapped value of a choice.
CLValue clUnwrapChoice(CLVM* vm, CLValue choice, CLStr name);

bool clIsFuture(CLVM* vm, CLValue val);
CLStr clNewValueDump(CLVM* vm, CLValue val);

Expand Down
66 changes: 56 additions & 10 deletions src/lib.zig
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,7 @@ export fn clNewHostObjectPtr(vm: *cy.VM, typeId: cy.TypeId, size: usize) *anyopa
export fn clNewInstance(vm: *cy.VM, type_id: cy.TypeId, fields: [*]const c.FieldInit, nfields: usize) cy.Value {
const type_e = vm.c.types[type_id];
if (type_e.kind != .object and type_e.kind != .struct_t) {
return vm.prepPanic("Expected object or struct type.");
cy.panicFmt("Expected object or struct type. Found `{}`.", .{type_e.kind});
}

const start = vm.compiler.main_chunk.valueStack.items.len;
Expand All @@ -701,9 +701,10 @@ export fn clNewInstance(vm: *cy.VM, type_id: cy.TypeId, fields: [*]const c.Field
for (fields[0..nfields]) |field| {
const name = c.fromStr(field.name);
const sym = mod.getSym(name) orelse {
return vm.prepPanic("No such field.");
cy.panicFmt("No such field `{s}`.", .{name});
};
if (sym.type != .field) {
cy.panicFmt("`{s}` is not a field.", .{name});
return vm.prepPanic("Not a field.");
}
args[sym.cast(.field).idx] = @bitCast(field.value);
Expand All @@ -721,7 +722,7 @@ test "clNewInstance()" {
defer vm.destroy();

var res: c.Value = undefined;
_ = vm.eval(
vm.evalMust(
\\type Foo:
\\ a int
\\ b String
Expand All @@ -744,7 +745,7 @@ export fn clSymbol(vm: *cy.VM, str: c.Str) Value {

export fn clGetField(vm: *cy.VM, val: cy.Value, name: c.Str) cy.Value {
return vm.getFieldName(val, c.fromStr(name)) catch {
return vm.prepPanic("Can not access field.");
return cy.panicFmt("Can not access field: `{s}`", .{c.fromStr(name)});
};
}

Expand All @@ -753,7 +754,7 @@ test "clGetField()" {
defer vm.destroy();

var res: c.Value = undefined;
_ = vm.eval(
vm.evalMust(
\\type Foo:
\\ a int
\\ b String
Expand All @@ -764,6 +765,55 @@ test "clGetField()" {
try t.eqStr(c.asString(vm.getField(res, "b")), "abc");
}

export fn clUnwrapChoice(vm: *cy.VM, choice: cy.Value, name: c.Str) cy.Value {
const type_e = vm.sema.getType(choice.getTypeId());
if (type_e.kind != .choice) {
return cy.panicFmt("Expected a choice type. Found `{}`", .{type_e.kind});
}

const zname = c.fromStr(name);
const sym = type_e.sym.getMod().?.getSym(zname) orelse {
return cy.panicFmt("Can not find case `{s}`.", .{zname});
};
if (sym.type != .enumMember) {
return cy.panicFmt("`{s}` is not choice case.", .{zname});
}
const case = sym.cast(.enumMember);

const active_tag = choice.asHeapObject().object.getValue(0).asInt();
if (active_tag != case.val) {
return cy.panicFmt("Expected active tag `{}` for `{s}`. Found `{}`.", .{case.val, zname, active_tag});
}

const payload = choice.asHeapObject().object.getValue(1);
if (!vm.sema.isUnboxedType(case.payloadType)) {
vm.retain(payload);
}
return payload;
}

// To enable logging for tests:
// c.setVerbose(true);
// c.setLog(printLogger);
pub fn printLogger(str: c.Str) callconv(.C) void {
std.debug.print("{s}\n", .{ c.fromStr(str) });
}

test "clUnwrapChoice()" {
const vm = c.create();
defer vm.destroy();

var res: c.Value = undefined;
vm.evalMust(
\\type Foo enum:
\\ case a int
\\ case b String
\\Foo.a(123)
, &res);
defer vm.release(res);
try t.eq(vm.unwrapChoice(res, "a"), 123);
}

export fn clNewPointerVoid(vm: *cy.VM, ptr: ?*anyopaque) Value {
const bt_data = vm.getData(*cy.builtins.BuiltinsData, "builtins");
return cy.heap.allocPointer(vm, bt_data.PtrVoid, ptr) catch fatal();
Expand Down Expand Up @@ -940,11 +990,7 @@ test "List ops." {
defer vm.destroy();

var list: c.Value = undefined;
const eval_res = vm.eval("{1, 2, 3}", &list);
if (eval_res != c.Success) {
const summary = vm.newLastErrorSummary();
std.debug.panic("{s}", .{summary});
}
vm.evalMust("{1, 2, 3}", &list);
defer vm.release(list);

// Initial cap.
Expand Down

0 comments on commit d5f4e68

Please sign in to comment.