2025-09-17 07:43:07 +09:00
|
|
|
|
use super::{MirInstruction, MirType, ValueId};
|
2025-09-03 05:04:56 +09:00
|
|
|
|
use crate::ast::{ASTNode, BinaryOperator};
|
2025-11-21 06:25:17 +09:00
|
|
|
|
use crate::mir::loop_api::LoopBuilderApi; // for current_block()
|
2025-09-17 07:43:07 +09:00
|
|
|
|
use crate::mir::{BinaryOp, CompareOp, TypeOpKind, UnaryOp};
|
2025-09-03 05:04:56 +09:00
|
|
|
|
|
|
|
|
|
|
// Internal classification for binary operations
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
|
enum BinaryOpType {
|
|
|
|
|
|
Arithmetic(BinaryOp),
|
|
|
|
|
|
Comparison(CompareOp),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-14 18:55:05 +09:00
|
|
|
|
// Phase 131-11-E: TypeFacts - operand type classification
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
|
enum OperandTypeClass {
|
|
|
|
|
|
String,
|
|
|
|
|
|
Integer,
|
|
|
|
|
|
Unknown,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-03 05:04:56 +09:00
|
|
|
|
impl super::MirBuilder {
|
2025-12-14 18:55:05 +09:00
|
|
|
|
// Phase 131-11-E: TypeFacts - classify operand type for BinOp inference
|
|
|
|
|
|
fn classify_operand_type(&self, vid: ValueId) -> OperandTypeClass {
|
|
|
|
|
|
let result = match self.value_types.get(&vid) {
|
|
|
|
|
|
Some(MirType::String) => OperandTypeClass::String,
|
|
|
|
|
|
Some(MirType::Box(bt)) if bt == "StringBox" => OperandTypeClass::String,
|
|
|
|
|
|
Some(MirType::Integer) => OperandTypeClass::Integer,
|
|
|
|
|
|
Some(MirType::Bool) => OperandTypeClass::Integer, // Bool can be used as integer
|
|
|
|
|
|
_ => {
|
|
|
|
|
|
// Check value_origin_newbox for StringBox
|
|
|
|
|
|
if self
|
|
|
|
|
|
.value_origin_newbox
|
|
|
|
|
|
.get(&vid)
|
|
|
|
|
|
.map(|s| s == "StringBox")
|
|
|
|
|
|
.unwrap_or(false)
|
|
|
|
|
|
{
|
|
|
|
|
|
return OperandTypeClass::String;
|
|
|
|
|
|
}
|
|
|
|
|
|
OperandTypeClass::Unknown
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
if std::env::var("NYASH_TYPEFACTS_DEBUG").is_ok() {
|
|
|
|
|
|
eprintln!("[typefacts] classify {:?} -> {:?}", vid, result);
|
|
|
|
|
|
}
|
|
|
|
|
|
result
|
|
|
|
|
|
}
|
2025-09-03 05:04:56 +09:00
|
|
|
|
// Build a binary operation
|
|
|
|
|
|
pub(super) fn build_binary_op(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
left: ASTNode,
|
|
|
|
|
|
operator: BinaryOperator,
|
|
|
|
|
|
right: ASTNode,
|
|
|
|
|
|
) -> Result<ValueId, String> {
|
2025-09-26 03:30:59 +09:00
|
|
|
|
// 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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-01 18:45:26 +09:00
|
|
|
|
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);
|
2025-11-17 00:48:18 +09:00
|
|
|
|
let dst = self.next_value_id();
|
2025-09-03 05:04:56 +09:00
|
|
|
|
|
|
|
|
|
|
let mir_op = self.convert_binary_operator(operator)?;
|
|
|
|
|
|
|
2025-11-21 06:25:17 +09:00
|
|
|
|
let all_call = std::env::var("NYASH_BUILDER_OPERATOR_BOX_ALL_CALL")
|
|
|
|
|
|
.ok()
|
|
|
|
|
|
.as_deref()
|
|
|
|
|
|
== Some("1");
|
2025-09-27 08:45:25 +09:00
|
|
|
|
|
2025-09-03 05:04:56 +09:00
|
|
|
|
match mir_op {
|
|
|
|
|
|
// Arithmetic operations
|
|
|
|
|
|
BinaryOpType::Arithmetic(op) => {
|
2025-09-27 08:45:25 +09:00
|
|
|
|
// 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
|
2025-11-21 06:25:17 +09:00
|
|
|
|
&& (all_call
|
|
|
|
|
|
|| std::env::var("NYASH_BUILDER_OPERATOR_BOX_ADD_CALL")
|
|
|
|
|
|
.ok()
|
|
|
|
|
|
.as_deref()
|
|
|
|
|
|
== Some("1"))
|
2025-09-27 08:45:25 +09:00
|
|
|
|
{
|
|
|
|
|
|
// AddOperator.apply/2(lhs, rhs)
|
|
|
|
|
|
let name = "AddOperator.apply/2".to_string();
|
2025-11-21 06:25:17 +09:00
|
|
|
|
self.emit_legacy_call(
|
|
|
|
|
|
Some(dst),
|
|
|
|
|
|
super::builder_calls::CallTarget::Global(name),
|
|
|
|
|
|
vec![lhs, rhs],
|
|
|
|
|
|
)?;
|
2025-12-14 18:27:24 +09:00
|
|
|
|
// Phase 196: TypeFacts SSOT - AddOperator call type annotation
|
2025-12-14 18:55:05 +09:00
|
|
|
|
// Phase 131-11-E: TypeFacts - classify operand types
|
|
|
|
|
|
let lhs_type = self.classify_operand_type(lhs);
|
|
|
|
|
|
let rhs_type = self.classify_operand_type(rhs);
|
|
|
|
|
|
|
|
|
|
|
|
use OperandTypeClass::*;
|
|
|
|
|
|
match (lhs_type, rhs_type) {
|
|
|
|
|
|
(String, String) => {
|
|
|
|
|
|
// BOTH are strings: result is string
|
|
|
|
|
|
self.value_types
|
|
|
|
|
|
.insert(dst, MirType::Box("StringBox".to_string()));
|
|
|
|
|
|
self.value_origin_newbox
|
|
|
|
|
|
.insert(dst, "StringBox".to_string());
|
|
|
|
|
|
}
|
|
|
|
|
|
(Integer, Integer) | (Integer, Unknown) | (Unknown, Integer) => {
|
|
|
|
|
|
// TypeFact: Integer + anything non-String = Integer
|
|
|
|
|
|
self.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
|
|
|
|
|
|
}
|
2025-09-14 19:16:32 +09:00
|
|
|
|
}
|
2025-09-27 08:45:25 +09:00
|
|
|
|
} 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/"),
|
2025-11-21 06:25:17 +09:00
|
|
|
|
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/")
|
|
|
|
|
|
}
|
2025-09-27 08:45:25 +09:00
|
|
|
|
_ => ("", ""),
|
|
|
|
|
|
};
|
|
|
|
|
|
if !name.is_empty() {
|
2025-11-21 06:25:17 +09:00
|
|
|
|
let in_guard = self
|
|
|
|
|
|
.current_function
|
|
|
|
|
|
.as_ref()
|
|
|
|
|
|
.map(|f| f.signature.name.starts_with(guard_prefix))
|
|
|
|
|
|
.unwrap_or(false);
|
2025-09-27 08:45:25 +09:00
|
|
|
|
if !in_guard {
|
2025-11-21 06:25:17 +09:00
|
|
|
|
self.emit_legacy_call(
|
|
|
|
|
|
Some(dst),
|
|
|
|
|
|
super::builder_calls::CallTarget::Global(name.to_string()),
|
|
|
|
|
|
vec![lhs, rhs],
|
|
|
|
|
|
)?;
|
2025-09-27 08:45:25 +09:00
|
|
|
|
// 型注釈: 算術はおおむね整数(Addは上で注釈済み)
|
|
|
|
|
|
self.value_types.insert(dst, MirType::Integer);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// guard中は従来のBinOp
|
2025-11-21 06:25:17 +09:00
|
|
|
|
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,
|
|
|
|
|
|
);
|
2025-11-04 20:46:43 +09:00
|
|
|
|
} else {
|
|
|
|
|
|
self.emit_instruction(MirInstruction::BinOp { dst, op, lhs, rhs })?;
|
|
|
|
|
|
}
|
2025-09-27 08:45:25 +09:00
|
|
|
|
self.value_types.insert(dst, MirType::Integer);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 既存の算術経路
|
2025-11-21 06:25:17 +09:00
|
|
|
|
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,
|
|
|
|
|
|
);
|
2025-11-04 20:46:43 +09:00
|
|
|
|
} else {
|
|
|
|
|
|
self.emit_instruction(MirInstruction::BinOp { dst, op, lhs, rhs })?;
|
|
|
|
|
|
}
|
2025-12-14 18:27:24 +09:00
|
|
|
|
// Phase 196: TypeFacts SSOT - BinOp type is determined by operands only
|
|
|
|
|
|
// String concatenation is handled at use-site in LLVM lowering
|
2025-09-27 08:45:25 +09:00
|
|
|
|
if matches!(op, crate::mir::BinaryOp::Add) {
|
2025-12-14 18:55:05 +09:00
|
|
|
|
// Phase 131-11-E: TypeFacts - classify operand types
|
|
|
|
|
|
let lhs_type = self.classify_operand_type(lhs);
|
|
|
|
|
|
let rhs_type = self.classify_operand_type(rhs);
|
|
|
|
|
|
|
|
|
|
|
|
use OperandTypeClass::*;
|
|
|
|
|
|
match (lhs_type, rhs_type) {
|
|
|
|
|
|
(String, String) => {
|
|
|
|
|
|
// BOTH are strings: result is definitely a string
|
|
|
|
|
|
self.value_types
|
|
|
|
|
|
.insert(dst, MirType::Box("StringBox".to_string()));
|
|
|
|
|
|
self.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.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
|
|
|
|
|
|
}
|
2025-09-27 08:45:25 +09:00
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
self.value_types.insert(dst, MirType::Integer);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-14 19:16:32 +09:00
|
|
|
|
} else {
|
2025-09-27 08:45:25 +09:00
|
|
|
|
// 既存の算術経路
|
2025-11-21 06:25:17 +09:00
|
|
|
|
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,
|
|
|
|
|
|
);
|
2025-11-04 20:46:43 +09:00
|
|
|
|
} else {
|
|
|
|
|
|
self.emit_instruction(MirInstruction::BinOp { dst, op, lhs, rhs })?;
|
|
|
|
|
|
}
|
2025-12-14 18:27:24 +09:00
|
|
|
|
// Phase 196: TypeFacts SSOT - BinOp type is determined by operands only
|
|
|
|
|
|
// String concatenation is handled at use-site in LLVM lowering
|
2025-09-27 08:45:25 +09:00
|
|
|
|
if matches!(op, crate::mir::BinaryOp::Add) {
|
2025-12-14 18:27:24 +09:00
|
|
|
|
// Check if BOTH operands are known to be strings (TypeFacts)
|
2025-09-27 08:45:25 +09:00
|
|
|
|
let lhs_is_str = match self.value_types.get(&lhs) {
|
|
|
|
|
|
Some(MirType::String) => true,
|
|
|
|
|
|
Some(MirType::Box(bt)) if bt == "StringBox" => true,
|
2025-11-24 14:17:02 +09:00
|
|
|
|
_ => self
|
|
|
|
|
|
.value_origin_newbox
|
|
|
|
|
|
.get(&lhs)
|
|
|
|
|
|
.map(|s| s == "StringBox")
|
|
|
|
|
|
.unwrap_or(false),
|
2025-09-27 08:45:25 +09:00
|
|
|
|
};
|
|
|
|
|
|
let rhs_is_str = match self.value_types.get(&rhs) {
|
|
|
|
|
|
Some(MirType::String) => true,
|
|
|
|
|
|
Some(MirType::Box(bt)) if bt == "StringBox" => true,
|
2025-11-24 14:17:02 +09:00
|
|
|
|
_ => self
|
|
|
|
|
|
.value_origin_newbox
|
|
|
|
|
|
.get(&rhs)
|
|
|
|
|
|
.map(|s| s == "StringBox")
|
|
|
|
|
|
.unwrap_or(false),
|
2025-09-27 08:45:25 +09:00
|
|
|
|
};
|
2025-12-14 18:27:24 +09:00
|
|
|
|
if lhs_is_str && rhs_is_str {
|
|
|
|
|
|
// BOTH are strings: result is definitely a string
|
2025-11-24 14:17:02 +09:00
|
|
|
|
self.value_types
|
|
|
|
|
|
.insert(dst, MirType::Box("StringBox".to_string()));
|
|
|
|
|
|
self.value_origin_newbox
|
|
|
|
|
|
.insert(dst, "StringBox".to_string());
|
2025-12-14 18:27:24 +09:00
|
|
|
|
} else if !lhs_is_str && !rhs_is_str {
|
|
|
|
|
|
// NEITHER is a string: numeric addition
|
2025-09-27 08:45:25 +09:00
|
|
|
|
self.value_types.insert(dst, MirType::Integer);
|
|
|
|
|
|
}
|
2025-12-14 18:27:24 +09:00
|
|
|
|
// else: Mixed types (string + int or int + string)
|
|
|
|
|
|
// Leave dst type as Unknown - LLVM will handle coercion at use-site
|
2025-09-27 08:45:25 +09:00
|
|
|
|
} else {
|
|
|
|
|
|
self.value_types.insert(dst, MirType::Integer);
|
|
|
|
|
|
}
|
2025-09-14 19:16:32 +09:00
|
|
|
|
}
|
2025-09-03 05:04:56 +09:00
|
|
|
|
}
|
|
|
|
|
|
// Comparison operations
|
|
|
|
|
|
BinaryOpType::Comparison(op) => {
|
2025-09-27 08:45:25 +09:00
|
|
|
|
// 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
|
2025-11-21 06:25:17 +09:00
|
|
|
|
&& (all_call
|
|
|
|
|
|
|| std::env::var("NYASH_BUILDER_OPERATOR_BOX_COMPARE_CALL")
|
|
|
|
|
|
.ok()
|
|
|
|
|
|
.as_deref()
|
|
|
|
|
|
== Some("1"))
|
|
|
|
|
|
{
|
2025-09-27 08:45:25 +09:00
|
|
|
|
// op名の文字列化
|
|
|
|
|
|
let opname = match op {
|
|
|
|
|
|
CompareOp::Eq => "Eq",
|
|
|
|
|
|
CompareOp::Ne => "Ne",
|
|
|
|
|
|
CompareOp::Lt => "Lt",
|
|
|
|
|
|
CompareOp::Le => "Le",
|
|
|
|
|
|
CompareOp::Gt => "Gt",
|
|
|
|
|
|
CompareOp::Ge => "Ge",
|
|
|
|
|
|
};
|
2025-11-21 06:25:17 +09:00
|
|
|
|
let op_const =
|
|
|
|
|
|
crate::mir::builder::emission::constant::emit_string(self, opname);
|
2025-09-27 08:45:25 +09:00
|
|
|
|
// そのまま値を渡す(型変換/slot化は演算子内orVMで行う)
|
|
|
|
|
|
let name = "CompareOperator.apply/3".to_string();
|
2025-11-21 06:25:17 +09:00
|
|
|
|
self.emit_legacy_call(
|
|
|
|
|
|
Some(dst),
|
|
|
|
|
|
super::builder_calls::CallTarget::Global(name),
|
|
|
|
|
|
vec![op_const, lhs, rhs],
|
|
|
|
|
|
)?;
|
2025-09-27 08:45:25 +09:00
|
|
|
|
self.value_types.insert(dst, MirType::Bool);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 既存の比較経路(安全のための型注釈/slot化含む)
|
|
|
|
|
|
let (lhs2_raw, rhs2_raw) = if self
|
2025-09-03 05:04:56 +09:00
|
|
|
|
.value_origin_newbox
|
2025-09-27 08:45:25 +09:00
|
|
|
|
.get(&lhs)
|
2025-09-03 05:04:56 +09:00
|
|
|
|
.map(|s| s == "IntegerBox")
|
|
|
|
|
|
.unwrap_or(false)
|
2025-09-27 08:45:25 +09:00
|
|
|
|
&& self
|
|
|
|
|
|
.value_origin_newbox
|
|
|
|
|
|
.get(&rhs)
|
|
|
|
|
|
.map(|s| s == "IntegerBox")
|
|
|
|
|
|
.unwrap_or(false)
|
|
|
|
|
|
{
|
2025-11-17 00:48:18 +09:00
|
|
|
|
let li = self.next_value_id();
|
|
|
|
|
|
let ri = self.next_value_id();
|
2025-09-27 08:45:25 +09:00
|
|
|
|
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)
|
|
|
|
|
|
};
|
2025-09-28 20:38:09 +09:00
|
|
|
|
// 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)?;
|
2025-09-27 08:45:25 +09:00
|
|
|
|
}
|
2025-09-03 05:04:56 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Ok(dst)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-26 03:30:59 +09:00
|
|
|
|
/// 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);
|
|
|
|
|
|
|
2025-09-26 04:17:56 +09:00
|
|
|
|
// 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")?;
|
2025-09-26 03:30:59 +09:00
|
|
|
|
|
|
|
|
|
|
// Prepare blocks
|
|
|
|
|
|
let then_block = self.block_gen.next();
|
|
|
|
|
|
let else_block = self.block_gen.next();
|
|
|
|
|
|
let merge_block = self.block_gen.next();
|
|
|
|
|
|
|
|
|
|
|
|
// Branch on LHS truthiness (runtime to_bool semantics in interpreter/LLVM)
|
2025-09-28 20:38:09 +09:00
|
|
|
|
let mut lhs_cond = self.local_cond(lhs_val);
|
|
|
|
|
|
crate::mir::builder::ssa::local::finalize_branch_cond(self, &mut lhs_cond);
|
2025-11-21 06:25:17 +09:00
|
|
|
|
crate::mir::builder::emission::branch::emit_conditional(
|
|
|
|
|
|
self, lhs_cond, then_block, else_block,
|
|
|
|
|
|
)?;
|
2025-09-26 05:28:20 +09:00
|
|
|
|
// Record predecessor block for branch (for single-pred PHI materialization)
|
|
|
|
|
|
let pre_branch_bb = self.current_block()?;
|
2025-09-26 03:30:59 +09:00
|
|
|
|
|
|
|
|
|
|
// Snapshot variables before entering branches
|
|
|
|
|
|
let pre_if_var_map = self.variable_map.clone();
|
|
|
|
|
|
|
|
|
|
|
|
// ---- THEN branch ----
|
2025-09-26 05:28:20 +09:00
|
|
|
|
self.start_new_block(then_block)?;
|
2025-09-26 03:30:59 +09:00
|
|
|
|
self.hint_scope_enter(0);
|
|
|
|
|
|
// Reset scope to pre-if snapshot for clean deltas
|
|
|
|
|
|
self.variable_map = pre_if_var_map.clone();
|
2025-09-26 05:28:20 +09:00
|
|
|
|
// Materialize all variables at entry via single-pred PHI (correctness-first)
|
|
|
|
|
|
for (name, &pre_v) in pre_if_var_map.iter() {
|
2025-11-06 23:57:24 +09:00
|
|
|
|
let phi_val = self.insert_phi_single(pre_branch_bb, pre_v)?;
|
2025-09-26 05:28:20 +09:00
|
|
|
|
self.variable_map.insert(name.clone(), phi_val);
|
|
|
|
|
|
}
|
2025-09-26 03:30:59 +09:00
|
|
|
|
|
|
|
|
|
|
// 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.block_gen.next();
|
|
|
|
|
|
let rhs_false = self.block_gen.next();
|
|
|
|
|
|
let rhs_join = self.block_gen.next();
|
|
|
|
|
|
let rhs_val = self.build_expression(right.clone())?;
|
2025-09-28 20:38:09 +09:00
|
|
|
|
let mut rhs_cond = self.local_cond(rhs_val);
|
|
|
|
|
|
crate::mir::builder::ssa::local::finalize_branch_cond(self, &mut rhs_cond);
|
2025-11-21 06:25:17 +09:00
|
|
|
|
crate::mir::builder::emission::branch::emit_conditional(
|
|
|
|
|
|
self, rhs_cond, rhs_true, rhs_false,
|
|
|
|
|
|
)?;
|
2025-09-26 03:30:59 +09:00
|
|
|
|
// true path
|
|
|
|
|
|
self.start_new_block(rhs_true)?;
|
2025-09-28 20:38:09 +09:00
|
|
|
|
let t_id = crate::mir::builder::emission::constant::emit_bool(self, true);
|
|
|
|
|
|
crate::mir::builder::emission::branch::emit_jump(self, rhs_join)?;
|
2025-09-26 03:30:59 +09:00
|
|
|
|
let rhs_true_exit = self.current_block()?;
|
|
|
|
|
|
// false path
|
|
|
|
|
|
self.start_new_block(rhs_false)?;
|
2025-09-28 20:38:09 +09:00
|
|
|
|
let f_id = crate::mir::builder::emission::constant::emit_bool(self, false);
|
|
|
|
|
|
crate::mir::builder::emission::branch::emit_jump(self, rhs_join)?;
|
2025-09-26 03:30:59 +09:00
|
|
|
|
let rhs_false_exit = self.current_block()?;
|
|
|
|
|
|
// join rhs result into a single bool
|
|
|
|
|
|
self.start_new_block(rhs_join)?;
|
2025-11-21 06:25:17 +09:00
|
|
|
|
if let Some(func) = self.current_function.as_mut() {
|
|
|
|
|
|
func.update_cfg();
|
|
|
|
|
|
}
|
2025-11-06 23:57:24 +09:00
|
|
|
|
let rhs_bool = self.insert_phi_binary(rhs_true_exit, t_id, rhs_false_exit, f_id)?;
|
2025-09-26 03:30:59 +09:00
|
|
|
|
self.value_types.insert(rhs_bool, MirType::Bool);
|
|
|
|
|
|
rhs_bool
|
|
|
|
|
|
} else {
|
2025-09-28 20:38:09 +09:00
|
|
|
|
let t_id = crate::mir::builder::emission::constant::emit_bool(self, true);
|
2025-09-26 03:30:59 +09:00
|
|
|
|
t_id
|
|
|
|
|
|
};
|
|
|
|
|
|
let then_exit_block = self.current_block()?;
|
2025-11-01 13:46:34 +09:00
|
|
|
|
let then_reaches_merge = !self.is_current_block_terminated();
|
2025-09-26 03:30:59 +09:00
|
|
|
|
let then_var_map_end = self.variable_map.clone();
|
2025-11-01 13:46:34 +09:00
|
|
|
|
if then_reaches_merge {
|
2025-09-26 03:30:59 +09:00
|
|
|
|
self.hint_scope_leave(0);
|
2025-09-28 20:38:09 +09:00
|
|
|
|
crate::mir::builder::emission::branch::emit_jump(self, merge_block)?;
|
2025-09-26 03:30:59 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---- ELSE branch ----
|
2025-09-26 05:28:20 +09:00
|
|
|
|
self.start_new_block(else_block)?;
|
2025-09-26 03:30:59 +09:00
|
|
|
|
self.hint_scope_enter(0);
|
|
|
|
|
|
self.variable_map = pre_if_var_map.clone();
|
2025-09-26 05:28:20 +09:00
|
|
|
|
// Materialize all variables at entry via single-pred PHI (correctness-first)
|
|
|
|
|
|
for (name, &pre_v) in pre_if_var_map.iter() {
|
2025-11-06 23:57:24 +09:00
|
|
|
|
let phi_val = self.insert_phi_single(pre_branch_bb, pre_v)?;
|
2025-09-26 05:28:20 +09:00
|
|
|
|
self.variable_map.insert(name.clone(), phi_val);
|
|
|
|
|
|
}
|
2025-09-26 03:30:59 +09:00
|
|
|
|
// AND: else → false
|
|
|
|
|
|
// OR: else → evaluate RHS and reduce to bool
|
|
|
|
|
|
let else_value_raw = if is_and {
|
2025-09-28 20:38:09 +09:00
|
|
|
|
let f_id = crate::mir::builder::emission::constant::emit_bool(self, false);
|
2025-09-26 03:30:59 +09:00
|
|
|
|
f_id
|
|
|
|
|
|
} else {
|
|
|
|
|
|
let rhs_true = self.block_gen.next();
|
|
|
|
|
|
let rhs_false = self.block_gen.next();
|
|
|
|
|
|
let rhs_join = self.block_gen.next();
|
|
|
|
|
|
let rhs_val = self.build_expression(right)?;
|
2025-09-28 20:38:09 +09:00
|
|
|
|
let mut rhs_cond = self.local_cond(rhs_val);
|
|
|
|
|
|
crate::mir::builder::ssa::local::finalize_branch_cond(self, &mut rhs_cond);
|
2025-11-21 06:25:17 +09:00
|
|
|
|
crate::mir::builder::emission::branch::emit_conditional(
|
|
|
|
|
|
self, rhs_cond, rhs_true, rhs_false,
|
|
|
|
|
|
)?;
|
2025-09-26 03:30:59 +09:00
|
|
|
|
// true path
|
|
|
|
|
|
self.start_new_block(rhs_true)?;
|
2025-09-28 20:38:09 +09:00
|
|
|
|
let t_id = crate::mir::builder::emission::constant::emit_bool(self, true);
|
|
|
|
|
|
crate::mir::builder::emission::branch::emit_jump(self, rhs_join)?;
|
2025-09-26 03:30:59 +09:00
|
|
|
|
let rhs_true_exit = self.current_block()?;
|
|
|
|
|
|
// false path
|
|
|
|
|
|
self.start_new_block(rhs_false)?;
|
2025-09-28 20:38:09 +09:00
|
|
|
|
let f_id = crate::mir::builder::emission::constant::emit_bool(self, false);
|
|
|
|
|
|
crate::mir::builder::emission::branch::emit_jump(self, rhs_join)?;
|
2025-09-26 03:30:59 +09:00
|
|
|
|
let rhs_false_exit = self.current_block()?;
|
|
|
|
|
|
// join rhs result into a single bool
|
|
|
|
|
|
self.start_new_block(rhs_join)?;
|
2025-11-21 06:25:17 +09:00
|
|
|
|
if let Some(func) = self.current_function.as_mut() {
|
|
|
|
|
|
func.update_cfg();
|
|
|
|
|
|
}
|
2025-11-06 23:57:24 +09:00
|
|
|
|
let rhs_bool = self.insert_phi_binary(rhs_true_exit, t_id, rhs_false_exit, f_id)?;
|
2025-09-26 03:30:59 +09:00
|
|
|
|
self.value_types.insert(rhs_bool, MirType::Bool);
|
|
|
|
|
|
rhs_bool
|
|
|
|
|
|
};
|
|
|
|
|
|
let else_exit_block = self.current_block()?;
|
2025-11-01 13:46:34 +09:00
|
|
|
|
let else_reaches_merge = !self.is_current_block_terminated();
|
2025-09-26 03:30:59 +09:00
|
|
|
|
let else_var_map_end = self.variable_map.clone();
|
2025-11-01 13:46:34 +09:00
|
|
|
|
if else_reaches_merge {
|
2025-09-26 03:30:59 +09:00
|
|
|
|
self.hint_scope_leave(0);
|
2025-11-05 18:57:03 +09:00
|
|
|
|
crate::mir::builder::emission::branch::emit_jump(self, merge_block)?;
|
2025-09-26 03:30:59 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---- MERGE ----
|
2025-09-26 05:28:20 +09:00
|
|
|
|
// 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)?;
|
2025-09-26 03:30:59 +09:00
|
|
|
|
self.push_if_merge(merge_block);
|
|
|
|
|
|
|
2025-11-01 13:46:34 +09:00
|
|
|
|
// Result PHI (bool) — consider only reachable predecessors
|
|
|
|
|
|
let mut inputs: Vec<(crate::mir::BasicBlockId, ValueId)> = Vec::new();
|
2025-11-21 06:25:17 +09:00
|
|
|
|
if then_reaches_merge {
|
|
|
|
|
|
inputs.push((then_exit_block, then_value_raw));
|
|
|
|
|
|
}
|
|
|
|
|
|
if else_reaches_merge {
|
|
|
|
|
|
inputs.push((else_exit_block, else_value_raw));
|
|
|
|
|
|
}
|
2025-11-01 13:46:34 +09:00
|
|
|
|
let result_val = if inputs.len() >= 2 {
|
2025-11-21 06:25:17 +09:00
|
|
|
|
if let Some(func) = self.current_function.as_mut() {
|
|
|
|
|
|
func.update_cfg();
|
|
|
|
|
|
}
|
2025-11-06 23:57:24 +09:00
|
|
|
|
let dst = self.insert_phi(inputs)?;
|
2025-11-01 13:46:34 +09:00
|
|
|
|
self.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)
|
|
|
|
|
|
};
|
2025-09-26 03:30:59 +09:00
|
|
|
|
|
|
|
|
|
|
// Merge modified vars from both branches back into current scope
|
|
|
|
|
|
self.merge_modified_vars(
|
|
|
|
|
|
then_block,
|
|
|
|
|
|
else_block,
|
2025-11-21 06:25:17 +09:00
|
|
|
|
if then_reaches_merge {
|
|
|
|
|
|
Some(then_exit_block)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
None
|
|
|
|
|
|
},
|
|
|
|
|
|
if else_reaches_merge {
|
|
|
|
|
|
Some(else_exit_block)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
None
|
|
|
|
|
|
},
|
2025-09-26 03:30:59 +09:00
|
|
|
|
&pre_if_var_map,
|
|
|
|
|
|
&then_var_map_end,
|
|
|
|
|
|
&Some(else_var_map_end),
|
|
|
|
|
|
None,
|
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
|
|
self.pop_if_merge();
|
|
|
|
|
|
Ok(result_val)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-03 05:04:56 +09:00
|
|
|
|
// Build a unary operation
|
2025-09-17 07:43:07 +09:00
|
|
|
|
pub(super) fn build_unary_op(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
operator: String,
|
|
|
|
|
|
operand: ASTNode,
|
|
|
|
|
|
) -> Result<ValueId, String> {
|
2025-09-03 05:04:56 +09:00
|
|
|
|
let operand_val = self.build_expression(operand)?;
|
2025-11-21 06:25:17 +09:00
|
|
|
|
let all_call = std::env::var("NYASH_BUILDER_OPERATOR_BOX_ALL_CALL")
|
|
|
|
|
|
.ok()
|
|
|
|
|
|
.as_deref()
|
|
|
|
|
|
== Some("1");
|
2025-09-27 08:45:25 +09:00
|
|
|
|
if all_call {
|
|
|
|
|
|
let (name, guard_prefix, rett) = match operator.as_str() {
|
2025-11-21 06:25:17 +09:00
|
|
|
|
"-" => (
|
|
|
|
|
|
"NegOperator.apply/1",
|
|
|
|
|
|
"NegOperator.apply/",
|
|
|
|
|
|
MirType::Integer,
|
|
|
|
|
|
),
|
2025-09-27 08:45:25 +09:00
|
|
|
|
"!" | "not" => ("NotOperator.apply/1", "NotOperator.apply/", MirType::Bool),
|
2025-11-21 06:25:17 +09:00
|
|
|
|
"~" => (
|
|
|
|
|
|
"BitNotOperator.apply/1",
|
|
|
|
|
|
"BitNotOperator.apply/",
|
|
|
|
|
|
MirType::Integer,
|
|
|
|
|
|
),
|
2025-09-27 08:45:25 +09:00
|
|
|
|
_ => ("", "", MirType::Integer),
|
|
|
|
|
|
};
|
|
|
|
|
|
if !name.is_empty() {
|
2025-11-21 06:25:17 +09:00
|
|
|
|
let in_guard = self
|
|
|
|
|
|
.current_function
|
|
|
|
|
|
.as_ref()
|
|
|
|
|
|
.map(|f| f.signature.name.starts_with(guard_prefix))
|
|
|
|
|
|
.unwrap_or(false);
|
2025-11-17 00:48:18 +09:00
|
|
|
|
let dst = self.next_value_id();
|
2025-09-27 08:45:25 +09:00
|
|
|
|
if !in_guard {
|
2025-11-21 06:25:17 +09:00
|
|
|
|
self.emit_legacy_call(
|
|
|
|
|
|
Some(dst),
|
|
|
|
|
|
super::builder_calls::CallTarget::Global(name.to_string()),
|
|
|
|
|
|
vec![operand_val],
|
|
|
|
|
|
)?;
|
2025-09-27 08:45:25 +09:00
|
|
|
|
self.value_types.insert(dst, rett);
|
|
|
|
|
|
return Ok(dst);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-07 07:28:53 +09:00
|
|
|
|
// Core-13 純化: UnaryOp を直接 展開(Neg/Not/BitNot)
|
|
|
|
|
|
if crate::config::env::mir_core13_pure() {
|
|
|
|
|
|
match operator.as_str() {
|
|
|
|
|
|
"-" => {
|
2025-09-28 20:38:09 +09:00
|
|
|
|
let zero = crate::mir::builder::emission::constant::emit_integer(self, 0);
|
2025-11-17 00:48:18 +09:00
|
|
|
|
let dst = self.next_value_id();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
self.emit_instruction(MirInstruction::BinOp {
|
|
|
|
|
|
dst,
|
|
|
|
|
|
op: crate::mir::BinaryOp::Sub,
|
|
|
|
|
|
lhs: zero,
|
|
|
|
|
|
rhs: operand_val,
|
|
|
|
|
|
})?;
|
2025-09-07 07:28:53 +09:00
|
|
|
|
return Ok(dst);
|
|
|
|
|
|
}
|
|
|
|
|
|
"!" | "not" => {
|
2025-09-28 20:38:09 +09:00
|
|
|
|
let f = crate::mir::builder::emission::constant::emit_bool(self, false);
|
2025-11-17 00:48:18 +09:00
|
|
|
|
let dst = self.next_value_id();
|
2025-09-28 20:38:09 +09:00
|
|
|
|
crate::mir::builder::emission::compare::emit_to(
|
|
|
|
|
|
self,
|
2025-09-17 07:43:07 +09:00
|
|
|
|
dst,
|
2025-09-28 20:38:09 +09:00
|
|
|
|
crate::mir::CompareOp::Eq,
|
|
|
|
|
|
operand_val,
|
|
|
|
|
|
f,
|
|
|
|
|
|
)?;
|
2025-09-07 07:28:53 +09:00
|
|
|
|
return Ok(dst);
|
|
|
|
|
|
}
|
|
|
|
|
|
"~" => {
|
2025-09-28 20:38:09 +09:00
|
|
|
|
let all1 = crate::mir::builder::emission::constant::emit_integer(self, -1);
|
2025-11-17 00:48:18 +09:00
|
|
|
|
let dst = self.next_value_id();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
self.emit_instruction(MirInstruction::BinOp {
|
|
|
|
|
|
dst,
|
|
|
|
|
|
op: crate::mir::BinaryOp::BitXor,
|
|
|
|
|
|
lhs: operand_val,
|
|
|
|
|
|
rhs: all1,
|
|
|
|
|
|
})?;
|
2025-09-07 07:28:53 +09:00
|
|
|
|
return Ok(dst);
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => {}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-17 00:48:18 +09:00
|
|
|
|
let dst = self.next_value_id();
|
2025-09-03 05:04:56 +09:00
|
|
|
|
let mir_op = self.convert_unary_operator(operator)?;
|
2025-09-17 07:43:07 +09:00
|
|
|
|
self.emit_instruction(MirInstruction::UnaryOp {
|
|
|
|
|
|
dst,
|
|
|
|
|
|
op: mir_op,
|
|
|
|
|
|
operand: operand_val,
|
|
|
|
|
|
})?;
|
2025-09-03 05:04:56 +09:00
|
|
|
|
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)),
|
2025-09-08 03:44:55 +09:00
|
|
|
|
BinaryOperator::Shl => Ok(BinaryOpType::Arithmetic(BinaryOp::Shl)),
|
2025-09-08 03:54:34 +09:00
|
|
|
|
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)),
|
2025-09-03 05:04:56 +09:00
|
|
|
|
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)),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|