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.
This commit is contained in:
K. Lange 2022-05-03 15:24:40 +09:00
parent c750f76a57
commit 6613db6cd4
18 changed files with 614 additions and 295 deletions

View File

@ -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);
}
}

View File

@ -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;
/**

View File

@ -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

View File

@ -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))

View File

@ -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.
*

View File

@ -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);

View File

@ -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]);
}
})

View File

@ -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());

181
src/obj_slice.c Normal file
View File

@ -0,0 +1,181 @@
#include <string.h>
#include <limits.h>
#include <kuroko/vm.h>
#include <kuroko/value.h>
#include <kuroko/memory.h>
#include <kuroko/util.h>
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);
}

View File

@ -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__);

View File

@ -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__,{

View File

@ -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,+)

View File

@ -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);

View File

@ -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;
}

2
src/vendor/rline.c vendored
View File

@ -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
};

View File

@ -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: {

34
test/testSlicers.krk Normal file
View File

@ -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]

View File

@ -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))