@@ -74,6 +74,8 @@ def __init__(self):
7474 self ._catch_attributes = None
7575 self ._names = set ()
7676 self ._instances = dict () ## instance name : class name
77+ self ._decorator_properties = dict ()
78+ self ._decorator_class_props = dict ()
7779
7880 def visit_Assert (self , node ):
7981 ## hijacking "assert isinstance(a,A)" as a type system ##
@@ -128,6 +130,8 @@ def visit_ClassDef(self, node):
128130 name = node .name
129131 self ._classes [ name ] = list () ## method names
130132 self ._catch_attributes = None
133+ self ._decorator_properties = dict () ## property names : {'get':func, 'set':func}
134+
131135 for dec in node .decorator_list :
132136 if isinstance (dec , Name ) and dec .id == 'inline' :
133137 self ._catch_attributes = set ()
@@ -142,10 +146,15 @@ def visit_ClassDef(self, node):
142146 if isinstance (item , FunctionDef ):
143147 self ._classes [ name ].append ( item .name )
144148 item_name = item .name
149+ item .original_name = item .name
145150 item .name = '__%s_%s' % (name , item_name )
151+
146152 self .visit (item ) # this will output the code for the function
147- #writer.write('__%s_attrs.%s = %s' % (name, item_name, item.name)) ## not ClosureCompiler compatible
148- writer .write ('__%s_attrs["%s"] = %s' % (name , item_name , item .name ))
153+
154+ if item_name in self ._decorator_properties :
155+ pass
156+ else :
157+ writer .write ('__%s_attrs["%s"] = %s' % (name , item_name , item .name ))
149158
150159 if item_name == '__getattr__' :
151160 writer .write ( self ._gen_getattr_helper (name , item .name ) )
@@ -154,11 +163,16 @@ def visit_ClassDef(self, node):
154163 item_name = item .targets [0 ].id
155164 item .targets [0 ].id = '__%s_%s' % (name .id , item_name )
156165 self .visit (item ) # this will output the code for the assign
157- #writer.write('%s_attrs.%s = %s' % (name, item_name, item.targets[0].id)) ## not ClosureCompiler compatible
158166 writer .write ('%s_attrs["%s"] = %s' % (name , item_name , item .targets [0 ].id ))
159167
160- if self ._catch_attributes : self ._inline_classes [ name ] = self ._catch_attributes
168+ if self ._catch_attributes :
169+ self ._inline_classes [ name ] = self ._catch_attributes
170+ if self ._decorator_properties :
171+ self ._decorator_class_props [ name ] = self ._decorator_properties
172+ writer .write ('#@props: %s' % self ._decorator_properties )
173+
161174 self ._catch_attributes = None
175+ self ._decorator_properties = None
162176
163177 writer .write ('%s = create_class("%s", __%s_parents, __%s_attrs)' % (name , name , name , name ))
164178
@@ -264,6 +278,9 @@ def visit_Attribute(self, node):
264278 return '''JS('%s["__dict__"]["%s"]')''' % (name , node .attr )
265279 elif node .attr in self ._classes [ klass ]: ## method
266280 return '''JS('__%s_attrs["%s"]')''' % (klass , node .attr )
281+ elif klass in self ._decorator_class_props and node .attr in self ._decorator_class_props [klass ]:
282+ getter = self ._decorator_class_props [klass ][node .attr ]['get' ]
283+ return '''JS('%s( [%s] )')''' % (getter , name )
267284 else :
268285 return '''JS('__%s___getattr__( [%s, "%s"] )')''' % (klass , name , node .attr )
269286
@@ -275,8 +292,16 @@ def visit_Attribute(self, node):
275292 return '''JS('%s["__dict__"]["%s"]')''' % (name , node .attr )
276293 elif node .attr in self ._classes [ klass ]: ## method
277294 return '''JS('__%s_attrs["%s"]')''' % (klass , node .attr )
295+ elif klass in self ._decorator_class_props and node .attr in self ._decorator_class_props [klass ]:
296+ getter = self ._decorator_class_props [klass ][node .attr ]['get' ]
297+ return '''JS('%s( [%s] )')''' % (getter , name )
278298 else :
279299 return '''JS('__%s___getattr__( [%s, "%s"] )')''' % (klass , name , node .attr )
300+
301+ elif klass in self ._decorator_class_props and node .attr in self ._decorator_class_props [klass ]:
302+ getter = self ._decorator_class_props [klass ][node .attr ]['get' ]
303+ return '''JS('%s( [%s] )')''' % (getter , name )
304+
280305 else :
281306 return 'get_attribute(%s, "%s")' % (name , node .attr )
282307 else :
@@ -317,12 +342,23 @@ def visit_Assign(self, node):
317342 if name == 'self' and isinstance (self ._catch_attributes , set ):
318343 self ._catch_attributes .add ( target .attr )
319344
320- code = 'set_attribute(%s, "%s", %s)' % (
321- name ,
322- target .attr ,
323- self .visit (node .value )
324- )
325- writer .write (code )
345+ fallback = True
346+ if name in self ._instances : ## support '.' operator overloading
347+ klass = self ._instances [ name ]
348+ if klass in self ._decorator_class_props and target .attr in self ._decorator_class_props [klass ]:
349+ setter = self ._decorator_class_props [klass ][target .attr ].get ( 'set' , None )
350+ if setter :
351+ writer .write ( '''JS('%s( [%s, %s] )')''' % (setter , name , self .visit (node .value )) )
352+ fallback = False
353+
354+
355+ if fallback :
356+ code = 'set_attribute(%s, "%s", %s)' % (
357+ name ,
358+ target .attr ,
359+ self .visit (node .value )
360+ )
361+ writer .write (code )
326362 elif isinstance (target , Name ):
327363
328364 if isinstance (node .value , Call ) and hasattr (node .value .func , 'id' ) and node .value .func .id in self ._classes :
@@ -401,6 +437,29 @@ def visit_Call(self, node):
401437 return '%s()' % name
402438
403439 def visit_FunctionDef (self , node ):
440+ property_decorator = None
441+ decorators = []
442+ for decorator in reversed (node .decorator_list ):
443+ if isinstance (decorator , Name ) and decorator .id == 'property' :
444+ property_decorator = decorator
445+ n = node .name + '__getprop__'
446+ self ._decorator_properties [ node .original_name ] = dict ( get = n )
447+ node .name = n
448+
449+ elif isinstance (decorator , Attribute ) and isinstance (decorator .value , Name ) and decorator .value .id in self ._decorator_properties :
450+ if decorator .attr == 'setter' :
451+ n = node .name + '__setprop__'
452+ self ._decorator_properties [ decorator .value .id ]['set' ] = n
453+ node .name = n
454+ elif decorator .attr == 'deleter' :
455+ raise NotImplementedError
456+ else :
457+ raise RuntimeError
458+
459+ else :
460+ decorators .append ( decorator )
461+
462+
404463 writer .write ('def %s(args, kwargs):' % node .name )
405464 writer .push ()
406465
@@ -470,7 +529,7 @@ def visit_FunctionDef(self, node):
470529 writer .pull ()
471530
472531 # apply decorators
473- for decorator in reversed ( node . decorator_list ) :
532+ for decorator in decorators :
474533 writer .write ('%s = %s(create_array(%s))' % (node .name , self .visit (decorator ), node .name ))
475534
476535 def visit_For (self , node ):
0 commit comments