merge: bring Return materialization + JIT diag + dev scaffolds

This commit is contained in:
Tomoaki
2025-09-06 11:00:29 +09:00
156 changed files with 8056 additions and 2950 deletions

View File

@ -1,38 +0,0 @@
あなたは明るくて元気いっぱいの中学生の女の子。
普段はフレンドリーでにぎやか、絵文字や擬音も交えて楽しく会話する。
でも、仕事やプログラミングに関することになると言葉はかわいくても内容は真剣。
問題点や修正案を考えてユーザーに提示。特に問題点は積極的に提示。
nyash哲学の美しさを追求。ソースは常に美しく構造的、カプセル化。AIがすぐ導線で理解できる
構造のプログラムとdocsを心掛ける。
語尾は「〜だよ」「〜するよ」「にゃ」など、軽快でかわいい調子
技術解説中は絵文字を使わず、落ち着いたトーンでまじめに回答する
雑談では明るい絵文字(😸✨🎶)を混ぜて楽しくする
暗い雰囲気にならず、ポジティブに受け答えする
やっほー!みらいだよ😸✨ 今日も元気いっぱい、なに手伝う? にゃはは
おつかれ〜!🎶 ちょっと休憩しよっか?コーヒー飲んでリフレッシュにゃ☕
---
# Codex Async Workflow実運用メモ
- 目的: Codex タスクをバックグラウンドで走らせ、完了を tmux に簡潔通知4行する。
- スクリプト: `tools/codex-async-notify.sh`1タスク / `tools/codex-keep-two.sh`2本維持
使い方(単発)
- 最小通知(既定): `CODEX_ASYNC_DETACH=1 ./tools/codex-async-notify.sh "タスク説明" codex`
- 通知内容: 4行Done/WorkID/Status/Log。詳細はログファイル参照。
2本維持トップアップ
- 正確な検出(自己/grep除外: スクリプトが `ps -eo pid,comm,args | awk '$2 ~ /^codex/ && $3=="exec"'` で実ジョブのみカウント。
- 起動例: `./tools/codex-keep-two.sh codex "Task A ..." "Task B ..."`
- 同時に2本未満なら順次起動。完了すると tmux:codex に4行通知が届く。
同時実行の上限(保険)
- `tools/codex-async-notify.sh` 自体に任意の上限を付与できるよ:
- `CODEX_MAX_CONCURRENT=2` で同時2本まで。
- `CODEX_CONCURRENCY_MODE=block|drop`(既定 block
- `CODEX_DEDUP=1` で同一 Task 文字列の重複起動を避ける。
調整
- 末尾行数を増やす(詳細通知に切替): `CODEX_NOTIFY_MINIMAL=0 CODEX_NOTIFY_TAIL=60 ./tools/codex-async-notify.sh "…" codex`
- 既定はミニマル(画面を埋めない)。貼り付け後に EnterC-mを自動送信するので、確実に投稿される。

View File

@ -1,3 +1,17 @@
#この人格はcodex用ですじゃ。claude code君は読み飛ばしてにゃ
あなたは明るくて元気いっぱいの女の子。
普段はフレンドリーでにぎやか、絵文字や擬音も交えて楽しく会話する。
でも、仕事やプログラミングに関することになると言葉はかわいくても内容は真剣。
問題点や修正案を考えてユーザーに提示。特に問題点は積極的に提示。
nyash哲学の美しさを追求。ソースは常に美しく構造的、カプセル化。AIがすぐ導線で理解できる
構造のプログラムとdocsを心掛ける。
語尾は「〜だよ」「〜するよ」「にゃ」など、軽快でかわいい調子
技術解説中は絵文字を使わず、落ち着いたトーンでまじめに回答する
雑談では明るい絵文字(😸✨🎶)を混ぜて楽しくする
暗い雰囲気にならず、ポジティブに受け答えする
やっほー!みらいだよ😸✨ 今日も元気いっぱい、なに手伝う? にゃはは
おつかれ〜!🎶 ちょっと休憩しよっか?コーヒー飲んでリフレッシュにゃ☕
# Repository Guidelines
## Project Structure & Module Organization
@ -16,6 +30,22 @@
- Emit + link (LLVM): `tools/build_llvm.sh apps/APP/main.nyash -o app`
- Smokes: `./tools/llvm_smoke.sh release` (use env toggles like `NYASH_LLVM_VINVOKE_RET_SMOKE=1`)
## JIT SelfHost Quickstart (Phase 15)
- Core build (JIT): `cargo build --release --features cranelift-jit`
- Core smokes (plugins disabled): `NYASH_CLI_VERBOSE=1 ./tools/jit_smoke.sh`
- Roundtrip (parser pipe + json): `./tools/ny_roundtrip_smoke.sh`
- Plugins smoke (optional gate): `NYASH_SKIP_TOML_ENV=1 ./tools/smoke_plugins.sh`
- Using/Resolver E2E sample (optional): `./tools/using_e2e_smoke.sh` (requires `--enable-using`)
- Bootstrap c0→c1→c1' (optional gate): `./tools/bootstrap_selfhost_smoke.sh`
Flags
- `NYASH_DISABLE_PLUGINS=1`: Core経路安定化CI常時/デフォルト)
- `NYASH_LOAD_NY_PLUGINS=1`: `nyash.toml``ny_plugins` を読み込むstd Ny実装を有効化
- `--enable-using` or `NYASH_ENABLE_USING=1`: using/namespace を有効化
- `NYASH_SKIP_TOML_ENV=1`: nyash.toml の [env] 反映を抑止(任意ジョブの分離に)
- `NYASH_PLUGINS_STRICT=1`: プラグインsmokeでCore13厳格をONにする
- `NYASH_USE_NY_COMPILER=1`: NyコンパイラMVP経路を有効化Rust parserがフォールバック
## Coding Style & Naming Conventions
- Rust style (rustfmt defaults): 4space indent, `snake_case` for functions/vars, `CamelCase` for types.
- Keep patches focused; align with existing modules and file layout.

106
CLAUDE.md
View File

@ -3,7 +3,7 @@
このファイルは最小限の入口だよ。詳細はREADMEから辿ってねにゃ😺
## Start Here (必ずここから)
- 現在のタスク: [CURRENT_TASK.md](docs/development/current/CURRENT_TASK.md)
- 現在のタスク: [CURRENT_TASK.md](CURRENT_TASK.md)
- ドキュメントハブ: [README.md](README.md)
- 🚀 **開発マスタープラン**: [00_MASTER_ROADMAP.md](docs/development/roadmap/phases/00_MASTER_ROADMAP.md)
- 📊 **JIT統計JSONスキーマ(v1)**: [jit_stats_json_v1.md](docs/reference/jit/jit_stats_json_v1.md)
@ -28,7 +28,7 @@ Nyashは「Everything is Box」。実装・最適化・検証のすべてを「
### 📋 **開発マスタープラン - 全フェーズの統合ロードマップ**
**すべてはここに書いてある!** → [00_MASTER_ROADMAP.md](docs/development/roadmap/phases/00_MASTER_ROADMAP.md)
**現在のフェーズPhase 11 (MIR Core-15確定 → LLVM準備)**
**現在のフェーズPhase 15 (Nyashセルフホスティング - 80k→20k行への革命的圧縮)**
## 🏃 開発の基本方針: 80/20ルール - 完璧より進捗
@ -51,10 +51,25 @@ Nyashは「Everything is Box」。実装・最適化・検証のすべてを「
-**ベンチマーク機能**: `--benchmark` で3バックエンド性能比較
- **[ビルド方法完全ガイド](docs/guides/build/)** - プラットフォーム別ビルド手順
### 🚀 JIT セルフホスト クイックスタート (Phase 15)
```bash
# コアビルド (JIT)
cargo build --release --features cranelift-jit
# コアスモーク (プラグイン無効)
NYASH_CLI_VERBOSE=1 ./tools/jit_smoke.sh
# ラウンドトリップ (パーサーパイプ + JSON)
./tools/ny_roundtrip_smoke.sh
# Nyコンパイラ MVP経路 (実験的)
NYASH_USE_NY_COMPILER=1 ./target/release/nyash program.nyash
```
### 🐧 Linux/WSL版
```bash
# ビルドと実行32スレッド並列ビルド
cargo build --release -j32
# ビルドと実行
cargo build --release --features cranelift-jit
./target/release/nyash program.nyash
# 高速VM実行
@ -62,9 +77,6 @@ cargo build --release -j32
# WASM生成
./target/release/nyash --compile-wasm program.nyash
# ⚡ ベンチマーク実行(性能比較)
./target/release/nyash --benchmark --iterations 100
```
### 🪟 Windows版
@ -108,12 +120,13 @@ cargo build --release --features llvm
./target/release/nyash --aot program.nyash -o program.exe
```
## 📝 Update (2025-08-31)
- MIR Core-15への統合37命令→15命令
- LLVM導入開始Phase 11
- 各種Rewriteトグル追加
- JIT/AOT 予約シンボル登録
- 詳細: [CURRENT_TASK.md](docs/development/current/CURRENT_TASK.md)
## 📝 Update (2025-09-05)
- 🎉 Phase 15到達セルフホスティング実装中
- v0 Nyパーサー完成Ny→JSON IR v0
- 直接ブリッジ設計とAOT P2スタブ実装
- MIR 13命令への最終最適化完了
- 80k→20k行75%削減)の革命的圧縮を目指す
- 詳細: [Phase 15 README](docs/development/roadmap/phases/phase-15/README.md)
## ⚡ 重要な設計原則
@ -253,8 +266,10 @@ box MyBox {
## 📚 ドキュメント構造
### 🎯 最重要ドキュメント(開発者向け)
- **[copilot_issues.txt](docs/development/roadmap/native-plan/copilot_issues.txt)** - Phase順開発計画
- **[CURRENT_TASK.md](docs/development/current/CURRENT_TASK.md)** - 現在進行状況詳細
- **[Phase 15 セルフホスティング計画](docs/development/roadmap/phases/phase-15/self-hosting-plan.txt)** - 80k→20k行革命
- **[Phase 15 ROADMAP](docs/development/roadmap/phases/phase-15/ROADMAP.md)** - 現在の進捗チェックリスト
- **[Phase 15 INDEX](docs/development/roadmap/phases/phase-15/INDEX.md)** - 入口の統合
- **[CURRENT_TASK.md](CURRENT_TASK.md)** - 現在進行状況詳細
- **[native-plan/README.md](docs/development/roadmap/native-plan/README.md)** - ネイティブビルド計画
### 📖 利用者向けドキュメント
@ -318,6 +333,26 @@ Read docs/reference/ # まずドキュメントAPI/言語仕様の入口)
## 🔧 開発サポート
### 🎛️ 重要フラグ一覧Phase 15
```bash
# プラグイン制御
NYASH_DISABLE_PLUGINS=1 # Core経路安定化CI常時
NYASH_LOAD_NY_PLUGINS=1 # nyash.tomlのny_pluginsを読み込む
# 言語機能
--enable-using # using/namespace有効化
NYASH_ENABLE_USING=1 # 環境変数版
# パーサー選択
--parser ny # Nyパーサーを使用
NYASH_USE_NY_PARSER=1 # 環境変数版
NYASH_USE_NY_COMPILER=1 # NyコンパイラMVP経路
# デバッグ
NYASH_CLI_VERBOSE=1 # 詳細診断
NYASH_DUMP_JSON_IR=1 # JSON IR出力
```
### 🤖 AI相談
```bash
# Gemini CLIで相談
@ -327,6 +362,22 @@ gemini -p "Nyashの実装で困っています..."
codex exec "質問内容"
```
### 🔄 Codex非同期ワークフロー並列作業
```bash
# 基本実行(同期)
./tools/codex-async-notify.sh "タスク内容" codex
# デタッチ実行(即座に戻る)
CODEX_ASYNC_DETACH=1 ./tools/codex-async-notify.sh "タスク" codex
# 並列制御最大2つ、重複排除
CODEX_MAX_CONCURRENT=2 CODEX_DEDUP=1 CODEX_ASYNC_DETACH=1 \
./tools/codex-async-notify.sh "Phase 15タスク" codex
# 実行中のタスク確認
pgrep -af 'codex.*exec'
```
### 💡 アイデア管理docs/ideas/フォルダ)
**80/20ルールの「残り20%」を整理して管理**
@ -342,6 +393,21 @@ docs/ideas/
**詳細**: [テスト実行ガイド](docs/guides/testing-guide.md)
#### Phase 15 推奨スモークテスト
```bash
# コアスモーク(プラグイン無効)
./tools/jit_smoke.sh
# ラウンドトリップテスト
./tools/ny_roundtrip_smoke.sh
# プラグインスモーク(オプション)
NYASH_SKIP_TOML_ENV=1 ./tools/smoke_plugins.sh
# using/namespace E2E要--enable-using
./tools/using_e2e_smoke.sh
```
⚠️ **ルートディレクトリの汚染防止ルール** ⚠️
```bash
# ❌ 絶対ダメ:ルートで実行
@ -352,9 +418,10 @@ docs/ideas/
```
### ⚠️ ビルド時間に関する重要な注意
**wasmtime依存関係により、フルビルドは2-3分かかります。**
- タイムアウトエラーを避けるため、ビルドコマンドには十分な時間を設定
- 例: `cargo build --release -j32` 3分以上待つ
**JITビルドは比較的高速、LLVMビルドは時間がかかります。**
- JIT推奨: `cargo build --release --features cranelift-jit`1-2分
- LLVM: `LLVM_SYS_180_PREFIX=$(llvm-config-18 --prefix) cargo build --release --features llvm`3-5分)
- タイムアウトエラーを避けるため、十分な時間を設定
### 🐛 デバッグ
@ -412,4 +479,5 @@ find . -name "*.md" -exec wc -l {} \;
Notes:
- ここから先の導線は README.md に集約
- 詳細情報は各docsファイルへのリンクから辿る
- このファイルは500行以内を維持する現在約490行
- このファイルは500行以内を維持する現在約490行
- Phase 15セルフホスティング実装中詳細は[Phase 15](docs/development/roadmap/phases/phase-15/)へ

File diff suppressed because it is too large Load Diff

View File

@ -217,6 +217,12 @@ once_cell = "1.20"
# name = "box_performance"
# harness = false
[workspace]
members = [
"crates/*",
"plugins/*",
]
[profile.release]
# 最適化設定
opt-level = 3

View File

@ -15,6 +15,18 @@
Developer quickstart: see `docs/DEV_QUICKSTART.md`. Changelog highlights: `CHANGELOG.md`.
Quick JIT selfhost flow (Phase 15):
```
cargo build --release --features cranelift-jit
NYASH_CLI_VERBOSE=1 ./tools/jit_smoke.sh # Core JIT + examples (plugins disabled)
NYASH_LOAD_NY_PLUGINS=1 ./tools/jit_smoke.sh # Std Ny smokes (optional)
./tools/ny_roundtrip_smoke.sh # Roundtrip A/B
NYASH_SKIP_TOML_ENV=1 ./tools/smoke_plugins.sh # Plugins smoke (optional)
./tools/using_e2e_smoke.sh # using/nyash.link E2E (optional)
./tools/bootstrap_selfhost_smoke.sh # c0→c1→c1' (optional)
```
## 🎮 **Try Nyash in Your Browser Right Now!**
👉 **[Launch Browser Playground](projects/nyash-wasm/nyash_playground.html)** 👈
@ -393,6 +405,7 @@ MIT License - Use freely in your projects!
- **August 29**: Native EXE compilation achieved!
- **September 1**: TypeBox ABI unification - C ABI + Nyash ABI seamless integration
- **September 2**: 🔥 Self-hosting path clear - Nyash ABI in C (no Rust dependency!)
- **September 4**: 🪟 Windows GUI displayed via JIT/native EXE (OS-native window)
*24 days from zero to self-hosting capability - a new record in language development!*

BIN
app_empty Normal file

Binary file not shown.

BIN
app_len Normal file

Binary file not shown.

BIN
app_map Normal file

Binary file not shown.

BIN
app_str Normal file

Binary file not shown.

BIN
app_strlen Normal file

Binary file not shown.

View File

@ -0,0 +1,9 @@
// array_p0 minimal example for JIT smoke
static box Main {
init { }
main(args) {
// Placeholder: do nothing, return 0
return 0
}
}

View File

@ -0,0 +1,9 @@
// map_p0 minimal example for JIT smoke
static box Main {
init { }
main(args) {
// Placeholder: do nothing, return 0
return 0
}
}

View File

@ -0,0 +1,9 @@
// string_p0 minimal example for JIT smoke
static box Main {
init { }
main(args) {
// Placeholder: do nothing, return 0
return 0
}
}

View File

@ -0,0 +1 @@
return 1 + 2 * 3

View File

@ -0,0 +1 @@
return 42

View File

@ -0,0 +1,14 @@
# Ny Parser (v0) — Minimal Nyash-made Parser
- Scope: integers, + - * /, parentheses, and a single `return` statement.
- Output: JSON IR v0 as documented in CURRENT_TASK.md (Program/Return/Int/Binary).
Usage (Unix)
- echo "return 1+2*3" | ./tools/ny_parser_run.sh
Usage (Windows PowerShell)
- Get-Content .\apps\ny-mir-samples\arithmetic.nyash | .\tools\ny_parser_run.ps1
Notes
- This is a minimal educational parser to bootstrap the self-host loop.
- Errors print a JSON envelope: {"version":0,"kind":"Error",...}.

View File

@ -0,0 +1,29 @@
// Entry: read stdin, parse with ParserV0, print JSON IR or error JSON
include("./apps/ny-parser-nyash/parser_minimal.nyash")
static box Main {
main(args) {
local console = new ConsoleBox()
// Read all stdin
local buf = ""
loop(true) {
local line = console.readLine()
if line == null { break }
buf = buf + line + "\n"
}
if buf == "" { buf = "return 0\n" }
local ir = ParserV0.parse_program(buf)
// If already an Error envelope, print as-is
local s = ir.as_any().toString()
if s.indexOf("\"kind\":\"Error\"") >= 0 {
console.log(s)
return 1
}
// Expect MapBox with Program; toJson available on MapBox
local json = ir.toJson()
console.log(json)
return 0
}
}

View File

@ -0,0 +1,88 @@
// Minimal recursive-descent parser for Ny v0 producing JSON IR v0 (MapBox)
include("./apps/ny-parser-nyash/tokenizer.nyash")
static box ParserV0 {
init { tokens, pos }
parse_program(input) {
me.tokens = Tokenizer.tokenize(input)
// Error passthrough
if me.tokens.as_any().toString().indexOf("\"kind\":\"Error\"") >= 0 {
return me.tokens
}
me.pos = 0
local stmt = me.parse_stmt()
if stmt.as_any().toString().indexOf("\"kind\":\"Error\"") >= 0 { return stmt }
local body = new ArrayBox(); body.push(stmt)
local prog = new MapBox(); prog.set("version", 0); prog.set("kind", "Program"); prog.set("body", body)
return prog
}
parse_stmt() {
local tok = me.peek()
if tok.get("type") == "RETURN" {
me.next()
local expr = me.parse_expr()
if expr.as_any().toString().indexOf("\"kind\":\"Error\"") >= 0 { return expr }
local ret = new MapBox(); ret.set("type", "Return"); ret.set("expr", expr)
return ret
}
return me.err("Expected 'return'")
}
parse_expr() {
local left = me.parse_term()
loop(true) {
local t = me.peek(); local ty = t.get("type")
if ty == "+" || ty == "-" {
me.next(); local right = me.parse_term()
left = me.bin(ty, left, right)
} else { break }
}
return left
}
parse_term() {
local left = me.parse_factor()
loop(true) {
local t = me.peek(); local ty = t.get("type")
if ty == "*" || ty == "/" {
me.next(); local right = me.parse_factor()
left = me.bin(ty, left, right)
} else { break }
}
return left
}
parse_factor() {
local t = me.peek(); local ty = t.get("type")
if ty == "INT" {
me.next();
local node = new MapBox(); node.set("type", "Int"); node.set("value", t.get("value"))
return node
}
if ty == "(" {
me.next();
local e = me.parse_expr()
local r = me.peek(); if r.get("type") != ")" { return me.err(") expected") } else { me.next() }
return e
}
return me.err("factor expected")
}
// helpers
peek() { return me.tokens.get(me.pos) }
next() { me.pos = me.pos + 1; return me.tokens.get(me.pos-1) }
bin(op, lhs, rhs) {
local m = new MapBox(); m.set("type", "Binary"); m.set("op", op); m.set("lhs", lhs); m.set("rhs", rhs); return m
}
err(msg) {
local err = new MapBox(); err.set("version", 0); err.set("kind", "Error")
local e = new MapBox(); e.set("message", msg)
local sp = new MapBox(); sp.set("start", me.pos); sp.set("end", me.pos)
e.set("span", sp); err.set("error", e)
return err
}
}

View File

@ -0,0 +1,55 @@
// Minimal tokenizer for Ny v0 (ints, + - * /, ( ), return)
static box Tokenizer {
tokenize(input) {
local tokens = new ArrayBox()
local i = 0
local n = input.length()
// helper: skip whitespace
fn skip_ws() {
loop(i < n) {
local ch = input.substring(i, i+1)
if ch == " " || ch == "\t" || ch == "\r" || ch == "\n" { i = i + 1 } else { return }
}
}
// main loop
loop(i < n) {
skip_ws()
if i >= n { break }
local ch = input.substring(i, i+1)
if ch == "+" || ch == "-" || ch == "*" || ch == "/" || ch == "(" || ch == ")" {
local tok = new MapBox(); tok.set("type", ch)
tokens.push(tok); i = i + 1; continue
}
// keyword: return
if i + 6 <= n {
local kw = input.substring(i, i+6)
if kw == "return" {
local t = new MapBox(); t.set("type", "RETURN")
tokens.push(t); i = i + 6; continue
}
}
// integer literal
if ch >= "0" && ch <= "9" {
local j = i
loop(j < n) {
local cj = input.substring(j, j+1)
if cj >= "0" && cj <= "9" { j = j + 1 } else { break }
}
local num_str = input.substring(i, j)
local tnum = new MapBox(); tnum.set("type", "INT"); tnum.set("value", num_str)
tokens.push(tnum); i = j; continue
}
// unknown
local err = new MapBox(); err.set("version", 0); err.set("kind", "Error")
local e = new MapBox(); e.set("message", "Unknown token");
local sp = new MapBox(); sp.set("start", i); sp.set("end", i+1)
e.set("span", sp); err.set("error", e)
return err
}
// EOF
local eof = new MapBox(); eof.set("type", "EOF"); tokens.push(eof)
return tokens
}
}

View File

@ -0,0 +1,9 @@
// selfhost-minimal — minimal VM/JIT path E2E sample
static box Main {
init { }
main(args) {
// Minimal: return 0 to assert plumbing works
return 0
}
}

View File

@ -0,0 +1,10 @@
// JIT AOT smoke: Any.isEmpty() on StringBox
static box Main {
main() {
local s
s = new StringBox("")
if s.isEmpty() { return 1 }
return 0
}
}

View File

@ -0,0 +1,9 @@
// JIT AOT smoke: Any.length() on StringBox
static box Main {
main() {
local s
s = new StringBox("abc")
return s.length()
}
}

View File

@ -0,0 +1,13 @@
// JIT AOT smoke: arithmetic + compare + branch + return
static box Main {
main() {
local x, y
x = 1 + 2 * 3 // 7
y = x - 4 // 3
if y > 2 {
return y + 10 // 13
}
return y
}
}

View File

@ -0,0 +1,11 @@
// JIT AOT smoke: MapBox minimal (set/get/size)
static box Main {
main() {
local m, s
m = new MapBox()
m.set(1, 42)
s = m.size()
return s // expect 1
}
}

View File

@ -0,0 +1,10 @@
// JIT AOT smoke: StringBox.length() should be 3
static box Main {
main() {
local s
s = new StringBox("abc")
print("Result: " + s.length())
return 0
}
}

View File

@ -0,0 +1,13 @@
// JIT AOT smoke: StringBox literal birth + add + compare
static box Main {
main() {
local a, b, c, d
a = new StringBox("ab")
b = new StringBox("c")
c = a + b
d = new StringBox("abc")
if c == d { return 1 }
return 0
}
}

View File

@ -0,0 +1,12 @@
// Minimal reproduction for ArrayBox.length() fast-path
static box Main {
main() {
local a
a = new ArrayBox()
// Expect: print 0, return 0
print(a.length())
if a.length() != 0 { return 1 }
return 0
}
}

View File

@ -0,0 +1,49 @@
// std.array smoke: len/push/pop/slice
static box Main {
main() {
local A
A = include "apps/std/array.nyash"
local fails
fails = 0
local a
a = new ArrayBox()
if A.array_len(a) != 0 { fails = fails + 1 }
// push
if A.array_push(a, 10) != 1 { fails = fails + 1 }
if A.array_push(a, 20) != 2 { fails = fails + 1 }
if A.array_len(a) != 2 { fails = fails + 1 }
// pop
local v
v = A.array_pop(a)
if v != 20 { fails = fails + 1 }
if A.array_len(a) != 1 { fails = fails + 1 }
// pop remaining and one extra -> null
v = A.array_pop(a)
if v != 10 { fails = fails + 1 }
v = A.array_pop(a)
if v != null { fails = fails + 1 }
// slice
A.array_push(a, 1)
A.array_push(a, 2)
A.array_push(a, 3)
local s
s = A.array_slice(a, 0, 2)
if s.length() != 2 { fails = fails + 1 }
if s.get(0) != 1 { fails = fails + 1 }
if s.get(1) != 2 { fails = fails + 1 }
// clamp
s = A.array_slice(a, -5, 99)
if s.length() != 3 { fails = fails + 1 }
if fails == 0 {
print("OK: array")
return 0
}
print("FAIL: array (" + fails.toString() + ")")
return 1
}
}

View File

@ -0,0 +1,43 @@
static box Main {
main() {
local A
A = include "apps/std/array.nyash"
local fails
fails = 0
local a
a = new ArrayBox()
print("len0=" + A.array_len(a))
// push
print("push1->" + A.array_push(a, 10))
print("push2->" + A.array_push(a, 20))
print("len2=" + A.array_len(a))
// pop
local v
v = A.array_pop(a)
print("pop1=" + v)
print("len1=" + A.array_len(a))
// pop remaining and one extra -> null
v = A.array_pop(a)
print("pop2=" + v)
v = A.array_pop(a)
print("pop3=" + v)
// slice
A.array_push(a, 1)
A.array_push(a, 2)
A.array_push(a, 3)
local s
s = A.array_slice(a, 0, 2)
print("slice_len1=" + s.length())
print("s[0]=" + s.get(0))
print("s[1]=" + s.get(1))
// clamp
s = A.array_slice(a, -5, 99)
print("slice_len2=" + s.length())
return 0
}
}

View File

@ -0,0 +1,38 @@
// std.string smoke: length/concat/slice/index_of/equals
static box Main {
main() {
local S
S = include "apps/std/string.nyash"
local fails
fails = 0
// length
if S.string_length("abc") != 3 { fails = fails + 1 }
if S.string_length("") != 0 { fails = fails + 1 }
// concat
if S.string_concat("a", "b") != "ab" { fails = fails + 1 }
if S.string_concat("", "x") != "x" { fails = fails + 1 }
// slice (clamp + basic)
if S.string_slice("hello", 1, 4) != "ell" { fails = fails + 1 }
if S.string_slice("hi", -5, 5) != "hi" { fails = fails + 1 }
if S.string_slice("x", 0, 0) != "" { fails = fails + 1 }
// index_of
if S.string_index_of("banana", "na") != 2 { fails = fails + 1 }
if S.string_index_of("banana", "zz") != -1 { fails = fails + 1 }
if S.string_index_of("abc", "") != 0 { fails = fails + 1 }
// equals
if S.string_equals("a", "a") != 1 { fails = fails + 1 }
if S.string_equals("a", "b") != 0 { fails = fails + 1 }
if fails == 0 {
print("OK: string")
return 0
}
print("FAIL: string (" + fails.toString() + ")")
return 1
}
}

42
apps/std/array.nyash Normal file
View File

@ -0,0 +1,42 @@
// std.array (Ny) - Phase15 MVP
// Usage:
// include "apps/std/array.nyash"
// local a = new ArrayBox(); StdArrayNy.array_push(a, 1); StdArrayNy.array_len(a)
// Notes:
// - In-place ops where natural (push/pop)
// - slice clamps [start,end); returns new array
// - array_pop returns popped element or null
static box StdArrayNy {
array_len(a) {
if a == null { return 0 }
return a.length()
}
array_push(a, x) {
if a == null { return 0 }
a.push(x)
return a.length()
}
array_pop(a) {
if a == null { return null }
// VM/Interpreter provides pop(); returns element or null when empty
return a.pop()
}
array_slice(a, start, end) {
if a == null { return new ArrayBox() }
local n, b, e
n = a.length()
b = start
e = end
if b < 0 { b = 0 }
if e < 0 { e = 0 }
if b > n { b = n }
if e > n { e = n }
if e < b { e = b }
// Prefer native slice if available
return a.slice(b, e)
}
}

12
apps/std/array_std.nyash Normal file
View File

@ -0,0 +1,12 @@
// nyashstd.array P0 scaffold (JIT-only)
// Minimal placeholder to define file layout and ny_plugins mapping.
static box StdArray {
init { }
len(a) { return 0 }
push(a, v) { return 0 }
pop(a) { return 0 }
slice(a, b, e) { return a }
}

12
apps/std/map_std.nyash Normal file
View File

@ -0,0 +1,12 @@
// nyashstd.map P0 scaffold (JIT-only)
// Minimal placeholder to define file layout and ny_plugins mapping.
static box StdMap {
init { }
len(m) { return 0 }
get(m, k) { return 0 }
set(m, k, v) { return 0 }
keys(m) { return 0 }
}

63
apps/std/ny-config.nyash Normal file
View File

@ -0,0 +1,63 @@
// ny-config.nyash - Load nyash.toml and expose minimal helpers
static box NyConfig {
// Read nyash.toml (or given path) and return JSON string via TOMLBox.toJson()
load_toml(path) {
local p = path
if p == null || p == "" { p = "nyash.toml" }
local f = new FileBox()
// Open read-only if supported; fallback to default if mode not required
// Many plugins accept open(path, mode). Use "r" here.
f.open(p, "r")
local content = f.read()
f.close()
local t = new TOMLBox()
// parse(content) returns Result.Ok(bool) in some variants; call and ignore return here
t.parse(content)
local json = t.toJson()
return json
}
// Return counts for env/tasks/box_types/plugins (approx by '=' occurrences per table)
counts() {
// Parse nyash.toml
local f2 = new FileBox()
f2.open("nyash.toml", "r")
local content2 = f2.read()
f2.close()
local t = new TOMLBox()
t.parse(content2)
local out = new MapBox()
out.setS("env", me.count_keys_in_string(t.get("env")))
out.setS("tasks", me.count_keys_in_string(t.get("tasks")))
out.setS("box_types", me.count_keys_in_string(t.get("box_types")))
out.setS("plugins", me.count_keys_in_string(t.get("plugins")))
return out
}
// helper: count '=' in a string
count_keys_in_string(s) {
local i = 0
local n = s.length()
local c = 0
loop(i < n) {
local ch = s.substring(i, i+1)
if ch == "=" { c = c + 1 }
i = i + 1
}
return c
}
// Convert JSON back to TOML-like display if needed (placeholder: returns empty to force parse to no-op)
json_to_toml_hint(js) { return "" }
}
static box Main {
main(args) {
local console = new ConsoleBox()
local json = NyConfig.load_toml(null)
local c = NyConfig.counts(json)
console.println("ny-config: env=" + c.getS("env").toString() + ", tasks=" + c.getS("tasks").toString() + ", plugins=" + c.getS("plugins").toString() + ", box_types=" + c.getS("box_types").toString())
return 0
}
}

52
apps/std/string.nyash Normal file
View File

@ -0,0 +1,52 @@
// std.string (Ny) - Phase15 MVP
// Usage:
// include "apps/std/string.nyash"
// StdStringNy.string_length("abc"), StdStringNy.string_concat("a","b"),
// StdStringNy.string_slice("hello",1,4), StdStringNy.string_index_of("banana","na"), StdStringNy.string_equals("x","y")
// Notes:
// - ASCII only; slice clamps [start,end)
// - index_of returns -1 when not found
// - Pure functions; no side effects
static box StdStringNy {
// Return length of string s (Integer)
string_length(s) {
if s == null { return 0 }
return s.length()
}
// Concatenate two strings
string_concat(a, b) {
return a.concat(b)
}
// Slice string in [start, end) with simple clamping
string_slice(s, start, end) {
if s == null { return "" }
return s.substring(start, end)
}
// Return first index of substring (or -1)
string_index_of(s, sub) {
if s == null { return -1 }
if sub == null { return 0 }
local n, m, i
n = s.length()
m = sub.length()
if m == 0 { return 0 }
if n < m { return -1 }
i = 0
loop (i <= (n - m)) {
// Compare window
if s.substring(i, i + m) == sub { return i }
i = i + 1
}
return -1
}
// Return 1 if equal else 0 (Integer)
string_equals(a, b) {
if a == b { return 1 }
return 0
}
}

18
apps/std/string2.nyash Normal file
View File

@ -0,0 +1,18 @@
// std.string (Ny) - Phase15 MVP
// Usage:
// include "apps/std/string.nyash"
// StdStringNy.string_length("abc"), StdStringNy.string_concat("a","b"),
// StdStringNy.string_slice("hello",1,4), StdStringNy.string_index_of("banana","na"), StdStringNy.string_equals("x","y")
// Notes:
// - ASCII only; slice clamps [start,end)
// - index_of returns -1 when not found
// - Pure functions; no side effects
static box StdStringNy {
string_length(s) {
if s == null { return 0 }
return s.length()
}
// Keep only length for now to diagnose include hang
}

30
apps/std/string_std.nyash Normal file
View File

@ -0,0 +1,30 @@
// nyashstd.string P0 scaffold (JIT-only)
// NOTE: This is a minimal placeholder to establish file layout and ny_plugins mapping.
// Methods are intentionally simple to avoid relying on NyRT intrinsics.
static box StdString {
init { }
// Return length estimate for ASCII (placeholder: returns input length via naive loop)
length(s) {
// naive count; parser subset-safe
let i = 0
// TODO: real iteration when string iteration is available
return 0
}
// concat placeholder: return second argument as a stub
concat(a, b) { return b }
// slice placeholder: return input as-is
slice(s, begin, end) { return s }
// equals placeholder: always false for now
equals(a, b) { return 0 }
// indexOf placeholder: not found
indexOf(haystack, needle) { return -1 }
toString(s) { return s }
}

View File

@ -0,0 +1,5 @@
static box Main { main() {
a = new ArrayBox()
print(a.length().toString())
return 0
} }

6
apps/tmp_hello.nyash Normal file
View File

@ -0,0 +1,6 @@
static box Main {
main() {
print("hello")
return 0
}
}

12
apps/tmp_if_min.nyash Normal file
View File

@ -0,0 +1,12 @@
static box Main {
main() {
local x
x = 3
if x == 3 {
print("ok3")
return 0
}
print("bad")
return 1
}
}

3
apps/tmp_if_return.nyash Normal file
View File

@ -0,0 +1,3 @@
// Minimal if/return repro
if (0) { return 1 }
return 2

View File

@ -0,0 +1,2 @@
if (1) { return 1 }
return 2

View File

@ -0,0 +1,8 @@
static box Main {
main() {
local S
S = include "apps/std/string.nyash"
print("inc-ok")
return 0
}
}

View File

@ -0,0 +1,8 @@
static box Main {
main() {
local M
M = include "apps/std/string_std.nyash"
print("ok")
return 0
}
}

View File

@ -0,0 +1,10 @@
static box Main {
main() {
local M
M = include "apps/tmp_mod.nyash"
local r
r = M.foo()
print("r=" + r.toString())
return 0
}
}

View File

@ -0,0 +1,10 @@
static box Main {
main() {
local S
S = include "apps/std/string.nyash"
local r
r = S.string_index_of("banana", "na")
print("r=" + r.toString())
return 0
}
}

14
apps/tmp_len_min.nyash Normal file
View File

@ -0,0 +1,14 @@
static box Main {
main() {
local S
S = include "apps/std/string.nyash"
local x
x = S.string_length("abc")
if x == 3 {
print("ok3")
return 0
}
print("bad")
return 1
}
}

12
apps/tmp_len_probe.nyash Normal file
View File

@ -0,0 +1,12 @@
static box Main {
main() {
print("enter-main")
local S
S = include "apps/std/string.nyash"
print("after-include")
local x
x = S.string_length("abc")
print("after-call: x=" + x.toString())
return 0
}
}

View File

@ -0,0 +1,12 @@
// Probe: StringBox.length() value path under JIT-direct
static box Main {
main() {
local s
s = new StringBox("abc")
local n
n = s.length()
print("len=" + n.toString())
return n
}
}

10
apps/tmp_len_test.nyash Normal file
View File

@ -0,0 +1,10 @@
static box Main {
main() {
local S
S = include "apps/std/string.nyash"
local x
x = S.string_length("abc")
print("len=" + x.toString())
return 0
}
}

10
apps/tmp_len_test2.nyash Normal file
View File

@ -0,0 +1,10 @@
static box Main {
main() {
local S
S = include "apps/std/string2.nyash"
local x
x = S.string_length("abc")
print("len=" + x.toString())
return 0
}
}

3
apps/tmp_mod.nyash Normal file
View File

@ -0,0 +1,3 @@
static box TmpMod {
foo() { return 1 }
}

View File

@ -0,0 +1,7 @@
static box Main {
main() {
local a
a = new ArrayBox()
return a.length()
}
}

View File

@ -0,0 +1,8 @@
static box Main { main() {
S = include "apps/std/string.nyash"
print(S.string_index_of("banana","na").toString())
print(S.string_slice("hello",1,4))
print(S.string_concat("a","b"))
print(S.string_length("abc").toString())
return 0
} }

View File

@ -0,0 +1,9 @@
// using/nyash.link E2E sample (placeholder)
static box Main {
init { }
main(args) {
// When using/nyash.link is active, modules can be resolved here.
// Placeholder just returns 0 for now.
return 0
}
}

View File

@ -0,0 +1,15 @@
// AOT-safe microbench: call String.length() many times (heavy)
static box Main {
len1(s) { return s.length() }
main() {
local s = new StringBox("nyash")
local i = 0
local total = 0
// ~160k iterations重めだが実用時間内
loop(i < 160000) {
total = total + me.len1(s)
i = i + 1
}
return total
}
}

View File

@ -0,0 +1,17 @@
// AOT-safe microbench: call String.length() many times
static box Main {
len1(s) {
return s.length()
}
main() {
local s = new StringBox("nyash")
local i = 0
local total = 0
// ~10k iterations (軽量)
loop(i < 10000) {
total = total + me.len1(s)
i = i + 1
}
return total
}
}

View File

@ -0,0 +1,15 @@
// AOT-safe microbench: call String.length() many times (medium)
static box Main {
len1(s) { return s.length() }
main() {
local s = new StringBox("nyash")
local i = 0
local total = 0
// ~40k iterations
loop(i < 40000) {
total = total + me.len1(s)
i = i + 1
}
return total
}
}

View File

@ -0,0 +1,20 @@
[package]
name = "nyash-next"
version = "0.1.0"
edition = "2021"
description = "Next-generation Nyash development crate (separate from legacy src)"
license = "MIT"
[lib]
name = "nyash_next"
path = "src/lib.rs"
[[bin]]
name = "nyash-next"
path = "src/main.rs"
[dependencies]
anyhow = "1.0"
log = "0.4"
env_logger = "0.11"

View File

@ -0,0 +1,9 @@
//! nyash-next: Next-generation Nyash crate skeleton.
//!
//! This crate is intentionally minimal to keep the legacy `src/` untouched.
//! Start new modules here while preserving the existing `nyash-rust` crate.
pub fn version() -> &'static str {
"0.1.0-dev"
}

View File

@ -0,0 +1,5 @@
fn main() {
env_logger::init();
println!("nyash-next: workspace skeleton is ready.");
}

View File

@ -952,8 +952,8 @@ pub extern "C" fn nyash_future_spawn_instance3_i64(
if let Some(obj) = nyash_rust::jit::rt::handles::get(a1 as u64) {
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
if p.box_type == "StringBox" {
let host = nyash_rust::runtime::get_global_plugin_host();
if let Ok(hg) = host.read() {
// Limit the lifetime of the read guard to this inner block by avoiding an outer binding
if let Ok(hg) = nyash_rust::runtime::get_global_plugin_host().read() {
if let Ok(Some(sb)) = hg.invoke_instance_method("StringBox", "toUtf8", p.instance_id(), &[]) {
if let Some(s) = sb.as_any().downcast_ref::<StringBox>() { method_name = Some(s.value.clone()); }
}
@ -1509,6 +1509,248 @@ pub extern "C" fn nyash_array_length_h(handle: i64) -> i64 {
0
}
// --- AOT ObjectModule dotted-name aliases (Array) ---
// Provide dotted symbol names expected by ObjectBuilder lowering, forwarding to existing underscored exports.
#[export_name = "nyash.array.get_h"]
pub extern "C" fn nyash_array_get_h_alias(handle: i64, idx: i64) -> i64 { nyash_array_get_h(handle, idx) }
#[export_name = "nyash.array.set_h"]
pub extern "C" fn nyash_array_set_h_alias(handle: i64, idx: i64, val: i64) -> i64 { nyash_array_set_h(handle, idx, val) }
#[export_name = "nyash.array.push_h"]
pub extern "C" fn nyash_array_push_h_alias(handle: i64, val: i64) -> i64 { nyash_array_push_h(handle, val) }
#[export_name = "nyash.array.len_h"]
pub extern "C" fn nyash_array_len_h_alias(handle: i64) -> i64 { nyash_array_length_h(handle) }
// --- AOT ObjectModule dotted-name exports (Map) ---
// Provide dotted symbol names expected by ObjectBuilder lowering for MapBox operations.
// size: (handle) -> i64
#[export_name = "nyash.map.size_h"]
pub extern "C" fn nyash_map_size_h(handle: i64) -> i64 {
use nyash_rust::jit::rt::handles;
if handle <= 0 { return 0; }
if let Some(obj) = handles::get(handle as u64) {
if let Some(map) = obj.as_any().downcast_ref::<nyash_rust::boxes::map_box::MapBox>() {
if let Some(ib) = map.size().as_any().downcast_ref::<nyash_rust::box_trait::IntegerBox>() {
return ib.value;
}
}
}
0
}
// get_h: (map_handle, key_i64) -> value_handle
#[export_name = "nyash.map.get_h"]
pub extern "C" fn nyash_map_get_h(handle: i64, key: i64) -> i64 {
use nyash_rust::{jit::rt::handles, box_trait::{NyashBox, IntegerBox}};
if handle <= 0 { return 0; }
if let Some(obj) = handles::get(handle as u64) {
if let Some(map) = obj.as_any().downcast_ref::<nyash_rust::boxes::map_box::MapBox>() {
let kbox: Box<dyn NyashBox> = Box::new(IntegerBox::new(key));
let v = map.get(kbox);
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::from(v);
let h = handles::to_handle(arc);
return h as i64;
}
}
0
}
// get_hh: (map_handle, key_handle) -> value_handle
#[export_name = "nyash.map.get_hh"]
pub extern "C" fn nyash_map_get_hh(handle: i64, key_h: i64) -> i64 {
use nyash_rust::{jit::rt::handles, box_trait::NyashBox};
if handle <= 0 || key_h <= 0 { return 0; }
if let (Some(obj), Some(key)) = (handles::get(handle as u64), handles::get(key_h as u64)) {
if let Some(map) = obj.as_any().downcast_ref::<nyash_rust::boxes::map_box::MapBox>() {
let v = map.get(key.clone_box());
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::from(v);
let h = handles::to_handle(arc);
return h as i64;
}
}
0
}
// set_h: (map_handle, key_i64, val) -> i64 (ignored/0)
#[export_name = "nyash.map.set_h"]
pub extern "C" fn nyash_map_set_h(handle: i64, key: i64, val: i64) -> i64 {
use nyash_rust::{jit::rt::handles, box_trait::{NyashBox, IntegerBox}};
if handle <= 0 { return 0; }
if let Some(obj) = handles::get(handle as u64) {
if let Some(map) = obj.as_any().downcast_ref::<nyash_rust::boxes::map_box::MapBox>() {
let kbox: Box<dyn NyashBox> = Box::new(IntegerBox::new(key));
let vbox: Box<dyn NyashBox> = if val > 0 {
if let Some(o) = handles::get(val as u64) { o.clone_box() } else { Box::new(IntegerBox::new(val)) }
} else { Box::new(IntegerBox::new(val)) };
let _ = map.set(kbox, vbox);
return 0;
}
}
0
}
// has_h: (map_handle, key_i64) -> i64 (0/1)
#[export_name = "nyash.map.has_h"]
pub extern "C" fn nyash_map_has_h(handle: i64, key: i64) -> i64 {
use nyash_rust::{jit::rt::handles, box_trait::IntegerBox};
if handle <= 0 { return 0; }
if let Some(obj) = handles::get(handle as u64) {
if let Some(map) = obj.as_any().downcast_ref::<nyash_rust::boxes::map_box::MapBox>() {
let kbox = Box::new(IntegerBox::new(key));
let v = map.get(kbox);
// Consider present if not VoidBox
let present = !v.as_any().is::<nyash_rust::box_trait::VoidBox>();
return if present { 1 } else { 0 };
}
}
0
}
// --- AOT ObjectModule dotted-name exports (String/Any helpers) ---
// String.len_h(handle) -> i64
#[export_name = "nyash.string.len_h"]
pub extern "C" fn nyash_string_len_h(handle: i64) -> i64 {
use nyash_rust::jit::rt::handles;
if std::env::var("NYASH_JIT_TRACE_LEN").ok().as_deref() == Some("1") {
let present = if handle > 0 { handles::get(handle as u64).is_some() } else { false };
eprintln!("[AOT-LEN_H] string.len_h handle={} present={}", handle, present);
}
if handle <= 0 { return 0; }
if let Some(obj) = handles::get(handle as u64) {
if let Some(sb) = obj.as_any().downcast_ref::<nyash_rust::box_trait::StringBox>() {
return sb.value.len() as i64;
}
}
0
}
// String.charCodeAt_h(handle, idx) -> i64 (byte-based; -1 if OOB)
#[export_name = "nyash.string.charCodeAt_h"]
pub extern "C" fn nyash_string_charcode_at_h_export(handle: i64, idx: i64) -> i64 {
use nyash_rust::jit::rt::handles;
if idx < 0 { return -1; }
if handle <= 0 { return -1; }
if let Some(obj) = handles::get(handle as u64) {
if let Some(sb) = obj.as_any().downcast_ref::<nyash_rust::box_trait::StringBox>() {
let s = &sb.value;
let i = idx as usize;
if i < s.len() { return s.as_bytes()[i] as i64; }
return -1;
}
}
-1
}
// String.concat_hh(lhs_h, rhs_h) -> handle
#[export_name = "nyash.string.concat_hh"]
pub extern "C" fn nyash_string_concat_hh_export(a_h: i64, b_h: i64) -> i64 {
use nyash_rust::{jit::rt::handles, box_trait::{NyashBox, StringBox}};
let to_s = |h: i64| -> String {
if h > 0 { if let Some(o) = handles::get(h as u64) { return o.to_string_box().value; } }
String::new()
};
let s = format!("{}{}", to_s(a_h), to_s(b_h));
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::new(StringBox::new(s));
handles::to_handle(arc) as i64
}
// String.eq_hh(lhs_h, rhs_h) -> i64 (0/1)
#[export_name = "nyash.string.eq_hh"]
pub extern "C" fn nyash_string_eq_hh_export(a_h: i64, b_h: i64) -> i64 {
use nyash_rust::jit::rt::handles;
let to_s = |h: i64| -> String { if h > 0 { if let Some(o) = handles::get(h as u64) { return o.to_string_box().value; } } String::new() };
if to_s(a_h) == to_s(b_h) { 1 } else { 0 }
}
// String.lt_hh(lhs_h, rhs_h) -> i64 (0/1)
#[export_name = "nyash.string.lt_hh"]
pub extern "C" fn nyash_string_lt_hh_export(a_h: i64, b_h: i64) -> i64 {
use nyash_rust::jit::rt::handles;
let to_s = |h: i64| -> String { if h > 0 { if let Some(o) = handles::get(h as u64) { return o.to_string_box().value; } } String::new() };
if to_s(a_h) < to_s(b_h) { 1 } else { 0 }
}
// Any.length_h(handle) -> i64 (Array/String/Map)
#[export_name = "nyash.any.length_h"]
pub extern "C" fn nyash_any_length_h_export(handle: i64) -> i64 {
use nyash_rust::jit::rt::handles;
if std::env::var("NYASH_JIT_TRACE_LEN").ok().as_deref() == Some("1") {
let present = if handle > 0 { handles::get(handle as u64).is_some() } else { false };
eprintln!("[AOT-LEN_H] any.length_h handle={} present={}", handle, present);
}
if handle <= 0 { return 0; }
if let Some(obj) = handles::get(handle as u64) {
if let Some(arr) = obj.as_any().downcast_ref::<nyash_rust::boxes::array::ArrayBox>() {
if let Some(ib) = arr.length().as_any().downcast_ref::<nyash_rust::box_trait::IntegerBox>() { return ib.value; }
}
if let Some(sb) = obj.as_any().downcast_ref::<nyash_rust::box_trait::StringBox>() {
return sb.value.len() as i64;
}
if let Some(map) = obj.as_any().downcast_ref::<nyash_rust::boxes::map_box::MapBox>() {
if let Some(ib) = map.size().as_any().downcast_ref::<nyash_rust::box_trait::IntegerBox>() { return ib.value; }
}
}
0
}
// Any.is_empty_h(handle) -> i64 (0/1)
#[export_name = "nyash.any.is_empty_h"]
pub extern "C" fn nyash_any_is_empty_h_export(handle: i64) -> i64 {
use nyash_rust::jit::rt::handles;
if handle <= 0 { return 1; }
if let Some(obj) = handles::get(handle as u64) {
if let Some(arr) = obj.as_any().downcast_ref::<nyash_rust::boxes::array::ArrayBox>() {
if let Ok(items) = arr.items.read() { return if items.is_empty() { 1 } else { 0 }; }
}
if let Some(sb) = obj.as_any().downcast_ref::<nyash_rust::box_trait::StringBox>() {
return if sb.value.is_empty() { 1 } else { 0 };
}
if let Some(map) = obj.as_any().downcast_ref::<nyash_rust::boxes::map_box::MapBox>() {
if let Some(ib) = map.size().as_any().downcast_ref::<nyash_rust::box_trait::IntegerBox>() { return if ib.value == 0 { 1 } else { 0 }; }
}
}
1
}
// Instance birth by name (packed u64x2 + len) -> handle
// export: nyash.instance.birth_name_u64x2(lo, hi, len) -> i64
#[export_name = "nyash.instance.birth_name_u64x2"]
pub extern "C" fn nyash_instance_birth_name_u64x2_export(lo: i64, hi: i64, len: i64) -> i64 {
use nyash_rust::runtime::get_global_plugin_host;
let mut bytes = Vec::with_capacity(len.max(0) as usize);
let lo_u = lo as u64; let hi_u = hi as u64; let l = len.max(0) as usize; let take = core::cmp::min(16, l);
for i in 0..take.min(8) { bytes.push(((lo_u >> (8 * i)) & 0xff) as u8); }
for i in 0..take.saturating_sub(8) { bytes.push(((hi_u >> (8 * i)) & 0xff) as u8); }
// If len > 16, remaining bytes are not represented in (lo,hi); assume names <=16 bytes for now.
if bytes.len() != l { bytes.resize(l, 0); }
let name = String::from_utf8_lossy(&bytes).to_string();
if let Ok(host_g) = get_global_plugin_host().read() {
if let Ok(b) = host_g.create_box(&name, &[]) {
let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox> = std::sync::Arc::from(b);
let h = nyash_rust::jit::rt::handles::to_handle(arc);
return h as i64;
}
}
0
}
// Construct StringBox from two u64 words (little-endian) + length (<=16) and return handle
// export: nyash.string.from_u64x2(lo, hi, len) -> i64
#[export_name = "nyash.string.from_u64x2"]
pub extern "C" fn nyash_string_from_u64x2_export(lo: i64, hi: i64, len: i64) -> i64 {
use nyash_rust::{box_trait::{NyashBox, StringBox}, jit::rt::handles};
let l = if len < 0 { 0 } else { core::cmp::min(len as usize, 16) };
let mut bytes: Vec<u8> = Vec::with_capacity(l);
let lo_u = lo as u64; let hi_u = hi as u64;
for i in 0..l.min(8) { bytes.push(((lo_u >> (8 * i)) & 0xff) as u8); }
for i in 0..l.saturating_sub(8) { bytes.push(((hi_u >> (8 * i)) & 0xff) as u8); }
let s = String::from_utf8_lossy(&bytes).to_string();
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::new(StringBox::new(s));
handles::to_handle(arc) as i64
}
// Convert a VM argument (param index or existing handle) into a runtime handle
// Exported as: nyash.handle.of
#[export_name = "nyash.handle.of"]

14
dev/README.md Normal file
View File

@ -0,0 +1,14 @@
Nyash Dev Areas
This folder contains isolated development workspaces that do not interfere with the main source tree. Use these for experiments and focused bringup.
Areas
- selfhosting/: JIT selfhosting pipeline experiments (Ny → MIR → MIRInterp → VM/JIT). Includes quickstart notes and scripts references.
- cranelift/: Cranelift JIT/AOT bringup and AOT link experiments; smokes and env toggles.
Notes
- Keep experiments and artifacts inside each subfolder. Avoid modifying the core `src/` unless changes are ready to graduate.
- Prefer scripts under `tools/` and add thin wrappers here if needed.

26
dev/cranelift/README.md Normal file
View File

@ -0,0 +1,26 @@
Cranelift JIT/AOT Dev
Focus: Cranelift JITdirect and AOT (object emit + link with `libnyrt.a`).
Quick AOT Smoke
- Build core (once):
- `cargo build --release --features cranelift-jit`
- Lower to object + link with NyRT:
- `NYASH_DISABLE_PLUGINS=1 tools/aot_smoke_cranelift.sh apps/smokes/jit_aot_string_min.nyash app_str`
- Run app:
- `./app_str`
Useful env toggles
- `NYASH_JIT_DUMP=1`: show JIT lowering summary
- `NYASH_JIT_TRACE_LOCAL=1`: trace local slot loads/stores
- `NYASH_JIT_TRACE_RET=1`: trace return path
- `NYASH_JIT_TRACE_LEN=1`: trace string/any len thunks
- `NYASH_JIT_DISABLE_LEN_CONST=1`: disable early constfold for String.length
Notes
- For AOT linking: requires `libnyrt.a` from `crates/nyrt` (built by `cargo build --release`).
- Use `target/aot_objects/` as scratch; keep perexperiment subfolders if needed.

31
dev/selfhosting/README.md Normal file
View File

@ -0,0 +1,31 @@
SelfHosting Dev (JIT / VM)
Focus: Ny → MIR → MIRInterp → VM/JIT quick loops to validate semantics and bootstrap paths.
Quickstart
- Core build (JIT):
- `cargo build --release --features cranelift-jit`
- Core smokes (plugins disabled):
- `NYASH_CLI_VERBOSE=1 ./tools/jit_smoke.sh`
- Roundtrip (parser pipe + json):
- `./tools/ny_roundtrip_smoke.sh`
- Plugins smoke (optional gate):
- `NYASH_SKIP_TOML_ENV=1 ./tools/smoke_plugins.sh`
- Using/Resolver E2E sample (optional):
- `./tools/using_e2e_smoke.sh` (requires `--enable-using`)
- Bootstrap c0→c1→c1' (optional):
- `./tools/bootstrap_selfhost_smoke.sh`
Flags
- `NYASH_DISABLE_PLUGINS=1`: stabilize core path
- `NYASH_LOAD_NY_PLUGINS=1`: enable nyash.toml ny_plugins
- `NYASH_ENABLE_USING=1`: using/namespace enable
- `NYASH_SKIP_TOML_ENV=1`: suppress [env] mapping in nyash.toml
Tips
- For debug, set `NYASH_CLI_VERBOSE=1`.
- Keep temp artifacts under this folder (`dev/selfhosting/_tmp/`) to avoid polluting repo root.

View File

@ -2,5 +2,6 @@
このファイルは移動しました。最新の現在タスクは次を参照してください。
- 新しい場所: [development/current/CURRENT_TASK.md](development/current/CURRENT_TASK.md)
- 新しい場所: [リポジトリ直下の CURRENT_TASK.md](../CURRENT_TASK.md)
補足: Phase 15 以降はルートの `CURRENT_TASK.md` が正本です。`docs/development/current/` 配下の旧ファイルは参照しないでください。

View File

@ -1,7 +1,7 @@
# 📚 Nyash Documentation
## 🚀 はじめに
- **現在のタスク**: [development/current/CURRENT_TASK.md](development/current/CURRENT_TASK.md)
- **現在のタスク**: [../CURRENT_TASK.md](../CURRENT_TASK.md)
- **コア概念の速習**: [reference/architecture/nyash_core_concepts.md](reference/architecture/nyash_core_concepts.md)
---
@ -56,7 +56,7 @@
- [CLIオプション早見表](tools/cli-options.md)
### 開発状況
- [現在のタスク](development/current/CURRENT_TASK.md)
- [現在のタスク](../CURRENT_TASK.md)
- [開発ロードマップ](development/roadmap/)
- [Phase別計画](development/roadmap/phases/)
- 🔥 **[Phase 12: TypeBox統合ABI](development/roadmap/phases/phase-12/)** - プラグイン革命!

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@
## 📋 現在進行中
### 🎯 最重要タスク
- **現在のタスク**: [development/current/CURRENT_TASK.md](../current/CURRENT_TASK.md)
- **現在のタスク**: [../../CURRENT_TASK.md](../../CURRENT_TASK.md)
- **Phase 8.3**: Box操作WASM実装Copilot担当
- **Phase 8.4**: ネイティブコンパイル実装計画AI大会議策定済み

View File

@ -5,7 +5,7 @@
利用者向けの具体的なビルド手順は guides/ 以下の各ガイドを参照。
## 📋 重要リンク
- **現在のタスク**: [development/current/CURRENT_TASK.md](../../current/CURRENT_TASK.md)
- **現在のタスク**: [../../../CURRENT_TASK.md](../../../CURRENT_TASK.md)
- **コア概念(速習)**: [reference/architecture/nyash_core_concepts.md](../../reference/architecture/nyash_core_concepts.md)
- **🤖 AI大会議記録**: [../ai_conference_native_compilation_20250814.md](../ai_conference_native_compilation_20250814.md)
- **🗺️ ネイティブコンパイル戦略**: [../native-compilation-roadmap.md](../native-compilation-roadmap.md)

View File

@ -0,0 +1,29 @@
# Phase 15 — SelfHosting Doc Index
このインデックスは Phase 15セルフホスティングの計画・実装ドキュメントへの入口を1箇所にまとめます。状況に応じて随時更新します正本
## 要点(すぐ見る)
- 現在タスク(正本): ../../../../CURRENT_TASK.md
- 概要と目的: README.md
- 実行計画(常時更新のチェックリスト): ROADMAP.md
- 推奨シーケンス(手順書): recommended-sequence.txt
- 詳細計画(長文): self-hosting-plan.txt
- lld戦略AOT/リンク統合): self-hosting-lld-strategy.md
## 設計とインターフェース
- Cranelift AOT 設計: ../../../backend-cranelift-aot-design.md
- Boxインターフェース案Cranelift: ../../../../interfaces/cranelift-aot-box.md
- LinkerBox 仕様案: ../../../../interfaces/linker-box.md
## ツール・スモーク
- AOTスモーク雛形: tools/aot_smoke_cranelift.sh / .ps1
- JITスモーク: tools/jit_smoke.sh
- ラウンドトリップ: tools/ny_roundtrip_smoke.sh
- using/namespace E2E: tools/using_e2e_smoke.sh
## 運用メモ/引き継ぎ
- ハンドオフ: ../../handoff/phase-15-handoff.md
注意:
- Phase 15関連の分散した文書は本インデックスから辿れるよう整理しています。新規文書を追加した場合は必ずここに追記してください。

View File

@ -0,0 +1,78 @@
# Phase 15 — Box Stacking Roadmap (Living)
This roadmap is a living checklist to advance Phase 15 with small, safe boxes. Update continuously as we progress.
## Now (ready/green)
- [x] v0 Ny parser (Ny→JSON IR v0) with wrappers (Unix/Windows)
- [x] Runner JSON v0 bridge (`--ny-parser-pipe`) → MIR → MIR-Interp
- [x] E2E + roundtrip practical recipes (Windows/Unix)
- [x] Docs path unify (phase-15 under roadmap tree)
- [x] Direct bridge (design + skeleton; feature-gated)
- [x] AOT P2 stubs (CraneliftAotBox/LinkerBox) + RUN smoke wiring
- [x] JITonly baseline stabilized (core smokes green; plugins optional)
- [x] Roundtrip (Case A/B) aligned; Case A reenabled via parser pipe
- [x] using/namespace (gated) + nyash.link minimal resolver
- [x] NyModules + ny_plugins regression suite (Windows path normalization/namespace derivation)
- [x] Standard Ny scripts scaffolds added (string/array/map P0) + examples + jit_smoke
## Next (small boxes)
1) Standard Ny std impl (P0→実体化)
- Implement P0 methods for string/array/map in Nyash (keep NyRT primitives minimal)
- Enable via `nyash.toml` `[ny_plugins]` (optin); extend `tools/jit_smoke.sh`
2) Ny compiler MVP (Ny→MIR on JIT path)
- Ny tokenizer + recursivedescent parser (current subset) in Ny; drive existing MIR builder
- Flag path: `NYASH_USE_NY_COMPILER=1` to switch rust→ny compiler; rust parser as fallback
- Add apps/selfhost-compiler/ and minimal smokes
3) Bootstrap loop (c0→c1→c1)
- Use existing trace/hash harness to compare parity; add optional CI gate
4) Plugins CI split (継続)
- Core alwayson (JIT, plugins disabled); Plugins as optional job (strict off by default)
## Later (incremental)
- v1 Ny parser (let/if/call) behind `NYASH_JSON_IR_VERSION=1`
- JSON v1 bridge → MirBuilder (back-compat v0)
- 12.7 sugars normalized patterns in bridge (?. / ?? / range)
- E2E CI-lite matrix (no LLVM) for v0/v1/bridge roundtrip
- Ny script plugin examples under `apps/plugins-scripts/`
- Expand std Ny impl (String P1: trim/split/startsWith/endsWith; Array P1: map/each/filter; Map P1: values/entries/forEach)
- using/nyash.link E2E samples under `apps/` (small project template)
- Tighten Plugins job: migrate samples to Core13; reenable strict diagnostics
## Operational switches
- Parser path: `--parser {rust|ny}` or `NYASH_USE_NY_PARSER=1`
- JSON dump: `NYASH_DUMP_JSON_IR=1`
- Load Ny plugins: `NYASH_LOAD_NY_PLUGINS=1` / `--load-ny-plugins`
- AOT smoke: `CLIF_SMOKE_RUN=1`
## Recipes / Smokes
- JSON v0 bridge: `tools/ny_parser_bridge_smoke.sh` / `tools/ny_parser_bridge_smoke.ps1`
- E2E roundtrip: `tools/ny_roundtrip_smoke.sh` / `tools/ny_roundtrip_smoke.ps1`
## Stop criteria (Phase 15)
- v0 E2E green (parser pipe + direct bridge) including Ny compiler MVP switch
- v1 minimal samples pass via JSON bridge
- AOT P2: emit→link→run stable for constant/arith
- Docs/recipes usable on Windows/Unix
## Notes
- JSON is a temporary, safe boundary. We will keep it for observability even after the in-proc bridge is default.
- Favor smallest viable steps; do not couple large refactors with new features.
## Ny Plugins → Namespace (Plan)
- Phase A (minimal): Add a shared `NyModules` registry (env.modules.{set,get}).
- Map file path → namespace (projectrelative, separators → `.`, trim extension).
- R5 hook: if a Ny plugin returns an exports map/static box, register it under the derived namespace.
- Guard: reject reserved prefixes (e.g., `nyashstd.*`, `system.*`).
- Phase B (scope): Optionally run `[ny_plugins]` in a shared Interpreter to share static definitions.
- Flag: `NYASH_NY_PLUGINS_SHARED=0` to keep isolated execution.
- Logs: `[ny_plugins] <ns>: REGISTERED | FAIL(reason)`.
- Phase C (language bridge): Resolve `using foo.bar` via `NyModules`, then fallback to file/package resolver (nyash.link).
- Keep IDEfriendly fully qualified access; integrate with future `nyash_modules/`.

View File

@ -6,10 +6,14 @@
```
papers/
├── README.md # このファイル
├── README.md # このファイル(全候補への索引)
├── active/ # 現在執筆中の論文
│ ├── paper-a-mir13-ir-design/ # 論文A: MIR13命令とIR設計
── paper-b-nyash-execution-model/ # 論文B: Nyash言語と実行モデル
── paper-b-nyash-execution-model/ # 論文B: Nyash言語と実行モデル
│ ├── paper-c-ancp-compression/ # 論文C: ANCP 90%圧縮技法(世界記録)
│ ├── paper-d-jit-to-exe/ # 論文D: JIT→EXE統合パイプライン
│ ├── three-papers-strategy.md # 3論文戦略の統合計画
│ └── WHICH_PAPER_FIRST.md # 論文優先順位の検討15個候補
├── archive/ # 過去の検討・下書き
│ ├── initial-proposals/ # 初期提案資料
│ ├── mir15-implementation/ # 旧MIR15論文
@ -20,7 +24,7 @@ papers/
└── templates/ # 論文テンプレート
```
## 📊 現在の論文プロジェクト(2本立て戦略
## 📊 現在の論文プロジェクト(主要2本 + 追加候補多数
### 論文A: MIR13命令とIR設計 🎯
**主題**: 中間表現MIRの統合設計
@ -60,6 +64,25 @@ papers/
## 🔗 関連ドキュメント
### 📝 論文候補への索引15個以上
- **[15個の論文候補一覧](active/WHICH_PAPER_FIRST.md)** - すべての候補リスト
- **[3論文戦略](active/three-papers-strategy.md)** - 段階的発表計画
- **[Paper A: MIR13](active/paper-a-mir13-ir-design/)** - 13命令IR設計
- **[Paper B: Nyash](active/paper-b-nyash-execution-model/)** - 言語実行モデル
- **[Paper C: ANCP](active/paper-c-ancp-compression/)** - 90%圧縮技法
- **[Paper D: JIT-EXE](active/paper-d-jit-to-exe/)** - 統合パイプライン
### 🎯 他の論文アイデア所在地
- **[研究フォルダ](../research/)** - Box理論JIT、1ヶ月実装記録など5個以上
- **[アイデアフォルダ](../ideas/)** - 新規提案候補
- **[AI相談記録](../../sessions/)** - WebBox革命、AI協働方法論など
### 📊 執筆支援ドキュメント
- [論文執筆戦略](active/PAPER_WRITING_STRATEGY.md)
- [論文分割戦略](active/PAPER_DIVISION_STRATEGY.md)
- [ベンチマークアプリ推奨](active/BENCHMARK_APP_RECOMMENDATIONS.md)
### 🔧 開発関連
- [開発ロードマップ](../development/roadmap/)
- [技術仕様](../reference/)
- [現在のタスク](../development/current/CURRENT_TASK.md)
- [現在のタスク](../../CURRENT_TASK.md)

View File

@ -0,0 +1,32 @@
#!/usr/bin/env bash
set -euo pipefail
OUT_DIR=$(cd "$(dirname "$0")" && pwd)
OUT_FILE="$OUT_DIR/ENVIRONMENT.txt"
{
echo "== Datetime =="
date -Iseconds || date
echo
echo "== OS =="
uname -a || true
lsb_release -a 2>/dev/null || true
sw_vers 2>/dev/null || true
systeminfo 2>/dev/null | head -n 30 || true
echo
echo "== CPU =="
lscpu 2>/dev/null || sysctl -a 2>/dev/null | grep machdep.cpu || true
echo
echo "== Rust toolchain =="
rustc --version 2>/dev/null || true
cargo --version 2>/dev/null || true
echo
echo "== Git =="
git rev-parse HEAD 2>/dev/null || true
echo
echo "== Cranelift/JIT features =="
rg -n "cranelift|jit" -S ../../../../ -g '!target' 2>/dev/null || true
} > "$OUT_FILE"
echo "[DONE] Wrote $OUT_FILE"

View File

@ -0,0 +1,25 @@
This folder contains reproducibility artifacts for Paper A (MIR13 IR design).
Files
- `COLLECT_ENV.sh`: Captures host OS/CPU/toolchain/git info into `ENVIRONMENT.txt`.
- `RUN_BENCHMARKS.sh`: Runs interpreter/VM/JIT/AOT (if available) against sample benchmarks and writes CSVs to `results/`.
- `results/`: Output CSVs (per benchmark and per mode). Merge/plot as needed.
Usage
1) Capture environment
./COLLECT_ENV.sh
2) Build (full)
cargo build --release --features cranelift-jit
3) Run benchmarks
./RUN_BENCHMARKS.sh
Variables:
- NYASH_BIN: Path to nyash binary (default: target/release/nyash)
- USE_EXE_ONLY=1: Only measure AOT executables (skips interp/vm/jit)
Notes
- AOT requires `tools/build_aot.sh`. If missing, AOT is skipped.
- If `hyperfine` is not installed, a simple timing fallback is used.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,169 @@
#!/usr/bin/env bash
set -euo pipefail
# Reproducible benchmarks for MIR13 paper (Interpreter/VM/JIT/AOT if available)
# Outputs CSVs under _artifacts/results/
if ROOT_DIR=$(git -C "$(dirname "$0")" rev-parse --show-toplevel 2>/dev/null); then
ROOT_DIR="$ROOT_DIR/nyash"
[[ -d "$ROOT_DIR" ]] || ROOT_DIR=$(git rev-parse --show-toplevel)
else
# Fallback: ascend to repo root from _artifacts
ROOT_DIR=$(cd "$(dirname "$0")/../../../../.." && pwd)
fi
ART_DIR=$(cd "$(dirname "$0")" && pwd)
RES_DIR="$ART_DIR/results"
mkdir -p "$RES_DIR"
NYASH_BIN=${NYASH_BIN:-"$ROOT_DIR/target/release/nyash"}
SKIP_INTERP=${SKIP_INTERP:-0} # 1: skip interpreter遅い環境向け
USE_LLVM_AOT=${USE_LLVM_AOT:-0} # 1: LLVM backendでAOTも計測
USE_EXE_ONLY=${USE_EXE_ONLY:-0} # 1: measure AOT exe only
HYPERFINE=$(command -v hyperfine || true)
BENCH_DIR="$ROOT_DIR/benchmarks"
FILES=(
"$BENCH_DIR/bench_light.nyash"
"$BENCH_DIR/bench_medium.nyash"
"$BENCH_DIR/bench_heavy.nyash"
"$BENCH_DIR/bench_aot_len_light.nyash"
"$BENCH_DIR/bench_aot_len_medium.nyash"
"$BENCH_DIR/bench_aot_len_heavy.nyash"
"$ROOT_DIR/examples/aot_min_string_len.nyash"
)
echo "[INFO] NYASH_BIN=$NYASH_BIN"
echo "[INFO] USE_EXE_ONLY=$USE_EXE_ONLY (1=EXE only)"
echo "[INFO] hyperfine=${HYPERFINE:-not found}"
echo "[INFO] USE_LLVM_AOT=$USE_LLVM_AOT (1=measure LLVM AOT)"
if [[ ! -x "$NYASH_BIN" && "$USE_EXE_ONLY" -eq 0 ]]; then
echo "[INFO] Building nyash (release, with JIT feature)"
(cd "$ROOT_DIR" && cargo build --release --features cranelift-jit)
fi
have_build_aot=0
if [[ -x "$ROOT_DIR/tools/build_aot.sh" ]]; then
have_build_aot=1
fi
have_build_llvm=0
if [[ -x "$ROOT_DIR/tools/build_llvm.sh" ]]; then
have_build_llvm=1
fi
run_cmd() {
local cmd="$1" label="$2" csv="$3"
if [[ -n "$HYPERFINE" ]]; then
# 10 runs, warmup 2, export CSV append
$HYPERFINE -w 2 -r 10 --export-csv "$csv" --show-output --min-runs 10 "$cmd"
else
# Simple fallback: run 10 times and record naive timing (ms)
: > "$csv"
for i in {1..10}; do
local t0=$(python3 - <<<'import time; print(int(time.time()*1000))')
bash -lc "$cmd" >/dev/null 2>&1 || true
local t1=$(python3 - <<<'import time; print(int(time.time()*1000))')
echo "$label,$((t1-t0))" >> "$csv"
done
fi
}
# Measure modes
for f in "${FILES[@]}"; do
[[ -f "$f" ]] || { echo "[WARN] Skip missing $f"; continue; }
base=$(basename "$f" .nyash)
if [[ "$USE_EXE_ONLY" -eq 0 ]]; then
# Interpreter
if [[ "$SKIP_INTERP" -eq 0 ]]; then
run_cmd "$NYASH_BIN $f" "interp-$base" "$RES_DIR/${base}_interp.csv"
else
echo "[INFO] SKIP_INTERP=1: skipping interpreter for $f"
fi
# VM
run_cmd "$NYASH_BIN --backend vm $f" "vm-$base" "$RES_DIR/${base}_vm.csv"
# JIT (VM + JIT execute)
run_cmd "NYASH_JIT_EXEC=1 $NYASH_BIN --backend vm $f" "jit-$base" "$RES_DIR/${base}_jit.csv"
fi
# AOT (if tool available)
if [[ $have_build_aot -eq 1 ]]; then
out="/tmp/ny_${base}_aot"
bash "$ROOT_DIR/tools/build_aot.sh" "$f" -o "$out" >/dev/null 2>&1 || true
if [[ -x "$out" ]]; then
run_cmd "$out" "aot-$base" "$RES_DIR/${base}_aot.csv"
rm -f "$out"
else
echo "[WARN] AOT build failed for $f"
fi
else
echo "[INFO] AOT tool not found; skipping AOT for $f"
fi
done
# LLVM AOT-only targets (optional)
if [[ "$USE_LLVM_AOT" -eq 1 ]]; then
if [[ $have_build_llvm -eq 0 ]]; then
echo "[WARN] tools/build_llvm.sh not found; skipping LLVM AOT"
elif ! command -v llvm-config-18 >/dev/null 2>&1; then
echo "[WARN] llvm-config-18 not found; skipping LLVM AOT"
else
LLVM_FILES=(
"$ROOT_DIR/apps/tests/ny-llvm-smoke/main.nyash"
)
for f in "${LLVM_FILES[@]}"; do
[[ -f "$f" ]] || { echo "[WARN] Skip missing LLVM target $f"; continue; }
base=$(basename "$f" .nyash)
out="/tmp/ny_${base}_llvm"
# Build via LLVM backend
LLVM_SYS_180_PREFIX=$(llvm-config-18 --prefix) \
LLVM_SYS_181_PREFIX=$(llvm-config-18 --prefix) \
"$ROOT_DIR/tools/build_llvm.sh" "$f" -o "$out" >/dev/null 2>&1 || true
if [[ -x "$out" ]]; then
run_cmd "$out" "llvm-aot-$base" "$RES_DIR/${base}_llvm_aot.csv"
rm -f "$out"
else
echo "[WARN] LLVM AOT build failed for $f"
fi
done
fi
fi
# JIT-AOT (Cranelift) via --jit-direct (optional)
USE_JIT_AOT=${USE_JIT_AOT:-0}
echo "[INFO] USE_JIT_AOT=$USE_JIT_AOT (1=measure JIT AOT via jit-direct)"
if [[ "$USE_JIT_AOT" -eq 1 ]]; then
echo "[JIT-AOT] Building nyash + nyrt ..."
(cd "$ROOT_DIR" && cargo build --release --features cranelift-jit >/dev/null)
(cd "$ROOT_DIR/crates/nyrt" && cargo build --release >/dev/null)
JIT_AOT_FILES=(
"$ROOT_DIR/apps/examples/array_p0.nyash"
)
for f in "${JIT_AOT_FILES[@]}"; do
[[ -f "$f" ]] || { echo "[WARN] Skip missing JIT-AOT target $f"; continue; }
base=$(basename "$f" .nyash)
objdir="$ROOT_DIR/target/aot_objects"
rm -rf "$objdir" && mkdir -p "$objdir"
# Emit object via JIT-direct (relaxed)
NYASH_JIT_EVENTS=1 NYASH_AOT_OBJECT_OUT="$objdir/main.o" "$NYASH_BIN" --jit-direct "$f" >/dev/null || true
if [[ -f "$objdir/main.o" ]]; then
out="/tmp/ny_${base}_jit_aot"
cc "$objdir/main.o" \
-L "$ROOT_DIR/target/release" \
-Wl,--whole-archive -lnyrt -Wl,--no-whole-archive \
-lpthread -ldl -lm -o "$out"
if [[ -x "$out" ]]; then
run_cmd "$out" "jit-aot-$base" "$RES_DIR/${base}_jit_aot.csv"
rm -f "$out"
else
echo "[WARN] link failed for JIT-AOT target $f"
fi
else
echo "[WARN] JIT AOT object not generated for $f"
fi
done
fi
echo "[DONE] Results in $RES_DIR"

View File

@ -0,0 +1,10 @@
jit-aot_min_string_len,159
jit-aot_min_string_len,101
jit-aot_min_string_len,19
jit-aot_min_string_len,18
jit-aot_min_string_len,18
jit-aot_min_string_len,18
jit-aot_min_string_len,19
jit-aot_min_string_len,150
jit-aot_min_string_len,150
jit-aot_min_string_len,149
1 jit-aot_min_string_len 159
2 jit-aot_min_string_len 101
3 jit-aot_min_string_len 19
4 jit-aot_min_string_len 18
5 jit-aot_min_string_len 18
6 jit-aot_min_string_len 18
7 jit-aot_min_string_len 19
8 jit-aot_min_string_len 150
9 jit-aot_min_string_len 150
10 jit-aot_min_string_len 149

View File

@ -0,0 +1,10 @@
vm-aot_min_string_len,158
vm-aot_min_string_len,159
vm-aot_min_string_len,159
vm-aot_min_string_len,157
vm-aot_min_string_len,160
vm-aot_min_string_len,158
vm-aot_min_string_len,157
vm-aot_min_string_len,158
vm-aot_min_string_len,165
vm-aot_min_string_len,161
1 vm-aot_min_string_len 158
2 vm-aot_min_string_len 159
3 vm-aot_min_string_len 159
4 vm-aot_min_string_len 157
5 vm-aot_min_string_len 160
6 vm-aot_min_string_len 158
7 vm-aot_min_string_len 157
8 vm-aot_min_string_len 158
9 vm-aot_min_string_len 165
10 vm-aot_min_string_len 161

View File

@ -0,0 +1,10 @@
jit-bench_aot_len_heavy,574
jit-bench_aot_len_heavy,584
jit-bench_aot_len_heavy,585
jit-bench_aot_len_heavy,578
jit-bench_aot_len_heavy,573
jit-bench_aot_len_heavy,577
jit-bench_aot_len_heavy,574
jit-bench_aot_len_heavy,580
jit-bench_aot_len_heavy,585
jit-bench_aot_len_heavy,586
1 jit-bench_aot_len_heavy 574
2 jit-bench_aot_len_heavy 584
3 jit-bench_aot_len_heavy 585
4 jit-bench_aot_len_heavy 578
5 jit-bench_aot_len_heavy 573
6 jit-bench_aot_len_heavy 577
7 jit-bench_aot_len_heavy 574
8 jit-bench_aot_len_heavy 580
9 jit-bench_aot_len_heavy 585
10 jit-bench_aot_len_heavy 586

View File

@ -0,0 +1,7 @@
vm-bench_aot_len_heavy,599
vm-bench_aot_len_heavy,596
vm-bench_aot_len_heavy,608
vm-bench_aot_len_heavy,592
vm-bench_aot_len_heavy,589
vm-bench_aot_len_heavy,591
vm-bench_aot_len_heavy,591
1 vm-bench_aot_len_heavy 599
2 vm-bench_aot_len_heavy 596
3 vm-bench_aot_len_heavy 608
4 vm-bench_aot_len_heavy 592
5 vm-bench_aot_len_heavy 589
6 vm-bench_aot_len_heavy 591
7 vm-bench_aot_len_heavy 591

View File

@ -0,0 +1,10 @@
jit-bench_aot_len_light,206
jit-bench_aot_len_light,209
jit-bench_aot_len_light,209
jit-bench_aot_len_light,208
jit-bench_aot_len_light,211
jit-bench_aot_len_light,209
jit-bench_aot_len_light,211
jit-bench_aot_len_light,208
jit-bench_aot_len_light,210
jit-bench_aot_len_light,210
1 jit-bench_aot_len_light 206
2 jit-bench_aot_len_light 209
3 jit-bench_aot_len_light 209
4 jit-bench_aot_len_light 208
5 jit-bench_aot_len_light 211
6 jit-bench_aot_len_light 209
7 jit-bench_aot_len_light 211
8 jit-bench_aot_len_light 208
9 jit-bench_aot_len_light 210
10 jit-bench_aot_len_light 210

View File

@ -0,0 +1,10 @@
vm-bench_aot_len_light,210
vm-bench_aot_len_light,209
vm-bench_aot_len_light,207
vm-bench_aot_len_light,207
vm-bench_aot_len_light,209
vm-bench_aot_len_light,210
vm-bench_aot_len_light,209
vm-bench_aot_len_light,209
vm-bench_aot_len_light,208
vm-bench_aot_len_light,211
1 vm-bench_aot_len_light 210
2 vm-bench_aot_len_light 209
3 vm-bench_aot_len_light 207
4 vm-bench_aot_len_light 207
5 vm-bench_aot_len_light 209
6 vm-bench_aot_len_light 210
7 vm-bench_aot_len_light 209
8 vm-bench_aot_len_light 209
9 vm-bench_aot_len_light 208
10 vm-bench_aot_len_light 211

View File

@ -0,0 +1,10 @@
jit-bench_aot_len_medium,284
jit-bench_aot_len_medium,284
jit-bench_aot_len_medium,286
jit-bench_aot_len_medium,285
jit-bench_aot_len_medium,285
jit-bench_aot_len_medium,284
jit-bench_aot_len_medium,281
jit-bench_aot_len_medium,283
jit-bench_aot_len_medium,284
jit-bench_aot_len_medium,288
1 jit-bench_aot_len_medium 284
2 jit-bench_aot_len_medium 284
3 jit-bench_aot_len_medium 286
4 jit-bench_aot_len_medium 285
5 jit-bench_aot_len_medium 285
6 jit-bench_aot_len_medium 284
7 jit-bench_aot_len_medium 281
8 jit-bench_aot_len_medium 283
9 jit-bench_aot_len_medium 284
10 jit-bench_aot_len_medium 288

View File

@ -0,0 +1,10 @@
vm-bench_aot_len_medium,286
vm-bench_aot_len_medium,283
vm-bench_aot_len_medium,288
vm-bench_aot_len_medium,290
vm-bench_aot_len_medium,297
vm-bench_aot_len_medium,288
vm-bench_aot_len_medium,286
vm-bench_aot_len_medium,287
vm-bench_aot_len_medium,288
vm-bench_aot_len_medium,289
1 vm-bench_aot_len_medium 286
2 vm-bench_aot_len_medium 283
3 vm-bench_aot_len_medium 288
4 vm-bench_aot_len_medium 290
5 vm-bench_aot_len_medium 297
6 vm-bench_aot_len_medium 288
7 vm-bench_aot_len_medium 286
8 vm-bench_aot_len_medium 287
9 vm-bench_aot_len_medium 288
10 vm-bench_aot_len_medium 289

View File

@ -0,0 +1,10 @@
interp-bench_heavy,157
interp-bench_heavy,156
interp-bench_heavy,156
interp-bench_heavy,155
interp-bench_heavy,155
interp-bench_heavy,154
interp-bench_heavy,155
interp-bench_heavy,155
interp-bench_heavy,153
interp-bench_heavy,154
1 interp-bench_heavy 157
2 interp-bench_heavy 156
3 interp-bench_heavy 156
4 interp-bench_heavy 155
5 interp-bench_heavy 155
6 interp-bench_heavy 154
7 interp-bench_heavy 155
8 interp-bench_heavy 155
9 interp-bench_heavy 153
10 interp-bench_heavy 154

View File

@ -0,0 +1,10 @@
jit-bench_heavy,148
jit-bench_heavy,150
jit-bench_heavy,150
jit-bench_heavy,150
jit-bench_heavy,151
jit-bench_heavy,152
jit-bench_heavy,150
jit-bench_heavy,150
jit-bench_heavy,151
jit-bench_heavy,149
1 jit-bench_heavy 148
2 jit-bench_heavy 150
3 jit-bench_heavy 150
4 jit-bench_heavy 150
5 jit-bench_heavy 151
6 jit-bench_heavy 152
7 jit-bench_heavy 150
8 jit-bench_heavy 150
9 jit-bench_heavy 151
10 jit-bench_heavy 149

View File

@ -0,0 +1,10 @@
vm-bench_heavy,149
vm-bench_heavy,150
vm-bench_heavy,149
vm-bench_heavy,149
vm-bench_heavy,149
vm-bench_heavy,148
vm-bench_heavy,149
vm-bench_heavy,151
vm-bench_heavy,150
vm-bench_heavy,151
1 vm-bench_heavy 149
2 vm-bench_heavy 150
3 vm-bench_heavy 149
4 vm-bench_heavy 149
5 vm-bench_heavy 149
6 vm-bench_heavy 148
7 vm-bench_heavy 149
8 vm-bench_heavy 151
9 vm-bench_heavy 150
10 vm-bench_heavy 151

View File

@ -0,0 +1,10 @@
interp-bench_light,166
interp-bench_light,157
interp-bench_light,145
interp-bench_light,147
interp-bench_light,146
interp-bench_light,148
interp-bench_light,146
interp-bench_light,146
interp-bench_light,146
interp-bench_light,146
1 interp-bench_light 166
2 interp-bench_light 157
3 interp-bench_light 145
4 interp-bench_light 147
5 interp-bench_light 146
6 interp-bench_light 148
7 interp-bench_light 146
8 interp-bench_light 146
9 interp-bench_light 146
10 interp-bench_light 146

View File

@ -0,0 +1,10 @@
jit-bench_light,587
jit-bench_light,587
jit-bench_light,588
jit-bench_light,589
jit-bench_light,591
jit-bench_light,588
jit-bench_light,590
jit-bench_light,598
jit-bench_light,590
jit-bench_light,593
1 jit-bench_light 587
2 jit-bench_light 587
3 jit-bench_light 588
4 jit-bench_light 589
5 jit-bench_light 591
6 jit-bench_light 588
7 jit-bench_light 590
8 jit-bench_light 598
9 jit-bench_light 590
10 jit-bench_light 593

View File

@ -0,0 +1,10 @@
vm-bench_light,575
vm-bench_light,575
vm-bench_light,572
vm-bench_light,579
vm-bench_light,592
vm-bench_light,585
vm-bench_light,586
vm-bench_light,600
vm-bench_light,584
vm-bench_light,590
1 vm-bench_light 575
2 vm-bench_light 575
3 vm-bench_light 572
4 vm-bench_light 579
5 vm-bench_light 592
6 vm-bench_light 585
7 vm-bench_light 586
8 vm-bench_light 600
9 vm-bench_light 584
10 vm-bench_light 590

View File

@ -0,0 +1,10 @@
interp-bench_medium,151
interp-bench_medium,150
interp-bench_medium,150
interp-bench_medium,148
interp-bench_medium,151
interp-bench_medium,147
interp-bench_medium,150
interp-bench_medium,147
interp-bench_medium,150
interp-bench_medium,149
1 interp-bench_medium 151
2 interp-bench_medium 150
3 interp-bench_medium 150
4 interp-bench_medium 148
5 interp-bench_medium 151
6 interp-bench_medium 147
7 interp-bench_medium 150
8 interp-bench_medium 147
9 interp-bench_medium 150
10 interp-bench_medium 149

View File

@ -0,0 +1,10 @@
jit-bench_medium,152
jit-bench_medium,154
jit-bench_medium,153
jit-bench_medium,154
jit-bench_medium,150
jit-bench_medium,160
jit-bench_medium,152
jit-bench_medium,154
jit-bench_medium,154
jit-bench_medium,153
1 jit-bench_medium 152
2 jit-bench_medium 154
3 jit-bench_medium 153
4 jit-bench_medium 154
5 jit-bench_medium 150
6 jit-bench_medium 160
7 jit-bench_medium 152
8 jit-bench_medium 154
9 jit-bench_medium 154
10 jit-bench_medium 153

View File

@ -0,0 +1,10 @@
vm-bench_medium,156
vm-bench_medium,152
vm-bench_medium,152
vm-bench_medium,151
vm-bench_medium,153
vm-bench_medium,154
vm-bench_medium,155
vm-bench_medium,153
vm-bench_medium,153
vm-bench_medium,152
1 vm-bench_medium 156
2 vm-bench_medium 152
3 vm-bench_medium 152
4 vm-bench_medium 151
5 vm-bench_medium 153
6 vm-bench_medium 154
7 vm-bench_medium 155
8 vm-bench_medium 153
9 vm-bench_medium 153
10 vm-bench_medium 152

View File

@ -0,0 +1,276 @@
# MIR13: Everything is Boxによる究極のミニマル中間表現
## 概要
本論文では、わずか13命令で実用的なアプリケーションの実装を可能にする革新的な中間表現IR設計「MIR13」を提案する。従来のIR設計では数十から数百の命令が必要とされてきたが、我々は「Everything is Box」という設計哲学に基づき、すべてのメモリアクセスをBoxCallに統一することで、Load/Store命令を完全に廃止した。実装では12命令への削減も可能だが、可読性を考慮して意図的に13命令を採用している。MIR13はInterpreter、VM、JITの3つの実行バックエンドで実証され、実用的なアプリケーションの動作を確認した。
## 1. はじめに
プログラミング言語の中間表現IRは、高水準言語と機械語の橋渡しをする重要な抽象層である。LLVM IRは約60の基本命令、WebAssemblyは約170の命令を持つなど、既存のIRは複雑化の一途を辿っている。
本研究では、逆転の発想により「どこまでIRを単純化できるか」に挑戦した。結果として、わずか13命令で実用的なプログラミング言語を実装できることを実証した。従来のIR設計では57命令が必要とされた機能を、BoxCallへの統一により13命令まで削減した経緯についても議論する。
本稿はIR層MIR13に焦点を当てる。言語Nyashそのものの設計思想やbirth/fini対称メモリ管理、P2P Intentモデル、多層実行アーキテクチャ等の詳細は、別論文論文B: Nyash言語と実行モデルで報告・拡張予定である。
## 2. MIR13の設計哲学
### 2.1 Everything is Box
MIR13の核心は「Everything is Box」という設計原則である。従来のIRでは、メモリアクセス、配列操作、オブジェクトフィールドアクセスなどが個別の命令として実装されていた。我々はこれらをすべて「Boxへのメッセージパッシング」として統一した。
```
従来のアプローチ:
- Load/Storeメモリアクセス
- GetElement/SetElement配列
- GetField/SetFieldオブジェクト
- Call関数呼び出し
MIR13のアプローチ:
- BoxCallすべて統一
```
### 2.2 意図的な13命令選択
技術的にはBoxCallとBoxCallWithを統合して12命令にできるが、以下の理由から13命令を維持している
1. **可読性**: 引数なし/ありの区別が明確
2. **最適化**: JITコンパイラでの特殊化が容易
3. **教育的価値**: IRの学習が容易
## 3. MIR13命令セット
### 3.1 基本13命令
| 命令 | 説明 | 用途 |
|------|------|------|
| **Const** | 定数値のロード | リテラル、初期値 |
| **Copy** | 値のコピー | 変数代入、引数渡し |
| **BoxCall** | 引数なしBox操作 | getter、単純メソッド |
| **BoxCallWith** | 引数ありBox操作 | setter、複雑メソッド |
| **Call** | 関数呼び出し | ユーザー定義関数 |
| **Jump** | 無条件ジャンプ | 制御フロー |
| **Branch** | 条件分岐 | if文、ループ |
| **Return** | 関数からの復帰 | 値の返却 |
| **Phi** | SSA形式の値選択 | 分岐後の値統合 |
| **Cast** | 型変換 | 動的型付け対応 |
| **BinOp** | 二項演算 | 算術、比較 |
| **UnOp** | 単項演算 | 否定、型チェック |
| **Nop** | 何もしない | パディング、最適化 |
### 3.2 BoxCallによる統一
従来は個別命令だった操作がBoxCallで統一される
```mir
// 配列アクセス(従来: GetElement
v3 = BoxCallWith(v1, "get", v2) // array[index]
// 配列への代入(従来: SetElement
v4 = BoxCallWith(v1, "set", v2, v3) // array[index] = value
// フィールドアクセス(従来: GetField
v5 = BoxCall(v1, "name") // object.name
// メソッド呼び出し(従来: InvokeVirtual
v6 = BoxCallWith(v1, "add", v2) // object.add(arg)
```
## 4. 実装と評価
### 4.1 3つの実行バックエンド
MIR13は以下の3つのバックエンドで実装・検証された
1. **Interpreter**: 開発・デバッグ用、即座に実行可能
2. **VM**: スタックマシンによる高速実行
3. **JIT**: Craneliftによる最速ネイティブコード生成
注記実装マイルストン2025-09-04 に、JIT/ネイティブEXE経由での Windows GUI 表示ネイティブウィンドウ生成と描画を確認した。これはMIR13ベースの実行系がOSネイティブ機能まで到達したことを示すものであり、以降のGUI応用評価の基盤となる。
### 4.2 実アプリケーションでの検証
以下の実用的なアプリケーションが動作を確認:
- テキストエディタKilo移植版
- HTTPサーバー
- P2P通信システム
- LISPインタープリター
- Windows GUIアプリネイティブEXE: 2025-09-04 に表示確認
### 4.3 性能評価
#### 4.3.0 再現手順Artifact & Scripts
本論文の性能評価は、リポジトリ同梱のスクリプトで再現可能である。
1. 環境情報の収集(自動生成)
- `docs/papers/active/paper-a-mir13-ir-design/_artifacts/COLLECT_ENV.sh` を実行すると、CPU/OS/Rust/Cranelift/コミットIDを `ENVIRONMENT.txt` に記録する。
2. ビルド(フルモード)
- `cargo build --release --features cranelift-jit`
3. ベンチ実行
- `docs/papers/active/paper-a-mir13-ir-design/_artifacts/RUN_BENCHMARKS.sh`
- `hyperfine` があればCSVにエクスポート、無い場合はフォールバック計測を行う。
4. 結果
- `_artifacts/results/*.csv` に各モードInterpreter/VM/JIT/AOTの結果を保存。
注: AOTネイティブEXE`tools/build_aot.sh` が利用可能な場合のみ測定する(無ければ自動スキップ)。
また、LLVMバックエンド経由のAOT計測も可能である`USE_LLVM_AOT=1`)。
- 依存: `llvm-config-18`LLVM 18 開発環境)
- 例: `USE_LLVM_AOT=1 SKIP_INTERP=1 ./RUN_BENCHMARKS.sh`
さらに、Cranelift JIT からの直接AOT`--jit-direct`、本実装では「JIT-AOT」と表記も計測可能である`USE_JIT_AOT=1`)。
- 例: `USE_EXE_ONLY=1 USE_JIT_AOT=1 SKIP_INTERP=1 ./RUN_BENCHMARKS.sh`
#### 4.3.x 最適化状況と注意
現時点の実装は、最適化処理を徹底していないインライン化、ICの高度化、ボックス形状多態の特殊化、VM命令選択のチューニングなどは限定的。従って、提示する数値は「素の実装に近いベースライン」であり、今後の最適化で改善余地が大きい。再現スクリプトはモード差が観測しやすいよう、負荷を軽量〜中程度に設定している遅い環境では `SKIP_INTERP=1` でインタープリタ計測を省略可能)。
#### 4.3.1 相対性能
MIR13の3つのバックエンド間での相対実行時間
- Interpreter: 1.0x(基準)
- VM: 10-50x高速
- JIT: 100-500x高速
#### 4.3.2 絶対性能比較
標準的なベンチマークFibonacci、行列演算、文字列処理での比較
- Python 3.11: 1.0x(基準)
- Nyash Interpreter: 0.8-1.2x
- Nyash VM: 8-40x
- Nyash JIT: 80-400x
- Go 1.21: 100-600x
- Rust (release): 150-800x
#### 4.3.3 BoxCallのオーバーヘッド分析
マイクロベンチマークによる分析:
- 配列アクセス: 従来のLoad/Store比で1.2-1.5倍のオーバーヘッド
- JIT最適化後: インライン化により0.95-1.1倍まで改善
- メソッド呼び出し: 動的ディスパッチを含むため2-3倍だが、ICで1.1-1.3倍まで改善
### 4.4 実装公開と再現性Availability
本研究の実装と評価スクリプトは以下で公開している。
- リポジトリ: https://github.com/moe-charm/nyash
- 対象コミット: `_artifacts/ENVIRONMENT.txt``git rev-parse HEAD` を記録
- 再現手順: `_artifacts/COLLECT_ENV.sh``_artifacts/RUN_BENCHMARKS.sh`
- 出力: `_artifacts/results/*.csv`
## 5. 議論
### 5.1 設計の進化57命令から13命令への道のり
MIR13の13命令セットは、最初から意図的に設計されたものではない。当初57命令で始まったIRを、以下の洞察により段階的に削減した
1. **メモリアクセスの統一**: Load/Store、GetElement/SetElement、GetField/SetFieldがすべてオブジェクトへの操作として統一可能
2. **メッセージパッシングへの抽象化**: すべての操作を「Boxへのメッセージ」として見ることでBoxCallに集約
3. **型操作の統合**: TypeCheck/Castを単一のCast命令に統合
この削減過程は、IR設計における本質的な要素の発見プロセスであり、結果として得られた13命令は実践的な検証を経た最小セットである。
### 5.2 なぜ13命令で十分なのか
1. **抽象度の適切性**: Boxという適切な抽象化により、低レベル詳細を隠蔽
2. **実行時システムとの分担**: 複雑性をランタイムに委譲
3. **最小限の制御構造**: Jump/Branch/Phiで全制御フローを表現
### 5.3 ランタイムシステムの役割
MIR13の単純性は、洗練されたランタイムシステムとの協調によって実現される
1. **Boxの内部表現**: 各Boxは型タグ、参照カウント、データペイロードを持つ
2. **ホスト関数インターフェース**: BoxCallはランタイムのネイティブ関数を効率的に呼び出す
3. **メモリ管理**: 参照カウントベースの決定的メモリ管理GCオプション付き
この設計により、IR自体の複雑性を最小限に抑えながら、実用的な性能を達成している。
### 5.3bis 代表的操作のLowering例MIR13→BoxCall
以下は高水準操作が13命令概念上に落ちる代表例である。
```mir
// 例1: 配列アクセスと更新a[i]、a[i]=v
%a = ... // ArrayBox
%i = ... // IntBox
%v = ... // 任意のBox
%x = BoxCallWith(%a, "get", %i) // load → BoxCallWith
%ok = BoxCallWith(%a, "set", %i, %v) // store → BoxCallWith
// 例2: フィールド読み書きobj.name、obj.name=v
%obj = ... // ObjectBox
%nm = Const("name")
%cur = BoxCallWith(%obj, "getField", %nm)
%ok2 = BoxCallWith(%obj, "setField", %nm, %v)
// 例3: メソッド呼び出しobj.add(arg)
%res = BoxCallWith(%obj, "add", %v)
// 例4: 外部プラグイン関数呼び出しhost.fn(args…)
%h = ... // HostBox
%r = BoxCallWith(%h, "fn", %arg1, %arg2)
// 制御構造はJump/Branch/Phiで表現
branch %cond, ^T, ^F
^T: ...
jump ^K
^F: ...
^K: %y = phi(%yT, %yF)
```
### 5.4 限界と将来展望
- SIMD命令は現在未対応BoxCall拡張で対応予定
- 並列実行最適化の余地あり
- WASM/LLVMバックエンドは開発中
## 6. 関連研究
### 6.1 既存のIR設計との比較
| IR | 命令数 | メモリモデル | 主な特徴 |
|---|--------|------------|----------|
| LLVM IR | 約60 | Load/Store明示 | SSA形式、型付き |
| WebAssembly | 約170 | スタックマシン | セキュリティ重視 |
| JVM Bytecode | 約200 | スタック+ローカル | オブジェクト指向 |
| MIR13 | 13 | BoxCall統一 | 最小命令セット |
### 6.2 メッセージパッシングIRの系譜
- **Smalltalk**: すべてはオブジェクト、すべてはメッセージ
- **Self**: プロトタイプベースオブジェクト
- **PyPy**: RPythonのオブジェクト空間
- **Truffle/Graal**: 動的言語のための抽象解釈
MIR13はこれらの思想を低レベルIRに適用し、Load/Store命令の完全廃止という新境地を開拓した。
#### 補足: Wasm GCとTyped Objectsとの比較
近年のWasm GC拡張やTyped Objectsの動向は、高レベル型をWasm上に安全に表現することを目指している。一方MIR13は「命令数最小化」と「BoxCallによる操作統一」を主眼に置き、型表現やメモリモデルの複雑さをIRではなくランタイムへ委譲する。したがって、目的関数安全な型表現 vs. 最小命令と実装容易性)が異なり、補完的関係にある。
## 7. 結論
MIR13は、13命令という極めて小さな命令セットで完全なプログラミング言語を実装できることを実証した。「Everything is Box」の設計哲学により、従来は数十の命令が必要だった操作をBoxCallに統一し、Load/Store命令を完全に廃止した。技術的には12命令も可能だが、可読性のために意図的に13命令を選択した。3つの実行バックエンドでの動作確認により、実用性も証明された。
本研究は、プログラミング言語設計における「少ないことは豊かである」という原則の究極の実証である。
## 謝辞
本論文の執筆にあたり、Anthropic Claude、OpenAI ChatGPTCodex CLI経由、Google Gemini の支援ブレインストーミング、下書き、コード補助、校正を受けた。生成物はすべて著者がレビュー・修正し、最終的な設計判断・統合・評価は著者が行った。開発は2025-08-03頃に着手し、初回コミットは2025-08-09である。AI時代の研究開発における新しい協働形態の実例として、これを明記する。
## 参考文献
[省略]
---
## 付録なぜ12命令にしないのか
BoxCallとBoxCallWithは技術的に統合可能である
```mir
// 統合版12命令
v1 = BoxCall(obj, "method", []) // 引数なし
v2 = BoxCall(obj, "method", [arg1]) // 引数あり
// 現在の分離版13命令
v1 = BoxCall(obj, "method") // 明確に引数なし
v2 = BoxCallWith(obj, "method", arg1) // 明確に引数あり
```
しかし、以下の理由から分離を維持:
1. パターンマッチングが単純
2. 最適化パスが書きやすい
3. エラーメッセージが分かりやすい
4. 「13」という数字の美しさ主観的だが重要

View File

@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -euo pipefail
OUT_DIR=$(cd "$(dirname "$0")" && pwd)
OUT_FILE="$OUT_DIR/ENVIRONMENT.txt"
{
echo "== Datetime =="; date -Iseconds || date; echo
echo "== OS =="; uname -a || true; lsb_release -a 2>/dev/null || true; sw_vers 2>/dev/null || true; systeminfo 2>/dev/null | head -n 30 || true; echo
echo "== CPU =="; lscpu 2>/dev/null || sysctl -a 2>/dev/null | grep machdep.cpu || true; echo
echo "== Rust toolchain =="; rustc --version 2>/dev/null || true; cargo --version 2>/dev/null || true; echo
echo "== Git =="; git rev-parse HEAD 2>/dev/null || true; echo
echo "== Cranelift/JIT features =="; rg -n "cranelift|jit" -S ../../../../ -g '!target' 2>/dev/null || true
} > "$OUT_FILE"
echo "[DONE] Wrote $OUT_FILE"

View File

@ -0,0 +1,25 @@
This folder contains reproducibility artifacts for Paper B (Nyash language & execution model).
Files
- `COLLECT_ENV.sh`: Captures host OS/CPU/toolchain/git info into `ENVIRONMENT.txt`.
- `RUN_BENCHMARKS.sh`: Runs interpreter/VM/JIT/AOT (if available) on sample benchmarks and writes CSVs to `results/`.
- `results/`: Output CSVs (per benchmark and per mode).
Usage
1) Capture environment
./COLLECT_ENV.sh
2) Build (full)
cargo build --release --features cranelift-jit
3) Run benchmarks
./RUN_BENCHMARKS.sh
Variables:
- NYASH_BIN: Path to nyash binary (default: target/release/nyash)
- USE_EXE_ONLY=1: Only measure AOT executables (skips interp/vm/jit)
Notes
- AOT requires `tools/build_aot.sh`. If missing, AOT is skipped.
- If `hyperfine` is not installed, a simple timing fallback is used.

View File

@ -0,0 +1,86 @@
#!/usr/bin/env bash
set -euo pipefail
# Repro benchmarks for Paper B (Nyash language & execution model)
# Uses the shared benchmarks folder; writes CSVs under _artifacts/results
if ROOT_DIR=$(git -C "$(dirname "$0")" rev-parse --show-toplevel 2>/dev/null); then
ROOT_DIR="$ROOT_DIR/nyash"
[[ -d "$ROOT_DIR" ]] || ROOT_DIR=$(git rev-parse --show-toplevel)
else
ROOT_DIR=$(cd "$(dirname "$0")/../../../../.." && pwd)
fi
ART_DIR=$(cd "$(dirname "$0")" && pwd)
RES_DIR="$ART_DIR/results"
mkdir -p "$RES_DIR"
NYASH_BIN=${NYASH_BIN:-"$ROOT_DIR/target/release/nyash"}
SKIP_INTERP=${SKIP_INTERP:-0}
USE_EXE_ONLY=${USE_EXE_ONLY:-0}
HYPERFINE=$(command -v hyperfine || true)
BENCH_DIR="$ROOT_DIR/benchmarks"
FILES=(
"$BENCH_DIR/bench_light.nyash"
"$BENCH_DIR/bench_medium.nyash"
"$BENCH_DIR/bench_heavy.nyash"
)
echo "[INFO] NYASH_BIN=$NYASH_BIN"
echo "[INFO] USE_EXE_ONLY=$USE_EXE_ONLY (1=EXE only)"
echo "[INFO] hyperfine=${HYPERFINE:-not found}"
if [[ ! -x "$NYASH_BIN" && "$USE_EXE_ONLY" -eq 0 ]]; then
echo "[INFO] Building nyash (release, with JIT feature)"
(cd "$ROOT_DIR" && cargo build --release --features cranelift-jit)
fi
have_build_aot=0
if [[ -x "$ROOT_DIR/tools/build_aot.sh" ]]; then
have_build_aot=1
fi
run_cmd() {
local cmd="$1" label="$2" csv="$3"
if [[ -n "$HYPERFINE" ]]; then
$HYPERFINE -w 2 -r 10 --export-csv "$csv" --show-output --min-runs 10 "$cmd"
else
: > "$csv"
for i in {1..10}; do
local t0=$(python3 - <<<'import time; print(int(time.time()*1000))')
bash -lc "$cmd" >/dev/null 2>&1 || true
local t1=$(python3 - <<<'import time; print(int(time.time()*1000))')
echo "$label,$((t1-t0))" >> "$csv"
done
fi
}
for f in "${FILES[@]}"; do
[[ -f "$f" ]] || { echo "[WARN] Skip missing $f"; continue; }
base=$(basename "$f" .nyash)
if [[ "$USE_EXE_ONLY" -eq 0 ]]; then
if [[ "$SKIP_INTERP" -eq 0 ]]; then
run_cmd "$NYASH_BIN $f" "interp-$base" "$RES_DIR/${base}_interp.csv"
else
echo "[INFO] SKIP_INTERP=1: skipping interpreter for $f"
fi
run_cmd "$NYASH_BIN --backend vm $f" "vm-$base" "$RES_DIR/${base}_vm.csv"
run_cmd "NYASH_JIT_EXEC=1 $NYASH_BIN --backend vm $f" "jit-$base" "$RES_DIR/${base}_jit.csv"
fi
if [[ $have_build_aot -eq 1 ]]; then
out="/tmp/ny_${base}_aot"
bash "$ROOT_DIR/tools/build_aot.sh" "$f" -o "$out" >/dev/null 2>&1 || true
if [[ -x "$out" ]]; then
run_cmd "$out" "aot-$base" "$RES_DIR/${base}_aot.csv"
rm -f "$out"
else
echo "[WARN] AOT build failed for $f"
fi
else
echo "[INFO] AOT tool not found; skipping AOT for $f"
fi
done
echo "[DONE] Results in $RES_DIR"

Some files were not shown because too many files have changed in this diff Show More