lukaslueg / macro_railroad

A library to generate syntax diagrams for Rust macros.
MIT License
536 stars 11 forks source link

jrust::java_inner fails to parse #5

Closed lukaslueg closed 6 years ago

lukaslueg commented 6 years ago

This macro should parse, but doesn't:

macro_rules! java_inner {
    (toplevel {}) => { ... };
    (toplevel { package $name:ident; $($remaining:tt)* }) => { ... };
    (toplevel { public class $name:ident {
        $($kind:ident $var:ident;)*
        ---
        $($inner:tt)*
    } $($remaining:tt)* }) => { ... };
    (toplevel { public class $name:ident {
        $($inner:tt)*
    } $($remaining:tt)* }) => { ... };
    (class($class:ident) {}) => { ... };
    (class($class:ident) { public static void main(String[] $args:ident) {
        $($inner:tt)*
    } $($remaining:tt)* }) => { ... };
    (class($class:ident) { public $constructor:ident($self:ident$(, $kind:ident $var:ident)*) {
        $($inner:tt)*
    } $($remaining:tt)* }) => { ... };
    (class($class:ident) { public $ret:ident $fn:ident($self:ident$(, $kind:ident $var:ident)*) {
        $($inner:tt)*
    } $($remaining:tt)* }) => { ... };
    (class($class:ident) { public static $ret:ident $fn:ident($($kind:ident $var:ident),*) {
        $($inner:tt)*
    } $($remaining:tt)* }) => { ... };
    (stmt($class:ident) {}) => { ... };
    (stmt($class:ident) { System.out.println($($out:tt)*); $($remaining:tt)* }) => { ... };
    (stmt($class:ident) { System.out.println_debug($($out:tt)*); $($remaining:tt)* }) => { ... };
    (stmt($class:ident) { System.out.print($($out:tt)*); $($remaining:tt)* }) => { ... };
    (stmt($class:ident) { $kind:ident $name:ident = ($($value:tt)*); $($remaining:tt)* }) => { ... };
    (stmt($class:ident) { $kind:ident $name:ident = $value:expr; $($remaining:tt)* }) => { ... };
    (stmt($class:ident) { ($name:expr) = ($($val:tt)*); $($remaining:tt)* }) => { ... };
    (stmt($class:ident) { ($name:expr) = $val:expr; $($remaining:tt)* }) => { ... };
    (stmt($class:ident) { $name:ident = $val:expr; $($remaining:tt)* }) => { ... };
    (stmt($class:ident) { ($name:expr) += $val:expr; $($remaining:tt)* }) => { ... };
    (stmt($class:ident) { ($name:expr) -= $val:expr; $($remaining:tt)* }) => { ... };
    (stmt($class:ident) { $name:ident++; $($remaining:tt)* }) => { ... };
    (stmt($class:ident) { $name:ident--; $($remaining:tt)* }) => { ... };
    (stmt($class:ident) { return ($($val:tt)*); $($remaining:tt)* }) => { ... };
    (stmt($class:ident) { return $val:expr; $($remaining:tt)* }) => { ... };
    (stmt($class:ident) { break; $($remaining:tt)* }) => { ... };
    (stmt($class:ident) { continue; $($remaining:tt)* }) => { ... };
    (stmt($class:ident) { for (($($pre:tt)*); ($($cond:tt)*); ($($post:tt)*)) {
        $($inner:tt)*
    } $($remaining:tt)* }) => { ... };
    (stmt($class:ident) { if ($($cond:tt)*) {
        $($success:tt)*
    } $(else if ($($elseif_cond:tt)*) {
        $($elseif_success:tt)*
    // Else is not optional but we can use * as a hack
    })* $(else {
        $($otherwise:tt)*
    })*; $($remaining:tt)* }) => { ... };
    (stmt($class:ident) { switch($($search:tt)*) {
        $(case ($match:expr) {
            $($success:tt)*
        })*
        // Should only be one default but rust doesn't have optional macro args yet AFAIK
        $(default {
            $($default:tt)*
        })*
    } $($remaining:tt)* }) => { ... };
    (stmt($class:ident) { while ($($cond:tt)*) {
        $($inner:tt)*
    } $($remaining:tt)* }) => { ... };
    (stmt($class:ident) { $val:ident.$fn:ident($(($($var:tt)*)),*); $($remaining:tt)* }) => { ... };
    (stmt($class:ident) { $val:ident.$fn:ident($($var:expr),*); $($remaining:tt)* }) => { ... };
    (stmt($class:ident) { $fn:ident($(($($var:tt)*)),*); $($remaining:tt)* }) => { ... };
    (stmt($class:ident) { $fn:ident($($var:expr),*); $($remaining:tt)* }) => { ... };
    (expr($class:ident) { $array:ident[$index:expr] }) => { ... };
    (expr($class:ident) { $array:ident.length }) => { ... };
    (expr($class:ident) { ($($var1:tt)*) $(+ ($($var2:tt)*))+ }) => { ... };
    (expr($class:ident) { $var:ident++ }) => { ... };
    (expr($class:ident) { $var1:ident $op:tt $var2:ident }) => { ... };
    (expr($class:ident) { ($($var1:tt)*) $op:tt ($($var2:tt)*) }) => { ... };
    (expr($_class:ident) { new $class:ident($(($($var:tt)*)),*) }) => { ... };
    (expr($_class:ident) { new $class:ident($($var:expr),*) }) => { ... };
    (expr($class:ident) { $fn:ident($(($($var:tt)*)),*) }) => { ... };
    (expr($class:ident) { $fn:ident($($var:expr),*) }) => { ... };
    (expr($class:ident) { $expr:expr }) => { ... };
    (kind byte) => { ... };
    (kind short) => { ... };
    (kind int) => { ... };
    (kind long) => { ... };
    (kind void) => { ... };
    (kind $name:ident) => { ... };
}
lukaslueg commented 6 years ago

Minimal example:

macro_rules! a {
    ($self:ident) => { ... };
}

We fail to parse $self, $foo works fine.

lukaslueg commented 6 years ago

@dtolnay what's your oppinion of leaving syn behind right now (and not trying to have a correct ™ macro_rules()-parser with it) and using libsyntax instead?

dtolnay commented 6 years ago

I think you will have a harder time with libsyntax. The API is not designed to be used outside of the compiler. In any case, this should be a one-line fix -- there is a syn!(Ident) that needs to be call!(Ident::parse_any) so as to permit keywords in the name of a fragment variable.

lukaslueg commented 6 years ago

Fixed in 2a45b0c