📚 Phase 12: Nyashスクリプトプラグインシステム設計と埋め込みVM構想

## 主な成果
- Nyashスクリプトでプラグイン作成可能という革命的発見
- C ABI制約の分析と埋め込みVMによる解決策
- MIR/VM/JIT層での箱引数サポートの詳細分析

## ドキュメント作成
- Phase 12基本構想(README.md)
- Gemini/Codex先生の技術分析
- C ABIとの整合性問題と解決策
- 埋め込みVM実装ロードマップ
- 箱引数サポートの技術詳細

## 重要な洞察
- 制約は「リンク時にC ABI必要」のみ
- 埋め込みVMでMIRバイトコード実行により解決可能
- Nyashスクリプト→C ABIプラグイン変換が実現可能

Everything is Box → Everything is Plugin → Everything is Possible!
This commit is contained in:
Moe Charm
2025-08-30 22:52:16 +09:00
parent 7a0f9bd432
commit c13d9c045e
82 changed files with 5842 additions and 138 deletions

View File

@ -131,8 +131,8 @@ impl NyashInterpreter {
}
ASTNode::Include { filename, .. } => {
self.execute_include(filename)?;
Ok(Box::new(VoidBox::new()))
// include式: 最初のstatic boxを返す
self.execute_include_expr(filename)
}
ASTNode::FromCall { parent, method, arguments, .. } => {

View File

@ -10,14 +10,53 @@ use super::*;
use crate::parser::NyashParser;
impl NyashInterpreter {
/// Resolve include path using nyash.toml [include.roots]
fn resolve_include_path(&self, filename: &str, caller_dir: Option<&str>) -> String {
// If explicit relative path, resolve relative to caller when provided
if filename.starts_with("./") || filename.starts_with("../") {
return filename.to_string();
}
// Try nyash.toml roots: key/path where key is first segment before '/'
let parts: Vec<&str> = filename.splitn(2, '/').collect();
if parts.len() == 2 {
let root = parts[0];
let rest = parts[1];
let cfg_path = "nyash.toml";
if let Ok(toml_str) = std::fs::read_to_string(cfg_path) {
if let Ok(toml_val) = toml::from_str::<toml::Value>(&toml_str) {
if let Some(include) = toml_val.get("include") {
if let Some(roots) = include.get("roots").and_then(|v| v.as_table()) {
if let Some(root_path_val) = roots.get(root).and_then(|v| v.as_str()) {
let mut base = root_path_val.to_string();
if !base.ends_with('/') && !base.ends_with('\\') { base.push('/'); }
let joined = format!("{}{}", base, rest);
return joined;
}
}
}
}
}
}
// Fallback: if caller_dir provided, join relative
if let Some(dir) = caller_dir {
if !filename.starts_with('/') && !filename.contains(":\\") && !filename.contains(":/") {
return format!("{}/{}", dir.trim_end_matches('/'), filename);
}
}
// Default to ./filename
format!("./{}", filename)
}
/// include文を実行ファイル読み込み・パース・実行 - File inclusion system
pub(super) fn execute_include(&mut self, filename: &str) -> Result<(), RuntimeError> {
// パス正規化(簡易版
let canonical_path = if filename.starts_with("./") || filename.starts_with("../") {
filename.to_string()
} else {
format!("./{}", filename)
};
// パス解決nyash.toml include.roots + 相対
let mut canonical_path = self.resolve_include_path(filename, None);
// 拡張子補完・index対応
if std::path::Path::new(&canonical_path).is_dir() {
let idx = format!("{}/index.nyash", canonical_path.trim_end_matches('/'));
canonical_path = idx;
} else if std::path::Path::new(&canonical_path).extension().is_none() {
canonical_path.push_str(".nyash");
}
// 重複読み込みチェック
if self.shared.included_files.lock().unwrap().contains(&canonical_path) {
@ -45,6 +84,72 @@ impl NyashInterpreter {
Ok(())
}
/// include式を実行ファイルを評価し、最初のstatic boxを返す
pub(super) fn execute_include_expr(&mut self, filename: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
// パス解決nyash.toml include.roots + 相対)
let mut canonical_path = self.resolve_include_path(filename, None);
// 拡張子補完・index対応
if std::path::Path::new(&canonical_path).is_dir() {
let idx = format!("{}/index.nyash", canonical_path.trim_end_matches('/'));
canonical_path = idx;
} else if std::path::Path::new(&canonical_path).extension().is_none() {
canonical_path.push_str(".nyash");
}
// ファイル読み込みstatic box名検出用
let content = std::fs::read_to_string(&canonical_path)
.map_err(|e| RuntimeError::InvalidOperation {
message: format!("Failed to read file '{}': {}", filename, e),
})?;
// パースして最初のstatic box名を特定
let ast = NyashParser::parse_from_string(&content)
.map_err(|e| RuntimeError::InvalidOperation {
message: format!("Parse error in '{}': {:?}", filename, e),
})?;
let mut static_names: Vec<String> = Vec::new();
if let crate::ast::ASTNode::Program { statements, .. } = &ast {
for st in statements {
if let crate::ast::ASTNode::BoxDeclaration { name, is_static, .. } = st {
if *is_static { static_names.push(name.clone()); }
}
}
}
if static_names.is_empty() {
return Err(RuntimeError::InvalidOperation { message: format!("include target '{}' does not define a static box", filename) });
}
if static_names.len() > 1 {
return Err(RuntimeError::InvalidOperation { message: format!("include target '{}' defines multiple static boxes; exactly one is required", filename) });
}
let box_name = static_names.remove(0);
// まだ未読なら評価(重複読み込みはスキップ)
let already = {
let set = self.shared.included_files.lock().unwrap();
set.contains(&canonical_path)
};
if !already {
self.shared.included_files.lock().unwrap().insert(canonical_path);
self.execute(ast)?;
}
// static boxを初期化・取得して返す
self.ensure_static_box_initialized(&box_name)?;
// statics名前空間からインスタンスを取り出す
let global_box = self.shared.global_box.lock()
.map_err(|_| RuntimeError::RuntimeFailure { message: "Failed to acquire global box lock".to_string() })?;
let statics = global_box.get_field("statics").ok_or(RuntimeError::TypeError { message: "statics namespace not found in GlobalBox".to_string() })?;
let statics_inst = statics.as_any().downcast_ref::<crate::instance_v2::InstanceBox>()
.ok_or(RuntimeError::TypeError { message: "statics field is not an InstanceBox".to_string() })?;
let value = statics_inst.get_field(&box_name)
.ok_or(RuntimeError::InvalidOperation { message: format!("Static box '{}' not found after include", box_name) })?;
Ok((*value).clone_or_share())
}
/// Arrow演算子を実行: sender >> receiver - Channel communication
pub(super) fn execute_arrow(&mut self, sender: &ASTNode, receiver: &ASTNode)
-> Result<Box<dyn NyashBox>, RuntimeError> {
@ -111,4 +216,4 @@ impl NyashInterpreter {
Ok(Box::new(VoidBox::new()))
}
}
}