line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Lox::Parser; |
2
|
1
|
|
|
1
|
|
18
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
24
|
|
3
|
1
|
|
|
1
|
|
13
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
27
|
|
4
|
1
|
|
|
1
|
|
4
|
use Lox::Bool; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
70
|
|
5
|
1
|
|
|
1
|
|
376
|
use Lox::Expr; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
23
|
|
6
|
1
|
|
|
1
|
|
5
|
use Lox::Nil; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
64
|
|
7
|
1
|
|
|
1
|
|
341
|
use Lox::Stmt; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
22
|
|
8
|
1
|
|
|
1
|
|
324
|
use Lox::String; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
22
|
|
9
|
1
|
|
|
1
|
|
6
|
use Lox::TokenType; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
2945
|
|
10
|
|
|
|
|
|
|
our $VERSION = 0.02; |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
sub new { |
13
|
2
|
|
|
2
|
0
|
4
|
my ($class, $args) = @_; |
14
|
|
|
|
|
|
|
return bless { |
15
|
2
|
|
50
|
|
|
10
|
tokens => ($args->{tokens} || die 'requires an arrayref of tokens'), |
16
|
|
|
|
|
|
|
errors => [], |
17
|
|
|
|
|
|
|
current=> 0, |
18
|
|
|
|
|
|
|
}, $class; |
19
|
|
|
|
|
|
|
} |
20
|
|
|
|
|
|
|
|
21
|
148
|
|
|
148
|
0
|
186
|
sub tokens { $_[0]->{tokens} } |
22
|
2
|
|
|
2
|
0
|
5
|
sub errors :lvalue { $_[0]->{errors} } |
23
|
156
|
|
|
156
|
0
|
503
|
sub current :lvalue { $_[0]->{current} } |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
sub parse { |
26
|
2
|
|
|
2
|
0
|
3
|
my $self = shift; |
27
|
2
|
|
|
|
|
3
|
my @statements; |
28
|
2
|
|
|
|
|
4
|
while (!$self->is_at_end) { |
29
|
2
|
|
|
|
|
6
|
push @statements, $self->declaration; |
30
|
|
|
|
|
|
|
} |
31
|
2
|
|
|
|
|
13
|
return \@statements; |
32
|
|
|
|
|
|
|
} |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
sub declaration { |
35
|
2
|
|
|
2
|
0
|
13
|
my $self = shift; |
36
|
2
|
|
|
|
|
3
|
my $dec = eval { |
37
|
2
|
50
|
33
|
|
|
14
|
if ($self->check(FUN) && $self->check(IDENTIFIER, 1)) { |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
38
|
0
|
|
|
|
|
0
|
$self->advance; |
39
|
0
|
|
|
|
|
0
|
$self->function_stmt('function'); |
40
|
|
|
|
|
|
|
} |
41
|
|
|
|
|
|
|
elsif ($self->match(CLASS)) { |
42
|
0
|
|
|
|
|
0
|
$self->class_declaration; |
43
|
|
|
|
|
|
|
} |
44
|
1
|
|
|
|
|
3
|
elsif ($self->match(VAR)) { $self->var_declaration } |
45
|
1
|
|
|
|
|
4
|
else { $self->statement } |
46
|
|
|
|
|
|
|
}; |
47
|
2
|
50
|
|
|
|
6
|
unless ($@) { |
48
|
2
|
|
|
|
|
6
|
return $dec; |
49
|
|
|
|
|
|
|
} |
50
|
0
|
|
|
|
|
0
|
$self->synchronize; |
51
|
0
|
|
|
|
|
0
|
return undef; |
52
|
|
|
|
|
|
|
} |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
sub class_declaration { |
55
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
56
|
0
|
|
|
|
|
0
|
my $name = $self->consume(IDENTIFIER, 'Expect class name'); |
57
|
|
|
|
|
|
|
|
58
|
0
|
|
|
|
|
0
|
my $superclass = undef; |
59
|
0
|
0
|
|
|
|
0
|
if ($self->match(LESS)) { |
60
|
0
|
|
|
|
|
0
|
$self->consume(IDENTIFIER, 'Expect superclass name'); |
61
|
0
|
|
|
|
|
0
|
$superclass = Lox::Expr::Variable->new({name => $self->previous}); |
62
|
|
|
|
|
|
|
} |
63
|
|
|
|
|
|
|
|
64
|
0
|
|
|
|
|
0
|
$self->consume(LEFT_BRACE, 'Expect \'{\' before class body'); |
65
|
0
|
|
|
|
|
0
|
my @methods = (); |
66
|
0
|
|
0
|
|
|
0
|
while (!$self->check(RIGHT_BRACE) && !$self->is_at_end) { |
67
|
0
|
|
|
|
|
0
|
push @methods, $self->function_stmt('method'); |
68
|
|
|
|
|
|
|
} |
69
|
0
|
|
|
|
|
0
|
$self->consume(RIGHT_BRACE, 'Expect \'}\' after class body'); |
70
|
0
|
|
|
|
|
0
|
return Lox::Stmt::Class->new({ |
71
|
|
|
|
|
|
|
superclass => $superclass, |
72
|
|
|
|
|
|
|
methods => \@methods, |
73
|
|
|
|
|
|
|
name => $name, |
74
|
|
|
|
|
|
|
}); |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
sub statement { |
78
|
1
|
|
|
1
|
0
|
3
|
my $self = shift; |
79
|
1
|
50
|
|
|
|
3
|
if ($self->match(FOR)) { |
80
|
0
|
|
|
|
|
0
|
return $self->for_statement; |
81
|
|
|
|
|
|
|
} |
82
|
1
|
50
|
|
|
|
4
|
if ($self->match(BREAK)) { |
83
|
0
|
|
|
|
|
0
|
return $self->break_statement; |
84
|
|
|
|
|
|
|
} |
85
|
1
|
50
|
|
|
|
3
|
if ($self->match(IF)) { |
86
|
0
|
|
|
|
|
0
|
return $self->if_statement; |
87
|
|
|
|
|
|
|
} |
88
|
1
|
50
|
|
|
|
3
|
if ($self->match(WHILE)) { |
89
|
0
|
|
|
|
|
0
|
return $self->while_statement; |
90
|
|
|
|
|
|
|
} |
91
|
1
|
50
|
|
|
|
3
|
if ($self->match(LEFT_BRACE)) { |
92
|
0
|
|
|
|
|
0
|
return Lox::Stmt::Block->new({statements => $self->block}); |
93
|
|
|
|
|
|
|
} |
94
|
1
|
50
|
|
|
|
3
|
if ($self->match(PRINT)) { |
95
|
1
|
|
|
|
|
3
|
return $self->print_statement; |
96
|
|
|
|
|
|
|
} |
97
|
0
|
0
|
|
|
|
0
|
if ($self->match(RETURN)) { |
98
|
0
|
|
|
|
|
0
|
return $self->return_statement; |
99
|
|
|
|
|
|
|
} |
100
|
0
|
|
|
|
|
0
|
return $self->expression_statement; |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
sub for_statement { |
104
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
105
|
0
|
|
|
|
|
0
|
$self->consume(LEFT_PAREN, "Expect '(' after 'for'"); |
106
|
|
|
|
|
|
|
|
107
|
0
|
|
|
|
|
0
|
my $initializer; |
108
|
0
|
0
|
|
|
|
0
|
if ($self->match(SEMICOLON)) { |
|
|
0
|
|
|
|
|
|
109
|
0
|
|
|
|
|
0
|
$initializer = undef; |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
elsif ($self->match(VAR)) { |
112
|
0
|
|
|
|
|
0
|
$initializer = $self->var_declaration; |
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
else { |
115
|
0
|
|
|
|
|
0
|
$initializer = $self->expression_statement; |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
0
|
|
|
|
|
0
|
my $condition = Lox::Expr::Literal->new({value => 1}); |
119
|
0
|
0
|
|
|
|
0
|
if (!$self->check(SEMICOLON)) { |
120
|
0
|
|
|
|
|
0
|
$condition = $self->expression; |
121
|
|
|
|
|
|
|
} |
122
|
0
|
|
|
|
|
0
|
$self->consume(SEMICOLON, 'Expect ";" after loop condition'); |
123
|
|
|
|
|
|
|
|
124
|
0
|
|
|
|
|
0
|
my $increment = undef; |
125
|
0
|
0
|
|
|
|
0
|
if (!$self->check(RIGHT_PAREN)) { |
126
|
0
|
|
|
|
|
0
|
$increment = $self->expression; |
127
|
|
|
|
|
|
|
} |
128
|
0
|
|
|
|
|
0
|
$self->consume(RIGHT_PAREN, 'Expect ")" after for clauses'); |
129
|
|
|
|
|
|
|
|
130
|
0
|
|
|
|
|
0
|
$self->{looping}++; |
131
|
0
|
|
|
|
|
0
|
my $body = $self->statement; |
132
|
0
|
0
|
|
|
|
0
|
if ($increment) { |
133
|
0
|
|
|
|
|
0
|
$body = Lox::Stmt::Block->new({statements => |
134
|
|
|
|
|
|
|
[$body, Lox::Stmt::Expression->new({expression => $increment})]}); |
135
|
|
|
|
|
|
|
} |
136
|
0
|
|
|
|
|
0
|
$body = Lox::Stmt::While->new({condition => $condition, body => $body}); |
137
|
|
|
|
|
|
|
|
138
|
0
|
0
|
|
|
|
0
|
if ($initializer) { |
139
|
0
|
|
|
|
|
0
|
$body = Lox::Stmt::Block->new({statements => [$initializer, $body]}); |
140
|
|
|
|
|
|
|
} |
141
|
0
|
|
|
|
|
0
|
$self->{looping}--; |
142
|
|
|
|
|
|
|
|
143
|
0
|
|
|
|
|
0
|
return $body; |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
sub if_statement { |
147
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
148
|
0
|
|
|
|
|
0
|
$self->consume(LEFT_PAREN, "Expect '(' after 'if'"); |
149
|
0
|
|
|
|
|
0
|
my $condition = $self->expression; |
150
|
0
|
|
|
|
|
0
|
$self->consume(RIGHT_PAREN, "Expect ')' after condition"); |
151
|
|
|
|
|
|
|
|
152
|
0
|
|
|
|
|
0
|
my $then_branch = $self->statement; |
153
|
0
|
|
|
|
|
0
|
my $else_branch = undef; |
154
|
0
|
0
|
|
|
|
0
|
if ($self->match(ELSE)) { |
155
|
0
|
|
|
|
|
0
|
$else_branch = $self->statement; |
156
|
|
|
|
|
|
|
} |
157
|
0
|
|
|
|
|
0
|
return Lox::Stmt::If->new({ |
158
|
|
|
|
|
|
|
condition => $condition, |
159
|
|
|
|
|
|
|
then_branch => $then_branch, |
160
|
|
|
|
|
|
|
else_branch => $else_branch, |
161
|
|
|
|
|
|
|
}); |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
sub break_statement { |
165
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
166
|
|
|
|
|
|
|
$self->error($self->previous, 'Can only break out of loops') |
167
|
0
|
0
|
|
|
|
0
|
unless $self->{looping}; |
168
|
0
|
|
|
|
|
0
|
$self->consume(SEMICOLON, 'Expect ";" after "break"'); |
169
|
0
|
|
|
|
|
0
|
return Lox::Stmt::Break->new({}); |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
sub var_declaration { |
173
|
1
|
|
|
1
|
0
|
2
|
my $self = shift; |
174
|
1
|
|
|
|
|
3
|
my $name = $self->consume(IDENTIFIER, "Expect variable name"); |
175
|
1
|
|
|
|
|
2
|
my $init = undef; |
176
|
1
|
50
|
|
|
|
2
|
if ($self->match(EQUAL)) { |
177
|
1
|
|
|
|
|
3
|
$init = $self->expression; |
178
|
|
|
|
|
|
|
} |
179
|
1
|
|
|
|
|
2
|
$self->consume(SEMICOLON, 'Expect ";" after variable declaration'); |
180
|
1
|
|
|
|
|
12
|
return Lox::Stmt::Var->new({name => $name, initializer => $init}); |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
sub while_statement { |
184
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
185
|
0
|
|
|
|
|
0
|
$self->consume(LEFT_PAREN, "Expect '(' after 'while'"); |
186
|
0
|
|
|
|
|
0
|
my $condition = $self->expression; |
187
|
0
|
|
|
|
|
0
|
$self->consume(RIGHT_PAREN, "Expect ')' after condition"); |
188
|
0
|
|
|
|
|
0
|
$self->{looping}++; |
189
|
0
|
|
|
|
|
0
|
my $while = Lox::Stmt::While->new({ |
190
|
|
|
|
|
|
|
condition => $condition, |
191
|
|
|
|
|
|
|
body => $self->statement, |
192
|
|
|
|
|
|
|
}); |
193
|
0
|
|
|
|
|
0
|
$self->{looping}--; |
194
|
0
|
|
|
|
|
0
|
return $while; |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
sub expression_statement { |
198
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
199
|
0
|
|
|
|
|
0
|
my $value = $self->expression; |
200
|
0
|
|
|
|
|
0
|
$self->consume(SEMICOLON, 'Expect \';\' after expression'); |
201
|
0
|
|
|
|
|
0
|
return Lox::Stmt::Expression->new({expression => $value}); |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
sub function_stmt { |
205
|
0
|
|
|
0
|
0
|
0
|
my ($self, $kind) = @_; |
206
|
0
|
|
|
|
|
0
|
my $name = $self->consume(IDENTIFIER, "Expect $kind name"); |
207
|
0
|
|
|
|
|
0
|
my ($parameters, $body) = $self->parse_function($kind); |
208
|
0
|
|
|
|
|
0
|
return Lox::Stmt::Function->new({ |
209
|
|
|
|
|
|
|
name => $name, |
210
|
|
|
|
|
|
|
params => $parameters, |
211
|
|
|
|
|
|
|
body => $body, |
212
|
|
|
|
|
|
|
}); |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
sub function_expr { |
216
|
0
|
|
|
0
|
0
|
0
|
my ($self) = @_; |
217
|
0
|
|
|
|
|
0
|
my ($parameters, $body) = $self->parse_function('lambda'); |
218
|
0
|
|
|
|
|
0
|
return Lox::Expr::Function->new({ |
219
|
|
|
|
|
|
|
params => $parameters, |
220
|
|
|
|
|
|
|
body => $body, |
221
|
|
|
|
|
|
|
}); |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
sub parse_function { |
225
|
0
|
|
|
0
|
0
|
0
|
my ($self, $kind) = @_; |
226
|
0
|
|
|
|
|
0
|
$self->consume(LEFT_PAREN, "Expect '(' after $kind declaration"); |
227
|
0
|
|
|
|
|
0
|
my @parameters; |
228
|
0
|
0
|
|
|
|
0
|
if (!$self->check(RIGHT_PAREN)) { |
229
|
0
|
|
|
|
|
0
|
do { |
230
|
0
|
0
|
|
|
|
0
|
if (@parameters >= 255) { |
231
|
0
|
|
|
|
|
0
|
$self->error($self->peek, "Cannot have more than 255 parameters"); |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
|
234
|
0
|
|
|
|
|
0
|
push @parameters, $self->consume(IDENTIFIER, "Expect parameter name"); |
235
|
|
|
|
|
|
|
} while ($self->match(COMMA)); |
236
|
|
|
|
|
|
|
} |
237
|
0
|
|
|
|
|
0
|
$self->consume(RIGHT_PAREN, "Expect ')' after parameters"); |
238
|
|
|
|
|
|
|
|
239
|
0
|
|
|
|
|
0
|
$self->consume(LEFT_BRACE, "Expect '{' before $kind body"); |
240
|
0
|
|
|
|
|
0
|
$self->{functioning}++; |
241
|
0
|
|
|
|
|
0
|
my $body = $self->block; |
242
|
0
|
|
|
|
|
0
|
$self->{functioning}--; |
243
|
0
|
|
|
|
|
0
|
return \@parameters, $body; |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
sub block { |
247
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
248
|
0
|
|
|
|
|
0
|
my @statements; |
249
|
0
|
|
0
|
|
|
0
|
while (!$self->check(RIGHT_BRACE) && !$self->is_at_end) { |
250
|
0
|
|
|
|
|
0
|
push @statements, $self->declaration; |
251
|
|
|
|
|
|
|
} |
252
|
0
|
|
|
|
|
0
|
$self->consume(RIGHT_BRACE, "Expect '}' after block"); |
253
|
0
|
|
|
|
|
0
|
return \@statements; |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
sub print_statement { |
257
|
1
|
|
|
1
|
0
|
3
|
my $self = shift; |
258
|
1
|
|
|
|
|
2
|
my $value = $self->expression; |
259
|
1
|
|
|
|
|
2
|
$self->consume(SEMICOLON, 'Expect ";" after value'); |
260
|
1
|
|
|
|
|
9
|
return Lox::Stmt::Print->new({expression => $value}); |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
sub return_statement { |
264
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
265
|
0
|
|
|
|
|
0
|
my $keyword = $self->previous; |
266
|
|
|
|
|
|
|
$self->error($keyword, 'Can only return from functions') |
267
|
0
|
0
|
|
|
|
0
|
unless $self->{functioning}; |
268
|
0
|
0
|
|
|
|
0
|
my $value = $self->check(SEMICOLON) ? undef : $self->expression; |
269
|
0
|
|
|
|
|
0
|
$self->consume(SEMICOLON, 'Expect ";" after return value'); |
270
|
0
|
|
|
|
|
0
|
return Lox::Stmt::Return->new({keyword => $keyword, value => $value}); |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
sub assignment { |
274
|
2
|
|
|
2
|
0
|
4
|
my $self = shift; |
275
|
2
|
|
|
|
|
4
|
my $expr = $self->_or; |
276
|
2
|
50
|
|
|
|
4
|
if ($self->match(EQUAL)) { |
277
|
0
|
|
|
|
|
0
|
my $equals = $self->previous; |
278
|
0
|
|
|
|
|
0
|
my $value = $self->assignment; |
279
|
|
|
|
|
|
|
# we parsed the left side THEN found an equals sign |
280
|
|
|
|
|
|
|
# returns a new expr using the left side input |
281
|
0
|
0
|
|
|
|
0
|
if (ref $expr eq 'Lox::Expr::Variable') { |
|
|
0
|
|
|
|
|
|
282
|
0
|
|
|
|
|
0
|
return Lox::Expr::Assign->new({name => $expr->name, value => $value}); |
283
|
|
|
|
|
|
|
} |
284
|
|
|
|
|
|
|
elsif (ref $expr eq 'Lox::Expr::Get') { |
285
|
0
|
|
|
|
|
0
|
return Lox::Expr::Set->new({ |
286
|
|
|
|
|
|
|
object => $expr->object, |
287
|
|
|
|
|
|
|
value => $value, |
288
|
|
|
|
|
|
|
name => $expr->name, |
289
|
|
|
|
|
|
|
}); |
290
|
|
|
|
|
|
|
} |
291
|
0
|
|
|
|
|
0
|
$self->error($equals, 'Invalid assignment target'); |
292
|
|
|
|
|
|
|
} |
293
|
2
|
|
|
|
|
5
|
return $expr; |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
sub _or { |
297
|
2
|
|
|
2
|
|
3
|
my $self = shift; |
298
|
2
|
|
|
|
|
3
|
my $expr = $self->_and; |
299
|
|
|
|
|
|
|
|
300
|
2
|
|
|
|
|
3
|
while ($self->match(OR)) { |
301
|
0
|
|
|
|
|
0
|
$expr = Lox::Expr::Logical->new({ |
302
|
|
|
|
|
|
|
left => $expr, |
303
|
|
|
|
|
|
|
operator => $self->previous, |
304
|
|
|
|
|
|
|
right => $self->_and, |
305
|
|
|
|
|
|
|
}); |
306
|
|
|
|
|
|
|
} |
307
|
2
|
|
|
|
|
3
|
return $expr; |
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
sub _and { |
311
|
2
|
|
|
2
|
|
2
|
my $self = shift; |
312
|
2
|
|
|
|
|
4
|
my $expr = $self->equality; |
313
|
|
|
|
|
|
|
|
314
|
2
|
|
|
|
|
3
|
while ($self->match(AND)) { |
315
|
0
|
|
|
|
|
0
|
$expr = Lox::Expr::Logical->new({ |
316
|
|
|
|
|
|
|
left => $expr, |
317
|
|
|
|
|
|
|
operator => $self->previous, |
318
|
|
|
|
|
|
|
right => $self->_and, |
319
|
|
|
|
|
|
|
}); |
320
|
|
|
|
|
|
|
} |
321
|
2
|
|
|
|
|
3
|
return $expr; |
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
|
324
|
2
|
|
|
2
|
0
|
4
|
sub expression { shift->assignment } |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
sub equality { |
327
|
2
|
|
|
2
|
0
|
3
|
my $self = shift; |
328
|
2
|
|
|
|
|
5
|
my $expr = $self->comparison; |
329
|
2
|
|
|
|
|
4
|
while ($self->match(BANG_EQUAL, EQUAL_EQUAL)) { |
330
|
0
|
|
|
|
|
0
|
$expr = Lox::Expr::Binary->new({ |
331
|
|
|
|
|
|
|
left => $expr, |
332
|
|
|
|
|
|
|
operator => $self->previous, |
333
|
|
|
|
|
|
|
right => $self->comparison, |
334
|
|
|
|
|
|
|
}); |
335
|
|
|
|
|
|
|
} |
336
|
2
|
|
|
|
|
4
|
return $expr; |
337
|
|
|
|
|
|
|
} |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
sub comparison { |
340
|
2
|
|
|
2
|
0
|
2
|
my $self = shift; |
341
|
2
|
|
|
|
|
5
|
my $expr = $self->addition; |
342
|
2
|
|
|
|
|
4
|
while ($self->match(GREATER, GREATER_EQUAL, LESS, LESS_EQUAL)) { |
343
|
0
|
|
|
|
|
0
|
$expr = Lox::Expr::Binary->new({ |
344
|
|
|
|
|
|
|
left => $expr, |
345
|
|
|
|
|
|
|
operator => $self->previous, |
346
|
|
|
|
|
|
|
right => $self->addition, |
347
|
|
|
|
|
|
|
}); |
348
|
|
|
|
|
|
|
} |
349
|
2
|
|
|
|
|
4
|
return $expr; |
350
|
|
|
|
|
|
|
} |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
sub addition { |
353
|
2
|
|
|
2
|
0
|
2
|
my $self = shift; |
354
|
2
|
|
|
|
|
5
|
my $expr = $self->multiplication; |
355
|
2
|
|
|
|
|
3
|
while ($self->match(MINUS, PLUS)) { |
356
|
0
|
|
|
|
|
0
|
$expr = Lox::Expr::Binary->new({ |
357
|
|
|
|
|
|
|
left => $expr, |
358
|
|
|
|
|
|
|
operator => $self->previous, |
359
|
|
|
|
|
|
|
right => $self->multiplication, |
360
|
|
|
|
|
|
|
}); |
361
|
|
|
|
|
|
|
} |
362
|
2
|
|
|
|
|
3
|
return $expr; |
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
sub multiplication { |
366
|
2
|
|
|
2
|
0
|
10
|
my $self = shift; |
367
|
2
|
|
|
|
|
6
|
my $expr = $self->unary; |
368
|
2
|
|
|
|
|
11
|
while ($self->match(SLASH, STAR)) { |
369
|
0
|
|
|
|
|
0
|
$expr = Lox::Expr::Binary->new({ |
370
|
|
|
|
|
|
|
left => $expr, |
371
|
|
|
|
|
|
|
operator => $self->previous, |
372
|
|
|
|
|
|
|
right => $self->unary, |
373
|
|
|
|
|
|
|
}); |
374
|
|
|
|
|
|
|
} |
375
|
2
|
|
|
|
|
4
|
return $expr; |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
sub unary { |
379
|
2
|
|
|
2
|
0
|
2
|
my $self = shift; |
380
|
2
|
50
|
|
|
|
16
|
if ($self->match(BANG, MINUS)) { |
381
|
0
|
|
|
|
|
0
|
my $expr = Lox::Expr::Unary->new({ |
382
|
|
|
|
|
|
|
operator => $self->previous, |
383
|
|
|
|
|
|
|
right => $self->unary, |
384
|
|
|
|
|
|
|
}); |
385
|
0
|
|
|
|
|
0
|
return $expr; |
386
|
|
|
|
|
|
|
} |
387
|
2
|
|
|
|
|
5
|
return $self->call; |
388
|
|
|
|
|
|
|
} |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
sub call { |
391
|
2
|
|
|
2
|
0
|
4
|
my $self = shift; |
392
|
2
|
|
|
|
|
3
|
my $expr = $self->primary; |
393
|
2
|
|
|
|
|
4
|
while (1) { |
394
|
2
|
50
|
|
|
|
3
|
if ($self->match(LEFT_PAREN)) { |
|
|
50
|
|
|
|
|
|
395
|
0
|
|
|
|
|
0
|
$expr = $self->finish_call($expr); |
396
|
|
|
|
|
|
|
} |
397
|
|
|
|
|
|
|
elsif ($self->match(DOT)) { |
398
|
0
|
|
|
|
|
0
|
$expr = Lox::Expr::Get->new({ |
399
|
|
|
|
|
|
|
object => $expr, |
400
|
|
|
|
|
|
|
name => $self->consume(IDENTIFIER,'Expect property name after \'.\''), |
401
|
|
|
|
|
|
|
}); |
402
|
|
|
|
|
|
|
} |
403
|
|
|
|
|
|
|
else { |
404
|
2
|
|
|
|
|
4
|
last; |
405
|
|
|
|
|
|
|
} |
406
|
|
|
|
|
|
|
} |
407
|
2
|
|
|
|
|
8
|
return $expr; |
408
|
|
|
|
|
|
|
} |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
sub finish_call { |
411
|
0
|
|
|
0
|
0
|
0
|
my ($self, $callee) = @_; |
412
|
0
|
|
|
|
|
0
|
my @args; |
413
|
0
|
0
|
|
|
|
0
|
if (!$self->check(RIGHT_PAREN)) { |
414
|
0
|
|
|
|
|
0
|
do { |
415
|
0
|
0
|
|
|
|
0
|
$self->error($self->peek, 'Cannot have more than 255 arguments') |
416
|
|
|
|
|
|
|
if @args >= 255; |
417
|
0
|
|
|
|
|
0
|
push @args, $self->expression; |
418
|
|
|
|
|
|
|
} while ($self->match(COMMA)); |
419
|
|
|
|
|
|
|
} |
420
|
0
|
|
|
|
|
0
|
my $paren = $self->consume(RIGHT_PAREN, 'Expect ")" after arguments'); |
421
|
0
|
|
|
|
|
0
|
return Lox::Expr::Call->new({ |
422
|
|
|
|
|
|
|
arguments => \@args, |
423
|
|
|
|
|
|
|
callee => $callee, |
424
|
|
|
|
|
|
|
paren => $paren, |
425
|
|
|
|
|
|
|
}); |
426
|
|
|
|
|
|
|
} |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
sub primary { |
429
|
2
|
|
|
2
|
0
|
3
|
my $self = shift; |
430
|
2
|
50
|
|
|
|
3
|
if ($self->match(FALSE)) { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
431
|
0
|
|
|
|
|
0
|
return Lox::Expr::Literal->new({value => $False}); |
432
|
|
|
|
|
|
|
} |
433
|
|
|
|
|
|
|
elsif ($self->match(TRUE)) { |
434
|
0
|
|
|
|
|
0
|
return Lox::Expr::Literal->new({value => $True}); |
435
|
|
|
|
|
|
|
} |
436
|
|
|
|
|
|
|
elsif ($self->match(NIL)) { |
437
|
0
|
|
|
|
|
0
|
return Lox::Expr::Literal->new({value => $Nil}); |
438
|
|
|
|
|
|
|
} |
439
|
|
|
|
|
|
|
elsif ($self->match(NUMBER)) { |
440
|
0
|
|
|
|
|
0
|
return Lox::Expr::Literal->new({value => $self->previous->{literal}}); |
441
|
|
|
|
|
|
|
} |
442
|
|
|
|
|
|
|
elsif ($self->match(STRING)) { |
443
|
1
|
|
|
|
|
4
|
return Lox::Expr::Literal->new({value => Lox::String->new($self->previous->{literal})}); |
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
elsif ($self->match(SUPER)) { |
446
|
0
|
|
|
|
|
0
|
my $keyword = $self->previous; |
447
|
0
|
|
|
|
|
0
|
$self->consume(DOT, 'Expect \'.\' after \'super\''); |
448
|
0
|
|
|
|
|
0
|
my $method = $self->consume(IDENTIFIER, 'Expect superclass method name'); |
449
|
0
|
|
|
|
|
0
|
return Lox::Expr::Super->new({keyword => $keyword, method => $method}); |
450
|
|
|
|
|
|
|
} |
451
|
|
|
|
|
|
|
elsif ($self->match(THIS)) { |
452
|
0
|
|
|
|
|
0
|
return Lox::Expr::This->new({keyword => $self->previous}); |
453
|
|
|
|
|
|
|
} |
454
|
|
|
|
|
|
|
elsif ($self->match(IDENTIFIER)) { |
455
|
1
|
|
|
|
|
2
|
return Lox::Expr::Variable->new({name => $self->previous}); |
456
|
|
|
|
|
|
|
} |
457
|
|
|
|
|
|
|
elsif ($self->match(LEFT_PAREN)) { |
458
|
0
|
|
|
|
|
0
|
my $expr = $self->expression; |
459
|
0
|
|
|
|
|
0
|
$self->consume(RIGHT_PAREN, 'Expect ")" after expression'); |
460
|
0
|
|
|
|
|
0
|
return Lox::Expr::Grouping->new({expression => $expr}); |
461
|
|
|
|
|
|
|
} |
462
|
|
|
|
|
|
|
elsif ($self->match(FUN)) { |
463
|
0
|
|
|
|
|
0
|
return $self->function_expr; |
464
|
|
|
|
|
|
|
} |
465
|
0
|
|
|
|
|
0
|
$self->error($self->peek, 'Expect expression'); |
466
|
|
|
|
|
|
|
} |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
sub match { |
469
|
44
|
|
|
44
|
0
|
74
|
my ($self, @types) = @_; |
470
|
44
|
|
|
|
|
49
|
for my $t (@types) { |
471
|
58
|
100
|
|
|
|
68
|
if ($self->check($t)) { |
472
|
5
|
|
|
|
|
10
|
$self->advance; |
473
|
5
|
|
|
|
|
13
|
return 1; |
474
|
|
|
|
|
|
|
} |
475
|
|
|
|
|
|
|
} |
476
|
39
|
|
|
|
|
89
|
return undef; |
477
|
|
|
|
|
|
|
} |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
sub consume { |
480
|
3
|
|
|
3
|
0
|
13
|
my ($self, $type, $msg) = @_; |
481
|
3
|
50
|
|
|
|
6
|
return $self->advance if $self->check($type); |
482
|
0
|
|
|
|
|
0
|
$self->error($self->peek, $msg); |
483
|
|
|
|
|
|
|
} |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
sub check { |
486
|
63
|
|
|
63
|
0
|
72
|
my ($self, $type, $offset) = @_; |
487
|
63
|
50
|
|
|
|
78
|
return $self->is_at_end ? undef : $self->peek($offset)->{type} == $type; |
488
|
|
|
|
|
|
|
} |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
sub advance { |
491
|
8
|
|
|
8
|
0
|
8
|
my $self = shift; |
492
|
8
|
50
|
|
|
|
9
|
$self->current++ unless $self->is_at_end; |
493
|
8
|
|
|
|
|
14
|
return $self->previous; |
494
|
|
|
|
|
|
|
} |
495
|
|
|
|
|
|
|
|
496
|
75
|
|
|
75
|
0
|
88
|
sub is_at_end { shift->peek->{type} == EOF } |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
sub peek { |
499
|
138
|
|
|
138
|
0
|
149
|
my ($self, $offset) = @_; |
500
|
138
|
|
50
|
|
|
164
|
return $self->tokens->[ $self->current + ($offset//0) ]; |
501
|
|
|
|
|
|
|
} |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
sub previous { |
504
|
10
|
|
|
10
|
0
|
11
|
my $self = shift; |
505
|
10
|
|
|
|
|
12
|
return $self->tokens->[ $self->current - 1]; |
506
|
|
|
|
|
|
|
} |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
sub error { |
509
|
0
|
|
|
0
|
0
|
|
my ($self, $token, $msg) = @_; |
510
|
0
|
|
|
|
|
|
push $self->errors->@*, [$token, $msg]; |
511
|
0
|
|
|
|
|
|
die $msg; |
512
|
|
|
|
|
|
|
} |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
sub synchronize { |
515
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
516
|
0
|
|
|
|
|
|
$self->advance; |
517
|
0
|
|
|
|
|
|
while (!$self->is_at_end) { |
518
|
0
|
0
|
|
|
|
|
return if $self->previous->{type} == SEMICOLON; |
519
|
0
|
|
|
|
|
|
my $next = $self->peek; |
520
|
0
|
0
|
|
|
|
|
return if grep { $next == $_ } CLASS,FUN,VAR,FOR,IF,WHILE,PRINT,RETURN; |
|
0
|
|
|
|
|
|
|
521
|
0
|
|
|
|
|
|
$self->advance; |
522
|
|
|
|
|
|
|
} |
523
|
|
|
|
|
|
|
} |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
1; |