/*! * Box Definition Parser Module * * Box宣言(box, interface box, static box)の解析を担当 * Nyashの中核概念「Everything is Box」を実現する重要モジュール */ use crate::ast::{ASTNode, Span}; use crate::parser::declarations::box_def::header as box_header; use crate::parser::common::ParserUtils; use crate::parser::{NyashParser, ParseError}; use crate::tokenizer::TokenType; use std::collections::HashMap; impl NyashParser { /// Extracted: parse block-first unified member: `{ body } as [once|birth_once]? name : Type [postfix]` /// Returns true if a member was parsed and emitted into `methods`. fn parse_unified_member_block_first( &mut self, methods: &mut HashMap, birth_once_props: &mut Vec, ) -> Result { if !(crate::config::env::unified_members() && self.match_token(&TokenType::LBRACE)) { return Ok(false); } // Parse block body first let mut final_body = self.parse_block_statements()?; self.skip_newlines(); // Expect 'as' if let TokenType::IDENTIFIER(kw) = &self.current_token().token_type { if kw != "as" { let line = self.current_token().line; return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "'as' after block for block-first member".to_string(), line, }); } } else { let line = self.current_token().line; return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "'as' after block for block-first member".to_string(), line, }); } self.advance(); // consume 'as' // Optional kind keyword let mut kind = "computed".to_string(); if let TokenType::IDENTIFIER(k) = &self.current_token().token_type { if k == "once" || k == "birth_once" { kind = k.clone(); self.advance(); } } // Name : Type let name = if let TokenType::IDENTIFIER(n) = &self.current_token().token_type { let s = n.clone(); self.advance(); s } else { let line = self.current_token().line; return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "identifier for member name".to_string(), line, }); }; if self.match_token(&TokenType::COLON) { self.advance(); if let TokenType::IDENTIFIER(_ty) = &self.current_token().token_type { self.advance(); } else { let line = self.current_token().line; return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "type name after ':'".to_string(), line, }); } } else { let line = self.current_token().line; return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: ": type".to_string(), line, }); } // Optional postfix handlers (Stage‑3) if crate::config::env::parser_stage3() && (self.match_token(&TokenType::CATCH) || self.match_token(&TokenType::CLEANUP)) { let mut catch_clauses: Vec = Vec::new(); if self.match_token(&TokenType::CATCH) { self.advance(); self.consume(TokenType::LPAREN)?; let (exc_ty, exc_var) = self.parse_catch_param()?; self.consume(TokenType::RPAREN)?; let catch_body = self.parse_block_statements()?; catch_clauses.push(crate::ast::CatchClause { exception_type: exc_ty, variable_name: exc_var, body: catch_body, span: crate::ast::Span::unknown(), }); self.skip_newlines(); 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 member block".to_string(), line, }); } } let finally_body = if self.match_token(&TokenType::CLEANUP) { self.advance(); Some(self.parse_block_statements()?) } else { None }; final_body = vec![ASTNode::TryCatch { try_body: final_body, catch_clauses, finally_body, span: Span::unknown(), }]; } // Generate method(s) according to kind (delegate to existing logic path by reusing current block) if kind == "once" || kind == "birth_once" { // Re-inject tokens to reuse existing lowering logic would be complex; we instead synthesize // small wrappers by mimicking existing naming. For safety, fall back to simple getter here. if kind == "birth_once" { birth_once_props.push(name.clone()); } let getter_name = if kind == "once" { format!("__get_once_{}", name) } else if kind == "birth_once" { format!("__get_birth_{}", name) } else { format!("__get_{}", name) }; let getter = ASTNode::FunctionDeclaration { name: getter_name.clone(), params: vec![], body: final_body, is_static: false, is_override: false, span: Span::unknown(), }; methods.insert(getter_name, getter); } else { let getter_name = format!("__get_{}", name); let getter = ASTNode::FunctionDeclaration { name: getter_name.clone(), params: vec![], body: final_body, is_static: false, is_override: false, span: Span::unknown(), }; methods.insert(getter_name, getter); } self.skip_newlines(); Ok(true) } /// Extracted: method-level postfix catch/cleanup after a method fn parse_method_postfix_after_last_method( &mut self, methods: &mut HashMap, last_method_name: &Option, ) -> Result { if !(self.match_token(&TokenType::CATCH) || self.match_token(&TokenType::CLEANUP)) || last_method_name.is_none() { return Ok(false); } let mname = last_method_name.clone().unwrap(); let mut catch_clauses: Vec = Vec::new(); if self.match_token(&TokenType::CATCH) { self.advance(); self.consume(TokenType::LPAREN)?; let (exc_ty, exc_var) = self.parse_catch_param()?; self.consume(TokenType::RPAREN)?; let catch_body = self.parse_block_statements()?; catch_clauses.push(crate::ast::CatchClause { exception_type: exc_ty, variable_name: exc_var, body: catch_body, span: crate::ast::Span::unknown(), }); self.skip_newlines(); 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 method body".to_string(), line, }); } } let finally_body = if self.match_token(&TokenType::CLEANUP) { self.advance(); Some(self.parse_block_statements()?) } else { None }; if let Some(mnode) = methods.get_mut(&mname) { if let crate::ast::ASTNode::FunctionDeclaration { body, .. } = mnode { let already = body .iter() .any(|n| matches!(n, crate::ast::ASTNode::TryCatch { .. })); if already { let line = self.current_token().line; return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "duplicate postfix catch/cleanup after method".to_string(), line, }); } let old = std::mem::take(body); *body = vec![crate::ast::ASTNode::TryCatch { try_body: old, catch_clauses, finally_body, span: crate::ast::Span::unknown(), }]; } } Ok(true) } /// Extracted: init { ... } block (non-call form) fn parse_init_block_if_any( &mut self, init_fields: &mut Vec, weak_fields: &mut Vec, ) -> Result { if !(self.match_token(&TokenType::INIT) && self.peek_token() != &TokenType::LPAREN) { return Ok(false); } self.advance(); // consume 'init' self.consume(TokenType::LBRACE)?; while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { self.skip_newlines(); if self.match_token(&TokenType::RBRACE) { break; } let is_weak = if self.match_token(&TokenType::WEAK) { self.advance(); true } else { false }; if let TokenType::IDENTIFIER(field_name) = &self.current_token().token_type { init_fields.push(field_name.clone()); if is_weak { weak_fields.push(field_name.clone()); } self.advance(); if self.match_token(&TokenType::COMMA) { self.advance(); } } else { return Err(ParseError::UnexpectedToken { expected: if is_weak { "field name after 'weak'" } else { "field name" } .to_string(), found: self.current_token().token_type.clone(), line: self.current_token().line, }); } } self.consume(TokenType::RBRACE)?; Ok(true) } /// Extracted: visibility block or single property header-first fn parse_visibility_block_or_single( &mut self, visibility: &str, methods: &mut HashMap, fields: &mut Vec, public_fields: &mut Vec, private_fields: &mut Vec, last_method_name: &mut Option, ) -> Result { if visibility != "public" && visibility != "private" { return Ok(false); } if self.match_token(&TokenType::LBRACE) { self.advance(); self.skip_newlines(); while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { if let TokenType::IDENTIFIER(fname) = &self.current_token().token_type { let fname = fname.clone(); if visibility == "public" { public_fields.push(fname.clone()); } else { private_fields.push(fname.clone()); } fields.push(fname); self.advance(); if self.match_token(&TokenType::COMMA) { self.advance(); } self.skip_newlines(); continue; } return Err(ParseError::UnexpectedToken { expected: "identifier in visibility block".to_string(), found: self.current_token().token_type.clone(), line: self.current_token().line, }); } self.consume(TokenType::RBRACE)?; self.skip_newlines(); return Ok(true); } if let TokenType::IDENTIFIER(n) = &self.current_token().token_type { let fname = n.clone(); self.advance(); if crate::parser::declarations::box_def::members::fields::try_parse_header_first_field_or_property( self, fname.clone(), methods, fields, )? { if visibility == "public" { public_fields.push(fname.clone()); } else { private_fields.push(fname.clone()); } *last_method_name = None; self.skip_newlines(); return Ok(true); } else { // Fallback: record visibility + field name for compatibility if visibility == "public" { public_fields.push(fname.clone()); } else { private_fields.push(fname.clone()); } fields.push(fname); self.skip_newlines(); return Ok(true); } } Ok(false) } /// box宣言をパース: box Name { fields... methods... } pub fn parse_box_declaration(&mut self) -> Result { self.consume(TokenType::BOX)?; let (name, type_parameters, extends, implements) = box_header::parse_header(self)?; self.consume(TokenType::LBRACE)?; self.skip_newlines(); // ブレース後の改行をスキップ let mut fields = Vec::new(); let mut methods = HashMap::new(); let mut public_fields: Vec = Vec::new(); let mut private_fields: Vec = Vec::new(); let mut constructors = HashMap::new(); let mut init_fields = Vec::new(); let mut weak_fields = Vec::new(); // 🔗 Track weak fields // Track birth_once properties to inject eager init into birth() let mut birth_once_props: Vec = Vec::new(); let mut last_method_name: Option = None; while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { self.skip_newlines(); // ループ開始時に改行をスキップ // 分類(段階移行用の観測): 将来の分岐移譲のための前処理 if crate::config::env::parser_stage3() { if let Ok(kind) = crate::parser::declarations::box_def::members::common::classify_member(self) { let _ = kind; // 現段階では観測のみ(無副作用) } } // nyashモード(block-first): { body } as (once|birth_once)? name : Type if crate::config::env::unified_members() && self.match_token(&TokenType::LBRACE) { // Parse block body first let mut final_body = self.parse_block_statements()?; self.skip_newlines(); // Expect 'as' if let TokenType::IDENTIFIER(kw) = &self.current_token().token_type { if kw != "as" { // Not a block-first member; treat as standalone block statement in box (unsupported) let line = self.current_token().line; return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "'as' after block for block-first member".to_string(), line }); } } else { let line = self.current_token().line; return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "'as' after block for block-first member".to_string(), line }); } self.advance(); // consume 'as' // Optional kind keyword let mut kind = "computed".to_string(); if let TokenType::IDENTIFIER(k) = &self.current_token().token_type { if k == "once" || k == "birth_once" { kind = k.clone(); self.advance(); } } // Name : Type let name = if let TokenType::IDENTIFIER(n) = &self.current_token().token_type { let s = n.clone(); self.advance(); s } else { let line = self.current_token().line; return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "identifier for member name".to_string(), line }); }; if self.match_token(&TokenType::COLON) { self.advance(); if let TokenType::IDENTIFIER(_ty) = &self.current_token().token_type { self.advance(); } else { let line = self.current_token().line; return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "type name after ':'".to_string(), line }); } } else { let line = self.current_token().line; return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: ": type".to_string(), line }); } // Optional postfix handlers (Stage-3) directly after block if crate::config::env::parser_stage3() && (self.match_token(&TokenType::CATCH) || self.match_token(&TokenType::CLEANUP)) { let mut catch_clauses: Vec = Vec::new(); if self.match_token(&TokenType::CATCH) { self.advance(); self.consume(TokenType::LPAREN)?; let (exc_ty, exc_var) = self.parse_catch_param()?; self.consume(TokenType::RPAREN)?; let catch_body = self.parse_block_statements()?; catch_clauses.push(crate::ast::CatchClause { exception_type: exc_ty, variable_name: exc_var, body: catch_body, span: crate::ast::Span::unknown() }); self.skip_newlines(); 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 member block".to_string(), line }); } } let finally_body = if self.match_token(&TokenType::CLEANUP) { self.advance(); Some(self.parse_block_statements()?) } else { None }; final_body = vec![ASTNode::TryCatch { try_body: final_body, catch_clauses, finally_body, span: crate::ast::Span::unknown() }]; } // Generate methods per kind if kind == "once" { let compute_name = format!("__compute_once_{}", name); let compute = ASTNode::FunctionDeclaration { name: compute_name.clone(), params: vec![], body: final_body, is_static: false, is_override: false, span: Span::unknown() }; methods.insert(compute_name.clone(), compute); let key = format!("__once_{}", name); let poison_key = format!("__once_poison_{}", name); let cached_local = format!("__ny_cached_{}", name); let poison_local = format!("__ny_poison_{}", name); let val_local = format!("__ny_val_{}", name); let me_node = ASTNode::Me { span: Span::unknown() }; let get_cached = ASTNode::MethodCall { object: Box::new(me_node.clone()), method: "getField".to_string(), arguments: vec![ASTNode::Literal { value: crate::ast::LiteralValue::String(key.clone()), span: Span::unknown() }], span: Span::unknown() }; let local_cached = ASTNode::Local { variables: vec![cached_local.clone()], initial_values: vec![Some(Box::new(get_cached))], span: Span::unknown() }; let cond_cached = ASTNode::BinaryOp { operator: crate::ast::BinaryOperator::NotEqual, left: Box::new(ASTNode::Variable { name: cached_local.clone(), span: Span::unknown() }), right: Box::new(ASTNode::Literal { value: crate::ast::LiteralValue::Null, span: Span::unknown() }), span: Span::unknown() }; let then_ret_cached = vec![ASTNode::Return { value: Some(Box::new(ASTNode::Variable { name: cached_local.clone(), span: Span::unknown() })), span: Span::unknown() }]; let if_cached = ASTNode::If { condition: Box::new(cond_cached), then_body: then_ret_cached, else_body: None, span: Span::unknown() }; let get_poison = ASTNode::MethodCall { object: Box::new(me_node.clone()), method: "getField".to_string(), arguments: vec![ASTNode::Literal { value: crate::ast::LiteralValue::String(poison_key.clone()), span: Span::unknown() }], span: Span::unknown() }; let local_poison = ASTNode::Local { variables: vec![poison_local.clone()], initial_values: vec![Some(Box::new(get_poison))], span: Span::unknown() }; let cond_poison = ASTNode::BinaryOp { operator: crate::ast::BinaryOperator::NotEqual, left: Box::new(ASTNode::Variable { name: poison_local.clone(), span: Span::unknown() }), right: Box::new(ASTNode::Literal { value: crate::ast::LiteralValue::Null, span: Span::unknown() }), span: Span::unknown() }; let then_throw = vec![ASTNode::Throw { expression: Box::new(ASTNode::Literal { value: crate::ast::LiteralValue::String(format!("once '{}' previously failed", name)), span: Span::unknown() }), span: Span::unknown() }]; let if_poison = ASTNode::If { condition: Box::new(cond_poison), then_body: then_throw, else_body: None, span: Span::unknown() }; let call_compute = ASTNode::MethodCall { object: Box::new(me_node.clone()), method: compute_name.clone(), arguments: vec![], span: Span::unknown() }; let local_val = ASTNode::Local { variables: vec![val_local.clone()], initial_values: vec![Some(Box::new(call_compute))], span: Span::unknown() }; let set_call = ASTNode::MethodCall { object: Box::new(me_node.clone()), method: "setField".to_string(), arguments: vec![ASTNode::Literal { value: crate::ast::LiteralValue::String(key.clone()), span: Span::unknown() }, ASTNode::Variable { name: val_local.clone(), span: Span::unknown() }], span: Span::unknown() }; let ret_stmt = ASTNode::Return { value: Some(Box::new(ASTNode::Variable { name: val_local.clone(), span: Span::unknown() })), span: Span::unknown() }; let try_body = vec![local_val, set_call, ret_stmt]; let catch_body = vec![ ASTNode::MethodCall { object: Box::new(me_node.clone()), method: "setField".to_string(), arguments: vec![ASTNode::Literal { value: crate::ast::LiteralValue::String(poison_key.clone()), span: Span::unknown() }, ASTNode::Literal { value: crate::ast::LiteralValue::Bool(true), span: Span::unknown() }], span: Span::unknown() }, ASTNode::Throw { expression: Box::new(ASTNode::Literal { value: crate::ast::LiteralValue::String(format!("once '{}' init failed", name)), span: Span::unknown() }), span: Span::unknown() } ]; let trycatch = ASTNode::TryCatch { try_body, catch_clauses: vec![crate::ast::CatchClause { exception_type: None, variable_name: None, body: catch_body, span: Span::unknown() }], finally_body: None, span: Span::unknown() }; let getter_body = vec![local_cached, if_cached, local_poison, if_poison, trycatch]; let getter_name = format!("__get_once_{}", name); let getter = ASTNode::FunctionDeclaration { name: getter_name.clone(), params: vec![], body: getter_body, is_static: false, is_override: false, span: Span::unknown() }; methods.insert(getter_name, getter); } else if kind == "birth_once" { // Self-cycle guard: birth_once cannot reference itself via me. if self.ast_contains_me_field(&final_body, &name) { let line = self.current_token().line; return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: format!("birth_once '{}' must not reference itself", name), line }); } birth_once_props.push(name.clone()); let compute_name = format!("__compute_birth_{}", name); let compute = ASTNode::FunctionDeclaration { name: compute_name.clone(), params: vec![], body: final_body, is_static: false, is_override: false, span: Span::unknown() }; methods.insert(compute_name.clone(), compute); let key = format!("__birth_{}", name); let me_node = ASTNode::Me { span: Span::unknown() }; let get_call = ASTNode::MethodCall { object: Box::new(me_node.clone()), method: "getField".to_string(), arguments: vec![ASTNode::Literal { value: crate::ast::LiteralValue::String(key.clone()), span: Span::unknown() }], span: Span::unknown() }; let getter_body = vec![ASTNode::Return { value: Some(Box::new(get_call)), span: Span::unknown() }]; let getter_name = format!("__get_birth_{}", name); let getter = ASTNode::FunctionDeclaration { name: getter_name.clone(), params: vec![], body: getter_body, is_static: false, is_override: false, span: Span::unknown() }; methods.insert(getter_name, getter); } else { // computed let getter_name = format!("__get_{}", name); let getter = ASTNode::FunctionDeclaration { name: getter_name.clone(), params: vec![], body: final_body, is_static: false, is_override: false, span: Span::unknown() }; methods.insert(getter_name, getter); } self.skip_newlines(); continue; } // Fallback: method-level postfix catch/cleanup after a method (non-static box) if self.parse_method_postfix_after_last_method(&mut methods, &last_method_name)? { continue; } // RBRACEに到達していればループを抜ける if self.match_token(&TokenType::RBRACE) { break; } // initブロックの処理(initメソッドではない場合のみ) if self.parse_init_block_if_any(&mut init_fields, &mut weak_fields)? { continue; } // overrideキーワードをチェック let mut is_override = false; if self.match_token(&TokenType::OVERRIDE) { is_override = true; self.advance(); } // constructor parsing moved to members::constructors if let Some((ctor_key, ctor_node)) = crate::parser::declarations::box_def::members::constructors::try_parse_constructor(self, is_override)? { constructors.insert(ctor_key, ctor_node); continue; } // 🚨 birth()統一システム: Box名コンストラクタ無効化 // Box名と同じ名前のコンストラクタは禁止(birth()のみ許可) if let TokenType::IDENTIFIER(id) = &self.current_token().token_type { if id == &name && self.peek_token() == &TokenType::LPAREN { return Err(ParseError::UnexpectedToken { expected: format!("birth() constructor instead of {}(). Nyash uses birth() for unified constructor syntax.", name), found: TokenType::IDENTIFIER(name.clone()), line: self.current_token().line, }); } } // 通常のフィールド名またはメソッド名、または unified members の先頭キーワードを読み取り if let TokenType::IDENTIFIER(field_or_method) = &self.current_token().token_type { let field_or_method = field_or_method.clone(); self.advance(); // 可視性: public/private ブロック/単行 if self.parse_visibility_block_or_single( &field_or_method, &mut methods, &mut fields, &mut public_fields, &mut private_fields, &mut last_method_name, )? { continue; } // Unified Members (header-first) gate: support once/birth_once via members::properties if crate::config::env::unified_members() && (field_or_method == "once" || field_or_method == "birth_once") { if crate::parser::declarations::box_def::members::properties::try_parse_unified_property( self, &field_or_method, &mut methods, &mut birth_once_props, )? { last_method_name = None; // do not attach method-level postfix here self.skip_newlines(); continue; } } // メソッド or フィールド(委譲) if let Some(method) = crate::parser::declarations::box_def::members::methods::try_parse_method( self, field_or_method.clone(), is_override, &birth_once_props, )? { last_method_name = Some(field_or_method.clone()); methods.insert(field_or_method, method); } else { // フィールド or 統一メンバ(computed/once/birth_once header-first) let fname = field_or_method; if crate::parser::declarations::box_def::members::fields::try_parse_header_first_field_or_property( self, fname, &mut methods, &mut fields, )? { continue; } } } else { return Err(ParseError::UnexpectedToken { expected: "method or field name".to_string(), found: self.current_token().token_type.clone(), line: self.current_token().line, }); } } self.consume(TokenType::RBRACE)?; // 🔥 Override validation for parent in &extends { self.validate_override_methods(&name, parent, &methods)?; } // birth_once 相互依存の簡易検出(宣言間の循環) if crate::config::env::unified_members() { // Collect birth_once compute bodies use std::collections::{HashMap, HashSet}; let mut birth_bodies: HashMap> = HashMap::new(); for (mname, mast) in &methods { if let Some(prop) = mname.strip_prefix("__compute_birth_") { if let ASTNode::FunctionDeclaration { body, .. } = mast { birth_bodies.insert(prop.to_string(), body.clone()); } } } // Build dependency graph: A -> {B | me.B used inside A} let mut deps: HashMap> = HashMap::new(); let props: HashSet = birth_bodies.keys().cloned().collect(); for (p, body) in &birth_bodies { let used = self.ast_collect_me_fields(body); let mut set = HashSet::new(); for u in used { if props.contains(&u) && u != *p { set.insert(u); } } deps.insert(p.clone(), set); } // Detect cycle via DFS fn has_cycle( node: &str, deps: &HashMap>, temp: &mut HashSet, perm: &mut HashSet, ) -> bool { if perm.contains(node) { return false; } if !temp.insert(node.to_string()) { return true; // back-edge } if let Some(ns) = deps.get(node) { for n in ns { if has_cycle(n, deps, temp, perm) { return true; } } } temp.remove(node); perm.insert(node.to_string()); false } let mut perm = HashSet::new(); let mut temp = HashSet::new(); for p in deps.keys() { if has_cycle(p, &deps, &mut temp, &mut perm) { let line = self.current_token().line; return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "birth_once declarations must not have cyclic dependencies".to_string(), line, }); } } } Ok(ASTNode::BoxDeclaration { name, fields, public_fields, private_fields, methods, constructors, init_fields, weak_fields, // 🔗 Add weak fields to AST is_interface: false, extends, implements, type_parameters, is_static: false, // 通常のboxはnon-static static_init: None, // 通常のboxはstatic初期化ブロックなし span: Span::unknown(), }) } /// interface box宣言をパース: interface box Name { methods... } pub fn parse_interface_box_declaration(&mut self) -> Result { self.consume(TokenType::INTERFACE)?; self.consume(TokenType::BOX)?; let name = 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: "identifier".to_string(), line, }); }; self.consume(TokenType::LBRACE)?; self.skip_newlines(); // ブレース後の改行をスキップ let mut methods = HashMap::new(); while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { self.skip_newlines(); // ループ開始時に改行をスキップ if let TokenType::IDENTIFIER(method_name) = &self.current_token().token_type { let method_name = method_name.clone(); self.advance(); // インターフェースメソッドはシグネチャのみ if self.match_token(&TokenType::LPAREN) { self.advance(); // consume '(' let mut params = Vec::new(); while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() { 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)?; // インターフェースメソッドは実装なし(空のbody) let method_decl = ASTNode::FunctionDeclaration { name: method_name.clone(), params, body: vec![], // 空の実装 is_static: false, // インターフェースメソッドは通常静的でない is_override: false, // デフォルトは非オーバーライド span: Span::unknown(), }; methods.insert(method_name, method_decl); // メソッド宣言後の改行をスキップ self.skip_newlines(); } else { let line = self.current_token().line; return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "(".to_string(), line, }); } } else { let line = self.current_token().line; return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "method name".to_string(), line, }); } } self.consume(TokenType::RBRACE)?; Ok(ASTNode::BoxDeclaration { name, fields: vec![], // インターフェースはフィールドなし public_fields: vec![], private_fields: vec![], methods, constructors: HashMap::new(), // インターフェースにコンストラクタなし init_fields: vec![], // インターフェースにinitブロックなし weak_fields: vec![], // 🔗 インターフェースにweak fieldsなし is_interface: true, // インターフェースフラグ extends: vec![], // 🚀 Multi-delegation: Changed from None to vec![] implements: vec![], type_parameters: Vec::new(), // 🔥 インターフェースではジェネリクス未対応 is_static: false, // インターフェースは非static static_init: None, // インターフェースにstatic initなし span: Span::unknown(), }) } } impl NyashParser { /// Minimal scan: does body contain `me.` access (direct self-cycle guard) fn ast_contains_me_field(&self, nodes: &Vec, field: &str) -> bool { fn scan(nodes: &Vec, field: &str) -> bool { for n in nodes { match n { ASTNode::FieldAccess { object, field: f, .. } => { if f == field { if let ASTNode::Me { .. } = object.as_ref() { return true; } } } ASTNode::Program { statements, .. } => { if scan(statements, field) { return true; } } ASTNode::If { then_body, else_body, .. } => { if scan(then_body, field) { return true; } if let Some(eb) = else_body { if scan(eb, field) { return true; } } } ASTNode::TryCatch { try_body, catch_clauses, finally_body, .. } => { if scan(try_body, field) { return true; } for c in catch_clauses { if scan(&c.body, field) { return true; } } if let Some(fb) = finally_body { if scan(fb, field) { return true; } } } ASTNode::FunctionDeclaration { body, .. } => { if scan(body, field) { return true; } } _ => {} } } false } scan(nodes, field) } /// Collect all `me.` accessed in nodes (flat set) fn ast_collect_me_fields(&self, nodes: &Vec) -> std::collections::HashSet { use std::collections::HashSet; fn scan(nodes: &Vec, out: &mut HashSet) { for n in nodes { match n { ASTNode::FieldAccess { object, field, .. } => { if let ASTNode::Me { .. } = object.as_ref() { out.insert(field.clone()); } } ASTNode::Program { statements, .. } => scan(statements, out), ASTNode::If { then_body, else_body, .. } => { scan(then_body, out); if let Some(eb) = else_body { scan(eb, out); } } ASTNode::TryCatch { try_body, catch_clauses, finally_body, .. } => { scan(try_body, out); for c in catch_clauses { scan(&c.body, out); } if let Some(fb) = finally_body { scan(fb, out); } } ASTNode::FunctionDeclaration { body, .. } => scan(body, out), _ => {} } } } let mut hs = HashSet::new(); scan(nodes, &mut hs); hs } }