feat: Phase 9.75g-0 BID-FFI完了 + Phase 9.8準備

- BID-FFI基盤実装完了(プラグインシステム動作確認)
- Phase 8.6 VM性能改善完了(50.94倍高速化達成)
- Phase 9.78 LLVM PoC基盤完成
- Phase 9.8 BIDレジストリ準備(nyash.toml活用戦略)
- ビルドエラー修正、警告は後で対応

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Moe Charm
2025-08-18 21:10:06 +09:00
parent 012fc1930f
commit 5d1a140919
11 changed files with 1332 additions and 27 deletions

View File

@ -111,41 +111,59 @@ read = {
- [phase_8_6_vm_performance_improvement.md](../予定/native-plan/issues/phase_8_6_vm_performance_improvement.md) - 詳細技術分析 - [phase_8_6_vm_performance_improvement.md](../予定/native-plan/issues/phase_8_6_vm_performance_improvement.md) - 詳細技術分析
- [copilot_issues.txt](../予定/native-plan/copilot_issues.txt) - 全体開発計画 - [copilot_issues.txt](../予定/native-plan/copilot_issues.txt) - 全体開発計画
## 📋 **今日の重要決定事項2025年8月21日** ## 🔧 **現在進行中ビルトインBoxプラグイン化プロジェクト**2025-08-18開始
### **1. Phase 9.8戦略転換** ### **目的**
- ✅ nyash.tomlに既存の型情報が豊富に存在することを発見 - **ビルド時間短縮**: 3分 → 30秒以下
- ✅ BID用の新規YAMLファイル不要と判断 - **バイナリサイズ削減**: 最小構成で500KB以下
- ✅ nyash.toml拡張によるBID機能実装を決定 - **保守性向上**: 各プラグイン独立開発
- ✅ GitHub Issue #116作成Phase 9.8実装計画)
### **2. 技術的洞察JIT vs AOT** ### **対象Box13種類**
- **MIRの存在により難易度が同等に**
- VM最適化でネイティブ速度に迫る可能性
- 将来: VM JIT化も選択肢に
### **3. 開発優先順位の更新**
``` ```
1. ✅ Phase 8.6 VM性能改善完了50.94倍達成 Phase 1: ネットワーク系HttpBox系、SocketBox
2. ✅ Phase 9.78 LLVM PoC基盤MIR修正完了 Phase 2: GUI系EguiBox、Canvas系、Web系
3. 🔄 Phase 9.8 BIDレジストリnyash.toml拡張方式 Phase 3: 特殊用途系AudioBox、QRBox、StreamBox等
4. → Phase 9.9 権限モデルFileBoxで実証
5. → Phase 10 LLVM本格実装将来検討
``` ```
### **4. Windows戦略の具体化** ### **進捗状況**
- Bitcodeキャッシュで1回生成→全OS対応 - ✅ プラグイン移行依頼書作成(`docs/plugin-migration-request.md`
- mingw-gnuで即座にWindows対応可能 - ✅ CopilotのBID変換コード抽出`src/bid-converter-copilot/`
- APEは小規模ツール専用として位置づけ - ✅ CopilotのBIDコード生成機能抽出`src/bid-codegen-from-copilot/`
- 🔄 HttpBoxプラグイン化作業をCopilotに依頼中
## 📋 **今日の重要決定事項2025年8月18日**
### **1. CopilotのPR管理戦略**
- ✅ 大規模変更1,735行を含むPR #117をrevert
- ✅ 必要な新規ファイルのみ選択的に抽出・保存
- ✅ cli.rs/runner.rsへの大幅変更は取り込まない方針
### **2. Copilot成果物の保存**
- **BID変換部分**: `src/bid-converter-copilot/` TLV、型変換
- **コード生成部分**: `src/bid-codegen-from-copilot/` (各言語向け生成)
- **活用方針**: 将来的にnyash2.toml実装時に参考資料として使用
### **3. 開発優先順位の明確化**
```
1. 🔄 ビルトインBoxプラグイン化HttpBox系から開始
2. → Phase 9.8 BIDレジストリnyash.toml拡張方式
3. → Phase 9.9 権限モデルFileBoxで実証
4. → Phase 10 LLVM本格実装将来検討
```
### **4. 選択的pull戦略の確立**
- **原則**: 必要な機能だけを取り込む
- **判断基準**: 現在の目標との関連性、複雑性、保守性
- **実践**: 新規ファイルは別フォルダに保存、既存ファイルの大幅変更は慎重に
--- ---
**最終更新**: 2025年8月21日 **最終更新**: 2025年8月18
**次回レビュー**: Phase 9.8実装開始 **次回レビュー**: HttpBoxプラグイン完成
**開発状況**: Phase 9.75g-0完了 → Phase 8.6完了 → Phase 9.78基盤完了 → Phase 9.8開始 **開発状況**: ビルトインBoxプラグイン化進行中
### 🎯 **次のアクション** ### 🎯 **次のアクション**
1. nyash.toml拡張仕様の設計 1. HttpBoxプラグイン化の完成待ちCopilot作業中
2. VMバックエンドでのFileBox統合テスト準備 2. plugin-testerでの動作確認
3. 権限モデルPhase 9.9)の実装計画 3. 次のプラグイン化対象EguiBox等の準備

View File

@ -0,0 +1,39 @@
# BID Code Generation from Copilot
このフォルダには、CopilotさんがPR #117で実装したBIDコード生成機能を保存しています
## 📦 含まれるファイル
### コア機能
- **schema.rs**: BIDスキーマ定義YAML/JSONパース
- **codegen/generator.rs**: コード生成エンジン
- **codegen/mod.rs**: モジュール定義
### 各言語向け生成ターゲット
- **codegen/targets/vm.rs**: VM用バイトコード生成
- **codegen/targets/wasm.rs**: WebAssembly生成最も詳細
- **codegen/targets/llvm.rs**: LLVM IR生成スタブ
- **codegen/targets/python.rs**: Pythonバインディングスタブ
- **codegen/targets/typescript.rs**: TypeScript定義スタブ
## 🎯 用途
将来的に以下の用途で活用可能:
1. **プラグインの多言語対応**:
- C以外の言語でプラグイン作成
- 各言語向けバインディング自動生成
2. **バックエンド統合**:
- VM/WASM/LLVM向けの統一インターフェース
- 外部関数定義の一元管理
3. **型安全性向上**:
- スキーマベースの型チェック
- コンパイル時の整合性検証
## 📝 メモ
- 現在は使用していない既存のnyash.tomlベースが動作中
- cli.rsとrunner.rsへの大幅変更は含まれていない別フォルダ保存
- 必要に応じて段階的に統合可能

View File

@ -0,0 +1,239 @@
/*!
* Code Generator - Main entry point for BID code generation
*/
use crate::bid::{BidDefinition, BidError, BidResult};
use std::path::{Path, PathBuf};
use std::fs;
/// Code generation target
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CodeGenTarget {
Wasm,
VM,
LLVM,
TypeScript,
Python,
}
impl CodeGenTarget {
/// Parse target from string
pub fn from_str(target: &str) -> Result<Self, BidError> {
match target.to_lowercase().as_str() {
"wasm" => Ok(CodeGenTarget::Wasm),
"vm" => Ok(CodeGenTarget::VM),
"llvm" => Ok(CodeGenTarget::LLVM),
"ts" | "typescript" => Ok(CodeGenTarget::TypeScript),
"py" | "python" => Ok(CodeGenTarget::Python),
_ => Err(BidError::UnsupportedTarget(target.to_string())),
}
}
/// Get the target name as string
pub fn as_str(&self) -> &'static str {
match self {
CodeGenTarget::Wasm => "wasm",
CodeGenTarget::VM => "vm",
CodeGenTarget::LLVM => "llvm",
CodeGenTarget::TypeScript => "ts",
CodeGenTarget::Python => "py",
}
}
}
/// Code generation options
pub struct CodeGenOptions {
pub target: CodeGenTarget,
pub output_dir: PathBuf,
pub force: bool,
pub dry_run: bool,
}
impl CodeGenOptions {
/// Create new options
pub fn new(target: CodeGenTarget, output_dir: PathBuf) -> Self {
Self {
target,
output_dir,
force: false,
dry_run: false,
}
}
/// Set force overwrite
pub fn with_force(mut self, force: bool) -> Self {
self.force = force;
self
}
/// Set dry run mode
pub fn with_dry_run(mut self, dry_run: bool) -> Self {
self.dry_run = dry_run;
self
}
}
/// Generated file
pub struct GeneratedFile {
pub path: PathBuf,
pub content: String,
}
impl GeneratedFile {
pub fn new(path: PathBuf, content: String) -> Self {
Self { path, content }
}
}
/// Code generation result
pub struct CodeGenResult {
pub files: Vec<GeneratedFile>,
pub target: CodeGenTarget,
pub bid_name: String,
}
/// Main code generator
pub struct CodeGenerator;
impl CodeGenerator {
/// Generate code from BID definition
pub fn generate(bid: &BidDefinition, options: &CodeGenOptions) -> BidResult<CodeGenResult> {
let bid_name = bid.name();
// Generate based on target
let files = match options.target {
CodeGenTarget::Wasm => Self::generate_wasm(bid, options)?,
CodeGenTarget::VM => Self::generate_vm(bid, options)?,
CodeGenTarget::LLVM => Self::generate_llvm(bid, options)?,
CodeGenTarget::TypeScript => Self::generate_typescript(bid, options)?,
CodeGenTarget::Python => Self::generate_python(bid, options)?,
};
// Write files unless dry run
if !options.dry_run {
Self::write_files(&files, options)?;
}
Ok(CodeGenResult {
files,
target: options.target.clone(),
bid_name,
})
}
/// Generate WASM import declarations
fn generate_wasm(bid: &BidDefinition, options: &CodeGenOptions) -> BidResult<Vec<GeneratedFile>> {
use super::targets::wasm::WasmGenerator;
WasmGenerator::generate(bid, options)
}
/// Generate VM function table definitions
fn generate_vm(bid: &BidDefinition, options: &CodeGenOptions) -> BidResult<Vec<GeneratedFile>> {
use super::targets::vm::VmGenerator;
VmGenerator::generate(bid, options)
}
/// Generate LLVM declare statements
fn generate_llvm(bid: &BidDefinition, options: &CodeGenOptions) -> BidResult<Vec<GeneratedFile>> {
use super::targets::llvm::LlvmGenerator;
LlvmGenerator::generate(bid, options)
}
/// Generate TypeScript wrapper
fn generate_typescript(bid: &BidDefinition, options: &CodeGenOptions) -> BidResult<Vec<GeneratedFile>> {
use super::targets::typescript::TypeScriptGenerator;
TypeScriptGenerator::generate(bid, options)
}
/// Generate Python wrapper
fn generate_python(bid: &BidDefinition, options: &CodeGenOptions) -> BidResult<Vec<GeneratedFile>> {
use super::targets::python::PythonGenerator;
PythonGenerator::generate(bid, options)
}
/// Write generated files to disk
fn write_files(files: &[GeneratedFile], options: &CodeGenOptions) -> BidResult<()> {
for file in files {
// Check if file exists and force is not set
if file.path.exists() && !options.force {
return Err(BidError::IoError(format!(
"File already exists: {} (use --force to overwrite)",
file.path.display()
)));
}
// Create parent directories
if let Some(parent) = file.path.parent() {
fs::create_dir_all(parent)
.map_err(|e| BidError::IoError(format!(
"Failed to create directory {}: {}",
parent.display(),
e
)))?;
}
// Write file
fs::write(&file.path, &file.content)
.map_err(|e| BidError::IoError(format!(
"Failed to write file {}: {}",
file.path.display(),
e
)))?;
}
Ok(())
}
/// Preview generated files (for dry run)
pub fn preview_files(result: &CodeGenResult) {
println!("📁 Generated files for target '{}' (BID: {}):",
result.target.as_str(), result.bid_name);
println!();
for file in &result.files {
println!("📄 {}", file.path.display());
println!(" {} lines, {} bytes",
file.content.lines().count(),
file.content.len());
// Show first few lines
let lines: Vec<&str> = file.content.lines().take(5).collect();
for line in lines {
println!("{}", line);
}
if file.content.lines().count() > 5 {
println!(" │ ... ({} more lines)", file.content.lines().count() - 5);
}
println!();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_code_gen_target_from_str() {
assert_eq!(CodeGenTarget::from_str("wasm").unwrap(), CodeGenTarget::Wasm);
assert_eq!(CodeGenTarget::from_str("vm").unwrap(), CodeGenTarget::VM);
assert_eq!(CodeGenTarget::from_str("llvm").unwrap(), CodeGenTarget::LLVM);
assert_eq!(CodeGenTarget::from_str("ts").unwrap(), CodeGenTarget::TypeScript);
assert_eq!(CodeGenTarget::from_str("typescript").unwrap(), CodeGenTarget::TypeScript);
assert_eq!(CodeGenTarget::from_str("py").unwrap(), CodeGenTarget::Python);
assert_eq!(CodeGenTarget::from_str("python").unwrap(), CodeGenTarget::Python);
assert!(CodeGenTarget::from_str("invalid").is_err());
}
#[test]
fn test_code_gen_target_as_str() {
assert_eq!(CodeGenTarget::Wasm.as_str(), "wasm");
assert_eq!(CodeGenTarget::VM.as_str(), "vm");
assert_eq!(CodeGenTarget::LLVM.as_str(), "llvm");
assert_eq!(CodeGenTarget::TypeScript.as_str(), "ts");
assert_eq!(CodeGenTarget::Python.as_str(), "py");
}
}

View File

@ -0,0 +1,9 @@
/*!
* Code Generation Module - Generate code from BID definitions
*/
pub mod generator;
pub mod targets;
pub use generator::*;
pub use targets::*;

View File

@ -0,0 +1,17 @@
/*!
* LLVM Target Generator - Generate LLVM IR declarations
*/
use crate::bid::{BidDefinition, BidResult};
use crate::bid::codegen::{CodeGenOptions, GeneratedFile};
pub struct LlvmGenerator;
impl LlvmGenerator {
/// Generate LLVM declarations
pub fn generate(bid: &BidDefinition, _options: &CodeGenOptions) -> BidResult<Vec<GeneratedFile>> {
// TODO: Implement LLVM code generation
println!("🚧 LLVM code generation not yet implemented for {}", bid.name());
Ok(vec![])
}
}

View File

@ -0,0 +1,9 @@
/*!
* Target-specific code generators
*/
pub mod wasm;
pub mod vm;
pub mod llvm;
pub mod typescript;
pub mod python;

View File

@ -0,0 +1,17 @@
/*!
* Python Target Generator - Generate Python FFI wrappers
*/
use crate::bid::{BidDefinition, BidResult};
use crate::bid::codegen::{CodeGenOptions, GeneratedFile};
pub struct PythonGenerator;
impl PythonGenerator {
/// Generate Python wrappers
pub fn generate(bid: &BidDefinition, _options: &CodeGenOptions) -> BidResult<Vec<GeneratedFile>> {
// TODO: Implement Python code generation
println!("🚧 Python code generation not yet implemented for {}", bid.name());
Ok(vec![])
}
}

View File

@ -0,0 +1,17 @@
/*!
* TypeScript Target Generator - Generate TypeScript FFI wrappers
*/
use crate::bid::{BidDefinition, BidResult};
use crate::bid::codegen::{CodeGenOptions, GeneratedFile};
pub struct TypeScriptGenerator;
impl TypeScriptGenerator {
/// Generate TypeScript wrappers
pub fn generate(bid: &BidDefinition, _options: &CodeGenOptions) -> BidResult<Vec<GeneratedFile>> {
// TODO: Implement TypeScript code generation
println!("🚧 TypeScript code generation not yet implemented for {}", bid.name());
Ok(vec![])
}
}

View File

@ -0,0 +1,296 @@
/*!
* VM Target Generator - Generate VM function table definitions
*/
use crate::bid::{BidDefinition, BidInterface, BidMethod, BidResult};
use crate::bid::codegen::{CodeGenOptions, GeneratedFile};
use std::path::PathBuf;
pub struct VmGenerator;
impl VmGenerator {
/// Generate VM function table definitions
pub fn generate(bid: &BidDefinition, options: &CodeGenOptions) -> BidResult<Vec<GeneratedFile>> {
let bid_name = bid.name();
let mut files = Vec::new();
// Generate Rust function table definition
let rust_content = Self::generate_rust_function_table(bid)?;
let rust_path = options.output_dir.join(format!("{}_vm_table.rs", bid_name));
files.push(GeneratedFile::new(rust_path, rust_content));
// Generate dispatcher implementation
let dispatcher_content = Self::generate_dispatcher(bid)?;
let dispatcher_path = options.output_dir.join(format!("{}_vm_dispatcher.rs", bid_name));
files.push(GeneratedFile::new(dispatcher_path, dispatcher_content));
// Generate README
let readme_content = Self::generate_readme(bid)?;
let readme_path = options.output_dir.join("README.md");
files.push(GeneratedFile::new(readme_path, readme_content));
Ok(files)
}
/// Generate Rust function table definition
fn generate_rust_function_table(bid: &BidDefinition) -> BidResult<String> {
let bid_name = bid.name();
let mut content = String::new();
content.push_str(&format!(
"// VM function table for {}\n",
bid_name
));
content.push_str("// Generated by Nyash BID code generator\n\n");
content.push_str("use crate::bid::{{BidHandle, BidResult, BidRuntimeError}};\n");
content.push_str("use crate::backend::vm::{{VM, ValueId}};\n");
content.push_str("use std::collections::HashMap;\n\n");
// Generate function type definitions
content.push_str(&format!("/// Function table for {} interface\n", bid_name));
content.push_str(&format!("pub struct {}VmTable {{\n", Self::pascal_case(&bid_name)));
content.push_str(" functions: HashMap<String, VmFunction>,\n");
content.push_str("}\n\n");
content.push_str("/// VM function pointer type\n");
content.push_str("type VmFunction = fn(&mut VM, &[ValueId]) -> BidResult<Option<ValueId>>;\n\n");
content.push_str(&format!("impl {}VmTable {{\n", Self::pascal_case(&bid_name)));
content.push_str(" /// Create new function table\n");
content.push_str(" pub fn new() -> Self {\n");
content.push_str(" let mut functions = HashMap::new();\n\n");
// Add function mappings
for interface in &bid.interfaces {
for method in &interface.methods {
let function_name = format!("{}.{}", interface.name, method.name);
let impl_name = format!("impl_{}", method.name.replace("-", "_"));
content.push_str(&format!(
" functions.insert(\"{}\".to_string(), Self::{});\n",
function_name, impl_name
));
}
}
content.push_str("\n Self { functions }\n");
content.push_str(" }\n\n");
content.push_str(" /// Call a function by name\n");
content.push_str(" pub fn call(&self, name: &str, vm: &mut VM, args: &[ValueId]) -> BidResult<Option<ValueId>> {\n");
content.push_str(" if let Some(func) = self.functions.get(name) {\n");
content.push_str(" func(vm, args)\n");
content.push_str(" } else {\n");
content.push_str(" Err(crate::bid::BidError::invalid_method())\n");
content.push_str(" }\n");
content.push_str(" }\n\n");
// Generate function implementations
for interface in &bid.interfaces {
content.push_str(&format!(" // {} methods\n", interface.name));
for method in &interface.methods {
let impl_function = Self::generate_vm_function_impl(interface, method)?;
content.push_str(&impl_function);
}
content.push_str("\n");
}
content.push_str("}\n\n");
// Generate integration helper
content.push_str("/// Integration with VM\n");
content.push_str("pub fn register_vm_functions(vm: &mut VM) {\n");
content.push_str(&format!(" let table = {}VmTable::new();\n", Self::pascal_case(&bid_name)));
content.push_str(" \n");
content.push_str(" // Register each function with the VM\n");
content.push_str(" // TODO: Integrate with VM's external function registry\n");
content.push_str(" // vm.register_external_function_table(table);\n");
content.push_str("}\n");
Ok(content)
}
/// Generate VM function implementation
fn generate_vm_function_impl(interface: &BidInterface, method: &BidMethod) -> BidResult<String> {
let impl_name = format!("impl_{}", method.name.replace("-", "_"));
let mut content = String::new();
content.push_str(&format!(" /// Implementation of {}.{}\n", interface.name, method.name));
content.push_str(&format!(" fn {}(vm: &mut VM, args: &[ValueId]) -> BidResult<Option<ValueId>> {{\n", impl_name));
// Parameter validation
content.push_str(&format!(" // Validate argument count\n"));
content.push_str(&format!(" if args.len() != {} {{\n", method.params.len()));
content.push_str(" return Err(crate::bid::BidError::invalid_args());\n");
content.push_str(" }\n\n");
// Extract parameters
for (i, param) in method.params.iter().enumerate() {
let param_name = param.get_name().unwrap_or_else(|| format!("param_{}", i));
content.push_str(&format!(" let {} = &args[{}]; // {}\n",
param_name, i, param.get_type()));
}
content.push_str("\n // TODO: Implement actual method logic\n");
content.push_str(&format!(" println!(\"VM: Called {}.{}({{:?}})\", args);\n",
interface.name, method.name));
// Return value
if method.returns.is_void() {
content.push_str("\n Ok(None) // void return\n");
} else {
content.push_str(&format!("\n // TODO: Return proper {} value\n", method.returns.type_name()));
content.push_str(" let result_id = vm.allocate_value(crate::value::NyashValue::Integer(0));\n");
content.push_str(" Ok(Some(result_id))\n");
}
content.push_str(" }\n\n");
Ok(content)
}
/// Generate dispatcher implementation
fn generate_dispatcher(bid: &BidDefinition) -> BidResult<String> {
let bid_name = bid.name();
let mut content = String::new();
content.push_str(&format!(
"// VM dispatcher for {}\n",
bid_name
));
content.push_str("// Generated by Nyash BID code generator\n\n");
content.push_str("use crate::backend::vm::VM;\n");
content.push_str("use crate::mir::MirInstruction;\n");
content.push_str("use crate::bid::BidResult;\n\n");
content.push_str(&format!("/// Dispatcher for {} VM calls\n", bid_name));
content.push_str(&format!("pub struct {}VmDispatcher;\n\n", Self::pascal_case(&bid_name)));
content.push_str(&format!("impl {}VmDispatcher {{\n", Self::pascal_case(&bid_name)));
content.push_str(" /// Handle ExternCall MIR instruction\n");
content.push_str(" pub fn handle_extern_call(\n");
content.push_str(" vm: &mut VM,\n");
content.push_str(" interface: &str,\n");
content.push_str(" method: &str,\n");
content.push_str(" args: &[crate::backend::vm::ValueId],\n");
content.push_str(" dst: Option<crate::backend::vm::ValueId>\n");
content.push_str(" ) -> BidResult<()> {\n");
content.push_str(" let function_name = format!(\"{}.{}\", interface, method);\n");
content.push_str(" \n");
content.push_str(" match function_name.as_str() {\n");
// Generate match arms for each method
for interface in &bid.interfaces {
for method in &interface.methods {
let function_name = format!("{}.{}", interface.name, method.name);
content.push_str(&format!(" \"{}\" => {{\n", function_name));
content.push_str(&format!(" // TODO: Call actual {} implementation\n", method.name));
content.push_str(" println!(\"VM Dispatcher: {} called\");\n");
if !method.returns.is_void() {
content.push_str(" if let Some(dst_id) = dst {\n");
content.push_str(" // Set return value\n");
content.push_str(" vm.set_value(dst_id, crate::value::NyashValue::Integer(0));\n");
content.push_str(" }\n");
}
content.push_str(" Ok(())\n");
content.push_str(" },\n");
}
}
content.push_str(" _ => {\n");
content.push_str(" Err(crate::bid::BidError::invalid_method())\n");
content.push_str(" }\n");
content.push_str(" }\n");
content.push_str(" }\n");
content.push_str("}\n");
Ok(content)
}
/// Generate README
fn generate_readme(bid: &BidDefinition) -> BidResult<String> {
let bid_name = bid.name();
let mut content = String::new();
content.push_str(&format!("# {} - VM Integration\n\n", bid_name));
content.push_str("Generated by Nyash BID code generator for VM target.\n\n");
content.push_str("## Files\n\n");
content.push_str(&format!("- `{}_vm_table.rs` - VM function table definitions\n", bid_name));
content.push_str(&format!("- `{}_vm_dispatcher.rs` - VM dispatcher implementation\n", bid_name));
content.push_str("- `README.md` - This file\n\n");
content.push_str("## Integration\n\n");
content.push_str("1. Add the generated files to your VM backend:\n");
content.push_str(" ```rust\n");
content.push_str(&format!(" mod {}_vm_table;\n", bid_name));
content.push_str(&format!(" mod {}_vm_dispatcher;\n", bid_name));
content.push_str(" ```\n\n");
content.push_str("2. Register the function table:\n");
content.push_str(" ```rust\n");
content.push_str(&format!(" {}::register_vm_functions(&mut vm);\n", bid_name));
content.push_str(" ```\n\n");
content.push_str("3. Handle ExternCall instructions:\n");
content.push_str(" ```rust\n");
content.push_str(" match instruction {\n");
content.push_str(" MirInstruction::ExternCall { interface, method, args, dst } => {\n");
content.push_str(&format!(" {}VmDispatcher::handle_extern_call(\n", Self::pascal_case(&bid_name)));
content.push_str(" &mut vm, interface, method, args, *dst\n");
content.push_str(" )?\n");
content.push_str(" }\n");
content.push_str(" // ... other instructions\n");
content.push_str(" }\n");
content.push_str(" ```\n\n");
content.push_str("## Available Functions\n\n");
for interface in &bid.interfaces {
content.push_str(&format!("### {}\n\n", interface.name));
for method in &interface.methods {
content.push_str(&format!("- `{}(", method.name));
let param_strs: Vec<String> = method.params.iter().map(|p| {
format!("{}: {}",
p.get_name().unwrap_or_else(|| "param".to_string()),
p.get_type())
}).collect();
content.push_str(&param_strs.join(", "));
content.push_str(&format!(")` → `{}`\n", method.returns.type_name()));
}
content.push_str("\n");
}
Ok(content)
}
/// Convert snake_case to PascalCase
fn pascal_case(s: &str) -> String {
s.split('_')
.map(|word| {
let mut chars = word.chars();
match chars.next() {
None => String::new(),
Some(first) => first.to_uppercase().collect::<String>() + &chars.as_str().to_lowercase(),
}
})
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pascal_case() {
assert_eq!(VmGenerator::pascal_case("console"), "Console");
assert_eq!(VmGenerator::pascal_case("file_box"), "FileBox");
assert_eq!(VmGenerator::pascal_case("my_long_name"), "MyLongName");
}
}

View File

@ -0,0 +1,357 @@
/*!
* WASM Target Generator - Generate WebAssembly import declarations
*/
use crate::bid::{BidDefinition, BidInterface, BidMethod, BidParameter, BidResult, BidError};
use crate::bid::codegen::{CodeGenOptions, GeneratedFile};
use std::path::PathBuf;
pub struct WasmGenerator;
impl WasmGenerator {
/// Generate WASM import declarations and host implementation templates
pub fn generate(bid: &BidDefinition, options: &CodeGenOptions) -> BidResult<Vec<GeneratedFile>> {
let bid_name = bid.name();
let mut files = Vec::new();
// Generate WAT import declarations
let wat_content = Self::generate_wat_imports(bid)?;
let wat_path = options.output_dir.join(format!("{}_imports.wat", bid_name));
files.push(GeneratedFile::new(wat_path, wat_content));
// Generate JavaScript host implementation template
let js_content = Self::generate_js_host_template(bid)?;
let js_path = options.output_dir.join(format!("{}_host.js", bid_name));
files.push(GeneratedFile::new(js_path, js_content));
// Generate README with usage instructions
let readme_content = Self::generate_readme(bid)?;
let readme_path = options.output_dir.join("README.md");
files.push(GeneratedFile::new(readme_path, readme_content));
Ok(files)
}
/// Generate WAT import declarations
fn generate_wat_imports(bid: &BidDefinition) -> BidResult<String> {
let mut content = String::new();
content.push_str(&format!(
";; WebAssembly import declarations for {}\n",
bid.name()
));
content.push_str(";; Generated by Nyash BID code generator\n\n");
content.push_str("(module\n");
for interface in &bid.interfaces {
content.push_str(&format!(" ;; Interface: {}\n", interface.name));
for method in &interface.methods {
let wasm_import = Self::generate_wasm_import(interface, method)?;
content.push_str(&format!(" {}\n", wasm_import));
}
content.push_str("\n");
}
content.push_str(" ;; Your WASM functions go here\n");
content.push_str(" ;; Example:\n");
content.push_str(" ;; (func $main (export \"main\")\n");
content.push_str(" ;; (call $env.console.log (i32.const 0) (i32.const 13)) ;; \"Hello, World!\"\n");
content.push_str(" ;; )\n\n");
content.push_str(" ;; Memory for string data\n");
content.push_str(" (memory $memory 1)\n");
content.push_str(" (export \"memory\" (memory $memory))\n");
content.push_str(")\n");
Ok(content)
}
/// Generate single WASM import declaration
fn generate_wasm_import(interface: &BidInterface, method: &BidMethod) -> BidResult<String> {
let import_name = format!("{}.{}", interface.name, method.name);
let func_name = format!("${}", import_name);
// Convert parameters to WASM types
let mut param_types = Vec::new();
for param in &method.params {
let wasm_type = Self::bid_type_to_wasm_type(&param.get_type())?;
param_types.push(wasm_type);
}
// Convert return type
let return_type = if method.returns.is_void() {
String::new()
} else {
format!(" (result {})", Self::bid_type_to_wasm_type(&method.returns.type_name())?)
};
// Build parameter list
let params = if param_types.is_empty() {
String::new()
} else {
format!(" (param {})", param_types.join(" "))
};
Ok(format!(
"(import \"env\" \"{}\" (func {}{}{}))",
import_name,
func_name,
params,
return_type
))
}
/// Convert BID type to WASM type
fn bid_type_to_wasm_type(bid_type: &str) -> BidResult<String> {
match bid_type {
"bool" => Ok("i32".to_string()), // 0/1 in i32
"i32" => Ok("i32".to_string()),
"i64" => Ok("i64".to_string()),
"f32" => Ok("f32".to_string()),
"f64" => Ok("f64".to_string()),
"string" => Ok("i32 i32".to_string()), // ptr + len
"bytes" => Ok("i32 i32".to_string()), // ptr + len
"handle" => Ok("i64".to_string()), // Handle as u64
"void" => Ok("".to_string()),
_ => Err(BidError::CodeGenError(format!("Unsupported type for WASM: {}", bid_type))),
}
}
/// Generate JavaScript host implementation template
fn generate_js_host_template(bid: &BidDefinition) -> BidResult<String> {
let mut content = String::new();
content.push_str(&format!(
"// JavaScript host implementation for {}\n",
bid.name()
));
content.push_str("// Generated by Nyash BID code generator\n\n");
content.push_str("class NyashWasmHost {\n");
content.push_str(" constructor() {\n");
content.push_str(" this.memory = null;\n");
content.push_str(" this.textDecoder = new TextDecoder();\n");
content.push_str(" this.textEncoder = new TextEncoder();\n");
content.push_str(" }\n\n");
content.push_str(" // Called when WASM instance is created\n");
content.push_str(" setMemory(memory) {\n");
content.push_str(" this.memory = memory;\n");
content.push_str(" }\n\n");
content.push_str(" // Helper: Read string from WASM memory\n");
content.push_str(" readString(ptr, len) {\n");
content.push_str(" const bytes = new Uint8Array(this.memory.buffer, ptr, len);\n");
content.push_str(" return this.textDecoder.decode(bytes);\n");
content.push_str(" }\n\n");
content.push_str(" // Import object for WASM instantiation\n");
content.push_str(" getImportObject() {\n");
content.push_str(" return {\n");
content.push_str(" env: {\n");
for interface in &bid.interfaces {
for method in &interface.methods {
let js_method = Self::generate_js_method(interface, method)?;
content.push_str(&format!(" {}\n", js_method));
}
}
content.push_str(" }\n");
content.push_str(" };\n");
content.push_str(" }\n");
content.push_str("}\n\n");
content.push_str("// Usage example:\n");
content.push_str("// const host = new NyashWasmHost();\n");
content.push_str("// const importObject = host.getImportObject();\n");
content.push_str("// const wasmInstance = await WebAssembly.instantiateStreaming(\n");
content.push_str("// fetch('your_module.wasm'),\n");
content.push_str("// importObject\n");
content.push_str("// );\n");
content.push_str("// host.setMemory(wasmInstance.instance.exports.memory);\n");
content.push_str("// wasmInstance.instance.exports.main();\n\n");
content.push_str("module.exports = { NyashWasmHost };\n");
Ok(content)
}
/// Generate JavaScript method implementation
fn generate_js_method(interface: &BidInterface, method: &BidMethod) -> BidResult<String> {
let import_name = format!("{}.{}", interface.name, method.name);
// Build parameter list with type comments
let mut params = Vec::new();
for param in &method.params {
match param.get_type().as_str() {
"string" => params.extend(vec!["ptr".to_string(), "len".to_string()]),
"bytes" => params.extend(vec!["ptr".to_string(), "len".to_string()]),
typ if typ.starts_with("i") || typ.starts_with("f") || typ == "bool" => {
params.push(param.get_name().unwrap_or_else(|| "value".to_string()));
},
"handle" => params.push("handle".to_string()),
_ => params.push("param".to_string()),
}
}
let param_list = params.join(", ");
// Generate method body
let mut body = String::new();
// Handle string parameters
let mut string_conversions = Vec::new();
let mut converted_params = Vec::new();
for param in &method.params {
let param_name = param.get_name().unwrap_or_else(|| "value".to_string());
match param.get_type().as_str() {
"string" => {
string_conversions.push(format!(
" const {} = this.readString(ptr, len);",
param_name
));
converted_params.push(param_name);
},
_ => {
converted_params.push(param_name);
}
}
}
if !string_conversions.is_empty() {
body.push_str(&string_conversions.join("\n"));
body.push_str("\n");
}
// Generate method call
body.push_str(&format!(
" // TODO: Implement {} method\n",
method.name
));
body.push_str(&format!(
" console.log('Called {}({})');",
method.name,
converted_params.join(", ")
));
if !method.returns.is_void() {
body.push_str("\n // TODO: Return appropriate value");
body.push_str(&format!(
"\n return 0; // Placeholder for {} return",
method.returns.type_name()
));
}
Ok(format!(
"\"{}\": ({}) => {{\n{}\n }},",
import_name,
param_list,
body
))
}
/// Generate README with usage instructions
fn generate_readme(bid: &BidDefinition) -> BidResult<String> {
let bid_name = bid.name();
let mut content = String::new();
content.push_str(&format!("# {} - WASM Integration\n\n", bid_name));
content.push_str("Generated by Nyash BID code generator for WebAssembly target.\n\n");
content.push_str("## Files\n\n");
content.push_str(&format!("- `{}_imports.wat` - WebAssembly import declarations\n", bid_name));
content.push_str(&format!("- `{}_host.js` - JavaScript host implementation template\n", bid_name));
content.push_str("- `README.md` - This file\n\n");
content.push_str("## Usage\n\n");
content.push_str("1. Include the import declarations in your WASM module:\n");
content.push_str(" ```wat\n");
content.push_str(&format!(" ;; Copy imports from {}_imports.wat\n", bid_name));
content.push_str(" ```\n\n");
content.push_str("2. Implement the host functions in JavaScript:\n");
content.push_str(" ```javascript\n");
content.push_str(&format!(" const {{ NyashWasmHost }} = require('./{}_host.js');\n", bid_name));
content.push_str(" const host = new NyashWasmHost();\n");
content.push_str(" \n");
content.push_str(" // Customize the host implementation\n");
content.push_str(" const importObject = host.getImportObject();\n");
content.push_str(" ```\n\n");
content.push_str("3. Load and run your WASM module:\n");
content.push_str(" ```javascript\n");
content.push_str(" const wasmInstance = await WebAssembly.instantiateStreaming(\n");
content.push_str(" fetch('your_module.wasm'),\n");
content.push_str(" importObject\n");
content.push_str(" );\n");
content.push_str(" host.setMemory(wasmInstance.instance.exports.memory);\n");
content.push_str(" wasmInstance.instance.exports.main();\n");
content.push_str(" ```\n\n");
content.push_str("## Available Functions\n\n");
for interface in &bid.interfaces {
content.push_str(&format!("### {}\n\n", interface.name));
for method in &interface.methods {
content.push_str(&format!("- `{}(", method.name));
let param_strs: Vec<String> = method.params.iter().map(|p| {
format!("{}: {}",
p.get_name().unwrap_or_else(|| "param".to_string()),
p.get_type())
}).collect();
content.push_str(&param_strs.join(", "));
content.push_str(&format!(")` → `{}`\n", method.returns.type_name()));
}
content.push_str("\n");
}
Ok(content)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::bid::{BidInterface, BidMethod, BidParameter, BidTypeRef};
use std::collections::HashMap;
#[test]
fn test_bid_type_to_wasm_type() {
assert_eq!(WasmGenerator::bid_type_to_wasm_type("i32").unwrap(), "i32");
assert_eq!(WasmGenerator::bid_type_to_wasm_type("string").unwrap(), "i32 i32");
assert_eq!(WasmGenerator::bid_type_to_wasm_type("handle").unwrap(), "i64");
assert_eq!(WasmGenerator::bid_type_to_wasm_type("void").unwrap(), "");
assert!(WasmGenerator::bid_type_to_wasm_type("unknown").is_err());
}
#[test]
fn test_generate_wasm_import() {
let interface = BidInterface {
name: "env.console".to_string(),
box_type: Some("Console".to_string()),
methods: vec![],
};
let mut param_map = HashMap::new();
param_map.insert("string".to_string(), "msg".to_string());
let method = BidMethod {
name: "log".to_string(),
params: vec![BidParameter {
param_type: BidTypeRef::Named(param_map),
}],
returns: BidTypeRef::Simple("void".to_string()),
effect: Some("io".to_string()),
};
let import = WasmGenerator::generate_wasm_import(&interface, &method).unwrap();
assert!(import.contains("env.console.log"));
assert!(import.contains("$env.console.log"));
assert!(import.contains("param i32 i32")); // string = ptr + len
}
}

View File

@ -0,0 +1,287 @@
/*!
* BID Schema Parsing - YAML/JSON schema for Box Interface Definitions
*/
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::Path;
use crate::bid::BidError;
/// BID Definition - Root structure for BID files
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct BidDefinition {
pub version: u32,
pub interfaces: Vec<BidInterface>,
pub metadata: Option<BidMetadata>,
}
/// Metadata for a BID definition
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct BidMetadata {
pub name: Option<String>,
pub description: Option<String>,
pub author: Option<String>,
pub version: Option<String>,
}
/// Interface definition
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct BidInterface {
pub name: String,
#[serde(rename = "box")]
pub box_type: Option<String>, // Box type name
pub methods: Vec<BidMethod>,
}
/// Method definition
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct BidMethod {
pub name: String,
pub params: Vec<BidParameter>,
pub returns: BidTypeRef,
pub effect: Option<String>,
}
/// Parameter definition
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct BidParameter {
#[serde(flatten)]
pub param_type: BidTypeRef,
}
/// Type reference in BID files
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum BidTypeRef {
/// Simple type: { string: "name" }
Named(HashMap<String, String>),
/// Just a type name: "void"
Simple(String),
}
impl BidDefinition {
/// Load BID definition from YAML file
pub fn load_from_file(path: &Path) -> Result<Self, BidError> {
let content = std::fs::read_to_string(path)
.map_err(|e| BidError::IoError(e.to_string()))?;
let bid: BidDefinition = serde_yaml::from_str(&content)
.map_err(|e| BidError::ParseError(format!("YAML parse error: {}", e)))?;
// Validate the definition
bid.validate()?;
Ok(bid)
}
/// Validate the BID definition
pub fn validate(&self) -> Result<(), BidError> {
// Check version
if self.version > 1 {
return Err(BidError::UnsupportedVersion(self.version));
}
// Check for duplicate interface names
let mut interface_names = std::collections::HashSet::new();
for interface in &self.interfaces {
if interface_names.contains(&interface.name) {
return Err(BidError::DuplicateInterface(interface.name.clone()));
}
interface_names.insert(interface.name.clone());
}
// Validate each interface
for interface in &self.interfaces {
interface.validate()?;
}
Ok(())
}
/// Get interface by name
pub fn get_interface(&self, name: &str) -> Option<&BidInterface> {
self.interfaces.iter().find(|i| i.name == name)
}
/// Get the definition name (from metadata or derived from first interface)
pub fn name(&self) -> String {
if let Some(ref metadata) = self.metadata {
if let Some(ref name) = metadata.name {
return name.clone();
}
}
// Derive from first interface name
if let Some(interface) = self.interfaces.first() {
// Extract the last part of the interface name
// e.g., "env.console" -> "console"
interface.name.split('.').last().unwrap_or(&interface.name).to_string()
} else {
"unknown".to_string()
}
}
}
impl BidInterface {
/// Validate the interface
pub fn validate(&self) -> Result<(), BidError> {
// Check for duplicate method names
let mut method_names = std::collections::HashSet::new();
for method in &self.methods {
if method_names.contains(&method.name) {
return Err(BidError::DuplicateMethod(method.name.clone()));
}
method_names.insert(method.name.clone());
}
// Validate each method
for method in &self.methods {
method.validate()?;
}
Ok(())
}
}
impl BidMethod {
/// Validate the method
pub fn validate(&self) -> Result<(), BidError> {
// Check for duplicate parameter names
let mut param_names = std::collections::HashSet::new();
for (i, param) in self.params.iter().enumerate() {
let param_name = param.get_name().unwrap_or_else(|| format!("param_{}", i));
if param_names.contains(&param_name) {
return Err(BidError::DuplicateParameter(param_name));
}
param_names.insert(param_name);
}
Ok(())
}
}
impl BidParameter {
/// Get the parameter name (from the type definition)
pub fn get_name(&self) -> Option<String> {
match &self.param_type {
BidTypeRef::Named(map) => {
// Return the first value (parameter name)
map.values().next().cloned()
},
BidTypeRef::Simple(_) => None,
}
}
/// Get the parameter type name
pub fn get_type(&self) -> String {
match &self.param_type {
BidTypeRef::Named(map) => {
// Return the first key (type name)
map.keys().next().cloned().unwrap_or_else(|| "unknown".to_string())
},
BidTypeRef::Simple(type_name) => type_name.clone(),
}
}
}
impl BidTypeRef {
/// Get the type name
pub fn type_name(&self) -> String {
match self {
BidTypeRef::Named(map) => {
map.keys().next().cloned().unwrap_or_else(|| "unknown".to_string())
},
BidTypeRef::Simple(type_name) => type_name.clone(),
}
}
/// Check if this is a void type
pub fn is_void(&self) -> bool {
self.type_name() == "void"
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
use tempfile::NamedTempFile;
#[test]
fn test_parse_console_bid() {
let yaml_content = r#"
version: 0
interfaces:
- name: env.console
box: Console
methods:
- name: log
params:
- { string: msg }
returns: void
effect: io
"#;
let mut temp_file = NamedTempFile::new().unwrap();
writeln!(temp_file, "{}", yaml_content).unwrap();
let bid = BidDefinition::load_from_file(temp_file.path()).unwrap();
assert_eq!(bid.version, 0);
assert_eq!(bid.interfaces.len(), 1);
let interface = &bid.interfaces[0];
assert_eq!(interface.name, "env.console");
assert_eq!(interface.box_type, Some("Console".to_string()));
assert_eq!(interface.methods.len(), 1);
let method = &interface.methods[0];
assert_eq!(method.name, "log");
assert_eq!(method.params.len(), 1);
assert!(method.returns.is_void());
let param = &method.params[0];
assert_eq!(param.get_type(), "string");
assert_eq!(param.get_name(), Some("msg".to_string()));
}
#[test]
fn test_bid_definition_name() {
let yaml_content = r#"
version: 0
interfaces:
- name: env.console
methods: []
"#;
let mut temp_file = NamedTempFile::new().unwrap();
writeln!(temp_file, "{}", yaml_content).unwrap();
let bid = BidDefinition::load_from_file(temp_file.path()).unwrap();
assert_eq!(bid.name(), "console");
}
#[test]
fn test_duplicate_interface_validation() {
let yaml_content = r#"
version: 0
interfaces:
- name: env.console
methods: []
- name: env.console
methods: []
"#;
let mut temp_file = NamedTempFile::new().unwrap();
writeln!(temp_file, "{}", yaml_content).unwrap();
let result = BidDefinition::load_from_file(temp_file.path());
assert!(result.is_err());
if let Err(BidError::DuplicateInterface(name)) = result {
assert_eq!(name, "env.console");
} else {
panic!("Expected DuplicateInterface error");
}
}
}