File Coverage

blib/lib/assign/0.pm
Criterion Covered Total %
statement 88 92 95.6
branch 16 22 72.7
condition 34 42 80.9
subroutine 20 20 100.0
pod 0 11 0.0
total 158 187 84.4


line stmt bran cond sub pod time code
1 5     5   37 use strict; use warnings;
  5     5   10  
  5         143  
  5         22  
  5         8  
  5         269  
2             package assign::0;
3              
4             our $VERSION = '0.0.17';
5              
6 5     5   1945 use assign::Array;
  5         13  
  5         1769  
7 5     5   1851 use assign::Hash;
  5         11  
  5         134  
8              
9 5     5   2820 use Filter::Simple;
  5         111730  
  5         31  
10 5     5   3064 use PPI;
  5         556370  
  5         222  
11 5     5   49 use XXX;
  5         12  
  5         49  
12              
13             our $var_prefix = '___';
14             our $var_id = 1000;
15             our $var_suffix = '';
16              
17             sub new {
18 22     22 0 53 my $class = shift;
19 22         81 my $self = bless { @_ }, $class;
20             my $code = $self->{code}
21 22 50       100 or die "$class\->new requires 'code' string";
22 22   100     118 $self->{line} //= 0;
23 22         112 $self->{doc} = PPI::Document->new(\$code);
24 22         320244 $self->{doc}->index_locations;
25 22         71704 return $self;
26             }
27              
28             # FILTER_ONLY code_no_comments => \&filter;
29             FILTER_ONLY all => \&filter;
30              
31             sub filter {
32 6     6 0 4119 my ($class) = @_;
33 6         81 $_ = $class->new(
34             code => $_,
35             line => ([caller(4)])->[2],
36             )->transform();
37             };
38              
39             sub debug {
40 1     1 0 135 my ($class, $code) = @_;
41 1 50       9 if (ref($code) eq 'SCALAR') {
    50          
42 0         0 $code = $$code;
43             } elsif (not ref($code)) {
44 1 50       21 if (not -f $code) {
45 0         0 XXX $code;
46 0         0 die "Argument to $class\->debug() is not a valid file.\n" .
47             "Code strings need to be passed as scalar refs.";
48             }
49 1 50       39 open my $fh, $code or die "Can't open '$code' for input";
50 1         3 $code = do { local $/; <$fh> };
  1         4  
  1         47  
51             } else {
52 0         0 die "Invalid arguments for $class->debug(...)";
53             }
54 1         11 $class->new(code => $code)->transform;
55             }
56              
57             sub transform {
58 22     22 0 61 my ($self) = @_;
59              
60             # Call the various possible assignment transformations:
61 22         76 $self->transform_assignment_statements_with_decl;
62 22         138 $self->transform_assignment_statements_no_decl;
63             # ... more to come ...
64              
65 22         106 $self->{doc}->serialize;
66             }
67              
68             sub transform_assignment_statements_with_decl {
69 22     22 0 46 my ($self) = @_;
70              
71 22         72 for my $node ($self->find_assignment_statements_with_decl) {
72 22         65 my ($decl, $lhs, $oper, @rhs) = $node->schildren;
73 22         435 my $rhs = join '', map $_->content, @rhs;
74 22         489 $self->transform_assignment_statement(
75             $node, $decl, $lhs, $oper, $rhs,
76             );
77             }
78             }
79              
80             sub transform_assignment_statements_no_decl {
81 22     22 0 3515 my ($self) = @_;
82              
83 22         64 for my $node ($self->find_assignment_statements_no_decl) {
84 2         6 my $decl = '';
85 2         7 my ($lhs, $oper, @rhs) = $node->schildren;
86 2         31 my $rhs = join '', map $_->content, @rhs;
87 2         20 $self->transform_assignment_statement(
88             $node, $decl, $lhs, $oper, $rhs,
89             );
90             }
91             }
92              
93             sub find_assignment_statements_with_decl {
94 22     22 0 37 my ($self) = @_;
95              
96 22   100     328 map { $_ ||= []; @$_ }
  22         115  
97             $self->{doc}->find(sub {
98 2737     2737   30167 my $n = $_[1];
99             return 0 unless
100             $n->isa('PPI::Statement::Variable') and
101 2737 100 100     9183 @{[$n->schildren]} >= 5 and
  61   66     165  
      66        
      100        
      66        
      66        
      66        
102             $n->schild(0)->isa('PPI::Token::Word') and
103             $n->schild(0)->content =~ /^(my|our|local)$/ and
104             (
105             $n->schild(1)->isa('PPI::Structure::Constructor') or
106             $n->schild(1)->isa('PPI::Structure::Block')
107             ) and
108             # or PPI::Structure::Block
109             $n->schild(2)->isa('PPI::Token::Operator') and
110             $n->schild(2)->content eq '=';
111 22         242 });
112             }
113              
114             sub find_assignment_statements_no_decl {
115 22     22 0 45 my ($self) = @_;
116              
117 22   100     361 map { $_ ||= []; @$_ }
  22         160  
118             $self->{doc}->find(sub {
119 3612     3612   38323 my $n = $_[1];
120             return 0 unless
121             ref($n) eq 'PPI::Statement' and
122 3612 100 100     9514 @{[$n->schildren]} >= 4 and
  137   66     313  
      100        
      66        
      66        
123             (
124             $n->schild(0)->isa('PPI::Structure::Constructor') or
125             $n->schild(0)->isa('PPI::Structure::Block')
126             ) and
127             $n->schild(1)->isa('PPI::Token::Operator') and
128             $n->schild(1)->content eq '=';
129 22         134 });
130             }
131              
132             sub transform_assignment_statement {
133 24     24 0 65 my ($self, $node, $decl, $lhs, $oper, $rhs) = @_;
134              
135 24 100       120 $decl = $decl ? $decl->{content} . ' ' : '';
136 24         81 $oper = $oper->{content};
137              
138 24 50       84 my $class =
    100          
139             $lhs->start->content eq '[' ? 'assign::Array' :
140             $lhs->start->content eq '{' ? 'assign::Hash' :
141             ZZZ $node, "Unsupported statement";
142              
143 24         170 my $from;
144 24         39 my $init = [];
145 24 100       130 if ($rhs =~ /^(\$\w+);/) {
146 16         45 $from = $1;
147             } else {
148 8         52 $from = $self->gen_var;
149 8         34 push @$init, "my $from = $rhs";
150             }
151              
152 24         149 my $code = $class->new(
153             node => $lhs,
154             )->parse->gen_code($decl, $oper, $from, $init);
155              
156 24         195 $self->replace_statement_node($node, $code);
157              
158 24         86 return;
159             }
160              
161             sub gen_var {
162 8     8 0 21 $var_id++;
163 8         35 return "\$$var_prefix$var_id$var_suffix";
164             }
165              
166             sub replace_statement_node {
167 24     24 0 65 my ($self, $node, $code) = @_;
168 24         112 my $line_number = $node->last_token->logical_line_number + $self->{line};
169             $node->insert_after($_->remove)
170 24         878 for reverse PPI::Document->new(\"\n#line $line_number")->elements;
171             $node->insert_after($_->remove)
172 24         15373 for reverse PPI::Document->new(\$code)->elements;
173 24         165877 $node->remove;
174 24         1464 return;
175             }
176              
177             1;