violet 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1440 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module Violet
4
+ # Internal: Records errors emitted by the parser.
5
+ class ParserError < Error
6
+ # Public: Gets the malformed `Token` associated with this error.
7
+ attr_reader :token
8
+
9
+ # Public: Creates a new `ParserError`.
10
+ #
11
+ # message - The error message.
12
+ # token - The malformed `Token`.
13
+ def initialize(message, token)
14
+ @token = token
15
+ super message
16
+ end
17
+ end
18
+
19
+ # Public: Parses a JavaScript source string.
20
+ class Parser
21
+ # Public: Parses a string of JavaScript source code.
22
+ #
23
+ # source - The source `String`.
24
+ #
25
+ # Returns the resulting `Token` stream as an `Array`.
26
+ def self.parse(source)
27
+ new(source).parse
28
+ end
29
+
30
+ # Public: The future reserved words specified in section 7.6.1.2.
31
+ FUTURE_RESERVED_WORDS = %w( class const enum export extends import super )
32
+
33
+ # Public: The reserved keywords specified in section 7.6.1.1, excluding
34
+ # `this` and the four unary operators.
35
+ KEYWORDS = %w( break case catch continue debugger default do else finally for function if in instanceof return switch throw try var while with )
36
+
37
+ # Public: The `true`, `false`, and `null` literals specified in section 7.8.
38
+ # The `this` keyword is grouped with the literals for convenience.
39
+ LITERALS = %w( this null true false )
40
+
41
+ # Public: The four unary keyword operators.
42
+ UNARY_KEYWORDS = %w( delete void typeof new )
43
+
44
+ # Public: A list of reserved words that may not be used as labels or
45
+ # function, argument, or variable names. Comprises the future reserved
46
+ # words, keywords, and literals.
47
+ KEYWORD_OR_RESERVED = FUTURE_RESERVED_WORDS.dup.push(*KEYWORDS, *LITERALS, *UNARY_KEYWORDS)
48
+
49
+ # Public: Matches valid left-hand side start operators.
50
+ LHS_START = /[-+~!({\[]/
51
+
52
+ # Public: Matches unary operators.
53
+ UNARY_OPERATORS = /[-+~!]/
54
+
55
+ # Public: Matches assignment operators.
56
+ ASSIGNMENTS = /^[\+\-\*\%\&\|\^\/]?=$|^\<\<\=$|^\>{2,3}\=$/
57
+
58
+ # Public: Matches binary operators.
59
+ BINARY_OPERATORS = /^[\+\-\*\%\|\^\&\?\/]$|^[\<\>]\=?$|^[\=\!]\=\=?$|^\<\<|\>\>\>?$|^\&\&$|^\|\|$/
60
+
61
+ # Internal: The error message produced when a statement cannot be parsed.
62
+ STATEMENT_ERROR = "Expected a statement."
63
+
64
+ # Public: Gets the `Lexer` instance associated with this parser.
65
+ attr_reader :lexer
66
+
67
+ # Public: Creates a new `Parser` with a source string.
68
+ #
69
+ # source - The source `String`.
70
+ def initialize(source)
71
+ @lexer = Lexer.new(source)
72
+ @tokens = []
73
+ @exception = nil
74
+ @try = false
75
+ end
76
+
77
+ # Public: Parses the JavaScript source string associated with this parser
78
+ # instance.
79
+ #
80
+ # Returns the token stream as an `Array`.
81
+ def parse
82
+ continue false
83
+ @tokens
84
+ end
85
+
86
+ # Internal: Stores the token in the parser token stream.
87
+ #
88
+ # token - The token to store.
89
+ #
90
+ # Returns nothing.
91
+ def save(token)
92
+ @tokens << token
93
+ nil
94
+ end
95
+
96
+ # Internal: Lexes the next non-whitespace token.
97
+ #
98
+ # pattern - If the token is `/` or `/=`, specifies whether it may be lexed
99
+ # as part of a regular expression. If `false`, the token will be lexed as
100
+ # a division operator instead (default: false).
101
+ #
102
+ # Returns the lexed `Token`.
103
+ def get(pattern = false)
104
+ # If the parser encountered an exception, the `@exception` instance
105
+ # variable will be set to the token that triggered the error.
106
+ if @exception
107
+ token, @exception = @exception, nil
108
+ return token
109
+ end
110
+
111
+ # Mark lines for automatic semicolon insertion.
112
+ multiline = false
113
+
114
+ # Consume tokens until a non-whitespace token is encountered.
115
+ loop do
116
+ token = lexer.lex pattern
117
+ # The end-of-file token is not stored in the syntax tree.
118
+ return token if token[:name] == 12
119
+ multiline ||= true if token[:lines] && token[:lines] > 0
120
+
121
+ # If the lexed token is a whitespace token, save it and continue
122
+ # consuming tokens. Otherwise, break and return the token. Note
123
+ # that the non-whitespace token is **not** automatically saved.
124
+ if token[:isWhite]
125
+ save token
126
+ else
127
+ break
128
+ end
129
+ end
130
+
131
+ # If multiple lines were lexed, mark the token for automatic semicolon
132
+ # insertion.
133
+ token[:asi] = true if multiline
134
+ token
135
+ end
136
+
137
+ # Internal: Recursively parses tokens until the source string is consumed.
138
+ #
139
+ # token - The current token.
140
+ #
141
+ # Returns the token immediately before the end-of-file mark.
142
+ def continue(token)
143
+ save token if token
144
+ # Parse as many tokens as possible. `token` should be the end-of-file mark
145
+ # following the call to `parse_source_elements`. If it is not, the source
146
+ # is malformed and requires additional parsing.
147
+ token = parse_source_elements get(:pattern)
148
+ parsed = false
149
+ loop do
150
+ unless token[:name] == 12
151
+ # Add a generic error to the token stream unless the current token
152
+ # already contains an error.
153
+ unless token[:name] == 14
154
+ fail "Unexpected token.", token
155
+ end
156
+ # Save the malformed token as-is and continue parsing.
157
+ save token
158
+ token = get :pattern
159
+ parsed = true
160
+ end
161
+ break unless token[:name] == 14
162
+ end
163
+ # Recursively parse the source until the end-of-file mark is reached.
164
+ token = continue(token) if token[:name] != 12 && parsed
165
+ # If an error occured at the end of the file, add it to the token stream.
166
+ if @exception
167
+ save @exception
168
+ @exception = nil
169
+ end
170
+ token
171
+ end
172
+
173
+ # Internal: Parses a function declaration.
174
+ #
175
+ # token - The current token.
176
+ #
177
+ # Returns the token immediately following the function body.
178
+ def parse_function_declaration(token)
179
+ save token
180
+ token = get :pattern
181
+ if token[:name] != 2
182
+ token = warn "Function declarations cannot be anonymous.", token
183
+ elsif KEYWORD_OR_RESERVED.include? token[:value]
184
+ token = warn "Function names may not be keywords or future reserved words.", token
185
+ end
186
+ # The subsequent token marks the beginning of the argument list.
187
+ save token
188
+ token = get :pattern
189
+ # Parse the function arguments and body.
190
+ parse_function_arguments(token, :pattern)
191
+ end
192
+
193
+ # Internal: Parses an arguments list.
194
+ #
195
+ # token - The current token.
196
+ # pattern - If the `/` or `/=` token immediately follows the function body,
197
+ # this boolean argument specifies whether it may be lexed as part of a
198
+ # regular expression (default: false).
199
+ #
200
+ # Returns the token immediately following the arguments list.
201
+ def parse_function_arguments(token, pattern = false)
202
+ unless token[:value] == ?(
203
+ token = warn "Expected to see an opening parenthesis after the function name.", token
204
+ end
205
+ save token
206
+ token = get :pattern
207
+
208
+ if token[:name] == 2
209
+ if KEYWORD_OR_RESERVED.include? token[:value]
210
+ fail "Function argument names may not be keywords or future reserved words.", token
211
+ end
212
+ save token
213
+ token = get :pattern
214
+
215
+ # Parse the argument list.
216
+ while token[:value] == ?,
217
+ save token
218
+ token = get :pattern
219
+ if token[:name] != 2
220
+ fail "Function argument names must be identifiers.", token
221
+ elsif KEYWORD_OR_RESERVED.include? token[:value]
222
+ fail "Function argument names may not be keywords or future reserved words.", token
223
+ end
224
+ save token
225
+ token = get :pattern
226
+ end
227
+ end
228
+ unless token[:value] == ?)
229
+ token = warn "Expected to see a closing parenthesis after the list of arguments.", token
230
+ end
231
+ save token
232
+ token = get :pattern
233
+ parse_function_body(token, pattern)
234
+ end
235
+
236
+ # Internal: Parses a function body.
237
+ #
238
+ # token - The current token.
239
+ # pattern - If the `/` or `/=` token immediately follows the function body,
240
+ # this boolean argument specifies whether it may be lexed as part of a
241
+ # regular expression (default: false).
242
+ #
243
+ # Returns the token immediately following the function body.
244
+ def parse_function_body(token, pattern = false)
245
+ unless token[:value] == ?{
246
+ token = warn "Expected `{` after the function header.", token
247
+ end
248
+ save token
249
+ token = get :pattern
250
+ token = parse_source_elements token
251
+ unless token[:value] == ?}
252
+ token = warn "Expected `}` after the function body.", token
253
+ end
254
+ save token
255
+ get pattern
256
+ end
257
+
258
+ # Internal: A `Hash` containing the default options for the `expressions`
259
+ # method.
260
+ PARSE_EXPRESSIONS_DEFAULTS = {
261
+ # If the two initial tokens comprise an identifier followed by a colon
262
+ # and this option is set to `true`, the parser will treat the lexed
263
+ # token as a labeled statement. The identifier must not be a reserved
264
+ # word.
265
+ :label => false,
266
+ # Parses a single assignment expression if set to `true`.
267
+ :single => false,
268
+ # Parses the expression as part of a `for` loop header. If set to `true`,
269
+ # the `in` operator is disallowed.
270
+ :header => false,
271
+ # Parses the expression as an identifier following a `break` or
272
+ # `continue` statement.
273
+ :identifier => false
274
+ }
275
+
276
+ # Internal: Parses one or more assignment expressions or a labeled
277
+ # statement.
278
+ #
279
+ # token - The current token.
280
+ # options - The expression parsing options.
281
+ #
282
+ # Returns the token immediately following the last expression.
283
+ def parse_assignment_expressions(token, options = {})
284
+ # @kitcambridge: This method should be broken up into several smaller
285
+ # helper methods.
286
+ options = PARSE_EXPRESSIONS_DEFAULTS.merge(options)
287
+ initial = true
288
+
289
+ loop do
290
+ # Specifies whether the current expression is a reference. Once a non-
291
+ # reference expression is encountered, subsequent expressions may not
292
+ # contain assignments.
293
+ reference = true
294
+ unless initial
295
+ save token
296
+ token = get :pattern
297
+ # A left-hand side start expression must follow the initial
298
+ # expression.
299
+ unless token[:name] <= 6 || LHS_START.match(token[:value])
300
+ token = warn "Expected a left-hand side expression following `,`.", token
301
+ end
302
+ end
303
+
304
+ # The following loop begins by parsing the left-hand side expression.
305
+ # If an identifier is encountered, an assignment operator is parsed.
306
+ # Otherwise, unary expressions are consumed until a binary operator
307
+ # is encountered. If a `new` or `()` expression was parsed, assignments
308
+ # are permitted. The right-hand side is then parsed.
309
+ continue = true
310
+ while continue
311
+ unary = false
312
+ # A unary operator cannot be used as an identifier following a
313
+ # `break` or `continue` statement, as it is reserved.
314
+ unless options[:identifier]
315
+ loop do
316
+ unary = token[:value] && UNARY_KEYWORDS.include?(token[:value])
317
+ # Break if the token is not a unary keyword or operator.
318
+ break unless unary || token[:name] == 11 && UNARY_OPERATORS.match(token[:value])
319
+ token[:isUnaryOp] = true if unary
320
+ save token
321
+ token = get :pattern
322
+ # A left-hand side expression must follow a unary operator.
323
+ unless token[:name] <= 6 || LHS_START.match(token[:value])
324
+ token = warn "Expected a left-hand side expression.", token
325
+ end
326
+ end
327
+ end
328
+ # If a unary operator was encountered, subsequent expressions may not
329
+ # be parsed as labeled statements.
330
+ options[:label] = false if unary
331
+ # Specifies whether an assignment may follow a parsed expression.
332
+ assignment_accepted = false
333
+
334
+ case token[:value]
335
+ # Parse a grouped expression.
336
+ # ---------------------------
337
+ when ?(
338
+ save token
339
+ token = get :pattern
340
+ unless token[:name] <= 6 || LHS_START.match(token[:value])
341
+ token = warn "A grouped expression should start with a left-hand-side expression.", token
342
+ end
343
+ # Recursively consume comma-separated expressions. The final token
344
+ # should close the group.
345
+ token = parse_assignment_expressions token
346
+ unless token[:value] == ?)
347
+ token = warn "Unterminated grouped expression.", token
348
+ end
349
+ save token
350
+ # In this context, `/` and `/=` are interpreted as division
351
+ # operators, rather than regular expressions.
352
+ token = get
353
+ # If the grouped expression contains a valid reference and excludes
354
+ # the comma operator, it may precede an assignment. `(b) = 1` is
355
+ # valid; `(a, b) = 1` is not.
356
+ assignment_accepted = true
357
+ # Parse an array literal.
358
+ # -----------------------
359
+ when ?[
360
+ save token
361
+ token = get :pattern
362
+ # Leading commas are treated as elisions.
363
+ while token[:value] == ?,
364
+ save token
365
+ token = get :pattern
366
+ end
367
+ # Parse the contents of the array literal. `token` should contain
368
+ # the closing array punctuator at the end of the following loop.
369
+ until token[:value] == ?]
370
+ unless token[:name] <= 6 || LHS_START.match(token[:value]) || token[:name] == 14
371
+ token = warn "An array literal should start with a left-hand side expression.", token
372
+ end
373
+ # Recursively parse each expression stored in the array.
374
+ # Consecutive commas are treated as elisions.
375
+ token = parse_assignment_expressions token, :single => true
376
+ while token[:value] == ?,
377
+ save token
378
+ token = get :pattern
379
+ end
380
+ end
381
+ unless token[:value] == ?]
382
+ token = warn "Unterminated array literal.", token
383
+ end
384
+ save token
385
+ # Treat `/` and `/=` as the division and division assignment
386
+ # operators, respectively.
387
+ token = get
388
+ # The postfix increment and decrement operators cannot follow
389
+ # values that are not references.
390
+ while %w(++ --).include? token[:value]
391
+ fail "The unary increment (`++`) and decrement (`--`) operators cannot be applied to an array literal.", token
392
+ save token
393
+ token = get :pattern
394
+ end
395
+ # Parse an object literal.
396
+ # ------------------------
397
+ when ?{
398
+ save token
399
+ token = get :pattern
400
+ # Unexpected end-of-file mark encountered.
401
+ if token[:name] == 12
402
+ token = warn "A colon should precede every object property value.", token
403
+ end
404
+ # Parse object members until either the closing brace or a parse
405
+ # error is encountered.
406
+ until token[:value] == ?} || token[:name] == 14
407
+ unless token[:isNumber] || token[:isString] || token[:name] == 2
408
+ token = warn "Object literal property names can only be strings, numbers, or identifiers.", token
409
+ end
410
+ # The `accessor` token references the property name.
411
+ accessor = token[:value]
412
+ save token
413
+ token = get :pattern
414
+ # Parse attribute accessors-getters and setters.
415
+ case accessor
416
+ when "get"
417
+ # If the current token is the `:` delimiter, the object member
418
+ # is a standard property, not a getter.
419
+ if token[:value] == ?:
420
+ token = parse_object_literal_member token
421
+ else
422
+ # Parse the getter as a function.
423
+ unless token[:isNumber] || token[:isString] || token[:name] == 2
424
+ # @kitcambridge: This error token is not added to the token
425
+ # stream in Peter's original implementation.
426
+ token = warn "Getter names can only be strings, numbers, or identifiers.", token
427
+ end
428
+ save token
429
+ token = get :pattern
430
+ # Getters cannot accept arguments.
431
+ unless token[:value] == ?(
432
+ token = warn "The name of the getter should be followed by the opening parenthesis.", token
433
+ end
434
+ save token
435
+ token = get :pattern
436
+ unless token[:value] == ?)
437
+ token = warn "A getter cannot accept arguments.", token
438
+ end
439
+ save token
440
+ token = get :pattern
441
+ token = parse_function_body(token, :pattern)
442
+ end
443
+ when "set"
444
+ if token[:value] == ?:
445
+ # The member is a standard property, not a setter.
446
+ token = parse_object_literal_member token
447
+ else
448
+ unless token[:isNumber] || token[:isString] || token[:name] == 2
449
+ # @kitcambridge: This error token *is* added in Peter's
450
+ # implementation.
451
+ token = warn "Setter names can only be strings, numbers, or identifiers.", token
452
+ end
453
+ save token
454
+ token = get :pattern
455
+ unless token[:value] == ?(
456
+ token = warn "The name of the setter should be followed by the opening parenthesis.", token
457
+ end
458
+ save token
459
+ token = get :pattern
460
+ # Setters can accept only one argument.
461
+ unless token[:name] == 2
462
+ if token[:value] == ?)
463
+ token = warn "Setter functions must accept only one argument.", token
464
+ else
465
+ token = warn "The setter argument name must be an identifier.", token
466
+ end
467
+ end
468
+ save token
469
+ token = get :pattern
470
+ unless token[:value] == ?)
471
+ if token[:value] == ?,
472
+ token = warn "Setters may not accept multiple arguments.", token
473
+ else
474
+ token = warn "A closing parenthesis should follow the setter argument.", token
475
+ end
476
+ end
477
+ save token
478
+ token = get :pattern
479
+ token = parse_function_body(token, :pattern)
480
+ end
481
+ else
482
+ # Standard object member.
483
+ token = parse_object_literal_member token
484
+ end
485
+ # A single trailing comma is permitted.
486
+ if token[:value] == ?,
487
+ save token
488
+ token = get :pattern
489
+ if token[:value] == ?,
490
+ token = warn "Multiple trailing commas in object literals are not permitted.", token
491
+ end
492
+ elsif token[:value] != ?}
493
+ token = warn "Expected `,` or `}`. Unexpected member delimiter.", token
494
+ end
495
+ end
496
+ save token
497
+ # `/` and `/=` are treated as division operators.
498
+ token = get
499
+ # Object literals are not references.
500
+ while %w(++ --).include? token[:value]
501
+ fail "The unary increment (`++`) and decrement (`--`) operators cannot be applied to an object literal.", token
502
+ save token
503
+ token = get :pattern
504
+ end
505
+ # Parse a function expression.
506
+ # ----------------------------
507
+ when "function"
508
+ save token
509
+ token = get :pattern
510
+ # A label may precede a function expression or declaration.
511
+ if options[:label] && token[:value] == ?:
512
+ token = warn "Labels may not be keywords or future reserved words.", token
513
+ end
514
+ # Validate the function name, if one is found. Anonymous function
515
+ # expressions will omit the name.
516
+ if token[:name] == 2
517
+ if KEYWORD_OR_RESERVED.include?(token[:value])
518
+ token = warn "Function names may not be keywords or future reserved words.", token
519
+ end
520
+ save token
521
+ token = get :pattern
522
+ end
523
+ # Parse the function arguments and body. The returned token should
524
+ # be the token immediately following the closing brace of the body.
525
+ token = parse_function_arguments token
526
+ while %w(++ --).include? token[:value]
527
+ fail "The unary increment (`++`) and decrement (`--`) operators cannot be applied to a function.", token
528
+ save token
529
+ token = get :pattern
530
+ end
531
+ # Parse labels and error cases.
532
+ # -----------------------------
533
+ else
534
+ # Parse labels for right-hand side expressions.
535
+ if token[:name] <= 6
536
+ # Save the current token, which may be a label.
537
+ possible_label = token
538
+
539
+ # Validate the label identifier. This is not optional if the
540
+ # expression should be parsed as an identifier following a
541
+ # `break` or `continue` statement.
542
+ if token[:name] == 2
543
+ # @kitcambridge: Peter's original parser allows `LITERALS` to
544
+ # be used as label names; all popular implementations disagree.
545
+ # Removing this causes an unexpected token error to be emitted
546
+ # for standalone literals. @qfox/ZeParser#9.
547
+ if !LITERALS.include?(token[:value]) && KEYWORD_OR_RESERVED.include?(token[:value])
548
+ # A statement label is an identifier, which may not be a
549
+ # reserved word.
550
+ if options[:identifier]
551
+ fail "Statement labels passed to `break` and `continue` must be identifiers.", token
552
+ elsif token[:value] == "else"
553
+ fail "Dangling `else`.", token
554
+ else
555
+ # @kitcambridge: Peter is considering adding a lookahead to
556
+ # check for a trailing colon and emit a malformed label
557
+ # error. This may also solve issue #9 above.
558
+ fail "Unexpected token.", token
559
+ end
560
+ end
561
+ # Assignments are only accepted after member expressions:
562
+ # either an identifier or square brackets.
563
+ assignment_accepted = true
564
+ elsif options[:identifier]
565
+ # Malformed label.
566
+ token = warn "Statement labels passed to `break` and `continue` must be identifiers.", token
567
+ end
568
+ save token
569
+ # `/` and `/=` are treated as division operators.
570
+ token = get
571
+ # Check for a labeled statement. If the `label` option was
572
+ # specified and the current token is the `:` delimiter,
573
+ # the previous `possible_label` token must be an
574
+ # identifier (validation occured above; this routine merely
575
+ # catches malformed labels).
576
+ if options[:label] && token[:value] == ?:
577
+ unless possible_label[:name] == 2
578
+ fail "Label names must be identifiers.", token
579
+ end
580
+ save token
581
+ token = get :pattern
582
+ possible_label[:isLabel] = true
583
+ # If the label is valid, the subsequent token marks the
584
+ # beginning of the labeled statement. `token` should
585
+ # reference the first token following the statement.
586
+ token = parse_statement token
587
+ # If the statement could not be parsed, correct the error to
588
+ # account for the label.
589
+ if token[:error] && token[:error].message == STATEMENT_ERROR
590
+ token[:error] = ParserError.new("Expected a statement after the label.", token)
591
+ end
592
+ token[:wasLabel] = true
593
+ return token
594
+ end
595
+ options[:label] = false
596
+ # Lexer Errors.
597
+ # -------------
598
+ elsif token[:name] == 14
599
+ loop do
600
+ if token[:tokenError]
601
+ error = Token.new(lexer, :error, token[:start]...token[:start])
602
+ error[:error] = ParserError.new("Lexer Error: #{token[:error].message}", token)
603
+ save error
604
+ lexer.insert_before(error, token)
605
+ end
606
+ save token
607
+ token = get :pattern
608
+ break unless token[:name] == 14
609
+ end
610
+ # Unexpected End-of-File.
611
+ # -----------------------
612
+ elsif token[:name] == 12
613
+ return token
614
+ # Fail.
615
+ # -----
616
+ # If the token value is the closing curly brace (`}`), it is
617
+ # ignored. According to Peter, automatic semicolon insertion may be
618
+ # applied; alternatively, this may be part of an object literal.
619
+ # The other parsing routines will attempt to handle it.
620
+ elsif token[:value] != ?}
621
+ fail "Unexpected token.", token
622
+ save token
623
+ token = get :pattern
624
+ end
625
+ end
626
+ # Property Access and Call Expressions.
627
+ # -------------------------------------
628
+ while token[:value] == ?. || token[:value] == ?[ || token[:value] == ?(
629
+ # None of the characters may occur in an identifier.
630
+ if options[:identifier]
631
+ token = warn "Statement labels passed to `break` and `continue` must be identifiers.", token
632
+ end
633
+ case token[:value]
634
+ # Dot Member Operator.
635
+ # --------------------
636
+ when ?.
637
+ save token
638
+ token = get :pattern
639
+ # The referenced property name must be an identifier. ES 5 allows
640
+ # the use of reserved words as property names, so no additional
641
+ # checks are performed.
642
+ unless token[:name] == 2
643
+ fail "Property names following the dot member operator must be identifiers.", token
644
+ end
645
+ save token
646
+ # `/` and `/=` are treated as division operators.
647
+ token = get
648
+ # The result of a member access operation may be assigned to.
649
+ assignment_accepted = true
650
+ # Square Bracket Member Operator.
651
+ # -------------------------------
652
+ when ?[
653
+ save token
654
+ token = get :pattern
655
+ # The brackets must contain at least one left-hand side start
656
+ # expression.
657
+ unless token[:name] <= 6 || LHS_START.match(token[:value])
658
+ token = warn "Square brackets must contain an expression.", token
659
+ end
660
+ # Recursively parse assignment expressions. The final token should
661
+ # be the terminating bracket.
662
+ token = parse_assignment_expressions token
663
+ unless token[:value] == ?]
664
+ token = warn "Unterminated square bracket member accessor.", token
665
+ end
666
+ save token
667
+ # `/` and `/=` are treated as division operators.
668
+ token = get
669
+ assignment_accepted = true
670
+ # Call Expression.
671
+ # ----------------
672
+ when ?(
673
+ save token
674
+ token = get :pattern
675
+ # The current token marks either the closing parenthesis of an
676
+ # empty argument list, or the first argument, which must be a
677
+ # left-hand side start expression.
678
+ if token[:name] <= 6 || LHS_START.match(token[:value])
679
+ # Recursively parse the arguments, which may be assignment
680
+ # expressions. The comma is used as a separator, not as an
681
+ # operator.
682
+ token = parse_assignment_expressions token
683
+ end
684
+ unless token[:value] == ?)
685
+ token = warn "Unterminated parentheses following the call expression.", token
686
+ end
687
+ save token
688
+ # `/` and `/=` are treated as division operators.
689
+ token = get
690
+ # The result of a function call may not be assigned to.
691
+ assignment_accepted = false
692
+ end
693
+ end
694
+ # Postfix unary increment and decrement operators.
695
+ if (token[:value] == "++" || token[:value] == "--") && !token[:asi]
696
+ if options[:identifier]
697
+ token = warn "Statement labels passed to `break` and `continue` must be identifiers.", token
698
+ end
699
+ save token
700
+ # `/` and `/=` are treated as division operators.
701
+ token = get
702
+ end
703
+
704
+ # Parse any subsequent operators.
705
+ # -------------------------------
706
+ loop do
707
+ conditional = false
708
+ # Parse the `instanceof` operator, assignment operators (+=, /=,
709
+ # >>=, etc.), and binary expression operators. The `in` operator
710
+ # is parsed only if the `header` option is set-this specifies
711
+ # that the expression should be treated as part of a `for` loop
712
+ # header.
713
+ if !options[:header] && token[:value] == "in" || token[:value] == "instanceof" || (token[:name] == 11 && (token[:isAssignment] = !!ASSIGNMENTS.match(token[:value])) || BINARY_OPERATORS.match(token[:value]))
714
+ if token[:isAssignment]
715
+ # Emit an error if an expression may not be assigned to or is
716
+ # not a valid reference.
717
+ case false
718
+ when assignment_accepted
719
+ fail "The left-hand side of the assignment expression is not a reference.", token
720
+ when reference
721
+ fail "An assignment expression may not follow a non-assignment operator.", token
722
+ end
723
+ end
724
+ # Labels may not share operator names.
725
+ if options[:identifier]
726
+ token = warn "Statement labels passed to `break` and `continue` must be identifiers.", token
727
+ end
728
+ # If a non-assignment operator was parsed, the remainder of the
729
+ # expression may not contain assignments.
730
+ reference = false unless token[:isAssignment]
731
+
732
+ # Conditional (Ternary) Operator.
733
+ # -------------------------------
734
+ conditional = token[:value] == ??
735
+ save token
736
+ token = get :pattern
737
+ if conditional
738
+ # The `true` condition should begin with a left-hand side
739
+ # expression.
740
+ unless token[:name] <= 6 || LHS_START.match(token[:value])
741
+ fail "Invalid conditional expression.", token
742
+ end
743
+ # Recursively parse the `true` assignment expression.
744
+ token = parse_assignment_expressions token, :single => true, :header => options[:header]
745
+ unless token[:value] == ?:
746
+ if token[:value] == ?,
747
+ token = warn "The comma operator may only be used as part of a nested expression.", token
748
+ else
749
+ token = warn "Missing second conditional expression.", token
750
+ end
751
+ end
752
+ save token
753
+ token = get :pattern
754
+ # Parse the `false` assignment expression.
755
+ token = parse_assignment_expressions token, :single => true, :header => options[:header]
756
+ end
757
+ else
758
+ # The operator is invalid.
759
+ continue = false
760
+ end
761
+ # If a ternary expression was parsed, verify if the next token is a
762
+ # binary operator.
763
+ break unless conditional
764
+ end
765
+ # Parse the next component. `token` marks the end of the right-hand
766
+ # side for the current left-hand side expression, and the left-hand
767
+ # side for the next.
768
+ if continue && !(token[:name] <= 6 || LHS_START.match(token[:value]))
769
+ fail "Expected a right-hand side expression following the assignment operator.", token
770
+ end
771
+ end
772
+
773
+ # Cleanup
774
+ # -------
775
+ # Subsequent tokens may not be parsed as labeled statements, and do not
776
+ # require an initial left-hand side expression.
777
+ options[:label] = initial = false
778
+ # Continue parsing expressions unless the `single` option was explicitly
779
+ # specified or the current token is the comma operator.
780
+ break unless !options[:single] && token[:value] == ?,
781
+ end
782
+ token
783
+ end
784
+
785
+ # Internal: Parses an object literal member.
786
+ #
787
+ # token - The current token.
788
+ #
789
+ # Returns the token immediately following the member.
790
+ def parse_object_literal_member(token)
791
+ unless token[:value] == ?:
792
+ token = warn "A colon should follow every property name.", token
793
+ end
794
+ save token
795
+ token = get :pattern
796
+ previous_token = token
797
+ token = parse_assignment_expressions token, :single => true
798
+ if previous_token == token
799
+ token = warn "Missing object literal property value.", token
800
+ end
801
+ token
802
+ end
803
+
804
+ # Internal: Parses a semicolon.
805
+ #
806
+ # token - The current token.
807
+ #
808
+ # Returns the token immediately adjacent to the semicolon.
809
+ def parse_semicolon(token)
810
+ if token[:value] == ?;
811
+ save token
812
+ token = get :pattern
813
+ else
814
+ # Automatic semicolon insertion cannot be applied if the end-of-file
815
+ # mark has not been reached and the current token either is a semicolon,
816
+ # or does not follow at least one line terminator and is not a closing
817
+ # curly brace, and is not preceded by a line terminator or is not a
818
+ # unary increment or decrement operator.
819
+ if token[:name] != 12 && (token[:semicolon] || !(token[:asi] || token[:value] == ?})) && !(token[:asi] && %w(++ --).include?(token[:value]))
820
+ fail "Expected `;`. Automatic semicolon insertion cannot be applied.", token
821
+ else
822
+ # @kitcambridge: This is an inversion of Peter's code, which checks
823
+ # for the affirmative condition. Peter also notes that this method
824
+ # does not check for restricted productions (`break`, `continue`, and
825
+ # `return`), if the subsequent line is a regular expression, or the
826
+ # semicolon is part of a `for` loop header. He notes that the parser
827
+ # is designed to automatically catch the latter two exceptions.
828
+ #
829
+ # The current token is the token that occurs *after* the insertion,
830
+ # not before; hence, its position is the *beginning* of the current
831
+ # token.
832
+ semicolon = Token.new(lexer, :asi, token[:start]...token[:start])
833
+ save semicolon
834
+ lexer.insert_before(semicolon, token)
835
+ end
836
+ end
837
+ token[:semicolon] = true
838
+ token
839
+ end
840
+
841
+ # Internal: Parses a statement.
842
+ #
843
+ # token - The current token.
844
+ #
845
+ # Returns the token immediately adjacent to the statement.
846
+ def parse_statement(token, optional = false)
847
+ # @kitcambridge: Peter's parser returns the value of the `token` parameter
848
+ # if it is falsy and the `optional` option is set. Because Ruby enforces
849
+ # method arity and none of the operations may produce a `nil` token, this
850
+ # seems redundant.
851
+ if token[:name] == 2
852
+ # If the token is an identifier, determine if it can be parsed as a
853
+ # statement.
854
+ if %w( var if do while for continue break return throw switch try debugger with ).include? token[:value]
855
+ token = send "parse_#{token[:value]}_statement", token
856
+ elsif token[:value] == "function"
857
+ fail "Function statements are an extension of ECMAScript semantics. Their use is discouraged.", token
858
+ token = parse_function_declaration token
859
+ else
860
+ token = parse_expression_or_label token
861
+ end
862
+ elsif token[:value] == ?{
863
+ # Blocks are parsed before expressions.
864
+ token = parse_block token
865
+ elsif token[:isString] || token[:isNumber] || token[:name] == 1 || LHS_START.match(token[:value])
866
+ # Parse expressions (strings, numbers, RegExps, and left-hand side start
867
+ # values). Each expression should be followed by a semicolon, whether
868
+ # explicit or implicit through ASI.
869
+ token = parse_assignment_expressions token
870
+ token = parse_semicolon token
871
+ elsif token[:value] == ?;
872
+ # The empty statement. Parse the current token as a semicolon.
873
+ token[:emptyStatement] = true
874
+ token = parse_semicolon token
875
+ elsif !optional
876
+ token = warn STATEMENT_ERROR, token
877
+ end
878
+ token
879
+ end
880
+
881
+ # Internal: Parses a block.
882
+ #
883
+ # token - The current token.
884
+ #
885
+ # Returns the token immediately adjacent to the block.
886
+ def parse_block(token)
887
+ save token
888
+ token = get :pattern
889
+ unless token[:value] == ?}
890
+ token = parse_statements token
891
+ end
892
+ unless token[:value] == ?}
893
+ token = warn "Unterminated block. Expected `}`.", token
894
+ end
895
+ save token
896
+ get :pattern
897
+ end
898
+
899
+ # Internal: Parses an expression or labeled statement.
900
+ #
901
+ # token - The current token.
902
+ #
903
+ # Returns the token immediately following the result.
904
+ def parse_expression_or_label(token)
905
+ token = parse_assignment_expressions token, :label => true
906
+ # Only parse a semicolon if a label was not parsed.
907
+ unless token[:wasLabel]
908
+ token = parse_semicolon token
909
+ end
910
+ token
911
+ end
912
+
913
+ # Internal: Continuously parses statements.
914
+ #
915
+ # token - The current token.
916
+ # allow_functions - If this option is set to `true`, function declarations
917
+ # may be parsed. Statements should not begin with functions.
918
+ #
919
+ # Returns the token immediately following the result.
920
+ def parse_tokens(token, allow_functions = false)
921
+ # @kitcambridge: Peter notes that detecting the beginning of a statement
922
+ # is difficult. The parser consumes statements until the current token is
923
+ # identical to the previous, which occurs if a statement is optional.
924
+ previous_token = token
925
+ loop do
926
+ token = if allow_functions && previous_token[:value] == "function"
927
+ parse_function_declaration previous_token
928
+ else
929
+ parse_statement(previous_token, :optional)
930
+ end
931
+ break if previous_token == token
932
+ previous_token = token
933
+ end
934
+ token
935
+ end
936
+
937
+ # Internal: Continuously parses statements.
938
+ def parse_statements(token)
939
+ parse_tokens(token)
940
+ end
941
+
942
+ # Internal: Continuously parses source elements.
943
+ def parse_source_elements(token)
944
+ parse_tokens(token, :functions)
945
+ end
946
+
947
+ # Internal.
948
+ def parse_var_statement(token)
949
+ token = parse_variable_declarations token
950
+ parse_semicolon token
951
+ end
952
+
953
+ # Internal.
954
+ def parse_variable_declarations(token, header = false)
955
+ initial = true
956
+ loop do
957
+ save token
958
+ # `token` references the `var` statement on the first iteration, and
959
+ # the comma separator for all subsequent iterations.
960
+ token = get :pattern
961
+ # The subsequent token should be an identifier that specifies the
962
+ # variable name.
963
+ if token[:name] == 12
964
+ # @kitcambridge: Peter proposes returning if an illegal trailing comma
965
+ # is encountered.
966
+ token = warn(initial ? "A `var` declaration should be followed by the variable name." : "Illegal trailing comma.", token)
967
+ elsif token[:name] != 2
968
+ token = warn "Variable names can only be identifiers.", token
969
+ elsif KEYWORD_OR_RESERVED.include? token[:value]
970
+ token = warn "Variable names may not be keywords or future reserved words.", token
971
+ end
972
+ save token
973
+ # The next token should be either `=`, `;`, or `,`.
974
+ token = get :pattern
975
+ if token[:value] == ?=
976
+ # Parse a single assignment expression and an optional trailing comma.
977
+ token[:initializer] = true
978
+ save token
979
+ token = get :pattern
980
+ unless token[:name] <= 6 || token[:name] == 14 || LHS_START.match(token[:value])
981
+ token = warn "The variable value must be an expression that does not contain a comma.", token
982
+ end
983
+ token = parse_assignment_expressions token, :single => true, :header => header
984
+ end
985
+ initial = false
986
+ # Loop until all trailing commas have been consumed.
987
+ break unless token[:value] == ?,
988
+ end
989
+ token
990
+ end
991
+
992
+ # Internal.
993
+ def parse_if_statement(token)
994
+ save token
995
+ token = get :pattern
996
+ unless token[:value] == ?(
997
+ token = warn "Expected `(`.", token
998
+ end
999
+ save token
1000
+ token = get :pattern
1001
+ unless token[:name] <= 6 || LHS_START.match(token[:value])
1002
+ token = warn "A statement header must contain at least one expression.", token
1003
+ end
1004
+ token = parse_assignment_expressions token
1005
+ unless token[:value] == ?)
1006
+ token = warn "Expected `)`.", token
1007
+ end
1008
+ save token
1009
+ token = get :pattern
1010
+ token = parse_statement token
1011
+ token = parse_else_statement(token) if token[:value] == "else"
1012
+ token
1013
+ end
1014
+
1015
+ # Internal.
1016
+ def parse_else_statement(token)
1017
+ save token
1018
+ token = get :pattern
1019
+ parse_statement token
1020
+ end
1021
+
1022
+ # Internal.
1023
+ def parse_do_statement(token)
1024
+ save token
1025
+ token = get :pattern
1026
+ token = parse_statement token
1027
+ unless token[:value] == "while"
1028
+ token = warn "The `do...while` loop requires the `while` statement after the loop body.", token
1029
+ end
1030
+ save token
1031
+ token = get :pattern
1032
+ unless token[:value] == ?(
1033
+ token = warn "Expected `(`.", token
1034
+ end
1035
+ save token
1036
+ token = get :pattern
1037
+ unless token[:name] <= 6 || LHS_START.match(token[:value])
1038
+ token = warn "A statement header must contain at least one expression.", token
1039
+ end
1040
+ token = parse_assignment_expressions token
1041
+ unless token[:value] == ?)
1042
+ token = warn "Expected `)`.", token
1043
+ end
1044
+ save token
1045
+ token = get :pattern
1046
+ # @kitcambridge: According to Peter, the trailing semicolon is not
1047
+ # optional, though implementations apply ASI nonetheless.
1048
+ parse_semicolon token
1049
+ end
1050
+
1051
+ # Internal.
1052
+ def parse_while_statement(token)
1053
+ save token
1054
+ token = get :pattern
1055
+ unless token[:value] == ?(
1056
+ token = warn "Expected `(`.", token
1057
+ end
1058
+ save token
1059
+ token = get :pattern
1060
+ # The `while` loop header must contain a valid left-hand side start
1061
+ # value.
1062
+ unless token[:name] <= 6 || LHS_START.match(token[:value])
1063
+ token = warn "A statement header must contain at least one expression.", token
1064
+ end
1065
+ token = parse_assignment_expressions token
1066
+ unless token[:value] == ?)
1067
+ token = "Expected `)`.", token
1068
+ end
1069
+ save token
1070
+ token = get :pattern
1071
+ parse_statement(token)
1072
+ end
1073
+
1074
+ # Public: Parses a `for` or `for...in` loop.
1075
+ def parse_for_statement(token)
1076
+ save token
1077
+ token = get :pattern
1078
+ unless token[:value] == ?(
1079
+ token = warn "Expected `(`.", token
1080
+ end
1081
+ save token
1082
+ token = get :pattern
1083
+ # Parse either the initialization section of a `for` loop or the left-hand
1084
+ # side `in` operand of a `for...in` loop. The parser subsequently
1085
+ # determines which loop type is used.
1086
+ if token[:value] == "var"
1087
+ token = parse_variable_declarations(token, :header)
1088
+ elsif token[:value] != ?;
1089
+ unless token[:name] <= 6 || LHS_START.match(token[:value])
1090
+ fail "A `for` statement header must contain at least one expression.", token
1091
+ end
1092
+ # Multiple expressions are permitted, but the `in` operator is not.
1093
+ token = parse_assignment_expressions token, :header => true
1094
+ end
1095
+ if token[:value] == "in"
1096
+ # @kitcambridge: Violet does not verify if the `for...in` loop header
1097
+ # introduces only one variable. This requires the full AST.
1098
+ save token
1099
+ token = get :pattern
1100
+ token = parse_assignment_expressions token
1101
+ else
1102
+ unless token[:value] == ?;
1103
+ token = warn "A `for` loop header must contain either the `in` operator or two consecutive semicolons (`;;`).", token
1104
+ end
1105
+ # Parse two optional expressions separated by a semicolon. The `in`
1106
+ # operator is permitted.
1107
+ save token
1108
+ token = get :pattern
1109
+ token = parse_assignment_expressions(token) if token[:name] <= 6 || LHS_START.match(token[:value])
1110
+ unless token[:value] == ?;
1111
+ token = warn "Expected `;`.", token
1112
+ end
1113
+ save token
1114
+ token = get :pattern
1115
+ token = parse_assignment_expressions(token) if token[:name] <= 6 || LHS_START.match(token[:value])
1116
+ end
1117
+ unless token[:value] == ?)
1118
+ token = warn "Expected `)`.", token
1119
+ end
1120
+ save token
1121
+ token = get :pattern
1122
+ parse_statement(token)
1123
+ end
1124
+
1125
+ # Internal.
1126
+ def parse_continue_statement(token)
1127
+ save token
1128
+ token = get :pattern
1129
+ unless token[:asi] || token[:value] == ?; || token[:name] == 12 || token[:value] == ?}
1130
+ # Only identifiers may follow a `continue` statement.
1131
+ token = parse_assignment_expressions token, :single => true, :identifier => true
1132
+ unless token[:asi] || token[:value] == ?; || token[:name] == 12 || token[:value] == ?}
1133
+ token = warn "The argument to a `continue` statement must be an identifier that references an existing label.", token
1134
+ end
1135
+ end
1136
+ parse_semicolon token
1137
+ end
1138
+
1139
+ # Internal.
1140
+ def parse_break_statement(token)
1141
+ save token
1142
+ token = get :pattern
1143
+ unless token[:asi] || token[:value] == ?; || token[:name] == 12 || token[:value] == ?}
1144
+ token = parse_assignment_expressions token, :single => true, :identifier => true
1145
+ unless token[:asi] || token[:value] == ?; || token[:name] == 12 || token[:value] == ?}
1146
+ token = warn "The argument to a `break` statement must be an identifier that references an existing label.", token
1147
+ end
1148
+ end
1149
+ parse_semicolon token
1150
+ end
1151
+
1152
+ # Internal.
1153
+ def parse_return_statement(token)
1154
+ save token
1155
+ token = get :pattern
1156
+ unless token[:asi] || token[:value] == ?; || token[:name] == 12 || token[:value] == ?}
1157
+ token = parse_assignment_expressions token
1158
+ end
1159
+ parse_semicolon token
1160
+ end
1161
+
1162
+ # Internal.
1163
+ def parse_throw_statement(token)
1164
+ save token
1165
+ token = get :pattern
1166
+ # `throw` may not precede a line terminator, as it is a restricted
1167
+ # production for automatic semicolon insertion.
1168
+ if token[:asi]
1169
+ token = warn "A line terminator may not occur between a `throw` statement and its argument.", token
1170
+ end
1171
+ if token[:value] == ?;
1172
+ token = warn "The argument to `throw` is not optional.", token
1173
+ end
1174
+ token = parse_assignment_expressions token
1175
+ parse_semicolon token
1176
+ end
1177
+
1178
+ # Internal.
1179
+ def parse_switch_statement(token)
1180
+ save token
1181
+ token = get :pattern
1182
+ unless token[:value] == ?(
1183
+ token = warn "Expected `(`.", token
1184
+ end
1185
+ save token
1186
+ token = get :pattern
1187
+ unless token[:name] <= 6 || LHS_START.match(token[:value])
1188
+ fail "A `switch` statement header must contain at least one expression.", token
1189
+ end
1190
+ token = parse_assignment_expressions token
1191
+ unless token[:value] == ?)
1192
+ token = warn "Expected `)`.", token
1193
+ end
1194
+ save token
1195
+ token = get :pattern
1196
+ unless token[:value] == ?{
1197
+ token = warn "A `switch` block must begin with `{`.", token
1198
+ end
1199
+ save token
1200
+ token = get :pattern
1201
+ # A `default` case may occur only once in a `switch` block, but may
1202
+ # occur anywhere.
1203
+ has_cases, default = false, false
1204
+ while token[:value] == "case" || !default && (default = token[:value] == "default")
1205
+ has_cases = true
1206
+ token = parse_switch_clause token
1207
+ end
1208
+ unless has_cases || token[:value] == ?}
1209
+ token = warn "A `switch` block must begin with a `case` or `default` clause.", token
1210
+ end
1211
+ if default && token[:value] == "default"
1212
+ fail "`switch` blocks may not contain multiple `default` clauses.", token
1213
+ end
1214
+ unless token[:value] == ?} || token[:name] == 14
1215
+ token = warn "A `switch` block must end with `}`.", token
1216
+ end
1217
+ save token
1218
+ get :pattern
1219
+ end
1220
+
1221
+ # Internal.
1222
+ def parse_switch_clause(token)
1223
+ token = parse_switch_header token
1224
+ parse_switch_block token
1225
+ end
1226
+
1227
+ # Internal.
1228
+ def parse_switch_header(token)
1229
+ if token[:value] == "case"
1230
+ token = parse_switch_case token
1231
+ else
1232
+ token = parse_switch_default token
1233
+ end
1234
+ unless token[:value] == ?:
1235
+ token = warn "`switch` clauses must be followed by a colon.", token
1236
+ end
1237
+ save token
1238
+ get :pattern
1239
+ end
1240
+
1241
+ # Internal.
1242
+ def parse_switch_block(token)
1243
+ previous_token = nil
1244
+ # `switch` clauses may be empty.
1245
+ until %w( } case default).include?(token[:value]) || [14, 12].include?(token[:name]) || previous_token == token
1246
+ previous_token = token
1247
+ token = parse_statement token, :optional
1248
+ end
1249
+ if previous_token == token
1250
+ warn "Unexpected token in `switch` block.", token
1251
+ end
1252
+ token
1253
+ end
1254
+
1255
+ # Internal.
1256
+ def parse_switch_case(token)
1257
+ save token
1258
+ token = get :pattern
1259
+ if token[:value] == ?:
1260
+ fail "A `case` clause expects an expression as its argument.", token
1261
+ else
1262
+ token = parse_assignment_expressions token
1263
+ end
1264
+ token
1265
+ end
1266
+
1267
+ # Internal.
1268
+ def parse_switch_default(token)
1269
+ save token
1270
+ get :pattern
1271
+ end
1272
+
1273
+ # Public: Parses a `try...catch...finally` statement.
1274
+ # Returns the token immediately following the statement.
1275
+ #
1276
+ # token - The current token.
1277
+ def parse_try_statement(token)
1278
+ token = parse_try_block token
1279
+ token = parse_catch_block(token) if token[:value] == "catch"
1280
+ token = parse_finally_block(token) if token[:value] == "finally"
1281
+ unless @try
1282
+ fail "A `try` statement must be followed by either a `catch` block, a `finally` block, or both.", token
1283
+ end
1284
+ token
1285
+ ensure
1286
+ @try = false
1287
+ end
1288
+
1289
+ # Internal.
1290
+ def parse_try_block(token)
1291
+ save token
1292
+ token = get :pattern
1293
+ unless token[:value] == ?{
1294
+ token = warn "A `try` block must begin with `{`."
1295
+ end
1296
+ save token
1297
+ token = get :pattern
1298
+ token = parse_statements(token) unless token[:value] == ?}
1299
+ unless token[:value] == ?}
1300
+ token = warn "A `try` block must end with `}`.", token
1301
+ end
1302
+ save token
1303
+ get :pattern
1304
+ end
1305
+
1306
+ # Internal.
1307
+ def parse_catch_block(token)
1308
+ @try = true
1309
+ save token
1310
+ token = get :pattern
1311
+ unless token[:value] == ?(
1312
+ token = warn "The `catch` block header must accept a single argument.", token
1313
+ end
1314
+ save token
1315
+ token = get :pattern
1316
+ if token[:name] != 2
1317
+ token = warn "The `catch` block argument must be an identifier.", token
1318
+ elsif KEYWORD_OR_RESERVED.include?(token[:value])
1319
+ fail "The `catch` block argument may not be a keyword or future reserved word.", token
1320
+ end
1321
+ save token
1322
+ token = get :pattern
1323
+ unless token[:value] == ?)
1324
+ token = warn "Expected `)`.", token
1325
+ end
1326
+ save token
1327
+ token = get :pattern
1328
+ unless token[:value] == ?{
1329
+ token = warn "A `catch` block must begin with `{`.", token
1330
+ end
1331
+ save token
1332
+ token = get :pattern
1333
+ # Statements inside the `catch` block are optional.
1334
+ token = parse_statements(token) unless token[:value] == ?}
1335
+ unless token[:value] == ?}
1336
+ token = warn "A `catch` block must end with `}`.", token
1337
+ end
1338
+ save token
1339
+ get :pattern
1340
+ end
1341
+
1342
+ # Internal: Parses a `finally` clause of a `try` block.
1343
+ #
1344
+ # token - The current token.
1345
+ #
1346
+ # Returns the token immediately following the block.
1347
+ def parse_finally_block(token)
1348
+ @try = true
1349
+ save token
1350
+ token = get :pattern
1351
+ unless token[:value] == ?{
1352
+ token = warn "A `finally` block must begin with `{`.", token
1353
+ end
1354
+ save token
1355
+ token = get :pattern
1356
+ # A `finally` block can be empty.
1357
+ token = parse_statements(token) unless token[:value] == ?}
1358
+ unless token[:value] == ?}
1359
+ token = warn "A `finally` block must end with `}`.", token
1360
+ end
1361
+ save token
1362
+ get :pattern
1363
+ end
1364
+
1365
+ # Internal: Parses a `debugger` statement.
1366
+ #
1367
+ # token - The current token.
1368
+ #
1369
+ # Returns the token immediately following the statement.
1370
+ def parse_debugger_statement(token)
1371
+ save token
1372
+ token = get :pattern
1373
+ parse_semicolon token
1374
+ end
1375
+
1376
+ # Internal: Parses a `with` statement.
1377
+ #
1378
+ # token - The current token.
1379
+ #
1380
+ # Returns the token immediately following the statement.
1381
+ def parse_with_statement(token)
1382
+ save token
1383
+ token = get :pattern
1384
+ unless token[:value] == ?(
1385
+ token = warn "The `with` statement must begin with `{`.", token
1386
+ end
1387
+ save token
1388
+ token = get :pattern
1389
+ # The statement header must contain a left-hand side start value.
1390
+ unless token[:name] <= 6 || LHS_START.match(token[:value])
1391
+ token = warn "The `with` statement header cannot be empty.", token
1392
+ end
1393
+ token = parse_assignment_expressions token
1394
+ unless token[:value] == ?)
1395
+ token = warn "The `with` statement must end with `}`.", token
1396
+ end
1397
+ save token
1398
+ token = get :pattern
1399
+ parse_statement token
1400
+ end
1401
+
1402
+ # Internal: Emits a warning.
1403
+ #
1404
+ # message - The warning message.
1405
+ # token - The malformed token.
1406
+ #
1407
+ # Returns the warning `Token`.
1408
+ def warn(message, token)
1409
+ message = ParserError.new(message, token)
1410
+ # If a malformed token was previously encountered, insert it into the
1411
+ # token stream at the current position.
1412
+ save(@exception) if @exception
1413
+ # Store the current malformed token.
1414
+ @exception = token
1415
+ # Produce an error token at the current position and add it to the
1416
+ # lexer's token stream.
1417
+ error = Token.new(lexer, :error, token[:start]...token[:start])
1418
+ error[:error] = message
1419
+ lexer.insert_before(error, token)
1420
+ error
1421
+ end
1422
+
1423
+ # Internal: Emits an error.
1424
+ #
1425
+ # message - The warning message.
1426
+ # token - The malformed token.
1427
+ #
1428
+ # Returns nothing.
1429
+ def fail(message, token)
1430
+ message = ParserError.new(message, token)
1431
+ # Ignore the parse error. This will affect how the remainder of the
1432
+ # source is parsed.
1433
+ error = Token.new(lexer, :error, token[:start]...token[:start])
1434
+ error[:error] = message
1435
+ save error
1436
+ lexer.insert_before(error, token)
1437
+ nil
1438
+ end
1439
+ end
1440
+ end