2025-09-21 08:53:00 +09:00
|
|
|
|
Exception Handling — Postfix catch / cleanup (Stage‑3)
|
|
|
|
|
|
|
|
|
|
|
|
Summary
|
|
|
|
|
|
- Nyash adopts a flatter, postfix-first exception style:
|
|
|
|
|
|
- try is deprecated. 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 language’s scope unification and keeps blocks shallow and readable.
|
|
|
|
|
|
|
|
|
|
|
|
Status
|
|
|
|
|
|
- Phase 1: normalization sugar(既存)
|
|
|
|
|
|
- `NYASH_CATCH_NEW=1` でコア正規化パスが有効化。
|
|
|
|
|
|
- 後置フォームは内部 `TryCatch` AST に変換され、既存経路で降下。
|
|
|
|
|
|
- 実行時コストはゼロ(意味論不変)。
|
|
|
|
|
|
- Phase 2(実装済み・Stage‑3ゲート)
|
|
|
|
|
|
- パーサが式レベルの後置 `catch/cleanup` を直接受理。
|
|
|
|
|
|
- ゲート: `NYASH_PARSER_STAGE3=1`
|
|
|
|
|
|
- 糖衣正規化はそのまま併存(関数糖衣専用)。キーワード直受理と二重適用はしない設計。
|
|
|
|
|
|
|
|
|
|
|
|
Syntax (postfix)
|
|
|
|
|
|
- Expression-level postfix handlers (Stage‑3):
|
|
|
|
|
|
- `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 (Stage‑3): 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.
|
|
|
|
|
|
|
|
|
|
|
|
Migration notes
|
|
|
|
|
|
- try is deprecated: prefer postfix `catch/cleanup`.
|
|
|
|
|
|
- Member-level handlers (computed/once/birth_once/method) keep allowing postfix `catch/cleanup` (Stage‑3), 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") }
|
|
|
|
|
|
|
|
|
|
|
|
// Stage‑3 parser gate quick smoke (direct acceptance)
|
|
|
|
|
|
// NYASH_PARSER_STAGE3=1 ./target/release/nyash --backend vm \
|
2025-11-06 15:41:52 +09:00
|
|
|
|
// apps/tests/macro/exception/expr_postfix_direct.hako
|
2025-09-21 08:53:00 +09:00
|
|
|
|
```
|