PageRenderTime 397ms CodeModel.GetById 100ms app.highlight 259ms RepoModel.GetById 23ms app.codeStats 1ms

/pyjs/pyjs.py

http://pyjamas.googlecode.com/
Python | 1125 lines | 1094 code | 16 blank | 15 comment | 14 complexity | 7e2ee59463fdad3863d512565a344f55 MD5 | raw file
   1#!/usr/bin/env python
   2from types import StringType
   3
   4# Copyright 2006 James Tauber and contributors
   5#
   6# Licensed under the Apache License, Version 2.0 (the "License");
   7# you may not use this file except in compliance with the License.
   8# You may obtain a copy of the License at
   9#
  10#     http://www.apache.org/licenses/LICENSE-2.0
  11#
  12# Unless required by applicable law or agreed to in writing, software
  13# distributed under the License is distributed on an "AS IS" BASIS,
  14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15# See the License for the specific language governing permissions and
  16# limitations under the License.
  17
  18
  19import compiler
  20from compiler import ast
  21import os
  22import copy
  23
  24# this is the python function used to wrap native javascript
  25NATIVE_JS_FUNC_NAME = "JS"
  26
  27class Klass:
  28
  29    klasses = {}
  30
  31    def __init__(self, name):
  32        self.name = name
  33        self.klasses[name] = self
  34        self.functions = set()
  35        
  36    def set_base(self, base_name):
  37        self.base = self.klasses.get(base_name)
  38        
  39    def add_function(self, function_name):
  40        self.functions.add(function_name)
  41
  42
  43class TranslationError(Exception):
  44    def __init__(self, message, node):
  45        self.message = "line %s:\n%s\n%s" % (node.lineno, message, node)
  46
  47    def __str__(self):
  48        return self.message
  49
  50
  51class Translator:
  52
  53    def __init__(self, module_name, mod, output):
  54    
  55        if module_name:
  56            self.module_prefix = module_name + "_"
  57        else:
  58            self.module_prefix = ""
  59        self.imported_modules = set()
  60        self.imported_js = set()
  61        self.top_level_functions = set()
  62        self.top_level_classes = set()
  63        self.top_level_vars = set()
  64        self.output = output
  65        self.imported_classes = {}
  66        self.method_imported_globals = set()
  67        self.method_self = None
  68        
  69        for child in mod.node:
  70            if isinstance(child, ast.Function):
  71                self.top_level_functions.add(child.name)
  72            elif isinstance(child, ast.Class):
  73                self.top_level_classes.add(child.name)
  74
  75        for child in mod.node:
  76            if isinstance(child, ast.Function):
  77                self._function(child, False)
  78            elif isinstance(child, ast.Class):
  79                self._class(child)
  80            elif isinstance(child, ast.Import):
  81                importName = child.names[0][0]
  82                if importName == '__pyjamas__': # special module to help make pyjamas modules loadable in the python interpreter
  83                    pass
  84                elif importName.endswith('.js'):
  85                   self.imported_js.add(importName)
  86                else:
  87                   self.imported_modules.add(importName)
  88            elif isinstance(child, ast.From):
  89                if child.modname == '__pyjamas__': # special module to help make pyjamas modules loadable in the python interpreter
  90                    pass
  91                else:
  92                    self.imported_modules.add(child.modname)
  93                    self._from(child)
  94            elif isinstance(child, ast.Discard):
  95                self._discard(child, None)
  96            elif isinstance(child, ast.Assign):
  97                self._assign(child, None, True)
  98            else:
  99                raise TranslationError("unsupported type (in __init__)", child)
 100        
 101        # Initialize all classes for this module
 102        for className in self.top_level_classes:
 103            print >> self.output, "__"+self.module_prefix+className+"_initialize();"
 104    
 105    def _default_args_handler(self, node, arg_names, current_klass):
 106        if len(node.defaults):
 107            default_pos = len(arg_names) - len(node.defaults)
 108            if arg_names and arg_names[0] == self.method_self:
 109                default_pos -= 1
 110            for default_node in node.defaults:
 111                if isinstance(default_node, ast.Const):
 112                    default_value = self._const(default_node)
 113                elif isinstance(default_node, ast.Name):
 114                    default_value = self._name(default_node)
 115                elif isinstance(default_node, ast.UnarySub):
 116                    default_value = self._unarysub(default_node, current_klass)
 117                else:
 118                    raise TranslationError("unsupported type (in _method)", default_node)
 119                
 120                default_name = arg_names[default_pos]
 121                default_pos += 1
 122                print >>self.output, "    if (typeof %s == 'undefined') %s=%s;" % (default_name, default_name, default_value)
 123    
 124    def _varargs_handler(self, node, varargname, arg_names, current_klass):
 125        print >>self.output, "    var", varargname, '= new pyjslib_List([]);'
 126        print >>self.output, "    for(var __va_arg="+str(len(arg_names))+"; __va_arg < arguments.length; __va_arg++) {"
 127        print >>self.output, "        var __arg = arguments[__va_arg];"
 128        print >>self.output, "        "+varargname+".append(__arg);"
 129        print >>self.output, "    }"
 130                
 131    def _kwargs_parser(self, node, function_name, arg_names, current_klass):
 132        if len(node.defaults) or node.kwargs:
 133            default_pos = len(arg_names) - len(node.defaults)
 134            if arg_names and arg_names[0] == self.method_self:
 135                default_pos -= 1
 136            print >>self.output, function_name+'.parse_kwargs = function (', ", ".join(["__kwargs"]+arg_names), ") {"
 137            for default_node in node.defaults:
 138                default_value = self.expr(default_node, current_klass)
 139#                if isinstance(default_node, ast.Const):
 140#                    default_value = self._const(default_node)
 141#                elif isinstance(default_node, ast.Name):
 142#                    default_value = self._name(default_node)
 143#                elif isinstance(default_node, ast.UnarySub):
 144#                    default_value = self._unarysub(default_node, current_klass)
 145#                else:
 146#                    raise TranslationError("unsupported type (in _method)", default_node)
 147                
 148                default_name = arg_names[default_pos]
 149                print >>self.output, "    if (typeof %s == 'undefined')"%(default_name)
 150                print >>self.output, "        %s=__kwargs.%s;"% (default_name, default_name)
 151                default_pos += 1
 152            
 153            #self._default_args_handler(node, arg_names, current_klass)
 154            if node.kwargs: arg_names += ["pyjslib_Dict(__kwargs)"]
 155            print >>self.output, "    var __r = "+"".join(["[", ", ".join(arg_names), "]"])+";"
 156            if node.varargs:
 157                self._varargs_handler(node, "__args", arg_names, current_klass)
 158                print >>self.output, "    __r.push.apply(__r, __args.getArray())"
 159            print >>self.output, "    return __r;"
 160            print >>self.output, "};"
 161        
 162    def _function(self, node, local=False):
 163        if local: function_name = node.name
 164        else: function_name = self.module_prefix + node.name
 165        arg_names = list(node.argnames)
 166        normal_arg_names = list(arg_names)
 167        if node.kwargs: kwargname = normal_arg_names.pop()
 168        if node.varargs: varargname = normal_arg_names.pop()
 169        declared_arg_names = list(normal_arg_names)
 170        if node.kwargs: declared_arg_names.append(kwargname)
 171        
 172        function_args = "(" + ", ".join(declared_arg_names) + ")"
 173        print >>self.output, "function %s%s {" % (function_name, function_args)
 174        self._default_args_handler(node, normal_arg_names, None)
 175        
 176        if node.varargs:
 177            self._varargs_handler(node, varargname, declared_arg_names, None)
 178        
 179        for child in node.code:
 180            self._stmt(child, None)
 181            
 182        print >>self.output, "}"
 183        print >>self.output, "\n"
 184        
 185        self._kwargs_parser(node, function_name, normal_arg_names, None)
 186    
 187    
 188    def _return(self, node, current_klass):
 189        expr = self.expr(node.value, current_klass)
 190        if expr != "null":
 191            print >>self.output, "    return " + expr + ";"
 192        else:
 193            print >>self.output, "    return;"
 194
 195
 196    def _break(self, node, current_klass):
 197        print >>self.output, "    break;"
 198
 199
 200    def _continue(self, node, current_klass):
 201        print >>self.output, "    continue;"
 202
 203
 204    def _callfunc(self, v, current_klass):
 205        if isinstance(v.node, ast.Name):
 206            if v.node.name in self.top_level_functions:
 207                call_name = self.module_prefix + v.node.name
 208            elif v.node.name in self.top_level_classes:
 209                call_name = self.module_prefix + v.node.name
 210            elif self.imported_classes.has_key(v.node.name):
 211                call_name = self.imported_classes[v.node.name] + '_' + v.node.name
 212            elif v.node.name == "callable":
 213                call_name = "pyjslib_isFunction"
 214            elif v.node.name == "map":
 215                call_name = "pyjslib_map"
 216            elif v.node.name == "filter":
 217                call_name = "pyjslib_filter"
 218            elif v.node.name == "dir":
 219                call_name = "pyjslib_dir"
 220            elif v.node.name == "getattr":
 221                call_name = "pyjslib_getattr"
 222            elif v.node.name == "hasattr":
 223                call_name = "pyjslib_hasattr"
 224            elif v.node.name == "int":
 225                call_name = "pyjslib_int"
 226            elif v.node.name == "str":
 227                call_name = "pyjslib_str"
 228            elif v.node.name == "range":
 229                call_name = "pyjslib_range"
 230            elif v.node.name == "len":
 231                call_name = "pyjslib_len"
 232            elif v.node.name == "hash":
 233                call_name = "pyjslib_hash"
 234            elif v.node.name == "abs":
 235                call_name = "pyjslib_abs"
 236            else:
 237                call_name = v.node.name
 238            call_args = []
 239        elif isinstance(v.node, ast.Getattr):
 240            attr_name = v.node.attrname
 241            if isinstance(v.node.expr, ast.Name):
 242                call_name = self._name2(v.node.expr, current_klass, attr_name)
 243                call_args = []
 244            elif isinstance(v.node.expr, ast.Getattr):
 245                call_name = self._getattr2(v.node.expr, current_klass, attr_name)
 246                call_args = []
 247            elif isinstance(v.node.expr, ast.CallFunc):
 248                call_name = self._callfunc(v.node.expr, current_klass) + "." + v.node.attrname
 249                call_args = []
 250            elif isinstance(v.node.expr, ast.Subscript):
 251                call_name = self._subscript(v.node.expr, current_klass) + "." + v.node.attrname
 252                call_args = []
 253            elif isinstance(v.node.expr, ast.Const):
 254                call_name = self.expr(v.node.expr, current_klass) + "." + v.node.attrname
 255                call_args = []
 256            else:
 257                raise TranslationError("unsupported type (in _callfunc)", v.node.expr)
 258        else:
 259            raise TranslationError("unsupported type (in _callfunc)", v.node)
 260         
 261        kwargs = []
 262        for ch4 in v.args:
 263            if isinstance(ch4, ast.Keyword):
 264                kwarg = ch4.name + ":" + self.expr(ch4.expr, current_klass)
 265                kwargs.append(kwarg)
 266            else:
 267                arg = self.expr(ch4, current_klass)
 268                call_args.append(arg)
 269        
 270        if kwargs:
 271            try: call_this, method_name = call_name.rsplit(".", 1)
 272            except ValueError:
 273                # Must be a function call ...
 274                return ("pyjs_kwargs_function_call("+call_name+", "
 275                                  + "["+", ".join(['{' + ', '.join(kwargs) + '}']+call_args)+"]"
 276                                  + ")" )
 277            else:
 278                return ("pyjs_kwargs_method_call("+call_this+", '"+method_name+"', "
 279                                  + "["+", ".join(['{' + ', '.join(kwargs) + '}']+call_args)+"]"
 280                                  + ")")
 281        else:
 282            return call_name + "(" + ", ".join(call_args) + ")"
 283    
 284    def _print(self, node, current_klass):
 285        call_args = []
 286        for ch4 in node.nodes:
 287            arg = self.expr(ch4, current_klass)
 288            call_args.append(arg)
 289        
 290        print >>self.output, "pyjslib_printFunc([", ', '.join(call_args), "],", int(isinstance(node, ast.Printnl)), ");"
 291        
 292
 293    def _getattr(self, v):
 294        attr_name = v.attrname
 295        if isinstance(v.expr, ast.Name):
 296            obj = self._name(v.expr)
 297            return obj + "." + attr_name
 298        elif isinstance(v.expr, ast.Getattr):
 299            return self._getattr(v.expr) + "." + attr_name
 300        elif isinstance(v.expr, ast.Subscript):
 301            return self._subscript(v.expr, self.module_prefix) + "." + attr_name
 302        elif isinstance(v.expr, ast.CallFunc):
 303            return self._callfunc(v.expr, self.module_prefix) + "." + attr_name
 304        else:
 305            raise TranslationError("unsupported type (in _getattr)", v.expr)
 306    
 307    
 308    def _name(self, v):
 309        if v.name == "True":
 310            return "true"
 311        elif v.name == "False":
 312            return "false"
 313        elif v.name == "None":
 314            return "null"
 315        elif v.name == self.method_self:
 316            return "this"
 317        elif v.name in self.method_imported_globals:
 318            return self.module_prefix + v.name
 319        elif self.imported_classes.has_key(v.name):
 320            return "__" + self.imported_classes[v.name] + '_' + v.name + ".prototype.__class__"
 321        elif v.name in self.top_level_classes:
 322            return "__" + self.module_prefix + v.name + ".prototype.__class__"
 323        else:
 324            return v.name
 325
 326
 327    def _name2(self, v, current_klass, attr_name):
 328        obj = v.name
 329
 330        if obj in self.method_imported_globals:
 331            call_name = self.module_prefix + obj + "." + attr_name
 332        elif self.imported_classes.has_key(obj):
 333            #attr_str = ""
 334            #if attr_name != "__init__":
 335            attr_str = ".prototype.__class__." + attr_name
 336            call_name = "__" + self.imported_classes[obj] + '_' + obj + attr_str
 337        elif obj in self.imported_modules:
 338            call_name = obj + "_" + attr_name
 339        elif obj[0] == obj[0].upper():
 340            call_name = "__" + self.module_prefix + obj + ".prototype.__class__." + attr_name
 341        else:
 342            call_name = self._name(v) + "." + attr_name
 343
 344        return call_name
 345
 346
 347    def _getattr2(self, v, current_klass, attr_name):
 348        if isinstance(v.expr, ast.Getattr):
 349            call_name = self._getattr2(v.expr, current_klass, v.attrname + "." + attr_name)
 350        else:
 351            obj = self.expr(v.expr, current_klass)
 352            call_name = obj + "." + v.attrname + "." + attr_name
 353            
 354        return call_name
 355    
 356    
 357    def _class(self, node):
 358        """
 359        Handle a class definition.
 360        
 361        In order to translate python semantics reasonably well, the following
 362        structure is used:
 363        
 364        A special object is created for the class, which inherits attributes
 365        from the superclass, or Object if there's no superclass.  This is the
 366        class object; the object which you refer to when specifying the
 367        class by name.  Static, class, and unbound methods are copied
 368        from the superclass object.
 369        
 370        A special constructor function is created with the same name as the
 371        class, which is used to create instances of that class.
 372        
 373        A javascript class (e.g. a function with a prototype attribute) is
 374        created which is the javascript class of created instances, and
 375        which inherits attributes from the class object. Bound methods are 
 376        copied from the superclass into this class rather than inherited,
 377        because the class object contains unbound, class, and static methods
 378        that we don't necessarily want to inherit.
 379        
 380        The type of a method can now be determined by inspecting its
 381        static_method, unbound_method, class_method, or instance_method
 382        attribute; only one of these should be true.
 383        
 384        Much of this work is done in pyjs_extend, is pyjslib.py
 385        """
 386        class_name = self.module_prefix + node.name
 387        current_klass = Klass(class_name)
 388        
 389        init_method = None
 390        for child in node.code:
 391            if isinstance(child, ast.Function):
 392                current_klass.add_function(child.name)
 393                if child.name == "__init__":
 394                    init_method = child
 395                
 396
 397        if len(node.bases) == 0:
 398            base_class = "pyjslib_Object"
 399        elif len(node.bases) == 1:
 400            if isinstance(node.bases[0], ast.Name):
 401                if self.imported_classes.has_key(node.bases[0].name):
 402                    base_class = self.imported_classes[node.bases[0].name] + '_' + node.bases[0].name
 403                else:
 404                    base_class = self.module_prefix + node.bases[0].name
 405            elif isinstance(node.bases[0], ast.Getattr):
 406                base_class = self._name(node.bases[0].expr) + "_" + node.bases[0].attrname
 407            else:
 408                raise TranslationError("unsupported type (in _class)", node.bases[0])
 409
 410            current_klass.set_base(base_class)
 411        else:
 412            raise TranslationError("more than one base (in _class)", node)
 413        
 414        print >>self.output, "function __" + class_name + "() {"
 415        # call superconstructor
 416        #if base_class:
 417        #    print >>self.output, "    __" + base_class + ".call(this);"
 418        print >>self.output, "}"
 419        
 420        if not init_method:
 421            init_method = ast.Function([], "__init__", ["self"], [], 0, None, [])
 422            #self._method(init_method, current_klass, class_name)
 423            
 424        # Generate a function which constructs the object
 425        clsfunc = ast.Function([], 
 426           node.name, 
 427           init_method.argnames[1:],
 428           init_method.defaults, 
 429           init_method.flags, 
 430           None,
 431           [ast.Discard(ast.CallFunc(ast.Name("JS"), [ast.Const(
 432#            I attempted lazy initialization, but then you can't access static class members
 433#            "    if(!__"+base_class+".__was_initialized__)"+
 434#            "        __" + class_name + "_initialize();\n" +
 435            "    var instance = new __" + class_name + "();\n" +
 436            "    if(instance.__init__) instance.__init__.apply(instance, arguments);\n" +
 437            "    return instance;"
 438            )]))])
 439        
 440        self._function(clsfunc, False)
 441        print >>self.output, "function __" + class_name + "_initialize() {"
 442        print >>self.output, "    if(__"+class_name+".__was_initialized__) return;"
 443        print >>self.output, "    __"+class_name+".__was_initialized__ = true;"
 444        cls_obj = "__" + class_name + '.prototype.__class__'
 445    
 446        if class_name == "pyjslib_Object":
 447            print >>self.output, "    "+cls_obj+" = {};"
 448        else:
 449            if base_class and base_class not in ("object", "pyjslib_Object"):
 450                print >>self.output, "    if(!__"+base_class+".__was_initialized__)"
 451                print >>self.output, "        __"+base_class+"_initialize();"
 452                print >>self.output, "    pyjs_extend(__" + class_name + ", __"+base_class+");"
 453            else:
 454                print >>self.output, "    pyjs_extend(__" + class_name + ", __pyjslib_Object);"
 455        
 456        print >>self.output, "    "+cls_obj+".__new__ = "+class_name+";"
 457        
 458        for child in node.code:
 459            if isinstance(child, ast.Pass):
 460                pass
 461            elif isinstance(child, ast.Function):
 462                self._method(child, current_klass, class_name)
 463            elif isinstance(child, ast.Assign):
 464                self.classattr(child, current_klass)
 465            elif isinstance(child, ast.Discard) and isinstance(child.expr, ast.Const):
 466                # Probably a docstring, turf it
 467                pass
 468            else:
 469                raise TranslationError("unsupported type (in _class)", child)
 470        print >>self.output, "}"
 471        
 472        
 473    
 474    def classattr(self, node, current_klass):
 475        self._assign(node, current_klass, True)
 476    
 477        
 478    def _method(self, node, current_klass, class_name):
 479        # reset global var scope
 480        self.method_imported_globals = set()
 481        
 482        arg_names = list(node.argnames)
 483        
 484        classmethod = False
 485        staticmethod = False
 486        if node.decorators:
 487            for d in node.decorators:
 488                if d.name == "classmethod":
 489                    classmethod = True
 490                elif d.name == "staticmethod":
 491                    staticmethod = True
 492       
 493        if staticmethod:
 494            staticfunc = ast.Function([], class_name+"_"+node.name, node.argnames, node.defaults, node.flags, node.doc, node.code, node.lineno)
 495            self._function(staticfunc, True)
 496            print >>self.output, "    __" + class_name + ".prototype.__class__." + node.name + " = " + class_name+"_"+node.name+";";
 497            print >>self.output, "    __" + class_name + ".prototype.__class__." + node.name + ".static_method = true;";
 498            return
 499        else: 
 500            if len(arg_names) == 0:
 501                raise TranslationError("methods must take an argument 'self' (in _method)", node)
 502            self.method_self = arg_names[0]
 503            
 504            #if not classmethod and arg_names[0] != "self":
 505            #    raise TranslationError("first arg not 'self' (in _method)", node)
 506
 507        normal_arg_names = arg_names[1:]
 508        if node.kwargs: kwargname = normal_arg_names.pop()
 509        if node.varargs: varargname = normal_arg_names.pop()        
 510        declared_arg_names = list(normal_arg_names)
 511        if node.kwargs: declared_arg_names.append(kwargname)
 512        
 513        function_args = "(" + ", ".join(declared_arg_names) + ")"
 514    
 515        if classmethod:
 516            fexpr = "__" + class_name + ".prototype.__class__." + node.name
 517        else:
 518            fexpr = "__" + class_name + ".prototype." + node.name
 519        print >>self.output, "    "+fexpr + " = function" + function_args + " {"
 520
 521        # default arguments
 522        self._default_args_handler(node, normal_arg_names, current_klass)
 523        
 524        if node.varargs:
 525            self._varargs_handler(node, varargname, declared_arg_names, current_klass)
 526
 527        for child in node.code:
 528            self._stmt(child, current_klass)
 529        
 530        print >>self.output, "    };"
 531            
 532        self._kwargs_parser(node, fexpr, normal_arg_names, current_klass)
 533        
 534        if classmethod:
 535            # Have to create a version on the instances which automatically passes the
 536            # class as "self"
 537            altexpr = "__" + class_name + ".prototype." + node.name
 538            print >>self.output, "    "+altexpr + " = function() {"
 539            print >>self.output, "        return " + fexpr + ".apply(this.__class__, arguments);"
 540            print >>self.output, "    };"
 541            print >>self.output, "    "+fexpr+".class_method = true;"
 542            print >>self.output, "    "+altexpr+".instance_method = true;"
 543        else:
 544            # For instance methods, we need an unbound version in the class object
 545            altexpr = "__" + class_name + ".prototype.__class__." + node.name
 546            print >>self.output, "    "+altexpr + " = function() {"
 547            print >>self.output, "        return " + fexpr + ".call.apply("+fexpr+", arguments);"
 548            print >>self.output, "    };"
 549            print >>self.output, "    "+altexpr+".unbound_method = true;"
 550            print >>self.output, "    "+fexpr+".instance_method = true;"
 551            
 552        if node.kwargs or len(node.defaults):
 553            print >>self.output, "    "+altexpr + ".parse_kwargs = " + fexpr + ".parse_kwargs;"
 554        
 555        self.method_self = None
 556        self.method_imported_globals = set()
 557
 558    def _stmt(self, node, current_klass):
 559        if isinstance(node, ast.Return):
 560            self._return(node, current_klass)
 561        elif isinstance(node, ast.Break):
 562            self._break(node, current_klass)
 563        elif isinstance(node, ast.Continue):
 564            self._continue(node, current_klass)
 565        elif isinstance(node, ast.Assign):
 566            self._assign(node, current_klass)
 567        elif isinstance(node, ast.AugAssign):
 568            self._augassign(node, current_klass)
 569        elif isinstance(node, ast.Discard):
 570            self._discard(node, current_klass)
 571        elif isinstance(node, ast.If):
 572            self._if(node, current_klass)
 573        elif isinstance(node, ast.For):
 574            self._for(node, current_klass)
 575        elif isinstance(node, ast.While):
 576            self._while(node, current_klass)
 577        elif isinstance(node, ast.Subscript):
 578            self._subscript_stmt(node, current_klass)
 579        elif isinstance(node, ast.Global):
 580            self._global(node, current_klass)
 581        elif isinstance(node, ast.Pass):
 582            pass
 583        elif isinstance(node, ast.Function):
 584            self._function(node, True)
 585        elif isinstance(node, ast.Printnl):
 586           self._print(node, current_klass)
 587        elif isinstance(node, ast.Print):
 588           self._print(node, current_klass)
 589        else:
 590            raise TranslationError("unsupported type (in _stmt)", node)
 591    
 592    def _augassign(self, node, current_klass):
 593        v = node.node
 594        if isinstance(v, ast.Getattr):
 595            lhs = self._getattr(v)
 596        else:
 597            lhs = self._name(node.node)
 598        op = node.op
 599        rhs = self.expr(node.expr, current_klass)
 600        print >>self.output, "    " + lhs + " " + op + " " + rhs + ";"
 601
 602    
 603    def _assign(self, node, current_klass, top_level = False):
 604        if len(node.nodes) != 1:
 605            tempvar = '__temp'+str(node.lineno)
 606            tnode = ast.Assign([ast.AssName(tempvar, "OP_ASSIGN", node.lineno)], node.expr, node.lineno)
 607            self._assign(tnode, current_klass, top_level)
 608            for v in node.nodes:
 609               tnode2 = ast.Assign([v], ast.Name(tempvar, node.lineno), node.lineno)
 610               self._assign(tnode2, current_klass, top_level)
 611            return
 612
 613        v = node.nodes[0]
 614        if isinstance(v, ast.AssAttr):
 615            attr_name = v.attrname
 616            if isinstance(v.expr, ast.Name):
 617                obj = v.expr.name
 618                lhs = "    " + self._name(v.expr) + "." + attr_name
 619
 620            elif isinstance(v.expr, ast.Getattr):
 621                lhs = "    " + self._getattr(v)
 622            elif isinstance(v.expr, ast.Subscript):
 623                lhs = "    " + self._subscript(v.expr, current_klass) + "." + attr_name
 624            else:
 625                raise TranslationError("unsupported type (in _assign)", v.expr)
 626            if v.flags == "OP_ASSIGN":
 627                op = "="
 628            else:
 629                raise TranslationError("unsupported flag (in _assign)", v)
 630    
 631        elif isinstance(v, ast.AssName):
 632            if top_level:
 633                if current_klass:
 634                    lhs = "__" + current_klass.name + ".prototype.__class__." + v.name
 635                else:
 636                    self.top_level_vars.add(v.name)
 637                    lhs = "var " + self.module_prefix + v.name
 638            else:
 639                if v.name in self.method_imported_globals:
 640                    lhs = "    " + self.module_prefix + v.name
 641                else:
 642                    lhs = "    var " + v.name
 643            if v.flags == "OP_ASSIGN":
 644                op = "="
 645            else:
 646                raise TranslationError("unsupported flag (in _assign)", v)
 647        elif isinstance(v, ast.Subscript):
 648            if v.flags == "OP_ASSIGN":
 649                obj = self.expr(v.expr, current_klass)
 650                if len(v.subs) != 1:
 651                    raise TranslationError("must have one sub (in _assign)", v)
 652                idx = self.expr(v.subs[0], current_klass)
 653                value = self.expr(node.expr, current_klass)
 654                print >>self.output, "    " + obj + ".__setitem__(" + idx + ", " + value + ");"
 655                return
 656            else:
 657                raise TranslationError("unsupported flag (in _assign)", v)
 658        else:
 659            raise TranslationError("unsupported type (in _assign)", v)
 660    
 661
 662        rhs = self.expr(node.expr, current_klass)
 663        print >>self.output, lhs + " " + op + " " + rhs + ";"
 664    
 665    
 666    def _discard(self, node, current_klass):
 667        if isinstance(node.expr, ast.CallFunc):
 668            if isinstance(node.expr.node, ast.Name) and node.expr.node.name == NATIVE_JS_FUNC_NAME:
 669                if len(node.expr.args) != 1:
 670                    raise TranslationError("native javascript function %s must have one arg" % NATIVE_JS_FUNC_NAME, node.expr)
 671                if not isinstance(node.expr.args[0], ast.Const):
 672                    raise TranslationError("native javascript function %s must have constant arg" % NATIVE_JS_FUNC_NAME, node.expr)
 673                print >>self.output, node.expr.args[0].value
 674            else:
 675                expr = self._callfunc(node.expr, current_klass)
 676                print >>self.output, "    " + expr + ";"
 677        elif isinstance(node.expr, ast.Const):
 678            if node.expr.value is not None: # Empty statements generate ignore None
 679                print >>self.output, self._const(node.expr)
 680        else:
 681            raise TranslationError("unsupported type (in _discard)", node.expr)
 682    
 683    
 684    def _if(self, node, current_klass):
 685        for i in range(len(node.tests)):
 686            test, consequence = node.tests[i]
 687            if i == 0:
 688                keyword = "if"
 689            else:
 690                keyword = "else if"
 691
 692            self._if_test(keyword, test, consequence, current_klass)
 693            
 694        if node.else_:
 695            keyword = "else"
 696            test = None
 697            consequence = node.else_
 698
 699            self._if_test(keyword, test, consequence, current_klass)
 700            
 701        
 702    def _if_test(self, keyword, test, consequence, current_klass):
 703    
 704        if test:
 705            expr = self.expr(test, current_klass)
 706    
 707            print >>self.output, "    " + keyword + " (" + expr + ") {"
 708        else:
 709            print >>self.output, "    " + keyword + " {"
 710
 711        if isinstance(consequence, ast.Stmt):
 712            for child in consequence.nodes:
 713                self._stmt(child, current_klass)
 714        else:
 715            raise TranslationError("unsupported type (in _if_test)", consequence)
 716
 717        print >>self.output, "    }"
 718
 719
 720    def _from(self, node):
 721        for name in node.names:
 722            self.imported_classes[name[0]] = node.modname
 723
 724
 725    def _compare(self, node, current_klass):
 726        lhs = self.expr(node.expr, current_klass)
 727
 728        if len(node.ops) != 1:
 729            raise TranslationError("only one ops supported (in _compare)", node)
 730
 731        op = node.ops[0][0]
 732        rhs_node = node.ops[0][1]
 733        rhs = self.expr(rhs_node, current_klass)
 734
 735        if op == "in":
 736            return rhs + ".__contains__(" + lhs + ")"
 737        elif op == "not in":
 738            return "!" + rhs + ".__contains__(" + lhs + ")"
 739        elif op == "is":
 740            op = "=="
 741           
 742        return "(" + lhs + " " + op + " " + rhs + ")"
 743
 744
 745    def _not(self, node, current_klass):
 746        expr = self.expr(node.expr, current_klass)
 747
 748        return "!(" + expr + ")"
 749
 750    def _or(self, node, current_klass):
 751        expr = " || ".join([self.expr(child, current_klass) for child in node.nodes])
 752        return expr
 753
 754    def _and(self, node, current_klass):
 755        expr = " && ".join([self.expr(child, current_klass) for child in node.nodes])
 756        return expr
 757
 758    def _for(self, node, current_klass):
 759        assign_name = ""
 760        assign_tuple = ""
 761
 762        # based on Bob Ippolito's Iteration in Javascript code
 763        if isinstance(node.assign, ast.AssName):
 764            assign_name = node.assign.name
 765            if node.assign.flags == "OP_ASSIGN":
 766                op = "="
 767        elif isinstance(node.assign, ast.AssTuple):
 768            op = "="
 769            i = 0
 770            for child in node.assign:
 771                child_name = child.name
 772                if assign_name == "":
 773                    assign_name = "temp_" + child_name
 774
 775                assign_tuple += """
 776                var %(child_name)s %(op)s %(assign_name)s.__getitem__(%(i)i);
 777                """ % locals()
 778                i += 1
 779        else:
 780            raise TranslationError("unsupported type (in _for)", node.assign)
 781
 782        if isinstance(node.list, ast.Name):
 783            list_expr = self._name(node.list)
 784        elif isinstance(node.list, ast.Getattr):
 785            list_expr = self._getattr(node.list)
 786        elif isinstance(node.list, ast.CallFunc):
 787            list_expr = self._callfunc(node.list, current_klass)
 788        else:
 789            raise TranslationError("unsupported type (in _for)", node.list)
 790        
 791        lhs = "var " + assign_name
 792        iterator_name = "__" + assign_name
 793        
 794        print >>self.output, """
 795        var %(iterator_name)s = %(list_expr)s.__iter__();
 796        try {
 797            while (true) {
 798                %(lhs)s %(op)s %(iterator_name)s.next();
 799                %(assign_tuple)s
 800        """ % locals()
 801        for node in node.body.nodes:
 802            self._stmt(node, current_klass)
 803        print >>self.output, """
 804            }
 805        } catch (e) {
 806            if (e != StopIteration) {
 807                throw e;
 808            }
 809        }
 810        """ % locals()
 811
 812
 813    def _while(self, node, current_klass):
 814        test = self.expr(node.test, current_klass)
 815        print >>self.output, "    while (" + test + ") {"
 816        if isinstance(node.body, ast.Stmt):
 817            for child in node.body.nodes:
 818                self._stmt(child, current_klass)
 819        else:
 820            raise TranslationError("unsupported type (in _while)", node.body)
 821        print >>self.output, "    }"
 822
 823
 824    def _const(self, node):
 825        if isinstance(node.value, int):
 826            return str(node.value)
 827        elif isinstance(node.value, float):
 828            return str(node.value)
 829        elif isinstance(node.value, str):
 830            return "'" + node.value.encode('string_escape') + "'"
 831        elif node.value is None:
 832            return "null"
 833        else:
 834            raise TranslationError("unsupported type (in _const)", node)
 835
 836    def _unarysub(self, node, current_klass):
 837        return "-" + self.expr(node.expr, current_klass)
 838
 839    def _add(self, node, current_klass):
 840        return self.expr(node.left, current_klass) + " + " + self.expr(node.right, current_klass)
 841
 842    def _sub(self, node, current_klass):
 843        return self.expr(node.left, current_klass) + " - " + self.expr(node.right, current_klass)
 844
 845    def _div(self, node, current_klass):
 846        return self.expr(node.left, current_klass) + " / " + self.expr(node.right, current_klass)
 847
 848    def _mul(self, node, current_klass):
 849        return self.expr(node.left, current_klass) + " * " + self.expr(node.right, current_klass)
 850
 851    def _mod(self, node, current_klass):
 852        if isinstance(node.left, ast.Const) and isinstance(node.left.value, StringType):
 853           self.imported_js.add("sprintf.js") # Include the sprintf functionality if it is used
 854           return "sprintf("+self.expr(node.left, current_klass) + ", " + self.expr(node.right, current_klass)+")"
 855        return self.expr(node.left, current_klass) + " % " + self.expr(node.right, current_klass)
 856
 857    def _invert(self, node, current_klass):
 858        return "~" + self.expr(node.expr, current_klass)
 859
 860    def _bitand(self, node, current_klass):
 861        return " & ".join([self.expr(child, current_klass) for child in node.nodes])
 862
 863    def _bitor(self, node, current_klass):
 864        return " | ".join([self.expr(child, current_klass) for child in node.nodes])
 865
 866    def _subscript(self, node, current_klass):
 867        if node.flags == "OP_APPLY":
 868            if len(node.subs) == 1:
 869                return self.expr(node.expr, current_klass) + ".__getitem__(" + self.expr(node.subs[0], current_klass) + ")"
 870            else:
 871                raise TranslationError("must have one sub (in _subscript)", node)
 872        else:
 873            raise TranslationError("unsupported flag (in _subscript)", node)
 874
 875    def _subscript_stmt(self, node, current_klass):
 876        if node.flags == "OP_DELETE":
 877            print >>self.output, "    " + self.expr(node.expr, current_klass) + ".__delitem__(" + self.expr(node.subs[0], current_klass) + ");"
 878        else:
 879            raise TranslationError("unsupported flag (in _subscript)", node)
 880
 881    def _list(self, node, current_klass):
 882        return "new pyjslib_List([" + ", ".join([self.expr(x, current_klass) for x in node.nodes]) + "])"
 883
 884    def _dict(self, node, current_klass):
 885        items = []
 886        for x in node.items:
 887            key = self.expr(x[0], current_klass)
 888            value = self.expr(x[1], current_klass)
 889            items.append("[" + key + ", " + value + "]")
 890        return "new pyjslib_Dict([" + ", ".join(items) + "])"
 891
 892    def _tuple(self, node, current_klass):
 893        return "new pyjslib_Tuple([" + ", ".join([self.expr(x, current_klass) for x in node.nodes]) + "])"
 894
 895    def _slice(self, node, current_klass):
 896        if node.flags == "OP_APPLY":
 897            lower = "null"
 898            upper = "null"
 899            if node.lower != None:
 900                lower = self.expr(node.lower, current_klass)
 901            if node.upper != None:
 902                upper = self.expr(node.upper, current_klass)
 903            return  "pyjslib_slice(" + self.expr(node.expr, current_klass) + ", " + lower + ", " + upper + ")"
 904        else:
 905            raise TranslationError("unsupported flag (in _slice)", node)
 906
 907    def _global(self, node, current_klass):
 908        for name in node.names:
 909            self.method_imported_globals.add(name)
 910
 911    def expr(self, node, current_klass):
 912        if isinstance(node, ast.Const):
 913            return self._const(node)
 914        # @@@ not sure if the parentheses should be here or in individual operator functions - JKT
 915        elif isinstance(node, ast.Mul):
 916            return " ( " + self._mul(node, current_klass) + " ) "
 917        elif isinstance(node, ast.Add):
 918            return " ( " + self._add(node, current_klass) + " ) "
 919        elif isinstance(node, ast.Sub):
 920            return " ( " + self._sub(node, current_klass) + " ) "
 921        elif isinstance(node, ast.Div):
 922            return " ( " + self._div(node, current_klass) + " ) "
 923        elif isinstance(node, ast.Mod):
 924            return self._mod(node, current_klass)
 925        elif isinstance(node, ast.UnarySub):
 926            return self._unarysub(node, current_klass)
 927        elif isinstance(node, ast.Not):
 928            return self._not(node, current_klass)
 929        elif isinstance(node, ast.Or):
 930            return self._or(node, current_klass)
 931        elif isinstance(node, ast.And):
 932            return self._and(node, current_klass)
 933        elif isinstance(node, ast.Invert):
 934            return self._invert(node, current_klass)
 935        elif isinstance(node, ast.Bitand):
 936            return self._bitand(node, current_klass)
 937        elif isinstance(node, ast.Bitor):
 938            return self._bitor(node, current_klass)
 939        elif isinstance(node, ast.Compare):
 940            return self._compare(node, current_klass)
 941        elif isinstance(node, ast.CallFunc):
 942            return self._callfunc(node, current_klass)
 943        elif isinstance(node, ast.Name):
 944            return self._name(node)
 945        elif isinstance(node, ast.Subscript):
 946            return self._subscript(node, current_klass)
 947        elif isinstance(node, ast.Getattr):
 948            return self._getattr(node)
 949        elif isinstance(node, ast.List):
 950            return self._list(node, current_klass)
 951        elif isinstance(node, ast.Dict):
 952            return self._dict(node, current_klass)
 953        elif isinstance(node, ast.Tuple):
 954            return self._tuple(node, current_klass)
 955        elif isinstance(node, ast.Slice):
 956            return self._slice(node, current_klass)
 957        else:
 958            raise TranslationError("unsupported type (in expr)", node)
 959
 960
 961
 962import cStringIO
 963
 964def translate(file_name, module_name):
 965    output = cStringIO.StringIO()
 966    mod = compiler.parseFile(file_name)
 967    t = Translator(module_name, mod, output)
 968    return output.getvalue()
 969
 970
 971class PlatformParser:
 972    def __init__(self, platform_dir = ""):
 973        self.platform_dir = platform_dir
 974        self.parse_cache = {}
 975        self.platform = ""
 976
 977    def setPlatform(self, platform):
 978        self.platform = platform
 979        
 980    def parseModule(self, module_name, file_name):
 981        if self.parse_cache.has_key(file_name):
 982            mod = self.parse_cache[file_name]
 983        else:
 984            print "Importing " + module_name
 985            mod = compiler.parseFile(file_name)
 986            self.parse_cache[file_name] = mod
 987        
 988        platform_file_name = self.generatePlatformFilename(file_name)
 989        if self.platform and os.path.isfile(platform_file_name):
 990            mod = copy.deepcopy(mod)
 991            mod_override = compiler.parseFile(platform_file_name)
 992            self.merge(mod, mod_override)
 993
 994        return mod
 995        
 996    def generatePlatformFilename(self, file_name):
 997        (module_name, extension) = os.path.splitext(os.path.basename(file_name))
 998        platform_file_name = module_name + self.platform + extension
 999        
1000        return os.path.join(os.path.dirname(file_name), self.platform_dir, platform_file_name)
1001
1002    def merge(self, tree1, tree2):
1003        for child in tree2.node:
1004            if isinstance(child, ast.Function):
1005                self.replaceFunction(tree1, child.name, child)
1006            elif isinstance(child, ast.Class):
1007                self.replaceClassMethods(tree1, child.name, child)
1008
1009        return tree1
1010            
1011    def replaceFunction(self, tree, function_name, function_node):
1012        # find function to replace
1013        for child in tree.node:
1014            if isinstance(child, ast.Function) and child.name == function_name:
1015                self.copyFunction(child, function_node)
1016                return
1017        raise TranslationError("function not found: " + function_name, function_node)
1018
1019    def replaceClassMethods(self, tree, class_name, class_node):
1020        # find class to replace
1021        old_class_node = None
1022        for child in tree.node:
1023            if isinstance(child, ast.Class) and child.name == class_name:
1024                old_class_node = child
1025                break
1026        
1027        if not old_class_node:
1028            raise TranslationError("class not found: " + class_name, class_node)
1029        
1030        # replace methods
1031        for function_node in class_node.code:
1032            if isinstance(function_node, ast.Function):
1033                found = False
1034                for child in old_class_node.code:
1035                    if isinstance(child, ast.Function) and child.name == function_node.name:
1036                        found = True
1037                        self.copyFunction(child, function_node)
1038                        break
1039
1040                if not found:
1041                    raise TranslationError("class method not found: " + class_name + "." + function_node.name, function_node)
1042
1043    def copyFunction(self, target, source):
1044        target.code = source.code
1045        target.argnames = source.argnames
1046        target.defaults = source.defaults
1047        target.doc = source.doc # @@@ not sure we need to do this any more
1048
1049
1050class AppTranslator:
1051
1052    def __init__(self, library_dirs=["../library"], parser=None):
1053        self.extension = ".py"
1054
1055        self.library_modules = []
1056        self.library_dirs = library_dirs
1057        
1058        if not parser:
1059            self.parser = PlatformParser()
1060        else:
1061            self.parser = parser
1062
1063    def findFile(self, file_name):
1064        if os.path.isfile(file_name):
1065            return file_name
1066        
1067        for library_dir in self.library_dirs:
1068            full_file_name = os.path.join(os.path.dirname(__file__), library_dir, file_name)
1069            if os.path.isfile(full_file_name):
1070                return full_file_name
1071        
1072        raise Exception("file not found: " + file_name)
1073    
1074    def translate(self, module_name, is_app=True):
1075
1076        if module_name not in self.library_modules:
1077            self.library_modules.append(module_name)
1078        
1079        file_name = self.findFile(module_name + self.extension)
1080        
1081        if is_app:
1082            module_name_translated = ""
1083        else:
1084            module_name_translated = module_name
1085        
1086        output = cStringIO.StringIO()
1087        
1088        mod = self.parser.parseModule(module_name, file_name)
1089        t = Translator(module_name_translated, mod, output)
1090        module_str = output.getvalue()
1091        
1092        imported_modules_str = ""
1093        for module in t.imported_modules:
1094            if module not in self.library_modules:
1095                imported_modules_str += self.translate(module, False)
1096        for js in t.imported_js:
1097           path = self.findFile(js)
1098           if os.path.isfile(path):
1099              print 'Including', js
1100              imported_modules_str += '\n//\n// BEGIN '+js+'\n//\n'
1101              imported_modules_str += file(path).read()
1102              imported_modules_str += '\n//\n// END '+js+'\n//\n'
1103           else:
1104              print >>sys.stderr, 'Warning: Unable to find imported javascript:', js
1105           
1106        return imported_modules_str + module_str
1107
1108    def translateLibraries(self, library_modules=[]):
1109        self.library_modules = library_modules
1110
1111        imported_modules_str = ""
1112        for library in self.library_modules:
1113            imported_modules_str += self.translate(library, False)
1114        
1115        return imported_modules_str
1116
1117
1118if __name__ == "__main__":
1119    import sys
1120    file_name = sys.argv[1]
1121    if len(sys.argv) > 2:
1122        module_name = sys.argv[2]
1123    else:
1124        module_name = None
1125    print translate(file_name, module_name),