nilang 0.4.1

A scripting language interpreter for Advent of Code
Documentation
use crate::codegen::func::Func;
use crate::ir::LabelID;
use crate::runtime::ByteCode;
use hashbrown::HashMap;

#[derive(Eq, PartialEq, Hash, Copy, Clone)]
pub enum BackPatchLabel {
    Label(LabelID),
    Temp(usize),
}

pub struct BackpatchContext {
    jump_positions: HashMap<BackPatchLabel, Vec<usize>>,
    label_positions: HashMap<BackPatchLabel, usize>,
    temp_label_counter: usize,
}

impl BackpatchContext {
    pub fn new() -> Self {
        Self {
            jump_positions: HashMap::new(),
            label_positions: HashMap::new(),
            temp_label_counter: 0,
        }
    }

    pub fn insert_label_position(&mut self, label: BackPatchLabel, position: usize) {
        self.label_positions.insert(label, position);
    }

    pub fn add_jump_position(&mut self, label: BackPatchLabel, position: usize) {
        if let Some(positions) = self.jump_positions.get_mut(&label) {
            positions.push(position);
        } else {
            self.jump_positions.insert(label, vec![position]);
        }
    }

    pub fn next_temp_label(&mut self) -> BackPatchLabel {
        let label = BackPatchLabel::Temp(self.temp_label_counter);
        self.temp_label_counter += 1;
        label
    }

    pub fn apply_patches(self, func: &mut Func) {
        let instrs = func.get_instrs_mut();
        for (label, positions) in self.jump_positions.iter() {
            let label_position = self.label_positions.get(label).unwrap();

            for jump_pos in positions.iter() {
                match &mut instrs[*jump_pos] {
                    ByteCode::Jnt { offset, .. }
                    | ByteCode::Jit { offset, .. }
                    | ByteCode::Jump { offset } => {
                        let abs_diff: usize = label_position.abs_diff(*jump_pos);
                        let signed_offset: isize = if *label_position < *jump_pos {
                            -isize::try_from(abs_diff).unwrap()
                        } else {
                            isize::try_from(abs_diff).unwrap()
                        };

                        *offset = i16::try_from(signed_offset).unwrap();
                    }
                    _ => panic!("CODEGEN ERROR during Back Patching"),
                }
            }
        }
    }

    pub fn push_jump_instr(&mut self, func: &mut Func, label: BackPatchLabel) {
        let instr = ByteCode::Jump { offset: 0 };

        self.push_generic_jump_instr(instr, func, label);
    }

    pub fn push_generic_jump_instr(
        &mut self,
        instr: ByteCode,
        func: &mut Func,
        label: BackPatchLabel,
    ) {
        let position = func.len();

        func.push_instr(instr);
        self.add_jump_position(label, position);
    }
}