Step 2完了: peek→match完全統一 + 重大PHI命令バグ発見
## 🎉 Step 2: peek→match完全統一アーキテクチャクリーンアップ完了 - ✅ 15ファイルで PeekExpr → MatchExpr 一括置換完了 - ✅ lowering/peek.rs → match_expr.rs 完全移行 - ✅ AI理解性・コードベース一貫性・保守性大幅向上 ## 🔍 Step 3: 複数行パース問題調査完了 - ✅ Task先生による根本原因特定完了 - 原因: オブジェクトリテラルパーサーの改行スキップ不足 - 修正: src/parser/expr/primary.rs の skip_newlines() 追加 ## 🚨 重大発見: PHI命令処理バグ - 問題: gemini_test_case.nyash で期待値2→実際0 - 原因: フェーズM+M.2のPHI統一作業でループ後変数マージに回帰バグ - 詳細: PHI命令は正常だが、print時に間違ったPHI参照 - 影響: Phase 15セルフホスティング基盤の重大バグ ## 📝 CLAUDE.md更新 - 全進捗状況の詳細記録 - 次のアクション: ChatGPT相談でMIRビルダー修正戦略立案 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
28
CLAUDE.md
28
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
|
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問題根本解決
|
- ✅ **フェーズM+M.2完全達成!** PHI統一革命でcollect_prints問題根本解決
|
||||||
- ✅ **Step 1完全達成!** match式オブジェクトリテラル判定修正完了
|
- ✅ **Step 1完全達成!** match式オブジェクトリテラル判定修正完了
|
||||||
- **修正完了**: `src/parser/expr/match_expr.rs` の `is_object_literal()` メソッド追加
|
- **修正完了**: `src/parser/expr/match_expr.rs` の `is_object_literal()` メソッド追加
|
||||||
- **副作用修正**: `match_token()` → `current_token()` で副作用除去
|
- **副作用修正**: `match_token()` → `current_token()` で副作用除去
|
||||||
- **動作確認**: 単行match式 + オブジェクトリテラル ✅ 完全動作
|
- **動作確認**: 単行match式 + オブジェクトリテラル ✅ 完全動作
|
||||||
- **発見**: 複数行パース未対応(後回し決定)
|
- ✅ **Step 2完全達成!** peek→match完全統一でアーキテクチャクリーンアップ完了
|
||||||
- 🚀 **Step 2開始準備!** peek→match完全統一でアーキテクチャクリーンアップ
|
- **統一完了**: 15ファイルで `PeekExpr` → `MatchExpr` 一括置換完了
|
||||||
- **対象ファイル**: `json_v0_bridge/ast.rs`, `lowering/peek.rs` → `match_expr.rs`
|
- **ファイル移行**: `lowering/peek.rs` → `match_expr.rs` 完全移行
|
||||||
- **統一効果**: AI理解向上・ドキュメント整合性・保守性向上・ソースコード美化
|
- **コンパイル成功**: エラーゼロで正常ビルド完了
|
||||||
- **作業順序**: claude.md更新→todo調整→commit→peek構造体→match構造体完全移行
|
- **動作確認**: match式テスト正常動作 ✅
|
||||||
- 🎯 **アーキテクチャ美化**: ややこしいpeek/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アーカイブ完了&デバッグ大進展!
|
## 📝 Update (2025-09-22) 🎯 Phase 15 JITアーカイブ完了&デバッグ大進展!
|
||||||
- ✅ **JIT/Craneliftアーカイブ完了!** Phase 15集中開発のため全JIT機能を安全にアーカイブ
|
- ✅ **JIT/Craneliftアーカイブ完了!** Phase 15集中開発のため全JIT機能を安全にアーカイブ
|
||||||
|
|||||||
14
gemini_test_case.nyash
Normal file
14
gemini_test_case.nyash
Normal file
@ -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)
|
||||||
@ -126,8 +126,8 @@ pub enum ExpressionNode {
|
|||||||
MeExpression {
|
MeExpression {
|
||||||
span: Span,
|
span: Span,
|
||||||
},
|
},
|
||||||
/// peek式: peek <expr> { lit => expr, ... else => expr }
|
/// match式: match <expr> { lit => expr, ... else => expr }
|
||||||
PeekExpr {
|
MatchExpr {
|
||||||
scrutinee: Box<ASTNode>,
|
scrutinee: Box<ASTNode>,
|
||||||
arms: Vec<(LiteralValue, ASTNode)>,
|
arms: Vec<(LiteralValue, ASTNode)>,
|
||||||
else_expr: Box<ASTNode>,
|
else_expr: Box<ASTNode>,
|
||||||
@ -398,8 +398,8 @@ pub enum ASTNode {
|
|||||||
span: Span,
|
span: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// peek式: peek <expr> { lit => expr, ... else => expr }
|
/// match式: match <expr> { lit => expr, ... else => expr }
|
||||||
PeekExpr {
|
MatchExpr {
|
||||||
scrutinee: Box<ASTNode>,
|
scrutinee: Box<ASTNode>,
|
||||||
arms: Vec<(LiteralValue, ASTNode)>,
|
arms: Vec<(LiteralValue, ASTNode)>,
|
||||||
else_expr: Box<ASTNode>,
|
else_expr: Box<ASTNode>,
|
||||||
|
|||||||
@ -43,7 +43,7 @@ impl ASTNode {
|
|||||||
ASTNode::Throw { .. } => "Throw",
|
ASTNode::Throw { .. } => "Throw",
|
||||||
ASTNode::AwaitExpression { .. } => "AwaitExpression",
|
ASTNode::AwaitExpression { .. } => "AwaitExpression",
|
||||||
ASTNode::QMarkPropagate { .. } => "QMarkPropagate",
|
ASTNode::QMarkPropagate { .. } => "QMarkPropagate",
|
||||||
ASTNode::PeekExpr { .. } => "PeekExpr",
|
ASTNode::MatchExpr { .. } => "MatchExpr",
|
||||||
ASTNode::Lambda { .. } => "Lambda",
|
ASTNode::Lambda { .. } => "Lambda",
|
||||||
ASTNode::ArrayLiteral { .. } => "ArrayLiteral",
|
ASTNode::ArrayLiteral { .. } => "ArrayLiteral",
|
||||||
ASTNode::MapLiteral { .. } => "MapLiteral",
|
ASTNode::MapLiteral { .. } => "MapLiteral",
|
||||||
@ -78,7 +78,7 @@ impl ASTNode {
|
|||||||
ASTNode::FromCall { .. } => ASTNodeType::Expression,
|
ASTNode::FromCall { .. } => ASTNodeType::Expression,
|
||||||
ASTNode::ThisField { .. } => ASTNodeType::Expression,
|
ASTNode::ThisField { .. } => ASTNodeType::Expression,
|
||||||
ASTNode::MeField { .. } => ASTNodeType::Expression,
|
ASTNode::MeField { .. } => ASTNodeType::Expression,
|
||||||
ASTNode::PeekExpr { .. } => ASTNodeType::Expression,
|
ASTNode::MatchExpr { .. } => ASTNodeType::Expression,
|
||||||
ASTNode::QMarkPropagate { .. } => ASTNodeType::Expression,
|
ASTNode::QMarkPropagate { .. } => ASTNodeType::Expression,
|
||||||
ASTNode::Lambda { .. } => ASTNodeType::Expression,
|
ASTNode::Lambda { .. } => ASTNodeType::Expression,
|
||||||
ASTNode::ArrayLiteral { .. } => ASTNodeType::Expression,
|
ASTNode::ArrayLiteral { .. } => ASTNodeType::Expression,
|
||||||
@ -303,7 +303,7 @@ impl ASTNode {
|
|||||||
ASTNode::AwaitExpression { expression, .. } => {
|
ASTNode::AwaitExpression { expression, .. } => {
|
||||||
format!("Await({:?})", expression)
|
format!("Await({:?})", expression)
|
||||||
}
|
}
|
||||||
ASTNode::PeekExpr { .. } => "PeekExpr".to_string(),
|
ASTNode::MatchExpr { .. } => "MatchExpr".to_string(),
|
||||||
ASTNode::QMarkPropagate { .. } => "QMarkPropagate".to_string(),
|
ASTNode::QMarkPropagate { .. } => "QMarkPropagate".to_string(),
|
||||||
ASTNode::Lambda { params, body, .. } => {
|
ASTNode::Lambda { params, body, .. } => {
|
||||||
format!("Lambda({} params, {} statements)", params.len(), body.len())
|
format!("Lambda({} params, {} statements)", params.len(), body.len())
|
||||||
@ -356,7 +356,7 @@ impl ASTNode {
|
|||||||
ASTNode::FunctionCall { span, .. } => *span,
|
ASTNode::FunctionCall { span, .. } => *span,
|
||||||
ASTNode::Call { span, .. } => *span,
|
ASTNode::Call { span, .. } => *span,
|
||||||
ASTNode::AwaitExpression { span, .. } => *span,
|
ASTNode::AwaitExpression { span, .. } => *span,
|
||||||
ASTNode::PeekExpr { span, .. } => *span,
|
ASTNode::MatchExpr { span, .. } => *span,
|
||||||
ASTNode::QMarkPropagate { span, .. } => *span,
|
ASTNode::QMarkPropagate { span, .. } => *span,
|
||||||
ASTNode::Lambda { span, .. } => *span,
|
ASTNode::Lambda { span, .. } => *span,
|
||||||
ASTNode::ArrayLiteral { span, .. } => *span,
|
ASTNode::ArrayLiteral { span, .. } => *span,
|
||||||
|
|||||||
@ -66,8 +66,11 @@ def op_boxcall(owner, fn, inst: Dict[str, Any], regs: Dict[int, Any]) -> None:
|
|||||||
return
|
return
|
||||||
|
|
||||||
# User-defined box: dispatch to lowered function if available (Box.method/N)
|
# 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:
|
if isinstance(recv, dict) and isinstance(method, str) and "__box__" in recv:
|
||||||
box_name = recv.get("__box__")
|
box_name = recv.get("__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)}"
|
cand = f"{box_name}.{method}/{len(args)}"
|
||||||
callee = owner.functions.get(cand)
|
callee = owner.functions.get(cand)
|
||||||
if callee is not None:
|
if callee is not None:
|
||||||
|
|||||||
@ -88,8 +88,8 @@ pub fn ast_to_json(ast: &ASTNode) -> Value {
|
|||||||
"kind":"Map",
|
"kind":"Map",
|
||||||
"entries": entries.into_iter().map(|(k,v)| json!({"k":k,"v":ast_to_json(&v)})).collect::<Vec<_>>()
|
"entries": entries.into_iter().map(|(k,v)| json!({"k":k,"v":ast_to_json(&v)})).collect::<Vec<_>>()
|
||||||
}),
|
}),
|
||||||
ASTNode::PeekExpr { scrutinee, arms, else_expr, .. } => json!({
|
ASTNode::MatchExpr { scrutinee, arms, else_expr, .. } => json!({
|
||||||
"kind":"PeekExpr",
|
"kind":"MatchExpr",
|
||||||
"scrutinee": ast_to_json(&scrutinee),
|
"scrutinee": ast_to_json(&scrutinee),
|
||||||
"arms": arms.into_iter().map(|(lit, body)| json!({
|
"arms": arms.into_iter().map(|(lit, body)| json!({
|
||||||
"literal": {
|
"literal": {
|
||||||
@ -147,7 +147,7 @@ pub fn json_to_ast(v: &Value) -> Option<ASTNode> {
|
|||||||
"Map" => ASTNode::MapLiteral { entries: v.get("entries")?.as_array()?.iter().filter_map(|e| {
|
"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")?)?))
|
Some((e.get("k")?.as_str()?.to_string(), json_to_ast(e.get("v")?)?))
|
||||||
}).collect(), span: Span::unknown() },
|
}).collect(), span: Span::unknown() },
|
||||||
"PeekExpr" => {
|
"MatchExpr" => {
|
||||||
let scr = json_to_ast(v.get("scrutinee")?)?;
|
let scr = json_to_ast(v.get("scrutinee")?)?;
|
||||||
let arms_json = v.get("arms")?.as_array()?.iter();
|
let arms_json = v.get("arms")?.as_array()?.iter();
|
||||||
let mut arms = Vec::new();
|
let mut arms = Vec::new();
|
||||||
@ -158,7 +158,7 @@ pub fn json_to_ast(v: &Value) -> Option<ASTNode> {
|
|||||||
arms.push((lit, body));
|
arms.push((lit, body));
|
||||||
}
|
}
|
||||||
let else_expr = json_to_ast(v.get("else")?)?;
|
let else_expr = json_to_ast(v.get("else")?)?;
|
||||||
ASTNode::PeekExpr {
|
ASTNode::MatchExpr {
|
||||||
scrutinee: Box::new(scr),
|
scrutinee: Box::new(scr),
|
||||||
arms,
|
arms,
|
||||||
else_expr: Box::new(else_expr),
|
else_expr: Box::new(else_expr),
|
||||||
|
|||||||
@ -85,7 +85,7 @@ impl super::MirBuilder {
|
|||||||
self.build_qmark_propagate_expression(*expression.clone())
|
self.build_qmark_propagate_expression(*expression.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTNode::PeekExpr {
|
ASTNode::MatchExpr {
|
||||||
scrutinee,
|
scrutinee,
|
||||||
arms,
|
arms,
|
||||||
else_expr,
|
else_expr,
|
||||||
|
|||||||
@ -123,7 +123,7 @@ impl super::MirBuilder {
|
|||||||
ASTNode::AwaitExpression { expression, .. } => {
|
ASTNode::AwaitExpression { expression, .. } => {
|
||||||
collect_vars(expression, used, locals);
|
collect_vars(expression, used, locals);
|
||||||
}
|
}
|
||||||
ASTNode::PeekExpr {
|
ASTNode::MatchExpr {
|
||||||
scrutinee,
|
scrutinee,
|
||||||
arms,
|
arms,
|
||||||
else_expr,
|
else_expr,
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
use super::{ConstValue, Effect, EffectMask, MirInstruction, ValueId};
|
use super::{ConstValue, Effect, EffectMask, MirInstruction, ValueId};
|
||||||
use crate::ast::{ASTNode, CallExpr};
|
use crate::ast::{ASTNode, CallExpr};
|
||||||
use crate::mir::TypeOpKind;
|
use crate::mir::TypeOpKind;
|
||||||
|
use crate::mir::utils::is_current_block_terminated;
|
||||||
|
|
||||||
impl super::MirBuilder {
|
impl super::MirBuilder {
|
||||||
// Print statement: env.console.log(value) with early TypeOp handling
|
// Print statement: env.console.log(value) with early TypeOp handling
|
||||||
@ -141,7 +142,7 @@ impl super::MirBuilder {
|
|||||||
last_value = Some(self.build_expression(statement)?);
|
last_value = Some(self.build_expression(statement)?);
|
||||||
// If the current block was terminated by this statement (e.g., return/throw),
|
// If the current block was terminated by this statement (e.g., return/throw),
|
||||||
// do not emit any further instructions for this block.
|
// do not emit any further instructions for this block.
|
||||||
if self.is_current_block_terminated() {
|
if is_current_block_terminated(self)? {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -118,7 +118,7 @@ pub(super) fn collect_free_vars(
|
|||||||
ASTNode::AwaitExpression { expression, .. } => {
|
ASTNode::AwaitExpression { expression, .. } => {
|
||||||
collect_free_vars(expression, used, locals);
|
collect_free_vars(expression, used, locals);
|
||||||
}
|
}
|
||||||
ASTNode::PeekExpr {
|
ASTNode::MatchExpr {
|
||||||
scrutinee,
|
scrutinee,
|
||||||
arms,
|
arms,
|
||||||
else_expr,
|
else_expr,
|
||||||
|
|||||||
@ -455,20 +455,8 @@ impl<'a> LoopBuilder<'a> {
|
|||||||
let mut last = None;
|
let mut last = None;
|
||||||
for s in statements.into_iter() {
|
for s in statements.into_iter() {
|
||||||
last = Some(self.build_statement(s)?);
|
last = Some(self.build_statement(s)?);
|
||||||
// Stop early if this block has been terminated (e.g., break/continue)
|
// フェーズS修正:統一終端検出ユーティリティ使用
|
||||||
let cur_id = self.current_block()?;
|
if is_current_block_terminated(self.parent_builder)? {
|
||||||
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 {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -154,7 +154,7 @@ impl NyashParser {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Wrap with peek: peek expr { null => null, else => access(expr) }
|
// Wrap with peek: peek expr { null => null, else => access(expr) }
|
||||||
expr = ASTNode::PeekExpr {
|
expr = ASTNode::MatchExpr {
|
||||||
scrutinee: Box::new(expr.clone()),
|
scrutinee: Box::new(expr.clone()),
|
||||||
arms: vec![(
|
arms: vec![(
|
||||||
crate::ast::LiteralValue::Null,
|
crate::ast::LiteralValue::Null,
|
||||||
|
|||||||
@ -23,7 +23,7 @@ impl NyashParser {
|
|||||||
self.advance();
|
self.advance();
|
||||||
let rhs = self.expr_parse_or()?;
|
let rhs = self.expr_parse_or()?;
|
||||||
let scr = expr;
|
let scr = expr;
|
||||||
expr = ASTNode::PeekExpr {
|
expr = ASTNode::MatchExpr {
|
||||||
scrutinee: Box::new(scr.clone()),
|
scrutinee: Box::new(scr.clone()),
|
||||||
arms: vec![(crate::ast::LiteralValue::Null, rhs)],
|
arms: vec![(crate::ast::LiteralValue::Null, rhs)],
|
||||||
else_expr: Box::new(scr),
|
else_expr: Box::new(scr),
|
||||||
|
|||||||
@ -193,7 +193,7 @@ impl NyashParser {
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
if !saw_type_arm && !saw_guard {
|
if !saw_type_arm && !saw_guard {
|
||||||
// 既存の Lower を活用するため PeekExpr に落とす(型パターンが無い場合のみ)
|
// 既存の Lower を活用するため MatchExpr に落とす(型パターンが無い場合のみ)
|
||||||
let mut lit_arms: Vec<(LiteralValue, ASTNode)> = Vec::new();
|
let mut lit_arms: Vec<(LiteralValue, ASTNode)> = Vec::new();
|
||||||
for arm in arms_any.into_iter() {
|
for arm in arms_any.into_iter() {
|
||||||
match arm {
|
match arm {
|
||||||
@ -206,7 +206,7 @@ impl NyashParser {
|
|||||||
MatchArm::Type { .. } => unreachable!(),
|
MatchArm::Type { .. } => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Ok(ASTNode::PeekExpr {
|
return Ok(ASTNode::MatchExpr {
|
||||||
scrutinee: Box::new(scrutinee),
|
scrutinee: Box::new(scrutinee),
|
||||||
arms: lit_arms,
|
arms: lit_arms,
|
||||||
else_expr: Box::new(else_expr),
|
else_expr: Box::new(else_expr),
|
||||||
|
|||||||
@ -46,7 +46,9 @@ impl NyashParser {
|
|||||||
let ident_key_on = std::env::var("NYASH_ENABLE_MAP_IDENT_KEY").ok().as_deref()
|
let ident_key_on = std::env::var("NYASH_ENABLE_MAP_IDENT_KEY").ok().as_deref()
|
||||||
== Some("1")
|
== Some("1")
|
||||||
|| sugar_level.as_deref() == Some("full");
|
|| sugar_level.as_deref() == Some("full");
|
||||||
|
self.skip_newlines();
|
||||||
while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() {
|
while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() {
|
||||||
|
self.skip_newlines();
|
||||||
let key = match &self.current_token().token_type {
|
let key = match &self.current_token().token_type {
|
||||||
TokenType::STRING(s) => {
|
TokenType::STRING(s) => {
|
||||||
let v = s.clone();
|
let v = s.clone();
|
||||||
@ -76,8 +78,10 @@ impl NyashParser {
|
|||||||
entries.push((key, value_expr));
|
entries.push((key, value_expr));
|
||||||
if self.match_token(&TokenType::COMMA) {
|
if self.match_token(&TokenType::COMMA) {
|
||||||
self.advance();
|
self.advance();
|
||||||
|
self.skip_newlines();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.skip_newlines();
|
||||||
self.consume(TokenType::RBRACE)?;
|
self.consume(TokenType::RBRACE)?;
|
||||||
Ok(ASTNode::MapLiteral {
|
Ok(ASTNode::MapLiteral {
|
||||||
entries,
|
entries,
|
||||||
|
|||||||
@ -84,12 +84,12 @@ fn rewrite(ast: ASTNode) -> ASTNode {
|
|||||||
operand: Box::new(rewrite(*operand)),
|
operand: Box::new(rewrite(*operand)),
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
ASTNode::PeekExpr {
|
ASTNode::MatchExpr {
|
||||||
scrutinee,
|
scrutinee,
|
||||||
arms,
|
arms,
|
||||||
else_expr,
|
else_expr,
|
||||||
span,
|
span,
|
||||||
} => ASTNode::PeekExpr {
|
} => ASTNode::MatchExpr {
|
||||||
scrutinee: Box::new(rewrite(*scrutinee)),
|
scrutinee: Box::new(rewrite(*scrutinee)),
|
||||||
arms: arms.into_iter().map(|(l, e)| (l, rewrite(e))).collect(),
|
arms: arms.into_iter().map(|(l, e)| (l, rewrite(e))).collect(),
|
||||||
else_expr: Box::new(rewrite(*else_expr)),
|
else_expr: Box::new(rewrite(*else_expr)),
|
||||||
|
|||||||
@ -58,7 +58,7 @@ pub(super) struct CatchV0 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
pub(super) struct PeekArmV0 {
|
pub(super) struct MatchArmV0 {
|
||||||
pub(super) label: String,
|
pub(super) label: String,
|
||||||
pub(super) expr: ExprV0,
|
pub(super) expr: ExprV0,
|
||||||
}
|
}
|
||||||
@ -120,9 +120,9 @@ pub(super) enum ExprV0 {
|
|||||||
#[serde(rename = "else")]
|
#[serde(rename = "else")]
|
||||||
r#else: Box<ExprV0>,
|
r#else: Box<ExprV0>,
|
||||||
},
|
},
|
||||||
Peek {
|
Match {
|
||||||
scrutinee: Box<ExprV0>,
|
scrutinee: Box<ExprV0>,
|
||||||
arms: Vec<PeekArmV0>,
|
arms: Vec<MatchArmV0>,
|
||||||
#[serde(rename = "else")]
|
#[serde(rename = "else")]
|
||||||
r#else: Box<ExprV0>,
|
r#else: Box<ExprV0>,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -14,7 +14,7 @@ pub(super) mod loop_;
|
|||||||
pub(super) mod try_catch;
|
pub(super) mod try_catch;
|
||||||
pub(super) mod expr;
|
pub(super) mod expr;
|
||||||
pub(super) mod ternary; // placeholder (not wired)
|
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
|
pub(super) mod throw_ctx; // thread-local ctx for Result-mode throw routing
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use super::merge::new_block;
|
use super::merge::new_block;
|
||||||
use super::BridgeEnv;
|
use super::BridgeEnv;
|
||||||
use super::ternary;
|
use super::ternary;
|
||||||
use super::peek;
|
use super::match_expr;
|
||||||
use crate::mir::{
|
use crate::mir::{
|
||||||
BasicBlockId, BinaryOp, ConstValue, EffectMask, MirFunction, MirInstruction, ValueId,
|
BasicBlockId, BinaryOp, ConstValue, EffectMask, MirFunction, MirInstruction, ValueId,
|
||||||
};
|
};
|
||||||
@ -395,8 +395,8 @@ pub(super) fn lower_expr_with_scope<S: VarScope>(
|
|||||||
}
|
}
|
||||||
ExprV0::Ternary { cond, then, r#else } =>
|
ExprV0::Ternary { cond, then, r#else } =>
|
||||||
ternary::lower_ternary_expr_with_scope(env, f, cur_bb, cond, then, r#else, vars),
|
ternary::lower_ternary_expr_with_scope(env, f, cur_bb, cond, then, r#else, vars),
|
||||||
ExprV0::Peek { scrutinee, arms, r#else } =>
|
ExprV0::Match { scrutinee, arms, r#else } =>
|
||||||
peek::lower_peek_expr_with_scope(env, f, cur_bb, scrutinee, arms, r#else, vars),
|
match_expr::lower_match_expr_with_scope(env, f, cur_bb, scrutinee, arms, r#else, vars),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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::merge::new_block;
|
||||||
use super::BridgeEnv;
|
use super::BridgeEnv;
|
||||||
use crate::mir::{BasicBlockId, CompareOp, ConstValue, MirFunction, MirInstruction, ValueId};
|
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};
|
use super::expr::{lower_expr_with_scope, VarScope};
|
||||||
|
|
||||||
pub(super) fn lower_peek_expr_with_scope<S: VarScope>(
|
pub(super) fn lower_match_expr_with_scope<S: VarScope>(
|
||||||
env: &BridgeEnv,
|
env: &BridgeEnv,
|
||||||
f: &mut MirFunction,
|
f: &mut MirFunction,
|
||||||
cur_bb: BasicBlockId,
|
cur_bb: BasicBlockId,
|
||||||
scrutinee: &ExprV0,
|
scrutinee: &ExprV0,
|
||||||
arms: &[PeekArmV0],
|
arms: &[MatchArmV0],
|
||||||
else_expr: &ExprV0,
|
else_expr: &ExprV0,
|
||||||
vars: &mut S,
|
vars: &mut S,
|
||||||
) -> Result<(ValueId, BasicBlockId), String> {
|
) -> Result<(ValueId, BasicBlockId), String> {
|
||||||
@ -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).
|
// 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 {
|
pub fn normalize_core_pass(ast: &nyash_rust::ASTNode) -> nyash_rust::ASTNode {
|
||||||
let a1 = transform_for_foreach(ast);
|
let a1 = transform_for_foreach(ast);
|
||||||
let a2 = transform_peek_match_literal(&a1);
|
let a2 = transform_peek_match_literal(&a1);
|
||||||
|
|||||||
@ -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<nyash_rust::ASTNode> {
|
fn transform_peek_to_if_expr(peek: &nyash_rust::ASTNode) -> Option<nyash_rust::ASTNode> {
|
||||||
use nyash_rust::ast::{ASTNode as A, BinaryOperator, Span};
|
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();
|
let mut conds_bodies: Vec<(nyash_rust::ast::LiteralValue, A)> = Vec::new();
|
||||||
for (lit, body) in arms { conds_bodies.push((lit.clone(), (*body).clone())); }
|
for (lit, body) in arms { conds_bodies.push((lit.clone(), (*body).clone())); }
|
||||||
let mut current: A = *(*else_expr).clone();
|
let mut current: A = *(*else_expr).clone();
|
||||||
@ -19,7 +19,7 @@ fn transform_peek_to_if_expr(peek: &nyash_rust::ASTNode) -> Option<nyash_rust::A
|
|||||||
|
|
||||||
fn transform_peek_to_if_stmt_assign(peek: &nyash_rust::ASTNode, target: &nyash_rust::ASTNode) -> Option<nyash_rust::ASTNode> {
|
fn transform_peek_to_if_stmt_assign(peek: &nyash_rust::ASTNode, target: &nyash_rust::ASTNode) -> Option<nyash_rust::ASTNode> {
|
||||||
use nyash_rust::ast::{ASTNode as A, BinaryOperator, Span};
|
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();
|
let mut pairs: Vec<(nyash_rust::ast::LiteralValue, A)> = Vec::new();
|
||||||
for (lit, body) in arms { pairs.push((lit.clone(), (*body).clone())); }
|
for (lit, body) in arms { pairs.push((lit.clone(), (*body).clone())); }
|
||||||
let mut current: A = *(*else_expr).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<nyash_rust::ASTNode> {
|
fn transform_peek_to_if_stmt_return(peek: &nyash_rust::ASTNode) -> Option<nyash_rust::ASTNode> {
|
||||||
use nyash_rust::ast::{ASTNode as A, BinaryOperator, Span};
|
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();
|
let mut pairs: Vec<(nyash_rust::ast::LiteralValue, A)> = Vec::new();
|
||||||
for (lit, body) in arms { pairs.push((lit.clone(), (*body).clone())); }
|
for (lit, body) in arms { pairs.push((lit.clone(), (*body).clone())); }
|
||||||
let mut current: A = *(*else_expr).clone();
|
let mut current: A = *(*else_expr).clone();
|
||||||
@ -53,7 +53,7 @@ fn transform_peek_to_if_stmt_return(peek: &nyash_rust::ASTNode) -> Option<nyash_
|
|||||||
|
|
||||||
fn transform_peek_to_if_stmt_print(peek: &nyash_rust::ASTNode) -> Option<nyash_rust::ASTNode> {
|
fn transform_peek_to_if_stmt_print(peek: &nyash_rust::ASTNode) -> Option<nyash_rust::ASTNode> {
|
||||||
use nyash_rust::ast::{ASTNode as A, BinaryOperator, Span};
|
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();
|
let mut pairs: Vec<(nyash_rust::ast::LiteralValue, A)> = Vec::new();
|
||||||
for (lit, body) in arms { pairs.push((lit.clone(), (*body).clone())); }
|
for (lit, body) in arms { pairs.push((lit.clone(), (*body).clone())); }
|
||||||
let mut current: A = *(*else_expr).clone();
|
let mut current: A = *(*else_expr).clone();
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use crate::ast::{ASTNode, LiteralValue, Span};
|
|||||||
#[test]
|
#[test]
|
||||||
fn mir_lowering_of_peek_expr() {
|
fn mir_lowering_of_peek_expr() {
|
||||||
// Build AST: peek 2 { 1 => 10, 2 => 20, else => 30 }
|
// 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() }),
|
scrutinee: Box::new(ASTNode::Literal { value: LiteralValue::Integer(2), span: Span::unknown() }),
|
||||||
arms: vec![
|
arms: vec![
|
||||||
(LiteralValue::Integer(1), ASTNode::Literal { value: LiteralValue::Integer(10), span: Span::unknown() }),
|
(LiteralValue::Integer(1), ASTNode::Literal { value: LiteralValue::Integer(10), span: Span::unknown() }),
|
||||||
|
|||||||
@ -11,10 +11,10 @@ fn parse_match_with_block_arm() {
|
|||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
let ast = NyashParser::parse_from_string(src).expect("parse ok");
|
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 {
|
fn find_peek(ast: &crate::ast::ASTNode) -> bool {
|
||||||
match ast {
|
match ast {
|
||||||
crate::ast::ASTNode::PeekExpr { arms, else_expr, .. } => {
|
crate::ast::ASTNode::MatchExpr { arms, else_expr, .. } => {
|
||||||
// Expect at least one Program arm
|
// Expect at least one Program arm
|
||||||
let has_block = arms.iter().any(|(_, e)| matches!(e, crate::ast::ASTNode::Program { .. }));
|
let has_block = arms.iter().any(|(_, e)| matches!(e, crate::ast::ASTNode::Program { .. }));
|
||||||
let else_is_block = matches!(**else_expr, crate::ast::ASTNode::Program { .. });
|
let else_is_block = matches!(**else_expr, crate::ast::ASTNode::Program { .. });
|
||||||
|
|||||||
@ -21,7 +21,7 @@ fn coalesce_peek_rewrite() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match assign.1.as_ref() {
|
match assign.1.as_ref() {
|
||||||
ASTNode::PeekExpr {
|
ASTNode::MatchExpr {
|
||||||
scrutinee,
|
scrutinee,
|
||||||
arms,
|
arms,
|
||||||
else_expr,
|
else_expr,
|
||||||
@ -42,6 +42,6 @@ fn coalesce_peek_rewrite() {
|
|||||||
_ => panic!("else not a"),
|
_ => panic!("else not a"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
other => panic!("expected PeekExpr, got {:?}", other),
|
other => panic!("expected MatchExpr, got {:?}", other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ fn safe_access_field_and_method() {
|
|||||||
// a = user?.profile
|
// a = user?.profile
|
||||||
match &program[0] {
|
match &program[0] {
|
||||||
ASTNode::Assignment { value, .. } => match value.as_ref() {
|
ASTNode::Assignment { value, .. } => match value.as_ref() {
|
||||||
ASTNode::PeekExpr {
|
ASTNode::MatchExpr {
|
||||||
scrutinee,
|
scrutinee,
|
||||||
else_expr,
|
else_expr,
|
||||||
..
|
..
|
||||||
@ -36,7 +36,7 @@ fn safe_access_field_and_method() {
|
|||||||
other => panic!("else not field access, got {:?}", other),
|
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),
|
other => panic!("expected assignment, got {:?}", other),
|
||||||
}
|
}
|
||||||
@ -44,7 +44,7 @@ fn safe_access_field_and_method() {
|
|||||||
// b = user?.m(1)
|
// b = user?.m(1)
|
||||||
match &program[1] {
|
match &program[1] {
|
||||||
ASTNode::Assignment { value, .. } => match value.as_ref() {
|
ASTNode::Assignment { value, .. } => match value.as_ref() {
|
||||||
ASTNode::PeekExpr {
|
ASTNode::MatchExpr {
|
||||||
scrutinee,
|
scrutinee,
|
||||||
else_expr,
|
else_expr,
|
||||||
..
|
..
|
||||||
@ -70,7 +70,7 @@ fn safe_access_field_and_method() {
|
|||||||
other => panic!("else not method call, got {:?}", other),
|
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),
|
other => panic!("expected assignment, got {:?}", other),
|
||||||
}
|
}
|
||||||
|
|||||||
14
test.nyash
Normal file
14
test.nyash
Normal file
@ -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)
|
||||||
Reference in New Issue
Block a user