Merge branch 'mob' of git://repo.or.cz/arxana into mob
[arxana.git] / elisp / honey-redux.el
blob648ff476685e333e78639580f5bd725e60f43dff
1 ;; honey.el - Higher Order NEsted Yarnknotter --- back-end for Arxana
3 ;; Copyright (C) 2010, 2011, 2012, 2013 Raymond S. Puzio
5 ;; This program is free software: you can redistribute it and/or modify
6 ;; it under the terms of the GNU Affero General Public License as published by
7 ;; the Free Software Foundation, either version 3 of the License, or
8 ;; (at your option) any later version.
10 ;; This program is distributed in the hope that it will be useful,
11 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ;; GNU Affero General Public License for more details.
15 ;; You should have received a copy of the GNU Affero General Public License
16 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
18 ;;; Commentary:
20 ;; See honey-demo.tex for background.
22 ;;; Code:
24 (defvar plexus-registry '(0 nil))
26 (defun add-plexus ()
27 "Create a new plexus."
28 (let ((newbie (list '*plexus*
29 1 ; nema counter
30 (make-hash-table :test 'equal) ; nema table
31 (make-hash-table :test 'equal) ; forward links
32 (make-hash-table :test 'equal) ; backward links
33 (make-hash-table :test 'equal) ; forward labels
34 (make-hash-table :test 'equal) ; backward labels
35 (car plexus-registry))))
36 ;; Define ground and type nodes.
37 (puthash 0 '(0 0) (nth 2 newbie))
38 (puthash 1 '(0 0) (nth 2 newbie))
39 (puthash 0 '((0 . 0) (1 . 0)) (nth 3 newbie))
40 (puthash 0 '((0 . 0) (1 . 0)) (nth 4 newbie))
41 (puthash 0 '"ground" (nth 5 newbie))
42 (puthash '"ground" 0 (nth 6 newbie))
43 (puthash 1 '"type" (nth 5 newbie))
44 (puthash '"type" 1 (nth 6 newbie))
45 ;; Register the new object and return it.
46 (setq plexus-registry
47 (append
48 `(,(+ (car plexus-registry) 1)
49 ,newbie)
50 (cdr plexus-registry)))
51 newbie))
53 (defun remove-plexus (plex)
54 "Remove a plexus."
55 ;; Wipe out the hash tables
56 (dotimes (i 5)
57 (clrhash (nth (+ i 2) plex)))
58 ;; Remove the entry from the registry.
59 (setq plexus-registry
60 (cons
61 (car plexus-registry)
62 (delete
63 (assoc (nth 7 plex)
64 (cdr plexus-registry))
65 (cdr plexus-registry)))))
67 (defun show-plexus-registry ()
68 plexus-registry)
70 ;; (defvar root-level 0)
72 (defun set-current-plexus (plex)
73 "Examine a different plexus instead."
74 (setq current-plexus plex))
76 (defmacro with-current-plexus (plex &rest expr)
77 (append `(let ((current-plexus ,plex))) ,expr))
79 (defun show-current-plexus ()
80 "Return the plexus currently being examined."
81 current-plexus)
83 (defun reset-plexus ()
84 "Reset the database to its initial configuration."
85 ; Reset nema counter and hash tables.
86 (setcar (cdr current-plexus) 1)
87 (dotimes (n 5)
88 (clrhash (nth (+ n 2) current-plexus)))
89 ;; Define ground and nema-type.
90 (puthash 0 '(0 0) (nth 2 current-plexus))
91 (puthash 1 '(0 0) (nth 2 current-plexus))
92 (puthash 0 '((0 . 0) (1 . 0)) (nth 3 current-plexus))
93 (puthash 0 '((0 . 0) (1 . 0)) (nth 4 current-plexus))
94 (puthash 0 '"ground" (nth 5 current-plexus))
95 (puthash '"ground" 0 (nth 6 current-plexus))
96 (puthash 1 '"nema-type" (nth 5 current-plexus))
97 (puthash '"nema-type" 1 (nth 6 current-plexus))
98 nil)
100 ;; Should not have this visible to user.
101 (defun next-unique-id ()
102 "Produce a yet unused unique identifier."
103 (setcar (cdr current-plexus)
104 (1+ (cadr current-plexus))))
106 ;; Bulk operations.
108 (defun download-en-masse ()
109 "Produce a representation of the database as quintuples."
110 (let ((plex nil))
111 (maphash (lambda (uid tplt)
112 ; Unpack triplet.
113 (let ((src (car tplt))
114 (snk (nth 1 tplt))
115 (txt (nthcdr 2 tplt)))
116 ; Obtain next label if exists.
117 (setq lbl (gethash uid
118 (nth 5 current-plexus)
119 nil))
120 ; Write data to list.
121 (setq plex (cons `(,uid ,lbl ,src ,snk . ,txt)
122 plex))))
123 (nth 2 current-plexus))
124 ; Return list of data.
125 (reverse plex)))
127 (defun upload-en-masse (plex)
128 "Load a representation of a database as quintuples into memory."
129 (dolist (qplt plex t)
130 ; unpack quintuplet
131 (let ((uid (car qplt))
132 (lbl (nth 1 qplt))
133 (src (nth 2 qplt))
134 (snk (nth 3 qplt))
135 (txt (nthcdr 4 qplt)))
136 ; plug into tables
137 (puthash uid
138 `(,src ,snk . ,txt)
139 (nth 2 current-plexus))
140 (puthash src
141 (cons `(,uid . ,snk)
142 (gethash src (nth 3 current-plexus) nil))
143 (nth 3 current-plexus))
144 (puthash snk
145 (cons
146 `(,uid . ,src)
147 (gethash snk (nth 4 current-plexus) nil))
148 (nth 4 current-plexus))
149 (when lbl
150 (progn
151 (puthash uid lbl (nth 5 current-plexus))
152 (puthash lbl uid (nth 6 current-plexus))))
153 ; Bump up nema counter if needed.
154 (when (> uid (cadr current-plexus))
155 (setcar (cdr current-plexus) uid)))))
157 (defun add-en-masse (plex)
158 "Add multiple nemata given as list of quartuplets."
159 (mapcar (lambda (qplt)
160 (let ((uid next-unique-id))
161 (put-nema (nth 1 plex)
162 (nth 2 plex)
163 (nthcar 2 plex))
164 (label-nema uid (car qplt))))
165 plex))
167 ;; Individual operations.
169 ;; Consider replacing the cons'es here and elewhere with
170 ;; add-to-list's so as to avoid duplication.
172 (defun add-nema (src snk txt)
173 "Enter a new nema to the database."
174 (let ((uid (next-unique-id)))
175 ;; Add record to nema table.
176 (puthash uid
177 `(,src ,snk . ,txt)
178 (nth 2 current-plexus))
179 ;; Add record to list of forward links of source.
180 (puthash src
181 (cons `(,uid . ,snk)
182 (gethash src (nth 3 current-plexus) nil))
183 (nth 3 current-plexus))
184 ;; Add record to list of backward links of sink.
185 (puthash snk
186 (cons
187 `(,uid . ,src)
188 (gethash snk (nth 4 current-plexus) nil))
189 (nth 4 current-plexus))
190 ;; Return the id of the new nema.
191 uid))
193 (defun get-content (uid)
194 "Return the content of the nema."
195 (cddr (gethash uid (nth 2 current-plexus))))
197 (defun get-source (uid)
198 "Return the source of the nema."
199 (car (gethash uid (nth 2 current-plexus))))
201 (defun get-sink (uid)
202 "Return the sink of the nema."
203 (cadr (gethash uid (nth 2 current-plexus))))
205 (defun get-forward-links (uid)
206 "Return all links having given object as source."
207 (mapcar 'car (gethash uid (nth 3 current-plexus))))
209 (defun get-backward-links (uid)
210 "Return all links having given object as sink."
211 (mapcar 'car (gethash uid (nth 4 current-plexus))))
213 (defun update-content (uid txt)
214 "Replace the content of the nema."
215 (puthash uid
216 (let ((x (gethash uid (nth 2 current-plexus))))
217 `(,(car x) ; old source
218 ,(cadr x) . ; old sink
219 ,txt)) ; new content
220 (nth 2 current-plexus)))
222 (defun update-source (uid new-src)
223 "Replace the source of the nema."
224 (let* ((x (gethash uid (nth 2 current-plexus)))
225 (old-src (car x)) ; extract current source
226 (old-snk (cadr x)) ; extract current sink
227 (old-txt (cddr x))) ; extract current content
228 ;; Update the entry in the nema table.
229 (puthash uid
230 `(,new-src ,old-snk . ,old-txt)
231 (nth 2 current-plexus))
232 ;; Remove the entry with the old source in the
233 ;; forward link table. If that is the only entry
234 ;; filed under old-src, remove it from table.
235 (let ((y (delete `(,uid . ,old-snk)
236 (gethash old-src
237 (nth 3 current-plexus)
238 nil))))
239 (if y
240 (puthash old-src y (nth 3 current-plexus))
241 (remhash old-src (nth 3 current-plexus))))
242 ;; Add an entry with the new source in the
243 ;; forward link table.
244 (puthash new-src
245 (cons `(,uid . ,old-snk)
246 (gethash old-src (nth 3 current-plexus) nil))
247 (nth 3 current-plexus))
248 ;; Update the entry in the backward link table.
249 (puthash old-snk
250 (cons `(,uid . ,new-src)
251 (delete `(,uid . ,old-src)
252 (gethash old-src
253 (nth 4 current-plexus)
254 nil)))
255 (nth 4 current-plexus))))
257 (defun update-sink (uid new-snk)
258 "Change the sink of the nema."
259 (let* ((x (gethash uid (nth 2 current-plexus)))
260 (old-src (car x)) ; extract current source
261 (old-snk (cadr x)) ; extract current sink
262 (old-txt (cddr x))) ; extract current content
263 ; Update the entry in the nema table.
264 (puthash uid
265 `(,old-src ,new-snk . ,old-txt)
266 (nth 2 current-plexus))
267 ;; Remove the entry with the old sink in the
268 ;; backward link table. If that is the only entry
269 ;; filed under old-src, remove it from table.
270 (let ((y (delete `(,uid . ,old-src)
271 (gethash old-snk
272 (nth 4 current-plexus)
273 nil))))
274 (if y
275 (puthash old-snk y (nth 4 current-plexus))
276 (remhash old-snk (nth 4 current-plexus))))
277 ;; Add an entry with the new source in the
278 ;; backward link table.
279 (puthash new-snk
280 (cons `(,uid . ,old-src)
281 (gethash old-snk
282 (nth 4 current-plexus)
283 nil))
284 (nth 4 current-plexus))
285 ;; Update the entry in the forward link table.
286 (puthash old-src
287 (cons `(,uid . ,new-snk)
288 (delete `(,uid . ,old-snk)
289 (gethash old-src
290 (nth 3 current-plexus)
291 nil)))
292 (nth 3 current-plexus))))
294 (defun remove-nema (uid)
295 "Remove this nema from the database."
296 (let ((old-src (car (gethash uid (nth 2 current-plexus))))
297 (old-snk (cadr (gethash uid (nth 2 current-plexus)))))
298 ;; Remove forward link created by nema.
299 (let ((new-fwd (delete `(,uid . ,old-snk)
300 (gethash old-src (nth 3 current-plexus)))))
301 (if new-fwd
302 (puthash old-src new-fwd (nth 3 current-plexus))
303 (remhash old-src (nth 3 current-plexus))))
304 ;; Remove backward link created by nema.
305 (let ((new-bkw (delete `(,uid . ,old-src)
306 (gethash old-snk (nth 4 current-plexus)))))
307 (if new-bkw
308 (puthash old-snk new-bkw (nth 4 current-plexus))
309 (remhash old-snk (nth 4 current-plexus))))
310 ;; Remove record from nema table.
311 (remhash uid (nth 2 current-plexus))))
313 ;; Labelling nemata.
315 (defun label-nema (uid label)
316 "Assign the label to the given object."
317 (puthash uid label (nth 5 current-plexus))
318 (puthash label uid (nth 6 current-plexus)))
320 (defun label2uid (label)
321 "Return the unique identifier corresponding to a label."
322 (gethash label (nth 6 current-plexus) nil))
324 (defun uid2label (uid)
325 "Return the label associated to a unique identifier."
326 (gethash uid (nth 5 current-plexus) nil))
328 ;; Queries
330 (defun uid-p (uid)
331 "Is this a valid uid?"
332 (let ((z '(())))
333 (not (eq z (gethash uid (nth 2 current-plexus) z)))))
335 (defun uid-list ()
336 "List of all valid uid's."
337 (let ((ans nil))
338 (maphash (lambda (key val)
339 (push key ans))
340 (nth 2 current-plexus))
341 ans))
343 (defun ground-p (uid)
344 "Is this nema the ground?"
345 (= uid 0))
347 (defun source-p (x y)
348 "Is the former nema the sink of the latter?"
349 (equal x (get-source y)))
351 (defun sink-p (x y)
352 "Is the former nema the sink of the latter?"
353 (equal x (get-sink y)))
355 (defun links-from (x y)
356 "Return all links from nema x to nema y."
357 (filter '(lambda (z) (source-p x z))
358 (get-backward-links y)))
360 (defun links-p (x y)
361 "Does nema x link to nema y?"
362 (when (member x (mapcar
363 'get-source
364 (get-backward-links y)))
367 (defun triple-p (x y z)
368 "Do the three items form a triplet?"
369 (and (source-p y x)
370 (sink-p y z)))
372 (defun plexus-p (x)
373 "Is this object a plexus?"
374 (let ((ans t))
375 (setq ans (and ans
376 (equal (car x) "*plexus*")))
377 (setq ans (and ans
378 (integrp (cadr x))))
379 (dotimes (n 5)
380 (setq ans (and ans (hash-table-p
381 (nth (+ n 2) x)))))
382 ans))
384 ;; Iteration
386 (defmacro do-plexus (var body res)
387 `((maphash (lambda (,var val) ,body)
388 (nth 2 current-plexus))
389 ,res))
391 (defun map-plexus (func)
392 (let ((ans nil))
393 (maphash
394 (lambda (key val)
395 (push (funcall func key) ans))
396 (nth 2 current-plexus))
397 ans))
399 (defun filter-plexus (pred)
400 (let ((ans nil))
401 (maphash
402 (lambda (key val)
403 (when (funcall pred key)
404 (push key ans)))
405 (nth 2 current-plexus))
406 ans))