- 目標: (x = expr) を expression として受け入れる(値は右辺と同じ) - 箱化モジュール化パターン適用(Phase 133/134 継承): - AssignmentExprParser (Rust/Selfhost) - AssignmentExprLowering (MIR) - Rust/Selfhost パーサ両対応 - 影響: shortcircuit_and_phi_skip.hako が緑化 - 工数: 5-6 時間(優先度: 中) 箱化の利点: 責務分離、テスタビリティ向上、保守性向上 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
15 KiB
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(このファイル)
内容:
仕様の要点
-
構文定義:
assignment_expr := IDENT '=' expr factor := ... | '(' expr ')' | '(' assignment_expr ')' -
値・型:
(x = e)の値・型はeと同じ- 副作用:
xにeの値を代入
-
使える場所の例:
local y = (x = x + 1) // y と x が同じ値に if (x = next()) != null { } // 代入して条件判定 -
Gate:
- Rust:
parser_stage3_enabled()/NYASH_FEATURES=stage3 - Selfhost:
--stage3/NYASH_NY_COMPILER_STAGE3=1
- Rust:
-
非対象:
- 多段代入
x = (y = 1)は当面テストしない
- 多段代入
Task 2: Rust パーサ対応(箱化モジュール化)
目的: Rust パーサが Stage-3 ON のときだけ (x = expr) を expression として受け入れる
箱化モジュール設計
新規ファイル: src/parser/stage3/assignment_expr_parser.rs(~80行)
責務:
(の直後にIDENT '=' exprパターンを検出GroupedAssignmentExprAST ノード生成- Stage-3 gate 確認
実装パターン:
// 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 または類似
修正内容:
// 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
追加内容:
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行)
責務:
(の直後にIDENT '=' exprパターンを検出- selfhost AST に
GroupedAssignmentExpr相当ノード生成 - Stage-3 gate 確認
実装パターン:
// 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 または類似
修正内容:
// 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: Lowering(AST → MIR/JoinIR)箱化モジュール化
目的: GroupedAssignmentExpr を既存の代入文 + SSA 値返却として扱う
箱化モジュール設計
新規ファイル: src/mir/lowering/assignment_expr_lowering.rs(~120行)
責務:
GroupedAssignmentExprを検出- 既存 assignment statement ロジック流用
- SSA
ValueIdを式の結果として返す
実装パターン:
// 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
修正内容:
// 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 両方)
テストケース:
-
単純ケース:
apps/tests/assignment_expr_simple.hako(新規)static box Main { main() { local x = 0 local y = (x = x + 1) return y // 期待値: RC 1 } } -
短絡と組み合わせ:
apps/tests/assignment_expr_shortcircuit.hako(新規)static box Main { main() { local x = 0 if (x = 1) > 0 and true { return x // 期待値: RC 1 } return -1 } } -
問題再現ケース:
apps/tests/shortcircuit_and_phi_skip.hako(既存)- Phase 150 で FAIL だったものが PASS になること
テスト実行:
# 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
追加内容:
# 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 更新
更新対象:
-
phase152a_assignment_expr_design.md に実装結果追記:
## 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 構文の表現力向上 -
phase150_selfhost_stage3_depth1_results.md の Phase 152-A セクション更新:
### Phase 152-A: 括弧付き代入式対応 ✅ **根本原因**: Stage-3 パーサーが括弧内代入式未対応 **解決策**: 箱化モジュール化パターンで Rust/Selfhost 両対応 **影響ケース**: shortcircuit_and_phi_skip.hako が緑化 -
CURRENT_TASK.md に Phase 152-A 完了エントリ追加:
### 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 PASS(Rust/Selfhost 両方) **成果**: - shortcircuit_and_phi_skip.hako 緑化 - Stage-3 構文の表現力向上 - 箱化モジュール化パターン確立(Phase 133/134 継承) **次フェーズ**: Phase 152-B - Static method テスト整理 -
git commit で記録
✅ 完成チェックリスト(Phase 152-A)
- Task 1: 仕様ドキュメント作成
- 構文定義・値/型・使用例を明記
- Gate(Rust/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 2(Rust パーサ箱化): 1.5時間
- Task 3(Selfhost パーサ箱化): 1.5時間
- Task 4(Lowering 箱化): 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 テスト整理(予定)