📚 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:
14
plugins/nyash-python-parser-plugin/Cargo.toml
Normal file
14
plugins/nyash-python-parser-plugin/Cargo.toml
Normal 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]
|
||||
29
plugins/nyash-python-parser-plugin/nyash_box.toml
Normal file
29
plugins/nyash-python-parser-plugin/nyash_box.toml
Normal 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"
|
||||
318
plugins/nyash-python-parser-plugin/src/lib.rs
Normal file
318
plugins/nyash-python-parser-plugin/src/lib.rs
Normal 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
BIN
plugins/nyash-python-parser-plugin/test_parser
Normal file
BIN
plugins/nyash-python-parser-plugin/test_parser
Normal file
Binary file not shown.
57
plugins/nyash-python-parser-plugin/test_parser.c
Normal file
57
plugins/nyash-python-parser-plugin/test_parser.c
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user