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