# -*- coding: utf-8 -*- # Copyright JS Foundation and other contributors, https://js.foundation/ # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from __future__ import absolute_import, unicode_literals from .objects import Object from .compat import basestring, unicode from .utils import format from .error_handler import ErrorHandler from .messages import Messages from .scanner import RawToken, Scanner, SourceLocation, Position, RegExp from .token import Token, TokenName from .syntax import Syntax from . import nodes as Node class Value(object): def __init__(self, value): self.value = value class Params(object): def __init__(self, simple=None, message=None, stricted=None, firstRestricted=None, inFor=None, paramSet=None, params=None, get=None): self.simple = simple self.message = message self.stricted = stricted self.firstRestricted = firstRestricted self.inFor = inFor self.paramSet = paramSet self.params = params self.get = get class Config(Object): def __init__(self, range=False, loc=False, source=None, tokens=False, comment=False, tolerant=False, **options): self.range = range self.loc = loc self.source = source self.tokens = tokens self.comment = comment self.tolerant = tolerant for k, v in options.items(): setattr(self, k, v) class Context(object): def __init__(self, isModule=False, allowAwait=False, allowIn=True, allowStrictDirective=True, allowYield=True, firstCoverInitializedNameError=None, isAssignmentTarget=False, isBindingElement=False, inFunctionBody=False, inIteration=False, inSwitch=False, labelSet=None, strict=False): self.isModule = isModule self.allowAwait = allowAwait self.allowIn = allowIn self.allowStrictDirective = allowStrictDirective self.allowYield = allowYield self.firstCoverInitializedNameError = firstCoverInitializedNameError self.isAssignmentTarget = isAssignmentTarget self.isBindingElement = isBindingElement self.inFunctionBody = inFunctionBody self.inIteration = inIteration self.inSwitch = inSwitch self.labelSet = {} if labelSet is None else labelSet self.strict = strict class Marker(object): def __init__(self, index=None, line=None, column=None): self.index = index self.line = line self.column = column class TokenEntry(Object): def __init__(self, type=None, value=None, regex=None, range=None, loc=None): self.type = type self.value = value self.regex = regex self.range = range self.loc = loc class Parser(object): def __init__(self, code, options={}, delegate=None): self.config = Config(**options) self.delegate = delegate self.errorHandler = ErrorHandler() self.errorHandler.tolerant = self.config.tolerant self.scanner = Scanner(code, self.errorHandler) self.scanner.trackComment = self.config.comment self.operatorPrecedence = { '||': 1, '&&': 2, '|': 3, '^': 4, '&': 5, '==': 6, '!=': 6, '===': 6, '!==': 6, '<': 7, '>': 7, '<=': 7, '>=': 7, 'instanceof': 7, 'in': 7, '<<': 8, '>>': 8, '>>>': 8, '+': 9, '-': 9, '*': 11, '/': 11, '%': 11, } self.lookahead = RawToken( type=Token.EOF, value='', lineNumber=self.scanner.lineNumber, lineStart=0, start=0, end=0 ) self.hasLineTerminator = False self.context = Context( isModule=False, allowAwait=False, allowIn=True, allowStrictDirective=True, allowYield=True, firstCoverInitializedNameError=None, isAssignmentTarget=False, isBindingElement=False, inFunctionBody=False, inIteration=False, inSwitch=False, labelSet={}, strict=False ) self.tokens = [] self.startMarker = Marker( index=0, line=self.scanner.lineNumber, column=0 ) self.lastMarker = Marker( index=0, line=self.scanner.lineNumber, column=0 ) self.nextToken() self.lastMarker = Marker( index=self.scanner.index, line=self.scanner.lineNumber, column=self.scanner.index - self.scanner.lineStart ) def throwError(self, messageFormat, *args): msg = format(messageFormat, *args) index = self.lastMarker.index line = self.lastMarker.line column = self.lastMarker.column + 1 raise self.errorHandler.createError(index, line, column, msg) def tolerateError(self, messageFormat, *args): msg = format(messageFormat, *args) index = self.lastMarker.index line = self.scanner.lineNumber column = self.lastMarker.column + 1 self.errorHandler.tolerateError(index, line, column, msg) # Throw an exception because of the token. def unexpectedTokenError(self, token=None, message=None): msg = message or Messages.UnexpectedToken if token: if not message: typ = token.type if typ is Token.EOF: msg = Messages.UnexpectedEOS elif typ is Token.Identifier: msg = Messages.UnexpectedIdentifier elif typ is Token.NumericLiteral: msg = Messages.UnexpectedNumber elif typ is Token.StringLiteral: msg = Messages.UnexpectedString elif typ is Token.Template: msg = Messages.UnexpectedTemplate elif typ is Token.Keyword: if self.scanner.isFutureReservedWord(token.value): msg = Messages.UnexpectedReserved elif self.context.strict and self.scanner.isStrictModeReservedWord(token.value): msg = Messages.StrictReservedWord else: msg = Messages.UnexpectedToken value = token.value else: value = 'ILLEGAL' msg = msg.replace('%0', unicode(value), 1) if token and isinstance(token.lineNumber, int): index = token.start line = token.lineNumber lastMarkerLineStart = self.lastMarker.index - self.lastMarker.column column = token.start - lastMarkerLineStart + 1 return self.errorHandler.createError(index, line, column, msg) else: index = self.lastMarker.index line = self.lastMarker.line column = self.lastMarker.column + 1 return self.errorHandler.createError(index, line, column, msg) def throwUnexpectedToken(self, token=None, message=None): raise self.unexpectedTokenError(token, message) def tolerateUnexpectedToken(self, token=None, message=None): self.errorHandler.tolerate(self.unexpectedTokenError(token, message)) def collectComments(self): if not self.config.comment: self.scanner.scanComments() else: comments = self.scanner.scanComments() if comments: for e in comments: if e.multiLine: node = Node.BlockComment(self.scanner.source[e.slice[0]:e.slice[1]]) else: node = Node.LineComment(self.scanner.source[e.slice[0]:e.slice[1]]) if self.config.range: node.range = e.range if self.config.loc: node.loc = e.loc if self.delegate: metadata = SourceLocation( start=Position( line=e.loc.start.line, column=e.loc.start.column, offset=e.range[0], ), end=Position( line=e.loc.end.line, column=e.loc.end.column, offset=e.range[1], ) ) new_node = self.delegate(node, metadata) if new_node is not None: node = new_node # From internal representation to an external structure def getTokenRaw(self, token): return self.scanner.source[token.start:token.end] def convertToken(self, token): t = TokenEntry( type=TokenName[token.type], value=self.getTokenRaw(token), ) if self.config.range: t.range = [token.start, token.end] if self.config.loc: t.loc = SourceLocation( start=Position( line=self.startMarker.line, column=self.startMarker.column, ), end=Position( line=self.scanner.lineNumber, column=self.scanner.index - self.scanner.lineStart, ), ) if token.type is Token.RegularExpression: t.regex = RegExp( pattern=token.pattern, flags=token.flags, ) return t def nextToken(self): token = self.lookahead self.lastMarker.index = self.scanner.index self.lastMarker.line = self.scanner.lineNumber self.lastMarker.column = self.scanner.index - self.scanner.lineStart self.collectComments() if self.scanner.index != self.startMarker.index: self.startMarker.index = self.scanner.index self.startMarker.line = self.scanner.lineNumber self.startMarker.column = self.scanner.index - self.scanner.lineStart next = self.scanner.lex() self.hasLineTerminator = token.lineNumber != next.lineNumber if next and self.context.strict and next.type is Token.Identifier: if self.scanner.isStrictModeReservedWord(next.value): next.type = Token.Keyword self.lookahead = next if self.config.tokens and next.type is not Token.EOF: self.tokens.append(self.convertToken(next)) return token def nextRegexToken(self): self.collectComments() token = self.scanner.scanRegExp() if self.config.tokens: # Pop the previous token, '/' or '/=' # self is added from the lookahead token. self.tokens.pop() self.tokens.append(self.convertToken(token)) # Prime the next lookahead. self.lookahead = token self.nextToken() return token def createNode(self): return Marker( index=self.startMarker.index, line=self.startMarker.line, column=self.startMarker.column, ) def startNode(self, token, lastLineStart=0): column = token.start - token.lineStart line = token.lineNumber if column < 0: column += lastLineStart line -= 1 return Marker( index=token.start, line=line, column=column, ) def finalize(self, marker, node): if self.config.range: node.range = [marker.index, self.lastMarker.index] if self.config.loc: node.loc = SourceLocation( start=Position( line=marker.line, column=marker.column, ), end=Position( line=self.lastMarker.line, column=self.lastMarker.column, ), ) if self.config.source: node.loc.source = self.config.source if self.delegate: metadata = SourceLocation( start=Position( line=marker.line, column=marker.column, offset=marker.index, ), end=Position( line=self.lastMarker.line, column=self.lastMarker.column, offset=self.lastMarker.index, ) ) new_node = self.delegate(node, metadata) if new_node is not None: node = new_node return node # Expect the next token to match the specified punctuator. # If not, an exception will be thrown. def expect(self, value): token = self.nextToken() if token.type is not Token.Punctuator or token.value != value: self.throwUnexpectedToken(token) # Quietly expect a comma when in tolerant mode, otherwise delegates to expect(). def expectCommaSeparator(self): if self.config.tolerant: token = self.lookahead if token.type is Token.Punctuator and token.value == ',': self.nextToken() elif token.type is Token.Punctuator and token.value == ';': self.nextToken() self.tolerateUnexpectedToken(token) else: self.tolerateUnexpectedToken(token, Messages.UnexpectedToken) else: self.expect(',') # Expect the next token to match the specified keyword. # If not, an exception will be thrown. def expectKeyword(self, keyword): token = self.nextToken() if token.type is not Token.Keyword or token.value != keyword: self.throwUnexpectedToken(token) # Return true if the next token matches the specified punctuator. def match(self, *value): return self.lookahead.type is Token.Punctuator and self.lookahead.value in value # Return true if the next token matches the specified keyword def matchKeyword(self, *keyword): return self.lookahead.type is Token.Keyword and self.lookahead.value in keyword # Return true if the next token matches the specified contextual keyword # (where an identifier is sometimes a keyword depending on the context) def matchContextualKeyword(self, *keyword): return self.lookahead.type is Token.Identifier and self.lookahead.value in keyword # Return true if the next token is an assignment operator def matchAssign(self): if self.lookahead.type is not Token.Punctuator: return False op = self.lookahead.value return op in ('=', '*=', '**=', '/=', '%=', '+=', '-=', '<<=', '>>=', '>>>=', '&=', '^=', '|=') # Cover grammar support. # # When an assignment expression position starts with a left parenthesis, the determination of the type # of the syntax is to be deferred arbitrarily long until the end of the parentheses pair (plus a lookahead) # or the first comma. This situation also defers the determination of all the expressions nested in the pair. # # There are three productions that can be parsed in a parentheses pair that needs to be determined # after the outermost pair is closed. They are: # # 1. AssignmentExpression # 2. BindingElements # 3. AssignmentTargets # # In order to avoid exponential backtracking, we use two flags to denote if the production can be # binding element or assignment target. # # The three productions have the relationship: # # BindingElements ⊆ AssignmentTargets ⊆ AssignmentExpression # # with a single exception that CoverInitializedName when used directly in an Expression, generates # an early error. Therefore, we need the third state, firstCoverInitializedNameError, to track the # first usage of CoverInitializedName and report it when we reached the end of the parentheses pair. # # isolateCoverGrammar function runs the given parser function with a new cover grammar context, and it does not # effect the current flags. This means the production the parser parses is only used as an expression. Therefore # the CoverInitializedName check is conducted. # # inheritCoverGrammar function runs the given parse function with a new cover grammar context, and it propagates # the flags outside of the parser. This means the production the parser parses is used as a part of a potential # pattern. The CoverInitializedName check is deferred. def isolateCoverGrammar(self, parseFunction): previousIsBindingElement = self.context.isBindingElement previousIsAssignmentTarget = self.context.isAssignmentTarget previousFirstCoverInitializedNameError = self.context.firstCoverInitializedNameError self.context.isBindingElement = True self.context.isAssignmentTarget = True self.context.firstCoverInitializedNameError = None result = parseFunction() if self.context.firstCoverInitializedNameError is not None: self.throwUnexpectedToken(self.context.firstCoverInitializedNameError) self.context.isBindingElement = previousIsBindingElement self.context.isAssignmentTarget = previousIsAssignmentTarget self.context.firstCoverInitializedNameError = previousFirstCoverInitializedNameError return result def inheritCoverGrammar(self, parseFunction): previousIsBindingElement = self.context.isBindingElement previousIsAssignmentTarget = self.context.isAssignmentTarget previousFirstCoverInitializedNameError = self.context.firstCoverInitializedNameError self.context.isBindingElement = True self.context.isAssignmentTarget = True self.context.firstCoverInitializedNameError = None result = parseFunction() self.context.isBindingElement = self.context.isBindingElement and previousIsBindingElement self.context.isAssignmentTarget = self.context.isAssignmentTarget and previousIsAssignmentTarget self.context.firstCoverInitializedNameError = previousFirstCoverInitializedNameError or self.context.firstCoverInitializedNameError return result def consumeSemicolon(self): if self.match(';'): self.nextToken() elif not self.hasLineTerminator: if self.lookahead.type is not Token.EOF and not self.match('}'): self.throwUnexpectedToken(self.lookahead) self.lastMarker.index = self.startMarker.index self.lastMarker.line = self.startMarker.line self.lastMarker.column = self.startMarker.column # https://tc39.github.io/ecma262/#sec-primary-expression def parsePrimaryExpression(self): node = self.createNode() typ = self.lookahead.type if typ is Token.Identifier: if (self.context.isModule or self.context.allowAwait) and self.lookahead.value == 'await': self.tolerateUnexpectedToken(self.lookahead) expr = self.parseFunctionExpression() if self.matchAsyncFunction() else self.finalize(node, Node.Identifier(self.nextToken().value)) elif typ in ( Token.NumericLiteral, Token.StringLiteral, ): if self.context.strict and self.lookahead.octal: self.tolerateUnexpectedToken(self.lookahead, Messages.StrictOctalLiteral) self.context.isAssignmentTarget = False self.context.isBindingElement = False token = self.nextToken() raw = self.getTokenRaw(token) expr = self.finalize(node, Node.Literal(token.value, raw)) elif typ is Token.BooleanLiteral: self.context.isAssignmentTarget = False self.context.isBindingElement = False token = self.nextToken() raw = self.getTokenRaw(token) expr = self.finalize(node, Node.Literal(token.value == 'true', raw)) elif typ is Token.NullLiteral: self.context.isAssignmentTarget = False self.context.isBindingElement = False token = self.nextToken() raw = self.getTokenRaw(token) expr = self.finalize(node, Node.Literal(None, raw)) elif typ is Token.Template: expr = self.parseTemplateLiteral() elif typ is Token.Punctuator: value = self.lookahead.value if value == '(': self.context.isBindingElement = False expr = self.inheritCoverGrammar(self.parseGroupExpression) elif value == '[': expr = self.inheritCoverGrammar(self.parseArrayInitializer) elif value == '{': expr = self.inheritCoverGrammar(self.parseObjectInitializer) elif value in ('/', '/='): self.context.isAssignmentTarget = False self.context.isBindingElement = False self.scanner.index = self.startMarker.index token = self.nextRegexToken() raw = self.getTokenRaw(token) expr = self.finalize(node, Node.RegexLiteral(token.regex, raw, token.pattern, token.flags)) else: expr = self.throwUnexpectedToken(self.nextToken()) elif typ is Token.Keyword: if not self.context.strict and self.context.allowYield and self.matchKeyword('yield'): expr = self.parseIdentifierName() elif not self.context.strict and self.matchKeyword('let'): expr = self.finalize(node, Node.Identifier(self.nextToken().value)) else: self.context.isAssignmentTarget = False self.context.isBindingElement = False if self.matchKeyword('function'): expr = self.parseFunctionExpression() elif self.matchKeyword('this'): self.nextToken() expr = self.finalize(node, Node.ThisExpression()) elif self.matchKeyword('class'): expr = self.parseClassExpression() elif self.matchImportCall(): expr = self.parseImportCall() else: expr = self.throwUnexpectedToken(self.nextToken()) else: expr = self.throwUnexpectedToken(self.nextToken()) return expr # https://tc39.github.io/ecma262/#sec-array-initializer def parseSpreadElement(self): node = self.createNode() self.expect('...') arg = self.inheritCoverGrammar(self.parseAssignmentExpression) return self.finalize(node, Node.SpreadElement(arg)) def parseArrayInitializer(self): node = self.createNode() elements = [] self.expect('[') while not self.match(']'): if self.match(','): self.nextToken() elements.append(None) elif self.match('...'): element = self.parseSpreadElement() if not self.match(']'): self.context.isAssignmentTarget = False self.context.isBindingElement = False self.expect(',') elements.append(element) else: elements.append(self.inheritCoverGrammar(self.parseAssignmentExpression)) if not self.match(']'): self.expect(',') self.expect(']') return self.finalize(node, Node.ArrayExpression(elements)) # https://tc39.github.io/ecma262/#sec-object-initializer def parsePropertyMethod(self, params): self.context.isAssignmentTarget = False self.context.isBindingElement = False previousStrict = self.context.strict previousAllowStrictDirective = self.context.allowStrictDirective self.context.allowStrictDirective = params.simple body = self.isolateCoverGrammar(self.parseFunctionSourceElements) if self.context.strict and params.firstRestricted: self.tolerateUnexpectedToken(params.firstRestricted, params.message) if self.context.strict and params.stricted: self.tolerateUnexpectedToken(params.stricted, params.message) self.context.strict = previousStrict self.context.allowStrictDirective = previousAllowStrictDirective return body def parsePropertyMethodFunction(self): isGenerator = False node = self.createNode() previousAllowYield = self.context.allowYield self.context.allowYield = True params = self.parseFormalParameters() method = self.parsePropertyMethod(params) self.context.allowYield = previousAllowYield return self.finalize(node, Node.FunctionExpression(None, params.params, method, isGenerator)) def parsePropertyMethodAsyncFunction(self): node = self.createNode() previousAllowYield = self.context.allowYield previousAwait = self.context.allowAwait self.context.allowYield = False self.context.allowAwait = True params = self.parseFormalParameters() method = self.parsePropertyMethod(params) self.context.allowYield = previousAllowYield self.context.allowAwait = previousAwait return self.finalize(node, Node.AsyncFunctionExpression(None, params.params, method)) def parseObjectPropertyKey(self): node = self.createNode() token = self.nextToken() typ = token.type if typ in ( Token.StringLiteral, Token.NumericLiteral, ): if self.context.strict and token.octal: self.tolerateUnexpectedToken(token, Messages.StrictOctalLiteral) raw = self.getTokenRaw(token) key = self.finalize(node, Node.Literal(token.value, raw)) elif typ in ( Token.Identifier, Token.BooleanLiteral, Token.NullLiteral, Token.Keyword, ): key = self.finalize(node, Node.Identifier(token.value)) elif typ is Token.Punctuator: if token.value == '[': key = self.isolateCoverGrammar(self.parseAssignmentExpression) self.expect(']') else: key = self.throwUnexpectedToken(token) else: key = self.throwUnexpectedToken(token) return key def isPropertyKey(self, key, value): return ( (key.type is Syntax.Identifier and key.name == value) or (key.type is Syntax.Literal and key.value == value) ) def parseObjectProperty(self, hasProto): node = self.createNode() token = self.lookahead key = None value = None computed = False method = False shorthand = False isAsync = False if token.type is Token.Identifier: id = token.value self.nextToken() computed = self.match('[') isAsync = not self.hasLineTerminator and (id == 'async') and not (self.match(':', '(', '*', ',')) key = self.parseObjectPropertyKey() if isAsync else self.finalize(node, Node.Identifier(id)) elif self.match('*'): self.nextToken() else: computed = self.match('[') key = self.parseObjectPropertyKey() lookaheadPropertyKey = self.qualifiedPropertyName(self.lookahead) if token.type is Token.Identifier and not isAsync and token.value == 'get' and lookaheadPropertyKey: kind = 'get' computed = self.match('[') key = self.parseObjectPropertyKey() self.context.allowYield = False value = self.parseGetterMethod() elif token.type is Token.Identifier and not isAsync and token.value == 'set' and lookaheadPropertyKey: kind = 'set' computed = self.match('[') key = self.parseObjectPropertyKey() value = self.parseSetterMethod() elif token.type is Token.Punctuator and token.value == '*' and lookaheadPropertyKey: kind = 'init' computed = self.match('[') key = self.parseObjectPropertyKey() value = self.parseGeneratorMethod() method = True else: if not key: self.throwUnexpectedToken(self.lookahead) kind = 'init' if self.match(':') and not isAsync: if not computed and self.isPropertyKey(key, '__proto__'): if hasProto.value: self.tolerateError(Messages.DuplicateProtoProperty) hasProto.value = True self.nextToken() value = self.inheritCoverGrammar(self.parseAssignmentExpression) elif self.match('('): value = self.parsePropertyMethodAsyncFunction() if isAsync else self.parsePropertyMethodFunction() method = True elif token.type is Token.Identifier: id = self.finalize(node, Node.Identifier(token.value)) if self.match('='): self.context.firstCoverInitializedNameError = self.lookahead self.nextToken() shorthand = True init = self.isolateCoverGrammar(self.parseAssignmentExpression) value = self.finalize(node, Node.AssignmentPattern(id, init)) else: shorthand = True value = id else: self.throwUnexpectedToken(self.nextToken()) return self.finalize(node, Node.Property(kind, key, computed, value, method, shorthand)) def parseObjectInitializer(self): node = self.createNode() self.expect('{') properties = [] hasProto = Value(False) while not self.match('}'): properties.append(self.parseSpreadElement() if self.match('...') else self.parseObjectProperty(hasProto)) if not self.match('}'): self.expectCommaSeparator() self.expect('}') return self.finalize(node, Node.ObjectExpression(properties)) # https://tc39.github.io/ecma262/#sec-template-literals def parseTemplateHead(self): assert self.lookahead.head, 'Template literal must start with a template head' node = self.createNode() token = self.nextToken() raw = token.value cooked = token.cooked return self.finalize(node, Node.TemplateElement(raw, cooked, token.tail)) def parseTemplateElement(self): if self.lookahead.type is not Token.Template: self.throwUnexpectedToken() node = self.createNode() token = self.nextToken() raw = token.value cooked = token.cooked return self.finalize(node, Node.TemplateElement(raw, cooked, token.tail)) def parseTemplateLiteral(self): node = self.createNode() expressions = [] quasis = [] quasi = self.parseTemplateHead() quasis.append(quasi) while not quasi.tail: expressions.append(self.parseExpression()) quasi = self.parseTemplateElement() quasis.append(quasi) return self.finalize(node, Node.TemplateLiteral(quasis, expressions)) # https://tc39.github.io/ecma262/#sec-grouping-operator def reinterpretExpressionAsPattern(self, expr): typ = expr.type if typ in ( Syntax.Identifier, Syntax.MemberExpression, Syntax.RestElement, Syntax.AssignmentPattern, ): pass elif typ is Syntax.SpreadElement: expr.type = Syntax.RestElement self.reinterpretExpressionAsPattern(expr.argument) elif typ is Syntax.ArrayExpression: expr.type = Syntax.ArrayPattern for elem in expr.elements: if elem is not None: self.reinterpretExpressionAsPattern(elem) elif typ is Syntax.ObjectExpression: expr.type = Syntax.ObjectPattern for prop in expr.properties: self.reinterpretExpressionAsPattern(prop if prop.type is Syntax.SpreadElement else prop.value) elif typ is Syntax.AssignmentExpression: expr.type = Syntax.AssignmentPattern del expr.operator self.reinterpretExpressionAsPattern(expr.left) else: # Allow other node type for tolerant parsing. pass def parseGroupExpression(self): self.expect('(') if self.match(')'): self.nextToken() if not self.match('=>'): self.expect('=>') expr = Node.ArrowParameterPlaceHolder([]) else: startToken = self.lookahead params = [] if self.match('...'): expr = self.parseRestElement(params) self.expect(')') if not self.match('=>'): self.expect('=>') expr = Node.ArrowParameterPlaceHolder([expr]) else: arrow = False self.context.isBindingElement = True expr = self.inheritCoverGrammar(self.parseAssignmentExpression) if self.match(','): expressions = [] self.context.isAssignmentTarget = False expressions.append(expr) while self.lookahead.type is not Token.EOF: if not self.match(','): break self.nextToken() if self.match(')'): self.nextToken() for expression in expressions: self.reinterpretExpressionAsPattern(expression) arrow = True expr = Node.ArrowParameterPlaceHolder(expressions) elif self.match('...'): if not self.context.isBindingElement: self.throwUnexpectedToken(self.lookahead) expressions.append(self.parseRestElement(params)) self.expect(')') if not self.match('=>'): self.expect('=>') self.context.isBindingElement = False for expression in expressions: self.reinterpretExpressionAsPattern(expression) arrow = True expr = Node.ArrowParameterPlaceHolder(expressions) else: expressions.append(self.inheritCoverGrammar(self.parseAssignmentExpression)) if arrow: break if not arrow: expr = self.finalize(self.startNode(startToken), Node.SequenceExpression(expressions)) if not arrow: self.expect(')') if self.match('=>'): if expr.type is Syntax.Identifier and expr.name == 'yield': arrow = True expr = Node.ArrowParameterPlaceHolder([expr]) if not arrow: if not self.context.isBindingElement: self.throwUnexpectedToken(self.lookahead) if expr.type is Syntax.SequenceExpression: for expression in expr.expressions: self.reinterpretExpressionAsPattern(expression) else: self.reinterpretExpressionAsPattern(expr) if expr.type is Syntax.SequenceExpression: parameters = expr.expressions else: parameters = [expr] expr = Node.ArrowParameterPlaceHolder(parameters) self.context.isBindingElement = False return expr # https://tc39.github.io/ecma262/#sec-left-hand-side-expressions def parseArguments(self): self.expect('(') args = [] if not self.match(')'): while True: if self.match('...'): expr = self.parseSpreadElement() else: expr = self.isolateCoverGrammar(self.parseAssignmentExpression) args.append(expr) if self.match(')'): break self.expectCommaSeparator() if self.match(')'): break self.expect(')') return args def isIdentifierName(self, token): return ( token.type is Token.Identifier or token.type is Token.Keyword or token.type is Token.BooleanLiteral or token.type is Token.NullLiteral ) def parseIdentifierName(self): node = self.createNode() token = self.nextToken() if not self.isIdentifierName(token): self.throwUnexpectedToken(token) return self.finalize(node, Node.Identifier(token.value)) def parseNewExpression(self): node = self.createNode() id = self.parseIdentifierName() assert id.name == 'new', 'New expression must start with `new`' if self.match('.'): self.nextToken() if self.lookahead.type is Token.Identifier and self.context.inFunctionBody and self.lookahead.value == 'target': property = self.parseIdentifierName() expr = Node.MetaProperty(id, property) else: self.throwUnexpectedToken(self.lookahead) elif self.matchKeyword('import'): self.throwUnexpectedToken(self.lookahead) else: callee = self.isolateCoverGrammar(self.parseLeftHandSideExpression) args = self.parseArguments() if self.match('(') else [] expr = Node.NewExpression(callee, args) self.context.isAssignmentTarget = False self.context.isBindingElement = False return self.finalize(node, expr) def parseAsyncArgument(self): arg = self.parseAssignmentExpression() self.context.firstCoverInitializedNameError = None return arg def parseAsyncArguments(self): self.expect('(') args = [] if not self.match(')'): while True: if self.match('...'): expr = self.parseSpreadElement() else: expr = self.isolateCoverGrammar(self.parseAsyncArgument) args.append(expr) if self.match(')'): break self.expectCommaSeparator() if self.match(')'): break self.expect(')') return args def matchImportCall(self): match = self.matchKeyword('import') if match: state = self.scanner.saveState() self.scanner.scanComments() next = self.scanner.lex() self.scanner.restoreState(state) match = (next.type is Token.Punctuator) and (next.value == '(') return match def parseImportCall(self): node = self.createNode() self.expectKeyword('import') return self.finalize(node, Node.Import()) def parseLeftHandSideExpressionAllowCall(self): startToken = self.lookahead maybeAsync = self.matchContextualKeyword('async') previousAllowIn = self.context.allowIn self.context.allowIn = True if self.matchKeyword('super') and self.context.inFunctionBody: expr = self.createNode() self.nextToken() expr = self.finalize(expr, Node.Super()) if not self.match('(') and not self.match('.') and not self.match('['): self.throwUnexpectedToken(self.lookahead) else: expr = self.inheritCoverGrammar(self.parseNewExpression if self.matchKeyword('new') else self.parsePrimaryExpression) while True: if self.match('.'): self.context.isBindingElement = False self.context.isAssignmentTarget = True self.expect('.') property = self.parseIdentifierName() expr = self.finalize(self.startNode(startToken), Node.StaticMemberExpression(expr, property)) elif self.match('('): asyncArrow = maybeAsync and (startToken.lineNumber == self.lookahead.lineNumber) self.context.isBindingElement = False self.context.isAssignmentTarget = False if asyncArrow: args = self.parseAsyncArguments() else: args = self.parseArguments() if expr.type is Syntax.Import and len(args) != 1: self.tolerateError(Messages.BadImportCallArity) expr = self.finalize(self.startNode(startToken), Node.CallExpression(expr, args)) if asyncArrow and self.match('=>'): for arg in args: self.reinterpretExpressionAsPattern(arg) expr = Node.AsyncArrowParameterPlaceHolder(args) elif self.match('['): self.context.isBindingElement = False self.context.isAssignmentTarget = True self.expect('[') property = self.isolateCoverGrammar(self.parseExpression) self.expect(']') expr = self.finalize(self.startNode(startToken), Node.ComputedMemberExpression(expr, property)) elif self.lookahead.type is Token.Template and self.lookahead.head: quasi = self.parseTemplateLiteral() expr = self.finalize(self.startNode(startToken), Node.TaggedTemplateExpression(expr, quasi)) else: break self.context.allowIn = previousAllowIn return expr def parseSuper(self): node = self.createNode() self.expectKeyword('super') if not self.match('[') and not self.match('.'): self.throwUnexpectedToken(self.lookahead) return self.finalize(node, Node.Super()) def parseLeftHandSideExpression(self): assert self.context.allowIn, 'callee of new expression always allow in keyword.' node = self.startNode(self.lookahead) if self.matchKeyword('super') and self.context.inFunctionBody: expr = self.parseSuper() else: expr = self.inheritCoverGrammar(self.parseNewExpression if self.matchKeyword('new') else self.parsePrimaryExpression) while True: if self.match('['): self.context.isBindingElement = False self.context.isAssignmentTarget = True self.expect('[') property = self.isolateCoverGrammar(self.parseExpression) self.expect(']') expr = self.finalize(node, Node.ComputedMemberExpression(expr, property)) elif self.match('.'): self.context.isBindingElement = False self.context.isAssignmentTarget = True self.expect('.') property = self.parseIdentifierName() expr = self.finalize(node, Node.StaticMemberExpression(expr, property)) elif self.lookahead.type is Token.Template and self.lookahead.head: quasi = self.parseTemplateLiteral() expr = self.finalize(node, Node.TaggedTemplateExpression(expr, quasi)) else: break return expr # https://tc39.github.io/ecma262/#sec-update-expressions def parseUpdateExpression(self): startToken = self.lookahead if self.match('++', '--'): node = self.startNode(startToken) token = self.nextToken() expr = self.inheritCoverGrammar(self.parseUnaryExpression) if self.context.strict and expr.type is Syntax.Identifier and self.scanner.isRestrictedWord(expr.name): self.tolerateError(Messages.StrictLHSPrefix) if not self.context.isAssignmentTarget: self.tolerateError(Messages.InvalidLHSInAssignment) prefix = True expr = self.finalize(node, Node.UpdateExpression(token.value, expr, prefix)) self.context.isAssignmentTarget = False self.context.isBindingElement = False else: expr = self.inheritCoverGrammar(self.parseLeftHandSideExpressionAllowCall) if not self.hasLineTerminator and self.lookahead.type is Token.Punctuator: if self.match('++', '--'): if self.context.strict and expr.type is Syntax.Identifier and self.scanner.isRestrictedWord(expr.name): self.tolerateError(Messages.StrictLHSPostfix) if not self.context.isAssignmentTarget: self.tolerateError(Messages.InvalidLHSInAssignment) self.context.isAssignmentTarget = False self.context.isBindingElement = False operator = self.nextToken().value prefix = False expr = self.finalize(self.startNode(startToken), Node.UpdateExpression(operator, expr, prefix)) return expr # https://tc39.github.io/ecma262/#sec-unary-operators def parseAwaitExpression(self): node = self.createNode() self.nextToken() argument = self.parseUnaryExpression() return self.finalize(node, Node.AwaitExpression(argument)) def parseUnaryExpression(self): if ( self.match('+', '-', '~', '!') or self.matchKeyword('delete', 'void', 'typeof') ): node = self.startNode(self.lookahead) token = self.nextToken() expr = self.inheritCoverGrammar(self.parseUnaryExpression) expr = self.finalize(node, Node.UnaryExpression(token.value, expr)) if self.context.strict and expr.operator == 'delete' and expr.argument.type is Syntax.Identifier: self.tolerateError(Messages.StrictDelete) self.context.isAssignmentTarget = False self.context.isBindingElement = False elif self.context.allowAwait and self.matchContextualKeyword('await'): expr = self.parseAwaitExpression() else: expr = self.parseUpdateExpression() return expr def parseExponentiationExpression(self): startToken = self.lookahead expr = self.inheritCoverGrammar(self.parseUnaryExpression) if expr.type is not Syntax.UnaryExpression and self.match('**'): self.nextToken() self.context.isAssignmentTarget = False self.context.isBindingElement = False left = expr right = self.isolateCoverGrammar(self.parseExponentiationExpression) expr = self.finalize(self.startNode(startToken), Node.BinaryExpression('**', left, right)) return expr # https://tc39.github.io/ecma262/#sec-exp-operator # https://tc39.github.io/ecma262/#sec-multiplicative-operators # https://tc39.github.io/ecma262/#sec-additive-operators # https://tc39.github.io/ecma262/#sec-bitwise-shift-operators # https://tc39.github.io/ecma262/#sec-relational-operators # https://tc39.github.io/ecma262/#sec-equality-operators # https://tc39.github.io/ecma262/#sec-binary-bitwise-operators # https://tc39.github.io/ecma262/#sec-binary-logical-operators def binaryPrecedence(self, token): op = token.value if token.type is Token.Punctuator: precedence = self.operatorPrecedence.get(op, 0) elif token.type is Token.Keyword: precedence = 7 if (op == 'instanceof' or (self.context.allowIn and op == 'in')) else 0 else: precedence = 0 return precedence def parseBinaryExpression(self): startToken = self.lookahead expr = self.inheritCoverGrammar(self.parseExponentiationExpression) token = self.lookahead prec = self.binaryPrecedence(token) if prec > 0: self.nextToken() self.context.isAssignmentTarget = False self.context.isBindingElement = False markers = [startToken, self.lookahead] left = expr right = self.isolateCoverGrammar(self.parseExponentiationExpression) stack = [left, token.value, right] precedences = [prec] while True: prec = self.binaryPrecedence(self.lookahead) if prec <= 0: break # Reduce: make a binary expression from the three topmost entries. while len(stack) > 2 and prec <= precedences[-1]: right = stack.pop() operator = stack.pop() precedences.pop() left = stack.pop() markers.pop() node = self.startNode(markers[-1]) stack.append(self.finalize(node, Node.BinaryExpression(operator, left, right))) # Shift. stack.append(self.nextToken().value) precedences.append(prec) markers.append(self.lookahead) stack.append(self.isolateCoverGrammar(self.parseExponentiationExpression)) # Final reduce to clean-up the stack. i = len(stack) - 1 expr = stack[i] lastMarker = markers.pop() while i > 1: marker = markers.pop() lastLineStart = lastMarker.lineStart if lastMarker else 0 node = self.startNode(marker, lastLineStart) operator = stack[i - 1] expr = self.finalize(node, Node.BinaryExpression(operator, stack[i - 2], expr)) i -= 2 lastMarker = marker return expr # https://tc39.github.io/ecma262/#sec-conditional-operator def parseConditionalExpression(self): startToken = self.lookahead expr = self.inheritCoverGrammar(self.parseBinaryExpression) if self.match('?'): self.nextToken() previousAllowIn = self.context.allowIn self.context.allowIn = True consequent = self.isolateCoverGrammar(self.parseAssignmentExpression) self.context.allowIn = previousAllowIn self.expect(':') alternate = self.isolateCoverGrammar(self.parseAssignmentExpression) expr = self.finalize(self.startNode(startToken), Node.ConditionalExpression(expr, consequent, alternate)) self.context.isAssignmentTarget = False self.context.isBindingElement = False return expr # https://tc39.github.io/ecma262/#sec-assignment-operators def checkPatternParam(self, options, param): typ = param.type if typ is Syntax.Identifier: self.validateParam(options, param, param.name) elif typ is Syntax.RestElement: self.checkPatternParam(options, param.argument) elif typ is Syntax.AssignmentPattern: self.checkPatternParam(options, param.left) elif typ is Syntax.ArrayPattern: for element in param.elements: if element is not None: self.checkPatternParam(options, element) elif typ is Syntax.ObjectPattern: for prop in param.properties: self.checkPatternParam(options, prop if prop.type is Syntax.RestElement else prop.value) options.simple = options.simple and isinstance(param, Node.Identifier) def reinterpretAsCoverFormalsList(self, expr): params = [expr] asyncArrow = False typ = expr.type if typ is Syntax.Identifier: pass elif typ is Syntax.ArrowParameterPlaceHolder: params = expr.params asyncArrow = expr.isAsync else: return None options = Params( simple=True, paramSet={}, ) for param in params: if param.type is Syntax.AssignmentPattern: if param.right.type is Syntax.YieldExpression: if param.right.argument: self.throwUnexpectedToken(self.lookahead) param.right.type = Syntax.Identifier param.right.name = 'yield' del param.right.argument del param.right.delegate elif asyncArrow and param.type is Syntax.Identifier and param.name == 'await': self.throwUnexpectedToken(self.lookahead) self.checkPatternParam(options, param) if self.context.strict or not self.context.allowYield: for param in params: if param.type is Syntax.YieldExpression: self.throwUnexpectedToken(self.lookahead) if options.message is Messages.StrictParamDupe: token = options.stricted if self.context.strict else options.firstRestricted self.throwUnexpectedToken(token, options.message) return Params( simple=options.simple, params=params, stricted=options.stricted, firstRestricted=options.firstRestricted, message=options.message ) def parseAssignmentExpression(self): if not self.context.allowYield and self.matchKeyword('yield'): expr = self.parseYieldExpression() else: startToken = self.lookahead token = startToken expr = self.parseConditionalExpression() if token.type is Token.Identifier and (token.lineNumber == self.lookahead.lineNumber) and token.value == 'async': if self.lookahead.type is Token.Identifier or self.matchKeyword('yield'): arg = self.parsePrimaryExpression() self.reinterpretExpressionAsPattern(arg) expr = Node.AsyncArrowParameterPlaceHolder([arg]) if expr.type is Syntax.ArrowParameterPlaceHolder or self.match('=>'): # https://tc39.github.io/ecma262/#sec-arrow-function-definitions self.context.isAssignmentTarget = False self.context.isBindingElement = False isAsync = expr.isAsync list = self.reinterpretAsCoverFormalsList(expr) if list: if self.hasLineTerminator: self.tolerateUnexpectedToken(self.lookahead) self.context.firstCoverInitializedNameError = None previousStrict = self.context.strict previousAllowStrictDirective = self.context.allowStrictDirective self.context.allowStrictDirective = list.simple previousAllowYield = self.context.allowYield previousAwait = self.context.allowAwait self.context.allowYield = True self.context.allowAwait = isAsync node = self.startNode(startToken) self.expect('=>') if self.match('{'): previousAllowIn = self.context.allowIn self.context.allowIn = True body = self.parseFunctionSourceElements() self.context.allowIn = previousAllowIn else: body = self.isolateCoverGrammar(self.parseAssignmentExpression) expression = body.type is not Syntax.BlockStatement if self.context.strict and list.firstRestricted: self.throwUnexpectedToken(list.firstRestricted, list.message) if self.context.strict and list.stricted: self.tolerateUnexpectedToken(list.stricted, list.message) if isAsync: expr = self.finalize(node, Node.AsyncArrowFunctionExpression(list.params, body, expression)) else: expr = self.finalize(node, Node.ArrowFunctionExpression(list.params, body, expression)) self.context.strict = previousStrict self.context.allowStrictDirective = previousAllowStrictDirective self.context.allowYield = previousAllowYield self.context.allowAwait = previousAwait else: if self.matchAssign(): if not self.context.isAssignmentTarget: self.tolerateError(Messages.InvalidLHSInAssignment) if self.context.strict and expr.type is Syntax.Identifier: id = expr if self.scanner.isRestrictedWord(id.name): self.tolerateUnexpectedToken(token, Messages.StrictLHSAssignment) if self.scanner.isStrictModeReservedWord(id.name): self.tolerateUnexpectedToken(token, Messages.StrictReservedWord) if not self.match('='): self.context.isAssignmentTarget = False self.context.isBindingElement = False else: self.reinterpretExpressionAsPattern(expr) token = self.nextToken() operator = token.value right = self.isolateCoverGrammar(self.parseAssignmentExpression) expr = self.finalize(self.startNode(startToken), Node.AssignmentExpression(operator, expr, right)) self.context.firstCoverInitializedNameError = None return expr # https://tc39.github.io/ecma262/#sec-comma-operator def parseExpression(self): startToken = self.lookahead expr = self.isolateCoverGrammar(self.parseAssignmentExpression) if self.match(','): expressions = [] expressions.append(expr) while self.lookahead.type is not Token.EOF: if not self.match(','): break self.nextToken() expressions.append(self.isolateCoverGrammar(self.parseAssignmentExpression)) expr = self.finalize(self.startNode(startToken), Node.SequenceExpression(expressions)) return expr # https://tc39.github.io/ecma262/#sec-block def parseStatementListItem(self): self.context.isAssignmentTarget = True self.context.isBindingElement = True if self.lookahead.type is Token.Keyword: value = self.lookahead.value if value == 'export': if not self.context.isModule: self.tolerateUnexpectedToken(self.lookahead, Messages.IllegalExportDeclaration) statement = self.parseExportDeclaration() elif value == 'import': if self.matchImportCall(): statement = self.parseExpressionStatement() else: if not self.context.isModule: self.tolerateUnexpectedToken(self.lookahead, Messages.IllegalImportDeclaration) statement = self.parseImportDeclaration() elif value == 'const': statement = self.parseLexicalDeclaration(Params(inFor=False)) elif value == 'function': statement = self.parseFunctionDeclaration() elif value == 'class': statement = self.parseClassDeclaration() elif value == 'let': statement = self.parseLexicalDeclaration(Params(inFor=False)) if self.isLexicalDeclaration() else self.parseStatement() else: statement = self.parseStatement() else: statement = self.parseStatement() return statement def parseBlock(self): node = self.createNode() self.expect('{') block = [] while True: if self.match('}'): break block.append(self.parseStatementListItem()) self.expect('}') return self.finalize(node, Node.BlockStatement(block)) # https://tc39.github.io/ecma262/#sec-let-and-const-declarations def parseLexicalBinding(self, kind, options): node = self.createNode() params = [] id = self.parsePattern(params, kind) if self.context.strict and id.type is Syntax.Identifier: if self.scanner.isRestrictedWord(id.name): self.tolerateError(Messages.StrictVarName) init = None if kind == 'const': if not self.matchKeyword('in') and not self.matchContextualKeyword('of'): if self.match('='): self.nextToken() init = self.isolateCoverGrammar(self.parseAssignmentExpression) else: self.throwError(Messages.DeclarationMissingInitializer, 'const') elif (not options.inFor and id.type is not Syntax.Identifier) or self.match('='): self.expect('=') init = self.isolateCoverGrammar(self.parseAssignmentExpression) return self.finalize(node, Node.VariableDeclarator(id, init)) def parseBindingList(self, kind, options): lst = [self.parseLexicalBinding(kind, options)] while self.match(','): self.nextToken() lst.append(self.parseLexicalBinding(kind, options)) return lst def isLexicalDeclaration(self): state = self.scanner.saveState() self.scanner.scanComments() next = self.scanner.lex() self.scanner.restoreState(state) return ( (next.type is Token.Identifier) or (next.type is Token.Punctuator and next.value == '[') or (next.type is Token.Punctuator and next.value == '{') or (next.type is Token.Keyword and next.value == 'let') or (next.type is Token.Keyword and next.value == 'yield') ) def parseLexicalDeclaration(self, options): node = self.createNode() kind = self.nextToken().value assert kind == 'let' or kind == 'const', 'Lexical declaration must be either or const' declarations = self.parseBindingList(kind, options) self.consumeSemicolon() return self.finalize(node, Node.VariableDeclaration(declarations, kind)) # https://tc39.github.io/ecma262/#sec-destructuring-binding-patterns def parseBindingRestElement(self, params, kind=None): node = self.createNode() self.expect('...') arg = self.parsePattern(params, kind) return self.finalize(node, Node.RestElement(arg)) def parseArrayPattern(self, params, kind=None): node = self.createNode() self.expect('[') elements = [] while not self.match(']'): if self.match(','): self.nextToken() elements.append(None) else: if self.match('...'): elements.append(self.parseBindingRestElement(params, kind)) break else: elements.append(self.parsePatternWithDefault(params, kind)) if not self.match(']'): self.expect(',') self.expect(']') return self.finalize(node, Node.ArrayPattern(elements)) def parsePropertyPattern(self, params, kind=None): node = self.createNode() computed = False shorthand = False method = False key = None if self.lookahead.type is Token.Identifier: keyToken = self.lookahead key = self.parseVariableIdentifier() init = self.finalize(node, Node.Identifier(keyToken.value)) if self.match('='): params.append(keyToken) shorthand = True self.nextToken() expr = self.parseAssignmentExpression() value = self.finalize(self.startNode(keyToken), Node.AssignmentPattern(init, expr)) elif not self.match(':'): params.append(keyToken) shorthand = True value = init else: self.expect(':') value = self.parsePatternWithDefault(params, kind) else: computed = self.match('[') key = self.parseObjectPropertyKey() self.expect(':') value = self.parsePatternWithDefault(params, kind) return self.finalize(node, Node.Property('init', key, computed, value, method, shorthand)) def parseRestProperty(self, params, kind): node = self.createNode() self.expect('...') arg = self.parsePattern(params) if self.match('='): self.throwError(Messages.DefaultRestProperty) if not self.match('}'): self.throwError(Messages.PropertyAfterRestProperty) return self.finalize(node, Node.RestElement(arg)) def parseObjectPattern(self, params, kind=None): node = self.createNode() properties = [] self.expect('{') while not self.match('}'): properties.append(self.parseRestProperty(params, kind) if self.match('...') else self.parsePropertyPattern(params, kind)) if not self.match('}'): self.expect(',') self.expect('}') return self.finalize(node, Node.ObjectPattern(properties)) def parsePattern(self, params, kind=None): if self.match('['): pattern = self.parseArrayPattern(params, kind) elif self.match('{'): pattern = self.parseObjectPattern(params, kind) else: if self.matchKeyword('let') and (kind in ('const', 'let')): self.tolerateUnexpectedToken(self.lookahead, Messages.LetInLexicalBinding) params.append(self.lookahead) pattern = self.parseVariableIdentifier(kind) return pattern def parsePatternWithDefault(self, params, kind=None): startToken = self.lookahead pattern = self.parsePattern(params, kind) if self.match('='): self.nextToken() previousAllowYield = self.context.allowYield self.context.allowYield = True right = self.isolateCoverGrammar(self.parseAssignmentExpression) self.context.allowYield = previousAllowYield pattern = self.finalize(self.startNode(startToken), Node.AssignmentPattern(pattern, right)) return pattern # https://tc39.github.io/ecma262/#sec-variable-statement def parseVariableIdentifier(self, kind=None): node = self.createNode() token = self.nextToken() if token.type is Token.Keyword and token.value == 'yield': if self.context.strict: self.tolerateUnexpectedToken(token, Messages.StrictReservedWord) elif not self.context.allowYield: self.throwUnexpectedToken(token) elif token.type is not Token.Identifier: if self.context.strict and token.type is Token.Keyword and self.scanner.isStrictModeReservedWord(token.value): self.tolerateUnexpectedToken(token, Messages.StrictReservedWord) else: if self.context.strict or token.value != 'let' or kind != 'var': self.throwUnexpectedToken(token) elif (self.context.isModule or self.context.allowAwait) and token.type is Token.Identifier and token.value == 'await': self.tolerateUnexpectedToken(token) return self.finalize(node, Node.Identifier(token.value)) def parseVariableDeclaration(self, options): node = self.createNode() params = [] id = self.parsePattern(params, 'var') if self.context.strict and id.type is Syntax.Identifier: if self.scanner.isRestrictedWord(id.name): self.tolerateError(Messages.StrictVarName) init = None if self.match('='): self.nextToken() init = self.isolateCoverGrammar(self.parseAssignmentExpression) elif id.type is not Syntax.Identifier and not options.inFor: self.expect('=') return self.finalize(node, Node.VariableDeclarator(id, init)) def parseVariableDeclarationList(self, options): opt = Params(inFor=options.inFor) lst = [] lst.append(self.parseVariableDeclaration(opt)) while self.match(','): self.nextToken() lst.append(self.parseVariableDeclaration(opt)) return lst def parseVariableStatement(self): node = self.createNode() self.expectKeyword('var') declarations = self.parseVariableDeclarationList(Params(inFor=False)) self.consumeSemicolon() return self.finalize(node, Node.VariableDeclaration(declarations, 'var')) # https://tc39.github.io/ecma262/#sec-empty-statement def parseEmptyStatement(self): node = self.createNode() self.expect(';') return self.finalize(node, Node.EmptyStatement()) # https://tc39.github.io/ecma262/#sec-expression-statement def parseExpressionStatement(self): node = self.createNode() expr = self.parseExpression() self.consumeSemicolon() return self.finalize(node, Node.ExpressionStatement(expr)) # https://tc39.github.io/ecma262/#sec-if-statement def parseIfClause(self): if self.context.strict and self.matchKeyword('function'): self.tolerateError(Messages.StrictFunction) return self.parseStatement() def parseIfStatement(self): node = self.createNode() alternate = None self.expectKeyword('if') self.expect('(') test = self.parseExpression() if not self.match(')') and self.config.tolerant: self.tolerateUnexpectedToken(self.nextToken()) consequent = self.finalize(self.createNode(), Node.EmptyStatement()) else: self.expect(')') consequent = self.parseIfClause() if self.matchKeyword('else'): self.nextToken() alternate = self.parseIfClause() return self.finalize(node, Node.IfStatement(test, consequent, alternate)) # https://tc39.github.io/ecma262/#sec-do-while-statement def parseDoWhileStatement(self): node = self.createNode() self.expectKeyword('do') previousInIteration = self.context.inIteration self.context.inIteration = True body = self.parseStatement() self.context.inIteration = previousInIteration self.expectKeyword('while') self.expect('(') test = self.parseExpression() if not self.match(')') and self.config.tolerant: self.tolerateUnexpectedToken(self.nextToken()) else: self.expect(')') if self.match(';'): self.nextToken() return self.finalize(node, Node.DoWhileStatement(body, test)) # https://tc39.github.io/ecma262/#sec-while-statement def parseWhileStatement(self): node = self.createNode() self.expectKeyword('while') self.expect('(') test = self.parseExpression() if not self.match(')') and self.config.tolerant: self.tolerateUnexpectedToken(self.nextToken()) body = self.finalize(self.createNode(), Node.EmptyStatement()) else: self.expect(')') previousInIteration = self.context.inIteration self.context.inIteration = True body = self.parseStatement() self.context.inIteration = previousInIteration return self.finalize(node, Node.WhileStatement(test, body)) # https://tc39.github.io/ecma262/#sec-for-statement # https://tc39.github.io/ecma262/#sec-for-in-and-for-of-statements def parseForStatement(self): init = None test = None update = None forIn = True left = None right = None node = self.createNode() self.expectKeyword('for') self.expect('(') if self.match(';'): self.nextToken() else: if self.matchKeyword('var'): init = self.createNode() self.nextToken() previousAllowIn = self.context.allowIn self.context.allowIn = False declarations = self.parseVariableDeclarationList(Params(inFor=True)) self.context.allowIn = previousAllowIn if len(declarations) == 1 and self.matchKeyword('in'): decl = declarations[0] if decl.init and (decl.id.type is Syntax.ArrayPattern or decl.id.type is Syntax.ObjectPattern or self.context.strict): self.tolerateError(Messages.ForInOfLoopInitializer, 'for-in') init = self.finalize(init, Node.VariableDeclaration(declarations, 'var')) self.nextToken() left = init right = self.parseExpression() init = None elif len(declarations) == 1 and declarations[0].init is None and self.matchContextualKeyword('of'): init = self.finalize(init, Node.VariableDeclaration(declarations, 'var')) self.nextToken() left = init right = self.parseAssignmentExpression() init = None forIn = False else: init = self.finalize(init, Node.VariableDeclaration(declarations, 'var')) self.expect(';') elif self.matchKeyword('const', 'let'): init = self.createNode() kind = self.nextToken().value if not self.context.strict and self.lookahead.value == 'in': init = self.finalize(init, Node.Identifier(kind)) self.nextToken() left = init right = self.parseExpression() init = None else: previousAllowIn = self.context.allowIn self.context.allowIn = False declarations = self.parseBindingList(kind, Params(inFor=True)) self.context.allowIn = previousAllowIn if len(declarations) == 1 and declarations[0].init is None and self.matchKeyword('in'): init = self.finalize(init, Node.VariableDeclaration(declarations, kind)) self.nextToken() left = init right = self.parseExpression() init = None elif len(declarations) == 1 and declarations[0].init is None and self.matchContextualKeyword('of'): init = self.finalize(init, Node.VariableDeclaration(declarations, kind)) self.nextToken() left = init right = self.parseAssignmentExpression() init = None forIn = False else: self.consumeSemicolon() init = self.finalize(init, Node.VariableDeclaration(declarations, kind)) else: initStartToken = self.lookahead previousAllowIn = self.context.allowIn self.context.allowIn = False init = self.inheritCoverGrammar(self.parseAssignmentExpression) self.context.allowIn = previousAllowIn if self.matchKeyword('in'): if not self.context.isAssignmentTarget or init.type is Syntax.AssignmentExpression: self.tolerateError(Messages.InvalidLHSInForIn) self.nextToken() self.reinterpretExpressionAsPattern(init) left = init right = self.parseExpression() init = None elif self.matchContextualKeyword('of'): if not self.context.isAssignmentTarget or init.type is Syntax.AssignmentExpression: self.tolerateError(Messages.InvalidLHSInForLoop) self.nextToken() self.reinterpretExpressionAsPattern(init) left = init right = self.parseAssignmentExpression() init = None forIn = False else: if self.match(','): initSeq = [init] while self.match(','): self.nextToken() initSeq.append(self.isolateCoverGrammar(self.parseAssignmentExpression)) init = self.finalize(self.startNode(initStartToken), Node.SequenceExpression(initSeq)) self.expect(';') if left is None: if not self.match(';'): test = self.parseExpression() self.expect(';') if not self.match(')'): update = self.parseExpression() if not self.match(')') and self.config.tolerant: self.tolerateUnexpectedToken(self.nextToken()) body = self.finalize(self.createNode(), Node.EmptyStatement()) else: self.expect(')') previousInIteration = self.context.inIteration self.context.inIteration = True body = self.isolateCoverGrammar(self.parseStatement) self.context.inIteration = previousInIteration if left is None: return self.finalize(node, Node.ForStatement(init, test, update, body)) if forIn: return self.finalize(node, Node.ForInStatement(left, right, body)) return self.finalize(node, Node.ForOfStatement(left, right, body)) # https://tc39.github.io/ecma262/#sec-continue-statement def parseContinueStatement(self): node = self.createNode() self.expectKeyword('continue') label = None if self.lookahead.type is Token.Identifier and not self.hasLineTerminator: id = self.parseVariableIdentifier() label = id key = '$' + id.name if key not in self.context.labelSet: self.throwError(Messages.UnknownLabel, id.name) self.consumeSemicolon() if label is None and not self.context.inIteration: self.throwError(Messages.IllegalContinue) return self.finalize(node, Node.ContinueStatement(label)) # https://tc39.github.io/ecma262/#sec-break-statement def parseBreakStatement(self): node = self.createNode() self.expectKeyword('break') label = None if self.lookahead.type is Token.Identifier and not self.hasLineTerminator: id = self.parseVariableIdentifier() key = '$' + id.name if key not in self.context.labelSet: self.throwError(Messages.UnknownLabel, id.name) label = id self.consumeSemicolon() if label is None and not self.context.inIteration and not self.context.inSwitch: self.throwError(Messages.IllegalBreak) return self.finalize(node, Node.BreakStatement(label)) # https://tc39.github.io/ecma262/#sec-return-statement def parseReturnStatement(self): if not self.context.inFunctionBody: self.tolerateError(Messages.IllegalReturn) node = self.createNode() self.expectKeyword('return') hasArgument = ( ( not self.match(';') and not self.match('}') and not self.hasLineTerminator and self.lookahead.type is not Token.EOF ) or self.lookahead.type is Token.StringLiteral or self.lookahead.type is Token.Template ) argument = self.parseExpression() if hasArgument else None self.consumeSemicolon() return self.finalize(node, Node.ReturnStatement(argument)) # https://tc39.github.io/ecma262/#sec-with-statement def parseWithStatement(self): if self.context.strict: self.tolerateError(Messages.StrictModeWith) node = self.createNode() self.expectKeyword('with') self.expect('(') object = self.parseExpression() if not self.match(')') and self.config.tolerant: self.tolerateUnexpectedToken(self.nextToken()) body = self.finalize(self.createNode(), Node.EmptyStatement()) else: self.expect(')') body = self.parseStatement() return self.finalize(node, Node.WithStatement(object, body)) # https://tc39.github.io/ecma262/#sec-switch-statement def parseSwitchCase(self): node = self.createNode() if self.matchKeyword('default'): self.nextToken() test = None else: self.expectKeyword('case') test = self.parseExpression() self.expect(':') consequent = [] while True: if self.match('}') or self.matchKeyword('default', 'case'): break consequent.append(self.parseStatementListItem()) return self.finalize(node, Node.SwitchCase(test, consequent)) def parseSwitchStatement(self): node = self.createNode() self.expectKeyword('switch') self.expect('(') discriminant = self.parseExpression() self.expect(')') previousInSwitch = self.context.inSwitch self.context.inSwitch = True cases = [] defaultFound = False self.expect('{') while True: if self.match('}'): break clause = self.parseSwitchCase() if clause.test is None: if defaultFound: self.throwError(Messages.MultipleDefaultsInSwitch) defaultFound = True cases.append(clause) self.expect('}') self.context.inSwitch = previousInSwitch return self.finalize(node, Node.SwitchStatement(discriminant, cases)) # https://tc39.github.io/ecma262/#sec-labelled-statements def parseLabelledStatement(self): node = self.createNode() expr = self.parseExpression() if expr.type is Syntax.Identifier and self.match(':'): self.nextToken() id = expr key = '$' + id.name if key in self.context.labelSet: self.throwError(Messages.Redeclaration, 'Label', id.name) self.context.labelSet[key] = True if self.matchKeyword('class'): self.tolerateUnexpectedToken(self.lookahead) body = self.parseClassDeclaration() elif self.matchKeyword('function'): token = self.lookahead declaration = self.parseFunctionDeclaration() if self.context.strict: self.tolerateUnexpectedToken(token, Messages.StrictFunction) elif declaration.generator: self.tolerateUnexpectedToken(token, Messages.GeneratorInLegacyContext) body = declaration else: body = self.parseStatement() del self.context.labelSet[key] statement = Node.LabeledStatement(id, body) else: self.consumeSemicolon() statement = Node.ExpressionStatement(expr) return self.finalize(node, statement) # https://tc39.github.io/ecma262/#sec-throw-statement def parseThrowStatement(self): node = self.createNode() self.expectKeyword('throw') if self.hasLineTerminator: self.throwError(Messages.NewlineAfterThrow) argument = self.parseExpression() self.consumeSemicolon() return self.finalize(node, Node.ThrowStatement(argument)) # https://tc39.github.io/ecma262/#sec-try-statement def parseCatchClause(self): node = self.createNode() self.expectKeyword('catch') self.expect('(') if self.match(')'): self.throwUnexpectedToken(self.lookahead) params = [] param = self.parsePattern(params) paramMap = {} for p in params: key = '$' + p.value if key in paramMap: self.tolerateError(Messages.DuplicateBinding, p.value) paramMap[key] = True if self.context.strict and param.type is Syntax.Identifier: if self.scanner.isRestrictedWord(param.name): self.tolerateError(Messages.StrictCatchVariable) self.expect(')') body = self.parseBlock() return self.finalize(node, Node.CatchClause(param, body)) def parseFinallyClause(self): self.expectKeyword('finally') return self.parseBlock() def parseTryStatement(self): node = self.createNode() self.expectKeyword('try') block = self.parseBlock() handler = self.parseCatchClause() if self.matchKeyword('catch') else None finalizer = self.parseFinallyClause() if self.matchKeyword('finally') else None if not handler and not finalizer: self.throwError(Messages.NoCatchOrFinally) return self.finalize(node, Node.TryStatement(block, handler, finalizer)) # https://tc39.github.io/ecma262/#sec-debugger-statement def parseDebuggerStatement(self): node = self.createNode() self.expectKeyword('debugger') self.consumeSemicolon() return self.finalize(node, Node.DebuggerStatement()) # https://tc39.github.io/ecma262/#sec-ecmascript-language-statements-and-declarations def parseStatement(self): typ = self.lookahead.type if typ in ( Token.BooleanLiteral, Token.NullLiteral, Token.NumericLiteral, Token.StringLiteral, Token.Template, Token.RegularExpression, ): statement = self.parseExpressionStatement() elif typ is Token.Punctuator: value = self.lookahead.value if value == '{': statement = self.parseBlock() elif value == '(': statement = self.parseExpressionStatement() elif value == ';': statement = self.parseEmptyStatement() else: statement = self.parseExpressionStatement() elif typ is Token.Identifier: statement = self.parseFunctionDeclaration() if self.matchAsyncFunction() else self.parseLabelledStatement() elif typ is Token.Keyword: value = self.lookahead.value if value == 'break': statement = self.parseBreakStatement() elif value == 'continue': statement = self.parseContinueStatement() elif value == 'debugger': statement = self.parseDebuggerStatement() elif value == 'do': statement = self.parseDoWhileStatement() elif value == 'for': statement = self.parseForStatement() elif value == 'function': statement = self.parseFunctionDeclaration() elif value == 'if': statement = self.parseIfStatement() elif value == 'return': statement = self.parseReturnStatement() elif value == 'switch': statement = self.parseSwitchStatement() elif value == 'throw': statement = self.parseThrowStatement() elif value == 'try': statement = self.parseTryStatement() elif value == 'var': statement = self.parseVariableStatement() elif value == 'while': statement = self.parseWhileStatement() elif value == 'with': statement = self.parseWithStatement() else: statement = self.parseExpressionStatement() else: statement = self.throwUnexpectedToken(self.lookahead) return statement # https://tc39.github.io/ecma262/#sec-function-definitions def parseFunctionSourceElements(self): node = self.createNode() self.expect('{') body = self.parseDirectivePrologues() previousLabelSet = self.context.labelSet previousInIteration = self.context.inIteration previousInSwitch = self.context.inSwitch previousInFunctionBody = self.context.inFunctionBody self.context.labelSet = {} self.context.inIteration = False self.context.inSwitch = False self.context.inFunctionBody = True while self.lookahead.type is not Token.EOF: if self.match('}'): break body.append(self.parseStatementListItem()) self.expect('}') self.context.labelSet = previousLabelSet self.context.inIteration = previousInIteration self.context.inSwitch = previousInSwitch self.context.inFunctionBody = previousInFunctionBody return self.finalize(node, Node.BlockStatement(body)) def validateParam(self, options, param, name): key = '$' + name if self.context.strict: if self.scanner.isRestrictedWord(name): options.stricted = param options.message = Messages.StrictParamName if key in options.paramSet: options.stricted = param options.message = Messages.StrictParamDupe elif not options.firstRestricted: if self.scanner.isRestrictedWord(name): options.firstRestricted = param options.message = Messages.StrictParamName elif self.scanner.isStrictModeReservedWord(name): options.firstRestricted = param options.message = Messages.StrictReservedWord elif key in options.paramSet: options.stricted = param options.message = Messages.StrictParamDupe options.paramSet[key] = True def parseRestElement(self, params): node = self.createNode() self.expect('...') arg = self.parsePattern(params) if self.match('='): self.throwError(Messages.DefaultRestParameter) if not self.match(')'): self.throwError(Messages.ParameterAfterRestParameter) return self.finalize(node, Node.RestElement(arg)) def parseFormalParameter(self, options): params = [] param = self.parseRestElement(params) if self.match('...') else self.parsePatternWithDefault(params) for p in params: self.validateParam(options, p, p.value) options.simple = options.simple and isinstance(param, Node.Identifier) options.params.append(param) def parseFormalParameters(self, firstRestricted=None): options = Params( simple=True, params=[], firstRestricted=firstRestricted ) self.expect('(') if not self.match(')'): options.paramSet = {} while self.lookahead.type is not Token.EOF: self.parseFormalParameter(options) if self.match(')'): break self.expect(',') if self.match(')'): break self.expect(')') return Params( simple=options.simple, params=options.params, stricted=options.stricted, firstRestricted=options.firstRestricted, message=options.message ) def matchAsyncFunction(self): match = self.matchContextualKeyword('async') if match: state = self.scanner.saveState() self.scanner.scanComments() next = self.scanner.lex() self.scanner.restoreState(state) match = (state.lineNumber == next.lineNumber) and (next.type is Token.Keyword) and (next.value == 'function') return match def parseFunctionDeclaration(self, identifierIsOptional=False): node = self.createNode() isAsync = self.matchContextualKeyword('async') if isAsync: self.nextToken() self.expectKeyword('function') isGenerator = False if isAsync else self.match('*') if isGenerator: self.nextToken() id = None firstRestricted = None if not identifierIsOptional or not self.match('('): token = self.lookahead id = self.parseVariableIdentifier() if self.context.strict: if self.scanner.isRestrictedWord(token.value): self.tolerateUnexpectedToken(token, Messages.StrictFunctionName) else: if self.scanner.isRestrictedWord(token.value): firstRestricted = token message = Messages.StrictFunctionName elif self.scanner.isStrictModeReservedWord(token.value): firstRestricted = token message = Messages.StrictReservedWord previousAllowAwait = self.context.allowAwait previousAllowYield = self.context.allowYield self.context.allowAwait = isAsync self.context.allowYield = not isGenerator formalParameters = self.parseFormalParameters(firstRestricted) params = formalParameters.params stricted = formalParameters.stricted firstRestricted = formalParameters.firstRestricted if formalParameters.message: message = formalParameters.message previousStrict = self.context.strict previousAllowStrictDirective = self.context.allowStrictDirective self.context.allowStrictDirective = formalParameters.simple body = self.parseFunctionSourceElements() if self.context.strict and firstRestricted: self.throwUnexpectedToken(firstRestricted, message) if self.context.strict and stricted: self.tolerateUnexpectedToken(stricted, message) self.context.strict = previousStrict self.context.allowStrictDirective = previousAllowStrictDirective self.context.allowAwait = previousAllowAwait self.context.allowYield = previousAllowYield if isAsync: return self.finalize(node, Node.AsyncFunctionDeclaration(id, params, body)) return self.finalize(node, Node.FunctionDeclaration(id, params, body, isGenerator)) def parseFunctionExpression(self): node = self.createNode() isAsync = self.matchContextualKeyword('async') if isAsync: self.nextToken() self.expectKeyword('function') isGenerator = False if isAsync else self.match('*') if isGenerator: self.nextToken() id = None firstRestricted = None previousAllowAwait = self.context.allowAwait previousAllowYield = self.context.allowYield self.context.allowAwait = isAsync self.context.allowYield = not isGenerator if not self.match('('): token = self.lookahead id = self.parseIdentifierName() if not self.context.strict and not isGenerator and self.matchKeyword('yield') else self.parseVariableIdentifier() if self.context.strict: if self.scanner.isRestrictedWord(token.value): self.tolerateUnexpectedToken(token, Messages.StrictFunctionName) else: if self.scanner.isRestrictedWord(token.value): firstRestricted = token message = Messages.StrictFunctionName elif self.scanner.isStrictModeReservedWord(token.value): firstRestricted = token message = Messages.StrictReservedWord formalParameters = self.parseFormalParameters(firstRestricted) params = formalParameters.params stricted = formalParameters.stricted firstRestricted = formalParameters.firstRestricted if formalParameters.message: message = formalParameters.message previousStrict = self.context.strict previousAllowStrictDirective = self.context.allowStrictDirective self.context.allowStrictDirective = formalParameters.simple body = self.parseFunctionSourceElements() if self.context.strict and firstRestricted: self.throwUnexpectedToken(firstRestricted, message) if self.context.strict and stricted: self.tolerateUnexpectedToken(stricted, message) self.context.strict = previousStrict self.context.allowStrictDirective = previousAllowStrictDirective self.context.allowAwait = previousAllowAwait self.context.allowYield = previousAllowYield if isAsync: return self.finalize(node, Node.AsyncFunctionExpression(id, params, body)) return self.finalize(node, Node.FunctionExpression(id, params, body, isGenerator)) # https://tc39.github.io/ecma262/#sec-directive-prologues-and-the-use-strict-directive def parseDirective(self): token = self.lookahead node = self.createNode() expr = self.parseExpression() directive = self.getTokenRaw(token)[1:-1] if expr.type is Syntax.Literal else None self.consumeSemicolon() return self.finalize(node, Node.Directive(expr, directive) if directive else Node.ExpressionStatement(expr)) def parseDirectivePrologues(self): firstRestricted = None body = [] while True: token = self.lookahead if token.type is not Token.StringLiteral: break statement = self.parseDirective() body.append(statement) directive = statement.directive if not isinstance(directive, basestring): break if directive == 'use strict': self.context.strict = True if firstRestricted: self.tolerateUnexpectedToken(firstRestricted, Messages.StrictOctalLiteral) if not self.context.allowStrictDirective: self.tolerateUnexpectedToken(token, Messages.IllegalLanguageModeDirective) else: if not firstRestricted and token.octal: firstRestricted = token return body # https://tc39.github.io/ecma262/#sec-method-definitions def qualifiedPropertyName(self, token): typ = token.type if typ in ( Token.Identifier, Token.StringLiteral, Token.BooleanLiteral, Token.NullLiteral, Token.NumericLiteral, Token.Keyword, ): return True elif typ is Token.Punctuator: return token.value == '[' return False def parseGetterMethod(self): node = self.createNode() isGenerator = False previousAllowYield = self.context.allowYield self.context.allowYield = not isGenerator formalParameters = self.parseFormalParameters() if len(formalParameters.params) > 0: self.tolerateError(Messages.BadGetterArity) method = self.parsePropertyMethod(formalParameters) self.context.allowYield = previousAllowYield return self.finalize(node, Node.FunctionExpression(None, formalParameters.params, method, isGenerator)) def parseSetterMethod(self): node = self.createNode() isGenerator = False previousAllowYield = self.context.allowYield self.context.allowYield = not isGenerator formalParameters = self.parseFormalParameters() if len(formalParameters.params) != 1: self.tolerateError(Messages.BadSetterArity) elif isinstance(formalParameters.params[0], Node.RestElement): self.tolerateError(Messages.BadSetterRestParameter) method = self.parsePropertyMethod(formalParameters) self.context.allowYield = previousAllowYield return self.finalize(node, Node.FunctionExpression(None, formalParameters.params, method, isGenerator)) def parseGeneratorMethod(self): node = self.createNode() isGenerator = True previousAllowYield = self.context.allowYield self.context.allowYield = True params = self.parseFormalParameters() self.context.allowYield = False method = self.parsePropertyMethod(params) self.context.allowYield = previousAllowYield return self.finalize(node, Node.FunctionExpression(None, params.params, method, isGenerator)) # https://tc39.github.io/ecma262/#sec-generator-function-definitions def isStartOfExpression(self): start = True value = self.lookahead.value typ = self.lookahead.type if typ is Token.Punctuator: start = value in ('[', '(', '{', '+', '-', '!', '~', '++', '--', '/', '/=') # regular expression literal ) elif typ is Token.Keyword: start = value in ('class', 'delete', 'function', 'let', 'new', 'super', 'this', 'typeof', 'void', 'yield') return start def parseYieldExpression(self): node = self.createNode() self.expectKeyword('yield') argument = None delegate = False if not self.hasLineTerminator: previousAllowYield = self.context.allowYield self.context.allowYield = False delegate = self.match('*') if delegate: self.nextToken() argument = self.parseAssignmentExpression() elif self.isStartOfExpression(): argument = self.parseAssignmentExpression() self.context.allowYield = previousAllowYield return self.finalize(node, Node.YieldExpression(argument, delegate)) # https://tc39.github.io/ecma262/#sec-class-definitions def parseClassElement(self, hasConstructor): token = self.lookahead node = self.createNode() kind = '' key = None value = None computed = False isStatic = False isAsync = False if self.match('*'): self.nextToken() else: computed = self.match('[') key = self.parseObjectPropertyKey() id = key if id.name == 'static' and (self.qualifiedPropertyName(self.lookahead) or self.match('*')): token = self.lookahead isStatic = True computed = self.match('[') if self.match('*'): self.nextToken() else: key = self.parseObjectPropertyKey() if token.type is Token.Identifier and not self.hasLineTerminator and token.value == 'async': punctuator = self.lookahead.value if punctuator != ':' and punctuator != '(' and punctuator != '*': isAsync = True token = self.lookahead key = self.parseObjectPropertyKey() if token.type is Token.Identifier and token.value == 'constructor': self.tolerateUnexpectedToken(token, Messages.ConstructorIsAsync) lookaheadPropertyKey = self.qualifiedPropertyName(self.lookahead) if token.type is Token.Identifier: if token.value == 'get' and lookaheadPropertyKey: kind = 'get' computed = self.match('[') key = self.parseObjectPropertyKey() self.context.allowYield = False value = self.parseGetterMethod() elif token.value == 'set' and lookaheadPropertyKey: kind = 'set' computed = self.match('[') key = self.parseObjectPropertyKey() value = self.parseSetterMethod() elif self.config.classProperties and not self.match('('): kind = 'init' id = self.finalize(node, Node.Identifier(token.value)) if self.match('='): self.nextToken() value = self.parseAssignmentExpression() elif token.type is Token.Punctuator and token.value == '*' and lookaheadPropertyKey: kind = 'method' computed = self.match('[') key = self.parseObjectPropertyKey() value = self.parseGeneratorMethod() if not kind and key and self.match('('): kind = 'method' value = self.parsePropertyMethodAsyncFunction() if isAsync else self.parsePropertyMethodFunction() if not kind: self.throwUnexpectedToken(self.lookahead) if not computed: if isStatic and self.isPropertyKey(key, 'prototype'): self.throwUnexpectedToken(token, Messages.StaticPrototype) if not isStatic and self.isPropertyKey(key, 'constructor'): if kind != 'method' or (value and value.generator): self.throwUnexpectedToken(token, Messages.ConstructorSpecialMethod) if hasConstructor.value: self.throwUnexpectedToken(token, Messages.DuplicateConstructor) else: hasConstructor.value = True kind = 'constructor' if kind in ('constructor', 'method', 'get', 'set'): return self.finalize(node, Node.MethodDefinition(key, computed, value, kind, isStatic)) else: return self.finalize(node, Node.FieldDefinition(key, computed, value, kind, isStatic)) def parseClassElementList(self): body = [] hasConstructor = Value(False) self.expect('{') while not self.match('}'): if self.match(';'): self.nextToken() else: body.append(self.parseClassElement(hasConstructor)) self.expect('}') return body def parseClassBody(self): node = self.createNode() elementList = self.parseClassElementList() return self.finalize(node, Node.ClassBody(elementList)) def parseClassDeclaration(self, identifierIsOptional=False): node = self.createNode() previousStrict = self.context.strict self.context.strict = True self.expectKeyword('class') id = None if identifierIsOptional and self.lookahead.type is not Token.Identifier else self.parseVariableIdentifier() superClass = None if self.matchKeyword('extends'): self.nextToken() superClass = self.isolateCoverGrammar(self.parseLeftHandSideExpressionAllowCall) classBody = self.parseClassBody() self.context.strict = previousStrict return self.finalize(node, Node.ClassDeclaration(id, superClass, classBody)) def parseClassExpression(self): node = self.createNode() previousStrict = self.context.strict self.context.strict = True self.expectKeyword('class') id = self.parseVariableIdentifier() if self.lookahead.type is Token.Identifier else None superClass = None if self.matchKeyword('extends'): self.nextToken() superClass = self.isolateCoverGrammar(self.parseLeftHandSideExpressionAllowCall) classBody = self.parseClassBody() self.context.strict = previousStrict return self.finalize(node, Node.ClassExpression(id, superClass, classBody)) # https://tc39.github.io/ecma262/#sec-scripts # https://tc39.github.io/ecma262/#sec-modules def parseModule(self): self.context.strict = True self.context.isModule = True self.scanner.isModule = True node = self.createNode() body = self.parseDirectivePrologues() while self.lookahead.type is not Token.EOF: body.append(self.parseStatementListItem()) return self.finalize(node, Node.Module(body)) def parseScript(self): node = self.createNode() body = self.parseDirectivePrologues() while self.lookahead.type is not Token.EOF: body.append(self.parseStatementListItem()) return self.finalize(node, Node.Script(body)) # https://tc39.github.io/ecma262/#sec-imports def parseModuleSpecifier(self): node = self.createNode() if self.lookahead.type is not Token.StringLiteral: self.throwError(Messages.InvalidModuleSpecifier) token = self.nextToken() raw = self.getTokenRaw(token) return self.finalize(node, Node.Literal(token.value, raw)) # import {} ... def parseImportSpecifier(self): node = self.createNode() if self.lookahead.type is Token.Identifier: imported = self.parseVariableIdentifier() local = imported if self.matchContextualKeyword('as'): self.nextToken() local = self.parseVariableIdentifier() else: imported = self.parseIdentifierName() local = imported if self.matchContextualKeyword('as'): self.nextToken() local = self.parseVariableIdentifier() else: self.throwUnexpectedToken(self.nextToken()) return self.finalize(node, Node.ImportSpecifier(local, imported)) # {foo, bar as bas def parseNamedImports(self): self.expect('{') specifiers = [] while not self.match('}'): specifiers.append(self.parseImportSpecifier()) if not self.match('}'): self.expect(',') self.expect('}') return specifiers # import ... def parseImportDefaultSpecifier(self): node = self.createNode() local = self.parseIdentifierName() return self.finalize(node, Node.ImportDefaultSpecifier(local)) # import <* as foo> ... def parseImportNamespaceSpecifier(self): node = self.createNode() self.expect('*') if not self.matchContextualKeyword('as'): self.throwError(Messages.NoAsAfterImportNamespace) self.nextToken() local = self.parseIdentifierName() return self.finalize(node, Node.ImportNamespaceSpecifier(local)) def parseImportDeclaration(self): if self.context.inFunctionBody: self.throwError(Messages.IllegalImportDeclaration) node = self.createNode() self.expectKeyword('import') specifiers = [] if self.lookahead.type is Token.StringLiteral: # import 'foo' src = self.parseModuleSpecifier() else: if self.match('{'): # import {bar specifiers.extend(self.parseNamedImports()) elif self.match('*'): # import * as foo specifiers.append(self.parseImportNamespaceSpecifier()) elif self.isIdentifierName(self.lookahead) and not self.matchKeyword('default'): # import foo specifiers.append(self.parseImportDefaultSpecifier()) if self.match(','): self.nextToken() if self.match('*'): # import foo, * as foo specifiers.append(self.parseImportNamespaceSpecifier()) elif self.match('{'): # import foo, {bar specifiers.extend(self.parseNamedImports()) else: self.throwUnexpectedToken(self.lookahead) else: self.throwUnexpectedToken(self.nextToken()) if not self.matchContextualKeyword('from'): message = Messages.UnexpectedToken if self.lookahead.value else Messages.MissingFromClause self.throwError(message, self.lookahead.value) self.nextToken() src = self.parseModuleSpecifier() self.consumeSemicolon() return self.finalize(node, Node.ImportDeclaration(specifiers, src)) # https://tc39.github.io/ecma262/#sec-exports def parseExportSpecifier(self): node = self.createNode() local = self.parseIdentifierName() exported = local if self.matchContextualKeyword('as'): self.nextToken() exported = self.parseIdentifierName() return self.finalize(node, Node.ExportSpecifier(local, exported)) def parseExportDefaultSpecifier(self): node = self.createNode() local = self.parseIdentifierName() return self.finalize(node, Node.ExportDefaultSpecifier(local)) def parseExportDeclaration(self): if self.context.inFunctionBody: self.throwError(Messages.IllegalExportDeclaration) node = self.createNode() self.expectKeyword('export') if self.matchKeyword('default'): # export default ... self.nextToken() if self.matchKeyword('function'): # export default function foo (: # export default function (: declaration = self.parseFunctionDeclaration(True) exportDeclaration = self.finalize(node, Node.ExportDefaultDeclaration(declaration)) elif self.matchKeyword('class'): # export default class foo { declaration = self.parseClassDeclaration(True) exportDeclaration = self.finalize(node, Node.ExportDefaultDeclaration(declaration)) elif self.matchContextualKeyword('async'): # export default async function f (: # export default async function (: # export default async x => x declaration = self.parseFunctionDeclaration(True) if self.matchAsyncFunction() else self.parseAssignmentExpression() exportDeclaration = self.finalize(node, Node.ExportDefaultDeclaration(declaration)) else: if self.matchContextualKeyword('from'): self.throwError(Messages.UnexpectedToken, self.lookahead.value) # export default {} # export default [] # export default (1 + 2) if self.match('{'): declaration = self.parseObjectInitializer() elif self.match('['): declaration = self.parseArrayInitializer() else: declaration = self.parseAssignmentExpression() self.consumeSemicolon() exportDeclaration = self.finalize(node, Node.ExportDefaultDeclaration(declaration)) elif self.match('*'): # export * from 'foo' self.nextToken() if not self.matchContextualKeyword('from'): message = Messages.UnexpectedToken if self.lookahead.value else Messages.MissingFromClause self.throwError(message, self.lookahead.value) self.nextToken() src = self.parseModuleSpecifier() self.consumeSemicolon() exportDeclaration = self.finalize(node, Node.ExportAllDeclaration(src)) elif self.lookahead.type is Token.Keyword: # export var f = 1 value = self.lookahead.value if value in ( 'let', 'const', ): declaration = self.parseLexicalDeclaration(Params(inFor=False)) elif value in ( 'var', 'class', 'function', ): declaration = self.parseStatementListItem() else: self.throwUnexpectedToken(self.lookahead) exportDeclaration = self.finalize(node, Node.ExportNamedDeclaration(declaration, [], None)) elif self.matchAsyncFunction(): declaration = self.parseFunctionDeclaration() exportDeclaration = self.finalize(node, Node.ExportNamedDeclaration(declaration, [], None)) else: specifiers = [] source = None isExportFromIdentifier = False expectSpecifiers = True if self.lookahead.type is Token.Identifier: specifiers.append(self.parseExportDefaultSpecifier()) if self.match(','): self.nextToken() else: expectSpecifiers = False if expectSpecifiers: self.expect('{') while not self.match('}'): isExportFromIdentifier = isExportFromIdentifier or self.matchKeyword('default') specifiers.append(self.parseExportSpecifier()) if not self.match('}'): self.expect(',') self.expect('}') if self.matchContextualKeyword('from'): # export {default} from 'foo' # export {foo} from 'foo' self.nextToken() source = self.parseModuleSpecifier() self.consumeSemicolon() elif isExportFromIdentifier: # export {default}; # missing fromClause message = Messages.UnexpectedToken if self.lookahead.value else Messages.MissingFromClause self.throwError(message, self.lookahead.value) else: # export {foo} self.consumeSemicolon() exportDeclaration = self.finalize(node, Node.ExportNamedDeclaration(None, specifiers, source)) return exportDeclaration