tiny.python.compile.compiler
Defined in compile.
The compiler walks a program's syntax tree once, in source order, and appends stack-machine instructions to a chunk as it goes.
API (2)
Actions
Public operations.
compile: Compiles a parsed program into a new top-level chunk that ends withret.
Types and contracts
Public types and contracts.
Error: The errorscompilereturns: four for programs it rejects, anderror.OutOfMemory.
Source
Source: lib/python/src/compile/compiler.zig
zig
//! The compiler walks a program's syntax tree once, in source order, and appends stack-machine//! instructions to a chunk as it goes.//!//! The compiler has to turn nested control flow into jumps, keep the value stack balanced on every//! path, and reject programs whose `break`, `continue`, `return` or `def` lacks a valid place.//!//! A jump forward goes to code yet to be generated, so its target is unknown when the jump is//! emitted. A `for` loop keeps its iterator on the value stack while the body runs, so leaving the//! loop early by `break` or `return` has to drop it. Python's `and` and `or` return the operand//! that decided them, and when the left operand decides, the right one is skipped. A chained//! comparison computes each middle operand once and stops at the first false result.//!//! The compiled code follows the evaluation rules of the [Python 3.14 language//! reference](https://docs.python.org/3.14/reference/) for these constructs, and the tests check//! them: `and` and `or` return an operand, a chained comparison short-circuits, and a loop's `else`//! block runs when the loop ends without `break`.//!//! The compiler emits a forward jump with a placeholder target, keeps the jump's position, and//! fills in the target once the code after the jump exists. Each loop records how many values a//! `break` has to pop and the positions of the jumps that `break` emitted, so that, when the loop//! ends, it can point those jumps past its `else` block. A `return` pops the iterators of every//! loop around it before it returns. At top level, an expression statement records its value with//! `save`, so the program's value is the last one recorded. Inside a function, the compiler pops an//! expression statement's value. A `def` inside a function body fails with `NestedFunction`, so//! every function lives in the top-level chunk. The chunk borrows names, parameter lists and string//! constants from the syntax tree and the source text, so both have to outlive it.const std = @import("std");const syntax = @import("../syntax/root.zig");const code = @import("../code/root.zig");const CompileError = error{ BreakOutsideLoop, ContinueOutsideLoop, NestedFunction, TopLevelReturn,};/// The errors `compile` returns: four for programs it rejects, and `error.OutOfMemory`. A caller/// switches on this error set to report why a parsed program failed to compile. `BreakOutsideLoop`/// and `ContinueOutsideLoop` mean a `break` or `continue` outside every loop body and every loop's/// `else` block. `NestedFunction` means a `def` inside a function body. `TopLevelReturn` means a/// `return` outside every function. An error carries no position in the source.pub const Error = CompileError || std.mem.Allocator.Error;/// Compiles a parsed program into a new top-level chunk that ends with `ret`. A caller that wants/// the bytecode of a program calls this function directly, and the package's `execute` calls it/// after `parse`. The function reads the program without changing it. The call allocates the/// chunk's lists and function bodies from the given allocator. The caller owns the result and frees/// it with `Chunk.deinit` and the same allocator. The chunk borrows names, parameter lists and/// string constants from the program's syntax tree and from the source text, so both have to/// outlive it. The call returns `BreakOutsideLoop`, `ContinueOutsideLoop`, `NestedFunction` or/// `TopLevelReturn` for a program it rejects, and `error.OutOfMemory` when an allocation fails. On/// any error the function frees everything it allocated.pub fn compile(allocator: std.mem.Allocator, program: *const syntax.Program) Error!code.Chunk { var compiler = Compiler{ .allocator = allocator, .chunk = .{}, .loop = null, .top_level = true, }; errdefer compiler.chunk.deinit(allocator); try compiler.statements(program.statements); try compiler.chunk.emit(allocator, .{ .op = .ret }); return compiler.chunk;}const Compiler = struct { allocator: std.mem.Allocator, chunk: code.Chunk, loop: ?*Loop, top_level: bool, fn statements(self: *Compiler, statement_nodes: []const syntax.Statement) Error!void { for (statement_nodes) |statement_node| try self.statement(statement_node); } fn statement(self: *Compiler, statement_node: syntax.Statement) Error!void { switch (statement_node) { .expression => |expression_node| { try self.expression(expression_node); try self.chunk.emit(self.allocator, .{ .op = if (self.top_level) .save else .pop }); }, .assign => |assign| { try self.expression(assign.value); const name = try self.chunk.addName(self.allocator, assign.name); try self.chunk.emit(self.allocator, .{ .op = .store, .operand = name }); }, .subscript_assign => |assign| { try self.expression(assign.target); try self.expression(assign.index); try self.expression(assign.value); try self.chunk.emit(self.allocator, .{ .op = .store_subscript }); }, .delete => |delete| switch (delete) { .name => |name_value| { const name = try self.chunk.addName(self.allocator, name_value); try self.chunk.emit(self.allocator, .{ .op = .delete, .operand = name }); }, .subscript => |subscript| { try self.expression(subscript.target); try self.expression(subscript.index); try self.chunk.emit(self.allocator, .{ .op = .delete_subscript }); }, }, .break_stmt => try self.breakStatement(), .continue_stmt => try self.continueStatement(), .function => |function_node| try self.function(function_node), .for_stmt => |for_stmt| try self.forStatement(for_stmt), .if_stmt => |if_stmt| try self.ifStatement(if_stmt), .pass => {}, .return_stmt => |return_stmt| { if (self.top_level) return Error.TopLevelReturn; try self.emitLoopCleanup(); if (return_stmt.value) |value| { try self.expression(value); } else { const none = try self.chunk.addConstant(self.allocator, .none); try self.chunk.emit(self.allocator, .{ .op = .constant, .operand = none }); } try self.chunk.emit(self.allocator, .{ .op = .return_value }); }, .while_stmt => |while_stmt| try self.whileStatement(while_stmt), } } fn function(self: *Compiler, function_node: syntax.ast.Function) Error!void { if (!self.top_level) return Error.NestedFunction; var child = Compiler{ .allocator = self.allocator, .chunk = .{}, .loop = null, .top_level = false, }; var child_owned = true; errdefer if (child_owned) child.chunk.deinit(self.allocator); try child.statements(function_node.body); const none = try child.chunk.addConstant(self.allocator, .none); try child.chunk.emit(self.allocator, .{ .op = .constant, .operand = none }); try child.chunk.emit(self.allocator, .{ .op = .return_value }); var function_value = code.Function{ .name = function_node.name, .params = function_node.params, .chunk = child.chunk, }; child_owned = false; var owned = true; errdefer if (owned) function_value.deinit(self.allocator); const function_index = try self.chunk.addFunction(self.allocator, function_value); owned = false; const constant = try self.chunk.addConstant(self.allocator, .{ .function = function_index }); try self.chunk.emit(self.allocator, .{ .op = .constant, .operand = constant }); const name = try self.chunk.addName(self.allocator, function_node.name); try self.chunk.emit(self.allocator, .{ .op = .store, .operand = name }); } fn forStatement(self: *Compiler, for_stmt: syntax.ast.ForStatement) Error!void { try self.expression(for_stmt.iterable); try self.chunk.emit(self.allocator, .{ .op = .iter }); const loop_start = self.chunk.instructions.items.len; const exit_jump = try self.emitJump(.for_next); const name = try self.chunk.addName(self.allocator, for_stmt.name); try self.chunk.emit(self.allocator, .{ .op = .store, .operand = name }); var loop = Loop{ .parent = self.loop, .continue_target = loop_start, .break_pops = 1, }; defer loop.break_jumps.deinit(self.allocator); self.loop = &loop; defer self.loop = loop.parent; try self.statements(for_stmt.body); try self.emitJumpTo(loop_start); self.patchJump(exit_jump); try self.statements(for_stmt.otherwise); for (loop.break_jumps.items) |jump| self.patchJump(jump); } fn ifStatement(self: *Compiler, if_stmt: syntax.ast.IfStatement) Error!void { try self.expression(if_stmt.condition); const false_jump = try self.emitJump(.jump_if_false); try self.chunk.emit(self.allocator, .{ .op = .pop }); try self.statements(if_stmt.body); if (if_stmt.otherwise.len > 0) { const end_jump = try self.emitJump(.jump); self.patchJump(false_jump); try self.chunk.emit(self.allocator, .{ .op = .pop }); try self.statements(if_stmt.otherwise); self.patchJump(end_jump); } else { const end_jump = try self.emitJump(.jump); self.patchJump(false_jump); try self.chunk.emit(self.allocator, .{ .op = .pop }); self.patchJump(end_jump); } } fn whileStatement(self: *Compiler, while_stmt: syntax.ast.WhileStatement) Error!void { const loop_start = self.chunk.instructions.items.len; try self.expression(while_stmt.condition); const exit_jump = try self.emitJump(.jump_if_false); try self.chunk.emit(self.allocator, .{ .op = .pop }); var loop = Loop{ .parent = self.loop, .continue_target = loop_start, }; defer loop.break_jumps.deinit(self.allocator); self.loop = &loop; defer self.loop = loop.parent; try self.statements(while_stmt.body); try self.emitJumpTo(loop_start); self.patchJump(exit_jump); try self.chunk.emit(self.allocator, .{ .op = .pop }); try self.statements(while_stmt.otherwise); for (loop.break_jumps.items) |jump| self.patchJump(jump); } fn breakStatement(self: *Compiler) Error!void { const loop = self.loop orelse return Error.BreakOutsideLoop; try self.emitPops(loop.break_pops); const jump = try self.emitJump(.jump); try loop.break_jumps.append(self.allocator, jump); } fn continueStatement(self: *Compiler) Error!void { const loop = self.loop orelse return Error.ContinueOutsideLoop; try self.emitJumpTo(loop.continue_target); } fn emitLoopCleanup(self: *Compiler) std.mem.Allocator.Error!void { var count: usize = 0; var current = self.loop; while (current) |loop| { count += loop.break_pops; current = loop.parent; } try self.emitPops(count); } fn emitPops(self: *Compiler, count: usize) std.mem.Allocator.Error!void { for (0..count) |_| try self.chunk.emit(self.allocator, .{ .op = .pop }); } fn emitJump(self: *Compiler, op: code.Op) std.mem.Allocator.Error!usize { try self.chunk.emit(self.allocator, .{ .op = op }); return self.chunk.instructions.items.len - 1; } fn emitJumpTo(self: *Compiler, target: usize) std.mem.Allocator.Error!void { try self.chunk.emit(self.allocator, .{ .op = .jump, .operand = target }); } fn patchJump(self: *Compiler, instruction_index: usize) void { self.chunk.instructions.items[instruction_index].operand = self.chunk.instructions.items.len; } fn expression(self: *Compiler, expression_node: *const syntax.Expression) Error!void { switch (expression_node.*) { .none => { const constant = try self.chunk.addConstant(self.allocator, .none); try self.chunk.emit(self.allocator, .{ .op = .constant, .operand = constant }); }, .boolean => |value| { const constant = try self.chunk.addConstant(self.allocator, .{ .boolean = value }); try self.chunk.emit(self.allocator, .{ .op = .constant, .operand = constant }); }, .integer => |value| { const constant = try self.chunk.addConstant(self.allocator, .{ .integer = value }); try self.chunk.emit(self.allocator, .{ .op = .constant, .operand = constant }); }, .string => |value| { const constant = try self.chunk.addConstant(self.allocator, .{ .string = value }); try self.chunk.emit(self.allocator, .{ .op = .constant, .operand = constant }); }, .name => |name| { const index = try self.chunk.addName(self.allocator, name); try self.chunk.emit(self.allocator, .{ .op = .load, .operand = index }); }, .unary => |unary| { try self.expression(unary.operand); switch (unary.op) { .negate => try self.chunk.emit(self.allocator, .{ .op = .neg }), .not => try self.chunk.emit(self.allocator, .{ .op = .not }), } }, .binary => |binary| { try self.expression(binary.left); try self.expression(binary.right); try self.chunk.emit(self.allocator, .{ .op = switch (binary.op) { .add => .add, .sub => .sub, .mul => .mul, } }); }, .comparison => |comparison_node| try self.comparison(comparison_node), .logical => |logical_node| try self.logical(logical_node), .call => |call| { try self.expression(call.target); for (call.arguments) |argument| try self.expression(argument); try self.chunk.emit(self.allocator, .{ .op = .call, .operand = call.arguments.len }); }, .attribute => |attribute| { try self.expression(attribute.target); const index = try self.chunk.addName(self.allocator, attribute.name); try self.chunk.emit(self.allocator, .{ .op = .attribute, .operand = index }); }, .list => |list| { for (list.items) |item| try self.expression(item); try self.chunk.emit(self.allocator, .{ .op = .build_list, .operand = list.items.len }); }, .tuple => |tuple| { for (tuple.items) |item| try self.expression(item); try self.chunk.emit(self.allocator, .{ .op = .build_tuple, .operand = tuple.items.len }); }, .dict => |dict| { for (dict.items) |item| { try self.expression(item.key); try self.expression(item.value); } try self.chunk.emit(self.allocator, .{ .op = .build_dict, .operand = dict.items.len }); }, .subscript => |subscript| { try self.expression(subscript.target); switch (subscript.selector) { .index => |index| { try self.expression(index); try self.chunk.emit(self.allocator, .{ .op = .subscript }); }, .slice => |slice| { try self.optionalExpression(slice.start); try self.optionalExpression(slice.stop); try self.optionalExpression(slice.step); try self.chunk.emit(self.allocator, .{ .op = .slice }); }, } }, } } fn optionalExpression(self: *Compiler, expression_node: ?*const syntax.Expression) Error!void { if (expression_node) |node| { try self.expression(node); } else { const constant = try self.chunk.addConstant(self.allocator, .none); try self.chunk.emit(self.allocator, .{ .op = .constant, .operand = constant }); } } fn comparison(self: *Compiler, comparison_node: syntax.ast.Comparison) Error!void { try self.expression(comparison_node.left); var false_jumps = std.ArrayListUnmanaged(usize).empty; defer false_jumps.deinit(self.allocator); for (comparison_node.terms, 0..) |term, index| { const last = index + 1 == comparison_node.terms.len; try self.expression(term.right); if (!last) { try self.chunk.emit(self.allocator, .{ .op = .dup }); try self.chunk.emit(self.allocator, .{ .op = .rotate_three }); } try self.chunk.emit(self.allocator, .{ .op = comparisonOp(term.op) }); if (!last) { const false_jump = try self.emitJump(.jump_if_false); try false_jumps.append(self.allocator, false_jump); try self.chunk.emit(self.allocator, .{ .op = .pop }); } } if (false_jumps.items.len > 0) { const end_jump = try self.emitJump(.jump); for (false_jumps.items) |jump| self.patchJump(jump); try self.chunk.emit(self.allocator, .{ .op = .swap }); try self.chunk.emit(self.allocator, .{ .op = .pop }); self.patchJump(end_jump); } } fn comparisonOp(op: syntax.ast.ComparisonOp) code.Op { return switch (op) { .equal => .equal, .not_equal => .not_equal, .less => .less, .less_equal => .less_equal, .greater => .greater, .greater_equal => .greater_equal, .contains => .contains, .not_contains => .not_contains, .identical => .identical, .not_identical => .not_identical, }; } fn logical(self: *Compiler, logical_node: syntax.ast.Logical) Error!void { switch (logical_node.op) { .and_op => { try self.expression(logical_node.left); const false_jump = try self.emitJump(.jump_if_false); try self.chunk.emit(self.allocator, .{ .op = .pop }); try self.expression(logical_node.right); self.patchJump(false_jump); }, .or_op => { try self.expression(logical_node.left); const false_jump = try self.emitJump(.jump_if_false); const end_jump = try self.emitJump(.jump); self.patchJump(false_jump); try self.chunk.emit(self.allocator, .{ .op = .pop }); try self.expression(logical_node.right); self.patchJump(end_jump); }, } }};const Loop = struct { parent: ?*Loop, continue_target: usize, break_pops: usize = 0, break_jumps: std.ArrayListUnmanaged(usize) = .empty,};test "compile emits bytecode" { const bytes = "x = 1\nx + 2"; var stream = try @import("../source/root.zig").tokenize(std.testing.allocator, bytes); defer stream.deinit(std.testing.allocator); var program = try @import("../syntax/root.zig").parse(std.testing.allocator, bytes, stream.tokens); defer program.deinit(); var chunk_value = try compile(std.testing.allocator, &program); defer chunk_value.deinit(std.testing.allocator); try std.testing.expect(chunk_value.instructions.items.len > 0); try std.testing.expectEqualStrings("x", chunk_value.names.items[0]);}test "compile rejects top level return" { const bytes = "return 1"; var stream = try @import("../source/root.zig").tokenize(std.testing.allocator, bytes); defer stream.deinit(std.testing.allocator); var program = try @import("../syntax/root.zig").parse(std.testing.allocator, bytes, stream.tokens); defer program.deinit(); try std.testing.expectError(Error.TopLevelReturn, compile(std.testing.allocator, &program));}test "compile rejects loop control outside loops" { { const bytes = "break"; var stream = try @import("../source/root.zig").tokenize(std.testing.allocator, bytes); defer stream.deinit(std.testing.allocator); var program = try @import("../syntax/root.zig").parse(std.testing.allocator, bytes, stream.tokens); defer program.deinit(); try std.testing.expectError(Error.BreakOutsideLoop, compile(std.testing.allocator, &program)); } { const bytes = "continue"; var stream = try @import("../source/root.zig").tokenize(std.testing.allocator, bytes); defer stream.deinit(std.testing.allocator); var program = try @import("../syntax/root.zig").parse(std.testing.allocator, bytes, stream.tokens); defer program.deinit(); try std.testing.expectError(Error.ContinueOutsideLoop, compile(std.testing.allocator, &program)); }}test "compile emits jumps for control flow" { const bytes = \\x = 0 \\while x < 3: \\ if x == 1: \\ pass \\ else: \\ x = x + 1 \\ x = x + 1 \\x ; var stream = try @import("../source/root.zig").tokenize(std.testing.allocator, bytes); defer stream.deinit(std.testing.allocator); var program = try @import("../syntax/root.zig").parse(std.testing.allocator, bytes, stream.tokens); defer program.deinit(); var chunk_value = try compile(std.testing.allocator, &program); defer chunk_value.deinit(std.testing.allocator); var has_jump = false; var has_jump_if_false = false; for (chunk_value.instructions.items) |instruction| { if (instruction.op == .jump) has_jump = true; if (instruction.op == .jump_if_false) has_jump_if_false = true; } try std.testing.expect(has_jump); try std.testing.expect(has_jump_if_false);}test "compile emits jumps for logical operators" { const bytes = "x = True or missing\nx and 7"; var stream = try @import("../source/root.zig").tokenize(std.testing.allocator, bytes); defer stream.deinit(std.testing.allocator); var program = try @import("../syntax/root.zig").parse(std.testing.allocator, bytes, stream.tokens); defer program.deinit(); var chunk_value = try compile(std.testing.allocator, &program); defer chunk_value.deinit(std.testing.allocator); var jumps: usize = 0; var false_jumps: usize = 0; for (chunk_value.instructions.items) |instruction| { if (instruction.op == .jump) jumps += 1; if (instruction.op == .jump_if_false) false_jumps += 1; } try std.testing.expect(jumps >= 1); try std.testing.expect(false_jumps >= 2);}test "compile emits stack operations for chained comparisons" { const bytes = "1 < x <= y"; var stream = try @import("../source/root.zig").tokenize(std.testing.allocator, bytes); defer stream.deinit(std.testing.allocator); var program = try @import("../syntax/root.zig").parse(std.testing.allocator, bytes, stream.tokens); defer program.deinit(); var chunk_value = try compile(std.testing.allocator, &program); defer chunk_value.deinit(std.testing.allocator); var has_dup = false; var has_rotate = false; var has_swap = false; for (chunk_value.instructions.items) |instruction| { if (instruction.op == .dup) has_dup = true; if (instruction.op == .rotate_three) has_rotate = true; if (instruction.op == .swap) has_swap = true; } try std.testing.expect(has_dup); try std.testing.expect(has_rotate); try std.testing.expect(has_swap);}test "compile emits membership and identity comparisons" { const bytes = "x in xs is not ys"; var stream = try @import("../source/root.zig").tokenize(std.testing.allocator, bytes); defer stream.deinit(std.testing.allocator); var program = try @import("../syntax/root.zig").parse(std.testing.allocator, bytes, stream.tokens); defer program.deinit(); var chunk_value = try compile(std.testing.allocator, &program); defer chunk_value.deinit(std.testing.allocator); var has_contains = false; var has_not_identical = false; for (chunk_value.instructions.items) |instruction| { if (instruction.op == .contains) has_contains = true; if (instruction.op == .not_identical) has_not_identical = true; } try std.testing.expect(has_contains); try std.testing.expect(has_not_identical);}test "compile emits list operations" { const bytes = "[1, 2][0]"; var stream = try @import("../source/root.zig").tokenize(std.testing.allocator, bytes); defer stream.deinit(std.testing.allocator); var program = try @import("../syntax/root.zig").parse(std.testing.allocator, bytes, stream.tokens); defer program.deinit(); var chunk_value = try compile(std.testing.allocator, &program); defer chunk_value.deinit(std.testing.allocator); var has_build_list = false; var has_subscript = false; for (chunk_value.instructions.items) |instruction| { if (instruction.op == .build_list) has_build_list = true; if (instruction.op == .subscript) has_subscript = true; } try std.testing.expect(has_build_list); try std.testing.expect(has_subscript);}test "compile emits attribute calls" { const bytes = "xs.append(1)"; var stream = try @import("../source/root.zig").tokenize(std.testing.allocator, bytes); defer stream.deinit(std.testing.allocator); var program = try @import("../syntax/root.zig").parse(std.testing.allocator, bytes, stream.tokens); defer program.deinit(); var chunk_value = try compile(std.testing.allocator, &program); defer chunk_value.deinit(std.testing.allocator); var has_attribute = false; var has_call = false; for (chunk_value.instructions.items) |instruction| { if (instruction.op == .attribute) has_attribute = true; if (instruction.op == .call) has_call = true; } try std.testing.expect(has_attribute); try std.testing.expect(has_call);}test "compile emits slice operations" { const bytes = "[1, 2, 3][1:]"; var stream = try @import("../source/root.zig").tokenize(std.testing.allocator, bytes); defer stream.deinit(std.testing.allocator); var program = try @import("../syntax/root.zig").parse(std.testing.allocator, bytes, stream.tokens); defer program.deinit(); var chunk_value = try compile(std.testing.allocator, &program); defer chunk_value.deinit(std.testing.allocator); var has_build_list = false; var has_slice = false; for (chunk_value.instructions.items) |instruction| { if (instruction.op == .build_list) has_build_list = true; if (instruction.op == .slice) has_slice = true; } try std.testing.expect(has_build_list); try std.testing.expect(has_slice);}test "compile emits tuple operations" { const bytes = "(1, 2)"; var stream = try @import("../source/root.zig").tokenize(std.testing.allocator, bytes); defer stream.deinit(std.testing.allocator); var program = try @import("../syntax/root.zig").parse(std.testing.allocator, bytes, stream.tokens); defer program.deinit(); var chunk_value = try compile(std.testing.allocator, &program); defer chunk_value.deinit(std.testing.allocator); var has_build_tuple = false; for (chunk_value.instructions.items) |instruction| { if (instruction.op == .build_tuple) has_build_tuple = true; } try std.testing.expect(has_build_tuple);}test "compile emits dictionary operations" { const bytes = "{\"a\": 1}"; var stream = try @import("../source/root.zig").tokenize(std.testing.allocator, bytes); defer stream.deinit(std.testing.allocator); var program = try @import("../syntax/root.zig").parse(std.testing.allocator, bytes, stream.tokens); defer program.deinit(); var chunk_value = try compile(std.testing.allocator, &program); defer chunk_value.deinit(std.testing.allocator); var has_build_dict = false; for (chunk_value.instructions.items) |instruction| { if (instruction.op == .build_dict) has_build_dict = true; } try std.testing.expect(has_build_dict);}test "compile emits list for loop operations" { const bytes = \\for x in [1]: \\ pass ; var stream = try @import("../source/root.zig").tokenize(std.testing.allocator, bytes); defer stream.deinit(std.testing.allocator); var program = try @import("../syntax/root.zig").parse(std.testing.allocator, bytes, stream.tokens); defer program.deinit(); var chunk_value = try compile(std.testing.allocator, &program); defer chunk_value.deinit(std.testing.allocator); var has_iter = false; var has_for_next = false; for (chunk_value.instructions.items) |instruction| { if (instruction.op == .iter) has_iter = true; if (instruction.op == .for_next) has_for_next = true; } try std.testing.expect(has_iter); try std.testing.expect(has_for_next);}test "compile emits subscript assignment" { const bytes = \\xs = [1] \\xs[0] = 2 ; var stream = try @import("../source/root.zig").tokenize(std.testing.allocator, bytes); defer stream.deinit(std.testing.allocator); var program = try @import("../syntax/root.zig").parse(std.testing.allocator, bytes, stream.tokens); defer program.deinit(); var chunk_value = try compile(std.testing.allocator, &program); defer chunk_value.deinit(std.testing.allocator); var has_store_subscript = false; for (chunk_value.instructions.items) |instruction| { if (instruction.op == .store_subscript) has_store_subscript = true; } try std.testing.expect(has_store_subscript);}test "compile emits deletion operations" { const bytes = \\del x \\del xs[0] ; var stream = try @import("../source/root.zig").tokenize(std.testing.allocator, bytes); defer stream.deinit(std.testing.allocator); var program = try @import("../syntax/root.zig").parse(std.testing.allocator, bytes, stream.tokens); defer program.deinit(); var chunk_value = try compile(std.testing.allocator, &program); defer chunk_value.deinit(std.testing.allocator); var has_delete = false; var has_delete_subscript = false; for (chunk_value.instructions.items) |instruction| { if (instruction.op == .delete) has_delete = true; if (instruction.op == .delete_subscript) has_delete_subscript = true; } try std.testing.expect(has_delete); try std.testing.expect(has_delete_subscript);}Source: lib/python/src/compile/root.zig:7
zig
pub const compiler = @import("compiler.zig");Complete caller list for compile.compiler.compile
16 direct callers.
lib.python.src.compile.compiler.test_compile_emits_attribute_calls[function] — test source atlib/python/src/compile/compiler.zig:570in nearest public ownertiny.python.compile.compilerlib.python.src.compile.compiler.test_compile_emits_bytecode[function] — test source atlib/python/src/compile/compiler.zig:419in nearest public ownertiny.python.compile.compilerlib.python.src.compile.compiler.test_compile_emits_deletion_operations[function] — test source atlib/python/src/compile/compiler.zig:681in nearest public ownertiny.python.compile.compilerlib.python.src.compile.compiler.test_compile_emits_dictionary_operations[function] — test source atlib/python/src/compile/compiler.zig:624in nearest public ownertiny.python.compile.compilerlib.python.src.compile.compiler.test_compile_emits_jumps_for_control_flow[function] — test source atlib/python/src/compile/compiler.zig:463in nearest public ownertiny.python.compile.compilerlib.python.src.compile.compiler.test_compile_emits_jumps_for_logical_operators[function] — test source atlib/python/src/compile/compiler.zig:491in nearest public ownertiny.python.compile.compilerlib.python.src.compile.compiler.test_compile_emits_list_for_loop_operations[function] — test source atlib/python/src/compile/compiler.zig:640in nearest public ownertiny.python.compile.compilerlib.python.src.compile.compiler.test_compile_emits_list_operations[function] — test source atlib/python/src/compile/compiler.zig:551in nearest public ownertiny.python.compile.compilerlib.python.src.compile.compiler.test_compile_emits_membership_and_identity_comparisons[function] — test source atlib/python/src/compile/compiler.zig:532in nearest public ownertiny.python.compile.compilerlib.python.src.compile.compiler.test_compile_emits_slice_operations[function] — test source atlib/python/src/compile/compiler.zig:589in nearest public ownertiny.python.compile.compilerlib.python.src.compile.compiler.test_compile_emits_stack_operations_for_chained_comparisons[function] — test source atlib/python/src/compile/compiler.zig:510in nearest public ownertiny.python.compile.compilerlib.python.src.compile.compiler.test_compile_emits_subscript_assignment[function] — test source atlib/python/src/compile/compiler.zig:662in nearest public ownertiny.python.compile.compilerlib.python.src.compile.compiler.test_compile_emits_tuple_operations[function] — test source atlib/python/src/compile/compiler.zig:608in nearest public ownertiny.python.compile.compilerlib.python.src.compile.compiler.test_compile_rejects_loop_control_outside_loops[function] — test source atlib/python/src/compile/compiler.zig:442in nearest public ownertiny.python.compile.compilerlib.python.src.compile.compiler.test_compile_rejects_top_level_return[function] — test source atlib/python/src/compile/compiler.zig:432in nearest public ownertiny.python.compile.compilertiny.python.runtime.vm.execute[function] atlib/python/src/runtime/vm.zig:106
Audit
| Definitions | 3 |
|---|---|
| Public names | 4 |
| Members | 0 |
| Version | 26.7.0 |
| Revision | daab053ee433 |