diff --git a/docs/reference/plugin-system/README.md b/docs/reference/plugin-system/README.md index b6a364de..bb1454c9 100644 --- a/docs/reference/plugin-system/README.md +++ b/docs/reference/plugin-system/README.md @@ -28,6 +28,9 @@ - **[net-plugin.md](./net-plugin.md)** - Netプラグイン(HTTP/TCP PoC) - GET/POST、ヘッダ、Content-Length、環境変数によるログ +- **[returns-result.md](./returns-result.md)** - 可選のResultBox正規化 + - `returns_result = true` で成功/失敗を `Ok/Err` に統一(段階導入推奨) + ### ⚙️ 戻り値のResult化(B案サポート) - `nyash.toml` のメソッド定義に `returns_result = true` を付けると、 - 成功: `Ok(value)` の `ResultBox` に包んで返す diff --git a/docs/reference/plugin-system/returns-result.md b/docs/reference/plugin-system/returns-result.md new file mode 100644 index 00000000..67f0f137 --- /dev/null +++ b/docs/reference/plugin-system/returns-result.md @@ -0,0 +1,47 @@ +# 可選: ResultBox 正規化(returns_result) + +最終更新: 2025-08-22 + +## 概要 +`nyash.toml` のメソッド定義に `returns_result = true` を付けると、そのメソッドの戻りが `ResultBox` で正規化されます。 +- 成功: `Ok(value)`(voidは `Ok(void)`) +- 失敗: `Err(ErrorBox("... (code: N)"))`(BID負エラーコードをErr化) + +これは「おすすめルール」で、強制ではありません。段階的に、必要なメソッドから選んで導入できます。 + +## 使い方 +`nyash.toml` を編集し、対象メソッドに `returns_result = true` を付けます。 + +```toml +[libraries."libnyash_net_plugin.so".HttpClientBox.methods] +# birth = { method_id = 0 } +# デフォルト(従来通り) +get = { method_id = 1 } +post = { method_id = 2 } +# 推奨: Result正規化(有効化する場合) +# get = { method_id = 1, returns_result = true } +# post = { method_id = 2, returns_result = true } +``` + +## 呼び出し側パターン +```nyash +res = http.get(url) +if res.is_ok() { + resp = res.get_value() + print(resp.readBody()) +} else { + err = res.get_error() + print("HTTP error: " + err.toString()) +} +``` + +## 推奨の導入順序 +- Stage 1: `HttpClientBox.get/post`, `SocketClientBox.connect` +- Stage 2: `HttpServerBox.start/stop/accept`(起動・待受系) +- Stage 3: 失敗が起きうる便宜メソッド(必要なところだけ) + +## 注意 +- `returns_result` を付けたメソッドのみ Result化されます。未指定メソッドは従来動作(生値/void/例外)。 +- タイムアウトをErrにしたい場合は、プラグイン側がBID負エラーを返すよう拡張してください(現状は空bytes/void)。 +- 段階導入により、既存コードを壊さずに移行できます。 + diff --git a/docs/予定/when-pattern-matching.md b/docs/予定/when-pattern-matching.md new file mode 100644 index 00000000..b3bca7df --- /dev/null +++ b/docs/予定/when-pattern-matching.md @@ -0,0 +1,80 @@ +# when構文 - パターンマッチング(将来実装予定) + +## 概要 +Nyashに「when構文」を導入し、より直感的で安全なエラー処理とパターンマッチングを実現する。 + +## 背景 +- ChatGPT5提案の`returns_result = true`による段階的Result正規化 +- 現在の`if res.is_ok()`パターンは冗長 +- Nyashの「Everything is Box」哲学に合致した統一的な構文が必要 + +## 提案構文 + +### 基本形 - ResultBoxのパターンマッチング +```nyash +when res { + ok(resp) -> { + body = resp.readBody() + print(body) + } + error(err) -> { + print("Error: " + err.message()) + } +} +``` + +### 汎用形 - あらゆるBoxでのパターンマッチング +```nyash +when value { + StringBox(s) -> print("文字列: " + s) + IntegerBox(n) -> print("数値: " + n.toString()) + ArrayBox(arr) -> print("配列の長さ: " + arr.length()) + NullBox -> print("nullです") + _ -> print("その他のBox") // デフォルトケース +} +``` + +### ネスト可能 +```nyash +when httpResult { + ok(response) -> { + when response.getStatus() { + 200 -> print("成功!") + 404 -> print("見つからない") + _ -> print("その他のステータス") + } + } + error(e) -> print("エラー: " + e) +} +``` + +## 実装前提条件 +1. **MIRダイエット完了**(現在33個→目標20-26個) + - 新しいパターンマッチング命令の追加余地が必要 +2. **VM最適化完了**(Phase 8.6) + - 効率的なジャンプテーブル実装が必要 +3. **`returns_result = true`の段階導入** + - Net系Boxから開始 + +## 実装計画 +1. Phase 9後半: MIR命令追加(Match, MatchBranch等) +2. Phase 10: VM/JITでの最適化実装 +3. Phase 11: 言語仕様への正式組み込み + +## 利点 +- **直感的**: switch-case的な馴染みやすい構文 +- **型安全**: 各パターンで正しい型のメソッドが呼べる +- **網羅的**: すべてのケースをカバー可能 +- **拡張性**: 将来の新Box型にも対応可能 +- **Nyash哲学**: Everything is Boxに完全に合致 + +## 他の検討案 +1. **chain構文**: `res.onSuccess({}).onError({})` +2. **try-else構文**: `try resp = res {} else err {}` +3. **?演算子**: `resp = res.unwrap?()` + +これらも将来的に検討可能だが、when構文が最もNyashらしい。 + +--- +提案日: 2025-08-20 +提案者: Claude & ChatGPT5 協調開発チーム \ No newline at end of file diff --git a/nyash.toml b/nyash.toml index bf7c8a6a..f3bff312 100644 --- a/nyash.toml +++ b/nyash.toml @@ -98,10 +98,12 @@ type_id = 23 [libraries."libnyash_net_plugin.so".HttpClientBox.methods] birth = { method_id = 0 } -get = { method_id = 1 } -post = { method_id = 2 } +get = { method_id = 1, returns_result = true } +post = { method_id = 2, returns_result = true } fini = { method_id = 4294967295 } +## ResultBox normalization enabled above for get/post + [libraries."libnyash_net_plugin.so".SocketServerBox] type_id = 30 @@ -113,6 +115,11 @@ accept = { method_id = 3 } acceptTimeout = { method_id = 4, args = ["timeout_ms"] } fini = { method_id = 4294967295 } +# Optional: ResultBox normalization (recommendation) +# start = { method_id = 1, args = ["port"], returns_result = true } +# stop = { method_id = 2, returns_result = true } +# accept = { method_id = 3, returns_result = true } + [libraries."libnyash_net_plugin.so".SocketClientBox] type_id = 32 @@ -121,6 +128,9 @@ birth = { method_id = 0 } connect = { method_id = 1, args = ["host", "port"] } fini = { method_id = 4294967295 } +# Optional: ResultBox normalization (recommendation) +# connect = { method_id = 1, args = ["host", "port"], returns_result = true } + [libraries."libnyash_net_plugin.so".SocketConnBox] type_id = 31 diff --git a/tests/e2e_plugin_net.rs b/tests/e2e_plugin_net.rs index bc2986a8..8435d1b3 100644 --- a/tests/e2e_plugin_net.rs +++ b/tests/e2e_plugin_net.rs @@ -25,7 +25,7 @@ fn e2e_http_stub_end_to_end() { if !try_init_plugins() { return; } let code = r#" -local srv, cli, r, req, resp, body +local srv, cli, r, resp, req, body srv = new HttpServerBox() srv.start(8080) @@ -38,7 +38,8 @@ resp.setStatus(200) resp.write("OK") req.respond(resp) -body = r.readBody() +resp = r.get_value() +body = resp.readBody() body "#; @@ -55,7 +56,7 @@ fn e2e_http_server_restart() { if !try_init_plugins() { return; } let code = r#" -local srv, cli, r, req, resp, body +local srv, cli, r, resp, req, body srv = new HttpServerBox() srv.start(8081) @@ -69,13 +70,17 @@ req.respond(resp) srv.stop() srv.start(8081) +resp = r.get_value() +_ = resp.readBody() # consume first response (optional) + r = cli.get("http://localhost:8081/test2") req = srv.accept() resp = new HttpResponseBox() resp.write("B") req.respond(resp) -body = r.readBody() +resp = r.get_value() +body = resp.readBody() body "#; @@ -93,7 +98,7 @@ fn e2e_http_server_shutdown_and_restart() { // First run: start and respond let code1 = r#" -local srv, cli, r, req, resp +local srv, cli, r, resp, req srv = new HttpServerBox() srv.start(8082) cli = new HttpClientBox() @@ -122,7 +127,8 @@ req = srv.accept() resp = new HttpResponseBox() resp.write("Y") req.respond(resp) -body = r.readBody() +resp = r.get_value() +body = resp.readBody() body "#; let ast2 = NyashParser::parse_from_string(code2).expect("parse2"); @@ -138,7 +144,7 @@ fn e2e_http_post_and_headers() { if !try_init_plugins() { return; } let code = r#" -local srv, cli, r, req, resp, body, st, hv +local srv, cli, r, resp, req, body, st, hv srv = new HttpServerBox() srv.start(8090) @@ -156,9 +162,10 @@ resp.write("R") req.respond(resp) // client reads status, header, body -st = r.getStatus() -hv = r.getHeader("X-Test") -body = r.readBody() +resp = r.get_value() +st = resp.getStatus() +hv = resp.getHeader("X-Test") +body = resp.readBody() st.toString() + ":" + hv + ":" + body "#; diff --git a/tests/e2e_plugin_net_additional.rs b/tests/e2e_plugin_net_additional.rs index 4642f1c0..af0d54df 100644 --- a/tests/e2e_plugin_net_additional.rs +++ b/tests/e2e_plugin_net_additional.rs @@ -25,7 +25,7 @@ fn e2e_http_two_servers_parallel() { if !try_init_plugins() { return; } let code = r#" -local s1, s2, c, r1, r2, req1, req2, p1, p2, x, y +local s1, s2, c, r1, r2, resp1, resp2, req1, req2, p1, p2, x, y s1 = new HttpServerBox() s2 = new HttpServerBox() s1.start(8101) @@ -51,8 +51,10 @@ req1.respond(x) req2.respond(y) // read results -x = r1.readBody() -y = r2.readBody() +resp1 = r1.get_value() +resp2 = r2.get_value() +x = resp1.readBody() +y = resp2.readBody() x + ":" + y "#; @@ -69,7 +71,7 @@ fn e2e_http_long_body_and_headers() { if !try_init_plugins() { return; } let code = r#" -local s, c, r, q, resp, body, hv +local s, c, r, resp, q, body, hv s = new HttpServerBox() s.start(8103) @@ -85,8 +87,9 @@ resp.setHeader("X-Beta", "B") resp.write("OK-LONG") q.respond(resp) -body = r.readBody() -hv = r.getHeader("X-Alpha") +resp = r.get_value() +body = resp.readBody() +hv = resp.getHeader("X-Alpha") hv + ":" + body "#;