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) ? GROW_CAPACITY(old) : (positionals->count + count);
17  positionals->values = 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  KrkClass * type = krk_getType(self->values.values[i]);
152  krk_push(self->values.values[i]);
153  KrkValue result = krk_callDirect(type->_reprer, 1);
154  if (IS_STRING(result)) {
155  pushStringBuilderStr(&sb, AS_STRING(result)->chars, AS_STRING(result)->length);
156  }
157  if (i != self->values.count - 1) {
158  pushStringBuilderStr(&sb, ", ", 2);
159  }
160  }
161 
162  if (self->values.count == 1) {
163  pushStringBuilder(&sb, ',');
164  }
165 
166  pushStringBuilder(&sb, ')');
167  ((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
168  return finishStringBuilder(&sb);
169 }
170 
171 KRK_Method(tuple,__add__) {
172  METHOD_TAKES_EXACTLY(1);
173  if (!IS_tuple(argv[1]))
174  return krk_runtimeError(vm.exceptions->typeError,
175  "can only concatenate tuple (not '%T') to tuple", argv[1]);
176 
177  KrkTuple * other = AS_tuple(argv[1]);
178  KrkTuple * out = krk_newTuple(self->values.count + other->values.count);
179  krk_push(OBJECT_VAL(out));
180  for (size_t i = 0; i < self->values.count; ++i) {
181  out->values.values[out->values.count++] = self->values.values[i];
182  }
183  for (size_t i = 0; i < other->values.count; ++i) {
184  out->values.values[out->values.count++] = other->values.values[i];
185  }
186  return krk_pop();
187 }
188 
193 struct TupleIter {
194  KrkInstance inst;
195  KrkValue myTuple;
196  int i;
197 };
198 
199 static KrkValue _tuple_iter_init(int argc, const KrkValue argv[], int hasKw) {
200  struct TupleIter * self = (struct TupleIter *)AS_OBJECT(argv[0]);
201  self->myTuple = argv[1];
202  self->i = 0;
203  return argv[0];
204 }
205 
206 static void _tuple_iter_gcscan(KrkInstance * self) {
207  krk_markValue(((struct TupleIter*)self)->myTuple);
208 }
209 
210 static KrkValue _tuple_iter_call(int argc, const KrkValue argv[], int hasKw) {
211  struct TupleIter * self = (struct TupleIter *)AS_OBJECT(argv[0]);
212  KrkValue t = self->myTuple; /* Tuple to iterate */
213  int i = self->i;
214  if (i >= (krk_integer_type)AS_TUPLE(t)->values.count) {
215  return argv[0];
216  } else {
217  self->i = i+1;
218  return AS_TUPLE(t)->values.values[i];
219  }
220 }
221 
222 KRK_Method(tuple,__iter__) {
223  KrkInstance * output = krk_newInstance(vm.baseClasses->tupleiteratorClass);
224  krk_push(OBJECT_VAL(output));
225  _tuple_iter_init(2, (KrkValue[]){krk_peek(0), argv[0]}, 0);
226  krk_pop();
227  return OBJECT_VAL(output);
228 }
229 
230 KRK_Method(tuple,__hash__) {
231  if (self->obj.flags & KRK_OBJ_FLAGS_VALID_HASH) {
232  return INTEGER_VAL(self->obj.hash);
233  }
234  uint32_t t = self->values.count;
235  uint32_t m = 0x3456;
236  for (size_t i = 0; i < (size_t)self->values.count; ++i) {
237  uint32_t step = 0;
238  if (krk_hashValue(self->values.values[i], &step)) goto _unhashable;
239  t = (t ^ step) * m;
240  m += 2 * (self->values.count - i) + 82520;
241  }
242  self->obj.hash = t;
243  self->obj.flags |= KRK_OBJ_FLAGS_VALID_HASH;
244  return INTEGER_VAL(self->obj.hash);
245 _unhashable:
246  return NONE_VAL();
247 }
248 
249 KRK_Method(tuple,__mul__) {
250  METHOD_TAKES_EXACTLY(1);
251 
252  if (!IS_INTEGER(argv[1])) return NOTIMPL_VAL();
253 
254  ssize_t count = AS_INTEGER(argv[1]);
255  if (count < 0) count = 0;
256  KrkTuple * out = krk_newTuple(count * self->values.count);
257  krk_push(OBJECT_VAL(out));
258  for (ssize_t i = 0; i < count; ++i) {
259  for (size_t j = 0; j < self->values.count; ++j) {
260  out->values.values[out->values.count++] = self->values.values[j];
261  }
262  }
263 
264  return krk_pop();
265 }
266 
267 _noexport
268 void _createAndBind_tupleClass(void) {
269  KrkClass * tuple = ADD_BASE_CLASS(vm.baseClasses->tupleClass, "tuple", vm.baseClasses->objectClass);
270  tuple->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
271  tuple->allocSize = 0;
272  BIND_STATICMETHOD(tuple,__new__);
273  BIND_METHOD(tuple,__repr__);
274  BIND_METHOD(tuple,__getitem__);
275  BIND_METHOD(tuple,__len__);
276  BIND_METHOD(tuple,__contains__);
277  BIND_METHOD(tuple,__iter__);
278  BIND_METHOD(tuple,__eq__);
279  BIND_METHOD(tuple,__lt__);
280  BIND_METHOD(tuple,__gt__);
281  BIND_METHOD(tuple,__le__);
282  BIND_METHOD(tuple,__ge__);
283  BIND_METHOD(tuple,__hash__);
284  BIND_METHOD(tuple,__add__);
285  BIND_METHOD(tuple,__mul__);
286  krk_defineNative(&tuple->methods, "__str__", FUNC_NAME(tuple,__repr__));
287  krk_finalizeClass(tuple);
288 
289  ADD_BASE_CLASS(vm.baseClasses->tupleiteratorClass, "tupleiterator", vm.baseClasses->objectClass);
290  vm.baseClasses->tupleiteratorClass->allocSize = sizeof(struct TupleIter);
291  vm.baseClasses->tupleiteratorClass->_ongcscan = _tuple_iter_gcscan;
292  krk_defineNative(&vm.baseClasses->tupleiteratorClass->methods, "__init__", _tuple_iter_init);
293  krk_defineNative(&vm.baseClasses->tupleiteratorClass->methods, "__call__", _tuple_iter_call);
294  krk_finalizeClass(vm.baseClasses->tupleiteratorClass);
295 
296 }
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
Type object.
Definition: object.h:189
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
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
KrkValue * stackTop
Definition: vm.h:168
Immutable sequence of arbitrary values.
Definition: object.h:297
KrkValueArray values
Stores the length, capacity, and actual values of the tuple.
Definition: object.h:299
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:353
Flexible vector of stack references.
Definition: value.h:70
size_t capacity
Definition: value.h:71
KrkValue * values
Definition: value.h:73
size_t count
Definition: value.h:72
Stack reference or primative value.
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.
int krk_hashValue(KrkValue value, uint32_t *hashOut)
Calculate the hash for a value.
Definition: table.c:25
Inline flexible string array.
Definition: util.h:150
Iterator over the values in a tuple.
Definition: obj_tuple.c:193
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
Definitions for primitive stack references.
Core API for the bytecode virtual machine.
#define vm
Convenience macro for namespacing.
Definition: vm.h:267
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