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
|
3
|
|
|
3
|
|
1119
|
use B::DeparseTree::Node; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
4265
|
|
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
|
593
|
|
|
593
|
0
|
1576
|
my ($self, $sep, $items) = @_; |
43
|
|
|
|
|
|
|
# FIXME: loop over $item, testing type. |
44
|
593
|
50
|
|
|
|
1542
|
Carp::confess("should be a reference to a array: is $items") unless |
45
|
|
|
|
|
|
|
ref $items eq 'ARRAY'; |
46
|
593
|
|
|
|
|
958
|
my @result = (); |
47
|
593
|
|
|
|
|
1473
|
foreach my $item (@$items) { |
48
|
764
|
|
|
|
|
981
|
my $add; |
49
|
764
|
50
|
|
|
|
1338
|
if (ref $item) { |
50
|
764
|
50
|
33
|
|
|
1718
|
if (ref $item eq 'ARRAY' and scalar(@$item) == 2) { |
|
|
50
|
|
|
|
|
|
51
|
0
|
|
|
|
|
0
|
$add = [$item->[0], $item->[1]]; |
52
|
764
|
|
|
|
|
2238
|
} elsif (eval{$item->isa("B::DeparseTree::Node")}) { |
53
|
764
|
|
|
|
|
1863
|
$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
|
764
|
100
|
66
|
|
|
2564
|
push @result, $sep if @result && $sep; |
62
|
764
|
|
|
|
|
1356
|
push @result, $add; |
63
|
|
|
|
|
|
|
} |
64
|
593
|
|
|
|
|
2476
|
return @result; |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
sub combine2str($$$) |
68
|
|
|
|
|
|
|
{ |
69
|
4501
|
|
|
4501
|
0
|
7251
|
my ($self, $sep, $items) = @_; |
70
|
4501
|
|
|
|
|
5287
|
my $result = ''; |
71
|
4501
|
|
|
|
|
6950
|
foreach my $item (@$items) { |
72
|
21697
|
100
|
|
|
|
30763
|
$result .= $sep if $result; |
73
|
21697
|
100
|
|
|
|
30303
|
if (ref $item) { |
74
|
9599
|
100
|
66
|
|
|
21206
|
if (ref $item eq 'ARRAY' and scalar(@$item) == 2) { |
|
|
50
|
|
|
|
|
|
75
|
|
|
|
|
|
|
# First item is text and second item is op address. |
76
|
5351
|
|
|
|
|
8417
|
$result .= $self->info2str($item->[0]); |
77
|
4248
|
|
|
|
|
9460
|
} elsif (eval{$item->isa("B::DeparseTree::Node")}) { |
78
|
4248
|
50
|
|
|
|
6163
|
if (exists $item->{fmt}) { |
79
|
4248
|
|
|
|
|
6782
|
$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
|
12098
|
50
|
|
|
|
18283
|
if (index($item, '@B::DeparseTree::Node') > 0) { |
89
|
0
|
|
|
|
|
0
|
Carp::confess("\@B::DeparseTree::Node as an item is probably wrong"); |
90
|
|
|
|
|
|
|
} |
91
|
12098
|
|
|
|
|
16117
|
$result .= $item; |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
} |
94
|
4501
|
|
|
|
|
8105
|
return $result; |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
sub expand_simple_spec($$) |
98
|
|
|
|
|
|
|
{ |
99
|
33139
|
|
|
33139
|
0
|
46227
|
my ($self, $fmt) = @_; |
100
|
33139
|
|
|
|
|
37418
|
my $result = ''; |
101
|
33139
|
|
|
|
|
53467
|
while ((my $k=index($fmt, '%')) >= 0) { |
102
|
26473
|
|
|
|
|
34040
|
$result .= substr($fmt, 0, $k); |
103
|
26473
|
|
|
|
|
30766
|
my $spec = substr($fmt, $k, 2); |
104
|
26473
|
|
|
|
|
33303
|
$fmt = substr($fmt, $k+2); |
105
|
|
|
|
|
|
|
|
106
|
26473
|
50
|
|
|
|
54053
|
if ($spec eq '%%') { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
107
|
0
|
|
|
|
|
0
|
$result .= '%'; |
108
|
|
|
|
|
|
|
} elsif ($spec eq '%+') { |
109
|
2770
|
|
|
|
|
4741
|
$result .= $self->indent_more(); |
110
|
|
|
|
|
|
|
} elsif ($spec eq '%-') { |
111
|
2770
|
|
|
|
|
6068
|
$result .= $self->indent_less(); |
112
|
|
|
|
|
|
|
} elsif ($spec eq '%|') { |
113
|
20933
|
|
|
|
|
30336
|
$result .= $self->indent_value(); |
114
|
|
|
|
|
|
|
} else { |
115
|
0
|
|
|
|
|
0
|
Carp::confess("Unknown spec $spec") |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
} |
118
|
33139
|
100
|
|
|
|
50153
|
$result .= $fmt if $fmt; |
119
|
33139
|
|
|
|
|
55008
|
return $result; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
sub indent_less($$) { |
123
|
2770
|
|
|
2770
|
0
|
4289
|
my ($self, $check_level) = @_; |
124
|
2770
|
50
|
|
|
|
5025
|
$check_level = 0 if !defined $check_level; |
125
|
|
|
|
|
|
|
|
126
|
2770
|
|
|
|
|
4204
|
$self->{level} -= $self->{'indent_size'}; |
127
|
2770
|
|
|
|
|
3437
|
my $level = $self->{level}; |
128
|
2770
|
50
|
|
|
|
4261
|
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
|
2770
|
|
|
|
|
4711
|
return $self->indent_value(); |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
sub indent_more($) { |
137
|
2770
|
|
|
2770
|
0
|
3706
|
my ($self) = @_; |
138
|
2770
|
|
|
|
|
4039
|
$self->{level} += $self->{'indent_size'}; |
139
|
2770
|
|
|
|
|
3903
|
return $self->indent_value(); |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
sub indent_value($) { |
143
|
26473
|
|
|
26473
|
0
|
32552
|
my ($self) = @_; |
144
|
26473
|
|
|
|
|
31251
|
my $level = $self->{level}; |
145
|
26473
|
50
|
|
|
|
37779
|
if ($self->{'use_tabs'}) { |
146
|
0
|
|
|
|
|
0
|
return "\t" x ($level / 8) . " " x ($level % 8); |
147
|
|
|
|
|
|
|
} else { |
148
|
26473
|
|
|
|
|
82772
|
return " " x $level; |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
sub info2str($$) |
153
|
|
|
|
|
|
|
{ |
154
|
111202
|
|
|
111202
|
0
|
139303
|
my ($self, $item) = @_; |
155
|
111202
|
|
|
|
|
125456
|
my $result = ''; |
156
|
111202
|
100
|
|
|
|
155423
|
if (ref $item) { |
157
|
97881
|
50
|
33
|
|
|
170644
|
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
|
97881
|
|
|
|
|
211993
|
} elsif (eval{$item->isa("B::DeparseTree::Node")}) { |
162
|
97881
|
100
|
66
|
|
|
190870
|
if (exists $item->{fmt}) { |
|
|
100
|
|
|
|
|
|
163
|
47881
|
|
|
|
|
71426
|
$result .= $self->template2str($item); |
164
|
47881
|
100
|
|
|
|
85219
|
if ($item->{maybe_parens}) { |
165
|
7256
|
|
|
|
|
9211
|
my $mp = $item->{maybe_parens}; |
166
|
7256
|
100
|
66
|
|
|
20413
|
if ($mp->{force} || $mp->{parens}) { |
167
|
192
|
|
|
|
|
367
|
$result = "($result)"; |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
} elsif (!exists $item->{texts} && exists $item->{text}) { |
171
|
|
|
|
|
|
|
# Is a constant string |
172
|
46094
|
|
|
|
|
65039
|
$result .= $item->{text}; |
173
|
|
|
|
|
|
|
} else { |
174
|
|
|
|
|
|
|
$result = $self->combine2str($item->{sep}, |
175
|
3906
|
|
|
|
|
6972
|
$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
|
13321
|
50
|
|
|
|
23266
|
if (index($item, '@B::DeparseTree::Node') > 0) { |
184
|
0
|
|
|
|
|
0
|
Carp::confess("\@B::DeparseTree::Node as an item is probably wrong"); |
185
|
|
|
|
|
|
|
} |
186
|
13321
|
|
|
|
|
15751
|
$result = $item; |
187
|
|
|
|
|
|
|
} |
188
|
111202
|
|
|
|
|
182272
|
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
|
46
|
|
|
46
|
0
|
133
|
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
|
46
|
|
|
|
|
176
|
my $info = B::DeparseTree::Node->new($op, $self, $texts, undef, |
200
|
|
|
|
|
|
|
$type, $opts); |
201
|
46
|
|
|
|
|
109
|
$info->{sep} = $sep; |
202
|
46
|
|
|
|
|
72
|
my $text = ''; |
203
|
46
|
|
|
|
|
84
|
foreach my $item (@$texts) { |
204
|
181
|
100
|
100
|
|
|
428
|
$text .= $sep if $text and $sep; |
205
|
181
|
50
|
|
|
|
295
|
if(ref($item) eq 'ARRAY'){ |
|
|
50
|
|
|
|
|
|
206
|
0
|
|
|
|
|
0
|
$text .= $item->[0]; |
207
|
181
|
|
|
|
|
862
|
} elsif (eval{$item->isa("B::DeparseTree::Node")}) { |
208
|
0
|
|
|
|
|
0
|
$text .= $item->{text}; |
209
|
|
|
|
|
|
|
} else { |
210
|
181
|
|
|
|
|
344
|
$text .= $item; |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
} |
213
|
|
|
|
|
|
|
|
214
|
46
|
|
|
|
|
125
|
$info->{text} = $text; |
215
|
46
|
100
|
|
|
|
128
|
if ($opts->{maybe_parens}) { |
216
|
4
|
|
|
|
|
6
|
my ($obj, $context, $precedence) = @{$opts->{maybe_parens}}; |
|
4
|
|
|
|
|
9
|
|
217
|
4
|
|
|
|
|
9
|
my $parens = B::DeparseTree::Node::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
|
|
|
14
|
$info->{text} = "($info->{text})" if exists $info->{text} and $parens; |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
46
|
|
|
|
|
188
|
return $info |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
# Create an info structure a template pattern |
231
|
|
|
|
|
|
|
sub info_from_template($$$$$) { |
232
|
12049
|
|
|
12049
|
0
|
25182
|
my ($self, $type, $op, $fmt, $indexes, $args, $opts) = @_; |
233
|
12049
|
100
|
|
|
|
23036
|
$opts = {} unless defined($opts); |
234
|
12049
|
|
|
|
|
19647
|
my @args = @$args; |
235
|
12049
|
|
|
|
|
29643
|
my $info = B::DeparseTree::Node->new($op, $self, $args, undef, $type, $opts); |
236
|
|
|
|
|
|
|
|
237
|
12049
|
100
|
|
|
|
24651
|
$indexes = [0..$#args] unless defined $indexes; |
238
|
12049
|
|
|
|
|
18670
|
$info->{'indexes'} = $indexes; |
239
|
12049
|
|
|
|
|
21667
|
my $text = $self->template_engine($fmt, $indexes, $args); |
240
|
|
|
|
|
|
|
|
241
|
12049
|
|
|
|
|
22952
|
$info->{'fmt'} = $fmt; |
242
|
12049
|
|
|
|
|
18686
|
$info->{'text'} = $self->template_engine($fmt, $indexes, $args); |
243
|
|
|
|
|
|
|
|
244
|
12049
|
100
|
|
|
|
21508
|
if (! defined $op) { |
245
|
2654
|
|
|
|
|
4102
|
$info->{addr} = ++$self->{'last_fake_addr'}; |
246
|
2654
|
|
|
|
|
7566
|
$self->{optree}{$info->{addr}} = $info; |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
12049
|
50
|
|
|
|
18713
|
if ($opts->{'relink_children'}) { |
250
|
|
|
|
|
|
|
# FIXME we should specify which children to relink |
251
|
0
|
|
|
|
|
0
|
for (my $i=0; $i < scalar @$args; $i++) { |
252
|
0
|
0
|
|
|
|
0
|
if ($args[$i]->isa("B::DeparseTree::Node")) { |
253
|
0
|
|
|
|
|
0
|
$args[$i]{parent} = $info->{addr}; |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
} |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
# Link the parent of Deparse::Tree::Nodes to this node. |
259
|
12049
|
50
|
|
|
|
17725
|
if ($opts->{'synthesized_nodes'}) { |
260
|
0
|
|
|
|
|
0
|
foreach my $node (@{$opts->{'synthesized_nodes'}}) { |
|
0
|
|
|
|
|
0
|
|
261
|
0
|
|
|
|
|
0
|
$node->{parent} = $info->{addr}; |
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
# Need to handle maybe_parens since B::DeparseNode couldn't do that |
266
|
|
|
|
|
|
|
# as it was passed a ref ARRAY rather than a string. |
267
|
12049
|
100
|
|
|
|
19307
|
if ($opts->{maybe_parens}) { |
268
|
1581
|
|
|
|
|
2107
|
my ($obj, $context, $precedence) = @{$opts->{maybe_parens}}; |
|
1581
|
|
|
|
|
3420
|
|
269
|
1581
|
|
|
|
|
3721
|
my $parens = B::DeparseTree::Node::parens_test($obj, |
270
|
|
|
|
|
|
|
$context, $precedence); |
271
|
|
|
|
|
|
|
$info->{maybe_parens} = { |
272
|
|
|
|
|
|
|
context => $context, |
273
|
|
|
|
|
|
|
precedence => $precedence, |
274
|
1581
|
100
|
|
|
|
8371
|
force => $obj->{'parens'}, |
275
|
|
|
|
|
|
|
parens => $parens ? 'true' : '' |
276
|
|
|
|
|
|
|
}; |
277
|
1581
|
100
|
66
|
|
|
5598
|
$info->{text} = "($info->{text})" if exists $info->{text} and $parens; |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
|
280
|
12049
|
|
|
|
|
42681
|
return $info; |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
# Create an info structure from a single string |
284
|
|
|
|
|
|
|
sub info_from_string($$$$$) |
285
|
|
|
|
|
|
|
{ |
286
|
11297
|
|
|
11297
|
0
|
21154
|
my ($self, $type, $op, $str, $opts) = @_; |
287
|
11297
|
|
100
|
|
|
30802
|
$opts ||= {}; |
288
|
11297
|
|
|
|
|
26193
|
return B::DeparseTree::Node->new($op, $self, $str, undef, |
289
|
|
|
|
|
|
|
$type, $opts); |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
# OBSOLETE: Create an info structure from a single string |
293
|
|
|
|
|
|
|
# FIXME: remove this |
294
|
|
|
|
|
|
|
sub info_from_text($$$$$) |
295
|
|
|
|
|
|
|
{ |
296
|
21
|
|
|
21
|
0
|
55
|
my ($op, $self, $text, $type, $opts) = @_; |
297
|
|
|
|
|
|
|
# Use this to smoke outt calls |
298
|
|
|
|
|
|
|
# use Enbugger 'trepan'; Enbugger->stop; |
299
|
21
|
|
|
|
|
50
|
return $self->info_from_string($type, $op, $text, $opts) |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
# List of suffix characters that are handled by "expand_simple_spec()". |
303
|
3
|
|
|
3
|
|
20
|
use constant SIMPLE_SPEC => '%+-|'; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
2372
|
|
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
# Extract the string at $args[$index] and if |
306
|
|
|
|
|
|
|
# we are looking for that position include where we are in |
307
|
|
|
|
|
|
|
# that position |
308
|
|
|
|
|
|
|
sub get_info_and_str($$$) |
309
|
|
|
|
|
|
|
{ |
310
|
104836
|
|
|
104836
|
0
|
140707
|
my ($self, $index, $args) = @_; |
311
|
104836
|
|
|
|
|
126353
|
my $info = $args->[$index]; |
312
|
104836
|
|
|
|
|
134120
|
my $str = $self->info2str($info); |
313
|
104836
|
|
|
|
|
195071
|
return ($info, $str); |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
sub template_engine($$$$) |
317
|
|
|
|
|
|
|
{ |
318
|
76227
|
|
|
76227
|
0
|
119337
|
my ($self, $fmt, $indexes, $args, $find_addr) = @_; |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
# use Data::Dumper; |
321
|
|
|
|
|
|
|
# print "-----\n"; |
322
|
|
|
|
|
|
|
# p $args; |
323
|
|
|
|
|
|
|
# print "'======\n"; |
324
|
|
|
|
|
|
|
# print $fmt, "\n" |
325
|
|
|
|
|
|
|
# print $args, "\n"; |
326
|
|
|
|
|
|
|
|
327
|
76227
|
|
|
|
|
82688
|
my $i = 0; |
328
|
76227
|
50
|
|
|
|
104819
|
$find_addr = -2 unless $find_addr; |
329
|
|
|
|
|
|
|
|
330
|
76227
|
|
|
|
|
82976
|
my $start_fmt = $fmt; # used in error messages |
331
|
76227
|
|
|
|
|
103169
|
my @args = @$args; |
332
|
|
|
|
|
|
|
|
333
|
76227
|
|
|
|
|
84472
|
my $result = ''; |
334
|
76227
|
|
|
|
|
80113
|
my $find_pos = undef; |
335
|
76227
|
|
|
|
|
139279
|
while ((my $k=index($fmt, '%')) >= 0) { |
336
|
73449
|
|
|
|
|
108999
|
$result .= substr($fmt, 0, $k); |
337
|
73449
|
|
|
|
|
89623
|
my $spec = substr($fmt, $k, 2); |
338
|
73449
|
|
|
|
|
90923
|
$fmt = substr($fmt, $k+2); |
339
|
|
|
|
|
|
|
|
340
|
73449
|
100
|
|
|
|
157233
|
if (index(SIMPLE_SPEC, substr($spec, 1, 1)) >= 0) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
341
|
8289
|
|
|
|
|
12718
|
$result .= $self->expand_simple_spec($spec); |
342
|
|
|
|
|
|
|
} elsif ($spec eq "%c") { |
343
|
|
|
|
|
|
|
# Insert child entry |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
# FIXME: turn this into a subroutine. |
346
|
39409
|
50
|
|
|
|
43527
|
if ($i >= scalar @{$indexes}) { |
|
39409
|
|
|
|
|
65210
|
|
347
|
0
|
|
|
|
|
0
|
Carp::confess("Need another entry in args_spec for %c in fmt: $start_fmt"); |
348
|
|
|
|
|
|
|
} |
349
|
39409
|
|
|
|
|
57861
|
my $index = $indexes->[$i++]; |
350
|
39409
|
50
|
|
|
|
57759
|
if ($index >= scalar @args) { |
351
|
0
|
|
|
|
|
0
|
Carp::confess("$index in $start_fmt for %c is too large; should be less " . |
352
|
|
|
|
|
|
|
"than scalar(@args)"); |
353
|
|
|
|
|
|
|
} |
354
|
39409
|
|
|
|
|
42008
|
my $str; |
355
|
39409
|
|
|
|
|
59871
|
my ($info, $str) = $self->get_info_and_str($index, $args); |
356
|
39409
|
50
|
66
|
|
|
108894
|
if (ref($info) && $info->{'addr'} == $find_addr) { |
357
|
0
|
|
|
|
|
0
|
my $len = length($result); |
358
|
|
|
|
|
|
|
$len++ if (exists $info->{maybe_parens} |
359
|
0
|
0
|
0
|
|
|
0
|
&& $info->{maybe_parens}{parens}); |
360
|
0
|
|
|
|
|
0
|
$find_pos = [$len, length($str)]; |
361
|
|
|
|
|
|
|
} |
362
|
39409
|
|
|
|
|
89573
|
$result .= $str; |
363
|
|
|
|
|
|
|
} elsif ($spec eq "%C") { |
364
|
|
|
|
|
|
|
# Insert separator between child entry lists |
365
|
6666
|
|
|
|
|
8037
|
my ($low, $high, $sub_spec) = @{$indexes->[$i++]}; |
|
6666
|
|
|
|
|
13040
|
|
366
|
6666
|
|
|
|
|
11313
|
my $sep = $self->expand_simple_spec($sub_spec); |
367
|
6666
|
|
|
|
|
8753
|
my $list = ''; |
368
|
6666
|
|
|
|
|
11109
|
for (my $j=$low; $j<=$high; $j++) { |
369
|
25101
|
100
|
|
|
|
39748
|
$result .= $sep if $j > $low; |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
# FIXME: Remove duplicate code |
372
|
25101
|
|
|
|
|
38379
|
my ($info, $str) = $self->get_info_and_str($j, $args); |
373
|
25101
|
50
|
33
|
|
|
67418
|
if (ref($info) && $info->{'addr'} == $find_addr) { |
374
|
0
|
|
|
|
|
0
|
my $len = length($result); |
375
|
|
|
|
|
|
|
$len++ if (exists $info->{maybe_parens} |
376
|
0
|
0
|
0
|
|
|
0
|
&& $info->{maybe_parens}{parens}); |
377
|
0
|
|
|
|
|
0
|
$find_pos = [$len, length($str)]; |
378
|
|
|
|
|
|
|
} |
379
|
25101
|
|
|
|
|
51831
|
$result .= $str; |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
} elsif ($spec eq "%f") { |
382
|
|
|
|
|
|
|
# Run maybe_parens_func |
383
|
0
|
|
|
|
|
0
|
my $fn_name = shift @args; |
384
|
0
|
|
|
|
|
0
|
my ($cx, $prec) = @{$indexes->[$i++]}; |
|
0
|
|
|
|
|
0
|
|
385
|
0
|
|
|
|
|
0
|
my $params = $self->template_engine("%C", [[0, $#args], ', ']); |
386
|
0
|
|
|
|
|
0
|
$result .= B::Deparse::maybe_parens_func($self, $fn_name, $params, $cx, $prec); |
387
|
|
|
|
|
|
|
} elsif ($spec eq "%F") { |
388
|
|
|
|
|
|
|
# Run a transformation function |
389
|
901
|
50
|
|
|
|
1618
|
if ($i >= scalar@$indexes) { |
390
|
0
|
|
|
|
|
0
|
Carp::confess("Need another entry in args_spec for %%F fmt: $start_fmt"); |
391
|
|
|
|
|
|
|
} |
392
|
901
|
|
|
|
|
1030
|
my ($arg_index, $transform_fn) = @{$indexes->[$i++]}; |
|
901
|
|
|
|
|
1612
|
|
393
|
901
|
50
|
|
|
|
1656
|
if ($arg_index >= scalar @args) { |
394
|
0
|
|
|
|
|
0
|
Carp::confess("argument index $arg_index in $start_fmt for %%F is too large; should be less than @$args"); |
395
|
|
|
|
|
|
|
} |
396
|
901
|
50
|
|
|
|
1915
|
if (ref($transform_fn ne 'CODE')) { |
397
|
0
|
|
|
|
|
0
|
Carp::confess("transformation function $transform_fn is not CODE"); |
398
|
|
|
|
|
|
|
} |
399
|
901
|
|
|
|
|
1378
|
my ($arg) = $args[$arg_index]; |
400
|
901
|
|
|
|
|
1786
|
$result .= $transform_fn->($arg); |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
} elsif ($spec eq "%;") { |
403
|
|
|
|
|
|
|
# Insert semicolons and indented newlines between statements. |
404
|
|
|
|
|
|
|
# Don't insert them around empty strings - some OPs |
405
|
|
|
|
|
|
|
# don't have an text associated with them. |
406
|
|
|
|
|
|
|
# Finally, replace semicolon a the end of statement that |
407
|
|
|
|
|
|
|
# end in "}" with a \n and proper indent. |
408
|
18184
|
|
|
|
|
28981
|
my $sep = $self->expand_simple_spec(";\n%|"); |
409
|
18184
|
|
|
|
|
22709
|
my $start_size = length($result); |
410
|
18184
|
|
|
|
|
35779
|
for (my $j=0; $j< @args; $j++) { |
411
|
40326
|
|
|
|
|
50810
|
my $old_result = $result; |
412
|
40326
|
100
|
100
|
|
|
89762
|
if ($j > 0 && length($result) > $start_size) { |
413
|
|
|
|
|
|
|
# Remove any prior ;\n |
414
|
26788
|
100
|
|
|
|
47006
|
$result = substr($result, 0, -1) if substr($result, -1) eq "\n"; |
415
|
26788
|
100
|
|
|
|
41803
|
$result = substr($result, 0, -1) if substr($result, -1) eq ";"; |
416
|
|
|
|
|
|
|
## The below needs to be done based on whether the previous construct is a compound statement or not. |
417
|
|
|
|
|
|
|
## That could be added in a trailing format specifier for it. |
418
|
|
|
|
|
|
|
## "sub {...}" and "$h = {..}" need a semicolon while "if () {...}" doesn't. |
419
|
|
|
|
|
|
|
# if (substr($result, -1) eq "}" & $j < $#args) { |
420
|
|
|
|
|
|
|
# # Omit ; from sep. FIXME: do this based on an option? |
421
|
|
|
|
|
|
|
# $result .= substr($sep, 1); |
422
|
|
|
|
|
|
|
# } else { |
423
|
|
|
|
|
|
|
# $result .= $sep; |
424
|
|
|
|
|
|
|
# } |
425
|
26788
|
|
|
|
|
35538
|
$result .= $sep; |
426
|
|
|
|
|
|
|
} |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
# FIXME: Remove duplicate code |
429
|
40326
|
|
|
|
|
62108
|
my ($info, $str) = $self->get_info_and_str($j, $args); |
430
|
40326
|
50
|
66
|
|
|
103385
|
if (ref($info) && $info->{'addr'} == $find_addr) { |
431
|
0
|
|
|
|
|
0
|
my $len = length($result); |
432
|
0
|
0
|
0
|
|
|
0
|
$len++ if exists $info->{maybe_parens} and $info->{maybe_parens}{parens}; |
433
|
0
|
|
|
|
|
0
|
$find_pos = [length($result), length($str)]; |
434
|
|
|
|
|
|
|
} |
435
|
40326
|
100
|
|
|
|
55372
|
if (!$str) { |
436
|
3352
|
|
|
|
|
7844
|
$result = $old_result; |
437
|
|
|
|
|
|
|
} else { |
438
|
36974
|
|
|
|
|
92281
|
$result .= $str |
439
|
|
|
|
|
|
|
} |
440
|
|
|
|
|
|
|
} |
441
|
|
|
|
|
|
|
# # FIXME: Add the final ';' based on an option? |
442
|
|
|
|
|
|
|
# if ($result and not |
443
|
|
|
|
|
|
|
# (substr($result, -1) eq ';' or |
444
|
|
|
|
|
|
|
# (substr($result, -1) eq ';\n'))) { |
445
|
|
|
|
|
|
|
# $result .= ';' if $result and substr($result, -1) ne ';'; |
446
|
|
|
|
|
|
|
# } |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
} elsif ($spec eq "\cS") { |
449
|
|
|
|
|
|
|
# FIXME: not handled yet |
450
|
|
|
|
|
|
|
; |
451
|
|
|
|
|
|
|
} else { |
452
|
|
|
|
|
|
|
# We have % with a non-special symbol. Just preserve those. |
453
|
0
|
|
|
|
|
0
|
$result .= $spec; |
454
|
|
|
|
|
|
|
} |
455
|
|
|
|
|
|
|
} |
456
|
76227
|
100
|
|
|
|
117249
|
$result .= $fmt if $fmt; |
457
|
76227
|
50
|
|
|
|
107840
|
if ($find_addr != -2) { |
458
|
|
|
|
|
|
|
# want result and position |
459
|
0
|
|
|
|
|
0
|
return $result, $find_pos; |
460
|
|
|
|
|
|
|
} |
461
|
|
|
|
|
|
|
# want just result |
462
|
76227
|
|
|
|
|
161801
|
return $result; |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
} |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
sub template2str($$) { |
467
|
52129
|
|
|
52129
|
0
|
67868
|
my ($self, $info) = @_; |
468
|
|
|
|
|
|
|
return $self->template_engine($info->{fmt}, |
469
|
|
|
|
|
|
|
$info->{indexes}, |
470
|
52129
|
|
|
|
|
87457
|
$info->{texts}); |
471
|
|
|
|
|
|
|
} |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
1; |