1pub struct KeywordInfo {
12 pub word: &'static str,
13 pub meaning: &'static str,
14}
15
16pub const KEYWORDS: &[KeywordInfo] = &[
18 k("Bool", "The boolean base type."),
19 k(
20 "Bytes",
21 "The binary base type — an immutable octet sequence, erased to `Uint8Array` (`Bytes.fromUtf8(s)`).",
22 ),
23 k(
24 "Decimal",
25 "The exact decimal base type — an arbitrary-precision coefficient with a scale (`1.20d`).",
26 ),
27 k(
28 "Duration",
29 "The time-span base type, in milliseconds (`5.minutes`).",
30 ),
31 k("Effect", "The effectful-computation type, `Effect[T]`."),
32 k("Err", "The error variant of `Result`."),
33 k("Float", "The floating-point base type."),
34 k(
35 "Instant",
36 "The absolute-time base type, in epoch milliseconds (`Clock.now()`).",
37 ),
38 k("Int", "The integer base type."),
39 k(
40 "JsonError",
41 "The JSON-decode error type, `Result[T, JsonError]` from `Json.decode`. Its fields are `kind` (`Malformed`, `StructuralMismatch` or `RefinementViolation`), `path` (e.g. `$.qty`) and `message`, all `String`: write `\"\\(e.kind) at \\(e.path): \\(e.message)\"`.",
42 ),
43 k("None", "The empty variant of `Option`."),
44 k("Ok", "The success variant of `Result`."),
45 k("Option", "The optional-value type, `Option[T]`."),
46 k("Result", "The success-or-error type, `Result[T, E]`."),
47 k("Some", "The present variant of `Option`."),
48 k("String", "The string base type."),
49 k(
50 "ValidationError",
51 "The error type returned by a refined type's `.of`. Its fields are `field` and `message`, both `String`: write `\"\\(e.field): \\(e.message)\"`.",
52 ),
53 k(
54 "actor",
55 "Declare an actor — a boundary contract a handler consumes via `by`.",
56 ),
57 k(
58 "adapter",
59 "Declare an adapter — the host boundary (capability contract + binding).",
60 ),
61 k("agent", "Declare a stateful, keyed agent inside a context."),
62 k(
63 "as",
64 "Alias a consumed context (`consumes X as Y`) or a consumed type (`consumes U { T as Y }`).",
65 ),
66 k(
67 "asset",
68 "Declare a static file the platform serves, public by construction (`asset name = \"path\"`).",
69 ),
70 k(
71 "binding",
72 "Name an adapter's TypeScript binding module (`binding \"<module>\"`).",
73 ),
74 k(
75 "by",
76 "Name the actor a handler consumes, after the return type — or a service-level default on the header (`… -> T by <name>: <Actor>`).",
77 ),
78 k(
79 "capability",
80 "Declare a capability (a dependency interface) in a context, or a distinct instance of a platform resource (`capability Sessions = Kv`).",
81 ),
82 k(
83 "case",
84 "Declare a test case inside a `suite` (`case \"…\" { … }`).",
85 ),
86 k(
87 "commons",
88 "Declare a pure, stateless module of types and functions.",
89 ),
90 k(
91 "const",
92 "Declare a named constant — a static value of a declared type, in a context or commons (`const MaxRetries: Int = 3`).",
93 ),
94 k(
95 "consumes",
96 "Declare a dependency on another context's services.",
97 ),
98 k(
99 "context",
100 "Declare a deployable context (services, agents, capabilities).",
101 ),
102 k(
103 "cron",
104 "The cron protocol on a service header (`from cron`).",
105 ),
106 k(
107 "do",
108 "Perform a unit effect as a statement (`do e` — the binder-free `let _ <- e`).",
109 ),
110 k(
111 "else",
112 "The alternative branch of an `if`, or what a block produces when a `let` pattern does not match (`let Ok(x) = r else { … }`).",
113 ),
114 k(
115 "embed",
116 "Declare a project file whose text is a `String` constant, compiled into the code and never served (`embed name = \"path\"`).",
117 ),
118 k(
119 "ensures",
120 "Declare a function postcondition — a pure `Bool` clause over the parameters and `result` (`ensures <name>: <pred>`).",
121 ),
122 k("enum", "Declare a payloadless sum type (`enum { A, B }`)."),
123 k(
124 "event",
125 "Declare a typed fact a context may emit (`event Name = { fields }`), inside a context.",
126 ),
127 k(
128 "expect",
129 "Assert a predicate inside a test case (`expect <bool-predicate>`).",
130 ),
131 k("exports", "Declare which types a context exposes, and how."),
132 k("false", "The boolean literal `false`."),
133 k("fn", "Declare a function."),
134 k(
135 "from",
136 "Name the protocol a service conforms to (`service X from http`).",
137 ),
138 k("given", "Declare the capabilities a handler requires."),
139 k(
140 "http",
141 "The HTTP protocol on a service header (`from http`).",
142 ),
143 k("if", "A conditional expression."),
144 k(
145 "implies",
146 "Logical implication (`P implies Q` ≡ `!P || Q`), used in invariant predicates.",
147 ),
148 k(
149 "invariant",
150 "Declare an agent invariant — a predicate that must hold of every committed state.",
151 ),
152 k(
153 "is",
154 "Test a value against a variant pattern, yielding a `Bool`.",
155 ),
156 k(
157 "let",
158 "Bind a local value (`let x = …`, or `let x <- …` for an effect), or match a pattern with a fallback (`let Ok(x) = r else { … }`).",
159 ),
160 k(
161 "match",
162 "Pattern-match over a sum type, `Result`, or `Option`.",
163 ),
164 k(
165 "messages",
166 "Declare a message bundle for one locale (`messages \"<tag>\" { \"code\" => \"template\" }`), inside a commons.",
167 ),
168 k(
169 "on",
170 "Begin a handler declaration (`on call`, `on GET(…)`, `on message`, `on open`/`on close`).",
171 ),
172 k(
173 "opaque",
174 "Declare an opaque type, or export a type opaquely.",
175 ),
176 k(
177 "property",
178 "Declare a generative test inside a `suite` (`property \"…\" { for all … }`).",
179 ),
180 k(
181 "protocol",
182 "Reserved keyword (protocols are a closed, compiler-known set).",
183 ),
184 k("provides", "Provide an implementation of a capability."),
185 k(
186 "queue",
187 "The queue protocol on a service header (`from queue(\"name\")`).",
188 ),
189 k(
190 "record",
191 "Reserved keyword (records are written `type X = { … }`).",
192 ),
193 k(
194 "requires",
195 "Declare a function precondition — a pure `Bool` clause over the parameters (`requires <name>: <pred>`).",
196 ),
197 k("self", "The current agent instance, inside a handler."),
198 k(
199 "service",
200 "Declare a service (a group of handlers) in a context.",
201 ),
202 k(
203 "stub",
204 "Stub a consumed capability operation at a test seam (`stub Cap.op(…) returns <v>` / `returns <v> after <d>` / `fails` / `never`).",
205 ),
206 k(
207 "suite",
208 "Declare a test suite targeting a unit (`suite <target> { case … }`).",
209 ),
210 k(
211 "transition",
212 "Declare an agent step invariant over the `old`/`new` state pair (`transition <name>: …`).",
213 ),
214 k(
215 "transparent",
216 "Export a type with its structure visible (`exports transparent { … }`).",
217 ),
218 k("true", "The boolean literal `true`."),
219 k(
220 "type",
221 "Declare a type: alias, record, sum, opaque, or refined.",
222 ),
223 k("uses", "Bring a commons into scope."),
224 k("where", "Attach refinement predicates to a base type."),
225];
226
227pub const CONTEXTUAL_KEYWORDS: &[KeywordInfo] = &[
235 k(
236 "advance",
237 "Move the test harness's clock forward in a `case` (`advance Clock by 15.minutes`).",
238 ),
239 k(
240 "derives",
241 "After a type declaration's body, opts the type into a constraint (`type Priority = enum { Low, High } derives Ord`): the type is then ordered by declaration order.",
242 ),
243 k(
244 "drop",
245 "Inside a `migrate` step, discard a stored field's value on purpose (`drop owner`).",
246 ),
247 k(
248 "fixture",
249 "In a suite, declares a project file's text as a `String` constant for its cases (`fixture issue = \"fixtures/issue.json\"`): test-only, read from the `fixtures/` folder and never deployed.",
250 ),
251 k(
252 "key",
253 "The agent's identity field — one per agent; keys the store.",
254 ),
255 k(
256 "migrate",
257 "An agent's migration step: carries a stored record from an older schema version to the current shape (`migrate from 1 (old: CartV1) { … }`).",
258 ),
259 k(
260 "par",
261 "Opens a concurrent binding block (`par { let a <- e1 let b <- e2 }`): every effect is issued before any is awaited, and the block completes when all have settled.",
262 ),
263 k(
264 "private",
265 "Before a `fn`, `type`, `messages`, `asset`, `embed` or `const` declaration in a commons or context: visible only within its own unit (and that unit's suites), never through `uses` (`private fn helper(…)`).",
266 ),
267 k(
268 "removed",
269 "Declares an agent class gone (`removed agent Old`): its stored data is deleted at the next deploy, after a named confirmation.",
270 ),
271 k(
272 "replaces",
273 "Renames an agent class (`agent New replaces Old`): the old class's stored data and schema history move to the new one.",
274 ),
275 k("store", "A persisted agent-state field."),
276];
277
278pub const RESERVED_CONTEXTUAL: &[&str] = &[
291 "asset", "case", "const", "embed", "event", "messages", "on", "suite",
292];
293
294pub fn is_reserved_contextual(word: &str) -> bool {
300 RESERVED_CONTEXTUAL.contains(&word)
301}
302
303pub const BUILTIN_TYPE_NAMES: &[KeywordInfo] = &[
317 k(
318 "Asset",
319 "A static file declared by `asset name = \"path\"`; its `.url` is the content-hashed URL the platform serves it at.",
320 ),
321 k(
322 "BoundaryRejection",
323 "Why the router refused a request before any handler ran; the parameter of `on rejected`.",
324 ),
325 k(
326 "Connection",
327 "A held WebSocket connection, `Connection[F]`.",
328 ),
329 k(
330 "History",
331 "A generated call-history generator, `History[Agent]` (test-only).",
332 ),
333 k(
334 "HttpResult",
335 "The HTTP handler result type, `HttpResult[T]`.",
336 ),
337 k("List", "The immutable list type, `List[T]`."),
338 k("Map", "The immutable map type, `Map[K, V]`."),
339 k("Query", "The lazy storage-read type, `Query[T]`."),
340 k(
341 "QueueResult",
342 "The queue handler result type (non-generic).",
343 ),
344 k(
345 "RequestHeaders",
346 "An HTTP handler's read-only view of the request's headers (`headers: RequestHeaders`).",
347 ),
348 k(
349 "RequestQuery",
350 "An HTTP handler's read-only view of the request's query string (`query: RequestQuery`).",
351 ),
352 k("Set", "The immutable set type, `Set[T]`."),
353 k("Stream", "The value-over-time primitive, `Stream[T]`."),
354 k(
355 "TokenClaims",
356 "An `Oidc` actor's verified token claims, read through its binder (`who.claims`).",
357 ),
358];
359
360pub const BUILTIN_FUNCTIONS: &[KeywordInfo] = &[
365 k(
366 "todo",
367 "Placeholder for unfinished code, `todo(message: String)`: type-checks anywhere, faults when run, and raises the `bynk.fault.todo` warning so `check --deny-warnings` can refuse it.",
368 ),
369 k(
370 "unreachable",
371 "Mark a case that cannot happen, `unreachable(message: String)`: type-checks anywhere and faults when evaluated, so a broken assumption is loud instead of masked by a made-up default.",
372 ),
373];
374
375pub fn is_builtin_type_name(name: &str) -> bool {
378 BUILTIN_TYPE_NAMES.iter().any(|b| b.word == name)
379}
380
381const fn k(word: &'static str, meaning: &'static str) -> KeywordInfo {
382 KeywordInfo { word, meaning }
383}
384
385pub fn render_markdown() -> String {
389 let mut out = String::new();
390 out.push_str("# Keywords\n\n");
391 out.push_str(
392 "<!-- GENERATED FILE — do not edit by hand.\n \
393 Source: bynk-syntax/src/keywords.rs (`render_markdown`).\n \
394 Regenerate with: BYNK_BLESS=1 cargo test -p xtask --test docs_consistency keywords_reference:: -->\n\n",
395 );
396 out.push_str(
397 "Bynk reserves names in four tiers. The first two are lexer keywords; the \
398 third are words the lexer treats as plain identifiers; the fourth are \
399 compiler-known type names. Only the **hard keywords** can never be used \
400 as an identifier. A last section lists the ambient built-in functions, \
401 which reserve nothing.\n\n",
402 );
403 out.push_str(
404 "A hard keyword is still allowed where a name is a *label* rather than a \
405 binding, so a field can carry the key a third-party payload uses \
406 (`type`, `from`, `self`):\n\n\
407 - a record field, a construction initialiser, a record or named pattern \
408 field and a variant payload field, when the keyword is followed by `:` \
409 (`{ type: String }`, `Account { type: \"Org\" }`, `Account { type: t, .. }`, \
410 `Push(from: String)`). `kind` stays reserved as a variant payload field.\n\
411 - a member after `.`, a field access or a method call, and a method \
412 declaration, when the keyword is on the same line as the `.` \
413 (`a.type`, `fn Sorted.from(…)`, `Sorted.from(xs)`).\n\n\
414 A keyword is never a parameter, a `let` or lambda binding or a bare \
415 reference, so field shorthand is refused: `Account { type, .. }` and \
416 `Account { type }` are `bynk.parse.keyword_field_shorthand`. Write \
417 `type: type_` in a pattern and `type: <value>` in a construction.\n\n",
418 );
419
420 let hard: Vec<&KeywordInfo> = KEYWORDS
422 .iter()
423 .filter(|k| !RESERVED_CONTEXTUAL.contains(&k.word))
424 .collect();
425 out.push_str("## Hard keywords\n\n");
426 out.push_str(&format!(
427 "Reserved everywhere — these **{}** words can never be used as an \
428 identifier.\n\n",
429 hard.len()
430 ));
431 out.push_str("| Keyword | Meaning |\n|---|---|\n");
432 for info in hard {
433 out.push_str(&format!("| `{}` | {} |\n", info.word, info.meaning));
434 }
435
436 let contextual: Vec<&KeywordInfo> = RESERVED_CONTEXTUAL
439 .iter()
440 .filter_map(|w| KEYWORDS.iter().find(|k| &k.word == w))
441 .collect();
442 out.push_str("\n## Contextual keywords\n\n");
443 out.push_str(
444 "Reserved only in the one position named below; elsewhere (a field, \
445 parameter, or other identifier) they are ordinary names.\n\n",
446 );
447 out.push_str("| Keyword | Meaning |\n|---|---|\n");
448 for info in contextual {
449 out.push_str(&format!("| `{}` | {} |\n", info.word, info.meaning));
450 }
451
452 out.push_str("\n## Contextual words\n\n");
455 out.push_str(
456 "Not lexer keywords: the lexer treats these as ordinary identifiers, and \
457 they carry meaning only in the position each entry describes (inside an \
458 agent body or a `migrate` step, for instance). You can still use them as a \
459 field, parameter, or other name elsewhere.\n\n",
460 );
461 out.push_str("| Word | Meaning |\n|---|---|\n");
462 for info in CONTEXTUAL_KEYWORDS {
463 out.push_str(&format!("| `{}` | {} |\n", info.word, info.meaning));
464 }
465
466 out.push_str("\n## Built-in type names\n\n");
469 out.push_str(
470 "Compiler-known type constructors. They are not lexer keywords — you may \
471 use them as an identifier in value position — but they are reserved in \
472 type position: a `type` declaration may not reuse one of these names \
473 (`bynk.resolve.reserved_builtin_type`).\n\n",
474 );
475 out.push_str("| Name | Meaning |\n|---|---|\n");
476 for info in BUILTIN_TYPE_NAMES {
477 out.push_str(&format!("| `{}` | {} |\n", info.word, info.meaning));
478 }
479
480 out.push_str("\n## Built-in functions\n\n");
483 out.push_str(
484 "Functions the compiler provides without a declaration. They reserve \
485 nothing: a `fn`, parameter or local of the same name shadows the built-in.\n\n",
486 );
487 out.push_str("| Name | Meaning |\n|---|---|\n");
488 for info in BUILTIN_FUNCTIONS {
489 out.push_str(&format!("| `{}` | {} |\n", info.word, info.meaning));
490 }
491 out
492}