summaryrefslogtreecommitdiff
path: root/doc/_extensions/pyjsparser/parser.py
diff options
context:
space:
mode:
authorVictor Feyens <vfe@odoo.com>2021-04-30 13:24:57 +0000
committerAntoine Vandevenne (anv) <anv@odoo.com>2021-05-04 08:09:20 +0000
commit27bad8c3e32049ec69a588df0558e57a0b928309 (patch)
treeb60e430163d486c8c1fd052738c9854aeb8300f5 /doc/_extensions/pyjsparser/parser.py
parent75a122bbae3a32bfa79da3506179fc63a8bced5d (diff)
[MOV] doc: move documentation to a dedicated repository
In odoo/documentation-user#945, the developer documentation is merged with the user documentation in a single repository and build config. This commit then removes source files for the developer documentation from the odoo/odoo repository. DOC PR: https://github.com/odoo/documentation-user/pull/945 task-2351938 task-2352371 task-2205684 task-2352544 Co-authored-by: Victor Feyens <vfe@odoo.com> Co-authored-by: Antoine Vandevenne <anv@odoo.com>
Diffstat (limited to 'doc/_extensions/pyjsparser/parser.py')
-rw-r--r--doc/_extensions/pyjsparser/parser.py2920
1 files changed, 0 insertions, 2920 deletions
diff --git a/doc/_extensions/pyjsparser/parser.py b/doc/_extensions/pyjsparser/parser.py
deleted file mode 100644
index 69729c44889..00000000000
--- a/doc/_extensions/pyjsparser/parser.py
+++ /dev/null
@@ -1,2920 +0,0 @@
-# The MIT License
-#
-# Copyright 2014, 2015 Piotr Dabkowski
-#
-# Permission is hereby granted, free of charge, to any person obtaining
-# a copy of this software and associated documentation files (the 'Software'),
-# to deal in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-# the Software, and to permit persons to whom the Software is furnished to do so, subject
-# to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in all copies or
-# substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
-# LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
-# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
-from __future__ import unicode_literals
-from .pyjsparserdata import *
-from .std_nodes import *
-from pprint import pprint
-import sys
-
-__all__ = ['PyJsParser', 'parse', 'ENABLE_JS2PY_ERRORS', 'ENABLE_PYIMPORT', 'JsSyntaxError']
-REGEXP_SPECIAL_SINGLE = ('\\', '^', '$', '*', '+', '?', '.', '[', ']', '(', ')', '{', '{', '|', '-')
-ENABLE_PYIMPORT = False
-ENABLE_JS2PY_ERRORS = False
-ESPRIMA_VERSION = '2.2.0'
-
-DEBUG = False
-# Small naming convention changes
-# len -> leng
-# id -> d
-# type -> typ
-# str -> st
-true = True
-false = False
-null = None
-
-
-class PyJsParser:
- """ Usage:
- parser = PyJsParser()
- parser.parse('var JavaScriptCode = 5.1')
- """
-
- def __init__(self):
- self.clean()
-
- def test(self, code):
- pprint(self.parse(code))
-
- def clean(self):
- self.strict = None
- self.sourceType = None
- self.index = 0
- self.lineNumber = 1
- self.lineStart = 0
- self.hasLineTerminator = None
- self.lastIndex = None
- self.lastLineNumber = None
- self.lastLineStart = None
- self.startIndex = None
- self.startLineNumber = None
- self.startLineStart = None
- self.scanning = None
- self.lookahead = None
- self.state = None
- self.extra = None
- self.isBindingElement = None
- self.isAssignmentTarget = None
- self.firstCoverInitializedNameError = None
-
- # 7.4 Comments
-
- def skipSingleLineComment(self, offset):
- start = self.index - offset;
- while self.index < self.length:
- ch = self.source[self.index];
- self.index += 1
- if isLineTerminator(ch):
- if (ord(ch) == 13 and ord(self.source[self.index]) == 10):
- self.index += 1
- self.lineNumber += 1
- self.hasLineTerminator = True
- self.lineStart = self.index
- return {
- 'type': 'Line',
- 'value': self.source[start + offset:self.index-2],
- 'leading': True,
- 'trailing': False,
- 'loc': None,
- }
-
- def skipMultiLineComment(self):
- start = self.index
- while self.index < self.length:
- ch = ord(self.source[self.index])
- if isLineTerminator(ch):
- if (ch == 0x0D and ord(self.source[self.index + 1]) == 0x0A):
- self.index += 1
- self.lineNumber += 1
- self.index += 1
- self.hasLineTerminator = True
- self.lineStart = self.index
- elif ch == 0x2A:
- # Block comment ends with '*/'.
- if ord(self.source[self.index + 1]) == 0x2F:
- self.index += 2
- return {
- 'type': 'Block',
- 'value': self.source[start:self.index-2],
- 'leading': True,
- 'trailing': False,
- 'loc': None,
- }
- self.index += 1
- else:
- self.index += 1
- self.tolerateUnexpectedToken()
-
- def skipComment(self):
- self.hasLineTerminator = False
- startIndex = self.index
- start = (self.index == 0)
- comments = []
- while self.index < self.length:
- ch = ord(self.source[self.index])
- if isWhiteSpace(ch):
- self.index += 1
- elif isLineTerminator(ch):
- self.hasLineTerminator = True
- self.index += 1
- if (ch == 0x0D and ord(self.source[self.index]) == 0x0A):
- self.index += 1
- self.lineNumber += 1
- self.lineStart = self.index
- start = True
- elif (ch == 0x2F): # U+002F is '/'
- ch = ord(self.source[self.index + 1])
- if (ch == 0x2F):
- self.index += 2
- comments.append(self.skipSingleLineComment(2))
- start = True
- elif (ch == 0x2A): # U+002A is '*'
- self.index += 2
- comments.append(self.skipMultiLineComment())
- else:
- break
- elif (start and ch == 0x2D): # U+002D is '-'
- # U+003E is '>'
- if (ord(self.source[self.index + 1]) == 0x2D) and (ord(self.source[self.index + 2]) == 0x3E):
- # '-->' is a single-line comment
- self.index += 3
- self.skipSingleLineComment(3)
- else:
- break
- elif (ch == 0x3C): # U+003C is '<'
- if self.source[self.index + 1: self.index + 4] == '!--':
- # <!--
- self.index += 4
- self.skipSingleLineComment(4)
- else:
- break
- else:
- break
- return [c for c in comments if c]
-
- def scanHexEscape(self, prefix):
- code = 0
- leng = 4 if (prefix == 'u') else 2
- for i in range(leng):
- if self.index < self.length and isHexDigit(self.source[self.index]):
- ch = self.source[self.index]
- self.index += 1
- code = code * 16 + HEX_CONV[ch]
- else:
- return ''
- return chr(code)
-
- def scanUnicodeCodePointEscape(self):
- ch = self.source[self.index]
- code = 0
- # At least, one hex digit is required.
- if ch == '}':
- self.throwUnexpectedToken()
- while (self.index < self.length):
- ch = self.source[self.index]
- self.index += 1
- if not isHexDigit(ch):
- break
- code = code * 16 + HEX_CONV[ch]
- if code > 0x10FFFF or ch != '}':
- self.throwUnexpectedToken()
- # UTF-16 Encoding
- if (code <= 0xFFFF):
- return chr(code)
- cu1 = ((code - 0x10000) >> 10) + 0xD800;
- cu2 = ((code - 0x10000) & 1023) + 0xDC00;
- return chr(cu1) + chr(cu2)
-
- def ccode(self, offset=0):
- return ord(self.source[self.index + offset])
-
- def log_err_case(self):
- if not DEBUG:
- return
- print('INDEX', self.index)
- print(self.source[self.index - 10:self.index + 10])
- print('')
-
- def at(self, loc):
- return None if loc >= self.length else self.source[loc]
-
- def substr(self, le, offset=0):
- return self.source[self.index + offset:self.index + offset + le]
-
- def getEscapedIdentifier(self):
- d = self.source[self.index]
- ch = ord(d)
- self.index += 1
- # '\u' (U+005C, U+0075) denotes an escaped character.
- if (ch == 0x5C):
- if (ord(self.source[self.index]) != 0x75):
- self.throwUnexpectedToken()
- self.index += 1
- ch = self.scanHexEscape('u')
- if not ch or ch == '\\' or not isIdentifierStart(ch[0]):
- self.throwUnexpectedToken()
- d = ch
- while (self.index < self.length):
- ch = self.ccode()
- if not isIdentifierPart(ch):
- break
- self.index += 1
- d += unichr(ch)
-
- # '\u' (U+005C, U+0075) denotes an escaped character.
- if (ch == 0x5C):
- d = d[0: len(d) - 1]
- if (self.ccode() != 0x75):
- self.throwUnexpectedToken()
- self.index += 1
- ch = self.scanHexEscape('u');
- if (not ch or ch == '\\' or not isIdentifierPart(ch[0])):
- self.throwUnexpectedToken()
- d += ch
- return d
-
- def getIdentifier(self):
- start = self.index
- self.index += 1
- while (self.index < self.length):
- ch = self.ccode()
- if (ch == 0x5C):
- # Blackslash (U+005C) marks Unicode escape sequence.
- self.index = start
- return self.getEscapedIdentifier()
- if (isIdentifierPart(ch)):
- self.index += 1
- else:
- break
- return self.source[start: self.index]
-
- def scanIdentifier(self):
- start = self.index
-
- # Backslash (U+005C) starts an escaped character.
- d = self.getEscapedIdentifier() if (self.ccode() == 0x5C) else self.getIdentifier()
-
- # There is no keyword or literal with only one character.
- # Thus, it must be an identifier.
- if (len(d) == 1):
- type = Token.Identifier
- elif (isKeyword(d)):
- type = Token.Keyword
- elif (d == 'null'):
- type = Token.NullLiteral
- elif (d == 'true' or d == 'false'):
- type = Token.BooleanLiteral
- else:
- type = Token.Identifier;
- return {
- 'type': type,
- 'value': d,
- 'lineNumber': self.lineNumber,
- 'lineStart': self.lineStart,
- 'start': start,
- 'end': self.index
- }
-
- # 7.7 Punctuators
-
- def scanPunctuator(self):
- token = {
- 'type': Token.Punctuator,
- 'value': '',
- 'lineNumber': self.lineNumber,
- 'lineStart': self.lineStart,
- 'start': self.index,
- 'end': self.index
- }
- # Check for most common single-character punctuators.
- st = self.source[self.index]
- if st == '{':
- self.state['curlyStack'].append('{')
- self.index += 1
- elif st == '}':
- self.index += 1
- self.state['curlyStack'].pop()
- elif st in ('.', '(', ')', ';', ',', '[', ']', ':', '?', '~'):
- self.index += 1
- else:
- # 4-character punctuator.
- st = self.substr(4)
- if (st == '>>>='):
- self.index += 4
- else:
- # 3-character punctuators.
- st = st[0:3]
- if st in ('===', '!==', '>>>', '<<=', '>>='):
- self.index += 3
- else:
- # 2-character punctuators.
- st = st[0:2]
- if st in ('&&', '||', '==', '!=', '+=', '-=', '*=', '/=', '++', '--', '<<', '>>', '&=', '|=', '^=',
- '%=', '<=', '>=', '=>'):
- self.index += 2
- else:
- # 1-character punctuators.
- st = self.source[self.index]
- if st in ('<', '>', '=', '!', '+', '-', '*', '%', '&', '|', '^', '/'):
- self.index += 1
- if self.index == token['start']:
- self.throwUnexpectedToken()
- token['end'] = self.index;
- token['value'] = st
- return token
-
- # 7.8.3 Numeric Literals
-
- def scanHexLiteral(self, start):
- number = ''
- while (self.index < self.length):
- if (not isHexDigit(self.source[self.index])):
- break
- number += self.source[self.index]
- self.index += 1
- if not number:
- self.throwUnexpectedToken()
- if isIdentifierStart(self.ccode()):
- self.throwUnexpectedToken()
- return {
- 'type': Token.NumericLiteral,
- 'value': int(number, 16),
- 'lineNumber': self.lineNumber,
- 'lineStart': self.lineStart,
- 'start': start,
- 'end': self.index}
-
- def scanBinaryLiteral(self, start):
- number = ''
- while (self.index < self.length):
- ch = self.source[self.index]
- if (ch != '0' and ch != '1'):
- break
- number += self.source[self.index]
- self.index += 1
-
- if not number:
- # only 0b or 0B
- self.throwUnexpectedToken()
- if (self.index < self.length):
- ch = self.source[self.index]
- # istanbul ignore else
- if (isIdentifierStart(ch) or isDecimalDigit(ch)):
- self.throwUnexpectedToken();
- return {
- 'type': Token.NumericLiteral,
- 'value': int(number, 2),
- 'lineNumber': self.lineNumber,
- 'lineStart': self.lineStart,
- 'start': start,
- 'end': self.index}
-
- def scanOctalLiteral(self, prefix, start):
- if isOctalDigit(prefix):
- octal = True
- number = '0' + self.source[self.index]
- self.index += 1
- else:
- octal = False
- self.index += 1
- number = ''
- while (self.index < self.length):
- if (not isOctalDigit(self.source[self.index])):
- break
- number += self.source[self.index]
- self.index += 1
- if (not octal and not number):
- # only 0o or 0O
- self.throwUnexpectedToken()
- if (isIdentifierStart(self.ccode()) or isDecimalDigit(self.ccode())):
- self.throwUnexpectedToken()
- return {
- 'type': Token.NumericLiteral,
- 'value': int(number, 8),
- 'lineNumber': self.lineNumber,
- 'lineStart': self.lineStart,
- 'start': start,
- 'end': self.index}
-
- def octalToDecimal(self, ch):
- # \0 is not octal escape sequence
- octal = (ch != '0')
- code = int(ch, 8)
-
- if (self.index < self.length and isOctalDigit(self.source[self.index])):
- octal = True
- code = code * 8 + int(self.source[self.index], 8)
- self.index += 1
-
- # 3 digits are only allowed when string starts
- # with 0, 1, 2, 3
- if (ch in '0123' and self.index < self.length and isOctalDigit(self.source[self.index])):
- code = code * 8 + int((self.source[self.index]), 8)
- self.index += 1
- return {
- 'code': code,
- 'octal': octal}
-
- def isImplicitOctalLiteral(self):
- # Implicit octal, unless there is a non-octal digit.
- # (Annex B.1.1 on Numeric Literals)
- for i in range(self.index + 1, self.length):
- ch = self.source[i];
- if (ch == '8' or ch == '9'):
- return False;
- if (not isOctalDigit(ch)):
- return True
- return True
-
- def scanNumericLiteral(self):
- ch = self.source[self.index]
- assert isDecimalDigit(ch) or (ch == '.'), 'Numeric literal must start with a decimal digit or a decimal point'
- start = self.index
- number = ''
- if ch != '.':
- number = self.source[self.index]
- self.index += 1
- ch = self.source[self.index]
- # Hex number starts with '0x'.
- # Octal number starts with '0'.
- # Octal number in ES6 starts with '0o'.
- # Binary number in ES6 starts with '0b'.
- if (number == '0'):
- if (ch == 'x' or ch == 'X'):
- self.index += 1
- return self.scanHexLiteral(start);
- if (ch == 'b' or ch == 'B'):
- self.index += 1
- return self.scanBinaryLiteral(start)
- if (ch == 'o' or ch == 'O'):
- return self.scanOctalLiteral(ch, start)
- if (isOctalDigit(ch)):
- if (self.isImplicitOctalLiteral()):
- return self.scanOctalLiteral(ch, start);
- while (isDecimalDigit(self.ccode())):
- number += self.source[self.index]
- self.index += 1
- ch = self.source[self.index];
- if (ch == '.'):
- number += self.source[self.index]
- self.index += 1
- while (isDecimalDigit(self.source[self.index])):
- number += self.source[self.index]
- self.index += 1
- ch = self.source[self.index]
- if (ch == 'e' or ch == 'E'):
- number += self.source[self.index]
- self.index += 1
- ch = self.source[self.index]
- if (ch == '+' or ch == '-'):
- number += self.source[self.index]
- self.index += 1
- if (isDecimalDigit(self.source[self.index])):
- while (isDecimalDigit(self.source[self.index])):
- number += self.source[self.index]
- self.index += 1
- else:
- self.throwUnexpectedToken()
- if (isIdentifierStart(self.source[self.index])):
- self.throwUnexpectedToken();
- return {
- 'type': Token.NumericLiteral,
- 'value': float(number),
- 'lineNumber': self.lineNumber,
- 'lineStart': self.lineStart,
- 'start': start,
- 'end': self.index}
-
- # 7.8.4 String Literals
-
- def _interpret_regexp(self, string, flags):
- '''Perform sctring escape - for regexp literals'''
- self.index = 0
- self.length = len(string)
- self.source = string
- self.lineNumber = 0
- self.lineStart = 0
- octal = False
- st = ''
- inside_square = 0
- while (self.index < self.length):
- template = '[%s]' if not inside_square else '%s'
- ch = self.source[self.index]
- self.index += 1
- if ch == '\\':
- ch = self.source[self.index]
- self.index += 1
- if (not isLineTerminator(ch)):
- if ch == 'u':
- digs = self.source[self.index:self.index + 4]
- if len(digs) == 4 and all(isHexDigit(d) for d in digs):
- st += template % chr(int(digs, 16))
- self.index += 4
- else:
- st += 'u'
- elif ch == 'x':
- digs = self.source[self.index:self.index + 2]
- if len(digs) == 2 and all(isHexDigit(d) for d in digs):
- st += template % chr(int(digs, 16))
- self.index += 2
- else:
- st += 'x'
- # special meaning - single char.
- elif ch == '0':
- st += '\\0'
- elif ch == 'n':
- st += '\\n'
- elif ch == 'r':
- st += '\\r'
- elif ch == 't':
- st += '\\t'
- elif ch == 'f':
- st += '\\f'
- elif ch == 'v':
- st += '\\v'
-
- # unescape special single characters like . so that they are interpreted literally
- elif ch in REGEXP_SPECIAL_SINGLE:
- st += '\\' + ch
-
- # character groups
- elif ch == 'b':
- st += '\\b'
- elif ch == 'B':
- st += '\\B'
- elif ch == 'w':
- st += '\\w'
- elif ch == 'W':
- st += '\\W'
- elif ch == 'd':
- st += '\\d'
- elif ch == 'D':
- st += '\\D'
- elif ch == 's':
- st += template % u' \f\n\r\t\v\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff'
- elif ch == 'S':
- st += template % u'\u0000-\u0008\u000e-\u001f\u0021-\u009f\u00a1-\u167f\u1681-\u180d\u180f-\u1fff\u200b-\u2027\u202a-\u202e\u2030-\u205e\u2060-\u2fff\u3001-\ufefe\uff00-\uffff'
- else:
- if isDecimalDigit(ch):
- num = ch
- while self.index < self.length and isDecimalDigit(self.source[self.index]):
- num += self.source[self.index]
- self.index += 1
- st += '\\' + num
-
- else:
- st += ch # DONT ESCAPE!!!
- else:
- self.lineNumber += 1
- if (ch == '\r' and self.source[self.index] == '\n'):
- self.index += 1
- self.lineStart = self.index
- else:
- if ch == '[':
- inside_square = True
- elif ch == ']':
- inside_square = False
- st += ch
- # print string, 'was transformed to', st
- return st
-
- def scanStringLiteral(self):
- st = ''
- octal = False
-
- quote = self.source[self.index]
- assert quote == '\'' or quote == '"', 'String literal must starts with a quote'
- start = self.index;
- self.index += 1
-
- while (self.index < self.length):
- ch = self.source[self.index]
- self.index += 1
- if (ch == quote):
- quote = ''
- break
- elif (ch == '\\'):
- ch = self.source[self.index]
- self.index += 1
- if (not isLineTerminator(ch)):
- if ch in 'ux':
- if (self.source[self.index] == '{'):
- self.index += 1
- st += self.scanUnicodeCodePointEscape()
- else:
- unescaped = self.scanHexEscape(ch)
- if (not unescaped):
- self.throwUnexpectedToken() # with throw I don't know whats the difference
- st += unescaped
- elif ch == 'n':
- st += '\n';
- elif ch == 'r':
- st += '\r';
- elif ch == 't':
- st += '\t';
- elif ch == 'b':
- st += '\b';
- elif ch == 'f':
- st += '\f';
- elif ch == 'v':
- st += '\x0B'
- # elif ch in '89':
- # self.throwUnexpectedToken() # again with throw....
- else:
- if isOctalDigit(ch):
- octToDec = self.octalToDecimal(ch)
- octal = octToDec.get('octal') or octal
- st += unichr(octToDec['code'])
- else:
- st += ch
- else:
- self.lineNumber += 1
- if (ch == '\r' and self.source[self.index] == '\n'):
- self.index += 1
- self.lineStart = self.index
- elif isLineTerminator(ch):
- break
- else:
- st += ch;
- if (quote != ''):
- self.throwUnexpectedToken()
- return {
- 'type': Token.StringLiteral,
- 'value': st,
- 'octal': octal,
- 'lineNumber': self.lineNumber,
- 'lineStart': self.startLineStart,
- 'start': start,
- 'end': self.index}
-
- def scanTemplate(self):
- cooked = ''
- terminated = False
- tail = False
- start = self.index
- head = (self.source[self.index] == '`')
- rawOffset = 2
-
- self.index += 1
-
- while (self.index < self.length):
- ch = self.source[self.index]
- self.index += 1
- if (ch == '`'):
- rawOffset = 1;
- tail = True
- terminated = True
- break
- elif (ch == '$'):
- if (self.source[self.index] == '{'):
- self.state['curlyStack'].append('${')
- self.index += 1
- terminated = True
- break;
- cooked += ch
- elif (ch == '\\'):
- ch = self.source[self.index]
- self.index += 1
- if (not isLineTerminator(ch)):
- if ch == 'n':
- cooked += '\n'
- elif ch == 'r':
- cooked += '\r'
- elif ch == 't':
- cooked += '\t'
- elif ch in 'ux':
- if (self.source[self.index] == '{'):
- self.index += 1
- cooked += self.scanUnicodeCodePointEscape()
- else:
- restore = self.index
- unescaped = self.scanHexEscape(ch)
- if (unescaped):
- cooked += unescaped
- else:
- self.index = restore
- cooked += ch
- elif ch == 'b':
- cooked += '\b'
- elif ch == 'f':
- cooked += '\f'
- elif ch == 'v':
- cooked += '\v'
- else:
- if (ch == '0'):
- if isDecimalDigit(self.ccode()):
- # Illegal: \01 \02 and so on
- self.throwError(Messages.TemplateOctalLiteral)
- cooked += '\0'
- elif (isOctalDigit(ch)):
- # Illegal: \1 \2
- self.throwError(Messages.TemplateOctalLiteral)
- else:
- cooked += ch
- else:
- self.lineNumber += 1
- if (ch == '\r' and self.source[self.index] == '\n'):
- self.index += 1
- self.lineStart = self.index
- elif (isLineTerminator(ch)):
- self.lineNumber += 1
- if (ch == '\r' and self.source[self.index] == '\n'):
- self.index += 1
- self.lineStart = self.index
- cooked += '\n'
- else:
- cooked += ch;
- if (not terminated):
- self.throwUnexpectedToken()
-
- if (not head):
- self.state['curlyStack'].pop();
-
- return {
- 'type': Token.Template,
- 'value': {
- 'cooked': cooked,
- 'raw': self.source[start + 1:self.index - rawOffset]},
- 'head': head,
- 'tail': tail,
- 'lineNumber': self.lineNumber,
- 'lineStart': self.lineStart,
- 'start': start,
- 'end': self.index}
-
- def testRegExp(self, pattern, flags):
- # todo: you should return python regexp object
- return (pattern, flags)
-
- def scanRegExpBody(self):
- ch = self.source[self.index]
- assert ch == '/', 'Regular expression literal must start with a slash'
- st = ch
- self.index += 1
-
- classMarker = False
- terminated = False
- while (self.index < self.length):
- ch = self.source[self.index]
- self.index += 1
- st += ch
- if (ch == '\\'):
- ch = self.source[self.index]
- self.index += 1
- # ECMA-262 7.8.5
- if (isLineTerminator(ch)):
- self.throwUnexpectedToken(None, Messages.UnterminatedRegExp)
- st += ch
- elif (isLineTerminator(ch)):
- self.throwUnexpectedToken(None, Messages.UnterminatedRegExp)
- elif (classMarker):
- if (ch == ']'):
- classMarker = False
- else:
- if (ch == '/'):
- terminated = True
- break
- elif (ch == '['):
- classMarker = True;
- if (not terminated):
- self.throwUnexpectedToken(None, Messages.UnterminatedRegExp)
-
- # Exclude leading and trailing slash.
- body = st[1:-1]
- return {
- 'value': body,
- 'literal': st}
-
- def scanRegExpFlags(self):
- st = ''
- flags = ''
- while (self.index < self.length):
- ch = self.source[self.index]
- if (not isIdentifierPart(ch)):
- break
- self.index += 1
- if (ch == '\\' and self.index < self.length):
- ch = self.source[self.index]
- if (ch == 'u'):
- self.index += 1
- restore = self.index
- ch = self.scanHexEscape('u')
- if (ch):
- flags += ch
- st += '\\u'
- while restore < self.index:
- st += self.source[restore]
- restore += 1
- else:
- self.index = restore
- flags += 'u'
- st += '\\u'
- self.tolerateUnexpectedToken()
- else:
- st += '\\'
- self.tolerateUnexpectedToken()
- else:
- flags += ch
- st += ch
- return {
- 'value': flags,
- 'literal': st}
-
- def scanRegExp(self, comments):
- self.scanning = True
- self.lookahead = None
- comments.extend(self.skipComment())
- start = self.index
-
- body = self.scanRegExpBody()
- flags = self.scanRegExpFlags()
- value = self.testRegExp(body['value'], flags['value'])
- scanning = False
- return {
- 'literal': body['literal'] + flags['literal'],
- 'value': value,
- 'regex': {
- 'pattern': body['value'],
- 'flags': flags['value']
- },
- 'start': start,
- 'end': self.index,
- 'comments': comments}
-
- def collectRegex(self):
- return self.scanRegExp(self.skipComment())
-
- def isIdentifierName(self, token):
- return token['type'] in (1, 3, 4, 5)
-
- # def advanceSlash(self): ???
-
- def advanceWithComments(self, comments):
- token = self.advance()
- token['comments'] = comments
- return token
-
- def advance(self):
- if (self.index >= self.length):
- return {
- 'type': Token.EOF,
- 'lineNumber': self.lineNumber,
- 'lineStart': self.lineStart,
- 'start': self.index,
- 'end': self.index}
- ch = self.ccode()
-
- if isIdentifierStart(ch):
- token = self.scanIdentifier()
- if (self.strict and isStrictModeReservedWord(token['value'])):
- token['type'] = Token.Keyword
- return token
- # Very common: ( and ) and ;
- if (ch == 0x28 or ch == 0x29 or ch == 0x3B):
- return self.scanPunctuator()
-
- # String literal starts with single quote (U+0027) or double quote (U+0022).
- if (ch == 0x27 or ch == 0x22):
- return self.scanStringLiteral()
-
- # Dot (.) U+002E can also start a floating-point number, hence the need
- # to check the next character.
- if (ch == 0x2E):
- if (isDecimalDigit(self.ccode(1))):
- return self.scanNumericLiteral()
- return self.scanPunctuator();
-
- if (isDecimalDigit(ch)):
- return self.scanNumericLiteral()
-
- # Slash (/) U+002F can also start a regex.
- # if (extra.tokenize && ch == 0x2F):
- # return advanceSlash();
-
- # Template literals start with ` (U+0060) for template head
- # or } (U+007D) for template middle or template tail.
- if (ch == 0x60 or (ch == 0x7D and self.state['curlyStack'][len(self.state['curlyStack']) - 1] == '${')):
- return self.scanTemplate()
- return self.scanPunctuator();
-
- # def collectToken(self):
- # loc = {
- # 'start': {
- # 'line': self.lineNumber,
- # 'column': self.index - self.lineStart}}
- #
- # token = self.advance()
- #
- # loc['end'] = {
- # 'line': self.lineNumber,
- # 'column': self.index - self.lineStart}
- # if (token['type'] != Token.EOF):
- # value = self.source[token['start']: token['end']]
- # entry = {
- # 'type': TokenName[token['type']],
- # 'value': value,
- # 'range': [token['start'], token['end']],
- # 'loc': loc}
- # if (token.get('regex')):
- # entry['regex'] = {
- # 'pattern': token['regex']['pattern'],
- # 'flags': token['regex']['flags']}
- # self.extra['tokens'].append(entry)
- # return token;
-
-
- def lex(self):
- self.scanning = True
-
- self.lastIndex = self.index
- self.lastLineNumber = self.lineNumber
- self.lastLineStart = self.lineStart
-
- comments = self.skipComment()
-
- token = self.lookahead
-
- self.startIndex = self.index
- self.startLineNumber = self.lineNumber
- self.startLineStart = self.lineStart
-
- self.lookahead = self.advanceWithComments(comments)
- self.scanning = False
- return token
-
- def peek(self):
- self.scanning = True
-
- comments = self.skipComment()
-
- self.lastIndex = self.index
- self.lastLineNumber = self.lineNumber
- self.lastLineStart = self.lineStart
-
- self.startIndex = self.index
- self.startLineNumber = self.lineNumber
- self.startLineStart = self.lineStart
-
- self.lookahead = self.advanceWithComments(comments)
- self.scanning = False
-
- def createError(self, line, pos, description):
- global ENABLE_PYIMPORT
- if ENABLE_JS2PY_ERRORS:
- old_pyimport = ENABLE_PYIMPORT # ENABLE_PYIMPORT will be affected by js2py import
- self.log_err_case()
- try:
- from js2py.base import ERRORS, Js, JsToPyException
- except:
- raise Exception("ENABLE_JS2PY_ERRORS was set to True, but Js2Py was not found!")
- ENABLE_PYIMPORT = old_pyimport
- error = ERRORS['SyntaxError']('Line ' + str(line) + ': ' + str(description))
- error.put('index', Js(pos))
- error.put('lineNumber', Js(line))
- error.put('column', Js(pos - (self.lineStart if self.scanning else self.lastLineStart) + 1))
- error.put('description', Js(description))
- return JsToPyException(error)
- else:
- return JsSyntaxError('Line ' + str(line) + ': ' + str(description))
-
-
- # Throw an exception
-
- def throwError(self, messageFormat, *args):
- msg = messageFormat % tuple(str(e) for e in args)
- raise self.createError(self.lastLineNumber, self.lastIndex, msg);
-
- def tolerateError(self, messageFormat, *args):
- return self.throwError(messageFormat, *args)
-
- # Throw an exception because of the token.
-
- def unexpectedTokenError(self, token={}, message=''):
- msg = message or Messages.UnexpectedToken
- if (token):
- typ = token['type']
- if (not message):
- if typ == Token.EOF:
- msg = Messages.UnexpectedEOS
- elif (typ == Token.Identifier):
- msg = Messages.UnexpectedIdentifier
- elif (typ == Token.NumericLiteral):
- msg = Messages.UnexpectedNumber
- elif (typ == Token.StringLiteral):
- msg = Messages.UnexpectedString
- elif (typ == Token.Template):
- msg = Messages.UnexpectedTemplate
- else:
- msg = Messages.UnexpectedToken;
- if (typ == Token.Keyword):
- if (isFutureReservedWord(token['value'])):
- msg = Messages.UnexpectedReserved
- elif (self.strict and isStrictModeReservedWord(token['value'])):
- msg = Messages.StrictReservedWord
- value = token['value']['raw'] if (typ == Token.Template) else token.get('value')
- else:
- value = 'ILLEGAL'
- msg = msg.replace('%s', str(value))
-
- return (self.createError(token['lineNumber'], token['start'], msg) if (token and token.get('lineNumber')) else
- self.createError(self.lineNumber if self.scanning else self.lastLineNumber,
- self.index if self.scanning else self.lastIndex, msg))
-
- def throwUnexpectedToken(self, token={}, message=''):
- raise self.unexpectedTokenError(token, message)
-
- def tolerateUnexpectedToken(self, token={}, message=''):
- self.throwUnexpectedToken(token, message)
-
- # Expect the next token to match the specified punctuator.
- # If not, an exception will be thrown.
-
- def expect(self, value):
- token = self.lex()
- if (token['type'] != Token.Punctuator or token['value'] != value):
- self.throwUnexpectedToken(token)
-
- # /**
- # * @name expectCommaSeparator
- # * @description Quietly expect a comma when in tolerant mode, otherwise delegates
- # * to <code>expect(value)</code>
- # * @since 2.0
- # */
- def expectCommaSeparator(self):
- self.expect(',')
-
- # Expect the next token to match the specified keyword.
- # If not, an exception will be thrown.
-
- def expectKeyword(self, keyword):
- token = self.lex();
- if (token['type'] != 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'] == Token.Punctuator and self.lookahead['value'] == value
-
- # Return true if the next token matches the specified keyword
-
- def matchKeyword(self, keyword):
- return self.lookahead['type'] == Token.Keyword and self.lookahead['value'] == 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'] == Token.Identifier and self.lookahead['value'] == keyword
-
- # Return true if the next token is an assignment operator
-
- def matchAssign(self):
- if (self.lookahead['type'] != Token.Punctuator):
- return False;
- op = self.lookahead['value']
- return op in ('=', '*=', '/=', '%=', '+=', '-=', '<<=', '>>=', '>>>=', '&=', '^=', '|=')
-
- def consumeSemicolon(self):
- # Catch the very common case first: immediately a semicolon (U+003B).
-
- if (self.at(self.startIndex) == ';' or self.match(';')):
- self.lex()
- return
-
- if (self.hasLineTerminator):
- return
-
- # TODO: FIXME(ikarienator): this is seemingly an issue in the previous location info convention.
- self.lastIndex = self.startIndex
- self.lastLineNumber = self.startLineNumber
- self.lastLineStart = self.startLineStart
-
- if (self.lookahead['type'] != Token.EOF and not self.match('}')):
- self.throwUnexpectedToken(self.lookahead)
-
- # // 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, parser):
- oldIsBindingElement = self.isBindingElement
- oldIsAssignmentTarget = self.isAssignmentTarget
- oldFirstCoverInitializedNameError = self.firstCoverInitializedNameError
- self.isBindingElement = true
- self.isAssignmentTarget = true
- self.firstCoverInitializedNameError = null
- result = parser()
- if (self.firstCoverInitializedNameError != null):
- self.throwUnexpectedToken(self.firstCoverInitializedNameError)
- self.isBindingElement = oldIsBindingElement
- self.isAssignmentTarget = oldIsAssignmentTarget
- self.firstCoverInitializedNameError = oldFirstCoverInitializedNameError
- return result
-
- def inheritCoverGrammar(self, parser):
- oldIsBindingElement = self.isBindingElement
- oldIsAssignmentTarget = self.isAssignmentTarget
- oldFirstCoverInitializedNameError = self.firstCoverInitializedNameError
- self.isBindingElement = true
- self.isAssignmentTarget = true
- self.firstCoverInitializedNameError = null
- result = parser()
- self.isBindingElement = self.isBindingElement and oldIsBindingElement
- self.isAssignmentTarget = self.isAssignmentTarget and oldIsAssignmentTarget
- self.firstCoverInitializedNameError = oldFirstCoverInitializedNameError or self.firstCoverInitializedNameError
- return result
-
- def parseArrayPattern(self):
- node = Node()
- elements = []
- self.expect('[');
- while (not self.match(']')):
- if (self.match(',')):
- self.lex()
- elements.append(null)
- else:
- if (self.match('...')):
- restNode = Node()
- self.lex()
- rest = self.parseVariableIdentifier()
- elements.append(restNode.finishRestElement(rest))
- break
- else:
- elements.append(self.parsePatternWithDefault())
- if (not self.match(']')):
- self.expect(',')
- self.expect(']')
- return node.finishArrayPattern(elements)
-
- def parsePropertyPattern(self):
- node = Node()
- computed = self.match('[')
- if (self.lookahead['type'] == Token.Identifier):
- key = self.parseVariableIdentifier()
- if (self.match('=')):
- self.lex();
- init = self.parseAssignmentExpression()
- return node.finishProperty(
- 'init', key, false, WrappingNode(key).finishAssignmentPattern(key, init), false, false)
- elif (not self.match(':')):
- return node.finishProperty('init', key, false, key, false, true)
- else:
- key = self.parseObjectPropertyKey()
- self.expect(':')
- init = self.parsePatternWithDefault()
- return node.finishProperty('init', key, computed, init, false, false)
-
- def parseObjectPattern(self):
- node = Node()
- properties = []
- self.expect('{')
- while (not self.match('}')):
- properties.append(self.parsePropertyPattern())
- if (not self.match('}')):
- self.expect(',')
- self.lex()
- return node.finishObjectPattern(properties)
-
- def parsePattern(self):
- if (self.lookahead['type'] == Token.Identifier):
- return self.parseVariableIdentifier()
- elif (self.match('[')):
- return self.parseArrayPattern()
- elif (self.match('{')):
- return self.parseObjectPattern()
- self.throwUnexpectedToken(self.lookahead)
-
- def parsePatternWithDefault(self):
- startToken = self.lookahead
-
- pattern = self.parsePattern()
- if (self.match('=')):
- self.lex()
- right = self.isolateCoverGrammar(self.parseAssignmentExpression)
- pattern = WrappingNode(startToken).finishAssignmentPattern(pattern, right)
- return pattern
-
- # 11.1.4 Array Initialiser
-
- def parseArrayInitialiser(self):
- elements = []
- node = Node()
-
- self.expect('[')
-
- while (not self.match(']')):
- if (self.match(',')):
- self.lex()
- elements.append(null)
- elif (self.match('...')):
- restSpread = Node()
- self.lex()
- restSpread.finishSpreadElement(self.inheritCoverGrammar(self.parseAssignmentExpression))
- if (not self.match(']')):
- self.isAssignmentTarget = self.isBindingElement = false
- self.expect(',')
- elements.append(restSpread)
- else:
- elements.append(self.inheritCoverGrammar(self.parseAssignmentExpression))
- if (not self.match(']')):
- self.expect(',')
- self.lex();
-
- return node.finishArrayExpression(elements)
-
- # 11.1.5 Object Initialiser
-
- def parsePropertyFunction(self, node, paramInfo):
-
- self.isAssignmentTarget = self.isBindingElement = false;
-
- previousStrict = self.strict;
- body = self.isolateCoverGrammar(self.parseFunctionSourceElements);
-
- if (self.strict and paramInfo['firstRestricted']):
- self.tolerateUnexpectedToken(paramInfo['firstRestricted'], paramInfo.get('message'))
- if (self.strict and paramInfo.get('stricted')):
- self.tolerateUnexpectedToken(paramInfo.get('stricted'), paramInfo.get('message'));
-
- self.strict = previousStrict;
- return node.finishFunctionExpression(null, paramInfo.get('params'), paramInfo.get('defaults'), body)
-
- def parsePropertyMethodFunction(self):
- node = Node();
-
- params = self.parseParams(null);
- method = self.parsePropertyFunction(node, params);
- return method;
-
- def parseObjectPropertyKey(self):
- node = Node()
-
- token = self.lex();
-
- # // Note: This function is called only from parseObjectProperty(), where
- # // EOF and Punctuator tokens are already filtered out.
-
- typ = token['type']
-
- if typ in [Token.StringLiteral, Token.NumericLiteral]:
- if self.strict and token.get('octal'):
- self.tolerateUnexpectedToken(token, Messages.StrictOctalLiteral);
- return node.finishLiteral(token);
- elif typ in (Token.Identifier, Token.BooleanLiteral, Token.NullLiteral, Token.Keyword):
- return node.finishIdentifier(token['value']);
- elif typ == Token.Punctuator:
- if (token['value'] == '['):
- expr = self.isolateCoverGrammar(self.parseAssignmentExpression)
- self.expect(']')
- return expr
- self.throwUnexpectedToken(token)
-
- def lookaheadPropertyName(self):
- typ = self.lookahead['type']
- if typ in (Token.Identifier, Token.StringLiteral, Token.BooleanLiteral, Token.NullLiteral, Token.NumericLiteral,
- Token.Keyword):
- return true
- if typ == Token.Punctuator:
- return self.lookahead['value'] == '['
- return false
-
- # // This function is to try to parse a MethodDefinition as defined in 14.3. But in the case of object literals,
- # // it might be called at a position where there is in fact a short hand identifier pattern or a data property.
- # // This can only be determined after we consumed up to the left parentheses.
- # //
- # // In order to avoid back tracking, it returns `null` if the position is not a MethodDefinition and the caller
- # // is responsible to visit other options.
- def tryParseMethodDefinition(self, token, key, computed, node):
- if (token['type'] == Token.Identifier):
- # check for `get` and `set`;
-
- if (token['value'] == 'get' and self.lookaheadPropertyName()):
- computed = self.match('[');
- key = self.parseObjectPropertyKey()
- methodNode = Node()
- self.expect('(')
- self.expect(')')
- value = self.parsePropertyFunction(methodNode, {
- 'params': [],
- 'defaults': [],
- 'stricted': null,
- 'firstRestricted': null,
- 'message': null
- })
- return node.finishProperty('get', key, computed, value, false, false)
- elif (token['value'] == 'set' and self.lookaheadPropertyName()):
- computed = self.match('[')
- key = self.parseObjectPropertyKey()
- methodNode = Node()
- self.expect('(')
-
- options = {
- 'params': [],
- 'defaultCount': 0,
- 'defaults': [],
- 'firstRestricted': null,
- 'paramSet': {}
- }
- if (self.match(')')):
- self.tolerateUnexpectedToken(self.lookahead);
- else:
- self.parseParam(options);
- if (options['defaultCount'] == 0):
- options['defaults'] = []
- self.expect(')')
-
- value = self.parsePropertyFunction(methodNode, options);
- return node.finishProperty('set', key, computed, value, false, false);
- if (self.match('(')):
- value = self.parsePropertyMethodFunction();
- return node.finishProperty('init', key, computed, value, true, false)
- return null;
-
- def checkProto(self, key, computed, hasProto):
- if (computed == false and (key['type'] == Syntax.Identifier and key['name'] == '__proto__' or
- key['type'] == Syntax.Literal and key['value'] == '__proto__')):
- if (hasProto['value']):
- self.tolerateError(Messages.DuplicateProtoProperty);
- else:
- hasProto['value'] = true;
-
- def parseObjectProperty(self, hasProto):
- token = self.lookahead
- node = Node()
- node.comments = self.lookahead.get('comments', [])
-
- computed = self.match('[');
- key = self.parseObjectPropertyKey();
- maybeMethod = self.tryParseMethodDefinition(token, key, computed, node)
-
- if (maybeMethod):
- self.checkProto(maybeMethod['key'], maybeMethod['computed'], hasProto);
- return maybeMethod;
-
- # // init property or short hand property.
- self.checkProto(key, computed, hasProto);
-
- if (self.match(':')):
- self.lex();
- value = self.inheritCoverGrammar(self.parseAssignmentExpression)
- return node.finishProperty('init', key, computed, value, false, false)
-
- if (token['type'] == Token.Identifier):
- if (self.match('=')):
- self.firstCoverInitializedNameError = self.lookahead;
- self.lex();
- value = self.isolateCoverGrammar(self.parseAssignmentExpression);
- return node.finishProperty('init', key, computed,
- WrappingNode(token).finishAssignmentPattern(key, value), false, true)
- return node.finishProperty('init', key, computed, key, false, true)
- self.throwUnexpectedToken(self.lookahead)
-
- def parseObjectInitialiser(self):
- properties = []
- hasProto = {'value': false}
- node = Node();
- node.comments = self.lookahead.get('comments', [])
- self.expect('{');
- while (not self.match('}')):
- properties.append(self.parseObjectProperty(hasProto));
-
- if (not self.match('}')):
- self.expectCommaSeparator()
- self.expect('}');
- return node.finishObjectExpression(properties)
-
- def reinterpretExpressionAsPattern(self, expr):
- typ = (expr['type'])
- if typ in (Syntax.Identifier, Syntax.MemberExpression, Syntax.RestElement, Syntax.AssignmentPattern):
- pass
- elif typ == Syntax.SpreadElement:
- expr['type'] = Syntax.RestElement
- self.reinterpretExpressionAsPattern(expr.argument)
- elif typ == Syntax.ArrayExpression:
- expr['type'] = Syntax.ArrayPattern
- for i in range(len(expr['elements'])):
- if (expr['elements'][i] != null):
- self.reinterpretExpressionAsPattern(expr['elements'][i])
- elif typ == Syntax.ObjectExpression:
- expr['type'] = Syntax.ObjectPattern
- for i in range(len(expr['properties'])):
- self.reinterpretExpressionAsPattern(expr['properties'][i]['value']);
- elif Syntax.AssignmentExpression:
- expr['type'] = Syntax.AssignmentPattern;
- self.reinterpretExpressionAsPattern(expr['left'])
- else:
- # // Allow other node type for tolerant parsing.
- return
-
- def parseTemplateElement(self, option):
-
- if (self.lookahead['type'] != Token.Template or (option['head'] and not self.lookahead['head'])):
- self.throwUnexpectedToken()
-
- node = Node();
- token = self.lex();
-
- return node.finishTemplateElement({'raw': token['value']['raw'], 'cooked': token['value']['cooked']},
- token['tail'])
-
- def parseTemplateLiteral(self):
- node = Node()
-
- quasi = self.parseTemplateElement({'head': true})
- quasis = [quasi]
- expressions = []
-
- while (not quasi['tail']):
- expressions.append(self.parseExpression());
- quasi = self.parseTemplateElement({'head': false});
- quasis.append(quasi)
- return node.finishTemplateLiteral(quasis, expressions)
-
- # 11.1.6 The Grouping Operator
-
- def parseGroupExpression(self):
- self.expect('(');
-
- if (self.match(')')):
- self.lex();
- if (not self.match('=>')):
- self.expect('=>')
- return {
- 'type': PlaceHolders.ArrowParameterPlaceHolder,
- 'params': []}
-
- startToken = self.lookahead
- if (self.match('...')):
- expr = self.parseRestElement();
- self.expect(')');
- if (not self.match('=>')):
- self.expect('=>')
- return {
- 'type': PlaceHolders.ArrowParameterPlaceHolder,
- 'params': [expr]}
-
- self.isBindingElement = true;
- expr = self.inheritCoverGrammar(self.parseAssignmentExpression);
-
- if (self.match(',')):
- self.isAssignmentTarget = false;
- expressions = [expr]
-
- while (self.startIndex < self.length):
- if (not self.match(',')):
- break
- self.lex();
-
- if (self.match('...')):
- if (not self.isBindingElement):
- self.throwUnexpectedToken(self.lookahead)
- expressions.append(self.parseRestElement())
- self.expect(')');
- if (not self.match('=>')):
- self.expect('=>');
- self.isBindingElement = false
- for i in range(len(expressions)):
- self.reinterpretExpressionAsPattern(expressions[i])
- return {
- 'type': PlaceHolders.ArrowParameterPlaceHolder,
- 'params': expressions}
- expressions.append(self.inheritCoverGrammar(self.parseAssignmentExpression))
- expr = WrappingNode(startToken).finishSequenceExpression(expressions);
- self.expect(')')
-
- if (self.match('=>')):
- if (not self.isBindingElement):
- self.throwUnexpectedToken(self.lookahead);
- if (expr['type'] == Syntax.SequenceExpression):
- for i in range(len(expr.expressions)):
- self.reinterpretExpressionAsPattern(expr['expressions'][i])
- else:
- self.reinterpretExpressionAsPattern(expr);
- expr = {
- 'type': PlaceHolders.ArrowParameterPlaceHolder,
- 'params': expr['expressions'] if expr['type'] == Syntax.SequenceExpression else [expr]}
- self.isBindingElement = false
- return expr
-
- # 11.1 Primary Expressions
-
- def parsePrimaryExpression(self):
- if (self.match('(')):
- self.isBindingElement = false;
- return self.inheritCoverGrammar(self.parseGroupExpression)
- if (self.match('[')):
- return self.inheritCoverGrammar(self.parseArrayInitialiser)
-
- if (self.match('{')):
- return self.inheritCoverGrammar(self.parseObjectInitialiser)
-
- typ = self.lookahead['type']
- node = Node();
- node.comments = self.lookahead.get('comments', [])
-
- if (typ == Token.Identifier):
- expr = node.finishIdentifier(self.lex()['value']);
- elif (typ == Token.StringLiteral or typ == Token.NumericLiteral):
- self.isAssignmentTarget = self.isBindingElement = false
- if (self.strict and self.lookahead.get('octal')):
- self.tolerateUnexpectedToken(self.lookahead, Messages.StrictOctalLiteral)
- expr = node.finishLiteral(self.lex())
- elif (typ == Token.Keyword):
- self.isAssignmentTarget = self.isBindingElement = false
- if (self.matchKeyword('function')):
- return self.parseFunctionExpression()
- if (self.matchKeyword('this')):
- self.lex()
- return node.finishThisExpression()
- if (self.matchKeyword('class')):
- return self.parseClassExpression()
- self.throwUnexpectedToken(self.lex())
- elif (typ == Token.BooleanLiteral):
- isAssignmentTarget = self.isBindingElement = false
- token = self.lex();
- token['value'] = (token['value'] == 'true')
- expr = node.finishLiteral(token)
- elif (typ == Token.NullLiteral):
- self.isAssignmentTarget = self.isBindingElement = false
- token = self.lex()
- token['value'] = null;
- expr = node.finishLiteral(token)
- elif (self.match('/') or self.match('/=')):
- self.isAssignmentTarget = self.isBindingElement = false;
- self.index = self.startIndex;
- token = self.scanRegExp([]); # hehe, here you are!
- self.lex();
- expr = node.finishLiteral(token);
- elif (typ == Token.Template):
- expr = self.parseTemplateLiteral()
- else:
- self.throwUnexpectedToken(self.lex());
- return expr;
-
- # 11.2 Left-Hand-Side Expressions
-
- def parseArguments(self):
- args = [];
-
- self.expect('(');
- if (not self.match(')')):
- while (self.startIndex < self.length):
- args.append(self.isolateCoverGrammar(self.parseAssignmentExpression))
- if (self.match(')')):
- break
- self.expectCommaSeparator()
- self.expect(')')
- return args;
-
- def parseNonComputedProperty(self):
- node = Node()
-
- token = self.lex();
-
- if (not self.isIdentifierName(token)):
- self.throwUnexpectedToken(token)
- return node.finishIdentifier(token['value'])
-
- def parseNonComputedMember(self):
- self.expect('.')
- return self.parseNonComputedProperty();
-
- def parseComputedMember(self):
- self.expect('[')
-
- expr = self.isolateCoverGrammar(self.parseExpression)
- self.expect(']')
-
- return expr
-
- def parseNewExpression(self):
- node = Node()
- self.expectKeyword('new')
- callee = self.isolateCoverGrammar(self.parseLeftHandSideExpression)
- args = self.parseArguments() if self.match('(') else []
-
- self.isAssignmentTarget = self.isBindingElement = false
-
- return node.finishNewExpression(callee, args)
-
- def parseLeftHandSideExpressionAllowCall(self):
- previousAllowIn = self.state['allowIn']
-
- startToken = self.lookahead;
- self.state['allowIn'] = true;
-
- if (self.matchKeyword('super') and self.state['inFunctionBody']):
- expr = Node();
- self.lex();
- expr = expr.finishSuper()
- 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.isBindingElement = false;
- self.isAssignmentTarget = true;
- property = self.parseNonComputedMember();
- expr = WrappingNode(startToken).finishMemberExpression('.', expr, property)
- elif (self.match('(')):
- self.isBindingElement = false;
- self.isAssignmentTarget = false;
- args = self.parseArguments();
- expr = WrappingNode(startToken).finishCallExpression(expr, args)
- elif (self.match('[')):
- self.isBindingElement = false;
- self.isAssignmentTarget = true;
- property = self.parseComputedMember();
- expr = WrappingNode(startToken).finishMemberExpression('[', expr, property)
- elif (self.lookahead['type'] == Token.Template and self.lookahead['head']):
- quasi = self.parseTemplateLiteral()
- expr = WrappingNode(startToken).finishTaggedTemplateExpression(expr, quasi)
- else:
- break
- self.state['allowIn'] = previousAllowIn
-
- return expr
-
- def parseLeftHandSideExpression(self):
- assert self.state['allowIn'], 'callee of new expression always allow in keyword.'
-
- startToken = self.lookahead
-
- if (self.matchKeyword('super') and self.state['inFunctionBody']):
- expr = Node();
- self.lex();
- expr = expr.finishSuper();
- if (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.isBindingElement = false;
- self.isAssignmentTarget = true;
- property = self.parseComputedMember();
- expr = WrappingNode(startToken).finishMemberExpression('[', expr, property)
- elif (self.match('.')):
- self.isBindingElement = false;
- self.isAssignmentTarget = true;
- property = self.parseNonComputedMember();
- expr = WrappingNode(startToken).finishMemberExpression('.', expr, property);
- elif (self.lookahead['type'] == Token.Template and self.lookahead['head']):
- quasi = self.parseTemplateLiteral();
- expr = WrappingNode(startToken).finishTaggedTemplateExpression(expr, quasi)
- else:
- break
- return expr
-
- # 11.3 Postfix Expressions
-
- def parsePostfixExpression(self):
- startToken = self.lookahead
-
- expr = self.inheritCoverGrammar(self.parseLeftHandSideExpressionAllowCall)
-
- if (not self.hasLineTerminator and self.lookahead['type'] == Token.Punctuator):
- if (self.match('++') or self.match('--')):
- # 11.3.1, 11.3.2
- if (self.strict and expr.type == Syntax.Identifier and isRestrictedWord(expr.name)):
- self.tolerateError(Messages.StrictLHSPostfix)
- if (not self.isAssignmentTarget):
- self.tolerateError(Messages.InvalidLHSInAssignment);
- self.isAssignmentTarget = self.isBindingElement = false;
-
- token = self.lex();
- expr = WrappingNode(startToken).finishPostfixExpression(token['value'], expr);
- return expr;
-
- # 11.4 Unary Operators
-
- def parseUnaryExpression(self):
-
- if (self.lookahead['type'] != Token.Punctuator and self.lookahead['type'] != Token.Keyword):
- expr = self.parsePostfixExpression();
- elif (self.match('++') or self.match('--')):
- startToken = self.lookahead;
- token = self.lex();
- expr = self.inheritCoverGrammar(self.parseUnaryExpression);
- # 11.4.4, 11.4.5
- if (self.strict and expr.type == Syntax.Identifier and isRestrictedWord(expr.name)):
- self.tolerateError(Messages.StrictLHSPrefix)
- if (not self.isAssignmentTarget):
- self.tolerateError(Messages.InvalidLHSInAssignment)
- expr = WrappingNode(startToken).finishUnaryExpression(token['value'], expr)
- self.isAssignmentTarget = self.isBindingElement = false
- elif (self.match('+') or self.match('-') or self.match('~') or self.match('!')):
- startToken = self.lookahead;
- token = self.lex();
- expr = self.inheritCoverGrammar(self.parseUnaryExpression);
- expr = WrappingNode(startToken).finishUnaryExpression(token['value'], expr)
- self.isAssignmentTarget = self.isBindingElement = false;
- elif (self.matchKeyword('delete') or self.matchKeyword('void') or self.matchKeyword('typeof')):
- startToken = self.lookahead;
- token = self.lex();
- expr = self.inheritCoverGrammar(self.parseUnaryExpression);
- expr = WrappingNode(startToken).finishUnaryExpression(token['value'], expr);
- if (self.strict and expr.operator == 'delete' and expr.argument.type == Syntax.Identifier):
- self.tolerateError(Messages.StrictDelete)
- self.isAssignmentTarget = self.isBindingElement = false;
- else:
- expr = self.parsePostfixExpression()
- return expr
-
- def binaryPrecedence(self, token, allowIn):
- prec = 0;
- typ = token['type']
- if (typ != Token.Punctuator and typ != Token.Keyword):
- return 0;
- val = token['value']
- if val == 'in' and not allowIn:
- return 0
- return PRECEDENCE.get(val, 0)
-
- # 11.5 Multiplicative Operators
- # 11.6 Additive Operators
- # 11.7 Bitwise Shift Operators
- # 11.8 Relational Operators
- # 11.9 Equality Operators
- # 11.10 Binary Bitwise Operators
- # 11.11 Binary Logical Operators
-
- def parseBinaryExpression(self):
-
- marker = self.lookahead;
- left = self.inheritCoverGrammar(self.parseUnaryExpression);
-
- token = self.lookahead;
- prec = self.binaryPrecedence(token, self.state['allowIn']);
- if (prec == 0):
- return left
- self.isAssignmentTarget = self.isBindingElement = false;
- token['prec'] = prec
- self.lex()
-
- markers = [marker, self.lookahead];
- right = self.isolateCoverGrammar(self.parseUnaryExpression);
-
- stack = [left, token, right];
-
- while True:
- prec = self.binaryPrecedence(self.lookahead, self.state['allowIn'])
- if not prec > 0:
- break
- # Reduce: make a binary expression from the three topmost entries.
- while ((len(stack) > 2) and (prec <= stack[len(stack) - 2]['prec'])):
- right = stack.pop();
- operator = stack.pop()['value']
- left = stack.pop()
- markers.pop()
- expr = WrappingNode(markers[len(markers) - 1]).finishBinaryExpression(operator, left, right)
- stack.append(expr)
-
- # Shift
- token = self.lex();
- token['prec'] = prec;
- stack.append(token);
- markers.append(self.lookahead);
- expr = self.isolateCoverGrammar(self.parseUnaryExpression);
- stack.append(expr);
-
- # Final reduce to clean-up the stack.
- i = len(stack) - 1;
- expr = stack[i]
- markers.pop()
- while (i > 1):
- expr = WrappingNode(markers.pop()).finishBinaryExpression(stack[i - 1]['value'], stack[i - 2], expr);
- i -= 2
- return expr
-
- # 11.12 Conditional Operator
-
- def parseConditionalExpression(self):
-
- startToken = self.lookahead
-
- expr = self.inheritCoverGrammar(self.parseBinaryExpression);
- if (self.match('?')):
- self.lex()
- previousAllowIn = self.state['allowIn']
- self.state['allowIn'] = true;
- consequent = self.isolateCoverGrammar(self.parseAssignmentExpression);
- self.state['allowIn'] = previousAllowIn;
- self.expect(':');
- alternate = self.isolateCoverGrammar(self.parseAssignmentExpression)
-
- expr = WrappingNode(startToken).finishConditionalExpression(expr, consequent, alternate);
- self.isAssignmentTarget = self.isBindingElement = false;
- return expr
-
- # [ES6] 14.2 Arrow Function
-
- def parseConciseBody(self):
- if (self.match('{')):
- return self.parseFunctionSourceElements()
- return self.isolateCoverGrammar(self.parseAssignmentExpression)
-
- def checkPatternParam(self, options, param):
- typ = param.type
- if typ == Syntax.Identifier:
- self.validateParam(options, param, param.name);
- elif typ == Syntax.RestElement:
- self.checkPatternParam(options, param.argument)
- elif typ == Syntax.AssignmentPattern:
- self.checkPatternParam(options, param.left)
- elif typ == Syntax.ArrayPattern:
- for i in range(len(param.elements)):
- if (param.elements[i] != null):
- self.checkPatternParam(options, param.elements[i]);
- else:
- assert typ == Syntax.ObjectPattern, 'Invalid type'
- for i in range(len(param.properties)):
- self.checkPatternParam(options, param.properties[i]['value']);
-
- def reinterpretAsCoverFormalsList(self, expr):
- defaults = [];
- defaultCount = 0;
- params = [expr];
- typ = expr.type
- if typ == Syntax.Identifier:
- pass
- elif typ == PlaceHolders.ArrowParameterPlaceHolder:
- params = expr.params
- else:
- return null
- options = {
- 'paramSet': {}}
- le = len(params)
- for i in range(le):
- param = params[i]
- if param.type == Syntax.AssignmentPattern:
- params[i] = param.left;
- defaults.append(param.right);
- defaultCount += 1
- self.checkPatternParam(options, param.left);
- else:
- self.checkPatternParam(options, param);
- params[i] = param;
- defaults.append(null);
- if (options.get('message') == Messages.StrictParamDupe):
- token = options.get('stricted') if self.strict else options['firstRestricted']
- self.throwUnexpectedToken(token, options.get('message'));
- if (defaultCount == 0):
- defaults = []
- return {
- 'params': params,
- 'defaults': defaults,
- 'stricted': options['stricted'],
- 'firstRestricted': options['firstRestricted'],
- 'message': options.get('message')}
-
- def parseArrowFunctionExpression(self, options, node):
- if (self.hasLineTerminator):
- self.tolerateUnexpectedToken(self.lookahead)
- self.expect('=>')
- previousStrict = self.strict;
-
- body = self.parseConciseBody();
-
- if (self.strict and options['firstRestricted']):
- self.throwUnexpectedToken(options['firstRestricted'], options.get('message'));
- if (self.strict and options['stricted']):
- self.tolerateUnexpectedToken(options['stricted'], options['message']);
-
- self.strict = previousStrict
-
- return node.finishArrowFunctionExpression(options['params'], options['defaults'], body,
- body.type != Syntax.BlockStatement)
-
- # 11.13 Assignment Operators
-
- def parseAssignmentExpression(self):
- startToken = self.lookahead;
- token = self.lookahead;
-
- expr = self.parseConditionalExpression();
-
- if (expr.type == PlaceHolders.ArrowParameterPlaceHolder or self.match('=>')):
- self.isAssignmentTarget = self.isBindingElement = false;
- lis = self.reinterpretAsCoverFormalsList(expr)
-
- if (lis):
- self.firstCoverInitializedNameError = null;
- return self.parseArrowFunctionExpression(lis, WrappingNode(startToken))
- return expr
-
- if (self.matchAssign()):
- if (not self.isAssignmentTarget):
- self.tolerateError(Messages.InvalidLHSInAssignment)
- # 11.13.1
-
- if (self.strict and expr.type == Syntax.Identifier and isRestrictedWord(expr.name)):
- self.tolerateUnexpectedToken(token, Messages.StrictLHSAssignment);
- if (not self.match('=')):
- self.isAssignmentTarget = self.isBindingElement = false;
- else:
- self.reinterpretExpressionAsPattern(expr)
- token = self.lex();
- right = self.isolateCoverGrammar(self.parseAssignmentExpression)
- expr = WrappingNode(startToken).finishAssignmentExpression(token['value'], expr, right);
- self.firstCoverInitializedNameError = null
- return expr
-
- # 11.14 Comma Operator
-
- def parseExpression(self):
- startToken = self.lookahead
- expr = self.isolateCoverGrammar(self.parseAssignmentExpression)
-
- if (self.match(',')):
- expressions = [expr];
-
- while (self.startIndex < self.length):
- if (not self.match(',')):
- break
- self.lex();
- expressions.append(self.isolateCoverGrammar(self.parseAssignmentExpression))
- expr = WrappingNode(startToken).finishSequenceExpression(expressions);
- return expr
-
- # 12.1 Block
-
- def parseStatementListItem(self):
- if (self.lookahead['type'] == Token.Keyword):
- val = (self.lookahead['value'])
- if val == 'export':
- if (self.sourceType != 'module'):
- self.tolerateUnexpectedToken(self.lookahead, Messages.IllegalExportDeclaration)
- return self.parseExportDeclaration();
- elif val == 'import':
- if (self.sourceType != 'module'):
- self.tolerateUnexpectedToken(self.lookahead, Messages.IllegalImportDeclaration);
- return self.parseImportDeclaration();
- elif val == 'const' or val == 'let':
- return self.parseLexicalDeclaration({'inFor': false});
- elif val == 'function':
- return self.parseFunctionDeclaration(Node());
- elif val == 'class':
- return self.parseClassDeclaration();
- elif ENABLE_PYIMPORT and val == 'pyimport': # <<<<< MODIFIED HERE
- return self.parsePyimportStatement()
- return self.parseStatement();
-
- def parsePyimportStatement(self):
- print(ENABLE_PYIMPORT)
- assert ENABLE_PYIMPORT
- n = Node()
- self.lex()
- n.finishPyimport(self.parseVariableIdentifier())
- self.consumeSemicolon()
- return n
-
- def parseStatementList(self):
- list = [];
- while (self.startIndex < self.length):
- if (self.match('}')):
- break
- list.append(self.parseStatementListItem())
- return list
-
- def parseBlock(self):
- node = Node();
-
- self.expect('{');
-
- block = self.parseStatementList()
-
- self.expect('}');
-
- return node.finishBlockStatement(block);
-
- # 12.2 Variable Statement
-
- def parseVariableIdentifier(self):
- node = Node()
-
- token = self.lex()
-
- if (token['type'] != Token.Identifier):
- if (self.strict and token['type'] == Token.Keyword and isStrictModeReservedWord(token['value'])):
- self.tolerateUnexpectedToken(token, Messages.StrictReservedWord);
- else:
- self.throwUnexpectedToken(token)
- return node.finishIdentifier(token['value'])
-
- def parseVariableDeclaration(self):
- init = null
- node = Node();
- d = self.parsePattern();
-
- # 12.2.1
- if (self.strict and isRestrictedWord(d.name)):
- self.tolerateError(Messages.StrictVarName);
-
- if (self.match('=')):
- self.lex();
- init = self.isolateCoverGrammar(self.parseAssignmentExpression);
- elif (d.type != Syntax.Identifier):
- self.expect('=')
- return node.finishVariableDeclarator(d, init)
-
- def parseVariableDeclarationList(self):
- lis = []
-
- while True:
- lis.append(self.parseVariableDeclaration())
- if (not self.match(',')):
- break
- self.lex();
- if not (self.startIndex < self.length):
- break
-
- return lis;
-
- def parseVariableStatement(self, node):
- self.expectKeyword('var')
- declarations = self.parseVariableDeclarationList()
-
- self.consumeSemicolon()
-
- return node.finishVariableDeclaration(declarations)
-
- def parseLexicalBinding(self, kind, options):
- init = null
- node = Node()
-
- d = self.parsePattern();
-
- # 12.2.1
- if (self.strict and d.type == Syntax.Identifier and isRestrictedWord(d.name)):
- self.tolerateError(Messages.StrictVarName);
-
- if (kind == 'const'):
- if (not self.matchKeyword('in')):
- self.expect('=')
- init = self.isolateCoverGrammar(self.parseAssignmentExpression)
- elif ((not options['inFor'] and d.type != Syntax.Identifier) or self.match('=')):
- self.expect('=');
- init = self.isolateCoverGrammar(self.parseAssignmentExpression);
- return node.finishVariableDeclarator(d, init)
-
- def parseBindingList(self, kind, options):
- list = [];
-
- while True:
- list.append(self.parseLexicalBinding(kind, options));
- if (not self.match(',')):
- break
- self.lex();
- if not (self.startIndex < self.length):
- break
- return list;
-
- def parseLexicalDeclaration(self, options):
- node = Node();
-
- kind = self.lex()['value']
- assert kind == 'let' or kind == 'const', 'Lexical declaration must be either let or const'
- declarations = self.parseBindingList(kind, options);
- self.consumeSemicolon();
- return node.finishLexicalDeclaration(declarations, kind);
-
- def parseRestElement(self):
- node = Node();
-
- self.lex();
-
- if (self.match('{')):
- self.throwError(Messages.ObjectPatternAsRestParameter)
- param = self.parseVariableIdentifier();
- if (self.match('=')):
- self.throwError(Messages.DefaultRestParameter);
-
- if (not self.match(')')):
- self.throwError(Messages.ParameterAfterRestParameter);
- return node.finishRestElement(param);
-
- # 12.3 Empty Statement
-
- def parseEmptyStatement(self, node):
- self.expect(';');
- return node.finishEmptyStatement()
-
- # 12.4 Expression Statement
-
- def parseExpressionStatement(self, node):
- expr = self.parseExpression();
- self.consumeSemicolon();
- return node.finishExpressionStatement(expr);
-
- # 12.5 If statement
-
- def parseIfStatement(self, node):
- self.expectKeyword('if');
-
- self.expect('(');
-
- test = self.parseExpression();
-
- self.expect(')');
-
- consequent = self.parseStatement();
-
- if (self.matchKeyword('else')):
- self.lex();
- alternate = self.parseStatement();
- else:
- alternate = null;
- return node.finishIfStatement(test, consequent, alternate)
-
- # 12.6 Iteration Statements
-
- def parseDoWhileStatement(self, node):
-
- self.expectKeyword('do')
-
- oldInIteration = self.state['inIteration']
- self.state['inIteration'] = true
-
- body = self.parseStatement();
-
- self.state['inIteration'] = oldInIteration;
-
- self.expectKeyword('while');
-
- self.expect('(');
-
- test = self.parseExpression();
-
- self.expect(')')
-
- if (self.match(';')):
- self.lex()
- return node.finishDoWhileStatement(body, test)
-
- def parseWhileStatement(self, node):
-
- self.expectKeyword('while')
-
- self.expect('(')
-
- test = self.parseExpression()
-
- self.expect(')')
-
- oldInIteration = self.state['inIteration']
- self.state['inIteration'] = true
-
- body = self.parseStatement()
-
- self.state['inIteration'] = oldInIteration
-
- return node.finishWhileStatement(test, body)
-
- def parseForStatement(self, node):
- previousAllowIn = self.state['allowIn']
-
- init = test = update = null
-
- self.expectKeyword('for')
-
- self.expect('(')
-
- if (self.match(';')):
- self.lex()
- else:
- if (self.matchKeyword('var')):
- init = Node()
- self.lex()
-
- self.state['allowIn'] = false;
- init = init.finishVariableDeclaration(self.parseVariableDeclarationList())
- self.state['allowIn'] = previousAllowIn
-
- if (len(init.declarations) == 1 and self.matchKeyword('in')):
- self.lex()
- left = init
- right = self.parseExpression()
- init = null
- else:
- self.expect(';')
- elif (self.matchKeyword('const') or self.matchKeyword('let')):
- init = Node()
- kind = self.lex()['value']
-
- self.state['allowIn'] = false
- declarations = self.parseBindingList(kind, {'inFor': true})
- self.state['allowIn'] = previousAllowIn
-
- if (len(declarations) == 1 and declarations[0].init == null and self.matchKeyword('in')):
- init = init.finishLexicalDeclaration(declarations, kind);
- self.lex();
- left = init;
- right = self.parseExpression();
- init = null;
- else:
- self.consumeSemicolon();
- init = init.finishLexicalDeclaration(declarations, kind);
- else:
- initStartToken = self.lookahead
- self.state['allowIn'] = false
- init = self.inheritCoverGrammar(self.parseAssignmentExpression);
- self.state['allowIn'] = previousAllowIn;
-
- if (self.matchKeyword('in')):
- if (not self.isAssignmentTarget):
- self.tolerateError(Messages.InvalidLHSInForIn)
- self.lex();
- self.reinterpretExpressionAsPattern(init);
- left = init;
- right = self.parseExpression();
- init = null;
- else:
- if (self.match(',')):
- initSeq = [init];
- while (self.match(',')):
- self.lex();
- initSeq.append(self.isolateCoverGrammar(self.parseAssignmentExpression))
- init = WrappingNode(initStartToken).finishSequenceExpression(initSeq)
- self.expect(';');
-
- if ('left' not in locals()):
- if (not self.match(';')):
- test = self.parseExpression();
-
- self.expect(';');
-
- if (not self.match(')')):
- update = self.parseExpression();
-
- self.expect(')');
-
- oldInIteration = self.state['inIteration']
- self.state['inIteration'] = true;
-
- body = self.isolateCoverGrammar(self.parseStatement)
-
- self.state['inIteration'] = oldInIteration;
-
- return node.finishForStatement(init, test, update, body) if (
- 'left' not in locals()) else node.finishForInStatement(left, right, body);
-
- # 12.7 The continue statement
-
- def parseContinueStatement(self, node):
- label = null
-
- self.expectKeyword('continue');
-
- # Optimize the most common form: 'continue;'.
- if ord(self.source[self.startIndex]) == 0x3B:
- self.lex();
- if (not self.state['inIteration']):
- self.throwError(Messages.IllegalContinue)
- return node.finishContinueStatement(null)
- if (self.hasLineTerminator):
- if (not self.state['inIteration']):
- self.throwError(Messages.IllegalContinue);
- return node.finishContinueStatement(null);
-
- if (self.lookahead['type'] == Token.Identifier):
- label = self.parseVariableIdentifier();
-
- key = '$' + label.name;
- if not key in self.state['labelSet']: # todo make sure its correct!
- self.throwError(Messages.UnknownLabel, label.name);
- self.consumeSemicolon()
-
- if (label == null and not self.state['inIteration']):
- self.throwError(Messages.IllegalContinue)
- return node.finishContinueStatement(label)
-
- # 12.8 The break statement
-
- def parseBreakStatement(self, node):
- label = null
-
- self.expectKeyword('break');
-
- # Catch the very common case first: immediately a semicolon (U+003B).
- if (ord(self.source[self.lastIndex]) == 0x3B):
- self.lex();
-
- if (not (self.state['inIteration'] or self.state['inSwitch'])):
- self.throwError(Messages.IllegalBreak)
- return node.finishBreakStatement(null)
- if (self.hasLineTerminator):
- if (not (self.state['inIteration'] or self.state['inSwitch'])):
- self.throwError(Messages.IllegalBreak);
- return node.finishBreakStatement(null);
- if (self.lookahead['type'] == Token.Identifier):
- label = self.parseVariableIdentifier();
-
- key = '$' + label.name;
- if not (key in self.state['labelSet']):
- self.throwError(Messages.UnknownLabel, label.name);
- self.consumeSemicolon();
-
- if (label == null and not (self.state['inIteration'] or self.state['inSwitch'])):
- self.throwError(Messages.IllegalBreak)
- return node.finishBreakStatement(label);
-
- # 12.9 The return statement
-
- def parseReturnStatement(self, node):
- argument = null;
-
- self.expectKeyword('return');
-
- if (not self.state['inFunctionBody']):
- self.tolerateError(Messages.IllegalReturn);
-
- # 'return' followed by a space and an identifier is very common.
- if (ord(self.source[self.lastIndex]) == 0x20):
- if (isIdentifierStart(self.source[self.lastIndex + 1])):
- argument = self.parseExpression();
- self.consumeSemicolon();
- return node.finishReturnStatement(argument)
- if (self.hasLineTerminator):
- # HACK
- return node.finishReturnStatement(null)
-
- if (not self.match(';')):
- if (not self.match('}') and self.lookahead['type'] != Token.EOF):
- argument = self.parseExpression();
- self.consumeSemicolon();
-
- return node.finishReturnStatement(argument);
-
- # 12.10 The with statement
-
- def parseWithStatement(self, node):
- if (self.strict):
- self.tolerateError(Messages.StrictModeWith)
-
- self.expectKeyword('with');
-
- self.expect('(');
-
- obj = self.parseExpression();
-
- self.expect(')');
-
- body = self.parseStatement();
-
- return node.finishWithStatement(obj, body);
-
- # 12.10 The swith statement
-
- def parseSwitchCase(self):
- consequent = []
- node = Node();
-
- if (self.matchKeyword('default')):
- self.lex();
- test = null;
- else:
- self.expectKeyword('case');
- test = self.parseExpression();
-
- self.expect(':');
-
- while (self.startIndex < self.length):
- if (self.match('}') or self.matchKeyword('default') or self.matchKeyword('case')):
- break
- statement = self.parseStatementListItem()
- consequent.append(statement)
- return node.finishSwitchCase(test, consequent)
-
- def parseSwitchStatement(self, node):
-
- self.expectKeyword('switch');
-
- self.expect('(');
-
- discriminant = self.parseExpression();
-
- self.expect(')');
-
- self.expect('{');
-
- cases = [];
-
- if (self.match('}')):
- self.lex();
- return node.finishSwitchStatement(discriminant, cases);
-
- oldInSwitch = self.state['inSwitch'];
- self.state['inSwitch'] = true;
- defaultFound = false;
-
- while (self.startIndex < self.length):
- if (self.match('}')):
- break;
- clause = self.parseSwitchCase();
- if (clause.test == null):
- if (defaultFound):
- self.throwError(Messages.MultipleDefaultsInSwitch);
- defaultFound = true;
- cases.append(clause);
-
- self.state['inSwitch'] = oldInSwitch;
-
- self.expect('}');
-
- return node.finishSwitchStatement(discriminant, cases);
-
- # 12.13 The throw statement
-
- def parseThrowStatement(self, node):
-
- self.expectKeyword('throw');
-
- if (self.hasLineTerminator):
- self.throwError(Messages.NewlineAfterThrow);
-
- argument = self.parseExpression();
-
- self.consumeSemicolon();
-
- return node.finishThrowStatement(argument);
-
- # 12.14 The try statement
-
- def parseCatchClause(self):
- node = Node();
-
- self.expectKeyword('catch');
-
- self.expect('(');
- if (self.match(')')):
- self.throwUnexpectedToken(self.lookahead);
- param = self.parsePattern();
-
- # 12.14.1
- if (self.strict and isRestrictedWord(param.name)):
- self.tolerateError(Messages.StrictCatchVariable);
-
- self.expect(')');
- body = self.parseBlock();
- return node.finishCatchClause(param, body);
-
- def parseTryStatement(self, node):
- handler = null
- finalizer = null;
-
- self.expectKeyword('try');
-
- block = self.parseBlock();
-
- if (self.matchKeyword('catch')):
- handler = self.parseCatchClause()
-
- if (self.matchKeyword('finally')):
- self.lex();
- finalizer = self.parseBlock();
-
- if (not handler and not finalizer):
- self.throwError(Messages.NoCatchOrFinally)
-
- return node.finishTryStatement(block, handler, finalizer)
-
- # 12.15 The debugger statement
-
- def parseDebuggerStatement(self, node):
- self.expectKeyword('debugger');
-
- self.consumeSemicolon();
-
- return node.finishDebuggerStatement();
-
- # 12 Statements
-
- def parseStatement(self):
- typ = self.lookahead['type']
-
- if (typ == Token.EOF):
- self.throwUnexpectedToken(self.lookahead)
-
- if (typ == Token.Punctuator and self.lookahead['value'] == '{'):
- return self.parseBlock()
-
- self.isAssignmentTarget = self.isBindingElement = true;
- node = Node();
- node.comments = self.lookahead.get('comments', [])
- val = self.lookahead['value']
-
- if (typ == Token.Punctuator):
- if val == ';':
- return self.parseEmptyStatement(node);
- elif val == '(':
- return self.parseExpressionStatement(node);
- elif (typ == Token.Keyword):
- if val == 'break':
- return self.parseBreakStatement(node);
- elif val == 'continue':
- return self.parseContinueStatement(node);
- elif val == 'debugger':
- return self.parseDebuggerStatement(node);
- elif val == 'do':
- return self.parseDoWhileStatement(node);
- elif val == 'for':
- return self.parseForStatement(node);
- elif val == 'function':
- return self.parseFunctionDeclaration(node);
- elif val == 'if':
- return self.parseIfStatement(node);
- elif val == 'return':
- return self.parseReturnStatement(node);
- elif val == 'switch':
- return self.parseSwitchStatement(node);
- elif val == 'throw':
- return self.parseThrowStatement(node);
- elif val == 'try':
- return self.parseTryStatement(node);
- elif val == 'var':
- return self.parseVariableStatement(node);
- elif val == 'while':
- return self.parseWhileStatement(node);
- elif val == 'with':
- return self.parseWithStatement(node);
-
- expr = self.parseExpression();
-
- # 12.12 Labelled Statements
- if ((expr.type == Syntax.Identifier) and self.match(':')):
- self.lex();
-
- key = '$' + expr.name
- if key in self.state['labelSet']:
- self.throwError(Messages.Redeclaration, 'Label', expr.name);
- self.state['labelSet'][key] = true
- labeledBody = self.parseStatement()
- del self.state['labelSet'][key]
- return node.finishLabeledStatement(expr, labeledBody)
- self.consumeSemicolon();
- return node.finishExpressionStatement(expr)
-
- # 13 Function Definition
-
- def parseFunctionSourceElements(self):
- body = []
- node = Node()
- firstRestricted = None
-
- self.expect('{')
-
- while (self.startIndex < self.length):
- if (self.lookahead['type'] != Token.StringLiteral):
- break
- token = self.lookahead;
-
- statement = self.parseStatementListItem()
- body.append(statement)
- if (statement.expression.type != Syntax.Literal):
- # this is not directive
- break
- directive = self.source[token['start'] + 1: token['end'] - 1]
- if (directive == 'use strict'):
- self.strict = true;
- if (firstRestricted):
- self.tolerateUnexpectedToken(firstRestricted, Messages.StrictOctalLiteral);
- else:
- if (not firstRestricted and token.get('octal')):
- firstRestricted = token;
-
- oldLabelSet = self.state['labelSet']
- oldInIteration = self.state['inIteration']
- oldInSwitch = self.state['inSwitch']
- oldInFunctionBody = self.state['inFunctionBody']
- oldParenthesisCount = self.state['parenthesizedCount']
-
- self.state['labelSet'] = {}
- self.state['inIteration'] = false
- self.state['inSwitch'] = false
- self.state['inFunctionBody'] = true
- self.state['parenthesizedCount'] = 0
-
- while (self.startIndex < self.length):
- if (self.match('}')):
- break
- body.append(self.parseStatementListItem())
- self.expect('}')
-
- self.state['labelSet'] = oldLabelSet;
- self.state['inIteration'] = oldInIteration;
- self.state['inSwitch'] = oldInSwitch;
- self.state['inFunctionBody'] = oldInFunctionBody;
- self.state['parenthesizedCount'] = oldParenthesisCount;
-
- return node.finishBlockStatement(body)
-
- def validateParam(self, options, param, name):
- key = '$' + name
- if (self.strict):
- if (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 (isRestrictedWord(name)):
- options['firstRestricted'] = param;
- options['message'] = Messages.StrictParamName;
- elif (isStrictModeReservedWord(name)):
- options['firstRestricted'] = param;
- options['message'] = Messages.StrictReservedWord;
- elif key in options['paramSet']:
- options['firstRestricted'] = param
- options['message'] = Messages.StrictParamDupe;
- options['paramSet'][key] = true
-
- def parseParam(self, options):
- token = self.lookahead
- de = None
- if (token['value'] == '...'):
- param = self.parseRestElement();
- self.validateParam(options, param.argument, param.argument.name);
- options['params'].append(param);
- options['defaults'].append(null);
- return false
- param = self.parsePatternWithDefault();
- self.validateParam(options, token, token['value']);
-
- if (param.type == Syntax.AssignmentPattern):
- de = param.right;
- param = param.left;
- options['defaultCount'] += 1
- options['params'].append(param);
- options['defaults'].append(de)
- return not self.match(')')
-
- def parseParams(self, firstRestricted):
- options = {
- 'params': [],
- 'defaultCount': 0,
- 'defaults': [],
- 'firstRestricted': firstRestricted}
-
- self.expect('(');
-
- if (not self.match(')')):
- options['paramSet'] = {};
- while (self.startIndex < self.length):
- if (not self.parseParam(options)):
- break
- self.expect(',');
- self.expect(')');
-
- if (options['defaultCount'] == 0):
- options['defaults'] = [];
-
- return {
- 'params': options['params'],
- 'defaults': options['defaults'],
- 'stricted': options.get('stricted'),
- 'firstRestricted': options.get('firstRestricted'),
- 'message': options.get('message')}
-
- def parseFunctionDeclaration(self, node, identifierIsOptional=None):
- node.comments = self.lookahead.get('comments', [])
- d = null
- params = []
- defaults = []
- message = None
- firstRestricted = None
-
- self.expectKeyword('function');
- if (identifierIsOptional or not self.match('(')):
- token = self.lookahead;
- d = self.parseVariableIdentifier();
- if (self.strict):
- if (isRestrictedWord(token['value'])):
- self.tolerateUnexpectedToken(token, Messages.StrictFunctionName);
- else:
- if (isRestrictedWord(token['value'])):
- firstRestricted = token;
- message = Messages.StrictFunctionName;
- elif (isStrictModeReservedWord(token['value'])):
- firstRestricted = token;
- message = Messages.StrictReservedWord;
-
- tmp = self.parseParams(firstRestricted);
- params = tmp['params']
- defaults = tmp['defaults']
- stricted = tmp.get('stricted')
- firstRestricted = tmp['firstRestricted']
- if (tmp.get('message')):
- message = tmp['message'];
-
- previousStrict = self.strict;
- body = self.parseFunctionSourceElements();
- if (self.strict and firstRestricted):
- self.throwUnexpectedToken(firstRestricted, message);
-
- if (self.strict and stricted):
- self.tolerateUnexpectedToken(stricted, message);
- self.strict = previousStrict;
-
- return node.finishFunctionDeclaration(d, params, defaults, body);
-
- def parseFunctionExpression(self):
- id = null
- params = []
- defaults = []
- node = Node();
- node.comments = self.lookahead.get('comments', [])
- firstRestricted = None
- message = None
-
- self.expectKeyword('function');
-
- if (not self.match('(')):
- token = self.lookahead;
- id = self.parseVariableIdentifier();
- if (self.strict):
- if (isRestrictedWord(token['value'])):
- self.tolerateUnexpectedToken(token, Messages.StrictFunctionName);
- else:
- if (isRestrictedWord(token['value'])):
- firstRestricted = token;
- message = Messages.StrictFunctionName;
- elif (isStrictModeReservedWord(token['value'])):
- firstRestricted = token;
- message = Messages.StrictReservedWord;
- tmp = self.parseParams(firstRestricted);
- params = tmp['params']
- defaults = tmp['defaults']
- stricted = tmp.get('stricted')
- firstRestricted = tmp['firstRestricted']
- if (tmp.get('message')):
- message = tmp['message']
-
- previousStrict = self.strict;
- body = self.parseFunctionSourceElements();
- if (self.strict and firstRestricted):
- self.throwUnexpectedToken(firstRestricted, message);
- if (self.strict and stricted):
- self.tolerateUnexpectedToken(stricted, message);
- self.strict = previousStrict;
-
- return node.finishFunctionExpression(id, params, defaults, body);
-
- # todo Translate parse class functions!
-
- def parseClassExpression(self):
- raise NotImplementedError()
-
- def parseClassDeclaration(self):
- raise NotImplementedError()
-
- # 14 Program
-
- def parseScriptBody(self):
- body = []
- firstRestricted = None
-
- while (self.startIndex < self.length):
- token = self.lookahead;
- if (token['type'] != Token.StringLiteral):
- break
- statement = self.parseStatementListItem();
- body.append(statement);
- if (statement.expression.type != Syntax.Literal):
- # this is not directive
- break
- directive = self.source[token['start'] + 1: token['end'] - 1]
- if (directive == 'use strict'):
- self.strict = true;
- if (firstRestricted):
- self.tolerateUnexpectedToken(firstRestricted, Messages.StrictOctalLiteral)
- else:
- if (not firstRestricted and token.get('octal')):
- firstRestricted = token;
- while (self.startIndex < self.length):
- statement = self.parseStatementListItem();
- # istanbul ignore if
- if (statement is None):
- break
- body.append(statement);
- return body;
-
- def parseProgram(self):
- self.peek()
- node = Node()
-
- body = self.parseScriptBody()
- return node.finishProgram(body)
-
- # DONE!!!
- def parse(self, code, options={}):
- if options:
- raise NotImplementedError('Options not implemented! You can only use default settings.')
-
- self.clean()
- self.source = str(code) + ' \n ; //END' # I have to add it in order not to check for EOF every time
- self.index = 0
- self.lineNumber = 1 if len(self.source) > 0 else 0
- self.lineStart = 0
- self.startIndex = self.index
- self.startLineNumber = self.lineNumber;
- self.startLineStart = self.lineStart;
- self.length = len(self.source)
- self.lookahead = null;
- self.state = {
- 'allowIn': true,
- 'labelSet': {},
- 'inFunctionBody': false,
- 'inIteration': false,
- 'inSwitch': false,
- 'lastCommentStart': -1,
- 'curlyStack': [],
- 'parenthesizedCount': None}
- self.sourceType = 'script';
- self.strict = false;
- program = self.parseProgram();
- return node_to_dict(program)
-
-
-
-def parse(javascript_code):
- """Returns syntax tree of javascript_code.
- Same as PyJsParser().parse For your convenience :) """
- p = PyJsParser()
- return p.parse(javascript_code)
-
-
-if __name__ == '__main__':
- import time
-
- test_path = None
- if test_path:
- f = open(test_path, 'rb')
- x = f.read()
- f.close()
- else:
- x = 'var $ = "Hello!"'
- p = PyJsParser()
- t = time.time()
- res = p.parse(x)
- dt = time.time() - t + 0.000000001
- if test_path:
- print(len(res))
- else:
- pprint(res)
- print()
- print('Parsed everyting in', round(dt, 5), 'seconds.')
- print('Thats %d characters per second' % int(len(x) / dt))
-
-
-