origen_metal 1.4.0

Bare metal APIs for the Origen SDK
use crate::prog_gen::{FlowCondition, PGM};
use crate::Result;
use crate::ast::{Node, Processor, Return};

pub struct Flags {
    open_if: Vec<Node<PGM>>,
    open_unless: Vec<Node<PGM>>,
    conflicting: Vec<(Node<PGM>, Node<PGM>)>,
    volatile_flags: Vec<String>,
}

pub fn run(node: &Node<PGM>) -> Result<()> {
    let mut p = Flags {
        open_if: vec![],
        open_unless: vec![],
        conflicting: vec![],
        volatile_flags: vec![],
    };
    let _ = node.process(&mut p)?;

    let mut msg = "".to_string();

    if !p.conflicting.is_empty() {
        msg += "\nif_flag and unless_flag conditions cannot be nested and refer to the same flag unless it is declared as volatile.";
        msg += "\nThe following conflicts were found:";
        if crate::PROG_GEN_CONFIG.debug_enabled() {
            for (a, b) in &p.conflicting {
                msg += &format!("\n  {} - {}", a, a.meta_string());
                msg += &format!("\n  {} - {}", b, b.meta_string());
            }
        } else {
            msg += "\n  run again with the --debug switch to see them";
        }
        bail!("{}", msg)
    } else {
        Ok(())
    }
}

impl Processor<PGM> for Flags {
    fn on_node(&mut self, node: &Node<PGM>) -> crate::Result<Return<PGM>> {
        Ok(match &node.attrs {
            PGM::Volatile(flag) => {
                self.volatile_flags.push(flag.to_owned());
                Return::None
            }
            PGM::Condition(cond) => match cond {
                FlowCondition::IfFlag(flags) => {
                    let flag = flags.first().unwrap();
                    if self.volatile_flags.iter().any(|f| f == flag) {
                        Return::ProcessChildren
                    } else {
                        for n in &self.open_unless {
                            if let PGM::Condition(attrs) = &n.attrs {
                                if let FlowCondition::UnlessFlag(f) = attrs {
                                    if f.first().unwrap() == flag {
                                        self.conflicting.push((n.clone(), node.without_children()));
                                    }
                                }
                            }
                        }
                        self.open_if.push(node.without_children());
                        let _ = node.process_children(self)?;
                        self.open_if.pop();
                        Return::None
                    }
                }
                FlowCondition::UnlessFlag(flags) => {
                    let flag = flags.first().unwrap();
                    if self.volatile_flags.iter().any(|f| f == flag) {
                        Return::ProcessChildren
                    } else {
                        for n in &self.open_if {
                            if let PGM::Condition(attrs) = &n.attrs {
                                if let FlowCondition::IfFlag(f) = attrs {
                                    if f.first().unwrap() == flag {
                                        self.conflicting.push((n.clone(), node.without_children()));
                                    }
                                }
                            }
                        }
                        self.open_unless.push(node.without_children());
                        let _ = node.process_children(self)?;
                        self.open_unless.pop();
                        Return::None
                    }
                }
                _ => Return::ProcessChildren,
            },
            _ => Return::ProcessChildren,
        })
    }
}