line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# B::DeparseTree tree-building routines. |
2
|
|
|
|
|
|
|
# Copyright (c) 2018 Rocky Bernstein |
3
|
|
|
|
|
|
|
# All rights reserved. |
4
|
|
|
|
|
|
|
# This module is free software; you can redistribute and/or modify |
5
|
|
|
|
|
|
|
# it under the same terms as Perl itself. |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
# This is based on the module B::Deparse by Stephen McCamant. |
8
|
|
|
|
|
|
|
# It has been extended save tree structure, and is addressible |
9
|
|
|
|
|
|
|
# by opcode address. |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
# Note the package name. It is *not* B::DeparseTree::Tree. |
12
|
|
|
|
|
|
|
# In the future there may be a version of this that doesn't |
13
|
|
|
|
|
|
|
# save as much information, but just stores enough to extract |
14
|
|
|
|
|
|
|
# a string, which would be a slightly more heavyweight version of |
15
|
|
|
|
|
|
|
# B::Deparse. |
16
|
|
|
|
|
|
|
package B::DeparseTree::SyntaxTree; |
17
|
|
|
|
|
|
|
|
18
|
8
|
|
|
8
|
|
2887
|
use B::DeparseTree::TreeNode; |
|
8
|
|
|
|
|
27
|
|
|
8
|
|
|
|
|
13907
|
|
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
our($VERSION, @EXPORT, @ISA); |
21
|
|
|
|
|
|
|
$VERSION = '3.2.0'; |
22
|
|
|
|
|
|
|
@ISA = qw(Exporter B::DeparseTree); |
23
|
|
|
|
|
|
|
@EXPORT = qw( |
24
|
|
|
|
|
|
|
combine |
25
|
|
|
|
|
|
|
combine2str |
26
|
|
|
|
|
|
|
get_info_and_str |
27
|
|
|
|
|
|
|
expand_simple_spec |
28
|
|
|
|
|
|
|
indent_less |
29
|
|
|
|
|
|
|
indent_more |
30
|
|
|
|
|
|
|
indent_value |
31
|
|
|
|
|
|
|
info2str |
32
|
|
|
|
|
|
|
info_from_list |
33
|
|
|
|
|
|
|
info_from_template |
34
|
|
|
|
|
|
|
info_from_string |
35
|
|
|
|
|
|
|
info_from_text |
36
|
|
|
|
|
|
|
template_engine |
37
|
|
|
|
|
|
|
template2str |
38
|
|
|
|
|
|
|
); |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
sub combine($$$) |
41
|
|
|
|
|
|
|
{ |
42
|
0
|
|
|
0
|
0
|
0
|
my ($self, $sep, $items) = @_; |
43
|
|
|
|
|
|
|
# FIXME: loop over $item, testing type. |
44
|
0
|
0
|
|
|
|
0
|
Carp::confess("should be a reference to a array: is $items") unless |
45
|
|
|
|
|
|
|
ref $items eq 'ARRAY'; |
46
|
0
|
|
|
|
|
0
|
my @result = (); |
47
|
0
|
|
|
|
|
0
|
foreach my $item (@$items) { |
48
|
0
|
|
|
|
|
0
|
my $add; |
49
|
0
|
0
|
|
|
|
0
|
if (ref $item) { |
50
|
0
|
0
|
0
|
|
|
0
|
if (ref $item eq 'ARRAY' and scalar(@$item) == 2) { |
|
|
0
|
|
|
|
|
|
51
|
0
|
|
|
|
|
0
|
$add = [$item->[0], $item->[1]]; |
52
|
0
|
|
|
|
|
0
|
} elsif (eval{$item->isa("B::DeparseTree::TreeNode")}) { |
53
|
0
|
|
|
|
|
0
|
$add = [$item->{text}, $item->{addr}]; |
54
|
|
|
|
|
|
|
# First item is text and second item is op address. |
55
|
|
|
|
|
|
|
} else { |
56
|
0
|
|
|
|
|
0
|
Carp::confess("don't know what to do with $item"); |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
} else { |
59
|
0
|
|
|
|
|
0
|
$add = $item; |
60
|
|
|
|
|
|
|
} |
61
|
0
|
0
|
0
|
|
|
0
|
push @result, $sep if @result && $sep; |
62
|
0
|
|
|
|
|
0
|
push @result, $add; |
63
|
|
|
|
|
|
|
} |
64
|
0
|
|
|
|
|
0
|
return @result; |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
sub combine2str($$$) |
68
|
|
|
|
|
|
|
{ |
69
|
168
|
|
|
168
|
0
|
285
|
my ($self, $sep, $items) = @_; |
70
|
168
|
|
|
|
|
214
|
my $result = ''; |
71
|
168
|
|
|
|
|
287
|
foreach my $item (@$items) { |
72
|
728
|
100
|
|
|
|
1070
|
$result .= $sep if $result; |
73
|
728
|
100
|
|
|
|
920
|
if (ref $item) { |
74
|
2
|
50
|
33
|
|
|
6
|
if (ref $item eq 'ARRAY' and scalar(@$item) == 2) { |
|
|
50
|
|
|
|
|
|
75
|
|
|
|
|
|
|
# First item is text and second item is op address. |
76
|
0
|
|
|
|
|
0
|
$result .= $self->info2str($item->[0]); |
77
|
2
|
|
|
|
|
7
|
} elsif (eval{$item->isa("B::DeparseTree::TreeNode")}) { |
78
|
2
|
50
|
|
|
|
5
|
if (exists $item->{fmt}) { |
79
|
2
|
|
|
|
|
5
|
$result .= $self->template2str($item); |
80
|
|
|
|
|
|
|
} else { |
81
|
0
|
|
|
|
|
0
|
$result .= $self->info2str($item); |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
} else { |
84
|
0
|
|
|
|
|
0
|
Carp::confess("Invalid ref item ref($item)"); |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
} else { |
87
|
|
|
|
|
|
|
# FIXME: add this and remove errors |
88
|
726
|
50
|
|
|
|
1076
|
if (index($item, '@B::DeparseTree::TreeNode') > 0) { |
89
|
0
|
|
|
|
|
0
|
Carp::confess("\@B::DeparseTree::TreeNode as an item is probably wrong"); |
90
|
|
|
|
|
|
|
} |
91
|
726
|
|
|
|
|
1032
|
$result .= $item; |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
} |
94
|
168
|
|
|
|
|
370
|
return $result; |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
sub expand_simple_spec($$) |
98
|
|
|
|
|
|
|
{ |
99
|
38331
|
|
|
38331
|
0
|
55614
|
my ($self, $fmt) = @_; |
100
|
38331
|
|
|
|
|
43016
|
my $result = ''; |
101
|
38331
|
|
|
|
|
67106
|
while ((my $k=index($fmt, '%')) >= 0) { |
102
|
26583
|
|
|
|
|
35044
|
$result .= substr($fmt, 0, $k); |
103
|
26583
|
|
|
|
|
34500
|
my $spec = substr($fmt, $k, 2); |
104
|
26583
|
|
|
|
|
32801
|
$fmt = substr($fmt, $k+2); |
105
|
|
|
|
|
|
|
|
106
|
26583
|
50
|
|
|
|
55666
|
if ($spec eq '%%') { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
107
|
0
|
|
|
|
|
0
|
$result .= '%'; |
108
|
|
|
|
|
|
|
} elsif ($spec eq '%+') { |
109
|
2785
|
|
|
|
|
5244
|
$result .= $self->indent_more(); |
110
|
|
|
|
|
|
|
} elsif ($spec eq '%-') { |
111
|
2785
|
|
|
|
|
5027
|
$result .= $self->indent_less(); |
112
|
|
|
|
|
|
|
} elsif ($spec eq '%|') { |
113
|
21013
|
|
|
|
|
31420
|
$result .= $self->indent_value(); |
114
|
|
|
|
|
|
|
} else { |
115
|
0
|
|
|
|
|
0
|
Carp::confess("Unknown spec $spec") |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
} |
118
|
38331
|
100
|
|
|
|
60768
|
$result .= $fmt if $fmt; |
119
|
38331
|
|
|
|
|
67307
|
return $result; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
sub indent_less($$) { |
123
|
2785
|
|
|
2785
|
0
|
4608
|
my ($self, $check_level) = @_; |
124
|
2785
|
50
|
|
|
|
5440
|
$check_level = 0 if !defined $check_level; |
125
|
|
|
|
|
|
|
|
126
|
2785
|
|
|
|
|
4404
|
$self->{level} -= $self->{'indent_size'}; |
127
|
2785
|
|
|
|
|
3716
|
my $level = $self->{level}; |
128
|
2785
|
50
|
|
|
|
4755
|
if ($check_level < 0) { |
129
|
0
|
0
|
|
|
|
0
|
Carp::confess("mismatched indent/dedent") if $check_level; |
130
|
0
|
|
|
|
|
0
|
$level = 0; |
131
|
0
|
|
|
|
|
0
|
$self->{level} = 0; |
132
|
|
|
|
|
|
|
} |
133
|
2785
|
|
|
|
|
4892
|
return $self->indent_value(); |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
sub indent_more($) { |
137
|
2785
|
|
|
2785
|
0
|
4085
|
my ($self) = @_; |
138
|
2785
|
|
|
|
|
4400
|
$self->{level} += $self->{'indent_size'}; |
139
|
2785
|
|
|
|
|
4407
|
return $self->indent_value(); |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
sub indent_value($) { |
143
|
26583
|
|
|
26583
|
0
|
33562
|
my ($self) = @_; |
144
|
26583
|
|
|
|
|
33154
|
my $level = $self->{level}; |
145
|
26583
|
50
|
|
|
|
38221
|
if ($self->{'use_tabs'}) { |
146
|
0
|
|
|
|
|
0
|
return "\t" x ($level / 8) . " " x ($level % 8); |
147
|
|
|
|
|
|
|
} else { |
148
|
26583
|
|
|
|
|
87436
|
return " " x $level; |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
sub info2str($$) |
153
|
|
|
|
|
|
|
{ |
154
|
122031
|
|
|
122031
|
0
|
151527
|
my ($self, $item) = @_; |
155
|
122031
|
|
|
|
|
136886
|
my $result = ''; |
156
|
122031
|
100
|
|
|
|
180980
|
if (ref $item) { |
157
|
114088
|
50
|
33
|
|
|
222137
|
if (ref $item eq 'ARRAY' and scalar(@$item) == 2) { |
|
|
50
|
|
|
|
|
|
158
|
|
|
|
|
|
|
# This code is going away... |
159
|
0
|
|
|
|
|
0
|
Carp::confess("fixme"); |
160
|
0
|
|
|
|
|
0
|
$result = $item->[0]; |
161
|
114088
|
|
|
|
|
266725
|
} elsif (eval{$item->isa("B::DeparseTree::TreeNode")}) { |
162
|
114088
|
100
|
66
|
|
|
234704
|
if (exists $item->{fmt}) { |
|
|
100
|
|
|
|
|
|
163
|
52066
|
|
|
|
|
85421
|
$result .= $self->template2str($item); |
164
|
52066
|
100
|
|
|
|
99313
|
if ($item->{maybe_parens}) { |
165
|
7243
|
|
|
|
|
10064
|
my $mp = $item->{maybe_parens}; |
166
|
7243
|
100
|
66
|
|
|
21498
|
if ($mp->{force} || $mp->{parens}) { |
167
|
306
|
|
|
|
|
733
|
$result = "($result)"; |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
} elsif (!exists $item->{texts} && exists $item->{text}) { |
171
|
|
|
|
|
|
|
# Is a constant string |
172
|
61856
|
|
|
|
|
89452
|
$result .= $item->{text}; |
173
|
|
|
|
|
|
|
} else { |
174
|
|
|
|
|
|
|
$result = $self->combine2str($item->{sep}, |
175
|
166
|
|
|
|
|
361
|
$item->{texts}); |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
} else { |
179
|
0
|
|
|
|
|
0
|
Carp::confess("Invalid ref item ref($item)"); |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
} else { |
182
|
|
|
|
|
|
|
# FIXME: add this and remove errors |
183
|
7943
|
50
|
|
|
|
14984
|
if (index($item, '@B::DeparseTree::TreeNode') > 0) { |
184
|
0
|
|
|
|
|
0
|
Carp::confess("\@B::DeparseTree::TreeNode as an item is probably wrong"); |
185
|
|
|
|
|
|
|
} |
186
|
7943
|
|
|
|
|
10488
|
$result = $item; |
187
|
|
|
|
|
|
|
} |
188
|
122031
|
|
|
|
|
211144
|
return $result; |
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
# Create an info structure from a list of strings |
192
|
|
|
|
|
|
|
# FIXME: $deparse (or rather $self) should be first |
193
|
|
|
|
|
|
|
sub info_from_list($$$$$$) |
194
|
|
|
|
|
|
|
{ |
195
|
29
|
|
|
29
|
0
|
87
|
my ($op, $self, $texts, $sep, $type, $opts) = @_; |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
# Set undef in "texts" argument position because we are going to create |
198
|
|
|
|
|
|
|
# our own text from the $texts. |
199
|
29
|
|
|
|
|
110
|
my $info = B::DeparseTree::TreeNode->new($op, $self, $texts, undef, |
200
|
|
|
|
|
|
|
$type, $opts); |
201
|
29
|
|
|
|
|
75
|
$info->{sep} = $sep; |
202
|
29
|
|
|
|
|
47
|
my $text = ''; |
203
|
29
|
|
|
|
|
57
|
foreach my $item (@$texts) { |
204
|
116
|
100
|
100
|
|
|
309
|
$text .= $sep if $text and $sep; |
205
|
116
|
50
|
|
|
|
217
|
if(ref($item) eq 'ARRAY'){ |
|
|
50
|
|
|
|
|
|
206
|
0
|
|
|
|
|
0
|
$text .= $item->[0]; |
207
|
116
|
|
|
|
|
606
|
} elsif (eval{$item->isa("B::DeparseTree::TreeNode")}) { |
208
|
0
|
|
|
|
|
0
|
$text .= $item->{text}; |
209
|
|
|
|
|
|
|
} else { |
210
|
116
|
|
|
|
|
255
|
$text .= $item; |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
} |
213
|
|
|
|
|
|
|
|
214
|
29
|
|
|
|
|
70
|
$info->{text} = $text; |
215
|
29
|
100
|
|
|
|
74
|
if ($opts->{maybe_parens}) { |
216
|
4
|
|
|
|
|
4
|
my ($obj, $context, $precedence) = @{$opts->{maybe_parens}}; |
|
4
|
|
|
|
|
36
|
|
217
|
4
|
|
|
|
|
12
|
my $parens = B::DeparseTree::TreeNode::parens_test($obj, $context, $precedence); |
218
|
|
|
|
|
|
|
$self->{maybe_parens} = { |
219
|
|
|
|
|
|
|
context => $context, |
220
|
|
|
|
|
|
|
precedence => $precedence, |
221
|
4
|
50
|
|
|
|
19
|
force => $obj->{'parens'}, |
222
|
|
|
|
|
|
|
parens => $parens ? 'true' : '' |
223
|
|
|
|
|
|
|
}; |
224
|
4
|
50
|
33
|
|
|
23
|
$info->{text} = "($info->{text})" if exists $info->{text} and $parens; |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
29
|
|
|
|
|
126
|
return $info |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
# Create an info structure a template pattern |
231
|
|
|
|
|
|
|
sub info_from_template($$$$$) { |
232
|
12206
|
|
|
12206
|
0
|
26969
|
my ($self, $type, $op, $fmt, $indexes, $args, $opts) = @_; |
233
|
12206
|
100
|
|
|
|
23504
|
$opts = {} unless defined($opts); |
234
|
|
|
|
|
|
|
# if (ref($args) ne "ARRAY") { |
235
|
|
|
|
|
|
|
# use Enbugger "trepan"; Enbugger->stop; |
236
|
|
|
|
|
|
|
# } |
237
|
12206
|
|
|
|
|
20289
|
my @args = @$args; |
238
|
12206
|
|
|
|
|
29965
|
my $info = B::DeparseTree::TreeNode->new($op, $self, $args, undef, $type, $opts); |
239
|
|
|
|
|
|
|
|
240
|
12206
|
100
|
|
|
|
30048
|
$indexes = [0..$#args] unless defined $indexes; |
241
|
12206
|
|
|
|
|
19824
|
$info->{'indexes'} = $indexes; |
242
|
12206
|
|
|
|
|
24065
|
my $text = $self->template_engine($fmt, $indexes, $args); |
243
|
|
|
|
|
|
|
|
244
|
12206
|
|
|
|
|
26289
|
$info->{'fmt'} = $fmt; |
245
|
12206
|
|
|
|
|
21412
|
$info->{'text'} = $self->template_engine($fmt, $indexes, $args); |
246
|
|
|
|
|
|
|
|
247
|
12206
|
100
|
|
|
|
24179
|
if (! defined $op) { |
248
|
1341
|
|
|
|
|
2742
|
$info->{addr} = ++$self->{'last_fake_addr'}; |
249
|
1341
|
|
|
|
|
4266
|
$self->{optree}{$info->{addr}} = $info; |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
|
252
|
12206
|
50
|
|
|
|
20887
|
if ($opts->{'relink_children'}) { |
253
|
|
|
|
|
|
|
# FIXME we should specify which children to relink |
254
|
0
|
|
|
|
|
0
|
for (my $i=0; $i < scalar @$args; $i++) { |
255
|
0
|
0
|
|
|
|
0
|
if ($args[$i]->isa("B::DeparseTree::TreeNode")) { |
256
|
0
|
|
|
|
|
0
|
$args[$i]{parent} = $info->{addr}; |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
# Link the parent of Deparse::Tree::TreeNodes to this node. |
262
|
12206
|
50
|
|
|
|
19314
|
if ($opts->{'synthesized_nodes'}) { |
263
|
0
|
|
|
|
|
0
|
foreach my $node (@{$opts->{'synthesized_nodes'}}) { |
|
0
|
|
|
|
|
0
|
|
264
|
0
|
|
|
|
|
0
|
$node->{parent} = $info->{addr}; |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
# Need to handle maybe_parens since B::DeparseNode couldn't do that |
269
|
|
|
|
|
|
|
# as it was passed a ref ARRAY rather than a string. |
270
|
12206
|
100
|
|
|
|
18813
|
if ($opts->{maybe_parens}) { |
271
|
1560
|
|
|
|
|
2251
|
my ($obj, $context, $precedence) = @{$opts->{maybe_parens}}; |
|
1560
|
|
|
|
|
3366
|
|
272
|
1560
|
|
|
|
|
4511
|
my $parens = B::DeparseTree::TreeNode::parens_test($obj, |
273
|
|
|
|
|
|
|
$context, $precedence); |
274
|
|
|
|
|
|
|
$info->{maybe_parens} = { |
275
|
|
|
|
|
|
|
context => $context, |
276
|
|
|
|
|
|
|
precedence => $precedence, |
277
|
1560
|
100
|
|
|
|
9132
|
force => $obj->{'parens'}, |
278
|
|
|
|
|
|
|
parens => $parens ? 'true' : '' |
279
|
|
|
|
|
|
|
}; |
280
|
1560
|
100
|
66
|
|
|
6098
|
$info->{text} = "($info->{text})" if exists $info->{text} and $parens; |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
|
283
|
12206
|
|
|
|
|
42921
|
return $info; |
284
|
|
|
|
|
|
|
} |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
# Create an info structure from a single string |
287
|
|
|
|
|
|
|
sub info_from_string($$$$$) |
288
|
|
|
|
|
|
|
{ |
289
|
13779
|
|
|
13779
|
0
|
28034
|
my ($self, $type, $op, $str, $opts) = @_; |
290
|
13779
|
|
100
|
|
|
40858
|
$opts ||= {}; |
291
|
13779
|
|
|
|
|
36674
|
return B::DeparseTree::TreeNode->new($op, $self, $str, undef, |
292
|
|
|
|
|
|
|
$type, $opts); |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
# OBSOLETE: Create an info structure from a single string |
296
|
|
|
|
|
|
|
# FIXME: remove this |
297
|
|
|
|
|
|
|
sub info_from_text($$$$$) |
298
|
|
|
|
|
|
|
{ |
299
|
18
|
|
|
18
|
0
|
41
|
my ($op, $self, $text, $type, $opts) = @_; |
300
|
|
|
|
|
|
|
# Use this to smoke outt calls |
301
|
|
|
|
|
|
|
# use Enbugger 'trepan'; Enbugger->stop; |
302
|
18
|
|
|
|
|
40
|
return $self->info_from_string($type, $op, $text, $opts) |
303
|
|
|
|
|
|
|
} |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
# List of suffix characters that are handled by "expand_simple_spec()". |
306
|
8
|
|
|
8
|
|
74
|
use constant SIMPLE_SPEC => '%+-|'; |
|
8
|
|
|
|
|
18
|
|
|
8
|
|
|
|
|
7931
|
|
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
# Extract the string at $args[$index] and if |
309
|
|
|
|
|
|
|
# we are looking for that position include where we are in |
310
|
|
|
|
|
|
|
# that position |
311
|
|
|
|
|
|
|
sub get_info_and_str($$$) |
312
|
|
|
|
|
|
|
{ |
313
|
121023
|
|
|
121023
|
0
|
167641
|
my ($self, $index, $args) = @_; |
314
|
121023
|
|
|
|
|
150034
|
my $info = $args->[$index]; |
315
|
121023
|
|
|
|
|
166482
|
my $str = $self->info2str($info); |
316
|
121023
|
|
|
|
|
230847
|
return ($info, $str); |
317
|
|
|
|
|
|
|
} |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
sub template_engine($$$$) |
320
|
|
|
|
|
|
|
{ |
321
|
76480
|
|
|
76480
|
0
|
123561
|
my ($self, $fmt, $indexes, $args, $find_addr) = @_; |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
# use Data::Dumper; |
324
|
|
|
|
|
|
|
# print "-----\n"; |
325
|
|
|
|
|
|
|
# p $args; |
326
|
|
|
|
|
|
|
# print "'======\n"; |
327
|
|
|
|
|
|
|
# print $fmt, "\n" |
328
|
|
|
|
|
|
|
# print $args, "\n"; |
329
|
|
|
|
|
|
|
|
330
|
76480
|
|
|
|
|
86139
|
my $i = 0; |
331
|
76480
|
50
|
|
|
|
109660
|
$find_addr = -2 unless $find_addr; |
332
|
|
|
|
|
|
|
|
333
|
76480
|
|
|
|
|
86724
|
my $start_fmt = $fmt; # used in error messages |
334
|
76480
|
|
|
|
|
109901
|
my @args = @$args; |
335
|
|
|
|
|
|
|
|
336
|
76480
|
|
|
|
|
87819
|
my $result = ''; |
337
|
76480
|
|
|
|
|
82378
|
my $find_pos = undef; |
338
|
76480
|
|
|
|
|
144543
|
while ((my $k=index($fmt, '%')) >= 0) { |
339
|
88927
|
|
|
|
|
138660
|
$result .= substr($fmt, 0, $k); |
340
|
88927
|
|
|
|
|
112619
|
my $spec = substr($fmt, $k, 2); |
341
|
88927
|
|
|
|
|
117302
|
$fmt = substr($fmt, $k+2); |
342
|
|
|
|
|
|
|
|
343
|
88927
|
100
|
|
|
|
196249
|
if (index(SIMPLE_SPEC, substr($spec, 1, 1)) >= 0) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
344
|
8320
|
|
|
|
|
13955
|
$result .= $self->expand_simple_spec($spec); |
345
|
|
|
|
|
|
|
} elsif ($spec eq "%c") { |
346
|
|
|
|
|
|
|
# Insert child entry |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
# FIXME: turn this into a subroutine. |
349
|
49716
|
50
|
|
|
|
55859
|
if ($i >= scalar @{$indexes}) { |
|
49716
|
|
|
|
|
80757
|
|
350
|
0
|
|
|
|
|
0
|
Carp::confess("Need another entry in args_spec for %c in fmt: $start_fmt"); |
351
|
|
|
|
|
|
|
} |
352
|
49716
|
|
|
|
|
69449
|
my $index = $indexes->[$i++]; |
353
|
49716
|
50
|
|
|
|
76300
|
if ($index >= scalar @args) { |
354
|
0
|
|
|
|
|
0
|
Carp::confess("$index in $start_fmt for %c is too large; should be less " . |
355
|
|
|
|
|
|
|
"than scalar(@args)"); |
356
|
|
|
|
|
|
|
} |
357
|
49716
|
|
|
|
|
55144
|
my $str; |
358
|
49716
|
|
|
|
|
76888
|
my ($info, $str) = $self->get_info_and_str($index, $args); |
359
|
49716
|
50
|
66
|
|
|
145393
|
if (ref($info) && $info->{'addr'} == $find_addr) { |
360
|
0
|
|
|
|
|
0
|
my $len = length($result); |
361
|
|
|
|
|
|
|
$len++ if (exists $info->{maybe_parens} |
362
|
0
|
0
|
0
|
|
|
0
|
&& $info->{maybe_parens}{parens}); |
363
|
0
|
|
|
|
|
0
|
$find_pos = [$len, length($str)]; |
364
|
|
|
|
|
|
|
} |
365
|
49716
|
|
|
|
|
115665
|
$result .= $str; |
366
|
|
|
|
|
|
|
} elsif ($spec eq "%C") { |
367
|
|
|
|
|
|
|
# Insert separator between child entry lists |
368
|
11748
|
|
|
|
|
13909
|
my ($low, $high, $sub_spec) = @{$indexes->[$i++]}; |
|
11748
|
|
|
|
|
23516
|
|
369
|
11748
|
|
|
|
|
22576
|
my $sep = $self->expand_simple_spec($sub_spec); |
370
|
11748
|
|
|
|
|
16223
|
my $list = ''; |
371
|
11748
|
|
|
|
|
22502
|
for (my $j=$low; $j<=$high; $j++) { |
372
|
31243
|
100
|
|
|
|
52038
|
$result .= $sep if $j > $low; |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
# FIXME: Remove duplicate code |
375
|
31243
|
|
|
|
|
50960
|
my ($info, $str) = $self->get_info_and_str($j, $args); |
376
|
31243
|
50
|
33
|
|
|
89346
|
if (ref($info) && $info->{'addr'} == $find_addr) { |
377
|
0
|
|
|
|
|
0
|
my $len = length($result); |
378
|
|
|
|
|
|
|
$len++ if (exists $info->{maybe_parens} |
379
|
0
|
0
|
0
|
|
|
0
|
&& $info->{maybe_parens}{parens}); |
380
|
0
|
|
|
|
|
0
|
$find_pos = [$len, length($str)]; |
381
|
|
|
|
|
|
|
} |
382
|
31243
|
|
|
|
|
68773
|
$result .= $str; |
383
|
|
|
|
|
|
|
} |
384
|
|
|
|
|
|
|
} elsif ($spec eq "%f") { |
385
|
|
|
|
|
|
|
# Run maybe_parens_func |
386
|
0
|
|
|
|
|
0
|
my $fn_name = shift @args; |
387
|
0
|
|
|
|
|
0
|
my ($cx, $prec) = @{$indexes->[$i++]}; |
|
0
|
|
|
|
|
0
|
|
388
|
0
|
|
|
|
|
0
|
my $params = $self->template_engine("%C", [[0, $#args], ', ']); |
389
|
0
|
|
|
|
|
0
|
$result .= B::Deparse::maybe_parens_func($self, $fn_name, $params, $cx, $prec); |
390
|
|
|
|
|
|
|
} elsif ($spec eq "%F") { |
391
|
|
|
|
|
|
|
# Run a transformation function |
392
|
880
|
50
|
|
|
|
1853
|
if ($i >= scalar@$indexes) { |
393
|
0
|
|
|
|
|
0
|
Carp::confess("Need another entry in args_spec for %%F fmt: $start_fmt"); |
394
|
|
|
|
|
|
|
} |
395
|
880
|
|
|
|
|
1171
|
my ($arg_index, $transform_fn) = @{$indexes->[$i++]}; |
|
880
|
|
|
|
|
1917
|
|
396
|
880
|
50
|
|
|
|
2176
|
if ($arg_index >= scalar @args) { |
397
|
0
|
|
|
|
|
0
|
Carp::confess("argument index $arg_index in $start_fmt for %%F is too large; should be less than @$args"); |
398
|
|
|
|
|
|
|
} |
399
|
880
|
50
|
|
|
|
2293
|
if (ref($transform_fn ne 'CODE')) { |
400
|
0
|
|
|
|
|
0
|
Carp::confess("transformation function $transform_fn is not CODE"); |
401
|
|
|
|
|
|
|
} |
402
|
880
|
|
|
|
|
1581
|
my ($arg) = $args[$arg_index]; |
403
|
880
|
|
|
|
|
2136
|
$result .= $transform_fn->($arg); |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
} elsif ($spec eq "%;") { |
406
|
|
|
|
|
|
|
# Insert semicolons and indented newlines between statements. |
407
|
|
|
|
|
|
|
# Don't insert them around empty strings - some OPs |
408
|
|
|
|
|
|
|
# don't have an text associated with them. |
409
|
|
|
|
|
|
|
# Finally, replace semicolon a the end of statement that |
410
|
|
|
|
|
|
|
# end in "}" with a \n and proper indent. |
411
|
18263
|
|
|
|
|
29436
|
my $sep = $self->expand_simple_spec(";\n%|"); |
412
|
18263
|
|
|
|
|
23238
|
my $start_size = length($result); |
413
|
18263
|
|
|
|
|
36014
|
for (my $j=0; $j< @args; $j++) { |
414
|
40064
|
|
|
|
|
49713
|
my $old_result = $result; |
415
|
40064
|
100
|
100
|
|
|
94063
|
if ($j > 0 && length($result) > $start_size) { |
416
|
|
|
|
|
|
|
# Remove any prior ;\n |
417
|
26538
|
100
|
|
|
|
46528
|
$result = substr($result, 0, -1) if substr($result, -1) eq "\n"; |
418
|
26538
|
100
|
|
|
|
41690
|
$result = substr($result, 0, -1) if substr($result, -1) eq ";"; |
419
|
|
|
|
|
|
|
## The below needs to be done based on whether the previous construct is a compound statement or not. |
420
|
|
|
|
|
|
|
## That could be added in a trailing format specifier for it. |
421
|
|
|
|
|
|
|
## "sub {...}" and "$h = {..}" need a semicolon while "if () {...}" doesn't. |
422
|
|
|
|
|
|
|
# if (substr($result, -1) eq "}" & $j < $#args) { |
423
|
|
|
|
|
|
|
# # Omit ; from sep. FIXME: do this based on an option? |
424
|
|
|
|
|
|
|
# $result .= substr($sep, 1); |
425
|
|
|
|
|
|
|
# } else { |
426
|
|
|
|
|
|
|
# $result .= $sep; |
427
|
|
|
|
|
|
|
# } |
428
|
26538
|
|
|
|
|
35691
|
$result .= $sep; |
429
|
|
|
|
|
|
|
} |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
# FIXME: Remove duplicate code |
432
|
40064
|
|
|
|
|
64115
|
my ($info, $str) = $self->get_info_and_str($j, $args); |
433
|
40064
|
50
|
66
|
|
|
107603
|
if (ref($info) && $info->{'addr'} == $find_addr) { |
434
|
0
|
|
|
|
|
0
|
my $len = length($result); |
435
|
0
|
0
|
0
|
|
|
0
|
$len++ if exists $info->{maybe_parens} and $info->{maybe_parens}{parens}; |
436
|
0
|
|
|
|
|
0
|
$find_pos = [length($result), length($str)]; |
437
|
|
|
|
|
|
|
} |
438
|
40064
|
100
|
|
|
|
56926
|
if (!$str) { |
439
|
3507
|
|
|
|
|
8270
|
$result = $old_result; |
440
|
|
|
|
|
|
|
} else { |
441
|
36557
|
|
|
|
|
98972
|
$result .= $str |
442
|
|
|
|
|
|
|
} |
443
|
|
|
|
|
|
|
} |
444
|
|
|
|
|
|
|
# # FIXME: Add the final ';' based on an option? |
445
|
|
|
|
|
|
|
# if ($result and not |
446
|
|
|
|
|
|
|
# (substr($result, -1) eq ';' or |
447
|
|
|
|
|
|
|
# (substr($result, -1) eq ';\n'))) { |
448
|
|
|
|
|
|
|
# $result .= ';' if $result and substr($result, -1) ne ';'; |
449
|
|
|
|
|
|
|
# } |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
} elsif ($spec eq "\cS") { |
452
|
|
|
|
|
|
|
# FIXME: not handled yet |
453
|
|
|
|
|
|
|
; |
454
|
|
|
|
|
|
|
} else { |
455
|
|
|
|
|
|
|
# We have % with a non-special symbol. Just preserve those. |
456
|
0
|
|
|
|
|
0
|
$result .= $spec; |
457
|
|
|
|
|
|
|
} |
458
|
|
|
|
|
|
|
} |
459
|
76480
|
100
|
|
|
|
125946
|
$result .= $fmt if $fmt; |
460
|
76480
|
50
|
|
|
|
109027
|
if ($find_addr != -2) { |
461
|
|
|
|
|
|
|
# want result and position |
462
|
0
|
|
|
|
|
0
|
return $result, $find_pos; |
463
|
|
|
|
|
|
|
} |
464
|
|
|
|
|
|
|
# want just result |
465
|
76480
|
|
|
|
|
167455
|
return $result; |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
} |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
sub template2str($$) { |
470
|
52068
|
|
|
52068
|
0
|
68968
|
my ($self, $info) = @_; |
471
|
|
|
|
|
|
|
return $self->template_engine($info->{fmt}, |
472
|
|
|
|
|
|
|
$info->{indexes}, |
473
|
52068
|
|
|
|
|
89029
|
$info->{texts}); |
474
|
|
|
|
|
|
|
} |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
1; |