Is this macro possible?


#1

I’d like to write a macro which would turn this:

do_something!(
    self,
    let one = "something",
    let two = "something",
    let three = "something",
)

into this:

let something_one = self.do_something();
let one = "something",
let something_two = self.do_something();
let two = "something",
//something_three is skipped
let three = "something",

self.do_something_with_one(one);
self.do_something_with_two(two);
self.do_something_with_three(three);

Is it even possible ? I figured out, that the matching would look like this:
($self:ident, $($cycle:stmt),+)

but I don’t know, how to do the expansion.


#2

What are you trying to do? What comes from the user and what doesn’t? You cannot concatenate idents in Rust macros, so if you want to combine one with do_something_with_, you cannot.


#3

Note that this will hopefully be stabilised as concat_idents! at some point. In the mean time, there’s mashup. However, I can’t quite get this example to work, whatever I try.

Also, a better match might be ($self:ident, $(let $thing:ident = $thang:expr),+)


#4

I’m trying to make a program that outputs a state machine, which is a part of my project (in Rust).

I guess this is better and a more complete example:

struct Generator {
    state: usize,
    state_machine: BTreeMap<(usize, usize), String>,
}

//This is in the source code
fn generate_php(&mut self, op: &Opcode) {
    add_instruction! (
        let cycle_0 = String::from("some metaprogramming"),
        let cycle_1 = String:from("some more metaprogramming"),
    )
}

//This would be the generated code
fn generate_php(&mut self, op: &Opcode) {
    let cycle_1_state = self.update_state();
    let cycle_0 = String::from("Some metaprogramming");
    let cycle_1 = String:from("some more metaprogramming");

    self.state_machine.insert((op.1, cycle_1_state), cycle_0);
    self.state_machine.insert((cycle_1_state, 0x100), cycle_1);
}

The inputs (cycle_0, cycle_1) describe, what happens at certains states in the state machine. I would like the macro to generate the state numbers (expect for the last cycle, which always goes to 0x100) and then add the states to the state machine.


#5

Sorry, my question was too vague :frowning: But I could use concat_idents! as I’m on nigthly anyway. Also, yep, that match works better.


#6

I don’t think this requires concatenating identifiers. Here is a runnable implementation that does my understanding of what you want.


use std::collections::BTreeMap;

struct Opcode((), usize);

#[derive(Debug)]
struct Generator {
    state: usize,
    state_machine: BTreeMap<(usize, usize), String>,
}

macro_rules! add_instruction {
    ($self:ident, $op:ident, $($cycle:expr,)+) => {
        private_add_instruction!($self, $op.1, $(String::from($cycle),)*);
    };
}

#[doc(hidden)]
macro_rules! private_add_instruction {
    ($self:ident, $prev:expr, $next:expr, $($rest:expr,)+) => {
        let state = $self.update_state();
        $self.state_machine.insert(($prev, state), $next);
        private_add_instruction!($self, state, $($rest,)*);
    };

    ($self:ident, $prev:expr, $last:expr,) => {
        $self.state_machine.insert(($prev, 0x100), $last);
    };
}

impl Generator {
    fn update_state(&mut self) -> usize {
        self.state += 1;
        self.state
    }

    fn generate_php(&mut self, op: &Opcode) {
        add_instruction! {
            self, op,
            "some metaprogramming",
            "some more metaprogramming",
        }
    }
}

fn main() {
    let mut generator = Generator {
        state: 0,
        state_machine: BTreeMap::new(),
    };
    generator.generate_php(&Opcode((), 0));
    println!("{:#?}", generator);
}

#7

There is a good reason why it wasn’t stabilized, in short, concat_idents! is pretty much useless.

Try using it for pretty much anything more complicated than concat_idents!(foo, bar) example provided in documentation, and you will see the issue. It’s pretty much usable only for accessing top scope items (note - accessing, not defining or whatever).

An identifier concatenation feature would be pretty cool to have, but concat_idents! isn’t the way to go. concat_idents! simply cannot work with the way macros work (macros return code without recursively evaluating other macros) and there is no way to fix it.

mashup on the other hand has none of issues concat_idents! has.