Skip to documentation
SLOP

tiny.python.compile.compiler

Reference 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.

Types and contracts

Public types and contracts.

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

Source

Called byCallsNo direct callstest sourcelib.python.src.compile.compilertest: compile emits attribute callstest sourcelib.python.src.compile.compilertest: compile emits bytecodetest sourcelib.python.src.compile.compilertest: compile emits deletion operatio...test sourcelib.python.src.compile.compilertest: compile emits dictionary operat...test sourcelib.python.src.compile.compilertest: compile emits jumps for control...+11 morecompile.compilercompile
Static calls · unresolved targets: 0 · external targets: 3.

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.

Audit

Definitions3
Public names4
Members0
Version26.7.0
Revisiondaab053ee433