diff --git a/CLAUDE.md b/CLAUDE.md index 0bda9aa3..46715f08 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -205,6 +205,31 @@ NYASH_DISABLE_PLUGINS=1 NYASH_ENABLE_USING=1 NYASH_VM_USE_PY=1 ./target/release/ **💡 覚え方**:迷ったら`NYASH_DISABLE_PLUGINS=1`から試す! +### ⚠️ **using system環境変数地獄(要整理)** + +**現状**: using関連テストに**8個**の環境変数が必要で複雑すぎる状況 +```bash +# using混在スモークテスト用の環境変数地獄 +NYASH_ENABLE_USING=1 # using構文有効化 +NYASH_VM_USE_PY=1 # PyVM使用 +NYASH_LOAD_NY_PLUGINS=1 # Nyプラグイン読み込み +NYASH_RESOLVE_FIX_BRACES=1 # ブレース修正 +NYASH_PARSER_STATIC_INIT_STRICT=1 # パーサー厳格モード +NYASH_PYVM_DUMP_CODE=1 # PyVMコードダンプ +NYASH_RESOLVE_SEAM_DEBUG=1 # seam結合デバッグ +NYASH_RESOLVE_DEDUP_BOX=1 # 重複Box削除 +``` + +**問題**: +- 🔥 認知負荷高すぎ(8個は覚えられない) +- 🔥 相互依存性不明(どれが必須?) +- 🔥 組み合わせ爆発(2^8 = 256通り) +- 🔥 デバッグ困難(どれが原因?) + +**将来の簡略化案**: +- `NYASH_USING_PROFILE=dev|smoke|debug` でプロファイル化 +- または `--using-mode=dev` CLIフラグで統合 + ## 🚀 よく使う実行コマンド(忘れやすい) ### 🎯 基本実行方法 diff --git a/src/parser/declarations/box_def/header.rs b/src/parser/declarations/box_def/header.rs index c5d8f64d..1ea71df1 100644 --- a/src/parser/declarations/box_def/header.rs +++ b/src/parser/declarations/box_def/header.rs @@ -34,7 +34,6 @@ pub(crate) fn parse_header( p.advance(); if p.match_token(&TokenType::COMMA) { p.advance(); - p.skip_newlines(); } } else { return Err(ParseError::UnexpectedToken { @@ -66,7 +65,6 @@ pub(crate) fn parse_header( } while p.match_token(&TokenType::COMMA) { p.advance(); // consume ',' - p.skip_newlines(); if let TokenType::IDENTIFIER(parent) = &p.current_token().token_type { parents.push(parent.clone()); p.advance(); diff --git a/src/parser/declarations/box_def/interface.rs b/src/parser/declarations/box_def/interface.rs index 11b06fb0..bf68cddd 100644 --- a/src/parser/declarations/box_def/interface.rs +++ b/src/parser/declarations/box_def/interface.rs @@ -25,12 +25,10 @@ pub(crate) fn parse_interface_box(p: &mut NyashParser) -> Result Result = Vec::new(); @@ -52,7 +51,6 @@ pub(crate) fn try_parse_constructor( body: catch_body, span: Span::unknown(), }); - p.skip_newlines(); if p.match_token(&TokenType::CATCH) { let line = p.current_token().line; return Err(ParseError::UnexpectedToken { diff --git a/src/parser/declarations/box_def/members/postfix.rs b/src/parser/declarations/box_def/members/postfix.rs index 6bcf5c1b..548d13c9 100644 --- a/src/parser/declarations/box_def/members/postfix.rs +++ b/src/parser/declarations/box_def/members/postfix.rs @@ -30,7 +30,6 @@ pub(crate) fn wrap_with_optional_postfix( body: catch_body, span: Span::unknown(), }); - p.skip_newlines(); if p.match_token(&TokenType::CATCH) { let line = p.current_token().line; return Err(ParseError::UnexpectedToken { @@ -78,7 +77,6 @@ pub(crate) fn try_parse_method_postfix_after_last_method( body: catch_body, span: Span::unknown(), }); - p.skip_newlines(); if p.match_token(&TokenType::CATCH) { let line = p.current_token().line; return Err(ParseError::UnexpectedToken { diff --git a/src/parser/declarations/box_def/members/properties.rs b/src/parser/declarations/box_def/members/properties.rs index 0fa0d42f..25c6957f 100644 --- a/src/parser/declarations/box_def/members/properties.rs +++ b/src/parser/declarations/box_def/members/properties.rs @@ -129,7 +129,6 @@ pub(crate) fn try_parse_block_first_property( } // 1) Parse block body first let mut final_body = p.parse_block_statements()?; - p.skip_newlines(); // 2) Expect 'as' if let TokenType::IDENTIFIER(kw) = &p.current_token().token_type { diff --git a/src/parser/expr/primary.rs b/src/parser/expr/primary.rs index 7529b1fa..1642deba 100644 --- a/src/parser/expr/primary.rs +++ b/src/parser/expr/primary.rs @@ -297,7 +297,6 @@ impl NyashParser { self.consume(TokenType::LBRACE)?; let mut body: Vec = 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()?); } diff --git a/src/parser/nyash_parser_v2.rs b/src/parser/nyash_parser_v2.rs new file mode 100644 index 00000000..d814892c --- /dev/null +++ b/src/parser/nyash_parser_v2.rs @@ -0,0 +1,244 @@ +/*! + * NyashParser v2 - TokenCursorベースの新パーサー + * + * 改行処理を完全自動化した次世代パーサー + * skip_newlines()の手動呼び出しを完全排除 + */ + +use crate::ast::{ASTNode, Span}; +use crate::parser::cursor::{TokenCursor, NewlineMode}; +use crate::parser::ParseError; +use crate::tokenizer::{Token, TokenType}; +use std::collections::{HashMap, HashSet}; + +/// TokenCursorベースの新パーサー +pub struct NyashParserV2<'a> { + cursor: TokenCursor<'a>, + static_box_dependencies: HashMap>, + debug_fuel: Option, +} + +impl<'a> NyashParserV2<'a> { + /// 新しいパーサーを作成 + pub fn new(tokens: &'a [Token]) -> Self { + Self { + cursor: TokenCursor::new(tokens), + static_box_dependencies: HashMap::new(), + debug_fuel: Some(100_000), + } + } + + /// プログラムをパース(エントリーポイント) + pub fn parse_program(&mut self) -> Result { + let mut statements = Vec::new(); + + // 文モードでパース(改行が文の区切り) + while !self.cursor.is_at_end() { + statements.push(self.parse_statement()?); + + // 文の区切り(改行やセミコロン)は自動処理 + while self.cursor.match_token(&TokenType::NEWLINE) + || self.cursor.match_token(&TokenType::SEMICOLON) { + self.cursor.advance(); + } + } + + Ok(ASTNode::Program { + statements, + span: Span::unknown(), + }) + } + + /// 文をパース + pub fn parse_statement(&mut self) -> Result { + // 文モードで実行(改行を文の区切りとして扱う) + match &self.cursor.current().token_type { + TokenType::LOCAL => self.parse_local_declaration(), + TokenType::IF => self.parse_if_statement(), + TokenType::LOOP => self.parse_loop_statement(), + TokenType::RETURN => self.parse_return_statement(), + TokenType::BREAK => self.parse_break_statement(), + TokenType::CONTINUE => self.parse_continue_statement(), + _ => { + // 式文(代入や関数呼び出しなど) + self.parse_expression_statement() + } + } + } + + /// 式をパース + pub fn parse_expression(&mut self) -> Result { + // 式モードで実行(改行を自動的にスキップ) + self.cursor.with_expr_mode(|c| { + Self::parse_or_expression_internal(c) + }) + } + + /// OR式をパース(内部実装) + fn parse_or_expression_internal(cursor: &mut TokenCursor) -> Result { + let mut left = Self::parse_and_expression_internal(cursor)?; + + while cursor.match_token(&TokenType::OR) { + cursor.advance(); + let right = Self::parse_and_expression_internal(cursor)?; + left = ASTNode::BinaryOp { + operator: crate::ast::BinaryOperator::Or, + left: Box::new(left), + right: Box::new(right), + span: Span::unknown(), + }; + } + + Ok(left) + } + + /// AND式をパース(内部実装) + fn parse_and_expression_internal(cursor: &mut TokenCursor) -> Result { + let mut left = Self::parse_primary_expression_internal(cursor)?; + + while cursor.match_token(&TokenType::AND) { + cursor.advance(); + let right = Self::parse_primary_expression_internal(cursor)?; + left = ASTNode::BinaryOp { + operator: crate::ast::BinaryOperator::And, + left: Box::new(left), + right: Box::new(right), + span: Span::unknown(), + }; + } + + Ok(left) + } + + /// プライマリ式をパース(内部実装) + fn parse_primary_expression_internal(cursor: &mut TokenCursor) -> Result { + match &cursor.current().token_type.clone() { + TokenType::NUMBER(n) => { + let value = *n; + cursor.advance(); + Ok(ASTNode::Literal { + value: crate::ast::LiteralValue::Integer(value), + span: Span::unknown(), + }) + } + TokenType::STRING(s) => { + let value = s.clone(); + cursor.advance(); + Ok(ASTNode::Literal { + value: crate::ast::LiteralValue::String(value), + span: Span::unknown(), + }) + } + TokenType::TRUE => { + cursor.advance(); + Ok(ASTNode::Literal { + value: crate::ast::LiteralValue::Bool(true), + span: Span::unknown(), + }) + } + TokenType::FALSE => { + cursor.advance(); + Ok(ASTNode::Literal { + value: crate::ast::LiteralValue::Bool(false), + span: Span::unknown(), + }) + } + TokenType::IDENTIFIER(name) => { + let name = name.clone(); + cursor.advance(); + Ok(ASTNode::Variable { + name, + span: Span::unknown(), + }) + } + TokenType::LBRACE => { + // オブジェクトリテラル(改行は自動処理) + Self::parse_object_literal_internal(cursor) + } + TokenType::LPAREN => { + cursor.advance(); + let expr = Self::parse_or_expression_internal(cursor)?; + cursor.consume(TokenType::RPAREN)?; + Ok(expr) + } + _ => { + let line = cursor.current().line; + Err(ParseError::InvalidExpression { line }) + } + } + } + + /// オブジェクトリテラルをパース(改行完全自動化) + fn parse_object_literal_internal(cursor: &mut TokenCursor) -> Result { + cursor.consume(TokenType::LBRACE)?; + let mut entries = Vec::new(); + + // ブレース内は改行が自動的にスキップされる! + while !cursor.match_token(&TokenType::RBRACE) && !cursor.is_at_end() { + // キーをパース + let key = match &cursor.current().token_type { + TokenType::STRING(s) => { + let k = s.clone(); + cursor.advance(); + k + } + TokenType::IDENTIFIER(id) => { + let k = id.clone(); + cursor.advance(); + k + } + _ => { + let line = cursor.current().line; + return Err(ParseError::UnexpectedToken { + found: cursor.current().token_type.clone(), + expected: "string or identifier key".to_string(), + line, + }); + } + }; + + cursor.consume(TokenType::COLON)?; + let value = Self::parse_or_expression_internal(cursor)?; + entries.push((key, value)); + + if cursor.match_token(&TokenType::COMMA) { + cursor.advance(); + } + } + + cursor.consume(TokenType::RBRACE)?; + Ok(ASTNode::MapLiteral { + entries, + span: Span::unknown(), + }) + } + + // 以下、各種文のパースメソッド(スタブ) + fn parse_local_declaration(&mut self) -> Result { + todo!("local宣言のパース実装") + } + + fn parse_if_statement(&mut self) -> Result { + todo!("if文のパース実装") + } + + fn parse_loop_statement(&mut self) -> Result { + todo!("loop文のパース実装") + } + + fn parse_return_statement(&mut self) -> Result { + todo!("return文のパース実装") + } + + fn parse_break_statement(&mut self) -> Result { + todo!("break文のパース実装") + } + + fn parse_continue_statement(&mut self) -> Result { + todo!("continue文のパース実装") + } + + fn parse_expression_statement(&mut self) -> Result { + self.parse_expression() + } +} \ No newline at end of file diff --git a/src/parser/parser_enhanced.rs b/src/parser/parser_enhanced.rs new file mode 100644 index 00000000..4560f6dc --- /dev/null +++ b/src/parser/parser_enhanced.rs @@ -0,0 +1,200 @@ +/*! + * Parser Enhanced - 既存パーサーの改行処理自動化 + * + * 既存のNyashParserを拡張し、advance()で自動的に改行をスキップ + * skip_newlines()の明示的呼び出しを不要にする + */ + +use crate::tokenizer::{Token, TokenType}; +use std::cell::Cell; + +/// パーサーコンテキスト(改行処理のモード管理) +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum ParserContext { + /// 文コンテキスト(改行は文の区切り) + Statement, + /// 式コンテキスト(改行を自動スキップ) + Expression, + /// ブロック内(改行を自動スキップ) + Block, +} + +thread_local! { + /// 現在のパーサーコンテキスト + static PARSER_CONTEXT: Cell = Cell::new(ParserContext::Statement); + + /// 括弧の深度(自動改行スキップの判定用) + static PAREN_DEPTH: Cell = Cell::new(0); + static BRACE_DEPTH: Cell = Cell::new(0); + static BRACKET_DEPTH: Cell = Cell::new(0); +} + +/// コンテキストガード(RAIIパターンで自動復元) +pub struct ContextGuard { + prev_context: ParserContext, +} + +impl Drop for ContextGuard { + fn drop(&mut self) { + PARSER_CONTEXT.with(|c| c.set(self.prev_context)); + } +} + +/// 式コンテキストで実行 +pub fn with_expr_context(f: F) -> T +where + F: FnOnce() -> T, +{ + let prev = PARSER_CONTEXT.with(|c| c.get()); + PARSER_CONTEXT.with(|c| c.set(ParserContext::Expression)); + let result = f(); + PARSER_CONTEXT.with(|c| c.set(prev)); + result +} + +/// ブロックコンテキストで実行 +pub fn with_block_context(f: F) -> T +where + F: FnOnce() -> T, +{ + let prev = PARSER_CONTEXT.with(|c| c.get()); + PARSER_CONTEXT.with(|c| c.set(ParserContext::Block)); + let result = f(); + PARSER_CONTEXT.with(|c| c.set(prev)); + result +} + +/// 改行をスキップすべきか判定 +pub fn should_skip_newlines() -> bool { + // 括弧内では常にスキップ + if PAREN_DEPTH.with(|d| d.get()) > 0 + || BRACE_DEPTH.with(|d| d.get()) > 0 + || BRACKET_DEPTH.with(|d| d.get()) > 0 + { + return true; + } + + // コンテキストによる判定 + match PARSER_CONTEXT.with(|c| c.get()) { + ParserContext::Expression | ParserContext::Block => true, + ParserContext::Statement => false, + } +} + +/// トークンタイプによる深度更新 +pub fn update_depth(token_type: &TokenType, advancing: bool) { + match token_type { + TokenType::LPAREN => { + if advancing { + PAREN_DEPTH.with(|d| d.set(d.get() + 1)); + } + } + TokenType::RPAREN => { + if !advancing { + PAREN_DEPTH.with(|d| d.set(d.get().saturating_sub(1))); + } + } + TokenType::LBRACE => { + if advancing { + BRACE_DEPTH.with(|d| d.set(d.get() + 1)); + } + } + TokenType::RBRACE => { + if !advancing { + BRACE_DEPTH.with(|d| d.set(d.get().saturating_sub(1))); + } + } + TokenType::LBRACK => { + if advancing { + BRACKET_DEPTH.with(|d| d.set(d.get() + 1)); + } + } + TokenType::RBRACK => { + if !advancing { + BRACKET_DEPTH.with(|d| d.set(d.get().saturating_sub(1))); + } + } + _ => {} + } +} + +/// 改良されたadvance実装(自動改行スキップ付き) +pub fn smart_advance( + tokens: &[Token], + current: &mut usize, + prev_token: Option<&TokenType>, +) { + if *current >= tokens.len() { + return; + } + + // 現在のトークンで深度を更新 + let current_token = &tokens[*current].token_type; + update_depth(current_token, true); + + // 位置を進める + *current += 1; + + // 改行を自動的にスキップ + while *current < tokens.len() { + let token_type = &tokens[*current].token_type; + + // 改行判定 + if matches!(token_type, TokenType::NEWLINE) { + // スキップすべきか判定 + if should_skip_newlines() || is_line_continuation(prev_token) { + *current += 1; + continue; + } + } + + // セミコロンも同様に処理 + if matches!(token_type, TokenType::SEMICOLON) { + if std::env::var("NYASH_PARSER_ALLOW_SEMICOLON").ok().as_deref() == Some("1") { + if should_skip_newlines() { + *current += 1; + continue; + } + } + } + + break; + } +} + +/// 行継続判定(直前のトークンから判断) +fn is_line_continuation(prev_token: Option<&TokenType>) -> bool { + match prev_token { + Some(token) => matches!( + token, + TokenType::PLUS + | TokenType::MINUS + | TokenType::MULTIPLY + | TokenType::DIVIDE + | TokenType::MODULO + | TokenType::AND + | TokenType::OR + | TokenType::DOT + | TokenType::DoubleColon + | TokenType::COMMA + | TokenType::FatArrow + ), + None => false, + } +} + +/// 既存のParserUtilsトレイトを拡張 +pub trait EnhancedParserUtils { + /// 改良版advance(改行自動処理) + fn advance_smart(&mut self); + + /// 式コンテキストでパース + fn parse_in_expr_context(&mut self, f: F) -> T + where + F: FnOnce(&mut Self) -> T; + + /// ブロックコンテキストでパース + fn parse_in_block_context(&mut self, f: F) -> T + where + F: FnOnce(&mut Self) -> T; +} \ No newline at end of file diff --git a/src/parser/statements.rs b/src/parser/statements.rs index 0b97cd94..dda2623c 100644 --- a/src/parser/statements.rs +++ b/src/parser/statements.rs @@ -17,9 +17,6 @@ impl NyashParser { // Parse the block body first let try_body = self.parse_block_statements()?; - // Allow whitespace/newlines between block and postfix keywords - self.skip_newlines(); - if crate::config::env::block_postfix_catch() && (self.match_token(&TokenType::CATCH) || self.match_token(&TokenType::CLEANUP)) { @@ -39,7 +36,6 @@ impl NyashParser { }); // Single‑catch policy (MVP): disallow multiple catch in postfix form - self.skip_newlines(); if self.match_token(&TokenType::CATCH) { let line = self.current_token().line; return Err(ParseError::UnexpectedToken { @@ -88,7 +84,6 @@ impl NyashParser { 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()?); } @@ -473,8 +468,6 @@ impl NyashParser { /// return文をパース pub(super) fn parse_return(&mut self) -> Result { self.advance(); // consume 'return' - // 許容: 改行をスキップしてから式有無を判定 - self.skip_newlines(); // returnの後に式があるかチェック(RBRACE/EOFなら値なし) let value = if self.is_at_end() || self.match_token(&TokenType::RBRACE) { None