improve ExportOptions support in assert_export_firrtl!

This commit is contained in:
Jacob Lifshay 2024-10-01 00:05:39 -07:00
parent 186488a82e
commit 9d66fcc548
Signed by: programmerjake
SSH key fingerprint: SHA256:B1iRVvUJkvd7upMIiMqn6OyxvD2SgJkAH3ZnUOj6z+c

View file

@ -2378,15 +2378,17 @@ impl FileBackendTrait for FileBackend {
}
#[doc(hidden)]
#[derive(PartialEq, Eq)]
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct TestBackendPrivate {
pub module_var_name: &'static str,
pub included_fields: &'static [&'static str],
}
impl Default for TestBackendPrivate {
fn default() -> Self {
Self {
module_var_name: "m",
included_fields: &[],
}
}
}
@ -2407,7 +2409,11 @@ impl fmt::Debug for TestBackend {
files,
error_after,
options,
__private: TestBackendPrivate { module_var_name },
__private:
TestBackendPrivate {
module_var_name,
included_fields,
},
} = self;
writeln!(
f,
@ -2415,14 +2421,43 @@ impl fmt::Debug for TestBackend {
)?;
writeln!(f, " assert_export_firrtl! {{")?;
writeln!(f, " {module_var_name} =>")?;
for (file, content) in files {
writeln!(f, " {file:?}: {:?},", DebugAsRawString(content))?;
}
if *error_after != Option::default() {
if *error_after != Option::default() || included_fields.contains(&"error_after") {
writeln!(f, " error_after: {error_after:?},")?;
}
if *options != ExportOptions::default() {
writeln!(f, " options: {options:?},")?;
if *options != ExportOptions::default() || included_fields.contains(&"options") {
struct DebugWithForceIncludeFields<'a> {
options: ExportOptions,
included_fields: &'a [&'a str],
}
impl fmt::Debug for DebugWithForceIncludeFields<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.options.debug_fmt(f, |field| {
self.included_fields.iter().any(|included_field| {
if let Some(("options", suffix)) = included_field.split_once(".") {
suffix == field
} else {
false
}
})
})
}
}
let options_str = format!(
"{:#?}",
DebugWithForceIncludeFields {
options: *options,
included_fields
}
);
let mut sep = " options: ";
for line in options_str.lines() {
write!(f, "{sep}{line}")?;
sep = "\n ";
}
writeln!(f, ",")?;
}
for (file, content) in files {
writeln!(f, " {file:?}: {:?},", DebugAsRawString(content))?;
}
write!(f, " }};")
}
@ -2507,6 +2542,7 @@ fn export_impl(
let ExportOptions {
simplify_memories: do_simplify_memories,
simplify_enums: do_simplify_enums,
__private: _,
} = options;
if let Some(kind) = do_simplify_enums {
top_module =
@ -2573,30 +2609,71 @@ impl clap::builder::TypedValueParser for OptionSimplifyEnumsKindValueParser {
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct ExportOptionsPrivate(());
#[derive(clap::Parser, Copy, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct ExportOptions {
#[clap(long = "no-simplify-memories", action = clap::ArgAction::SetFalse)]
pub simplify_memories: bool,
#[clap(long, value_parser = OptionSimplifyEnumsKindValueParser, default_value = OptionSimplifyEnumsKindValueParser::NONE_NAME)]
pub simplify_enums: std::option::Option<SimplifyEnumsKind>,
#[doc(hidden)]
#[clap(skip = ExportOptionsPrivate(()))]
/// `#[non_exhaustive]` except allowing struct update syntax
pub __private: ExportOptionsPrivate,
}
impl fmt::Debug for ExportOptions {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.debug_fmt(f, |_| false)
}
}
impl ExportOptions {
fn debug_fmt(
&self,
f: &mut fmt::Formatter<'_>,
force_include_field: impl Fn(&str) -> bool,
) -> fmt::Result {
let Self {
simplify_memories,
simplify_enums,
__private: _,
} = *self;
f.write_str("ExportOptions {")?;
if f.alternate() {
f.write_str("\n")?;
}
let mut sep = if f.alternate() { "\n " } else { " " };
let comma_sep = if f.alternate() { ",\n " } else { ", " };
let default = ExportOptions::default();
if self.simplify_memories != default.simplify_memories {
write!(f, "{sep}simplify_memories: {:?}", self.simplify_memories)?;
if simplify_memories != default.simplify_memories
|| force_include_field("simplify_memories")
{
write!(f, "{sep}simplify_memories: {:?}", simplify_memories)?;
sep = comma_sep;
}
if self.simplify_enums != default.simplify_enums {
write!(f, "{sep}simplify_enums: {:?}", self.simplify_enums)?;
if simplify_enums != default.simplify_enums || force_include_field("simplify_enums") {
write!(f, "{sep}simplify_enums: ")?;
macro_rules! debug_cases {
($($ident:ident $(($($args:tt)*))?,)*) => {
match simplify_enums {
// use more complex stringify to avoid the compiler inserting spaces
$($ident $(($($args)*))? => {
f.write_str(concat!(
stringify!($ident),
$("(",
$(stringify!($args),)*
")")?
))?;
})*
}
};
}
debug_cases! {
Some(SimplifyEnumsKind::SimplifyToEnumsWithNoBody),
Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts),
Some(SimplifyEnumsKind::ReplaceWithUInt),
None,
}
sep = comma_sep;
}
write!(
@ -2612,6 +2689,7 @@ impl Default for ExportOptions {
Self {
simplify_memories: true,
simplify_enums: None,
__private: ExportOptionsPrivate(()),
}
}
}
@ -2630,7 +2708,17 @@ pub fn export<T: BundleType, B: FileBackendTrait>(
#[doc(hidden)]
#[track_caller]
pub fn assert_export_firrtl_impl<T: BundleType>(top_module: &Module<T>, expected: TestBackend) {
let result = export(TestBackend::default(), top_module, expected.options).unwrap();
let result = export(
TestBackend {
files: BTreeMap::default(),
error_after: expected.error_after,
options: expected.options,
__private: expected.__private,
},
top_module,
expected.options,
)
.unwrap();
if result != expected {
panic!(
"assert_export_firrtl failed:\nyou can update the expected output by using:\n-------START-------\n{result:?}\n-------END-------"
@ -2647,21 +2735,69 @@ pub fn make_test_expected_files(v: &[(&str, &str)]) -> BTreeMap<String, String>
macro_rules! assert_export_firrtl {
{
$m:ident =>
$($file_name:literal: $file_contents:literal,)*
$($field:ident: $value:expr,)*
@parsed_fields($($field_strings:expr,)*)
$($file_name:literal: $file_contents:literal,)*
} => {
$crate::firrtl::assert_export_firrtl_impl(
&$m,
$crate::firrtl::TestBackend {
$($field: $value,)*
files: $crate::firrtl::make_test_expected_files(&[
$(($file_name, $file_contents),)*
]),
$($field: $value,)*
__private: $crate::firrtl::TestBackendPrivate {
module_var_name: stringify!($m),
included_fields: &[$($field_strings,)*],
},
..<$crate::firrtl::TestBackend as $crate::__std::default::Default>::default()
},
);
};
{
$m:ident =>
$($parsed_fields:ident: $parsed_field_values:expr,)*
@parsed_fields($($field_strings:expr,)*)
options: ExportOptions {
$($export_option_fields:ident: $parsed_export_option_field_values:expr,)*
..$export_option_default:expr
},
$($rest:tt)*
} => {
$crate::assert_export_firrtl!(
$m =>
$($parsed_fields: $parsed_field_values,)*
options: ExportOptions {
$($export_option_fields: $parsed_export_option_field_values,)*
..$export_option_default
},
@parsed_fields($($field_strings,)* "options", $(concat!("options.", stringify!($export_option_fields)),)*)
$($rest)*
);
};
{
$m:ident =>
$($parsed_fields:ident: $parsed_field_values:expr,)*
@parsed_fields($($field_strings:expr,)*)
$field:ident: $field_value:expr,
$($rest:tt)*
} => {
$crate::assert_export_firrtl!(
$m =>
$($parsed_fields: $parsed_field_values,)*
$field: $field_value,
@parsed_fields($($field_strings,)* stringify!($field),)
$($rest)*
);
};
{
$m:ident =>
$($rest:tt)*
} => {
$crate::assert_export_firrtl!(
$m =>
@parsed_fields()
$($rest)*
);
};
}