Files
hakorune/plugins/nyash-python-parser-plugin/src/lib.rs
Moe Charm c13d9c045e 📚 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!
2025-08-30 22:52:16 +09:00

318 lines
12 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
});
}
}