Is it possible to create a macro which counts the number of expanded items? - macros

Is it possible to create a macro which counts the number of expanded items?
macro_rules! count {
($($name:ident),*) => {
pub enum Count {
$(
$name = 1 << $i // $i is the current expansion index
),*
}
}
}
count!(A, B, C);

Here is a macro that counts the number of matched items:
macro_rules! count_items {
($name:ident) => { 1 };
($first:ident, $($rest:ident),*) => {
1 + count_items!($($rest),*)
}
}
fn main() {
const X: usize = count_items!(a);
const Y: usize = count_items!(a, b);
const Z: usize = count_items!(a, b, c);
assert_eq!(1, X);
assert_eq!(2, Y);
assert_eq!(3, Z);
}
Note that the counting is computed at compile time.
For your example, you can do it using accumulation:
macro_rules! count {
($first:ident, $($rest:ident),*) => (
count!($($rest),+ ; 0; $first = 0)
);
($cur:ident, $($rest:ident),* ; $last_index: expr ; $($var:ident = $index:expr)+) => (
count!($($rest),* ; $last_index + 1; $($var = $index)* $cur = $last_index + 1)
);
($cur:ident; $last_index:expr ; $($var:ident = $index:expr)+) => (
#[repr(C)]
enum Count {
$($var = 1 << $index),*,
$cur = 1 << ($last_index + 1),
}
);
}
pub fn main() {
count!(A, B, C, D);
assert_eq!(1, Count::A as usize);
assert_eq!(2, Count::B as usize);
assert_eq!(4, Count::C as usize);
assert_eq!(8, Count::D as usize);
}

Yes, if you pack it as array of idents:
macro_rules! count {
($($name:ident),*) => {
{
let counter = [$(stringify!($name),)*];
counter.len()
}
}
}
Count, names, reverse order of names are available. After, you can use it to construct something. For enum building you have to join it with something like this.

In this context, no. A macro could create an expression that counts the number of identifiers passed to it, but it would only be evaluated at runtime. I created this example in just a few minutes, but I realized it would not work for what you're doing.
Compiler plugins, however, are particularly suited to this sort of work. While they're not trivial to implement, I don't think it would be overly difficult to create one for this purpose. Maybe take a look, try your hand at it, and come back if you get stuck?

Since this question is general, posting an example of counting where arguments are separated by white-space (not commas).
Although in retrospect it seems obvious, it took me a while to figure out:
/// Separated by white-space.
#[macro_export]
macro_rules! count_args_space {
($name:ident) => { 1 };
($first:ident $($rest:ident) *) => {
1 + count_args_space!($($rest) *)
}
}
/// Separated by commas.
#[macro_export]
macro_rules! count_args_comma {
($name:ident) => { 1 };
($first:ident, $($rest:ident),*) => {
1 + count_args_comma!($($rest),*)
}
}
Second example is from #malbarbo, just posting to so you can see the 2x changes that were needed.

Related

Flutter, Dart Split sentence every one character to 2 characters size, for flutter custom tts project

Example:
var sentence = (hello there, I am Bob.)
var result = [
'he',
'el',
'll',
'lo',
' ',
'th',
'he',
'er',
're',
',',
' ',
'I',
' ',
'am',
' ',
'bo',
'ob',
'.']
I've found here working example, though it is in Javascript and I don't really know how to adopt it for Dart, and not sure how will behave once white space and punctuation is added in. Punctation and white space I need always split on its own not in combination with letters, I need them as well, as I will use them to add pauses in between words and sentences.
Thank you
var a = 12345678;
a= a.toString();
var arr=[];
for (var i =0; i<a.length-1; i++) {
arr.push(Number(a[i]+''+a[i+1]));
}
console.log(arr);
You could use regular expressions to split the sentence. For example:
void main() {
var exp = RegExp('([A-Za-z]{1,2}|[,!.?\s ])');
var str = "hello there, I am Bob.";
var matches = exp.allMatches(str);
for (var m in matches) {
print(m.group(0));
}
}
This looks for letters (A-Z or a-z) in groups of either 1 or 2, or single punctuation characters (,!.?) \s represents a white space.
Running the above would produce:
he
ll
o
th
er
e
,
I
am
Bo
b
.
Another approach
void main() {
var a = "1234!5678";
a = a.toString();
var arr = [];
for (var i = 0; i < a.length - 1; i++) {
if (a[i + 1] == '!') {
continue;
}
if (a[i] == '!') {
arr.add(a[i]);
continue;
}
arr.add(a[i] + '' + a[i + 1]);
}
print(arr);
}
I don't know dart much but I wrote this simple algorithm on dartpad and it works
If someone is having same issue, this is how I solved it
void main(String string) {
var test = "I Hello there I am Bob 23!";
List<String> nameArray = test.split('');
for (int curIndex = 0; curIndex < nameArray.length; curIndex++) {
if (curIndex >= 1 && nameArray[curIndex].contains(new RegExp(r'[a-zA-Z]')) && nameArray[curIndex-1].contains(new RegExp(r'[a-zA-Z]'))) {
print(nameArray[curIndex-1] + nameArray[curIndex]); // checks if current curIndex and previous curIndex are letters, if so returns previous and curent letters joined
} else {
if (curIndex >= 1 && nameArray[curIndex].contains(new RegExp(r'[a-zA-Z]')) && nameArray[curIndex+1].contains(new RegExp(r'[a-zA-Z]'))) {
null; // checks if curIndex and next curIndex are letters, if so returns null
}else{
print(nameArray[curIndex]);
}
}
}
}
Which returns
I
He
el
ll
lo
th
he
er
re
I
am
Bo
ob
2
3
!

CS50 pset 3: Tideman sort_pairs function

I need some assistance in understanding the logic behind this function. This is my current sort_pairs function in Tideman:
// Sort pairs in decreasing order by the strength of victory
void sort_pairs(void)
{
qsort(pairs, pair_count, sizeof(pair), compare);
return;
}
// Function for sort_pairs
int compare(const void *a, const void *b)
{
const pair *p1 = (const pair *) a;
const pair *p2 = (const pair *) b;
if (p1->winner < p2->winner)
{
return -1;
}
else if (p1->winner > p2->winner)
{
return 1;
}
else
{
return 0;
}
}
This does not clear check50 and I looked online to find how to approach this problem. It seems that most functions compare the values from the preferences array instead (eg preferences[pairs[i].winner][pairs[i].loser]) . My previous functions vote, record_preferences, and add_pairs all clear check50. I have not advanced beyond sort_pairs yet.
Why can't I compare the strength of victory directly from the pairs array instead since I already have the data stored there?
You don't need to make this so complex, you can use your own sorting here. Let's try a simple insertion sort-
void sort_pairs()
{
pair temp;
for (int i = 1, j; i < pair_count; i++)
{
temp = pairs[i];
j = i - 1;
for (; j >= 0 && preferences[pairs[j].winner][pairs[j].loser] < preferences[temp.winner][temp.loser]; j--)
{
pairs[j + 1] = pairs[j];
}
pairs[j + 1] = temp;
}
}
The pair struct looks like-
typedef struct
{
int winner;
int loser;
}
pair;
Explanation:-
We go through each pair of elements inside the pairs array - starting at 1 since I'm going to compare with the previous element (j = i - 1)
Now we check all the previous elements from the current element and compare them with the key - preferences[pairs[INDEX].winner][pairs[INDEX].loser]
This is the key you should be sorting by. preferences[WINNER_ID][LOSER_ID] means the amount of people that prefer WINNER_ID over LOSER_ID.
And that's pretty much it!, it's simply a insertion sort but the key is the important part.

Simplifying Rust macro rules for nested looping

I have a simple macro with three very similar rules:
macro_rules! c {
($exp:expr, for $i:ident in $iter:expr) => (
{
let mut r = vec![];
for $i in $iter {
r.push($exp);
}
r
}
);
($exp:expr, for $i:ident in $iter:expr, for $i2:ident in $iter2:expr) => (
{
let mut r = vec![];
for $i2 in $iter2 {
for $i in $iter {
r.push($exp);
}
}
r
}
);
($exp:expr, for $i:ident in $iter:expr, for $i2:ident in $iter2:expr, for $i3:ident in $iter3:expr) => (
{
let mut r = vec![];
for $i in $iter {
for $i2 in $iter2 {
for $i3 in $iter3 {
r.push($exp);
}
}
}
r
}
);
}
Each rule differs from the others by the number of for $i:ident in $iter:exp patterns being matched. The logic is similarly the same.
Is there a way to simplify these rules into one using repetition patterns such as $(...)* or $(...)+ and still be able to express the nested looping in the macro logic?
Playground link
You can use a recursive TT (token tree) munching macro:
macro_rules! c {
(#loop $v:ident, $exp:expr, for $i:ident in $iter:expr) => (
for $i in $iter {
$v.push($exp);
}
);
(#loop $v:ident, $exp:expr, for $i:ident in $iter:expr, $($tail:tt)*) => (
for $i in $iter {
c!(#loop $v, $exp, $($tail)*);
}
);
($exp:expr, $(for $i:ident in $iter:expr),*) => (
{
let mut r = vec![];
c!(#loop r, $exp, $(for $i in $iter),*);
r
}
);
}
The rules labelled with #loop do all the work.
A TT munching recursive macro is very similar to a recursive function. At every invocation, it processes (munches) only a portion of the input, generates intermediate output, and sends the remaining "unmunched" input tail to another macro invocation. Eventually, the input is small enough to not require any more macro invocations and reaches the base case at which the recursion is terminated.
Here, the recursive #loop rule captures a single token tree matching to for $i:ident in $iter:expr, and stores the remaining input (other such for $i in $iter expressions) in a $($tail:tt)*. The macro rule then generates the loop for the captured for $i in $iter expression and generates the loop body by invoking the same rule with the unmunched input ($($tail)*).
Eventually, $($tail)* contains only one token tree that can be matched to for $i:ident in $iter:expr. In that case, the base case #loop rule is called, generating the innermost loop which pushes the expression onto the Vec.
This macro should work for an arbitrary number of for $i in $iter expressions as long as it stays within the macro recursion limit. If you do find yourself up against the recursion limit, the number of recursive invocations can be reduced by processing two for $i:ident in $iter:expr expressions at once in the recursive #loop rule.
Rust Playground

Counting length of repetition in macro

I'm trying to implement a macro to allow MATLAB-esque matrix creation. I've got a basic working macro but I still have a long way to go.
I want to be able to enforce the right structure (same number of elements in each row) but I'm not sure how to do this within the macro. I think I want to enforce that each internal repetition has the same length - is this something I can do?
Here is my code so far:
pub struct Matrix<T> {
pub cols: usize,
pub rows: usize,
pub data: Vec<T>
}
macro_rules! mat {
( $($( $x:expr ),*);* ) => {
{
let mut vec = Vec::new();
let mut rows = 0;
$(
$(
vec.push($x);
)*
rows += 1;
)*
Matrix { cols : vec.len()/rows, rows: rows, data: vec}
}
};
}
It works but as you can see isn't very safe. It has no restrictions on the structure.
I want to do a lot more with this macro but I think this is a good start!
Update:
Here is some playground code for a crappy implementation I worked out. If anyone has any better suggestions please let me know! Otherwise I'll close this myself.
macro_rules! count {
() => (0usize);
( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));
}
macro_rules! mat {
( $( $x:expr ),* ) => { {
let vec = vec![$($x),*];
Matrix { cols : vec.len(), rows: 1, data: vec }
} };
( $( $x0:expr ),* ; $($( $x:expr ),*);* ) => { {
let mut _assert_width0 = [(); count!($($x0)*)];
let mut vec = Vec::new();
let rows = 1usize;
let cols = count!($($x0)*);
$( vec.push($x0); )*
$(
let rows = rows + 1usize;
let _assert_width = [(); count!($($x)*)];
_assert_width0 = _assert_width;
$( vec.push($x); )*
)*
Matrix { cols : cols, rows: rows, data: vec }
} }
}
playground
The count! macro expands to a constant expression that represents the number of arguments it got as input. It's just a helper for the mat! macro. If you need to count a lot of items and the compiler can't cope with it, see the Counting chapter in The Little Book of Rust Macros, which has more complex macros for counting.
My version of the macro uses dummy variables and assignments to verify that the width of all rows are the same. First off, I changed the macro's pattern to handle the first row separately from the subsequent rows. The first variable, _assert_width0, is initialized with an array of units ((), which makes the array take no memory), with the size of the array being the number of items in the first row. Then, _assert_width is also initialized with an array of units, with the size of the array being the number of items in each subsequent row. Then, _assert_width is assigned to _assert_width0. The magic here is that this line will raise a compiler error if the width of a row doesn't match the width of the first row, since the types of the array won't match (you might have e.g. [(); 3] and [(); 4]). The error isn't super clear if you don't know what's going on in the macro, though:
<anon>:38:24: 38:37 error: mismatched types:
expected `[(); 3]`,
found `[(); 4]`
(expected an array with a fixed size of 3 elements,
found one with 4 elements) [E0308]
<anon>:38 _assert_width0 = _assert_width;
^~~~~~~~~~~~~
<anon>:47:13: 47:44 note: in this expansion of mat! (defined in <anon>)
<anon>:38:24: 38:37 help: see the detailed explanation for E0308
First, to quickly address the title of your question: see the Counting chapter in The Little Book of Rust Macros. To summarise: there is no direct way, you need to write a macro that expands to something you can count in regular code.
Now, to address your actual question: hoo boy.
It's not so much counting that you want, it's to fail at compile time if the sub-sequences have different lengths.
First of all, there's no clean way to trigger a compilation failure from a macro. You can trigger some other pre-existing error, but you can't control the actual error message.
Secondly, there's no easy way to do "variable" comparisons in macros at all. You can sometimes compare against a fixed token sequence, but you're not doing that here.
So it's doubly not-really-doable.
The simplest thing to do is check the lengths during construction at runtime, and return an error or panic if they don't match.
Is it actually impossible? I don't believe so. If you're willing to accept inscrutable error messages and a massive jump in complexity, you can check for length equality between two token sequences like so:
macro_rules! tts_equal_len {
(($_lhs:tt $($lhs_tail:tt)*), ($_rhs:tt $($rhs_tail:tt)*)) => {
tts_equal_len!(($($lhs_tail)*), ($($rhs_tail)*))
};
(($($_lhs_tail:tt)+), ()) => { do_something_bad!() };
((), ($($_rhs_tail:tt)+)) => { do_something_bad!() };
((), ()) => { do_something_good!() };
}
macro_rules! do_something_bad { () => { { println!("kaboom!") } } }
macro_rules! do_something_good { () => { { println!("no kaboom!") } } }
fn main() {
tts_equal_len!((,,,), (,,,));
tts_equal_len!((,,,), (,,));
tts_equal_len!((,), (,,));
}
Again, the real problem is finding some way to fail at compile time such that the user will understand why compilation failed.
Update: there's a new way of doing things
As of the day on which this was written, the feature of rust which enables the following (count) to be done, in still unstable and is available in nightly builds.
You can check out the github issues and test cases for further understanding of what's given below
To enable this feature, you need to add the line #![feature(macro_metavar_expr)] to the top of the crate's root module (usually main.rs or lib.rs), and also set your repo to use nightly builds, which is easily done by creating a file rust-toolchain.toml in the root directory (alongside Cargo.toml) and add the folllowing lines to it:
[toolchain]
channel = "nightly"
Now, instead of providing a solution to you specific problem, I'd like to share a generic solution I created to better illustrate most situations.
I highly recommend studying the code AND the comments, by pasting the following two code blocks in a file (main.rs).
The macro_rules
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
struct SumLen {
sum: i32,
len: u32
}
/// currently one `i32` type is available
///
/// # Examples
///
/// The output of the following:
/// ```ignore
/// sumnarr!(i32 => 5 ; 6, 7, 8)
/// ```
/// will be `[(5, 1), (21, 3)]`
macro_rules! sumnarr {
( $type:ty => $( $( $x: expr ),* );* ) => {
{
// `${count(x,0)}` will give you "length" (number of iterations)
// in `$( )*` loop that you are IMMEDIATELY OUTSIDE OF (e.g.: the `$( )*` loop below)
// `${count(x,1)}` will give you TOTAL number of iterations that the `$( )*` loop
// INSIDE of the IMMEDIATE `$( )*` loop will make. i.e. it is similar to converting
// [ [i1,i2], [i1,i2,i3] ] TO [ i1,i2,i3,i4,i5 ] i.e. flatten the nested iteration.
// In case of `[ [i1,i2], [i1,i2,i3] ]`, `${count(x,0)}` is 2 and `${count(x,1)}` is 5
let mut arr: [SumLen; ${count(x,0)}] = [SumLen{ sum:0, len:0}; ${count(x,0)}];
$(
// `${index()}` refers to the iteration number within the `$( )*` loop
arr[${index()}] = {
let mut sum = 0;
//let mut len = 0;
// THe following will give us length is the loop it is IMMEDIATELY OUTSIDE OF
// (the one just below)
let len = ${count(x,0)};
$(
sum += $x;
// If you were NOT using `$x` somewhere else inside `$( )*`,
// then you should use `${ignore(x)};` to inform the compiler
//You could use the below method, where `${length()}` will give you
//"length" or "number of iterations" in current loop that you are in
// OR
// you could go with my method of `${count(x,0)}` which is explained above
//len = ${length()};
)*
SumLen {
sum,
len
}
};
)*
arr
}
};
}
The #[test] (unit test)
#[test]
fn sumnarr_macro() {
let (a, b, c, d, e) = (4, 5, 6, 9, 10);
let sum_lens = [
SumLen {
sum: a + e,
len: 2
},
SumLen {
sum: b + c + d,
len: 3
}
];
assert_eq!(sum_lens, sumnarr!(i32 => a,e;b,c,d));
}
I hope this helps

SweetJS : Write a macro for a specific library

I'm currently working on a little project which consists about writing macros for Ramda. Here is an example :
let map = macro {
rule { $f $array } => { R.map($f, $array) }
rule { $f } => { R.map($f) }
}
I tried to compile this simple sample of code as a beginning :
var R = require('ramda');
function log (value) {
console.log(value);
}
map log [1, 2, 3];
Because of hygiene, the compiled code looks like this :
var R$759 = require('ramda');
function log$761(value$762) {
console.log(value$762);
}
R.map(log$761)[1, 2, 3];
My problem is that I don't know how to make reference to ramda.
Has anyone tried to write macros for a specific library and encountered this problem ?
At the moment the ways to do it are a little hacky. In the next release when we get ES6 modules this will actually be taken care of automatically for you but until then the best option is to have an initialization macro (this is what ki and contracts.js do). This works by having a shared variable in scope for all of your macros and then having the user first invoke an import macro that does the necessary require:
var r_lib;
macro import {
rule { $name from $path } => {
r_lib = require($path);
}
}
let map = macro {
rule { $f $l } => { r_lib.map($f, $l) }
}
import R from "ramda"
map log [1,2,3]