ThirdParty/Jasy/jasy/js/parse/Parser.py

branch
Py2 comp.
changeset 2847
1843ef6e2656
parent 2779
4d433896b6d6
child 3145
a9de05d4a22f
equal deleted inserted replaced
2846:b852fe4d153a 2847:1843ef6e2656
1 #
2 # Jasy - Web Tooling Framework
3 # Copyright 2010-2012 Zynga Inc.
4 #
5
6 #
7 # License: MPL 1.1/GPL 2.0/LGPL 2.1
8 # Authors:
9 # - Brendan Eich <brendan@mozilla.org> (Original JavaScript) (2004-2010)
10 # - Sebastian Werner <info@sebastian-werner.net> (Python Port) (2010-2012)
11 #
12
13 from __future__ import unicode_literals # __IGNORE_WARNING__
14
15 import jasy.js.tokenize.Tokenizer
16 import jasy.js.parse.VanillaBuilder
17 import jasy.js.tokenize.Lang
18
19 __all__ = [ "parse", "parseExpression" ]
20
21 def parseExpression(source, fileId=None, line=1, builder=None):
22 if builder == None:
23 builder = jasy.js.parse.VanillaBuilder.VanillaBuilder()
24
25 # Convert source into expression statement to be friendly to the Tokenizer
26 if not source.endswith(";"):
27 source = source + ";"
28
29 tokenizer = jasy.js.tokenize.Tokenizer.Tokenizer(source, fileId, line)
30 staticContext = StaticContext(False, builder)
31
32 return Expression(tokenizer, staticContext)
33
34
35 def parse(source, fileId=None, line=1, builder=None):
36 if builder == None:
37 builder = jasy.js.parse.VanillaBuilder.VanillaBuilder()
38
39 tokenizer = jasy.js.tokenize.Tokenizer.Tokenizer(source, fileId, line)
40 staticContext = StaticContext(False, builder)
41 node = Script(tokenizer, staticContext)
42
43 # store fileId on top-level node
44 node.fileId = tokenizer.fileId
45
46 # add missing comments e.g. empty file with only a comment etc.
47 # if there is something non-attached by an inner node it is attached to
48 # the top level node, which is not correct, but might be better than
49 # just ignoring the comment after all.
50 if len(node) > 0:
51 builder.COMMENTS_add(node[-1], None, tokenizer.getComments())
52 else:
53 builder.COMMENTS_add(node, None, tokenizer.getComments())
54
55 if not tokenizer.done():
56 raise SyntaxError("Unexpected end of file", tokenizer)
57
58 return node
59
60
61
62 class SyntaxError(Exception):
63 def __init__(self, message, tokenizer):
64 Exception.__init__(self, "Syntax error: %s\n%s:%s" % (
65 message, tokenizer.fileId, tokenizer.line))
66
67
68 # Used as a status container during tree-building for every def body and the global body
69 class StaticContext(object):
70 # inFunction is used to check if a return stm appears in a valid context.
71 def __init__(self, inFunction, builder):
72 # Whether this is inside a function, mostly True, only for top-level scope
73 # it's False
74 self.inFunction = inFunction
75
76 self.hasEmptyReturn = False
77 self.hasReturnWithValue = False
78 self.isGenerator = False
79 self.blockId = 0
80 self.builder = builder
81 self.statementStack = []
82
83 # Sets to store variable uses
84 # self.functions = set()
85 # self.variables = set()
86
87 # Status
88 # self.needsHoisting = False
89 self.bracketLevel = 0
90 self.curlyLevel = 0
91 self.parenLevel = 0
92 self.hookLevel = 0
93
94 # Configure strict ecmascript 3 mode
95 self.ecma3OnlyMode = False
96
97 # Status flag during parsing
98 self.inForLoopInit = False
99
100
101 def Script(tokenizer, staticContext):
102 """Parses the toplevel and def bodies."""
103 node = Statements(tokenizer, staticContext)
104
105 # change type from "block" to "script" for script root
106 node.type = "script"
107
108 # copy over data from compiler context
109 # node.functions = staticContext.functions
110 # node.variables = staticContext.variables
111
112 return node
113
114
115 def nest(tokenizer, staticContext, node, func, end=None):
116 """Statement stack and nested statement handler."""
117 staticContext.statementStack.append(node)
118 node = func(tokenizer, staticContext)
119 staticContext.statementStack.pop()
120 end and tokenizer.mustMatch(end)
121
122 return node
123
124
125 def Statements(tokenizer, staticContext):
126 """Parses a list of Statements."""
127
128 builder = staticContext.builder
129 node = builder.BLOCK_build(tokenizer, staticContext.blockId)
130 staticContext.blockId += 1
131
132 builder.BLOCK_hoistLets(node)
133 staticContext.statementStack.append(node)
134
135 prevNode = None
136 while not tokenizer.done() and tokenizer.peek(True) != "right_curly":
137 comments = tokenizer.getComments()
138 childNode = Statement(tokenizer, staticContext)
139 builder.COMMENTS_add(childNode, prevNode, comments)
140 builder.BLOCK_addStatement(node, childNode)
141 prevNode = childNode
142
143 staticContext.statementStack.pop()
144 builder.BLOCK_finish(node)
145
146 # if getattr(node, "needsHoisting", False):
147 # # TODO
148 # raise Exception("Needs hoisting went true!!!")
149 # builder.setHoists(node.id, node.variables)
150 # # Propagate up to the function.
151 # staticContext.needsHoisting = True
152
153 return node
154
155
156 def Block(tokenizer, staticContext):
157 tokenizer.mustMatch("left_curly")
158 node = Statements(tokenizer, staticContext)
159 tokenizer.mustMatch("right_curly")
160
161 return node
162
163
164 def Statement(tokenizer, staticContext):
165 """Parses a Statement."""
166
167 tokenType = tokenizer.get(True)
168 builder = staticContext.builder
169
170 # Cases for statements ending in a right curly return early, avoiding the
171 # common semicolon insertion magic after this switch.
172
173 if tokenType == "function":
174 # "declared_form" extends functions of staticContext,
175 # "statement_form" doesn'tokenizer.
176 if len(staticContext.statementStack) > 1:
177 kind = "statement_form"
178 else:
179 kind = "declared_form"
180
181 return FunctionDefinition(tokenizer, staticContext, True, kind)
182
183
184 elif tokenType == "left_curly":
185 node = Statements(tokenizer, staticContext)
186 tokenizer.mustMatch("right_curly")
187
188 return node
189
190
191 elif tokenType == "if":
192 node = builder.IF_build(tokenizer)
193 builder.IF_setCondition(node, ParenExpression(tokenizer, staticContext))
194 staticContext.statementStack.append(node)
195 builder.IF_setThenPart(node, Statement(tokenizer, staticContext))
196
197 if tokenizer.match("else"):
198 comments = tokenizer.getComments()
199 elsePart = Statement(tokenizer, staticContext)
200 builder.COMMENTS_add(elsePart, node, comments)
201 builder.IF_setElsePart(node, elsePart)
202
203 staticContext.statementStack.pop()
204 builder.IF_finish(node)
205
206 return node
207
208
209 elif tokenType == "switch":
210 # This allows CASEs after a "default", which is in the standard.
211 node = builder.SWITCH_build(tokenizer)
212 builder.SWITCH_setDiscriminant(node, ParenExpression(tokenizer, staticContext))
213 staticContext.statementStack.append(node)
214
215 tokenizer.mustMatch("left_curly")
216 tokenType = tokenizer.get()
217
218 while tokenType != "right_curly":
219 if tokenType == "default":
220 if node.defaultIndex >= 0:
221 raise SyntaxError("More than one switch default", tokenizer)
222
223 childNode = builder.DEFAULT_build(tokenizer)
224 builder.SWITCH_setDefaultIndex(node, len(node)-1)
225 tokenizer.mustMatch("colon")
226 builder.DEFAULT_initializeStatements(childNode, tokenizer)
227
228 while True:
229 tokenType=tokenizer.peek(True)
230 if tokenType == "case" or tokenType == "default" or tokenType == "right_curly":
231 break
232 builder.DEFAULT_addStatement(childNode, Statement(tokenizer, staticContext))
233
234 builder.DEFAULT_finish(childNode)
235
236 elif tokenType == "case":
237 childNode = builder.CASE_build(tokenizer)
238 builder.CASE_setLabel(childNode, Expression(tokenizer, staticContext))
239 tokenizer.mustMatch("colon")
240 builder.CASE_initializeStatements(childNode, tokenizer)
241
242 while True:
243 tokenType=tokenizer.peek(True)
244 if tokenType == "case" or tokenType == "default" or tokenType == "right_curly":
245 break
246 builder.CASE_addStatement(childNode, Statement(tokenizer, staticContext))
247
248 builder.CASE_finish(childNode)
249
250 else:
251 raise SyntaxError("Invalid switch case", tokenizer)
252
253 builder.SWITCH_addCase(node, childNode)
254 tokenType = tokenizer.get()
255
256 staticContext.statementStack.pop()
257 builder.SWITCH_finish(node)
258
259 return node
260
261
262 elif tokenType == "for":
263 node = builder.FOR_build(tokenizer)
264 forBlock = None
265
266 if tokenizer.match("identifier") and tokenizer.token.value == "each":
267 builder.FOR_rebuildForEach(node)
268
269 tokenizer.mustMatch("left_paren")
270 tokenType = tokenizer.peek()
271 childNode = None
272
273 if tokenType != "semicolon":
274 staticContext.inForLoopInit = True
275
276 if tokenType == "var" or tokenType == "const":
277 tokenizer.get()
278 childNode = Variables(tokenizer, staticContext)
279
280 elif tokenType == "let":
281 tokenizer.get()
282
283 if tokenizer.peek() == "left_paren":
284 childNode = LetBlock(tokenizer, staticContext, False)
285
286 else:
287 # Let in for head, we need to add an implicit block
288 # around the rest of the for.
289 forBlock = builder.BLOCK_build(tokenizer, staticContext.blockId)
290 staticContext.blockId += 1
291 staticContext.statementStack.append(forBlock)
292 childNode = Variables(tokenizer, staticContext, forBlock)
293
294 else:
295 childNode = Expression(tokenizer, staticContext)
296
297 staticContext.inForLoopInit = False
298
299 if childNode and tokenizer.match("in"):
300 builder.FOR_rebuildForIn(node)
301 builder.FOR_setObject(node, Expression(tokenizer, staticContext), forBlock)
302
303 if childNode.type == "var" or childNode.type == "let":
304 if len(childNode) != 1:
305 raise SyntaxError("Invalid for..in left-hand side", tokenizer)
306
307 builder.FOR_setIterator(node, childNode, forBlock)
308
309 else:
310 builder.FOR_setIterator(node, childNode, forBlock)
311
312 else:
313 builder.FOR_setSetup(node, childNode)
314 tokenizer.mustMatch("semicolon")
315
316 if node.isEach:
317 raise SyntaxError("Invalid for each..in loop", tokenizer)
318
319 if tokenizer.peek() == "semicolon":
320 builder.FOR_setCondition(node, None)
321 else:
322 builder.FOR_setCondition(node, Expression(tokenizer, staticContext))
323
324 tokenizer.mustMatch("semicolon")
325
326 if tokenizer.peek() == "right_paren":
327 builder.FOR_setUpdate(node, None)
328 else:
329 builder.FOR_setUpdate(node, Expression(tokenizer, staticContext))
330
331 tokenizer.mustMatch("right_paren")
332 builder.FOR_setBody(node, nest(tokenizer, staticContext, node, Statement))
333
334 if forBlock:
335 builder.BLOCK_finish(forBlock)
336 staticContext.statementStack.pop()
337
338 builder.FOR_finish(node)
339 return node
340
341
342 elif tokenType == "while":
343 node = builder.WHILE_build(tokenizer)
344
345 builder.WHILE_setCondition(node, ParenExpression(tokenizer, staticContext))
346 builder.WHILE_setBody(node, nest(tokenizer, staticContext, node, Statement))
347 builder.WHILE_finish(node)
348
349 return node
350
351
352 elif tokenType == "do":
353 node = builder.DO_build(tokenizer)
354
355 builder.DO_setBody(node, nest(tokenizer, staticContext, node, Statement, "while"))
356 builder.DO_setCondition(node, ParenExpression(tokenizer, staticContext))
357 builder.DO_finish(node)
358
359 if not staticContext.ecma3OnlyMode:
360 # <script language="JavaScript"> (without version hints) may need
361 # automatic semicolon insertion without a newline after do-while.
362 # See http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
363 tokenizer.match("semicolon")
364 return node
365
366 # NO RETURN
367
368
369 elif tokenType == "break" or tokenType == "continue":
370 if tokenType == "break":
371 node = builder.BREAK_build(tokenizer)
372 else:
373 node = builder.CONTINUE_build(tokenizer)
374
375 if tokenizer.peekOnSameLine() == "identifier":
376 tokenizer.get()
377
378 if tokenType == "break":
379 builder.BREAK_setLabel(node, tokenizer.token.value)
380 else:
381 builder.CONTINUE_setLabel(node, tokenizer.token.value)
382
383 statementStack = staticContext.statementStack
384 i = len(statementStack)
385 label = node.label if hasattr(node, "label") else None
386
387 if label:
388 while True:
389 i -= 1
390 if i < 0:
391 raise SyntaxError("Label not found", tokenizer)
392 if getattr(statementStack[i], "label", None) == label:
393 break
394
395 #
396 # Both break and continue to label need to be handled specially
397 # within a labeled loop, so that they target that loop. If not in
398 # a loop, then break targets its labeled statement. Labels can be
399 # nested so we skip all labels immediately enclosing the nearest
400 # non-label statement.
401 #
402 while i < len(statementStack) - 1 and statementStack[i+1].type == "label":
403 i += 1
404
405 if i < len(statementStack) - 1 and getattr(statementStack[i+1], "isLoop", False):
406 i += 1
407 elif tokenType == "continue":
408 raise SyntaxError("Invalid continue", tokenizer)
409
410 else:
411 while True:
412 i -= 1
413 if i < 0:
414 if tokenType == "break":
415 raise SyntaxError("Invalid break", tokenizer)
416 else:
417 raise SyntaxError("Invalid continue", tokenizer)
418
419 if getattr(statementStack[i], "isLoop", False) or (tokenType == "break" and statementStack[i].type == "switch"):
420 break
421
422 if tokenType == "break":
423 builder.BREAK_finish(node)
424 else:
425 builder.CONTINUE_finish(node)
426
427 # NO RETURN
428
429
430 elif tokenType == "try":
431 node = builder.TRY_build(tokenizer)
432 builder.TRY_setTryBlock(node, Block(tokenizer, staticContext))
433
434 while tokenizer.match("catch"):
435 childNode = builder.CATCH_build(tokenizer)
436 tokenizer.mustMatch("left_paren")
437 nextTokenType = tokenizer.get()
438
439 if nextTokenType == "left_bracket" or nextTokenType == "left_curly":
440 # Destructured catch identifiers.
441 tokenizer.unget()
442 exception = DestructuringExpression(tokenizer, staticContext, True)
443
444 elif nextTokenType == "identifier":
445 exception = builder.CATCH_wrapException(tokenizer)
446
447 else:
448 raise SyntaxError("Missing identifier in catch", tokenizer)
449
450 builder.CATCH_setException(childNode, exception)
451
452 if tokenizer.match("if"):
453 if staticContext.ecma3OnlyMode:
454 raise SyntaxError("Illegal catch guard", tokenizer)
455
456 if node.getChildrenLength() > 0 and not node.getUnrelatedChildren()[0].guard:
457 raise SyntaxError("Guarded catch after unguarded", tokenizer)
458
459 builder.CATCH_setGuard(childNode, Expression(tokenizer, staticContext))
460
461 else:
462 builder.CATCH_setGuard(childNode, None)
463
464 tokenizer.mustMatch("right_paren")
465
466 builder.CATCH_setBlock(childNode, Block(tokenizer, staticContext))
467 builder.CATCH_finish(childNode)
468
469 builder.TRY_addCatch(node, childNode)
470
471 builder.TRY_finishCatches(node)
472
473 if tokenizer.match("finally"):
474 builder.TRY_setFinallyBlock(node, Block(tokenizer, staticContext))
475
476 if node.getChildrenLength() == 0 and not hasattr(node, "finallyBlock"):
477 raise SyntaxError("Invalid try statement", tokenizer)
478
479 builder.TRY_finish(node)
480 return node
481
482
483 elif tokenType == "catch" or tokenType == "finally":
484 raise SyntaxError(tokenizer.tokens[tokenType] + " without preceding try", tokenizer)
485
486
487 elif tokenType == "throw":
488 node = builder.THROW_build(tokenizer)
489
490 builder.THROW_setException(node, Expression(tokenizer, staticContext))
491 builder.THROW_finish(node)
492
493 # NO RETURN
494
495
496 elif tokenType == "return":
497 node = returnOrYield(tokenizer, staticContext)
498
499 # NO RETURN
500
501
502 elif tokenType == "with":
503 node = builder.WITH_build(tokenizer)
504
505 builder.WITH_setObject(node, ParenExpression(tokenizer, staticContext))
506 builder.WITH_setBody(node, nest(tokenizer, staticContext, node, Statement))
507 builder.WITH_finish(node)
508
509 return node
510
511
512 elif tokenType == "var" or tokenType == "const":
513 node = Variables(tokenizer, staticContext)
514
515 # NO RETURN
516
517
518 elif tokenType == "let":
519 if tokenizer.peek() == "left_paren":
520 node = LetBlock(tokenizer, staticContext, True)
521 else:
522 node = Variables(tokenizer, staticContext)
523
524 # NO RETURN
525
526
527 elif tokenType == "debugger":
528 node = builder.DEBUGGER_build(tokenizer)
529
530 # NO RETURN
531
532
533 elif tokenType == "newline" or tokenType == "semicolon":
534 node = builder.SEMICOLON_build(tokenizer)
535
536 builder.SEMICOLON_setExpression(node, None)
537 builder.SEMICOLON_finish(tokenizer)
538
539 return node
540
541
542 else:
543 if tokenType == "identifier":
544 tokenType = tokenizer.peek()
545
546 # Labeled statement.
547 if tokenType == "colon":
548 label = tokenizer.token.value
549 statementStack = staticContext.statementStack
550
551 i = len(statementStack)-1
552 while i >= 0:
553 if getattr(statementStack[i], "label", None) == label:
554 raise SyntaxError("Duplicate label", tokenizer)
555
556 i -= 1
557
558 tokenizer.get()
559 node = builder.LABEL_build(tokenizer)
560
561 builder.LABEL_setLabel(node, label)
562 builder.LABEL_setStatement(node, nest(tokenizer, staticContext, node, Statement))
563 builder.LABEL_finish(node)
564
565 return node
566
567 # Expression statement.
568 # We unget the current token to parse the expression as a whole.
569 node = builder.SEMICOLON_build(tokenizer)
570 tokenizer.unget()
571 builder.SEMICOLON_setExpression(node, Expression(tokenizer, staticContext))
572 node.end = node.expression.end
573 builder.SEMICOLON_finish(node)
574
575 # NO RETURN
576
577
578 MagicalSemicolon(tokenizer)
579 return node
580
581
582
583 def MagicalSemicolon(tokenizer):
584 if tokenizer.line == tokenizer.token.line:
585 tokenType = tokenizer.peekOnSameLine()
586
587 if tokenType != "end" and tokenType != "newline" and tokenType != "semicolon" and tokenType != "right_curly":
588 raise SyntaxError("Missing ; before statement", tokenizer)
589
590 tokenizer.match("semicolon")
591
592
593
594 def returnOrYield(tokenizer, staticContext):
595 builder = staticContext.builder
596 tokenType = tokenizer.token.type
597
598 if tokenType == "return":
599 if not staticContext.inFunction:
600 raise SyntaxError("Return not in function", tokenizer)
601
602 node = builder.RETURN_build(tokenizer)
603
604 else:
605 if not staticContext.inFunction:
606 raise SyntaxError("Yield not in function", tokenizer)
607
608 staticContext.isGenerator = True
609 node = builder.YIELD_build(tokenizer)
610
611 nextTokenType = tokenizer.peek(True)
612 if nextTokenType != "end" and nextTokenType != "newline" and nextTokenType != "semicolon" and nextTokenType != "right_curly" and (tokenType != "yield" or (nextTokenType != tokenType and nextTokenType != "right_bracket" and nextTokenType != "right_paren" and nextTokenType != "colon" and nextTokenType != "comma")):
613 if tokenType == "return":
614 builder.RETURN_setValue(node, Expression(tokenizer, staticContext))
615 staticContext.hasReturnWithValue = True
616 else:
617 builder.YIELD_setValue(node, AssignExpression(tokenizer, staticContext))
618
619 elif tokenType == "return":
620 staticContext.hasEmptyReturn = True
621
622 # Disallow return v; in generator.
623 if staticContext.hasReturnWithValue and staticContext.isGenerator:
624 raise SyntaxError("Generator returns a value", tokenizer)
625
626 if tokenType == "return":
627 builder.RETURN_finish(node)
628 else:
629 builder.YIELD_finish(node)
630
631 return node
632
633
634
635 def FunctionDefinition(tokenizer, staticContext, requireName, functionForm):
636 builder = staticContext.builder
637 functionNode = builder.FUNCTION_build(tokenizer)
638
639 if tokenizer.match("identifier"):
640 builder.FUNCTION_setName(functionNode, tokenizer.token.value)
641 elif requireName:
642 raise SyntaxError("Missing def identifier", tokenizer)
643
644 tokenizer.mustMatch("left_paren")
645
646 if not tokenizer.match("right_paren"):
647 builder.FUNCTION_initParams(functionNode, tokenizer)
648 prevParamNode = None
649 while True:
650 tokenType = tokenizer.get()
651 if tokenType == "left_bracket" or tokenType == "left_curly":
652 # Destructured formal parameters.
653 tokenizer.unget()
654 paramNode = DestructuringExpression(tokenizer, staticContext)
655
656 elif tokenType == "identifier":
657 paramNode = builder.FUNCTION_wrapParam(tokenizer)
658
659 else:
660 raise SyntaxError("Missing formal parameter", tokenizer)
661
662 builder.FUNCTION_addParam(functionNode, tokenizer, paramNode)
663 builder.COMMENTS_add(paramNode, prevParamNode, tokenizer.getComments())
664
665 if not tokenizer.match("comma"):
666 break
667
668 prevParamNode = paramNode
669
670 tokenizer.mustMatch("right_paren")
671
672 # Do we have an expression closure or a normal body?
673 tokenType = tokenizer.get()
674 if tokenType != "left_curly":
675 builder.FUNCTION_setExpressionClosure(functionNode, True)
676 tokenizer.unget()
677
678 childContext = StaticContext(True, builder)
679 tokenizer.save()
680
681 if staticContext.inFunction:
682 # Inner functions don't reset block numbering, only functions at
683 # the top level of the program do.
684 childContext.blockId = staticContext.blockId
685
686 if tokenType != "left_curly":
687 builder.FUNCTION_setBody(functionNode, AssignExpression(tokenizer, staticContext))
688 if staticContext.isGenerator:
689 raise SyntaxError("Generator returns a value", tokenizer)
690
691 else:
692 builder.FUNCTION_hoistVars(childContext.blockId)
693 builder.FUNCTION_setBody(functionNode, Script(tokenizer, childContext))
694
695 #
696 # Hoisting makes parse-time binding analysis tricky. A taxonomy of hoists:
697 #
698 # 1. vars hoist to the top of their function:
699 #
700 # var x = 'global';
701 # function f() {
702 # x = 'f';
703 # if (false)
704 # var x;
705 # }
706 # f();
707 # print(x); // "global"
708 #
709 # 2. lets hoist to the top of their block:
710 #
711 # function f() { // id: 0
712 # var x = 'f';
713 # {
714 # {
715 # print(x); // "undefined"
716 # }
717 # let x;
718 # }
719 # }
720 # f();
721 #
722 # 3. inner functions at function top-level hoist to the beginning
723 # of the function.
724 #
725 # If the builder used is doing parse-time analyses, hoisting may
726 # invalidate earlier conclusions it makes about variable scope.
727 #
728 # The builder can opt to set the needsHoisting flag in a
729 # CompilerContext (in the case of var and function hoisting) or in a
730 # node of type BLOCK (in the case of let hoisting). This signals for
731 # the parser to reparse sections of code.
732 #
733 # To avoid exponential blowup, if a function at the program top-level
734 # has any hoists in its child blocks or inner functions, we reparse
735 # the entire toplevel function. Each toplevel function is parsed at
736 # most twice.
737 #
738 # The list of declarations can be tied to block ids to aid talking
739 # about declarations of blocks that have not yet been fully parsed.
740 #
741 # Blocks are already uniquely numbered; see the comment in
742 # Statements.
743 #
744
745 #
746 # wpbasti:
747 # Don't have the feeling that I need this functionality because the
748 # tree is often modified before the variables and names inside are
749 # of any interest. So better doing this in a post-scan.
750 #
751
752 #
753 # if childContext.needsHoisting:
754 # # Order is important here! Builders expect functions to come after variables!
755 # builder.setHoists(functionNode.body.id, childContext.variables.concat(childContext.functions))
756 #
757 # if staticContext.inFunction:
758 # # If an inner function needs hoisting, we need to propagate
759 # # this flag up to the parent function.
760 # staticContext.needsHoisting = True
761 #
762 # else:
763 # # Only re-parse functions at the top level of the program.
764 # childContext = StaticContext(True, builder)
765 # tokenizer.rewind(rp)
766 #
767 # # Set a flag in case the builder wants to have different behavior
768 # # on the second pass.
769 # builder.secondPass = True
770 # builder.FUNCTION_hoistVars(functionNode.body.id, True)
771 # builder.FUNCTION_setBody(functionNode, Script(tokenizer, childContext))
772 # builder.secondPass = False
773
774 if tokenType == "left_curly":
775 tokenizer.mustMatch("right_curly")
776
777 functionNode.end = tokenizer.token.end
778 functionNode.functionForm = functionForm
779
780 builder.COMMENTS_add(functionNode.body, functionNode.body, tokenizer.getComments())
781 builder.FUNCTION_finish(functionNode, staticContext)
782
783 return functionNode
784
785
786
787 def Variables(tokenizer, staticContext, letBlock=None):
788 """Parses a comma-separated list of var declarations (and maybe initializations)."""
789
790 builder = staticContext.builder
791 if tokenizer.token.type == "var":
792 build = builder.VAR_build
793 addDecl = builder.VAR_addDecl
794 finish = builder.VAR_finish
795 childContext = staticContext
796
797 elif tokenizer.token.type == "const":
798 build = builder.CONST_build
799 addDecl = builder.CONST_addDecl
800 finish = builder.CONST_finish
801 childContext = staticContext
802
803 elif tokenizer.token.type == "let" or tokenizer.token.type == "left_paren":
804 build = builder.LET_build
805 addDecl = builder.LET_addDecl
806 finish = builder.LET_finish
807
808 if not letBlock:
809 statementStack = staticContext.statementStack
810 i = len(statementStack) - 1
811
812 # a BLOCK *must* be found.
813 while statementStack[i].type != "block":
814 i -= 1
815
816 # Lets at the def toplevel are just vars, at least in SpiderMonkey.
817 if i == 0:
818 build = builder.VAR_build
819 addDecl = builder.VAR_addDecl
820 finish = builder.VAR_finish
821 childContext = staticContext
822
823 else:
824 childContext = statementStack[i]
825
826 else:
827 childContext = letBlock
828
829 node = build(tokenizer)
830
831 while True:
832 tokenType = tokenizer.get()
833
834 # Done in Python port!
835 # FIXME Should have a special DECLARATION node instead of overloading
836 # IDENTIFIER to mean both identifier declarations and destructured
837 # declarations.
838 childNode = builder.DECL_build(tokenizer)
839
840 if tokenType == "left_bracket" or tokenType == "left_curly":
841 # Pass in childContext if we need to add each pattern matched into
842 # its variables, else pass in staticContext.
843 # Need to unget to parse the full destructured expression.
844 tokenizer.unget()
845 builder.DECL_setNames(childNode, DestructuringExpression(tokenizer, staticContext, True, childContext))
846
847 if staticContext.inForLoopInit and tokenizer.peek() == "in":
848 addDecl(node, childNode, childContext)
849 if tokenizer.match("comma"):
850 continue
851 else:
852 break
853
854 tokenizer.mustMatch("assign")
855 if tokenizer.token.assignOp:
856 raise SyntaxError("Invalid variable initialization", tokenizer)
857
858 # Parse the init as a normal assignment.
859 builder.DECL_setInitializer(childNode, AssignExpression(tokenizer, staticContext))
860 builder.DECL_finish(childNode)
861 addDecl(node, childNode, childContext)
862
863 # Copy over names for variable list
864 # for nameNode in childNode.names:
865 # childContext.variables.add(nameNode.value)
866
867 if tokenizer.match("comma"):
868 continue
869 else:
870 break
871
872 if tokenType != "identifier":
873 raise SyntaxError("Missing variable name", tokenizer)
874
875 builder.DECL_setName(childNode, tokenizer.token.value)
876 builder.DECL_setReadOnly(childNode, node.type == "const")
877 addDecl(node, childNode, childContext)
878
879 if tokenizer.match("assign"):
880 if tokenizer.token.assignOp:
881 raise SyntaxError("Invalid variable initialization", tokenizer)
882
883 initializerNode = AssignExpression(tokenizer, staticContext)
884 builder.DECL_setInitializer(childNode, initializerNode)
885
886 builder.DECL_finish(childNode)
887
888 # If we directly use the node in "let" constructs
889 # if not hasattr(childContext, "variables"):
890 # childContext.variables = set()
891
892 # childContext.variables.add(childNode.name)
893
894 if not tokenizer.match("comma"):
895 break
896
897 finish(node)
898 return node
899
900
901
902 def LetBlock(tokenizer, staticContext, isStatement):
903 """Does not handle let inside of for loop init."""
904 builder = staticContext.builder
905
906 # tokenizer.token.type must be "let"
907 node = builder.LETBLOCK_build(tokenizer)
908 tokenizer.mustMatch("left_paren")
909 builder.LETBLOCK_setVariables(node, Variables(tokenizer, staticContext, node))
910 tokenizer.mustMatch("right_paren")
911
912 if isStatement and tokenizer.peek() != "left_curly":
913 # If this is really an expression in let statement guise, then we
914 # need to wrap the "let_block" node in a "semicolon" node so that we pop
915 # the return value of the expression.
916 childNode = builder.SEMICOLON_build(tokenizer)
917 builder.SEMICOLON_setExpression(childNode, node)
918 builder.SEMICOLON_finish(childNode)
919 isStatement = False
920
921 if isStatement:
922 childNode = Block(tokenizer, staticContext)
923 builder.LETBLOCK_setBlock(node, childNode)
924
925 else:
926 childNode = AssignExpression(tokenizer, staticContext)
927 builder.LETBLOCK_setExpression(node, childNode)
928
929 builder.LETBLOCK_finish(node)
930 return node
931
932
933 def checkDestructuring(tokenizer, staticContext, node, simpleNamesOnly=None, data=None):
934 if node.type == "array_comp":
935 raise SyntaxError("Invalid array comprehension left-hand side", tokenizer)
936
937 if node.type != "array_init" and node.type != "object_init":
938 return
939
940 builder = staticContext.builder
941
942 for child in node:
943 if child == None:
944 continue
945
946 if child.type == "property_init":
947 lhs = child[0]
948 rhs = child[1]
949 else:
950 lhs = None
951 rhs = None
952
953
954 if rhs and (rhs.type == "array_init" or rhs.type == "object_init"):
955 checkDestructuring(tokenizer, staticContext, rhs, simpleNamesOnly, data)
956
957 if lhs and simpleNamesOnly:
958 # In declarations, lhs must be simple names
959 if lhs.type != "identifier":
960 raise SyntaxError("Missing name in pattern", tokenizer)
961
962 elif data:
963 childNode = builder.DECL_build(tokenizer)
964 builder.DECL_setName(childNode, lhs.value)
965
966 # Don't need to set initializer because it's just for
967 # hoisting anyways.
968 builder.DECL_finish(childNode)
969
970 # Each pattern needs to be added to variables.
971 # data.variables.add(childNode.name)
972
973
974 # JavaScript 1.7
975 def DestructuringExpression(tokenizer, staticContext, simpleNamesOnly=None, data=None):
976 node = PrimaryExpression(tokenizer, staticContext)
977 checkDestructuring(tokenizer, staticContext, node, simpleNamesOnly, data)
978
979 return node
980
981
982 # JavsScript 1.7
983 def GeneratorExpression(tokenizer, staticContext, expression):
984 builder = staticContext.builder
985 node = builder.GENERATOR_build(tokenizer)
986
987 builder.GENERATOR_setExpression(node, expression)
988 builder.GENERATOR_setTail(node, comprehensionTail(tokenizer, staticContext))
989 builder.GENERATOR_finish(node)
990
991 return node
992
993
994 # JavaScript 1.7 Comprehensions Tails (Generators / Arrays)
995 def comprehensionTail(tokenizer, staticContext):
996 builder = staticContext.builder
997
998 # tokenizer.token.type must be "for"
999 body = builder.COMPTAIL_build(tokenizer)
1000
1001 while True:
1002 node = builder.FOR_build(tokenizer)
1003
1004 # Comprehension tails are always for..in loops.
1005 builder.FOR_rebuildForIn(node)
1006 if tokenizer.match("identifier"):
1007 # But sometimes they're for each..in.
1008 if tokenizer.token.value == "each":
1009 builder.FOR_rebuildForEach(node)
1010 else:
1011 tokenizer.unget()
1012
1013 tokenizer.mustMatch("left_paren")
1014
1015 tokenType = tokenizer.get()
1016 if tokenType == "left_bracket" or tokenType == "left_curly":
1017 tokenizer.unget()
1018 # Destructured left side of for in comprehension tails.
1019 builder.FOR_setIterator(node, DestructuringExpression(tokenizer, staticContext))
1020
1021 elif tokenType == "identifier":
1022 # Removed variable/declaration substructure in Python port.
1023 # Variable declarations are not allowed here. So why process them in such a way?
1024
1025 # declaration = builder.DECL_build(tokenizer)
1026 # builder.DECL_setName(declaration, tokenizer.token.value)
1027 # builder.DECL_finish(declaration)
1028 # childNode = builder.VAR_build(tokenizer)
1029 # builder.VAR_addDecl(childNode, declaration)
1030 # builder.VAR_finish(childNode)
1031 # builder.FOR_setIterator(node, declaration)
1032
1033 # Don't add to variables since the semantics of comprehensions is
1034 # such that the variables are in their own def when desugared.
1035
1036 identifier = builder.PRIMARY_build(tokenizer, "identifier")
1037 builder.FOR_setIterator(node, identifier)
1038
1039 else:
1040 raise SyntaxError("Missing identifier", tokenizer)
1041
1042 tokenizer.mustMatch("in")
1043 builder.FOR_setObject(node, Expression(tokenizer, staticContext))
1044 tokenizer.mustMatch("right_paren")
1045 builder.COMPTAIL_addFor(body, node)
1046
1047 if not tokenizer.match("for"):
1048 break
1049
1050 # Optional guard.
1051 if tokenizer.match("if"):
1052 builder.COMPTAIL_setGuard(body, ParenExpression(tokenizer, staticContext))
1053
1054 builder.COMPTAIL_finish(body)
1055
1056 return body
1057
1058
1059 def ParenExpression(tokenizer, staticContext):
1060 tokenizer.mustMatch("left_paren")
1061
1062 # Always accept the 'in' operator in a parenthesized expression,
1063 # where it's unambiguous, even if we might be parsing the init of a
1064 # for statement.
1065 oldLoopInit = staticContext.inForLoopInit
1066 staticContext.inForLoopInit = False
1067 node = Expression(tokenizer, staticContext)
1068 staticContext.inForLoopInit = oldLoopInit
1069
1070 err = "expression must be parenthesized"
1071 if tokenizer.match("for"):
1072 if node.type == "yield" and not node.parenthesized:
1073 raise SyntaxError("Yield " + err, tokenizer)
1074
1075 if node.type == "comma" and not node.parenthesized:
1076 raise SyntaxError("Generator " + err, tokenizer)
1077
1078 node = GeneratorExpression(tokenizer, staticContext, node)
1079
1080 tokenizer.mustMatch("right_paren")
1081
1082 return node
1083
1084
1085 def Expression(tokenizer, staticContext):
1086 """Top-down expression parser matched against SpiderMonkey."""
1087 builder = staticContext.builder
1088 node = AssignExpression(tokenizer, staticContext)
1089
1090 if tokenizer.match("comma"):
1091 childNode = builder.COMMA_build(tokenizer)
1092 builder.COMMA_addOperand(childNode, node)
1093 node = childNode
1094 while True:
1095 childNode = node[len(node)-1]
1096 if childNode.type == "yield" and not childNode.parenthesized:
1097 raise SyntaxError("Yield expression must be parenthesized", tokenizer)
1098 builder.COMMA_addOperand(node, AssignExpression(tokenizer, staticContext))
1099
1100 if not tokenizer.match("comma"):
1101 break
1102
1103 builder.COMMA_finish(node)
1104
1105 return node
1106
1107
1108 def AssignExpression(tokenizer, staticContext):
1109 builder = staticContext.builder
1110
1111 # Have to treat yield like an operand because it could be the leftmost
1112 # operand of the expression.
1113 if tokenizer.match("yield", True):
1114 return returnOrYield(tokenizer, staticContext)
1115
1116 comments = tokenizer.getComments()
1117 node = builder.ASSIGN_build(tokenizer)
1118 lhs = ConditionalExpression(tokenizer, staticContext)
1119 builder.COMMENTS_add(lhs, None, comments)
1120
1121 if not tokenizer.match("assign"):
1122 builder.ASSIGN_finish(node)
1123 return lhs
1124
1125 if lhs.type == "object_init" or lhs.type == "array_init":
1126 checkDestructuring(tokenizer, staticContext, lhs)
1127 elif lhs.type == "identifier" or lhs.type == "dot" or lhs.type == "index" or lhs.type == "call":
1128 pass
1129 else:
1130 raise SyntaxError("Bad left-hand side of assignment", tokenizer)
1131
1132 builder.ASSIGN_setAssignOp(node, tokenizer.token.assignOp)
1133 builder.ASSIGN_addOperand(node, lhs)
1134 builder.ASSIGN_addOperand(node, AssignExpression(tokenizer, staticContext))
1135 builder.ASSIGN_finish(node)
1136
1137 return node
1138
1139
1140 def ConditionalExpression(tokenizer, staticContext):
1141 builder = staticContext.builder
1142 node = OrExpression(tokenizer, staticContext)
1143
1144 if tokenizer.match("hook"):
1145 childNode = node
1146 node = builder.HOOK_build(tokenizer)
1147 builder.HOOK_setCondition(node, childNode)
1148
1149 # Always accept the 'in' operator in the middle clause of a ternary,
1150 # where it's unambiguous, even if we might be parsing the init of a
1151 # for statement.
1152 oldLoopInit = staticContext.inForLoopInit
1153 staticContext.inForLoopInit = False
1154 builder.HOOK_setThenPart(node, AssignExpression(tokenizer, staticContext))
1155 staticContext.inForLoopInit = oldLoopInit
1156
1157 if not tokenizer.match("colon"):
1158 raise SyntaxError("Missing : after ?", tokenizer)
1159
1160 builder.HOOK_setElsePart(node, AssignExpression(tokenizer, staticContext))
1161 builder.HOOK_finish(node)
1162
1163 return node
1164
1165
1166 def OrExpression(tokenizer, staticContext):
1167 builder = staticContext.builder
1168 node = AndExpression(tokenizer, staticContext)
1169
1170 while tokenizer.match("or"):
1171 childNode = builder.OR_build(tokenizer)
1172 builder.OR_addOperand(childNode, node)
1173 builder.OR_addOperand(childNode, AndExpression(tokenizer, staticContext))
1174 builder.OR_finish(childNode)
1175 node = childNode
1176
1177 return node
1178
1179
1180 def AndExpression(tokenizer, staticContext):
1181 builder = staticContext.builder
1182 node = BitwiseOrExpression(tokenizer, staticContext)
1183
1184 while tokenizer.match("and"):
1185 childNode = builder.AND_build(tokenizer)
1186 builder.AND_addOperand(childNode, node)
1187 builder.AND_addOperand(childNode, BitwiseOrExpression(tokenizer, staticContext))
1188 builder.AND_finish(childNode)
1189 node = childNode
1190
1191 return node
1192
1193
1194 def BitwiseOrExpression(tokenizer, staticContext):
1195 builder = staticContext.builder
1196 node = BitwiseXorExpression(tokenizer, staticContext)
1197
1198 while tokenizer.match("bitwise_or"):
1199 childNode = builder.BITWISEOR_build(tokenizer)
1200 builder.BITWISEOR_addOperand(childNode, node)
1201 builder.BITWISEOR_addOperand(childNode, BitwiseXorExpression(tokenizer, staticContext))
1202 builder.BITWISEOR_finish(childNode)
1203 node = childNode
1204
1205 return node
1206
1207
1208 def BitwiseXorExpression(tokenizer, staticContext):
1209 builder = staticContext.builder
1210 node = BitwiseAndExpression(tokenizer, staticContext)
1211
1212 while tokenizer.match("bitwise_xor"):
1213 childNode = builder.BITWISEXOR_build(tokenizer)
1214 builder.BITWISEXOR_addOperand(childNode, node)
1215 builder.BITWISEXOR_addOperand(childNode, BitwiseAndExpression(tokenizer, staticContext))
1216 builder.BITWISEXOR_finish(childNode)
1217 node = childNode
1218
1219 return node
1220
1221
1222 def BitwiseAndExpression(tokenizer, staticContext):
1223 builder = staticContext.builder
1224 node = EqualityExpression(tokenizer, staticContext)
1225
1226 while tokenizer.match("bitwise_and"):
1227 childNode = builder.BITWISEAND_build(tokenizer)
1228 builder.BITWISEAND_addOperand(childNode, node)
1229 builder.BITWISEAND_addOperand(childNode, EqualityExpression(tokenizer, staticContext))
1230 builder.BITWISEAND_finish(childNode)
1231 node = childNode
1232
1233 return node
1234
1235
1236 def EqualityExpression(tokenizer, staticContext):
1237 builder = staticContext.builder
1238 node = RelationalExpression(tokenizer, staticContext)
1239
1240 while tokenizer.match("eq") or tokenizer.match("ne") or tokenizer.match("strict_eq") or tokenizer.match("strict_ne"):
1241 childNode = builder.EQUALITY_build(tokenizer)
1242 builder.EQUALITY_addOperand(childNode, node)
1243 builder.EQUALITY_addOperand(childNode, RelationalExpression(tokenizer, staticContext))
1244 builder.EQUALITY_finish(childNode)
1245 node = childNode
1246
1247 return node
1248
1249
1250 def RelationalExpression(tokenizer, staticContext):
1251 builder = staticContext.builder
1252 oldLoopInit = staticContext.inForLoopInit
1253
1254 # Uses of the in operator in shiftExprs are always unambiguous,
1255 # so unset the flag that prohibits recognizing it.
1256 staticContext.inForLoopInit = False
1257 node = ShiftExpression(tokenizer, staticContext)
1258
1259 while tokenizer.match("lt") or tokenizer.match("le") or tokenizer.match("ge") or tokenizer.match("gt") or (oldLoopInit == False and tokenizer.match("in")) or tokenizer.match("instanceof"):
1260 childNode = builder.RELATIONAL_build(tokenizer)
1261 builder.RELATIONAL_addOperand(childNode, node)
1262 builder.RELATIONAL_addOperand(childNode, ShiftExpression(tokenizer, staticContext))
1263 builder.RELATIONAL_finish(childNode)
1264 node = childNode
1265
1266 staticContext.inForLoopInit = oldLoopInit
1267
1268 return node
1269
1270
1271 def ShiftExpression(tokenizer, staticContext):
1272 builder = staticContext.builder
1273 node = AddExpression(tokenizer, staticContext)
1274
1275 while tokenizer.match("lsh") or tokenizer.match("rsh") or tokenizer.match("ursh"):
1276 childNode = builder.SHIFT_build(tokenizer)
1277 builder.SHIFT_addOperand(childNode, node)
1278 builder.SHIFT_addOperand(childNode, AddExpression(tokenizer, staticContext))
1279 builder.SHIFT_finish(childNode)
1280 node = childNode
1281
1282 return node
1283
1284
1285 def AddExpression(tokenizer, staticContext):
1286 builder = staticContext.builder
1287 node = MultiplyExpression(tokenizer, staticContext)
1288
1289 while tokenizer.match("plus") or tokenizer.match("minus"):
1290 childNode = builder.ADD_build(tokenizer)
1291 builder.ADD_addOperand(childNode, node)
1292 builder.ADD_addOperand(childNode, MultiplyExpression(tokenizer, staticContext))
1293 builder.ADD_finish(childNode)
1294 node = childNode
1295
1296 return node
1297
1298
1299 def MultiplyExpression(tokenizer, staticContext):
1300 builder = staticContext.builder
1301 node = UnaryExpression(tokenizer, staticContext)
1302
1303 while tokenizer.match("mul") or tokenizer.match("div") or tokenizer.match("mod"):
1304 childNode = builder.MULTIPLY_build(tokenizer)
1305 builder.MULTIPLY_addOperand(childNode, node)
1306 builder.MULTIPLY_addOperand(childNode, UnaryExpression(tokenizer, staticContext))
1307 builder.MULTIPLY_finish(childNode)
1308 node = childNode
1309
1310 return node
1311
1312
1313 def UnaryExpression(tokenizer, staticContext):
1314 builder = staticContext.builder
1315 tokenType = tokenizer.get(True)
1316
1317 if tokenType in ["delete", "void", "typeof", "not", "bitwise_not", "plus", "minus"]:
1318 node = builder.UNARY_build(tokenizer)
1319 builder.UNARY_addOperand(node, UnaryExpression(tokenizer, staticContext))
1320
1321 elif tokenType == "increment" or tokenType == "decrement":
1322 # Prefix increment/decrement.
1323 node = builder.UNARY_build(tokenizer)
1324 builder.UNARY_addOperand(node, MemberExpression(tokenizer, staticContext, True))
1325
1326 else:
1327 tokenizer.unget()
1328 node = MemberExpression(tokenizer, staticContext, True)
1329
1330 # Don't look across a newline boundary for a postfix {in,de}crement.
1331 if tokenizer.tokens[(tokenizer.tokenIndex + tokenizer.lookahead - 1) & 3].line == tokenizer.line:
1332 if tokenizer.match("increment") or tokenizer.match("decrement"):
1333 childNode = builder.UNARY_build(tokenizer)
1334 builder.UNARY_setPostfix(childNode)
1335 builder.UNARY_finish(node)
1336 builder.UNARY_addOperand(childNode, node)
1337 node = childNode
1338
1339 builder.UNARY_finish(node)
1340 return node
1341
1342
1343 def MemberExpression(tokenizer, staticContext, allowCallSyntax):
1344 builder = staticContext.builder
1345
1346 if tokenizer.match("new"):
1347 node = builder.MEMBER_build(tokenizer)
1348 builder.MEMBER_addOperand(node, MemberExpression(tokenizer, staticContext, False))
1349
1350 if tokenizer.match("left_paren"):
1351 builder.MEMBER_rebuildNewWithArgs(node)
1352 builder.MEMBER_addOperand(node, ArgumentList(tokenizer, staticContext))
1353
1354 builder.MEMBER_finish(node)
1355
1356 else:
1357 node = PrimaryExpression(tokenizer, staticContext)
1358
1359 while True:
1360 tokenType = tokenizer.get()
1361 if tokenType == "end":
1362 break
1363
1364 if tokenType == "dot":
1365 childNode = builder.MEMBER_build(tokenizer)
1366 builder.MEMBER_addOperand(childNode, node)
1367 tokenizer.mustMatch("identifier")
1368 builder.MEMBER_addOperand(childNode, builder.MEMBER_build(tokenizer))
1369
1370 elif tokenType == "left_bracket":
1371 childNode = builder.MEMBER_build(tokenizer, "index")
1372 builder.MEMBER_addOperand(childNode, node)
1373 builder.MEMBER_addOperand(childNode, Expression(tokenizer, staticContext))
1374 tokenizer.mustMatch("right_bracket")
1375
1376 elif tokenType == "left_paren" and allowCallSyntax:
1377 childNode = builder.MEMBER_build(tokenizer, "call")
1378 builder.MEMBER_addOperand(childNode, node)
1379 builder.MEMBER_addOperand(childNode, ArgumentList(tokenizer, staticContext))
1380
1381 else:
1382 tokenizer.unget()
1383 return node
1384
1385 builder.MEMBER_finish(childNode)
1386 node = childNode
1387
1388 return node
1389
1390
1391 def ArgumentList(tokenizer, staticContext):
1392 builder = staticContext.builder
1393 node = builder.LIST_build(tokenizer)
1394
1395 if tokenizer.match("right_paren", True):
1396 return node
1397
1398 while True:
1399 childNode = AssignExpression(tokenizer, staticContext)
1400 if childNode.type == "yield" and not childNode.parenthesized and tokenizer.peek() == "comma":
1401 raise SyntaxError("Yield expression must be parenthesized", tokenizer)
1402
1403 if tokenizer.match("for"):
1404 childNode = GeneratorExpression(tokenizer, staticContext, childNode)
1405 if len(node) > 1 or tokenizer.peek(True) == "comma":
1406 raise SyntaxError("Generator expression must be parenthesized", tokenizer)
1407
1408 builder.LIST_addOperand(node, childNode)
1409 if not tokenizer.match("comma"):
1410 break
1411
1412 tokenizer.mustMatch("right_paren")
1413 builder.LIST_finish(node)
1414
1415 return node
1416
1417
1418 def PrimaryExpression(tokenizer, staticContext):
1419 builder = staticContext.builder
1420 tokenType = tokenizer.get(True)
1421
1422 if tokenType == "function":
1423 node = FunctionDefinition(tokenizer, staticContext, False, "expressed_form")
1424
1425 elif tokenType == "left_bracket":
1426 node = builder.ARRAYINIT_build(tokenizer)
1427 while True:
1428 tokenType = tokenizer.peek(True)
1429 if tokenType == "right_bracket":
1430 break
1431
1432 if tokenType == "comma":
1433 tokenizer.get()
1434 builder.ARRAYINIT_addElement(node, None)
1435 continue
1436
1437 builder.ARRAYINIT_addElement(node, AssignExpression(tokenizer, staticContext))
1438
1439 if tokenType != "comma" and not tokenizer.match("comma"):
1440 break
1441
1442 # If we matched exactly one element and got a "for", we have an
1443 # array comprehension.
1444 if len(node) == 1 and tokenizer.match("for"):
1445 childNode = builder.ARRAYCOMP_build(tokenizer)
1446 builder.ARRAYCOMP_setExpression(childNode, node[0])
1447 builder.ARRAYCOMP_setTail(childNode, comprehensionTail(tokenizer, staticContext))
1448 node = childNode
1449
1450 builder.COMMENTS_add(node, node, tokenizer.getComments())
1451 tokenizer.mustMatch("right_bracket")
1452 builder.PRIMARY_finish(node)
1453
1454 elif tokenType == "left_curly":
1455 node = builder.OBJECTINIT_build(tokenizer)
1456
1457 if not tokenizer.match("right_curly"):
1458 while True:
1459 tokenType = tokenizer.get()
1460 tokenValue = getattr(tokenizer.token, "value", None)
1461 comments = tokenizer.getComments()
1462
1463 if tokenValue in ("get", "set") and tokenizer.peek() == "identifier":
1464 if staticContext.ecma3OnlyMode:
1465 raise SyntaxError("Illegal property accessor", tokenizer)
1466
1467 fd = FunctionDefinition(tokenizer, staticContext, True, "expressed_form")
1468 builder.OBJECTINIT_addProperty(node, fd)
1469
1470 else:
1471 if tokenType == "identifier" or tokenType == "number" or tokenType == "string":
1472 id = builder.PRIMARY_build(tokenizer, "identifier")
1473 builder.PRIMARY_finish(id)
1474
1475 elif tokenType == "right_curly":
1476 if staticContext.ecma3OnlyMode:
1477 raise SyntaxError("Illegal trailing ,", tokenizer)
1478
1479 tokenizer.unget()
1480 break
1481
1482 else:
1483 if tokenValue in jasy.js.tokenize.Lang.keywords:
1484 id = builder.PRIMARY_build(tokenizer, "identifier")
1485 builder.PRIMARY_finish(id)
1486 else:
1487 print("Value is '%s'" % tokenValue)
1488 raise SyntaxError("Invalid property name", tokenizer)
1489
1490 if tokenizer.match("colon"):
1491 childNode = builder.PROPERTYINIT_build(tokenizer)
1492 builder.COMMENTS_add(childNode, node, comments)
1493 builder.PROPERTYINIT_addOperand(childNode, id)
1494 builder.PROPERTYINIT_addOperand(childNode, AssignExpression(tokenizer, staticContext))
1495 builder.PROPERTYINIT_finish(childNode)
1496 builder.OBJECTINIT_addProperty(node, childNode)
1497
1498 else:
1499 # Support, e.g., |var {staticContext, y} = o| as destructuring shorthand
1500 # for |var {staticContext: staticContext, y: y} = o|, per proposed JS2/ES4 for JS1.8.
1501 if tokenizer.peek() != "comma" and tokenizer.peek() != "right_curly":
1502 raise SyntaxError("Missing : after property", tokenizer)
1503 builder.OBJECTINIT_addProperty(node, id)
1504
1505 if not tokenizer.match("comma"):
1506 break
1507
1508 builder.COMMENTS_add(node, node, tokenizer.getComments())
1509 tokenizer.mustMatch("right_curly")
1510
1511 builder.OBJECTINIT_finish(node)
1512
1513 elif tokenType == "left_paren":
1514 # ParenExpression does its own matching on parentheses, so we need to unget.
1515 tokenizer.unget()
1516 node = ParenExpression(tokenizer, staticContext)
1517 node.parenthesized = True
1518
1519 elif tokenType == "let":
1520 node = LetBlock(tokenizer, staticContext, False)
1521
1522 elif tokenType in ["null", "this", "true", "false", "identifier", "number", "string", "regexp"]:
1523 node = builder.PRIMARY_build(tokenizer, tokenType)
1524 builder.PRIMARY_finish(node)
1525
1526 else:
1527 raise SyntaxError("Missing operand. Found type: %s" % tokenType, tokenizer)
1528
1529 return node

eric ide

mercurial