How to parse a function with a declarative macro?

I need a declarative macro to parse a function.

I wrote one:

macro_rules! parse_fn
{

  () => {};

  (
    $( #[ $Meta : meta ] )*
    $( pub )?
    fn $Name : ident
    $( $Rest : tt )*
  )
  =>
  {
    $crate::parse_fn!
    {
      as DEFINE_FN
      as META $( #[ $Meta ] )*
      as VIS $( pub )?
      as NAME $Name
      as INPUT ()
      as OUTPUT
      as BLOCK {}
      as REST
        $( #[ $Meta ] )*
        $( pub )? fn $Name
        $( $Rest )*
    }
  };

  (
    as DEFINE_FN
    as META $( #[ $Meta : meta ] )*
    as VIS $( pub )*
    as NAME $Name : ident
    as INPUT $Input : tt
    as OUTPUT $( -> $Output : ty )?
    as BLOCK $Block : block
    as REST
      $Item : item
      $( $Rest : tt )*
  )
  =>
  {
    macro_rules! $Name
    {
      () =>
      {
        $Item
      };
    }
    $crate::parse_fn!
    {
      $( $Rest )*
    }
  };

}

But the problem of my solution is that it does not let pass visibility directive $( pub )?.

It gives

error: attempted to repeat an expression containing no syntax variables matched as repeating at this depth

There is workaround of duplicating of

  (
    $( #[ $Meta : meta ] )*
    pub
    fn $Name : ident
    $( $Rest : tt )*
  ) =>
...
  (
    $( #[ $Meta : meta ] )*
    fn $Name : ident
    $( $Rest : tt )*
  ) =>
...

But that solution is not good enough because visibility prefix is not only optional part, permutation of all combinations is too tedious.

Why am I getting the exception?
Better solution to parse a function?

Original Playground


Problem with visibility directive is solved. Thanks for advice.

Another obstacle is parsing parameters fn f3< T : Clone >() of a function. Best what I got is here.

It throws error:

error: local ambiguity when calling macro `parse_fn`: multiple parsing options: built-in NTs tt ('Input') or 1 other option.
  --> src/bin/problem2.rs:80:8
   |
80 |   fn f3< T : Clone >( _ : T )
   |        ^

Use the :vis metaspecifier.

There is no such thing in documentation. More over even if it existed, it probably does not solve the problem with $( x )?. Does it?

Is it you posting the same question on StackOverflow? Answered there :slight_smile: This is not the documentation.

Yep.

The documentation is at Introduction - The Rust Reference. What you linked is a (very good!) book about macros, and it was not updated for a long time. There is an updated version at Introduction - The Little Book of Rust Macros, and it does mention :vis.

1 Like

Looks like it can help.
But there is no specifier for parameters.
By the way, don't I use muncher?

No there is not, but parameters are just pattern : type. However pat cannot be followed by ty. In general, function syntax cannot be fully parsed by a declarative macro without TT munching. If you want simpler grammar (no generics, no where clause, no parameter patterns - only identifiers, no methods) it looks like:

$vis:vis fn $name:ident ( $($param:ident : $param_ty:ty),* $(,)? ) $( -> $ret_ty:ty )?
$code:block

Doesn't look like.

1 Like

Version based on your advice.

Does exist easy solution for parameters?

fn f1< Parameter >() {}

If you need full parsing, better dropping into proc macros land.

Thanks. Not an option.