Get the last expr passed into a declarative macro

Hi, I was writing a macro for instantiating a linked list where;

  • Each list item is assigned a number
  • Each list item is also passed the number of the final element in the first

The types for this linked list look like this;

trait Item<const END: usize> {}

struct ListItem<const NUM: usize, const END: usize, T: Item<END>>(T);
impl <const NUM: usize, const END: usize, T: Item<END>> Item<END> for ListItem<NUM, END, T> {}

struct EndItem<const END: usize>();
impl <const END: usize> Item<END> for EndItem<END> {}

The set of macros I have written for them look like this:

macro_rules! instantiate {
    ($a:expr, $b:expr) => {
        (EndItem())
    };
    ($a:expr, $($b:tt)*) => {
        ListItem(instantiate!($($b)*))
    }
}

macro_rules! make_type {
    ($num:expr) => {
        EndItem::<$num>
    };
    (end $end:expr, $num:expr) => {
        ListItem::<$num, $end, EndItem::<$end>>
    };
    (end $end:expr, $num:expr, $($c:tt)*) => {
        ListItem::<$num, $end, make_type!(end $end, $($c)*)>
    };
}

macro_rules! list {
    (end $end:expr) => {
        make_type!($end)()
    };
    (end $end:expr, $num:expr) => {
        make_type!(end $end, $num)(instantiate!($num, $end))
    };
  (end $end:expr, $num:expr, $($c:tt)*) => {
      make_type!(end $end, $num, $($c)*)(instantiate!($num, $($c)*, $end))
  };
}

I have checked and this is working such that list!(end 4, 0, 1, 2, 3) generates and instantiates a list of the correct type, but currently I'm having to put the value assigned to the final element first just so it's in a fixed place since after trying a few methods I couldn't seem to find a pattern where I can access the final expression passed into the macro.

I just wondered if it would be possible to write something like

($num:expr, $($c:tt)*; $end:expr) => {
    make_type!(end $end, $num, $($c)*)(instantiate!($num, $($c)*, $end))
};

So that way the macro can be called more intuitively with the final item's value coming in the last place (I.E. list!(0, 1, 2, 3, 4) rather than the current list!(end 4, 0, 1, 2, 3)) but the macros is still able to pass the value to the preceding items.

perhaps this helper macro can be of use. it should also be possible to make a macro that expands to everything except its last argument with a similar recursive technique. the combination of the two shows allows you to move the last argument to the start, allowing your current logic to work.

note that if you want a recursive macro to work in other crates, it should be exported and use $crate (doesn't work on the playground for some reason)

1 Like

Does this fit your needs?

1 Like

That works! Thank you.

That's working! Thank you, I think I did try something before but I think I may have done something wrong with how I called the helper macro, but @jendrikw pulled it off below.