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 "Duration",
25 "The time-span base type, in milliseconds (`5.minutes`).",
26 ),
27 k("Effect", "The effectful-computation type, `Effect[T]`."),
28 k("Err", "The error variant of `Result`."),
29 k("Float", "The floating-point base type."),
30 k(
31 "Instant",
32 "The absolute-time base type, in epoch milliseconds (`Clock.now()`).",
33 ),
34 k("Int", "The integer base type."),
35 k(
36 "JsonError",
37 "The JSON-decode error type, `Result[T, JsonError]` from `Json.decode`.",
38 ),
39 k("None", "The empty variant of `Option`."),
40 k("Ok", "The success variant of `Result`."),
41 k("Option", "The optional-value type, `Option[T]`."),
42 k("Result", "The success-or-error type, `Result[T, E]`."),
43 k("Some", "The present variant of `Option`."),
44 k("String", "The string base type."),
45 k(
46 "ValidationError",
47 "The error type returned by a refined type's `.of`.",
48 ),
49 k(
50 "actor",
51 "Declare an actor — a boundary contract a handler consumes via `by`.",
52 ),
53 k(
54 "adapter",
55 "Declare an adapter — the host boundary (capability contract + binding).",
56 ),
57 k("agent", "Declare a stateful, keyed agent inside a context."),
58 k("as", "Alias a consumed context (`consumes X as Y`)."),
59 k(
60 "asset",
61 "Declare a static file the platform serves, public by construction (`asset name = \"path\"`).",
62 ),
63 k(
64 "binding",
65 "Name an adapter's TypeScript binding module (`binding \"<module>\"`).",
66 ),
67 k(
68 "by",
69 "Name the actor a handler consumes, after the return type — or a service-level default on the header (`… -> T by <name>: <Actor>`).",
70 ),
71 k(
72 "capability",
73 "Declare a capability (a dependency interface) in a context, or a distinct instance of a platform resource (`capability Sessions = Kv`).",
74 ),
75 k(
76 "case",
77 "Declare a test case inside a `suite` (`case \"…\" { … }`).",
78 ),
79 k(
80 "commons",
81 "Declare a pure, stateless module of types and functions.",
82 ),
83 k(
84 "consumes",
85 "Declare a dependency on another context's services.",
86 ),
87 k(
88 "context",
89 "Declare a deployable context (services, agents, capabilities).",
90 ),
91 k(
92 "cron",
93 "The cron protocol on a service header (`from cron`).",
94 ),
95 k(
96 "do",
97 "Perform a unit effect as a statement (`do e` — the binder-free `let _ <- e`).",
98 ),
99 k(
100 "else",
101 "The alternative branch of an `if`, or what a block produces when a `let` pattern does not match (`let Ok(x) = r else { … }`).",
102 ),
103 k(
104 "embed",
105 "Declare a project file whose text is a `String` constant, compiled into the code and never served (`embed name = \"path\"`).",
106 ),
107 k(
108 "ensures",
109 "Declare a function postcondition — a pure `Bool` clause over the parameters and `result` (`ensures <name>: <pred>`).",
110 ),
111 k("enum", "Declare a payloadless sum type (`enum { A, B }`)."),
112 k(
113 "event",
114 "Declare a typed fact a context may emit (`event Name = { fields }`), inside a context.",
115 ),
116 k(
117 "expect",
118 "Assert a predicate inside a test case (`expect <bool-predicate>`).",
119 ),
120 k("exports", "Declare which types a context exposes, and how."),
121 k("false", "The boolean literal `false`."),
122 k("fn", "Declare a function."),
123 k(
124 "from",
125 "Name the protocol a service conforms to (`service X from http`).",
126 ),
127 k("given", "Declare the capabilities a handler requires."),
128 k(
129 "http",
130 "The HTTP protocol on a service header (`from http`).",
131 ),
132 k("if", "A conditional expression."),
133 k(
134 "implies",
135 "Logical implication (`P implies Q` ≡ `!P || Q`), used in invariant predicates.",
136 ),
137 k(
138 "invariant",
139 "Declare an agent invariant — a predicate that must hold of every committed state.",
140 ),
141 k(
142 "is",
143 "Test a value against a variant pattern, yielding a `Bool`.",
144 ),
145 k(
146 "let",
147 "Bind a local value (`let x = …`, or `let x <- …` for an effect), or match a pattern with a fallback (`let Ok(x) = r else { … }`).",
148 ),
149 k(
150 "match",
151 "Pattern-match over a sum type, `Result`, or `Option`.",
152 ),
153 k(
154 "messages",
155 "Declare a message bundle for one locale (`messages \"<tag>\" { \"code\" => \"template\" }`), inside a commons.",
156 ),
157 k(
158 "on",
159 "Begin a handler declaration (`on call`, `on GET(…)`, `on message`, `on open`/`on close`).",
160 ),
161 k(
162 "opaque",
163 "Declare an opaque type, or export a type opaquely.",
164 ),
165 k(
166 "property",
167 "Declare a generative test inside a `suite` (`property \"…\" { for all … }`).",
168 ),
169 k(
170 "protocol",
171 "Reserved keyword (protocols are a closed, compiler-known set).",
172 ),
173 k("provides", "Provide an implementation of a capability."),
174 k(
175 "queue",
176 "The queue protocol on a service header (`from queue(\"name\")`).",
177 ),
178 k(
179 "record",
180 "Reserved keyword (records are written `type X = { … }`).",
181 ),
182 k(
183 "requires",
184 "Declare a function precondition — a pure `Bool` clause over the parameters (`requires <name>: <pred>`).",
185 ),
186 k("self", "The current agent instance, inside a handler."),
187 k(
188 "service",
189 "Declare a service (a group of handlers) in a context.",
190 ),
191 k(
192 "stub",
193 "Stub a consumed capability operation at a test seam (`stub Cap.op(…) returns <v>` / `fails`).",
194 ),
195 k(
196 "suite",
197 "Declare a test suite targeting a unit (`suite <target> { case … }`).",
198 ),
199 k(
200 "transition",
201 "Declare an agent step invariant over the `old`/`new` state pair (`transition <name>: …`).",
202 ),
203 k(
204 "transparent",
205 "Export a type with its structure visible (`exports transparent { … }`).",
206 ),
207 k("true", "The boolean literal `true`."),
208 k(
209 "type",
210 "Declare a type: alias, record, sum, opaque, or refined.",
211 ),
212 k("uses", "Bring a commons into scope."),
213 k("where", "Attach refinement predicates to a base type."),
214];
215
216pub const CONTEXTUAL_KEYWORDS: &[KeywordInfo] = &[
224 k(
225 "advance",
226 "Move the test harness's clock forward in a `case` (`advance Clock by 15.minutes`).",
227 ),
228 k(
229 "drop",
230 "Inside a `migrate` step, discard a stored field's value on purpose (`drop owner`).",
231 ),
232 k(
233 "key",
234 "The agent's identity field — one per agent; keys the store.",
235 ),
236 k(
237 "migrate",
238 "An agent's migration step: carries a stored record from an older schema version to the current shape (`migrate from 1 (old: CartV1) { … }`).",
239 ),
240 k(
241 "removed",
242 "Declares an agent class gone (`removed agent Old`): its stored data is deleted at the next deploy, after a named confirmation.",
243 ),
244 k(
245 "replaces",
246 "Renames an agent class (`agent New replaces Old`): the old class's stored data and schema history move to the new one.",
247 ),
248 k("store", "A persisted agent-state field."),
249];
250
251pub const RESERVED_CONTEXTUAL: &[&str] =
264 &["asset", "case", "embed", "event", "messages", "on", "suite"];
265
266pub fn is_reserved_contextual(word: &str) -> bool {
272 RESERVED_CONTEXTUAL.contains(&word)
273}
274
275pub const BUILTIN_TYPE_NAMES: &[KeywordInfo] = &[
289 k(
290 "Asset",
291 "A static file declared by `asset name = \"path\"`; its `.url` is the content-hashed URL the platform serves it at.",
292 ),
293 k(
294 "BoundaryRejection",
295 "Why the router refused a request before any handler ran; the parameter of `on rejected`.",
296 ),
297 k(
298 "Connection",
299 "A held WebSocket connection, `Connection[F]`.",
300 ),
301 k(
302 "History",
303 "A generated call-history generator, `History[Agent]` (test-only).",
304 ),
305 k(
306 "HttpResult",
307 "The HTTP handler result type, `HttpResult[T]`.",
308 ),
309 k("List", "The immutable list type, `List[T]`."),
310 k("Map", "The immutable map type, `Map[K, V]`."),
311 k("Query", "The lazy storage-read type, `Query[T]`."),
312 k(
313 "QueueResult",
314 "The queue handler result type (non-generic).",
315 ),
316 k(
317 "RequestHeaders",
318 "An HTTP handler's read-only view of the request's headers (`headers: RequestHeaders`).",
319 ),
320 k("Stream", "The value-over-time primitive, `Stream[T]`."),
321 k(
322 "TokenClaims",
323 "An `Oidc` actor's verified token claims, read through its binder (`who.claims`).",
324 ),
325];
326
327pub fn is_builtin_type_name(name: &str) -> bool {
330 BUILTIN_TYPE_NAMES.iter().any(|b| b.word == name)
331}
332
333const fn k(word: &'static str, meaning: &'static str) -> KeywordInfo {
334 KeywordInfo { word, meaning }
335}
336
337pub fn render_markdown() -> String {
341 let mut out = String::new();
342 out.push_str("# Keywords\n\n");
343 out.push_str(
344 "<!-- GENERATED FILE — do not edit by hand.\n \
345 Source: bynk-syntax/src/keywords.rs (`render_markdown`).\n \
346 Regenerate with: BYNK_BLESS=1 cargo test -p bynkc --test it keywords_reference:: -->\n\n",
347 );
348 out.push_str(
349 "Bynk reserves names in three tiers. The first two are lexer keywords; the \
350 third are compiler-known type names. Only the **hard keywords** can never \
351 be used as an identifier.\n\n",
352 );
353
354 let hard: Vec<&KeywordInfo> = KEYWORDS
356 .iter()
357 .filter(|k| !RESERVED_CONTEXTUAL.contains(&k.word))
358 .collect();
359 out.push_str("## Hard keywords\n\n");
360 out.push_str(&format!(
361 "Reserved everywhere — these **{}** words can never be used as an \
362 identifier.\n\n",
363 hard.len()
364 ));
365 out.push_str("| Keyword | Meaning |\n|---|---|\n");
366 for info in hard {
367 out.push_str(&format!("| `{}` | {} |\n", info.word, info.meaning));
368 }
369
370 let contextual: Vec<&KeywordInfo> = RESERVED_CONTEXTUAL
373 .iter()
374 .filter_map(|w| KEYWORDS.iter().find(|k| &k.word == w))
375 .collect();
376 out.push_str("\n## Contextual keywords\n\n");
377 out.push_str(
378 "Reserved only in the one position named below; elsewhere (a field, \
379 parameter, or other identifier) they are ordinary names.\n\n",
380 );
381 out.push_str("| Keyword | Meaning |\n|---|---|\n");
382 for info in contextual {
383 out.push_str(&format!("| `{}` | {} |\n", info.word, info.meaning));
384 }
385
386 out.push_str("\n## Built-in type names\n\n");
389 out.push_str(
390 "Compiler-known type constructors. They are not lexer keywords — you may \
391 use them as an identifier in value position — but they are reserved in \
392 type position: a `type` declaration may not reuse one of these names \
393 (`bynk.resolve.reserved_builtin_type`).\n\n",
394 );
395 out.push_str("| Name | Meaning |\n|---|---|\n");
396 for info in BUILTIN_TYPE_NAMES {
397 out.push_str(&format!("| `{}` | {} |\n", info.word, info.meaning));
398 }
399 out
400}