The complete surface for Strata's committed core, designed against
15-syntax-requirements.md R1–R36 and reconciled from the syntax
probe wave: probes/syntax-grammar/ (resilient-LL +
tree-sitter grammar, 26 GAP designs), probes/syntax-corpus-core/ (hardware corpus, 31-entry gap
ledger), probes/syntax-corpus-verif/ (verification/protocol corpus, G-S1–G-S38). Charter picks
(D-077) are SETTLED; reg forms are SETTLED (D-079); the grammar-evidence decisions are SETTLED
(D-080). The whole document is DRAFT-COMPLETE (D-078) pending the fmt dry-run and blind-regeneration
passes. Standing tiebreaker: Rust style when unsure.
The 2026-07-21 systems-safety wave added requirements R37–R50 but did not run a syntax/corpus probe for them. This document therefore does not pretend to settle spellings for health contracts, mapping leases, progress summaries, memory events, observers, power/reconfiguration, physical certificate binding, numeric budgets, DFT authority, or assurance cases. Their architectural homes and syntax constraints are settled provisionally; their surface forms require a dedicated corpus, grammar, formatter, and blind-regeneration wave before joining this specification (D-091).
component, inst, reg, domains, events, protocols).:=/<> zoo is the cautionary tale).T @ 'E) it is the fmt-canonical form, so corpus and formatter agree.| Fork | Pick |
|---|---|
| Component flavor | Value-flow: inputs as parameters, outputs as return type; no in/out/inout keywords; bidirectionality via protocol endpoint types |
| Generics | Rust split: bare <> in type position, mandatory ::<> turbofish in expression position; lexer emits single >, parser glues |
| Bindings | let / reg / inst triad, comptime/ghost prefixes — binder form makes phase syntactically recoverable per token (14 §19) |
| Terminators | Semicolons for statements (including requires); comma-lists for all data-shaped declaration bodies (fmt trailing-comma steering, 14 §2) |
| Registers (D-079) | reg name: T reset(v); declares; name <- expr; is the next-cycle write. No = expr next-value form at the declaration. One meaning per token: = binds combinational values, <- is the visible Later-boundary write; multi-arm updates stay natural (sequential last-write-wins per body, elaborating to a priority mux) |
| Tiebreaker | Rust when unsure — resolves: #[attr], where, :: paths, //+/// comments, ../..= ranges, match/pattern syntax, snake_case/CamelCase |
Identifiers. [A-Za-z_][A-Za-z0-9_]*. Convention (lint-enforced, not grammar): CamelCase types/variants/evidence constructors, snake_case values/functions/schemas, lowercase keywords.
Apostrophe labels. 'ident is one token: temporal validity — clock domains ('core) and events ('Exec) share the grammar, deliberately resonant with Rust lifetimes. There are no char literals; 'x is always a label.
Keywords (hard-reserved). Every form-opening word plus the four soft keywords the grammar probe flagged as recovery hazards (state, on, preserve, at_most — D-080):
| 1 | accept await bundle by claim clock_domain comptime component |
| 2 | const contract counter differential_test discharge div domain |
| 3 | elastic else enum experiment expect false fn for |
| 4 | from ghost halt handshake if impl in inst |
| 5 | interface invariant is latch let loop match mem |
| 6 | mod msg mut no_reset on opaque ordering port |
| 7 | preserve process property protocol prove pub quiesce recv |
| 8 | reg repeat require requires resource retain return role |
| 9 | rule sealed send sequence session solve stage stamped |
| 10 | state state_machine static strategy stream struct to |
| 11 | true type unsafe use where while* with |
| 12 | at_most cycleobs |
(while reserved unused, Rust-lean.) Soft (position-only) words, ordinary identifiers elsewhere: param, fixed, reset, related_by, key_set, binds, scope, over, under, evidence, assume, guarantee, liveness, arbitrate, sweep, as, at, cycle and the unit idents. narrow, weighted, config are special only as ident!.
Integer literals. Unsuffixed integers are static-phase polymorphic naturals checked to fit exactly (too wide = error, never truncation — D-030); 0x/0b/decimal with _ separators. No tick-literals ever (8'hFF needs context-sensitive lexing and truncates silently — D-048). Explicit form where inference is absent: UInt::<8>(0xFF).
Quantity literals (verif corpus, G-S5): NUMBER unit where unit comes from the closed, edition-versioned unit table, legal only in quantity positions (evidence arguments, flow entries, #[schedule], experiment entries): 17.2e9 txns, 842 MHz, 1/cycle. N/cycle is a grammatical Rate, not division — / exists nowhere else in the language. Decimal-fraction/exponent numerals (17.2e9, 0.3, 1.0e-6) are legal only inside quantity and metadata positions; there are no hardware floats.
| Unit class | v1 closed table |
|---|---|
| rate denominator | cycle |
| count | txns, evals |
| frequency | MHz, GHz |
| time | ns, us, ms |
String literals. Metadata positions only (reason =, attribute args, tool versions like "vivado-2026.1" — bare vivado-2026.1 is lexer poison). No hardware string type exists.
Comments. //, /// doc (item-anchored), //! module doc — anchored trivia per D-053; never column-significant.
The > discipline (D-080). The lexer never emits >> or >=: it emits single > (and =); the parser glues byte-adjacent > > into shift and > = into >= in expression-operator position only. Consumer<ready_valid<T>> needs no splitting — each > closes one level. <, <=, <<, ::< lex directly. ..=/.. are single tokens. No context-sensitive lexing anywhere (the tree-sitter co-grammar, D-066, depends on this).
Every item form opens with a unique keyword (design rule 2). Items take #[attributes], /// docs, and visibility pub / pub(package) (module-private default; pub(crate) etc. deferred).
component — value-flow: inputs are parameters, the output is the return type. Multi-output components use named tuple returns (G-S35: contracts and require guarantee must name outputs):
| 1 | pub component MemoryController<const OUTSTANDING: Nat1, const BANKS: Nat1 = 1>( |
| 2 | requests: Consumer<ready_valid<MemRequest>>, |
| 3 | ) -> (resp: Producer<ready_valid<MemResponse>>, dram_cmd: Producer<ready_valid<DramCmd>>) |
| 4 | where OUTSTANDING <= 64 |
| 5 | { ... } |
Statefulness is visible at the use site: components instantiate with inst, functions call bare. A component's operation surface (port items, §5) is reachable through a borrowed handle parameter table: &TxnTable; an inst value coerces to its value-flow result where one is expected (core G19). Item-position unsafe surface: pub unsafe(clock_crossing) component AsyncFifo<'src, 'dst, T, const DEPTH: Nat1>(...).
fn / comptime fn — pure/combinational functions and generators. ghost fn declares monitor-phase functions (relations, predicates); refinement/ghost integers are mathematical, so bare - is legal there (never on hardware integers, R8).
interface / sealed interface / impl — Rust-trait-shaped (D-038): member fn ...; signatures, associated type Grant;, impl Path for Type { }. sealed interface is the protocol-catalog mechanism.
enum — Rust bodies plus explicit encodings (G-S1): the ascription makes it Encoded<Opcode, Bits<7>> — encoding is semantics, not a #[repr] layout hint. Discriminants checked to fit exactly.
| 1 | pub enum Opcode: Bits<7> { |
| 2 | Load = 0b000_0011, |
| 3 | Store = 0b010_0011, |
| 4 | } |
struct — Rust bodies, comma-lists, unit structs struct Active;, grade-0 phantom type params legal unused.
opaque type / type alias — pub(package) opaque type TxnId = Handle<...>; (declaring module owns mint/open); plain pub type Addr = UInt<44>;.
const — const TXN_ENTRIES: Nat1 = 16; — item-position, implicitly static-phase (comptime let is the body-local form).
clock_domain — declared once, referenced by name (R2): clock_domain core { clock: CoreClock, reset: CoreReset { polarity: active_low, deassert: sync }, }; no_reset as a marker entry declares a reset-free (unit-epoch) domain.
resource — resource adder: Unit<Latency<0>, II<1>> = share(wrapping_add); — abstract, = share(f) bound, or arrays [Unit<...>; N] = replicate(f) (replication always explicit).
protocol / bundle / stream — §10. state_machine / strategy fn / property / differential_test / experiment — §11.
use / mod — Rust use-trees (use a::b::{C, D};, ::*, as); pkg:: is the current package root; pub mod x; + pub use re-exports define the package surface (13 §6).
discharge — statement-positioned, §5.
Instantiation is named-only with punning — positional connection does not exist:
| 1 | let fifo = inst Fifo::<LoadReq, 8> { push }; // pun connects `push` |
| 2 | let out = fifo; // value-flow result |
One protocol-connect form, checked by duality (04 §3): passing a producer value to a Consumer<P> parameter is the connection. A standalone connect statement was drafted in the core corpus and never needed — rejected (§15).
Component/fn bodies are statement lists terminated by ;; block-like expressions (match/if/for/domain/blocks) in statement position take no ;. Comma-lists are reserved for data-shaped braces (struct/enum/contract/arbitrate/protocol/bundle/stream/clock_domain bodies) — the two disciplines never share a brace pair.
| 1 | let decoded = decode(instr); // combinational; IMMUTABLE (R1) |
| 2 | let mut acc = init; // mutation is marked, rare |
| 3 | let (a, b, c) = triple; // n-ary destructuring (R34) |
| 4 | if let Some(item) = pushed { ... } // rust if-let |
| 5 | |
| 6 | reg rd_ptr: Index<DEPTH> reset(0); // D-079: declaration, reset mandatory* |
| 7 | rd_ptr <- rd_ptr.incr_wrap(); // next-cycle write; last-write-wins per body |
| 8 | buf[wr_ptr] <- item; // element write (same statement form) |
| 9 | retain reg saved: ConfigState reset(dflt);// survives warm reset; reset() = POR only |
| 10 | mem buf: [T; DEPTH]; // un-reset storage; reads need an |
| 11 | // initializedness argument or unsafe(uninitialized_use) |
| 12 | |
| 13 | inst — expression form only: `let x = inst C { ... };` (bare `inst x = ...;` rejected, §15) |
| 14 | comptime let LANES = cfg.lanes; // static phase |
| 15 | comptime for i in 0..LANES { ... } // static loop; also an expression (array per iteration) |
| 16 | comptime param STAGES: Nat in 1..=6; // design-space knob (07 §7) |
| 17 | comptime fixed DEPTH: Nat = cfg.depth; // user-owned config |
| 18 | ghost let trace = observe(bus); // ghost phase, erased |
* reset(v) is mandatory unless the ambient domain is no_reset or the register is explicitly uninit (02 §6). Unwritten paths hold. reg is never inferred from code shape (kills latch inference and blocking/nonblocking outright).
Obligations and discharge (named forms — verif G-S2/G-S3; ; canonical, D-080/GAP-19):
| 1 | requires solve mem_has_base_reg(opcode: Bits<7>) { |
| 2 | if entry_for_encoding(opcode).class is Mem { entry_for_encoding(opcode).has_rs1 } |
| 3 | }; |
| 4 | discharge mem_has_base_reg by comptime_exhaustive(opcode); |
| 5 | requires static { DEPTH mod 2 == 0 }; |
| 6 | requires prove home_serves_all { every_request_eventually_responds(requests, resp) }; |
Names are mandatory on solve/prove (discharge and diagnostics address by name), optional on static. The binder on the requires is the finite domain the discharge strategy enumerates. narrow!(cond); at dataflow boundaries only (R4); the weakened-guard diagnostic quotes the dropped conjunct. require guarantee mem.no_duplicate_response with evidence >= BoundedProved; states a consumer's evidence floor (§11).
Pipeline and time. stage; is the bare separator; stage 'Name; additionally binds the event label used by .at('Name) claims. Values crossing a stage; are auto-piped; inserted registers are named in the structure report. Region markers: elastic { ... } (TransactionEquivalent bound) and cycleobs { ... } (CycleExact), R35.
Multi-cycle control (core G24): sequence { ... } elaborates to an FSM; await bool_expr; waits; await process_expr runs a Process to completion yielding Result<S, F>; halt diverges (types as !); loop { } as in Rust. Typestate transitions construct Process values with the process { step:, done:, ok:, err:, } block (core G23).
Handshake endpoints (core G11/G12): the value-flow introduction/elimination forms for protocol endpoints —
| 1 | let (out, popped) = handshake ready_valid::<T> { valid: !empty, payload: head }; |
| 2 | let (out, _sent) = handshake ready_valid::<T> from opt_value; // Option drives valid/payload |
| 3 | let pushed = push.accept(ready = !full); // drives ready, yields Option<T> |
accept returning Some implies the ready expression held that cycle (a stated protocol-semantics fact feeding guard inference).
Other statements: port name(args) -> Ret { body } (component-body only; elaborates to a request_response endpoint; body-less port log(pkt: stamped TracePkt); declares); domain 'core { ... } blocks (expressions; escaping values carry their domain tag); unsafe(category, reason = "...") { expr } granular unsafe; return expr;; assert expr; (ghost/test phase only); contract { ... } blocks (§11); resource declarations; expect exists only as the #[expect] attribute (§9), never a statement.
match/if are expressions (R3/R5); blocks yield their trailing expression. Variant names resolve against the scrutinee's declared enum type (R2).
Precedence (low → high; comparisons non-associative, Rust-lean):
| Level | Operators |
|---|---|
| assign | = (to let mut targets; lvalue-checked) |
| range | .., ..= |
| or | || |
| and | && |
| compare / test | == != < <= > >= · is variant-set |
| concat | ++ (bit concat; widths sum in the D-037 algebra) |
| shift | <<, >> (glued) |
| additive | +, - (grammatical; hardware integers reject bare - — R8) |
| multiplicative | * · div/mod with ConstAtom right operand by grammar (the R7 Presburger fence) |
| unary | !, - |
| postfix | call, .method, ::<T> turbofish, [i] index/slice, ? |
op is Load | Store, closed under &&/||/! (R3); diagnostics render op ∈ {Load, Store} (R5). Idiom the docs and lints teach: bind then match — let d = decode(raw); match d { ... } — let-bound scrutinees and predicate aliases are what guard inference traces.wrapping_add, full_add, checked_add, saturating_add; subtraction forces the choice — checked_sub (Option) vs monus (floors at zero). Conversions: wrap_to::<N>(), saturate_to, checked_to, sign_extend_to (no as). No compound assignment (+=) — exception: session counters (§10), which are math naturals.x[0..8] is the low byte, x[msb] sugar; never Verilog [7:0]. x[0..k] ++ x[k..N] widths sum. Named Encoded fields displace raw slicing (lint on raw slices of typed values).? propagates stall/fault through the canonical Bypass/Poison node: a Bubble<T> unwraps or kills the dominated region; Bubble::of(x) re-wraps (core G28). Also queue.reserve()?.UInt::<8>(0xFF), x.wrap_to::<4>(), overlaps::<W> inside relation args — F15's fix-it: "expression position: write ::<"). Domain labels ride along: AsyncFifo::<'core, 'dbg, T, 8>.'Exec, 'G + 2; resource claims adder.at('Exec)(a, b) — guards on conditional claims are never written (R2), they are inferred from if/match path conditions and rendered in set form with the harvested path condition (R5/R6).if/match/for headers and match-arm guards (D-080; Rust's rule): if x == S { } parses S as a path and { } as the body; parenthesize to opt in — if a == (Grant { slot: 0 }) { b }. Functional-record-update ..rest is legal (CacheAddress { offset: 0, ..arbitrary() }).guarantee/liveness join the comma-list with mandatory names; eligibility/keys are explicit closures, never a magic r):| 1 | let granted = arbitrate dram_issue { |
| 2 | inputs: [read_q, write_q], |
| 3 | eligibility: |r| r.ready, |
| 4 | policy: oldest_first(key = |r| r.age, tie_break = round_robin), |
| 5 | resource: dram_cmd_port, |
| 6 | guarantee exclusive_grant: at_most_one_grant_per_cycle(), |
| 7 | liveness weak_fairness: fair_grants() under continuously_ready(dram_cmd_port), |
| 8 | }; |
if cond { pred } means !cond || pred (R11; predicate position only — the expression-side error must say so), and div/mod keep the syntactic fence. Interpreted predicate symbols (closed set): clog2, max, min, prev (R9 stability), accepted, transferred, outstanding, same_epoch.reflect(x), reify(x), symbolize(x), observe(x).| 1 | Type = ["stamped"] ['domain] TypeCore ["@" 'Event] |
| 2 | TypeCore = Path<GenericArgs> | (T1, T2) | [T; N] | fn(A) -> B // fn types comptime-phase only |
T: Ord + Encode; const params const DEPTH: Nat1 with optional ranges const N: Nat in 1..=6; label params 'src, 'dst (domains/events — the Rust-lifetime grammar accepts all four positions unchanged: params, type args Epoch<'core>, signal prefixes x: 'core Bool, turbofish).where takes both interface bounds and Tier-1 infix predicates: where DEPTH <= 1024, T: Encode (D-080: bound-vs-predicate needs LL(2), committed). Where-lists take no trailing comma (not a braced body).Nat1 is the standard depth/count parameter type (where N >= 1 also legal; Nat1 is the path of least resistance). Width expressions in <> follow the D-037 closed algebra (clog2/max/min spelled exactly so, R21); leaving it triggers the "left Presburger" fix-it (R20).<...> the grammar excludes comparisons, shifts, &&/||, and struct literals, so > always closes and , always separates — bare <> in type position needs no symbol table and no brace fallback. A bare path argument is type-or-const until name resolution (as in Rust). Trailing commas are legal in <> lists (fmt wrap rule, §13). Schema config blocks are type-position only: burst_dyn<Flit> { max_len: 8 } (expression position would collide with struct literals — same class as the header rule).Occupancy<0..=ENTRIES> (state refinement, 06 §1), Index<N>, Bits<N>, UInt<N>, OneHot<N>, Gray<N>, Encoded<T,E>, Handle<R,Slot,Gen,Epoch>.stamped T — the one epoch keyword users write (§12). T @ 'E availability sugar for At<T, 'E> is adopted (§15): it parsed cleanly through both grammar implementations and is the fmt-canonical form; annotation only for deviation from the ambient stage.Consumer<P> / Producer<P> for binary protocols; View<Bundle, Role> is the per-role projection for ≥3-role bundles (G-S22; binary duality is provably no participant's view of a 3-role body).Rust patterns: _, literals, ranges 0..=3 =>, or-patterns A | B, tuple (n-ary, R34), Path(p1, p2), Path { field, .. }, mut x, if let. A bare lowercase path binds; a bare CamelCase path is a unit variant, resolved against the scrutinee's declared enum (R2 — wave-1's only bugs were hand-written variant-index typos). Match arms: Pattern [if guard] => expr, — exhaustive over enums; arm attributes legal.
#[attr] (F6, tiebreaker), declaration-position only. The Meta micro-grammar is committed (D-080 — attribute/expect metadata is structured, never a balanced-token soup):
| 1 | Attribute = "#" "[" Meta "]" |
| 2 | Meta = Path [ "(" CommaList(MetaItem) ")" ] |
| 3 | MetaItem = Meta | IDENT "=" MetaValue | Predicate | Literal |
| 4 | MetaValue = Literal | QuantityLit | Rate | Path | "derived" | IDENT "(" CommaList(MetaValue) ")" |
The canonical inhabitants:
#[budget(comptime_steps = 20_000_000, per_eval = derived)] — budgets are attributes and only attributes (R22); no raise API exists syntactically; per_eval = <n> is the declared-path override (R23).#[schedule(latency <= 2, throughput >= 1/cycle)] — on architectural nodes.#[expect(lint = ..., reason = "...", evidence = ..., review_by = edition(2028))] — suppression (D-057); on any item, statement, or declaration entry; never an inner attribute; one lint per attribute (G-S30).#[derive(Pack, Arbitrary, Shrink, Waveform, Symbolic)] — the five artifact families (08 §1).#[metamorphic(relation = f)], #[symmetric] (checked, R15), #[encoding(auto where preserves_debug_identity)].Two protocol dialects, one item (G-S10 rule; never mixed in one body): role-less binary bodies use the sequential dialect; bodies declaring roles use the msg + state session dialect.
Sequential dialect (catalog-style):
| 1 | protocol Burst<const N: Nat1, T> { |
| 2 | const granule = 64, // R16 shared constants |
| 3 | send Header { length: N }, |
| 4 | repeat N { send Beat<T> }, // R10: no hand-rolled beat counters |
| 5 | if broadcast { send Snoop }, // R11 conditional refinement |
| 6 | recv Completion, |
| 7 | quiesce, // R17 acceptance point |
| 8 | invariant beats_bounded: accepted(Beat) <= N, // names mandatory (R12) |
| 9 | } |
Session dialect (D-027's first customer — roles, families, ghost state, guarded FSM):
| 1 | protocol ChiReadSharedTxn<const N_RN: Nat1> { |
| 2 | role Requester, |
| 3 | role Home, |
| 4 | role Peer[N_RN - 1], // role family; cardinality monomorphized |
| 5 | msg ReadShared: ReqFlit<N_RN> from Requester to Home, |
| 6 | msg Snoop: SnpFlit<N_RN> from Home to Peer[tgt], // indexed by a payload field |
| 7 | counter snp_sent: 0..=N_RN - 1, // session-scoped ghost naturals |
| 8 | counter snp_pend[peer: Index<N_RN>]: 0..=1, // keyed family |
| 9 | latch requester: Index<N_RN>, // open-time binding |
| 10 | invariant resp_bounded: snp_rcvd <= snp_sent, |
| 11 | accept state Idle { // `accept` marks retirement states |
| 12 | on ReadShared(req) => Snooping { requester = req.src, snp_sent = 0 }, |
| 13 | }, |
| 14 | state Snooping { |
| 15 | on SnpResp(r) if snp_pend[r.src] == 1 => Snooping { snp_rcvd += 1, snp_pend[r.src] = 0 }, |
| 16 | on CompData(_) if snp_rcvd == snp_sent && if bcast { snp_sent == N_RN - 1 } => AwaitAck, |
| 17 | }, |
| 18 | } |
Counter updates after the target state are name = e / name += e (-= never arises: math naturals, total assignments or increments). from/to tags are mandatory once ≥2 roles exist (R18).
bundle — channels with roles, shared constants, cross-channel invariants (named, R12), session attachments, and quiesce points:
| 1 | bundle ChiPort<const N_RN: Nat1> { |
| 2 | const granule = 64, |
| 3 | role Rn, role Hn, role Sn, |
| 4 | req: credit<ReqFlit<N_RN>, 4> from Rn to Hn, |
| 5 | dat: layer<burst<4, DatFlit>, credit<4>> from Hn to Rn, // layer combinator (type-level) |
| 6 | vc: [layer<burst_dyn<Flit> { max_len: 8 }, credit<4>>; VCS] from Sn to Hn, // array channel; |
| 7 | // invariants apply per element |
| 8 | invariant comp_bounded_by_req: accepted(dat) <= accepted(req), |
| 9 | |
| 10 | session<txn = txn> lifecycle: ChiReadSharedTxn<N_RN> over { // spawn-per-key + projection |
| 11 | ReadShared: req, Snoop: snp, CompData: dat, CompAck: ack, |
| 12 | }, |
| 13 | session<line = addr div granule> line_serial: hazard_lock { // R13; div fence applies |
| 14 | binds: txn, // per-message key bindings (addr-less messages inherit at open) |
| 15 | scope: lifecycle, // lock acquire/release rides the named session's open/retire |
| 16 | }, |
| 17 | quiesce maintenance_barrier: at(maint.Accept), // R17; end-of-trace implicit |
| 18 | } |
The session<key = expr> clause is its own mini-grammar (a diagnostic label binding, not generic args — tree-sitter uses a dedicated session_key node).
Ordering — a bundle/stream clause: PreservesOrder<A.Accept, B.Transfer, key = TxnId>, group = id, related_by = overlaps::<W> (relations #[symmetric]-checked, R15), key_set = lines(addr, len) (R14 — one event joins every keyed order; start-line-only keying is machine-refuted), CommitsIn<ProgramOrder>. Relation args are the most heterogeneous bracket context in the language; they get a dedicated relation_args production (F12).
stream — every entry name: value, (G-S36): payload:, domain: Core (a clock_domain reference), protocol: ready_valid { payload_stable_while_blocked }, flow: { acceptance: at_most(1/cycle), outstanding: 0..=8, latency: dynamic }, identity:, ordering:, authority: { speculative: BranchEpoch, cancellation: BranchRecovery }.
contract blocks (core G13 + verif G-S4 merged) — component-body position, comma-list entries, three entry kinds plus flavors:
| 1 | contract { |
| 2 | assume payload_stable: requests.payload_stable_while_blocked(), |
| 3 | guarantee no_duplicate_response: exactly_one_response_per_request(requests, resp) |
| 4 | with evidence [ |
| 5 | Tested<17.2e9 txns>, |
| 6 | BoundedProved<depth = 256>, |
| 7 | MutationValidated<duplicate_response_mutants>, |
| 8 | ], |
| 9 | claim frequency: Measured<842 MHz, vu19p, "vivado-2026.1">, |
| 10 | } |
| 11 | contract cdc { ordering: preserved, capacity: DEPTH, ... } |
| 12 | contract death { drain_within_cycles: 64 } // or drain_within_responses: N |
claim carries physical facts — evidence about an artifact, never a guarantee of the term (D-075). Evidence literals: Ctor<args> with quantity literals, idents, name = literal pairs, strings (G-S5); evidence is a set (D-017), bracketed. Contract predicates take the ports/outputs they constrain — which is what forces named returns (G-S35). Inside contracts and requires, result names a single anonymous output (core G14).
Consumer evidence floor: require guarantee mem.no_duplicate_response with evidence >= BoundedProved; — >= compares against the set's best entry on the relevant axis.
Obligations: requires static/solve/prove + named discharge (§5). State refinements over runtime values are 06 §1's second sort; "monitors watch wires, they don't take tokens" (R33) fires on linear values in bounds. Failure-arm disposition diagnostics point at the arm (R32 — checker duty).
Generators: strategy fn name(state: &Model) -> T { ... } with weighted! { 25 => e, ... } arms (G-S24); generator binders param in strategy_expr in rule/property parameter lists, bare name: Type = the type's derived default strategy (G-S25).
state_machine (rule-based lockstep testing, G-S26 — distinct from session FSMs):
| 1 | state_machine CacheLockstep { |
| 2 | state { model: CacheModel, dut: DutHandle<L1Cache>, } |
| 3 | rule load(addr in cache_address(&model)) if dut.can_accept() { ...; } |
| 4 | invariant committed_loads_match_model { assert_eq(dut.x(), model.x()); } |
| 5 | } |
property — #[metamorphic(relation = f)] property name(binders) { stmts; assert rel(a, b); }. assert is the ghost/test-phase assertion (hardware claims stay requires/narrow!).
differential_test — comma-list block: dut: RvCore::<Rv32iMinCfg>, reference:, interface:, lockstep:, strategies: [..], coverage: [..], on_divergence: [..],.
experiment (G-S29 — designed from nothing):
| 1 | experiment mem_ctrl_depth_sweep on MemoryController { |
| 2 | target: vu19p, |
| 3 | variants: sweep { OUTSTANDING: [8, 16, 32], BANKS: 1..=2 }, // or any comptime Sweep<C> expr |
| 4 | workloads: [stream_copy(bytes = 1_048_576, seed = 7)], // explicit seeds (P-6) |
| 5 | measures: [fmax, luts, occupancy(of = self.occupancy)], // self = the DUT instance |
| 6 | faults: [dropped_response(rate = 1.0e-6, seed = 23)], |
| 7 | budget: { builds: 24, board_hours: 8 }, |
| 8 | evidence: Measured<vu19p, "vivado-2026.1">, |
| 9 | } |
Every sweep point must satisfy the DUT's where bounds up front (D-042).
The entire user-facing epoch vocabulary is three forms (R24–R26) plus the reset witness:
| 1 | port log(pkt: stamped TracePkt); // R24: the ONE epoch keyword users write |
| 2 | let sealed = stamp(head); // introduction — legal only where the surrounding |
| 3 | // port/return type says `stamped` |
| 4 | match rebind(pkt) { // R25: bind-epoch as match; whole coordinate |
| 5 | Fresh(p) => consume(p), |
| 6 | Stale(p) => { stale_drops <- stale_drops.wrapping_add(1); drop_it(p) }, |
| 7 | } |
| 8 | requires solve diff_same(a, b) { same_epoch(a, b) }; // R26 |
Stamped<T,D> and Crosses are elaboration-owned (R27); the CDC mirror chain is synthesized from rebind over a stamped stream (R30); token states carry facets structurally (R29).pkt.map(|p| ...) transforms the payload under a sealed coordinate — transport stays epoch-oblivious (core G22).stamped over a no_reset (unit-epoch) domain is the identity — so a multi-hop crossing that would produce stamped (stamped T) through a unit-epoch fabric collapses, and the inner coordinate is the one that survives.ResetWitness::mint() at the lifecycle transition, &w ω-borrows at flush sites, w.close() linear consumption — one death event per assertion, unified with the squash contract. retain reg reads elaborate as stamped (the register legally outlives reset, core G30).Additions the corpus waves demanded; normative text lives in 14:
entry(...)) keep table rows one-per-line under no-alignment; raw struct-literal rows explode 7×-vertical. Style-guide idiom + pedantic lint (verif verdict 1).on-arm wrap rule: session-FSM guards that exceed 100 cols break before =>, indent one level (verif verdict 4).<...> lists break after <, one argument per line, trailing comma legal in <> lists (verif verdict 5 — new relative to the charter).[proofs.<name>] one-key-per-line is the canonical form (inline tables can't fit a content hash in 100 cols) — D-054 spec addition (verif verdict 8).requires normalizes to ;; data bodies steer via trailing comma, statement bodies are immune — the disciplines never collide in one brace pair.guarantee/predicate/with evidence stanza; positional booleans in row constructors (style guide: prefer small enums).| R | Section | R | Section | R | Section |
|---|---|---|---|---|---|
| R1 immutability | §5 | R13 session keys | §10 | R25 rebind | §12 |
| R2 checked names / inferred guards | §4 §6 §8 | R14 key_set | §10 | R26 same_epoch | §12 |
| R3 is-sets, bind-then-match | §6 | R15 related_by symmetric | §10 §9 | R27 Crosses elab-owned | §12 |
| R4 narrow! | §5 | R16 protocol consts | §10 | R28 ResetWitness | §12 |
| R5 set rendering | §6 | R17 quiesce | §10 | R29 structural facets | §12 |
| R6 path-condition identity | §6 §11 | R18 role tags | §10 | R30 mirror chain | §12 |
| R7 infix Presburger + fence | §6 §7 | R19 Nat1 | §7 | R31 CommitProof wording | lands in 05 |
| R8 monus vs checked_sub | §6 | R20 left-Presburger fix-it | §7 | R32 arm-anchored diags | checker |
| R9 prev(x) | §6 | R21 clog2, closed symbols | §3 §7 | R33 monitor diagnostic | §11 |
| R10 repeat N | §10 | R22 budget attrs only | §9 | R34 n-ary destructuring | §5 §8 |
| R11 conditional refinement | §6 §10 | R23 no overrun ctor | §9 | R35 region markers | §5 |
| R12 named invariants | §10 | R24 stamped | §12 | R36 pass/semver/pinning | 07/13 formats; §13.4 |
Fence caveat (grammar probe): the grammar fences out compound div/mod right operands (the non-linearity); const-ness of a bare identifier is a trivial name-resolution check — non-constant expressions are grammatically impossible; non-const names are a resolution error.
R37–R50 remain requirements, not syntax. The extension wave must demonstrate all of the following before adding keywords or grammar productions here:
contract, named entries, assumptions/guarantees, requires, evidence sets, and metadata rather than each inventing a top-level mini-language unless a corpus proves that impossible.Rejected (recorded so they are not re-invented):
connect a => b; — drafted in the core corpus, never needed: named-argument instantiation is the duality-checked connect (G6; one way to write each thing).reg ... = next declaration-site next-value form — D-079: one meaning per token; <- is the only Later-boundary write. (Supersedes core G7's optional inline form and verif G-S34.)inst name = expr; binder (grammar GAP-18 sugar) — corpus never used it; let x = inst ... is the one form.discharge by ... without a target name (core F5) and unnamed guarantee {}/liveness {} blocks in arbitrate — named entries are mandatory (G-S23).r arbitrate fields (key = r.age) — explicit closures only (F6/F10).session<txn> shorthand — label ≠ field in the computed case (verif F5).expect(...) — #[expect] attribute only (G-S30). Tick-literals, as casts, bare - on hardware integers, += outside session counters, vertical alignment — per charter and 14.Resolved this pass: T @ 'E availability sugar is ADOPTED — it parsed cleanly in both the resilient-LL and tree-sitter implementations (GAP-24) and is the fmt-canonical spelling of At<T, 'E>.
Deferred, with reasons:
is_pow2 in the width algebra — candidate interpreted symbol awaiting its D-037 confluence-interaction argument; until then the blessed idiom is clog2(DEPTH + 1) == clog2(DEPTH) + 1 (core F9).pub(crate)-style finer visibility beyond pub(package) — no corpus demand (GAP-04, G-S33).at_most 1 per cycle alternative rate spelling — only needed if / ever wants to mean division at comptime (grammar note).The reconciled surface grammar. This EBNF is normative and matches the prose above; it updates the
probe's GRAMMAR.ebnf for D-079 (reg/<-), D-080 hard-reservations, and every corpus-won
construct. Notation: ISO-flavored EBNF; CommaList(X) = [ X { "," X } [ "," ] ].
| 1 | (* ---------- 0. Lexical ---------- *) |
| 2 | IDENT = ident_start { ident_continue } ; |
| 3 | LABEL = "'" IDENT ; (* domains and events; never a char literal *) |
| 4 | INT_LITERAL = dec_literal | hex_literal | bin_literal ; (* "_" separators legal *) |
| 5 | NUM_LITERAL = dec_literal [ "." dec_literal ] [ ("e"|"E") ["-"] dec_literal ] ; |
| 6 | (* fraction/exponent forms legal ONLY inside QuantityLit / MetaValue *) |
| 7 | STRING_LITERAL = '"' { string_char | escape } '"' ; (* metadata positions only *) |
| 8 | BOOL_LITERAL = "true" | "false" ; |
| 9 | QuantityLit = NUM_LITERAL UnitIdent ; (* 842 MHz, 17.2e9 txns — closed unit table *) |
| 10 | Rate = ConstExpr "/" "cycle" ; (* the only "/" in the language *) |
| 11 | COMMENT = "//" rest | "///" rest | "//!" rest ; (* anchored trivia *) |
| 12 | (* Lexer: never emits ">>" or ">="; parser glues adjacent ">" ">" / ">" "=" in |
| 13 | expression-operator position only. "..="/".." are single tokens. |
| 14 | Hard-reserved keywords per 16 §3; soft words are keywords only in the |
| 15 | positions shown below. No context-sensitive lexing. *) |
| 16 | |
| 17 | (* ---------- 1. Source file and items ---------- *) |
| 18 | SourceFile = { Item } ; |
| 19 | Item = { Attribute } { DOC_COMMENT } [ Visibility ] [ UnsafeQual ] ItemKind ; |
| 20 | Visibility = "pub" [ "(" "package" ")" ] ; |
| 21 | UnsafeQual = "unsafe" "(" IDENT ")" ; (* item-position unsafe surface *) |
| 22 | ItemKind = Component | FnItem | GhostFn | StrategyFn | Protocol | Bundle | Stream |
| 23 | | Interface | ImplItem | EnumItem | StructItem | OpaqueType | TypeAlias |
| 24 | | ClockDomain | ResourceItem | Experiment | StateMachine | Property |
| 25 | | DifferentialTest | UseItem | ModItem | ConstItem ; |
| 26 | |
| 27 | Component = "component" IDENT [ GenericParams ] ParamClause |
| 28 | [ "->" ReturnType ] [ WhereClause ] Block ; |
| 29 | ReturnType = Type | "(" CommaList( IDENT ":" Type ) ")" ; (* named tuple returns, G-S35 *) |
| 30 | FnItem = [ "comptime" ] "fn" IDENT [ GenericParams ] ParamClause |
| 31 | [ "->" Type ] [ WhereClause ] ( Block | ";" ) ; (* ";" in interfaces only *) |
| 32 | GhostFn = "ghost" FnItem ; |
| 33 | StrategyFn = "strategy" FnItem ; (* G-S24; body may use weighted! *) |
| 34 | ParamClause = "(" CommaList( Param ) ")" ; |
| 35 | Param = { Attribute } IDENT ( ":" Type | "in" Expr ) ; |
| 36 | (* "in strategy_expr" generator binders: rule/property/strategy params only *) |
| 37 | GenericParams = "<" CommaList( GenericParam ) ">" ; |
| 38 | GenericParam = IDENT [ ":" Bound ] |
| 39 | | "const" IDENT ":" Type [ "=" ConstExpr ] [ "in" RangeConst ] |
| 40 | | LABEL ; (* domain/event params: <'src, 'dst, T> *) |
| 41 | Bound = Path { "+" Path } ; |
| 42 | WhereClause = "where" WherePred { "," WherePred } ; (* NO trailing comma *) |
| 43 | WherePred = IDENT ":" Bound | Predicate ; |
| 44 | |
| 45 | Interface = [ "sealed" ] "interface" IDENT [ GenericParams ] [ WhereClause ] |
| 46 | "{" { InterfaceMember } "}" ; |
| 47 | InterfaceMember= { Attribute } ( FnItem | ConstItem | "type" IDENT ";" ) ; |
| 48 | ImplItem = "impl" [ GenericParams ] Path [ GenericArgs ] [ "for" Type ] |
| 49 | [ WhereClause ] "{" { Item } "}" ; |
| 50 | |
| 51 | EnumItem = "enum" IDENT [ GenericParams ] [ ":" Type ] (* encoding ascription, G-S1 *) |
| 52 | "{" CommaList( EnumVariant ) "}" ; |
| 53 | EnumVariant = { Attribute } IDENT [ "(" CommaList( Type ) ")" |
| 54 | | "{" CommaList( Field ) "}" |
| 55 | | "=" INT_LITERAL ] ; (* checked to fit exactly *) |
| 56 | StructItem = "struct" IDENT [ GenericParams ] [ WhereClause ] |
| 57 | ( ";" | "{" CommaList( { Attribute } [ Visibility ] Field ) "}" ) ; |
| 58 | Field = IDENT ":" Type ; |
| 59 | OpaqueType = "opaque" "type" IDENT [ GenericParams ] "=" Type ";" ; |
| 60 | TypeAlias = "type" IDENT [ GenericParams ] "=" Type ";" ; |
| 61 | ConstItem = "const" IDENT [ ":" Type ] "=" Expr ";" ; |
| 62 | UseItem = "use" UseTree ";" ; |
| 63 | UseTree = Path [ "::" ( "*" | "{" CommaList( UseTree ) "}" ) ] [ "as" IDENT ] ; |
| 64 | ModItem = "mod" IDENT ( ";" | "{" { Item } "}" ) ; |
| 65 | |
| 66 | ClockDomain = "clock_domain" IDENT "{" CommaList( DomainEntry ) "}" ; |
| 67 | DomainEntry = "clock" ":" Type |
| 68 | | "reset" ":" Type [ "{" CommaList( IDENT ":" IDENT ) "}" ] |
| 69 | | "no_reset" ; |
| 70 | ResourceItem = "resource" IDENT ":" Type [ "=" Expr ] ";" ; (* share(f) / replicate(f) *) |
| 71 | |
| 72 | (* ---------- 2. Protocols / bundles / streams (16 §10) ---------- *) |
| 73 | Protocol = "protocol" IDENT [ GenericParams ] [ WhereClause ] |
| 74 | "{" CommaList( SeqEntry ) "}" (* role-less: sequential *) |
| 75 | | "protocol" IDENT [ GenericParams ] [ WhereClause ] |
| 76 | "{" CommaList( SessEntry ) "}" ; (* roles present: session *) |
| 77 | (* one node kind, two dialects; never mixed (G-S10) *) |
| 78 | SeqEntry = ("send" | "recv") IDENT [ GenericArgs ] [ "{" CommaList( Field | FieldBind ) "}" ] |
| 79 | | "repeat" ConstExpr "{" CommaList( SeqEntry ) "}" |
| 80 | | "if" Predicate "{" CommaList( SeqEntry ) "}" |
| 81 | | "const" IDENT "=" ConstExpr |
| 82 | | "quiesce" |
| 83 | | "invariant" IDENT ":" Predicate ; |
| 84 | FieldBind = IDENT ":" ConstExpr ; |
| 85 | SessEntry = "role" IDENT [ "[" ConstExpr "]" ] (* role family *) |
| 86 | | "msg" IDENT ":" Type "from" RoleRef "to" RoleRef |
| 87 | | "counter" IDENT [ "[" IDENT ":" Type "]" ] ":" RangeConst |
| 88 | | "latch" IDENT ":" Type |
| 89 | | "invariant" IDENT ":" Predicate |
| 90 | | "const" IDENT "=" ConstExpr |
| 91 | | [ "accept" ] "state" IDENT [ "{" CommaList( OnArm ) "}" ] ; |
| 92 | RoleRef = IDENT [ "[" IDENT "]" ] ; (* Peer[tgt]: payload field *) |
| 93 | OnArm = "on" IDENT "(" Pattern ")" [ "if" Predicate ] "=>" IDENT |
| 94 | [ "{" CommaList( CounterUpdate ) "}" ] ; |
| 95 | CounterUpdate = CounterRef ( "=" | "+=" ) Expr ; |
| 96 | CounterRef = IDENT [ "[" Expr "]" ] ; |
| 97 | |
| 98 | Bundle = "bundle" IDENT [ GenericParams ] "{" CommaList( BundleEntry ) "}" ; |
| 99 | BundleEntry = "const" IDENT "=" ConstExpr |
| 100 | | "role" IDENT [ "[" ConstExpr "]" ] |
| 101 | | IDENT ":" ChannelType [ "from" RoleRef "to" RoleRef ] |
| 102 | | "invariant" IDENT ":" Predicate |
| 103 | | SessionAttach |
| 104 | | "quiesce" IDENT ":" "at" "(" Expr ")" |
| 105 | | "ordering" ":" "{" CommaList( IDENT ":" OrderingRel ) "}" ; |
| 106 | ChannelType = Type | "[" Type ";" ConstExpr "]" ; (* array channels, G-S38 *) |
| 107 | SessionAttach = "session" "<" IDENT "=" KeyExpr ">" IDENT ":" Type |
| 108 | ( "over" "{" CommaList( IDENT ":" IDENT ) "}" |
| 109 | | "{" CommaList( IDENT ":" Expr ) "}" ) ; (* hazard_lock config *) |
| 110 | KeyExpr = ConstExpr ; (* div fence applies (R13) *) |
| 111 | OrderingRel = Path "<" CommaList( RelationArg ) ">" ; (* dedicated node kind (F12) *) |
| 112 | RelationArg = EventPath | IDENT "=" ( Expr | Path Turbofish ) ; |
| 113 | EventPath = IDENT { "." IDENT } ; |
| 114 | |
| 115 | Stream = "stream" IDENT [ GenericParams ] "{" CommaList( StreamEntry ) "}" ; |
| 116 | StreamEntry = IDENT ":" ( Type | Expr | "{" CommaList( StreamEntry ) "}" ) ; |
| 117 | (* payload/domain/protocol/flow/identity/ordering/authority; |
| 118 | flow entries: acceptance: at_most(Rate), outstanding: range *) |
| 119 | |
| 120 | (* ---------- 3. Verification items (16 §11) ---------- *) |
| 121 | StateMachine = "state_machine" IDENT [ GenericParams ] "{" |
| 122 | "state" "{" CommaList( Field ) "}" |
| 123 | { RuleItem | SmInvariant } |
| 124 | "}" ; |
| 125 | RuleItem = "rule" IDENT ParamClause [ "if" Expr ] Block ; |
| 126 | SmInvariant = "invariant" IDENT Block ; |
| 127 | Property = "property" IDENT ParamClause Block ; (* + #[metamorphic] attr *) |
| 128 | DifferentialTest = "differential_test" IDENT "{" CommaList( IDENT ":" Expr ) "}" ; |
| 129 | Experiment = "experiment" IDENT "on" Path "{" CommaList( ExpEntry ) "}" ; |
| 130 | ExpEntry = IDENT ":" ( Expr | SweepLit | "{" CommaList( IDENT ":" Expr ) "}" |
| 131 | | "[" CommaList( Expr ) "]" | EvidenceLit ) ; |
| 132 | SweepLit = "sweep" "{" CommaList( IDENT ":" ( "[" CommaList(ConstExpr) "]" | RangeConst ) ) "}" ; |
| 133 | EvidenceLit = Path "<" CommaList( EvidenceArg ) ">" ; |
| 134 | EvidenceArg = QuantityLit | IDENT | IDENT "=" Literal | STRING_LITERAL ; |
| 135 | |
| 136 | (* ---------- 4. Attributes: the committed Meta micro-grammar (16 §9) ---------- *) |
| 137 | Attribute = "#" "[" Meta "]" ; |
| 138 | Meta = Path [ "(" CommaList( MetaItem ) ")" ] ; |
| 139 | MetaItem = Meta | IDENT "=" MetaValue | Predicate | Literal ; |
| 140 | MetaValue = Literal | QuantityLit | Rate | Path | "derived" |
| 141 | | IDENT "(" CommaList( MetaValue ) ")" ; |
| 142 | |
| 143 | (* ---------- 5. Statements (16 §5) ---------- *) |
| 144 | Block = "{" { Stmt } [ Expr ] "}" ; |
| 145 | Stmt = LetStmt | RegStmt | RegUpdate | MemStmt | ComptimeStmt | GhostStmt |
| 146 | | RequiresStmt | DischargeStmt | RequireGuarantee | NarrowStmt | AssertStmt |
| 147 | | StageStmt | PortStmt | SequenceStmt | ForStmt | ReturnStmt | RegionStmt |
| 148 | | ContractBlock | ResourceItem | Item | ExprStmt | ";" ; |
| 149 | LetStmt = "let" [ "mut" ] Pattern [ ":" Type ] "=" Expr ";" ; |
| 150 | RegStmt = [ "retain" ] "reg" IDENT ":" Type [ "reset" "(" Expr ")" ] [ "uninit" ] ";" ; |
| 151 | (* D-079: reset mandatory unless no_reset domain or uninit; NO "=" form *) |
| 152 | RegUpdate = LValue "<-" Expr ";" ; (* next-cycle write; last-write-wins per body *) |
| 153 | LValue = IDENT { "[" Expr "]" | "." IDENT } ; |
| 154 | MemStmt = "mem" IDENT ":" "[" Type ";" ConstExpr "]" ";" ; |
| 155 | ComptimeStmt = "comptime" ( LetStmt | ForStmt | Block |
| 156 | | "param" IDENT ":" Type "in" RangeConst ";" |
| 157 | | "fixed" IDENT ":" Type "=" Expr ";" ) ; |
| 158 | GhostStmt = "ghost" LetStmt ; |
| 159 | RequiresStmt = "requires" ("static" | "solve" | "prove") [ IDENT [ ParamClause ] ] |
| 160 | "{" Predicate "}" ";" ; (* name mandatory on solve/prove; |
| 161 | ";" canonical, "," accepted + fmt-normalized *) |
| 162 | DischargeStmt = "discharge" IDENT "by" IDENT "(" CommaList( IDENT | Expr ) ")" ";" ; |
| 163 | RequireGuarantee = "require" "guarantee" Path "with" "evidence" ">=" Path ";" ; |
| 164 | NarrowStmt = "narrow" "!" "(" Predicate ")" ";" ; |
| 165 | AssertStmt = "assert" Expr ";" ; (* ghost/test phase only *) |
| 166 | StageStmt = "stage" [ LABEL ] ";" ; |
| 167 | PortStmt = "port" IDENT ParamClause [ "->" Type ] ( Block | ";" ) ; |
| 168 | SequenceStmt = "sequence" Block ; (* await/halt/loop legal inside *) |
| 169 | ForStmt = "for" Pattern "in" Expr Block ; |
| 170 | ReturnStmt = "return" [ Expr ] ";" ; |
| 171 | RegionStmt = ( "elastic" | "cycleobs" ) Block ; |
| 172 | ContractBlock = "contract" [ IDENT ] "{" CommaList( ContractEntry ) "}" ; |
| 173 | ContractEntry = "assume" IDENT ":" Predicate |
| 174 | | "guarantee" IDENT ":" Predicate [ "with" "evidence" "[" CommaList(EvidenceLit) "]" ] |
| 175 | | "claim" IDENT ":" EvidenceLit |
| 176 | | IDENT ":" ( Expr | EvidenceLit ) ; (* flavored: cdc / death entries *) |
| 177 | ExprStmt = Expr ";" | BlockLikeExpr ; (* match/if/for/domain/blocks need no ";" *) |
| 178 | |
| 179 | (* ---------- 6. Expressions (16 §6) ---------- *) |
| 180 | Expr = AssignExpr ; |
| 181 | AssignExpr = RangeExpr [ "=" AssignExpr ] ; (* lvalue-checked post-parse *) |
| 182 | RangeExpr = OrExpr [ ( ".." | "..=" ) OrExpr ] | ( ".." | "..=" ) OrExpr ; |
| 183 | OrExpr = AndExpr { "||" AndExpr } ; |
| 184 | AndExpr = CmpExpr { "&&" CmpExpr } ; |
| 185 | CmpExpr = ConcatExpr [ CmpOp ConcatExpr ] | ConcatExpr "is" VariantSet ; |
| 186 | CmpOp = "==" | "!=" | "<" | "<=" | ">" | ">=" ; (* ">"/" >=" glued *) |
| 187 | VariantSet = Path { "|" Path } ; |
| 188 | ConcatExpr = ShiftExpr { "++" ShiftExpr } ; |
| 189 | ShiftExpr = AddExpr { ( "<<" | ">>" ) AddExpr } ; |
| 190 | AddExpr = MulExpr { ( "+" | "-" ) MulExpr } ; |
| 191 | MulExpr = UnaryExpr { "*" UnaryExpr | ( "div" | "mod" ) ConstAtom } ; |
| 192 | (* THE PRESBURGER FENCE (R7): div/mod right operand is a ConstAtom |
| 193 | by grammar; const-ness of a Path is a resolution check *) |
| 194 | ConstAtom = INT_LITERAL | Path ; |
| 195 | UnaryExpr = ( "!" | "-" ) UnaryExpr | PostfixExpr ; |
| 196 | PostfixExpr = PrimaryExpr { Postfix } ; |
| 197 | Postfix = "." IDENT [ Turbofish ] [ CallArgs ] | Turbofish CallArgs | CallArgs |
| 198 | | "[" Expr "]" | "?" ; |
| 199 | Turbofish = "::" "<" CommaList( GenericArg ) ">" ; (* mandatory in expr position *) |
| 200 | CallArgs = "(" CommaList( Expr | ClosureExpr | IDENT "=" Expr ) ")" ; |
| 201 | (* IDENT "=" only in builtin forms: accept(ready = e), policy keys, measures *) |
| 202 | PrimaryExpr = Literal | QuantityLit (* quantity positions only *) |
| 203 | | Path | Path StructLiteralBody (* restricted: see CondExpr *) |
| 204 | | "(" CommaList( Expr ) ")" | "[" CommaList( Expr ) "]" |
| 205 | | Block | IfExpr | IfLetExpr | MatchExpr | LoopExpr |
| 206 | | InstExpr | HandshakeExpr | ArbitrateExpr | DomainExpr | ProcessExpr |
| 207 | | AwaitExpr | "halt" | ClosureExpr | UnsafeExpr | MacroExpr |
| 208 | | LABEL [ ("+"|"-") ConstAtom ] ; (* event expr: 'G + 2 *) |
| 209 | Path = IDENT { "::" IDENT } ; |
| 210 | StructLiteralBody = "{" CommaList( IDENT [ ":" Expr ] | ".." Expr ) "}" ; (* punning + FRU *) |
| 211 | InstExpr = "inst" Path [ Turbofish ] StructLiteralBody ; (* named-only + punning *) |
| 212 | HandshakeExpr = "handshake" Path Turbofish |
| 213 | ( StructLiteralBody | "from" Expr ) ; (* core G11 *) |
| 214 | ArbitrateExpr = "arbitrate" [ IDENT ] "{" CommaList( ArbEntry ) "}" ; |
| 215 | ArbEntry = "inputs" ":" Expr | "eligibility" ":" ClosureExpr |
| 216 | | "policy" ":" Expr | "resource" ":" Expr |
| 217 | | "guarantee" IDENT ":" Predicate |
| 218 | | "liveness" IDENT ":" Predicate [ "under" Expr ] ; (* names mandatory, G-S23 *) |
| 219 | DomainExpr = "domain" LABEL Block ; (* an expression *) |
| 220 | ProcessExpr = "process" "{" CommaList( IDENT ":" ( Stmt-fragment | Expr ) ) "}" ; |
| 221 | (* step: <reg-update>, done: Bool, ok: S, err: Option<F> — core G23 *) |
| 222 | AwaitExpr = "await" Expr ; (* inside sequence only *) |
| 223 | LoopExpr = "loop" Block ; |
| 224 | IfExpr = "if" CondExpr Block [ "else" ( IfExpr | Block ) ] ; |
| 225 | IfLetExpr = "if" "let" Pattern "=" CondExpr Block [ "else" Block ] ; |
| 226 | MatchExpr = "match" CondExpr "{" CommaList( MatchArm ) "}" ; |
| 227 | MatchArm = { Attribute } Pattern [ "if" CondExpr ] "=>" Expr ; |
| 228 | CondExpr = Expr ; (* StructLiteralBody FORBIDDEN at top level (D-080); |
| 229 | transitively through operators, not through parens/args *) |
| 230 | ClosureExpr = "|" CommaList( Pattern [ ":" Type ] ) "|" Expr ; |
| 231 | UnsafeExpr = "unsafe" "(" IDENT [ "," "reason" "=" STRING_LITERAL ] ")" Block ; |
| 232 | MacroExpr = IDENT "!" "{" CommaList( MacroArm | IDENT ":" Expr ) "}" ; |
| 233 | MacroArm = INT_LITERAL "=>" Expr ; (* weighted! / config! *) |
| 234 | Literal = INT_LITERAL | BOOL_LITERAL | STRING_LITERAL ; |
| 235 | |
| 236 | (* ---------- 7. Predicates ---------- *) |
| 237 | Predicate = Expr | "if" Expr "{" Predicate "}" ; |
| 238 | (* implication sugar, predicate position only (R11): !cond || pred. |
| 239 | Predicates share the Expr grammar (one precedence table); tier |
| 240 | membership is semantic EXCEPT the div/mod fence, which is syntactic. |
| 241 | Interpreted symbols (closed): clog2 max min prev accepted |
| 242 | transferred outstanding same_epoch. *) |
| 243 | |
| 244 | (* ---------- 8. Types (16 §7) ---------- *) |
| 245 | Type = [ "stamped" ] [ LABEL ] TypeCore [ "@" EventExpr ] (* T @ 'E adopted *) |
| 246 | | "&" Type ; (* port-surface borrows, ghost borrows *) |
| 247 | TypeCore = Path [ GenericArgs ] [ TypeConfigBlock ] |
| 248 | | "(" CommaList( Type ) ")" |
| 249 | | "[" Type ";" ConstExpr "]" |
| 250 | | "fn" "(" CommaList( Type ) ")" [ "->" Type ] ; (* comptime-phase only *) |
| 251 | TypeConfigBlock= "{" CommaList( IDENT [ ":" ConstExpr ] ) "}" ; (* schema config; TYPE POSITION ONLY *) |
| 252 | EventExpr = LABEL [ ("+"|"-") ConstAtom ] ; |
| 253 | GenericArgs = "<" CommaList( GenericArg ) ">" ; |
| 254 | GenericArg = IDENT "=" ConstExpr | RangeConst | TypeOrConst | LABEL ; |
| 255 | TypeOrConst = Type | ConstExpr ; (* unified on Path prefix; elaboration sorts *) |
| 256 | RangeConst = [ ConstExpr ] ( ".." | "..=" ) ConstExpr ; |
| 257 | (* Angle-bracket sub-language: NO comparisons, shifts, "&&"/"||", struct literals |
| 258 | inside "<...>" — ">" always closes, "," always separates; no symbol table, |
| 259 | no brace fallback (D-080). *) |
| 260 | ConstExpr = ConstMul { ("+" | "-") ConstMul } ; (* "-" = monus at type level *) |
| 261 | ConstMul = ConstPrimary { "*" ConstPrimary | ("div" | "mod") ConstAtom } ; |
| 262 | ConstPrimary = INT_LITERAL | Path [ GenericArgs ] |
| 263 | | ("clog2" | "max" | "min") "(" CommaList( ConstExpr ) ")" |
| 264 | | "(" ConstExpr ")" ; |
| 265 | |
| 266 | (* ---------- 9. Patterns (16 §8) ---------- *) |
| 267 | Pattern = PatternNoAlt { "|" PatternNoAlt } ; |
| 268 | PatternNoAlt = "_" | Literal |
| 269 | | INT_LITERAL ("..=" | "..") INT_LITERAL |
| 270 | | Path [ "(" CommaList( Pattern ) ")" | "{" CommaList( IDENT [ ":" Pattern ] | ".." ) "}" ] |
| 271 | | "(" CommaList( Pattern ) ")" | "mut" IDENT ; |
| 272 | |
| 273 | (* ---------- 10. Resilient-LL anchors (informative) ---------- *) |
| 274 | (* Every ItemKind and most statements open with a unique hard keyword; the |
| 275 | item-recovery set is FIRST(Item), the statement set adds the statement |
| 276 | openers + ";" "}". ERROR nodes wrap skipped tokens; MISSING tokens are |
| 277 | zero-width; text(tree) == input on ALL inputs (D-062). *) |
D-077 (charter, SETTLED — Veronica), D-078 (this spec, DRAFT-COMPLETE), D-079 (reg forms,
SETTLED), D-080 (grammar-evidence decisions, SETTLED) in decisions.md. Remaining
gates before D-078 settles: the fmt dry-run (D-049–D-053 over both corpora, §13 rules) and the
blind-regeneration pass (a fresh agent rewrites the corpora from this document alone; divergence
measures spec completeness).