Skip to documentation
SLOP

tiny.python.code.chunk

Reference tiny.python code chunk

Defined in code.

A compiled program is a list of instructions plus three tables that the instructions refer to by position: constants, variable names and function bodies.

API (9)

Actions

Public operations.

Types and contracts

Public types and contracts.

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

Source

Called byCallsNo direct callscode.Functiondeinitcode.Chunkdeinit
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callerscode.Chunkdeinitcode.Functiondeinit
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/python/src/code/chunk.zig

zig
//! A compiled program is a list of instructions plus three tables that the instructions refer to by//! position: constants, variable names and function bodies. The compiler appends to it while it//! walks the syntax tree, and the virtual machine reads it at every step, so both sides need cheap//! appends and lookup by position. A name used many times in a program should take one slot, so//! that every read and write of it refers to the same position. A function body is a compiled//! program of its own, with its own instructions and tables, and a call has to find it from a value//! at run time.//!//! `Chunk` holds the instruction list and the three tables as four growable lists, `Instruction`//! pairs an operation with one integer operand, and `Function` holds a body's name, its parameter//! names and its own `Chunk`. `addName` searches the name table before it appends, so each distinct//! name gets one slot, and the search takes time linear in the number of names. A chunk owns its//! lists and its function bodies, and it borrows every name, parameter list and string constant//! from the syntax tree and the source text it was compiled from. A function value is the position//! of its body in the function table of the top-level chunk, and every body lives in that one table//! because the compiler rejects a `def` inside a function.const std = @import("std");const object = @import("../object/root.zig");const op = @import("op.zig");/// One instruction: an operation and one integer operand. The compiler emits one for each step of/// the program, and the virtual machine decodes one at every step. The meaning of the operand/// depends on the operation, and each `Op` tag's doc gives that meaning.pub const Instruction = struct {    /// The operation to perform.    op: op.Op,    /// The operation's integer argument: a table position, a count, or the position of another    /// instruction, depending on the operation. The default is 0, and an operation that takes no    /// argument ignores it. The virtual machine checks no table position against its table's    /// length, so a chunk built outside `compile` has to keep every position in range.    operand: usize = 0,};/// A compiled function: its name, its parameter names in order, and its body as a chunk of its own./// The compiler records each `def` statement as one, and a call finds it by its position in the/// top-level chunk. A call binds each argument to the parameter at the same position and runs the/// body in a new call frame.pub const Function = struct {    /// The function's name as the `def` statement spells it. The record borrows the name from the    /// source text. The `def` statement also binds the function to this name.    name: []const u8,    /// The function's parameter names, in the order the `def` statement lists them. The record    /// borrows this list from the syntax tree. A call has to pass exactly this many arguments, or    /// it fails with `ArityMismatch`. Each parameter becomes a local variable of the call.    params: []const []const u8,    /// The body's own chunk of instructions and tables. This record owns the chunk and frees it in    /// `deinit`. The compiler ends every body with instructions that return `None`, so a body that    /// runs off its end returns `None`.    chunk: Chunk,    /// Frees the body's chunk with the given allocator and leaves the record undefined.    /// `Chunk.deinit` calls it for each function the chunk owns. The call frees nothing that the    /// name and the parameter list point to, because the record borrows them.    pub fn deinit(self: *Function, allocator: std.mem.Allocator) void {        self.chunk.deinit(allocator);        self.* = undefined;    }};/// A compiled program or function body: its instructions and the tables of constants, names and/// functions that they index. `compile` returns one, the virtual machine runs it through a pointer,/// and the caller frees it with `deinit` once the run is over. A new chunk is `.{}`, with every/// table empty. Each table grows with the allocator passed to the method that appends to it. A/// chunk owns its four lists and its function bodies, and it borrows its names, parameter lists and/// string constants from the syntax tree and the source text, so both have to outlive it. Only the/// top-level chunk holds functions, because the compiler rejects a `def` inside a function body.pub const Chunk = struct {    /// The instructions, in the order they run. A jump's operand is a position in this list. The    /// virtual machine starts at position 0, and a frame that runs past the last instruction fails    /// with `InvalidFunction`.    instructions: std.ArrayListUnmanaged(Instruction) = .empty,    /// The literal values the instructions push: `None`, booleans, integers, strings and function    /// values. The `constant` operation's operand is a position in this list. A literal written    /// twice takes two slots, because `addConstant` appends without searching.    constants: std.ArrayListUnmanaged(object.Value) = .empty,    /// The distinct variable and attribute names the instructions use, each stored once. The    /// operands of `load`, `store`, `delete` and `attribute` are positions in this list.    names: std.ArrayListUnmanaged([]const u8) = .empty,    /// The compiled bodies of the program's `def` statements, in the order the compiler met them. A    /// function value is a position in this list, and the virtual machine reads only the top-level    /// chunk's list.    functions: std.ArrayListUnmanaged(Function) = .empty,    /// Frees each owned function body and then the four lists. The caller of `compile` defers it    /// once the chunk exists, as `execute` does. The given allocator has to be the one the lists    /// grew with. The call leaves the chunk undefined.    pub fn deinit(self: *Chunk, allocator: std.mem.Allocator) void {        for (self.functions.items) |*function| function.deinit(allocator);        self.instructions.deinit(allocator);        self.constants.deinit(allocator);        self.names.deinit(allocator);        self.functions.deinit(allocator);        self.* = undefined;    }    /// Appends one instruction to the end of the instruction list. The compiler appends each    /// instruction it generates with it. The call returns `error.OutOfMemory` when the list cannot    /// grow.    pub fn emit(self: *Chunk, allocator: std.mem.Allocator, instruction: Instruction) std.mem.Allocator.Error!void {        try self.instructions.append(allocator, instruction);    }    /// Appends a value to the constant list and returns its position. The compiler stores each    /// literal with it and then emits the `constant` operation that pushes it. The call returns    /// `error.OutOfMemory` when the list cannot grow.    pub fn addConstant(self: *Chunk, allocator: std.mem.Allocator, value: object.Value) std.mem.Allocator.Error!usize {        try self.constants.append(allocator, value);        return self.constants.items.len - 1;    }    /// Returns the position of a name in the name table, and appends the name first when the table    /// lacks it. The compiler turns every variable and attribute name into a table position with    /// it. The search compares bytes and walks the whole table, so it takes time linear in the    /// number of distinct names. The table stores the slice it is given without copying the bytes,    /// so those bytes have to outlive the chunk. The call returns `error.OutOfMemory` when the    /// table cannot grow.    pub fn addName(self: *Chunk, allocator: std.mem.Allocator, name: []const u8) std.mem.Allocator.Error!usize {        for (self.names.items, 0..) |existing, index| {            if (std.mem.eql(u8, existing, name)) return index;        }        try self.names.append(allocator, name);        return self.names.items.len - 1;    }    /// Appends a function to the function list and returns its position. The compiler records each    /// compiled `def` body with it, and the returned position becomes the function's value. The    /// chunk takes ownership of the function and frees it in `deinit`. The call returns    /// `error.OutOfMemory` when the list cannot grow. After that error, the caller still owns the    /// function and has to free it.    pub fn addFunction(self: *Chunk, allocator: std.mem.Allocator, function: Function) std.mem.Allocator.Error!usize {        try self.functions.append(allocator, function);        return self.functions.items.len - 1;    }};test "chunk interns names" {    var chunk_value = Chunk{};    defer chunk_value.deinit(std.testing.allocator);    try std.testing.expectEqual(@as(usize, 0), try chunk_value.addName(std.testing.allocator, "x"));    try std.testing.expectEqual(@as(usize, 0), try chunk_value.addName(std.testing.allocator, "x"));}

Source: lib/python/src/code/root.zig:9

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

Audit

Definitions10
Public names19
Members9
Version26.7.0
Revisiondaab053ee433