5 * (c) 2019 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the Python interface to mLib.
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.
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.
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,
28 /*----- Header files ------------------------------------------------------*/
30 #include "mLib-python.h"
32 /*----- Atoms -------------------------------------------------------------*/
39 static 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)))
56 static 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)
61 static PyObject *default_obarray(void)
63 PyObject *oa = 0, *rc = 0;
65 if (!home_module) SYSERR("home module not set");
66 oa = PyObject_GetAttrString(home_module, "DEFAULT_ATOMTABLE");
68 if (!OBARRAY_PYCHECK(oa)) TYERR("DEFAULT_ATOMTABLE isn't an AtomTable");
75 static PyObject *atom_pywrap(PyObject *oaobj, atom *a)
78 PyObject *oawk = 0, *rc = 0;
82 e = assoc_find(OBARRAY_MAP(oaobj), a, sizeof(*e), &f);
86 oawk = PyWeakref_NewRef(oaobj, 0); if (!oawk) goto end;
87 aobj = PyObject_NEW(atom_pyobj, atom_pytype);
89 aobj->oawk = oawk; oawk = 0;
90 e->aobj = (PyObject *)aobj;
91 rc = (PyObject *)aobj;
96 if (e && !rc) assoc_remove(OBARRAY_MAP(oaobj), e);
100 static PyObject *atom_pyintern(PyObject *oaobj, PyObject *x)
107 if (ATOM_PYCHECK(x)) {
108 if (ATOM_OAOBJ(x) != oaobj) VALERR("wrong table for existing atom");
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); }
116 TYERR("expected string or `None'");
117 rc = atom_pywrap(oaobj, a);
122 static PyObject *atom_pynew(PyTypeObject *cls, PyObject *arg, PyObject *kw)
124 static const char *const kwlist[] = { "name", "table", 0 };
125 PyObject *name = Py_None, *oaobj = 0, *rc = 0;
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);
138 static void atom_pydealloc(PyObject *me)
139 { assert(!ATOM_OAWK(me)); FREEOBJ(me); }
141 static int atom_check(PyObject *me)
143 if (!ATOM_A(me)) VALERR("atom is stale");
149 static PyObject *atomget_name(PyObject *me, void *hunoz)
153 if (atom_check(me)) return (0);
154 a = ATOM_A(me); return (TEXT_FROMSTRLEN(ATOM_NAME(a), ATOM_LEN(a)));
157 static PyObject *atomget_home(PyObject *me, void *hunoz)
161 if (atom_check(me)) return (0);
162 rc = ATOM_OAOBJ(me); assert(rc != Py_None); RETURN_OBJ(rc);
165 static PyObject *atomget_internedp(PyObject *me, void *hunoz)
167 if (atom_check(me)) return (0);
168 return (getbool(!(ATOM_A(me)->f&ATOMF_GENSYM)));
171 static PyObject *atomget_livep(PyObject *me, void *hunoz)
172 { return (getbool(!!ATOM_A(me))); }
174 static PyObject *atom_pyrichcompare(PyObject *x, PyObject *y, int op)
179 case Py_EQ: r = (x == y); break;
180 case Py_NE: r = (x != y); break;
181 default: TYERR("atoms are unordered");
188 static Py_hash_t atom_pyhash(PyObject *me)
189 { return (atom_check(me) ? -1 : ATOM_HASH(ATOM_A(me))); }
191 static 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?")
201 static 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@ */
207 atom_pydealloc, /* @tp_dealloc@ */
209 0, /* @tp_getattr@ */
210 0, /* @tp_setattr@ */
211 0, /* @tp_compare@ */
213 0, /* @tp_as_number@ */
214 0, /* @tp_as_sequence@ */
215 0, /* @tp_as_mapping@ */
216 atom_pyhash, /* @tp_hash@ */
219 0, /* @tp_getattro@ */
220 0, /* @tp_setattro@ */
221 0, /* @tp_as_buffer@ */
222 Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
226 "Atom([name = None], [table = DEFAULT_ATOMTABLE])",
228 0, /* @tp_traverse@ */
230 atom_pyrichcompare, /* @tp_richcompare@ */
231 0, /* @tp_weaklistoffset@ */
233 0, /* @tp_iternext@ */
234 0, /* @tp_methods@ */
235 0, /* @tp_members@ */
236 PYGETSET(atom), /* @tp_getset@ */
239 0, /* @tp_descr_get@ */
240 0, /* @tp_descr_set@ */
241 0, /* @tp_dictoffset@ */
243 PyType_GenericAlloc, /* @tp_alloc@ */
244 atom_pynew, /* @tp_new@ */
249 static void *obarray_gmlookup(PyObject *me, PyObject *key, unsigned *f)
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);
261 static void obarray_gmiterinit(PyObject *me, void *i)
262 { atom_mkiter(i, OBARRAY_TAB(me)); }
264 static void *obarray_gmiternext(PyObject *me, void *i)
265 { return (atom_next(i)); }
267 static PyObject *obarray_gmentrykey(PyObject *me, void *e)
268 { return (TEXT_FROMSTRLEN(ATOM_NAME(e), ATOM_LEN(e))); }
270 static PyObject *obarray_gmentryvalue(PyObject *me, void *e)
271 { return (atom_pywrap(me, e)); }
273 static const gmap_ops obarray_gmops = {
282 static PyObject *obarray_pynew(PyTypeObject *cls,
283 PyObject *arg, PyObject *kw)
285 obarray_pyobj *rc = 0;
286 static const char *const kwlist[] = { 0 };
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);
293 return ((PyObject *)rc);
296 static void obarray_pydealloc(PyObject *me)
301 ASSOC_MKITER(&it, OBARRAY_MAP(me));
303 ASSOC_NEXT(&it, e); if (!e) break;
305 Py_DECREF(ATOM_OAWK(e->aobj)); ATOM_OAWK(e->aobj) = 0;
308 assoc_destroy(OBARRAY_MAP(me));
309 atom_destroytable(OBARRAY_TAB(me));
313 static PyObject *oameth_intern(PyObject *me, PyObject *arg)
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);
327 static PyObject *oameth_gensym(PyObject *me)
328 { return (atom_pywrap(me, atom_gensym(OBARRAY_TAB(me)))); }
330 static const PyMappingMethods obarray_pymapping = {
336 static const PyMethodDef obarray_pymethods[] = {
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")
345 static 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@ */
351 obarray_pydealloc, /* @tp_dealloc@ */
353 0, /* @tp_getattr@ */
354 0, /* @tp_setattr@ */
355 0, /* @tp_compare@ */
357 0, /* @tp_as_number@ */
358 PYSEQUENCE(gmap), /* @tp_as_sequence@ */
359 PYMAPPING(obarray), /* @tp_as_mapping@ */
363 0, /* @tp_getattro@ */
364 0, /* @tp_setattro@ */
365 0, /* @tp_as_buffer@ */
366 Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
372 0, /* @tp_traverse@ */
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@ */
383 0, /* @tp_descr_get@ */
384 0, /* @tp_descr_set@ */
385 0, /* @tp_dictoffset@ */
387 PyType_GenericAlloc, /* @tp_alloc@ */
388 obarray_pynew, /* @tp_new@ */
393 /*----- Association tables ------------------------------------------------*/
400 static 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)
410 static void *assoc_gmlookup(PyObject *me, PyObject *key, unsigned *f)
412 struct aentry *e = 0;
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");
426 TYERR("expected atom or string");
427 e = assoc_find(ASSOC_T(me), a, f ? sizeof(*e) : 0, f);
429 if (f && !*f) e->obj = 0;
434 static void assoc_gmiterinit(PyObject *me, void *i)
435 { assoc_iter *it = i; ASSOC_MKITER(it, ASSOC_T(me)); }
437 static void *assoc_gmiternext(PyObject *me, void *i)
438 { assoc_iter *it = i; void *e; ASSOC_NEXT(it, e); return (e); }
440 static PyObject *assoc_gmentrykey(PyObject *me, void *e)
441 { return (atom_pywrap(ASSOC_OAOBJ(me), ASSOC_ATOM(e))); }
443 static PyObject *assoc_gmentryvalue(PyObject *me, void *e)
444 { struct aentry *ae = e; RETURN_OBJ(ae->obj); }
446 static int assoc_gmsetentry(PyObject *me, void *e, PyObject *val)
448 struct aentry *ae = e;
450 Py_XDECREF(ae->obj); Py_INCREF(val); ae->obj = val;
454 static int assoc_gmdelentry(PyObject *me, void *e)
455 { assoc_remove(ASSOC_T(me), e); return (0); }
457 static const gmap_ops assoc_gmops = {
468 static PyObject *assoc_pynew(PyTypeObject *cls, PyObject *arg, PyObject *kw)
470 PyObject *oaobj = 0, *map = Py_None;
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; }
486 return ((PyObject *)me);
489 static void assoc_pydealloc(PyObject *me)
494 ASSOC_MKITER(&it, ASSOC_T(me));
496 ASSOC_NEXT(&it, ae); if (!ae) break;
499 Py_DECREF(ASSOC_OAOBJ(me));
503 static Py_ssize_t assoc_pysize(PyObject *me)
505 assoc_table *t = ASSOC_T(me);
506 return (SYM_LIMIT(t->t.mask + 1) - t->load);
509 static const PyMemberDef assoc_pymembers[] = {
510 #define MEMBERSTRUCT assoc_pyobj
511 MEMRNM(table, T_OBJECT, oaobj, READONLY,
512 "AS.table -> ATAB: home atom table")
517 static const PyMappingMethods assoc_pymapping = {
523 static 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@ */
529 assoc_pydealloc, /* @tp_dealloc@ */
531 0, /* @tp_getattr@ */
532 0, /* @tp_setattr@ */
533 0, /* @tp_compare@ */
535 0, /* @tp_as_number@ */
536 PYSEQUENCE(gmap), /* @tp_as_sequence@ */
537 PYMAPPING(assoc), /* @tp_as_mapping@ */
541 0, /* @tp_getattro@ */
542 0, /* @tp_setattro@ */
543 0, /* @tp_as_buffer@ */
544 Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
548 "AssocTable([MAP], [ATAB], [ATOM = VALUE, ...])",
550 0, /* @tp_traverse@ */
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@ */
561 0, /* @tp_descr_get@ */
562 0, /* @tp_descr_set@ */
563 0, /* @tp_dictoffset@ */
565 PyType_GenericAlloc, /* @tp_alloc@ */
566 assoc_pynew, /* @tp_new@ */
571 /*----- Main code ---------------------------------------------------------*/
573 void atom_pyinit(void)
575 INITTYPE(atom, root);
576 INITTYPE(obarray, root);
577 INITTYPE(assoc, root);
580 void atom_pyinsert(PyObject *mod)
582 INSERT("Atom", atom_pytype);
583 INSERT("AtomTable", obarray_pytype);
584 INSERT("AssocTable", assoc_pytype);
587 /*----- That's all, folks -------------------------------------------------*/