alpaca_ir 0.0.1

AlpacaIR is a intermediate represenation meant to make making compilers easier.
Documentation
pub mod stage1;
pub mod stage2;

use std::any::Any;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;

use crate::error::ErrorSection;
use crate::exit;

/// Not recommended to use this struct directly
/// its purpose is for ```AlpacaPipeline```
#[derive(Debug)]
pub struct AlpacaPipelineInfo {
    data: HashMap<&'static str, Box<dyn Any>>,
}

impl AlpacaPipelineInfo {
    pub fn new() -> Self {
        Self { data: HashMap::new() }
    }
    
    /// Add data to the info, can be accessed by other passthroughs
    pub fn add_data<T: 'static>(&mut self, key: &'static str, value: T) {
        if self.data.contains_key(key) {
            log::error!("key already exists!");
            exit!(ErrorSection::Pipeline, 13474);
        }
        
        self.data.insert(key, Box::new(value));
    }
    
    /// Gets selected data as a ref
    pub fn get_data_ref<T: 'static>(&self, key: &'static str) -> Option<&T> {
        self.data.get(key)?.downcast_ref::<T>()
    }
    
    /// Like ```get_data_ref``` but allows mutability
    pub fn get_data_mut<T: 'static>(&mut self, key: &'static str) -> Option<&mut T> {
        self.data.get_mut(key)?.downcast_mut::<T>()
    }
    
    /// Completely removes data from the pipeline
    pub fn remove_data(&mut self, key: &str) { self.data.remove(key); }
}

/// Trait for creating pipeline modules,
/// allows for easy use of community created passthroughs
pub trait CriaPipelineModule {
    fn build(&self, pipeline: &mut AlpacaPipeline);
}

/// ```AlpacaPipeline``` is used to manage passthroughs like, optimizations, translation, etc.
/// It is the Core of the MIRP Paradigm, mainly refered as MIRPP
pub struct AlpacaPipeline {
    info: Rc<RefCell<AlpacaPipelineInfo>>,
    passes: Vec<Box<dyn Fn(Rc<RefCell<AlpacaPipelineInfo>>) -> u32>>,
}

impl Default for AlpacaPipeline {
    fn default() -> Self {
        env_logger::init();
        Self { info: Rc::new(RefCell::new(AlpacaPipelineInfo::new())), passes: Vec::new() }
    }
}

impl AlpacaPipeline {
    /// Adds a new pass the the pipeline, but be careful where
    /// you place it, as passess are ran orderly from the first
    /// created pass to the last
    pub fn add_pass<F>(&mut self, pass: F) -> &mut Self
    where
        F: Fn(Rc<RefCell<AlpacaPipelineInfo>>) -> u32 + 'static,
    {
        self.passes.push(Box::new(pass));
        self
    }
    
    /// Unloads passes from modules
    pub fn add_modules(&mut self, modules: &[&dyn CriaPipelineModule]) -> &mut Self {
        for module in modules {
            module.build(self);
        }
        
        self
    }
    
    /// Runs the pipeline, obviously
    pub fn run(&mut self) {
        for pass in &mut self.passes {
            let exit_code = pass(self.info.clone());
            if exit_code != 0 {
                exit!(ErrorSection::Pipeline, exit_code);
            }
        }
    }
}