svgui/pyjs/pyjs.py
changeset 3359 2c924cf26161
parent 3358 7478d0c0dc1c
child 3360 746e3e3f6537
equal deleted inserted replaced
3358:7478d0c0dc1c 3359:2c924cf26161
     1 #!/usr/bin/env python
       
     2 # Copyright 2006 James Tauber and contributors
       
     3 #
       
     4 # Licensed under the Apache License, Version 2.0 (the "License");
       
     5 # you may not use this file except in compliance with the License.
       
     6 # You may obtain a copy of the License at
       
     7 #
       
     8 #     http://www.apache.org/licenses/LICENSE-2.0
       
     9 #
       
    10 # Unless required by applicable law or agreed to in writing, software
       
    11 # distributed under the License is distributed on an "AS IS" BASIS,
       
    12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       
    13 # See the License for the specific language governing permissions and
       
    14 # limitations under the License.
       
    15 #
       
    16 # pylint: disable=no-absolute-import,bad-python3-import
       
    17 
       
    18 from __future__ import print_function
       
    19 import sys
       
    20 import compiler
       
    21 from compiler import ast
       
    22 import os
       
    23 import copy
       
    24 from builtins import str as text
       
    25 from past.builtins import basestring
       
    26 from six.moves import cStringIO
       
    27 
       
    28 # the standard location for builtins (e.g. pyjslib) can be
       
    29 # over-ridden by changing this.  it defaults to sys.prefix
       
    30 # so that on a system-wide install of pyjamas the builtins
       
    31 # can be found in e.g. {sys.prefix}/share/pyjamas
       
    32 #
       
    33 # over-rides can be done by either explicitly modifying
       
    34 # pyjs.prefix or by setting an environment variable, PYJSPREFIX.
       
    35 
       
    36 prefix = sys.prefix
       
    37 
       
    38 if 'PYJSPREFIX' in os.environ:
       
    39     prefix = os.environ['PYJSPREFIX']
       
    40 
       
    41 # pyjs.path is the list of paths, just like sys.path, from which
       
    42 # library modules will be searched for, for compile purposes.
       
    43 # obviously we don't want to use sys.path because that would result
       
    44 # in compiling standard python modules into javascript!
       
    45 
       
    46 path = [os.path.abspath('')]
       
    47 
       
    48 if 'PYJSPATH' in os.environ:
       
    49     for p in os.environ['PYJSPATH'].split(os.pathsep):
       
    50         p = os.path.abspath(p)
       
    51         if os.path.isdir(p):
       
    52             path.append(p)
       
    53 
       
    54 # this is the python function used to wrap native javascript
       
    55 NATIVE_JS_FUNC_NAME = "JS"
       
    56 
       
    57 UU = ""
       
    58 
       
    59 PYJSLIB_BUILTIN_FUNCTIONS = ("cmp",
       
    60                              "map",
       
    61                              "filter",
       
    62                              "dir",
       
    63                              "getattr",
       
    64                              "setattr",
       
    65                              "hasattr",
       
    66                              "int",
       
    67                              "float",
       
    68                              "str",
       
    69                              "repr",
       
    70                              "range",
       
    71                              "len",
       
    72                              "hash",
       
    73                              "abs",
       
    74                              "ord",
       
    75                              "chr",
       
    76                              "enumerate",
       
    77                              "min",
       
    78                              "max",
       
    79                              "bool",
       
    80                              "type",
       
    81                              "isinstance")
       
    82 
       
    83 PYJSLIB_BUILTIN_CLASSES = ("BaseException",
       
    84                            "Exception",
       
    85                            "StandardError",
       
    86                            "StopIteration",
       
    87                            "AttributeError",
       
    88                            "TypeError",
       
    89                            "KeyError",
       
    90                            "LookupError",
       
    91                            "list",
       
    92                            "dict",
       
    93                            "object",
       
    94                            "tuple")
       
    95 
       
    96 
       
    97 def pyjs_builtin_remap(name):
       
    98     # XXX HACK!
       
    99     if name == 'list':
       
   100         name = 'List'
       
   101     if name == 'object':
       
   102         name = '__Object'
       
   103     if name == 'dict':
       
   104         name = 'Dict'
       
   105     if name == 'tuple':
       
   106         name = 'Tuple'
       
   107     return name
       
   108 
       
   109 
       
   110 # XXX: this is a hack: these should be dealt with another way
       
   111 # however, console is currently the only global name which is causing
       
   112 # problems.
       
   113 PYJS_GLOBAL_VARS = ("console")
       
   114 
       
   115 # This is taken from the django project.
       
   116 # Escape every ASCII character with a value less than 32.
       
   117 JS_ESCAPES = (
       
   118     ('\\', r'\x5C'),
       
   119     ('\'', r'\x27'),
       
   120     ('"', r'\x22'),
       
   121     ('>', r'\x3E'),
       
   122     ('<', r'\x3C'),
       
   123     ('&', r'\x26'),
       
   124     (';', r'\x3B')
       
   125     ) + tuple([('%c' % z, '\\x%02X' % z) for z in range(32)])
       
   126 
       
   127 
       
   128 def escapejs(value):
       
   129     """Hex encodes characters for use in JavaScript strings."""
       
   130     for bad, good in JS_ESCAPES:
       
   131         value = value.replace(bad, good)
       
   132     return value
       
   133 
       
   134 
       
   135 def uuprefix(name, leave_alone=0):
       
   136     name = name.split(".")
       
   137     name = name[:leave_alone] + map(lambda x: "__%s" % x, name[leave_alone:])
       
   138     return '.'.join(name)
       
   139 
       
   140 
       
   141 class Klass(object):
       
   142 
       
   143     klasses = {}
       
   144 
       
   145     def __init__(self, name, name_):
       
   146         self.name = name
       
   147         self.name_ = name_
       
   148         self.klasses[name] = self
       
   149         self.functions = set()
       
   150 
       
   151     def set_base(self, base_name):
       
   152         self.base = self.klasses.get(base_name)
       
   153 
       
   154     def add_function(self, function_name):
       
   155         self.functions.add(function_name)
       
   156 
       
   157 
       
   158 class TranslationError(Exception):
       
   159     def __init__(self, message, node):
       
   160         Exception.__init__(self)
       
   161         self.message = "line %s:\n%s\n%s" % (node.lineno, message, node)
       
   162 
       
   163     def __str__(self):
       
   164         return self.message
       
   165 
       
   166 
       
   167 def strip_py(name):
       
   168     return name
       
   169 
       
   170 
       
   171 def mod_var_name_decl(raw_module_name):
       
   172     """ function to get the last component of the module e.g.
       
   173         pyjamas.ui.DOM into the "namespace".  i.e. doing
       
   174         "import pyjamas.ui.DOM" actually ends up with _two_
       
   175         variables - one pyjamas.ui.DOM, the other just "DOM".
       
   176         but "DOM" is actually local, hence the "var" prefix.
       
   177 
       
   178         for PyV8, this might end up causing problems - we'll have
       
   179         to see: gen_mod_import and mod_var_name_decl might have
       
   180         to end up in a library-specific module, somewhere.
       
   181     """
       
   182     name = raw_module_name.split(".")
       
   183     if len(name) == 1:
       
   184         return ''
       
   185     child_name = name[-1]
       
   186     return "var %s = %s;\n" % (child_name, raw_module_name)
       
   187 
       
   188 
       
   189 def gen_mod_import(parentName, importName, dynamic=1):
       
   190     # pyjs_ajax_eval("%(n)s.cache.js", null, true);
       
   191     return """
       
   192     pyjslib.import_module(sys.loadpath, '%(p)s', '%(n)s', %(d)d, false);
       
   193     """ % ({'p': parentName, 'd': dynamic, 'n': importName}) + \
       
   194         mod_var_name_decl(importName)
       
   195 
       
   196 
       
   197 class Translator(object):
       
   198 
       
   199     def __init__(self, mn, module_name, raw_module_name, src, debug, mod, output,
       
   200                  dynamic=0, optimize=False,
       
   201                  findFile=None):
       
   202 
       
   203         if module_name:
       
   204             self.module_prefix = module_name + "."
       
   205         else:
       
   206             self.module_prefix = ""
       
   207         self.raw_module_name = raw_module_name
       
   208         src = src.replace("\r\n", "\n")
       
   209         src = src.replace("\n\r", "\n")
       
   210         src = src.replace("\r",   "\n")
       
   211         self.src = src.split("\n")
       
   212         self.debug = debug
       
   213         self.imported_modules = []
       
   214         self.imported_modules_as = []
       
   215         self.imported_js = set()
       
   216         self.top_level_functions = set()
       
   217         self.top_level_classes = set()
       
   218         self.top_level_vars = set()
       
   219         self.local_arg_stack = [[]]
       
   220         self.output = output
       
   221         self.imported_classes = {}
       
   222         self.method_imported_globals = set()
       
   223         self.method_self = None
       
   224         self.nextTupleAssignID = 1
       
   225         self.dynamic = dynamic
       
   226         self.optimize = optimize
       
   227         self.findFile = findFile
       
   228 
       
   229         if module_name.find(".") >= 0:
       
   230             vdec = ''
       
   231         else:
       
   232             vdec = 'var '
       
   233         self.printo(UU+"%s%s = function (__mod_name__) {" % (vdec, module_name))
       
   234 
       
   235         self.printo("    if("+module_name+".__was_initialized__) return;")
       
   236         self.printo("    "+UU+module_name+".__was_initialized__ = true;")
       
   237         self.printo(UU+"if (__mod_name__ == null) __mod_name__ = '%s';" % (mn))
       
   238         self.printo(UU+"%s.__name__ = __mod_name__;" % (raw_module_name))
       
   239 
       
   240         decl = mod_var_name_decl(raw_module_name)
       
   241         if decl:
       
   242             self.printo(decl)
       
   243 
       
   244         if self.debug:
       
   245             haltException = self.module_prefix + "HaltException"
       
   246             self.printo(haltException + ' = function () {')
       
   247             self.printo('  this.message = "Program Halted";')
       
   248             self.printo('  this.name = "' + haltException + '";')
       
   249             self.printo('}')
       
   250             self.printo('')
       
   251             self.printo(haltException + ".prototype.__str__ = function()")
       
   252             self.printo('{')
       
   253             self.printo('return this.message ;')
       
   254             self.printo('}')
       
   255 
       
   256             self.printo(haltException + ".prototype.toString = function()")
       
   257             self.printo('{')
       
   258             self.printo('return this.name + ": \\"" + this.message + "\\"";')
       
   259             self.printo('}')
       
   260 
       
   261             isHaltFunction = self.module_prefix + "IsHaltException"
       
   262             self.printo(""")
       
   263     %s = function (s) {
       
   264       var suffix="HaltException";
       
   265       if (s.length < suffix.length) {
       
   266         //alert(s + " " + suffix);
       
   267         return false;
       
   268       } else {
       
   269         var ss = s.substring(s.length, (s.length - suffix.length));
       
   270         //alert(s + " " + suffix + " " + ss);
       
   271         return ss == suffix;
       
   272       }
       
   273     }
       
   274                 """ % isHaltFunction)
       
   275         for child in mod.node:
       
   276             if isinstance(child, ast.Function):
       
   277                 self.top_level_functions.add(child.name)
       
   278             elif isinstance(child, ast.Class):
       
   279                 self.top_level_classes.add(child.name)
       
   280 
       
   281         for child in mod.node:
       
   282             if isinstance(child, ast.Function):
       
   283                 self._function(child, False)
       
   284             elif isinstance(child, ast.Class):
       
   285                 self._class(child)
       
   286             elif isinstance(child, ast.Import):
       
   287                 importName = child.names[0][0]
       
   288                 if importName == '__pyjamas__':  # special module to help make pyjamas modules loadable in the python interpreter
       
   289                     pass
       
   290                 elif importName.endswith('.js'):
       
   291                     self.imported_js.add(importName)
       
   292                 else:
       
   293                     self.add_imported_module(strip_py(importName))
       
   294             elif isinstance(child, ast.From):
       
   295                 if child.modname == '__pyjamas__':  # special module to help make pyjamas modules loadable in the python interpreter
       
   296                     pass
       
   297                 else:
       
   298                     self.add_imported_module(child.modname)
       
   299                     self._from(child)
       
   300             elif isinstance(child, ast.Discard):
       
   301                 self._discard(child, None)
       
   302             elif isinstance(child, ast.Assign):
       
   303                 self._assign(child, None, True)
       
   304             elif isinstance(child, ast.AugAssign):
       
   305                 self._augassign(child, None)
       
   306             elif isinstance(child, ast.If):
       
   307                 self._if(child, None)
       
   308             elif isinstance(child, ast.For):
       
   309                 self._for(child, None)
       
   310             elif isinstance(child, ast.While):
       
   311                 self._while(child, None)
       
   312             elif isinstance(child, ast.Subscript):
       
   313                 self._subscript_stmt(child, None)
       
   314             elif isinstance(child, ast.Global):
       
   315                 self._global(child, None)
       
   316             elif isinstance(child, ast.Printnl):
       
   317                 self._print(child, None)
       
   318             elif isinstance(child, ast.Print):
       
   319                 self._print(child, None)
       
   320             elif isinstance(child, ast.TryExcept):
       
   321                 self._tryExcept(child, None)
       
   322             elif isinstance(child, ast.Raise):
       
   323                 self._raise(child, None)
       
   324             elif isinstance(child, ast.Stmt):
       
   325                 self._stmt(child, None)
       
   326             else:
       
   327                 raise TranslationError("unsupported type (in __init__)", child)
       
   328 
       
   329         # Initialize all classes for this module
       
   330         # self.printo("__"+self.modpfx()+\
       
   331         #          "classes_initialize = function() {\n")
       
   332         # for className in self.top_level_classes:
       
   333         #    self.printo("\t"+UU+self.modpfx()+"__"+className+"_initialize();")
       
   334         # self.printo("};\n")
       
   335 
       
   336         self.printo("return this;\n")
       
   337         self.printo("}; /* end %s */ \n" % module_name)
       
   338 
       
   339     def printo(self, *args):
       
   340         print(*args, file=self.output)
       
   341 
       
   342     def module_imports(self):
       
   343         return self.imported_modules + self.imported_modules_as
       
   344 
       
   345     def add_local_arg(self, varname):
       
   346         local_vars = self.local_arg_stack[-1]
       
   347         if varname not in local_vars:
       
   348             local_vars.append(varname)
       
   349 
       
   350     def add_imported_module(self, importName):
       
   351 
       
   352         if importName in self.imported_modules:
       
   353             return
       
   354         self.imported_modules.append(importName)
       
   355         name = importName.split(".")
       
   356         if len(name) != 1:
       
   357             # add the name of the module to the namespace,
       
   358             # but don't add the short name to imported_modules
       
   359             # because then the short name would be attempted to be
       
   360             # added to the dependencies, and it's half way up the
       
   361             # module import directory structure!
       
   362             child_name = name[-1]
       
   363             self.imported_modules_as.append(child_name)
       
   364         self.printo(gen_mod_import(self.raw_module_name,
       
   365                                    strip_py(importName),
       
   366                                    self.dynamic))
       
   367 
       
   368     def _default_args_handler(self, node, arg_names, current_klass,
       
   369                               output=None):
       
   370         if len(node.defaults):
       
   371             output = output or self.output
       
   372             default_pos = len(arg_names) - len(node.defaults)
       
   373             if arg_names and arg_names[0] == self.method_self:
       
   374                 default_pos -= 1
       
   375             for default_node in node.defaults:
       
   376                 if isinstance(default_node, ast.Const):
       
   377                     default_value = self._const(default_node)
       
   378                 elif isinstance(default_node, ast.Name):
       
   379                     default_value = self._name(default_node, current_klass)
       
   380                 elif isinstance(default_node, ast.UnarySub):
       
   381                     default_value = self._unarysub(default_node, current_klass)
       
   382                 else:
       
   383                     raise TranslationError("unsupported type (in _method)", default_node)
       
   384 
       
   385                 default_name = arg_names[default_pos]
       
   386                 default_pos += 1
       
   387                 self.printo("    if (typeof %s == 'undefined') %s=%s;" % (default_name, default_name, default_value))
       
   388 
       
   389     def _varargs_handler(self, node, varargname, arg_names, current_klass):
       
   390         self.printo("    var", varargname, '= new pyjslib.Tuple();')
       
   391         self.printo("    for(var __va_arg="+str(len(arg_names))+"; __va_arg < arguments.length; __va_arg++) {")
       
   392         self.printo("        var __arg = arguments[__va_arg];")
       
   393         self.printo("        "+varargname+".append(__arg);")
       
   394         self.printo("    }")
       
   395 
       
   396     def _kwargs_parser(self, node, function_name, arg_names, current_klass):
       
   397         if len(node.defaults) or node.kwargs:
       
   398             default_pos = len(arg_names) - len(node.defaults)
       
   399             if arg_names and arg_names[0] == self.method_self:
       
   400                 default_pos -= 1
       
   401             self.printo(function_name+'.parse_kwargs = function (', ", ".join(["__kwargs"]+arg_names), ") {")
       
   402             for _default_node in node.defaults:
       
   403                 # default_value = self.expr(default_node, current_klass)
       
   404                 # if isinstance(default_node, ast.Const):
       
   405                 #     default_value = self._const(default_node)
       
   406                 # elif isinstance(default_node, ast.Name):
       
   407                 #     default_value = self._name(default_node)
       
   408                 # elif isinstance(default_node, ast.UnarySub):
       
   409                 #     default_value = self._unarysub(default_node, current_klass)
       
   410                 # else:
       
   411                 #     raise TranslationError("unsupported type (in _method)", default_node)
       
   412 
       
   413                 default_name = arg_names[default_pos]
       
   414                 self.printo("    if (typeof %s == 'undefined')" % (default_name))
       
   415                 self.printo("        %s=__kwargs.%s;" % (default_name, default_name))
       
   416                 default_pos += 1
       
   417 
       
   418             # self._default_args_handler(node, arg_names, current_klass)
       
   419             if node.kwargs:
       
   420                 arg_names += ["pyjslib.Dict(__kwargs)"]
       
   421             self.printo("    var __r = "+"".join(["[", ", ".join(arg_names), "]"])+";")
       
   422             if node.varargs:
       
   423                 self._varargs_handler(node, "__args", arg_names, current_klass)
       
   424                 self.printo("    __r.push.apply(__r, __args.getArray())")
       
   425             self.printo("    return __r;")
       
   426             self.printo("};")
       
   427 
       
   428     def _function(self, node, local=False):
       
   429         if local:
       
   430             function_name = node.name
       
   431             self.add_local_arg(function_name)
       
   432         else:
       
   433             function_name = UU + self.modpfx() + node.name
       
   434 
       
   435         arg_names = list(node.argnames)
       
   436         normal_arg_names = list(arg_names)
       
   437         if node.kwargs:
       
   438             kwargname = normal_arg_names.pop()
       
   439         if node.varargs:
       
   440             varargname = normal_arg_names.pop()
       
   441         declared_arg_names = list(normal_arg_names)
       
   442         if node.kwargs:
       
   443             declared_arg_names.append(kwargname)
       
   444 
       
   445         function_args = "(" + ", ".join(declared_arg_names) + ")"
       
   446         self.printo("%s = function%s {" % (function_name, function_args))
       
   447         self._default_args_handler(node, normal_arg_names, None)
       
   448 
       
   449         local_arg_names = normal_arg_names + declared_arg_names
       
   450 
       
   451         if node.varargs:
       
   452             self._varargs_handler(node, varargname, declared_arg_names, None)
       
   453             local_arg_names.append(varargname)
       
   454 
       
   455         # stack of local variable names for this function call
       
   456         self.local_arg_stack.append(local_arg_names)
       
   457 
       
   458         for child in node.code:
       
   459             self._stmt(child, None)
       
   460 
       
   461         # remove the top local arg names
       
   462         self.local_arg_stack.pop()
       
   463 
       
   464         # we need to return null always, so it is not undefined
       
   465         lastStmt = [p for p in node.code][-1]
       
   466         if not isinstance(lastStmt, ast.Return):
       
   467             if not self._isNativeFunc(lastStmt):
       
   468                 self.printo("    return null;")
       
   469 
       
   470         self.printo("};")
       
   471         self.printo("%s.__name__ = '%s';\n" % (function_name, node.name))
       
   472 
       
   473         self._kwargs_parser(node, function_name, normal_arg_names, None)
       
   474 
       
   475     def _return(self, node, current_klass):
       
   476         expr = self.expr(node.value, current_klass)
       
   477         # in python a function call always returns None, so we do it
       
   478         # here too
       
   479         self.printo("    return " + expr + ";")
       
   480 
       
   481     def _break(self, node, current_klass):
       
   482         self.printo("    break;")
       
   483 
       
   484     def _continue(self, node, current_klass):
       
   485         self.printo("    continue;")
       
   486 
       
   487     def _callfunc(self, v, current_klass):
       
   488 
       
   489         if isinstance(v.node, ast.Name):
       
   490             if v.node.name in self.top_level_functions:
       
   491                 call_name = self.modpfx() + v.node.name
       
   492             elif v.node.name in self.top_level_classes:
       
   493                 call_name = self.modpfx() + v.node.name
       
   494             elif v.node.name in self.imported_classes:
       
   495                 call_name = self.imported_classes[v.node.name] + '.' + v.node.name
       
   496             elif v.node.name in PYJSLIB_BUILTIN_FUNCTIONS:
       
   497                 call_name = 'pyjslib.' + v.node.name
       
   498             elif v.node.name in PYJSLIB_BUILTIN_CLASSES:
       
   499                 name = pyjs_builtin_remap(v.node.name)
       
   500                 call_name = 'pyjslib.' + name
       
   501             elif v.node.name == "callable":
       
   502                 call_name = "pyjslib.isFunction"
       
   503             else:
       
   504                 call_name = v.node.name
       
   505             call_args = []
       
   506         elif isinstance(v.node, ast.Getattr):
       
   507             attr_name = v.node.attrname
       
   508 
       
   509             if isinstance(v.node.expr, ast.Name):
       
   510                 call_name = self._name2(v.node.expr, current_klass, attr_name)
       
   511                 call_args = []
       
   512             elif isinstance(v.node.expr, ast.Getattr):
       
   513                 call_name = self._getattr2(v.node.expr, current_klass, attr_name)
       
   514                 call_args = []
       
   515             elif isinstance(v.node.expr, ast.CallFunc):
       
   516                 call_name = self._callfunc(v.node.expr, current_klass) + "." + v.node.attrname
       
   517                 call_args = []
       
   518             elif isinstance(v.node.expr, ast.Subscript):
       
   519                 call_name = self._subscript(v.node.expr, current_klass) + "." + v.node.attrname
       
   520                 call_args = []
       
   521             elif isinstance(v.node.expr, ast.Const):
       
   522                 call_name = self.expr(v.node.expr, current_klass) + "." + v.node.attrname
       
   523                 call_args = []
       
   524             else:
       
   525                 raise TranslationError("unsupported type (in _callfunc)", v.node.expr)
       
   526         else:
       
   527             raise TranslationError("unsupported type (in _callfunc)", v.node)
       
   528 
       
   529         call_name = strip_py(call_name)
       
   530 
       
   531         kwargs = []
       
   532         star_arg_name = None
       
   533         if v.star_args:
       
   534             star_arg_name = self.expr(v.star_args, current_klass)
       
   535 
       
   536         for ch4 in v.args:
       
   537             if isinstance(ch4, ast.Keyword):
       
   538                 kwarg = ch4.name + ":" + self.expr(ch4.expr, current_klass)
       
   539                 kwargs.append(kwarg)
       
   540             else:
       
   541                 arg = self.expr(ch4, current_klass)
       
   542                 call_args.append(arg)
       
   543 
       
   544         if kwargs:
       
   545             fn_args = ", ".join(['{' + ', '.join(kwargs) + '}']+call_args)
       
   546         else:
       
   547             fn_args = ", ".join(call_args)
       
   548 
       
   549         if kwargs or star_arg_name:
       
   550             if not star_arg_name:
       
   551                 star_arg_name = 'null'
       
   552             try:
       
   553                 call_this, method_name = call_name.rsplit(".", 1)
       
   554             except ValueError:
       
   555                 # Must be a function call ...
       
   556                 return ("pyjs_kwargs_function_call("+call_name+", " +
       
   557                         star_arg_name + ", ["+fn_args+"]" + ")")
       
   558             else:
       
   559                 return ("pyjs_kwargs_method_call("+call_this+", '"+method_name+"', " +
       
   560                         star_arg_name + ", ["+fn_args+"]" + ")")
       
   561         else:
       
   562             return call_name + "(" + ", ".join(call_args) + ")"
       
   563 
       
   564     def _print(self, node, current_klass):
       
   565         if self.optimize:
       
   566             return
       
   567         call_args = []
       
   568         for ch4 in node.nodes:
       
   569             arg = self.expr(ch4, current_klass)
       
   570             call_args.append(arg)
       
   571 
       
   572         self.printo("pyjslib.printFunc([", ', '.join(call_args), "],", int(isinstance(node, ast.Printnl)), ");")
       
   573 
       
   574     def _tryExcept(self, node, current_klass):
       
   575         if len(node.handlers) != 1:
       
   576             raise TranslationError("except statements in this form are" +
       
   577                                    " not supported", node)
       
   578 
       
   579         expr = node.handlers[0][0]
       
   580         as_ = node.handlers[0][1]
       
   581         if as_:
       
   582             errName = as_.name
       
   583         else:
       
   584             errName = 'err'
       
   585 
       
   586         # XXX TODO: check that this should instead be added as a _separate_
       
   587         # local scope, temporary to the function.  oh dearie me.
       
   588         self.add_local_arg(errName)
       
   589 
       
   590         self.printo("    try {")
       
   591         for stmt in node.body.nodes:
       
   592             self._stmt(stmt, current_klass)
       
   593         self.printo("    } catch(%s) {" % errName)
       
   594         if expr:
       
   595             k = []
       
   596             if isinstance(expr, ast.Tuple):
       
   597                 for x in expr.nodes:
       
   598                     k.append("(%(err)s.__name__ == %(expr)s.__name__)" % dict(err=errName, expr=self.expr(x, current_klass)))
       
   599             else:
       
   600                 k = [" (%(err)s.__name__ == %(expr)s.__name__) " % dict(err=errName, expr=self.expr(expr, current_klass))]
       
   601             self.printo("   if(%s) {" % '||\n\t\t'.join(k))
       
   602         for stmt in node.handlers[0][2]:
       
   603             self._stmt(stmt, current_klass)
       
   604         if expr:
       
   605             # self.printo("} else { throw(%s); } " % errName)
       
   606             self.printo("}")
       
   607         if node.else_ is not None:
       
   608             self.printo("    } finally {")
       
   609             for stmt in node.else_:
       
   610                 self._stmt(stmt, current_klass)
       
   611         self.printo("    }")
       
   612 
       
   613     # XXX: change use_getattr to True to enable "strict" compilation
       
   614     # but incurring a 100% performance penalty. oops.
       
   615     def _getattr(self, v, current_klass, use_getattr=False):
       
   616         attr_name = v.attrname
       
   617         if isinstance(v.expr, ast.Name):
       
   618             obj = self._name(v.expr, current_klass, return_none_for_module=True)
       
   619             if obj is None and v.expr.name in self.module_imports():
       
   620                 # XXX TODO: distinguish between module import classes
       
   621                 # and variables.  right now, this is a hack to get
       
   622                 # the sys module working.
       
   623                 # if v.expr.name == 'sys':
       
   624                 return v.expr.name+'.'+attr_name
       
   625                 # return v.expr.name+'.__'+attr_name+'.prototype.__class__'
       
   626             if not use_getattr or attr_name == '__class__' or \
       
   627                     attr_name == '__name__':
       
   628                 return obj + "." + attr_name
       
   629             return "pyjslib.getattr(%s, '%s')" % (obj, attr_name)
       
   630         elif isinstance(v.expr, ast.Getattr):
       
   631             return self._getattr(v.expr, current_klass) + "." + attr_name
       
   632         elif isinstance(v.expr, ast.Subscript):
       
   633             return self._subscript(v.expr, self.modpfx()) + "." + attr_name
       
   634         elif isinstance(v.expr, ast.CallFunc):
       
   635             return self._callfunc(v.expr, self.modpfx()) + "." + attr_name
       
   636         else:
       
   637             raise TranslationError("unsupported type (in _getattr)", v.expr)
       
   638 
       
   639     def modpfx(self):
       
   640         return strip_py(self.module_prefix)
       
   641 
       
   642     def _name(self, v, current_klass, top_level=False,
       
   643               return_none_for_module=False):
       
   644 
       
   645         if v.name == 'ilikesillynamesfornicedebugcode':
       
   646             print(top_level, current_klass, repr(v))
       
   647             print(self.top_level_vars)
       
   648             print(self.top_level_functions)
       
   649             print(self.local_arg_stack)
       
   650             print("error...")
       
   651 
       
   652         local_var_names = None
       
   653         las = len(self.local_arg_stack)
       
   654         if las > 0:
       
   655             local_var_names = self.local_arg_stack[-1]
       
   656 
       
   657         if v.name == "True":
       
   658             return "true"
       
   659         elif v.name == "False":
       
   660             return "false"
       
   661         elif v.name == "None":
       
   662             return "null"
       
   663         elif v.name == '__name__' and current_klass is None:
       
   664             return self.modpfx() + v.name
       
   665         elif v.name == self.method_self:
       
   666             return "this"
       
   667         elif v.name in self.top_level_functions:
       
   668             return UU+self.modpfx() + v.name
       
   669         elif v.name in self.method_imported_globals:
       
   670             return UU+self.modpfx() + v.name
       
   671         elif not current_klass and las == 1 and v.name in self.top_level_vars:
       
   672             return UU+self.modpfx() + v.name
       
   673         elif v.name in local_var_names:
       
   674             return v.name
       
   675         elif v.name in self.imported_classes:
       
   676             return UU+self.imported_classes[v.name] + '.__' + v.name + ".prototype.__class__"
       
   677         elif v.name in self.top_level_classes:
       
   678             return UU+self.modpfx() + "__" + v.name + ".prototype.__class__"
       
   679         elif v.name in self.module_imports() and return_none_for_module:
       
   680             return None
       
   681         elif v.name in PYJSLIB_BUILTIN_CLASSES:
       
   682             return "pyjslib." + pyjs_builtin_remap(v.name)
       
   683         elif current_klass:
       
   684             if v.name not in local_var_names and \
       
   685                v.name not in self.top_level_vars and \
       
   686                v.name not in PYJS_GLOBAL_VARS and \
       
   687                v.name not in self.top_level_functions:
       
   688 
       
   689                 cls_name = current_klass
       
   690                 if hasattr(cls_name, "name"):
       
   691                     cls_name_ = cls_name.name_
       
   692                     cls_name = cls_name.name
       
   693                 else:
       
   694                     cls_name_ = current_klass + "_"  # XXX ???
       
   695                 name = UU+cls_name_ + ".prototype.__class__." + v.name
       
   696                 if v.name == 'listener':
       
   697                     name = 'listener+' + name
       
   698                 return name
       
   699 
       
   700         return v.name
       
   701 
       
   702     def _name2(self, v, current_klass, attr_name):
       
   703         obj = v.name
       
   704 
       
   705         if obj in self.method_imported_globals:
       
   706             call_name = UU+self.modpfx() + obj + "." + attr_name
       
   707         elif obj in self.imported_classes:
       
   708             # attr_str = ""
       
   709             # if attr_name != "__init__":
       
   710             attr_str = ".prototype.__class__." + attr_name
       
   711             call_name = UU+self.imported_classes[obj] + '.__' + obj + attr_str
       
   712         elif obj in self.module_imports():
       
   713             call_name = obj + "." + attr_name
       
   714         elif obj[0] == obj[0].upper():  # XXX HACK ALERT
       
   715             call_name = UU + self.modpfx() + "__" + obj + ".prototype.__class__." + attr_name
       
   716         else:
       
   717             call_name = UU+self._name(v, current_klass) + "." + attr_name
       
   718 
       
   719         return call_name
       
   720 
       
   721     def _getattr2(self, v, current_klass, attr_name):
       
   722         if isinstance(v.expr, ast.Getattr):
       
   723             call_name = self._getattr2(v.expr, current_klass, v.attrname + "." + attr_name)
       
   724         elif isinstance(v.expr, ast.Name) and v.expr.name in self.module_imports():
       
   725             call_name = UU+v.expr.name + '.__' + v.attrname+".prototype.__class__."+attr_name
       
   726         else:
       
   727             obj = self.expr(v.expr, current_klass)
       
   728             call_name = obj + "." + v.attrname + "." + attr_name
       
   729 
       
   730         return call_name
       
   731 
       
   732     def _class(self, node):
       
   733         """
       
   734         Handle a class definition.
       
   735 
       
   736         In order to translate python semantics reasonably well, the following
       
   737         structure is used:
       
   738 
       
   739         A special object is created for the class, which inherits attributes
       
   740         from the superclass, or Object if there's no superclass.  This is the
       
   741         class object; the object which you refer to when specifying the
       
   742         class by name.  Static, class, and unbound methods are copied
       
   743         from the superclass object.
       
   744 
       
   745         A special constructor function is created with the same name as the
       
   746         class, which is used to create instances of that class.
       
   747 
       
   748         A javascript class (e.g. a function with a prototype attribute) is
       
   749         created which is the javascript class of created instances, and
       
   750         which inherits attributes from the class object. Bound methods are
       
   751         copied from the superclass into this class rather than inherited,
       
   752         because the class object contains unbound, class, and static methods
       
   753         that we don't necessarily want to inherit.
       
   754 
       
   755         The type of a method can now be determined by inspecting its
       
   756         static_method, unbound_method, class_method, or instance_method
       
   757         attribute; only one of these should be true.
       
   758 
       
   759         Much of this work is done in pyjs_extend, is pyjslib.py
       
   760         """
       
   761         class_name = self.modpfx() + uuprefix(node.name, 1)
       
   762         class_name_ = self.modpfx() + uuprefix(node.name)
       
   763         current_klass = Klass(class_name, class_name_)
       
   764         init_method = None
       
   765         for child in node.code:
       
   766             if isinstance(child, ast.Function):
       
   767                 current_klass.add_function(child.name)
       
   768                 if child.name == "__init__":
       
   769                     init_method = child
       
   770 
       
   771         if len(node.bases) == 0:
       
   772             base_class = "pyjslib.__Object"
       
   773         elif len(node.bases) == 1:
       
   774             if isinstance(node.bases[0], ast.Name):
       
   775                 if node.bases[0].name in self.imported_classes:
       
   776                     base_class_ = self.imported_classes[node.bases[0].name] + '.__' + node.bases[0].name
       
   777                     base_class = self.imported_classes[node.bases[0].name] + '.' + node.bases[0].name
       
   778                 else:
       
   779                     base_class_ = self.modpfx() + "__" + node.bases[0].name
       
   780                     base_class = self.modpfx() + node.bases[0].name
       
   781             elif isinstance(node.bases[0], ast.Getattr):
       
   782                 # the bases are not in scope of the class so do not
       
   783                 # pass our class to self._name
       
   784                 base_class_ = self._name(node.bases[0].expr, None) + \
       
   785                              ".__" + node.bases[0].attrname
       
   786                 base_class = \
       
   787                     self._name(node.bases[0].expr, None) + \
       
   788                     "." + node.bases[0].attrname
       
   789             else:
       
   790                 raise TranslationError("unsupported type (in _class)", node.bases[0])
       
   791 
       
   792             current_klass.set_base(base_class)
       
   793         else:
       
   794             raise TranslationError("more than one base (in _class)", node)
       
   795 
       
   796         self.printo(UU+class_name_ + " = function () {")
       
   797         # call superconstructor
       
   798         # if base_class:
       
   799         #    self.printo("    __" + base_class + ".call(this);")
       
   800         self.printo("}")
       
   801 
       
   802         if not init_method:
       
   803             init_method = ast.Function([], "__init__", ["self"], [], 0, None, [])
       
   804             # self._method(init_method, current_klass, class_name)
       
   805 
       
   806         # Generate a function which constructs the object
       
   807         clsfunc = ast.Function(
       
   808             [], node.name,
       
   809             init_method.argnames[1:],
       
   810             init_method.defaults,
       
   811             init_method.flags,
       
   812             None,
       
   813             [ast.Discard(ast.CallFunc(ast.Name("JS"), [ast.Const(
       
   814                 #            I attempted lazy initialization, but then you can't access static class members
       
   815                 #            "    if(!__"+base_class+".__was_initialized__)"+
       
   816                 #            "        __" + class_name + "_initialize();\n" +
       
   817                 "    var instance = new " + UU + class_name_ + "();\n" +
       
   818                 "    if(instance.__init__) instance.__init__.apply(instance, arguments);\n" +
       
   819                 "    return instance;"
       
   820             )]))])
       
   821 
       
   822         self._function(clsfunc, False)
       
   823         self.printo(UU+class_name_ + ".__initialize__ = function () {")
       
   824         self.printo("    if("+UU+class_name_+".__was_initialized__) return;")
       
   825         self.printo("    "+UU+class_name_+".__was_initialized__ = true;")
       
   826         cls_obj = UU+class_name_ + '.prototype.__class__'
       
   827 
       
   828         if class_name == "pyjslib.__Object":
       
   829             self.printo("    "+cls_obj+" = {};")
       
   830         else:
       
   831             if base_class and base_class not in ("object", "pyjslib.__Object"):
       
   832                 self.printo("    if(!"+UU+base_class_+".__was_initialized__)")
       
   833                 self.printo("        "+UU+base_class_+".__initialize__();")
       
   834                 self.printo("    pyjs_extend(" + UU+class_name_ + ", "+UU+base_class_+");")
       
   835             else:
       
   836                 self.printo("    pyjs_extend(" + UU+class_name_ + ", "+UU+"pyjslib.__Object);")
       
   837 
       
   838         self.printo("    "+cls_obj+".__new__ = "+UU+class_name+";")
       
   839         self.printo("    "+cls_obj+".__name__ = '"+UU+node.name+"';")
       
   840 
       
   841         for child in node.code:
       
   842             if isinstance(child, ast.Pass):
       
   843                 pass
       
   844             elif isinstance(child, ast.Function):
       
   845                 self._method(child, current_klass, class_name, class_name_)
       
   846             elif isinstance(child, ast.Assign):
       
   847                 self.classattr(child, current_klass)
       
   848             elif isinstance(child, ast.Discard) and isinstance(child.expr, ast.Const):
       
   849                 # Probably a docstring, turf it
       
   850                 pass
       
   851             else:
       
   852                 raise TranslationError("unsupported type (in _class)", child)
       
   853         self.printo("}")
       
   854 
       
   855         self.printo(class_name_+".__initialize__();")
       
   856 
       
   857     def classattr(self, node, current_klass):
       
   858         self._assign(node, current_klass, True)
       
   859 
       
   860     def _raise(self, node, current_klass):
       
   861         if node.expr2:
       
   862             raise TranslationError("More than one expression unsupported",
       
   863                                    node)
       
   864         self.printo("throw (%s);" % self.expr(
       
   865             node.expr1, current_klass))
       
   866 
       
   867     def _method(self, node, current_klass, class_name, class_name_):
       
   868         # reset global var scope
       
   869         self.method_imported_globals = set()
       
   870 
       
   871         arg_names = list(node.argnames)
       
   872 
       
   873         classmethod = False
       
   874         staticmethod = False
       
   875         if node.decorators:
       
   876             for d in node.decorators:
       
   877                 if d.name == "classmethod":
       
   878                     classmethod = True
       
   879                 elif d.name == "staticmethod":
       
   880                     staticmethod = True
       
   881 
       
   882         if staticmethod:
       
   883             staticfunc = ast.Function([], class_name_+"."+node.name, node.argnames, node.defaults, node.flags, node.doc, node.code, node.lineno)
       
   884             self._function(staticfunc, True)
       
   885             self.printo("    " + UU+class_name_ + ".prototype.__class__." + node.name + " = " + class_name_+"."+node.name+";")
       
   886             self.printo("    " + UU+class_name_ + ".prototype.__class__." + node.name + ".static_method = true;")
       
   887             return
       
   888         else:
       
   889             if len(arg_names) == 0:
       
   890                 raise TranslationError("methods must take an argument 'self' (in _method)", node)
       
   891             self.method_self = arg_names[0]
       
   892 
       
   893             # if not classmethod and arg_names[0] != "self":
       
   894             #    raise TranslationError("first arg not 'self' (in _method)", node)
       
   895 
       
   896         normal_arg_names = arg_names[1:]
       
   897         if node.kwargs:
       
   898             kwargname = normal_arg_names.pop()
       
   899         if node.varargs:
       
   900             varargname = normal_arg_names.pop()
       
   901         declared_arg_names = list(normal_arg_names)
       
   902         if node.kwargs:
       
   903             declared_arg_names.append(kwargname)
       
   904 
       
   905         function_args = "(" + ", ".join(declared_arg_names) + ")"
       
   906 
       
   907         if classmethod:
       
   908             fexpr = UU + class_name_ + ".prototype.__class__." + node.name
       
   909         else:
       
   910             fexpr = UU + class_name_ + ".prototype." + node.name
       
   911         self.printo("    "+fexpr + " = function" + function_args + " {")
       
   912 
       
   913         # default arguments
       
   914         self._default_args_handler(node, normal_arg_names, current_klass)
       
   915 
       
   916         local_arg_names = normal_arg_names + declared_arg_names
       
   917 
       
   918         if node.varargs:
       
   919             self._varargs_handler(node, varargname, declared_arg_names, current_klass)
       
   920             local_arg_names.append(varargname)
       
   921 
       
   922         # stack of local variable names for this function call
       
   923         self.local_arg_stack.append(local_arg_names)
       
   924 
       
   925         for child in node.code:
       
   926             self._stmt(child, current_klass)
       
   927 
       
   928         # remove the top local arg names
       
   929         self.local_arg_stack.pop()
       
   930 
       
   931         self.printo("    };")
       
   932 
       
   933         self._kwargs_parser(node, fexpr, normal_arg_names, current_klass)
       
   934 
       
   935         if classmethod:
       
   936             # Have to create a version on the instances which automatically passes the
       
   937             # class as "self"
       
   938             altexpr = UU + class_name_ + ".prototype." + node.name
       
   939             self.printo("    "+altexpr + " = function() {")
       
   940             self.printo("        return " + fexpr + ".apply(this.__class__, arguments);")
       
   941             self.printo("    };")
       
   942             self.printo("    "+fexpr+".class_method = true;")
       
   943             self.printo("    "+altexpr+".instance_method = true;")
       
   944         else:
       
   945             # For instance methods, we need an unbound version in the class object
       
   946             altexpr = UU + class_name_ + ".prototype.__class__." + node.name
       
   947             self.printo("    "+altexpr + " = function() {")
       
   948             self.printo("        return " + fexpr + ".call.apply("+fexpr+", arguments);")
       
   949             self.printo("    };")
       
   950             self.printo("    "+altexpr+".unbound_method = true;")
       
   951             self.printo("    "+fexpr+".instance_method = true;")
       
   952             self.printo("    "+altexpr+".__name__ = '%s';" % node.name)
       
   953 
       
   954         self.printo(UU + class_name_ + ".prototype.%s.__name__ = '%s';" %
       
   955                     (node.name, node.name))
       
   956 
       
   957         if node.kwargs or len(node.defaults):
       
   958             self.printo("    "+altexpr + ".parse_kwargs = " + fexpr + ".parse_kwargs;")
       
   959 
       
   960         self.method_self = None
       
   961         self.method_imported_globals = set()
       
   962 
       
   963     def _isNativeFunc(self, node):
       
   964         if isinstance(node, ast.Discard):
       
   965             if isinstance(node.expr, ast.CallFunc):
       
   966                 if isinstance(node.expr.node, ast.Name) and \
       
   967                        node.expr.node.name == NATIVE_JS_FUNC_NAME:
       
   968                     return True
       
   969         return False
       
   970 
       
   971     def _stmt(self, node, current_klass):
       
   972         debugStmt = self.debug and not self._isNativeFunc(node)
       
   973         if debugStmt:
       
   974             self.printo('  try {')
       
   975 
       
   976         if isinstance(node, ast.Return):
       
   977             self._return(node, current_klass)
       
   978         elif isinstance(node, ast.Break):
       
   979             self._break(node, current_klass)
       
   980         elif isinstance(node, ast.Continue):
       
   981             self._continue(node, current_klass)
       
   982         elif isinstance(node, ast.Assign):
       
   983             self._assign(node, current_klass)
       
   984         elif isinstance(node, ast.AugAssign):
       
   985             self._augassign(node, current_klass)
       
   986         elif isinstance(node, ast.Discard):
       
   987             self._discard(node, current_klass)
       
   988         elif isinstance(node, ast.If):
       
   989             self._if(node, current_klass)
       
   990         elif isinstance(node, ast.For):
       
   991             self._for(node, current_klass)
       
   992         elif isinstance(node, ast.While):
       
   993             self._while(node, current_klass)
       
   994         elif isinstance(node, ast.Subscript):
       
   995             self._subscript_stmt(node, current_klass)
       
   996         elif isinstance(node, ast.Global):
       
   997             self._global(node, current_klass)
       
   998         elif isinstance(node, ast.Pass):
       
   999             pass
       
  1000         elif isinstance(node, ast.Function):
       
  1001             self._function(node, True)
       
  1002         elif isinstance(node, ast.Printnl):
       
  1003             self._print(node, current_klass)
       
  1004         elif isinstance(node, ast.Print):
       
  1005             self._print(node, current_klass)
       
  1006         elif isinstance(node, ast.TryExcept):
       
  1007             self._tryExcept(node, current_klass)
       
  1008         elif isinstance(node, ast.Raise):
       
  1009             self._raise(node, current_klass)
       
  1010         else:
       
  1011             raise TranslationError("unsupported type (in _stmt)", node)
       
  1012 
       
  1013         if debugStmt:
       
  1014 
       
  1015             lt = self.get_line_trace(node)
       
  1016             isHaltFunction = self.module_prefix + "IsHaltException"
       
  1017 
       
  1018             out = (
       
  1019                 '  } catch (__err) {',
       
  1020                 '      if (' + isHaltFunction + '(__err.name)) {',
       
  1021                 '          throw __err;',
       
  1022                 '      } else {',
       
  1023                 '          st = sys.printstack() + ' + '"%s"' % lt + "+ '\\n' ;"
       
  1024                 '          alert("' + 'Error in ' + lt + '"' +
       
  1025                 '+"\\n"+__err.name+": "+__err.message' +
       
  1026                 '+"\\n\\nStack trace:\\n"' + '+st' + ');',
       
  1027                 '          debugger;',
       
  1028                 '          throw new ' + self.module_prefix + 'HaltException();',
       
  1029                 '      }',
       
  1030                 '  }'
       
  1031             )
       
  1032             for s in out:
       
  1033                 self.printo(s)
       
  1034 
       
  1035     def get_line_trace(self, node):
       
  1036         lineNum = "Unknown"
       
  1037         srcLine = ""
       
  1038         if hasattr(node, "lineno"):
       
  1039             if node.lineno is not None:
       
  1040                 lineNum = node.lineno
       
  1041                 srcLine = self.src[min(lineNum, len(self.src))-1]
       
  1042                 srcLine = srcLine.replace('\\', '\\\\')
       
  1043                 srcLine = srcLine.replace('"', '\\"')
       
  1044                 srcLine = srcLine.replace("'", "\\'")
       
  1045 
       
  1046         return self.raw_module_name + ".py, line " \
       
  1047             + str(lineNum) + ":"\
       
  1048             + "\\n" \
       
  1049             + "    " + srcLine
       
  1050 
       
  1051     def _augassign(self, node, current_klass):
       
  1052         v = node.node
       
  1053         if isinstance(v, ast.Getattr):
       
  1054             # XXX HACK!  don't allow += on return result of getattr.
       
  1055             # TODO: create a temporary variable or something.
       
  1056             lhs = self._getattr(v, current_klass, False)
       
  1057         else:
       
  1058             lhs = self._name(node.node, current_klass)
       
  1059         op = node.op
       
  1060         rhs = self.expr(node.expr, current_klass)
       
  1061         self.printo("    " + lhs + " " + op + " " + rhs + ";")
       
  1062 
       
  1063     def _assign(self, node, current_klass, top_level=False):
       
  1064         if len(node.nodes) != 1:
       
  1065             tempvar = '__temp'+str(node.lineno)
       
  1066             tnode = ast.Assign([ast.AssName(tempvar, "OP_ASSIGN", node.lineno)], node.expr, node.lineno)
       
  1067             self._assign(tnode, current_klass, top_level)
       
  1068             for v in node.nodes:
       
  1069                 tnode2 = ast.Assign([v], ast.Name(tempvar, node.lineno), node.lineno)
       
  1070                 self._assign(tnode2, current_klass, top_level)
       
  1071             return
       
  1072 
       
  1073         local_var_names = None
       
  1074         if len(self.local_arg_stack) > 0:
       
  1075             local_var_names = self.local_arg_stack[-1]
       
  1076 
       
  1077         def _lhsFromAttr(v, current_klass):
       
  1078             attr_name = v.attrname
       
  1079             if isinstance(v.expr, ast.Name):
       
  1080                 lhs = self._name(v.expr, current_klass) + "." + attr_name
       
  1081             elif isinstance(v.expr, ast.Getattr):
       
  1082                 lhs = self._getattr(v, current_klass)
       
  1083             elif isinstance(v.expr, ast.Subscript):
       
  1084                 lhs = self._subscript(v.expr, current_klass) + "." + attr_name
       
  1085             else:
       
  1086                 raise TranslationError("unsupported type (in _assign)", v.expr)
       
  1087             return lhs
       
  1088 
       
  1089         def _lhsFromName(v, top_level, current_klass):
       
  1090             if top_level:
       
  1091                 if current_klass:
       
  1092                     lhs = UU+current_klass.name_ + ".prototype.__class__." \
       
  1093                                + v.name
       
  1094                 else:
       
  1095                     self.top_level_vars.add(v.name)
       
  1096                     vname = self.modpfx() + v.name
       
  1097                     if not self.modpfx() and v.name not in\
       
  1098                        self.method_imported_globals:
       
  1099                         lhs = "var " + vname
       
  1100                     else:
       
  1101                         lhs = UU + vname
       
  1102                     self.add_local_arg(v.name)
       
  1103             else:
       
  1104                 if v.name in local_var_names:
       
  1105                     lhs = v.name
       
  1106                 elif v.name in self.method_imported_globals:
       
  1107                     lhs = self.modpfx() + v.name
       
  1108                 else:
       
  1109                     lhs = "var " + v.name
       
  1110                     self.add_local_arg(v.name)
       
  1111             return lhs
       
  1112 
       
  1113         dbg = 0
       
  1114         v = node.nodes[0]
       
  1115         if isinstance(v, ast.AssAttr):
       
  1116             lhs = _lhsFromAttr(v, current_klass)
       
  1117             if v.flags == "OP_ASSIGN":
       
  1118                 op = "="
       
  1119             else:
       
  1120                 raise TranslationError("unsupported flag (in _assign)", v)
       
  1121 
       
  1122         elif isinstance(v, ast.AssName):
       
  1123             lhs = _lhsFromName(v, top_level, current_klass)
       
  1124             if v.flags == "OP_ASSIGN":
       
  1125                 op = "="
       
  1126             else:
       
  1127                 raise TranslationError("unsupported flag (in _assign)", v)
       
  1128         elif isinstance(v, ast.Subscript):
       
  1129             if v.flags == "OP_ASSIGN":
       
  1130                 obj = self.expr(v.expr, current_klass)
       
  1131                 if len(v.subs) != 1:
       
  1132                     raise TranslationError("must have one sub (in _assign)", v)
       
  1133                 idx = self.expr(v.subs[0], current_klass)
       
  1134                 value = self.expr(node.expr, current_klass)
       
  1135                 self.printo("    " + obj + ".__setitem__(" + idx + ", " + value + ");")
       
  1136                 return
       
  1137             else:
       
  1138                 raise TranslationError("unsupported flag (in _assign)", v)
       
  1139         elif isinstance(v, (ast.AssList, ast.AssTuple)):
       
  1140             uniqueID = self.nextTupleAssignID
       
  1141             self.nextTupleAssignID += 1
       
  1142             tempName = "__tupleassign" + str(uniqueID) + "__"
       
  1143             self.printo("    var " + tempName + " = " + self.expr(node.expr, current_klass) + ";")
       
  1144             for index, child in enumerate(v.getChildNodes()):
       
  1145                 rhs = tempName + ".__getitem__(" + str(index) + ")"
       
  1146 
       
  1147                 if isinstance(child, ast.AssAttr):
       
  1148                     lhs = _lhsFromAttr(child, current_klass)
       
  1149                 elif isinstance(child, ast.AssName):
       
  1150                     lhs = _lhsFromName(child, top_level, current_klass)
       
  1151                 elif isinstance(child, ast.Subscript):
       
  1152                     if child.flags == "OP_ASSIGN":
       
  1153                         obj = self.expr(child.expr, current_klass)
       
  1154                         if len(child.subs) != 1:
       
  1155                             raise TranslationError("must have one sub " +
       
  1156                                                    "(in _assign)", child)
       
  1157                         idx = self.expr(child.subs[0], current_klass)
       
  1158                         value = self.expr(node.expr, current_klass)
       
  1159                         self.printo("    " + obj + ".__setitem__(" + idx + ", " + rhs + ");")
       
  1160                         continue
       
  1161                 self.printo("    " + lhs + " = " + rhs + ";")
       
  1162             return
       
  1163         else:
       
  1164             raise TranslationError("unsupported type (in _assign)", v)
       
  1165 
       
  1166         rhs = self.expr(node.expr, current_klass)
       
  1167         if dbg:
       
  1168             print("b", repr(node.expr), rhs)
       
  1169         self.printo("    " + lhs + " " + op + " " + rhs + ";")
       
  1170 
       
  1171     def _discard(self, node, current_klass):
       
  1172 
       
  1173         if isinstance(node.expr, ast.CallFunc):
       
  1174             debugStmt = self.debug and not self._isNativeFunc(node)
       
  1175             if debugStmt and isinstance(node.expr.node, ast.Name) and \
       
  1176                node.expr.node.name == 'import_wait':
       
  1177                 debugStmt = False
       
  1178             if debugStmt:
       
  1179                 st = self.get_line_trace(node)
       
  1180                 self.printo("sys.addstack('%s');\n" % st)
       
  1181             if isinstance(node.expr.node, ast.Name) and node.expr.node.name == NATIVE_JS_FUNC_NAME:
       
  1182                 if len(node.expr.args) != 1:
       
  1183                     raise TranslationError("native javascript function %s must have one arg" % NATIVE_JS_FUNC_NAME, node.expr)
       
  1184                 if not isinstance(node.expr.args[0], ast.Const):
       
  1185                     raise TranslationError("native javascript function %s must have constant arg" % NATIVE_JS_FUNC_NAME, node.expr)
       
  1186                 raw_js = node.expr.args[0].value
       
  1187                 self.printo(raw_js)
       
  1188             else:
       
  1189                 expr = self._callfunc(node.expr, current_klass)
       
  1190                 self.printo("    " + expr + ";")
       
  1191 
       
  1192             if debugStmt:
       
  1193                 self.printo("sys.popstack();\n")
       
  1194 
       
  1195         elif isinstance(node.expr, ast.Const):
       
  1196             if node.expr.value is not None:  # Empty statements generate ignore None
       
  1197                 self.printo(self._const(node.expr))
       
  1198         else:
       
  1199             raise TranslationError("unsupported type (in _discard)", node.expr)
       
  1200 
       
  1201     def _if(self, node, current_klass):
       
  1202         for i in range(len(node.tests)):
       
  1203             test, consequence = node.tests[i]
       
  1204             if i == 0:
       
  1205                 keyword = "if"
       
  1206             else:
       
  1207                 keyword = "else if"
       
  1208 
       
  1209             self._if_test(keyword, test, consequence, current_klass)
       
  1210 
       
  1211         if node.else_:
       
  1212             keyword = "else"
       
  1213             test = None
       
  1214             consequence = node.else_
       
  1215 
       
  1216             self._if_test(keyword, test, consequence, current_klass)
       
  1217 
       
  1218     def _if_test(self, keyword, test, consequence, current_klass):
       
  1219         if test:
       
  1220             expr = self.expr(test, current_klass)
       
  1221 
       
  1222             self.printo("    " + keyword + " (pyjslib.bool(" + expr + ")) {")
       
  1223         else:
       
  1224             self.printo("    " + keyword + " {")
       
  1225 
       
  1226         if isinstance(consequence, ast.Stmt):
       
  1227             for child in consequence.nodes:
       
  1228                 self._stmt(child, current_klass)
       
  1229         else:
       
  1230             raise TranslationError("unsupported type (in _if_test)", consequence)
       
  1231 
       
  1232         self.printo("    }")
       
  1233 
       
  1234     def _from(self, node):
       
  1235         for name in node.names:
       
  1236             # look up "hack" in AppTranslator as to how findFile gets here
       
  1237             module_name = node.modname + "." + name[0]
       
  1238             try:
       
  1239                 ff = self.findFile(module_name + ".py")
       
  1240             except Exception:
       
  1241                 ff = None
       
  1242             if ff:
       
  1243                 self.add_imported_module(module_name)
       
  1244             else:
       
  1245                 self.imported_classes[name[0]] = node.modname
       
  1246 
       
  1247     def _compare(self, node, current_klass):
       
  1248         lhs = self.expr(node.expr, current_klass)
       
  1249 
       
  1250         if len(node.ops) != 1:
       
  1251             raise TranslationError("only one ops supported (in _compare)", node)
       
  1252 
       
  1253         op = node.ops[0][0]
       
  1254         rhs_node = node.ops[0][1]
       
  1255         rhs = self.expr(rhs_node, current_klass)
       
  1256 
       
  1257         if op == "==":
       
  1258             return "pyjslib.eq(%s, %s)" % (lhs, rhs)
       
  1259         if op == "in":
       
  1260             return rhs + ".__contains__(" + lhs + ")"
       
  1261         elif op == "not in":
       
  1262             return "!" + rhs + ".__contains__(" + lhs + ")"
       
  1263         elif op == "is":
       
  1264             op = "==="
       
  1265         elif op == "is not":
       
  1266             op = "!=="
       
  1267 
       
  1268         return "(" + lhs + " " + op + " " + rhs + ")"
       
  1269 
       
  1270     def _not(self, node, current_klass):
       
  1271         expr = self.expr(node.expr, current_klass)
       
  1272 
       
  1273         return "!(" + expr + ")"
       
  1274 
       
  1275     def _or(self, node, current_klass):
       
  1276         expr = "("+(") || (".join([self.expr(child, current_klass) for child in node.nodes]))+')'
       
  1277         return expr
       
  1278 
       
  1279     def _and(self, node, current_klass):
       
  1280         expr = "("+(") && (".join([self.expr(child, current_klass) for child in node.nodes]))+")"
       
  1281         return expr
       
  1282 
       
  1283     def _for(self, node, current_klass):
       
  1284         assign_name = ""
       
  1285         assign_tuple = ""
       
  1286 
       
  1287         # based on Bob Ippolito's Iteration in Javascript code
       
  1288         if isinstance(node.assign, ast.AssName):
       
  1289             assign_name = node.assign.name
       
  1290             self.add_local_arg(assign_name)
       
  1291             if node.assign.flags == "OP_ASSIGN":
       
  1292                 op = "="
       
  1293         elif isinstance(node.assign, ast.AssTuple):
       
  1294             op = "="
       
  1295             i = 0
       
  1296             for child in node.assign:
       
  1297                 child_name = child.name
       
  1298                 if assign_name == "":
       
  1299                     assign_name = "temp_" + child_name
       
  1300                 self.add_local_arg(child_name)
       
  1301                 assign_tuple += """
       
  1302                 var %(child_name)s %(op)s %(assign_name)s.__getitem__(%(i)i);
       
  1303                 """ % locals()
       
  1304                 i += 1
       
  1305         else:
       
  1306             raise TranslationError("unsupported type (in _for)", node.assign)
       
  1307 
       
  1308         if isinstance(node.list, ast.Name):
       
  1309             list_expr = self._name(node.list, current_klass)
       
  1310         elif isinstance(node.list, ast.Getattr):
       
  1311             list_expr = self._getattr(node.list, current_klass)
       
  1312         elif isinstance(node.list, ast.CallFunc):
       
  1313             list_expr = self._callfunc(node.list, current_klass)
       
  1314         else:
       
  1315             raise TranslationError("unsupported type (in _for)", node.list)
       
  1316 
       
  1317         lhs = "var " + assign_name
       
  1318         iterator_name = "__" + assign_name
       
  1319 
       
  1320         loc_dict = {
       
  1321             "iterator_name": iterator_name,
       
  1322             "list_expr": list_expr,
       
  1323             "lhs": lhs,
       
  1324             "op": op,
       
  1325             "assign_tuple": assign_tuple,
       
  1326         }
       
  1327 
       
  1328         self.printo("""
       
  1329         var %(iterator_name)s = %(list_expr)s.__iter__();
       
  1330         try {
       
  1331             while (true) {
       
  1332                 %(lhs)s %(op)s %(iterator_name)s.next();
       
  1333                 %(assign_tuple)s
       
  1334         """ % loc_dict)
       
  1335         for n in node.body.nodes:
       
  1336             self._stmt(n, current_klass)
       
  1337         self.printo("""
       
  1338             }
       
  1339         } catch (e) {
       
  1340             if (e.__name__ != pyjslib.StopIteration.__name__) {
       
  1341                 throw e;
       
  1342             }
       
  1343         }
       
  1344         """)
       
  1345 
       
  1346     def _while(self, node, current_klass):
       
  1347         test = self.expr(node.test, current_klass)
       
  1348         self.printo("    while (pyjslib.bool(" + test + ")) {")
       
  1349         if isinstance(node.body, ast.Stmt):
       
  1350             for child in node.body.nodes:
       
  1351                 self._stmt(child, current_klass)
       
  1352         else:
       
  1353             raise TranslationError("unsupported type (in _while)", node.body)
       
  1354         self.printo("    }")
       
  1355 
       
  1356     def _const(self, node):
       
  1357         if isinstance(node.value, int):
       
  1358             return str(node.value)
       
  1359         elif isinstance(node.value, float):
       
  1360             return str(node.value)
       
  1361         elif isinstance(node.value, basestring):
       
  1362             v = node.value
       
  1363             if isinstance(node.value, text):
       
  1364                 v = v.encode('utf-8')
       
  1365             return "String('%s')" % escapejs(v)
       
  1366         elif node.value is None:
       
  1367             return "null"
       
  1368         else:
       
  1369             raise TranslationError("unsupported type (in _const)", node)
       
  1370 
       
  1371     def _unaryadd(self, node, current_klass):
       
  1372         return self.expr(node.expr, current_klass)
       
  1373 
       
  1374     def _unarysub(self, node, current_klass):
       
  1375         return "-" + self.expr(node.expr, current_klass)
       
  1376 
       
  1377     def _add(self, node, current_klass):
       
  1378         return self.expr(node.left, current_klass) + " + " + self.expr(node.right, current_klass)
       
  1379 
       
  1380     def _sub(self, node, current_klass):
       
  1381         return self.expr(node.left, current_klass) + " - " + self.expr(node.right, current_klass)
       
  1382 
       
  1383     def _div(self, node, current_klass):
       
  1384         return self.expr(node.left, current_klass) + " / " + self.expr(node.right, current_klass)
       
  1385 
       
  1386     def _mul(self, node, current_klass):
       
  1387         return self.expr(node.left, current_klass) + " * " + self.expr(node.right, current_klass)
       
  1388 
       
  1389     def _mod(self, node, current_klass):
       
  1390         if isinstance(node.left, ast.Const) and isinstance(node.left.value, str):
       
  1391             self.imported_js.add("sprintf.js")  # Include the sprintf functionality if it is used
       
  1392             return "sprintf("+self.expr(node.left, current_klass) + ", " + self.expr(node.right, current_klass)+")"
       
  1393         return self.expr(node.left, current_klass) + " % " + self.expr(node.right, current_klass)
       
  1394 
       
  1395     def _invert(self, node, current_klass):
       
  1396         return "~" + self.expr(node.expr, current_klass)
       
  1397 
       
  1398     def _bitand(self, node, current_klass):
       
  1399         return " & ".join([self.expr(child, current_klass) for child in node.nodes])
       
  1400 
       
  1401     def _bitshiftleft(self, node, current_klass):
       
  1402         return self.expr(node.left, current_klass) + " << " + self.expr(node.right, current_klass)
       
  1403 
       
  1404     def _bitshiftright(self, node, current_klass):
       
  1405         return self.expr(node.left, current_klass) + " >>> " + self.expr(node.right, current_klass)
       
  1406 
       
  1407     def _bitxor(self, node, current_klass):
       
  1408         return " ^ ".join([self.expr(child, current_klass) for child in node.nodes])
       
  1409 
       
  1410     def _bitor(self, node, current_klass):
       
  1411         return " | ".join([self.expr(child, current_klass) for child in node.nodes])
       
  1412 
       
  1413     def _subscript(self, node, current_klass):
       
  1414         if node.flags == "OP_APPLY":
       
  1415             if len(node.subs) == 1:
       
  1416                 return self.expr(node.expr, current_klass) + ".__getitem__(" + self.expr(node.subs[0], current_klass) + ")"
       
  1417             else:
       
  1418                 raise TranslationError("must have one sub (in _subscript)", node)
       
  1419         else:
       
  1420             raise TranslationError("unsupported flag (in _subscript)", node)
       
  1421 
       
  1422     def _subscript_stmt(self, node, current_klass):
       
  1423         if node.flags == "OP_DELETE":
       
  1424             self.printo("    " + self.expr(node.expr, current_klass) + ".__delitem__(" + self.expr(node.subs[0], current_klass) + ");")
       
  1425         else:
       
  1426             raise TranslationError("unsupported flag (in _subscript)", node)
       
  1427 
       
  1428     def _list(self, node, current_klass):
       
  1429         return "new pyjslib.List([" + ", ".join([self.expr(x, current_klass) for x in node.nodes]) + "])"
       
  1430 
       
  1431     def _dict(self, node, current_klass):
       
  1432         items = []
       
  1433         for x in node.items:
       
  1434             key = self.expr(x[0], current_klass)
       
  1435             value = self.expr(x[1], current_klass)
       
  1436             items.append("[" + key + ", " + value + "]")
       
  1437         return "new pyjslib.Dict([" + ", ".join(items) + "])"
       
  1438 
       
  1439     def _tuple(self, node, current_klass):
       
  1440         return "new pyjslib.Tuple([" + ", ".join([self.expr(x, current_klass) for x in node.nodes]) + "])"
       
  1441 
       
  1442     def _lambda(self, node, current_klass):
       
  1443         if node.varargs:
       
  1444             raise TranslationError("varargs are not supported in Lambdas", node)
       
  1445         if node.kwargs:
       
  1446             raise TranslationError("kwargs are not supported in Lambdas", node)
       
  1447         res = cStringIO()
       
  1448         arg_names = list(node.argnames)
       
  1449         function_args = ", ".join(arg_names)
       
  1450         for child in node.getChildNodes():
       
  1451             expr = self.expr(child, None)
       
  1452         print("function (%s){" % function_args, file=res)
       
  1453         self._default_args_handler(node, arg_names, None,
       
  1454                                    output=res)
       
  1455         print('return %s;}' % expr, file=res)
       
  1456         return res.getvalue()
       
  1457 
       
  1458     def _slice(self, node, current_klass):
       
  1459         if node.flags == "OP_APPLY":
       
  1460             lower = "null"
       
  1461             upper = "null"
       
  1462             if node.lower is not None:
       
  1463                 lower = self.expr(node.lower, current_klass)
       
  1464             if node.upper is not None:
       
  1465                 upper = self.expr(node.upper, current_klass)
       
  1466             return "pyjslib.slice(" + self.expr(node.expr, current_klass) + ", " + lower + ", " + upper + ")"
       
  1467         else:
       
  1468             raise TranslationError("unsupported flag (in _slice)", node)
       
  1469 
       
  1470     def _global(self, node, current_klass):
       
  1471         for name in node.names:
       
  1472             self.method_imported_globals.add(name)
       
  1473 
       
  1474     def expr(self, node, current_klass):
       
  1475         if isinstance(node, ast.Const):
       
  1476             return self._const(node)
       
  1477         # @@@ not sure if the parentheses should be here or in individual operator functions - JKT
       
  1478         elif isinstance(node, ast.Mul):
       
  1479             return " ( " + self._mul(node, current_klass) + " ) "
       
  1480         elif isinstance(node, ast.Add):
       
  1481             return " ( " + self._add(node, current_klass) + " ) "
       
  1482         elif isinstance(node, ast.Sub):
       
  1483             return " ( " + self._sub(node, current_klass) + " ) "
       
  1484         elif isinstance(node, ast.Div):
       
  1485             return " ( " + self._div(node, current_klass) + " ) "
       
  1486         elif isinstance(node, ast.Mod):
       
  1487             return self._mod(node, current_klass)
       
  1488         elif isinstance(node, ast.UnaryAdd):
       
  1489             return self._unaryadd(node, current_klass)
       
  1490         elif isinstance(node, ast.UnarySub):
       
  1491             return self._unarysub(node, current_klass)
       
  1492         elif isinstance(node, ast.Not):
       
  1493             return self._not(node, current_klass)
       
  1494         elif isinstance(node, ast.Or):
       
  1495             return self._or(node, current_klass)
       
  1496         elif isinstance(node, ast.And):
       
  1497             return self._and(node, current_klass)
       
  1498         elif isinstance(node, ast.Invert):
       
  1499             return self._invert(node, current_klass)
       
  1500         elif isinstance(node, ast.Bitand):
       
  1501             return "("+self._bitand(node, current_klass)+")"
       
  1502         elif isinstance(node, ast.LeftShift):
       
  1503             return self._bitshiftleft(node, current_klass)
       
  1504         elif isinstance(node, ast.RightShift):
       
  1505             return self._bitshiftright(node, current_klass)
       
  1506         elif isinstance(node, ast.Bitxor):
       
  1507             return "("+self._bitxor(node, current_klass)+")"
       
  1508         elif isinstance(node, ast.Bitor):
       
  1509             return "("+self._bitor(node, current_klass)+")"
       
  1510         elif isinstance(node, ast.Compare):
       
  1511             return self._compare(node, current_klass)
       
  1512         elif isinstance(node, ast.CallFunc):
       
  1513             return self._callfunc(node, current_klass)
       
  1514         elif isinstance(node, ast.Name):
       
  1515             return self._name(node, current_klass)
       
  1516         elif isinstance(node, ast.Subscript):
       
  1517             return self._subscript(node, current_klass)
       
  1518         elif isinstance(node, ast.Getattr):
       
  1519             return self._getattr(node, current_klass)
       
  1520         elif isinstance(node, ast.List):
       
  1521             return self._list(node, current_klass)
       
  1522         elif isinstance(node, ast.Dict):
       
  1523             return self._dict(node, current_klass)
       
  1524         elif isinstance(node, ast.Tuple):
       
  1525             return self._tuple(node, current_klass)
       
  1526         elif isinstance(node, ast.Slice):
       
  1527             return self._slice(node, current_klass)
       
  1528         elif isinstance(node, ast.Lambda):
       
  1529             return self._lambda(node, current_klass)
       
  1530         else:
       
  1531             raise TranslationError("unsupported type (in expr)", node)
       
  1532 
       
  1533 
       
  1534 def translate(file_name, module_name, debug=False):
       
  1535     f = open(file_name, "r")
       
  1536     src = f.read()
       
  1537     f.close()
       
  1538     output = cStringIO()
       
  1539     mod = compiler.parseFile(file_name)
       
  1540     Translator(module_name, module_name, module_name, src, debug, mod, output)
       
  1541     return output.getvalue()
       
  1542 
       
  1543 
       
  1544 class PlatformParser(object):
       
  1545     def __init__(self, platform_dir="", verbose=True):
       
  1546         self.platform_dir = platform_dir
       
  1547         self.parse_cache = {}
       
  1548         self.platform = ""
       
  1549         self.verbose = verbose
       
  1550 
       
  1551     def setPlatform(self, platform):
       
  1552         self.platform = platform
       
  1553 
       
  1554     def parseModule(self, module_name, file_name):
       
  1555 
       
  1556         importing = False
       
  1557         if file_name not in self.parse_cache:
       
  1558             importing = True
       
  1559             mod = compiler.parseFile(file_name)
       
  1560             self.parse_cache[file_name] = mod
       
  1561         else:
       
  1562             mod = self.parse_cache[file_name]
       
  1563 
       
  1564         override = False
       
  1565         platform_file_name = self.generatePlatformFilename(file_name)
       
  1566         if self.platform and os.path.isfile(platform_file_name):
       
  1567             mod = copy.deepcopy(mod)
       
  1568             mod_override = compiler.parseFile(platform_file_name)
       
  1569             self.merge(mod, mod_override)
       
  1570             override = True
       
  1571 
       
  1572         if self.verbose:
       
  1573             if override:
       
  1574                 print("Importing %s (Platform %s)" % (module_name, self.platform))
       
  1575             elif importing:
       
  1576                 print("Importing %s" % (module_name))
       
  1577 
       
  1578         return mod, override
       
  1579 
       
  1580     def generatePlatformFilename(self, file_name):
       
  1581         (module_name, extension) = os.path.splitext(os.path.basename(file_name))
       
  1582         platform_file_name = module_name + self.platform + extension
       
  1583 
       
  1584         return os.path.join(os.path.dirname(file_name), self.platform_dir, platform_file_name)
       
  1585 
       
  1586     def merge(self, tree1, tree2):
       
  1587         for child in tree2.node:
       
  1588             if isinstance(child, ast.Function):
       
  1589                 self.replaceFunction(tree1, child.name, child)
       
  1590             elif isinstance(child, ast.Class):
       
  1591                 self.replaceClassMethods(tree1, child.name, child)
       
  1592 
       
  1593         return tree1
       
  1594 
       
  1595     def replaceFunction(self, tree, function_name, function_node):
       
  1596         # find function to replace
       
  1597         for child in tree.node:
       
  1598             if isinstance(child, ast.Function) and child.name == function_name:
       
  1599                 self.copyFunction(child, function_node)
       
  1600                 return
       
  1601         raise TranslationError("function not found: " + function_name, function_node)
       
  1602 
       
  1603     def replaceClassMethods(self, tree, class_name, class_node):
       
  1604         # find class to replace
       
  1605         old_class_node = None
       
  1606         for child in tree.node:
       
  1607             if isinstance(child, ast.Class) and child.name == class_name:
       
  1608                 old_class_node = child
       
  1609                 break
       
  1610 
       
  1611         if not old_class_node:
       
  1612             raise TranslationError("class not found: " + class_name, class_node)
       
  1613 
       
  1614         # replace methods
       
  1615         for function_node in class_node.code:
       
  1616             if isinstance(function_node, ast.Function):
       
  1617                 found = False
       
  1618                 for child in old_class_node.code:
       
  1619                     if isinstance(child, ast.Function) and child.name == function_node.name:
       
  1620                         found = True
       
  1621                         self.copyFunction(child, function_node)
       
  1622                         break
       
  1623 
       
  1624                 if not found:
       
  1625                     raise TranslationError("class method not found: " + class_name + "." + function_node.name, function_node)
       
  1626 
       
  1627     def copyFunction(self, target, source):
       
  1628         target.code = source.code
       
  1629         target.argnames = source.argnames
       
  1630         target.defaults = source.defaults
       
  1631         target.doc = source.doc  # @@@ not sure we need to do this any more
       
  1632 
       
  1633 
       
  1634 def dotreplace(fname):
       
  1635     path, ext = os.path.splitext(fname)
       
  1636     return path.replace(".", "/") + ext
       
  1637 
       
  1638 
       
  1639 class AppTranslator(object):
       
  1640 
       
  1641     def __init__(self, library_dirs=None, parser=None, dynamic=False,
       
  1642                  optimize=False, verbose=True):
       
  1643         self.extension = ".py"
       
  1644         self.optimize = optimize
       
  1645         self.library_modules = []
       
  1646         self.overrides = {}
       
  1647         library_dirs = [] if library_dirs is None else library_dirs
       
  1648         self.library_dirs = path + library_dirs
       
  1649         self.dynamic = dynamic
       
  1650         self.verbose = verbose
       
  1651 
       
  1652         if not parser:
       
  1653             self.parser = PlatformParser()
       
  1654         else:
       
  1655             self.parser = parser
       
  1656 
       
  1657         self.parser.dynamic = dynamic
       
  1658 
       
  1659     def findFile(self, file_name):
       
  1660         if os.path.isfile(file_name):
       
  1661             return file_name
       
  1662 
       
  1663         for library_dir in self.library_dirs:
       
  1664             file_name = dotreplace(file_name)
       
  1665             full_file_name = os.path.join(
       
  1666                 os.path.abspath(os.path.dirname(__file__)), library_dir, file_name)
       
  1667             if os.path.isfile(full_file_name):
       
  1668                 return full_file_name
       
  1669 
       
  1670             fnameinit, _ext = os.path.splitext(file_name)
       
  1671             fnameinit = fnameinit + "/__init__.py"
       
  1672 
       
  1673             full_file_name = os.path.join(
       
  1674                 os.path.abspath(os.path.dirname(__file__)), library_dir, fnameinit)
       
  1675             if os.path.isfile(full_file_name):
       
  1676                 return full_file_name
       
  1677 
       
  1678         raise Exception("file not found: " + file_name)
       
  1679 
       
  1680     def _translate(self, module_name, is_app=True, debug=False,
       
  1681                    imported_js=None):
       
  1682         if module_name not in self.library_modules:
       
  1683             self.library_modules.append(module_name)
       
  1684 
       
  1685         file_name = self.findFile(module_name + self.extension)
       
  1686 
       
  1687         output = cStringIO()
       
  1688 
       
  1689         f = open(file_name, "r")
       
  1690         src = f.read()
       
  1691         f.close()
       
  1692 
       
  1693         mod, override = self.parser.parseModule(module_name, file_name)
       
  1694         if override:
       
  1695             override_name = "%s.%s" % (self.parser.platform.lower(),
       
  1696                                        module_name)
       
  1697             self.overrides[override_name] = override_name
       
  1698         if is_app:
       
  1699             mn = '__main__'
       
  1700         else:
       
  1701             mn = module_name
       
  1702         t = Translator(mn, module_name, module_name,
       
  1703                        src, debug, mod, output, self.dynamic, self.optimize,
       
  1704                        self.findFile)
       
  1705 
       
  1706         module_str = output.getvalue()
       
  1707         if imported_js is None:
       
  1708             imported_js = set()
       
  1709         imported_js.update(set(t.imported_js))
       
  1710         imported_modules_str = ""
       
  1711         for module in t.imported_modules:
       
  1712             if module not in self.library_modules:
       
  1713                 self.library_modules.append(module)
       
  1714                 # imported_js.update(set(t.imported_js))
       
  1715                 # imported_modules_str += self._translate(
       
  1716                 #    module, False, debug=debug, imported_js=imported_js)
       
  1717 
       
  1718         return imported_modules_str + module_str
       
  1719 
       
  1720     def translate(self, module_name, is_app=True, debug=False,
       
  1721                   library_modules=None):
       
  1722         app_code = cStringIO()
       
  1723         lib_code = cStringIO()
       
  1724         imported_js = set()
       
  1725         self.library_modules = []
       
  1726         self.overrides = {}
       
  1727         if library_modules is not None:
       
  1728             for library in library_modules:
       
  1729                 if library.endswith(".js"):
       
  1730                     imported_js.add(library)
       
  1731                     continue
       
  1732                 self.library_modules.append(library)
       
  1733                 if self.verbose:
       
  1734                     print('Including LIB', library)
       
  1735                 print('\n//\n// BEGIN LIB '+library+'\n//\n', file=lib_code)
       
  1736                 print(self._translate(library, False, debug=debug, imported_js=imported_js),
       
  1737                       file=lib_code)
       
  1738 
       
  1739                 print("/* initialize static library */", file=lib_code)
       
  1740                 print("%s%s();\n" % (UU, library), file=lib_code)
       
  1741 
       
  1742                 print('\n//\n// END LIB '+library+'\n//\n', file=lib_code)
       
  1743         if module_name:
       
  1744             print(self._translate(module_name, is_app, debug=debug, imported_js=imported_js),
       
  1745                   file=app_code)
       
  1746         for js in imported_js:
       
  1747             path = self.findFile(js)
       
  1748             if os.path.isfile(path):
       
  1749                 if self.verbose:
       
  1750                     print('Including JS', js)
       
  1751                 print('\n//\n// BEGIN JS '+js+'\n//\n', file=lib_code)
       
  1752                 print(open(path).read(), file=lib_code)
       
  1753                 print('\n//\n// END JS '+js+'\n//\n', file=lib_code)
       
  1754             else:
       
  1755                 print('Warning: Unable to find imported javascript:', js, file=sys.stderr)
       
  1756         return lib_code.getvalue(), app_code.getvalue()
       
  1757 
       
  1758 
       
  1759 usage = """
       
  1760   usage: %s file_name [module_name]
       
  1761 """
       
  1762 
       
  1763 
       
  1764 def main():
       
  1765     if len(sys.argv) < 2:
       
  1766         print(usage % sys.argv[0], file=sys.stderr)
       
  1767         sys.exit(1)
       
  1768     file_name = os.path.abspath(sys.argv[1])
       
  1769     if not os.path.isfile(file_name):
       
  1770         print("File not found %s" % file_name, file=sys.stderr)
       
  1771         sys.exit(1)
       
  1772     if len(sys.argv) > 2:
       
  1773         module_name = sys.argv[2]
       
  1774     else:
       
  1775         module_name = None
       
  1776     print(translate(file_name, module_name), end="")
       
  1777 
       
  1778 
       
  1779 if __name__ == "__main__":
       
  1780     main()