lib/python/src/root.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

 1 //! A Zig program hands this package the text of a small Python program and gets back the value that
 2 //! program computed. That value is the value of the last expression statement run at the program's
 3 //! top level, or Python's `None` when none ran. The package itself reads, compiles and runs the
 4 //! program, all in Zig. The package calls no outside Python runtime and links none. Every byte the
 5 //! run uses comes from the allocator the caller passes. The run touches no file, clock, process or
 6 //! network.
 7 //!
 8 //! An embedding program needs the answer as an ordinary Zig value that it can switch on. The
 9 //! embedding program also needs to know how long that value stays valid and which call frees the
10 //! memory behind it. The embedding program needs to know which part of Python the package accepts,
11 //! and how a program outside that part fails.
12 //!
13 //! Python programs build lists, dictionaries and strings while they run. The value a program
14 //! returns can point into that storage, so the storage has to outlive the run. Python marks where a
15 //! block begins and ends by indentation alone, so reading the text means tracking how deeply each
16 //! line is indented. A chained comparison such as `a < b < c` tests each neighboring pair and
17 //! evaluates each operand once. Python's integers grow without bound, and an integer of fixed width
18 //! needs a rule for results past its range.
19 //!
20 //! The [Python 3.14 language reference](https://docs.python.org/3.14/reference/) defines the
21 //! language, and the package follows it for the part it supports. From the reference the package
22 //! takes the way indentation opens and closes blocks, the order of operator precedence, and the
23 //! meaning of chained comparisons. [CPython](https://github.com/python/cpython), the reference
24 //! implementation, compiles a program to bytecode and runs it on a stack machine. The package keeps
25 //! that shape: a compiler emits a flat list of instructions, and a virtual machine runs them with a
26 //! stack of values and one call frame per function call.
27 //!
28 //! The package splits the work into four stages, one namespace each. The first stage, `source`,
29 //! turns the text into tokens. Each token records its kind and the byte range it covers in the
30 //! text. The second stage, `syntax`, builds a syntax tree from the tokens. The third stage,
31 //! `compile`, turns the tree into bytecode. The compiler hands the virtual machine its bytecode in
32 //! one shared format, `code`. That format holds the instructions and the tables of constants, names
33 //! and functions that the instructions refer to by index. The fourth stage, `runtime`, runs the
34 //! bytecode on a virtual machine. One set of types, `object`, describes every value a program
35 //! computes. `object` also defines the heap that owns the larger values. A namespace of constants,
36 //! `spec`, records the addresses of the language and library references and the targeted version,
37 //! 3.14.0. One call, `execute`, runs all four stages in order. The package root re-exports
38 //! `execute` with the value type (`Value`) and the result type (`Result`).
39 //!
40 //! The statements of the subset are expression statements, assignment to a name or to one
41 //! subscripted item, `del` of a name or of one subscripted item, `if` with `elif` and `else`,
42 //! `while` and `for` with `else`, `break`, `continue`, `pass`, `def` and `return`. The expressions
43 //! of the subset are integers, strings, `True`, `False`, `None`, names, `+`, `-` and `*`, unary
44 //! minus, the comparisons `==`, `!=`, `<`, `<=`, `>`, `>=`, `in`, `not in`, `is` and `is not`, then
45 //! `and`, `or` and `not`, calls, attribute access, list, tuple and dictionary displays, indexing
46 //! and slicing. The built-in functions are `dict`, `enumerate`, `iter`, `len`, `list`, `next`,
47 //! `range`, `reversed` and `tuple`. Lists carry the methods `append`, `clear`, `copy` and `pop`.
48 //! Dictionaries carry the methods `clear`, `copy`, `get`, `items`, `keys`, `pop`, `popitem`,
49 //! `setdefault`, `update` and `values`.
50 //!
51 //! A `def` inside a function fails to compile, so every function is defined at the top level. A
52 //! `return` outside a function fails to compile. A `break` or `continue` outside a loop fails to
53 //! compile. Integers are signed 128-bit numbers. An integer literal past that range fails to parse.
54 //! Arithmetic past that range fails with `IntegerOverflow`. Indentation is spaces only, and a tab
55 //! in a line's indentation fails. A string literal sits on one line between single or double
56 //! quotes. A string literal's value is the bytes between its quotes as written, backslashes
57 //! included.
58 //!
59 //! A failure comes back as a Zig error from the stage that found it. Six of the virtual machine's
60 //! eleven errors carry the names of Python exceptions: `AttributeError`, `IndexError`, `KeyError`,
61 //! `StopIteration`, `TypeError` and `ValueError`. An error carries no line or column of the source
62 //! text.
63 //!
64 //! `execute` frees the tokens, the tree and the bytecode before it returns. `execute` returns a
65 //! `Result` that holds the program's value and a heap. That heap, `Result.heap`, holds the lists,
66 //! tuples, dictionaries, ranges, iterators and strings the program builds while it runs. A returned
67 //! value that points into memory borrows that heap. A string taken from a string literal borrows
68 //! the caller's source text, so that text has to outlive the value too. The caller frees the heap
69 //! with `Result.deinit`, which invalidates every value that points into it. `None`, booleans and
70 //! integers hold no pointer, so a copy of one stays valid after `Result.deinit`.
71 //!
72 //! This example runs a three-line program whose last expression statement is `y`:
73 //!
74 //! ```zig
75 //! const python = @import("python");
76 //!
77 //! var result = try python.execute(allocator,
78 //!     \\x = 40
79 //!     \\y = x + 2
80 //!     \\y
81 //! );
82 //! defer result.deinit();
83 //!
84 //! try testing.expectEqual(python.Value{ .integer = 42 }, result.value);
85 //! ```
86 
87 pub const spec = @import("spec.zig");
88 pub const source = @import("source/root.zig");
89 pub const syntax = @import("syntax/root.zig");
90 pub const object = @import("object/root.zig");
91 pub const code = @import("code/root.zig");
92 pub const compile = @import("compile/root.zig");
93 pub const runtime = @import("runtime/root.zig");
94 
95 pub const Value = object.Value;
96 pub const Result = runtime.Result;
97 pub const execute = runtime.execute;