diff --git a/src/context.rs b/src/context.rs index 96a3bad..d51c54d 100644 --- a/src/context.rs +++ b/src/context.rs @@ -379,9 +379,10 @@ impl<'a> TypleContext<'a> { match expr { Expr::Array(array) => { self.replace_attrs(&mut array.attrs)?; - for expr in &mut array.elems { - self.replace_expr(expr, state)?; - } + array.elems = std::mem::take(&mut array.elems) + .into_iter() + .flat_map(move |expr| self.replace_expr_in_list(expr)) + .collect::>()?; } Expr::Assign(assign) => { self.replace_attrs(&mut assign.attrs)?; @@ -422,9 +423,10 @@ impl<'a> TypleContext<'a> { Expr::Call(call) => { self.replace_attrs(&mut call.attrs)?; self.replace_expr(&mut call.func, state)?; - for expr in &mut call.args { - self.replace_expr(expr, state)?; - } + call.args = std::mem::take(&mut call.args) + .into_iter() + .flat_map(move |expr| self.replace_expr_in_list(expr)) + .collect::>()?; } Expr::Cast(cast) => { self.replace_attrs(&mut cast.attrs)?; @@ -744,9 +746,10 @@ impl<'a> TypleContext<'a> { method_call.turbofish = None; } } - for arg in &mut method_call.args { - self.replace_expr(arg, state)?; - } + method_call.args = std::mem::take(&mut method_call.args) + .into_iter() + .flat_map(move |expr| self.replace_expr_in_list(expr)) + .collect::>()?; } Expr::Paren(paren) => { self.replace_attrs(&mut paren.attrs)?; @@ -880,9 +883,10 @@ impl<'a> TypleContext<'a> { } Expr::Tuple(tuple) => { self.replace_attrs(&mut tuple.attrs)?; - for expr in &mut tuple.elems { - self.replace_expr(expr, state)?; - } + tuple.elems = std::mem::take(&mut tuple.elems) + .into_iter() + .flat_map(move |expr| self.replace_expr_in_list(expr)) + .collect::>()?; } Expr::Unary(unary) => { self.replace_attrs(&mut unary.attrs)?; @@ -910,6 +914,108 @@ impl<'a> TypleContext<'a> { Ok(()) } + fn replace_expr_in_list(&'a self, mut expr: Expr) -> impl Iterator> + 'a { + let mut state = BlockState::default(); + match &mut expr { + Expr::Macro(syn::ExprMacro { mac, .. }) => { + if let Some(ident) = mac.path.get_ident() { + if ident == "typle_args" { + let token_stream = std::mem::take(&mut mac.tokens); + let default_span = token_stream.span(); + let mut tokens = token_stream.into_iter(); + let (pattern, range) = + match self.parse_pattern_range(&mut tokens, default_span) { + Ok(t) => t, + Err(e) => return ListIterator4::Variant0(std::iter::once(Err(e))), + }; + if range.is_empty() { + return ListIterator4::Variant1(std::iter::empty()); + } + let token_stream = tokens.collect::(); + let expr = match syn::parse2::(token_stream) { + Ok(expr) => expr, + Err(e) => return ListIterator4::Variant0(std::iter::once(Err(e))), + }; + let mut context = self.clone(); + if let Some(ident) = pattern.clone() { + context.constants.insert(ident, 0); + } + return ListIterator4::Variant2(range.zip_clone(expr).map({ + move |(index, mut expr)| { + if let Some(ident) = &pattern { + *context.constants.get_mut(ident).unwrap() = index; + } + match context.replace_expr(&mut expr, &mut state) { + Ok(()) => Ok(expr), + Err(e) => Err(e), + } + } + })); + } + } + } + Expr::Index(syn::ExprIndex { expr, index, .. }) => { + if let Expr::Array(array) = &mut **index { + // t[[..]] + let mut iter = array.elems.iter_mut().fuse(); + if let (Some(field), None) = (iter.next(), iter.next()) { + if let Err(e) = self.replace_expr(field, &mut state) { + return ListIterator4::Variant0(std::iter::once(Err(e))); + } + if let Some((start, end)) = evaluate_range(field) { + let start = match start { + Bound::Included(Err(span)) | Bound::Excluded(Err(span)) => { + return ListIterator4::Variant0(std::iter::once(Err( + Error::new(span, "expected integer for start of range"), + ))); + } + Bound::Included(Ok(start)) => start, + Bound::Excluded(Ok(start)) => start.saturating_add(1), + Bound::Unbounded => 0, + }; + let end = match end { + Bound::Included(Err(span)) | Bound::Excluded(Err(span)) => { + return ListIterator4::Variant0(std::iter::once(Err( + Error::new(span, "expected integer for end of range"), + ))); + } + Bound::Included(Ok(end)) => end.saturating_add(1), + Bound::Excluded(Ok(end)) => end, + Bound::Unbounded => match self.typle_len { + Some(end) => end, + None => { + return ListIterator4::Variant0(std::iter::once(Err( + Error::new(expr.span(), "need an explicit range end"), + ))); + } + }, + }; + return ListIterator4::Variant3({ + let span = index.span(); + (start..end).zip_clone(expr.clone()).map(move |(i, base)| { + Ok(Expr::Field(syn::ExprField { + attrs: Vec::new(), + base, + dot_token: token::Dot::default(), + member: Member::Unnamed(syn::Index { + index: i as u32, + span, + }), + })) + }) + }); + } + } + } + } + _ => {} + } + match self.replace_expr(&mut expr, &mut state) { + Ok(()) => ListIterator4::Variant0(std::iter::once(Ok(expr))), + Err(e) => ListIterator4::Variant0(std::iter::once(Err(e))), + } + } + fn replace_fields(&self, fields: &mut Fields) -> Result<()> { match fields { Fields::Named(syn::FieldsNamed { named: fields, .. }) @@ -2606,9 +2712,10 @@ impl<'a> TypleContext<'a> { self.replace_type(&mut slice.elem)?; } Type::Tuple(tuple) => { - for elem in &mut tuple.elems { - self.replace_type(elem)?; - } + tuple.elems = std::mem::take(&mut tuple.elems) + .into_iter() + .flat_map(|ty| self.replace_type_in_list(ty)) + .collect::>()?; } _ => {} } diff --git a/src/lib.rs b/src/lib.rs index 16d4b1a..69b1c9b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -521,8 +521,9 @@ impl TryFrom for TypleMacro { .ok_or_else(|| Error::new(range.span(), "range end must be bounded"))?; let max = match range.limits { syn::RangeLimits::HalfOpen(_) => evaluate_usize(end) - .and_then(|max| max.checked_sub(1)) - .ok_or_else(|| Error::new(end.span(), "range end invalid1"))?, + .ok_or_else(|| Error::new(end.span(), "range end invalid"))? + .checked_sub(1) + .ok_or_else(|| Error::new(end.span(), "range end invalid"))?, syn::RangeLimits::Closed(_) => { evaluate_usize(end).ok_or_else(|| Error::new(end.span(), "range end invalid"))? } @@ -612,6 +613,72 @@ pub fn typle_any(item: proc_macro::TokenStream) -> proc_macro::TokenStream { .into() } +/// Insert tuple components into a sequence. +/// +/// The `typle_args!` macro allows components of a tuple to be inserted into an existing sequence. +/// +/// For types, `T<{..}>` is a shorthand for `typle_args!(i in .. => T<{i}>)`. +/// For values, `t[[..]]` is a shorthand for `typle_args!(i in .. => t[[i]])`. +/// +/// ``` +/// # use typle::typle; +/// #[typle(Tuple for 0..12)] +/// fn append(t: T, a: A) -> (T<{..}>, A) { +/// (t[[..]], a) +/// } +/// +/// assert_eq!(append((1, 2, 3), 4), (1, 2, 3, 4)); +/// ``` +/// +/// The full `typle_args!` macro is required when modifying each component. +/// ``` +/// # use typle::typle; +/// #[typle(Tuple for 0..=12)] +/// fn coalesce_some( +/// s: S, +/// t: T +/// ) -> (typle_args!(i in .. => Option>), typle_args!(i in .. => Option>)) +/// where +/// T: Tuple, +/// { +/// (typle_args!(i in .. => Some(s[[i]])), typle_args!(i in .. => Some(t[[i]]))) +/// } +/// +/// assert_eq!( +/// coalesce_some((1, 2), (3, 4)), +/// (Some(1), Some(2), Some(3), Some(4)) +/// ); +/// ``` +/// +/// Note how this behaves differently to [`typle_for!`]: +/// ``` +/// # use typle::typle; +/// #[typle(Tuple for 0..=12)] +/// fn coalesce_some( +/// s: S, +/// t: T +/// ) -> (typle_for!(i in .. => Option>), typle_for!(i in .. => Option>)) +/// where +/// T: Tuple, +/// { +/// (typle_for!(i in .. => Some(s[[i]])), typle_for!(i in .. => Some(t[[i]]))) +/// } +/// +/// assert_eq!( +/// coalesce_some((1, 2), (3, 4)), +/// ((Some(1), Some(2)), (Some(3), Some(4))) +/// ); +/// ``` +#[proc_macro] +pub fn typle_args(item: proc_macro::TokenStream) -> proc_macro::TokenStream { + Error::new_spanned( + TokenStream::from(item), + "typle_args macro only available in item with typle attribute", + ) + .into_compile_error() + .into() +} + /// Reduce a tuple to a single value. /// /// The `typle_fold!` macro repeatedly applies an expression to an accumulator @@ -746,6 +813,8 @@ pub fn typle_fold(item: proc_macro::TokenStream) -> proc_macro::TokenStream { /// assert_eq!(append((1, 2, 3), 4), (1, 2, 3, 4)); /// ``` /// +/// Note: See [`typle_args!`] for a shorter way to write `append`. +/// /// `typle_for![...]` with brackets creates an array expression. /// /// ``` @@ -789,7 +858,6 @@ pub fn typle_fold(item: proc_macro::TokenStream) -> proc_macro::TokenStream { /// ((1, 3, 5), (2, 4, 6)) /// ); /// ``` -/// #[proc_macro] pub fn typle_for(item: proc_macro::TokenStream) -> proc_macro::TokenStream { Error::new_spanned( diff --git a/tests/compile/mod.expanded.rs b/tests/compile/mod.expanded.rs index 4277b2f..cc04ef5 100644 --- a/tests/compile/mod.expanded.rs +++ b/tests/compile/mod.expanded.rs @@ -2035,6 +2035,110 @@ pub mod type_alias { pub mod typle_args { #![allow(unused)] use typle::typle; + #[allow(non_camel_case_types)] + trait _typle_fn_append { + type Return; + fn apply(self) -> Self::Return; + } + impl _typle_fn_append for ((), A) { + type Return = (A,); + fn apply(self) -> Self::Return { + #[allow(unused_variables)] + let (t, a) = self; + { (a,) } + } + } + impl _typle_fn_append for ((T0,), A) { + type Return = (T0, A); + fn apply(self) -> Self::Return { + let (t, a) = self; + { (t.0, a) } + } + } + impl _typle_fn_append for ((T0, T1), A) { + type Return = (T0, T1, A); + fn apply(self) -> Self::Return { + let (t, a) = self; + { (t.0, t.1, a) } + } + } + impl _typle_fn_append for ((T0, T1, T2), A) { + type Return = (T0, T1, T2, A); + fn apply(self) -> Self::Return { + let (t, a) = self; + { (t.0, t.1, t.2, a) } + } + } + fn append(t: T, a: A) -> <(T, A) as _typle_fn_append>::Return + where + (T, A): _typle_fn_append, + { + <(T, A) as _typle_fn_append>::apply((t, a)) + } + #[allow(non_camel_case_types)] + trait _typle_fn_append_array { + type Return; + fn apply(self) -> Self::Return; + } + impl _typle_fn_append_array for ((bool,), bool) { + type Return = [bool; 1 + 1]; + fn apply(self) -> Self::Return { + let (t, a) = self; + { [t.0, a] } + } + } + impl _typle_fn_append_array for ((bool, bool), bool) { + type Return = [bool; 2 + 1]; + fn apply(self) -> Self::Return { + let (t, a) = self; + { [t.0, t.1, a] } + } + } + impl _typle_fn_append_array for ((bool, bool, bool), bool) { + type Return = [bool; 3 + 1]; + fn apply(self) -> Self::Return { + let (t, a) = self; + { [t.0, t.1, t.2, a] } + } + } + fn append_array(t: T, a: bool) -> <(T, bool) as _typle_fn_append_array>::Return + where + (T, bool): _typle_fn_append_array, + { + <(T, bool) as _typle_fn_append_array>::apply((t, a)) + } + #[allow(non_camel_case_types)] + trait _typle_fn_append_double { + type Return; + fn apply(self) -> Self::Return; + } + impl _typle_fn_append_double for ((u32,), u32) { + type Return = [u32; 1 + 1]; + fn apply(self) -> Self::Return { + let (t, a) = self; + { [2 * t.0, 2 * a] } + } + } + impl _typle_fn_append_double for ((u32, u32), u32) { + type Return = [u32; 2 + 1]; + fn apply(self) -> Self::Return { + let (t, a) = self; + { [2 * t.0, 2 * t.1, 2 * a] } + } + } + impl _typle_fn_append_double for ((u32, u32, u32), u32) { + type Return = [u32; 3 + 1]; + fn apply(self) -> Self::Return { + let (t, a) = self; + { [2 * t.0, 2 * t.1, 2 * t.2, 2 * a] } + } + } + fn append_double(t: T, a: u32) -> <(T, u32) as _typle_fn_append_double>::Return + where + (T, u32): _typle_fn_append_double, + { + <(T, u32) as _typle_fn_append_double>::apply((t, a)) + } struct World {} trait ExclusiveSystemParam {} struct ExclusiveSystemParamItem { @@ -2051,7 +2155,6 @@ pub mod typle_args { param_value: ExclusiveSystemParamItem, ) -> Self::Out; } - struct Func {} impl ExclusiveSystemParamFunction Out> for Func where diff --git a/tests/compile/typle_args.rs b/tests/compile/typle_args.rs index 331ed70..c3d2eea 100644 --- a/tests/compile/typle_args.rs +++ b/tests/compile/typle_args.rs @@ -1,6 +1,21 @@ #![allow(unused)] use typle::typle; +#[typle(Tuple for 0..=3)] +fn append(t: T, a: A) -> (T<{ .. }>, A) { + (t[[..]], a) +} + +#[typle(Tuple for 1..=3)] +fn append_array>(t: T, a: bool) -> [bool; T::LEN + 1] { + [t[[..]], a] +} + +#[typle(Tuple for 1..=3)] +fn append_double>(t: T, a: u32) -> [u32; T::LEN + 1] { + [typle_args!(i in .. => 2 * t[[i]]), 2 * a] +} + struct World {} trait ExclusiveSystemParam {} @@ -22,8 +37,6 @@ trait ExclusiveSystemParamFunction { ) -> Self::Out; } -struct Func {} - // https://github.com/jongiddy/bevy/blob/ac91b191/crates/bevy_ecs/src/system/exclusive_function_system.rs#L183 #[typle(Tuple for 0..=3)] impl ExclusiveSystemParamFunction) -> Out>