From 04691fc1c1bb737c0db772f5c1ea697a351a01d9 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 12 Aug 1992 15:35:34 +0000 Subject: [PATCH] Changes so that user-defined classes can implement operations invoked by special syntax: you can now define your own numbers, sequences and mappings. --- Include/ceval.h | 6 +- Include/classobject.h | 3 + Objects/classobject.c | 501 +++++++++++++++++++++++++++++++++++++++++- Python/bltinmodule.c | 35 ++- Python/ceval.c | 106 ++++++--- 5 files changed, 609 insertions(+), 42 deletions(-) diff --git a/Include/ceval.h b/Include/ceval.h index d9708a2d32c..dc3125588bc 100644 --- a/Include/ceval.h +++ b/Include/ceval.h @@ -77,13 +77,13 @@ void flushline PROTO((void)); */ extern void init_save_thread PROTO((void)); -extern void *save_thread PROTO((void)); -extern void restore_thread PROTO((void *)); +extern object *save_thread PROTO((void)); +extern void restore_thread PROTO((object *)); #ifdef USE_THREAD #define BGN_SAVE { \ - void *_save; \ + object *_save; \ _save = save_thread(); #define RET_SAVE restore_thread(_save); #define RES_SAVE _save = save_thread(); diff --git a/Include/classobject.h b/Include/classobject.h index 7dd66e791c6..1d24a7c86a7 100644 --- a/Include/classobject.h +++ b/Include/classobject.h @@ -42,3 +42,6 @@ extern object *newinstancemethodobject PROTO((object *, object *)); extern object *instancemethodgetfunc PROTO((object *)); extern object *instancemethodgetself PROTO((object *)); + +extern int instance_coerce PROTO((object **, object **)); +extern object *instance_convert PROTO((object *, char *)); diff --git a/Objects/classobject.c b/Objects/classobject.c index ed0e6f4cc7a..40fe0c9b34f 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -25,8 +25,12 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* Class object implementation */ #include "allobjects.h" - +#include "modsupport.h" #include "structmember.h" +#include "ceval.h" + +extern typeobject MappingInstancetype; +extern typeobject SequenceInstancetype; typedef struct { OB_HEAD @@ -166,6 +170,7 @@ newinstanceobject(class) register object *class; { register instanceobject *inst; + object *v; if (!is_classobject(class)) { err_badcall(); return NULL; @@ -246,6 +251,428 @@ instance_setattr(inst, name, v) return dictinsert(inst->in_attr, name, v); } +int +instance_print(inst, fp, flags) + instanceobject *inst; + FILE *fp; + int flags; +{ + object *func, *repr; + int ret; + + func = instance_getattr(inst, "__repr__"); + if (func == NULL) { + err_clear(); + fprintf(fp, "", (long)inst); + return 0; + } + repr = call_object(func, (object *)NULL); + DECREF(func); + if (repr == NULL) + return -1; + ret = printobject(repr, fp, flags | PRINT_RAW); + DECREF(repr); + return ret; +} + +object * +instance_repr(inst) + instanceobject *inst; +{ + object *func; + object *res; + + func = instance_getattr(inst, "__repr__"); + if (func == NULL) { + char buf[80]; + err_clear(); + sprintf(buf, "", (long)inst); + return newstringobject(buf); + } + res = call_object(func, (object *)NULL); + DECREF(func); + return res; +} + +int +instance_compare(inst, other) + instanceobject *inst, *other; +{ + object *func; + object *res; + int outcome; + + func = instance_getattr(inst, "__cmp__"); + if (func == NULL) { + err_clear(); + if (inst < other) + return -1; + if (inst > other) + return 1; + return 0; + } + res = call_object(func, (object *)other); + DECREF(func); + if (res == NULL) { + err_clear(); /* XXX Should report the error, bot how...??? */ + return 0; + } + if (is_intobject(res)) + outcome = getintvalue(res); + else + outcome = 0; /* XXX Should report the error, bot how...??? */ + DECREF(res); + return outcome; +} + +int +instance_length(inst) + instanceobject *inst; +{ + object *func; + object *res; + int outcome; + + func = instance_getattr(inst, "__len__"); + if (func == NULL) + return -1; + res = call_object(func, (object *)NULL); + DECREF(func); + if (is_intobject(res)) { + outcome = getintvalue(res); + if (outcome < 0) + err_setstr(ValueError, "__len__() should return >= 0"); + } + else { + err_setstr(TypeError, "__len__() should return an int"); + outcome = -1; + } + DECREF(res); + return outcome; +} + +object * +instance_subscript(inst, key) + instanceobject *inst; + object *key; +{ + object *func; + object *arg; + object *res; + + func = instance_getattr(inst, "__getitem__"); + if (func == NULL) + return NULL; + arg = mkvalue("(O)", key); + if (arg == NULL) { + DECREF(func); + return NULL; + } + res = call_object(func, arg); + DECREF(func); + DECREF(arg); + return res; +} + +int +instance_ass_subscript(inst, key, value) + instanceobject*inst; + object *key; + object *value; +{ + object *func; + object *arg; + object *res; + + if (value == NULL) + func = instance_getattr(inst, "__delitem__"); + else + func = instance_getattr(inst, "__setitem__"); + if (func == NULL) + return -1; + if (value == NULL) + arg = mkvalue("(O)", key); + else + arg = mkvalue("(OO)", key, value); + if (arg == NULL) { + DECREF(func); + return NULL; + } + res = call_object(func, arg); + DECREF(func); + DECREF(arg); + if (res == NULL) + return -1; + DECREF(res); + return 0; +} + +mapping_methods instance_as_mapping = { + instance_length, /*mp_length*/ + instance_subscript, /*mp_subscript*/ + instance_ass_subscript, /*mp_ass_subscript*/ +}; + +static object * +instance_concat(inst, other) + instanceobject *inst, *other; +{ + object *func, *res; + + func = instance_getattr(inst, "__add__"); + if (func == NULL) + return NULL; + res = call_object(func, (object *)other); + DECREF(func); + return res; +} + +static object * +instance_repeat(inst, count) + instanceobject *inst; + int count; +{ + object *func, *arg, *res; + + func = instance_getattr(inst, "__mul__"); + if (func == NULL) + return NULL; + arg = newintobject((long)count); + if (arg == NULL) { + DECREF(func); + return NULL; + } + res = call_object(func, arg); + DECREF(func); + DECREF(arg); + return res; +} + +static object * +instance_item(inst, i) + instanceobject *inst; + int i; +{ + object *func, *arg, *res; + + func = instance_getattr(inst, "__getitem__"); + if (func == NULL) + return NULL; + arg = newintobject((long)i); + if (arg == NULL) { + DECREF(func); + return NULL; + } + res = call_object(func, arg); + DECREF(func); + DECREF(arg); + return res; +} + +static object * +instance_slice(inst, i, j) + instanceobject *inst; + int i, j; +{ + object *func, *arg, *res; + + func = instance_getattr(inst, "__getslice__"); + if (func == NULL) + return NULL; + arg = mkvalue("(ii)", i, j); + if (arg == NULL) { + DECREF(func); + return NULL; + } + res = call_object(func, arg); + DECREF(func); + DECREF(arg); + return res; +} + +static int +instance_ass_item(inst, i, item) + instanceobject *inst; + int i; + object *item; +{ + object *func, *arg, *res; + + if (item == NULL) + func = instance_getattr(inst, "__delitem__"); + else + func = instance_getattr(inst, "__setitem__"); + if (func == NULL) + return NULL; + if (item == NULL) + arg = mkvalue("i", i); + else + arg = mkvalue("(iO)", i, item); + if (arg == NULL) { + DECREF(func); + return NULL; + } + res = call_object(func, arg); + DECREF(func); + DECREF(arg); + if (res == NULL) + return -1; + DECREF(res); + return 0; +} + +static int +instance_ass_slice(inst, i, j, value) + instanceobject *inst; + int i, j; + object *value; +{ + object *func, *arg, *res; + + if (value == NULL) + func = instance_getattr(inst, "__delslice__"); + else + func = instance_getattr(inst, "__setslice__"); + if (func == NULL) + return NULL; + if (value == NULL) + arg = mkvalue("(ii)", i, j); + else + arg = mkvalue("(iiO)", i, j, value); + if (arg == NULL) { + DECREF(func); + return NULL; + } + res = call_object(func, arg); + DECREF(func); + DECREF(arg); + if (res == NULL) + return -1; + DECREF(res); + return 0; +} + +static sequence_methods instance_as_sequence = { + instance_length, /*sq_length*/ + instance_concat, /*sq_concat*/ + instance_repeat, /*sq_repeat*/ + instance_item, /*sq_item*/ + instance_slice, /*sq_slice*/ + instance_ass_item, /*sq_ass_item*/ + instance_ass_slice, /*sq_ass_slice*/ +}; + +static object * +generic_binary_op(self, other, methodname) + instanceobject *self; + object *other; + char *methodname; +{ + object *func, *res; + + if ((func = instance_getattr(self, methodname)) == NULL) + return NULL; + res = call_object(func, other); + DECREF(func); + return res; +} + +static object * +generic_unary_op(self, methodname) + instanceobject *self; + char *methodname; +{ + object *func, *res; + + if ((func = instance_getattr(self, methodname)) == NULL) + return NULL; + res = call_object(func, (object *)NULL); + DECREF(func); + return res; +} + +#define BINARY(funcname, methodname) \ +static object * funcname(self, other) instanceobject *self; object *other; { \ + return generic_binary_op(self, other, methodname); \ +} + +#define UNARY(funcname, methodname) \ +static object *funcname(self) instanceobject *self; { \ + return generic_unary_op(self, methodname); \ +} + +BINARY(instance_add, "__add__") +BINARY(instance_sub, "__sub__") +BINARY(instance_mul, "__mul__") +BINARY(instance_div, "__div__") +BINARY(instance_mod, "__mod__") +BINARY(instance_divmod, "__divmod__") +BINARY(instance_pow, "__pow__") +UNARY(instance_neg, "__neg__") +UNARY(instance_pos, "__pos__") +UNARY(instance_abs, "__abs__") + +int +instance_nonzero(self) + instanceobject *self; +{ + object *func, *res; + long outcome; + + if ((func = instance_getattr(self, "__len__")) == NULL) { + err_clear(); + if ((func = instance_getattr(self, "__nonzero__")) == NULL) { + err_clear(); + /* Fall back to the default behavior: + all instances are nonzero */ + return 1; + } + } + res = call_object(func, (object *)NULL); + DECREF(func); + if (res == NULL) + return -1; + if (!is_intobject(res)) { + DECREF(res); + err_setstr(TypeError, "__nonzero__ should return an int"); + return -1; + } + outcome = getintvalue(res); + DECREF(res); + if (outcome < 0) { + err_setstr(ValueError, "__nonzero__ should return >= 0"); + return -1; + } + return outcome > 0; +} + +UNARY(instance_invert, "__invert__") +BINARY(instance_lshift, "__lshift__") +BINARY(instance_rshift, "__rshift__") +BINARY(instance_and, "__and__") +BINARY(instance_xor, "__xor__") +BINARY(instance_or, "__or__") + +static number_methods instance_as_number = { + instance_add, /*nb_add*/ + instance_sub, /*nb_subtract*/ + instance_mul, /*nb_multiply*/ + instance_div, /*nb_divide*/ + instance_mod, /*nb_remainder*/ + instance_divmod, /*nb_divmod*/ + instance_pow, /*nb_power*/ + instance_neg, /*nb_negative*/ + instance_pos, /*nb_positive*/ + instance_abs, /*nb_absolute*/ + instance_nonzero, /*nb_nonzero*/ + instance_invert, /*nb_invert*/ + instance_lshift, /*nb_lshift*/ + instance_rshift, /*nb_rshift*/ + instance_and, /*nb_and*/ + instance_xor, /*nb_xor*/ + instance_or, /*nb_or*/ +}; + typeobject Instancetype = { OB_HEAD_INIT(&Typetype) 0, @@ -253,16 +680,76 @@ typeobject Instancetype = { sizeof(instanceobject), 0, instance_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ + instance_print, /*tp_print*/ instance_getattr, /*tp_getattr*/ instance_setattr, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ + instance_compare, /*tp_compare*/ + instance_repr, /*tp_repr*/ + &instance_as_number, /*tp_as_number*/ + &instance_as_sequence, /*tp_as_sequence*/ + &instance_as_mapping, /*tp_as_mapping*/ }; +static int +one_coerce(pv, pw) + object **pv, **pw; +{ + object *v = *pv; + object *w = *pw; + object *func; + + if (!is_instanceobject(v)) + return 1; + func = instance_getattr((instanceobject *)v, "__coerce__"); + if (func == NULL) { + err_clear(); + return 1; + } + if (func != NULL) { + object *res = call_object(func, w); + int outcome; + if (res == NULL) + return -1; + outcome = getargs(res, "(OO)", &v, &w); + if (!outcome || v->ob_type != w->ob_type || + v->ob_type->tp_as_number == NULL) { + DECREF(res); + err_setstr(TypeError, "bad __coerce__ result"); + return -1; + } + INCREF(v); + INCREF(w); + DECREF(res); + *pv = v; + *pw = w; + return 0; + } +} + +int +instance_coerce(pv, pw) + object **pv, **pw; +{ + int outcome; + outcome = one_coerce(pv, pw); + if (outcome > 0) { + outcome = one_coerce(pw, pv); + if (outcome > 0) { + err_setstr(TypeError, "uncoerceable instance"); + outcome = -1; + } + } + return outcome; +} + +object * +instance_convert(inst, methodname) + object *inst; + char *methodname; +{ + return generic_unary_op((instanceobject *)inst, methodname); +} + /* And finally, here are instance method objects */ diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index ca5043d764b..cce19e1ed84 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -84,6 +84,24 @@ builtin_chr(self, v) return newsizedstringobject(s, 1); } +static object * +builtin_coerce(self, args) + object *self; + object *args; +{ + object *v, *w; + object *res; + + if (!getargs(args, "(OO)", &v, &w)) + return NULL; + if (coerce(&v, &w) < 0) + return NULL; + res = mkvalue("(OO)", v, w); + DECREF(v); + DECREF(w); + return res; +} + static object * builtin_dir(self, v) object *self; @@ -250,6 +268,9 @@ builtin_float(self, v) INCREF(v); return v; } + else if (is_instanceobject(v)) { + return instance_convert(v, "__float__"); + } err_setstr(TypeError, "float() argument must be int, long or float"); return NULL; } @@ -359,6 +380,9 @@ builtin_int(self, v) /* XXX should check for overflow */ return newintobject((long)x); } + else if (is_instanceobject(v)) { + return instance_convert(v, "__int__"); + } err_setstr(TypeError, "int() argument must be int, long or float"); return NULL; } @@ -385,7 +409,10 @@ builtin_len(self, v) err_setstr(TypeError, "len() of unsized object"); return NULL; } - return newintobject(len); + if (len < 0) + return NULL; + else + return newintobject(len); } static object * @@ -407,6 +434,9 @@ builtin_long(self, v) double x = getfloatvalue(v); return dnewlongobject(x); } + else if (is_instanceobject(v)) { + return instance_convert(v, "__long__"); + } err_setstr(TypeError, "long() argument must be int, long or float"); return NULL; } @@ -648,6 +678,7 @@ static struct methodlist builtin_methods[] = { {"abs", builtin_abs}, {"apply", builtin_apply}, {"chr", builtin_chr}, + {"coerce", builtin_coerce}, {"dir", builtin_dir}, {"divmod", builtin_divmod}, {"eval", builtin_eval}, @@ -766,6 +797,8 @@ coerce(pv, pw) INCREF(w); return 0; } + if (is_instanceobject(v) || is_instanceobject(w)) + return instance_coerce(pv, pw); if (v->ob_type->tp_as_number == NULL || w->ob_type->tp_as_number == NULL) { err_setstr(TypeError, "mixing number and non-number"); diff --git a/Python/ceval.c b/Python/ceval.c index 252b1dcdcce..ad679a68861 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -36,6 +36,9 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "bltinmodule.h" #include "traceback.h" +/* Turn this on if your compiler chokes on the big switch: */ +/* #define CASE_TOO_BIG 1 /**/ + #ifndef NDEBUG /* For debugging the interpreter: */ #define LLTRACE 1 /* Low-level trace feature */ @@ -106,13 +109,13 @@ init_save_thread() dynamically loaded modules needn't be compiled separately for use with and without threads: */ -void * +object * save_thread() { #ifdef USE_THREAD if (interpreter_lock) { - void *res; - res = (void *)current_frame; + object *res; + res = (object *)current_frame; current_frame = NULL; release_lock(interpreter_lock); return res; @@ -124,7 +127,7 @@ save_thread() void restore_thread(x) - void *x; + object *x; { #ifdef USE_THREAD if (interpreter_lock) { @@ -722,6 +725,10 @@ eval_code(co, globals, locals, arg) if ((err = dict2remove(f->f_locals, w)) != 0) err_setstr(NameError, getstringvalue(w)); break; + +#ifdef CASE_TOO_BIG + default: switch (opcode) { +#endif case UNPACK_VARARG: if (EMPTY()) { @@ -1023,13 +1030,19 @@ eval_code(co, globals, locals, arg) break; case JUMP_IF_FALSE: - if (!testbool(TOP())) + err = testbool(TOP()); + if (err > 0) + err = 0; + else if (err == 0) JUMPBY(oparg); break; case JUMP_IF_TRUE: - if (testbool(TOP())) + err = testbool(TOP()); + if (err > 0) { + err = 0; JUMPBY(oparg); + } break; case JUMP_ABSOLUTE: @@ -1092,7 +1105,11 @@ eval_code(co, globals, locals, arg) err_setstr(SystemError, "eval_code: unknown opcode"); why = WHY_EXCEPTION; break; - + +#ifdef CASE_TOO_BIG + } +#endif + } /* switch */ on_error: @@ -1388,22 +1405,27 @@ flushline() } -/* Test a value used as condition, e.g., in a for or if statement */ +/* Test a value used as condition, e.g., in a for or if statement. + Return -1 if an error occurred */ static int testbool(v) object *v; { + int res; if (v == None) - return 0; - if (v->ob_type->tp_as_number != NULL) - return (*v->ob_type->tp_as_number->nb_nonzero)(v); - if (v->ob_type->tp_as_sequence != NULL) - return (*v->ob_type->tp_as_sequence->sq_length)(v) != 0; - if (v->ob_type->tp_as_mapping != NULL) - return (*v->ob_type->tp_as_mapping->mp_length)(v) != 0; - /* All other objects are 'true' */ - return 1; + res = 0; + else if (v->ob_type->tp_as_number != NULL) + res = (*v->ob_type->tp_as_number->nb_nonzero)(v); + else if (v->ob_type->tp_as_mapping != NULL) + res = (*v->ob_type->tp_as_mapping->mp_length)(v); + else if (v->ob_type->tp_as_sequence != NULL) + res = (*v->ob_type->tp_as_sequence->sq_length)(v); + else + res = 0; + if (res > 0) + res = 1; + return res; } static object * @@ -1649,7 +1671,13 @@ not(v) object *v; { int outcome = testbool(v); - object *w = outcome == 0 ? True : False; + object *w; + if (outcome < 0) + return NULL; + if (outcome == 0) + w = True; + else + w = False; INCREF(w); return w; } @@ -1780,18 +1808,24 @@ apply_subscript(v, w) err_setstr(TypeError, "unsubscriptable object"); return NULL; } - if (tp->tp_as_sequence != NULL) { + if (tp->tp_as_mapping != NULL) { + return (*tp->tp_as_mapping->mp_subscript)(v, w); + } + else { int i; if (!is_intobject(w)) { err_setstr(TypeError, "sequence subscript not int"); return NULL; } i = getintvalue(w); - if (i < 0) - i += (*tp->tp_as_sequence->sq_length)(v); + if (i < 0) { + int len = (*tp->tp_as_sequence->sq_length)(v); + if (len < 0) + return NULL; + i += len; + } return (*tp->tp_as_sequence->sq_item)(v, i); } - return (*tp->tp_as_mapping->mp_subscript)(v, w); } static object * @@ -1841,6 +1875,8 @@ apply_slice(u, v, w) /* return u[v:w] */ } ilow = 0; isize = ihigh = (*tp->tp_as_sequence->sq_length)(u); + if (isize < 0) + return NULL; if (slice_index(v, isize, &ilow) != 0) return NULL; if (slice_index(w, isize, &ihigh) != 0) @@ -1858,7 +1894,11 @@ assign_subscript(w, key, v) /* w[key] = v */ sequence_methods *sq; mapping_methods *mp; int (*func)(); - if ((sq = tp->tp_as_sequence) != NULL && + if ((mp = tp->tp_as_mapping) != NULL && + (func = mp->mp_ass_subscript) != NULL) { + return (*func)(w, key, v); + } + else if ((sq = tp->tp_as_sequence) != NULL && (func = sq->sq_ass_item) != NULL) { if (!is_intobject(key)) { err_setstr(TypeError, @@ -1867,15 +1907,15 @@ assign_subscript(w, key, v) /* w[key] = v */ } else { int i = getintvalue(key); - if (i < 0) - i += (*sq->sq_length)(w); + if (i < 0) { + int len = (*sq->sq_length)(w); + if (len < 0) + return -1; + i += len; + } return (*func)(w, i, v); } } - else if ((mp = tp->tp_as_mapping) != NULL && - (func = mp->mp_ass_subscript) != NULL) { - return (*func)(w, key, v); - } else { err_setstr(TypeError, "can't assign to this subscripted object"); @@ -1899,6 +1939,8 @@ assign_slice(u, v, w, x) /* u[v:w] = x */ } ilow = 0; isize = ihigh = (*sq->sq_length)(u); + if (isize < 0) + return -1; if (slice_index(v, isize, &ilow) != 0) return -1; if (slice_index(w, isize, &ihigh) != 0) @@ -1955,6 +1997,8 @@ cmp_member(v, w) return -1; } n = (*sq->sq_length)(w); + if (n < 0) + return -1; for (i = 0; i < n; i++) { x = (*sq->sq_item)(w, i); cmp = cmpobject(v, x); @@ -1977,7 +2021,7 @@ cmp_outcome(op, v, w) case IS: case IS_NOT: res = (v == w); - if (op == IS_NOT) + if (op == (int) IS_NOT) res = !res; break; case IN: @@ -1985,7 +2029,7 @@ cmp_outcome(op, v, w) res = cmp_member(v, w); if (res < 0) return NULL; - if (op == NOT_IN) + if (op == (int) NOT_IN) res = !res; break; case EXC_MATCH: