line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Text::Xslate::Parser; |
2
|
172
|
|
|
172
|
|
225108
|
use Mouse; |
|
172
|
|
|
|
|
124281
|
|
|
172
|
|
|
|
|
989
|
|
3
|
|
|
|
|
|
|
|
4
|
172
|
|
|
172
|
|
47538
|
use Scalar::Util (); |
|
172
|
|
|
|
|
358
|
|
|
172
|
|
|
|
|
3281
|
|
5
|
|
|
|
|
|
|
|
6
|
172
|
|
|
172
|
|
90993
|
use Text::Xslate::Symbol; |
|
172
|
|
|
|
|
17387
|
|
|
172
|
|
|
|
|
7169
|
|
7
|
172
|
|
|
|
|
29428
|
use Text::Xslate::Util qw( |
8
|
|
|
|
|
|
|
$DEBUG |
9
|
|
|
|
|
|
|
$STRING $NUMBER |
10
|
|
|
|
|
|
|
is_int any_in |
11
|
|
|
|
|
|
|
neat |
12
|
|
|
|
|
|
|
literal_to_value |
13
|
|
|
|
|
|
|
make_error |
14
|
|
|
|
|
|
|
p |
15
|
172
|
|
|
172
|
|
1090
|
); |
|
172
|
|
|
|
|
295
|
|
16
|
|
|
|
|
|
|
|
17
|
172
|
|
|
172
|
|
958
|
use constant _DUMP_PROTO => scalar($DEBUG =~ /\b dump=proto \b/xmsi); |
|
172
|
|
|
|
|
316
|
|
|
172
|
|
|
|
|
11898
|
|
18
|
172
|
|
|
172
|
|
839
|
use constant _DUMP_TOKEN => scalar($DEBUG =~ /\b dump=token \b/xmsi); |
|
172
|
|
|
|
|
320
|
|
|
172
|
|
|
|
|
1979458
|
|
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
our @CARP_NOT = qw(Text::Xslate::Compiler Text::Xslate::Symbol); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
my $CODE = qr/ (?: $STRING | [^'"] ) /xms; |
23
|
|
|
|
|
|
|
my $COMMENT = qr/\# [^\n;]* (?= [;\n] | \z)/xms; |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
# Operator tokens that the parser recognizes. |
26
|
|
|
|
|
|
|
# All the single characters are tokenized as an operator. |
27
|
|
|
|
|
|
|
my $OPERATOR_TOKEN = sprintf '(?:%s|[^ \t\r\n])', join('|', map{ quotemeta } qw( |
28
|
|
|
|
|
|
|
... |
29
|
|
|
|
|
|
|
.. |
30
|
|
|
|
|
|
|
== != <=> <= >= |
31
|
|
|
|
|
|
|
<< >> |
32
|
|
|
|
|
|
|
+= -= *= /= %= ~= |
33
|
|
|
|
|
|
|
&&= ||= //= |
34
|
|
|
|
|
|
|
~~ =~ |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
&& || // |
37
|
|
|
|
|
|
|
-> => |
38
|
|
|
|
|
|
|
:: |
39
|
|
|
|
|
|
|
++ -- |
40
|
|
|
|
|
|
|
+| +& +^ +< +> +~ |
41
|
|
|
|
|
|
|
), ','); |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
my %shortcut_table = ( |
44
|
|
|
|
|
|
|
'=' => 'print', |
45
|
|
|
|
|
|
|
); |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
my $CHOMP_FLAGS = qr/-/xms; |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
has identity_pattern => ( |
51
|
|
|
|
|
|
|
is => 'ro', |
52
|
|
|
|
|
|
|
isa => 'RegexpRef', |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
builder => '_build_identity_pattern', |
55
|
|
|
|
|
|
|
init_arg => undef, |
56
|
|
|
|
|
|
|
); |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
sub _build_identity_pattern { |
59
|
182
|
|
|
182
|
|
28669
|
return qr/(?: (?:[A-Za-z_]|\$\~?) [A-Za-z0-9_]* )/xms; |
60
|
|
|
|
|
|
|
} |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
has [qw(compiler engine)] => ( |
63
|
|
|
|
|
|
|
is => 'rw', |
64
|
|
|
|
|
|
|
required => 0, |
65
|
|
|
|
|
|
|
weak_ref => 1, |
66
|
|
|
|
|
|
|
); |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
has symbol_table => ( # the global symbol table |
69
|
|
|
|
|
|
|
is => 'ro', |
70
|
|
|
|
|
|
|
isa => 'HashRef', |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
default => sub{ {} }, |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
init_arg => undef, |
75
|
|
|
|
|
|
|
); |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
has iterator_element => ( |
78
|
|
|
|
|
|
|
is => 'ro', |
79
|
|
|
|
|
|
|
isa => 'HashRef', |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
lazy => 1, |
82
|
|
|
|
|
|
|
builder => '_build_iterator_element', |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
init_arg => undef, |
85
|
|
|
|
|
|
|
); |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
has scope => ( |
88
|
|
|
|
|
|
|
is => 'rw', |
89
|
|
|
|
|
|
|
isa => 'ArrayRef[HashRef]', |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
clearer => 'init_scope', |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
lazy => 1, |
94
|
|
|
|
|
|
|
default => sub{ [ {} ] }, |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
init_arg => undef, |
97
|
|
|
|
|
|
|
); |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
has token => ( |
100
|
|
|
|
|
|
|
is => 'rw', |
101
|
|
|
|
|
|
|
isa => 'Maybe[Object]', |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
init_arg => undef, |
104
|
|
|
|
|
|
|
); |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
has next_token => ( # to peek the next token |
107
|
|
|
|
|
|
|
is => 'rw', |
108
|
|
|
|
|
|
|
isa => 'Maybe[ArrayRef]', |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
init_arg => undef, |
111
|
|
|
|
|
|
|
); |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
has statement_is_finished => ( |
114
|
|
|
|
|
|
|
is => 'rw', |
115
|
|
|
|
|
|
|
isa => 'Bool', |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
init_arg => undef, |
118
|
|
|
|
|
|
|
); |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
has following_newline => ( |
121
|
|
|
|
|
|
|
is => 'rw', |
122
|
|
|
|
|
|
|
isa => 'Int', |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
default => 0, |
125
|
|
|
|
|
|
|
init_arg => undef, |
126
|
|
|
|
|
|
|
); |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
has input => ( |
129
|
|
|
|
|
|
|
is => 'rw', |
130
|
|
|
|
|
|
|
isa => 'Str', |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
init_arg => undef, |
133
|
|
|
|
|
|
|
); |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
has line_start => ( |
136
|
|
|
|
|
|
|
is => 'ro', |
137
|
|
|
|
|
|
|
isa => 'Maybe[Str]', |
138
|
|
|
|
|
|
|
builder => '_build_line_start', |
139
|
|
|
|
|
|
|
); |
140
|
176
|
|
|
176
|
|
6508
|
sub _build_line_start { ':' } |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
has tag_start => ( |
143
|
|
|
|
|
|
|
is => 'ro', |
144
|
|
|
|
|
|
|
isa => 'Str', |
145
|
|
|
|
|
|
|
builder => '_build_tag_start', |
146
|
|
|
|
|
|
|
); |
147
|
177
|
|
|
177
|
|
6330
|
sub _build_tag_start { '<:' } |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
has tag_end => ( |
150
|
|
|
|
|
|
|
is => 'ro', |
151
|
|
|
|
|
|
|
isa => 'Str', |
152
|
|
|
|
|
|
|
builder => '_build_tag_end', |
153
|
|
|
|
|
|
|
); |
154
|
177
|
|
|
177
|
|
7175
|
sub _build_tag_end { ':>' } |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
has comment_pattern => ( |
157
|
|
|
|
|
|
|
is => 'ro', |
158
|
|
|
|
|
|
|
isa => 'RegexpRef', |
159
|
|
|
|
|
|
|
builder => '_build_comment_pattern', |
160
|
|
|
|
|
|
|
); |
161
|
241
|
|
|
241
|
|
7490
|
sub _build_comment_pattern { $COMMENT } |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
has shortcut_table => ( |
164
|
|
|
|
|
|
|
is => 'ro', |
165
|
|
|
|
|
|
|
isa => 'HashRef[Str]', |
166
|
|
|
|
|
|
|
builder => '_build_shortcut_table', |
167
|
|
|
|
|
|
|
); |
168
|
241
|
|
|
241
|
|
5653
|
sub _build_shortcut_table { \%shortcut_table } |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
has in_given => ( |
171
|
|
|
|
|
|
|
is => 'rw', |
172
|
|
|
|
|
|
|
isa => 'Bool', |
173
|
|
|
|
|
|
|
init_arg => undef, |
174
|
|
|
|
|
|
|
); |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
# attributes for error messages |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
has near_token => ( |
179
|
|
|
|
|
|
|
is => 'rw', |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
init_arg => undef, |
182
|
|
|
|
|
|
|
); |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
has file => ( |
185
|
|
|
|
|
|
|
is => 'rw', |
186
|
|
|
|
|
|
|
required => 0, |
187
|
|
|
|
|
|
|
); |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
has line => ( |
190
|
|
|
|
|
|
|
is => 'rw', |
191
|
|
|
|
|
|
|
required => 0, |
192
|
|
|
|
|
|
|
); |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
has input_layer => ( |
195
|
|
|
|
|
|
|
is => 'ro', |
196
|
|
|
|
|
|
|
default => ':utf8', |
197
|
|
|
|
|
|
|
); |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
sub symbol_class() { 'Text::Xslate::Symbol' } |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
# the entry point |
202
|
|
|
|
|
|
|
sub parse { |
203
|
3458
|
|
|
3458
|
0
|
30177
|
my($parser, $input, %args) = @_; |
204
|
|
|
|
|
|
|
|
205
|
3458
|
|
100
|
|
|
16732
|
local $parser->{file} = $args{file} || \$input; |
206
|
3458
|
|
50
|
|
|
17618
|
local $parser->{line} = $args{line} || 1; |
207
|
3458
|
|
|
|
|
8424
|
local $parser->{in_given} = 0; |
208
|
3458
|
|
|
|
|
6357
|
local $parser->{scope} = [ map { +{ %{$_} } } @{ $parser->scope } ]; |
|
3458
|
|
|
|
|
5919
|
|
|
3458
|
|
|
|
|
20135
|
|
|
3458
|
|
|
|
|
15762
|
|
209
|
3458
|
|
|
|
|
6683
|
local $parser->{symbol_table} = { %{ $parser->symbol_table } }; |
|
3458
|
|
|
|
|
173673
|
|
210
|
3458
|
|
|
|
|
29557
|
local $parser->{near_token}; |
211
|
3458
|
|
|
|
|
8161
|
local $parser->{next_token}; |
212
|
3458
|
|
|
|
|
7753
|
local $parser->{token}; |
213
|
3458
|
|
|
|
|
7439
|
local $parser->{input}; |
214
|
|
|
|
|
|
|
|
215
|
3458
|
|
|
|
|
11185
|
$parser->input( $parser->preprocess($input) ); |
216
|
|
|
|
|
|
|
|
217
|
3453
|
|
|
|
|
11385
|
$parser->next_token( $parser->tokenize() ); |
218
|
3453
|
|
|
|
|
10956
|
$parser->advance(); |
219
|
3452
|
|
|
|
|
10253
|
my $ast = $parser->statements(); |
220
|
|
|
|
|
|
|
|
221
|
3411
|
100
|
|
|
|
14274
|
if(my $input_pos = pos $parser->{input}) { |
222
|
3408
|
100
|
|
|
|
12845
|
if($input_pos != length($parser->{input})) { |
223
|
2
|
|
|
|
|
8
|
$parser->_error("Syntax error", $parser->token); |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
3409
|
|
|
|
|
76499
|
return $ast; |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
sub trim_code { |
231
|
5197
|
|
|
5197
|
0
|
9942
|
my($parser, $s) = @_; |
232
|
|
|
|
|
|
|
|
233
|
5197
|
|
|
|
|
15902
|
$s =~ s/\A [ \t]+ //xms; |
234
|
5197
|
|
|
|
|
22424
|
$s =~ s/ [ \t]+ \n?\z//xms; |
235
|
|
|
|
|
|
|
|
236
|
5197
|
|
|
|
|
15781
|
return $s; |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
sub auto_chomp { |
240
|
10377
|
|
|
10377
|
0
|
18620
|
my($parser, $tokens_ref, $i, $s_ref) = @_; |
241
|
|
|
|
|
|
|
|
242
|
10377
|
|
|
|
|
14528
|
my $p; |
243
|
10377
|
|
|
|
|
15541
|
my $nl = 0; |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
# postchomp |
246
|
10377
|
100
|
100
|
|
|
49135
|
if($i >= 1 |
247
|
|
|
|
|
|
|
and ($p = $tokens_ref->[$i-1])->[0] eq 'postchomp') { |
248
|
|
|
|
|
|
|
# [ CODE ][*][ TEXT ] |
249
|
|
|
|
|
|
|
# <: ... -:> \nfoobar |
250
|
|
|
|
|
|
|
# ^^^^ |
251
|
391
|
|
|
|
|
446
|
${$s_ref} =~ s/\A [ \t]* (\n)//xms; |
|
391
|
|
|
|
|
1567
|
|
252
|
391
|
100
|
|
|
|
1211
|
if($1) { |
253
|
386
|
|
|
|
|
543
|
$nl++; |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
} |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
# prechomp |
258
|
10377
|
100
|
100
|
|
|
16132
|
if(($i+1) < @{$tokens_ref} |
|
10377
|
100
|
100
|
|
|
52825
|
|
|
|
|
66
|
|
|
|
|
|
|
|
100
|
|
|
|
|
259
|
|
|
|
|
|
|
and ($p = $tokens_ref->[$i+1])->[0] eq 'prechomp') { |
260
|
51
|
100
|
|
|
|
62
|
if(${$s_ref} !~ / [^ \t] /xms) { |
|
51
|
|
|
|
|
167
|
|
261
|
|
|
|
|
|
|
# HERE |
262
|
|
|
|
|
|
|
# [ TEXT ][*][ CODE ] |
263
|
|
|
|
|
|
|
# <:- ... :> |
264
|
|
|
|
|
|
|
# ^^^^^^^^ |
265
|
34
|
|
|
|
|
44
|
${$s_ref} = ''; |
|
34
|
|
|
|
|
62
|
|
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
else { |
268
|
|
|
|
|
|
|
# HERE |
269
|
|
|
|
|
|
|
# [ TEXT ][*][ CODE ] |
270
|
|
|
|
|
|
|
# \n<:- ... :> |
271
|
|
|
|
|
|
|
# ^^ |
272
|
17
|
|
|
|
|
34
|
$nl += chomp ${$s_ref}; |
|
17
|
|
|
|
|
55
|
|
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
} |
275
|
10326
|
|
|
|
|
59886
|
elsif(($i+2) < @{$tokens_ref} |
276
|
|
|
|
|
|
|
and ($p = $tokens_ref->[$i+2])->[0] eq 'prechomp' |
277
|
|
|
|
|
|
|
and ($p = $tokens_ref->[$i+1])->[0] eq 'text' |
278
|
|
|
|
|
|
|
and $p->[1] !~ / [^ \t] /xms) { |
279
|
|
|
|
|
|
|
# HERE |
280
|
|
|
|
|
|
|
# [ TEXT ][ TEXT ][*][ CODE ] |
281
|
|
|
|
|
|
|
# \n <:- ... :> |
282
|
|
|
|
|
|
|
# ^^^^^^^^^^ |
283
|
16
|
|
|
|
|
30
|
$p->[1] = ''; |
284
|
16
|
|
|
|
|
22
|
$nl += (${$s_ref} =~ s/\n\z//xms); |
|
16
|
|
|
|
|
43
|
|
285
|
|
|
|
|
|
|
} |
286
|
10377
|
|
|
|
|
26432
|
return $nl; |
287
|
|
|
|
|
|
|
} |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
# split templates by tags before tokenizing |
290
|
|
|
|
|
|
|
sub split :method { |
291
|
3458
|
|
|
3458
|
0
|
6460
|
my $parser = shift; |
292
|
3458
|
|
|
|
|
8351
|
local($_) = @_; |
293
|
|
|
|
|
|
|
|
294
|
3458
|
|
|
|
|
6283
|
my @tokens; |
295
|
|
|
|
|
|
|
|
296
|
3458
|
|
|
|
|
11298
|
my $line_start = $parser->line_start; |
297
|
3458
|
|
|
|
|
9917
|
my $tag_start = $parser->tag_start; |
298
|
3458
|
|
|
|
|
9763
|
my $tag_end = $parser->tag_end; |
299
|
|
|
|
|
|
|
|
300
|
3458
|
|
66
|
|
|
37247
|
my $lex_line_code = defined($line_start) |
301
|
|
|
|
|
|
|
&& qr/\A ^ [ \t]* \Q$line_start\E ([^\n]* \n?) /xms; |
302
|
|
|
|
|
|
|
|
303
|
3458
|
|
|
|
|
19649
|
my $lex_tag_start = qr/\A \Q$tag_start\E ($CHOMP_FLAGS?)/xms; |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
# 'text' is a something without newlines |
306
|
|
|
|
|
|
|
# following a newline, $tag_start, or end of the input |
307
|
3458
|
|
|
|
|
19542
|
my $lex_text = qr/\A ( [^\n]*? (?: \n | (?= \Q$tag_start\E ) | \z ) ) /xms; |
308
|
|
|
|
|
|
|
|
309
|
3458
|
|
|
|
|
11244
|
my $lex_comment = $parser->comment_pattern; |
310
|
3458
|
|
|
|
|
29577
|
my $lex_code = qr/(?: $lex_comment | $CODE )/xms; |
311
|
|
|
|
|
|
|
|
312
|
3458
|
|
|
|
|
7435
|
my $in_tag = 0; |
313
|
|
|
|
|
|
|
|
314
|
3458
|
|
|
|
|
10716
|
while($_ ne '') { |
315
|
18244
|
100
|
100
|
|
|
3218739
|
if($in_tag) { |
|
|
100
|
66
|
|
|
|
|
|
|
100
|
100
|
|
|
|
|
|
|
50
|
|
|
|
|
|
316
|
2652
|
|
|
|
|
4970
|
my $start = 0; |
317
|
2652
|
|
|
|
|
4775
|
my $pos; |
318
|
2652
|
|
|
|
|
11367
|
while( ($pos = index $_, $tag_end, $start) >= 0 ) { |
319
|
2651
|
|
|
|
|
8358
|
my $code = substr $_, 0, $pos; |
320
|
2651
|
|
|
|
|
44947
|
$code =~ s/$lex_code//xmsg; |
321
|
2651
|
100
|
|
|
|
9029
|
if(length($code) == 0) { |
322
|
2648
|
|
|
|
|
7719
|
last; |
323
|
|
|
|
|
|
|
} |
324
|
3
|
|
|
|
|
11
|
$start = $pos + 1; |
325
|
|
|
|
|
|
|
} |
326
|
|
|
|
|
|
|
|
327
|
2652
|
100
|
|
|
|
6977
|
if($pos >= 0) { |
328
|
2648
|
|
|
|
|
9308
|
my $code = substr $_, 0, $pos, ''; |
329
|
2648
|
|
|
|
|
15720
|
$code =~ s/($CHOMP_FLAGS?) \z//xmso; |
330
|
2648
|
|
|
|
|
6877
|
my $chomp = $1; |
331
|
|
|
|
|
|
|
|
332
|
2648
|
50
|
|
|
|
16201
|
s/\A \Q$tag_end\E //xms or die "Oops!"; |
333
|
|
|
|
|
|
|
|
334
|
2648
|
|
|
|
|
8971
|
push @tokens, [ code => $code ]; |
335
|
2648
|
100
|
|
|
|
7895
|
if($chomp) { |
336
|
393
|
|
|
|
|
932
|
push @tokens, [ postchomp => $chomp ]; |
337
|
|
|
|
|
|
|
} |
338
|
2648
|
|
|
|
|
13817
|
$in_tag = 0; |
339
|
|
|
|
|
|
|
} |
340
|
|
|
|
|
|
|
else { |
341
|
4
|
|
|
|
|
8
|
last; # the end tag is not found |
342
|
|
|
|
|
|
|
} |
343
|
|
|
|
|
|
|
} |
344
|
|
|
|
|
|
|
# not $in_tag |
345
|
|
|
|
|
|
|
elsif($lex_line_code |
346
|
|
|
|
|
|
|
&& (@tokens == 0 || $tokens[-1][1] =~ /\n\z/xms) |
347
|
|
|
|
|
|
|
&& s/$lex_line_code//xms) { |
348
|
2554
|
|
|
|
|
12560
|
push @tokens, [ code => $1 ]; |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
elsif(s/$lex_tag_start//xms) { |
351
|
2653
|
|
|
|
|
5517
|
$in_tag = 1; |
352
|
|
|
|
|
|
|
|
353
|
2653
|
|
|
|
|
6848
|
my $chomp = $1; |
354
|
2653
|
100
|
|
|
|
13366
|
if($chomp) { |
355
|
60
|
|
|
|
|
258
|
push @tokens, [ prechomp => $chomp ]; |
356
|
|
|
|
|
|
|
} |
357
|
|
|
|
|
|
|
} |
358
|
|
|
|
|
|
|
elsif(s/$lex_text//xms) { |
359
|
10385
|
|
|
|
|
57292
|
push @tokens, [ text => $1 ]; |
360
|
|
|
|
|
|
|
} |
361
|
|
|
|
|
|
|
else { |
362
|
0
|
|
|
|
|
0
|
confess "Oops: Unreached code, near" . p($_); |
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
} |
365
|
|
|
|
|
|
|
|
366
|
3458
|
100
|
|
|
|
9511
|
if($in_tag) { |
367
|
|
|
|
|
|
|
# calculate line number |
368
|
5
|
|
|
|
|
10
|
my $orig_src = $_[0]; |
369
|
5
|
|
|
|
|
21
|
substr $orig_src, -length($_), length($_), ''; |
370
|
5
|
|
|
|
|
13
|
my $line = ($orig_src =~ tr/\n/\n/); |
371
|
5
|
|
|
|
|
32
|
$parser->_error("Malformed templates detected", |
372
|
|
|
|
|
|
|
neat((split /\n/, $_)[0]), ++$line, |
373
|
|
|
|
|
|
|
); |
374
|
|
|
|
|
|
|
} |
375
|
|
|
|
|
|
|
#p(\@tokens); |
376
|
3453
|
|
|
|
|
20276
|
return \@tokens; |
377
|
|
|
|
|
|
|
} |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
sub preprocess { |
380
|
3458
|
|
|
3458
|
0
|
7118
|
my($parser, $input) = @_; |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
# tokenization |
383
|
|
|
|
|
|
|
|
384
|
3458
|
|
|
|
|
9832
|
my $tokens_ref = $parser->split($input); |
385
|
3453
|
|
|
|
|
7138
|
my $code = ''; |
386
|
|
|
|
|
|
|
|
387
|
3453
|
|
|
|
|
11553
|
my $shortcut_table = $parser->shortcut_table; |
388
|
3453
|
|
|
|
|
9431
|
my $shortcut = join('|', map{ quotemeta } keys %shortcut_table); |
|
3453
|
|
|
|
|
14286
|
|
389
|
3453
|
|
|
|
|
17051
|
my $shortcut_rx = qr/\A ($shortcut)/xms; |
390
|
|
|
|
|
|
|
|
391
|
3453
|
|
|
|
|
7619
|
for(my $i = 0; $i < @{$tokens_ref}; $i++) { |
|
19484
|
|
|
|
|
62494
|
|
392
|
16031
|
|
|
|
|
23404
|
my($type, $s) = @{ $tokens_ref->[$i] }; |
|
16031
|
|
|
|
|
42616
|
|
393
|
|
|
|
|
|
|
|
394
|
16031
|
100
|
|
|
|
42701
|
if($type eq 'text') { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
395
|
10377
|
|
|
|
|
26642
|
my $nl = $parser->auto_chomp($tokens_ref, $i, \$s); |
396
|
|
|
|
|
|
|
|
397
|
10377
|
|
|
|
|
24203
|
$s =~ s/(["\\])/\\$1/gxms; # " for poor editors |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
# $s may have single new line |
400
|
10377
|
|
|
|
|
31567
|
$nl += ($s =~ s/\n/\\n/xms); |
401
|
|
|
|
|
|
|
|
402
|
10377
|
|
|
|
|
27412
|
$code .= qq{print_raw "$s";}; # must set even if $s is empty |
403
|
10377
|
100
|
|
|
|
44023
|
$code .= qq{\n} if $nl > 0; |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
elsif($type eq 'code') { |
406
|
|
|
|
|
|
|
# shortcut commands |
407
|
5201
|
50
|
|
|
|
27413
|
$s =~ s/$shortcut_rx/$shortcut_table->{$1}/xms |
408
|
|
|
|
|
|
|
if $shortcut; |
409
|
|
|
|
|
|
|
|
410
|
5201
|
|
|
|
|
14859
|
$s = $parser->trim_code($s); |
411
|
|
|
|
|
|
|
|
412
|
5201
|
100
|
|
|
|
24576
|
if($s =~ /\A \s* [}] \s* \z/xms){ |
|
|
100
|
|
|
|
|
|
413
|
546
|
|
|
|
|
1248
|
$code .= $s; |
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
elsif($s =~ s/\n\z//xms) { |
416
|
2028
|
|
|
|
|
6710
|
$code .= qq{$s\n}; |
417
|
|
|
|
|
|
|
} |
418
|
|
|
|
|
|
|
else { |
419
|
2627
|
|
|
|
|
15006
|
$code .= qq{$s;}; # auto semicolon insertion |
420
|
|
|
|
|
|
|
} |
421
|
|
|
|
|
|
|
} |
422
|
|
|
|
|
|
|
elsif($type eq 'prechomp') { |
423
|
|
|
|
|
|
|
# noop, just a marker |
424
|
|
|
|
|
|
|
} |
425
|
|
|
|
|
|
|
elsif($type eq 'postchomp') { |
426
|
|
|
|
|
|
|
# noop, just a marker |
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
else { |
429
|
0
|
|
|
|
|
0
|
$parser->_error("Oops: Unknown token: $s ($type)"); |
430
|
|
|
|
|
|
|
} |
431
|
|
|
|
|
|
|
} |
432
|
3453
|
|
|
|
|
5889
|
print STDOUT $code, "\n" if _DUMP_PROTO; |
433
|
3453
|
|
|
|
|
27627
|
return $code; |
434
|
|
|
|
|
|
|
} |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
sub BUILD { |
437
|
241
|
|
|
241
|
1
|
2121
|
my($parser) = @_; |
438
|
241
|
|
|
|
|
3524
|
$parser->_init_basic_symbols(); |
439
|
241
|
|
|
|
|
2876
|
$parser->init_symbols(); |
440
|
241
|
|
|
|
|
7230
|
return; |
441
|
|
|
|
|
|
|
} |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
# The grammer |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
sub _init_basic_symbols { |
446
|
241
|
|
|
241
|
|
2282
|
my($parser) = @_; |
447
|
|
|
|
|
|
|
|
448
|
241
|
|
|
|
|
3264
|
$parser->symbol('(end)')->is_block_end(1); # EOF |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
# prototypes of value symbols |
451
|
241
|
|
|
|
|
2384
|
foreach my $type (qw(name variable literal)) { |
452
|
723
|
|
|
|
|
7518
|
my $s = $parser->symbol("($type)"); |
453
|
723
|
|
|
|
|
7663
|
$s->arity($type); |
454
|
723
|
|
|
|
|
17951
|
$s->set_nud( $parser->can("nud_$type") ); |
455
|
|
|
|
|
|
|
} |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
# common separators |
458
|
241
|
|
|
|
|
2520
|
$parser->symbol(';')->set_nud(\&nud_separator); |
459
|
241
|
|
|
|
|
3053
|
$parser->define_pair('(' => ')'); |
460
|
241
|
|
|
|
|
2513
|
$parser->define_pair('{' => '}'); |
461
|
241
|
|
|
|
|
2439
|
$parser->define_pair('[' => ']'); |
462
|
241
|
|
|
|
|
2710
|
$parser->symbol(',') ->is_comma(1); |
463
|
241
|
|
|
|
|
2412
|
$parser->symbol('=>') ->is_comma(1); |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
# common commands |
466
|
241
|
|
|
|
|
2310
|
$parser->symbol('print') ->set_std(\&std_print); |
467
|
241
|
|
|
|
|
2489
|
$parser->symbol('print_raw')->set_std(\&std_print); |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
# special literals |
470
|
241
|
|
|
|
|
3164
|
$parser->define_literal(nil => undef); |
471
|
241
|
|
|
|
|
2288
|
$parser->define_literal(true => 1); |
472
|
241
|
|
|
|
|
2348
|
$parser->define_literal(false => 0); |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
# special tokens |
475
|
241
|
|
|
|
|
2332
|
$parser->symbol('__FILE__')->set_nud(\&nud_current_file); |
476
|
241
|
|
|
|
|
2492
|
$parser->symbol('__LINE__')->set_nud(\&nud_current_line); |
477
|
241
|
|
|
|
|
2360
|
$parser->symbol('__ROOT__')->set_nud(\&nud_current_vars); |
478
|
|
|
|
|
|
|
|
479
|
241
|
|
|
|
|
3844
|
return; |
480
|
|
|
|
|
|
|
} |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
sub init_basic_operators { |
483
|
241
|
|
|
241
|
0
|
2230
|
my($parser) = @_; |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
# define operator precedence |
486
|
|
|
|
|
|
|
|
487
|
241
|
|
|
|
|
3331
|
$parser->prefix('{', 256, \&nud_brace); |
488
|
241
|
|
|
|
|
2410
|
$parser->prefix('[', 256, \&nud_brace); |
489
|
|
|
|
|
|
|
|
490
|
241
|
|
|
|
|
3216
|
$parser->infix('(', 256, \&led_call); |
491
|
241
|
|
|
|
|
3057
|
$parser->infix('.', 256, \&led_dot); |
492
|
241
|
|
|
|
|
2858
|
$parser->infix('[', 256, \&led_fetch); |
493
|
|
|
|
|
|
|
|
494
|
241
|
|
|
|
|
4006
|
$parser->prefix('(', 256, \&nud_paren); |
495
|
|
|
|
|
|
|
|
496
|
241
|
|
|
|
|
2854
|
$parser->prefix('!', 200)->is_logical(1); |
497
|
241
|
|
|
|
|
2917
|
$parser->prefix('+', 200); |
498
|
241
|
|
|
|
|
2323
|
$parser->prefix('-', 200); |
499
|
241
|
|
|
|
|
2306
|
$parser->prefix('+^', 200); # numeric bitwise negate |
500
|
|
|
|
|
|
|
|
501
|
241
|
|
|
|
|
2341
|
$parser->infix('*', 190); |
502
|
241
|
|
|
|
|
2477
|
$parser->infix('/', 190); |
503
|
241
|
|
|
|
|
2400
|
$parser->infix('%', 190); |
504
|
241
|
|
|
|
|
2514
|
$parser->infix('x', 190); |
505
|
241
|
|
|
|
|
2280
|
$parser->infix('+&', 190); # numeric bitwise and |
506
|
|
|
|
|
|
|
|
507
|
241
|
|
|
|
|
2282
|
$parser->infix('+', 180); |
508
|
241
|
|
|
|
|
2332
|
$parser->infix('-', 180); |
509
|
241
|
|
|
|
|
2705
|
$parser->infix('~', 180); # connect |
510
|
241
|
|
|
|
|
2416
|
$parser->infix('+|', 180); # numeric bitwise or |
511
|
241
|
|
|
|
|
2298
|
$parser->infix('+^', 180); # numeric bitwise xor |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
|
514
|
241
|
|
|
|
|
2401
|
$parser->prefix('defined', 170, \&nud_defined); # named unary operator |
515
|
|
|
|
|
|
|
|
516
|
241
|
|
|
|
|
2313
|
$parser->infix('<', 160)->is_logical(1); |
517
|
241
|
|
|
|
|
2275
|
$parser->infix('<=', 160)->is_logical(1); |
518
|
241
|
|
|
|
|
2293
|
$parser->infix('>', 160)->is_logical(1); |
519
|
241
|
|
|
|
|
2328
|
$parser->infix('>=', 160)->is_logical(1); |
520
|
|
|
|
|
|
|
|
521
|
241
|
|
|
|
|
2306
|
$parser->infix('==', 150)->is_logical(1); |
522
|
241
|
|
|
|
|
2344
|
$parser->infix('!=', 150)->is_logical(1); |
523
|
241
|
|
|
|
|
2341
|
$parser->infix('<=>', 150); |
524
|
241
|
|
|
|
|
2494
|
$parser->infix('cmp', 150); |
525
|
241
|
|
|
|
|
2412
|
$parser->infix('~~', 150); |
526
|
|
|
|
|
|
|
|
527
|
241
|
|
|
|
|
2402
|
$parser->infix('|', 140, \&led_pipe); |
528
|
|
|
|
|
|
|
|
529
|
241
|
|
|
|
|
2323
|
$parser->infix('&&', 130)->is_logical(1); |
530
|
|
|
|
|
|
|
|
531
|
241
|
|
|
|
|
2484
|
$parser->infix('||', 120)->is_logical(1); |
532
|
241
|
|
|
|
|
2412
|
$parser->infix('//', 120)->is_logical(1); |
533
|
241
|
|
|
|
|
2422
|
$parser->infix('min', 120); |
534
|
241
|
|
|
|
|
2300
|
$parser->infix('max', 120); |
535
|
|
|
|
|
|
|
|
536
|
241
|
|
|
|
|
2571
|
$parser->infix('..', 110, \&led_range); |
537
|
|
|
|
|
|
|
|
538
|
241
|
|
|
|
|
2282
|
$parser->symbol(':'); |
539
|
241
|
|
|
|
|
3153
|
$parser->infixr('?', 100, \&led_ternary); |
540
|
|
|
|
|
|
|
|
541
|
241
|
|
|
|
|
3907
|
$parser->assignment('=', 90); |
542
|
241
|
|
|
|
|
2534
|
$parser->assignment('+=', 90); |
543
|
241
|
|
|
|
|
2356
|
$parser->assignment('-=', 90); |
544
|
241
|
|
|
|
|
2264
|
$parser->assignment('*=', 90); |
545
|
241
|
|
|
|
|
2384
|
$parser->assignment('/=', 90); |
546
|
241
|
|
|
|
|
2481
|
$parser->assignment('%=', 90); |
547
|
241
|
|
|
|
|
2403
|
$parser->assignment('~=', 90); |
548
|
241
|
|
|
|
|
2361
|
$parser->assignment('&&=', 90); |
549
|
241
|
|
|
|
|
2794
|
$parser->assignment('||=', 90); |
550
|
241
|
|
|
|
|
2300
|
$parser->assignment('//=', 90); |
551
|
|
|
|
|
|
|
|
552
|
241
|
|
|
|
|
3135
|
$parser->make_alias('!' => 'not')->ubp(70); |
553
|
241
|
|
|
|
|
3629
|
$parser->make_alias('&&' => 'and')->lbp(60); |
554
|
241
|
|
|
|
|
2629
|
$parser->make_alias('||' => 'or') ->lbp(50); |
555
|
241
|
|
|
|
|
5667
|
return; |
556
|
|
|
|
|
|
|
} |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
sub init_symbols { |
559
|
182
|
|
|
182
|
0
|
2033
|
my($parser) = @_; |
560
|
182
|
|
|
|
|
1976
|
my $s; |
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
# syntax specific separators |
563
|
182
|
|
|
|
|
2125
|
$parser->symbol('{'); |
564
|
182
|
|
|
|
|
2084
|
$parser->symbol('}')->is_block_end(1); # block end |
565
|
182
|
|
|
|
|
2297
|
$parser->symbol('->'); |
566
|
182
|
|
|
|
|
2130
|
$parser->symbol('else'); |
567
|
182
|
|
|
|
|
2111
|
$parser->symbol('with'); |
568
|
182
|
|
|
|
|
2152
|
$parser->symbol('::'); |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
# operators |
571
|
182
|
|
|
|
|
2719
|
$parser->init_basic_operators(); |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
# statements |
574
|
182
|
|
|
|
|
2253
|
$s = $parser->symbol('if'); |
575
|
182
|
|
|
|
|
2448
|
$s->set_std(\&std_if); |
576
|
182
|
|
|
|
|
2569
|
$s->can_be_modifier(1); |
577
|
|
|
|
|
|
|
|
578
|
182
|
|
|
|
|
2172
|
$parser->symbol('for') ->set_std(\&std_for); |
579
|
182
|
|
|
|
|
2287
|
$parser->symbol('while' ) ->set_std(\&std_while); |
580
|
182
|
|
|
|
|
2200
|
$parser->symbol('given') ->set_std(\&std_given); |
581
|
182
|
|
|
|
|
2322
|
$parser->symbol('when') ->set_std(\&std_when); |
582
|
182
|
|
|
|
|
2131
|
$parser->symbol('default') ->set_std(\&std_when); |
583
|
|
|
|
|
|
|
|
584
|
182
|
|
|
|
|
2166
|
$parser->symbol('include') ->set_std(\&std_include); |
585
|
|
|
|
|
|
|
|
586
|
182
|
|
|
|
|
2227
|
$parser->symbol('last') ->set_std(\&std_last); |
587
|
182
|
|
|
|
|
2221
|
$parser->symbol('next') ->set_std(\&std_next); |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
# macros |
590
|
|
|
|
|
|
|
|
591
|
182
|
|
|
|
|
2348
|
$parser->symbol('cascade') ->set_std(\&std_cascade); |
592
|
182
|
|
|
|
|
2143
|
$parser->symbol('macro') ->set_std(\&std_proc); |
593
|
182
|
|
|
|
|
2222
|
$parser->symbol('around') ->set_std(\&std_proc); |
594
|
182
|
|
|
|
|
2368
|
$parser->symbol('before') ->set_std(\&std_proc); |
595
|
182
|
|
|
|
|
2257
|
$parser->symbol('after') ->set_std(\&std_proc); |
596
|
182
|
|
|
|
|
2244
|
$parser->symbol('block') ->set_std(\&std_macro_block); |
597
|
182
|
|
|
|
|
2319
|
$parser->symbol('super') ->set_std(\&std_super); |
598
|
182
|
|
|
|
|
2418
|
$parser->symbol('override') ->set_std(\&std_override); |
599
|
|
|
|
|
|
|
|
600
|
182
|
|
|
|
|
2385
|
$parser->symbol('->') ->set_nud(\&nud_lambda); |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
# lexical variables/constants stuff |
603
|
182
|
|
|
|
|
2180
|
$parser->symbol('constant')->set_nud(\&nud_constant); |
604
|
182
|
|
|
|
|
2253
|
$parser->symbol('my' )->set_nud(\&nud_constant); |
605
|
|
|
|
|
|
|
|
606
|
182
|
|
|
|
|
3712
|
return; |
607
|
|
|
|
|
|
|
} |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
sub _build_iterator_element { |
610
|
|
|
|
|
|
|
return { |
611
|
4
|
|
|
4
|
|
90
|
index => \&iterator_index, |
612
|
|
|
|
|
|
|
count => \&iterator_count, |
613
|
|
|
|
|
|
|
is_first => \&iterator_is_first, |
614
|
|
|
|
|
|
|
is_last => \&iterator_is_last, |
615
|
|
|
|
|
|
|
body => \&iterator_body, |
616
|
|
|
|
|
|
|
size => \&iterator_size, |
617
|
|
|
|
|
|
|
max_index => \&iterator_max_index, |
618
|
|
|
|
|
|
|
peek_next => \&iterator_peek_next, |
619
|
|
|
|
|
|
|
peek_prev => \&iterator_peek_prev, |
620
|
|
|
|
|
|
|
cycle => \&iterator_cycle, |
621
|
|
|
|
|
|
|
}; |
622
|
|
|
|
|
|
|
} |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
sub symbol { |
626
|
52955
|
|
|
52955
|
0
|
284756
|
my($parser, $id, $lbp) = @_; |
627
|
|
|
|
|
|
|
|
628
|
52955
|
|
|
|
|
314049
|
my $stash = $parser->symbol_table; |
629
|
52955
|
|
|
|
|
285198
|
my $s = $stash->{$id}; |
630
|
52955
|
100
|
|
|
|
300576
|
if(defined $s) { |
631
|
25240
|
100
|
|
|
|
98762
|
if(defined $lbp) { |
632
|
1205
|
|
|
|
|
27549
|
$s->lbp($lbp); |
633
|
|
|
|
|
|
|
} |
634
|
|
|
|
|
|
|
} |
635
|
|
|
|
|
|
|
else { # create a new symbol |
636
|
27715
|
|
100
|
|
|
509024
|
$s = $parser->symbol_class->new(id => $id, lbp => $lbp || 0); |
637
|
27715
|
|
|
|
|
464240
|
$stash->{$id} = $s; |
638
|
|
|
|
|
|
|
} |
639
|
|
|
|
|
|
|
|
640
|
52955
|
|
|
|
|
573156
|
return $s; |
641
|
|
|
|
|
|
|
} |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
sub define_pair { |
644
|
723
|
|
|
723
|
0
|
6468
|
my($parser, $left, $right) = @_; |
645
|
723
|
|
|
|
|
6689
|
$parser->symbol($left) ->counterpart($right); |
646
|
723
|
|
|
|
|
6765
|
$parser->symbol($right)->counterpart($left); |
647
|
723
|
|
|
|
|
11528
|
return; |
648
|
|
|
|
|
|
|
} |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
# the low-level tokenizer. Don't use it directly, use advance() instead. |
651
|
|
|
|
|
|
|
sub tokenize { |
652
|
64596
|
|
|
64596
|
0
|
103673
|
my($parser) = @_; |
653
|
|
|
|
|
|
|
|
654
|
64596
|
|
|
|
|
139145
|
local *_ = \$parser->{input}; |
655
|
|
|
|
|
|
|
|
656
|
64596
|
|
|
|
|
154679
|
my $comment_rx = $parser->comment_pattern; |
657
|
64596
|
|
|
|
|
139842
|
my $id_rx = $parser->identity_pattern; |
658
|
64596
|
|
|
|
|
92633
|
my $count = 0; |
659
|
|
|
|
|
|
|
TRY: { |
660
|
64596
|
|
|
|
|
91740
|
/\G (\s*) /xmsgc; |
|
64642
|
|
|
|
|
168784
|
|
661
|
64642
|
|
|
|
|
138551
|
$count += ( $1 =~ tr/\n/\n/); |
662
|
64642
|
|
|
|
|
166816
|
$parser->following_newline( $count ); |
663
|
|
|
|
|
|
|
|
664
|
64642
|
100
|
|
|
|
587028
|
if(/\G $comment_rx /xmsgc) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
665
|
46
|
|
|
|
|
103
|
redo TRY; # retry |
666
|
|
|
|
|
|
|
} |
667
|
|
|
|
|
|
|
elsif(/\G ($id_rx)/xmsgc){ |
668
|
19446
|
|
|
|
|
122033
|
return [ name => $1 ]; |
669
|
|
|
|
|
|
|
} |
670
|
|
|
|
|
|
|
elsif(/\G ($NUMBER | $STRING)/xmsogc){ |
671
|
17591
|
|
|
|
|
102855
|
return [ literal => $1 ]; |
672
|
|
|
|
|
|
|
} |
673
|
|
|
|
|
|
|
elsif(/\G ($OPERATOR_TOKEN)/xmsogc){ |
674
|
24143
|
|
|
|
|
139451
|
return [ operator => $1 ]; |
675
|
|
|
|
|
|
|
} |
676
|
|
|
|
|
|
|
elsif(/\G (\S+)/xmsgc) { |
677
|
0
|
|
|
|
|
0
|
Carp::confess("Oops: Unexpected token '$1'"); |
678
|
|
|
|
|
|
|
} |
679
|
|
|
|
|
|
|
else { # empty |
680
|
3416
|
|
|
|
|
20292
|
return [ special => '(end)' ]; |
681
|
|
|
|
|
|
|
} |
682
|
|
|
|
|
|
|
} |
683
|
|
|
|
|
|
|
} |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
sub next_token_is { |
686
|
19485
|
|
|
19485
|
0
|
35035
|
my($parser, $token) = @_; |
687
|
19485
|
|
|
|
|
116975
|
return $parser->next_token->[1] eq $token; |
688
|
|
|
|
|
|
|
} |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
# the high-level tokenizer |
691
|
|
|
|
|
|
|
sub advance { |
692
|
64567
|
|
|
64567
|
0
|
105548
|
my($parser, $expect) = @_; |
693
|
|
|
|
|
|
|
|
694
|
64567
|
|
|
|
|
143247
|
my $t = $parser->token; |
695
|
64567
|
100
|
100
|
|
|
176111
|
if(defined($expect) && $t->id ne $expect) { |
696
|
7
|
|
|
|
|
24
|
$parser->_unexpected(neat($expect), $t); |
697
|
|
|
|
|
|
|
} |
698
|
|
|
|
|
|
|
|
699
|
64560
|
|
|
|
|
151685
|
$parser->near_token($t); |
700
|
|
|
|
|
|
|
|
701
|
64560
|
|
|
|
|
254737
|
my $stash = $parser->symbol_table; |
702
|
|
|
|
|
|
|
|
703
|
64560
|
|
|
|
|
144333
|
$t = $parser->next_token; |
704
|
|
|
|
|
|
|
|
705
|
64560
|
100
|
|
|
|
160247
|
if($t->[0] eq 'special') { |
706
|
3417
|
|
|
|
|
19636
|
return $parser->token( $stash->{ $t->[1] } ); |
707
|
|
|
|
|
|
|
} |
708
|
61143
|
|
|
|
|
206893
|
$parser->statement_is_finished( $parser->following_newline != 0 ); |
709
|
61143
|
|
|
|
|
238938
|
my $line = $parser->line( $parser->line + $parser->following_newline ); |
710
|
|
|
|
|
|
|
|
711
|
61143
|
|
|
|
|
143639
|
$parser->next_token( $parser->tokenize() ); |
712
|
|
|
|
|
|
|
|
713
|
61143
|
|
|
|
|
96847
|
my($arity, $id) = @{$t}; |
|
61143
|
|
|
|
|
153597
|
|
714
|
61143
|
100
|
100
|
|
|
190231
|
if( $arity eq "name" && $parser->next_token_is("=>") ) { |
715
|
63
|
|
|
|
|
111
|
$arity = "literal"; |
716
|
|
|
|
|
|
|
} |
717
|
|
|
|
|
|
|
|
718
|
61143
|
|
|
|
|
83296
|
print STDOUT "[$arity => $id] #$line\n" if _DUMP_TOKEN; |
719
|
|
|
|
|
|
|
|
720
|
61143
|
|
|
|
|
80323
|
my $symbol; |
721
|
61143
|
100
|
|
|
|
154175
|
if($arity eq "literal") { |
|
|
100
|
|
|
|
|
|
722
|
17653
|
|
|
|
|
40238
|
$symbol = $parser->symbol('(literal)')->clone( |
723
|
|
|
|
|
|
|
id => $id, |
724
|
|
|
|
|
|
|
value => $parser->parse_literal($id) |
725
|
|
|
|
|
|
|
); |
726
|
|
|
|
|
|
|
} |
727
|
|
|
|
|
|
|
elsif($arity eq "operator") { |
728
|
24114
|
|
|
|
|
47024
|
$symbol = $stash->{$id}; |
729
|
24114
|
100
|
|
|
|
54368
|
if(not defined $symbol) { |
730
|
3
|
|
|
|
|
13
|
$parser->_error("Unknown operator '$id'"); |
731
|
|
|
|
|
|
|
} |
732
|
24111
|
|
|
|
|
71742
|
$symbol = $symbol->clone( |
733
|
|
|
|
|
|
|
arity => $arity, # to make error messages clearer |
734
|
|
|
|
|
|
|
); |
735
|
|
|
|
|
|
|
} |
736
|
|
|
|
|
|
|
else { # name |
737
|
|
|
|
|
|
|
# find_or_create() returns a cloned symbol, |
738
|
|
|
|
|
|
|
# so there's not need to clone() here |
739
|
19376
|
|
|
|
|
46502
|
$symbol = $parser->find_or_create($id); |
740
|
|
|
|
|
|
|
} |
741
|
|
|
|
|
|
|
|
742
|
61140
|
|
|
|
|
863923
|
$symbol->line($line); |
743
|
61140
|
|
|
|
|
276667
|
return $parser->token($symbol); |
744
|
|
|
|
|
|
|
} |
745
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
sub parse_literal { |
747
|
17653
|
|
|
17653
|
0
|
29080
|
my($parser, $literal) = @_; |
748
|
17653
|
|
|
|
|
50407
|
return literal_to_value($literal); |
749
|
|
|
|
|
|
|
} |
750
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
sub nud_name { |
752
|
341
|
|
|
341
|
0
|
548
|
my($parser, $symbol) = @_; |
753
|
341
|
|
|
|
|
946
|
return $symbol->clone( |
754
|
|
|
|
|
|
|
arity => 'name', |
755
|
|
|
|
|
|
|
); |
756
|
|
|
|
|
|
|
} |
757
|
|
|
|
|
|
|
sub nud_variable { |
758
|
2774
|
|
|
2774
|
0
|
5845
|
my($parser, $symbol) = @_; |
759
|
2774
|
|
|
|
|
8684
|
return $symbol->clone( |
760
|
|
|
|
|
|
|
arity => 'variable', |
761
|
|
|
|
|
|
|
); |
762
|
|
|
|
|
|
|
} |
763
|
|
|
|
|
|
|
sub nud_literal { |
764
|
17632
|
|
|
17632
|
0
|
27182
|
my($parser, $symbol) = @_; |
765
|
17632
|
|
|
|
|
46009
|
return $symbol->clone( |
766
|
|
|
|
|
|
|
arity => 'literal', |
767
|
|
|
|
|
|
|
); |
768
|
|
|
|
|
|
|
} |
769
|
|
|
|
|
|
|
|
770
|
|
|
|
|
|
|
sub default_nud { |
771
|
377
|
|
|
377
|
0
|
547
|
my($parser, $symbol) = @_; |
772
|
377
|
|
|
|
|
1006
|
return $symbol->clone(); # as is |
773
|
|
|
|
|
|
|
} |
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
sub default_led { |
776
|
0
|
|
|
0
|
0
|
0
|
my($parser, $symbol) = @_; |
777
|
0
|
|
|
|
|
0
|
$parser->near_token($parser->token); |
778
|
0
|
|
|
|
|
0
|
$parser->_error( |
779
|
|
|
|
|
|
|
sprintf 'Missing operator (%s): %s', |
780
|
|
|
|
|
|
|
$symbol->arity, $symbol->id); |
781
|
|
|
|
|
|
|
} |
782
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
sub default_std { |
784
|
0
|
|
|
0
|
0
|
0
|
my($parser, $symbol) = @_; |
785
|
0
|
|
|
|
|
0
|
$parser->near_token($parser->token); |
786
|
0
|
|
|
|
|
0
|
$parser->_error( |
787
|
|
|
|
|
|
|
sprintf 'Not a statement (%s): %s', |
788
|
|
|
|
|
|
|
$symbol->arity, $symbol->id); |
789
|
|
|
|
|
|
|
} |
790
|
|
|
|
|
|
|
|
791
|
|
|
|
|
|
|
sub expression { |
792
|
21883
|
|
|
21883
|
0
|
35308
|
my($parser, $rbp) = @_; |
793
|
|
|
|
|
|
|
|
794
|
21883
|
|
|
|
|
47638
|
my $t = $parser->token; |
795
|
|
|
|
|
|
|
|
796
|
21883
|
|
|
|
|
46353
|
$parser->advance(); |
797
|
|
|
|
|
|
|
|
798
|
21883
|
|
|
|
|
64374
|
my $left = $t->nud($parser); |
799
|
|
|
|
|
|
|
|
800
|
21868
|
|
|
|
|
520658
|
while($rbp < $parser->token->lbp) { |
801
|
1923
|
|
|
|
|
9694
|
$t = $parser->token; |
802
|
1923
|
|
|
|
|
5101
|
$parser->advance(); |
803
|
1923
|
|
|
|
|
5717
|
$left = $t->led($parser, $left); |
804
|
|
|
|
|
|
|
} |
805
|
|
|
|
|
|
|
|
806
|
21850
|
|
|
|
|
88589
|
return $left; |
807
|
|
|
|
|
|
|
} |
808
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
sub expression_list { |
810
|
12211
|
|
|
12211
|
0
|
21176
|
my($parser) = @_; |
811
|
12211
|
|
|
|
|
18469
|
my @list; |
812
|
12211
|
|
|
|
|
19069
|
while(1) { |
813
|
17521
|
100
|
|
|
|
68774
|
if($parser->token->is_value) { |
814
|
17273
|
|
|
|
|
39008
|
push @list, $parser->expression(0); |
815
|
|
|
|
|
|
|
} |
816
|
|
|
|
|
|
|
|
817
|
17521
|
100
|
|
|
|
72067
|
if(!$parser->token->is_comma) { |
818
|
12211
|
|
|
|
|
27474
|
last; |
819
|
|
|
|
|
|
|
} |
820
|
|
|
|
|
|
|
|
821
|
5310
|
|
|
|
|
9534
|
$parser->advance(); # comma |
822
|
|
|
|
|
|
|
} |
823
|
12211
|
|
|
|
|
39446
|
return \@list; |
824
|
|
|
|
|
|
|
} |
825
|
|
|
|
|
|
|
|
826
|
|
|
|
|
|
|
# for left associative infix operators |
827
|
|
|
|
|
|
|
sub led_infix { |
828
|
863
|
|
|
863
|
0
|
1353
|
my($parser, $symbol, $left) = @_; |
829
|
863
|
|
|
|
|
2799
|
return $parser->binary( $symbol, $left, $parser->expression($symbol->lbp) ); |
830
|
|
|
|
|
|
|
} |
831
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
sub infix { |
833
|
6989
|
|
|
6989
|
0
|
59473
|
my($parser, $id, $bp, $led) = @_; |
834
|
|
|
|
|
|
|
|
835
|
6989
|
|
|
|
|
60403
|
my $symbol = $parser->symbol($id, $bp); |
836
|
6989
|
|
100
|
|
|
79911
|
$symbol->set_led($led || \&led_infix); |
837
|
6989
|
|
|
|
|
109350
|
return $symbol; |
838
|
|
|
|
|
|
|
} |
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
# for right associative infix operators |
841
|
|
|
|
|
|
|
sub led_infixr { |
842
|
26
|
|
|
26
|
0
|
48
|
my($parser, $symbol, $left) = @_; |
843
|
26
|
|
|
|
|
113
|
return $parser->binary( $symbol, $left, $parser->expression($symbol->lbp - 1) ); |
844
|
|
|
|
|
|
|
} |
845
|
|
|
|
|
|
|
|
846
|
|
|
|
|
|
|
sub infixr { |
847
|
241
|
|
|
241
|
0
|
2516
|
my($parser, $id, $bp, $led) = @_; |
848
|
|
|
|
|
|
|
|
849
|
241
|
|
|
|
|
4517
|
my $symbol = $parser->symbol($id, $bp); |
850
|
241
|
|
50
|
|
|
3322
|
$symbol->set_led($led || \&led_infixr); |
851
|
241
|
|
|
|
|
4045
|
return $symbol; |
852
|
|
|
|
|
|
|
} |
853
|
|
|
|
|
|
|
|
854
|
|
|
|
|
|
|
# for prefix operators |
855
|
|
|
|
|
|
|
sub prefix { |
856
|
1928
|
|
|
1928
|
0
|
16500
|
my($parser, $id, $bp, $nud) = @_; |
857
|
|
|
|
|
|
|
|
858
|
1928
|
|
|
|
|
16991
|
my $symbol = $parser->symbol($id); |
859
|
1928
|
|
|
|
|
18980
|
$symbol->ubp($bp); |
860
|
1928
|
|
100
|
|
|
39238
|
$symbol->set_nud($nud || \&nud_prefix); |
861
|
1928
|
|
|
|
|
30340
|
return $symbol; |
862
|
|
|
|
|
|
|
} |
863
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
sub nud_prefix { |
865
|
51
|
|
|
51
|
0
|
88
|
my($parser, $symbol) = @_; |
866
|
51
|
|
|
|
|
186
|
my $un = $symbol->clone(arity => 'unary'); |
867
|
51
|
|
|
|
|
1110
|
$parser->reserve($un); |
868
|
51
|
|
|
|
|
190
|
$un->first($parser->expression($symbol->ubp)); |
869
|
50
|
|
|
|
|
115
|
return $un; |
870
|
|
|
|
|
|
|
} |
871
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
sub led_assignment { |
873
|
11
|
|
|
11
|
0
|
18
|
my($parser, $symbol, $left) = @_; |
874
|
|
|
|
|
|
|
|
875
|
11
|
|
|
|
|
49
|
$parser->_error("Assignment ($symbol) is forbidden", $left); |
876
|
|
|
|
|
|
|
} |
877
|
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
sub assignment { |
879
|
1820
|
|
|
1820
|
0
|
20428
|
my($parser, $id, $bp) = @_; |
880
|
|
|
|
|
|
|
|
881
|
1820
|
|
|
|
|
22268
|
$parser->symbol($id, $bp)->set_led(\&led_assignment); |
882
|
1820
|
|
|
|
|
35085
|
return; |
883
|
|
|
|
|
|
|
} |
884
|
|
|
|
|
|
|
|
885
|
|
|
|
|
|
|
# the ternary is a right associative operator |
886
|
|
|
|
|
|
|
sub led_ternary { |
887
|
115
|
|
|
115
|
0
|
193
|
my($parser, $symbol, $left) = @_; |
888
|
|
|
|
|
|
|
|
889
|
115
|
|
|
|
|
312
|
my $if = $symbol->clone(arity => 'if'); |
890
|
|
|
|
|
|
|
|
891
|
115
|
|
|
|
|
2373
|
$if->first($left); |
892
|
115
|
|
|
|
|
422
|
$if->second([$parser->expression( $symbol->lbp - 1 )]); |
893
|
113
|
|
|
|
|
284
|
$parser->advance(":"); |
894
|
113
|
|
|
|
|
434
|
$if->third([$parser->expression( $symbol->lbp - 1 )]); |
895
|
113
|
|
|
|
|
608
|
return $if; |
896
|
|
|
|
|
|
|
} |
897
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
sub is_valid_field { |
899
|
447
|
|
|
447
|
0
|
614
|
my($parser, $token) = @_; |
900
|
447
|
|
|
|
|
1186
|
my $arity = $token->arity; |
901
|
|
|
|
|
|
|
|
902
|
447
|
100
|
|
|
|
1099
|
if($arity eq "name") { |
|
|
100
|
|
|
|
|
|
903
|
409
|
|
|
|
|
1579
|
return 1; |
904
|
|
|
|
|
|
|
} |
905
|
|
|
|
|
|
|
elsif($arity eq "literal") { |
906
|
9
|
|
|
|
|
45
|
return is_int($token->id); |
907
|
|
|
|
|
|
|
} |
908
|
29
|
|
|
|
|
288
|
return 0; |
909
|
|
|
|
|
|
|
} |
910
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
sub led_dot { |
912
|
447
|
|
|
447
|
0
|
715
|
my($parser, $symbol, $left) = @_; |
913
|
|
|
|
|
|
|
|
914
|
447
|
|
|
|
|
1042
|
my $t = $parser->token; |
915
|
447
|
50
|
|
|
|
1203
|
if(!$parser->is_valid_field($t)) { |
916
|
0
|
|
|
|
|
0
|
$parser->_unexpected("a field name", $t); |
917
|
|
|
|
|
|
|
} |
918
|
|
|
|
|
|
|
|
919
|
447
|
|
|
|
|
1467
|
my $dot = $symbol->clone( |
920
|
|
|
|
|
|
|
arity => "field", |
921
|
|
|
|
|
|
|
first => $left, |
922
|
|
|
|
|
|
|
second => $t->clone(arity => 'literal'), |
923
|
|
|
|
|
|
|
); |
924
|
|
|
|
|
|
|
|
925
|
447
|
|
|
|
|
9635
|
$t = $parser->advance(); |
926
|
447
|
100
|
|
|
|
1797
|
if($t->id eq "(") { |
927
|
230
|
|
|
|
|
638
|
$parser->advance(); # "(" |
928
|
230
|
|
|
|
|
564
|
$dot->third( $parser->expression_list() ); |
929
|
230
|
|
|
|
|
561
|
$parser->advance(")"); |
930
|
230
|
|
|
|
|
652
|
$dot->arity("methodcall"); |
931
|
|
|
|
|
|
|
} |
932
|
|
|
|
|
|
|
|
933
|
447
|
|
|
|
|
3308
|
return $dot; |
934
|
|
|
|
|
|
|
} |
935
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
sub led_fetch { # $h[$field] |
937
|
90
|
|
|
90
|
0
|
150
|
my($parser, $symbol, $left) = @_; |
938
|
|
|
|
|
|
|
|
939
|
90
|
|
|
|
|
276
|
my $fetch = $symbol->clone( |
940
|
|
|
|
|
|
|
arity => "field", |
941
|
|
|
|
|
|
|
first => $left, |
942
|
|
|
|
|
|
|
second => $parser->expression(0), |
943
|
|
|
|
|
|
|
); |
944
|
90
|
|
|
|
|
2121
|
$parser->advance("]"); |
945
|
90
|
|
|
|
|
498
|
return $fetch; |
946
|
|
|
|
|
|
|
} |
947
|
|
|
|
|
|
|
|
948
|
|
|
|
|
|
|
sub call { |
949
|
193
|
|
|
193
|
0
|
409
|
my($parser, $function, @args) = @_; |
950
|
193
|
100
|
|
|
|
629
|
if(not ref $function) { |
951
|
4
|
|
|
|
|
11
|
$function = $parser->symbol('(name)')->clone( |
952
|
|
|
|
|
|
|
arity => 'name', |
953
|
|
|
|
|
|
|
id => $function, |
954
|
|
|
|
|
|
|
line => $parser->line, |
955
|
|
|
|
|
|
|
); |
956
|
|
|
|
|
|
|
} |
957
|
|
|
|
|
|
|
|
958
|
193
|
|
|
|
|
566
|
return $parser->symbol('(call)')->clone( |
959
|
|
|
|
|
|
|
arity => 'call', |
960
|
|
|
|
|
|
|
first => $function, |
961
|
|
|
|
|
|
|
second => \@args, |
962
|
|
|
|
|
|
|
); |
963
|
|
|
|
|
|
|
} |
964
|
|
|
|
|
|
|
|
965
|
|
|
|
|
|
|
sub led_call { |
966
|
295
|
|
|
295
|
0
|
521
|
my($parser, $symbol, $left) = @_; |
967
|
|
|
|
|
|
|
|
968
|
295
|
|
|
|
|
816
|
my $call = $symbol->clone(arity => 'call'); |
969
|
295
|
|
|
|
|
5992
|
$call->first($left); |
970
|
295
|
|
|
|
|
776
|
$call->second( $parser->expression_list() ); |
971
|
295
|
|
|
|
|
704
|
$parser->advance(")"); |
972
|
|
|
|
|
|
|
|
973
|
294
|
|
|
|
|
1578
|
return $call; |
974
|
|
|
|
|
|
|
} |
975
|
|
|
|
|
|
|
|
976
|
|
|
|
|
|
|
sub led_pipe { # filter |
977
|
64
|
|
|
64
|
0
|
123
|
my($parser, $symbol, $left) = @_; |
978
|
|
|
|
|
|
|
# a | b -> b(a) |
979
|
64
|
|
|
|
|
239
|
return $parser->call($parser->expression($symbol->lbp), $left); |
980
|
|
|
|
|
|
|
} |
981
|
|
|
|
|
|
|
|
982
|
|
|
|
|
|
|
sub led_range { # x .. y |
983
|
7
|
|
|
7
|
0
|
12
|
my($parser, $symbol, $left) = @_; |
984
|
7
|
|
|
|
|
19
|
return $symbol->clone( |
985
|
|
|
|
|
|
|
arity => 'range', |
986
|
|
|
|
|
|
|
first => $left, |
987
|
|
|
|
|
|
|
second => $parser->expression(0), |
988
|
|
|
|
|
|
|
); |
989
|
|
|
|
|
|
|
} |
990
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
sub nil { |
992
|
26
|
|
|
26
|
0
|
113
|
my($parser) = @_; |
993
|
26
|
|
|
|
|
71
|
return $parser->symbol('nil')->nud($parser); |
994
|
|
|
|
|
|
|
} |
995
|
|
|
|
|
|
|
|
996
|
|
|
|
|
|
|
sub nud_defined { |
997
|
23
|
|
|
23
|
0
|
39
|
my($parser, $symbol) = @_; |
998
|
23
|
|
|
|
|
65
|
$parser->reserve( $symbol->clone() ); |
999
|
|
|
|
|
|
|
# prefix: is a syntactic sugar to $a != nil |
1000
|
23
|
|
|
|
|
111
|
return $parser->binary( |
1001
|
|
|
|
|
|
|
'!=', |
1002
|
|
|
|
|
|
|
$parser->expression($symbol->ubp), |
1003
|
|
|
|
|
|
|
$parser->nil, |
1004
|
|
|
|
|
|
|
); |
1005
|
|
|
|
|
|
|
} |
1006
|
|
|
|
|
|
|
|
1007
|
|
|
|
|
|
|
# for special literals (e.g. nil, true, false) |
1008
|
|
|
|
|
|
|
sub nud_special { |
1009
|
170
|
|
|
170
|
0
|
270
|
my($parser, $symbol) = @_; |
1010
|
170
|
|
|
|
|
640
|
return $symbol->first; |
1011
|
|
|
|
|
|
|
} |
1012
|
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
sub define_literal { # special literals |
1014
|
723
|
|
|
723
|
0
|
6160
|
my($parser, $id, $value) = @_; |
1015
|
|
|
|
|
|
|
|
1016
|
723
|
|
|
|
|
6358
|
my $symbol = $parser->symbol($id); |
1017
|
723
|
100
|
|
|
|
8468
|
$symbol->first( $symbol->clone( |
1018
|
|
|
|
|
|
|
arity => defined($value) ? 'literal' : 'nil', |
1019
|
|
|
|
|
|
|
value => $value, |
1020
|
|
|
|
|
|
|
) ); |
1021
|
723
|
|
|
|
|
14443
|
$symbol->set_nud(\&nud_special); |
1022
|
723
|
|
|
|
|
7136
|
$symbol->is_defined(1); |
1023
|
723
|
|
|
|
|
10882
|
return $symbol; |
1024
|
|
|
|
|
|
|
} |
1025
|
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
sub new_scope { |
1027
|
918
|
|
|
918
|
0
|
1296
|
my($parser) = @_; |
1028
|
918
|
|
|
|
|
1194
|
push @{ $parser->scope }, {}; |
|
918
|
|
|
|
|
2865
|
|
1029
|
918
|
|
|
|
|
1718
|
return; |
1030
|
|
|
|
|
|
|
} |
1031
|
|
|
|
|
|
|
|
1032
|
|
|
|
|
|
|
sub pop_scope { |
1033
|
901
|
|
|
901
|
0
|
1355
|
my($parser) = @_; |
1034
|
901
|
|
|
|
|
1112
|
pop @{ $parser->scope }; |
|
901
|
|
|
|
|
2319
|
|
1035
|
901
|
|
|
|
|
2596
|
return; |
1036
|
|
|
|
|
|
|
} |
1037
|
|
|
|
|
|
|
|
1038
|
|
|
|
|
|
|
sub undefined_name { |
1039
|
3615
|
|
|
3615
|
0
|
7844
|
my($parser, $name) = @_; |
1040
|
3615
|
100
|
|
|
|
12770
|
if($name =~ /\A \$/xms) { |
1041
|
2844
|
|
|
|
|
13884
|
return $parser->symbol_table->{'(variable)'}->clone( |
1042
|
|
|
|
|
|
|
id => $name, |
1043
|
|
|
|
|
|
|
); |
1044
|
|
|
|
|
|
|
} |
1045
|
|
|
|
|
|
|
else { |
1046
|
771
|
|
|
|
|
3397
|
return $parser->symbol_table->{'(name)'}->clone( |
1047
|
|
|
|
|
|
|
id => $name, |
1048
|
|
|
|
|
|
|
); |
1049
|
|
|
|
|
|
|
} |
1050
|
|
|
|
|
|
|
} |
1051
|
|
|
|
|
|
|
|
1052
|
|
|
|
|
|
|
sub find_or_create { # find a name from all the scopes |
1053
|
19435
|
|
|
19435
|
0
|
35838
|
my($parser, $name) = @_; |
1054
|
19435
|
|
|
|
|
28352
|
my $s; |
1055
|
19435
|
|
|
|
|
29529
|
foreach my $scope(reverse @{$parser->scope}){ |
|
19435
|
|
|
|
|
68095
|
|
1056
|
22628
|
|
|
|
|
42717
|
$s = $scope->{$name}; |
1057
|
22628
|
100
|
|
|
|
64415
|
if(defined $s) { |
1058
|
6016
|
|
|
|
|
18196
|
return $s->clone(); |
1059
|
|
|
|
|
|
|
} |
1060
|
|
|
|
|
|
|
} |
1061
|
13419
|
|
|
|
|
43746
|
$s = $parser->symbol_table->{$name}; |
1062
|
13419
|
100
|
|
|
|
47881
|
return defined($s) ? $s : $parser->undefined_name($name); |
1063
|
|
|
|
|
|
|
} |
1064
|
|
|
|
|
|
|
|
1065
|
|
|
|
|
|
|
sub reserve { # reserve a name to the scope |
1066
|
13814
|
|
|
13814
|
0
|
24199
|
my($parser, $symbol) = @_; |
1067
|
13814
|
100
|
100
|
|
|
93722
|
if($symbol->arity ne 'name' or $symbol->is_reserved) { |
1068
|
13243
|
|
|
|
|
24059
|
return $symbol; |
1069
|
|
|
|
|
|
|
} |
1070
|
|
|
|
|
|
|
|
1071
|
571
|
|
|
|
|
5068
|
my $top = $parser->scope->[-1]; |
1072
|
571
|
|
|
|
|
5050
|
my $t = $top->{$symbol->id}; |
1073
|
571
|
50
|
|
|
|
4854
|
if($t) { |
1074
|
0
|
0
|
|
|
|
0
|
if($t->is_reserved) { |
1075
|
0
|
|
|
|
|
0
|
return $symbol; |
1076
|
|
|
|
|
|
|
} |
1077
|
0
|
0
|
|
|
|
0
|
if($t->arity eq "name") { |
1078
|
0
|
|
|
|
|
0
|
$parser->_error("Already defined: $symbol"); |
1079
|
|
|
|
|
|
|
} |
1080
|
|
|
|
|
|
|
} |
1081
|
571
|
|
|
|
|
5123
|
$top->{$symbol->id} = $symbol; |
1082
|
571
|
|
|
|
|
5231
|
$symbol->is_reserved(1); |
1083
|
|
|
|
|
|
|
#$symbol->scope($top); |
1084
|
571
|
|
|
|
|
7668
|
return $symbol; |
1085
|
|
|
|
|
|
|
} |
1086
|
|
|
|
|
|
|
|
1087
|
|
|
|
|
|
|
sub define { # define a name to the scope |
1088
|
595
|
|
|
595
|
0
|
952
|
my($parser, $symbol) = @_; |
1089
|
595
|
|
|
|
|
1508
|
my $top = $parser->scope->[-1]; |
1090
|
|
|
|
|
|
|
|
1091
|
595
|
|
|
|
|
1572
|
my $t = $top->{$symbol->id}; |
1092
|
595
|
100
|
|
|
|
1352
|
if(defined $t) { |
1093
|
1
|
50
|
|
|
|
6
|
$parser->_error($t->is_reserved ? "Already is_reserved: $t" : "Already defined: $t"); |
1094
|
|
|
|
|
|
|
} |
1095
|
|
|
|
|
|
|
|
1096
|
594
|
|
|
|
|
1866
|
$top->{$symbol->id} = $symbol; |
1097
|
|
|
|
|
|
|
|
1098
|
594
|
|
|
|
|
1586
|
$symbol->is_defined(1); |
1099
|
594
|
|
|
|
|
1760
|
$symbol->is_reserved(0); |
1100
|
594
|
|
|
|
|
1591
|
$symbol->remove_nud(); |
1101
|
594
|
|
|
|
|
1357
|
$symbol->remove_led(); |
1102
|
594
|
|
|
|
|
1223
|
$symbol->remove_std(); |
1103
|
594
|
|
|
|
|
1303
|
$symbol->lbp(0); |
1104
|
|
|
|
|
|
|
#$symbol->scope($top); |
1105
|
594
|
|
|
|
|
1163
|
return $symbol; |
1106
|
|
|
|
|
|
|
} |
1107
|
|
|
|
|
|
|
|
1108
|
|
|
|
|
|
|
sub print { |
1109
|
1223
|
|
|
1223
|
0
|
2522
|
my($parser, @args) = @_; |
1110
|
1223
|
|
|
|
|
2893
|
return $parser->symbol('print')->clone( |
1111
|
|
|
|
|
|
|
arity => 'print', |
1112
|
|
|
|
|
|
|
first => \@args, |
1113
|
|
|
|
|
|
|
line => $parser->line, |
1114
|
|
|
|
|
|
|
); |
1115
|
|
|
|
|
|
|
} |
1116
|
|
|
|
|
|
|
|
1117
|
|
|
|
|
|
|
sub binary { |
1118
|
994
|
|
|
994
|
0
|
1910
|
my($parser, $symbol, $lhs, $rhs) = @_; |
1119
|
994
|
100
|
|
|
|
2392
|
if(!ref $symbol) { |
1120
|
|
|
|
|
|
|
# operator |
1121
|
109
|
|
|
|
|
239
|
$symbol = $parser->symbol($symbol); |
1122
|
|
|
|
|
|
|
} |
1123
|
994
|
50
|
|
|
|
2130
|
if(!ref $lhs) { |
1124
|
|
|
|
|
|
|
# literal |
1125
|
0
|
|
|
|
|
0
|
$lhs = $parser->symbol('(literal)')->clone( |
1126
|
|
|
|
|
|
|
id => $lhs, |
1127
|
|
|
|
|
|
|
); |
1128
|
|
|
|
|
|
|
} |
1129
|
994
|
100
|
|
|
|
2079
|
if(!ref $rhs) { |
1130
|
|
|
|
|
|
|
# literal |
1131
|
39
|
|
|
|
|
81
|
$rhs = $parser->symbol('(literal)')->clone( |
1132
|
|
|
|
|
|
|
id => $rhs, |
1133
|
|
|
|
|
|
|
); |
1134
|
|
|
|
|
|
|
} |
1135
|
994
|
|
|
|
|
3663
|
return $symbol->clone( |
1136
|
|
|
|
|
|
|
arity => 'binary', |
1137
|
|
|
|
|
|
|
first => $lhs, |
1138
|
|
|
|
|
|
|
second => $rhs, |
1139
|
|
|
|
|
|
|
); |
1140
|
|
|
|
|
|
|
} |
1141
|
|
|
|
|
|
|
|
1142
|
|
|
|
|
|
|
sub define_function { |
1143
|
497
|
|
|
497
|
0
|
12545
|
my($parser, @names) = @_; |
1144
|
|
|
|
|
|
|
|
1145
|
497
|
|
|
|
|
2785
|
foreach my $name(@names) { |
1146
|
6035
|
|
|
|
|
52398
|
my $s = $parser->symbol($name); |
1147
|
6035
|
|
|
|
|
109448
|
$s->set_nud(\&nud_name); |
1148
|
6035
|
|
|
|
|
59709
|
$s->is_defined(1); |
1149
|
|
|
|
|
|
|
} |
1150
|
497
|
|
|
|
|
6669
|
return; |
1151
|
|
|
|
|
|
|
} |
1152
|
|
|
|
|
|
|
|
1153
|
|
|
|
|
|
|
sub finish_statement { |
1154
|
14063
|
|
|
14063
|
0
|
25918
|
my($parser, $expr) = @_; |
1155
|
|
|
|
|
|
|
|
1156
|
14063
|
|
|
|
|
35521
|
my $t = $parser->token; |
1157
|
14063
|
100
|
|
|
|
45196
|
if($t->can_be_modifier) { |
1158
|
30
|
|
|
|
|
65
|
$parser->advance(); |
1159
|
30
|
|
|
|
|
93
|
$expr = $t->std($parser, $expr); |
1160
|
30
|
|
|
|
|
74
|
$t = $parser->token; |
1161
|
|
|
|
|
|
|
} |
1162
|
|
|
|
|
|
|
|
1163
|
14063
|
100
|
100
|
|
|
103071
|
if($t->is_block_end or $parser->statement_is_finished) { |
|
|
100
|
|
|
|
|
|
1164
|
|
|
|
|
|
|
# noop |
1165
|
|
|
|
|
|
|
} |
1166
|
|
|
|
|
|
|
elsif($t->id eq ";") { |
1167
|
12614
|
|
|
|
|
30384
|
$parser->advance(); |
1168
|
|
|
|
|
|
|
} |
1169
|
|
|
|
|
|
|
else { |
1170
|
4
|
|
|
|
|
28
|
$parser->_unexpected("a semicolon or block end", $t); |
1171
|
|
|
|
|
|
|
} |
1172
|
14057
|
|
|
|
|
133195
|
return $expr; |
1173
|
|
|
|
|
|
|
} |
1174
|
|
|
|
|
|
|
|
1175
|
|
|
|
|
|
|
sub statement { # process one or more statements |
1176
|
15326
|
|
|
15326
|
0
|
25020
|
my($parser) = @_; |
1177
|
15326
|
|
|
|
|
36446
|
my $t = $parser->token; |
1178
|
|
|
|
|
|
|
|
1179
|
15326
|
100
|
|
|
|
51248
|
if($t->id eq ";"){ |
1180
|
448
|
|
|
|
|
1003
|
$parser->advance(); # ";" |
1181
|
448
|
|
|
|
|
2293
|
return; |
1182
|
|
|
|
|
|
|
} |
1183
|
|
|
|
|
|
|
|
1184
|
14878
|
100
|
|
|
|
48615
|
if($t->has_std) { # is $t a statement? |
1185
|
13652
|
|
|
|
|
32253
|
$parser->reserve($t); |
1186
|
13652
|
|
|
|
|
32060
|
$parser->advance(); |
1187
|
|
|
|
|
|
|
|
1188
|
|
|
|
|
|
|
# std() can return a list of nodes |
1189
|
13652
|
|
|
|
|
43115
|
return $t->std($parser); |
1190
|
|
|
|
|
|
|
} |
1191
|
|
|
|
|
|
|
|
1192
|
1226
|
|
|
|
|
2965
|
my $expr = $parser->auto_command( $parser->expression(0) ); |
1193
|
1200
|
|
|
|
|
25310
|
return $parser->finish_statement($expr); |
1194
|
|
|
|
|
|
|
} |
1195
|
|
|
|
|
|
|
|
1196
|
|
|
|
|
|
|
sub auto_command { |
1197
|
1200
|
|
|
1200
|
0
|
1897
|
my($parser, $expr) = @_; |
1198
|
1200
|
100
|
|
|
|
3702
|
if($expr->is_statement) { |
1199
|
|
|
|
|
|
|
# expressions can produce pure statements (e.g. assignment ) |
1200
|
81
|
|
|
|
|
164
|
return $expr; |
1201
|
|
|
|
|
|
|
} |
1202
|
|
|
|
|
|
|
else { |
1203
|
1119
|
|
|
|
|
3032
|
return $parser->print($expr); |
1204
|
|
|
|
|
|
|
} |
1205
|
|
|
|
|
|
|
} |
1206
|
|
|
|
|
|
|
|
1207
|
|
|
|
|
|
|
sub statements { # process statements |
1208
|
4316
|
|
|
4316
|
0
|
8117
|
my($parser) = @_; |
1209
|
4316
|
|
|
|
|
7347
|
my @a; |
1210
|
|
|
|
|
|
|
|
1211
|
4316
|
|
|
|
|
21763
|
for(my $t = $parser->token; !$t->is_block_end; $t = $parser->token) { |
1212
|
15276
|
|
|
|
|
40331
|
push @a, $parser->statement(); |
1213
|
|
|
|
|
|
|
} |
1214
|
|
|
|
|
|
|
|
1215
|
4265
|
|
|
|
|
14087
|
return \@a; |
1216
|
|
|
|
|
|
|
} |
1217
|
|
|
|
|
|
|
|
1218
|
|
|
|
|
|
|
sub block { |
1219
|
212
|
|
|
212
|
0
|
307
|
my($parser) = @_; |
1220
|
212
|
|
|
|
|
519
|
$parser->new_scope(); |
1221
|
212
|
|
|
|
|
451
|
$parser->advance("{"); |
1222
|
212
|
|
|
|
|
476
|
my $a = $parser->statements(); |
1223
|
212
|
|
|
|
|
499
|
$parser->advance("}"); |
1224
|
211
|
|
|
|
|
506
|
$parser->pop_scope(); |
1225
|
211
|
|
|
|
|
642
|
return $a; |
1226
|
|
|
|
|
|
|
} |
1227
|
|
|
|
|
|
|
|
1228
|
|
|
|
|
|
|
sub nud_paren { |
1229
|
121
|
|
|
121
|
0
|
213
|
my($parser, $symbol) = @_; |
1230
|
121
|
|
|
|
|
321
|
my $expr = $parser->expression(0); |
1231
|
121
|
|
|
|
|
446
|
$parser->advance( $symbol->counterpart ); |
1232
|
121
|
|
|
|
|
275
|
return $expr; |
1233
|
|
|
|
|
|
|
} |
1234
|
|
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
# for object literals |
1236
|
|
|
|
|
|
|
sub nud_brace { |
1237
|
141
|
|
|
141
|
0
|
286
|
my($parser, $symbol) = @_; |
1238
|
|
|
|
|
|
|
|
1239
|
141
|
|
|
|
|
407
|
my $list = $parser->expression_list(); |
1240
|
|
|
|
|
|
|
|
1241
|
141
|
|
|
|
|
543
|
$parser->advance($symbol->counterpart); |
1242
|
141
|
|
|
|
|
463
|
return $symbol->clone( |
1243
|
|
|
|
|
|
|
arity => 'composer', |
1244
|
|
|
|
|
|
|
first => $list, |
1245
|
|
|
|
|
|
|
); |
1246
|
|
|
|
|
|
|
} |
1247
|
|
|
|
|
|
|
|
1248
|
|
|
|
|
|
|
# iterator variables ($~iterator) |
1249
|
|
|
|
|
|
|
# $~iterator . NAME | NAME() |
1250
|
|
|
|
|
|
|
sub nud_iterator { |
1251
|
55
|
|
|
55
|
0
|
89
|
my($parser, $symbol) = @_; |
1252
|
|
|
|
|
|
|
|
1253
|
55
|
|
|
|
|
136
|
my $iterator = $symbol->clone(); |
1254
|
55
|
100
|
|
|
|
1047
|
if($parser->token->id eq ".") { |
1255
|
51
|
|
|
|
|
105
|
$parser->advance(); |
1256
|
|
|
|
|
|
|
|
1257
|
51
|
|
|
|
|
114
|
my $t = $parser->token; |
1258
|
51
|
50
|
|
|
|
214
|
if(!any_in($t->arity, qw(variable name))) { |
1259
|
0
|
|
|
|
|
0
|
$parser->_unexpected("a field name", $t); |
1260
|
|
|
|
|
|
|
} |
1261
|
|
|
|
|
|
|
|
1262
|
51
|
|
|
|
|
393
|
my $generator = $parser->iterator_element->{$t->value}; |
1263
|
51
|
50
|
|
|
|
123
|
if(!$generator) { |
1264
|
0
|
|
|
|
|
0
|
$parser->_error("Undefined iterator element: $t"); |
1265
|
|
|
|
|
|
|
} |
1266
|
|
|
|
|
|
|
|
1267
|
51
|
|
|
|
|
101
|
$parser->advance(); # element name |
1268
|
|
|
|
|
|
|
|
1269
|
51
|
|
|
|
|
57
|
my $args; |
1270
|
51
|
100
|
|
|
|
245
|
if($parser->token->id eq "(") { |
1271
|
15
|
|
|
|
|
31
|
$parser->advance(); |
1272
|
15
|
|
|
|
|
36
|
$args = $parser->expression_list(); |
1273
|
15
|
|
|
|
|
36
|
$parser->advance(")"); |
1274
|
|
|
|
|
|
|
} |
1275
|
|
|
|
|
|
|
|
1276
|
51
|
|
|
|
|
128
|
$iterator->second($t); |
1277
|
51
|
|
|
|
|
62
|
return $generator->($parser, $iterator, @{$args}); |
|
51
|
|
|
|
|
157
|
|
1278
|
|
|
|
|
|
|
} |
1279
|
4
|
|
|
|
|
11
|
return $iterator; |
1280
|
|
|
|
|
|
|
} |
1281
|
|
|
|
|
|
|
|
1282
|
|
|
|
|
|
|
sub nud_constant { |
1283
|
70
|
|
|
70
|
0
|
111
|
my($parser, $symbol) = @_; |
1284
|
70
|
|
|
|
|
178
|
my $t = $parser->token; |
1285
|
|
|
|
|
|
|
|
1286
|
70
|
50
|
|
|
|
352
|
my $expect = $symbol->id eq 'constant' ? 'name' |
|
|
100
|
|
|
|
|
|
1287
|
|
|
|
|
|
|
: $symbol->id eq 'my' ? 'variable' |
1288
|
|
|
|
|
|
|
: die "Oops: $symbol"; |
1289
|
|
|
|
|
|
|
|
1290
|
70
|
50
|
|
|
|
245
|
if($t->arity ne $expect) { |
1291
|
0
|
|
|
|
|
0
|
$parser->_unexpected("a $expect", $t); |
1292
|
|
|
|
|
|
|
} |
1293
|
70
|
|
|
|
|
196
|
$parser->define($t)->arity("name"); |
1294
|
|
|
|
|
|
|
|
1295
|
69
|
|
|
|
|
163
|
$parser->advance(); |
1296
|
69
|
|
|
|
|
153
|
$parser->advance("="); |
1297
|
|
|
|
|
|
|
|
1298
|
69
|
|
|
|
|
202
|
return $symbol->clone( |
1299
|
|
|
|
|
|
|
arity => 'constant', |
1300
|
|
|
|
|
|
|
first => $t, |
1301
|
|
|
|
|
|
|
second => $parser->expression(0), |
1302
|
|
|
|
|
|
|
is_statement => 1, |
1303
|
|
|
|
|
|
|
); |
1304
|
|
|
|
|
|
|
} |
1305
|
|
|
|
|
|
|
|
1306
|
|
|
|
|
|
|
my $lambda_id = 0; |
1307
|
|
|
|
|
|
|
sub lambda { |
1308
|
56
|
|
|
56
|
0
|
86
|
my($parser, $proto) = @_; |
1309
|
56
|
|
|
|
|
131
|
my $name = $parser->symbol('(name)')->clone( |
1310
|
|
|
|
|
|
|
id => sprintf('lambda@%s:%d', $parser->file, $lambda_id++), |
1311
|
|
|
|
|
|
|
); |
1312
|
|
|
|
|
|
|
|
1313
|
56
|
|
|
|
|
1116
|
return $parser->symbol('(name)')->clone( |
1314
|
|
|
|
|
|
|
arity => 'proc', |
1315
|
|
|
|
|
|
|
id => 'macro', |
1316
|
|
|
|
|
|
|
first => $name, |
1317
|
|
|
|
|
|
|
line => $proto->line, |
1318
|
|
|
|
|
|
|
); |
1319
|
|
|
|
|
|
|
} |
1320
|
|
|
|
|
|
|
|
1321
|
|
|
|
|
|
|
# -> $x { ... } |
1322
|
|
|
|
|
|
|
sub nud_lambda { |
1323
|
39
|
|
|
39
|
0
|
61
|
my($parser, $symbol) = @_; |
1324
|
|
|
|
|
|
|
|
1325
|
39
|
|
|
|
|
108
|
my $pointy = $parser->lambda($symbol); |
1326
|
|
|
|
|
|
|
|
1327
|
39
|
|
|
|
|
897
|
$parser->new_scope(); |
1328
|
39
|
|
|
|
|
49
|
my @params; |
1329
|
39
|
50
|
|
|
|
170
|
if($parser->token->id ne "{") { # has params |
1330
|
39
|
|
|
|
|
124
|
my $paren = ($parser->token->id eq "("); |
1331
|
|
|
|
|
|
|
|
1332
|
39
|
50
|
|
|
|
86
|
$parser->advance("(") if $paren; # optional |
1333
|
|
|
|
|
|
|
|
1334
|
39
|
|
|
|
|
87
|
my $t = $parser->token; |
1335
|
39
|
|
|
|
|
129
|
while($t->arity eq "variable") { |
1336
|
54
|
|
|
|
|
78
|
push @params, $t; |
1337
|
54
|
|
|
|
|
122
|
$parser->define($t); |
1338
|
|
|
|
|
|
|
|
1339
|
54
|
|
|
|
|
119
|
$t = $parser->advance(); |
1340
|
54
|
100
|
|
|
|
164
|
if($t->id eq ",") { |
1341
|
15
|
|
|
|
|
32
|
$t = $parser->advance(); # "," |
1342
|
|
|
|
|
|
|
} |
1343
|
|
|
|
|
|
|
else { |
1344
|
39
|
|
|
|
|
57
|
last; |
1345
|
|
|
|
|
|
|
} |
1346
|
|
|
|
|
|
|
} |
1347
|
|
|
|
|
|
|
|
1348
|
39
|
50
|
|
|
|
97
|
$parser->advance(")") if $paren; |
1349
|
|
|
|
|
|
|
} |
1350
|
39
|
|
|
|
|
134
|
$pointy->second( \@params ); |
1351
|
|
|
|
|
|
|
|
1352
|
39
|
|
|
|
|
88
|
$parser->advance("{"); |
1353
|
39
|
|
|
|
|
98
|
$pointy->third($parser->statements()); |
1354
|
39
|
|
|
|
|
96
|
$parser->advance("}"); |
1355
|
39
|
|
|
|
|
97
|
$parser->pop_scope(); |
1356
|
|
|
|
|
|
|
|
1357
|
39
|
|
|
|
|
118
|
return $symbol->clone( |
1358
|
|
|
|
|
|
|
arity => 'lambda', |
1359
|
|
|
|
|
|
|
first => $pointy, |
1360
|
|
|
|
|
|
|
); |
1361
|
|
|
|
|
|
|
} |
1362
|
|
|
|
|
|
|
|
1363
|
|
|
|
|
|
|
sub nud_current_file { |
1364
|
1
|
|
|
1
|
0
|
3
|
my($self, $symbol) = @_; |
1365
|
1
|
|
|
|
|
6
|
my $file = $self->file; |
1366
|
1
|
50
|
|
|
|
6
|
return $symbol->clone( |
1367
|
|
|
|
|
|
|
arity => 'literal', |
1368
|
|
|
|
|
|
|
value => ref($file) ? '' : $file, |
1369
|
|
|
|
|
|
|
); |
1370
|
|
|
|
|
|
|
} |
1371
|
|
|
|
|
|
|
|
1372
|
|
|
|
|
|
|
sub nud_current_line { |
1373
|
14
|
|
|
14
|
0
|
20
|
my($self, $symbol) = @_; |
1374
|
14
|
|
|
|
|
52
|
return $symbol->clone( |
1375
|
|
|
|
|
|
|
arity => 'literal', |
1376
|
|
|
|
|
|
|
value => $symbol->line, |
1377
|
|
|
|
|
|
|
); |
1378
|
|
|
|
|
|
|
} |
1379
|
|
|
|
|
|
|
|
1380
|
|
|
|
|
|
|
sub nud_current_vars { |
1381
|
5
|
|
|
5
|
0
|
10
|
my($self, $symbol) = @_; |
1382
|
5
|
|
|
|
|
13
|
return $symbol->clone( |
1383
|
|
|
|
|
|
|
arity => 'vars', |
1384
|
|
|
|
|
|
|
); |
1385
|
|
|
|
|
|
|
} |
1386
|
|
|
|
|
|
|
|
1387
|
|
|
|
|
|
|
sub nud_separator { |
1388
|
4
|
|
|
4
|
0
|
8
|
my($self, $symbol) = @_; |
1389
|
4
|
|
|
|
|
11
|
$self->_error("Invalid expression found", $symbol); |
1390
|
|
|
|
|
|
|
} |
1391
|
|
|
|
|
|
|
|
1392
|
|
|
|
|
|
|
# -> VARS { STATEMENTS } |
1393
|
|
|
|
|
|
|
# -> { STATEMENTS } |
1394
|
|
|
|
|
|
|
# { STATEMENTS } |
1395
|
|
|
|
|
|
|
sub pointy { |
1396
|
436
|
|
|
436
|
0
|
733
|
my($parser, $pointy, $in_for) = @_; |
1397
|
|
|
|
|
|
|
|
1398
|
436
|
|
|
|
|
548
|
my @params; |
1399
|
|
|
|
|
|
|
|
1400
|
436
|
|
|
|
|
1156
|
$parser->new_scope(); |
1401
|
|
|
|
|
|
|
|
1402
|
436
|
100
|
|
|
|
2063
|
if($parser->token->id eq "->") { |
1403
|
416
|
|
|
|
|
880
|
$parser->advance(); |
1404
|
416
|
100
|
|
|
|
1968
|
if($parser->token->id ne "{") { |
1405
|
225
|
|
|
|
|
829
|
my $paren = ($parser->token->id eq "("); |
1406
|
|
|
|
|
|
|
|
1407
|
225
|
100
|
|
|
|
648
|
$parser->advance("(") if $paren; |
1408
|
|
|
|
|
|
|
|
1409
|
225
|
|
|
|
|
536
|
my $t = $parser->token; |
1410
|
225
|
|
|
|
|
842
|
while($t->arity eq "variable") { |
1411
|
231
|
|
|
|
|
447
|
push @params, $t; |
1412
|
231
|
|
|
|
|
654
|
$parser->define($t); |
1413
|
|
|
|
|
|
|
|
1414
|
231
|
100
|
|
|
|
547
|
if($in_for) { |
1415
|
149
|
|
|
|
|
473
|
$parser->define_iterator($t); |
1416
|
|
|
|
|
|
|
} |
1417
|
|
|
|
|
|
|
|
1418
|
231
|
|
|
|
|
515
|
$t = $parser->advance(); |
1419
|
|
|
|
|
|
|
|
1420
|
231
|
100
|
|
|
|
995
|
if($t->id eq ",") { |
1421
|
7
|
|
|
|
|
20
|
$t = $parser->advance(); # "," |
1422
|
|
|
|
|
|
|
} |
1423
|
|
|
|
|
|
|
else { |
1424
|
224
|
|
|
|
|
346
|
last; |
1425
|
|
|
|
|
|
|
} |
1426
|
|
|
|
|
|
|
} |
1427
|
|
|
|
|
|
|
|
1428
|
225
|
100
|
|
|
|
760
|
$parser->advance(")") if $paren; |
1429
|
|
|
|
|
|
|
} |
1430
|
|
|
|
|
|
|
} |
1431
|
434
|
|
|
|
|
1531
|
$pointy->second( \@params ); |
1432
|
|
|
|
|
|
|
|
1433
|
434
|
|
|
|
|
1199
|
$parser->advance("{"); |
1434
|
432
|
|
|
|
|
1163
|
$pointy->third($parser->statements()); |
1435
|
422
|
|
|
|
|
1025
|
$parser->advance("}"); |
1436
|
422
|
|
|
|
|
1235
|
$parser->pop_scope(); |
1437
|
|
|
|
|
|
|
|
1438
|
422
|
|
|
|
|
778
|
return; |
1439
|
|
|
|
|
|
|
} |
1440
|
|
|
|
|
|
|
|
1441
|
|
|
|
|
|
|
sub iterator_name { |
1442
|
149
|
|
|
149
|
0
|
230
|
my($parser, $var) = @_; |
1443
|
|
|
|
|
|
|
# $foo -> $~foo |
1444
|
149
|
|
|
|
|
1122
|
(my $it_name = $var->id) =~ s/\A (\$?) /${1}~/xms; |
1445
|
149
|
|
|
|
|
593
|
return $it_name; |
1446
|
|
|
|
|
|
|
} |
1447
|
|
|
|
|
|
|
|
1448
|
|
|
|
|
|
|
sub define_iterator { |
1449
|
184
|
|
|
184
|
0
|
311
|
my($parser, $var) = @_; |
1450
|
|
|
|
|
|
|
|
1451
|
184
|
|
|
|
|
556
|
my $it = $parser->symbol( $parser->iterator_name($var) )->clone( |
1452
|
|
|
|
|
|
|
arity => 'iterator', |
1453
|
|
|
|
|
|
|
first => $var, |
1454
|
|
|
|
|
|
|
); |
1455
|
184
|
|
|
|
|
3954
|
$parser->define($it); |
1456
|
184
|
|
|
|
|
1261
|
$it->set_nud(\&nud_iterator); |
1457
|
184
|
|
|
|
|
450
|
return $it; |
1458
|
|
|
|
|
|
|
} |
1459
|
|
|
|
|
|
|
|
1460
|
|
|
|
|
|
|
sub std_for { |
1461
|
149
|
|
|
149
|
0
|
248
|
my($parser, $symbol) = @_; |
1462
|
|
|
|
|
|
|
|
1463
|
149
|
|
|
|
|
434
|
my $proc = $symbol->clone(arity => 'for'); |
1464
|
149
|
|
|
|
|
3076
|
$proc->first( $parser->expression(0) ); |
1465
|
149
|
|
|
|
|
562
|
$parser->pointy($proc, 1); |
1466
|
|
|
|
|
|
|
|
1467
|
|
|
|
|
|
|
# for-else support |
1468
|
139
|
100
|
|
|
|
2024
|
if($parser->token eq 'else') { |
1469
|
5
|
|
|
|
|
12
|
$parser->advance(); |
1470
|
5
|
|
|
|
|
19
|
my $else = $parser->block(); |
1471
|
5
|
|
|
|
|
17
|
$proc = $symbol->clone( arity => 'for_else', |
1472
|
|
|
|
|
|
|
first => $proc, |
1473
|
|
|
|
|
|
|
second => $else, |
1474
|
|
|
|
|
|
|
) |
1475
|
|
|
|
|
|
|
} |
1476
|
139
|
|
|
|
|
1034
|
return $proc; |
1477
|
|
|
|
|
|
|
} |
1478
|
|
|
|
|
|
|
|
1479
|
|
|
|
|
|
|
sub std_while { |
1480
|
15
|
|
|
15
|
0
|
25
|
my($parser, $symbol) = @_; |
1481
|
|
|
|
|
|
|
|
1482
|
15
|
|
|
|
|
44
|
my $proc = $symbol->clone(arity => 'while'); |
1483
|
15
|
|
|
|
|
338
|
$proc->first( $parser->expression(0) ); |
1484
|
15
|
|
|
|
|
45
|
$parser->pointy($proc); |
1485
|
15
|
|
|
|
|
90
|
return $proc; |
1486
|
|
|
|
|
|
|
} |
1487
|
|
|
|
|
|
|
|
1488
|
|
|
|
|
|
|
# macro name -> { ... } |
1489
|
|
|
|
|
|
|
sub std_proc { |
1490
|
150
|
|
|
150
|
0
|
386
|
my($parser, $symbol) = @_; |
1491
|
|
|
|
|
|
|
|
1492
|
150
|
|
|
|
|
435
|
my $macro = $symbol->clone(arity => "proc"); |
1493
|
150
|
|
|
|
|
3094
|
my $name = $parser->token; |
1494
|
|
|
|
|
|
|
|
1495
|
150
|
50
|
|
|
|
639
|
if($name->arity ne "name") { |
1496
|
0
|
|
|
|
|
0
|
$parser->_unexpected("a name", $name); |
1497
|
|
|
|
|
|
|
} |
1498
|
|
|
|
|
|
|
|
1499
|
150
|
|
|
|
|
519
|
$parser->define_function($name->id); |
1500
|
150
|
|
|
|
|
412
|
$macro->first($name); |
1501
|
150
|
|
|
|
|
335
|
$parser->advance(); |
1502
|
150
|
|
|
|
|
456
|
$parser->pointy($macro); |
1503
|
146
|
|
|
|
|
956
|
return $macro; |
1504
|
|
|
|
|
|
|
} |
1505
|
|
|
|
|
|
|
|
1506
|
|
|
|
|
|
|
# block name -> { ... } |
1507
|
|
|
|
|
|
|
# block name | filter -> { ... } |
1508
|
|
|
|
|
|
|
sub std_macro_block { |
1509
|
92
|
|
|
92
|
0
|
160
|
my($parser, $symbol) = @_; |
1510
|
|
|
|
|
|
|
|
1511
|
92
|
|
|
|
|
288
|
my $macro = $symbol->clone(arity => "proc"); |
1512
|
92
|
|
|
|
|
1963
|
my $name = $parser->token; |
1513
|
|
|
|
|
|
|
|
1514
|
92
|
50
|
|
|
|
371
|
if($name->arity ne "name") { |
1515
|
0
|
|
|
|
|
0
|
$parser->_unexpected("a name", $name); |
1516
|
|
|
|
|
|
|
} |
1517
|
|
|
|
|
|
|
|
1518
|
|
|
|
|
|
|
# auto filters |
1519
|
92
|
|
|
|
|
156
|
my @filters; |
1520
|
92
|
|
|
|
|
218
|
my $t = $parser->advance(); |
1521
|
92
|
|
|
|
|
410
|
while($t->id eq "|") { |
1522
|
11
|
|
|
|
|
57
|
$t = $parser->advance(); |
1523
|
|
|
|
|
|
|
|
1524
|
11
|
50
|
|
|
|
80
|
if($t->arity ne "name") { |
1525
|
0
|
|
|
|
|
0
|
$parser->_unexpected("a name", $name); |
1526
|
|
|
|
|
|
|
} |
1527
|
11
|
|
|
|
|
42
|
my $filter = $t->clone(); |
1528
|
11
|
|
|
|
|
236
|
$t = $parser->advance(); |
1529
|
|
|
|
|
|
|
|
1530
|
11
|
|
|
|
|
22
|
my $args; |
1531
|
11
|
100
|
|
|
|
86
|
if($t->id eq "(") { |
1532
|
2
|
|
|
|
|
8
|
$parser->advance(); |
1533
|
2
|
|
|
|
|
8
|
$args = $parser->expression_list(); |
1534
|
2
|
|
|
|
|
9
|
$t = $parser->advance(")"); |
1535
|
|
|
|
|
|
|
} |
1536
|
|
|
|
|
|
|
push @filters, $args |
1537
|
11
|
100
|
|
|
|
66
|
? $parser->call($filter, @{$args}) |
|
2
|
|
|
|
|
9
|
|
1538
|
|
|
|
|
|
|
: $filter; |
1539
|
|
|
|
|
|
|
} |
1540
|
|
|
|
|
|
|
|
1541
|
92
|
|
|
|
|
393
|
$parser->define_function($name->id); |
1542
|
92
|
|
|
|
|
263
|
$macro->first($name); |
1543
|
92
|
|
|
|
|
269
|
$parser->pointy($macro); |
1544
|
|
|
|
|
|
|
|
1545
|
92
|
|
|
|
|
471
|
my $call = $parser->call($macro->first); |
1546
|
92
|
100
|
|
|
|
2145
|
if(@filters) { |
1547
|
9
|
|
|
|
|
22
|
foreach my $filter(@filters) { # apply filters |
1548
|
11
|
|
|
|
|
81
|
$call = $parser->call($filter, $call); |
1549
|
|
|
|
|
|
|
} |
1550
|
|
|
|
|
|
|
} |
1551
|
|
|
|
|
|
|
# std() can return a list |
1552
|
92
|
|
|
|
|
542
|
return( $macro, $parser->print($call) ); |
1553
|
|
|
|
|
|
|
} |
1554
|
|
|
|
|
|
|
|
1555
|
|
|
|
|
|
|
sub std_override { # synonym to 'around' |
1556
|
6
|
|
|
6
|
0
|
11
|
my($parser, $symbol) = @_; |
1557
|
|
|
|
|
|
|
|
1558
|
6
|
|
|
|
|
21
|
return $parser->std_proc($symbol->clone(id => 'around')); |
1559
|
|
|
|
|
|
|
} |
1560
|
|
|
|
|
|
|
|
1561
|
|
|
|
|
|
|
sub std_if { |
1562
|
112
|
|
|
112
|
0
|
190
|
my($parser, $symbol, $expr) = @_; |
1563
|
|
|
|
|
|
|
|
1564
|
112
|
|
|
|
|
334
|
my $if = $symbol->clone(arity => "if"); |
1565
|
|
|
|
|
|
|
|
1566
|
112
|
|
|
|
|
2342
|
$if->first( $parser->expression(0) ); |
1567
|
|
|
|
|
|
|
|
1568
|
112
|
100
|
|
|
|
306
|
if(defined $expr) { # statement modifier |
1569
|
13
|
|
|
|
|
48
|
$if->second([$expr]); |
1570
|
13
|
|
|
|
|
35
|
return $if; |
1571
|
|
|
|
|
|
|
} |
1572
|
|
|
|
|
|
|
|
1573
|
99
|
|
|
|
|
298
|
$if->second( $parser->block() ); |
1574
|
|
|
|
|
|
|
|
1575
|
98
|
|
|
|
|
139
|
my $top_if = $if; |
1576
|
|
|
|
|
|
|
|
1577
|
98
|
|
|
|
|
230
|
my $t = $parser->token; |
1578
|
98
|
|
|
|
|
387
|
while($t->id eq "elsif") { |
1579
|
3
|
|
|
|
|
8
|
$parser->reserve($t); |
1580
|
3
|
|
|
|
|
8
|
$parser->advance(); # "elsif" |
1581
|
|
|
|
|
|
|
|
1582
|
3
|
|
|
|
|
10
|
my $elsif = $t->clone(arity => "if"); |
1583
|
3
|
|
|
|
|
57
|
$elsif->first( $parser->expression(0) ); |
1584
|
3
|
|
|
|
|
9
|
$elsif->second( $parser->block() ); |
1585
|
3
|
|
|
|
|
10
|
$if->third([$elsif]); |
1586
|
3
|
|
|
|
|
4
|
$if = $elsif; |
1587
|
3
|
|
|
|
|
14
|
$t = $parser->token; |
1588
|
|
|
|
|
|
|
} |
1589
|
|
|
|
|
|
|
|
1590
|
98
|
100
|
|
|
|
388
|
if($t->id eq "else") { |
1591
|
56
|
|
|
|
|
125
|
$parser->reserve($t); |
1592
|
56
|
|
|
|
|
130
|
$t = $parser->advance(); # "else" |
1593
|
|
|
|
|
|
|
|
1594
|
56
|
100
|
|
|
|
290
|
$if->third( $t->id eq "if" |
1595
|
|
|
|
|
|
|
? [$parser->statement()] |
1596
|
|
|
|
|
|
|
: $parser->block()); |
1597
|
|
|
|
|
|
|
} |
1598
|
98
|
|
|
|
|
793
|
return $top_if; |
1599
|
|
|
|
|
|
|
} |
1600
|
|
|
|
|
|
|
|
1601
|
|
|
|
|
|
|
sub std_given { |
1602
|
30
|
|
|
30
|
0
|
49
|
my($parser, $symbol) = @_; |
1603
|
|
|
|
|
|
|
|
1604
|
30
|
|
|
|
|
81
|
my $given = $symbol->clone(arity => 'given'); |
1605
|
30
|
|
|
|
|
602
|
$given->first( $parser->expression(0) ); |
1606
|
|
|
|
|
|
|
|
1607
|
30
|
|
|
|
|
72
|
local $parser->{in_given} = 1; |
1608
|
30
|
|
|
|
|
74
|
$parser->pointy($given); |
1609
|
|
|
|
|
|
|
|
1610
|
30
|
100
|
66
|
|
|
106
|
if(!(defined $given->second && @{$given->second})) { # if no topic vars |
|
30
|
|
|
|
|
141
|
|
1611
|
14
|
|
|
|
|
34
|
$given->second([ |
1612
|
|
|
|
|
|
|
$parser->symbol('($_)')->clone(arity => 'variable' ) |
1613
|
|
|
|
|
|
|
]); |
1614
|
|
|
|
|
|
|
} |
1615
|
|
|
|
|
|
|
|
1616
|
30
|
|
|
|
|
342
|
$parser->build_given_body($given, "when"); |
1617
|
30
|
|
|
|
|
198
|
return $given; |
1618
|
|
|
|
|
|
|
} |
1619
|
|
|
|
|
|
|
|
1620
|
|
|
|
|
|
|
# when/default |
1621
|
|
|
|
|
|
|
sub std_when { |
1622
|
60
|
|
|
60
|
0
|
81
|
my($parser, $symbol) = @_; |
1623
|
|
|
|
|
|
|
|
1624
|
60
|
50
|
|
|
|
200
|
if(!$parser->in_given) { |
1625
|
0
|
|
|
|
|
0
|
$parser->_error("You cannot use $symbol blocks outside given blocks"); |
1626
|
|
|
|
|
|
|
} |
1627
|
60
|
|
|
|
|
156
|
my $proc = $symbol->clone(arity => 'when'); |
1628
|
60
|
100
|
|
|
|
1219
|
if($symbol->id eq "when") { |
1629
|
31
|
|
|
|
|
243
|
$proc->first( $parser->expression(0) ); |
1630
|
|
|
|
|
|
|
} |
1631
|
60
|
|
|
|
|
139
|
$proc->second( $parser->block() ); |
1632
|
60
|
|
|
|
|
334
|
return $proc; |
1633
|
|
|
|
|
|
|
} |
1634
|
|
|
|
|
|
|
|
1635
|
|
|
|
|
|
|
sub _only_white_spaces { |
1636
|
21
|
|
|
21
|
|
27
|
my($s) = @_; |
1637
|
21
|
|
33
|
|
|
224
|
return $s->arity eq "literal" |
1638
|
|
|
|
|
|
|
&& $s->value =~ m{\A [ \t\r\n]* \z}xms |
1639
|
|
|
|
|
|
|
} |
1640
|
|
|
|
|
|
|
|
1641
|
|
|
|
|
|
|
sub build_given_body { |
1642
|
40
|
|
|
40
|
0
|
61
|
my($parser, $given, $expect) = @_; |
1643
|
40
|
|
|
|
|
59
|
my($topic) = @{$given->second}; |
|
40
|
|
|
|
|
113
|
|
1644
|
|
|
|
|
|
|
|
1645
|
|
|
|
|
|
|
# make if-elsif-else chain from given-when |
1646
|
40
|
|
|
|
|
56
|
my $if; |
1647
|
|
|
|
|
|
|
my $elsif; |
1648
|
0
|
|
|
|
|
0
|
my $else; |
1649
|
40
|
|
|
|
|
67
|
foreach my $when(@{$given->third}) { |
|
40
|
|
|
|
|
112
|
|
1650
|
101
|
100
|
|
|
|
313
|
if($when->arity ne $expect) { |
1651
|
|
|
|
|
|
|
# ignore white space |
1652
|
22
|
100
|
66
|
|
|
74
|
if($when->id eq "print_raw" |
1653
|
21
|
|
|
|
|
35
|
&& !grep { !_only_white_spaces($_) } @{$when->first}) { |
|
21
|
|
|
|
|
51
|
|
1654
|
21
|
|
|
|
|
34
|
next; |
1655
|
|
|
|
|
|
|
} |
1656
|
1
|
|
|
|
|
5
|
$parser->_unexpected("$expect blocks", $when); |
1657
|
|
|
|
|
|
|
} |
1658
|
79
|
|
|
|
|
180
|
$when->arity("if"); # change the arity |
1659
|
|
|
|
|
|
|
|
1660
|
79
|
100
|
|
|
|
219
|
if(defined(my $test = $when->first)) { # when |
1661
|
43
|
100
|
|
|
|
123
|
if(!$test->is_logical) { |
1662
|
31
|
|
|
|
|
82
|
$when->first( $parser->binary('~~', $topic, $test) ); |
1663
|
|
|
|
|
|
|
} |
1664
|
|
|
|
|
|
|
} |
1665
|
|
|
|
|
|
|
else { # default |
1666
|
36
|
|
|
|
|
92
|
$when->first( $parser->symbol('true')->nud($parser) ); |
1667
|
36
|
|
|
|
|
56
|
$else = $when; |
1668
|
36
|
|
|
|
|
76
|
next; |
1669
|
|
|
|
|
|
|
} |
1670
|
|
|
|
|
|
|
|
1671
|
43
|
100
|
|
|
|
689
|
if(!defined $if) { |
1672
|
35
|
|
|
|
|
40
|
$if = $when; |
1673
|
35
|
|
|
|
|
64
|
$elsif = $when; |
1674
|
|
|
|
|
|
|
} |
1675
|
|
|
|
|
|
|
else { |
1676
|
8
|
|
|
|
|
26
|
$elsif->third([$when]); |
1677
|
8
|
|
|
|
|
17
|
$elsif = $when; |
1678
|
|
|
|
|
|
|
} |
1679
|
|
|
|
|
|
|
} |
1680
|
39
|
100
|
|
|
|
93
|
if(defined $else) { # default |
1681
|
36
|
100
|
|
|
|
69
|
if(defined $elsif) { |
1682
|
33
|
|
|
|
|
97
|
$elsif->third([$else]); |
1683
|
|
|
|
|
|
|
} |
1684
|
|
|
|
|
|
|
else { |
1685
|
3
|
|
|
|
|
7
|
$if = $else; # only default |
1686
|
|
|
|
|
|
|
} |
1687
|
|
|
|
|
|
|
} |
1688
|
39
|
100
|
|
|
|
150
|
$given->third(defined $if ? [$if] : undef); |
1689
|
39
|
|
|
|
|
125
|
return; |
1690
|
|
|
|
|
|
|
} |
1691
|
|
|
|
|
|
|
|
1692
|
|
|
|
|
|
|
sub std_include { |
1693
|
1253
|
|
|
1253
|
0
|
1792
|
my($parser, $symbol) = @_; |
1694
|
|
|
|
|
|
|
|
1695
|
1253
|
|
|
|
|
3032
|
my $arg = $parser->barename(); |
1696
|
1253
|
|
|
|
|
3023
|
my $vars = $parser->localize_vars(); |
1697
|
1253
|
|
|
|
|
3631
|
my $stmt = $symbol->clone( |
1698
|
|
|
|
|
|
|
first => $arg, |
1699
|
|
|
|
|
|
|
second => $vars, |
1700
|
|
|
|
|
|
|
arity => 'include', |
1701
|
|
|
|
|
|
|
); |
1702
|
1253
|
|
|
|
|
27881
|
return $parser->finish_statement($stmt); |
1703
|
|
|
|
|
|
|
} |
1704
|
|
|
|
|
|
|
|
1705
|
|
|
|
|
|
|
sub std_print { |
1706
|
11515
|
|
|
11515
|
0
|
21107
|
my($parser, $symbol) = @_; |
1707
|
11515
|
|
|
|
|
17285
|
my $args; |
1708
|
11515
|
50
|
|
|
|
53113
|
if($parser->token->id ne ";") { |
1709
|
11515
|
|
|
|
|
28468
|
$args = $parser->expression_list(); |
1710
|
|
|
|
|
|
|
} |
1711
|
11515
|
|
|
|
|
37563
|
my $stmt = $symbol->clone( |
1712
|
|
|
|
|
|
|
arity => 'print', |
1713
|
|
|
|
|
|
|
first => $args, |
1714
|
|
|
|
|
|
|
); |
1715
|
11515
|
|
|
|
|
272744
|
return $parser->finish_statement($stmt); |
1716
|
|
|
|
|
|
|
} |
1717
|
|
|
|
|
|
|
|
1718
|
|
|
|
|
|
|
# for cascade() and include() |
1719
|
|
|
|
|
|
|
sub barename { |
1720
|
1333
|
|
|
1333
|
0
|
1835
|
my($parser) = @_; |
1721
|
|
|
|
|
|
|
|
1722
|
1333
|
|
|
|
|
3075
|
my $t = $parser->token; |
1723
|
1333
|
100
|
100
|
|
|
5149
|
if($t->arity ne 'name' or $t->is_defined) { |
1724
|
|
|
|
|
|
|
# string literal for 'cascade', or any expression for 'include' |
1725
|
1264
|
|
|
|
|
2919
|
return $parser->expression(0); |
1726
|
|
|
|
|
|
|
} |
1727
|
|
|
|
|
|
|
|
1728
|
|
|
|
|
|
|
# path::to::name |
1729
|
69
|
|
|
|
|
95
|
my @parts; |
1730
|
69
|
|
|
|
|
132
|
push @parts, $t; |
1731
|
69
|
|
|
|
|
174
|
$parser->advance(); |
1732
|
|
|
|
|
|
|
|
1733
|
69
|
|
|
|
|
100
|
while(1) { |
1734
|
121
|
|
|
|
|
298
|
my $t = $parser->token; |
1735
|
|
|
|
|
|
|
|
1736
|
121
|
100
|
|
|
|
421
|
if($t->id eq "::") { |
1737
|
52
|
|
|
|
|
118
|
$t = $parser->advance(); # "::" |
1738
|
|
|
|
|
|
|
|
1739
|
52
|
50
|
|
|
|
207
|
if($t->arity ne "name") { |
1740
|
0
|
|
|
|
|
0
|
$parser->_unexpected("a name", $t); |
1741
|
|
|
|
|
|
|
} |
1742
|
|
|
|
|
|
|
|
1743
|
52
|
|
|
|
|
83
|
push @parts, $t; |
1744
|
52
|
|
|
|
|
109
|
$parser->advance(); |
1745
|
|
|
|
|
|
|
} |
1746
|
|
|
|
|
|
|
else { |
1747
|
69
|
|
|
|
|
166
|
last; |
1748
|
|
|
|
|
|
|
} |
1749
|
|
|
|
|
|
|
} |
1750
|
69
|
|
|
|
|
170
|
return \@parts; |
1751
|
|
|
|
|
|
|
} |
1752
|
|
|
|
|
|
|
|
1753
|
|
|
|
|
|
|
# NOTHING | { expression-list } |
1754
|
|
|
|
|
|
|
sub localize_vars { |
1755
|
1299
|
|
|
1299
|
0
|
2051
|
my($parser) = @_; |
1756
|
1299
|
100
|
|
|
|
5073
|
if($parser->token->id eq "{") { |
1757
|
13
|
|
|
|
|
27
|
$parser->advance(); |
1758
|
13
|
|
|
|
|
40
|
$parser->new_scope(); |
1759
|
13
|
|
|
|
|
41
|
my $vars = $parser->expression_list(); |
1760
|
13
|
|
|
|
|
39
|
$parser->pop_scope(); |
1761
|
13
|
|
|
|
|
28
|
$parser->advance("}"); |
1762
|
13
|
|
|
|
|
29
|
return $vars; |
1763
|
|
|
|
|
|
|
} |
1764
|
1286
|
|
|
|
|
2074
|
return undef; |
1765
|
|
|
|
|
|
|
} |
1766
|
|
|
|
|
|
|
|
1767
|
|
|
|
|
|
|
sub std_cascade { |
1768
|
64
|
|
|
64
|
0
|
157
|
my($parser, $symbol) = @_; |
1769
|
|
|
|
|
|
|
|
1770
|
64
|
|
|
|
|
104
|
my $base; |
1771
|
64
|
100
|
|
|
|
337
|
if($parser->token->id ne "with") { |
1772
|
57
|
|
|
|
|
220
|
$base = $parser->barename(); |
1773
|
|
|
|
|
|
|
} |
1774
|
|
|
|
|
|
|
|
1775
|
64
|
|
|
|
|
95
|
my $components; |
1776
|
64
|
100
|
|
|
|
310
|
if($parser->token->id eq "with") { |
1777
|
11
|
|
|
|
|
25
|
$parser->advance(); # "with" |
1778
|
|
|
|
|
|
|
|
1779
|
11
|
|
|
|
|
30
|
my @c = $parser->barename(); |
1780
|
11
|
|
|
|
|
51
|
while($parser->token->id eq ",") { |
1781
|
2
|
|
|
|
|
5
|
$parser->advance(); # "," |
1782
|
2
|
|
|
|
|
5
|
push @c, $parser->barename(); |
1783
|
|
|
|
|
|
|
} |
1784
|
11
|
|
|
|
|
22
|
$components = \@c; |
1785
|
|
|
|
|
|
|
} |
1786
|
|
|
|
|
|
|
|
1787
|
64
|
|
|
|
|
228
|
my $vars = $parser->localize_vars(); |
1788
|
64
|
|
|
|
|
235
|
my $stmt = $symbol->clone( |
1789
|
|
|
|
|
|
|
arity => 'cascade', |
1790
|
|
|
|
|
|
|
first => $base, |
1791
|
|
|
|
|
|
|
second => $components, |
1792
|
|
|
|
|
|
|
third => $vars, |
1793
|
|
|
|
|
|
|
); |
1794
|
64
|
|
|
|
|
1610
|
return $parser->finish_statement($stmt); |
1795
|
|
|
|
|
|
|
} |
1796
|
|
|
|
|
|
|
|
1797
|
|
|
|
|
|
|
sub std_super { |
1798
|
7
|
|
|
7
|
0
|
15
|
my($parser, $symbol) = @_; |
1799
|
7
|
|
|
|
|
20
|
my $stmt = $symbol->clone(arity => 'super'); |
1800
|
7
|
|
|
|
|
155
|
return $parser->finish_statement($stmt); |
1801
|
|
|
|
|
|
|
} |
1802
|
|
|
|
|
|
|
|
1803
|
|
|
|
|
|
|
sub std_next { |
1804
|
5
|
|
|
5
|
0
|
9
|
my($parser, $symbol) = @_; |
1805
|
5
|
|
|
|
|
16
|
my $stmt = $symbol->clone(arity => 'loop_control', id => 'next'); |
1806
|
5
|
|
|
|
|
113
|
return $parser->finish_statement($stmt); |
1807
|
|
|
|
|
|
|
} |
1808
|
|
|
|
|
|
|
|
1809
|
|
|
|
|
|
|
sub std_last { |
1810
|
7
|
|
|
7
|
0
|
13
|
my($parser, $symbol) = @_; |
1811
|
7
|
|
|
|
|
23
|
my $stmt = $symbol->clone(arity => 'loop_control', id => 'last'); |
1812
|
7
|
|
|
|
|
157
|
return $parser->finish_statement($stmt); |
1813
|
|
|
|
|
|
|
} |
1814
|
|
|
|
|
|
|
|
1815
|
|
|
|
|
|
|
# iterator elements |
1816
|
|
|
|
|
|
|
|
1817
|
|
|
|
|
|
|
sub bad_iterator_args { |
1818
|
8
|
|
|
8
|
0
|
14
|
my($parser, $iterator) = @_; |
1819
|
8
|
|
|
|
|
30
|
$parser->_error("Wrong number of arguments for $iterator." . $iterator->second); |
1820
|
|
|
|
|
|
|
} |
1821
|
|
|
|
|
|
|
|
1822
|
|
|
|
|
|
|
sub iterator_index { |
1823
|
16
|
|
|
16
|
0
|
144
|
my($parser, $iterator, @args) = @_; |
1824
|
16
|
100
|
|
|
|
55
|
$parser->bad_iterator_args($iterator) if @args != 0; |
1825
|
|
|
|
|
|
|
# $~iterator |
1826
|
15
|
|
|
|
|
43
|
return $iterator; |
1827
|
|
|
|
|
|
|
} |
1828
|
|
|
|
|
|
|
|
1829
|
|
|
|
|
|
|
sub iterator_count { |
1830
|
10
|
|
|
10
|
0
|
20
|
my($parser, $iterator, @args) = @_; |
1831
|
10
|
100
|
|
|
|
36
|
$parser->bad_iterator_args($iterator) if @args != 0; |
1832
|
|
|
|
|
|
|
# $~iterator + 1 |
1833
|
9
|
|
|
|
|
36
|
return $parser->binary('+', $iterator, 1); |
1834
|
|
|
|
|
|
|
} |
1835
|
|
|
|
|
|
|
|
1836
|
|
|
|
|
|
|
sub iterator_is_first { |
1837
|
7
|
|
|
7
|
0
|
16
|
my($parser, $iterator, @args) = @_; |
1838
|
7
|
100
|
|
|
|
24
|
$parser->bad_iterator_args($iterator) if @args != 0; |
1839
|
|
|
|
|
|
|
# $~iterator == 0 |
1840
|
6
|
|
|
|
|
20
|
return $parser->binary('==', $iterator, 0); |
1841
|
|
|
|
|
|
|
} |
1842
|
|
|
|
|
|
|
|
1843
|
|
|
|
|
|
|
sub iterator_is_last { |
1844
|
4
|
|
|
4
|
0
|
10
|
my($parser, $iterator, @args) = @_; |
1845
|
4
|
100
|
|
|
|
17
|
$parser->bad_iterator_args($iterator) if @args != 0; |
1846
|
|
|
|
|
|
|
# $~iterator == $~iterator.max_index |
1847
|
3
|
|
|
|
|
24
|
return $parser->binary('==', $iterator, $parser->iterator_max_index($iterator)); |
1848
|
|
|
|
|
|
|
} |
1849
|
|
|
|
|
|
|
|
1850
|
|
|
|
|
|
|
sub iterator_body { |
1851
|
16
|
|
|
16
|
0
|
29
|
my($parser, $iterator, @args) = @_; |
1852
|
16
|
50
|
|
|
|
50
|
$parser->bad_iterator_args($iterator) if @args != 0; |
1853
|
|
|
|
|
|
|
# $~iterator.body |
1854
|
16
|
|
|
|
|
49
|
return $iterator->clone( |
1855
|
|
|
|
|
|
|
arity => 'iterator_body', |
1856
|
|
|
|
|
|
|
); |
1857
|
|
|
|
|
|
|
} |
1858
|
|
|
|
|
|
|
|
1859
|
|
|
|
|
|
|
sub iterator_size { |
1860
|
3
|
|
|
3
|
0
|
10
|
my($parser, $iterator, @args) = @_; |
1861
|
3
|
100
|
|
|
|
14
|
$parser->bad_iterator_args($iterator) if @args != 0; |
1862
|
|
|
|
|
|
|
# $~iterator.max_index + 1 |
1863
|
2
|
|
|
|
|
8
|
return $parser->binary('+', $parser->iterator_max_index($iterator), 1); |
1864
|
|
|
|
|
|
|
} |
1865
|
|
|
|
|
|
|
|
1866
|
|
|
|
|
|
|
sub iterator_max_index { |
1867
|
8
|
|
|
8
|
0
|
15
|
my($parser, $iterator, @args) = @_; |
1868
|
8
|
100
|
|
|
|
33
|
$parser->bad_iterator_args($iterator) if @args != 0; |
1869
|
|
|
|
|
|
|
# __builtin_max_index($~iterator.body) |
1870
|
7
|
|
|
|
|
20
|
return $parser->symbol('max_index')->clone( |
1871
|
|
|
|
|
|
|
arity => 'unary', |
1872
|
|
|
|
|
|
|
first => $parser->iterator_body($iterator), |
1873
|
|
|
|
|
|
|
); |
1874
|
|
|
|
|
|
|
} |
1875
|
|
|
|
|
|
|
|
1876
|
|
|
|
|
|
|
sub _iterator_peek { |
1877
|
6
|
|
|
6
|
|
10
|
my($parser, $iterator, $pos) = @_; |
1878
|
|
|
|
|
|
|
# $~iterator.body[ $~iterator.index + $pos ] |
1879
|
6
|
|
|
|
|
18
|
return $parser->binary('[', |
1880
|
|
|
|
|
|
|
$parser->iterator_body($iterator), |
1881
|
|
|
|
|
|
|
$parser->binary('+', $parser->iterator_index($iterator), $pos), |
1882
|
|
|
|
|
|
|
); |
1883
|
|
|
|
|
|
|
} |
1884
|
|
|
|
|
|
|
|
1885
|
|
|
|
|
|
|
sub iterator_peek_next { |
1886
|
3
|
|
|
3
|
0
|
6
|
my($parser, $iterator, @args) = @_; |
1887
|
3
|
50
|
|
|
|
12
|
$parser->bad_iterator_args($iterator) if @args != 0; |
1888
|
3
|
|
|
|
|
22
|
return $parser->_iterator_peek($iterator, +1); |
1889
|
|
|
|
|
|
|
} |
1890
|
|
|
|
|
|
|
|
1891
|
|
|
|
|
|
|
sub iterator_peek_prev { |
1892
|
5
|
|
|
5
|
0
|
12
|
my($parser, $iterator, @args) = @_; |
1893
|
5
|
100
|
|
|
|
23
|
$parser->bad_iterator_args($iterator) if @args != 0; |
1894
|
|
|
|
|
|
|
# $~iterator.is_first ? nil : |
1895
|
3
|
|
|
|
|
9
|
return $parser->symbol('?')->clone( |
1896
|
|
|
|
|
|
|
arity => 'if', |
1897
|
|
|
|
|
|
|
first => $parser->iterator_is_first($iterator), |
1898
|
|
|
|
|
|
|
second => [$parser->nil], |
1899
|
|
|
|
|
|
|
third => [$parser->_iterator_peek($iterator, -1)], |
1900
|
|
|
|
|
|
|
); |
1901
|
|
|
|
|
|
|
} |
1902
|
|
|
|
|
|
|
|
1903
|
|
|
|
|
|
|
sub iterator_cycle { |
1904
|
6
|
|
|
6
|
0
|
15
|
my($parser, $iterator, @args) = @_; |
1905
|
6
|
50
|
|
|
|
16
|
$parser->bad_iterator_args($iterator) if @args < 2; |
1906
|
|
|
|
|
|
|
# $iterator.cycle("foo", "bar", "baz") makes: |
1907
|
|
|
|
|
|
|
# ($tmp = $~iterator % n) == 0 ? "foo" |
1908
|
|
|
|
|
|
|
# : $tmp == 1 ? "bar" |
1909
|
|
|
|
|
|
|
# : "baz" |
1910
|
6
|
|
|
|
|
16
|
$parser->new_scope(); |
1911
|
|
|
|
|
|
|
|
1912
|
6
|
|
|
|
|
16
|
my $mod = $parser->binary('%', $iterator, scalar @args); |
1913
|
|
|
|
|
|
|
|
1914
|
|
|
|
|
|
|
# for the second time |
1915
|
6
|
|
|
|
|
127
|
my $tmp = $parser->symbol('($cycle)')->clone(arity => 'name'); |
1916
|
|
|
|
|
|
|
|
1917
|
|
|
|
|
|
|
# for the first time |
1918
|
6
|
|
|
|
|
114
|
my $cond = $iterator->clone( |
1919
|
|
|
|
|
|
|
arity => 'constant', |
1920
|
|
|
|
|
|
|
first => $tmp, |
1921
|
|
|
|
|
|
|
second => $mod, |
1922
|
|
|
|
|
|
|
); |
1923
|
|
|
|
|
|
|
|
1924
|
6
|
|
|
|
|
121
|
my $parent = $iterator->clone( |
1925
|
|
|
|
|
|
|
arity => 'if', |
1926
|
|
|
|
|
|
|
first => $parser->binary('==', $cond, 0), |
1927
|
|
|
|
|
|
|
second => [ $args[0] ], |
1928
|
|
|
|
|
|
|
); |
1929
|
6
|
|
|
|
|
121
|
my $child = $parent; |
1930
|
|
|
|
|
|
|
|
1931
|
6
|
|
|
|
|
10
|
my $last = pop @args; |
1932
|
6
|
|
|
|
|
21
|
for(my $i = 1; $i < @args; $i++) { |
1933
|
4
|
|
|
|
|
17
|
my $nth = $iterator->clone( |
1934
|
|
|
|
|
|
|
arity => 'if', |
1935
|
|
|
|
|
|
|
id => "$iterator.cycle: $i", |
1936
|
|
|
|
|
|
|
first => $parser->binary('==', $tmp, $i), |
1937
|
|
|
|
|
|
|
second => [$args[$i]], |
1938
|
|
|
|
|
|
|
); |
1939
|
|
|
|
|
|
|
|
1940
|
4
|
|
|
|
|
101
|
$child->third([$nth]); |
1941
|
4
|
|
|
|
|
16
|
$child = $nth; |
1942
|
|
|
|
|
|
|
} |
1943
|
6
|
|
|
|
|
18
|
$child->third([$last]); |
1944
|
|
|
|
|
|
|
|
1945
|
6
|
|
|
|
|
14
|
$parser->pop_scope(); |
1946
|
6
|
|
|
|
|
18
|
return $parent; |
1947
|
|
|
|
|
|
|
} |
1948
|
|
|
|
|
|
|
|
1949
|
|
|
|
|
|
|
# utils |
1950
|
|
|
|
|
|
|
|
1951
|
|
|
|
|
|
|
sub make_alias { # alas(from => to) |
1952
|
3260
|
|
|
3260
|
0
|
10460
|
my($parser, $from, $to) = @_; |
1953
|
|
|
|
|
|
|
|
1954
|
3260
|
|
|
|
|
12482
|
my $stash = $parser->symbol_table; |
1955
|
3260
|
50
|
|
|
|
14922
|
if(exists $parser->symbol_table->{$to}) { |
1956
|
|
|
|
|
|
|
Carp::confess( |
1957
|
|
|
|
|
|
|
"Cannot make an alias to an existing symbol ($from => $to / " |
1958
|
0
|
|
|
|
|
0
|
. p($parser->symbol_table->{$to}) .")"); |
1959
|
|
|
|
|
|
|
} |
1960
|
|
|
|
|
|
|
|
1961
|
|
|
|
|
|
|
# make a snapshot |
1962
|
3260
|
|
|
|
|
11419
|
return $stash->{$to} = $parser->symbol($from)->clone( |
1963
|
|
|
|
|
|
|
value => $to, # real id |
1964
|
|
|
|
|
|
|
); |
1965
|
|
|
|
|
|
|
} |
1966
|
|
|
|
|
|
|
|
1967
|
|
|
|
|
|
|
sub not_supported { |
1968
|
2
|
|
|
2
|
0
|
3
|
my($parser, $symbol) = @_; |
1969
|
2
|
|
|
|
|
52
|
$parser->_error("'$symbol' is not supported"); |
1970
|
|
|
|
|
|
|
} |
1971
|
|
|
|
|
|
|
|
1972
|
|
|
|
|
|
|
sub _unexpected { |
1973
|
13
|
|
|
13
|
|
24
|
my($parser, $expected, $got) = @_; |
1974
|
13
|
100
|
66
|
|
|
161
|
if(defined($got) && $got ne ";") { |
1975
|
12
|
100
|
|
|
|
32
|
if($got eq '(end)') { |
1976
|
2
|
|
|
|
|
12
|
$parser->_error("Expected $expected, but reached EOF"); |
1977
|
|
|
|
|
|
|
} |
1978
|
|
|
|
|
|
|
else { |
1979
|
10
|
|
|
|
|
35
|
$parser->_error("Expected $expected, but got " . neat("$got")); |
1980
|
|
|
|
|
|
|
} |
1981
|
|
|
|
|
|
|
} |
1982
|
|
|
|
|
|
|
else { |
1983
|
1
|
|
|
|
|
5
|
$parser->_error("Expected $expected"); |
1984
|
|
|
|
|
|
|
} |
1985
|
|
|
|
|
|
|
} |
1986
|
|
|
|
|
|
|
|
1987
|
|
|
|
|
|
|
sub _error { |
1988
|
49
|
|
|
49
|
|
99
|
my($parser, $message, $near, $line) = @_; |
1989
|
|
|
|
|
|
|
|
1990
|
49
|
|
100
|
|
|
330
|
$near ||= $parser->near_token || ";"; |
|
|
|
66
|
|
|
|
|
1991
|
49
|
100
|
100
|
|
|
215
|
if($near ne ";" && $message !~ /\b \Q$near\E \b/xms) { |
1992
|
39
|
|
|
|
|
117
|
$message .= ", near $near"; |
1993
|
|
|
|
|
|
|
} |
1994
|
49
|
|
66
|
|
|
557
|
die $parser->make_error($message . ", while parsing templates", |
1995
|
|
|
|
|
|
|
$parser->file, $line || $parser->line); |
1996
|
|
|
|
|
|
|
} |
1997
|
|
|
|
|
|
|
|
1998
|
172
|
|
|
172
|
|
1675
|
no Mouse; |
|
172
|
|
|
|
|
351
|
|
|
172
|
|
|
|
|
1387
|
|
1999
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
2000
|
|
|
|
|
|
|
__END__ |