Search code examples
parsingrustmacrosrust-proc-macros

Why is this procedural macro interpreting this expression as a function?


So my current code has a procedural macro that is interpreting this algebraic expression as a function this is the error

error: expected expression, found keyword `fn`
 --> src\main.rs:5:5
  |
5 |     symbolic!(x^2 + 2*x)
  |     ^^^^^^^^^^^^^^^^^^^^ expected expression
  |
= note: this error originates in the macro `symbolic` (in Nightly builds, run with -Z macro-backtrace for more info)

I tried to install the nightly build and ran with the backtrace but ...

> cargo run +nightly -Z macro-backtrace

5 |     symbolic!(x^2 + 2*x)
  |     ^^^^^^^^^^^^^^^^^^^^ expected expression
  = note: this error originates in the macro `symbolic` (in Nightly builds, run with -Z macro-backtrace for more info)

same error any clues as to what this is? I may be missing something about rusts interpreter but as far as I can tell this should be reading this as an expression this is my current code for the macro:

use proc_macro::*;

#[proc_macro]
pub fn symbolic(body: TokenStream) -> TokenStream {
    // ---shunting yard---
    let mut stack : Vec<TokenTree> = vec![];
    let mut que : Vec<TokenTree> = vec![];

    shunting_yard(body, &mut stack, &mut que);
    println!(stringify!(output_que));
    "fn answer() -> u32 { 42 }".parse().unwrap()
}

fn shunting_yard(body: TokenStream, 
    stack: &mut Vec<TokenTree>,
    que: &mut Vec<TokenTree>,
) {
    for tt in body.into_iter(){
        match tt {
            TokenTree::Ident(_) => que.push(tt),
            TokenTree::Punct(_) => {
                while precedence(Some(&tt)) <= precedence(stack.last()){
                    if let Some(punct) = stack.pop() { que.push(punct); }
                }
                stack.push(tt)
            },
            TokenTree::Literal(_) => que.push(tt),
            TokenTree::Group(group) => shunting_yard(group.stream() , stack, que),
        }
    } while let Some(op) = stack.pop() {
        que.push(op);
    }
}

fn precedence(tt: Option<&TokenTree>) -> usize{
    if let Some(TokenTree::Punct(punct)) = tt{
        match punct.as_char() {
            '^' => 3,
            '/' | '*' => 2,
            '-' | '+' => 1,
            _ => 0
        }
    } else {
        0
    }
}

Solution

  • It's not the macro trying to interpret anything as function. Your macro produces a fn item because you tell it to produce one here:

    "fn answer() -> u32 { 42 }".parse().unwrap()
    

    Since you ignore all other input writing this:

    symbolic!{whatever garbage}
    

    is equivalent to writing this:

    fn answer() -> u32 { 42 }
    

    which seems to be illegal in the context you're trying to call symbolic.