obj_tuple.c
1 #include <string.h>
2 #include <limits.h>
3 #include <kuroko/vm.h>
4 #include <kuroko/value.h>
5 #include <kuroko/memory.h>
6 #include <kuroko/util.h>
7 
8 #define TUPLE_WRAP_INDEX() \
9  if (index < 0) index += self->values.count; \
10  if (index < 0 || index >= (krk_integer_type)self->values.count) return krk_runtimeError(vm.exceptions->indexError, "tuple index out of range: %zd", (ssize_t)index)
11 
12 static int _tuple_init_callback(void * context, const KrkValue * values, size_t count) {
13  KrkValueArray * positionals = context;
14  if (positionals->count + count > positionals->capacity) {
15  size_t old = positionals->capacity;
16  positionals->capacity = (count == 1) ? KRK_GROW_CAPACITY(old) : (positionals->count + count);
17  positionals->values = KRK_GROW_ARRAY(KrkValue, positionals->values, old, positionals->capacity);
18  }
19 
20  for (size_t i = 0; i < count; ++i) {
21  positionals->values[positionals->count++] = values[i];
22  }
23 
24  return 0;
25 }
26 
27 KRK_StaticMethod(tuple,__new__) {
28  METHOD_TAKES_AT_MOST(1);
29  if (argc == 1) {
30  return OBJECT_VAL(krk_newTuple(0));
31  }
32  krk_push(OBJECT_VAL(krk_newTuple(0)));
33  KrkValueArray * positionals = &AS_TUPLE(krk_peek(0))->values;
34  KrkValue other = argv[1];
35  krk_unpackIterable(other, positionals, _tuple_init_callback);
36  return krk_pop();
37 }
38 
39 /* tuple creator */
40 KrkValue krk_tuple_of(int argc, const KrkValue argv[], int hasKw) {
41  KrkTuple * self = krk_newTuple(argc);
42  krk_push(OBJECT_VAL(self));
43  for (size_t i = 0; i < (size_t)argc; ++i) {
44  self->values.values[self->values.count++] = argv[i];
45  }
46  krk_pop();
47 
48  return OBJECT_VAL(self);
49 }
50 
51 #define IS_tuple(o) IS_TUPLE(o)
52 #define AS_tuple(o) AS_TUPLE(o)
53 
54 #define CURRENT_CTYPE KrkTuple *
55 #define CURRENT_NAME self
56 
57 KRK_Method(tuple,__contains__) {
58  METHOD_TAKES_EXACTLY(1);
59  for (size_t i = 0; i < self->values.count; ++i) {
60  if (krk_valuesSameOrEqual(self->values.values[i], argv[1])) return BOOLEAN_VAL(1);
61  }
62  return BOOLEAN_VAL(0);
63 }
64 
65 KRK_Method(tuple,__len__) {
66  METHOD_TAKES_NONE();
67  return INTEGER_VAL(self->values.count);
68 }
69 
70 KRK_Method(tuple,__getitem__) {
71  METHOD_TAKES_EXACTLY(1);
72  if (IS_INTEGER(argv[1])) {
73  CHECK_ARG(1,int,krk_integer_type,index);
74  TUPLE_WRAP_INDEX();
75  return self->values.values[index];
76  } else if (IS_slice(argv[1])) {
77  KRK_SLICER(argv[1],self->values.count) {
78  return NONE_VAL();
79  }
80 
81  if (step == 1) {
82  krk_integer_type len = end - start;
83  KrkValue result = krk_tuple_of(len, &self->values.values[start], 0);
84  return result;
85  } else {
86  /* iterate and push */
87  krk_push(NONE_VAL());
88  krk_integer_type len = 0;
89  krk_integer_type i = start;
90  while ((step < 0) ? (i > end) : (i < end)) {
91  krk_push(self->values.values[i]);
92  len++;
93  i += step;
94  }
95 
96  /* make into a list */
98  krk_currentThread.stackTop[-len-1] = result;
99  while (len) {
100  krk_pop();
101  len--;
102  }
103 
104  return krk_pop();
105  }
106  } else {
107  return TYPE_ERROR(int or slice, argv[1]);
108  }
109 }
110 
111 KRK_Method(tuple,__eq__) {
112  METHOD_TAKES_EXACTLY(1);
113  if (!IS_tuple(argv[1])) return NOTIMPL_VAL();
114  KrkTuple * them = AS_tuple(argv[1]);
115  if (self->values.count != them->values.count) return BOOLEAN_VAL(0);
116  for (size_t i = 0; i < self->values.count; ++i) {
117  if (!krk_valuesSameOrEqual(self->values.values[i], them->values.values[i])) return BOOLEAN_VAL(0);
118  }
119  return BOOLEAN_VAL(1);
120 }
121 
122 #define MAKE_TUPLE_COMPARE(name,op) \
123  KRK_Method(tuple,__ ## name ## __) { \
124  METHOD_TAKES_EXACTLY(1); \
125  if (!IS_tuple(argv[1])) return NOTIMPL_VAL(); \
126  KrkTuple * them = AS_tuple(argv[1]); \
127  size_t lesser = self->values.count < them->values.count ? self->values.count : them->values.count; \
128  for (size_t i = 0; i < lesser; ++i) { \
129  KrkValue a = self->values.values[i]; \
130  KrkValue b = them->values.values[i]; \
131  if (krk_valuesSameOrEqual(a,b)) continue; \
132  if (unlikely(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) return NONE_VAL(); \
133  return krk_operator_ ## name(a,b); \
134  } \
135  return BOOLEAN_VAL((self->values.count op them->values.count)); \
136  }
137 
138 MAKE_TUPLE_COMPARE(gt,>)
139 MAKE_TUPLE_COMPARE(lt,<)
140 MAKE_TUPLE_COMPARE(ge,>=)
141 MAKE_TUPLE_COMPARE(le,<=)
142 
143 KRK_Method(tuple,__repr__) {
144  if (((KrkObj*)self)->flags & KRK_OBJ_FLAGS_IN_REPR) return OBJECT_VAL(S("(...)"));
145  ((KrkObj*)self)->flags |= KRK_OBJ_FLAGS_IN_REPR;
146  /* String building time. */
147  struct StringBuilder sb = {0};
148  pushStringBuilder(&sb, '(');
149 
150  for (size_t i = 0; i < self->values.count; ++i) {
151  if (i) pushStringBuilderStr(&sb, ", ", 2);
152  if (!krk_pushStringBuilderFormat(&sb, "%R", self->values.values[i])) goto _error;
153  }
154 
155  if (self->values.count == 1) {
156  pushStringBuilder(&sb, ',');
157  }
158 
159  pushStringBuilder(&sb, ')');
160  ((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
161  return finishStringBuilder(&sb);
162 
163 _error:
164  ((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
166  return NONE_VAL();
167 }
168 
169 KRK_Method(tuple,__add__) {
170  METHOD_TAKES_EXACTLY(1);
171  if (!IS_tuple(argv[1]))
172  return krk_runtimeError(vm.exceptions->typeError,
173  "can only concatenate tuple (not '%T') to tuple", argv[1]);
174 
175  KrkTuple * other = AS_tuple(argv[1]);
176  KrkTuple * out = krk_newTuple(self->values.count + other->values.count);
177  krk_push(OBJECT_VAL(out));
178  for (size_t i = 0; i < self->values.count; ++i) {
179  out->values.values[out->values.count++] = self->values.values[i];
180  }
181  for (size_t i = 0; i < other->values.count; ++i) {
182  out->values.values[out->values.count++] = other->values.values[i];
183  }
184  return krk_pop();
185 }
186 
191 struct TupleIter {
192  KrkInstance inst;
193  KrkValue myTuple;
194  int i;
195 };
196 
197 static KrkValue _tuple_iter_init(int argc, const KrkValue argv[], int hasKw) {
198  struct TupleIter * self = (struct TupleIter *)AS_OBJECT(argv[0]);
199  self->myTuple = argv[1];
200  self->i = 0;
201  return argv[0];
202 }
203 
204 static void _tuple_iter_gcscan(KrkInstance * self) {
205  krk_markValue(((struct TupleIter*)self)->myTuple);
206 }
207 
208 static KrkValue _tuple_iter_call(int argc, const KrkValue argv[], int hasKw) {
209  struct TupleIter * self = (struct TupleIter *)AS_OBJECT(argv[0]);
210  KrkValue t = self->myTuple; /* Tuple to iterate */
211  int i = self->i;
212  if (i >= (krk_integer_type)AS_TUPLE(t)->values.count) {
213  return argv[0];
214  } else {
215  self->i = i+1;
216  return AS_TUPLE(t)->values.values[i];
217  }
218 }
219 
220 KRK_Method(tuple,__iter__) {
221  KrkInstance * output = krk_newInstance(vm.baseClasses->tupleiteratorClass);
222  krk_push(OBJECT_VAL(output));
223  _tuple_iter_init(2, (KrkValue[]){krk_peek(0), argv[0]}, 0);
224  krk_pop();
225  return OBJECT_VAL(output);
226 }
227 
228 KRK_Method(tuple,__hash__) {
229  if (self->obj.flags & KRK_OBJ_FLAGS_VALID_HASH) {
230  return INTEGER_VAL(self->obj.hash);
231  }
232  uint32_t t = self->values.count;
233  uint32_t m = 0x3456;
234  for (size_t i = 0; i < (size_t)self->values.count; ++i) {
235  uint32_t step = 0;
236  if (krk_hashValue(self->values.values[i], &step)) goto _unhashable;
237  t = (t ^ step) * m;
238  m += 2 * (self->values.count - i) + 82520;
239  }
240  self->obj.hash = t;
241  self->obj.flags |= KRK_OBJ_FLAGS_VALID_HASH;
242  return INTEGER_VAL(self->obj.hash);
243 _unhashable:
244  return NONE_VAL();
245 }
246 
247 KRK_Method(tuple,__mul__) {
248  METHOD_TAKES_EXACTLY(1);
249 
250  if (!IS_INTEGER(argv[1])) return NOTIMPL_VAL();
251 
252  ssize_t count = AS_INTEGER(argv[1]);
253  if (count < 0) count = 0;
254  KrkTuple * out = krk_newTuple(count * self->values.count);
255  krk_push(OBJECT_VAL(out));
256  for (ssize_t i = 0; i < count; ++i) {
257  for (size_t j = 0; j < self->values.count; ++j) {
258  out->values.values[out->values.count++] = self->values.values[j];
259  }
260  }
261 
262  return krk_pop();
263 }
264 
265 _noexport
266 void _createAndBind_tupleClass(void) {
267  KrkClass * tuple = ADD_BASE_CLASS(vm.baseClasses->tupleClass, "tuple", vm.baseClasses->objectClass);
268  tuple->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
269  tuple->allocSize = 0;
270  BIND_STATICMETHOD(tuple,__new__);
271  BIND_METHOD(tuple,__repr__);
272  BIND_METHOD(tuple,__getitem__);
273  BIND_METHOD(tuple,__len__);
274  BIND_METHOD(tuple,__contains__);
275  BIND_METHOD(tuple,__iter__);
276  BIND_METHOD(tuple,__eq__);
277  BIND_METHOD(tuple,__lt__);
278  BIND_METHOD(tuple,__gt__);
279  BIND_METHOD(tuple,__le__);
280  BIND_METHOD(tuple,__ge__);
281  BIND_METHOD(tuple,__hash__);
282  BIND_METHOD(tuple,__add__);
283  BIND_METHOD(tuple,__mul__);
284  krk_finalizeClass(tuple);
285 
286  ADD_BASE_CLASS(vm.baseClasses->tupleiteratorClass, "tupleiterator", vm.baseClasses->objectClass);
287  vm.baseClasses->tupleiteratorClass->allocSize = sizeof(struct TupleIter);
288  vm.baseClasses->tupleiteratorClass->_ongcscan = _tuple_iter_gcscan;
289  krk_defineNative(&vm.baseClasses->tupleiteratorClass->methods, "__init__", _tuple_iter_init);
290  krk_defineNative(&vm.baseClasses->tupleiteratorClass->methods, "__call__", _tuple_iter_call);
291  krk_finalizeClass(vm.baseClasses->tupleiteratorClass);
292 
293 }
KrkValue krk_runtimeError(KrkClass *type, const char *fmt,...)
Produce and raise an exception with a formatted message.
Definition: exceptions.c:460
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:334
Type object.
Definition: object.h:215
size_t allocSize
Size to allocate when creating instances of this class.
Definition: object.h:222
KrkObj obj
Base.
Definition: object.h:216
void krk_finalizeClass(KrkClass *_class)
Finalize a class by collecting pointers to core methods.
Definition: vm.c:189
An object of a class.
Definition: object.h:281
KrkInstance * krk_newInstance(KrkClass *_class)
Create a new instance of the given class.
Definition: object.c:343
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:155
KrkValue * stackTop
Definition: vm.h:159
Immutable sequence of arbitrary values.
Definition: object.h:323
KrkValueArray values
Stores the length, capacity, and actual values of the tuple.
Definition: object.h:325
KrkValue krk_tuple_of(int argc, const KrkValue argv[], int hasKw)
Create a tuple object.
Definition: obj_tuple.c:40
KrkTuple * krk_newTuple(size_t length)
Create a new tuple.
Definition: object.c:357
Flexible vector of stack references.
Definition: value.h:75
size_t capacity
Definition: value.h:76
KrkValue * values
Definition: value.h:78
size_t count
Definition: value.h:77
Stack reference or primative value.
int krk_valuesSameOrEqual(KrkValue a, KrkValue b)
Compare two values by identity, then by equality.
Definition: value.c:96
int krk_hashValue(KrkValue value, uint32_t *hashOut)
Calculate the hash for a value.
Definition: table.c:47
Inline flexible string array.
Definition: util.h:162
Iterator over the values in a tuple.
Definition: obj_tuple.c:191
Utilities for creating native bindings.
KrkValue krk_discardStringBuilder(struct StringBuilder *sb)
Discard the contents of a string builder.
Definition: obj_str.c:1123
int krk_unpackIterable(KrkValue iterable, void *context, int callback(void *, const KrkValue *, size_t))
Unpack an iterable.
Definition: builtins.c:387
Definitions for primitive stack references.
Core API for the bytecode virtual machine.
krk_threadLocal KrkThreadState krk_currentThread
Thread-local VM state.
#define vm
Convenience macro for namespacing.
Definition: vm.h:257
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:601
KrkValue krk_pop(void)
Pop the top of the stack.
Definition: vm.c:131
void krk_push(KrkValue value)
Push a stack value.
Definition: vm.c:118
KrkValue krk_peek(int distance)
Peek down from the top of the stack.
Definition: vm.c:139