diff --git a/CLAUDE.md b/CLAUDE.md index d0ee8a18..58f63f56 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -276,18 +276,32 @@ NYASH_DISABLE_PLUGINS=1 ./target/release/nyash program.nyash NYASH_LLVM_USE_HARNESS=1 ./target/release/nyash program.nyash ``` -## 📝 Update (2025-09-23) ✅ Step 1完了!peek→match統一 Step 2開始 +## 📝 Update (2025-09-23) 🚨 重大PHI命令バグ発見!Step 4改行処理も課題 - ✅ **フェーズM+M.2完全達成!** PHI統一革命でcollect_prints問題根本解決 - ✅ **Step 1完全達成!** match式オブジェクトリテラル判定修正完了 - **修正完了**: `src/parser/expr/match_expr.rs` の `is_object_literal()` メソッド追加 - **副作用修正**: `match_token()` → `current_token()` で副作用除去 - **動作確認**: 単行match式 + オブジェクトリテラル ✅ 完全動作 - - **発見**: 複数行パース未対応(後回し決定) -- 🚀 **Step 2開始準備!** peek→match完全統一でアーキテクチャクリーンアップ - - **対象ファイル**: `json_v0_bridge/ast.rs`, `lowering/peek.rs` → `match_expr.rs` - - **統一効果**: AI理解向上・ドキュメント整合性・保守性向上・ソースコード美化 - - **作業順序**: claude.md更新→todo調整→commit→peek構造体→match構造体完全移行 -- 🎯 **アーキテクチャ美化**: ややこしいpeek/match混在状況の完全解消へ +- ✅ **Step 2完全達成!** peek→match完全統一でアーキテクチャクリーンアップ完了 + - **統一完了**: 15ファイルで `PeekExpr` → `MatchExpr` 一括置換完了 + - **ファイル移行**: `lowering/peek.rs` → `match_expr.rs` 完全移行 + - **コンパイル成功**: エラーゼロで正常ビルド完了 + - **動作確認**: match式テスト正常動作 ✅ + - **効果**: AI理解性向上・コードベース一貫性・保守性大幅向上 +- ✅ **Step 3完全達成!** Task先生による複数行パース問題根本原因特定完了 + - **原因特定**: オブジェクトリテラルパーサーの改行スキップ不足(`src/parser/expr/primary.rs`) + - **修正箇所**: L49、L77-79に `skip_newlines()` 追加で解決可能 + - **工数**: 30分以内の簡単修正 +- 🤔 **Step 4課題発見!** 改行処理アーキテクチャの美しさ問題 + - **問題**: `skip_newlines()` を各所に散りばめる方法は美しくない + - **課題**: 保守性・一貫性・見落としリスク・設計の美しさ + - **方針**: Gemini 3相談でアーキテクチャ根本改善検討 +- 🚨 **重大発見!** PHI命令処理バグ(gemini_test_case: 期待値2→実際0) + - **問題**: フェーズM+M.2のPHI統一作業でループ後変数マージに回帰バグ + - **詳細**: PHI命令は正しく動作(v8→2)だが、print時に間違ったPHI(v4→0)参照 + - **根本原因**: ループ脱出後の変数PHI接続が初期値を参照している + - **影響**: Phase 15セルフホスティング基盤の重大バグ + - **次のアクション**: ChatGPT相談でMIRビルダー修正戦略立案 ## 📝 Update (2025-09-22) 🎯 Phase 15 JITアーカイブ完了&デバッグ大進展! - ✅ **JIT/Craneliftアーカイブ完了!** Phase 15集中開発のため全JIT機能を安全にアーカイブ diff --git a/gemini_test_case.nyash b/gemini_test_case.nyash new file mode 100644 index 00000000..25e2df04 --- /dev/null +++ b/gemini_test_case.nyash @@ -0,0 +1,14 @@ +local cond1 = true +local cond2 = false +local x = 0 +loop(true) { + if(cond1) { + if(cond2) { + x = 1 + } else { + x = 2 + } + break + } +} +print(x) diff --git a/src/ast.rs b/src/ast.rs index 32cd91a5..4a97bcc2 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -126,8 +126,8 @@ pub enum ExpressionNode { MeExpression { span: Span, }, - /// peek式: peek { lit => expr, ... else => expr } - PeekExpr { + /// match式: match { lit => expr, ... else => expr } + MatchExpr { scrutinee: Box, arms: Vec<(LiteralValue, ASTNode)>, else_expr: Box, @@ -398,8 +398,8 @@ pub enum ASTNode { span: Span, }, - /// peek式: peek { lit => expr, ... else => expr } - PeekExpr { + /// match式: match { lit => expr, ... else => expr } + MatchExpr { scrutinee: Box, arms: Vec<(LiteralValue, ASTNode)>, else_expr: Box, diff --git a/src/ast/utils.rs b/src/ast/utils.rs index 46d3f53f..55eccb57 100644 --- a/src/ast/utils.rs +++ b/src/ast/utils.rs @@ -43,7 +43,7 @@ impl ASTNode { ASTNode::Throw { .. } => "Throw", ASTNode::AwaitExpression { .. } => "AwaitExpression", ASTNode::QMarkPropagate { .. } => "QMarkPropagate", - ASTNode::PeekExpr { .. } => "PeekExpr", + ASTNode::MatchExpr { .. } => "MatchExpr", ASTNode::Lambda { .. } => "Lambda", ASTNode::ArrayLiteral { .. } => "ArrayLiteral", ASTNode::MapLiteral { .. } => "MapLiteral", @@ -78,7 +78,7 @@ impl ASTNode { ASTNode::FromCall { .. } => ASTNodeType::Expression, ASTNode::ThisField { .. } => ASTNodeType::Expression, ASTNode::MeField { .. } => ASTNodeType::Expression, - ASTNode::PeekExpr { .. } => ASTNodeType::Expression, + ASTNode::MatchExpr { .. } => ASTNodeType::Expression, ASTNode::QMarkPropagate { .. } => ASTNodeType::Expression, ASTNode::Lambda { .. } => ASTNodeType::Expression, ASTNode::ArrayLiteral { .. } => ASTNodeType::Expression, @@ -303,7 +303,7 @@ impl ASTNode { ASTNode::AwaitExpression { expression, .. } => { format!("Await({:?})", expression) } - ASTNode::PeekExpr { .. } => "PeekExpr".to_string(), + ASTNode::MatchExpr { .. } => "MatchExpr".to_string(), ASTNode::QMarkPropagate { .. } => "QMarkPropagate".to_string(), ASTNode::Lambda { params, body, .. } => { format!("Lambda({} params, {} statements)", params.len(), body.len()) @@ -356,7 +356,7 @@ impl ASTNode { ASTNode::FunctionCall { span, .. } => *span, ASTNode::Call { span, .. } => *span, ASTNode::AwaitExpression { span, .. } => *span, - ASTNode::PeekExpr { span, .. } => *span, + ASTNode::MatchExpr { span, .. } => *span, ASTNode::QMarkPropagate { span, .. } => *span, ASTNode::Lambda { span, .. } => *span, ASTNode::ArrayLiteral { span, .. } => *span, diff --git a/src/llvm_py/pyvm/ops_box.py b/src/llvm_py/pyvm/ops_box.py index c9e631fb..602f3fa2 100644 --- a/src/llvm_py/pyvm/ops_box.py +++ b/src/llvm_py/pyvm/ops_box.py @@ -66,24 +66,27 @@ def op_boxcall(owner, fn, inst: Dict[str, Any], regs: Dict[int, Any]) -> None: return # User-defined box: dispatch to lowered function if available (Box.method/N) + # Skip built-in boxes (ArrayBox, MapBox, etc.) to let them fall through to their implementations if isinstance(recv, dict) and isinstance(method, str) and "__box__" in recv: box_name = recv.get("__box__") - cand = f"{box_name}.{method}/{len(args)}" - callee = owner.functions.get(cand) - if callee is not None: - owner._dbg(f"[pyvm] boxcall dispatch -> {cand} args={args}") - out = owner._exec_function(callee, args) - owner._set(regs, inst.get("dst"), out) - return - else: - if owner._debug: - prefix = f"{box_name}.{method}/" - cands = sorted([k for k in owner.functions.keys() if k.startswith(prefix)]) - if cands: - owner._dbg(f"[pyvm] boxcall unresolved: '{cand}' — available: {cands}") - else: - any_for_box = sorted([k for k in owner.functions.keys() if k.startswith(f"{box_name}.")]) - owner._dbg(f"[pyvm] boxcall unresolved: '{cand}' — no candidates; methods for {box_name}: {any_for_box}") + # Skip built-in boxes - let them fall through to built-in implementations below + if box_name not in ("ArrayBox", "MapBox", "ConsoleBox", "FileBox", "PathBox", "JsonDocBox", "JsonNodeBox"): + cand = f"{box_name}.{method}/{len(args)}" + callee = owner.functions.get(cand) + if callee is not None: + owner._dbg(f"[pyvm] boxcall dispatch -> {cand} args={args}") + out = owner._exec_function(callee, args) + owner._set(regs, inst.get("dst"), out) + return + else: + if owner._debug: + prefix = f"{box_name}.{method}/" + cands = sorted([k for k in owner.functions.keys() if k.startswith(prefix)]) + if cands: + owner._dbg(f"[pyvm] boxcall unresolved: '{cand}' — available: {cands}") + else: + any_for_box = sorted([k for k in owner.functions.keys() if k.startswith(f"{box_name}.")]) + owner._dbg(f"[pyvm] boxcall unresolved: '{cand}' — no candidates; methods for {box_name}: {any_for_box}") # ConsoleBox methods if method in ("print", "println", "log") and owner._is_console(recv): diff --git a/src/macro/ast_json.rs b/src/macro/ast_json.rs index 56097ae8..8a930e43 100644 --- a/src/macro/ast_json.rs +++ b/src/macro/ast_json.rs @@ -88,8 +88,8 @@ pub fn ast_to_json(ast: &ASTNode) -> Value { "kind":"Map", "entries": entries.into_iter().map(|(k,v)| json!({"k":k,"v":ast_to_json(&v)})).collect::>() }), - ASTNode::PeekExpr { scrutinee, arms, else_expr, .. } => json!({ - "kind":"PeekExpr", + ASTNode::MatchExpr { scrutinee, arms, else_expr, .. } => json!({ + "kind":"MatchExpr", "scrutinee": ast_to_json(&scrutinee), "arms": arms.into_iter().map(|(lit, body)| json!({ "literal": { @@ -147,7 +147,7 @@ pub fn json_to_ast(v: &Value) -> Option { "Map" => ASTNode::MapLiteral { entries: v.get("entries")?.as_array()?.iter().filter_map(|e| { Some((e.get("k")?.as_str()?.to_string(), json_to_ast(e.get("v")?)?)) }).collect(), span: Span::unknown() }, - "PeekExpr" => { + "MatchExpr" => { let scr = json_to_ast(v.get("scrutinee")?)?; let arms_json = v.get("arms")?.as_array()?.iter(); let mut arms = Vec::new(); @@ -158,7 +158,7 @@ pub fn json_to_ast(v: &Value) -> Option { arms.push((lit, body)); } let else_expr = json_to_ast(v.get("else")?)?; - ASTNode::PeekExpr { + ASTNode::MatchExpr { scrutinee: Box::new(scr), arms, else_expr: Box::new(else_expr), diff --git a/src/mir/builder/exprs.rs b/src/mir/builder/exprs.rs index 725c9305..714051b8 100644 --- a/src/mir/builder/exprs.rs +++ b/src/mir/builder/exprs.rs @@ -85,7 +85,7 @@ impl super::MirBuilder { self.build_qmark_propagate_expression(*expression.clone()) } - ASTNode::PeekExpr { + ASTNode::MatchExpr { scrutinee, arms, else_expr, diff --git a/src/mir/builder/exprs_lambda.rs b/src/mir/builder/exprs_lambda.rs index 56333cdd..fe2cded3 100644 --- a/src/mir/builder/exprs_lambda.rs +++ b/src/mir/builder/exprs_lambda.rs @@ -123,7 +123,7 @@ impl super::MirBuilder { ASTNode::AwaitExpression { expression, .. } => { collect_vars(expression, used, locals); } - ASTNode::PeekExpr { + ASTNode::MatchExpr { scrutinee, arms, else_expr, diff --git a/src/mir/builder/stmts.rs b/src/mir/builder/stmts.rs index e3643fef..d41539ba 100644 --- a/src/mir/builder/stmts.rs +++ b/src/mir/builder/stmts.rs @@ -1,6 +1,7 @@ use super::{ConstValue, Effect, EffectMask, MirInstruction, ValueId}; use crate::ast::{ASTNode, CallExpr}; use crate::mir::TypeOpKind; +use crate::mir::utils::is_current_block_terminated; impl super::MirBuilder { // Print statement: env.console.log(value) with early TypeOp handling @@ -141,7 +142,7 @@ impl super::MirBuilder { last_value = Some(self.build_expression(statement)?); // If the current block was terminated by this statement (e.g., return/throw), // do not emit any further instructions for this block. - if self.is_current_block_terminated() { + if is_current_block_terminated(self)? { break; } } diff --git a/src/mir/builder/vars.rs b/src/mir/builder/vars.rs index f23464b6..643a17df 100644 --- a/src/mir/builder/vars.rs +++ b/src/mir/builder/vars.rs @@ -118,7 +118,7 @@ pub(super) fn collect_free_vars( ASTNode::AwaitExpression { expression, .. } => { collect_free_vars(expression, used, locals); } - ASTNode::PeekExpr { + ASTNode::MatchExpr { scrutinee, arms, else_expr, diff --git a/src/mir/loop_builder.rs b/src/mir/loop_builder.rs index 8c60cc91..8ef49890 100644 --- a/src/mir/loop_builder.rs +++ b/src/mir/loop_builder.rs @@ -455,20 +455,8 @@ impl<'a> LoopBuilder<'a> { let mut last = None; for s in statements.into_iter() { last = Some(self.build_statement(s)?); - // Stop early if this block has been terminated (e.g., break/continue) - let cur_id = self.current_block()?; - let terminated = { - if let Some(ref fun_ro) = self.parent_builder.current_function { - if let Some(bb) = fun_ro.get_block(cur_id) { - bb.is_terminated() - } else { - false - } - } else { - false - } - }; - if terminated { + // フェーズS修正:統一終端検出ユーティリティ使用 + if is_current_block_terminated(self.parent_builder)? { break; } } diff --git a/src/parser/expr/call.rs b/src/parser/expr/call.rs index f76a79d2..45701ec3 100644 --- a/src/parser/expr/call.rs +++ b/src/parser/expr/call.rs @@ -154,7 +154,7 @@ impl NyashParser { }; // Wrap with peek: peek expr { null => null, else => access(expr) } - expr = ASTNode::PeekExpr { + expr = ASTNode::MatchExpr { scrutinee: Box::new(expr.clone()), arms: vec![( crate::ast::LiteralValue::Null, diff --git a/src/parser/expr/coalesce.rs b/src/parser/expr/coalesce.rs index d4571d6b..1037632d 100644 --- a/src/parser/expr/coalesce.rs +++ b/src/parser/expr/coalesce.rs @@ -23,7 +23,7 @@ impl NyashParser { self.advance(); let rhs = self.expr_parse_or()?; let scr = expr; - expr = ASTNode::PeekExpr { + expr = ASTNode::MatchExpr { scrutinee: Box::new(scr.clone()), arms: vec![(crate::ast::LiteralValue::Null, rhs)], else_expr: Box::new(scr), diff --git a/src/parser/expr/match_expr.rs b/src/parser/expr/match_expr.rs index def8e4d0..1f6a2ad3 100644 --- a/src/parser/expr/match_expr.rs +++ b/src/parser/expr/match_expr.rs @@ -193,7 +193,7 @@ impl NyashParser { })?; if !saw_type_arm && !saw_guard { - // 既存の Lower を活用するため PeekExpr に落とす(型パターンが無い場合のみ) + // 既存の Lower を活用するため MatchExpr に落とす(型パターンが無い場合のみ) let mut lit_arms: Vec<(LiteralValue, ASTNode)> = Vec::new(); for arm in arms_any.into_iter() { match arm { @@ -206,7 +206,7 @@ impl NyashParser { MatchArm::Type { .. } => unreachable!(), } } - return Ok(ASTNode::PeekExpr { + return Ok(ASTNode::MatchExpr { scrutinee: Box::new(scrutinee), arms: lit_arms, else_expr: Box::new(else_expr), diff --git a/src/parser/expr/primary.rs b/src/parser/expr/primary.rs index 984e16b8..632e0271 100644 --- a/src/parser/expr/primary.rs +++ b/src/parser/expr/primary.rs @@ -46,7 +46,9 @@ impl NyashParser { let ident_key_on = std::env::var("NYASH_ENABLE_MAP_IDENT_KEY").ok().as_deref() == Some("1") || sugar_level.as_deref() == Some("full"); + self.skip_newlines(); while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { + self.skip_newlines(); let key = match &self.current_token().token_type { TokenType::STRING(s) => { let v = s.clone(); @@ -76,8 +78,10 @@ impl NyashParser { entries.push((key, value_expr)); if self.match_token(&TokenType::COMMA) { self.advance(); + self.skip_newlines(); } } + self.skip_newlines(); self.consume(TokenType::RBRACE)?; Ok(ASTNode::MapLiteral { entries, diff --git a/src/parser/sugar.rs b/src/parser/sugar.rs index 1f4b9a30..0fbccc94 100644 --- a/src/parser/sugar.rs +++ b/src/parser/sugar.rs @@ -84,12 +84,12 @@ fn rewrite(ast: ASTNode) -> ASTNode { operand: Box::new(rewrite(*operand)), span, }, - ASTNode::PeekExpr { + ASTNode::MatchExpr { scrutinee, arms, else_expr, span, - } => ASTNode::PeekExpr { + } => ASTNode::MatchExpr { scrutinee: Box::new(rewrite(*scrutinee)), arms: arms.into_iter().map(|(l, e)| (l, rewrite(e))).collect(), else_expr: Box::new(rewrite(*else_expr)), diff --git a/src/runner/json_v0_bridge/ast.rs b/src/runner/json_v0_bridge/ast.rs index b0e7f8d7..8a679724 100644 --- a/src/runner/json_v0_bridge/ast.rs +++ b/src/runner/json_v0_bridge/ast.rs @@ -58,7 +58,7 @@ pub(super) struct CatchV0 { } #[derive(Debug, Deserialize, Serialize, Clone)] -pub(super) struct PeekArmV0 { +pub(super) struct MatchArmV0 { pub(super) label: String, pub(super) expr: ExprV0, } @@ -120,9 +120,9 @@ pub(super) enum ExprV0 { #[serde(rename = "else")] r#else: Box, }, - Peek { + Match { scrutinee: Box, - arms: Vec, + arms: Vec, #[serde(rename = "else")] r#else: Box, }, diff --git a/src/runner/json_v0_bridge/lowering.rs b/src/runner/json_v0_bridge/lowering.rs index ffa8e642..b1eeb12e 100644 --- a/src/runner/json_v0_bridge/lowering.rs +++ b/src/runner/json_v0_bridge/lowering.rs @@ -14,7 +14,7 @@ pub(super) mod loop_; pub(super) mod try_catch; pub(super) mod expr; pub(super) mod ternary; // placeholder (not wired) -pub(super) mod peek; // placeholder (not wired) +pub(super) mod match_expr; // placeholder (not wired) pub(super) mod throw_ctx; // thread-local ctx for Result-mode throw routing #[derive(Clone, Copy)] diff --git a/src/runner/json_v0_bridge/lowering/expr.rs b/src/runner/json_v0_bridge/lowering/expr.rs index d1258e10..52b20b38 100644 --- a/src/runner/json_v0_bridge/lowering/expr.rs +++ b/src/runner/json_v0_bridge/lowering/expr.rs @@ -1,7 +1,7 @@ use super::merge::new_block; use super::BridgeEnv; use super::ternary; -use super::peek; +use super::match_expr; use crate::mir::{ BasicBlockId, BinaryOp, ConstValue, EffectMask, MirFunction, MirInstruction, ValueId, }; @@ -395,8 +395,8 @@ pub(super) fn lower_expr_with_scope( } ExprV0::Ternary { cond, then, r#else } => ternary::lower_ternary_expr_with_scope(env, f, cur_bb, cond, then, r#else, vars), - ExprV0::Peek { scrutinee, arms, r#else } => - peek::lower_peek_expr_with_scope(env, f, cur_bb, scrutinee, arms, r#else, vars), + ExprV0::Match { scrutinee, arms, r#else } => + match_expr::lower_match_expr_with_scope(env, f, cur_bb, scrutinee, arms, r#else, vars), } } diff --git a/src/runner/json_v0_bridge/lowering/peek.rs b/src/runner/json_v0_bridge/lowering/match_expr.rs similarity index 94% rename from src/runner/json_v0_bridge/lowering/peek.rs rename to src/runner/json_v0_bridge/lowering/match_expr.rs index 05eca213..1823c997 100644 --- a/src/runner/json_v0_bridge/lowering/peek.rs +++ b/src/runner/json_v0_bridge/lowering/match_expr.rs @@ -1,17 +1,17 @@ -//! Peek/expr-block lowering for JSON v0 bridge. +//! Match/expr-block lowering for JSON v0 bridge. use super::merge::new_block; use super::BridgeEnv; use crate::mir::{BasicBlockId, CompareOp, ConstValue, MirFunction, MirInstruction, ValueId}; -use super::super::ast::{ExprV0, PeekArmV0}; +use super::super::ast::{ExprV0, MatchArmV0}; use super::expr::{lower_expr_with_scope, VarScope}; -pub(super) fn lower_peek_expr_with_scope( +pub(super) fn lower_match_expr_with_scope( env: &BridgeEnv, f: &mut MirFunction, cur_bb: BasicBlockId, scrutinee: &ExprV0, - arms: &[PeekArmV0], + arms: &[MatchArmV0], else_expr: &ExprV0, vars: &mut S, ) -> Result<(ValueId, BasicBlockId), String> { diff --git a/src/runner/modes/macro_child/transforms/mod.rs b/src/runner/modes/macro_child/transforms/mod.rs index 21b6fa0d..dc86ddbb 100644 --- a/src/runner/modes/macro_child/transforms/mod.rs +++ b/src/runner/modes/macro_child/transforms/mod.rs @@ -42,7 +42,7 @@ pub(super) fn transform_postfix_handlers(ast: &nyash_rust::ASTNode) -> nyash_rus } // Core normalization pass used by runners (always-on when macros enabled). -// Order matters: for/foreach → match(PeekExpr) → loop tail alignment. +// Order matters: for/foreach → match(MatchExpr) → loop tail alignment. pub fn normalize_core_pass(ast: &nyash_rust::ASTNode) -> nyash_rust::ASTNode { let a1 = transform_for_foreach(ast); let a2 = transform_peek_match_literal(&a1); diff --git a/src/runner/modes/macro_child/transforms/peek.rs b/src/runner/modes/macro_child/transforms/peek.rs index 25382c3c..72f352d4 100644 --- a/src/runner/modes/macro_child/transforms/peek.rs +++ b/src/runner/modes/macro_child/transforms/peek.rs @@ -2,7 +2,7 @@ fn map_expr_to_stmt(e: nyash_rust::ASTNode) -> nyash_rust::ASTNode { e } fn transform_peek_to_if_expr(peek: &nyash_rust::ASTNode) -> Option { use nyash_rust::ast::{ASTNode as A, BinaryOperator, Span}; - if let A::PeekExpr { scrutinee, arms, else_expr, .. } = peek { + if let A::MatchExpr { scrutinee, arms, else_expr, .. } = peek { let mut conds_bodies: Vec<(nyash_rust::ast::LiteralValue, A)> = Vec::new(); for (lit, body) in arms { conds_bodies.push((lit.clone(), (*body).clone())); } let mut current: A = *(*else_expr).clone(); @@ -19,7 +19,7 @@ fn transform_peek_to_if_expr(peek: &nyash_rust::ASTNode) -> Option Option { use nyash_rust::ast::{ASTNode as A, BinaryOperator, Span}; - if let A::PeekExpr { scrutinee, arms, else_expr, .. } = peek { + if let A::MatchExpr { scrutinee, arms, else_expr, .. } = peek { let mut pairs: Vec<(nyash_rust::ast::LiteralValue, A)> = Vec::new(); for (lit, body) in arms { pairs.push((lit.clone(), (*body).clone())); } let mut current: A = *(*else_expr).clone(); @@ -36,7 +36,7 @@ fn transform_peek_to_if_stmt_assign(peek: &nyash_rust::ASTNode, target: &nyash_r fn transform_peek_to_if_stmt_return(peek: &nyash_rust::ASTNode) -> Option { use nyash_rust::ast::{ASTNode as A, BinaryOperator, Span}; - if let A::PeekExpr { scrutinee, arms, else_expr, .. } = peek { + if let A::MatchExpr { scrutinee, arms, else_expr, .. } = peek { let mut pairs: Vec<(nyash_rust::ast::LiteralValue, A)> = Vec::new(); for (lit, body) in arms { pairs.push((lit.clone(), (*body).clone())); } let mut current: A = *(*else_expr).clone(); @@ -53,7 +53,7 @@ fn transform_peek_to_if_stmt_return(peek: &nyash_rust::ASTNode) -> Option Option { use nyash_rust::ast::{ASTNode as A, BinaryOperator, Span}; - if let A::PeekExpr { scrutinee, arms, else_expr, .. } = peek { + if let A::MatchExpr { scrutinee, arms, else_expr, .. } = peek { let mut pairs: Vec<(nyash_rust::ast::LiteralValue, A)> = Vec::new(); for (lit, body) in arms { pairs.push((lit.clone(), (*body).clone())); } let mut current: A = *(*else_expr).clone(); diff --git a/src/tests/mir_peek_lower.rs b/src/tests/mir_peek_lower.rs index 00a91e29..02124024 100644 --- a/src/tests/mir_peek_lower.rs +++ b/src/tests/mir_peek_lower.rs @@ -4,7 +4,7 @@ use crate::ast::{ASTNode, LiteralValue, Span}; #[test] fn mir_lowering_of_peek_expr() { // Build AST: peek 2 { 1 => 10, 2 => 20, else => 30 } - let ast = ASTNode::PeekExpr { + let ast = ASTNode::MatchExpr { scrutinee: Box::new(ASTNode::Literal { value: LiteralValue::Integer(2), span: Span::unknown() }), arms: vec![ (LiteralValue::Integer(1), ASTNode::Literal { value: LiteralValue::Integer(10), span: Span::unknown() }), diff --git a/src/tests/parser_peek_block.rs b/src/tests/parser_peek_block.rs index f5cdca03..ebe7ee21 100644 --- a/src/tests/parser_peek_block.rs +++ b/src/tests/parser_peek_block.rs @@ -11,10 +11,10 @@ fn parse_match_with_block_arm() { } "#; let ast = NyashParser::parse_from_string(src).expect("parse ok"); - // Quick structural check: ensure AST contains PeekExpr and Program nodes inside arms + // Quick structural check: ensure AST contains MatchExpr and Program nodes inside arms fn find_peek(ast: &crate::ast::ASTNode) -> bool { match ast { - crate::ast::ASTNode::PeekExpr { arms, else_expr, .. } => { + crate::ast::ASTNode::MatchExpr { arms, else_expr, .. } => { // Expect at least one Program arm let has_block = arms.iter().any(|(_, e)| matches!(e, crate::ast::ASTNode::Program { .. })); let else_is_block = matches!(**else_expr, crate::ast::ASTNode::Program { .. }); diff --git a/src/tests/sugar_coalesce_test.rs b/src/tests/sugar_coalesce_test.rs index 0be603c8..f59d665b 100644 --- a/src/tests/sugar_coalesce_test.rs +++ b/src/tests/sugar_coalesce_test.rs @@ -21,7 +21,7 @@ fn coalesce_peek_rewrite() { } match assign.1.as_ref() { - ASTNode::PeekExpr { + ASTNode::MatchExpr { scrutinee, arms, else_expr, @@ -42,6 +42,6 @@ fn coalesce_peek_rewrite() { _ => panic!("else not a"), } } - other => panic!("expected PeekExpr, got {:?}", other), + other => panic!("expected MatchExpr, got {:?}", other), } } diff --git a/src/tests/sugar_safe_access_test.rs b/src/tests/sugar_safe_access_test.rs index 4b460122..69ac6beb 100644 --- a/src/tests/sugar_safe_access_test.rs +++ b/src/tests/sugar_safe_access_test.rs @@ -16,7 +16,7 @@ fn safe_access_field_and_method() { // a = user?.profile match &program[0] { ASTNode::Assignment { value, .. } => match value.as_ref() { - ASTNode::PeekExpr { + ASTNode::MatchExpr { scrutinee, else_expr, .. @@ -36,7 +36,7 @@ fn safe_access_field_and_method() { other => panic!("else not field access, got {:?}", other), } } - other => panic!("expected PeekExpr, got {:?}", other), + other => panic!("expected MatchExpr, got {:?}", other), }, other => panic!("expected assignment, got {:?}", other), } @@ -44,7 +44,7 @@ fn safe_access_field_and_method() { // b = user?.m(1) match &program[1] { ASTNode::Assignment { value, .. } => match value.as_ref() { - ASTNode::PeekExpr { + ASTNode::MatchExpr { scrutinee, else_expr, .. @@ -70,7 +70,7 @@ fn safe_access_field_and_method() { other => panic!("else not method call, got {:?}", other), } } - other => panic!("expected PeekExpr, got {:?}", other), + other => panic!("expected MatchExpr, got {:?}", other), }, other => panic!("expected assignment, got {:?}", other), } diff --git a/test.nyash b/test.nyash new file mode 100644 index 00000000..25e2df04 --- /dev/null +++ b/test.nyash @@ -0,0 +1,14 @@ +local cond1 = true +local cond2 = false +local x = 0 +loop(true) { + if(cond1) { + if(cond2) { + x = 1 + } else { + x = 2 + } + break + } +} +print(x)