Kernels & the .ent language
An .ent file is a Kernel library: kernel items and the declarations they share (struct, type, fn). .ent and .esn share one grammar, lexer and literal syntax: // comments, ;-terminated statements, { } blocks. Diagnostics are file:line:column: message. The exact language is defined by the front end (crates/frontend/language) and its tests.
Anatomy of a Kernel
Section titled “Anatomy of a Kernel”struct Particle { position: Vec2; velocity: Vec2; } // a document struct: every Kernel below sees ittype Swarm = Rows<Particle>; // a port type alias, shared the same way
kernel image.grayscale@1 { requires { field.read; field.write; } // capabilities input source: Image; // an input port param amount: Float = 1.0; // a param and its default output result: Image; // an output port struct Hit { t: Float; id: Int; } // this Kernel's own struct fn luma(c: Vec4) -> Float { return c.r * 0.2126 + c.g * 0.7152 + c.b * 0.0722; } compute { // the body (here, the map mode) let c = sample(source); let g = luma(c); return mix(c, vec4(g, g, g, c.a), amount); }}- Identity:
name@versionis the Kernel’s identity. Graphs bind to it, never to a file. - Capabilities:
requires { … }lists what it needs; without one, its builtins cannot be called. See Capabilities & security. - Ports:
input,output, and a scan’smemory. Each carries a Field. - Body: one of
compute,scan,scatter,prefix,group N— the execution mode. - Budget:
budget N;declares the loop iterations one element may run (16777216 by default).
//! A preset graph's or a package manifest's doc: the lines a document opens with.use "kernels/";
/// A Kernel's doc: the lines directly above it.kernel tally.twice@1 { requires { field.read; field.write; } /// A port's or a param's doc, directly above it. input counts: Rows<Int>; output result: Rows<Int>; compute { return sample(counts) * 2; }}/// lines directly above a kernel, input, param, output or memory are its doc; a blank or // line ends them. They are comments to the grammar, but travel with the Kernel’s source through publish, kernel and snapshot; changing them publishes it again.
Expressions
Section titled “Expressions”A compute body is let bindings and a result.
| Kind | Forms and rules |
|---|---|
| Bindings | let n = …;; let { x, y } = window(); by name; let { x: width, y: height } = window(); renames; _ binds nothing; expr; is let _ = expr; |
| Arithmetic | + - * /, % (truncated remainder); Int and Float kept apart, but vec2/3/4 take Int components; a Float beside a vector broadcasts |
| Swizzles | v.xy, v.zyx, c.rgb; v.x one component |
| Bits | & | ^, << >> >>> (count modulo 32, >> arithmetic, >>> logical); 0xFF hex, more than eight digits or an l a Long |
| Logic | &&, || short-circuit; ==, != compare scalars and whole vectors, < and the rest scalars |
if |
if c { a } else { b } is an expression; if c { return a; } with no else guards what follows; a statements-only if hit { emit(p); } runs and goes on |
match |
on an Int: match i { 0 => a, 1 | 2 => …, 8..16 => c, 16..=31 => d, _ => b }; ranges at most 256 values; arms of one type |
| Enums | type Mode = enum { Idle, Walk, Attack = 10, Guard }; — named Ints, Mode.Guard is 11; a match naming every variant needs no _ |
loop |
loop n with x = init until done { next } is an expression; the count a literal (at most 65536) or any Int expression; if done { break x; } stops the innermost loop |
| Structs | Name { a: …, b: … } builds; Name { ..base, a: … } copies with fields replaced |
| Nesting | at most 256 deep; past that the document is refused, saying where |
Arrays
Section titled “Arrays”[T; N] holds N values of one type: [Int; 16], [Vec3; 4], [[Int; 4]; 4] — a struct field, an argument or result, a loop’s carried value, or a Field’s element.
let a = [1, 2, 3];let zeros = [0.0; 8];let w = [each 32 { unit(weights, x, index()) }]; // element by elementlet b = [each 4 { if index() == k { v } else { a[index()] } }]; // element k replacedA literal index is checked when compiled; a computed Int is clamped. 1 to 4096 elements; a value holds at most 65536 words; structs and arrays nest at most 64 deep.
Parameters
Section titled “Parameters”A param is any value a Kernel holds:
param lens: Mat4 = mat4(1.0);param cell: IVec2 = ivec2(3, 4);param edges: [IVec2; 12] = [ … ];param style: Style = Style { ground: vec3(0.1, 0.1, 0.1), width: 1.0 };A default is a literal. A value matches by layout, never spelling: from outside (a scene, a replay’s param, the protocol) an array of values in order takes any shape — [[1, 0], [0, 1]] is a Mat2.
Functions, declarations and use
Section titled “Functions, declarations and use”- A top-level
struct,typeorfnis shared by every Kernel in the document and every document thatuses it. An.entmayuseother.entfiles or directories. - A name reaches the Kernel’s own declaration, then the document’s own, else the one in the documents it uses. Where two used packages answer differently, qualify:
cast_fx::clear(p),Rows<ui_panel::Mark>. - Functions call one another in any order but never recurse (a cycle is refused, named); repetition is a
loop. - A function may take a Field:
fn cast(voxels: Volume<Int>, eye: Vec3) -> Hit, called with an input port’s name, instantiated once per port. - Generics:
fn twice<T>(v: T) -> T, one instance per set of types. - A function may take a math builtin’s name (
step,mix) and stands for it in its own document; the Field, memory, context and construction builtins keep their names.
Failures and budgets
Section titled “Failures and budgets”- Integer failures: overflow of
+ - *, division or remainder by zero, the least integer negated, fail the element on every backend, naming the first element in row-major order; nothing the evaluation computed commits.add_wrap,sub_wrap,mul_wrapwrap. - Floats: a positive over zero is infinity, zero over zero NaN, and NaN equals and orders against nothing. A NaN made by arithmetic on the GPU may come out a number under a driver’s fast math.
- Budget: an element’s iterations, all loops together, 16777216 unless
budget N;(1 to 2^30). A run, all elements together, is bounded by the Host (four thousand million unless--work <M>, in millions). Past either, the run fails and the system keeps its last state.
64-bit numbers
Section titled “64-bit numbers”Double, Long and their vectors and matrices (DVec2..4, LVec2/3, DMat2..4). double(x) and long(x) convert; float(x) and int(x) convert back. A Double never meets a Float, nor a Long an Int, without a conversion. A written number is exact until it meets a width; 0.1d and 8l name one. A GPU computes Long in maps, groups, reductions and some scatters but never a Double: a GPU session runs such a node on the JIT.
Narrow encodings
Section titled “Narrow encodings”After as, a port stores scalars narrower: Grid<Vec4 as unorm8> is four bytes a pixel, Rows<Int as u8> a byte a row. The Kernel still computes in Float and Int; the Field widens on read and narrows on write. f16, unorm8, unorm16, snorm8, u8, i8, u16, i16. A narrow and a wide Field are different layouts, and a wire between them is refused; only a map or a scan’s steps write a narrow output; a 64-bit scalar is never narrow.
- Types & Fields
- Execution modes: map, reduction, scan, scatter, prefix, group

