The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# Source grammar for Java class parser.
#
# AFTER EDITING THIS FILE YOU MUST UPDATE THE PARSER BY RUNNING:
#   cd lib/Java/Javap && perl -MParse::RecDescent - javap.grammar Java::Javap::Grammar
#
# http://java.sun.com/docs/books/jls/third_edition/html/syntax.html
# http://www.cmis.brighton.ac.uk/staff/rnb/bosware/javaSyntax/Java1_6GrammarCorrected.html
# http://www.cmis.brighton.ac.uk/staff/rnb/bosware/javaSyntax/rulesLinked.html

{
    my %methods;
    my $constructors;
}

comp_unit : comp_stmt(?)
    comp_unit_decl '{' body '}'
{
    my $retval = $item{ comp_unit_decl };
    # Match of subrule X(?) always generates an array
    my $comp_stmt = $item{ q{comp_stmt(?)} }->[0];
    $retval->{ compiled_from } = "${comp_stmt}.java";
    $retval->{ contents      } = $item{ body };
    $retval->{ methods       } = { %methods };  %methods = ();
    $retval->{ constructors  } = $constructors; $constructors = 0;
    $retval;
}
          | <error>

comp_stmt : 'Compiled from "' NAME '.java"' { $item{NAME} }

comp_unit_decl : ACCESS class_qualifier(s?)
                 CLASS_OR_INTERFACE qualified_name
                 extends_clause(?)
                 implements_clause(?)
                 verbose_class_details(?)
{
    my $perl_qualified_name = $item{ qualified_name };
    $perl_qualified_name    =~ s/\./::/g;
    {
        access              => $item{ ACCESS                 },
        qualifiers          => $item{ 'class_qualifier(s?)'  },
        class_or_interface  => $item{ CLASS_OR_INTERFACE     },
        implements          => $item{ 'implements_clause(?)' }[0],
        parent              => $item{ 'extends_clause(?)'    }[0],
        java_qualified_name => $item{ qualified_name         },
        perl_qualified_name => $perl_qualified_name,
    }
}

verbose_class_details : /[^{]*/

class_qualifier   : 'final'    { 'final'  }
                  | 'abstract' { 'status' }

extends_clause    : 'extends' qualified_name { $item{ qualified_name } }
implements_clause : 'implements' comma_list  { $item{ comma_list } }

body : body_element(s?) { $item[1] }

body_element : constant { $item[1] }
             | static_block { $item[1] }
             | method   { $item[1] }
             | variable { $item[1] }

static_block : 'static' '{' '}' ';'
    verbose_method_detail(s?) {
        { body_element => 'static_block' }
}

constant : ACCESS 'static' constant_modifier(s?) arg NAME ';'
           verbose_constant_details(?) {
    my $type = $item{ arg }[0];
    my $value = $item{ 'verbose_constant_details(?)' }[0];
    # remove trailing 'd' from double constants
    $value =~ s/d$// if $type->{name} eq 'double';
    # remove trailing 'l' from long constants
    $value =~ s/l$// if $type->{name} eq 'long';
    #warn "constant( $item{ NAME } ) @{[ %$type ]}: '$value'";
    {
        body_element => 'constant',
        access       => $item{ ACCESS },
        modifiers    => $item{ 'constant_modifier(s?)' },
        type         => $type,
        name         => $item{ NAME },
        value        => $value,
    }
}
         | 'static' 'transient' arg NAME ';' {
    {
        body_element => 'transient_constant',
        type         => $item{ arg }[0],
        name         => $item{ NAME },
    }
}

verbose_constant_details : 'Constant' 'value:' arg constant_value {
    $item{ constant_value }
}

constant_value : /[^\n]*/

constant_modifier : 'final' | 'transient' | 'volatile'

method : ACCESS method_qualifier(s?)
         arg NAME '(' arg_list(?) ')'
         throws_clause(?) ';'
         verbose_method_detail(s?) {
             $methods{ $item[4] }++;
             {
                 body_element => 'method',
                 access       => $item[1],
                 attrs        => $item[2],
                 type         => $item[3][0],
                 name         => $item[4],
                 args         => $item{ 'arg_list(?)' }[0] || [],
                 throws       => $item{ 'throws_clause(?)' }[0] || [],
             }
         }
       | ACCESS /(native)?/ qualified_name '(' arg_list(?) ')'
         throws_clause(?) ';'
         verbose_method_detail(s?) {
             $constructors++;
             $methods{ 'new' }++;
             {
                 body_element => 'constructor',
                 access       => $item[1],
                 native       => ( $item[2] eq 'native' ) ? 'native' : '',
                 args         => $item{ 'arg_list(?)' }[0] || [],
                 throws       => $item{ 'throws_clause(?)' }[0] || [],
                 # add name and type so constructor data is like a method
                 name         => 'new',
                 type         => {
                    array_depth => 0,
                    array_text => '',
                    name => $item[3],
                 },
             }
         }

verbose_method_detail : verbose_method_code
                      | verbose_line_number_table
                      | verbose_method_deprecated
                      | verbose_exceptions
                      | verbose_signature
                      | verbose_annotations

verbose_method_code       : 'Code:' /Stack.*=\d+/ byte_code_line(s?)
# Using "byte_code_line : /\d+:[^\n]*/" doesn't handle multi-line opcodes
# like tableswitch in java.sql.Date. Also skips "Exception table:"
byte_code_line : /(?!LineNumberTable)[^\n]*/

verbose_line_number_table : 'LineNumberTable:' number_table_line(s?)
number_table_line : /line \d+:[^\n]*/

verbose_method_deprecated : 'Deprecated:' 'true'
verbose_exceptions        : 'Exceptions:' throws_clause

verbose_signature         : /Signature: .*/ signature_line(s)
signature_line : /\d\d\s+[A-F0-9 ]+/

verbose_annotations       : /RuntimeVisibleAnnotations: .*/ annotations_line(s)
annotations_line : /[A-F0-9][A-F0-9]/


method_qualifier : 'abstract'     { 'abstract'     }
                 | 'native'       { 'native'       }
                 | 'static'       { 'static'       }
                 | 'synchronized' { 'synchronized' }
                 | 'final'        { 'final'        }

throws_clause  : 'throws' comma_list { $item{comma_list} }

variable : ACCESS var_modifier(s?) arg NAME ';' {
    {
        body_element => 'variable',
        access       => $item{ ACCESS },
        name         => $item{ NAME },
        type         => $item{ arg }[0],
    }
}

var_modifier : 'volatile' | 'final' | 'transient'

qualified_name : NAME '.' qualified_name
                 { "$item{NAME}.$item{qualified_name}" }
               | NAME { $item[1] }

comma_list : qualified_name ',' comma_list    {
                my @names = ( $item[1] );
                if ( ref( $item[3] ) eq 'ARRAY' ) {
                    push @names, @{ $item[3] };
                }
                else {
                    push @names, $item[3];
                }
                \@names;
             }
           | qualified_name {
                [ $item[1] ]
            }

arg_list : arg ',' arg_list { [ @{ $item[1] }, @{ $item[3] } ] }
         | arg              { $item[1] }

arg : qualified_name array_depth {
    my $array_text = '';
    foreach my $i ( 1 .. $item[2] ) {
        $array_text .= 'Array of ';
    }
    [
        {
            name        => $item[1],
            array_depth => $item[2],
            array_text  => $array_text,
        }
    ]
}

array_depth : ARRAY_LEVEL(s?) {
                my $depth = scalar @{ $item[1] };
                $depth;
              }

ARRAY_LEVEL : '[]' { 1 }

NAME : /^([\w\d\$]+)/ { $1 }

ACCESS : 'public'    { $item[1] }
       | 'protected' { $item[1] }
       | 'private'   { $item[1] }
       |             { '' }

CLASS_OR_INTERFACE : 'class'     { $item[1] }
                   | 'interface' { $item[1] }

comment : '/*' /[^*]*/ '*/' {}
        |