vm(compare): fix BoxRef(IntegerBox) comparisons by upstream casts and fallbacks; unify IntegerBox (re-export); as_bool truthiness; NewBox primitive fastpath; phi minimal fallback; add temp debug logs for route tracing
This commit is contained in:
10
CLAUDE.md
10
CLAUDE.md
@ -31,12 +31,12 @@ Nyashプログラミング言語開発に必要な情報をまとめたクイッ
|
||||
|
||||
### 実践方法
|
||||
1. **まず動くものを作る**(80%)
|
||||
2. **改善アイデアは `ideas/` フォルダに記録**(20%)
|
||||
2. **改善アイデアは `docs/ideas/` フォルダに記録**(20%)
|
||||
3. **優先度に応じて後から改善**
|
||||
|
||||
例: VM and/or実装
|
||||
- 80%: `as_bool()`で基本動作 → コミット ✅
|
||||
- 20%: 短絡評価、型最適化 → `ideas/improvements/2025-08-25-vm-andor-shortcircuit.md`
|
||||
- 20%: 短絡評価、型最適化 → `docs/ideas/improvements/2025-08-25-vm-andor-shortcircuit.md`
|
||||
|
||||
## 🚀 クイックスタート
|
||||
|
||||
@ -457,12 +457,12 @@ NYASH_VM_DEBUG=1 # VM のみ
|
||||
gemini -p "Nyashの実装で困っています..."
|
||||
```
|
||||
|
||||
### 💡 アイデア管理(ideas/フォルダ)
|
||||
### 💡 アイデア管理(docs/ideas/フォルダ)
|
||||
|
||||
**80/20ルールの「残り20%」を整理して管理**
|
||||
|
||||
```
|
||||
ideas/
|
||||
docs/ideas/
|
||||
├── improvements/ # 80%実装の残り20%改善候補
|
||||
│ ├── 2025-08-25-vm-andor-shortcircuit.md
|
||||
│ └── archived/ # 実装済みor却下
|
||||
@ -735,5 +735,5 @@ let new_plugin_box = PluginBoxV2 {
|
||||
|
||||
最終更新: 2025年8月25日 - **🏃 80/20ルールとideas/フォルダ構造追加**
|
||||
- **80/20ルール**: 完璧より進捗を優先する開発方針を明記
|
||||
- **ideas/フォルダ**: 改善案・新機能・その他アイデアの管理構造を追加
|
||||
- **docs/ideas/フォルダ**: 改善案・新機能・その他アイデアの管理構造を追加
|
||||
- **ドキュメント整理**: 重複セクションを統合してスリム化
|
||||
|
||||
183
docs/ideas/other/2025-08-25-debugging-nightmare-lessons.md
Normal file
183
docs/ideas/other/2025-08-25-debugging-nightmare-lessons.md
Normal file
@ -0,0 +1,183 @@
|
||||
# デバッグ地獄からの教訓 - TypeError犯人捜しの苦戦
|
||||
Status: Research
|
||||
Created: 2025-08-25
|
||||
Priority: High
|
||||
Related: リファクタリング戦略、デバッグ手法
|
||||
|
||||
## 問題の症状
|
||||
|
||||
ChatGPT5さんが現在直面している問題:
|
||||
- `BoxRef(IntegerBox) < BoxRef(IntegerBox)` でTypeError
|
||||
- ログを仕込んでも該当箇所が出力されない
|
||||
- 「犯人」が別の場所にいる可能性大
|
||||
|
||||
**Nyash開発で何十回も経験した典型的パターン**
|
||||
|
||||
## なぜログに出てこないのか
|
||||
|
||||
### 1. 別の実行パスを通っている
|
||||
```
|
||||
想定: VM → execute_compare → ログ出力
|
||||
実際: インタープリター → 別の比較処理 → エラー
|
||||
または
|
||||
VM → 早期最適化パス → 別の比較処理 → エラー
|
||||
```
|
||||
|
||||
### 2. 型変換が複数箇所で起きている
|
||||
```
|
||||
IntegerBox
|
||||
↓
|
||||
VMValue::BoxRef(場所A)
|
||||
↓
|
||||
比較処理(場所B) ← ログはここ
|
||||
↓
|
||||
別の型変換(場所C) ← 実際のエラーはここ!
|
||||
```
|
||||
|
||||
### 3. エラーメッセージが誤解を招く
|
||||
- 表示: `BoxRef(IntegerBox) < BoxRef(IntegerBox)`
|
||||
- 実際: 片方が `InstanceBox` や `UserDefinedBox` の可能性
|
||||
- `type_name()` が同じでも内部実装が異なる
|
||||
|
||||
## リファクタリングによる根本解決
|
||||
|
||||
### 1. 関数の超細分化
|
||||
```rust
|
||||
// ❌ 現在:巨大な関数
|
||||
fn execute_compare(&mut self, dst: ValueId, op: &CompareOp, lhs: ValueId, rhs: ValueId) -> Result<ControlFlow, VMError> {
|
||||
// 100行以上のロジック
|
||||
// どこでエラーが起きてるか分からない
|
||||
}
|
||||
|
||||
// ✅ 改善:細かく分割
|
||||
fn execute_compare(&mut self, dst: ValueId, op: &CompareOp, lhs: ValueId, rhs: ValueId) -> Result<ControlFlow, VMError> {
|
||||
let left = self.get_and_canonicalize_value(lhs)?;
|
||||
let right = self.get_and_canonicalize_value(rhs)?;
|
||||
let result = compare_canonical_values(&op, &left, &right)?;
|
||||
self.set_value(dst, VMValue::Bool(result));
|
||||
Ok(ControlFlow::Continue)
|
||||
}
|
||||
|
||||
fn get_and_canonicalize_value(&self, id: ValueId) -> Result<CanonicalValue, VMError> {
|
||||
eprintln!("[CANON] Getting value {:?}", id);
|
||||
let raw = self.get_value(id)?;
|
||||
eprintln!("[CANON] Raw value: {:?}", raw);
|
||||
canonicalize_vm_value(raw)
|
||||
}
|
||||
|
||||
fn canonicalize_vm_value(val: VMValue) -> Result<CanonicalValue, VMError> {
|
||||
// 型変換ロジックを完全に独立
|
||||
}
|
||||
|
||||
fn compare_canonical_values(op: &CompareOp, left: &CanonicalValue, right: &CanonicalValue) -> Result<bool, VMError> {
|
||||
// 比較ロジックを完全に独立
|
||||
}
|
||||
```
|
||||
|
||||
### 2. トレース可能な設計
|
||||
```rust
|
||||
// 各変換ステップを追跡可能に
|
||||
#[derive(Debug)]
|
||||
struct ValueTrace {
|
||||
original: VMValue,
|
||||
conversions: Vec<String>,
|
||||
final_value: CanonicalValue,
|
||||
}
|
||||
|
||||
impl ValueTrace {
|
||||
fn log_conversion(&mut self, step: &str) {
|
||||
self.conversions.push(format!("[{}] {}", self.conversions.len(), step));
|
||||
eprintln!("TRACE: {}", self.conversions.last().unwrap());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 今すぐできる対策(80/20)
|
||||
|
||||
### 1. 一時的ワークアラウンド
|
||||
```rust
|
||||
// すべてのBoxRefを強制的にプリミティブ変換
|
||||
fn force_canonicalize(val: VMValue) -> VMValue {
|
||||
match val {
|
||||
VMValue::BoxRef(b) => {
|
||||
// IntegerBox, BoolBox, StringBoxを強制的に展開
|
||||
if let Some(ib) = b.as_any().downcast_ref::<IntegerBox>() {
|
||||
return VMValue::Integer(ib.value);
|
||||
}
|
||||
// ... 他の基本型も同様
|
||||
val // 変換できない場合はそのまま
|
||||
}
|
||||
_ => val
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. エラー箇所の網羅的ログ
|
||||
```rust
|
||||
// マクロで全箇所にログ挿入
|
||||
macro_rules! trace_compare {
|
||||
($left:expr, $right:expr, $location:expr) => {
|
||||
eprintln!("[COMPARE @{}] left={:?} right={:?}", $location, $left, $right);
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## 過去の経験からの教訓
|
||||
|
||||
### 1. 「ログが出ない = 想定外の場所」の法則
|
||||
- 9割は別の実行パスを通っている
|
||||
- 残り1割は早期リターンで到達していない
|
||||
|
||||
### 2. 巨大関数は悪
|
||||
- 100行を超えたら分割を検討
|
||||
- 50行が理想的
|
||||
- 各関数は単一の責任のみ
|
||||
|
||||
### 3. デバッグ戦略
|
||||
1. **最小再現コードの作成**
|
||||
```nyash
|
||||
local a = new IntegerBox(5)
|
||||
local b = new IntegerBox(10)
|
||||
print(a < b) // これだけでエラー再現するか?
|
||||
```
|
||||
|
||||
2. **バイナリサーチデバッグ**
|
||||
- コードを半分ずつコメントアウト
|
||||
- エラーが消える境界を探す
|
||||
|
||||
3. **printf デバッグの限界**
|
||||
- ログが多すぎると見落とす
|
||||
- 構造化されたトレースが必要
|
||||
|
||||
## 長期的な改善策
|
||||
|
||||
### 1. 実行トレースシステム
|
||||
```rust
|
||||
// すべての型変換を記録
|
||||
struct TypeConversionTrace {
|
||||
conversions: Vec<(String, String, String)>, // (from, to, location)
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 型システムの簡素化
|
||||
- VMValue と NyashBox の境界を明確に
|
||||
- 変換箇所を最小限に
|
||||
|
||||
### 3. テスタブルな設計
|
||||
- 各変換関数を独立してテスト可能に
|
||||
- エッジケースのユニットテスト充実
|
||||
|
||||
## まとめ
|
||||
|
||||
**この種のデバッグ地獄は、関数が大きすぎることが根本原因**
|
||||
|
||||
短期的には:
|
||||
- ワークアラウンドで動作を優先
|
||||
- 網羅的ログで犯人特定
|
||||
|
||||
長期的には:
|
||||
- 徹底的なリファクタリング
|
||||
- 関数の細分化
|
||||
- トレース可能な設計
|
||||
|
||||
「何十回も経験した」この問題、今回で最後にしたいですにゃ!
|
||||
187
docs/ideas/other/2025-08-25-gemini-sensei-full-response.md
Normal file
187
docs/ideas/other/2025-08-25-gemini-sensei-full-response.md
Normal file
@ -0,0 +1,187 @@
|
||||
# Gemini先生の統一Box設計に関する完全回答(途中でレート制限)
|
||||
Status: Research
|
||||
Created: 2025-08-25
|
||||
Priority: High
|
||||
Related: 統一Box設計の専門的分析
|
||||
|
||||
## Gemini先生の回答(レート制限で途中まで)
|
||||
|
||||
Nyashの統一Box設計について、専門的な観点から分析と提案をさせていただきます。
|
||||
|
||||
## 設計の評価
|
||||
|
||||
### 美しい点
|
||||
|
||||
1. **「Everything is Box」の一貫性**: この哲学は Smalltalk の「Everything is an Object」に通じる美しさがあります。概念的な統一感は言語の学習曲線を緩やかにし、予測可能性を高めます。
|
||||
|
||||
2. **透過的な置き換え機構**: プラグインによるビルトインBoxの置き換えは、Rubyの `refinements` や Python の `__import__` フックを彷彿とさせます。テスタビリティと拡張性の観点から優れています。
|
||||
|
||||
3. **優先順位システム**: 明確な優先順位(ビルトイン > ユーザー定義 > プラグイン)は、名前解決の曖昧さを排除し、デバッグを容易にします。
|
||||
|
||||
### 改善の余地がある点
|
||||
|
||||
1. **パフォーマンスのオーバーヘッド**: すべてを `Box<dyn NyashBox>` でラップすることは、小さな値(整数、真偽値など)にとって大きなオーバーヘッドです。Rust の `enum` を活用した値の内部表現最適化を検討すべきです。
|
||||
|
||||
2. **型の曖昧さ**: `Option<Box<dyn NyashBox>>` のような入れ子構造は、実行時の型チェックを複雑にします。静的型付けの利点を活かしきれていません。
|
||||
|
||||
## 改善提案
|
||||
|
||||
### 1. Enum ベースの値表現
|
||||
|
||||
現在の `Box<dyn NyashBox>` 一辺倒から、以下のような enum ベースの表現への移行を提案します:
|
||||
|
||||
```rust
|
||||
pub enum NyashValue {
|
||||
// 即値(スタック上)
|
||||
Void,
|
||||
Bool(bool),
|
||||
Integer(i64),
|
||||
Float(f64),
|
||||
|
||||
// ヒープ上の不変値
|
||||
String(Arc<String>),
|
||||
|
||||
// 複雑なオブジェクト
|
||||
Object(Arc<NyashObject>),
|
||||
}
|
||||
```
|
||||
|
||||
**メリット**:
|
||||
- 小さな値のヒープアロケーションを回避
|
||||
- パターンマッチによる高速なディスパッチ
|
||||
- 型安全性の向上
|
||||
|
||||
### 2. トレイトの階層化
|
||||
|
||||
基本メソッドの統一案は良いですが、さらに階層化することで柔軟性を高められます:
|
||||
|
||||
```rust
|
||||
// 基本トレイト(すべてのBoxが実装)
|
||||
pub trait NyashBox: Send + Sync + Debug {
|
||||
fn to_string(&self) -> String;
|
||||
fn type_name(&self) -> &'static str;
|
||||
fn equals(&self, other: &dyn NyashBox) -> bool;
|
||||
}
|
||||
|
||||
// 拡張トレイト(必要に応じて実装)
|
||||
pub trait Comparable: NyashBox {
|
||||
fn compare(&self, other: &dyn NyashBox) -> Option<Ordering>;
|
||||
}
|
||||
|
||||
pub trait Arithmetic: NyashBox {
|
||||
fn add(&self, other: &dyn NyashBox) -> Result<Box<dyn NyashBox>, String>;
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 3. メタプログラミング機能
|
||||
|
||||
ユーザー定義Boxの完全統一のために、メタプログラミング機能を提供:
|
||||
|
||||
```rust
|
||||
// Box定義時のフック
|
||||
pub trait BoxMetadata {
|
||||
fn on_method_missing(&self, name: &str, args: &[NyashValue]) -> Option<NyashValue>;
|
||||
fn on_field_access(&self, name: &str) -> Option<NyashValue>;
|
||||
}
|
||||
```
|
||||
|
||||
### 4. パイプライン演算子の実装
|
||||
|
||||
パイプライン演算子は、メソッドチェーンの代替として優れています:
|
||||
|
||||
```nyash
|
||||
// 現在
|
||||
result = str.substring(0, 5).toUpperCase().trim()
|
||||
|
||||
// パイプライン版
|
||||
result = str
|
||||
|> substring(0, 5)
|
||||
|> toUpperCase()
|
||||
|> trim()
|
||||
```
|
||||
|
||||
実装は、単純な構文糖として扱えます。
|
||||
|
||||
## 他言語との比較
|
||||
|
||||
**Smalltalk**: 最も近い哲学を持ちますが、Nyashの方が実用的(ファイルI/O、ネットワーク等を最初から考慮)
|
||||
|
||||
**Ruby**: オープンクラスの柔軟性はありませんが、その分予測可能性が高い
|
||||
|
||||
**Python**: `__getattr__` のような動的フックより、明示的なメソッド定義の方が保守しやすい
|
||||
|
||||
## 総合的な推奨事項
|
||||
|
||||
1. **段階的な移行**: 現在の設計を一度に変更するのではなく、まず `NyashValue` enum を導入し、徐々に最適化
|
||||
|
||||
2. **プロファイリング駆動**: パフォーマンスの問題が実際に発生している箇所から最適化
|
||||
|
||||
3. **ドキュメント駆動開発**: 各Boxの振る舞いを明確に文書化し、それに基づいて実装
|
||||
|
||||
4. **テストの充実**: 特に異なる種類のBox間の相互作用をテスト
|
||||
|
||||
この設計は十分に美しく、実用的です。過度に複雑というより、むしろ適切な抽象化レベルだと評価します。
|
||||
|
||||
---
|
||||
|
||||
## 以下、レート制限でエラーになった後の続き(推測される内容)
|
||||
|
||||
おそらくGemini先生は、以下のような内容を続けて説明しようとしていたと思われます:
|
||||
|
||||
### 設計思想:「値と参照の分離」と「Enumによる静的ディスパッチ」
|
||||
|
||||
現在の `Box<dyn NyashBox>` にすべてを押し込めるのではなく、値の性質に応じて内部表現を最適化する提案。
|
||||
|
||||
#### NyashObject構造体の定義
|
||||
|
||||
```rust
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::collections::HashMap;
|
||||
|
||||
// ビルトインの振る舞いを定義するトレイト
|
||||
pub trait NativeObject: Send + Sync + Debug {
|
||||
// ... as_any(), type_name() など
|
||||
}
|
||||
|
||||
// ユーザー定義メソッドか、ビルトインの振る舞いか
|
||||
pub enum ObjectKind {
|
||||
// ユーザーが Nyash で定義したオブジェクト
|
||||
UserDefined {
|
||||
methods: Arc<HashMap<String, NyashFunction>>,
|
||||
},
|
||||
// Rustで実装されたビルトインオブジェクト
|
||||
Native(Arc<dyn NativeObject>),
|
||||
}
|
||||
|
||||
pub struct NyashObject {
|
||||
pub type_name: String, // "Point", "File", "HttpClient" など
|
||||
// フィールドはすべて NyashValue
|
||||
fields: RwLock<HashMap<String, NyashValue>>,
|
||||
kind: ObjectKind,
|
||||
}
|
||||
```
|
||||
|
||||
#### 演算とメソッド呼び出しの実装
|
||||
|
||||
`NyashValue` に対して、`Add` や `Sub` などの演算子トレイトを実装する提案。
|
||||
|
||||
### 移行計画
|
||||
|
||||
この新しい設計への移行は段階的に行える:
|
||||
|
||||
1. **`NyashValue` Enum の導入**: まず `NyashValue` を定義し、VMやインタプリタのコア部分で使用
|
||||
2. **既存Box型の変換層**: 既存のBox型は `NyashValue` に変換する層を設けて互換性維持
|
||||
3. **段階的な最適化**: パフォーマンスが重要な箇所から順次最適化
|
||||
|
||||
---
|
||||
|
||||
## エラー情報
|
||||
|
||||
エラーコード: 429 (Too Many Requests)
|
||||
メッセージ: Resource exhausted. Please try again later.
|
||||
原因: Gemini APIのレート制限に到達
|
||||
|
||||
## まとめ
|
||||
|
||||
Gemini先生は、Nyashの統一Box設計を高く評価しつつ、パフォーマンス最適化のための具体的な提案(NyashValue enum導入)をしてくれました。設計の美しさと実用性のバランスが取れているという評価でした。
|
||||
242
docs/ideas/other/2025-08-25-unified-box-design-deep-analysis.md
Normal file
242
docs/ideas/other/2025-08-25-unified-box-design-deep-analysis.md
Normal file
@ -0,0 +1,242 @@
|
||||
# Nyash統一Box設計の深い分析と今後の方向性
|
||||
Status: Research
|
||||
Created: 2025-08-25
|
||||
Priority: High
|
||||
Related: Everything is Box哲学の実装レベルでの完全実現
|
||||
|
||||
## 現状の統一Box設計
|
||||
|
||||
### 3種類のBoxの存在
|
||||
1. **ビルトインBox** - Rustで実装(StringBox, IntegerBox, ConsoleBox等)
|
||||
2. **プラグインBox** - 動的ライブラリで提供(FileBox等の置き換え可能)
|
||||
3. **ユーザー定義Box** - Nyashコードで定義(box Person等)
|
||||
|
||||
### 現在の統一アーキテクチャ
|
||||
```
|
||||
UnifiedBoxRegistry(統一レジストリ)
|
||||
├── BuiltinBoxFactory(優先度1)
|
||||
├── UserDefinedBoxFactory(優先度2)
|
||||
└── PluginBoxFactory(優先度3)
|
||||
```
|
||||
|
||||
### 統一の美しさ
|
||||
|
||||
1. **透過的な置き換え**
|
||||
- 同じ名前のBoxをプラグインで上書き可能
|
||||
- 実行時の動的切り替えも可能
|
||||
|
||||
2. **統一インターフェース**
|
||||
```rust
|
||||
pub trait BoxFactory: Send + Sync {
|
||||
fn create_box(&self, name: &str, args: &[Box<dyn NyashBox>]) -> Result<Box<dyn NyashBox>, RuntimeError>;
|
||||
fn box_types(&self) -> Vec<&str>;
|
||||
fn supports_birth(&self) -> bool;
|
||||
}
|
||||
```
|
||||
|
||||
3. **優先順位システム**
|
||||
- ビルトイン > ユーザー定義 > プラグイン
|
||||
- 予約語保護(StringBox等は上書き不可)
|
||||
|
||||
## InstanceBoxによる完全統一
|
||||
|
||||
### 統一実装の核心
|
||||
```rust
|
||||
pub struct InstanceBox {
|
||||
pub class_name: String, // "StringBox", "Person"等
|
||||
pub inner_content: Option<Box<dyn NyashBox>>, // 内包Box(統一!)
|
||||
pub fields_ng: Arc<Mutex<HashMap<String, NyashValue>>>,
|
||||
pub methods: Arc<HashMap<String, ASTNode>>,
|
||||
}
|
||||
```
|
||||
|
||||
### 3つの形態を統一的に扱う
|
||||
```rust
|
||||
// ビルトイン
|
||||
InstanceBox::from_any_box("StringBox", Box::new(StringBox::new("hello")))
|
||||
|
||||
// プラグイン
|
||||
InstanceBox::from_any_box("FileBox", plugin_loader.create_box("FileBox"))
|
||||
|
||||
// ユーザー定義
|
||||
InstanceBox::from_declaration("Person", vec!["name", "age"], methods)
|
||||
```
|
||||
|
||||
## 基本メソッドの統一提案
|
||||
|
||||
### 問題点
|
||||
- `toString()` → `to_string_box()` (Rustの命名規則で異なる)
|
||||
- `type()` → `type_name()` (微妙に異なる)
|
||||
- 各Boxで個別実装されていて統一感がない
|
||||
|
||||
### 解決案:NyashBoxトレイトにデフォルト実装
|
||||
```rust
|
||||
pub trait NyashBox: BoxCore + Debug {
|
||||
// Nyash標準メソッド(デフォルト実装)
|
||||
fn toString(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new(&self.to_string_box().value))
|
||||
}
|
||||
|
||||
fn type(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new(self.type_name()))
|
||||
}
|
||||
|
||||
fn equals(&self, other: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
Box::new(BoolBox::new(self.equals_internal(other.as_ref())))
|
||||
}
|
||||
|
||||
fn clone(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### VMでの統一的な呼び出し
|
||||
```rust
|
||||
pub(super) fn call_box_method(&self, box_value: Box<dyn NyashBox>, method: &str, args: Vec<Box<dyn NyashBox>>) -> Result<Box<dyn NyashBox>, VMError> {
|
||||
// 基本メソッドは全Boxで使える!
|
||||
match method {
|
||||
"toString" => Ok(box_value.toString()),
|
||||
"type" => Ok(box_value.type()),
|
||||
"equals" => Ok(box_value.equals(args[0].clone_or_share())),
|
||||
"clone" => Ok(box_value.clone()),
|
||||
_ => self.call_specific_method(box_value, method, args)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Gemini先生からの提案(部分)
|
||||
|
||||
### 1. NyashValue Enumの導入
|
||||
```rust
|
||||
pub enum NyashValue {
|
||||
// 即値(スタック上)
|
||||
Void,
|
||||
Bool(bool),
|
||||
Integer(i64),
|
||||
Float(f64),
|
||||
|
||||
// ヒープ上の不変値
|
||||
String(Arc<String>),
|
||||
|
||||
// 複雑なオブジェクト
|
||||
Object(Arc<NyashObject>),
|
||||
}
|
||||
```
|
||||
|
||||
**メリット**:
|
||||
- 小さな値のヒープアロケーション回避
|
||||
- パターンマッチによる高速ディスパッチ
|
||||
- 型安全性の向上
|
||||
|
||||
### 2. トレイトの階層化
|
||||
```rust
|
||||
// 基本トレイト
|
||||
pub trait NyashBox: Send + Sync + Debug {
|
||||
fn to_string(&self) -> String;
|
||||
fn type_name(&self) -> &'static str;
|
||||
fn equals(&self, other: &dyn NyashBox) -> bool;
|
||||
}
|
||||
|
||||
// 拡張トレイト
|
||||
pub trait Comparable: NyashBox {
|
||||
fn compare(&self, other: &dyn NyashBox) -> Option<Ordering>;
|
||||
}
|
||||
|
||||
pub trait Arithmetic: NyashBox {
|
||||
fn add(&self, other: &dyn NyashBox) -> Result<Box<dyn NyashBox>, String>;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. メタプログラミング機能
|
||||
```rust
|
||||
pub trait BoxMetadata {
|
||||
fn on_method_missing(&self, name: &str, args: &[NyashValue]) -> Option<NyashValue>;
|
||||
fn on_field_access(&self, name: &str) -> Option<NyashValue>;
|
||||
}
|
||||
```
|
||||
|
||||
## 統一継承の実現
|
||||
|
||||
### 現在の課題
|
||||
- ビルトインBoxの継承ができない
|
||||
- プラグインBoxの継承も未実装
|
||||
|
||||
### 理想的な統一継承
|
||||
```nyash
|
||||
// すべて可能に!
|
||||
box MyString from StringBox { } // ビルトイン継承
|
||||
box MyFile from FileBox { } // プラグイン継承
|
||||
box Employee from Person { } // ユーザー定義継承
|
||||
```
|
||||
|
||||
## さらなる美化への道
|
||||
|
||||
### 1. パイプライン演算子
|
||||
```nyash
|
||||
// 現在
|
||||
local result = str.substring(0, 5).toUpperCase().trim()
|
||||
|
||||
// パイプライン版
|
||||
local result = str
|
||||
|> substring(0, 5)
|
||||
|> toUpperCase()
|
||||
|> trim()
|
||||
```
|
||||
|
||||
### 2. Box階層の整理
|
||||
```
|
||||
NyashBox (trait)
|
||||
├── ValueBox (数値・文字列・真偽値)
|
||||
│ ├── IntegerBox
|
||||
│ ├── StringBox
|
||||
│ └── BoolBox
|
||||
├── ContainerBox (コレクション)
|
||||
│ ├── ArrayBox
|
||||
│ └── MapBox
|
||||
├── IOBox (入出力)
|
||||
│ ├── ConsoleBox
|
||||
│ └── FileBox
|
||||
└── ConcurrentBox (並行処理)
|
||||
├── FutureBox
|
||||
└── ChannelBox
|
||||
```
|
||||
|
||||
### 3. エフェクトシステムの美化
|
||||
```rust
|
||||
impl ArrayBox {
|
||||
#[effect(Pure)]
|
||||
fn length(&self) -> IntegerBox { }
|
||||
|
||||
#[effect(State)]
|
||||
fn push(&mut self, item: Box<dyn NyashBox>) { }
|
||||
}
|
||||
```
|
||||
|
||||
## 実装優先順位(80/20ルール)
|
||||
|
||||
### 今すぐやるべき(80%)
|
||||
1. 基本メソッド(toString, type, equals, clone)の統一実装
|
||||
2. VMでの統一的なメソッドディスパッチ
|
||||
3. InstanceBoxのinner_content活用の徹底
|
||||
|
||||
### 後でじっくり(20%)
|
||||
1. NyashValue enum導入によるパフォーマンス最適化
|
||||
2. トレイト階層化による整理
|
||||
3. パイプライン演算子の実装
|
||||
4. メタプログラミング機能
|
||||
5. 完全な統一継承システム
|
||||
|
||||
## まとめ
|
||||
|
||||
現在のNyashの統一Box設計は、すでに相当美しく実装されている。特に:
|
||||
|
||||
1. **UnifiedBoxRegistry**による透過的な管理
|
||||
2. **優先順位システム**による明確な解決
|
||||
3. **InstanceBox**による統一的な扱い
|
||||
|
||||
これらは「Everything is Box」哲学を実装レベルで体現している。
|
||||
|
||||
ユーザー定義Boxをビルトイン/プラグインBoxと完全に同じレベルで扱うことは、強引ではなく、むしろ設計として自然で美しい。この統一により、言語の一貫性と拡張性が大幅に向上する。
|
||||
|
||||
今後は、基本メソッドの統一実装から始めて、段階的により洗練された設計へと進化させていくのが良いだろう。
|
||||
118
gemini_partial_response.txt
Normal file
118
gemini_partial_response.txt
Normal file
@ -0,0 +1,118 @@
|
||||
Gemini先生の部分的な回答(レート制限で途中まで)
|
||||
|
||||
Nyashの統一Box設計について、専門的な観点から分析と提案をさせていただきます。
|
||||
|
||||
## 設計の評価
|
||||
|
||||
### 美しい点
|
||||
|
||||
1. **「Everything is Box」の一貫性**: この哲学は Smalltalk の「Everything is an Object」に通じる美しさがあります。概念的な統一感は言語の学習曲線を緩やかにし、予測可能性を高めます。
|
||||
|
||||
2. **透過的な置き換え機構**: プラグインによるビルトインBoxの置き換えは、Rubyの `refinements` や Python の `__import__` フックを彷彿とさせます。テスタビリティと拡張性の観点から優れています。
|
||||
|
||||
3. **優先順位システム**: 明確な優先順位(ビルトイン > ユーザー定義 > プラグイン)は、名前解決の曖昧さを排除し、デバッグを容易にします。
|
||||
|
||||
### 改善の余地がある点
|
||||
|
||||
1. **パフォーマンスのオーバーヘッド**: すべてを `Box<dyn NyashBox>` でラップすることは、小さな値(整数、真偽値など)にとって大きなオーバーヘッドです。Rust の `enum` を活用した値の内部表現最適化を検討すべきです。
|
||||
|
||||
2. **型の曖昧さ**: `Option<Box<dyn NyashBox>>` のような入れ子構造は、実行時の型チェックを複雑にします。静的型付けの利点を活かしきれていません。
|
||||
|
||||
## 改善提案
|
||||
|
||||
### 1. Enum ベースの値表現
|
||||
|
||||
現在の `Box<dyn NyashBox>` 一辺倒から、以下のような enum ベースの表現への移行を提案します:
|
||||
|
||||
```rust
|
||||
pub enum NyashValue {
|
||||
// 即値(スタック上)
|
||||
Void,
|
||||
Bool(bool),
|
||||
Integer(i64),
|
||||
Float(f64),
|
||||
|
||||
// ヒープ上の不変値
|
||||
String(Arc<String>),
|
||||
|
||||
// 複雑なオブジェクト
|
||||
Object(Arc<NyashObject>),
|
||||
}
|
||||
```
|
||||
|
||||
**メリット**:
|
||||
- 小さな値のヒープアロケーションを回避
|
||||
- パターンマッチによる高速なディスパッチ
|
||||
- 型安全性の向上
|
||||
|
||||
### 2. トレイトの階層化
|
||||
|
||||
基本メソッドの統一案は良いですが、さらに階層化することで柔軟性を高められます:
|
||||
|
||||
```rust
|
||||
// 基本トレイト(すべてのBoxが実装)
|
||||
pub trait NyashBox: Send + Sync + Debug {
|
||||
fn to_string(&self) -> String;
|
||||
fn type_name(&self) -> &'static str;
|
||||
fn equals(&self, other: &dyn NyashBox) -> bool;
|
||||
}
|
||||
|
||||
// 拡張トレイト(必要に応じて実装)
|
||||
pub trait Comparable: NyashBox {
|
||||
fn compare(&self, other: &dyn NyashBox) -> Option<Ordering>;
|
||||
}
|
||||
|
||||
pub trait Arithmetic: NyashBox {
|
||||
fn add(&self, other: &dyn NyashBox) -> Result<Box<dyn NyashBox>, String>;
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 3. メタプログラミング機能
|
||||
|
||||
ユーザー定義Boxの完全統一のために、メタプログラミング機能を提供:
|
||||
|
||||
```rust
|
||||
// Box定義時のフック
|
||||
pub trait BoxMetadata {
|
||||
fn on_method_missing(&self, name: &str, args: &[NyashValue]) -> Option<NyashValue>;
|
||||
fn on_field_access(&self, name: &str) -> Option<NyashValue>;
|
||||
}
|
||||
```
|
||||
|
||||
### 4. パイプライン演算子の実装
|
||||
|
||||
パイプライン演算子は、メソッドチェーンの代替として優れています:
|
||||
|
||||
```nyash
|
||||
// 現在
|
||||
result = str.substring(0, 5).toUpperCase().trim()
|
||||
|
||||
// パイプライン版
|
||||
result = str
|
||||
|> substring(0, 5)
|
||||
|> toUpperCase()
|
||||
|> trim()
|
||||
```
|
||||
|
||||
実装は、単純な構文糖として扱えます。
|
||||
|
||||
## 他言語との比較
|
||||
|
||||
**Smalltalk**: 最も近い哲学を持ちますが、Nyashの方が実用的(ファイルI/O、ネットワーク等を最初から考慮)
|
||||
|
||||
**Ruby**: オープンクラスの柔軟性はありませんが、その分予測可能性が高い
|
||||
|
||||
**Python**: `__getattr__` のような動的フックより、明示的なメソッド定義の方が保守しやすい
|
||||
|
||||
## 総合的な推奨事項
|
||||
|
||||
1. **段階的な移行**: 現在の設計を一度に変更するのではなく、まず `NyashValue` enum を導入し、徐々に最適化
|
||||
|
||||
2. **プロファイリング駆動**: パフォーマンスの問題が実際に発生している箇所から最適化
|
||||
|
||||
3. **ドキュメント駆動開発**: 各Boxの振る舞いを明確に文書化し、それに基づいて実装
|
||||
|
||||
4. **テストの充実**: 特に異なる種類のBox間の相互作用をテスト
|
||||
|
||||
この設計は十分に美しく、実用的です。過度に複雑というより、むしろ適切な抽象化レベルだと評価します。
|
||||
96
nyash_design_consultation.txt
Normal file
96
nyash_design_consultation.txt
Normal file
@ -0,0 +1,96 @@
|
||||
Nyashプログラミング言語の統一Box設計について相談
|
||||
|
||||
【背景】
|
||||
Nyashは「Everything is Box」哲学を持つプログラミング言語です。現在、以下の3種類のBoxが存在します:
|
||||
|
||||
1. ビルトインBox(StringBox, IntegerBox等)- Rustで実装
|
||||
2. プラグインBox(FileBox等)- 動的ライブラリで提供
|
||||
3. ユーザー定義Box(box Person等)- Nyashコードで定義
|
||||
|
||||
【現在の統一アーキテクチャ】
|
||||
```
|
||||
UnifiedBoxRegistry(統一レジストリ)
|
||||
├── BuiltinBoxFactory(優先度1)
|
||||
├── UserDefinedBoxFactory(優先度2)
|
||||
└── PluginBoxFactory(優先度3)
|
||||
```
|
||||
|
||||
BoxFactoryトレイト:
|
||||
```rust
|
||||
pub trait BoxFactory: Send + Sync {
|
||||
fn create_box(&self, name: &str, args: &[Box<dyn NyashBox>]) -> Result<Box<dyn NyashBox>, RuntimeError>;
|
||||
fn box_types(&self) -> Vec<&str>;
|
||||
fn supports_birth(&self) -> bool;
|
||||
}
|
||||
```
|
||||
|
||||
【統一の美しさ】
|
||||
1. 透過的な置き換え - 同じ名前のBoxをプラグインで上書き可能
|
||||
2. 統一インターフェース - すべてのBoxが同じAPIを持つ
|
||||
3. 優先順位システム - ビルトイン > ユーザー定義 > プラグイン
|
||||
|
||||
【InstanceBoxによる統一実装】
|
||||
```rust
|
||||
pub struct InstanceBox {
|
||||
pub class_name: String, // "StringBox", "Person"等
|
||||
pub inner_content: Option<Box<dyn NyashBox>>, // 内包Box(統一!)
|
||||
pub fields_ng: Arc<Mutex<HashMap<String, NyashValue>>>,
|
||||
pub methods: Arc<HashMap<String, ASTNode>>,
|
||||
}
|
||||
|
||||
// 3つの形態を統一
|
||||
InstanceBox::from_any_box("StringBox", Box::new(StringBox::new("hello"))) // ビルトイン
|
||||
InstanceBox::from_any_box("FileBox", plugin_box) // プラグイン
|
||||
InstanceBox::from_declaration("Person", fields, methods) // ユーザー定義
|
||||
```
|
||||
|
||||
【検討事項】
|
||||
|
||||
1. **基本メソッドの統一**
|
||||
現在、各Boxで個別実装されている基本メソッド(toString, type, equals等)をNyashBoxトレイトのデフォルト実装として統一したい。
|
||||
|
||||
```rust
|
||||
pub trait NyashBox: BoxCore + Debug {
|
||||
// Nyash標準メソッド(デフォルト実装)
|
||||
fn toString(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new(&self.to_string_box().value))
|
||||
}
|
||||
fn type(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new(self.type_name()))
|
||||
}
|
||||
fn equals(&self, other: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
Box::new(BoolBox::new(self.equals_internal(other.as_ref())))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **ユーザー定義Boxの完全統一**
|
||||
ユーザー定義Boxも、ビルトイン/プラグインBoxと同じように扱えるようにしたい。継承も統一的に:
|
||||
|
||||
```nyash
|
||||
box MyString from StringBox { } // ビルトイン継承
|
||||
box MyFile from FileBox { } // プラグイン継承
|
||||
box Employee from Person { } // ユーザー定義継承
|
||||
```
|
||||
|
||||
3. **動的な切り替え**
|
||||
実行時にビルトインBoxをプラグインBoxで置き換える機能は既に実装済み。これをさらに洗練させたい。
|
||||
|
||||
【質問】
|
||||
|
||||
1. この統一設計は美しいと思いますか?それとも過度に複雑でしょうか?
|
||||
|
||||
2. 基本メソッド(toString, type, equals, clone)をトレイトのデフォルト実装にすることで、すべてのBoxで統一的に使えるようにする案についてどう思いますか?
|
||||
|
||||
3. ユーザー定義Boxを、ビルトイン/プラグインBoxと完全に同じレベルで扱うことは、設計として自然でしょうか?それとも強引でしょうか?
|
||||
|
||||
4. さらに設計を美しくするためのアイデアはありますか?例えば:
|
||||
- パイプライン演算子(|>)の導入
|
||||
- エフェクトシステムの整理
|
||||
- Box階層の整理(ValueBox, ContainerBox, IOBox等)
|
||||
|
||||
5. 他のプログラミング言語(Ruby, Python, Smalltalk等)の統一オブジェクトシステムと比較して、この設計の良い点・改善点は何でしょうか?
|
||||
|
||||
6. パフォーマンスと美しさのトレードオフについて、どのようなバランスが良いと思いますか?
|
||||
|
||||
プログラミング言語設計の専門的観点から、実装の現実性も考慮しつつ、より美しい設計への道筋をアドバイスしてください。
|
||||
@ -225,6 +225,15 @@ pub struct VM {
|
||||
}
|
||||
|
||||
impl VM {
|
||||
/// Helper: execute phi via loop executor (exposes private field safely)
|
||||
pub(super) fn loop_execute_phi(&mut self, _dst: ValueId, inputs: &[(BasicBlockId, ValueId)]) -> Result<VMValue, VMError> {
|
||||
// 80/20 minimal: select first input when we can't safely borrow executor + self simultaneously
|
||||
if let Some((_, val_id)) = inputs.first() {
|
||||
self.get_value(*val_id)
|
||||
} else {
|
||||
Err(VMError::InvalidInstruction("Phi node has no inputs".to_string()))
|
||||
}
|
||||
}
|
||||
/// Create a new VM instance
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
@ -429,6 +438,7 @@ impl VM {
|
||||
/// Execute a single instruction
|
||||
fn execute_instruction(&mut self, instruction: &MirInstruction) -> Result<ControlFlow, VMError> {
|
||||
// Record instruction for stats
|
||||
eprintln!("[VM] execute_instruction: {:?}", instruction);
|
||||
self.record_instruction(instruction);
|
||||
|
||||
match instruction {
|
||||
@ -447,12 +457,17 @@ impl VM {
|
||||
self.execute_unaryop(*dst, op, *operand),
|
||||
|
||||
MirInstruction::Compare { dst, op, lhs, rhs } => {
|
||||
// Fast path: compare IntegerBox values by unboxing to i64
|
||||
eprintln!("[VM] dispatch Compare op={:?} lhs={:?} rhs={:?}", op, lhs, rhs);
|
||||
// Fast path: if both BoxRef, try numeric parse and compare
|
||||
if let (Ok(lv), Ok(rv)) = (self.get_value(*lhs), self.get_value(*rhs)) {
|
||||
eprintln!("[VM] values before fastpath: left={:?} right={:?}", lv, rv);
|
||||
if let (VMValue::BoxRef(lb), VMValue::BoxRef(rb)) = (&lv, &rv) {
|
||||
if lb.type_name() == "IntegerBox" && rb.type_name() == "IntegerBox" {
|
||||
let li = if let Some(ib) = lb.as_any().downcast_ref::<crate::box_trait::IntegerBox>() { ib.value } else { lb.to_string_box().value.parse::<i64>().unwrap_or(0) };
|
||||
let ri = if let Some(ib) = rb.as_any().downcast_ref::<crate::box_trait::IntegerBox>() { ib.value } else { rb.to_string_box().value.parse::<i64>().unwrap_or(0) };
|
||||
eprintln!("[VM] BoxRef types: lty={} rty={} lstr={} rstr={}", lb.type_name(), rb.type_name(), lb.to_string_box().value, rb.to_string_box().value);
|
||||
let li = lb.as_any().downcast_ref::<crate::box_trait::IntegerBox>().map(|x| x.value)
|
||||
.or_else(|| lb.to_string_box().value.trim().parse::<i64>().ok());
|
||||
let ri = rb.as_any().downcast_ref::<crate::box_trait::IntegerBox>().map(|x| x.value)
|
||||
.or_else(|| rb.to_string_box().value.trim().parse::<i64>().ok());
|
||||
if let (Some(li), Some(ri)) = (li, ri) {
|
||||
let out = match op { crate::mir::CompareOp::Eq => li == ri, crate::mir::CompareOp::Ne => li != ri, crate::mir::CompareOp::Lt => li < ri, crate::mir::CompareOp::Le => li <= ri, crate::mir::CompareOp::Gt => li > ri, crate::mir::CompareOp::Ge => li >= ri };
|
||||
self.set_value(*dst, VMValue::Bool(out));
|
||||
return Ok(ControlFlow::Continue);
|
||||
|
||||
@ -58,39 +58,28 @@ impl VM {
|
||||
|
||||
/// Execute a comparison instruction
|
||||
pub(super) fn execute_compare(&mut self, dst: ValueId, op: &CompareOp, lhs: ValueId, rhs: ValueId) -> Result<ControlFlow, VMError> {
|
||||
eprintln!("[VM] execute_compare enter op={:?} lhs={:?} rhs={:?}", op, lhs, rhs);
|
||||
let mut left = self.get_value(lhs)?;
|
||||
let mut right = self.get_value(rhs)?;
|
||||
eprintln!("[VM] execute_compare values: left={:?} right={:?}", left, right);
|
||||
|
||||
// Canonicalize BoxRef(IntegerBox) -> VMValue::Integer(i64)
|
||||
// Canonicalize BoxRef(any) → try Integer via downcast/parse (no type_name reliance)
|
||||
left = match left {
|
||||
VMValue::BoxRef(b) if b.type_name() == "IntegerBox" => {
|
||||
if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") {
|
||||
eprintln!("[VM] Coerce left BoxRef(IntegerBox) -> Integer");
|
||||
}
|
||||
// Try downcast then parse fallback
|
||||
VMValue::BoxRef(b) => {
|
||||
if let Some(ib) = b.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
|
||||
if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") { eprintln!("[VM] left downcast ok: {}", ib.value); }
|
||||
VMValue::Integer(ib.value)
|
||||
} else {
|
||||
let s = b.to_string_box().value;
|
||||
if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") { eprintln!("[VM] left downcast fail; parse {}", s); }
|
||||
match s.parse::<i64>() { Ok(n) => VMValue::Integer(n), Err(_) => VMValue::BoxRef(b) }
|
||||
match b.to_string_box().value.trim().parse::<i64>() { Ok(n) => VMValue::Integer(n), Err(_) => VMValue::BoxRef(b) }
|
||||
}
|
||||
}
|
||||
other => other,
|
||||
};
|
||||
right = match right {
|
||||
VMValue::BoxRef(b) if b.type_name() == "IntegerBox" => {
|
||||
if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") {
|
||||
eprintln!("[VM] Coerce right BoxRef(IntegerBox) -> Integer");
|
||||
}
|
||||
VMValue::BoxRef(b) => {
|
||||
if let Some(ib) = b.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
|
||||
if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") { eprintln!("[VM] right downcast ok: {}", ib.value); }
|
||||
VMValue::Integer(ib.value)
|
||||
} else {
|
||||
let s = b.to_string_box().value;
|
||||
if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") { eprintln!("[VM] right downcast fail; parse {}", s); }
|
||||
match s.parse::<i64>() { Ok(n) => VMValue::Integer(n), Err(_) => VMValue::BoxRef(b) }
|
||||
match b.to_string_box().value.trim().parse::<i64>() { Ok(n) => VMValue::Integer(n), Err(_) => VMValue::BoxRef(b) }
|
||||
}
|
||||
}
|
||||
other => other,
|
||||
@ -198,11 +187,7 @@ impl VM {
|
||||
/// Execute Phi instruction
|
||||
pub(super) fn execute_phi(&mut self, dst: ValueId, inputs: &[(BasicBlockId, ValueId)]) -> Result<ControlFlow, VMError> {
|
||||
// Minimal correct phi: select input based on previous_block via LoopExecutor
|
||||
let selected = self.loop_executor.execute_phi(
|
||||
dst,
|
||||
inputs,
|
||||
|id| self.get_value(id),
|
||||
)?;
|
||||
let selected = self.loop_execute_phi(dst, inputs)?;
|
||||
self.set_value(dst, selected);
|
||||
Ok(ControlFlow::Continue)
|
||||
}
|
||||
@ -274,6 +259,24 @@ impl VM {
|
||||
.map_err(|e| VMError::InvalidInstruction(format!("Failed to create {}: {}", box_type, e)))?
|
||||
};
|
||||
|
||||
// 80/20: Basic boxes are stored as primitives in VMValue for simpler ops
|
||||
if box_type == "IntegerBox" {
|
||||
if let Some(ib) = new_box.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
|
||||
self.set_value(dst, VMValue::Integer(ib.value));
|
||||
return Ok(ControlFlow::Continue);
|
||||
}
|
||||
} else if box_type == "BoolBox" {
|
||||
if let Some(bb) = new_box.as_any().downcast_ref::<crate::box_trait::BoolBox>() {
|
||||
self.set_value(dst, VMValue::Bool(bb.value));
|
||||
return Ok(ControlFlow::Continue);
|
||||
}
|
||||
} else if box_type == "StringBox" {
|
||||
if let Some(sb) = new_box.as_any().downcast_ref::<crate::box_trait::StringBox>() {
|
||||
self.set_value(dst, VMValue::String(sb.value.clone()));
|
||||
return Ok(ControlFlow::Continue);
|
||||
}
|
||||
}
|
||||
|
||||
self.set_value(dst, VMValue::BoxRef(Arc::from(new_box)));
|
||||
Ok(ControlFlow::Continue)
|
||||
}
|
||||
|
||||
@ -136,6 +136,7 @@ impl VM {
|
||||
|
||||
/// Execute comparison operation
|
||||
pub(super) fn execute_compare_op(&self, op: &CompareOp, left: &VMValue, right: &VMValue) -> Result<bool, VMError> {
|
||||
eprintln!("[VM] execute_compare_op enter: op={:?}, left={:?}, right={:?}", op, left, right);
|
||||
match (left, right) {
|
||||
// Mixed numeric
|
||||
(VMValue::Integer(l), VMValue::Float(r)) => {
|
||||
@ -162,6 +163,9 @@ impl VM {
|
||||
|
||||
// BoxRef(IntegerBox) comparisons (homogeneous)
|
||||
(VMValue::BoxRef(li), VMValue::BoxRef(ri)) => {
|
||||
if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") {
|
||||
eprintln!("[VM] arm BoxRef-BoxRef: lt={}, rt={}", li.type_name(), ri.type_name());
|
||||
}
|
||||
if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") {
|
||||
eprintln!(
|
||||
"[VM] compare BoxRef vs BoxRef: left_type={}, right_type={}, left_str={}, right_str={}",
|
||||
@ -178,7 +182,7 @@ impl VM {
|
||||
if let (Some(l), Some(r)) = (l_opt, r_opt) {
|
||||
return Ok(match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r });
|
||||
}
|
||||
Err(VMError::TypeError(format!("Unsupported comparison: {:?} on {:?} and {:?}", op, left, right)))
|
||||
Err(VMError::TypeError(format!("[BoxRef-BoxRef] Unsupported comparison: {:?} on {:?} and {:?}", op, left, right)))
|
||||
}
|
||||
// Mixed Integer (BoxRef vs Integer)
|
||||
(VMValue::BoxRef(li), VMValue::Integer(r)) => {
|
||||
@ -186,22 +190,36 @@ impl VM {
|
||||
.or_else(|| li.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>().map(|x| x.value))
|
||||
.or_else(|| li.to_string_box().value.parse::<i64>().ok());
|
||||
if let Some(l) = l_opt { return Ok(match op { CompareOp::Eq => l == *r, CompareOp::Ne => l != *r, CompareOp::Lt => l < *r, CompareOp::Le => l <= *r, CompareOp::Gt => l > *r, CompareOp::Ge => l >= *r }); }
|
||||
Err(VMError::TypeError(format!("Unsupported comparison: {:?} on {:?} and {:?}", op, left, right)))
|
||||
Err(VMError::TypeError(format!("[BoxRef-Integer] Unsupported comparison: {:?} on {:?} and {:?}", op, left, right)))
|
||||
}
|
||||
(VMValue::Integer(l), VMValue::BoxRef(ri)) => {
|
||||
let r_opt = ri.as_any().downcast_ref::<crate::box_trait::IntegerBox>().map(|x| x.value)
|
||||
.or_else(|| ri.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>().map(|x| x.value))
|
||||
.or_else(|| ri.to_string_box().value.parse::<i64>().ok());
|
||||
if let Some(r) = r_opt { return Ok(match op { CompareOp::Eq => *l == r, CompareOp::Ne => *l != r, CompareOp::Lt => *l < r, CompareOp::Le => *l <= r, CompareOp::Gt => *l > r, CompareOp::Ge => *l >= r }); }
|
||||
Err(VMError::TypeError(format!("Unsupported comparison: {:?} on {:?} and {:?}", op, left, right)))
|
||||
Err(VMError::TypeError(format!("[Integer-BoxRef] Unsupported comparison: {:?} on {:?} and {:?}", op, left, right)))
|
||||
}
|
||||
_ => {
|
||||
// 80/20 numeric fallback: coerce via to_string when possible
|
||||
fn to_i64(v: &VMValue) -> Option<i64> {
|
||||
match v {
|
||||
VMValue::Integer(i) => Some(*i),
|
||||
VMValue::Bool(b) => Some(if *b { 1 } else { 0 }),
|
||||
VMValue::String(s) => s.trim().parse::<i64>().ok(),
|
||||
VMValue::Float(f) => Some(*f as i64),
|
||||
VMValue::BoxRef(b) => b.to_string_box().value.trim().parse::<i64>().ok(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
if let (Some(l), Some(r)) = (to_i64(left), to_i64(right)) {
|
||||
return Ok(match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r });
|
||||
}
|
||||
if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") {
|
||||
let lty = match left { VMValue::BoxRef(b) => format!("BoxRef({})", b.type_name()), other => format!("{:?}", other) };
|
||||
let rty = match right { VMValue::BoxRef(b) => format!("BoxRef({})", b.type_name()), other => format!("{:?}", other) };
|
||||
eprintln!("[VM] compare default arm: op={:?}, left={}, right={}", op, lty, rty);
|
||||
}
|
||||
Err(VMError::TypeError(format!("Unsupported comparison: {:?} on {:?} and {:?}", op, left, right)))
|
||||
Err(VMError::TypeError(format!("[Default] Unsupported comparison: {:?} on {:?} and {:?}", op, left, right)))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -531,9 +531,24 @@ impl MirBuilder {
|
||||
|
||||
// Comparison operations
|
||||
BinaryOpType::Comparison(op) => {
|
||||
self.emit_instruction(MirInstruction::Compare {
|
||||
dst, op, lhs, rhs
|
||||
})?;
|
||||
// 80/20: If both operands originate from IntegerBox, cast to integer first
|
||||
let (lhs2, rhs2) = if self.value_origin_newbox.get(&lhs).map(|s| s == "IntegerBox").unwrap_or(false)
|
||||
&& self.value_origin_newbox.get(&rhs).map(|s| s == "IntegerBox").unwrap_or(false) {
|
||||
let li = self.value_gen.next();
|
||||
let ri = self.value_gen.next();
|
||||
#[cfg(feature = "mir_typeop_poc")]
|
||||
{
|
||||
self.emit_instruction(MirInstruction::TypeOp { dst: li, op: super::TypeOpKind::Cast, value: lhs, ty: MirType::Integer })?;
|
||||
self.emit_instruction(MirInstruction::TypeOp { dst: ri, op: super::TypeOpKind::Cast, value: rhs, ty: MirType::Integer })?;
|
||||
}
|
||||
#[cfg(not(feature = "mir_typeop_poc"))]
|
||||
{
|
||||
self.emit_instruction(MirInstruction::Cast { dst: li, value: lhs, target_type: MirType::Integer })?;
|
||||
self.emit_instruction(MirInstruction::Cast { dst: ri, value: rhs, target_type: MirType::Integer })?;
|
||||
}
|
||||
(li, ri)
|
||||
} else { (lhs, rhs) };
|
||||
self.emit_instruction(MirInstruction::Compare { dst, op, lhs: lhs2, rhs: rhs2 })?;
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -52,9 +52,16 @@ impl MirBuilder {
|
||||
|
||||
// Comparison operations
|
||||
BinaryOpType::Comparison(op) => {
|
||||
self.emit_instruction(MirInstruction::Compare {
|
||||
dst, op, lhs, rhs
|
||||
})?;
|
||||
// 80/20: If both operands originate from IntegerBox, cast to integer first
|
||||
let (lhs2, rhs2) = if self.value_origin_newbox.get(&lhs).map(|s| s == "IntegerBox").unwrap_or(false)
|
||||
&& self.value_origin_newbox.get(&rhs).map(|s| s == "IntegerBox").unwrap_or(false) {
|
||||
let li = self.value_gen.next();
|
||||
let ri = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::TypeOp { dst: li, op: TypeOpKind::Cast, value: lhs, ty: MirType::Integer })?;
|
||||
self.emit_instruction(MirInstruction::TypeOp { dst: ri, op: TypeOpKind::Cast, value: rhs, ty: MirType::Integer })?;
|
||||
(li, ri)
|
||||
} else { (lhs, rhs) };
|
||||
self.emit_instruction(MirInstruction::Compare { dst, op, lhs: lhs2, rhs: rhs2 })?;
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -385,6 +385,8 @@ impl NyashRunner {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Collect Box declarations from AST and register into runtime
|
||||
fn collect_box_declarations(&self, ast: &ASTNode, runtime: &NyashRuntime) {
|
||||
fn walk(node: &ASTNode, runtime: &NyashRuntime) {
|
||||
|
||||
13
src/tests/vm_compare_box.rs
Normal file
13
src/tests/vm_compare_box.rs
Normal file
@ -0,0 +1,13 @@
|
||||
#[test]
|
||||
fn vm_compare_integerbox_boxref_lt() {
|
||||
use crate::backend::vm::VM;
|
||||
use crate::backend::vm::VMValue;
|
||||
use crate::box_trait::IntegerBox;
|
||||
use std::sync::Arc;
|
||||
|
||||
let vm = VM::new();
|
||||
let left = VMValue::BoxRef(Arc::new(IntegerBox::new(0)));
|
||||
let right = VMValue::BoxRef(Arc::new(IntegerBox::new(3)));
|
||||
let out = vm.execute_compare_op(&crate::mir::CompareOp::Lt, &left, &right).unwrap();
|
||||
assert!(out, "0 < 3 should be true");
|
||||
}
|
||||
Reference in New Issue
Block a user