File Coverage

blib/lib/Parse/Method/Signatures.pm
Criterion Covered Total %
statement 352 362 97.2
branch 154 168 91.6
condition 69 80 86.2
subroutine 56 57 98.2
pod 3 16 18.7
total 634 683 92.8


line stmt bran cond sub pod time code
1             package Parse::Method::Signatures;
2              
3 3     3   821423 use Moose;
  3         653924  
  3         18  
4 3         27 use MooseX::Types::Moose qw/
5             ArrayRef HashRef ScalarRef CodeRef Int Str ClassName
6 3     3   15845 /;
  3         96165  
7              
8 3     3   11987 use Class::Load qw(load_class);
  3         5  
  3         198  
9 3     3   1932 use PPI;
  3         267593  
  3         108  
10 3     3   22 use Moose::Util::TypeConstraints;
  3         5  
  3         37  
11 3     3   6812 use Parse::Method::Signatures::ParamCollection;
  3         10  
  3         130  
12 3         15 use Parse::Method::Signatures::Types qw/
13             PositionalParam NamedParam UnpackedParam
14 3     3   21 /;
  3         4  
15              
16 3     3   4989 use Carp qw/croak/;
  3         3  
  3         185  
17              
18 3     3   13 use namespace::clean -except => 'meta';
  3         4  
  3         21  
19             our $VERSION = '1.003018_01';
20             $VERSION = eval $VERSION;
21             our $ERROR_LEVEL = 0;
22             our %LEXTABLE;
23             our $DEBUG = $ENV{PMS_DEBUG} || 0;
24              
25             # Setup what we need for specific PPI subclasses
26             @PPI::Token::EOF::ISA = 'PPI::Token';
27              
28             class_type "PPI::Document";
29             class_type "PPI::Element";
30              
31             has 'input' => (
32             is => 'ro',
33             isa => Str,
34             required => 1
35             );
36              
37             has 'offset' => (
38             is => 'rw',
39             isa => Int,
40             default => 0,
41             );
42              
43             has 'signature_class' => (
44             is => 'ro',
45             isa => Str,
46             default => 'Parse::Method::Signatures::Sig',
47             );
48              
49             has 'param_class' => (
50             is => 'ro',
51             isa => Str,
52             default => 'Parse::Method::Signatures::Param',
53             );
54              
55             has 'type_constraint_class' => (
56             is => 'ro',
57             isa => Str,
58             default => 'Parse::Method::Signatures::TypeConstraint',
59             );
60              
61             has 'type_constraint_callback' => (
62             is => 'ro',
63             isa => CodeRef,
64             predicate => 'has_type_constraint_callback',
65             );
66              
67             has 'from_namespace' => (
68             is => 'rw',
69             isa => ClassName,
70             predicate => 'has_from_namespace'
71             );
72              
73             has 'ppi_doc' => (
74             is => 'ro',
75             isa => 'PPI::Document',
76             lazy_build => 1,
77             builder => 'parse',
78             );
79              
80             # A bit dirty, but we set this with local most of the time
81             has 'ppi' => (
82             is => 'ro',
83             isa => 'PPI::Element',
84             lazy_build => 1,
85             writer => '_set_ppi'
86             );
87              
88             sub BUILD {
89 147     147 0 261 my ($self) = @_;
90              
91             load_class($_)
92 147         321 for map { $self->$_ } qw/
  441         14034  
93             signature_class
94             param_class
95             type_constraint_class
96             /;
97              
98 147         13330 my $ppi = $self->ppi;
99              
100             # Skip leading whitespace
101 147 50       4563 $self->consume_token
102             unless $ppi->significant;
103             }
104              
105             sub create_param {
106 206     206 0 264 my ($self, $args) = @_;
107              
108 206         279 my @traits;
109             push @traits, $args->{ variable_name } ? 'Bindable' : 'Placeholder'
110 206 100       844 if !exists $args->{unpacking};
    100          
111 206 100       570 push @traits, $args->{ named } ? 'Named' : 'Positional';
112             push @traits, 'Unpacked::' . $args->{unpacking}
113 206 100       471 if exists $args->{unpacking};
114              
115 206         5804 return $self->param_class->new_with_traits(traits => \@traits, %{ $args });
  206         1444  
116             }
117              
118             override BUILDARGS => sub {
119             my $class = shift;
120              
121             return { input => $_[0] } if @_ == 1 and !ref $_[0];
122              
123             return super();
124             };
125              
126             sub parse {
127 147     147 0 243 my ($self) = @_;
128            
129 147         4196 my $input = substr($self->input, $self->offset);
130 147         1058 my $doc = PPI::Document->new(\$input);
131              
132             # Append the magic EOF Token
133 147         191991 $doc->add_element(PPI::Token::EOF->new(""));
134              
135             # Annoyingly "m($x)" gets treated as a regex operator. This isn't what we
136             # want. so replace it with a Word, then a list. The way we do this is by
137             # taking the operator off the front, then reparsing the rest of the content
138             # This will look the same (so wont affect anything in a code block) but is
139             # just store different token wise.
140 147         3487 $self->_replace_regexps($doc);
141              
142             # ($, $x) parses the $, as a single var. not what we want. FIX UP
143             # While we're att it lets fixup $: $? and $!
144 147         71373 $self->_replace_magic($doc);
145              
146             # (Str :$x) yields a label of "Str :"
147             # (Foo Bar :$x) yields a label of "Bar :"
148 147         52162 $self->_replace_labels($doc);
149              
150             # This one is actually a bug in PPI, rather than just an oddity
151             # (Str $x = 0xfF) parses as "Oxf" and a word of "F"
152 147         54776 $self->_fixup_hex($doc);
153              
154 147         61898 return $doc;
155             }
156              
157             sub _replace_regexps {
158 147     147   524 my ($self, $doc) = @_;
159              
160             REGEXP:
161 147 100       211 foreach my $node ( @{ $doc->find('Token::Regexp') || [] } ) {
  147         533  
162 2         1126 my $str = $node->content;
163              
164 2 100       17 next REGEXP unless defined $node->{operator};
165              
166             # Rather annoyingly, there are *no* methods on Token::Regexp;
167 1         2 my ($word, $rest) = $str =~ /^(\Q@{[$node->{operator}]}\E)(.*)$/s;
  1         17  
168              
169 1         5 my $subdoc = PPI::Document->new(\$rest);
170 1         718 my @to_add = reverse map { $_->remove } $subdoc->children;
  1         11  
171 1         47 push @to_add, new PPI::Token::Word($word);
172             # insert_after restricts what you can insert.
173             # $node->insert_after($_) for @to_add;
174 1         12 $node->__insert_after($_) for @to_add;
175              
176 1         54 $node->delete;
177             }
178             }
179              
180              
181             sub _replace_magic {
182 147     147   258 my ($self, $doc) = @_;
183              
184 147 100       235 foreach my $node ( @{ $doc->find('Token::Magic') || [] } ) {
  147         390  
185 17 100       7391 my ($op) = $node->content =~ /^\$([,?:!)])$/ or next;
186              
187 7         73 $node->insert_after(new PPI::Token::Operator($op));
188 7         1972 $node->insert_after(new PPI::Token::Cast('$'));
189 7         356 $node->delete;
190             }
191             }
192              
193             sub _replace_labels {
194 147     147   309 my ($self, $doc) = @_;
195              
196 147 100       240 foreach my $node ( @{ $doc->find('Token::Label') || [] } ) {
  147         433  
197 8 50       3492 my ($word, $ws) = $node->content =~ /^(.*?)(\s+)?:$/s or next;
198              
199 8         109 $node->insert_after(new PPI::Token::Operator(':'));
200 8 100       530 $node->insert_after(new PPI::Token::Whitespace($ws)) if defined $ws;
201 8         289 $node->insert_after(new PPI::Token::Word($word));
202 8         302 $node->delete;
203             }
204             }
205              
206             sub _fixup_hex {
207 147     147   278 my ($self, $doc) = @_;
208              
209 147 100       215 foreach my $node ( @{ $doc->find('Token::Number::Hex') || [] } ) {
  147         328  
210 3         1406 my $next = $node->next_token;
211 3 50 66     309 next unless $next->isa('PPI::Token::Word')
212             && $next->content =~ /^[0-9a-f]+$/i;
213              
214 0         0 $node->add_content($next->content);
215 0         0 $next->delete;
216             }
217             }
218              
219             sub _build_ppi {
220 147     147   218 my ($self) = @_;
221 147         4356 my $ppi = $self->ppi_doc->first_token;
222              
223 147 100 100     3756 if ($ppi->class eq 'PPI::Token::Word' && exists $LEXTABLE{"$ppi"}) {
224 1         13 bless $ppi, "PPI::Token::LexSymbol";
225 1         61 $ppi->{lex} = $LEXTABLE{"$ppi"};
226             }
227 147         5043 return $ppi;
228             }
229              
230             # signature: O_PAREN
231             # invocant
232             # params
233             # C_PAREN
234             #
235             # invocant: param ':'
236             #
237             # params: param COMMA params
238             # | param
239             # | /* NUL */
240             sub signature {
241 120     120 1 40751 my $self = shift;
242              
243 120 100       4578 $self = $self->new(@_) unless blessed($self);
244              
245 120         384 $self->assert_token('(');
246              
247 120         179 my $args = {};
248 120         172 my $params = [];
249              
250 120         393 my $param = $self->param;
251              
252 108 100 100     3601 if ($param && $self->ppi->content eq ':') {
253             # That param was actually the invocant
254 13         131 $args->{invocant} = $param;
255 13 100       70 croak "Invocant cannot be named"
256             if NamedParam->check($param);
257 12 100       15111 croak "Invocant cannot be optional"
258             if !$param->required;
259 11 100       473 croak "Invocant cannot have a default value"
260             if $param->has_default_value;
261              
262 10 100 66     46 croak "Invocant must be a simple scalar"
263             if UnpackedParam->check($param) || $param->sigil ne '$';
264              
265 7         31 $self->consume_token;
266 7         20 $param = $self->param;
267              
268             }
269              
270 102 100       875 if ($param) {
271 95         356 push @$params, $param;
272              
273 95 100       3225 my $greedy = $param->sigil ne '$' ? $param : undef;
274 95         3118 my $opt_pos_param = !$param->required;
275              
276 95         2554 while ($self->ppi->content eq ',') {
277 42         298 $self->consume_token;
278              
279 42         1040 my $err_ctx = $self->ppi;
280 42         112 $param = $self->param;
281 42 100       182 $self->error($err_ctx, "Parameter expected")
282             if !$param;
283              
284 41         192 my $is_named = NamedParam->check($param);
285 41 100       39066 if (!$is_named) {
286 26 100 100     908 if ($param->required && $opt_pos_param) {
287 1         10 $self->error($err_ctx, "Invalid: Required positional param " .
288             " found after optional one");
289             }
290 25 100       93 if ($greedy) {
291 4         168 croak "Invalid: Un-named parameter '" . $param->variable_name
292             . "' after greedy '"
293             . $greedy->variable_name . "'\n";
294             }
295             }
296              
297 36         78 push @$params, $param;
298 36   100     851 $opt_pos_param = $opt_pos_param || !$param->required;
299 36 100       1097 $greedy = $param->sigil ne '$' ? $param : undef;
300             }
301             }
302              
303 96         738 $self->assert_token(')');
304 87         201 $args->{params} = $params;
305              
306 87         2733 my $sig = $self->signature_class->new($args);
307              
308 87         2842 return $sig;
309             }
310              
311              
312             # param: tc?
313             # var
314             # (OPTIONAL|REQUIRED)?
315             # default?
316             # where*
317             # trait*
318             #
319             # where: WHERE <code block>
320             #
321             # trait: TRAIT class
322             #
323             # var : COLON label '(' var_or_unpack ')' # label is classish, with only /a-z0-9_/i allowed
324             # | COLON VAR
325             # | var_or_unpack
326             #
327             # var_or_unpack : '[' param* ']' # should all be required + un-named
328             # | '{' param* '}' # Should all be named
329             # | VAR
330             #
331             # OPTIONAL: '?'
332             # REQUIRED: '!'
333             sub param {
334 232     232 1 476 my $self = shift;
335 232         259 my $class_meth;
336 232 100       810 unless (blessed($self)) {
337 2 50       68 $self = $self->new(@_) unless blessed($self);
338 2         4 $class_meth = 1;
339             }
340              
341             # Also used to check if a anything has been consumed
342 232         6020 my $err_ctx = $self->ppi;
343              
344 232         635 my $param = {
345             required => 1,
346             };
347              
348 232         851 $self->_param_typed($param);
349              
350 230 50 66     817 $self->_param_opt_or_req(
      100        
      66        
351             $self->_param_labeled($param)
352             || $self->_param_named($param)
353             || $self->_param_variable($param)
354             || $self->_unpacked_param($param)
355             ) or ($err_ctx == $self->ppi and return)
356             or $self->error($err_ctx);
357              
358 209         513 $self->_param_default($param);
359 209         1176 $self->_param_constraint_or_traits($param);
360              
361 206         449 $param = $self->create_param($param);
362              
363 206 50       282410 return !$class_meth
    100          
364             ? $param
365             : wantarray
366             ? ($param, $self->remaining_input)
367             : $param;
368             }
369              
370             sub _param_opt_or_req {
371 219     219   488 my ($self, $param) = @_;
372              
373 219 100       779 return unless $param;
374              
375 209 100       5600 if ($self->ppi->class eq 'PPI::Token::Operator') {
376 115         3320 my $c = $self->ppi->content;
377 115 100       677 if ($c eq '?') {
    100          
378 9         23 $param->{required} = 0;
379 9         25 $self->consume_token;
380             } elsif ($c eq '!') {
381 7         13 $param->{required} = 1;
382 7         19 $self->consume_token;
383             }
384             }
385 209         859 return $param;
386              
387             }
388              
389             sub _param_constraint_or_traits {
390 209     209   320 my ($self, $param) = @_;
391              
392 209   100     662 while ($self->_param_where($param) ||
393             $self->_param_traits($param) ) {
394             # No op;
395              
396             }
397 206         263 return $param;
398             }
399              
400             sub _param_where {
401 228     228   279 my ($self, $param) = @_;
402              
403 228 100 100     5933 return unless $self->ppi->isa('PPI::Token::LexSymbol')
404             && $self->ppi->lex eq 'WHERE';
405              
406 13         24 $self->consume_token;
407              
408 13   100     56 $param->{constraints} ||= [];
409              
410 13         348 my $ppi = $self->ppi;
411              
412 13 100 66     36 $self->error($ppi, "Block expected after where")
413             unless $ppi->class eq 'PPI::Token::Structure'
414             && $ppi->content eq '{';
415              
416             # Go from token to block
417 12         128 $ppi = $ppi->parent;
418              
419 12 100       66 $ppi->finish or $self->error($ppi,
420             "Runaway '" . $ppi->braces . "' in " . $self->_parsing_area(1), 1);
421              
422 11         57 push @{$param->{constraints}}, $ppi->content;
  11         41  
423              
424 11         483 $self->_set_ppi($ppi->finish);
425 11         28 $self->consume_token;
426 11         41 return $param;
427             }
428              
429             sub _param_traits {
430 215     215   293 my ($self, $param) = @_;
431 215 100 66     5519 return unless $self->ppi->isa('PPI::Token::LexSymbol')
432             && $self->ppi->lex eq 'TRAIT';
433              
434 9         33 my $op = $self->consume_token->content;
435              
436 9 100       374 $self->error($self->ppi, "Error parsing parameter trait")
437             unless $self->ppi->isa('PPI::Token::Word');
438              
439 8   100     59 $param->{param_traits} ||= [];
440              
441 8         11 push @{$param->{param_traits}}, [$op, $self->consume_token->content];
  8         36  
442 8         130 return $param;
443             }
444              
445             sub _param_labeled {
446 230     230   273 my ($self, $param) = @_;
447              
448             return unless
449 230 100 100     6130 $self->ppi->content eq ':' &&
450             $self->ppi->next_token->isa('PPI::Token::Word');
451              
452 14         603 $self->consume_token;
453              
454 14 50       349 $self->error($self->ppi, "Invalid label")
455             if $self->ppi->content =~ /[^-\w]/;
456              
457 14         81 $param->{named} = 1;
458 14         24 $param->{required} = 0;
459 14         33 $param->{label} = $self->consume_token->content;
460              
461 14         70 $self->assert_token('(');
462 14 100 100     44 $self->_unpacked_param($param)
463             || $self->_param_variable($param)
464             || $self->error($self->ppi);
465              
466 9         65 $self->assert_token(')');
467              
468 9         55 return $param;
469             }
470              
471             sub _unpacked_param {
472 38     38   64 my ($self, $param) = @_;
473              
474 38   100     156 return $self->bracketed('[', \&unpacked_array, $param) ||
475             $self->bracketed('{', \&unpacked_hash, $param);
476             }
477              
478             sub _param_named {
479 216     216   3630 my ($self, $param) = @_;
480              
481             return unless
482 216 100 100     5633 $self->ppi->content eq ':' &&
483             $self->ppi->next_token->isa('PPI::Token::Symbol');
484              
485 45         1408 $param->{required} = 0;
486 45         96 $param->{named} = 1;
487 45         101 $self->consume_token;
488              
489 45         1142 my $err_ctx = $self->ppi;
490 45         136 $param = $self->_param_variable($param);
491              
492             $self->error($err_ctx, "Arrays or hashes cannot be named")
493 45 100       154 if $param->{sigil} ne '$';
494              
495 42         276 return $param;
496             }
497              
498             sub _param_typed {
499 232     232   366 my ($self, $param) = @_;
500              
501 232 100       774 my $tc = $self->tc
502             or return;
503              
504              
505 29 100       934 $tc = $self->type_constraint_class->new(
    50          
506             ppi => $tc,
507             ( $self->has_type_constraint_callback
508             ? (tc_callback => $self->type_constraint_callback)
509             : ()
510             ),
511             ( $self->has_from_namespace
512             ? ( from_namespace => $self->from_namespace )
513             : ()
514             ),
515             );
516 29         76 $param->{type_constraints} = $tc;
517              
518 29         41 return $param;
519             }
520            
521             sub _param_default {
522 209     209   288 my ($self, $param) = @_;
523              
524 209 100       5195 return unless $self->ppi->content eq '=';
525              
526 25         114 $self->consume_token;
527              
528             $param->{default_value} =
529 25 50 66     106 $self->_consume_if_isa(qw/
530             PPI::Token::QuoteLike
531             PPI::Token::Number
532             PPI::Token::Quote
533             PPI::Token::Symbol
534             PPI::Token::Magic
535             PPI::Token::ArrayIndex
536             /) ||
537             $self->bracketed('[') ||
538             $self->bracketed('{')
539             or $self->error($self->ppi);
540            
541 25         84 $param->{default_value} = $param->{default_value}->content;
542             }
543              
544              
545             sub _param_variable {
546 222     222   1809 my ($self, $param) = @_;
547              
548 222         5978 my $ppi = $self->ppi;
549 222         521 my $class = $ppi->class;
550 222 100 100     1295 return unless $class eq 'PPI::Token::Symbol'
551             || $class eq 'PPI::Token::Cast';
552              
553 197 100       571 if ($class eq 'PPI::Token::Symbol') {
554 187 100       663 $ppi->symbol_type eq $ppi->raw_type or $self->error($ppi);
555              
556 186         12375 $param->{sigil} = $ppi->raw_type;
557 186         990 $param->{variable_name} = $self->consume_token->content;
558             } else {
559 10         20 $param->{sigil} = $self->consume_token->content;
560             }
561              
562 196         1362 return $param;
563             }
564              
565             sub unpacked_hash {
566 9     9 0 17 my ($self, $list, $param) = @_;
567              
568 9         15 my $params = [];
569 9         266 while ($self->ppi->content ne '}') {
570 20         610 my $errctx = $self->ppi;
571 20 50       66 my $p = $self->param
572             or $self->error($self->ppi);
573              
574 20 100 100     649 $self->error($errctx, "Cannot have positional parameters in an unpacked-array")
575             if $p->sigil eq '$' && PositionalParam->check($p);
576 19         11690 push @$params, $p;
577              
578 19 100       531 last if $self->ppi->content eq '}';
579 11         105 $self->assert_token(',');
580             }
581 8         62 $param->{params} = $params;
582 8         17 $param->{sigil} = '$';
583 8         21 $param->{unpacking} = 'Hash';
584 8         20 return $param;
585             }
586              
587             sub unpacked_array {
588 12     12 0 21 my ($self, $list, $param) = @_;
589              
590 12         24 my $params = [];
591 12         330 while ($self->ppi->content ne ']') {
592 27         724 my $watermark = $self->ppi;
593 27 50       72 my $param = $self->param
594             or $self->error($self->ppi);
595              
596 26 100       111 $self->error($watermark, "Cannot have named parameters in an unpacked-array")
597             if NamedParam->check($param);
598              
599 24 100       21749 $self->error($watermark, "Cannot have optional parameters in an unpacked-array")
600             unless $param->required;
601              
602 23         41 push @$params, $param;
603              
604 23 100       572 last if $self->ppi->content eq ']';
605 15         93 $self->assert_token(',');
606             }
607 8         44 $param->{params} = $params;
608 8         22 $param->{sigil} = '$';
609 8         16 $param->{unpacking} = 'Array';
610 8         21 return $param;
611             }
612              
613             sub tc {
614 267     267 0 324 my ($self, $required) = @_;
615              
616 267         700 my $ident = $self->_ident;
617              
618 267 50 66     1527 $ident or ($required and $self->error($self->ppi)) or return;
      66        
619              
620 64   66     233 return $self->_tc_union(
621             $self->bracketed('[', \&_tc_params, $ident)
622             || $ident->clone
623             );
624             }
625              
626             # Handle parameterized TCs. e.g.:
627             # ArrayRef[Str]
628             # Dict[Str => Str]
629             # Dict["foo bar", Baz]
630             sub _tc_params {
631 15     15   117 my ($self, $list, $tc) = @_;
632              
633 15         50 my $new = PPI::Statement::Expression::TCParams->new($tc->clone);
634              
635 15 100       4400 return $new if $self->ppi->content eq ']';
636              
637 14         92 $new->add_element($self->_tc_param);
638              
639 13         336 while ($self->ppi->content =~ /^,|=>$/ ) {
640              
641 6         42 my $op = $self->consume_token;
642 6 100       18 $self->_stringify_last($new) if $op->content eq '=>';
643              
644 6         23 $new->add_element($self->tc(1));
645             }
646              
647 13         73 return $new;
648             }
649              
650             # Valid token for individual component of parameterized TC
651             sub _tc_param {
652 14     14   23 my ($self) = @_;
653              
654 14         352 (my $class = $self->ppi->class) =~ s/^PPI:://;
655 14 100 100     174 return $self->consume_token->clone
656             if $class eq 'Token::Number' ||
657             $class =~ /^Token::Quote::(?:Single|Double|Literal|Interpolate)/;
658              
659 12         42 return $self->tc(1);
660             }
661              
662             sub _tc_union {
663 61     61   1013 my ($self, $tc) = @_;
664            
665 61 100       1712 return $tc unless $self->ppi->content eq '|';
666              
667 9         102 my $union = PPI::Statement::Expression::TCUnion->new;
668 9         117 $union->add_element($tc);
669 9         607 while ( $self->ppi->content eq '|' ) {
670            
671 9         50 $self->consume_token;
672 9         28 $union->add_element($self->tc(1));
673             }
674              
675 8         80 return $union;
676             }
677              
678             # Stringify LHS of fat comma
679             sub _stringify_last {
680 4     4   32 my ($self, $list) = @_;
681 4         36 my $last = $list->last_token;
682 4 100       87 return unless $last->isa('PPI::Token::Word');
683              
684             # Is this conditional on the content of the word?
685 3         20 bless $last, "PPI::Token::StringifiedWord";
686 3         11 return $list;
687             }
688              
689             # Handle the boring bits of bracketed product, then call $code->($self, ...)
690             sub bracketed {
691 131     131 0 416 my ($self, $type, $code, @args) = @_;
692              
693 131         179 local $ERROR_LEVEL = $ERROR_LEVEL + 1;
694 131         3453 my $ppi = $self->ppi;
695 131 100       309 return unless $ppi->content eq $type;
696              
697 40         193 $self->consume_token; # consume '[';
698              
699             # Get from the '[' token the to Strucure::Constructor
700 40         123 $ppi = $ppi->parent;
701              
702 40 100       217 $ppi->finish or $self->error($ppi,
703             "Runaway '" . $ppi->braces . "' in " . $self->_parsing_area(1), 1);
704              
705              
706 38         172 my $ret;
707 38 100       93 if ($code) {
708 36         108 my $list = PPI::Structure::Constructor->new($ppi->start->clone);
709 36         1113 $ret = $code->($self, $list, @args);
710              
711 30 100       781 $self->error($self->ppi)
712             if $self->ppi != $ppi->finish;
713              
714             # There is no public way to do this as of PPI 1.204_06. I'll add one to the
715             # next release, 1.205 (or so)
716 29         428 $list->{finish} = $self->consume_token->clone;
717             } else {
718             # Just clone the entire [] or {}
719 2         14 $ret = $ppi->clone;
720 2         131 $self->_set_ppi($ppi->finish);
721 2         58 $self->consume_token;
722             }
723              
724 31         1047 return $ret;
725             }
726              
727             # Work out what sort of production we are in for sane default error messages
728             sub _parsing_area {
729 19     19   119 shift;
730 19   50     52 my $height = shift || 0;
731 19         100 my (undef, undef, undef, $sub) = caller($height+$ERROR_LEVEL);
732              
733 19 100       947 return "type constraint" if $sub =~ /(?:\b|_)tc(?:\b|_)/;
734 15 100       66 return "unpacked parameter"
735             if $sub =~ /(?:\b|_)unpacked(?:\b|_)/;
736 14 100       76 return "parameter" if $sub =~ /(?:\b|_)param(?:\b|_)/;
737 9 50       90 return "signature" if $sub =~ /(?:\b|_)signature(?:\b|_)/;
738              
739 0         0 " unknown production ($sub)";
740             }
741              
742             # error(PPI::Token $token, Str $msg?, Bool $no_in = 0)
743             sub error {
744 30     30 0 3040 my ($self, $token, $msg, $no_in) = @_;
745              
746 30 100       102 $msg = "Error parsing " . $self->_parsing_area(2)
747             unless ($msg);
748              
749              
750 30 100       137 $msg = $msg . " near '$token'" .
751             ($no_in ? ""
752             : " in '" . $token->statement . "'"
753             );
754              
755 30 50       1652 if ($DEBUG) {
756 0         0 Carp::confess($msg);
757             } else {
758 30         703 Carp::croak($msg);
759             }
760             }
761              
762             sub assert_token {
763 265     265 0 423 my ($self, $need, $msg) = @_;
764              
765 265 100       6876 if ($self->ppi->content ne $need) {
766 9         250 $self->error($self->ppi, "'$need' expected whilst parsing " . $self->_parsing_area(2));
767             }
768 256         1317 return $self->consume_token;
769             }
770              
771              
772             %LEXTABLE = (
773             where => 'WHERE',
774             is => 'TRAIT',
775             does => 'TRAIT',
776             );
777              
778             sub _ident {
779 270     270   392 my ($self) = @_;
780              
781 270         7020 my $ppi = $self->ppi;
782 270 100       785 return $self->consume_token
783             if $ppi->class eq 'PPI::Token::Word';
784 204         850 return undef;
785             }
786              
787             sub _consume_if_isa {
788 25     25   73 my ($self, @classes) = @_;
789              
790 25         62 for (@classes) {
791 74 100       1778 return $self->consume_token
792             if $self->ppi->isa($_);
793             }
794              
795             }
796              
797             sub consume_token {
798 833     833 0 1141 my ($self) = @_;
799              
800 833         21570 my $ppi = $self->ppi;
801 833         916 my $ret = $ppi;
802              
803 833         3218 while (!$ppi->isa('PPI::Token::EOF') ) {
804 1052         2727 $ppi = $ppi->next_token;
805 1052 100       45188 last if $ppi->significant;
806             }
807              
808 833 100 100     1894 if ($ppi->class eq 'PPI::Token::Word' && exists $LEXTABLE{"$ppi"}) {
809 22         266 bless $ppi, "PPI::Token::LexSymbol";
810 22         179 $ppi->{lex} = $LEXTABLE{"$ppi"};
811             }
812 833         30202 $self->_set_ppi( $ppi );
813 833         2203 return $ret;
814             }
815              
816             sub remaining_input {
817 0     0 0 0 my $tok = $_[0]->ppi;
818 0         0 my $buff;
819              
820 0         0 while ( !$tok->isa('PPI::Token::EOF') ) {
821 0         0 $buff .= $tok->content;
822 0         0 $tok = $tok->next_token;
823             }
824 0         0 return $buff;
825             }
826              
827             __PACKAGE__->meta->make_immutable;
828              
829              
830             # Extra PPI classes to represent what we want.
831             { package
832             PPI::Statement::Expression::TCUnion;
833 3     3   11649 use base 'PPI::Statement::Expression';
  3         5  
  3         384  
834              
835             sub content {
836 6     6   365 join('|', $_[0]->children );
837             }
838             }
839              
840             { package
841             PPI::Statement::Expression::TCParams;
842            
843 3     3   16 use base 'PPI::Statement::Expression';
  3         3  
  3         162  
844 3     3   11 use Moose;
  3         8  
  3         20  
845              
846             # $self->children stores everything so PPI can track parents
847             # params just contains the keywords (not commas) inside the []
848             has type => ( is => 'ro');
849             has params => (
850             is => 'ro',
851             default => sub { [] },
852             );
853              
854             sub new {
855 15     15 0 154 my ($class, $type) = @_;
856              
857 15         68 return $class->meta->new_object(
858             __INSTANCE__ => $class->SUPER::new($type),
859             type => $type
860             );
861             };
862              
863             override add_element => sub {
864             my ($self, $ele) = @_;
865             super();
866             push @{$self->params}, $ele;
867             };
868              
869             sub content {
870 10     10 1 1734 $_[0]->type->content . '[' . join(',', @{$_[0]->params}) . ']'
  10         373  
871             }
872              
873 3     3   14740 no Moose;
  3         5  
  3         14  
874             }
875              
876             { package
877             PPI::Token::LexSymbol;
878 3     3   467 use base 'PPI::Token::Word';
  3         4  
  3         338  
879              
880             sub lex {
881 31     31   39 my ($self) = @_;
882             return $self->{lex}
883 31         169 }
884             }
885              
886             # Used for LHS of fat comma
887             { package
888             PPI::Token::StringifiedWord;
889 3     3   12 use base 'PPI::Token::Word';
  3         3  
  3         144  
890              
891 3     3   11 use Moose;
  3         3  
  3         10  
892             override content => sub {
893             return '"' . super() . '"';
894             };
895              
896             sub string {
897 1     1 0 5 return $_[0]->PPI::Token::Word::content();
898             }
899 3     3   12223 no Moose;
  3         3  
  3         10  
900             }
901              
902             1;
903              
904             __END__
905              
906             =head1 NAME
907              
908             Parse::Method::Signatures - Perl6 like method signature parser
909              
910             =head1 DESCRIPTION
911              
912             Inspired by L<Perl6::Signature> but streamlined to just support the subset
913             deemed useful for L<TryCatch> and L<MooseX::Method::Signatures>.
914              
915             =head1 TODO
916              
917             =over
918              
919             =item * Document the parameter return types.
920              
921             =item * Probably lots of other things
922              
923             =back
924              
925             =head1 METHODS
926              
927             There are only two public methods to this module, both of which should be
928             called as class methods. Both methods accept either a single (non-ref) scalar
929             as the value for the L</input> attribute, or normal new style arguments (hash
930             or hash-ref).
931              
932             =head2 signature
933              
934             my $sig = Parse::Method::Signatures->signature( '(Str $foo)' )
935              
936             Attempts to parse the (bracketed) method signature. Returns a value or croaks
937             on error.
938              
939             =head2 param
940              
941             my $param = Parse::Method::Signatures->param( 'Str $foo where { length($_) < 10 }')
942              
943             Attempts to parse the specification for a single parameter. Returns value or
944             croaks on error.
945              
946             =head1 ATTRIBUTES
947              
948             All the attributes on this class are read-only.
949              
950             =head2 input
951              
952             B<Type:> Str
953              
954             The string to parse.
955              
956             =head2 offset
957              
958             B<Type:> Int
959              
960             Offset into L</input> at which to start parsing. Useful for using with
961             Devel::Declare linestring
962              
963             =head2 signature_class
964              
965             B<Default:> Parse::Method::Signatures::Sig
966              
967             B<Type:> Str (loaded on demand class name)
968              
969             =head2 param_class
970              
971             B<Default:> Parse::Method::Signatures::Param
972              
973             B<Type:> Str (loaded on demand class name)
974              
975             =head2 type_constraint_class
976              
977             B<Default:> L<Parse::Method::Signatures::TypeConstraint>
978              
979             B<Type:> Str (loaded on demand class name)
980              
981             Class that is used to turn the parsed type constraint into an actual
982             L<Moose::Meta::TypeConstraint> object.
983              
984             =head2 from_namespace
985              
986             B<Type:> ClassName
987              
988             Let this module know which package it is parsing signatures form. This is
989             entirely optional, and the only effect is has is on parsing type constraints.
990              
991             If this attribute is set it is passed to L</type_constraint_class> which can
992             use it to introspect the package (commonly for L<MooseX::Types> exported
993             types). See
994             L<Parse::Method::Signature::TypeConstraints/find_registered_constraint> for
995             more details.
996              
997             =head2 type_constraint_callback
998              
999             B<Type:> CodeRef
1000              
1001             Passed to the constructor of L</type_constraint_class>. Default implementation
1002             of this callback asks Moose for a type constrain matching the name passed in.
1003             If you have more complex requirements, such as parsing types created by
1004             L<MooseX::Types> then you will want a callback similar to this:
1005              
1006             # my $target_package defined elsewhere.
1007             my $tc_cb = sub {
1008             my ($pms_tc, $name) = @_;
1009             my $code = $target_package->can($name);
1010             $code ? eval { $code->() }
1011             : $pms_tc->find_registered_constraint($name);
1012             }
1013              
1014             Note that the above example is better provided by providing the
1015             L</from_namespace> attribute.
1016              
1017             =head1 CAVEATS
1018              
1019             Like Perl6::Signature, the parsing of certain constructs is currently only a
1020             'best effort' - specifically default values and where code blocks might not
1021             successfully for certain complex cases. Patches/Failing tests welcome.
1022              
1023             Additionally, default value specifications are not evaluated which means that
1024             no such lexical or similar errors will not be produced by this module.
1025             Constant folding will also not be performed.
1026              
1027             There are certain constructs that are simply too much hassle to avoid when the
1028             work around is simple. Currently the only cases that are known to parse wrong
1029             are when using anonymous variables (i.e. just sigils) in unpacked arrays. Take
1030             the following example:
1031              
1032             method foo (ArrayRef [$, $], $some_value_we_care_about) {
1033              
1034             In this case the C<$]> is treated as one of perl's magic variables
1035             (specifically, the patch level of the Perl interpreter) rather than a C<$>
1036             followed by a C<]> as was almost certainly intended. The work around for this
1037             is simple: introduce a space between the characters:
1038              
1039             method foo (ArrayRef [ $, $ ], $some_value_we_care_about) {
1040              
1041             The same applies
1042              
1043             =head1 AUTHOR
1044              
1045             Ash Berlin <ash@cpan.org>.
1046              
1047             Thanks to Florian Ragwitz <rafl@debian.org>.
1048              
1049             Many thanks to Piers Cawley to showing me the way to refactor my spaghetti
1050             code into something more manageable.
1051              
1052             =head1 SEE ALSO
1053              
1054             L<Devel::Declare> which is used by most modules that use this (currently by
1055             all modules known to the author.)
1056              
1057             L<http://github.com/ashb/trycatch/tree>.
1058              
1059             =head1 LICENSE
1060              
1061             Licensed under the same terms as Perl itself.
1062              
1063             This distribution copyright 2008-2009, Ash Berlin <ash@cpan.org>
1064