pyvm: split op handlers into ops_core/ops_box/ops_ctrl; add ops_flow + intrinsic; delegate vm.py without behavior change

net-plugin: modularize constants (consts.rs) and sockets (sockets.rs); remove legacy commented socket code; fix unused imports
mir: move instruction unit tests to tests/mir_instruction_unit.rs (file lean-up); no semantic changes
runner/pyvm: ensure using pre-strip; misc docs updates

Build: cargo build ok; legacy cfg warnings remain as before
This commit is contained in:
Selfhosting Dev
2025-09-21 08:53:00 +09:00
parent ee17cfd979
commit c8063c9e41
247 changed files with 10187 additions and 23124 deletions

View File

@ -0,0 +1,49 @@
# Nyash Core Principles — Minimal Syntax, Zero Runtime, Visual Flow
Status: design-only during freeze (no implementation changes)
Core (one page summary)
- Minimal syntax: `{ … }` + `->` + `|args|` or `_` for flow; guard chains as the canonical first-match form. No new `match` construct; normalize instead.
- Zero-runtime lowering: always desugar to `let/if/call/ret/phi`. No new opcodes, no implicit closures.
- Capture policy: capture by reference by default; mutation requires `mut` on the captured binding. Captures remain within the defining scope.
- ASI/format: treat `->` as a low-precedence line-continue. Formatter aligns arrows vertically.
Before/After (normalize view)
- Each example documents Ny → normalized Ny → MIR intent (design-only):
1) Flow serial: `{read} -> { |x| validate(x) } -> { |x| save(x) }`
2) Guard chain: `guard c1 -> {A}; guard c2 -> {B}; else -> {C}`
3) If horizontal: `if cond -> {A} else -> {B}`
4) Range pattern: `guard ch in '0'..'9' -> { ... }`
5) Digit helper: `acc = acc*10 + ch.toDigitOrNull()` (null-guarded)
nyfmt alignment (visual flow)
```
{ fetch() }
-> { validate(_) }
-> { save(_) }
```
Domain demos (≤20 lines each)
- ETL pipeline: `read -> validate -> normalize -> save`
- Text/number parse: `guard ch in '0'..'9' -> { acc = acc*10 + ch.toDigitOrNull() }`
- Mini state machine: guard-first horizontal description with `else` fallback
Observability (spec hooks; design-only)
- `NYASH_SCOPE_TRACE=1|json`: enter/exit + captures (JSONL: `sid`, `caps`, `ret`).
- `NYASH_FLOW_TRACE=1`: desugared steps like `t0=B0(); t1=B1(t0);`.
Runtime/API additions (docs-only at freeze)
- `StringBox/Utf8Cursor`: `toDigitOrNull(base=10)`, `toIntOrNull()` — compile to simple comparisons/arithmetic.
- Guard sugar: Range (`'0'..'9'`) and CharClass (`Digit`, `AZ`, `az`, `Alnum`, `Space`) — compile to bound checks.
Acceptance & guardrails (freeze)
- “No new grammar beyond sugar” and “no new VM opcodes” as hard rules during freeze.
- Golden texts (Ny → MIR fragments) to lock compatibility where practical.
- Lint proposals are documentation-only: single-use scope, long `->` chains, duplicated side effects.
Related docs
- proposals/scope-reuse.md — local scope reuse blocks (MVP)
- design/flow-blocks.md — arrow flow + anonymous blocks
- reference/language/match-guards.md — guard chains + range/charclass sugar
- reference/language/strings.md — UTF8 first; proposed digit helpers

View File

@ -0,0 +1,88 @@
Exception Handling — Postfix catch / cleanup (Stage3)
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 languages scope unification and keeps blocks shallow and readable.
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.
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/nyash --backend vm \
// apps/tests/macro/exception/expr_postfix_direct.nyash
```

View File

@ -0,0 +1,46 @@
# Nyash: Core Minimal + Strong Sugar
> 最小のコア言語に、強力な糖衣構文を重ねて「書きやすさ」と「0コスト正規化」を両立する方針です。
## Core最小
- 制御: `if`, `loop(condition) { … }`, `break`, `continue`(単一入口・先頭条件)
- 式: `const/binop/compare/branch/jump/ret/phi``call/boxcall`
- 単項: `-x`, `!x` / `not x`(真偽は i64 0/1 へ正規化)
- 例外: `try/catch/cleanup`postfix 版は正規化で TryCatch に降下)
設計上の非採用
- dowhile: 不採用(先頭条件原則)。代替は糖衣で表現→先頭条件へ正規化。
### 演算子とループの方針(要約)
- 単項 not`!`)は採用(既存の `not` と同義)。
- dowhile は非採用(明確性と正規化単純性を優先)。
- ループは LoopForm 正規化に合わせて糖衣→正規形に落とすbreak/continue を含む)。
## Sugar強く・美しく・0コスト
- repeat N { … }
- 正規化: `i=0; while(i<N){ …; i=i+1 }``loop`に降下)
- until cond { … }
- 正規化: `while(!cond){ … }``!` は Compare(Eq,0) へ)
- for i in A..B { … }
- 正規化: 範囲生成なしでカウンタ while へ
- foreach x in arr { … }
- 正規化: `i=0; while(i < arr.size()){ x=arr.get(i); …; i=i+1 }`
- 文字列補間: `"hello ${name}"``"hello " + name`
- 三項: `cond ? a : b``if/else + PHI`
- 論理代入: `a ||= b` / `a &&= b``if(!a) a=b` / `if(a) a=b`
いずれも「意味論を変えずに」`loop/if` へ降下します。MIR/LLVM/Cranelift の下層は常にコア形にのみ対応すればよく、認知負荷を小さく保てます。
## 実装ガイドRust/PyVM共通
- Parser は糖衣の表層を受理し、Normalize前段で 0コストに正規化→ MIR 降下。
- PyVM は正規化後の MIR を実行P0 機能のみ実装)。
- LLVM は PHI/SSA 衛生を守る。空PHIは不可、PHIはブロック先頭。
## Profiles実行プロファイル・方針
- dev: 糖衣ON/デバッグON作業向け
- lite: 糖衣OFF/静音(軽量)
- ci: 糖衣ON/strict/静音(最小スモークのみ)
将来の拡張
- 文字列補間/複数行/安全アクセスなどの糖衣は、常に正規化→コア形if/loopへ降下していきます。
- 例外の後処理は `cleanup` に統一し、`defer` 的表現は糖衣→`cleanup` へ。

View File

@ -1,39 +1,31 @@
# ScopeBox(コンパイル時メタ)設計ガイド
ScopeBox and MIR Scope Hints (Dev/CI option)
目的
- スコープ境界・defer・capabilities を“箱Box”のメタとして表現しつつ、最終的な実行物ではゼロコストにする。
- LoopFormループのみキャリア整形と責務分離し、If/Match は合流点正規化join変数単一PHI群に限定する。
Overview
- ScopeBox is an optional, compile-time-only wrapper that makes lexical scopes explicit in the AST for diagnostics and macro visibility. It is a no-op for execution: MIR lowering treats ScopeBox like a normal block and semantics are unchanged.
基本方針
- ScopeBox は“消える箱”。AST/マクロ段階で Block に属性を付与し、MIR ではヒントとして解釈、IR では完全に剥がす。
- ループを伴わないスコープを LoopForm に持ち込まない0回ループ化はしない
How to enable
- Inject ScopeBox wrappers during core normalization by setting:
- `NYASH_SCOPEBOX_ENABLE=1`
- Injection points:
- If.then / If.else bodies
- Loop.body
- Bare blocks are represented by `Program { statements }` and already get ScopeEnter/ScopeLeave hints.
属性とヒントMVP
- ASTマクロ内部表現
- Block.attrs.scope: { id, name, caps?: {io,net,env}, defer?: [Call…], diag?: {...} }
- 備考: 現段階では属性はマクロ内で保持するだけ。MIR 降下時にヒントへ写すのが目標。
- MIRヒントのみ構造不変
- hint.scope_enter(id) / hint.scope_leave(id)
- hint.defer(list) ・・・ 静的展開cleanup合流に用いる
- hint.join_result(var) ・・・ If/Match の合流結果を明示
- hint.loop_carrier(vars...) ・・・ ループヘッダで同一PHI群に導く
- hint.no_empty_phi検証
- IRRelease
- すべての hint は生成前に剥離。追加命令は一切残らない。
MIR Scope Hints (unified env)
- Configure hint output with a single env using a pipe-style syntax:
- `NYASH_MIR_HINTS="<target>|<filters>..."`
- Targets:
- `trace` or `stderr`: print human-friendly hints to stderr
- `jsonl=<path>` or a file path: append one JSON object per line
- Filters:
- `all` (default), `scope`, `join`, `loop`, `phi`
- Examples:
- `NYASH_MIR_HINTS="trace|all"`
- `NYASH_MIR_HINTS="jsonl=tmp/hints.jsonl|scope|join"`
- `NYASH_MIR_HINTS="tmp/hints.jsonl|loop"`
- Back-compat:
- `NYASH_MIR_TRACE_HINTS=1` is still accepted (equivalent to `trace|all`).
パイプライン
1) Parse → MacroIf/Match 正規化、Scope 属性付与)
2) LoopFormwhile/for/foreach のみ、キャリア整形)
3) Resolve → MIR Lowerヒント埋め、defer静的展開
4) 検証PHI先頭空PHI無し→ ヒント剥離 → Backend
受け入れ基準Release
- IR に scope/hint 名が一切出現しない。
- LoopForm ケースはヘッダ先頭に PHI がまとまり、空PHI無し。
- If/Match 式は join 変数 1 個で収束空PHI無し
今後の予定(短期)
- マクロ: @scope/@defer 記法のスキャフォールド → AST 属性付与(挙動は現状どおり、構造変更無し)。
- MIR: hint.* の型と注入ポイント定義(降下部のスケルトン、既定は no-op
- スモーク: IR に scope/hint 名が残らないこと、PHI 健全性が保たれることを確認。
Zero-cost policy
- ScopeBox is removed implicitly during MIR lowering (treated as a block). ScopeEnter/ScopeLeave hints are observational only. Execution and IR are unchanged.

View File

@ -0,0 +1,26 @@
# Testing Matrix — Mapping Specs to Tests
Purpose
- Map invariants/constraints to the concrete tests (smokes/goldens/unit) that verify them.
Categories
- PHI hygiene (LLVM)
- ir_phi_empty_check.sh — no empty PHIs
- ir_phi_hygiene_if_phi_ret.sh — PHIs at block head with if/ret pattern
- MIR hints (VM)
- hints_trace_smoke.sh — basic scope enter/leave
- hints_join_result_* — join diagnostics for 2/3 vars
- hints_scope_trycatch_smoke.sh — try/catch scopes
- Match normalization (VM/goldens)
- match_literal_basic / literal_three_arms output smokes
- match_guard_literal_or / type_basic_min goldens
- Exceptions (VM)
- expr_postfix_catch_cleanup_output_smoke.sh — postfix direct parser
- loop_postfix_catch_cleanup_output_smoke.sh — combined with loops
- LoopForm break/continue (VM)
- loopform_continue_break_output_smoke.sh — basic continue/break
- loop_nested_if_ctrl_output_smoke.sh — nested if inside loop
- loop_nested_block_break_output_smoke.sh — nested bare block with break
Maintenance
- When adding an invariant or lifting a constraint, update this matrix and link the tests.

View File

@ -29,7 +29,7 @@ Backward compat (deprecated)
MacroCtx (MVP)
- Rust側に最小の `MacroCtx``MacroCaps` を用意将来のAPI統合のため
- フィールド/メソッドMVP:
- `MacroCtx::from_env()` → 環境からcapabilitiesを組み立て
- `MacroCtx::from_env()` → 環境からcapabilitiesを組み立て(親プロセス)
- `ctx.gensym(prefix)` → 衛生識別子生成
- `ctx.report(level, message)` → 開発用レポート(標準エラー)
- `ctx.get_env(key)` → 環境取得(`NYASH_MACRO_CAP_ENV=1` のときのみ)
@ -88,6 +88,8 @@ CLI プロファイル(推奨)
Notes
- Built-in child route (stdin JSON -> stdout JSON) remains available when `NYASH_MACRO_BOX_CHILD_RUNNER=0`.
- Internal child can receive ctx via env: `NYASH_MACRO_CTX_JSON='{"caps":{"io":false,"net":false,"env":true}}'`
- CLI からも指定可能: `--macro-ctx-json '{"caps":{"io":false,"net":false,"env":true}}'`
- Strict mode: `NYASH_MACRO_STRICT=1` (default) fails build on macro child error/timeout; set `0` to fallback to identity.
- Timeout: `NYASH_NY_COMPILER_TIMEOUT_MS` (default `2000`).