refactor: MIR builder modularization complete - ready for handoff

- MIRビルダーのモジュール化完了(1,547行→6モジュール)
  - core.rs (205行): MirBuilder本体
  - expressions.rs (621行): 式変換処理
  - statements.rs (165行): 文変換処理
  - control_flow.rs (194行): 制御フロー
  - box_handlers.rs (73行): Box処理
- 現在builder_modularizedに退避(MIR命令構造変更により調整必要)
- フルビルド可能な状態を維持
- CURRENT_TASK.mdに引き継ぎポイント記載

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Moe Charm
2025-08-25 19:33:07 +09:00
parent 4caa07d865
commit b8e416fb03
13 changed files with 3254 additions and 1 deletions

View File

@ -1,73 +0,0 @@
/*!
* MIR Builder Box Handlers - Box-related AST node conversion
*
* Handles conversion of Box-related AST nodes (new expressions, box declarations) to MIR instructions
*/
use super::*;
use crate::ast::ASTNode;
use std::collections::{HashMap, HashSet};
impl MirBuilder {
/// Build static box Main - extracts main() method body and converts to Program
pub(super) fn build_static_main_box(&mut self, methods: HashMap<String, ASTNode>) -> Result<ValueId, String> {
// Look for the main() method
if let Some(main_method) = methods.get("main") {
if let ASTNode::FunctionDeclaration { body, .. } = main_method {
// Convert the method body to a Program AST node and lower it
let program_ast = ASTNode::Program {
statements: body.clone(),
span: crate::ast::Span::unknown(),
};
// Use existing Program lowering logic
self.build_expression(program_ast)
} else {
Err("main method in static box Main is not a FunctionDeclaration".to_string())
}
} else {
Err("static box Main must contain a main() method".to_string())
}
}
/// Build box declaration - register type metadata
pub(super) fn build_box_declaration(&mut self, name: String, methods: HashMap<String, ASTNode>, fields: Vec<String>, weak_fields: Vec<String>) -> Result<(), String> {
// For Phase 8.4, we'll emit metadata instructions to register the box type
// In a full implementation, this would register type information for later use
// Create a type registration constant
let type_id = self.value_gen.next();
self.emit_instruction(MirInstruction::Const {
dst: type_id,
value: ConstValue::String(format!("__box_type_{}", name)),
})?;
// For each field, emit metadata about the field
for field in fields {
let field_id = self.value_gen.next();
self.emit_instruction(MirInstruction::Const {
dst: field_id,
value: ConstValue::String(format!("__field_{}_{}", name, field)),
})?;
}
// Record weak fields for this box
if !weak_fields.is_empty() {
let set: HashSet<String> = weak_fields.into_iter().collect();
self.weak_fields_by_box.insert(name.clone(), set);
}
// Process methods - now methods is a HashMap
for (method_name, method_ast) in methods {
if let ASTNode::FunctionDeclaration { .. } = method_ast {
let method_id = self.value_gen.next();
self.emit_instruction(MirInstruction::Const {
dst: method_id,
value: ConstValue::String(format!("__method_{}_{}", name, method_name)),
})?;
}
}
Ok(())
}
}

View File

@ -1,194 +0,0 @@
/*!
* MIR Builder Control Flow - Control flow AST node conversion
*
* Handles conversion of control flow AST nodes (if, loop, try-catch) to MIR instructions
*/
use super::*;
use crate::ast::ASTNode;
impl MirBuilder {
/// Build if statement with conditional branches
pub(super) fn build_if_statement(&mut self, condition: ASTNode, then_branch: ASTNode, else_branch: Option<ASTNode>) -> Result<ValueId, String> {
let condition_val = self.build_expression(condition)?;
// Create basic blocks for then/else/merge
let then_block = self.block_gen.next();
let else_block = self.block_gen.next();
let merge_block = self.block_gen.next();
// Emit branch instruction in current block
self.emit_instruction(MirInstruction::Branch {
condition: condition_val,
then_bb: then_block,
else_bb: else_block,
})?;
// Build then branch
self.current_block = Some(then_block);
self.ensure_block_exists(then_block)?;
// Keep a copy of AST for analysis (phi for variable reassignment)
let then_ast_for_analysis = then_branch.clone();
let then_value = self.build_expression(then_branch)?;
if !self.is_current_block_terminated() {
self.emit_instruction(MirInstruction::Jump { target: merge_block })?;
}
// Build else branch
self.current_block = Some(else_block);
self.ensure_block_exists(else_block)?;
let (else_value, else_ast_for_analysis) = if let Some(else_ast) = else_branch {
let val = self.build_expression(else_ast.clone())?;
(val, Some(else_ast))
} else {
// No else branch, use void
let void_val = self.value_gen.next();
self.emit_instruction(MirInstruction::Const {
dst: void_val,
value: ConstValue::Void,
})?;
(void_val, None)
};
if !self.is_current_block_terminated() {
self.emit_instruction(MirInstruction::Jump { target: merge_block })?;
}
// Create merge block with phi function
self.current_block = Some(merge_block);
self.ensure_block_exists(merge_block)?;
let result_val = self.value_gen.next();
self.emit_instruction(MirInstruction::Phi {
dst: result_val,
inputs: vec![
(then_block, then_value),
(else_block, else_value),
],
})?;
// Heuristic: If both branches assign the same variable name, bind that variable to the phi result
let assigned_var_then = Self::extract_assigned_var(&then_ast_for_analysis);
let assigned_var_else = else_ast_for_analysis.as_ref().and_then(|a| Self::extract_assigned_var(a));
if let (Some(a), Some(b)) = (assigned_var_then, assigned_var_else) {
if a == b {
self.variable_map.insert(a, result_val);
}
}
Ok(result_val)
}
/// Extract assigned variable name from an AST node if it represents an assignment to a variable.
/// Handles direct Assignment and Program with trailing single-statement Assignment.
fn extract_assigned_var(ast: &ASTNode) -> Option<String> {
match ast {
ASTNode::Assignment { target, .. } => {
if let ASTNode::Variable { name, .. } = target.as_ref() { Some(name.clone()) } else { None }
}
ASTNode::Program { statements, .. } => {
// Inspect the last statement as the resulting value of the block
statements.last().and_then(|st| Self::extract_assigned_var(st))
}
_ => None,
}
}
/// Build a loop statement: loop(condition) { body }
pub(super) fn build_loop_statement(&mut self, condition: ASTNode, body: Vec<ASTNode>) -> Result<ValueId, String> {
// Use the specialized LoopBuilder for proper SSA loop construction
let mut loop_builder = super::loop_builder::LoopBuilder::new(self);
loop_builder.build_loop(condition, body)
}
/// Build a try/catch statement
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> {
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();
// Set up exception handler for the try block (before we enter it)
if let Some(catch_clause) = catch_clauses.first() {
let exception_value = self.value_gen.next();
// Register catch handler for exceptions that may occur in try block
self.emit_instruction(MirInstruction::Catch {
exception_type: catch_clause.exception_type.clone(),
exception_value,
handler_bb: catch_block,
})?;
}
// Jump to try block
self.emit_instruction(MirInstruction::Jump { target: try_block })?;
// Build 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)?;
// Normal completion of try block - jump to finally or exit (if not already terminated)
if !self.is_current_block_terminated() {
let next_target = finally_block.unwrap_or(exit_block);
self.emit_instruction(MirInstruction::Jump { target: next_target })?;
}
// Build catch block (reachable via exception handling)
self.start_new_block(catch_block)?;
// Handle catch clause
if let Some(catch_clause) = catch_clauses.first() {
// Build catch body
let catch_ast = ASTNode::Program {
statements: catch_clause.body.clone(),
span: crate::ast::Span::unknown(),
};
self.build_expression(catch_ast)?;
}
// Catch completion - jump to finally or exit (if not already terminated)
if !self.is_current_block_terminated() {
let next_target = finally_block.unwrap_or(exit_block);
self.emit_instruction(MirInstruction::Jump { target: next_target })?;
}
// Build finally block if present
if let (Some(finally_block_id), Some(finally_statements)) = (finally_block, finally_body) {
self.start_new_block(finally_block_id)?;
let finally_ast = ASTNode::Program {
statements: finally_statements,
span: crate::ast::Span::unknown(),
};
self.build_expression(finally_ast)?;
self.emit_instruction(MirInstruction::Jump { target: exit_block })?;
}
// Create exit block
self.start_new_block(exit_block)?;
// Return void for now (in a complete implementation, would use phi for try/catch values)
let result = self.value_gen.next();
self.emit_instruction(MirInstruction::Const {
dst: result,
value: ConstValue::Void,
})?;
Ok(result)
}
/// Check if the current basic block is terminated
pub(super) fn is_current_block_terminated(&self) -> bool {
if let (Some(block_id), Some(ref function)) = (self.current_block, &self.current_function) {
if let Some(block) = function.get_block(block_id) {
return block.is_terminated();
}
}
false
}
}

View File

@ -1,205 +0,0 @@
/*!
* MIR Builder Core - Core builder functionality
*
* Contains the MirBuilder struct and core instruction emission functionality
*/
use super::*;
use crate::ast::ASTNode;
use std::collections::HashMap;
use std::collections::HashSet;
pub fn builder_debug_enabled() -> bool {
std::env::var("NYASH_BUILDER_DEBUG").is_ok()
}
pub fn builder_debug_log(msg: &str) {
if builder_debug_enabled() {
eprintln!("[BUILDER] {}", msg);
}
}
/// MIR builder for converting AST to SSA form
pub struct MirBuilder {
/// Current module being built
pub(super) current_module: Option<MirModule>,
/// Current function being built
pub(super) current_function: Option<MirFunction>,
/// Current basic block being built
pub(super) current_block: Option<BasicBlockId>,
/// Value ID generator
pub(super) value_gen: ValueIdGenerator,
/// Basic block ID generator
pub(super) block_gen: BasicBlockIdGenerator,
/// Variable name to ValueId mapping (for SSA conversion)
pub(super) variable_map: HashMap<String, ValueId>,
/// Pending phi functions to be inserted
#[allow(dead_code)]
pub(super) pending_phis: Vec<(BasicBlockId, ValueId, String)>,
/// Origin tracking for simple optimizations (e.g., object.method after new)
/// Maps a ValueId to the class name if it was produced by NewBox of that class
pub(super) value_origin_newbox: HashMap<ValueId, String>,
/// Names of user-defined boxes declared in the current module
pub(super) user_defined_boxes: HashSet<String>,
/// Weak field registry: BoxName -> {weak field names}
pub(super) weak_fields_by_box: HashMap<String, HashSet<String>>,
/// Remember class of object fields after assignments: (base_id, field) -> class_name
pub(super) field_origin_class: HashMap<(ValueId, String), String>,
}
impl MirBuilder {
pub fn new() -> Self {
Self {
current_module: None,
current_function: None,
current_block: None,
value_gen: ValueIdGenerator::new(),
block_gen: BasicBlockIdGenerator::new(),
variable_map: HashMap::new(),
pending_phis: Vec::new(),
value_origin_newbox: HashMap::new(),
user_defined_boxes: HashSet::new(),
weak_fields_by_box: HashMap::new(),
field_origin_class: HashMap::new(),
}
}
pub(super) fn emit_type_check(&mut self, value: ValueId, expected_type: String) -> Result<ValueId, String> {
let target_value = self.value_gen.next_value_id();
let instruction = MirInstruction::TypeOp {
dst: target_value,
operation: super::TypeOpKind::Check,
operand: value,
type_info: expected_type,
effects: EffectMask::new(Effect::ReadOnly),
};
self.emit_instruction(instruction)?;
Ok(target_value)
}
pub(super) fn emit_cast(&mut self, value: ValueId, target_type: super::MirType) -> Result<ValueId, String> {
let target_value = self.value_gen.next_value_id();
let instruction = MirInstruction::TypeOp {
dst: target_value,
operation: super::TypeOpKind::Cast,
operand: value,
type_info: format!("{:?}", target_type),
effects: EffectMask::new(Effect::ReadOnly),
};
self.emit_instruction(instruction)?;
Ok(target_value)
}
pub(super) fn emit_weak_new(&mut self, box_val: ValueId) -> Result<ValueId, String> {
let weak_ref = self.value_gen.next_value_id();
let instruction = MirInstruction::WeakNew {
dst: weak_ref,
source: box_val,
effects: EffectMask::new(Effect::Pure),
};
self.emit_instruction(instruction)?;
Ok(weak_ref)
}
pub(super) fn emit_weak_load(&mut self, weak_ref: ValueId) -> Result<ValueId, String> {
let loaded_value = self.value_gen.next_value_id();
let instruction = MirInstruction::WeakLoad {
dst: loaded_value,
weak_ref,
effects: EffectMask::new(Effect::ReadOnly),
};
self.emit_instruction(instruction)?;
Ok(loaded_value)
}
pub(super) fn emit_barrier_read(&mut self, ptr: ValueId) -> Result<(), String> {
let instruction = MirInstruction::BarrierRead {
ptr,
effects: EffectMask::new(Effect::SideEffect),
};
self.emit_instruction(instruction)?;
Ok(())
}
pub(super) fn emit_barrier_write(&mut self, ptr: ValueId) -> Result<(), String> {
let instruction = MirInstruction::BarrierWrite {
ptr,
effects: EffectMask::new(Effect::SideEffect),
};
self.emit_instruction(instruction)?;
Ok(())
}
pub(super) fn emit_instruction(&mut self, instruction: MirInstruction) -> Result<(), String> {
// Ensure we have a current function to emit into
if self.current_function.is_none() {
return Err("Cannot emit instruction without current function".to_string());
}
// Ensure we have a current block to emit into
if self.current_block.is_none() {
return Err("Cannot emit instruction without current block".to_string());
}
let current_block_id = self.current_block.unwrap();
// Get a mutable reference to the current function
let current_function = self.current_function.as_mut().unwrap();
// Ensure the block exists
self.ensure_block_exists(current_block_id)?;
// Add instruction to current block
if let Some(block) = current_function.basic_blocks.get_mut(&current_block_id) {
block.instructions.push(instruction);
} else {
return Err(format!("Block {:?} not found in current function", current_block_id));
}
Ok(())
}
pub(super) fn ensure_block_exists(&mut self, block_id: BasicBlockId) -> Result<(), String> {
let current_function = self.current_function.as_mut()
.ok_or("No current function")?;
if !current_function.basic_blocks.contains_key(&block_id) {
current_function.basic_blocks.insert(block_id, BasicBlock {
id: block_id,
instructions: Vec::new(),
});
}
Ok(())
}
pub(super) fn start_new_block(&mut self, block_id: BasicBlockId) -> Result<(), String> {
// Ensure the block exists in the current function
self.ensure_block_exists(block_id)?;
// Set as current block
self.current_block = Some(block_id);
Ok(())
}
}

View File

@ -1,621 +0,0 @@
/*!
* MIR Builder Expressions - Expression AST node conversion
*
* Handles conversion of expression AST nodes to MIR instructions
*/
use super::*;
use crate::ast::{ASTNode, LiteralValue, BinaryOperator};
/// Binary operation type classification
enum BinaryOpType {
Arithmetic(BinaryOp),
Comparison(CompareOp),
}
impl MirBuilder {
/// Build a literal value into MIR
pub(super) fn build_literal(&mut self, literal: LiteralValue) -> Result<ValueId, String> {
let const_value = match literal {
LiteralValue::Integer(n) => ConstValue::Integer(n),
LiteralValue::Float(f) => ConstValue::Float(f),
LiteralValue::String(s) => ConstValue::String(s),
LiteralValue::Bool(b) => ConstValue::Bool(b),
LiteralValue::Null => ConstValue::Null,
LiteralValue::Void => ConstValue::Void,
};
let dst = self.value_gen.next();
self.emit_instruction(MirInstruction::Const {
dst,
value: const_value,
})?;
Ok(dst)
}
/// Build a binary operation
pub(super) fn build_binary_op(&mut self, left: ASTNode, operator: BinaryOperator, right: ASTNode) -> Result<ValueId, String> {
let lhs = self.build_expression(left)?;
let rhs = self.build_expression(right)?;
let dst = self.value_gen.next();
let mir_op = self.convert_binary_operator(operator)?;
match mir_op {
// Arithmetic operations
BinaryOpType::Arithmetic(op) => {
self.emit_instruction(MirInstruction::BinOp {
dst, op, lhs, rhs
})?;
},
// Comparison operations
BinaryOpType::Comparison(op) => {
self.emit_instruction(MirInstruction::Compare {
dst, op, lhs, rhs
})?;
},
}
Ok(dst)
}
/// Build a unary operation
pub(super) fn build_unary_op(&mut self, operator: String, operand: ASTNode) -> Result<ValueId, String> {
let operand_val = self.build_expression(operand)?;
let dst = self.value_gen.next();
let mir_op = self.convert_unary_operator(operator)?;
self.emit_instruction(MirInstruction::UnaryOp {
dst,
op: mir_op,
operand: operand_val,
})?;
Ok(dst)
}
/// Convert AST binary operator to MIR operator
fn convert_binary_operator(&self, op: BinaryOperator) -> Result<BinaryOpType, String> {
match op {
BinaryOperator::Add => Ok(BinaryOpType::Arithmetic(BinaryOp::Add)),
BinaryOperator::Subtract => Ok(BinaryOpType::Arithmetic(BinaryOp::Sub)),
BinaryOperator::Multiply => Ok(BinaryOpType::Arithmetic(BinaryOp::Mul)),
BinaryOperator::Divide => Ok(BinaryOpType::Arithmetic(BinaryOp::Div)),
BinaryOperator::Modulo => Ok(BinaryOpType::Arithmetic(BinaryOp::Mod)),
BinaryOperator::Equal => Ok(BinaryOpType::Comparison(CompareOp::Eq)),
BinaryOperator::NotEqual => Ok(BinaryOpType::Comparison(CompareOp::Ne)),
BinaryOperator::Less => Ok(BinaryOpType::Comparison(CompareOp::Lt)),
BinaryOperator::LessEqual => Ok(BinaryOpType::Comparison(CompareOp::Le)),
BinaryOperator::Greater => Ok(BinaryOpType::Comparison(CompareOp::Gt)),
BinaryOperator::GreaterEqual => Ok(BinaryOpType::Comparison(CompareOp::Ge)),
BinaryOperator::And => Ok(BinaryOpType::Arithmetic(BinaryOp::And)),
BinaryOperator::Or => Ok(BinaryOpType::Arithmetic(BinaryOp::Or)),
}
}
/// Convert AST unary operator to MIR operator
fn convert_unary_operator(&self, op: String) -> Result<UnaryOp, String> {
match op.as_str() {
"-" => Ok(UnaryOp::Neg),
"!" | "not" => Ok(UnaryOp::Not),
"~" => Ok(UnaryOp::BitNot),
_ => Err(format!("Unsupported unary operator: {}", op)),
}
}
/// Build variable access
pub(super) fn build_variable_access(&mut self, name: String) -> Result<ValueId, String> {
if let Some(&value_id) = self.variable_map.get(&name) {
Ok(value_id)
} else {
Err(format!("Undefined variable: {}", name))
}
}
/// Build field access
pub(super) fn build_field_access(&mut self, object: ASTNode, field: String) -> Result<ValueId, String> {
// Clone the object before building expression if we need to check it later
let object_clone = object.clone();
// First, build the object expression to get its ValueId
let object_value = self.build_expression(object)?;
// Get the field from the object using RefGet
let field_val = self.value_gen.next();
self.emit_instruction(MirInstruction::RefGet {
dst: field_val,
reference: object_value,
field: field.clone(),
})?;
// If we recorded origin class for this field on this base object, propagate it to this value id
if let Some(class_name) = self.field_origin_class.get(&(object_value, field.clone())).cloned() {
self.value_origin_newbox.insert(field_val, class_name);
}
// If we can infer the box type and the field is weak, emit WeakLoad (+ optional barrier)
let mut inferred_class: Option<String> = self.value_origin_newbox.get(&object_value).cloned();
// Fallback: if the object is a nested field access like (X.Y).Z, consult recorded field origins for X.Y
if inferred_class.is_none() {
if let ASTNode::FieldAccess { object: inner_obj, field: ref parent_field, .. } = object_clone {
// Build inner base to get a stable id and consult mapping
if let Ok(base_id) = self.build_expression(*inner_obj.clone()) {
if let Some(cls) = self.field_origin_class.get(&(base_id, parent_field.clone())) {
inferred_class = Some(cls.clone());
}
}
}
}
if let Some(class_name) = inferred_class {
if let Some(weak_set) = self.weak_fields_by_box.get(&class_name) {
if weak_set.contains(&field) {
// Barrier (read) PoC
let _ = self.emit_barrier_read(field_val);
// WeakLoad
let loaded = self.emit_weak_load(field_val)?;
return Ok(loaded);
}
}
}
Ok(field_val)
}
/// Build function call
pub(super) fn build_function_call(&mut self, name: String, args: Vec<ASTNode>) -> Result<ValueId, String> {
// Minimal TypeOp wiring via function-style: isType(value, "Type"), asType(value, "Type")
if (name == "isType" || name == "asType") && args.len() == 2 {
if let Some(type_name) = Self::extract_string_literal(&args[1]) {
let val = self.build_expression(args[0].clone())?;
let ty = Self::parse_type_name_to_mir(&type_name);
let dst = self.value_gen.next();
let op = if name == "isType" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast };
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: val, ty })?;
return Ok(dst);
}
}
// Build argument values
let mut arg_values = Vec::new();
for arg in args {
arg_values.push(self.build_expression(arg)?);
}
let dst = self.value_gen.next();
// For now, treat all function calls as Box method calls
self.emit_instruction(MirInstruction::Call {
dst,
function: name,
arguments: arg_values,
})?;
Ok(dst)
}
/// Parse type name string to MIR type
fn parse_type_name_to_mir(name: &str) -> super::MirType {
match name {
"Integer" | "Int" | "I64" => super::MirType::Integer,
"Float" | "F64" => super::MirType::Float,
"Bool" | "Boolean" => super::MirType::Bool,
"String" => super::MirType::String,
"Void" | "Unit" => super::MirType::Void,
other => super::MirType::Box(other.to_string()),
}
}
/// Extract string literal from AST node if possible
/// Supports: Literal("Type") and new StringBox("Type")
fn extract_string_literal(node: &ASTNode) -> Option<String> {
let mut cur = node;
loop {
match cur {
ASTNode::Literal { value: crate::ast::LiteralValue::String(s), .. } => return Some(s.clone()),
ASTNode::New { class, arguments, .. } if class == "StringBox" && arguments.len() == 1 => {
cur = &arguments[0];
continue;
}
_ => return None,
}
}
}
/// Build me expression
pub(super) fn build_me_expression(&mut self) -> Result<ValueId, String> {
// If lowering a method/birth function, "me" should be a parameter
if let Some(id) = self.variable_map.get("me").cloned() {
return Ok(id);
}
// Fallback: use a symbolic constant (legacy behavior)
let me_value = self.value_gen.next();
self.emit_instruction(MirInstruction::Const {
dst: me_value,
value: ConstValue::String("__me__".to_string()),
})?;
// Register a stable mapping so subsequent 'me' resolves to the same ValueId
self.variable_map.insert("me".to_string(), me_value);
Ok(me_value)
}
/// Build method call: object.method(arguments)
pub(super) fn build_method_call(&mut self, object: ASTNode, method: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> {
// Minimal TypeOp wiring via method-style syntax: value.is("Type") / value.as("Type")
if (method == "is" || method == "as") && arguments.len() == 1 {
if let Some(type_name) = Self::extract_string_literal(&arguments[0]) {
// Build the object expression
let object_value = self.build_expression(object.clone())?;
// Map string to MIR type
let mir_ty = Self::parse_type_name_to_mir(&type_name);
let dst = self.value_gen.next();
let op = if method == "is" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast };
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: object_value, ty: mir_ty })?;
return Ok(dst);
}
}
// ExternCall判定はobjectの変数解決より先に行う未定義変数で落とさない
if let ASTNode::Variable { name: object_name, .. } = object.clone() {
// Build argument expressions first (externはobject自体を使わない)
let mut arg_values = Vec::new();
for arg in &arguments {
arg_values.push(self.build_expression(arg.clone())?);
}
match (object_name.as_str(), method.as_str()) {
("console", "log") => {
self.emit_instruction(MirInstruction::ExternCall {
dst: None,
iface_name: "env.console".to_string(),
method_name: "log".to_string(),
args: arg_values,
effects: EffectMask::IO,
})?;
let void_id = self.value_gen.next();
self.emit_instruction(MirInstruction::Const { dst: void_id, value: ConstValue::Void })?;
return Ok(void_id);
},
("canvas", "fillRect") | ("canvas", "fillText") => {
self.emit_instruction(MirInstruction::ExternCall {
dst: None,
iface_name: "env.canvas".to_string(),
method_name: method,
args: arg_values,
effects: EffectMask::IO,
})?;
let void_id = self.value_gen.next();
self.emit_instruction(MirInstruction::Const { dst: void_id, value: ConstValue::Void })?;
return Ok(void_id);
},
_ => {}
}
}
// Build the object expression
let object_value = self.build_expression(object.clone())?;
// Secondary interception for is/as in case early path did not trigger
if (method == "is" || method == "as") && arguments.len() == 1 {
if let Some(type_name) = Self::extract_string_literal(&arguments[0]) {
let mir_ty = Self::parse_type_name_to_mir(&type_name);
let dst = self.value_gen.next();
let op = if method == "is" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast };
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: object_value, ty: mir_ty })?;
return Ok(dst);
}
}
// Build argument expressions
let mut arg_values = Vec::new();
for arg in &arguments {
arg_values.push(self.build_expression(arg.clone())?);
}
// Create result value
let result_id = self.value_gen.next();
// Optimization: If the object is a direct `new ClassName(...)`, lower to a direct Call
if let ASTNode::New { class, .. } = object {
// Build function name and only lower to Call if the function exists (user-defined)
let func_name = format!("{}.{}{}", class, method, format!("/{}", arg_values.len()));
let can_lower = self.user_defined_boxes.contains(&class)
&& if let Some(ref module) = self.current_module { module.functions.contains_key(&func_name) } else { false };
if can_lower {
let func_val = self.value_gen.next();
self.emit_instruction(MirInstruction::Const { dst: func_val, value: ConstValue::String(func_name) })?;
let mut call_args = Vec::with_capacity(arg_values.len() + 1);
call_args.push(object_value);
call_args.extend(arg_values);
self.emit_instruction(MirInstruction::Call {
dst: Some(result_id),
func: func_val,
args: call_args,
effects: EffectMask::READ.add(Effect::ReadHeap),
})?;
return Ok(result_id);
}
// else fall through to BoxCall below
} else {
// If the object originates from a NewBox in this function, we can lower to Call as well
if let Some(class_name) = self.value_origin_newbox.get(&object_value).cloned() {
let func_name = format!("{}.{}{}", class_name, method, format!("/{}", arg_values.len()));
let can_lower = self.user_defined_boxes.contains(&class_name)
&& if let Some(ref module) = self.current_module { module.functions.contains_key(&func_name) } else { false };
if can_lower {
let func_val = self.value_gen.next();
self.emit_instruction(MirInstruction::Const { dst: func_val, value: ConstValue::String(func_name) })?;
let mut call_args = Vec::with_capacity(arg_values.len() + 1);
call_args.push(object_value);
call_args.extend(arg_values);
self.emit_instruction(MirInstruction::Call {
dst: Some(result_id),
func: func_val,
args: call_args,
effects: EffectMask::READ.add(Effect::ReadHeap),
})?;
return Ok(result_id);
}
}
}
// Fallback: Emit a BoxCall instruction for regular or plugin/builtin method calls
self.emit_instruction(MirInstruction::BoxCall {
dst: Some(result_id),
box_val: object_value,
method,
args: arg_values,
effects: EffectMask::READ.add(Effect::ReadHeap), // Method calls may have side effects
})?;
Ok(result_id)
}
/// Build from expression: from Parent.method(arguments)
pub(super) fn build_from_expression(&mut self, parent: String, method: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> {
// Build argument expressions
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.build_expression(arg)?);
}
// Get parent box's me value if available
if let Some(&parent_me) = self.variable_map.get(&format!("{}.__me__", parent)) {
// Emit from call with parent's me
let result = self.value_gen.next();
self.emit_instruction(MirInstruction::Call {
dst: Some(result),
func: self.value_gen.next(), // Placeholder for from resolution
args: vec![parent_me], // Include parent's me plus other args
effects: EffectMask::READ.add(Effect::ReadHeap),
})?;
Ok(result)
} else {
// Fallback behavior without proper parent context
let result = self.value_gen.next();
Ok(result)
}
}
/// Build expression from AST
pub(super) fn build_expression(&mut self, ast: ASTNode) -> Result<ValueId, String> {
match ast {
ASTNode::Literal { value, .. } => {
self.build_literal(value)
},
ASTNode::BinaryOp { left, operator, right, .. } => {
self.build_binary_op(*left, operator, *right)
},
ASTNode::UnaryOp { operator, operand, .. } => {
let op_string = match operator {
crate::ast::UnaryOperator::Minus => "-".to_string(),
crate::ast::UnaryOperator::Not => "not".to_string(),
};
self.build_unary_op(op_string, *operand)
},
ASTNode::Variable { name, .. } => {
self.build_variable_access(name.clone())
},
ASTNode::Me { .. } => {
self.build_me_expression()
},
ASTNode::MethodCall { object, method, arguments, .. } => {
// Early TypeOp lowering for method-style is()/as()
if (method == "is" || method == "as") && arguments.len() == 1 {
if let Some(type_name) = Self::extract_string_literal(&arguments[0]) {
let obj_val = self.build_expression(*object.clone())?;
let ty = Self::parse_type_name_to_mir(&type_name);
let dst = self.value_gen.next();
let op = if method == "is" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast };
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: obj_val, ty })?;
return Ok(dst);
}
}
self.build_method_call(*object.clone(), method.clone(), arguments.clone())
},
ASTNode::FromCall { parent, method, arguments, .. } => {
self.build_from_expression(parent.clone(), method.clone(), arguments.clone())
},
ASTNode::Assignment { target, value, .. } => {
// Check if target is a field access for RefSet
if let ASTNode::FieldAccess { object, field, .. } = target.as_ref() {
self.build_field_assignment(*object.clone(), field.clone(), *value.clone())
} else if let ASTNode::Variable { name, .. } = target.as_ref() {
self.build_assignment(name.clone(), *value.clone())
} else {
Err("Invalid assignment target".to_string())
}
},
ASTNode::FunctionCall { name, arguments, .. } => {
self.build_function_call(name.clone(), arguments.clone())
},
ASTNode::FieldAccess { object, field, .. } => {
self.build_field_access(*object.clone(), field.clone())
},
ASTNode::ArrayAccess { array, index, .. } => {
// Build array and index expressions
let array_val = self.build_expression(*array)?;
let index_val = self.build_expression(*index)?;
// Generate ArrayGet instruction
let result = self.value_gen.next();
self.emit_instruction(MirInstruction::ArrayGet {
dst: result,
array: array_val,
index: index_val,
effects: EffectMask::READ.add(Effect::ReadHeap),
})?;
Ok(result)
},
ASTNode::ArrayLiteral { elements, .. } => {
// Create new ArrayBox
let array_val = self.build_new_expression("ArrayBox".to_string(), vec![])?;
// Add each element
for elem in elements {
let elem_val = self.build_expression(elem)?;
// array.push(elem)
self.emit_instruction(MirInstruction::BoxCall {
dst: None,
box_val: array_val,
method: "push".to_string(),
args: vec![elem_val],
effects: EffectMask::WRITE.add(Effect::WriteHeap),
})?;
}
Ok(array_val)
},
ASTNode::New { class, arguments, .. } => {
self.build_new_expression(class.clone(), arguments.clone())
},
ASTNode::Await { expression, .. } => {
self.build_await_expression(*expression)
},
_ => {
Err(format!("Unsupported expression type: {:?}", ast))
}
}
}
/// Build assignment: variable = value
pub(super) fn build_assignment(&mut self, var_name: String, value: ASTNode) -> Result<ValueId, String> {
let value_id = self.build_expression(value)?;
// In SSA form, each assignment creates a new value
self.variable_map.insert(var_name.clone(), value_id);
Ok(value_id)
}
/// Build field assignment: object.field = value
pub(super) fn build_field_assignment(&mut self, object: ASTNode, field: String, value: ASTNode) -> Result<ValueId, String> {
// Build the object and value expressions
let object_value = self.build_expression(object)?;
let mut value_result = self.build_expression(value)?;
// If we can infer the box type and the field is weak, create WeakRef before store
if let Some(class_name) = self.value_origin_newbox.get(&object_value).cloned() {
if let Some(weak_set) = self.weak_fields_by_box.get(&class_name) {
if weak_set.contains(&field) {
value_result = self.emit_weak_new(value_result)?;
}
}
}
// Set the field using RefSet
self.emit_instruction(MirInstruction::RefSet {
reference: object_value,
field: field.clone(),
value: value_result,
})?;
// Emit a write barrier for weak fields (PoC)
if let Some(class_name) = self.value_origin_newbox.get(&object_value).cloned() {
if let Some(weak_set) = self.weak_fields_by_box.get(&class_name) {
if weak_set.contains(&field) {
let _ = self.emit_barrier_write(value_result);
}
}
}
// Record origin class for this field (if the value originates from NewBox of a known class)
if let Some(class_name) = self.value_origin_newbox.get(&value_result).cloned() {
self.field_origin_class.insert((object_value, field.clone()), class_name);
}
// Return the assigned value
Ok(value_result)
}
/// Build new expression: new ClassName(arguments)
pub(super) fn build_new_expression(&mut self, class: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> {
// Phase 9.78a: Unified Box creation using NewBox instruction
// First, evaluate all arguments to get their ValueIds
let mut arg_values = Vec::new();
for arg in arguments {
let arg_value = self.build_expression(arg)?;
arg_values.push(arg_value);
}
// Generate the destination ValueId
let dst = self.value_gen.next();
// Emit NewBox instruction for all Box types
// VM will handle optimization for basic types internally
self.emit_instruction(MirInstruction::NewBox {
dst,
box_type: class.clone(),
args: arg_values.clone(),
})?;
// Record origin for optimization: dst was created by NewBox of class
self.value_origin_newbox.insert(dst, class);
// Immediately call birth(...) on the created instance to run constructor semantics.
// birth typically returns void; we don't capture the result here (dst: None)
self.emit_instruction(MirInstruction::BoxCall {
dst: None,
box_val: dst,
method: "birth".to_string(),
args: arg_values,
effects: EffectMask::READ.add(Effect::ReadHeap),
})?;
Ok(dst)
}
/// Build await expression: await expression
pub(super) fn build_await_expression(&mut self, expression: ASTNode) -> Result<ValueId, String> {
// Evaluate the expression (should be a Future)
let future_value = self.build_expression(expression)?;
// Create result value for the await
let result_id = self.value_gen.next();
// Emit await instruction
self.emit_instruction(MirInstruction::Await {
dst: result_id,
future: future_value,
effects: EffectMask::READ.add(Effect::Async),
})?;
Ok(result_id)
}
}

View File

@ -1,27 +0,0 @@
/*!
* MIR Builder Module - Modular AST to MIR conversion
*
* This module contains the refactored MIR builder split into focused sub-modules:
*
* - `core`: Core builder functionality and instruction emission
* - `expressions`: Expression AST node conversion
* - `statements`: Statement AST node conversion
* - `control_flow`: Control flow constructs (if, loop, try-catch)
* - `box_handlers`: Box-related operations (new, declarations)
*/
pub mod core;
pub mod expressions;
pub mod statements;
pub mod control_flow;
pub mod box_handlers;
// Re-export the main builder struct and key functionality
pub use self::core::MirBuilder;
// Re-export commonly used types from the parent module
pub use super::{
MirInstruction, BasicBlock, BasicBlockId, MirFunction, MirModule,
FunctionSignature, ValueId, ConstValue, BinaryOp, UnaryOp, CompareOp,
MirType, EffectMask, Effect, BasicBlockIdGenerator, ValueIdGenerator
};

View File

@ -1,165 +0,0 @@
/*!
* MIR Builder Statements - Statement AST node conversion
*
* Handles conversion of statement AST nodes to MIR instructions
*/
use super::*;
use crate::ast::ASTNode;
impl MirBuilder {
/// Build print statement - converts to console output
pub(super) fn build_print_statement(&mut self, expression: ASTNode) -> Result<ValueId, String> {
builder_debug_log("enter build_print_statement");
// 根治: print(isType(...)) / print(asType(...)) / print(obj.is(...)) / print(obj.as(...)) は必ずTypeOpを先に生成してからprintする
match &expression {
ASTNode::FunctionCall { name, arguments, .. } if (name == "isType" || name == "asType") && arguments.len() == 2 => {
builder_debug_log("pattern: print(FunctionCall isType|asType)");
if let Some(type_name) = Self::extract_string_literal(&arguments[1]) {
builder_debug_log(&format!("extract_string_literal OK: {}", type_name));
let val = self.build_expression(arguments[0].clone())?;
let ty = Self::parse_type_name_to_mir(&type_name);
let dst = self.value_gen.next();
let op = if name == "isType" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast };
builder_debug_log(&format!("emit TypeOp {:?} value={} dst= {}", op, val, dst));
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: val, ty })?;
self.emit_instruction(MirInstruction::Print { value: dst, effects: EffectMask::PURE.add(Effect::Io) })?;
return Ok(dst);
} else {
builder_debug_log("extract_string_literal FAIL");
}
}
ASTNode::MethodCall { object, method, arguments, .. } if (method == "is" || method == "as") && arguments.len() == 1 => {
builder_debug_log("pattern: print(MethodCall is|as)");
if let Some(type_name) = Self::extract_string_literal(&arguments[0]) {
builder_debug_log(&format!("extract_string_literal OK: {}", type_name));
let obj_val = self.build_expression(*object.clone())?;
let ty = Self::parse_type_name_to_mir(&type_name);
let dst = self.value_gen.next();
let op = if method == "is" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast };
builder_debug_log(&format!("emit TypeOp {:?} obj={} dst= {}", op, obj_val, dst));
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: obj_val, ty })?;
self.emit_instruction(MirInstruction::Print { value: dst, effects: EffectMask::PURE.add(Effect::Io) })?;
return Ok(dst);
} else {
builder_debug_log("extract_string_literal FAIL");
}
}
_ => {}
}
let value = self.build_expression(expression)?;
builder_debug_log(&format!("fallback print value={}", value));
// For now, use a special Print instruction (minimal scope)
self.emit_instruction(MirInstruction::Print {
value,
effects: EffectMask::PURE.add(Effect::Io),
})?;
// Return the value that was printed
Ok(value)
}
/// Build a block of statements
pub(super) fn build_block(&mut self, statements: Vec<ASTNode>) -> Result<ValueId, String> {
let mut last_value = None;
for statement in statements {
last_value = Some(self.build_expression(statement)?);
}
// Return last value or void
Ok(last_value.unwrap_or_else(|| {
let void_val = self.value_gen.next();
self.emit_instruction(MirInstruction::Const {
dst: void_val,
value: ConstValue::Void,
}).unwrap();
void_val
}))
}
/// Build local variable declarations with optional initial values
pub(super) fn build_local_statement(&mut self, variables: Vec<String>, initial_values: Vec<Option<Box<ASTNode>>>) -> Result<ValueId, String> {
let mut last_value = None;
// Process each variable declaration
for (i, var_name) in variables.iter().enumerate() {
let value_id = if i < initial_values.len() && initial_values[i].is_some() {
// Variable has initial value - evaluate it
let init_expr = initial_values[i].as_ref().unwrap();
self.build_expression(*init_expr.clone())?
} else {
// No initial value - do not emit a const; leave uninitialized until assigned
// Use a fresh SSA id only for name binding; consumers should not use it before assignment
self.value_gen.next()
};
// Register variable in SSA form
self.variable_map.insert(var_name.clone(), value_id);
last_value = Some(value_id);
}
// Return the last bound value id (no emission); callers shouldn't rely on this value
Ok(last_value.unwrap_or_else(|| {
// create a dummy id without emission
self.value_gen.next()
}))
}
/// Build return statement
pub(super) fn build_return_statement(&mut self, value: Option<Box<ASTNode>>) -> Result<ValueId, String> {
let return_value = if let Some(expr) = value {
self.build_expression(*expr)?
} else {
// Return void if no value specified
let void_dst = self.value_gen.next();
self.emit_instruction(MirInstruction::Const {
dst: void_dst,
value: ConstValue::Void,
})?;
void_dst
};
// Emit return instruction
self.emit_instruction(MirInstruction::Return {
value: Some(return_value),
})?;
Ok(return_value)
}
/// Build a throw statement
pub(super) fn build_throw_statement(&mut self, expression: ASTNode) -> Result<ValueId, String> {
let exception_value = self.build_expression(expression)?;
// Emit throw instruction with PANIC effect (this is a terminator)
self.emit_instruction(MirInstruction::Throw {
exception: exception_value,
effects: EffectMask::PANIC,
})?;
// Throw doesn't return normally, but we need to return a value for the type system
// We can't add more instructions after throw, so just return the exception value
Ok(exception_value)
}
/// Build nowait statement: nowait variable = expression
pub(super) fn build_nowait_statement(&mut self, variable: String, expression: ASTNode) -> Result<ValueId, String> {
// Evaluate the expression
let expression_value = self.build_expression(expression)?;
// Create a new Future with the evaluated expression as the initial value
let future_id = self.value_gen.next();
self.emit_instruction(MirInstruction::FutureNew {
dst: future_id,
value: expression_value,
})?;
// Store the future in the variable
self.variable_map.insert(variable.clone(), future_id);
Ok(future_id)
}
}