Skip to content

Commit 610edf8

Browse files
author
hartsantler
committed
optional C style static typing for variables, and optimizations for integer math (multiply or divide by power numbers)
1 parent ab607ab commit 610edf8

7 files changed

Lines changed: 109 additions & 7 deletions

File tree

pythonjs/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"author": "Brett Hartshorn <goatman.py@gmail.com>",
33
"name": "python-js",
44
"description": "python multi-translator: javascript, dart, coffee, lua, vis.js",
5-
"version": "0.9.6",
5+
"version": "0.9.7",
66
"license": "BSD-3-Clause",
77
"repository": {
88
"type": "git",
@@ -43,6 +43,7 @@
4343
"code_writer.py",
4444
"inline_function.py",
4545
"ast_utils.py",
46+
"typedpython.py",
4647
"cli.js",
4748
"runtime"
4849
]

pythonjs/python-js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ empythoned.FS.createLazyFile(".", "ministdlib.py", "./ministdlib.py",
3030
true,false
3131
);
3232

33+
empythoned.FS.createLazyFile(".", "typedpython.py", "./typedpython.py",
34+
true,false
35+
);
36+
3337
empythoned.FS.createLazyFile(".", "pythonjs_to_dart.py", "./pythonjs_to_dart.py",
3438
true,false
3539
);

pythonjs/python_to_pythonjs.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,19 @@
2525
from ast import parse
2626
from ast import NodeVisitor
2727

28+
import typedpython
2829
import ministdlib
2930
import inline_function
3031
import code_writer
3132
from ast_utils import *
3233

33-
3434
## TODO
3535
def log(txt):
3636
pass
3737

3838

39+
POWER_OF_TWO = [ 2**i for i in range(32) ]
40+
3941
GLOBAL_VARIABLE_SCOPE = False ## Python style
4042
if '--global-variable-scope' in sys.argv: ## JavaScript style
4143
GLOBAL_VARIABLE_SCOPE = True
@@ -121,6 +123,9 @@ class PythonToPythonJS(NodeVisitor, inline_function.Inliner):
121123
_func_typedefs = ()
122124

123125
def __init__(self, source=None, module=None, module_path=None, dart=False, coffee=False, lua=False):
126+
127+
source = typedpython.transform_source( source )
128+
124129
super(PythonToPythonJS, self).__init__()
125130
self.setup_inliner( writer )
126131

@@ -144,7 +149,8 @@ def __init__(self, source=None, module=None, module_path=None, dart=False, coffe
144149
self._instance_attributes = dict() ## class name : [attribute names]
145150
self._class_attributes = dict()
146151
self._catch_attributes = None
147-
self._names = set() ## not used?
152+
self._typedef_vars = dict()
153+
#self._names = set() ## not used?
148154
self._instances = dict() ## instance name : class name
149155
self._decorator_properties = dict()
150156
self._decorator_class_props = dict()
@@ -1162,6 +1168,14 @@ def visit_BinOp(self, node):
11621168
else:
11631169
return '[%s]' %','.join(expanded)
11641170

1171+
elif op == '*' and left in self._typedef_vars and self._typedef_vars[left]=='int' and isinstance(node.right, ast.Num) and node.right.n in POWER_OF_TWO:
1172+
power = POWER_OF_TWO.index( node.right.n )
1173+
return '%s << %s'%(left, power)
1174+
1175+
elif op == '//' and left in self._typedef_vars and self._typedef_vars[left]=='int' and isinstance(node.right, ast.Num) and node.right.n in POWER_OF_TWO:
1176+
power = POWER_OF_TWO.index( node.right.n )
1177+
return '%s >> %s'%(left, power)
1178+
11651179
elif op == '//':
11661180
if self._with_dart:
11671181
return '(%s/%s).floor()' %(left, right)
@@ -1174,6 +1188,9 @@ def visit_BinOp(self, node):
11741188
elif op == '+' and not self._with_dart:
11751189
if '+' in self._direct_operators:
11761190
return '%s+%s'%(left, right)
1191+
elif left in self._typedef_vars and self._typedef_vars[left] in typedpython.number_types:
1192+
return '%s+%s'%(left, right)
1193+
11771194
elif self._with_lua or self._in_lambda or self._in_while_test:
11781195
## this is also required when in an inlined lambda like "(lambda a,b: a+b)(1,2)"
11791196
return '__add_op(%s, %s)'%(left, right)
@@ -1444,7 +1461,16 @@ def visit_Assign(self, node):
14441461
writer.write('try:')
14451462
writer.push()
14461463

1447-
for target in node.targets:
1464+
targets = list( node.targets )
1465+
target = targets[0]
1466+
if isinstance(target, ast.Name) and target.id in typedpython.types:
1467+
if len(targets)==2 and isinstance(targets[1], ast.Name):
1468+
self._typedef_vars[ targets[1].id ] = target.id
1469+
targets = targets[1:]
1470+
else:
1471+
raise SyntaxError(targets)
1472+
1473+
for target in targets:
14481474
self._visit_assign_helper( node, target )
14491475
node = ast.Expr( value=target )
14501476

@@ -2664,6 +2690,8 @@ def visit_FunctionDef(self, node):
26642690

26652691
writer.pull() ## end function body
26662692

2693+
self._typedef_vars = dict() ## clear typed variables
2694+
26672695
if inline:
26682696
self._with_inline = False
26692697

pythonjs/typedpython.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
whitespace = [' ', '\t']
2+
number_types = ['int', 'float']
3+
types = ['str']
4+
types.extend( number_types)
5+
6+
7+
def transform_source( source, strip=False ):
8+
output = []
9+
for line in source.splitlines():
10+
a = []
11+
for char in line:
12+
if a and char in whitespace:
13+
b = ''.join(a)
14+
b = b.strip()
15+
if b in types:
16+
if strip:
17+
a = a[ : -len(b) ]
18+
else:
19+
a.append('=')
20+
a.append( char )
21+
else:
22+
a.append( char )
23+
else:
24+
a.append( char )
25+
output.append( ''.join(a) )
26+
27+
return '\n'.join(output)
28+
29+
30+
test = '''
31+
int a = 1
32+
float b = 1.1
33+
str c = "hi"
34+
int d
35+
'''
36+
37+
if __name__ == '__main__':
38+
out = transform_source(test)
39+
print(out)
40+
import ast
41+
print( ast.parse(out) )

regtests/dict/contains.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
"""__getitem__"""
1+
"""key in dict"""
22

33
def main():
44
a = {'2': 22, 3:33}
5-
print(a)
6-
TestError('2' in a )
5+
TestError( '2' in a )
6+
TestError( 3 in a )
77

regtests/run.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515

1616
import os, sys, re, tempfile, subprocess, json
1717

18+
sys.path.append('../pythonjs')
19+
import typedpython
20+
1821
if 'NODE_PATH' not in os.environ:
1922
os.environ['NODE_PATH'] = '/usr/local/lib/node_modules/'
2023

@@ -262,6 +265,7 @@ def patch_python(filename, dart=False, python='PYTHONJS', backend=None):
262265
]
263266

264267
if python != 'PYTHONJS':
268+
code = typedpython.transform_source( code, strip=True )
265269
a.append( _python_only_extra_header )
266270

267271
a.append( code )

regtests/typed/int.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""int static type"""
2+
3+
4+
def main():
5+
int x = 1
6+
y = x + x
7+
TestError( y==2 )
8+
9+
int z = 2
10+
w = z * 2
11+
TestError( w==4 )
12+
13+
w = z * 3
14+
TestError( w==6 )
15+
16+
w = z * 64
17+
TestError( w==128 )
18+
19+
w = z // 2
20+
TestError( w==1 )
21+
22+
z = 640
23+
w = z // 64
24+
TestError( w==10 )

0 commit comments

Comments
 (0)