mod op; mod cond; pub use op::Op; pub use cond::Cond; use crate::data::literal::{Label, RoutineName}; use std::sync::atomic::{AtomicU32}; use std::collections::HashMap; use crate::error::{AsmError, Error}; /// A higher-level instruction pub struct Instr { pub op: Op, pub branches: Option)>>, } /// A routine pub struct Routine { pub name: RoutineName, pub body: Vec, } /// A trait for something that can turn into multiple instructions pub trait Flatten { fn flatten(self, label_num: &AtomicU32) -> Result, Error>; } impl Flatten for Instr { fn flatten(self, label_num: &AtomicU32) -> Result, Error> { let mut ops = vec![self.op]; if let Some(branches) = self.branches { let labels = HashMap::::new(); let _branch_count = branches.len(); for (_cnt, (cond, branch)) in branches.into_iter().enumerate() { if labels.contains_key(&cond) { return Err(Error::Asm(AsmError::ConditionalAlreadyUsed(cond))); } let next_lbl = Label::unique(label_num); ops.push(Op::JumpIf(!cond, next_lbl.clone())); for branch_instr in branch { ops.extend(branch_instr.flatten(label_num)?); } ops.push(Op::Label(next_lbl)); } } Ok(ops) } } impl Flatten for Routine { fn flatten(self, label_num: &AtomicU32) -> Result, Error> { let mut ops = vec![ Op::Routine(self.name.clone()), ]; for instr in self.body { ops.extend(instr.flatten(label_num)?); } ops.push(Op::Barrier(Some(format!("Routine \"{}\" overrun", self.name).into()))); Ok(ops) } }