Permalink
Browse files

new support for custom operators with preprocessing hack.

hijacks @custom_operator("operator-name") decorator, and the |"operator"| trick.
  • Loading branch information...
hartsantler committed Oct 17, 2013
1 parent e80ce2e commit 796002312a19df63bb74a9456682067392951a24
Showing with 111 additions and 7 deletions.
  1. +47 −7 pythonscript/python_to_pythonjs.py
  2. +12 −0 pythonscript/pythonjs.py
  3. +23 −0 tests/test_bitwise_or.html
  4. +29 −0 tests/test_custom_operators.html
@@ -176,8 +176,31 @@ def __init__(self, module=None, module_path=None):
self._module_path = module_path
self._with_js = False
self._typedefs = dict() ## class name : typedef (not pickled)
self._custom_operators = {}
self.setup_builtins()
def preprocess_custom_operators(self, data):
'''
custom operators must be defined before they are used
'''
code = []
for line in data.splitlines():
if line.strip().startswith('@custom_operator'):
l = line.replace('"', "'")
a,b,c = l.split("'")
self._custom_operators[ b ] = None
else:
for op in self._custom_operators:
line = line.replace(op, '|u"%s"|'%op)
code.append( line )
data = '\n'.join( code )
print( data )
return data
def setup_builtins(self):
self._classes['dict'] = set(['__getitem__', '__setitem__'])
self._classes['list'] = set(['__getitem__', '__setitem__'])
@@ -433,18 +456,25 @@ def visit_BinOp(self, node):
left = self.visit(node.left)
op = self.visit(node.op)
right = self.visit(node.right)
#if isinstance(node.left, Name) and node.left.id in self._instances:
# klass = self._instances[ node.left.id ]
# if op == '*' and '__mul__' in self._classes[klass]:
# node.operator_overloading = '__%s___mul__' %klass
# assert node.operator_overloading
# return '''JS('__%s___mul__( [%s, %s] )')''' %(klass, left, right)
if op == '|':
if isinstance(node.right, Str):
self._custom_op_hack = (node.right.s, left)
return ''
elif hasattr(self, '_custom_op_hack') and isinstance(node.left, BinOp):
op,left_operand = self._custom_op_hack
right_operand = self.visit(node.right)
#return '%s( %s, %s )' %(op, left_operand, right_operand)
if op in self._custom_operators: ## swap name to python function
op = self._custom_operators[ op ]
return '%s( [%s, %s] )' %(op, left_operand, right_operand)
if isinstance(node.left, Name):
typedef = self.get_typedef( node.left )
if typedef and op in typedef.operators:
func = typedef.operators[ op ]
node.operator_overloading = func
return '''JS('%s( [%s, %s] )')''' %(func, left, right)
return '''JS('%s( [%s, %s] )')''' %(func, left, right) ## TODO double check this without wrapping in JS()
return '%s %s %s' % (left, op, right)
@@ -812,6 +842,13 @@ def visit_FunctionDef(self, node):
else:
raise RuntimeError
elif isinstance(decorator, Call) and decorator.func.id == 'custom_operator':
assert len(decorator.args) == 1
assert isinstance( decorator.args[0], Str )
op = decorator.args[0].s
assert op in self._custom_operators
self._custom_operators[ op ] = node.name
else:
decorators.append( decorator )
@@ -985,7 +1022,10 @@ def command():
module_path = header
compiler = PythonToPythonJS( module=module, module_path=module_path )
data = compiler.preprocess_custom_operators( data )
compiler.visit( parse(data) )
compiler.save_module()
output = writer.getvalue()
print( output ) ## pipe to stdout
View
@@ -211,6 +211,18 @@ def visit_GtE(self, node):
def visit_LtE(self, node):
return '<='
def visit_LShift(self, node):
return '<<'
def visit_RShift(self, node):
return '>>'
def visit_BitXor(self, node):
return '^'
def visit_BitOr(self, node):
return '|'
def visit_BitAnd(self, node):
return '&'
def visit_Assign(self, node):
# XXX: I'm not sure why it is a list since, mutiple targets are inside a tuple
target = node.targets[0]
View
@@ -0,0 +1,23 @@
<html>
<head>
<script src="pythonscript.js"></script>
<script type="text/python">
def test():
print 1 | 0 ## should be 1
print 1 | 100 ## 101
print 1 | 99 ## 99
print 1 | 98 ## 99
print 1 | 97 ## 97
print 1|2|3 ## 3
</script>
</head>
<body>
<button onclick="test()">click me</button>
</body>
</html>
@@ -0,0 +1,29 @@
<html>
<head>
<script src="pythonscript.js"></script>
<script type="text/python">
@custom_operator( 'XXX' )
def operatorX(x,y):
return x | y
@custom_operator( 'YYY' )
def operatorY(x,y):
return x | y
def test():
a = 1 |'operatorX'| 2 |'operatorY'| 3
print a
b = 1 XXX 2 YYY 3
print b
</script>
</head>
<body>
<button onclick="test()">click me</button>
</body>
</html>

0 comments on commit 7960023

Please sign in to comment.