Skip to content

Commit

Permalink
Support typle_args! in array, tuple, call arg values
Browse files Browse the repository at this point in the history
  • Loading branch information
jongiddy committed May 19, 2024
1 parent 18eaa49 commit f6619ad
Show file tree
Hide file tree
Showing 4 changed files with 312 additions and 21 deletions.
137 changes: 122 additions & 15 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Result<_>>()?;
}
Expr::Assign(assign) => {
self.replace_attrs(&mut assign.attrs)?;
Expand Down Expand Up @@ -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::<Result<_>>()?;
}
Expr::Cast(cast) => {
self.replace_attrs(&mut cast.attrs)?;
Expand Down Expand Up @@ -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::<Result<_>>()?;
}
Expr::Paren(paren) => {
self.replace_attrs(&mut paren.attrs)?;
Expand Down Expand Up @@ -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::<Result<_>>()?;
}
Expr::Unary(unary) => {
self.replace_attrs(&mut unary.attrs)?;
Expand Down Expand Up @@ -910,6 +914,108 @@ impl<'a> TypleContext<'a> {
Ok(())
}

fn replace_expr_in_list(&'a self, mut expr: Expr) -> impl Iterator<Item = Result<Expr>> + '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::<TokenStream>();
let expr = match syn::parse2::<Expr>(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, .. })
Expand Down Expand Up @@ -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::<Result<_>>()?;
}
_ => {}
}
Expand Down
74 changes: 71 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -521,8 +521,9 @@ impl TryFrom<TokenStream> 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"))?
}
Expand Down Expand Up @@ -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: Tuple, A>(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: Tuple, T: Tuple>(
/// s: S,
/// t: T
/// ) -> (typle_args!(i in .. => Option<S<{i}>>), typle_args!(i in .. => Option<T<{i}>>))
/// 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: Tuple, T: Tuple>(
/// s: S,
/// t: T
/// ) -> (typle_for!(i in .. => Option<S<{i}>>), typle_for!(i in .. => Option<T<{i}>>))
/// 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
Expand Down Expand Up @@ -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.
///
/// ```
Expand Down Expand Up @@ -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(
Expand Down
105 changes: 104 additions & 1 deletion tests/compile/mod.expanded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<A> _typle_fn_append for ((), A) {
type Return = (A,);
fn apply(self) -> Self::Return {
#[allow(unused_variables)]
let (t, a) = self;
{ (a,) }
}
}
impl<T0, A> _typle_fn_append for ((T0,), A) {
type Return = (T0, A);
fn apply(self) -> Self::Return {
let (t, a) = self;
{ (t.0, a) }
}
}
impl<T0, T1, A> _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<T0, T1, T2, A> _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, A>(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: 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: 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<F> {
Expand All @@ -2051,7 +2155,6 @@ pub mod typle_args {
param_value: ExclusiveSystemParamItem<Self::Param>,
) -> Self::Out;
}
struct Func {}
impl<Out, Func: Send + Sync + 'static> ExclusiveSystemParamFunction<fn() -> Out>
for Func
where
Expand Down
Loading

0 comments on commit f6619ad

Please sign in to comment.