holey-bytes/hblang/build.rs

184 lines
5.8 KiB
Rust
Raw Normal View History

2024-05-10 14:33:42 -05:00
#![feature(iter_next_chunk)]
2024-09-03 15:34:17 -05:00
use std::{collections::HashSet, fmt::Write};
2024-05-10 14:33:42 -05:00
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=../hbbytecode/instructions.in");
2024-09-03 15:34:17 -05:00
gen_instrs()?;
Ok(())
}
2024-09-03 15:34:17 -05:00
fn gen_instrs() -> Result<(), Box<dyn std::error::Error>> {
let mut generated = String::new();
writeln!(generated, "#![allow(dead_code)] #![allow(clippy::upper_case_acronyms)]")?;
'_max_size: {
let max = instructions()
.map(
|[_, _, ty, _]| {
if ty == "N" {
1
} else {
iter_args(ty).map(arg_to_width).sum::<usize>() + 1
}
},
)
.max()
.unwrap();
writeln!(generated, "pub const MAX_SIZE: usize = {max};")?;
}
2024-09-03 15:34:17 -05:00
'_encoders: {
for [op, name, ty, doc] in instructions() {
writeln!(generated, "/// {}", doc.trim_matches('"'))?;
let name = name.to_lowercase();
let args = comma_sep(
iter_args(ty)
.enumerate()
.map(|(i, c)| format!("{}{i}: {}", arg_to_name(c), arg_to_type(c))),
);
writeln!(generated, "pub fn {name}({args}) -> (usize, [u8; MAX_SIZE]) {{")?;
let arg_names =
comma_sep(iter_args(ty).enumerate().map(|(i, c)| format!("{}{i}", arg_to_name(c))));
writeln!(generated, " unsafe {{ crate::encode({ty}({op}, {arg_names})) }}")?;
writeln!(generated, "}}")?;
}
}
2024-09-03 15:34:17 -05:00
'_structs: {
let mut seen = std::collections::HashSet::new();
for [_, _, ty, _] in instructions() {
if !seen.insert(ty) {
continue;
2024-05-10 14:33:42 -05:00
}
2024-09-03 15:34:17 -05:00
let types = comma_sep(iter_args(ty).map(arg_to_type).map(|s| s.to_string()));
writeln!(generated, "#[repr(packed)] pub struct {ty}(u8, {types});")?;
}
}
2024-09-03 15:34:17 -05:00
'_name_list: {
writeln!(generated, "pub const NAMES: [&str; {}] = [", instructions().count())?;
for [_, name, _, _] in instructions() {
writeln!(generated, " \"{}\",", name.to_lowercase())?;
}
writeln!(generated, "];")?;
}
2024-09-03 15:34:17 -05:00
let instr = "Instr";
let oper = "Oper";
2024-05-10 14:33:42 -05:00
2024-09-03 15:34:17 -05:00
'_instr_enum: {
writeln!(generated, "#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)]")?;
writeln!(generated, "pub enum {instr} {{")?;
for [id, name, ..] in instructions() {
writeln!(generated, " {name} = {id},")?;
}
writeln!(generated, "}}")?;
2024-05-10 14:33:42 -05:00
}
2024-09-03 15:34:17 -05:00
'_arg_kind: {
writeln!(generated, "#[derive(Debug, Clone, Copy, PartialEq, Eq)]")?;
writeln!(generated, "pub enum {oper} {{")?;
let mut seen = HashSet::new();
for ty in instructions().flat_map(|[.., ty, _]| iter_args(ty)) {
if !seen.insert(ty) {
continue;
}
writeln!(generated, " {ty}({}),", arg_to_type(ty))?;
}
writeln!(generated, "}}")?;
}
'_parse_opers: {
writeln!(
generated,
"/// This assumes the instruction byte is still at the beginning of the buffer"
)?;
writeln!(generated, "pub fn parse_args(bytes: &mut &[u8], kind: {instr}, buf: &mut Vec<{oper}>) -> Option<()> {{")?;
writeln!(generated, " match kind {{")?;
let mut instrs = instructions().collect::<Vec<_>>();
instrs.sort_unstable_by_key(|&[.., ty, _]| ty);
for group in instrs.chunk_by(|[.., a, _], [.., b, _]| a == b) {
let ty = group[0][2];
for &[_, name, ..] in group {
writeln!(generated, " | {instr}::{name}")?;
}
generated.pop();
writeln!(generated, " => {{")?;
if iter_args(ty).count() != 0 {
2024-09-04 09:54:34 -05:00
writeln!(generated, " let data = crate::decode::<{ty}>(bytes)?;")?;
2024-09-03 15:34:17 -05:00
writeln!(
generated,
" buf.extend([{}]);",
comma_sep(
iter_args(ty).zip(1u32..).map(|(t, i)| format!("{oper}::{t}(data.{i})"))
)
)?;
} else {
2024-09-04 09:54:34 -05:00
writeln!(generated, " crate::decode::<{ty}>(bytes)?;")?;
2024-09-03 15:34:17 -05:00
}
2024-05-10 14:33:42 -05:00
2024-09-03 15:34:17 -05:00
writeln!(generated, " }}")?;
}
2024-09-03 15:34:17 -05:00
writeln!(generated, " }}")?;
writeln!(generated, " Some(())")?;
writeln!(generated, "}}")?;
}
2024-05-10 14:33:42 -05:00
2024-09-03 15:34:17 -05:00
std::fs::write("src/instrs.rs", generated)?;
2024-05-10 14:33:42 -05:00
Ok(())
}
fn comma_sep(items: impl Iterator<Item = String>) -> String {
2024-07-08 00:22:53 -05:00
items.map(|item| item.to_string()).collect::<Vec<_>>().join(", ")
}
fn instructions() -> impl Iterator<Item = [&'static str; 4]> {
include_str!("../hbbytecode/instructions.in")
.lines()
.filter_map(|line| line.strip_suffix(';'))
.map(|line| line.splitn(4, ',').map(str::trim).next_chunk().unwrap())
}
fn arg_to_type(arg: char) -> &'static str {
match arg {
'R' | 'B' => "u8",
'H' => "u16",
'W' => "u32",
'D' | 'A' => "u64",
'P' => "i16",
'O' => "i32",
_ => panic!("unknown type: {}", arg),
}
}
fn arg_to_width(arg: char) -> usize {
match arg {
'R' | 'B' => 1,
'H' => 2,
'W' => 4,
'D' | 'A' => 8,
'P' => 2,
'O' => 4,
_ => panic!("unknown type: {}", arg),
}
}
fn arg_to_name(arg: char) -> &'static str {
match arg {
'R' => "reg",
'B' | 'H' | 'W' | 'D' => "imm",
'P' | 'O' => "offset",
'A' => "addr",
_ => panic!("unknown type: {}", arg),
}
}
2024-09-03 15:34:17 -05:00
fn iter_args(ty: &'static str) -> impl Iterator<Item = char> {
ty.chars().filter(|c| *c != 'N')
}