From 6613db6cd4250c9321b847f7012f733865f40b98 Mon Sep 17 00:00:00 2001 From: "K. Lange" Date: Tue, 3 May 2022 15:24:40 +0900 Subject: [PATCH] Implement slice objects, slice stepping This is an initial implementation of slice stepping and slice objects. The __getslice__, __setslice__, and __delslice__ methods have been removed. Slice expressions are now turned into slice objects with the OP_SLICE instruction. Slice objects have a start, end, and step, all of which default to None. Slice objects are passed to __getitem__, et al., as a normal parameter. Support for slices in list.__getitem__, str.__getitem__, and bytes.__getitem__ has been implemented. --- src/compiler.c | 121 +++++++++++----------- src/kuroko/chunk.h | 5 +- src/kuroko/object.h | 13 ++- src/kuroko/util.h | 10 ++ src/kuroko/vm.h | 7 ++ src/obj_bytes.c | 110 ++++++++++---------- src/obj_dict.c | 5 +- src/obj_list.c | 199 +++++++++++++++++++++--------------- src/obj_slice.c | 181 ++++++++++++++++++++++++++++++++ src/obj_str.c | 106 +++++++++---------- src/obj_tuple.c | 40 +++++++- src/opcodes.h | 4 +- src/private.h | 1 + src/table.c | 3 +- src/vendor/rline.c | 2 +- src/vm.c | 42 ++------ test/testSlicers.krk | 34 ++++++ test/testSlicers.krk.expect | 26 +++++ 18 files changed, 614 insertions(+), 295 deletions(-) create mode 100644 src/obj_slice.c create mode 100644 test/testSlicers.krk create mode 100644 test/testSlicers.krk.expect diff --git a/src/compiler.c b/src/compiler.c index c39a49e..c8b6593 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -861,75 +861,78 @@ static void expression(void) { parsePrecedence(PREC_CAN_ASSIGN); } -static void getitem(int exprType) { +static void sliceExpression(void) { int isSlice = 0; if (match(TOKEN_COLON)) { emitByte(OP_NONE); isSlice = 1; } else { - parsePrecedence(PREC_COMMA); + parsePrecedence(PREC_CAN_ASSIGN); } if (isSlice || match(TOKEN_COLON)) { - if (isSlice && match(TOKEN_COLON)) { - error("Step value not supported in slice. (GH-11)"); + /* We have the start value, which is either something or None */ + if (check(TOKEN_RIGHT_SQUARE) || check(TOKEN_COMMA)) { + /* foo[x:] */ + emitByte(OP_NONE); + EMIT_OPERAND_OP(OP_SLICE, 2); + } else { + if (check(TOKEN_COLON)) { + /* foo[x::... */ + emitByte(OP_NONE); + } else { + /* foo[x:e... */ + parsePrecedence(PREC_CAN_ASSIGN); + } + if (match(TOKEN_COLON) && !check(TOKEN_RIGHT_SQUARE) && !check(TOKEN_COMMA)) { + /* foo[x:e:s] */ + parsePrecedence(PREC_CAN_ASSIGN); + EMIT_OPERAND_OP(OP_SLICE, 3); + } else { + /* foo[x:e] */ + EMIT_OPERAND_OP(OP_SLICE, 2); + } + } + } +} + +static void getitem(int exprType) { + + sliceExpression(); + + if (match(TOKEN_COMMA)) { + size_t argCount = 1; + if (!check(TOKEN_RIGHT_SQUARE)) { + do { + sliceExpression(); + argCount++; + } while (match(TOKEN_COMMA) && !check(TOKEN_RIGHT_SQUARE)); + } + EMIT_OPERAND_OP(OP_TUPLE, argCount); + } + + consume(TOKEN_RIGHT_SQUARE, "Expected ']' after index."); + if (exprType == EXPR_ASSIGN_TARGET) { + if (matchComplexEnd()) { + EMIT_OPERAND_OP(OP_DUP, 2); + emitByte(OP_INVOKE_SETTER); + emitByte(OP_POP); return; } - if (match(TOKEN_RIGHT_SQUARE)) { - emitByte(OP_NONE); - } else { - parsePrecedence(PREC_COMMA); - consume(TOKEN_RIGHT_SQUARE, "Expected ']' after slice."); - } - if (exprType == EXPR_ASSIGN_TARGET) { - if (matchComplexEnd()) { - EMIT_OPERAND_OP(OP_DUP, 3); - emitByte(OP_INVOKE_SETSLICE); - emitByte(OP_POP); - return; - } - exprType = EXPR_NORMAL; - } - if (exprType == EXPR_CAN_ASSIGN && (match(TOKEN_EQUAL))) { - parsePrecedence(PREC_ASSIGNMENT); - emitByte(OP_INVOKE_SETSLICE); - } else if (exprType ==EXPR_CAN_ASSIGN && matchAssignment()) { - /* o s e */ - emitBytes(OP_DUP, 2); /* o s e o */ - emitBytes(OP_DUP, 2); /* o s e o s */ - emitBytes(OP_DUP, 2); /* o s e o s e */ - emitByte(OP_INVOKE_GETSLICE); /* o s e v */ - assignmentValue(); - emitByte(OP_INVOKE_SETSLICE); - } else if (exprType == EXPR_DEL_TARGET && checkEndOfDel()) { - emitByte(OP_INVOKE_DELSLICE); - } else { - emitByte(OP_INVOKE_GETSLICE); - } + exprType = EXPR_NORMAL; + } + if (exprType == EXPR_CAN_ASSIGN && match(TOKEN_EQUAL)) { + parsePrecedence(PREC_ASSIGNMENT); + emitByte(OP_INVOKE_SETTER); + } else if (exprType == EXPR_CAN_ASSIGN && matchAssignment()) { + emitBytes(OP_DUP, 1); /* o e o */ + emitBytes(OP_DUP, 1); /* o e o e */ + emitByte(OP_INVOKE_GETTER); /* o e v */ + assignmentValue(); /* o e v a */ + emitByte(OP_INVOKE_SETTER); /* r */ + } else if (exprType == EXPR_DEL_TARGET && checkEndOfDel()) { + emitByte(OP_INVOKE_DELETE); } else { - consume(TOKEN_RIGHT_SQUARE, "Expected ']' after index."); - if (exprType == EXPR_ASSIGN_TARGET) { - if (matchComplexEnd()) { - EMIT_OPERAND_OP(OP_DUP, 2); - emitByte(OP_INVOKE_SETTER); - emitByte(OP_POP); - return; - } - exprType = EXPR_NORMAL; - } - if (exprType == EXPR_CAN_ASSIGN && match(TOKEN_EQUAL)) { - parsePrecedence(PREC_ASSIGNMENT); - emitByte(OP_INVOKE_SETTER); - } else if (exprType == EXPR_CAN_ASSIGN && matchAssignment()) { - emitBytes(OP_DUP, 1); /* o e o */ - emitBytes(OP_DUP, 1); /* o e o e */ - emitByte(OP_INVOKE_GETTER); /* o e v */ - assignmentValue(); /* o e v a */ - emitByte(OP_INVOKE_SETTER); /* r */ - } else if (exprType == EXPR_DEL_TARGET && checkEndOfDel()) { - emitByte(OP_INVOKE_DELETE); - } else { - emitByte(OP_INVOKE_GETTER); - } + emitByte(OP_INVOKE_GETTER); } } diff --git a/src/kuroko/chunk.h b/src/kuroko/chunk.h index 9fadd91..d06075a 100644 --- a/src/kuroko/chunk.h +++ b/src/kuroko/chunk.h @@ -40,10 +40,7 @@ typedef enum { OP_GREATER, OP_INHERIT, OP_INVOKE_DELETE, - OP_INVOKE_DELSLICE, - OP_INVOKE_GETSLICE, OP_INVOKE_GETTER, - OP_INVOKE_SETSLICE, OP_INVOKE_SETTER, OP_IS, OP_LESS, @@ -106,6 +103,7 @@ typedef enum { OP_MAKE_DICT, OP_MAKE_SET, OP_REVERSE, + OP_SLICE, /* Two opcode instructions */ OP_JUMP_IF_FALSE_OR_POP, @@ -149,6 +147,7 @@ typedef enum { OP_MAKE_DICT_LONG, OP_MAKE_SET_LONG, OP_REVERSE_LONG, + OP_SLICE_LONG, } KrkOpCode; /** diff --git a/src/kuroko/object.h b/src/kuroko/object.h index 21fc54f..d151d9f 100644 --- a/src/kuroko/object.h +++ b/src/kuroko/object.h @@ -190,7 +190,6 @@ typedef struct KrkClass { KrkObj * _getter; /**< @brief @c %__getitem__ Called when an instance is subscripted */ KrkObj * _setter; /**< @brief @c %__setitem__ Called when a subscripted instance is assigned to */ - KrkObj * _getslice; /**< @brief @c %__getslice__ Called when a slice is used with a subscript access */ KrkObj * _reprer; /**< @brief @c %__repr__ Called to create a reproducible string representation of an instance */ KrkObj * _tostr; /**< @brief @c %__str__ Called to produce a string from an instance */ KrkObj * _call; /**< @brief @c %__call__ Called when an instance is called like a function */ @@ -203,8 +202,6 @@ typedef struct KrkClass { KrkObj * _iter; /**< @brief @c %__iter__ Called by `for ... in ...`, etc. */ KrkObj * _getattr; /**< @brief @c %__getattr__ Overrides normal behavior for attribute access */ KrkObj * _dir; /**< @brief @c %__dir__ Overrides normal behavior for @c dir() */ - KrkObj * _setslice; /**< @brief @c %__setslice__ Called when a slice subscript is an assignment target */ - KrkObj * _delslice; /**< @brief @c %__delslice__ Called when a slice subscript is a `del` target */ KrkObj * _contains; /**< @brief @c %__contains__ Called to resolve `in` (as a binary operator) */ KrkObj * _descget; /**< @brief @c %__get__ Called when a descriptor object is bound as a property */ KrkObj * _descset; /**< @brief @c %__set__ Called when a descriptor object is assigned to as a property */ @@ -336,6 +333,16 @@ struct KrkModule { #endif }; +/** + * @extends KrkInstance + */ +struct KrkSlice { + KrkInstance inst; + KrkValue start; + KrkValue end; + KrkValue step; +}; + /** * @brief Yield ownership of a C string to the GC and obtain a string object. * @memberof KrkString diff --git a/src/kuroko/util.h b/src/kuroko/util.h index c4775c2..944f78f 100644 --- a/src/kuroko/util.h +++ b/src/kuroko/util.h @@ -213,6 +213,9 @@ static inline KrkValue discardStringBuilder(struct StringBuilder * sb) { #define IS_bytearray(o) (krk_isInstanceOf(o,vm.baseClasses->bytearrayClass)) #define AS_bytearray(o) ((struct ByteArray*)AS_INSTANCE(o)) +#define IS_slice(o) krk_isInstanceOf(o,vm.baseClasses->sliceClass) +#define AS_slice(o) ((struct KrkSlice*)AS_INSTANCE(o)) + #ifndef unpackError #define unpackError(fromInput) return krk_runtimeError(vm.exceptions->typeError, "'%s' object is not iterable", krk_typeName(fromInput)); #endif @@ -304,3 +307,10 @@ static inline void _setDoc_native(KrkNative * thing, const char * text, size_t s #define BUILTIN_FUNCTION(name, func, docStr) KRK_DOC(krk_defineNative(&vm.builtins->fields, name, func), docStr) +extern int krk_extractSlicer(const char * _method_name, KrkValue slicerVal, krk_integer_type count, krk_integer_type *start, krk_integer_type *end, krk_integer_type *step); +#define KRK_SLICER(arg,count) \ + krk_integer_type start; \ + krk_integer_type end; \ + krk_integer_type step; \ + if (krk_extractSlicer(_method_name, arg, count, &start, &end, &step)) + diff --git a/src/kuroko/vm.h b/src/kuroko/vm.h index 5206a93..9ec5f0d 100644 --- a/src/kuroko/vm.h +++ b/src/kuroko/vm.h @@ -168,6 +168,7 @@ struct BaseClasses { KrkClass * notImplClass; /**< NotImplementedType */ KrkClass * bytearrayClass; /**< Mutable array of bytes */ KrkClass * dictvaluesClass; /**< Iterator over values of a dict */ + KrkClass * sliceClass; /**< Slice object */ }; /** @@ -619,6 +620,12 @@ extern KrkValue krk_tuple_of(int argc, KrkValue argv[], int hasKw); */ extern KrkValue krk_set_of(int argc, KrkValue argv[], int hasKw); +/** + * @brief Create a slice object. + * @memberof KrkSlice + */ +extern KrkValue krk_slice_of(int argc, KrkValue argv[], int hasKw); + /** * @brief Call a callable on the stack with @p argCount arguments. * diff --git a/src/obj_bytes.c b/src/obj_bytes.c index 36c6836..77548c8 100644 --- a/src/obj_bytes.c +++ b/src/obj_bytes.c @@ -123,14 +123,37 @@ KRK_METHOD(bytes,__repr__,{ KRK_METHOD(bytes,__getitem__,{ METHOD_TAKES_EXACTLY(1); - CHECK_ARG(1,int,krk_integer_type,asInt); - if (asInt < 0) asInt += (long)self->length; - if (asInt < 0 || asInt >= (long)self->length) { - return krk_runtimeError(vm.exceptions->indexError, "bytes index out of range: %d", (int)asInt); + if (IS_INTEGER(argv[1])) { + CHECK_ARG(1,int,krk_integer_type,asInt); + + if (asInt < 0) asInt += (long)self->length; + if (asInt < 0 || asInt >= (long)self->length) { + return krk_runtimeError(vm.exceptions->indexError, "bytes index out of range: %d", (int)asInt); + } + + return INTEGER_VAL(self->bytes[asInt]); + + } else if (IS_slice(argv[1])) { + KRK_SLICER(argv[1],self->length) { + return NONE_VAL(); + } + + if (step == 1) { + krk_integer_type len = end - start; + return OBJECT_VAL(krk_newBytes(len, &self->bytes[start])); + } else { + struct StringBuilder sb = {0}; + krk_integer_type i = start; + while ((step < 0) ? (i > end) : (i < end)) { + pushStringBuilder(&sb, self->bytes[i]); + i += step; + } + return finishStringBuilderBytes(&sb); + } + } else { + return TYPE_ERROR(int or slice, argv[1]); } - - return INTEGER_VAL(self->bytes[asInt]); }) KRK_METHOD(bytes,__len__,{ @@ -186,25 +209,6 @@ KRK_METHOD(bytes,__add__,{ return finishStringBuilderBytes(&sb); }) -#define BYTES_WRAP_SOFT(val) \ - if (val < 0) val += self->length; \ - if (val < 0) val = 0; \ - if (val > (krk_integer_type)self->length) val = self->length - -KRK_METHOD(bytes,__getslice__,{ - METHOD_TAKES_EXACTLY(2); - if (!(IS_INTEGER(argv[1]) || IS_NONE(argv[1]))) return TYPE_ERROR(int or None, argv[1]); - if (!(IS_INTEGER(argv[2]) || IS_NONE(argv[2]))) return TYPE_ERROR(int or None, argv[2]); - krk_integer_type start = IS_NONE(argv[1]) ? 0 : AS_INTEGER(argv[1]); - krk_integer_type end = IS_NONE(argv[2]) ? (krk_integer_type)self->length : AS_INTEGER(argv[2]); - BYTES_WRAP_SOFT(start); - BYTES_WRAP_SOFT(end); - if (end < start) end = start; - krk_integer_type len = end - start; - - return OBJECT_VAL(krk_newBytes(len, &self->bytes[start])); -}) - FUNC_SIG(bytesiterator,__init__); KRK_METHOD(bytes,__iter__,{ @@ -295,14 +299,37 @@ KRK_METHOD(bytearray,__repr__,{ KRK_METHOD(bytearray,__getitem__,{ METHOD_TAKES_EXACTLY(1); - CHECK_ARG(1,int,krk_integer_type,asInt); - if (asInt < 0) asInt += (long)AS_BYTES(self->actual)->length; - if (asInt < 0 || asInt >= (long)AS_BYTES(self->actual)->length) { - return krk_runtimeError(vm.exceptions->indexError, "bytearray index out of range: %d", (int)asInt); + if (IS_INTEGER(argv[1])) { + CHECK_ARG(1,int,krk_integer_type,asInt); + + if (asInt < 0) asInt += (long)AS_BYTES(self->actual)->length; + if (asInt < 0 || asInt >= (long)AS_BYTES(self->actual)->length) { + return krk_runtimeError(vm.exceptions->indexError, "bytearray index out of range: %d", (int)asInt); + } + + return INTEGER_VAL(AS_BYTES(self->actual)->bytes[asInt]); + } else if (IS_slice(argv[1])) { + KRK_SLICER(argv[1],AS_BYTES(self->actual)->length) { + return NONE_VAL(); + } + + if (step == 1) { + krk_integer_type len = end - start; + return OBJECT_VAL(krk_newBytes(len, &AS_BYTES(self->actual)->bytes[start])); + } else { + struct StringBuilder sb = {0}; + krk_integer_type i = start; + while ((step < 0) ? (i > end) : (i < end)) { + pushStringBuilder(&sb, AS_BYTES(self->actual)->bytes[i]); + i += step; + } + return finishStringBuilderBytes(&sb); + } + + } else { + return TYPE_ERROR(int or slice, argv[1]); } - - return INTEGER_VAL(AS_BYTES(self->actual)->bytes[asInt]); }) KRK_METHOD(bytearray,__setitem__,{ @@ -337,25 +364,6 @@ KRK_METHOD(bytearray,decode,{ return OBJECT_VAL(krk_copyString((char*)AS_BYTES(self->actual)->bytes, AS_BYTES(self->actual)->length)); }) -#define BYTEARRAY_WRAP_SOFT(val) \ - if (val < 0) val += AS_BYTES(self->actual)->length; \ - if (val < 0) val = 0; \ - if (val > (krk_integer_type)AS_BYTES(self->actual)->length) val = AS_BYTES(self->actual)->length - -KRK_METHOD(bytearray,__getslice__,{ - METHOD_TAKES_EXACTLY(2); - if (!(IS_INTEGER(argv[1]) || IS_NONE(argv[1]))) return TYPE_ERROR(int or None, argv[1]); - if (!(IS_INTEGER(argv[2]) || IS_NONE(argv[2]))) return TYPE_ERROR(int or None, argv[2]); - krk_integer_type start = IS_NONE(argv[1]) ? 0 : AS_INTEGER(argv[1]); - krk_integer_type end = IS_NONE(argv[2]) ? (krk_integer_type)AS_BYTES(self->actual)->length : AS_INTEGER(argv[2]); - BYTEARRAY_WRAP_SOFT(start); - BYTEARRAY_WRAP_SOFT(end); - if (end < start) end = start; - krk_integer_type len = end - start; - - return OBJECT_VAL(krk_newBytes(len, &AS_BYTES(self->actual)->bytes[start])); -}) - KRK_METHOD(bytearray,__iter__,{ METHOD_TAKES_NONE(); KrkInstance * output = krk_newInstance(vm.baseClasses->bytesiteratorClass); @@ -380,7 +388,6 @@ void _createAndBind_bytesClass(void) { BIND_METHOD(bytes,__len__); BIND_METHOD(bytes,__contains__); BIND_METHOD(bytes,__getitem__); - BIND_METHOD(bytes,__getslice__); BIND_METHOD(bytes,__eq__); BIND_METHOD(bytes,__add__); BIND_METHOD(bytes,__iter__); @@ -408,7 +415,6 @@ void _createAndBind_bytesClass(void) { BIND_METHOD(bytearray,__contains__); BIND_METHOD(bytearray,__getitem__); BIND_METHOD(bytearray,__setitem__); - BIND_METHOD(bytearray,__getslice__); BIND_METHOD(bytearray,__eq__); BIND_METHOD(bytearray,__iter__); BIND_METHOD(bytearray,decode); diff --git a/src/obj_dict.c b/src/obj_dict.c index b33ad1b..55d9349 100644 --- a/src/obj_dict.c +++ b/src/obj_dict.c @@ -88,8 +88,10 @@ KRK_METHOD(dict,__init__,{ KRK_METHOD(dict,__getitem__,{ METHOD_TAKES_EXACTLY(1); KrkValue out; - if (!krk_tableGet(&self->entries, argv[1], &out)) + if (!krk_tableGet(&self->entries, argv[1], &out)) { + if (!IS_NONE(krk_currentThread.currentException)) return NONE_VAL(); KEY_ERROR(argv[1]); + } return out; }) @@ -112,6 +114,7 @@ KRK_METHOD(dict,__or__,{ KRK_METHOD(dict,__delitem__,{ METHOD_TAKES_EXACTLY(1); if (!krk_tableDelete(&self->entries, argv[1])) { + if (!IS_NONE(krk_currentThread.currentException)) return NONE_VAL(); KEY_ERROR(argv[1]); } }) diff --git a/src/obj_list.c b/src/obj_list.c index a02ddc9..d8022f2 100644 --- a/src/obj_list.c +++ b/src/obj_list.c @@ -49,22 +49,51 @@ KrkValue krk_list_of(int argc, KrkValue argv[], int hasKw) { KRK_METHOD(list,__getitem__,{ METHOD_TAKES_EXACTLY(1); - CHECK_ARG(1,int,krk_integer_type,index); - if (vm.globalFlags & KRK_GLOBAL_THREADS) pthread_rwlock_rdlock(&self->rwlock); - LIST_WRAP_INDEX(); - KrkValue result = self->values.values[index]; - if (vm.globalFlags & KRK_GLOBAL_THREADS) pthread_rwlock_unlock(&self->rwlock); - return result; -}) + if (IS_INTEGER(argv[1])) { + CHECK_ARG(1,int,krk_integer_type,index); + if (vm.globalFlags & KRK_GLOBAL_THREADS) pthread_rwlock_rdlock(&self->rwlock); + LIST_WRAP_INDEX(); + KrkValue result = self->values.values[index]; + if (vm.globalFlags & KRK_GLOBAL_THREADS) pthread_rwlock_unlock(&self->rwlock); + return result; + } else if (IS_slice(argv[1])) { + pthread_rwlock_rdlock(&self->rwlock); -KRK_METHOD(list,__setitem__,{ - METHOD_TAKES_EXACTLY(2); - CHECK_ARG(1,int,krk_integer_type,index); - if (vm.globalFlags & KRK_GLOBAL_THREADS) pthread_rwlock_rdlock(&self->rwlock); - LIST_WRAP_INDEX(); - self->values.values[index] = argv[2]; - if (vm.globalFlags & KRK_GLOBAL_THREADS) pthread_rwlock_unlock(&self->rwlock); - return argv[2]; + KRK_SLICER(argv[1],self->values.count) { + pthread_rwlock_unlock(&self->rwlock); + return NONE_VAL(); + } + + if (step == 1) { + krk_integer_type len = end - start; + KrkValue result = krk_list_of(len, &AS_LIST(argv[0])->values[start], 0); + pthread_rwlock_unlock(&self->rwlock); + return result; + } else { + /* iterate and push */ + krk_push(NONE_VAL()); + krk_integer_type len = 0; + krk_integer_type i = start; + while ((step < 0) ? (i > end) : (i < end)) { + krk_push(self->values.values[i]); + len++; + i += step; + } + + /* make into a list */ + KrkValue result = krk_list_of(len, &krk_currentThread.stackTop[-len], 0); + krk_currentThread.stackTop[-len-1] = result; + while (len) { + krk_pop(); + len--; + } + + pthread_rwlock_unlock(&self->rwlock); + return krk_pop(); + } + } else { + return TYPE_ERROR(int or slice,argv[1]); + } }) KRK_METHOD(list,__eq__,{ @@ -196,70 +225,6 @@ KRK_METHOD(list,__contains__,{ return BOOLEAN_VAL(0); }) -KRK_METHOD(list,__getslice__,{ - METHOD_TAKES_EXACTLY(2); - if (!(IS_INTEGER(argv[1]) || IS_NONE(argv[1]))) return TYPE_ERROR(int or None, argv[1]); - if (!(IS_INTEGER(argv[2]) || IS_NONE(argv[2]))) return TYPE_ERROR(int or None, argv[2]); - pthread_rwlock_rdlock(&self->rwlock); - krk_integer_type start = IS_NONE(argv[1]) ? 0 : AS_INTEGER(argv[1]); - krk_integer_type end = IS_NONE(argv[2]) ? (krk_integer_type)self->values.count : AS_INTEGER(argv[2]); - LIST_WRAP_SOFT(start); - LIST_WRAP_SOFT(end); - if (end < start) end = start; - krk_integer_type len = end - start; - - KrkValue result = krk_list_of(len, &AS_LIST(argv[0])->values[start], 0); - pthread_rwlock_unlock(&self->rwlock); - return result; -}) - -FUNC_SIG(list,pop); -KRK_METHOD(list,__delslice__,{ - METHOD_TAKES_EXACTLY(2); - if (!(IS_INTEGER(argv[1]) || IS_NONE(argv[1]))) return TYPE_ERROR(int or None, argv[1]); - if (!(IS_INTEGER(argv[2]) || IS_NONE(argv[2]))) return TYPE_ERROR(int or None, argv[2]); - krk_integer_type start = IS_NONE(argv[1]) ? 0 : AS_INTEGER(argv[1]); - krk_integer_type end = IS_NONE(argv[2]) ? (krk_integer_type)self->values.count : AS_INTEGER(argv[2]); - LIST_WRAP_SOFT(start); - LIST_WRAP_SOFT(end); - if (end < start) end = start; - krk_integer_type len = end - start; - - while (len > 0) { - FUNC_NAME(list,pop)(2,(KrkValue[]){argv[0],INTEGER_VAL(start)},0); - len--; - } -}) - -KRK_METHOD(list,__setslice__,{ - METHOD_TAKES_EXACTLY(3); - if (!(IS_INTEGER(argv[1]) || IS_NONE(argv[1]))) return TYPE_ERROR(int or None, argv[1]); - if (!(IS_INTEGER(argv[2]) || IS_NONE(argv[2]))) return TYPE_ERROR(int or None, argv[2]); - if (!IS_list(argv[3])) return TYPE_ERROR(list,argv[3]); /* TODO other sequence types */ - krk_integer_type start = IS_NONE(argv[1]) ? 0 : AS_INTEGER(argv[1]); - krk_integer_type end = IS_NONE(argv[2]) ? (krk_integer_type)self->values.count : AS_INTEGER(argv[2]); - LIST_WRAP_SOFT(start); - LIST_WRAP_SOFT(end); - if (end < start) end = start; - krk_integer_type len = end - start; - - krk_integer_type newLen = (krk_integer_type)AS_LIST(argv[3])->count; - - for (krk_integer_type i = 0; (i < len && i < newLen); ++i) { - AS_LIST(argv[0])->values[start+i] = AS_LIST(argv[3])->values[i]; - } - - while (len < newLen) { - FUNC_NAME(list,insert)(3, (KrkValue[]){argv[0], INTEGER_VAL(start + len), AS_LIST(argv[3])->values[len]}, 0); - len++; - } - - while (newLen < len) { - FUNC_NAME(list,pop)(2, (KrkValue[]){argv[0], INTEGER_VAL(start + len - 1)}, 0); - len--; - } -}) - KRK_METHOD(list,pop,{ METHOD_TAKES_AT_MOST(1); pthread_rwlock_wrlock(&self->rwlock); @@ -285,6 +250,77 @@ KRK_METHOD(list,pop,{ } }) +KRK_METHOD(list,__setitem__,{ + METHOD_TAKES_EXACTLY(2); + if (IS_INTEGER(argv[1])) { + CHECK_ARG(1,int,krk_integer_type,index); + if (vm.globalFlags & KRK_GLOBAL_THREADS) pthread_rwlock_rdlock(&self->rwlock); + LIST_WRAP_INDEX(); + self->values.values[index] = argv[2]; + if (vm.globalFlags & KRK_GLOBAL_THREADS) pthread_rwlock_unlock(&self->rwlock); + return argv[2]; + } else if (IS_slice(argv[1])) { + if (!IS_list(argv[2])) { + return TYPE_ERROR(list,argv[2]); /* TODO other sequence types */ + } + + KRK_SLICER(argv[1],self->values.count) { + return NONE_VAL(); + } + + if (step != 1) { + return krk_runtimeError(vm.exceptions->valueError, "step value unsupported"); + } + + krk_integer_type len = end - start; + krk_integer_type newLen = (krk_integer_type)AS_LIST(argv[2])->count; + + for (krk_integer_type i = 0; (i < len && i < newLen); ++i) { + AS_LIST(argv[0])->values[start+i] = AS_LIST(argv[2])->values[i]; + } + + while (len < newLen) { + FUNC_NAME(list,insert)(3, (KrkValue[]){argv[0], INTEGER_VAL(start + len), AS_LIST(argv[2])->values[len]}, 0); + len++; + } + + while (newLen < len) { + FUNC_NAME(list,pop)(2, (KrkValue[]){argv[0], INTEGER_VAL(start + len - 1)}, 0); + len--; + } + + return OBJECT_VAL(self); + } else { + return TYPE_ERROR(int or slice, argv[1]); + } +}) + + +KRK_METHOD(list,__delitem__,{ + METHOD_TAKES_EXACTLY(1); + + if (IS_INTEGER(argv[1])) { + FUNC_NAME(list,pop)(2,(KrkValue[]){argv[0],INTEGER_VAL(argv[1])},0); + } else if (IS_slice(argv[1])) { + KRK_SLICER(argv[1],self->values.count) { + return NONE_VAL(); + } + + if (step != 1) { + return krk_runtimeError(vm.exceptions->valueError, "step value unsupported"); + } + + krk_integer_type len = end - start; + + while (len > 0) { + FUNC_NAME(list,pop)(2,(KrkValue[]){argv[0],INTEGER_VAL(start)},0); + len--; + } + } else { + return TYPE_ERROR(int or slice, argv[1]); + } +}) + KRK_METHOD(list,remove,{ METHOD_TAKES_EXACTLY(1); pthread_rwlock_wrlock(&self->rwlock); @@ -486,12 +522,10 @@ void _createAndBind_listClass(void) { BIND_METHOD(list,__eq__); BIND_METHOD(list,__getitem__); BIND_METHOD(list,__setitem__); + BIND_METHOD(list,__delitem__); BIND_METHOD(list,__len__); BIND_METHOD(list,__repr__); BIND_METHOD(list,__contains__); - BIND_METHOD(list,__getslice__); - BIND_METHOD(list,__delslice__); - BIND_METHOD(list,__setslice__); BIND_METHOD(list,__iter__); BIND_METHOD(list,__mul__); BIND_METHOD(list,__add__); @@ -546,7 +580,6 @@ void _createAndBind_listClass(void) { "@brief Sort the contents of a list.\n\n" "Performs an in-place sort of the elements in the list, returning @c None as a gentle reminder " "that the sort is in-place. If a sorted copy is desired, use @ref sorted instead."); - krk_defineNative(&list->methods, "__delitem__", FUNC_NAME(list,pop)); krk_defineNative(&list->methods, "__str__", FUNC_NAME(list,__repr__)); krk_defineNative(&list->methods, "__class_getitem__", KrkGenericAlias)->flags |= KRK_NATIVE_FLAGS_IS_CLASS_METHOD; krk_attachNamedValue(&list->methods, "__hash__", NONE_VAL()); diff --git a/src/obj_slice.c b/src/obj_slice.c new file mode 100644 index 0000000..4984ef6 --- /dev/null +++ b/src/obj_slice.c @@ -0,0 +1,181 @@ +#include +#include +#include +#include +#include +#include + +static void _slice_gcscan(KrkInstance * self) { + krk_markValue(((struct KrkSlice*)self)->start); + krk_markValue(((struct KrkSlice*)self)->end); + krk_markValue(((struct KrkSlice*)self)->step); +} + +KrkValue krk_slice_of(int argc, KrkValue argv[], int hasKw) { + KrkValue outSlice = OBJECT_VAL(krk_newInstance(vm.baseClasses->sliceClass)); + krk_push(outSlice); + + AS_slice(outSlice)->start = (argc > 0) ? argv[0] : NONE_VAL(); + AS_slice(outSlice)->end = (argc > 1) ? argv[1] : NONE_VAL(); + AS_slice(outSlice)->step = (argc > 2) ? argv[2] : NONE_VAL(); + + return krk_pop(); +} + +static inline krk_integer_type _wrap(krk_integer_type count, krk_integer_type val) { + if (val < 0) val += count; + if (val < 0) val = 0; + if (val > count) val = count; + return val; +} + +static inline krk_integer_type _wrapn(krk_integer_type count, krk_integer_type val) { + if (val < 0) val += count; + if (val < -1) val = -1; + if (val > count) val = count; + return val; +} + +int krk_extractSlicer(const char * _method_name, KrkValue slicerVal, krk_integer_type count, krk_integer_type *start, krk_integer_type *end, krk_integer_type *step) { + if (!(IS_slice(slicerVal))) { + TYPE_ERROR(slice, slicerVal); + return 1; + } + + struct KrkSlice * slicer = AS_slice(slicerVal); + + KrkValue _start = slicer->start; + KrkValue _end = slicer->end; + KrkValue _step = slicer->step; + + if (!(IS_INTEGER(_start) || IS_NONE(_start))) { + TYPE_ERROR(int or None, _start); + return 1; + } + + if (!(IS_INTEGER(_end) || IS_NONE(_end))) { + TYPE_ERROR(int or None, _end); + return 1; + } + + if (!(IS_INTEGER(_step) || IS_NONE(_step))) { + TYPE_ERROR(int or None, _step); + } + + if (count == 0) { + *start = 0; + *end = 0; + *step = 1; + return 0; + } + + /* First off, the step */ + *step = IS_NONE(_step) ? 1 : AS_INTEGER(_step); + + if (*step == 0) { + krk_runtimeError(vm.exceptions->valueError, "invalid 0 step"); + return 1; + } + + if (*step > 0) { + /* Normal step bounds */ + *start = _wrap(count, IS_NONE(_start) ? 0 : AS_INTEGER(_start)); + *end = _wrap(count, IS_NONE(_end) ? count : AS_INTEGER(_end)); + if (*end < *start) *end = *start; + } else { + *start = IS_NONE(_start) ? (count-1) : _wrap(count, AS_INTEGER(_start)); + if (*start >= count) *start = count -1; + *end = IS_NONE(_end) ? -1 : _wrapn(count, AS_INTEGER(_end)); + if (*end > *start) *end = *start; + } + + return 0; +} + +#define CURRENT_CTYPE struct KrkSlice * +#define CURRENT_NAME self + +KRK_METHOD(slice,__init__,{ + METHOD_TAKES_AT_LEAST(1); + METHOD_TAKES_AT_MOST(3); + + if (argc == 2) { + self->start = NONE_VAL(); + self->end = argv[1]; + self->step = NONE_VAL(); + } else { + self->start = argv[1]; + self->end = argv[2]; + if (argc > 3) { + self->step = argv[3]; + } else { + self->step = NONE_VAL(); + } + } + return argv[0]; +}) + +KRK_METHOD(slice,__repr__,{ + METHOD_TAKES_NONE(); + if (((KrkObj*)self)->flags & KRK_OBJ_FLAGS_IN_REPR) return OBJECT_VAL("slice(...)"); + ((KrkObj*)self)->flags |= KRK_OBJ_FLAGS_IN_REPR; + struct StringBuilder sb = {0}; + pushStringBuilderStr(&sb,"slice(",6); + + KrkClass * type; + KrkValue result; + + /* start */ + type = krk_getType(self->start); + krk_push(self->start); + result = krk_callDirect(type->_reprer, 1); + if (IS_STRING(result)) pushStringBuilderStr(&sb, AS_STRING(result)->chars, AS_STRING(result)->length); + pushStringBuilderStr(&sb,", ",2); + + /* end */ + type = krk_getType(self->end); + krk_push(self->end); + result = krk_callDirect(type->_reprer, 1); + if (IS_STRING(result)) pushStringBuilderStr(&sb, AS_STRING(result)->chars, AS_STRING(result)->length); + pushStringBuilderStr(&sb,", ",2); + + /* step */ + type = krk_getType(self->step); + krk_push(self->step); + result = krk_callDirect(type->_reprer, 1); + if (IS_STRING(result)) pushStringBuilderStr(&sb, AS_STRING(result)->chars, AS_STRING(result)->length); + + pushStringBuilder(&sb,')'); + ((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR); + return finishStringBuilder(&sb); +}) + +KRK_METHOD(slice,start,{ + ATTRIBUTE_NOT_ASSIGNABLE(); + return self->start; +}) + +KRK_METHOD(slice,end,{ + ATTRIBUTE_NOT_ASSIGNABLE(); + return self->end; +}) + +KRK_METHOD(slice,step,{ + ATTRIBUTE_NOT_ASSIGNABLE(); + return self->step; +}) + +_noexport +void _createAndBind_sliceClass(void) { + KrkClass * slice = ADD_BASE_CLASS(vm.baseClasses->sliceClass, "slice", vm.baseClasses->objectClass); + slice->allocSize = sizeof(struct KrkSlice); + slice->_ongcscan = _slice_gcscan; + BIND_METHOD(slice,__init__); + BIND_METHOD(slice,__repr__); + BIND_PROP(slice,start); + BIND_PROP(slice,end); + BIND_PROP(slice,step); + krk_defineNative(&slice->methods, "__str__", FUNC_NAME(slice,__repr__)); + krk_attachNamedValue(&slice->methods, "__hash__", NONE_VAL()); + krk_finalizeClass(slice); +} diff --git a/src/obj_str.c b/src/obj_str.c index 2aa2334..fb7b6d3 100644 --- a/src/obj_str.c +++ b/src/obj_str.c @@ -90,46 +90,6 @@ KRK_METHOD(str,__setitem__,{ return krk_runtimeError(vm.exceptions->typeError, "Strings are not mutable."); }) -/** - * Unlike in Python, we actually handle negative values here rather than - * somewhere else? I'm not even sure where Python does do it, but a quick - * says not if you call __getslice__ directly... - */ -KRK_METHOD(str,__getslice__,{ - METHOD_TAKES_EXACTLY(2); - if (!(IS_INTEGER(argv[1]) || IS_NONE(argv[1]))) - return TYPE_ERROR(int,argv[1]); - if (!(IS_INTEGER(argv[2]) || IS_NONE(argv[2]))) - return TYPE_ERROR(int,argv[2]); - /* bounds check */ - long start = IS_NONE(argv[1]) ? 0 : AS_INTEGER(argv[1]); - long end = IS_NONE(argv[2]) ? (long)self->codesLength : AS_INTEGER(argv[2]); - if (start < 0) start = self->codesLength + start; - if (start < 0) start = 0; - if (end < 0) end = self->codesLength + end; - if (start > (long)self->codesLength) start = self->codesLength; - if (end > (long)self->codesLength) end = self->codesLength; - if (end < start) end = start; - long len = end - start; - if (self->type == KRK_STRING_ASCII) { - return OBJECT_VAL(krk_copyString(self->chars + start, len)); - } else { - size_t offset = 0; - size_t length = 0; - /* Figure out where the UTF8 for this string starts. */ - krk_unicodeString(self); - for (long i = 0; i < start; ++i) { - uint32_t cp = KRK_STRING_FAST(self,i); - offset += CODEPOINT_BYTES(cp); - } - for (long i = start; i < end; ++i) { - uint32_t cp = KRK_STRING_FAST(self,i); - length += CODEPOINT_BYTES(cp); - } - return OBJECT_VAL(krk_copyString(self->chars + offset, length)); - } -}) - /* str.__int__(base=10) */ KRK_METHOD(str,__int__,{ METHOD_TAKES_AT_MOST(1); @@ -159,18 +119,61 @@ KRK_METHOD(str,__float__,{ KRK_METHOD(str,__getitem__,{ METHOD_TAKES_EXACTLY(1); - CHECK_ARG(1,int,krk_integer_type,asInt); - if (asInt < 0) asInt += (int)AS_STRING(argv[0])->codesLength; - if (asInt < 0 || asInt >= (int)AS_STRING(argv[0])->codesLength) { - return krk_runtimeError(vm.exceptions->indexError, "String index out of range: " PRIkrk_int, asInt); - } - if (self->type == KRK_STRING_ASCII) { - return OBJECT_VAL(krk_copyString(self->chars + asInt, 1)); + if (IS_INTEGER(argv[1])) { + CHECK_ARG(1,int,krk_integer_type,asInt); + if (asInt < 0) asInt += (int)AS_STRING(argv[0])->codesLength; + if (asInt < 0 || asInt >= (int)AS_STRING(argv[0])->codesLength) { + return krk_runtimeError(vm.exceptions->indexError, "String index out of range: " PRIkrk_int, asInt); + } + if (self->type == KRK_STRING_ASCII) { + return OBJECT_VAL(krk_copyString(self->chars + asInt, 1)); + } else { + krk_unicodeString(self); + unsigned char asbytes[5]; + size_t length = krk_codepointToBytes(KRK_STRING_FAST(self,asInt),(unsigned char*)&asbytes); + return OBJECT_VAL(krk_copyString((char*)&asbytes, length)); + } + } else if (IS_slice(argv[1])) { + KRK_SLICER(argv[1], self->codesLength) { + return NONE_VAL(); + } + + if (step == 1) { + long len = end - start; + if (self->type == KRK_STRING_ASCII) { + return OBJECT_VAL(krk_copyString(self->chars + start, len)); + } else { + size_t offset = 0; + size_t length = 0; + /* Figure out where the UTF8 for this string starts. */ + krk_unicodeString(self); + for (long i = 0; i < start; ++i) { + uint32_t cp = KRK_STRING_FAST(self,i); + offset += CODEPOINT_BYTES(cp); + } + for (long i = start; i < end; ++i) { + uint32_t cp = KRK_STRING_FAST(self,i); + length += CODEPOINT_BYTES(cp); + } + return OBJECT_VAL(krk_copyString(self->chars + offset, length)); + } + } else { + struct StringBuilder sb = {0}; + krk_unicodeString(self); + + unsigned char asbytes[5]; + krk_integer_type i = start; + + while ((step < 0) ? (i > end) : (i < end)) { + size_t length = krk_codepointToBytes(KRK_STRING_FAST(self,i),(unsigned char*)&asbytes); + pushStringBuilderStr(&sb, (char*)asbytes, length); + i += step; + } + + return finishStringBuilder(&sb); + } } else { - krk_unicodeString(self); - unsigned char asbytes[5]; - size_t length = krk_codepointToBytes(KRK_STRING_FAST(self,asInt),(unsigned char*)&asbytes); - return OBJECT_VAL(krk_copyString((char*)&asbytes, length)); + return TYPE_ERROR(int or slice, argv[1]); } }) @@ -885,7 +888,6 @@ void _createAndBind_strClass(void) { BIND_METHOD(str,__ord__); BIND_METHOD(str,__int__); BIND_METHOD(str,__float__); - BIND_METHOD(str,__getslice__); BIND_METHOD(str,__getitem__); BIND_METHOD(str,__setitem__); BIND_METHOD(str,__add__); diff --git a/src/obj_tuple.c b/src/obj_tuple.c index 5179ee2..c88a0b8 100644 --- a/src/obj_tuple.c +++ b/src/obj_tuple.c @@ -62,9 +62,43 @@ KRK_METHOD(tuple,__len__,{ KRK_METHOD(tuple,__getitem__,{ METHOD_TAKES_EXACTLY(1); - CHECK_ARG(1,int,krk_integer_type,index); - TUPLE_WRAP_INDEX(); - return self->values.values[index]; + if (IS_INTEGER(argv[1])) { + CHECK_ARG(1,int,krk_integer_type,index); + TUPLE_WRAP_INDEX(); + return self->values.values[index]; + } else if (IS_slice(argv[1])) { + KRK_SLICER(argv[1],self->values.count) { + return NONE_VAL(); + } + + if (step == 1) { + krk_integer_type len = end - start; + KrkValue result = krk_tuple_of(len, &self->values.values[start], 0); + return result; + } else { + /* iterate and push */ + krk_push(NONE_VAL()); + krk_integer_type len = 0; + krk_integer_type i = start; + while ((step < 0) ? (i > end) : (i < end)) { + krk_push(self->values.values[i]); + len++; + i += step; + } + + /* make into a list */ + KrkValue result = krk_tuple_of(len, &krk_currentThread.stackTop[-len], 0); + krk_currentThread.stackTop[-len-1] = result; + while (len) { + krk_pop(); + len--; + } + + return krk_pop(); + } + } else { + return TYPE_ERROR(int or slice, argv[1]); + } }) KRK_METHOD(tuple,__eq__,{ diff --git a/src/opcodes.h b/src/opcodes.h index f536aa3..7e08e16 100644 --- a/src/opcodes.h +++ b/src/opcodes.h @@ -26,9 +26,6 @@ SIMPLE(OP_BITNEGATE) SIMPLE(OP_INVOKE_GETTER) SIMPLE(OP_INVOKE_SETTER) SIMPLE(OP_INVOKE_DELETE) -SIMPLE(OP_INVOKE_GETSLICE) -SIMPLE(OP_INVOKE_SETSLICE) -SIMPLE(OP_INVOKE_DELSLICE) SIMPLE(OP_INVOKE_ITER) SIMPLE(OP_INVOKE_CONTAINS) SIMPLE(OP_INVOKE_AWAIT) @@ -78,6 +75,7 @@ OPERAND(OP_MAKE_LIST, (void)0) OPERAND(OP_MAKE_DICT, (void)0) OPERAND(OP_MAKE_SET, (void)0) OPERAND(OP_REVERSE, (void)0) +OPERAND(OP_SLICE, (void)0) JUMP(OP_JUMP_IF_FALSE_OR_POP,+) JUMP(OP_JUMP_IF_TRUE_OR_POP,+) JUMP(OP_JUMP,+) diff --git a/src/private.h b/src/private.h index 91a236e..1bde4ea 100644 --- a/src/private.h +++ b/src/private.h @@ -17,6 +17,7 @@ extern void _createAndBind_functionClass(void); extern void _createAndBind_rangeClass(void); extern void _createAndBind_setClass(void); extern void _createAndBind_generatorClass(void); +extern void _createAndBind_sliceClass(void); extern void _createAndBind_builtins(void); extern void _createAndBind_type(void); extern void _createAndBind_exceptions(void); diff --git a/src/table.c b/src/table.c index f6e7425..b63e183 100644 --- a/src/table.c +++ b/src/table.c @@ -50,7 +50,8 @@ inline int krk_hashValue(KrkValue value, uint32_t *hashOut) { return 0; } _unhashable: - krk_runtimeError(vm.exceptions->typeError, "unhashable type: '%s'", krk_typeName(value)); + if (IS_NONE(krk_currentThread.currentException)) + krk_runtimeError(vm.exceptions->typeError, "unhashable type: '%s'", krk_typeName(value)); return 1; } diff --git a/src/vendor/rline.c b/src/vendor/rline.c index 5497f7a..773b0a9 100644 --- a/src/vendor/rline.c +++ b/src/vendor/rline.c @@ -559,7 +559,7 @@ char * syn_krk_types[] = { "print","set","any","all","bool","ord","chr","hex","oct","filter", "sorted","bytes","getattr","sum","min","max","id","hash","map","bin", "enumerate","zip","setattr","property","staticmethod","classmethod", - "issubclass","hasattr","delattr","NotImplemented","abs", + "issubclass","hasattr","delattr","NotImplemented","abs","slice", NULL }; diff --git a/src/vm.c b/src/vm.c index 490b4cd..9641988 100644 --- a/src/vm.c +++ b/src/vm.c @@ -392,9 +392,6 @@ void krk_finalizeClass(KrkClass * _class) { struct TypeMap specials[] = { {&_class->_getter, METHOD_GET}, {&_class->_setter, METHOD_SET}, - {&_class->_getslice, METHOD_GETSLICE}, - {&_class->_setslice, METHOD_SETSLICE}, - {&_class->_delslice, METHOD_DELSLICE}, {&_class->_reprer, METHOD_REPR}, {&_class->_tostr, METHOD_STR}, {&_class->_call, METHOD_CALL}, @@ -1199,10 +1196,6 @@ void krk_initVM(int flags) { _(METHOD_GET, "__getitem__"), _(METHOD_SET, "__setitem__"), _(METHOD_DELITEM, "__delitem__"), - /* Slice subscripting */ - _(METHOD_GETSLICE, "__getslice__"), - _(METHOD_SETSLICE, "__setslice__"), - _(METHOD_DELSLICE, "__delslice__"), /* Dynamic properties */ _(METHOD_CLASS, "__class__"), _(METHOD_NAME, "__name__"), @@ -1251,6 +1244,7 @@ void krk_initVM(int flags) { _createAndBind_functionClass(); _createAndBind_rangeClass(); _createAndBind_setClass(); + _createAndBind_sliceClass(); _createAndBind_exceptions(); _createAndBind_generatorClass(); _createAndBind_gcMod(); @@ -2246,33 +2240,6 @@ _finishReturn: (void)0; } break; } - case OP_INVOKE_GETSLICE: { - KrkClass * type = krk_getType(krk_peek(2)); - if (likely(type->_getslice != NULL)) { - krk_push(krk_callDirect(type->_getslice, 3)); - } else { - krk_runtimeError(vm.exceptions->attributeError, "'%s' object is not sliceable", krk_typeName(krk_peek(2))); - } - break; - } - case OP_INVOKE_SETSLICE: { - KrkClass * type = krk_getType(krk_peek(3)); - if (likely(type->_setslice != NULL)) { - krk_push(krk_callDirect(type->_setslice, 4)); - } else { - krk_runtimeError(vm.exceptions->attributeError, "'%s' object is not sliceable", krk_typeName(krk_peek(3))); - } - break; - } - case OP_INVOKE_DELSLICE: { - KrkClass * type = krk_getType(krk_peek(2)); - if (likely(type->_delslice != NULL)) { - krk_callDirect(type->_delslice, 3); - } else { - krk_runtimeError(vm.exceptions->attributeError, "'%s' object is not sliceable", krk_typeName(krk_peek(2))); - } - break; - } case OP_INVOKE_DELETE: { KrkClass * type = krk_getType(krk_peek(1)); if (likely(type->_delitem != NULL)) { @@ -2824,6 +2791,13 @@ _finishReturn: (void)0; doMake(krk_set_of); break; } + case OP_SLICE_LONG: + THREE_BYTE_OPERAND; + case OP_SLICE: { + ONE_BYTE_OPERAND; + doMake(krk_slice_of); + break; + } case OP_LIST_APPEND_LONG: THREE_BYTE_OPERAND; case OP_LIST_APPEND: { diff --git a/test/testSlicers.krk b/test/testSlicers.krk new file mode 100644 index 0000000..20063ce --- /dev/null +++ b/test/testSlicers.krk @@ -0,0 +1,34 @@ + + +def test(a): + print(a[::]) + print(a[::-1]) + print(a[::2]) + print(a[::-2]) + print(a[1:2]) + print(a[1:7:3]) + print(a[5::-3]) + + +test([1,2,3,4,5,6,7,8,9,10,11,12,13]) + +test("こんにちは、みんなさま。クロコへようこそ。") + + +class SlicerTester: + def __getitem__(self, indexer): + print(indexer) + + +SlicerTester()[::] +SlicerTester()[::-1] +SlicerTester()[1:2:3] +SlicerTester()['a':'b':'c'] +SlicerTester()[:] +SlicerTester()[:'end'] +SlicerTester()['start':] +SlicerTester()[:'end':] +SlicerTester()[:'end':'step'] +SlicerTester()['start'::'step'] +SlicerTester()[1:2,3:4] +SlicerTester()[1:2:3,::4] diff --git a/test/testSlicers.krk.expect b/test/testSlicers.krk.expect new file mode 100644 index 0000000..e9951b0 --- /dev/null +++ b/test/testSlicers.krk.expect @@ -0,0 +1,26 @@ +[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] +[13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1] +[1, 3, 5, 7, 9, 11, 13] +[13, 11, 9, 7, 5, 3, 1] +[2] +[2, 5] +[6, 3] +こんにちは、みんなさま。クロコへようこそ。 +。そこうよへコロク。まさなんみ、はちにんこ +こにはみなまクコよこ。 +。こよコクまなみはにこ +ん +んは +、に +slice(None, None, None) +slice(None, None, -1) +slice(1, 2, 3) +slice('a', 'b', 'c') +slice(None, None, None) +slice(None, 'end', None) +slice('start', None, None) +slice(None, 'end', None) +slice(None, 'end', 'step') +slice('start', None, 'step') +(slice(1, 2, None), slice(3, 4, None)) +(slice(1, 2, 3), slice(None, None, 4))