📚 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

@ -0,0 +1,14 @@
[package]
name = "nyash-python-parser-plugin"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
pyo3 = { version = "0.22", features = ["auto-initialize"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
[dev-dependencies]

View File

@ -0,0 +1,29 @@
[box]
name = "Nyash Python Parser Plugin"
version = "0.1.0"
description = "Python -> AST/IR (Phase 10.7 C1)"
author = "Nyash Team"
[provides]
boxes = ["PythonParserBox"]
[PythonParserBox]
type_id = 60
[PythonParserBox.lifecycle]
birth = { id = 0 }
fini = { id = 4294967295 }
[PythonParserBox.methods.parse]
id = 1
args = [ { name = "code", type = "string" } ]
returns = { type = "string" } # JSON AST (暫定)
[implementation]
ffi_version = 1
thread_safe = false
[artifacts]
windows = "target/x86_64-pc-windows-msvc/release/nyash_python_parser_plugin.dll"
linux = "target/release/libnyash_python_parser_plugin.so"
macos = "target/release/libnyash_python_parser_plugin.dylib"

View File

@ -0,0 +1,318 @@
use pyo3::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
struct ParseResult {
success: bool,
dump: Option<String>,
counts: ParseCounts,
unsupported: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug)]
struct ParseCounts {
total_nodes: usize,
functions: usize,
classes: usize,
supported: usize,
unsupported: usize,
}
/// FFI: プラグインABIバージョン
#[no_mangle]
pub extern "C" fn nyash_plugin_abi_version() -> u32 {
1
}
/// FFI: プラグイン初期化
#[no_mangle]
pub extern "C" fn nyash_plugin_init() -> i32 {
// Python初期化は pyo3 の auto-initialize が処理
0
}
/// FFI: プラグインメソッド呼び出しBID形式
#[no_mangle]
pub extern "C" fn nyash_plugin_invoke(
type_id: u32,
method_id: u32,
_instance_id: u32,
args: *const u8,
args_len: usize,
result: *mut u8,
result_len: *mut usize,
) -> i32 {
const TYPE_ID_PARSER: u32 = 60;
const METHOD_BIRTH: u32 = 0;
const METHOD_PARSE: u32 = 1;
const METHOD_FINI: u32 = u32::MAX;
if type_id != TYPE_ID_PARSER {
return -3; // NYB_E_INVALID_METHOD
}
match method_id {
METHOD_BIRTH => {
// インスタンスIDを返す
unsafe {
let instance_id = 1u32; // 簡易実装
if *result_len < 4 {
*result_len = 4;
return -1; // NYB_E_SHORT_BUFFER
}
let out = std::slice::from_raw_parts_mut(result, *result_len);
out[0..4].copy_from_slice(&instance_id.to_le_bytes());
*result_len = 4;
}
0
}
METHOD_PARSE => {
// 引数からコードを取得TLV形式の文字列を期待
let code = unsafe {
if args.is_null() || args_len < 4 {
// 引数なしの場合は環境変数から取得
std::env::var("NYASH_PY_CODE").unwrap_or_else(|_| "def main():\n return 0".to_string())
} else {
// TLVデコード簡易版
let buf = std::slice::from_raw_parts(args, args_len);
if args_len >= 8 {
let tag = u16::from_le_bytes([buf[0], buf[1]]);
let len = u16::from_le_bytes([buf[2], buf[3]]) as usize;
if tag == 6 && 4 + len <= args_len {
match std::str::from_utf8(&buf[4..4+len]) {
Ok(s) => s.to_string(),
Err(_) => std::env::var("NYASH_PY_CODE").unwrap_or_else(|_| "def main():\n return 0".to_string())
}
} else {
std::env::var("NYASH_PY_CODE").unwrap_or_else(|_| "def main():\n return 0".to_string())
}
} else {
std::env::var("NYASH_PY_CODE").unwrap_or_else(|_| "def main():\n return 0".to_string())
}
}
};
// パース実行
let parse_result = Python::with_gil(|py| {
parse_python_code(py, &code)
});
// JSONにシリアライズ
match serde_json::to_string(&parse_result) {
Ok(json) => {
unsafe {
let bytes = json.as_bytes();
let need = 4 + bytes.len();
if *result_len < need {
*result_len = need;
return -1; // NYB_E_SHORT_BUFFER
}
let out = std::slice::from_raw_parts_mut(result, *result_len);
// TLVエンコードtag=6:string
out[0..2].copy_from_slice(&6u16.to_le_bytes());
out[2..4].copy_from_slice(&(bytes.len() as u16).to_le_bytes());
out[4..4+bytes.len()].copy_from_slice(bytes);
*result_len = need;
}
0
}
Err(_) => -4 // エラー
}
}
METHOD_FINI => 0,
_ => -3 // NYB_E_INVALID_METHOD
}
}
/// FFI: Pythonコードをパース
#[no_mangle]
pub extern "C" fn nyash_python_parse(code: *const std::os::raw::c_char) -> *mut std::os::raw::c_char {
let code = unsafe {
if code.is_null() {
return std::ptr::null_mut();
}
match std::ffi::CStr::from_ptr(code).to_str() {
Ok(s) => s,
Err(_) => return std::ptr::null_mut(),
}
};
let result = Python::with_gil(|py| {
parse_python_code(py, code)
});
match serde_json::to_string(&result) {
Ok(json) => {
let c_str = std::ffi::CString::new(json).unwrap();
c_str.into_raw()
}
Err(_) => std::ptr::null_mut(),
}
}
/// FFI: 文字列解放
#[no_mangle]
pub extern "C" fn nyash_python_free_string(ptr: *mut std::os::raw::c_char) {
if !ptr.is_null() {
unsafe {
let _ = std::ffi::CString::from_raw(ptr);
}
}
}
fn parse_python_code(py: Python, code: &str) -> ParseResult {
let mut result = ParseResult {
success: false,
dump: None,
counts: ParseCounts {
total_nodes: 0,
functions: 0,
classes: 0,
supported: 0,
unsupported: 0,
},
unsupported: Vec::new(),
};
// Pythonのastモジュールをインポート
let ast_module = match py.import_bound("ast") {
Ok(m) => m,
Err(e) => {
result.dump = Some(format!("Failed to import ast module: {}", e));
return result;
}
};
// コードをパース
let tree = match ast_module.call_method1("parse", (code,)) {
Ok(t) => t,
Err(e) => {
result.dump = Some(format!("Parse error: {}", e));
return result;
}
};
// ASTをダンプ文字列表現
if let Ok(dump_str) = ast_module.call_method1("dump", (&tree,)) {
if let Ok(s) = dump_str.extract::<String>() {
result.dump = Some(s);
}
}
// ASTを解析してカウント
analyze_ast(py, &tree, &mut result);
result.success = true;
result
}
fn analyze_ast(_py: Python, node: &Bound<'_, PyAny>, result: &mut ParseResult) {
result.counts.total_nodes += 1;
// ノードタイプを取得 - __class__.__name__ を使用
if let Ok(class_obj) = node.getattr("__class__") {
if let Ok(name_obj) = class_obj.getattr("__name__") {
if let Ok(type_name) = name_obj.extract::<String>() {
match type_name.as_str() {
"FunctionDef" => {
result.counts.functions += 1;
result.counts.supported += 1;
}
"AsyncFunctionDef" => {
result.counts.functions += 1;
result.counts.unsupported += 1;
result.unsupported.push("async function".to_string());
}
"ClassDef" => {
result.counts.classes += 1;
result.counts.unsupported += 1;
result.unsupported.push("class definition".to_string());
}
"For" | "While" | "If" => {
result.counts.supported += 1;
}
"Yield" | "YieldFrom" => {
result.counts.unsupported += 1;
result.unsupported.push("generator".to_string());
}
_ => {}
}
}
}
}
// 子ノードを再帰的に解析
// ast.walk() を使って全ノードを取得
if let Ok(ast_module) = node.py().import_bound("ast") {
if let Ok(walk_iter) = ast_module.call_method1("walk", (node,)) {
if let Ok(nodes) = walk_iter.iter() {
for child_result in nodes {
if let Ok(child) = child_result {
// 自分自身はスキップ(すでにカウント済み)
if !child.is(node) {
// 再帰的に解析(ただし walk は全ノードを返すので、
// 実際には再帰なしでフラットに処理される)
result.counts.total_nodes += 1;
if let Ok(class_obj) = child.getattr("__class__") {
if let Ok(name_obj) = class_obj.getattr("__name__") {
if let Ok(type_name) = name_obj.extract::<String>() {
match type_name.as_str() {
"FunctionDef" => {
result.counts.functions += 1;
result.counts.supported += 1;
}
"AsyncFunctionDef" => {
result.counts.functions += 1;
result.counts.unsupported += 1;
if !result.unsupported.contains(&"async function".to_string()) {
result.unsupported.push("async function".to_string());
}
}
"ClassDef" => {
result.counts.classes += 1;
result.counts.unsupported += 1;
if !result.unsupported.contains(&"class definition".to_string()) {
result.unsupported.push("class definition".to_string());
}
}
"For" | "While" | "If" => {
result.counts.supported += 1;
}
"Yield" | "YieldFrom" => {
result.counts.unsupported += 1;
if !result.unsupported.contains(&"generator".to_string()) {
result.unsupported.push("generator".to_string());
}
}
_ => {}
}
}
}
}
}
}
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple_parse() {
pyo3::prepare_freethreaded_python();
Python::with_gil(|py| {
let code = "def main():\n return 0";
let result = parse_python_code(py, code);
assert!(result.success);
assert_eq!(result.counts.functions, 1);
assert_eq!(result.counts.supported, 1);
});
}
}

Binary file not shown.

View File

@ -0,0 +1,57 @@
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <string.h>
// FFI関数型定義
typedef int (*plugin_init_fn)(void);
typedef char* (*parse_fn)(const char*);
typedef void (*free_fn)(char*);
int main() {
// プラグインをロード
void* handle = dlopen("./target/release/libnyash_python_parser_plugin.so", RTLD_LAZY);
if (!handle) {
fprintf(stderr, "Failed to load plugin: %s\n", dlerror());
return 1;
}
// 関数を取得
plugin_init_fn init_fn = (plugin_init_fn)dlsym(handle, "nyash_plugin_init");
parse_fn parse_func = (parse_fn)dlsym(handle, "nyash_python_parse");
free_fn free_func = (free_fn)dlsym(handle, "nyash_python_free_string");
if (!init_fn || !parse_func || !free_func) {
fprintf(stderr, "Failed to load functions\n");
dlclose(handle);
return 1;
}
// 初期化
if (init_fn() != 0) {
fprintf(stderr, "Plugin init failed\n");
dlclose(handle);
return 1;
}
// 環境変数からコードを取得
const char* code = getenv("NYASH_PY_CODE");
if (!code) {
code = "def main():\n return 0";
printf("Using default code: %s\n", code);
} else {
printf("Parsing code from NYASH_PY_CODE: %s\n", code);
}
// パース実行
char* result = parse_func(code);
if (result) {
printf("\n=== Parse Result ===\n%s\n", result);
free_func(result);
} else {
printf("Parse failed\n");
}
dlclose(handle);
return 0;
}