FHERMA KERNEL LANGUAGE
Declarations
A declaration is either a kernel or a specification, and they share one production:
kernel-decl = "kernel" ident [ parameters ] "(" [ arguments ] ")" "->" results
spec-decl = "spec" ident version [ title ] "{" kernel-decl "}"
The name inside a specification's block must match the kernel it refines.
The version is three numbers — 1.0.0 — and the title is an optional
string. Comments run // to the end of the line; they do not enter the
syntax tree, but the source text is canonical and a kernel's page renders it
in full.
Parameters
All parameters are declared in <…>; there is no other mechanism.
type T a type, any type T: Numeric a type, constrained by a class type T = i64 a type, settled N: u32 a value, ranging over that strict scalar type
A parameter announces what it is. A bare name — N — is an error: it says
nothing about what it is. A value written with = — N = 1024 — is also an
error: a signature does not fix a number, a point does.
A value parameter takes the same strict scalar types as data — i8…i64,
u8…u64, f32, f64 — so its width on every wire is fixed by the
signature rather than assumed. The loose words uint, int and real are
retired and refused with a migration hint. A parameter used as a tensor
dimension must be unsigned: a length below zero or with a fraction is not a
length.
A kernel may leave type parameters open; a specification must settle every one of them. That rule belongs to refinement, not to the grammar.
Arguments and results
%name: type
Arguments are named bindings, one per value the computation takes. Results follow the arrow — one bare, or several in parentheses:
-> %c: tensor<N x T> -> (%q: T, %r: T)
Every declaration has at least one result. A kernel may have no arguments at all; such a kernel describes generation — a key, a sample, a fresh encryption of zero:
kernel keygen<N: u32>() -> %k: secret<tensor<N x i64>>
Argument order is part of the interface. Parameters and bindings are
separate namespaces — a parameter N and an argument %N do not collide —
but within one namespace a name is declared once.
The = ident suffix
An argument a specification adds beyond the kernel's may carry
= ident, naming a declared parameter its value comes from:
spec scale 1.0.0 "Fixed scaling factor" {
kernel scale<type T = f64, N: u32, s: f64>(
%x: tensor<N x T>,
%factor: T = s,
) -> %y: tensor<N x T>
}
Without the suffix, an added argument takes its value from point data keyed by the argument's name — which is how a value too wide for a parameter travels:
%q: tensor<L x T>, // a 1740-bit modulus is not a parameter
Either way this is a technical argument of the representation, not a new logical input: the generator produces values only for the kernel's own arguments. If a caller should choose the value, the argument belongs in the kernel.