File Coverage

blib/lib/Inline/C/Parser/RegExp.pm
Criterion Covered Total %
statement 71 77 92.2
branch 13 24 54.1
condition 5 9 55.5
subroutine 7 7 100.0
pod 0 3 0.0
total 96 120 80.0


line stmt bran cond sub pod time code
1 8     8   16009 use strict; use warnings;
  8     8   19  
  8         240  
  8         43  
  8         18  
  8         347  
2             package Inline::C::Parser::RegExp;
3              
4 8     8   51 use Carp;
  8         14  
  8         7618  
5              
6             sub register {
7             {
8 8     8 0 7124 extends => [qw(C)],
9             overrides => [qw(get_parser)],
10             }
11             }
12              
13             sub get_parser {
14 8 100   8 0 46 Inline::C::_parser_test($_[0]->{CONFIG}{DIRECTORY}, "Inline::C::Parser::RegExp::get_parser called\n") if $_[0]->{CONFIG}{_TESTING};
15 8         63 bless {}, 'Inline::C::Parser::RegExp'
16             }
17              
18             sub code {
19 8     8 0 23 my ($self,$code) = @_;
20              
21             # These regular expressions were derived from Regexp::Common v0.01.
22 8         20 my $RE_comment_C = q{(?:(?:\/\*)(?:(?:(?!\*\/)[\s\S])*)(?:\*\/))};
23 8         24 my $RE_comment_Cpp = q{(?:\/\*(?:(?!\*\/)[\s\S])*\*\/|\/\/[^\n]*\n)};
24 8         18 my $RE_quoted = (
25             q{(?:(?:\")(?:[^\\\"]*(?:\\.[^\\\"]*)*)(?:\")}
26             . q{|(?:\')(?:[^\\\']*(?:\\.[^\\\']*)*)(?:\'))}
27             );
28 8         19 our $RE_balanced_brackets; $RE_balanced_brackets =
29 8         76 qr'(?:[{]((?:(?>[^{}]+)|(??{$RE_balanced_brackets}))*)[}])';
30 8         13 our $RE_balanced_parens; $RE_balanced_parens =
31 8         50 qr'(?:[(]((?:(?>[^()]+)|(??{$RE_balanced_parens}))*)[)])';
32              
33             # First, we crush out anything potentially confusing.
34             # The order of these _does_ matter.
35 8         185 $code =~ s/$RE_comment_C/ /go;
36 8         204 $code =~ s/$RE_comment_Cpp/ /go;
37 8         25 $code =~ s/^\#.*(\\\n.*)*//mgo;
38             #$code =~ s/$RE_quoted/\"\"/go; # Buggy, if included.
39 8         69 $code =~ s/$RE_balanced_brackets/{ }/go;
40              
41 8         24 $self->{_the_code_most_recently_parsed} = $code; # Simplifies debugging.
42              
43             my $normalize_type = sub {
44             # Normalize a type for lookup in a typemap.
45 66     66   93 my($type) = @_;
46              
47             # Remove "extern".
48             # But keep "static", "inline", "typedef", etc,
49             # to cause desirable typemap misses.
50 66         82 $type =~ s/\bextern\b//g;
51              
52             # Whitespace: only single spaces, none leading or trailing.
53 66         165 $type =~ s/\s+/ /g;
54 66         101 $type =~ s/^\s//; $type =~ s/\s$//;
  66         120  
55              
56             # Adjacent "derivative characters" are not separated by whitespace,
57             # but _are_ separated from the adjoining text.
58             # [ Is really only * (and not ()[]) needed??? ]
59 66         88 $type =~ s/\*\s\*/\*\*/g;
60 66         90 $type =~ s/(?<=[^ \*])\*/ \*/g;
61              
62 66         106 return $type;
63 8         50 };
64              
65             # The decision of what is an acceptable declaration was originally
66             # derived from Inline::C::grammar.pm version 0.30 (Inline 0.43).
67              
68 8         28 my $re_plausible_place_to_begin_a_declaration = qr {
69             # The beginning of a line, possibly indented.
70             # (Accepting indentation allows for C code to be aligned with
71             # its surrounding perl, and for backwards compatibility with
72             # Inline 0.43).
73             (?m: ^ ) \s*
74             }xo;
75              
76             # Instead of using \s , we don't tolerate blank lines.
77             # This matches user expectation better than allowing arbitrary
78             # vertical whitespace.
79 8         18 my $sp = qr{[ \t]|\n(?![ \t]*\n)};
80              
81 8         220 my $re_type = qr{
82             (
83             (?: \w+ $sp* )+? # words
84             (?: \* $sp* )* # stars
85             )
86             }xo;
87              
88 8         102 my $re_identifier = qr{ (\w+) $sp* }xo;
89              
90 8         28 $code =~ s/\bconst\b//g; # Remove "const" qualifier - it's not wanted here.
91              
92 8         1392 while ($code =~ m{
93             $re_plausible_place_to_begin_a_declaration
94             ( $re_type $re_identifier $RE_balanced_parens $sp* (\;|\{) )
95             }xgo
96             ) {
97 34         187 my ($type, $identifier, $args, $what) = ($2,$3,$4,$5);
98 34 50       111 $args = "" if $args =~ /^\s+$/;
99              
100 34         64 my $is_decl = $what eq ';';
101 34         40 my $function = $identifier;
102 34         57 my $return_type = &$normalize_type($type);
103 34         79 my @arguments = split ',', $args;
104              
105 34 50 33     72 goto RESYNC if $is_decl && !$self->{data}{AUTOWRAP};
106 34 50       78 goto RESYNC if $self->{data}{done}{$function};
107             goto RESYNC if !defined
108 34 50       77 $self->{data}{typeconv}{valid_rtypes}{$return_type};
109              
110 34         47 my(@arg_names,@arg_types);
111 34         59 my $dummy_name = 'arg1';
112              
113 34         54 foreach my $arg (@arguments) {
114 32         37 my $arg_no_space = $arg;
115 32         79 $arg_no_space =~ s/\s//g;
116             # If $arg_no_space is 'void', there will be no identifier.
117 32 50       464 if (my($type, $identifier) =
    0          
118             $arg =~ /^\s*$re_type(?:$re_identifier)?\s*$/o
119             ) {
120 32         48 my $arg_name = $identifier;
121 32         45 my $arg_type = &$normalize_type($type);
122              
123 32 50 66     78 if ((!defined $arg_name) && ($arg_no_space ne 'void')) {
124 0 0       0 goto RESYNC if !$is_decl;
125 0         0 $arg_name = $dummy_name++;
126             }
127             goto RESYNC if ((!defined
128 32 50 66     79 $self->{data}{typeconv}{valid_types}{$arg_type}) && ($arg_no_space ne 'void'));
129              
130             # Push $arg_name onto @arg_names iff it's defined. Otherwise ($arg_no_space
131             # was 'void'), push the empty string onto @arg_names (to avoid uninitialized
132             # warnings emanating from C.pm).
133 32 100       66 defined($arg_name) ? push(@arg_names,$arg_name)
134             : push(@arg_names, '');
135 32 100       42 if ($arg_name) {push(@arg_types,$arg_type)}
  22         48  
136 10         21 else {push(@arg_types,'')} # $arg_no_space was 'void' - this push() avoids 'uninitialized' warnings from C.pm
137             }
138             elsif ($arg =~ /^\s*\.\.\.\s*$/) {
139 0         0 push(@arg_names,'...');
140 0         0 push(@arg_types,'...');
141             }
142             else {
143 0         0 goto RESYNC;
144             }
145             }
146              
147             # Commit.
148 34         39 push @{$self->{data}{functions}}, $function;
  34         77  
149 34         111 $self->{data}{function}{$function}{return_type}= $return_type;
150 34         77 $self->{data}{function}{$function}{arg_names} = [@arg_names];
151 34         68 $self->{data}{function}{$function}{arg_types} = [@arg_types];
152 34         53 $self->{data}{done}{$function} = 1;
153              
154 34         168 next;
155              
156 0         0 RESYNC: # Skip the rest of the current line, and continue.
157             $code =~ /\G[^\n]*\n/gc;
158             }
159              
160 8         96 return 1; # We never fail.
161             }
162              
163             1;