# ====== Legal notices
#
# Copyright 2014, 2015, 2016, 2017 Jacques de Hooge, GEATEC engineering, www.geatec.com
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import os.path
import sys
import tokenize
import ast
import re
import copy
import datetime
import math
import traceback
import io
import subprocess
import shlex

from org.transcrypt import __base__, utils, sourcemaps, minify, static_check, type_check

class ModuleMetadata:
    def __init__ (self, program, name):
        self.program = program
        self.name = name
        searchedModulePaths = []
        for searchDir in self.program.moduleSearchDirs:
            relPrepath = self.name.replace ('.', '/')
            prepath = '{}/{}'.format (searchDir, relPrepath)
            self.isDir = os.path.isdir (prepath)

            if self.isDir:
                self.sourceDir = prepath
                self.filePrename = '__init__'
            else:
                self.sourceDir, self.filePrename = prepath.rsplit ('/', 1)

            self.targetDir = '{}/{}'.format (self.sourceDir, __base__.__envir__.target_subdir)
            self.targetPath = '{}/{}.mod.js'.format (self.targetDir, self.filePrename)

            self.sourcePath = '{}/{}{}' .format (self.sourceDir, self.filePrename, '' if self.name == self.program.mainModuleName and not self.program.sourceFileName.endswith ('.py') else '.py')
            searchedModulePaths += [self.sourcePath, self.targetPath]

            if not os.path.isfile (self.sourcePath):
                self.sourcePath = self.targetPath   # For a Javascript-only module, source and target are the same and a source map can be faked

            self.extraSubdir = 'extra'
            self.treePath = '{}/{}/{}.mod.tree'.format (self.targetDir, self.extraSubdir, self.filePrename)
            
            if (os.path.isfile (self.sourcePath)):
                break
        else:
            # If even the target can't be loaded then there's a problem with this module, root or not
            raise utils.Error (
                message = (
                    '\n\tAttempt to import module: {}'.format (self.name) +
                    '\n\tCan\'t find any of:\n\t\t{}\n'.format ('\n\t\t'. join (searchedModulePaths))
                )
            )

    def sourceExists (self):
        return os.path.isfile (self.sourcePath)

    def targetExists (self):
        return os.path.isfile (self.targetPath)

    def exists (self):
        return self.sourceExists () or self.targetExists ()

    def dirty (self):
        # Javascript-only modules are never dirty (so don't try to parse and compile Javascript)
        if self.targetPath == self.sourcePath:
            return False

        # Find youngest of .py and .js files and use that as "original"
        youngestTime = 0
        youngestPath = None
        for path in self.targetPath, self.sourcePath:                   # Order matters
            if os.path.isfile (path):
                pathTime = os.path.getmtime (path)
                if utils.commandArgs.build or pathTime > youngestTime:  # Builds correctly also if some source files are missing
                    youngestTime = pathTime
                    youngestPath = path

        return youngestPath == self.sourcePath
        
    def getStandardName (self):
        try:
            if self.name == self.program.mainModuleName:
                return '__main__'
            else:
                return self.name
        except:
            print (traceback.format_exc ())

class Program:
    def __init__ (self, moduleSearchDirs, symbols):
        utils.setProgram (self)
    
        self.moduleSearchDirs = moduleSearchDirs
        self.symbols = symbols

        self.pythonVersion = sys.version_info [0] + 0.1 * sys.version_info [1]

        if utils.commandArgs.esv == None:
            self.javaScriptVersion = utils.defaultJavaScriptVersion
        else:
            self.javaScriptVersion = int (utils.commandArgs.esv)

        self.rawModuleCaption = '\n\n// ============ Source: {} ============\n\n' if utils.commandArgs.anno else ''
        self.moduleCaptionSkip = self.rawModuleCaption.count ('\n')

        self.moduleDict = {}
        self.importStack = []   # Enables showing load sequence in case a module cannot be loaded

        # Set paths that don't require the module dict
        self.sourcePath = os.path.abspath (utils.commandArgs.source) .replace ('\\', '/')
        self.sourceDir = '/'.join (self.sourcePath.split ('/') [ : -1])
        self.sourceFileName = self.sourcePath.split ('/') [-1]

        # Define names early, since they are cross-used in module compilation
        prefix = 'org.{}'.format (__base__.__envir__.transpiler_name)
        self.coreModuleName = '{}.{}'.format (prefix, '__core__')
        self.baseModuleName = '{}.{}'.format (prefix, '__base__')
        self.standardModuleName = '{}.{}'.format (prefix, '__standard__')
        self.builtinModuleName = '{}.{}'.format (prefix, '__builtin__')
        self.mainModuleName = self.sourceFileName [ : -3] if self.sourceFileName.endswith ('.py') else self.sourceFileName

        # Compile inline modules
        Module (self, ModuleMetadata (self, self.coreModuleName), True)
        Module (self, ModuleMetadata (self, self.baseModuleName))
        Module (self, ModuleMetadata (self, self.standardModuleName))
        Module (self, ModuleMetadata (self, self.builtinModuleName), True)

        # Optionally perfom static typing validation

        if utils.commandArgs.dstat:
            try:
                type_check.run (self.sourcePath)
            except Exception as exception:
                utils.log (True, 'Validating: {} and dependencies\n\tInternal error in static typing validator\n', self.sourcePath)

        # Compile imported modules
        try:
            moduleMetadata = ModuleMetadata (self, self.mainModuleName)
            Module (self, moduleMetadata)   # Compile of main module, will trigger recursive compilation
        except Exception as exception:
            utils.enhanceException (
                exception,
                message = '\n\t{}'.format (str (exception))
            )

        # Set paths that require the module dict
        self.targetPath = '{}/{}.js'.format (self.moduleDict [self.mainModuleName] .metadata.targetDir, self.mainModuleName)
        self.miniTargetPath = '{}/{}.min.js'.format (self.moduleDict [self.mainModuleName] .metadata.targetDir, self.mainModuleName)

        # Set sourcemaps
        if utils.commandArgs.map:
            self.prettyMap = sourcemaps.SourceMap (
                self.moduleDict [self.mainModuleName] .metadata.targetDir,
                '{}.js'.format (self.mainModuleName),
                self.moduleDict [self.mainModuleName] .metadata.extraSubdir,

            )

            if not utils.commandArgs.nomin:
                self.shrinkMap = sourcemaps.SourceMap (
                    self.moduleDict [self.mainModuleName] .metadata.targetDir,
                    '{}.shrink.js'.format (self.mainModuleName),
                    self.moduleDict [self.mainModuleName] .metadata.extraSubdir,
                )

                self.miniMap = sourcemaps.SourceMap (
                    self.moduleDict [self.mainModuleName] .metadata.targetDir,
                    '{}.min.js'.format (self.mainModuleName),
                    self.moduleDict [self.mainModuleName] .metadata.extraSubdir,
                )

        # Round up imported modules
        importedModules = [
            self.moduleDict [moduleName]
            for moduleName in sorted (self.moduleDict)
            if not moduleName in (self.coreModuleName, self.baseModuleName, self.standardModuleName, self.builtinModuleName, self.mainModuleName)
        ]

        # And sandwich them between the in-line modules
        self.allModules = (
            [
                self.moduleDict [self.coreModuleName],
                self.moduleDict [self.baseModuleName],
                self.moduleDict [self.standardModuleName],
                self.moduleDict [self.builtinModuleName]
            ] +
            importedModules +
            [self.moduleDict [self.mainModuleName]]
        )

        # Encapsulate target code for all modules
        header = '"use strict";\n// {}\'ed from Python, {}\n'.format (
            __base__.__envir__.transpiler_name.capitalize (), datetime.datetime.now ().strftime ('%Y-%m-%d %H:%M:%S'),
        )

        if utils.commandArgs.parent == '.none':
            initializer = '{0} ();\n' .format (self.mainModuleName)
        elif utils.commandArgs.parent == '.user':
            initializer = ''
        else:
            if utils.commandArgs.parent == None:
                parent = 'window'
            else:
                parent = utils.commandArgs.parent

            initializer =  '{0} [\'{1}\'] = {1} ();\n' .format (parent, self.mainModuleName)

        targetCode = (
            # BEGIN prologue, be sure to adapt sourcemaps.nrOfPadLines if the nr of prologue lines is changed!
            header +
            'function {} () {{\n'.format (self.mainModuleName) +
            '    var __symbols__ = {};\n'.format (self.symbols) +
            # END prologue
            ''.join ([module.getModuleCaption () + module.targetCode for module in self.allModules]) +
            '    return __all__;\n' +
            '}\n' +
            initializer
        )

        # Write encapsulated target code
        utils.log (True, 'Saving result in: {}\n', self.targetPath)
        with utils.create (self.targetPath) as aFile:
            aFile.write (targetCode)

            if utils.commandArgs.map:
                aFile.write (self.prettyMap.mapRef)

        # Join and save source maps
        if utils.commandArgs.map:
            utils.log (False, 'Saving single-level sourcemap in: {}\n', self.prettyMap.mapPath)
            self.prettyMap.concatenate ([module.modMap for module in self.allModules], self.moduleCaptionSkip)
            self.prettyMap.save ()

        # Minify
        if not utils.commandArgs.nomin:
            utils.log (True, 'Saving minified result in: {}\n', self.miniTargetPath)
            minify.run (self.targetPath, self.miniTargetPath, self.shrinkMap.mapPath if utils.commandArgs.map else None, 8) # Minifier has to accept JavaScript 6 input code, it is there in the autotest, even if not executed.
            if utils.commandArgs.map:
                utils.log (False, 'Saving multi-level sourcemap in: {}\n', self.miniMap.mapPath)
                self.shrinkMap.load ()
                self.prettyMap.cascade (self.shrinkMap, self.miniMap)
                self.miniMap.save ()

                with open (self.miniTargetPath, 'a') as miniFile:
                    miniFile.write (self.miniMap.mapRef)

    def provide (self, moduleName):
        # moduleName may contain dots if it's imported, but it'll have the same name in every import
    
        if moduleName == '__main__':
            moduleName = self.mainModuleName

        moduleMetadata = ModuleMetadata (self, moduleName)

        if moduleMetadata.name in self.moduleDict:  # Find out if module is already provided
            return self.moduleDict [moduleMetadata.name]
        else:                                       # If not, provide by loading or compiling
            return Module (self, moduleMetadata)

class Module:
    def __init__ (self, program, moduleMetadata, stripCommentsIfAllowed = False):
        self.program = program
        self.metadata = moduleMetadata
        self.stripComments = stripCommentsIfAllowed and not utils.commandArgs.dnostrip
        self.program.moduleDict [self.metadata.name] = self

        # Names of module being under compilation and line nrs of current import
        # Used for error reports
        # Note that JavaScript-only modules will leave lineNr None if they import something
        # This is since there's no explicit import location in such modules
        self.program.importStack.append ([self.metadata, None])
                
        # Set sourcemap
        if utils.commandArgs.map:
            self.modMap = sourcemaps.SourceMap (
                self.metadata.targetDir,
                '{}.mod.js'.format (self.metadata.name),
                self.metadata.extraSubdir
            )

        if self.metadata.dirty ():
            self.parse ()
            if utils.commandArgs.dcheck:
                try:
                    static_check.run (self.metadata.sourcePath, self.parseTree)
                except Exception as exception:
                    utils.log (True, 'Checking: {}\n\tInternal error in lightweight consistency checker, remainder of module skipped\n', self.metadata.sourcePath)

            if utils.commandArgs.dtree:
                self.dumpTree ()

            self.generateJavascriptAndMap ()
            self.extractPropertiesFromJavascript ()
        else:
            self.loadJavascript ()
            self.extractPropertiesFromJavascript ()

            if utils.commandArgs.map:
                self.modMap.loadOrFake (self.metadata.sourcePath, self.nrOfTargetLines)
                
        self.program.importStack.pop ()

    def getModuleCaption (self):
        return self.program.rawModuleCaption.format (self.metadata.sourcePath) if utils.commandArgs.anno else ''

    def parse (self):
        def pragmasFromComments (sourceCode):
            # This function changes rather than regenerates the sourcecode, since tokenize/untokenize will mess up formatting
        
            tokens = tokenize.tokenize (io.BytesIO (sourceCode.encode ('utf-8')) .readline)
            pragmaCommentLineIndices = []
            for tokenType, tokenString, startRowColumn, endRowColumn, logicalLine in tokens:
                if tokenType == tokenize.COMMENT and tokenString [1 : ] .lstrip () .startswith ('__pragma__'):
                    pragmaCommentLineIndices.append (startRowColumn [0] - 1)
                    
            sourceLines = sourceCode.split ('\n') 
            for pragmaCommentLineIndex in pragmaCommentLineIndices:
                head, separator, tail = sourceLines [pragmaCommentLineIndex] .partition ('#')
                sourceLines [pragmaCommentLineIndex] = head + tail.lstrip ()

            return '\n'.join (sourceLines)
            
        try:
            utils.log (False, 'Parsing module: {}\n', self.metadata.sourcePath)

            with tokenize.open (self.metadata.sourcePath) as sourceFile:
                self.sourceCode = utils.extraLines + sourceFile.read ()
                
            self.parseTree = ast.parse (pragmasFromComments (self.sourceCode))
        except SyntaxError as syntaxError:
            utils.enhanceException (
                syntaxError,
                lineNr = syntaxError.lineno,
                message = (
                        '\n\t{} <SYNTAX FAULT] {}'.format (
                            syntaxError.text [:syntaxError.offset].lstrip (),
                            syntaxError.text [syntaxError.offset:].rstrip ()
                        )
                    if syntaxError.text else
                        syntaxError.args [0]
                )
            )

    def dumpTree (self):
        utils.log (False, 'Dumping syntax tree for module: {}\n', self.metadata.sourcePath)

        def walk (name, value, tabLevel):
            self.treeFragments .append ('\n{0}{1}: {2} '.format (tabLevel * '\t', name, type (value).__name__ ))
            if isinstance (value, ast.AST):
                for field in ast.iter_fields (value):
                    walk (field [0], field [1], tabLevel + 1)
            elif isinstance (value, list):
                for element in value:
                    walk ('element', element, tabLevel + 1)
            else:
                self.treeFragments.append ('= {0}'.format (value))

        self.treeFragments = []
        walk ('file', self.parseTree, 0)
        self.textTree = ''.join (self.treeFragments) [1:]

        with utils.create (self.metadata.treePath) as treeFile:
            treeFile.write (self.textTree)

    def loadJavascript (self):
        def strip (code, symbols):
            def stripSingleLineComments (line):
                pos = line.find ('//')
                return (line if pos < 0 else line [ : pos]) .rstrip ()
        
            loading = True

            def loadable (targetLine):
                nonlocal loading

                def __pragma__ (name, *args):
                    nonlocal loading

                    if name == 'ifdef':
                        loading = args [0] in symbols
                    elif name == 'ifndef':
                        loading = not args [0] in symbols
                    elif name == 'else':
                        loading = not loading
                    elif name == 'endif':
                        loading = True

                strippedLine = targetLine.lstrip ()
                if self.stripComments and strippedLine.startswith ('/*'):
                    loading = False
                    return loading  # So skip this line
                elif self.stripComments and strippedLine.endswith ('*/'):
                    loading = True  # Load next line
                elif strippedLine.startswith ('__pragma__') and (
                    'ifdef' in strippedLine or
                    'ifndef' in strippedLine or
                    'else' in strippedLine or
                    'endif' in strippedLine
                ):
                    exec (strippedLine)
                    return False    # Skip line anyhow
                else:
                    return loading  # Skip line only if not in loading state
            
            if self.stripComments:
                loadableLines = [commentlessLine for commentlessLine in [stripSingleLineComments (line) for line in code.split ('\n') if loadable (line)] if commentlessLine]
            else:
                loadableLines = [line for line in code.split ('\n') if loadable (line)]
            return '\n'.join (loadableLines)

        with open (self.metadata.targetPath) as targetFile:
            self.targetCode = strip (targetFile.read (), self.program.symbols)

    def generateJavascriptAndMap (self):
        utils.log (False, 'Generating code for module: {}\n', self.metadata.targetPath)

        generator = Generator (self)

        if utils.commandArgs.map or utils.commandArgs.anno:             # In both cases the generator will have instrumented the target fragments by appending line numbers
                                                                        # N.B. __pragma__ ('noanno') will be too late to prevent instrumenting of the main module's first line
                                                                        # In that case if no source maps are required either, the appended line numbers simply won't be used
            instrumentedTargetLines = ''.join (generator.targetFragments) .split ('\n')                 # Split joined fragments into lines

            # Split instrumentedTargetLines in (bare) targetLines and sourceLineNrs, skipping empty statements
            targetLines = []

            if utils.commandArgs.map:
                self.sourceLineNrs = []                                                                 # Only needed for source maps, not for annotations

            for targetLine in instrumentedTargetLines:
                sourceLineNrString = targetLine [-sourcemaps.lineNrLength : ]                           # Take the appended line number, e.g. the string '000014'
                sourceLineNr = int ('1' + sourceLineNrString) - sourcemaps.maxNrOfSourceLinesPerModule  # Turn it into an integer, e.g. 14

                targetLine = targetLine [ : -sourcemaps.lineNrLength]                                   # Obtain non-instrumented line by removing the appended line number

                # Only append non-emptpy statements and their number info
                if targetLine.strip () != ';':                                                          # If the non-instrumented line isn't empty
                    if generator.allowDebugMap:                                                         # If annotations comments have to be prepended
                        targetLine = '/* {} */ {}'.format (sourceLineNrString, targetLine)              # Prepend them

                    targetLines.append (targetLine)                                                     # Add the target line, with or without prepended annotation commend

                    if utils.commandArgs.map:
                        self.sourceLineNrs.append (sourceLineNr)                                        # Remember its line number to be able to generate a sourcemap

            # Generate per module sourcemap and copy sourcefile
            if utils.commandArgs.map:
                utils.log (False, 'Generating source map for module: {}\n', self.metadata.sourcePath)
                self.modMap.generate (self.metadata.sourcePath, self.sourceLineNrs)
                self.modMap.save ()
        else:                                                           # No maps needed, so this 'no stripping' shortcut for speed
            targetLines = [line for line in  ''.join (generator.targetFragments) .split ('\n') if line.strip () != ';']

        # Join and save module code
        self.targetCode = '\n'.join (targetLines)
        with utils.create (self.metadata.targetPath) as aFile:
            aFile.write (self.targetCode)

    def extractPropertiesFromJavascript (self):
        def removeLineNrs (clause):
            if utils.commandArgs.anno:
                return re.sub ('/\* {} \*/'.format (sourcemaps.lineNrLength * '\d'), '', clause)
            else:
                return clause

        utils.log (False, 'Extracting module properties from: {}\n', self.metadata.targetPath)

        useClause = removeLineNrs (self.targetCode [self.targetCode.rfind ('<use>') : self.targetCode.rfind ('</use>')])
        self.use = sorted (set ([
            word
            for word in useClause.replace ('__pragma__', ' ') .replace ('(', ' ') .replace (')', ' ') .replace ('\'', ' ') .replace ('+', ' ') .split ()
            if not word.startswith ('<')
        ]))
        for moduleName in self.use:
            self.program.provide (moduleName)

        allClause = removeLineNrs (self.targetCode [self.targetCode.rfind ('<all>') : self.targetCode.rfind ('</all>')])
        self.all = sorted (set ([
            word [1 : ]
            for word in allClause.replace ('__all__', ' ') .replace ('=', ' ') .split ()
            if word.startswith ('.')
        ]))

        if utils.commandArgs.map:
            self.nrOfTargetLines = self.targetCode.count ('\n') + 1

class Scope:
    def __init__ (self, node):
        self.node = node
        self.nonlocals = set ()
        self.containsYield = False

class Generator (ast.NodeVisitor):
# Terms like parent, child, ancestor and descendant refer to the parse tree here, not to inheritance

    def __init__ (self, module):
        self.module = module

        self.targetFragments = []
        self.indentLevel = 0
        self.scopes = []
        self.classDecorators = []
        self.use = set ()
        self.all = set ()
        self.importHeads = set ()
        self.docString = None
        self.docStringEmitted = False
        self.expectingNonOverloadedLhsIndex = False
        self.lineNr = 1

        self.aliasers = [self.getAliaser (*alias) for alias in (
# START predef_aliases

            # Format: ('<Python source identifier>', '<JavaScript target identifier>')

                                                    ('js_and', 'and'),
            ('arguments', 'py_arguments'),          ('js_arguments', 'arguments'),
            ('case', 'py_case'),
            ('clear', 'py_clear'),                  ('js_clear', 'clear'),
            ('default', 'py_default'),
            ('del', 'py_del'),                      ('js_del', 'del'),
            ('false', 'py_false'),
                                                    ('js_from', 'from'),
            ('get', 'py_get'),                      ('js_get', 'get'),
                                                    ('js_global', 'global'),
            ('Infinity', 'py_Infinity'),            ('js_Infinity', 'Infinity'),
            ('isNaN', 'py_isNaN'),                  ('js_isNaN', 'isNaN'),
            ('iter', 'py_iter'),                    ('js_iter', 'iter'),
            ('items', 'py_items'),                  ('js_items', 'items'),
            ('keys', 'py_keys'),                    ('js_keys', 'keys'),
            ('name', 'py_name'),                    ('js_name', 'name'),
            ('NaN', 'py_NaN'),                      ('js_NaN', 'NaN'),
            ('new', 'py_new'),
            ('next', 'py_next'),                    ('js_next', 'next'),
                                                    ('js_not', 'not'),
                                                    ('js_or', 'or'),
            ('pop', 'py_pop'),                      ('js_pop', 'pop'),
            ('popitem', 'py_popitem'),              ('js_popitem', 'popitem'),
            ('replace', 'py_replace'),              ('js_replace', 'replace'),
            ('selector', 'py_selector'),            ('js_selector', 'selector'),
            ('sort', 'py_sort'),                    ('js_sort', 'sort'),
            ('split', 'py_split'),                  ('js_split', 'split'),
            ('switch', 'py_switch'),
            ('type', 'py_metatype'),                ('js_type', 'type'),    # Only for the type metaclass, the type operator is dealth with separately in visit_Call
            ('TypeError', 'py_TypeError'),          ('js_TypeError', 'TypeError'),
            ('update', 'py_update'),                ('js_update', 'update'),
            ('values', 'py_values'),                ('js_values', 'values'),
            ('reversed', 'py_reversed'),            ('js_reversed', 'reversed'),
            ('setdefault', 'py_setdefault'),        ('js_setdefault', 'setdefault'),
                                                    ('js_super', 'super'),
            ('true', 'py_true'),
            ('undefined', 'py_undefined'),          ('js_undefined', 'undefined'),

# END predef_aliases
        )]

        self.idFiltering = True

        self.tempIndices = {}
        self.skippedTemps = set ()
        self.stubsName = 'org.{}.stubs.'.format (__base__.__envir__.transpiler_name)

        self.nameConsts = {
            None: 'null',
            True: 'true',
            False: 'false'
        }

        '''
        The precendences explicitly given as integers in the list below are JavaScript precedences as specified by:
        https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence .

        Python precedences are implicitly present in branch ordering of the AST generated by CPython's parser.
        '''

        self.operators = {
            # Highest precendence

            ast.Not: ('!', 16),
            ast.Invert: ('~', 16),
            ast.UAdd: ('+', 16),
            ast.USub: ('-', 16),

            ast.Pow: (None, 15),        # Dealt with separately

            ast.Mult: ('*', 14),
            ast.MatMult: (None, 14),    # Dealt with separately
            ast.Div: ('/', 14),
            ast.FloorDiv: (None, 14),   # Dealt with separately
            ast.Mod: ('%', 14),         # Used only for JavaScript %, which differs from Python %

            ast.Add: ('+', 13),
            ast.Sub: ('-', 13),

            ast.LShift: ('<<', 12),
            ast.RShift: ('>>', 12),

            ast.Lt: ('<', 11),
            ast.LtE: ('<=', 11),
            ast.Gt: ('>', 11),
            ast.GtE: ('>=', 11),
            ast.In: (None, 11),         # Dealt with separately
            ast.NotIn: (None, 11),      # Dealt with separately

            ast.Eq: ('==', 10),
            ast.NotEq: ('!=', 10),
            ast.Is: ('===', 10),        # Not really, but closest for now
            ast.IsNot: ('!==', 10),     # Not really, but closest for now

            ast.BitAnd: ('&', 9),
            ast.BitOr: ('|', 8),
            ast.BitXor: ('^', 7),
            ast.And: ('&&', 6),
            ast.Or: ('||', 5)

            # Lowest precedence
        }

        self.allowKeywordArgs = utils.commandArgs.kwargs
        self.allowOperatorOverloading = utils.commandArgs.opov
        self.allowConversionToIterable = utils.commandArgs.iconv
        self.allowConversionToTruthValue = utils.commandArgs.tconv
        self.allowKeyCheck = utils.commandArgs.keycheck
        self.allowDebugMap = utils.commandArgs.anno and not self.module.metadata.sourcePath.endswith ('.js')
        self.allowDocAttribs = utils.commandArgs.docat
        self.allowJavaScriptIter = False
        self.allowJavaScriptKeys = utils.commandArgs.jskeys
        self.allowJavaScriptMod = utils.commandArgs.jsmod
        self.allowMemoizeCalls = utils.commandArgs.fcall

        self.noskipCodeGeneration = True
        self.conditionalCodeGeneration = True
        self.stripTuple = False     # For speed, tuples are translated to bare JavaScript arrays if they're just indices. Will autoreset.
        self.stripTuples = False    # For correctness, tuples are translated to bare JavaScript arrays if they are assignment target in a JavaScript 6 for-loop. Will not autoreset.
        self.replaceSend = False

        try:
            self.visit (module.parseTree)
            self.targetFragments.append (self.lineNrString) # Last target fragment doesn't have a '\n' to replace in the emit method

        except Exception as exception:
            utils.enhanceException (
                exception,
                lineNr = self.lineNr
            )

        if self.tempIndices:
            raise utils.Error (
                message = '\n\tTemporary variables leak in code generator: {}'.format (self.tempIndices)
            )

    def visitSubExpr (self, node, child):
        def getPriority (exprNode):
            if type (exprNode) in (ast.BinOp, ast.BoolOp):
                return self.operators [type (exprNode.op)][1]
            elif type (exprNode) == ast.Compare:
                return self.operators [type (exprNode.ops [0])][1]  # All ops have same priority
            elif type (exprNode) == ast.Yield:
                return -1000000
            else:
                return 1000000  # No need for parenthesis

        if getPriority (child) <= getPriority (node):
            self.emit ('(')
            self.visit (child)
            self.emit (')')
        else:
            self.visit (child)

    def getAliaser (self, sourceFragment, targetFragment):
        return (sourceFragment, re.compile ('''
            (^{0}$)|            # Whole word
            (.+__{0}__.+)|      # Truly contains __<pyFragment>__ (Truly, so spare e.g. __name__)
            (^{0}__)|           # Starts with <pyFragment>__
            (__{0}$)|           # Ends with __<pyFragment>
            ((?<=\.){0}__)|     # Starts with '.<pyFragment>__'
            (__{0}(?=\.))       # Ends with '__<pyFragment>.'
        '''.format (sourceFragment), re.VERBOSE), targetFragment)

    def filterId (self, qualifiedId):   # Convention: only called at emission time
        if self.idFiltering:
            for aliaser in self.aliasers:
                qualifiedId = re.sub (aliaser [1], aliaser [2], qualifiedId)
        return qualifiedId

    def tabs (self, indentLevel = None):
        if indentLevel == None:
            indentLevel = self.indentLevel
        return indentLevel * '\t'

    def emit (self, fragment, *formatter):
        if (    # At the start of a new line
            not self.targetFragments or
            (self.targetFragments and self.targetFragments [-1] .endswith ('\n'))
        ):
            self.targetFragments.append (self.tabs ())

        fragment = fragment [:-1] .replace ('\n', '\n' + self.tabs ()) + fragment [-1]
        self.targetFragments.append (fragment.format (*formatter) .replace ('\n', self.lineNrString + '\n'))

    def indent (self):
        self.indentLevel += 1

    def dedent (self):
        self.indentLevel -= 1

    def inscope (self, node):
        # Called at visiting modules, classes and functions
        self.scopes.append (Scope (node))

    def descope (self):
        self.scopes.pop ()

    def getScope (self, *nodeTypes):
        if nodeTypes:
            for scope in reversed (self.scopes):
                if type (scope.node) in nodeTypes:
                    return scope
        else:
            return self.scopes [-1]

    def getAdjacentClassScopes (self, inMethod = False):
        # Work backward until finding an interruption in the chain
        # Needed to fix destructuring assignment in nested classes and to make super () work
        # The latter needs inMethod, since suported use of super () is directly or indirectly enclosed in a method body
        reversedClassScopes = []
        for scope in reversed (self.scopes):
            if inMethod:
                if type (scope.node) in (ast.FunctionDef, ast.AsyncFunctionDef):
                    continue
                else:
                    inMethod = False
                    
            if type (scope.node) != ast.ClassDef:
                break;
            reversedClassScopes.append (scope)
        return reversed (reversedClassScopes)

    def emitComma (self, index, blank = True):
        if self.noskipCodeGeneration and self.conditionalCodeGeneration and index:
            self.emit (', ' if blank else ',')

    def emitBeginTruthy (self):
        if self.allowConversionToTruthValue:
            self.emit ('__t__ (')

    def emitEndTruthy (self):
        if self.allowConversionToTruthValue:
            self.emit (')')

    def adaptLineNrString (self, node, offset = 0):
        if utils.commandArgs.map or utils.commandArgs.anno: # Under these conditions, appended line numbers will be stripped later, so they have to be there
            if hasattr (node, 'lineno'):
                lineNr = node.lineno + offset   # Use new line number
            else:
                lineNr = self.lineNr + offset   # Use 'cached' line nubmer

            self.lineNrString = str (sourcemaps.maxNrOfSourceLinesPerModule + lineNr) [1 : ]
        else:                                               # __pragma__ ('noanno') isn't enough to perform this else-clause and to later on take the 'no stripping' shortcut
                                                            # This is in the main module the first line will already have been instrumented
                                                            # So in that case each line is instrumented and instrumentation will be stripped later on
            self.lineNrString = ''

    def isDocString (self, stmt):
        return isinstance (stmt, ast.Expr) and isinstance (stmt.value, ast.Str)

    def emitSetDoc (self, stmt):
        self.emit (' .__setdoc__ (')
        self.visit (stmt)
        self.emit (')')

    def emitBody (self, body):  # First docstring already removed
        for stmt in body:
            if self.isDocString (stmt): # Further docstrings ignored
                pass
            else:
                self.visit (stmt)
                self.emit (';\n')
                
    def emitSubscriptAssign (self, target, value, emitPathIndices = lambda: None): 
        if type (target.slice) == ast.Index:        # Always overloaded
            if type (target.slice.value) == ast.Tuple:
                self.visit (target.value)
                self.emit ('.__setitem__ (')        # Free function tries .__setitem__ (overload) and [] (native)
                self.stripTuple = True
                self.visit (target.slice.value)
                self.emit (', ')
                self.visit (value)
                emitPathIndices ()
                self.emit (')')
            elif self.allowOperatorOverloading:     # Possibly overloaded LHS index dealth with here, is special case
                self.emit ('__setitem__ (')         # Free function tries .__setitem__ (overload) and [] (native)
                self.visit (target.value)
                self.emit (', ')
                self.visit (target.slice.value)
                self.emit (', ')
                self.visit (value)
                emitPathIndices ()
                self.emit (')')
            else:                                   # Non-overloaded LHS index just dealth with by visit_Subscript
                                                    # which is called indirectly here
                self.expectingNonOverloadedLhsIndex = True
                self.visit (target)
                self.emit (' = ')
                self.visit (value)
                emitPathIndices ()
        elif type (target.slice) == ast.Slice:
            if self.allowOperatorOverloading:
                self.emit ('__setslice__ (')        # Free function tries .__setitem__ (overload) and .__setslice__ (native)
                self.visit (target.value)
                self.emit (', ')
            else:
                self.visit (target.value)
                self.emit ('.__setslice__ (')

            if target.slice.lower == None:
                self.emit ('0')
            else:
                self.visit (target.slice.lower)
            self.emit (', ')

            if target.slice.upper == None:
                self.emit ('null')
            else:
                self.visit (target.slice.upper)
            self.emit (', ')

            if target.slice.step:
                self.visit (target.slice.step)
            else:
                self.emit ('null')                  # Must be null rather than 1, see Array.prototype.__setslice__
            self.emit (', ')

            self.visit (value)

            self.emit (')')
        elif type (target.slice) == ast.ExtSlice:   # Always overloaded
            self.visit (target.value)
            self.emit ('.__setitem__ (')            # Method, since extended slice access is always overloaded
            self.emit ('[')
            for index, dim in enumerate (target.slice.dims):
                self.emitComma (index)
                self.visit (dim)
            self.emit (']')
            self.emit (', ')
            self.visit (value)
            self.emit (')')

    def pushDecorator (self, node):
        if node.decorator_list:
            context = {
                'node': node,
                'scopes': self.scopes [1:],
                'opov': self.allowOperatorOverloading
            }
            self.classDecorators.insert (0, context)
            
    def emitDecorators (self, callable=None):

        def emitDecorator (sourceName, targetName, decorator):
            self.emit ('\n')
            self.visit (
                ast.Assign (
                    targets = [ast.Name (
                        id = sourceName,  # Rename to original callable
                        ctx = ast.Store
                    )],
                    value = (
                            # Simple decorator
                            ast.Call (  # Call to decorating callable
                                func = ast.Name (  # supplied literally by the name of the decorator
                                    id = decorator.id,
                                    ctx = ast.Load
                                ),
                                args = [ast.Name (  # Original callable as parameter, supplied by name
                                    id = targetName,
                                    ctx = ast.Load
                                )],
                                keywords = []
                            )
                        if type (decorator) == ast.Name else
                            # Decorator factory
                            ast.Call (  # Call to decorating callable
                                func = decorator,  # Supplied to call by factory callable which is the decorator
                                args = [ast.Name (  # Original callable as parameter, supplied by name
                                    id = targetName,
                                    ctx = ast.Load
                                )],
                                keywords = []
                            )
                    )
                )
            )

        if callable:
            for decorator in reversed (callable.decorator_list):
                emitDecorator (callable.name, callable.name, decorator)
        else:
            while self.classDecorators:
                context = self.classDecorators.pop ()
                oldOpov = self.allowOperatorOverloading
                self.allowOperatorOverloading = context ['opov']

                if type (context ['node']) in (ast.FunctionDef, ast.AsyncFunctionDef):
                    sourceName = '__impl__{}'.format (context ['node'] .name)
                else:
                    sourceName = context ['node'] .name

                targetName = sourceName
                classList = ''
                if context ['scopes']:
                    for scope in context ['scopes']:
                        self.inscope (scope.node)
                    classList = '.'.join ([_scope.node.name for _scope in self.getAdjacentClassScopes ()])
                    if classList:
                        targetName = '{}.{}'.format (classList, targetName)

                for decorator in reversed (context ['node'].decorator_list):
                    if not (type (decorator) == ast.Name and decorator.id in ('classmethod', 'staticmethod')):
                        emitDecorator (sourceName, targetName, decorator)
                        if classList:
                            self.emit (';')

                for _ in range (len (context ['scopes'])):
                    self.descope ()

                self.allowOperatorOverloading = oldOpov

    def nextTemp (self, name):
        if name in self.tempIndices:
            self.tempIndices [name] += 1
        else:
            self.tempIndices [name] = 0
        return self.getTemp (name)

    def skipTemp (self, name):
        self.skippedTemps.add (self.nextTemp (name))

    def skippedTemp (self, name):
        return self.getTemp (name) in self.skippedTemps

    def getTemp (self, name):
        if name in self.tempIndices:
            return '__{}{}__'.format (name, self.tempIndices [name])
        else:
            return None

    def prevTemp (self, name):
        if self.getTemp (name) in self.skippedTemps:
            self.skippedTemps.remove (self.getTemp (name))

        self.tempIndices [name] -= 1
        if self.tempIndices [name] < 0:
            del self.tempIndices [name]

    def useModule (self, name):
        self.module.program.importStack [-1][1] = self.lineNr   # Remember line nr of import statement for the error report
        result = self.module.program.provide (name)             # Must be done first because it can generate a healthy exception
        self.use.add (name)                                     # Must not be done if the healthy exception occurs
        return result

    def isCall (self, node, name):
        return type (node) == ast.Call and type (node.func) == ast.Name and node.func.id == name

    def getPragmaFromExpr (self, node):
        return (
            node.value.args
            if type (node) == ast.Expr and self.isCall (node.value, '__pragma__') else
            None
        )

    def visit (self, node):
        try:
            self.lineNr = node.lineno
        except:
            pass

        pragma = self.getPragmaFromExpr (node)

        if pragma:
            if pragma [0] .s == 'skip':
                self.noskipCodeGeneration = False
            elif pragma [0] .s == 'noskip':
                self.noskipCodeGeneration = True

            if pragma [0] .s in ('ifdef', 'ifndef'):
                defined = eval (    # Explained with __pragma__ ('js', ...)
                    compile (
                        ast.Expression (pragma [1]),
                        '<string>',
                        'eval'
                    ),
                    {},
                    {'__envir__': __base__.__envir__}
                ) in self.module.program.symbols

                if pragma [0] .s == 'ifdef':
                    self.conditionalCodeGeneration = defined
                else:
                    self.conditionalCodeGeneration = not defined
            elif pragma [0] .s == 'else':
                self.conditionalCodeGeneration = not self.conditionalCodeGeneration
            elif pragma [0] .s == 'endif':
                self.conditionalCodeGeneration = True

        if self.noskipCodeGeneration and self.conditionalCodeGeneration:
            ast.NodeVisitor.visit (self, node)

    def visit_arg (self, node):
        self.emit (self.filterId (node.arg))

    def visit_arguments (self, node):   # Visited for def's, not for calls
        self.emit ('(')

        for index, arg in enumerate (node.args):
            self.emitComma (index)
            self.visit (arg)

        # If there's a vararg or a kwarg, no formal parameter is emitted for it, it's just retrieved in the body
        # so def f (a, b=3, *x, c, d=4, **y, e, f = 5) generates function f (a, b, c, d, e, f), since x and y are never passed in positionally

        self.emit (') {{\n')

        self.indent ()  # Start of function body, the end is not in visit_arguments

        # Defaults for positional args (before *), only if not passed normally before this point
        # They can also be passed in as keyword args
        # If so, the keywords are filled in starting with the last positional arg
        # So after a keyword positional arg cannot follow a non-keyword positional arg
        # The kwarg transfer object may be the last of the actual params
        # It should not initialize a formal param, so it's overwritten by the default as well.
        for arg, expr in reversed (list (zip (reversed (node.args), reversed (node.defaults)))):
            if expr:
                self.emit ('if (typeof {0} == \'undefined\' || ({0} != null && {0} .hasOwnProperty ("__kwargtrans__"))) {{;\n', self.filterId (arg.arg))
                self.indent ()
                self.emit ('var {} = ', self.filterId (arg.arg))
                self.visit (expr)
                self.emit (';\n')
                self.dedent ()
                self.emit ('}};\n')

        # Defaults for kwonly args (after *), unconditionally, since they will be passed only after this point
        for arg, expr in zip (node.kwonlyargs, node.kw_defaults):
            if expr:
                self.emit ('var {} = ', self.filterId (arg.arg))
                self.visit (expr)
                self.emit (';\n')

        if self.allowKeywordArgs:
            # If there is a **kwargs arg, make a local to hold its calltime contents
            # This local is needed even if arguments.length == 0, it's just empty then but may be read or passed on
            if node.kwarg:
                self.emit ('var {} = dict ();\n', self.filterId (node.kwarg.arg))

            self.emit ('if (arguments.length) {{\n')
            self.indent ()

            # Store index of last actual param
            self.emit ('var {} = arguments.length - 1;\n', self.nextTemp ('ilastarg'))

            # Any calltime keyword args are passed in a special JavaScript-only kwarg transfer object, recognizable by attribute __kwargtrans__
            # If it's there, copy this special object into local var __allkwargs__
            # And lower __ilastarg__ by 1, since the last calltime arg wasn't a normal (Python) one
            # It's only known at call time if there are keyword arguments, unless there are no arguments at all, so allways have to generate this code
            self.emit ('if (arguments [{0}] && arguments [{0}].hasOwnProperty ("__kwargtrans__")) {{\n', self.getTemp ('ilastarg'))
            self.indent ()
            self.emit ('var {} = arguments [{}--];\n', self.nextTemp ('allkwargs'), self.getTemp ('ilastarg'))

            # kwarg transfer object may contain deftime defined keyword args, but also keyword args that are absorbed by **kwargs
            self.emit ('for (var {} in {}) {{\n', self.nextTemp ('attrib'), self.getTemp ('allkwargs'))
            self.indent ()

            # We'll make the distinction between normal keyword args and **kwargs keyword args in a switch
            if node.args + node.kwonlyargs or node.kwarg:
                self.emit ('switch ({}) {{\n', self.getTemp ('attrib'))
                self.indent ()

                # First generate a case for each normal keyword arg, generating a local for it
                for arg in node.args + node.kwonlyargs:
                    self.emit ('case \'{0}\': var {0} = {1} [{2}]; break;\n', self.filterId (arg.arg), self.getTemp ('allkwargs'), self.getTemp ('attrib'))

                # Then put the rest into the **kwargs local
                if node.kwarg:
                    self.emit ('default: {0} [{1}] = {2} [{1}];\n', self.filterId (node.kwarg.arg), self.getTemp ('attrib'), self.getTemp ('allkwargs'))

                self.dedent ()
                self.emit ('}}\n')  # switch..

            self.prevTemp ('allkwargs')
            self.prevTemp ('attrib')

            self.dedent ()
            self.emit ('}}\n')  # for (__attrib__..

            # Take out the __kwargstrans__ marker, to prevent it from being passed in to another call, leading to confusion there
            # So after being used once, the kwarg transfer object becomes an ordinary object
            if node.kwarg:
                self.emit ('delete {}.__kwargtrans__;\n', self.filterId (node.kwarg.arg))

            self.dedent ()
            self.emit ('}}\n')  # if (arguments [{0}]..

            # If there's a vararg, assign an array containing the remainder of the actual non keyword only params, except for the kwarg transfer object
            if node.vararg:
                # Slice starts at end of formal positional params, ends with last actual param, all actual keyword args are taken out into the kwarg transfer object
                self.emit (
                    'var {} = tuple ([].slice.apply (arguments).slice ({}, {} + 1));\n',
                    self.filterId (node.vararg.arg),
                    len (node.args),
                    self.getTemp ('ilastarg')
                )

            self.prevTemp ('ilastarg')

            self.dedent ()
            self.emit ('}}\n')  # if (arguments.length..
            self.emit ('else {{\n')
            self.indent ()
            if node.vararg:     # if there's a formal vararg param, even if there isn't an actual one
                self.emit ('var {} = tuple ();\n', self.filterId (node.vararg.arg))
            self.dedent ()
            self.emit ('}}\n')
        else:
            if node.vararg: # See above
                self.emit (
                    'var {} = tuple ([].slice.apply (arguments).slice ({}));\n',
                    self.filterId (node.vararg.arg),
                    len (node.args),
                )

    def visit_AnnAssign (self, node):
        if node.value != None:  # Rather than a node.value is a NameConstant with value None
            self.visit (
                ast.Assign (
                    [node.target],
                    node.value
                )
            )
                        
    def visit_Assert (self, node):
        if utils.commandArgs.dassert:
            self.emit ('assert (')
            self.visit (node.test)
            if node.msg:
                self.emit (', ')
                self.visit (node.msg)
            self.emit (');\n')

    def visit_Assign (self, node):       
        self.adaptLineNrString (node)

        targetLeafs = (ast.Attribute, ast.Subscript, ast.Name)

        def assignTarget (target, value, pathIndices = []):
            def emitPathIndices ():
                if pathIndices:
                    self.emit (' ')
                    for pathIndex in pathIndices:
                        self.emit ('[{}]'.format (pathIndex))
                else:   # Most frequent and simple case, only one atomary LHS
                    pass

            if type (target) == ast.Subscript:              # Only non-overloaded LHS index can be left to visit_Subscript
                self.emitSubscriptAssign (target, value, emitPathIndices)
            else:
                if isPropertyAssign and target.id != self.getTemp ('left'):
                    self.emit ('Object.defineProperty ({}, \'{}\', '.format (self.getScope () .node.name, target.id))
                    self.visit (value)
                    emitPathIndices ()
                    self.emit (');')
                else:
                    if type (target) == ast.Name:
                        if type (self.getScope () .node) == ast.ClassDef and target.id != self.getTemp ('left'):
                            self.emit ('{}.'.format ('.'.join ([scope.node.name for scope in self.getAdjacentClassScopes ()]))) # The target is a class attribute
                        elif target.id in self.getScope () .nonlocals:
                            pass
                        else:
                            self.emit ('var ')

                    self.visit (target)
                    self.emit (' = ')
                    self.visit (value)
                    emitPathIndices ()

        # Tuple assignment LHS tree walker
        # The target (LHS) guides the walk, so it determines the source indices
        # However if a target leaf is an LHS slice,
        # the actual assignment will involve iterating through an extra index level,
        # as [1, 2][1:1] = [2, 3] should give [1, 2, 3, 4] rather than [1, [2, 3], 4]
        # This extra target level is walked in the slice
        def walkTarget (expr, pathIndices):
            if type (expr) in targetLeafs:          # It's an LValue, matching an RHS leaf source
                self.emit (';\n')

                # Create and visit RHS node on the fly, to benefit from assignTarget
                assignTarget (expr, ast.Name (
                    id = self.getTemp ('left'),
                    ctx = ast.Load
                ), pathIndices)
            else:                                   # It's a sequence
                pathIndices.append (None)           # Add indexing level for that sequence
                for index, elt in enumerate (expr.elts):
                    pathIndices [-1] = index
                    walkTarget (elt, pathIndices)   # Walk deeper until finally pathIndices is used in emitting an RHS leaf
                pathIndices.pop ()                  # Remove the indexing level since we're done with that sequence

        def getIsPropertyAssign (value):
            if self.isCall (value, 'property'):
                return True
            else:
                try:    # Assume it's a tuple or a list of properties (and so recursively)
                    return getIsPropertyAssign (value.elts [0])

                except: # At this point it wasn't a property and also not a tuple or a list of properties
                    return False

        isPropertyAssign = type (self.getScope () .node) == ast.ClassDef and getIsPropertyAssign (node.value)
        # In transpiling to efficient JavaScript, we need a special, simplified case for properties
        # In JavaScript generating '=' for properties won't do, it has to be 'Object.defineProperty'
        # We can't look out for property installation at runtime, that would make all assignments slow
        # So we introduce the restriction that an assignment involves no properties at all or only properties
        # Also these properties have to use the 'property' function 'literally'
        # With these restrictions we can recognize property installation at compile time
        
        if len (node.targets) == 1 and type (node.targets [0]) in targetLeafs:
            # Fast shortcut for the most frequent and simple case
            assignTarget (node.targets [0], node.value)
        else:
            # Multiple RHS or tuple assignment, we need __tmp__, create assignment node on the fly and visit it
            self.visit (ast.Assign (
                targets = [ast.Name (
                    id = self.nextTemp ('left'),
                    ctx = ast.Store
                )],
                value = node.value
            ))

            for expr in node.targets:
                walkTarget (expr, [])

            self.prevTemp ('left')

    def visit_Attribute (self, node):
        if type (node.value) in (ast.BinOp, ast.BoolOp, ast.Compare):
            self.emit ('(')

        self.visit (node.value)

        if type (node.value) in (ast.BinOp, ast.BoolOp, ast.Compare):
            self.emit (')')

        self.emit ('.{}', self.filterId (node.attr))
        
    def visit_Await (self, node):
        self.emit ('await ')
        self.visit (node.value)

    def visit_AugAssign (self, node):
        if self.allowOperatorOverloading:
            rhsFunctionName = self.filterId (
                # Non-overloaded
                '__ipow__' if type (node.op) == ast.Pow else
                '__imatmul__' if type (node.op) == ast.MatMult else
                ('__ijsmod__' if self.allowJavaScriptMod else '__imod__') if type (node.op) == ast.Mod else
                
                # Overloaded arithmetic
                '__imul__' if type (node.op) == ast.Mult else
                '__idiv__' if type (node.op) == ast.Div else
                '__iadd__' if type (node.op) == ast.Add else
                '__isub__' if type (node.op) == ast.Sub else
                
                # Overloaded bitwise
                '__ilshift__' if type (node.op) == ast.LShift else
                '__irshift__' if type (node.op) == ast.RShift else
                '__ior__' if type (node.op) == ast.BitOr else
                '__ixor__' if type (node.op) == ast.BitXor else
                '__iand__' if type (node.op) == ast.BitAnd else
                
                'Never here'
            )
                
            rhsCall = ast.Call (
                func = ast.Name (
                    id = rhsFunctionName,
                    ctx = ast.Load
                ),
                args = [
                    node.target,
                    node.value
                ],
                keywords = []
            )
        
            if type (node.target) == ast.Subscript:              # Only non-overloaded LHS index can be left to visit_Subscript
                self.emitSubscriptAssign (node.target, rhsCall)
            else:
                if type (node.target) == ast.Name and not node.target.id in self.getScope () .nonlocals:
                    self.emit ('var ')

                self.visit (node.target)
                self.emit (' = ')
                self.visit (rhsCall)       
        elif (
            # Python //, @ and ** have no operator symbol in JavaScript, so <operator>= won't work
            type (node.op) in (ast.FloorDiv, ast.MatMult, ast.Pow)
            or
            # Python % (as opposed to JavaScript %) has no operator symbol in JavaScript, so <operator>= won't work
            (type (node.op) == ast.Mod and not self.allowJavaScriptMod)
            or
            # LHS is a call to __getitem__ or __getslice__, so <operator>= won't work
            (                         
                type (node.target) == ast.Subscript and (
                    type (node.target.slice) != ast.Index or
                    type (node.target.slice.value) == ast.Tuple
                )
            )
        ):
            # Just translate to binary operator node
            self.visit (ast.Assign (
                targets = [node.target],
                value = ast.BinOp (left = node.target, op = node.op, right = node.value)
            ))         
        else:   # No overloading in this branch
            self.expectingNonOverloadedLhsIndex = True
            self.visit (node.target)        # No need to emit var first, it has to exist already

            # Optimize for ++ and --
            if type (node.value) == ast.Num and node.value.n == 1:
                if type (node.op) == ast.Add:
                    self.emit ('++')
                    return
                elif type (node.op) == ast.Sub:
                    self.emit ('--')
                    return
            elif type (node.value) == ast.UnaryOp and type (node.value.operand) == ast.Num and node.value.operand.n == 1:
                if type (node.op) == ast.Add:
                    if type (node.value.op) == ast.UAdd:
                        self.emit ('++')
                        return
                    elif type (node.value.op) == ast.USub:
                        self.emit ('--')
                        return
                elif type (node.op) == ast.Sub:
                    if type (node.value.op) == ast.UAdd:
                        self.emit ('--')
                        return
                    elif type (node.value.op) == ast.USub:
                        self.emit ('++')
                        return

            self.emit (' {}= ', self.operators [type (node.op)][0])
            self.visit (node.value)

    def visit_BinOp (self, node):
        if type (node.op) == ast.FloorDiv:
            if self.allowOperatorOverloading:
                self.emit ('__floordiv__ (')
                self.visitSubExpr (node, node.left)
                self.emit (', ')
                self.visitSubExpr (node, node.right)
                self.emit (')')
            else:
                self.emit ('Math.floor (')
                self.visitSubExpr (node, node.left)
                self.emit (' / ')
                self.visitSubExpr (node, node.right)
                self.emit (')') 
        elif (
            type (node.op) in (ast.Pow, ast.MatMult) or
            (type (node.op) == ast.Mod and (self.allowOperatorOverloading or not self.allowJavaScriptMod)) or
            (type (node.op) in (
                ast.Mult, ast.Div, ast.Add, ast.Sub,
                ast.LShift, ast.RShift, ast.BitOr, ast.BitXor, ast.BitAnd
            ) and self.allowOperatorOverloading)
        ):
            self.emit ('{} ('.format (self.filterId (
                # Non-overloaded
                ('__floordiv__' if self.allowOperatorOverloading else 'Math.floor') if type (node.op) == ast.FloorDiv else
                ('__pow__' if self.allowOperatorOverloading else 'Math.pow') if type (node.op) == ast.Pow else
                '__matmul__' if type (node.op) == ast.MatMult else
                ('__jsmod__' if self.allowJavaScriptMod else '__mod__') if type (node.op) == ast.Mod else
                
                # Overloaded arithmetic
                '__mul__' if type (node.op) == ast.Mult else
                '__truediv__' if type (node.op) == ast.Div else
                '__add__' if type (node.op) == ast.Add else
                '__sub__' if type (node.op) == ast.Sub else
                
                # Overloaded bitwise
                '__lshift__' if type (node.op) == ast.LShift else
                '__rshift__' if type (node.op) == ast.RShift else
                '__or__' if type (node.op) == ast.BitOr else
                '__xor__' if type (node.op) == ast.BitXor else
                '__and__' if type (node.op) == ast.BitAnd else
                
                'Never here'
            )))
            self.visit (node.left)
            self.emit (', ')
            self.visit (node.right)
            self.emit (')')
        else:
            self.visitSubExpr (node, node.left)
            self.emit (' {} '.format (self.operators [type (node.op)][0]))
            self.visitSubExpr (node, node.right)

    def visit_BoolOp (self, node):
        for index, value in enumerate (node.values):
            if index:
                self.emit (' {} '.format (self.operators [type (node.op)][0]))

            if index < len (node.values) - 1:
                self.emitBeginTruthy ()

            self.visitSubExpr (node, value)

            if index < len (node.values) - 1:
                self.emitEndTruthy ()

    def visit_Break (self, node):
        if not self.skippedTemp ('break'):
            self.emit ('{} = true;\n', self.getTemp ('break'))
        self.emit ('break')
        
    def visit_Bytes (self, node):
        self.emit ('bytes (\'{}\')', node.s.decode ('ASCII'))

    def visit_Call (self, node):
        self.adaptLineNrString (node)

        def emitKwargTrans ():
            self.emit ('__kwargtrans__ (')

            hasSeparateKeyArgs = False
            hasKwargs = False
            for keyword in node.keywords:
                if keyword.arg:
                    hasSeparateKeyArgs = True
                else:
                    hasKwargs = True
                    break   # **kwargs is always the last arg

            if hasSeparateKeyArgs:
                if hasKwargs:
                    self.emit ('__merge__ (')
                self.emit ('{{')    # Allways if hasSeparateKeyArgs

            for keywordIndex, keyword in enumerate (node.keywords):
                if keyword.arg:
                    self.emitComma (keywordIndex)
                    self.emit ('{}: ', self.filterId (keyword.arg))
                    self.visit (keyword.value)
                else:
                    # It's the **kwargs arg, so the last arg
                    # In JavaScript this must be an expression denoting an Object
                    # The keyword args in there have to be added to the kwargs transfer object as well
                    if hasSeparateKeyArgs:
                        self.emit ('}}, ')
                    self.visit (keyword.value)

            if hasSeparateKeyArgs:
                if hasKwargs:
                    self.emit (')')     # Terminate merge
                else:
                    self.emit ('}}')    # Only if not terminated already because hasKwargs

            self.emit (')')
            
        def include (fileName):
            searchedIncludePaths = []
            for searchDir in self.module.program.moduleSearchDirs:
                filePath = '{}/{}'.format (searchDir, fileName)
                if os.path.isfile (filePath):
                    return tokenize.open (filePath) .read ()
                else:
                    searchedIncludePaths.append (filePath)
            else:
                raise utils.Error (
                    lineNr = self.lineNr,
                    message = '\n\tAttempt to include file: {}\n\tCan\'t find any of:\n\t\t{}\n'.format (
                        node.args [0], '\n\t\t'. join (searchedIncludePaths)
                    )
                )

        if type (node.func) == ast.Name:
            if node.func.id == 'type':
                self.emit ('py_typeof (')   # Don't use general alias, since this is the type operator, not the type metaclass
                self.visit (node.args [0])
                self.emit (')')
                return
            elif node.func.id == 'property':
                self.emit ('{0}.call ({1}, {1}.{2}'.format (node.func.id, self.getScope (ast.ClassDef) .node.name, self.filterId (node.args [0].id)))
                if len (node.args) > 1:
                    self.emit (', {}.{}'.format (self.getScope (ast.ClassDef) .node.name, node.args [1].id))
                self.emit (')')
                return
            elif node.func.id == 'globals':
                self.emit ('__globals__ (__all__)')
                return
            elif node.func.id == '__pragma__':
                if node.args [0] .s == 'alias':
                    self.aliasers.insert (0, self.getAliaser (node.args [1] .s, node.args [2].s))
                elif node.args [0] .s == 'noalias':
                    if len (node.args) == 1:
                        self.aliasers = []
                    else:
                        for index in reversed (range (len (self.aliasers))):
                            if self.aliasers [index][0] == node.args [1] .s:
                                self.aliasers.pop (index)

                elif node.args [0] .s == 'noanno':
                    self.allowDebugMap = False

                elif node.args [0] .s == 'fcall':
                    self.allowMemoizeCalls = True
                elif node.args [0] .s == 'nofcall':
                    self.allowMemoizeCalls = False

                elif node.args [0] .s == 'docat':
                    self.allowDocAttribs = True
                elif node.args [0] .s == 'nodocat':
                    self.allowDocAttribs = False

                elif node.args [0] .s == 'iconv':       # Automatic conversion to iterable supported
                    self.allowConversionToIterable = True
                elif node.args [0] .s == 'noiconv':     # Automatic conversion to iterable not supported
                    self.allowConversionToIterable = False

                elif node.args [0] .s == 'jsiter':      # Translate for ... in ... : ... literally to for (... in ...) {...},
                    self.allowJavaScriptIter = True     # to enable iterating JavaScript objects that are not dicts
                elif node.args [0] .s == 'nojsiter':    # Dictionary keys without quotes are identifiers
                    self.allowJavaScriptIter = False

                elif node.args [0] .s == 'jskeys':      # Dictionary keys without quotes are string literals
                    self.allowJavaScriptKeys = True
                elif node.args [0] .s == 'nojskeys':    # Dictionary keys without quotes are identifiers
                    self.allowJavaScriptKeys = False

                elif node.args [0] .s == 'keycheck':    # Nonexistent dict keys or list indices generate an exception
                    self.allowKeyCheck = True
                elif node.args [0] .s == 'nokeycheck':  # Nonexistent dict keys or list indices produce undefined values
                    self.allowKeyCheck = False

                elif node.args [0] .s == 'jsmod':       # % has JavaScript behaviour
                    self.allowJavaScriptMod = True
                elif node.args [0] .s == 'nojsmod':     # % has Python behaviour
                    self.allowJavaScriptMod = False

                elif node.args [0] .s == 'gsend':       # Replace send by next.value
                    self.replaceSend = True
                elif node.args [0] .s == 'nogsend':     # Don't replace send by next.value
                    self.replaceSend = False

                elif node.args [0] .s == 'tconv':       # Automatic conversion to truth value supported
                    self.allowConversionToTruthValue = True
                elif node.args [0] .s == 'notconv':     # Automatic conversion to truth value not supported
                    self.allowConversionToTruthValue = False

                elif node.args [0] .s == 'js':          # Include JavaScript code literally in the output
                    code = node.args [1] .s.format (* [
                        eval (
                            compile (
                                ast.Expression (arg),   # Code to compile (can be AST or source)
                                '<string>',             # Not read from a file
                                'eval'                  # Code is an expression, namely __include__  (<fileName>) in most cases
                            ),
                            {},
                            {'__include__': include}
                        )
                        for arg in node.args [2:]
                    ])
                    for line in code.split ('\n'):
                        self.emit ('{}\n', line)
                    
                elif node.args [0] .s == 'xtrans':       # Include code transpiled by external process in the output
                    try:
                        sourceCode = node.args [2] .s.format (* [
                            eval (
                                compile (
                                    ast.Expression (arg),   # Code to compile (can be AST or source)
                                    '<string>',             # Not read from a file
                                    'eval'                  # Code is an expression, namely __include__  (<fileName>) in most cases
                                ),
                                {},
                                {'__include__': include}
                            )
                            for arg in node.args [3:]
                        ])
                        workDir = '.'
                        for keyword in node.keywords:
                            if keyword.arg == 'cwd':
                                workDir = keyword.value.s
                        process = subprocess.Popen (
                            shlex.split(node.args [1] .s),
                            stdin = subprocess.PIPE,
                            stdout = subprocess.PIPE,
                            cwd = workDir
                        )
                        process.stdin.write ((sourceCode).encode ('utf8'))
                        process.stdin.close ()
                        while process.returncode is None:
                            process.poll ()
                        targetCode = process.stdout.read (). decode ('utf8'). replace ('\r\n', '\n')
                        for line in targetCode.split ('\n'):
                            self.emit ('{}\n', line)
                    except:
                        print (traceback.format_exc ())

                elif node.args [0] .s == 'kwargs':      # Start emitting kwargs code for FunctionDef's
                    self.allowKeywordArgs = True
                elif node.args [0] .s == 'nokwargs':    # Stop emitting kwargs code for FunctionDef's
                    self.allowKeywordArgs = False

                elif node.args [0] .s == 'opov':        # Overloading of a small sane subset of operators allowed
                    self.allowOperatorOverloading = True
                elif node.args [0] .s == 'noopov':      # Operloading of a small sane subset of operators disallowed
                    self.allowOperatorOverloading = False
                    
                elif node.args [0] .s == 'redirect':
                    if node.args [1] .s == 'stdout':
                        self.emit ('__stdout__ = \'{}\'', node.args [2])
                elif node.args [0] .s == 'noredirect':
                    if node.args [1] .s == 'stdout':
                        self.emit ('__stdout__ = \'__console__\'')
                        
                elif node.args [0] .s in ('skip', 'noskip', 'ifdef', 'ifndef', 'else', 'endif'):
                    pass                                # Easier dealth with on statement / expression level in self.visit
                    
                elif node.args [0] .s == 'xpath':
                    self.module.program.moduleSearchDirs [1 : 1] = [elt.s for elt in node.args [1] .elts]
                    
                else:
                    raise utils.Error (
                        lineNr = self.lineNr,
                        message = '\n\tUnknown pragma: {}'.format (
                            node.args [0] .s if type (node.args [0]) == ast.Str else node.args [0]
                        )
                    )
                return
            elif node.func.id == '__new__':
                self.emit ('new ')
                self.visit (node.args [0])
                return
            elif node.func.id == '__typeof__':
                self.emit ('typeof ')
                self.visit (node.args [0])
                return
            elif node.func.id == '__preinc__':
                self.emit ('++')
                self.visit (node.args [0])
                return
            elif node.func.id == '__postinc__':
                self.visit (node.args [0])
                self.emit ('++')
                return
            elif node.func.id == '__predec__':
                self.emit ('--')
                self.visit (node.args [0])
                return
            elif node.func.id == '__postdec__':
                self.visit (node.args [0])
                self.emit ('--')
                return

        elif (
            type (node.func) == ast.Attribute and 
            self.replaceSend and
            node.func.attr == 'send'                # Construct Attribute instead of bare Call node on the fly and visit it
        ):
            self.emit ('(function () {{return ')    # Encapsulate in function to prevent minifier complaining if value isn't used
            self.visit (ast.Attribute (
                value = ast.Call (
                    func = ast.Attribute (
                        value = ast.Name (
                            id = node.func.value.id,
                            ctx = ast.Load
                        ),
                        attr = 'js_next',
                        ctx = ast.Load
                    ),
                    args = node.args,
                    keywords = node.keywords
                ),
                attr = 'value',
                ctx = ast.Load
            ))
            self.emit ('}}) ()')
            return  # The newly created node was visited by a recursive call to visit_Call. This replaces the current visit.
            
        elif (
            type (node.func) == ast.Attribute and
            type (node.func.value) == ast.Call and
            type (node.func.value.func) == ast.Name and
            node.func.value.func.id == 'super'
        ):
            if node.func.value.args or node.func.value.keywords:
                raise utils.Error (
                    lineNr = self.lineNr,
                    message = '\n\tBuilt in function \'super\' with arguments not supported'
                )
                
            else:   # Construct node for __super__ (self, '<methodName>')(self, <params>) and visit it
                self.visit (
                    ast.Call (
                        func = ast.Call (
                            func = ast.Name (
                                id = '__super__',
                                ctx = ast.Load
                            ),
                            args = [
                                ast.Name (
                                    id = '.'.join ([scope.node.name for scope in self.getAdjacentClassScopes (True)]),
                                    ctx = ast.Load
                                ),
                                ast.Str (
                                    s = node.func.attr  # <methodName>
                                )
                            ],
                            keywords = []
                        ),
                        args = (
                            [
                                ast.Name (
                                    id = 'self',
                                    ctx = ast.Load
                                )
                            ] +
                            node.args                   # <normal part of params>
                        ),
                        keywords = node.keywords        # <keyword part of params>
                    )
                )
                return
        
        if (
            self.allowOperatorOverloading and       # If operator overloading and
            not (                                   # whe're not already in the __call__ that we generated on the fly,
                type (node.func) == ast.Name and
                node.func.id == '__call__'
            )
        ):
            self.visit (ast.Call (                  # generate __call__ node on the fly and visit it                                                            
                func = ast.Name (
                    id = '__call__',
                    ctx = ast.Load  # Don't use node.func.ctx since callable object decorators don't have a ctx, and they too the overloading mechanism
                ),
                args = (
                        [node.func, node.func.value] + node.args
                    if type (node.func) == ast.Attribute else
                        [
                            node.func,
                            ast.NameConstant (
                                value = None
                            )
                        ] + node.args
                ),
                keywords = node.keywords
            ))
            return  # The newly created node was visited by a recursive call to visit_Call. This replaces the current visit.

        self.visit (node.func)

        if self.module.program.javaScriptVersion >= 6:
            self.emit (' (')

            for index, expr in enumerate (node.args):
                self.emitComma (index)

                if type (expr) == ast.Starred:
                    self.emit ('...')

                self.visit (expr)

            if node.keywords:
                self.emitComma (len (node.args))
                emitKwargTrans ()

            self.emit (')')
        else:
            for index, expr in enumerate (node.args):
                if type (expr) == ast.Starred:
                    self.emit ('.apply (null, ')    # Note that in generated a.b.f (), a.b.f is a bound function already

                    for index, expr in enumerate (node.args):
                        if index:
                            self.emit ('.concat (')
                        if type (expr) == ast.Starred:
                            self.visit (expr)
                        else:
                            self.emit ('[')
                            self.visit (expr)
                            self.emit (']')
                        if index:
                            self.emit (')')

                    if node.keywords:
                        self.emit ('.concat ([')    # At least *args was present before this point
                        emitKwargTrans ()
                        self.emit ('])')

                    self.emit (')')
                    break;
            else:
                self.emit (' (')

                for index, expr in enumerate (node.args):
                    self.emitComma (index)
                    self.visit (expr)

                if node.keywords:
                    self.emitComma (len (node.args))
                    emitKwargTrans ()

                self.emit (')')

    def visit_ClassDef (self, node):
        self.adaptLineNrString (node)

        if type (self.getScope () .node) == ast.Module:
            self.all.add (node.name)

        if type (self.getScope () .node) == ast.ClassDef:
            self.emit (self.filterId ('\n{}:'.format (node.name)))
        else:
            self.emit ('var {} ='.format (self.filterId (node.name)))

        self.emit (' __class__ (\'{}\', [', self.filterId (node.name))
        if node.bases:
            for index, expr in enumerate (node.bases):
                try:
                    self.emitComma (index)
                    self.visit (expr)
                except Exception as exception:
                    utils.enhanceException (
                        exception,
                        lineNr = self.lineNr,
                        message = '\n\tInvalid base class'
                    )
        else:
            self.emit ('object')
        self.emit ('], {{')
        self.inscope (node)

        self.indent ()
        self.emit ('\n__module__: __name__,')
        classVarAssigns = []
        index = 0
        for stmt in node.body:
            if self.isDocString (stmt):
                pass
            elif type (stmt) in (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef):
                self.emitComma (index, False)
                self.visit (stmt)
                index += 1
            elif type (stmt) == ast.Assign:
                if (
                    len (stmt.targets) == 1 and type (stmt.targets [0]) == ast.Name and
                    not (type (stmt.value) == ast.Call and type (stmt.value.func) == ast.Name and stmt.value.func.id == 'property')
                ):
                    self.emitComma (index, False)
                    self.emit ('\n{}: ', self.filterId (stmt.targets [0] .id), stmt.targets)
                    self.visit (stmt.value)
                    self.adaptLineNrString (stmt)
                    index += 1
                else:
                    # Limitation: Can't properly deal with line number in this case
                    classVarAssigns.append (stmt)   # Has to be done after the class because tuple assignment requires the use of an algorithm.
                                                    # May also be a property assign
            elif self.getPragmaFromExpr (stmt):
                self.visit (stmt)               # If it's a pragma, just visit it
        self.dedent ()

        self.emit ('\n}}')

        if node.keywords:
            if node.keywords [0] .arg == 'metaclass':
                self.emit (', ')
                self.visit (node.keywords [0] .value)
            else:
                raise utils.Error (
                    lineNr = self.lineNr,
                    message = '\n\tUnknown keyword argument {} definition of class {}'.format (node.keywords [0] .arg, node.name)
                )

        self.emit (')')

        if self.allowDocAttribs and self.isDocString (node.body [0]):
            self.emitSetDoc (node.body [0])

        for index, classVarAssign in enumerate (classVarAssigns):
            self.emit (';\n')
            self.visit (classVarAssign)

        self.descope () # No earlier, class vars need it

        self.pushDecorator (node)

        if type (self.getScope (). node) != ast.ClassDef:  # If outer class emits decorators
            self.emitDecorators ()

    def visit_Compare (self, node):
        if len (node.comparators) > 1:
            self.emit ('(')

        left = node.left
        for index, (op, right) in enumerate (zip (node.ops, node.comparators)):
            if index:
                self.emit (' && ')

            if type (op) in (ast.In, ast.NotIn) or (self.allowOperatorOverloading and type (op) in (
                ast.Eq, ast.NotEq, ast.Lt, ast.LtE, ast.Gt, ast.GtE
            )):
                self.emit ('{} ('.format (self.filterId (
                
                    # Non-overloaded
                    '__in__' if type (op) == ast.In else
                    '!__in__' if type (op) == ast.NotIn else
                    
                    # Overloaded
                    '__eq__' if type (op) == ast.Eq else
                    '__ne__' if type (op) == ast.NotEq else
                    '__lt__' if type (op) == ast.Lt else
                    '__le__' if type (op) == ast.LtE else
                    '__gt__' if type (op) == ast.Gt else
                    '__ge__' if type (op) == ast.GtE else
                    
                    'Never here'
                )))
                self.visitSubExpr (node, left)
                self.emit (', ')
                self.visitSubExpr (node, right)
                self.emit (')')
            else:
                self.visitSubExpr (node, left)
                self.emit (' {0} '.format (self.operators [type (op)][0]))
                self.visitSubExpr (node, right)

            left = right

        if len (node.comparators) > 1:
            self.emit(')')

    def visit_Continue (self, node):
        self.emit ('continue')

    def visit_Delete (self, node):  # Currently dict element only, rest can be done with empty slice assignment
        for expr in node.targets:
            if type (expr) != ast.Name:
                self.emit ('delete ')
                self.visit (expr)
                self.emit (';\n')

    def visit_Dict (self, node):
        if not self.allowJavaScriptKeys:                    # If we don't want JavaScript treatment of keys, for literal keys it doesn't make a difference
            for key in node.keys:
                if not type (key) in (ast.Str, ast.Num):    # but if there's only one non-literal key there's a difference, and all keys are treated the Python way
                    self.emit ('dict ([')
                    for index, (key, value) in enumerate (zip (node.keys, node.values)):
                        self.emitComma (index)
                        self.emit ('[')
                        self.visit (key)                    # In a JavaScript list, name is evaluated as variable or function call to produce a key
                        self.emit (', ')
                        self.visit (value)
                        self.emit (']')
                    self.emit ('])')
                    return

        if self.allowJavaScriptIter:
            self.emit ('{{')
        else:
            self.emit ('dict ({{')                              # Since we didn't return, we want identifier keys to be treated as string literals
        for index, (key, value) in enumerate (zip (node.keys, node.values)):
            self.emitComma (index)
            self.idFiltering = False                        # The key may be a string or an identifier, the latter normally would be filtered, which we don't want
            self.visit (key)                                # In a JavaScript object literal, an identifier isn't evaluated but literally taken to be a key.
            self.idFiltering = True
            self.emit (': ')
            self.visit (value)

        if self.allowJavaScriptIter:
            self.emit ('}}')
        else:
            self.emit ('}})')

    def visit_DictComp (self, node):
        self.visit_ListComp (node, isDict = True)

    def visit_Expr (self, node):
        self.visit (node.value)

    def visit_For (self, node):
        self.adaptLineNrString (node)

        if node.orelse and not self.allowJavaScriptIter:
            self.emit ('var {} = false;\n', self.nextTemp ('break'))
        else:
            self.skipTemp ('break')

        # Special case optimization: iterating through range with constant literal step, start and stop can be expressions
        # Starred args not allowed, since what's 'behind' the star is only known at runtime, so there's no saying wether there's a constant literal step
        optimize = (
            type (node.target) == ast.Name and  # Since 'var' is emitted, target must not yet exist, so e.g. not be element of array
            self.isCall (node.iter, 'range') and
                type (node.iter.args [0]) != ast.Starred and (
                len (node.iter.args) < 3 or                         # Constant step of 1
                type (node.iter.args [2]) == ast.Num or (           # Positive constant step
                    type (node.iter.args [2]) == ast.UnaryOp and    # Negative constant step
                    type (node.iter.args [2] .operand) == ast.Num
                )
            )
        )

        if self.allowJavaScriptIter:
            self.emit ('for (var ')
            self.visit (node.target)
            self.emit (' in ')
            self.visit (node.iter)
            self.emit (') {{\n')
            self.indent ()
        elif optimize:
            step = (
                    1
                if len (node.iter.args) <= 2 else
                    node.iter.args [2] .n
                if type (node.iter.args [2]) == ast.Num else
                    node.iter.args [2] .operand .n
                if type (node.iter.args [2] .op) == ast.UAdd else
                    -node.iter.args [2] .operand .n
            )

            self.emit ('for (var ')
            self.visit (node.target)
            self.emit (' = ')
            self.visit (node.iter.args [0] if len (node.iter.args) > 1 else ast.Num (0))
            self.emit ('; ')
            self.visit (node.target)
            self.emit (' < ' if step > 0 else ' > ')
            self.visit (node.iter.args [1] if len (node.iter.args) > 1 else node.iter.args [0])
            self.emit ('; ')
            self.visit (node.target)
            if step == 1:
                self.emit ('++')
            elif step == -1:
                self.emit ('--')
            elif step >= 0:
                self.emit ( ' += {}', step)
            else:
                self.emit ( ' -= {}', -step)

            self.emit (') {{\n')
            self.indent ()

        elif (
            self.module.program.javaScriptVersion >= 6 and  # Supports for ... of
            not self.allowOperatorOverloading               # No overloaded __len__ c.q. __getitem__
        ):
            self.emit ('for (var ')
            self.stripTuples = True
            self.visit (node.target)
            self.stripTuples = False
            self.emit (' of ')

            if self.allowConversionToIterable:
                self.emit ('__i__ (')

            self.visit (node.iter)

            if self.allowConversionToIterable:
                self.emit (')')

            self.emit (') {{\n')
            self.indent ()

            ''' (1)
            # In the code below, destructuring assignment is done explicitly rather than left to JavaScript
            # It is left here as a comment until it becomes more clear that JavaScript destructuring suffices in all cases

            # Produce universal iterator (something with a Python '__next__' and a JavaScript 'next') from iterable by calling py_iter from __builtin__
            self.emit ('var {} = {} (', self.nextTemp ('iterator'), self.filterId ('iter'))
            self.visit (node.iter)
            self.emit (');\n')

            self.emit ('while (true) {{\n')
            self.indent ()

            # Create and visit Assign node on the fly to benefit from tupple assignment
            self.emit ('try {{')
            self.indent ()
            self.visit (ast.Assign (
                targets = [node.target],        # As in Python: for <target> in ...
                value = ast.Call (              # Result of calling 'next' on the just produced universal iterator
                    func = ast.Name (
                        id = 'next',
                        ctx = ast.Load
                    ),
                    args = [ast.Name (
                        id = self.getTemp ('iterator'),
                        ctx = ast.Load
                    )],
                    keywords = []
                )
            ))
            self.emit (';')
            self.dedent ()
            self.emit ('}} ')
            self.emit ('catch (exception) {{')  # Assume 'StopIteration' thrown, iterator exhausted
            self.indent ()
            self.emit ('break;')
            self.dedent ()
            self.emit ('}}\n')
            '''

        else:
            self.emit ('var {} = ', self.nextTemp ('iterable'))
            self.visit (node.iter)
            self.emit (';\n')

            if self.allowConversionToIterable:
                self.emit ('{0} = __i__ ({0});\n', self.getTemp ('iterable'))

            self.emit ('for (var {0} = 0; {0} < len ({1}); {0}++) {{\n', self.nextTemp ('index'), self.getTemp ('iterable'))
            self.indent ()

            # Create and visit Assign node on the fly to benefit from tupple assignment
            self.visit (ast.Assign (
                targets = [node.target],
                value = ast.Subscript (
                    value = ast.Name (
                        id = self.getTemp ('iterable'),
                        ctx = ast.Load
                    ),
                    slice = ast.Index (
                        value = ast.Num (
                            n = self.getTemp ('index')
                        )
                    ),
                    ctx = ast.Load
                )
            ))
            self.emit (';\n')


        self.emitBody (node.body)
        self.dedent ()
        self.emit ('}}\n')

        if not (self.allowJavaScriptIter or optimize):
            if (
                self.module.program.javaScriptVersion >= 6 and  # Supports for ... of
                not self.allowOperatorOverloading               # No overloaded __len__ c.q. __getitem__
            ):
                pass
                # self.prevTemp ('iterator')    # Leave in for now, see outcommented fragment (1) above
            else:
                self.prevTemp ('index')
                self.prevTemp ('iterable')

        if node.orelse:
            self.adaptLineNrString (node.orelse, 1) # One off, since 'else' doesn't have it's own node and line nr

            self.emit ('if (!{}) {{\n', self.getTemp ('break'))
            self.prevTemp ('break')

            self.indent ()
            self.emitBody (node.orelse)
            self.dedent ()

            self.emit ('}}\n')
        else:
            self.prevTemp ('break')

    def visit_FormattedValue (self, node):
        self.visit (node.value)
        
    def visit_AsyncFunctionDef (self, node):
        self.visit_FunctionDef (node, async = True)
    
    def visit_FunctionDef (self, node, async = False):
        def emitScopedBody ():
            self.inscope (node)

            self.emitBody (node.body)
            self.dedent ()

            if self.getScope (ast.AsyncFunctionDef if async else ast.FunctionDef) .containsYield:
                # !!! Check: yield forbidden in AsyncFunctionDef
                self.targetFragments.insert (yieldStarIndex, '*')

            self.descope ()

        if not node.name == '__pragma__':   # Don't generate code for the dummy pragma definition starting the extraLines in utils
                                            # Pragma should never be defined, except once directly in JavaScript to support __pragma__ ('<all>')
                                            # The rest of its use is only at compile time

            if type (self.getScope () .node) in (ast.Module, ast.FunctionDef, ast.AsyncFunctionDef):  # Global or function scope, so it's no method
                if type (self.getScope () .node) == ast.Module:
                    self.all.add (node.name)

                self.adaptLineNrString (node)
                self.emit ('var {} = {}function', self.filterId (node.name), 'async ' if async else '')

                yieldStarIndex = len (self.targetFragments)
                yieldStarLevel = self.indentLevel

                self.emit (' ')
                self.visit (node.args)
                emitScopedBody ()
                self.emit ('}}')

                if self.allowDocAttribs and self.isDocString (node.body [0]):
                    self.emitSetDoc (node.body [0])

                self.emitDecorators (node)
            else:                                                           # Class scope, so it's a method and needs the currying mechanism
                self.emit ('\n')
                self.adaptLineNrString (node)

                decorate = False
                isClassMethod = False
                isStaticMethod = False

                if node.decorator_list:
                    for decorator in node.decorator_list:
                        if type(decorator) == ast.Name:
                            nameCheck = decorator.id
                        elif type(decorator) == ast.Call:
                            nameCheck = decorator.func.id

                        if nameCheck == 'classmethod':
                            isClassMethod = True
                        elif nameCheck == 'staticmethod':
                            isStaticMethod = True
                        else:
                            decorate = True

                if decorate:
                    self.emit ('__impl__{}: {}function', node.name, 'async ' if async else '')
                else:
                    if isClassMethod:
                        self.emit ('get {} () {{return __getcm__ (this, {}function', self.filterId (node.name),  'async ' if async else '')
                    elif isStaticMethod:
                        self.emit ('get {} () {{return {}function', self.filterId (node.name), 'async ' if async else '')
                    else:
                        self.emit ('get {} () {{return __get__ (this, {}function', self.filterId (node.name),  'async ' if async else '')

                yieldStarIndex = len (self.targetFragments)
                yieldStarLevel = self.indentLevel

                self.emit (' ')
                self.visit (node.args)
                emitScopedBody ()
                self.emit ('}}')

                if self.allowDocAttribs and self.isDocString (node.body [0]):
                    self.emitSetDoc (node.body [0])

                if self.allowMemoizeCalls:
                    self.emit (', \'{}\'', node.name)   # Name will be used as attribute name to add bound function to instance

                if decorate:
                    self.emit (',\n')
                    if isClassMethod:
                        self.emit ('get {} () {{return __getcm__ (this, this.__impl__{});}}', self.filterId (node.name), node.name)
                    elif isStaticMethod:
                        self.emit ('get {} () {{return __getsm__ (this, this.__impl__{});}}', self.filterId (node.name), node.name)
                    else:
                        self.emit ('get {} () {{return __get__ (this, this.__impl__{});}}', self.filterId (node.name), node.name)
                    self.pushDecorator (node)
                else:
                    if isStaticMethod:
                        self.emit ('}}')
                    else:
                        self.emit (');}}')

                if node.name == '__iter__':
                    self.emit (',\n[Symbol.iterator] () {{return this.__iter__ ()}}')

                if node.name == '__next__':
                    self.emit (',\nnext: __jsUsePyNext__')  # ??? Shouldn't this be a property, to allow bound method pointers

    def visit_GeneratorExp (self, node):
        # Currently generator expressions are just iterators on lists.
        # It's important that they aren't just lists,
        # because the each for ... in ... would create a fresh iterator from it, prevening exhaustion.
        # Since a list comp is an iterator itself, just copies will be created,
        # which will exhaust together.
        self.visit_ListComp (node, isGenExp = True)

    def visit_Global (self, node):
        self.getScope (ast.FunctionDef, ast.AsyncFunctionDef) .nonlocals.update (node.names)

        # raise utils.Error (
            # lineNr = self.lineNr,
            # message = '\n\tKeyword \'global\' not supported, use \'nonlocal\' instead, or make variable attribute of \'window\'\n'
        # )

    def visit_If (self, node):
        self.adaptLineNrString (node)

        self.emit ('if (')
        self.emitBeginTruthy ()
        self.visit (node.test)
        self.emitEndTruthy ()
        self.emit (') {{\n')

        self.indent ()
        self.emitBody (node.body)
        self.dedent ()

        self.emit ('}}\n')

        if node.orelse:
            if len (node.orelse) == 1 and node.orelse [0].__class__.__name__ == 'If':
                # elif statement, we stay on the same line, no need to call adaptLineNrString
                self.emit ('else ')
                self.visit (node.orelse [0])
            else:
                self.adaptLineNrString (node.orelse, 1) # One off, since 'else' doesn't have it's own node and line nr
                
                self.emit ('else {{\n')
                self.indent ()
                self.emitBody (node.orelse)
                self.dedent ()
                self.emit ('}}\n')

    def visit_IfExp (self, node):
        self.emit ('(')
        self.emitBeginTruthy ()
        self.visit (node.test)
        self.emitEndTruthy ()
        self.emit (' ? ')
        self.visit (node.body)
        self.emit (' : ')
        self.visit (node.orelse)
        self.emit (')')

    def visit_Import (self, node):  # Import .. can only import modules
        self.adaptLineNrString (node)

        names = [alias for alias in node.names if not alias.name.startswith (self.stubsName)]

        if not names:
            return

        for index, alias in enumerate (names):
            try:
                self.useModule (alias.name)
            except Exception as exception:
                utils.enhanceException (
                    exception,
                    lineNr = self.lineNr,
                    message = '\n\tCan\'t import module \'{}\''.format (alias.name)
                )

            if alias.asname:
                self.emit ('var {} =  __init__ (__world__.{})', self.filterId (alias.asname), self.filterId (alias.name))
            else:
                aliasSplit = alias.name.split ('.', 1)
                head = aliasSplit [0]
                tail = aliasSplit [1] if len (aliasSplit) > 1 else ''

                self.importHeads.add (head)
                self.emit ('__nest__ ({}, \'{}\', __init__ (__world__.{}))', self.filterId (head), self.filterId (tail), self.filterId (alias.name))

            if index < len (names) - 1:
                self.emit (';\n')

    def visit_ImportFrom (self, node):  # From .. import can import modules or entities in modules
        self.adaptLineNrString (node)

        if node.module.startswith (self.stubsName):
            return

        try:
            # N.B. It's ok to call a modules __init__ multiple times, see __core__.mod.js
            for index, alias in enumerate (node.names):
                if alias.name == '*':                                           # * Never refers to modules, only to entities in modules
                    if len (node.names) > 1:
                        raise Error (
                            lineNr = node.lineno,
                            message = '\n\tCan\'t import module \'{}\''.format (alias.name)
                        )

                    module = self.useModule (node.module)

                    # Import everything
                    for index, name in enumerate (module.all):
                        self.emit ('var {0} = __init__ (__world__.{1}).{0}', self.filterId (name), self.filterId (node.module))
                        if index < len (module.all) - 1:
                            self.emit (';\n')

                    if type (self.getScope ().node) == ast.Module:
                        # And export everything imported, if we are not importing inside of a function.
                        if not utils.commandArgs.xconfimp or self.module.metadata.filePrename == '__init__':
                            self.all.update (module.all)
                else:
                    # Import something
                    # N.B. The emits in the try and except clauses have different placement of brackets
                    try:                                                        # Try if alias.name denotes a module
                        self.useModule ('{}.{}'.format (node.module, alias.name))

                        if alias.asname:
                            self.emit ('var {} = __init__ (__world__.{}.{})', self.filterId (alias.asname), self.filterId (node.module), self.filterId (alias.name))
                        else:
                            self.emit ('var {0} = __init__ (__world__.{1}.{0})', self.filterId (alias.name), self.filterId (node.module))
                    except:                                                     # If it doesn't it denotes an entity inside a module
                        self.useModule (node.module)

                        if alias.asname:
                            self.emit ('var {} = __init__ (__world__.{}).{}', self.filterId (alias.asname), self.filterId (node.module), self.filterId (alias.name))
                        else:
                            self.emit ('var {0} = __init__ (__world__.{1}).{0}', self.filterId (alias.name), self.filterId (node.module))

                    if type (self.getScope ().node) == ast.Module:
                        # And export that something imported
                        if not utils.commandArgs.xconfimp or self.module.metadata.filePrename == '__init__':
                            if alias.asname:
                                self.all.add (alias.asname)
                            else:
                                self.all.add (alias.name)

                    if index < len (node.names) - 1:
                        self.emit (';\n')
        except Exception as exception:
            utils.enhanceException (
                exception,
                lineNr = self.lineNr,
                message = '\n\tCan\'t import from module \'{}\''.format (node.module)
            )

    def visit_JoinedStr (self, node):
        self.emit (repr (''.join ([value.s if type (value) == ast.Str else '{{}}' for value in node.values])))
        self.emit ('.format (')
        index = 0
        for value in node.values:
            if type (value) == ast.FormattedValue:
                self.emitComma (index)
                self.visit (value)
                index += 1
        self.emit (')')

    def visit_Lambda (self, node):
        self.emit ('(function __lambda__ ',)    # Extra () needed to make it callable at definition time
        self.visit (node.args)
        self.emit ('return ')
        self.visit (node.body)
        self.dedent ()
        self.emit (';\n}})')

    def visit_List (self, node):
        self.emit ('list ([')
        for index, elt in enumerate (node.elts):
            self.emitComma (index)
            self.visit (elt)
        self.emit ('])')

    def visit_ListComp (self, node, isSet = False, isDict = False, isGenExp = False):
        elts = []
        bodies = [[]]

        # Create and visit For node on the fly to benefit from tupple assignment
        # The For node creates an Assign node on the fly, to get this done
        def nestLoops (generators):
            for comprehension in generators:
                target = comprehension.target
                iter = comprehension.iter

                # Make room for body of this for
                bodies.append ([])
                # Append this for to previous body
                bodies [-2].append (ast.For (target, iter, bodies [-1], []))

                for expr in comprehension.ifs:
                    test = expr

                    # Make room for body of this if
                    bodies.append ([])
                    # Append this if to previous body
                    bodies [-2].append (ast.If (
                        test = test,
                        body = bodies [-1],
                        orelse = []
                    ))

            bodies [-1].append (
                # Nodes to generate __accu<i>__.append (<elt>)
                ast.Call (
                    func = ast.Attribute (
                        value = ast.Name (
                            id = self.getTemp ('accu'),
                            ctx = ast.Load),
                        attr = 'append',
                        ctx = ast.Load
                    ),
                    args = [
                            ast.List (
                                elts = [node.key, node.value],
                                ctx = ast.Load
                            )
                        if isDict else
                            node.elt
                    ],
                    keywords = []
                )
            )

            self.visit (
                bodies [0][0]
            )

        self.emit ('(function () {{\n')
        self.inscope (ast.FunctionDef ())
        self.indent ()
        self.emit ('var {} = [];\n', self.nextTemp ('accu'))
        nestLoops (node.generators [:]) # Leave original in intact, just for neatness
        self.emit (
            'return {}{}{};\n',
            'set (' if isSet else 'dict (' if isDict else '{} ('.format (self.filterId ('iter')) if isGenExp else '' ,
            self.getTemp ('accu'),
            ')' if isSet or isDict or isGenExp else ''
        )
        self.prevTemp ('accu')
        self.dedent ()
        self.descope ()
        self.emit ('}}) ()')

    def visit_Module (self, node):
        self.adaptLineNrString (node)

        self.inscope (node)
        self.indent ()
        if self.module.metadata.name == self.module.program.mainModuleName:
            self.emit ('(function () {{\n')
            self.indent ()
        else:
            self.emit ('__nest__ (\n')
            self.indent ()
            self.emit ('__all__,\n')
            self.emit ('\'{}\', {{\n', self.filterId (self.module.metadata.name))
            self.indent ()
            self.emit ('__all__: {{\n')
            self.indent ()
            self.emit ('__inited__: false,\n')
            self.emit ('__init__: function (__all__) {{\n')
            self.indent ()

        importHeadsIndex = len (self.targetFragments)
        importHeadsLevel = self.indentLevel
        
        self.emit ('var __name__ = \'{}\';\n', self.module.metadata.getStandardName ())
        self.all.add ('__name__')

        for stmt in node.body:
            if self.isDocString (stmt):
                if not self.docString:  # Remember first docstring seen (may not be first statement, because of a.o. __pragma__ ('docat')
                    self.docString = stmt
            else:
                self.visit (stmt)
                self.emit (';\n')

            # As soon as doc attribs are allowed, emit only once the first one ever seen, to match CPython behaviour
            if not self.docStringEmitted and self.allowDocAttribs and self.docString:
                self.emit ('var __doc__ = ')
                self.visit (self.docString)
                self.emit (';\n')
                self.all.add ('__doc__')
                self.docStringEmitted = True

        if self.use:
            self.use = sorted (self.use)
            self.emit ('__pragma__ (\'<use>\' +\n') # Only the last occurence of <use> and </use> are special.
            self.indent ()
            for name in self.use:
                self.emit ('\'{}\' +\n', name)
            self.dedent ()
            self.emit ('\'</use>\')\n')

        if self.all:
            self.all = sorted (self.all)
            self.emit ('__pragma__ (\'<all>\')\n')  # Only the last occurence of <all> and </all> are special.
            self.indent ()
            for name in self.all:
                self.emit ('__all__.{0} = {0};\n', self.filterId (name))
            self.dedent ()
            self.emit ('__pragma__ (\'</all>\')\n')

        self.dedent ()

        if self.module.metadata.name == self.module.program.mainModuleName:
            self.emit ('}}) ();\n')
        else:
            self.emit ('}}\n')
            self.dedent ()
            self.emit ('}}\n')
            self.dedent ()
            self.emit ('}}\n')
            self.dedent ()
            self.emit (');\n')
            self.dedent ()

        self.targetFragments.insert (importHeadsIndex, ''.join ([
            '{}var {} = {{}};{}\n'.format (self.tabs (importHeadsLevel), self.filterId (head), self.lineNrString)
            for head in sorted (self.importHeads)
        ]))
        self.descope ()

    def visit_Name (self, node):
        if node.id == '__file__':
            self.visit (ast.Str (s = self.module.metadata.sourcePath))
            return

        elif node.id == '__filename__':
            path = os.path.split (self.module.metadata.sourcePath)
            fileName = path [1]
            
            if fileName.startswith ('__init__'):
                subDir = os.path.split (path [0])
                fileName = os.path.join (subDir [1], fileName)
                
            self.visit (ast.Str (s = fileName))
            return

        elif node.id == '__line__':
            self.visit (ast.Num (n = self.lineNr))
            return

        elif type (node.ctx) == ast.Store:
            if type (self.getScope () .node) == ast.Module:
                self.all.add (node.id)

        self.emit (self.filterId (node.id))

    def visit_NameConstant (self, node):
        self.emit (self.nameConsts [node.value])

    def visit_Nonlocal (self, node):
        self.getScope (ast.FunctionDef, ast.AsyncFunctionDef) .nonlocals.update (node.names)

    def visit_Num (self, node):
        self.emit ('complex (0, {})'.format (node.n.imag) if type (node.n) == complex else '{}'.format (node.n))

    def visit_Pass (self, node):
        self.adaptLineNrString (node)

        self.emit ('// pass')

    def visit_Raise (self, node):
        self.adaptLineNrString (node)

        if node.exc:
            # Create an exception object with a temporary name
            self.emit ('var {} = ', self.nextTemp ('except'))
            self.visit (node.exc)
            self.emit (';\n')
        else:
            # We want to rethrow, so we must be in a catch block, so the 'current exception' with name self.getTemp ('except') will exist already
            pass

        # Optionally add a __cause__ attribute to it
        self.emit ('{}.__cause__ = ', self.getTemp ('except'))
        if node.cause:
            self.visit (node.cause)
        else:
            self.emit ('null')

        self.emit (';\n')

        # Throw the exception
        self.emit ('throw {}', self.getTemp ('except'))

        # Relinquish the temporary name if we own it
        if node.exc:
            self.prevTemp ('except')

    def visit_Return (self, node):
        self.adaptLineNrString (node)

        self.emit ('return ')
        if node.value:
            self.visit (node.value)

    def visit_Set (self, node):
        self.emit ('new set ([')
        for index, elt in enumerate (node.elts):
            self.emitComma (index)
            self.visit (elt)
        self.emit ('])')

    def visit_SetComp (self, node):
        self.visit_ListComp (node, isSet = True)

    def visit_Slice (self, node):   # Only visited for dims as part of ExtSlice
        self.emit ('tuple ([')

        if node.lower == None:
            self.emit ('0')
        else:
            self.visit (node.lower)

        self.emit (', ')

        if node.upper == None:
            self.emit ('null')
        else:
            self.visit (node.upper)

        self.emit (', ')

        if node.step == None:
            self.emit ('1')
        else:
            self.visit (node.step)

        self.emit ('])')

    def visit_Str (self, node):
        self.emit ('{}', repr (node.s)) # Use repr (node.s) as second, rather than first parameter, since node.s may contain {}

    # Visited for RHS index, non-overloaded LHS index, RHS slice and RHS extended slice
    # LHS slice and overloaded LHS index are dealth with directy in visit_Assign, since the RHS is needed for them also
    def visit_Subscript (self, node):
        if type (node.slice) == ast.Index:
            if type (node.slice.value) == ast.Tuple:    # Always overloaded, it must be an RHS index
                self.visit (node.value)
                self.emit ('.__getitem__ (')
                self.stripTuple = True
                self.visit (node.slice.value)
                self.emit (')')
            elif self.allowOperatorOverloading:         # It must be an RHS index
                self.emit ('__getitem__ (')             # Free function tries .__getitem__ (overload) and [] (native)
                self.visit (node.value)
                self.emit (', ')
                self.visit (node.slice.value)
                self.emit (')')
            else:                                       # It may be an LHS or RHS index
                try:
                    isRhsIndex = not self.expectingNonOverloadedLhsIndex
                    self.expectingNonOverloadedLhsIndex = False
                    if isRhsIndex and self.allowKeyCheck:
                        self.emit ('__k__ (')
                        self.visit (node.value)
                        self.emit (', ')
                        self.visit (node.slice.value)
                        self.emit (')')
                    else:
                        self.visit (node.value)
                        self.emit (' [')
                        self.visit (node.slice.value)
                        self.emit (']')
                except:
                    print (traceback.format_exc ())
        elif type (node.slice) == ast.Slice:
            if self.allowOperatorOverloading:
                self.emit ('__getslice__ (')            # Free function, tries .__getitem__ (overload) and .__getslice__ (native)
                self.visit (node.value)
                self.emit (', ')
            else:
                self.visit (node.value)
                self.emit ('.__getslice__ (')

            if node.slice.lower == None:
                self.emit ('0')
            else:
                self.visit (node.slice.lower)
            self.emit (', ')

            if node.slice.upper == None:
                self.emit ('null')
            else:
                self.visit (node.slice.upper)
            self.emit (', ')

            if node.slice.step == None:
                self.emit ('1')
            else:
                self.visit (node.slice.step)

            self.emit (')')
        elif type (node.slice) == ast.ExtSlice:         # Always overloaded
            self.visit (node.value)
            self.emit ('.__getitem__ (')                # Method, since extended slice access is always overloaded
            self.emit ('[')
            for index, dim in enumerate (node.slice.dims):
                self.emitComma (index)
                self.visit (dim)
            self.emit (']')
            self.emit (')')

    def visit_Try (self, node):
        self.adaptLineNrString (node)

        self.emit ('try {{\n')
        self.indent ()
        self.emitBody (node.body)

        if node.orelse:
            self.emit ('try {{\n')
            self.indent ()
            self.emitBody (node.orelse)
            self.dedent ()
            self.emit ('}}\n')
            self.emit ('catch ({}) {{\n', self.nextTemp ('except'))
            self.emit ('}}\n')
            self.prevTemp ('except')

        self.dedent ()
        self.emit ('}}\n')

        if node.handlers:                           # try ... finally (without any catch) is also valid Python
            self.emit ('catch ({}) {{\n', self.nextTemp ('except'))
            self.indent ()

            for index, excepthandler in enumerate (node.handlers):
                if index:
                    self.emit ('else ')             # Never here after a catch all

                if excepthandler.type:
                    self.emit ('if (isinstance ({}, ', self.getTemp ('except'))
                    self.visit (excepthandler.type)
                    self.emit (')) {{\n')
                    self.indent ()

                    if excepthandler.name:
                        self.emit ('var {} = {};\n', excepthandler.name, self.getTemp ('except'))

                    self.emitBody (excepthandler.body)

                    self.dedent ()
                    self.emit ('}}\n')
                else:                               # Catch all, swallowing no problem
                    self.emitBody (excepthandler.body)
                    break
            else:                                   # No catch all, avoid swallowing exception
                self.emit ('else {{\n')
                self.indent ()
                self.emit ('throw {};\n', self.getTemp ('except'))
                self.dedent ()
                self.emit ('}}\n')

            self.dedent ()
            self.prevTemp ('except')
            self.emit ('}}\n')

        if node.finalbody:
            self.emit ('finally {{\n')
            self.indent ()
            self.emitBody (node.finalbody)
            self.dedent ()
            self.emit ('}}\n')

    def visit_Tuple (self, node):
        keepTuple = not (self.stripTuple or self.stripTuples)       # Tuples used as indices are stripped for speed
        self.stripTuple = False             # Tuples used as indices are stripped for speed, only strip first tuple encountered
                                            # Tuples used as assignment target in a JavaScript 6 for-loop are stripped for correctness, not only first
        if keepTuple:
            self.emit ('tuple (')

        self.emit ('[')
        for index, elt in enumerate (node.elts):
            self.emitComma (index)
            self.visit (elt)

        self.emit (']')

        if keepTuple:
            self.emit (')')

    def visit_UnaryOp (self, node):
        if self.allowOperatorOverloading and type (node.op) == ast.USub:
            self.emit ('{} ('.format (self.filterId ('__neg__' )))
            self.visit (node.operand)
            self.emit (')')
        else:
            self.emit (self.operators [type (node.op)][0])
            self.emitBeginTruthy ()
            self.visitSubExpr (node, node.operand)
            self.emitEndTruthy ()

    def visit_While (self, node):
        self.adaptLineNrString (node)

        if node.orelse:
            self.emit ('var {} = false;\n', self.nextTemp ('break'))
        else:
            self.skipTemp ('break')

        self.emit ('while (')
        self.emitBeginTruthy ()
        self.visit (node.test)
        self.emitEndTruthy ()
        self.emit (') {{\n')

        self.indent ()
        self.emitBody (node.body)
        self.dedent ()

        self.emit ('}}\n')

        if node.orelse:
            self.adaptLineNrString (node.orelse, 1) # One off, since 'else' doesn't have it's own node and line nr

            self.emit ('if (!{}) {{\n', self.getTemp ('break'))
            self.prevTemp ('break')

            self.indent ()
            self.emitBody (node.orelse)
            self.dedent ()

            self.emit ('}}\n')
        else:
            self.prevTemp ('break')

    def visit_With (self, node):
        self.adaptLineNrString (node)

        for withitem in node.items:
            self.visit (withitem.optional_vars)
            self.emit (' = ')
            self.visit (withitem.context_expr)
            self.emit (';\n')

        self.emitBody (node.body)

        for withitem in node.items:
            self.visit (withitem.optional_vars)
            self.emit ('.close ()')

    def visit_Yield (self, node):
        self.getScope (ast.FunctionDef, ast.AsyncFunctionDef) .containsYield = True
        self.emit ('yield')
        if (node.value != None):
            self.emit (' ')
            self.visit (node.value)
            
    def visit_YieldFrom (self, node):
        self.getScope (ast.FunctionDef, ast.AsyncFunctionDef) .containsYield = True
        self.emit ('yield* ')
        self.visit (node.value)
