diff --git a/CLAUDE.md b/CLAUDE.md index 5260cad7..3dba6040 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -60,6 +60,20 @@ cd projects/nyash-wasm - すべての値がBox(StringBox, IntegerBox, BoolBox等) - ユーザー定義Box: `box ClassName { init { field1, field2 } }` +### 🌟 完全明示デリゲーション(2025-08-11革命) +```nyash +// デリゲーション構文 +box Child from Parent { // from構文でデリゲーション + init(args) { // コンストラクタは「init」に統一 + from Parent.init(args) // 親の初期化 + } + + override method() { // 明示的オーバーライド必須 + from Parent.method() // 親メソッド呼び出し + } +} +``` + ### 🔄 統一ループ構文 ```nyash // ✅ 唯一の正しい形式 @@ -70,6 +84,32 @@ while condition { } // 使用不可 loop() { } // 使用不可 ``` +### 🎁 pack構文 - Box哲学の具現化(2025-08-11実装) +```nyash +// 🎁 「箱に詰める」直感的コンストラクタ +box User { + init { name, email } + + pack(userName, userEmail) { // ← Box哲学を体現! + me.name = userName + me.email = userEmail + } +} + +// 🔄 デリゲーションでのpack +box AdminUser from User { + init { permissions } + + pack(adminName, adminEmail, perms) { + from User.pack(adminName, adminEmail) // 親のpackを呼び出し + me.permissions = perms + } +} + +// ✅ 優先順位: pack > init > Box名形式 +local user = new User("Alice", "alice@example.com") // packが使われる +``` + ### 🎯 正統派Nyashスタイル(2025-08-09実装) ```nyash // 🚀 Static Box Main パターン - エントリーポイントの統一スタイル @@ -238,4 +278,9 @@ docs/ --- -最終更新: 2025年8月9日 - **🔥 パーサー無限ループ完全制圧!`--debug-fuel`引数でユーザー制御可能+must_advance!マクロによる早期検出システム完成。予約語問題も安全にエラー表示。静的Box Mainパターン+変数宣言厳密化と合わせて本格言語レベル到達!** \ No newline at end of file +最終更新: 2025年8月11日 - **🎁 `pack`構文革命完全達成!** +- **Everything is Packed**: Gemini・ChatGPT両先生一致推薦の`pack`コンストラクタ採用 +- **Box哲学具現化**: 「箱に詰める」直感的メタファーでコードを書くたび哲学体験 +- **完全実装**: `pack()`、`from Parent.pack()`、`pack`>`init`>Box名順優先選択 +- **革命的UX**: 他言語の`new`/`init`を超越、Nyash独自アイデンティティ確立 +- **デリゲーション完成**: `box Child from Parent`+`pack`+`override`+`from`統合完了 \ No newline at end of file diff --git a/src/interpreter/expressions.rs b/src/interpreter/expressions.rs index a3c40ede..c095590d 100644 --- a/src/interpreter/expressions.rs +++ b/src/interpreter/expressions.rs @@ -748,8 +748,8 @@ impl NyashInterpreter { drop(box_declarations); // ロック早期解放 - // 4. constructorの場合の特別処理 - if method == "constructor" { + // 4. constructorまたはinitまたはpackの場合の特別処理 + if method == "constructor" || method == "init" || method == "pack" || method == parent { return self.execute_from_parent_constructor(parent, &parent_box_decl, current_instance_val.clone_box(), arguments); } @@ -820,16 +820,17 @@ impl NyashInterpreter { current_instance: Box, arguments: &[ASTNode]) -> Result, RuntimeError> { - // 1. 親クラスのコンストラクタを取得(デフォルトコンストラクタまたは指定されたもの) - let constructor_name = if arguments.is_empty() { - "constructor" - } else { - "constructor" // TODO: 将来的に名前付きコンストラクタ対応 - }; + // 1. 親クラスのコンストラクタを取得(引数の数でキーを作成) + // "pack/引数数"、"init/引数数"、"Box名/引数数" の順で試す + let pack_key = format!("pack/{}", arguments.len()); + let init_key = format!("init/{}", arguments.len()); + let box_name_key = format!("{}/{}", parent, arguments.len()); - let parent_constructor = parent_box_decl.constructors.get(constructor_name) + let parent_constructor = parent_box_decl.constructors.get(&pack_key) + .or_else(|| parent_box_decl.constructors.get(&init_key)) + .or_else(|| parent_box_decl.constructors.get(&box_name_key)) .ok_or(RuntimeError::InvalidOperation { - message: format!("Constructor '{}' not found in parent class '{}'", constructor_name, parent), + message: format!("No constructor found for parent class '{}' with {} arguments", parent, arguments.len()), })? .clone(); @@ -844,8 +845,8 @@ impl NyashInterpreter { // パラメータ数チェック if arg_values.len() != params.len() { return Err(RuntimeError::InvalidOperation { - message: format!("Parent constructor {}.{} expects {} arguments, got {}", - parent, constructor_name, params.len(), arg_values.len()), + message: format!("Parent constructor {} expects {} arguments, got {}", + parent, params.len(), arg_values.len()), }); } @@ -881,7 +882,7 @@ impl NyashInterpreter { Ok(current_instance) } else { Err(RuntimeError::InvalidOperation { - message: format!("Parent constructor '{}' is not a valid function declaration", constructor_name), + message: format!("Parent constructor is not a valid function declaration"), }) } } diff --git a/src/interpreter/objects.rs b/src/interpreter/objects.rs index e32eaf3b..e1fd3065 100644 --- a/src/interpreter/objects.rs +++ b/src/interpreter/objects.rs @@ -592,8 +592,14 @@ impl NyashInterpreter { // 🌍 革命的実装:Environment tracking廃止 // コンストラクタを呼び出す - let constructor_key = format!("{}/{}", actual_class_name, arguments.len()); - if let Some(constructor) = final_box_decl.constructors.get(&constructor_key) { + // "pack/引数数"、"init/引数数"、"Box名/引数数" の順で試す + let pack_key = format!("pack/{}", arguments.len()); + let init_key = format!("init/{}", arguments.len()); + let box_name_key = format!("{}/{}", actual_class_name, arguments.len()); + + if let Some(constructor) = final_box_decl.constructors.get(&pack_key) + .or_else(|| final_box_decl.constructors.get(&init_key)) + .or_else(|| final_box_decl.constructors.get(&box_name_key)) { // コンストラクタを実行 self.execute_constructor(&instance_box, constructor, arguments, &final_box_decl)?; } else if !arguments.is_empty() { @@ -797,8 +803,12 @@ impl NyashInterpreter { }; // 親コンストラクタを探す - let constructor_key = format!("{}/{}", parent_class, arguments.len()); - if let Some(parent_constructor) = parent_decl.constructors.get(&constructor_key) { + // まず "init/引数数" を試し、なければ "Box名/引数数" を試す + let init_key = format!("init/{}", arguments.len()); + let box_name_key = format!("{}/{}", parent_class, arguments.len()); + + if let Some(parent_constructor) = parent_decl.constructors.get(&init_key) + .or_else(|| parent_decl.constructors.get(&box_name_key)) { // 現在のthis参照を取得 // 🌍 革命的this取得:local変数から let this_instance = self.resolve_variable("me") diff --git a/src/parser/expressions.rs b/src/parser/expressions.rs index ae1de2ee..08694030 100644 --- a/src/parser/expressions.rs +++ b/src/parser/expressions.rs @@ -480,18 +480,29 @@ impl NyashParser { // DOTを確認 self.consume(TokenType::DOT)?; - // method名を取得 - let method = if let TokenType::IDENTIFIER(name) = &self.current_token().token_type { - let name = name.clone(); - self.advance(); - name - } else { - let line = self.current_token().line; - return Err(ParseError::UnexpectedToken { - found: self.current_token().token_type.clone(), - expected: "method name".to_string(), - line, - }); + // method名を取得 (IDENTIFIERまたはINITを受け入れ) + let method = match &self.current_token().token_type { + TokenType::IDENTIFIER(name) => { + let name = name.clone(); + self.advance(); + name + } + TokenType::INIT => { + self.advance(); + "init".to_string() + } + TokenType::PACK => { + self.advance(); + "pack".to_string() + } + _ => { + let line = self.current_token().line; + return Err(ParseError::UnexpectedToken { + found: self.current_token().token_type.clone(), + expected: "method name".to_string(), + line, + }); + } }; // 引数リストをパース diff --git a/src/parser/mod.rs b/src/parser/mod.rs index fc951d33..893cf738 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -205,8 +205,8 @@ impl NyashParser { Vec::new() }; - // from句のパース(継承) - let extends = if self.match_token(&TokenType::COLON) { + // from句のパース(デリゲーション) + let extends = if self.match_token(&TokenType::FROM) { self.advance(); // consume 'from' if let TokenType::IDENTIFIER(parent_name) = &self.current_token().token_type { @@ -272,8 +272,8 @@ impl NyashParser { break; } - // initブロックの処理 - if self.match_token(&TokenType::INIT) { + // initブロックの処理(initメソッドではない場合のみ) + if self.match_token(&TokenType::INIT) && self.peek_token() != &TokenType::LPAREN { self.advance(); // consume 'init' self.consume(TokenType::LBRACE)?; @@ -307,14 +307,147 @@ impl NyashParser { continue; } - if let TokenType::IDENTIFIER(field_or_method) = &self.current_token().token_type { + // overrideキーワードをチェック + let mut is_override = false; + if self.match_token(&TokenType::OVERRIDE) { + is_override = true; + self.advance(); + } + + // initトークンをメソッド名として特別処理 + if self.match_token(&TokenType::INIT) && self.peek_token() == &TokenType::LPAREN { + let field_or_method = "init".to_string(); + self.advance(); // consume 'init' + + // コンストラクタとして処理 + if self.match_token(&TokenType::LPAREN) { + // initは常にコンストラクタ + if is_override { + return Err(ParseError::UnexpectedToken { + expected: "method definition, not constructor after override keyword".to_string(), + found: TokenType::INIT, + line: self.current_token().line, + }); + } + // コンストラクタの処理 + self.advance(); // consume '(' + + let mut params = Vec::new(); + while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() { + must_advance!(self, _unused, "constructor parameter parsing"); + + if let TokenType::IDENTIFIER(param) = &self.current_token().token_type { + params.push(param.clone()); + self.advance(); + } + + if self.match_token(&TokenType::COMMA) { + self.advance(); + } + } + + self.consume(TokenType::RPAREN)?; + self.consume(TokenType::LBRACE)?; + + let mut body = Vec::new(); + while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { + self.skip_newlines(); + if !self.match_token(&TokenType::RBRACE) { + body.push(self.parse_statement()?); + } + } + + self.consume(TokenType::RBRACE)?; + + let constructor = ASTNode::FunctionDeclaration { + name: field_or_method.clone(), + params: params.clone(), + body, + is_static: false, + is_override: false, + span: Span::unknown(), + }; + + // パラメータの数でコンストラクタを区別 + let constructor_key = format!("{}/{}", field_or_method, params.len()); + constructors.insert(constructor_key, constructor); + } + } + + // packトークンをメソッド名として特別処理 + else if self.match_token(&TokenType::PACK) && self.peek_token() == &TokenType::LPAREN { + let field_or_method = "pack".to_string(); + self.advance(); // consume 'pack' + + // コンストラクタとして処理 + if self.match_token(&TokenType::LPAREN) { + // packは常にコンストラクタ + if is_override { + return Err(ParseError::UnexpectedToken { + expected: "method definition, not constructor after override keyword".to_string(), + found: TokenType::PACK, + line: self.current_token().line, + }); + } + // コンストラクタの処理 + self.advance(); // consume '(' + + let mut params = Vec::new(); + while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() { + must_advance!(self, _unused, "constructor parameter parsing"); + + if let TokenType::IDENTIFIER(param) = &self.current_token().token_type { + params.push(param.clone()); + self.advance(); + } + + if self.match_token(&TokenType::COMMA) { + self.advance(); + } + } + + self.consume(TokenType::RPAREN)?; + self.consume(TokenType::LBRACE)?; + + let mut body = Vec::new(); + while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { + self.skip_newlines(); + if !self.match_token(&TokenType::RBRACE) { + body.push(self.parse_statement()?); + } + } + + self.consume(TokenType::RBRACE)?; + + let constructor = ASTNode::FunctionDeclaration { + name: field_or_method.clone(), + params: params.clone(), + body, + is_static: false, + is_override: false, + span: Span::unknown(), + }; + + // パラメータの数でコンストラクタを区別 + let constructor_key = format!("{}/{}", field_or_method, params.len()); + constructors.insert(constructor_key, constructor); + } + } else if let TokenType::IDENTIFIER(field_or_method) = &self.current_token().token_type { let field_or_method = field_or_method.clone(); self.advance(); // メソッド定義またはコンストラクタか? if self.match_token(&TokenType::LPAREN) { - // Box名と同じ場合はコンストラクタ - if field_or_method == name { + // Box名と同じまたは"init"または"pack"の場合はコンストラクタ + if field_or_method == name || field_or_method == "init" || field_or_method == "pack" { + // コンストラクタはoverrideできない + if is_override { + return Err(ParseError::UnexpectedToken { + expected: "method definition, not constructor after override keyword".to_string(), + found: TokenType::IDENTIFIER(field_or_method.clone()), + line: self.current_token().line, + }); + } // コンストラクタの処理 self.advance(); // consume '(' @@ -402,7 +535,7 @@ impl NyashParser { params, body, is_static: false, // メソッドは通常静的でない - is_override: false, // デフォルトは非オーバーライド + is_override, // overrideキーワードの有無を反映 span: Span::unknown(), }; @@ -410,6 +543,13 @@ impl NyashParser { } } else { // フィールド定義 + if is_override { + return Err(ParseError::UnexpectedToken { + expected: "method definition after override keyword".to_string(), + found: self.current_token().token_type.clone(), + line: self.current_token().line, + }); + } fields.push(field_or_method); } self.skip_newlines(); // フィールド/メソッド定義後の改行をスキップ @@ -781,8 +921,8 @@ impl NyashParser { Vec::new() }; - // from句のパース(継承)- static boxでも継承可能 - let extends = if self.match_token(&TokenType::COLON) { + // from句のパース(デリゲーション)- static boxでもデリゲーション可能 + let extends = if self.match_token(&TokenType::FROM) { self.advance(); // consume 'from' if let TokenType::IDENTIFIER(parent_name) = &self.current_token().token_type { @@ -1030,6 +1170,15 @@ impl NyashParser { }) } + /// 次のトークンを先読み(位置を進めない) + fn peek_token(&self) -> &TokenType { + if self.current + 1 < self.tokens.len() { + &self.tokens[self.current + 1].token_type + } else { + &TokenType::EOF + } + } + /// 位置を1つ進める fn advance(&mut self) { if !self.is_at_end() { diff --git a/src/tokenizer.rs b/src/tokenizer.rs index cabac952..59fac6ba 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -32,6 +32,7 @@ pub enum TokenType { THIS, ME, INIT, // init (初期化ブロック) + PACK, // pack (コンストラクタ) NOWAIT, // nowait AWAIT, // await INTERFACE, // interface @@ -390,6 +391,7 @@ impl NyashTokenizer { "this" => TokenType::THIS, "me" => TokenType::ME, "init" => TokenType::INIT, + "pack" => TokenType::PACK, "nowait" => TokenType::NOWAIT, "await" => TokenType::AWAIT, "interface" => TokenType::INTERFACE, diff --git a/test_pack_syntax.nyash b/test_pack_syntax.nyash new file mode 100644 index 00000000..014196d3 --- /dev/null +++ b/test_pack_syntax.nyash @@ -0,0 +1,108 @@ +// 🎁 pack構文の包括的テスト + +// 1. 基本的なpack構文 +box Animal { + init { name, age } + + pack(animalName, animalAge) { + me.name = animalName + me.age = animalAge + } + + speak() { + return me.name + " (age " + me.age + ") makes a sound" + } +} + +// 2. fromデリゲーションでのpack +box Dog from Animal { + init { breed } + + pack(dogName, dogAge, dogBreed) { + from Animal.pack(dogName, dogAge) // 親のpackを呼び出し + me.breed = dogBreed + } + + override speak() { + return me.name + " (age " + me.age + ", " + me.breed + ") barks: Woof!" + } +} + +// 3. 混在形式(packと従来形式の共存) +box Cat from Animal { + init { color } + + Cat(catName, catAge, catColor) { // 従来形式 + from Animal.pack(catName, catAge) // 親はpack使用 + me.color = catColor + } + + override speak() { + return me.name + " (age " + me.age + ", " + me.color + ") meows: Meow!" + } +} + +// 4. pack単独使用テスト +box Bird { + init { species, canFly } + + // pack単独 - Box哲学に最適! + pack(birdSpecies, flying) { + me.species = birdSpecies + " (pack使用)" + me.canFly = flying + } + + describe() { + local flyText + if me.canFly { + flyText = "can fly" + } else { + flyText = "cannot fly" + } + return me.species + " - " + flyText + } +} + +// テスト実行 +print("=== 🎁 pack構文テスト ===") + +// 1. 基本的なpack +local animal +animal = new Animal("Generic", 5) +print("Animal: " + animal.speak()) + +// 2. デリゲーションでのpack +local dog +dog = new Dog("Buddy", 3, "Golden Retriever") +print("Dog: " + dog.speak()) + +// 3. 混在形式 +local cat +cat = new Cat("Whiskers", 2, "Black") +print("Cat: " + cat.speak()) + +// 4. pack単独使用確認 +local bird +bird = new Bird("Eagle", true) +print("Bird: " + bird.describe()) + +// 5. 引数なしpack +box SimpleBox { + init { message } + + pack() { // 引数なしpack + me.message = "Packed with love! 🎁" + } + + getMessage() { + return me.message + } +} + +local simple +simple = new SimpleBox() +print("SimpleBox: " + simple.getMessage()) + +print("") +print("✅ pack構文テスト完了!") +print("🎉 Everything is Box - Now Everything is Packed!") \ No newline at end of file