Skip to content

Implement __bool__ and __len__ via unary_op virtual method for all types. #238

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 30, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions py/obj.c
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,15 @@ mp_obj_t mp_obj_len_maybe(mp_obj_t o_in) {
len = seq_len;
} else if (MP_OBJ_IS_TYPE(o_in, &dict_type)) {
len = mp_obj_dict_len(o_in);
} else if (MP_OBJ_IS_TYPE(o_in, &array_type)) {
len = mp_obj_array_len(o_in);
} else {
mp_obj_type_t *type = mp_obj_get_type(o_in);
if (type->unary_op != NULL) {
mp_obj_t result = type->unary_op(RT_UNARY_OP_LEN, o_in);
if (result != MP_OBJ_NULL) {
return result;
}
}

return MP_OBJ_NULL;
}
return MP_OBJ_NEW_SMALL_INT(len);
Expand Down
10 changes: 10 additions & 0 deletions py/objarray.c
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,15 @@ static mp_obj_t mp_builtin_bytearray(mp_obj_t arg) {
}
MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_bytearray_obj, mp_builtin_bytearray);

static mp_obj_t array_unary_op(int op, mp_obj_t o_in) {
mp_obj_array_t *o = o_in;
switch (op) {
case RT_UNARY_OP_BOOL: return MP_BOOL(o->len != 0);
case RT_UNARY_OP_LEN: return MP_OBJ_NEW_SMALL_INT(o->len);
default: return MP_OBJ_NULL; // op not supported
}
}

static mp_obj_t array_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
mp_obj_array_t *o = lhs;
switch (op) {
Expand Down Expand Up @@ -245,6 +254,7 @@ const mp_obj_type_t array_type = {
.print = array_print,
.make_new = array_make_new,
.getiter = array_iterator_new,
.unary_op = array_unary_op,
.binary_op = array_binary_op,
.store_item = array_store_item,
.methods = array_type_methods,
Expand Down
2 changes: 1 addition & 1 deletion py/objbool.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ static mp_obj_t bool_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp
static mp_obj_t bool_unary_op(int op, mp_obj_t o_in) {
machine_int_t value = ((mp_obj_bool_t*)o_in)->value;
switch (op) {
case RT_UNARY_OP_NOT: if (value) { return mp_const_false; } else { return mp_const_true; }
case RT_UNARY_OP_BOOL: return o_in;
case RT_UNARY_OP_POSITIVE: return MP_OBJ_NEW_SMALL_INT(value);
case RT_UNARY_OP_NEGATIVE: return MP_OBJ_NEW_SMALL_INT(-value);
case RT_UNARY_OP_INVERT:
Expand Down
2 changes: 1 addition & 1 deletion py/objcomplex.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ static mp_obj_t complex_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const
static mp_obj_t complex_unary_op(int op, mp_obj_t o_in) {
mp_obj_complex_t *o = o_in;
switch (op) {
case RT_UNARY_OP_NOT: if (o->real != 0 || o->imag != 0) { return mp_const_true;} else { return mp_const_false; }
case RT_UNARY_OP_BOOL: return MP_BOOL(o->real != 0 || o->imag != 0);
case RT_UNARY_OP_POSITIVE: return o_in;
case RT_UNARY_OP_NEGATIVE: return mp_obj_new_complex(-o->real, -o->imag);
default: return MP_OBJ_NULL; // op not supported
Expand Down
3 changes: 2 additions & 1 deletion py/objdict.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ static mp_obj_t dict_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp
static mp_obj_t dict_unary_op(int op, mp_obj_t self_in) {
mp_obj_dict_t *self = self_in;
switch (op) {
case RT_UNARY_OP_NOT: if (self->map.used == 0) { return mp_const_true; } else { return mp_const_false; }
case RT_UNARY_OP_BOOL: return MP_BOOL(self->map.used != 0);
case RT_UNARY_OP_LEN: return MP_OBJ_NEW_SMALL_INT(self->map.used);
default: return MP_OBJ_NULL; // op not supported for None
}
}
Expand Down
2 changes: 1 addition & 1 deletion py/objfloat.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ static mp_obj_t float_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const m
static mp_obj_t float_unary_op(int op, mp_obj_t o_in) {
mp_obj_float_t *o = o_in;
switch (op) {
case RT_UNARY_OP_NOT: if (o->value != 0) { return mp_const_true;} else { return mp_const_false; }
case RT_UNARY_OP_BOOL: return MP_BOOL(o->value != 0);
case RT_UNARY_OP_POSITIVE: return o_in;
case RT_UNARY_OP_NEGATIVE: return mp_obj_new_float(-o->value);
default: return NULL; // op not supported
Expand Down
2 changes: 1 addition & 1 deletion py/objint_longlong.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ void int_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj
mp_obj_t int_unary_op(int op, mp_obj_t o_in) {
mp_obj_int_t *o = o_in;
switch (op) {
case RT_UNARY_OP_NOT: return MP_BOOL(o->val != 0); // TODO: implements RT_UNARY_OP_BOOL
case RT_UNARY_OP_BOOL: return MP_BOOL(o->val != 0);
case RT_UNARY_OP_POSITIVE: return o_in;
case RT_UNARY_OP_NEGATIVE: return mp_obj_new_int_from_ll(-o->val);
case RT_UNARY_OP_INVERT: return mp_obj_new_int_from_ll(~o->val);
Expand Down
3 changes: 2 additions & 1 deletion py/objlist.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ static bool list_cmp_helper(int op, mp_obj_t self_in, mp_obj_t another_in) {
static mp_obj_t list_unary_op(int op, mp_obj_t self_in) {
mp_obj_list_t *self = self_in;
switch (op) {
case RT_UNARY_OP_NOT: if (self->len == 0) { return mp_const_true; } else { return mp_const_false; }
case RT_UNARY_OP_BOOL: return MP_BOOL(self->len != 0);
case RT_UNARY_OP_LEN: return MP_OBJ_NEW_SMALL_INT(self->len);
default: return MP_OBJ_NULL; // op not supported for None
}
}
Expand Down
2 changes: 1 addition & 1 deletion py/objnone.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ static void none_print(void (*print)(void *env, const char *fmt, ...), void *env

static mp_obj_t none_unary_op(int op, mp_obj_t o_in) {
switch (op) {
case RT_UNARY_OP_NOT: return mp_const_true;
case RT_UNARY_OP_BOOL: return mp_const_false;
default: return MP_OBJ_NULL; // op not supported for None
}
}
Expand Down
3 changes: 2 additions & 1 deletion py/objtuple.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ static mp_obj_t tuple_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const m
static mp_obj_t tuple_unary_op(int op, mp_obj_t self_in) {
mp_obj_tuple_t *self = self_in;
switch (op) {
case RT_UNARY_OP_NOT: if (self->len == 0) { return mp_const_true; } else { return mp_const_false; }
case RT_UNARY_OP_BOOL: return MP_BOOL(self->len != 0);
case RT_UNARY_OP_LEN: return MP_OBJ_NEW_SMALL_INT(self->len);
default: return MP_OBJ_NULL; // op not supported for None
}
}
Expand Down
15 changes: 10 additions & 5 deletions py/runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -317,15 +317,20 @@ int rt_is_true(mp_obj_t arg) {
} else if (arg == mp_const_true) {
return 1;
} else {
mp_obj_type_t *type = mp_obj_get_type(arg);
if (type->unary_op != NULL) {
mp_obj_t result = type->unary_op(RT_UNARY_OP_BOOL, arg);
if (result != NULL) {
return result == mp_const_true;
}
}

mp_obj_t len = mp_obj_len_maybe(arg);
if (len != MP_OBJ_NULL) {
// obj has a length, truth determined if len != 0
return len != MP_OBJ_NEW_SMALL_INT(0);
} else {
// TODO check for __bool__ method
// TODO check floats and complex numbers

// any other obj is true (TODO is that correct?)
// any other obj is true per Python semantics
return 1;
}
}
Expand Down Expand Up @@ -476,7 +481,7 @@ mp_obj_t rt_unary_op(int op, mp_obj_t arg) {
if (MP_OBJ_IS_SMALL_INT(arg)) {
mp_small_int_t val = MP_OBJ_SMALL_INT_VALUE(arg);
switch (op) {
case RT_UNARY_OP_NOT: if (val == 0) { return mp_const_true;} else { return mp_const_false; }
case RT_UNARY_OP_BOOL: return MP_BOOL(val != 0);
case RT_UNARY_OP_POSITIVE: break;
case RT_UNARY_OP_NEGATIVE: val = -val; break;
case RT_UNARY_OP_INVERT: val = ~val; break;
Expand Down
5 changes: 4 additions & 1 deletion py/runtime0.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
typedef enum {
RT_UNARY_OP_NOT, // TODO remove this op since it's no longer needed
RT_UNARY_OP_BOOL, // __bool__
RT_UNARY_OP_LEN, // __len__
RT_UNARY_OP_POSITIVE,
RT_UNARY_OP_NEGATIVE,
RT_UNARY_OP_INVERT,
// Used only for CPython-compatible codegeneration
RT_UNARY_OP_NOT,
} rt_unary_op_t;

typedef enum {
Expand Down
9 changes: 9 additions & 0 deletions tests/basics/true-value.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,18 @@
if not False:
print("False")

if not None:
print("None")

if not 0:
print("0")

if not 0.0:
print("float 0")

if not 0+0j:
print("complex 0")

if not "":
print("Empty string")
if "foo":
Expand Down