line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Templ::Parser; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
4
|
use strict; |
|
1
|
|
|
|
|
11
|
|
|
1
|
|
|
|
|
25
|
|
4
|
1
|
|
|
1
|
|
3
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
19
|
|
5
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
3
|
use Carp qw(cluck croak); |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
37
|
|
7
|
1
|
|
|
1
|
|
3
|
use Data::Dumper; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
956
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
my $PKG = __PACKAGE__; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
eval { require Perl::Tidy; require File::Temp }; |
12
|
|
|
|
|
|
|
my $can_tidy = $@ ? 0 : 1; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
eval { require v5.10; }; |
15
|
|
|
|
|
|
|
my $can_say = $@ ? 0 : 1; |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
sub new { |
18
|
0
|
|
|
0
|
0
|
|
my $class = shift; |
19
|
0
|
0
|
0
|
|
|
|
if ( not defined $class || ref $class || $class !~ m/^(\w+\:\:)*\w+$/ ) { |
|
|
|
0
|
|
|
|
|
20
|
0
|
|
|
|
|
|
croak "Can only be called as Templ::Parser::...->new"; |
21
|
|
|
|
|
|
|
} |
22
|
0
|
0
|
|
|
|
|
if ($class eq $PKG) { |
23
|
0
|
|
|
|
|
|
croak "$PKG cannot be instantiated directly, use a subclass"; |
24
|
|
|
|
|
|
|
} |
25
|
0
|
|
|
|
|
|
my $self = bless {@_}, $class; |
26
|
0
|
|
|
|
|
|
return $self; |
27
|
|
|
|
|
|
|
} |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
sub parse { |
30
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
31
|
0
|
|
|
|
|
|
my $templ = shift; |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# The template is assumed to be starting as printing output, so |
34
|
|
|
|
|
|
|
# wrap the whole template in a header/footer, escaping the contents |
35
|
0
|
|
|
|
|
|
my $perl = ''; |
36
|
0
|
0
|
|
|
|
|
if ($self->prettify) { $perl .= $self->pretty_header; } |
|
0
|
|
|
|
|
|
|
37
|
0
|
|
|
|
|
|
my $quoted = $templ->templ_code; |
38
|
0
|
|
|
|
|
|
$quoted =~ s|\\|\\\\|gs; |
39
|
0
|
|
|
|
|
|
$quoted =~ s|'|\\'|gs; |
40
|
0
|
|
|
|
|
|
$perl .= $templ->header; |
41
|
0
|
|
|
|
|
|
$perl .= $self->header; |
42
|
0
|
|
|
|
|
|
$perl .= "'$quoted'"; |
43
|
0
|
|
|
|
|
|
$perl .= $self->footer; |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
# Loop over all of the remaining <* ... *> tag types, performing the |
46
|
|
|
|
|
|
|
# prescribed replacements... run in reverse to process latest / deepest |
47
|
|
|
|
|
|
|
# subclass tags first |
48
|
|
|
|
|
|
|
# |
49
|
|
|
|
|
|
|
# $self->debug && print Data::Dumper->Dump([[$templ->tags]],['tags']); |
50
|
|
|
|
|
|
|
# $self->debug && print Data::Dumper->Dump([$templ],['templ']); |
51
|
0
|
|
|
|
|
|
foreach my $tag ( $templ->tags ) { |
52
|
0
|
|
|
|
|
|
$perl = $tag->process( $perl, $self ); |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
|
55
|
0
|
0
|
|
|
|
|
if ( $self->prettify ) { |
56
|
|
|
|
|
|
|
# Change any standalone single-quote print statements with |
57
|
|
|
|
|
|
|
# literal newlines in them to a series of individual print |
58
|
|
|
|
|
|
|
# or say statements for readability |
59
|
0
|
|
|
|
|
|
$perl =~ s{ |
60
|
|
|
|
|
|
|
(?: |
61
|
|
|
|
|
|
|
# $1 = Previous statement separator |
62
|
|
|
|
|
|
|
( (?: ^ | \; | \{ | \} ) [ \t]*? (?:\r?\n)? ) |
63
|
|
|
|
|
|
|
# $2 = Print statement indentation |
64
|
|
|
|
|
|
|
( \s*? ) |
65
|
|
|
|
|
|
|
# $3 = Double quote contents |
66
|
|
|
|
|
|
|
print \s* '(.*?)(?
|
67
|
|
|
|
|
|
|
# $4 = Closing brace or semicolon |
68
|
|
|
|
|
|
|
( \s* (?: (?:\;|\}) (?:\r?\n)? | $ ) ) |
69
|
|
|
|
|
|
|
) |
70
|
|
|
|
|
|
|
} |
71
|
0
|
|
|
|
|
|
{ $self->prettify_lines($1, $2, $3, $4) }egsx; |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
|
74
|
0
|
0
|
|
|
|
|
if ( $self->tidy ) { |
75
|
0
|
0
|
|
|
|
|
if ($can_tidy) { |
76
|
0
|
|
|
|
|
|
require File::Temp; |
77
|
0
|
|
|
|
|
|
require Perl::Tidy; |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
# Using a temp file because the output is weird when we don't |
80
|
0
|
|
|
|
|
|
( undef, my $tmp ) = File::Temp::tempfile(); |
81
|
0
|
|
|
|
|
|
Perl::Tidy::perltidy( |
82
|
|
|
|
|
|
|
'source' => \$perl, |
83
|
|
|
|
|
|
|
'destination' => $tmp, |
84
|
|
|
|
|
|
|
'argv' => [ split /\s+/, $self->tidy_options ], |
85
|
|
|
|
|
|
|
); |
86
|
0
|
|
0
|
|
|
|
open my $FH, '<', $tmp |
87
|
|
|
|
|
|
|
|| die "Unable to open file for reading $tmp: $!"; |
88
|
0
|
|
|
|
|
|
local $/ = undef; |
89
|
0
|
|
|
|
|
|
$perl = <$FH>; |
90
|
0
|
|
|
|
|
|
close $FH; |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
else { |
93
|
0
|
|
|
|
|
|
warn "Unable to load Perl::Tidy and/or File::Temp\n"; |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
|
97
|
0
|
0
|
0
|
|
|
|
if ( $self->tidy || $self->prettify ) { |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
# Remove blank append statements |
100
|
0
|
|
|
|
|
|
my $append = $self->append; |
101
|
0
|
|
|
|
|
|
$perl =~ s/(?:^|(?<=\n)[ \t]*)\Q$append\E'';[ \t]*(?:\r?\n|$)//; |
102
|
|
|
|
|
|
|
} |
103
|
|
|
|
|
|
|
|
104
|
0
|
0
|
|
|
|
|
if ( $self->debug ) { |
105
|
0
|
|
|
|
|
|
my @lines = split /\n/, $perl; |
106
|
0
|
|
|
|
|
|
my $format = '%' . length( scalar(@lines) . '' ) . "s: %s\n"; |
107
|
0
|
|
|
|
|
|
print STDERR sprintf( $format, ( $_ + 1 ), $lines[$_] ) foreach (0 .. $#lines); |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
|
110
|
0
|
|
|
|
|
|
return $perl; |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
# Breaks a print statement with newlines in it into multiple statements |
114
|
|
|
|
|
|
|
# Helps with formatting code to preserve indentation (used when prettify is |
115
|
|
|
|
|
|
|
# enabled) |
116
|
|
|
|
|
|
|
sub prettify_lines { |
117
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
118
|
0
|
|
|
|
|
|
my $pre = shift; # Previous opening brace or semicolon |
119
|
0
|
|
|
|
|
|
my $indent = shift; # Indentation spacing of the print statement |
120
|
0
|
|
|
|
|
|
my $contents = shift; # Contents of the single quotes of the print |
121
|
0
|
|
|
|
|
|
my $post = shift; # Closing brace or semicolon |
122
|
|
|
|
|
|
|
|
123
|
0
|
|
|
|
|
|
my $out = $pre; |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
# Create a list of lines (and the ending partial line) in the print |
126
|
0
|
|
|
|
|
|
my @chunks = split /(.*?\n)/, $contents; |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
# print "CHUNK <<$_>>\n" foreach @chunks; |
129
|
0
|
|
|
|
|
|
foreach ( 0 .. $#chunks ) { |
130
|
0
|
|
|
|
|
|
my $is_last_chunk = ( $_ == $#chunks ); |
131
|
0
|
|
|
|
|
|
my $chunk = $chunks[$_]; |
132
|
0
|
0
|
0
|
|
|
|
next if ( ( $chunk eq '' ) && ( not $is_last_chunk ) ); |
133
|
|
|
|
|
|
|
|
134
|
0
|
|
|
|
|
|
my $nl = ''; |
135
|
0
|
0
|
|
|
|
|
if ( $chunk =~ s/\r\n$// ) { $nl = '\r\n'; } |
|
0
|
0
|
|
|
|
|
|
136
|
0
|
|
|
|
|
|
elsif ( $chunk =~ s/\n$// ) { $nl = '\n'; } |
137
|
|
|
|
|
|
|
|
138
|
0
|
|
|
|
|
|
my $statement; |
139
|
0
|
0
|
|
|
|
|
if ($nl) { |
140
|
0
|
0
|
|
|
|
|
if ($self->append_pretty) { |
141
|
0
|
|
|
|
|
|
$statement = $self->append_pretty . "'$chunk'"; |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
else { |
144
|
0
|
|
|
|
|
|
$statement = $self->append . "'$chunk'" . '."$nl"'; |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
else { |
148
|
0
|
|
|
|
|
|
$statement = $self->append . "'$chunk'"; |
149
|
|
|
|
|
|
|
} |
150
|
0
|
0
|
|
|
|
|
$statement .= $is_last_chunk ? $post : ";\n"; |
151
|
|
|
|
|
|
|
|
152
|
0
|
|
|
|
|
|
$out .= $statement; |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
|
155
|
0
|
|
|
|
|
|
return $out; |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
############################################################################## |
159
|
|
|
|
|
|
|
# Some override-able functions for subclasses |
160
|
|
|
|
|
|
|
|
161
|
0
|
|
|
0
|
0
|
|
sub pretty_header { return ''; } |
162
|
0
|
|
|
0
|
0
|
|
sub header { die "Subclass must override Templ::Parser->header"; } |
163
|
0
|
|
|
0
|
0
|
|
sub append { die "Subclass must override Templ::Parser->append"; } |
164
|
0
|
|
|
0
|
0
|
|
sub append_pretty { return ''; } |
165
|
0
|
|
|
0
|
0
|
|
sub footer { die "Subclass must override Templ::Parser->footer"; } |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
############################################################################## |
168
|
|
|
|
|
|
|
# Utility Functions |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
# Returns the first defined value in a list, or a blank string if there are |
171
|
|
|
|
|
|
|
# # no defined values |
172
|
|
|
|
|
|
|
sub _default (@) { |
173
|
0
|
0
|
|
0
|
|
|
foreach (@_) { defined($_) && return $_; } |
|
0
|
|
|
|
|
|
|
174
|
0
|
|
|
|
|
|
return ''; |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
############################################################################## |
178
|
|
|
|
|
|
|
# Accessors... |
179
|
|
|
|
|
|
|
sub debug { |
180
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
181
|
0
|
0
|
|
|
|
|
if ( defined $_[0] ) { $self->{'debug'} = shift; } |
|
0
|
|
|
|
|
|
|
182
|
0
|
|
|
|
|
|
return _default $self->{'debug'}, $Templ::Parser::debug, 0; |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
sub tidy { |
186
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
187
|
0
|
0
|
|
|
|
|
if ( defined $_[0] ) { $self->{'tidy'} = shift; } |
|
0
|
|
|
|
|
|
|
188
|
0
|
|
|
|
|
|
return _default $self->{'tidy'}, $Templ::Parser::tidy, 0; |
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
sub tidy_options { |
192
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
193
|
0
|
0
|
|
|
|
|
if ( defined $_[0] ) { $self->{'tidy_options'} = shift; } |
|
0
|
|
|
|
|
|
|
194
|
0
|
|
|
|
|
|
return _default $self->{'tidy_options'}, $Templ::Parser::tidy_options, |
195
|
|
|
|
|
|
|
'-pbp -nst -b -aws -dws -dsm -nbbc -kbl=0 -asc -npro -sbl'; |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
sub prettify { |
199
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
200
|
0
|
0
|
|
|
|
|
if ( defined $_[0] ) { $self->{'prettify'} = shift; } |
|
0
|
|
|
|
|
|
|
201
|
0
|
0
|
|
|
|
|
return _default $self->{'prettify'}, $Templ::Parser::prettify, |
202
|
|
|
|
|
|
|
( $self->tidy ? 1 : 0 ); |
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
1; |