use crate::ksm::sections::{ArgumentSection, CodeSection, DebugSection};
use crate::ksm::KSMFile;
use std::marker::PhantomData;
#[allow(missing_docs)]
#[allow(missing_debug_implementations)]
pub mod completeness {
#[non_exhaustive]
pub struct Empty;
#[non_exhaustive]
pub struct WithArgSection;
#[non_exhaustive]
pub struct WithCodeSections;
#[non_exhaustive]
pub struct WithDebugSection;
#[non_exhaustive]
pub struct WithArgAndCode;
#[non_exhaustive]
pub struct WithArgAndDebug;
#[non_exhaustive]
pub struct WithCodeAndDebug;
#[non_exhaustive]
pub struct Complete;
}
macro_rules! with_arg_section {
($transition: ty) => {
pub fn with_arg_section(self, arg_section: ArgumentSection) -> KSMFileBuilder<$transition> {
KSMFileBuilder {
argument_section: Some(arg_section),
code_sections: self.code_sections,
debug_section: self.debug_section,
completeness: PhantomData,
}
}
};
}
macro_rules! with_code_section {
($transition: ty) => {
pub fn with_code_section(
mut self,
code_section: CodeSection,
) -> KSMFileBuilder<$transition> {
self.code_sections.push(code_section);
KSMFileBuilder {
argument_section: self.argument_section,
code_sections: self.code_sections,
debug_section: self.debug_section,
completeness: PhantomData,
}
}
};
}
macro_rules! with_code_sections {
($transition: ty) => {
pub fn with_code_sections(
mut self,
code_sections: Vec<CodeSection>,
) -> KSMFileBuilder<$transition> {
self.code_sections.extend(code_sections);
KSMFileBuilder {
argument_section: self.argument_section,
code_sections: self.code_sections,
debug_section: self.debug_section,
completeness: PhantomData,
}
}
};
}
macro_rules! with_debug_section {
($transition: ty) => {
pub fn with_debug_section(
self,
debug_section: DebugSection,
) -> KSMFileBuilder<$transition> {
KSMFileBuilder {
argument_section: self.argument_section,
code_sections: self.code_sections,
debug_section: Some(debug_section),
completeness: PhantomData,
}
}
};
}
#[must_use]
#[derive(Debug, Clone)]
pub struct KSMFileBuilder<Completeness = completeness::Empty> {
argument_section: Option<ArgumentSection>,
code_sections: Vec<CodeSection>,
debug_section: Option<DebugSection>,
completeness: PhantomData<Completeness>,
}
impl Default for KSMFileBuilder<completeness::Empty> {
fn default() -> Self {
KSMFileBuilder::new()
}
}
impl KSMFileBuilder {
pub const fn new() -> Self {
Self {
argument_section: None,
code_sections: Vec::new(),
debug_section: None,
completeness: PhantomData,
}
}
}
impl KSMFileBuilder<completeness::Empty> {
with_arg_section!(completeness::WithArgSection);
with_code_section!(completeness::WithCodeSections);
with_code_sections!(completeness::WithCodeSections);
with_debug_section!(completeness::WithDebugSection);
}
impl KSMFileBuilder<completeness::WithArgSection> {
with_code_section!(completeness::WithArgAndCode);
with_code_sections!(completeness::WithArgAndCode);
with_debug_section!(completeness::WithArgAndDebug);
}
impl KSMFileBuilder<completeness::WithCodeSections> {
with_arg_section!(completeness::WithArgAndCode);
with_code_section!(completeness::WithCodeSections);
with_code_sections!(completeness::WithCodeSections);
with_debug_section!(completeness::WithCodeAndDebug);
}
impl KSMFileBuilder<completeness::WithDebugSection> {
with_arg_section!(completeness::WithArgAndDebug);
with_code_section!(completeness::WithCodeAndDebug);
with_code_sections!(completeness::WithCodeAndDebug);
}
impl KSMFileBuilder<completeness::WithArgAndCode> {
with_code_section!(completeness::WithArgAndCode);
with_code_sections!(completeness::WithArgAndCode);
with_debug_section!(completeness::Complete);
}
impl KSMFileBuilder<completeness::WithArgAndDebug> {
with_code_section!(completeness::Complete);
with_code_sections!(completeness::Complete);
}
impl KSMFileBuilder<completeness::WithCodeAndDebug> {
with_arg_section!(completeness::Complete);
with_code_section!(completeness::WithCodeAndDebug);
with_code_sections!(completeness::WithCodeAndDebug);
}
impl KSMFileBuilder<completeness::Complete> {
with_code_section!(completeness::Complete);
with_code_sections!(completeness::Complete);
pub fn finish(self) -> KSMFile {
self.into()
}
}
impl From<KSMFileBuilder<completeness::Complete>> for KSMFile {
fn from(builder: KSMFileBuilder<completeness::Complete>) -> Self {
Self::new_from_parts(
builder.argument_section.unwrap(),
builder.code_sections,
builder.debug_section.unwrap(),
)
}
}
#[cfg(test)]
mod tests {
use crate::ksm::sections::{
ArgumentSection, CodeSection, CodeType, DebugEntry, DebugRange, DebugSection,
};
use crate::ksm::{Instr, KSMFile, KSMFileBuilder};
use crate::{KOSValue, Opcode};
#[test]
fn nominal() {
let arg_section = ArgumentSection::new()
.with_arguments_unchecked(vec![
KOSValue::Null,
KOSValue::ArgMarker,
KOSValue::ScalarInt(4),
])
.0;
let code_sections = vec![CodeSection::new(CodeType::Function)
.with_instructions(vec![Instr::ZeroOp(Opcode::Add)])];
let debug_section = DebugSection::new_empty()
.with_entries(vec![DebugEntry::new(2).with_range(DebugRange::new(2, 10))]);
let ksm1 = KSMFile::new_from_parts(
arg_section.clone(),
code_sections.clone(),
debug_section.clone(),
);
let ksm2 = KSMFileBuilder::new()
.with_arg_section(arg_section)
.with_code_sections(code_sections)
.with_debug_section(debug_section)
.finish();
assert_eq!(ksm1, ksm2);
}
#[test]
fn multiple_code_additions() {
let arg_section = ArgumentSection::new()
.with_arguments_unchecked(vec![
KOSValue::Null,
KOSValue::ArgMarker,
KOSValue::ScalarInt(4),
])
.0;
let code_section_1 = CodeSection::new(CodeType::Function)
.with_instructions(vec![Instr::ZeroOp(Opcode::Add)]);
let code_section_2 = CodeSection::new(CodeType::Initialization)
.with_instructions(vec![Instr::ZeroOp(Opcode::Pop)]);
let code_sections = vec![code_section_1.clone(), code_section_2.clone()];
let debug_section = DebugSection::new_empty()
.with_entries(vec![DebugEntry::new(2).with_range(DebugRange::new(2, 10))]);
let ksm1 =
KSMFile::new_from_parts(arg_section.clone(), code_sections, debug_section.clone());
let ksm2 = KSMFileBuilder::new()
.with_arg_section(arg_section)
.with_code_section(code_section_1)
.with_code_section(code_section_2)
.with_debug_section(debug_section)
.finish();
assert_eq!(ksm1, ksm2);
}
}