| line | stmt | bran | cond | sub | pod | time | code | 
| 1 | 43 |  |  | 43 |  | 339 | use strict; use warnings; | 
|  | 43 |  |  | 43 |  | 315 |  | 
|  | 43 |  |  |  |  | 1391 |  | 
|  | 43 |  |  |  |  | 236 |  | 
|  | 43 |  |  |  |  | 92 |  | 
|  | 43 |  |  |  |  | 2314 |  | 
| 2 |  |  |  |  |  |  | package Inline::CPP::Parser::RecDescent; | 
| 3 |  |  |  |  |  |  |  | 
| 4 |  |  |  |  |  |  | # Dev versions will have a _0xx suffix. | 
| 5 |  |  |  |  |  |  | # We eval the $VERSION to accommodate dev version numbering as described in | 
| 6 |  |  |  |  |  |  | # perldoc perlmodstyle | 
| 7 |  |  |  |  |  |  | our $VERSION = '0.80'; | 
| 8 |  |  |  |  |  |  | #$VERSION = eval $VERSION;  ## no critic (eval) | 
| 9 |  |  |  |  |  |  |  | 
| 10 | 43 |  |  | 43 |  | 257 | use Carp; | 
|  | 43 |  |  |  |  | 90 |  | 
|  | 43 |  |  |  |  | 6472 |  | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | sub register { | 
| 13 |  |  |  |  |  |  | { | 
| 14 | 0 |  |  | 0 | 0 | 0 | extends => [qw(CPP)], | 
| 15 |  |  |  |  |  |  | overrides => [qw(get_parser)], | 
| 16 |  |  |  |  |  |  | } | 
| 17 |  |  |  |  |  |  | } | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | sub get_parser { | 
| 20 | 0 |  |  | 0 | 0 | 0 | my $o = shift; | 
| 21 | 0 |  |  |  |  | 0 | return Inline::CPP::Parser::RecDescent::get_parser_recdescent($o); | 
| 22 |  |  |  |  |  |  | } | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | sub get_parser_recdescent { | 
| 25 | 44 |  |  | 44 | 0 | 120 | my $o = shift; | 
| 26 | 44 |  |  |  |  | 114 | eval { require Parse::RecDescent }; | 
|  | 44 |  |  |  |  | 431 |  | 
| 27 | 44 | 50 |  |  |  | 232 | croak < | 
| 28 |  |  |  |  |  |  | This invocation of Inline requires the Parse::RecDescent module. | 
| 29 |  |  |  |  |  |  | $@ | 
| 30 |  |  |  |  |  |  | END | 
| 31 | 43 |  |  | 43 |  | 281 | no warnings qw/ once /;    ## no critic (warnings) | 
|  | 43 |  |  |  |  | 99 |  | 
|  | 43 |  |  |  |  | 4131 |  | 
| 32 | 44 |  |  |  |  | 110 | $::RD_HINT = 1;    # Turns on Parse::RecDescent's warnings/diagnostics. | 
| 33 | 44 |  |  |  |  | 183 | my $parser = Parse::RecDescent->new(grammar()); | 
| 34 | 44 |  |  |  |  | 19448952 | $parser->{data}{typeconv} = $o->{ILSM}{typeconv}; | 
| 35 | 44 |  |  |  |  | 199 | $parser->{ILSM} = $o->{ILSM};    # give parser access to config options | 
| 36 | 44 |  |  |  |  | 387 | return $parser; | 
| 37 |  |  |  |  |  |  | } | 
| 38 |  |  |  |  |  |  |  | 
| 39 | 43 |  |  | 43 |  | 388 | use vars qw($TYPEMAP_KIND $fixkey); | 
|  | 43 |  |  |  |  | 89 |  | 
|  | 43 |  |  |  |  | 6293 |  | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | # Parse::RecDescent 1.90 and later have an incompatible change | 
| 42 |  |  |  |  |  |  | # 'The key of an %item entry for a repeated subrule now includes | 
| 43 |  |  |  |  |  |  | # the repetition specifier.' | 
| 44 |  |  |  |  |  |  | # Hence various hash keys may or may not need trailing '(s?)' depending on | 
| 45 |  |  |  |  |  |  | # the version of Parse::RecDescent we are using. | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | require Parse::RecDescent; | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | # Deal with Parse::RecDescent's version numbers for development | 
| 50 |  |  |  |  |  |  | # releases (eg, '1.96_000') resulting in a warning about non-numeric in > | 
| 51 |  |  |  |  |  |  | # comparison. | 
| 52 |  |  |  |  |  |  | {    # Lexical scope. | 
| 53 |  |  |  |  |  |  | # Eval away the underscore.  "1.96_000" => "1.96000". | 
| 54 |  |  |  |  |  |  | # Use that "stable release" version number as the basis for our numeric | 
| 55 |  |  |  |  |  |  | # comparison. | 
| 56 |  |  |  |  |  |  | my $stable_version = eval $Parse::RecDescent::VERSION;    ## no critic (eval) | 
| 57 |  |  |  |  |  |  | $fixkey = ($stable_version > 1.89) | 
| 58 |  |  |  |  |  |  | ? sub{ $_[0] } : sub{ local $_=shift; s/\(.*\)$//; $_ }; | 
| 59 |  |  |  |  |  |  | }    # End lexical scope. | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  | #============================================================================ | 
| 63 |  |  |  |  |  |  | # Regular expressions to match code blocks, numbers, strings, parenthesized | 
| 64 |  |  |  |  |  |  | # expressions, function calls, and macros. The more complex regexes are only | 
| 65 |  |  |  |  |  |  | # implemented in 5.6.0 and above, so they're in eval-blocks. | 
| 66 |  |  |  |  |  |  | # | 
| 67 |  |  |  |  |  |  | # These are all adapted from the output of Damian Conway's excellent | 
| 68 |  |  |  |  |  |  | # Regexp::Common module. In future, Inline::CPP may depend directly on it, | 
| 69 |  |  |  |  |  |  | # but for now I'll just duplicate the code. | 
| 70 | 43 |  |  | 43 |  | 285 | use vars qw( $code_block $string $number $parens $funccall ); | 
|  | 43 |  |  |  |  | 89 |  | 
|  | 43 |  |  |  |  | 36412 |  | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | #============================================================================ | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | # $RE{balanced}{-parens=>q|{}()[]"'|} | 
| 75 |  |  |  |  |  |  | eval <<'END';    ## no critic (eval) | 
| 76 |  |  |  |  |  |  | $code_block = qr'(?-xism:(?-xism:(?:[{](?:(?>[^][)(}{]+)|(??{$Inline::CPP::Parser::RecDescent::code_block}))*[}]))|(?-xism:(?-xism:(?:[(](?:(?>[^][)(}{]+)|(??{$Inline::CPP::Parser::RecDescent::code_block}))*[)]))|(?-xism:(?-xism:(?:[[](?:(?>[^][)(}{]+)|(??{$Inline::CPP::Parser::RecDescent::code_block}))*[]]))|(?-xism:(?!)))))'; | 
| 77 |  |  |  |  |  |  | END | 
| 78 |  |  |  |  |  |  | $code_block = qr'{[^}]*}' if $@;    # For the stragglers: here's a lame regexp. | 
| 79 |  |  |  |  |  |  |  | 
| 80 |  |  |  |  |  |  | # $RE{balanced}{-parens=>q|()"'|} | 
| 81 |  |  |  |  |  |  | eval <<'END';                       ## no critic (eval) | 
| 82 |  |  |  |  |  |  | $parens = qr'(?-xism:(?-xism:(?:[(](?:(?>[^)(]+)|(??{$Inline::CPP::Parser::RecDescent::parens}))*[)]))|(?-xism:(?!)))'; | 
| 83 |  |  |  |  |  |  | END | 
| 84 |  |  |  |  |  |  | $parens = qr'\([^)]*\)' if $@;      # For the stragglers: here's another | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | # $RE{quoted} | 
| 87 |  |  |  |  |  |  | $string | 
| 88 |  |  |  |  |  |  | = qr'(?:(?:\")(?:[^\\\"]*(?:\\.[^\\\"]*)*)(?:\")|(?:\')(?:[^\\\']*(?:\\.[^\\\']*)*)(?:\')|(?:\`)(?:[^\\\`]*(?:\\.[^\\\`]*)*)(?:\`))'; | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | # $RE{num}{real}|$RE{num}{real}{-base=>16}|$RE{num}{int} | 
| 91 |  |  |  |  |  |  | $number | 
| 92 |  |  |  |  |  |  | = qr'(?:(?i)(?:[+-]?)(?:(?=[0123456789]|[.])(?:[0123456789]*)(?:(?:[.])(?:[0123456789]{0,}))?)(?:(?:[E])(?:(?:[+-]?)(?:[0123456789]+))|))|(?:(?i)(?:[+-]?)(?:(?=[0123456789ABCDEF]|[.])(?:[0123456789ABCDEF]*)(?:(?:[.])(?:[0123456789ABCDEF]{0,}))?)(?:(?:[G])(?:(?:[+-]?)(?:[0123456789ABCDEF]+))|))|(?:(?:[+-]?)(?:\d+))'; | 
| 93 |  |  |  |  |  |  | $funccall | 
| 94 |  |  |  |  |  |  | = qr/(?:[_a-zA-Z][_a-zA-Z0-9]*::)*[_a-zA-Z][_a-zA-Z0-9]*(?:$Inline::CPP::Parser::RecDescent::parens)?/; | 
| 95 |  |  |  |  |  |  |  | 
| 96 |  |  |  |  |  |  | #============================================================================ | 
| 97 |  |  |  |  |  |  | # Inline::CPP's grammar | 
| 98 |  |  |  |  |  |  | #============================================================================ | 
| 99 |  |  |  |  |  |  | sub grammar { | 
| 100 | 44 |  |  | 44 | 0 | 500 | return <<'END'; | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | { use Data::Dumper; } | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | { | 
| 105 |  |  |  |  |  |  | sub fixkey { &$Inline::CPP::Parser::RecDescent::fixkey } | 
| 106 |  |  |  |  |  |  | } | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | { | 
| 109 |  |  |  |  |  |  | sub handle_args { | 
| 110 |  |  |  |  |  |  | my ($args) = @_; | 
| 111 |  |  |  |  |  |  | my %argsdef; | 
| 112 |  |  |  |  |  |  | $argsdef{arg_names} = [ map $_->{name}, @$args ]; | 
| 113 |  |  |  |  |  |  | $argsdef{arg_types} = [ map $_->{type}, @$args ]; | 
| 114 |  |  |  |  |  |  | $argsdef{arg_offsets} = [ map $_->{offset}, @$args ]; | 
| 115 |  |  |  |  |  |  | $argsdef{arg_optional} = [ map $_->{optional}, @$args ]; | 
| 116 |  |  |  |  |  |  | \%argsdef; | 
| 117 |  |  |  |  |  |  | } | 
| 118 |  |  |  |  |  |  | sub handle_class_def { | 
| 119 |  |  |  |  |  |  | my ($thisparser, $def) = @_; | 
| 120 |  |  |  |  |  |  | #         print "Found a class: $def->[0]\n"; | 
| 121 |  |  |  |  |  |  | my $class = $def->[0]; | 
| 122 |  |  |  |  |  |  | my @parts; | 
| 123 |  |  |  |  |  |  | for my $part (@{$def->[1]}) { push @parts, @$_ for @$part } | 
| 124 |  |  |  |  |  |  | push @{$thisparser->{data}{classes}}, $class | 
| 125 |  |  |  |  |  |  | unless defined $thisparser->{data}{class}{$class}; | 
| 126 |  |  |  |  |  |  | $thisparser->{data}{class}{$class} = \@parts; | 
| 127 |  |  |  |  |  |  | #   print "Class $class:\n", Dumper \@parts; | 
| 128 |  |  |  |  |  |  | Inline::CPP::Parser::RecDescent::typemap($thisparser, $class); | 
| 129 |  |  |  |  |  |  | [$class, \@parts]; | 
| 130 |  |  |  |  |  |  | } | 
| 131 |  |  |  |  |  |  | sub handle_typedef { | 
| 132 |  |  |  |  |  |  | my ($thisparser, $t) = @_; | 
| 133 |  |  |  |  |  |  | my ($name, $type) = @{$t}{qw(name type)}; | 
| 134 |  |  |  |  |  |  | #   print "found a typedef: $name => $type\n"; | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | # XXX: this doesn't handle non-class typedefs that we could handle, | 
| 137 |  |  |  |  |  |  | # e.g. "typedef int my_int_t" | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | if ($thisparser->{data}{class}{$type} | 
| 140 |  |  |  |  |  |  | && !exists($thisparser->{data}{class}{$name})) { | 
| 141 |  |  |  |  |  |  | push @{$thisparser->{data}{classes}}, $name; | 
| 142 |  |  |  |  |  |  | $thisparser->{data}{class}{$name} = $thisparser->{data}{class}{$type}; | 
| 143 |  |  |  |  |  |  | Inline::CPP::Parser::RecDescent::typemap($thisparser, $name); | 
| 144 |  |  |  |  |  |  | } | 
| 145 |  |  |  |  |  |  | $t; | 
| 146 |  |  |  |  |  |  | } | 
| 147 |  |  |  |  |  |  | sub handle_enum { | 
| 148 |  |  |  |  |  |  | my ($thisparser, $t) = @_; | 
| 149 |  |  |  |  |  |  | $t; | 
| 150 |  |  |  |  |  |  | } | 
| 151 |  |  |  |  |  |  | } | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | code: part(s) {1} | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  | part: comment | 
| 156 |  |  |  |  |  |  | | typedef | 
| 157 |  |  |  |  |  |  | { | 
| 158 |  |  |  |  |  |  | handle_typedef($thisparser, $item[1]); | 
| 159 |  |  |  |  |  |  | 1; | 
| 160 |  |  |  |  |  |  | } | 
| 161 |  |  |  |  |  |  | | enum | 
| 162 |  |  |  |  |  |  | { | 
| 163 |  |  |  |  |  |  | my $t = handle_enum($thisparser, $item[1]); | 
| 164 |  |  |  |  |  |  | push @{$thisparser->{data}{enums}}, $t; | 
| 165 |  |  |  |  |  |  | 1; | 
| 166 |  |  |  |  |  |  | } | 
| 167 |  |  |  |  |  |  | | class_def | 
| 168 |  |  |  |  |  |  | { | 
| 169 |  |  |  |  |  |  | handle_class_def($thisparser, $item[1]); | 
| 170 |  |  |  |  |  |  | 1; | 
| 171 |  |  |  |  |  |  | } | 
| 172 |  |  |  |  |  |  | | function_def | 
| 173 |  |  |  |  |  |  | { | 
| 174 |  |  |  |  |  |  | #         print "found a function: $item[1]->{name}\n"; | 
| 175 |  |  |  |  |  |  | my $name = $item[1]->{name}; | 
| 176 |  |  |  |  |  |  | my $i=0; | 
| 177 |  |  |  |  |  |  | for my $arg (@{$item[1]->{args}}) { | 
| 178 |  |  |  |  |  |  | $arg->{name} = 'dummy' . ++$i unless defined $arg->{name}; | 
| 179 |  |  |  |  |  |  | } | 
| 180 |  |  |  |  |  |  | Inline::CPP::Parser::RecDescent::strip_ellipsis($thisparser, | 
| 181 |  |  |  |  |  |  | $item[1]->{args}); | 
| 182 |  |  |  |  |  |  | push @{$thisparser->{data}{functions}}, $name | 
| 183 |  |  |  |  |  |  | unless defined $thisparser->{data}{function}{$name}; | 
| 184 |  |  |  |  |  |  | my %funcdef = %{ $item[1] }; | 
| 185 |  |  |  |  |  |  | %funcdef = (%funcdef, %{ handle_args(delete $funcdef{args}) }); | 
| 186 |  |  |  |  |  |  | $thisparser->{data}{function}{$name} = \%funcdef; | 
| 187 |  |  |  |  |  |  | #    print Dumper $item[1]; | 
| 188 |  |  |  |  |  |  | 1; | 
| 189 |  |  |  |  |  |  | } | 
| 190 |  |  |  |  |  |  | | all | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | typedef: 'typedef' class IDENTIFIER(?) '{'  class_part(s?) '}' IDENTIFIER ';' | 
| 193 |  |  |  |  |  |  | { | 
| 194 |  |  |  |  |  |  | my ($class, $parts); | 
| 195 |  |  |  |  |  |  | $class = $item[3][0] || 'anon_class'.($thisparser->{data}{anonclass}++); | 
| 196 |  |  |  |  |  |  | ($class, $parts)= handle_class_def($thisparser, [$class, $item{fixkey('class_part(s?)')}]); | 
| 197 |  |  |  |  |  |  | { thing => 'typedef', name => $item[8], type => $class, body => $parts } | 
| 198 |  |  |  |  |  |  | } | 
| 199 |  |  |  |  |  |  | | 'typedef' IDENTIFIER IDENTIFIER ';' | 
| 200 |  |  |  |  |  |  | { { thing => 'typedef', name => $item[3], type => $item[2] } } | 
| 201 |  |  |  |  |  |  | | 'typedef' /[^;]*/ ';' | 
| 202 |  |  |  |  |  |  | { | 
| 203 |  |  |  |  |  |  | #         dprint "Typedef $item{__DIRECTIVE1__} is too heinous\n"; | 
| 204 |  |  |  |  |  |  | { thing => 'comment'} | 
| 205 |  |  |  |  |  |  | } | 
| 206 |  |  |  |  |  |  |  | 
| 207 |  |  |  |  |  |  | enum: 'enum' IDENTIFIER(?) '{'  '}' ';' | 
| 208 |  |  |  |  |  |  | { | 
| 209 |  |  |  |  |  |  | { thing => 'enum', name => $item{fixkey('IDENTIFIER(?)')}[0], | 
| 210 |  |  |  |  |  |  | body => $item{__DIRECTIVE1__} } | 
| 211 |  |  |  |  |  |  | } | 
| 212 |  |  |  |  |  |  |  | 
| 213 |  |  |  |  |  |  | enum_item: IDENTIFIER '='  /[0-9]+/ | 
| 214 |  |  |  |  |  |  | { [$item{IDENTIFIER}, $item{__PATTERN1__}] } | 
| 215 |  |  |  |  |  |  | | IDENTIFIER | 
| 216 |  |  |  |  |  |  | { [$item{IDENTIFIER}, undef] } | 
| 217 |  |  |  |  |  |  |  | 
| 218 |  |  |  |  |  |  | class_def: class IDENTIFIER '{'  class_part(s?) '}' ';' | 
| 219 |  |  |  |  |  |  | { | 
| 220 |  |  |  |  |  |  | [@item{'IDENTIFIER',fixkey('class_part(s?)')}] | 
| 221 |  |  |  |  |  |  | } | 
| 222 |  |  |  |  |  |  | | class IDENTIFIER ':' | 
| 223 |  |  |  |  |  |  | '{' class_part(s?) '}' ';' | 
| 224 |  |  |  |  |  |  | { | 
| 225 |  |  |  |  |  |  | push @{$item{fixkey('class_part(s?)')}}, [$item{__DIRECTIVE2__}]; | 
| 226 |  |  |  |  |  |  | [@item{'IDENTIFIER',fixkey('class_part(s?)')}] | 
| 227 |  |  |  |  |  |  | } | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  | inherit: scope IDENTIFIER | 
| 230 |  |  |  |  |  |  | { {thing => 'inherits', name => $item[2], scope => $item[1]} } | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | class_part: comment { [ {thing => 'comment'} ] } | 
| 233 |  |  |  |  |  |  | | scope ':'  class_decl(s?) | 
| 234 |  |  |  |  |  |  | { | 
| 235 |  |  |  |  |  |  | for my $part (@{$item{fixkey('class_decl(s?)')}}) { | 
| 236 |  |  |  |  |  |  | $_->{scope} = $item[1] for @$part; | 
| 237 |  |  |  |  |  |  | } | 
| 238 |  |  |  |  |  |  | $item{fixkey('class_decl(s?)')} | 
| 239 |  |  |  |  |  |  | } | 
| 240 |  |  |  |  |  |  | | class_decl(s) | 
| 241 |  |  |  |  |  |  | { | 
| 242 |  |  |  |  |  |  | for my $part (@{$item[1]}) { | 
| 243 |  |  |  |  |  |  | $_->{scope} = $thisparser->{data}{defaultscope} | 
| 244 |  |  |  |  |  |  | for @$part; | 
| 245 |  |  |  |  |  |  | } | 
| 246 |  |  |  |  |  |  | $item[1] | 
| 247 |  |  |  |  |  |  | } | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | class_decl: comment { [{thing => 'comment'}] } | 
| 250 |  |  |  |  |  |  | | typedef { [ handle_typedef($thisparser, $item[1]) ] } | 
| 251 |  |  |  |  |  |  | | enum { [ handle_enum($thisparser, $item[1]) ] } | 
| 252 |  |  |  |  |  |  | | class_def | 
| 253 |  |  |  |  |  |  | { | 
| 254 |  |  |  |  |  |  | my ($class, $parts) = handle_class_def($thisparser, $item[1]); | 
| 255 |  |  |  |  |  |  | [{ thing => 'class', name => $class, body => $parts }]; | 
| 256 |  |  |  |  |  |  | } | 
| 257 |  |  |  |  |  |  | | method_def | 
| 258 |  |  |  |  |  |  | { | 
| 259 |  |  |  |  |  |  | $item[1]->{thing} = 'method'; | 
| 260 |  |  |  |  |  |  | #         print "class_decl found a method: $item[1]->{name}\n"; | 
| 261 |  |  |  |  |  |  | my $i=0; | 
| 262 |  |  |  |  |  |  | for my $arg (@{$item[1]->{args}}) { | 
| 263 |  |  |  |  |  |  | $arg->{name} = 'dummy' . ++$i unless defined $arg->{name}; | 
| 264 |  |  |  |  |  |  | } | 
| 265 |  |  |  |  |  |  | Inline::CPP::Parser::RecDescent::strip_ellipsis($thisparser, | 
| 266 |  |  |  |  |  |  | $item[1]->{args}); | 
| 267 |  |  |  |  |  |  | my %funcdef = %{ $item[1] }; | 
| 268 |  |  |  |  |  |  | %funcdef = (%funcdef, %{ handle_args(delete $funcdef{args}) }); | 
| 269 |  |  |  |  |  |  | [\%funcdef]; | 
| 270 |  |  |  |  |  |  | } | 
| 271 |  |  |  |  |  |  | | member_def | 
| 272 |  |  |  |  |  |  | { | 
| 273 |  |  |  |  |  |  | #         print "class_decl found one or more members:\n", Dumper(\@item); | 
| 274 |  |  |  |  |  |  | $_->{thing} = 'member' for @{$item[1]}; | 
| 275 |  |  |  |  |  |  | $item[1]; | 
| 276 |  |  |  |  |  |  | } | 
| 277 |  |  |  |  |  |  |  | 
| 278 |  |  |  |  |  |  | function_def: operator  ';' | 
| 279 |  |  |  |  |  |  | { | 
| 280 |  |  |  |  |  |  | $item[1] | 
| 281 |  |  |  |  |  |  | } | 
| 282 |  |  |  |  |  |  | | operator  smod(?) code_block | 
| 283 |  |  |  |  |  |  | { | 
| 284 |  |  |  |  |  |  | $item[1] | 
| 285 |  |  |  |  |  |  | } | 
| 286 |  |  |  |  |  |  | | IDENTIFIER '('  (s?) ')' smod(?) code_block | 
| 287 |  |  |  |  |  |  | { | 
| 288 |  |  |  |  |  |  | {name => $item{IDENTIFIER}, args => $item{__DIRECTIVE2__}, return_type => '' } | 
| 289 |  |  |  |  |  |  | } | 
| 290 |  |  |  |  |  |  | | return_type IDENTIFIER '(' (s?) ')' ';' | 
| 291 |  |  |  |  |  |  | { | 
| 292 |  |  |  |  |  |  | {return_type => $item[1], name => $item[2], args => $item{__DIRECTIVE1__} } | 
| 293 |  |  |  |  |  |  | } | 
| 294 |  |  |  |  |  |  | | return_type IDENTIFIER '(' (s?) ')' smod(?) code_block | 
| 295 |  |  |  |  |  |  | { | 
| 296 |  |  |  |  |  |  | {return_type => $item{return_type}, name => $item[2], args => $item{__DIRECTIVE1__} } | 
| 297 |  |  |  |  |  |  | } | 
| 298 |  |  |  |  |  |  |  | 
| 299 |  |  |  |  |  |  | method_def: operator  method_imp | 
| 300 |  |  |  |  |  |  | { | 
| 301 |  |  |  |  |  |  | #               print "method operator:\n", Dumper $item[1]; | 
| 302 |  |  |  |  |  |  | $item[1]; | 
| 303 |  |  |  |  |  |  | } | 
| 304 |  |  |  |  |  |  |  | 
| 305 |  |  |  |  |  |  | | IDENTIFIER '('  (s?) ')' method_imp | 
| 306 |  |  |  |  |  |  | { | 
| 307 |  |  |  |  |  |  | #         print "con-/de-structor found: $item[1]\n"; | 
| 308 |  |  |  |  |  |  | {name => $item[1], args => $item{__DIRECTIVE2__}, abstract => ${$item{method_imp}} }; | 
| 309 |  |  |  |  |  |  | } | 
| 310 |  |  |  |  |  |  | | return_type IDENTIFIER '(' (s?) ')' method_imp | 
| 311 |  |  |  |  |  |  | { | 
| 312 |  |  |  |  |  |  | #         print "method found: $item[2]\n"; | 
| 313 |  |  |  |  |  |  | $return = | 
| 314 |  |  |  |  |  |  | {name => $item[2], return_type => $item[1], args => $item[4], | 
| 315 |  |  |  |  |  |  | abstract => ${$item[6]}, | 
| 316 |  |  |  |  |  |  | rconst => $thisparser->{data}{smod}{const}, | 
| 317 |  |  |  |  |  |  | }; | 
| 318 |  |  |  |  |  |  | $thisparser->{data}{smod}{const} = 0; | 
| 319 |  |  |  |  |  |  | } | 
| 320 |  |  |  |  |  |  |  | 
| 321 |  |  |  |  |  |  | operator: return_type(?) 'operator' /\(\)|[^()]+/ '(' (s?) ')' | 
| 322 |  |  |  |  |  |  | { | 
| 323 |  |  |  |  |  |  | #            print "Found operator: $item[1][0] operator $item[3]\n"; | 
| 324 |  |  |  |  |  |  | {name=> "operator $item[3]", args => $item[5], ret => $item[1][0]} | 
| 325 |  |  |  |  |  |  | } | 
| 326 |  |  |  |  |  |  |  | 
| 327 |  |  |  |  |  |  | # By adding smod, we allow 'const' member functions. This would also bind to | 
| 328 |  |  |  |  |  |  | # incorrect C++ with the word 'static' after the argument list, but we don't | 
| 329 |  |  |  |  |  |  | # care at all because such code would never be compiled successfully. | 
| 330 |  |  |  |  |  |  |  | 
| 331 |  |  |  |  |  |  | # By adding init, we allow constructors to initialize references. Again, we'll | 
| 332 |  |  |  |  |  |  | # allow them anywhere, but our goal is not to enforce c++ standards -- that's | 
| 333 |  |  |  |  |  |  | # the compiler's job. | 
| 334 |  |  |  |  |  |  | method_imp: smod(?) ';' { \0 } | 
| 335 |  |  |  |  |  |  | | smod(?) '='  '0' ';' { \1 } | 
| 336 |  |  |  |  |  |  | | smod(?) initlist(?) code_block { \0 } | 
| 337 |  |  |  |  |  |  | | smod(?) '=' '0' code_block { \0 } | 
| 338 |  |  |  |  |  |  |  | 
| 339 |  |  |  |  |  |  | initlist: ':' | 
| 340 |  |  |  |  |  |  |  | 
| 341 |  |  |  |  |  |  | member_def: anytype  ';' | 
| 342 |  |  |  |  |  |  | { | 
| 343 |  |  |  |  |  |  | my @retval; | 
| 344 |  |  |  |  |  |  | for my $def (@{$item[2]}) { | 
| 345 |  |  |  |  |  |  | my $type = join '', $item[1], @{$def->[0]}; | 
| 346 |  |  |  |  |  |  | my $name = $def->[1]; | 
| 347 |  |  |  |  |  |  | #             print "member found: type=$type, name=$name\n"; | 
| 348 |  |  |  |  |  |  | push @retval, { name => $name, type => $type }; | 
| 349 |  |  |  |  |  |  | } | 
| 350 |  |  |  |  |  |  | \@retval; | 
| 351 |  |  |  |  |  |  | } | 
| 352 |  |  |  |  |  |  |  | 
| 353 |  |  |  |  |  |  | var: star(s?) IDENTIFIER '=' expr { [@item[1,2]] } | 
| 354 |  |  |  |  |  |  | | star(s?) IDENTIFIER '[' expr ']' { [@item[1,2]] } | 
| 355 |  |  |  |  |  |  | | star(s?) IDENTIFIER          { [@item[1,2]] } | 
| 356 |  |  |  |  |  |  |  | 
| 357 |  |  |  |  |  |  | arg: type IDENTIFIER '=' expr | 
| 358 |  |  |  |  |  |  | { | 
| 359 |  |  |  |  |  |  | #       print "argument $item{IDENTIFIER} found\n"; | 
| 360 |  |  |  |  |  |  | #       print "expression: $item{expr}\n"; | 
| 361 |  |  |  |  |  |  | {type => $item[1], name => $item{IDENTIFIER}, optional => 1, | 
| 362 |  |  |  |  |  |  | offset => $thisoffset} | 
| 363 |  |  |  |  |  |  | } | 
| 364 |  |  |  |  |  |  | | type IDENTIFIER | 
| 365 |  |  |  |  |  |  | { | 
| 366 |  |  |  |  |  |  | #       print "argument $item{IDENTIFIER} found\n"; | 
| 367 |  |  |  |  |  |  | {type => $item[1], name => $item{IDENTIFIER}, offset => $thisoffset} | 
| 368 |  |  |  |  |  |  | } | 
| 369 |  |  |  |  |  |  | | type { {type => $item[1]} } | 
| 370 |  |  |  |  |  |  | | '...' | 
| 371 |  |  |  |  |  |  | { {name => '...', type => '...', offset => $thisoffset} } | 
| 372 |  |  |  |  |  |  |  | 
| 373 |  |  |  |  |  |  | ident_part: /[~_a-z]\w*/i '<'  (s?) '>' | 
| 374 |  |  |  |  |  |  | { | 
| 375 |  |  |  |  |  |  | $item[1].'<'.join('', @{$item[4]}).'>' | 
| 376 |  |  |  |  |  |  | } | 
| 377 |  |  |  |  |  |  |  | 
| 378 |  |  |  |  |  |  | | /[~_a-z]\w*/i | 
| 379 |  |  |  |  |  |  | { | 
| 380 |  |  |  |  |  |  | $item[1] | 
| 381 |  |  |  |  |  |  | } | 
| 382 |  |  |  |  |  |  |  | 
| 383 |  |  |  |  |  |  | IDENTIFIER: | 
| 384 |  |  |  |  |  |  | { | 
| 385 |  |  |  |  |  |  | my $x = join '::', @{$item[1]}; | 
| 386 |  |  |  |  |  |  | #              print "IDENTIFIER: $x\n"; | 
| 387 |  |  |  |  |  |  | $x | 
| 388 |  |  |  |  |  |  | } | 
| 389 |  |  |  |  |  |  |  | 
| 390 |  |  |  |  |  |  | # Parse::RecDescent is retarded in this one case: if a subrule fails, it | 
| 391 |  |  |  |  |  |  | # gives up the entire rule. This is a stupid way to get around that. | 
| 392 |  |  |  |  |  |  | return_type: rtype2 | rtype1 | 
| 393 |  |  |  |  |  |  | rtype1: TYPE star(s?) | 
| 394 |  |  |  |  |  |  | { | 
| 395 |  |  |  |  |  |  | $return = $item[1]; | 
| 396 |  |  |  |  |  |  | $return .= join '',' ',@{$item[2]} if @{$item[2]}; | 
| 397 |  |  |  |  |  |  | #    print "rtype1: $return\n"; | 
| 398 |  |  |  |  |  |  | #          return undef | 
| 399 |  |  |  |  |  |  | #            unless(defined$thisparser->{data}{typeconv}{valid_rtypes}{$return}); | 
| 400 |  |  |  |  |  |  | } | 
| 401 |  |  |  |  |  |  | rtype2: modifier(s) TYPE star(s?) | 
| 402 |  |  |  |  |  |  | { | 
| 403 |  |  |  |  |  |  | $return = $item[2]; | 
| 404 |  |  |  |  |  |  | $return = join ' ',grep{$_}@{$item[1]},$return | 
| 405 |  |  |  |  |  |  | if @{$item[1]}; | 
| 406 |  |  |  |  |  |  | $return .= join '',' ',@{$item[3]} if @{$item[3]}; | 
| 407 |  |  |  |  |  |  | #    print "rtype2: $return\n"; | 
| 408 |  |  |  |  |  |  | #          return undef | 
| 409 |  |  |  |  |  |  | #            unless(defined$thisparser->{data}{typeconv}{valid_rtypes}{$return}); | 
| 410 |  |  |  |  |  |  | $return = 'static ' . $return | 
| 411 |  |  |  |  |  |  | if $thisparser->{data}{smod}{static}; | 
| 412 |  |  |  |  |  |  | $thisparser->{data}{smod}{static} = 0; | 
| 413 |  |  |  |  |  |  | } | 
| 414 |  |  |  |  |  |  |  | 
| 415 |  |  |  |  |  |  | type: type2 | type1 | 
| 416 |  |  |  |  |  |  | type1: TYPE star(s?) | 
| 417 |  |  |  |  |  |  | { | 
| 418 |  |  |  |  |  |  | $return = $item[1]; | 
| 419 |  |  |  |  |  |  | $return .= join '',' ',@{$item{fixkey('star(s?)')}} if @{$item{fixkey('star(s?)')}}; | 
| 420 |  |  |  |  |  |  | #    print "type1: $return\n"; | 
| 421 |  |  |  |  |  |  | #          return undef | 
| 422 |  |  |  |  |  |  | #            unless(defined$thisparser->{data}{typeconv}{valid_types}{$return}); | 
| 423 |  |  |  |  |  |  | } | 
| 424 |  |  |  |  |  |  | type2: modifier(s) TYPE star(s?) | 
| 425 |  |  |  |  |  |  | { | 
| 426 |  |  |  |  |  |  | $return = $item{TYPE}; | 
| 427 |  |  |  |  |  |  | $return = join ' ',grep{$_}@{$item[1]},$return if @{$item[1]}; | 
| 428 |  |  |  |  |  |  | $return .= join '',' ',@{$item{fixkey('star(s?)')}} if @{$item{fixkey('star(s?)')}}; | 
| 429 |  |  |  |  |  |  | #    print "type2: $return\n"; | 
| 430 |  |  |  |  |  |  | #          return undef | 
| 431 |  |  |  |  |  |  | #            unless(defined$thisparser->{data}{typeconv}{valid_types}{$return}); | 
| 432 |  |  |  |  |  |  | } | 
| 433 |  |  |  |  |  |  |  | 
| 434 |  |  |  |  |  |  | anytype: anytype2 | anytype1 | 
| 435 |  |  |  |  |  |  | anytype1: TYPE star(s?) | 
| 436 |  |  |  |  |  |  | { | 
| 437 |  |  |  |  |  |  | $return = $item[1]; | 
| 438 |  |  |  |  |  |  | $return .= join '',' ',@{$item[2]} if @{$item[2]}; | 
| 439 |  |  |  |  |  |  | } | 
| 440 |  |  |  |  |  |  | anytype2: modifier(s) TYPE star(s?) | 
| 441 |  |  |  |  |  |  | { | 
| 442 |  |  |  |  |  |  | $return = $item[2]; | 
| 443 |  |  |  |  |  |  | $return = join ' ',grep{$_}@{$item[1]},$return if @{$item[1]}; | 
| 444 |  |  |  |  |  |  | $return .= join '',' ',@{$item[3]} if @{$item[3]}; | 
| 445 |  |  |  |  |  |  | } | 
| 446 |  |  |  |  |  |  |  | 
| 447 |  |  |  |  |  |  | comment: m{\s* // [^\n]* \n }x | 
| 448 |  |  |  |  |  |  | | m{\s* /\* (?:[^*]+|\*(?!/))* \*/  ([ \t]*)? }x | 
| 449 |  |  |  |  |  |  |  | 
| 450 |  |  |  |  |  |  | # long and short aren't recognized as modifiers because they break when used | 
| 451 |  |  |  |  |  |  | # as regular types. Another Parse::RecDescent problem is greedy matching; I | 
| 452 |  |  |  |  |  |  | # need tmodifier to "give back" long or short in cases where keeping them would | 
| 453 |  |  |  |  |  |  | # cause the modifier rule to fail. One side-effect is 'long long' can never | 
| 454 |  |  |  |  |  |  | # be parsed correctly here. | 
| 455 |  |  |  |  |  |  | modifier: tmod | 
| 456 |  |  |  |  |  |  | | smod { ++$thisparser->{data}{smod}{$item[1]}; ''} | 
| 457 |  |  |  |  |  |  | | nmod { '' } | 
| 458 |  |  |  |  |  |  | tmod: 'unsigned' # | 'long' | 'short' | 
| 459 |  |  |  |  |  |  | smod: 'const' | 'static' | 
| 460 |  |  |  |  |  |  | nmod: 'extern' | 'virtual' | 'mutable' | 'volatile' | 'inline' | 
| 461 |  |  |  |  |  |  |  | 
| 462 |  |  |  |  |  |  | scope: 'public' | 'private' | 'protected' | 
| 463 |  |  |  |  |  |  |  | 
| 464 |  |  |  |  |  |  | class: 'class' { $thisparser->{data}{defaultscope} = 'private'; $item[1] } | 
| 465 |  |  |  |  |  |  | | 'struct' { $thisparser->{data}{defaultscope} = 'public'; $item[1] } | 
| 466 |  |  |  |  |  |  |  | 
| 467 |  |  |  |  |  |  | star: '*' | '&' | 
| 468 |  |  |  |  |  |  |  | 
| 469 |  |  |  |  |  |  | code_block: /$Inline::CPP::Parser::RecDescent::code_block/ | 
| 470 |  |  |  |  |  |  |  | 
| 471 |  |  |  |  |  |  | # Consume expressions | 
| 472 |  |  |  |  |  |  | expr:  { | 
| 473 |  |  |  |  |  |  | my $o = join '', @{$item[1]}; | 
| 474 |  |  |  |  |  |  | #   print "expr: $o\n"; | 
| 475 |  |  |  |  |  |  | $o; | 
| 476 |  |  |  |  |  |  | } | 
| 477 |  |  |  |  |  |  | subexpr: /$Inline::CPP::Parser::RecDescent::funccall/ # Matches a macro, too | 
| 478 |  |  |  |  |  |  | | /$Inline::CPP::Parser::RecDescent::string/ | 
| 479 |  |  |  |  |  |  | | /$Inline::CPP::Parser::RecDescent::number/ | 
| 480 |  |  |  |  |  |  | | UOP subexpr | 
| 481 |  |  |  |  |  |  | OP: '+' | '-' | '*' | '/' | '^' | '&' | '|' | '%' | '||' | '&&' | 
| 482 |  |  |  |  |  |  | UOP: '~' | '!' | '-' | '*' | '&' | 
| 483 |  |  |  |  |  |  |  | 
| 484 |  |  |  |  |  |  | TYPE: IDENTIFIER | 
| 485 |  |  |  |  |  |  |  | 
| 486 |  |  |  |  |  |  | all: /.*/ | 
| 487 |  |  |  |  |  |  |  | 
| 488 |  |  |  |  |  |  | END | 
| 489 |  |  |  |  |  |  | } | 
| 490 |  |  |  |  |  |  |  | 
| 491 |  |  |  |  |  |  | #============================================================================ | 
| 492 |  |  |  |  |  |  | # Generate typemap code for the classes and structs we bind to. This allows | 
| 493 |  |  |  |  |  |  | # functions declared after a class to return or accept class objects as | 
| 494 |  |  |  |  |  |  | # parameters. | 
| 495 |  |  |  |  |  |  | #============================================================================ | 
| 496 |  |  |  |  |  |  | $TYPEMAP_KIND = 'O_Inline_CPP_Class'; | 
| 497 |  |  |  |  |  |  |  | 
| 498 |  |  |  |  |  |  | sub typemap { | 
| 499 | 49 |  |  | 49 | 0 | 4546 | my ($parser, $typename) = @_; | 
| 500 |  |  |  |  |  |  |  | 
| 501 |  |  |  |  |  |  | #    print "Inline::CPP::Parser::RecDescent::typemap(): typename=$typename\n"; | 
| 502 |  |  |  |  |  |  |  | 
| 503 | 49 |  |  |  |  | 173 | my ($TYPEMAP, $INPUT, $OUTPUT); | 
| 504 | 49 |  |  |  |  | 273 | $TYPEMAP = "$typename *\t\t$TYPEMAP_KIND\n"; | 
| 505 | 49 |  |  |  |  | 163 | $INPUT   = <<"END"; | 
| 506 |  |  |  |  |  |  | if (sv_isobject(\$arg) && (SvTYPE(SvRV(\$arg)) == SVt_PVMG)) { | 
| 507 |  |  |  |  |  |  | \$var = (\$type)SvIV((SV*)SvRV( \$arg )); | 
| 508 |  |  |  |  |  |  | } | 
| 509 |  |  |  |  |  |  | else { | 
| 510 |  |  |  |  |  |  | warn ( \\"\${Package}::\$func_name() -- \$var is not a blessed reference\\" ); | 
| 511 |  |  |  |  |  |  | XSRETURN_UNDEF; | 
| 512 |  |  |  |  |  |  | } | 
| 513 |  |  |  |  |  |  | END | 
| 514 | 49 |  |  |  |  | 136 | $OUTPUT = <<"END"; | 
| 515 |  |  |  |  |  |  | sv_setref_pv( \$arg, CLASS, (void*)\$var ); | 
| 516 |  |  |  |  |  |  | END | 
| 517 |  |  |  |  |  |  |  | 
| 518 | 49 |  |  |  |  | 173 | my $ctypename = $typename . ' *'; | 
| 519 | 49 |  | 66 |  |  | 581 | $parser->{data}{typeconv}{input_expr}{$TYPEMAP_KIND}  ||= $INPUT; | 
| 520 | 49 |  | 66 |  |  | 421 | $parser->{data}{typeconv}{output_expr}{$TYPEMAP_KIND} ||= $OUTPUT; | 
| 521 | 49 |  |  |  |  | 212 | $parser->{data}{typeconv}{type_kind}{$ctypename} = $TYPEMAP_KIND; | 
| 522 | 49 |  |  |  |  | 204 | $parser->{data}{typeconv}{valid_types}{$ctypename}++; | 
| 523 | 49 |  |  |  |  | 157 | $parser->{data}{typeconv}{valid_rtypes}{$ctypename}++; | 
| 524 | 49 |  |  |  |  | 1098 | return; | 
| 525 |  |  |  |  |  |  | } | 
| 526 |  |  |  |  |  |  |  | 
| 527 |  |  |  |  |  |  | #============================================================================ | 
| 528 |  |  |  |  |  |  | # Default action is to strip ellipses from the C++ code. This allows having | 
| 529 |  |  |  |  |  |  | # _only_ a '...' in the code, just like XS. It is the default. | 
| 530 |  |  |  |  |  |  | #============================================================================ | 
| 531 |  |  |  |  |  |  | sub strip_ellipsis { | 
| 532 | 175 |  |  | 175 | 0 | 184453 | my ($parser, $args) = @_; | 
| 533 | 175 | 50 |  |  |  | 990 | return if $parser->{ILSM}{PRESERVE_ELLIPSIS}; | 
| 534 | 175 |  |  |  |  | 833 | for (my $i = 0; $i < @$args; $i++) { | 
| 535 | 57 | 100 |  |  |  | 251 | next unless $args->[$i]{name} eq '...'; | 
| 536 |  |  |  |  |  |  |  | 
| 537 |  |  |  |  |  |  | # if it's the first one, just strip it | 
| 538 | 1 | 50 |  |  |  | 5 | if ($i == 0) { | 
| 539 | 1 |  |  |  |  | 6 | substr($parser->{ILSM}{code}, $args->[$i]{offset} - 3, 3, '   '); | 
| 540 |  |  |  |  |  |  | } | 
| 541 |  |  |  |  |  |  | else { | 
| 542 | 0 |  |  |  |  | 0 | my $prev        = $i - 1; | 
| 543 | 0 |  |  |  |  | 0 | my $prev_offset = $args->[$prev]{offset}; | 
| 544 | 0 |  |  |  |  | 0 | my $length      = $args->[$i]{offset} - $prev_offset; | 
| 545 | 0 |  |  |  |  | 0 | substr($parser->{ILSM}{code}, $prev_offset, $length) =~ s/\S/ /g; | 
| 546 |  |  |  |  |  |  | } | 
| 547 |  |  |  |  |  |  | } | 
| 548 | 175 |  |  |  |  | 3880 | return; | 
| 549 |  |  |  |  |  |  | } | 
| 550 |  |  |  |  |  |  |  | 
| 551 |  |  |  |  |  |  | my $hack = sub { # Appease -w using Inline::Files | 
| 552 |  |  |  |  |  |  | print Parse::RecDescent::IN ''; | 
| 553 |  |  |  |  |  |  | print Parse::RecDescent::IN ''; | 
| 554 |  |  |  |  |  |  | print Parse::RecDescent::TRACE_FILE ''; | 
| 555 |  |  |  |  |  |  | print Parse::RecDescent::TRACE_FILE ''; | 
| 556 |  |  |  |  |  |  | }; | 
| 557 |  |  |  |  |  |  |  | 
| 558 |  |  |  |  |  |  | 1; | 
| 559 |  |  |  |  |  |  |  | 
| 560 |  |  |  |  |  |  | =head1 Inline::CPP::Parser::RecDescent | 
| 561 |  |  |  |  |  |  |  | 
| 562 |  |  |  |  |  |  | All functions are internal.  No documentation necessary. | 
| 563 |  |  |  |  |  |  |  | 
| 564 |  |  |  |  |  |  | =cut |