Search code examples
rustrust-proc-macros

Lookahead for "=>" token with crate syn


I'm working on a little domain-specific language with proc-macros and syn, and there I want to parse a sequence of operators on the form

 lhs + rsh => res
 lhs - rsh => res

or

 lhs += rsh
 lhs -= rsh

and such.

I'm doing this using the syn crate, implementing Parser for the various structures I need. My thought was that I could parse these using a look-ahead after the first three tokens, with something like this:

let lookahead = input.lookahead1();
            
let lhs = input.parse()?;
let op = input.parse()?;
let rhs = input.parse()?;

// now, conditionally parse the res part of an operator
let res = if lookahead.peek(syn::token::FatArrow) {
    input.parse::<syn::token::FatArrow>()?;
    Some(input.parse()?)
} else { None };

The problem is that while syn::token::FatArrow is the token for => when invoking parse, it doesn't seem to be when invoking peek.

// This says false
println!("do we see =>? {}", lookahead.peek(syn::token::FatArrow));
// this also says false
println!("do we see =>? {}", lookahead.peek(Token![=>]));

The parsing works fine, but if I can't look ahead, then I can't distinguish between the two cases. (Well, I can by forking the input stream and trying both rules, then replacing the stream with the one that worked, but that seems a bit dramatic when a peak at the next token, if only I knew how to, would solve the problem).

What am I doing wrong here? Do I need to call peek with some other argument to match the token =>?


Solution

  • There is a significance to when the Lookahead1 is created: it points to the location where the cursor was at the call to lookahead1(). Lookahead1 is meant to use when you have a list of possible values, and you want to provide a nice error message when the value is invalid, listing all possible values. If you just want to peek the next token, use ParseBuffer::peek():

    let res = if input.peek(syn::token::FatArrow) {
        input.parse::<syn::token::FatArrow>()?;
        Some(input.parse()?)
    } else { None };