Skip to content

Commit a6d3f52

Browse files
author
hartsantler
committed
lua backend: improved list implementation, support for simple classes.
1 parent eb0a714 commit a6d3f52

7 files changed

Lines changed: 283 additions & 98 deletions

File tree

pythonjs.js

Lines changed: 77 additions & 77 deletions
Large diffs are not rendered by default.

pythonjs/python_to_pythonjs.py

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ class PythonToPythonJS(NodeVisitor):
172172
def __init__(self, source=None, module=None, module_path=None, dart=False, coffee=False, lua=False):
173173
super(PythonToPythonJS, self).__init__()
174174

175+
self._with_ll = False ## lowlevel
175176
self._with_lua = lua
176177
self._with_coffee = coffee
177178
self._with_dart = dart
@@ -380,7 +381,7 @@ def visit_Dict(self, node):
380381
v = self.visit( node.values[i] )
381382
if self._with_js:
382383
a.append( '[%s,%s]'%(k,v) )
383-
elif self._with_dart:
384+
elif self._with_dart or self._with_ll:
384385
a.append( '%s:%s'%(k,v) )
385386
#if isinstance(node.keys[i], ast.Str):
386387
# a.append( '%s:%s'%(k,v) )
@@ -392,7 +393,7 @@ def visit_Dict(self, node):
392393
if self._with_js:
393394
b = ','.join( a )
394395
return '__jsdict( [%s] )' %b
395-
elif self._with_dart:
396+
elif self._with_dart or self._with_ll:
396397
b = ','.join( a )
397398
return '{%s}' %b
398399
else:
@@ -411,6 +412,8 @@ def visit_Tuple(self, node):
411412
def visit_List(self, node):
412413
node.returns_type = 'list'
413414
a = '[%s]' % ', '.join(map(self.visit, node.elts))
415+
if self._with_lua:
416+
a = '__get__(list, "__call__")({}, {pointer:%s, length:%s})'%(a, len(node.elts))
414417
return a
415418

416419
def visit_GeneratorExp(self, node):
@@ -513,7 +516,35 @@ def visit_AugAssign(self, node):
513516
op = '%s=' %self.visit( node.op )
514517

515518
typedef = self.get_typedef( node.target )
516-
if typedef and op in typedef.operators:
519+
520+
if self._with_lua:
521+
if op == '+=':
522+
a = '__add__(%s,%s)' %(target, self.visit(node.value))
523+
elif op == '-=':
524+
a = '__sub__(%s,%s)' %(target, self.visit(node.value))
525+
elif op == '*=':
526+
a = '__mul__(%s,%s)' %(target, self.visit(node.value))
527+
elif op == '/=':
528+
a = '__div__(%s,%s)' %(target, self.visit(node.value))
529+
elif op == '%=':
530+
a = '__mod__(%s,%s)' %(target, self.visit(node.value))
531+
elif op == '&=':
532+
a = '__and__(%s,%s)' %(target, self.visit(node.value))
533+
elif op == '|=':
534+
a = '__or__(%s,%s)' %(target, self.visit(node.value))
535+
elif op == '^=':
536+
a = '__xor__(%s,%s)' %(target, self.visit(node.value))
537+
elif op == '<<=':
538+
a = '__lshift__(%s,%s)' %(target, self.visit(node.value))
539+
elif op == '>>=':
540+
a = '__rshift__(%s,%s)' %(target, self.visit(node.value))
541+
else:
542+
raise NotImplementedError
543+
544+
writer.write('%s=%s' %(target,a))
545+
546+
547+
elif typedef and op in typedef.operators:
517548
func = typedef.operators[ op ]
518549
a = '%s( [%s, %s] )' %(func, target, self.visit(node.value))
519550
writer.write( a )
@@ -793,13 +824,13 @@ def visit_ClassDef(self, node):
793824
if item_name in self._decorator_properties:
794825
pass
795826
else:
796-
writer.write('__%s_attrs["%s"] = %s' % (name, item_name, item.name))
827+
writer.write('__%s_attrs.%s = %s' % (name, item_name, item.name))
797828

798829
elif isinstance(item, Assign) and isinstance(item.targets[0], Name):
799830
item_name = item.targets[0].id
800831
item.targets[0].id = '__%s_%s' % (name, item_name)
801832
self.visit(item) # this will output the code for the assign
802-
writer.write('__%s_attrs["%s"] = %s' % (name, item_name, item.targets[0].id))
833+
writer.write('__%s_attrs.%s = %s' % (name, item_name, item.targets[0].id))
803834
self._class_attributes[ name ].add( item_name ) ## should this come before self.visit(item) ??
804835
elif isinstance(item, Pass):
805836
pass
@@ -813,7 +844,7 @@ def visit_ClassDef(self, node):
813844
item_name = sub.targets[0].id
814845
sub.targets[0].id = '__%s_%s' % (name, item_name)
815846
self.visit(sub) # this will output the code for the assign
816-
writer.write('__%s_attrs["%s"] = %s' % (name, item_name, sub.targets[0].id))
847+
writer.write('__%s_attrs.%s = %s' % (name, item_name, sub.targets[0].id))
817848
self._class_attributes[ name ].add( item_name ) ## should this come before self.visit(item) ??
818849
else:
819850
raise NotImplementedError( sub )
@@ -836,7 +867,7 @@ def visit_ClassDef(self, node):
836867
self._instances.pop('self')
837868
self._in_class = False
838869

839-
writer.write('%s = create_class("%s", __%s_parents, __%s_attrs, __%s_properties)' % (name, name, name, name, name))
870+
writer.write('%s = __create_class__("%s", __%s_parents, __%s_attrs, __%s_properties)' % (name, name, name, name, name))
840871
if 'init' in self._injector:
841872
writer.write('%s.init_callbacks = JSArray()' %name)
842873
self._injector = []
@@ -1194,7 +1225,7 @@ def visit_Subscript(self, node):
11941225
#return '%s["$wrapped"]' %name
11951226
return '%s[...]' %name
11961227

1197-
elif self._with_lua:
1228+
elif self._with_ll:
11981229
return '%s[ %s ]' %(name, self.visit(node.slice))
11991230

12001231
elif self._with_js or self._with_dart:
@@ -1271,7 +1302,7 @@ def _visit_assign_helper(self, node, target):
12711302
#code = '%s["$wrapped"] = %s' %(self.visit(target.value), self.visit(node.value))
12721303
code = '%s[...] = %s' %(self.visit(target.value), self.visit(node.value))
12731304

1274-
elif self._with_dart:
1305+
elif self._with_dart or self._with_ll:
12751306
code = '%s[ %s ] = %s'
12761307
code = code % (self.visit(target.value), self.visit(target.slice.value), self.visit(node.value))
12771308

@@ -1320,8 +1351,8 @@ def _visit_assign_helper(self, node, target):
13201351
setter = parent_prop.properties[target.attr]['set']
13211352
writer.write( '%s( [%s, %s], JSObject() )' %(setter, target_value, self.visit(node.value)) )
13221353

1323-
elif parent_classattr: ## TODO fix get/set class attributes
1324-
writer.write( "__%s_attrs['%s'] = %s" %(parent_classattr.name, target.attr, self.visit(node.value)) )
1354+
elif parent_classattr:
1355+
writer.write( "__%s_attrs.%s = %s" %(parent_classattr.name, target.attr, self.visit(node.value)) )
13251356

13261357
elif parent_setattr:
13271358
func = parent_setattr.get_pythonjs_function_name( '__setattr__' )
@@ -2024,8 +2055,6 @@ def visit_FunctionDef(self, node):
20242055
#####################################################################
20252056
if self._with_dart:
20262057
pass
2027-
#elif self._with_coffee:
2028-
# pass
20292058

20302059
elif self._with_js or javascript:
20312060
if node.args.defaults:
@@ -2057,7 +2086,7 @@ def visit_FunctionDef(self, node):
20572086
default_value = self.visit( node.args.defaults[dindex] )
20582087
writer.write("%s = kwargs.%s or %s" % (arg.id, arg.id, default_value))
20592088
else:
2060-
writer.write( "%s = args[ %s ]" %(arg.id, i) )
2089+
writer.write( "%s = args[ %s ]" %(arg.id, i+1) )
20612090

20622091
elif len(node.args.defaults) or len(node.args.args) or node.args.vararg or node.args.kwarg:
20632092
# First check the arguments are well formed
@@ -2442,7 +2471,11 @@ def visit_While(self, node):
24422471
writer.pull()
24432472

24442473
def visit_With(self, node):
2445-
if isinstance( node.context_expr, Name ) and node.context_expr.id == 'javascript':
2474+
if isinstance( node.context_expr, Name ) and node.context_expr.id == 'lowlevel':
2475+
self._with_ll = True
2476+
map(self.visit, node.body)
2477+
self._with_ll = False
2478+
elif isinstance( node.context_expr, Name ) and node.context_expr.id == 'javascript':
24462479
self._with_js = True
24472480
writer.with_javascript = True
24482481
map(self.visit, node.body)

pythonjs/pythonjs_to_lua.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ class LuaGenerator( pythonjs.JSGenerator ):
3131
_classes = dict()
3232
_class_props = dict()
3333

34+
def _visit_subscript_ellipsis(self, node):
35+
name = self.visit(node.value)
36+
return '%s.__wrapped__' %name
37+
3438
def visit_Name(self, node):
3539
if node.id == 'None':
3640
return 'nil'
@@ -53,7 +57,7 @@ def visit_Subscript(self, node):
5357
if isinstance(node.slice, ast.Ellipsis):
5458
return self._visit_subscript_ellipsis( node )
5559
else:
56-
return '%s[%s+1]' % (self.visit(node.value), self.visit(node.slice))
60+
return '%s[%s]' % (self.visit(node.value), self.visit(node.slice))
5761

5862
def _visit_call_helper_JSObject(self, node):
5963
if node.keywords:
@@ -103,9 +107,7 @@ def visit_While(self, node):
103107
self.pull()
104108
return '\n'.join( body )
105109

106-
def _visit_subscript_ellipsis(self, node):
107-
name = self.visit(node.value)
108-
return '%s.$wrapped' %name
110+
109111

110112
def visit_Pass(self, node):
111113
return '###pass###'

regtests/list/index.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"""list indices"""
2+
def main():
3+
a = [1,2,3,4]
4+
idx = 1
5+
TestError( a[0]==1 )
6+
TestError( a[idx]==2 )
7+
TestError( a.index(3)==2 )

regtests/list/setitem.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""setitem and append"""
2+
def main():
3+
a = [1,2,3,4]
4+
idx = 1
5+
TestError( a[0]==1 )
6+
TestError( a[idx]==2 )
7+
8+
a[ 0 ] = 'hello'
9+
a[ 1 ] = 'world'
10+
TestError( a[0]=='hello' )
11+
TestError( a[1]=='world' )
12+
13+
a.append( 'xxx' )
14+
TestError( a[4]=='xxx' )

runtime/builtins.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ def __sprintf(fmt, args):
188188
arr.append( Object.prototype.toString.call(item) )
189189
return ''.join(arr)
190190

191-
def create_class(class_name, parents, attrs, props):
191+
def __create_class__(class_name, parents, attrs, props):
192192
"""Create a PythonScript class"""
193193
if attrs.__metaclass__:
194194
metaclass = attrs.__metaclass__

runtime/lua_builtins.py

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,84 @@
11
JS("""
22
__NULL_OBJECT__ = {}
33
4+
__concat_tables_array = function(t1, t2)
5+
for i=1,#t2 do
6+
t1[ #t1+1 ] = t2[i]
7+
end
8+
return t1
9+
end
10+
11+
__concat_tables = function(t1, t2)
12+
for k,v in pairs(t2) do
13+
t1[k] = v
14+
end
15+
return t1
16+
end
17+
18+
__test_if_true__ = function( x )
19+
if x == 0 then
20+
return false
21+
elseif x == nil then
22+
return false
23+
else
24+
return true
25+
end
26+
end
27+
428
__get__ = function(ob, name)
529
if name == '__call__' then
6-
return ob
30+
if type(ob)=='function' then
31+
return ob
32+
else
33+
return ob.__call__
34+
end
35+
elseif name == '__iter__' then
36+
local iter = {
37+
index = 1,
38+
length = #ob,
39+
object = ob,
40+
next_fast = function(self)
41+
local x = self.object[ self.index ]
42+
self.index = self.index + 1
43+
return x
44+
end
45+
}
46+
return iter
47+
else
48+
return ob[ name ]
749
end
850
end
951
52+
__create_class__ = function(class_name, parents, attrs, props)
53+
local class = {
54+
__bases__ = parents,
55+
__name__ = class_name,
56+
__properties__ = props,
57+
__attributes__ = attrs
58+
}
59+
function class.__call__( args, kwargs )
60+
object = {
61+
__class__ = class,
62+
__dict__ = 'TODO'
63+
}
64+
for k,v in pairs(attrs) do
65+
if type(v)=='function' then
66+
object[ k ] = function(_args, _kwargs)
67+
a = {object}
68+
return v(__concat_tables_array(a, _args), _kwargs)
69+
end
70+
else
71+
object[ k ] = v
72+
end
73+
end
74+
a = {object}
75+
attrs.__init__( __concat_tables_array(a, args), kwargs )
76+
return object
77+
end
78+
return class
79+
end
80+
81+
1082
__add_op = function(a,b)
1183
if type(a) == 'string' then
1284
return a .. b
@@ -15,6 +87,14 @@
1587
end
1688
end
1789
90+
__add__ = function(a,b)
91+
if type(a) == 'string' then
92+
return a .. b
93+
else
94+
return a + b
95+
end
96+
end
97+
1898
""")
1999

20100
def str(ob):
@@ -25,3 +105,52 @@ def int(ob):
25105

26106
def float(ob):
27107
return tonumber(ob)
108+
109+
class list:
110+
'''
111+
Array length in Lua must be manually tracked, because a normal for loop will not
112+
properly loop over a sparse Array with nil holes.
113+
'''
114+
def __init__(self, items, pointer=None, length=0):
115+
with lowlevel:
116+
self.length = length
117+
if pointer:
118+
self[...] = pointer
119+
else:
120+
self[...] = {}
121+
122+
def __getitem__(self, index):
123+
with lowlevel:
124+
if index < 0:
125+
index = self.length + index
126+
return self[...][index+1]
127+
128+
def __setitem__(self, index, value):
129+
with lowlevel:
130+
if index < 0:
131+
index = self.length + index
132+
self[...][index+1] = value
133+
134+
def __getslice__(self, start, stop, step):
135+
if stop == null and step == null:
136+
return self[...].sublist( start )
137+
elif stop < 0:
138+
stop = self[...].length + stop
139+
return self[...].sublist(start, stop)
140+
141+
def __add__(self, other):
142+
for item in other:
143+
self.append( item )
144+
145+
def append(self, item):
146+
with lowlevel:
147+
self.length += 1
148+
self[...][ self.length ] = item
149+
150+
def index(self, obj):
151+
with lowlevel:
152+
i = 1
153+
while i < self.length:
154+
if self[...][i] == obj:
155+
return i
156+
i += 1

0 commit comments

Comments
 (0)