File Coverage

blib/lib/Variable/Declaration.pm
Criterion Covered Total %
statement 108 108 100.0
branch 36 36 100.0
condition 12 12 100.0
subroutine 26 26 100.0
pod 0 6 0.0
total 182 188 96.8


line stmt bran cond sub pod time code
1             package Variable::Declaration;
2 14     14   909499 use v5.12.0;
  14         173  
3 14     14   79 use strict;
  14         26  
  14         292  
4 14     14   69 use warnings;
  14         24  
  14         557  
5              
6             our $VERSION = "0.04";
7              
8 14     14   6219 use Keyword::Simple;
  14         327113  
  14         565  
9 14     14   9809 use PPR;
  14         564794  
  14         544  
10 14     14   151 use Carp ();
  14         31  
  14         259  
11 14     14   7441 use Import::Into;
  14         34366  
  14         413  
12 14     14   5814 use Data::Lock ();
  14         78810  
  14         349  
13 14     14   6232 use Type::Tie ();
  14         143956  
  14         18798  
14              
15             our $LEVEL;
16             our $DEFAULT_LEVEL = 2;
17              
18             sub import {
19 21     21   12819 shift;
20 21         71 my %args = @_;
21 21         60 my $caller = caller;
22              
23             $LEVEL = exists $args{level} ? $args{level}
24 21 100       115 : exists $ENV{'Variable::Declaration::LEVEL'} ? $ENV{'Variable::Declaration::LEVEL'}
    100          
25             : $DEFAULT_LEVEL;
26              
27 21         240 feature->import::into($caller, 'state');
28              
29 21         6518 Keyword::Simple::define 'let' => \&define_let;
30 21         612 Keyword::Simple::define 'static' => \&define_static;
31 21         410 Keyword::Simple::define 'const' => \&define_const;
32             }
33              
34             sub unimport {
35 1     1   16 Keyword::Simple::undefine 'let';
36 1         31 Keyword::Simple::undefine 'static';
37 1         16 Keyword::Simple::undefine 'const';
38             }
39              
40 23     23 0 38702 sub define_let { define_declaration(let => 'my', @_) }
41 12     12 0 30301 sub define_static { define_declaration(static => 'state', @_) }
42 13     13 0 25436 sub define_const { define_declaration(const => 'my', @_) }
43              
44             sub define_declaration {
45 48     48 0 186 my ($declaration, $perl_declaration, $ref) = @_;
46              
47 48         183 my $match = _valid($declaration => _parse($$ref));
48 28         178 my $tv = _parse_type_varlist($match->{type_varlist});
49 28         1089 my $args = +{ declaration => $declaration, perl_declaration => $perl_declaration, %$match, %$tv, level => $LEVEL };
50              
51 28         237 substr($$ref, 0, length $match->{statement}) = _render_declaration($args);
52             }
53              
54 23     23 0 5441 sub croak { Carp::croak @_ }
55              
56 1     1 0 98 sub data_lock { Data::Lock::dlock @_ }
57              
58             sub type_tie(\[$@%]@);
59             {
60             *type_tie = \&Type::Tie::ttie;
61             }
62              
63             sub _valid {
64 48     48   298 my ($declaration, $match) = @_;
65              
66             croak "variable declaration is required'"
67 48 100       235 unless $match->{type_varlist};
68              
69 39         137 my ($eq, $assign) = ($match->{eq}, $match->{assign});
70 39 100       166 if ($declaration eq 'const') {
71 10 100 100     79 croak "'const' declaration must be assigned"
72             unless defined $eq && defined $assign;
73             }
74             else {
75 29 100 100     348 croak "illegal expression"
      100        
      100        
76             unless (defined $eq && defined $assign) or (!defined $eq && !defined $assign);
77             }
78              
79 28         87 return $match;
80             }
81              
82             sub _render_declaration {
83 39     39   15745 my $args = shift;
84 39         180 my @lines;
85 39         143 push @lines => _lines_declaration($args);
86 39 100       193 push @lines => _lines_type_check($args) if $args->{level} >= 1;
87 39 100       158 push @lines => _lines_type_tie($args) if $args->{level} == 2;
88 39 100       147 push @lines => _lines_data_lock($args) if $args->{declaration} eq 'const';
89 39         6986 return join ";", @lines;
90             }
91              
92             sub _lines_declaration {
93 39     39   108 my $args = shift;
94 39         109 my $s = $args->{perl_declaration};
95 39         98 $s .= do {
96 39         94 my $s = join ', ', map { $_->{var} } @{$args->{type_vars}};
  42         192  
  39         119  
97 39 100       204 $args->{is_list_context} ? " ($s)" : " $s";
98             };
99 39 100       149 $s .= $args->{attributes} if $args->{attributes};
100 39 100       141 $s .= " = @{[$args->{assign}]}" if defined $args->{assign};
  18         68  
101 39         138 return ($s);
102             }
103              
104             sub _lines_type_tie {
105 29     29   65 my $args = shift;
106 29         56 my @lines;
107 29         49 for (@{$args->{type_vars}}) {
  29         76  
108 32         86 my ($type, $var) = ($_->{type}, $_->{var});
109 32 100       80 next unless $type;
110 9         36 push @lines => sprintf('Variable::Declaration::type_tie(%s, %s, %s)', $var, $type, $var);
111             }
112 29         70 return @lines;
113             }
114              
115             sub _lines_type_check {
116 35     35   65 my $args = shift;
117 35         63 my @lines;
118 35         55 for (@{$args->{type_vars}}) {
  35         109  
119 38         142 my ($type, $var) = ($_->{type}, $_->{var});
120 38 100       114 next unless $type;
121 14         101 push @lines => sprintf('Variable::Declaration::croak(%s->get_message(%s)) unless %s->check(%s)', $type, $var, $type, $var)
122             }
123 35         83 return @lines;
124             }
125              
126             sub _lines_data_lock {
127 4     4   9 my $args = shift;
128 4         8 my @lines;
129 4         10 for my $type_var (@{$args->{type_vars}}) {
  4         12  
130 4         16 push @lines => "Variable::Declaration::data_lock($type_var->{var})";
131             }
132 4         15 return @lines;
133             }
134              
135             sub _parse {
136 82     82   132745 my $src = shift;
137              
138 82 100       2869060 return unless $src =~ m{
139             \A
140             (?
141             (?&PerlOWS)
142             (?
143             (?
144             (?&PerlIdentifier)? (?&PerlOWS)
145             (?&PerlVariable)
146             | (?&PerlParenthesesList)
147             ) (?&PerlOWS)
148             (?(?&PerlAttributes))? (?&PerlOWS)
149             )
150             (?=)? (?&PerlOWS)
151             (?(?&PerlConditionalExpression))?
152             ) $PPR::GRAMMAR }x;
153              
154             return +{
155             statement => $+{statement},
156             type_varlist => $+{type_varlist},
157             assign_to => $+{assign_to},
158             eq => $+{eq},
159             assign => $+{assign},
160             attributes => $+{attributes},
161             }
162 73         13645 }
163              
164             sub _parse_type_varlist {
165 40     40   35702 my $expression = shift;
166              
167 40 100       1377228 if ($expression =~ m{ (?(?&PerlParenthesesList)) $PPR::GRAMMAR }x) {
    100          
168 9         240 my ($type_vars) = $+{list} =~ m/\A\((.+)\)\Z/;
169 9         58 my @list = split ',', $type_vars;
170             return +{
171             is_list_context => 1,
172 9         34 type_vars => [ map { _parse_type_var($_) } @list ],
  12         51  
173             }
174             }
175             elsif (my $type_var = _parse_type_var($expression)) {
176             return +{
177 29         4468 is_list_context => 0,
178             type_vars => [ $type_var ],
179             }
180             }
181             else {
182 2         250 return;
183             }
184             }
185              
186             sub _parse_type_var {
187 74     74   30547 my $expression = shift;
188              
189 74 100       2567781 return unless $expression =~ m{
190             \A
191             (?&PerlOWS)
192             (?(?&PerlIdentifier))? (?&PerlOWS)
193             (?(?:(?&PerlVariable)))
194             \Z
195             $PPR::GRAMMAR
196             }x;
197              
198             return +{
199             type => $+{type},
200             var => $+{var},
201             }
202 68         12927 }
203              
204             1;
205             __END__