polydat_core/lib.rs
1// Copyright 2024-2026 Jonathan Shook
2// SPDX-License-Identifier: Apache-2.0
3
4//! # polydat-core
5//!
6//! The Polydat runtime: the value model, the graph compiler, the
7//! execution engines, the kernels, the comprehension runtime, the node
8//! macro's support surface, the nodes the compiler synthesizes
9//! (adapters, passthroughs, constants, assertions, tile rendering)
10//! together with the nodes that stay with the runtime (formatting,
11//! JSON, data files, diagnostics, context, logging, and the
12//! `vectordata` accessors), and the numeric bodies the native
13//! lowerings share with the node library.
14//!
15//! A program declares typed inputs and a graph of named functions; the
16//! compiler produces a kernel whose named outputs are pulled on demand.
17//! The same inputs always yield the same outputs, on any thread, any
18//! host, and any engine, with no state carried between evaluations.
19//!
20//! Most programs depend on the `polydat` facade, which re-exports this
21//! crate together with the node library (`polydat-nodes`) and the
22//! language (`polydat-grammar`) at the paths they always had. Depend on
23//! `polydat-core` directly to assemble your own node set without the
24//! standard library linked, or to build a tool that needs the compiler
25//! and engines alone.
26//!
27//! ## Quick start
28//!
29//! The runtime compiles any program whose functions are linked. The
30//! standard functions such as `hash` live in `polydat-nodes` and
31//! register at link time, so a program that calls them needs that
32//! crate linked as well:
33//!
34//! ```rust,ignore
35//! use polydat_core::dsl::compile_polydat_with;
36//! use polydat_core::{Engine, Provenance};
37//!
38//! let mut kernel = compile_polydat_with(
39//! r#"
40//! input cycle: u64
41//! id := mod(hash(cycle), 1000)
42//! "#,
43//! Engine::Closures(Provenance::PushPull),
44//! )?;
45//!
46//! kernel.set_inputs(&[7]);
47//! assert!(kernel.pull("id").as_u64() < 1000);
48//! ```
49//!
50//! For programmatic construction, [`compile::assembly::PolydatAssembler`]
51//! wires boxed nodes by name and compiles the result the same way.
52//!
53//! ## Engines
54//!
55//! One program compiles to any of four engines and gives the same
56//! values on each; the host names one with [`Engine`], and
57//! [`Engine::default`] is the fastest the build has.
58//!
59//! - [`Engine::Interpreter`]: boxed nodes over typed value buffers,
60//! with as much of the graph fused into native cones as its
61//! [`JitMode`] allows.
62//! - [`Engine::Closures`]: one generated closure per node over a flat
63//! slot buffer.
64//! - [`Engine::Native`]: Cranelift machine code where a node has a
65//! lowering and the node's closure elsewhere. Needs the `jit`
66//! feature.
67//! - [`Engine::PureNative`]: Cranelift machine code and nothing else,
68//! refusing the program where `Native` would fall back to a closure.
69//! A host asks for it to learn whether its program is fully native.
70//! Needs the `jit` feature.
71//!
72//! [`Provenance`] chooses how much re-evaluation a changed input
73//! triggers; it is an optimization and never changes a result. Every
74//! engine accepts every program the interpreter accepts and drives it
75//! through the one [`Kernel`] trait.
76//!
77//! ## Program and state
78//!
79//! ```text
80//! inputs (u64 tuple, cursors, externs)
81//! │
82//! ▼
83//! ┌──────────────────────────────────┐
84//! │ KernelProgram immutable, Arc │ shared by every thread
85//! │ nodes · wiring · outputs · consts│
86//! └───────────────┬──────────────────┘
87//! │ create_kernel()
88//! ▼
89//! ┌──────────────────────────────────┐
90//! │ Kernel one per thread │ no locks, no shared writes
91//! │ slot buffers · provenance masks │
92//! └───────────────┬──────────────────┘
93//! ▼
94//! pull("id") → Value
95//! ```
96//!
97//! A [`KernelProgram`] is the compiled, immutable half, shared by
98//! reference; a [`Kernel`] is one thread's private state over it.
99//! Outputs are owned by their provenance: a value stands until an
100//! input that reaches it is written.
101//!
102//! ## Cargo features
103//!
104//! - **`jit`** (default): the native engine, on Cranelift.
105//! - **`vectordata`**: vector-dataset access nodes for ML/AI-oriented
106//! workloads.
107//!
108//! ## Modules
109//!
110//! - [`ast`]: the value model and node contract: [`ast::Value`],
111//! the [`ast::PolydatNode`] trait, [`ast::Port`].
112//! - [`dsl`]: compiling Polydat source:
113//! [`dsl::compile_polydat_with`] for a chosen engine,
114//! [`dsl::compile_polydat_kernel`] for the default, and
115//! [`dsl::compile_polydat`] for the interpreter kernel; the node
116//! registry, factories, and compile events.
117//! - [`compile`]: graph construction and the engines:
118//! [`compile::assembly`] (the assembler and adapter insertion),
119//! [`compile::fusion`], [`compile::closures`], [`compile::hybrid`]
120//! (the native engine's kernel), `compile::jit` (Cranelift lowering,
121//! feature-gated), [`compile::select`] (engine and provenance
122//! selection).
123//! - [`kernel`]: the runtime: the [`Kernel`] and [`KernelProgram`]
124//! traits, the interpreter's [`kernel::PolydatProgram`] and
125//! [`kernel::PolydatState`], shared cells, scopes, subcontexts,
126//! traversal activation.
127//! - [`iteration`]: comprehensions, cursors, partitions, and the
128//! coordinate algebra.
129//! - [`library`]: the nodes the compiler synthesizes (adapters via
130//! [`library::polyfill`], passthroughs, constants, assertions, tile
131//! rendering via [`library::tile_render`]) together with the nodes
132//! that stay with the runtime: formatting, JSON, data files,
133//! diagnostics, context/environment, logging, and the `vectordata`
134//! accessors; plus the library-internal support
135//! ([`library::support`]). Every other node is in `polydat-nodes`.
136//! - [`numeric`]: the numeric bodies shared by the node library and the
137//! native lowerings.
138//! - [`tile`]: Polytile at the host boundary.
139//! - [`binder`], [`derive_support`], [`resource`], [`audit`]: the typed
140//! binding contracts, the `#[polydat_node]` macro's support surface,
141//! the host resource bridge, and the log sink.
142//! - [`viz`]: AST and graph visualization, re-exported from the grammar.
143//!
144//! The narrative documentation lives in the repository under
145//! `crates/polydat/docs/`, organized by the
146//! [documentation index](https://github.com/nosqlbench/polydat/blob/main/crates/polydat/docs/README.md);
147//! the [runtime model](https://github.com/nosqlbench/polydat/blob/main/crates/polydat/docs/design/runtime_model.md),
148//! the [graph compiler](https://github.com/nosqlbench/polydat/blob/main/crates/polydat/docs/design/graph_compiler.md),
149//! and the [engines](https://github.com/nosqlbench/polydat/blob/main/crates/polydat/docs/design/engines.md)
150//! design documents are the ones to read first.
151
152// Unit tests use round-number float literals (`3.14`, `1.57`,
153// `2.71`, …) as arbitrary fixture data. clippy's `approx_constant`
154// is a deny-by-default correctness lint that reads those as
155// fat-fingered `std::f*::consts::*` — true for production code,
156// noise for test data. Scope the allowance to `cfg(test)` so the
157// lint still guards real code.
158#![cfg_attr(test, allow(clippy::approx_constant))]
159#![warn(missing_docs)]
160
161// SRD-80 PR B.3 — let the `#[polydat_node]` macro's emitted
162// `polydat::...` paths resolve when the macro is invoked from
163// INSIDE the polydat crate itself (library nodes migrating to
164// the macro form). External callers don't need this — they
165// reference `polydat` via the regular crate-name lookup.
166extern crate self as polydat;
167
168pub mod ast;
169pub mod binder;
170pub mod compile;
171pub mod convert;
172pub mod dsl;
173pub mod iteration;
174pub mod kernel;
175pub mod library;
176pub mod numeric;
177pub use polydat_grammar::viz;
178
179/// Polytile at the host boundary (SRD 114 §5.6): build a tile from
180/// template text, from structural JSON text, or from a parsed JSON
181/// value, then add it to a program with [`tile::add_tiles`].
182///
183/// A host tile reaches a kernel the way every other host intention
184/// does, as a transform over the parsed program:
185///
186/// ```no_run
187/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
188/// use polydat_core::dsl::{CompileOptions, compile_ast_with_engine, parse_polydat};
189/// use polydat_core::tile::{Span, TileOptions, add_tiles, tile_from_text};
190///
191/// let tile = tile_from_text("greeting", "hi ${who}", &TileOptions::default(), Span::default())?;
192/// let source = "input cycle: u64\nwho := \"world\"\n";
193/// let mut program = parse_polydat(source)?;
194/// add_tiles(&mut program, vec![tile])?;
195/// let kernel = compile_ast_with_engine(
196/// &program,
197/// source,
198/// &CompileOptions::default(),
199/// None,
200/// polydat_core::Engine::default(),
201/// )?;
202/// # let _ = kernel;
203/// # Ok(())
204/// # }
205/// ```
206///
207/// There is no `compile_with_tiles`, deliberately. Adding tiles is one
208/// rewrite among the ones a host may want, and a dedicated entry point
209/// would be the only one that could not be combined with another. From
210/// `add_tiles` on, the tile is a `tile` statement like any the author
211/// wrote, and the program's own typing and engine selection apply to
212/// it unchanged.
213pub mod tile {
214 pub use crate::dsl::ast::{TileBodyKind, TileDef, TileOptions, TilePiece};
215 pub use crate::dsl::lexer::Span;
216 pub use crate::dsl::tile::{parse_template, render_template};
217 pub use crate::dsl::tile_structural::{
218 ENCODINGS, template_text_from_value, tile_from_json_text, tile_from_json_value,
219 tile_from_text,
220 };
221 /// Add host-built tiles to a parsed program; see
222 /// [`transform::add_tiles`](crate::dsl::transform::add_tiles).
223 pub use crate::dsl::transform::add_tiles;
224}
225
226// SRD-104 — dependency-inverted resource-accessor bridge. A
227// type-erased trait + process-global install point by which a
228// kernel node reaches a live, host-owned resource by fingerprint,
229// without polydat depending on the host runtime.
230pub mod resource;
231
232// SRD-80 — proc-macro trait surface. The `polydat-derive`
233// crate emits paths like `polydat::derive_support::FromValue` /
234// `IntoValue` that resolve here.
235pub mod derive_support;
236
237// SRD-80 PR B.5 — `Const<T>` wrapper re-exported at crate root
238// for ergonomic use in `#[polydat_node]` function signatures.
239pub use derive_support::Const;
240
241/// The slot surface of a compiled kernel: the extended API, opt-in at
242/// the import, over and above the `Kernel` trait every engine answers.
243pub use compile::SlotKernel;
244/// How much of the interpreter's graph is fused into native cones:
245/// what `Engine::Interpreter` carries.
246pub use compile::cone::JitMode;
247/// The engine a host chooses and the one error of every constructor
248/// that takes it (docs/design/engines.md §3.5).
249pub use compile::select::{Engine, EnginePlan, KernelError, Provenance};
250/// One kernel API for every engine, and the one error a write to a
251/// declared slot can fail with. `WriteError` sits beside `KernelError`
252/// at the root because they are the pair a host handles: one for
253/// building a kernel, one for writing to it.
254pub use kernel::{Kernel, KernelProgram, ProgramId, WriteError};
255
256// SRD-82 §"Panic reporting: one full render" — host runtimes with
257// their own panic reporting declare it so the eval-panic hook
258// prints a short notice instead of the full diagnostic.
259pub use kernel::set_panic_reporting_downstream;
260
261// SRD-80 — re-export the `#[polydat_node]` attribute so
262// library callers can write `#[polydat::polydat_node]` without
263// a separate `use polydat_derive::polydat_node;` line.
264pub use polydat_derive::polydat_node;
265
266// SRD-80 — re-export `inventory` so the macro's emitted
267// `::polydat::inventory::submit!` path resolves at every call
268// site without users having to add `inventory` to their own
269// dependencies.
270pub use inventory;
271
272/// Re-exported for `#[polydat_node]`-generated Phase-2 buffer
273/// casts on `half::f16`-typed wires (the generated code spells
274/// `polydat::half::f16`, which `extern crate self as polydat`
275/// resolves inside this crate too).
276pub use half;
277
278/// SRD-104 — the resource-accessor bridge at the crate root so the
279/// host installs via `polydat::RESOURCE_ACCESSOR` and nodes resolve
280/// via `polydat::resource_lookup`, without reaching a deep module
281/// path (D6).
282pub use resource::{RESOURCE_ACCESSOR, ResourceAccessor, resource_lookup};
283
284/// Host-log sink bridge — the sanctioned public path for installing
285/// a leveled log sink into the kernel (`set_log_fn`) and for emitting
286/// through it (`warn` / `info` / …). The activity runner installs its
287/// `observer::log` here so polydat's cycle-time data-source audit lines
288/// land in `session.log`. This is the one public entry point for the
289/// audit channel; the implementation lives under `library::support`,
290/// which is library-internal and must not be reached directly.
291pub use library::support::audit;