Which doesn't work. I think $a:pat is wrong but I don't know what the type should be. Can someone point me in the right direction if this is even possible? Thank you!
If the length of the instructions is "variadic" in tokens (which it probably is), then in order to keep your current syntax you'd have to mix parsing instructions with recursing, which will make the instructions part a bit less readable. I thus recommend that you implement your macro in two passes:
Split all the instructions into handling individual instructions, thanks to a global separator (e.g., trailing ; for each instruction, or have the instructions be […] wrapped);
Have another macro to handle each individual instruction.
[add 5, 5, 5 ] syntax
Now, for macro reasons, using […] as the syntactical separator in between instructions will be the easiest to implement (no need to munch/recurse), so let's start with that approach to get the gist:
macro_rules! assemble_mips {(
$(
// a single instruction, with opaque contents
[ $($instruction:tt)* ]
)*
) => ({
let mut v = ::std::vec![];
macro_rules! __assembled_bytes {() => ( v )} // circumvents hygiene
$(
$crate::macros::__assemble_single_mips_instruction!( $($instruction)* );
)*
v
})}
pub(crate) use assemble_mips;
This one will be a bit more annoying, since we'll need to use recursive :tt-munching to locate the ; and split there. The previous macro will be handy, and will thus remain available, named $crate::macros::__assemble_mips_bracketed!, within the following section:
Okay thats an interesting way to do it, thanks for the help! Glad it at least wasn't something simple and obvious (unless this solution is to people here!)
But despite it being simpler to write, it has a lot of that $($rest:tt)* -> recurse!($($rest)*); repetition (it will occur once per instruction rule!) which, given the expected big number of supported instructions, would not pay off in practice.
It that's practical task (and not an exercise of learning Rust macro language) then I strongly recommend to look on procedural macro approach in general and dynasm crate in particular (currently it doesn't support MIPS, but it would be easier to add it than to write something like that from scratch).
Macro-by-example Rust's facility is much stronger than many other languages have but they are still not powerful enough to build ergonomic DSL.