chiark / gitweb /
@@@ constification
[mLib-python] / atom.c
CommitLineData
81f68b64
MW
1/* -*-c-*-
2 *
3 * Atoms and obarrays
4 *
5 * (c) 2019 Straylight/Edgeware
6 */
7
8/*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of the Python interface to mLib.
11 *
12 * mLib/Python is free software: you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version.
16 *
17 * mLib/Python is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with mLib/Python. If not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
25 * USA.
26 */
27
28/*----- Header files ------------------------------------------------------*/
29
30#include "mLib-python.h"
31
32/*----- Atoms -------------------------------------------------------------*/
33
34typedef struct {
35 PyObject_HEAD
36 atom *a;
37 PyObject *oawk;
38} atom_pyobj;
39static PyTypeObject *atom_pytype;
40#define ATOM_PYCHECK(o) PyObject_TypeCheck((o), atom_pytype)
41#define ATOM_A(o) (((atom_pyobj *)(o))->a)
42#define ATOM_OAWK(o) (((atom_pyobj *)(o))->oawk)
43#define ATOM_OAOBJ(o) (PyWeakref_GET_OBJECT(ATOM_OAWK(o)))
44
45struct obentry {
46 assoc_base _b;
47 PyObject *aobj;
48};
49
50typedef struct {
51 GMAP_PYOBJ_HEAD
52 atom_table tab;
53 assoc_table map;
54 PyObject *wkls;
55} obarray_pyobj;
56static PyTypeObject *obarray_pytype;
57#define OBARRAY_PYCHECK(o) PyObject_TypeCheck((o), obarray_pytype)
58#define OBARRAY_TAB(o) (&((obarray_pyobj *)(o))->tab)
59#define OBARRAY_MAP(o) (&((obarray_pyobj *)(o))->map)
60
61static PyObject *default_obarray(void)
62{
63 PyObject *oa = 0, *rc = 0;
64
65 if (!home_module) SYSERR("home module not set");
66 oa = PyObject_GetAttrString(home_module, "DEFAULT_ATOMTABLE");
67 if (!oa) goto end;
68 if (!OBARRAY_PYCHECK(oa)) TYERR("DEFAULT_ATOMTABLE isn't an AtomTable");
69 rc = oa; oa = 0;
70end:
71 Py_XDECREF(oa);
72 return (rc);
73}
74
75static PyObject *atom_pywrap(PyObject *oaobj, atom *a)
76{
77 struct obentry *e;
78 PyObject *oawk = 0, *rc = 0;
79 atom_pyobj *aobj;
80 unsigned f;
81
82 e = assoc_find(OBARRAY_MAP(oaobj), a, sizeof(*e), &f);
83 if (f)
84 rc = e->aobj;
85 else {
86 oawk = PyWeakref_NewRef(oaobj, 0); if (!oawk) goto end;
87 aobj = PyObject_NEW(atom_pyobj, atom_pytype);
88 aobj->a = a;
89 aobj->oawk = oawk; oawk = 0;
90 e->aobj = (PyObject *)aobj;
91 rc = (PyObject *)aobj;
92 }
93 Py_INCREF(rc);
94end:
95 Py_XDECREF(oawk);
96 if (e && !rc) assoc_remove(OBARRAY_MAP(oaobj), e);
97 return (rc);
98}
99
100static PyObject *atom_pyintern(PyObject *oaobj, PyObject *x)
101{
102 atom *a;
103 const char *p;
104 size_t sz;
105 PyObject *rc = 0;
106
107 if (ATOM_PYCHECK(x)) {
108 if (ATOM_OAOBJ(x) != oaobj) VALERR("wrong table for existing atom");
109 RETURN_OBJ(x);
110 }
111 if (x == Py_None)
112 a = atom_gensym(OBARRAY_TAB(oaobj));
113 else if (TEXT_CHECK(x))
114 { TEXT_PTRLEN(x, p, sz); a = atom_nintern(OBARRAY_TAB(oaobj), p, sz); }
115 else
116 TYERR("expected string or `None'");
117 rc = atom_pywrap(oaobj, a);
118end:
119 return (rc);
120}
121
122static PyObject *atom_pynew(PyTypeObject *cls, PyObject *arg, PyObject *kw)
123{
124 static const char *const kwlist[] = { "name", "table", 0 };
125 PyObject *name = Py_None, *oaobj = 0, *rc = 0;
126
127 if (!PyArg_ParseTupleAndKeywords(arg, kw, "|OO!:new", KWLIST,
128 &name, obarray_pytype, &oaobj))
129 { oaobj = 0; goto end; }
130 if (oaobj) Py_INCREF(oaobj);
131 else { oaobj = default_obarray(); if (!oaobj) goto end; }
132 rc = atom_pyintern(oaobj, name);
133end:
134 Py_XDECREF(oaobj);
135 return (rc);
136}
137
138static void atom_pydealloc(PyObject *me)
139 { assert(!ATOM_OAWK(me)); FREEOBJ(me); }
140
141static int atom_check(PyObject *me)
142{
143 if (!ATOM_A(me)) VALERR("atom is stale");
144 return (0);
145end:
146 return (-1);
147}
148
149static PyObject *atomget_name(PyObject *me, void *hunoz)
150{
151 atom *a;
152
153 if (atom_check(me)) return (0);
154 a = ATOM_A(me); return (TEXT_FROMSTRLEN(ATOM_NAME(a), ATOM_LEN(a)));
155}
156
157static PyObject *atomget_home(PyObject *me, void *hunoz)
158{
159 PyObject *rc;
160
161 if (atom_check(me)) return (0);
162 rc = ATOM_OAOBJ(me); assert(rc != Py_None); RETURN_OBJ(rc);
163}
164
165static PyObject *atomget_internedp(PyObject *me, void *hunoz)
166{
167 if (atom_check(me)) return (0);
168 return (getbool(!(ATOM_A(me)->f&ATOMF_GENSYM)));
169}
170
171static PyObject *atomget_livep(PyObject *me, void *hunoz)
172 { return (getbool(!!ATOM_A(me))); }
173
174static PyObject *atom_pyrichcompare(PyObject *x, PyObject *y, int op)
175{
176 int r;
177
178 switch (op) {
179 case Py_EQ: r = (x == y); break;
180 case Py_NE: r = (x != y); break;
181 default: TYERR("atoms are unordered");
182 }
183 return (getbool(r));
184end:
185 return (0);
186}
187
188static Py_hash_t atom_pyhash(PyObject *me)
189 { return (atom_check(me) ? -1 : ATOM_HASH(ATOM_A(me))); }
190
191static const PyGetSetDef atom_pygetset[] = {
192#define GETSETNAME(op, name) atom##op##_##name
193 GET (name, "A.name -> STR: atom name")
194 GET (home, "A.home -> ATAB: atom home table")
195 GET (internedp, "A.internedp -> BOOL: atom interned (not gensym)?")
196 GET (livep, "A.livep -> BOOL: atom table still alive?")
197#undef GETSETNAME
198 { 0 }
199};
200
201static const PyTypeObject atom_pytype_skel = {
202 PyVarObject_HEAD_INIT(0, 0) /* Header */
203 "Atom", /* @tp_name@ */
204 sizeof(atom_pyobj), /* @tp_basicsize@ */
205 0, /* @tp_itemsize@ */
206
207 atom_pydealloc, /* @tp_dealloc@ */
208 0, /* @tp_print@ */
209 0, /* @tp_getattr@ */
210 0, /* @tp_setattr@ */
211 0, /* @tp_compare@ */
212 0, /* @tp_repr@ */
213 0, /* @tp_as_number@ */
214 0, /* @tp_as_sequence@ */
215 0, /* @tp_as_mapping@ */
216 atom_pyhash, /* @tp_hash@ */
217 0, /* @tp_call@ */
218 0, /* @tp_str@ */
219 0, /* @tp_getattro@ */
220 0, /* @tp_setattro@ */
221 0, /* @tp_as_buffer@ */
222 Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
223 Py_TPFLAGS_BASETYPE,
224
225 /* @tp_doc@ */
226 "Atom([name = None], [table = DEFAULT_ATOMTABLE])",
227
228 0, /* @tp_traverse@ */
229 0, /* @tp_clear@ */
230 atom_pyrichcompare, /* @tp_richcompare@ */
231 0, /* @tp_weaklistoffset@ */
232 0, /* @tp_iter@ */
233 0, /* @tp_iternext@ */
234 0, /* @tp_methods@ */
235 0, /* @tp_members@ */
236 PYGETSET(atom), /* @tp_getset@ */
237 0, /* @tp_base@ */
238 0, /* @tp_dict@ */
239 0, /* @tp_descr_get@ */
240 0, /* @tp_descr_set@ */
241 0, /* @tp_dictoffset@ */
242 0, /* @tp_init@ */
243 PyType_GenericAlloc, /* @tp_alloc@ */
244 atom_pynew, /* @tp_new@ */
245 0, /* @tp_free@ */
246 0 /* @tp_is_gc@ */
247};
248
249static void *obarray_gmlookup(PyObject *me, PyObject *key, unsigned *f)
250{
251 atom *a = 0;
252 const char *p;
253 size_t sz;
254
255 if (!TEXT_CHECK(key)) TYERR("expected string");
256 TEXT_PTRLEN(key, p, sz); a = sym_find(&OBARRAY_TAB(me)->t, p, sz, 0, f);
257end:
258 return (a);
259}
260
261static void obarray_gmiterinit(PyObject *me, void *i)
262 { atom_mkiter(i, OBARRAY_TAB(me)); }
263
264static void *obarray_gmiternext(PyObject *me, void *i)
265 { return (atom_next(i)); }
266
267static PyObject *obarray_gmentrykey(PyObject *me, void *e)
268 { return (TEXT_FROMSTRLEN(ATOM_NAME(e), ATOM_LEN(e))); }
269
270static PyObject *obarray_gmentryvalue(PyObject *me, void *e)
271 { return (atom_pywrap(me, e)); }
272
273static const gmap_ops obarray_gmops = {
274 sizeof(atom_iter),
275 obarray_gmlookup,
276 obarray_gmiterinit,
277 obarray_gmiternext,
278 obarray_gmentrykey,
279 obarray_gmentryvalue
280};
281
282static PyObject *obarray_pynew(PyTypeObject *cls,
283 PyObject *arg, PyObject *kw)
284{
285 obarray_pyobj *rc = 0;
286 static const char *const kwlist[] = { 0 };
287
288 if (!PyArg_ParseTupleAndKeywords(arg, kw, ":new", KWLIST)) goto end;
289 rc = PyObject_NEW(obarray_pyobj, cls); if (!rc) goto end;
290 rc->gmops = &obarray_gmops; rc->wkls = 0;
291 atom_createtable(&rc->tab); assoc_create(&rc->map);
292end:
293 return ((PyObject *)rc);
294}
295
296static void obarray_pydealloc(PyObject *me)
297{
298 assoc_iter it;
299 struct obentry *e;
300
301 ASSOC_MKITER(&it, OBARRAY_MAP(me));
302 for (;;) {
303 ASSOC_NEXT(&it, e); if (!e) break;
304 ATOM_A(e->aobj) = 0;
305 Py_DECREF(ATOM_OAWK(e->aobj)); ATOM_OAWK(e->aobj) = 0;
306 Py_DECREF(e->aobj);
307 }
308 assoc_destroy(OBARRAY_MAP(me));
309 atom_destroytable(OBARRAY_TAB(me));
310 FREEOBJ(me);
311}
312
313static PyObject *oameth_intern(PyObject *me, PyObject *arg)
314{
315 char *p;
316 Py_ssize_t sz;
317 atom *a;
318 PyObject *rc = 0;
319
320 if (!PyArg_ParseTuple(arg, "s#:intern", &p, &sz)) goto end;
321 a = atom_nintern(OBARRAY_TAB(me), p, sz);
322 rc = atom_pywrap(me, a);
323end:
324 return (rc);
325}
326
327static PyObject *oameth_gensym(PyObject *me)
328 { return (atom_pywrap(me, atom_gensym(OBARRAY_TAB(me)))); }
329
330static const PyMappingMethods obarray_pymapping = {
331 gmap_pysize,
332 atom_pyintern,
333 0
334};
335
336static const PyMethodDef obarray_pymethods[] = {
337 GMAP_ROMETHODS
338#define METHNAME(name) oameth_##name
339 METH (intern, "ATAB.intern(STR) -> A: atom with given name")
340 NAMETH(gensym, "ATAB.gensym() -> A: fresh uninterned atom")
341#undef METHNAME
342 { 0 }
343};
344
345static const PyTypeObject obarray_pytype_skel = {
346 PyVarObject_HEAD_INIT(0, 0) /* Header */
347 "AtomTable", /* @tp_name@ */
348 sizeof(obarray_pyobj), /* @tp_basicsize@ */
349 0, /* @tp_itemsize@ */
350
351 obarray_pydealloc, /* @tp_dealloc@ */
352 0, /* @tp_print@ */
353 0, /* @tp_getattr@ */
354 0, /* @tp_setattr@ */
355 0, /* @tp_compare@ */
356 0, /* @tp_repr@ */
357 0, /* @tp_as_number@ */
358 PYSEQUENCE(gmap), /* @tp_as_sequence@ */
359 PYMAPPING(obarray), /* @tp_as_mapping@ */
360 0, /* @tp_hash@ */
361 0, /* @tp_call@ */
362 0, /* @tp_str@ */
363 0, /* @tp_getattro@ */
364 0, /* @tp_setattro@ */
365 0, /* @tp_as_buffer@ */
366 Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
367 Py_TPFLAGS_BASETYPE,
368
369 /* @tp_doc@ */
370 "AtomTable()",
371
372 0, /* @tp_traverse@ */
373 0, /* @tp_clear@ */
374 0, /* @tp_richcompare@ */
375 offsetof(obarray_pyobj, wkls), /* @tp_weaklistoffset@ */
376 gmap_pyiter, /* @tp_iter@ */
377 0, /* @tp_iternext@ */
378 PYMETHODS(obarray), /* @tp_methods@ */
379 0, /* @tp_members@ */
380 0, /* @tp_getset@ */
381 0, /* @tp_base@ */
382 0, /* @tp_dict@ */
383 0, /* @tp_descr_get@ */
384 0, /* @tp_descr_set@ */
385 0, /* @tp_dictoffset@ */
386 0, /* @tp_init@ */
387 PyType_GenericAlloc, /* @tp_alloc@ */
388 obarray_pynew, /* @tp_new@ */
389 0, /* @tp_free@ */
390 0 /* @tp_is_gc@ */
391};
392
393/*----- Association tables ------------------------------------------------*/
394
395typedef struct {
396 GMAP_PYOBJ_HEAD
397 assoc_table t;
398 PyObject *oaobj;
399} assoc_pyobj;
400static PyTypeObject *assoc_pytype;
401#define ASSOC_PYCHECK(o) PyObject_TypeCheck((o), assoc_pytype)
402#define ASSOC_T(o) (&((assoc_pyobj *)(o))->t)
403#define ASSOC_OAOBJ(o) (((assoc_pyobj *)(o))->oaobj)
404
405struct aentry {
406 assoc_base _b;
407 PyObject *obj;
408};
409
410static void *assoc_gmlookup(PyObject *me, PyObject *key, unsigned *f)
411{
412 struct aentry *e = 0;
50845e59 413 const char *p;
81f68b64
MW
414 size_t sz;
415 atom *a;
416
417 if (TEXT_CHECK(key)) {
418 TEXT_PTRLEN(key, p, sz);
419 a = atom_nintern(OBARRAY_TAB(ASSOC_OAOBJ(me)), p, sz);
420 } else if (ATOM_PYCHECK(key)) {
421 if (atom_check(key)) goto end;
422 if (ATOM_OAOBJ(key) != ASSOC_OAOBJ(me))
423 VALERR("wrong atom table for assoc");
424 a = ATOM_A(key);
425 } else
426 TYERR("expected atom or string");
427 e = assoc_find(ASSOC_T(me), a, f ? sizeof(*e) : 0, f);
428 if (!e) goto end;
429 if (f && !*f) e->obj = 0;
430end:
431 return (e);
432}
433
434static void assoc_gmiterinit(PyObject *me, void *i)
435 { assoc_iter *it = i; ASSOC_MKITER(it, ASSOC_T(me)); }
436
437static void *assoc_gmiternext(PyObject *me, void *i)
438 { assoc_iter *it = i; void *e; ASSOC_NEXT(it, e); return (e); }
439
440static PyObject *assoc_gmentrykey(PyObject *me, void *e)
441 { return (atom_pywrap(ASSOC_OAOBJ(me), ASSOC_ATOM(e))); }
442
443static PyObject *assoc_gmentryvalue(PyObject *me, void *e)
444 { struct aentry *ae = e; RETURN_OBJ(ae->obj); }
445
446static int assoc_gmsetentry(PyObject *me, void *e, PyObject *val)
447{
448 struct aentry *ae = e;
449
450 Py_XDECREF(ae->obj); Py_INCREF(val); ae->obj = val;
451 return (0);
452}
453
454static int assoc_gmdelentry(PyObject *me, void *e)
455 { assoc_remove(ASSOC_T(me), e); return (0); }
456
457static const gmap_ops assoc_gmops = {
458 sizeof(atom_iter),
459 assoc_gmlookup,
460 assoc_gmiterinit,
461 assoc_gmiternext,
462 assoc_gmentrykey,
463 assoc_gmentryvalue,
464 assoc_gmsetentry,
465 assoc_gmdelentry
466};
467
468static PyObject *assoc_pynew(PyTypeObject *cls, PyObject *arg, PyObject *kw)
469{
470 PyObject *oaobj = 0, *map = Py_None;
471 assoc_pyobj *me = 0;
472
473 if (!PyArg_ParseTuple(arg, "|OO!:new", &map, obarray_pytype, &oaobj))
474 { oaobj = 0; goto end; }
475 if (oaobj) Py_INCREF(oaobj);
476 else { oaobj = default_obarray(); if (!oaobj) goto end; }
477 me = PyObject_NEW(assoc_pyobj, cls);
478 me->gmops = &assoc_gmops;
479 assoc_create(&me->t);
480 me->oaobj = oaobj; oaobj = 0;
481 if ((map != Py_None && gmap_pyupdate((PyObject *)me, map)) ||
482 gmap_pyupdate((PyObject *)me, kw))
483 { Py_DECREF(me); me = 0; goto end; }
484end:
485 Py_XDECREF(oaobj);
486 return ((PyObject *)me);
487}
488
489static void assoc_pydealloc(PyObject *me)
490{
491 assoc_iter it;
492 struct aentry *ae;
493
494 ASSOC_MKITER(&it, ASSOC_T(me));
495 for (;;) {
496 ASSOC_NEXT(&it, ae); if (!ae) break;
497 Py_DECREF(ae->obj);
498 }
499 Py_DECREF(ASSOC_OAOBJ(me));
500 FREEOBJ(me);
501}
502
503static Py_ssize_t assoc_pysize(PyObject *me)
504{
505 assoc_table *t = ASSOC_T(me);
506 return (SYM_LIMIT(t->t.mask + 1) - t->load);
507}
508
509static const PyMemberDef assoc_pymembers[] = {
510#define MEMBERSTRUCT assoc_pyobj
511 MEMRNM(table, T_OBJECT, oaobj, READONLY,
512 "AS.table -> ATAB: home atom table")
513#undef MEMBERSTRUCT
514 { 0 }
515};
516
517static const PyMappingMethods assoc_pymapping = {
518 assoc_pysize,
519 gmap_pylookup,
520 gmap_pystore
521};
522
523static const PyTypeObject assoc_pytype_skel = {
524 PyVarObject_HEAD_INIT(0, 0) /* Header */
525 "AssocTable", /* @tp_name@ */
526 sizeof(assoc_pyobj), /* @tp_basicsize@ */
527 0, /* @tp_itemsize@ */
528
529 assoc_pydealloc, /* @tp_dealloc@ */
530 0, /* @tp_print@ */
531 0, /* @tp_getattr@ */
532 0, /* @tp_setattr@ */
533 0, /* @tp_compare@ */
534 0, /* @tp_repr@ */
535 0, /* @tp_as_number@ */
536 PYSEQUENCE(gmap), /* @tp_as_sequence@ */
537 PYMAPPING(assoc), /* @tp_as_mapping@ */
538 0, /* @tp_hash@ */
539 0, /* @tp_call@ */
540 0, /* @tp_str@ */
541 0, /* @tp_getattro@ */
542 0, /* @tp_setattro@ */
543 0, /* @tp_as_buffer@ */
544 Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
545 Py_TPFLAGS_BASETYPE,
546
547 /* @tp_doc@ */
548 "AssocTable([MAP], [ATAB], [ATOM = VALUE, ...])",
549
550 0, /* @tp_traverse@ */
551 0, /* @tp_clear@ */
552 0, /* @tp_richcompare@ */
553 0, /* @tp_weaklistoffset@ */
554 gmap_pyiter, /* @tp_iter@ */
555 0, /* @tp_iternext@ */
556 PYMETHODS(gmap), /* @tp_methods@ */
557 PYMEMBERS(assoc), /* @tp_members@ */
558 0, /* @tp_getset@ */
559 0, /* @tp_base@ */
560 0, /* @tp_dict@ */
561 0, /* @tp_descr_get@ */
562 0, /* @tp_descr_set@ */
563 0, /* @tp_dictoffset@ */
564 0, /* @tp_init@ */
565 PyType_GenericAlloc, /* @tp_alloc@ */
566 assoc_pynew, /* @tp_new@ */
567 0, /* @tp_free@ */
568 0 /* @tp_is_gc@ */
569};
570
571/*----- Main code ---------------------------------------------------------*/
572
573void atom_pyinit(void)
574{
575 INITTYPE(atom, root);
576 INITTYPE(obarray, root);
577 INITTYPE(assoc, root);
578}
579
580void atom_pyinsert(PyObject *mod)
581{
582 INSERT("Atom", atom_pytype);
583 INSERT("AtomTable", obarray_pytype);
584 INSERT("AssocTable", assoc_pytype);
585};
586
587/*----- That's all, folks -------------------------------------------------*/