use std::collections::BTreeMap;
use crate::cfg::{BlockRef, Cfg};
use crate::hir::common::{
HirAssign, HirBinaryExpr, HirBinaryOpKind, HirBlock, HirExpr, HirGoto, HirIf, HirLValue,
HirLabelId, HirProto, HirProtoRef, HirReturn, HirStmt, HirUnresolvedExpr, HirUnstructured,
};
use crate::transformer::InstrRef;
pub(super) fn assign_stmt(targets: Vec<HirLValue>, values: Vec<HirExpr>) -> HirStmt {
HirStmt::Assign(Box::new(HirAssign { targets, values }))
}
pub(super) fn return_stmt(values: Vec<HirExpr>) -> HirStmt {
HirStmt::Return(Box::new(HirReturn { values }))
}
pub(super) fn goto_stmt(target: HirLabelId) -> HirStmt {
HirStmt::Goto(Box::new(HirGoto { target }))
}
pub(super) fn goto_block(target: HirLabelId) -> HirBlock {
HirBlock {
stmts: vec![goto_stmt(target)],
}
}
pub(super) fn branch_stmt(
cond: HirExpr,
then_block: HirBlock,
else_block: Option<HirBlock>,
) -> HirStmt {
HirStmt::If(Box::new(HirIf {
cond,
then_block,
else_block,
}))
}
pub(super) fn unstructured_stmt(summary: impl Into<String>) -> HirStmt {
HirStmt::Unstructured(Box::new(HirUnstructured {
body: HirBlock::default(),
summary: Some(summary.into()),
}))
}
pub(super) fn unresolved_expr(summary: impl Into<String>) -> HirExpr {
HirExpr::Unresolved(Box::new(HirUnresolvedExpr {
summary: summary.into(),
}))
}
pub(super) fn concat_expr(parts: impl IntoIterator<Item = HirExpr>) -> HirExpr {
let mut parts = parts.into_iter().collect::<Vec<_>>();
let Some(last) = parts.pop() else {
return unresolved_expr("concat empty source");
};
parts.into_iter().rfold(last, |rhs, lhs| {
HirExpr::Binary(Box::new(crate::hir::common::HirBinaryExpr {
op: crate::hir::common::HirBinaryOpKind::Concat,
lhs,
rhs,
}))
})
}
pub(super) fn binary_expr(op: HirBinaryOpKind, lhs: HirExpr, rhs: HirExpr) -> HirExpr {
let (op, rhs) = match (op, rhs) {
(HirBinaryOpKind::Shl, HirExpr::Integer(value)) if value < 0 => {
(HirBinaryOpKind::Shr, HirExpr::Integer(-value))
}
(HirBinaryOpKind::Shr, HirExpr::Integer(value)) if value < 0 => {
(HirBinaryOpKind::Shl, HirExpr::Integer(-value))
}
(HirBinaryOpKind::Shl, HirExpr::Int64(value)) if value < 0 => {
(HirBinaryOpKind::Shr, HirExpr::Int64(-value))
}
(HirBinaryOpKind::Shr, HirExpr::Int64(value)) if value < 0 => {
(HirBinaryOpKind::Shl, HirExpr::Int64(-value))
}
(op, rhs) => (op, rhs),
};
HirExpr::Binary(Box::new(HirBinaryExpr { op, lhs, rhs }))
}
pub(super) fn label_for_block(
cfg: &Cfg,
label_map: &BTreeMap<BlockRef, HirLabelId>,
target: InstrRef,
) -> HirLabelId {
let block = cfg.instr_to_block[target.index()];
label_map[&block]
}
pub(super) fn build_label_map_for_summary(cfg: &Cfg) -> BTreeMap<BlockRef, HirLabelId> {
cfg.block_order
.iter()
.filter(|block| cfg.reachable_blocks.contains(block))
.enumerate()
.map(|(index, block)| (*block, HirLabelId(index)))
.collect()
}
pub(super) fn decode_raw_string(raw: &crate::parser::RawString) -> String {
raw.text
.as_ref()
.map(|text| text.value.clone())
.unwrap_or_else(|| String::from_utf8_lossy(&raw.bytes).into_owned())
}
pub(super) fn empty_proto(id: HirProtoRef) -> HirProto {
HirProto {
id,
source: None,
line_range: crate::parser::ProtoLineRange {
defined_start: 0,
defined_end: 0,
},
signature: crate::parser::ProtoSignature {
num_params: 0,
is_vararg: false,
has_vararg_param_reg: false,
named_vararg_table: false,
},
params: Vec::new(),
param_debug_hints: Vec::new(),
locals: Vec::new(),
local_debug_hints: Vec::new(),
upvalues: Vec::new(),
upvalue_debug_hints: Vec::new(),
temps: Vec::new(),
temp_debug_locals: Vec::new(),
body: HirBlock::default(),
children: Vec::new(),
}
}