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 _unused;
67  return BOOLEAN_VAL(krk_tableGet(&self->entries, argv[1], &_unused));
68 }
69 
70 KRK_Method(set,__repr__) {
71  METHOD_TAKES_NONE();
72  if (((KrkObj*)self)->flags & KRK_OBJ_FLAGS_IN_REPR) return OBJECT_VAL("{...}");
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 > 0) {
84  pushStringBuilderStr(&sb, ", ", 2);
85  }
86  c++;
87 
88  KrkClass * type = krk_getType(entry->key);
89  krk_push(entry->key);
90  KrkValue result = krk_callDirect(type->_reprer, 1);
91  if (IS_STRING(result)) {
92  pushStringBuilderStr(&sb, AS_CSTRING(result), AS_STRING(result)->length);
93  }
94  }
95 
96  pushStringBuilder(&sb,'}');
97  ((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
98  return finishStringBuilder(&sb);
99 }
100 
101 KRK_Method(set,__and__) {
102  METHOD_TAKES_EXACTLY(1);
103  CHECK_ARG(1,set,struct Set*,them);
104 
105  KrkValue outSet = OBJECT_VAL(krk_newInstance(KRK_BASE_CLASS(set)));
106  krk_push(outSet);
107  FUNC_NAME(set,__init__)(1,&outSet,0);
108 
109  KrkClass * type = krk_getType(argv[1]);
110  if (!type->_contains)
111  return krk_runtimeError(vm.exceptions->typeError, "unsupported operand types for %s: '%T' and '%T'", "&", argv[0], argv[1]);
112 
113  for (size_t i = 0; i < self->entries.capacity; ++i) {
114  KrkTableEntry * entry = &self->entries.entries[i];
115  if (IS_KWARGS(entry->key)) continue;
116 
117  krk_push(argv[1]);
118  krk_push(entry->key);
119  KrkValue result = krk_callDirect(type->_contains, 2);
120 
121  if (IS_BOOLEAN(result) && AS_BOOLEAN(result)) {
122  krk_tableSet(&AS_set(outSet)->entries, entry->key, BOOLEAN_VAL(1));
123  }
124  }
125 
126  return krk_pop();
127 }
128 
129 KRK_Method(set,__xor__) {
130  METHOD_TAKES_EXACTLY(1);
131  CHECK_ARG(1,set,struct Set*,them);
132 
133  KrkValue outSet = OBJECT_VAL(krk_newInstance(KRK_BASE_CLASS(set)));
134  krk_push(outSet);
135  FUNC_NAME(set,__init__)(1,&outSet,0);
136 
137  KrkClass * type = krk_getType(argv[1]);
138  if (!type->_contains)
139  return krk_runtimeError(vm.exceptions->typeError, "unsupported operand types for %s: '%T' and '%T'", "&", argv[0], argv[1]);
140 
141  for (size_t i = 0; i < self->entries.capacity; ++i) {
142  KrkTableEntry * entry = &self->entries.entries[i];
143  if (IS_KWARGS(entry->key)) continue;
144 
145  krk_push(argv[1]);
146  krk_push(entry->key);
147  KrkValue result = krk_callDirect(type->_contains, 2);
148 
149  if (IS_BOOLEAN(result) && !AS_BOOLEAN(result)) {
150  krk_tableSet(&AS_set(outSet)->entries, entry->key, BOOLEAN_VAL(1));
151  }
152  }
153 
154  /* Ours better have something... */
155  type = krk_getType(argv[0]);
156 
157  for (size_t i = 0; i < them->entries.capacity; ++i) {
158  KrkTableEntry * entry = &them->entries.entries[i];
159  if (IS_KWARGS(entry->key)) continue;
160 
161  krk_push(argv[0]);
162  krk_push(entry->key);
163  KrkValue result = krk_callDirect(type->_contains, 2);
164 
165  if (IS_BOOLEAN(result) && !AS_BOOLEAN(result)) {
166  krk_tableSet(&AS_set(outSet)->entries, entry->key, BOOLEAN_VAL(1));
167  }
168  }
169 
170  return krk_pop();
171 }
172 
173 
174 KRK_Method(set,__or__) {
175  METHOD_TAKES_EXACTLY(1);
176  CHECK_ARG(1,set,struct Set*,them);
177 
178  KrkValue outSet = OBJECT_VAL(krk_newInstance(KRK_BASE_CLASS(set)));
179  krk_push(outSet);
180  FUNC_NAME(set,__init__)(1,&outSet,0);
181 
182  krk_tableAddAll(&self->entries, &AS_set(outSet)->entries);
183  krk_tableAddAll(&them->entries, &AS_set(outSet)->entries);
184 
185  return krk_pop();
186 }
187 
188 KRK_Method(set,__len__) {
189  METHOD_TAKES_NONE();
190  return INTEGER_VAL(self->entries.count);
191 }
192 
193 KRK_Method(set,__eq__) {
194  METHOD_TAKES_EXACTLY(1);
195  if (!IS_set(argv[1]))
196  return NOTIMPL_VAL();
197  CHECK_ARG(1,set,struct Set*,them);
198  if (self->entries.count != them->entries.count)
199  return BOOLEAN_VAL(0);
200 
201  KrkValue _unused;
202 
203  for (unsigned int i = 0; i < self->entries.capacity; ++i) {
204  if (IS_KWARGS(self->entries.entries[i].key)) continue;
205  if (!krk_tableGet(&them->entries, self->entries.entries[i].key, &_unused)) return BOOLEAN_VAL(0);
206  }
207 
208  return BOOLEAN_VAL(1);
209 }
210 
211 /* Strict subset */
212 KRK_Method(set,__lt__) {
213  METHOD_TAKES_EXACTLY(1);
214  if (!IS_set(argv[1]))
215  return NOTIMPL_VAL();
216  struct Set * them = AS_set(argv[1]);
217  if (self->entries.count == them->entries.count)
218  return BOOLEAN_VAL(0);
219  KrkValue _unused;
220  for (unsigned int i = 0; i < self->entries.capacity; ++i) {
221  if (IS_KWARGS(self->entries.entries[i].key)) continue;
222  if (!krk_tableGet(&them->entries, self->entries.entries[i].key, &_unused)) return BOOLEAN_VAL(0);
223  }
224  return BOOLEAN_VAL(1);
225 }
226 
227 /* Subset or equal to */
228 KRK_Method(set,__le__) {
229  METHOD_TAKES_EXACTLY(1);
230  if (!IS_set(argv[1]))
231  return NOTIMPL_VAL();
232  struct Set * them = AS_set(argv[1]);
233  KrkValue _unused;
234  for (unsigned int i = 0; i < self->entries.capacity; ++i) {
235  if (IS_KWARGS(self->entries.entries[i].key)) continue;
236  if (!krk_tableGet(&them->entries, self->entries.entries[i].key, &_unused)) return BOOLEAN_VAL(0);
237  }
238  return BOOLEAN_VAL(1);
239 }
240 
241 /* Strict superset */
242 KRK_Method(set,__gt__) {
243  METHOD_TAKES_EXACTLY(1);
244  if (!IS_set(argv[1]))
245  return NOTIMPL_VAL();
246  struct Set * them = AS_set(argv[1]);
247  if (self->entries.count == them->entries.count)
248  return BOOLEAN_VAL(0);
249  KrkValue _unused;
250  for (unsigned int i = 0; i < them->entries.capacity; ++i) {
251  if (IS_KWARGS(them->entries.entries[i].key)) continue;
252  if (!krk_tableGet(&self->entries, them->entries.entries[i].key, &_unused)) return BOOLEAN_VAL(0);
253  }
254  return BOOLEAN_VAL(1);
255 }
256 
257 KRK_Method(set,__ge__) {
258  METHOD_TAKES_EXACTLY(1);
259  if (!IS_set(argv[1]))
260  return NOTIMPL_VAL();
261  struct Set * them = AS_set(argv[1]);
262  KrkValue _unused;
263  for (unsigned int i = 0; i < them->entries.capacity; ++i) {
264  if (IS_KWARGS(them->entries.entries[i].key)) continue;
265  if (!krk_tableGet(&self->entries, them->entries.entries[i].key, &_unused)) return BOOLEAN_VAL(0);
266  }
267  return BOOLEAN_VAL(1);
268 }
269 
270 
271 KRK_Method(set,add) {
272  METHOD_TAKES_EXACTLY(1);
273  krk_tableSet(&self->entries, argv[1], BOOLEAN_VAL(1));
274  return NONE_VAL();
275 }
276 
277 KRK_Method(set,remove) {
278  METHOD_TAKES_EXACTLY(1);
279  if (!krk_tableDelete(&self->entries, argv[1]))
280  return krk_runtimeError(vm.exceptions->keyError, "key error");
281  return NONE_VAL();
282 }
283 
284 KRK_Method(set,discard) {
285  METHOD_TAKES_EXACTLY(1);
286  krk_tableDelete(&self->entries, argv[1]);
287  return NONE_VAL();
288 }
289 
290 KRK_Method(set,clear) {
291  METHOD_TAKES_NONE();
292  krk_freeTable(&self->entries);
293  krk_initTable(&self->entries);
294  return NONE_VAL();
295 }
296 
297 KRK_Method(set,update) {
298  METHOD_TAKES_AT_MOST(1);
299  if (argc > 1) {
300  if (IS_set(argv[1])) {
301  krk_tableAddAll(&AS_set(argv[1])->entries, &self->entries);
302  } else {
303  if (krk_unpackIterable(argv[1], self, _set_init_callback)) return NONE_VAL();
304  }
305  }
306  return NONE_VAL();
307 }
308 
309 FUNC_SIG(setiterator,__init__);
310 
311 KRK_Method(set,__iter__) {
312  METHOD_TAKES_NONE();
313  KrkInstance * output = krk_newInstance(KRK_BASE_CLASS(setiterator));
314  krk_push(OBJECT_VAL(output));
315  FUNC_NAME(setiterator,__init__)(2,(KrkValue[]){krk_peek(0), argv[0]}, 0);
316  return krk_pop();
317 }
318 
319 #undef CURRENT_CTYPE
320 #define CURRENT_CTYPE struct SetIterator *
321 
322 KRK_Method(setiterator,__init__) {
323  METHOD_TAKES_EXACTLY(1);
324  CHECK_ARG(1,set,void*,source);
325  self->set = argv[1];
326  self->i = 0;
327  return NONE_VAL();
328 }
329 
330 KRK_Method(setiterator,__call__) {
331  METHOD_TAKES_NONE();
332 
333  if (unlikely(!IS_set(self->set))) return argv[0];
334 
335  do {
336  if (self->i >= AS_set(self->set)->entries.capacity) return argv[0];
337  if (!IS_KWARGS(AS_set(self->set)->entries.entries[self->i].key)) {
338  krk_push(AS_set(self->set)->entries.entries[self->i].key);
339  self->i++;
340  return krk_pop();
341  }
342  self->i++;
343  } while (1);
344 }
345 
346 KrkValue krk_set_of(int argc, const KrkValue argv[], int hasKw) {
347  KrkValue outSet = OBJECT_VAL(krk_newInstance(KRK_BASE_CLASS(set)));
348  krk_push(outSet);
349  krk_initTable(&AS_set(outSet)->entries);
350 
351  while (argc) {
352  krk_tableSet(&AS_set(outSet)->entries, argv[argc-1], BOOLEAN_VAL(1));
353  argc--;
354  }
355 
356  return krk_pop();
357 }
358 
359 _noexport
360 void _createAndBind_setClass(void) {
361  KrkClass * set = krk_makeClass(vm.builtins, &KRK_BASE_CLASS(set), "set", vm.baseClasses->objectClass);
362  set->allocSize = sizeof(struct Set);
363  set->_ongcscan = _set_gcscan;
364  set->_ongcsweep = _set_gcsweep;
365  BIND_METHOD(set,__init__);
366  BIND_METHOD(set,__repr__);
367  BIND_METHOD(set,__len__);
368  BIND_METHOD(set,__eq__);
369  BIND_METHOD(set,__lt__);
370  BIND_METHOD(set,__gt__);
371  BIND_METHOD(set,__le__);
372  BIND_METHOD(set,__ge__);
373  BIND_METHOD(set,__and__);
374  BIND_METHOD(set,__or__);
375  BIND_METHOD(set,__xor__);
376  BIND_METHOD(set,__contains__);
377  BIND_METHOD(set,__iter__);
378  KRK_DOC(BIND_METHOD(set,add),
379  "@brief Add an element to the set.\n"
380  "@arguments value\n\n"
381  "Adds the given @p value to the set. @p value must be hashable.");
382  KRK_DOC(BIND_METHOD(set,remove),
383  "@brief Remove an element from the set.\n"
384  "@arguments value\n\n"
385  "Removes @p value from the set, raising @ref KeyError if it is not a member of the set.");
386  KRK_DOC(BIND_METHOD(set,discard),
387  "@brief Remove an element from the set, quietly.\n"
388  "@arguments value\n\n"
389  "Removes @p value from the set, without raising an exception if it is not a member.");
390  KRK_DOC(BIND_METHOD(set,clear),
391  "@brief Empty the set.\n\n"
392  "Removes all elements from the set, in-place.");
393  BIND_METHOD(set,update);
394  krk_defineNative(&set->methods, "__str__", FUNC_NAME(set,__repr__));
395  krk_attachNamedValue(&set->methods, "__hash__", NONE_VAL());
396  krk_finalizeClass(set);
397 
398  KrkClass * setiterator = krk_makeClass(vm.builtins, &KRK_BASE_CLASS(setiterator), "setiterator", vm.baseClasses->objectClass);
399  setiterator->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
400  setiterator->allocSize = sizeof(struct SetIterator);
401  setiterator->_ongcscan = _setiterator_gcscan;
402  BIND_METHOD(setiterator,__init__);
403  BIND_METHOD(setiterator,__call__);
404  krk_finalizeClass(setiterator);
405 }
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
void krk_markTable(KrkTable *table)
During a GC scan cycle, mark the contents of a table as used.
Definition: memory.c:438
Struct definitions for core object types.
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
KrkClass * krk_makeClass(KrkInstance *module, KrkClass **_class, const char *name, KrkClass *base)
Convenience function for creating new types.
Definition: vm.c:203
KrkTable methods
General attributes table.
Definition: object.h:192
void krk_finalizeClass(KrkClass *_class)
Finalize a class by collecting pointers to core methods.
KrkObj * _contains
__contains__ Called to resolve in (as a binary operator)
Definition: object.h:215
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
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:14
int krk_tableGet(KrkTable *table, KrkValue key, KrkValue *value)
Obtain the value associated with a key in a table.
Definition: table.c:178
int krk_tableDelete(KrkTable *table, KrkValue key)
Remove a key from a hash table.
Definition: table.c:208
int krk_tableSet(KrkTable *table, KrkValue key, KrkValue value)
Assign a value to a key in a table.
Definition: table.c:145
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
void krk_tableAddAll(KrkTable *from, KrkTable *to)
Add all key-value pairs from 'from' into 'to'.
Definition: table.c:169
void krk_freeTable(KrkTable *table)
Release resources associated with a hash table.
Definition: table.c:20
int flags
Definition: vm.h:174
Stack reference or primative value.
KrkClass * krk_getType(KrkValue value)
Get the class representing a value.
Definition: vm.c:275
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:346
Inline flexible string array.
Definition: util.h:150
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
Core API for the bytecode virtual machine.
#define vm
Convenience macro for namespacing.
Definition: vm.h:267
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