Files
hakorune/docs/development/current/main/phase152a_assignment_expr_design.md

520 lines
15 KiB
Markdown
Raw Normal View History

# Phase 152-A: 括弧付き代入式 (x = x + 1) の Rust/Selfhost パーサ両対応
## 🎯 ゴール
**Stage-3 構文として括弧付き代入式を箱化モジュール化パターンで実装**
仕様:
- `x = expr` は statement のまま
- `(x = expr)` だけ expression として受け入れる(値は右辺と同じ)
- Rust パーサ/selfhost パーサの両方で対応
- `shortcircuit_and_phi_skip.hako` が selfhost/Rust 両方で動作
目的:
- Stage-3 構文の表現力向上
- Rust/Ny パーサの挙動統一
- **箱化モジュール化パターン適用**Phase 133/134-A/134-B 継承)
## 📋 スコープ(やること・やらないこと)
### ✅ やること
- Rust パーサStage-3の拡張
- `factor``'(' assignment_expr ')'` を追加
- **箱化**: `AssignmentExprParser` モジュール作成
- AST に `GroupedAssignmentExpr` ノード追加
- Selfhost パーサ(.hako 側)の拡張:
- Stage-3 受理時に同じ形を受理
- **箱化**: `assignment_expr_parser.hako` モジュール作成
- Lowering:
- **箱化**: `assignment_expr_lowering.rs` モジュール作成
- 既存 assignment statement ロジック流用 + SSA Value 返却
- テスト追加Rust/selfhost 両方)
### ❌ やらないこと
- `x = expr` を expression としてどこでも受け入れる拡張(文のまま)
- Stage-2 パーサや Stage-3 以外の profile の意味論変更
- C 互換の多段代入 (`x = y = 1`) は対象外
---
## 🏗️ 6 つのタスク
### Task 1: 仕様ドキュメント作成(設計確認)
**ファイル**: `docs/development/current/main/phase152a_assignment_expr_design.md`(このファイル)
**内容**:
#### 仕様の要点
1. **構文定義**:
```ebnf
assignment_expr := IDENT '=' expr
factor := ... | '(' expr ')' | '(' assignment_expr ')'
```
2. **値・型**:
- `(x = e)` の値・型は `e` と同じ
- 副作用: `x``e` の値を代入
3. **使える場所の例**:
```nyash
local y = (x = x + 1) // y と x が同じ値に
if (x = next()) != null { } // 代入して条件判定
```
4. **Gate**:
- **Rust**: `parser_stage3_enabled()` / `NYASH_FEATURES=stage3`
- **Selfhost**: `--stage3` / `NYASH_NY_COMPILER_STAGE3=1`
5. **非対象**:
- 多段代入 `x = (y = 1)` は当面テストしない
---
### Task 2: Rust パーサ対応(箱化モジュール化)
**目的**: Rust パーサが Stage-3 ON のときだけ `(x = expr)` を expression として受け入れる
#### 箱化モジュール設計
**新規ファイル**: `src/parser/stage3/assignment_expr_parser.rs`~80行
**責務**:
1. `(` の直後に `IDENT '=' expr` パターンを検出
2. `GroupedAssignmentExpr` AST ノード生成
3. Stage-3 gate 確認
**実装パターン**:
```rust
// src/parser/stage3/assignment_expr_parser.rs
pub struct AssignmentExprParser;
impl AssignmentExprParser {
/// Parse grouped assignment expression: (x = expr)
pub fn try_parse_grouped_assignment(
tokens: &mut TokenStream,
config: &ParserConfig,
) -> Option<AstNode> {
// Stage-3 gate check
if !config.is_stage3_enabled() {
return None;
}
// Look ahead: '(' IDENT '=' ...
if !Self::is_grouped_assignment_pattern(tokens) {
return None;
}
// Parse: '(' IDENT '=' expr ')'
tokens.expect(Token::LParen)?;
let ident = tokens.expect_identifier()?;
tokens.expect(Token::Assign)?;
let rhs = parse_expr(tokens, config)?;
tokens.expect(Token::RParen)?;
Some(AstNode::GroupedAssignmentExpr {
lhs: ident,
rhs: Box::new(rhs)
})
}
fn is_grouped_assignment_pattern(tokens: &TokenStream) -> bool {
tokens.peek() == Some(&Token::LParen) &&
tokens.peek_ahead(1).is_identifier() &&
tokens.peek_ahead(2) == Some(&Token::Assign)
}
}
```
#### 既存パーサへの統合
**修正ファイル**: `src/parser/stage3/factor.rs` または類似
**修正内容**:
```rust
// Before: factor parsing
fn parse_factor(tokens: &mut TokenStream, config: &ParserConfig) -> Result<AstNode> {
match tokens.peek() {
Some(Token::LParen) => {
// Try grouped assignment first (Stage-3 only)
if let Some(assignment) = AssignmentExprParser::try_parse_grouped_assignment(tokens, config) {
return Ok(assignment);
}
// Fallback: normal grouped expression
parse_grouped_expr(tokens, config)
}
// ... other factor cases
}
}
```
#### AST ノード追加
**修正ファイル**: `src/ast/mod.rs`
**追加内容**:
```rust
pub enum AstNode {
// ... existing nodes
/// Grouped assignment expression: (x = expr)
/// Value and type are same as rhs
GroupedAssignmentExpr {
lhs: String, // variable name
rhs: Box<AstNode>, // right-hand side expression
},
}
```
---
### Task 3: Selfhost パーサ対応(箱化モジュール化)
**目的**: Stage-3 selfhost コンパイラでも Rust パーサと同じ構文を受け入れる
#### 箱化モジュール設計
**新規ファイル**: `apps/lib/parser/assignment_expr_parser.hako`~100行
**責務**:
1. `(` の直後に `IDENT '=' expr` パターンを検出
2. selfhost AST に `GroupedAssignmentExpr` 相当ノード生成
3. Stage-3 gate 確認
**実装パターン**:
```nyash
// apps/lib/parser/assignment_expr_parser.hako
static box AssignmentExprParser {
/// Try parse grouped assignment: (x = expr)
tryParseGroupedAssignment(tokens, config) {
// Stage-3 gate check
if not config.isStage3Enabled() {
return null
}
// Look ahead: '(' IDENT '=' ...
if not me.isGroupedAssignmentPattern(tokens) {
return null
}
// Parse: '(' IDENT '=' expr ')'
me.expectToken(tokens, TOKEN_LPAREN)
local ident = me.expectIdentifier(tokens)
me.expectToken(tokens, TOKEN_ASSIGN)
local rhs = ExprParser.parseExpr(tokens, config)
me.expectToken(tokens, TOKEN_RPAREN)
return new GroupedAssignmentExprNode(ident, rhs)
}
isGroupedAssignmentPattern(tokens) {
return tokens.peek() == TOKEN_LPAREN and
tokens.peekAhead(1).isIdentifier() and
tokens.peekAhead(2) == TOKEN_ASSIGN
}
}
```
#### 既存 selfhost パーサへの統合
**修正ファイル**: `apps/lib/parser/factor_parser.hako` または類似
**修正内容**:
```nyash
// Before: factor parsing
static box FactorParser {
parseFactor(tokens, config) {
if tokens.peek() == TOKEN_LPAREN {
// Try grouped assignment first (Stage-3 only)
local assignment = AssignmentExprParser.tryParseGroupedAssignment(tokens, config)
if assignment != null {
return assignment
}
// Fallback: normal grouped expression
return me.parseGroupedExpr(tokens, config)
}
// ... other factor cases
}
}
```
---
### Task 4: LoweringAST → MIR/JoinIR箱化モジュール化
**目的**: `GroupedAssignmentExpr` を既存の代入文 + SSA 値返却として扱う
#### 箱化モジュール設計
**新規ファイル**: `src/mir/lowering/assignment_expr_lowering.rs`~120行
**責務**:
1. `GroupedAssignmentExpr` を検出
2. 既存 assignment statement ロジック流用
3. SSA `ValueId` を式の結果として返す
**実装パターン**:
```rust
// src/mir/lowering/assignment_expr_lowering.rs
pub struct AssignmentExprLowering;
impl AssignmentExprLowering {
/// Lower grouped assignment expression: (x = expr)
///
/// Returns SSA ValueId representing the assigned value
pub fn lower_grouped_assignment(
builder: &mut MirBuilder,
lhs: &str,
rhs: &AstNode,
) -> Result<ValueId> {
// 1. Evaluate rhs expression
let rhs_value = builder.lower_expr(rhs)?;
// 2. Assign to lhs variable (reuse existing assignment logic)
builder.lower_assignment(lhs, rhs_value)?;
// 3. Return the same SSA value as expression result
Ok(rhs_value)
}
}
```
#### 既存 lowering への統合
**修正ファイル**: `src/mir/lowering/expr.rs`
**修正内容**:
```rust
// Before: expression lowering
fn lower_expr(builder: &mut MirBuilder, ast: &AstNode) -> Result<ValueId> {
match ast {
// ... existing expression cases
AstNode::GroupedAssignmentExpr { lhs, rhs } => {
// Delegate to assignment expression lowering module
AssignmentExprLowering::lower_grouped_assignment(builder, lhs, rhs)
}
// ... other cases
}
}
```
---
### Task 5: テスト追加Rust/Selfhost 両方)
**テストケース**:
1. **単純ケース**: `apps/tests/assignment_expr_simple.hako`(新規)
```nyash
static box Main {
main() {
local x = 0
local y = (x = x + 1)
return y // 期待値: RC 1
}
}
```
2. **短絡と組み合わせ**: `apps/tests/assignment_expr_shortcircuit.hako`(新規)
```nyash
static box Main {
main() {
local x = 0
if (x = 1) > 0 and true {
return x // 期待値: RC 1
}
return -1
}
}
```
3. **問題再現ケース**: `apps/tests/shortcircuit_and_phi_skip.hako`(既存)
- Phase 150 で FAIL だったものが PASS になること
**テスト実行**:
```bash
# Rust パーサ直接実行Stage-3 ON
NYASH_FEATURES=stage3 ./target/release/hakorune apps/tests/assignment_expr_simple.hako
# Selfhost 経路Stage-3 ON
NYASH_FEATURES=stage3 NYASH_USE_NY_COMPILER=1 NYASH_JOINIR_STRICT=1 \
./target/release/hakorune apps/tests/assignment_expr_simple.hako
# Stage-3 OFF でエラー確認(既存動作維持)
./target/release/hakorune apps/tests/assignment_expr_simple.hako
# 期待: "Unexpected ASSIGN" エラー
```
**スモークテスト更新**:
**修正ファイル**: `tools/smokes/v2/profiles/integration/selfhost_phase150_depth1_smoke.sh`
**追加内容**:
```bash
# Add new test cases
CANDIDATES=(
"apps/tests/peek_expr_block.hako"
"apps/tests/loop_min_while.hako"
# ... existing cases
"apps/tests/assignment_expr_simple.hako" # NEW
"apps/tests/assignment_expr_shortcircuit.hako" # NEW
"apps/tests/shortcircuit_and_phi_skip.hako" # FIXED
)
```
---
### Task 6: ドキュメント・CURRENT_TASK 更新
**更新対象**:
1. **phase152a_assignment_expr_design.md** に実装結果追記:
```markdown
## Phase 152-A 実装結果
### 箱化モジュール作成Phase 133/134-A/134-B パターン継承)
**Rust 側**:
- `src/parser/stage3/assignment_expr_parser.rs` (+80行) - パーサー箱化
- `src/mir/lowering/assignment_expr_lowering.rs` (+120行) - Lowering 箱化
- `src/ast/mod.rs` (+10行) - AST ノード追加
**Selfhost 側**:
- `apps/lib/parser/assignment_expr_parser.hako` (+100行) - パーサー箱化
### テスト結果
- Rust パーサ: 3/3 PASS
- Selfhost パーサ: 3/3 PASS
- shortcircuit_and_phi_skip.hako: ✅ 修正完了
### 成果
- 括弧付き代入式の箱化モジュール化完成
- Rust/Selfhost パーサ挙動統一
- Stage-3 構文の表現力向上
```
2. **phase150_selfhost_stage3_depth1_results.md** の Phase 152-A セクション更新:
```markdown
### Phase 152-A: 括弧付き代入式対応 ✅
**根本原因**: Stage-3 パーサーが括弧内代入式未対応
**解決策**: 箱化モジュール化パターンで Rust/Selfhost 両対応
**影響ケース**: shortcircuit_and_phi_skip.hako が緑化
```
3. **CURRENT_TASK.md** に Phase 152-A 完了エントリ追加:
```markdown
### Phase 152-A: 括弧付き代入式Rust/Selfhost パーサ両対応)✅
**完了内容**:
- 括弧付き代入式 `(x = expr)` の仕様確定
- 箱化モジュール化パターン適用:
- AssignmentExprParser (Rust/Selfhost)
- AssignmentExprLowering (MIR)
- Rust/Selfhost パーサ挙動統一
**修正ファイル**:
- src/parser/stage3/assignment_expr_parser.rs (+80行)
- src/mir/lowering/assignment_expr_lowering.rs (+120行)
- apps/lib/parser/assignment_expr_parser.hako (+100行)
**テスト結果**: 3/3 PASSRust/Selfhost 両方)
**成果**:
- shortcircuit_and_phi_skip.hako 緑化
- Stage-3 構文の表現力向上
- 箱化モジュール化パターン確立Phase 133/134 継承)
**次フェーズ**: Phase 152-B - Static method テスト整理
```
4. **git commit で記録**
---
## ✅ 完成チェックリストPhase 152-A
- [ ] Task 1: 仕様ドキュメント作成
- [ ] 構文定義・値/型・使用例を明記
- [ ] GateRust/Selfhostを明記
- [ ] Task 2: Rust パーサ箱化モジュール実装
- [ ] AssignmentExprParser 作成(~80行
- [ ] factor.rs への統合1行委譲
- [ ] AST ノード追加
- [ ] Task 3: Selfhost パーサ箱化モジュール実装
- [ ] assignment_expr_parser.hako 作成(~100行
- [ ] factor_parser.hako への統合
- [ ] Task 4: Lowering 箱化モジュール実装
- [ ] AssignmentExprLowering 作成(~120行
- [ ] expr.rs への統合1行委譲
- [ ] Task 5: テスト追加・実行
- [ ] assignment_expr_simple.hako: ✅ PASS
- [ ] assignment_expr_shortcircuit.hako: ✅ PASS
- [ ] shortcircuit_and_phi_skip.hako: ✅ PASS
- [ ] スモークテスト更新
- [ ] Task 6: ドキュメント更新
- [ ] phase152a_assignment_expr_design.md 完成
- [ ] phase150_selfhost_stage3_depth1_results.md 更新
- [ ] CURRENT_TASK.md 更新
- [ ] git commit で記録
---
## 所要時間
**5-6 時間程度**
- Task 1仕様ドキュメント: 30分
- Task 2Rust パーサ箱化): 1.5時間
- Task 3Selfhost パーサ箱化): 1.5時間
- Task 4Lowering 箱化): 1時間
- Task 5テスト追加・実行: 1時間
- Task 6ドキュメント: 30分
---
## 箱化モジュール化パターン継承
Phase 152-A は **Phase 133/134-A/134-B で確立した箱化モジュール化パターン**を継承:
| Phase | 箱化対象 | 専用モジュール | 統合先 |
|-------|---------|--------------|--------|
| 133 | ConsoleBox methods | `console_bridge.py` | `boxcall.py` (1行委譲) |
| 134-A | MIR Call | `mir_call/*.py` | `llvm_builder.py` (1行委譲) |
| 134-B | StringBox methods | `stringbox.py` | `boxcall.py` (1行委譲) |
| **152-A** | **Assignment Expr** | **`assignment_expr_parser.rs/hako`** | **`factor.rs` (1行委譲)** |
**箱化の利点**:
- 責務分離(パーサ/Lowering が肥大化しない)
- テスタビリティ向上(単体テスト可能)
- 保守性向上(変更影響範囲が明確)
- Rust/Selfhost 対応統一(同じパターン適用)
---
## 次のステップ
**Phase 152-B: Static method テスト整理**
- `stage1_run_min.hako` を static box スタイルに書き換え
- legacy `static method` 構文を削除
---
## 進捗
- ✅ Phase 130-134: LLVM Python バックエンド整理
- ✅ Phase 150: Selfhost Stage-3 Depth-1 ベースライン強化
- ✅ Phase 151: ConsoleBox Selfhost Support
- 🎯 Phase 152-A: 括弧付き代入式Rust/Selfhost パーサ両対応)(← **現在のフェーズ**
- 📋 Phase 152-B: Static method テスト整理(予定)