Source code

Revision control

Copy as Markdown

Other Tools

# -*- 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 <COPYRIGHT HOLDER> 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 an 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
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
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))
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))
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))
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
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
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
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
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
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
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('=>'):
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
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
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))
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))
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
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'))
def parseEmptyStatement(self):
node = self.createNode()
self.expect(';')
return self.finalize(node, Node.EmptyStatement())
def parseExpressionStatement(self):
node = self.createNode()
expr = self.parseExpression()
self.consumeSemicolon()
return self.finalize(node, Node.ExpressionStatement(expr))
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))
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))
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))
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))
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))
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))
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))
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))
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))
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)
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))
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))
def parseDebuggerStatement(self):
node = self.createNode()
self.expectKeyword('debugger')
self.consumeSemicolon()
return self.finalize(node, Node.DebuggerStatement())
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
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))
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
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))
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))
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))
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))
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 {<foo as bar>} ...
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 <foo> ...
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))
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