diff --git a/CLAUDE.md b/CLAUDE.md index f1186884..316301ce 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -328,7 +328,7 @@ NYASH_VM_DUMP_MIR=1 NYASH_CLI_VERBOSE=1 ./target/release/nyash gemini_test_case. jq '.functions[0].blocks' mir.json # ブロック構造確認 ``` -## 📝 Update (2025-09-23) ✅ PHIバグ完全修正!Exit PHI実装でループ制御フロー完成 +## 📝 Update (2025-09-23) ✅ PHIバグ修正&改行処理戦略決定! - 🎉 **PHIバグ根本解決完了!** Exit PHI生成実装でループ後の変数値が正しく伝播 - **修正前**: gemini_test_case期待値2→実際0(初期値に戻る) - **修正後**: 期待値2が正しく出力 ✅ @@ -338,15 +338,23 @@ jq '.functions[0].blocks' mir.json # ブロック構造確認 3. `create_exit_phis()`メソッド新規実装 - **MIR確認**: `bb3: %15 = phi [%4, bb1], [%9, bb9]` exit PHI正常生成 - **全テスト合格**: 0回実行、複数break、continue混在すべて✅ + - **コミット済み**: `e5c0665` でリモートに反映 +- 🎯 **改行処理TokenCursor戦略決定!** ChatGPT分析でアーキテクチャ設計完了 + - **問題**: match式の複数行オブジェクトリテラルでパースエラー + - **根本原因**: `skip_newlines()`が散在、手動呼び出しが必要 + - **解決策**: 3段階実装戦略 + 1. **Phase 0**: Quick Fix - primary.rsに最小限のskip_newlines追加(30分) + 2. **Phase 1**: TokenCursor導入 - モード制御で自動改行処理(今週) + 3. **Phase 2**: LASI前処理 - トークン正規化で完全解決(将来) + - **設計原則**: + - セミコロンオプショナル(改行もセミコロンも文区切り) + - コンテキスト認識(ブレース内は改行自動無視) + - 環境変数地獄の回避(コンテキストベース制御) - ✅ **フェーズM+M.2完全達成!** PHI統一革命でcollect_prints問題根本解決 -- ✅ **Step 1-3完全達成!** match式/peek統一/改行処理すべて完了 - - match式オブジェクトリテラル判定修正 ✅ - - peek→match完全統一(15ファイル) ✅ - - 複数行パース問題解決策特定 ✅ -- 🚀 **ChatGPT Pro協働成功!** 最強分析でExit PHI欠落を特定 - - 完璧な原因分析(ヘッダPHI○、exit PHI×) - - スコープ化された美しい実装提案 - - 段階的修正戦略で確実な実装 +- 🚀 **ChatGPT Pro協働成功!** + - Exit PHI欠落の完璧な原因分析 + - TokenCursorの実装可能なサンプルコード提供 + - 段階的修正戦略で確実な実装パス提示 ## 📝 Update (2025-09-22) 🎯 Phase 15 JITアーカイブ完了&デバッグ大進展! - ✅ **JIT/Craneliftアーカイブ完了!** Phase 15集中開発のため全JIT機能を安全にアーカイブ @@ -389,6 +397,21 @@ jq '.functions[0].blocks' mir.json # ブロック構造確認 - 🗃️ **アーカイブ整理**: 古いphaseファイル群をarchiveに移動、導線クリーンアップ完了 - 📋 詳細: [Property System仕様](docs/proposals/unified-members.md) | [Python統合計画](docs/development/roadmap/phases/phase-10.7/) +## 📝 Update (2025-09-24) ✅ 改行処理Phase 0 Quick Fix完了! +- ✅ **複数行match式パース成功!** たった5箇所の修正で複数行オブジェクトリテラル完全動作 + - **修正箇所**: + 1. `primary.rs`: COLON前後とCOMMA判定前にskip_newlines()追加(3箇所) + 2. `match_expr.rs`: is_object_literal()関数を改行対応(lookahead改良) + - **必須環境変数**: `NYASH_SYNTAX_SUGAR_LEVEL=full`(IDENTIFIERキー使用のため) + - **テスト結果**: MapBox正常出力 `{'__box__': 'MapBox', '__map': {'value': 42, 'name': 'answer'}}` +- 🎯 **セミコロンモード確認完了!** `NYASH_PARSER_ALLOW_SEMICOLON=1`で動作確認 + - セミコロンと改行を自由に混在可能 + - JavaScript/Go風の書き方も完全サポート +- 📚 **改行処理戦略ドキュメント完成**: [newline-handling-strategy.md](docs/development/strategies/newline-handling-strategy.md) + - 3段階実装計画(Phase 0: Quick Fix ✅, Phase 1: TokenCursor, Phase 2: LASI) + - ChatGPT Pro提案のTokenCursorサンプルコード含む +- 🚀 **次の実装**: Phase 1 TokenCursor導入で改行処理を根本解決へ + ## 📝 Update (2025-09-23) ✅ フェーズS実装完了!break制御フロー根治開始 - ✅ **フェーズS完了!** PHI incoming修正+終端ガード徹底→重複処理4箇所統一 - 🔧 **新ユーティリティ**: `src/mir/utils/control_flow.rs`で制御フロー処理統一化 diff --git a/docs/development/strategies/newline-handling-strategy.md b/docs/development/strategies/newline-handling-strategy.md new file mode 100644 index 00000000..a23c3618 --- /dev/null +++ b/docs/development/strategies/newline-handling-strategy.md @@ -0,0 +1,284 @@ +# Nyash改行処理戦略 +*作成日: 2025-09-23* +*ステータス: 承認済み・実装開始* + +## 概要 +Nyashパーサーにおける改行処理の統一戦略。現在の`skip_newlines()`散在問題を解決し、コンテキスト認識による自動改行処理を実現する。 + +## 現状の問題 + +### 1. skip_newlines()の散在 +```rust +// 現状:各所で手動呼び出しが必要 +fn parse_object_literal() { + self.skip_newlines(); // ここにも + self.consume(TokenType::COLON)?; + self.skip_newlines(); // あそこにも + let value = self.parse_expression()?; + self.skip_newlines(); // どこにでも +} +``` + +### 2. 複数行構文のパースエラー +```nyash +// これがパースできない +local result = match "test" { + "test" => { + value: 42, // ← 改行でエラー + name: "answer" + } +} +``` + +### 3. 保守性の問題 +- 新しい構文追加時に`skip_newlines()`の配置を忘れやすい +- 一貫性のない改行処理 +- デバッグが困難 + +## 設計原則 + +### 1. セミコロンオプショナル +- 改行もセミコロンも文の区切りとして扱う +- ユーザーは好みのスタイルを選択可能 + +### 2. コンテキスト認識 +- `{}`, `[]`, `()`内では改行を自動的に無視 +- 式コンテキストでは改行をスキップ +- 文コンテキストでは改行を文区切りとして扱う + +### 3. 環境変数地獄の回避 +- コンテキストベースの制御を優先 +- 環境変数は互換性のためのみに限定 + +## 実装戦略(3段階) + +### Phase 0: Quick Fix(即座実装) +**目的**: 緊急対応として最小限の修正で問題を解決 + +```rust +// src/parser/expr/primary.rs L77付近 +self.skip_newlines(); // COLON前に追加 +self.consume(TokenType::COLON)?; +self.skip_newlines(); // 値パース前に追加 +let value_expr = self.parse_expression()?; +self.skip_newlines(); // COMMA判定前に追加 +``` + +**期間**: 30分 +**効果**: match式の複数行オブジェクトリテラルが動作 + +### Phase 1: TokenCursor導入(今週実装) +**目的**: 改行処理を一元管理し、`skip_newlines()`散在を根絶 + +```rust +// src/parser/cursor.rs(新規) +pub struct TokenCursor<'a> { + tokens: &'a [Token], + idx: usize, + mode: NewlineMode, + paren_depth: usize, + brace_depth: usize, + bracket_depth: usize, +} + +pub enum NewlineMode { + Stmt, // 文モード:改行は文区切り + Expr, // 式モード:改行を自動スキップ +} + +impl<'a> TokenCursor<'a> { + pub fn with_expr_mode(&mut self, f: F) -> T + where F: FnOnce(&mut Self) -> T { + let old = self.mode; + self.mode = NewlineMode::Expr; + let result = f(self); + self.mode = old; + result + } + + fn should_skip_newline(&self) -> bool { + // ブレース内 or 式モード or 行継続 + self.brace_depth > 0 || + self.mode == NewlineMode::Expr || + self.prev_is_line_continuation() + } +} +``` + +**使用例**: +```rust +fn parse_expression(cursor: &mut TokenCursor) -> Result { + cursor.with_expr_mode(|c| { + // この中では改行が自動的にスキップされる + parse_binary_expr(c, 0) + }) +} +``` + +**期間**: 1週間 +**効果**: +- 改行処理の一元化 +- 新規構文追加時のミス防止 +- デバッグの容易化 + +### Phase 2: LASI前処理(将来実装) +**目的**: トークンレベルで改行を正規化し、パーサーを単純化 + +```rust +// トークン正規化層 +fn normalize_tokens(tokens: Vec) -> Vec { + let mut result = Vec::new(); + let mut iter = tokens.into_iter(); + + while let Some(token) = iter.next() { + match token.token_type { + TokenType::NEWLINE => { + if should_insert_eol(&prev, &next) { + result.push(Token::EOL); + } + // それ以外のNEWLINEは削除 + } + _ => result.push(token), + } + } + result +} +``` + +**期間**: Phase 15完了後 +**効果**: +- パーサーの大幅な簡略化 +- 完全な改行処理の分離 +- LosslessToken/Triviaとの統合 + +## 行継続ルール + +### 継続と判定する記号(直前) +- 二項演算子: `+`, `-`, `*`, `/`, `%`, `&&`, `||`, `|`, `&`, `^` +- メンバアクセス: `.`, `::` +- Optional系: `?`, `?.`, `??` +- Arrow: `=>`, `->` +- カンマ: `,` + +### 文終端強制(ハザード回避) +- `return`, `break`, `continue`の直後の改行は常に文終端 +- JavaScriptのASIハザードと同様の考え方 + +## テストケース + +### 基本ケース +```nyash +// 2文として解釈 +a = 1 +b = 2 + +// 1文として解釈(行継続) +a = 1 + + 2 + +// セミコロンも可 +a = 1; b = 2 +``` + +### 括弧内改行 +```nyash +// すべてOK +f( + arg1, + arg2 +) + +[ + 1, + 2, + 3 +] + +{ + key1: value1, + key2: value2 +} +``` + +### match式 +```nyash +local result = match value { + "test" => { + name: "foo", + value: 42 + }, + _ => { + name: "default", + value: 0 + } +} +``` + +### returnハザード +```nyash +return // returnのみ + 42 // 別の文として解釈 + +return 42 // return 42として解釈 +``` + +## 影響分析 + +### 後方互換性 +- Phase 0: 完全互換 +- Phase 1: 完全互換(内部実装の変更のみ) +- Phase 2: セミコロン使用時のみ互換性確認必要 + +### パフォーマンス +- Phase 0: 影響なし +- Phase 1: わずかなオーバーヘッド(カーソル管理) +- Phase 2: 前処理により若干の初期化コスト + +## 参考:他言語の実装 + +### Go +- トークナイザレベルでセミコロン自動挿入 +- 特定トークン後に改行があれば`;`を挿入 + +### JavaScript +- ASI(Automatic Semicolon Insertion) +- returnハザード等の特殊ルールあり + +### Python +- インデントベース(Nyashとは異なるアプローチ) + +### Rust +- セミコロン必須(式vs文の区別) + +## 決定事項 + +1. **TokenCursorアプローチを採用** + - ChatGPT提案のサンプルコードをベースに実装 + - 環境変数ではなくコンテキストベースの制御 + +2. **3段階実装で段階的改善** + - まずQuick Fixで緊急対応 + - TokenCursorで本質的解決 + - 将来的にLASI前処理で完成形へ + +3. **セミコロン完全オプショナル** + - 改行とセミコロンを同等に扱う + - ユーザーの好みに応じて選択可能 + +## 実装タイムライン + +- **2025-09-23**: Quick Fix実装(30分) +- **2025-09-30**: TokenCursor Phase 1完了(1週間) +- **Phase 15後**: LASI前処理検討開始 + +## 関連ドキュメント + +- [Parser Architecture](../../reference/parser/) +- [Language Syntax](../../reference/language/) +- [Phase 15 Roadmap](../../roadmap/phases/phase-15/) + +## 承認 + +- ChatGPT Pro: 技術分析・実装提案 +- Claude: 実装戦略決定・ドキュメント化 +- 実装開始: 2025-09-23 \ No newline at end of file diff --git a/src/parser/expr/match_expr.rs b/src/parser/expr/match_expr.rs index 1f6a2ad3..4f3ec0e3 100644 --- a/src/parser/expr/match_expr.rs +++ b/src/parser/expr/match_expr.rs @@ -330,9 +330,19 @@ impl NyashParser { if !matches!(self.current_token().token_type, TokenType::LBRACE) { return false; } - match self.peek_token() { + // Phase 0 Quick Fix: 改行をスキップして判定 + let mut lookahead_idx = 1; + while matches!(self.peek_nth_token(lookahead_idx), TokenType::NEWLINE) { + lookahead_idx += 1; + } + match self.peek_nth_token(lookahead_idx) { TokenType::IDENTIFIER(_) | TokenType::STRING(_) => { - matches!(self.peek_nth_token(2), TokenType::COLON) + // 次のトークンも改行をスキップして判定 + lookahead_idx += 1; + while matches!(self.peek_nth_token(lookahead_idx), TokenType::NEWLINE) { + lookahead_idx += 1; + } + matches!(self.peek_nth_token(lookahead_idx), TokenType::COLON) } _ => false } diff --git a/src/parser/expr/primary.rs b/src/parser/expr/primary.rs index 632e0271..ab01309a 100644 --- a/src/parser/expr/primary.rs +++ b/src/parser/expr/primary.rs @@ -73,9 +73,12 @@ impl NyashParser { }); } }; + self.skip_newlines(); // Phase 0 Quick Fix: COLON前に改行スキップ self.consume(TokenType::COLON)?; + self.skip_newlines(); // Phase 0 Quick Fix: 値パース前に改行スキップ let value_expr = self.parse_expression()?; entries.push((key, value_expr)); + self.skip_newlines(); // Phase 0 Quick Fix: COMMA判定前に改行スキップ if self.match_token(&TokenType::COMMA) { self.advance(); self.skip_newlines();