/*! * Nyash AST (Abstract Syntax Tree) - Rust Implementation * * Python版nyashc_v4.pyのAST構造をRustで完全再実装 * Everything is Box哲学に基づく型安全なAST設計 */ use crate::box_trait::NyashBox; use std::collections::HashMap; use std::fmt; /// ソースコード位置情報 - エラー報告とデバッグの革命 #[derive(Debug, Clone, Copy, PartialEq)] pub struct Span { pub start: usize, // 開始位置(バイトオフセット) pub end: usize, // 終了位置(バイトオフセット) pub line: usize, // 行番号(1から開始) pub column: usize, // 列番号(1から開始) } impl Span { /// 新しいSpanを作成 pub fn new(start: usize, end: usize, line: usize, column: usize) -> Self { Self { start, end, line, column } } /// デフォルトのSpan(不明な位置) pub fn unknown() -> Self { Self { start: 0, end: 0, line: 1, column: 1 } } /// 2つのSpanを結合(開始位置から終了位置まで) pub fn merge(&self, other: Span) -> Span { Span { start: self.start.min(other.start), end: self.end.max(other.end), line: self.line, column: self.column, } } /// ソースコードから該当箇所を抽出してエラー表示用文字列を生成 pub fn error_context(&self, source: &str) -> String { let lines: Vec<&str> = source.lines().collect(); if self.line == 0 || self.line > lines.len() { return format!("line {}, column {}", self.line, self.column); } let line_content = lines[self.line - 1]; let mut context = String::new(); // 行番号とソース行を表示 context.push_str(&format!(" |\n{:3} | {}\n", self.line, line_content)); // カーソル位置を表示(簡易版) if self.column > 0 && self.column <= line_content.len() + 1 { context.push_str(" | "); for _ in 1..self.column { context.push(' '); } let span_length = if self.end > self.start { (self.end - self.start).min(line_content.len() - self.column + 1) } else { 1 }; for _ in 0..span_length.max(1) { context.push('^'); } context.push('\n'); } context } /// 位置情報の文字列表現 pub fn location_string(&self) -> String { format!("line {}, column {}", self.line, self.column) } } impl fmt::Display for Span { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "line {}, column {}", self.line, self.column) } } /// 🌟 AST分類システム - ChatGPTアドバイス統合による3層アーキテクチャ /// Structure/Expression/Statement の明確な分離による型安全性向上 /// ASTノードの種類分類 #[derive(Debug, Clone, PartialEq)] pub enum ASTNodeType { Structure, // 構造定義: box, function, if, loop, try/catch Expression, // 式: リテラル, 変数, 演算, 呼び出し Statement, // 文: 代入, return, break, include } /// 構造ノード - 言語の基本構造を定義 #[derive(Debug, Clone)] pub enum StructureNode { BoxDeclaration { name: String, fields: Vec, methods: Vec, constructors: Vec, init_fields: Vec, is_interface: bool, extends: Option, implements: Vec, /// 🔥 ジェネリクス型パラメータ (例: ["T", "U"]) type_parameters: Vec, /// 🔥 Static boxかどうかのフラグ is_static: bool, /// 🔥 Static初期化ブロック (static { ... }) static_init: Option>, span: Span, }, FunctionDeclaration { name: String, params: Vec, body: Vec, is_static: bool, // 🔥 静的メソッドフラグ is_override: bool, // 🔥 オーバーライドフラグ span: Span, }, IfStructure { condition: Box, then_body: Vec, else_body: Option>, span: Span, }, LoopStructure { condition: Box, body: Vec, span: Span, }, TryCatchStructure { try_body: Vec, catch_clauses: Vec, finally_body: Option>, span: Span, }, } /// 式ノード - 値を生成する表現 #[derive(Debug, Clone)] pub enum ExpressionNode { Literal { value: LiteralValue, span: Span, }, Variable { name: String, span: Span, }, BinaryOperation { operator: BinaryOperator, left: Box, right: Box, span: Span, }, UnaryOperation { operator: UnaryOperator, operand: Box, span: Span, }, FunctionCall { name: String, arguments: Vec, span: Span, }, MethodCall { object: Box, method: String, arguments: Vec, span: Span, }, FieldAccess { object: Box, field: String, span: Span, }, NewExpression { class: String, arguments: Vec, /// 🔥 ジェネリクス型引数 (例: ["IntegerBox", "StringBox"]) type_arguments: Vec, span: Span, }, ThisExpression { span: Span, }, MeExpression { span: Span, }, } /// 文ノード - 実行可能なアクション #[derive(Debug, Clone)] pub enum StatementNode { Assignment { target: Box, value: Box, span: Span, }, Print { expression: Box, span: Span, }, Return { value: Option>, span: Span, }, Break { span: Span, }, Include { filename: String, span: Span, }, Local { variables: Vec, span: Span, }, Throw { exception_type: String, message: Box, span: Span, }, Expression { expr: Box, span: Span, }, } /// Catch節の構造体 #[derive(Debug, Clone)] pub struct CatchClause { pub exception_type: Option, // None = catch-all pub variable_name: Option, // 例外を受け取る変数名 pub body: Vec, // catch本体 pub span: Span, // ソースコード位置 } /// リテラル値の型 (Clone可能) #[derive(Debug, Clone)] pub enum LiteralValue { String(String), Integer(i64), Float(f64), // 浮動小数点数サポート追加 Bool(bool), Void, } impl LiteralValue { /// LiteralValueをNyashBoxに変換 pub fn to_nyash_box(&self) -> Box { use crate::box_trait::{StringBox, IntegerBox, BoolBox, VoidBox}; use crate::boxes::math_box::FloatBox; match self { LiteralValue::String(s) => Box::new(StringBox::new(s)), LiteralValue::Integer(i) => Box::new(IntegerBox::new(*i)), LiteralValue::Float(f) => Box::new(FloatBox::new(*f)), LiteralValue::Bool(b) => Box::new(BoolBox::new(*b)), LiteralValue::Void => Box::new(VoidBox::new()), } } /// NyashBoxからLiteralValueに変換 pub fn from_nyash_box(box_val: &dyn NyashBox) -> Option { #[allow(unused_imports)] use std::any::Any; use crate::box_trait::{StringBox, IntegerBox, BoolBox, VoidBox}; use crate::boxes::math_box::FloatBox; if let Some(string_box) = box_val.as_any().downcast_ref::() { Some(LiteralValue::String(string_box.value.clone())) } else if let Some(int_box) = box_val.as_any().downcast_ref::() { Some(LiteralValue::Integer(int_box.value)) } else if let Some(float_box) = box_val.as_any().downcast_ref::() { Some(LiteralValue::Float(float_box.value)) } else if let Some(bool_box) = box_val.as_any().downcast_ref::() { Some(LiteralValue::Bool(bool_box.value)) } else if box_val.as_any().downcast_ref::().is_some() { Some(LiteralValue::Void) } else { None } } } impl fmt::Display for LiteralValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { LiteralValue::String(s) => write!(f, "\"{}\"", s), LiteralValue::Integer(i) => write!(f, "{}", i), LiteralValue::Float(fl) => write!(f, "{}", fl), LiteralValue::Bool(b) => write!(f, "{}", b), LiteralValue::Void => write!(f, "void"), } } } /// 単項演算子の種類 #[derive(Debug, Clone, PartialEq)] pub enum UnaryOperator { Minus, // -x Not, // not x } /// 二項演算子の種類 #[derive(Debug, Clone, PartialEq)] pub enum BinaryOperator { Add, Subtract, Multiply, Divide, Equal, NotEqual, Less, Greater, LessEqual, GreaterEqual, And, Or, } impl fmt::Display for UnaryOperator { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let symbol = match self { UnaryOperator::Minus => "-", UnaryOperator::Not => "not", }; write!(f, "{}", symbol) } } impl fmt::Display for BinaryOperator { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let symbol = match self { BinaryOperator::Add => "+", BinaryOperator::Subtract => "-", BinaryOperator::Multiply => "*", BinaryOperator::Divide => "/", BinaryOperator::Equal => "==", BinaryOperator::NotEqual => "!=", BinaryOperator::Less => "<", BinaryOperator::Greater => ">", BinaryOperator::LessEqual => "<=", BinaryOperator::GreaterEqual => ">=", BinaryOperator::And => "&&", BinaryOperator::Or => "||", }; write!(f, "{}", symbol) } } /// AST Node - Everything is Box哲学に基づく統一構造 #[derive(Debug, Clone)] pub enum ASTNode { /// プログラム全体 - 文のリスト Program { statements: Vec, span: Span, }, // ===== 文 (Statements) ===== /// 代入文: target = value Assignment { target: Box, value: Box, span: Span, }, /// print文: print(expression) Print { expression: Box, span: Span, }, /// if文: if condition { then_body } else { else_body } If { condition: Box, then_body: Vec, else_body: Option>, span: Span, }, /// loop文: loop(condition) { body } のみ Loop { condition: Box, body: Vec, span: Span, }, /// return文: return value Return { value: Option>, span: Span, }, /// break文 Break { span: Span, }, /// nowait文: nowait variable = expression Nowait { variable: String, expression: Box, span: Span, }, /// await式: await expression AwaitExpression { expression: Box, span: Span, }, /// arrow文: (sender >> receiver).method(args) Arrow { sender: Box, receiver: Box, span: Span, }, /// try/catch/finally文: try { ... } catch (Type e) { ... } finally { ... } TryCatch { try_body: Vec, catch_clauses: Vec, finally_body: Option>, span: Span, }, /// throw文: throw expression Throw { expression: Box, span: Span, }, // ===== 宣言 (Declarations) ===== /// box宣言: box Name { fields... methods... } BoxDeclaration { name: String, fields: Vec, methods: HashMap, // method_name -> FunctionDeclaration constructors: HashMap, // constructor_key -> FunctionDeclaration init_fields: Vec, // initブロック内のフィールド定義 is_interface: bool, // interface box かどうか extends: Option, // 継承元のBox名 implements: Vec, // 実装するinterface名のリスト type_parameters: Vec, // 🔥 ジェネリクス型パラメータ (例: ["T", "U"]) /// 🔥 Static boxかどうかのフラグ is_static: bool, /// 🔥 Static初期化ブロック (static { ... }) static_init: Option>, span: Span, }, /// 関数宣言: functionName(params) { body } FunctionDeclaration { name: String, params: Vec, body: Vec, is_static: bool, // 🔥 静的メソッドフラグ is_override: bool, // 🔥 オーバーライドフラグ span: Span, }, /// グローバル変数: global name = value GlobalVar { name: String, value: Box, span: Span, }, // ===== 式 (Expressions) ===== /// リテラル値: "string", 42, true, etc Literal { value: LiteralValue, span: Span, }, /// 変数参照: variableName Variable { name: String, span: Span, }, /// 単項演算: operator operand UnaryOp { operator: UnaryOperator, operand: Box, span: Span, }, /// 二項演算: left operator right BinaryOp { operator: BinaryOperator, left: Box, right: Box, span: Span, }, /// メソッド呼び出し: object.method(arguments) MethodCall { object: Box, method: String, arguments: Vec, span: Span, }, /// フィールドアクセス: object.field FieldAccess { object: Box, field: String, span: Span, }, /// コンストラクタ呼び出し: new ClassName(arguments) New { class: String, arguments: Vec, type_arguments: Vec, // 🔥 ジェネリクス型引数 (例: ["IntegerBox", "StringBox"]) span: Span, }, /// this参照 This { span: Span, }, /// me参照 Me { span: Span, }, /// 🔥 from呼び出し: from Parent.method(arguments) or from Parent.constructor(arguments) FromCall { parent: String, // Parent名 method: String, // method名またはconstructor arguments: Vec, // 引数 span: Span, }, /// thisフィールドアクセス: this.field ThisField { field: String, span: Span, }, /// meフィールドアクセス: me.field MeField { field: String, span: Span, }, /// ファイル読み込み: include "filename.nyash" Include { filename: String, span: Span, }, /// ローカル変数宣言: local x, y, z Local { variables: Vec, /// 初期化値(変数と同じ順序、Noneは初期化なし) initial_values: Vec>>, span: Span, }, /// Outbox変数宣言: outbox x, y, z (static関数内専用) Outbox { variables: Vec, /// 初期化値(変数と同じ順序、Noneは初期化なし) initial_values: Vec>>, span: Span, }, /// 関数呼び出し: functionName(arguments) FunctionCall { name: String, arguments: Vec, span: Span, }, } impl ASTNode { /// AST nodeの種類を文字列で取得 (デバッグ用) pub fn node_type(&self) -> &'static str { match self { ASTNode::Program { .. } => "Program", ASTNode::Assignment { .. } => "Assignment", ASTNode::Print { .. } => "Print", ASTNode::If { .. } => "If", ASTNode::Loop { .. } => "Loop", ASTNode::Return { .. } => "Return", ASTNode::Break { .. } => "Break", ASTNode::BoxDeclaration { .. } => "BoxDeclaration", ASTNode::FunctionDeclaration { .. } => "FunctionDeclaration", ASTNode::GlobalVar { .. } => "GlobalVar", ASTNode::Literal { .. } => "Literal", ASTNode::Variable { .. } => "Variable", ASTNode::UnaryOp { .. } => "UnaryOp", ASTNode::BinaryOp { .. } => "BinaryOp", ASTNode::MethodCall { .. } => "MethodCall", ASTNode::FieldAccess { .. } => "FieldAccess", ASTNode::New { .. } => "New", ASTNode::This { .. } => "This", ASTNode::Me { .. } => "Me", ASTNode::FromCall { .. } => "FromCall", ASTNode::ThisField { .. } => "ThisField", ASTNode::MeField { .. } => "MeField", ASTNode::Include { .. } => "Include", ASTNode::Local { .. } => "Local", ASTNode::Outbox { .. } => "Outbox", ASTNode::FunctionCall { .. } => "FunctionCall", ASTNode::Nowait { .. } => "Nowait", ASTNode::Arrow { .. } => "Arrow", ASTNode::TryCatch { .. } => "TryCatch", ASTNode::Throw { .. } => "Throw", ASTNode::AwaitExpression { .. } => "AwaitExpression", } } /// 🌟 AST分類 - ChatGPTアドバイス統合による革新的分類システム /// Structure/Expression/Statement の明確な分離 pub fn classify(&self) -> ASTNodeType { match self { // Structure nodes - 言語の基本構造 ASTNode::BoxDeclaration { .. } => ASTNodeType::Structure, ASTNode::FunctionDeclaration { .. } => ASTNodeType::Structure, ASTNode::If { .. } => ASTNodeType::Structure, ASTNode::Loop { .. } => ASTNodeType::Structure, ASTNode::TryCatch { .. } => ASTNodeType::Structure, // Expression nodes - 値を生成する表現 ASTNode::Literal { .. } => ASTNodeType::Expression, ASTNode::Variable { .. } => ASTNodeType::Expression, ASTNode::BinaryOp { .. } => ASTNodeType::Expression, ASTNode::UnaryOp { .. } => ASTNodeType::Expression, ASTNode::FunctionCall { .. } => ASTNodeType::Expression, ASTNode::MethodCall { .. } => ASTNodeType::Expression, ASTNode::FieldAccess { .. } => ASTNodeType::Expression, ASTNode::New { .. } => ASTNodeType::Expression, ASTNode::This { .. } => ASTNodeType::Expression, ASTNode::Me { .. } => ASTNodeType::Expression, ASTNode::FromCall { .. } => ASTNodeType::Expression, ASTNode::ThisField { .. } => ASTNodeType::Expression, ASTNode::MeField { .. } => ASTNodeType::Expression, // Statement nodes - 実行可能なアクション ASTNode::Program { .. } => ASTNodeType::Statement, // プログラム全体 ASTNode::Assignment { .. } => ASTNodeType::Statement, ASTNode::Print { .. } => ASTNodeType::Statement, ASTNode::Return { .. } => ASTNodeType::Statement, ASTNode::Break { .. } => ASTNodeType::Statement, ASTNode::GlobalVar { .. } => ASTNodeType::Statement, ASTNode::Include { .. } => ASTNodeType::Statement, ASTNode::Local { .. } => ASTNodeType::Statement, ASTNode::Outbox { .. } => ASTNodeType::Statement, ASTNode::Nowait { .. } => ASTNodeType::Statement, ASTNode::Arrow { .. } => ASTNodeType::Statement, ASTNode::Throw { .. } => ASTNodeType::Statement, ASTNode::AwaitExpression { .. } => ASTNodeType::Expression, } } /// 🎯 構造パターンチェック - 2段階パーサー用 pub fn is_structure(&self) -> bool { matches!(self.classify(), ASTNodeType::Structure) } /// ⚡ 式パターンチェック - 評価エンジン用 pub fn is_expression(&self) -> bool { matches!(self.classify(), ASTNodeType::Expression) } /// 📝 文パターンチェック - 実行エンジン用 pub fn is_statement(&self) -> bool { matches!(self.classify(), ASTNodeType::Statement) } /// AST nodeの詳細情報を取得 (デバッグ用) pub fn info(&self) -> String { match self { ASTNode::Program { statements, .. } => { format!("Program({} statements)", statements.len()) } ASTNode::Assignment { target, .. } => { format!("Assignment(target: {})", target.info()) } ASTNode::Print { .. } => "Print".to_string(), ASTNode::If { .. } => "If".to_string(), ASTNode::Loop { condition: _, body, .. } => { format!("Loop({} statements)", body.len()) } ASTNode::Return { value, .. } => { if value.is_some() { "Return(with value)".to_string() } else { "Return(void)".to_string() } } ASTNode::Break { .. } => "Break".to_string(), ASTNode::BoxDeclaration { name, fields, methods, constructors, is_interface, extends, implements, .. } => { let mut desc = if *is_interface { format!("InterfaceBox({}, {} methods", name, methods.len()) } else { format!("BoxDeclaration({}, {} fields, {} methods, {} constructors", name, fields.len(), methods.len(), constructors.len()) }; if let Some(parent) = extends { desc.push_str(&format!(", extends {}", parent)); } if !implements.is_empty() { desc.push_str(&format!(", implements [{}]", implements.join(", "))); } desc.push(')'); desc } ASTNode::FunctionDeclaration { name, params, body, is_static, is_override, .. } => { let static_str = if *is_static { "static " } else { "" }; let override_str = if *is_override { "override " } else { "" }; format!("FunctionDeclaration({}{}{}({}), {} statements)", override_str, static_str, name, params.join(", "), body.len()) } ASTNode::GlobalVar { name, .. } => { format!("GlobalVar({})", name) } ASTNode::Literal { .. } => "Literal".to_string(), ASTNode::Variable { name, .. } => { format!("Variable({})", name) } ASTNode::UnaryOp { operator, .. } => { format!("UnaryOp({})", operator) } ASTNode::BinaryOp { operator, .. } => { format!("BinaryOp({})", operator) } ASTNode::MethodCall { method, arguments, .. } => { format!("MethodCall({}, {} args)", method, arguments.len()) } ASTNode::FieldAccess { field, .. } => { format!("FieldAccess({})", field) } ASTNode::New { class, arguments, type_arguments, .. } => { if type_arguments.is_empty() { format!("New({}, {} args)", class, arguments.len()) } else { format!("New({}<{}>, {} args)", class, type_arguments.join(", "), arguments.len()) } } ASTNode::This { .. } => "This".to_string(), ASTNode::Me { .. } => "Me".to_string(), ASTNode::FromCall { parent, method, arguments, .. } => { format!("FromCall({}.{}, {} args)", parent, method, arguments.len()) } ASTNode::ThisField { field, .. } => { format!("ThisField({})", field) } ASTNode::MeField { field, .. } => { format!("MeField({})", field) } ASTNode::Include { filename, .. } => { format!("Include({})", filename) } ASTNode::Local { variables, .. } => { format!("Local({})", variables.join(", ")) } ASTNode::Outbox { variables, .. } => { format!("Outbox({})", variables.join(", ")) } ASTNode::FunctionCall { name, arguments, .. } => { format!("FunctionCall({}, {} args)", name, arguments.len()) } ASTNode::Nowait { variable, .. } => { format!("Nowait({})", variable) } ASTNode::Arrow { .. } => { "Arrow(>>)".to_string() } ASTNode::TryCatch { try_body, catch_clauses, finally_body, .. } => { let mut desc = format!("TryCatch({} try statements, {} catch clauses", try_body.len(), catch_clauses.len()); if finally_body.is_some() { desc.push_str(", has finally"); } desc.push(')'); desc } ASTNode::Throw { .. } => "Throw".to_string(), ASTNode::AwaitExpression { expression, .. } => { format!("Await({:?})", expression) } } } /// ASTノードからSpan情報を取得 pub fn span(&self) -> Span { match self { ASTNode::Program { span, .. } => *span, ASTNode::Assignment { span, .. } => *span, ASTNode::Print { span, .. } => *span, ASTNode::If { span, .. } => *span, ASTNode::Loop { span, .. } => *span, ASTNode::Return { span, .. } => *span, ASTNode::Break { span, .. } => *span, ASTNode::Nowait { span, .. } => *span, ASTNode::Arrow { span, .. } => *span, ASTNode::TryCatch { span, .. } => *span, ASTNode::Throw { span, .. } => *span, ASTNode::BoxDeclaration { span, .. } => *span, ASTNode::FunctionDeclaration { span, .. } => *span, ASTNode::GlobalVar { span, .. } => *span, ASTNode::Literal { span, .. } => *span, ASTNode::Variable { span, .. } => *span, ASTNode::UnaryOp { span, .. } => *span, ASTNode::BinaryOp { span, .. } => *span, ASTNode::MethodCall { span, .. } => *span, ASTNode::FieldAccess { span, .. } => *span, ASTNode::New { span, .. } => *span, ASTNode::This { span, .. } => *span, ASTNode::Me { span, .. } => *span, ASTNode::FromCall { span, .. } => *span, ASTNode::ThisField { span, .. } => *span, ASTNode::MeField { span, .. } => *span, ASTNode::Include { span, .. } => *span, ASTNode::Local { span, .. } => *span, ASTNode::Outbox { span, .. } => *span, ASTNode::FunctionCall { span, .. } => *span, ASTNode::AwaitExpression { span, .. } => *span, } } } impl fmt::Display for ASTNode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.info()) } } impl ASTNode { /// FunctionDeclarationのパラメータ数を取得 pub fn get_param_count(&self) -> usize { match self { ASTNode::FunctionDeclaration { params, .. } => params.len(), _ => 0, } } } // ===== Tests ===== #[cfg(test)] mod tests { use super::*; use crate::box_trait::{StringBox, IntegerBox, BoolBox}; #[test] fn test_ast_node_creation() { // Program node let program = ASTNode::Program { statements: vec![], span: Span::unknown(), }; assert_eq!(program.node_type(), "Program"); assert!(program.info().contains("Program(0 statements)")); // Variable node let variable = ASTNode::Variable { name: "test_var".to_string(), span: Span::unknown(), }; assert_eq!(variable.node_type(), "Variable"); assert!(variable.is_expression()); assert!(!variable.is_statement()); // Assignment node let assignment = ASTNode::Assignment { target: Box::new(ASTNode::Variable { name: "x".to_string(), span: Span::unknown() }), value: Box::new(ASTNode::Literal { value: LiteralValue::Integer(42), span: Span::unknown(), }), span: Span::unknown(), }; assert_eq!(assignment.node_type(), "Assignment"); assert!(!assignment.is_expression()); assert!(assignment.is_statement()); } #[test] fn test_binary_operator() { let add_op = BinaryOperator::Add; assert_eq!(format!("{}", add_op), "+"); let equals_op = BinaryOperator::Equal; assert_eq!(format!("{}", equals_op), "=="); let less_equals_op = BinaryOperator::LessEqual; assert_eq!(format!("{}", less_equals_op), "<="); } #[test] fn test_complex_ast() { // box TestBox { value }のAST let mut methods = HashMap::new(); methods.insert("getValue".to_string(), ASTNode::FunctionDeclaration { name: "getValue".to_string(), params: vec![], body: vec![ ASTNode::Return { value: Some(Box::new(ASTNode::FieldAccess { object: Box::new(ASTNode::This { span: Span::unknown() }), field: "value".to_string(), span: Span::unknown(), })), span: Span::unknown(), } ], is_static: false, // 通常のメソッド span: Span::unknown(), }); let box_decl = ASTNode::BoxDeclaration { name: "TestBox".to_string(), fields: vec!["value".to_string()], methods, constructors: HashMap::new(), init_fields: vec![], is_interface: false, extends: None, implements: vec![], span: Span::unknown(), }; assert_eq!(box_decl.node_type(), "BoxDeclaration"); assert!(box_decl.info().contains("TestBox")); assert!(box_decl.info().contains("1 fields")); assert!(box_decl.info().contains("1 methods")); } #[test] fn test_method_call() { // obj.getValue()のAST let method_call = ASTNode::MethodCall { object: Box::new(ASTNode::Variable { name: "obj".to_string(), span: Span::unknown() }), method: "getValue".to_string(), arguments: vec![], span: Span::unknown(), }; assert_eq!(method_call.node_type(), "MethodCall"); assert!(method_call.is_expression()); assert!(method_call.info().contains("getValue")); assert!(method_call.info().contains("0 args")); } #[test] fn test_binary_operation() { // x + y のAST let binary_op = ASTNode::BinaryOp { operator: BinaryOperator::Add, left: Box::new(ASTNode::Variable { name: "x".to_string(), span: Span::unknown() }), right: Box::new(ASTNode::Variable { name: "y".to_string(), span: Span::unknown() }), span: Span::unknown(), }; assert_eq!(binary_op.node_type(), "BinaryOp"); assert!(binary_op.is_expression()); assert!(binary_op.info().contains("+")); } }