feat: nyash.toml SSOT + using AST統合完了(12時間の戦い)

- nyash.tomlを唯一の真実(SSOT)として依存管理確立
- dev/ci/prodプロファイルによる段階的厳格化実装
- AST結合で宣言/式の曖昧性を根本解決
- Fail-Fast原則をCLAUDE.md/AGENTS.mdに明文化
- VM fallbackでもASTベース using有効化(NYASH_USING_AST=1)
- 静的メソッドの is_static=true 修正で解決安定化
- STATICブレークハック既定OFF化で堅牢性向上

🎉 usingシステム完全体への道筋確立!JSONライブラリ・Nyash VM開発が可能に

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Selfhosting Dev
2025-09-25 16:03:29 +09:00
parent 2f5723b56d
commit d9f26d4549
19 changed files with 762 additions and 97 deletions

View File

@ -29,6 +29,15 @@ impl NyashParser {
// 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(
@ -45,8 +54,14 @@ impl NyashParser {
static_init = Some(body);
continue;
} else if self.match_token(&TokenType::STATIC) {
// STRICT で top-level seam を検出した場合は while を抜ける
break;
// 互換用の暫定ガード既定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ブロックの処理共通ヘルパに委譲
@ -69,6 +84,12 @@ impl NyashParser {
}
}
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
);
}
self.consume(TokenType::RBRACE)?;
// 🔥 Static初期化ブロックから依存関係を抽出

View File

@ -56,6 +56,7 @@ pub(crate) fn try_parse_method_or_field(
fields: &mut Vec<String>,
last_method_name: &mut Option<String>,
) -> Result<bool, ParseError> {
let trace = std::env::var("NYASH_PARSER_TRACE_STATIC").ok().as_deref() == Some("1");
// Allow NEWLINE(s) between identifier and '('
if !p.match_token(&TokenType::LPAREN) {
// Lookahead skipping NEWLINE to see if a '(' follows → treat as method head
@ -65,11 +66,13 @@ pub(crate) fn try_parse_method_or_field(
// Consume intervening NEWLINEs so current becomes '('
while p.match_token(&TokenType::NEWLINE) { p.advance(); }
} else {
if trace { eprintln!("[parser][static-box] field detected: {}", name); }
// Field
fields.push(name);
return Ok(true);
}
}
if trace { eprintln!("[parser][static-box] method head detected: {}(..)", name); }
// Method
p.advance(); // consume '('
let mut params = Vec::new();
@ -84,14 +87,20 @@ pub(crate) fn try_parse_method_or_field(
p.consume(TokenType::RPAREN)?;
// Allow NEWLINE(s) between ')' and '{' of method body
while p.match_token(&TokenType::NEWLINE) { p.advance(); }
let body = p.parse_block_statements()?;
// Parse method body; optionally use strict method-body guard when enabled
let body = if std::env::var("NYASH_PARSER_METHOD_BODY_STRICT").ok().as_deref() == Some("1") {
p.parse_method_body_statements()?
} else {
p.parse_block_statements()?
};
let body = wrap_method_body_with_postfix_if_any(p, body)?;
// Construct method node
let method = ASTNode::FunctionDeclaration {
name: name.clone(),
params,
body,
is_static: false,
// Methods inside a static box are semantically static
is_static: true,
is_override: false,
span: crate::ast::Span::unknown(),
};

View File

@ -83,13 +83,96 @@ impl NyashParser {
/// Parse block statements: { statement* }
pub(super) fn parse_block_statements(&mut self) -> Result<Vec<ASTNode>, ParseError> {
let trace_blocks = std::env::var("NYASH_PARSER_TRACE_BLOCKS").ok().as_deref() == Some("1");
if trace_blocks {
eprintln!(
"[parser][block] enter '{{' at line {}",
self.current_token().line
);
}
self.consume(TokenType::LBRACE)?;
let mut statements = Vec::new();
while !self.is_at_end() && !self.match_token(&TokenType::RBRACE) {
statements.push(self.parse_statement()?);
}
if trace_blocks {
eprintln!(
"[parser][block] exit '}}' at line {}",
self.current_token().line
);
}
self.consume(TokenType::RBRACE)?;
Ok(statements)
}
/// Parse method body statements: { statement* }
/// Optional seam-guard (env-gated via NYASH_PARSER_METHOD_BODY_STRICT=1) is applied
/// conservatively at top-level only, and only right after a nested block '}' was
/// just consumed, to avoid false positives inside method bodies.
pub(super) fn parse_method_body_statements(&mut self) -> Result<Vec<ASTNode>, ParseError> {
// Reuse block entry tracing
let trace_blocks = std::env::var("NYASH_PARSER_TRACE_BLOCKS").ok().as_deref() == Some("1");
if trace_blocks {
eprintln!(
"[parser][block] enter '{{' (method) at line {}",
self.current_token().line
);
}
self.consume(TokenType::LBRACE)?;
let mut statements = Vec::new();
// Helper: lookahead for `ident '(' ... ')' [NEWLINE*] '{'`
let mut looks_like_method_head = |this: &Self| -> bool {
// Only meaningful when starting at a new statement head
match &this.current_token().token_type {
TokenType::IDENTIFIER(_) => {
// Expect '(' after optional NEWLINE
let mut k = 1usize;
while matches!(this.peek_nth_token(k), TokenType::NEWLINE) { k += 1; }
if !matches!(this.peek_nth_token(k), TokenType::LPAREN) { return false; }
// Walk to matching ')'
k += 1; // after '('
let mut depth: i32 = 1;
while !matches!(this.peek_nth_token(k), TokenType::EOF) {
match this.peek_nth_token(k) {
TokenType::LPAREN => depth += 1,
TokenType::RPAREN => { depth -= 1; if depth == 0 { k += 1; break; } },
_ => {}
}
k += 1;
}
// Allow NEWLINE(s) between ')' and '{'
while matches!(this.peek_nth_token(k), TokenType::NEWLINE) { k += 1; }
matches!(this.peek_nth_token(k), TokenType::LBRACE)
}
_ => false,
}
};
while !self.is_at_end() && !self.match_token(&TokenType::RBRACE) {
statements.push(self.parse_statement()?);
// Conservative seam guard: apply only when env is ON, we just consumed a '}'
// (end of a nested block), and the next tokens at the top-level look like a
// method head. This limits the guard to real seams between members.
if std::env::var("NYASH_PARSER_METHOD_BODY_STRICT").ok().as_deref() == Some("1") {
// If the next token would close the current method, do not guard here
if self.match_token(&TokenType::RBRACE) { break; }
// Check if we just consumed a '}' token from inner content
let just_saw_rbrace = if self.current > 0 {
matches!(self.tokens[self.current - 1].token_type, TokenType::RBRACE)
} else { false };
if just_saw_rbrace && looks_like_method_head(self) {
break;
}
}
}
if trace_blocks {
eprintln!(
"[parser][block] exit '}}' (method) at line {}",
self.current_token().line
);
}
self.consume(TokenType::RBRACE)?;
Ok(statements)
}
@ -158,4 +241,4 @@ impl NyashParser {
result
}
}
}