From 0fa07b64d5ab8017b828bb7e84dd66748a42d25d Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Tue, 3 Dec 2024 17:32:46 -0800 Subject: [PATCH] More --- TODO.md | 6 - .../src/macros/generator.rs | 248 +++++++++++++----- .../src/macros/map_macros.rs | 32 ++- .../src/macros/set_macros.rs | 41 ++- frozen-collections/tests/hash_macro_tests.rs | 12 +- ...ar_macro_tests.rs => scalar_macro_test.rs} | 17 ++ 6 files changed, 261 insertions(+), 95 deletions(-) rename frozen-collections/tests/{scalar_macro_tests.rs => scalar_macro_test.rs} (96%) diff --git a/TODO.md b/TODO.md index 9fe4247..1c99dc3 100644 --- a/TODO.md +++ b/TODO.md @@ -21,16 +21,10 @@ - Add support for empty literal collections in the macros. -- Add support for non-literal values when using the macros. This will end up using a facade - type as implementation. - - Consider adding support for case-insensitive strings. - Extend the Scalar derive macro to support more varieties of enum types. -- In the generator, even when using fz_hash_map and fz_ordered_map, detect if the - keys are all integer or string literals and generate better code accordingly. - ## Type System Nightmares These are some things which I haven't done yet since I can't figure out how to express these things in the diff --git a/frozen-collections-core/src/macros/generator.rs b/frozen-collections-core/src/macros/generator.rs index 61a01d6..6089181 100644 --- a/frozen-collections-core/src/macros/generator.rs +++ b/frozen-collections-core/src/macros/generator.rs @@ -15,7 +15,7 @@ use quote::{format_ident, quote, ToTokens}; use std::fmt::Display; use std::ops::Range; use std::str::FromStr; -use syn::{parse2, Expr, ExprLit, Lit, LitInt, LitStr}; +use syn::{parse2, Expr, Lit, LitInt, LitStr}; struct ProcessedEntry { base: Entry, @@ -46,13 +46,35 @@ impl Output { } #[derive(Clone, Copy)] -pub enum KeyKind { +pub enum AssertedKeyKind { Scalar, String, Hashed, Ordered, } +enum ScalarType { + I8, + I16, + I32, + I64, + ISize, + U8, + U16, + U32, + U64, + USize, +} + +enum EffectiveKeyKind { + AllLiteralScalars(Option), + LiteralAndExpressionScalars, + AllLiteralStrings, + LiteralAndExpressionStrings, + Hashed, + Ordered, +} + struct Generator { entries: Vec, seeds: (u64, u64, u64, u64), @@ -67,7 +89,7 @@ pub fn generate( as_set: bool, key_type: TokenStream, value_type: TokenStream, - key_kind: KeyKind, + asserted_key_kind: AssertedKeyKind, ) -> syn::Result { let mut gen = Generator { entries: vec![], @@ -88,77 +110,139 @@ pub fn generate( )); } - match key_kind { - KeyKind::Scalar => match &gen.entries.clone()[0].key { - Expr::Lit(expr) => gen.decode_scalar_expr(expr), - Expr::Group(group) => match (*group.expr).clone() { - Expr::Lit(expr) => gen.decode_scalar_expr(&expr), - _ => gen.handle_non_literal_scalar_keys(), - }, - _ => gen.handle_non_literal_scalar_keys(), - }, - - KeyKind::String => match &gen.entries[0].key { - Expr::Lit(expr) => match &expr.lit { - Lit::Str(_) => gen.handle_literal_string_keys(), - _ => Err(syn::Error::new_spanned( - expr, - "invalid literal, expecting a string value", - )), - }, - Expr::Group(group) => match (*group.expr).clone() { - Expr::Lit(expr) => match &expr.lit { - Lit::Str(_) => gen.handle_literal_string_keys(), - _ => Err(syn::Error::new_spanned( - expr, - "invalid literal, expecting a string value", - )), - }, - _ => gen.handle_non_literal_hashed_keys(), - }, - _ => gen.handle_non_literal_hashed_keys(), - }, - KeyKind::Hashed => gen.handle_non_literal_hashed_keys(), - KeyKind::Ordered => gen.handle_non_literal_ordered_keys(), + match gen.assess_keys(asserted_key_kind) { + EffectiveKeyKind::AllLiteralScalars(Some(ScalarType::I8)) => { + gen.handle_literal_scalar_keys::("") + } + EffectiveKeyKind::AllLiteralScalars(Some(ScalarType::I16)) => { + gen.handle_literal_scalar_keys::("") + } + EffectiveKeyKind::AllLiteralScalars(Some(ScalarType::I32)) => { + gen.handle_literal_scalar_keys::("") + } + EffectiveKeyKind::AllLiteralScalars(Some(ScalarType::I64)) => { + gen.handle_literal_scalar_keys::("") + } + EffectiveKeyKind::AllLiteralScalars(Some(ScalarType::ISize)) => { + gen.handle_literal_scalar_keys::("") + } + EffectiveKeyKind::AllLiteralScalars(Some(ScalarType::U8)) => { + gen.handle_literal_scalar_keys::("") + } + EffectiveKeyKind::AllLiteralScalars(Some(ScalarType::U16)) => { + gen.handle_literal_scalar_keys::("") + } + EffectiveKeyKind::AllLiteralScalars(Some(ScalarType::U32)) => { + gen.handle_literal_scalar_keys::("") + } + EffectiveKeyKind::AllLiteralScalars(Some(ScalarType::U64)) => { + gen.handle_literal_scalar_keys::("") + } + EffectiveKeyKind::AllLiteralScalars(Some(ScalarType::USize)) => { + gen.handle_literal_scalar_keys::("") + } + EffectiveKeyKind::AllLiteralScalars(None) => { + gen.handle_literal_scalar_keys::("i32") + } + EffectiveKeyKind::LiteralAndExpressionScalars => { + gen.handle_non_literal_scalar_keys() + } + EffectiveKeyKind::AllLiteralStrings => gen.handle_literal_string_keys(), + EffectiveKeyKind::LiteralAndExpressionStrings => { + gen.handle_non_literal_string_keys() + } + EffectiveKeyKind::Hashed => gen.handle_hashed_keys(), + EffectiveKeyKind::Ordered => gen.handle_ordered_keys(), } } - Payload::Vector(expr) => match key_kind { - KeyKind::Scalar => Ok(gen.emit_facade(&expr, "Scalar")), - KeyKind::String => Ok(gen.emit_facade(&expr, "String")), - KeyKind::Hashed => Ok(gen.emit_facade(&expr, "Hash")), - KeyKind::Ordered => Ok(gen.emit_facade(&expr, "Ordered")), + Payload::Vector(expr) => match asserted_key_kind { + AssertedKeyKind::Scalar => Ok(gen.emit_facade(&expr, "Scalar")), + AssertedKeyKind::String => Ok(gen.emit_facade(&expr, "String")), + AssertedKeyKind::Hashed => Ok(gen.emit_facade(&expr, "Hash")), + AssertedKeyKind::Ordered => Ok(gen.emit_facade(&expr, "Ordered")), }, } } impl Generator { - fn decode_scalar_expr(self, expr: &ExprLit) -> syn::Result { - match &expr.lit { - Lit::Int(expr) => match expr.suffix() { - "i8" => self.handle_literal_scalar_keys::(), - "i16" => self.handle_literal_scalar_keys::(), - "i32" | "" => self.handle_literal_scalar_keys::(), - "i64" => self.handle_literal_scalar_keys::(), - "isize" => self.handle_literal_scalar_keys::(), - "u8" => self.handle_literal_scalar_keys::(), - "u16" => self.handle_literal_scalar_keys::(), - "u32" => self.handle_literal_scalar_keys::(), - "u64" => self.handle_literal_scalar_keys::(), - "usize" => self.handle_literal_scalar_keys::(), - _ => Err(syn::Error::new_spanned( - expr, - format!("unknown suffix {} for scalar value", expr.suffix()), - )), - }, - _ => Err(syn::Error::new_spanned( - expr, - "invalid literal, expecting an integer value", - )), + fn assess_keys(&self, asserted_key_kind: AssertedKeyKind) -> EffectiveKeyKind { + let mut num_strings = 0; + let mut num_scalars = 0; + let mut scalar_type: Option = None; + + for entry in &self.entries { + match &entry.key { + Expr::Lit(expr) => match &expr.lit { + Lit::Str(_) => num_strings += 1, + Lit::Int(expr) => { + num_scalars += 1; + + match expr.suffix() { + "i8" => scalar_type = Some(ScalarType::I8), + "i16" => scalar_type = Some(ScalarType::I16), + "i32" => scalar_type = Some(ScalarType::I32), + "i64" => scalar_type = Some(ScalarType::I64), + "isize" => scalar_type = Some(ScalarType::ISize), + "u8" => scalar_type = Some(ScalarType::U8), + "u16" => scalar_type = Some(ScalarType::U16), + "u32" => scalar_type = Some(ScalarType::U32), + "u64" => scalar_type = Some(ScalarType::U64), + "usize" => scalar_type = Some(ScalarType::USize), + _ => {} + } + } + _ => {} + }, + + Expr::Group(group) => match &*group.expr { + Expr::Lit(expr) => match &expr.lit { + Lit::Str(_) => num_strings += 1, + Lit::Int(expr) => { + num_scalars += 1; + + match expr.suffix() { + "i8" => scalar_type = Some(ScalarType::I8), + "i16" => scalar_type = Some(ScalarType::I16), + "i32" => scalar_type = Some(ScalarType::I32), + "i64" => scalar_type = Some(ScalarType::I64), + "isize" => scalar_type = Some(ScalarType::ISize), + "u8" => scalar_type = Some(ScalarType::U8), + "u16" => scalar_type = Some(ScalarType::U16), + "u32" => scalar_type = Some(ScalarType::U32), + "u64" => scalar_type = Some(ScalarType::U64), + "usize" => scalar_type = Some(ScalarType::USize), + _ => {} + } + } + _ => {} + }, + _ => {} + }, + + _ => {} + } + } + + if num_scalars == self.entries.len() { + EffectiveKeyKind::AllLiteralScalars(scalar_type) + } else if num_scalars > 0 && num_strings == 0 { + EffectiveKeyKind::LiteralAndExpressionScalars + } else if num_strings == self.entries.len() { + EffectiveKeyKind::AllLiteralStrings + } else if num_strings > 0 { + EffectiveKeyKind::LiteralAndExpressionStrings + } else { + match asserted_key_kind { + AssertedKeyKind::Scalar => EffectiveKeyKind::LiteralAndExpressionScalars, + AssertedKeyKind::String => EffectiveKeyKind::LiteralAndExpressionStrings, + AssertedKeyKind::Hashed => EffectiveKeyKind::Hashed, + AssertedKeyKind::Ordered => EffectiveKeyKind::Ordered, + } } } - fn handle_literal_scalar_keys(self) -> syn::Result + fn handle_literal_scalar_keys(self, suffix: &str) -> syn::Result where K: Scalar + Ord + FromStr, K::Err: Display, @@ -167,9 +251,19 @@ impl Generator { let hasher = PassthroughHasher::new(); for entry in &self.entries { - let k = parse2::(entry.key.to_token_stream())?.base10_parse::()?; + let lit = parse2::(entry.key.to_token_stream())?; + let k = lit.base10_parse::()?; + + let mut e = entry.clone(); + if suffix != "" { + e = Entry { + key: syn::parse_str::(&format!("{}{}", lit.to_string(), suffix))?, + value: e.value, + } + } + processed_entries.push(ProcessedEntry { - base: entry.clone(), + base: e, parsed_key: k, hash_code: hasher.hash(&k), }); @@ -283,7 +377,27 @@ impl Generator { } } - fn handle_non_literal_hashed_keys(mut self) -> syn::Result { + fn handle_non_literal_string_keys(mut self) -> syn::Result { + slow_dedup_by_keep_last(&mut self.entries, |x, y| x.key == y.key); + + let mut processed_entries = Vec::with_capacity(self.entries.len()); + + for entry in &self.entries { + processed_entries.push(ProcessedEntry { + base: entry.clone(), + parsed_key: (), + hash_code: 0, + }); + } + + if processed_entries.len() < 3 { + Ok(self.emit_inline_scan(&processed_entries)) + } else { + Ok(self.emit_hash_with_bridge(&processed_entries)) + } + } + + fn handle_hashed_keys(mut self) -> syn::Result { slow_dedup_by_keep_last(&mut self.entries, |x, y| x.key == y.key); let mut processed_entries = Vec::with_capacity(self.entries.len()); @@ -303,7 +417,7 @@ impl Generator { } } - fn handle_non_literal_ordered_keys(mut self) -> syn::Result { + fn handle_ordered_keys(mut self) -> syn::Result { slow_dedup_by_keep_last(&mut self.entries, |x, y| x.key == y.key); let mut processed_entries = Vec::with_capacity(self.entries.len()); diff --git a/frozen-collections-core/src/macros/map_macros.rs b/frozen-collections-core/src/macros/map_macros.rs index c0b0b8c..b00deaf 100644 --- a/frozen-collections-core/src/macros/map_macros.rs +++ b/frozen-collections-core/src/macros/map_macros.rs @@ -1,5 +1,5 @@ use crate::macros::generator; -use crate::macros::generator::KeyKind; +use crate::macros::generator::AssertedKeyKind; use crate::macros::parsing::long_form_map::LongFormMap; use crate::macros::parsing::map::Map; use crate::macros::parsing::short_form_map::ShortFormMap; @@ -14,7 +14,11 @@ use syn::parse2; /// /// Bad things happen to bad input pub fn fz_hash_map_macro(args: TokenStream) -> syn::Result { - fz_map_macro(args, pick_compile_time_random_seeds(), KeyKind::Hashed) + fz_map_macro( + args, + pick_compile_time_random_seeds(), + AssertedKeyKind::Hashed, + ) } /// Implementation logic for the `fz_ordered_map!` macro. @@ -23,7 +27,11 @@ pub fn fz_hash_map_macro(args: TokenStream) -> syn::Result { /// /// Bad things happen to bad input pub fn fz_ordered_map_macro(args: TokenStream) -> syn::Result { - fz_map_macro(args, pick_compile_time_random_seeds(), KeyKind::Ordered) + fz_map_macro( + args, + pick_compile_time_random_seeds(), + AssertedKeyKind::Ordered, + ) } /// Implementation logic for the `fz_string_map!` macro. @@ -32,7 +40,11 @@ pub fn fz_ordered_map_macro(args: TokenStream) -> syn::Result { /// /// Bad things happen to bad input pub fn fz_string_map_macro(args: TokenStream) -> syn::Result { - fz_map_macro(args, pick_compile_time_random_seeds(), KeyKind::String) + fz_map_macro( + args, + pick_compile_time_random_seeds(), + AssertedKeyKind::String, + ) } /// Implementation logic for the `fz_scalar_map!` macro. @@ -41,13 +53,17 @@ pub fn fz_string_map_macro(args: TokenStream) -> syn::Result { /// /// Bad things happen to bad input pub fn fz_scalar_map_macro(args: TokenStream) -> syn::Result { - fz_map_macro(args, pick_compile_time_random_seeds(), KeyKind::Scalar) + fz_map_macro( + args, + pick_compile_time_random_seeds(), + AssertedKeyKind::Scalar, + ) } fn fz_map_macro( args: TokenStream, seeds: (u64, u64, u64, u64), - key_kind: KeyKind, + key_kind: AssertedKeyKind, ) -> syn::Result { let input = parse2::(args)?; @@ -60,7 +76,7 @@ fn fz_map_macro( fn short_form_fz_map_macro( map: ShortFormMap, seeds: (u64, u64, u64, u64), - key_kind: KeyKind, + key_kind: AssertedKeyKind, ) -> syn::Result { Ok(generator::generate(map.payload, seeds, false, quote!(_), quote!(_), key_kind)?.ctor) } @@ -68,7 +84,7 @@ fn short_form_fz_map_macro( fn long_form_fz_map_macro( map: LongFormMap, seeds: (u64, u64, u64, u64), - key_kind: KeyKind, + key_kind: AssertedKeyKind, ) -> syn::Result { let key_type = map.key_type; let value_type = map.value_type; diff --git a/frozen-collections-core/src/macros/set_macros.rs b/frozen-collections-core/src/macros/set_macros.rs index b4ff38d..98148b7 100644 --- a/frozen-collections-core/src/macros/set_macros.rs +++ b/frozen-collections-core/src/macros/set_macros.rs @@ -1,5 +1,5 @@ use crate::macros::generator; -use crate::macros::generator::KeyKind; +use crate::macros::generator::AssertedKeyKind; use crate::macros::parsing::long_form_set::LongFormSet; use crate::macros::parsing::set::Set; use crate::macros::parsing::short_form_set::ShortFormSet; @@ -14,7 +14,11 @@ use syn::parse2; /// /// Bad things happen to bad input pub fn fz_hash_set_macro(args: TokenStream) -> syn::Result { - fz_set_macro(args, pick_compile_time_random_seeds(), KeyKind::Hashed) + fz_set_macro( + args, + pick_compile_time_random_seeds(), + AssertedKeyKind::Hashed, + ) } /// Implementation logic for the `fz_ordered_set!` macro. @@ -23,7 +27,11 @@ pub fn fz_hash_set_macro(args: TokenStream) -> syn::Result { /// /// Bad things happen to bad input pub fn fz_ordered_set_macro(args: TokenStream) -> syn::Result { - fz_set_macro(args, pick_compile_time_random_seeds(), KeyKind::Ordered) + fz_set_macro( + args, + pick_compile_time_random_seeds(), + AssertedKeyKind::Ordered, + ) } /// Implementation logic for the `fz_string_set!` macro. @@ -32,7 +40,11 @@ pub fn fz_ordered_set_macro(args: TokenStream) -> syn::Result { /// /// Bad things happen to bad input pub fn fz_string_set_macro(args: TokenStream) -> syn::Result { - fz_set_macro(args, pick_compile_time_random_seeds(), KeyKind::String) + fz_set_macro( + args, + pick_compile_time_random_seeds(), + AssertedKeyKind::String, + ) } /// Implementation logic for the `fz_scalar_set!` macro. @@ -41,13 +53,17 @@ pub fn fz_string_set_macro(args: TokenStream) -> syn::Result { /// /// Bad things happen to bad input pub fn fz_scalar_set_macro(args: TokenStream) -> syn::Result { - fz_set_macro(args, pick_compile_time_random_seeds(), KeyKind::Scalar) + fz_set_macro( + args, + pick_compile_time_random_seeds(), + AssertedKeyKind::Scalar, + ) } fn fz_set_macro( args: TokenStream, seeds: (u64, u64, u64, u64), - key_kind: KeyKind, + key_kind: AssertedKeyKind, ) -> syn::Result { let input = parse2::(args)?; @@ -60,7 +76,7 @@ fn fz_set_macro( fn short_form_fz_set_macro( set: ShortFormSet, seeds: (u64, u64, u64, u64), - key_kind: KeyKind, + key_kind: AssertedKeyKind, ) -> syn::Result { Ok(generator::generate(set.payload, seeds, true, quote!(_), quote!(_), key_kind)?.ctor) } @@ -68,7 +84,7 @@ fn short_form_fz_set_macro( fn long_form_fz_set_macro( set: LongFormSet, seeds: (u64, u64, u64, u64), - key_kind: KeyKind, + key_kind: AssertedKeyKind, ) -> syn::Result { let value_type = set.value_type; @@ -122,6 +138,14 @@ mod tests { assert_eq!("no collection entries supplied", r.unwrap_err().to_string()); } + #[test] + fn type_testing() { + let r = fz_scalar_set_macro(quote!({ 0i8 })); + + println!("{}", r.unwrap().to_string()); + } + + /* #[test] fn invalid_suffix() { let r = fz_scalar_set_macro(quote!( @@ -154,4 +178,5 @@ mod tests { r.unwrap_err().to_string() ); } + */ } diff --git a/frozen-collections/tests/hash_macro_tests.rs b/frozen-collections/tests/hash_macro_tests.rs index d426877..51a73c9 100644 --- a/frozen-collections/tests/hash_macro_tests.rs +++ b/frozen-collections/tests/hash_macro_tests.rs @@ -70,10 +70,10 @@ macro_rules! test_hash { )* }); - assert_eq!(s0, s1); - assert_eq!(s0, s2); + assert_eq!(s0, s1, "S0 vs s1"); + assert_eq!(s0, s2, "s0 vs s2"); // assert_eq!(s0, S3); - assert_eq!(s0, s4); + assert_eq!(s0, s4, "s0 vs s4"); } { @@ -134,10 +134,10 @@ macro_rules! test_hash { )* }); - assert_eq!(m0, m1); - assert_eq!(m0, m2); + assert_eq!(m0, m1, "m0 vs m1"); + assert_eq!(m0, m2, "m0 vs m2"); // assert_eq!(m0, M3); - assert_eq!(m0, m4); + assert_eq!(m0, m4, "m0 vs m4");; } } } diff --git a/frozen-collections/tests/scalar_macro_tests.rs b/frozen-collections/tests/scalar_macro_test.rs similarity index 96% rename from frozen-collections/tests/scalar_macro_tests.rs rename to frozen-collections/tests/scalar_macro_test.rs index fe4d462..6c8e04d 100644 --- a/frozen-collections/tests/scalar_macro_tests.rs +++ b/frozen-collections/tests/scalar_macro_test.rs @@ -136,6 +136,23 @@ macro_rules! test_scalar { } } +#[test] +fn foo() { + let s0 = ::frozen_collections::inline_sets::InlineDenseScalarLookupSet::<_, 1>::new( + ::frozen_collections::inline_maps::InlineDenseScalarLookupMap::<_, _, 1>::new_raw( + [(0i8, ())], + 128usize, + 128usize, + ), + ); + + fz_scalar_set!( let s4 : Bar < i8 > = { + 0i8 , + } ); + + assert!(s0.eq(&s4)); +} + #[test] fn scalar_i8() { test_scalar!(i8, 0i8);