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