| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Text::MustacheTemplate::Parser; |
|
2
|
14
|
|
|
14
|
|
667
|
use 5.022000; |
|
|
14
|
|
|
|
|
61
|
|
|
3
|
14
|
|
|
14
|
|
84
|
use strict; |
|
|
14
|
|
|
|
|
42
|
|
|
|
14
|
|
|
|
|
524
|
|
|
4
|
14
|
|
|
14
|
|
64
|
use warnings; |
|
|
14
|
|
|
|
|
19
|
|
|
|
14
|
|
|
|
|
736
|
|
|
5
|
|
|
|
|
|
|
|
|
6
|
14
|
|
|
14
|
|
115
|
use Carp qw/croak/; |
|
|
14
|
|
|
|
|
116
|
|
|
|
14
|
|
|
|
|
1061
|
|
|
7
|
14
|
|
|
14
|
|
85
|
use Exporter 5.57 'import'; |
|
|
14
|
|
|
|
|
244
|
|
|
|
14
|
|
|
|
|
600
|
|
|
8
|
|
|
|
|
|
|
|
|
9
|
14
|
|
|
14
|
|
69
|
use Text::MustacheTemplate::Lexer qw/:types/; |
|
|
14
|
|
|
|
|
30
|
|
|
|
14
|
|
|
|
|
2265
|
|
|
10
|
14
|
|
|
14
|
|
6372
|
use Text::MustacheTemplate::Generator; |
|
|
14
|
|
|
|
|
49
|
|
|
|
14
|
|
|
|
|
1588
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( |
|
13
|
|
|
|
|
|
|
syntaxes => [qw/SYNTAX_RAW_TEXT SYNTAX_VARIABLE SYNTAX_BOX SYNTAX_COMMENT SYNTAX_PARTIAL SYNTAX_DELIMITER/], |
|
14
|
|
|
|
|
|
|
variables => [qw/VARIABLE_HTML_ESCAPE VARIABLE_RAW/], |
|
15
|
|
|
|
|
|
|
boxes => [qw/BOX_SECTION BOX_INVERTED_SECTION BOX_BLOCK BOX_PARENT/], |
|
16
|
|
|
|
|
|
|
references => [qw/REFERENCE_STATIC REFERENCE_DYNAMIC/], |
|
17
|
|
|
|
|
|
|
); |
|
18
|
|
|
|
|
|
|
our @EXPORT_OK = map @$_, values %EXPORT_TAGS; |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
use constant { |
|
21
|
|
|
|
|
|
|
# enum |
|
22
|
14
|
|
|
|
|
1884
|
SYNTAX_RAW_TEXT => 0, |
|
23
|
|
|
|
|
|
|
SYNTAX_VARIABLE => 1, |
|
24
|
|
|
|
|
|
|
SYNTAX_BOX => 2, |
|
25
|
|
|
|
|
|
|
SYNTAX_COMMENT => 3, |
|
26
|
|
|
|
|
|
|
SYNTAX_PARTIAL => 4, |
|
27
|
|
|
|
|
|
|
SYNTAX_DELIMITER => 5, |
|
28
|
14
|
|
|
14
|
|
136
|
}; |
|
|
14
|
|
|
|
|
24
|
|
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
use constant { |
|
31
|
|
|
|
|
|
|
# enum |
|
32
|
14
|
|
|
|
|
1068
|
VARIABLE_HTML_ESCAPE => 0, |
|
33
|
|
|
|
|
|
|
VARIABLE_RAW => 1, |
|
34
|
14
|
|
|
14
|
|
83
|
}; |
|
|
14
|
|
|
|
|
23
|
|
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
use constant { |
|
37
|
|
|
|
|
|
|
# enum |
|
38
|
14
|
|
|
|
|
1141
|
BOX_SECTION => 0, |
|
39
|
|
|
|
|
|
|
BOX_INVERTED_SECTION => 1, |
|
40
|
|
|
|
|
|
|
BOX_BLOCK => 2, |
|
41
|
|
|
|
|
|
|
BOX_PARENT => 3, |
|
42
|
14
|
|
|
14
|
|
111
|
}; |
|
|
14
|
|
|
|
|
69
|
|
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
use constant { |
|
45
|
|
|
|
|
|
|
# enum |
|
46
|
14
|
|
|
|
|
29346
|
REFERENCE_STATIC => 0, |
|
47
|
|
|
|
|
|
|
REFERENCE_DYNAMIC => 1, |
|
48
|
14
|
|
|
14
|
|
80
|
}; |
|
|
14
|
|
|
|
|
26
|
|
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
my %TRIM_INNER_WHITESPACE_TYPES = map { $_ => 1 } (qw({ & ^ $ < >), '#', '/'); # set |
|
51
|
|
|
|
|
|
|
my %TRIM_AROUND_WHITESPACE_TYPES = map { $_ => 1 } (qw(! ^ $ < >), '#', '/'); |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
our $SOURCE; # optional for error reporting and optimized lambda support |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
sub parse { |
|
56
|
546
|
|
|
546
|
1
|
255615
|
my $class = shift; |
|
57
|
546
|
|
|
|
|
1457
|
my $ast = _parse(@_); |
|
58
|
536
|
|
|
|
|
1629
|
return $ast; |
|
59
|
|
|
|
|
|
|
} |
|
60
|
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
sub _parse { |
|
62
|
546
|
|
|
546
|
|
1492
|
my @tokens = @_; |
|
63
|
|
|
|
|
|
|
|
|
64
|
546
|
|
|
|
|
1423
|
my $last_delimiter_token; |
|
65
|
|
|
|
|
|
|
my @root; |
|
66
|
546
|
|
|
|
|
0
|
my @stack; |
|
67
|
546
|
|
|
|
|
1042
|
my $ast = \@root; |
|
68
|
546
|
|
|
|
|
2037
|
for my $i (0..$#tokens) { |
|
69
|
3156
|
|
|
|
|
5531
|
my $token = $tokens[$i]; |
|
70
|
|
|
|
|
|
|
|
|
71
|
3156
|
|
|
|
|
5940
|
my ($type, $pos) = @$token; |
|
72
|
3156
|
100
|
|
|
|
7873
|
if ($type == TOKEN_RAW_TEXT) { # uncoverable branch false count:4 |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
73
|
1226
|
|
|
|
|
2591
|
my (undef, undef, $text) = @$token; |
|
74
|
1226
|
100
|
|
|
|
2730
|
if ($i != 1) { # optimized $i >= 2: comes delimiter token when $i == 0 always, so the first raw text token should be $i == 1. |
|
75
|
850
|
|
|
|
|
1394
|
my $prev = $i-1; # $prev >= 1 |
|
76
|
850
|
100
|
|
|
|
2008
|
if (_needs_trim_around_whitespaces($tokens[$prev])) { |
|
77
|
621
|
|
100
|
|
|
2133
|
while ($prev != 1 && _needs_trim_around_whitespaces($tokens[$prev-1])) { |
|
78
|
54
|
|
|
|
|
131
|
$prev--; |
|
79
|
|
|
|
|
|
|
} |
|
80
|
621
|
100
|
|
|
|
1522
|
my $before_prev_token = $prev != 1 ? $tokens[$prev-1] : undef; |
|
81
|
621
|
|
100
|
|
|
4191
|
my $is_empty_before_prev = !defined $before_prev_token || $before_prev_token->[0] == TOKEN_PADDING || ( |
|
82
|
|
|
|
|
|
|
$before_prev_token->[0] == TOKEN_RAW_TEXT && $before_prev_token->[2] =~ /[\r\n]\z/mano |
|
83
|
|
|
|
|
|
|
); |
|
84
|
621
|
100
|
|
|
|
1490
|
if ($is_empty_before_prev) { |
|
85
|
270
|
|
|
|
|
1096
|
$text =~ s/\A(\r\n|[\r\n])//mano; |
|
86
|
|
|
|
|
|
|
} |
|
87
|
|
|
|
|
|
|
} |
|
88
|
|
|
|
|
|
|
} |
|
89
|
1226
|
100
|
|
|
|
5028
|
push @$ast => [SYNTAX_RAW_TEXT, $text] if $text; |
|
90
|
|
|
|
|
|
|
} elsif ($type == TOKEN_PADDING) { |
|
91
|
91
|
|
|
|
|
226
|
my (undef, undef, $padding) = @$token; |
|
92
|
91
|
|
|
|
|
189
|
my $needs_padding = 1; |
|
93
|
91
|
50
|
|
|
|
291
|
if ($i == $#tokens) { # uncoverable branch true |
|
94
|
0
|
|
|
|
|
0
|
_error($token, 'Syntax Error: Padding token should not be last'); # uncoverable statement |
|
95
|
|
|
|
|
|
|
} |
|
96
|
|
|
|
|
|
|
|
|
97
|
91
|
|
|
|
|
215
|
my $next = $i+1; |
|
98
|
91
|
100
|
|
|
|
268
|
if (_needs_trim_around_whitespaces($tokens[$next])) { |
|
99
|
73
|
|
100
|
|
|
356
|
while ($next != $#tokens && _needs_trim_around_whitespaces($tokens[$next+1])) { |
|
100
|
1
|
|
|
|
|
4
|
$next++; |
|
101
|
|
|
|
|
|
|
} |
|
102
|
73
|
100
|
|
|
|
242
|
my $after_next_token = $next != $#tokens ? $tokens[$next+1] : undef; |
|
103
|
73
|
|
100
|
|
|
515
|
my $is_empty_after_next = !defined $after_next_token || ( |
|
104
|
|
|
|
|
|
|
$after_next_token->[0] == TOKEN_RAW_TEXT && $after_next_token->[2] =~ /\A[\r\n]/mano |
|
105
|
|
|
|
|
|
|
); |
|
106
|
73
|
|
|
|
|
218
|
$needs_padding = !$is_empty_after_next; |
|
107
|
|
|
|
|
|
|
} |
|
108
|
91
|
100
|
|
|
|
292
|
if ($needs_padding) { |
|
109
|
36
|
|
|
|
|
136
|
push @$ast => [SYNTAX_RAW_TEXT, $padding]; |
|
110
|
|
|
|
|
|
|
} |
|
111
|
|
|
|
|
|
|
} elsif ($type == TOKEN_TAG) { |
|
112
|
1256
|
100
|
|
|
|
2960
|
if (@$token == 3) { # uncoverable branch false count:2 |
|
|
|
50
|
|
|
|
|
|
|
113
|
325
|
|
|
|
|
710
|
my (undef, undef, $tag_body) = @$token; |
|
114
|
325
|
100
|
|
|
|
1030
|
_error($token, 'Syntax Error: Must not contain newlines') if $tag_body =~ /[\r\n]/mo; |
|
115
|
323
|
|
|
|
|
820
|
$tag_body =~ s/^\s+//ano; |
|
116
|
323
|
|
|
|
|
749
|
$tag_body =~ s/\s+$//ano; |
|
117
|
323
|
|
|
|
|
1141
|
push @$ast => [SYNTAX_VARIABLE, VARIABLE_HTML_ESCAPE, $tag_body]; |
|
118
|
|
|
|
|
|
|
} elsif (@$token == 4) { |
|
119
|
931
|
|
|
|
|
2103
|
my (undef, undef, $tag_type, $tag_body) = @$token; |
|
120
|
931
|
100
|
|
|
|
2557
|
if ($TRIM_INNER_WHITESPACE_TYPES{$tag_type}) { |
|
121
|
897
|
100
|
|
|
|
2414
|
_error($token, 'Syntax Error: Must not contain newlines') if $tag_body =~ /[\r\n]/mo; |
|
122
|
895
|
|
|
|
|
2058
|
$tag_body =~ s/^\s+//ano; |
|
123
|
895
|
|
|
|
|
1933
|
$tag_body =~ s/\s+$//ano; |
|
124
|
|
|
|
|
|
|
} |
|
125
|
|
|
|
|
|
|
|
|
126
|
929
|
100
|
100
|
|
|
5142
|
if ($tag_type eq '{' || $tag_type eq '&') { # uncoverable branch false count:8 |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
127
|
65
|
|
|
|
|
212
|
push @$ast => [SYNTAX_VARIABLE, VARIABLE_RAW, $tag_body]; |
|
128
|
|
|
|
|
|
|
} elsif ($tag_type eq '!') { |
|
129
|
34
|
|
|
|
|
281
|
push @$ast => [SYNTAX_COMMENT, $tag_body]; |
|
130
|
|
|
|
|
|
|
} elsif ($tag_type eq '#') { |
|
131
|
154
|
|
|
|
|
633
|
push @stack => [$i, $tag_body, [SYNTAX_BOX, BOX_SECTION, $tag_body], $ast]; |
|
132
|
154
|
|
|
|
|
437
|
$ast = []; |
|
133
|
|
|
|
|
|
|
} elsif ($tag_type eq '^') { |
|
134
|
64
|
|
|
|
|
253
|
push @stack => [$i, $tag_body, [SYNTAX_BOX, BOX_INVERTED_SECTION, $tag_body], $ast]; |
|
135
|
64
|
|
|
|
|
257
|
$ast = []; |
|
136
|
|
|
|
|
|
|
} elsif ($tag_type eq '$') { |
|
137
|
105
|
|
|
|
|
306
|
push @stack => [$i, $tag_body, [SYNTAX_BOX, BOX_BLOCK, $tag_body], $ast]; |
|
138
|
105
|
|
|
|
|
191
|
$ast = []; |
|
139
|
|
|
|
|
|
|
} elsif ($tag_type eq '<') { |
|
140
|
53
|
|
|
|
|
118
|
my $name = $tag_body; |
|
141
|
53
|
|
|
|
|
68
|
my $syntax = do { |
|
142
|
53
|
|
|
|
|
106
|
my $is_dynamic = substr($name,0,1) eq '*'; |
|
143
|
53
|
100
|
|
|
|
125
|
if ($is_dynamic) { |
|
144
|
3
|
|
|
|
|
11
|
$name = substr $name, 1; |
|
145
|
3
|
|
|
|
|
13
|
[SYNTAX_BOX, BOX_PARENT, REFERENCE_DYNAMIC, $name]; |
|
146
|
|
|
|
|
|
|
} else { |
|
147
|
50
|
|
|
|
|
135
|
[SYNTAX_BOX, BOX_PARENT, REFERENCE_STATIC, $name]; |
|
148
|
|
|
|
|
|
|
} |
|
149
|
|
|
|
|
|
|
}; |
|
150
|
53
|
|
|
|
|
143
|
push @stack => [$i, $tag_body, $syntax, $ast]; |
|
151
|
53
|
|
|
|
|
118
|
$ast = []; |
|
152
|
|
|
|
|
|
|
} elsif ($tag_type eq '/') { |
|
153
|
372
|
100
|
|
|
|
835
|
_error($token, 'Syntax Error: Unbalanced Section') unless @stack; |
|
154
|
371
|
|
|
|
|
641
|
my $item = pop @stack; |
|
155
|
371
|
|
|
|
|
884
|
my ($open_idx, $open_tag_body, $syntax, $parent) = @$item; |
|
156
|
371
|
|
|
|
|
4907
|
s/\s*//go for ($open_tag_body, $tag_body); |
|
157
|
371
|
100
|
|
|
|
1044
|
if ($open_tag_body ne $tag_body) { |
|
158
|
2
|
|
|
|
|
31
|
_error($token, "Syntax Error: Unbalanced Section: open=$open_tag_body close=$tag_body"); |
|
159
|
|
|
|
|
|
|
} |
|
160
|
369
|
100
|
|
|
|
895
|
if ($syntax->[1] == BOX_SECTION) { |
|
161
|
|
|
|
|
|
|
# keep inner template to support lambda |
|
162
|
147
|
100
|
|
|
|
720
|
my $inner_template = defined $SOURCE |
|
163
|
|
|
|
|
|
|
? substr $SOURCE, $tokens[$open_idx+1][1], $pos-$tokens[$open_idx+1][1] |
|
164
|
|
|
|
|
|
|
: Text::MustacheTemplate::Generator->generate_from_tokens($last_delimiter_token, @tokens[$open_idx+1..$i-1]); |
|
165
|
147
|
|
|
|
|
447
|
push @$syntax => $inner_template; |
|
166
|
|
|
|
|
|
|
} |
|
167
|
369
|
|
|
|
|
793
|
push @$syntax => $ast; |
|
168
|
369
|
|
|
|
|
540
|
$ast = $parent; |
|
169
|
369
|
|
|
|
|
1205
|
push @$ast => $syntax; |
|
170
|
|
|
|
|
|
|
} elsif ($tag_type eq '>') { |
|
171
|
82
|
|
|
|
|
183
|
my $name = $tag_body; |
|
172
|
82
|
|
|
|
|
960
|
$name =~ s/\s*//go; |
|
173
|
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
# comes delimiter token when $i == 0 always, so the first tag token should be $i == 1. |
|
175
|
82
|
|
|
|
|
177
|
my $padding; |
|
176
|
82
|
100
|
|
|
|
252
|
if ($i != 1) { |
|
177
|
75
|
|
|
|
|
191
|
my $prev = $tokens[$i-1]; |
|
178
|
75
|
100
|
|
|
|
281
|
if ($prev->[0] == TOKEN_PADDING) { |
|
179
|
14
|
|
|
|
|
34
|
$padding = $prev->[2] ; |
|
180
|
14
|
|
|
|
|
69
|
push @$ast => [SYNTAX_RAW_TEXT, $padding]; |
|
181
|
|
|
|
|
|
|
} |
|
182
|
|
|
|
|
|
|
} |
|
183
|
|
|
|
|
|
|
|
|
184
|
82
|
|
|
|
|
250
|
my $is_dynamic = substr($name,0,1) eq '*'; |
|
185
|
82
|
100
|
|
|
|
218
|
if ($is_dynamic) { |
|
186
|
48
|
|
|
|
|
167
|
$name = substr $name, 1; |
|
187
|
48
|
|
|
|
|
217
|
push @$ast => [SYNTAX_PARTIAL, REFERENCE_DYNAMIC, $name, $padding]; |
|
188
|
|
|
|
|
|
|
} else { |
|
189
|
34
|
|
|
|
|
149
|
push @$ast => [SYNTAX_PARTIAL, REFERENCE_STATIC, $name, $padding]; |
|
190
|
|
|
|
|
|
|
} |
|
191
|
|
|
|
|
|
|
} else { |
|
192
|
0
|
|
|
|
|
0
|
_error($token, "Syntax Error: Unknown Tag Type: '$tag_type'"); # uncoverable statement |
|
193
|
|
|
|
|
|
|
} |
|
194
|
|
|
|
|
|
|
} else { |
|
195
|
0
|
|
|
|
|
0
|
_error($token, 'Syntax Error: Unknown Token'); # uncoverable statement |
|
196
|
|
|
|
|
|
|
} |
|
197
|
|
|
|
|
|
|
} elsif ($type == TOKEN_DELIMITER) { |
|
198
|
583
|
|
|
|
|
1671
|
my @delimiters = @$token[3,4]; |
|
199
|
583
|
|
|
|
|
965
|
$last_delimiter_token = $token; |
|
200
|
583
|
|
|
|
|
2448
|
push @$ast => [SYNTAX_DELIMITER, @delimiters]; |
|
201
|
|
|
|
|
|
|
} else { |
|
202
|
0
|
|
|
|
|
0
|
_error($token, 'Syntax Error: Unknown Token'); # uncoverable statement |
|
203
|
|
|
|
|
|
|
} |
|
204
|
|
|
|
|
|
|
} |
|
205
|
539
|
100
|
|
|
|
1366
|
if (@stack) { |
|
206
|
3
|
|
|
|
|
5
|
my $item = pop @stack; |
|
207
|
3
|
|
|
|
|
8
|
my (undef, undef, $open_token) = @$item; |
|
208
|
3
|
|
|
|
|
13
|
_error($open_token, 'Syntax Error: Unbalanced Section'); |
|
209
|
|
|
|
|
|
|
} |
|
210
|
536
|
|
|
|
|
1535
|
return \@root; |
|
211
|
|
|
|
|
|
|
} |
|
212
|
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
sub _needs_trim_around_whitespaces { |
|
214
|
1580
|
|
|
1580
|
|
2596
|
my $token = shift; |
|
215
|
1580
|
|
|
|
|
2637
|
my ($type) = @$token; |
|
216
|
|
|
|
|
|
|
|
|
217
|
1580
|
100
|
|
|
|
3965
|
if ($type == TOKEN_DELIMITER) { |
|
|
|
100
|
|
|
|
|
|
|
218
|
40
|
|
|
|
|
84
|
my (undef, undef, $tag_body) = @$token; |
|
219
|
40
|
|
|
|
|
140
|
return defined $tag_body; ## tag body is undef when first implicit TOKEN_DELIMITER |
|
220
|
|
|
|
|
|
|
} elsif ($type == TOKEN_TAG) { |
|
221
|
984
|
100
|
|
|
|
1932
|
if (@$token == 4) { |
|
222
|
771
|
|
|
|
|
1546
|
my (undef, undef, $tag_type, undef) = @$token; |
|
223
|
771
|
|
|
|
|
2972
|
return !!$TRIM_AROUND_WHITESPACE_TYPES{$tag_type}; |
|
224
|
|
|
|
|
|
|
} else { |
|
225
|
213
|
|
|
|
|
728
|
return !!0; |
|
226
|
|
|
|
|
|
|
} |
|
227
|
|
|
|
|
|
|
} |
|
228
|
556
|
|
|
|
|
2061
|
return !!0; |
|
229
|
|
|
|
|
|
|
} |
|
230
|
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
sub _error { |
|
232
|
10
|
|
|
10
|
|
26
|
my ($token, $msg) = @_; |
|
233
|
10
|
100
|
|
|
|
210
|
croak $msg unless $SOURCE; |
|
234
|
|
|
|
|
|
|
|
|
235
|
9
|
|
|
|
|
17
|
my $src = $SOURCE; |
|
236
|
9
|
|
|
|
|
16
|
my $curr = $token->[1]; |
|
237
|
9
|
|
|
|
|
14
|
my $line = 1; |
|
238
|
9
|
|
|
|
|
17
|
my $start = 0; |
|
239
|
9
|
|
66
|
|
|
82
|
while ($src =~ /$/smgco and pos $src <= $curr) {# uncoverable condition left |
|
240
|
2
|
|
|
|
|
5
|
$start = pos $src; |
|
241
|
2
|
|
|
|
|
7
|
$line++; |
|
242
|
|
|
|
|
|
|
} |
|
243
|
9
|
|
|
|
|
49
|
my $end = pos $src; |
|
244
|
9
|
|
|
|
|
18
|
my $len = $curr - $start; |
|
245
|
9
|
100
|
|
|
|
19
|
$len-- if $len > 0; |
|
246
|
|
|
|
|
|
|
|
|
247
|
9
|
|
100
|
|
|
67
|
my $trace = join "\n", |
|
248
|
|
|
|
|
|
|
"${msg}: line:$line", |
|
249
|
|
|
|
|
|
|
substr($src, $start || 0, $end - $start), |
|
250
|
|
|
|
|
|
|
(' ' x $len) . '^'; |
|
251
|
9
|
|
|
|
|
1556
|
croak $trace, "\n"; |
|
252
|
|
|
|
|
|
|
} |
|
253
|
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
1; |
|
255
|
|
|
|
|
|
|
__END__ |