use crate::compiler::instr::flow::{Branch, Element};
use crate::compiler::instr::{InstrDef, InstrId};
use crate::microcode::Microcode;
#[derive(Default, Debug, Clone)]
pub struct InstrBuilder {
flow: Element,
}
impl InstrBuilder {
pub fn new() -> Self {
Default::default()
}
pub fn first(code: impl Into<InstrBuilder>) -> Self {
code.into()
}
pub fn r#yield() -> Self {
Self::first(Microcode::Yield)
}
pub fn cond(
code_if_true: impl Into<InstrBuilder>,
code_if_false: impl Into<InstrBuilder>,
) -> Self {
Self {
flow: Element::Branch(Branch {
code_if_true: Box::new(code_if_true.into().flow),
code_if_false: Box::new(code_if_false.into().flow),
}),
}
}
pub fn if_true(code_if_true: impl Into<InstrBuilder>) -> Self {
InstrBuilder::cond(code_if_true, InstrBuilder::new())
}
pub fn if_false(code_if_false: impl Into<InstrBuilder>) -> Self {
InstrBuilder::cond(InstrBuilder::new(), code_if_false)
}
pub fn then_cond(
self,
code_if_true: impl Into<InstrBuilder>,
code_if_false: impl Into<InstrBuilder>,
) -> Self {
self.then(InstrBuilder::cond(code_if_true, code_if_false))
}
pub fn then_if_true(self, code_if_true: impl Into<InstrBuilder>) -> Self {
self.then_cond(code_if_true, InstrBuilder::new())
}
pub fn then_if_false(self, code_if_false: impl Into<InstrBuilder>) -> Self {
self.then_cond(InstrBuilder::new(), code_if_false)
}
pub fn read<R: MicrocodeReadable>(from: R) -> Self {
from.to_read()
}
pub fn write<W: MicrocodeWritable>(to: W) -> Self {
to.to_write()
}
pub fn then(mut self, code: impl Into<InstrBuilder>) -> Self {
let other = code.into();
self.flow.extend_block(other.flow);
self
}
pub fn then_yield(self) -> Self {
self.then(Microcode::Yield)
}
pub fn then_read<R: MicrocodeReadable>(self, from: R) -> Self {
self.then(from.to_read())
}
pub fn then_write<W: MicrocodeWritable>(self, to: W) -> Self {
self.then(to.to_write())
}
pub fn build(mut self, id: InstrId) -> InstrDef {
add_fetch_next_to_end(&mut self.flow);
InstrDef {
id,
microcode: self.flow.flatten(),
flow: self.flow,
}
}
}
fn add_fetch_next_to_end(elem: &mut Element) {
match elem {
Element::Microcode(microcode) => {
match microcode {
Microcode::FetchNextInstruction
| Microcode::ParseOpcode
| Microcode::ParseCBOpcode => {}
_ => elem.extend_block(Element::Microcode(Microcode::FetchNextInstruction)),
}
}
Element::Block(block) => match block.elements.last_mut() {
Some(last) => {
if let Element::Microcode(microcode) = last {
match microcode {
Microcode::FetchNextInstruction
| Microcode::ParseOpcode
| Microcode::ParseCBOpcode => {}
_ => block
.elements
.push(Element::Microcode(Microcode::FetchNextInstruction)),
}
} else {
add_fetch_next_to_end(last)
}
}
None => {
*elem = Element::Microcode(Microcode::FetchNextInstruction)
}
},
Element::Branch(Branch {
code_if_true,
code_if_false,
}) => {
let true_end_terminal = code_if_true.ends_with_terminal();
let false_end_terminal = code_if_false.ends_with_terminal();
if !true_end_terminal && !false_end_terminal {
elem.extend_block(Element::Microcode(Microcode::FetchNextInstruction));
} else if !true_end_terminal {
add_fetch_next_to_end(code_if_true);
} else if !false_end_terminal {
add_fetch_next_to_end(code_if_false);
}
}
}
}
impl<T: Into<InstrBuilder>> From<Option<T>> for InstrBuilder {
fn from(value: Option<T>) -> Self {
value.map(Into::into).unwrap_or_default()
}
}
impl From<Microcode> for InstrBuilder {
fn from(value: Microcode) -> Self {
if let Microcode::Skip { .. } | Microcode::SkipIf { .. } = value {
panic!("InstrBuilder does not permit explicit skips. Use `cond`.");
}
Self {
flow: Element::Microcode(value),
}
}
}
pub trait MicrocodeReadable {
fn to_read(self) -> InstrBuilder;
}
pub trait MicrocodeWritable {
fn to_write(self) -> InstrBuilder;
}