From d5f4e686a4afc800bacc4e061bcad0e40c6f3d43 Mon Sep 17 00:00:00 2001 From: fubark Date: Mon, 2 Sep 2024 11:23:24 -0400 Subject: [PATCH] Add `clUnwrapChoice`. --- src/capi.zig | 12 +++++++++ src/include/cyber.h | 6 ++--- src/lib.zig | 66 ++++++++++++++++++++++++++++++++++++++------- 3 files changed, 71 insertions(+), 13 deletions(-) diff --git a/src/capi.zig b/src/capi.zig index dd99a1d0d..c23d98501 100644 --- a/src/capi.zig +++ b/src/capi.zig @@ -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); } @@ -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); } diff --git a/src/include/cyber.h b/src/include/cyber.h index c3e125361..8877772ec 100644 --- a/src/include/cyber.h +++ b/src/include/cyber.h @@ -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); diff --git a/src/lib.zig b/src/lib.zig index 18fb0a806..2ee5a0381 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -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; @@ -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); @@ -721,7 +722,7 @@ test "clNewInstance()" { defer vm.destroy(); var res: c.Value = undefined; - _ = vm.eval( + vm.evalMust( \\type Foo: \\ a int \\ b String @@ -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)}); }; } @@ -753,7 +754,7 @@ test "clGetField()" { defer vm.destroy(); var res: c.Value = undefined; - _ = vm.eval( + vm.evalMust( \\type Foo: \\ a int \\ b String @@ -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(); @@ -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.