From 8f7a0b8302cea6dcac92ff7d9466f7f32519d27e Mon Sep 17 00:00:00 2001 From: "Michael G. Schwern" Date: Thu, 6 Mar 2014 01:56:58 -0800 Subject: [PATCH 01/15] has_named is never set, this code is dead. If it uses ->{overall}{num_named} it breaks the yadayada handling. Must be a leftover from before yadayada. --- lib/Method/Signatures.pm | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/Method/Signatures.pm b/lib/Method/Signatures.pm index 8de221e..638a7ac 100644 --- a/lib/Method/Signatures.pm +++ b/lib/Method/Signatures.pm @@ -1024,8 +1024,6 @@ sub inject_from_signature { if $signature->{overall}{num_named} && !$signature->{overall}{yadayada}; } - push @code, $class . '->named_param_error(\%args) if keys %args;' if $signature->{overall}{has_named}; - my $max_argv = $signature->{overall}{max_argv_size}; my $max_args = $signature->{overall}{max_args}; push @code, qq[$class->too_many_args_error($max_args) if \@_ > $max_argv; ] From 40b89088ec7b182b708e6e592a92eacd323cc772 Mon Sep 17 00:00:00 2001 From: "Michael G. Schwern" Date: Thu, 6 Mar 2014 01:58:57 -0800 Subject: [PATCH 02/15] Refactor $self->{signature} into Method::Signatures::Signature MS::Signature is just a basic data container object for now to prepare for moving the functionality out of MS and into MS::Signature. --- lib/Method/Signatures.pm | 110 +++++++++++---------------- lib/Method/Signatures/Signature.pm | 118 +++++++++++++++++++++++++++++ lib/Method/Signatures/Types.pm | 9 +++ t/types.t | 3 +- 4 files changed, 172 insertions(+), 68 deletions(-) create mode 100644 lib/Method/Signatures/Signature.pm create mode 100644 lib/Method/Signatures/Types.pm diff --git a/lib/Method/Signatures.pm b/lib/Method/Signatures.pm index 638a7ac..4f6b9d2 100644 --- a/lib/Method/Signatures.pm +++ b/lib/Method/Signatures.pm @@ -7,6 +7,7 @@ use Lexical::SealRequireHints; use base 'Devel::Declare::MethodInstaller::Simple'; use Method::Signatures::Parser; use Method::Signatures::Parameter; +use Method::Signatures::Signature; our $VERSION = '20140224'; @@ -814,16 +815,17 @@ sub parse_signature { my $self = shift; my %args = @_; my @protos = $self->_split_proto($args{proto} || []); - my $signature = $args{signature} || {}; + my $signature = $args{signature} || Method::Signatures::Signature->new(); # JIC there's anything we need to pull out before the invocant # (primary example would be the $orig for around modifiers in Moose/Mouse - $signature->{pre_invocant} = $args{pre_invocant}; + $signature->pre_invocant($args{pre_invocant}); # Special case for methods, they will pass in an invocant to use as the default - if( $signature->{invocant} = $args{invocant} ) { + if( $args{invocant} ) { + $signature->invocant($args{invocant}); if( @protos ) { - $signature->{invocant} = $_ for extract_invocant(\$protos[0]); + $signature->invocant($_) for extract_invocant(\$protos[0]); shift @protos unless $protos[0] =~ /\S/; } } @@ -854,18 +856,7 @@ sub parse_func { my $self = shift; my %args = @_; my @protos = $self->_split_proto($args{proto} || []); - my $signature = $args{signature} || {}; - - $signature->{named} = []; - $signature->{positional} = []; - $signature->{overall} = { - num_optional => 0, - num_optional_positional => 0, - num_named => 0, - num_positional => 0, - has_invocant => $signature->{invocant} ? 1 : 0, - num_slurpy => 0 - }; + my $signature = $args{signature} || Method::Signatures::Signature->new; my $idx = 0; for my $proto (@protos) { @@ -877,28 +868,23 @@ sub parse_func { ); $idx++ if $sig->is_positional; + push @{$signature->parameters}, $sig; + # Handle "don't care" specifier if ($sig->is_yadayada) { - $signature->{overall}{num_slurpy}++; - $signature->{overall}{yadayada}++; + push @{$signature->slurpy_parameters}, $sig; + push @{$signature->yadayada_parameters}, $sig; next; } $self->_check_sig($sig, $signature); - if( $sig->is_named ) { - push @{$signature->{named}}, $sig; - } - else { - push @{$signature->{positional}}, $sig; - } - - my $overall = $signature->{overall}; - $overall->{num_optional}++ if $sig->is_optional; - $overall->{num_named}++ if $sig->is_named; - $overall->{num_positional}++ if $sig->is_positional; - $overall->{num_optional_positional}++ if $sig->is_optional and $sig->is_positional; - $overall->{num_slurpy}++ if $sig->is_slurpy; + push @{$signature->named_parameters}, $sig if $sig->is_named; + push @{$signature->positional_parameters}, $sig if $sig->is_positional; + push @{$signature->optional_parameters}, $sig if $sig->is_optional; + push @{$signature->optional_positional_parameters}, $sig + if $sig->is_optional and $sig->is_positional; + push @{$signature->slurpy_parameters}, $sig if $sig->is_slurpy; DEBUG( "sig: ", $sig ); } @@ -916,21 +902,19 @@ sub parse_func { sub _calculate_max_args { my $self = shift; - my $overall = $self->{signature}{overall}; + + my $signature = $self->{signature}; # If there's a slurpy argument, the max is infinity. - if( $overall->{num_slurpy} ) { - $overall->{max_argv_size} = $INF; - $overall->{max_args} = $INF; + if( $signature->num_slurpy ) { + $signature->max_argv_size($INF); + $signature->max_args($INF); return; } - # How big can @_ be? - $overall->{max_argv_size} = ($overall->{num_named} * 2) + $overall->{num_positional}; - - # The maximum logical arguments (name => value counts as one argument) - $overall->{max_args} = $overall->{num_named} + $overall->{num_positional}; + $signature->max_argv_size( ($signature->num_named * 2) + $signature->num_positional ); + $signature->max_args( $signature->num_named + $signature->num_positional ); return; } @@ -942,21 +926,21 @@ sub _check_sig { if( $sig->is_slurpy ) { sig_parsing_error("Signature can only have one slurpy parameter") - if $signature->{overall}{num_slurpy} >= 1; + if $signature->num_slurpy >= 1; sig_parsing_error("Slurpy parameter '@{[$sig->variable]}' cannot be named; use a reference instead") if $sig->is_named; } if( $sig->is_named ) { - if( $signature->{overall}{num_optional_positional} ) { - my $pos_var = $signature->{positional}[-1]->variable; + if( $signature->num_optional_positional ) { + my $pos_var = $signature->positional_parameters->[-1]->variable; my $var = $sig->variable; sig_parsing_error("Named parameter '$var' mixed with optional positional '$pos_var'"); } } else { - if( $signature->{overall}{num_named} ) { - my $named_var = $signature->{named}[-1]->variable; + if( $signature->num_named ) { + my $named_var = $signature->named_parameters->[-1]->variable; my $var = $sig->variable; sig_parsing_error("Positional parameter '$var' after named param '$named_var'"); } @@ -967,29 +951,21 @@ sub _check_sig { # Check the integrity of the signature as a whole sub _check_signature { my $self = shift; + my $signature = $self->{signature}; - my $overall = $signature->{overall}; # Check that slurpy arguments come at the end if( - $overall->{num_slurpy} && - !($overall->{yadayada} || $signature->{positional}[-1]->is_slurpy) + $signature->num_slurpy && + !($signature->num_yadayada || $signature->positional_parameters->[-1]->is_slurpy) ) { - my($slurpy_param) = $self->_find_slurpy_params; + my $slurpy_param = $signature->slurpy_parameters->[0]; sig_parsing_error("Slurpy parameter '@{[$slurpy_param->variable]}' must come at the end"); } } -sub _find_slurpy_params { - my $self = shift; - my $signature = $self->{signature}; - - return grep { $_->is_slurpy } @{ $signature->{named} }, @{ $signature->{positional} }; -} - - # Turn the parsed signature into Perl code sub inject_from_signature { my $self = shift; @@ -997,16 +973,16 @@ sub inject_from_signature { my $signature = shift; my @code; - push @code, "my $signature->{pre_invocant} = shift;" if $signature->{pre_invocant}; - push @code, "my $signature->{invocant} = shift;" if $signature->{invocant}; + push @code, "my @{[$signature->pre_invocant]} = shift;" if $signature->pre_invocant; + push @code, "my @{[$signature->invocant]} = shift;" if $signature->invocant; - for my $sig (@{$signature->{positional}}) { + for my $sig (@{$signature->positional_parameters}) { push @code, $self->inject_for_sig($sig); } - if( @{$signature->{named}} ) { - my $first_named_idx = @{$signature->{positional}}; - if (grep { $_->is_ref_alias or $_->traits->{alias} } @{$signature->{named}}) + if( @{$signature->named_parameters} ) { + my $first_named_idx = @{$signature->positional_parameters}; + if (grep { $_->is_ref_alias or $_->traits->{alias} } @{$signature->named_parameters}) { require Data::Alias; push @code, "Data::Alias::alias( my (\%args) = \@_[$first_named_idx..\$#_] );"; @@ -1016,16 +992,16 @@ sub inject_from_signature { push @code, "my (\%args) = \@_[$first_named_idx..\$#_];"; } - for my $sig (@{$signature->{named}}) { + for my $sig (@{$signature->named_parameters}) { push @code, $self->inject_for_sig($sig); } push @code, $class . '->named_param_error(\%args) if keys %args;' - if $signature->{overall}{num_named} && !$signature->{overall}{yadayada}; + if $signature->num_named && !$signature->num_yadayada; } - my $max_argv = $signature->{overall}{max_argv_size}; - my $max_args = $signature->{overall}{max_args}; + my $max_argv = $signature->max_argv_size; + my $max_args = $signature->max_args; push @code, qq[$class->too_many_args_error($max_args) if \@_ > $max_argv; ] unless $max_argv == $INF; diff --git a/lib/Method/Signatures/Signature.pm b/lib/Method/Signatures/Signature.pm new file mode 100644 index 0000000..1df654d --- /dev/null +++ b/lib/Method/Signatures/Signature.pm @@ -0,0 +1,118 @@ +package Method::Signatures::Signature; + +use Mouse; +use Method::Signatures::Types; + +my $INF = ( 0 + "inf" ) == 0 ? 9e9999 : "inf"; + +# The unmodified, uncleaned up original signature for reference +has signature_string => + is => 'ro', + isa => 'Str'; + +# Just the parameter part of the signature, no invocant +has parameter_string => + is => 'ro', + isa => 'Str'; + +# A list of strings for each parameter tokenized from parameter_string +has parameter_strings => + is => 'ro', + isa => 'ArrayRef', + default => sub { [] }; + +# The parsed Method::Signature::Parameter objects +has parameters => + is => 'ro', + isa => 'ArrayRef[Method::Signatures::Parameter]', + default => sub { [] }; + +has named_parameters => + is => 'ro', + isa => 'ArrayRef[Method::Signatures::Parameter]', + default => sub { [] }; + +has positional_parameters => + is => 'ro', + isa => 'ArrayRef[Method::Signatures::Parameter]', + default => sub { [] }; + +has optional_parameters => + is => 'ro', + isa => 'ArrayRef[Method::Signatures::Parameter]', + default => sub { [] }; + +has optional_positional_parameters => + is => 'ro', + isa => 'ArrayRef[Method::Signatures::Parameter]', + default => sub { [] }; + +has slurpy_parameters => + is => 'ro', + isa => 'ArrayRef[Method::Signatures::Parameter]', + default => sub { [] }; + +has yadayada_parameters => + is => 'ro', + isa => 'ArrayRef[Method::Signatures::Parameter]', + default => sub { [] }; + + +sub num_named { + return scalar @{$_[0]->named_parameters}; +} + +sub num_positional { + return scalar @{$_[0]->positional_parameters}; +} + +sub num_optional { + return scalar @{$_[0]->optional_parameters}; +} + +sub num_optional_positional { + return scalar @{$_[0]->optional_positional_parameters}; +} + +sub num_slurpy { + return scalar @{$_[0]->slurpy_parameters}; +} + +sub num_yadayada { + return scalar @{$_[0]->yadayada_parameters}; +} + +# Anything we need to pull out before the invocant. +# Primary example would be the $orig for around modifiers in Moose/Mouse +has pre_invocant => + is => 'rw', + isa => 'Maybe[Str]', + default => ''; + +has is_method => + is => 'rw', + isa => 'Bool', + default => 0; + +has invocant => + is => 'rw', + isa => 'Str', + default => sub { + return $_[0]->is_method ? '$self' : ''; + }; + +sub has_invocant { + return $_[0]->invocant ? 1 : 0; +} + +# How big can @_ be? +has max_argv_size => + is => 'rw', + isa => 'Int|Inf'; + +# The maximum logical arguments (name => value counts as one argument) +has max_args => + is => 'rw', + isa => 'Int|Inf'; + +1; diff --git a/lib/Method/Signatures/Types.pm b/lib/Method/Signatures/Types.pm new file mode 100644 index 0000000..76be401 --- /dev/null +++ b/lib/Method/Signatures/Types.pm @@ -0,0 +1,9 @@ +package Method::Signatures::Types; + +use Mouse::Util::TypeConstraints; + +subtype 'Inf', + as 'Str', + where { $_ eq 'inf' }; + +1; diff --git a/t/types.t b/t/types.t index 73dc1e0..6ac1fe2 100644 --- a/t/types.t +++ b/t/types.t @@ -25,7 +25,8 @@ note "types"; { my $which = shift @$want; for my $idx (0..$#{$want}) { - is $ms->{signature}{$which}[$idx]->type, $want->[$idx] || ''; + my $method = $which.'_parameters'; + is $ms->{signature}->$method->[$idx]->type, $want->[$idx] || ''; } } } From bf7e527c34ebc756bc11f19856b2484a7e8d132e Mon Sep 17 00:00:00 2001 From: "Michael G. Schwern" Date: Thu, 6 Mar 2014 11:17:19 -0800 Subject: [PATCH 03/15] Make the order of evaluating where clauses stable. This turns out to be important for neg_and_odd_prime() in t/where.t and in general our order of evaluation should be predictable. --- lib/Method/Signatures.pm | 2 +- lib/Method/Signatures/Parameter.pm | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/Method/Signatures.pm b/lib/Method/Signatures.pm index 4f6b9d2..fe93ea7 100644 --- a/lib/Method/Signatures.pm +++ b/lib/Method/Signatures.pm @@ -1118,7 +1118,7 @@ sub inject_for_sig { # Handle 'where' constraints (after defaults are resolved) if ( $sig->where ) { - for my $constraint ( keys %{$sig->where} ) { + for my $constraint ( @{$sig->where} ) { # Handle 'where { block using $_ }' my $constraint_impl = $constraint =~ m{^ \s* \{ (?: .* ; .* | (?:(?! => ). )* ) \} \s* $}xs diff --git a/lib/Method/Signatures/Parameter.pm b/lib/Method/Signatures/Parameter.pm index 3644cc8..ccb52a6 100644 --- a/lib/Method/Signatures/Parameter.pm +++ b/lib/Method/Signatures/Parameter.pm @@ -90,13 +90,13 @@ has variable_name => has where => is => 'rw', - isa => 'HashRef[Int]', - default => sub { {} }; + isa => 'ArrayRef', + default => sub { [] }; sub has_where { my $self = shift; - return keys %{$self->where} ? 1 : 0; + return @{$self->where} ? 1 : 0; } has traits => @@ -231,7 +231,7 @@ sub _parse_with_ppi { while ($self->_extract_leading(qr{^ where $}x, $tokens)) { sig_parsing_error("'where' constraint only available under Perl 5.10 or later. Error") if $] < 5.010; - $self->where->{ $self->_extract_until(qr{^ (?: where | is | = | //= ) $}x, $tokens) }++; + push @{$self->where}, $self->_extract_until(qr{^ (?: where | is | = | //= ) $}x, $tokens); } # Extract parameter traits... From fa80c0d56a663940cf54a62e0b95dae773ce4ce7 Mon Sep 17 00:00:00 2001 From: "Michael G. Schwern" Date: Thu, 6 Mar 2014 11:18:48 -0800 Subject: [PATCH 04/15] Remove the unnecessary (and broken) check to see if we have where clauses. Checking 'if $sig->where' will always return true, so the clause was a waste of time anyway. $sig->where is always initialized, no reason to spend the extra time checking if there's anything in it, the for loop will do that for us. Save a method call. --- lib/Method/Signatures.pm | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/Method/Signatures.pm b/lib/Method/Signatures.pm index fe93ea7..363d70a 100644 --- a/lib/Method/Signatures.pm +++ b/lib/Method/Signatures.pm @@ -1117,16 +1117,14 @@ sub inject_for_sig { push @code, "delete( $deletion_target );" if $deletion_target; # Handle 'where' constraints (after defaults are resolved) - if ( $sig->where ) { - for my $constraint ( @{$sig->where} ) { - # Handle 'where { block using $_ }' - my $constraint_impl = - $constraint =~ m{^ \s* \{ (?: .* ; .* | (?:(?! => ). )* ) \} \s* $}xs - ? "sub $constraint" - : $constraint; - my $error = sprintf q{ %s->where_error(%s, '%s', '%s') }, $class, $var, $var, $constraint; - push @code, "$error unless do { use experimental 'smartmatch'; grep { \$_ ~~ $constraint_impl } $var }; "; - } + for my $constraint ( @{$sig->where} ) { + # Handle 'where { block using $_ }' + my $constraint_impl = + $constraint =~ m{^ \s* \{ (?: .* ; .* | (?:(?! => ). )* ) \} \s* $}xs + ? "sub $constraint" + : $constraint; + my $error = sprintf q{ %s->where_error(%s, '%s', '%s') }, $class, $var, $var, $constraint; + push @code, "$error unless do { use experimental 'smartmatch'; grep { \$_ ~~ $constraint_impl } $var }; "; } return @code; From 48f49f17dc7a371923f926ce9b8d24024d8ba824 Mon Sep 17 00:00:00 2001 From: "Michael G. Schwern" Date: Thu, 6 Mar 2014 11:21:53 -0800 Subject: [PATCH 05/15] Make sure the prototype is always defined. Makes life easier down the line, especially when it's being passed into the MS::Signature object which has a Str type. --- lib/Method/Signatures.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Method/Signatures.pm b/lib/Method/Signatures.pm index 363d70a..49ffe26 100644 --- a/lib/Method/Signatures.pm +++ b/lib/Method/Signatures.pm @@ -803,7 +803,7 @@ sub parse_proto { die $@ if _parser_is_fucked; return $self->parse_signature( - proto => $proto, + proto => defined $proto ? $proto : "", invocant => $self->{invocant}, pre_invocant => $self->{pre_invocant} ); From c916f1741512df45279aca039232238bba448590 Mon Sep 17 00:00:00 2001 From: "Michael G. Schwern" Date: Thu, 6 Mar 2014 11:23:13 -0800 Subject: [PATCH 06/15] Remove $signature->has_method Eventually we'll want the Signature to handle the default invocant logic, but for refactoring it's just getting in the way. --- lib/Method/Signatures/Signature.pm | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/Method/Signatures/Signature.pm b/lib/Method/Signatures/Signature.pm index 1df654d..7ceb242 100644 --- a/lib/Method/Signatures/Signature.pm +++ b/lib/Method/Signatures/Signature.pm @@ -89,17 +89,10 @@ has pre_invocant => isa => 'Maybe[Str]', default => ''; -has is_method => - is => 'rw', - isa => 'Bool', - default => 0; - has invocant => is => 'rw', isa => 'Str', - default => sub { - return $_[0]->is_method ? '$self' : ''; - }; + default => ''; sub has_invocant { return $_[0]->invocant ? 1 : 0; From 558dd3d35823a5491301215aabaefe8c1ed8ed56 Mon Sep 17 00:00:00 2001 From: "Michael G. Schwern" Date: Thu, 6 Mar 2014 11:26:17 -0800 Subject: [PATCH 07/15] Move the invocant parsing into MS::Signature. Its really "reduce the signature string down to just the parameter list". I'm kinda waffling between "do everything in one build step" and "only calculate on demand". --- lib/Method/Signatures.pm | 23 +++++++++++------------ lib/Method/Signatures/Parser.pm | 12 ------------ lib/Method/Signatures/Signature.pm | 25 +++++++++++++++++++++++-- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/lib/Method/Signatures.pm b/lib/Method/Signatures.pm index 49ffe26..b176c1c 100644 --- a/lib/Method/Signatures.pm +++ b/lib/Method/Signatures.pm @@ -814,23 +814,19 @@ sub parse_proto { sub parse_signature { my $self = shift; my %args = @_; - my @protos = $self->_split_proto($args{proto} || []); - my $signature = $args{signature} || Method::Signatures::Signature->new(); + + my $signature = $args{signature} || Method::Signatures::Signature->new( + signature_string => $args{proto} + ); # JIC there's anything we need to pull out before the invocant # (primary example would be the $orig for around modifiers in Moose/Mouse $signature->pre_invocant($args{pre_invocant}); # Special case for methods, they will pass in an invocant to use as the default - if( $args{invocant} ) { - $signature->invocant($args{invocant}); - if( @protos ) { - $signature->invocant($_) for extract_invocant(\$protos[0]); - shift @protos unless $protos[0] =~ /\S/; - } - } + $signature->invocant($args{invocant}) if $args{invocant}; - return $self->parse_func( proto => \@protos, signature => $signature ); + return $self->parse_func( signature => $signature ); } @@ -855,8 +851,11 @@ sub _split_proto { sub parse_func { my $self = shift; my %args = @_; - my @protos = $self->_split_proto($args{proto} || []); - my $signature = $args{signature} || Method::Signatures::Signature->new; + my $signature = $args{signature} || Method::Signatures::Signature->new( + signature_string => $args{proto} + ); + + my @protos = $self->_split_proto($signature->parameter_string || []); my $idx = 0; for my $proto (@protos) { diff --git a/lib/Method/Signatures/Parser.pm b/lib/Method/Signatures/Parser.pm index 92c591f..dc3ba27 100644 --- a/lib/Method/Signatures/Parser.pm +++ b/lib/Method/Signatures/Parser.pm @@ -44,18 +44,6 @@ sub split_proto { } -# Extract an invocant, if one is present... -my $IDENTIFIER = qr{ [^\W\d] \w* }x; -sub extract_invocant { - my ($param_ref) = @_; - - if ($$param_ref =~ s{ ^ (\$ $IDENTIFIER) \s* : \s* }{}x) { - return $1; - } - return; -} - - sub strip_ws { $_[0] =~ s{^\s+}{}; $_[0] =~ s{\s+$}{}; diff --git a/lib/Method/Signatures/Signature.pm b/lib/Method/Signatures/Signature.pm index 7ceb242..bbc4b89 100644 --- a/lib/Method/Signatures/Signature.pm +++ b/lib/Method/Signatures/Signature.pm @@ -8,12 +8,15 @@ my $INF = ( 0 + "inf" ) == 0 ? 9e9999 : "inf"; # The unmodified, uncleaned up original signature for reference has signature_string => is => 'ro', - isa => 'Str'; + isa => 'Str', + required => 1; # Just the parameter part of the signature, no invocant has parameter_string => is => 'ro', - isa => 'Str'; + isa => 'Str', + lazy => 1, + builder => '_build_parameter_string'; # A list of strings for each parameter tokenized from parameter_string has parameter_strings => @@ -108,4 +111,22 @@ has max_args => is => 'rw', isa => 'Int|Inf'; + +my $IDENTIFIER = qr{ [^\W\d] \w* }x; +sub _build_parameter_string { + my $self = shift; + + my $sig_string = $self->signature_string; + my $invocant; + + # Extract an invocant, if one is present. + if ($sig_string =~ s{ ^ (\$ $IDENTIFIER) \s* : \s* }{}x) { + $self->invocant($1); + } + + # The siganture, minus the invocant, is just the list of parameters + return $sig_string; +} + + 1; From b2abf2bf14802a9e74fb17d833ac0e48ca3cae84 Mon Sep 17 00:00:00 2001 From: "Michael G. Schwern" Date: Thu, 6 Mar 2014 11:31:49 -0800 Subject: [PATCH 08/15] Collapse parse_func() and parse_signature() together. No need to split them anymore. parse_signature() seemed more namey of the two. --- lib/Method/Signatures.pm | 32 ++++++++++---------------------- t/types.t | 4 ++-- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/lib/Method/Signatures.pm b/lib/Method/Signatures.pm index b176c1c..cbd0b35 100644 --- a/lib/Method/Signatures.pm +++ b/lib/Method/Signatures.pm @@ -810,26 +810,6 @@ sub parse_proto { } -# Parse a signature -sub parse_signature { - my $self = shift; - my %args = @_; - - my $signature = $args{signature} || Method::Signatures::Signature->new( - signature_string => $args{proto} - ); - - # JIC there's anything we need to pull out before the invocant - # (primary example would be the $orig for around modifiers in Moose/Mouse - $signature->pre_invocant($args{pre_invocant}); - - # Special case for methods, they will pass in an invocant to use as the default - $signature->invocant($args{invocant}) if $args{invocant}; - - return $self->parse_func( signature => $signature ); -} - - sub _split_proto { my $self = shift; my $proto = shift; @@ -847,14 +827,22 @@ sub _split_proto { } -# Parse a subroutine signature -sub parse_func { +# Parse a signature +sub parse_signature { my $self = shift; my %args = @_; + my $signature = $args{signature} || Method::Signatures::Signature->new( signature_string => $args{proto} ); + # JIC there's anything we need to pull out before the invocant + # (primary example would be the $orig for around modifiers in Moose/Mouse + $signature->pre_invocant($args{pre_invocant}); + + # Special case for methods, they will pass in an invocant to use as the default + $signature->invocant($args{invocant}) if $args{invocant}; + my @protos = $self->_split_proto($signature->parameter_string || []); my $idx = 0; diff --git a/t/types.t b/t/types.t index 6ac1fe2..23d30c5 100644 --- a/t/types.t +++ b/t/types.t @@ -21,7 +21,7 @@ note "types"; { my $want = $tests{$proto}; my $ms = Method::Signatures->new; - $ms->parse_func(proto => $proto); + $ms->parse_signature(proto => $proto); my $which = shift @$want; for my $idx (0..$#{$want}) { @@ -46,7 +46,7 @@ note "inject_for_type_check"; { } my $ms = My::MS->new; - my $code = $ms->parse_func( proto => 'Foo $this, :$bar, Foo::Bar :$foobar' ); + my $code = $ms->parse_signature( proto => 'Foo $this, :$bar, Foo::Bar :$foobar' ); like $code, qr{type_check\('\$this'\)}; like $code, qr{type_check\('\$foobar'\)}; } From 51691eef58a23b3a85c3494af5c81f51d72564c0 Mon Sep 17 00:00:00 2001 From: "Michael G. Schwern" Date: Thu, 6 Mar 2014 11:33:37 -0800 Subject: [PATCH 09/15] Simplify _split_proto() No need for the special check that the prototype has already been split, we're only calling it once and it's going away soon. --- lib/Method/Signatures.pm | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/lib/Method/Signatures.pm b/lib/Method/Signatures.pm index cbd0b35..2b5715b 100644 --- a/lib/Method/Signatures.pm +++ b/lib/Method/Signatures.pm @@ -814,16 +814,8 @@ sub _split_proto { my $self = shift; my $proto = shift; - my @protos; - if( ref $proto ) { - @protos = @$proto; - } - else { - _strip_ws($proto); - @protos = split_proto($proto); - } - - return @protos; + _strip_ws($proto); + return split_proto($proto); } @@ -843,7 +835,7 @@ sub parse_signature { # Special case for methods, they will pass in an invocant to use as the default $signature->invocant($args{invocant}) if $args{invocant}; - my @protos = $self->_split_proto($signature->parameter_string || []); + my @protos = $self->_split_proto($signature->parameter_string); my $idx = 0; for my $proto (@protos) { From a69ff61f6963629aa930b2254e34388a7cac1083 Mon Sep 17 00:00:00 2001 From: "Michael G. Schwern" Date: Thu, 6 Mar 2014 12:23:46 -0800 Subject: [PATCH 10/15] Move DEBUG into MS::Parser for all to have. MS::Signature will need it shortly. MS::Parser is rapidly becoming a utility class. --- lib/Method/Signatures.pm | 7 ------- lib/Method/Signatures/Parser.pm | 8 +++++++- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/Method/Signatures.pm b/lib/Method/Signatures.pm index 2b5715b..d16ff70 100644 --- a/lib/Method/Signatures.pm +++ b/lib/Method/Signatures.pm @@ -17,13 +17,6 @@ our @CARP_NOT; our $INF = ( 0 + "inf" ) == 0 ? 9e9999 : "inf"; -sub DEBUG { - return unless $DEBUG; - - require Data::Dumper; - print STDERR "DEBUG: ", map { ref $_ ? Data::Dumper::Dumper($_) : $_ } @_; -} - # copied from Devel::Pragma sub my_hints() { $^H |= 0x20000; diff --git a/lib/Method/Signatures/Parser.pm b/lib/Method/Signatures/Parser.pm index dc3ba27..e096058 100644 --- a/lib/Method/Signatures/Parser.pm +++ b/lib/Method/Signatures/Parser.pm @@ -5,8 +5,14 @@ use warnings; use Carp; use base qw(Exporter); -our @EXPORT = qw(split_proto split_parameter extract_invocant sig_parsing_error carp_location_for); +our @EXPORT = qw(split_proto split_parameter extract_invocant sig_parsing_error carp_location_for DEBUG); +sub DEBUG { + return unless $Method::Signatures::DEBUG; + + require Data::Dumper; + print STDERR "DEBUG: ", map { ref $_ ? Data::Dumper::Dumper($_) : $_ } @_; +} sub split_proto { my $proto = shift; From 54470e1e8fe9edafcf9adc418f5726b58df95e2e Mon Sep 17 00:00:00 2001 From: "Michael G. Schwern" Date: Thu, 6 Mar 2014 12:25:55 -0800 Subject: [PATCH 11/15] Move the signature parsing and check code into MS::Signature All the logic for parsing the signature is now in MS::Siganture. MS::Signature and MS::Parameter can now check themselves via ->check methods. Tokenizing via split_proto() remains in MS::Parser for the moment just to keep this refactoring small. --- lib/Method/Signatures.pm | 137 ++--------------------------- lib/Method/Signatures/Parameter.pm | 30 +++++++ lib/Method/Signatures/Signature.pm | 97 +++++++++++++++++++- 3 files changed, 133 insertions(+), 131 deletions(-) diff --git a/lib/Method/Signatures.pm b/lib/Method/Signatures.pm index d16ff70..37d9c7c 100644 --- a/lib/Method/Signatures.pm +++ b/lib/Method/Signatures.pm @@ -748,12 +748,6 @@ sub _do_compile_at_BEGIN { } -sub _strip_ws { - $_[0] =~ s/^\s+//; - $_[0] =~ s/\s+$//; -} - - # Sometimes a compilation error will happen but not throw an error causing the # code to continue compiling and producing an unrelated error down the road. # @@ -796,145 +790,28 @@ sub parse_proto { die $@ if _parser_is_fucked; return $self->parse_signature( - proto => defined $proto ? $proto : "", + proto => $proto, invocant => $self->{invocant}, pre_invocant => $self->{pre_invocant} ); } -sub _split_proto { - my $self = shift; - my $proto = shift; - - _strip_ws($proto); - return split_proto($proto); -} - - # Parse a signature sub parse_signature { my $self = shift; my %args = @_; - my $signature = $args{signature} || Method::Signatures::Signature->new( - signature_string => $args{proto} + $self->{signature} = Method::Signatures::Signature->new( + signature_string => defined $args{proto} ? $args{proto} : "", + pre_invocant => $args{pre_invocant}, + invocant => $args{invocant}, ); - # JIC there's anything we need to pull out before the invocant - # (primary example would be the $orig for around modifiers in Moose/Mouse - $signature->pre_invocant($args{pre_invocant}); - - # Special case for methods, they will pass in an invocant to use as the default - $signature->invocant($args{invocant}) if $args{invocant}; - - my @protos = $self->_split_proto($signature->parameter_string); - - my $idx = 0; - for my $proto (@protos) { - DEBUG( "proto: $proto\n" ); - - my $sig = Method::Signatures::Parameter->new( - original_code => $proto, - position => $idx, - ); - $idx++ if $sig->is_positional; - - push @{$signature->parameters}, $sig; - - # Handle "don't care" specifier - if ($sig->is_yadayada) { - push @{$signature->slurpy_parameters}, $sig; - push @{$signature->yadayada_parameters}, $sig; - next; - } - - $self->_check_sig($sig, $signature); - - push @{$signature->named_parameters}, $sig if $sig->is_named; - push @{$signature->positional_parameters}, $sig if $sig->is_positional; - push @{$signature->optional_parameters}, $sig if $sig->is_optional; - push @{$signature->optional_positional_parameters}, $sig - if $sig->is_optional and $sig->is_positional; - push @{$signature->slurpy_parameters}, $sig if $sig->is_slurpy; - - DEBUG( "sig: ", $sig ); - } - - $self->{signature} = $signature; - - $self->_calculate_max_args; - $self->_check_signature; - # Then turn it into Perl code - my $inject = $self->inject_from_signature($signature); - return $inject; -} - - -sub _calculate_max_args { - my $self = shift; + my $inject = $self->inject_from_signature($self->{signature}); - my $signature = $self->{signature}; - - # If there's a slurpy argument, the max is infinity. - if( $signature->num_slurpy ) { - $signature->max_argv_size($INF); - $signature->max_args($INF); - - return; - } - - $signature->max_argv_size( ($signature->num_named * 2) + $signature->num_positional ); - $signature->max_args( $signature->num_named + $signature->num_positional ); - - return; -} - - -# Check the integrity of one piece of the signature -sub _check_sig { - my($self, $sig, $signature) = @_; - - if( $sig->is_slurpy ) { - sig_parsing_error("Signature can only have one slurpy parameter") - if $signature->num_slurpy >= 1; - sig_parsing_error("Slurpy parameter '@{[$sig->variable]}' cannot be named; use a reference instead") - if $sig->is_named; - } - - if( $sig->is_named ) { - if( $signature->num_optional_positional ) { - my $pos_var = $signature->positional_parameters->[-1]->variable; - my $var = $sig->variable; - sig_parsing_error("Named parameter '$var' mixed with optional positional '$pos_var'"); - } - } - else { - if( $signature->num_named ) { - my $named_var = $signature->named_parameters->[-1]->variable; - my $var = $sig->variable; - sig_parsing_error("Positional parameter '$var' after named param '$named_var'"); - } - } -} - - -# Check the integrity of the signature as a whole -sub _check_signature { - my $self = shift; - - my $signature = $self->{signature}; - - # Check that slurpy arguments come at the end - if( - $signature->num_slurpy && - !($signature->num_yadayada || $signature->positional_parameters->[-1]->is_slurpy) - ) - { - my $slurpy_param = $signature->slurpy_parameters->[0]; - sig_parsing_error("Slurpy parameter '@{[$slurpy_param->variable]}' must come at the end"); - } + return $inject; } diff --git a/lib/Method/Signatures/Parameter.pm b/lib/Method/Signatures/Parameter.pm index ccb52a6..81bc865 100644 --- a/lib/Method/Signatures/Parameter.pm +++ b/lib/Method/Signatures/Parameter.pm @@ -336,4 +336,34 @@ sub _init_split_variable { return; } + +# Check the integrity of one piece of the signature +sub check { + my($self, $signature) = @_; + + if( $self->is_slurpy ) { + sig_parsing_error("Signature can only have one slurpy parameter") + if $signature->num_slurpy >= 1; + sig_parsing_error("Slurpy parameter '@{[$self->variable]}' cannot be named; use a reference instead") + if $self->is_named; + } + + if( $self->is_named ) { + if( $signature->num_optional_positional ) { + my $pos_var = $signature->positional_parameters->[-1]->variable; + my $var = $self->variable; + sig_parsing_error("Named parameter '$var' mixed with optional positional '$pos_var'"); + } + } + else { + if( $signature->num_named ) { + my $named_var = $signature->named_parameters->[-1]->variable; + my $var = $self->variable; + sig_parsing_error("Positional parameter '$var' after named param '$named_var'"); + } + } + + return 1; +} + 1; diff --git a/lib/Method/Signatures/Signature.pm b/lib/Method/Signatures/Signature.pm index bbc4b89..2206a8a 100644 --- a/lib/Method/Signatures/Signature.pm +++ b/lib/Method/Signatures/Signature.pm @@ -2,6 +2,7 @@ package Method::Signatures::Signature; use Mouse; use Method::Signatures::Types; +use Method::Signatures::Parser qw(split_proto sig_parsing_error DEBUG); my $INF = ( 0 + "inf" ) == 0 ? 9e9999 : "inf"; @@ -94,7 +95,7 @@ has pre_invocant => has invocant => is => 'rw', - isa => 'Str', + isa => 'Maybe[Str]', default => ''; sub has_invocant { @@ -112,6 +113,100 @@ has max_args => isa => 'Int|Inf'; +sub BUILD { + my $self = shift; + + my @protos = $self->_split_proto($self->parameter_string); + + my $idx = 0; + for my $proto (@protos) { + DEBUG( "proto: $proto\n" ); + + my $sig = Method::Signatures::Parameter->new( + original_code => $proto, + position => $idx, + ); + $idx++ if $sig->is_positional; + + push @{$self->parameters}, $sig; + + # Handle "don't care" specifier + if ($sig->is_yadayada) { + push @{$self->slurpy_parameters}, $sig; + push @{$self->yadayada_parameters}, $sig; + next; + } + + $sig->check($self); + + push @{$self->named_parameters}, $sig if $sig->is_named; + push @{$self->positional_parameters}, $sig if $sig->is_positional; + push @{$self->optional_parameters}, $sig if $sig->is_optional; + push @{$self->optional_positional_parameters}, $sig + if $sig->is_optional and $sig->is_positional; + push @{$self->slurpy_parameters}, $sig if $sig->is_slurpy; + + DEBUG( "sig: ", $sig ); + } + + $self->_calculate_max_args; + $self->check; + + return; +} + + +sub _calculate_max_args { + my $self = shift; + + # If there's a slurpy argument, the max is infinity. + if( $self->num_slurpy ) { + $self->max_argv_size($INF); + $self->max_args($INF); + + return; + } + + $self->max_argv_size( ($self->num_named * 2) + $self->num_positional ); + $self->max_args( $self->num_named + $self->num_positional ); + + return; +} + + +# Check the integrity of the signature as a whole +sub check { + my $self = shift; + + # Check that slurpy arguments come at the end + if( + $self->num_slurpy && + !($self->num_yadayada || $self->positional_parameters->[-1]->is_slurpy) + ) + { + my $slurpy_param = $self->slurpy_parameters->[0]; + sig_parsing_error("Slurpy parameter '@{[$slurpy_param->variable]}' must come at the end"); + } + + return 1; +} + + +sub _strip_ws { + $_[1] =~ s/^\s+//; + $_[1] =~ s/\s+$//; +} + + +sub _split_proto { + my $self = shift; + my $proto = shift; + + $self->_strip_ws($proto); + return split_proto($proto); +} + + my $IDENTIFIER = qr{ [^\W\d] \w* }x; sub _build_parameter_string { my $self = shift; From 0104081a75e95299b0df44c2c9aec1fb6fb91012 Mon Sep 17 00:00:00 2001 From: "Michael G. Schwern" Date: Thu, 6 Mar 2014 12:31:28 -0800 Subject: [PATCH 12/15] Eliminate the now redundant parse_signature(). parse_signature() is a better name, but all that functionality will move into MS::Signature so it's moot. --- lib/Method/Signatures.pm | 19 +++---------------- t/types.t | 4 ++-- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/lib/Method/Signatures.pm b/lib/Method/Signatures.pm index 37d9c7c..bd8a5fa 100644 --- a/lib/Method/Signatures.pm +++ b/lib/Method/Signatures.pm @@ -789,23 +789,10 @@ sub parse_proto { # Before we try to compile signatures, make sure there isn't a hidden compilation error. die $@ if _parser_is_fucked; - return $self->parse_signature( - proto => $proto, - invocant => $self->{invocant}, - pre_invocant => $self->{pre_invocant} - ); -} - - -# Parse a signature -sub parse_signature { - my $self = shift; - my %args = @_; - $self->{signature} = Method::Signatures::Signature->new( - signature_string => defined $args{proto} ? $args{proto} : "", - pre_invocant => $args{pre_invocant}, - invocant => $args{invocant}, + signature_string => defined $proto ? $proto : "", + invocant => $self->{invocant}, + pre_invocant => $self->{pre_invocant} ); # Then turn it into Perl code diff --git a/t/types.t b/t/types.t index 23d30c5..205cef2 100644 --- a/t/types.t +++ b/t/types.t @@ -21,7 +21,7 @@ note "types"; { my $want = $tests{$proto}; my $ms = Method::Signatures->new; - $ms->parse_signature(proto => $proto); + $ms->parse_proto($proto); my $which = shift @$want; for my $idx (0..$#{$want}) { @@ -46,7 +46,7 @@ note "inject_for_type_check"; { } my $ms = My::MS->new; - my $code = $ms->parse_signature( proto => 'Foo $this, :$bar, Foo::Bar :$foobar' ); + my $code = $ms->parse_proto( 'Foo $this, :$bar, Foo::Bar :$foobar' ); like $code, qr{type_check\('\$this'\)}; like $code, qr{type_check\('\$foobar'\)}; } From 2cb93735b123008b548b171e73bfd341cb0d0200 Mon Sep 17 00:00:00 2001 From: "Michael G. Schwern" Date: Thu, 6 Mar 2014 12:41:37 -0800 Subject: [PATCH 13/15] Move tokenization of the parameter list into MS::Signature. MS::Parser is now just a utility library and needs to be renamed. MS::Signature now completely handles signature parsing. I'm pretty sure MS::Signature could hand MS::Parameter pieces of the PPI document and avoid having to reparse each parameter. --- lib/Method/Signatures/Parameter.pm | 13 ++++- lib/Method/Signatures/Parser.pm | 57 ++++----------------- lib/Method/Signatures/Signature.pm | 79 +++++++++++++++++++++++------- t/split_proto.t | 9 +++- 4 files changed, 91 insertions(+), 67 deletions(-) diff --git a/lib/Method/Signatures/Parameter.pm b/lib/Method/Signatures/Parameter.pm index 81bc865..0250372 100644 --- a/lib/Method/Signatures/Parameter.pm +++ b/lib/Method/Signatures/Parameter.pm @@ -162,6 +162,17 @@ has is_required => isa => 'Bool', ; +# A PPI::Document representing the parameter +has ppi_doc => + is => 'ro', + isa => 'PPI::Document', + lazy => 1, + default => sub { + my $code = $_[0]->ppi_clean_code; + return new_ppi_doc(\$code); + }; + + sub is_optional { my $self = shift; @@ -219,7 +230,7 @@ sub _parse_with_ppi { $self->ppi_clean_code($self->variable. " " .$self->ppi_clean_code); # Tokenize... - my $components = Method::Signatures::Parser->new_ppi_doc(\($self->ppi_clean_code)); + my $components = $self->ppi_doc; my $statement = $components->find_first("PPI::Statement") or sig_parsing_error("Could not understand parameter specification: @{[$self->ppi_clean_code]}"); my $tokens = [ $statement->children ]; diff --git a/lib/Method/Signatures/Parser.pm b/lib/Method/Signatures/Parser.pm index e096058..5dcff52 100644 --- a/lib/Method/Signatures/Parser.pm +++ b/lib/Method/Signatures/Parser.pm @@ -5,7 +5,7 @@ use warnings; use Carp; use base qw(Exporter); -our @EXPORT = qw(split_proto split_parameter extract_invocant sig_parsing_error carp_location_for DEBUG); +our @EXPORT = qw(new_ppi_doc sig_parsing_error carp_location_for DEBUG); sub DEBUG { return unless $Method::Signatures::DEBUG; @@ -14,46 +14,19 @@ sub DEBUG { print STDERR "DEBUG: ", map { ref $_ ? Data::Dumper::Dumper($_) : $_ } @_; } -sub split_proto { - my $proto = shift; - return unless $proto =~ /\S/; - local $@ = undef; - - my $ppi = __PACKAGE__->new_ppi_doc(\$proto); - $ppi->prune('PPI::Token::Comment'); - - my $statement = $ppi->find_first("PPI::Statement"); - sig_parsing_error("Could not understand parameter list specification: $proto") - unless $statement; - my $token = $statement->first_token; - - my @proto = (''); - do { - if( $token->class eq "PPI::Token::Operator" and $token->content eq ',' ) { - push @proto, ''; - } - else { - $proto[-1] .= $token->content; - } - - $token = $token->class eq 'PPI::Token::Label' ? $token->next_token : $token->next_sibling; - } while( $token ); - - - strip_ws($_) for @proto; - - # Remove blank entries due to trailing comma. - @proto = grep { /\S/ } @proto; +sub new_ppi_doc { + my $code = shift; - return @proto; -} + require PPI; + my $ppi = PPI::Document->new($code) or + sig_parsing_error( + "source '$$code' cannot be parsed by PPI: " . PPI::Document->errstr + ); + return $ppi; +}; -sub strip_ws { - $_[0] =~ s{^\s+}{}; - $_[0] =~ s{\s+$}{}; -} # Generate cleaner error messages... sub carp_location_for { @@ -82,16 +55,6 @@ sub carp_location_for { return ($file, $line, $method); } -sub new_ppi_doc { - my $class = shift; - my $source = shift; - - require PPI; - my $ppi = PPI::Document->new($source) or - sig_parsing_error("source '$$source' cannot be parsed by PPI: " . PPI::Document->errstr); - return $ppi; -} - sub sig_parsing_error { my ($file, $line) = carp_location_for(__PACKAGE__, 'Devel::Declare::linestr_callback'); my $msg = join('', @_, " in declaration at $file line $line.\n"); diff --git a/lib/Method/Signatures/Signature.pm b/lib/Method/Signatures/Signature.pm index 2206a8a..47dc428 100644 --- a/lib/Method/Signatures/Signature.pm +++ b/lib/Method/Signatures/Signature.pm @@ -2,7 +2,8 @@ package Method::Signatures::Signature; use Mouse; use Method::Signatures::Types; -use Method::Signatures::Parser qw(split_proto sig_parsing_error DEBUG); +use Method::Signatures::Parameter; +use Method::Signatures::Parser qw(new_ppi_doc sig_parsing_error DEBUG); my $INF = ( 0 + "inf" ) == 0 ? 9e9999 : "inf"; @@ -22,8 +23,9 @@ has parameter_string => # A list of strings for each parameter tokenized from parameter_string has parameter_strings => is => 'ro', - isa => 'ArrayRef', - default => sub { [] }; + isa => 'ArrayRef[Str]', + lazy => 1, + builder => '_build_parameter_strings'; # The parsed Method::Signature::Parameter objects has parameters => @@ -112,14 +114,28 @@ has max_args => is => 'rw', isa => 'Int|Inf'; +# A PPI::Document representing the list of parameters +has ppi_doc => + is => 'ro', + isa => 'PPI::Document', + lazy => 1, + default => sub { + my $code = $_[0]->parameter_string; + return new_ppi_doc(\$code); + }; + +# If set, no checks will be done on the signature or parameters +has no_checks => + is => 'rw', + isa => 'Bool', + default => 0; + sub BUILD { my $self = shift; - my @protos = $self->_split_proto($self->parameter_string); - my $idx = 0; - for my $proto (@protos) { + for my $proto (@{$self->parameter_strings}) { DEBUG( "proto: $proto\n" ); my $sig = Method::Signatures::Parameter->new( @@ -137,7 +153,7 @@ sub BUILD { next; } - $sig->check($self); + $sig->check($self) unless $self->no_checks; push @{$self->named_parameters}, $sig if $sig->is_named; push @{$self->positional_parameters}, $sig if $sig->is_positional; @@ -150,7 +166,7 @@ sub BUILD { } $self->_calculate_max_args; - $self->check; + $self->check unless $self->no_checks; return; } @@ -198,15 +214,6 @@ sub _strip_ws { } -sub _split_proto { - my $self = shift; - my $proto = shift; - - $self->_strip_ws($proto); - return split_proto($proto); -} - - my $IDENTIFIER = qr{ [^\W\d] \w* }x; sub _build_parameter_string { my $self = shift; @@ -224,4 +231,42 @@ sub _build_parameter_string { } +sub _build_parameter_strings { + my $self = shift; + + my $param_string = $self->parameter_string; + return [] unless $param_string =~ /\S/; + + local $@ = undef; + + my $ppi = $self->ppi_doc; + $ppi->prune('PPI::Token::Comment'); + + my $statement = $ppi->find_first("PPI::Statement"); + sig_parsing_error("Could not understand parameter list specification: $param_string") + unless $statement; + my $token = $statement->first_token; + + my @params = (''); + do { + if( $token->class eq "PPI::Token::Operator" and $token->content eq ',' ) { + push @params, ''; + } + else { + $params[-1] .= $token->content; + } + + $token = $token->class eq 'PPI::Token::Label' ? $token->next_token : $token->next_sibling; + } while( $token ); + + + $self->_strip_ws($_) for @params; + + # Remove blank entries due to trailing comma. + @params = grep { /\S/ } @params; + + return \@params; +} + + 1; diff --git a/t/split_proto.t b/t/split_proto.t index 5dbdcff..fdee2aa 100644 --- a/t/split_proto.t +++ b/t/split_proto.t @@ -5,7 +5,7 @@ use warnings; use Test::More 'no_plan'; -use Method::Signatures::Parser; +use Method::Signatures::Signature; my %tests = ( '$foo' => ['$foo'], @@ -37,5 +37,10 @@ my %tests = ( ); while(my($args, $expect) = each %tests) { - is_deeply [split_proto($args)], $expect, "split_proto($args)"; + my $sig = Method::Signatures::Signature->new( + signature_string => $args, + # we just want to test the tokenizing + no_checks => 1, + ); + is_deeply $sig->parameter_strings, $expect, "split_proto($args)"; } From 159c711fdf93accaf33a781def61a9787d1e5f9d Mon Sep 17 00:00:00 2001 From: "Michael G. Schwern" Date: Thu, 6 Mar 2014 13:26:32 -0800 Subject: [PATCH 14/15] Rename MS::Parser to MS::Utils Parsing is done in MS::Signature and MS::Parameter now. --- lib/Method/Signatures.pm | 2 +- lib/Method/Signatures/Parameter.pm | 2 +- lib/Method/Signatures/Signature.pm | 2 +- lib/Method/Signatures/{Parser.pm => Utils.pm} | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename lib/Method/Signatures/{Parser.pm => Utils.pm} (97%) diff --git a/lib/Method/Signatures.pm b/lib/Method/Signatures.pm index bd8a5fa..35a739d 100644 --- a/lib/Method/Signatures.pm +++ b/lib/Method/Signatures.pm @@ -5,7 +5,7 @@ use warnings; use Lexical::SealRequireHints; use base 'Devel::Declare::MethodInstaller::Simple'; -use Method::Signatures::Parser; +use Method::Signatures::Utils; use Method::Signatures::Parameter; use Method::Signatures::Signature; diff --git a/lib/Method/Signatures/Parameter.pm b/lib/Method/Signatures/Parameter.pm index 0250372..dd821e8 100644 --- a/lib/Method/Signatures/Parameter.pm +++ b/lib/Method/Signatures/Parameter.pm @@ -2,7 +2,7 @@ package Method::Signatures::Parameter; use Mouse; use Carp; -use Method::Signatures::Parser; +use Method::Signatures::Utils; my $IDENTIFIER = qr{ [^\W\d] \w* }x; my $VARIABLE = qr{ [\$\@%] $IDENTIFIER }x; diff --git a/lib/Method/Signatures/Signature.pm b/lib/Method/Signatures/Signature.pm index 47dc428..7e266d2 100644 --- a/lib/Method/Signatures/Signature.pm +++ b/lib/Method/Signatures/Signature.pm @@ -3,7 +3,7 @@ package Method::Signatures::Signature; use Mouse; use Method::Signatures::Types; use Method::Signatures::Parameter; -use Method::Signatures::Parser qw(new_ppi_doc sig_parsing_error DEBUG); +use Method::Signatures::Utils qw(new_ppi_doc sig_parsing_error DEBUG); my $INF = ( 0 + "inf" ) == 0 ? 9e9999 : "inf"; diff --git a/lib/Method/Signatures/Parser.pm b/lib/Method/Signatures/Utils.pm similarity index 97% rename from lib/Method/Signatures/Parser.pm rename to lib/Method/Signatures/Utils.pm index 5dcff52..d201114 100644 --- a/lib/Method/Signatures/Parser.pm +++ b/lib/Method/Signatures/Utils.pm @@ -1,4 +1,4 @@ -package Method::Signatures::Parser; +package Method::Signatures::Utils; use strict; use warnings; From 2640c4744c1a4730b6fa4fe7b65361f8989c802c Mon Sep 17 00:00:00 2001 From: "Michael G. Schwern" Date: Thu, 6 Mar 2014 13:27:51 -0800 Subject: [PATCH 15/15] This @CARP_NOT serves no purpose. Leftover from a58cebe270d928d59cf0b2e13f23c187eb5a2d54 before the stack handling was moved to carp_location_for(). --- lib/Method/Signatures.pm | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/Method/Signatures.pm b/lib/Method/Signatures.pm index 35a739d..3d93244 100644 --- a/lib/Method/Signatures.pm +++ b/lib/Method/Signatures.pm @@ -13,8 +13,6 @@ our $VERSION = '20140224'; our $DEBUG = $ENV{METHOD_SIGNATURES_DEBUG} || 0; -our @CARP_NOT; - our $INF = ( 0 + "inf" ) == 0 ? 9e9999 : "inf"; # copied from Devel::Pragma