fix(parser): Add Stage-3 gate for LOCAL/TRY/CATCH/THROW keywords
🔧 Problem: Using-chain files with 'local' keyword fail to parse - Error: 'Unexpected token LOCAL, expected identifier at line 19' - Tokenizer treats 'local' as keyword regardless of NYASH_PARSER_STAGE3 ✅ Solution: Add Stage-3 gate in tokenizer - src/tokenizer/lex_ident.rs: Check parser_stage3() before emitting LOCAL/TRY/CATCH/THROW tokens - If Stage-3 disabled, degrade to IDENTIFIER - Trace output with NYASH_TOK_TRACE=1 🐛 Debug enhancements: - src/runner/modes/common_util/resolve/strip.rs: Add NYASH_STRIP_DEBUG=1 tracing for using-chain parsing - src/runner/modes/vm.rs: Add vm-debug trace for main source parsing 📋 Investigation ongoing: - Using-chain preludes parse successfully - Error occurs later (possibly during VM execution) - Next: Check if selfhost ParserBox needs Stage-3 awareness Related: #stageb-緑化 #phase-20.33
This commit is contained in:
@ -156,7 +156,10 @@ impl NyashParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 文字列からパース (トークナイズ + パース)
|
/// 文字列からパース (トークナイズ + パース)
|
||||||
|
/// Note: Reads environment variables (NYASH_PARSER_STAGE3, etc.) for using-chain parsing
|
||||||
pub fn parse_from_string(input: impl Into<String>) -> Result<ASTNode, ParseError> {
|
pub fn parse_from_string(input: impl Into<String>) -> Result<ASTNode, ParseError> {
|
||||||
|
// Ensure Stage-3 features are enabled when parsing using-chain files
|
||||||
|
// if the parent process has NYASH_PARSER_STAGE3=1 set
|
||||||
Self::parse_from_string_with_fuel(input, Some(100_000))
|
Self::parse_from_string_with_fuel(input, Some(100_000))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -443,18 +443,59 @@ pub fn parse_preludes_to_asts(
|
|||||||
runner: &NyashRunner,
|
runner: &NyashRunner,
|
||||||
prelude_paths: &[String],
|
prelude_paths: &[String],
|
||||||
) -> Result<Vec<nyash_rust::ast::ASTNode>, String> {
|
) -> Result<Vec<nyash_rust::ast::ASTNode>, String> {
|
||||||
|
let debug = std::env::var("NYASH_STRIP_DEBUG").ok().as_deref() == Some("1");
|
||||||
|
if debug {
|
||||||
|
eprintln!("[strip-debug] parse_preludes_to_asts: {} files total", prelude_paths.len());
|
||||||
|
for (idx, p) in prelude_paths.iter().enumerate() {
|
||||||
|
eprintln!("[strip-debug] [{}] {}", idx, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
let mut out: Vec<nyash_rust::ast::ASTNode> = Vec::with_capacity(prelude_paths.len());
|
let mut out: Vec<nyash_rust::ast::ASTNode> = Vec::with_capacity(prelude_paths.len());
|
||||||
for prelude_path in prelude_paths {
|
for (idx, prelude_path) in prelude_paths.iter().enumerate() {
|
||||||
|
if debug {
|
||||||
|
eprintln!("[strip-debug] [{}/{}] Processing: {}", idx + 1, prelude_paths.len(), prelude_path);
|
||||||
|
}
|
||||||
let src = std::fs::read_to_string(prelude_path)
|
let src = std::fs::read_to_string(prelude_path)
|
||||||
.map_err(|e| format!("using: error reading {}: {}", prelude_path, e))?;
|
.map_err(|e| format!("using: error reading {}: {}", prelude_path, e))?;
|
||||||
let (clean_src, _nested) = collect_using_and_strip(runner, &src, prelude_path)?;
|
let (clean_src, _nested) = collect_using_and_strip(runner, &src, prelude_path)?;
|
||||||
match crate::parser::NyashParser::parse_from_string(&clean_src) {
|
|
||||||
Ok(ast) => out.push(ast),
|
// Debug: dump clean_src if NYASH_STRIP_DEBUG=1
|
||||||
Err(e) => return Err(format!(
|
if debug {
|
||||||
"Parse error in using prelude {}: {}",
|
eprintln!("[strip-debug] [{}/{}] About to parse: {}", idx + 1, prelude_paths.len(), prelude_path);
|
||||||
prelude_path, e
|
eprintln!("[strip-debug] clean_src first 500 chars:\n{}\n---",
|
||||||
)),
|
&clean_src.chars().take(500).collect::<String>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match crate::parser::NyashParser::parse_from_string(&clean_src) {
|
||||||
|
Ok(ast) => {
|
||||||
|
if debug {
|
||||||
|
eprintln!("[strip-debug] [{}/{}] ✅ Parse SUCCESS: {}", idx + 1, prelude_paths.len(), prelude_path);
|
||||||
|
}
|
||||||
|
out.push(ast)
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
// Always output debug info on parse failure if NYASH_STRIP_DEBUG=1
|
||||||
|
let debug = std::env::var("NYASH_STRIP_DEBUG").ok().as_deref() == Some("1");
|
||||||
|
eprintln!("[strip-debug] Parse FAILED for: {} (debug={})", prelude_path, debug);
|
||||||
|
if debug {
|
||||||
|
eprintln!("[strip-debug] Error: {}", e);
|
||||||
|
let lines: Vec<&str> = clean_src.lines().collect();
|
||||||
|
eprintln!("[strip-debug] Total lines: {}", lines.len());
|
||||||
|
eprintln!("[strip-debug] Lines 15-25:");
|
||||||
|
for (idx, line) in lines.iter().enumerate().skip(14).take(11) {
|
||||||
|
eprintln!(" {:3}: {}", idx + 1, line);
|
||||||
|
}
|
||||||
|
eprintln!("[strip-debug] Full clean_src:\n{}\n---", clean_src);
|
||||||
|
}
|
||||||
|
return Err(format!(
|
||||||
|
"Parse error in using prelude {}: {}",
|
||||||
|
prelude_path, e
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if debug {
|
||||||
|
eprintln!("[strip-debug] parse_preludes_to_asts: ✅ All {} files parsed successfully", out.len());
|
||||||
}
|
}
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -144,10 +144,24 @@ impl NyashRunner {
|
|||||||
code_ref = &preexpanded_owned;
|
code_ref = &preexpanded_owned;
|
||||||
|
|
||||||
// Parse to AST
|
// Parse to AST
|
||||||
|
if std::env::var("NYASH_STRIP_DEBUG").ok().as_deref() == Some("1") {
|
||||||
|
eprintln!("[vm-debug] About to parse main source ({} bytes)", code_ref.len());
|
||||||
|
eprintln!("[vm-debug] First 20 lines:");
|
||||||
|
for (idx, line) in code_ref.lines().enumerate().take(20) {
|
||||||
|
eprintln!(" {:3}: {}", idx + 1, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
let main_ast = match NyashParser::parse_from_string(code_ref) {
|
let main_ast = match NyashParser::parse_from_string(code_ref) {
|
||||||
Ok(ast) => ast,
|
Ok(ast) => ast,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("❌ Parse error: {}", e);
|
eprintln!("❌ Parse error: {}", e);
|
||||||
|
if std::env::var("NYASH_STRIP_DEBUG").ok().as_deref() == Some("1") {
|
||||||
|
eprintln!("[vm-debug] Parse failed for main source");
|
||||||
|
eprintln!("[vm-debug] Line 15-25 of source:");
|
||||||
|
for (idx, line) in code_ref.lines().enumerate().skip(14).take(11) {
|
||||||
|
eprintln!(" {:3}: {}", idx + 1, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -61,6 +61,39 @@ impl NyashTokenizer {
|
|||||||
_ => TokenType::IDENTIFIER(identifier.clone()),
|
_ => TokenType::IDENTIFIER(identifier.clone()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Stage-3 gate: LOCAL/TRY/CATCH/THROW require NYASH_PARSER_STAGE3=1
|
||||||
|
let stage3_enabled = crate::config::env::parser_stage3();
|
||||||
|
if !stage3_enabled {
|
||||||
|
let is_stage3 = matches!(
|
||||||
|
tok,
|
||||||
|
TokenType::LOCAL
|
||||||
|
| TokenType::TRY
|
||||||
|
| TokenType::CATCH
|
||||||
|
| TokenType::THROW
|
||||||
|
);
|
||||||
|
if is_stage3 {
|
||||||
|
if std::env::var("NYASH_TOK_TRACE").ok().as_deref() == Some("1") {
|
||||||
|
eprintln!("[tok-stage3] Degrading {:?} to IDENTIFIER (NYASH_PARSER_STAGE3={})",
|
||||||
|
tok, stage3_enabled);
|
||||||
|
}
|
||||||
|
tok = TokenType::IDENTIFIER(identifier.clone());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if std::env::var("NYASH_TOK_TRACE").ok().as_deref() == Some("1") {
|
||||||
|
let is_stage3 = matches!(
|
||||||
|
tok,
|
||||||
|
TokenType::LOCAL
|
||||||
|
| TokenType::TRY
|
||||||
|
| TokenType::CATCH
|
||||||
|
| TokenType::THROW
|
||||||
|
);
|
||||||
|
if is_stage3 {
|
||||||
|
eprintln!("[tok-stage3] Keeping {:?} as keyword (NYASH_PARSER_STAGE3={})",
|
||||||
|
tok, stage3_enabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 12.7 Strict mode: fallback extended keywords to IDENTIFIER
|
// 12.7 Strict mode: fallback extended keywords to IDENTIFIER
|
||||||
if Self::strict_12_7() {
|
if Self::strict_12_7() {
|
||||||
let is_extended = matches!(
|
let is_extended = matches!(
|
||||||
|
|||||||
Reference in New Issue
Block a user