File Coverage

blib/lib/Perl/ToPerl6/Transformer/ModuleSpecific/Moose.pm
Criterion Covered Total %
statement 23 95 24.2
branch 0 38 0.0
condition 0 21 0.0
subroutine 10 19 52.6
pod 3 10 30.0
total 36 183 19.6


line stmt bran cond sub pod time code
1             package Perl::ToPerl6::Transformer::ModuleSpecific::Moose;
2              
3 1     1   803 use 5.006001;
  1         3  
4 1     1   5 use strict;
  1         2  
  1         19  
5 1     1   5 use warnings;
  1         1  
  1         21  
6 1     1   4 use Readonly;
  1         2  
  1         48  
7              
8 1     1   5 use Perl::ToPerl6::Utils qw{ :severities };
  1         2  
  1         48  
9 1         39 use Perl::ToPerl6::Utils::PPI qw{
10             insert_trailing_whitespace
11 1     1   114 };
  1         2  
12              
13 1     1   5 use base 'Perl::ToPerl6::Transformer';
  1         1  
  1         1054  
14              
15             #-----------------------------------------------------------------------------
16              
17             Readonly::Scalar my $DESC => q{Transform Moose attributes to Perl6};
18             Readonly::Scalar my $EXPL => q{Transform Moose attributes to Perl6};
19              
20             #-----------------------------------------------------------------------------
21              
22 1     1 0 3 sub run_before { return 'Operators::FormatOperators' }
23 1     1 0 3 sub supported_parameters { return () }
24 1     1 1 5 sub default_necessity { return $NECESSITY_HIGHEST }
25 0     0 1   sub default_themes { return qw( tweaks ) }
26 0     0 1   sub applies_to { return 'PPI::Document' }
27              
28             #-----------------------------------------------------------------------------
29              
30             sub ppi_is_fat_comma {
31 0 0 0 0 0   $_[1] and
32             $_[1]->isa('PPI::Token::Operator') and
33             $_[1]->content eq '=>';
34             }
35              
36             sub ppi_is_comma {
37 0 0 0 0 0   $_[1] and
38             $_[1]->isa('PPI::Token::Operator') and
39             $_[1]->content eq ',';
40             }
41              
42             sub moose_has_attribute {
43 0     0 0   my ($elem) = @_;
44 0           my $head = $elem;
45              
46             # In the string that follows, (( .. )) is not present in the code, but
47             # represents the nesting of an expression inside a list that PPI does.
48              
49             # ----V
50             # C<< has x => (( is => 'rw', isa => 'Int' )) >>
51             #
52 0           $head = $head->snext_sibling;
53              
54             # --------V
55             # C<< has x => (( is => 'rw', isa => 'Int' )) >>
56             # C<< has 'x' => (( is => 'rw', isa => 'Int' )) >>
57             #
58 0           my $name;
59 0 0         if ( $head->isa('PPI::Token::Word') ) {
    0          
60 0           $name = $head->content;
61 0           $head = $head->snext_sibling;
62             }
63             elsif ( $head->isa('PPI::Token::Quote') ) {
64 0           $name = $head->string;
65 0           $head = $head->snext_sibling;
66             }
67             else {
68 0           return;
69             }
70              
71 0           my $attributes;
72              
73             # ----------V
74             # C<< has x => (( is => 'rw', isa => 'Int' )) >>
75             # C<< has x , (( is => 'rw', isa => 'Int' )) >>
76             #
77 0 0 0       return unless ppi_is_fat_comma(undef,$head) or
78             ppi_is_comma(undef,$head);
79 0           $head = $head->snext_sibling;
80              
81             # -------------V
82             # C<< has x => (( is => 'rw', isa => 'Int' )) >>
83             #
84 0 0 0       if ( $head->isa('PPI::Structure::List') and
85             $head->start->content eq '(' ) {
86 0           $head = $head->schild(0);
87             }
88              
89             # --------------V
90             # C<< has x => (( is => 'rw', isa => 'Int' )) >>
91             #
92 0 0         if ( $head->isa('PPI::Statement::Expression') ) {
93 0           $head = $head->schild(0);
94             }
95              
96 0           while ( $head ) {
97             # ----------------V
98             # C<< has x => (( is => 'rw' )) >>
99             # C<< has x => (( 'is' , 'rw' )) >>
100             #
101 0           my $key;
102 0 0         if ( $head->isa('PPI::Token::Word') ) {
    0          
103 0           $key = $head->content;
104 0           $head = $head->snext_sibling;
105             }
106             elsif ( $head->isa('PPI::Token::Quote') ) {
107 0           $key = $head->string;
108 0           $head = $head->snext_sibling;
109             }
110             else {
111 0           warn "Unknown term >" . $head->content . "< found while processing Moose attribute, please tell the author.";
112 0           return;
113             }
114              
115             # -------------------V
116             # C<< has x => (( is => 'rw' )) >>
117             # C<< has x => (( 'is' , 'rw' )) >>
118             #
119 0 0 0       return unless ppi_is_fat_comma(undef,$head) or
120             ppi_is_comma(undef,$head);
121 0           $head = $head->snext_sibling;
122              
123             # ----------------------V
124             # C<< has x => (( is => 'rw' )) >>
125             #
126 0 0         if ( $key eq 'default' ) {
127 0           $attributes->{$key} = $head->clone;
128             }
129             else {
130 0           $attributes->{$key} = $head->string;
131             }
132 0           $head = $head->snext_sibling;
133              
134             # --------------------------V
135             # C<< has x => (( is => 'rw', isa => 'Int' )) >>
136             #
137 0 0 0       last unless ppi_is_comma(undef,$head) or
138             ppi_is_fat_comma(undef,$head);
139 0           $head = $head->snext_sibling;
140             }
141              
142 0           return ( $name, $attributes );
143             }
144              
145             sub make_perl6_attribute {
146 0     0 0   my ($name, $attributes) = @_;
147 0           my $statement = PPI::Statement->new;
148 0           $statement->add_element(
149             PPI::Token::Word->new('has')
150             );
151              
152 0 0         if ( $attributes->{isa} ) {
153 0           $statement->add_element( PPI::Token::Whitespace->new(' ') );
154             $statement->add_element(
155             PPI::Token::Word->new( $attributes->{isa} )
156 0           );
157             }
158              
159             # Insert '$.<attribute name>'
160             #
161 0           $statement->add_element( PPI::Token::Whitespace->new(' ') );
162 0           $statement->add_element(
163             PPI::Token::Symbol->new('$.' . $name)
164             );
165              
166             # If we have a read-write attribute flag, add that here.
167             #
168 0 0 0       if ( $attributes->{is} and $attributes->{is} eq 'rw' ) {
169 0           $statement->add_element( PPI::Token::Whitespace->new(' ') );
170 0           $statement->add_element(
171             PPI::Token::Word->new('is')
172             );
173 0           $statement->add_element( PPI::Token::Whitespace->new(' ') );
174 0           $statement->add_element(
175             PPI::Token::Word->new('rw')
176             );
177             }
178              
179             # If we have a default attribute, add that here.
180             #
181 0 0         if ( $attributes->{default} ) {
182 0           $statement->add_element( PPI::Token::Whitespace->new(' ') );
183 0           $statement->add_element(
184             PPI::Token::Operator->new('=')
185             );
186 0           $statement->add_element( PPI::Token::Whitespace->new(' ') );
187 0           $statement->add_element( $attributes->{default} );
188              
189             }
190             $statement->add_element(
191 0           PPI::Token::Structure->new(';')
192             );
193 0           $statement->add_element( PPI::Token::Whitespace->new("\n") );
194              
195 0           return $statement;
196             }
197              
198             sub transform {
199 0     0 0   my ($self, $elem, $doc) = @_;
200             return unless $doc->find( sub {
201 0 0   0     $_[1]->isa('PPI::Statement::Include') and
202             $_[1]->schild(1)->content =~ m< ^ Moose >x
203 0 0         } );
204              
205             my $attribute_keywords = $doc->find( sub {
206 0 0   0     $_[1]->isa('PPI::Token::Word') and
207             $_[1]->content eq 'has'
208 0           } );
209              
210 0           for my $has ( @$attribute_keywords ) {
211 0           my ( $name, $attributes ) = moose_has_attribute( $has );
212 0 0         next unless $name;
213              
214 0           my $attribute = make_perl6_attribute( $name, $attributes );
215 0           $has->parent->insert_after( $attribute );
216 0           $has->parent->insert_after(
217             PPI::Token::Whitespace->new("\n")
218             );
219             }
220              
221 0           return $self->transformation( $DESC, $EXPL, $elem );
222             }
223              
224             1;
225              
226             #-----------------------------------------------------------------------------
227              
228             __END__
229              
230             =pod
231              
232             =head1 NAME
233              
234             Perl::ToPerl6::Transformer::ModuleSpecific::Moose - Add Perl6-style class attributes
235              
236              
237             =head1 AFFILIATION
238              
239             This Transformer is part of the core L<Perl::ToPerl6|Perl::ToPerl6>
240             distribution.
241              
242              
243             =head1 DESCRIPTION
244              
245             Perl6 uses a similar syntax to L<Moose|Moose>'s 'has' declaration style. This module attempts to convert basic Moose C<< has x => ( isa => 'Int', is => 'ro' ) >> declarations to C< has Int $.x; >.
246              
247             has x => ( is => 'rw', isa => 'Int', default => 42 );
248             --> has Int $.x is rw = 42;
249              
250             Eventually will do:
251              
252             $self->x(3); --> $.x = 3;
253              
254             Currently doesn't comment out the old declarations, as they could be multiline. And I'm lazy :)
255              
256             Transforms 'has' statements outside of comments, heredocs, strings and POD.
257              
258             =head1 CONFIGURATION
259              
260             This Transformer is not configurable except for the standard options.
261              
262             =head1 AUTHOR
263              
264             Jeffrey Goff <drforr@pobox.com>
265              
266             =head1 COPYRIGHT
267              
268             Copyright (c) 2015 Jeffrey Goff
269              
270             This program is free software; you can redistribute it and/or modify
271             it under the same terms as Perl itself.
272              
273             =cut
274              
275             ##############################################################################
276             # Local Variables:
277             # mode: cperl
278             # cperl-indent-level: 4
279             # fill-column: 78
280             # indent-tabs-mode: nil
281             # c-indentation-style: bsd
282             # End:
283             # ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab shiftround :