obj_set.c
1 #include <string.h>
2 #include <kuroko/vm.h>
3 #include <kuroko/object.h>
4 #include <kuroko/memory.h>
5 #include <kuroko/util.h>
6 
11 struct Set {
12  KrkInstance inst;
13  KrkTable entries;
14 };
15 
16 #define IS_set(o) krk_isInstanceOf(o,KRK_BASE_CLASS(set))
17 #define AS_set(o) ((struct Set*)AS_OBJECT(o))
18 
19 static void _set_gcscan(KrkInstance * self) {
20  krk_markTable(&((struct Set*)self)->entries);
21 }
22 
23 static void _set_gcsweep(KrkInstance * self) {
24  krk_freeTable(&((struct Set*)self)->entries);
25 }
26 
31 struct SetIterator {
32  KrkInstance inst;
33  KrkValue set;
34  size_t i;
35 };
36 #define IS_setiterator(o) krk_isInstanceOf(o,KRK_BASE_CLASS(setiterator))
37 #define AS_setiterator(o) ((struct SetIterator*)AS_OBJECT(o))
38 
39 static void _setiterator_gcscan(KrkInstance * self) {
40  krk_markValue(((struct SetIterator*)self)->set);
41 }
42 
43 #define CURRENT_CTYPE struct Set *
44 #define CURRENT_NAME self
45 
46 static int _set_init_callback(void * context, const KrkValue * values, size_t count) {
47  struct Set * self = context;
48  for (size_t i = 0; i < count; ++i) {
49  krk_tableSet(&self->entries, values[i], BOOLEAN_VAL(1));
50  if (unlikely(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) return 1;
51  }
52  return 0;
53 }
54 
55 KRK_Method(set,__init__) {
56  METHOD_TAKES_AT_MOST(1);
57  krk_initTable(&self->entries);
58  if (argc == 2) {
59  if (krk_unpackIterable(argv[1], self, _set_init_callback)) return NONE_VAL();
60  }
61  return NONE_VAL();
62 }
63 
64 KRK_Method(set,__contains__) {
65  METHOD_TAKES_EXACTLY(1);
66  KrkValue v;
67  return BOOLEAN_VAL(krk_tableGet(&self->entries, argv[1], &v));
68 }
69 
70 KRK_Method(set,__repr__) {
71  METHOD_TAKES_NONE();
72  if (((KrkObj*)self)->flags & KRK_OBJ_FLAGS_IN_REPR) return OBJECT_VAL(S("{...}"));
73  if (!self->entries.capacity) return OBJECT_VAL(S("set()"));
74  ((KrkObj*)self)->flags |= KRK_OBJ_FLAGS_IN_REPR;
75  struct StringBuilder sb = {0};
76  pushStringBuilder(&sb,'{');
77 
78  size_t c = 0;
79  size_t len = self->entries.capacity;
80  for (size_t i = 0; i < len; ++i) {
81  KrkTableEntry * entry = &self->entries.entries[i];
82  if (IS_KWARGS(entry->key)) continue;
83  if (c) pushStringBuilderStr(&sb, ", ", 2);
84  c++;
85  if (!krk_pushStringBuilderFormat(&sb, "%R", entry->key)) goto _error;
86  }
87 
88  pushStringBuilder(&sb,'}');
89  ((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
90  return finishStringBuilder(&sb);
91 
92 _error:
93  ((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
95  return NONE_VAL();
96 }
97 
98 KRK_Method(set,__and__) {
99  METHOD_TAKES_EXACTLY(1);
100  CHECK_ARG(1,set,struct Set*,them);
101 
102  KrkValue outSet = OBJECT_VAL(krk_newInstance(KRK_BASE_CLASS(set)));
103  krk_push(outSet);
104  FUNC_NAME(set,__init__)(1,&outSet,0);
105 
106  KrkClass * type = krk_getType(argv[1]);
107  if (!type->_contains)
108  return krk_runtimeError(vm.exceptions->typeError, "unsupported operand types for %s: '%T' and '%T'", "&", argv[0], argv[1]);
109 
110  for (size_t i = 0; i < self->entries.capacity; ++i) {
111  KrkTableEntry * entry = &self->entries.entries[i];
112  if (IS_KWARGS(entry->key)) continue;
113 
114  krk_push(argv[1]);
115  krk_push(entry->key);
116  KrkValue result = krk_callDirect(type->_contains, 2);
117 
118  if (IS_BOOLEAN(result) && AS_BOOLEAN(result)) {
119  krk_tableSet(&AS_set(outSet)->entries, entry->key, BOOLEAN_VAL(1));
120  }
121  }
122 
123  return krk_pop();
124 }
125 
126 KRK_Method(set,__xor__) {
127  METHOD_TAKES_EXACTLY(1);
128  CHECK_ARG(1,set,struct Set*,them);
129 
130  KrkValue outSet = OBJECT_VAL(krk_newInstance(KRK_BASE_CLASS(set)));
131  krk_push(outSet);
132  FUNC_NAME(set,__init__)(1,&outSet,0);
133 
134  KrkClass * type = krk_getType(argv[1]);
135  if (!type->_contains)
136  return krk_runtimeError(vm.exceptions->typeError, "unsupported operand types for %s: '%T' and '%T'", "&", argv[0], argv[1]);
137 
138  for (size_t i = 0; i < self->entries.capacity; ++i) {
139  KrkTableEntry * entry = &self->entries.entries[i];
140  if (IS_KWARGS(entry->key)) continue;
141 
142  krk_push(argv[1]);
143  krk_push(entry->key);
144  KrkValue result = krk_callDirect(type->_contains, 2);
145 
146  if (IS_BOOLEAN(result) && !AS_BOOLEAN(result)) {
147  krk_tableSet(&AS_set(outSet)->entries, entry->key, BOOLEAN_VAL(1));
148  }
149  }
150 
151  /* Ours better have something... */
152  type = krk_getType(argv[0]);
153 
154  for (size_t i = 0; i < them->entries.capacity; ++i) {
155  KrkTableEntry * entry = &them->entries.entries[i];
156  if (IS_KWARGS(entry->key)) continue;
157 
158  krk_push(argv[0]);
159  krk_push(entry->key);
160  KrkValue result = krk_callDirect(type->_contains, 2);
161 
162  if (IS_BOOLEAN(result) && !AS_BOOLEAN(result)) {
163  krk_tableSet(&AS_set(outSet)->entries, entry->key, BOOLEAN_VAL(1));
164  }
165  }
166 
167  return krk_pop();
168 }
169 
170 
171 KRK_Method(set,__or__) {
172  METHOD_TAKES_EXACTLY(1);
173  CHECK_ARG(1,set,struct Set*,them);
174 
175  KrkValue outSet = OBJECT_VAL(krk_newInstance(KRK_BASE_CLASS(set)));
176  krk_push(outSet);
177  FUNC_NAME(set,__init__)(1,&outSet,0);
178 
179  krk_tableAddAll(&self->entries, &AS_set(outSet)->entries);
180  krk_tableAddAll(&them->entries, &AS_set(outSet)->entries);
181 
182  return krk_pop();
183 }
184 
185 KRK_Method(set,__len__) {
186  METHOD_TAKES_NONE();
187  return INTEGER_VAL(self->entries.count);
188 }
189 
190 KRK_Method(set,__eq__) {
191  METHOD_TAKES_EXACTLY(1);
192  if (!IS_set(argv[1]))
193  return NOTIMPL_VAL();
194  CHECK_ARG(1,set,struct Set*,them);
195  if (self->entries.count != them->entries.count)
196  return BOOLEAN_VAL(0);
197 
198  KrkValue v;
199 
200  for (unsigned int i = 0; i < self->entries.capacity; ++i) {
201  if (IS_KWARGS(self->entries.entries[i].key)) continue;
202  if (!krk_tableGet(&them->entries, self->entries.entries[i].key, &v)) return BOOLEAN_VAL(0);
203  }
204 
205  return BOOLEAN_VAL(1);
206 }
207 
208 /* Strict subset */
209 KRK_Method(set,__lt__) {
210  METHOD_TAKES_EXACTLY(1);
211  if (!IS_set(argv[1]))
212  return NOTIMPL_VAL();
213  struct Set * them = AS_set(argv[1]);
214  if (self->entries.count == them->entries.count)
215  return BOOLEAN_VAL(0);
216  KrkValue v;
217  for (unsigned int i = 0; i < self->entries.capacity; ++i) {
218  if (IS_KWARGS(self->entries.entries[i].key)) continue;
219  if (!krk_tableGet(&them->entries, self->entries.entries[i].key, &v)) return BOOLEAN_VAL(0);
220  }
221  return BOOLEAN_VAL(1);
222 }
223 
224 /* Subset or equal to */
225 KRK_Method(set,__le__) {
226  METHOD_TAKES_EXACTLY(1);
227  if (!IS_set(argv[1]))
228  return NOTIMPL_VAL();
229  struct Set * them = AS_set(argv[1]);
230  KrkValue v;
231  for (unsigned int i = 0; i < self->entries.capacity; ++i) {
232  if (IS_KWARGS(self->entries.entries[i].key)) continue;
233  if (!krk_tableGet(&them->entries, self->entries.entries[i].key, &v)) return BOOLEAN_VAL(0);
234  }
235  return BOOLEAN_VAL(1);
236 }
237 
238 /* Strict superset */
239 KRK_Method(set,__gt__) {
240  METHOD_TAKES_EXACTLY(1);
241  if (!IS_set(argv[1]))
242  return NOTIMPL_VAL();
243  struct Set * them = AS_set(argv[1]);
244  if (self->entries.count == them->entries.count)
245  return BOOLEAN_VAL(0);
246  KrkValue v;
247  for (unsigned int i = 0; i < them->entries.capacity; ++i) {
248  if (IS_KWARGS(them->entries.entries[i].key)) continue;
249  if (!krk_tableGet(&self->entries, them->entries.entries[i].key, &v)) return BOOLEAN_VAL(0);
250  }
251  return BOOLEAN_VAL(1);
252 }
253 
254 KRK_Method(set,__ge__) {
255  METHOD_TAKES_EXACTLY(1);
256  if (!IS_set(argv[1]))
257  return NOTIMPL_VAL();
258  struct Set * them = AS_set(argv[1]);
259  KrkValue v;
260  for (unsigned int i = 0; i < them->entries.capacity; ++i) {
261  if (IS_KWARGS(them->entries.entries[i].key)) continue;
262  if (!krk_tableGet(&self->entries, them->entries.entries[i].key, &v)) return BOOLEAN_VAL(0);
263  }
264  return BOOLEAN_VAL(1);
265 }
266 
267 
268 KRK_Method(set,add) {
269  METHOD_TAKES_EXACTLY(1);
270  krk_tableSet(&self->entries, argv[1], BOOLEAN_VAL(1));
271  return NONE_VAL();
272 }
273 
274 KRK_Method(set,remove) {
275  METHOD_TAKES_EXACTLY(1);
276  if (!krk_tableDelete(&self->entries, argv[1]))
277  return krk_runtimeError(vm.exceptions->keyError, "key error");
278  return NONE_VAL();
279 }
280 
281 KRK_Method(set,discard) {
282  METHOD_TAKES_EXACTLY(1);
283  krk_tableDelete(&self->entries, argv[1]);
284  return NONE_VAL();
285 }
286 
287 KRK_Method(set,clear) {
288  METHOD_TAKES_NONE();
289  krk_freeTable(&self->entries);
290  krk_initTable(&self->entries);
291  return NONE_VAL();
292 }
293 
294 KRK_Method(set,update) {
295  METHOD_TAKES_AT_MOST(1);
296  if (argc > 1) {
297  if (IS_set(argv[1])) {
298  krk_tableAddAll(&AS_set(argv[1])->entries, &self->entries);
299  } else {
300  if (krk_unpackIterable(argv[1], self, _set_init_callback)) return NONE_VAL();
301  }
302  }
303  return NONE_VAL();
304 }
305 
306 FUNC_SIG(setiterator,__init__);
307 
308 KRK_Method(set,__iter__) {
309  METHOD_TAKES_NONE();
310  KrkInstance * output = krk_newInstance(KRK_BASE_CLASS(setiterator));
311  krk_push(OBJECT_VAL(output));
312  FUNC_NAME(setiterator,__init__)(2,(KrkValue[]){krk_peek(0), argv[0]}, 0);
313  return krk_pop();
314 }
315 
316 #undef CURRENT_CTYPE
317 #define CURRENT_CTYPE struct SetIterator *
318 
319 KRK_Method(setiterator,__init__) {
320  METHOD_TAKES_EXACTLY(1);
321  CHECK_ARG(1,set,void*,source);
322  self->set = argv[1];
323  self->i = 0;
324  return NONE_VAL();
325 }
326 
327 KRK_Method(setiterator,__call__) {
328  METHOD_TAKES_NONE();
329 
330  if (unlikely(!IS_set(self->set))) return argv[0];
331 
332  do {
333  if (self->i >= AS_set(self->set)->entries.capacity) return argv[0];
334  if (!IS_KWARGS(AS_set(self->set)->entries.entries[self->i].key)) {
335  krk_push(AS_set(self->set)->entries.entries[self->i].key);
336  self->i++;
337  return krk_pop();
338  }
339  self->i++;
340  } while (1);
341 }
342 
343 KrkValue krk_set_of(int argc, const KrkValue argv[], int hasKw) {
344  KrkValue outSet = OBJECT_VAL(krk_newInstance(KRK_BASE_CLASS(set)));
345  krk_push(outSet);
346  krk_initTable(&AS_set(outSet)->entries);
347 
348  while (argc) {
349  krk_tableSet(&AS_set(outSet)->entries, argv[argc-1], BOOLEAN_VAL(1));
350  argc--;
351  }
352 
353  return krk_pop();
354 }
355 
356 _noexport
357 void _createAndBind_setClass(void) {
358  KrkClass * set = krk_makeClass(vm.builtins, &KRK_BASE_CLASS(set), "set", vm.baseClasses->objectClass);
359  set->allocSize = sizeof(struct Set);
360  set->_ongcscan = _set_gcscan;
361  set->_ongcsweep = _set_gcsweep;
362  BIND_METHOD(set,__init__);
363  BIND_METHOD(set,__repr__);
364  BIND_METHOD(set,__len__);
365  BIND_METHOD(set,__eq__);
366  BIND_METHOD(set,__lt__);
367  BIND_METHOD(set,__gt__);
368  BIND_METHOD(set,__le__);
369  BIND_METHOD(set,__ge__);
370  BIND_METHOD(set,__and__);
371  BIND_METHOD(set,__or__);
372  BIND_METHOD(set,__xor__);
373  BIND_METHOD(set,__contains__);
374  BIND_METHOD(set,__iter__);
375  KRK_DOC(BIND_METHOD(set,add),
376  "@brief Add an element to the set.\n"
377  "@arguments value\n\n"
378  "Adds the given @p value to the set. @p value must be hashable.");
379  KRK_DOC(BIND_METHOD(set,remove),
380  "@brief Remove an element from the set.\n"
381  "@arguments value\n\n"
382  "Removes @p value from the set, raising @ref KeyError if it is not a member of the set.");
383  KRK_DOC(BIND_METHOD(set,discard),
384  "@brief Remove an element from the set, quietly.\n"
385  "@arguments value\n\n"
386  "Removes @p value from the set, without raising an exception if it is not a member.");
387  KRK_DOC(BIND_METHOD(set,clear),
388  "@brief Empty the set.\n\n"
389  "Removes all elements from the set, in-place.");
390  BIND_METHOD(set,update);
391  krk_attachNamedValue(&set->methods, "__hash__", NONE_VAL());
392  krk_finalizeClass(set);
393 
394  KrkClass * setiterator = krk_makeClass(vm.builtins, &KRK_BASE_CLASS(setiterator), "setiterator", vm.baseClasses->objectClass);
395  setiterator->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
396  setiterator->allocSize = sizeof(struct SetIterator);
397  setiterator->_ongcscan = _setiterator_gcscan;
398  BIND_METHOD(setiterator,__init__);
399  BIND_METHOD(setiterator,__call__);
400  krk_finalizeClass(setiterator);
401 }
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
void krk_markTable(KrkTable *table)
During a GC scan cycle, mark the contents of a table as used.
Definition: memory.c:444
Struct definitions for core object types.
Type object.
Definition: object.h:215
KrkCleanupCallback _ongcsweep
C function to call when the garbage collector is discarding an instance of this class.
Definition: object.h:224
KrkCleanupCallback _ongcscan
C function to call when the garbage collector visits an instance of this class in the scan phase.
Definition: object.h:223
size_t allocSize
Size to allocate when creating instances of this class.
Definition: object.h:222
KrkObj obj
Base.
Definition: object.h:216
KrkClass * krk_makeClass(KrkInstance *module, KrkClass **_class, const char *name, KrkClass *base)
Convenience function for creating new types.
Definition: vm.c:164
KrkTable methods
General attributes table.
Definition: object.h:218
void krk_finalizeClass(KrkClass *_class)
Finalize a class by collecting pointers to core methods.
Definition: vm.c:189
KrkObj * _contains
__contains__ Called to resolve in (as a binary operator)
Definition: object.h:241
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
One (key,value) pair in a table.
Definition: table.h:20
Simple hash table of arbitrary keys to values.
Definition: table.h:28
void krk_initTable(KrkTable *table)
Initialize a hash table.
Definition: table.c:33
int krk_tableGet(KrkTable *table, KrkValue key, KrkValue *value)
Obtain the value associated with a key in a table.
Definition: table.c:211
int krk_tableDelete(KrkTable *table, KrkValue key)
Remove a key from a hash table.
Definition: table.c:238
int krk_tableSet(KrkTable *table, KrkValue key, KrkValue value)
Assign a value to a key in a table.
Definition: table.c:148
KrkTableEntry * entries
Definition: table.h:32
void krk_attachNamedValue(KrkTable *table, const char name[], KrkValue obj)
Attach a value to an attribute table.
Definition: vm.c:794
void krk_tableAddAll(KrkTable *from, KrkTable *to)
Add all key-value pairs from 'from' into 'to'.
Definition: table.c:202
void krk_freeTable(KrkTable *table)
Release resources associated with a hash table.
Definition: table.c:41
size_t count
Definition: table.h:29
size_t capacity
Definition: table.h:30
int flags
Definition: vm.h:165
Stack reference or primative value.
KrkClass * krk_getType(KrkValue value)
Get the class representing a value.
Definition: vm.c:240
Iterator over the values in a set.
Definition: obj_set.c:31
Mutable unordered set of values.
Definition: obj_set.c:11
KrkValue krk_set_of(int argc, const KrkValue argv[], int hasKw)
Create a set object.
Definition: obj_set.c:343
Inline flexible string array.
Definition: util.h:162
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
#define KRK_DOC(thing, text)
Attach documentation to a thing of various types.
Definition: util.h:304
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_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_callDirect(KrkObj *callable, int argCount)
Call a closure or native function with argCount arguments.
Definition: vm.c:740
KrkValue krk_peek(int distance)
Peek down from the top of the stack.
Definition: vm.c:139