parser(match): add MVP type patterns (IntegerBox(x)/StringBox(s)) via AST If-chain; keep literal-only path using PeekExpr; add smoke app (apps/tests/match_type_pattern_basic.nyash); build + stage-2 smokes green

This commit is contained in:
Selfhosting Dev
2025-09-19 08:34:29 +09:00
parent f4e340da08
commit 9142476484
348 changed files with 2539 additions and 281 deletions

View File

@ -1,6 +1,5 @@
use super::{ConstValue, Effect, EffectMask, MirInstruction, ValueId};
use crate::ast::{ASTNode, CallExpr};
use crate::mir::loop_builder::LoopBuilder;
use crate::mir::TypeOpKind;
impl super::MirBuilder {
@ -154,195 +153,7 @@ impl super::MirBuilder {
}))
}
// If: lower to Branch + Phi, bind reassigned var name if identical
pub(super) fn build_if_statement(
&mut self,
condition: ASTNode,
then_branch: ASTNode,
else_branch: Option<ASTNode>,
) -> Result<ValueId, String> {
self.lower_if_form(condition, then_branch, else_branch)
}
// Loop: delegate to LoopBuilder
pub(super) fn build_loop_statement(
&mut self,
condition: ASTNode,
body: Vec<ASTNode>,
) -> Result<ValueId, String> {
let mut loop_builder = LoopBuilder::new(self);
loop_builder.build_loop(condition, body)
}
// Try/Catch/Finally lowering with basic Extern semantics when disabled
pub(super) fn build_try_catch_statement(
&mut self,
try_body: Vec<ASTNode>,
catch_clauses: Vec<crate::ast::CatchClause>,
finally_body: Option<Vec<ASTNode>>,
) -> Result<ValueId, String> {
if std::env::var("NYASH_BUILDER_DISABLE_TRYCATCH")
.ok()
.as_deref()
== Some("1")
{
let try_ast = ASTNode::Program {
statements: try_body,
span: crate::ast::Span::unknown(),
};
let result = self.build_expression(try_ast)?;
return Ok(result);
}
let try_block = self.block_gen.next();
let catch_block = self.block_gen.next();
let finally_block = if finally_body.is_some() {
Some(self.block_gen.next())
} else {
None
};
let exit_block = self.block_gen.next();
// Snapshot and enable deferred-return mode for try/catch
let saved_defer_active = self.return_defer_active;
let saved_defer_slot = self.return_defer_slot;
let saved_defer_target = self.return_defer_target;
let saved_deferred_flag = self.return_deferred_emitted;
let saved_in_cleanup = self.in_cleanup_block;
let saved_allow_ret = self.cleanup_allow_return;
let saved_allow_throw = self.cleanup_allow_throw;
let ret_slot = self.value_gen.next();
self.return_defer_active = true;
self.return_defer_slot = Some(ret_slot);
self.return_deferred_emitted = false;
self.return_defer_target = Some(finally_block.unwrap_or(exit_block));
if let Some(catch_clause) = catch_clauses.first() {
if std::env::var("NYASH_DEBUG_TRYCATCH").ok().as_deref() == Some("1") {
eprintln!(
"[BUILDER] Emitting catch handler for {:?}",
catch_clause.exception_type
);
}
let exception_value = self.value_gen.next();
self.emit_instruction(MirInstruction::Catch {
exception_type: catch_clause.exception_type.clone(),
exception_value,
handler_bb: catch_block,
})?;
}
self.emit_instruction(MirInstruction::Jump { target: try_block })?;
self.start_new_block(try_block)?;
let try_ast = ASTNode::Program {
statements: try_body,
span: crate::ast::Span::unknown(),
};
let _try_result = self.build_expression(try_ast)?;
if !self.is_current_block_terminated() {
let next_target = finally_block.unwrap_or(exit_block);
self.emit_instruction(MirInstruction::Jump {
target: next_target,
})?;
}
self.start_new_block(catch_block)?;
if std::env::var("NYASH_DEBUG_TRYCATCH").ok().as_deref() == Some("1") {
eprintln!("[BUILDER] Enter catch block {:?}", catch_block);
}
if let Some(catch_clause) = catch_clauses.first() {
if std::env::var("NYASH_DEBUG_TRYCATCH").ok().as_deref() == Some("1") {
eprintln!(
"[BUILDER] Emitting catch handler for {:?}",
catch_clause.exception_type
);
}
let catch_ast = ASTNode::Program {
statements: catch_clause.body.clone(),
span: crate::ast::Span::unknown(),
};
self.build_expression(catch_ast)?;
}
if !self.is_current_block_terminated() {
let next_target = finally_block.unwrap_or(exit_block);
self.emit_instruction(MirInstruction::Jump {
target: next_target,
})?;
}
let mut cleanup_terminated = false;
if let (Some(finally_block_id), Some(finally_statements)) = (finally_block, finally_body) {
self.start_new_block(finally_block_id)?;
// Enter cleanup mode; returns may or may not be allowed by policy
self.in_cleanup_block = true;
self.cleanup_allow_return = crate::config::env::cleanup_allow_return();
self.cleanup_allow_throw = crate::config::env::cleanup_allow_throw();
// Inside cleanup, do not defer returns; either forbid or emit real Return
self.return_defer_active = false;
let finally_ast = ASTNode::Program {
statements: finally_statements,
span: crate::ast::Span::unknown(),
};
self.build_expression(finally_ast)?;
// Do not emit a Jump if the finally block already terminated (e.g., via return/throw)
cleanup_terminated = self.is_current_block_terminated();
if !cleanup_terminated {
self.emit_instruction(MirInstruction::Jump { target: exit_block })?;
}
self.in_cleanup_block = false;
}
self.start_new_block(exit_block)?;
// If any deferred return occurred in try/catch and cleanup did not already return,
// finalize with a Return of the slot; otherwise emit a dummy const/ret to satisfy structure.
let result = if self.return_deferred_emitted && !cleanup_terminated {
self.emit_instruction(MirInstruction::Return { value: Some(ret_slot) })?;
// Emit a symbolic const to satisfy return type inference paths when inspecting non-terminated blocks (not used here)
let r = self.value_gen.next();
self.emit_instruction(MirInstruction::Const { dst: r, value: ConstValue::Void })?;
r
} else {
let r = self.value_gen.next();
self.emit_instruction(MirInstruction::Const { dst: r, value: ConstValue::Void })?;
r
};
// Restore deferred-return context
self.return_defer_active = saved_defer_active;
self.return_defer_slot = saved_defer_slot;
self.return_defer_target = saved_defer_target;
self.return_deferred_emitted = saved_deferred_flag;
self.in_cleanup_block = saved_in_cleanup;
self.cleanup_allow_return = saved_allow_ret;
self.cleanup_allow_throw = saved_allow_throw;
Ok(result)
}
// Throw: emit Throw or fallback to env.debug.trace when disabled
pub(super) fn build_throw_statement(&mut self, expression: ASTNode) -> Result<ValueId, String> {
// Enforce cleanup policy
if self.in_cleanup_block && !self.cleanup_allow_throw {
return Err("throw is not allowed inside cleanup block (enable NYASH_CLEANUP_ALLOW_THROW=1 to permit)".to_string());
}
if std::env::var("NYASH_BUILDER_DISABLE_THROW").ok().as_deref() == Some("1") {
let v = self.build_expression(expression)?;
self.emit_instruction(MirInstruction::ExternCall {
dst: None,
iface_name: "env.debug".to_string(),
method_name: "trace".to_string(),
args: vec![v],
effects: EffectMask::PURE.add(Effect::Debug),
})?;
return Ok(v);
}
let exception_value = self.build_expression(expression)?;
self.emit_instruction(MirInstruction::Throw {
exception: exception_value,
effects: EffectMask::PANIC,
})?;
Ok(exception_value)
}
// control-flow build_* moved to control_flow.rs (use cf_* instead)
// Local declarations with optional initializers
pub(super) fn build_local_statement(