File Coverage

blib/lib/ExtUtils/XSpp/Lexer.pm
Criterion Covered Total %
statement 247 261 94.6
branch 63 80 78.7
condition 14 17 82.3
subroutine 51 53 96.2
pod 0 27 0.0
total 375 438 85.6


line stmt bran cond sub pod time code
1             # this module will be loaded by ExtUtils/XSpp/Grammar.pm and needs to
2             # define subroutines in the ExtUtils::XSpp::Grammar namespace
3             package ExtUtils::XSpp::Lexer;
4             # for the indexer and friends
5 21     21   127 use strict;
  21         43  
  21         933  
6 21     21   130 use warnings;
  21         46  
  21         1004  
7              
8             package ExtUtils::XSpp::Grammar;
9              
10 21     21   3001 use ExtUtils::XSpp::Node;
  21         48  
  21         544  
11 21     21   9735 use ExtUtils::XSpp::Node::Access;
  21         50  
  21         508  
12 21     21   9829 use ExtUtils::XSpp::Node::Argument;
  21         61  
  21         588  
13 21     21   11639 use ExtUtils::XSpp::Node::Class;
  21         72  
  21         660  
14 21     21   11294 use ExtUtils::XSpp::Node::Comment;
  21         59  
  21         611  
15 21     21   10432 use ExtUtils::XSpp::Node::Constructor;
  21         72  
  21         908  
16 21     21   11800 use ExtUtils::XSpp::Node::Destructor;
  21         60  
  21         646  
17 21     21   10181 use ExtUtils::XSpp::Node::File;
  21         65  
  21         816  
18 21     21   128 use ExtUtils::XSpp::Node::Function;
  21         39  
  21         468  
19 21     21   13344 use ExtUtils::XSpp::Node::Member;
  21         67  
  21         543  
20 21     21   254 use ExtUtils::XSpp::Node::Method;
  21         41  
  21         734  
21 21     21   9654 use ExtUtils::XSpp::Node::Module;
  21         64  
  21         546  
22 21     21   130 use ExtUtils::XSpp::Node::Package;
  21         40  
  21         542  
23 21     21   115 use ExtUtils::XSpp::Node::Raw;
  21         38  
  21         611  
24 21     21   3656 use ExtUtils::XSpp::Node::Type;
  21         56  
  21         591  
25 21     21   11894 use ExtUtils::XSpp::Node::PercAny;
  21         59  
  21         510  
26 21     21   9859 use ExtUtils::XSpp::Node::Enum;
  21         66  
  21         520  
27 21     21   10953 use ExtUtils::XSpp::Node::EnumValue;
  21         62  
  21         538  
28 21     21   11999 use ExtUtils::XSpp::Node::Preprocessor;
  21         54  
  21         548  
29              
30 21     21   1703 use ExtUtils::XSpp::Typemap;
  21         47  
  21         1037  
31 21     21   529405 use ExtUtils::XSpp::Exception;
  21         68  
  21         1443  
32              
33 21     21   131 use Digest::MD5 qw(md5_hex);
  21         48  
  21         100927  
34              
35             my %tokens = ( '::' => 'DCOLON',
36             ':' => 'COLON',
37             '%{' => 'OPSPECIAL',
38             '%}' => 'CLSPECIAL',
39             '{%' => 'OPSPECIAL',
40             '{' => 'OPCURLY',
41             '}' => 'CLCURLY',
42             '(' => 'OPPAR',
43             ')' => 'CLPAR',
44             ';' => 'SEMICOLON',
45             '%' => 'PERC',
46             '~' => 'TILDE',
47             '*' => 'STAR',
48             '&' => 'AMP',
49             '|' => 'PIPE',
50             ',' => 'COMMA',
51             '=' => 'EQUAL',
52             '/' => 'SLASH',
53             '.' => 'DOT',
54             '-' => 'DASH',
55             '<' => 'OPANG',
56             '>' => 'CLANG',
57             # these are here due to my lack of skill with yacc
58             '%name' => 'p_name',
59             '%typemap' => 'p_typemap',
60             '%exception' => 'p_exceptionmap',
61             '%catch' => 'p_catch',
62             '%file' => 'p_file',
63             '%module' => 'p_module',
64             '%code' => 'p_code',
65             '%cleanup' => 'p_cleanup',
66             '%postcall' => 'p_postcall',
67             '%package' => 'p_package',
68             '%length' => 'p_length',
69             '%loadplugin' => 'p_loadplugin',
70             '%include' => 'p_include',
71             '%alias' => 'p_alias',
72             '%_type' => 'p__type',
73             );
74              
75             my %keywords = ( const => 1,
76             class => 1,
77             unsigned => 1,
78             short => 1,
79             long => 1,
80             int => 1,
81             char => 1,
82             void => 1,
83             package_static => 1,
84             class_static => 1,
85             static => 1,
86             public => 1,
87             private => 1,
88             protected => 1,
89             virtual => 1,
90             enum => 1,
91             );
92              
93 3603   100 3603 0 8352 sub get_lex_mode { return $_[0]->YYData->{LEX}{MODES}[0] || '' }
94              
95             sub push_lex_mode {
96 33     33 0 66 my( $p, $mode ) = @_;
97              
98 33         57 push @{$p->YYData->{LEX}{MODES}}, $mode;
  33         122  
99             }
100              
101             sub pop_lex_mode {
102 33     33 0 70 my( $p, $mode ) = @_;
103              
104 33 50       113 die "Unexpected mode: '$mode'"
105             unless get_lex_mode( $p ) eq $mode;
106              
107 33         71 pop @{$p->YYData->{LEX}{MODES}};
  33         105  
108             }
109              
110             sub read_more {
111 841     841 0 2427 my $v = readline $_[0]->YYData->{LEX}{FH};
112 841         2375 my $buf = $_[0]->YYData->{LEX}{BUFFER};
113              
114 841 100       1779 unless( defined $v ) {
115 88 100       392 if( $_[0]->YYData->{LEX}{NEXT} ) {
116 2         7 $_[0]->YYData->{LEX} = $_[0]->YYData->{LEX}{NEXT};
117 2         7 $buf = $_[0]->YYData->{LEX}{BUFFER};
118              
119 2 50       12 return $buf if length $$buf;
120 0         0 return read_more( $_[0] );
121             } else {
122 86         288 return;
123             }
124             }
125              
126 753         1341 $$buf .= $v;
127              
128 753         1981 return $buf;
129             }
130              
131             # for tests
132 0     0   0 sub _random_digits { sprintf '%06d', rand 100000 }
133              
134             sub push_conditional {
135 12     12 0 18 my $p = $_[0];
136 12 50       35 my $file = $p->YYData->{LEX}{FILE} ?
137             substr md5_hex( $p->YYData->{LEX}{FILE} ), 0, 8 :
138             'zzzzzzzz';
139 12         46 my $rand = _random_digits;
140              
141 12         76 my $symbol = 'XSpp_' . $file . '_' . $rand;
142 12         17 push @{$p->YYData->{LEX}{CONDITIONAL}}, $symbol;
  12         176  
143              
144 12         33 return $symbol;
145             }
146              
147             sub pop_conditional {
148 12     12 0 22 pop @{$_[0]->YYData->{LEX}{CONDITIONAL}};
  12         44  
149             }
150              
151             sub get_conditional {
152 171 100   171 0 598 return undef unless $_[0]->YYData->{LEX}{CONDITIONAL};
153 8 50       11 return undef unless @{$_[0]->YYData->{LEX}{CONDITIONAL}};
  8         23  
154 8         26 return $_[0]->YYData->{LEX}{CONDITIONAL}[-1];
155             }
156              
157             sub yylex {
158 2972     2972 0 7781 my $data = $_[0]->YYData->{LEX};
159 2972         5141 my $buf = $data->{BUFFER};
160              
161 2972         3392 for(;;) {
162 3656 100 66     9112 if( !length( $$buf ) && !( $buf = read_more( $_[0] ) ) ) {
163 86         324 return ( '', undef );
164             }
165              
166 3570 100       7533 if( get_lex_mode( $_[0] ) eq 'special' ) {
167 81 100       539 if( $$buf =~ s/^%}// ) {
    50          
168 33         135 return ( 'CLSPECIAL', '%}' );
169             } elsif( $$buf =~ s/^([^\n]*)\n$// ) {
170 48         117 my $line = $1;
171              
172 48 100       211 if( $line =~ m/^(.*?)\%}(.*)$/ ) {
173 27         93 $$buf = "%}$2\n";
174 27         62 $line = $1;
175             }
176              
177 48         214 return ( 'line', $line );
178             }
179             } else {
180 3489         9296 $$buf =~ s/^[\s\n\r]+//;
181 3489 100       20723 next unless length $$buf;
182              
183 2805 100       31875 if( $$buf =~ s/^([+-]?0x[0-9a-fA-F]+)// ) {
    100          
    100          
    100          
    100          
    100          
    50          
    100          
    50          
    100          
    50          
184 1         5 return ( 'INTEGER', $1 );
185             } elsif( $$buf =~ s/^([+-]?(?=\d|\.\d)\d*(?:\.\d*)?(?:[Ee](?:[+-]?\d+))?)// ) {
186 14         37 my $v = $1;
187 14 50       102 return ( 'INTEGER', $v ) if $v =~ /^[+-]?\d+$/;
188 0         0 return ( 'FLOAT', $v );
189             } elsif( $$buf =~ s/^\/\/(.*)(?:\r\n|\r|\n)// ) {
190 4         28 return ( 'COMMENT', [ $1 ] );
191             } elsif( $$buf =~ /^\/\*/ ) {
192 1         3 my @rows;
193 1   66     8 for(; length( $$buf ) || ( $buf = read_more( $_[0] ) ); $$buf = '') {
194 4 100       29 if( $$buf =~ s/(.*?\*\/)// ) {
195 1         4 push @rows, $1;
196 1         6 return ( 'COMMENT', \@rows );
197             }
198 3         16 $$buf =~ s/(?:\r\n|\r|\n)$//;
199 3         18 push @rows, $$buf;
200             }
201             } elsif( $$buf =~ s/^(\%\w+)// ) {
202 300 100       2555 return ( $tokens{$1}, $1 ) if exists $tokens{$1};
203 59         310 return ( 'p_any', substr $1, 1 );
204             } elsif( $$buf =~ s/^( \%}
205             | \%\{ | \{\%
206             | [{}();%~*&,=\/\.\-<>|]
207             | :: | :
208             )//x ) {
209 1491         8380 return ( $tokens{$1}, $1 );
210             } elsif( $$buf =~ s/^(INCLUDE(?:_COMMAND)?:.*)(?:\r\n|\r|\n)// ) {
211 0         0 return ( 'RAW_CODE', "$1\n" );
212             } elsif( $$buf =~ s/^([a-zA-Z_]\w*)// ) {
213 954 100       4262 return ( $1, $1 ) if exists $keywords{$1};
214              
215 638         2800 return ( 'ID', $1 );
216             } elsif( $$buf =~ s/^("[^"]*")// ) {
217 0         0 return ( 'QUOTED_STRING', $1 );
218             } elsif( $$buf =~ s/^(#\s*(if|ifdef|ifndef|else|elif|endif)\b.*)(?:\r\n|\r|\n)// ) {
219 22         30 my $symbol;
220 22 100 100     191 if( $2 eq 'else' || $2 eq 'elif' || $2 eq 'endif' ) {
      100        
221 12         31 pop_conditional( $_[0] );
222             }
223 22 100       68 if( $2 ne 'endif' ) {
224 12         37 $symbol = push_conditional( $_[0] );
225             }
226              
227 22         118 return ( 'PREPROCESSOR', [ $1, $symbol ] );
228             } elsif( $$buf =~ s/^(#.*)(?:\r\n|\r|\n)// ) {
229 18         128 return ( 'RAW_CODE', $1 );
230             } else {
231 0         0 die $$buf;
232             }
233             }
234             }
235             }
236              
237             sub yyerror {
238 0     0 0 0 my $data = $_[0]->YYData->{LEX};
239 0         0 my $buf = $data->{BUFFER};
240 0         0 my $fh = $data->{FH};
241              
242 0 0       0 print STDERR "Error: line " . $fh->input_line_number . " (Current token type: '",
243             $_[0]->YYCurtok, "') (Current value: '",
244             $_[0]->YYCurval, '\') Buffer: "', ( $buf ? $$buf : '--empty buffer--' ),
245             q{"} . "\n";
246 0         0 print STDERR "Expecting: (", ( join ", ", map { "'$_'" } $_[0]->YYExpect ),
  0         0  
247             ")\n";
248             }
249              
250 16     16 0 36 sub make_const { $_[0]->{CONST} = 1; $_[0] }
  16         37  
251 13     13 0 30 sub make_ref { $_[0]->{REFERENCE} = 1; $_[0] }
  13         31  
252 9     9 0 33 sub make_ptr { $_[0]->{POINTER}++; $_[0] }
  9         23  
253 256     256 0 1289 sub make_type { ExtUtils::XSpp::Node::Type->new( base => $_[0] ) }
254              
255             sub make_template {
256 9     9 0 40 ExtUtils::XSpp::Node::Type->new( base => $_[0],
257             template_args => $_[1],
258             )
259             }
260              
261             sub add_typemap {
262 28     28 0 74 my( $name, $type, @args ) = @_;
263 28         156 my $tm = ExtUtils::XSpp::Typemap::create( $name, type => $type, @args );
264              
265 28         143 ExtUtils::XSpp::Typemap::add_typemap_for_type( $type, $tm );
266             }
267              
268             sub add_data_raw {
269 24     24 0 54 my $p = shift;
270 24         37 my $rows = shift;
271              
272 24         214 ExtUtils::XSpp::Node::Raw->new( rows => $rows );
273             }
274              
275             sub add_data_comment {
276 5     5 0 10 my $p = shift;
277 5         11 my $rows = shift;
278              
279 5         49 ExtUtils::XSpp::Node::Comment->new( rows => $rows );
280             }
281              
282             sub add_top_level_directive {
283 2     2 0 6 my( $parser, %args ) = @_;
284              
285 2         6 $parser->YYData->{PARSER}->handle_toplevel_tag_plugins
286             ( $args{any},
287             named => $args{named},
288             positional => $args{positional},
289             any_named_arguments => $args{named},
290             any_positional_arguments => $args{positional},
291             condition => $parser->get_conditional,
292             );
293             }
294              
295             sub make_argument {
296 114     114 0 263 my( $p, $type, $name, $default, @args ) = @_;
297 114         244 my %args = @args;
298 114         427 _merge_keys( 'tag', \%args, \@args );
299              
300 114         862 my $arg = ExtUtils::XSpp::Node::Argument->new
301             ( type => $type,
302             name => $name,
303             default => $default,
304             tags => $args{tag} );
305              
306 114         377 return $arg;
307             }
308              
309             sub create_class {
310 54     54 0 167 my( $parser, $name, $bases, $metadata, $methods, $condition ) = @_;
311 54         142 my %args = @$metadata;
312 54         164 _merge_keys( 'catch', \%args, $metadata );
313              
314 54         579 my $class = ExtUtils::XSpp::Node::Class->new( %args, # <-- catch only for now
315             cpp_name => $name,
316             base_classes => $bases,
317             condition => $condition,
318             );
319              
320             # when adding a class C, automatically add weak typemaps for C* and C&
321 54         342 ExtUtils::XSpp::Typemap::add_class_default_typemaps( $name );
322              
323 54         3891 my @any = grep $_->isa( 'ExtUtils::XSpp::Node::PercAny' ), @$methods;
324 54         432 my @rest = grep !$_->isa( 'ExtUtils::XSpp::Node::PercAny' ), @$methods;
325              
326             # finish creating the class
327 54         327 $class->add_methods( @rest );
328              
329 54         425 foreach my $meth ( grep $_->isa( 'ExtUtils::XSpp::Node::Method' ), @rest ) {
330 51         191 call_argument_tags( $parser, $meth );
331              
332 51         189 my $nodes = $parser->YYData->{PARSER}->handle_method_tags_plugins( $meth, $meth->tags );
333              
334 51         236 $class->add_methods( @$nodes );
335             }
336              
337 54         139 foreach my $any ( @any ) {
338 8 100       29 if( $any->{NAME} eq 'accessors' ) {
339             # TODO use plugin infrastructure, add decent validation
340 6         7 my %args = @{$any->{NAMED_ARGUMENTS}};
  6         25  
341 6 50       20 if( $args{get_style} ) {
342 6 50       7 if( @{$args{get_style}} ) {
  6         17  
343 6         28 $class->set_getter_style( $args{get_style}[0][0] );
344             } else {
345 0         0 die "Invalid accessor style declaration";
346             }
347             }
348 6 50       18 if( $args{set_style} ) {
349 6 50       7 if( @{$args{set_style}} ) {
  6         17  
350 6         24 $class->set_setter_style( $args{set_style}[0][0] );
351             } else {
352 0         0 die "Invalid accessor style declaration";
353             }
354             }
355 6         17 next;
356             }
357              
358 2         6 my $nodes = $parser->YYData->{PARSER}->handle_class_tag_plugins
359             ( $class, $any->{NAME},
360             named => $any->{NAMED_ARGUMENTS},
361             positional => $any->{POSITIONAL_ARGUMENTS},
362             any_named_arguments => $any->{NAMED_ARGUMENTS},
363             any_positional_arguments => $any->{POSITIONAL_ARGUMENTS},
364             );
365              
366 2         169 $class->add_methods( @$nodes );
367             }
368              
369 54         265 return $class;
370             }
371              
372             # support multiple occurrances of specific keys
373             # => transform to flattened array ref
374             sub _merge_keys {
375 458     458   794 my $key = shift;
376 458         537 my $argshash = shift;
377 458         525 my $paramlist = shift;
378 458         587 my @occurrances;
379 458         1342 for (my $i = 0; $i < @$paramlist; $i += 2) {
380 1679 100 66     8423 if (defined $paramlist->[$i] and $paramlist->[$i] eq $key) {
381 52         169 push @occurrances, $paramlist->[$i+1];
382             }
383             }
384 458 100       724 @occurrances = map {ref($_) eq 'ARRAY' ? @$_ : $_} @occurrances;
  52         181  
385 458         1581 $argshash->{$key} = \@occurrances;
386             }
387              
388              
389             sub create_member {
390 12     12 0 51 my( $parser, @args ) = @_;
391 12         95 my %args = @args;
392 12         47 _merge_keys( 'tag', \%args, \@args );
393              
394 12         125 return ExtUtils::XSpp::Node::Member->new
395             ( cpp_name => $args{name},
396             perl_name => $args{perl_name},
397             class => $args{class},
398             type => $args{type},
399             condition => $args{condition},
400             tags => $args{tag},
401             );
402             }
403              
404             sub add_data_function {
405 44     44 0 222 my( $parser, @args ) = @_;
406 44         292 my %args = @args;
407 44         162 _merge_keys( 'catch', \%args, \@args );
408 44         138 _merge_keys( 'alias', \%args, \@args );
409 44         152 _merge_keys( 'tag', \%args, \@args );
410 44 50       175 $args{alias} = +{@{$args{alias}}} if exists $args{alias};
  44         147  
411              
412 44         785 return ExtUtils::XSpp::Node::Function->new
413             ( cpp_name => $args{name},
414             perl_name => $args{perl_name},
415             class => $args{class},
416             ret_type => $args{ret_type},
417             arguments => $args{arguments},
418             code => $args{code},
419             cleanup => $args{cleanup},
420             postcall => $args{postcall},
421             catch => $args{catch},
422             condition => $args{condition},
423             alias => $args{alias},
424             tags => $args{tag},
425             );
426             }
427              
428             sub add_data_method {
429 44     44 0 236 my( $parser, @args ) = @_;
430 44         282 my %args = @args;
431 44         187 _merge_keys( 'catch', \%args, \@args );
432 44         139 _merge_keys( 'alias', \%args, \@args );
433 44         147 _merge_keys( 'tag', \%args, \@args );
434 44 50       167 $args{alias} = +{@{$args{alias}}} if exists $args{alias};
  44         135  
435              
436 44         746 my $m = ExtUtils::XSpp::Node::Method->new
437             ( cpp_name => $args{name},
438             ret_type => $args{ret_type},
439             arguments => $args{arguments},
440             const => $args{const},
441             code => $args{code},
442             cleanup => $args{cleanup},
443             postcall => $args{postcall},
444             perl_name => $args{perl_name},
445             catch => $args{catch},
446             condition => $args{condition},
447             alias => $args{alias},
448             tags => $args{tag},
449             );
450              
451 44         288 return $m;
452             }
453              
454             sub add_data_ctor {
455 4     4 0 20 my( $parser, @args ) = @_;
456 4         26 my %args = @args;
457 4         24 _merge_keys( 'catch', \%args, \@args );
458 4         15 _merge_keys( 'tag', \%args, \@args );
459              
460 4         93 my $m = ExtUtils::XSpp::Node::Constructor->new
461             ( cpp_name => $args{name},
462             arguments => $args{arguments},
463             code => $args{code},
464             cleanup => $args{cleanup},
465             postcall => $args{postcall},
466             catch => $args{catch},
467             condition => $args{condition},
468             tags => $args{tag},
469             );
470              
471 4         31 return $m;
472             }
473              
474             sub add_data_dtor {
475 3     3 0 12 my( $parser, @args ) = @_;
476 3         16 my %args = @args;
477 3         18 _merge_keys( 'catch', \%args, \@args );
478 3         19 _merge_keys( 'tag', \%args, \@args );
479              
480 3         74 my $m = ExtUtils::XSpp::Node::Destructor->new
481             ( cpp_name => $args{name},
482             code => $args{code},
483             cleanup => $args{cleanup},
484             postcall => $args{postcall},
485             catch => $args{catch},
486             condition => $args{condition},
487             tags => $args{tag},
488             );
489              
490 3         19 return $m;
491             }
492              
493             sub process_function {
494 44     44 0 91 my( $parser, $function ) = @_;
495              
496 44         196 $function->resolve_typemaps;
497 44         202 $function->resolve_exceptions;
498 44         180 call_argument_tags( $parser, $function );
499              
500 44         2347 my $nodes = $parser->YYData->{PARSER}->handle_function_tags_plugins( $function, $function->tags );
501              
502 44         199 return [ $function, @$nodes ];
503             }
504              
505             sub call_argument_tags {
506 95     95 0 185 my( $parser, $function ) = @_;
507              
508 95         205 foreach my $arg ( @{$function->arguments} ) {
  95         312  
509 114         403 $parser->YYData->{PARSER}->handle_argument_tags_plugins( $arg, $arg->tags );
510             }
511             }
512              
513             1;