FHERMA KERNEL LANGUAGE

Types

Scalars

i8   i16   i32   i64      signed integers of the stated width
u8   u16   u32   u64      unsigned integers of the stated width
f32  f64                  floating-point numbers of the stated width

The set is closed. i1, i128, f16, index are not types of this language. A value wider than 64 bits is written as a tensor of machine words — a 1740-bit modulus is tensor<L x u64>, L words, low word first. A position or an index is a u64.

Half- and quarter-precision floats are absent on purpose: neither has a native form in C++ before 23 or in stable Rust, and f8 is two incompatible formats rather than one — a scaffold would compile and then fail on a byte count nobody could trace. They return when a kernel needs them, together with a wire form.

Value-parameter types

A value parameter takes the same strict scalars as data: N: u32, s: f64. Its width on every wire is the width of its type — u32 is four bytes in the point, uint32_t in a C++ skeleton, u32 in a Rust one — never assumed.

The loose words uint, int and real are retired: they named a number without naming its width, and every wire had to guess. They remain reserved, and using one is refused with a hint naming the strict spelling (uintu32, inti64, realf64).

A parameter that stands as a tensor dimension must be an unsigned integer type — a length below zero or with a fraction is not a length.

Classes

A class is a set of scalars a type parameter may range over:

Numeric  = Integer ∪ Real
Integer  = i8, i16, i32, i64, u8, u16, u32, u64
Real     = f32, f64

type T: Integer admits any integer; a specification then settles it — type T = i64 — and i64 ∈ Integer is what makes that refinement valid.

Tensors

tensor<N x T>
tensor<N x L x i64>
tensor<1024 x 4 x u64>
tensor<? x T>

The last position is the element type — a scalar or a type parameter; the positions before it are dimensions — a declared value parameter, a literal, or ?. Tensors do not nest, and secret never appears inside one: secrecy applies to the aggregate as a whole.

? is a dimension a kernel leaves unknown. A specification must resolve every one — a buffer whose size does not follow from the point cannot be tested.

The x separator

Inside tensor<…> the letter x separates dimensions, but x is also a legal identifier letter. The rule: x is a separator when it is surrounded by whitespace, or when the character before it is a digit or ?.

tensor<1024x1xi64>       three tokens — unambiguous after digits
tensor<? x ? x i64>      three tokens
tensor<N x L x i64>      three tokens
tensor<NxLxi64>          ONE identifier — invalid

When any dimension is named, put spaces around x. The spaced form is canonical, and tools that print the language always use it.

Secret

secret<i64>
secret<tensor<N x T>>

secret<T> marks a value hidden from whoever performs the computation. Visibility is part of the problem statement: a specification may not change secret<T> to T or back — that is a different problem and wants a different kernel. secret does not nest.

Mixed visibility is ordinary, and often the whole point:

kernel lookup<type T: Numeric, N: u32>(
    %index: secret<u64>,
    %table: tensor<N x T>,
) -> %value: secret<T>

The index is hidden, the table is public, the result is hidden — which is what makes retrieval private.