Final meta patch

35 views
Skip to first unread message

Phil Hassey

unread,
Jun 10, 2008, 8:49:30 PM6/10/08
to tin...@googlegroups.com
Hey,

So .. here's another class / meta patch.  This one combines the "best of the best" from all the previous approaches.

- In python code, you can write really clean class interfaces (like in the python style patch).
- You also get a getmeta / setmeta function which you can use to change the "class" of an object.  Or even change the parent class of a class.  Or whatever.
- You can change methods on a class "real-time" and the objects will "reflect" those changes since methods are looked-up at request.
- Memory use is reduced for objects with dozens of methods as these methods aren't all created upon instantiation of an object.
- Speed increase is only 10%, which is quite good.
- Internally dicts can be in any of 3 states: "default" which will check its meta on a get.  "object" which will check its metas do various meta actions for you and will return found functions as methods.  "raw" which ignores its meta.  (There might be some bugs in my differentiation of "object" vs "default" .. these will have to be fixed.)

Feedback requested before I commit this.  I'm thinking this one is looking pretty good ...

-Phil


meta4.patch

Phil Hassey

unread,
Jun 10, 2008, 10:47:12 PM6/10/08
to tin...@googlegroups.com
I've patched the hole I alluded to in the previous post ...  I think the three "kinds" of dicts are now properly distinct.

Allefant - take a good look at this patch, I think it does everything you want, cleanly and quickly.

-Phil

--- On Tue, 6/10/08, Phil Hassey <philh...@yahoo.com> wrote:
Index: tinypy/ops.c =================================================================== --- tinypy/ops.c (revision 49) +++ tinypy/ops.c (working copy) @@ -1,13 +1,3 @@ -#define TP_META_BEGIN(self,name) \ - if (self.dict.dtype && self.dict.val->meta.type != TP_NONE) { \ - int n = _tp_dict_find(tp,self.dict.val->meta.dict.val,tp_string(name)); \ - if (n != -1) { \ - tp_obj meta = self.dict.val->meta.dict.val->items[n].val; - -#define TP_META_END \ - } \ - } - tp_obj tp_str(TP,tp_obj self) { int type = self.type; if (type == TP_STRING) { return self; } @@ -80,8 +70,9 @@ tp_obj r; if (type == TP_DICT) { TP_META_BEGIN(self,"__get__"); - return tp_call(tp,meta,tp_params_v(tp,2,self,k)); + return tp_call(tp,meta,tp_params_v(tp,1,k)); TP_META_END; + if (self.dict.dtype && _tp_lookup(tp,self,k,&r)) { return r; } return _tp_dict_get(tp,self.dict.val,k,"tp_get"); } else if (type == TP_LIST) { if (k.type == TP_NUMBER) { @@ -176,7 +167,7 @@ if (type == TP_DICT) { TP_META_BEGIN(self,"__set__"); - tp_call(tp,meta,tp_params_v(tp,3,self,k,v)); + tp_call(tp,meta,tp_params_v(tp,2,k,v)); return; TP_META_END; _tp_dict_set(tp,self.dict.val,k,v); Index: tinypy/encode.py =================================================================== --- tinypy/encode.py (revision 49) +++ tinypy/encode.py (working copy) @@ -470,6 +470,7 @@ parent = None if items[0].type == 'name': name = items[0].val + parent = Token(tok.pos,'name','object') else: name = items[0].items[0].val parent = items[0].items[1] @@ -480,22 +481,15 @@ code(GSET,ts,kls) free_tmp(ts) #REG - if parent: - free_tmp(do(Token(tok.pos,'symbol','=',[ - Token(tok.pos,'get',None,[ - Token(tok.pos,'reg',kls), - Token(tok.pos,'string','__parent__')]), - parent]))) - + free_tmp(do(Token(tok.pos,'call',None,[ + Token(tok.pos,'name','setmeta'), + Token(tok.pos,'reg',kls), + parent]))) + for fc in items[1].items: if fc.type != 'def': continue do_def(fc,kls) - free_tmp(do(Token(tok.pos,'call',None,[ - Token(tok.pos,'name','setmeta'), - Token(tok.pos,'reg',kls), - Token(tok.pos,'name','ClassMeta')]))) - free_reg(kls) #REG Index: tinypy/tests.py =================================================================== --- tinypy/tests.py (revision 49) +++ tinypy/tests.py (working copy) @@ -442,13 +442,6 @@ """ ,"OK") - t_render(""" -class C: - def __init__(self,data): self.data = data - def print(self): print(self.data) -C("OK").print() -""" -,"OK") t_render(""" x = [v*v for v in range(0,5)] @@ -582,22 +575,6 @@ print(x) ""","OK") - t_render(""" -class X: - pass -y = X() -print("OK") -""","OK") - - t_render(""" -class X: pass -def test(): y = X() -test() -print("OK") -""","OK") - - t_render(["class X: pass\ndef test(): y = X()","import tmp1\ntmp1.test();print('OK')"],"OK") - t_render("print(len([1,2,3]))","3") t_render('if not "?" in "xyz": print("OK")',"OK") @@ -637,30 +614,8 @@ #t_render("def test(): print('OK')\n{'__call__':test}()","OK") - t_render(""" -class A: - def __init__(self): - self.a = 'O' - self.b = 'x' - def test(self): - print("KO") -class B(A): - def __init__(self): - A.__init__(self) - self.b = 'K' - def test(self): - print(self.a+self.b) -B().test() -""","OK") t_render(""" -class A: - def test(self): - print(self) -A.test("OK") -""","OK") - - t_render(""" def test(): def fnc(): print("OK") @@ -722,39 +677,28 @@ t_render(""" def get(self,k): return k+"K" -m = {"__get__":get} -v = {} -setmeta(v,m) +v = object() +v.__get__ = bind(get,v) print(v.O) -""" -,"OK") +""", +"OK") t_render(""" def set(self,k,v): self = getraw(self) self[k] = v + "K" -m = {"__set__":set} -v = {} -setmeta(v,m) +v = object() +v.__set__ = bind(set,v) v.x = "O" print(v.x) -""" -,"OK") - - t_render(""" -m = {"test":"OK"} -v = {} -setmeta(v,m) -print(getmeta(v)["test"]) """, "OK") t_render(""" def call(self,x): print(x) -m = {"__call__":call} -v = {} -setmeta(v,m) +v = object() +v.__call__ = bind(call,v) v("OK") """ ,"OK") @@ -769,24 +713,12 @@ print("OK") ""","OK") - meta_objs_init = """ -def MyObj_bind(klass,self): - if '__parent__' in klass: - MyObj_bind(klass.__parent__,self) - for k in klass: - v = klass[k] - if istype(v,'fnc'): - self[k] = bind(v,self) - else: - self[k] = v -def MyObj_call(klass,*p): - self = {} - MyObj_bind(klass,self) - if '__init__' in self: - self.__init__(*p) +def my_new(klass,*p): + self = object() + setmeta(self,klass) + self.__init__(*p) return self -MyObj = {'__call__':MyObj_call} def A_init(self,v): if v: print("A_init") @@ -794,16 +726,14 @@ print("A_test1") def A_test2(self): print("A_test2") -A = {'__init__':A_init,'test1':A_test1,'test2':A_test2} -setmeta(A,MyObj) +A = {'__new__':my_new,'__init__':A_init,'test1':A_test1,'test2':A_test2} def B_init(self,v): if v: print("B_init") def B_test2(self): print("B_test2") -B = {'__parent__':A,'__init__':B_init,'test2':B_test2} -setmeta(B,MyObj) - +B = {'__init__':B_init,'test2':B_test2} +setmeta(B,A) """ t_render(meta_objs_init+"""A(True)""","A_init") @@ -813,22 +743,85 @@ t_render(meta_objs_init+"""B(False).test1()""","A_test1") t_render(meta_objs_init+"""B(False).test2()""","B_test2") - #test that you can make a callable meta object + #various class construct use tests t_render(""" -class TestMeta: +class C: + def __init__(self,data): self.data = data + def print(self): print(self.data) +C("OK").print() +""" +,"OK") + + t_render(""" +class X: + pass +y = X() +print("OK") +""","OK") + + t_render(""" +class X: pass +def test(): y = X() +test() +print("OK") +""","OK") + + t_render(["class X: pass\ndef test(): y = X()","import tmp1\ntmp1.test();print('OK')"],"OK") + + t_render(""" +class A: + def __init__(self): + self.a = 'O' + self.b = 'x' + def test(self): + print("KO") +class B(A): + def __init__(self): + A.__init__(self) + self.b = 'K' + def test(self): + print(self.a+self.b) +B().test() +""","OK") + + t_render(""" +class A: + def test(self): + print(self) +A.test("OK") +""","OK") + + + #test that you can make a callable object + t_render(""" +class Test: + def __init__(self,v): + self.value = v def __call__(self): - self = getraw(self) print(self.value) -class Test: - def __init__(self,value): - self.value = value - setmeta(self,TestMeta) - x = Test('OK') x() ""","OK") + #test that you can use a __get__ + t_render(""" +class Test: + def __get__(self,k): + return k+"K" +x = Test() +print(x.O) +""","OK") + #test that you can use __set__ + t_render(""" +class Test: + def __set__(self,k,v): + getraw(self)[k] = "O"+v +x = Test() +x.v = "K" +print(x.v) +""","OK") + #test that exceptions are cleared after they are caught #and not repeated t_render(""" @@ -845,6 +838,35 @@ pass ""","OK") + #check that missing attributes throw an error + t_render(""" +class A: pass +try: + A().x +except: + print('OK') +""","OK") + + #check that a changed attribute gets changed + t_render(""" +class A: + def x(self): pass +a = A() +a.x = "OK" +print(a.x) +""","OK") + + #test that you can use a __get__ gets inherited + t_render(""" +class A: + def __get__(self,k): + return k+"K" +class B(A): + pass +x = B() +print(x.O) +""","OK") + ################################################################################ def t_boot(ss,ex,exact=True): @@ -969,11 +991,9 @@ /* create our class */ tp_obj tmp; - tp_obj A = tp_dict(tp); + tp_obj A = tp_class(tp); tp_set(tp,A,tp_string("__init__"),tp_fnc(tp,A_init)); tp_set(tp,A,tp_string("test"),tp_fnc(tp,A_test)); - tp_params_v(tp,2,A,tp_get(tp,tp->builtins,tp_string("ClassMeta"))); - tp_setmeta(tp); /* instantiate it and call test */ tmp = tp_call(tp,A,tp_params_v(tp,1,tp_string("OK"))); Index: tinypy/vm.c =================================================================== --- tinypy/vm.c (revision 49) +++ tinypy/vm.c (working copy) @@ -105,10 +105,16 @@ tp->params = params; if (self.type == TP_DICT) { - TP_META_BEGIN(self,"__call__"); - _tp_list_insert(tp,params.list.val,0,self); - return tp_call(tp,meta,params); - TP_META_END; + if (self.dict.dtype == 1) { + TP_META_BEGIN(self,"__new__"); + _tp_list_insert(tp,params.list.val,0,self); + return tp_call(tp,meta,params); + TP_META_END; + } else if (self.dict.dtype == 3) { + TP_META_BEGIN(self,"__call__"); + return tp_call(tp,meta,params); + TP_META_END; + } } if (self.type == TP_FNC && !(self.fnc.ftype&1)) { tp_obj r = _tp_tcall(tp,self); @@ -334,6 +340,7 @@ } void tp_builtins(TP) { + tp_obj o; struct {const char *s;void *f;} b[] = { {"print",tp_print}, {"range",tp_range}, {"min",tp_min}, {"max",tp_max}, {"bind",tp_bind}, {"copy",tp_copy}, @@ -343,19 +350,18 @@ {"load",tp_load}, {"fpack",tp_fpack}, {"abs",tp_abs}, {"int",tp_int}, {"exec",tp_exec_}, {"exists",tp_exists}, {"mtime",tp_mtime}, {"number",tp_float}, {"round",tp_round}, - {"ord",tp_ord}, {"merge",tp_merge}, {"setmeta",tp_setmeta}, - {"getraw",tp_getraw}, {"getmeta",tp_getmeta}, + {"ord",tp_ord}, {"merge",tp_merge}, {"getraw",tp_getraw}, + {"setmeta",tp_setmeta}, {"getmeta",tp_getmeta}, {0,0}, }; int i; for(i=0; b[i].s; i++) { tp_set(tp,tp->builtins,tp_string(b[i].s),tp_fnc(tp,(tp_obj (*)(tp_vm *))b[i].f)); } - /* - BUILTINS['ClassMeta'] = {'__call__':ClassMeta_call} - */ - tp_set(tp,tp->builtins,tp_string("ClassMeta"),tp_dict_n(tp,1,(tp_obj[]){tp_string("__call__"),tp_fnc(tp,tp_ClassMeta_call)})); - + o = tp_object(tp); + tp_set(tp,o,tp_string("__call__"),tp_fnc(tp,tp_object_call)); + tp_set(tp,o,tp_string("__new__"),tp_fnc(tp,tp_object_new)); + tp_set(tp,tp->builtins,tp_string("object"),o); } Index: tinypy/tp.h =================================================================== --- tinypy/tp.h (revision 49) +++ tinypy/tp.h (working copy) @@ -183,6 +183,7 @@ void tp_set(TP,tp_obj,tp_obj,tp_obj); tp_obj tp_get(TP,tp_obj,tp_obj); +tp_obj tp_has(TP,tp_obj self, tp_obj k); tp_obj tp_len(TP,tp_obj); tp_obj tp_str(TP,tp_obj); int tp_cmp(TP,tp_obj,tp_obj); Index: tinypy/builtins.c =================================================================== --- tinypy/builtins.c (revision 49) +++ tinypy/builtins.c (working copy) @@ -89,7 +89,8 @@ if (strcmp("list",t) == 0) { return tp_number(v.type == TP_LIST); } if (strcmp("dict",t) == 0) { return tp_number(v.type == TP_DICT); } if (strcmp("number",t) == 0) { return tp_number(v.type == TP_NUMBER); } - if (strcmp("fnc",t) == 0) { return tp_number(v.type == TP_FNC); } + if (strcmp("fnc",t) == 0) { return tp_number(v.type == TP_FNC && (v.fnc.ftype&2) == 0); } + if (strcmp("method",t) == 0) { return tp_number(v.type == TP_FNC && (v.fnc.ftype&2) != 0); } tp_raise(tp_None,"is_type(%s,%s)",TP_CSTR(v),t); } @@ -173,6 +174,29 @@ tp_raise(tp_None,"tp_mtime(%s)",s); } +int _tp_lookup(TP,tp_obj self, tp_obj k, tp_obj *meta) { + int n = _tp_dict_find(tp,self.dict.val,k); + if (n != -1) { + *meta = self.dict.val->items[n].val; + return 1; + } + if (self.dict.dtype && self.dict.val->meta.type == TP_DICT && _tp_lookup(tp,self.dict.val->meta,k,meta)) { + if (self.dict.dtype == 3 && meta->type == TP_FNC) { + *meta = tp_fnc_new(tp,meta->fnc.ftype|2,meta->fnc.val,self,meta->fnc.info->globals); + } + return 1; + } + return 0; +} + +#define TP_META_BEGIN(self,name) \ + if (self.dict.dtype) { \ + tp_obj meta; if (_tp_lookup(tp,self,tp_string(name),&meta)) { + +#define TP_META_END \ + } \ + } + tp_obj tp_setmeta(TP) { tp_obj self = TP_TYPE(TP_DICT); tp_obj meta = TP_TYPE(TP_DICT); @@ -185,68 +209,42 @@ return self.dict.val->meta; } -tp_obj tp_getraw(TP) { - tp_obj self = TP_TYPE(TP_DICT); - self.dict.dtype = 0; +tp_obj tp_object(TP) { + tp_obj self = tp_dict(tp); + self.dict.dtype |= 2; return self; } - - -/* -def ClassMeta_bind(klass,self): -*/ -tp_obj tp_has(TP,tp_obj self, tp_obj k) ; -void tp_ClassMeta_bind(TP,tp_obj klass,tp_obj self) { - int i; - - /* - if '__parent__' in klass: - ClassMeta_bind(klass.__parent__,self) - */ - - if (tp_has(tp,klass,tp_string("__parent__")).number.val) { - tp_ClassMeta_bind(tp,tp_get(tp,klass,tp_string("__parent__")),self); - } - - /* - for k in klass: - v = klass[k] - if istype(v,'fnc'): - self[k] = bind(v,self) - else: - self[k] = v - */ - - for (i=0; ilen; i++) { - int n = _tp_dict_next(tp,klass.dict.val); - tp_obj k = klass.dict.val->items[n].key; - tp_obj v = klass.dict.val->items[n].val; - if (v.type == TP_FNC) { - tp_set(tp,self,k,tp_fnc_new(tp,v.fnc.ftype|2,v.fnc.val,self,v.fnc.info->globals)); - } else { - tp_set(tp,self,k,v); - } - } -} -/* -def ClassMeta_call(klass,*p): -*/ -tp_obj tp_ClassMeta_call(TP) { +tp_obj tp_object_new(TP) { tp_obj klass = TP_TYPE(TP_DICT); - - /* - self = {} - ClassMeta_bind(klass,self) - if '__init__' in self: - self.__init__(*p) - return self - */ - tp_obj self = tp_dict(tp); - tp_ClassMeta_bind(tp,klass,self); - if (tp_has(tp,self,tp_string("__init__")).number.val) { - tp_call(tp,tp_get(tp,self,tp_string("__init__")),tp->params); + tp_obj self = tp_object(tp); + self.dict.val->meta = klass; + TP_META_BEGIN(self,"__init__"); + tp_call(tp,meta,tp->params); + TP_META_END; + return self; +} + +tp_obj tp_object_call(TP) { + tp_obj self; + if (tp->params.list.val->len) { + self = TP_TYPE(TP_DICT); + self.dict.dtype |= 2; + } else { + self = tp_object(tp); } return self; } +tp_obj tp_getraw(TP) { + tp_obj self = TP_TYPE(TP_DICT); + self.dict.dtype = 0; + return self; +} + +tp_obj tp_class(TP) { + tp_obj klass = tp_dict(tp); + klass.dict.val->meta = tp_get(tp,tp->builtins,tp_string("object")); + return klass; +} + Index: tinypy/gc.c =================================================================== --- tinypy/gc.c (revision 49) +++ tinypy/gc.c (working copy) @@ -30,7 +30,7 @@ tp_grey(tp,v.dict.val->items[n].key); tp_grey(tp,v.dict.val->items[n].val); } - tp_grey(tp,v.dict.val->meta); + tp_grey(tp,v.dict.val->meta); } if (type == TP_FNC) { tp_grey(tp,v.fnc.info->self);

meta41.patch

Phil Hassey

unread,
Jun 11, 2008, 1:42:52 PM6/11/08
to tin...@googlegroups.com
P.S. I learned something new about python today!

http://www.philhassey.com/blog/2008/06/11/learning-python-by-reinventing-wheels/


--- On Tue, 6/10/08, Phil Hassey <philh...@yahoo.com> wrote:
From: Phil Hassey <philh...@yahoo.com>
Subject: [tinypy] Re: Final meta patch
To: tin...@googlegroups.com
Date: Tuesday, June 10, 2008, 8:47 PM

I've patched the hole I alluded to in the previous post ...  I think the three "kinds" of dicts are now properly distinct.

Allefant - take a good look at this patch, I think it does everything you want, cleanly and quickly.

-Phil

--- On Tue, 6/10/08, Phil Hassey <philh...@yahoo.com> wrote:
From: Phil Hassey <philh...@yahoo.com>
Subject: [tinypy] Final meta patch
To: tin...@googlegroups.com
Date: Tuesday, June 10, 2008, 6:49 PM

Hey,

So .. here's another class / meta patch.  This one combines the "best of the best" from all the previous approaches.

- In python code, you can write really clean class interfaces (like in the python style patch).
- You also get a getmeta / setmeta function which you can use to change the "class" of an object.  Or even change the parent class of a class.  Or whatever.
- You can change methods on a class "real-time" and the objects will "reflect" those changes since methods are looked-up at request.
- Memory use is reduced for objects with dozens of methods as these methods aren't all created upon instantiation of an object.
- Speed increase is only 10%, which is quite good.
- Internally dicts can be in any of 3 states: "default" which will check its meta on a get.  "object" which will check its metas do various meta actions for you and will return found functions as methods.  "raw" which ignores its meta.  (There might be some bugs in my differentiation of "object" vs "default" .. these will have to be fixed.)

Feedback requested before I commit this.  I'm thinking this one is looking pretty good ...

-Phil




Index: tinypy/ops.c =================================================================== --- tinypy/ops.c (revision 49) +++ tinypy/ops.c (working copy) @@ -1,13 +1,3 @@ -#define TP_META_BEGIN(self,name) \ - if (self.dict.dtype && self.dict.val->meta.type != TP_NONE) { \ - int n = _tp_dict_find(tp,self.dict.val->meta.dict.val,tp_string(name)); \ - if (n != -1) { \ - tp_obj meta = self.dict.val->meta.dict.val->items[n].val; - -#define TP_META_END \ - } \ - } - tp_obj tp_str(TP,tp_obj self) { int type = self.type; if (type == TP_STRING) { return self; } @@ -80,8 +70,9 @@ tp_obj r; if (type == TP_DICT) { TP_META_BEGIN(self,"__get__"); - return tp_call(tp,meta,tp_params_v(tp,2,self,k)); + return tp_call(tp,meta,tp_params_v(tp,1,k)); TP_META_END; + if (self.dict.dtype && _tp_lookup(tp,self,k,&r)) { return r; } return _tp_dict_get(tp,self.dict.val,k,"tp_get"); } else if (type == TP_LIST) { if (k.type == TP_NUMBER) { @@ -176,7 +167,7 @@ if (type == TP_DICT) { TP_META_BEGIN(self,"__set__"); - tp_call(tp,meta,tp_params_v(tp,3,self,k,v)); + tp_call(tp,meta,tp_params_v(tp,2,k,v)); return; TP_META_END; _tp_dict_set(tp,self.dict.val,k,v); Index: tinypy/encode.py =================================================================== --- tinypy/encode.py (revision 49) +++ tinypy/encode.py (working copy) @@ -470,6 +470,7 @@ parent = None if items[0].type == 'name': name = items[0].val + parent = Token(tok.pos,'name','object') else: name = items[0].items[0].val parent = items[0].items[1] @@ -480,22 +481,15 @@ code(GSET,ts,kls) free_tmp(ts) #REG - if parent: - free_tmp(do(Token(tok.pos,'symbol','=',[ - Token(tok.pos,'get',None,[ - Token(tok.pos,'reg',kls), - Token(tok.pos,'string','__parent__')]), - parent]))) - + free_tmp(do(Token(tok.pos,'call',None,[ + Token(tok.pos,'name','setmeta'), + Token(tok.pos,'reg',kls), + parent]))) + for fc in items[1].items: if fc.type != 'def': continue do_def(fc,kls) - free_tmp(do(Token(tok.pos,'call',None,[ - Token(tok.pos,'name','setmeta'), - Token(tok.pos,'reg',kls), - Token(tok.pos,'name','ClassMeta')]))) - free_reg(kls) #REG Index: tinypy/tests.py =================================================================== --- tinypy/tests.py (revision 49) +++ tinypy/tests.py (working copy) @@ -442,13 +442,6 @@ """ ,"OK") - t_render(""" -class C: - def __init__(self,data): self.data = data - def print(self): print(self.data) -C("OK").print() -""" -,"OK") t_render(""" x = [v*v for v in range(0,5)] @@ -582,22 +575,6 @@ print(x) ""","OK") - t_render(""" -class X: - pass -y = X() -print("OK") -""","OK") - - t_render(""" -class X: pass -def test(): y = X() -test() -print("OK") -""","OK") - - t_render(["class X: pass\ndef test(): y = X()","import tmp1\ntmp1.test();print('OK')"],"OK") - t_render("print(len([1,2,3]))","3") t_render('if not "?" in "xyz": print("OK")',"OK") @@ -637,30 +614,8 @@ #t_render("def test(): print('OK')\n{'__call__':test}()","OK") - t_render(""" -class A: - def __init__(self): - self.a = 'O' - self.b = 'x' - def test(self): - print("KO") -class B(A): - def __init__(self): - A.__init__(self) - self.b = 'K' - def test(self): - print(self.a+self.b) -B().test() -""","OK") t_render(""" -class A: - def test(self): - print(self) -A.test("OK") -""","OK") - - t_render(""" def test(): def fnc(): print("OK") @@ -722,39 +677,28 @@ t_render(""" def get(self,k): return k+"K" -m = {"__get__":get} -v = {} -setmeta(v,m) +v = object() +v.__get__ = bind(get,v) print(v.O) -""" -,"OK") +""", +"OK") t_render(""" def set(self,k,v): self = getraw(self) self[k] = v + "K" -m = {"__set__":set} -v = {} -setmeta(v,m) +v = object() +v.__set__ = bind(set,v) v.x = "O" print(v.x) -""" -,"OK") - - t_render(""" -m = {"test":"OK"} -v = {} -setmeta(v,m) -print(getmeta(v)["test"]) """, "OK") t_render(""" def call(self,x): print(x) -m = {"__call__":call} -v = {} -setmeta(v,m) +v = object() +v.__call__ = bind(call,v) v("OK") """ ,"OK") @@ -769,24 +713,12 @@ print("OK") ""","OK") - meta_objs_init = """ -def MyObj_bind(klass,self): - if '__parent__' in klass: - MyObj_bind(klass.__parent__,self) - for k in klass: - v = klass[k] - if istype(v,'fnc'): - self[k] = bind(v,self) - else: - self[k] = v -def MyObj_call(klass,*p): - self = {} - MyObj_bind(klass,self) - if '__init__' in self: - self.__init__(*p) +def my_new(klass,*p): + self = object() + setmeta(self,klass) + self.__init__(*p) return self -MyObj = {'__call__':MyObj_call} def A_init(self,v): if v: print("A_init") @@ -794,16 +726,14 @@ print("A_test1") def A_test2(self): print("A_test2") -A = {'__init__':A_init,'test1':A_test1,'test2':A_test2} -setmeta(A,MyObj) +A = {'__new__':my_new,'__init__':A_init,'test1':A_test1,'test2':A_test2} def B_init(self,v): if v: print("B_init") def B_test2(self): print("B_test2") -B = {'__parent__':A,'__init__':B_init,'test2':B_test2} -setmeta(B,MyObj) - +B = {'__init__':B_init,'test2':B_test2} +setmeta(B,A) """ t_render(meta_objs_init+"""A(True)""","A_init") @@ -813,22 +743,85 @@ t_render(meta_objs_init+"""B(False).test1()""","A_test1") t_render(meta_objs_init+"""B(False).test2()""","B_test2") - #test that you can make a callable meta object + #various class construct use tests t_render(""" -class TestMeta: +class C: + def __init__(self,data): self.data = data + def print(self): print(self.data) +C("OK").print() +""" +,"OK") + + t_render(""" +class X: + pass +y = X() +print("OK") +""","OK") + + t_render(""" +class X: pass +def test(): y = X() +test() +print("OK") +""","OK") + + t_render(["class X: pass\ndef test(): y = X()","import tmp1\ntmp1.test();print('OK')"],"OK") + + t_render(""" +class A: + def __init__(self): + self.a = 'O' + self.b = 'x' + def test(self): + print("KO") +class B(A): + def __init__(self): + A.__init__(self) + self.b = 'K' + def test(self): + print(self.a+self.b) +B().test() +""","OK") + + t_render(""" +class A: + def test(self): + print(self) +A.test("OK") +""","OK") + + + #test that you can make a callable object + t_render(""" +class Test: + def __init__(self,v): + self.value = v def __call__(self): - self = getraw(self) print(self.value) -class Test: - def __init__(self,value): - self.value = value - setmeta(self,TestMeta) - x = Test('OK') x() ""","OK") + #test that you can use a __get__ + t_render(""" +class Test: + def __get__(self,k): + return k+"K" +x = Test() +print(x.O) +""","OK") + #test that you can use __set__ + t_render(""" +class Test: + def __set__(self,k,v): + getraw(self)[k] = "O"+v +x = Test() +x.v = "K" +print(x.v) +""","OK") + #test that exceptions are cleared after they are caught #and not repeated t_render(""" @@ -845,6 +838,35 @@ pass ""","OK") + #check that missing attributes throw an error + t_render(""" +class A: pass +try: + A().x +except: + print('OK') +""","OK") + + #check that a changed attribute gets changed + t_render(""" +class A: + def x(self): pass +a = A() +a.x = "OK" +print(a.x) +""","OK") + + #test that you can use a __get__ gets inherited + t_render(""" +class A: + def __get__(self,k): + return k+"K" +class B(A): + pass +x = B() +print(x.O) +""","OK") + ################################################################################ def t_boot(ss,ex,exact=True): @@ -969,11 +991,9 @@ /* create our class */ tp_obj tmp; - tp_obj A = tp_dict(tp); + tp_obj A = tp_class(tp); tp_set(tp,A,tp_string("__init__"),tp_fnc(tp,A_init)); tp_set(tp,A,tp_string("test"),tp_fnc(tp,A_test)); - tp_params_v(tp,2,A,tp_get(tp,tp->builtins,tp_string("ClassMeta"))); - tp_setmeta(tp); /* instantiate it and call test */ tmp = tp_call(tp,A,tp_params_v(tp,1,tp_string("OK"))); Index: tinypy/vm.c =================================================================== --- tinypy/vm.c (revision 49) +++ tinypy/vm.c (working copy) @@ -105,10 +105,16 @@ tp->params = params; if (self.type == TP_DICT) { - TP_META_BEGIN(self,"__call__"); - _tp_list_insert(tp,params.list.val,0,self); - return tp_call(tp,meta,params); - TP_META_END; + if (self.dict.dtype == 1) { + TP_META_BEGIN(self,"__new__"); + _tp_list_insert(tp,params.list.val,0,self); + return tp_call(tp,meta,params); + TP_META_END; + } else if (self.dict.dtype == 3) { + TP_META_BEGIN(self,"__call__"); + return tp_call(tp,meta,params); + TP_META_END; + } } if (self.type == TP_FNC && !(self.fnc.ftype&1)) { tp_obj r = _tp_tcall(tp,self); @@ -334,6 +340,7 @@ } void tp_builtins(TP) { + tp_obj o; struct {const char *s;void *f;} b[] = { {"print",tp_print}, {"range",tp_range}, {"min",tp_min}, {"max",tp_max}, {"bind",tp_bind}, {"copy",tp_copy}, @@ -343,19 +350,18 @@ {"load",tp_load}, {"fpack",tp_fpack}, {"abs",tp_abs}, {"int",tp_int}, {"exec",tp_exec_}, {"exists",tp_exists}, {"mtime",tp_mtime}, {"number",tp_float}, {"round",tp_round}, - {"ord",tp_ord}, {"merge",tp_merge}, {"setmeta",tp_setmeta}, - {"getraw",tp_getraw}, {"getmeta",tp_getmeta}, + {"ord",tp_ord}, {"merge",tp_merge}, {"getraw",tp_getraw}, + {"setmeta",tp_setmeta}, {"getmeta",tp_getmeta}, {0,0}, }; int i; for(i=0; b[i].s; i++) { tp_set(tp,tp->builtins,tp_string(b[i].s),tp_fnc(tp,(tp_obj (*)(tp_vm *))b[i].f)); } - /* - BUILTINS['ClassMeta'] = {'__call__':ClassMeta_call} - */ - tp_set(tp,tp->builtins,tp_string("ClassMeta"),tp_dict_n(tp,1,(tp_obj[]){tp_string("__call__"),tp_fnc(tp,tp_ClassMeta_call)})); - + o = tp_object(tp); + tp_set(tp,o,tp_string("__call__"),tp_fnc(tp,tp_object_call)); + tp_set(tp,o,tp_string("__new__"),tp_fnc(tp,tp_object_new)); + tp_set(tp,tp->builtins,tp_string("object"),o); } Index: tinypy/tp.h =================================================================== --- tinypy/tp.h (revision 49) +++ tinypy/tp.h (working copy) @@ -183,6 +183,7 @@ void tp_set(TP,tp_obj,tp_obj,tp_obj); tp_obj tp_get(TP,tp_obj,tp_obj); +tp_obj tp_has(TP,tp_obj self, tp_obj k); tp_obj tp_len(TP,tp_obj); tp_obj tp_str(TP,tp_obj); int tp_cmp(TP,tp_obj,tp_obj); Index: tinypy/builtins.c =================================================================== --- tinypy/builtins.c (revision 49) +++ tinypy/builtins.c (working copy) @@ -89,7 +89,8 @@ if (strcmp("list",t) == 0) { return tp_number(v.type == TP_LIST); } if (strcmp("dict",t) == 0) { return tp_number(v.type == TP_DICT); } if (strcmp("number",t) == 0) { return tp_number(v.type == TP_NUMBER); } - if (strcmp("fnc",t) == 0) { return tp_number(v.type == TP_FNC); } + if (strcmp("fnc",t) == 0) { return tp_number(v.type == TP_FNC && (v.fnc.ftype&2) == 0); } + if (strcmp("method",t) == 0) { return tp_number(v.type == TP_FNC && (v.fnc.ftype&2) != 0); } tp_raise(tp_None,"is_type(%s,%s)",TP_CSTR(v),t); } @@ -173,6 +174,29 @@ tp_raise(tp_None,"tp_mtime(%s)",s); } +int _tp_lookup(TP,tp_obj self, tp_obj k, tp_obj *meta) { + int n = _tp_dict_find(tp,self.dict.val,k); + if (n != -1) { + *meta = self.dict.val->items[n].val; + return 1; + } + if (self.dict.dtype && self.dict.val->meta.type == TP_DICT && _tp_lookup(tp,self.dict.val->meta,k,meta)) { + if (self.dict.dtype == 3 && meta->type == TP_FNC) { + *meta = tp_fnc_new(tp,meta->fnc.ftype|2,meta->fnc.val,self,meta->fnc.info->globals); + } + return 1; + } + return 0; +} + +#define TP_META_BEGIN(self,name) \ + if (self.dict.dtype) { \ + tp_obj meta; if (_tp_lookup(tp,self,tp_string(name),&meta)) { + +#define TP_META_END \ + } \ + } + tp_obj tp_setmeta(TP) { tp_obj self = TP_TYPE(TP_DICT); tp_obj meta = TP_TYPE(TP_DICT); @@ -185,68 +209,42 @@ return self.dict.val->meta; } -tp_obj tp_getraw(TP) { - tp_obj self = TP_TYPE(TP_DICT); - self.dict.dtype = 0; +tp_obj tp_object(TP) { + tp_obj self = tp_dict(tp); + self.dict.dtype |= 2; return self; } - - -/* -def ClassMeta_bind(klass,self): -*/ -tp_obj tp_has(TP,tp_obj self, tp_obj k) ; -void tp_ClassMeta_bind(TP,tp_obj klass,tp_obj self) { - int i; - - /* - if '__parent__' in klass: - ClassMeta_bind(klass.__parent__,self) - */ - - if (tp_has(tp,klass,tp_string("__parent__")).number.val) { - tp_ClassMeta_bind(tp,tp_get(tp,klass,tp_string("__parent__")),self); - } - - /* - for k in klass: - v = klass[k] - if istype(v,'fnc'): - self[k] = bind(v,self) - else: - self[k] = v - */ - - for (i=0; i len; i++) { - int n = _tp_dict_next(tp,klass.dict.val); - tp_obj k = klass.dict.val->items[n].key; - tp_obj v = klass.dict.val->items[n].val; - if (v.type == TP_FNC) { - tp_set(tp,self,k,tp_fnc_new(tp,v.fnc.ftype|2,v.fnc.val,self,v.fnc.info->globals)); - } else { - tp_set(tp,self,k,v); - } - } -} -/* -def ClassMeta_call(klass,*p): -*/ -tp_obj tp_ClassMeta_call(TP) { +tp_obj tp_object_new(TP) { tp_obj klass = TP_TYPE(TP_DICT); - - /* - self = {} - ClassMeta_bind(klass,self) - if '__init__' in self: - self.__init__(*p) - return self - */ - tp_obj self = tp_dict(tp); - tp_ClassMeta_bind(tp,klass,self); - if (tp_has(tp,self,tp_string("__init__")).number.val) { - tp_call(tp,tp_get(tp,self,tp_string("__init__")),tp->params); + tp_obj self = tp_object(tp); + self.dict.val->meta = klass; + TP_META_BEGIN(self,"__init__"); + tp_call(tp,meta,tp->params); + TP_META_END; + return self; +} + +tp_obj tp_object_call(TP) { + tp_obj self; + if (tp->params.list.val->len) { + self = TP_TYPE(TP_DICT); + self.dict.dtype |= 2; + } else { + self = tp_object(tp); } return self; } +tp_obj tp_getraw(TP) { + tp_obj self = TP_TYPE(TP_DICT); + self.dict.dtype = 0; + return self; +} + +tp_obj tp_class(TP) { + tp_obj klass = tp_dict(tp); + klass.dict.val->meta = tp_get(tp,tp->builtins,tp_string("object")); + return klass; +} + Index: tinypy/gc.c =================================================================== --- tinypy/gc.c (revision 49) +++ tinypy/gc.c (working copy) @@ -30,7 +30,7 @@ tp_grey(tp,v.dict.val->items[n].key); tp_grey(tp,v.dict.val->items[n].val); } - tp_grey(tp,v.dict.val->meta); + tp_grey(tp,v.dict.val->meta); } if (type == TP_FNC) { tp_grey(tp,v.fnc.info->self);



Index: tinypy/ops.c =================================================================== --- tinypy/ops.c (revision 49) +++ tinypy/ops.c (working copy) @@ -1,13 +1,3 @@ -#define TP_META_BEGIN(self,name) \ - if (self.dict.dtype && self.dict.val->meta.type != TP_NONE) { \ - int n = _tp_dict_find(tp,self.dict.val->meta.dict.val,tp_string(name)); \ - if (n != -1) { \ - tp_obj meta = self.dict.val->meta.dict.val->items[n].val; - -#define TP_META_END \ - } \ - } - tp_obj tp_str(TP,tp_obj self) { int type = self.type; if (type == TP_STRING) { return self; } @@ -80,8 +70,9 @@ tp_obj r; if (type == TP_DICT) { TP_META_BEGIN(self,"__get__"); - return tp_call(tp,meta,tp_params_v(tp,2,self,k)); + return tp_call(tp,meta,tp_params_v(tp,1,k)); TP_META_END; + if (self.dict.dtype && _tp_lookup(tp,self,k,&r)) { return r; } return _tp_dict_get(tp,self.dict.val,k,"tp_get"); } else if (type == TP_LIST) { if (k.type == TP_NUMBER) { @@ -176,7 +167,7 @@ if (type == TP_DICT) { TP_META_BEGIN(self,"__set__"); - tp_call(tp,meta,tp_params_v(tp,3,self,k,v)); + tp_call(tp,meta,tp_params_v(tp,2,k,v)); return; TP_META_END; _tp_dict_set(tp,self.dict.val,k,v); Index: tinypy/encode.py =================================================================== --- tinypy/encode.py (revision 49) +++ tinypy/encode.py (working copy) @@ -470,6 +470,7 @@ parent = None if items[0].type == 'name': name = items[0].val + parent = Token(tok.pos,'name','object') else: name = items[0].items[0].val parent = items[0].items[1] @@ -480,22 +481,15 @@ code(GSET,ts,kls) free_tmp(ts) #REG - if parent: - free_tmp(do(Token(tok.pos,'symbol','=',[ - Token(tok.pos,'get',None,[ - Token(tok.pos,'reg',kls), - Token(tok.pos,'string','__parent__')]), - parent]))) - + free_tmp(do(Token(tok.pos,'call',None,[ + Token(tok.pos,'name','setmeta'), + Token(tok.pos,'reg',kls), + parent]))) + for fc in items[1].items: if fc.type != 'def': continue do_def(fc,kls) - free_tmp(do(Token(tok.pos,'call',None,[ - Token(tok.pos,'name','setmeta'), - Token(tok.pos,'reg',kls), - Token(tok.pos,'name','ClassMeta')]))) - free_reg(kls) #REG Index: tinypy/tests.py =================================================================== --- tinypy/tests.py (revision 49) +++ tinypy/tests.py (working copy) @@ -442,13 +442,6 @@ """ ,"OK") - t_render(""" -class C: - def __init__(self,data): self.data = data - def print(self): print(self.data) -C("OK").print() -""" -,"OK") t_render(""" x = [v*v for v in range(0,5)] @@ -582,22 +575,6 @@ print(x) ""","OK") - t_render(""" -class X: - pass -y = X() -print("OK") -""","OK") - - t_render(""" -class X: pass -def test(): y = X() -test() -print("OK") -""","OK") - - t_render(["class X: pass\ndef test(): y = X()","import tmp1\ntmp1.test();print('OK')"],"OK") - t_render("print(len([1,2,3]))","3") t_render('if not "?" in "xyz": print("OK")',"OK") @@ -637,30 +614,8 @@ #t_render("def test(): print('OK')\n{'__call__':test}()","OK") - t_render(""" -class A: - def __init__(self): - self.a = 'O' - self.b = 'x' - def test(self): - print("KO") -class B(A): - def __init__(self): - A.__init__(self) - self.b = 'K' - def test(self): - print(self.a+self.b) -B().test() -""","OK") t_render(""" -class A: - def test(self): - print(self) -A.test("OK") -""","OK") - - t_render(""" def test(): def fnc(): print("OK") @@ -722,39 +677,28 @@ t_render(""" def get(self,k): return k+"K" -m = {"__get__":get} -v = {} -setmeta(v,m) +v = object() +v.__get__ = bind(get,v) print(v.O) -""" -,"OK") +""", +"OK") t_render(""" def set(self,k,v): self = getraw(self) self[k] = v + "K" -m = {"__set__":set} -v = {} -setmeta(v,m) +v = object() +v.__set__ = bind(set,v) v.x = "O" print(v.x) -""" -,"OK") - - t_render(""" -m = {"test":"OK"} -v = {} -setmeta(v,m) -print(getmeta(v)["test"]) """, "OK") t_render(""" def call(self,x): print(x) -m = {"__call__":call} -v = {} -setmeta(v,m) +v = object() +v.__call__ = bind(call,v) v("OK") """ ,"OK") @@ -769,24 +713,12 @@ print("OK") ""","OK") - meta_objs_init = """ -def MyObj_bind(klass,self): - if '__parent__' in klass: - MyObj_bind(klass.__parent__,self) - for k in klass: - v = klass[k] - if istype(v,'fnc'): - self[k] = bind(v,self) - else: - self[k] = v -def MyObj_call(klass,*p): - self = {} - MyObj_bind(klass,self) - if '__init__' in self: - self.__init__(*p) +def my_new(klass,*p): + self = object() + setmeta(self,klass) + self.__init__(*p) return self -MyObj = {'__call__':MyObj_call} def A_init(self,v): if v: print("A_init") @@ -794,16 +726,14 @@ print("A_test1") def A_test2(self): print("A_test2") -A = {'__init__':A_init,'test1':A_test1,'test2':A_test2} -setmeta(A,MyObj) +A = {'__new__':my_new,'__init__':A_init,'test1':A_test1,'test2':A_test2} def B_init(self,v): if v: print("B_init") def B_test2(self): print("B_test2") -B = {'__parent__':A,'__init__':B_init,'test2':B_test2} -setmeta(B,MyObj) - +B = {'__init__':B_init,'test2':B_test2} +setmeta(B,A) """ t_render(meta_objs_init+"""A(True)""","A_init") @@ -813,22 +743,85 @@ t_render(meta_objs_init+"""B(False).test1()""","A_test1") t_render(meta_objs_init+"""B(False).test2()""","B_test2") - #test that you can make a callable meta object + #various class construct use tests t_render(""" -class TestMeta: +class C: + def __init__(self,data): self.data = data + def print(self): print(self.data) +C("OK").print() +""" +,"OK") + + t_render(""" +class X: + pass +y = X() +print("OK") +""","OK") + + t_render(""" +class X: pass +def test(): y = X() +test() +print("OK") +""","OK") + + t_render(["class X: pass\ndef test(): y = X()","import tmp1\ntmp1.test();print('OK')"],"OK") + + t_render(""" +class A: + def __init__(self): + self.a = 'O' + self.b = 'x' + def test(self): + print("KO") +class B(A): + def __init__(self): + A.__init__(self) + self.b = 'K' + def test(self): + print(self.a+self.b) +B().test() +""","OK") + + t_render(""" +class A: + def test(self): + print(self) +A.test("OK") +""","OK") + + + #test that you can make a callable object + t_render(""" +class Test: + def __init__(self,v): + self.value = v def __call__(self): - self = getraw(self) print(self.value) -class Test: - def __init__(self,value): - self.value = value - setmeta(self,TestMeta) - x = Test('OK') x() ""","OK") + #test that you can use a __get__ + t_render(""" +class Test: + def __get__(self,k): + return k+"K" +x = Test() +print(x.O) +""","OK") + #test that you can use __set__ + t_render(""" +class Test: + def __set__(self,k,v): + getraw(self)[k] = "O"+v +x = Test() +x.v = "K" +print(x.v) +""","OK") + #test that exceptions are cleared after they are caught #and not repeated t_render(""" @@ -845,6 +838,60 @@ pass ""","OK") + #check that missing attributes throw an error + t_render(""" +class A: pass +try: + A().x +except: + print('OK') +""","OK") + + #check that a changed attribute gets changed + t_render(""" +class A: + def x(self): pass +a = A() +a.x = "OK" +print(a.x) +""","OK") + + #test that you can use a __get__ gets inherited + t_render(""" +class A: + def __get__(self,k): + return k+"K" +class B(A): + pass +x = B() +print(x.O) +""","OK") + + #test that meta methods aren't called on non-objects + t_render(""" +def get(): pass +x = {"__get__":get} +try: + z = x.y +except: + print("OK") +""","OK") + + #test that meta stuff is inheritited in dicts + t_render(""" +x = {1:"O"} +y = {2:"K"} +setmeta(y,x) +print(y[1]+y[2]) +""","OK") + + #test that meta stuff doesn't change into methods in dicts + t_render(""" +def get(k): return k +x = {"get":get} +print(x.get("OK")) +""","OK") + ################################################################################ def t_boot(ss,ex,exact=True): @@ -969,11 +1016,9 @@ /* create our class */ tp_obj tmp; - tp_obj A = tp_dict(tp); + tp_obj A = tp_class(tp); tp_set(tp,A,tp_string("__init__"),tp_fnc(tp,A_init)); tp_set(tp,A,tp_string("test"),tp_fnc(tp,A_test)); - tp_params_v(tp,2,A,tp_get(tp,tp->builtins,tp_string("ClassMeta"))); - tp_setmeta(tp); /* instantiate it and call test */ tmp = tp_call(tp,A,tp_params_v(tp,1,tp_string("OK"))); Index: tinypy/vm.c =================================================================== --- tinypy/vm.c (revision 49) +++ tinypy/vm.c (working copy) @@ -105,10 +105,16 @@ tp->params = params; if (self.type == TP_DICT) { - TP_META_BEGIN(self,"__call__"); - _tp_list_insert(tp,params.list.val,0,self); - return tp_call(tp,meta,params); - TP_META_END; + if (self.dict.dtype == 1) { + tp_obj meta; if (_tp_lookup(tp,self,tp_string("__new__"),&meta)) { + _tp_list_insert(tp,params.list.val,0,self); + return tp_call(tp,meta,params); + } + } else if (self.dict.dtype == 2) { + TP_META_BEGIN(self,"__call__"); + return tp_call(tp,meta,params); + TP_META_END; + } } if (self.type == TP_FNC && !(self.fnc.ftype&1)) { tp_obj r = _tp_tcall(tp,self); @@ -334,6 +340,7 @@ } void tp_builtins(TP) { + tp_obj o; struct {const char *s;void *f;} b[] = { {"print",tp_print}, {"range",tp_range}, {"min",tp_min}, {"max",tp_max}, {"bind",tp_bind}, {"copy",tp_copy}, @@ -343,19 +350,18 @@ {"load",tp_load}, {"fpack",tp_fpack}, {"abs",tp_abs}, {"int",tp_int}, {"exec",tp_exec_}, {"exists",tp_exists}, {"mtime",tp_mtime}, {"number",tp_float}, {"round",tp_round}, - {"ord",tp_ord}, {"merge",tp_merge}, {"setmeta",tp_setmeta}, - {"getraw",tp_getraw}, {"getmeta",tp_getmeta}, + {"ord",tp_ord}, {"merge",tp_merge}, {"getraw",tp_getraw}, + {"setmeta",tp_setmeta}, {"getmeta",tp_getmeta}, {0,0}, }; int i; for(i=0; b[i].s; i++) { tp_set(tp,tp->builtins,tp_string(b[i].s),tp_fnc(tp,(tp_obj (*)(tp_vm *))b[i].f)); } - /* - BUILTINS['ClassMeta'] = {'__call__':ClassMeta_call} - */ - tp_set(tp,tp->builtins,tp_string("ClassMeta"),tp_dict_n(tp,1,(tp_obj[]){tp_string("__call__"),tp_fnc(tp,tp_ClassMeta_call)})); - + o = tp_object(tp); + tp_set(tp,o,tp_string("__call__"),tp_fnc(tp,tp_object_call)); + tp_set(tp,o,tp_string("__new__"),tp_fnc(tp,tp_object_new)); + tp_set(tp,tp->builtins,tp_string("object"),o); } Index: tinypy/tp.h =================================================================== --- tinypy/tp.h (revision 49) +++ tinypy/tp.h (working copy) @@ -183,6 +183,7 @@ void tp_set(TP,tp_obj,tp_obj,tp_obj); tp_obj tp_get(TP,tp_obj,tp_obj); +tp_obj tp_has(TP,tp_obj self, tp_obj k); tp_obj tp_len(TP,tp_obj); tp_obj tp_str(TP,tp_obj); int tp_cmp(TP,tp_obj,tp_obj); Index: tinypy/builtins.c =================================================================== --- tinypy/builtins.c (revision 49) +++ tinypy/builtins.c (working copy) @@ -89,7 +89,8 @@ if (strcmp("list",t) == 0) { return tp_number(v.type == TP_LIST); } if (strcmp("dict",t) == 0) { return tp_number(v.type == TP_DICT); } if (strcmp("number",t) == 0) { return tp_number(v.type == TP_NUMBER); } - if (strcmp("fnc",t) == 0) { return tp_number(v.type == TP_FNC); } + if (strcmp("fnc",t) == 0) { return tp_number(v.type == TP_FNC && (v.fnc.ftype&2) == 0); } + if (strcmp("method",t) == 0) { return tp_number(v.type == TP_FNC && (v.fnc.ftype&2) != 0); } tp_raise(tp_None,"is_type(%s,%s)",TP_CSTR(v),t); } @@ -173,6 +174,29 @@ tp_raise(tp_None,"tp_mtime(%s)",s); } +int _tp_lookup(TP,tp_obj self, tp_obj k, tp_obj *meta) { + int n = _tp_dict_find(tp,self.dict.val,k); + if (n != -1) { + *meta = self.dict.val->items[n].val; + return 1; + } + if (self.dict.dtype && self.dict.val->meta.type == TP_DICT && _tp_lookup(tp,self.dict.val->meta,k,meta)) { + if (self.dict.dtype == 2 && meta->type == TP_FNC) { + *meta = tp_fnc_new(tp,meta->fnc.ftype|2,meta->fnc.val,self,meta->fnc.info->globals); + } + return 1; + } + return 0; +} + +#define TP_META_BEGIN(self,name) \ + if (self.dict.dtype == 2) { \ + tp_obj meta; if (_tp_lookup(tp,self,tp_string(name),&meta)) { + +#define TP_META_END \ + } \ + } + tp_obj tp_setmeta(TP) { tp_obj self = TP_TYPE(TP_DICT); tp_obj meta = TP_TYPE(TP_DICT); @@ -185,68 +209,42 @@ return self.dict.val->meta; } -tp_obj tp_getraw(TP) { - tp_obj self = TP_TYPE(TP_DICT); - self.dict.dtype = 0; +tp_obj tp_object(TP) { + tp_obj self = tp_dict(tp); + self.dict.dtype = 2; return self; } - - -/* -def ClassMeta_bind(klass,self): -*/ -tp_obj tp_has(TP,tp_obj self, tp_obj k) ; -void tp_ClassMeta_bind(TP,tp_obj klass,tp_obj self) { - int i; - - /* - if '__parent__' in klass: - ClassMeta_bind(klass.__parent__,self) - */ - - if (tp_has(tp,klass,tp_string("__parent__")).number.val) { - tp_ClassMeta_bind(tp,tp_get(tp,klass,tp_string("__parent__")),self); - } - - /* - for k in klass: - v = klass[k] - if istype(v,'fnc'): - self[k] = bind(v,self) - else: - self[k] = v - */ - - for (i=0; ilen; i++) { - int n = _tp_dict_next(tp,klass.dict.val); - tp_obj k = klass.dict.val->items[n].key; - tp_obj v = klass.dict.val->items[n].val; - if (v.type == TP_FNC) { - tp_set(tp,self,k,tp_fnc_new(tp,v.fnc.ftype|2,v.fnc.val,self,v.fnc.info->globals)); - } else { - tp_set(tp,self,k,v); - } - } -} -/* -def ClassMeta_call(klass,*p): -*/ -tp_obj tp_ClassMeta_call(TP) { +tp_obj tp_object_new(TP) { tp_obj klass = TP_TYPE(TP_DICT); - - /* - self = {} - ClassMeta_bind(klass,self) - if '__init__' in self: - self.__init__(*p) - return self - */ - tp_obj self = tp_dict(tp); - tp_ClassMeta_bind(tp,klass,self); - if (tp_has(tp,self,tp_string("__init__")).number.val) { - tp_call(tp,tp_get(tp,self,tp_string("__init__")),tp->params); + tp_obj self = tp_object(tp); + self.dict.val->meta = klass; + TP_META_BEGIN(self,"__init__"); + tp_call(tp,meta,tp->params); + TP_META_END; + return self; +} + +tp_obj tp_object_call(TP) { + tp_obj self; + if (tp->params.list.val->len) { + self = TP_TYPE(TP_DICT); + self.dict.dtype = 2; + } else { + self = tp_object(tp); } return self; } +tp_obj tp_getraw(TP) { + tp_obj self = TP_TYPE(TP_DICT); + self.dict.dtype = 0; + return self; +} + +tp_obj tp_class(TP) { + tp_obj klass = tp_dict(tp); + klass.dict.val->meta = tp_get(tp,tp->builtins,tp_string("object")); + return klass; +} + Index: tinypy/gc.c =================================================================== --- tinypy/gc.c (revision 49) +++ tinypy/gc.c (working copy) @@ -30,7 +30,7 @@ tp_grey(tp,v.dict.val->items[n].key); tp_grey(tp,v.dict.val->items[n].val); } - tp_grey(tp,v.dict.val->meta); + tp_grey(tp,v.dict.val->meta); } if (type == TP_FNC) { tp_grey(tp,v.fnc.info->self);

allefant

unread,
Jun 12, 2008, 12:24:55 PM6/12/08
to tinypy
On Jun 11, 4:47 am, Phil Hassey <philhas...@yahoo.com> wrote:
> I've patched the hole I alluded to in the previous post ...&nbsp; I think the three "kinds" of dicts are now properly distinct.
>
> Allefant - take a good look at this patch, I think it does everything you want, cleanly and quickly.
>

Yes, this is just what I wanted :) I.e., Python style run-time
lookup. But I think, I shouldn't be the one to approve it or not,
others here most likely know a lot more about Python and scripting
languages. But then, I guess being closer to Python generally will be
considered good :)

Btw., What did you change to have it work faster? Looking at the
patch, there's apparently no trick like caching anything involved this
time..

About the setmeta, do I understand this right..
- for a class, it will set the parent class
- for an object, it will set the class
?

In which case, it seems to be an ideal solution. I think I remember
that someone already hinted about a solution like this at some point,
when asking about the difference of __class__ and __parent__...


PS: Replying to this post over the web interface was quite hard,
because it's in the middle of two 10 page posts.. see here:
http://groups.google.com/group/tinypy/browse_thread/thread/6fffa1ceca14a3cf
:P

Phil Hassey

unread,
Jun 13, 2008, 1:08:49 AM6/13/08
to tin...@googlegroups.com
Hmn, maybe you could file a complaint with google about how the google groups web interface works?  Or subscribe as a mailing list?

In my previous python style patch, the class / parent were actually members of the dictionary, so all lookups took 2-3x as many checks to resolve.  By having a "meta" variable in the dict structure, it cuts out a lot of nonsense.

-Phil

--- On Thu, 6/12/08, allefant <alle...@gmail.com> wrote:
From: allefant <alle...@gmail.com>
Subject: [tinypy] Re: Final meta patch

illume

unread,
Jun 17, 2008, 9:50:31 PM6/17/08
to tinypy
How about using the __class__ assignment like how python does it?
Rather than a different set/get?


On Jun 13, 3:08 pm, Phil Hassey <philhas...@yahoo.com> wrote:
> Hmn, maybe you could file a complaint with google about how the google groups web interface works?&nbsp; Or subscribe as a mailing list?
>
> In my previous python style patch, the class / parent were actually members of the dictionary, so all lookups took 2-3x as many checks to resolve.&nbsp; By having a "meta" variable in the dict structure, it cuts out a lot of nonsense.
>
> -Phil
Reply all
Reply to author
Forward
0 new messages