8 #define LIST_WRAP_INDEX() \
9 if (index < 0) index += self->values.count; \
10 if (unlikely(index < 0 || index >= (krk_integer_type)self->values.count)) return krk_runtimeError(vm.exceptions->indexError, "list index out of range: %zd", (ssize_t)index)
12 #define LIST_WRAP_SOFT(val) \
13 if (val < 0) val += self->values.count; \
14 if (val < 0) val = 0; \
15 if (val > (krk_integer_type)self->values.count) val = self->values.count
18 for (
size_t i = 0; i < ((
KrkList*)
self)->values.count; ++i) {
36 AS_LIST(outList)->capacity = argc;
37 AS_LIST(outList)->values = GROW_ARRAY(
KrkValue, AS_LIST(outList)->values, 0, argc);
38 memcpy(AS_LIST(outList)->values, argv,
sizeof(
KrkValue) * argc);
39 AS_LIST(outList)->count = argc;
42 pthread_rwlock_init(&((
KrkList*)AS_OBJECT(outList))->rwlock, NULL);
46 #define CURRENT_CTYPE KrkList *
47 #define CURRENT_NAME self
49 KRK_Method(list,__getitem__) {
50 METHOD_TAKES_EXACTLY(1);
51 if (IS_INTEGER(argv[1])) {
52 CHECK_ARG(1,
int,krk_integer_type,index);
53 if (
vm.globalFlags & KRK_GLOBAL_THREADS) pthread_rwlock_rdlock(&self->rwlock);
55 KrkValue result =
self->values.values[index];
56 if (
vm.globalFlags & KRK_GLOBAL_THREADS) pthread_rwlock_unlock(&self->rwlock);
58 }
else if (IS_slice(argv[1])) {
59 pthread_rwlock_rdlock(&self->rwlock);
61 KRK_SLICER(argv[1],self->values.count) {
62 pthread_rwlock_unlock(&self->rwlock);
67 krk_integer_type len = end - start;
69 pthread_rwlock_unlock(&self->rwlock);
74 krk_integer_type len = 0;
75 krk_integer_type i = start;
76 while ((step < 0) ? (i > end) : (i < end)) {
90 pthread_rwlock_unlock(&self->rwlock);
94 return TYPE_ERROR(
int or slice,argv[1]);
98 KRK_Method(list,__eq__) {
99 METHOD_TAKES_EXACTLY(1);
100 if (!IS_list(argv[1]))
return NOTIMPL_VAL();
101 KrkList * them = AS_list(argv[1]);
102 if (self->values.count != them->
values.
count)
return BOOLEAN_VAL(0);
103 for (
size_t i = 0; i <
self->values.count; ++i) {
106 return BOOLEAN_VAL(1);
109 KRK_Method(list,append) {
110 METHOD_TAKES_EXACTLY(1);
111 pthread_rwlock_wrlock(&self->rwlock);
113 pthread_rwlock_unlock(&self->rwlock);
117 KRK_Method(list,insert) {
118 METHOD_TAKES_EXACTLY(2);
119 CHECK_ARG(1,
int,krk_integer_type,index);
120 pthread_rwlock_wrlock(&self->rwlock);
121 LIST_WRAP_SOFT(index);
124 &self->values.values[index+1],
125 &self->values.values[index],
126 sizeof(
KrkValue) * (self->values.count - index - 1)
128 self->values.values[index] = argv[2];
129 pthread_rwlock_unlock(&self->rwlock);
133 KRK_Method(list,__repr__) {
135 if (((
KrkObj*)
self)->flags & KRK_OBJ_FLAGS_IN_REPR)
return OBJECT_VAL(S(
"[...]"));
136 ((
KrkObj*)
self)->flags |= KRK_OBJ_FLAGS_IN_REPR;
138 pushStringBuilder(&sb,
'[');
139 pthread_rwlock_rdlock(&self->rwlock);
140 for (
size_t i = 0; i <
self->values.count; ++i) {
146 if (IS_STRING(result)) {
147 pushStringBuilderStr(&sb, AS_STRING(result)->chars, AS_STRING(result)->length);
150 if (i + 1 < self->values.count) {
151 pushStringBuilderStr(&sb,
", ", 2);
154 pthread_rwlock_unlock(&self->rwlock);
156 pushStringBuilder(&sb,
']');
157 ((
KrkObj*)
self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
158 return finishStringBuilder(&sb);
161 static int _list_extend_callback(
void * context,
const KrkValue * values,
size_t count) {
165 positionals->
capacity = (count == 1) ? GROW_CAPACITY(old) : (positionals->
count + count);
169 for (
size_t i = 0; i < count; ++i) {
170 positionals->
values[positionals->
count++] = values[i];
176 KRK_Method(list,extend) {
177 METHOD_TAKES_EXACTLY(1);
178 pthread_rwlock_wrlock(&self->rwlock);
182 other =
krk_list_of(self->values.count, self->values.values, 0);
187 pthread_rwlock_unlock(&self->rwlock);
191 KRK_Method(list,__init__) {
192 METHOD_TAKES_AT_MOST(1);
194 pthread_rwlock_init(&self->rwlock, NULL);
196 _list_extend(2,(
KrkValue[]){argv[0],argv[1]},0);
201 KRK_Method(list,__mul__) {
202 METHOD_TAKES_EXACTLY(1);
203 CHECK_ARG(1,
int,krk_integer_type,howMany);
209 for (krk_integer_type i = 0; i < howMany; i++) {
210 _list_extend(2, (
KrkValue[]){out,argv[0]},0);
216 KRK_Method(list,__len__) {
218 return INTEGER_VAL(self->values.count);
221 KRK_Method(list,__contains__) {
222 METHOD_TAKES_EXACTLY(1);
223 pthread_rwlock_rdlock(&self->rwlock);
224 for (
size_t i = 0; i <
self->values.count; ++i) {
226 pthread_rwlock_unlock(&self->rwlock);
227 return BOOLEAN_VAL(1);
231 pthread_rwlock_unlock(&self->rwlock);
232 return BOOLEAN_VAL(0);
235 KRK_Method(list,pop) {
236 METHOD_TAKES_AT_MOST(1);
237 pthread_rwlock_wrlock(&self->rwlock);
238 krk_integer_type index =
self->values.count - 1;
240 CHECK_ARG(1,
int,krk_integer_type,ind);
244 KrkValue outItem = AS_LIST(argv[0])->values[index];
245 if (index == (
long)AS_LIST(argv[0])->count-1) {
246 AS_LIST(argv[0])->count--;
247 pthread_rwlock_unlock(&self->rwlock);
251 size_t remaining = AS_LIST(argv[0])->count - index - 1;
252 memmove(&AS_LIST(argv[0])->values[index], &AS_LIST(argv[0])->values[index+1],
254 AS_LIST(argv[0])->count--;
255 pthread_rwlock_unlock(&self->rwlock);
260 KRK_Method(list,__setitem__) {
261 METHOD_TAKES_EXACTLY(2);
262 if (IS_INTEGER(argv[1])) {
263 CHECK_ARG(1,
int,krk_integer_type,index);
264 if (
vm.globalFlags & KRK_GLOBAL_THREADS) pthread_rwlock_rdlock(&self->rwlock);
266 self->values.values[index] = argv[2];
267 if (
vm.globalFlags & KRK_GLOBAL_THREADS) pthread_rwlock_unlock(&self->rwlock);
269 }
else if (IS_slice(argv[1])) {
270 if (!IS_list(argv[2])) {
271 return TYPE_ERROR(list,argv[2]);
274 KRK_SLICER(argv[1],self->values.count) {
282 krk_integer_type len = end - start;
283 krk_integer_type newLen = (krk_integer_type)AS_LIST(argv[2])->count;
285 for (krk_integer_type i = 0; (i < len && i < newLen); ++i) {
286 AS_LIST(argv[0])->values[start+i] = AS_LIST(argv[2])->values[i];
289 while (len < newLen) {
290 FUNC_NAME(list,insert)(3, (
KrkValue[]){argv[0], INTEGER_VAL(start + len), AS_LIST(argv[2])->values[len]}, 0);
294 while (newLen < len) {
295 FUNC_NAME(list,pop)(2, (
KrkValue[]){argv[0], INTEGER_VAL(start + len - 1)}, 0);
299 return OBJECT_VAL(
self);
301 return TYPE_ERROR(
int or slice, argv[1]);
305 KRK_Method(list,__delitem__) {
306 METHOD_TAKES_EXACTLY(1);
308 if (IS_INTEGER(argv[1])) {
309 FUNC_NAME(list,pop)(2,(
KrkValue[]){argv[0],INTEGER_VAL(argv[1])},0);
310 }
else if (IS_slice(argv[1])) {
311 KRK_SLICER(argv[1],self->values.count) {
319 krk_integer_type len = end - start;
322 FUNC_NAME(list,pop)(2,(
KrkValue[]){argv[0],INTEGER_VAL(start)},0);
326 return TYPE_ERROR(
int or slice, argv[1]);
332 KRK_Method(list,remove) {
333 METHOD_TAKES_EXACTLY(1);
334 pthread_rwlock_wrlock(&self->rwlock);
335 for (
size_t i = 0; i <
self->values.count; ++i) {
337 pthread_rwlock_unlock(&self->rwlock);
338 return FUNC_NAME(list,pop)(2,(
KrkValue[]){argv[0], INTEGER_VAL(i)},0);
341 pthread_rwlock_unlock(&self->rwlock);
345 pthread_rwlock_unlock(&self->rwlock);
349 KRK_Method(list,clear) {
351 pthread_rwlock_wrlock(&self->rwlock);
353 pthread_rwlock_unlock(&self->rwlock);
357 KRK_Method(list,index) {
358 METHOD_TAKES_AT_LEAST(1);
359 METHOD_TAKES_AT_MOST(3);
361 krk_integer_type min = 0;
362 krk_integer_type max =
self->values.count;
365 if (IS_INTEGER(argv[2]))
366 min = AS_INTEGER(argv[2]);
368 return krk_runtimeError(
vm.exceptions->typeError,
"%s must be int, not '%T'",
"min", argv[2]);
372 if (IS_INTEGER(argv[3]))
373 max = AS_INTEGER(argv[3]);
375 return krk_runtimeError(
vm.exceptions->typeError,
"%s must be int, not '%T'",
"max", argv[3]);
378 pthread_rwlock_rdlock(&self->rwlock);
382 for (krk_integer_type i = min; i < max; ++i) {
384 pthread_rwlock_unlock(&self->rwlock);
385 return INTEGER_VAL(i);
388 pthread_rwlock_unlock(&self->rwlock);
393 pthread_rwlock_unlock(&self->rwlock);
397 KRK_Method(list,count) {
398 METHOD_TAKES_EXACTLY(1);
399 krk_integer_type count = 0;
401 pthread_rwlock_rdlock(&self->rwlock);
402 for (
size_t i = 0; i <
self->values.count; ++i) {
406 pthread_rwlock_unlock(&self->rwlock);
408 return INTEGER_VAL(count);
411 KRK_Method(list,copy) {
413 pthread_rwlock_rdlock(&self->rwlock);
415 pthread_rwlock_unlock(&self->rwlock);
419 KRK_Method(list,reverse) {
421 pthread_rwlock_wrlock(&self->rwlock);
422 for (
size_t i = 0; i < (
self->values.count) / 2; i++) {
423 KrkValue tmp =
self->values.values[i];
424 self->values.values[i] =
self->values.values[
self->values.count-i-1];
425 self->values.values[
self->values.count-i-1] = tmp;
427 pthread_rwlock_unlock(&self->rwlock);
431 static int _list_sorter(
const void * _a,
const void * _b) {
438 if (IS_NONE(ltComp) || (IS_BOOLEAN(ltComp) && AS_BOOLEAN(ltComp)))
return -1;
440 if (IS_NONE(gtComp) || (IS_BOOLEAN(gtComp) && AS_BOOLEAN(gtComp)))
return 1;
444 KRK_Method(list,sort) {
447 pthread_rwlock_wrlock(&self->rwlock);
448 qsort(self->values.values, self->values.count,
sizeof(
KrkValue), _list_sorter);
449 pthread_rwlock_unlock(&self->rwlock);
454 KRK_Method(list,__add__) {
455 METHOD_TAKES_EXACTLY(1);
456 if (!IS_list(argv[1]))
return TYPE_ERROR(list,argv[1]);
458 pthread_rwlock_rdlock(&self->rwlock);
460 pthread_rwlock_unlock(&self->rwlock);
461 FUNC_NAME(list,extend)(2,(
KrkValue[]){outList,argv[1]},0);
465 FUNC_SIG(listiterator,__init__);
467 KRK_Method(list,__iter__) {
475 return OBJECT_VAL(output);
478 #define MAKE_LIST_COMPARE(name,op) \
479 KRK_Method(list,__ ## name ## __) { \
480 METHOD_TAKES_EXACTLY(1); \
481 if (!IS_list(argv[1])) return NOTIMPL_VAL(); \
482 KrkList * them = AS_list(argv[1]); \
483 size_t lesser = self->values.count < them->values.count ? self->values.count : them->values.count; \
484 for (size_t i = 0; i < lesser; ++i) { \
485 KrkValue a = self->values.values[i]; \
486 KrkValue b = them->values.values[i]; \
487 if (krk_valuesSameOrEqual(a,b)) continue; \
488 if (unlikely(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) return NONE_VAL(); \
489 return krk_operator_ ## name(a,b); \
491 return BOOLEAN_VAL((self->values.count op them->values.count)); \
494 MAKE_LIST_COMPARE(gt,>)
495 MAKE_LIST_COMPARE(lt,<)
496 MAKE_LIST_COMPARE(ge,>=)
497 MAKE_LIST_COMPARE(le,<=)
507 #define CURRENT_CTYPE struct ListIterator *
508 #define IS_listiterator(o) (likely(IS_INSTANCE(o) && AS_INSTANCE(o)->_class == vm.baseClasses->listiteratorClass) || krk_isInstanceOf(o,vm.baseClasses->listiteratorClass))
509 #define AS_listiterator(o) (struct ListIterator*)AS_OBJECT(o)
511 static void _listiterator_gcscan(
KrkInstance *
self) {
515 KRK_Method(listiterator,__init__) {
516 METHOD_TAKES_EXACTLY(1);
517 CHECK_ARG(1,list,
KrkList*,list);
523 FUNC_SIG(listiterator,__call__) {
524 static __attribute__ ((unused))
const char* _method_name =
"__call__";
525 if (unlikely((argc != 1)))
goto _bad;
526 if (unlikely(!IS_OBJECT(argv[0])))
goto _bad;
527 if (unlikely(AS_INSTANCE(argv[0])->_class !=
vm.baseClasses->listiteratorClass))
goto _bad;
533 size_t _counter =
self->i;
534 if (unlikely(_counter >= AS_LIST(_list)->count)) {
537 self->i = _counter + 1;
538 return AS_LIST(_list)->values[_counter];
542 if (argc != 1)
return NOT_ENOUGH_ARGS(name);
543 if (!
krk_isInstanceOf(argv[0],
vm.baseClasses->listiteratorClass))
return TYPE_ERROR(listiterator, argv[0]);
548 if (argc != 1)
return krk_runtimeError(
vm.exceptions->argumentError,
"%s() takes %s %d argument%s (%d given)",
"sorted",
"exactly",1,
"",argc);
551 FUNC_NAME(list,extend)(2,(
KrkValue[]){listOut,argv[0]},0);
553 FUNC_NAME(list,sort)(1,&listOut,0);
561 if (argc != 1)
return krk_runtimeError(
vm.exceptions->argumentError,
"%s() takes %s %d argument%s (%d given)",
"reversed",
"exactly",1,
"",argc);
564 FUNC_NAME(list,extend)(2,(
KrkValue[]){listOut,argv[0]},0);
566 FUNC_NAME(list,reverse)(1,&listOut,0);
572 void _createAndBind_listClass(
void) {
573 KrkClass * list = ADD_BASE_CLASS(
vm.baseClasses->listClass,
"list",
vm.baseClasses->objectClass);
577 BIND_METHOD(list,__init__);
578 BIND_METHOD(list,__eq__);
579 BIND_METHOD(list,__getitem__);
580 BIND_METHOD(list,__setitem__);
581 BIND_METHOD(list,__delitem__);
582 BIND_METHOD(list,__len__);
583 BIND_METHOD(list,__repr__);
584 BIND_METHOD(list,__contains__);
585 BIND_METHOD(list,__iter__);
586 BIND_METHOD(list,__mul__);
587 BIND_METHOD(list,__add__);
588 BIND_METHOD(list,__lt__);
589 BIND_METHOD(list,__gt__);
590 BIND_METHOD(list,__le__);
591 BIND_METHOD(list,__ge__);
592 KRK_DOC(BIND_METHOD(list,append),
593 "@brief Add an item to the end of the list.\n"
594 "@arguments item\n\n"
595 "Adds an item to the end of a list. Appending items to a list is an amortized constant-time "
596 "operation, but may result in the reallocation of the list if not enough additional space is "
597 "available to store to the new element in the current allocation.");
598 KRK_DOC(BIND_METHOD(list,extend),
599 "@brief Add the contents of an iterable to the end of a list.\n"
600 "@argument iterable\n\n"
601 "Adds all of the elements of @p iterable to the end of the list, as if each were added individually "
602 "with @ref _list_append.");
604 "@brief Remove and return an element from the list.\n"
605 "@arguments [index]\n\n"
606 "Removes and returns the entry at the end of the list, or at @p index if provided. "
607 "Popping from the end of the list is constant-time. Popping from the head of the list "
608 "is always O(n) as the contents of the list must be shifted.");
609 KRK_DOC(BIND_METHOD(list,insert),
610 "@brief Add an entry to the list at a given offset.\n"
611 "@arguments index, val\n\n"
612 "Adds @p val to the list at offset @p index, moving all following items back. Inserting "
613 "near the beginning of a list can be costly.");
614 KRK_DOC(BIND_METHOD(list,clear),
615 "@brief Empty a list.\n\n"
616 "Removes all entries from the list.");
617 KRK_DOC(BIND_METHOD(list,index),
618 "@brief Locate an item in the list by value.\n"
619 "@arguments val,[min,[max]]\n\n"
620 "Searches for @p val in the list and returns its index if found. If @p min is provided, "
621 "the search will begin at index @p min. If @p max is also provided, the search will end "
623 "Raises @ref ValueError if the item is not found.");
624 KRK_DOC(BIND_METHOD(list,count),
625 "@brief Count instances of a value in the list.\n"
627 "Scans the list for values equal to @p val and returns the count of matching entries.");
628 KRK_DOC(BIND_METHOD(list,copy),
629 "@brief Clone a list.\n\n"
630 "Equivalent to @c list[:], creates a new list with the same items as this list.");
631 KRK_DOC(BIND_METHOD(list,remove),
632 "@brief Remove an item from the list.\n"
634 "Scans the list for an entry equivalent to @p val and removes it from the list.\n"
635 "Raises @ref ValueError if no matching entry is found.");
636 KRK_DOC(BIND_METHOD(list,reverse),
637 "@brief Reverse the contents of a list.\n\n"
638 "Reverses the elements of the list in-place.");
639 KRK_DOC(BIND_METHOD(list,sort),
640 "@brief Sort the contents of a list.\n\n"
641 "Performs an in-place sort of the elements in the list, returning @c None as a gentle reminder "
642 "that the sort is in-place. If a sorted copy is desired, use @ref sorted instead.");
647 KRK_DOC(list,
"Mutable sequence of arbitrary values.");
649 BUILTIN_FUNCTION(
"sorted", _sorted,
650 "@brief Return a sorted representation of an iterable.\n"
651 "@arguments iterable\n\n"
652 "Creates a new, sorted list from the elements of @p iterable.");
653 BUILTIN_FUNCTION(
"reversed", _reversed,
654 "@brief Return a reversed representation of an iterable.\n"
655 "@arguments iterable\n\n"
656 "Creates a new, reversed list from the elements of @p iterable.");
658 KrkClass * listiterator = ADD_BASE_CLASS(
vm.baseClasses->listiteratorClass,
"listiterator",
vm.baseClasses->objectClass);
660 listiterator->
_ongcscan = _listiterator_gcscan;
661 listiterator->
obj.
flags |= KRK_OBJ_FLAGS_NO_INHERIT;
662 BIND_METHOD(listiterator,__init__);
663 BIND_METHOD(listiterator,__call__);
KrkValue krk_runtimeError(KrkClass *type, const char *fmt,...)
Produce and raise an exception with a formatted message.
Functions for dealing with garbage collection and memory allocation.
void krk_markValue(KrkValue value)
During a GC scan cycle, mark a value as used.
NativeFn krk_GenericAlias
Special value for type hint expressions.
KrkCleanupCallback _ongcsweep
C function to call when the garbage collector is discarding an instance of this class.
KrkCleanupCallback _ongcscan
C function to call when the garbage collector visits an instance of this class in the scan phase.
KrkObj * _reprer
__repr__ Called to create a reproducible string representation of an instance
size_t allocSize
Size to allocate when creating instances of this class.
KrkTable methods
General attributes table.
void krk_finalizeClass(KrkClass *_class)
Finalize a class by collecting pointers to core methods.
KrkInstance * krk_newInstance(KrkClass *_class)
Create a new instance of the given class.
KrkValue krk_list_of(int argc, const KrkValue argv[], int hasKw)
Create a list object.
KrkValueArray values
Stores the length, capacity, and actual values of the list.
The most basic object type.
uint16_t flags
General object flags, mostly related to garbage collection.
KrkNative * krk_defineNative(KrkTable *table, const char *name, NativeFn function)
Attach a native C function to an attribute table.
void krk_attachNamedValue(KrkTable *table, const char name[], KrkValue obj)
Attach a value to an attribute table.
KrkValue currentException
Flexible vector of stack references.
void krk_initValueArray(KrkValueArray *array)
Initialize a value array.
void krk_freeValueArray(KrkValueArray *array)
Release relesources used by a value array.
void krk_writeValueArray(KrkValueArray *array, KrkValue value)
Add a value to a value array.
Stack reference or primative value.
int krk_isInstanceOf(KrkValue obj, const KrkClass *type)
Determine if a class is an instance or subclass of a given type.
int krk_valuesSame(KrkValue a, KrkValue b)
Compare two values by identity.
KrkClass * krk_getType(KrkValue value)
Get the class representing a value.
int krk_valuesSameOrEqual(KrkValue a, KrkValue b)
Compare two values by identity, then by equality.
Inline flexible string array.
Convience header for providing atomic operations to threads.
Utilities for creating native bindings.
int krk_unpackIterable(KrkValue iterable, void *context, int callback(void *, const KrkValue *, size_t))
Unpack an iterable.
#define KRK_DOC(thing, text)
Attach documentation to a thing of various types.
Definitions for primitive stack references.
Core API for the bytecode virtual machine.
#define vm
Convenience macro for namespacing.
KrkValue krk_operator_lt(KrkValue, KrkValue)
Compare two values, returning True if the left is less than the right.
KrkValue krk_callNativeOnStack(size_t argCount, const KrkValue *stackArgs, int hasKw, NativeFn native)
Call a native function using a reference to stack arguments safely.
KrkValue krk_pop(void)
Pop the top of the stack.
threadLocal KrkThreadState krk_currentThread
Thread-local VM state.
void krk_push(KrkValue value)
Push a stack value.
KrkValue krk_callDirect(KrkObj *callable, int argCount)
Call a closure or native function with argCount arguments.
KrkValue krk_peek(int distance)
Peek down from the top of the stack.
KrkValue krk_operator_gt(KrkValue, KrkValue)
Compare to values, returning True if the left is greater than the right.