fix(userdata): Dropped sloppy JSOn parser in favour of a true JavaScript AST analyzer
authorSebastian Golasch <public@asciidisco.com>
Mon, 27 Feb 2017 12:54:06 +0000 (13:54 +0100)
committerSebastian Golasch <public@asciidisco.com>
Mon, 27 Feb 2017 12:54:06 +0000 (13:54 +0100)
resources/lib/NetflixSession.py
resources/lib/pyjsparser/__init__.py [new file with mode: 0644]
resources/lib/pyjsparser/parser.py [new file with mode: 0644]
resources/lib/pyjsparser/pyjsparserdata.py [new file with mode: 0644]
resources/lib/pyjsparser/std_nodes.py [new file with mode: 0644]
resources/lib/utils.py

index d1169117c2536c3e2e53623ff6ee731a87fc7395..46f2f2de5e6652f1e95dfea803d0e609073bad3a 100644 (file)
@@ -15,7 +15,7 @@ try:
 except:
    import pickle
 from bs4 import BeautifulSoup
-from utils import strip_tags
+from pyjsparser import PyJsParser
 from utils import noop
 
 class NetflixSession:
@@ -145,53 +145,57 @@ class NetflixSession:
     def extract_inline_netflix_page_data (self, page_soup):
         """Extracts all <script/> tags from the given document and parses the contents of each one of `em.
         The contents of the parsable tags looks something like this:
-
-            <script>window.netflix = window.netflix || {} ;
-                    netflix.notification = {"constants":{"sessionLength":30,"ownerToken":"ZDD...};</script>
-
-        So we´re extracting every JavaScript object contained in the `netflix.x = {};` variable,
-        strip all html tags, unescape the whole thing & finally parse the resulting serialized JSON from this
-        operations. Errors are expected, as not all <script/> tags contained in the page follow these pattern,
-        but the ones we need do, so we´re just catching any errors and applying a noop() function in case this happens,
-        as we´re not interested in those.
-
-        Note: Yes this is ugly & I´d like to avoid doing this, but Netflix leaves us no other choice,
-            as there are simply no api endpoints for the data, we need to extract them from HTML,
-            or better, JavaScript as we´re parsing the contents of <script/> tags
+            <script>window.netflix = window.netflix || {} ; netflix.notification = {"constants":{"sessionLength":30,"ownerToken":"ZDD...};</script>
+        We use a JS parser to generate an AST of the code given & then parse that AST into a python dict.
+        This should be okay, as we´re only interested in a few static values & put the rest aside
 
         Parameters
         ----------
         page_soup : :obj:`BeautifulSoup`
             Instance of an BeautifulSoup document or node containing the complete page contents
-
         Returns
         -------
             :obj:`list` of :obj:`dict`
                 List of all the serialized data pulled out of the pagws <script/> tags
         """
         inline_data = [];
+        parser = PyJsParser()
         data_scripts = page_soup.find_all('script', attrs={'src': None});
         for script in data_scripts:
-            # ugly part: try to parse the data & don't care about errors (as they will be some)
-            try:
-                # find the first occurance of the 'netflix.' string, assigning the contents to a global js var
-                str_index = str(script).find('netflix.')
-                # filter out the contents between the 'netflix.x =' & ';<script>'
-                stripped_data = str(script)[str_index:][(str(script)[str_index:].find('= ') + 2):].replace(';</script>', '').strip()
-                # unescape the contents as they contain characters a JSON parser chokes up upon
-                unescaped_data = stripped_data.decode('string_escape')
-                # strip all the HTML tags within the strings a JSON parser chokes up upon them
-                transformed_data = strip_tags(unescaped_data)
-                # parse the contents with a regular JSON parser, as they should be in a shape that ot actually works
-                try:
-                    parsed_data = json.loads(transformed_data)
-                    inline_data.append(parsed_data)
-                except ValueError, e:
-                    noop()
-            except TypeError, e:
-                noop()
-
-        return inline_data;
+            data = {};
+            # unicode escape that incoming script stuff
+            contents = self._to_unicode(str(script.contents[0]))
+            # parse the JS & load the declarations we´re interested in
+            declarations = parser.parse(contents)['body'][1]['expression']['right']['properties'];
+            for declaration in declarations:
+                for key in declaration:
+                    # we found the correct path if the declaration is a dict & of type 'ObjectExpression'
+                    if type(declaration[key]) is dict:
+                        if declaration[key]['type'] == 'ObjectExpression':
+                            # add all static data recursivly
+                            for expression in declaration[key]['properties']:
+                                data[expression['key']['value']] = self._parse_rec(expression['value'])
+                inline_data.append(data)
+        return inline_data
+
+    def _parse_rec (self, node):
+        """Iterates over a JavaScript AST and retu values found
+        Parameters
+        ----------
+        value : :obj:`dict`
+            JS AST Expression
+        Returns
+        -------
+        :obj:`dict` of :obj:`dict` or :obj:`str`
+            Parsed contents of the node
+        """
+        if node['type'] == 'ObjectExpression':
+            _ret = {}
+            for prop in node['properties']:
+                _ret.update({prop['key']['value']: self._parse_rec(prop['value'])})
+            return _ret
+        if node['type'] == 'Literal':
+            return node['value']
 
     def _parse_user_data (self, netflix_page_data):
         """Parse out the user data from the big chunk of dicts we got from
@@ -236,9 +240,12 @@ class NetflixSession:
             'pinEnabled'
         ]
         for item in netflix_page_data:
-            if 'models' in dict(item).keys():
+            if 'memberContext' in dict(item).keys():
                 for important_field in important_fields:
-                    user_data.update({important_field: item['models']['userInfo']['data'][important_field]})
+                    user_data.update({important_field: item['memberContext']['data']['userInfo'][important_field]})
+        print '.............'
+        print user_data
+        print '.............'
         return user_data
 
     def _parse_profile_data (self, netflix_page_data):
@@ -275,17 +282,19 @@ class NetflixSession:
         ]
         # TODO: get rid of this christmas tree of doom
         for item in netflix_page_data:
-            if 'profiles' in dict(item).keys():
-                for profile_id in item['profiles']:
-                    if self._is_size_key(key=profile_id) == False:
+            if 'hasViewedRatingWelcomeModal' in dict(item).keys():
+                for profile_id in item:
+                    print '------------'
+                    print profile_id
+                    print '------------'
+                    if self._is_size_key(key=profile_id) == False and type(item[profile_id]) == dict and item[profile_id].get('avatar', False) != False:
                         profile = {'id': profile_id}
                         for important_field in important_fields:
-                            profile.update({important_field: item['profiles'][profile_id]['summary'][important_field]})
-                        avatar_base = item['avatars']['nf'].get(item['profiles'][profile_id]['summary']['avatarName'], False);
+                            profile.update({important_field: item[profile_id]['summary'][important_field]})
+                        avatar_base = item['nf'].get(item[profile_id]['summary']['avatarName'], False);
                         avatar = 'https://secure.netflix.com/ffe/profiles/avatars_v2/320x320/PICON_029.png' if avatar_base == False else avatar_base['images']['byWidth']['320']['value']
                         profile.update({'avatar': avatar})
                         profiles.update({profile_id: profile})
-
         return profiles
 
     def _parse_api_base_data (self, netflix_page_data):
@@ -317,9 +326,9 @@ class NetflixSession:
             'ICHNAEA_ROOT'
         ]
         for item in netflix_page_data:
-            if 'models' in dict(item).keys():
+            if 'serverDefs' in dict(item).keys():
                 for important_field in important_fields:
-                    api_data.update({important_field: item['models']['serverDefs']['data'][important_field]})
+                    api_data.update({important_field: item['serverDefs']['data'][important_field]})
         return api_data
 
     def _parse_esn_data (self, netflix_page_data):
@@ -339,8 +348,8 @@ class NetflixSession:
         """
         esn = '';
         for item in netflix_page_data:
-            if 'models' in dict(item).keys():
-                esn = item['models']['esnGeneratorModel']['data']['esn']
+            if 'esnGeneratorModel' in dict(item).keys():
+                esn = item['esnGeneratorModel']['data']['esn']
         return esn
 
     def _parse_page_contents (self, page_soup):
@@ -357,6 +366,11 @@ class NetflixSession:
         self.esn = self._parse_esn_data(netflix_page_data=netflix_page_data)
         self.api_data = self._parse_api_base_data(netflix_page_data=netflix_page_data)
         self.profiles = self._parse_profile_data(netflix_page_data=netflix_page_data)
+        if self.user_data.get('bauthURL', False) == False:
+            print '...............'
+            print page_soup.text.find('authURL');
+            print '...............'
+
 
     def is_logged_in (self, account):
         """Determines if a user is already logged in (with a valid cookie),
@@ -1850,6 +1864,34 @@ class NetflixSession:
         # return the parsed response & everything´s fine
         return response.json()
 
+    def _to_unicode(self, str):
+        '''Attempt to fix non uft-8 string into utf-8, using a limited set of encodings
+
+        Parameters
+        ----------
+        str : `str`
+            String to decode
+
+        Returns
+        -------
+        `str`
+            Decoded string
+        '''
+        # fuller list of encodings at http://docs.python.org/library/codecs.html#standard-encodings
+        if not str:  return u''
+        u = None
+        # we could add more encodings here, as warranted.
+        encodings = ('ascii', 'utf8', 'latin1')
+        for enc in encodings:
+            if u:  break
+            try:
+                u = unicode(str,enc)
+            except UnicodeDecodeError:
+                pass
+        if not u:
+            u = unicode(str, errors='replace')
+        return u
+
     def _update_my_list (self, video_id, operation):
         """Tiny helper to add & remove items from "my list"
 
diff --git a/resources/lib/pyjsparser/__init__.py b/resources/lib/pyjsparser/__init__.py
new file mode 100644 (file)
index 0000000..07eae8f
--- /dev/null
@@ -0,0 +1,4 @@
+__all__ = ['PyJsParser', 'parse', 'JsSyntaxError']
+__author__ = 'Piotr Dabkowski'
+__version__ = '2.2.0'
+from .parser import PyJsParser, parse, JsSyntaxError
diff --git a/resources/lib/pyjsparser/parser.py b/resources/lib/pyjsparser/parser.py
new file mode 100644 (file)
index 0000000..b483a97
--- /dev/null
@@ -0,0 +1,2902 @@
+# 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 (i == '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
+        if ENABLE_JS2PY_ERRORS:
+            old_pyimport = ENABLE_PYIMPORT  # ENABLE_PYIMPORT will be affected by js2py import
+            self.log_err_case()
+            try:
+                from js2py.base import ERRORS, Js, JsToPyException
+            except:
+                raise Exception("ENABLE_JS2PY_ERRORS was set to True, but Js2Py was not found!")
+            ENABLE_PYIMPORT = old_pyimport
+            error = ERRORS['SyntaxError']('Line ' + unicode(line) + ': ' + unicode(description))
+            error.put('index', Js(pos))
+            error.put('lineNumber', Js(line))
+            error.put('column', Js(pos - (self.lineStart if self.scanning else self.lastLineStart) + 1))
+            error.put('description', Js(description))
+            return JsToPyException(error)
+        else:
+            return JsSyntaxError('Line ' + unicode(line) + ': ' + unicode(description))
+
+
+    # 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['stricted']):
+            self.tolerateUnexpectedToken(paramInfo['stricted'], paramInfo.get('message'));
+
+        self.strict = previousStrict;
+        return node.finishFunctionExpression(null, paramInfo['params'], paramInfo['defaults'], body)
+
+    def parsePropertyMethodFunction(self):
+        node = Node();
+
+        params = self.parseParams();
+        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['stricted'] if self.strict else options['firstRestricted']
+            self.throwUnexpectedToken(token, options.get('message'));
+        if (defaultCount == 0):
+            defaults = []
+        return {
+            'params': params,
+            'defaults': defaults,
+            'stricted': options['stricted'],
+            'firstRestricted': options['firstRestricted'],
+            'message': options.get('message')}
+
+    def parseArrowFunctionExpression(self, options, node):
+        if (self.hasLineTerminator):
+            self.tolerateUnexpectedToken(self.lookahead)
+        self.expect('=>')
+        previousStrict = self.strict;
+
+        body = self.parseConciseBody();
+
+        if (self.strict and options['firstRestricted']):
+            self.throwUnexpectedToken(options['firstRestricted'], options.get('message'));
+        if (self.strict and options['stricted']):
+            self.tolerateUnexpectedToken(options['stricted'], options['message']);
+
+        self.strict = previousStrict
+
+        return node.finishArrowFunctionExpression(options['params'], options['defaults'], body,
+                                                  body.type != Syntax.BlockStatement)
+
+    # 11.13 Assignment Operators
+
+    def parseAssignmentExpression(self):
+        startToken = self.lookahead;
+        token = self.lookahead;
+
+        expr = self.parseConditionalExpression();
+
+        if (expr.type == PlaceHolders.ArrowParameterPlaceHolder or self.match('=>')):
+            self.isAssignmentTarget = self.isBindingElement = false;
+            lis = self.reinterpretAsCoverFormalsList(expr)
+
+            if (lis):
+                self.firstCoverInitializedNameError = null;
+                return self.parseArrowFunctionExpression(lis, WrappingNode(startToken))
+            return expr
+
+        if (self.matchAssign()):
+            if (not self.isAssignmentTarget):
+                self.tolerateError(Messages.InvalidLHSInAssignment)
+            # 11.13.1
+
+            if (self.strict and expr.type == Syntax.Identifier and isRestrictedWord(expr.name)):
+                self.tolerateUnexpectedToken(token, Messages.StrictLHSAssignment);
+            if (not self.match('=')):
+                self.isAssignmentTarget = self.isBindingElement = false;
+            else:
+                self.reinterpretExpressionAsPattern(expr)
+            token = self.lex();
+            right = self.isolateCoverGrammar(self.parseAssignmentExpression)
+            expr = WrappingNode(startToken).finishAssignmentExpression(token['value'], expr, right);
+            self.firstCoverInitializedNameError = null
+        return expr
+
+    # 11.14 Comma Operator
+
+    def parseExpression(self):
+        startToken = self.lookahead
+        expr = self.isolateCoverGrammar(self.parseAssignmentExpression)
+
+        if (self.match(',')):
+            expressions = [expr];
+
+            while (self.startIndex < self.length):
+                if (not self.match(',')):
+                    break
+                self.lex();
+                expressions.append(self.isolateCoverGrammar(self.parseAssignmentExpression))
+            expr = WrappingNode(startToken).finishSequenceExpression(expressions);
+        return expr
+
+    # 12.1 Block
+
+    def parseStatementListItem(self):
+        if (self.lookahead['type'] == Token.Keyword):
+            val = (self.lookahead['value'])
+            if val == 'export':
+                if (self.sourceType != 'module'):
+                    self.tolerateUnexpectedToken(self.lookahead, Messages.IllegalExportDeclaration)
+                return self.parseExportDeclaration();
+            elif val == 'import':
+                if (self.sourceType != 'module'):
+                    self.tolerateUnexpectedToken(self.lookahead, Messages.IllegalImportDeclaration);
+                return self.parseImportDeclaration();
+            elif val == 'const' or val == 'let':
+                return self.parseLexicalDeclaration({'inFor': false});
+            elif val == 'function':
+                return self.parseFunctionDeclaration(Node());
+            elif val == 'class':
+                return self.parseClassDeclaration();
+            elif ENABLE_PYIMPORT and val == 'pyimport':  # <<<<< MODIFIED HERE
+                return self.parsePyimportStatement()
+        return self.parseStatement();
+
+    def parsePyimportStatement(self):
+        print(ENABLE_PYIMPORT)
+        assert ENABLE_PYIMPORT
+        n = Node()
+        self.lex()
+        n.finishPyimport(self.parseVariableIdentifier())
+        self.consumeSemicolon()
+        return n
+
+    def parseStatementList(self):
+        list = [];
+        while (self.startIndex < self.length):
+            if (self.match('}')):
+                break
+            list.append(self.parseStatementListItem())
+        return list
+
+    def parseBlock(self):
+        node = Node();
+
+        self.expect('{');
+
+        block = self.parseStatementList()
+
+        self.expect('}');
+
+        return node.finishBlockStatement(block);
+
+    # 12.2 Variable Statement
+
+    def parseVariableIdentifier(self):
+        node = Node()
+
+        token = self.lex()
+
+        if (token['type'] != Token.Identifier):
+            if (self.strict and token['type'] == Token.Keyword and isStrictModeReservedWord(token['value'])):
+                self.tolerateUnexpectedToken(token, Messages.StrictReservedWord);
+            else:
+                self.throwUnexpectedToken(token)
+        return node.finishIdentifier(token['value'])
+
+    def parseVariableDeclaration(self):
+        init = null
+        node = Node();
+
+        d = self.parsePattern();
+
+        # 12.2.1
+        if (self.strict and isRestrictedWord(d.name)):
+            self.tolerateError(Messages.StrictVarName);
+
+        if (self.match('=')):
+            self.lex();
+            init = self.isolateCoverGrammar(self.parseAssignmentExpression);
+        elif (d.type != Syntax.Identifier):
+            self.expect('=')
+        return node.finishVariableDeclarator(d, init)
+
+    def parseVariableDeclarationList(self):
+        lis = []
+
+        while True:
+            lis.append(self.parseVariableDeclaration())
+            if (not self.match(',')):
+                break
+            self.lex();
+            if not (self.startIndex < self.length):
+                break
+
+        return lis;
+
+    def parseVariableStatement(self, node):
+        self.expectKeyword('var')
+
+        declarations = self.parseVariableDeclarationList()
+
+        self.consumeSemicolon()
+
+        return node.finishVariableDeclaration(declarations)
+
+    def parseLexicalBinding(self, kind, options):
+        init = null
+        node = Node()
+
+        d = self.parsePattern();
+
+        # 12.2.1
+        if (self.strict and d.type == Syntax.Identifier and isRestrictedWord(d.name)):
+            self.tolerateError(Messages.StrictVarName);
+
+        if (kind == 'const'):
+            if (not self.matchKeyword('in')):
+                self.expect('=')
+                init = self.isolateCoverGrammar(self.parseAssignmentExpression)
+        elif ((not options['inFor'] and d.type != Syntax.Identifier) or self.match('=')):
+            self.expect('=');
+            init = self.isolateCoverGrammar(self.parseAssignmentExpression);
+        return node.finishVariableDeclarator(d, init)
+
+    def parseBindingList(self, kind, options):
+        list = [];
+
+        while True:
+            list.append(self.parseLexicalBinding(kind, options));
+            if (not self.match(',')):
+                break
+            self.lex();
+            if not (self.startIndex < self.length):
+                break
+        return list;
+
+    def parseLexicalDeclaration(self, options):
+        node = Node();
+
+        kind = self.lex()['value']
+        assert kind == 'let' or kind == 'const', 'Lexical declaration must be either let or const'
+        declarations = self.parseBindingList(kind, options);
+        self.consumeSemicolon();
+        return node.finishLexicalDeclaration(declarations, kind);
+
+    def parseRestElement(self):
+        node = Node();
+
+        self.lex();
+
+        if (self.match('{')):
+            self.throwError(Messages.ObjectPatternAsRestParameter)
+        param = self.parseVariableIdentifier();
+        if (self.match('=')):
+            self.throwError(Messages.DefaultRestParameter);
+
+        if (not self.match(')')):
+            self.throwError(Messages.ParameterAfterRestParameter);
+        return node.finishRestElement(param);
+
+    # 12.3 Empty Statement
+
+    def parseEmptyStatement(self, node):
+        self.expect(';');
+        return node.finishEmptyStatement()
+
+    # 12.4 Expression Statement
+
+    def parseExpressionStatement(self, node):
+        expr = self.parseExpression();
+        self.consumeSemicolon();
+        return node.finishExpressionStatement(expr);
+
+    # 12.5 If statement
+
+    def parseIfStatement(self, node):
+        self.expectKeyword('if');
+
+        self.expect('(');
+
+        test = self.parseExpression();
+
+        self.expect(')');
+
+        consequent = self.parseStatement();
+
+        if (self.matchKeyword('else')):
+            self.lex();
+            alternate = self.parseStatement();
+        else:
+            alternate = null;
+        return node.finishIfStatement(test, consequent, alternate)
+
+    # 12.6 Iteration Statements
+
+    def parseDoWhileStatement(self, node):
+
+        self.expectKeyword('do')
+
+        oldInIteration = self.state['inIteration']
+        self.state['inIteration'] = true
+
+        body = self.parseStatement();
+
+        self.state['inIteration'] = oldInIteration;
+
+        self.expectKeyword('while');
+
+        self.expect('(');
+
+        test = self.parseExpression();
+
+        self.expect(')')
+
+        if (self.match(';')):
+            self.lex()
+        return node.finishDoWhileStatement(body, test)
+
+    def parseWhileStatement(self, node):
+
+        self.expectKeyword('while')
+
+        self.expect('(')
+
+        test = self.parseExpression()
+
+        self.expect(')')
+
+        oldInIteration = self.state['inIteration']
+        self.state['inIteration'] = true
+
+        body = self.parseStatement()
+
+        self.state['inIteration'] = oldInIteration
+
+        return node.finishWhileStatement(test, body)
+
+    def parseForStatement(self, node):
+        previousAllowIn = self.state['allowIn']
+
+        init = test = update = null
+
+        self.expectKeyword('for')
+
+        self.expect('(')
+
+        if (self.match(';')):
+            self.lex()
+        else:
+            if (self.matchKeyword('var')):
+                init = Node()
+                self.lex()
+
+                self.state['allowIn'] = false;
+                init = init.finishVariableDeclaration(self.parseVariableDeclarationList())
+                self.state['allowIn'] = previousAllowIn
+
+                if (len(init.declarations) == 1 and self.matchKeyword('in')):
+                    self.lex()
+                    left = init
+                    right = self.parseExpression()
+                    init = null
+                else:
+                    self.expect(';')
+            elif (self.matchKeyword('const') or self.matchKeyword('let')):
+                init = Node()
+                kind = self.lex()['value']
+
+                self.state['allowIn'] = false
+                declarations = self.parseBindingList(kind, {'inFor': true})
+                self.state['allowIn'] = previousAllowIn
+
+                if (len(declarations) == 1 and declarations[0].init == null and self.matchKeyword('in')):
+                    init = init.finishLexicalDeclaration(declarations, kind);
+                    self.lex();
+                    left = init;
+                    right = self.parseExpression();
+                    init = null;
+                else:
+                    self.consumeSemicolon();
+                    init = init.finishLexicalDeclaration(declarations, kind);
+            else:
+                initStartToken = self.lookahead
+                self.state['allowIn'] = false
+                init = self.inheritCoverGrammar(self.parseAssignmentExpression);
+                self.state['allowIn'] = previousAllowIn;
+
+                if (self.matchKeyword('in')):
+                    if (not self.isAssignmentTarget):
+                        self.tolerateError(Messages.InvalidLHSInForIn)
+                    self.lex();
+                    self.reinterpretExpressionAsPattern(init);
+                    left = init;
+                    right = self.parseExpression();
+                    init = null;
+                else:
+                    if (self.match(',')):
+                        initSeq = [init];
+                        while (self.match(',')):
+                            self.lex();
+                            initSeq.append(self.isolateCoverGrammar(self.parseAssignmentExpression))
+                        init = WrappingNode(initStartToken).finishSequenceExpression(initSeq)
+                    self.expect(';');
+
+        if ('left' not in locals()):
+            if (not self.match(';')):
+                test = self.parseExpression();
+
+            self.expect(';');
+
+            if (not self.match(')')):
+                update = self.parseExpression();
+
+        self.expect(')');
+
+        oldInIteration = self.state['inIteration']
+        self.state['inIteration'] = true;
+
+        body = self.isolateCoverGrammar(self.parseStatement)
+
+        self.state['inIteration'] = oldInIteration;
+
+        return node.finishForStatement(init, test, update, body) if (
+        'left' not in locals()) else node.finishForInStatement(left, right, body);
+
+    # 12.7 The continue statement
+
+    def parseContinueStatement(self, node):
+        label = null
+
+        self.expectKeyword('continue');
+
+        # Optimize the most common form: 'continue;'.
+        if ord(self.source[self.startIndex]) == 0x3B:
+            self.lex();
+            if (not self.state['inIteration']):
+                self.throwError(Messages.IllegalContinue)
+            return node.finishContinueStatement(null)
+        if (self.hasLineTerminator):
+            if (not self.state['inIteration']):
+                self.throwError(Messages.IllegalContinue);
+            return node.finishContinueStatement(null);
+
+        if (self.lookahead['type'] == Token.Identifier):
+            label = self.parseVariableIdentifier();
+
+            key = '$' + label.name;
+            if not key in self.state['labelSet']:  # todo make sure its correct!
+                self.throwError(Messages.UnknownLabel, label.name);
+        self.consumeSemicolon()
+
+        if (label == null and not self.state['inIteration']):
+            self.throwError(Messages.IllegalContinue)
+        return node.finishContinueStatement(label)
+
+    # 12.8 The break statement
+
+    def parseBreakStatement(self, node):
+        label = null
+
+        self.expectKeyword('break');
+
+        # Catch the very common case first: immediately a semicolon (U+003B).
+        if (ord(self.source[self.lastIndex]) == 0x3B):
+            self.lex();
+
+            if (not (self.state['inIteration'] or self.state['inSwitch'])):
+                self.throwError(Messages.IllegalBreak)
+            return node.finishBreakStatement(null)
+        if (self.hasLineTerminator):
+            if (not (self.state['inIteration'] or self.state['inSwitch'])):
+                self.throwError(Messages.IllegalBreak);
+            return node.finishBreakStatement(null);
+        if (self.lookahead['type'] == Token.Identifier):
+            label = self.parseVariableIdentifier();
+
+            key = '$' + label.name;
+            if not (key in self.state['labelSet']):
+                self.throwError(Messages.UnknownLabel, label.name);
+        self.consumeSemicolon();
+
+        if (label == null and not (self.state['inIteration'] or self.state['inSwitch'])):
+            self.throwError(Messages.IllegalBreak)
+        return node.finishBreakStatement(label);
+
+    # 12.9 The return statement
+
+    def parseReturnStatement(self, node):
+        argument = null;
+
+        self.expectKeyword('return');
+
+        if (not self.state['inFunctionBody']):
+            self.tolerateError(Messages.IllegalReturn);
+
+        # 'return' followed by a space and an identifier is very common.
+        if (ord(self.source[self.lastIndex]) == 0x20):
+            if (isIdentifierStart(self.source[self.lastIndex + 1])):
+                argument = self.parseExpression();
+                self.consumeSemicolon();
+                return node.finishReturnStatement(argument)
+        if (self.hasLineTerminator):
+            # HACK
+            return node.finishReturnStatement(null)
+
+        if (not self.match(';')):
+            if (not self.match('}') and self.lookahead['type'] != Token.EOF):
+                argument = self.parseExpression();
+        self.consumeSemicolon();
+
+        return node.finishReturnStatement(argument);
+
+    # 12.10 The with statement
+
+    def parseWithStatement(self, node):
+        if (self.strict):
+            self.tolerateError(Messages.StrictModeWith)
+
+        self.expectKeyword('with');
+
+        self.expect('(');
+
+        obj = self.parseExpression();
+
+        self.expect(')');
+
+        body = self.parseStatement();
+
+        return node.finishWithStatement(obj, body);
+
+    # 12.10 The swith statement
+
+    def parseSwitchCase(self):
+        consequent = []
+        node = Node();
+
+        if (self.matchKeyword('default')):
+            self.lex();
+            test = null;
+        else:
+            self.expectKeyword('case');
+            test = self.parseExpression();
+
+        self.expect(':');
+
+        while (self.startIndex < self.length):
+            if (self.match('}') or self.matchKeyword('default') or self.matchKeyword('case')):
+                break
+            statement = self.parseStatementListItem()
+            consequent.append(statement)
+        return node.finishSwitchCase(test, consequent)
+
+    def parseSwitchStatement(self, node):
+
+        self.expectKeyword('switch');
+
+        self.expect('(');
+
+        discriminant = self.parseExpression();
+
+        self.expect(')');
+
+        self.expect('{');
+
+        cases = [];
+
+        if (self.match('}')):
+            self.lex();
+            return node.finishSwitchStatement(discriminant, cases);
+
+        oldInSwitch = self.state['inSwitch'];
+        self.state['inSwitch'] = true;
+        defaultFound = false;
+
+        while (self.startIndex < self.length):
+            if (self.match('}')):
+                break;
+            clause = self.parseSwitchCase();
+            if (clause.test == null):
+                if (defaultFound):
+                    self.throwError(Messages.MultipleDefaultsInSwitch);
+                defaultFound = true;
+            cases.append(clause);
+
+        self.state['inSwitch'] = oldInSwitch;
+
+        self.expect('}');
+
+        return node.finishSwitchStatement(discriminant, cases);
+
+    # 12.13 The throw statement
+
+    def parseThrowStatement(self, node):
+
+        self.expectKeyword('throw');
+
+        if (self.hasLineTerminator):
+            self.throwError(Messages.NewlineAfterThrow);
+
+        argument = self.parseExpression();
+
+        self.consumeSemicolon();
+
+        return node.finishThrowStatement(argument);
+
+    # 12.14 The try statement
+
+    def parseCatchClause(self):
+        node = Node();
+
+        self.expectKeyword('catch');
+
+        self.expect('(');
+        if (self.match(')')):
+            self.throwUnexpectedToken(self.lookahead);
+        param = self.parsePattern();
+
+        # 12.14.1
+        if (self.strict and isRestrictedWord(param.name)):
+            self.tolerateError(Messages.StrictCatchVariable);
+
+        self.expect(')');
+        body = self.parseBlock();
+        return node.finishCatchClause(param, body);
+
+    def parseTryStatement(self, node):
+        handler = null
+        finalizer = null;
+
+        self.expectKeyword('try');
+
+        block = self.parseBlock();
+
+        if (self.matchKeyword('catch')):
+            handler = self.parseCatchClause()
+
+        if (self.matchKeyword('finally')):
+            self.lex();
+            finalizer = self.parseBlock();
+
+        if (not handler and not finalizer):
+            self.throwError(Messages.NoCatchOrFinally)
+
+        return node.finishTryStatement(block, handler, finalizer)
+
+    # 12.15 The debugger statement
+
+    def parseDebuggerStatement(self, node):
+        self.expectKeyword('debugger');
+
+        self.consumeSemicolon();
+
+        return node.finishDebuggerStatement();
+
+    # 12 Statements
+
+    def parseStatement(self):
+        typ = self.lookahead['type']
+
+        if (typ == Token.EOF):
+            self.throwUnexpectedToken(self.lookahead)
+
+        if (typ == Token.Punctuator and self.lookahead['value'] == '{'):
+            return self.parseBlock()
+
+        self.isAssignmentTarget = self.isBindingElement = true;
+        node = Node();
+        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['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['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/resources/lib/pyjsparser/pyjsparserdata.py b/resources/lib/pyjsparser/pyjsparserdata.py
new file mode 100644 (file)
index 0000000..6ca6dbc
--- /dev/null
@@ -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(' ')
diff --git a/resources/lib/pyjsparser/std_nodes.py b/resources/lib/pyjsparser/std_nodes.py
new file mode 100644 (file)
index 0000000..a4b37ca
--- /dev/null
@@ -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())
index 25cd28551f2f5dd77b3b5cf2af1f9c54d261a136..afb056f7994fa7c5a9483bd1a06ca70fad3029c8 100644 (file)
@@ -3,23 +3,6 @@
 # Module: utils
 # Created on: 13.01.2017
 
-# strips html from input
-# used the kick out the junk, when parsing the inline JS objects of the Netflix homepage
-from HTMLParser import HTMLParser
-class MLStripper(HTMLParser):
-    def __init__(self):
-        self.reset()
-        self.fed = []
-    def handle_data(self, d):
-        self.fed.append(d)
-    def get_data(self):
-        return ''.join(self.fed)
-
-def strip_tags(html):
-    s = MLStripper()
-    s.feed(html)
-    return s.get_data()
-
 # Takes everything, does nothing, classic no operation function
 def noop (**kwargs):
     return True