Files
hakorune/docs/guides/exception-handling.md
nyash-codex d7805e5974 feat(joinir): Phase 213-2 Step 2-2 & 2-3 Data structure extensions
Extended PatternPipelineContext and CarrierUpdateInfo for Pattern 3 AST-based generalization.

Changes:
1. PatternPipelineContext:
   - Added loop_condition: Option<ASTNode>
   - Added loop_body: Option<Vec<ASTNode>>
   - Added loop_update_summary: Option<LoopUpdateSummary>
   - Updated build_pattern_context() for Pattern 3

2. CarrierUpdateInfo:
   - Added then_expr: Option<ASTNode>
   - Added else_expr: Option<ASTNode>
   - Updated analyze_loop_updates() with None defaults

Status: Phase 213-2 Steps 2-2 & 2-3 complete
Next: Create Pattern3IfAnalyzer to extract if statement and populate update summary
2025-12-10 00:01:53 +09:00

121 lines
6.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Exception Handling — Postfix catch / cleanup (Stage3)
Summary
- Nyash adopts a flatter, postfix-first exception style:
- There is no `try` statement in the language spec. Use postfix `catch` and `cleanup` instead.
- `catch` = handle exceptions from the immediately preceding expression/call.
- `cleanup` = always-run finalization (formerly finally), regardless of success or failure.
- This matches the languages scope unification and keeps blocks shallow and readable.
Spec Clarifications (Stage3)
- Acceptance gates and profiles
- Expressionpostfix: `NYASH_PARSER_STAGE3=1` enables `expr catch(...) {..} cleanup {..}` on calls/chains.
- Blockpostfix: `NYASH_BLOCK_CATCH=1` or Stage3 enables `{ ... } catch(...) {..} cleanup {..}` (standalone block statement)。
- Methodpostfix: `NYASH_METHOD_CATCH=1` or Stage3 enables method body postfix on the most recent method.
- Cardinality and order
- Postfix (expr/block/method): at most one `catch` and at most one `cleanup` — in this order. A second `catch` after postfix is a parse error. Multiple `cleanup` are not allowed.
- Legacy compatibility: some builds may still accept the historical `try { ... } catch ... cleanup ...` form, but it is not part of the language spec and will be disabled by default. Prefer postfix forms.
- Binding and chaining
- Postfix binds to the immediately preceding expression (the last call in a chain) or to the justparsed block/method body. It does not extend to the entire statement unless parentheses are used.
- After constructing the postfix `TryCatch`, further method chaining on that expression is not accepted.
- Semantics and controlflow
- `cleanup` (finally) always runs, regardless of success/failure of the try part.
- `return` inside the try part is deferred until after `cleanup` executes. This is implemented by the MIR builder as a deferred return slot/jump to the `cleanup`/exit block.
- `return` inside `cleanup` is disallowed by default; enable with `NYASH_CLEANUP_ALLOW_RETURN=1`.
- `throw` inside `cleanup` is disallowed by default; enable with `NYASH_CLEANUP_ALLOW_THROW=1`.
- `break/continue` inside `cleanup` are allowed (no special guard); use with care. Cleanup executes before the loop transfer takes effect.
- Nested cleanup follows lexical unwinding order (inner cleanup runs before outer cleanup).
- If no `catch` is present, thrown exceptions still trigger `cleanup`, then propagate outward.
- Diagnostics
- Methodpostfix: duplicate postfix after a method body is a parse error: "duplicate postfix catch/cleanup after method".
- Blockpostfix: a standalone postfix without a preceding block is a parse error: "catch/cleanup must follow a try block or standalone block".
- Expressionpostfix: only one `catch` is accepted at expression level; a second `catch` triggers a parse error.
Status
- Phase 1: normalization sugar既存
- `NYASH_CATCH_NEW=1` でコア正規化パスが有効化。
- 後置フォームは内部 `TryCatch` AST に変換され、既存経路で降下。
- 実行時コストはゼロ(意味論不変)。
- Phase 2実装済み・Stage3ゲート
- パーサが式レベルの後置 `catch/cleanup` を直接受理。
- ゲート: `NYASH_PARSER_STAGE3=1`
- 糖衣正規化はそのまま併存(関数糖衣専用)。キーワード直受理と二重適用はしない設計。
Syntax (postfix)
- Expression-level postfix handlers (Stage3):
- `expr catch(Type e) { /* handle */ }`
- `expr catch { /* handle (no variable) */ }`
- `expr cleanup { /* always-run */ }`
- Combine: `expr catch(Type e){...} cleanup{...}`
- Method/function calls are just expressions, so postfix applies:
- `call(arg1, arg2) catch(Error e) { log(e) }`
- `obj.method(x) cleanup { obj.release() }`
Precedence and chaining
- Postfix `catch`/`cleanup` binds to the immediately preceding expression (call/chain result), not to the whole statement.
- For long chains, we recommend parentheses to make intent explicit:
- `(obj.m1().m2()) catch { ... }`
- `f(a, b) catch { ... } cleanup { ... }`
- Parser rule (Stage3): postfix attaches once at the end of a call/chain and stops further chaining on that expression.
Diagram (conceptual)
```
// before (parse)
obj.m1().m2() catch { H } cleanup { C }
// precedence (binding)
obj.m1().[ m2() ↖ binds to this call ] catch { H } cleanup { C }
// normalization (conceptual AST)
TryCatch {
try: [ obj.m1().m2() ],
catch: [ (type:Any, var:None) -> H ],
finally: [ C ]
}
```
Normalization (Phase 1)
- With `NYASH_CATCH_NEW=1`, postfix sugar is transformed into legacy `TryCatch` AST:
- `EXPR catch(T e){B}``TryCatch { try_body:[EXPR], catch:[(T,e,B)], finally:None }`
- `EXPR cleanup {B}``TryCatch { try_body:[EXPR], catch:[], finally:Some(B) }`
- Multiple `catch` are ordered top-to-bottom; first matching type handles the error.
- Combined `catch ... cleanup ...` expands to a single `TryCatch` with both blocks.
- Lowering uses the existing builder (`cf_try_catch`) which already supports cleanup semantics.
Semantics
- catch handles exceptions from the immediately preceding expression only.
- cleanup is always executed regardless of success/failure (formerly finally).
- Multiple catch blocks match by type in order; the first match is taken.
- In loops, `break/continue` cooperate with cleanup: cleanup is run before leaving the scope.
- Return deferral: A `return` in the try section defers until after cleanup. `return`/`throw` inside cleanup are disabled by default; see env toggles below.
Environment toggles
- `NYASH_PARSER_STAGE3=1`: Enable Stage3 syntax (postfix catch/cleanup for expressions; also gates others by default)
- `NYASH_BLOCK_CATCH=1`: Allow blockpostfix (independent of Stage3 if needed)
- `NYASH_METHOD_CATCH=1`: Allow methodpostfix (independent of Stage3 if needed)
- `NYASH_CLEANUP_ALLOW_RETURN=1`: Permit `return` inside cleanup (default: off)
- `NYASH_CLEANUP_ALLOW_THROW=1`: Permit `throw` inside cleanup (default: off)
Migration notes
- try is deprecated: prefer postfix `catch/cleanup`.
- Member-level handlers (computed/once/birth_once/method) keep allowing postfix `catch/cleanup` (Stage3), unchanged.
- Parser acceptance of postfix at expression level will land in Phase 2; until then use the gate for normalization.
Examples
```
// Postfix catch on a call
do_work() catch(Error e) { env.console.log("error: " + e) }
// Always-run cleanup
open_file(path) cleanup { env.console.log("closed") }
// Combined
connect(url)
catch(NetworkError e) { env.console.warn(e) }
cleanup { env.console.log("done") }
// Stage3 parser gate quick smoke (direct acceptance)
// NYASH_PARSER_STAGE3=1 ./target/release/hakorune --backend vm \
// apps/tests/macro/exception/expr_postfix_direct.hako
```