bynk-syntax 0.208.2

Bynk's syntax foundation: lexer, parser, AST, spans, the CompileError type, and the diagnostic-code registry — the lowest leaf of the compiler crate set.
Documentation
//! Registry of reserved keywords.
//!
//! Single source of truth for the keyword list in
//! `site/src/content/docs/book/reference/keywords.md`, generated by
//! [`render_markdown`]. The test
//! `tests/keywords_reference.rs` asserts this table matches exactly the
//! alphabetic `#[token("…")]` keywords declared in `lexer.rs`, so the two
//! cannot drift.

/// One reserved keyword and a one-line description of its role.
pub struct KeywordInfo {
    pub word: &'static str,
    pub meaning: &'static str,
}

/// Every reserved keyword, sorted.
pub const KEYWORDS: &[KeywordInfo] = &[
    k("Bool", "The boolean base type."),
    k(
        "Bytes",
        "The binary base type — an immutable octet sequence, erased to `Uint8Array` (`Bytes.fromUtf8(s)`).",
    ),
    k(
        "Duration",
        "The time-span base type, in milliseconds (`5.minutes`).",
    ),
    k("Effect", "The effectful-computation type, `Effect[T]`."),
    k("Err", "The error variant of `Result`."),
    k("Float", "The floating-point base type."),
    k(
        "Instant",
        "The absolute-time base type, in epoch milliseconds (`Clock.now()`).",
    ),
    k("Int", "The integer base type."),
    k(
        "JsonError",
        "The JSON-decode error type, `Result[T, JsonError]` from `Json.decode`.",
    ),
    k("None", "The empty variant of `Option`."),
    k("Ok", "The success variant of `Result`."),
    k("Option", "The optional-value type, `Option[T]`."),
    k("Result", "The success-or-error type, `Result[T, E]`."),
    k("Some", "The present variant of `Option`."),
    k("String", "The string base type."),
    k(
        "ValidationError",
        "The error type returned by a refined type's `.of`.",
    ),
    k(
        "actor",
        "Declare an actor — a boundary contract a handler consumes via `by`.",
    ),
    k(
        "adapter",
        "Declare an adapter — the host boundary (capability contract + binding).",
    ),
    k("agent", "Declare a stateful, keyed agent inside a context."),
    k("as", "Alias a consumed context (`consumes X as Y`)."),
    k(
        "binding",
        "Name an adapter's TypeScript binding module (`binding \"<module>\"`).",
    ),
    k(
        "by",
        "Name the actor a handler consumes, after the return type — or a service-level default on the header (`… -> T by <name>: <Actor>`).",
    ),
    k(
        "capability",
        "Declare a capability (a dependency interface) in a context.",
    ),
    k(
        "case",
        "Declare a test case inside a `suite` (`case \"\" { … }`).",
    ),
    k(
        "commons",
        "Declare a pure, stateless module of types and functions.",
    ),
    k(
        "consumes",
        "Declare a dependency on another context's services.",
    ),
    k(
        "context",
        "Declare a deployable context (services, agents, capabilities).",
    ),
    k(
        "cron",
        "The cron protocol on a service header (`from cron`).",
    ),
    k(
        "do",
        "Perform a unit effect as a statement (`do e` — the binder-free `let _ <- e`).",
    ),
    k("else", "The alternative branch of an `if` expression."),
    k(
        "ensures",
        "Declare a function postcondition — a pure `Bool` clause over the parameters and `result` (`ensures <name>: <pred>`).",
    ),
    k("enum", "Declare a payloadless sum type (`enum { A, B }`)."),
    k(
        "expect",
        "Assert a predicate inside a test case (`expect <bool-predicate>`).",
    ),
    k("exports", "Declare which types a context exposes, and how."),
    k("false", "The boolean literal `false`."),
    k("fn", "Declare a function."),
    k(
        "from",
        "Name the protocol a service conforms to (`service X from http`).",
    ),
    k("given", "Declare the capabilities a handler requires."),
    k(
        "http",
        "The HTTP protocol on a service header (`from http`).",
    ),
    k("if", "A conditional expression."),
    k(
        "implies",
        "Logical implication (`P implies Q` ≡ `!P || Q`), used in invariant predicates.",
    ),
    k(
        "invariant",
        "Declare an agent invariant — a predicate that must hold of every committed state.",
    ),
    k(
        "is",
        "Test a value against a variant pattern, yielding a `Bool`.",
    ),
    k(
        "let",
        "Bind a local value (`let x = …`, or `let x <- …` for an effect).",
    ),
    k(
        "match",
        "Pattern-match over a sum type, `Result`, or `Option`.",
    ),
    k(
        "on",
        "Begin a handler declaration (`on call`, `on GET(…)`, `on message`, `on open`/`on close`).",
    ),
    k(
        "opaque",
        "Declare an opaque type, or export a type opaquely.",
    ),
    k(
        "property",
        "Declare a generative test inside a `suite` (`property \"\" { for all … }`).",
    ),
    k(
        "protocol",
        "Reserved keyword (protocols are a closed, compiler-known set).",
    ),
    k("provides", "Provide an implementation of a capability."),
    k(
        "queue",
        "The queue protocol on a service header (`from queue(\"name\")`).",
    ),
    k(
        "record",
        "Reserved keyword (records are written `type X = { … }`).",
    ),
    k(
        "requires",
        "Declare a function precondition — a pure `Bool` clause over the parameters (`requires <name>: <pred>`).",
    ),
    k("self", "The current agent instance, inside a handler."),
    k(
        "service",
        "Declare a service (a group of handlers) in a context.",
    ),
    k(
        "stub",
        "Stub a consumed capability operation at a test seam (`stub Cap.op(…) returns <v>` / `fails`).",
    ),
    k(
        "suite",
        "Declare a test suite targeting a unit (`suite <target> { case … }`).",
    ),
    k(
        "transition",
        "Declare an agent step invariant over the `old`/`new` state pair (`transition <name>: …`).",
    ),
    k(
        "transparent",
        "Export a type with its structure visible (`exports transparent { … }`).",
    ),
    k("true", "The boolean literal `true`."),
    k(
        "type",
        "Declare a type: alias, record, sum, opaque, or refined.",
    ),
    k("uses", "Bring a commons into scope."),
    k("where", "Attach refinement predicates to a base type."),
];

/// Contextual keywords — words that read as keywords in one position but stay
/// usable as ordinary identifiers elsewhere, so they are lexed as `Ident` and
/// are deliberately *absent* from [`KEYWORDS`] (which is drift-guarded to equal
/// the lexer's reserved `#[token]`s). Editor surfaces still owe them a hover and
/// a doc — the mechanical floor over this table lives in
/// `bynk-lsp/tests/editor_coverage.rs`, mirroring the reserved-keyword tooth
/// (ADR 0156 / ADR 0161).
pub const CONTEXTUAL_KEYWORDS: &[KeywordInfo] = &[
    k(
        "key",
        "The agent's identity field — one per agent; keys the store.",
    ),
    k("store", "A persisted agent-state field."),
];

/// The reserved lexer tokens the parser deliberately re-admits as identifiers
/// outside their one keyword position (`expect_ident`, ADR-tracked at
/// `parser.rs`). Unlike [`CONTEXTUAL_KEYWORDS`] these words *are* real
/// `#[token]`s and *are* members of [`KEYWORDS`] (so the lexer↔registry drift
/// guard sees them) — they simply are not rejected in identifier position. The
/// keyword reference renders them as a distinct tier so the page no longer
/// claims, falsely, that every listed word is unusable as an identifier.
///
/// Single source of truth: the `expect_ident` exemption arm (via
/// [`is_reserved_contextual`]) and the
/// `is_reserved_keyword_covers_every_lexer_keyword` drift guard both defer to
/// this list, so adding a word here is enough to make the parser admit it.
pub const RESERVED_CONTEXTUAL: &[&str] = &["case", "on", "suite"];

/// True when `word` is a [reserved contextual keyword](RESERVED_CONTEXTUAL) —
/// a reserved token `expect_ident` re-admits as an identifier. Because each of
/// these words lexes only to its own dedicated token, matching on the source
/// text is equivalent to matching the token kind, but keeps the exemption
/// single-sourced against [`RESERVED_CONTEXTUAL`].
pub fn is_reserved_contextual(word: &str) -> bool {
    RESERVED_CONTEXTUAL.contains(&word)
}

/// Built-in type names — compiler-known type constructors the parser dispatches
/// on by identifier text in `parser/types.rs`, *outside* the keyword/token
/// system entirely (they are lexed as ordinary `Ident`s, so they are absent
/// from both the lexer's `#[token]`s and [`KEYWORDS`]). They are nonetheless
/// reserved in type position: a `type` declaration may not reuse one of these
/// names (`bynk.resolve.reserved_builtin_type`), because the parser would
/// otherwise intercept every later reference and the user's alias would be
/// silently shadowed or fail incoherently.
///
/// Single source of truth: [`is_builtin_type_name`] gates the resolver's
/// redeclaration diagnostic, and a drift guard
/// (`bynkc/tests/keywords_reference.rs`) asserts this list equals the set of
/// names the type parser dispatches on. Keep it sorted.
pub const BUILTIN_TYPE_NAMES: &[KeywordInfo] = &[
    k(
        "Connection",
        "A held WebSocket connection, `Connection[F]`.",
    ),
    k(
        "History",
        "A generated call-history generator, `History[Agent]` (test-only).",
    ),
    k(
        "HttpResult",
        "The HTTP handler result type, `HttpResult[T]`.",
    ),
    k("List", "The immutable list type, `List[T]`."),
    k("Map", "The immutable map type, `Map[K, V]`."),
    k("Query", "The lazy storage-read type, `Query[T]`."),
    k(
        "QueueResult",
        "The queue handler result type (non-generic).",
    ),
    k("Stream", "The value-over-time primitive, `Stream[T]`."),
];

/// True when `name` is a compiler-known [built-in type name](BUILTIN_TYPE_NAMES).
/// Used by the resolver to reject `type <name> = …` redeclarations.
pub fn is_builtin_type_name(name: &str) -> bool {
    BUILTIN_TYPE_NAMES.iter().any(|b| b.word == name)
}

const fn k(word: &'static str, meaning: &'static str) -> KeywordInfo {
    KeywordInfo { word, meaning }
}

/// Render the keyword reference page — three tiers, each with prose that is
/// true of *that* tier (the page used to make one blanket claim that was false
/// of two of them).
pub fn render_markdown() -> String {
    let mut out = String::new();
    out.push_str("# Keywords\n\n");
    out.push_str(
        "<!-- GENERATED FILE — do not edit by hand.\n     \
         Source: bynk-syntax/src/keywords.rs (`render_markdown`).\n     \
         Regenerate with: BYNK_BLESS=1 cargo test -p bynkc --test keywords_reference -->\n\n",
    );
    out.push_str(
        "Bynk reserves names in three tiers. The first two are lexer keywords; the \
         third are compiler-known type names. Only the **hard keywords** can never \
         be used as an identifier.\n\n",
    );

    // Tier 1 — hard keywords: every reserved token except the contextual ones.
    let hard: Vec<&KeywordInfo> = KEYWORDS
        .iter()
        .filter(|k| !RESERVED_CONTEXTUAL.contains(&k.word))
        .collect();
    out.push_str("## Hard keywords\n\n");
    out.push_str(&format!(
        "Reserved everywhere — these **{}** words can never be used as an \
         identifier.\n\n",
        hard.len()
    ));
    out.push_str("| Keyword | Meaning |\n|---|---|\n");
    for info in hard {
        out.push_str(&format!("| `{}` | {} |\n", info.word, info.meaning));
    }

    // Tier 2 — contextual keywords: reserved tokens the parser re-admits as
    // identifiers outside their one keyword position.
    let contextual: Vec<&KeywordInfo> = RESERVED_CONTEXTUAL
        .iter()
        .filter_map(|w| KEYWORDS.iter().find(|k| &k.word == w))
        .collect();
    out.push_str("\n## Contextual keywords\n\n");
    out.push_str(
        "Reserved only in the one position named below; elsewhere (a field, \
         parameter, or other identifier) they are ordinary names.\n\n",
    );
    out.push_str("| Keyword | Meaning |\n|---|---|\n");
    for info in contextual {
        out.push_str(&format!("| `{}` | {} |\n", info.word, info.meaning));
    }

    // Tier 3 — built-in type names: not lexer keywords at all, but reserved in
    // type position (a `type` declaration may not reuse one).
    out.push_str("\n## Built-in type names\n\n");
    out.push_str(
        "Compiler-known type constructors. They are not lexer keywords — you may \
         use them as an identifier in value position — but they are reserved in \
         type position: a `type` declaration may not reuse one of these names \
         (`bynk.resolve.reserved_builtin_type`).\n\n",
    );
    out.push_str("| Name | Meaning |\n|---|---|\n");
    for info in BUILTIN_TYPE_NAMES {
        out.push_str(&format!("| `{}` | {} |\n", info.word, info.meaning));
    }
    out
}