Files
hakorune/src/mir/builder/ops.rs
nyash-codex b92f85f993 refactor(mir): Remove TypeContext legacy fields (Phase 2-3/7)
完全移行→削除の安全順序(Option C)に従い、TypeContext の
deprecated フィールドと sync helpers を完全削除。

⚠️ 危険ゾーン: TypeFactsBox 等の同名フィールドと混同しないよう、
ファイル単位で手作業移行を実施。

## Changes
- Migrated all MirBuilder access sites to type_ctx.* (manual, 40+ files)
- Removed 3 deprecated fields (value_types, value_kinds, value_origin_newbox)
- Removed 2 sync helpers (sync_type_ctx_to_legacy, sync_legacy_to_type_ctx)
- Verified TypeFactsBox, CalleeGuardBox unchanged (no false positives)

## Tests
- cargo test --release --lib: 1029/1033 PASS
- TypeFactsBox integration: PASS (borrowed references unchanged)
- Deprecation warnings: 456 → 255 (-201, -44%)

## Safety Verification
 TypeFactsBox unchanged (still uses &'a BTreeMap borrowed references)
 CalleeGuardBox unchanged
 CalleeResolverBox unchanged
 BoxCompilationContext unchanged

Phase 2 Progress: 3/7 contexts complete (43%)
-  MetadataContext
-  CoreContext
-  TypeContext (this commit)

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-15 23:27:24 +09:00

644 lines
30 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use super::{MirInstruction, MirType, ValueId};
use crate::ast::{ASTNode, BinaryOperator};
use crate::mir::loop_api::LoopBuilderApi; // for current_block()
use crate::mir::{BinaryOp, CompareOp, TypeOpKind, UnaryOp};
// Internal classification for binary operations
#[derive(Debug)]
enum BinaryOpType {
Arithmetic(BinaryOp),
Comparison(CompareOp),
}
impl super::MirBuilder {
// Build a binary operation
pub(super) fn build_binary_op(
&mut self,
left: ASTNode,
operator: BinaryOperator,
right: ASTNode,
) -> Result<ValueId, String> {
// Short-circuit logical ops: lower to control-flow so RHS is evaluated conditionally
if matches!(operator, BinaryOperator::And | BinaryOperator::Or) {
return self.build_logical_shortcircuit(left, operator, right);
}
let lhs_raw = self.build_expression(left)?;
let rhs_raw = self.build_expression(right)?;
// Correctness-first: ensure both operands have block-local definitions
// so they participate in PHI/materialization and avoid use-before-def across
// complex control-flow (e.g., loop headers and nested branches).
let lhs = self
.ensure_slotified_for_use(lhs_raw, "@binop_lhs")
.unwrap_or(lhs_raw);
let rhs = self
.ensure_slotified_for_use(rhs_raw, "@binop_rhs")
.unwrap_or(rhs_raw);
let dst = self.next_value_id();
let mir_op = self.convert_binary_operator(operator)?;
let all_call = std::env::var("NYASH_BUILDER_OPERATOR_BOX_ALL_CALL")
.ok()
.as_deref()
== Some("1");
match mir_op {
// Arithmetic operations
BinaryOpType::Arithmetic(op) => {
// Dev: Lower '+' を演算子ボックス呼び出しに置換既定OFF
let in_add_op = self
.current_function
.as_ref()
.map(|f| f.signature.name.starts_with("AddOperator.apply/"))
.unwrap_or(false);
if matches!(op, crate::mir::BinaryOp::Add)
&& !in_add_op
&& (all_call
|| std::env::var("NYASH_BUILDER_OPERATOR_BOX_ADD_CALL")
.ok()
.as_deref()
== Some("1"))
{
// AddOperator.apply/2(lhs, rhs)
let name = "AddOperator.apply/2".to_string();
self.emit_legacy_call(
Some(dst),
super::builder_calls::CallTarget::Global(name),
vec![lhs, rhs],
)?;
// Phase 196: TypeFacts SSOT - AddOperator call type annotation
// Phase 131-11-E: TypeFacts - classify operand types (Phase 136: use TypeFactsBox)
let type_facts = super::type_facts::TypeFactsBox::new(
&self.type_ctx.value_types,
&self.type_ctx.value_origin_newbox,
);
let lhs_type = type_facts.classify_operand_type(lhs);
let rhs_type = type_facts.classify_operand_type(rhs);
use super::type_facts::OperandTypeClass::*;
match (lhs_type, rhs_type) {
(String, String) => {
// BOTH are strings: result is string
self.type_ctx.value_types
.insert(dst, MirType::Box("StringBox".to_string()));
self.type_ctx.value_origin_newbox
.insert(dst, "StringBox".to_string());
}
(Integer, Integer) | (Integer, Unknown) | (Unknown, Integer) => {
// TypeFact: Integer + anything non-String = Integer
self.type_ctx.value_types.insert(dst, MirType::Integer);
}
(String, Integer) | (Integer, String) => {
// Mixed types: leave as Unknown for use-site coercion
}
(Unknown, Unknown) => {
// Both Unknown: cannot infer
}
(String, Unknown) | (Unknown, String) => {
// One side is String, other is Unknown: cannot infer safely
}
}
} else if all_call {
// Lower other arithmetic ops to operator boxes under ALL flag
let (name, guard_prefix) = match op {
crate::mir::BinaryOp::Sub => ("SubOperator.apply/2", "SubOperator.apply/"),
crate::mir::BinaryOp::Mul => ("MulOperator.apply/2", "MulOperator.apply/"),
crate::mir::BinaryOp::Div => ("DivOperator.apply/2", "DivOperator.apply/"),
crate::mir::BinaryOp::Mod => ("ModOperator.apply/2", "ModOperator.apply/"),
crate::mir::BinaryOp::Shl => ("ShlOperator.apply/2", "ShlOperator.apply/"),
crate::mir::BinaryOp::Shr => ("ShrOperator.apply/2", "ShrOperator.apply/"),
crate::mir::BinaryOp::BitAnd => {
("BitAndOperator.apply/2", "BitAndOperator.apply/")
}
crate::mir::BinaryOp::BitOr => {
("BitOrOperator.apply/2", "BitOrOperator.apply/")
}
crate::mir::BinaryOp::BitXor => {
("BitXorOperator.apply/2", "BitXorOperator.apply/")
}
_ => ("", ""),
};
if !name.is_empty() {
let in_guard = self
.current_function
.as_ref()
.map(|f| f.signature.name.starts_with(guard_prefix))
.unwrap_or(false);
if !in_guard {
self.emit_legacy_call(
Some(dst),
super::builder_calls::CallTarget::Global(name.to_string()),
vec![lhs, rhs],
)?;
// 型注釈: 算術はおおむね整数Addは上で注釈済み
self.type_ctx.value_types.insert(dst, MirType::Integer);
} else {
// guard中は従来のBinOp
if let (Some(func), Some(cur_bb)) =
(self.current_function.as_mut(), self.current_block)
{
crate::mir::ssot::binop_lower::emit_binop_to_dst(
func, cur_bb, dst, op, lhs, rhs,
);
} else {
self.emit_instruction(MirInstruction::BinOp { dst, op, lhs, rhs })?;
}
self.type_ctx.value_types.insert(dst, MirType::Integer);
}
} else {
// 既存の算術経路
if let (Some(func), Some(cur_bb)) =
(self.current_function.as_mut(), self.current_block)
{
crate::mir::ssot::binop_lower::emit_binop_to_dst(
func, cur_bb, dst, op, lhs, rhs,
);
} else {
self.emit_instruction(MirInstruction::BinOp { dst, op, lhs, rhs })?;
}
// Phase 196: TypeFacts SSOT - BinOp type is determined by operands only
// String concatenation is handled at use-site in LLVM lowering
if matches!(op, crate::mir::BinaryOp::Add) {
// Phase 131-11-E: TypeFacts - classify operand types (Phase 136: use TypeFactsBox)
let type_facts = super::type_facts::TypeFactsBox::new(
&self.type_ctx.value_types,
&self.type_ctx.value_origin_newbox,
);
let lhs_type = type_facts.classify_operand_type(lhs);
let rhs_type = type_facts.classify_operand_type(rhs);
use super::type_facts::OperandTypeClass::*;
match (lhs_type, rhs_type) {
(String, String) => {
// BOTH are strings: result is definitely a string
self.type_ctx.value_types
.insert(dst, MirType::Box("StringBox".to_string()));
self.type_ctx.value_origin_newbox
.insert(dst, "StringBox".to_string());
}
(Integer, Integer) | (Integer, Unknown) | (Unknown, Integer) => {
// TypeFact: Integer + anything non-String = Integer
// This handles `counter + 1` where counter might be Unknown
self.type_ctx.value_types.insert(dst, MirType::Integer);
}
(String, Integer) | (Integer, String) => {
// Mixed types: leave as Unknown for use-site coercion
// LLVM backend will handle string concatenation
}
(Unknown, Unknown) => {
// Both Unknown: cannot infer, leave as Unknown
}
(String, Unknown) | (Unknown, String) => {
// One side is String, other is Unknown: cannot infer safely
// Leave as Unknown
}
}
} else {
self.type_ctx.value_types.insert(dst, MirType::Integer);
}
}
} else {
// 既存の算術経路
if let (Some(func), Some(cur_bb)) =
(self.current_function.as_mut(), self.current_block)
{
crate::mir::ssot::binop_lower::emit_binop_to_dst(
func, cur_bb, dst, op, lhs, rhs,
);
} else {
self.emit_instruction(MirInstruction::BinOp { dst, op, lhs, rhs })?;
}
// Phase 196: TypeFacts SSOT - BinOp type is determined by operands only
// String concatenation is handled at use-site in LLVM lowering
if matches!(op, crate::mir::BinaryOp::Add) {
// Check if BOTH operands are known to be strings (TypeFacts)
let lhs_is_str = match self.type_ctx.value_types.get(&lhs) {
Some(MirType::String) => true,
Some(MirType::Box(bt)) if bt == "StringBox" => true,
_ => self
.type_ctx.value_origin_newbox
.get(&lhs)
.map(|s| s == "StringBox")
.unwrap_or(false),
};
let rhs_is_str = match self.type_ctx.value_types.get(&rhs) {
Some(MirType::String) => true,
Some(MirType::Box(bt)) if bt == "StringBox" => true,
_ => self
.type_ctx.value_origin_newbox
.get(&rhs)
.map(|s| s == "StringBox")
.unwrap_or(false),
};
if lhs_is_str && rhs_is_str {
// BOTH are strings: result is definitely a string
self.type_ctx.value_types
.insert(dst, MirType::Box("StringBox".to_string()));
self.type_ctx.value_origin_newbox
.insert(dst, "StringBox".to_string());
} else if !lhs_is_str && !rhs_is_str {
// NEITHER is a string: numeric addition
self.type_ctx.value_types.insert(dst, MirType::Integer);
}
// else: Mixed types (string + int or int + string)
// Leave dst type as Unknown - LLVM will handle coercion at use-site
} else {
self.type_ctx.value_types.insert(dst, MirType::Integer);
}
}
}
// Comparison operations
BinaryOpType::Comparison(op) => {
// Dev: Lower 比較 を演算子ボックス呼び出しに置換既定OFF
let in_cmp_op = self
.current_function
.as_ref()
.map(|f| f.signature.name.starts_with("CompareOperator.apply/"))
.unwrap_or(false);
if !in_cmp_op
&& (all_call
|| std::env::var("NYASH_BUILDER_OPERATOR_BOX_COMPARE_CALL")
.ok()
.as_deref()
== Some("1"))
{
// op名の文字列化
let opname = match op {
CompareOp::Eq => "Eq",
CompareOp::Ne => "Ne",
CompareOp::Lt => "Lt",
CompareOp::Le => "Le",
CompareOp::Gt => "Gt",
CompareOp::Ge => "Ge",
};
let op_const =
crate::mir::builder::emission::constant::emit_string(self, opname);
// そのまま値を渡す(型変換/slot化は演算子内orVMで行う
let name = "CompareOperator.apply/3".to_string();
self.emit_legacy_call(
Some(dst),
super::builder_calls::CallTarget::Global(name),
vec![op_const, lhs, rhs],
)?;
self.type_ctx.value_types.insert(dst, MirType::Bool);
} else {
// 既存の比較経路(安全のための型注釈/slot化含む
let (lhs2_raw, rhs2_raw) = if self
.type_ctx.value_origin_newbox
.get(&lhs)
.map(|s| s == "IntegerBox")
.unwrap_or(false)
&& self
.type_ctx.value_origin_newbox
.get(&rhs)
.map(|s| s == "IntegerBox")
.unwrap_or(false)
{
let li = self.next_value_id();
let ri = self.next_value_id();
self.emit_instruction(MirInstruction::TypeOp {
dst: li,
op: TypeOpKind::Cast,
value: lhs,
ty: MirType::Integer,
})?;
self.emit_instruction(MirInstruction::TypeOp {
dst: ri,
op: TypeOpKind::Cast,
value: rhs,
ty: MirType::Integer,
})?;
(li, ri)
} else {
(lhs, rhs)
};
// Finalize compare operands in current block via LocalSSA
let mut lhs2 = lhs2_raw;
let mut rhs2 = rhs2_raw;
crate::mir::builder::ssa::local::finalize_compare(self, &mut lhs2, &mut rhs2);
crate::mir::builder::emission::compare::emit_to(self, dst, op, lhs2, rhs2)?;
}
}
}
Ok(dst)
}
/// Lower logical && / || with proper short-circuit semantics.
/// Result is a Bool, and RHS is only evaluated if needed.
fn build_logical_shortcircuit(
&mut self,
left: ASTNode,
operator: BinaryOperator,
right: ASTNode,
) -> Result<ValueId, String> {
let is_and = matches!(operator, BinaryOperator::And);
// Evaluate LHS only once and pin to a slot so it can be reused safely across blocks
let lhs_val0 = self.build_expression(left)?;
let lhs_val = self.pin_to_slot(lhs_val0, "@sc_lhs")?;
// Prepare blocks
let then_block = self.next_block_id();
let else_block = self.next_block_id();
let merge_block = self.next_block_id();
// Branch on LHS truthiness (runtime to_bool semantics in interpreter/LLVM)
let mut lhs_cond = self.local_cond(lhs_val);
crate::mir::builder::ssa::local::finalize_branch_cond(self, &mut lhs_cond);
crate::mir::builder::emission::branch::emit_conditional(
self, lhs_cond, then_block, else_block,
)?;
// Record predecessor block for branch (for single-pred PHI materialization)
let pre_branch_bb = self.current_block()?;
// Snapshot variables before entering branches
let pre_if_var_map = self.variable_map.clone();
// ---- THEN branch ----
self.start_new_block(then_block)?;
self.hint_scope_enter(0);
// Reset scope to pre-if snapshot for clean deltas
self.variable_map = pre_if_var_map.clone();
// Materialize all variables at entry via single-pred PHI (correctness-first)
for (name, &pre_v) in pre_if_var_map.iter() {
let phi_val = self.insert_phi_single(pre_branch_bb, pre_v)?;
self.variable_map.insert(name.clone(), phi_val);
}
// AND: then → evaluate RHS and reduce to bool
// OR: then → constant true
let then_value_raw = if is_and {
// Reduce arbitrary RHS to bool by branching on its truthiness and returning consts
let rhs_true = self.next_block_id();
let rhs_false = self.next_block_id();
let rhs_join = self.next_block_id();
let rhs_val = self.build_expression(right.clone())?;
let mut rhs_cond = self.local_cond(rhs_val);
crate::mir::builder::ssa::local::finalize_branch_cond(self, &mut rhs_cond);
crate::mir::builder::emission::branch::emit_conditional(
self, rhs_cond, rhs_true, rhs_false,
)?;
// true path
self.start_new_block(rhs_true)?;
let t_id = crate::mir::builder::emission::constant::emit_bool(self, true);
crate::mir::builder::emission::branch::emit_jump(self, rhs_join)?;
let rhs_true_exit = self.current_block()?;
// false path
self.start_new_block(rhs_false)?;
let f_id = crate::mir::builder::emission::constant::emit_bool(self, false);
crate::mir::builder::emission::branch::emit_jump(self, rhs_join)?;
let rhs_false_exit = self.current_block()?;
// join rhs result into a single bool
self.start_new_block(rhs_join)?;
if let Some(func) = self.current_function.as_mut() {
func.update_cfg();
}
let rhs_bool = self.insert_phi_binary(rhs_true_exit, t_id, rhs_false_exit, f_id)?;
self.type_ctx.value_types.insert(rhs_bool, MirType::Bool);
rhs_bool
} else {
let t_id = crate::mir::builder::emission::constant::emit_bool(self, true);
t_id
};
let then_exit_block = self.current_block()?;
let then_reaches_merge = !self.is_current_block_terminated();
let then_var_map_end = self.variable_map.clone();
if then_reaches_merge {
self.hint_scope_leave(0);
crate::mir::builder::emission::branch::emit_jump(self, merge_block)?;
}
// ---- ELSE branch ----
self.start_new_block(else_block)?;
self.hint_scope_enter(0);
self.variable_map = pre_if_var_map.clone();
// Materialize all variables at entry via single-pred PHI (correctness-first)
for (name, &pre_v) in pre_if_var_map.iter() {
let phi_val = self.insert_phi_single(pre_branch_bb, pre_v)?;
self.variable_map.insert(name.clone(), phi_val);
}
// AND: else → false
// OR: else → evaluate RHS and reduce to bool
let else_value_raw = if is_and {
let f_id = crate::mir::builder::emission::constant::emit_bool(self, false);
f_id
} else {
let rhs_true = self.next_block_id();
let rhs_false = self.next_block_id();
let rhs_join = self.next_block_id();
let rhs_val = self.build_expression(right)?;
let mut rhs_cond = self.local_cond(rhs_val);
crate::mir::builder::ssa::local::finalize_branch_cond(self, &mut rhs_cond);
crate::mir::builder::emission::branch::emit_conditional(
self, rhs_cond, rhs_true, rhs_false,
)?;
// true path
self.start_new_block(rhs_true)?;
let t_id = crate::mir::builder::emission::constant::emit_bool(self, true);
crate::mir::builder::emission::branch::emit_jump(self, rhs_join)?;
let rhs_true_exit = self.current_block()?;
// false path
self.start_new_block(rhs_false)?;
let f_id = crate::mir::builder::emission::constant::emit_bool(self, false);
crate::mir::builder::emission::branch::emit_jump(self, rhs_join)?;
let rhs_false_exit = self.current_block()?;
// join rhs result into a single bool
self.start_new_block(rhs_join)?;
if let Some(func) = self.current_function.as_mut() {
func.update_cfg();
}
let rhs_bool = self.insert_phi_binary(rhs_true_exit, t_id, rhs_false_exit, f_id)?;
self.type_ctx.value_types.insert(rhs_bool, MirType::Bool);
rhs_bool
};
let else_exit_block = self.current_block()?;
let else_reaches_merge = !self.is_current_block_terminated();
let else_var_map_end = self.variable_map.clone();
if else_reaches_merge {
self.hint_scope_leave(0);
crate::mir::builder::emission::branch::emit_jump(self, merge_block)?;
}
// ---- MERGE ----
// Merge block: suppress entry pin copy so PHIs remain first and materialize pins explicitly
self.suppress_next_entry_pin_copy();
self.start_new_block(merge_block)?;
self.push_if_merge(merge_block);
// Result PHI (bool) — consider only reachable predecessors
let mut inputs: Vec<(crate::mir::BasicBlockId, ValueId)> = Vec::new();
if then_reaches_merge {
inputs.push((then_exit_block, then_value_raw));
}
if else_reaches_merge {
inputs.push((else_exit_block, else_value_raw));
}
let result_val = if inputs.len() >= 2 {
if let Some(func) = self.current_function.as_mut() {
func.update_cfg();
}
let dst = self.insert_phi(inputs)?;
self.type_ctx.value_types.insert(dst, MirType::Bool);
dst
} else if inputs.len() == 1 {
inputs[0].1
} else {
// Unreachable fallthrough: synthesize false (defensive)
crate::mir::builder::emission::constant::emit_bool(self, false)
};
// Merge modified vars from both branches back into current scope
self.merge_modified_vars(
then_block,
else_block,
if then_reaches_merge {
Some(then_exit_block)
} else {
None
},
if else_reaches_merge {
Some(else_exit_block)
} else {
None
},
&pre_if_var_map,
&then_var_map_end,
&Some(else_var_map_end),
None,
)?;
self.pop_if_merge();
Ok(result_val)
}
// 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 all_call = std::env::var("NYASH_BUILDER_OPERATOR_BOX_ALL_CALL")
.ok()
.as_deref()
== Some("1");
if all_call {
let (name, guard_prefix, rett) = match operator.as_str() {
"-" => (
"NegOperator.apply/1",
"NegOperator.apply/",
MirType::Integer,
),
"!" | "not" => ("NotOperator.apply/1", "NotOperator.apply/", MirType::Bool),
"~" => (
"BitNotOperator.apply/1",
"BitNotOperator.apply/",
MirType::Integer,
),
_ => ("", "", MirType::Integer),
};
if !name.is_empty() {
let in_guard = self
.current_function
.as_ref()
.map(|f| f.signature.name.starts_with(guard_prefix))
.unwrap_or(false);
let dst = self.next_value_id();
if !in_guard {
self.emit_legacy_call(
Some(dst),
super::builder_calls::CallTarget::Global(name.to_string()),
vec![operand_val],
)?;
self.type_ctx.value_types.insert(dst, rett);
return Ok(dst);
}
}
}
// Core-13 純化: UnaryOp を直接 展開Neg/Not/BitNot
if crate::config::env::mir_core13_pure() {
match operator.as_str() {
"-" => {
let zero = crate::mir::builder::emission::constant::emit_integer(self, 0);
let dst = self.next_value_id();
self.emit_instruction(MirInstruction::BinOp {
dst,
op: crate::mir::BinaryOp::Sub,
lhs: zero,
rhs: operand_val,
})?;
return Ok(dst);
}
"!" | "not" => {
let f = crate::mir::builder::emission::constant::emit_bool(self, false);
let dst = self.next_value_id();
crate::mir::builder::emission::compare::emit_to(
self,
dst,
crate::mir::CompareOp::Eq,
operand_val,
f,
)?;
return Ok(dst);
}
"~" => {
let all1 = crate::mir::builder::emission::constant::emit_integer(self, -1);
let dst = self.next_value_id();
self.emit_instruction(MirInstruction::BinOp {
dst,
op: crate::mir::BinaryOp::BitXor,
lhs: operand_val,
rhs: all1,
})?;
return Ok(dst);
}
_ => {}
}
}
let dst = self.next_value_id();
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 enum or compare
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::Shl => Ok(BinaryOpType::Arithmetic(BinaryOp::Shl)),
BinaryOperator::Shr => Ok(BinaryOpType::Arithmetic(BinaryOp::Shr)),
BinaryOperator::BitAnd => Ok(BinaryOpType::Arithmetic(BinaryOp::BitAnd)),
BinaryOperator::BitOr => Ok(BinaryOpType::Arithmetic(BinaryOp::BitOr)),
BinaryOperator::BitXor => Ok(BinaryOpType::Arithmetic(BinaryOp::BitXor)),
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)),
}
}
}