Files
hakorune/docs/development/strategies/newline-handling-strategy.md
Selfhosting Dev 75b42bbff5 feat: 改行処理Phase 0 Quick Fix完了 - 複数行match式完全対応
- primary.rsに3箇所のskip_newlines()追加(COLON前後、COMMA判定前)
- match_expr.rsのis_object_literal()を改行対応(lookahead改良)
- セミコロンモード確認(NYASH_PARSER_ALLOW_SEMICOLON=1)
- テストケース全て成功(NYASH_SYNTAX_SUGAR_LEVEL=full必須)
- CLAUDE.md更新、改行処理戦略ドキュメント作成済み

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-23 10:14:53 +09:00

6.8 KiB
Raw Blame History

Nyash改行処理戦略

作成日: 2025-09-23 ステータス: 承認済み・実装開始

概要

Nyashパーサーにおける改行処理の統一戦略。現在のskip_newlines()散在問題を解決し、コンテキスト認識による自動改行処理を実現する。

現状の問題

1. skip_newlines()の散在

// 現状:各所で手動呼び出しが必要
fn parse_object_literal() {
    self.skip_newlines();  // ここにも
    self.consume(TokenType::COLON)?;
    self.skip_newlines();  // あそこにも
    let value = self.parse_expression()?;
    self.skip_newlines();  // どこにでも
}

2. 複数行構文のパースエラー

// これがパースできない
local result = match "test" {
    "test" => {
        value: 42,  // ← 改行でエラー
        name: "answer"
    }
}

3. 保守性の問題

  • 新しい構文追加時にskip_newlines()の配置を忘れやすい
  • 一貫性のない改行処理
  • デバッグが困難

設計原則

1. セミコロンオプショナル

  • 改行もセミコロンも文の区切りとして扱う
  • ユーザーは好みのスタイルを選択可能

2. コンテキスト認識

  • {}, [], ()内では改行を自動的に無視
  • 式コンテキストでは改行をスキップ
  • 文コンテキストでは改行を文区切りとして扱う

3. 環境変数地獄の回避

  • コンテキストベースの制御を優先
  • 環境変数は互換性のためのみに限定

実装戦略3段階

Phase 0: Quick Fix即座実装

目的: 緊急対応として最小限の修正で問題を解決

// 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()散在を根絶

// 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<F, T>(&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()
    }
}

使用例:

fn parse_expression(cursor: &mut TokenCursor) -> Result<Expr> {
    cursor.with_expr_mode(|c| {
        // この中では改行が自動的にスキップされる
        parse_binary_expr(c, 0)
    })
}

期間: 1週間 効果:

  • 改行処理の一元化
  • 新規構文追加時のミス防止
  • デバッグの容易化

Phase 2: LASI前処理将来実装

目的: トークンレベルで改行を正規化し、パーサーを単純化

// トークン正規化層
fn normalize_tokens(tokens: Vec<Token>) -> Vec<Token> {
    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ハザードと同様の考え方

テストケース

基本ケース

// 2文として解釈
a = 1
b = 2

// 1文として解釈行継続
a = 1 +
    2

// セミコロンも可
a = 1; b = 2

括弧内改行

// すべてOK
f(
    arg1,
    arg2
)

[
    1,
    2,
    3
]

{
    key1: value1,
    key2: value2
}

match式

local result = match value {
    "test" => {
        name: "foo",
        value: 42
    },
    _ => {
        name: "default",
        value: 0
    }
}

returnハザード

return   // returnのみ
    42   // 別の文として解釈

return 42  // return 42として解釈

影響分析

後方互換性

  • Phase 0: 完全互換
  • Phase 1: 完全互換(内部実装の変更のみ)
  • Phase 2: セミコロン使用時のみ互換性確認必要

パフォーマンス

  • Phase 0: 影響なし
  • Phase 1: わずかなオーバーヘッド(カーソル管理)
  • Phase 2: 前処理により若干の初期化コスト

参考:他言語の実装

Go

  • トークナイザレベルでセミコロン自動挿入
  • 特定トークン後に改行があれば;を挿入

JavaScript

  • ASIAutomatic 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前処理検討開始

関連ドキュメント

承認

  • ChatGPT Pro: 技術分析・実装提案
  • Claude: 実装戦略決定・ドキュメント化
  • 実装開始: 2025-09-23