Parse-QTEDI/lib/Parse/QTEDI.pm
package Parse::QTEDI;
# Author: Dongxu Ma <dongxu@cpan.org>
use 5.005;
use strict;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $parser $DEBUG);
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw();
@EXPORT_OK = qw($parser);
use Parse::RecDescent ();
use YAML::Syck ();
$VERSION = '0.19';
$VERSION = eval $VERSION; # see L<perlmodstyle>
# Global flags
# unless undefined, report fatal errors
#$::RD_ERRORS = 1;
# unless undefined, also report non-fatal problems
#$::RD_WARN = 1;
# if defined, also suggestion remedies
$::RD_HINT = 1;
# if defined, also trace parsers' behaviour
#$::RD_TRACE = 1;
# if defined, generates "stubs" for undefined rules
#$::RD_AUTOSTUB = 1;
# if defined, appends specified action to productions
#$::RD_AUTOACTION = 1;
$::RD_DEBUG = $DEBUG ? 1 : 0;
my $grammar = do { local $/; <DATA> };
$parser = Parse::RecDescent::->new($grammar);
1;
__DATA__
# focus on:
# Level 1: class, namespace, typedef, function, enum, union
# Level 2: template, expression
#
# which are relavant to make binding
# loop structure
# CAUTION: the biggest assert here is we are working on a _VALID_ header
begin : <rulevar: local $stash = [] >
begin :
loop(s) eof { print YAML::Syck::Dump($stash) }
eof : /^\Z/
# make sure function_pointer is IN FRONT OF function
# since function is compatible with function_pointer, unfortunately
primitive_loop :
qt_macro(s)
| kde_macro(s)
| typedef(s)
| comment(s)
| enum(s)
| template(s)
| extern(s)
| namespace(s)
| class(s)
| function_pointer(s)
| function(s)
| expression(s)
# inside a class each primitive code block has to consider
# accessibility keyword(s) in front of
primitive_loop_inside_class :
qt_macro
| kde_macro
| typedef
| comment
| enum
| template
| extern
| namespace
| class
| function_pointer
| function
| expression
loop :
primitive_loop { push @$stash, @{$item[1]} }
|
# keywords
keywords :
keyword_class | keyword_typedef | keyword_comment
| keyword_template | keyword_enum
keyword_class_optional :
'friend' | 'static' | 'mutable'
keyword_class :
keyword_class_optional(s?) ( 'class' | 'struct' | 'union' )
{ $return = [ $item[2], $item[1] ] }
keyword_namespace: 'namespace'
keyword_typedef : 'typedef'
keyword_comment : '#'
keyword_template : 'template'
keyword_enum : 'enum'
keyword_inside_expression :
'struct' | 'enum' | 'union'
# primitive code blocks
comment :
keyword_comment /.*?$/mio
{ $return = { type => 'comment', value => $item[2] } }
{ print STDERR $item[1], ": ", $item[2], "\n" if $::RD_DEBUG }
typedef :
keyword_typedef
( enum { $return = $item[1] }
| class { $return = $item[1] }
| function_pointer { $return = $item[1] }
| /(?>[^;]+)/sio ';' { $return = $item[1] }
)
{ $return = { type => 'typedef', body => $item[2] } }
{ print STDERR $item[1], ": ", $item[2], "\n" if $::RD_DEBUG }
enum :
keyword_enum enum_name enum_body variables ';'
{
$return = { type => 'enum' };
$return->{name} = $item[2] if $item[2];
$return->{value} = $item[3] if $item[3];
$return->{variable} = $item[4] if $item[4];
unless($item[3]) {
# no enum body, possibly inside typedef or forward decl
# split variables from enum_name
my @v = split /(?<!,)\s+(?!,)/, $item[2];
if (@v > 1) {
$return->{variable} = pop @v;
$return->{name} = join(" ", @v);
}
}
}
{ print STDERR $item[1], ": ", join(" ", $item[2], join(" ", @{$item[3]}), $item[4]), "\n"
if $::RD_DEBUG }
# make sure it has no other structure delimiters
# special handle for C-stype enum/struct/union expression
expression:
( class_accessibility_content
| keyword_class | keyword_typedef | keyword_comment
| keyword_template
) <commit> <reject>
| keyword_inside_expression <commit> next_brace_or_semicolon ';'
{
$return = { type => 'expression',
value => join(" ", $item[1], $item[3]) }
}
{ print STDERR "expression: ", $return, "\n" if $::RD_DEBUG }
| expression_body ';'
{ $return = { type => 'expression', value => $item[1] } }
{ print STDERR "expression: ", $item[1], "\n" if $::RD_DEBUG }
# container code blocks
template :
keyword_template '<' template_typename '>' template_body
{
$return = { type => 'template', body => $item[5] };
$return->{typename} = $item[3] if $item[3];
}
{ print STDERR $item[1], ": ",
join(" ", @item[2 .. 5]), "\n" if $::RD_DEBUG }
extern :
'extern' '"C"' '{' namespace_body(s?) '}'
{ $return = { type => 'extern', subtype => 'C', body => [] } }
{ foreach my $a (@{$item[4]}) { push @{$return->{body}}, @$a } }
{ print STDERR "extern: ",
join(" ", $item[2], $item[4]), "\n" if $::RD_DEBUG }
| 'extern'
( class
{ $return = { subtype => 'class', body => $item[1] } }
| enum
{ $return = { subtype => 'enum', body => $item[1] } }
| function
{ $return = { subtype => 'function', body => $item[1] } }
| expression
{ $return = { subtype => 'expression', body => $item[1] } } )
{ $return = $item[2]; $return->{type} = 'extern' }
{ print STDERR "extern: ",
join(" ", $return->{type}, $return->{subtype}), "\n" if $::RD_DEBUG }
namespace:
keyword_namespace namespace_name '{' namespace_body(s?) '}' ( ';' | )
{ $return = { type => 'namespace', name => $item[2], body => [] } }
{ foreach my $a (@{$item[4]}) { push @{$return->{body}}, @$a } }
{ print STDERR "namespace: ", $item[2], "\n" if $::RD_DEBUG }
class :
keyword_class class_name class_inheritance class_body class_attribute variables ';'
{
$return = { type => $item[1][0], name => $item[2] };
$return->{property} = $item[1][1] if @{$item[1][1]};
$return->{inheritance} = $item[3] if $item[3];
$return->{body} = $item[4] if $item[4];
$return->{attribute} = $item[5] if $item[5];
$return->{variable} = $item[6] if $item[6];
unless($item[4]) {
# no class body, possibly inside typedef or forward decl
# split variables from enum_name
my @v = split /(?<!,)\s+(?!,)/, $item[2];
if (@v > 1) {
$return->{variable} = pop @v;
$return->{name} = join(" ", @v);
}
}
}
{ print STDERR $item[1][0], ": ",
join(" ", @item[2 .. $#item-3]), "\n" if $::RD_DEBUG }
# a simple trap here
# to prevent template function from being parsed as normal one
function :
keyword_template <commit> <reject>
| class_accessibility <commit> <reject>
| function_header function_body
{
$return = { type => 'function', %{$item[1]} };
if ($item[2]) {
push @{$return->{property}}, $item[2];
}
}
{ print STDERR "function: ", $item[1]->{name}, "\n" if $::RD_DEBUG }
# QT-specific macros
qt_macro_1 :
'QT_BEGIN_HEADER' | 'QT_END_HEADER' | 'Q_OBJECT' | 'Q_GADGET'
qt_macro_2 :
'QT_MODULE' | 'Q_FLAGS' | 'Q_DISABLE_COPY' |
'QDOC_PROPERTY' | 'Q_ENUMS' | 'Q_SETS' | 'Q_OVERRIDE' |
'Q_DECLARE_FLAGS' | 'Q_DECLARE_PRIVATE' | 'Q_DECLARE_TYPEINFO' |
'Q_DECLARE_METATYPE' | 'Q_DECLARE_BUILTIN_METATYPE' |
'Q_DECLARE_EXTENSION_INTERFACE' |
'Q_DECLARE_OPERATORS_FOR_FLAGS' | 'Q_DECLARE_SHARED' |
'Q_DECLARE_INTERFACE' | 'Q_DECLARE_ASSOCIATIVE_ITERATOR' |
'Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR' |
'Q_DECLARE_SEQUENTIAL_ITERATOR' |
'Q_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR' |
'Q_DUMMY_COMPARISON_OPERATOR'
qt_macro_3 :
'Q_PRIVATE_SLOT' | 'Q_PROPERTY' | 'Q_PRIVATE_PROPERTY' | 'Q_CLASSINFO' | 'Q_INTERFACES'
qt_macro_10:
'Q_INVOKABLE'
qt_macro_99:
'Q_REQUIRED_RESULT'
qt_macro :
qt_macro_1 ( ';' | )
{ $return = { type => 'macro', subtype => 1, name => $item[1] } }
{ print STDERR $item[1], "\n" if $::RD_DEBUG }
| qt_macro_2 '(' next_end_bracket ')' ( ';' | )
{ $return = { type => 'macro', subtype => 2, name => $item[1],
value => $item[3] } }
{ print STDERR join(" ", @item[1 .. 4]), "\n" if $::RD_DEBUG }
| qt_macro_3 '(' balanced_bracket(s) ')' ( ';' | )
{ $return = { type => 'macro', subtype => 3, name => $item[1],
values => join(" ", @{$item[3]}) } }
{ print STDERR join(" ", $item[1 .. 2], @{$item[3]}, $item[4]), "\n" if $::RD_DEBUG }
# KDE related
kde_macro_1 :
'K_DCOP'
kde_macro_2 :
'K_SYCOCATYPE'
kde_macro_3 :
'K_FIXME_FIXME_FIXME'
kde_macro_99 :
'KDE_DEPRECATED' | 'KDE_EXPORT'
kde_macro :
kde_macro_1 ( ';' | )
{ $return = { type => 'macro', subtype => 1, name => $item[1] } }
{ print STDERR $item[1], "\n" if $::RD_DEBUG }
| kde_macro_2 '(' next_end_bracket ')' ( ';' | )
{ $return = { type => 'macro', subtype => 2, name => $item[1],
value => $item[3] } }
{ print STDERR join(" ", @item[1 .. $#item]), "\n" if $::RD_DEBUG }
| kde_macro_3 '(' balanced_bracket(s) ')' ( ';' | )
{ $return = { type => 'macro', subtype => 3, name => $item[1],
values => join(" ", @{$item[3]}) } }
{ print STDERR join(" ", $item[1 .. 2], @{$item[3]}, $item[4]), "\n" if $::RD_DEBUG }
# functional code blocks
# internal actions
# CAUTION: might get dirty string which contains \t\n
# strip hard return
# FIXME: \015 for MSWin32
next_begin_brace :
/(?>[^\{]+)/sio { ( $return = $item[1] ) =~ s/\n/ /go }
next_end_brace :
/(?>[^\}]+)/sio { ( $return = $item[1] ) =~ s/\n/ /go }
next_begin_or_end_brace :
/(?>[^\{\}]+)/sio { ( $return = $item[1] ) =~ s/\n/ /go }
next_brace_or_semicolon :
/(?>[^\{\}\;]+)/sio { ( $return = $item[1] ) =~ s/\n/ /go }
next_begin_angle_bracket :
/(?>[^\<]+)/sio { ( $return = $item[1] ) =~ s/\n/ /go }
next_end_angle_bracket :
/(?>[^\>]+)/sio { ( $return = $item[1] ) =~ s/\n/ /go }
next_angle_bracket :
/(?>[^\<\>]+)/sio { ( $return = $item[1] ) =~ s/\n/ /go }
next_begin_square_bracket :
/(?>[^\[]+)/sio { ( $return = $item[1] ) =~ s/\n/ /go }
next_end_square_bracket :
/(?>[^\]]+)/sio { ( $return = $item[1] ) =~ s/\n/ /go }
next_square_bracket :
/(?>[^\[\]]+)/sio { ( $return = $item[1] ) =~ s/\n/ /go }
next_equals :
/(?>[^\=]+)/sio { ( $return = $item[1] ) =~ s/\n/ /go }
next_dot :
/(?>[^\,]+)/sio { ( $return = $item[1] ) =~ s/\n/ /go }
next_dot_or_end_brace :
/(?>[^\,\}]+)/sio { ( $return = $item[1] ) =~ s/\n/ /go }
next_begin_bracket :
/(?>[^\(]+)/sio { ( $return = $item[1] ) =~ s/\n/ /go }
next_end_bracket :
/(?>[^\)]+)/sio { ( $return = $item[1] ) =~ s/\n/ /go }
next_bracket_or_semicolon :
/(?>[^\(\)\;]+)/sio { ( $return = $item[1] ) =~ s/\n/ /go }
next_begin_or_end_bracket :
/(?>[^\(\)]+)/sio { ( $return = $item[1] ) =~ s/\n/ /go }
next_bracket_or_brace_or_semicolon :
/(?>[^\(\)\{\}\;]+)/sio { ( $return = $item[1] ) =~ s/\n/ /go }
next_bracket_or_brace_or_semicolon_or_equal :
/(?>[^\(\)\{\}\;\=]+)/sio { ( $return = $item[1] ) =~ s/\n/ /go }
next_bracket_or_square_bracket_or_brace_or_semicolon_or_equal :
/(?>[^\(\)\{\}\[\]\;\=]+)/sio { ( $return = $item[1] ) =~ s/\n/ /go }
next_semicolon :
/(?>[^\;]+)/sio { ( $return = $item[1] ) =~ s/\n/ /go }
next_begin_brace_or_colon_or_semicolon :
/(?>[^\{\:\;]+)/sio { ( $return = $item[1] ) =~ s/\n/ /go }
balanced_bracket_next_token :
next_begin_or_end_bracket { $return = $item[1] }
| { $return = '' }
balanced_bracket :
balanced_bracket_next_token
( '(' balanced_bracket(s) ')'
{ $return = join(" ", $item[1], @{$item[2]}, $item[3]) }
| { $return = '' } )
{ $return = join(" ", @item[1 .. $#item]) }
| { $return = '' }
balanced_angle_bracket_next_token :
next_angle_bracket { $return = $item[1] }
| { $return = '' }
balanced_angle_bracket :
balanced_angle_bracket_next_token
( '<' balanced_angle_bracket(s) '>'
{ $return = join(" ", $item[1], @{$item[2]}, $item[3]) }
| { $return = '' } )
{ $return = join(" ", @item[1 .. $#item]) }
| { $return = '' }
# expression related
# array declaration should be handled carefully
# FIXME: __attribute__()
expression_next_token :
next_bracket_or_square_bracket_or_brace_or_semicolon_or_equal
{ $return = $item[1] }
| { $return = '' }
array_dimention_next_token :
next_square_bracket { $return = $item[1] }
| { $return = '' }
expression_body :
expression_next_token
{ $item[1] =~ m/\boperator\W?$/o ? undef : 1 }
array_dimention(s?)
expression_value(?)
{ $return = join(" ", $item[1], @{$item[3]}, $item[4]) }
array_dimention :
'[' ( next_square_bracket { $return = $item[1] }
| { $return = '' } ) ']'
{ $return = join(" ", @item[1 .. 3]) }
expression_value:
'=' ( '{' balanced_brace(s) '}' | next_semicolon )
{ $return = '= '. $item[2] }
# variable related
variables : next_semicolon { $return = $item[1] } | { $return = '' }
# function related
# at least one '()' block should appear for a valid header
# trap other keywords to prevent mess
function_header :
( keyword_comment | keyword_class | keyword_enum
| keyword_typedef ) <commit> <reject>
| ( qt_macro_10 | ) function_header_block(s)
{
$return->{name} = '';
my $seen_function_name = 0;
foreach my $i (@{$item[2]}) {
if ($i->{_subtype} == 1) {
# attribute
# stripped currently
#$return->{name} .= " ". $i->{_value};
} elsif ($i->{_subtype} == 2) {
# function name with params
$seen_function_name = 1;
$return->{name} .= $i->{_name};
$return->{parameter} = $i->{_value} if
@{$i->{_value}};
} elsif ($i->{_subtype} == 3) {
# other macros
push @{$return->{property}}, $i->{_value} if
$i->{_value};
} elsif ($i->{_subtype} == 4) {
# const
if ($i->{_value}) {
if ($seen_function_name) {
push @{$return->{property}}, $i->{_value};
}
else {
$return->{name} = $i->{_value}. ' '. $return->{name};
}
}
}
}
}
function_header_next_token :
next_bracket_or_brace_or_semicolon_or_equal
{ $return = $item[1] }
| { $return = '' }
# function parameter process
# parse parameters and simply consume __attribute__
function_header_block :
( /const\b/o
{
#print STDERR "const\n";
$return = { _subtype => 4, _value => 'const' };
}
| function_header_next_token
{ $item[1] =~ m/\boperator\b/o ? 1 : undef }
( '(' ')' { $return = '()' } | next_begin_bracket { $return = $item[1] } )
'('
( function_parameters { $return = $item[1]; } | { $return = []; } )
')'
{
#print STDERR "operator\n";
$return = { _subtype => 2, _name => $item[1].$item[3], };
$return->{_value} = $item[5];
}
| function_header_next_token
{ $item[1] =~ m/^\s*throw\s*$/o ? 1 : undef }
'(' next_bracket_or_brace_or_semicolon ')'
{
#print STDERR "throw\n";
$return = { _subtype => 3, _value => 'throw('. $item[4]. ')' }
}
| function_header_next_token
{ $item[1] =~ m/\_\_attribute\_\_\s*$/o ? 1 : undef }
'(' function_header_loop(s?) ')'
{
#print STDERR "__attribute__\n";
$return = { _subtype => 1,
_value => join("", $item[1], $item[3],
@{$item[4]}, $item[5]) }
}
| function_header_next_token
{ $item[1] =~ m/^\:/o ? 1 : undef }
function_header_loop(s)
{ $return = { _subtype => 0 } }
| function_header_next_token
'('
( function_parameters { $return = $item[1]; } | { $return = []; } )
')'
{
#print STDERR "name:$item[1]\n";
$return = { _subtype => 2, _name => $item[1], };
$return->{_value} = $item[3];
}
) { $return = $item[1]; }
| function_macro_99(s?)
{
#print STDERR "property:", @{$item[1]}, "\n";
$return = { _subtype => 3, _value => join("", @{$item[1]}) }
}
# TODO: loop in more elegant way
function_header_loop :
( function_header_next_token { $return = $item[1]; }
| { $return = ''; }
)
( '(' ')' { $return = '()'; }
| '(' function_header_loop(s?) ')'
{ $return = join("", $item[1], @{$item[2]}, $item[3]) }
| { $return = '' }
)
{ $return = join("", @item[1 .. $#item]) }
function_macro_99 :
'const' | qt_macro_99 | kde_macro_99
function_parameters :
function_parameter_loop
function_parameter_loop :
function_parameter
( ',' function_parameter_loop { $return = $item[2] }
| { $return = [] }
)
{ $return = [ $item[1] ]; push @$return, @{$item[2]} if @{$item[2]} }
function_parameter :
function_parameter_declaration function_parameter_default_value(?)
{
$return = $item[1];
$return->{default} = $item[2]->[0] if @{$item[2]};
}
function_parameter_declaration_next_token :
/(?>[^\,\=\(\)\<\>]+)/iso { ( $return = $item[1] ) =~ s/\n/ /go }
function_parameter_declaration :
( function_parameter_declaration_next_token
{ $return = $item[1]; }
| { $return = ''; }
)
( function_parameter_function_pointer
{ $return = { subtype => 'fpointer', value => $item[1] }; }
| function_parameter_template_type
{ $return = { subtype => 'template', value => $item[1] }; }
| function_parameter_array_pointer
{ $return = { subtype => 'apointer', value => $item[1] }; }
| { $return = { subtype => 'simple' }; }
)
{
if ($item[2]->{subtype} eq 'simple') {
$return = { name => $item[1], subtype => 'simple', };
} elsif ($item[2]->{subtype} eq 'template') {
$return = { name => $item[1]. $item[2]->{value} };
$return->{subtype} = 'template';
} elsif ($item[2]->{subtype} eq 'fpointer') {
$return = $item[2]->{value};
$return->{return} = $item[1];
$return->{subtype} = 'fpointer';
} elsif ($item[2]->{subtype} eq 'apointer') {
$return = $item[2]->{value};
$return->{type} = $item[1]. $return->{type};
$return->{subtype} = 'apointer';
}
}
function_parameter_array_pointer :
'(' '&' function_parameter_declaration_next_token ')'
'[' /(?>[^\]]+)/iso ']'
{ $return = { type => join("", @item[1,2,4..7]), name => $item[3] } }
function_parameter_template_type_next_token :
/(?>[^\<\>]+)/iso { ( $return = $item[1] ) =~ s/\n/ /go }
function_parameter_template_type :
'<' function_parameter_template_type_loop(s) '>'
( function_parameter_declaration_next_token
{ $return = $item[1]; }
| { $return = ''; }
)
{
$return = join(" ",
join("", $item[1], join("", @{$item[2]}), $item[3]),
$item[4]);
}
function_parameter_template_type_loop :
( function_parameter_template_type_next_token
{ $return = $item[1]; }
| { $return = ''; }
)
( '<' function_parameter_template_type_loop '>'
{ $return = join("", @item[1 .. 3]); }
| { $return = ''; }
)
{ $return = join("", @item[1 .. 2]); }
function_parameter_function_pointer_next_token :
/(?>[^\(\)]+)/iso { ( $return = $item[1] ) =~ s/\n/ /go }
function_parameter_function_pointer :
'(' function_parameter_function_pointer_loop ')'
{ $item[2] ? 1 : undef }
'(' function_parameter_loop ')'
function_parameter_function_pointer_const
{
$return = { name => $item[2], parameter => $item[6], };
push @{ $return->{property} }, $item[8] if $item[8];
}
# 'const' could be either return/param type attribute
# or function property
function_parameter_function_pointer_const :
( 'const' { $return = 'const'; }
| { $return = ''; }
)
{ $return = $item[1]; }
function_parameter_function_pointer_loop :
( function_parameter_function_pointer_next_token
{ $return = $item[1]; }
| { $return = ''; }
)
( function_parameter_function_pointer
{ $return = $item[1]; }
| { $return = {}; }
)
{
if (exists $item[2]->{name}) {
$return->{name} = $item[2]->{name};
$return->{parameter} = $item[2]->{parameter};
}
else {
$return = $item[1];
}
}
function_parameter_default_value_next_token :
/(?>[^\(\'\"\)\,]+)/iso { ( $return = $item[1] ) =~ s/\n/ /go }
function_parameter_default_value :
'=' function_parameter_default_value_loop(s)
{ $return = join("", @{$item[2]}); }
{ print STDERR "default value:", $return, "\n" if $::RD_DEBUG; }
function_parameter_default_value_loop_token_dispatch :
'(' ')'
{ $return = '()'; }
| '" "' { $return = $item[1]; }
| "' '" { $return = $item[1]; }
| "(' ')" { $return = $item[1]; }
| '(" ")' { $return = $item[1]; }
| '(' function_parameter_default_value_loop2 ')'
{ $return = join("", @item[1 .. 3]); }
| "'" /(?>[^\']*)/iso "'" { $return = join("", @item[1 .. 3]); }
| '"' /(?>[^\"]*)/iso '"' { $return = join("", @item[1 .. 3]); }
function_parameter_default_value_loop2 :
( function_parameter_default_value_next_token
{ $return = $item[1]; }
| { $return = ''; }
)
( function_parameter_default_value_loop_token_dispatch
{ $return = $item[1]; }
| ',' function_parameter_default_value_loop2
{ $return = join("", @item[1 .. 2]); }
| { $return = ''; }
)
{ $return = join("", @item[1 .. 2]); }
function_parameter_default_value_loop :
( function_parameter_default_value_next_token
{ $return = $item[1]; }
| { $return = ''; }
)
( function_parameter_default_value_loop_token_dispatch
{ $return = $item[1]; }
| { $return = ''; }
)
{ $return = join('', @item[1 .. 2]); }
function_body :
';' { $return = '' }
| '=' '0' ';' { $return = 'pure virtual' }
| '{' balanced_brace(s) '}' ( ';' | ) { $return = '' }
balanced_brace_next_token :
next_begin_or_end_brace { $return = $item[1] }
| { $return = '' }
balanced_brace :
balanced_brace_next_token ( '{' balanced_brace(s) '}' | )
| { $return = '' }
# enum related
enum_name :
next_brace_or_semicolon { $return = $item[1] }
| { $return = '' }
# enum_unit(s /,/) _NOT_ work here
enum_body :
'{' '}' { $return = [] }
| '{' enum_unit(s) '}'
{ $return = [ grep { ref $_ } @{$item[2]} ] }
#{ print STDERR "enum_body: ", join(" ", @{$item[2]}), "\n" if $::RD_DEBUG }
| { $return = '' }
enum_unit :
comment { $return = '' }
| next_dot_or_end_brace ( ',' | )
{ $return = [ split /\s*=\s*/, $item[1] ] }
#{ print STDERR "enum_unit: $return\n" if $::RD_DEBUG }
# template related
template_typename :
balanced_angle_bracket(s) { $return = join(" ", @{$item[1]}) }
| { $return = '' }
# TODO: better way to handle template expression
template_body :
class { $return = $item[1] }
| function { $return = $item[1] }
| expression { $return = $item[1] }
# class related
class_name :
class_name_loop
class_name_next_token :
next_begin_brace_or_colon_or_semicolon { $return = $item[1] }
| { $return = '' }
class_name_loop :
class_name_next_token
( '::' class_name_loop { $return = '::'.$item[2] }
| { $return = '' } )
{ $return = join("", $item[1], $item[2]) }
#{ print STDERR "class_name: ", $item[1], "\n" if $::RD_DEBUG }
| { $return = '' }
# FIXME: multiple inherit
class_inheritance :
':' next_begin_brace { $return = $item[2] }
#{ print STDERR "class_inheritance: ", $item[2], "\n" if $::RD_DEBUG }
| { $return = '' }
# class_body_content(s?) _NOT_ work here
class_body :
'{' '}' { $return = '' }
| '{' class_body_content(s) '}'
{ $return = $item[2] }
| { $return = '' }
class_body_content :
class_accessibility { $return = $item[1] }
| noop(s) { $return = { type => 'noop' } }
| primitive_loop_inside_class { $return = $item[1] }
#{ print STDERR "class_body_content: ", $return, "\n" if $::RD_DEBUG }
# | { $return = '' }
# #{ print STDERR "class_body_content: NULL\n" if $::RD_DEBUG }
class_accessibility_loop :
( class_accessibility_content { $return = $item[1] } | { $return = '' } ) qt_accessibility_content { $return = $item[1] ? join(' ', $item[1], $item[2]) : $item[2] }
| class_accessibility_content
| kde_accessibility_content
class_accessibility :
class_accessibility_loop ':'
{ $return = { type => 'accessibility', value => $item[1] } }
qt_accessibility_content :
'Q_SIGNALS' | 'Q_SLOTS' | 'signals' | 'slots'
kde_accessibility_content:
'k_dcop'
class_accessibility_content :
'public' | 'private' | 'protected'
class_attribute:
/__attribute__\s*\(\((.+?)\)\)/io { $return = $1 } | { $return = '' }
noop :
';'
#namespace related
namespace_name :
next_begin_brace { $return = $item[1] } | { $return = '' }
namespace_body : primitive_loop { $return = $item[1] }
#typedef related
function_pointer :
next_bracket_or_semicolon function_parameter_function_pointer ';'
{
$return = $item[2];
$return->{return} = $item[1];
$return->{type} = 'fpointer';
}