use std::{env, fs}; use std::fs::File; use std::io::{BufWriter, Write}; use std::path::Path; use serde::Deserialize; const DOCUMENTATION_TITLE: &str = r" ▗▖ ▗▖▗▖ ▗▖▗▄▄▖ ▗▖ ▗▖ ▗▄▖ ▗▄▄▄▖ ▗▖ ▗▖▗▖ ▗▖ ▐▌ ▐▌ ▝▚▞▘ ▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌ ▐▌▐▛▚▞▜▌ ▐▛▀▜▌ ▐▌ ▐▛▀▘ ▐▛▀▜▌▐▛▀▜▌▐▛▀▀▘ ▐▌ ▐▌▐▌ ▐▌ ▐▌ ▐▌ ▐▌ ▐▌ ▐▌ ▐▌▐▌ ▐▌▐▙▄▄▖ ▝▚▞▘ ▐▌ ▐▌ "; const DOCUMENTATION_MODES: &str = r" ▗▄▖ ▗▄▄▄ ▗▄▄▄ ▗▄▄▖ ▗▄▄▄▖ ▗▄▄▖ ▗▄▄▖▗▄▄▄▖▗▖ ▗▖ ▗▄▄▖ ▗▖ ▗▖ ▗▄▖ ▗▄▄▄ ▗▄▄▄▖ ▗▄▄▖ ▐▌ ▐▌▐▌ █▐▌ █▐▌ ▐▌▐▌ ▐▌ ▐▌ █ ▐▛▚▖▐▌▐▌ ▐▛▚▞▜▌▐▌ ▐▌▐▌ █▐▌ ▐▌ ▐▛▀▜▌▐▌ █▐▌ █▐▛▀▚▖▐▛▀▀▘ ▝▀▚▖ ▝▀▚▖ █ ▐▌ ▝▜▌▐▌▝▜▌ ▐▌ ▐▌▐▌ ▐▌▐▌ █▐▛▀▀▘ ▝▀▚▖ ▐▌ ▐▌▐▙▄▄▀▐▙▄▄▀▐▌ ▐▌▐▙▄▄▖▗▄▄▞▘▗▄▄▞▘▗▄█▄▖▐▌ ▐▌▝▚▄▞▘ ▐▌ ▐▌▝▚▄▞▘▐▙▄▄▀▐▙▄▄▖▗▄▄▞▘ "; const DOCUMENTATION_REGS: &str = r" ▗▄▄▖ ▗▄▄▄▖ ▗▄▄▖▗▄▄▄▖ ▗▄▄▖▗▄▄▄▖▗▄▄▄▖▗▄▄▖ ▗▄▄▖ ▐▌ ▐▌▐▌ ▐▌ █ ▐▌ █ ▐▌ ▐▌ ▐▌▐▌ ▐▛▀▚▖▐▛▀▀▘▐▌▝▜▌ █ ▝▀▚▖ █ ▐▛▀▀▘▐▛▀▚▖ ▝▀▚▖ ▐▌ ▐▌▐▙▄▄▖▝▚▄▞▘▗▄█▄▖▗▄▄▞▘ █ ▐▙▄▄▖▐▌ ▐▌▗▄▄▞▘ "; const DOCUMENTATION_DTS: &str = r" ▗▄▄▄ ▗▄▖▗▄▄▄▖▗▄▖ ▗▄▄▄▖▗▖ ▗▖▗▄▄▖ ▗▄▄▄▖ ▗▄▄▖ ▐▌ █▐▌ ▐▌ █ ▐▌ ▐▌ █ ▝▚▞▘ ▐▌ ▐▌▐▌ ▐▌ ▐▌ █▐▛▀▜▌ █ ▐▛▀▜▌ █ ▐▌ ▐▛▀▘ ▐▛▀▀▘ ▝▀▚▖ ▐▙▄▄▀▐▌ ▐▌ █ ▐▌ ▐▌ █ ▐▌ ▐▌ ▐▙▄▄▖▗▄▄▞▘ "; const DOCUMENTATION_INSTRS: &str = r" ▗▄▄▄▖▗▖ ▗▖ ▗▄▄▖▗▄▄▄▖▗▄▄▖ ▗▖ ▗▖ ▗▄▄▖▗▄▄▄▖▗▄▄▄▖ ▗▄▖ ▗▖ ▗▖ ▗▄▄▖ █ ▐▛▚▖▐▌▐▌ █ ▐▌ ▐▌▐▌ ▐▌▐▌ █ █ ▐▌ ▐▌▐▛▚▖▐▌▐▌ █ ▐▌ ▝▜▌ ▝▀▚▖ █ ▐▛▀▚▖▐▌ ▐▌▐▌ █ █ ▐▌ ▐▌▐▌ ▝▜▌ ▝▀▚▖ ▗▄█▄▖▐▌ ▐▌▗▄▄▞▘ █ ▐▌ ▐▌▝▚▄▞▘▝▚▄▄▖ █ ▗▄█▄▖▝▚▄▞▘▐▌ ▐▌▗▄▄▞▘ "; #[derive(Deserialize)] struct Document { pub description: String, pub datum: String, pub error_handling: String, pub sym_table: String, pub traps: String, pub registers: Vec, pub data_types: Vec, pub instructions: Vec, pub addressing_modes: Vec, } #[derive(Deserialize)] struct Description { pub name: String, pub description: String, } #[derive(Deserialize)] struct AddressingMode { pub name: String, pub mutable: bool, pub symbol: String, pub example: String, pub description: String, } #[derive(Deserialize)] struct Instruction { pub name: String, pub args: Vec, pub output: String, pub description: String, } fn main() { let mut peak = 0; let output_path = Path::new(&env::var("OUT_DIR").unwrap()) .join("hyphae_instr.rs"); let doc_path = Path::new(&env::var("OUT_DIR").unwrap()) .join("hyphae_manual.txt"); let input = fs::read_to_string("vm.toml") .unwrap(); let mut output_file = BufWriter::new(File::create(&output_path).unwrap()); let mut manual_file = BufWriter::new(File::create(&doc_path).unwrap()); let doc: Document = toml::from_str(&input) .expect("hyphae: failed to parse instructions.toml"); let mut isa = "#[repr(transparent)]\n".to_owned(); isa += "#[derive(Clone, Debug, PartialEq)]\n"; isa += "pub struct Operation(pub u8);\n\n"; let mut isa_from_byte = "impl TryFrom for Operation {\n".to_owned(); isa_from_byte += " type Error = &'static str;\n"; isa_from_byte += " fn try_from(v: u8) -> Result {\n"; isa_from_byte += " match v {\n"; let mut isa_fromstr = "impl FromStr for Operation {\n".to_owned(); isa_fromstr += " type Err = &'static str;\n"; isa_fromstr += " fn from_str(v: &str) -> Result {\n"; isa_fromstr += " let a = v.to_ascii_uppercase();\n"; isa_fromstr += " let v = a.as_str();\n"; isa_fromstr += " match v {\n"; let mut isa_from_str = "impl TryFrom<&str> for Operation {\n".to_owned(); isa_from_str += " type Error = &'static str;\n"; isa_from_str += " fn try_from(v: &str) -> Result {\n"; isa_from_str += " let a = v.to_ascii_uppercase();\n"; isa_from_str += " let v = a.as_str();\n"; isa_from_str += " match v {\n"; let mut isa_into_str = "impl Display for Operation {\n".to_owned(); isa_into_str += " fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), E> {\n"; isa_into_str += " match self.0 {\n"; let mut isa_num_args = "impl Operation {\n".to_owned(); isa_num_args += " pub fn num_args(&self) -> Result {\n"; isa_num_args += " match self.0 {\n"; doc.instructions.iter() .enumerate() .for_each(|(idx, instr)| { let const_name = instr.name.to_ascii_uppercase(); isa += format!("pub const {}: Operation = Operation({});\n", const_name, idx).as_str(); isa_from_byte += format!(" {} => Ok({}),\n", idx, const_name) .as_str(); isa_from_str += format!(" \"{}\" => Ok({}),\n", const_name, const_name).as_str(); isa_fromstr += format!(" \"{}\" => Ok({}),\n", const_name, const_name).as_str(); isa_into_str += format!(" {} => write!(f, \"{}\"),\n", idx, const_name).as_str(); isa_num_args += format!(" {} => Ok({}),\n", idx, instr.args.len()) .as_str(); peak = idx + 1; }); isa_from_byte += " _ => Err(\"illegal instruction\"),\n"; isa_from_byte += " }\n"; isa_from_byte += " }\n"; isa_from_byte += "}\n\n"; isa_from_str += " _ => Err(\"illegal instruction\"),\n"; isa_from_str += " }\n"; isa_from_str += " }\n"; isa_from_str += "}\n\n"; isa_fromstr += " _ => Err(\"illegal instruction\"),\n"; isa_fromstr += " }\n"; isa_fromstr += " }\n"; isa_fromstr += "}\n\n"; isa_into_str += " _ => panic!(\"illegal instruction\"),\n"; isa_into_str += " }\n"; isa_into_str += " }\n"; isa_into_str += "}\n\n"; isa_num_args += " _ => Err(\"illegal instruction\"),\n"; isa_num_args += " }\n"; isa_num_args += " }\n"; isa_num_args += "}\n\n"; isa += "\n"; isa += isa_from_byte.as_str(); isa += isa_from_str.as_str(); isa += isa_fromstr.as_str(); isa += isa_into_str.as_str(); isa += isa_num_args.as_str(); write!(&mut output_file, "use core::str::FromStr;\n").unwrap(); write!(&mut output_file, "use alloc::fmt::{{Display, Formatter, Error as E}};\n\n\n").unwrap(); write!(&mut output_file, "{}", isa).unwrap(); write!(&mut output_file, "\n\npub const TOTAL_INSTRUCTIONS: usize = {};", peak) .unwrap(); let separator = "----------------------------\n"; let section_end = "\n\n\n"; let mut documentation = String::from(""); documentation += DOCUMENTATION_TITLE; documentation += separator; documentation += &doc.description; documentation += section_end; documentation += "Datum\n"; documentation += separator; documentation += &doc.datum; documentation += section_end; documentation += "Error Handling\n"; documentation += separator; documentation += &doc.error_handling; documentation += section_end; documentation += "Symbol Table\n"; documentation += separator; documentation += &doc.sym_table; documentation += section_end; documentation += "Traps\n"; documentation += separator; documentation += &doc.traps; documentation += section_end; documentation += DOCUMENTATION_MODES; documentation += "\n\n"; doc.addressing_modes.iter() .for_each(|i| { documentation += &i.name; documentation += "\n"; documentation += separator; if i.mutable { documentation += "Provides mutable access.\n\n"; } documentation += "symbol: "; documentation += &i.symbol; documentation += "\nexample: "; documentation += &i.example; documentation += "\n\n"; documentation += &i.description; documentation += section_end; }); documentation += "\n"; documentation += DOCUMENTATION_DTS; documentation += "\n\n"; doc.data_types.iter() .for_each(|i| { documentation += "> "; documentation += &i.name; documentation += "\n"; documentation += &i.description; documentation += section_end; }); documentation += DOCUMENTATION_REGS; documentation += "\n\n"; doc.registers.iter() .for_each(|i| { documentation += "> "; documentation += &i.name; documentation += "\n"; documentation += &i.description; documentation += section_end; }); documentation += DOCUMENTATION_INSTRS; documentation += "\n\n"; doc.instructions.iter() .for_each(|i| { documentation += &i.name; documentation += "\n"; documentation += separator; documentation += "Arguments:"; i.args.iter().for_each(|j| { documentation += " "; documentation += j; }); documentation += "\n"; documentation += "Sets expression register to: "; documentation += if !i.output.is_empty() { &i.output } else { "" }; documentation += "\n\n"; documentation += &i.description; documentation += section_end; }); write!(manual_file, "{}", documentation).unwrap(); println!("cargo::rerun-if-changed=build.rs"); println!("cargo::rerun-if-changed=vm.toml"); }