line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Text::Xslate::Syntax::Handlebars; |
2
|
|
|
|
|
|
|
our $AUTHORITY = 'cpan:DOY'; |
3
|
|
|
|
|
|
|
$Text::Xslate::Syntax::Handlebars::VERSION = '0.05'; |
4
|
12
|
|
|
12
|
|
11778
|
use Mouse; |
|
12
|
|
|
|
|
27
|
|
|
12
|
|
|
|
|
73
|
|
5
|
|
|
|
|
|
|
|
6
|
12
|
|
|
12
|
|
3650
|
use Carp 'confess'; |
|
12
|
|
|
|
|
24
|
|
|
12
|
|
|
|
|
621
|
|
7
|
12
|
|
|
12
|
|
66
|
use Text::Xslate::Util qw($DEBUG $NUMBER neat p); |
|
12
|
|
|
|
|
19
|
|
|
12
|
|
|
|
|
1490
|
|
8
|
|
|
|
|
|
|
|
9
|
12
|
|
|
12
|
|
6758
|
use Text::Handlebars::Symbol; |
|
12
|
|
|
|
|
28
|
|
|
12
|
|
|
|
|
732
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
extends 'Text::Xslate::Parser'; |
12
|
|
|
|
|
|
|
|
13
|
12
|
|
|
12
|
|
69
|
use constant _DUMP_PROTO => scalar($DEBUG =~ /\b dump=proto \b/xmsi); |
|
12
|
|
|
|
|
22
|
|
|
12
|
|
|
|
|
50115
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
my $nl = qr/\x0d?\x0a/; |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
my $bracket_string = qr/\[ [^\]]* \]/xms; |
18
|
|
|
|
|
|
|
my $STRING = qr/(?: $Text::Xslate::Util::STRING | $bracket_string )/xms; |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
my $single_char = '[.#^/>&;=]'; |
21
|
|
|
|
|
|
|
my $OPERATOR_TOKEN = sprintf( |
22
|
|
|
|
|
|
|
"(?:%s|$single_char)", |
23
|
|
|
|
|
|
|
join('|', map{ quotemeta } qw(..)) |
24
|
|
|
|
|
|
|
); |
25
|
|
|
|
|
|
|
|
26
|
81
|
|
|
81
|
|
12756
|
sub _build_identity_pattern { qr/\@?[A-Za-z_][A-Za-z0-9_?-]*/ } |
27
|
81
|
|
|
81
|
|
588
|
sub _build_comment_pattern { qr/\![^;]*/ } |
28
|
|
|
|
|
|
|
|
29
|
81
|
|
|
81
|
|
1468
|
sub _build_line_start { undef } |
30
|
81
|
|
|
81
|
|
513
|
sub _build_tag_start { '{{' } |
31
|
81
|
|
|
81
|
|
564
|
sub _build_tag_end { '}}' } |
32
|
|
|
|
|
|
|
|
33
|
81
|
|
|
81
|
|
663
|
sub _build_shortcut_table { +{} } |
34
|
|
|
|
|
|
|
|
35
|
4623
|
|
|
4623
|
0
|
104204
|
sub symbol_class { 'Text::Handlebars::Symbol' } |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
sub split_tags { |
38
|
145
|
|
|
145
|
0
|
229
|
my $self = shift; |
39
|
145
|
|
|
|
|
232
|
my ($input) = @_; |
40
|
|
|
|
|
|
|
|
41
|
145
|
|
|
|
|
462
|
my $tag_start = $self->tag_start; |
42
|
145
|
|
|
|
|
369
|
my $tag_end = $self->tag_end; |
43
|
|
|
|
|
|
|
|
44
|
145
|
|
|
|
|
352
|
my $lex_comment = $self->comment_pattern; |
45
|
145
|
|
|
|
|
1852
|
my $lex_code = qr/(?: $lex_comment | (?: $STRING | [^\['"] ) )/xms; |
46
|
|
|
|
|
|
|
|
47
|
145
|
|
|
|
|
257
|
my @chunks; |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
my @raw_text; |
50
|
0
|
|
|
|
|
0
|
my @delimiters; |
51
|
|
|
|
|
|
|
|
52
|
0
|
|
|
|
|
0
|
my $close_tag; |
53
|
145
|
|
|
|
|
329
|
my $standalone = 1; |
54
|
145
|
|
|
|
|
709
|
while ($input) { |
55
|
1142
|
100
|
|
|
|
6629
|
if ($close_tag) { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
56
|
359
|
|
|
|
|
480
|
my $start = 0; |
57
|
359
|
|
|
|
|
416
|
my $pos; |
58
|
359
|
|
|
|
|
1026
|
while(($pos = index $input, $close_tag, $start) >= 0) { |
59
|
359
|
|
|
|
|
670
|
my $code = substr $input, 0, $pos; |
60
|
359
|
|
|
|
|
2863
|
$code =~ s/$lex_code//g; |
61
|
359
|
50
|
|
|
|
969
|
if(length($code) == 0) { |
62
|
359
|
|
|
|
|
549
|
last; |
63
|
|
|
|
|
|
|
} |
64
|
0
|
|
|
|
|
0
|
$start = $pos + 1; |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
|
67
|
359
|
50
|
|
|
|
696
|
if ($pos >= 0) { |
68
|
359
|
|
|
|
|
743
|
my $code = substr $input, 0, $pos, ''; |
69
|
359
|
50
|
|
|
|
1888
|
$input =~ s/\A\Q$close_tag// |
70
|
|
|
|
|
|
|
or die "Oops!"; |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# XXX this is ugly, but i don't know how to get the parsing |
73
|
|
|
|
|
|
|
# right otherwise if we also need to support ^foo |
74
|
359
|
100
|
|
|
|
811
|
$code = 'else' if $code eq '^'; |
75
|
|
|
|
|
|
|
|
76
|
359
|
|
|
|
|
434
|
my @extra; |
77
|
|
|
|
|
|
|
|
78
|
359
|
|
100
|
|
|
1454
|
my $autochomp = $code =~ m{^[!#^/=>]} || $code eq 'else'; |
79
|
|
|
|
|
|
|
|
80
|
359
|
100
|
|
|
|
1157
|
if ($code =~ s/^=\s*([^\s]+)\s+([^\s]+)\s*=$//) { |
|
|
50
|
|
|
|
|
|
81
|
2
|
|
|
|
|
6
|
($tag_start, $tag_end) = ($1, $2); |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
elsif ($code =~ /^=/) { |
84
|
0
|
|
|
|
|
0
|
die "Invalid delimiter tag: $code"; |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
|
87
|
359
|
100
|
100
|
|
|
1152
|
if ($autochomp && $standalone) { |
88
|
122
|
100
|
|
|
|
480
|
if ($input =~ /\A\s*(?:\n|\z)/) { |
89
|
94
|
|
|
|
|
511
|
$input =~ s/\A$nl//; |
90
|
94
|
100
|
100
|
|
|
691
|
if (@chunks > 0 && $chunks[-1][0] eq 'text' && $code !~ m{^>}) { |
|
|
|
66
|
|
|
|
|
91
|
73
|
|
|
|
|
291
|
$chunks[-1][1] =~ s/^(?:(?!\n)\s)*\z//m; |
92
|
73
|
100
|
|
|
|
198
|
if (@raw_text) { |
93
|
49
|
|
|
|
|
237
|
$raw_text[-1] =~ s/^(?:(?!\n)\s)*\z//m; |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
else { |
99
|
237
|
|
|
|
|
334
|
$standalone = 0; |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
|
102
|
359
|
100
|
100
|
|
|
1577
|
if ($code =~ m{^/} || $code eq 'else') { |
103
|
83
|
|
|
|
|
159
|
push @extra, pop @raw_text; |
104
|
83
|
|
|
|
|
141
|
push @extra, pop @delimiters; |
105
|
83
|
100
|
|
|
|
218
|
if (@raw_text) { |
106
|
9
|
|
|
|
|
22
|
$raw_text[-1] .= $extra[0]; |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
} |
109
|
359
|
100
|
|
|
|
869
|
if (@raw_text) { |
110
|
96
|
100
|
|
|
|
209
|
if ($close_tag eq '}}}') { |
111
|
8
|
|
|
|
|
20
|
$raw_text[-1] .= '{{{' . $code . '}}}'; |
112
|
|
|
|
|
|
|
} |
113
|
|
|
|
|
|
|
else { |
114
|
88
|
|
|
|
|
220
|
$raw_text[-1] .= $tag_start . $code . $tag_end; |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
} |
117
|
359
|
100
|
100
|
|
|
1647
|
if ($code =~ m{^[#^]} || $code eq 'else') { |
118
|
83
|
|
|
|
|
143
|
push @raw_text, ''; |
119
|
83
|
|
|
|
|
211
|
push @delimiters, [$tag_start, $tag_end]; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
359
|
100
|
|
|
|
816
|
if (length($code)) { |
123
|
357
|
100
|
|
|
|
1148
|
push @chunks, [ |
124
|
|
|
|
|
|
|
($close_tag eq '}}}' ? 'raw_code' : 'code'), |
125
|
|
|
|
|
|
|
$code, |
126
|
|
|
|
|
|
|
@extra, |
127
|
|
|
|
|
|
|
]; |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
|
130
|
359
|
|
|
|
|
1241
|
undef $close_tag; |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
else { |
133
|
0
|
|
|
|
|
0
|
last; # the end tag is not found |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
elsif ($input =~ s/\A\Q$tag_start//) { |
137
|
359
|
100
|
100
|
|
|
1780
|
if ($tag_start eq '{{' && $input =~ s/\A\{//) { |
138
|
25
|
|
|
|
|
76
|
$close_tag = '}}}'; |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
else { |
141
|
334
|
|
|
|
|
1106
|
$close_tag = $tag_end; |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
elsif ($input =~ s/\A([^\n]*?(?:\n|(?=\Q$tag_start\E)|\z))//) { |
145
|
424
|
|
|
|
|
892
|
my $text = $1; |
146
|
424
|
50
|
|
|
|
1466
|
if (length($text)) { |
147
|
424
|
|
|
|
|
1053
|
push @chunks, [ text => $text ]; |
148
|
|
|
|
|
|
|
|
149
|
424
|
100
|
|
|
|
887
|
if ($standalone) { |
150
|
243
|
|
|
|
|
1130
|
$standalone = $text =~ /(?:^|\n)\s*$/; |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
else { |
153
|
181
|
|
|
|
|
519
|
$standalone = $text =~ /\n\s*$/; |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
424
|
100
|
|
|
|
1515
|
if (@raw_text) { |
157
|
148
|
|
|
|
|
528
|
$raw_text[-1] .= $text; |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
else { |
162
|
0
|
|
|
|
|
0
|
confess "Oops: unreached code, near " . p($input); |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
|
166
|
145
|
50
|
|
|
|
319
|
if ($close_tag) { |
167
|
|
|
|
|
|
|
# calculate line number |
168
|
0
|
|
|
|
|
0
|
my $orig_src = $_[0]; |
169
|
0
|
|
|
|
|
0
|
substr $orig_src, -length($input), length($input), ''; |
170
|
0
|
|
|
|
|
0
|
my $line = ($orig_src =~ tr/\n/\n/); |
171
|
0
|
|
|
|
|
0
|
$self->_error("Malformed templates detected", |
172
|
|
|
|
|
|
|
neat((split /\n/, $input)[0]), ++$line, |
173
|
|
|
|
|
|
|
); |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
|
176
|
145
|
|
|
|
|
738
|
return @chunks; |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
sub preprocess { |
180
|
145
|
|
|
145
|
0
|
17988
|
my $self = shift; |
181
|
145
|
|
|
|
|
255
|
my ($input) = @_; |
182
|
|
|
|
|
|
|
|
183
|
145
|
|
|
|
|
360
|
my @chunks = $self->split_tags($input); |
184
|
|
|
|
|
|
|
|
185
|
145
|
|
|
|
|
258
|
my $code = ''; |
186
|
145
|
|
|
|
|
283
|
for my $chunk (@chunks) { |
187
|
781
|
|
|
|
|
1690
|
my ($type, $content, $raw_text, $delimiters) = @$chunk; |
188
|
781
|
100
|
|
|
|
1890
|
if ($type eq 'text') { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
189
|
424
|
|
|
|
|
1039
|
$content =~ s/(["\\])/\\$1/g; |
190
|
424
|
100
|
|
|
|
1570
|
$code .= qq{print_raw "$content";\n} |
191
|
|
|
|
|
|
|
if length($content); |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
elsif ($type eq 'code') { |
194
|
332
|
|
|
|
|
432
|
my $extra = ''; |
195
|
332
|
100
|
|
|
|
998
|
if ($content =~ s{^/}{}) { |
|
|
100
|
|
|
|
|
|
196
|
73
|
|
|
|
|
232
|
$chunk->[2] =~ s/(["\\])/\\$1/g; |
197
|
73
|
|
|
|
|
142
|
$chunk->[3][0] =~ s/(["\\])/\\$1/g; |
198
|
73
|
|
|
|
|
148
|
$chunk->[3][1] =~ s/(["\\])/\\$1/g; |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
$extra = '"' |
201
|
73
|
|
|
|
|
125
|
. join('" "', $chunk->[2], @{ $chunk->[3] }) |
|
73
|
|
|
|
|
247
|
|
202
|
|
|
|
|
|
|
. '"'; |
203
|
73
|
|
|
|
|
279
|
$code .= qq{/$extra $content;\n}; |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
elsif ($content eq 'else') { |
206
|
|
|
|
|
|
|
# XXX fix duplication |
207
|
10
|
|
|
|
|
42
|
$chunk->[2] =~ s/(["\\])/\\$1/g; |
208
|
10
|
|
|
|
|
24
|
$chunk->[3][0] =~ s/(["\\])/\\$1/g; |
209
|
10
|
|
|
|
|
23
|
$chunk->[3][1] =~ s/(["\\])/\\$1/g; |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
$extra = '"' |
212
|
10
|
|
|
|
|
19
|
. join('" "', $chunk->[2], @{ $chunk->[3] }) |
|
10
|
|
|
|
|
40
|
|
213
|
|
|
|
|
|
|
. '"'; |
214
|
10
|
|
|
|
|
35
|
$code .= qq{$content $extra;\n}; |
215
|
|
|
|
|
|
|
} |
216
|
|
|
|
|
|
|
else { |
217
|
249
|
|
|
|
|
635
|
$code .= qq{$content;\n}; |
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
elsif ($type eq 'raw_code') { |
221
|
25
|
|
|
|
|
67
|
$code .= qq{&$content;\n}; |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
else { |
224
|
0
|
|
|
|
|
0
|
$self->_error("Oops: Unknown token: $content ($type)"); |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
|
228
|
145
|
|
|
|
|
176
|
print STDOUT $code, "\n" if _DUMP_PROTO; |
229
|
145
|
|
|
|
|
1086
|
return $code; |
230
|
|
|
|
|
|
|
} |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
# XXX advance has some syntax special cases in it, probably need to override |
233
|
|
|
|
|
|
|
# it too eventually |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
sub init_symbols { |
236
|
81
|
|
|
81
|
0
|
1888
|
my $self = shift; |
237
|
|
|
|
|
|
|
|
238
|
81
|
|
|
|
|
189
|
for my $type (qw(name key literal)) { |
239
|
243
|
|
|
|
|
855
|
my $symbol = $self->symbol("($type)"); |
240
|
243
|
|
|
|
|
2824
|
$symbol->arity($type); |
241
|
243
|
|
|
|
|
1594
|
$symbol->set_nud($self->can("nud_$type")); |
242
|
243
|
|
|
|
|
1695
|
$symbol->lbp(10); |
243
|
|
|
|
|
|
|
} |
244
|
|
|
|
|
|
|
|
245
|
81
|
|
|
|
|
167
|
for my $this (qw(. this)) { |
246
|
162
|
|
|
|
|
805
|
my $symbol = $self->symbol($this); |
247
|
162
|
|
|
|
|
2482
|
$symbol->arity('key'); |
248
|
162
|
|
|
|
|
421
|
$symbol->id('.'); |
249
|
162
|
|
|
|
|
354
|
$symbol->lbp(10); |
250
|
162
|
|
|
|
|
1050
|
$symbol->set_nud($self->can('nud_key')); |
251
|
|
|
|
|
|
|
} |
252
|
|
|
|
|
|
|
|
253
|
81
|
|
|
|
|
573
|
for my $field_access (qw(. /)) { |
254
|
162
|
|
|
|
|
1980
|
$self->infix($field_access, 256, $self->can('led_dot')); |
255
|
|
|
|
|
|
|
} |
256
|
|
|
|
|
|
|
|
257
|
81
|
|
|
|
|
1500
|
for my $block ('#', '^') { |
258
|
162
|
|
|
|
|
1624
|
$self->symbol($block)->set_std($self->can('std_block')); |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
81
|
|
|
|
|
1462
|
for my $else (qw(/ else)) { |
262
|
162
|
|
|
|
|
1062
|
$self->symbol($else)->is_block_end(1); |
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
|
265
|
81
|
|
|
|
|
1300
|
$self->symbol('>')->set_std($self->can('std_partial')); |
266
|
|
|
|
|
|
|
|
267
|
81
|
|
|
|
|
1332
|
$self->symbol('&')->set_nud($self->can('nud_mark_raw')); |
268
|
81
|
|
|
|
|
1752
|
$self->symbol('..')->set_nud($self->can('nud_uplevel')); |
269
|
81
|
|
|
|
|
1777
|
$self->symbol('..')->lbp(10); |
270
|
|
|
|
|
|
|
|
271
|
81
|
|
|
|
|
1067
|
$self->infix('=', 20, $self->can('led_equals')); |
272
|
|
|
|
|
|
|
} |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
# copied from Text::Xslate::Parser, but using different definitions of |
275
|
|
|
|
|
|
|
# $STRING and $OPERATOR_TOKEN |
276
|
|
|
|
|
|
|
sub tokenize { |
277
|
2608
|
|
|
2608
|
0
|
3887
|
my($parser) = @_; |
278
|
|
|
|
|
|
|
|
279
|
2608
|
|
|
|
|
5263
|
local *_ = \$parser->{input}; |
280
|
|
|
|
|
|
|
|
281
|
2608
|
|
|
|
|
6129
|
my $comment_rx = $parser->comment_pattern; |
282
|
2608
|
|
|
|
|
5467
|
my $id_rx = $parser->identity_pattern; |
283
|
2608
|
|
|
|
|
3408
|
my $count = 0; |
284
|
|
|
|
|
|
|
TRY: { |
285
|
2608
|
|
|
|
|
3168
|
/\G (\s*) /xmsgc; |
|
2610
|
|
|
|
|
6277
|
|
286
|
2610
|
|
|
|
|
5222
|
$count += ( $1 =~ tr/\n/\n/); |
287
|
2610
|
|
|
|
|
6234
|
$parser->following_newline( $count ); |
288
|
|
|
|
|
|
|
|
289
|
2610
|
100
|
|
|
|
23576
|
if(/\G $comment_rx /xmsgc) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
290
|
2
|
|
|
|
|
8
|
redo TRY; # retry |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
elsif(/\G ($id_rx)/xmsgc){ |
293
|
806
|
|
|
|
|
5205
|
return [ name => $1 ]; |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
elsif(/\G ($NUMBER | $STRING)/xmsogc){ |
296
|
650
|
|
|
|
|
4158
|
return [ literal => $1 ]; |
297
|
|
|
|
|
|
|
} |
298
|
|
|
|
|
|
|
elsif(/\G ($OPERATOR_TOKEN)/xmsogc){ |
299
|
1007
|
|
|
|
|
6380
|
return [ operator => $1 ]; |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
elsif(/\G (\S+)/xmsgc) { |
302
|
0
|
|
|
|
|
0
|
Carp::confess("Oops: Unexpected token '$1'"); |
303
|
|
|
|
|
|
|
} |
304
|
|
|
|
|
|
|
else { # empty |
305
|
145
|
|
|
|
|
807
|
return [ special => '(end)' ]; |
306
|
|
|
|
|
|
|
} |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
sub nud_name { |
311
|
112
|
|
|
112
|
0
|
1396
|
my $self = shift; |
312
|
112
|
|
|
|
|
163
|
my ($symbol) = @_; |
313
|
|
|
|
|
|
|
|
314
|
112
|
|
|
|
|
344
|
my $name = $self->SUPER::nud_name($symbol); |
315
|
|
|
|
|
|
|
|
316
|
112
|
|
|
|
|
2856
|
my $call = $self->call($name); |
317
|
|
|
|
|
|
|
|
318
|
112
|
100
|
|
|
|
497
|
if ($self->token->is_defined) { |
319
|
3
|
|
|
|
|
3
|
push @{ $call->second }, $self->expression(0); |
|
3
|
|
|
|
|
23
|
|
320
|
|
|
|
|
|
|
} |
321
|
|
|
|
|
|
|
|
322
|
112
|
|
|
|
|
319
|
return $call; |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
sub nud_key { |
326
|
287
|
|
|
287
|
0
|
1454
|
my $self = shift; |
327
|
287
|
|
|
|
|
417
|
my ($symbol) = @_; |
328
|
|
|
|
|
|
|
|
329
|
287
|
|
|
|
|
758
|
return $symbol->clone(arity => 'key'); |
330
|
|
|
|
|
|
|
} |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
sub led_dot { |
333
|
48
|
|
|
48
|
0
|
283
|
my $self = shift; |
334
|
48
|
|
|
|
|
87
|
my ($symbol, $left) = @_; |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
# XXX hack to make {{{.}}} work, but in general this syntax is ambiguous |
337
|
|
|
|
|
|
|
# and i'm not going to deal with it |
338
|
48
|
100
|
66
|
|
|
203
|
if ($left->arity eq 'call' && $left->first->id eq 'mark_raw') { |
339
|
1
|
|
|
|
|
2
|
push @{ $left->second }, $symbol->nud($self); |
|
1
|
|
|
|
|
6
|
|
340
|
1
|
|
|
|
|
27
|
return $left; |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
|
343
|
47
|
|
|
|
|
170
|
my $dot = $self->make_field_lookup($left, $self->token, $symbol); |
344
|
|
|
|
|
|
|
|
345
|
47
|
|
|
|
|
2041
|
$self->advance; |
346
|
|
|
|
|
|
|
|
347
|
47
|
|
|
|
|
305
|
return $dot; |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
sub std_block { |
351
|
73
|
|
|
73
|
0
|
450
|
my $self = shift; |
352
|
73
|
|
|
|
|
114
|
my ($symbol) = @_; |
353
|
|
|
|
|
|
|
|
354
|
73
|
|
|
|
|
210
|
my $inverted = $symbol->id eq '^'; |
355
|
|
|
|
|
|
|
|
356
|
73
|
|
|
|
|
204
|
my $name = $self->expression(0); |
357
|
|
|
|
|
|
|
|
358
|
73
|
50
|
66
|
|
|
534
|
if ($name->arity ne 'key' && $name->arity ne 'key_field' && $name->arity ne 'call') { |
|
|
|
66
|
|
|
|
|
359
|
0
|
|
|
|
|
0
|
$self->_unexpected("opening block name", $self->token); |
360
|
|
|
|
|
|
|
} |
361
|
73
|
|
|
|
|
218
|
my $name_string = $self->_field_to_string($name); |
362
|
|
|
|
|
|
|
|
363
|
73
|
|
|
|
|
311
|
$self->advance(';'); |
364
|
|
|
|
|
|
|
|
365
|
73
|
|
|
|
|
97
|
my %block; |
366
|
73
|
|
|
|
|
117
|
my $context = 'if'; |
367
|
73
|
|
|
|
|
240
|
$block{$context}{body} = $self->statements; |
368
|
|
|
|
|
|
|
|
369
|
73
|
100
|
|
|
|
359
|
if ($self->token->id eq 'else') { |
370
|
10
|
|
|
|
|
33
|
$self->advance; |
371
|
|
|
|
|
|
|
|
372
|
10
|
|
|
|
|
44
|
$block{$context}{raw_text} = $self->token; |
373
|
10
|
|
|
|
|
31
|
$self->advance; |
374
|
10
|
|
|
|
|
38
|
$block{$context}{open_tag} = $self->token; |
375
|
10
|
|
|
|
|
31
|
$self->advance; |
376
|
10
|
|
|
|
|
34
|
$block{$context}{close_tag} = $self->token; |
377
|
10
|
|
|
|
|
33
|
$self->advance; |
378
|
|
|
|
|
|
|
|
379
|
10
|
|
|
|
|
19
|
$context = 'else'; |
380
|
10
|
|
|
|
|
32
|
$block{$context}{body} = $self->statements; |
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
|
383
|
73
|
|
|
|
|
209
|
$self->advance('/'); |
384
|
|
|
|
|
|
|
|
385
|
73
|
|
|
|
|
255
|
$block{$context}{raw_text} = $self->token; |
386
|
73
|
|
|
|
|
209
|
$self->advance; |
387
|
73
|
|
|
|
|
242
|
$block{$context}{open_tag} = $self->token; |
388
|
73
|
|
|
|
|
205
|
$self->advance; |
389
|
73
|
|
|
|
|
261
|
$block{$context}{close_tag} = $self->token; |
390
|
73
|
|
|
|
|
202
|
$self->advance; |
391
|
|
|
|
|
|
|
|
392
|
73
|
100
|
|
|
|
252
|
if ($inverted) { |
393
|
1
|
|
|
|
|
4
|
($block{if}, $block{else}) = ($block{else}, $block{if}); |
394
|
1
|
50
|
|
|
|
4
|
if (!$block{if}) { |
395
|
1
|
|
|
|
|
3
|
$block{if}{body} = $self->literal(''); |
396
|
1
|
|
|
|
|
30
|
$block{if}{raw_text} = $self->literal(''); |
397
|
1
|
|
|
|
|
28
|
$block{if}{open_tag} = $block{else}{open_tag}; |
398
|
1
|
|
|
|
|
3
|
$block{if}{close_tag} = $block{else}{close_tag}; |
399
|
|
|
|
|
|
|
} |
400
|
|
|
|
|
|
|
} |
401
|
|
|
|
|
|
|
|
402
|
73
|
|
|
|
|
167
|
my $closing_name = $self->expression(0); |
403
|
|
|
|
|
|
|
|
404
|
73
|
50
|
66
|
|
|
523
|
if ($closing_name->arity ne 'key' && $closing_name->arity ne 'key_field' && $closing_name->arity ne 'call') { |
|
|
|
66
|
|
|
|
|
405
|
0
|
|
|
|
|
0
|
$self->_unexpected("closing block name", $self->token); |
406
|
|
|
|
|
|
|
} |
407
|
73
|
|
|
|
|
171
|
my $closing_name_string = $self->_field_to_string($closing_name); |
408
|
|
|
|
|
|
|
|
409
|
73
|
50
|
|
|
|
212
|
if ($name_string ne $closing_name_string) { |
410
|
0
|
|
|
|
|
0
|
$self->_unexpected('/' . $name_string, $self->token); |
411
|
|
|
|
|
|
|
} |
412
|
|
|
|
|
|
|
|
413
|
73
|
|
|
|
|
212
|
$self->advance(';'); |
414
|
|
|
|
|
|
|
|
415
|
73
|
|
|
|
|
278
|
return $self->print_raw( |
416
|
|
|
|
|
|
|
$name->clone( |
417
|
|
|
|
|
|
|
arity => 'block', |
418
|
|
|
|
|
|
|
first => $name, |
419
|
|
|
|
|
|
|
second => \%block, |
420
|
|
|
|
|
|
|
), |
421
|
|
|
|
|
|
|
); |
422
|
|
|
|
|
|
|
} |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
sub nud_mark_raw { |
425
|
26
|
|
|
26
|
0
|
156
|
my $self = shift; |
426
|
26
|
|
|
|
|
41
|
my ($symbol) = @_; |
427
|
|
|
|
|
|
|
|
428
|
26
|
|
|
|
|
87
|
return $self->symbol('mark_raw')->clone( |
429
|
|
|
|
|
|
|
line => $symbol->line, |
430
|
|
|
|
|
|
|
)->nud($self); |
431
|
|
|
|
|
|
|
} |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
sub nud_uplevel { |
434
|
17
|
|
|
17
|
0
|
95
|
my $self = shift; |
435
|
17
|
|
|
|
|
30
|
my ($symbol) = @_; |
436
|
|
|
|
|
|
|
|
437
|
17
|
|
|
|
|
61
|
return $symbol->clone(arity => 'variable'); |
438
|
|
|
|
|
|
|
} |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
sub std_partial { |
441
|
3
|
|
|
3
|
0
|
18
|
my $self = shift; |
442
|
3
|
|
|
|
|
5
|
my ($symbol) = @_; |
443
|
|
|
|
|
|
|
|
444
|
3
|
|
|
|
|
15
|
my $partial = $self->token->clone(arity => 'literal'); |
445
|
3
|
|
|
|
|
72
|
$self->advance; |
446
|
3
|
|
|
|
|
5
|
my $args; |
447
|
3
|
100
|
|
|
|
20
|
if ($self->token->id ne ';') { |
448
|
1
|
|
|
|
|
3
|
$args = $self->expression(0); |
449
|
|
|
|
|
|
|
} |
450
|
3
|
|
|
|
|
18
|
$self->advance(';'); |
451
|
|
|
|
|
|
|
|
452
|
3
|
50
|
|
|
|
26
|
return $symbol->clone( |
453
|
|
|
|
|
|
|
arity => 'partial', |
454
|
|
|
|
|
|
|
first => ($partial->id =~ /\./ ? $partial : [ $partial ]), |
455
|
|
|
|
|
|
|
second => $args, |
456
|
|
|
|
|
|
|
); |
457
|
|
|
|
|
|
|
} |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
sub led_equals { |
460
|
4
|
|
|
4
|
0
|
28
|
my $self = shift; |
461
|
4
|
|
|
|
|
8
|
my ($symbol, $left) = @_; |
462
|
|
|
|
|
|
|
|
463
|
4
|
|
|
|
|
22
|
my $right = $self->expression($symbol->lbp); |
464
|
|
|
|
|
|
|
|
465
|
4
|
|
|
|
|
15
|
return $symbol->clone( |
466
|
|
|
|
|
|
|
arity => 'pair', |
467
|
|
|
|
|
|
|
first => $left->clone(arity => 'literal'), |
468
|
|
|
|
|
|
|
second => $right, |
469
|
|
|
|
|
|
|
); |
470
|
|
|
|
|
|
|
} |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
sub undefined_name { |
473
|
283
|
|
|
283
|
0
|
422
|
my $self = shift; |
474
|
283
|
|
|
|
|
435
|
my ($name) = @_; |
475
|
|
|
|
|
|
|
|
476
|
283
|
|
|
|
|
815
|
return $self->symbol('(key)')->clone(id => $name); |
477
|
|
|
|
|
|
|
} |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
sub define_function { |
480
|
243
|
|
|
243
|
0
|
2865
|
my $self = shift; |
481
|
243
|
|
|
|
|
633
|
my (@names) = @_; |
482
|
|
|
|
|
|
|
|
483
|
243
|
|
|
|
|
787
|
$self->SUPER::define_function(@_); |
484
|
243
|
|
|
|
|
8217
|
for my $name (@names) { |
485
|
1651
|
|
|
|
|
4107
|
my $symbol = $self->symbol($name); |
486
|
1651
|
|
|
|
|
19517
|
$symbol->set_nud($self->can('nud_name')); |
487
|
1651
|
|
|
|
|
10372
|
$symbol->lbp(10); |
488
|
|
|
|
|
|
|
} |
489
|
|
|
|
|
|
|
|
490
|
243
|
|
|
|
|
626
|
return; |
491
|
|
|
|
|
|
|
} |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
sub define_helper { |
494
|
162
|
|
|
162
|
0
|
295
|
my $self = shift; |
495
|
162
|
|
|
|
|
348
|
my (@names) = @_; |
496
|
|
|
|
|
|
|
|
497
|
162
|
|
|
|
|
355
|
$self->define_function(@names); |
498
|
162
|
|
|
|
|
316
|
for my $name (@names) { |
499
|
343
|
|
|
|
|
877
|
my $symbol = $self->symbol($name); |
500
|
343
|
|
|
|
|
3134
|
$symbol->is_helper(1); |
501
|
|
|
|
|
|
|
} |
502
|
|
|
|
|
|
|
|
503
|
162
|
|
|
|
|
430
|
return; |
504
|
|
|
|
|
|
|
} |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
sub parse_literal { |
507
|
650
|
|
|
650
|
0
|
852
|
my $self = shift; |
508
|
650
|
|
|
|
|
1010
|
my ($literal) = @_; |
509
|
|
|
|
|
|
|
|
510
|
650
|
100
|
|
|
|
1628
|
if ($literal =~ /\A\[(.*)\]\z/ms) { |
511
|
4
|
|
|
|
|
10
|
$literal = $1; |
512
|
4
|
|
|
|
|
15
|
$literal =~ s/(["\\])/\\$1/g; |
513
|
4
|
|
|
|
|
10
|
$literal = '"' . $literal . '"'; |
514
|
|
|
|
|
|
|
} |
515
|
|
|
|
|
|
|
|
516
|
650
|
|
|
|
|
1848
|
return $self->SUPER::parse_literal($literal); |
517
|
|
|
|
|
|
|
} |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
sub is_valid_field { |
520
|
47
|
|
|
47
|
0
|
64
|
my $self = shift; |
521
|
47
|
|
|
|
|
72
|
my ($field) = @_; |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
# allow foo.[10] |
524
|
47
|
100
|
|
|
|
159
|
return 1 if $field->arity eq 'literal'; |
525
|
|
|
|
|
|
|
# undefined symbols are all treated as keys - see undefined_name |
526
|
44
|
100
|
|
|
|
213
|
return 1 if $field->arity eq 'key'; |
527
|
|
|
|
|
|
|
# allow ../../foo |
528
|
11
|
50
|
|
|
|
54
|
return 1 if $field->id eq '..'; |
529
|
|
|
|
|
|
|
|
530
|
0
|
|
|
|
|
0
|
return; |
531
|
|
|
|
|
|
|
} |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
sub expression { |
534
|
812
|
|
|
812
|
0
|
6750
|
my $self = shift; |
535
|
812
|
|
|
|
|
1167
|
my ($rbp) = @_; |
536
|
|
|
|
|
|
|
|
537
|
812
|
|
|
|
|
1770
|
my $token = $self->token; |
538
|
812
|
|
|
|
|
2031
|
$self->advance; |
539
|
812
|
|
|
|
|
2253
|
my $left = $token->nud($self); |
540
|
|
|
|
|
|
|
|
541
|
812
|
|
|
|
|
20061
|
while ($rbp < $self->token->lbp) { |
542
|
122
|
|
|
|
|
298
|
$token = $self->token; |
543
|
122
|
100
|
|
|
|
442
|
if ($token->has_led) { |
544
|
52
|
|
|
|
|
155
|
$self->advance; |
545
|
52
|
|
|
|
|
268
|
$left = $token->led($self, $left); |
546
|
|
|
|
|
|
|
} |
547
|
|
|
|
|
|
|
else { |
548
|
70
|
50
|
|
|
|
245
|
if ($left->arity ne 'call') { |
549
|
0
|
|
|
|
|
0
|
$self->_error("Unexpected " . $token->arity, $left); |
550
|
|
|
|
|
|
|
} |
551
|
70
|
|
|
|
|
103
|
push @{ $left->second }, $self->expression($token->lbp); |
|
70
|
|
|
|
|
345
|
|
552
|
|
|
|
|
|
|
} |
553
|
|
|
|
|
|
|
} |
554
|
|
|
|
|
|
|
|
555
|
812
|
|
|
|
|
2828
|
return $left; |
556
|
|
|
|
|
|
|
} |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
sub call { |
559
|
932
|
|
|
932
|
0
|
1326
|
my $self = shift; |
560
|
|
|
|
|
|
|
|
561
|
932
|
|
|
|
|
2677
|
my $call = $self->SUPER::call(@_); |
562
|
932
|
|
|
|
|
33389
|
$call->is_helper($call->first->is_helper); |
563
|
932
|
|
|
|
|
2755
|
return $call; |
564
|
|
|
|
|
|
|
} |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
sub make_field_lookup { |
567
|
47
|
|
|
47
|
0
|
78
|
my $self = shift; |
568
|
47
|
|
|
|
|
72
|
my ($var, $field, $dot) = @_; |
569
|
|
|
|
|
|
|
|
570
|
47
|
50
|
|
|
|
118
|
if (!$self->is_valid_field($field)) { |
571
|
0
|
|
|
|
|
0
|
$self->_unexpected("a field name", $field); |
572
|
|
|
|
|
|
|
} |
573
|
|
|
|
|
|
|
|
574
|
47
|
|
33
|
|
|
586
|
$dot ||= $self->symbol('.'); |
575
|
|
|
|
|
|
|
|
576
|
47
|
|
|
|
|
142
|
return $dot->clone( |
577
|
|
|
|
|
|
|
arity => 'key_field', |
578
|
|
|
|
|
|
|
first => $var, |
579
|
|
|
|
|
|
|
second => $field->clone(arity => 'literal'), |
580
|
|
|
|
|
|
|
); |
581
|
|
|
|
|
|
|
} |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
sub print_raw { |
584
|
73
|
|
|
73
|
0
|
1693
|
my $self = shift; |
585
|
73
|
|
|
|
|
251
|
return $self->print(@_)->clone(id => 'print_raw'); |
586
|
|
|
|
|
|
|
} |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
sub literal { |
589
|
384
|
|
|
384
|
0
|
511
|
my $self = shift; |
590
|
384
|
|
|
|
|
539
|
my ($value) = @_; |
591
|
384
|
|
|
|
|
978
|
return $self->symbol('(literal)')->clone(id => $value); |
592
|
|
|
|
|
|
|
} |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
sub _field_to_string { |
595
|
146
|
|
|
146
|
|
200
|
my $self = shift; |
596
|
146
|
|
|
|
|
236
|
my ($symbol) = @_; |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
# name and key can just be returned |
599
|
146
|
50
|
|
|
|
769
|
return $symbol->id |
600
|
|
|
|
|
|
|
unless $symbol->arity eq 'field'; |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
# field accesses should recurse on the first and append the second |
603
|
0
|
|
|
|
|
|
return $self->_field_to_string($symbol->first) . '.' . $symbol->second->id; |
604
|
|
|
|
|
|
|
} |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
607
|
12
|
|
|
12
|
|
80
|
no Mouse; |
|
12
|
|
|
|
|
23
|
|
|
12
|
|
|
|
|
65
|
|
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
=for Pod::Coverage |
610
|
|
|
|
|
|
|
call |
611
|
|
|
|
|
|
|
define_function |
612
|
|
|
|
|
|
|
define_helper |
613
|
|
|
|
|
|
|
expression |
614
|
|
|
|
|
|
|
init_symbols |
615
|
|
|
|
|
|
|
is_valid_field |
616
|
|
|
|
|
|
|
led_dot |
617
|
|
|
|
|
|
|
led_equals |
618
|
|
|
|
|
|
|
literal |
619
|
|
|
|
|
|
|
make_field_lookup |
620
|
|
|
|
|
|
|
nud_key |
621
|
|
|
|
|
|
|
nud_mark_raw |
622
|
|
|
|
|
|
|
nud_name |
623
|
|
|
|
|
|
|
nud_uplevel |
624
|
|
|
|
|
|
|
parse_literal |
625
|
|
|
|
|
|
|
preprocess |
626
|
|
|
|
|
|
|
print_raw |
627
|
|
|
|
|
|
|
split_tags |
628
|
|
|
|
|
|
|
std_block |
629
|
|
|
|
|
|
|
std_partial |
630
|
|
|
|
|
|
|
symbol_class |
631
|
|
|
|
|
|
|
tokenize |
632
|
|
|
|
|
|
|
undefined_name |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
=cut |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
1; |