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.
Chunk.addConstant: Appends a value to the constant list and returns its position.Chunk.addFunction: Appends a function to the function list and returns its position.Chunk.addName: Returns the position of a name in the name table, and appends the name first when the table lacks it.Chunk.deinit: Frees each owned function body and then the four lists.Chunk.emit: Appends one instruction to the end of the instruction list.Function.deinit: Frees the body's chunk with the given allocator and leaves the record undefined.
Types and contracts
Public types and contracts.
Chunk: A compiled program or function body: its instructions and the tables of constants, names and functions that they index.Function: A compiled function: its name, its parameter names in order, and its body as a chunk of its own.Instruction: One instruction: an operation and one integer operand.
Source
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
| Definitions | 10 |
|---|---|
| Public names | 19 |
| Members | 9 |
| Version | 26.7.0 |
| Revision | daab053ee433 |