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 -------------------------------------------------*/