lib/python/src/runtime/vm.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

   1 //! The virtual machine runs a compiled Python program one instruction at a time, with one stack of
   2 //! values shared by every call, a list of call frames, a table of global variables and a heap.
   3 //!
   4 //! A caller hands over source text and needs back the program's value, with memory it can free in
   5 //! one step, or an error that says why the program failed. The package accepts part of Python 3.14,
   6 //! and a run has to follow Python's rules for every name, call, loop and builtin type a program can
   7 //! use.
   8 //!
   9 //! A run creates objects whose lifetimes the program decides, and the program's value may point at
  10 //! any of them, so the objects have to outlive the machine that made them. The package accepts no
  11 //! `try` statement, so a program cannot handle an error, and every error ends the run.
  12 //!
  13 //! The package keeps the stack-machine design of [CPython](https://github.com/python/cpython), with
  14 //! Python's names for the builtins it provides (`len`, `range`, `iter`, `next` and five more) and
  15 //! for the errors it reports (`TypeError`, `KeyError`, `IndexError`, `ValueError`,
  16 //! `AttributeError`, `StopIteration`).
  17 //!
  18 //! Each Python exception that a run can raise becomes a member of `Error`. A member of `Error` ends
  19 //! the run and carries no message, value or position. A name resolves in three places: the current
  20 //! call's local variables, then the global variables, then the nine builtin functions, so a global
  21 //! named `len` hides the builtin. Assignment at top level writes a global variable, and assignment
  22 //! inside a function writes a local variable of that call, because the package accepts no `global`
  23 //! statement. `execute` compiles and runs a program and moves the heap into its `Result`, so the
  24 //! value it returns stays valid after the machine is gone. The returned value can point into the
  25 //! heap and into the source text, because a string literal and its slices with step 1 borrow the
  26 //! source. A run has no bound on its steps or its call depth. Each call adds a frame, so a program
  27 //! that recurses without end runs until an allocation fails and the run returns
  28 //! `error.OutOfMemory`. A loop without end that allocates nothing never returns.
  29 const std = @import("std");
  30 const source = @import("../source/root.zig");
  31 const syntax = @import("../syntax/root.zig");
  32 const compiler = @import("../compile/root.zig");
  33 const code = @import("../code/root.zig");
  34 const object = @import("../object/root.zig");
  35 
  36 /// The errors the virtual machine returns when a program fails, besides `error.OutOfMemory`. A
  37 /// caller switches on it to report why a program failed while it ran. Most tags carry the name of
  38 /// the Python exception that the same failure raises. An error carries no message, value or
  39 /// position. The package accepts no `try` statement, so every error ends the run.
  40 pub const Error = error{
  41     /// A call with the wrong number of arguments, to a Python function, a builtin or a bound
  42     /// method.
  43     ArityMismatch,
  44     /// A read of a method the list or dictionary lacks, or of any attribute of another type.
  45     AttributeError,
  46     /// A list or tuple index outside the sequence, including `pop` on an empty list.
  47     IndexError,
  48     /// A call to a function value whose position names no function in the top-level chunk. The same
  49     /// error also reports a frame whose position has passed the end of its instructions. Only a
  50     /// chunk built outside `compile` reaches that second case.
  51     InvalidFunction,
  52     /// Integer arithmetic outside the signed 128-bit range, the negation of the smallest such
  53     /// integer, or a length or count that overflows while joining, repeating, slicing or building a
  54     /// dictionary. The error also reports a range element or an `enumerate` counter that passes the
  55     /// largest such integer while items remain.
  56     IntegerOverflow,
  57     /// A missing dictionary key in a lookup, a `del`, or `pop` without a default, or `popitem` on
  58     /// an empty dictionary.
  59     KeyError,
  60     /// An operation that needs more values than the stack holds.
  61     StackUnderflow,
  62     /// `next` on an exhausted iterator with no default.
  63     StopIteration,
  64     /// An operation given a value of a type it does not support: arithmetic, ordering, calls,
  65     /// iteration, subscripts and slices, an unhashable dictionary key, and builtins given an
  66     /// unsupported argument.
  67     TypeError,
  68     /// A read or `del` of a name that is bound nowhere the lookup searches.
  69     UndefinedName,
  70     /// A step of zero in `range` or a slice, a string of invalid UTF-8 given to an operation that
  71     /// counts its codepoints, or an item of the wrong length given to `dict` or `update` as a key
  72     /// and value pair.
  73     ValueError,
  74 };
  75 
  76 /// A finished run's value together with the heap that owns every object the value can point to.
  77 /// `execute` returns one, and the caller reads `value` and then frees everything with `deinit`.
  78 /// `value` stays valid until `deinit`. Strings in `value` can borrow the source text passed to
  79 /// `execute`, so that text has to stay alive while `value` is in use.
  80 pub const Result = struct {
  81     /// The program's value: the value of its last top-level expression statement, or `None` when no
  82     /// expression statement ran.
  83     value: object.Value,
  84     /// The heap that owns every object `value` can point to.
  85     heap: object.Heap,
  86 
  87     /// Frees the heap of the returned value, and every value derived from it becomes invalid. The
  88     /// caller of `execute` defers it right after the call, as the README's example does. The call
  89     /// leaves the result undefined.
  90     pub fn deinit(self: *Result) void {
  91         self.heap.deinit();
  92         self.* = undefined;
  93     }
  94 };
  95 
  96 /// Tokenizes, parses, compiles and runs the source text, then returns the program's value together
  97 /// with the heap that owns the value's objects. The README's example and nearly every test of the
  98 /// package call it with a short program. The call borrows the source text. Before it returns, the
  99 /// call frees the tokens, the syntax tree, the chunk and the machine's own state, so the result's
 100 /// heap is the only memory it hands back. The returned value borrows the result's heap and stays
 101 /// valid only until `Result.deinit`. The returned value can also point into the source text,
 102 /// through string literals and their slices, so the text has to stay alive while the value is used.
 103 /// `Result.deinit` frees the heap with the allocator passed to this call. The call returns the
 104 /// errors of `tokenize`, `parse`, `compile` and `Vm.run`, and `error.OutOfMemory`, as one inferred
 105 /// error set. On any error the call frees everything it allocated, the heap included.
 106 pub fn execute(allocator: std.mem.Allocator, bytes: []const u8) !Result {
 107     var stream = try source.tokenize(allocator, bytes);
 108     defer stream.deinit(allocator);
 109     var program = try syntax.parse(allocator, bytes, stream.tokens);
 110     defer program.deinit();
 111     var chunk_value = try compiler.compile(allocator, &program);
 112     defer chunk_value.deinit(allocator);
 113     var vm = Vm.init(allocator, &chunk_value);
 114     defer vm.deinit();
 115     const value = try vm.run();
 116     return .{
 117         .value = value,
 118         .heap = vm.takeHeap(),
 119     };
 120 }
 121 
 122 /// The state of one run: the chunk it runs, its heap, its call frames, its value stack and its
 123 /// global variables. `execute` drives one, and a caller that already holds a compiled chunk can run
 124 /// it directly. A machine is made with `init`, run with `run`, and freed with `deinit`. The machine
 125 /// borrows the chunk, which has to outlive it. The machine checks stack depth and function
 126 /// positions, and it trusts every other position in the chunk, so a chunk built outside `compile`
 127 /// has to keep them in range.
 128 pub const Vm = struct {
 129     /// The allocator given to `init`. The frames, the stack, the globals and the heap all allocate
 130     /// from it.
 131     allocator: std.mem.Allocator,
 132     /// The top-level chunk, which the machine borrows. Every call finds its function in this
 133     /// chunk's function table.
 134     module: *const code.Chunk,
 135     /// The heap that owns every object the run creates. `takeHeap` moves this heap out and leaves
 136     /// an empty one in its place.
 137     heap: object.Heap,
 138     /// One frame per call in progress, the top-level frame first. Each frame holds the chunk it
 139     /// runs, its position in that chunk, its local variables and the value `save` last recorded.
 140     /// The list grows by one frame per call, with no bound.
 141     frames: std.ArrayListUnmanaged(Frame) = .empty,
 142     /// The value stack shared by every frame: each operation pops its inputs from the end and
 143     /// pushes its result there.
 144     stack: std.ArrayListUnmanaged(object.Value) = .empty,
 145     /// The global variables, keyed by names borrowed from the chunk.
 146     globals: std.StringHashMapUnmanaged(object.Value) = .empty,
 147 
 148     /// Returns a machine for the given chunk, with an empty heap, stack, frame list and table of
 149     /// globals. `execute` calls it with the chunk from `compile`, and a caller that holds its own
 150     /// chunk does the same. The call allocates nothing, so it cannot fail. The machine borrows the
 151     /// chunk for its whole life.
 152     pub fn init(allocator: std.mem.Allocator, chunk_value: *const code.Chunk) Vm {
 153         return .{
 154             .allocator = allocator,
 155             .module = chunk_value,
 156             .heap = object.Heap.init(allocator),
 157         };
 158     }
 159 
 160     /// Frees the frames, the stack, the globals and the heap, and leaves the machine undefined.
 161     /// `execute` defers it right after `init`, so it runs whether `run` succeeds or fails. Values
 162     /// from `run` point into the heap freed here, unless `takeHeap` moved the heap out first.
 163     pub fn deinit(self: *Vm) void {
 164         while (self.frames.items.len > 0) self.popFrame();
 165         self.frames.deinit(self.allocator);
 166         self.stack.deinit(self.allocator);
 167         self.globals.deinit(self.allocator);
 168         self.heap.deinit();
 169         self.* = undefined;
 170     }
 171 
 172     /// Returns the machine's heap and leaves an empty heap in its place. `execute` calls it after
 173     /// `run`, so the objects of the result outlive the machine. The caller owns the returned heap
 174     /// and frees it with `Heap.deinit`.
 175     pub fn takeHeap(self: *Vm) object.Heap {
 176         const heap = self.heap;
 177         self.heap = object.Heap.init(self.allocator);
 178         return heap;
 179     }
 180 
 181     /// Runs the chunk from its first instruction in a new top-level frame and returns the program's
 182     /// value. `execute` calls it once per program. The value is the one that `ret` or a top-level
 183     /// `return_value` returns. Returned values point into the machine's heap. The call returns a
 184     /// member of `Error` when the program fails, and `error.OutOfMemory` when an allocation fails.
 185     /// After an error, the frames, the stack and the heap keep the state at the failure until
 186     /// `deinit` frees them. A run has no bound on its steps or its call depth.
 187     pub fn run(self: *Vm) (Error || std.mem.Allocator.Error)!object.Value {
 188         try self.pushFrame(self.module, .{});
 189         while (self.frames.items.len > 0) {
 190             const frame = self.currentFrame();
 191             if (frame.ip >= frame.chunk.instructions.items.len) return Error.InvalidFunction;
 192             const instruction = frame.chunk.instructions.items[frame.ip];
 193             frame.ip += 1;
 194             switch (instruction.op) {
 195                 .constant => try self.push(frame.chunk.constants.items[instruction.operand]),
 196                 .load => try self.load(instruction.operand),
 197                 .store => try self.store(instruction.operand),
 198                 .delete => try self.delete(instruction.operand),
 199                 .pop => _ = try self.pop(),
 200                 .save => frame.last = try self.pop(),
 201                 .dup => try self.dup(),
 202                 .swap => try self.swap(),
 203                 .rotate_three => try self.rotateThree(),
 204                 .jump => frame.ip = instruction.operand,
 205                 .jump_if_false => {
 206                     if (!(try self.peek()).truthy()) frame.ip = instruction.operand;
 207                 },
 208                 .add => try self.binary(.add),
 209                 .sub => try self.binary(.sub),
 210                 .mul => try self.binary(.mul),
 211                 .neg => try self.negate(),
 212                 .not => try self.logicalNot(),
 213                 .equal => try self.binary(.equal),
 214                 .not_equal => try self.binary(.not_equal),
 215                 .less => try self.binary(.less),
 216                 .less_equal => try self.binary(.less_equal),
 217                 .greater => try self.binary(.greater),
 218                 .greater_equal => try self.binary(.greater_equal),
 219                 .contains => try self.binary(.contains),
 220                 .not_contains => try self.binary(.not_contains),
 221                 .identical => try self.binary(.identical),
 222                 .not_identical => try self.binary(.not_identical),
 223                 .call => try self.call(instruction.operand),
 224                 .attribute => try self.attribute(instruction.operand),
 225                 .build_list => try self.buildList(instruction.operand),
 226                 .build_tuple => try self.buildTuple(instruction.operand),
 227                 .build_dict => try self.buildDict(instruction.operand),
 228                 .iter => try self.iter(),
 229                 .for_next => try self.forNext(instruction.operand),
 230                 .subscript => try self.subscript(),
 231                 .slice => try self.slice(),
 232                 .store_subscript => try self.storeSubscript(),
 233                 .delete_subscript => try self.deleteSubscript(),
 234                 .return_value => if (try self.returnValue(try self.pop())) |value| return value,
 235                 .ret => if (try self.returnValue(frame.last)) |value| return value,
 236             }
 237         }
 238         return .none;
 239     }
 240 
 241     fn currentFrame(self: *Vm) *Frame {
 242         return &self.frames.items[self.frames.items.len - 1];
 243     }
 244 
 245     fn pushFrame(self: *Vm, chunk_value: *const code.Chunk, locals: std.StringHashMapUnmanaged(object.Value)) std.mem.Allocator.Error!void {
 246         try self.frames.append(self.allocator, .{
 247             .chunk = chunk_value,
 248             .locals = locals,
 249         });
 250     }
 251 
 252     fn popFrame(self: *Vm) void {
 253         var frame = self.frames.items[self.frames.items.len - 1];
 254         self.frames.items.len -= 1;
 255         frame.deinit(self.allocator);
 256     }
 257 
 258     fn push(self: *Vm, value: object.Value) std.mem.Allocator.Error!void {
 259         try self.stack.append(self.allocator, value);
 260     }
 261 
 262     fn pop(self: *Vm) Error!object.Value {
 263         if (self.stack.items.len == 0) return Error.StackUnderflow;
 264         const value = self.stack.items[self.stack.items.len - 1];
 265         self.stack.items.len -= 1;
 266         return value;
 267     }
 268 
 269     fn peek(self: *const Vm) Error!object.Value {
 270         if (self.stack.items.len == 0) return Error.StackUnderflow;
 271         return self.stack.items[self.stack.items.len - 1];
 272     }
 273 
 274     fn dup(self: *Vm) (Error || std.mem.Allocator.Error)!void {
 275         try self.push(try self.peek());
 276     }
 277 
 278     fn swap(self: *Vm) Error!void {
 279         if (self.stack.items.len < 2) return Error.StackUnderflow;
 280         const top = self.stack.items.len - 1;
 281         std.mem.swap(object.Value, &self.stack.items[top], &self.stack.items[top - 1]);
 282     }
 283 
 284     fn rotateThree(self: *Vm) Error!void {
 285         if (self.stack.items.len < 3) return Error.StackUnderflow;
 286         const base = self.stack.items.len - 3;
 287         const first = self.stack.items[base];
 288         const second = self.stack.items[base + 1];
 289         const third = self.stack.items[base + 2];
 290         self.stack.items[base] = third;
 291         self.stack.items[base + 1] = first;
 292         self.stack.items[base + 2] = second;
 293     }
 294 
 295     fn load(self: *Vm, name_index: usize) (Error || std.mem.Allocator.Error)!void {
 296         const frame = self.currentFrame();
 297         const name = frame.chunk.names.items[name_index];
 298         if (frame.locals.get(name)) |value| {
 299             try self.push(value);
 300             return;
 301         }
 302         if (self.globals.get(name)) |value| {
 303             try self.push(value);
 304             return;
 305         }
 306         if (builtin(name)) |value| {
 307             try self.push(value);
 308             return;
 309         }
 310         return Error.UndefinedName;
 311     }
 312 
 313     fn store(self: *Vm, name_index: usize) (Error || std.mem.Allocator.Error)!void {
 314         const value = try self.pop();
 315         const frame = self.currentFrame();
 316         const name = frame.chunk.names.items[name_index];
 317         if (self.frames.items.len == 1) {
 318             try self.globals.put(self.allocator, name, value);
 319         } else {
 320             try frame.locals.put(self.allocator, name, value);
 321         }
 322     }
 323 
 324     fn delete(self: *Vm, name_index: usize) Error!void {
 325         const frame = self.currentFrame();
 326         const name = frame.chunk.names.items[name_index];
 327         if (self.frames.items.len == 1) {
 328             if (!self.globals.remove(name)) return Error.UndefinedName;
 329         } else {
 330             if (!frame.locals.remove(name)) return Error.UndefinedName;
 331         }
 332     }
 333 
 334     fn call(self: *Vm, argument_count: usize) (Error || std.mem.Allocator.Error)!void {
 335         if (self.stack.items.len < argument_count + 1) return Error.StackUnderflow;
 336         const callee_index = self.stack.items.len - argument_count - 1;
 337         const callee = self.stack.items[callee_index];
 338         switch (callee) {
 339             .function => |index| try self.callFunction(callee_index, argument_count, index),
 340             .builtin => |value| try self.callBuiltin(callee_index, argument_count, value),
 341             .method => |value| try self.callNativeMethod(callee_index, argument_count, value),
 342             else => return Error.TypeError,
 343         }
 344     }
 345 
 346     fn callFunction(self: *Vm, callee_index: usize, argument_count: usize, function_index: usize) (Error || std.mem.Allocator.Error)!void {
 347         if (function_index >= self.module.functions.items.len) return Error.InvalidFunction;
 348         const function = &self.module.functions.items[function_index];
 349         if (function.params.len != argument_count) return Error.ArityMismatch;
 350         var locals = std.StringHashMapUnmanaged(object.Value).empty;
 351         errdefer locals.deinit(self.allocator);
 352         for (function.params, 0..) |param, index| {
 353             try locals.put(self.allocator, param, self.stack.items[callee_index + 1 + index]);
 354         }
 355         self.stack.items.len = callee_index;
 356         try self.pushFrame(&function.chunk, locals);
 357     }
 358 
 359     fn callBuiltin(self: *Vm, callee_index: usize, argument_count: usize, value: object.Builtin) (Error || std.mem.Allocator.Error)!void {
 360         const arguments = self.stack.items[callee_index + 1 ..][0..argument_count];
 361         const result = switch (value) {
 362             .dict => try self.dictBuiltin(arguments),
 363             .enumerate => try self.enumerateBuiltin(arguments),
 364             .iter => try self.iterBuiltin(arguments),
 365             .len => try self.lenBuiltin(arguments),
 366             .list => try self.listBuiltin(arguments),
 367             .next => try self.nextBuiltin(arguments),
 368             .range => try self.rangeBuiltin(arguments),
 369             .reversed => try self.reversedBuiltin(arguments),
 370             .tuple => try self.tupleBuiltin(arguments),
 371         };
 372         self.stack.items.len = callee_index;
 373         try self.push(result);
 374     }
 375 
 376     fn callNativeMethod(self: *Vm, callee_index: usize, argument_count: usize, method: *object.NativeMethod) (Error || std.mem.Allocator.Error)!void {
 377         const arguments = self.stack.items[callee_index + 1 ..][0..argument_count];
 378         const result = switch (method.*) {
 379             .dict_clear => |dict| try self.dictClearMethod(dict, arguments),
 380             .dict_copy => |dict| try self.dictCopyMethod(dict, arguments),
 381             .dict_get => |dict| try self.dictGetMethod(dict, arguments),
 382             .dict_items => |dict| try self.dictItemsMethod(dict, arguments),
 383             .dict_keys => |dict| try self.dictKeysMethod(dict, arguments),
 384             .dict_pop => |dict| try self.dictPopMethod(dict, arguments),
 385             .dict_popitem => |dict| try self.dictPopitemMethod(dict, arguments),
 386             .dict_setdefault => |dict| try self.dictSetdefaultMethod(dict, arguments),
 387             .dict_update => |dict| try self.dictUpdateMethod(dict, arguments),
 388             .dict_values => |dict| try self.dictValuesMethod(dict, arguments),
 389             .list_append => |list| try self.listAppendMethod(list, arguments),
 390             .list_clear => |list| try self.listClearMethod(list, arguments),
 391             .list_copy => |list| try self.listCopyMethod(list, arguments),
 392             .list_pop => |list| try self.listPopMethod(list, arguments),
 393         };
 394         self.stack.items.len = callee_index;
 395         try self.push(result);
 396     }
 397 
 398     fn attribute(self: *Vm, name_index: usize) (Error || std.mem.Allocator.Error)!void {
 399         const target = try self.pop();
 400         const name = self.currentFrame().chunk.names.items[name_index];
 401         const result = switch (target) {
 402             .dict => |dict| try self.dictAttribute(dict, name),
 403             .list => |list| try self.listAttribute(list, name),
 404             else => return Error.AttributeError,
 405         };
 406         try self.push(result);
 407     }
 408 
 409     fn dictAttribute(self: *Vm, dict: *object.Dict, name: []const u8) (Error || std.mem.Allocator.Error)!object.Value {
 410         if (std.mem.eql(u8, name, "clear")) return try self.heap.createNativeMethod(.{ .dict_clear = dict });
 411         if (std.mem.eql(u8, name, "copy")) return try self.heap.createNativeMethod(.{ .dict_copy = dict });
 412         if (std.mem.eql(u8, name, "get")) return try self.heap.createNativeMethod(.{ .dict_get = dict });
 413         if (std.mem.eql(u8, name, "items")) return try self.heap.createNativeMethod(.{ .dict_items = dict });
 414         if (std.mem.eql(u8, name, "keys")) return try self.heap.createNativeMethod(.{ .dict_keys = dict });
 415         if (std.mem.eql(u8, name, "pop")) return try self.heap.createNativeMethod(.{ .dict_pop = dict });
 416         if (std.mem.eql(u8, name, "popitem")) return try self.heap.createNativeMethod(.{ .dict_popitem = dict });
 417         if (std.mem.eql(u8, name, "setdefault")) return try self.heap.createNativeMethod(.{ .dict_setdefault = dict });
 418         if (std.mem.eql(u8, name, "update")) return try self.heap.createNativeMethod(.{ .dict_update = dict });
 419         if (std.mem.eql(u8, name, "values")) return try self.heap.createNativeMethod(.{ .dict_values = dict });
 420         return Error.AttributeError;
 421     }
 422 
 423     fn listAttribute(self: *Vm, list: *object.List, name: []const u8) (Error || std.mem.Allocator.Error)!object.Value {
 424         if (std.mem.eql(u8, name, "append")) return try self.heap.createNativeMethod(.{ .list_append = list });
 425         if (std.mem.eql(u8, name, "clear")) return try self.heap.createNativeMethod(.{ .list_clear = list });
 426         if (std.mem.eql(u8, name, "copy")) return try self.heap.createNativeMethod(.{ .list_copy = list });
 427         if (std.mem.eql(u8, name, "pop")) return try self.heap.createNativeMethod(.{ .list_pop = list });
 428         return Error.AttributeError;
 429     }
 430 
 431     fn dictClearMethod(self: *Vm, dict: *object.Dict, arguments: []const object.Value) Error!object.Value {
 432         _ = self;
 433         if (arguments.len != 0) return Error.ArityMismatch;
 434         dict.clearRetainingCapacity();
 435         return .none;
 436     }
 437 
 438     fn dictCopyMethod(self: *Vm, dict: *object.Dict, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 439         if (arguments.len != 0) return Error.ArityMismatch;
 440         return try self.heap.createDict(dict.entries.items);
 441     }
 442 
 443     fn dictGetMethod(self: *Vm, dict: *object.Dict, arguments: []const object.Value) Error!object.Value {
 444         _ = self;
 445         if (arguments.len == 0 or arguments.len > 2) return Error.ArityMismatch;
 446         if (try dictEntryIndex(dict, arguments[0])) |index| return dict.entries.items[index].value;
 447         if (arguments.len == 2) return arguments[1];
 448         return .none;
 449     }
 450 
 451     fn dictItemsMethod(self: *Vm, dict: *object.Dict, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 452         if (arguments.len != 0) return Error.ArityMismatch;
 453         return try self.heap.createDictView(dict, .items);
 454     }
 455 
 456     fn dictKeysMethod(self: *Vm, dict: *object.Dict, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 457         if (arguments.len != 0) return Error.ArityMismatch;
 458         return try self.heap.createDictView(dict, .keys);
 459     }
 460 
 461     fn dictPopMethod(self: *Vm, dict: *object.Dict, arguments: []const object.Value) Error!object.Value {
 462         _ = self;
 463         if (arguments.len == 0 or arguments.len > 2) return Error.ArityMismatch;
 464         if (try dictEntryIndex(dict, arguments[0])) |index| return dict.removeAt(index);
 465         if (arguments.len == 2) return arguments[1];
 466         return Error.KeyError;
 467     }
 468 
 469     fn dictPopitemMethod(self: *Vm, dict: *object.Dict, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 470         if (arguments.len != 0) return Error.ArityMismatch;
 471         if (dict.entries.items.len == 0) return Error.KeyError;
 472         const entry = dict.popLast().?;
 473         const pair = [_]object.Value{ entry.key, entry.value };
 474         return try self.heap.createTuple(&pair);
 475     }
 476 
 477     fn dictSetdefaultMethod(self: *Vm, dict: *object.Dict, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 478         if (arguments.len == 0 or arguments.len > 2) return Error.ArityMismatch;
 479         if (try dictEntryIndex(dict, arguments[0])) |index| return dict.entries.items[index].value;
 480         const value = if (arguments.len == 2) arguments[1] else object.Value.none;
 481         try self.dictSet(dict, arguments[0], value);
 482         return value;
 483     }
 484 
 485     fn dictUpdateMethod(self: *Vm, dict: *object.Dict, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 486         if (arguments.len > 1) return Error.ArityMismatch;
 487         if (arguments.len == 1) try self.updateDictFromValue(dict, arguments[0]);
 488         return .none;
 489     }
 490 
 491     fn dictValuesMethod(self: *Vm, dict: *object.Dict, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 492         if (arguments.len != 0) return Error.ArityMismatch;
 493         return try self.heap.createDictView(dict, .values);
 494     }
 495 
 496     fn listAppendMethod(self: *Vm, list: *object.List, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 497         if (arguments.len != 1) return Error.ArityMismatch;
 498         try self.listAppend(list, arguments[0]);
 499         return .none;
 500     }
 501 
 502     fn listClearMethod(self: *Vm, list: *object.List, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 503         if (arguments.len != 0) return Error.ArityMismatch;
 504         self.listClear(list);
 505         return .none;
 506     }
 507 
 508     fn listCopyMethod(self: *Vm, list: *object.List, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 509         if (arguments.len != 0) return Error.ArityMismatch;
 510         return try self.heap.createList(list.items);
 511     }
 512 
 513     fn listPopMethod(self: *Vm, list: *object.List, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 514         if (arguments.len > 1) return Error.ArityMismatch;
 515         const index = if (arguments.len == 1) arguments[0] else object.Value{ .integer = -1 };
 516         return try self.listRemoveAt(list, index);
 517     }
 518 
 519     fn dictBuiltin(self: *Vm, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 520         if (arguments.len > 1) return Error.ArityMismatch;
 521         if (arguments.len == 0) return try self.heap.createDict(&.{});
 522         return switch (arguments[0]) {
 523             .dict => |value| try self.heap.createDict(value.entries.items),
 524             .iterator => |value| try self.dictFromIterator(value),
 525             .string => |value| try self.dictFromIterator((try self.heap.createStringIterator(value)).iterator),
 526             .list => |value| try self.dictFromIterator((try self.heap.createListIterator(value)).iterator),
 527             .tuple => |value| try self.dictFromIterator((try self.heap.createTupleIterator(value)).iterator),
 528             .view => |value| try self.dictFromIterator((try self.heap.createDictViewIterator(value)).iterator),
 529             .range => |value| try self.dictFromIterator((try self.heap.createRangeIterator(value)).iterator),
 530             else => Error.TypeError,
 531         };
 532     }
 533 
 534     fn enumerateBuiltin(self: *Vm, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 535         if (arguments.len == 0 or arguments.len > 2) return Error.ArityMismatch;
 536         const start = if (arguments.len == 2) arguments[1].integerLike() orelse return Error.TypeError else 0;
 537         return try self.heap.createEnumerateIterator((try self.iteratorValue(arguments[0])).iterator, start);
 538     }
 539 
 540     fn iterBuiltin(self: *Vm, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 541         if (arguments.len != 1) return Error.ArityMismatch;
 542         return try self.iteratorValue(arguments[0]);
 543     }
 544 
 545     fn lenBuiltin(self: *Vm, arguments: []const object.Value) Error!object.Value {
 546         _ = self;
 547         if (arguments.len != 1) return Error.ArityMismatch;
 548         const length = switch (arguments[0]) {
 549             .string => |value| std.unicode.utf8CountCodepoints(value) catch return Error.ValueError,
 550             .list => |value| value.items.len,
 551             .tuple => |value| value.items.len,
 552             .dict => |value| value.entries.items.len,
 553             .view => |value| value.dict.entries.items.len,
 554             .range => |value| value.length,
 555             else => return Error.TypeError,
 556         };
 557         return .{ .integer = @intCast(length) };
 558     }
 559 
 560     fn listBuiltin(self: *Vm, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 561         if (arguments.len > 1) return Error.ArityMismatch;
 562         if (arguments.len == 0) return try self.heap.createList(&.{});
 563         return switch (arguments[0]) {
 564             .iterator => |value| try self.listFromIterator(value),
 565             .string => |value| try self.listFromIterator((try self.heap.createStringIterator(value)).iterator),
 566             .list => |value| try self.heap.createList(value.items),
 567             .tuple => |value| try self.heap.createList(value.items),
 568             .dict => |value| try self.listFromIterator((try self.heap.createDictIterator(value)).iterator),
 569             .view => |value| try self.listFromIterator((try self.heap.createDictViewIterator(value)).iterator),
 570             .range => |value| try self.listFromIterator((try self.heap.createRangeIterator(value)).iterator),
 571             else => Error.TypeError,
 572         };
 573     }
 574 
 575     fn nextBuiltin(self: *Vm, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 576         if (arguments.len == 0 or arguments.len > 2) return Error.ArityMismatch;
 577         const iterator = switch (arguments[0]) {
 578             .iterator => |value| value,
 579             else => return Error.TypeError,
 580         };
 581         if (try self.iteratorNext(iterator)) |value| return value;
 582         if (arguments.len == 2) return arguments[1];
 583         return Error.StopIteration;
 584     }
 585 
 586     fn rangeBuiltin(self: *Vm, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 587         if (arguments.len == 0 or arguments.len > 3) return Error.ArityMismatch;
 588         const stop = arguments[if (arguments.len == 1) 0 else 1].integerLike() orelse return Error.TypeError;
 589         const start = if (arguments.len == 1) 0 else arguments[0].integerLike() orelse return Error.TypeError;
 590         const step = if (arguments.len == 3) arguments[2].integerLike() orelse return Error.TypeError else 1;
 591         if (step == 0) return Error.ValueError;
 592         return try self.heap.createRange(start, stop, step, try rangeLength(start, stop, step));
 593     }
 594 
 595     fn reversedBuiltin(self: *Vm, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 596         if (arguments.len != 1) return Error.ArityMismatch;
 597         return try self.reverseIteratorValue(arguments[0]);
 598     }
 599 
 600     fn tupleBuiltin(self: *Vm, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 601         if (arguments.len > 1) return Error.ArityMismatch;
 602         if (arguments.len == 0) return try self.heap.createTuple(&.{});
 603         return switch (arguments[0]) {
 604             .iterator => |value| try self.tupleFromIterator(value),
 605             .string => |value| try self.tupleFromIterator((try self.heap.createStringIterator(value)).iterator),
 606             .list => |value| try self.heap.createTuple(value.items),
 607             .tuple => arguments[0],
 608             .view => |value| try self.tupleFromIterator((try self.heap.createDictViewIterator(value)).iterator),
 609             .range => |value| try self.tupleFromIterator((try self.heap.createRangeIterator(value)).iterator),
 610             else => Error.TypeError,
 611         };
 612     }
 613 
 614     fn listFromIterator(self: *Vm, iterator: *object.value.Iterator) (Error || std.mem.Allocator.Error)!object.Value {
 615         var items = std.ArrayListUnmanaged(object.Value).empty;
 616         defer items.deinit(self.allocator);
 617         while (try self.iteratorNext(iterator)) |item| {
 618             try items.append(self.allocator, item);
 619         }
 620         return try self.heap.createList(items.items);
 621     }
 622 
 623     fn tupleFromIterator(self: *Vm, iterator: *object.value.Iterator) (Error || std.mem.Allocator.Error)!object.Value {
 624         var items = std.ArrayListUnmanaged(object.Value).empty;
 625         defer items.deinit(self.allocator);
 626         while (try self.iteratorNext(iterator)) |item| {
 627             try items.append(self.allocator, item);
 628         }
 629         return try self.heap.createTuple(items.items);
 630     }
 631 
 632     fn dictFromIterator(self: *Vm, iterator: *object.value.Iterator) (Error || std.mem.Allocator.Error)!object.Value {
 633         const dict = (try self.heap.createDict(&.{})).dict;
 634         while (try self.iteratorNext(iterator)) |item| {
 635             const pair = try pairFromValue(item);
 636             try self.dictSet(dict, pair.key, pair.value);
 637         }
 638         return .{ .dict = dict };
 639     }
 640 
 641     fn updateDictFromValue(self: *Vm, dict: *object.Dict, value: object.Value) (Error || std.mem.Allocator.Error)!void {
 642         switch (value) {
 643             .dict => |other| try self.updateDictFromEntries(dict, other.entries.items),
 644             .iterator => |iterator| try self.updateDictFromIterator(dict, iterator),
 645             .string => |string| try self.updateDictFromIterator(dict, (try self.heap.createStringIterator(string)).iterator),
 646             .list => |list| try self.updateDictFromIterator(dict, (try self.heap.createListIterator(list)).iterator),
 647             .tuple => |tuple| try self.updateDictFromIterator(dict, (try self.heap.createTupleIterator(tuple)).iterator),
 648             .view => |view| try self.updateDictFromIterator(dict, (try self.heap.createDictViewIterator(view)).iterator),
 649             .range => |range| try self.updateDictFromIterator(dict, (try self.heap.createRangeIterator(range)).iterator),
 650             else => return Error.TypeError,
 651         }
 652     }
 653 
 654     fn updateDictFromEntries(self: *Vm, dict: *object.Dict, entries: []const object.DictEntry) (Error || std.mem.Allocator.Error)!void {
 655         for (entries) |entry| try self.dictSet(dict, entry.key, entry.value);
 656     }
 657 
 658     fn updateDictFromIterator(self: *Vm, dict: *object.Dict, iterator: *object.value.Iterator) (Error || std.mem.Allocator.Error)!void {
 659         while (try self.iteratorNext(iterator)) |item| {
 660             const pair = try pairFromValue(item);
 661             try self.dictSet(dict, pair.key, pair.value);
 662         }
 663     }
 664 
 665     fn buildList(self: *Vm, item_count: usize) (Error || std.mem.Allocator.Error)!void {
 666         if (self.stack.items.len < item_count) return Error.StackUnderflow;
 667         const start = self.stack.items.len - item_count;
 668         const list = try self.heap.createList(self.stack.items[start..]);
 669         self.stack.items.len = start;
 670         try self.push(list);
 671     }
 672 
 673     fn buildTuple(self: *Vm, item_count: usize) (Error || std.mem.Allocator.Error)!void {
 674         if (self.stack.items.len < item_count) return Error.StackUnderflow;
 675         const start = self.stack.items.len - item_count;
 676         const tuple = try self.heap.createTuple(self.stack.items[start..]);
 677         self.stack.items.len = start;
 678         try self.push(tuple);
 679     }
 680 
 681     fn buildDict(self: *Vm, item_count: usize) (Error || std.mem.Allocator.Error)!void {
 682         const stack_count = std.math.mul(usize, item_count, 2) catch return Error.IntegerOverflow;
 683         if (self.stack.items.len < stack_count) return Error.StackUnderflow;
 684         const start = self.stack.items.len - stack_count;
 685         const dict = (try self.heap.createDict(&.{})).dict;
 686         try dict.ensureTotalCapacity(self.allocator, item_count);
 687         for (0..item_count) |index| {
 688             const key_index = start + index * 2;
 689             try self.dictSet(dict, self.stack.items[key_index], self.stack.items[key_index + 1]);
 690         }
 691         self.stack.items.len = start;
 692         try self.push(.{ .dict = dict });
 693     }
 694 
 695     fn iter(self: *Vm) (Error || std.mem.Allocator.Error)!void {
 696         const iterable = try self.pop();
 697         try self.push(try self.iteratorValue(iterable));
 698     }
 699 
 700     fn iteratorValue(self: *Vm, iterable: object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 701         return switch (iterable) {
 702             .iterator => iterable,
 703             .list => |list| try self.heap.createListIterator(list),
 704             .dict => |dict| try self.heap.createDictIterator(dict),
 705             .view => |view| try self.heap.createDictViewIterator(view),
 706             .tuple => |tuple| try self.heap.createTupleIterator(tuple),
 707             .range => |range| try self.heap.createRangeIterator(range),
 708             .string => |string| try self.heap.createStringIterator(string),
 709             else => Error.TypeError,
 710         };
 711     }
 712 
 713     fn reverseIteratorValue(self: *Vm, iterable: object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 714         return switch (iterable) {
 715             .list => |list| try self.heap.createListReverseIterator(list),
 716             .dict => |dict| try self.heap.createDictReverseIterator(dict),
 717             .view => |view| try self.heap.createDictViewReverseIterator(view),
 718             .tuple => |tuple| try self.heap.createTupleReverseIterator(tuple),
 719             .range => |range| try self.heap.createRangeReverseIterator(range),
 720             .string => |string| try self.heap.createStringReverseIterator(string),
 721             else => Error.TypeError,
 722         };
 723     }
 724 
 725     fn forNext(self: *Vm, exit: usize) (Error || std.mem.Allocator.Error)!void {
 726         const value = try self.peek();
 727         switch (value) {
 728             .iterator => |iterator| {
 729                 if (try self.iteratorNext(iterator)) |item| {
 730                     try self.push(item);
 731                 } else {
 732                     _ = try self.pop();
 733                     self.currentFrame().ip = exit;
 734                 }
 735             },
 736             else => return Error.TypeError,
 737         }
 738     }
 739 
 740     fn iteratorNext(self: *Vm, iterator: *object.value.Iterator) (Error || std.mem.Allocator.Error)!?object.Value {
 741         return switch (iterator.*) {
 742             .dict => |*dict| dictIteratorNext(dict),
 743             .enumerate => |*enumerate| try self.enumerateIteratorNext(enumerate),
 744             .view => |*view| try self.dictViewIteratorNext(view),
 745             .list => |*list| listIteratorNext(list),
 746             .tuple => |*tuple| tupleIteratorNext(tuple),
 747             .range => |*range| rangeIteratorNext(range),
 748             .string => |*string| stringIteratorNext(string),
 749         };
 750     }
 751 
 752     fn enumerateIteratorNext(self: *Vm, iterator: *object.value.EnumerateIterator) (Error || std.mem.Allocator.Error)!?object.Value {
 753         if (iterator.overflowed) {
 754             if (try self.iteratorNext(iterator.iterator)) |_| return Error.IntegerOverflow;
 755             return null;
 756         }
 757         const item = (try self.iteratorNext(iterator.iterator)) orelse return null;
 758         const pair = [_]object.Value{
 759             .{ .integer = iterator.index },
 760             item,
 761         };
 762         iterator.index = std.math.add(i128, iterator.index, 1) catch blk: {
 763             iterator.overflowed = true;
 764             break :blk iterator.index;
 765         };
 766         return try self.heap.createTuple(&pair);
 767     }
 768 
 769     fn listIteratorNext(iterator: *object.value.ListIterator) ?object.Value {
 770         if (iterator.index >= iterator.list.items.len) return null;
 771         const index = if (iterator.reverse) iterator.list.items.len - 1 - iterator.index else iterator.index;
 772         const item = iterator.list.items[index];
 773         iterator.index += 1;
 774         return item;
 775     }
 776 
 777     fn dictIteratorNext(iterator: *object.value.DictIterator) ?object.Value {
 778         if (iterator.index >= iterator.dict.entries.items.len) return null;
 779         const index = if (iterator.reverse) iterator.dict.entries.items.len - 1 - iterator.index else iterator.index;
 780         const item = iterator.dict.entries.items[index].key;
 781         iterator.index += 1;
 782         return item;
 783     }
 784 
 785     fn dictViewIteratorNext(self: *Vm, iterator: *object.value.DictViewIterator) (Error || std.mem.Allocator.Error)!?object.Value {
 786         if (iterator.index >= iterator.view.dict.entries.items.len) return null;
 787         const index = if (iterator.reverse) iterator.view.dict.entries.items.len - 1 - iterator.index else iterator.index;
 788         const entry = iterator.view.dict.entries.items[index];
 789         iterator.index += 1;
 790         return switch (iterator.view.kind) {
 791             .keys => entry.key,
 792             .values => entry.value,
 793             .items => blk: {
 794                 const pair = [_]object.Value{ entry.key, entry.value };
 795                 break :blk try self.heap.createTuple(&pair);
 796             },
 797         };
 798     }
 799 
 800     fn tupleIteratorNext(iterator: *object.value.TupleIterator) ?object.Value {
 801         if (iterator.index >= iterator.tuple.items.len) return null;
 802         const index = if (iterator.reverse) iterator.tuple.items.len - 1 - iterator.index else iterator.index;
 803         const item = iterator.tuple.items[index];
 804         iterator.index += 1;
 805         return item;
 806     }
 807 
 808     fn rangeIteratorNext(iterator: *object.value.RangeIterator) Error!?object.Value {
 809         if (iterator.index >= iterator.range.length) {
 810             return null;
 811         }
 812         if (iterator.reverse) {
 813             const index: i128 = @intCast(iterator.range.length - 1 - iterator.index);
 814             const item = try rangeValueAt(iterator.range, index);
 815             iterator.index += 1;
 816             return .{ .integer = item };
 817         }
 818         const item = iterator.next;
 819         const next_index = iterator.index + 1;
 820         const next = if (next_index < iterator.range.length) std.math.add(i128, iterator.next, iterator.range.step) catch return Error.IntegerOverflow else undefined;
 821         iterator.index = next_index;
 822         if (iterator.index < iterator.range.length) iterator.next = next;
 823         return .{ .integer = item };
 824     }
 825 
 826     fn stringIteratorNext(iterator: *object.value.StringIterator) Error!?object.Value {
 827         if (iterator.reverse) return stringReverseIteratorNext(iterator);
 828         if (iterator.index >= iterator.value.len) return null;
 829         var view = std.unicode.Utf8View.init(iterator.value[iterator.index..]) catch return Error.ValueError;
 830         var utf8 = view.iterator();
 831         const codepoint = utf8.nextCodepointSlice() orelse return null;
 832         const start = iterator.index;
 833         iterator.index += codepoint.len;
 834         return .{ .string = iterator.value[start..iterator.index] };
 835     }
 836 
 837     fn stringReverseIteratorNext(iterator: *object.value.StringIterator) Error!?object.Value {
 838         if (iterator.index == 0) return null;
 839         _ = std.unicode.Utf8View.init(iterator.value) catch return Error.ValueError;
 840         var start = iterator.index - 1;
 841         while (start > 0 and (iterator.value[start] & 0xc0) == 0x80) start -= 1;
 842         const end = iterator.index;
 843         iterator.index = start;
 844         return .{ .string = iterator.value[start..end] };
 845     }
 846 
 847     fn subscript(self: *Vm) (Error || std.mem.Allocator.Error)!void {
 848         const index = try self.pop();
 849         const target = try self.pop();
 850         switch (target) {
 851             .list => |list| try self.push(try listItem(list, index)),
 852             .tuple => |tuple| try self.push(try tupleItem(tuple, index)),
 853             .dict => |dict| try self.push(try dictItem(dict, index)),
 854             else => return Error.TypeError,
 855         }
 856     }
 857 
 858     fn slice(self: *Vm) (Error || std.mem.Allocator.Error)!void {
 859         const step = try self.pop();
 860         const stop = try self.pop();
 861         const start = try self.pop();
 862         const target = try self.pop();
 863         const result = switch (target) {
 864             .list => |list| try self.listSlice(list, start, stop, step),
 865             .tuple => |tuple| try self.tupleSlice(tuple, start, stop, step),
 866             .range => |range| try self.rangeSlice(range, start, stop, step),
 867             .string => |string| try self.stringSlice(string, start, stop, step),
 868             else => return Error.TypeError,
 869         };
 870         try self.push(result);
 871     }
 872 
 873     fn storeSubscript(self: *Vm) (Error || std.mem.Allocator.Error)!void {
 874         const value = try self.pop();
 875         const index = try self.pop();
 876         const target = try self.pop();
 877         switch (target) {
 878             .list => |list| list.items[try listIndex(list, index)] = value,
 879             .dict => |dict| try self.dictSet(dict, index, value),
 880             else => return Error.TypeError,
 881         }
 882     }
 883 
 884     fn deleteSubscript(self: *Vm) (Error || std.mem.Allocator.Error)!void {
 885         const index = try self.pop();
 886         const target = try self.pop();
 887         switch (target) {
 888             .list => |list| try self.listDelete(list, index),
 889             .dict => |dict| try dictDelete(dict, index),
 890             else => return Error.TypeError,
 891         }
 892     }
 893 
 894     fn listItem(list: *const object.List, index_value: object.Value) Error!object.Value {
 895         return list.items[try sequenceIndex(list.items.len, index_value)];
 896     }
 897 
 898     fn listIndex(list: *const object.List, index_value: object.Value) Error!usize {
 899         return sequenceIndex(list.items.len, index_value);
 900     }
 901 
 902     fn tupleItem(tuple: *const object.Tuple, index_value: object.Value) Error!object.Value {
 903         return tuple.items[try sequenceIndex(tuple.items.len, index_value)];
 904     }
 905 
 906     fn dictItem(dict: *const object.Dict, key: object.Value) Error!object.Value {
 907         const index = try dictEntryIndex(dict, key) orelse return Error.KeyError;
 908         return dict.entries.items[index].value;
 909     }
 910 
 911     fn dictSet(self: *Vm, dict: *object.Dict, key: object.Value, value: object.Value) (Error || std.mem.Allocator.Error)!void {
 912         if (!key.hashable()) return Error.TypeError;
 913         try dict.put(self.allocator, key, value);
 914     }
 915 
 916     fn listDelete(self: *Vm, list: *object.List, index_value: object.Value) (Error || std.mem.Allocator.Error)!void {
 917         _ = try self.listRemoveAt(list, index_value);
 918     }
 919 
 920     fn listAppend(self: *Vm, list: *object.List, value: object.Value) (Error || std.mem.Allocator.Error)!void {
 921         _ = try sequenceConcatLength(list.items.len, 1);
 922         try list.append(self.allocator, value);
 923     }
 924 
 925     fn listClear(self: *Vm, list: *object.List) void {
 926         list.clearAndFree(self.allocator);
 927     }
 928 
 929     fn listRemoveAt(self: *Vm, list: *object.List, index_value: object.Value) Error!object.Value {
 930         const index = try listIndex(list, index_value);
 931         const removed = list.orderedRemove(index);
 932         if (list.items.len < list.capacity / 2) list.shrinkAndFree(self.allocator, list.items.len);
 933         return removed;
 934     }
 935 
 936     fn dictDelete(dict: *object.Dict, key: object.Value) Error!void {
 937         const index = try dictEntryIndex(dict, key) orelse return Error.KeyError;
 938         _ = dict.removeAt(index);
 939     }
 940 
 941     fn listSlice(self: *Vm, list: *const object.List, start_value: object.Value, stop_value: object.Value, step_value: object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 942         const bounds = try sliceBounds(list.items.len, start_value, stop_value, step_value);
 943         var items = std.ArrayListUnmanaged(object.Value).empty;
 944         defer items.deinit(self.allocator);
 945         try items.ensureTotalCapacity(self.allocator, sliceCount(bounds));
 946         var index = bounds.start;
 947         while (sliceIncludes(index, bounds)) {
 948             try items.append(self.allocator, list.items[@intCast(index)]);
 949             index = std.math.add(i128, index, bounds.step) catch break;
 950         }
 951         return try self.heap.createList(items.items);
 952     }
 953 
 954     fn tupleSlice(self: *Vm, tuple: *const object.Tuple, start_value: object.Value, stop_value: object.Value, step_value: object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 955         const bounds = try sliceBounds(tuple.items.len, start_value, stop_value, step_value);
 956         var items = std.ArrayListUnmanaged(object.Value).empty;
 957         defer items.deinit(self.allocator);
 958         try items.ensureTotalCapacity(self.allocator, sliceCount(bounds));
 959         var index = bounds.start;
 960         while (sliceIncludes(index, bounds)) {
 961             try items.append(self.allocator, tuple.items[@intCast(index)]);
 962             index = std.math.add(i128, index, bounds.step) catch break;
 963         }
 964         return try self.heap.createTuple(items.items);
 965     }
 966 
 967     fn rangeSlice(self: *Vm, range: *const object.Range, start_value: object.Value, stop_value: object.Value, step_value: object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 968         const bounds = try sliceBounds(range.length, start_value, stop_value, step_value);
 969         const length = sliceCount(bounds);
 970         if (length == 0) return try self.heap.createRange(0, 0, 1, 0);
 971         const start = try rangeValueAt(range, bounds.start);
 972         const step = std.math.mul(i128, range.step, bounds.step) catch return Error.IntegerOverflow;
 973         const length_value: i128 = @intCast(length);
 974         const span = std.math.mul(i128, step, length_value) catch return Error.IntegerOverflow;
 975         const stop = std.math.add(i128, start, span) catch return Error.IntegerOverflow;
 976         return try self.heap.createRange(start, stop, step, length);
 977     }
 978 
 979     fn stringSlice(self: *Vm, value: []const u8, start_value: object.Value, stop_value: object.Value, step_value: object.Value) (Error || std.mem.Allocator.Error)!object.Value {
 980         var offsets = std.ArrayListUnmanaged(usize).empty;
 981         defer offsets.deinit(self.allocator);
 982         var view = std.unicode.Utf8View.init(value) catch return Error.ValueError;
 983         var iterator = view.iterator();
 984         var byte_index: usize = 0;
 985         while (iterator.nextCodepointSlice()) |codepoint| {
 986             try offsets.append(self.allocator, byte_index);
 987             byte_index += codepoint.len;
 988         }
 989         try offsets.append(self.allocator, value.len);
 990 
 991         const bounds = try sliceBounds(offsets.items.len - 1, start_value, stop_value, step_value);
 992         if (sliceCount(bounds) == 0) return .{ .string = value[0..0] };
 993         if (bounds.step == 1) {
 994             const start: usize = @intCast(bounds.start);
 995             const stop: usize = @intCast(bounds.stop);
 996             return .{ .string = value[offsets.items[start]..offsets.items[stop]] };
 997         }
 998 
 999         var bytes = std.ArrayListUnmanaged(u8).empty;
1000         defer bytes.deinit(self.allocator);
1001         var index = bounds.start;
1002         while (sliceIncludes(index, bounds)) {
1003             const item: usize = @intCast(index);
1004             try bytes.appendSlice(self.allocator, value[offsets.items[item]..offsets.items[item + 1]]);
1005             index = std.math.add(i128, index, bounds.step) catch break;
1006         }
1007         return try self.heap.createString(bytes.items);
1008     }
1009 
1010     fn sequenceIndex(length: usize, index_value: object.Value) Error!usize {
1011         var index = index_value.integerLike() orelse return Error.TypeError;
1012         const len: i128 = @intCast(length);
1013         if (index < 0) index += len;
1014         if (index < 0 or index >= len) return Error.IndexError;
1015         return @intCast(index);
1016     }
1017 
1018     fn returnValue(self: *Vm, value: object.Value) std.mem.Allocator.Error!?object.Value {
1019         self.popFrame();
1020         if (self.frames.items.len == 0) {
1021             return value;
1022         } else {
1023             try self.push(value);
1024         }
1025         return null;
1026     }
1027 
1028     fn negate(self: *Vm) (Error || std.mem.Allocator.Error)!void {
1029         const value = try self.pop();
1030         const integer = value.integerLike() orelse return Error.TypeError;
1031         if (integer == std.math.minInt(i128)) return Error.IntegerOverflow;
1032         try self.push(.{ .integer = -integer });
1033     }
1034 
1035     fn logicalNot(self: *Vm) (Error || std.mem.Allocator.Error)!void {
1036         const value = try self.pop();
1037         try self.push(.{ .boolean = !value.truthy() });
1038     }
1039 
1040     fn binary(self: *Vm, op: BinaryOp) (Error || std.mem.Allocator.Error)!void {
1041         const right = try self.pop();
1042         const left = try self.pop();
1043         switch (op) {
1044             .equal => {
1045                 try self.push(.{ .boolean = left.eql(right) });
1046                 return;
1047             },
1048             .not_equal => {
1049                 try self.push(.{ .boolean = !left.eql(right) });
1050                 return;
1051             },
1052             .identical => {
1053                 try self.push(.{ .boolean = identical(left, right) });
1054                 return;
1055             },
1056             .not_identical => {
1057                 try self.push(.{ .boolean = !identical(left, right) });
1058                 return;
1059             },
1060             .contains => {
1061                 try self.push(.{ .boolean = try self.contains(left, right) });
1062                 return;
1063             },
1064             .not_contains => {
1065                 try self.push(.{ .boolean = !(try self.contains(left, right)) });
1066                 return;
1067             },
1068             .add => {
1069                 try self.push(try self.add(left, right));
1070                 return;
1071             },
1072             .mul => {
1073                 try self.push(try self.multiply(left, right));
1074                 return;
1075             },
1076             .less, .less_equal, .greater, .greater_equal => {
1077                 try self.push(.{ .boolean = try orderValues(left, right, op) });
1078                 return;
1079             },
1080             else => {},
1081         }
1082         const a = left.integerLike() orelse return Error.TypeError;
1083         const b = right.integerLike() orelse return Error.TypeError;
1084         switch (op) {
1085             .add => try self.push(.{ .integer = std.math.add(i128, a, b) catch return Error.IntegerOverflow }),
1086             .sub => try self.push(.{ .integer = std.math.sub(i128, a, b) catch return Error.IntegerOverflow }),
1087             .mul => try self.push(.{ .integer = std.math.mul(i128, a, b) catch return Error.IntegerOverflow }),
1088             .less => try self.push(.{ .boolean = a < b }),
1089             .less_equal => try self.push(.{ .boolean = a <= b }),
1090             .greater => try self.push(.{ .boolean = a > b }),
1091             .greater_equal => try self.push(.{ .boolean = a >= b }),
1092             .equal, .not_equal, .identical, .not_identical, .contains, .not_contains => unreachable,
1093         }
1094     }
1095 
1096     fn add(self: *Vm, left: object.Value, right: object.Value) (Error || std.mem.Allocator.Error)!object.Value {
1097         return switch (left) {
1098             .list => |list| switch (right) {
1099                 .list => |other| try self.listConcat(list.items, other.items),
1100                 else => Error.TypeError,
1101             },
1102             .tuple => |tuple| switch (right) {
1103                 .tuple => |other| try self.tupleConcat(tuple.items, other.items),
1104                 else => Error.TypeError,
1105             },
1106             .string => |string| switch (right) {
1107                 .string => |other| try self.stringConcat(string, other),
1108                 else => Error.TypeError,
1109             },
1110             else => {
1111                 const a = left.integerLike() orelse return Error.TypeError;
1112                 const b = right.integerLike() orelse return Error.TypeError;
1113                 return .{ .integer = std.math.add(i128, a, b) catch return Error.IntegerOverflow };
1114             },
1115         };
1116     }
1117 
1118     fn multiply(self: *Vm, left: object.Value, right: object.Value) (Error || std.mem.Allocator.Error)!object.Value {
1119         return switch (left) {
1120             .list => |list| try self.listRepeat(list.items, right),
1121             .tuple => |tuple| try self.tupleRepeat(tuple.items, right),
1122             .string => |string| try self.stringRepeat(string, right),
1123             else => switch (right) {
1124                 .list => |list| try self.listRepeat(list.items, left),
1125                 .tuple => |tuple| try self.tupleRepeat(tuple.items, left),
1126                 .string => |string| try self.stringRepeat(string, left),
1127                 else => {
1128                     const a = left.integerLike() orelse return Error.TypeError;
1129                     const b = right.integerLike() orelse return Error.TypeError;
1130                     return .{ .integer = std.math.mul(i128, a, b) catch return Error.IntegerOverflow };
1131                 },
1132             },
1133         };
1134     }
1135 
1136     fn listConcat(self: *Vm, left: []const object.Value, right: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {
1137         var items = std.ArrayListUnmanaged(object.Value).empty;
1138         defer items.deinit(self.allocator);
1139         try items.ensureTotalCapacity(self.allocator, try sequenceConcatLength(left.len, right.len));
1140         try items.appendSlice(self.allocator, left);
1141         try items.appendSlice(self.allocator, right);
1142         return try self.heap.createList(items.items);
1143     }
1144 
1145     fn tupleConcat(self: *Vm, left: []const object.Value, right: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {
1146         var items = std.ArrayListUnmanaged(object.Value).empty;
1147         defer items.deinit(self.allocator);
1148         try items.ensureTotalCapacity(self.allocator, try sequenceConcatLength(left.len, right.len));
1149         try items.appendSlice(self.allocator, left);
1150         try items.appendSlice(self.allocator, right);
1151         return try self.heap.createTuple(items.items);
1152     }
1153 
1154     fn stringConcat(self: *Vm, left: []const u8, right: []const u8) (Error || std.mem.Allocator.Error)!object.Value {
1155         var bytes = std.ArrayListUnmanaged(u8).empty;
1156         defer bytes.deinit(self.allocator);
1157         try bytes.ensureTotalCapacity(self.allocator, try sequenceConcatLength(left.len, right.len));
1158         try bytes.appendSlice(self.allocator, left);
1159         try bytes.appendSlice(self.allocator, right);
1160         return try self.heap.createString(bytes.items);
1161     }
1162 
1163     fn listRepeat(self: *Vm, items: []const object.Value, count_value: object.Value) (Error || std.mem.Allocator.Error)!object.Value {
1164         const count = try repeatCount(count_value);
1165         const length = try repeatLength(items.len, count);
1166         if (length == 0) return try self.heap.createList(&.{});
1167         var repeated = std.ArrayListUnmanaged(object.Value).empty;
1168         defer repeated.deinit(self.allocator);
1169         try repeated.ensureTotalCapacity(self.allocator, length);
1170         for (0..count) |_| try repeated.appendSlice(self.allocator, items);
1171         return try self.heap.createList(repeated.items);
1172     }
1173 
1174     fn tupleRepeat(self: *Vm, items: []const object.Value, count_value: object.Value) (Error || std.mem.Allocator.Error)!object.Value {
1175         const count = try repeatCount(count_value);
1176         const length = try repeatLength(items.len, count);
1177         if (length == 0) return try self.heap.createTuple(&.{});
1178         var repeated = std.ArrayListUnmanaged(object.Value).empty;
1179         defer repeated.deinit(self.allocator);
1180         try repeated.ensureTotalCapacity(self.allocator, length);
1181         for (0..count) |_| try repeated.appendSlice(self.allocator, items);
1182         return try self.heap.createTuple(repeated.items);
1183     }
1184 
1185     fn stringRepeat(self: *Vm, bytes: []const u8, count_value: object.Value) (Error || std.mem.Allocator.Error)!object.Value {
1186         const count = try repeatCount(count_value);
1187         const length = try repeatLength(bytes.len, count);
1188         if (length == 0) return try self.heap.createString("");
1189         var repeated = std.ArrayListUnmanaged(u8).empty;
1190         defer repeated.deinit(self.allocator);
1191         try repeated.ensureTotalCapacity(self.allocator, length);
1192         for (0..count) |_| try repeated.appendSlice(self.allocator, bytes);
1193         return try self.heap.createString(repeated.items);
1194     }
1195 
1196     fn contains(self: *Vm, item: object.Value, container: object.Value) (Error || std.mem.Allocator.Error)!bool {
1197         return switch (container) {
1198             .list => |list| listContains(item, list),
1199             .tuple => |tuple| sequenceContains(item, tuple.items),
1200             .dict => |dict| try dictContains(item, dict),
1201             .view => |view| try self.dictViewContains(item, view),
1202             .range => |range| rangeContains(item, range),
1203             .string => |string| stringContains(item, string),
1204             .iterator => |iterator| try self.iteratorContains(item, iterator),
1205             else => Error.TypeError,
1206         };
1207     }
1208 
1209     fn iteratorContains(self: *Vm, item: object.Value, iterator: *object.value.Iterator) (Error || std.mem.Allocator.Error)!bool {
1210         while (try self.iteratorNext(iterator)) |candidate| {
1211             if (identical(item, candidate) or item.eql(candidate)) return true;
1212         }
1213         return false;
1214     }
1215 
1216     fn dictViewContains(self: *Vm, item: object.Value, view: *const object.DictView) (Error || std.mem.Allocator.Error)!bool {
1217         _ = self;
1218         return switch (view.kind) {
1219             .keys => try dictContains(item, view.dict),
1220             .values => dictValuesContain(item, view.dict),
1221             .items => try dictItemsContain(item, view.dict),
1222         };
1223     }
1224 };
1225 
1226 const BinaryOp = enum {
1227     add,
1228     sub,
1229     mul,
1230     equal,
1231     not_equal,
1232     less,
1233     less_equal,
1234     greater,
1235     greater_equal,
1236     contains,
1237     not_contains,
1238     identical,
1239     not_identical,
1240 };
1241 
1242 fn sequenceConcatLength(left: usize, right: usize) Error!usize {
1243     return std.math.add(usize, left, right) catch Error.IntegerOverflow;
1244 }
1245 
1246 fn repeatCount(value: object.Value) Error!usize {
1247     const count = value.integerLike() orelse return Error.TypeError;
1248     if (count <= 0) return 0;
1249     const max: i128 = @intCast(std.math.maxInt(usize));
1250     if (count > max) return Error.IntegerOverflow;
1251     return @intCast(count);
1252 }
1253 
1254 fn repeatLength(length: usize, count: usize) Error!usize {
1255     return std.math.mul(usize, length, count) catch Error.IntegerOverflow;
1256 }
1257 
1258 const ValueOrder = enum {
1259     lt,
1260     eq,
1261     gt,
1262 };
1263 
1264 fn orderValues(left: object.Value, right: object.Value, op: BinaryOp) Error!bool {
1265     return orderResult(try compareValues(left, right), op);
1266 }
1267 
1268 fn compareValues(left: object.Value, right: object.Value) Error!ValueOrder {
1269     if (left.integerLike()) |a| {
1270         if (right.integerLike()) |b| return compareInteger(a, b);
1271     }
1272     return switch (left) {
1273         .string => |value| switch (right) {
1274             .string => |other| compareString(value, other),
1275             else => Error.TypeError,
1276         },
1277         .list => |value| switch (right) {
1278             .list => |other| try compareSequences(value.items, other.items),
1279             else => Error.TypeError,
1280         },
1281         .tuple => |value| switch (right) {
1282             .tuple => |other| try compareSequences(value.items, other.items),
1283             else => Error.TypeError,
1284         },
1285         else => Error.TypeError,
1286     };
1287 }
1288 
1289 fn compareSequences(left: []const object.Value, right: []const object.Value) Error!ValueOrder {
1290     const count = @min(left.len, right.len);
1291     for (left[0..count], right[0..count]) |a, b| {
1292         if (identical(a, b) or a.eql(b)) continue;
1293         const item_order = try compareValues(a, b);
1294         if (item_order != .eq) return item_order;
1295     }
1296     return compareLength(left.len, right.len);
1297 }
1298 
1299 fn compareInteger(left: i128, right: i128) ValueOrder {
1300     if (left < right) return .lt;
1301     if (left > right) return .gt;
1302     return .eq;
1303 }
1304 
1305 fn compareLength(left: usize, right: usize) ValueOrder {
1306     if (left < right) return .lt;
1307     if (left > right) return .gt;
1308     return .eq;
1309 }
1310 
1311 fn compareString(left: []const u8, right: []const u8) ValueOrder {
1312     return switch (std.mem.order(u8, left, right)) {
1313         .lt => .lt,
1314         .eq => .eq,
1315         .gt => .gt,
1316     };
1317 }
1318 
1319 fn orderResult(order: ValueOrder, op: BinaryOp) bool {
1320     return switch (op) {
1321         .less => order == .lt,
1322         .less_equal => order != .gt,
1323         .greater => order == .gt,
1324         .greater_equal => order != .lt,
1325         else => unreachable,
1326     };
1327 }
1328 
1329 const SliceBounds = struct {
1330     start: i128,
1331     stop: i128,
1332     step: i128,
1333 };
1334 
1335 fn sliceBounds(length: usize, start_value: object.Value, stop_value: object.Value, step_value: object.Value) Error!SliceBounds {
1336     const step = (try optionalSliceInteger(step_value)) orelse 1;
1337     if (step == 0) return Error.ValueError;
1338     const len: i128 = @intCast(length);
1339     if (step > 0) {
1340         return .{
1341             .start = if (try optionalSliceInteger(start_value)) |value| positiveSliceBound(value, len) else 0,
1342             .stop = if (try optionalSliceInteger(stop_value)) |value| positiveSliceBound(value, len) else len,
1343             .step = step,
1344         };
1345     }
1346     return .{
1347         .start = if (try optionalSliceInteger(start_value)) |value| negativeSliceBound(value, len) else len - 1,
1348         .stop = if (try optionalSliceInteger(stop_value)) |value| negativeSliceBound(value, len) else -1,
1349         .step = step,
1350     };
1351 }
1352 
1353 fn optionalSliceInteger(value: object.Value) Error!?i128 {
1354     return switch (value) {
1355         .none => null,
1356         else => value.integerLike() orelse Error.TypeError,
1357     };
1358 }
1359 
1360 fn positiveSliceBound(value: i128, length: i128) i128 {
1361     var index = value;
1362     if (index < 0) index += length;
1363     if (index < 0) return 0;
1364     if (index > length) return length;
1365     return index;
1366 }
1367 
1368 fn negativeSliceBound(value: i128, length: i128) i128 {
1369     var index = value;
1370     if (index < 0) index += length;
1371     if (index < 0) return -1;
1372     if (index >= length) return length - 1;
1373     return index;
1374 }
1375 
1376 fn sliceIncludes(index: i128, bounds: SliceBounds) bool {
1377     if (bounds.step > 0) return index < bounds.stop;
1378     return index > bounds.stop;
1379 }
1380 
1381 fn sliceCount(bounds: SliceBounds) usize {
1382     var count: usize = 0;
1383     var index = bounds.start;
1384     while (sliceIncludes(index, bounds)) {
1385         count += 1;
1386         index = std.math.add(i128, index, bounds.step) catch break;
1387     }
1388     return count;
1389 }
1390 
1391 fn rangeValueAt(range: *const object.Range, index: i128) Error!i128 {
1392     const offset = std.math.mul(i128, range.step, index) catch return Error.IntegerOverflow;
1393     return std.math.add(i128, range.start, offset) catch return Error.IntegerOverflow;
1394 }
1395 
1396 fn identical(left: object.Value, right: object.Value) bool {
1397     return switch (left) {
1398         .none => right == .none,
1399         .boolean => |value| switch (right) {
1400             .boolean => |other| value == other,
1401             else => false,
1402         },
1403         .integer => |value| switch (right) {
1404             .integer => |other| value == other,
1405             else => false,
1406         },
1407         .string => |value| switch (right) {
1408             .string => |other| value.ptr == other.ptr and value.len == other.len,
1409             else => false,
1410         },
1411         .function => |value| switch (right) {
1412             .function => |other| value == other,
1413             else => false,
1414         },
1415         .builtin => |value| switch (right) {
1416             .builtin => |other| value == other,
1417             else => false,
1418         },
1419         .method => |value| switch (right) {
1420             .method => |other| value == other,
1421             else => false,
1422         },
1423         .view => |value| switch (right) {
1424             .view => |other| value == other,
1425             else => false,
1426         },
1427         .list => |value| switch (right) {
1428             .list => |other| value == other,
1429             else => false,
1430         },
1431         .tuple => |value| switch (right) {
1432             .tuple => |other| value == other,
1433             else => false,
1434         },
1435         .dict => |value| switch (right) {
1436             .dict => |other| value == other,
1437             else => false,
1438         },
1439         .range => |value| switch (right) {
1440             .range => |other| value == other,
1441             else => false,
1442         },
1443         .iterator => |value| switch (right) {
1444             .iterator => |other| value == other,
1445             else => false,
1446         },
1447     };
1448 }
1449 
1450 fn listContains(item: object.Value, list: *const object.List) bool {
1451     return sequenceContains(item, list.items);
1452 }
1453 
1454 fn sequenceContains(item: object.Value, items: []const object.Value) bool {
1455     for (items) |candidate| {
1456         if (identical(item, candidate) or item.eql(candidate)) return true;
1457     }
1458     return false;
1459 }
1460 
1461 fn dictContains(item: object.Value, dict: *const object.Dict) Error!bool {
1462     return (try dictEntryIndex(dict, item)) != null;
1463 }
1464 
1465 fn dictValuesContain(item: object.Value, dict: *const object.Dict) bool {
1466     for (dict.entries.items) |entry| {
1467         if (identical(item, entry.value) or item.eql(entry.value)) return true;
1468     }
1469     return false;
1470 }
1471 
1472 fn dictItemsContain(item: object.Value, dict: *const object.Dict) Error!bool {
1473     return switch (item) {
1474         .tuple => |tuple| {
1475             if (tuple.items.len != 2) return false;
1476             const index = try dictEntryIndex(dict, tuple.items[0]) orelse return false;
1477             const value = dict.entries.items[index].value;
1478             return identical(tuple.items[1], value) or tuple.items[1].eql(value);
1479         },
1480         else => false,
1481     };
1482 }
1483 
1484 fn dictEntryIndex(dict: *const object.Dict, key: object.Value) Error!?usize {
1485     if (!key.hashable()) return Error.TypeError;
1486     return dict.indexOf(key);
1487 }
1488 
1489 const Pair = struct {
1490     key: object.Value,
1491     value: object.Value,
1492 };
1493 
1494 fn pairFromValue(value: object.Value) Error!Pair {
1495     return switch (value) {
1496         .list => |list| pairFromSlice(list.items),
1497         .tuple => |tuple| pairFromSlice(tuple.items),
1498         else => Error.TypeError,
1499     };
1500 }
1501 
1502 fn pairFromSlice(items: []const object.Value) Error!Pair {
1503     if (items.len != 2) return Error.ValueError;
1504     return .{
1505         .key = items[0],
1506         .value = items[1],
1507     };
1508 }
1509 
1510 fn rangeContains(item: object.Value, range: *const object.Range) bool {
1511     if (range.length == 0) return false;
1512     const value = item.integerLike() orelse return false;
1513     if (range.step > 0) {
1514         if (value < range.start or value >= range.stop) return false;
1515         return orderedDistance(range.start, value) % @as(u128, @intCast(range.step)) == 0;
1516     }
1517     if (value > range.start or value <= range.stop) return false;
1518     return orderedDistance(value, range.start) % negativeMagnitude(range.step) == 0;
1519 }
1520 
1521 fn stringContains(item: object.Value, string: []const u8) Error!bool {
1522     const needle = switch (item) {
1523         .string => |value| value,
1524         else => return Error.TypeError,
1525     };
1526     return std.mem.indexOf(u8, string, needle) != null;
1527 }
1528 
1529 fn orderedDistance(start: i128, stop: i128) u128 {
1530     std.debug.assert(start <= stop);
1531     if (start == stop) return 0;
1532     if (start >= 0) return @intCast(stop - start);
1533     if (stop <= 0) return nonPositiveMagnitude(start) - nonPositiveMagnitude(stop);
1534     return nonPositiveMagnitude(start) + @as(u128, @intCast(stop));
1535 }
1536 
1537 fn builtin(name: []const u8) ?object.Value {
1538     if (std.mem.eql(u8, name, "dict")) return .{ .builtin = .dict };
1539     if (std.mem.eql(u8, name, "enumerate")) return .{ .builtin = .enumerate };
1540     if (std.mem.eql(u8, name, "iter")) return .{ .builtin = .iter };
1541     if (std.mem.eql(u8, name, "len")) return .{ .builtin = .len };
1542     if (std.mem.eql(u8, name, "list")) return .{ .builtin = .list };
1543     if (std.mem.eql(u8, name, "next")) return .{ .builtin = .next };
1544     if (std.mem.eql(u8, name, "range")) return .{ .builtin = .range };
1545     if (std.mem.eql(u8, name, "reversed")) return .{ .builtin = .reversed };
1546     if (std.mem.eql(u8, name, "tuple")) return .{ .builtin = .tuple };
1547     return null;
1548 }
1549 
1550 fn rangeLength(start: i128, stop: i128, step: i128) Error!usize {
1551     if (step > 0) {
1552         if (start >= stop) return 0;
1553         return try rangeCount(distanceAscending(start, stop), @intCast(step));
1554     }
1555     if (start <= stop) return 0;
1556     return try rangeCount(distanceAscending(stop, start), negativeMagnitude(step));
1557 }
1558 
1559 fn rangeCount(distance: u128, step: u128) Error!usize {
1560     const count = (distance - 1) / step + 1;
1561     if (count > @as(u128, std.math.maxInt(usize))) return Error.IntegerOverflow;
1562     return @intCast(count);
1563 }
1564 
1565 fn distanceAscending(start: i128, stop: i128) u128 {
1566     std.debug.assert(start < stop);
1567     if (start >= 0) return @intCast(stop - start);
1568     if (stop <= 0) return nonPositiveMagnitude(start) - nonPositiveMagnitude(stop);
1569     return nonPositiveMagnitude(start) + @as(u128, @intCast(stop));
1570 }
1571 
1572 fn nonPositiveMagnitude(value: i128) u128 {
1573     if (value == 0) return 0;
1574     return negativeMagnitude(value);
1575 }
1576 
1577 fn negativeMagnitude(value: i128) u128 {
1578     std.debug.assert(value < 0);
1579     return @as(u128, @intCast(-(value + 1))) + 1;
1580 }
1581 
1582 const Frame = struct {
1583     chunk: *const code.Chunk,
1584     ip: usize = 0,
1585     locals: std.StringHashMapUnmanaged(object.Value) = .empty,
1586     last: object.Value = .none,
1587 
1588     fn deinit(self: *Frame, allocator: std.mem.Allocator) void {
1589         self.locals.deinit(allocator);
1590         self.* = undefined;
1591     }
1592 };
1593 
1594 fn executeValue(allocator: std.mem.Allocator, bytes: []const u8) !object.Value {
1595     var result = try execute(allocator, bytes);
1596     defer result.deinit();
1597     return result.value;
1598 }
1599 
1600 test "execute arithmetic with precedence" {
1601     try std.testing.expectEqual(object.Value{ .integer = 7 }, try executeValue(std.testing.allocator, "1 + 2 * 3"));
1602 }
1603 
1604 test "execute assignments" {
1605     try std.testing.expectEqual(object.Value{ .integer = 13 }, try executeValue(std.testing.allocator,
1606         \\x = 5
1607         \\y = x * 2
1608         \\y + 3
1609     ));
1610 }
1611 
1612 test "execute name deletion" {
1613     try std.testing.expectEqual(object.Value{ .integer = 2 }, try executeValue(std.testing.allocator,
1614         \\x = 1
1615         \\del x
1616         \\x = 2
1617         \\x
1618     ));
1619     try std.testing.expectError(Error.UndefinedName, executeValue(std.testing.allocator,
1620         \\x = 1
1621         \\del x
1622         \\x
1623     ));
1624     try std.testing.expectError(Error.UndefinedName, executeValue(std.testing.allocator,
1625         \\def f():
1626         \\    x = 1
1627         \\    del x
1628         \\    return x
1629         \\f()
1630     ));
1631     try std.testing.expectError(Error.UndefinedName, executeValue(std.testing.allocator, "del missing"));
1632 }
1633 
1634 test "execute booleans as integers" {
1635     try std.testing.expectEqual(object.Value{ .integer = 2 }, try executeValue(std.testing.allocator, "True + True"));
1636 }
1637 
1638 test "execute simple function call" {
1639     try std.testing.expectEqual(object.Value{ .integer = 42 }, try executeValue(std.testing.allocator,
1640         \\def add(a, b):
1641         \\    return a + b
1642         \\add(20, 22)
1643     ));
1644 }
1645 
1646 test "execute function local frame with global fallback" {
1647     try std.testing.expectEqual(object.Value{ .integer = 17 }, try executeValue(std.testing.allocator,
1648         \\x = 10
1649         \\def f(x):
1650         \\    y = x + 2
1651         \\    return y
1652         \\f(5) + x
1653     ));
1654 }
1655 
1656 test "execute function without return yields none" {
1657     try std.testing.expectEqual(object.Value.none, try executeValue(std.testing.allocator,
1658         \\def f():
1659         \\    1 + 2
1660         \\f()
1661     ));
1662 }
1663 
1664 test "execute comparisons" {
1665     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "1 < 2"));
1666     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "True == 1"));
1667     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "None != 0"));
1668 }
1669 
1670 test "execute chained comparisons" {
1671     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "1 < 2 <= 2 != 3"));
1672     try std.testing.expectEqual(object.Value{ .boolean = false }, try executeValue(std.testing.allocator, "1 < 2 < 2"));
1673     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "1 < 3 > 2"));
1674 }
1675 
1676 test "execute chained comparisons short circuit" {
1677     try std.testing.expectEqual(object.Value{ .boolean = false }, try executeValue(std.testing.allocator, "3 < 2 < missing"));
1678 }
1679 
1680 test "execute chained sequence comparisons" {
1681     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1] < [2] < [3]"));
1682     try std.testing.expectEqual(object.Value{ .boolean = false }, try executeValue(std.testing.allocator, "\"b\" < \"a\" < missing"));
1683 }
1684 
1685 test "execute identity comparisons" {
1686     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "None is None"));
1687     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "True is not False"));
1688     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
1689         \\xs = []
1690         \\ys = xs
1691         \\xs is ys
1692     ));
1693     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
1694         \\xs = []
1695         \\ys = []
1696         \\xs is not ys
1697     ));
1698 }
1699 
1700 test "execute membership comparisons over supported containers" {
1701     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "2 in [1, 2, 3]"));
1702     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "4 not in [1, 2, 3]"));
1703     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "2 in (1, 2, 3)"));
1704     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "4 not in (1, 2, 3)"));
1705     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "2 in range(0, 5, 2)"));
1706     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "3 not in range(0, 5, 2)"));
1707     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "5 in range(7, 3, -1)"));
1708     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "\"bc\" in \"abcd\""));
1709     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "\"\" in \"abcd\""));
1710     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "\"z\" not in \"abcd\""));
1711 }
1712 
1713 test "execute membership consumes iterators" {
1714     try std.testing.expectEqual(object.Value{ .integer = 2 }, try executeValue(std.testing.allocator,
1715         \\it = iter([1, 2, 3])
1716         \\first = 1 in it
1717         \\second = next(it)
1718         \\third = 3 in it
1719         \\if first and third:
1720         \\    value = second
1721         \\else:
1722         \\    value = 0
1723         \\value
1724     ));
1725     try std.testing.expectEqual(object.Value{ .integer = 9 }, try executeValue(std.testing.allocator,
1726         \\it = iter([1])
1727         \\missing = 2 in it
1728         \\next(it, 9)
1729     ));
1730 }
1731 
1732 test "execute membership errors" {
1733     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "1 in 2"));
1734     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "1 in \"123\""));
1735 }
1736 
1737 test "execute membership in chained comparisons" {
1738     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
1739         \\xs = [1, 2]
1740         \\1 in xs == [1, 2]
1741     ));
1742     try std.testing.expectEqual(object.Value{ .boolean = false }, try executeValue(std.testing.allocator, "1 in [] < missing"));
1743 }
1744 
1745 test "execute if else branches" {
1746     try std.testing.expectEqual(object.Value{ .integer = 7 }, try executeValue(std.testing.allocator,
1747         \\x = 3
1748         \\if x > 5:
1749         \\    y = 1
1750         \\else:
1751         \\    y = 7
1752         \\y
1753     ));
1754 }
1755 
1756 test "execute if without else true branch" {
1757     try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator,
1758         \\x = 1
1759         \\if x:
1760         \\    x = x + 2
1761         \\x
1762     ));
1763 }
1764 
1765 test "execute while loop" {
1766     try std.testing.expectEqual(object.Value{ .integer = 6 }, try executeValue(std.testing.allocator,
1767         \\x = 0
1768         \\sum = 0
1769         \\while x < 4:
1770         \\    sum = sum + x
1771         \\    x = x + 1
1772         \\sum
1773     ));
1774 }
1775 
1776 test "execute control flow inside function" {
1777     try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator,
1778         \\def first_three(limit):
1779         \\    x = 0
1780         \\    while x < limit:
1781         \\        if x == 3:
1782         \\            return x
1783         \\        x = x + 1
1784         \\    return -1
1785         \\first_three(5)
1786     ));
1787 }
1788 
1789 test "execute break" {
1790     try std.testing.expectEqual(object.Value{ .integer = 4 }, try executeValue(std.testing.allocator,
1791         \\x = 0
1792         \\while True:
1793         \\    x = x + 1
1794         \\    if x == 4:
1795         \\        break
1796         \\x
1797     ));
1798 }
1799 
1800 test "execute continue" {
1801     try std.testing.expectEqual(object.Value{ .integer = 12 }, try executeValue(std.testing.allocator,
1802         \\x = 0
1803         \\sum = 0
1804         \\while x < 5:
1805         \\    x = x + 1
1806         \\    if x == 3:
1807         \\        continue
1808         \\    sum = sum + x
1809         \\sum
1810     ));
1811 }
1812 
1813 test "execute loop control inside function" {
1814     try std.testing.expectEqual(object.Value{ .integer = 7 }, try executeValue(std.testing.allocator,
1815         \\def stop_at(limit):
1816         \\    x = 0
1817         \\    while True:
1818         \\        x = x + 1
1819         \\        if x == limit:
1820         \\            break
1821         \\        if x < 3:
1822         \\            continue
1823         \\    return x
1824         \\stop_at(7)
1825     ));
1826 }
1827 
1828 test "execute logical not" {
1829     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "not None"));
1830     try std.testing.expectEqual(object.Value{ .boolean = false }, try executeValue(std.testing.allocator, "not 1 == 1"));
1831 }
1832 
1833 test "execute logical and returns selected operand" {
1834     try std.testing.expectEqual(object.Value{ .integer = 0 }, try executeValue(std.testing.allocator, "0 and missing"));
1835     try std.testing.expectEqual(object.Value{ .integer = 7 }, try executeValue(std.testing.allocator, "1 and 7"));
1836 }
1837 
1838 test "execute logical or returns selected operand" {
1839     try std.testing.expectEqual(object.Value{ .integer = 5 }, try executeValue(std.testing.allocator, "5 or missing"));
1840     try std.testing.expectEqual(object.Value{ .integer = 9 }, try executeValue(std.testing.allocator, "None or 9"));
1841 }
1842 
1843 test "execute logical operators in control flow" {
1844     try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator,
1845         \\x = 0
1846         \\while x < 5:
1847         \\    x = x + 1
1848         \\    if x > 1 and not x == 3:
1849         \\        continue
1850         \\    if x == 3 or False:
1851         \\        break
1852         \\x
1853     ));
1854 }
1855 
1856 test "execute string literals" {
1857     try expectString("alpha", try executeValue(std.testing.allocator, "\"alpha\""));
1858     try expectString("beta", try executeValue(std.testing.allocator, "'beta'"));
1859 }
1860 
1861 test "execute string assignment and return" {
1862     try expectString("value", try executeValue(std.testing.allocator,
1863         \\x = "value"
1864         \\x
1865     ));
1866     try expectString("done", try executeValue(std.testing.allocator,
1867         \\def f():
1868         \\    return "done"
1869         \\f()
1870     ));
1871 }
1872 
1873 test "execute string equality" {
1874     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "\"a\" == 'a'"));
1875     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "\"a\" != \"b\""));
1876 }
1877 
1878 test "execute string truthiness" {
1879     try std.testing.expectEqual(object.Value{ .integer = 1 }, try executeValue(std.testing.allocator,
1880         \\if "":
1881         \\    x = 0
1882         \\else:
1883         \\    x = 1
1884         \\x
1885     ));
1886     try expectString("fallback", try executeValue(std.testing.allocator, "\"\" or \"fallback\""));
1887     try expectString("omega", try executeValue(std.testing.allocator, "\"alpha\" and \"omega\" or \"alpha\""));
1888 }
1889 
1890 test "execute string concatenation and repetition" {
1891     try expectExecutedString("abcd", "\"ab\" + \"cd\"");
1892     try expectExecutedString("ababab", "\"ab\" * 3");
1893     try expectExecutedString("abab", "2 * \"ab\"");
1894     try expectExecutedString("", "\"ab\" * -1");
1895     try expectExecutedString("", "\"ab\" * False");
1896     try expectExecutedString("ab", "\"ab\" * True");
1897     try expectExecutedString("\xc3\xab\xc3\xab", "\"" ++ "\xc3\xab" ++ "\" * 2");
1898 }
1899 
1900 test "execute string ordering" {
1901     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "\"abc\" < \"abd\""));
1902     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "\"abc\" <= \"abc\""));
1903     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "\"abd\" > \"abc\""));
1904     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "\"" ++ "\xc3\xa9" ++ "\" > \"z\""));
1905 }
1906 
1907 test "execute tuple literals" {
1908     {
1909         var result = try execute(std.testing.allocator, "()");
1910         defer result.deinit();
1911         try std.testing.expect(result.value == .tuple);
1912         try std.testing.expectEqual(@as(usize, 0), result.value.tuple.items.len);
1913     }
1914     {
1915         var result = try execute(std.testing.allocator, "(1,)");
1916         defer result.deinit();
1917         try std.testing.expect(result.value == .tuple);
1918         try std.testing.expectEqual(@as(usize, 1), result.value.tuple.items.len);
1919         try std.testing.expectEqual(object.Value{ .integer = 1 }, result.value.tuple.items[0]);
1920     }
1921     {
1922         var result = try execute(std.testing.allocator, "1, 2");
1923         defer result.deinit();
1924         try std.testing.expect(result.value == .tuple);
1925         try std.testing.expectEqual(@as(usize, 2), result.value.tuple.items.len);
1926         try std.testing.expectEqual(object.Value{ .integer = 2 }, result.value.tuple.items[1]);
1927     }
1928 }
1929 
1930 test "execute tuple indexing truthiness and equality" {
1931     try std.testing.expectEqual(object.Value{ .integer = 2 }, try executeValue(std.testing.allocator, "(1, 2, 3)[1]"));
1932     try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator, "(1, 2, 3)[-1]"));
1933     try std.testing.expectEqual(object.Value{ .integer = 2 }, try executeValue(std.testing.allocator,
1934         \\if ():
1935         \\    x = 1
1936         \\else:
1937         \\    x = 2
1938         \\x
1939     ));
1940     try std.testing.expectEqual(object.Value{ .integer = 1 }, try executeValue(std.testing.allocator,
1941         \\if (0,):
1942         \\    x = 1
1943         \\else:
1944         \\    x = 2
1945         \\x
1946     ));
1947     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "(1, 2) == (1, 2)"));
1948     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "(1, 2) != (1, 3)"));
1949 }
1950 
1951 test "execute tuple concatenation and repetition" {
1952     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "(1,) + (2, 3) == (1, 2, 3)"));
1953     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "(1, 2) * 2 == (1, 2, 1, 2)"));
1954     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "2 * (1,) == (1, 1)"));
1955     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "(1,) * -1 == ()"));
1956 }
1957 
1958 test "execute tuple ordering" {
1959     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "(1, 2) < (1, 3)"));
1960     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "(1, 2) <= (1, 2)"));
1961     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "(1, 3) > (1, 2)"));
1962     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "(1, 2) < (1, 2, 0)"));
1963     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "((1,),) < ((2,),)"));
1964 }
1965 
1966 test "execute tuple indexing and assignment errors" {
1967     try std.testing.expectError(Error.IndexError, executeValue(std.testing.allocator, "(1,)[1]"));
1968     try std.testing.expectError(Error.IndexError, executeValue(std.testing.allocator, "(1,)[-2]"));
1969     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "(1,)[None]"));
1970     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,
1971         \\xs = (1, 2)
1972         \\xs[0] = 3
1973     ));
1974 }
1975 
1976 test "execute list displays" {
1977     var result = try execute(std.testing.allocator, "[1, 2, 3]");
1978     defer result.deinit();
1979 
1980     try std.testing.expect(result.value == .list);
1981     try std.testing.expectEqual(@as(usize, 3), result.value.list.items.len);
1982     try std.testing.expectEqual(object.Value{ .integer = 1 }, result.value.list.items[0]);
1983     try std.testing.expectEqual(object.Value{ .integer = 3 }, result.value.list.items[2]);
1984 }
1985 
1986 test "execute list indexing" {
1987     try std.testing.expectEqual(object.Value{ .integer = 2 }, try executeValue(std.testing.allocator, "[1, 2, 3][1]"));
1988     try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator, "[1, 2, 3][-1]"));
1989     try std.testing.expectEqual(object.Value{ .integer = 7 }, try executeValue(std.testing.allocator, "[[7]][0][0]"));
1990 }
1991 
1992 test "execute list truthiness and equality" {
1993     try std.testing.expectEqual(object.Value{ .integer = 2 }, try executeValue(std.testing.allocator,
1994         \\if []:
1995         \\    x = 1
1996         \\else:
1997         \\    x = 2
1998         \\x
1999     ));
2000     try std.testing.expectEqual(object.Value{ .integer = 1 }, try executeValue(std.testing.allocator,
2001         \\if [0]:
2002         \\    x = 1
2003         \\else:
2004         \\    x = 2
2005         \\x
2006     ));
2007     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1, 2] == [1, 2]"));
2008     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1, 2] != [1, 3]"));
2009 }
2010 
2011 test "execute self-containing list equality" {
2012     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2013         \\xs = []
2014         \\xs.append(xs)
2015         \\xs == xs
2016     ));
2017     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2018         \\xs = []
2019         \\xs.append((xs,))
2020         \\xs == xs
2021     ));
2022 }
2023 
2024 test "execute list concatenation and repetition" {
2025     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1] + [2, 3] == [1, 2, 3]"));
2026     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1, 2] * 2 == [1, 2, 1, 2]"));
2027     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "2 * [1] == [1, 1]"));
2028     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1] * -1 == []"));
2029     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1] * False == []"));
2030     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1] * True == [1]"));
2031     try std.testing.expectEqual(object.Value{ .integer = 9 }, try executeValue(std.testing.allocator,
2032         \\inner = [1]
2033         \\xs = [inner] * 2
2034         \\xs[0][0] = 9
2035         \\xs[1][0]
2036     ));
2037     try std.testing.expectEqual(object.Value{ .integer = 7 }, try executeValue(std.testing.allocator,
2038         \\inner = [1]
2039         \\xs = [inner] + []
2040         \\xs[0][0] = 7
2041         \\inner[0]
2042     ));
2043 }
2044 
2045 test "execute list ordering" {
2046     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1, 2] < [1, 3]"));
2047     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1, 2] <= [1, 2]"));
2048     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1, 3] > [1, 2]"));
2049     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1, 2] < [1, 2, 0]"));
2050     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[[1]] < [[2]]"));
2051 }
2052 
2053 test "execute list index errors" {
2054     try std.testing.expectError(Error.IndexError, executeValue(std.testing.allocator, "[1][1]"));
2055     try std.testing.expectError(Error.IndexError, executeValue(std.testing.allocator, "[1][-2]"));
2056     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "[1][None]"));
2057 }
2058 
2059 test "execute list subscript assignment" {
2060     try std.testing.expectEqual(object.Value{ .integer = 7 }, try executeValue(std.testing.allocator,
2061         \\xs = [1, 2]
2062         \\xs[0] = 7
2063         \\xs[0]
2064     ));
2065     try std.testing.expectEqual(object.Value{ .integer = 9 }, try executeValue(std.testing.allocator,
2066         \\xs = [1, 2]
2067         \\xs[-1] = 9
2068         \\xs[1]
2069     ));
2070 }
2071 
2072 test "execute nested list subscript assignment" {
2073     try std.testing.expectEqual(object.Value{ .integer = 5 }, try executeValue(std.testing.allocator,
2074         \\xs = [[1]]
2075         \\xs[0][0] = 5
2076         \\xs[0][0]
2077     ));
2078 }
2079 
2080 test "execute list subscript assignment errors" {
2081     try std.testing.expectError(Error.IndexError, executeValue(std.testing.allocator,
2082         \\xs = [1]
2083         \\xs[1] = 2
2084     ));
2085     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,
2086         \\xs = [1]
2087         \\xs[None] = 2
2088     ));
2089     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "1[0] = 2"));
2090 }
2091 
2092 test "execute list item deletion" {
2093     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2094         \\xs = [1, 2, 3]
2095         \\del xs[1]
2096         \\xs == [1, 3]
2097     ));
2098     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2099         \\xs = [1, 2, 3]
2100         \\del xs[-1]
2101         \\xs == [1, 2]
2102     ));
2103     try std.testing.expectEqual(object.Value{ .integer = 12 }, try executeValue(std.testing.allocator,
2104         \\xs = [0, 1, 2]
2105         \\del xs[0]
2106         \\xs[0] * 10 + len(xs)
2107     ));
2108 }
2109 
2110 test "execute list item deletion errors" {
2111     try std.testing.expectError(Error.IndexError, executeValue(std.testing.allocator,
2112         \\xs = [1]
2113         \\del xs[1]
2114     ));
2115     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,
2116         \\xs = [1]
2117         \\del xs[None]
2118     ));
2119     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "del (1,)[0]"));
2120     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "del 1[0]"));
2121 }
2122 
2123 test "execute list append method" {
2124     try std.testing.expectEqual(object.Value{ .integer = 31 }, try executeValue(std.testing.allocator,
2125         \\xs = []
2126         \\result = xs.append(2)
2127         \\xs.append(1)
2128         \\if result is None:
2129         \\    marker = 10
2130         \\else:
2131         \\    marker = 0
2132         \\xs[0] * 10 + xs[1] + marker
2133     ));
2134 }
2135 
2136 test "execute stored bound list methods" {
2137     try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator,
2138         \\xs = []
2139         \\push = xs.append
2140         \\push(3)
2141         \\xs[0]
2142     ));
2143     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2144         \\xs = []
2145         \\first = xs.append
2146         \\second = xs.append
2147         \\first == second and not first is second
2148     ));
2149     try std.testing.expectEqual(object.Value{ .integer = 7 }, try executeValue(std.testing.allocator,
2150         \\xs = []
2151         \\d = {xs.append: 7}
2152         \\d[xs.append]
2153     ));
2154 }
2155 
2156 test "execute list pop method" {
2157     try std.testing.expectEqual(object.Value{ .integer = 131 }, try executeValue(std.testing.allocator,
2158         \\xs = [1, 2, 3]
2159         \\last = xs.pop()
2160         \\first = xs.pop(0)
2161         \\last * 10 + first * 100 + len(xs)
2162     ));
2163     try std.testing.expectEqual(object.Value{ .integer = 21 }, try executeValue(std.testing.allocator,
2164         \\xs = [1, 2]
2165         \\xs.pop(-1) * 10 + len(xs)
2166     ));
2167 }
2168 
2169 test "execute list clear and copy methods" {
2170     try std.testing.expectEqual(object.Value{ .integer = 19 }, try executeValue(std.testing.allocator,
2171         \\xs = [1, 2]
2172         \\ys = xs.copy()
2173         \\ys[0] = 9
2174         \\xs[0] * 10 + ys[0]
2175     ));
2176     try std.testing.expectEqual(object.Value{ .integer = 0 }, try executeValue(std.testing.allocator,
2177         \\xs = [1]
2178         \\result = xs.clear()
2179         \\if result is None:
2180         \\    len(xs)
2181         \\else:
2182         \\    9
2183     ));
2184 
2185     var result = try execute(std.testing.allocator,
2186         \\xs = [0, 1, 2, 3, 4, 5, 6, 7]
2187         \\xs.clear()
2188         \\xs
2189     );
2190     defer result.deinit();
2191     try std.testing.expectEqual(@as(usize, 0), result.value.list.items.len);
2192     try std.testing.expectEqual(@as(usize, 0), result.value.list.capacity);
2193 }
2194 
2195 test "execute list method errors" {
2196     try std.testing.expectError(Error.AttributeError, executeValue(std.testing.allocator, "[1].missing"));
2197     try std.testing.expectError(Error.AttributeError, executeValue(std.testing.allocator, "1.append"));
2198     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "[].append()"));
2199     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "[].append(1, 2)"));
2200     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "[].clear(1)"));
2201     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "[].copy(1)"));
2202     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "[].pop(0, 1)"));
2203     try std.testing.expectError(Error.IndexError, executeValue(std.testing.allocator, "[].pop()"));
2204     try std.testing.expectError(Error.IndexError, executeValue(std.testing.allocator, "[1].pop(1)"));
2205     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "[1].pop(None)"));
2206 }
2207 
2208 test "execute list slices" {
2209     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[0, 1, 2, 3, 4][1:4] == [1, 2, 3]"));
2210     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[0, 1, 2, 3, 4][-4:-1:2] == [1, 3]"));
2211     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[0, 1, 2, 3][::-1] == [3, 2, 1, 0]"));
2212     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1, 2][None:None:None] == [1, 2]"));
2213     try std.testing.expectEqual(object.Value{ .integer = 193 }, try executeValue(std.testing.allocator,
2214         \\xs = [0, 1, 2, 3, 4]
2215         \\ys = xs[1:4]
2216         \\ys[0] = 9
2217         \\xs[1] * 100 + ys[0] * 10 + len(ys)
2218     ));
2219 }
2220 
2221 test "execute tuple slices" {
2222     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "(0, 1, 2, 3)[1:3] == (1, 2)"));
2223     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "(0, 1, 2, 3)[::-1] == (3, 2, 1, 0)"));
2224     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "(0, 1, 2, 3)[10:] == ()"));
2225 }
2226 
2227 test "execute range slices" {
2228     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "list(range(10)[2:8:2]) == [2, 4, 6]"));
2229     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "list(range(9, 0, -2)[1:3]) == [7, 5]"));
2230     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "list(range(10)[::-3]) == [9, 6, 3, 0]"));
2231     try std.testing.expectEqual(object.Value{ .integer = 0 }, try executeValue(std.testing.allocator, "len(range(10)[20:])"));
2232 }
2233 
2234 test "execute string slices" {
2235     {
2236         var result = try execute(std.testing.allocator, "\"abcd\"[1:3]");
2237         defer result.deinit();
2238         try expectString("bc", result.value);
2239     }
2240     {
2241         var result = try execute(std.testing.allocator, "\"abcd\"[::2]");
2242         defer result.deinit();
2243         try expectString("ac", result.value);
2244     }
2245     {
2246         var result = try execute(std.testing.allocator, "\"abcd\"[::-1]");
2247         defer result.deinit();
2248         try expectString("dcba", result.value);
2249     }
2250     {
2251         var result = try execute(std.testing.allocator, "\"no" ++ "\xc3\xabl" ++ "\"[2:3]");
2252         defer result.deinit();
2253         try expectString("\xc3\xab", result.value);
2254     }
2255 }
2256 
2257 test "execute slice errors" {
2258     try std.testing.expectError(Error.ValueError, executeValue(std.testing.allocator, "[1][::0]"));
2259     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "[1][\"a\":]"));
2260     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "1[0:1]"));
2261 }
2262 
2263 test "execute sequence operator errors" {
2264     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "[1] + (2,)"));
2265     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "\"a\" + 1"));
2266     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "[1] * None"));
2267     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "None * [1]"));
2268     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "range(3) + range(3)"));
2269     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "range(3) * 2"));
2270 }
2271 
2272 test "execute sequence ordering errors" {
2273     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "[1] < (1,)"));
2274     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "\"a\" < 1"));
2275     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "[None] < [0]"));
2276     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "range(3) < range(4)"));
2277     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "[range(1)] < [range(2)]"));
2278 }
2279 
2280 test "execute for loop over list" {
2281     try std.testing.expectEqual(object.Value{ .integer = 6 }, try executeValue(std.testing.allocator,
2282         \\total = 0
2283         \\for x in [1, 2, 3]:
2284         \\    total = total + x
2285         \\total
2286     ));
2287 }
2288 
2289 test "execute for loop variable persists" {
2290     try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator,
2291         \\for x in [1, 2, 3]:
2292         \\    pass
2293         \\x
2294     ));
2295 }
2296 
2297 test "execute empty for loop leaves target unassigned" {
2298     try std.testing.expectError(Error.UndefinedName, executeValue(std.testing.allocator,
2299         \\for x in []:
2300         \\    pass
2301         \\x
2302     ));
2303 }
2304 
2305 test "execute for else on natural exhaustion" {
2306     try expectString("done", try executeValue(std.testing.allocator,
2307         \\for x in [1, 2]:
2308         \\    y = "body"
2309         \\else:
2310         \\    y = "done"
2311         \\y
2312     ));
2313 }
2314 
2315 test "execute for else skipped by break" {
2316     try expectString("break", try executeValue(std.testing.allocator,
2317         \\y = "start"
2318         \\for x in [1, 2, 3]:
2319         \\    if x == 2:
2320         \\        y = "break"
2321         \\        break
2322         \\else:
2323         \\    y = "else"
2324         \\y
2325     ));
2326 }
2327 
2328 test "execute for else after continue" {
2329     try expectString("done", try executeValue(std.testing.allocator,
2330         \\for x in [1, 2, 3]:
2331         \\    if x < 3:
2332         \\        continue
2333         \\else:
2334         \\    y = "done"
2335         \\y
2336     ));
2337 }
2338 
2339 test "execute return from for loop" {
2340     try std.testing.expectEqual(object.Value{ .integer = 5 }, try executeValue(std.testing.allocator,
2341         \\def first():
2342         \\    for x in [1, 2]:
2343         \\        return x
2344         \\    return 9
2345         \\first() + 4
2346     ));
2347 }
2348 
2349 test "execute rejects non-iterable for loop" {
2350     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,
2351         \\for x in 1:
2352         \\    pass
2353     ));
2354 }
2355 
2356 test "execute range builtin creates range objects" {
2357     var result = try execute(std.testing.allocator, "range(1, 6, 2)");
2358     defer result.deinit();
2359 
2360     try std.testing.expect(result.value == .range);
2361     try std.testing.expectEqual(@as(i128, 1), result.value.range.start);
2362     try std.testing.expectEqual(@as(i128, 6), result.value.range.stop);
2363     try std.testing.expectEqual(@as(i128, 2), result.value.range.step);
2364     try std.testing.expectEqual(@as(usize, 3), result.value.range.length);
2365 }
2366 
2367 test "execute for loop over range" {
2368     try std.testing.expectEqual(object.Value{ .integer = 6 }, try executeValue(std.testing.allocator,
2369         \\total = 0
2370         \\for x in range(4):
2371         \\    total = total + x
2372         \\total
2373     ));
2374     try std.testing.expectEqual(object.Value{ .integer = 9 }, try executeValue(std.testing.allocator,
2375         \\total = 0
2376         \\for x in range(1, 6, 2):
2377         \\    total = total + x
2378         \\total
2379     ));
2380     try std.testing.expectEqual(object.Value{ .integer = 9 }, try executeValue(std.testing.allocator,
2381         \\total = 0
2382         \\for x in range(5, 0, -2):
2383         \\    total = total + x
2384         \\total
2385     ));
2386 }
2387 
2388 test "execute range loop supports else and empty target behavior" {
2389     try expectString("done", try executeValue(std.testing.allocator,
2390         \\for x in range(0):
2391         \\    y = "body"
2392         \\else:
2393         \\    y = "done"
2394         \\y
2395     ));
2396     try std.testing.expectError(Error.UndefinedName, executeValue(std.testing.allocator,
2397         \\for x in range(0):
2398         \\    pass
2399         \\x
2400     ));
2401 }
2402 
2403 test "execute range truthiness and equality" {
2404     try std.testing.expectEqual(object.Value{ .integer = 2 }, try executeValue(std.testing.allocator,
2405         \\if range(0):
2406         \\    x = 1
2407         \\else:
2408         \\    x = 2
2409         \\x
2410     ));
2411     try std.testing.expectEqual(object.Value{ .integer = 1 }, try executeValue(std.testing.allocator,
2412         \\if range(1):
2413         \\    x = 1
2414         \\else:
2415         \\    x = 2
2416         \\x
2417     ));
2418     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "range(0, 3, 2) == range(0, 4, 2)"));
2419     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "range(0) == range(1, 1, 3)"));
2420     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "range(0, 5, 2) != range(0, 4, 2)"));
2421 }
2422 
2423 test "execute range builtin errors" {
2424     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "range()"));
2425     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "range(1, 2, 3, 4)"));
2426     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "range(None)"));
2427     try std.testing.expectError(Error.ValueError, executeValue(std.testing.allocator, "range(1, 2, 0)"));
2428 }
2429 
2430 test "execute globals shadow range builtin" {
2431     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,
2432         \\range = 3
2433         \\range(1)
2434     ));
2435 }
2436 
2437 test "execute len builtin for supported sequences" {
2438     try std.testing.expectEqual(object.Value{ .integer = 0 }, try executeValue(std.testing.allocator, "len([])"));
2439     try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator, "len([1, 2, 3])"));
2440     try std.testing.expectEqual(object.Value{ .integer = 2 }, try executeValue(std.testing.allocator, "len((1, 2))"));
2441     try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator, "len(range(1, 6, 2))"));
2442     try std.testing.expectEqual(object.Value{ .integer = 4 }, try executeValue(std.testing.allocator, "len(\"abcd\")"));
2443     try std.testing.expectEqual(object.Value{ .integer = 4 }, try executeValue(std.testing.allocator, "len(\"no" ++ "\xc3\xabl" ++ "\")"));
2444 }
2445 
2446 test "execute len builtin errors and shadowing" {
2447     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "len()"));
2448     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "len([], [])"));
2449     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "len(1)"));
2450     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,
2451         \\len = 4
2452         \\len([])
2453     ));
2454 }
2455 
2456 test "execute list builtin creates lists" {
2457     {
2458         var result = try execute(std.testing.allocator, "list()");
2459         defer result.deinit();
2460         try std.testing.expect(result.value == .list);
2461         try std.testing.expectEqual(@as(usize, 0), result.value.list.items.len);
2462     }
2463     {
2464         var result = try execute(std.testing.allocator, "list(range(1, 6, 2))");
2465         defer result.deinit();
2466         try std.testing.expect(result.value == .list);
2467         try std.testing.expectEqual(@as(usize, 3), result.value.list.items.len);
2468         try std.testing.expectEqual(object.Value{ .integer = 1 }, result.value.list.items[0]);
2469         try std.testing.expectEqual(object.Value{ .integer = 3 }, result.value.list.items[1]);
2470         try std.testing.expectEqual(object.Value{ .integer = 5 }, result.value.list.items[2]);
2471     }
2472     {
2473         var result = try execute(std.testing.allocator, "list(\"ab\")");
2474         defer result.deinit();
2475         try std.testing.expect(result.value == .list);
2476         try std.testing.expectEqual(@as(usize, 2), result.value.list.items.len);
2477         try expectString("a", result.value.list.items[0]);
2478         try expectString("b", result.value.list.items[1]);
2479     }
2480     {
2481         var result = try execute(std.testing.allocator, "list((1, 2))");
2482         defer result.deinit();
2483         try std.testing.expect(result.value == .list);
2484         try std.testing.expectEqual(@as(usize, 2), result.value.list.items.len);
2485         try std.testing.expectEqual(object.Value{ .integer = 1 }, result.value.list.items[0]);
2486         try std.testing.expectEqual(object.Value{ .integer = 2 }, result.value.list.items[1]);
2487     }
2488 }
2489 
2490 test "execute list builtin copies list values" {
2491     try std.testing.expectEqual(object.Value{ .integer = 17 }, try executeValue(std.testing.allocator,
2492         \\xs = [1, 2]
2493         \\ys = list(xs)
2494         \\ys[0] = 7
2495         \\xs[0] * 10 + ys[0]
2496     ));
2497 }
2498 
2499 test "execute list builtin errors and shadowing" {
2500     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "list(1, 2)"));
2501     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "list(1)"));
2502     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,
2503         \\list = 4
2504         \\list()
2505     ));
2506 }
2507 
2508 test "execute iter and next builtins over ranges and lists" {
2509     try std.testing.expectEqual(object.Value{ .integer = 12 }, try executeValue(std.testing.allocator,
2510         \\it = iter(range(1, 4))
2511         \\a = next(it)
2512         \\b = next(it)
2513         \\a * 10 + b
2514     ));
2515     try std.testing.expectEqual(object.Value{ .integer = 12 }, try executeValue(std.testing.allocator,
2516         \\it = iter([1, 2])
2517         \\same = iter(it)
2518         \\next(same) * 10 + next(it)
2519     ));
2520 }
2521 
2522 test "execute next builtin exhaustion" {
2523     try std.testing.expectError(Error.StopIteration, executeValue(std.testing.allocator, "next(iter([]))"));
2524     try std.testing.expectEqual(object.Value{ .integer = 9 }, try executeValue(std.testing.allocator, "next(iter([]), 9)"));
2525     try std.testing.expectEqual(object.Value.none, try executeValue(std.testing.allocator, "next(iter([]), None)"));
2526 }
2527 
2528 test "execute iter and next builtin errors and shadowing" {
2529     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "iter()"));
2530     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "iter([], None)"));
2531     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "iter(1)"));
2532     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "next()"));
2533     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "next(iter([]), 1, 2)"));
2534     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "next([])"));
2535     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,
2536         \\iter = 4
2537         \\iter([])
2538     ));
2539     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,
2540         \\next = 4
2541         \\next(iter([]))
2542     ));
2543 }
2544 
2545 test "execute for loop over iterators and strings" {
2546     try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator,
2547         \\total = 0
2548         \\it = iter(range(3))
2549         \\for x in it:
2550         \\    total = total + x
2551         \\total
2552     ));
2553     try std.testing.expectEqual(object.Value{ .integer = 2 }, try executeValue(std.testing.allocator,
2554         \\total = 0
2555         \\for ch in "ab":
2556         \\    total = total + len(ch)
2557         \\total
2558     ));
2559 }
2560 
2561 test "execute for loop over tuples" {
2562     try std.testing.expectEqual(object.Value{ .integer = 6 }, try executeValue(std.testing.allocator,
2563         \\total = 0
2564         \\for x in (1, 2, 3):
2565         \\    total = total + x
2566         \\total
2567     ));
2568     try std.testing.expectEqual(object.Value{ .integer = 12 }, try executeValue(std.testing.allocator,
2569         \\it = iter((1, 2))
2570         \\next(it) * 10 + next(it)
2571     ));
2572 }
2573 
2574 test "execute string iterator yields codepoint slices" {
2575     try expectString("a", try executeValue(std.testing.allocator, "next(iter(\"ab\"))"));
2576     try expectString("\xc3\xab", try executeValue(std.testing.allocator, "next(iter(\"" ++ "\xc3\xab" ++ "\"))"));
2577 }
2578 
2579 test "execute enumerate builtin over iterable values" {
2580     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "list(enumerate([\"a\", \"b\"])) == [(0, \"a\"), (1, \"b\")]"));
2581     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "tuple(enumerate((3, 4), -1)) == ((-1, 3), (0, 4))"));
2582     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "list(enumerate(\"a" ++ "\xc3\xab" ++ "\")) == [(0, \"a\"), (1, \"" ++ "\xc3\xab" ++ "\")]"));
2583     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "list(enumerate({\"a\": 1, \"b\": 2}.items(), True)) == [(1, (\"a\", 1)), (2, (\"b\", 2))]"));
2584 }
2585 
2586 test "execute enumerate builtin consumes iterators with next and for loops" {
2587     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2588         \\it = iter([10, 20, 30])
2589         \\first = next(it)
2590         \\first == 10 and list(enumerate(it, 5)) == [(5, 20), (6, 30)]
2591     ));
2592     try std.testing.expectEqual(object.Value{ .integer = 468 }, try executeValue(std.testing.allocator,
2593         \\total = 0
2594         \\for pair in enumerate(range(3), 4):
2595         \\    total = total * 10 + pair[0] + pair[1]
2596         \\total
2597     ));
2598     try std.testing.expectEqual(object.Value{ .integer = 52 }, try executeValue(std.testing.allocator,
2599         \\pair = next(enumerate(reversed([1, 2]), 5))
2600         \\pair[0] * 10 + pair[1]
2601     ));
2602 }
2603 
2604 test "execute enumerate builtin errors and shadowing" {
2605     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "enumerate()"));
2606     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "enumerate([], 1, 2)"));
2607     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "enumerate(1)"));
2608     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "enumerate([], None)"));
2609     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,
2610         \\enumerate = 4
2611         \\enumerate([])
2612     ));
2613 }
2614 
2615 test "execute reversed builtin over sequences" {
2616     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "list(reversed([1, 2, 3])) == [3, 2, 1]"));
2617     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "tuple(reversed((1, 2, 3))) == (3, 2, 1)"));
2618     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "list(reversed(range(1, 6, 2))) == [5, 3, 1]"));
2619     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "list(reversed(\"a" ++ "\xc3\xab" ++ "\")) == [\"" ++ "\xc3\xab" ++ "\", \"a\"]"));
2620 }
2621 
2622 test "execute reversed builtin over dictionaries and views" {
2623     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2624         \\d = {"a": 1, "b": 2}
2625         \\list(reversed(d)) == ["b", "a"]
2626     ));
2627     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2628         \\d = {"a": 1, "b": 2}
2629         \\list(reversed(d.keys())) == ["b", "a"]
2630     ));
2631     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2632         \\d = {"a": 1, "b": 2}
2633         \\list(reversed(d.values())) == [2, 1]
2634     ));
2635     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2636         \\d = {"a": 1, "b": 2}
2637         \\list(reversed(d.items())) == [("b", 2), ("a", 1)]
2638     ));
2639 }
2640 
2641 test "execute reversed builtin consumes with next and for loops" {
2642     try std.testing.expectEqual(object.Value{ .integer = 21 }, try executeValue(std.testing.allocator,
2643         \\it = reversed([1, 2])
2644         \\next(it) * 10 + next(it)
2645     ));
2646     try std.testing.expectEqual(object.Value{ .integer = 210 }, try executeValue(std.testing.allocator,
2647         \\total = 0
2648         \\for value in reversed(range(3)):
2649         \\    total = total * 10 + value
2650         \\total
2651     ));
2652 }
2653 
2654 test "execute reversed builtin errors and shadowing" {
2655     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "reversed()"));
2656     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "reversed([], [])"));
2657     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "reversed(1)"));
2658     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "reversed(iter([1]))"));
2659     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,
2660         \\reversed = 4
2661         \\reversed([])
2662     ));
2663 }
2664 
2665 test "execute list builtin consumes iterators" {
2666     try std.testing.expectEqual(object.Value{ .integer = 13 }, try executeValue(std.testing.allocator,
2667         \\it = iter(range(4))
2668         \\first = next(it)
2669         \\xs = list(it)
2670         \\first * 100 + xs[0] * 10 + len(xs)
2671     ));
2672 }
2673 
2674 test "execute dictionary displays" {
2675     {
2676         var result = try execute(std.testing.allocator, "{}");
2677         defer result.deinit();
2678         try std.testing.expect(result.value == .dict);
2679         try std.testing.expectEqual(@as(usize, 0), result.value.dict.entries.items.len);
2680     }
2681     {
2682         var result = try execute(std.testing.allocator, "{\"a\": 1, \"b\": 2}");
2683         defer result.deinit();
2684         try std.testing.expect(result.value == .dict);
2685         try std.testing.expectEqual(@as(usize, 2), result.value.dict.entries.items.len);
2686         try expectString("a", result.value.dict.entries.items[0].key);
2687         try std.testing.expectEqual(object.Value{ .integer = 1 }, result.value.dict.entries.items[0].value);
2688         try expectString("b", result.value.dict.entries.items[1].key);
2689         try std.testing.expectEqual(object.Value{ .integer = 2 }, result.value.dict.entries.items[1].value);
2690     }
2691 }
2692 
2693 test "execute dictionary duplicate keys replace values" {
2694     try std.testing.expectEqual(object.Value{ .integer = 12 }, try executeValue(std.testing.allocator,
2695         \\d = {"a": 1, "a": 2}
2696         \\len(d) * 10 + d["a"]
2697     ));
2698     try std.testing.expectEqual(object.Value{ .integer = 12 }, try executeValue(std.testing.allocator,
2699         \\d = {True: 1, 1: 2}
2700         \\len(d) * 10 + d[True]
2701     ));
2702 }
2703 
2704 test "execute dictionary lookup assignment truthiness and equality" {
2705     try std.testing.expectEqual(object.Value{ .integer = 2 }, try executeValue(std.testing.allocator, "{\"a\": 2}[\"a\"]"));
2706     try std.testing.expectEqual(object.Value{ .integer = 12 }, try executeValue(std.testing.allocator,
2707         \\d = {}
2708         \\if d:
2709         \\    empty = 1
2710         \\else:
2711         \\    empty = 2
2712         \\d["a"] = 7
2713         \\if d:
2714         \\    full = 1
2715         \\else:
2716         \\    full = 2
2717         \\full * 10 + empty
2718     ));
2719     try std.testing.expectEqual(object.Value{ .integer = 37 }, try executeValue(std.testing.allocator,
2720         \\d = {"a": 1, "b": 2}
2721         \\d["a"] = 3
2722         \\keys = list(d)
2723         \\if keys == ["a", "b"]:
2724         \\    order = 30
2725         \\else:
2726         \\    order = 0
2727         \\order + d["a"] + d["b"] * 2
2728     ));
2729     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "{\"a\": 1, \"b\": 2} == {\"b\": 2, \"a\": 1}"));
2730     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "{\"a\": 1} != {\"a\": 2}"));
2731 }
2732 
2733 test "execute dictionary iteration membership and builtins" {
2734     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "\"a\" in {\"a\": 1}"));
2735     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "1 not in {\"a\": 1}"));
2736     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "list({\"a\": 1, \"b\": 2}) == [\"a\", \"b\"]"));
2737     try expectString("a", try executeValue(std.testing.allocator, "next(iter({\"a\": 1}))"));
2738     try std.testing.expectEqual(object.Value{ .integer = 2 }, try executeValue(std.testing.allocator,
2739         \\total = 0
2740         \\for key in {"a": 1, "b": 2}:
2741         \\    total = total + len(key)
2742         \\total
2743     ));
2744     try std.testing.expectEqual(object.Value{ .integer = 7 }, try executeValue(std.testing.allocator,
2745         \\d = {"a": 1}
2746         \\copy = dict(d)
2747         \\d["a"] = 7
2748         \\copy["a"] + d["a"] - 1
2749     ));
2750     try std.testing.expectEqual(object.Value{ .integer = 12 }, try executeValue(std.testing.allocator,
2751         \\d = dict([("a", 1), ["b", 2]])
2752         \\d["a"] * 10 + d["b"]
2753     ));
2754     try std.testing.expectEqual(object.Value{ .integer = 0 }, try executeValue(std.testing.allocator, "len(dict())"));
2755 }
2756 
2757 test "execute dictionary key errors" {
2758     try std.testing.expectError(Error.KeyError, executeValue(std.testing.allocator, "{\"a\": 1}[\"b\"]"));
2759     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "{[]: 1}"));
2760     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "{([],): 1}"));
2761     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,
2762         \\d = {}
2763         \\d[[]] = 1
2764     ));
2765     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "[] in {}"));
2766 }
2767 
2768 test "execute dictionary key deletion" {
2769     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2770         \\d = {"a": 1, "b": 2, "c": 3}
2771         \\del d["b"]
2772         \\list(d) == ["a", "c"]
2773     ));
2774     try std.testing.expectEqual(object.Value{ .integer = 31 }, try executeValue(std.testing.allocator,
2775         \\d = {"a": 1, "b": 2}
2776         \\del d["a"]
2777         \\len(d) * 10 + d["b"] * 10 + ("a" not in d)
2778     ));
2779     try std.testing.expectEqual(object.Value{ .integer = 0 }, try executeValue(std.testing.allocator,
2780         \\d = {True: 1, 1: 2}
2781         \\del d[True]
2782         \\len(d)
2783     ));
2784 }
2785 
2786 test "execute dictionary key deletion errors" {
2787     try std.testing.expectError(Error.KeyError, executeValue(std.testing.allocator,
2788         \\d = {"a": 1}
2789         \\del d["b"]
2790     ));
2791     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,
2792         \\d = {}
2793         \\del d[[]]
2794     ));
2795 }
2796 
2797 test "execute dictionary get method" {
2798     try std.testing.expectEqual(object.Value{ .integer = 121 }, try executeValue(std.testing.allocator,
2799         \\d = {"a": 1}
2800         \\d.get("a") * 100 + d.get("b", 2) * 10 + (d.get("b") is None)
2801     ));
2802 }
2803 
2804 test "execute stored bound dictionary methods" {
2805     try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator,
2806         \\d = {}
2807         \\get = d.get
2808         \\d["a"] = 3
2809         \\get("a")
2810     ));
2811     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2812         \\d = {}
2813         \\first = d.get
2814         \\second = d.get
2815         \\first == second and not first is second
2816     ));
2817     try std.testing.expectEqual(object.Value{ .integer = 7 }, try executeValue(std.testing.allocator,
2818         \\d = {}
2819         \\outer = {d.get: 7}
2820         \\outer[d.get]
2821     ));
2822     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2823         \\d = {}
2824         \\update = d.update
2825         \\setdefault = d.setdefault
2826         \\popitem = d.popitem
2827         \\update([("a", 1)])
2828         \\setdefault("b", 2)
2829         \\popitem() == ("b", 2) and d.update == d.update and d.setdefault == d.setdefault and d.popitem == d.popitem
2830     ));
2831 }
2832 
2833 test "execute dictionary pop method" {
2834     try std.testing.expectEqual(object.Value{ .integer = 124 }, try executeValue(std.testing.allocator,
2835         \\d = {"a": 1, "b": 2, "c": 3}
2836         \\value = d.pop("b")
2837         \\missing = d.pop("z", 4)
2838         \\keys = list(d)
2839         \\if keys == ["a", "c"]:
2840         \\    order = 100
2841         \\else:
2842         \\    order = 0
2843         \\order + value * 10 + missing
2844     ));
2845 }
2846 
2847 test "execute dictionary setdefault method" {
2848     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2849         \\d = {"a": 1}
2850         \\first = d.setdefault("a", 9)
2851         \\second = d.setdefault("b", 2)
2852         \\third = d.setdefault("c")
2853         \\first == 1 and second == 2 and third is None and d["a"] == 1 and d["b"] == 2 and d["c"] is None and list(d) == ["a", "b", "c"]
2854     ));
2855 }
2856 
2857 test "execute dictionary update method" {
2858     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2859         \\d = {"a": 1, "b": 2}
2860         \\result = d.update({"b": 20, "c": 3})
2861         \\result is None and list(d.items()) == [("a", 1), ("b", 20), ("c", 3)]
2862     ));
2863     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2864         \\d = {"a": 1}
2865         \\d.update([("a", 10), ["b", 2]])
2866         \\d.update({"c": 3}.items())
2867         \\d.update(iter([("d", 4)]))
2868         \\list(d.items()) == [("a", 10), ("b", 2), ("c", 3), ("d", 4)]
2869     ));
2870     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2871         \\d = {"a": 1}
2872         \\result = d.update()
2873         \\result is None and list(d.items()) == [("a", 1)]
2874     ));
2875 }
2876 
2877 test "execute dictionary popitem method" {
2878     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2879         \\d = {"a": 1, "b": 2, "c": 3}
2880         \\first = d.popitem()
2881         \\second = d.popitem()
2882         \\first == ("c", 3) and second == ("b", 2) and list(d.items()) == [("a", 1)]
2883     ));
2884 }
2885 
2886 test "execute dictionary clear and copy methods" {
2887     try std.testing.expectEqual(object.Value{ .integer = 17 }, try executeValue(std.testing.allocator,
2888         \\d = {"a": 1}
2889         \\copy = d.copy()
2890         \\d["a"] = 7
2891         \\copy["a"] * 10 + d["a"]
2892     ));
2893     try std.testing.expectEqual(object.Value{ .integer = 5 }, try executeValue(std.testing.allocator,
2894         \\inner = []
2895         \\d = {"a": inner}
2896         \\copy = d.copy()
2897         \\copy["a"].append(5)
2898         \\d["a"][0]
2899     ));
2900     try std.testing.expectEqual(object.Value{ .integer = 0 }, try executeValue(std.testing.allocator,
2901         \\d = {"a": 1}
2902         \\result = d.clear()
2903         \\if result is None:
2904         \\    len(d)
2905         \\else:
2906         \\    9
2907     ));
2908 }
2909 
2910 test "execute dictionary method errors" {
2911     try std.testing.expectError(Error.AttributeError, executeValue(std.testing.allocator, "{}.missing"));
2912     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.get()"));
2913     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.get(1, 2, 3)"));
2914     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "{}.get([])"));
2915     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "{}.get([], 1)"));
2916     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.pop()"));
2917     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.pop(1, 2, 3)"));
2918     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "{}.pop([])"));
2919     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "{}.pop([], 1)"));
2920     try std.testing.expectError(Error.KeyError, executeValue(std.testing.allocator, "{}.pop(\"a\")"));
2921     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.setdefault()"));
2922     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.setdefault(1, 2, 3)"));
2923     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "{}.setdefault([])"));
2924     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.update(1, 2)"));
2925     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "{}.update(1)"));
2926     try std.testing.expectError(Error.ValueError, executeValue(std.testing.allocator, "{}.update([[1]])"));
2927     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "{}.update([1])"));
2928     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "{}.update([([], 1)])"));
2929     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.popitem(1)"));
2930     try std.testing.expectError(Error.KeyError, executeValue(std.testing.allocator, "{}.popitem()"));
2931     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.clear(1)"));
2932     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.copy(1)"));
2933 }
2934 
2935 test "execute dictionary view iteration and length" {
2936     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2937         \\d = {"a": 1, "b": 2}
2938         \\list(d.keys()) == ["a", "b"] and list(d.values()) == [1, 2] and list(d.items()) == [("a", 1), ("b", 2)]
2939     ));
2940     try std.testing.expectEqual(object.Value{ .integer = 222 }, try executeValue(std.testing.allocator,
2941         \\d = {"a": 1, "b": 2}
2942         \\len(d.keys()) * 100 + len(d.values()) * 10 + len(d.items())
2943     ));
2944     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2945         \\tuple({"a": 1}.items()) == (("a", 1),)
2946     ));
2947     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2948         \\d = {"a": 1}
2949         \\dict(d.items()) == d
2950     ));
2951 }
2952 
2953 test "execute dictionary views reflect mutation" {
2954     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2955         \\d = {"a": 1}
2956         \\keys = d.keys()
2957         \\values = d.values()
2958         \\items = d.items()
2959         \\d["b"] = 2
2960         \\list(keys) == ["a", "b"] and list(values) == [1, 2] and list(items) == [("a", 1), ("b", 2)]
2961     ));
2962 }
2963 
2964 test "execute dictionary views in loops and membership" {
2965     try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator,
2966         \\total = 0
2967         \\for value in {"a": 1, "b": 2}.values():
2968         \\    total = total + value
2969         \\total
2970     ));
2971     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2972         \\d = {"a": 1, "b": 2}
2973         \\"a" in d.keys() and 2 in d.values() and ("a", 1) in d.items() and ("a", 2) not in d.items() and ["a", 1] not in d.items()
2974     ));
2975 }
2976 
2977 test "execute dictionary view equality and truthiness" {
2978     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2979         \\{"a": 1, "b": 2}.keys() == {"b": 9, "a": 8}.keys()
2980     ));
2981     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2982         \\{"a": 1}.items() == {"a": 1}.items()
2983     ));
2984     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
2985         \\d = {"a": 1}
2986         \\values = d.values()
2987         \\values == values and d.values() != d.values()
2988     ));
2989     try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator,
2990         \\if {}.keys():
2991         \\    total = 9
2992         \\else:
2993         \\    total = 1
2994         \\if {"a": 1}.values():
2995         \\    total = total + 2
2996         \\total
2997     ));
2998 }
2999 
3000 test "execute stored bound dictionary view methods" {
3001     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
3002         \\d = {"a": 1}
3003         \\keys = d.keys
3004         \\list(keys()) == ["a"]
3005     ));
3006 }
3007 
3008 test "execute dictionary view errors" {
3009     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.keys(1)"));
3010     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.values(1)"));
3011     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.items(1)"));
3012     try std.testing.expectError(Error.AttributeError, executeValue(std.testing.allocator, "{}.keys().missing"));
3013     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,
3014         \\d = {}
3015         \\outer = {d.keys(): 1}
3016     ));
3017     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "([], 1) in {\"a\": 1}.items()"));
3018 }
3019 
3020 test "execute dictionary builtin errors and ordering errors" {
3021     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "dict(1, 2)"));
3022     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "dict(1)"));
3023     try std.testing.expectError(Error.ValueError, executeValue(std.testing.allocator, "dict([[1]])"));
3024     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "dict([1])"));
3025     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,
3026         \\dict = 4
3027         \\dict()
3028     ));
3029     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "{} < {}"));
3030     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[{}] <= [{}]"));
3031 }
3032 
3033 test "execute tuple builtin creates tuples" {
3034     {
3035         var result = try execute(std.testing.allocator, "tuple()");
3036         defer result.deinit();
3037         try std.testing.expect(result.value == .tuple);
3038         try std.testing.expectEqual(@as(usize, 0), result.value.tuple.items.len);
3039     }
3040     {
3041         var result = try execute(std.testing.allocator, "tuple([1, 2])");
3042         defer result.deinit();
3043         try std.testing.expect(result.value == .tuple);
3044         try std.testing.expectEqual(@as(usize, 2), result.value.tuple.items.len);
3045         try std.testing.expectEqual(object.Value{ .integer = 1 }, result.value.tuple.items[0]);
3046         try std.testing.expectEqual(object.Value{ .integer = 2 }, result.value.tuple.items[1]);
3047     }
3048     try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator, "len(tuple(range(3)))"));
3049     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "tuple(\"ab\")[1] == \"b\""));
3050 }
3051 
3052 test "execute tuple builtin consumes iterators and preserves tuples" {
3053     try std.testing.expectEqual(object.Value{ .integer = 12 }, try executeValue(std.testing.allocator,
3054         \\it = iter([1, 2])
3055         \\first = next(it)
3056         \\xs = tuple(it)
3057         \\first * 10 + xs[0]
3058     ));
3059     try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,
3060         \\xs = (1, 2)
3061         \\tuple(xs) is xs
3062     ));
3063 }
3064 
3065 test "execute tuple builtin errors and shadowing" {
3066     try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "tuple(1, 2)"));
3067     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "tuple(1)"));
3068     try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,
3069         \\tuple = 4
3070         \\tuple()
3071     ));
3072 }
3073 
3074 test "execute elif chains" {
3075     try expectString("middle", try executeValue(std.testing.allocator,
3076         \\x = 2
3077         \\if x == 1:
3078         \\    y = "first"
3079         \\elif x == 2:
3080         \\    y = "middle"
3081         \\else:
3082         \\    y = "last"
3083         \\y
3084     ));
3085 }
3086 
3087 test "execute while else on natural exhaustion" {
3088     try expectString("done", try executeValue(std.testing.allocator,
3089         \\x = 0
3090         \\while x < 3:
3091         \\    x = x + 1
3092         \\else:
3093         \\    y = "done"
3094         \\y
3095     ));
3096 }
3097 
3098 test "execute while else skipped by break" {
3099     try expectString("break", try executeValue(std.testing.allocator,
3100         \\x = 0
3101         \\y = "start"
3102         \\while x < 5:
3103         \\    x = x + 1
3104         \\    if x == 3:
3105         \\        y = "break"
3106         \\        break
3107         \\else:
3108         \\    y = "else"
3109         \\y
3110     ));
3111 }
3112 
3113 test "execute while else after continue" {
3114     try expectString("done", try executeValue(std.testing.allocator,
3115         \\x = 0
3116         \\while x < 3:
3117         \\    x = x + 1
3118         \\    if x < 3:
3119         \\        continue
3120         \\else:
3121         \\    y = "done"
3122         \\y
3123     ));
3124 }
3125 
3126 fn expectString(expected: []const u8, actual: object.Value) !void {
3127     try std.testing.expect(actual == .string);
3128     try std.testing.expectEqualStrings(expected, actual.string);
3129 }
3130 
3131 fn expectExecutedString(expected: []const u8, bytes: []const u8) !void {
3132     var result = try execute(std.testing.allocator, bytes);
3133     defer result.deinit();
3134     try expectString(expected, result.value);
3135 }