feat(loop-phi): Add body-local variable PHI generation for Rust AST loops
Phase 25.1c/k: Fix ValueId undefined errors in loops with body-local variables **Problem:** - FuncScannerBox.scan_all_boxes/1 and BreakFinderBox._find_loops/2 had ValueId undefined errors for variables declared inside loop bodies - LoopFormBuilder only generated PHIs for preheader variables, missing body-locals - Example: `local ch = s.substring(i, i+1)` inside loop → undefined on next iteration **Solution:** 1. **Rust AST path** (src/mir/loop_builder.rs): - Detect body-local variables by comparing body_end_vars vs current_vars - Generate empty PHI nodes at loop header for body-local variables - Seal PHIs with latch + continue snapshot inputs after seal_phis() - Added HAKO_LOOP_PHI_TRACE=1 logging for debugging 2. **JSON v0 path** (already fixed in previous session): - src/runner/json_v0_bridge/lowering/loop_.rs handles body-locals - Uses same strategy but for JSON v0 bridge lowering **Results:** - ✅ FuncScannerBox.scan_all_boxes: 41 body-local PHIs generated - ✅ Main.main (demo harness): 23 body-local PHIs generated - ⚠️ Still some ValueId undefined errors remaining (exit PHI issue) **Files changed:** - src/mir/loop_builder.rs: body-local PHI generation logic - lang/src/compiler/entry/func_scanner.hako: debug logging - /tmp/stageb_funcscan_demo.hako: test harness 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -1,29 +1,254 @@
|
||||
//! Box Definition parser (scaffold)
|
||||
#![allow(dead_code)]
|
||||
//! Box Definition Parser Module
|
||||
//!
|
||||
//! This module will progressively take over parsing of large `parse_box_declaration`
|
||||
//! by splitting header and member parsing into focused units.
|
||||
//! For now, it provides only type skeletons to stage the refactor safely.
|
||||
//! Box宣言(box, interface box, static box)の解析を担当
|
||||
//! Nyashの中核概念「Everything is Box」を実現する重要モジュール
|
||||
|
||||
use crate::ast::ASTNode;
|
||||
use crate::ast::{ASTNode, Span};
|
||||
use crate::parser::{NyashParser, ParseError};
|
||||
use crate::parser::common::ParserUtils;
|
||||
use crate::tokenizer::TokenType;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub mod header;
|
||||
pub mod members;
|
||||
pub mod validators;
|
||||
pub mod interface;
|
||||
|
||||
/// Facade to host the staged migration.
|
||||
pub(crate) struct BoxDefParserFacade;
|
||||
|
||||
impl BoxDefParserFacade {
|
||||
/// Entry planned: parse full box declaration (header + members).
|
||||
/// Not wired yet; use NyashParser::parse_box_declaration for now.
|
||||
pub(crate) fn parse_box(_p: &mut NyashParser) -> Result<ASTNode, ParseError> {
|
||||
Err(ParseError::UnexpectedToken {
|
||||
found: crate::tokenizer::TokenType::EOF,
|
||||
expected: "box declaration (facade not wired)".to_string(),
|
||||
line: 0,
|
||||
})
|
||||
}
|
||||
/// Thin wrappers to keep the main loop tidy (behavior-preserving)
|
||||
fn box_try_block_first_property(
|
||||
p: &mut NyashParser,
|
||||
methods: &mut HashMap<String, ASTNode>,
|
||||
birth_once_props: &mut Vec<String>,
|
||||
) -> Result<bool, ParseError> {
|
||||
members::properties::try_parse_block_first_property(
|
||||
p, methods, birth_once_props,
|
||||
)
|
||||
}
|
||||
|
||||
fn box_try_method_postfix_after_last(
|
||||
p: &mut NyashParser,
|
||||
methods: &mut HashMap<String, ASTNode>,
|
||||
last_method_name: &Option<String>,
|
||||
) -> Result<bool, ParseError> {
|
||||
members::postfix::try_parse_method_postfix_after_last_method(
|
||||
p, methods, last_method_name,
|
||||
)
|
||||
}
|
||||
|
||||
fn box_try_init_block(
|
||||
p: &mut NyashParser,
|
||||
init_fields: &mut Vec<String>,
|
||||
weak_fields: &mut Vec<String>,
|
||||
) -> Result<bool, ParseError> {
|
||||
members::fields::parse_init_block_if_any(
|
||||
p, init_fields, weak_fields,
|
||||
)
|
||||
}
|
||||
|
||||
fn box_try_constructor(
|
||||
p: &mut NyashParser,
|
||||
is_override: bool,
|
||||
constructors: &mut HashMap<String, ASTNode>,
|
||||
) -> Result<bool, ParseError> {
|
||||
if let Some((key, node)) = members::constructors::try_parse_constructor(p, is_override)? {
|
||||
constructors.insert(key, node);
|
||||
return Ok(true);
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn box_try_visibility(
|
||||
p: &mut NyashParser,
|
||||
visibility: &str,
|
||||
methods: &mut HashMap<String, ASTNode>,
|
||||
fields: &mut Vec<String>,
|
||||
public_fields: &mut Vec<String>,
|
||||
private_fields: &mut Vec<String>,
|
||||
last_method_name: &mut Option<String>,
|
||||
) -> Result<bool, ParseError> {
|
||||
members::fields::try_parse_visibility_block_or_single(
|
||||
p,
|
||||
visibility,
|
||||
methods,
|
||||
fields,
|
||||
public_fields,
|
||||
private_fields,
|
||||
last_method_name,
|
||||
)
|
||||
}
|
||||
|
||||
/// Parse either a method or a header-first field/property starting with `name`.
|
||||
/// Updates `methods`/`fields` and `last_method_name` as appropriate.
|
||||
fn box_try_method_or_field(
|
||||
p: &mut NyashParser,
|
||||
name: String,
|
||||
is_override: bool,
|
||||
methods: &mut HashMap<String, ASTNode>,
|
||||
fields: &mut Vec<String>,
|
||||
birth_once_props: &Vec<String>,
|
||||
last_method_name: &mut Option<String>,
|
||||
) -> Result<bool, ParseError> {
|
||||
if let Some(method) = members::methods::try_parse_method(
|
||||
p,
|
||||
name.clone(),
|
||||
is_override,
|
||||
birth_once_props,
|
||||
)? {
|
||||
*last_method_name = Some(name.clone());
|
||||
methods.insert(name, method);
|
||||
return Ok(true);
|
||||
}
|
||||
// Fallback: header-first field/property (computed/once/birth_once handled inside)
|
||||
members::fields::try_parse_header_first_field_or_property(
|
||||
p,
|
||||
name,
|
||||
methods,
|
||||
fields,
|
||||
)
|
||||
}
|
||||
|
||||
/// box宣言をパース: box Name { fields... methods... }
|
||||
pub fn parse_box_declaration(p: &mut NyashParser) -> Result<ASTNode, ParseError> {
|
||||
// Accept either 'box' or 'flow' (flow is syntactic sugar for static box)
|
||||
if !p.match_token(&TokenType::BOX) && !p.match_token(&TokenType::FLOW) {
|
||||
return Err(ParseError::UnexpectedToken {
|
||||
found: p.current_token().token_type.clone(),
|
||||
expected: "'box' or 'flow'".to_string(),
|
||||
line: p.current_token().line,
|
||||
});
|
||||
}
|
||||
p.advance(); // consume BOX or FLOW
|
||||
let (name, type_parameters, extends, implements) =
|
||||
header::parse_header(p)?;
|
||||
|
||||
p.consume(TokenType::LBRACE)?;
|
||||
|
||||
let mut fields = Vec::new();
|
||||
let mut methods = HashMap::new();
|
||||
let mut public_fields: Vec<String> = Vec::new();
|
||||
let mut private_fields: Vec<String> = 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<String> = Vec::new();
|
||||
|
||||
let mut last_method_name: Option<String> = None;
|
||||
while !p.match_token(&TokenType::RBRACE) && !p.is_at_end() {
|
||||
// 分類(段階移行用の観測): 将来の分岐移譲のための前処理
|
||||
if crate::config::env::parser_stage3() {
|
||||
if let Ok(kind) = members::common::classify_member(p) {
|
||||
let _ = kind; // 現段階では観測のみ(無副作用)
|
||||
}
|
||||
}
|
||||
|
||||
// nyashモード(block-first): { body } as (once|birth_once)? name : Type
|
||||
if box_try_block_first_property(p, &mut methods, &mut birth_once_props)? { continue; }
|
||||
|
||||
// Fallback: method-level postfix catch/cleanup after a method (non-static box)
|
||||
if box_try_method_postfix_after_last(p, &mut methods, &last_method_name)? { continue; }
|
||||
|
||||
// RBRACEに到達していればループを抜ける
|
||||
if p.match_token(&TokenType::RBRACE) {
|
||||
break;
|
||||
}
|
||||
|
||||
// initブロックの処理(initメソッドではない場合のみ)
|
||||
if box_try_init_block(p, &mut init_fields, &mut weak_fields)? { continue; }
|
||||
|
||||
// overrideキーワードをチェック
|
||||
let mut is_override = false;
|
||||
if p.match_token(&TokenType::OVERRIDE) {
|
||||
is_override = true;
|
||||
p.advance();
|
||||
}
|
||||
|
||||
// constructor parsing moved to members::constructors
|
||||
if box_try_constructor(p, is_override, &mut constructors)? { continue; }
|
||||
|
||||
// 🚨 birth()統一システム: Box名コンストラクタ無効化
|
||||
validators::forbid_box_named_constructor(p, &name)?;
|
||||
|
||||
// 通常のフィールド名またはメソッド名、または unified members の先頭キーワードを読み取り
|
||||
if let TokenType::IDENTIFIER(field_or_method) = &p.current_token().token_type {
|
||||
let field_or_method = field_or_method.clone();
|
||||
p.advance();
|
||||
|
||||
// 可視性: public/private ブロック/単行
|
||||
if box_try_visibility(
|
||||
p,
|
||||
&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 members::properties::try_parse_unified_property(
|
||||
p,
|
||||
&field_or_method,
|
||||
&mut methods,
|
||||
&mut birth_once_props,
|
||||
)? {
|
||||
last_method_name = None; // do not attach method-level postfix here
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// メソッド or フィールド(委譲)
|
||||
if box_try_method_or_field(
|
||||
p,
|
||||
field_or_method,
|
||||
is_override,
|
||||
&mut methods,
|
||||
&mut fields,
|
||||
&birth_once_props,
|
||||
&mut last_method_name,
|
||||
)? { continue; }
|
||||
} else {
|
||||
return Err(ParseError::UnexpectedToken {
|
||||
expected: "method or field name".to_string(),
|
||||
found: p.current_token().token_type.clone(),
|
||||
line: p.current_token().line,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
p.consume(TokenType::RBRACE)?;
|
||||
// 🚫 Disallow method named same as the box (constructor-like confusion)
|
||||
validators::validate_no_ctor_like_name(p, &name, &methods)?;
|
||||
|
||||
// 🔥 Override validation
|
||||
for parent in &extends {
|
||||
p.validate_override_methods(&name, parent, &methods)?;
|
||||
}
|
||||
|
||||
// birth_once 相互依存の簡易検出(宣言間の循環)
|
||||
validators::validate_birth_once_cycles(p, &methods)?;
|
||||
|
||||
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(p: &mut NyashParser) -> Result<ASTNode, ParseError> {
|
||||
interface::parse_interface_box(p)
|
||||
}
|
||||
|
||||
@ -1,257 +0,0 @@
|
||||
/*!
|
||||
* 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 {
|
||||
/// Thin wrappers to keep the main loop tidy (behavior-preserving)
|
||||
fn box_try_block_first_property(
|
||||
&mut self,
|
||||
methods: &mut HashMap<String, ASTNode>,
|
||||
birth_once_props: &mut Vec<String>,
|
||||
) -> Result<bool, ParseError> {
|
||||
crate::parser::declarations::box_def::members::properties::try_parse_block_first_property(
|
||||
self, methods, birth_once_props,
|
||||
)
|
||||
}
|
||||
|
||||
fn box_try_method_postfix_after_last(
|
||||
&mut self,
|
||||
methods: &mut HashMap<String, ASTNode>,
|
||||
last_method_name: &Option<String>,
|
||||
) -> Result<bool, ParseError> {
|
||||
crate::parser::declarations::box_def::members::postfix::try_parse_method_postfix_after_last_method(
|
||||
self, methods, last_method_name,
|
||||
)
|
||||
}
|
||||
|
||||
fn box_try_init_block(
|
||||
&mut self,
|
||||
init_fields: &mut Vec<String>,
|
||||
weak_fields: &mut Vec<String>,
|
||||
) -> Result<bool, ParseError> {
|
||||
crate::parser::declarations::box_def::members::fields::parse_init_block_if_any(
|
||||
self, init_fields, weak_fields,
|
||||
)
|
||||
}
|
||||
|
||||
fn box_try_constructor(
|
||||
&mut self,
|
||||
is_override: bool,
|
||||
constructors: &mut HashMap<String, ASTNode>,
|
||||
) -> Result<bool, ParseError> {
|
||||
if let Some((key, node)) = crate::parser::declarations::box_def::members::constructors::try_parse_constructor(self, is_override)? {
|
||||
constructors.insert(key, node);
|
||||
return Ok(true);
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn box_try_visibility(
|
||||
&mut self,
|
||||
visibility: &str,
|
||||
methods: &mut HashMap<String, ASTNode>,
|
||||
fields: &mut Vec<String>,
|
||||
public_fields: &mut Vec<String>,
|
||||
private_fields: &mut Vec<String>,
|
||||
last_method_name: &mut Option<String>,
|
||||
) -> Result<bool, ParseError> {
|
||||
crate::parser::declarations::box_def::members::fields::try_parse_visibility_block_or_single(
|
||||
self,
|
||||
visibility,
|
||||
methods,
|
||||
fields,
|
||||
public_fields,
|
||||
private_fields,
|
||||
last_method_name,
|
||||
)
|
||||
}
|
||||
|
||||
/// Parse either a method or a header-first field/property starting with `name`.
|
||||
/// Updates `methods`/`fields` and `last_method_name` as appropriate.
|
||||
fn box_try_method_or_field(
|
||||
&mut self,
|
||||
name: String,
|
||||
is_override: bool,
|
||||
methods: &mut HashMap<String, ASTNode>,
|
||||
fields: &mut Vec<String>,
|
||||
birth_once_props: &Vec<String>,
|
||||
last_method_name: &mut Option<String>,
|
||||
) -> Result<bool, ParseError> {
|
||||
if let Some(method) = crate::parser::declarations::box_def::members::methods::try_parse_method(
|
||||
self,
|
||||
name.clone(),
|
||||
is_override,
|
||||
birth_once_props,
|
||||
)? {
|
||||
*last_method_name = Some(name.clone());
|
||||
methods.insert(name, method);
|
||||
return Ok(true);
|
||||
}
|
||||
// Fallback: header-first field/property (computed/once/birth_once handled inside)
|
||||
crate::parser::declarations::box_def::members::fields::try_parse_header_first_field_or_property(
|
||||
self,
|
||||
name,
|
||||
methods,
|
||||
fields,
|
||||
)
|
||||
}
|
||||
// parse_unified_member_block_first moved to members::properties
|
||||
|
||||
// parse_method_postfix_after_last_method moved to members::postfix
|
||||
|
||||
/// box宣言をパース: box Name { fields... methods... }
|
||||
pub fn parse_box_declaration(&mut self) -> Result<ASTNode, ParseError> {
|
||||
// Accept either 'box' or 'flow' (flow is syntactic sugar for static box)
|
||||
if !self.match_token(&TokenType::BOX) && !self.match_token(&TokenType::FLOW) {
|
||||
return Err(ParseError::UnexpectedToken {
|
||||
found: self.current_token().token_type.clone(),
|
||||
expected: "'box' or 'flow'".to_string(),
|
||||
line: self.current_token().line,
|
||||
});
|
||||
}
|
||||
self.advance(); // consume BOX or FLOW
|
||||
let (name, type_parameters, extends, implements) =
|
||||
box_header::parse_header(self)?;
|
||||
|
||||
self.consume(TokenType::LBRACE)?;
|
||||
|
||||
let mut fields = Vec::new();
|
||||
let mut methods = HashMap::new();
|
||||
let mut public_fields: Vec<String> = Vec::new();
|
||||
let mut private_fields: Vec<String> = 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<String> = Vec::new();
|
||||
|
||||
let mut last_method_name: Option<String> = None;
|
||||
while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() {
|
||||
// 分類(段階移行用の観測): 将来の分岐移譲のための前処理
|
||||
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 self.box_try_block_first_property(&mut methods, &mut birth_once_props)? { continue; }
|
||||
|
||||
// Fallback: method-level postfix catch/cleanup after a method (non-static box)
|
||||
if self.box_try_method_postfix_after_last(&mut methods, &last_method_name)? { continue; }
|
||||
|
||||
// RBRACEに到達していればループを抜ける
|
||||
if self.match_token(&TokenType::RBRACE) {
|
||||
break;
|
||||
}
|
||||
|
||||
// initブロックの処理(initメソッドではない場合のみ)
|
||||
if self.box_try_init_block(&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 self.box_try_constructor(is_override, &mut constructors)? { continue; }
|
||||
|
||||
// 🚨 birth()統一システム: Box名コンストラクタ無効化
|
||||
crate::parser::declarations::box_def::validators::forbid_box_named_constructor(self, &name)?;
|
||||
|
||||
// 通常のフィールド名またはメソッド名、または 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.box_try_visibility(
|
||||
&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
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// メソッド or フィールド(委譲)
|
||||
if self.box_try_method_or_field(
|
||||
field_or_method,
|
||||
is_override,
|
||||
&mut methods,
|
||||
&mut fields,
|
||||
&birth_once_props,
|
||||
&mut last_method_name,
|
||||
)? { 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)?;
|
||||
// 🚫 Disallow method named same as the box (constructor-like confusion)
|
||||
crate::parser::declarations::box_def::validators::validate_no_ctor_like_name(self, &name, &methods)?;
|
||||
|
||||
// 🔥 Override validation
|
||||
for parent in &extends {
|
||||
self.validate_override_methods(&name, parent, &methods)?;
|
||||
}
|
||||
|
||||
// birth_once 相互依存の簡易検出(宣言間の循環)
|
||||
crate::parser::declarations::box_def::validators::validate_birth_once_cycles(self, &methods)?;
|
||||
|
||||
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<ASTNode, ParseError> {
|
||||
crate::parser::declarations::box_def::interface::parse_interface_box(self)
|
||||
}
|
||||
}
|
||||
|
||||
// ast_collect_me_fields moved into box_def::validators (private helper)
|
||||
@ -5,10 +5,8 @@
|
||||
* Box定義、関数定義、use文などの宣言を処理
|
||||
*/
|
||||
|
||||
pub mod box_definition;
|
||||
pub mod box_def;
|
||||
pub mod dependency_helpers;
|
||||
pub mod static_box;
|
||||
pub mod static_def;
|
||||
|
||||
// Re-export commonly used items
|
||||
|
||||
@ -1,180 +0,0 @@
|
||||
/*!
|
||||
* Static Box Definition Parser
|
||||
*
|
||||
* static box宣言と関連ヘルパー関数
|
||||
*/
|
||||
|
||||
use crate::ast::{ASTNode, Span};
|
||||
use crate::parser::common::ParserUtils;
|
||||
use crate::parser::{NyashParser, ParseError};
|
||||
use crate::tokenizer::TokenType;
|
||||
use std::collections::HashMap;
|
||||
|
||||
impl NyashParser {
|
||||
/// static box宣言をパース: static box Name { ... }
|
||||
pub fn parse_static_box(&mut self) -> Result<ASTNode, ParseError> {
|
||||
self.consume(TokenType::BOX)?;
|
||||
let (name, type_parameters, extends, implements) =
|
||||
crate::parser::declarations::static_def::header::parse_static_header(self)?;
|
||||
|
||||
self.consume(TokenType::LBRACE)?;
|
||||
|
||||
let mut fields = Vec::new();
|
||||
let mut methods = HashMap::new();
|
||||
let constructors = HashMap::new();
|
||||
let mut init_fields = Vec::new();
|
||||
let mut weak_fields = Vec::new(); // 🔗 Track weak fields for static box
|
||||
let mut static_init: Option<Vec<ASTNode>> = None;
|
||||
|
||||
// Track last inserted method name to allow postfix catch/cleanup fallback parsing
|
||||
let mut last_method_name: Option<String> = None;
|
||||
while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() {
|
||||
// Tolerate blank lines between members
|
||||
while self.match_token(&TokenType::NEWLINE) { self.advance(); }
|
||||
let trace = std::env::var("NYASH_PARSER_TRACE_STATIC").ok().as_deref() == Some("1");
|
||||
if trace {
|
||||
eprintln!(
|
||||
"[parser][static-box] loop token={:?}",
|
||||
self.current_token().token_type
|
||||
);
|
||||
}
|
||||
|
||||
// Fallback: method-level postfix catch/cleanup immediately following a method
|
||||
if crate::parser::declarations::box_def::members::postfix::try_parse_method_postfix_after_last_method(
|
||||
self, &mut methods, &last_method_name,
|
||||
)? { continue; }
|
||||
|
||||
// RBRACEに到達していればループを抜ける
|
||||
if self.match_token(&TokenType::RBRACE) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 🔥 static 初期化子の処理(厳密ゲート互換)
|
||||
if let Some(body) = crate::parser::declarations::static_def::members::parse_static_initializer_if_any(self)? {
|
||||
static_init = Some(body);
|
||||
continue;
|
||||
} else if self.match_token(&TokenType::STATIC) {
|
||||
// 互換用の暫定ガード(既定OFF): using テキスト結合の継ぎ目で誤って 'static' が入った場合に
|
||||
// ループを抜けて外側の '}' 消費に委ねる。既定では無効化し、文脈エラーとして扱う。
|
||||
if std::env::var("NYASH_PARSER_SEAM_BREAK_ON_STATIC").ok().as_deref() == Some("1") {
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[parser][static-box][seam] encountered 'static' inside static box; breaking (compat shim)");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// initブロックの処理(共通ヘルパに委譲)
|
||||
if crate::parser::declarations::box_def::members::fields::parse_init_block_if_any(
|
||||
self, &mut init_fields, &mut weak_fields,
|
||||
)? { continue; }
|
||||
|
||||
// 🔧 Safety valve: if we encounter statement keywords (LOCAL, RETURN, etc.) at member level,
|
||||
// it means we've likely exited a method body prematurely. Break to close the static box.
|
||||
match self.current_token().token_type {
|
||||
TokenType::LOCAL | TokenType::RETURN | TokenType::IF | TokenType::LOOP |
|
||||
TokenType::BREAK | TokenType::CONTINUE | TokenType::PRINT => {
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[parser][static-box][safety] encountered statement keyword {:?} at member level (line {}); assuming premature method body exit",
|
||||
self.current_token().token_type, self.current_token().line);
|
||||
}
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Seam/robustness: tolerate stray tokens between members (text-merge or prelude seams)
|
||||
// NYASH_PARSER_SEAM_TOLERANT=1 (dev/ci既定): ASSIGN を継ぎ目として箱を閉じる(break)
|
||||
// NYASH_PARSER_SEAM_TOLERANT=0 (prod既定): ASSIGN でエラー(Fail-Fast)
|
||||
match &self.current_token().token_type {
|
||||
TokenType::SEMICOLON | TokenType::NEWLINE => { self.advance(); continue; }
|
||||
// If we encounter a bare '=' at member level, treat as seam boundary (gated by flag)
|
||||
// Resynchronize by advancing to the closing '}' so outer logic can consume it.
|
||||
TokenType::ASSIGN => {
|
||||
let seam_tolerant = std::env::var("NYASH_PARSER_SEAM_TOLERANT")
|
||||
.ok()
|
||||
.as_deref() == Some("1");
|
||||
if seam_tolerant {
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!(
|
||||
"[parser][static-box][seam] encountered ASSIGN at member level (line {}); treating as seam boundary (closing box)",
|
||||
self.current_token().line
|
||||
);
|
||||
}
|
||||
// advance until '}' or EOF
|
||||
while !self.is_at_end() && !self.match_token(&TokenType::RBRACE) {
|
||||
self.advance();
|
||||
}
|
||||
// do not consume RBRACE here; let trailing logic handle it
|
||||
break; // 継ぎ目として箱を閉じる
|
||||
} else {
|
||||
// Prod: strict mode, fail fast on unexpected ASSIGN
|
||||
return Err(ParseError::UnexpectedToken {
|
||||
expected: "method or field name".to_string(),
|
||||
found: self.current_token().token_type.clone(),
|
||||
line: self.current_token().line,
|
||||
});
|
||||
}
|
||||
}
|
||||
TokenType::IDENTIFIER(field_or_method) => {
|
||||
let field_or_method = field_or_method.clone();
|
||||
self.advance();
|
||||
crate::parser::declarations::static_def::members::try_parse_method_or_field(
|
||||
self, field_or_method, &mut methods, &mut fields, &mut last_method_name,
|
||||
)?;
|
||||
}
|
||||
_ => {
|
||||
return Err(ParseError::UnexpectedToken {
|
||||
expected: "method or field name".to_string(),
|
||||
found: self.current_token().token_type.clone(),
|
||||
line: self.current_token().line,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tolerate trailing NEWLINE(s) before the closing '}' of the static box
|
||||
while self.match_token(&TokenType::NEWLINE) { self.advance(); }
|
||||
if std::env::var("NYASH_PARSER_TRACE_STATIC").ok().as_deref() == Some("1") {
|
||||
eprintln!(
|
||||
"[parser][static-box] closing '}}' at token={:?}",
|
||||
self.current_token().token_type
|
||||
);
|
||||
}
|
||||
|
||||
// Consume the closing RBRACE of the static box
|
||||
self.consume(TokenType::RBRACE)?;
|
||||
|
||||
if std::env::var("NYASH_PARSER_TRACE_STATIC").ok().as_deref() == Some("1") {
|
||||
eprintln!("[parser][static-box] successfully closed static box '{}'", name);
|
||||
}
|
||||
|
||||
// 🔥 Static初期化ブロックから依存関係を抽出
|
||||
if let Some(ref init_stmts) = static_init {
|
||||
let dependencies = self.extract_dependencies_from_statements(init_stmts);
|
||||
self.static_box_dependencies
|
||||
.insert(name.clone(), dependencies);
|
||||
} else {
|
||||
self.static_box_dependencies
|
||||
.insert(name.clone(), std::collections::HashSet::new());
|
||||
}
|
||||
|
||||
Ok(ASTNode::BoxDeclaration {
|
||||
name,
|
||||
fields,
|
||||
public_fields: vec![],
|
||||
private_fields: vec![],
|
||||
methods,
|
||||
constructors,
|
||||
init_fields,
|
||||
weak_fields, // 🔗 Add weak fields to static box construction
|
||||
is_interface: false,
|
||||
extends,
|
||||
implements,
|
||||
type_parameters,
|
||||
is_static: true, // 🔥 static boxフラグを設定
|
||||
static_init, // 🔥 static初期化ブロック
|
||||
span: Span::unknown(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1,23 +1,179 @@
|
||||
//! Static Box Definition (staged split)
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::ast::ASTNode;
|
||||
use crate::ast::{ASTNode, Span};
|
||||
use crate::parser::{NyashParser, ParseError};
|
||||
use crate::parser::common::ParserUtils;
|
||||
use crate::tokenizer::TokenType;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub mod header;
|
||||
pub mod members;
|
||||
pub mod validators;
|
||||
|
||||
/// Facade placeholder for static box parsing (to be wired gradually).
|
||||
pub(crate) struct StaticDefFacade;
|
||||
/// Parse static box declaration: static box Name { ... }
|
||||
pub fn parse_static_box(p: &mut NyashParser) -> Result<ASTNode, ParseError> {
|
||||
p.consume(TokenType::BOX)?;
|
||||
let (name, type_parameters, extends, implements) =
|
||||
header::parse_static_header(p)?;
|
||||
|
||||
impl StaticDefFacade {
|
||||
pub(crate) fn parse_box(_p: &mut NyashParser) -> Result<ASTNode, ParseError> {
|
||||
Err(ParseError::UnexpectedToken {
|
||||
found: crate::tokenizer::TokenType::EOF,
|
||||
expected: "static box declaration (facade not wired)".to_string(),
|
||||
line: 0,
|
||||
})
|
||||
p.consume(TokenType::LBRACE)?;
|
||||
|
||||
let mut fields = Vec::new();
|
||||
let mut methods = HashMap::new();
|
||||
let constructors = HashMap::new();
|
||||
let mut init_fields = Vec::new();
|
||||
let mut weak_fields = Vec::new(); // 🔗 Track weak fields for static box
|
||||
let mut static_init: Option<Vec<ASTNode>> = None;
|
||||
|
||||
// Track last inserted method name to allow postfix catch/cleanup fallback parsing
|
||||
let mut last_method_name: Option<String> = None;
|
||||
while !p.match_token(&TokenType::RBRACE) && !p.is_at_end() {
|
||||
// Tolerate blank lines between members
|
||||
while p.match_token(&TokenType::NEWLINE) { p.advance(); }
|
||||
let trace = std::env::var("NYASH_PARSER_TRACE_STATIC").ok().as_deref() == Some("1");
|
||||
if trace {
|
||||
eprintln!(
|
||||
"[parser][static-box] loop token={:?}",
|
||||
p.current_token().token_type
|
||||
);
|
||||
}
|
||||
|
||||
// Fallback: method-level postfix catch/cleanup immediately following a method
|
||||
if crate::parser::declarations::box_def::members::postfix::try_parse_method_postfix_after_last_method(
|
||||
p, &mut methods, &last_method_name,
|
||||
)? { continue; }
|
||||
|
||||
// RBRACEに到達していればループを抜ける
|
||||
if p.match_token(&TokenType::RBRACE) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 🔥 static 初期化子の処理(厳密ゲート互換)
|
||||
if let Some(body) = members::parse_static_initializer_if_any(p)? {
|
||||
static_init = Some(body);
|
||||
continue;
|
||||
} else if p.match_token(&TokenType::STATIC) {
|
||||
// 互換用の暫定ガード(既定OFF): using テキスト結合の継ぎ目で誤って 'static' が入った場合に
|
||||
// ループを抜けて外側の '}' 消費に委ねる。既定では無効化し、文脈エラーとして扱う。
|
||||
if std::env::var("NYASH_PARSER_SEAM_BREAK_ON_STATIC").ok().as_deref() == Some("1") {
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[parser][static-box][seam] encountered 'static' inside static box; breaking (compat shim)");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// initブロックの処理(共通ヘルパに委譲)
|
||||
if crate::parser::declarations::box_def::members::fields::parse_init_block_if_any(
|
||||
p, &mut init_fields, &mut weak_fields,
|
||||
)? { continue; }
|
||||
|
||||
// 🔧 Safety valve: if we encounter statement keywords (LOCAL, RETURN, etc.) at member level,
|
||||
// it means we've likely exited a method body prematurely. Break to close the static box.
|
||||
match p.current_token().token_type {
|
||||
TokenType::LOCAL | TokenType::RETURN | TokenType::IF | TokenType::LOOP |
|
||||
TokenType::BREAK | TokenType::CONTINUE | TokenType::PRINT => {
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[parser][static-box][safety] encountered statement keyword {:?} at member level (line {}); assuming premature method body exit",
|
||||
p.current_token().token_type, p.current_token().line);
|
||||
}
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Seam/robustness: tolerate stray tokens between members (text-merge or prelude seams)
|
||||
// NYASH_PARSER_SEAM_TOLERANT=1 (dev/ci既定): ASSIGN を継ぎ目として箱を閉じる(break)
|
||||
// NYASH_PARSER_SEAM_TOLERANT=0 (prod既定): ASSIGN でエラー(Fail-Fast)
|
||||
match &p.current_token().token_type {
|
||||
TokenType::SEMICOLON | TokenType::NEWLINE => { p.advance(); continue; }
|
||||
// If we encounter a bare '=' at member level, treat as seam boundary (gated by flag)
|
||||
// Resynchronize by advancing to the closing '}' so outer logic can consume it.
|
||||
TokenType::ASSIGN => {
|
||||
let seam_tolerant = std::env::var("NYASH_PARSER_SEAM_TOLERANT")
|
||||
.ok()
|
||||
.as_deref() == Some("1");
|
||||
if seam_tolerant {
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!(
|
||||
"[parser][static-box][seam] encountered ASSIGN at member level (line {}); treating as seam boundary (closing box)",
|
||||
p.current_token().line
|
||||
);
|
||||
}
|
||||
// advance until '}' or EOF
|
||||
while !p.is_at_end() && !p.match_token(&TokenType::RBRACE) {
|
||||
p.advance();
|
||||
}
|
||||
// do not consume RBRACE here; let trailing logic handle it
|
||||
break; // 継ぎ目として箱を閉じる
|
||||
} else {
|
||||
// Prod: strict mode, fail fast on unexpected ASSIGN
|
||||
return Err(ParseError::UnexpectedToken {
|
||||
expected: "method or field name".to_string(),
|
||||
found: p.current_token().token_type.clone(),
|
||||
line: p.current_token().line,
|
||||
});
|
||||
}
|
||||
}
|
||||
TokenType::IDENTIFIER(field_or_method) => {
|
||||
let field_or_method = field_or_method.clone();
|
||||
p.advance();
|
||||
members::try_parse_method_or_field(
|
||||
p, field_or_method, &mut methods, &mut fields, &mut last_method_name,
|
||||
)?;
|
||||
}
|
||||
_ => {
|
||||
return Err(ParseError::UnexpectedToken {
|
||||
expected: "method or field name".to_string(),
|
||||
found: p.current_token().token_type.clone(),
|
||||
line: p.current_token().line,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tolerate trailing NEWLINE(s) before the closing '}' of the static box
|
||||
while p.match_token(&TokenType::NEWLINE) { p.advance(); }
|
||||
if std::env::var("NYASH_PARSER_TRACE_STATIC").ok().as_deref() == Some("1") {
|
||||
eprintln!(
|
||||
"[parser][static-box] closing '}}' at token={:?}",
|
||||
p.current_token().token_type
|
||||
);
|
||||
}
|
||||
|
||||
// Consume the closing RBRACE of the static box
|
||||
p.consume(TokenType::RBRACE)?;
|
||||
|
||||
if std::env::var("NYASH_PARSER_TRACE_STATIC").ok().as_deref() == Some("1") {
|
||||
eprintln!("[parser][static-box] successfully closed static box '{}'", name);
|
||||
}
|
||||
|
||||
// 🔥 Static初期化ブロックから依存関係を抽出
|
||||
if let Some(ref init_stmts) = static_init {
|
||||
let dependencies = p.extract_dependencies_from_statements(init_stmts);
|
||||
p.static_box_dependencies
|
||||
.insert(name.clone(), dependencies);
|
||||
} else {
|
||||
p.static_box_dependencies
|
||||
.insert(name.clone(), std::collections::HashSet::new());
|
||||
}
|
||||
|
||||
Ok(ASTNode::BoxDeclaration {
|
||||
name,
|
||||
fields,
|
||||
public_fields: vec![],
|
||||
private_fields: vec![],
|
||||
methods,
|
||||
constructors,
|
||||
init_fields,
|
||||
weak_fields, // 🔗 Add weak fields to static box construction
|
||||
is_interface: false,
|
||||
extends,
|
||||
implements,
|
||||
type_parameters,
|
||||
is_static: true, // 🔥 static boxフラグを設定
|
||||
static_init, // 🔥 static初期化ブロック
|
||||
span: Span::unknown(),
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user