obj_list.c
1 #include <string.h>
2 #include <kuroko/vm.h>
3 #include <kuroko/value.h>
4 #include <kuroko/memory.h>
5 #include <kuroko/util.h>
6 #include <kuroko/threads.h>
7 
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)
11 
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
16 
17 static void _list_gcscan(KrkInstance * self) {
18  for (size_t i = 0; i < ((KrkList*)self)->values.count; ++i) {
19  krk_markValue(((KrkList*)self)->values.values[i]);
20  }
21 }
22 
23 static void _list_gcsweep(KrkInstance * self) {
24  krk_freeValueArray(&((KrkList*)self)->values);
25 }
26 
30 KrkValue krk_list_of(int argc, const KrkValue argv[], int hasKw) {
31  KrkValue outList = OBJECT_VAL(krk_newInstance(vm.baseClasses->listClass));
32  krk_push(outList);
33  krk_initValueArray(AS_LIST(outList));
34 
35  if (argc) {
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;
40  }
41 
42  pthread_rwlock_init(&((KrkList*)AS_OBJECT(outList))->rwlock, NULL);
43  return krk_pop();
44 }
45 
46 #define CURRENT_CTYPE KrkList *
47 #define CURRENT_NAME self
48 
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);
54  LIST_WRAP_INDEX();
55  KrkValue result = self->values.values[index];
56  if (vm.globalFlags & KRK_GLOBAL_THREADS) pthread_rwlock_unlock(&self->rwlock);
57  return result;
58  } else if (IS_slice(argv[1])) {
59  pthread_rwlock_rdlock(&self->rwlock);
60 
61  KRK_SLICER(argv[1],self->values.count) {
62  pthread_rwlock_unlock(&self->rwlock);
63  return NONE_VAL();
64  }
65 
66  if (step == 1) {
67  krk_integer_type len = end - start;
68  KrkValue result = krk_list_of(len, &AS_LIST(argv[0])->values[start], 0);
69  pthread_rwlock_unlock(&self->rwlock);
70  return result;
71  } else {
72  /* iterate and push */
73  krk_push(NONE_VAL());
74  krk_integer_type len = 0;
75  krk_integer_type i = start;
76  while ((step < 0) ? (i > end) : (i < end)) {
77  krk_push(self->values.values[i]);
78  len++;
79  i += step;
80  }
81 
82  /* make into a list */
84  krk_currentThread.stackTop[-len-1] = result;
85  while (len) {
86  krk_pop();
87  len--;
88  }
89 
90  pthread_rwlock_unlock(&self->rwlock);
91  return krk_pop();
92  }
93  } else {
94  return TYPE_ERROR(int or slice,argv[1]);
95  }
96 }
97 
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) {
104  if (!krk_valuesSameOrEqual(self->values.values[i], them->values.values[i])) return BOOLEAN_VAL(0);
105  }
106  return BOOLEAN_VAL(1);
107 }
108 
109 KRK_Method(list,append) {
110  METHOD_TAKES_EXACTLY(1);
111  pthread_rwlock_wrlock(&self->rwlock);
112  krk_writeValueArray(&self->values, argv[1]);
113  pthread_rwlock_unlock(&self->rwlock);
114  return NONE_VAL();
115 }
116 
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);
122  krk_writeValueArray(&self->values, NONE_VAL());
123  memmove(
124  &self->values.values[index+1],
125  &self->values.values[index],
126  sizeof(KrkValue) * (self->values.count - index - 1)
127  );
128  self->values.values[index] = argv[2];
129  pthread_rwlock_unlock(&self->rwlock);
130  return NONE_VAL();
131 }
132 
133 KRK_Method(list,__repr__) {
134  METHOD_TAKES_NONE();
135  if (((KrkObj*)self)->flags & KRK_OBJ_FLAGS_IN_REPR) return OBJECT_VAL(S("[...]"));
136  ((KrkObj*)self)->flags |= KRK_OBJ_FLAGS_IN_REPR;
137  struct StringBuilder sb = {0};
138  pushStringBuilder(&sb, '[');
139  pthread_rwlock_rdlock(&self->rwlock);
140  for (size_t i = 0; i < self->values.count; ++i) {
141  /* repr(self[i]) */
142  KrkClass * type = krk_getType(self->values.values[i]);
143  krk_push(self->values.values[i]);
144  KrkValue result = krk_callDirect(type->_reprer, 1);
145 
146  if (IS_STRING(result)) {
147  pushStringBuilderStr(&sb, AS_STRING(result)->chars, AS_STRING(result)->length);
148  }
149 
150  if (i + 1 < self->values.count) {
151  pushStringBuilderStr(&sb, ", ", 2);
152  }
153  }
154  pthread_rwlock_unlock(&self->rwlock);
155 
156  pushStringBuilder(&sb,']');
157  ((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
158  return finishStringBuilder(&sb);
159 }
160 
161 static int _list_extend_callback(void * context, const KrkValue * values, size_t count) {
162  KrkValueArray * positionals = context;
163  if (positionals->count + count > positionals->capacity) {
164  size_t old = positionals->capacity;
165  positionals->capacity = (count == 1) ? GROW_CAPACITY(old) : (positionals->count + count);
166  positionals->values = GROW_ARRAY(KrkValue, positionals->values, old, positionals->capacity);
167  }
168 
169  for (size_t i = 0; i < count; ++i) {
170  positionals->values[positionals->count++] = values[i];
171  }
172 
173  return 0;
174 }
175 
176 KRK_Method(list,extend) {
177  METHOD_TAKES_EXACTLY(1);
178  pthread_rwlock_wrlock(&self->rwlock);
179  KrkValueArray * positionals = AS_LIST(argv[0]);
180  KrkValue other = argv[1];
181  if (krk_valuesSame(argv[0],other)) {
182  other = krk_list_of(self->values.count, self->values.values, 0);
183  }
184 
185  krk_unpackIterable(other, positionals, _list_extend_callback);
186 
187  pthread_rwlock_unlock(&self->rwlock);
188  return NONE_VAL();
189 }
190 
191 KRK_Method(list,__init__) {
192  METHOD_TAKES_AT_MOST(1);
193  krk_initValueArray(AS_LIST(argv[0]));
194  pthread_rwlock_init(&self->rwlock, NULL);
195  if (argc == 2) {
196  _list_extend(2,(KrkValue[]){argv[0],argv[1]},0);
197  }
198  return NONE_VAL();
199 }
200 
201 KRK_Method(list,__mul__) {
202  METHOD_TAKES_EXACTLY(1);
203  CHECK_ARG(1,int,krk_integer_type,howMany);
204 
205  KrkValue out = krk_list_of(0, NULL, 0);
206 
207  krk_push(out);
208 
209  for (krk_integer_type i = 0; i < howMany; i++) {
210  _list_extend(2, (KrkValue[]){out,argv[0]},0);
211  }
212 
213  return krk_pop();
214 }
215 
216 KRK_Method(list,__len__) {
217  METHOD_TAKES_NONE();
218  return INTEGER_VAL(self->values.count);
219 }
220 
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) {
225  if (krk_valuesSameOrEqual(argv[1], self->values.values[i])) {
226  pthread_rwlock_unlock(&self->rwlock);
227  return BOOLEAN_VAL(1);
228  }
229  if (unlikely(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) break;
230  }
231  pthread_rwlock_unlock(&self->rwlock);
232  return BOOLEAN_VAL(0);
233 }
234 
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;
239  if (argc == 2) {
240  CHECK_ARG(1,int,krk_integer_type,ind);
241  index = ind;
242  }
243  LIST_WRAP_INDEX();
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);
248  return outItem;
249  } else {
250  /* Need to move up */
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],
253  sizeof(KrkValue) * remaining);
254  AS_LIST(argv[0])->count--;
255  pthread_rwlock_unlock(&self->rwlock);
256  return outItem;
257  }
258 }
259 
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);
265  LIST_WRAP_INDEX();
266  self->values.values[index] = argv[2];
267  if (vm.globalFlags & KRK_GLOBAL_THREADS) pthread_rwlock_unlock(&self->rwlock);
268  return argv[2];
269  } else if (IS_slice(argv[1])) {
270  if (!IS_list(argv[2])) {
271  return TYPE_ERROR(list,argv[2]); /* TODO other sequence types */
272  }
273 
274  KRK_SLICER(argv[1],self->values.count) {
275  return NONE_VAL();
276  }
277 
278  if (step != 1) {
279  return krk_runtimeError(vm.exceptions->valueError, "step value unsupported");
280  }
281 
282  krk_integer_type len = end - start;
283  krk_integer_type newLen = (krk_integer_type)AS_LIST(argv[2])->count;
284 
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];
287  }
288 
289  while (len < newLen) {
290  FUNC_NAME(list,insert)(3, (KrkValue[]){argv[0], INTEGER_VAL(start + len), AS_LIST(argv[2])->values[len]}, 0);
291  len++;
292  }
293 
294  while (newLen < len) {
295  FUNC_NAME(list,pop)(2, (KrkValue[]){argv[0], INTEGER_VAL(start + len - 1)}, 0);
296  len--;
297  }
298 
299  return OBJECT_VAL(self);
300  } else {
301  return TYPE_ERROR(int or slice, argv[1]);
302  }
303 }
304 
305 KRK_Method(list,__delitem__) {
306  METHOD_TAKES_EXACTLY(1);
307 
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) {
312  return NONE_VAL();
313  }
314 
315  if (step != 1) {
316  return krk_runtimeError(vm.exceptions->valueError, "step value unsupported");
317  }
318 
319  krk_integer_type len = end - start;
320 
321  while (len > 0) {
322  FUNC_NAME(list,pop)(2,(KrkValue[]){argv[0],INTEGER_VAL(start)},0);
323  len--;
324  }
325  } else {
326  return TYPE_ERROR(int or slice, argv[1]);
327  }
328 
329  return NONE_VAL();
330 }
331 
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) {
336  if (krk_valuesSameOrEqual(self->values.values[i], argv[1])) {
337  pthread_rwlock_unlock(&self->rwlock);
338  return FUNC_NAME(list,pop)(2,(KrkValue[]){argv[0], INTEGER_VAL(i)},0);
339  }
340  if (unlikely(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) {
341  pthread_rwlock_unlock(&self->rwlock);
342  return NONE_VAL();
343  }
344  }
345  pthread_rwlock_unlock(&self->rwlock);
346  return krk_runtimeError(vm.exceptions->valueError, "not found");
347 }
348 
349 KRK_Method(list,clear) {
350  METHOD_TAKES_NONE();
351  pthread_rwlock_wrlock(&self->rwlock);
352  krk_freeValueArray(&self->values);
353  pthread_rwlock_unlock(&self->rwlock);
354  return NONE_VAL();
355 }
356 
357 KRK_Method(list,index) {
358  METHOD_TAKES_AT_LEAST(1);
359  METHOD_TAKES_AT_MOST(3);
360 
361  krk_integer_type min = 0;
362  krk_integer_type max = self->values.count;
363 
364  if (argc > 2) {
365  if (IS_INTEGER(argv[2]))
366  min = AS_INTEGER(argv[2]);
367  else
368  return krk_runtimeError(vm.exceptions->typeError, "%s must be int, not '%T'", "min", argv[2]);
369  }
370 
371  if (argc > 3) {
372  if (IS_INTEGER(argv[3]))
373  max = AS_INTEGER(argv[3]);
374  else
375  return krk_runtimeError(vm.exceptions->typeError, "%s must be int, not '%T'", "max", argv[3]);
376  }
377 
378  pthread_rwlock_rdlock(&self->rwlock);
379  LIST_WRAP_SOFT(min);
380  LIST_WRAP_SOFT(max);
381 
382  for (krk_integer_type i = min; i < max; ++i) {
383  if (krk_valuesSameOrEqual(self->values.values[i], argv[1])) {
384  pthread_rwlock_unlock(&self->rwlock);
385  return INTEGER_VAL(i);
386  }
387  if (unlikely(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) {
388  pthread_rwlock_unlock(&self->rwlock);
389  return NONE_VAL();
390  }
391  }
392 
393  pthread_rwlock_unlock(&self->rwlock);
394  return krk_runtimeError(vm.exceptions->valueError, "not found");
395 }
396 
397 KRK_Method(list,count) {
398  METHOD_TAKES_EXACTLY(1);
399  krk_integer_type count = 0;
400 
401  pthread_rwlock_rdlock(&self->rwlock);
402  for (size_t i = 0; i < self->values.count; ++i) {
403  if (krk_valuesSameOrEqual(self->values.values[i], argv[1])) count++;
404  if (unlikely(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) break;
405  }
406  pthread_rwlock_unlock(&self->rwlock);
407 
408  return INTEGER_VAL(count);
409 }
410 
411 KRK_Method(list,copy) {
412  METHOD_TAKES_NONE();
413  pthread_rwlock_rdlock(&self->rwlock);
414  KrkValue result = krk_list_of(self->values.count, self->values.values, 0);
415  pthread_rwlock_unlock(&self->rwlock);
416  return result;
417 }
418 
419 KRK_Method(list,reverse) {
420  METHOD_TAKES_NONE();
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;
426  }
427  pthread_rwlock_unlock(&self->rwlock);
428  return NONE_VAL();
429 }
430 
431 static int _list_sorter(const void * _a, const void * _b) {
432  KrkValue a = *(KrkValue*)_a;
433  KrkValue b = *(KrkValue*)_b;
434 
435  /* Avoid actually calling the sort function if there's an active exception */
436  if (unlikely(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) return -1;
437  KrkValue ltComp = krk_operator_lt(a,b);
438  if (IS_NONE(ltComp) || (IS_BOOLEAN(ltComp) && AS_BOOLEAN(ltComp))) return -1;
439  KrkValue gtComp = krk_operator_gt(a,b);
440  if (IS_NONE(gtComp) || (IS_BOOLEAN(gtComp) && AS_BOOLEAN(gtComp))) return 1;
441  return 0;
442 }
443 
444 KRK_Method(list,sort) {
445  METHOD_TAKES_NONE();
446 
447  pthread_rwlock_wrlock(&self->rwlock);
448  qsort(self->values.values, self->values.count, sizeof(KrkValue), _list_sorter);
449  pthread_rwlock_unlock(&self->rwlock);
450 
451  return NONE_VAL();
452 }
453 
454 KRK_Method(list,__add__) {
455  METHOD_TAKES_EXACTLY(1);
456  if (!IS_list(argv[1])) return TYPE_ERROR(list,argv[1]);
457 
458  pthread_rwlock_rdlock(&self->rwlock);
459  KrkValue outList = krk_list_of(self->values.count, self->values.values, 0); /* copy */
460  pthread_rwlock_unlock(&self->rwlock);
461  FUNC_NAME(list,extend)(2,(KrkValue[]){outList,argv[1]},0); /* extend */
462  return outList;
463 }
464 
465 FUNC_SIG(listiterator,__init__);
466 
467 KRK_Method(list,__iter__) {
468  METHOD_TAKES_NONE();
469  KrkInstance * output = krk_newInstance(vm.baseClasses->listiteratorClass);
470 
471  krk_push(OBJECT_VAL(output));
472  FUNC_NAME(listiterator,__init__)(2, (KrkValue[]){krk_peek(0), argv[0]},0);
473  krk_pop();
474 
475  return OBJECT_VAL(output);
476 }
477 
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); \
490  } \
491  return BOOLEAN_VAL((self->values.count op them->values.count)); \
492  }
493 
494 MAKE_LIST_COMPARE(gt,>)
495 MAKE_LIST_COMPARE(lt,<)
496 MAKE_LIST_COMPARE(ge,>=)
497 MAKE_LIST_COMPARE(le,<=)
498 
499 #undef CURRENT_CTYPE
500 
501 struct ListIterator {
502  KrkInstance inst;
503  KrkValue l;
504  size_t i;
505 };
506 
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)
510 
511 static void _listiterator_gcscan(KrkInstance * self) {
512  krk_markValue(((struct ListIterator*)self)->l);
513 }
514 
515 KRK_Method(listiterator,__init__) {
516  METHOD_TAKES_EXACTLY(1);
517  CHECK_ARG(1,list,KrkList*,list);
518  self->l = argv[1];
519  self->i = 0;
520  return NONE_VAL();
521 }
522 
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;
528 
529 _maybeGood: (void)0;
530  struct ListIterator * self = (struct ListIterator *)AS_OBJECT(argv[0]);
531 
532  KrkValue _list = self->l;
533  size_t _counter = self->i;
534  if (unlikely(_counter >= AS_LIST(_list)->count)) {
535  return argv[0];
536  } else {
537  self->i = _counter + 1;
538  return AS_LIST(_list)->values[_counter];
539  }
540 
541 _bad:
542  if (argc != 1) return NOT_ENOUGH_ARGS(name);
543  if (!krk_isInstanceOf(argv[0], vm.baseClasses->listiteratorClass)) return TYPE_ERROR(listiterator, argv[0]);
544  goto _maybeGood;
545 }
546 
547 static KrkValue _sorted(int argc, const KrkValue argv[], int hasKw) {
548  if (argc != 1) return krk_runtimeError(vm.exceptions->argumentError,"%s() takes %s %d argument%s (%d given)","sorted","exactly",1,"",argc);
549  KrkValue listOut = krk_list_of(0,NULL,0);
550  krk_push(listOut);
551  FUNC_NAME(list,extend)(2,(KrkValue[]){listOut,argv[0]},0);
552  if (!IS_NONE(krk_currentThread.currentException)) return NONE_VAL();
553  FUNC_NAME(list,sort)(1,&listOut,0);
554  if (!IS_NONE(krk_currentThread.currentException)) return NONE_VAL();
555  return krk_pop();
556 }
557 
558 static KrkValue _reversed(int argc, const KrkValue argv[], int hasKw) {
559  /* FIXME The Python reversed() function produces an iterator and only works for things with indexing or a __reversed__ method;
560  * Building a list and reversing it like we do here is not correct! */
561  if (argc != 1) return krk_runtimeError(vm.exceptions->argumentError,"%s() takes %s %d argument%s (%d given)","reversed","exactly",1,"",argc);
562  KrkValue listOut = krk_list_of(0,NULL,0);
563  krk_push(listOut);
564  FUNC_NAME(list,extend)(2,(KrkValue[]){listOut,argv[0]},0);
565  if (!IS_NONE(krk_currentThread.currentException)) return NONE_VAL();
566  FUNC_NAME(list,reverse)(1,&listOut,0);
567  if (!IS_NONE(krk_currentThread.currentException)) return NONE_VAL();
568  return krk_pop();
569 }
570 
571 _noexport
572 void _createAndBind_listClass(void) {
573  KrkClass * list = ADD_BASE_CLASS(vm.baseClasses->listClass, "list", vm.baseClasses->objectClass);
574  list->allocSize = sizeof(KrkList);
575  list->_ongcscan = _list_gcscan;
576  list->_ongcsweep = _list_gcsweep;
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.");
603  KRK_DOC(BIND_METHOD(list,pop),
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 "
622  "at index @p max.\n"
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"
626  "@arguments val\n\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"
633  "@arguments val\n\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.");
643  krk_defineNative(&list->methods, "__str__", FUNC_NAME(list,__repr__));
644  krk_defineNative(&list->methods, "__class_getitem__", krk_GenericAlias)->obj.flags |= KRK_OBJ_FLAGS_FUNCTION_IS_CLASS_METHOD;
645  krk_attachNamedValue(&list->methods, "__hash__", NONE_VAL());
646  krk_finalizeClass(list);
647  KRK_DOC(list, "Mutable sequence of arbitrary values.");
648 
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.");
657 
658  KrkClass * listiterator = ADD_BASE_CLASS(vm.baseClasses->listiteratorClass, "listiterator", vm.baseClasses->objectClass);
659  listiterator->allocSize = sizeof(struct ListIterator);
660  listiterator->_ongcscan = _listiterator_gcscan;
661  listiterator->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
662  BIND_METHOD(listiterator,__init__);
663  BIND_METHOD(listiterator,__call__);
664  krk_finalizeClass(listiterator);
665 
666 }
KrkValue krk_runtimeError(KrkClass *type, const char *fmt,...)
Produce and raise an exception with a formatted message.
Definition: exceptions.c:445
Functions for dealing with garbage collection and memory allocation.
void krk_markValue(KrkValue value)
During a GC scan cycle, mark a value as used.
Definition: memory.c:329
NativeFn krk_GenericAlias
Special value for type hint expressions.
Definition: obj_typing.c:67
Type object.
Definition: object.h:189
KrkCleanupCallback _ongcsweep
C function to call when the garbage collector is discarding an instance of this class.
Definition: object.h:198
KrkCleanupCallback _ongcscan
C function to call when the garbage collector visits an instance of this class in the scan phase.
Definition: object.h:197
KrkObj * _reprer
__repr__ Called to create a reproducible string representation of an instance
Definition: object.h:203
size_t allocSize
Size to allocate when creating instances of this class.
Definition: object.h:196
KrkObj obj
Base.
Definition: object.h:190
KrkTable methods
General attributes table.
Definition: object.h:192
void krk_finalizeClass(KrkClass *_class)
Finalize a class by collecting pointers to core methods.
An object of a class.
Definition: object.h:255
KrkInstance * krk_newInstance(KrkClass *_class)
Create a new instance of the given class.
Definition: object.c:339
Mutable array of values.
Definition: object.h:309
KrkValue krk_list_of(int argc, const KrkValue argv[], int hasKw)
Create a list object.
Definition: obj_list.c:30
KrkValueArray values
Stores the length, capacity, and actual values of the list.
Definition: object.h:311
KrkObj obj
Base.
Definition: object.h:284
The most basic object type.
Definition: object.h:41
uint16_t flags
General object flags, mostly related to garbage collection.
Definition: object.h:43
KrkNative * krk_defineNative(KrkTable *table, const char *name, NativeFn function)
Attach a native C function to an attribute table.
Definition: vm.c:194
void krk_attachNamedValue(KrkTable *table, const char name[], KrkValue obj)
Attach a value to an attribute table.
Definition: vm.c:825
KrkValue currentException
Definition: vm.h:173
KrkValue * stackTop
Definition: vm.h:168
int flags
Definition: vm.h:174
Flexible vector of stack references.
Definition: value.h:70
size_t capacity
Definition: value.h:71
KrkValue * values
Definition: value.h:73
void krk_initValueArray(KrkValueArray *array)
Initialize a value array.
Definition: value.c:11
void krk_freeValueArray(KrkValueArray *array)
Release relesources used by a value array.
Definition: value.c:28
void krk_writeValueArray(KrkValueArray *array, KrkValue value)
Add a value to a value array.
Definition: value.c:17
size_t count
Definition: value.h:72
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.
Definition: vm.c:317
int krk_valuesSame(KrkValue a, KrkValue b)
Compare two values by identity.
Definition: value.c:151
KrkClass * krk_getType(KrkValue value)
Get the class representing a value.
Definition: vm.c:275
int krk_valuesSameOrEqual(KrkValue a, KrkValue b)
Compare two values by identity, then by equality.
Inline flexible string array.
Definition: util.h:150
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.
Definition: builtins.c:380
#define KRK_DOC(thing, text)
Attach documentation to a thing of various types.
Definition: util.h:292
Definitions for primitive stack references.
Core API for the bytecode virtual machine.
#define vm
Convenience macro for namespacing.
Definition: vm.h:267
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.
Definition: vm.c:637
KrkValue krk_pop(void)
Pop the top of the stack.
Definition: vm.c:170
threadLocal KrkThreadState krk_currentThread
Thread-local VM state.
void krk_push(KrkValue value)
Push a stack value.
Definition: vm.c:157
KrkValue krk_callDirect(KrkObj *callable, int argCount)
Call a closure or native function with argCount arguments.
Definition: vm.c:771
KrkValue krk_peek(int distance)
Peek down from the top of the stack.
Definition: vm.c:178
KrkValue krk_operator_gt(KrkValue, KrkValue)
Compare to values, returning True if the left is greater than the right.