Skip to documentation
SLOP

tiny.python.runtime.vm

Reference tiny.python runtime vm

Defined in runtime.

The virtual machine runs a compiled Python program one instruction at a time, with one stack of values shared by every call, a list of call frames, a table of global variables and a heap.

API (8)

Actions

Public operations.

Types and contracts

Public types and contracts.

No direct callersNo direct callsruntimevm
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallsruntime.vmexecuteprivate sourcelib.python.src.runtime.vm.VmpopFrameruntime.Vmdeinit
Static calls · unresolved targets: 0 · external targets: 3.
Called byCallsNo direct callsruntime.vmexecuteruntime.Vminit
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsruntime.vmexecuteprivate sourcelib.python.src.runtime.vm.Vmattributeprivate sourcelib.python.src.runtime.vm.Vmbinaryprivate sourcelib.python.src.runtime.vm.VmbuildDictprivate sourcelib.python.src.runtime.vm.VmbuildListprivate sourcelib.python.src.runtime.vm.VmbuildTuple+21 moreruntime.Vmrun
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callsruntime.vmexecuteruntime.VmtakeHeap
Static calls · unresolved targets: 0 · external targets: 1.

Source: lib/python/src/runtime/root.zig:7

zig
pub const vm = @import("vm.zig");

Source: lib/python/src/runtime/vm.zig

zig
//! The virtual machine runs a compiled Python program one instruction at a time, with one stack of//! values shared by every call, a list of call frames, a table of global variables and a heap.//!//! A caller hands over source text and needs back the program's value, with memory it can free in//! one step, or an error that says why the program failed. The package accepts part of Python 3.14,//! and a run has to follow Python's rules for every name, call, loop and builtin type a program can//! use.//!//! A run creates objects whose lifetimes the program decides, and the program's value may point at//! any of them, so the objects have to outlive the machine that made them. The package accepts no//! `try` statement, so a program cannot handle an error, and every error ends the run.//!//! The package keeps the stack-machine design of [CPython](https://github.com/python/cpython), with//! Python's names for the builtins it provides (`len`, `range`, `iter`, `next` and five more) and//! for the errors it reports (`TypeError`, `KeyError`, `IndexError`, `ValueError`,//! `AttributeError`, `StopIteration`).//!//! Each Python exception that a run can raise becomes a member of `Error`. A member of `Error` ends//! the run and carries no message, value or position. A name resolves in three places: the current//! call's local variables, then the global variables, then the nine builtin functions, so a global//! named `len` hides the builtin. Assignment at top level writes a global variable, and assignment//! inside a function writes a local variable of that call, because the package accepts no `global`//! statement. `execute` compiles and runs a program and moves the heap into its `Result`, so the//! value it returns stays valid after the machine is gone. The returned value can point into the//! heap and into the source text, because a string literal and its slices with step 1 borrow the//! source. A run has no bound on its steps or its call depth. Each call adds a frame, so a program//! that recurses without end runs until an allocation fails and the run returns//! `error.OutOfMemory`. A loop without end that allocates nothing never returns.const std = @import("std");const source = @import("../source/root.zig");const syntax = @import("../syntax/root.zig");const compiler = @import("../compile/root.zig");const code = @import("../code/root.zig");const object = @import("../object/root.zig");/// The errors the virtual machine returns when a program fails, besides `error.OutOfMemory`. A/// caller switches on it to report why a program failed while it ran. Most tags carry the name of/// the Python exception that the same failure raises. An error carries no message, value or/// position. The package accepts no `try` statement, so every error ends the run.pub const Error = error{    /// A call with the wrong number of arguments, to a Python function, a builtin or a bound    /// method.    ArityMismatch,    /// A read of a method the list or dictionary lacks, or of any attribute of another type.    AttributeError,    /// A list or tuple index outside the sequence, including `pop` on an empty list.    IndexError,    /// A call to a function value whose position names no function in the top-level chunk. The same    /// error also reports a frame whose position has passed the end of its instructions. Only a    /// chunk built outside `compile` reaches that second case.    InvalidFunction,    /// Integer arithmetic outside the signed 128-bit range, the negation of the smallest such    /// integer, or a length or count that overflows while joining, repeating, slicing or building a    /// dictionary. The error also reports a range element or an `enumerate` counter that passes the    /// largest such integer while items remain.    IntegerOverflow,    /// A missing dictionary key in a lookup, a `del`, or `pop` without a default, or `popitem` on    /// an empty dictionary.    KeyError,    /// An operation that needs more values than the stack holds.    StackUnderflow,    /// `next` on an exhausted iterator with no default.    StopIteration,    /// An operation given a value of a type it does not support: arithmetic, ordering, calls,    /// iteration, subscripts and slices, an unhashable dictionary key, and builtins given an    /// unsupported argument.    TypeError,    /// A read or `del` of a name that is bound nowhere the lookup searches.    UndefinedName,    /// A step of zero in `range` or a slice, a string of invalid UTF-8 given to an operation that    /// counts its codepoints, or an item of the wrong length given to `dict` or `update` as a key    /// and value pair.    ValueError,};/// A finished run's value together with the heap that owns every object the value can point to./// `execute` returns one, and the caller reads `value` and then frees everything with `deinit`./// `value` stays valid until `deinit`. Strings in `value` can borrow the source text passed to/// `execute`, so that text has to stay alive while `value` is in use.pub const Result = struct {    /// The program's value: the value of its last top-level expression statement, or `None` when no    /// expression statement ran.    value: object.Value,    /// The heap that owns every object `value` can point to.    heap: object.Heap,    /// Frees the heap of the returned value, and every value derived from it becomes invalid. The    /// caller of `execute` defers it right after the call, as the README's example does. The call    /// leaves the result undefined.    pub fn deinit(self: *Result) void {        self.heap.deinit();        self.* = undefined;    }};/// Tokenizes, parses, compiles and runs the source text, then returns the program's value together/// with the heap that owns the value's objects. The README's example and nearly every test of the/// package call it with a short program. The call borrows the source text. Before it returns, the/// call frees the tokens, the syntax tree, the chunk and the machine's own state, so the result's/// heap is the only memory it hands back. The returned value borrows the result's heap and stays/// valid only until `Result.deinit`. The returned value can also point into the source text,/// through string literals and their slices, so the text has to stay alive while the value is used./// `Result.deinit` frees the heap with the allocator passed to this call. The call returns the/// errors of `tokenize`, `parse`, `compile` and `Vm.run`, and `error.OutOfMemory`, as one inferred/// error set. On any error the call frees everything it allocated, the heap included.pub fn execute(allocator: std.mem.Allocator, bytes: []const u8) !Result {    var stream = try source.tokenize(allocator, bytes);    defer stream.deinit(allocator);    var program = try syntax.parse(allocator, bytes, stream.tokens);    defer program.deinit();    var chunk_value = try compiler.compile(allocator, &program);    defer chunk_value.deinit(allocator);    var vm = Vm.init(allocator, &chunk_value);    defer vm.deinit();    const value = try vm.run();    return .{        .value = value,        .heap = vm.takeHeap(),    };}/// The state of one run: the chunk it runs, its heap, its call frames, its value stack and its/// global variables. `execute` drives one, and a caller that already holds a compiled chunk can run/// it directly. A machine is made with `init`, run with `run`, and freed with `deinit`. The machine/// borrows the chunk, which has to outlive it. The machine checks stack depth and function/// positions, and it trusts every other position in the chunk, so a chunk built outside `compile`/// has to keep them in range.pub const Vm = struct {    /// The allocator given to `init`. The frames, the stack, the globals and the heap all allocate    /// from it.    allocator: std.mem.Allocator,    /// The top-level chunk, which the machine borrows. Every call finds its function in this    /// chunk's function table.    module: *const code.Chunk,    /// The heap that owns every object the run creates. `takeHeap` moves this heap out and leaves    /// an empty one in its place.    heap: object.Heap,    /// One frame per call in progress, the top-level frame first. Each frame holds the chunk it    /// runs, its position in that chunk, its local variables and the value `save` last recorded.    /// The list grows by one frame per call, with no bound.    frames: std.ArrayListUnmanaged(Frame) = .empty,    /// The value stack shared by every frame: each operation pops its inputs from the end and    /// pushes its result there.    stack: std.ArrayListUnmanaged(object.Value) = .empty,    /// The global variables, keyed by names borrowed from the chunk.    globals: std.StringHashMapUnmanaged(object.Value) = .empty,    /// Returns a machine for the given chunk, with an empty heap, stack, frame list and table of    /// globals. `execute` calls it with the chunk from `compile`, and a caller that holds its own    /// chunk does the same. The call allocates nothing, so it cannot fail. The machine borrows the    /// chunk for its whole life.    pub fn init(allocator: std.mem.Allocator, chunk_value: *const code.Chunk) Vm {        return .{            .allocator = allocator,            .module = chunk_value,            .heap = object.Heap.init(allocator),        };    }    /// Frees the frames, the stack, the globals and the heap, and leaves the machine undefined.    /// `execute` defers it right after `init`, so it runs whether `run` succeeds or fails. Values    /// from `run` point into the heap freed here, unless `takeHeap` moved the heap out first.    pub fn deinit(self: *Vm) void {        while (self.frames.items.len > 0) self.popFrame();        self.frames.deinit(self.allocator);        self.stack.deinit(self.allocator);        self.globals.deinit(self.allocator);        self.heap.deinit();        self.* = undefined;    }    /// Returns the machine's heap and leaves an empty heap in its place. `execute` calls it after    /// `run`, so the objects of the result outlive the machine. The caller owns the returned heap    /// and frees it with `Heap.deinit`.    pub fn takeHeap(self: *Vm) object.Heap {        const heap = self.heap;        self.heap = object.Heap.init(self.allocator);        return heap;    }    /// Runs the chunk from its first instruction in a new top-level frame and returns the program's    /// value. `execute` calls it once per program. The value is the one that `ret` or a top-level    /// `return_value` returns. Returned values point into the machine's heap. The call returns a    /// member of `Error` when the program fails, and `error.OutOfMemory` when an allocation fails.    /// After an error, the frames, the stack and the heap keep the state at the failure until    /// `deinit` frees them. A run has no bound on its steps or its call depth.    pub fn run(self: *Vm) (Error || std.mem.Allocator.Error)!object.Value {        try self.pushFrame(self.module, .{});        while (self.frames.items.len > 0) {            const frame = self.currentFrame();            if (frame.ip >= frame.chunk.instructions.items.len) return Error.InvalidFunction;            const instruction = frame.chunk.instructions.items[frame.ip];            frame.ip += 1;            switch (instruction.op) {                .constant => try self.push(frame.chunk.constants.items[instruction.operand]),                .load => try self.load(instruction.operand),                .store => try self.store(instruction.operand),                .delete => try self.delete(instruction.operand),                .pop => _ = try self.pop(),                .save => frame.last = try self.pop(),                .dup => try self.dup(),                .swap => try self.swap(),                .rotate_three => try self.rotateThree(),                .jump => frame.ip = instruction.operand,                .jump_if_false => {                    if (!(try self.peek()).truthy()) frame.ip = instruction.operand;                },                .add => try self.binary(.add),                .sub => try self.binary(.sub),                .mul => try self.binary(.mul),                .neg => try self.negate(),                .not => try self.logicalNot(),                .equal => try self.binary(.equal),                .not_equal => try self.binary(.not_equal),                .less => try self.binary(.less),                .less_equal => try self.binary(.less_equal),                .greater => try self.binary(.greater),                .greater_equal => try self.binary(.greater_equal),                .contains => try self.binary(.contains),                .not_contains => try self.binary(.not_contains),                .identical => try self.binary(.identical),                .not_identical => try self.binary(.not_identical),                .call => try self.call(instruction.operand),                .attribute => try self.attribute(instruction.operand),                .build_list => try self.buildList(instruction.operand),                .build_tuple => try self.buildTuple(instruction.operand),                .build_dict => try self.buildDict(instruction.operand),                .iter => try self.iter(),                .for_next => try self.forNext(instruction.operand),                .subscript => try self.subscript(),                .slice => try self.slice(),                .store_subscript => try self.storeSubscript(),                .delete_subscript => try self.deleteSubscript(),                .return_value => if (try self.returnValue(try self.pop())) |value| return value,                .ret => if (try self.returnValue(frame.last)) |value| return value,            }        }        return .none;    }    fn currentFrame(self: *Vm) *Frame {        return &self.frames.items[self.frames.items.len - 1];    }    fn pushFrame(self: *Vm, chunk_value: *const code.Chunk, locals: std.StringHashMapUnmanaged(object.Value)) std.mem.Allocator.Error!void {        try self.frames.append(self.allocator, .{            .chunk = chunk_value,            .locals = locals,        });    }    fn popFrame(self: *Vm) void {        var frame = self.frames.items[self.frames.items.len - 1];        self.frames.items.len -= 1;        frame.deinit(self.allocator);    }    fn push(self: *Vm, value: object.Value) std.mem.Allocator.Error!void {        try self.stack.append(self.allocator, value);    }    fn pop(self: *Vm) Error!object.Value {        if (self.stack.items.len == 0) return Error.StackUnderflow;        const value = self.stack.items[self.stack.items.len - 1];        self.stack.items.len -= 1;        return value;    }    fn peek(self: *const Vm) Error!object.Value {        if (self.stack.items.len == 0) return Error.StackUnderflow;        return self.stack.items[self.stack.items.len - 1];    }    fn dup(self: *Vm) (Error || std.mem.Allocator.Error)!void {        try self.push(try self.peek());    }    fn swap(self: *Vm) Error!void {        if (self.stack.items.len < 2) return Error.StackUnderflow;        const top = self.stack.items.len - 1;        std.mem.swap(object.Value, &self.stack.items[top], &self.stack.items[top - 1]);    }    fn rotateThree(self: *Vm) Error!void {        if (self.stack.items.len < 3) return Error.StackUnderflow;        const base = self.stack.items.len - 3;        const first = self.stack.items[base];        const second = self.stack.items[base + 1];        const third = self.stack.items[base + 2];        self.stack.items[base] = third;        self.stack.items[base + 1] = first;        self.stack.items[base + 2] = second;    }    fn load(self: *Vm, name_index: usize) (Error || std.mem.Allocator.Error)!void {        const frame = self.currentFrame();        const name = frame.chunk.names.items[name_index];        if (frame.locals.get(name)) |value| {            try self.push(value);            return;        }        if (self.globals.get(name)) |value| {            try self.push(value);            return;        }        if (builtin(name)) |value| {            try self.push(value);            return;        }        return Error.UndefinedName;    }    fn store(self: *Vm, name_index: usize) (Error || std.mem.Allocator.Error)!void {        const value = try self.pop();        const frame = self.currentFrame();        const name = frame.chunk.names.items[name_index];        if (self.frames.items.len == 1) {            try self.globals.put(self.allocator, name, value);        } else {            try frame.locals.put(self.allocator, name, value);        }    }    fn delete(self: *Vm, name_index: usize) Error!void {        const frame = self.currentFrame();        const name = frame.chunk.names.items[name_index];        if (self.frames.items.len == 1) {            if (!self.globals.remove(name)) return Error.UndefinedName;        } else {            if (!frame.locals.remove(name)) return Error.UndefinedName;        }    }    fn call(self: *Vm, argument_count: usize) (Error || std.mem.Allocator.Error)!void {        if (self.stack.items.len < argument_count + 1) return Error.StackUnderflow;        const callee_index = self.stack.items.len - argument_count - 1;        const callee = self.stack.items[callee_index];        switch (callee) {            .function => |index| try self.callFunction(callee_index, argument_count, index),            .builtin => |value| try self.callBuiltin(callee_index, argument_count, value),            .method => |value| try self.callNativeMethod(callee_index, argument_count, value),            else => return Error.TypeError,        }    }    fn callFunction(self: *Vm, callee_index: usize, argument_count: usize, function_index: usize) (Error || std.mem.Allocator.Error)!void {        if (function_index >= self.module.functions.items.len) return Error.InvalidFunction;        const function = &self.module.functions.items[function_index];        if (function.params.len != argument_count) return Error.ArityMismatch;        var locals = std.StringHashMapUnmanaged(object.Value).empty;        errdefer locals.deinit(self.allocator);        for (function.params, 0..) |param, index| {            try locals.put(self.allocator, param, self.stack.items[callee_index + 1 + index]);        }        self.stack.items.len = callee_index;        try self.pushFrame(&function.chunk, locals);    }    fn callBuiltin(self: *Vm, callee_index: usize, argument_count: usize, value: object.Builtin) (Error || std.mem.Allocator.Error)!void {        const arguments = self.stack.items[callee_index + 1 ..][0..argument_count];        const result = switch (value) {            .dict => try self.dictBuiltin(arguments),            .enumerate => try self.enumerateBuiltin(arguments),            .iter => try self.iterBuiltin(arguments),            .len => try self.lenBuiltin(arguments),            .list => try self.listBuiltin(arguments),            .next => try self.nextBuiltin(arguments),            .range => try self.rangeBuiltin(arguments),            .reversed => try self.reversedBuiltin(arguments),            .tuple => try self.tupleBuiltin(arguments),        };        self.stack.items.len = callee_index;        try self.push(result);    }    fn callNativeMethod(self: *Vm, callee_index: usize, argument_count: usize, method: *object.NativeMethod) (Error || std.mem.Allocator.Error)!void {        const arguments = self.stack.items[callee_index + 1 ..][0..argument_count];        const result = switch (method.*) {            .dict_clear => |dict| try self.dictClearMethod(dict, arguments),            .dict_copy => |dict| try self.dictCopyMethod(dict, arguments),            .dict_get => |dict| try self.dictGetMethod(dict, arguments),            .dict_items => |dict| try self.dictItemsMethod(dict, arguments),            .dict_keys => |dict| try self.dictKeysMethod(dict, arguments),            .dict_pop => |dict| try self.dictPopMethod(dict, arguments),            .dict_popitem => |dict| try self.dictPopitemMethod(dict, arguments),            .dict_setdefault => |dict| try self.dictSetdefaultMethod(dict, arguments),            .dict_update => |dict| try self.dictUpdateMethod(dict, arguments),            .dict_values => |dict| try self.dictValuesMethod(dict, arguments),            .list_append => |list| try self.listAppendMethod(list, arguments),            .list_clear => |list| try self.listClearMethod(list, arguments),            .list_copy => |list| try self.listCopyMethod(list, arguments),            .list_pop => |list| try self.listPopMethod(list, arguments),        };        self.stack.items.len = callee_index;        try self.push(result);    }    fn attribute(self: *Vm, name_index: usize) (Error || std.mem.Allocator.Error)!void {        const target = try self.pop();        const name = self.currentFrame().chunk.names.items[name_index];        const result = switch (target) {            .dict => |dict| try self.dictAttribute(dict, name),            .list => |list| try self.listAttribute(list, name),            else => return Error.AttributeError,        };        try self.push(result);    }    fn dictAttribute(self: *Vm, dict: *object.Dict, name: []const u8) (Error || std.mem.Allocator.Error)!object.Value {        if (std.mem.eql(u8, name, "clear")) return try self.heap.createNativeMethod(.{ .dict_clear = dict });        if (std.mem.eql(u8, name, "copy")) return try self.heap.createNativeMethod(.{ .dict_copy = dict });        if (std.mem.eql(u8, name, "get")) return try self.heap.createNativeMethod(.{ .dict_get = dict });        if (std.mem.eql(u8, name, "items")) return try self.heap.createNativeMethod(.{ .dict_items = dict });        if (std.mem.eql(u8, name, "keys")) return try self.heap.createNativeMethod(.{ .dict_keys = dict });        if (std.mem.eql(u8, name, "pop")) return try self.heap.createNativeMethod(.{ .dict_pop = dict });        if (std.mem.eql(u8, name, "popitem")) return try self.heap.createNativeMethod(.{ .dict_popitem = dict });        if (std.mem.eql(u8, name, "setdefault")) return try self.heap.createNativeMethod(.{ .dict_setdefault = dict });        if (std.mem.eql(u8, name, "update")) return try self.heap.createNativeMethod(.{ .dict_update = dict });        if (std.mem.eql(u8, name, "values")) return try self.heap.createNativeMethod(.{ .dict_values = dict });        return Error.AttributeError;    }    fn listAttribute(self: *Vm, list: *object.List, name: []const u8) (Error || std.mem.Allocator.Error)!object.Value {        if (std.mem.eql(u8, name, "append")) return try self.heap.createNativeMethod(.{ .list_append = list });        if (std.mem.eql(u8, name, "clear")) return try self.heap.createNativeMethod(.{ .list_clear = list });        if (std.mem.eql(u8, name, "copy")) return try self.heap.createNativeMethod(.{ .list_copy = list });        if (std.mem.eql(u8, name, "pop")) return try self.heap.createNativeMethod(.{ .list_pop = list });        return Error.AttributeError;    }    fn dictClearMethod(self: *Vm, dict: *object.Dict, arguments: []const object.Value) Error!object.Value {        _ = self;        if (arguments.len != 0) return Error.ArityMismatch;        dict.clearRetainingCapacity();        return .none;    }    fn dictCopyMethod(self: *Vm, dict: *object.Dict, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {        if (arguments.len != 0) return Error.ArityMismatch;        return try self.heap.createDict(dict.entries.items);    }    fn dictGetMethod(self: *Vm, dict: *object.Dict, arguments: []const object.Value) Error!object.Value {        _ = self;        if (arguments.len == 0 or arguments.len > 2) return Error.ArityMismatch;        if (try dictEntryIndex(dict, arguments[0])) |index| return dict.entries.items[index].value;        if (arguments.len == 2) return arguments[1];        return .none;    }    fn dictItemsMethod(self: *Vm, dict: *object.Dict, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {        if (arguments.len != 0) return Error.ArityMismatch;        return try self.heap.createDictView(dict, .items);    }    fn dictKeysMethod(self: *Vm, dict: *object.Dict, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {        if (arguments.len != 0) return Error.ArityMismatch;        return try self.heap.createDictView(dict, .keys);    }    fn dictPopMethod(self: *Vm, dict: *object.Dict, arguments: []const object.Value) Error!object.Value {        _ = self;        if (arguments.len == 0 or arguments.len > 2) return Error.ArityMismatch;        if (try dictEntryIndex(dict, arguments[0])) |index| return dict.removeAt(index);        if (arguments.len == 2) return arguments[1];        return Error.KeyError;    }    fn dictPopitemMethod(self: *Vm, dict: *object.Dict, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {        if (arguments.len != 0) return Error.ArityMismatch;        if (dict.entries.items.len == 0) return Error.KeyError;        const entry = dict.popLast().?;        const pair = [_]object.Value{ entry.key, entry.value };        return try self.heap.createTuple(&pair);    }    fn dictSetdefaultMethod(self: *Vm, dict: *object.Dict, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {        if (arguments.len == 0 or arguments.len > 2) return Error.ArityMismatch;        if (try dictEntryIndex(dict, arguments[0])) |index| return dict.entries.items[index].value;        const value = if (arguments.len == 2) arguments[1] else object.Value.none;        try self.dictSet(dict, arguments[0], value);        return value;    }    fn dictUpdateMethod(self: *Vm, dict: *object.Dict, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {        if (arguments.len > 1) return Error.ArityMismatch;        if (arguments.len == 1) try self.updateDictFromValue(dict, arguments[0]);        return .none;    }    fn dictValuesMethod(self: *Vm, dict: *object.Dict, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {        if (arguments.len != 0) return Error.ArityMismatch;        return try self.heap.createDictView(dict, .values);    }    fn listAppendMethod(self: *Vm, list: *object.List, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {        if (arguments.len != 1) return Error.ArityMismatch;        try self.listAppend(list, arguments[0]);        return .none;    }    fn listClearMethod(self: *Vm, list: *object.List, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {        if (arguments.len != 0) return Error.ArityMismatch;        self.listClear(list);        return .none;    }    fn listCopyMethod(self: *Vm, list: *object.List, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {        if (arguments.len != 0) return Error.ArityMismatch;        return try self.heap.createList(list.items);    }    fn listPopMethod(self: *Vm, list: *object.List, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {        if (arguments.len > 1) return Error.ArityMismatch;        const index = if (arguments.len == 1) arguments[0] else object.Value{ .integer = -1 };        return try self.listRemoveAt(list, index);    }    fn dictBuiltin(self: *Vm, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {        if (arguments.len > 1) return Error.ArityMismatch;        if (arguments.len == 0) return try self.heap.createDict(&.{});        return switch (arguments[0]) {            .dict => |value| try self.heap.createDict(value.entries.items),            .iterator => |value| try self.dictFromIterator(value),            .string => |value| try self.dictFromIterator((try self.heap.createStringIterator(value)).iterator),            .list => |value| try self.dictFromIterator((try self.heap.createListIterator(value)).iterator),            .tuple => |value| try self.dictFromIterator((try self.heap.createTupleIterator(value)).iterator),            .view => |value| try self.dictFromIterator((try self.heap.createDictViewIterator(value)).iterator),            .range => |value| try self.dictFromIterator((try self.heap.createRangeIterator(value)).iterator),            else => Error.TypeError,        };    }    fn enumerateBuiltin(self: *Vm, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {        if (arguments.len == 0 or arguments.len > 2) return Error.ArityMismatch;        const start = if (arguments.len == 2) arguments[1].integerLike() orelse return Error.TypeError else 0;        return try self.heap.createEnumerateIterator((try self.iteratorValue(arguments[0])).iterator, start);    }    fn iterBuiltin(self: *Vm, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {        if (arguments.len != 1) return Error.ArityMismatch;        return try self.iteratorValue(arguments[0]);    }    fn lenBuiltin(self: *Vm, arguments: []const object.Value) Error!object.Value {        _ = self;        if (arguments.len != 1) return Error.ArityMismatch;        const length = switch (arguments[0]) {            .string => |value| std.unicode.utf8CountCodepoints(value) catch return Error.ValueError,            .list => |value| value.items.len,            .tuple => |value| value.items.len,            .dict => |value| value.entries.items.len,            .view => |value| value.dict.entries.items.len,            .range => |value| value.length,            else => return Error.TypeError,        };        return .{ .integer = @intCast(length) };    }    fn listBuiltin(self: *Vm, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {        if (arguments.len > 1) return Error.ArityMismatch;        if (arguments.len == 0) return try self.heap.createList(&.{});        return switch (arguments[0]) {            .iterator => |value| try self.listFromIterator(value),            .string => |value| try self.listFromIterator((try self.heap.createStringIterator(value)).iterator),            .list => |value| try self.heap.createList(value.items),            .tuple => |value| try self.heap.createList(value.items),            .dict => |value| try self.listFromIterator((try self.heap.createDictIterator(value)).iterator),            .view => |value| try self.listFromIterator((try self.heap.createDictViewIterator(value)).iterator),            .range => |value| try self.listFromIterator((try self.heap.createRangeIterator(value)).iterator),            else => Error.TypeError,        };    }    fn nextBuiltin(self: *Vm, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {        if (arguments.len == 0 or arguments.len > 2) return Error.ArityMismatch;        const iterator = switch (arguments[0]) {            .iterator => |value| value,            else => return Error.TypeError,        };        if (try self.iteratorNext(iterator)) |value| return value;        if (arguments.len == 2) return arguments[1];        return Error.StopIteration;    }    fn rangeBuiltin(self: *Vm, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {        if (arguments.len == 0 or arguments.len > 3) return Error.ArityMismatch;        const stop = arguments[if (arguments.len == 1) 0 else 1].integerLike() orelse return Error.TypeError;        const start = if (arguments.len == 1) 0 else arguments[0].integerLike() orelse return Error.TypeError;        const step = if (arguments.len == 3) arguments[2].integerLike() orelse return Error.TypeError else 1;        if (step == 0) return Error.ValueError;        return try self.heap.createRange(start, stop, step, try rangeLength(start, stop, step));    }    fn reversedBuiltin(self: *Vm, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {        if (arguments.len != 1) return Error.ArityMismatch;        return try self.reverseIteratorValue(arguments[0]);    }    fn tupleBuiltin(self: *Vm, arguments: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {        if (arguments.len > 1) return Error.ArityMismatch;        if (arguments.len == 0) return try self.heap.createTuple(&.{});        return switch (arguments[0]) {            .iterator => |value| try self.tupleFromIterator(value),            .string => |value| try self.tupleFromIterator((try self.heap.createStringIterator(value)).iterator),            .list => |value| try self.heap.createTuple(value.items),            .tuple => arguments[0],            .view => |value| try self.tupleFromIterator((try self.heap.createDictViewIterator(value)).iterator),            .range => |value| try self.tupleFromIterator((try self.heap.createRangeIterator(value)).iterator),            else => Error.TypeError,        };    }    fn listFromIterator(self: *Vm, iterator: *object.value.Iterator) (Error || std.mem.Allocator.Error)!object.Value {        var items = std.ArrayListUnmanaged(object.Value).empty;        defer items.deinit(self.allocator);        while (try self.iteratorNext(iterator)) |item| {            try items.append(self.allocator, item);        }        return try self.heap.createList(items.items);    }    fn tupleFromIterator(self: *Vm, iterator: *object.value.Iterator) (Error || std.mem.Allocator.Error)!object.Value {        var items = std.ArrayListUnmanaged(object.Value).empty;        defer items.deinit(self.allocator);        while (try self.iteratorNext(iterator)) |item| {            try items.append(self.allocator, item);        }        return try self.heap.createTuple(items.items);    }    fn dictFromIterator(self: *Vm, iterator: *object.value.Iterator) (Error || std.mem.Allocator.Error)!object.Value {        const dict = (try self.heap.createDict(&.{})).dict;        while (try self.iteratorNext(iterator)) |item| {            const pair = try pairFromValue(item);            try self.dictSet(dict, pair.key, pair.value);        }        return .{ .dict = dict };    }    fn updateDictFromValue(self: *Vm, dict: *object.Dict, value: object.Value) (Error || std.mem.Allocator.Error)!void {        switch (value) {            .dict => |other| try self.updateDictFromEntries(dict, other.entries.items),            .iterator => |iterator| try self.updateDictFromIterator(dict, iterator),            .string => |string| try self.updateDictFromIterator(dict, (try self.heap.createStringIterator(string)).iterator),            .list => |list| try self.updateDictFromIterator(dict, (try self.heap.createListIterator(list)).iterator),            .tuple => |tuple| try self.updateDictFromIterator(dict, (try self.heap.createTupleIterator(tuple)).iterator),            .view => |view| try self.updateDictFromIterator(dict, (try self.heap.createDictViewIterator(view)).iterator),            .range => |range| try self.updateDictFromIterator(dict, (try self.heap.createRangeIterator(range)).iterator),            else => return Error.TypeError,        }    }    fn updateDictFromEntries(self: *Vm, dict: *object.Dict, entries: []const object.DictEntry) (Error || std.mem.Allocator.Error)!void {        for (entries) |entry| try self.dictSet(dict, entry.key, entry.value);    }    fn updateDictFromIterator(self: *Vm, dict: *object.Dict, iterator: *object.value.Iterator) (Error || std.mem.Allocator.Error)!void {        while (try self.iteratorNext(iterator)) |item| {            const pair = try pairFromValue(item);            try self.dictSet(dict, pair.key, pair.value);        }    }    fn buildList(self: *Vm, item_count: usize) (Error || std.mem.Allocator.Error)!void {        if (self.stack.items.len < item_count) return Error.StackUnderflow;        const start = self.stack.items.len - item_count;        const list = try self.heap.createList(self.stack.items[start..]);        self.stack.items.len = start;        try self.push(list);    }    fn buildTuple(self: *Vm, item_count: usize) (Error || std.mem.Allocator.Error)!void {        if (self.stack.items.len < item_count) return Error.StackUnderflow;        const start = self.stack.items.len - item_count;        const tuple = try self.heap.createTuple(self.stack.items[start..]);        self.stack.items.len = start;        try self.push(tuple);    }    fn buildDict(self: *Vm, item_count: usize) (Error || std.mem.Allocator.Error)!void {        const stack_count = std.math.mul(usize, item_count, 2) catch return Error.IntegerOverflow;        if (self.stack.items.len < stack_count) return Error.StackUnderflow;        const start = self.stack.items.len - stack_count;        const dict = (try self.heap.createDict(&.{})).dict;        try dict.ensureTotalCapacity(self.allocator, item_count);        for (0..item_count) |index| {            const key_index = start + index * 2;            try self.dictSet(dict, self.stack.items[key_index], self.stack.items[key_index + 1]);        }        self.stack.items.len = start;        try self.push(.{ .dict = dict });    }    fn iter(self: *Vm) (Error || std.mem.Allocator.Error)!void {        const iterable = try self.pop();        try self.push(try self.iteratorValue(iterable));    }    fn iteratorValue(self: *Vm, iterable: object.Value) (Error || std.mem.Allocator.Error)!object.Value {        return switch (iterable) {            .iterator => iterable,            .list => |list| try self.heap.createListIterator(list),            .dict => |dict| try self.heap.createDictIterator(dict),            .view => |view| try self.heap.createDictViewIterator(view),            .tuple => |tuple| try self.heap.createTupleIterator(tuple),            .range => |range| try self.heap.createRangeIterator(range),            .string => |string| try self.heap.createStringIterator(string),            else => Error.TypeError,        };    }    fn reverseIteratorValue(self: *Vm, iterable: object.Value) (Error || std.mem.Allocator.Error)!object.Value {        return switch (iterable) {            .list => |list| try self.heap.createListReverseIterator(list),            .dict => |dict| try self.heap.createDictReverseIterator(dict),            .view => |view| try self.heap.createDictViewReverseIterator(view),            .tuple => |tuple| try self.heap.createTupleReverseIterator(tuple),            .range => |range| try self.heap.createRangeReverseIterator(range),            .string => |string| try self.heap.createStringReverseIterator(string),            else => Error.TypeError,        };    }    fn forNext(self: *Vm, exit: usize) (Error || std.mem.Allocator.Error)!void {        const value = try self.peek();        switch (value) {            .iterator => |iterator| {                if (try self.iteratorNext(iterator)) |item| {                    try self.push(item);                } else {                    _ = try self.pop();                    self.currentFrame().ip = exit;                }            },            else => return Error.TypeError,        }    }    fn iteratorNext(self: *Vm, iterator: *object.value.Iterator) (Error || std.mem.Allocator.Error)!?object.Value {        return switch (iterator.*) {            .dict => |*dict| dictIteratorNext(dict),            .enumerate => |*enumerate| try self.enumerateIteratorNext(enumerate),            .view => |*view| try self.dictViewIteratorNext(view),            .list => |*list| listIteratorNext(list),            .tuple => |*tuple| tupleIteratorNext(tuple),            .range => |*range| rangeIteratorNext(range),            .string => |*string| stringIteratorNext(string),        };    }    fn enumerateIteratorNext(self: *Vm, iterator: *object.value.EnumerateIterator) (Error || std.mem.Allocator.Error)!?object.Value {        if (iterator.overflowed) {            if (try self.iteratorNext(iterator.iterator)) |_| return Error.IntegerOverflow;            return null;        }        const item = (try self.iteratorNext(iterator.iterator)) orelse return null;        const pair = [_]object.Value{            .{ .integer = iterator.index },            item,        };        iterator.index = std.math.add(i128, iterator.index, 1) catch blk: {            iterator.overflowed = true;            break :blk iterator.index;        };        return try self.heap.createTuple(&pair);    }    fn listIteratorNext(iterator: *object.value.ListIterator) ?object.Value {        if (iterator.index >= iterator.list.items.len) return null;        const index = if (iterator.reverse) iterator.list.items.len - 1 - iterator.index else iterator.index;        const item = iterator.list.items[index];        iterator.index += 1;        return item;    }    fn dictIteratorNext(iterator: *object.value.DictIterator) ?object.Value {        if (iterator.index >= iterator.dict.entries.items.len) return null;        const index = if (iterator.reverse) iterator.dict.entries.items.len - 1 - iterator.index else iterator.index;        const item = iterator.dict.entries.items[index].key;        iterator.index += 1;        return item;    }    fn dictViewIteratorNext(self: *Vm, iterator: *object.value.DictViewIterator) (Error || std.mem.Allocator.Error)!?object.Value {        if (iterator.index >= iterator.view.dict.entries.items.len) return null;        const index = if (iterator.reverse) iterator.view.dict.entries.items.len - 1 - iterator.index else iterator.index;        const entry = iterator.view.dict.entries.items[index];        iterator.index += 1;        return switch (iterator.view.kind) {            .keys => entry.key,            .values => entry.value,            .items => blk: {                const pair = [_]object.Value{ entry.key, entry.value };                break :blk try self.heap.createTuple(&pair);            },        };    }    fn tupleIteratorNext(iterator: *object.value.TupleIterator) ?object.Value {        if (iterator.index >= iterator.tuple.items.len) return null;        const index = if (iterator.reverse) iterator.tuple.items.len - 1 - iterator.index else iterator.index;        const item = iterator.tuple.items[index];        iterator.index += 1;        return item;    }    fn rangeIteratorNext(iterator: *object.value.RangeIterator) Error!?object.Value {        if (iterator.index >= iterator.range.length) {            return null;        }        if (iterator.reverse) {            const index: i128 = @intCast(iterator.range.length - 1 - iterator.index);            const item = try rangeValueAt(iterator.range, index);            iterator.index += 1;            return .{ .integer = item };        }        const item = iterator.next;        const next_index = iterator.index + 1;        const next = if (next_index < iterator.range.length) std.math.add(i128, iterator.next, iterator.range.step) catch return Error.IntegerOverflow else undefined;        iterator.index = next_index;        if (iterator.index < iterator.range.length) iterator.next = next;        return .{ .integer = item };    }    fn stringIteratorNext(iterator: *object.value.StringIterator) Error!?object.Value {        if (iterator.reverse) return stringReverseIteratorNext(iterator);        if (iterator.index >= iterator.value.len) return null;        var view = std.unicode.Utf8View.init(iterator.value[iterator.index..]) catch return Error.ValueError;        var utf8 = view.iterator();        const codepoint = utf8.nextCodepointSlice() orelse return null;        const start = iterator.index;        iterator.index += codepoint.len;        return .{ .string = iterator.value[start..iterator.index] };    }    fn stringReverseIteratorNext(iterator: *object.value.StringIterator) Error!?object.Value {        if (iterator.index == 0) return null;        _ = std.unicode.Utf8View.init(iterator.value) catch return Error.ValueError;        var start = iterator.index - 1;        while (start > 0 and (iterator.value[start] & 0xc0) == 0x80) start -= 1;        const end = iterator.index;        iterator.index = start;        return .{ .string = iterator.value[start..end] };    }    fn subscript(self: *Vm) (Error || std.mem.Allocator.Error)!void {        const index = try self.pop();        const target = try self.pop();        switch (target) {            .list => |list| try self.push(try listItem(list, index)),            .tuple => |tuple| try self.push(try tupleItem(tuple, index)),            .dict => |dict| try self.push(try dictItem(dict, index)),            else => return Error.TypeError,        }    }    fn slice(self: *Vm) (Error || std.mem.Allocator.Error)!void {        const step = try self.pop();        const stop = try self.pop();        const start = try self.pop();        const target = try self.pop();        const result = switch (target) {            .list => |list| try self.listSlice(list, start, stop, step),            .tuple => |tuple| try self.tupleSlice(tuple, start, stop, step),            .range => |range| try self.rangeSlice(range, start, stop, step),            .string => |string| try self.stringSlice(string, start, stop, step),            else => return Error.TypeError,        };        try self.push(result);    }    fn storeSubscript(self: *Vm) (Error || std.mem.Allocator.Error)!void {        const value = try self.pop();        const index = try self.pop();        const target = try self.pop();        switch (target) {            .list => |list| list.items[try listIndex(list, index)] = value,            .dict => |dict| try self.dictSet(dict, index, value),            else => return Error.TypeError,        }    }    fn deleteSubscript(self: *Vm) (Error || std.mem.Allocator.Error)!void {        const index = try self.pop();        const target = try self.pop();        switch (target) {            .list => |list| try self.listDelete(list, index),            .dict => |dict| try dictDelete(dict, index),            else => return Error.TypeError,        }    }    fn listItem(list: *const object.List, index_value: object.Value) Error!object.Value {        return list.items[try sequenceIndex(list.items.len, index_value)];    }    fn listIndex(list: *const object.List, index_value: object.Value) Error!usize {        return sequenceIndex(list.items.len, index_value);    }    fn tupleItem(tuple: *const object.Tuple, index_value: object.Value) Error!object.Value {        return tuple.items[try sequenceIndex(tuple.items.len, index_value)];    }    fn dictItem(dict: *const object.Dict, key: object.Value) Error!object.Value {        const index = try dictEntryIndex(dict, key) orelse return Error.KeyError;        return dict.entries.items[index].value;    }    fn dictSet(self: *Vm, dict: *object.Dict, key: object.Value, value: object.Value) (Error || std.mem.Allocator.Error)!void {        if (!key.hashable()) return Error.TypeError;        try dict.put(self.allocator, key, value);    }    fn listDelete(self: *Vm, list: *object.List, index_value: object.Value) (Error || std.mem.Allocator.Error)!void {        _ = try self.listRemoveAt(list, index_value);    }    fn listAppend(self: *Vm, list: *object.List, value: object.Value) (Error || std.mem.Allocator.Error)!void {        _ = try sequenceConcatLength(list.items.len, 1);        try list.append(self.allocator, value);    }    fn listClear(self: *Vm, list: *object.List) void {        list.clearAndFree(self.allocator);    }    fn listRemoveAt(self: *Vm, list: *object.List, index_value: object.Value) Error!object.Value {        const index = try listIndex(list, index_value);        const removed = list.orderedRemove(index);        if (list.items.len < list.capacity / 2) list.shrinkAndFree(self.allocator, list.items.len);        return removed;    }    fn dictDelete(dict: *object.Dict, key: object.Value) Error!void {        const index = try dictEntryIndex(dict, key) orelse return Error.KeyError;        _ = dict.removeAt(index);    }    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 {        const bounds = try sliceBounds(list.items.len, start_value, stop_value, step_value);        var items = std.ArrayListUnmanaged(object.Value).empty;        defer items.deinit(self.allocator);        try items.ensureTotalCapacity(self.allocator, sliceCount(bounds));        var index = bounds.start;        while (sliceIncludes(index, bounds)) {            try items.append(self.allocator, list.items[@intCast(index)]);            index = std.math.add(i128, index, bounds.step) catch break;        }        return try self.heap.createList(items.items);    }    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 {        const bounds = try sliceBounds(tuple.items.len, start_value, stop_value, step_value);        var items = std.ArrayListUnmanaged(object.Value).empty;        defer items.deinit(self.allocator);        try items.ensureTotalCapacity(self.allocator, sliceCount(bounds));        var index = bounds.start;        while (sliceIncludes(index, bounds)) {            try items.append(self.allocator, tuple.items[@intCast(index)]);            index = std.math.add(i128, index, bounds.step) catch break;        }        return try self.heap.createTuple(items.items);    }    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 {        const bounds = try sliceBounds(range.length, start_value, stop_value, step_value);        const length = sliceCount(bounds);        if (length == 0) return try self.heap.createRange(0, 0, 1, 0);        const start = try rangeValueAt(range, bounds.start);        const step = std.math.mul(i128, range.step, bounds.step) catch return Error.IntegerOverflow;        const length_value: i128 = @intCast(length);        const span = std.math.mul(i128, step, length_value) catch return Error.IntegerOverflow;        const stop = std.math.add(i128, start, span) catch return Error.IntegerOverflow;        return try self.heap.createRange(start, stop, step, length);    }    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 {        var offsets = std.ArrayListUnmanaged(usize).empty;        defer offsets.deinit(self.allocator);        var view = std.unicode.Utf8View.init(value) catch return Error.ValueError;        var iterator = view.iterator();        var byte_index: usize = 0;        while (iterator.nextCodepointSlice()) |codepoint| {            try offsets.append(self.allocator, byte_index);            byte_index += codepoint.len;        }        try offsets.append(self.allocator, value.len);        const bounds = try sliceBounds(offsets.items.len - 1, start_value, stop_value, step_value);        if (sliceCount(bounds) == 0) return .{ .string = value[0..0] };        if (bounds.step == 1) {            const start: usize = @intCast(bounds.start);            const stop: usize = @intCast(bounds.stop);            return .{ .string = value[offsets.items[start]..offsets.items[stop]] };        }        var bytes = std.ArrayListUnmanaged(u8).empty;        defer bytes.deinit(self.allocator);        var index = bounds.start;        while (sliceIncludes(index, bounds)) {            const item: usize = @intCast(index);            try bytes.appendSlice(self.allocator, value[offsets.items[item]..offsets.items[item + 1]]);            index = std.math.add(i128, index, bounds.step) catch break;        }        return try self.heap.createString(bytes.items);    }    fn sequenceIndex(length: usize, index_value: object.Value) Error!usize {        var index = index_value.integerLike() orelse return Error.TypeError;        const len: i128 = @intCast(length);        if (index < 0) index += len;        if (index < 0 or index >= len) return Error.IndexError;        return @intCast(index);    }    fn returnValue(self: *Vm, value: object.Value) std.mem.Allocator.Error!?object.Value {        self.popFrame();        if (self.frames.items.len == 0) {            return value;        } else {            try self.push(value);        }        return null;    }    fn negate(self: *Vm) (Error || std.mem.Allocator.Error)!void {        const value = try self.pop();        const integer = value.integerLike() orelse return Error.TypeError;        if (integer == std.math.minInt(i128)) return Error.IntegerOverflow;        try self.push(.{ .integer = -integer });    }    fn logicalNot(self: *Vm) (Error || std.mem.Allocator.Error)!void {        const value = try self.pop();        try self.push(.{ .boolean = !value.truthy() });    }    fn binary(self: *Vm, op: BinaryOp) (Error || std.mem.Allocator.Error)!void {        const right = try self.pop();        const left = try self.pop();        switch (op) {            .equal => {                try self.push(.{ .boolean = left.eql(right) });                return;            },            .not_equal => {                try self.push(.{ .boolean = !left.eql(right) });                return;            },            .identical => {                try self.push(.{ .boolean = identical(left, right) });                return;            },            .not_identical => {                try self.push(.{ .boolean = !identical(left, right) });                return;            },            .contains => {                try self.push(.{ .boolean = try self.contains(left, right) });                return;            },            .not_contains => {                try self.push(.{ .boolean = !(try self.contains(left, right)) });                return;            },            .add => {                try self.push(try self.add(left, right));                return;            },            .mul => {                try self.push(try self.multiply(left, right));                return;            },            .less, .less_equal, .greater, .greater_equal => {                try self.push(.{ .boolean = try orderValues(left, right, op) });                return;            },            else => {},        }        const a = left.integerLike() orelse return Error.TypeError;        const b = right.integerLike() orelse return Error.TypeError;        switch (op) {            .add => try self.push(.{ .integer = std.math.add(i128, a, b) catch return Error.IntegerOverflow }),            .sub => try self.push(.{ .integer = std.math.sub(i128, a, b) catch return Error.IntegerOverflow }),            .mul => try self.push(.{ .integer = std.math.mul(i128, a, b) catch return Error.IntegerOverflow }),            .less => try self.push(.{ .boolean = a < b }),            .less_equal => try self.push(.{ .boolean = a <= b }),            .greater => try self.push(.{ .boolean = a > b }),            .greater_equal => try self.push(.{ .boolean = a >= b }),            .equal, .not_equal, .identical, .not_identical, .contains, .not_contains => unreachable,        }    }    fn add(self: *Vm, left: object.Value, right: object.Value) (Error || std.mem.Allocator.Error)!object.Value {        return switch (left) {            .list => |list| switch (right) {                .list => |other| try self.listConcat(list.items, other.items),                else => Error.TypeError,            },            .tuple => |tuple| switch (right) {                .tuple => |other| try self.tupleConcat(tuple.items, other.items),                else => Error.TypeError,            },            .string => |string| switch (right) {                .string => |other| try self.stringConcat(string, other),                else => Error.TypeError,            },            else => {                const a = left.integerLike() orelse return Error.TypeError;                const b = right.integerLike() orelse return Error.TypeError;                return .{ .integer = std.math.add(i128, a, b) catch return Error.IntegerOverflow };            },        };    }    fn multiply(self: *Vm, left: object.Value, right: object.Value) (Error || std.mem.Allocator.Error)!object.Value {        return switch (left) {            .list => |list| try self.listRepeat(list.items, right),            .tuple => |tuple| try self.tupleRepeat(tuple.items, right),            .string => |string| try self.stringRepeat(string, right),            else => switch (right) {                .list => |list| try self.listRepeat(list.items, left),                .tuple => |tuple| try self.tupleRepeat(tuple.items, left),                .string => |string| try self.stringRepeat(string, left),                else => {                    const a = left.integerLike() orelse return Error.TypeError;                    const b = right.integerLike() orelse return Error.TypeError;                    return .{ .integer = std.math.mul(i128, a, b) catch return Error.IntegerOverflow };                },            },        };    }    fn listConcat(self: *Vm, left: []const object.Value, right: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {        var items = std.ArrayListUnmanaged(object.Value).empty;        defer items.deinit(self.allocator);        try items.ensureTotalCapacity(self.allocator, try sequenceConcatLength(left.len, right.len));        try items.appendSlice(self.allocator, left);        try items.appendSlice(self.allocator, right);        return try self.heap.createList(items.items);    }    fn tupleConcat(self: *Vm, left: []const object.Value, right: []const object.Value) (Error || std.mem.Allocator.Error)!object.Value {        var items = std.ArrayListUnmanaged(object.Value).empty;        defer items.deinit(self.allocator);        try items.ensureTotalCapacity(self.allocator, try sequenceConcatLength(left.len, right.len));        try items.appendSlice(self.allocator, left);        try items.appendSlice(self.allocator, right);        return try self.heap.createTuple(items.items);    }    fn stringConcat(self: *Vm, left: []const u8, right: []const u8) (Error || std.mem.Allocator.Error)!object.Value {        var bytes = std.ArrayListUnmanaged(u8).empty;        defer bytes.deinit(self.allocator);        try bytes.ensureTotalCapacity(self.allocator, try sequenceConcatLength(left.len, right.len));        try bytes.appendSlice(self.allocator, left);        try bytes.appendSlice(self.allocator, right);        return try self.heap.createString(bytes.items);    }    fn listRepeat(self: *Vm, items: []const object.Value, count_value: object.Value) (Error || std.mem.Allocator.Error)!object.Value {        const count = try repeatCount(count_value);        const length = try repeatLength(items.len, count);        if (length == 0) return try self.heap.createList(&.{});        var repeated = std.ArrayListUnmanaged(object.Value).empty;        defer repeated.deinit(self.allocator);        try repeated.ensureTotalCapacity(self.allocator, length);        for (0..count) |_| try repeated.appendSlice(self.allocator, items);        return try self.heap.createList(repeated.items);    }    fn tupleRepeat(self: *Vm, items: []const object.Value, count_value: object.Value) (Error || std.mem.Allocator.Error)!object.Value {        const count = try repeatCount(count_value);        const length = try repeatLength(items.len, count);        if (length == 0) return try self.heap.createTuple(&.{});        var repeated = std.ArrayListUnmanaged(object.Value).empty;        defer repeated.deinit(self.allocator);        try repeated.ensureTotalCapacity(self.allocator, length);        for (0..count) |_| try repeated.appendSlice(self.allocator, items);        return try self.heap.createTuple(repeated.items);    }    fn stringRepeat(self: *Vm, bytes: []const u8, count_value: object.Value) (Error || std.mem.Allocator.Error)!object.Value {        const count = try repeatCount(count_value);        const length = try repeatLength(bytes.len, count);        if (length == 0) return try self.heap.createString("");        var repeated = std.ArrayListUnmanaged(u8).empty;        defer repeated.deinit(self.allocator);        try repeated.ensureTotalCapacity(self.allocator, length);        for (0..count) |_| try repeated.appendSlice(self.allocator, bytes);        return try self.heap.createString(repeated.items);    }    fn contains(self: *Vm, item: object.Value, container: object.Value) (Error || std.mem.Allocator.Error)!bool {        return switch (container) {            .list => |list| listContains(item, list),            .tuple => |tuple| sequenceContains(item, tuple.items),            .dict => |dict| try dictContains(item, dict),            .view => |view| try self.dictViewContains(item, view),            .range => |range| rangeContains(item, range),            .string => |string| stringContains(item, string),            .iterator => |iterator| try self.iteratorContains(item, iterator),            else => Error.TypeError,        };    }    fn iteratorContains(self: *Vm, item: object.Value, iterator: *object.value.Iterator) (Error || std.mem.Allocator.Error)!bool {        while (try self.iteratorNext(iterator)) |candidate| {            if (identical(item, candidate) or item.eql(candidate)) return true;        }        return false;    }    fn dictViewContains(self: *Vm, item: object.Value, view: *const object.DictView) (Error || std.mem.Allocator.Error)!bool {        _ = self;        return switch (view.kind) {            .keys => try dictContains(item, view.dict),            .values => dictValuesContain(item, view.dict),            .items => try dictItemsContain(item, view.dict),        };    }};const BinaryOp = enum {    add,    sub,    mul,    equal,    not_equal,    less,    less_equal,    greater,    greater_equal,    contains,    not_contains,    identical,    not_identical,};fn sequenceConcatLength(left: usize, right: usize) Error!usize {    return std.math.add(usize, left, right) catch Error.IntegerOverflow;}fn repeatCount(value: object.Value) Error!usize {    const count = value.integerLike() orelse return Error.TypeError;    if (count <= 0) return 0;    const max: i128 = @intCast(std.math.maxInt(usize));    if (count > max) return Error.IntegerOverflow;    return @intCast(count);}fn repeatLength(length: usize, count: usize) Error!usize {    return std.math.mul(usize, length, count) catch Error.IntegerOverflow;}const ValueOrder = enum {    lt,    eq,    gt,};fn orderValues(left: object.Value, right: object.Value, op: BinaryOp) Error!bool {    return orderResult(try compareValues(left, right), op);}fn compareValues(left: object.Value, right: object.Value) Error!ValueOrder {    if (left.integerLike()) |a| {        if (right.integerLike()) |b| return compareInteger(a, b);    }    return switch (left) {        .string => |value| switch (right) {            .string => |other| compareString(value, other),            else => Error.TypeError,        },        .list => |value| switch (right) {            .list => |other| try compareSequences(value.items, other.items),            else => Error.TypeError,        },        .tuple => |value| switch (right) {            .tuple => |other| try compareSequences(value.items, other.items),            else => Error.TypeError,        },        else => Error.TypeError,    };}fn compareSequences(left: []const object.Value, right: []const object.Value) Error!ValueOrder {    const count = @min(left.len, right.len);    for (left[0..count], right[0..count]) |a, b| {        if (identical(a, b) or a.eql(b)) continue;        const item_order = try compareValues(a, b);        if (item_order != .eq) return item_order;    }    return compareLength(left.len, right.len);}fn compareInteger(left: i128, right: i128) ValueOrder {    if (left < right) return .lt;    if (left > right) return .gt;    return .eq;}fn compareLength(left: usize, right: usize) ValueOrder {    if (left < right) return .lt;    if (left > right) return .gt;    return .eq;}fn compareString(left: []const u8, right: []const u8) ValueOrder {    return switch (std.mem.order(u8, left, right)) {        .lt => .lt,        .eq => .eq,        .gt => .gt,    };}fn orderResult(order: ValueOrder, op: BinaryOp) bool {    return switch (op) {        .less => order == .lt,        .less_equal => order != .gt,        .greater => order == .gt,        .greater_equal => order != .lt,        else => unreachable,    };}const SliceBounds = struct {    start: i128,    stop: i128,    step: i128,};fn sliceBounds(length: usize, start_value: object.Value, stop_value: object.Value, step_value: object.Value) Error!SliceBounds {    const step = (try optionalSliceInteger(step_value)) orelse 1;    if (step == 0) return Error.ValueError;    const len: i128 = @intCast(length);    if (step > 0) {        return .{            .start = if (try optionalSliceInteger(start_value)) |value| positiveSliceBound(value, len) else 0,            .stop = if (try optionalSliceInteger(stop_value)) |value| positiveSliceBound(value, len) else len,            .step = step,        };    }    return .{        .start = if (try optionalSliceInteger(start_value)) |value| negativeSliceBound(value, len) else len - 1,        .stop = if (try optionalSliceInteger(stop_value)) |value| negativeSliceBound(value, len) else -1,        .step = step,    };}fn optionalSliceInteger(value: object.Value) Error!?i128 {    return switch (value) {        .none => null,        else => value.integerLike() orelse Error.TypeError,    };}fn positiveSliceBound(value: i128, length: i128) i128 {    var index = value;    if (index < 0) index += length;    if (index < 0) return 0;    if (index > length) return length;    return index;}fn negativeSliceBound(value: i128, length: i128) i128 {    var index = value;    if (index < 0) index += length;    if (index < 0) return -1;    if (index >= length) return length - 1;    return index;}fn sliceIncludes(index: i128, bounds: SliceBounds) bool {    if (bounds.step > 0) return index < bounds.stop;    return index > bounds.stop;}fn sliceCount(bounds: SliceBounds) usize {    var count: usize = 0;    var index = bounds.start;    while (sliceIncludes(index, bounds)) {        count += 1;        index = std.math.add(i128, index, bounds.step) catch break;    }    return count;}fn rangeValueAt(range: *const object.Range, index: i128) Error!i128 {    const offset = std.math.mul(i128, range.step, index) catch return Error.IntegerOverflow;    return std.math.add(i128, range.start, offset) catch return Error.IntegerOverflow;}fn identical(left: object.Value, right: object.Value) bool {    return switch (left) {        .none => right == .none,        .boolean => |value| switch (right) {            .boolean => |other| value == other,            else => false,        },        .integer => |value| switch (right) {            .integer => |other| value == other,            else => false,        },        .string => |value| switch (right) {            .string => |other| value.ptr == other.ptr and value.len == other.len,            else => false,        },        .function => |value| switch (right) {            .function => |other| value == other,            else => false,        },        .builtin => |value| switch (right) {            .builtin => |other| value == other,            else => false,        },        .method => |value| switch (right) {            .method => |other| value == other,            else => false,        },        .view => |value| switch (right) {            .view => |other| value == other,            else => false,        },        .list => |value| switch (right) {            .list => |other| value == other,            else => false,        },        .tuple => |value| switch (right) {            .tuple => |other| value == other,            else => false,        },        .dict => |value| switch (right) {            .dict => |other| value == other,            else => false,        },        .range => |value| switch (right) {            .range => |other| value == other,            else => false,        },        .iterator => |value| switch (right) {            .iterator => |other| value == other,            else => false,        },    };}fn listContains(item: object.Value, list: *const object.List) bool {    return sequenceContains(item, list.items);}fn sequenceContains(item: object.Value, items: []const object.Value) bool {    for (items) |candidate| {        if (identical(item, candidate) or item.eql(candidate)) return true;    }    return false;}fn dictContains(item: object.Value, dict: *const object.Dict) Error!bool {    return (try dictEntryIndex(dict, item)) != null;}fn dictValuesContain(item: object.Value, dict: *const object.Dict) bool {    for (dict.entries.items) |entry| {        if (identical(item, entry.value) or item.eql(entry.value)) return true;    }    return false;}fn dictItemsContain(item: object.Value, dict: *const object.Dict) Error!bool {    return switch (item) {        .tuple => |tuple| {            if (tuple.items.len != 2) return false;            const index = try dictEntryIndex(dict, tuple.items[0]) orelse return false;            const value = dict.entries.items[index].value;            return identical(tuple.items[1], value) or tuple.items[1].eql(value);        },        else => false,    };}fn dictEntryIndex(dict: *const object.Dict, key: object.Value) Error!?usize {    if (!key.hashable()) return Error.TypeError;    return dict.indexOf(key);}const Pair = struct {    key: object.Value,    value: object.Value,};fn pairFromValue(value: object.Value) Error!Pair {    return switch (value) {        .list => |list| pairFromSlice(list.items),        .tuple => |tuple| pairFromSlice(tuple.items),        else => Error.TypeError,    };}fn pairFromSlice(items: []const object.Value) Error!Pair {    if (items.len != 2) return Error.ValueError;    return .{        .key = items[0],        .value = items[1],    };}fn rangeContains(item: object.Value, range: *const object.Range) bool {    if (range.length == 0) return false;    const value = item.integerLike() orelse return false;    if (range.step > 0) {        if (value < range.start or value >= range.stop) return false;        return orderedDistance(range.start, value) % @as(u128, @intCast(range.step)) == 0;    }    if (value > range.start or value <= range.stop) return false;    return orderedDistance(value, range.start) % negativeMagnitude(range.step) == 0;}fn stringContains(item: object.Value, string: []const u8) Error!bool {    const needle = switch (item) {        .string => |value| value,        else => return Error.TypeError,    };    return std.mem.indexOf(u8, string, needle) != null;}fn orderedDistance(start: i128, stop: i128) u128 {    std.debug.assert(start <= stop);    if (start == stop) return 0;    if (start >= 0) return @intCast(stop - start);    if (stop <= 0) return nonPositiveMagnitude(start) - nonPositiveMagnitude(stop);    return nonPositiveMagnitude(start) + @as(u128, @intCast(stop));}fn builtin(name: []const u8) ?object.Value {    if (std.mem.eql(u8, name, "dict")) return .{ .builtin = .dict };    if (std.mem.eql(u8, name, "enumerate")) return .{ .builtin = .enumerate };    if (std.mem.eql(u8, name, "iter")) return .{ .builtin = .iter };    if (std.mem.eql(u8, name, "len")) return .{ .builtin = .len };    if (std.mem.eql(u8, name, "list")) return .{ .builtin = .list };    if (std.mem.eql(u8, name, "next")) return .{ .builtin = .next };    if (std.mem.eql(u8, name, "range")) return .{ .builtin = .range };    if (std.mem.eql(u8, name, "reversed")) return .{ .builtin = .reversed };    if (std.mem.eql(u8, name, "tuple")) return .{ .builtin = .tuple };    return null;}fn rangeLength(start: i128, stop: i128, step: i128) Error!usize {    if (step > 0) {        if (start >= stop) return 0;        return try rangeCount(distanceAscending(start, stop), @intCast(step));    }    if (start <= stop) return 0;    return try rangeCount(distanceAscending(stop, start), negativeMagnitude(step));}fn rangeCount(distance: u128, step: u128) Error!usize {    const count = (distance - 1) / step + 1;    if (count > @as(u128, std.math.maxInt(usize))) return Error.IntegerOverflow;    return @intCast(count);}fn distanceAscending(start: i128, stop: i128) u128 {    std.debug.assert(start < stop);    if (start >= 0) return @intCast(stop - start);    if (stop <= 0) return nonPositiveMagnitude(start) - nonPositiveMagnitude(stop);    return nonPositiveMagnitude(start) + @as(u128, @intCast(stop));}fn nonPositiveMagnitude(value: i128) u128 {    if (value == 0) return 0;    return negativeMagnitude(value);}fn negativeMagnitude(value: i128) u128 {    std.debug.assert(value < 0);    return @as(u128, @intCast(-(value + 1))) + 1;}const Frame = struct {    chunk: *const code.Chunk,    ip: usize = 0,    locals: std.StringHashMapUnmanaged(object.Value) = .empty,    last: object.Value = .none,    fn deinit(self: *Frame, allocator: std.mem.Allocator) void {        self.locals.deinit(allocator);        self.* = undefined;    }};fn executeValue(allocator: std.mem.Allocator, bytes: []const u8) !object.Value {    var result = try execute(allocator, bytes);    defer result.deinit();    return result.value;}test "execute arithmetic with precedence" {    try std.testing.expectEqual(object.Value{ .integer = 7 }, try executeValue(std.testing.allocator, "1 + 2 * 3"));}test "execute assignments" {    try std.testing.expectEqual(object.Value{ .integer = 13 }, try executeValue(std.testing.allocator,        \\x = 5        \\y = x * 2        \\y + 3    ));}test "execute name deletion" {    try std.testing.expectEqual(object.Value{ .integer = 2 }, try executeValue(std.testing.allocator,        \\x = 1        \\del x        \\x = 2        \\x    ));    try std.testing.expectError(Error.UndefinedName, executeValue(std.testing.allocator,        \\x = 1        \\del x        \\x    ));    try std.testing.expectError(Error.UndefinedName, executeValue(std.testing.allocator,        \\def f():        \\    x = 1        \\    del x        \\    return x        \\f()    ));    try std.testing.expectError(Error.UndefinedName, executeValue(std.testing.allocator, "del missing"));}test "execute booleans as integers" {    try std.testing.expectEqual(object.Value{ .integer = 2 }, try executeValue(std.testing.allocator, "True + True"));}test "execute simple function call" {    try std.testing.expectEqual(object.Value{ .integer = 42 }, try executeValue(std.testing.allocator,        \\def add(a, b):        \\    return a + b        \\add(20, 22)    ));}test "execute function local frame with global fallback" {    try std.testing.expectEqual(object.Value{ .integer = 17 }, try executeValue(std.testing.allocator,        \\x = 10        \\def f(x):        \\    y = x + 2        \\    return y        \\f(5) + x    ));}test "execute function without return yields none" {    try std.testing.expectEqual(object.Value.none, try executeValue(std.testing.allocator,        \\def f():        \\    1 + 2        \\f()    ));}test "execute comparisons" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "1 < 2"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "True == 1"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "None != 0"));}test "execute chained comparisons" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "1 < 2 <= 2 != 3"));    try std.testing.expectEqual(object.Value{ .boolean = false }, try executeValue(std.testing.allocator, "1 < 2 < 2"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "1 < 3 > 2"));}test "execute chained comparisons short circuit" {    try std.testing.expectEqual(object.Value{ .boolean = false }, try executeValue(std.testing.allocator, "3 < 2 < missing"));}test "execute chained sequence comparisons" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1] < [2] < [3]"));    try std.testing.expectEqual(object.Value{ .boolean = false }, try executeValue(std.testing.allocator, "\"b\" < \"a\" < missing"));}test "execute identity comparisons" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "None is None"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "True is not False"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\xs = []        \\ys = xs        \\xs is ys    ));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\xs = []        \\ys = []        \\xs is not ys    ));}test "execute membership comparisons over supported containers" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "2 in [1, 2, 3]"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "4 not in [1, 2, 3]"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "2 in (1, 2, 3)"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "4 not in (1, 2, 3)"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "2 in range(0, 5, 2)"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "3 not in range(0, 5, 2)"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "5 in range(7, 3, -1)"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "\"bc\" in \"abcd\""));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "\"\" in \"abcd\""));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "\"z\" not in \"abcd\""));}test "execute membership consumes iterators" {    try std.testing.expectEqual(object.Value{ .integer = 2 }, try executeValue(std.testing.allocator,        \\it = iter([1, 2, 3])        \\first = 1 in it        \\second = next(it)        \\third = 3 in it        \\if first and third:        \\    value = second        \\else:        \\    value = 0        \\value    ));    try std.testing.expectEqual(object.Value{ .integer = 9 }, try executeValue(std.testing.allocator,        \\it = iter([1])        \\missing = 2 in it        \\next(it, 9)    ));}test "execute membership errors" {    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "1 in 2"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "1 in \"123\""));}test "execute membership in chained comparisons" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\xs = [1, 2]        \\1 in xs == [1, 2]    ));    try std.testing.expectEqual(object.Value{ .boolean = false }, try executeValue(std.testing.allocator, "1 in [] < missing"));}test "execute if else branches" {    try std.testing.expectEqual(object.Value{ .integer = 7 }, try executeValue(std.testing.allocator,        \\x = 3        \\if x > 5:        \\    y = 1        \\else:        \\    y = 7        \\y    ));}test "execute if without else true branch" {    try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator,        \\x = 1        \\if x:        \\    x = x + 2        \\x    ));}test "execute while loop" {    try std.testing.expectEqual(object.Value{ .integer = 6 }, try executeValue(std.testing.allocator,        \\x = 0        \\sum = 0        \\while x < 4:        \\    sum = sum + x        \\    x = x + 1        \\sum    ));}test "execute control flow inside function" {    try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator,        \\def first_three(limit):        \\    x = 0        \\    while x < limit:        \\        if x == 3:        \\            return x        \\        x = x + 1        \\    return -1        \\first_three(5)    ));}test "execute break" {    try std.testing.expectEqual(object.Value{ .integer = 4 }, try executeValue(std.testing.allocator,        \\x = 0        \\while True:        \\    x = x + 1        \\    if x == 4:        \\        break        \\x    ));}test "execute continue" {    try std.testing.expectEqual(object.Value{ .integer = 12 }, try executeValue(std.testing.allocator,        \\x = 0        \\sum = 0        \\while x < 5:        \\    x = x + 1        \\    if x == 3:        \\        continue        \\    sum = sum + x        \\sum    ));}test "execute loop control inside function" {    try std.testing.expectEqual(object.Value{ .integer = 7 }, try executeValue(std.testing.allocator,        \\def stop_at(limit):        \\    x = 0        \\    while True:        \\        x = x + 1        \\        if x == limit:        \\            break        \\        if x < 3:        \\            continue        \\    return x        \\stop_at(7)    ));}test "execute logical not" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "not None"));    try std.testing.expectEqual(object.Value{ .boolean = false }, try executeValue(std.testing.allocator, "not 1 == 1"));}test "execute logical and returns selected operand" {    try std.testing.expectEqual(object.Value{ .integer = 0 }, try executeValue(std.testing.allocator, "0 and missing"));    try std.testing.expectEqual(object.Value{ .integer = 7 }, try executeValue(std.testing.allocator, "1 and 7"));}test "execute logical or returns selected operand" {    try std.testing.expectEqual(object.Value{ .integer = 5 }, try executeValue(std.testing.allocator, "5 or missing"));    try std.testing.expectEqual(object.Value{ .integer = 9 }, try executeValue(std.testing.allocator, "None or 9"));}test "execute logical operators in control flow" {    try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator,        \\x = 0        \\while x < 5:        \\    x = x + 1        \\    if x > 1 and not x == 3:        \\        continue        \\    if x == 3 or False:        \\        break        \\x    ));}test "execute string literals" {    try expectString("alpha", try executeValue(std.testing.allocator, "\"alpha\""));    try expectString("beta", try executeValue(std.testing.allocator, "'beta'"));}test "execute string assignment and return" {    try expectString("value", try executeValue(std.testing.allocator,        \\x = "value"        \\x    ));    try expectString("done", try executeValue(std.testing.allocator,        \\def f():        \\    return "done"        \\f()    ));}test "execute string equality" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "\"a\" == 'a'"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "\"a\" != \"b\""));}test "execute string truthiness" {    try std.testing.expectEqual(object.Value{ .integer = 1 }, try executeValue(std.testing.allocator,        \\if "":        \\    x = 0        \\else:        \\    x = 1        \\x    ));    try expectString("fallback", try executeValue(std.testing.allocator, "\"\" or \"fallback\""));    try expectString("omega", try executeValue(std.testing.allocator, "\"alpha\" and \"omega\" or \"alpha\""));}test "execute string concatenation and repetition" {    try expectExecutedString("abcd", "\"ab\" + \"cd\"");    try expectExecutedString("ababab", "\"ab\" * 3");    try expectExecutedString("abab", "2 * \"ab\"");    try expectExecutedString("", "\"ab\" * -1");    try expectExecutedString("", "\"ab\" * False");    try expectExecutedString("ab", "\"ab\" * True");    try expectExecutedString("\xc3\xab\xc3\xab", "\"" ++ "\xc3\xab" ++ "\" * 2");}test "execute string ordering" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "\"abc\" < \"abd\""));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "\"abc\" <= \"abc\""));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "\"abd\" > \"abc\""));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "\"" ++ "\xc3\xa9" ++ "\" > \"z\""));}test "execute tuple literals" {    {        var result = try execute(std.testing.allocator, "()");        defer result.deinit();        try std.testing.expect(result.value == .tuple);        try std.testing.expectEqual(@as(usize, 0), result.value.tuple.items.len);    }    {        var result = try execute(std.testing.allocator, "(1,)");        defer result.deinit();        try std.testing.expect(result.value == .tuple);        try std.testing.expectEqual(@as(usize, 1), result.value.tuple.items.len);        try std.testing.expectEqual(object.Value{ .integer = 1 }, result.value.tuple.items[0]);    }    {        var result = try execute(std.testing.allocator, "1, 2");        defer result.deinit();        try std.testing.expect(result.value == .tuple);        try std.testing.expectEqual(@as(usize, 2), result.value.tuple.items.len);        try std.testing.expectEqual(object.Value{ .integer = 2 }, result.value.tuple.items[1]);    }}test "execute tuple indexing truthiness and equality" {    try std.testing.expectEqual(object.Value{ .integer = 2 }, try executeValue(std.testing.allocator, "(1, 2, 3)[1]"));    try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator, "(1, 2, 3)[-1]"));    try std.testing.expectEqual(object.Value{ .integer = 2 }, try executeValue(std.testing.allocator,        \\if ():        \\    x = 1        \\else:        \\    x = 2        \\x    ));    try std.testing.expectEqual(object.Value{ .integer = 1 }, try executeValue(std.testing.allocator,        \\if (0,):        \\    x = 1        \\else:        \\    x = 2        \\x    ));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "(1, 2) == (1, 2)"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "(1, 2) != (1, 3)"));}test "execute tuple concatenation and repetition" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "(1,) + (2, 3) == (1, 2, 3)"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "(1, 2) * 2 == (1, 2, 1, 2)"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "2 * (1,) == (1, 1)"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "(1,) * -1 == ()"));}test "execute tuple ordering" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "(1, 2) < (1, 3)"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "(1, 2) <= (1, 2)"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "(1, 3) > (1, 2)"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "(1, 2) < (1, 2, 0)"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "((1,),) < ((2,),)"));}test "execute tuple indexing and assignment errors" {    try std.testing.expectError(Error.IndexError, executeValue(std.testing.allocator, "(1,)[1]"));    try std.testing.expectError(Error.IndexError, executeValue(std.testing.allocator, "(1,)[-2]"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "(1,)[None]"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,        \\xs = (1, 2)        \\xs[0] = 3    ));}test "execute list displays" {    var result = try execute(std.testing.allocator, "[1, 2, 3]");    defer result.deinit();    try std.testing.expect(result.value == .list);    try std.testing.expectEqual(@as(usize, 3), result.value.list.items.len);    try std.testing.expectEqual(object.Value{ .integer = 1 }, result.value.list.items[0]);    try std.testing.expectEqual(object.Value{ .integer = 3 }, result.value.list.items[2]);}test "execute list indexing" {    try std.testing.expectEqual(object.Value{ .integer = 2 }, try executeValue(std.testing.allocator, "[1, 2, 3][1]"));    try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator, "[1, 2, 3][-1]"));    try std.testing.expectEqual(object.Value{ .integer = 7 }, try executeValue(std.testing.allocator, "[[7]][0][0]"));}test "execute list truthiness and equality" {    try std.testing.expectEqual(object.Value{ .integer = 2 }, try executeValue(std.testing.allocator,        \\if []:        \\    x = 1        \\else:        \\    x = 2        \\x    ));    try std.testing.expectEqual(object.Value{ .integer = 1 }, try executeValue(std.testing.allocator,        \\if [0]:        \\    x = 1        \\else:        \\    x = 2        \\x    ));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1, 2] == [1, 2]"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1, 2] != [1, 3]"));}test "execute self-containing list equality" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\xs = []        \\xs.append(xs)        \\xs == xs    ));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\xs = []        \\xs.append((xs,))        \\xs == xs    ));}test "execute list concatenation and repetition" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1] + [2, 3] == [1, 2, 3]"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1, 2] * 2 == [1, 2, 1, 2]"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "2 * [1] == [1, 1]"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1] * -1 == []"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1] * False == []"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1] * True == [1]"));    try std.testing.expectEqual(object.Value{ .integer = 9 }, try executeValue(std.testing.allocator,        \\inner = [1]        \\xs = [inner] * 2        \\xs[0][0] = 9        \\xs[1][0]    ));    try std.testing.expectEqual(object.Value{ .integer = 7 }, try executeValue(std.testing.allocator,        \\inner = [1]        \\xs = [inner] + []        \\xs[0][0] = 7        \\inner[0]    ));}test "execute list ordering" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1, 2] < [1, 3]"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1, 2] <= [1, 2]"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1, 3] > [1, 2]"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1, 2] < [1, 2, 0]"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[[1]] < [[2]]"));}test "execute list index errors" {    try std.testing.expectError(Error.IndexError, executeValue(std.testing.allocator, "[1][1]"));    try std.testing.expectError(Error.IndexError, executeValue(std.testing.allocator, "[1][-2]"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "[1][None]"));}test "execute list subscript assignment" {    try std.testing.expectEqual(object.Value{ .integer = 7 }, try executeValue(std.testing.allocator,        \\xs = [1, 2]        \\xs[0] = 7        \\xs[0]    ));    try std.testing.expectEqual(object.Value{ .integer = 9 }, try executeValue(std.testing.allocator,        \\xs = [1, 2]        \\xs[-1] = 9        \\xs[1]    ));}test "execute nested list subscript assignment" {    try std.testing.expectEqual(object.Value{ .integer = 5 }, try executeValue(std.testing.allocator,        \\xs = [[1]]        \\xs[0][0] = 5        \\xs[0][0]    ));}test "execute list subscript assignment errors" {    try std.testing.expectError(Error.IndexError, executeValue(std.testing.allocator,        \\xs = [1]        \\xs[1] = 2    ));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,        \\xs = [1]        \\xs[None] = 2    ));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "1[0] = 2"));}test "execute list item deletion" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\xs = [1, 2, 3]        \\del xs[1]        \\xs == [1, 3]    ));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\xs = [1, 2, 3]        \\del xs[-1]        \\xs == [1, 2]    ));    try std.testing.expectEqual(object.Value{ .integer = 12 }, try executeValue(std.testing.allocator,        \\xs = [0, 1, 2]        \\del xs[0]        \\xs[0] * 10 + len(xs)    ));}test "execute list item deletion errors" {    try std.testing.expectError(Error.IndexError, executeValue(std.testing.allocator,        \\xs = [1]        \\del xs[1]    ));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,        \\xs = [1]        \\del xs[None]    ));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "del (1,)[0]"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "del 1[0]"));}test "execute list append method" {    try std.testing.expectEqual(object.Value{ .integer = 31 }, try executeValue(std.testing.allocator,        \\xs = []        \\result = xs.append(2)        \\xs.append(1)        \\if result is None:        \\    marker = 10        \\else:        \\    marker = 0        \\xs[0] * 10 + xs[1] + marker    ));}test "execute stored bound list methods" {    try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator,        \\xs = []        \\push = xs.append        \\push(3)        \\xs[0]    ));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\xs = []        \\first = xs.append        \\second = xs.append        \\first == second and not first is second    ));    try std.testing.expectEqual(object.Value{ .integer = 7 }, try executeValue(std.testing.allocator,        \\xs = []        \\d = {xs.append: 7}        \\d[xs.append]    ));}test "execute list pop method" {    try std.testing.expectEqual(object.Value{ .integer = 131 }, try executeValue(std.testing.allocator,        \\xs = [1, 2, 3]        \\last = xs.pop()        \\first = xs.pop(0)        \\last * 10 + first * 100 + len(xs)    ));    try std.testing.expectEqual(object.Value{ .integer = 21 }, try executeValue(std.testing.allocator,        \\xs = [1, 2]        \\xs.pop(-1) * 10 + len(xs)    ));}test "execute list clear and copy methods" {    try std.testing.expectEqual(object.Value{ .integer = 19 }, try executeValue(std.testing.allocator,        \\xs = [1, 2]        \\ys = xs.copy()        \\ys[0] = 9        \\xs[0] * 10 + ys[0]    ));    try std.testing.expectEqual(object.Value{ .integer = 0 }, try executeValue(std.testing.allocator,        \\xs = [1]        \\result = xs.clear()        \\if result is None:        \\    len(xs)        \\else:        \\    9    ));    var result = try execute(std.testing.allocator,        \\xs = [0, 1, 2, 3, 4, 5, 6, 7]        \\xs.clear()        \\xs    );    defer result.deinit();    try std.testing.expectEqual(@as(usize, 0), result.value.list.items.len);    try std.testing.expectEqual(@as(usize, 0), result.value.list.capacity);}test "execute list method errors" {    try std.testing.expectError(Error.AttributeError, executeValue(std.testing.allocator, "[1].missing"));    try std.testing.expectError(Error.AttributeError, executeValue(std.testing.allocator, "1.append"));    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "[].append()"));    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "[].append(1, 2)"));    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "[].clear(1)"));    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "[].copy(1)"));    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "[].pop(0, 1)"));    try std.testing.expectError(Error.IndexError, executeValue(std.testing.allocator, "[].pop()"));    try std.testing.expectError(Error.IndexError, executeValue(std.testing.allocator, "[1].pop(1)"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "[1].pop(None)"));}test "execute list slices" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[0, 1, 2, 3, 4][1:4] == [1, 2, 3]"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[0, 1, 2, 3, 4][-4:-1:2] == [1, 3]"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[0, 1, 2, 3][::-1] == [3, 2, 1, 0]"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[1, 2][None:None:None] == [1, 2]"));    try std.testing.expectEqual(object.Value{ .integer = 193 }, try executeValue(std.testing.allocator,        \\xs = [0, 1, 2, 3, 4]        \\ys = xs[1:4]        \\ys[0] = 9        \\xs[1] * 100 + ys[0] * 10 + len(ys)    ));}test "execute tuple slices" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "(0, 1, 2, 3)[1:3] == (1, 2)"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "(0, 1, 2, 3)[::-1] == (3, 2, 1, 0)"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "(0, 1, 2, 3)[10:] == ()"));}test "execute range slices" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "list(range(10)[2:8:2]) == [2, 4, 6]"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "list(range(9, 0, -2)[1:3]) == [7, 5]"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "list(range(10)[::-3]) == [9, 6, 3, 0]"));    try std.testing.expectEqual(object.Value{ .integer = 0 }, try executeValue(std.testing.allocator, "len(range(10)[20:])"));}test "execute string slices" {    {        var result = try execute(std.testing.allocator, "\"abcd\"[1:3]");        defer result.deinit();        try expectString("bc", result.value);    }    {        var result = try execute(std.testing.allocator, "\"abcd\"[::2]");        defer result.deinit();        try expectString("ac", result.value);    }    {        var result = try execute(std.testing.allocator, "\"abcd\"[::-1]");        defer result.deinit();        try expectString("dcba", result.value);    }    {        var result = try execute(std.testing.allocator, "\"no" ++ "\xc3\xabl" ++ "\"[2:3]");        defer result.deinit();        try expectString("\xc3\xab", result.value);    }}test "execute slice errors" {    try std.testing.expectError(Error.ValueError, executeValue(std.testing.allocator, "[1][::0]"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "[1][\"a\":]"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "1[0:1]"));}test "execute sequence operator errors" {    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "[1] + (2,)"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "\"a\" + 1"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "[1] * None"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "None * [1]"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "range(3) + range(3)"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "range(3) * 2"));}test "execute sequence ordering errors" {    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "[1] < (1,)"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "\"a\" < 1"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "[None] < [0]"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "range(3) < range(4)"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "[range(1)] < [range(2)]"));}test "execute for loop over list" {    try std.testing.expectEqual(object.Value{ .integer = 6 }, try executeValue(std.testing.allocator,        \\total = 0        \\for x in [1, 2, 3]:        \\    total = total + x        \\total    ));}test "execute for loop variable persists" {    try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator,        \\for x in [1, 2, 3]:        \\    pass        \\x    ));}test "execute empty for loop leaves target unassigned" {    try std.testing.expectError(Error.UndefinedName, executeValue(std.testing.allocator,        \\for x in []:        \\    pass        \\x    ));}test "execute for else on natural exhaustion" {    try expectString("done", try executeValue(std.testing.allocator,        \\for x in [1, 2]:        \\    y = "body"        \\else:        \\    y = "done"        \\y    ));}test "execute for else skipped by break" {    try expectString("break", try executeValue(std.testing.allocator,        \\y = "start"        \\for x in [1, 2, 3]:        \\    if x == 2:        \\        y = "break"        \\        break        \\else:        \\    y = "else"        \\y    ));}test "execute for else after continue" {    try expectString("done", try executeValue(std.testing.allocator,        \\for x in [1, 2, 3]:        \\    if x < 3:        \\        continue        \\else:        \\    y = "done"        \\y    ));}test "execute return from for loop" {    try std.testing.expectEqual(object.Value{ .integer = 5 }, try executeValue(std.testing.allocator,        \\def first():        \\    for x in [1, 2]:        \\        return x        \\    return 9        \\first() + 4    ));}test "execute rejects non-iterable for loop" {    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,        \\for x in 1:        \\    pass    ));}test "execute range builtin creates range objects" {    var result = try execute(std.testing.allocator, "range(1, 6, 2)");    defer result.deinit();    try std.testing.expect(result.value == .range);    try std.testing.expectEqual(@as(i128, 1), result.value.range.start);    try std.testing.expectEqual(@as(i128, 6), result.value.range.stop);    try std.testing.expectEqual(@as(i128, 2), result.value.range.step);    try std.testing.expectEqual(@as(usize, 3), result.value.range.length);}test "execute for loop over range" {    try std.testing.expectEqual(object.Value{ .integer = 6 }, try executeValue(std.testing.allocator,        \\total = 0        \\for x in range(4):        \\    total = total + x        \\total    ));    try std.testing.expectEqual(object.Value{ .integer = 9 }, try executeValue(std.testing.allocator,        \\total = 0        \\for x in range(1, 6, 2):        \\    total = total + x        \\total    ));    try std.testing.expectEqual(object.Value{ .integer = 9 }, try executeValue(std.testing.allocator,        \\total = 0        \\for x in range(5, 0, -2):        \\    total = total + x        \\total    ));}test "execute range loop supports else and empty target behavior" {    try expectString("done", try executeValue(std.testing.allocator,        \\for x in range(0):        \\    y = "body"        \\else:        \\    y = "done"        \\y    ));    try std.testing.expectError(Error.UndefinedName, executeValue(std.testing.allocator,        \\for x in range(0):        \\    pass        \\x    ));}test "execute range truthiness and equality" {    try std.testing.expectEqual(object.Value{ .integer = 2 }, try executeValue(std.testing.allocator,        \\if range(0):        \\    x = 1        \\else:        \\    x = 2        \\x    ));    try std.testing.expectEqual(object.Value{ .integer = 1 }, try executeValue(std.testing.allocator,        \\if range(1):        \\    x = 1        \\else:        \\    x = 2        \\x    ));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "range(0, 3, 2) == range(0, 4, 2)"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "range(0) == range(1, 1, 3)"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "range(0, 5, 2) != range(0, 4, 2)"));}test "execute range builtin errors" {    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "range()"));    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "range(1, 2, 3, 4)"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "range(None)"));    try std.testing.expectError(Error.ValueError, executeValue(std.testing.allocator, "range(1, 2, 0)"));}test "execute globals shadow range builtin" {    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,        \\range = 3        \\range(1)    ));}test "execute len builtin for supported sequences" {    try std.testing.expectEqual(object.Value{ .integer = 0 }, try executeValue(std.testing.allocator, "len([])"));    try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator, "len([1, 2, 3])"));    try std.testing.expectEqual(object.Value{ .integer = 2 }, try executeValue(std.testing.allocator, "len((1, 2))"));    try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator, "len(range(1, 6, 2))"));    try std.testing.expectEqual(object.Value{ .integer = 4 }, try executeValue(std.testing.allocator, "len(\"abcd\")"));    try std.testing.expectEqual(object.Value{ .integer = 4 }, try executeValue(std.testing.allocator, "len(\"no" ++ "\xc3\xabl" ++ "\")"));}test "execute len builtin errors and shadowing" {    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "len()"));    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "len([], [])"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "len(1)"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,        \\len = 4        \\len([])    ));}test "execute list builtin creates lists" {    {        var result = try execute(std.testing.allocator, "list()");        defer result.deinit();        try std.testing.expect(result.value == .list);        try std.testing.expectEqual(@as(usize, 0), result.value.list.items.len);    }    {        var result = try execute(std.testing.allocator, "list(range(1, 6, 2))");        defer result.deinit();        try std.testing.expect(result.value == .list);        try std.testing.expectEqual(@as(usize, 3), result.value.list.items.len);        try std.testing.expectEqual(object.Value{ .integer = 1 }, result.value.list.items[0]);        try std.testing.expectEqual(object.Value{ .integer = 3 }, result.value.list.items[1]);        try std.testing.expectEqual(object.Value{ .integer = 5 }, result.value.list.items[2]);    }    {        var result = try execute(std.testing.allocator, "list(\"ab\")");        defer result.deinit();        try std.testing.expect(result.value == .list);        try std.testing.expectEqual(@as(usize, 2), result.value.list.items.len);        try expectString("a", result.value.list.items[0]);        try expectString("b", result.value.list.items[1]);    }    {        var result = try execute(std.testing.allocator, "list((1, 2))");        defer result.deinit();        try std.testing.expect(result.value == .list);        try std.testing.expectEqual(@as(usize, 2), result.value.list.items.len);        try std.testing.expectEqual(object.Value{ .integer = 1 }, result.value.list.items[0]);        try std.testing.expectEqual(object.Value{ .integer = 2 }, result.value.list.items[1]);    }}test "execute list builtin copies list values" {    try std.testing.expectEqual(object.Value{ .integer = 17 }, try executeValue(std.testing.allocator,        \\xs = [1, 2]        \\ys = list(xs)        \\ys[0] = 7        \\xs[0] * 10 + ys[0]    ));}test "execute list builtin errors and shadowing" {    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "list(1, 2)"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "list(1)"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,        \\list = 4        \\list()    ));}test "execute iter and next builtins over ranges and lists" {    try std.testing.expectEqual(object.Value{ .integer = 12 }, try executeValue(std.testing.allocator,        \\it = iter(range(1, 4))        \\a = next(it)        \\b = next(it)        \\a * 10 + b    ));    try std.testing.expectEqual(object.Value{ .integer = 12 }, try executeValue(std.testing.allocator,        \\it = iter([1, 2])        \\same = iter(it)        \\next(same) * 10 + next(it)    ));}test "execute next builtin exhaustion" {    try std.testing.expectError(Error.StopIteration, executeValue(std.testing.allocator, "next(iter([]))"));    try std.testing.expectEqual(object.Value{ .integer = 9 }, try executeValue(std.testing.allocator, "next(iter([]), 9)"));    try std.testing.expectEqual(object.Value.none, try executeValue(std.testing.allocator, "next(iter([]), None)"));}test "execute iter and next builtin errors and shadowing" {    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "iter()"));    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "iter([], None)"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "iter(1)"));    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "next()"));    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "next(iter([]), 1, 2)"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "next([])"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,        \\iter = 4        \\iter([])    ));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,        \\next = 4        \\next(iter([]))    ));}test "execute for loop over iterators and strings" {    try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator,        \\total = 0        \\it = iter(range(3))        \\for x in it:        \\    total = total + x        \\total    ));    try std.testing.expectEqual(object.Value{ .integer = 2 }, try executeValue(std.testing.allocator,        \\total = 0        \\for ch in "ab":        \\    total = total + len(ch)        \\total    ));}test "execute for loop over tuples" {    try std.testing.expectEqual(object.Value{ .integer = 6 }, try executeValue(std.testing.allocator,        \\total = 0        \\for x in (1, 2, 3):        \\    total = total + x        \\total    ));    try std.testing.expectEqual(object.Value{ .integer = 12 }, try executeValue(std.testing.allocator,        \\it = iter((1, 2))        \\next(it) * 10 + next(it)    ));}test "execute string iterator yields codepoint slices" {    try expectString("a", try executeValue(std.testing.allocator, "next(iter(\"ab\"))"));    try expectString("\xc3\xab", try executeValue(std.testing.allocator, "next(iter(\"" ++ "\xc3\xab" ++ "\"))"));}test "execute enumerate builtin over iterable values" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "list(enumerate([\"a\", \"b\"])) == [(0, \"a\"), (1, \"b\")]"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "tuple(enumerate((3, 4), -1)) == ((-1, 3), (0, 4))"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "list(enumerate(\"a" ++ "\xc3\xab" ++ "\")) == [(0, \"a\"), (1, \"" ++ "\xc3\xab" ++ "\")]"));    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))]"));}test "execute enumerate builtin consumes iterators with next and for loops" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\it = iter([10, 20, 30])        \\first = next(it)        \\first == 10 and list(enumerate(it, 5)) == [(5, 20), (6, 30)]    ));    try std.testing.expectEqual(object.Value{ .integer = 468 }, try executeValue(std.testing.allocator,        \\total = 0        \\for pair in enumerate(range(3), 4):        \\    total = total * 10 + pair[0] + pair[1]        \\total    ));    try std.testing.expectEqual(object.Value{ .integer = 52 }, try executeValue(std.testing.allocator,        \\pair = next(enumerate(reversed([1, 2]), 5))        \\pair[0] * 10 + pair[1]    ));}test "execute enumerate builtin errors and shadowing" {    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "enumerate()"));    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "enumerate([], 1, 2)"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "enumerate(1)"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "enumerate([], None)"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,        \\enumerate = 4        \\enumerate([])    ));}test "execute reversed builtin over sequences" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "list(reversed([1, 2, 3])) == [3, 2, 1]"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "tuple(reversed((1, 2, 3))) == (3, 2, 1)"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "list(reversed(range(1, 6, 2))) == [5, 3, 1]"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "list(reversed(\"a" ++ "\xc3\xab" ++ "\")) == [\"" ++ "\xc3\xab" ++ "\", \"a\"]"));}test "execute reversed builtin over dictionaries and views" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\d = {"a": 1, "b": 2}        \\list(reversed(d)) == ["b", "a"]    ));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\d = {"a": 1, "b": 2}        \\list(reversed(d.keys())) == ["b", "a"]    ));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\d = {"a": 1, "b": 2}        \\list(reversed(d.values())) == [2, 1]    ));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\d = {"a": 1, "b": 2}        \\list(reversed(d.items())) == [("b", 2), ("a", 1)]    ));}test "execute reversed builtin consumes with next and for loops" {    try std.testing.expectEqual(object.Value{ .integer = 21 }, try executeValue(std.testing.allocator,        \\it = reversed([1, 2])        \\next(it) * 10 + next(it)    ));    try std.testing.expectEqual(object.Value{ .integer = 210 }, try executeValue(std.testing.allocator,        \\total = 0        \\for value in reversed(range(3)):        \\    total = total * 10 + value        \\total    ));}test "execute reversed builtin errors and shadowing" {    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "reversed()"));    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "reversed([], [])"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "reversed(1)"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "reversed(iter([1]))"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,        \\reversed = 4        \\reversed([])    ));}test "execute list builtin consumes iterators" {    try std.testing.expectEqual(object.Value{ .integer = 13 }, try executeValue(std.testing.allocator,        \\it = iter(range(4))        \\first = next(it)        \\xs = list(it)        \\first * 100 + xs[0] * 10 + len(xs)    ));}test "execute dictionary displays" {    {        var result = try execute(std.testing.allocator, "{}");        defer result.deinit();        try std.testing.expect(result.value == .dict);        try std.testing.expectEqual(@as(usize, 0), result.value.dict.entries.items.len);    }    {        var result = try execute(std.testing.allocator, "{\"a\": 1, \"b\": 2}");        defer result.deinit();        try std.testing.expect(result.value == .dict);        try std.testing.expectEqual(@as(usize, 2), result.value.dict.entries.items.len);        try expectString("a", result.value.dict.entries.items[0].key);        try std.testing.expectEqual(object.Value{ .integer = 1 }, result.value.dict.entries.items[0].value);        try expectString("b", result.value.dict.entries.items[1].key);        try std.testing.expectEqual(object.Value{ .integer = 2 }, result.value.dict.entries.items[1].value);    }}test "execute dictionary duplicate keys replace values" {    try std.testing.expectEqual(object.Value{ .integer = 12 }, try executeValue(std.testing.allocator,        \\d = {"a": 1, "a": 2}        \\len(d) * 10 + d["a"]    ));    try std.testing.expectEqual(object.Value{ .integer = 12 }, try executeValue(std.testing.allocator,        \\d = {True: 1, 1: 2}        \\len(d) * 10 + d[True]    ));}test "execute dictionary lookup assignment truthiness and equality" {    try std.testing.expectEqual(object.Value{ .integer = 2 }, try executeValue(std.testing.allocator, "{\"a\": 2}[\"a\"]"));    try std.testing.expectEqual(object.Value{ .integer = 12 }, try executeValue(std.testing.allocator,        \\d = {}        \\if d:        \\    empty = 1        \\else:        \\    empty = 2        \\d["a"] = 7        \\if d:        \\    full = 1        \\else:        \\    full = 2        \\full * 10 + empty    ));    try std.testing.expectEqual(object.Value{ .integer = 37 }, try executeValue(std.testing.allocator,        \\d = {"a": 1, "b": 2}        \\d["a"] = 3        \\keys = list(d)        \\if keys == ["a", "b"]:        \\    order = 30        \\else:        \\    order = 0        \\order + d["a"] + d["b"] * 2    ));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "{\"a\": 1, \"b\": 2} == {\"b\": 2, \"a\": 1}"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "{\"a\": 1} != {\"a\": 2}"));}test "execute dictionary iteration membership and builtins" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "\"a\" in {\"a\": 1}"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "1 not in {\"a\": 1}"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "list({\"a\": 1, \"b\": 2}) == [\"a\", \"b\"]"));    try expectString("a", try executeValue(std.testing.allocator, "next(iter({\"a\": 1}))"));    try std.testing.expectEqual(object.Value{ .integer = 2 }, try executeValue(std.testing.allocator,        \\total = 0        \\for key in {"a": 1, "b": 2}:        \\    total = total + len(key)        \\total    ));    try std.testing.expectEqual(object.Value{ .integer = 7 }, try executeValue(std.testing.allocator,        \\d = {"a": 1}        \\copy = dict(d)        \\d["a"] = 7        \\copy["a"] + d["a"] - 1    ));    try std.testing.expectEqual(object.Value{ .integer = 12 }, try executeValue(std.testing.allocator,        \\d = dict([("a", 1), ["b", 2]])        \\d["a"] * 10 + d["b"]    ));    try std.testing.expectEqual(object.Value{ .integer = 0 }, try executeValue(std.testing.allocator, "len(dict())"));}test "execute dictionary key errors" {    try std.testing.expectError(Error.KeyError, executeValue(std.testing.allocator, "{\"a\": 1}[\"b\"]"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "{[]: 1}"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "{([],): 1}"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,        \\d = {}        \\d[[]] = 1    ));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "[] in {}"));}test "execute dictionary key deletion" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\d = {"a": 1, "b": 2, "c": 3}        \\del d["b"]        \\list(d) == ["a", "c"]    ));    try std.testing.expectEqual(object.Value{ .integer = 31 }, try executeValue(std.testing.allocator,        \\d = {"a": 1, "b": 2}        \\del d["a"]        \\len(d) * 10 + d["b"] * 10 + ("a" not in d)    ));    try std.testing.expectEqual(object.Value{ .integer = 0 }, try executeValue(std.testing.allocator,        \\d = {True: 1, 1: 2}        \\del d[True]        \\len(d)    ));}test "execute dictionary key deletion errors" {    try std.testing.expectError(Error.KeyError, executeValue(std.testing.allocator,        \\d = {"a": 1}        \\del d["b"]    ));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,        \\d = {}        \\del d[[]]    ));}test "execute dictionary get method" {    try std.testing.expectEqual(object.Value{ .integer = 121 }, try executeValue(std.testing.allocator,        \\d = {"a": 1}        \\d.get("a") * 100 + d.get("b", 2) * 10 + (d.get("b") is None)    ));}test "execute stored bound dictionary methods" {    try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator,        \\d = {}        \\get = d.get        \\d["a"] = 3        \\get("a")    ));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\d = {}        \\first = d.get        \\second = d.get        \\first == second and not first is second    ));    try std.testing.expectEqual(object.Value{ .integer = 7 }, try executeValue(std.testing.allocator,        \\d = {}        \\outer = {d.get: 7}        \\outer[d.get]    ));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\d = {}        \\update = d.update        \\setdefault = d.setdefault        \\popitem = d.popitem        \\update([("a", 1)])        \\setdefault("b", 2)        \\popitem() == ("b", 2) and d.update == d.update and d.setdefault == d.setdefault and d.popitem == d.popitem    ));}test "execute dictionary pop method" {    try std.testing.expectEqual(object.Value{ .integer = 124 }, try executeValue(std.testing.allocator,        \\d = {"a": 1, "b": 2, "c": 3}        \\value = d.pop("b")        \\missing = d.pop("z", 4)        \\keys = list(d)        \\if keys == ["a", "c"]:        \\    order = 100        \\else:        \\    order = 0        \\order + value * 10 + missing    ));}test "execute dictionary setdefault method" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\d = {"a": 1}        \\first = d.setdefault("a", 9)        \\second = d.setdefault("b", 2)        \\third = d.setdefault("c")        \\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"]    ));}test "execute dictionary update method" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\d = {"a": 1, "b": 2}        \\result = d.update({"b": 20, "c": 3})        \\result is None and list(d.items()) == [("a", 1), ("b", 20), ("c", 3)]    ));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\d = {"a": 1}        \\d.update([("a", 10), ["b", 2]])        \\d.update({"c": 3}.items())        \\d.update(iter([("d", 4)]))        \\list(d.items()) == [("a", 10), ("b", 2), ("c", 3), ("d", 4)]    ));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\d = {"a": 1}        \\result = d.update()        \\result is None and list(d.items()) == [("a", 1)]    ));}test "execute dictionary popitem method" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\d = {"a": 1, "b": 2, "c": 3}        \\first = d.popitem()        \\second = d.popitem()        \\first == ("c", 3) and second == ("b", 2) and list(d.items()) == [("a", 1)]    ));}test "execute dictionary clear and copy methods" {    try std.testing.expectEqual(object.Value{ .integer = 17 }, try executeValue(std.testing.allocator,        \\d = {"a": 1}        \\copy = d.copy()        \\d["a"] = 7        \\copy["a"] * 10 + d["a"]    ));    try std.testing.expectEqual(object.Value{ .integer = 5 }, try executeValue(std.testing.allocator,        \\inner = []        \\d = {"a": inner}        \\copy = d.copy()        \\copy["a"].append(5)        \\d["a"][0]    ));    try std.testing.expectEqual(object.Value{ .integer = 0 }, try executeValue(std.testing.allocator,        \\d = {"a": 1}        \\result = d.clear()        \\if result is None:        \\    len(d)        \\else:        \\    9    ));}test "execute dictionary method errors" {    try std.testing.expectError(Error.AttributeError, executeValue(std.testing.allocator, "{}.missing"));    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.get()"));    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.get(1, 2, 3)"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "{}.get([])"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "{}.get([], 1)"));    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.pop()"));    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.pop(1, 2, 3)"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "{}.pop([])"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "{}.pop([], 1)"));    try std.testing.expectError(Error.KeyError, executeValue(std.testing.allocator, "{}.pop(\"a\")"));    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.setdefault()"));    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.setdefault(1, 2, 3)"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "{}.setdefault([])"));    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.update(1, 2)"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "{}.update(1)"));    try std.testing.expectError(Error.ValueError, executeValue(std.testing.allocator, "{}.update([[1]])"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "{}.update([1])"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "{}.update([([], 1)])"));    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.popitem(1)"));    try std.testing.expectError(Error.KeyError, executeValue(std.testing.allocator, "{}.popitem()"));    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.clear(1)"));    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.copy(1)"));}test "execute dictionary view iteration and length" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\d = {"a": 1, "b": 2}        \\list(d.keys()) == ["a", "b"] and list(d.values()) == [1, 2] and list(d.items()) == [("a", 1), ("b", 2)]    ));    try std.testing.expectEqual(object.Value{ .integer = 222 }, try executeValue(std.testing.allocator,        \\d = {"a": 1, "b": 2}        \\len(d.keys()) * 100 + len(d.values()) * 10 + len(d.items())    ));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\tuple({"a": 1}.items()) == (("a", 1),)    ));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\d = {"a": 1}        \\dict(d.items()) == d    ));}test "execute dictionary views reflect mutation" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\d = {"a": 1}        \\keys = d.keys()        \\values = d.values()        \\items = d.items()        \\d["b"] = 2        \\list(keys) == ["a", "b"] and list(values) == [1, 2] and list(items) == [("a", 1), ("b", 2)]    ));}test "execute dictionary views in loops and membership" {    try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator,        \\total = 0        \\for value in {"a": 1, "b": 2}.values():        \\    total = total + value        \\total    ));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\d = {"a": 1, "b": 2}        \\"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()    ));}test "execute dictionary view equality and truthiness" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\{"a": 1, "b": 2}.keys() == {"b": 9, "a": 8}.keys()    ));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\{"a": 1}.items() == {"a": 1}.items()    ));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\d = {"a": 1}        \\values = d.values()        \\values == values and d.values() != d.values()    ));    try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator,        \\if {}.keys():        \\    total = 9        \\else:        \\    total = 1        \\if {"a": 1}.values():        \\    total = total + 2        \\total    ));}test "execute stored bound dictionary view methods" {    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\d = {"a": 1}        \\keys = d.keys        \\list(keys()) == ["a"]    ));}test "execute dictionary view errors" {    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.keys(1)"));    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.values(1)"));    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "{}.items(1)"));    try std.testing.expectError(Error.AttributeError, executeValue(std.testing.allocator, "{}.keys().missing"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,        \\d = {}        \\outer = {d.keys(): 1}    ));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "([], 1) in {\"a\": 1}.items()"));}test "execute dictionary builtin errors and ordering errors" {    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "dict(1, 2)"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "dict(1)"));    try std.testing.expectError(Error.ValueError, executeValue(std.testing.allocator, "dict([[1]])"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "dict([1])"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,        \\dict = 4        \\dict()    ));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "{} < {}"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "[{}] <= [{}]"));}test "execute tuple builtin creates tuples" {    {        var result = try execute(std.testing.allocator, "tuple()");        defer result.deinit();        try std.testing.expect(result.value == .tuple);        try std.testing.expectEqual(@as(usize, 0), result.value.tuple.items.len);    }    {        var result = try execute(std.testing.allocator, "tuple([1, 2])");        defer result.deinit();        try std.testing.expect(result.value == .tuple);        try std.testing.expectEqual(@as(usize, 2), result.value.tuple.items.len);        try std.testing.expectEqual(object.Value{ .integer = 1 }, result.value.tuple.items[0]);        try std.testing.expectEqual(object.Value{ .integer = 2 }, result.value.tuple.items[1]);    }    try std.testing.expectEqual(object.Value{ .integer = 3 }, try executeValue(std.testing.allocator, "len(tuple(range(3)))"));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator, "tuple(\"ab\")[1] == \"b\""));}test "execute tuple builtin consumes iterators and preserves tuples" {    try std.testing.expectEqual(object.Value{ .integer = 12 }, try executeValue(std.testing.allocator,        \\it = iter([1, 2])        \\first = next(it)        \\xs = tuple(it)        \\first * 10 + xs[0]    ));    try std.testing.expectEqual(object.Value{ .boolean = true }, try executeValue(std.testing.allocator,        \\xs = (1, 2)        \\tuple(xs) is xs    ));}test "execute tuple builtin errors and shadowing" {    try std.testing.expectError(Error.ArityMismatch, executeValue(std.testing.allocator, "tuple(1, 2)"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator, "tuple(1)"));    try std.testing.expectError(Error.TypeError, executeValue(std.testing.allocator,        \\tuple = 4        \\tuple()    ));}test "execute elif chains" {    try expectString("middle", try executeValue(std.testing.allocator,        \\x = 2        \\if x == 1:        \\    y = "first"        \\elif x == 2:        \\    y = "middle"        \\else:        \\    y = "last"        \\y    ));}test "execute while else on natural exhaustion" {    try expectString("done", try executeValue(std.testing.allocator,        \\x = 0        \\while x < 3:        \\    x = x + 1        \\else:        \\    y = "done"        \\y    ));}test "execute while else skipped by break" {    try expectString("break", try executeValue(std.testing.allocator,        \\x = 0        \\y = "start"        \\while x < 5:        \\    x = x + 1        \\    if x == 3:        \\        y = "break"        \\        break        \\else:        \\    y = "else"        \\y    ));}test "execute while else after continue" {    try expectString("done", try executeValue(std.testing.allocator,        \\x = 0        \\while x < 3:        \\    x = x + 1        \\    if x < 3:        \\        continue        \\else:        \\    y = "done"        \\y    ));}fn expectString(expected: []const u8, actual: object.Value) !void {    try std.testing.expect(actual == .string);    try std.testing.expectEqualStrings(expected, actual.string);}fn expectExecutedString(expected: []const u8, bytes: []const u8) !void {    var result = try execute(std.testing.allocator, bytes);    defer result.deinit();    try expectString(expected, result.value);}

Complete call list for runtime.Vm.run

26 direct calls.

Audit

Definitions7
Public names13
Members17
Version26.7.0
Revisiondaab053ee433