1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//! The `as` binding (B1b, issue #1475) — one construct, both condition
//! positions.
//!
//! Ruled `docs/decision-log.md` 2026-07-26 ("The `as` binding: one
//! construct, both condition positions, `{if}` spelling"): the statement
//! form (`if EXPR as NAME { … }`, `while EXPR as NAME { … }`,
//! `parser/control_flow.rs`) and the template form (`{if EXPR as NAME: …
//! else: …}`, `parser/family.rs`) are the SAME `if` construct in brink's
//! two condition positions, so they share this one grammar rule and one
//! node kind ([`AS_BINDING`]) rather than forking into two binding
//! spellings.
//!
//! The binding is a **suffix of the condition head**, never an expression
//! operator: `expr::expression` never sees `as`, so `KW_AS` cannot be
//! absorbed mid-expression, and the "the binding is the ENTIRE condition"
//! v1 restriction falls out of the grammar shape instead of needing a
//! precedence rule. What the grammar can't refuse on its own is a *trailing*
//! `&&`/`||`/`or` after the binding (`if find(x) as s && s.sharp { … }`) —
//! that's [`reject_composition`]'s clear parse error, per the ruling's
//! "composition with `&&`/`||` is a parse/analysis error with a clear
//! diagnostic". The mirror-image spelling (`if a && find(x) as s { … }`,
//! where the binding lands on a boolean sub-expression) is not a parse
//! failure at all — it parses as a binding over `a && find(x)` — and is
//! rejected downstream by `brink-ir`'s `E145`, which can see the bound
//! expression's shape.
use crate;
use Parser;
/// Whether an `as` binding follows the just-parsed condition head.
pub
/// `as NAME` — parsed as a trailing sibling of the condition head, inside
/// the construct that binds it (`IF_STMT`/`WHILE_STMT`/
/// `CONDITIONAL_BLOCK`/`CHOICE_GUARD`).
///
/// Call sites must check [`at_as_binding`] first; this unconditionally
/// consumes the `as` keyword.
pub
/// The v1 whole-condition restriction: an operator directly after the
/// binding means the author tried to compose `as` with `&&`/`||`, which
/// let-chains would admit later but v1 does not. Diagnose it by name —
/// the fallback would be `expected L_BRACE, found AMP_AMP`, which says
/// nothing about the actual rule.
///
/// `||` is two adjacent `PIPE` tokens (the lexer has no compound token for
/// it — `expr::infix_expression`'s own `is_double_pipe` check), and a
/// single `PIPE` here can only be a mis-typed one, so both are caught.