File Coverage

blib/lib/Variable/Declaration.pm
Criterion Covered Total %
statement 122 122 100.0
branch 40 42 95.2
condition 14 14 100.0
subroutine 29 29 100.0
pod 0 8 0.0
total 205 215 95.3


line stmt bran cond sub pod time code
1             package Variable::Declaration;
2 14     14   963429 use v5.12.0;
  14         194  
3 14     14   98 use strict;
  14         30  
  14         290  
4 14     14   74 use warnings;
  14         35  
  14         616  
5              
6             our $VERSION = "0.06";
7              
8 14     14   6870 use Keyword::Simple;
  14         361078  
  14         619  
9 14     14   14303 use PPR;
  14         613861  
  14         611  
10 14     14   180 use Carp ();
  14         31  
  14         281  
11 14     14   8245 use Import::Into;
  14         37196  
  14         466  
12 14     14   6096 use Data::Lock ();
  14         89123  
  14         360  
13 14     14   6998 use Type::Tie ();
  14         166462  
  14         23715  
14              
15             our $LEVEL;
16             our $DEFAULT_LEVEL = 2;
17              
18             sub import {
19 21     21   12913 shift;
20 21         71 my %args = @_;
21 21         57 my $caller = caller;
22              
23             $LEVEL = exists $args{level} ? $args{level}
24 21 100       122 : exists $ENV{'Variable::Declaration::LEVEL'} ? $ENV{'Variable::Declaration::LEVEL'}
    100          
25             : $DEFAULT_LEVEL;
26              
27 21         191 feature->import::into($caller, 'state');
28              
29 21         6700 Keyword::Simple::define 'let' => \&define_let;
30 21         633 Keyword::Simple::define 'static' => \&define_static;
31 21         420 Keyword::Simple::define 'const' => \&define_const;
32             }
33              
34             sub unimport {
35 1     1   21 Keyword::Simple::undefine 'let';
36 1         39 Keyword::Simple::undefine 'static';
37 1         17 Keyword::Simple::undefine 'const';
38             }
39              
40 32     32 0 58625 sub define_let { define_declaration(let => 'my', @_) }
41 12     12 0 37079 sub define_static { define_declaration(static => 'state', @_) }
42 14     14 0 29293 sub define_const { define_declaration(const => 'my', @_) }
43              
44             sub define_declaration {
45 58     58 0 225 my ($declaration, $perl_declaration, $ref) = @_;
46              
47 58         234 my $match = _valid($declaration => _parse($$ref));
48 38         256 my $tv = _parse_type_varlist($match->{type_varlist});
49 38         1654 my $args = +{ declaration => $declaration, perl_declaration => $perl_declaration, %$match, %$tv, level => $LEVEL };
50              
51 38         339 substr($$ref, 0, length $match->{statement}) = _render_declaration($args);
52             }
53              
54 23     23 0 4540 sub croak { Carp::croak @_ }
55              
56 2     2 0 113 sub data_lock { Data::Lock::dlock @_ }
57              
58             sub type_tie(\[$@%]@);
59             {
60             *type_tie = \&Type::Tie::ttie;
61             }
62              
63             our %metadata;
64             sub info {
65 5     5 0 512 my $variable_ref = shift;
66 5 50       18 die 'argument must be reference' unless ref $variable_ref;
67 5 50       21 my $info = $metadata{$variable_ref} or return undef;
68 5         506 require Variable::Declaration::Info;
69             Variable::Declaration::Info->new(
70             declaration => $info->{declaration},
71             type => $info->{type},
72             attributes => $info->{attributes},
73             )
74 5         35 }
75              
76             sub register_info {
77 17     17 0 16329 my ($variable_ref, $info) = @_;
78             $metadata{$variable_ref} = {
79             declaration => $info->{declaration},
80             type => $info->{type},
81             attributes => $info->{attributes},
82 17         104 };
83             }
84              
85             sub _valid {
86 58     58   389 my ($declaration, $match) = @_;
87              
88             croak "variable declaration is required"
89 58 100       301 unless $match->{type_varlist};
90              
91 49         233 my ($eq, $assign) = ($match->{eq}, $match->{assign});
92 49 100       212 if ($declaration eq 'const') {
93 11 100 100     90 croak "'const' declaration must be assigned"
94             unless defined $eq && defined $assign;
95             }
96             else {
97 38 100 100     371 croak "illegal expression"
      100        
      100        
98             unless (defined $eq && defined $assign) or (!defined $eq && !defined $assign);
99             }
100              
101 38         114 return $match;
102             }
103              
104             sub _render_declaration {
105 38     38   274 my $args = shift;
106 38         101 my @lines;
107 38         215 push @lines => _lines_declaration($args);
108 38         181 push @lines => _lines_register_info($args);
109 38 100       237 push @lines => _lines_type_check($args) if $args->{level} >= 1;
110 38 100       221 push @lines => _lines_type_tie($args) if $args->{level} == 2;
111 38 100       176 push @lines => _lines_data_lock($args) if $args->{declaration} eq 'const';
112 38         12350 return join ";", @lines;
113             }
114              
115             sub _lines_declaration {
116 38     38   108 my $args = shift;
117 38         131 my $s = $args->{perl_declaration};
118 38         77 $s .= do {
119 38         88 my $s = join ', ', map { $_->{var} } @{$args->{type_vars}};
  38         241  
  38         185  
120 38 100       241 $args->{is_list_context} ? " ($s)" : " $s";
121             };
122 38 100       147 $s .= $args->{attributes} if $args->{attributes};
123 38 100       172 $s .= " = @{[$args->{assign}]}" if defined $args->{assign};
  24         105  
124 38         168 return ($s);
125             }
126              
127             sub _lines_type_tie {
128 31     31   90 my $args = shift;
129 31         48 my @lines;
130 31         65 for (@{$args->{type_vars}}) {
  31         88  
131 31         101 my ($type, $var) = ($_->{type}, $_->{var});
132 31 100       104 next unless $type;
133 7         39 push @lines => sprintf('Variable::Declaration::type_tie(%s, %s, %s)', $var, $type, $var);
134             }
135 31         72 return @lines;
136             }
137              
138             sub _lines_type_check {
139 35     35   96 my $args = shift;
140 35         74 my @lines;
141 35         64 for (@{$args->{type_vars}}) {
  35         131  
142 35         193 my ($type, $var) = ($_->{type}, $_->{var});
143 35 100       109 next unless $type;
144 11         57 push @lines => sprintf('Variable::Declaration::croak(%s->get_message(%s)) unless %s->check(%s)', $type, $var, $type, $var)
145             }
146 35         113 return @lines;
147             }
148              
149             sub _lines_data_lock {
150 4     4   10 my $args = shift;
151 4         10 my @lines;
152 4         15 for my $type_var (@{$args->{type_vars}}) {
  4         15  
153 4         23 push @lines => "Variable::Declaration::data_lock($type_var->{var})";
154             }
155 4         18 return @lines;
156             }
157              
158             sub _lines_register_info {
159 38     38   107 my $args = shift;
160 38         87 my @lines;
161 38         101 for my $type_var (@{$args->{type_vars}}) {
  38         201  
162             push @lines => sprintf("Variable::Declaration::register_info(\\%s, { declaration => '%s', attributes => %s, type => %s })",
163             $type_var->{var},
164             $args->{declaration},
165             ($args->{attributes} ? "'$args->{attributes}'" : 'undef'),
166 38 100 100     554 ($type_var->{type} or 'undef'),
167             );
168             }
169 38         125 return @lines;
170             }
171              
172             sub _parse {
173 92     92   140851 my $src = shift;
174              
175 92 100       3040907 return unless $src =~ m{
176             \A
177             (?
178             (?&PerlOWS)
179             (?
180             (?
181             (?&PerlIdentifier)? (?&PerlOWS)
182             (?&PerlVariable)
183             | (?&PerlParenthesesList)
184             ) (?&PerlOWS)
185             (?(?&PerlAttributes))? (?&PerlOWS)
186             )
187             (?=)? (?&PerlOWS)
188             (?(?&PerlConditionalExpression))?
189             ) $PPR::GRAMMAR }x;
190              
191             return +{
192             statement => $+{statement},
193             type_varlist => $+{type_varlist},
194             assign_to => $+{assign_to},
195             eq => $+{eq},
196             assign => $+{assign},
197             attributes => $+{attributes},
198             }
199 83         18568 }
200              
201             sub _parse_type_varlist {
202 50     50   42758 my $expression = shift;
203              
204 50 100       1556536 if ($expression =~ m{ (?(?&PerlParenthesesList)) $PPR::GRAMMAR }x) {
    100          
205 9         248 my ($type_vars) = $+{list} =~ m/\A\((.+)\)\Z/;
206 9         78 my @list = split ',', $type_vars;
207             return +{
208             is_list_context => 1,
209 9         43 type_vars => [ map { _parse_type_var($_) } @list ],
  12         74  
210             }
211             }
212             elsif (my $type_var = _parse_type_var($expression)) {
213             return +{
214 39         7488 is_list_context => 0,
215             type_vars => [ $type_var ],
216             }
217             }
218             else {
219 2         446 return;
220             }
221             }
222              
223             sub _parse_type_var {
224 92     92   46372 my $expression = shift;
225              
226 92 100       3034503 return unless $expression =~ m{
227             \A
228             (?&PerlOWS)
229             (?(?&PerlIdentifier) | (?&PerlCall) )? (?&PerlOWS)
230             (?(?:(?&PerlVariable)))
231             (?&PerlOWS)
232             \Z
233             $PPR::GRAMMAR
234             }x;
235              
236             return +{
237             type => $+{type},
238             var => $+{var},
239             }
240 83         17226 }
241              
242             1;
243             __END__