Skip to main content

bynk_syntax/
keywords.rs

1//! Registry of reserved keywords.
2//!
3//! Single source of truth for the keyword list in
4//! `site/src/content/docs/book/reference/keywords.md`, generated by
5//! [`render_markdown`]. The test
6//! `tests/keywords_reference.rs` asserts this table matches exactly the
7//! alphabetic `#[token("…")]` keywords declared in `lexer.rs`, so the two
8//! cannot drift.
9
10/// One reserved keyword and a one-line description of its role.
11pub struct KeywordInfo {
12    pub word: &'static str,
13    pub meaning: &'static str,
14}
15
16/// Every reserved keyword, sorted.
17pub 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("and", "Combine refinement predicates (`where A and B`)."),
59    k("as", "Alias a consumed context (`consumes X as Y`)."),
60    k(
61        "binding",
62        "Name an adapter's TypeScript binding module (`binding \"<module>\"`).",
63    ),
64    k(
65        "by",
66        "Name the actor a handler consumes (`on … by <name>: <Actor>`).",
67    ),
68    k(
69        "capability",
70        "Declare a capability (a dependency interface) in a context.",
71    ),
72    k(
73        "case",
74        "Declare a test case inside a `suite` (`case \"…\" { … }`).",
75    ),
76    k(
77        "commons",
78        "Declare a pure, stateless module of types and functions.",
79    ),
80    k(
81        "consumes",
82        "Declare a dependency on another context's services.",
83    ),
84    k(
85        "context",
86        "Declare a deployable context (services, agents, capabilities).",
87    ),
88    k(
89        "cron",
90        "The cron protocol on a service header (`from cron`).",
91    ),
92    k(
93        "do",
94        "Perform a unit effect as a statement (`do e` — the binder-free `let _ <- e`).",
95    ),
96    k("else", "The alternative branch of an `if` expression."),
97    k(
98        "ensures",
99        "Declare a function postcondition — a pure `Bool` clause over the parameters and `result` (`ensures <name>: <pred>`).",
100    ),
101    k("enum", "Declare a payloadless sum type (`enum { A, B }`)."),
102    k(
103        "expect",
104        "Assert a predicate inside a test case (`expect <bool-predicate>`).",
105    ),
106    k("exports", "Declare which types a context exposes, and how."),
107    k("false", "The boolean literal `false`."),
108    k("fn", "Declare a function."),
109    k(
110        "from",
111        "Name the protocol a service conforms to (`service X from http`).",
112    ),
113    k("given", "Declare the capabilities a handler requires."),
114    k(
115        "http",
116        "The HTTP protocol on a service header (`from http`).",
117    ),
118    k("if", "A conditional expression."),
119    k(
120        "implies",
121        "Logical implication (`P implies Q` ≡ `!P || Q`), used in invariant predicates.",
122    ),
123    k(
124        "invariant",
125        "Declare an agent invariant — a predicate that must hold of every committed state.",
126    ),
127    k(
128        "is",
129        "Test a value against a variant pattern, yielding a `Bool`.",
130    ),
131    k(
132        "let",
133        "Bind a local value (`let x = …`, or `let x <- …` for an effect).",
134    ),
135    k(
136        "match",
137        "Pattern-match over a sum type, `Result`, or `Option`.",
138    ),
139    k(
140        "on",
141        "Begin a handler declaration (`on call`, `on GET(…)`, `on message`, `on open`/`on close`).",
142    ),
143    k(
144        "opaque",
145        "Declare an opaque type, or export a type opaquely.",
146    ),
147    k(
148        "property",
149        "Declare a generative test inside a `suite` (`property \"…\" { for all … }`).",
150    ),
151    k(
152        "protocol",
153        "Reserved keyword (protocols are a closed, compiler-known set).",
154    ),
155    k("provides", "Provide an implementation of a capability."),
156    k(
157        "queue",
158        "The queue protocol on a service header (`from queue(\"name\")`).",
159    ),
160    k(
161        "record",
162        "Reserved keyword (records are written `type X = { … }`).",
163    ),
164    k(
165        "requires",
166        "Declare a function precondition — a pure `Bool` clause over the parameters (`requires <name>: <pred>`).",
167    ),
168    k("self", "The current agent instance, inside a handler."),
169    k(
170        "service",
171        "Declare a service (a group of handlers) in a context.",
172    ),
173    k(
174        "suite",
175        "Declare a test suite targeting a unit (`suite <target> { case … }`).",
176    ),
177    k(
178        "transition",
179        "Declare an agent step invariant over the `old`/`new` state pair (`transition <name>: …`).",
180    ),
181    k(
182        "transparent",
183        "Export a type with its structure visible (`exports transparent { … }`).",
184    ),
185    k("true", "The boolean literal `true`."),
186    k(
187        "type",
188        "Declare a type: alias, record, sum, opaque, or refined.",
189    ),
190    k("uses", "Bring a commons into scope."),
191    k("where", "Attach refinement predicates to a base type."),
192];
193
194/// Contextual keywords — words that read as keywords in one position but stay
195/// usable as ordinary identifiers elsewhere, so they are lexed as `Ident` and
196/// are deliberately *absent* from [`KEYWORDS`] (which is drift-guarded to equal
197/// the lexer's reserved `#[token]`s). Editor surfaces still owe them a hover and
198/// a doc — the mechanical floor over this table lives in
199/// `bynk-lsp/tests/editor_coverage.rs`, mirroring the reserved-keyword tooth
200/// (ADR 0156 / ADR 0161).
201pub const CONTEXTUAL_KEYWORDS: &[KeywordInfo] = &[
202    k(
203        "key",
204        "The agent's identity field — one per agent; keys the store.",
205    ),
206    k("store", "A persisted agent-state field."),
207];
208
209const fn k(word: &'static str, meaning: &'static str) -> KeywordInfo {
210    KeywordInfo { word, meaning }
211}
212
213/// Render the keyword list as a Markdown reference page.
214pub fn render_markdown() -> String {
215    let mut out = String::new();
216    out.push_str("# Keywords\n\n");
217    out.push_str(
218        "<!-- GENERATED FILE — do not edit by hand.\n     \
219         Source: bynkc/src/keywords.rs (`render_markdown`).\n     \
220         Regenerate with: BYNK_BLESS=1 cargo test -p bynkc --test keywords_reference -->\n\n",
221    );
222    out.push_str(
223        "Every reserved keyword, with a one-line description. Reserved words cannot \
224         be used as identifiers.\n\n",
225    );
226    out.push_str(&format!(
227        "There are **{}** reserved keywords.\n\n",
228        KEYWORDS.len()
229    ));
230    out.push_str("| Keyword | Meaning |\n|---|---|\n");
231    for info in KEYWORDS {
232        out.push_str(&format!("| `{}` | {} |\n", info.word, info.meaning));
233    }
234    out
235}