Files
ic10emu/ic10emu/build.rs
2024-03-22 04:09:04 -07:00

436 lines
14 KiB
Rust

use convert_case::{Case, Casing};
use std::{
collections::HashSet,
env,
fs::{self, File},
io::{BufRead, BufReader, BufWriter, Read, Write},
path::{Path, PathBuf},
};
fn write_logictypes(logictypes_grammar: &mut HashSet<String>) {
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("logictypes.rs");
let output_file = File::create(dest_path).unwrap();
let mut writer = BufWriter::new(&output_file);
let mut logictype_set = ::phf_codegen::Set::new();
let mut logictype_lookup_map_builder = ::phf_codegen::Map::new();
let l_infile = Path::new("data/logictypes.txt");
let l_contents = fs::read_to_string(l_infile).unwrap();
for line in l_contents.lines().filter(|l| !l.trim().is_empty()) {
let mut it = line.splitn(3, ' ');
let name = it.next().unwrap();
let val_str = it.next().unwrap();
let val: Option<u8> = val_str.parse().ok();
logictype_set.entry(name);
logictypes_grammar.insert(name.to_string());
if let Some(v) = val {
logictype_lookup_map_builder.entry(name, &format!("{}u8", v));
}
}
let mut slotlogictype_set = ::phf_codegen::Set::new();
let mut slotlogictype_lookup_map_builder = ::phf_codegen::Map::new();
let sl_infile = Path::new("data/slotlogictypes.txt");
let sl_contents = fs::read_to_string(sl_infile).unwrap();
for line in sl_contents.lines().filter(|l| !l.trim().is_empty()) {
let mut it = line.splitn(3, ' ');
let name = it.next().unwrap();
let val_str = it.next().unwrap();
let val: Option<u8> = val_str.parse().ok();
slotlogictype_set.entry(name);
logictypes_grammar.insert(name.to_string());
if let Some(v) = val {
slotlogictype_lookup_map_builder.entry(name, &format!("{}u8", v));
}
}
write!(
&mut writer,
"pub(crate) const LOGIC_TYPES: phf::Set<&'static str> = {};\n",
logictype_set.build()
)
.unwrap();
write!(
&mut writer,
"pub(crate) const LOGIC_TYPE_LOOKUP: phf::Map<&'static str, u8> = {};\n",
logictype_lookup_map_builder.build()
)
.unwrap();
println!("cargo:rerun-if-changed=data/logictypes.txt");
write!(
&mut writer,
"pub(crate) const SLOT_LOGIC_TYPES: phf::Set<&'static str> = {};\n",
slotlogictype_set.build()
)
.unwrap();
write!(
&mut writer,
"pub(crate) const SLOT_TYPE_LOOKUP: phf::Map<&'static str, u8> = {};\n",
slotlogictype_lookup_map_builder.build()
)
.unwrap();
println!("cargo:rerun-if-changed=data/slotlogictypes.txt");
}
fn write_enums(enums_grammar: &mut HashSet<String>) {
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("enums.rs");
let output_file = File::create(dest_path).unwrap();
let mut writer = BufWriter::new(&output_file);
let mut enums_set = ::phf_codegen::Set::new();
let mut enums_lookup_map_builder = ::phf_codegen::Map::new();
let mut check_set = std::collections::HashSet::new();
let e_infile = Path::new("data/enums.txt");
let e_contents = fs::read_to_string(e_infile).unwrap();
for line in e_contents.lines().filter(|l| !l.trim().is_empty()) {
let mut it = line.splitn(2, ' ');
let name = it.next().unwrap();
let val_str = it.next().unwrap();
let val: Option<u8> = val_str.parse().ok();
if !check_set.contains(name) {
enums_set.entry(name);
enums_grammar.insert(name.to_string());
check_set.insert(name);
}
if let Some(v) = val {
enums_lookup_map_builder.entry(name, &format!("{}u8", v));
}
}
write!(
&mut writer,
"pub(crate) const ENUMS: phf::Set<&'static str> = {};\n",
enums_set.build()
)
.unwrap();
write!(
&mut writer,
"pub(crate) const ENUM_LOOKUP: phf::Map<&'static str, u8> = {};\n",
enums_lookup_map_builder.build()
)
.unwrap();
println!("cargo:rerun-if-changed=data/enums.txt");
}
fn write_modes(logictypes_grammar: &mut HashSet<String>) {
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("modes.rs");
let output_file = File::create(dest_path).unwrap();
let mut writer = BufWriter::new(&output_file);
let mut batchmode_set = ::phf_codegen::Set::new();
let mut batchmode_lookup_map_builder = ::phf_codegen::Map::new();
let b_infile = Path::new("data/batchmodes.txt");
let b_contents = fs::read_to_string(b_infile).unwrap();
for line in b_contents.lines().filter(|l| !l.trim().is_empty()) {
let mut it = line.splitn(3, ' ');
let name = it.next().unwrap();
let val_str = it.next().unwrap();
let val: Option<u8> = val_str.parse().ok();
batchmode_set.entry(name);
logictypes_grammar.insert(name.to_string());
if let Some(v) = val {
batchmode_lookup_map_builder.entry(name, &format!("{}u8", v));
}
}
let mut reagentmode_set = ::phf_codegen::Set::new();
let mut reagentmode_lookup_map_builder = ::phf_codegen::Map::new();
let r_infile = Path::new("data/reagentmodes.txt");
let r_contents = fs::read_to_string(r_infile).unwrap();
for line in r_contents.lines().filter(|l| !l.trim().is_empty()) {
let mut it = line.splitn(3, ' ');
let name = it.next().unwrap();
let val_str = it.next().unwrap();
let val: Option<u8> = val_str.parse().ok();
reagentmode_set.entry(name);
logictypes_grammar.insert(name.to_string());
if let Some(v) = val {
reagentmode_lookup_map_builder.entry(name, &format!("{}u8", v));
}
}
write!(
&mut writer,
"pub(crate) const BATCH_MODES: phf::Set<&'static str> = {};\n",
batchmode_set.build()
)
.unwrap();
write!(
&mut writer,
"pub(crate) const BATCH_MODE_LOOKUP: phf::Map<&'static str, u8> = {};\n",
batchmode_lookup_map_builder.build()
)
.unwrap();
println!("cargo:rerun-if-changed=data/batchmodes.txt");
write!(
&mut writer,
"pub(crate) const REAGENT_MODES: phf::Set<&'static str> = {};\n",
reagentmode_set.build()
)
.unwrap();
write!(
&mut writer,
"pub(crate) const REAGENT_MODE_LOOKUP: phf::Map<&'static str, u8> = {};\n",
reagentmode_lookup_map_builder.build()
)
.unwrap();
println!("cargo:rerun-if-changed=data/reagentmodes.txt");
}
fn write_constants(constants_grammar: &mut HashSet<String>) {
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("constants.rs");
let output_file = File::create(dest_path).unwrap();
let mut writer = BufWriter::new(&output_file);
let mut constants_set = ::phf_codegen::Set::new();
let mut constants_lookup_map_builder = ::phf_codegen::Map::new();
let infile = Path::new("data/constants.txt");
let contents = fs::read_to_string(infile).unwrap();
for line in contents.lines().filter(|l| !l.trim().is_empty()) {
let mut it = line.splitn(3, ' ');
let name = it.next().unwrap();
let constant = it.next().unwrap();
constants_set.entry(name);
constants_grammar.insert(name.to_string());
constants_lookup_map_builder.entry(name, constant);
}
write!(
&mut writer,
"pub(crate) const CONSTANTS: phf::Set<&'static str> = {};\n",
constants_set.build()
)
.unwrap();
println!("cargo:rerun-if-changed=data/constants.txt");
}
fn write_logictypes_grammar(logictypes: &HashSet<String>) {
let dest_path = Path::new("src/grammar/ic10").join("logictypes.rs");
let output_file = File::create(dest_path).unwrap();
let mut writer = BufWriter::new(&output_file);
write!(
&mut writer,
"// GENERATED CODE DO NOT MODIFY\n\
#[derive(PartialEq, Debug)]\n\
pub enum LogicType {{\n\
"
)
.unwrap();
for typ in logictypes {
let enum_name = typ.to_case(Case::Pascal);
write!(
&mut writer,
" {}( #[rust_sitter::leaf(text = \"{typ}\", transform = |s| s.to_string() )] String ),\n",
&enum_name
)
.unwrap();
}
write!(&mut writer, "}}\n").unwrap();
}
fn write_enums_grammar(enums: HashSet<String>) {
let dest_path = Path::new("src/grammar/ic10").join("enums.rs");
let output_file = File::create(dest_path).unwrap();
let mut writer = BufWriter::new(&output_file);
write!(
&mut writer,
"// GENERATED CODE DO NOT MODIFY\n\
#[derive(PartialEq, Debug)]\n\
pub enum Enum {{\n\
"
)
.unwrap();
for typ in enums {
let enum_name = typ.replace(".", "").to_case(Case::Pascal);
write!(
&mut writer,
" {}( #[rust_sitter::leaf(text = \"{typ}\", transform = |s| s.to_string() )] String ),\n",
&enum_name
)
.unwrap();
}
write!(&mut writer, "}}\n").unwrap();
}
fn write_constants_grammar(constants: HashSet<String>) {
let dest_path = Path::new("src/grammar/ic10").join("constants.rs");
let output_file = File::create(dest_path).unwrap();
let mut writer = BufWriter::new(&output_file);
write!(
&mut writer,
"// GENERATED CODE DO NOT MODIFY\n\
#[derive(PartialEq, Debug)]\n\
pub enum Constant {{\n\
"
)
.unwrap();
for typ in constants {
let enum_name = typ.replace(".", "").to_case(Case::Pascal);
write!(
&mut writer,
" {}( #[rust_sitter::leaf(text = \"{typ}\", transform = |s| s.to_string() )] String ),\n",
&enum_name
)
.unwrap();
}
write!(&mut writer, "}}\n").unwrap();
}
fn write_instructions_grammar() {
let dest_path = Path::new("src/grammar/ic10").join("instructions.rs");
let output_file = File::create(dest_path).unwrap();
let mut writer = BufWriter::new(&output_file);
let mut instructions = HashSet::new();
let infile = Path::new("data/instructions.txt");
let contents = fs::read_to_string(infile).unwrap();
for line in contents.lines() {
let mut it = line.split(' ');
let instruction = it.next().unwrap();
instructions.insert(instruction.to_string());
}
write!(
&mut writer,
"// GENERATED CODE DO NOT MODIFY\n\
#[derive(PartialEq, Debug)]\n\
pub enum InstructionOp {{\n\
"
)
.unwrap();
for typ in instructions {
let enum_name = typ.to_case(Case::Pascal);
write!(
&mut writer,
" {}( #[rust_sitter::leaf(text = \"{typ}\", transform = |s| s.to_string() )] String ),\n",
&enum_name
)
.unwrap();
}
write!(&mut writer, "}}\n").unwrap();
}
fn patch_grammar() {
let out_path = env::var_os("OUT_DIR").unwrap();
let out_dir = Path::new(&out_path);
let src_dir = Path::new("src");
fs::copy(
src_dir.join("grammar.rs"),
out_dir.join("grammar_unpatched.rs"),
)
.unwrap();
{
let grammar_file = File::open(src_dir.join("grammar.rs")).unwrap();
let output_file = File::create(out_dir.join("grammar_patched.rs")).unwrap();
let mut writer = BufWriter::new(output_file);
let patch_regex = regex::Regex::new(r"^\s+// PATCH ([\w/.]+)\s*$").unwrap();
let mut patch_marker: Option<regex::Regex> = None;
for line in BufReader::new(grammar_file).lines().flatten() {
if let Some(marker) = &patch_marker {
if let Some(_) = marker.captures(&line) {
write!(&mut writer, "{}\n", line).unwrap();
patch_marker = None;
} else {
continue;
}
} else {
if let Some(captures) = patch_regex.captures(&line) {
write!(&mut writer, "{}\n", line).unwrap();
let in_path = captures.get(1).unwrap();
patch_marker = Some(
regex::Regex::new(&format!(r"^\s+// END PATCH {}\s*$", in_path.as_str()))
.unwrap(),
);
let in_buff =
BufReader::new(File::open(src_dir.join(in_path.as_str())).unwrap());
write!(
&mut writer,
" {}\n",
in_buff.lines().flatten().collect::<Vec<_>>().join("\n ")
)
.unwrap();
} else {
write!(&mut writer, "{}\n", line).unwrap();
}
}
}
}
fs::rename(
out_dir.join("grammar_patched.rs"),
src_dir.join("grammar.rs"),
)
.unwrap();
}
fn build_grammar() {
println!("cargo:rerun-if-changed=src/");
// let out_path = env::var_os("OUT_DIR").unwrap();
// let out_dir = Path::new(&out_path);
rust_sitter_tool::build_parsers(&PathBuf::from("src/lib.rs"));
// rust_sitter_tool::build_parsers(&out_dir.join("grammar_patched.rs"));
}
fn main() {
let mut logictype_grammar = HashSet::new();
let mut enums_grammar = HashSet::new();
let mut constants_grammar = HashSet::new();
// write_instructions();
write_logictypes(&mut logictype_grammar);
write_modes(&mut logictype_grammar);
write_constants(&mut constants_grammar);
write_enums(&mut enums_grammar);
write_logictypes_grammar(&logictype_grammar);
write_enums_grammar(enums_grammar);
write_constants_grammar(constants_grammar);
write_instructions_grammar();
patch_grammar();
build_grammar();
}