ERR/E0.2: raise / try / catch / onfail + precedence + consumer-aware pipe (parser)

Parser-only second step of the error-handling stream. No sema/codegen.

- token: 4 keywords — `raise`, `try`, `catch`, `onfail`.
- ast: RaiseStmt, TryExpr, CatchExpr {operand, binding?, body, is_match_body},
  OnFailStmt {binding?, body}.
- parser:
  - `try` is a unary prefix (binds tighter than `or`; right-recursive so it
    stacks under `xx`/`@`/etc).
  - `or` is already left-associative (precedence-climbing loop) — no change.
  - `catch` is a postfix with four body shapes (no-binding block / block /
    bare-expr / `== { case }` match-body, the latter reusing parseMatchBody
    with the binding as subject).
  - `raise EXPR;` and `onfail [e] { } | onfail EXPR;` statements; `error`
    parses in expression position so `raise error.X` works; raise rejected
    in expression position and inside an onfail body (in_onfail_body flag).
  - consumer-aware `|>`: pipes the LHS into the head call through a
    try/catch/or wrapper, preserving the wrapper.
- print: printExpr + match-arm printing for round-trips (anyerror!void to
  break the printExpr<->printMatchArms inferred-error-set loop).
- sema/lsp: exhaustive switch arms for the 4 nodes + 4 keyword tokens.
- tests: ~22 inline parser tests (precedence, all catch forms, both
  rejections, pipe cases, round-trip prints incl. match-body).

zig build, zig build test (264), and 254/254 examples green.
This commit is contained in:
agra
2026-05-31 17:07:49 +03:00
parent e88ee66953
commit 1b777dd6ab
6 changed files with 579 additions and 23 deletions

View File

@@ -58,6 +58,10 @@ pub const Node = struct {
many_pointer_type_expr: ManyPointerTypeExpr,
optional_type_expr: OptionalTypeExpr,
error_type_expr: ErrorTypeExpr,
raise_stmt: RaiseStmt,
try_expr: TryExpr,
catch_expr: CatchExpr,
onfail_stmt: OnFailStmt,
pack_index_type_expr: PackIndexTypeExpr,
comptime_pack_ref: ComptimePackRef,
force_unwrap: ForceUnwrap,
@@ -407,6 +411,40 @@ pub const DeferStmt = struct {
expr: *Node,
};
// ── Error handling (ERR stream) ──────────────────────────────────────────
/// `raise EXPR;` — terminates control flow like `return`, populating the
/// error channel. `tag` is a tag-typed expression: `error.X` (a field
/// access on the `error` keyword) or a tag-bound variable (`raise e`).
pub const RaiseStmt = struct {
tag: *Node,
};
/// `try X` — a failable attempt. Unary prefix, binds tighter than any
/// binary operator. Sema (E1.4) rejects a non-failable operand.
pub const TryExpr = struct {
operand: *Node,
};
/// `X catch [e] BODY` — inline failure handler (postfix). The binding is a
/// bare name (no parens) and optional. Body is a block, a bare expression,
/// or — when `is_match_body` — a `match_expr` from the `== { case ... }`
/// sugar (whose subject is the binding).
pub const CatchExpr = struct {
operand: *Node,
binding: ?[]const u8 = null,
body: *Node,
is_match_body: bool = false,
};
/// `onfail [e] BODY` — cleanup run on error-exit of the enclosing block.
/// Binding optional (bare name). Body is a block (`onfail [e] { ... }`) or
/// a bare expression (`onfail EXPR;`).
pub const OnFailStmt = struct {
binding: ?[]const u8 = null,
body: *Node,
};
pub const PushStmt = struct {
context_expr: *Node,
body: *Node,