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   740786 use Moose;
  3         619406  
  3         20  
4 3         28 use MooseX::Types::Moose qw/
5             ArrayRef HashRef ScalarRef CodeRef Int Str ClassName
6 3     3   15237 /;
  3         92638  
7              
8 3     3   11386 use Class::Load qw(load_class);
  3         5  
  3         148  
9 3     3   1692 use PPI;
  3         258612  
  3         190  
10 3     3   26 use Moose::Util::TypeConstraints;
  3         3  
  3         41  
11 3     3   6837 use Parse::Method::Signatures::ParamCollection;
  3         8  
  3         135  
12 3         17 use Parse::Method::Signatures::Types qw/
13             PositionalParam NamedParam UnpackedParam
14 3     3   19 /;
  3         5  
15              
16 3     3   5038 use Carp qw/croak/;
  3         5  
  3         186  
17              
18 3     3   35 use namespace::clean -except => 'meta';
  3         5  
  3         19  
19             our $VERSION = '1.003019';
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 206 my ($self) = @_;
90              
91             load_class($_)
92 147         295 for map { $self->$_ } qw/
  441         12536  
93             signature_class
94             param_class
95             type_constraint_class
96             /;
97              
98 147         12147 my $ppi = $self->ppi;
99              
100             # Skip leading whitespace
101 147 50       4114 $self->consume_token
102             unless $ppi->significant;
103             }
104              
105             sub create_param {
106 206     206 0 248 my ($self, $args) = @_;
107              
108 206         231 my @traits;
109             push @traits, $args->{ variable_name } ? 'Bindable' : 'Placeholder'
110 206 100       913 if !exists $args->{unpacking};
    100          
111 206 100       534 push @traits, $args->{ named } ? 'Named' : 'Positional';
112             push @traits, 'Unpacked::' . $args->{unpacking}
113 206 100       493 if exists $args->{unpacking};
114              
115 206         5427 return $self->param_class->new_with_traits(traits => \@traits, %{ $args });
  206         1358  
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 212 my ($self) = @_;
128            
129 147         3824 my $input = substr($self->input, $self->offset);
130 147         920 my $doc = PPI::Document->new(\$input);
131              
132             # Append the magic EOF Token
133 147         174751 $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         3254 $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         63128 $self->_replace_magic($doc);
145              
146             # (Str :$x) yields a label of "Str :"
147             # (Foo Bar :$x) yields a label of "Bar :"
148 147         48201 $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         50783 $self->_fixup_hex($doc);
153              
154 147         58258 return $doc;
155             }
156              
157             sub _replace_regexps {
158 147     147   236 my ($self, $doc) = @_;
159              
160             REGEXP:
161 147 100       179 foreach my $node ( @{ $doc->find('Token::Regexp') || [] } ) {
  147         437  
162 2         825 my $str = $node->content;
163              
164 2 100       13 next REGEXP unless defined $node->{operator};
165              
166             # Rather annoyingly, there are *no* methods on Token::Regexp;
167 1         3 my ($word, $rest) = $str =~ /^(\Q@{[$node->{operator}]}\E)(.*)$/s;
  1         16  
168              
169 1         5 my $subdoc = PPI::Document->new(\$rest);
170 1         682 my @to_add = reverse map { $_->remove } $subdoc->children;
  1         12  
171 1         46 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         11 $node->__insert_after($_) for @to_add;
175              
176 1         84 $node->delete;
177             }
178             }
179              
180              
181             sub _replace_magic {
182 147     147   254 my ($self, $doc) = @_;
183              
184 147 100       187 foreach my $node ( @{ $doc->find('Token::Magic') || [] } ) {
  147         356  
185 17 100       7119 my ($op) = $node->content =~ /^\$([,?:!)])$/ or next;
186              
187 7         64 $node->insert_after(new PPI::Token::Operator($op));
188 7         2041 $node->insert_after(new PPI::Token::Cast('$'));
189 7         358 $node->delete;
190             }
191             }
192              
193             sub _replace_labels {
194 147     147   334 my ($self, $doc) = @_;
195              
196 147 100       168 foreach my $node ( @{ $doc->find('Token::Label') || [] } ) {
  147         384  
197 8 50       3500 my ($word, $ws) = $node->content =~ /^(.*?)(\s+)?:$/s or next;
198              
199 8         93 $node->insert_after(new PPI::Token::Operator(':'));
200 8 100       525 $node->insert_after(new PPI::Token::Whitespace($ws)) if defined $ws;
201 8         311 $node->insert_after(new PPI::Token::Word($word));
202 8         318 $node->delete;
203             }
204             }
205              
206             sub _fixup_hex {
207 147     147   294 my ($self, $doc) = @_;
208              
209 147 100       217 foreach my $node ( @{ $doc->find('Token::Number::Hex') || [] } ) {
  147         343  
210 3         1058 my $next = $node->next_token;
211 3 50 66     227 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   216 my ($self) = @_;
221 147         3921 my $ppi = $self->ppi_doc->first_token;
222              
223 147 100 100     3570 if ($ppi->class eq 'PPI::Token::Word' && exists $LEXTABLE{"$ppi"}) {
224 1         12 bless $ppi, "PPI::Token::LexSymbol";
225 1         76 $ppi->{lex} = $LEXTABLE{"$ppi"};
226             }
227 147         4588 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 32744 my $self = shift;
242              
243 120 100       4020 $self = $self->new(@_) unless blessed($self);
244              
245 120         355 $self->assert_token('(');
246              
247 120         215 my $args = {};
248 120         187 my $params = [];
249              
250 120         326 my $param = $self->param;
251              
252 108 100 100     3152 if ($param && $self->ppi->content eq ':') {
253             # That param was actually the invocant
254 13         93 $args->{invocant} = $param;
255 13 100       54 croak "Invocant cannot be named"
256             if NamedParam->check($param);
257 12 100       12027 croak "Invocant cannot be optional"
258             if !$param->required;
259 11 100       369 croak "Invocant cannot have a default value"
260             if $param->has_default_value;
261              
262 10 100 66     35 croak "Invocant must be a simple scalar"
263             if UnpackedParam->check($param) || $param->sigil ne '$';
264              
265 7         34 $self->consume_token;
266 7         16 $param = $self->param;
267              
268             }
269              
270 102 100       678 if ($param) {
271 95         214 push @$params, $param;
272              
273 95 100       3035 my $greedy = $param->sigil ne '$' ? $param : undef;
274 95         2976 my $opt_pos_param = !$param->required;
275              
276 95         2374 while ($self->ppi->content eq ',') {
277 42         265 $self->consume_token;
278              
279 42         1009 my $err_ctx = $self->ppi;
280 42         118 $param = $self->param;
281 42 100       173 $self->error($err_ctx, "Parameter expected")
282             if !$param;
283              
284 41         213 my $is_named = NamedParam->check($param);
285 41 100       36964 if (!$is_named) {
286 26 100 100     849 if ($param->required && $opt_pos_param) {
287 1         4 $self->error($err_ctx, "Invalid: Required positional param " .
288             " found after optional one");
289             }
290 25 100       82 if ($greedy) {
291 4         119 croak "Invalid: Un-named parameter '" . $param->variable_name
292             . "' after greedy '"
293             . $greedy->variable_name . "'\n";
294             }
295             }
296              
297 36         95 push @$params, $param;
298 36   100     859 $opt_pos_param = $opt_pos_param || !$param->required;
299 36 100       1087 $greedy = $param->sigil ne '$' ? $param : undef;
300             }
301             }
302              
303 96         671 $self->assert_token(')');
304 87         215 $args->{params} = $params;
305              
306 87         2507 my $sig = $self->signature_class->new($args);
307              
308 87         2746 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 422 my $self = shift;
335 232         294 my $class_meth;
336 232 100       722 unless (blessed($self)) {
337 2 50       61 $self = $self->new(@_) unless blessed($self);
338 2         2 $class_meth = 1;
339             }
340              
341             # Also used to check if a anything has been consumed
342 232         5558 my $err_ctx = $self->ppi;
343              
344 232         662 my $param = {
345             required => 1,
346             };
347              
348 232         668 $self->_param_typed($param);
349              
350 230 50 66     671 $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         669 $self->_param_default($param);
359 209         1219 $self->_param_constraint_or_traits($param);
360              
361 206         501 $param = $self->create_param($param);
362              
363 206 50       276876 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   501 my ($self, $param) = @_;
372              
373 219 100       783 return unless $param;
374              
375 209 100       5489 if ($self->ppi->class eq 'PPI::Token::Operator') {
376 115         3075 my $c = $self->ppi->content;
377 115 100       684 if ($c eq '?') {
    100          
378 9         19 $param->{required} = 0;
379 9         18 $self->consume_token;
380             } elsif ($c eq '!') {
381 7         12 $param->{required} = 1;
382 7         16 $self->consume_token;
383             }
384             }
385 209         848 return $param;
386              
387             }
388              
389             sub _param_constraint_or_traits {
390 209     209   425 my ($self, $param) = @_;
391              
392 209   100     547 while ($self->_param_where($param) ||
393             $self->_param_traits($param) ) {
394             # No op;
395              
396             }
397 206         261 return $param;
398             }
399              
400             sub _param_where {
401 228     228   244 my ($self, $param) = @_;
402              
403 228 100 100     5378 return unless $self->ppi->isa('PPI::Token::LexSymbol')
404             && $self->ppi->lex eq 'WHERE';
405              
406 13         26 $self->consume_token;
407              
408 13   100     63 $param->{constraints} ||= [];
409              
410 13         308 my $ppi = $self->ppi;
411              
412 13 100 66     27 $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         165 $ppi = $ppi->parent;
418              
419 12 100       68 $ppi->finish or $self->error($ppi,
420             "Runaway '" . $ppi->braces . "' in " . $self->_parsing_area(1), 1);
421              
422 11         47 push @{$param->{constraints}}, $ppi->content;
  11         39  
423              
424 11         531 $self->_set_ppi($ppi->finish);
425 11         20 $self->consume_token;
426 11         43 return $param;
427             }
428              
429             sub _param_traits {
430 215     215   247 my ($self, $param) = @_;
431 215 100 66     5047 return unless $self->ppi->isa('PPI::Token::LexSymbol')
432             && $self->ppi->lex eq 'TRAIT';
433              
434 9         23 my $op = $self->consume_token->content;
435              
436 9 100       232 $self->error($self->ppi, "Error parsing parameter trait")
437             unless $self->ppi->isa('PPI::Token::Word');
438              
439 8   100     35 $param->{param_traits} ||= [];
440              
441 8         11 push @{$param->{param_traits}}, [$op, $self->consume_token->content];
  8         22  
442 8         73 return $param;
443             }
444              
445             sub _param_labeled {
446 230     230   303 my ($self, $param) = @_;
447              
448             return unless
449 230 100 100     5542 $self->ppi->content eq ':' &&
450             $self->ppi->next_token->isa('PPI::Token::Word');
451              
452 14         497 $self->consume_token;
453              
454 14 50       343 $self->error($self->ppi, "Invalid label")
455             if $self->ppi->content =~ /[^-\w]/;
456              
457 14         88 $param->{named} = 1;
458 14         26 $param->{required} = 0;
459 14         26 $param->{label} = $self->consume_token->content;
460              
461 14         58 $self->assert_token('(');
462 14 100 100     39 $self->_unpacked_param($param)
463             || $self->_param_variable($param)
464             || $self->error($self->ppi);
465              
466 9         69 $self->assert_token(')');
467              
468 9         52 return $param;
469             }
470              
471             sub _unpacked_param {
472 38     38   77 my ($self, $param) = @_;
473              
474 38   100     142 return $self->bracketed('[', \&unpacked_array, $param) ||
475             $self->bracketed('{', \&unpacked_hash, $param);
476             }
477              
478             sub _param_named {
479 216     216   3614 my ($self, $param) = @_;
480              
481             return unless
482 216 100 100     5203 $self->ppi->content eq ':' &&
483             $self->ppi->next_token->isa('PPI::Token::Symbol');
484              
485 45         1348 $param->{required} = 0;
486 45         114 $param->{named} = 1;
487 45         97 $self->consume_token;
488              
489 45         1098 my $err_ctx = $self->ppi;
490 45         143 $param = $self->_param_variable($param);
491              
492             $self->error($err_ctx, "Arrays or hashes cannot be named")
493 45 100       174 if $param->{sigil} ne '$';
494              
495 42         277 return $param;
496             }
497              
498             sub _param_typed {
499 232     232   317 my ($self, $param) = @_;
500              
501 232 100       676 my $tc = $self->tc
502             or return;
503              
504              
505 29 100       943 $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         70 $param->{type_constraints} = $tc;
517              
518 29         46 return $param;
519             }
520            
521             sub _param_default {
522 209     209   247 my ($self, $param) = @_;
523              
524 209 100       5019 return unless $self->ppi->content eq '=';
525              
526 25         132 $self->consume_token;
527              
528             $param->{default_value} =
529 25 50 66     89 $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         68 $param->{default_value} = $param->{default_value}->content;
542             }
543              
544              
545             sub _param_variable {
546 222     222   1738 my ($self, $param) = @_;
547              
548 222         5304 my $ppi = $self->ppi;
549 222         470 my $class = $ppi->class;
550 222 100 100     1244 return unless $class eq 'PPI::Token::Symbol'
551             || $class eq 'PPI::Token::Cast';
552              
553 197 100       535 if ($class eq 'PPI::Token::Symbol') {
554 187 100       678 $ppi->symbol_type eq $ppi->raw_type or $self->error($ppi);
555              
556 186         11158 $param->{sigil} = $ppi->raw_type;
557 186         942 $param->{variable_name} = $self->consume_token->content;
558             } else {
559 10         25 $param->{sigil} = $self->consume_token->content;
560             }
561              
562 196         1326 return $param;
563             }
564              
565             sub unpacked_hash {
566 9     9 0 15 my ($self, $list, $param) = @_;
567              
568 9         12 my $params = [];
569 9         260 while ($self->ppi->content ne '}') {
570 20         533 my $errctx = $self->ppi;
571 20 50       57 my $p = $self->param
572             or $self->error($self->ppi);
573              
574 20 100 100     655 $self->error($errctx, "Cannot have positional parameters in an unpacked-array")
575             if $p->sigil eq '$' && PositionalParam->check($p);
576 19         10888 push @$params, $p;
577              
578 19 100       537 last if $self->ppi->content eq '}';
579 11         95 $self->assert_token(',');
580             }
581 8         55 $param->{params} = $params;
582 8         18 $param->{sigil} = '$';
583 8         15 $param->{unpacking} = 'Hash';
584 8         19 return $param;
585             }
586              
587             sub unpacked_array {
588 12     12 0 24 my ($self, $list, $param) = @_;
589              
590 12         18 my $params = [];
591 12         319 while ($self->ppi->content ne ']') {
592 27         699 my $watermark = $self->ppi;
593 27 50       66 my $param = $self->param
594             or $self->error($self->ppi);
595              
596 26 100       114 $self->error($watermark, "Cannot have named parameters in an unpacked-array")
597             if NamedParam->check($param);
598              
599 24 100       23027 $self->error($watermark, "Cannot have optional parameters in an unpacked-array")
600             unless $param->required;
601              
602 23         43 push @$params, $param;
603              
604 23 100       586 last if $self->ppi->content eq ']';
605 15         99 $self->assert_token(',');
606             }
607 8         54 $param->{params} = $params;
608 8         19 $param->{sigil} = '$';
609 8         16 $param->{unpacking} = 'Array';
610 8         20 return $param;
611             }
612              
613             sub tc {
614 267     267 0 376 my ($self, $required) = @_;
615              
616 267         710 my $ident = $self->_ident;
617              
618 267 50 66     1537 $ident or ($required and $self->error($self->ppi)) or return;
      66        
619              
620 64   66     239 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   98 my ($self, $list, $tc) = @_;
632              
633 15         42 my $new = PPI::Statement::Expression::TCParams->new($tc->clone);
634              
635 15 100       4380 return $new if $self->ppi->content eq ']';
636              
637 14         86 $new->add_element($self->_tc_param);
638              
639 13         335 while ($self->ppi->content =~ /^,|=>$/ ) {
640              
641 6         40 my $op = $self->consume_token;
642 6 100       15 $self->_stringify_last($new) if $op->content eq '=>';
643              
644 6         23 $new->add_element($self->tc(1));
645             }
646              
647 13         69 return $new;
648             }
649              
650             # Valid token for individual component of parameterized TC
651             sub _tc_param {
652 14     14   16 my ($self) = @_;
653              
654 14         353 (my $class = $self->ppi->class) =~ s/^PPI:://;
655 14 100 100     164 return $self->consume_token->clone
656             if $class eq 'Token::Number' ||
657             $class =~ /^Token::Quote::(?:Single|Double|Literal|Interpolate)/;
658              
659 12         34 return $self->tc(1);
660             }
661              
662             sub _tc_union {
663 61     61   1007 my ($self, $tc) = @_;
664            
665 61 100       1602 return $tc unless $self->ppi->content eq '|';
666              
667 9         96 my $union = PPI::Statement::Expression::TCUnion->new;
668 9         97 $union->add_element($tc);
669 9         612 while ( $self->ppi->content eq '|' ) {
670            
671 9         47 $self->consume_token;
672 9         27 $union->add_element($self->tc(1));
673             }
674              
675 8         56 return $union;
676             }
677              
678             # Stringify LHS of fat comma
679             sub _stringify_last {
680 4     4   30 my ($self, $list) = @_;
681 4         29 my $last = $list->last_token;
682 4 100       78 return unless $last->isa('PPI::Token::Word');
683              
684             # Is this conditional on the content of the word?
685 3         13 bless $last, "PPI::Token::StringifiedWord";
686 3         8 return $list;
687             }
688              
689             # Handle the boring bits of bracketed product, then call $code->($self, ...)
690             sub bracketed {
691 131     131 0 390 my ($self, $type, $code, @args) = @_;
692              
693 131         185 local $ERROR_LEVEL = $ERROR_LEVEL + 1;
694 131         3282 my $ppi = $self->ppi;
695 131 100       283 return unless $ppi->content eq $type;
696              
697 40         175 $self->consume_token; # consume '[';
698              
699             # Get from the '[' token the to Strucure::Constructor
700 40         97 $ppi = $ppi->parent;
701              
702 40 100       172 $ppi->finish or $self->error($ppi,
703             "Runaway '" . $ppi->braces . "' in " . $self->_parsing_area(1), 1);
704              
705              
706 38         170 my $ret;
707 38 100       75 if ($code) {
708 36         99 my $list = PPI::Structure::Constructor->new($ppi->start->clone);
709 36         965 $ret = $code->($self, $list, @args);
710              
711 30 100       730 $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         377 $list->{finish} = $self->consume_token->clone;
717             } else {
718             # Just clone the entire [] or {}
719 2         11 $ret = $ppi->clone;
720 2         125 $self->_set_ppi($ppi->finish);
721 2         44 $self->consume_token;
722             }
723              
724 31         924 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   76 shift;
730 19   50     44 my $height = shift || 0;
731 19         70 my (undef, undef, undef, $sub) = caller($height+$ERROR_LEVEL);
732              
733 19 100       739 return "type constraint" if $sub =~ /(?:\b|_)tc(?:\b|_)/;
734 15 100       59 return "unpacked parameter"
735             if $sub =~ /(?:\b|_)unpacked(?:\b|_)/;
736 14 100       65 return "parameter" if $sub =~ /(?:\b|_)param(?:\b|_)/;
737 9 50       72 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 2699 my ($self, $token, $msg, $no_in) = @_;
745              
746 30 100       76 $msg = "Error parsing " . $self->_parsing_area(2)
747             unless ($msg);
748              
749              
750 30 100       107 $msg = $msg . " near '$token'" .
751             ($no_in ? ""
752             : " in '" . $token->statement . "'"
753             );
754              
755 30 50       1343 if ($DEBUG) {
756 0         0 Carp::confess($msg);
757             } else {
758 30         549 Carp::croak($msg);
759             }
760             }
761              
762             sub assert_token {
763 265     265 0 436 my ($self, $need, $msg) = @_;
764              
765 265 100       6433 if ($self->ppi->content ne $need) {
766 9         238 $self->error($self->ppi, "'$need' expected whilst parsing " . $self->_parsing_area(2));
767             }
768 256         1385 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   282 my ($self) = @_;
780              
781 270         6810 my $ppi = $self->ppi;
782 270 100       596 return $self->consume_token
783             if $ppi->class eq 'PPI::Token::Word';
784 204         934 return undef;
785             }
786              
787             sub _consume_if_isa {
788 25     25   84 my ($self, @classes) = @_;
789              
790 25         58 for (@classes) {
791 74 100       1804 return $self->consume_token
792             if $self->ppi->isa($_);
793             }
794              
795             }
796              
797             sub consume_token {
798 833     833 0 1008 my ($self) = @_;
799              
800 833         20169 my $ppi = $self->ppi;
801 833         819 my $ret = $ppi;
802              
803 833         3172 while (!$ppi->isa('PPI::Token::EOF') ) {
804 1052         2728 $ppi = $ppi->next_token;
805 1052 100       42703 last if $ppi->significant;
806             }
807              
808 833 100 100     1843 if ($ppi->class eq 'PPI::Token::Word' && exists $LEXTABLE{"$ppi"}) {
809 22         218 bless $ppi, "PPI::Token::LexSymbol";
810 22         154 $ppi->{lex} = $LEXTABLE{"$ppi"};
811             }
812 833         27562 $self->_set_ppi( $ppi );
813 833         2174 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   11670 use base 'PPI::Statement::Expression';
  3         4  
  3         415  
834              
835             sub content {
836 6     6   461 join('|', $_[0]->children );
837             }
838             }
839              
840             { package
841             PPI::Statement::Expression::TCParams;
842            
843 3     3   16 use base 'PPI::Statement::Expression';
  3         2  
  3         172  
844 3     3   13 use Moose;
  3         7  
  3         22  
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 133 my ($class, $type) = @_;
856              
857 15         59 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 1437 $_[0]->type->content . '[' . join(',', @{$_[0]->params}) . ']'
  10         340  
871             }
872              
873 3     3   15147 no Moose;
  3         6  
  3         13  
874             }
875              
876             { package
877             PPI::Token::LexSymbol;
878 3     3   469 use base 'PPI::Token::Word';
  3         4  
  3         335  
879              
880             sub lex {
881 31     31   39 my ($self) = @_;
882             return $self->{lex}
883 31         144 }
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         11  
  3         141  
890              
891 3     3   11 use Moose;
  3         4  
  3         12  
892             override content => sub {
893             return '"' . super() . '"';
894             };
895              
896             sub string {
897 1     1 0 6 return $_[0]->PPI::Token::Word::content();
898             }
899 3     3   12432 no Moose;
  3         6  
  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