--- a/ThirdParty/Jasy/jasy/js/parse/Parser.py Thu Aug 10 13:58:50 2017 +0200 +++ b/ThirdParty/Jasy/jasy/js/parse/Parser.py Fri Aug 11 14:40:54 2017 +0200 @@ -1,11 +1,12 @@ # # Jasy - Web Tooling Framework # Copyright 2010-2012 Zynga Inc. +# Copyright 2013-2014 Sebastian Werner # # # License: MPL 1.1/GPL 2.0/LGPL 2.1 -# Authors: +# Authors: # - Brendan Eich <brendan@mozilla.org> (Original JavaScript) (2004-2010) # - Sebastian Werner <info@sebastian-werner.net> (Python Port) (2010-2012) # @@ -21,28 +22,29 @@ def parseExpression(source, fileId=None, line=1, builder=None): if builder == None: builder = jasy.js.parse.VanillaBuilder.VanillaBuilder() - + # Convert source into expression statement to be friendly to the Tokenizer if not source.endswith(";"): source = source + ";" - + tokenizer = jasy.js.tokenize.Tokenizer.Tokenizer(source, fileId, line) staticContext = StaticContext(False, builder) - + return Expression(tokenizer, staticContext) + def parse(source, fileId=None, line=1, builder=None): if builder == None: builder = jasy.js.parse.VanillaBuilder.VanillaBuilder() - + tokenizer = jasy.js.tokenize.Tokenizer.Tokenizer(source, fileId, line) staticContext = StaticContext(False, builder) node = Script(tokenizer, staticContext) - + # store fileId on top-level node node.fileId = tokenizer.fileId - + # add missing comments e.g. empty file with only a comment etc. # if there is something non-attached by an inner node it is attached to # the top level node, which is not correct, but might be better than @@ -51,7 +53,7 @@ builder.COMMENTS_add(node[-1], None, tokenizer.getComments()) else: builder.COMMENTS_add(node, None, tokenizer.getComments()) - + if not tokenizer.done(): raise SyntaxError("Unexpected end of file", tokenizer) @@ -61,39 +63,37 @@ class SyntaxError(Exception): def __init__(self, message, tokenizer): - Exception.__init__(self, "Syntax error: %s\n%s:%s" % ( - message, tokenizer.fileId, tokenizer.line)) + Exception.__init__(self, "Syntax error: %s\n%s:%s" % (message, tokenizer.fileId, tokenizer.line)) # Used as a status container during tree-building for every def body and the global body class StaticContext(object): # inFunction is used to check if a return stm appears in a valid context. def __init__(self, inFunction, builder): - # Whether this is inside a function, mostly True, only for top-level scope - # it's False + # Whether this is inside a function, mostly True, only for top-level scope it's False self.inFunction = inFunction - + self.hasEmptyReturn = False self.hasReturnWithValue = False self.isGenerator = False self.blockId = 0 self.builder = builder self.statementStack = [] - + # Sets to store variable uses # self.functions = set() # self.variables = set() - + # Status # self.needsHoisting = False self.bracketLevel = 0 self.curlyLevel = 0 self.parenLevel = 0 self.hookLevel = 0 - + # Configure strict ecmascript 3 mode self.ecma3OnlyMode = False - + # Status flag during parsing self.inForLoopInit = False @@ -101,16 +101,16 @@ def Script(tokenizer, staticContext): """Parses the toplevel and def bodies.""" node = Statements(tokenizer, staticContext) - + # change type from "block" to "script" for script root node.type = "script" - + # copy over data from compiler context # node.functions = staticContext.functions # node.variables = staticContext.variables return node - + def nest(tokenizer, staticContext, node, func, end=None): """Statement stack and nested statement handler.""" @@ -118,7 +118,7 @@ node = func(tokenizer, staticContext) staticContext.statementStack.pop() end and tokenizer.mustMatch(end) - + return node @@ -157,7 +157,7 @@ tokenizer.mustMatch("left_curly") node = Statements(tokenizer, staticContext) tokenizer.mustMatch("right_curly") - + return node @@ -169,25 +169,24 @@ # Cases for statements ending in a right curly return early, avoiding the # common semicolon insertion magic after this switch. - + if tokenType == "function": - # "declared_form" extends functions of staticContext, - # "statement_form" doesn'tokenizer. + # "declared_form" extends functions of staticContext, "statement_form" doesn'tokenizer. if len(staticContext.statementStack) > 1: kind = "statement_form" else: kind = "declared_form" - + return FunctionDefinition(tokenizer, staticContext, True, kind) - - + + elif tokenType == "left_curly": node = Statements(tokenizer, staticContext) tokenizer.mustMatch("right_curly") - + return node - - + + elif tokenType == "if": node = builder.IF_build(tokenizer) builder.IF_setCondition(node, ParenExpression(tokenizer, staticContext)) @@ -202,10 +201,10 @@ staticContext.statementStack.pop() builder.IF_finish(node) - + return node - - + + elif tokenType == "switch": # This allows CASEs after a "default", which is in the standard. node = builder.SWITCH_build(tokenizer) @@ -214,23 +213,23 @@ tokenizer.mustMatch("left_curly") tokenType = tokenizer.get() - + while tokenType != "right_curly": if tokenType == "default": if node.defaultIndex >= 0: raise SyntaxError("More than one switch default", tokenizer) - + childNode = builder.DEFAULT_build(tokenizer) builder.SWITCH_setDefaultIndex(node, len(node)-1) tokenizer.mustMatch("colon") builder.DEFAULT_initializeStatements(childNode, tokenizer) - + while True: tokenType=tokenizer.peek(True) if tokenType == "case" or tokenType == "default" or tokenType == "right_curly": break builder.DEFAULT_addStatement(childNode, Statement(tokenizer, staticContext)) - + builder.DEFAULT_finish(childNode) elif tokenType == "case": @@ -244,7 +243,7 @@ if tokenType == "case" or tokenType == "default" or tokenType == "right_curly": break builder.CASE_addStatement(childNode, Statement(tokenizer, staticContext)) - + builder.CASE_finish(childNode) else: @@ -257,32 +256,32 @@ builder.SWITCH_finish(node) return node - + elif tokenType == "for": node = builder.FOR_build(tokenizer) forBlock = None - + if tokenizer.match("identifier") and tokenizer.token.value == "each": builder.FOR_rebuildForEach(node) - + tokenizer.mustMatch("left_paren") tokenType = tokenizer.peek() childNode = None - + if tokenType != "semicolon": staticContext.inForLoopInit = True - + if tokenType == "var" or tokenType == "const": tokenizer.get() childNode = Variables(tokenizer, staticContext) - + elif tokenType == "let": tokenizer.get() if tokenizer.peek() == "left_paren": childNode = LetBlock(tokenizer, staticContext, False) - + else: # Let in for head, we need to add an implicit block # around the rest of the for. @@ -290,72 +289,72 @@ staticContext.blockId += 1 staticContext.statementStack.append(forBlock) childNode = Variables(tokenizer, staticContext, forBlock) - + else: childNode = Expression(tokenizer, staticContext) - + staticContext.inForLoopInit = False if childNode and tokenizer.match("in"): builder.FOR_rebuildForIn(node) builder.FOR_setObject(node, Expression(tokenizer, staticContext), forBlock) - + if childNode.type == "var" or childNode.type == "let": if len(childNode) != 1: raise SyntaxError("Invalid for..in left-hand side", tokenizer) builder.FOR_setIterator(node, childNode, forBlock) - + else: builder.FOR_setIterator(node, childNode, forBlock) else: builder.FOR_setSetup(node, childNode) tokenizer.mustMatch("semicolon") - + if node.isEach: raise SyntaxError("Invalid for each..in loop", tokenizer) - + if tokenizer.peek() == "semicolon": builder.FOR_setCondition(node, None) else: builder.FOR_setCondition(node, Expression(tokenizer, staticContext)) - + tokenizer.mustMatch("semicolon") - + if tokenizer.peek() == "right_paren": builder.FOR_setUpdate(node, None) - else: + else: builder.FOR_setUpdate(node, Expression(tokenizer, staticContext)) - + tokenizer.mustMatch("right_paren") builder.FOR_setBody(node, nest(tokenizer, staticContext, node, Statement)) - + if forBlock: builder.BLOCK_finish(forBlock) staticContext.statementStack.pop() - + builder.FOR_finish(node) return node - - + + elif tokenType == "while": node = builder.WHILE_build(tokenizer) - + builder.WHILE_setCondition(node, ParenExpression(tokenizer, staticContext)) builder.WHILE_setBody(node, nest(tokenizer, staticContext, node, Statement)) builder.WHILE_finish(node) - - return node - - + + return node + + elif tokenType == "do": node = builder.DO_build(tokenizer) - + builder.DO_setBody(node, nest(tokenizer, staticContext, node, Statement, "while")) builder.DO_setCondition(node, ParenExpression(tokenizer, staticContext)) builder.DO_finish(node) - + if not staticContext.ecma3OnlyMode: # <script language="JavaScript"> (without version hints) may need # automatic semicolon insertion without a newline after do-while. @@ -364,17 +363,17 @@ return node # NO RETURN - - + + elif tokenType == "break" or tokenType == "continue": if tokenType == "break": - node = builder.BREAK_build(tokenizer) + node = builder.BREAK_build(tokenizer) else: node = builder.CONTINUE_build(tokenizer) if tokenizer.peekOnSameLine() == "identifier": tokenizer.get() - + if tokenType == "break": builder.BREAK_setLabel(node, tokenizer.token.value) else: @@ -392,21 +391,21 @@ if getattr(statementStack[i], "label", None) == label: break - # + # # Both break and continue to label need to be handled specially # within a labeled loop, so that they target that loop. If not in # a loop, then break targets its labeled statement. Labels can be # nested so we skip all labels immediately enclosing the nearest # non-label statement. - # + # while i < len(statementStack) - 1 and statementStack[i+1].type == "label": i += 1 - + if i < len(statementStack) - 1 and getattr(statementStack[i+1], "isLoop", False): i += 1 elif tokenType == "continue": raise SyntaxError("Invalid continue", tokenizer) - + else: while True: i -= 1 @@ -418,84 +417,84 @@ if getattr(statementStack[i], "isLoop", False) or (tokenType == "break" and statementStack[i].type == "switch"): break - + if tokenType == "break": builder.BREAK_finish(node) else: builder.CONTINUE_finish(node) - + # NO RETURN elif tokenType == "try": node = builder.TRY_build(tokenizer) builder.TRY_setTryBlock(node, Block(tokenizer, staticContext)) - + while tokenizer.match("catch"): childNode = builder.CATCH_build(tokenizer) tokenizer.mustMatch("left_paren") nextTokenType = tokenizer.get() - + if nextTokenType == "left_bracket" or nextTokenType == "left_curly": # Destructured catch identifiers. tokenizer.unget() exception = DestructuringExpression(tokenizer, staticContext, True) - + elif nextTokenType == "identifier": exception = builder.CATCH_wrapException(tokenizer) - + else: raise SyntaxError("Missing identifier in catch", tokenizer) - + builder.CATCH_setException(childNode, exception) - + if tokenizer.match("if"): if staticContext.ecma3OnlyMode: raise SyntaxError("Illegal catch guard", tokenizer) - + if node.getChildrenLength() > 0 and not node.getUnrelatedChildren()[0].guard: raise SyntaxError("Guarded catch after unguarded", tokenizer) - + builder.CATCH_setGuard(childNode, Expression(tokenizer, staticContext)) - + else: builder.CATCH_setGuard(childNode, None) - + tokenizer.mustMatch("right_paren") - + builder.CATCH_setBlock(childNode, Block(tokenizer, staticContext)) builder.CATCH_finish(childNode) - + builder.TRY_addCatch(node, childNode) - + builder.TRY_finishCatches(node) - + if tokenizer.match("finally"): builder.TRY_setFinallyBlock(node, Block(tokenizer, staticContext)) - + if node.getChildrenLength() == 0 and not hasattr(node, "finallyBlock"): raise SyntaxError("Invalid try statement", tokenizer) - + builder.TRY_finish(node) return node - + elif tokenType == "catch" or tokenType == "finally": - raise SyntaxError(tokenizer.tokens[tokenType] + " without preceding try", tokenizer) + raise SyntaxError(tokens[tokenType] + " without preceding try", tokenizer) elif tokenType == "throw": node = builder.THROW_build(tokenizer) - + builder.THROW_setException(node, Expression(tokenizer, staticContext)) builder.THROW_finish(node) - + # NO RETURN elif tokenType == "return": node = returnOrYield(tokenizer, staticContext) - + # NO RETURN @@ -511,31 +510,31 @@ elif tokenType == "var" or tokenType == "const": node = Variables(tokenizer, staticContext) - + # NO RETURN - + elif tokenType == "let": if tokenizer.peek() == "left_paren": node = LetBlock(tokenizer, staticContext, True) else: node = Variables(tokenizer, staticContext) - + # NO RETURN - + elif tokenType == "debugger": node = builder.DEBUGGER_build(tokenizer) - + # NO RETURN - + elif tokenType == "newline" or tokenType == "semicolon": node = builder.SEMICOLON_build(tokenizer) builder.SEMICOLON_setExpression(node, None) builder.SEMICOLON_finish(tokenizer) - + return node @@ -547,21 +546,21 @@ if tokenType == "colon": label = tokenizer.token.value statementStack = staticContext.statementStack - + i = len(statementStack)-1 while i >= 0: if getattr(statementStack[i], "label", None) == label: raise SyntaxError("Duplicate label", tokenizer) - + i -= 1 - + tokenizer.get() node = builder.LABEL_build(tokenizer) - + builder.LABEL_setLabel(node, label) builder.LABEL_setStatement(node, nest(tokenizer, staticContext, node, Statement)) builder.LABEL_finish(node) - + return node # Expression statement. @@ -571,9 +570,9 @@ builder.SEMICOLON_setExpression(node, Expression(tokenizer, staticContext)) node.end = node.expression.end builder.SEMICOLON_finish(node) - + # NO RETURN - + MagicalSemicolon(tokenizer) return node @@ -583,13 +582,13 @@ def MagicalSemicolon(tokenizer): if tokenizer.line == tokenizer.token.line: tokenType = tokenizer.peekOnSameLine() - + if tokenType != "end" and tokenType != "newline" and tokenType != "semicolon" and tokenType != "right_curly": raise SyntaxError("Missing ; before statement", tokenizer) - + tokenizer.match("semicolon") - + def returnOrYield(tokenizer, staticContext): builder = staticContext.builder @@ -598,13 +597,13 @@ if tokenType == "return": if not staticContext.inFunction: raise SyntaxError("Return not in function", tokenizer) - + node = builder.RETURN_build(tokenizer) - + else: if not staticContext.inFunction: raise SyntaxError("Yield not in function", tokenizer) - + staticContext.isGenerator = True node = builder.YIELD_build(tokenizer) @@ -615,7 +614,7 @@ staticContext.hasReturnWithValue = True else: builder.YIELD_setValue(node, AssignExpression(tokenizer, staticContext)) - + elif tokenType == "return": staticContext.hasEmptyReturn = True @@ -635,14 +634,14 @@ def FunctionDefinition(tokenizer, staticContext, requireName, functionForm): builder = staticContext.builder functionNode = builder.FUNCTION_build(tokenizer) - + if tokenizer.match("identifier"): builder.FUNCTION_setName(functionNode, tokenizer.token.value) elif requireName: raise SyntaxError("Missing def identifier", tokenizer) tokenizer.mustMatch("left_paren") - + if not tokenizer.match("right_paren"): builder.FUNCTION_initParams(functionNode, tokenizer) prevParamNode = None @@ -652,21 +651,21 @@ # Destructured formal parameters. tokenizer.unget() paramNode = DestructuringExpression(tokenizer, staticContext) - + elif tokenType == "identifier": paramNode = builder.FUNCTION_wrapParam(tokenizer) - + else: raise SyntaxError("Missing formal parameter", tokenizer) - + builder.FUNCTION_addParam(functionNode, tokenizer, paramNode) builder.COMMENTS_add(paramNode, prevParamNode, tokenizer.getComments()) - + if not tokenizer.match("comma"): break - + prevParamNode = paramNode - + tokenizer.mustMatch("right_paren") # Do we have an expression closure or a normal body? @@ -676,8 +675,7 @@ tokenizer.unget() childContext = StaticContext(True, builder) - tokenizer.save() - + if staticContext.inFunction: # Inner functions don't reset block numbering, only functions at # the top level of the program do. @@ -687,128 +685,49 @@ builder.FUNCTION_setBody(functionNode, AssignExpression(tokenizer, staticContext)) if staticContext.isGenerator: raise SyntaxError("Generator returns a value", tokenizer) - + else: builder.FUNCTION_hoistVars(childContext.blockId) builder.FUNCTION_setBody(functionNode, Script(tokenizer, childContext)) - # - # Hoisting makes parse-time binding analysis tricky. A taxonomy of hoists: - # - # 1. vars hoist to the top of their function: - # - # var x = 'global'; - # function f() { - # x = 'f'; - # if (false) - # var x; - # } - # f(); - # print(x); // "global" - # - # 2. lets hoist to the top of their block: - # - # function f() { // id: 0 - # var x = 'f'; - # { - # { - # print(x); // "undefined" - # } - # let x; - # } - # } - # f(); - # - # 3. inner functions at function top-level hoist to the beginning - # of the function. - # - # If the builder used is doing parse-time analyses, hoisting may - # invalidate earlier conclusions it makes about variable scope. - # - # The builder can opt to set the needsHoisting flag in a - # CompilerContext (in the case of var and function hoisting) or in a - # node of type BLOCK (in the case of let hoisting). This signals for - # the parser to reparse sections of code. - # - # To avoid exponential blowup, if a function at the program top-level - # has any hoists in its child blocks or inner functions, we reparse - # the entire toplevel function. Each toplevel function is parsed at - # most twice. - # - # The list of declarations can be tied to block ids to aid talking - # about declarations of blocks that have not yet been fully parsed. - # - # Blocks are already uniquely numbered; see the comment in - # Statements. - # - - # - # wpbasti: - # Don't have the feeling that I need this functionality because the - # tree is often modified before the variables and names inside are - # of any interest. So better doing this in a post-scan. - # - - # - # if childContext.needsHoisting: - # # Order is important here! Builders expect functions to come after variables! - # builder.setHoists(functionNode.body.id, childContext.variables.concat(childContext.functions)) - # - # if staticContext.inFunction: - # # If an inner function needs hoisting, we need to propagate - # # this flag up to the parent function. - # staticContext.needsHoisting = True - # - # else: - # # Only re-parse functions at the top level of the program. - # childContext = StaticContext(True, builder) - # tokenizer.rewind(rp) - # - # # Set a flag in case the builder wants to have different behavior - # # on the second pass. - # builder.secondPass = True - # builder.FUNCTION_hoistVars(functionNode.body.id, True) - # builder.FUNCTION_setBody(functionNode, Script(tokenizer, childContext)) - # builder.secondPass = False - if tokenType == "left_curly": tokenizer.mustMatch("right_curly") functionNode.end = tokenizer.token.end functionNode.functionForm = functionForm - + builder.COMMENTS_add(functionNode.body, functionNode.body, tokenizer.getComments()) builder.FUNCTION_finish(functionNode, staticContext) - + return functionNode def Variables(tokenizer, staticContext, letBlock=None): """Parses a comma-separated list of var declarations (and maybe initializations).""" - + builder = staticContext.builder if tokenizer.token.type == "var": build = builder.VAR_build addDecl = builder.VAR_addDecl finish = builder.VAR_finish childContext = staticContext - + elif tokenizer.token.type == "const": build = builder.CONST_build addDecl = builder.CONST_addDecl finish = builder.CONST_finish childContext = staticContext - + elif tokenizer.token.type == "let" or tokenizer.token.type == "left_paren": build = builder.LET_build addDecl = builder.LET_addDecl finish = builder.LET_finish - + if not letBlock: statementStack = staticContext.statementStack i = len(statementStack) - 1 - + # a BLOCK *must* be found. while statementStack[i].type != "block": i -= 1 @@ -822,12 +741,12 @@ else: childContext = statementStack[i] - + else: childContext = letBlock node = build(tokenizer) - + while True: tokenType = tokenizer.get() @@ -836,20 +755,20 @@ # IDENTIFIER to mean both identifier declarations and destructured # declarations. childNode = builder.DECL_build(tokenizer) - + if tokenType == "left_bracket" or tokenType == "left_curly": # Pass in childContext if we need to add each pattern matched into # its variables, else pass in staticContext. # Need to unget to parse the full destructured expression. tokenizer.unget() builder.DECL_setNames(childNode, DestructuringExpression(tokenizer, staticContext, True, childContext)) - + if staticContext.inForLoopInit and tokenizer.peek() == "in": addDecl(node, childNode, childContext) - if tokenizer.match("comma"): + if tokenizer.match("comma"): continue - else: - break + else: + break tokenizer.mustMatch("assign") if tokenizer.token.assignOp: @@ -859,15 +778,15 @@ builder.DECL_setInitializer(childNode, AssignExpression(tokenizer, staticContext)) builder.DECL_finish(childNode) addDecl(node, childNode, childContext) - + # Copy over names for variable list # for nameNode in childNode.names: # childContext.variables.add(nameNode.value) - - if tokenizer.match("comma"): + + if tokenizer.match("comma"): continue - else: - break + else: + break if tokenType != "identifier": raise SyntaxError("Missing variable name", tokenizer) @@ -884,16 +803,16 @@ builder.DECL_setInitializer(childNode, initializerNode) builder.DECL_finish(childNode) - + # If we directly use the node in "let" constructs # if not hasattr(childContext, "variables"): # childContext.variables = set() - + # childContext.variables.add(childNode.name) - + if not tokenizer.match("comma"): break - + finish(node) return node @@ -921,7 +840,7 @@ if isStatement: childNode = Block(tokenizer, staticContext) builder.LETBLOCK_setBlock(node, childNode) - + else: childNode = AssignExpression(tokenizer, staticContext) builder.LETBLOCK_setExpression(node, childNode) @@ -933,7 +852,7 @@ def checkDestructuring(tokenizer, staticContext, node, simpleNamesOnly=None, data=None): if node.type == "array_comp": raise SyntaxError("Invalid array comprehension left-hand side", tokenizer) - + if node.type != "array_init" and node.type != "object_init": return @@ -942,23 +861,23 @@ for child in node: if child == None: continue - + if child.type == "property_init": lhs = child[0] rhs = child[1] else: lhs = None rhs = None - - + + if rhs and (rhs.type == "array_init" or rhs.type == "object_init"): checkDestructuring(tokenizer, staticContext, rhs, simpleNamesOnly, data) - + if lhs and simpleNamesOnly: # In declarations, lhs must be simple names if lhs.type != "identifier": raise SyntaxError("Missing name in pattern", tokenizer) - + elif data: childNode = builder.DECL_build(tokenizer) builder.DECL_setName(childNode, lhs.value) @@ -969,7 +888,7 @@ # Each pattern needs to be added to variables. # data.variables.add(childNode.name) - + # JavaScript 1.7 def DestructuringExpression(tokenizer, staticContext, simpleNamesOnly=None, data=None): @@ -987,20 +906,20 @@ builder.GENERATOR_setExpression(node, expression) builder.GENERATOR_setTail(node, comprehensionTail(tokenizer, staticContext)) builder.GENERATOR_finish(node) - + return node # JavaScript 1.7 Comprehensions Tails (Generators / Arrays) def comprehensionTail(tokenizer, staticContext): builder = staticContext.builder - + # tokenizer.token.type must be "for" body = builder.COMPTAIL_build(tokenizer) - + while True: node = builder.FOR_build(tokenizer) - + # Comprehension tails are always for..in loops. builder.FOR_rebuildForIn(node) if tokenizer.match("identifier"): @@ -1011,7 +930,7 @@ tokenizer.unget() tokenizer.mustMatch("left_paren") - + tokenType = tokenizer.get() if tokenType == "left_bracket" or tokenType == "left_curly": tokenizer.unget() @@ -1021,7 +940,7 @@ elif tokenType == "identifier": # Removed variable/declaration substructure in Python port. # Variable declarations are not allowed here. So why process them in such a way? - + # declaration = builder.DECL_build(tokenizer) # builder.DECL_setName(declaration, tokenizer.token.value) # builder.DECL_finish(declaration) @@ -1032,18 +951,18 @@ # Don't add to variables since the semantics of comprehensions is # such that the variables are in their own def when desugared. - + identifier = builder.PRIMARY_build(tokenizer, "identifier") builder.FOR_setIterator(node, identifier) else: raise SyntaxError("Missing identifier", tokenizer) - + tokenizer.mustMatch("in") builder.FOR_setObject(node, Expression(tokenizer, staticContext)) tokenizer.mustMatch("right_paren") builder.COMPTAIL_addFor(body, node) - + if not tokenizer.match("for"): break @@ -1071,10 +990,10 @@ if tokenizer.match("for"): if node.type == "yield" and not node.parenthesized: raise SyntaxError("Yield " + err, tokenizer) - + if node.type == "comma" and not node.parenthesized: raise SyntaxError("Generator " + err, tokenizer) - + node = GeneratorExpression(tokenizer, staticContext, node) tokenizer.mustMatch("right_paren") @@ -1096,10 +1015,10 @@ if childNode.type == "yield" and not childNode.parenthesized: raise SyntaxError("Yield expression must be parenthesized", tokenizer) builder.COMMA_addOperand(node, AssignExpression(tokenizer, staticContext)) - + if not tokenizer.match("comma"): break - + builder.COMMA_finish(node) return node @@ -1128,7 +1047,7 @@ pass else: raise SyntaxError("Bad left-hand side of assignment", tokenizer) - + builder.ASSIGN_setAssignOp(node, tokenizer.token.assignOp) builder.ASSIGN_addOperand(node, lhs) builder.ASSIGN_addOperand(node, AssignExpression(tokenizer, staticContext)) @@ -1153,20 +1072,20 @@ staticContext.inForLoopInit = False builder.HOOK_setThenPart(node, AssignExpression(tokenizer, staticContext)) staticContext.inForLoopInit = oldLoopInit - + if not tokenizer.match("colon"): raise SyntaxError("Missing : after ?", tokenizer) - + builder.HOOK_setElsePart(node, AssignExpression(tokenizer, staticContext)) builder.HOOK_finish(node) return node - + def OrExpression(tokenizer, staticContext): builder = staticContext.builder node = AndExpression(tokenizer, staticContext) - + while tokenizer.match("or"): childNode = builder.OR_build(tokenizer) builder.OR_addOperand(childNode, node) @@ -1194,7 +1113,7 @@ def BitwiseOrExpression(tokenizer, staticContext): builder = staticContext.builder node = BitwiseXorExpression(tokenizer, staticContext) - + while tokenizer.match("bitwise_or"): childNode = builder.BITWISEOR_build(tokenizer) builder.BITWISEOR_addOperand(childNode, node) @@ -1208,7 +1127,7 @@ def BitwiseXorExpression(tokenizer, staticContext): builder = staticContext.builder node = BitwiseAndExpression(tokenizer, staticContext) - + while tokenizer.match("bitwise_xor"): childNode = builder.BITWISEXOR_build(tokenizer) builder.BITWISEXOR_addOperand(childNode, node) @@ -1236,7 +1155,7 @@ def EqualityExpression(tokenizer, staticContext): builder = staticContext.builder node = RelationalExpression(tokenizer, staticContext) - + while tokenizer.match("eq") or tokenizer.match("ne") or tokenizer.match("strict_eq") or tokenizer.match("strict_ne"): childNode = builder.EQUALITY_build(tokenizer) builder.EQUALITY_addOperand(childNode, node) @@ -1262,7 +1181,7 @@ builder.RELATIONAL_addOperand(childNode, ShiftExpression(tokenizer, staticContext)) builder.RELATIONAL_finish(childNode) node = childNode - + staticContext.inForLoopInit = oldLoopInit return node @@ -1271,7 +1190,7 @@ def ShiftExpression(tokenizer, staticContext): builder = staticContext.builder node = AddExpression(tokenizer, staticContext) - + while tokenizer.match("lsh") or tokenizer.match("rsh") or tokenizer.match("ursh"): childNode = builder.SHIFT_build(tokenizer) builder.SHIFT_addOperand(childNode, node) @@ -1285,7 +1204,7 @@ def AddExpression(tokenizer, staticContext): builder = staticContext.builder node = MultiplyExpression(tokenizer, staticContext) - + while tokenizer.match("plus") or tokenizer.match("minus"): childNode = builder.ADD_build(tokenizer) builder.ADD_addOperand(childNode, node) @@ -1299,7 +1218,7 @@ def MultiplyExpression(tokenizer, staticContext): builder = staticContext.builder node = UnaryExpression(tokenizer, staticContext) - + while tokenizer.match("mul") or tokenizer.match("div") or tokenizer.match("mod"): childNode = builder.MULTIPLY_build(tokenizer) builder.MULTIPLY_addOperand(childNode, node) @@ -1317,7 +1236,7 @@ if tokenType in ["delete", "void", "typeof", "not", "bitwise_not", "plus", "minus"]: node = builder.UNARY_build(tokenizer) builder.UNARY_addOperand(node, UnaryExpression(tokenizer, staticContext)) - + elif tokenType == "increment" or tokenType == "decrement": # Prefix increment/decrement. node = builder.UNARY_build(tokenizer) @@ -1346,13 +1265,13 @@ if tokenizer.match("new"): node = builder.MEMBER_build(tokenizer) builder.MEMBER_addOperand(node, MemberExpression(tokenizer, staticContext, False)) - + if tokenizer.match("left_paren"): builder.MEMBER_rebuildNewWithArgs(node) builder.MEMBER_addOperand(node, ArgumentList(tokenizer, staticContext)) - + builder.MEMBER_finish(node) - + else: node = PrimaryExpression(tokenizer, staticContext) @@ -1360,7 +1279,7 @@ tokenType = tokenizer.get() if tokenType == "end": break - + if tokenType == "dot": childNode = builder.MEMBER_build(tokenizer) builder.MEMBER_addOperand(childNode, node) @@ -1391,20 +1310,20 @@ def ArgumentList(tokenizer, staticContext): builder = staticContext.builder node = builder.LIST_build(tokenizer) - + if tokenizer.match("right_paren", True): return node - - while True: + + while True: childNode = AssignExpression(tokenizer, staticContext) if childNode.type == "yield" and not childNode.parenthesized and tokenizer.peek() == "comma": raise SyntaxError("Yield expression must be parenthesized", tokenizer) - + if tokenizer.match("for"): childNode = GeneratorExpression(tokenizer, staticContext, childNode) if len(node) > 1 or tokenizer.peek(True) == "comma": raise SyntaxError("Generator expression must be parenthesized", tokenizer) - + builder.LIST_addOperand(node, childNode) if not tokenizer.match("comma"): break @@ -1428,7 +1347,7 @@ tokenType = tokenizer.peek(True) if tokenType == "right_bracket": break - + if tokenType == "comma": tokenizer.get() builder.ARRAYINIT_addElement(node, None) @@ -1446,7 +1365,7 @@ builder.ARRAYCOMP_setExpression(childNode, node[0]) builder.ARRAYCOMP_setTail(childNode, comprehensionTail(tokenizer, staticContext)) node = childNode - + builder.COMMENTS_add(node, node, tokenizer.getComments()) tokenizer.mustMatch("right_bracket") builder.PRIMARY_finish(node) @@ -1459,26 +1378,26 @@ tokenType = tokenizer.get() tokenValue = getattr(tokenizer.token, "value", None) comments = tokenizer.getComments() - + if tokenValue in ("get", "set") and tokenizer.peek() == "identifier": if staticContext.ecma3OnlyMode: raise SyntaxError("Illegal property accessor", tokenizer) - + fd = FunctionDefinition(tokenizer, staticContext, True, "expressed_form") builder.OBJECTINIT_addProperty(node, fd) - + else: if tokenType == "identifier" or tokenType == "number" or tokenType == "string": id = builder.PRIMARY_build(tokenizer, "identifier") builder.PRIMARY_finish(id) - + elif tokenType == "right_curly": if staticContext.ecma3OnlyMode: raise SyntaxError("Illegal trailing ,", tokenizer) - + tokenizer.unget() break - + else: if tokenValue in jasy.js.tokenize.Lang.keywords: id = builder.PRIMARY_build(tokenizer, "identifier") @@ -1486,7 +1405,7 @@ else: print("Value is '%s'" % tokenValue) raise SyntaxError("Invalid property name", tokenizer) - + if tokenizer.match("colon"): childNode = builder.PROPERTYINIT_build(tokenizer) builder.COMMENTS_add(childNode, node, comments) @@ -1494,14 +1413,14 @@ builder.PROPERTYINIT_addOperand(childNode, AssignExpression(tokenizer, staticContext)) builder.PROPERTYINIT_finish(childNode) builder.OBJECTINIT_addProperty(node, childNode) - + else: # Support, e.g., |var {staticContext, y} = o| as destructuring shorthand # for |var {staticContext: staticContext, y: y} = o|, per proposed JS2/ES4 for JS1.8. if tokenizer.peek() != "comma" and tokenizer.peek() != "right_curly": raise SyntaxError("Missing : after property", tokenizer) builder.OBJECTINIT_addProperty(node, id) - + if not tokenizer.match("comma"): break