# Copyright 2006 James Tauber and contributors
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from types import StringType
# the standard location for builtins (e.g. pyjslib) can be
# over-ridden by changing this. it defaults to sys.prefix
# so that on a system-wide install of pyjamas the builtins
# can be found in e.g. {sys.prefix}/share/pyjamas
# over-rides can be done by either explicitly modifying
# pyjs.prefix or by setting an environment variable, PYJSPREFIX.
if os.environ.has_key('PYJSPREFIX'):
prefix = os.environ['PYJSPREFIX']
# pyjs.path is the list of paths, just like sys.path, from which
# library modules will be searched for, for compile purposes.
# obviously we don't want to use sys.path because that would result
# in compiling standard python modules into javascript!
path = [os.path.abspath('')]
if os.environ.has_key('PYJSPATH'):
for p in os.environ['PYJSPATH'].split(os.pathsep):
# this is the python function used to wrap native javascript
NATIVE_JS_FUNC_NAME = "JS"
PYJSLIB_BUILTIN_FUNCTIONS=("cmp",
PYJSLIB_BUILTIN_CLASSES=("BaseException",
def pyjs_builtin_remap(name):
# XXX: this is a hack: these should be dealt with another way
# however, console is currently the only global name which is causing
PYJS_GLOBAL_VARS=("console")
# This is taken from the django project.
# Escape every ASCII character with a value less than 32.
) + tuple([('%c' % z, '\\x%02X' % z) for z in range(32)])
"""Hex encodes characters for use in JavaScript strings."""
for bad, good in JS_ESCAPES:
value = value.replace(bad, good)
def uuprefix(name, leave_alone=0):
name = name[:leave_alone] + map(lambda x: "__%s" % x, name[leave_alone:])
def __init__(self, name, name_):
self.klasses[name] = self
def set_base(self, base_name):
self.base = self.klasses.get(base_name)
def add_function(self, function_name):
self.functions.add(function_name)
class TranslationError(Exception):
def __init__(self, message, node):
self.message = "line %s:\n%s\n%s" % (node.lineno, message, node)
def mod_var_name_decl(raw_module_name):
""" function to get the last component of the module e.g.
pyjamas.ui.DOM into the "namespace". i.e. doing
"import pyjamas.ui.DOM" actually ends up with _two_
variables - one pyjamas.ui.DOM, the other just "DOM".
but "DOM" is actually local, hence the "var" prefix.
for PyV8, this might end up causing problems - we'll have
to see: gen_mod_import and mod_var_name_decl might have
to end up in a library-specific module, somewhere.
name = raw_module_name.split(".")
return "var %s = %s;\n" % (child_name, raw_module_name)
def gen_mod_import(parentName, importName, dynamic=1):
#pyjs_ajax_eval("%(n)s.cache.js", null, true);
pyjslib.import_module(sys.loadpath, '%(p)s', '%(n)s', %(d)d, false);
""" % ({'p': parentName, 'd': dynamic, 'n': importName}) + \
mod_var_name_decl(importName)
def __init__(self, mn, module_name, raw_module_name, src, debug, mod, output,
dynamic=0, optimize=False,
self.module_prefix = module_name + "."
self.raw_module_name = raw_module_name
src = src.replace("\r\n", "\n")
src = src.replace("\n\r", "\n")
src = src.replace("\r", "\n")
self.src = src.split("\n")
self.imported_modules = []
self.imported_modules_as = []
self.top_level_functions = set()
self.top_level_classes = set()
self.top_level_vars = set()
self.local_arg_stack = [[]]
self.imported_classes = {}
self.method_imported_globals = set()
self.nextTupleAssignID = 1
if module_name.find(".") >= 0:
print >>self.output, UU+"%s%s = function (__mod_name__) {" % (vdec, module_name)
print >>self.output, " if("+module_name+".__was_initialized__) return;"
print >>self.output, " "+UU+module_name+".__was_initialized__ = true;"
print >>self.output, UU+"if (__mod_name__ == null) __mod_name__ = '%s';" % (mn)
print >>self.output, UU+"%s.__name__ = __mod_name__;" % (raw_module_name)
decl = mod_var_name_decl(raw_module_name)
print >>self.output, decl
haltException = self.module_prefix + "HaltException"
print >>self.output, haltException + ' = function () {'
print >>self.output, ' this.message = "Program Halted";'
print >>self.output, ' this.name = "' + haltException + '";'
print >>self.output, haltException + ".prototype.__str__ = function()"
print >>self.output, 'return this.message ;'
print >>self.output, haltException + ".prototype.toString = function()"
print >>self.output, 'return this.name + ": \\"" + this.message + "\\"";'
isHaltFunction = self.module_prefix + "IsHaltException"
var suffix="HaltException";
if (s.length < suffix.length) {
//alert(s + " " + suffix);
var ss = s.substring(s.length, (s.length - suffix.length));
//alert(s + " " + suffix + " " + ss);
if isinstance(child, ast.Function):
self.top_level_functions.add(child.name)
elif isinstance(child, ast.Class):
self.top_level_classes.add(child.name)
if isinstance(child, ast.Function):
self._function(child, False)
elif isinstance(child, ast.Class):
elif isinstance(child, ast.Import):
importName = child.names[0][0]
if importName == '__pyjamas__': # special module to help make pyjamas modules loadable in the python interpreter
elif importName.endswith('.js'):
self.imported_js.add(importName)
self.add_imported_module(strip_py(importName))
elif isinstance(child, ast.From):
if child.modname == '__pyjamas__': # special module to help make pyjamas modules loadable in the python interpreter
self.add_imported_module(child.modname)
elif isinstance(child, ast.Discard):
self._discard(child, None)
elif isinstance(child, ast.Assign):
self._assign(child, None, True)
elif isinstance(child, ast.AugAssign):
self._augassign(child, None)
elif isinstance(child, ast.If):
elif isinstance(child, ast.For):
elif isinstance(child, ast.While):
elif isinstance(child, ast.Subscript):
self._subscript_stmt(child, None)
elif isinstance(child, ast.Global):
self._global(child, None)
elif isinstance(child, ast.Printnl):
elif isinstance(child, ast.Print):
elif isinstance(child, ast.TryExcept):
self._tryExcept(child, None)
elif isinstance(child, ast.Raise):
elif isinstance(child, ast.Stmt):
raise TranslationError("unsupported type (in __init__)", child)
# Initialize all classes for this module
#print >> self.output, "__"+self.modpfx()+\
# "classes_initialize = function() {\n"
#for className in self.top_level_classes:
# print >> self.output, "\t"+UU+self.modpfx()+"__"+className+"_initialize();"
#print >> self.output, "};\n"
print >> self.output, "return this;\n"
print >> self.output, "}; /* end %s */ \n" % module_name
def module_imports(self):
return self.imported_modules + self.imported_modules_as
def add_local_arg(self, varname):
local_vars = self.local_arg_stack[-1]
if varname not in local_vars:
local_vars.append(varname)
def add_imported_module(self, importName):
if importName in self.imported_modules:
self.imported_modules.append(importName)
name = importName.split(".")
# add the name of the module to the namespace,
# but don't add the short name to imported_modules
# because then the short name would be attempted to be
# added to the dependencies, and it's half way up the
# module import directory structure!
self.imported_modules_as.append(child_name)
print >> self.output, gen_mod_import(self.raw_module_name,
def _default_args_handler(self, node, arg_names, current_klass,
output = output or self.output
default_pos = len(arg_names) - len(node.defaults)
if arg_names and arg_names[0] == self.method_self:
for default_node in node.defaults:
if isinstance(default_node, ast.Const):
default_value = self._const(default_node)
elif isinstance(default_node, ast.Name):
default_value = self._name(default_node, current_klass)
elif isinstance(default_node, ast.UnarySub):
default_value = self._unarysub(default_node, current_klass)
raise TranslationError("unsupported type (in _method)", default_node)
default_name = arg_names[default_pos]
print >> output, " if (typeof %s == 'undefined') %s=%s;" % (default_name, default_name, default_value)
def _varargs_handler(self, node, varargname, arg_names, current_klass):
print >>self.output, " var", varargname, '= new pyjslib.Tuple();'
print >>self.output, " for(var __va_arg="+str(len(arg_names))+"; __va_arg < arguments.length; __va_arg++) {"
print >>self.output, " var __arg = arguments[__va_arg];"
print >>self.output, " "+varargname+".append(__arg);"
print >>self.output, " }"
def _kwargs_parser(self, node, function_name, arg_names, current_klass):
if len(node.defaults) or node.kwargs:
default_pos = len(arg_names) - len(node.defaults)
if arg_names and arg_names[0] == self.method_self:
print >>self.output, function_name+'.parse_kwargs = function (', ", ".join(["__kwargs"]+arg_names), ") {"
for default_node in node.defaults:
default_value = self.expr(default_node, current_klass)
# if isinstance(default_node, ast.Const):
# default_value = self._const(default_node)
# elif isinstance(default_node, ast.Name):
# default_value = self._name(default_node)
# elif isinstance(default_node, ast.UnarySub):
# default_value = self._unarysub(default_node, current_klass)
# raise TranslationError("unsupported type (in _method)", default_node)
default_name = arg_names[default_pos]
print >>self.output, " if (typeof %s == 'undefined')"%(default_name)
print >>self.output, " %s=__kwargs.%s;"% (default_name, default_name)
#self._default_args_handler(node, arg_names, current_klass)
if node.kwargs: arg_names += ["pyjslib.Dict(__kwargs)"]
print >>self.output, " var __r = "+"".join(["[", ", ".join(arg_names), "]"])+";"
self._varargs_handler(node, "__args", arg_names, current_klass)
print >>self.output, " __r.push.apply(__r, __args.getArray())"
print >>self.output, " return __r;"
print >>self.output, "};"
def _function(self, node, local=False):
function_name = node.name
self.add_local_arg(function_name)
function_name = UU + self.modpfx() + node.name
arg_names = list(node.argnames)
normal_arg_names = list(arg_names)
if node.kwargs: kwargname = normal_arg_names.pop()
if node.varargs: varargname = normal_arg_names.pop()
declared_arg_names = list(normal_arg_names)
if node.kwargs: declared_arg_names.append(kwargname)
function_args = "(" + ", ".join(declared_arg_names) + ")"
print >>self.output, "%s = function%s {" % (function_name, function_args)
self._default_args_handler(node, normal_arg_names, None)
local_arg_names = normal_arg_names + declared_arg_names
self._varargs_handler(node, varargname, declared_arg_names, None)
local_arg_names.append(varargname)
# stack of local variable names for this function call
self.local_arg_stack.append(local_arg_names)
# remove the top local arg names
self.local_arg_stack.pop()
# we need to return null always, so it is not undefined
lastStmt = [p for p in node.code][-1]
if not isinstance(lastStmt, ast.Return):
if not self._isNativeFunc(lastStmt):
print >>self.output, " return null;"
print >>self.output, "};"
print >>self.output, "%s.__name__ = '%s';\n" % (function_name, node.name)
self._kwargs_parser(node, function_name, normal_arg_names, None)
def _return(self, node, current_klass):
expr = self.expr(node.value, current_klass)
# in python a function call always returns None, so we do it
print >>self.output, " return " + expr + ";"
def _break(self, node, current_klass):
print >>self.output, " break;"
def _continue(self, node, current_klass):
print >>self.output, " continue;"
def _callfunc(self, v, current_klass):
if isinstance(v.node, ast.Name):
if v.node.name in self.top_level_functions:
call_name = self.modpfx() + v.node.name
elif v.node.name in self.top_level_classes:
call_name = self.modpfx() + v.node.name
elif self.imported_classes.has_key(v.node.name):
call_name = self.imported_classes[v.node.name] + '.' + v.node.name
elif v.node.name in PYJSLIB_BUILTIN_FUNCTIONS:
call_name = 'pyjslib.' + v.node.name
elif v.node.name in PYJSLIB_BUILTIN_CLASSES:
name = pyjs_builtin_remap(v.node.name)
call_name = 'pyjslib.' + name
elif v.node.name == "callable":
call_name = "pyjslib.isFunction"
elif isinstance(v.node, ast.Getattr):
attr_name = v.node.attrname
if isinstance(v.node.expr, ast.Name):
call_name = self._name2(v.node.expr, current_klass, attr_name)
elif isinstance(v.node.expr, ast.Getattr):
call_name = self._getattr2(v.node.expr, current_klass, attr_name)
elif isinstance(v.node.expr, ast.CallFunc):
call_name = self._callfunc(v.node.expr, current_klass) + "." + v.node.attrname
elif isinstance(v.node.expr, ast.Subscript):
call_name = self._subscript(v.node.expr, current_klass) + "." + v.node.attrname
elif isinstance(v.node.expr, ast.Const):
call_name = self.expr(v.node.expr, current_klass) + "." + v.node.attrname
raise TranslationError("unsupported type (in _callfunc)", v.node.expr)
raise TranslationError("unsupported type (in _callfunc)", v.node)
call_name = strip_py(call_name)
star_arg_name = self.expr(v.star_args, current_klass)
if isinstance(ch4, ast.Keyword):
kwarg = ch4.name + ":" + self.expr(ch4.expr, current_klass)
arg = self.expr(ch4, current_klass)
fn_args = ", ".join(['{' + ', '.join(kwargs) + '}']+call_args)
fn_args = ", ".join(call_args)
if kwargs or star_arg_name:
try: call_this, method_name = call_name.rsplit(".", 1)
# Must be a function call ...
return ("pyjs_kwargs_function_call("+call_name+", "
return ("pyjs_kwargs_method_call("+call_this+", '"+method_name+"', "
return call_name + "(" + ", ".join(call_args) + ")"
def _print(self, node, current_klass):
arg = self.expr(ch4, current_klass)
print >>self.output, "pyjslib.printFunc([", ', '.join(call_args), "],", int(isinstance(node, ast.Printnl)), ");"
def _tryExcept(self, node, current_klass):
if len(node.handlers) != 1:
raise TranslationError("except statements in this form are" +
expr = node.handlers[0][0]
as_ = node.handlers[0][1]
# XXX TODO: check that this should instead be added as a _separate_
# local scope, temporary to the function. oh dearie me.
self.add_local_arg(errName)
print >>self.output, " try {"
for stmt in node.body.nodes:
self._stmt(stmt, current_klass)
print >> self.output, " } catch(%s) {" % errName
if isinstance(expr, ast.Tuple):
l.append("(%(err)s.__name__ == %(expr)s.__name__)" % dict (err=errName, expr=self.expr(x, current_klass)))
l = [ " (%(err)s.__name__ == %(expr)s.__name__) " % dict (err=errName, expr=self.expr(expr, current_klass)) ]
print >> self.output, " if(%s) {" % '||\n\t\t'.join(l)
for stmt in node.handlers[0][2]:
self._stmt(stmt, current_klass)
#print >> self.output, "} else { throw(%s); } " % errName
print >> self.output, "}"
print >>self.output, " } finally {"
self._stmt(stmt, current_klass)
print >>self.output, " }"
# XXX: change use_getattr to True to enable "strict" compilation
# but incurring a 100% performance penalty. oops.
def _getattr(self, v, current_klass, use_getattr=False):
if isinstance(v.expr, ast.Name):
obj = self._name(v.expr, current_klass, return_none_for_module=True)
if obj == None and v.expr.name in self.module_imports():
# XXX TODO: distinguish between module import classes
# and variables. right now, this is a hack to get
# the sys module working.
#if v.expr.name == 'sys':
return v.expr.name+'.'+attr_name
#return v.expr.name+'.__'+attr_name+'.prototype.__class__'
if not use_getattr or attr_name == '__class__' or \
return obj + "." + attr_name
return "pyjslib.getattr(%s, '%s')" % (obj, attr_name)
elif isinstance(v.expr, ast.Getattr):
return self._getattr(v.expr, current_klass) + "." + attr_name
elif isinstance(v.expr, ast.Subscript):
return self._subscript(v.expr, self.modpfx()) + "." + attr_name
elif isinstance(v.expr, ast.CallFunc):
return self._callfunc(v.expr, self.modpfx()) + "." + attr_name
raise TranslationError("unsupported type (in _getattr)", v.expr)
return strip_py(self.module_prefix)
def _name(self, v, current_klass, top_level=False,
return_none_for_module=False):
if v.name == 'ilikesillynamesfornicedebugcode':
print top_level, current_klass, repr(v)
print self.top_level_vars
print self.top_level_functions
print self.local_arg_stack
las = len(self.local_arg_stack)
local_var_names = self.local_arg_stack[-1]
elif v.name == '__name__' and current_klass is None:
return self.modpfx() + v.name
elif v.name == self.method_self:
elif v.name in self.top_level_functions:
return UU+self.modpfx() + v.name
elif v.name in self.method_imported_globals:
return UU+self.modpfx() + v.name
elif not current_klass and las == 1 and v.name in self.top_level_vars:
return UU+self.modpfx() + v.name
elif v.name in local_var_names:
elif self.imported_classes.has_key(v.name):
return UU+self.imported_classes[v.name] + '.__' + v.name + ".prototype.__class__"
elif v.name in self.top_level_classes:
return UU+self.modpfx() + "__" + v.name + ".prototype.__class__"
elif v.name in self.module_imports() and return_none_for_module:
elif v.name in PYJSLIB_BUILTIN_CLASSES:
return "pyjslib." + pyjs_builtin_remap( v.name )
if v.name not in local_var_names and \
v.name not in self.top_level_vars and \
v.name not in PYJS_GLOBAL_VARS and \
v.name not in self.top_level_functions:
if hasattr(cls_name, "name"):
cls_name_ = cls_name.name_
cls_name_ = current_klass + "_" # XXX ???
name = UU+cls_name_ + ".prototype.__class__." \
name = 'listener+' + name
def _name2(self, v, current_klass, attr_name):
if obj in self.method_imported_globals:
call_name = UU+self.modpfx() + obj + "." + attr_name
elif self.imported_classes.has_key(obj):
#if attr_name != "__init__":
attr_str = ".prototype.__class__." + attr_name
call_name = UU+self.imported_classes[obj] + '.__' + obj + attr_str
elif obj in self.module_imports():
call_name = obj + "." + attr_name
elif obj[0] == obj[0].upper(): # XXX HACK ALERT
call_name = UU + self.modpfx() + "__" + obj + ".prototype.__class__." + attr_name
call_name = UU+self._name(v, current_klass) + "." + attr_name
def _getattr2(self, v, current_klass, attr_name):
if isinstance(v.expr, ast.Getattr):
call_name = self._getattr2(v.expr, current_klass, v.attrname + "." + attr_name)
elif isinstance(v.expr, ast.Name) and v.expr.name in self.module_imports():
call_name = UU+v.expr.name + '.__' +v.attrname+".prototype.__class__."+attr_name
obj = self.expr(v.expr, current_klass)
call_name = obj + "." + v.attrname + "." + attr_name
Handle a class definition.
In order to translate python semantics reasonably well, the following
A special object is created for the class, which inherits attributes
from the superclass, or Object if there's no superclass. This is the
class object; the object which you refer to when specifying the
class by name. Static, class, and unbound methods are copied
from the superclass object.
A special constructor function is created with the same name as the
class, which is used to create instances of that class.
A javascript class (e.g. a function with a prototype attribute) is
created which is the javascript class of created instances, and
which inherits attributes from the class object. Bound methods are
copied from the superclass into this class rather than inherited,
because the class object contains unbound, class, and static methods
that we don't necessarily want to inherit.
The type of a method can now be determined by inspecting its
static_method, unbound_method, class_method, or instance_method
attribute; only one of these should be true.
Much of this work is done in pyjs_extend, is pyjslib.py
class_name = self.modpfx() + uuprefix(node.name, 1)
class_name_ = self.modpfx() + uuprefix(node.name)
current_klass = Klass(class_name, class_name_)
if isinstance(child, ast.Function):
current_klass.add_function(child.name)
if child.name == "__init__":
base_class = "pyjslib.__Object"
elif len(node.bases) == 1:
if isinstance(node.bases[0], ast.Name):
if self.imported_classes.has_key(node.bases[0].name):
base_class_ = self.imported_classes[node.bases[0].name] + '.__' + node.bases[0].name
base_class = self.imported_classes[node.bases[0].name] + '.' + node.bases[0].name
base_class_ = self.modpfx() + "__" + node.bases[0].name
base_class = self.modpfx() + node.bases[0].name
elif isinstance(node.bases[0], ast.Getattr):
# the bases are not in scope of the class so do not
# pass our class to self._name
base_class_ = self._name(node.bases[0].expr, None) + \
".__" + node.bases[0].attrname
base_class = self._name(node.bases[0].expr, None) + \
"." + node.bases[0].attrname
raise TranslationError("unsupported type (in _class)", node.bases[0])
current_klass.set_base(base_class)
raise TranslationError("more than one base (in _class)", node)
print >>self.output, UU+class_name_ + " = function () {"
# print >>self.output, " __" + base_class + ".call(this);"
init_method = ast.Function([], "__init__", ["self"], [], 0, None, [])
#self._method(init_method, current_klass, class_name)
# Generate a function which constructs the object
clsfunc = ast.Function([],
init_method.argnames[1:],
[ast.Discard(ast.CallFunc(ast.Name("JS"), [ast.Const(
# I attempted lazy initialization, but then you can't access static class members
# " if(!__"+base_class+".__was_initialized__)"+
# " __" + class_name + "_initialize();\n" +
" var instance = new " + UU + class_name_ + "();\n" +
" if(instance.__init__) instance.__init__.apply(instance, arguments);\n" +
self._function(clsfunc, False)
print >>self.output, UU+class_name_ + ".__initialize__ = function () {"
print >>self.output, " if("+UU+class_name_+".__was_initialized__) return;"
print >>self.output, " "+UU+class_name_+".__was_initialized__ = true;"
cls_obj = UU+class_name_ + '.prototype.__class__'
if class_name == "pyjslib.__Object":
print >>self.output, " "+cls_obj+" = {};"
if base_class and base_class not in ("object", "pyjslib.__Object"):
print >>self.output, " if(!"+UU+base_class_+".__was_initialized__)"
print >>self.output, " "+UU+base_class_+".__initialize__();"
print >>self.output, " pyjs_extend(" + UU+class_name_ + ", "+UU+base_class_+");"
print >>self.output, " pyjs_extend(" + UU+class_name_ + ", "+UU+"pyjslib.__Object);"
print >>self.output, " "+cls_obj+".__new__ = "+UU+class_name+";"
print >>self.output, " "+cls_obj+".__name__ = '"+UU+node.name+"';"
if isinstance(child, ast.Pass):
elif isinstance(child, ast.Function):
self._method(child, current_klass, class_name, class_name_)
elif isinstance(child, ast.Assign):
self.classattr(child, current_klass)
elif isinstance(child, ast.Discard) and isinstance(child.expr, ast.Const):
# Probably a docstring, turf it
raise TranslationError("unsupported type (in _class)", child)
print >> self.output, class_name_+".__initialize__();"
def classattr(self, node, current_klass):
self._assign(node, current_klass, True)
def _raise(self, node, current_klass):
raise TranslationError("More than one expression unsupported",
print >> self.output, "throw (%s);" % self.expr(
node.expr1, current_klass)
def _method(self, node, current_klass, class_name, class_name_):
self.method_imported_globals = set()
arg_names = list(node.argnames)
for d in node.decorators:
if d.name == "classmethod":
elif d.name == "staticmethod":
staticfunc = ast.Function([], class_name_+"."+node.name, node.argnames, node.defaults, node.flags, node.doc, node.code, node.lineno)
self._function(staticfunc, True)
print >>self.output, " " + UU+class_name_ + ".prototype.__class__." + node.name + " = " + class_name_+"."+node.name+";";
print >>self.output, " " + UU+class_name_ + ".prototype.__class__." + node.name + ".static_method = true;";
raise TranslationError("methods must take an argument 'self' (in _method)", node)
self.method_self = arg_names[0]
#if not classmethod and arg_names[0] != "self":
# raise TranslationError("first arg not 'self' (in _method)", node)
normal_arg_names = arg_names[1:]
if node.kwargs: kwargname = normal_arg_names.pop()
if node.varargs: varargname = normal_arg_names.pop()
declared_arg_names = list(normal_arg_names)
if node.kwargs: declared_arg_names.append(kwargname)
function_args = "(" + ", ".join(declared_arg_names) + ")"
fexpr = UU + class_name_ + ".prototype.__class__." + node.name
fexpr = UU + class_name_ + ".prototype." + node.name
print >>self.output, " "+fexpr + " = function" + function_args + " {"
self._default_args_handler(node, normal_arg_names, current_klass)
local_arg_names = normal_arg_names + declared_arg_names
self._varargs_handler(node, varargname, declared_arg_names, current_klass)
local_arg_names.append(varargname)
# stack of local variable names for this function call
self.local_arg_stack.append(local_arg_names)
self._stmt(child, current_klass)
# remove the top local arg names
self.local_arg_stack.pop()
print >>self.output, " };"
self._kwargs_parser(node, fexpr, normal_arg_names, current_klass)
# Have to create a version on the instances which automatically passes the
altexpr = UU + class_name_ + ".prototype." + node.name
print >>self.output, " "+altexpr + " = function() {"
print >>self.output, " return " + fexpr + ".apply(this.__class__, arguments);"
print >>self.output, " };"
print >>self.output, " "+fexpr+".class_method = true;"
print >>self.output, " "+altexpr+".instance_method = true;"
# For instance methods, we need an unbound version in the class object
altexpr = UU + class_name_ + ".prototype.__class__." + node.name
print >>self.output, " "+altexpr + " = function() {"
print >>self.output, " return " + fexpr + ".call.apply("+fexpr+", arguments);"
print >>self.output, " };"
print >>self.output, " "+altexpr+".unbound_method = true;"
print >>self.output, " "+fexpr+".instance_method = true;"
print >>self.output, " "+altexpr+".__name__ = '%s';" % node.name
print >>self.output, UU + class_name_ + ".prototype.%s.__name__ = '%s';" % \
if node.kwargs or len(node.defaults):
print >>self.output, " "+altexpr + ".parse_kwargs = " + fexpr + ".parse_kwargs;"
self.method_imported_globals = set()
def _isNativeFunc(self, node):
if isinstance(node, ast.Discard):
if isinstance(node.expr, ast.CallFunc):
if isinstance(node.expr.node, ast.Name) and \
node.expr.node.name == NATIVE_JS_FUNC_NAME:
def _stmt(self, node, current_klass):
debugStmt = self.debug and not self._isNativeFunc(node)
print >>self.output, ' try {'
if isinstance(node, ast.Return):
self._return(node, current_klass)
elif isinstance(node, ast.Break):
self._break(node, current_klass)
elif isinstance(node, ast.Continue):
self._continue(node, current_klass)
elif isinstance(node, ast.Assign):
self._assign(node, current_klass)
elif isinstance(node, ast.AugAssign):
self._augassign(node, current_klass)
elif isinstance(node, ast.Discard):
self._discard(node, current_klass)
elif isinstance(node, ast.If):
self._if(node, current_klass)
elif isinstance(node, ast.For):
self._for(node, current_klass)
elif isinstance(node, ast.While):
self._while(node, current_klass)
elif isinstance(node, ast.Subscript):
self._subscript_stmt(node, current_klass)
elif isinstance(node, ast.Global):
self._global(node, current_klass)
elif isinstance(node, ast.Pass):
elif isinstance(node, ast.Function):
self._function(node, True)
elif isinstance(node, ast.Printnl):
self._print(node, current_klass)
elif isinstance(node, ast.Print):
self._print(node, current_klass)
elif isinstance(node, ast.TryExcept):
self._tryExcept(node, current_klass)
elif isinstance(node, ast.Raise):
self._raise(node, current_klass)
raise TranslationError("unsupported type (in _stmt)", node)
lt = self.get_line_trace(node)
haltException = self.module_prefix + "HaltException"
isHaltFunction = self.module_prefix + "IsHaltException"
print >>self.output, ' } catch (__err) {'
print >>self.output, ' if (' + isHaltFunction + '(__err.name)) {'
print >>self.output, ' throw __err;'
print >>self.output, ' } else {'
print >>self.output, " st = sys.printstack() + "\
+ '"%s"' % lt + "+ '\\n' ;"
print >>self.output, ' alert("' + "Error in " \
+ '+"\\n"+__err.name+": "+__err.message'\
+ '+"\\n\\nStack trace:\\n"' \
print >>self.output, ' debugger;'
print >>self.output, ' throw new ' + self.module_prefix + "HaltException();"
print >>self.output, ' }'
print >>self.output, ' }'
def get_line_trace(self, node):
if hasattr(node, "lineno"):
srcLine = self.src[min(lineNum, len(self.src))-1]
srcLine = srcLine.replace('\\', '\\\\')
srcLine = srcLine.replace('"', '\\"')
srcLine = srcLine.replace("'", "\\'")
return self.raw_module_name + ".py, line " \
def _augassign(self, node, current_klass):
if isinstance(v, ast.Getattr):
# XXX HACK! don't allow += on return result of getattr.
# TODO: create a temporary variable or something.
lhs = self._getattr(v, current_klass, False)
lhs = self._name(node.node, current_klass)
rhs = self.expr(node.expr, current_klass)
print >>self.output, " " + lhs + " " + op + " " + rhs + ";"
def _assign(self, node, current_klass, top_level = False):
tempvar = '__temp'+str(node.lineno)
tnode = ast.Assign([ast.AssName(tempvar, "OP_ASSIGN", node.lineno)], node.expr, node.lineno)
self._assign(tnode, current_klass, top_level)
tnode2 = ast.Assign([v], ast.Name(tempvar, node.lineno), node.lineno)
self._assign(tnode2, current_klass, top_level)
if len(self.local_arg_stack) > 0:
local_var_names = self.local_arg_stack[-1]
def _lhsFromAttr(v, current_klass):
if isinstance(v.expr, ast.Name):
lhs = self._name(v.expr, current_klass) + "." + attr_name
elif isinstance(v.expr, ast.Getattr):
lhs = self._getattr(v, current_klass)
elif isinstance(v.expr, ast.Subscript):
lhs = self._subscript(v.expr, current_klass) + "." + attr_name
raise TranslationError("unsupported type (in _assign)", v.expr)
def _lhsFromName(v, top_level, current_klass):
lhs = UU+current_klass.name_ + ".prototype.__class__." \
self.top_level_vars.add(v.name)
vname = self.modpfx() + v.name
if not self.modpfx() and v.name not in\
self.method_imported_globals:
self.add_local_arg(v.name)
if v.name in local_var_names:
elif v.name in self.method_imported_globals:
lhs = self.modpfx() + v.name
self.add_local_arg(v.name)
if isinstance(v, ast.AssAttr):
lhs = _lhsFromAttr(v, current_klass)
if v.flags == "OP_ASSIGN":
raise TranslationError("unsupported flag (in _assign)", v)
elif isinstance(v, ast.AssName):
lhs = _lhsFromName(v, top_level, current_klass)
if v.flags == "OP_ASSIGN":
raise TranslationError("unsupported flag (in _assign)", v)
elif isinstance(v, ast.Subscript):
if v.flags == "OP_ASSIGN":
obj = self.expr(v.expr, current_klass)
raise TranslationError("must have one sub (in _assign)", v)
idx = self.expr(v.subs[0], current_klass)
value = self.expr(node.expr, current_klass)
print >>self.output, " " + obj + ".__setitem__(" + idx + ", " + value + ");"
raise TranslationError("unsupported flag (in _assign)", v)
elif isinstance(v, (ast.AssList, ast.AssTuple)):
uniqueID = self.nextTupleAssignID
self.nextTupleAssignID += 1
tempName = "__tupleassign" + str(uniqueID) + "__"
print >>self.output, " var " + tempName + " = " + \
self.expr(node.expr, current_klass) + ";"
for index,child in enumerate(v.getChildNodes()):
rhs = tempName + ".__getitem__(" + str(index) + ")"
if isinstance(child, ast.AssAttr):
lhs = _lhsFromAttr(child, current_klass)
elif isinstance(child, ast.AssName):
lhs = _lhsFromName(child, top_level, current_klass)
elif isinstance(child, ast.Subscript):
if child.flags == "OP_ASSIGN":
obj = self.expr(child.expr, current_klass)
raise TranslationError("must have one sub " +
idx = self.expr(child.subs[0], current_klass)
value = self.expr(node.expr, current_klass)
print >>self.output, " " + obj + ".__setitem__(" \
+ idx + ", " + rhs + ");"
print >>self.output, " " + lhs + " = " + rhs + ";"
raise TranslationError("unsupported type (in _assign)", v)
rhs = self.expr(node.expr, current_klass)
print "b", repr(node.expr), rhs
print >>self.output, " " + lhs + " " + op + " " + rhs + ";"
def _discard(self, node, current_klass):
if isinstance(node.expr, ast.CallFunc):
debugStmt = self.debug and not self._isNativeFunc(node)
if debugStmt and isinstance(node.expr.node, ast.Name) and \
node.expr.node.name == 'import_wait':
st = self.get_line_trace(node)
print >>self.output, "sys.addstack('%s');\n" % st
if isinstance(node.expr.node, ast.Name) and node.expr.node.name == NATIVE_JS_FUNC_NAME:
if len(node.expr.args) != 1:
raise TranslationError("native javascript function %s must have one arg" % NATIVE_JS_FUNC_NAME, node.expr)
if not isinstance(node.expr.args[0], ast.Const):
raise TranslationError("native javascript function %s must have constant arg" % NATIVE_JS_FUNC_NAME, node.expr)
raw_js = node.expr.args[0].value
print >>self.output, raw_js
expr = self._callfunc(node.expr, current_klass)
print >>self.output, " " + expr + ";"
print >>self.output, "sys.popstack();\n"
elif isinstance(node.expr, ast.Const):
if node.expr.value is not None: # Empty statements generate ignore None
print >>self.output, self._const(node.expr)
raise TranslationError("unsupported type (in _discard)", node.expr)
def _if(self, node, current_klass):
for i in range(len(node.tests)):
test, consequence = node.tests[i]
self._if_test(keyword, test, consequence, current_klass)
self._if_test(keyword, test, consequence, current_klass)
def _if_test(self, keyword, test, consequence, current_klass):
expr = self.expr(test, current_klass)
print >>self.output, " " + keyword + " (pyjslib.bool(" + expr + ")) {"
print >>self.output, " " + keyword + " {"
if isinstance(consequence, ast.Stmt):
for child in consequence.nodes:
self._stmt(child, current_klass)
raise TranslationError("unsupported type (in _if_test)", consequence)
print >>self.output, " }"
# look up "hack" in AppTranslator as to how findFile gets here
module_name = node.modname + "." + name[0]
ff = self.findFile(module_name + ".py")
self.add_imported_module(module_name)
self.imported_classes[name[0]] = node.modname
def _compare(self, node, current_klass):
lhs = self.expr(node.expr, current_klass)
raise TranslationError("only one ops supported (in _compare)", node)
rhs_node = node.ops[0][1]
rhs = self.expr(rhs_node, current_klass)
return "pyjslib.eq(%s, %s)" % (lhs, rhs)
return rhs + ".__contains__(" + lhs + ")"
return "!" + rhs + ".__contains__(" + lhs + ")"
return "(" + lhs + " " + op + " " + rhs + ")"
def _not(self, node, current_klass):
expr = self.expr(node.expr, current_klass)
def _or(self, node, current_klass):
expr = "("+(") || (".join([self.expr(child, current_klass) for child in node.nodes]))+')'
def _and(self, node, current_klass):
expr = "("+(") && (".join([self.expr(child, current_klass) for child in node.nodes]))+")"
def _for(self, node, current_klass):
# based on Bob Ippolito's Iteration in Javascript code
if isinstance(node.assign, ast.AssName):
assign_name = node.assign.name
self.add_local_arg(assign_name)
if node.assign.flags == "OP_ASSIGN":
elif isinstance(node.assign, ast.AssTuple):
for child in node.assign:
assign_name = "temp_" + child_name
self.add_local_arg(child_name)
var %(child_name)s %(op)s %(assign_name)s.__getitem__(%(i)i);
raise TranslationError("unsupported type (in _for)", node.assign)
if isinstance(node.list, ast.Name):
list_expr = self._name(node.list, current_klass)
elif isinstance(node.list, ast.Getattr):
list_expr = self._getattr(node.list, current_klass)
elif isinstance(node.list, ast.CallFunc):
list_expr = self._callfunc(node.list, current_klass)
raise TranslationError("unsupported type (in _for)", node.list)
lhs = "var " + assign_name
iterator_name = "__" + assign_name
var %(iterator_name)s = %(list_expr)s.__iter__();
%(lhs)s %(op)s %(iterator_name)s.next();
for node in node.body.nodes:
self._stmt(node, current_klass)
if (e.__name__ != pyjslib.StopIteration.__name__) {
def _while(self, node, current_klass):
test = self.expr(node.test, current_klass)
print >>self.output, " while (pyjslib.bool(" + test + ")) {"
if isinstance(node.body, ast.Stmt):
for child in node.body.nodes:
self._stmt(child, current_klass)
raise TranslationError("unsupported type (in _while)", node.body)
print >>self.output, " }"
if isinstance(node.value, int):
elif isinstance(node.value, float):
elif isinstance(node.value, basestring):
if isinstance(node.value, unicode):
return "String('%s')" % escapejs(v)
raise TranslationError("unsupported type (in _const)", node)
def _unaryadd(self, node, current_klass):
return self.expr(node.expr, current_klass)
def _unarysub(self, node, current_klass):
return "-" + self.expr(node.expr, current_klass)
def _add(self, node, current_klass):
return self.expr(node.left, current_klass) + " + " + self.expr(node.right, current_klass)
def _sub(self, node, current_klass):
return self.expr(node.left, current_klass) + " - " + self.expr(node.right, current_klass)
def _div(self, node, current_klass):
return self.expr(node.left, current_klass) + " / " + self.expr(node.right, current_klass)
def _mul(self, node, current_klass):
return self.expr(node.left, current_klass) + " * " + self.expr(node.right, current_klass)
def _mod(self, node, current_klass):
if isinstance(node.left, ast.Const) and isinstance(node.left.value, StringType):
self.imported_js.add("sprintf.js") # Include the sprintf functionality if it is used
return "sprintf("+self.expr(node.left, current_klass) + ", " + self.expr(node.right, current_klass)+")"
return self.expr(node.left, current_klass) + " % " + self.expr(node.right, current_klass)
def _invert(self, node, current_klass):
return "~" + self.expr(node.expr, current_klass)
def _bitand(self, node, current_klass):
return " & ".join([self.expr(child, current_klass) for child in node.nodes])
def _bitshiftleft(self, node, current_klass):
return self.expr(node.left, current_klass) + " << " + self.expr(node.right, current_klass)
def _bitshiftright(self, node, current_klass):
return self.expr(node.left, current_klass) + " >>> " + self.expr(node.right, current_klass)
def _bitxor(self,node, current_klass):
return " ^ ".join([self.expr(child, current_klass) for child in node.nodes])
def _bitor(self, node, current_klass):
return " | ".join([self.expr(child, current_klass) for child in node.nodes])
def _subscript(self, node, current_klass):
if node.flags == "OP_APPLY":
return self.expr(node.expr, current_klass) + ".__getitem__(" + self.expr(node.subs[0], current_klass) + ")"
raise TranslationError("must have one sub (in _subscript)", node)
raise TranslationError("unsupported flag (in _subscript)", node)
def _subscript_stmt(self, node, current_klass):
if node.flags == "OP_DELETE":
print >>self.output, " " + self.expr(node.expr, current_klass) + ".__delitem__(" + self.expr(node.subs[0], current_klass) + ");"
raise TranslationError("unsupported flag (in _subscript)", node)
def _list(self, node, current_klass):
return "new pyjslib.List([" + ", ".join([self.expr(x, current_klass) for x in node.nodes]) + "])"
def _dict(self, node, current_klass):
key = self.expr(x[0], current_klass)
value = self.expr(x[1], current_klass)
items.append("[" + key + ", " + value + "]")
return "new pyjslib.Dict([" + ", ".join(items) + "])"
def _tuple(self, node, current_klass):
return "new pyjslib.Tuple([" + ", ".join([self.expr(x, current_klass) for x in node.nodes]) + "])"
def _lambda(self, node, current_klass):
raise TranslationError("varargs are not supported in Lambdas", node)
raise TranslationError("kwargs are not supported in Lambdas", node)
res = cStringIO.StringIO()
arg_names = list(node.argnames)
function_args = ", ".join(arg_names)
for child in node.getChildNodes():
expr = self.expr(child, None)
print >> res, "function (%s){" % function_args
self._default_args_handler(node, arg_names, None,
print >> res, 'return %s;}' % expr
def _slice(self, node, current_klass):
if node.flags == "OP_APPLY":
lower = self.expr(node.lower, current_klass)
upper = self.expr(node.upper, current_klass)
return "pyjslib.slice(" + self.expr(node.expr, current_klass) + ", " + lower + ", " + upper + ")"
raise TranslationError("unsupported flag (in _slice)", node)
def _global(self, node, current_klass):
self.method_imported_globals.add(name)
def expr(self, node, current_klass):
if isinstance(node, ast.Const):
# @@@ not sure if the parentheses should be here or in individual operator functions - JKT
elif isinstance(node, ast.Mul):
return " ( " + self._mul(node, current_klass) + " ) "
elif isinstance(node, ast.Add):
return " ( " + self._add(node, current_klass) + " ) "
elif isinstance(node, ast.Sub):
return " ( " + self._sub(node, current_klass) + " ) "
elif isinstance(node, ast.Div):
return " ( " + self._div(node, current_klass) + " ) "
elif isinstance(node, ast.Mod):
return self._mod(node, current_klass)
elif isinstance(node, ast.UnaryAdd):
return self._unaryadd(node, current_klass)
elif isinstance(node, ast.UnarySub):
return self._unarysub(node, current_klass)
elif isinstance(node, ast.Not):
return self._not(node, current_klass)
elif isinstance(node, ast.Or):
return self._or(node, current_klass)
elif isinstance(node, ast.And):
return self._and(node, current_klass)
elif isinstance(node, ast.Invert):
return self._invert(node, current_klass)
elif isinstance(node, ast.Bitand):
return "("+self._bitand(node, current_klass)+")"
elif isinstance(node,ast.LeftShift):
return self._bitshiftleft(node, current_klass)
elif isinstance(node, ast.RightShift):
return self._bitshiftright(node, current_klass)
elif isinstance(node, ast.Bitxor):
return "("+self._bitxor(node, current_klass)+")"
elif isinstance(node, ast.Bitor):
return "("+self._bitor(node, current_klass)+")"
elif isinstance(node, ast.Compare):
return self._compare(node, current_klass)
elif isinstance(node, ast.CallFunc):
return self._callfunc(node, current_klass)
elif isinstance(node, ast.Name):
return self._name(node, current_klass)
elif isinstance(node, ast.Subscript):
return self._subscript(node, current_klass)
elif isinstance(node, ast.Getattr):
return self._getattr(node, current_klass)
elif isinstance(node, ast.List):
return self._list(node, current_klass)
elif isinstance(node, ast.Dict):
return self._dict(node, current_klass)
elif isinstance(node, ast.Tuple):
return self._tuple(node, current_klass)
elif isinstance(node, ast.Slice):
return self._slice(node, current_klass)
elif isinstance(node, ast.Lambda):
return self._lambda(node, current_klass)
raise TranslationError("unsupported type (in expr)", node)
def translate(file_name, module_name, debug=False):
output = cStringIO.StringIO()
mod = compiler.parseFile(file_name)
t = Translator(module_name, module_name, module_name, src, debug, mod, output)
def __init__(self, platform_dir = "", verbose=True):
self.platform_dir = platform_dir
def setPlatform(self, platform):
def parseModule(self, module_name, file_name):
if not self.parse_cache.has_key(file_name):
mod = compiler.parseFile(file_name)
self.parse_cache[file_name] = mod
mod = self.parse_cache[file_name]
platform_file_name = self.generatePlatformFilename(file_name)
if self.platform and os.path.isfile(platform_file_name):
mod_override = compiler.parseFile(platform_file_name)
self.merge(mod, mod_override)
print "Importing %s (Platform %s)" % (module_name, self.platform)
print "Importing %s" % (module_name)
def generatePlatformFilename(self, file_name):
(module_name, extension) = os.path.splitext(os.path.basename(file_name))
platform_file_name = module_name + self.platform + extension
return os.path.join(os.path.dirname(file_name), self.platform_dir, platform_file_name)
def merge(self, tree1, tree2):
if isinstance(child, ast.Function):
self.replaceFunction(tree1, child.name, child)
elif isinstance(child, ast.Class):
self.replaceClassMethods(tree1, child.name, child)
def replaceFunction(self, tree, function_name, function_node):
# find function to replace
if isinstance(child, ast.Function) and child.name == function_name:
self.copyFunction(child, function_node)
raise TranslationError("function not found: " + function_name, function_node)
def replaceClassMethods(self, tree, class_name, class_node):
if isinstance(child, ast.Class) and child.name == class_name:
raise TranslationError("class not found: " + class_name, class_node)
for function_node in class_node.code:
if isinstance(function_node, ast.Function):
for child in old_class_node.code:
if isinstance(child, ast.Function) and child.name == function_node.name:
self.copyFunction(child, function_node)
raise TranslationError("class method not found: " + class_name + "." + function_node.name, function_node)
def copyFunction(self, target, source):
target.code = source.code
target.argnames = source.argnames
target.defaults = source.defaults
target.doc = source.doc # @@@ not sure we need to do this any more
path, ext = os.path.splitext(fname)
return path.replace(".", "/") + ext
def __init__(self, library_dirs=[], parser=None, dynamic=False,
optimize=False, verbose=True):
self.library_modules = []
self.library_dirs = path + library_dirs
self.parser = PlatformParser()
self.parser.dynamic = dynamic
def findFile(self, file_name):
if os.path.isfile(file_name):
for library_dir in self.library_dirs:
file_name = dotreplace(file_name)
full_file_name = os.path.join(
os.path.abspath(os.path.dirname(__file__)), library_dir, file_name)
if os.path.isfile(full_file_name):
fnameinit, ext = os.path.splitext(file_name)
fnameinit = fnameinit + "/__init__.py"
full_file_name = os.path.join(
os.path.abspath(os.path.dirname(__file__)), library_dir, fnameinit)
if os.path.isfile(full_file_name):
raise Exception("file not found: " + file_name)
def _translate(self, module_name, is_app=True, debug=False,
if module_name not in self.library_modules:
self.library_modules.append(module_name)
file_name = self.findFile(module_name + self.extension)
output = cStringIO.StringIO()
mod, override = self.parser.parseModule(module_name, file_name)
override_name = "%s.%s" % (self.parser.platform.lower(),
self.overrides[override_name] = override_name
t = Translator(mn, module_name, module_name,
src, debug, mod, output, self.dynamic, self.optimize,
module_str = output.getvalue()
imported_js.update(set(t.imported_js))
imported_modules_str = ""
for module in t.imported_modules:
if module not in self.library_modules:
self.library_modules.append(module)
#imported_js.update(set(t.imported_js))
#imported_modules_str += self._translate(
# module, False, debug=debug, imported_js=imported_js)
return imported_modules_str + module_str
def translate(self, module_name, is_app=True, debug=False,
app_code = cStringIO.StringIO()
lib_code = cStringIO.StringIO()
self.library_modules = []
for library in library_modules:
if library.endswith(".js"):
self.library_modules.append(library)
print 'Including LIB', library
print >> lib_code, '\n//\n// BEGIN LIB '+library+'\n//\n'
print >> lib_code, self._translate(
library, False, debug=debug, imported_js=imported_js)
print >> lib_code, "/* initialize static library */"
print >> lib_code, "%s%s();\n" % (UU, library)
print >> lib_code, '\n//\n// END LIB '+library+'\n//\n'
print >> app_code, self._translate(
module_name, is_app, debug=debug, imported_js=imported_js)
print >> lib_code, '\n//\n// BEGIN JS '+js+'\n//\n'
print >> lib_code, file(path).read()
print >> lib_code, '\n//\n// END JS '+js+'\n//\n'
print >>sys.stderr, 'Warning: Unable to find imported javascript:', js
return lib_code.getvalue(), app_code.getvalue()
usage: %s file_name [module_name]
print >> sys.stderr, usage % sys.argv[0]
file_name = os.path.abspath(sys.argv[1])
if not os.path.isfile(file_name):
print >> sys.stderr, "File not found %s" % file_name
module_name = sys.argv[2]
print translate(file_name, module_name),
if __name__ == "__main__":