Macro to transform non-tuple arguments to tuples - macros

Is it possible to create a macro to transform non-tuple arguments to tuples? I would like something like this:
assert_eq!(tuplify!(1, (2, 3), 4), ((1,), (2, 3), (4,)));
I tried to create such macro but was unable to do so. The problem that I faced was that each argument can have one of two forms and I couldn't figure out how to specify it.

If each argument to the macro is a single token tree, then this will do:
macro_rules! tuplify {
(#inner ($($args:expr),*)) => (($($args,)*));
(#inner $arg:expr) => (($arg,));
($($arg:tt),*) => (($(tuplify!(#inner $arg),)*));
}
What if arguments could have more than one token tree? For example:
assert_eq!(tuplify!(1 + 6, (2, 3), 4), ((7,), (2, 3), (4,)));
Then we just need to accept a sequence of token trees, right?
macro_rules! tuplify {
(#inner ($($args:expr),*)) => (($($args,)*));
(#inner $arg:expr) => (($arg,));
($($($arg_tt:tt)+),*) => (($(tuplify!(#inner $($arg)+),)*));
}
Nah, that would be too easy:
<anon>:12:30: 12:31 error: local ambiguity: multiple parsing options: built-in NTs tt ('arg_tt') or 1 other option.
<anon>:12 assert_eq!(tuplify!(1 + 6, (2, 3), 4), ((7,), (2, 3), (4,)));
It's ambiguous because , can also be parsed as a token tree.
In order to tackle this, I think we need to write a "TT muncher".
macro_rules! tuplify {
(#as_expr $e:expr) => { $e };
// No more tokens
(#parse { } -> { $($current:tt)* } -> { $($output:tt)* }) => {
tuplify!(#as_expr ( $($output)* ($($current)*,), ))
};
// Comma
(#parse { , $($ts:tt)* } -> { $($current:tt)* } -> { $($output:tt)* }) => {
tuplify!(#parse { $($ts)* } -> { } -> { $($output)* ($($current)*,), })
};
// Tuple followed by a comma, nothing in the current argument yet
(#parse { ($($tuple_item:expr),*) , $($ts:tt)* } -> { } -> { $($output:tt)* }) => {
tuplify!(#parse { $($ts)* } -> { } -> { $($output)* ($($tuple_item,)*), })
};
// Tuple followed by nothing else, nothing in the current argument yet
(#parse { ($($tuple_item:expr),*) } -> { } -> { $($output:tt)* }) => {
tuplify!(#parse { } -> { } -> { $($output)* ($($tuple_item,)*), })
};
// Base case
(#parse { $t:tt $($ts:tt)* } -> { $($current:tt)* } -> { $($output:tt)* }) => {
tuplify!(#parse { $($ts)* } -> { $t $($current)* } -> { $($output)* })
};
// Entry point
($($tokens:tt)*) => (tuplify!(#parse { $($tokens)* } -> { } -> { }));
}
fn main() {
assert_eq!(tuplify!(1 + 6, (2, 3), 4), ((7,), (2, 3), (4,)));
}

Related

How to sort a list twice in Flutter?

I want to sort the list twice.
Example list (here is an example list with models):
[Model("A", 5), Model("C", 3), Model("B", 7) Model("F", 5), Model("D", 5)]
What I want is to sort alphabetically and then numerically. If I do this, I get the following result:
[Model("C", 3), Model("A", 5), Model("D", 5) Model("F", 5), Model("B", 7)]
Else:
Sort alphabetically:
[Model("A", 5), Model("B", 7), Model("C", 3) Model("D", 5), Model("F", 5)]
Sort numerically:
[Model("C", 3), Model("A", 5), Model("F", 5) Model("D", 5), Model("B", 7)]
My code:
Stream<List<T>> function<T>({
…
int Function(T lhs, T rhs) sort,
}) {
…
List<T> result = …;
result.sort(sort);
}
Stream<List<Model>> stream() {
return function(
…
sort: (a, b) => a.alphabet.toLowerCase().compareTo(b.alphabetc.toLowerCase())
);
}
Feel free to leave a comment if you need more information.
How to sort a list twice? I would appreciate any help. Thank you in advance!
Try the following code, first, implement the Comparable class on you Model class, and implement the compareTo method like this:
class Model implements Comparable {
final String char;
final int number;
Model(this.char, this.number);
#override
toString() {
return "Model('$char', '$number')";
}
#override
int compareTo(other) {
if (other is Model) {
if (number == other.number) {
return other.char.compareTo(char);
} else {
return other.number.compareTo(number);
}
} else {
return 0;
}
}
}
Then, taking this example
List<Model> a = [
Model("A", 5),
Model("C", 3),
Model("B", 7),
Model("F", 5),
Model("D", 5),
];
Trying to sort it will get you to your desired result:
a.sort((a, b) => b.compareTo(a) );
print(a); // [Model('C', '3'), Model('A', '5'), Model('D', '5'), Model('F', '5'), Model('B', '7')]
You can call it twice after each other like this:
modelList.sort(
(a, b) => a.alphabet.compareTo(b.alphabet),
);
modelList.sort(
(a, b) => a.number.compareTo(b.number),
);
in your case:
Stream<List<T>> function<T>({
…
int Function(T lhs, T rhs) sort1,
int Function(T lhs, T rhs) sort2,
}) {
…
List<T> result = …;
result.sort(sort1);
result.sort(sort2);
}
result :
for (var element in modelList) {
print("element = ${element.alphabet}"); //C, A, D, F, B
}

How to convert Observable<string>[] to Observable<string[]>

I’ve got an Observable that has an array of objects and I would like to convert them to a different object using a second observable. This is part of a larger project so to simplify this for my question I am going to have an observable that has an array of number and I would like to convert them to a string. I started with the following.
const response$ = of({results: [1, 2, 3]});
response$.pipe(
map((response) => {
return response.results.map((id) => {
return id.toString();
})
})
)
.subscribe((response: string[]) => {
console.log(response);
})
The response in the subscribe will be an array of string as expected. Now I need to use a second observable to convert the number to string (again just to make the question simpler). So I replaced return id.toString() with return of(id.toString()) to simulate making a second call to an observable.
const response$ = of({results: [1, 2, 3]});
response$.pipe(
map((response) => {
return response.results.map((id) => {
return of(id.toString());
})
}),
)
.subscribe((response: Observable<string>[]) => {
})
Now the signature of the response is Observable<string>[] but I need the response to be string[] so I started reading about other RxJS operators and I ended up with the following.
const response$ = of({results: [1, 2, 3]});
response$.pipe(
concatMap((response) => {
return response.results.map((id) => {
return of(id.toString());
})
}),
concatAll()
)
.subscribe((response: string) => {
console.log('bar', response);
})
I used concatMap() and concatAll() because I need the call to the second observable to happen in sequence. The problem now is that my response is a string and I get three calls to the subscriber “1” “2” “3”. I need one response of string[]. Can someone explain how to take an Observable<string>[] and convert it to Observable<string[]> in my example?
I think what you're looking for is this:
const response$ = of({results: [1, 2, 3]});
response$.pipe(
switchMap((response) => {
// map array to observables and execute all with forkJoin
return forkJoin(...response.results.map((id) => {
return of(id.toString());
}))
})
)
.subscribe((response: string) => {
console.log('bar', response);
})
however, this is going to execute in parallel. if you need sequential execution on the inner, you can use concat and reduce
const response$ = of({results: [1, 2, 3]});
response$.pipe(
switchMap((response) => {
// map array to observables and execute all with concat and collect results with reduce
return concat(...response.results.map((id) => {
return of(id.toString());
})).pipe(reduce((acc, v) => acc.concat([v]), []))
})
)
.subscribe((response: string) => {
console.log('bar', response);
})
only thing to be careful of is to make sure response.results has items in it. may need a length check like:
if (!response.results.length)
return of([])

How to match Rust's `if` expressions in a macro?

I'm trying to write a macro that will rewrite certain Rust control flow, but I'm having difficulty matching an if expression. The problem is that the predicate is an expression, but an expr is not permitted to be followed by a block or {.
The best I've got is to use tt:
macro_rules! branch {
(
if $pred:tt
$r1:block
else
$r2:block
) => {
if $pred {
$r1
} else {
$r2
}
};
}
Which works fine with single-token or grouped predicates:
branch! {
if (foo == bar) {
1
} else {
2
}
}
But fails if the predicate was not grouped:
branch! {
if foo == bar {
1
} else {
2
}
}
error: no rules expected the token `==`
I also tried to use a repeating pattern of tt in the predicate:
macro_rules! branch {
(
if $($pred:tt)+
$r1:block
else
$r2:block
) => {
if $($pred)+ {
$r1
} else {
$r2
}
};
}
But this produces an error because it's now ambiguous whether subsequent block should match the tt too:
error: local ambiguity: multiple parsing options: built-in NTs tt ('pred') or block ('r1').
Is there a way to do this, or am I stuck with inventing special syntax to use in the macro?
You could use a TT muncher to parse the predicate:
macro_rules! branch {
{
if $($rest:tt)*
} => {
branch_parser! {
predicate = ()
rest = ($($rest)*)
}
};
}
macro_rules! branch_parser {
{
predicate = ($($predicate:tt)*)
rest = ({ $($then:tt)* } else { $($else:tt)* })
} => {
println!("predicate: {}", stringify!($($predicate)*));
println!("then: {}", stringify!($($then)*));
println!("else: {}", stringify!($($else)*));
};
{
predicate = ($($predicate:tt)*)
rest = ($next:tt $($rest:tt)*)
} => {
branch_parser! {
predicate = ($($predicate)* $next)
rest = ($($rest)*)
}
};
}
fn main() {
branch! {
if foo == bar {
1
} else {
2
}
}
}
Output:
predicate: foo == bar
then: 1
else: 2

How to avoid repetition expanding indices with macros in Rust?

Is there a way to write this macro that expands array access in such a way that larger arrays can be written in a less verbose way?
/// Avoid manually expanding an expression, eg:
///
/// let array = unpack!([some.vec; 3]);
///
/// Expands into: [some.vec[0], some.vec[1], some.vec[2]]
///
/// Supports expanding into different bracket types based on the input args.
macro_rules! unpack {
([$v_:expr; 2]) => { { let v = $v_; [v[0], v[1]] } };
(($v_:expr; 2)) => { { let v = $v_; (v[0], v[1]) } };
({$v_:expr; 2}) => { { let v = $v_; {v[0], v[1]} } };
([$v_:expr; 3]) => { { let v = $v_; [v[0], v[1], v[2]] } };
(($v_:expr; 3)) => { { let v = $v_; (v[0], v[1], v[2]) } };
({$v_:expr; 3}) => { { let v = $v_; {v[0], v[1], v[2]} } };
([$v_:expr; 4]) => { { let v = $v_; [v[0], v[1], v[2], v[3]] } };
(($v_:expr; 4)) => { { let v = $v_; (v[0], v[1], v[2], v[3]) } };
({$v_:expr; 4}) => { { let v = $v_; {v[0], v[1], v[2], v[3]} } };
}
To reduce verbosity you can construct recursive macro.
macro_rules! unpack {
({$vec:expr; $count:expr}) => {
unpack!([$vec; $count])
};
(($vec:expr; $count:expr)) => {
unpack!([$vec; $count])
};
([$vec:expr; $count:expr]) => {
$vec[0..$count]
};
}
fn main() {
let vec = vec![1, 2, 3, 4, 5];
assert_eq!([1, 2], unpack!({vec; 2}));
assert_eq!([1, 2, 3], unpack!((vec; 3)));
assert_eq!([1, 2, 3, 4], unpack!([vec; 4]));
}
Every macro can be called with (), [] and {} brackets, so if you don't need additional pair of brackets your macro can be as simple as that:
macro_rules! unpack {
($vec:expr; $count:expr) => {
$vec[0..$count]
};
}
fn main() {
let vec = vec![1, 2, 3, 4, 5];
assert_eq!([1, 2], unpack!{vec; 2});
assert_eq!([1, 2, 3], unpack!(vec; 3));
assert_eq!([1, 2, 3, 4], unpack![vec; 4]);
}
Example from Rust Book.

How to choose between macros at compile time?

I have different versions of the same macro and I want to be able to choose one of them at compile time.
Here is the code I have:
macro_rules! macro_a {
($identifier:ident) => {
println!("A: {}", stringify!($identifier));
}
}
macro_rules! macro_b {
($identifier:ident) => {
println!("B: {}", stringify!($identifier));
}
}
macro_rules! macro_c {
($identifier:ident) => {
println!("C: {}", stringify!($identifier));
}
}
macro_rules! choose_macro {
(a) => {
const CHOSEN_MACRO: u32 = 1;
};
(b) => {
const CHOSEN_MACRO: u32 = 2;
};
(c) => {
const CHOSEN_MACRO: u32 = 3;
};
}
choose_macro!(c);
macro_rules! use_macro {
($identifier:ident) => {
match CHOSEN_MACRO {
1 => macro_a!($identifier),
2 => macro_b!($identifier),
3 => macro_c!($identifier),
_ => unreachable!(),
}
}
}
fn main() {
use_macro!(test);
}
This will print, as expected:
C: test
I wonder if there is a better way to doing this (with macro or attribute or anything else).
It is not clear if the macro is chosen at compile time here. Will Rust remove the match because it is on a constant?
Update: I prefer to choose the macro in the code, not using compiler flags. Also, I do not want to hide the macros that are not chosen: I want to be able to use them using their real name.
I would recommend using conditional compilation flags for something like this. See https://doc.rust-lang.org/book/conditional-compilation.html
In your case, it might look something like this:
#[cfg(feature = "feature_a")]
macro_rules! use_macro {
($identifier:ident) => {
println!("A: {}", stringify!($identifier));
}
}
#[cfg(feature = "feature_b")]
macro_rules! use_macro {
($identifier:ident) => {
println!("B: {}", stringify!($identifier));
}
}
#[cfg(feature = "feature_c")]
macro_rules! use_macro {
($identifier:ident) => {
println!("C: {}", stringify!($identifier));
}
}
fn main() {
use_macro!(test);
}
Then add the following to your Cargo.toml file:
[features]
feature_a = []
feature_b = []
feature_c = []
If you want it to print out "C: test" for example, then run the following:
cargo run --features feature_c