Some checks failed
per-push tests / build (push) Failing after 39s
per-push tests / test-frontend (push) Has been skipped
per-push tests / timed-decomposer-parse (push) Has been skipped
per-push tests / test-utility (push) Has been skipped
per-push tests / test-backend (push) Has been skipped
This commit implements Display and FromStr for Datum, Operation, Operand, Instruction, and Program types. Additionally, tests are added for the new routines. This change was implemented in furtherance of a command line assembler and disassembler, as well as for the implementation of a debugger.
290 lines
11 KiB
Rust
290 lines
11 KiB
Rust
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<Description>,
|
|
pub data_types: Vec<Description>,
|
|
pub instructions: Vec<Instruction>,
|
|
pub addressing_modes: Vec<AddressingMode>,
|
|
}
|
|
|
|
#[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<String>,
|
|
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<u8> for Operation {\n".to_owned();
|
|
isa_from_byte += " type Error = &'static str;\n";
|
|
isa_from_byte += " fn try_from(v: u8) -> Result<Self, Self::Error> {\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<Self, Self::Err> {\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<Self, Self::Error> {\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<u8, &'static str> {\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 {
|
|
"<doesn't write expression register>"
|
|
};
|
|
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");
|
|
}
|