diff --git a/src/mir/optimizer_passes/mod.rs b/src/mir/optimizer_passes/mod.rs index 45890b04..9d9c6144 100644 --- a/src/mir/optimizer_passes/mod.rs +++ b/src/mir/optimizer_passes/mod.rs @@ -4,4 +4,3 @@ pub mod intrinsics; pub mod normalize; pub mod reorder; pub mod normalize_core13_pure; -pub mod normalize_legacy_all; diff --git a/src/mir/optimizer_passes/normalize_legacy_all.rs b/src/mir/optimizer_passes/normalize_legacy_all.rs deleted file mode 100644 index a5358b87..00000000 --- a/src/mir/optimizer_passes/normalize_legacy_all.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::mir::optimizer::MirOptimizer; -use crate::mir::optimizer_stats::OptimizationStats; - -/// Delegate: legacy normalization (moved from optimizer.rs) -pub fn normalize_legacy_instructions(opt: &mut MirOptimizer, module: &mut crate::mir::MirModule) -> OptimizationStats { - crate::mir::optimizer_passes::normalize::normalize_legacy_instructions(opt, module) -} - diff --git a/src/parser/statements_backup.rs b/src/parser/statements_backup.rs deleted file mode 100644 index 7d205494..00000000 --- a/src/parser/statements_backup.rs +++ /dev/null @@ -1,723 +0,0 @@ -/*! - * Nyash Parser - Statement Parsing Module - * - * 文(Statement)の解析を担当するモジュール - * if, loop, break, return, print等の制御構文を処理 - */ - -use super::common::ParserUtils; -use super::cursor::TokenCursor; -use super::{NyashParser, ParseError}; -use crate::ast::{ASTNode, CatchClause, Span}; -use crate::tokenizer::TokenType; - -impl NyashParser { - #[inline] - fn cursor_enabled() -> bool { - std::env::var("NYASH_PARSER_TOKEN_CURSOR").ok().as_deref() == Some("1") - } - - /// Thin adapter: when Cursor route is enabled, align statement start position - /// by letting TokenCursor apply its statement-mode newline policy, then - /// continue with the legacy statement parser. No behavior change otherwise. - #[inline] - fn with_stmt_cursor(&mut self, f: F) -> Result - where - F: FnOnce(&mut Self) -> Result, - { - if Self::cursor_enabled() { - let mut cursor = TokenCursor::new(&self.tokens); - cursor.set_position(self.current); - cursor.with_stmt_mode(|c| { - // Allow cursor to collapse any leading NEWLINEs in stmt mode - c.skip_newlines(); - }); - self.current = cursor.position(); - } - f(self) - } - /// Map a starting token into a grammar keyword string used by GRAMMAR_DIFF tracing. - #[inline] - fn grammar_keyword_for(start: &TokenType) -> Option<&'static str> { - match start { - TokenType::BOX => Some("box"), - TokenType::GLOBAL => Some("global"), - TokenType::FUNCTION => Some("function"), - TokenType::STATIC => Some("static"), - TokenType::IF => Some("if"), - TokenType::LOOP => Some("loop"), - TokenType::BREAK => Some("break"), - TokenType::RETURN => Some("return"), - TokenType::PRINT => Some("print"), - TokenType::NOWAIT => Some("nowait"), - TokenType::LOCAL => Some("local"), - TokenType::OUTBOX => Some("outbox"), - TokenType::TRY => Some("try"), - TokenType::THROW => Some("throw"), - TokenType::USING => Some("using"), - TokenType::FROM => Some("from"), - _ => None, - } - } - /// Small helper: build UnexpectedToken with current token and line. - #[inline] - fn err_unexpected>(&self, expected: S) -> ParseError { - ParseError::UnexpectedToken { - found: self.current_token().token_type.clone(), - expected: expected.into(), - line: self.current_token().line, - } - } - - /// Expect an identifier and advance. Returns its string or an UnexpectedToken error. - #[inline] - fn expect_identifier(&mut self, what: &str) -> Result { - if let TokenType::IDENTIFIER(name) = &self.current_token().token_type { - let out = name.clone(); - self.advance(); - Ok(out) - } else { - Err(self.err_unexpected(what)) - } - } - - /// Parse a standalone block `{ ... }` and optional postfix `catch/cleanup` sequence. - /// Returns Program(body) when no postfix keywords follow. - fn parse_standalone_block_statement(&mut self) -> Result { - // Parse the block body first - let try_body = self.parse_block_statements()?; - - if crate::config::env::block_postfix_catch() - && (self.match_token(&TokenType::CATCH) || self.match_token(&TokenType::CLEANUP)) - { - // Parse at most one catch, then optional cleanup - let mut catch_clauses: Vec = Vec::new(); - if self.match_token(&TokenType::CATCH) { - self.advance(); // consume 'catch' - self.consume(TokenType::LPAREN)?; - let (exception_type, exception_var) = self.parse_catch_param()?; - self.consume(TokenType::RPAREN)?; - let catch_body = self.parse_block_statements()?; - catch_clauses.push(CatchClause { - exception_type, - variable_name: exception_var, - body: catch_body, - span: Span::unknown(), - }); - - // Single‑catch policy (MVP): disallow multiple catch in postfix form - if self.match_token(&TokenType::CATCH) { - let line = self.current_token().line; - return Err(ParseError::UnexpectedToken { - found: self.current_token().token_type.clone(), - expected: "single catch only after standalone block".to_string(), - line, - }); - } - } - - // Optional cleanup - let finally_body = if self.match_token(&TokenType::CLEANUP) { - self.advance(); // consume 'cleanup' - Some(self.parse_block_statements()?) - } else { - None - }; - - Ok(ASTNode::TryCatch { - try_body, - catch_clauses, - finally_body, - span: Span::unknown(), - }) - } else { - // No postfix keywords. If gate is on, enforce MVP static check: - // direct top-level `throw` inside the standalone block must be followed by catch - if crate::config::env::block_postfix_catch() - && try_body.iter().any(|n| matches!(n, ASTNode::Throw { .. })) - { - let line = self.current_token().line; - return Err(ParseError::UnexpectedToken { - found: self.current_token().token_type.clone(), - expected: "block with direct 'throw' must be followed by 'catch'".to_string(), - line, - }); - } - Ok(ASTNode::Program { - statements: try_body, - span: Span::unknown(), - }) - } - } - /// Helper: parse a block `{ stmt* }` and return its statements - pub(super) fn parse_block_statements(&mut self) -> Result, ParseError> { - self.consume(TokenType::LBRACE)?; - let mut body = Vec::new(); - while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { - if !self.match_token(&TokenType::RBRACE) { - body.push(self.parse_statement()?); - } - } - self.consume(TokenType::RBRACE)?; - Ok(body) - } - - /// Grouped: declarations (box/interface/global/function/static/import) - fn parse_declaration_statement(&mut self) -> Result { - match &self.current_token().token_type { - TokenType::BOX => self.parse_box_declaration(), - TokenType::IMPORT => self.parse_import(), - TokenType::INTERFACE => self.parse_interface_box_declaration(), - TokenType::GLOBAL => self.parse_global_var(), - TokenType::FUNCTION => self.parse_function_declaration(), - TokenType::STATIC => self.parse_static_declaration(), - _ => Err(self.err_unexpected("declaration statement")), - } - } - - /// Grouped: control flow (if/loop/break/continue/return) - fn parse_control_flow_statement(&mut self) -> Result { - match &self.current_token().token_type { - TokenType::IF => self.parse_if(), - TokenType::LOOP => self.parse_loop(), - TokenType::BREAK => self.parse_break(), - TokenType::CONTINUE => self.parse_continue(), - TokenType::RETURN => self.parse_return(), - _ => Err(self.err_unexpected("control-flow statement")), - } - } - - /// Grouped: IO/module-ish (print/nowait) - fn parse_io_module_statement(&mut self) -> Result { - match &self.current_token().token_type { - TokenType::PRINT => self.parse_print(), - TokenType::NOWAIT => self.parse_nowait(), - _ => Err(self.err_unexpected("io/module statement")), - } - } - - /// Grouped: variable-related (local/outbox) - fn parse_variable_declaration_statement(&mut self) -> Result { - match &self.current_token().token_type { - TokenType::LOCAL => self.parse_local(), - TokenType::OUTBOX => self.parse_outbox(), - _ => Err(self.err_unexpected("variable declaration")), - } - } - - /// Grouped: exception (try/throw) with gate checks preserved - fn parse_exception_statement(&mut self) -> Result { - match &self.current_token().token_type { - TokenType::TRY => { - if crate::config::env::parser_stage3() { - self.parse_try_catch() - } else { - Err(ParseError::UnexpectedToken { - found: self.current_token().token_type.clone(), - expected: "enable NYASH_PARSER_STAGE3=1 to use 'try'".to_string(), - line: self.current_token().line, - }) - } - } - TokenType::THROW => { - if crate::config::env::parser_stage3() { - self.parse_throw() - } else { - Err(ParseError::UnexpectedToken { - found: self.current_token().token_type.clone(), - expected: "enable NYASH_PARSER_STAGE3=1 to use 'throw'".to_string(), - line: self.current_token().line, - }) - } - } - _ => Err(self.err_unexpected("try/throw")), - } - } - - /// Error helpers for standalone postfix keywords (catch/cleanup) - fn parse_postfix_catch_cleanup_error(&mut self) -> Result { - match &self.current_token().token_type { - TokenType::CATCH => { - if crate::config::env::block_postfix_catch() { - Err(ParseError::UnexpectedToken { - found: self.current_token().token_type.clone(), - expected: "postfix 'catch' is only allowed immediately after a standalone block: { ... } catch (...) { ... } (wrap if/else/loop in a standalone block)".to_string(), - line: self.current_token().line, - }) - } else { - Err(ParseError::UnexpectedToken { - found: self.current_token().token_type.clone(), - expected: "enable NYASH_BLOCK_CATCH=1 (or NYASH_PARSER_STAGE3=1) to use postfix 'catch' after a standalone block".to_string(), - line: self.current_token().line, - }) - } - } - TokenType::CLEANUP => { - if crate::config::env::block_postfix_catch() { - Err(ParseError::UnexpectedToken { - found: self.current_token().token_type.clone(), - expected: "postfix 'cleanup' is only allowed immediately after a standalone block: { ... } cleanup { ... }".to_string(), - line: self.current_token().line, - }) - } else { - Err(ParseError::UnexpectedToken { - found: self.current_token().token_type.clone(), - expected: "enable NYASH_BLOCK_CATCH=1 (or NYASH_PARSER_STAGE3=1) to use postfix 'cleanup' after a standalone block".to_string(), - line: self.current_token().line, - }) - } - } - _ => unreachable!(), - } - } - - /// Helper: parse catch parameter inside parentheses (after '(' consumed) - /// Forms: (Type ident) | (ident) | () - pub(super) fn parse_catch_param(&mut self) -> Result<(Option, Option), ParseError> { - if self.match_token(&TokenType::RPAREN) { - return Ok((None, None)); - } - match &self.current_token().token_type { - TokenType::IDENTIFIER(first) => { - let first_str = first.clone(); - let two_idents = matches!(self.peek_token(), TokenType::IDENTIFIER(_)); - if two_idents { - self.advance(); // consume type ident - if let TokenType::IDENTIFIER(var_name) = &self.current_token().token_type { - let var = var_name.clone(); - self.advance(); - Ok((Some(first_str), Some(var))) - } else { - Err(self.err_unexpected("exception variable name")) - } - } else { - self.advance(); - Ok((None, Some(first_str))) - } - } - _ => { - if self.match_token(&TokenType::RPAREN) { - Ok((None, None)) - } else { - Err(self.err_unexpected(") or identifier")) - } - } - } - } - - /// 文をパース - pub(super) fn parse_statement(&mut self) -> Result { - // For grammar diff: capture starting token to classify statement keyword - let start_tok = self.current_token().token_type.clone(); - let result = match &start_tok { - TokenType::LBRACE => self.parse_standalone_block_statement(), - TokenType::BOX - | TokenType::IMPORT - | TokenType::INTERFACE - | TokenType::GLOBAL - | TokenType::FUNCTION - | TokenType::STATIC => self.parse_declaration_statement(), - TokenType::IF - | TokenType::LOOP - | TokenType::BREAK - | TokenType::CONTINUE - | TokenType::RETURN => self.parse_control_flow_statement(), - TokenType::PRINT | TokenType::NOWAIT => self.parse_io_module_statement(), - TokenType::LOCAL | TokenType::OUTBOX => self.parse_variable_declaration_statement(), - TokenType::TRY | TokenType::THROW => self.parse_exception_statement(), - TokenType::CATCH | TokenType::CLEANUP => self.parse_postfix_catch_cleanup_error(), - TokenType::USING => self.parse_using(), - TokenType::FROM => { - // 🔥 from構文: from Parent.method(args) または from Parent.constructor(args) - self.parse_from_call_statement() - } - TokenType::IDENTIFIER(_name) => { - // function宣言 または 代入文 または 関数呼び出し - self.parse_assignment_or_function_call() - } - TokenType::THIS | TokenType::ME => { - // this/me で始まる文も通常の代入文または関数呼び出しとして処理 - self.parse_assignment_or_function_call() - } - _ => { - // Fallback: treat as expression statement - // Allows forms like: print("x") or a bare literal as the last value in a block - // Thin-adapt with Cursor in stmt mode (env on) to normalize leading newlines. - self.with_stmt_cursor(|p| Ok(p.parse_expression()?)) - } - }; - - // Non-invasive syntax rule check - if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") { - if let Some(k) = Self::grammar_keyword_for(&start_tok) { - let ok = crate::grammar::engine::get().syntax_is_allowed_statement(k); - if !ok { - eprintln!( - "[GRAMMAR-DIFF][Parser] statement '{}' not allowed by syntax rules", - k - ); - } - } - } - result - } - - /// import文をパース: import "path" (as Alias)? - pub(super) fn parse_import(&mut self) -> Result { - self.advance(); // consume 'import' - let path = if let TokenType::STRING(s) = &self.current_token().token_type { - let v = s.clone(); - self.advance(); - v - } else { - return Err(self.err_unexpected("string literal")); - }; - // Optional: 'as' Alias (treat 'as' as identifier literal) - let mut alias: Option = None; - if let TokenType::IDENTIFIER(w) = &self.current_token().token_type { - if w == "as" { - self.advance(); - if let TokenType::IDENTIFIER(name) = &self.current_token().token_type { - alias = Some(name.clone()); - self.advance(); - } else { - return Err(self.err_unexpected("alias name")); - } - } - } - Ok(ASTNode::ImportStatement { - path, - alias, - span: Span::unknown(), - }) - } - - /// if文をパース: if (condition) { body } else if ... else { body } - pub(super) fn parse_if(&mut self) -> Result { - // Thin-adapt statement start when Cursor route is enabled - if Self::cursor_enabled() { - let mut cursor = TokenCursor::new(&self.tokens); - cursor.set_position(self.current); - cursor.with_stmt_mode(|c| c.skip_newlines()); - self.current = cursor.position(); - } - self.advance(); // consume 'if' - - // 条件部分を取得 - let condition = Box::new(self.parse_expression()?); - - // then部分を取得(共通ブロックヘルパー) - let then_body = self.parse_block_statements()?; - - // else if/else部分を処理 - let else_body = if self.match_token(&TokenType::ELSE) { - self.advance(); // consume 'else' - - if self.match_token(&TokenType::IF) { - // else if を ネストしたifとして処理 - let nested_if = self.parse_if()?; - Some(vec![nested_if]) - } else { - // plain else(共通ブロックヘルパー) - Some(self.parse_block_statements()?) - } - } else { - None - }; - - Ok(ASTNode::If { - condition, - then_body, - else_body, - span: Span::unknown(), - }) - } - - /// loop文をパース - pub(super) fn parse_loop(&mut self) -> Result { - if Self::cursor_enabled() { - let mut cursor = TokenCursor::new(&self.tokens); - cursor.set_position(self.current); - cursor.with_stmt_mode(|c| c.skip_newlines()); - self.current = cursor.position(); - } - self.advance(); // consume 'loop' - - // 条件部分を取得(省略可: `loop { ... }` は無条件ループとして扱う) - let condition = if self.match_token(&TokenType::LPAREN) { - self.advance(); // consume '(' - let cond = Box::new(self.parse_expression()?); - self.consume(TokenType::RPAREN)?; - cond - } else { - // default: true - Box::new(ASTNode::Literal { - value: crate::ast::LiteralValue::Bool(true), - span: Span::unknown(), - }) - }; - - // body部分を取得(共通ブロックヘルパー) - let body = self.parse_block_statements()?; - - Ok(ASTNode::Loop { - condition, - body, - span: Span::unknown(), - }) - } - - /// break文をパース - pub(super) fn parse_break(&mut self) -> Result { - self.advance(); // consume 'break' - Ok(ASTNode::Break { - span: Span::unknown(), - }) - } - - /// continue文をパース - pub(super) fn parse_continue(&mut self) -> Result { - self.advance(); // consume 'continue' - Ok(ASTNode::Continue { - span: Span::unknown(), - }) - } - - /// return文をパース - pub(super) fn parse_return(&mut self) -> Result { - if Self::cursor_enabled() { - let mut cursor = TokenCursor::new(&self.tokens); - cursor.set_position(self.current); - cursor.with_stmt_mode(|c| c.skip_newlines()); - self.current = cursor.position(); - } - self.advance(); // consume 'return' - // returnの後に式があるかチェック(RBRACE/EOFなら値なし) - let value = if self.is_at_end() || self.match_token(&TokenType::RBRACE) { - None - } else { - Some(Box::new(self.parse_expression()?)) - }; - - Ok(ASTNode::Return { - value, - span: Span::unknown(), - }) - } - - /// print文をパース - pub(super) fn parse_print(&mut self) -> Result { - if Self::cursor_enabled() { - let mut cursor = TokenCursor::new(&self.tokens); - cursor.set_position(self.current); - cursor.with_stmt_mode(|c| c.skip_newlines()); - self.current = cursor.position(); - } - self.advance(); // consume 'print' - self.consume(TokenType::LPAREN)?; - let value = Box::new(self.parse_expression()?); - self.consume(TokenType::RPAREN)?; - - Ok(ASTNode::Print { - expression: value, - span: Span::unknown(), - }) - } - - /// nowait文をパース: nowait variable = expression - pub(super) fn parse_nowait(&mut self) -> Result { - self.advance(); // consume 'nowait' - - // 変数名を取得 - let variable = self.expect_identifier("variable name")?; - - self.consume(TokenType::ASSIGN)?; - let expression = Box::new(self.parse_expression()?); - - Ok(ASTNode::Nowait { - variable, - expression, - span: Span::unknown(), - }) - } - - // include文は廃止(usingを使用) - - /// local変数宣言をパース: local var1, var2, var3 または local x = 10 - pub(super) fn parse_local(&mut self) -> Result { - if Self::cursor_enabled() { - let mut cursor = TokenCursor::new(&self.tokens); - cursor.set_position(self.current); - cursor.with_stmt_mode(|c| c.skip_newlines()); - self.current = cursor.position(); - } - self.advance(); // consume 'local' - - let mut names = Vec::new(); - let mut initial_values = Vec::new(); - - // 最初の変数名を取得 - if let TokenType::IDENTIFIER(name) = &self.current_token().token_type { - names.push(name.clone()); - self.advance(); - - // = があれば初期値を設定 - if self.match_token(&TokenType::ASSIGN) { - self.advance(); // consume '=' - initial_values.push(Some(Box::new(self.parse_expression()?))); - - // 初期化付きlocalは単一変数のみ(カンマ区切り不可) - Ok(ASTNode::Local { - variables: names, - initial_values, - span: Span::unknown(), - }) - } else { - // 初期化なしの場合はカンマ区切りで複数変数可能 - initial_values.push(None); - - // カンマ区切りで追加の変数名を取得 - while self.match_token(&TokenType::COMMA) { - self.advance(); // consume ',' - - if let TokenType::IDENTIFIER(name) = &self.current_token().token_type { - names.push(name.clone()); - initial_values.push(None); - self.advance(); - } else { - return Err(self.err_unexpected("identifier")); - } - } - - Ok(ASTNode::Local { - variables: names, - initial_values, - span: Span::unknown(), - }) - } - } else { - Err(self.err_unexpected("identifier")) - } - } - - /// outbox変数宣言をパース: outbox var1, var2, var3 - pub(super) fn parse_outbox(&mut self) -> Result { - self.advance(); // consume 'outbox' - - let mut names = Vec::new(); - - // 最初の変数名を取得 - if let TokenType::IDENTIFIER(name) = &self.current_token().token_type { - names.push(name.clone()); - self.advance(); - - // カンマ区切りで追加の変数名を取得 - while self.match_token(&TokenType::COMMA) { - self.advance(); // consume ',' - - if let TokenType::IDENTIFIER(name) = &self.current_token().token_type { - names.push(name.clone()); - self.advance(); - } else { - return Err(self.err_unexpected("identifier")); - } - } - - let num_vars = names.len(); - Ok(ASTNode::Outbox { - variables: names, - initial_values: vec![None; num_vars], - span: Span::unknown(), - }) - } else { - Err(self.err_unexpected("identifier")) - } - } - - /// try-catch文をパース - pub(super) fn parse_try_catch(&mut self) -> Result { - self.advance(); // consume 'try' - let try_body = self.parse_block_statements()?; - - let mut catch_clauses = Vec::new(); - - // catch節をパース - while self.match_token(&TokenType::CATCH) { - self.advance(); // consume 'catch' - self.consume(TokenType::LPAREN)?; - let (exception_type, exception_var) = self.parse_catch_param()?; - self.consume(TokenType::RPAREN)?; - let catch_body = self.parse_block_statements()?; - - catch_clauses.push(CatchClause { - exception_type, - variable_name: exception_var, - body: catch_body, - span: Span::unknown(), - }); - } - - // cleanup節をパース (オプション) - let finally_body = if self.match_token(&TokenType::CLEANUP) { - self.advance(); // consume 'cleanup' - Some(self.parse_block_statements()?) - } else { - None - }; - - Ok(ASTNode::TryCatch { - try_body, - catch_clauses, - finally_body, - span: Span::unknown(), - }) - } - - /// throw文をパース - pub(super) fn parse_throw(&mut self) -> Result { - self.advance(); // consume 'throw' - let value = Box::new(self.parse_expression()?); - Ok(ASTNode::Throw { - expression: value, - span: Span::unknown(), - }) - } - - /// 🔥 from構文を文としてパース: from Parent.method(args) - pub(super) fn parse_from_call_statement(&mut self) -> Result { - // 既存のparse_from_call()を使用してFromCall ASTノードを作成 - let from_call_expr = self.parse_from_call()?; - - // FromCallは式でもあるが、文としても使用可能 - // 例: from Animal.constructor() (戻り値を使わない) - Ok(from_call_expr) - } - - /// using文をパース: using namespace_name - pub(super) fn parse_using(&mut self) -> Result { - self.advance(); // consume 'using' - - // 名前空間名を取得 - if let TokenType::IDENTIFIER(namespace_name) = &self.current_token().token_type { - let name = namespace_name.clone(); - self.advance(); - - // Phase 0では "nyashstd" のみ許可 - if name != "nyashstd" { - return Err(ParseError::UnsupportedNamespace { - name, - line: self.current_token().line, - }); - } - - Ok(ASTNode::UsingStatement { - namespace_name: name, - span: Span::unknown(), - }) - } else { - Err(ParseError::ExpectedIdentifier { - line: self.current_token().line, - }) - } - } -} diff --git a/src/runtime/plugin_box_legacy.rs b/src/runtime/plugin_box_legacy.rs deleted file mode 100644 index 177f17bb..00000000 --- a/src/runtime/plugin_box_legacy.rs +++ /dev/null @@ -1,159 +0,0 @@ -//! PluginBoxプロキシ - プラグインBoxの統一インターフェース -//! -//! すべてのプラグインから提供されるBoxを、 -//! 通常のNyashBoxとして扱えるようにするプロキシ実装 - -use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; -use crate::bid::BidHandle; -use std::any::Any; -use std::fmt; - -/// プラグインから提供されるBoxのプロキシ -/// -/// FFI境界を越えてプラグイン内のBoxインスタンスと通信する -#[derive(Debug)] -pub struct PluginBox { - /// BoxCoreトレイト用の基本情報 - base: BoxBase, - - /// プラグイン名(例: "filebox") - plugin_name: String, - - /// プラグイン内のインスタンスハンドル - handle: BidHandle, -} - -impl PluginBox { - /// 新しいPluginBoxプロキシを作成 - pub fn new(plugin_name: String, handle: BidHandle) -> Self { - Self { - base: BoxBase::new(), - plugin_name, - handle, - } - } - - /// プラグイン名を取得 - pub fn plugin_name(&self) -> &str { - &self.plugin_name - } - - /// ハンドルを取得 - pub fn handle(&self) -> BidHandle { - self.handle - } - - /// プラグインメソッド呼び出し(内部使用) - fn call_plugin_method(&self, method_name: &str, args: &[Box]) -> Result, String> { - use crate::runtime::get_global_loader; - let loader = get_global_loader(); - loader.invoke_plugin_method(&self.plugin_name, self.handle, method_name, args) - } -} - -impl BoxCore for PluginBox { - fn box_id(&self) -> u64 { - self.base.id - } - - fn parent_type_id(&self) -> Option { - self.base.parent_type_id - } - - fn fmt_box(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "PluginBox({}, handle={:?})", self.plugin_name, self.handle) - } - - fn as_any(&self) -> &dyn Any { - self - } - - fn as_any_mut(&mut self) -> &mut dyn Any { - self - } -} - -impl NyashBox for PluginBox { - fn clone_box(&self) -> Box { - // TODO: FFI経由でプラグインにcloneを依頼 - // 現在は同じハンドルを持つ新しいプロキシを返す(簡易実装) - Box::new(PluginBox::new(self.plugin_name.clone(), self.handle)) - } - - fn share_box(&self) -> Box { - // 現在はclone_boxと同じ実装 - self.clone_box() - } - - fn to_string_box(&self) -> StringBox { - // FFI経由でプラグインのtoStringメソッド呼び出し - match self.call_plugin_method("toString", &[]) { - Ok(result) => result.to_string_box(), - Err(_) => { - // エラー時はフォールバック - StringBox::new(&format!("PluginBox({}, {:?})", self.plugin_name, self.handle)) - } - } - } - - fn type_name(&self) -> &'static str { - // TODO: プラグインから実際の型名を取得 - "PluginBox" - } - - fn equals(&self, other: &dyn NyashBox) -> BoolBox { - if let Some(other_plugin) = other.as_any().downcast_ref::() { - // 同じプラグイン&同じハンドルなら等しい - BoolBox::new( - self.plugin_name == other_plugin.plugin_name && - self.handle == other_plugin.handle - ) - } else { - BoolBox::new(false) - } - } -} - -impl fmt::Display for PluginBox { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.fmt_box(f) - } -} - -impl Clone for PluginBox { - fn clone(&self) -> Self { - Self { - base: BoxBase::new(), // 新しいIDを生成 - plugin_name: self.plugin_name.clone(), - handle: self.handle, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::bid::BoxTypeId; - - #[test] - fn test_plugin_box_creation() { - let handle = BidHandle::new(BoxTypeId::FileBox as u32, 123); - let plugin_box = PluginBox::new("filebox".to_string(), handle); - - assert_eq!(plugin_box.plugin_name(), "filebox"); - assert_eq!(plugin_box.handle(), handle); - } - - #[test] - fn test_plugin_box_equality() { - let handle1 = BidHandle::new(BoxTypeId::FileBox as u32, 123); - let handle2 = BidHandle::new(BoxTypeId::FileBox as u32, 456); - - let box1 = PluginBox::new("filebox".to_string(), handle1); - let box2 = PluginBox::new("filebox".to_string(), handle1); - let box3 = PluginBox::new("filebox".to_string(), handle2); - - assert!(box1.equals(&box2).value); - assert!(!box1.equals(&box3).value); - } -} \ No newline at end of file diff --git a/tools/plugin-tester/src/main_old.rs b/tools/plugin-tester/src/main_old.rs deleted file mode 100644 index a5ca22a4..00000000 --- a/tools/plugin-tester/src/main_old.rs +++ /dev/null @@ -1,788 +0,0 @@ -//! Nyash Plugin Tester - Multi-Box Type Support (v2) -//! -//! プラグイン開発者向けの診断ツール -//! 単一Box型・複数Box型の両方をサポート - -use clap::{Parser, Subcommand}; -use colored::*; -use libloading::{Library, Symbol}; -use serde::Deserialize; -use std::collections::HashMap; -use std::ffi::{CStr, CString}; -use std::fs; -use std::os::raw::{c_char, c_void}; -use std::path::PathBuf; -use std::io::Write; - -// ============ FFI Types (プラグインと同じ定義) ============ - -#[repr(C)] -pub struct NyashHostVtable { - pub alloc: unsafe extern "C" fn(size: usize) -> *mut u8, - pub free: unsafe extern "C" fn(ptr: *mut u8), - pub wake: unsafe extern "C" fn(handle: u64), - pub log: unsafe extern "C" fn(level: i32, msg: *const c_char), -} - -#[repr(C)] -pub struct NyashMethodInfo { - pub method_id: u32, - pub name: *const c_char, - pub signature: u32, -} - -#[repr(C)] -pub struct NyashPluginInfo { - pub type_id: u32, - pub type_name: *const c_char, - pub method_count: usize, - pub methods: *const NyashMethodInfo, -} - -// ============ TOML Configuration Types ============ - -#[derive(Debug)] -struct NyashConfig { - plugins: HashMap, - plugin_configs: HashMap, -} - -#[derive(Debug)] -struct PluginConfig { - methods: HashMap, -} - -#[derive(Debug, Deserialize)] -struct MethodDef { - args: Vec, - #[serde(skip_serializing_if = "Option::is_none")] - returns: Option, -} - -#[derive(Debug, Deserialize)] -struct ArgDef { - #[serde(skip_serializing_if = "Option::is_none")] - name: Option, - from: String, - to: String, -} - -// ============ CLI ============ - -#[derive(Parser)] -#[command(name = "plugin-tester")] -#[command(about = "Nyash plugin testing tool", long_about = None)] -struct Args { - #[command(subcommand)] - command: Commands, -} - -#[derive(Subcommand)] -enum Commands { - /// Check plugin exports and basic functionality - Check { - /// Path to plugin .so file - plugin: PathBuf, - - /// Check for multiple Box types (v2 plugin) - #[arg(short = 'm', long)] - multi: bool, - }, - /// Test Box lifecycle (birth/fini) - Lifecycle { - /// Path to plugin .so file - plugin: PathBuf, - - /// Specify Box type name (for multi-box plugins) - #[arg(short = 'b', long)] - box_type: Option, - }, - /// Test file I/O operations - Io { - /// Path to plugin .so file - plugin: PathBuf, - }, - /// Debug TLV encoding/decoding - TlvDebug { - /// Path to plugin .so file (optional) - #[arg(short, long)] - plugin: Option, - - /// Test message to encode/decode - #[arg(short, long, default_value = "Hello TLV Debug!")] - message: String, - }, - /// Validate plugin type information against nyash.toml - Typecheck { - /// Path to plugin .so file - plugin: PathBuf, - /// Path to nyash.toml configuration file - #[arg(short, long, default_value = "../../nyash.toml")] - config: PathBuf, - }, -} - -// ============ Host Functions (テスト用実装) ============ - -unsafe extern "C" fn test_alloc(size: usize) -> *mut u8 { - let layout = std::alloc::Layout::from_size_align(size, 8).unwrap(); - std::alloc::alloc(layout) -} - -unsafe extern "C" fn test_free(ptr: *mut u8) { - if !ptr.is_null() { - // サイズ情報が必要だが、簡易実装のため省略 - } -} - -unsafe extern "C" fn test_wake(_handle: u64) { - // テスト用なので何もしない -} - -unsafe extern "C" fn test_log(level: i32, msg: *const c_char) { - if !msg.is_null() { - let c_str = CStr::from_ptr(msg); - let message = c_str.to_string_lossy(); - - match level { - 0 => println!("{}: {}", "DEBUG".blue(), message), - 1 => println!("{}: {}", "INFO".green(), message), - 2 => println!("{}: {}", "WARN".yellow(), message), - 3 => println!("{}: {}", "ERROR".red(), message), - _ => println!("{}: {}", "UNKNOWN".white(), message), - } - } -} - -static HOST_VTABLE: NyashHostVtable = NyashHostVtable { - alloc: test_alloc, - free: test_free, - wake: test_wake, - log: test_log, -}; - -// ============ Main Functions ============ - -fn main() { - let args = Args::parse(); - - match args.command { - Commands::Check { plugin, multi } => { - if multi { - check_multi_box_plugin(&plugin) - } else { - check_plugin(&plugin) - } - }, - Commands::Lifecycle { plugin, box_type } => test_lifecycle(&plugin, box_type), - Commands::Io { plugin } => test_file_io(&plugin), - Commands::TlvDebug { plugin, message } => test_tlv_debug(&plugin, &message), - Commands::Typecheck { plugin, config } => typecheck_plugin(&plugin, &config), - } -} - -// ============ Minimal BID-1 TLV Helpers ============ - -#[repr(C)] -#[derive(Clone, Copy)] -struct TlvHeader { version: u16, argc: u16 } - -const TLV_VERSION: u16 = 1; - -#[repr(u8)] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -enum Tag { Bool=1, I32=2, I64=3, F32=4, F64=5, String=6, Bytes=7, Handle=8, Void=9 } - -fn tlv_encode_string(s: &str, buf: &mut Vec) { - let header_pos = buf.len(); - buf.extend_from_slice(&[0,0,0,0]); - let mut argc: u16 = 0; - // entry - let bytes = s.as_bytes(); - buf.push(Tag::String as u8); - buf.push(0); - buf.extend_from_slice(&(bytes.len() as u16).to_le_bytes()); - buf.extend_from_slice(bytes); - argc += 1; - // write header - buf[header_pos..header_pos+2].copy_from_slice(&TLV_VERSION.to_le_bytes()); - buf[header_pos+2..header_pos+4].copy_from_slice(&argc.to_le_bytes()); -} - -fn tlv_encode_two_strings(a: &str, b: &str, buf: &mut Vec) { - let header_pos = buf.len(); - buf.extend_from_slice(&[0,0,0,0]); - let mut argc: u16 = 0; - for s in [a,b] { - let bytes = s.as_bytes(); - buf.push(Tag::String as u8); - buf.push(0); - buf.extend_from_slice(&(bytes.len() as u16).to_le_bytes()); - buf.extend_from_slice(bytes); - argc += 1; - } - buf[header_pos..header_pos+2].copy_from_slice(&TLV_VERSION.to_le_bytes()); - buf[header_pos+2..header_pos+4].copy_from_slice(&argc.to_le_bytes()); -} - -fn tlv_decode_i32(data: &[u8]) -> Result { - if data.len() < 12 { - return Err("Buffer too short for I32 TLV".to_string()); - } - let version = u16::from_le_bytes([data[0], data[1]]); - let argc = u16::from_le_bytes([data[2], data[3]]); - if version != TLV_VERSION || argc != 1 { - return Err(format!("Invalid TLV header: v{} argc={}", version, argc)); - } - let tag = data[4]; - if tag != Tag::I32 as u8 { - return Err(format!("Expected I32 tag, got {}", tag)); - } - let len = u16::from_le_bytes([data[6], data[7]]); - if len != 4 { - return Err(format!("Invalid I32 length: {}", len)); - } - Ok(i32::from_le_bytes([data[8], data[9], data[10], data[11]])) -} - -// ============ Plugin Check Functions ============ - -fn check_plugin(path: &PathBuf) { - println!("{}", "=== Plugin Check (Single Box Type) ===".bold()); - println!("Plugin: {}", path.display()); - - let library = match unsafe { Library::new(path) } { - Ok(lib) => lib, - Err(e) => { - eprintln!("{}: Failed to load plugin: {}", "ERROR".red(), e); - return; - } - }; - - println!("{}: Plugin loaded successfully", "✓".green()); - - // ABI version確認 - unsafe { - let abi_fn: Symbol u32> = match library.get(b"nyash_plugin_abi") { - Ok(f) => f, - Err(e) => { - eprintln!("{}: nyash_plugin_abi not found: {}", "ERROR".red(), e); - return; - } - }; - - let abi_version = abi_fn(); - println!("{}: ABI version: {}", "✓".green(), abi_version); - - if abi_version != 1 { - eprintln!("{}: Unsupported ABI version (expected 1)", "WARNING".yellow()); - } - } - - // Plugin初期化とBox名取得 - unsafe { - let init_fn: Symbol i32> = - match library.get(b"nyash_plugin_init") { - Ok(f) => f, - Err(e) => { - eprintln!("{}: nyash_plugin_init not found: {}", "ERROR".red(), e); - return; - } - }; - - let mut plugin_info = std::mem::zeroed::(); - let result = init_fn(&HOST_VTABLE, &mut plugin_info); - - if result != 0 { - eprintln!("{}: nyash_plugin_init failed with code {}", "ERROR".red(), result); - return; - } - - println!("{}: Plugin initialized", "✓".green()); - - // 重要:Box名をプラグインから取得(決め打ちしない!) - let box_name = if plugin_info.type_name.is_null() { - "".to_string() - } else { - CStr::from_ptr(plugin_info.type_name).to_string_lossy().to_string() - }; - - println!("\n{}", "Plugin Information:".bold()); - println!(" Box Type: {} (ID: {})", box_name.cyan(), plugin_info.type_id); - println!(" Methods: {}", plugin_info.method_count); - - // メソッド一覧表示 - if plugin_info.method_count > 0 && !plugin_info.methods.is_null() { - println!("\n{}", "Methods:".bold()); - let methods = std::slice::from_raw_parts(plugin_info.methods, plugin_info.method_count); - - for method in methods { - let method_name = if method.name.is_null() { - "".to_string() - } else { - CStr::from_ptr(method.name).to_string_lossy().to_string() - }; - - let method_type = match method.method_id { - 0 => " (constructor)".yellow(), - id if id == u32::MAX => " (destructor)".yellow(), - _ => "".normal(), - }; - - println!(" - {} [ID: {}, Sig: 0x{:08X}]{}", - method_name, - method.method_id, - method.signature, - method_type - ); - } - } - } - - // シャットダウン - unsafe { - if let Ok(shutdown_fn) = library.get::>(b"nyash_plugin_shutdown") { - shutdown_fn(); - println!("\n{}: Plugin shutdown completed", "✓".green()); - } - } - - println!("\n{}", "Check completed!".green().bold()); -} - -// ============ Multi-Box Plugin Support (v2) ============ - -fn check_multi_box_plugin(path: &PathBuf) { - println!("{}", "=== Plugin Check (Multi-Box Type v2) ===".bold()); - println!("Plugin: {}", path.display()); - - let library = match unsafe { Library::new(path) } { - Ok(lib) => lib, - Err(e) => { - eprintln!("{}: Failed to load plugin: {}", "ERROR".red(), e); - return; - } - }; - - println!("{}: Plugin loaded successfully", "✓".green()); - - // Check for v2 functions - unsafe { - // Check if this is a v2 plugin - let has_v2 = library.get:: u32>>(b"nyash_plugin_get_box_count").is_ok(); - - if !has_v2 { - println!("{}: This is not a v2 multi-box plugin", "INFO".yellow()); - println!(" Falling back to single-box check...\n"); - drop(library); - check_plugin(path); - return; - } - - // Get box count - let get_count_fn: Symbol u32> = - library.get(b"nyash_plugin_get_box_count").unwrap(); - - let box_count = get_count_fn(); - println!("{}: Plugin provides {} Box types", "✓".green(), box_count); - - // Get box info function - let get_info_fn: Symbol *const NyashPluginInfo> = - match library.get(b"nyash_plugin_get_box_info") { - Ok(f) => f, - Err(e) => { - eprintln!("{}: nyash_plugin_get_box_info not found: {}", "ERROR".red(), e); - return; - } - }; - - // Initialize plugin - let init_fn: Symbol i32> = - match library.get(b"nyash_plugin_init") { - Ok(f) => f, - Err(e) => { - eprintln!("{}: nyash_plugin_init not found: {}", "ERROR".red(), e); - return; - } - }; - - let result = init_fn(&HOST_VTABLE, std::ptr::null_mut()); - if result != 0 { - eprintln!("{}: Plugin initialization failed", "ERROR".red()); - return; - } - - println!("\n{}", "Box Types:".bold()); - - // Display info for each Box type - for i in 0..box_count { - let info_ptr = get_info_fn(i); - if info_ptr.is_null() { - eprintln!("{}: Failed to get info for box index {}", "ERROR".red(), i); - continue; - } - - let info = &*info_ptr; - let box_name = if info.type_name.is_null() { - "".to_string() - } else { - CStr::from_ptr(info.type_name).to_string_lossy().to_string() - }; - - println!("\n {}. {} (ID: {})", i + 1, box_name.cyan(), info.type_id); - println!(" Methods: {}", info.method_count); - - // Display methods - if info.method_count > 0 && !info.methods.is_null() { - let methods = std::slice::from_raw_parts(info.methods, info.method_count); - - for method in methods { - let method_name = if method.name.is_null() { - "".to_string() - } else { - CStr::from_ptr(method.name).to_string_lossy().to_string() - }; - - let method_type = match method.method_id { - 0 => " (constructor)".yellow(), - id if id == u32::MAX => " (destructor)".yellow(), - _ => "".normal(), - }; - - println!(" - {} [ID: {}]{}", - method_name, - method.method_id, - method_type - ); - } - } - } - - // Check for get_type_id function - if let Ok(get_type_id_fn) = library.get:: u32>>(b"nyash_plugin_get_type_id") { - println!("\n{}: Plugin supports type name resolution", "✓".green()); - - // Test type name resolution - for test_name in ["TestBoxA", "TestBoxB", "UnknownBox"] { - let c_name = CString::new(test_name).unwrap(); - let type_id = get_type_id_fn(c_name.as_ptr()); - if type_id != 0 { - println!(" {} -> type_id: {}", test_name, type_id); - } else { - println!(" {} -> not found", test_name.dimmed()); - } - } - } - } - - println!("\n{}", "Multi-box check completed!".green().bold()); -} - -fn test_lifecycle(path: &PathBuf, box_type: Option) { - println!("{}", "=== Lifecycle Test ===".bold()); - - // Load plugin - let library = match unsafe { Library::new(path) } { - Ok(lib) => lib, - Err(e) => { - eprintln!("{}: Failed to load plugin: {}", "ERROR".red(), e); - return; - } - }; - - unsafe { - // Initialize plugin - let init_fn: Symbol i32> = - match library.get(b"nyash_plugin_init") { - Ok(f) => f, - Err(e) => { - eprintln!("{}: nyash_plugin_init not found: {}", "ERROR".red(), e); - return; - } - }; - - let mut plugin_info = std::mem::zeroed::(); - let result = init_fn(&HOST_VTABLE, &mut plugin_info); - - if result != 0 { - eprintln!("{}: Plugin initialization failed", "ERROR".red()); - return; - } - - // Get invoke function - let invoke_fn: Symbol i32> = - match library.get(b"nyash_plugin_invoke") { - Ok(f) => f, - Err(e) => { - eprintln!("{}: nyash_plugin_invoke not found: {}", "ERROR".red(), e); - return; - } - }; - - // Determine type_id - let type_id = if let Some(ref box_name) = box_type { - // For multi-box plugins, resolve type_id from name - if let Ok(get_type_id_fn) = library.get:: u32>>(b"nyash_plugin_get_type_id") { - let c_name = CString::new(box_name.as_str()).unwrap(); - let id = get_type_id_fn(c_name.as_ptr()); - if id == 0 { - eprintln!("{}: Box type '{}' not found", "ERROR".red(), box_name); - return; - } - id - } else { - eprintln!("{}: Multi-box plugin doesn't support type name resolution", "ERROR".red()); - return; - } - } else { - plugin_info.type_id - }; - - println!("Testing lifecycle for type_id: {}", type_id); - - // Test birth - println!("\n{}", "1. Testing birth (constructor)...".cyan()); - - let mut result_buf = vec![0u8; 1024]; - let mut result_len = result_buf.len(); - - let result = invoke_fn( - type_id, - 0, // METHOD_BIRTH - 0, // instance_id = 0 for birth - std::ptr::null(), - 0, - result_buf.as_mut_ptr(), - &mut result_len - ); - - if result != 0 { - eprintln!("{}: Birth failed with code {}", "ERROR".red(), result); - return; - } - - // Parse instance_id from result - let instance_id = if result_len >= 4 { - u32::from_le_bytes([result_buf[0], result_buf[1], result_buf[2], result_buf[3]]) - } else { - eprintln!("{}: Invalid birth response", "ERROR".red()); - return; - }; - - println!("{}: Birth successful, instance_id = {}", "✓".green(), instance_id); - - // Test a method if FileBox - if plugin_info.type_name != std::ptr::null() { - let box_name = CStr::from_ptr(plugin_info.type_name).to_string_lossy(); - if box_name == "FileBox" { - test_file_operations(&invoke_fn, type_id, instance_id); - } - } - - // Test fini - println!("\n{}", "2. Testing fini (destructor)...".cyan()); - - result_len = result_buf.len(); - let result = invoke_fn( - type_id, - u32::MAX, // METHOD_FINI - instance_id, - std::ptr::null(), - 0, - result_buf.as_mut_ptr(), - &mut result_len - ); - - if result != 0 { - eprintln!("{}: Fini failed with code {}", "ERROR".red(), result); - } else { - println!("{}: Fini successful", "✓".green()); - } - } - - println!("\n{}", "Lifecycle test completed!".green().bold()); -} - -fn test_file_operations( - invoke_fn: &Symbol i32>, - type_id: u32, - instance_id: u32 -) { - println!("\n{}", "Testing file operations...".cyan()); - - // Test open - let mut args = Vec::new(); - tlv_encode_two_strings("test_lifecycle.txt", "w", &mut args); - - let mut result_buf = vec![0u8; 1024]; - let mut result_len = result_buf.len(); - - unsafe { - let result = invoke_fn( - type_id, - 1, // METHOD_OPEN - instance_id, - args.as_ptr(), - args.len(), - result_buf.as_mut_ptr(), - &mut result_len - ); - - if result == 0 { - println!("{}: Open successful", "✓".green()); - } else { - eprintln!("{}: Open failed", "ERROR".red()); - } - } -} - -fn test_file_io(path: &PathBuf) { - println!("{}", "=== File I/O Test ===".bold()); - println!("(Full I/O test implementation omitted for brevity)"); - println!("Use lifecycle test with FileBox for basic I/O testing"); -} - -fn test_tlv_debug(plugin: &Option, message: &str) { - println!("{}", "=== TLV Debug ===".bold()); - - // Encode string - let mut encoded = Vec::new(); - tlv_encode_string(message, &mut encoded); - - println!("Original message: {}", message.cyan()); - println!("Encoded bytes ({} bytes):", encoded.len()); - - // Display hex dump - for (i, chunk) in encoded.chunks(16).enumerate() { - print!("{:04x}: ", i * 16); - for byte in chunk { - print!("{:02x} ", byte); - } - println!(); - } - - // Decode header - if encoded.len() >= 4 { - let version = u16::from_le_bytes([encoded[0], encoded[1]]); - let argc = u16::from_le_bytes([encoded[2], encoded[3]]); - println!("\nTLV Header:"); - println!(" Version: {}", version); - println!(" Arg count: {}", argc); - } -} - -fn typecheck_plugin(plugin_path: &PathBuf, config_path: &PathBuf) { - println!("{}", "=== Type Check ===".bold()); - - // Load nyash.toml - let config_content = match fs::read_to_string(config_path) { - Ok(content) => content, - Err(e) => { - eprintln!("{}: Failed to read config: {}", "ERROR".red(), e); - return; - } - }; - - let config_value: toml::Value = match toml::from_str(&config_content) { - Ok(val) => val, - Err(e) => { - eprintln!("{}: Failed to parse TOML: {}", "ERROR".red(), e); - return; - } - }; - - // Load plugin - let library = match unsafe { Library::new(plugin_path) } { - Ok(lib) => lib, - Err(e) => { - eprintln!("{}: Failed to load plugin: {}", "ERROR".red(), e); - return; - } - }; - - unsafe { - // Get plugin info - let init_fn: Symbol i32> = - match library.get(b"nyash_plugin_init") { - Ok(f) => f, - Err(_) => { - eprintln!("{}: Plugin doesn't export nyash_plugin_init", "ERROR".red()); - return; - } - }; - - let mut plugin_info = std::mem::zeroed::(); - let result = init_fn(&HOST_VTABLE, &mut plugin_info); - - if result != 0 { - eprintln!("{}: Plugin initialization failed", "ERROR".red()); - return; - } - - let box_name = if plugin_info.type_name.is_null() { - eprintln!("{}: Plugin doesn't provide type name", "ERROR".red()); - return; - } else { - CStr::from_ptr(plugin_info.type_name).to_string_lossy().to_string() - }; - - println!("Plugin Box type: {}", box_name.cyan()); - - // Check if box is configured in nyash.toml - if let Some(plugins) = config_value.get("plugins").and_then(|v| v.as_table()) { - if let Some(plugin_name) = plugins.get(&box_name).and_then(|v| v.as_str()) { - println!("{}: {} is configured as '{}'", "✓".green(), box_name, plugin_name); - - // Check method definitions - let methods_key = format!("plugins.{}.methods", box_name); - if let Some(methods) = config_value.get("plugins") - .and_then(|v| v.get(&box_name)) - .and_then(|v| v.get("methods")) - .and_then(|v| v.as_table()) { - - println!("\n{}", "Configured methods:".bold()); - - // Get actual methods from plugin - let actual_methods = if plugin_info.method_count > 0 && !plugin_info.methods.is_null() { - let methods = std::slice::from_raw_parts(plugin_info.methods, plugin_info.method_count); - methods.iter() - .filter_map(|m| { - if m.name.is_null() { - None - } else { - Some(CStr::from_ptr(m.name).to_string_lossy().to_string()) - } - }) - .collect::>() - } else { - vec![] - }; - - for (method_name, _method_def) in methods { - let status = if actual_methods.contains(method_name) { - format!("{}", "✓".green()) - } else { - format!("{}", "✗".red()) - }; - println!(" {} {}", status, method_name); - } - - // Check for duplicate method names - let mut seen = std::collections::HashSet::new(); - for method in &actual_methods { - if !seen.insert(method) { - eprintln!("{}: Duplicate method name: {}", "WARNING".yellow(), method); - eprintln!(" Note: Nyash doesn't support function overloading"); - } - } - } else { - eprintln!("{}: No method definitions found for {}", "WARNING".yellow(), box_name); - } - } else { - eprintln!("{}: {} is not configured in nyash.toml", "WARNING".yellow(), box_name); - } - } - } - - println!("\n{}", "Type check completed!".green().bold()); -} \ No newline at end of file