tiny.pluck.runtime
Defined in tiny.pluck.
API (97)
Actions
Public operations.
Closure.deinitClosure.eqlClosure.formatClosure.hashClosure.initClosure.initWithThunkClosure.isSelfLoopClosure.makeSelfLoopConstructedValue.eqlConstructedValue.hashEnv.deinitEnv.eqlEnv.extendEnv.firstEnv.firstNameEnv.formatEnv.getEnv.hashEnv.isEmptyEnv.lenEnv.parseEnvEnv.tailEnvCons.getIntDist.eqlIntDist.formatIntDist.initLazyEnumeratorThunk.deinitLazyEnumeratorThunk.deinitWithEnvLazyEnumeratorThunk.formatLazyEnumeratorThunk.initLazyKCThunk.deinitLazyKCThunk.deinitWithEnvLazyKCThunk.formatLazyKCThunk.initLazyKCThunkUnion.deinitLazyKCThunkUnion.formatLazyKCThunkUnion.initNativeValueData.eqlNativeValueData.formatNativeValueData.hashRuntimeValue.deinitRuntimeValue.eqlRuntimeValue.formatRuntimeValue.hashRuntimeValue.initClosureRuntimeValue.initConstructedRuntimeValue.initFalseRuntimeValue.initLazyKCThunkRuntimeValue.initLazyKCThunkUnionRuntimeValue.initNativeRuntimeValue.initTrueRuntimeValue.initUnitRuntimeValue.isFalseRuntimeValue.isThunkRuntimeValue.isTrueRuntimeValue.maybeIntDistRuntimeValue.maybeListRuntimeValue.maybeNatRuntimeValue.maybeNatBoundedRuntimeValue.maybePairStateVars.consumeFuelStateVars.exhaustedStateVars.initStateVars.initWithFuelStateVars.resetfindFirstThunkfindFirstThunkIntofromValuegetValueAtPathpluckListpluckNatreplaceAtPath
Types and contracts
Public types and contracts.
CallstackClosureClosure.ClosureExprConstructedValueConvertedValueEnvEnvConsFromValueResultGuardedWorldGuardedWorldsIntDistLazyEnumeratorThunkLazyKCThunkLazyKCThunk.ThunkExprLazyKCThunkUnionLazyKCThunkUnion.ThunkGuardLazyKCThunkUnion.ThunkInputNativeValueDataNestedWorldRuntimeValueRuntimeValue.DataRuntimeValueContextStateVarsThunkPath
Values and defaults
Public values and defaults.
Source
Source: lib/pluck/src/root.zig:21
zig
pub const runtime = @import("runtime.zig");Source: lib/pluck/src/runtime.zig
zig
const std = @import("std");const Allocator = std.mem.Allocator;const pexpr = @import("pexpr.zig");const PExpr = pexpr.PExpr;const Symbol = pexpr.Symbol;const bdd = @import("bdd.zig");const Bdd = bdd.Bdd;pub const Env = union(enum) { cons: *EnvCons, nil: void, const Self = @This(); pub const empty: Self = .nil; pub fn get(self: Self, name: Symbol) ?*RuntimeValue { return switch (self) { .cons => |c| c.get(name), .nil => null, }; } pub fn tail(self: Self) ?Self { return switch (self) { .cons => |c| c.tail, .nil => null, }; } pub fn first(self: Self) ?*RuntimeValue { return switch (self) { .cons => |c| c.val, .nil => null, }; } pub fn firstName(self: Self) ?Symbol { return switch (self) { .cons => |c| c.name, .nil => null, }; } pub fn isEmpty(self: Self) bool { return self == .nil; } pub fn len(self: Self) usize { return switch (self) { .cons => |c| 1 + c.tail.len(), .nil => 0, }; } pub fn extend(self: Self, allocator: Allocator, name: Symbol, val: *RuntimeValue) !Self { const cons = try allocator.create(EnvCons); cons.* = EnvCons{ .name = name, .val = val, .tail = self, }; return Self{ .cons = cons }; } pub fn deinit(self: Self, allocator: Allocator) void { switch (self) { .cons => |c| { c.tail.deinit(allocator); allocator.destroy(c); }, .nil => {}, } } pub fn parseEnv(self: Self, allocator: Allocator) ![]Symbol { var names: std.ArrayList(Symbol) = .empty; errdefer names.deinit(allocator); var current = self; while (current != .nil) { const cons = current.cons; try names.append(allocator, cons.name); current = cons.tail; } return names.toOwnedSlice(allocator); } pub fn format( self: Self, comptime fmt: []const u8, options: std.fmt.Options, writer: anytype, ) !void { _ = fmt; _ = options; try writer.writeAll("["); var current = self; var first_item = true; while (current != .nil) { if (!first_item) try writer.writeAll(", "); const cons = current.cons; try writer.print("{s}={}", .{ cons.name, cons.val }); current = cons.tail; first_item = false; } try writer.writeAll("]"); } pub fn eql(self: Self, other: Self) bool { var a = self; var b = other; while (true) { switch (a) { .nil => return b == .nil, .cons => |ac| { switch (b) { .nil => return false, .cons => |bc| { if (!std.mem.eql(u8, ac.name, bc.name)) return false; if (!ac.val.eql(bc.val)) return false; a = ac.tail; b = bc.tail; }, } }, } } } pub fn hash(self: Self) u64 { var h = std.hash.Wyhash.init(0); var current = self; while (current != .nil) { const cons = current.cons; h.update(cons.name); h.update(std.mem.asBytes(&cons.val.hash())); current = cons.tail; } return h.final(); }};pub const EnvCons = struct { name: Symbol, val: *RuntimeValue, tail: Env, pub fn get(self: *EnvCons, name: Symbol) ?*RuntimeValue { if (std.mem.eql(u8, self.name, name)) { return self.val; } return self.tail.get(name); }};pub const IntDist = struct { bits: []Bdd, pub fn init(bits: []Bdd) IntDist { return IntDist{ .bits = bits }; } pub fn eql(self: IntDist, other: IntDist, manager: *@import("bdd.zig").Manager) Allocator.Error!Bdd { if (self.bits.len != other.bits.len) { return Bdd.FALSE; } var result = Bdd.TRUE; for (self.bits, other.bits) |a, b| { result = try manager.bddAnd(result, try manager.bddIff(a, b)); if (result.isFalse()) { return Bdd.FALSE; } } return result; } pub fn format( self: IntDist, comptime fmt: []const u8, options: std.fmt.Options, writer: anytype, ) !void { _ = fmt; _ = options; try writer.print("IntDist{{{d}}}", .{self.bits.len}); }};pub const NativeValueData = union(enum) { int: i64, float: f64, symbol: Symbol, bool_val: bool, pexpr: *PExpr, int_dist: IntDist, pub fn eql(self: NativeValueData, other: NativeValueData) bool { if (@as(std.meta.Tag(NativeValueData), self) != @as(std.meta.Tag(NativeValueData), other)) { return false; } return switch (self) { .int => |i| i == other.int, .float => |f| f == other.float, .symbol => |s| std.mem.eql(u8, s, other.symbol), .bool_val => |b| b == other.bool_val, .pexpr => |p| p == other.pexpr, .int_dist => |d| { if (d.bits.len != other.int_dist.bits.len) return false; for (d.bits, other.int_dist.bits) |a, b| { if (a.toRaw() != b.toRaw()) return false; } return true; }, }; } pub fn hash(self: NativeValueData) u64 { var h = std.hash.Wyhash.init(0); h.update(std.mem.asBytes(&@as(u8, @backingInt(self)))); switch (self) { .int => |i| h.update(std.mem.asBytes(&i)), .float => |f| h.update(std.mem.asBytes(&f)), .symbol => |s| h.update(s), .bool_val => |b| h.update(std.mem.asBytes(&b)), .pexpr => |p| h.update(std.mem.asBytes(&@intFromPtr(p))), .int_dist => |d| { for (d.bits) |bit| { h.update(std.mem.asBytes(&bit.toRaw())); } }, } return h.final(); } pub fn format( self: NativeValueData, comptime fmt: []const u8, options: std.fmt.Options, writer: anytype, ) !void { _ = fmt; _ = options; switch (self) { .int => |i| try writer.print("{d}", .{i}), .float => |f| try writer.print("{d}", .{f}), .symbol => |s| try writer.print("'{s}", .{s}), .bool_val => |b| try writer.print("{}", .{b}), .pexpr => |p| try writer.print("{}", .{p}), .int_dist => |d| try writer.print("{}", .{d}), } }};pub const ConstructedValue = struct { constructor: Symbol, args: []*RuntimeValue, pub fn eql(self: ConstructedValue, other: ConstructedValue) bool { if (!std.mem.eql(u8, self.constructor, other.constructor)) return false; if (self.args.len != other.args.len) return false; for (self.args, other.args) |a, b| { if (!a.eql(b)) return false; } return true; } pub fn hash(self: ConstructedValue) u64 { var h = std.hash.Wyhash.init(0); h.update(self.constructor); for (self.args) |arg| { h.update(std.mem.asBytes(&arg.hash())); } return h.final(); }};pub const RuntimeValue = struct { data: Data, pub const Data = union(enum) { native: NativeValueData, constructed: ConstructedValue, closure: *Closure, lazy_kc_thunk: *LazyKCThunk, lazy_kc_thunk_union: *LazyKCThunkUnion, lazy_enum_thunk: *LazyEnumeratorThunk, }; const Self = @This(); pub fn initNative(allocator: Allocator, native: NativeValueData) !*Self { const val = try allocator.create(Self); val.* = Self{ .data = .{ .native = native } }; return val; } pub fn initConstructed(allocator: Allocator, constructor: Symbol, args: []*Self) !*Self { const val = try allocator.create(Self); val.* = Self{ .data = .{ .constructed = .{ .constructor = constructor, .args = args } } }; return val; } pub fn initTrue(allocator: Allocator) !*Self { return initConstructed(allocator, "True", &[_]*Self{}); } pub fn initFalse(allocator: Allocator) !*Self { return initConstructed(allocator, "False", &[_]*Self{}); } pub fn initUnit(allocator: Allocator) !*Self { return initConstructed(allocator, "Unit", &[_]*Self{}); } pub fn initClosure(allocator: Allocator, closure: *Closure) !*Self { const val = try allocator.create(Self); val.* = Self{ .data = .{ .closure = closure } }; return val; } pub fn initLazyKCThunk(allocator: Allocator, thunk: *LazyKCThunk) !*Self { const val = try allocator.create(Self); val.* = Self{ .data = .{ .lazy_kc_thunk = thunk } }; return val; } pub fn initLazyKCThunkUnion(allocator: Allocator, thunk_union: *LazyKCThunkUnion) !*Self { const val = try allocator.create(Self); val.* = Self{ .data = .{ .lazy_kc_thunk_union = thunk_union } }; return val; } pub fn deinit(self: *Self, allocator: Allocator) void { switch (self.data) { .constructed => |c| { for (c.args) |arg| { arg.deinit(allocator); } allocator.free(c.args); }, .closure => |cl| cl.deinit(allocator), .lazy_kc_thunk => |t| t.deinit(allocator), .lazy_kc_thunk_union => |t| t.deinit(allocator), .lazy_enum_thunk => |t| t.deinit(allocator), .native => |n| { switch (n) { .int_dist => |d| allocator.free(d.bits), else => {}, } }, } allocator.destroy(self); } pub fn eql(self: *const Self, other: *const Self) bool { if (@as(std.meta.Tag(Data), self.data) != @as(std.meta.Tag(Data), other.data)) { return false; } return switch (self.data) { .native => |n| n.eql(other.data.native), .constructed => |c| c.eql(other.data.constructed), .closure => |cl| cl.eql(other.data.closure), .lazy_kc_thunk => |t| t == other.data.lazy_kc_thunk, .lazy_kc_thunk_union => |t| t == other.data.lazy_kc_thunk_union, .lazy_enum_thunk => |t| t == other.data.lazy_enum_thunk, }; } pub fn hash(self: *const Self) u64 { var h = std.hash.Wyhash.init(0); h.update(std.mem.asBytes(&@as(u8, @backingInt(self.data)))); switch (self.data) { .native => |n| h.update(std.mem.asBytes(&n.hash())), .constructed => |c| h.update(std.mem.asBytes(&c.hash())), .closure => |cl| h.update(std.mem.asBytes(&cl.hash())), .lazy_kc_thunk => |t| h.update(std.mem.asBytes(&@intFromPtr(t))), .lazy_kc_thunk_union => |t| h.update(std.mem.asBytes(&@intFromPtr(t))), .lazy_enum_thunk => |t| h.update(std.mem.asBytes(&@intFromPtr(t))), } return h.final(); } pub fn isThunk(self: *const Self) bool { return switch (self.data) { .lazy_kc_thunk, .lazy_kc_thunk_union, .lazy_enum_thunk => true, else => false, }; } pub fn isTrue(self: *const Self) bool { return switch (self.data) { .constructed => |c| std.mem.eql(u8, c.constructor, "True") and c.args.len == 0, else => false, }; } pub fn isFalse(self: *const Self) bool { return switch (self.data) { .constructed => |c| std.mem.eql(u8, c.constructor, "False") and c.args.len == 0, else => false, }; } pub fn maybeIntDist(self: *const Self) ?IntDist { return switch (self.data) { .native => |n| switch (n) { .int_dist => |d| d, else => null, }, else => null, }; } pub fn maybeNat(self: *const Self) ?i64 { switch (self.data) { .constructed => |c| { if (std.mem.eql(u8, c.constructor, "O") and c.args.len == 0) { return 0; } else if (std.mem.eql(u8, c.constructor, "S") and c.args.len == 1) { if (c.args[0].maybeNat()) |inner| { return inner + 1; } } return null; }, .lazy_kc_thunk => |thunk| { switch (thunk.expr) { .pexpr => |expr| return Self.maybeNatFromPExpr(expr), .thunk => return null, } }, else => return null, } } fn maybeNatFromPExpr(expr: *const @import("pexpr.zig").PExpr) ?i64 { if (expr.head == .construct) { const constructor = expr.head.construct.constructor; if (std.mem.eql(u8, constructor, "O") and expr.args.len == 0) { return 0; } else if (std.mem.eql(u8, constructor, "S") and expr.args.len == 1) { return if (Self.maybeNatFromPExpr(expr.args[0])) |inner| inner + 1 else null; } } return null; } pub fn maybeNatBounded(self: *const Self, max_value: i64) ?i64 { var current: *const Self = self; var count: i64 = 0; while (true) { switch (current.data) { .constructed => |c| { if (std.mem.eql(u8, c.constructor, "O") and c.args.len == 0) { return count; } else if (std.mem.eql(u8, c.constructor, "S") and c.args.len == 1) { count += 1; if (count > max_value) { return null; } current = c.args[0]; } else { return null; } }, else => return null, } } } pub fn maybeList(self: *const Self, allocator: Allocator) !?[]*Self { var items: std.ArrayList(*Self) = .empty; errdefer items.deinit(allocator); var current = self; while (true) { switch (current.data) { .constructed => |c| { if (std.mem.eql(u8, c.constructor, "Nil") and c.args.len == 0) { const slice = try items.toOwnedSlice(allocator); return slice; } else if (std.mem.eql(u8, c.constructor, "Cons") and c.args.len == 2) { try items.append(allocator, c.args[0]); current = c.args[1]; } else { items.deinit(allocator); return null; } }, else => { items.deinit(allocator); return null; }, } } } pub fn maybePair(self: *const Self) ?struct { fst: *Self, snd: *Self } { switch (self.data) { .constructed => |c| { if (std.mem.eql(u8, c.constructor, "Pair") and c.args.len == 2) { return .{ .fst = c.args[0], .snd = c.args[1] }; } return null; }, else => return null, } } pub fn format( self: *const Self, comptime fmt: []const u8, options: std.fmt.Options, writer: anytype, ) !void { switch (self.data) { .native => |n| { switch (n) { .int => |i| try writer.print("{d}", .{i}), .float => |f| try writer.print("{d}", .{f}), .symbol => |s| try writer.writeAll(s), .bool_val => |b| try writer.writeAll(if (b) "True" else "False"), .pexpr => |p| try writer.print("{any}", .{p}), .int_dist => |d| try writer.print("IntDist({d} bits)", .{d.bits.len}), } }, .constructed => |c| { if (self.maybeNat()) |n| { try writer.print("{d}", .{n}); return; } if (std.mem.eql(u8, c.constructor, "True") and c.args.len == 0) { try writer.writeAll("True"); return; } if (std.mem.eql(u8, c.constructor, "False") and c.args.len == 0) { try writer.writeAll("False"); return; } if (std.mem.eql(u8, c.constructor, "Unit") and c.args.len == 0) { try writer.writeAll("()"); return; } try writer.print("({s}", .{c.constructor}); for (c.args) |arg| { try writer.writeAll(" "); try arg.format(fmt, options, writer); } try writer.writeAll(")"); }, .closure => |cl| try writer.print("{any}", .{cl}), .lazy_kc_thunk => |t| try writer.print("LazyKCThunk({any})", .{t.expr}), .lazy_kc_thunk_union => |t| try t.format(fmt, options, writer), .lazy_enum_thunk => |t| try writer.print("LazyEnumThunk(id={d})", .{t.id}), } }};pub const FromValueResult = struct { value: ConvertedValue, concrete: bool,};pub const ConvertedValue = union(enum) { bool_val: bool, int: i64, unit: void, list: []*RuntimeValue, pair: struct { fst: *RuntimeValue, snd: *RuntimeValue }, original: *RuntimeValue,};pub fn fromValue(val: *RuntimeValue) FromValueResult { switch (val.data) { .constructed => |c| { if (std.mem.eql(u8, c.constructor, "True") and c.args.len == 0) { return .{ .value = .{ .bool_val = true }, .concrete = true }; } if (std.mem.eql(u8, c.constructor, "False") and c.args.len == 0) { return .{ .value = .{ .bool_val = false }, .concrete = true }; } if (std.mem.eql(u8, c.constructor, "Unit") and c.args.len == 0) { return .{ .value = .unit, .concrete = true }; } if (val.maybeNat()) |n| { return .{ .value = .{ .int = n }, .concrete = true }; } if (std.mem.eql(u8, c.constructor, "Pair") and c.args.len == 2) { const fst_result = fromValue(c.args[0]); const snd_result = fromValue(c.args[1]); if (fst_result.concrete and snd_result.concrete) { return .{ .value = .{ .pair = .{ .fst = c.args[0], .snd = c.args[1] } }, .concrete = true }; } return .{ .value = .{ .original = val }, .concrete = false }; } if (std.mem.eql(u8, c.constructor, "Nil") and c.args.len == 0) { return .{ .value = .{ .original = val }, .concrete = true }; } if (std.mem.eql(u8, c.constructor, "Cons") and c.args.len == 2) { var concrete = true; var current: *RuntimeValue = val; while (true) { switch (current.data) { .constructed => |inner| { if (std.mem.eql(u8, inner.constructor, "Nil") and inner.args.len == 0) { break; } else if (std.mem.eql(u8, inner.constructor, "Cons") and inner.args.len == 2) { if (inner.args[0].isThunk()) { concrete = false; break; } current = inner.args[1]; } else { break; } }, else => { concrete = current.isThunk() == false; break; }, } } return .{ .value = .{ .original = val }, .concrete = concrete }; } for (c.args) |arg| { if (arg.isThunk()) { return .{ .value = .{ .original = val }, .concrete = false }; } } return .{ .value = .{ .original = val }, .concrete = true }; }, .native => return .{ .value = .{ .original = val }, .concrete = true }, .closure => return .{ .value = .{ .original = val }, .concrete = true }, .lazy_kc_thunk, .lazy_kc_thunk_union, .lazy_enum_thunk => { return .{ .value = .{ .original = val }, .concrete = false }; }, }}pub fn pluckNat(allocator: Allocator, n: i64) !*RuntimeValue { if (n <= 0) { return RuntimeValue.initConstructed(allocator, "O", &[_]*RuntimeValue{}); } const pred = try pluckNat(allocator, n - 1); errdefer pred.deinit(allocator); const args = try allocator.alloc(*RuntimeValue, 1); args[0] = pred; return RuntimeValue.initConstructed(allocator, "S", args);}pub fn pluckList(allocator: Allocator, items: []*RuntimeValue) !*RuntimeValue { var result = try RuntimeValue.initConstructed(allocator, "Nil", &[_]*RuntimeValue{}); var i = items.len; while (i > 0) { i -= 1; const args = try allocator.alloc(*RuntimeValue, 2); args[0] = items[i]; args[1] = result; result = try RuntimeValue.initConstructed(allocator, "Cons", args); } return result;}pub const Closure = struct { expr: ClosureExpr, env: Env, name: Symbol, pub const ClosureExpr = union(enum) { pexpr: *PExpr, thunk: *RuntimeValue, }; const Self = @This(); pub fn init(allocator: Allocator, expr: *PExpr, env: Env, name: Symbol) !*Self { const closure = try allocator.create(Self); closure.* = Self{ .expr = .{ .pexpr = expr }, .env = env, .name = name, }; return closure; } pub fn initWithThunk(allocator: Allocator, thunk: *RuntimeValue, env: Env, name: Symbol) !*Self { const closure = try allocator.create(Self); closure.* = Self{ .expr = .{ .thunk = thunk }, .env = env, .name = name, }; return closure; } pub fn makeSelfLoop( allocator: Allocator, body: *PExpr, env: Env, rec_name: Symbol, nonrec_name: Symbol, ) !*Self { const closure = try allocator.create(Self); errdefer allocator.destroy(closure); const closure_val = try allocator.create(RuntimeValue); errdefer allocator.destroy(closure_val); closure_val.* = RuntimeValue{ .data = .{ .closure = closure } }; const new_env = try env.extend(allocator, rec_name, closure_val); errdefer new_env.deinit(allocator); closure.* = Self{ .expr = .{ .pexpr = body }, .env = new_env, .name = nonrec_name, }; return closure; } pub fn isSelfLoop(self: *const Self) bool { if (self.env.isEmpty()) return false; if (self.env.first()) |first_val| { if (first_val.data == .closure) { return first_val.data.closure == self; } } return false; } pub fn deinit(self: *Self, allocator: Allocator) void { if (!self.isSelfLoop()) { self.env.deinit(allocator); } allocator.destroy(self); } pub fn eql(self: *const Self, other: *const Self) bool { if (!std.mem.eql(u8, self.name, other.name)) return false; switch (self.expr) { .pexpr => |p| { if (other.expr != .pexpr) return false; if (p != other.expr.pexpr) return false; }, .thunk => |t| { if (other.expr != .thunk) return false; if (t != other.expr.thunk) return false; }, } if (self.isSelfLoop() and other.isSelfLoop()) { const self_tail = self.env.tail() orelse return true; const other_tail = other.env.tail() orelse return true; return self_tail.eql(other_tail); } return self.env.eql(other.env); } pub fn hash(self: *const Self) u64 { var h = std.hash.Wyhash.init(0); switch (self.expr) { .pexpr => |p| h.update(std.mem.asBytes(&@intFromPtr(p))), .thunk => |t| h.update(std.mem.asBytes(&@intFromPtr(t))), } h.update(self.name); h.update(std.mem.asBytes(&self.env.len())); return h.final(); } pub fn format( self: *const Self, comptime fmt: []const u8, options: std.fmt.Options, writer: anytype, ) !void { _ = fmt; _ = options; try writer.print("Closure((λ{s} -> ", .{self.name}); switch (self.expr) { .pexpr => |p| try writer.print("{}", .{p}), .thunk => try writer.writeAll("<thunk>"), } try writer.writeAll("), env=["); var env = self.env; var first_item = true; while (env != .nil) { if (!first_item) try writer.writeAll(", "); const cons = env.cons; if (cons.val.data == .closure and cons.val.data.closure == self) { try writer.writeAll("[recursive]"); } else { try writer.print("{}", .{cons.val}); } env = cons.tail; first_item = false; } try writer.writeAll("])"); }};pub const Callstack = []const i32;pub const GuardedWorld = struct { value: *RuntimeValue, guard: Bdd,};pub const GuardedWorlds = struct { worlds: []GuardedWorld, validity_guard: Bdd,};pub const RuntimeValueContext = struct { pub fn hash(_: RuntimeValueContext, key: *RuntimeValue) u64 { return key.hash(); } pub fn eql(_: RuntimeValueContext, a: *RuntimeValue, b: *RuntimeValue) bool { return a.eql(b); }};pub const NestedWorld = struct { result: GuardedWorlds, guard: Bdd,};pub const LazyKCThunk = struct { expr: ThunkExpr, env: Env, cache: std.ArrayList(GuardedWorlds), callstack: []i32, strict_order_index: i32, allocator: Allocator, pub const ThunkExpr = union(enum) { pexpr: *PExpr, thunk: *LazyKCThunk, }; const Self = @This(); pub fn init( allocator: Allocator, expr: *PExpr, env: Env, strict_order_index: i32, callstack: []const i32, ) !*Self { if (expr.head == .var_ref) { if (env.get(expr.head.var_ref.name)) |val| { if (val.data == .lazy_kc_thunk) { return val.data.lazy_kc_thunk; } } } const thunk = try allocator.create(Self); errdefer allocator.destroy(thunk); const callstack_copy = try allocator.dupe(i32, callstack); errdefer allocator.free(callstack_copy); thunk.* = Self{ .expr = .{ .pexpr = expr }, .env = env, .cache = .empty, .callstack = callstack_copy, .strict_order_index = strict_order_index, .allocator = allocator, }; return thunk; } pub fn deinit(self: *Self, allocator: Allocator) void { allocator.free(self.callstack); for (self.cache.items) |gw| { allocator.free(gw.worlds); } self.cache.deinit(allocator); allocator.destroy(self); } pub fn deinitWithEnv(self: *Self, allocator: Allocator) void { self.env.deinit(allocator); self.deinit(allocator); } pub fn format( self: *const Self, comptime fmt: []const u8, options: std.fmt.Options, writer: anytype, ) !void { try writer.writeAll("LazyKCThunk("); switch (self.expr) { .pexpr => |p| try p.format(fmt, options, writer), .thunk => try writer.writeAll("<nested-thunk>"), } try writer.writeAll(")"); }};pub const LazyKCThunkUnion = struct { thunks: []ThunkGuard, allocator: Allocator, pub const ThunkGuard = struct { thunk: *LazyKCThunk, guard: Bdd, }; const Self = @This(); pub const ThunkInput = struct { value: *RuntimeValue, outer_guard: Bdd, }; pub fn init(allocator: Allocator, manager: *bdd.Manager, worlds: []const ThunkInput) !*Self { var uniq_thunks: std.ArrayList(*LazyKCThunk) = .empty; defer uniq_thunks.deinit(allocator); var uniq_guards: std.ArrayList(Bdd) = .empty; defer uniq_guards.deinit(allocator); var thunk_indices = std.AutoHashMap(*LazyKCThunk, usize).init(allocator); defer thunk_indices.deinit(); for (worlds) |input| { switch (input.value.data) { .lazy_kc_thunk_union => |union_thunk| { for (union_thunk.thunks) |inner| { const combined_guard = try manager.bddAnd(inner.guard, input.outer_guard); if (thunk_indices.get(inner.thunk)) |idx| { uniq_guards.items[idx] = try manager.bddOr(uniq_guards.items[idx], combined_guard); } else { try thunk_indices.put(inner.thunk, uniq_thunks.items.len); try uniq_thunks.append(allocator, inner.thunk); try uniq_guards.append(allocator, combined_guard); } } }, .lazy_kc_thunk => |thunk| { if (thunk_indices.get(thunk)) |idx| { uniq_guards.items[idx] = try manager.bddOr(uniq_guards.items[idx], input.outer_guard); } else { try thunk_indices.put(thunk, uniq_thunks.items.len); try uniq_thunks.append(allocator, thunk); try uniq_guards.append(allocator, input.outer_guard); } }, else => { return error.InvalidThunkUnion; }, } } const result = try allocator.create(Self); errdefer allocator.destroy(result); const thunks = try allocator.alloc(ThunkGuard, uniq_thunks.items.len); for (thunks, uniq_thunks.items, uniq_guards.items) |*t, thunk, guard| { t.* = .{ .thunk = thunk, .guard = guard }; } result.* = Self{ .thunks = thunks, .allocator = allocator, }; return result; } pub fn deinit(self: *Self, allocator: Allocator) void { allocator.free(self.thunks); allocator.destroy(self); } pub fn format( self: *const Self, comptime fmt: []const u8, options: std.fmt.Options, writer: anytype, ) !void { try writer.print("LazyKCThunkUnion{{{d}}}(", .{self.thunks.len}); for (self.thunks, 0..) |tg, i| { try tg.thunk.format(fmt, options, writer); if (i < self.thunks.len - 1) { try writer.writeAll(" | "); } } try writer.writeAll(")"); }};pub const LazyEnumeratorThunk = struct { expr: *PExpr, env: Env, callstack: []i32, strict_order_index: i32, id: u32, const Self = @This(); pub fn init( allocator: Allocator, expr: *PExpr, env: Env, callstack: []const i32, strict_order_index: i32, next_id: *u32, ) !*Self { if (expr.head == .var_ref) { if (env.get(expr.head.var_ref.name)) |val| { if (val.data == .lazy_enum_thunk) { return val.data.lazy_enum_thunk; } } } const thunk = try allocator.create(Self); errdefer allocator.destroy(thunk); const callstack_copy = try allocator.dupe(i32, callstack); errdefer allocator.free(callstack_copy); const id = next_id.*; next_id.* += 1; thunk.* = Self{ .expr = expr, .env = env, .callstack = callstack_copy, .strict_order_index = strict_order_index, .id = id, }; return thunk; } pub fn deinit(self: *Self, allocator: Allocator) void { allocator.free(self.callstack); allocator.destroy(self); } pub fn deinitWithEnv(self: *Self, allocator: Allocator) void { self.env.deinit(allocator); self.deinit(allocator); } pub fn format( self: *const Self, comptime fmt: []const u8, options: std.fmt.Options, writer: anytype, ) !void { _ = fmt; _ = options; try writer.print("LazyEnumeratorThunk(id={d}, {})", .{ self.id, self.expr }); }};pub const StateVars = struct { initial_fuel: i64, remaining_fuel: i64, const Self = @This(); pub fn init() Self { return Self{ .initial_fuel = 0, .remaining_fuel = 0 }; } pub fn initWithFuel(fuel: i64) Self { return Self{ .initial_fuel = fuel, .remaining_fuel = fuel }; } pub fn exhausted(self: *const Self) bool { return self.initial_fuel > 0 and self.remaining_fuel <= 0; } pub fn consumeFuel(self: *Self) bool { if (self.initial_fuel == 0) { return true; } if (self.remaining_fuel > 0) { self.remaining_fuel -= 1; return true; } return false; } pub fn reset(self: *Self) void { self.remaining_fuel = self.initial_fuel; }};pub const ThunkPath = []const usize;pub fn findFirstThunk(allocator: Allocator, val: *RuntimeValue) !?[]usize { var path: std.ArrayList(usize) = .empty; errdefer path.deinit(allocator); if (try findFirstThunkInto(allocator, val, &path)) { const slice = try path.toOwnedSlice(allocator); return slice; } path.deinit(allocator); return null;}pub fn findFirstThunkInto(allocator: Allocator, val: *RuntimeValue, path: *std.ArrayList(usize)) !bool { path.clearRetainingCapacity(); return findFirstThunkInner(allocator, val, path);}fn findFirstThunkInner(allocator: Allocator, val: *RuntimeValue, path: *std.ArrayList(usize)) !bool { switch (val.data) { .lazy_kc_thunk, .lazy_kc_thunk_union, .lazy_enum_thunk => { return true; }, .constructed => |c| { for (c.args, 0..) |arg, i| { try path.append(allocator, i); if (try findFirstThunkInner(allocator, arg, path)) { return true; } _ = path.pop(); } }, .native => |n| { if (n == .pexpr) {} }, .closure => {}, } return false;}pub fn getValueAtPath(val: *RuntimeValue, path: []const usize) ?*RuntimeValue { if (path.len == 0) return val; switch (val.data) { .constructed => |c| { if (path[0] >= c.args.len) return null; return getValueAtPath(c.args[path[0]], path[1..]); }, else => return null, }}pub fn replaceAtPath( allocator: Allocator, val: *RuntimeValue, path: []const usize, new_val: *RuntimeValue,) !*RuntimeValue { if (path.len == 0) return new_val; switch (val.data) { .constructed => |c| { const new_args = try allocator.alloc(*RuntimeValue, c.args.len); errdefer allocator.free(new_args); @memcpy(new_args, c.args); if (path[0] < c.args.len) { new_args[path[0]] = try replaceAtPath(allocator, c.args[path[0]], path[1..], new_val); } return RuntimeValue.initConstructed(allocator, c.constructor, new_args); }, else => return val, }}test "empty environment" { const env = Env.empty; try std.testing.expect(env.isEmpty()); try std.testing.expectEqual(@as(usize, 0), env.len()); try std.testing.expectEqual(@as(?*RuntimeValue, null), env.get("x"));}test "environment extension and lookup" { const allocator = std.testing.allocator; const val = try RuntimeValue.initNative(allocator, .{ .int = 42 }); defer val.deinit(allocator); var env = Env.empty; env = try env.extend(allocator, "x", val); defer env.deinit(allocator); try std.testing.expect(!env.isEmpty()); try std.testing.expectEqual(@as(usize, 1), env.len()); try std.testing.expectEqual(val, env.get("x").?); try std.testing.expectEqual(@as(?*RuntimeValue, null), env.get("y"));}test "runtime value nat conversion" { const allocator = std.testing.allocator; const nat3 = try pluckNat(allocator, 3); defer nat3.deinit(allocator); try std.testing.expectEqual(@as(?i64, 3), nat3.maybeNat());}test "runtime value bounded nat conversion" { const allocator = std.testing.allocator; const nat50 = try pluckNat(allocator, 50); defer nat50.deinit(allocator); try std.testing.expectEqual(@as(?i64, 50), nat50.maybeNatBounded(100)); try std.testing.expectEqual(@as(?i64, 50), nat50.maybeNatBounded(50)); try std.testing.expectEqual(@as(?i64, null), nat50.maybeNatBounded(49)); const nat0 = try pluckNat(allocator, 0); defer nat0.deinit(allocator); try std.testing.expectEqual(@as(?i64, 0), nat0.maybeNatBounded(100));}test "runtime value true/false" { const allocator = std.testing.allocator; const true_val = try RuntimeValue.initTrue(allocator); defer true_val.deinit(allocator); const false_val = try RuntimeValue.initFalse(allocator); defer false_val.deinit(allocator); try std.testing.expect(true_val.isTrue()); try std.testing.expect(!true_val.isFalse()); try std.testing.expect(!false_val.isTrue()); try std.testing.expect(false_val.isFalse());}test "closure creation" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); const allocator = arena.allocator(); var types = try pexpr.TypeRegistry.initWithDefaults(allocator); var defs = pexpr.Definitions.init(allocator); const expr = try pexpr.parseExpr(allocator, "(λ x -> x)", &types, &defs); const body = expr.args[0]; const closure = try Closure.init(allocator, body, Env.empty, "x"); try std.testing.expect(!closure.isSelfLoop()); try std.testing.expectEqualStrings("x", closure.name);}test "state vars" { const state = StateVars.init(); try std.testing.expectEqual(@as(i64, 0), state.initial_fuel); try std.testing.expect(!state.exhausted()); var limited_state = StateVars.initWithFuel(3); try std.testing.expect(!limited_state.exhausted()); try std.testing.expect(limited_state.consumeFuel()); try std.testing.expect(limited_state.consumeFuel()); try std.testing.expect(limited_state.consumeFuel()); try std.testing.expect(limited_state.exhausted()); try std.testing.expect(!limited_state.consumeFuel()); limited_state.reset(); try std.testing.expect(!limited_state.exhausted());}Complete caller list for runtime.IntDist.init
7 direct callers.
tiny.pluck.evaluator.combineIntDists[function] atlib/pluck/src/dist.zig:23lib.pluck.src.evaluator.compileMkInt[function] — private source atlib/pluck/src/evaluator.zig:1372in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.createWeightedIntDist[function] — private source atlib/pluck/src/evaluator.zig:1585in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.test_combineIntDists_-_combines_two_IntDists_under_different_guards[function] — test source atlib/pluck/src/evaluator.zig:4114in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.test_enumerateIntDist_-_deterministic_value[function] — test source atlib/pluck/src/evaluator.zig:4150in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.test_enumerateIntDist_-_non-deterministic[function] — test source atlib/pluck/src/evaluator.zig:4178in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.test_joinMonad_merges_identical_IntDist_values_-_pluck-rs-ui3[function] — test source atlib/pluck/src/evaluator.zig:2392in nearest public ownertiny.pluck.evaluator
Complete caller list for runtime.RuntimeValue.initConstructed
23 direct callers.
lib.pluck.src.evaluator.ConfigPrepender.prepend[function] — private source atlib/pluck/src/evaluator.zig:1773in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.GetArgsContinuation.cont[function] — private source atlib/pluck/src/evaluator.zig:1218in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.PBoolContinuation.cont[function] — private source atlib/pluck/src/evaluator.zig:1302in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.compileConstruct[function] — private source atlib/pluck/src/evaluator.zig:419in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.compileGetConfig[function] — private source atlib/pluck/src/evaluator.zig:1788in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.finishFactorFromGuardList[function] — private source atlib/pluck/src/evaluator.zig:818in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.makeOptionalFloat[function] — private source atlib/pluck/src/evaluator.zig:1761in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.makeOptionalInt[function] — private source atlib/pluck/src/evaluator.zig:1750in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.makePair[function] — private source atlib/pluck/src/evaluator.zig:1743in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.test_joinMonad_collapses_constructor_worlds_with_thunk_unions[function] — test source atlib/pluck/src/evaluator.zig:2451in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.test_joinMonad_collapses_list_constructors_with_thunk_unions[function] — test source atlib/pluck/src/evaluator.zig:2525in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.test_joinMonad_collapses_nested_constructors_with_thunk_unions[function] — test source atlib/pluck/src/evaluator.zig:2630in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.unitFactorResult[function] — private source atlib/pluck/src/evaluator.zig:808in nearest public ownertiny.pluck.evaluatorlib.pluck.src.lpsmc.handleConstructedRunWorld[function] — private source atlib/pluck/src/lpsmc.zig:673in nearest public ownertiny.pluck.lpsmctiny.pluck.evaluator.joinMonad[function] atlib/pluck/src/monad.zig:136tiny.pluck.runtime.RuntimeValue.initFalse[function] atlib/pluck/src/runtime.zig:308tiny.pluck.runtime.RuntimeValue.initTrue[function] atlib/pluck/src/runtime.zig:304tiny.pluck.runtime.RuntimeValue.initUnit[function] atlib/pluck/src/runtime.zig:312tiny.pluck.runtime.pluckList[function] atlib/pluck/src/runtime.zig:652tiny.pluck.runtime.pluckNat[function] atlib/pluck/src/runtime.zig:641tiny.pluck.runtime.replaceAtPath[function] atlib/pluck/src/runtime.zig:1149lib.pluck.src.toplevel.query.forceSampledValue[function] — private source atlib/pluck/src/toplevel/query.zig:1084in nearest public ownertiny.pluck.toplevel.querytiny.pluck.toplevel.query.runLpsmcFallbackQuery[method] atlib/pluck/src/toplevel/query.zig:848
Complete caller list for runtime.RuntimeValue.initFalse
8 direct callers.
lib.pluck.src.evaluator.CompileFlipContinuation.cont[function] — private source atlib/pluck/src/evaluator.zig:623in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.IntDistEqSecondContinuation.cont[function] — private source atlib/pluck/src/evaluator.zig:1672in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.NativeEqSecondContinuation.cont[function] — private source atlib/pluck/src/evaluator.zig:1070in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.compileGetConfig[function] — private source atlib/pluck/src/evaluator.zig:1788in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.evaluateLazyKCThunk[function] — private source atlib/pluck/src/evaluator.zig:1912in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.test_if_then_else_monad[function] — test source atlib/pluck/src/evaluator.zig:2313in nearest public ownertiny.pluck.evaluatorlib.pluck.src.runtime.test_runtime_value_true/false[function] — test source atlib/pluck/src/runtime.zig:1221in nearest public ownertiny.pluck.runtimelib.pluck.src.toplevel.query.test_samplesToQueryResult_merges_repeated_and_equal_sample_values[function] — test source atlib/pluck/src/toplevel/query.zig:1827in nearest public ownertiny.pluck.toplevel.query
Complete caller list for runtime.RuntimeValue.initLazyKCThunk
7 direct callers.
lib.pluck.src.evaluator.CompileAppContinuation.cont[function] — private source atlib/pluck/src/evaluator.zig:303in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.compileConstruct[function] — private source atlib/pluck/src/evaluator.zig:419in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.compileDefined[function] — private source atlib/pluck/src/evaluator.zig:381in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.test_joinMonad_collapses_constructor_worlds_with_thunk_unions[function] — test source atlib/pluck/src/evaluator.zig:2451in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.test_joinMonad_collapses_list_constructors_with_thunk_unions[function] — test source atlib/pluck/src/evaluator.zig:2525in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.test_joinMonad_collapses_nested_constructors_with_thunk_unions[function] — test source atlib/pluck/src/evaluator.zig:2630in nearest public ownertiny.pluck.evaluatorlib.pluck.src.toplevel.query.test_query_thunk_cache_clearing_drops_manager-owned_worlds[function] — test source atlib/pluck/src/toplevel/query.zig:1799in nearest public ownertiny.pluck.toplevel.query
Complete caller list for runtime.RuntimeValue.initNative
18 direct callers.
tiny.pluck.evaluator.enumerateIntDist[function] atlib/pluck/src/dist.zig:79lib.pluck.src.evaluator.ConfigPrepender.prepend[function] — private source atlib/pluck/src/evaluator.zig:1773in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.FloatBinopSecondContinuation.cont[function] — private source atlib/pluck/src/evaluator.zig:1152in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.GetConstructorContinuation.cont[function] — private source atlib/pluck/src/evaluator.zig:1260in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.PBoolContinuation.cont[function] — private source atlib/pluck/src/evaluator.zig:1302in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.compileConstNative[function] — private source atlib/pluck/src/evaluator.zig:1054in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.compileMkInt[function] — private source atlib/pluck/src/evaluator.zig:1372in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.createWeightedIntDist[function] — private source atlib/pluck/src/evaluator.zig:1585in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.makeOptionalFloat[function] — private source atlib/pluck/src/evaluator.zig:1761in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.makeOptionalInt[function] — private source atlib/pluck/src/evaluator.zig:1750in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.test_ThunkRegistry_refineVariable_restricts_guards[function] — test source atlib/pluck/src/evaluator.zig:4446in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.test_bindMonad_frees_input_worlds_slice[function] — test source atlib/pluck/src/evaluator.zig:2855in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.test_joinMonad_merges_identical_IntDist_values_-_pluck-rs-ui3[function] — test source atlib/pluck/src/evaluator.zig:2392in nearest public ownertiny.pluck.evaluatorlib.pluck.src.profiling.internal.incremental.test_BENCHMARK:_ThunkRegistry_refineVariable[function] — test source atlib/pluck/src/profiling/internal/incremental.zig:872in nearest public ownerlib.pluck.src.profiling.internal.incrementallib.pluck.src.runtime.test_environment_extension_and_lookup[function] — test source atlib/pluck/src/runtime.zig:1180in nearest public ownertiny.pluck.runtimetiny.pluck.toplevel.query.runLpsmcFallbackQuery[method] atlib/pluck/src/toplevel/query.zig:848lib.pluck.src.wmc.test_parallel_WMC_handles_exact_divisibility_by_thread_count[function] — test source atlib/pluck/src/wmc.zig:224in nearest public ownertiny.pluck.wmclib.pluck.src.wmc.test_parallel_WMC_produces_same_results_as_sequential[function] — test source atlib/pluck/src/wmc.zig:173in nearest public ownertiny.pluck.wmc
Complete caller list for runtime.RuntimeValue.initTrue
11 direct callers.
lib.pluck.src.evaluator.CompileFlipContinuation.cont[function] — private source atlib/pluck/src/evaluator.zig:623in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.IntDistEqSecondContinuation.cont[function] — private source atlib/pluck/src/evaluator.zig:1672in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.NativeEqSecondContinuation.cont[function] — private source atlib/pluck/src/evaluator.zig:1070in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.compileGetConfig[function] — private source atlib/pluck/src/evaluator.zig:1788in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.evaluateLazyKCThunk[function] — private source atlib/pluck/src/evaluator.zig:1912in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.test_if_then_else_monad[function] — test source atlib/pluck/src/evaluator.zig:2313in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.test_joinMonad_merges_structurally_identical_values_-_pluck-rs-ui3[function] — test source atlib/pluck/src/evaluator.zig:2338in nearest public ownertiny.pluck.evaluatorlib.pluck.src.evaluator.test_pure_monad[function] — test source atlib/pluck/src/evaluator.zig:2291in nearest public ownertiny.pluck.evaluatorlib.pluck.src.runtime.test_runtime_value_true/false[function] — test source atlib/pluck/src/runtime.zig:1221in nearest public ownertiny.pluck.runtimelib.pluck.src.toplevel.query.test_query_thunk_cache_clearing_drops_manager-owned_worlds[function] — test source atlib/pluck/src/toplevel/query.zig:1799in nearest public ownertiny.pluck.toplevel.querylib.pluck.src.toplevel.query.test_samplesToQueryResult_merges_repeated_and_equal_sample_values[function] — test source atlib/pluck/src/toplevel/query.zig:1827in nearest public ownertiny.pluck.toplevel.query
Audit
| Definitions | 97 |
|---|---|
| Public names | 104 |
| Members | 61 |
| Version | 26.7.0 |
| Revision | daab053ee433 |