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 — i8i64, u8u64, 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.