From 70d5f1b2b7865f03f6f2bff8df2db9461268042c Mon Sep 17 00:00:00 2001 From: "K. Lange" Date: Tue, 30 Mar 2021 16:42:29 +0900 Subject: [PATCH] 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. --- src/compiler.c | 4 +- src/kuroko/chunk.h | 4 +- src/kuroko/value.h | 5 + src/kuroko/vm.h | 1 + src/obj_numeric.c | 13 +++ src/obj_str.c | 60 ++++------- src/opcodes.h | 2 + src/vm.c | 102 +++++++++++------- test/testComparisonOperatorFallbacks.krk | 41 +++++++ ...testComparisonOperatorFallbacks.krk.expect | 33 ++++++ test/testReflectedBinaryOperators.krk | 51 +++++++++ test/testReflectedBinaryOperators.krk.expect | 18 ++++ 12 files changed, 257 insertions(+), 77 deletions(-) create mode 100644 test/testComparisonOperatorFallbacks.krk create mode 100644 test/testComparisonOperatorFallbacks.krk.expect create mode 100644 test/testReflectedBinaryOperators.krk create mode 100644 test/testReflectedBinaryOperators.krk.expect diff --git a/src/compiler.c b/src/compiler.c index 6bdfce9..037a27b 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -527,9 +527,9 @@ static void compareChained(int inner) { case TOKEN_BANG_EQUAL: emitBytes(OP_EQUAL, OP_NOT); break; case TOKEN_EQUAL_EQUAL: emitByte(OP_EQUAL); 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_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; diff --git a/src/kuroko/chunk.h b/src/kuroko/chunk.h index daa985a..4398d5e 100644 --- a/src/kuroko/chunk.h +++ b/src/kuroko/chunk.h @@ -68,7 +68,9 @@ typedef enum { OP_ANNOTATE, OP_BEGIN_FINALLY, OP_END_FINALLY, - /* current highest: 45 */ + OP_GREATER_EQUAL, + OP_LESS_EQUAL, + /* current highest: 47 */ OP_CALL = 64, OP_CLASS, diff --git a/src/kuroko/value.h b/src/kuroko/value.h index 4752d74..ce11ec2 100644 --- a/src/kuroko/value.h +++ b/src/kuroko/value.h @@ -29,6 +29,7 @@ typedef enum { KRK_VAL_NONE = 0xFFFF, KRK_VAL_KWARGS = 0x7FFC, KRK_VAL_OBJECT = 0x7FFD, + KRK_VAL_NOTIMPL = 0x7FFE, } KrkValueType; #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_KWARGS ((uint64_t)0x7FFC000000000000) /* 0..1100 */ #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_LOW ((uint64_t)0x0000FFFFFFFFFFFF) @@ -171,6 +173,7 @@ typedef union { } KrkValueDbl; #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 INTEGER_VAL(value) ((KrkValue)((uint32_t)(value) | KRK_VAL_MASK_INTEGER)) #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_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_OBJECT(value) ((KrkObj*)(uintptr_t)((value) & KRK_VAL_MASK_LOW)) #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_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_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 AS_HANDLER_TYPE(value) (AS_HANDLER(value) >> 16) diff --git a/src/kuroko/vm.h b/src/kuroko/vm.h index a85b585..9037c16 100644 --- a/src/kuroko/vm.h +++ b/src/kuroko/vm.h @@ -165,6 +165,7 @@ struct BaseClasses { 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 * generatorClass; /**< Generator object. */ + KrkClass * notImplClass; /**< NotImplementedType */ }; /** diff --git a/src/obj_numeric.c b/src/obj_numeric.c index 6e1bb72..0822b73 100644 --- a/src/obj_numeric.c +++ b/src/obj_numeric.c @@ -94,6 +94,12 @@ KRK_METHOD(NoneType,__str__,{ 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 #define BIND_METHOD(klass,method) do { krk_defineNative(& _ ## klass->methods, #method, _ ## klass ## _ ## method); } while (0) _noexport @@ -128,4 +134,11 @@ void _createAndBind_numericClasses(void) { BIND_METHOD(NoneType, __str__); krk_defineNative(&_NoneType->methods, "__repr__", FUNC_NAME(NoneType,__str__)); 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()); } diff --git a/src/obj_str.c b/src/obj_str.c index aff0a38..346a783 100644 --- a/src/obj_str.c +++ b/src/obj_str.c @@ -434,45 +434,27 @@ KRK_METHOD(str,rstrip,{ return _string_strip_shared(argc,argv,2); }) -KRK_METHOD(str,__lt__,{ - 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); +#define strCompare(name,lop,iop,rop) \ + KRK_METHOD(str,name,{ \ + METHOD_TAKES_EXACTLY(1); \ + if (!IS_STRING(argv[1])) { \ + return NOTIMPL_VAL(); \ + } \ + 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; - 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(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)); -}) +strCompare(__gt__,>,<,>) +strCompare(__lt__,<,>,<) +strCompare(__ge__,>,<,>=) +strCompare(__le__,<,>,<=) /** TODO but throw a more descriptive error for now */ KRK_METHOD(str,__mod__,{ @@ -903,6 +885,8 @@ void _createAndBind_strClass(void) { BIND_METHOD(str,__contains__); BIND_METHOD(str,__lt__); BIND_METHOD(str,__gt__); + BIND_METHOD(str,__le__); + BIND_METHOD(str,__ge__); BIND_METHOD(str,__mod__); BIND_METHOD(str,__repr__); BIND_METHOD(str,__str__); diff --git a/src/opcodes.h b/src/opcodes.h index bd12e3b..f9a3308 100644 --- a/src/opcodes.h +++ b/src/opcodes.h @@ -42,6 +42,8 @@ SIMPLE(OP_YIELD) SIMPLE(OP_ANNOTATE) SIMPLE(OP_BEGIN_FINALLY) SIMPLE(OP_END_FINALLY) +SIMPLE(OP_GREATER_EQUAL) +SIMPLE(OP_LESS_EQUAL) CONSTANT(OP_DEFINE_GLOBAL,(void)0) CONSTANT(OP_CONSTANT,(void)0) CONSTANT(OP_GET_GLOBAL,(void)0) diff --git a/src/vm.c b/src/vm.c index 7d9226c..afbc4c1 100644 --- a/src/vm.c +++ b/src/vm.c @@ -463,6 +463,8 @@ inline KrkClass * krk_getType(KrkValue of) { return vm.baseClasses->boolClass; case KRK_VAL_NONE: return vm.baseClasses->noneTypeClass; + case KRK_VAL_NOTIMPL: + return vm.baseClasses->notImplClass; case KRK_VAL_OBJECT: switch (AS_OBJECT(of)->type) { case KRK_OBJ_CLASS: @@ -1379,32 +1381,56 @@ const char * krk_typeName(KrkValue value) { return krk_getType(value)->name->chars; } -static KrkValue tryBind(const char * name, KrkValue a, KrkValue b, const char * operator, const char * msg) { - krk_push(b); - krk_push(a); - KrkClass * type = krk_getType(a); +static KrkValue tryBind(const char * name, KrkValue a, KrkValue b, const char * operator, const char * msg, const char * inverse) { + krk_currentThread.scratchSpace[0] = a; + krk_currentThread.scratchSpace[1] = b; + + /* Potential return value */ + KrkValue value = NONE_VAL(); KrkString * methodName = krk_copyString(name, strlen(name)); 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)) { - krk_swap(1); - krk_pop(); - krk_swap(1); + krk_push(b); value = krk_callSimple(krk_peek(1), 1, 1); - } - if (IS_KWARGS(value)) { - return krk_runtimeError(vm.exceptions->typeError, msg, operator, krk_typeName(a), krk_typeName(b)); + if (!IS_NOTIMPL(value)) goto _success; + krk_pop(); /* name */ } 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. */ -#define MAKE_BIN_OP(name,operator) \ +#define MAKE_BIN_OP(name,operator,inv) \ 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_FLOATING(a)) { \ @@ -1413,43 +1439,43 @@ static KrkValue tryBind(const char * name, KrkValue a, KrkValue b, const char * } else if (IS_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(sub,-) -MAKE_BIN_OP(mul,*) -MAKE_BIN_OP(div,/) +MAKE_BIN_OP(add,+,radd) +MAKE_BIN_OP(sub,-,ssub) +MAKE_BIN_OP(mul,*,rmul) +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) { \ - 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; * 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) { \ 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)); \ - 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) { \ 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(xor,^) -MAKE_BIT_OP_BOOL(and,&) -MAKE_BIT_OP(lshift,<<) -MAKE_BIT_OP(rshift,>>) -MAKE_BIT_OP(mod,%) /* not a bit op, but doesn't work on floating point */ +MAKE_BIT_OP_BOOL(or,|,ror) +MAKE_BIT_OP_BOOL(xor,^,rxor) +MAKE_BIT_OP_BOOL(and,&,rand) +MAKE_BIT_OP(lshift,<<,rlshift) +MAKE_BIT_OP(rshift,>>,rrshift) +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) { \ if (IS_INTEGER(a) && IS_INTEGER(b)) return BOOLEAN_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \ 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)) { \ 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(gt, >) +MAKE_COMPARATOR(lt, <, 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 @@ -2128,6 +2156,8 @@ _finishReturn: (void)0; } case OP_LESS: BINARY_OP(lt); 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_SUBTRACT: BINARY_OP(sub) case OP_MULTIPLY: BINARY_OP(mul) diff --git a/test/testComparisonOperatorFallbacks.krk b/test/testComparisonOperatorFallbacks.krk new file mode 100644 index 0000000..a6622de --- /dev/null +++ b/test/testComparisonOperatorFallbacks.krk @@ -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()) diff --git a/test/testComparisonOperatorFallbacks.krk.expect b/test/testComparisonOperatorFallbacks.krk.expect new file mode 100644 index 0000000..7a4944a --- /dev/null +++ b/test/testComparisonOperatorFallbacks.krk.expect @@ -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 diff --git a/test/testReflectedBinaryOperators.krk b/test/testReflectedBinaryOperators.krk new file mode 100644 index 0000000..57d6c3d --- /dev/null +++ b/test/testReflectedBinaryOperators.krk @@ -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()) diff --git a/test/testReflectedBinaryOperators.krk.expect b/test/testReflectedBinaryOperators.krk.expect new file mode 100644 index 0000000..d65d90c --- /dev/null +++ b/test/testReflectedBinaryOperators.krk.expect @@ -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()