diff options
author | panni <[email protected]> | 2019-04-11 02:02:14 +0200 |
---|---|---|
committer | panni <[email protected]> | 2019-04-11 02:02:14 +0200 |
commit | 8cd81774552d21d77ef9eef69743f21ca1b8713a (patch) | |
tree | bb0b6e2c34362af2e57aa0578e88c3538d9492b5 /libs/pyjsparser | |
parent | c2a7c1247056a8c307419c578e115085397810e9 (diff) | |
download | bazarr-8cd81774552d21d77ef9eef69743f21ca1b8713a.tar.gz bazarr-8cd81774552d21d77ef9eef69743f21ca1b8713a.zip |
core: update to subliminal_patch:head; replace cfscrape; add dependencies
Diffstat (limited to 'libs/pyjsparser')
-rw-r--r-- | libs/pyjsparser/__init__.py | 4 | ||||
-rw-r--r-- | libs/pyjsparser/parser.py | 2898 | ||||
-rw-r--r-- | libs/pyjsparser/pyjsparserdata.py | 303 | ||||
-rw-r--r-- | libs/pyjsparser/std_nodes.py | 471 |
4 files changed, 3676 insertions, 0 deletions
diff --git a/libs/pyjsparser/__init__.py b/libs/pyjsparser/__init__.py new file mode 100644 index 000000000..2a9de69e9 --- /dev/null +++ b/libs/pyjsparser/__init__.py @@ -0,0 +1,4 @@ +__all__ = ['PyJsParser', 'parse', 'JsSyntaxError'] +__author__ = 'Piotr Dabkowski' +__version__ = '2.2.0' +from .parser import PyJsParser, parse, JsSyntaxError
\ No newline at end of file diff --git a/libs/pyjsparser/parser.py b/libs/pyjsparser/parser.py new file mode 100644 index 000000000..a67869ce8 --- /dev/null +++ b/libs/pyjsparser/parser.py @@ -0,0 +1,2898 @@ +# 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 + +PY3 = sys.version_info >= (3,0) + +if PY3: + basestring = str + long = int + xrange = range + unicode = str + +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 + + def skipMultiLineComment(self): + 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 + self.index += 1 + else: + self.index += 1 + self.tolerateUnexpectedToken() + + def skipComment(self): + self.hasLineTerminator = False + start = (self.index == 0) + 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 + self.skipSingleLineComment(2) + start = True + elif (ch == 0x2A): # U+002A is '*' + self.index += 2 + 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 + + def scanHexEscape(self, prefix): + code = 0 + leng = 4 if (prefix == 'u') else 2 + for i in xrange(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 unichr(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 unichr(code) + cu1 = ((code - 0x10000) >> 10) + 0xD800; + cu2 = ((code - 0x10000) & 1023) + 0xDC00; + return unichr(cu1) + unichr(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 xrange(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 % unichr(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 % unichr(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): + self.scanning = True + self.lookahead = None + 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} + + def collectRegex(self): + self.skipComment(); + return self.scanRegExp() + + def isIdentifierName(self, token): + return token['type'] in (1, 3, 4, 5) + + # def advanceSlash(self): ??? + + 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 + + self.skipComment() + + token = self.lookahead + + self.startIndex = self.index + self.startLineNumber = self.lineNumber + self.startLineStart = self.lineStart + + self.lookahead = self.advance() + self.scanning = False + return token + + def peek(self): + self.scanning = True + + 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.advance() + self.scanning = False + + def createError(self, line, pos, description): + global ENABLE_PYIMPORT + msg = 'Line ' + unicode(line) + ': ' + unicode(description) + if ENABLE_JS2PY_ERRORS: + if isinstance(ENABLE_JS2PY_ERRORS, bool): + import js2py.base + return js2py.base.MakeError('SyntaxError', msg) + else: + return ENABLE_JS2PY_ERRORS(msg) + else: + return JsSyntaxError(msg) + + + # Throw an exception + + def throwError(self, messageFormat, *args): + msg = messageFormat % tuple(unicode(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', unicode(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() + + 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(); + + 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 xrange(len(expr['elements'])): + if (expr['elements'][i] != null): + self.reinterpretExpressionAsPattern(expr['elements'][i]) + elif typ == Syntax.ObjectExpression: + expr['type'] = Syntax.ObjectPattern + for i in xrange(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 xrange(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 xrange(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(); + + 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 xrange(len(param.elements)): + if (param.elements[i] != null): + self.checkPatternParam(options, param.elements[i]); + else: + assert typ == Syntax.ObjectPattern, 'Invalid type' + for i in xrange(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 xrange(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 or expr.type==Syntax.Literal): + 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(); + 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): + 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(); + 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 = unicode(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)) + + + diff --git a/libs/pyjsparser/pyjsparserdata.py b/libs/pyjsparser/pyjsparserdata.py new file mode 100644 index 000000000..1d6ab2ee2 --- /dev/null +++ b/libs/pyjsparser/pyjsparserdata.py @@ -0,0 +1,303 @@ +# 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 + +import sys +import unicodedata +from collections import defaultdict + +PY3 = sys.version_info >= (3,0) + +if PY3: + unichr = chr + xrange = range + unicode = str + +token = { + 'BooleanLiteral': 1, + 'EOF': 2, + 'Identifier': 3, + 'Keyword': 4, + 'NullLiteral': 5, + 'NumericLiteral': 6, + 'Punctuator': 7, + 'StringLiteral': 8, + 'RegularExpression': 9, + 'Template': 10 + } + + +TokenName = dict((v,k) for k,v in token.items()) + +FnExprTokens = ['(', '{', '[', 'in', 'typeof', 'instanceof', 'new', + 'return', 'case', 'delete', 'throw', 'void', + # assignment operators + '=', '+=', '-=', '*=', '/=', '%=', '<<=', '>>=', '>>>=', + '&=', '|=', '^=', ',', + # binary/unary operators + '+', '-', '*', '/', '%', '++', '--', '<<', '>>', '>>>', '&', + '|', '^', '!', '~', '&&', '||', '?', ':', '===', '==', '>=', + '<=', '<', '>', '!=', '!=='] + +syntax= set(('AssignmentExpression', + 'AssignmentPattern', + 'ArrayExpression', + 'ArrayPattern', + 'ArrowFunctionExpression', + 'BlockStatement', + 'BinaryExpression', + 'BreakStatement', + 'CallExpression', + 'CatchClause', + 'ClassBody', + 'ClassDeclaration', + 'ClassExpression', + 'ConditionalExpression', + 'ContinueStatement', + 'DoWhileStatement', + 'DebuggerStatement', + 'EmptyStatement', + 'ExportAllDeclaration', + 'ExportDefaultDeclaration', + 'ExportNamedDeclaration', + 'ExportSpecifier', + 'ExpressionStatement', + 'ForStatement', + 'ForInStatement', + 'FunctionDeclaration', + 'FunctionExpression', + 'Identifier', + 'IfStatement', + 'ImportDeclaration', + 'ImportDefaultSpecifier', + 'ImportNamespaceSpecifier', + 'ImportSpecifier', + 'Literal', + 'LabeledStatement', + 'LogicalExpression', + 'MemberExpression', + 'MethodDefinition', + 'NewExpression', + 'ObjectExpression', + 'ObjectPattern', + 'Program', + 'Property', + 'RestElement', + 'ReturnStatement', + 'SequenceExpression', + 'SpreadElement', + 'Super', + 'SwitchCase', + 'SwitchStatement', + 'TaggedTemplateExpression', + 'TemplateElement', + 'TemplateLiteral', + 'ThisExpression', + 'ThrowStatement', + 'TryStatement', + 'UnaryExpression', + 'UpdateExpression', + 'VariableDeclaration', + 'VariableDeclarator', + 'WhileStatement', + 'WithStatement')) + + +# Error messages should be identical to V8. +messages = { + 'UnexpectedToken': 'Unexpected token %s', + 'UnexpectedNumber': 'Unexpected number', + 'UnexpectedString': 'Unexpected string', + 'UnexpectedIdentifier': 'Unexpected identifier', + 'UnexpectedReserved': 'Unexpected reserved word', + 'UnexpectedTemplate': 'Unexpected quasi %s', + 'UnexpectedEOS': 'Unexpected end of input', + 'NewlineAfterThrow': 'Illegal newline after throw', + 'InvalidRegExp': 'Invalid regular expression', + 'UnterminatedRegExp': 'Invalid regular expression: missing /', + 'InvalidLHSInAssignment': 'Invalid left-hand side in assignment', + 'InvalidLHSInForIn': 'Invalid left-hand side in for-in', + 'MultipleDefaultsInSwitch': 'More than one default clause in switch statement', + 'NoCatchOrFinally': 'Missing catch or finally after try', + 'UnknownLabel': 'Undefined label \'%s\'', + 'Redeclaration': '%s \'%s\' has already been declared', + 'IllegalContinue': 'Illegal continue statement', + 'IllegalBreak': 'Illegal break statement', + 'IllegalReturn': 'Illegal return statement', + 'StrictModeWith': 'Strict mode code may not include a with statement', + 'StrictCatchVariable': 'Catch variable may not be eval or arguments in strict mode', + 'StrictVarName': 'Variable name may not be eval or arguments in strict mode', + 'StrictParamName': 'Parameter name eval or arguments is not allowed in strict mode', + 'StrictParamDupe': 'Strict mode function may not have duplicate parameter names', + 'StrictFunctionName': 'Function name may not be eval or arguments in strict mode', + 'StrictOctalLiteral': 'Octal literals are not allowed in strict mode.', + 'StrictDelete': 'Delete of an unqualified identifier in strict mode.', + 'StrictLHSAssignment': 'Assignment to eval or arguments is not allowed in strict mode', + 'StrictLHSPostfix': 'Postfix increment/decrement may not have eval or arguments operand in strict mode', + 'StrictLHSPrefix': 'Prefix increment/decrement may not have eval or arguments operand in strict mode', + 'StrictReservedWord': 'Use of future reserved word in strict mode', + 'TemplateOctalLiteral': 'Octal literals are not allowed in template strings.', + 'ParameterAfterRestParameter': 'Rest parameter must be last formal parameter', + 'DefaultRestParameter': 'Unexpected token =', + 'ObjectPatternAsRestParameter': 'Unexpected token {', + 'DuplicateProtoProperty': 'Duplicate __proto__ fields are not allowed in object literals', + 'ConstructorSpecialMethod': 'Class constructor may not be an accessor', + 'DuplicateConstructor': 'A class may only have one constructor', + 'StaticPrototype': 'Classes may not have static property named prototype', + 'MissingFromClause': 'Unexpected token', + 'NoAsAfterImportNamespace': 'Unexpected token', + 'InvalidModuleSpecifier': 'Unexpected token', + 'IllegalImportDeclaration': 'Unexpected token', + 'IllegalExportDeclaration': 'Unexpected token'} + +PRECEDENCE = {'||':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} + +class Token: pass +class Syntax: pass +class Messages: pass +class PlaceHolders: + ArrowParameterPlaceHolder = 'ArrowParameterPlaceHolder' + +for k,v in token.items(): + setattr(Token, k, v) + +for e in syntax: + setattr(Syntax, e, e) + +for k,v in messages.items(): + setattr(Messages, k, v) + +#http://stackoverflow.com/questions/14245893/efficiently-list-all-characters-in-a-given-unicode-category +BOM = u'\uFEFF' +ZWJ = u'\u200D' +ZWNJ = u'\u200C' +TAB = u'\u0009' +VT = u'\u000B' +FF = u'\u000C' +SP = u'\u0020' +NBSP = u'\u00A0' +LF = u'\u000A' +CR = u'\u000D' +LS = u'\u2028' +PS = u'\u2029' + +U_CATEGORIES = defaultdict(list) +for c in map(unichr, range(sys.maxunicode + 1)): + U_CATEGORIES[unicodedata.category(c)].append(c) +UNICODE_LETTER = set(U_CATEGORIES['Lu']+U_CATEGORIES['Ll']+ + U_CATEGORIES['Lt']+U_CATEGORIES['Lm']+ + U_CATEGORIES['Lo']+U_CATEGORIES['Nl']) +UNICODE_COMBINING_MARK = set(U_CATEGORIES['Mn']+U_CATEGORIES['Mc']) +UNICODE_DIGIT = set(U_CATEGORIES['Nd']) +UNICODE_CONNECTOR_PUNCTUATION = set(U_CATEGORIES['Pc']) +IDENTIFIER_START = UNICODE_LETTER.union(set(('$','_', '\\'))) # and some fucking unicode escape sequence +IDENTIFIER_PART = IDENTIFIER_START.union(UNICODE_COMBINING_MARK).union(UNICODE_DIGIT)\ + .union(UNICODE_CONNECTOR_PUNCTUATION).union(set((ZWJ, ZWNJ))) + +WHITE_SPACE = set((0x20, 0x09, 0x0B, 0x0C, 0xA0, 0x1680, + 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, + 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, + 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, + 0xFEFF)) + +LINE_TERMINATORS = set((0x0A, 0x0D, 0x2028, 0x2029)) + +def isIdentifierStart(ch): + return (ch if isinstance(ch, unicode) else unichr(ch)) in IDENTIFIER_START + +def isIdentifierPart(ch): + return (ch if isinstance(ch, unicode) else unichr(ch)) in IDENTIFIER_PART + +def isWhiteSpace(ch): + return (ord(ch) if isinstance(ch, unicode) else ch) in WHITE_SPACE + +def isLineTerminator(ch): + return (ord(ch) if isinstance(ch, unicode) else ch) in LINE_TERMINATORS + +OCTAL = set(('0', '1', '2', '3', '4', '5', '6', '7')) +DEC = set(('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')) +HEX = set('0123456789abcdefABCDEF') +HEX_CONV = dict(('0123456789abcdef'[n],n) for n in xrange(16)) +for i,e in enumerate('ABCDEF', 10): + HEX_CONV[e] = i + + +def isDecimalDigit(ch): + return (ch if isinstance(ch, unicode) else unichr(ch)) in DEC + +def isHexDigit(ch): + return (ch if isinstance(ch, unicode) else unichr(ch)) in HEX + +def isOctalDigit(ch): + return (ch if isinstance(ch, unicode) else unichr(ch)) in OCTAL + +def isFutureReservedWord(w): + return w in ('enum', 'export', 'import', 'super') + + +RESERVED_WORD = set(('implements', 'interface', 'package', 'private', 'protected', 'public', 'static', 'yield', 'let')) +def isStrictModeReservedWord(w): + return w in RESERVED_WORD + +def isRestrictedWord(w): + return w in ('eval', 'arguments') + + +KEYWORDS = set(('if', 'in', 'do', 'var', 'for', 'new', 'try', 'let', 'this', 'else', 'case', + 'void', 'with', 'enum', 'while', 'break', 'catch', 'throw', 'const', 'yield', + 'class', 'super', 'return', 'typeof', 'delete', 'switch', 'export', 'import', + 'default', 'finally', 'extends', 'function', 'continue', 'debugger', 'instanceof', 'pyimport')) +def isKeyword(w): + # 'const' is specialized as Keyword in V8. + # 'yield' and 'let' are for compatibility with SpiderMonkey and ES.next. + # Some others are from future reserved words. + return w in KEYWORDS + + +class JsSyntaxError(Exception): pass + +if __name__=='__main__': + assert isLineTerminator('\n') + assert isLineTerminator(0x0A) + assert isIdentifierStart('$') + assert isIdentifierStart(100) + assert isWhiteSpace(' ')
\ No newline at end of file diff --git a/libs/pyjsparser/std_nodes.py b/libs/pyjsparser/std_nodes.py new file mode 100644 index 000000000..a4b37ca3d --- /dev/null +++ b/libs/pyjsparser/std_nodes.py @@ -0,0 +1,471 @@ +from .pyjsparserdata import * + + +class BaseNode: + def finish(self): + pass + + def finishArrayExpression(self, elements): + self.type = Syntax.ArrayExpression + self.elements = elements + self.finish() + return self + + def finishArrayPattern(self, elements): + self.type = Syntax.ArrayPattern + self.elements = elements + self.finish() + return self + + def finishArrowFunctionExpression(self, params, defaults, body, expression): + self.type = Syntax.ArrowFunctionExpression + self.id = None + self.params = params + self.defaults = defaults + self.body = body + self.generator = False + self.expression = expression + self.finish() + return self + + def finishAssignmentExpression(self, operator, left, right): + self.type = Syntax.AssignmentExpression + self.operator = operator + self.left = left + self.right = right + self.finish() + return self + + def finishAssignmentPattern(self, left, right): + self.type = Syntax.AssignmentPattern + self.left = left + self.right = right + self.finish() + return self + + def finishBinaryExpression(self, operator, left, right): + self.type = Syntax.LogicalExpression if (operator == '||' or operator == '&&') else Syntax.BinaryExpression + self.operator = operator + self.left = left + self.right = right + self.finish() + return self + + def finishBlockStatement(self, body): + self.type = Syntax.BlockStatement + self.body = body + self.finish() + return self + + def finishBreakStatement(self, label): + self.type = Syntax.BreakStatement + self.label = label + self.finish() + return self + + def finishCallExpression(self, callee, args): + self.type = Syntax.CallExpression + self.callee = callee + self.arguments = args + self.finish() + return self + + def finishCatchClause(self, param, body): + self.type = Syntax.CatchClause + self.param = param + self.body = body + self.finish() + return self + + def finishClassBody(self, body): + self.type = Syntax.ClassBody + self.body = body + self.finish() + return self + + def finishClassDeclaration(self, id, superClass, body): + self.type = Syntax.ClassDeclaration + self.id = id + self.superClass = superClass + self.body = body + self.finish() + return self + + def finishClassExpression(self, id, superClass, body): + self.type = Syntax.ClassExpression + self.id = id + self.superClass = superClass + self.body = body + self.finish() + return self + + def finishConditionalExpression(self, test, consequent, alternate): + self.type = Syntax.ConditionalExpression + self.test = test + self.consequent = consequent + self.alternate = alternate + self.finish() + return self + + def finishContinueStatement(self, label): + self.type = Syntax.ContinueStatement + self.label = label + self.finish() + return self + + def finishDebuggerStatement(self, ): + self.type = Syntax.DebuggerStatement + self.finish() + return self + + def finishDoWhileStatement(self, body, test): + self.type = Syntax.DoWhileStatement + self.body = body + self.test = test + self.finish() + return self + + def finishEmptyStatement(self, ): + self.type = Syntax.EmptyStatement + self.finish() + return self + + def finishExpressionStatement(self, expression): + self.type = Syntax.ExpressionStatement + self.expression = expression + self.finish() + return self + + def finishForStatement(self, init, test, update, body): + self.type = Syntax.ForStatement + self.init = init + self.test = test + self.update = update + self.body = body + self.finish() + return self + + def finishForInStatement(self, left, right, body): + self.type = Syntax.ForInStatement + self.left = left + self.right = right + self.body = body + self.each = False + self.finish() + return self + + def finishFunctionDeclaration(self, id, params, defaults, body): + self.type = Syntax.FunctionDeclaration + self.id = id + self.params = params + self.defaults = defaults + self.body = body + self.generator = False + self.expression = False + self.finish() + return self + + def finishFunctionExpression(self, id, params, defaults, body): + self.type = Syntax.FunctionExpression + self.id = id + self.params = params + self.defaults = defaults + self.body = body + self.generator = False + self.expression = False + self.finish() + return self + + def finishIdentifier(self, name): + self.type = Syntax.Identifier + self.name = name + self.finish() + return self + + def finishIfStatement(self, test, consequent, alternate): + self.type = Syntax.IfStatement + self.test = test + self.consequent = consequent + self.alternate = alternate + self.finish() + return self + + def finishLabeledStatement(self, label, body): + self.type = Syntax.LabeledStatement + self.label = label + self.body = body + self.finish() + return self + + def finishLiteral(self, token): + self.type = Syntax.Literal + self.value = token['value'] + self.raw = None # todo fix it? + if token.get('regex'): + self.regex = token['regex'] + self.finish() + return self + + def finishMemberExpression(self, accessor, object, property): + self.type = Syntax.MemberExpression + self.computed = accessor == '[' + self.object = object + self.property = property + self.finish() + return self + + def finishNewExpression(self, callee, args): + self.type = Syntax.NewExpression + self.callee = callee + self.arguments = args + self.finish() + return self + + def finishObjectExpression(self, properties): + self.type = Syntax.ObjectExpression + self.properties = properties + self.finish() + return self + + def finishObjectPattern(self, properties): + self.type = Syntax.ObjectPattern + self.properties = properties + self.finish() + return self + + def finishPostfixExpression(self, operator, argument): + self.type = Syntax.UpdateExpression + self.operator = operator + self.argument = argument + self.prefix = False + self.finish() + return self + + def finishProgram(self, body): + self.type = Syntax.Program + self.body = body + self.finish() + return self + + def finishPyimport(self, imp): + self.type = 'PyimportStatement' + self.imp = imp + self.finish() + return self + + def finishProperty(self, kind, key, computed, value, method, shorthand): + self.type = Syntax.Property + self.key = key + self.computed = computed + self.value = value + self.kind = kind + self.method = method + self.shorthand = shorthand + self.finish() + return self + + def finishRestElement(self, argument): + self.type = Syntax.RestElement + self.argument = argument + self.finish() + return self + + def finishReturnStatement(self, argument): + self.type = Syntax.ReturnStatement + self.argument = argument + self.finish() + return self + + def finishSequenceExpression(self, expressions): + self.type = Syntax.SequenceExpression + self.expressions = expressions + self.finish() + return self + + def finishSpreadElement(self, argument): + self.type = Syntax.SpreadElement + self.argument = argument + self.finish() + return self + + def finishSwitchCase(self, test, consequent): + self.type = Syntax.SwitchCase + self.test = test + self.consequent = consequent + self.finish() + return self + + def finishSuper(self, ): + self.type = Syntax.Super + self.finish() + return self + + def finishSwitchStatement(self, discriminant, cases): + self.type = Syntax.SwitchStatement + self.discriminant = discriminant + self.cases = cases + self.finish() + return self + + def finishTaggedTemplateExpression(self, tag, quasi): + self.type = Syntax.TaggedTemplateExpression + self.tag = tag + self.quasi = quasi + self.finish() + return self + + def finishTemplateElement(self, value, tail): + self.type = Syntax.TemplateElement + self.value = value + self.tail = tail + self.finish() + return self + + def finishTemplateLiteral(self, quasis, expressions): + self.type = Syntax.TemplateLiteral + self.quasis = quasis + self.expressions = expressions + self.finish() + return self + + def finishThisExpression(self, ): + self.type = Syntax.ThisExpression + self.finish() + return self + + def finishThrowStatement(self, argument): + self.type = Syntax.ThrowStatement + self.argument = argument + self.finish() + return self + + def finishTryStatement(self, block, handler, finalizer): + self.type = Syntax.TryStatement + self.block = block + self.guardedHandlers = [] + self.handlers = [handler] if handler else [] + self.handler = handler + self.finalizer = finalizer + self.finish() + return self + + def finishUnaryExpression(self, operator, argument): + self.type = Syntax.UpdateExpression if (operator == '++' or operator == '--') else Syntax.UnaryExpression + self.operator = operator + self.argument = argument + self.prefix = True + self.finish() + return self + + def finishVariableDeclaration(self, declarations): + self.type = Syntax.VariableDeclaration + self.declarations = declarations + self.kind = 'var' + self.finish() + return self + + def finishLexicalDeclaration(self, declarations, kind): + self.type = Syntax.VariableDeclaration + self.declarations = declarations + self.kind = kind + self.finish() + return self + + def finishVariableDeclarator(self, id, init): + self.type = Syntax.VariableDeclarator + self.id = id + self.init = init + self.finish() + return self + + def finishWhileStatement(self, test, body): + self.type = Syntax.WhileStatement + self.test = test + self.body = body + self.finish() + return self + + def finishWithStatement(self, object, body): + self.type = Syntax.WithStatement + self.object = object + self.body = body + self.finish() + return self + + def finishExportSpecifier(self, local, exported): + self.type = Syntax.ExportSpecifier + self.exported = exported or local + self.local = local + self.finish() + return self + + def finishImportDefaultSpecifier(self, local): + self.type = Syntax.ImportDefaultSpecifier + self.local = local + self.finish() + return self + + def finishImportNamespaceSpecifier(self, local): + self.type = Syntax.ImportNamespaceSpecifier + self.local = local + self.finish() + return self + + def finishExportNamedDeclaration(self, declaration, specifiers, src): + self.type = Syntax.ExportNamedDeclaration + self.declaration = declaration + self.specifiers = specifiers + self.source = src + self.finish() + return self + + def finishExportDefaultDeclaration(self, declaration): + self.type = Syntax.ExportDefaultDeclaration + self.declaration = declaration + self.finish() + return self + + def finishExportAllDeclaration(self, src): + self.type = Syntax.ExportAllDeclaration + self.source = src + self.finish() + return self + + def finishImportSpecifier(self, local, imported): + self.type = Syntax.ImportSpecifier + self.local = local or imported + self.imported = imported + self.finish() + return self + + def finishImportDeclaration(self, specifiers, src): + self.type = Syntax.ImportDeclaration + self.specifiers = specifiers + self.source = src + self.finish() + return self + + def __getitem__(self, item): + return getattr(self, item) + + def __setitem__(self, key, value): + setattr(self, key, value) + + +class Node(BaseNode): + pass + + +class WrappingNode(BaseNode): + def __init__(self, startToken=None): + pass + + +def node_to_dict(node): # extremely important for translation speed + if isinstance(node, list): + return [node_to_dict(e) for e in node] + elif isinstance(node, dict): + return dict((k, node_to_dict(v)) for k, v in node.items()) + elif not isinstance(node, BaseNode): + return node + return dict((k, node_to_dict(v)) for k, v in node.__dict__.items()) |