Implement NotImplemented, fallback operators
Adds 'NOTIMPL' as a new primitive value, available from __builtins__.NotImplemented. Adds support for inverse / reflected overrides for binary operators. Adds opcodes for LESS_EQUAL and GREATER_EQUAL.
This commit is contained in:
parent
873daa1f67
commit
70d5f1b2b7
@ -527,9 +527,9 @@ static void compareChained(int inner) {
|
|||||||
case TOKEN_BANG_EQUAL: emitBytes(OP_EQUAL, OP_NOT); break;
|
case TOKEN_BANG_EQUAL: emitBytes(OP_EQUAL, OP_NOT); break;
|
||||||
case TOKEN_EQUAL_EQUAL: emitByte(OP_EQUAL); break;
|
case TOKEN_EQUAL_EQUAL: emitByte(OP_EQUAL); break;
|
||||||
case TOKEN_GREATER: emitByte(OP_GREATER); break;
|
case TOKEN_GREATER: emitByte(OP_GREATER); break;
|
||||||
case TOKEN_GREATER_EQUAL: emitBytes(OP_LESS, OP_NOT); break;
|
case TOKEN_GREATER_EQUAL: emitByte(OP_GREATER_EQUAL); break;
|
||||||
case TOKEN_LESS: emitByte(OP_LESS); break;
|
case TOKEN_LESS: emitByte(OP_LESS); break;
|
||||||
case TOKEN_LESS_EQUAL: emitBytes(OP_GREATER, OP_NOT); break;
|
case TOKEN_LESS_EQUAL: emitByte(OP_LESS_EQUAL); break;
|
||||||
|
|
||||||
case TOKEN_IS: emitByte(OP_IS); if (invert) emitByte(OP_NOT); break;
|
case TOKEN_IS: emitByte(OP_IS); if (invert) emitByte(OP_NOT); break;
|
||||||
|
|
||||||
|
@ -68,7 +68,9 @@ typedef enum {
|
|||||||
OP_ANNOTATE,
|
OP_ANNOTATE,
|
||||||
OP_BEGIN_FINALLY,
|
OP_BEGIN_FINALLY,
|
||||||
OP_END_FINALLY,
|
OP_END_FINALLY,
|
||||||
/* current highest: 45 */
|
OP_GREATER_EQUAL,
|
||||||
|
OP_LESS_EQUAL,
|
||||||
|
/* current highest: 47 */
|
||||||
|
|
||||||
OP_CALL = 64,
|
OP_CALL = 64,
|
||||||
OP_CLASS,
|
OP_CLASS,
|
||||||
|
@ -29,6 +29,7 @@ typedef enum {
|
|||||||
KRK_VAL_NONE = 0xFFFF,
|
KRK_VAL_NONE = 0xFFFF,
|
||||||
KRK_VAL_KWARGS = 0x7FFC,
|
KRK_VAL_KWARGS = 0x7FFC,
|
||||||
KRK_VAL_OBJECT = 0x7FFD,
|
KRK_VAL_OBJECT = 0x7FFD,
|
||||||
|
KRK_VAL_NOTIMPL = 0x7FFE,
|
||||||
} KrkValueType;
|
} KrkValueType;
|
||||||
|
|
||||||
#define KRK_VAL_MASK_BOOLEAN ((uint64_t)0xFFFC000000000000) /* 1..1100 */
|
#define KRK_VAL_MASK_BOOLEAN ((uint64_t)0xFFFC000000000000) /* 1..1100 */
|
||||||
@ -37,6 +38,7 @@ typedef enum {
|
|||||||
#define KRK_VAL_MASK_NONE ((uint64_t)0xFFFF000000000000) /* 1..1111 */
|
#define KRK_VAL_MASK_NONE ((uint64_t)0xFFFF000000000000) /* 1..1111 */
|
||||||
#define KRK_VAL_MASK_KWARGS ((uint64_t)0x7FFC000000000000) /* 0..1100 */
|
#define KRK_VAL_MASK_KWARGS ((uint64_t)0x7FFC000000000000) /* 0..1100 */
|
||||||
#define KRK_VAL_MASK_OBJECT ((uint64_t)0x7FFD000000000000) /* 0..1101 */
|
#define KRK_VAL_MASK_OBJECT ((uint64_t)0x7FFD000000000000) /* 0..1101 */
|
||||||
|
#define KRK_VAL_MASK_NOTIMPL ((uint64_t)0x7FFE000000000000) /* 0..1110 */
|
||||||
#define KRK_VAL_MASK_NAN ((uint64_t)0x7FFC000000000000)
|
#define KRK_VAL_MASK_NAN ((uint64_t)0x7FFC000000000000)
|
||||||
#define KRK_VAL_MASK_LOW ((uint64_t)0x0000FFFFFFFFFFFF)
|
#define KRK_VAL_MASK_LOW ((uint64_t)0x0000FFFFFFFFFFFF)
|
||||||
|
|
||||||
@ -171,6 +173,7 @@ typedef union {
|
|||||||
} KrkValueDbl;
|
} KrkValueDbl;
|
||||||
|
|
||||||
#define NONE_VAL(value) ((KrkValue)(KRK_VAL_MASK_LOW | KRK_VAL_MASK_NONE))
|
#define NONE_VAL(value) ((KrkValue)(KRK_VAL_MASK_LOW | KRK_VAL_MASK_NONE))
|
||||||
|
#define NOTIMPL_VAL(value) ((KrkValue)(KRK_VAL_MASK_LOW | KRK_VAL_MASK_NOTIMPL))
|
||||||
#define BOOLEAN_VAL(value) ((KrkValue)((uint32_t)(value) | KRK_VAL_MASK_BOOLEAN))
|
#define BOOLEAN_VAL(value) ((KrkValue)((uint32_t)(value) | KRK_VAL_MASK_BOOLEAN))
|
||||||
#define INTEGER_VAL(value) ((KrkValue)((uint32_t)(value) | KRK_VAL_MASK_INTEGER))
|
#define INTEGER_VAL(value) ((KrkValue)((uint32_t)(value) | KRK_VAL_MASK_INTEGER))
|
||||||
#define KWARGS_VAL(value) ((KrkValue)((uint32_t)(value) | KRK_VAL_MASK_KWARGS))
|
#define KWARGS_VAL(value) ((KrkValue)((uint32_t)(value) | KRK_VAL_MASK_KWARGS))
|
||||||
@ -182,6 +185,7 @@ typedef union {
|
|||||||
|
|
||||||
#define AS_BOOLEAN(value) ((krk_integer_type)((value) & KRK_VAL_MASK_LOW))
|
#define AS_BOOLEAN(value) ((krk_integer_type)((value) & KRK_VAL_MASK_LOW))
|
||||||
#define AS_INTEGER(value) ((krk_integer_type)((value) & KRK_VAL_MASK_LOW))
|
#define AS_INTEGER(value) ((krk_integer_type)((value) & KRK_VAL_MASK_LOW))
|
||||||
|
#define AS_NOTIMPL(value) ((krk_integer_type)((value) & KRK_VAL_MASK_LOW))
|
||||||
#define AS_HANDLER(value) ((uint32_t)((value) & KRK_VAL_MASK_LOW))
|
#define AS_HANDLER(value) ((uint32_t)((value) & KRK_VAL_MASK_LOW))
|
||||||
#define AS_OBJECT(value) ((KrkObj*)(uintptr_t)((value) & KRK_VAL_MASK_LOW))
|
#define AS_OBJECT(value) ((KrkObj*)(uintptr_t)((value) & KRK_VAL_MASK_LOW))
|
||||||
#define AS_FLOATING(value) (((KrkValueDbl){.val = (value)}).dbl)
|
#define AS_FLOATING(value) (((KrkValueDbl){.val = (value)}).dbl)
|
||||||
@ -192,6 +196,7 @@ typedef union {
|
|||||||
#define IS_HANDLER(value) (((value) & KRK_VAL_MASK_NONE) == KRK_VAL_MASK_HANDLER)
|
#define IS_HANDLER(value) (((value) & KRK_VAL_MASK_NONE) == KRK_VAL_MASK_HANDLER)
|
||||||
#define IS_OBJECT(value) (((value) & KRK_VAL_MASK_NONE) == KRK_VAL_MASK_OBJECT)
|
#define IS_OBJECT(value) (((value) & KRK_VAL_MASK_NONE) == KRK_VAL_MASK_OBJECT)
|
||||||
#define IS_KWARGS(value) (((value) & KRK_VAL_MASK_NONE) == KRK_VAL_MASK_KWARGS)
|
#define IS_KWARGS(value) (((value) & KRK_VAL_MASK_NONE) == KRK_VAL_MASK_KWARGS)
|
||||||
|
#define IS_NOTIMPL(value) (((value) & KRK_VAL_MASK_NONE) == KRK_VAL_MASK_NOTIMPL)
|
||||||
#define IS_FLOATING(value) (((value) & KRK_VAL_MASK_NAN) != KRK_VAL_MASK_NAN)
|
#define IS_FLOATING(value) (((value) & KRK_VAL_MASK_NAN) != KRK_VAL_MASK_NAN)
|
||||||
|
|
||||||
#define AS_HANDLER_TYPE(value) (AS_HANDLER(value) >> 16)
|
#define AS_HANDLER_TYPE(value) (AS_HANDLER(value) >> 16)
|
||||||
|
@ -165,6 +165,7 @@ struct BaseClasses {
|
|||||||
KrkClass * propertyClass; /**< Magic object that calls a function when accessed from an instance through the dot operator. */
|
KrkClass * propertyClass; /**< Magic object that calls a function when accessed from an instance through the dot operator. */
|
||||||
KrkClass * codeobjectClass; /**< Static compiled bytecode container (KrkCodeObject) */
|
KrkClass * codeobjectClass; /**< Static compiled bytecode container (KrkCodeObject) */
|
||||||
KrkClass * generatorClass; /**< Generator object. */
|
KrkClass * generatorClass; /**< Generator object. */
|
||||||
|
KrkClass * notImplClass; /**< NotImplementedType */
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -94,6 +94,12 @@ KRK_METHOD(NoneType,__str__,{
|
|||||||
return OBJECT_VAL(S("None"));
|
return OBJECT_VAL(S("None"));
|
||||||
})
|
})
|
||||||
|
|
||||||
|
#define IS_NotImplementedType(o) IS_NOTIMPL(o)
|
||||||
|
#define AS_NotImplementedType(o) (1)
|
||||||
|
KRK_METHOD(NotImplementedType,__str__,{
|
||||||
|
return OBJECT_VAL(S("NotImplemented"));
|
||||||
|
})
|
||||||
|
|
||||||
#undef BIND_METHOD
|
#undef BIND_METHOD
|
||||||
#define BIND_METHOD(klass,method) do { krk_defineNative(& _ ## klass->methods, #method, _ ## klass ## _ ## method); } while (0)
|
#define BIND_METHOD(klass,method) do { krk_defineNative(& _ ## klass->methods, #method, _ ## klass ## _ ## method); } while (0)
|
||||||
_noexport
|
_noexport
|
||||||
@ -128,4 +134,11 @@ void _createAndBind_numericClasses(void) {
|
|||||||
BIND_METHOD(NoneType, __str__);
|
BIND_METHOD(NoneType, __str__);
|
||||||
krk_defineNative(&_NoneType->methods, "__repr__", FUNC_NAME(NoneType,__str__));
|
krk_defineNative(&_NoneType->methods, "__repr__", FUNC_NAME(NoneType,__str__));
|
||||||
krk_finalizeClass(_NoneType);
|
krk_finalizeClass(_NoneType);
|
||||||
|
|
||||||
|
KrkClass * _NotImplementedType = ADD_BASE_CLASS(vm.baseClasses->notImplClass, "NotImplementedType", vm.baseClasses->objectClass);
|
||||||
|
BIND_METHOD(NotImplementedType, __str__);
|
||||||
|
krk_defineNative(&_NotImplementedType->methods, "__repr__", FUNC_NAME(NotImplementedType,__str__));
|
||||||
|
krk_finalizeClass(_NotImplementedType);
|
||||||
|
|
||||||
|
krk_attachNamedValue(&vm.builtins->fields, "NotImplemented", NOTIMPL_VAL());
|
||||||
}
|
}
|
||||||
|
@ -434,45 +434,27 @@ KRK_METHOD(str,rstrip,{
|
|||||||
return _string_strip_shared(argc,argv,2);
|
return _string_strip_shared(argc,argv,2);
|
||||||
})
|
})
|
||||||
|
|
||||||
KRK_METHOD(str,__lt__,{
|
#define strCompare(name,lop,iop,rop) \
|
||||||
METHOD_TAKES_EXACTLY(1);
|
KRK_METHOD(str,name,{ \
|
||||||
if (!IS_STRING(argv[1])) {
|
METHOD_TAKES_EXACTLY(1); \
|
||||||
return KWARGS_VAL(0); /* represents 'not implemented' */
|
if (!IS_STRING(argv[1])) { \
|
||||||
}
|
return NOTIMPL_VAL(); \
|
||||||
if (AS_STRING(argv[0]) == AS_STRING(argv[1])) return BOOLEAN_VAL(0);
|
} \
|
||||||
|
size_t aLen = AS_STRING(argv[0])->length; \
|
||||||
|
size_t bLen = AS_STRING(argv[1])->length; \
|
||||||
|
const char * a = AS_CSTRING(argv[0]); \
|
||||||
|
const char * b = AS_CSTRING(argv[1]); \
|
||||||
|
for (size_t i = 0; i < (aLen < bLen) ? aLen : bLen; i++) { \
|
||||||
|
if (a[i] lop b[i]) return BOOLEAN_VAL(1); \
|
||||||
|
if (a[i] iop b[i]) return BOOLEAN_VAL(0); \
|
||||||
|
} \
|
||||||
|
return BOOLEAN_VAL((aLen rop bLen)); \
|
||||||
|
})
|
||||||
|
|
||||||
size_t aLen = AS_STRING(argv[0])->length;
|
strCompare(__gt__,>,<,>)
|
||||||
size_t bLen = AS_STRING(argv[1])->length;
|
strCompare(__lt__,<,>,<)
|
||||||
const char * a = AS_CSTRING(argv[0]);
|
strCompare(__ge__,>,<,>=)
|
||||||
const char * b = AS_CSTRING(argv[1]);
|
strCompare(__le__,<,>,<=)
|
||||||
|
|
||||||
for (size_t i = 0; i < (aLen < bLen) ? aLen : bLen; i++) {
|
|
||||||
if (a[i] < b[i]) return BOOLEAN_VAL(1);
|
|
||||||
if (a[i] > b[i]) return BOOLEAN_VAL(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return BOOLEAN_VAL((aLen < bLen));
|
|
||||||
})
|
|
||||||
|
|
||||||
KRK_METHOD(str,__gt__,{
|
|
||||||
METHOD_TAKES_EXACTLY(1);
|
|
||||||
if (!IS_STRING(argv[1])) {
|
|
||||||
return KWARGS_VAL(0); /* represents 'not implemented' */
|
|
||||||
}
|
|
||||||
if (AS_STRING(argv[0]) == AS_STRING(argv[1])) return BOOLEAN_VAL(0);
|
|
||||||
|
|
||||||
size_t aLen = AS_STRING(argv[0])->length;
|
|
||||||
size_t bLen = AS_STRING(argv[1])->length;
|
|
||||||
const char * a = AS_CSTRING(argv[0]);
|
|
||||||
const char * b = AS_CSTRING(argv[1]);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < (aLen < bLen) ? aLen : bLen; i++) {
|
|
||||||
if (a[i] < b[i]) return BOOLEAN_VAL(0);
|
|
||||||
if (a[i] > b[i]) return BOOLEAN_VAL(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return BOOLEAN_VAL((aLen > bLen));
|
|
||||||
})
|
|
||||||
|
|
||||||
/** TODO but throw a more descriptive error for now */
|
/** TODO but throw a more descriptive error for now */
|
||||||
KRK_METHOD(str,__mod__,{
|
KRK_METHOD(str,__mod__,{
|
||||||
@ -903,6 +885,8 @@ void _createAndBind_strClass(void) {
|
|||||||
BIND_METHOD(str,__contains__);
|
BIND_METHOD(str,__contains__);
|
||||||
BIND_METHOD(str,__lt__);
|
BIND_METHOD(str,__lt__);
|
||||||
BIND_METHOD(str,__gt__);
|
BIND_METHOD(str,__gt__);
|
||||||
|
BIND_METHOD(str,__le__);
|
||||||
|
BIND_METHOD(str,__ge__);
|
||||||
BIND_METHOD(str,__mod__);
|
BIND_METHOD(str,__mod__);
|
||||||
BIND_METHOD(str,__repr__);
|
BIND_METHOD(str,__repr__);
|
||||||
BIND_METHOD(str,__str__);
|
BIND_METHOD(str,__str__);
|
||||||
|
@ -42,6 +42,8 @@ SIMPLE(OP_YIELD)
|
|||||||
SIMPLE(OP_ANNOTATE)
|
SIMPLE(OP_ANNOTATE)
|
||||||
SIMPLE(OP_BEGIN_FINALLY)
|
SIMPLE(OP_BEGIN_FINALLY)
|
||||||
SIMPLE(OP_END_FINALLY)
|
SIMPLE(OP_END_FINALLY)
|
||||||
|
SIMPLE(OP_GREATER_EQUAL)
|
||||||
|
SIMPLE(OP_LESS_EQUAL)
|
||||||
CONSTANT(OP_DEFINE_GLOBAL,(void)0)
|
CONSTANT(OP_DEFINE_GLOBAL,(void)0)
|
||||||
CONSTANT(OP_CONSTANT,(void)0)
|
CONSTANT(OP_CONSTANT,(void)0)
|
||||||
CONSTANT(OP_GET_GLOBAL,(void)0)
|
CONSTANT(OP_GET_GLOBAL,(void)0)
|
||||||
|
102
src/vm.c
102
src/vm.c
@ -463,6 +463,8 @@ inline KrkClass * krk_getType(KrkValue of) {
|
|||||||
return vm.baseClasses->boolClass;
|
return vm.baseClasses->boolClass;
|
||||||
case KRK_VAL_NONE:
|
case KRK_VAL_NONE:
|
||||||
return vm.baseClasses->noneTypeClass;
|
return vm.baseClasses->noneTypeClass;
|
||||||
|
case KRK_VAL_NOTIMPL:
|
||||||
|
return vm.baseClasses->notImplClass;
|
||||||
case KRK_VAL_OBJECT:
|
case KRK_VAL_OBJECT:
|
||||||
switch (AS_OBJECT(of)->type) {
|
switch (AS_OBJECT(of)->type) {
|
||||||
case KRK_OBJ_CLASS:
|
case KRK_OBJ_CLASS:
|
||||||
@ -1379,32 +1381,56 @@ const char * krk_typeName(KrkValue value) {
|
|||||||
return krk_getType(value)->name->chars;
|
return krk_getType(value)->name->chars;
|
||||||
}
|
}
|
||||||
|
|
||||||
static KrkValue tryBind(const char * name, KrkValue a, KrkValue b, const char * operator, const char * msg) {
|
static KrkValue tryBind(const char * name, KrkValue a, KrkValue b, const char * operator, const char * msg, const char * inverse) {
|
||||||
krk_push(b);
|
krk_currentThread.scratchSpace[0] = a;
|
||||||
krk_push(a);
|
krk_currentThread.scratchSpace[1] = b;
|
||||||
KrkClass * type = krk_getType(a);
|
|
||||||
|
/* Potential return value */
|
||||||
|
KrkValue value = NONE_VAL();
|
||||||
KrkString * methodName = krk_copyString(name, strlen(name));
|
KrkString * methodName = krk_copyString(name, strlen(name));
|
||||||
krk_push(OBJECT_VAL(methodName));
|
krk_push(OBJECT_VAL(methodName));
|
||||||
KrkValue value = KWARGS_VAL(0);
|
|
||||||
krk_swap(1);
|
/* Bind from a */
|
||||||
|
KrkClass * type = krk_getType(a);
|
||||||
|
krk_push(a);
|
||||||
if (krk_bindMethod(type, methodName)) {
|
if (krk_bindMethod(type, methodName)) {
|
||||||
krk_swap(1);
|
krk_push(b);
|
||||||
krk_pop();
|
|
||||||
krk_swap(1);
|
|
||||||
value = krk_callSimple(krk_peek(1), 1, 1);
|
value = krk_callSimple(krk_peek(1), 1, 1);
|
||||||
}
|
if (!IS_NOTIMPL(value)) goto _success;
|
||||||
if (IS_KWARGS(value)) {
|
krk_pop(); /* name */
|
||||||
return krk_runtimeError(vm.exceptions->typeError, msg, operator, krk_typeName(a), krk_typeName(b));
|
|
||||||
} else {
|
} else {
|
||||||
return value;
|
krk_pop(); /* a */
|
||||||
|
krk_pop(); /* name */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Bind from b */
|
||||||
|
methodName = krk_copyString(inverse, strlen(inverse));
|
||||||
|
krk_push(OBJECT_VAL(methodName));
|
||||||
|
type = krk_getType(b);
|
||||||
|
krk_push(b);
|
||||||
|
if (krk_bindMethod(type, methodName)) {
|
||||||
|
krk_push(a);
|
||||||
|
value = krk_callSimple(krk_peek(1), 1, 1);
|
||||||
|
if (!IS_NOTIMPL(value)) goto _success;
|
||||||
|
krk_pop(); /* name */
|
||||||
|
} else {
|
||||||
|
krk_pop(); /* b */
|
||||||
|
krk_pop(); /* name */
|
||||||
|
}
|
||||||
|
|
||||||
|
return krk_runtimeError(vm.exceptions->typeError, msg, operator, krk_typeName(a), krk_typeName(b));
|
||||||
|
|
||||||
|
_success:
|
||||||
|
krk_pop(); /* name */
|
||||||
|
/* Return result */
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic arithmetic and string functions follow.
|
* Basic arithmetic and string functions follow.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define MAKE_BIN_OP(name,operator) \
|
#define MAKE_BIN_OP(name,operator,inv) \
|
||||||
KrkValue krk_operator_ ## name (KrkValue a, KrkValue b) { \
|
KrkValue krk_operator_ ## name (KrkValue a, KrkValue b) { \
|
||||||
if (IS_INTEGER(a) && IS_INTEGER(b)) return INTEGER_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \
|
if (IS_INTEGER(a) && IS_INTEGER(b)) return INTEGER_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \
|
||||||
if (IS_FLOATING(a)) { \
|
if (IS_FLOATING(a)) { \
|
||||||
@ -1413,43 +1439,43 @@ static KrkValue tryBind(const char * name, KrkValue a, KrkValue b, const char *
|
|||||||
} else if (IS_FLOATING(b)) { \
|
} else if (IS_FLOATING(b)) { \
|
||||||
if (IS_INTEGER(a)) return FLOATING_VAL((double)AS_INTEGER(a) operator AS_FLOATING(b)); \
|
if (IS_INTEGER(a)) return FLOATING_VAL((double)AS_INTEGER(a) operator AS_FLOATING(b)); \
|
||||||
} \
|
} \
|
||||||
return tryBind("__" #name "__", a, b, #operator, "unsupported operand types for %s: '%s' and '%s'"); \
|
return tryBind("__" #name "__", a, b, #operator, "unsupported operand types for %s: '%s' and '%s'", "__" #inv "__"); \
|
||||||
}
|
}
|
||||||
|
|
||||||
MAKE_BIN_OP(add,+)
|
MAKE_BIN_OP(add,+,radd)
|
||||||
MAKE_BIN_OP(sub,-)
|
MAKE_BIN_OP(sub,-,ssub)
|
||||||
MAKE_BIN_OP(mul,*)
|
MAKE_BIN_OP(mul,*,rmul)
|
||||||
MAKE_BIN_OP(div,/)
|
MAKE_BIN_OP(div,/,rdiv)
|
||||||
|
|
||||||
#define MAKE_UNOPTIMIZED_BIN_OP(name,operator) \
|
#define MAKE_UNOPTIMIZED_BIN_OP(name,operator,inv) \
|
||||||
KrkValue krk_operator_ ## name (KrkValue a, KrkValue b) { \
|
KrkValue krk_operator_ ## name (KrkValue a, KrkValue b) { \
|
||||||
return tryBind("__" #name "__", a, b, #operator, "unsupported operand types for %s: '%s' and '%s'"); \
|
return tryBind("__" #name "__", a, b, #operator, "unsupported operand types for %s: '%s' and '%s'", "__" #inv "__"); \
|
||||||
}
|
}
|
||||||
|
|
||||||
MAKE_UNOPTIMIZED_BIN_OP(pow,**)
|
MAKE_UNOPTIMIZED_BIN_OP(pow,**,rpow)
|
||||||
|
|
||||||
/* Bit ops are invalid on doubles in C, so we can't use the same set of macros for them;
|
/* Bit ops are invalid on doubles in C, so we can't use the same set of macros for them;
|
||||||
* they should be invalid in Kuroko as well. */
|
* they should be invalid in Kuroko as well. */
|
||||||
#define MAKE_BIT_OP_BOOL(name,operator) \
|
#define MAKE_BIT_OP_BOOL(name,operator,inv) \
|
||||||
KrkValue krk_operator_ ## name (KrkValue a, KrkValue b) { \
|
KrkValue krk_operator_ ## name (KrkValue a, KrkValue b) { \
|
||||||
if (IS_BOOLEAN(a) && IS_BOOLEAN(b)) return BOOLEAN_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \
|
if (IS_BOOLEAN(a) && IS_BOOLEAN(b)) return BOOLEAN_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \
|
||||||
if (IS_INTEGER(a) && IS_INTEGER(b)) return INTEGER_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \
|
if (IS_INTEGER(a) && IS_INTEGER(b)) return INTEGER_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \
|
||||||
return tryBind("__" #name "__", a, b, #operator, "unsupported operand types for %s: '%s' and '%s'"); \
|
return tryBind("__" #name "__", a, b, #operator, "unsupported operand types for %s: '%s' and '%s'", "__" #inv "__"); \
|
||||||
}
|
}
|
||||||
#define MAKE_BIT_OP(name,operator) \
|
#define MAKE_BIT_OP(name,operator,inv) \
|
||||||
KrkValue krk_operator_ ## name (KrkValue a, KrkValue b) { \
|
KrkValue krk_operator_ ## name (KrkValue a, KrkValue b) { \
|
||||||
if (IS_INTEGER(a) && IS_INTEGER(b)) return INTEGER_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \
|
if (IS_INTEGER(a) && IS_INTEGER(b)) return INTEGER_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \
|
||||||
return tryBind("__" #name "__", a, b, #operator, "unsupported operand types for %s: '%s' and '%s'"); \
|
return tryBind("__" #name "__", a, b, #operator, "unsupported operand types for %s: '%s' and '%s'", "__" #inv "__"); \
|
||||||
}
|
}
|
||||||
|
|
||||||
MAKE_BIT_OP_BOOL(or,|)
|
MAKE_BIT_OP_BOOL(or,|,ror)
|
||||||
MAKE_BIT_OP_BOOL(xor,^)
|
MAKE_BIT_OP_BOOL(xor,^,rxor)
|
||||||
MAKE_BIT_OP_BOOL(and,&)
|
MAKE_BIT_OP_BOOL(and,&,rand)
|
||||||
MAKE_BIT_OP(lshift,<<)
|
MAKE_BIT_OP(lshift,<<,rlshift)
|
||||||
MAKE_BIT_OP(rshift,>>)
|
MAKE_BIT_OP(rshift,>>,rrshift)
|
||||||
MAKE_BIT_OP(mod,%) /* not a bit op, but doesn't work on floating point */
|
MAKE_BIT_OP(mod,%,rmod) /* not a bit op, but doesn't work on floating point */
|
||||||
|
|
||||||
#define MAKE_COMPARATOR(name, operator) \
|
#define MAKE_COMPARATOR(name, operator,inv) \
|
||||||
KrkValue krk_operator_ ## name (KrkValue a, KrkValue b) { \
|
KrkValue krk_operator_ ## name (KrkValue a, KrkValue b) { \
|
||||||
if (IS_INTEGER(a) && IS_INTEGER(b)) return BOOLEAN_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \
|
if (IS_INTEGER(a) && IS_INTEGER(b)) return BOOLEAN_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \
|
||||||
if (IS_FLOATING(a)) { \
|
if (IS_FLOATING(a)) { \
|
||||||
@ -1458,11 +1484,13 @@ MAKE_BIT_OP(mod,%) /* not a bit op, but doesn't work on floating point */
|
|||||||
} else if (IS_FLOATING(b)) { \
|
} else if (IS_FLOATING(b)) { \
|
||||||
if (IS_INTEGER(a)) return BOOLEAN_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \
|
if (IS_INTEGER(a)) return BOOLEAN_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \
|
||||||
} \
|
} \
|
||||||
return tryBind("__" #name "__", a, b, #operator, "unsupported operand types for %s: '%s' and '%s'"); \
|
return tryBind("__" #name "__", a, b, #operator, "unsupported operand types for %s: '%s' and '%s'", "__" #inv "__"); \
|
||||||
}
|
}
|
||||||
|
|
||||||
MAKE_COMPARATOR(lt, <)
|
MAKE_COMPARATOR(lt, <, gt)
|
||||||
MAKE_COMPARATOR(gt, >)
|
MAKE_COMPARATOR(gt, >, lt)
|
||||||
|
MAKE_COMPARATOR(le, <=, ge)
|
||||||
|
MAKE_COMPARATOR(ge, >=, le)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* At the end of each instruction cycle, we check the exception flag to see
|
* At the end of each instruction cycle, we check the exception flag to see
|
||||||
@ -2128,6 +2156,8 @@ _finishReturn: (void)0;
|
|||||||
}
|
}
|
||||||
case OP_LESS: BINARY_OP(lt);
|
case OP_LESS: BINARY_OP(lt);
|
||||||
case OP_GREATER: BINARY_OP(gt);
|
case OP_GREATER: BINARY_OP(gt);
|
||||||
|
case OP_LESS_EQUAL: BINARY_OP(le);
|
||||||
|
case OP_GREATER_EQUAL: BINARY_OP(ge);
|
||||||
case OP_ADD: BINARY_OP(add);
|
case OP_ADD: BINARY_OP(add);
|
||||||
case OP_SUBTRACT: BINARY_OP(sub)
|
case OP_SUBTRACT: BINARY_OP(sub)
|
||||||
case OP_MULTIPLY: BINARY_OP(mul)
|
case OP_MULTIPLY: BINARY_OP(mul)
|
||||||
|
41
test/testComparisonOperatorFallbacks.krk
Normal file
41
test/testComparisonOperatorFallbacks.krk
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
class Foo:
|
||||||
|
def __init__(self):
|
||||||
|
self.val = 42
|
||||||
|
def __lt__(self, o):
|
||||||
|
print('call lt',o)
|
||||||
|
if isinstance(o,(int,float)):
|
||||||
|
return self.val < o
|
||||||
|
return NotImplemented
|
||||||
|
def __le__(self, o):
|
||||||
|
print('call le',o)
|
||||||
|
if isinstance(o,(int,float)):
|
||||||
|
return self.val <= o
|
||||||
|
return NotImplemented
|
||||||
|
def __gt__(self, o):
|
||||||
|
print('call gt',o)
|
||||||
|
if isinstance(o,(int,float)):
|
||||||
|
return self.val > o
|
||||||
|
return NotImplemented
|
||||||
|
def __ge__(self, o):
|
||||||
|
print('call ge',o)
|
||||||
|
if isinstance(o,(int,float)):
|
||||||
|
return self.val >= o
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
print(Foo() < 43)
|
||||||
|
print(Foo() > 43)
|
||||||
|
print(Foo() > 41)
|
||||||
|
print(Foo() < 41)
|
||||||
|
print(Foo() >= 41)
|
||||||
|
print(Foo() >= 42)
|
||||||
|
print(Foo() <= 43)
|
||||||
|
print(Foo() <= 42)
|
||||||
|
print('---')
|
||||||
|
print(43 > Foo())
|
||||||
|
print(43 < Foo())
|
||||||
|
print(41 < Foo())
|
||||||
|
print(41 > Foo())
|
||||||
|
print(41 <= Foo())
|
||||||
|
print(42 <= Foo())
|
||||||
|
print(43 >= Foo())
|
||||||
|
print(42 >= Foo())
|
33
test/testComparisonOperatorFallbacks.krk.expect
Normal file
33
test/testComparisonOperatorFallbacks.krk.expect
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
call lt 43
|
||||||
|
True
|
||||||
|
call gt 43
|
||||||
|
False
|
||||||
|
call gt 41
|
||||||
|
True
|
||||||
|
call lt 41
|
||||||
|
False
|
||||||
|
call ge 41
|
||||||
|
True
|
||||||
|
call ge 42
|
||||||
|
True
|
||||||
|
call le 43
|
||||||
|
True
|
||||||
|
call le 42
|
||||||
|
True
|
||||||
|
---
|
||||||
|
call lt 43
|
||||||
|
True
|
||||||
|
call gt 43
|
||||||
|
False
|
||||||
|
call gt 41
|
||||||
|
True
|
||||||
|
call lt 41
|
||||||
|
False
|
||||||
|
call ge 41
|
||||||
|
True
|
||||||
|
call ge 42
|
||||||
|
True
|
||||||
|
call le 43
|
||||||
|
True
|
||||||
|
call le 42
|
||||||
|
True
|
51
test/testReflectedBinaryOperators.krk
Normal file
51
test/testReflectedBinaryOperators.krk
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
class RAdder:
|
||||||
|
def __radd__(self, o):
|
||||||
|
print("__radd__ called",o)
|
||||||
|
return f'{o} + RAdder()'
|
||||||
|
|
||||||
|
def __rmul__(self, o):
|
||||||
|
print("__rmul__ called", o)
|
||||||
|
return f'{o} * RAdder()'
|
||||||
|
|
||||||
|
def __rdiv__(self, o):
|
||||||
|
print("__rdiv__ called", o)
|
||||||
|
return f'{o} / RAdder()'
|
||||||
|
|
||||||
|
def __rpow__(self, o):
|
||||||
|
print("__rpow__ called", o)
|
||||||
|
return f'{o} ** RAdder()'
|
||||||
|
|
||||||
|
def __rrshift__(self, o):
|
||||||
|
print("__rrshift__ called", o)
|
||||||
|
return f'{o} >> RAdder()'
|
||||||
|
|
||||||
|
def __rlshift__(self, o):
|
||||||
|
print("__rlshift__ called", o)
|
||||||
|
return f'{o} << RAdder()'
|
||||||
|
|
||||||
|
def __ror__(self, o):
|
||||||
|
print("__ror__ called", o)
|
||||||
|
return f'{o} | RAdder()'
|
||||||
|
|
||||||
|
def __rand__(self, o):
|
||||||
|
print("__rand__ called", o)
|
||||||
|
return f'{o} & RAdder()'
|
||||||
|
|
||||||
|
def __rxor__(self, o):
|
||||||
|
print("__rxor__ called", o)
|
||||||
|
return f'{o} ^ RAdder()'
|
||||||
|
|
||||||
|
def __rmod__(self, o):
|
||||||
|
print("__rmod__ called", o)
|
||||||
|
return f'{o} % RAdder()'
|
||||||
|
|
||||||
|
print(42 + RAdder())
|
||||||
|
print(42 * RAdder())
|
||||||
|
#print(42 / RAdder())
|
||||||
|
print(42 ** RAdder())
|
||||||
|
print(42 >> RAdder())
|
||||||
|
print(42 << RAdder())
|
||||||
|
print(42 | RAdder())
|
||||||
|
print(42 & RAdder())
|
||||||
|
print(42 ^ RAdder())
|
||||||
|
print(42 % RAdder())
|
18
test/testReflectedBinaryOperators.krk.expect
Normal file
18
test/testReflectedBinaryOperators.krk.expect
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
__radd__ called 42
|
||||||
|
42 + RAdder()
|
||||||
|
__rmul__ called 42
|
||||||
|
42 * RAdder()
|
||||||
|
__rpow__ called 42
|
||||||
|
42 ** RAdder()
|
||||||
|
__rrshift__ called 42
|
||||||
|
42 >> RAdder()
|
||||||
|
__rlshift__ called 42
|
||||||
|
42 << RAdder()
|
||||||
|
__ror__ called 42
|
||||||
|
42 | RAdder()
|
||||||
|
__rand__ called 42
|
||||||
|
42 & RAdder()
|
||||||
|
__rxor__ called 42
|
||||||
|
42 ^ RAdder()
|
||||||
|
__rmod__ called 42
|
||||||
|
42 % RAdder()
|
Loading…
Reference in New Issue
Block a user