line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Attribute::Handlers::Prospective; |
2
|
|
|
|
|
|
|
$VERSION = '0.01'; |
3
|
1
|
|
|
1
|
|
11313
|
use Filter::Simple; |
|
1
|
|
|
|
|
45312
|
|
|
1
|
|
|
|
|
10
|
|
4
|
1
|
|
|
1
|
|
84
|
use Text::Balanced ':ALL'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
260
|
|
5
|
1
|
|
|
1
|
|
6
|
use Carp; |
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
943
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $id = qr/(?>[a-z_]\w*(?:::[a-z_]\w*)*)/i; |
8
|
|
|
|
|
|
|
our $parens = qr/[(](?:(?>[^()]+)|(??{$parens}))*[)]/; |
9
|
|
|
|
|
|
|
our $attr = qr/$id(?:$parens)?/; |
10
|
|
|
|
|
|
|
our $decl = qr/my|our|local/; |
11
|
|
|
|
|
|
|
our $sigil = qr/[\$\@%*]/; |
12
|
|
|
|
|
|
|
our $comments = qr/(?-sm:\s*#.*\n)*\s*/; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our $attr_list5 = qr/:$comments($attr$comments(?::?$comments$attr$comments)*)/; |
15
|
|
|
|
|
|
|
our $sub_decl5 = qr/\bsub\s+($id)\s*(?:$attr_list5)?\s*($parens)?/; |
16
|
|
|
|
|
|
|
our $sub_anon5 = qr/\bsub\s*(?:$attr_list5)?\s*($parens)?/; |
17
|
|
|
|
|
|
|
our $var_decl5 = qr/\b($decl)\s*($id?)\s*($sigil)($id)\s*$attr_list5\s*(\S)/; |
18
|
|
|
|
|
|
|
our $var_noattr5 = qr/\b($decl\s*$id?\s*$sigil$id\s*(?=\S)(?!:))/; |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
our $attr_list6 = qr/\bis\s+($attr(?:\s*(?:(?:\bis\b)?\s*$attr|#[^\n]\n))*)/; |
21
|
|
|
|
|
|
|
our $sub_decl6 = qr/\bsub\s+($id)\s*(?:$attr_list6)?\s*($parens)?(?=\s*[{])/; |
22
|
|
|
|
|
|
|
our $sub_anon6 = qr/\bsub\s*(?:$attr_list6)?\s*($parens)?/; |
23
|
|
|
|
|
|
|
our $var_decl6 = qr/\b($decl)\s*($id?)\s*($sigil)($id)\s*$attr_list6\s*(\S)/; |
24
|
|
|
|
|
|
|
our $var_noattr6 = qr/\b($decl\s+$id?\s*$sigil$id\s*(?=\S)(?!is\b))/; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
our ($attr_list, $sub_decl, $sub_anon, $var_decl, $var_noattrs); |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
our %ATTRS = ( ATTR => {} ); |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
our @PHASES = qw(BEGIN CHECK INIT RUN END); |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
sub get_attr { |
33
|
0
|
|
|
0
|
0
|
0
|
my $attr = shift; |
34
|
0
|
|
0
|
|
|
0
|
my $package = shift || caller; |
35
|
0
|
0
|
|
|
|
0
|
return $ATTRS{$package} unless $attr; |
36
|
0
|
|
|
|
|
0
|
return $ATTRS{$package}{$attr}; |
37
|
|
|
|
|
|
|
} |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
sub def_handler { |
40
|
341
|
|
|
341
|
0
|
475
|
my ($location, $type, $phase, $attr, $ATTR) = @_; |
41
|
1
|
|
|
1
|
|
1315
|
use Data::Dumper 'Dumper'; |
|
1
|
|
|
|
|
9096
|
|
|
1
|
|
|
|
|
2767
|
|
42
|
341
|
50
|
66
|
|
|
738
|
return $ATTR->{rawdata} ? "*{'$attr (RAWDATA)'} = \\&$attr;" : "" |
|
|
100
|
|
|
|
|
|
43
|
|
|
|
|
|
|
if !$phase && !$type; |
44
|
330
|
100
|
100
|
|
|
1012
|
return "*{'$attr ($type)'} = \\&$attr;" |
45
|
|
|
|
|
|
|
if !$phase && $ATTR->{types}{$type}; |
46
|
299
|
100
|
100
|
|
|
1252
|
return "*{'$attr ($type $phase)'} = \\&$attr;" |
47
|
|
|
|
|
|
|
if $ATTR->{types}{$type} && $ATTR->{phases}{$phase}; |
48
|
268
|
|
|
|
|
632
|
return ""; |
49
|
|
|
|
|
|
|
} |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
sub def_attr { |
52
|
11
|
|
|
11
|
0
|
23
|
my ($sub, $data, $pkg) = @_; |
53
|
11
|
|
|
|
|
49
|
my $ATTR = $ATTRS{$pkg}{$sub} = {}; |
54
|
11
|
|
|
|
|
1260
|
$ATTR->{phases}{$_} = $data =~ s/\s*,?\s*($_)\s*,?\s*// for @PHASES; |
55
|
11
|
100
|
|
|
|
87
|
$ATTR->{phases}{INIT} = 1 unless grep $ATTR->{phases}{$_}, @PHASES; |
56
|
11
|
100
|
|
|
|
60
|
$ATTR->{handler} = $sub =~ /::/ ? $sub : $pkg."::".$sub; |
57
|
11
|
100
|
|
|
|
59
|
$data .= ',ANY' unless $data =~/\b(SCALAR|ARRAY|HASH|GLOB|CODE|VAR)\b/; |
58
|
11
|
100
|
|
|
|
74
|
$ATTR->{types}{SCALAR} = 1 if $data =~ /\b(undef|ANY|VAR|SCALAR)\b/; |
59
|
11
|
100
|
|
|
|
70
|
$ATTR->{types}{ARRAY} = 1 if $data =~ /\b(undef|ANY|VAR|ARRAY)\b/; |
60
|
11
|
100
|
|
|
|
60
|
$ATTR->{types}{HASH} = 1 if $data =~ /\b(undef|ANY|VAR|HASH)\b/; |
61
|
11
|
100
|
|
|
|
52
|
$ATTR->{types}{GLOB} = 1 if $data =~ /\b(undef|ANY|GLOB)\b/; |
62
|
11
|
100
|
|
|
|
69
|
$ATTR->{types}{CODE} = 1 if $data =~ /\b(undef|ANY|CODE)\b/; |
63
|
11
|
50
|
|
|
|
31
|
$ATTR->{rawdata} = 1 if $data =~ /\b(RAWDATA)\b/; |
64
|
11
|
|
|
|
|
27
|
return $ATTR; |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
sub def_call { |
68
|
34
|
|
|
34
|
0
|
75
|
my ($impl, $owner, $reftype, $attr, $args) = @_; |
69
|
34
|
|
|
|
|
81
|
foreach my $phase ( qw(BEGIN CHECK INIT RUN END) ) { |
70
|
170
|
|
|
|
|
1016
|
$impl->{$phase} .= |
71
|
|
|
|
|
|
|
"eval{$owner->\${\\'$attr ($reftype $phase)'}($args,'$phase');1} || " . |
72
|
|
|
|
|
|
|
"eval{$owner->\${\\'AUTOATTR ($reftype $phase)'}($args,'$phase');1};"; |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
} |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
sub def_call_prepost { |
77
|
45
|
|
|
45
|
0
|
99
|
my ($impl, $owner, $reftype, $args, $arglist) = @_; |
78
|
45
|
|
|
|
|
90
|
foreach my $handler ( qw(PREATTR POSTATTR) ) { |
79
|
90
|
|
|
|
|
131
|
foreach my $phase ( qw(BEGIN CHECK INIT RUN END) ) { |
80
|
450
|
|
|
|
|
1941
|
$impl->{$phase} .= |
81
|
|
|
|
|
|
|
"eval{$owner->\${\\'$handler ($reftype $phase)'}($args, '$handler',$arglist,'$phase');1}; "; |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
my %sigil_to_type = ( |
87
|
|
|
|
|
|
|
'$' => 'SCALAR', |
88
|
|
|
|
|
|
|
'@' => 'ARRAY', |
89
|
|
|
|
|
|
|
'%' => 'HASH', |
90
|
|
|
|
|
|
|
'&' => 'CODE', |
91
|
|
|
|
|
|
|
'*' => 'GLOB', |
92
|
|
|
|
|
|
|
); |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
sub impl_attrs { |
95
|
56
|
|
|
56
|
0
|
141
|
my ($attrs,$name,$pkg,$sigil,$decl,$type) = @_; |
96
|
56
|
|
|
|
|
83
|
my %impl; |
97
|
56
|
|
|
|
|
97
|
my $prepostargs = ""; |
98
|
56
|
|
|
|
|
83
|
my $noprepost = 0; |
99
|
56
|
50
|
33
|
|
|
351
|
my $glob = ($decl eq 'my') ? "'LEXICAL($sigil$name)'" |
|
|
100
|
|
|
|
|
|
100
|
|
|
|
|
|
|
: ($decl eq 'sub' && !$name) ? "'ANON'" |
101
|
|
|
|
|
|
|
: "\\*$name"; |
102
|
56
|
50
|
|
|
|
145
|
my $referent = $name ? "\\$sigil$name" : '$_'; |
103
|
56
|
100
|
66
|
|
|
323
|
my $location = $name && $name =~ /^(.*::)+/ ? $1 |
104
|
|
|
|
|
|
|
: '__PACKAGE__'; |
105
|
56
|
100
|
|
|
|
117
|
my $owner = $type ? $type : $location; |
106
|
56
|
|
|
|
|
138
|
my $reftype = $sigil_to_type{$sigil}; |
107
|
56
|
|
|
|
|
68
|
while (1) { |
108
|
101
|
100
|
|
|
|
12052
|
$attrs =~ m/\G:?$comments\s*($id)($parens)?$comments/gc or last; |
109
|
45
|
|
100
|
|
|
527
|
my ($attr, $data) = ($1, $2||""); |
110
|
45
|
|
|
|
|
328
|
$data =~ s/^[(]|[)]$//g; |
111
|
45
|
|
100
|
|
|
135
|
$data ||= 'undef'; |
112
|
45
|
100
|
|
|
|
118
|
if ($attr eq 'ATTR') { |
113
|
11
|
|
|
|
|
41
|
my $ATTR = def_attr($name, $data, $pkg); |
114
|
11
|
|
|
|
|
16
|
$noprepost=1; |
115
|
11
|
|
|
|
|
41
|
$impl{BEGIN} .= def_handler($location, undef, undef, $name, $ATTR); |
116
|
11
|
|
|
|
|
26
|
foreach my $type ( qw(SCALAR ARRAY HASH CODE GLOB) ) { |
117
|
55
|
|
|
|
|
114
|
$impl{BEGIN} .= def_handler($location, $type, undef, $name, $ATTR); |
118
|
55
|
|
|
|
|
87
|
foreach my $phase ( qw(BEGIN CHECK INIT RUN END) ) { |
119
|
275
|
|
|
|
|
500
|
$impl{BEGIN} .= def_handler($location, $type, $phase, $name, $ATTR); |
120
|
|
|
|
|
|
|
}} |
121
|
11
|
|
|
|
|
25
|
next; |
122
|
|
|
|
|
|
|
} |
123
|
34
|
|
33
|
|
|
228
|
$data &&= "$owner->can('$attr (RAWDATA)') ? q($data) : eval q([$data])"; |
124
|
34
|
|
|
|
|
85
|
my $args = "$glob,$referent,'$attr',$data"; |
125
|
34
|
|
|
|
|
189
|
$impl{BEGIN} .= "die 'No such $reftype attribute: '.${owner}.'::$attr' unless $owner->can('$attr ($reftype)') || $owner->can('AUTOATTR ($reftype)');"; |
126
|
34
|
|
|
|
|
114
|
def_call(\%impl, $owner, $reftype, $attr, $args); |
127
|
34
|
|
|
|
|
121
|
$prepostargs .= "[$owner,$args],"; |
128
|
|
|
|
|
|
|
} |
129
|
56
|
100
|
|
|
|
686
|
def_call_prepost(\%impl, $owner, $reftype, "$glob, $referent", "[$prepostargs]") |
130
|
|
|
|
|
|
|
unless $noprepost; |
131
|
236
|
100
|
|
|
|
3769
|
return join " ", |
132
|
280
|
|
|
|
|
630
|
map { ($_ eq 'RUN' ? "" : $_) . "{ $impl{$_} }" } |
133
|
56
|
|
|
|
|
159
|
grep { defined $impl{$_} } |
134
|
|
|
|
|
|
|
qw(BEGIN CHECK INIT RUN END); |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
sub _usage_AH_ { |
138
|
0
|
|
|
0
|
|
0
|
croak "Usage: use $_[0] autotie => {AttrName => TieClassName,...}"; |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
FILTER { |
142
|
|
|
|
|
|
|
my $caller = shift; |
143
|
|
|
|
|
|
|
my $classname = shift; |
144
|
|
|
|
|
|
|
my $autotied = ""; |
145
|
|
|
|
|
|
|
while (@_) { |
146
|
|
|
|
|
|
|
my $cmd = shift; |
147
|
|
|
|
|
|
|
next if $cmd =~ /^Perl\s*6$/; |
148
|
|
|
|
|
|
|
if ($cmd =~ /^autotie((?:ref)?)$/) { |
149
|
|
|
|
|
|
|
my $tiedata = $1 ? '$ref, @$data' : '@$data'; |
150
|
|
|
|
|
|
|
my $mapping = shift; |
151
|
|
|
|
|
|
|
_usage_AHI_ $class unless ref($mapping) eq 'HASH'; |
152
|
|
|
|
|
|
|
while (my($attr, $tieclass) = each %$mapping) { |
153
|
|
|
|
|
|
|
$tieclass =~ s/^($id)(.*)/$1/is; |
154
|
|
|
|
|
|
|
my $args = $2||'()'; |
155
|
|
|
|
|
|
|
_usage_AH_ $class unless $attr =~ $id |
156
|
|
|
|
|
|
|
&& $tieclass =~ $id; |
157
|
|
|
|
|
|
|
$attr =~ s/__CALLER__/$caller/e; |
158
|
|
|
|
|
|
|
$attr = $caller."::".$attr unless $attr =~ /::/; |
159
|
|
|
|
|
|
|
$autotied .= qq( |
160
|
|
|
|
|
|
|
eval { require $tieclass and $tieclass->import($args) }; |
161
|
|
|
|
|
|
|
sub $attr : ATTR(VAR,RUN) { |
162
|
|
|
|
|
|
|
my (\$ref, \$data) = \@_[2,4]; |
163
|
|
|
|
|
|
|
my \$type = ref(\$ref); |
164
|
|
|
|
|
|
|
if (\$type eq 'SCALAR') { |
165
|
|
|
|
|
|
|
tie \$\$ref,'$tieclass',$tiedata |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
elsif (\$type eq 'ARRAY') { |
168
|
|
|
|
|
|
|
tie \@\$ref,'$tieclass',$tiedata |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
elsif (\$type eq 'HASH') { |
171
|
|
|
|
|
|
|
tie \%\$ref,'$tieclass',$tiedata |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
else { |
174
|
|
|
|
|
|
|
print STDERR "Can't autotie a \$type\n" and exit |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
); |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
else { |
181
|
|
|
|
|
|
|
print STDERR "Can't understand $cmd\n" and exit; |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
$_ = $autotied . $_; |
185
|
|
|
|
|
|
|
pos() ||= 0; |
186
|
|
|
|
|
|
|
my $newcode; |
187
|
|
|
|
|
|
|
my $extracted; |
188
|
|
|
|
|
|
|
while (pos() < length()) { |
189
|
|
|
|
|
|
|
my @found; |
190
|
|
|
|
|
|
|
if (($extracted) = extract_quotelike($_,q//) and $extracted or |
191
|
|
|
|
|
|
|
($extracted) = extract_variable($_,q//) and $extracted ) { |
192
|
|
|
|
|
|
|
$newcode .= $extracted; |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
elsif (m/\G$sub_decl/gc) { |
195
|
|
|
|
|
|
|
my ($name, $attrs, $params) = ($1,$2||"",$3||""); |
196
|
|
|
|
|
|
|
my ($block) = extract_codeblock; |
197
|
|
|
|
|
|
|
$DB::single = 1; |
198
|
|
|
|
|
|
|
$newcode .= "sub $name $params $block ;" |
199
|
|
|
|
|
|
|
. impl_attrs($attrs,$name,$caller,'&','sub'); |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
elsif (m/\G$sub_anon/gc) { |
202
|
|
|
|
|
|
|
my ($attrs, $params) = ($1||"",$2||""); |
203
|
|
|
|
|
|
|
my ($block) = extract_codeblock; |
204
|
|
|
|
|
|
|
$newcode .= "do { local \$_ = sub $params $block; " |
205
|
|
|
|
|
|
|
. impl_attrs($attrs,undef,$caller,'&','sub') |
206
|
|
|
|
|
|
|
. ' ; $_ }'; |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
elsif (m/\G$var_noattrs/gc) { |
209
|
|
|
|
|
|
|
$newcode .= $1; |
210
|
|
|
|
|
|
|
} |
211
|
|
|
|
|
|
|
elsif (m/\G$var_decl/gc) { |
212
|
|
|
|
|
|
|
my ($decl, $type, $sigil, $name, $attrs, $nextchar) |
213
|
|
|
|
|
|
|
= ($1, $2||"",$3, $4, $5||"", $6); |
214
|
|
|
|
|
|
|
$newcode .= "$decl $type $sigil$name; " |
215
|
|
|
|
|
|
|
. impl_attrs($attrs,$name,$caller,$sigil,$decl,$type) |
216
|
|
|
|
|
|
|
. "; " |
217
|
|
|
|
|
|
|
. ($nextchar eq '=' ? "$sigil$name " : "") |
218
|
|
|
|
|
|
|
. $nextchar; |
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
elsif (m/\G($id|$parens|.)/gcs) { |
221
|
|
|
|
|
|
|
$newcode .= $1; |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
else { |
224
|
|
|
|
|
|
|
die "Internal error"; |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
$_ = $newcode; |
228
|
|
|
|
|
|
|
# print STDERR if $_; |
229
|
|
|
|
|
|
|
} |
230
|
|
|
|
|
|
|
qr/^__(END|DATA)__$/m; |
231
|
|
|
|
|
|
|
|
232
|
1
|
|
|
1
|
|
14
|
no warnings; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
380
|
|
233
|
|
|
|
|
|
|
my $filterer = *import{CODE}; |
234
|
2
|
|
|
2
|
|
7
|
my $mod_filterer = sub { unshift @_, scalar caller; goto &$filterer }; |
|
2
|
|
|
|
|
10
|
|
235
|
|
|
|
|
|
|
*import = sub { |
236
|
2
|
50
|
|
2
|
|
53747
|
if (grep /Perl\s*6/, @_) { |
237
|
0
|
|
|
|
|
0
|
$attr_list = $attr_list6; |
238
|
0
|
|
|
|
|
0
|
$sub_decl = $sub_decl6; |
239
|
0
|
|
|
|
|
0
|
$sub_anon = $sub_anon6; |
240
|
0
|
|
|
|
|
0
|
$var_decl = $var_decl6; |
241
|
0
|
|
|
|
|
0
|
$var_noattrs = $var_noattr6; |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
else { |
244
|
2
|
|
|
|
|
6
|
$attr_list = $attr_list5; |
245
|
2
|
|
|
|
|
4
|
$sub_decl = $sub_decl5; |
246
|
2
|
|
|
|
|
3
|
$sub_anon = $sub_anon5; |
247
|
2
|
|
|
|
|
4
|
$var_decl = $var_decl5; |
248
|
2
|
|
|
|
|
13
|
$var_noattrs = $var_noattr5; |
249
|
|
|
|
|
|
|
} |
250
|
2
|
|
|
|
|
5
|
*{caller()."::import"} = $mod_filterer; |
|
2
|
|
|
|
|
13
|
|
251
|
2
|
|
|
|
|
12
|
goto &$mod_filterer |
252
|
|
|
|
|
|
|
}; |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
1; |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
__END__ |