Merge branch 'master' of https://repo.or.cz/arxana
[arxana.git] / elisp / honey-standardized.el
blobac458205078fa80b45305cfca9ed5775cc04e60a
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 ;; This is the first version in which we matched it to the spec we
21 ;; just wrote. To keep the emacs community happy, all our symbols
22 ;; have been prefaced with "arxana-". We have a plexus registry
23 ;; but, as yet, no dispatcher.
25 ;; See honey-demo.tex for background.
27 ;;; Code:
29 ;; === Instance Operations ===
31 (defvar arxana-plexus-registry '(0 nil))
33 (defun arxana-add-plexus ()
34 "Create a new plexus."
35 (let ((newbie (list '*plexus*
36 1 ; nema counter
37 (make-hash-table :test 'equal) ; nema table
38 (make-hash-table :test 'equal) ; forward links
39 (make-hash-table :test 'equal) ; backward links
40 (make-hash-table :test 'equal) ; forward labels
41 (make-hash-table :test 'equal) ; backward labels
42 (car arxana-plexus-registry))))
43 ;; Define ground and type nodes.
44 (puthash 0 '(0 0) (nth 2 newbie))
45 (puthash 1 '(0 0) (nth 2 newbie))
46 (puthash 0 '((0 . 0) (1 . 0)) (nth 3 newbie))
47 (puthash 0 '((0 . 0) (1 . 0)) (nth 4 newbie))
48 (puthash 0 '"ground" (nth 5 newbie))
49 (puthash '"ground" 0 (nth 6 newbie))
50 (puthash 1 '"type" (nth 5 newbie))
51 (puthash '"type" 1 (nth 6 newbie))
52 ;; Register the new object and return it.
53 (setq arxana-plexus-registry
54 (append
55 `(,(+ (car arxana-plexus-registry) 1)
56 ,newbie)
57 (cdr arxana-plexus-registry)))
58 newbie))
60 (defun arxana-remove-plexus (plex)
61 "Remove a plexus."
62 ;; Wipe out the hash tables
63 (dotimes (i 5)
64 (clrhash (nth (+ i 2) plex)))
65 ;; Remove the entry from the registry.
66 (setq arxana-plexus-registry
67 (cons
68 (car arxana-plexus-registry)
69 (delete
70 (assoc (nth 7 plex)
71 (cdr arxana-plexus-registry))
72 (cdr arxana-plexus-registry)))))
74 (defun arxana-reset-plexus ()
75 "Reset the database to its initial configuration."
76 ; Reset nema counter and hash tables.
77 (setcar (cdr arxana-current-plexus) 1)
78 (dotimes (n 5)
79 (clrhash (nth (+ n 2) arxana-current-plexus)))
80 ;; Define ground and nema-type.
81 (puthash 0 '(0 0) (nth 2 arxana-current-plexus))
82 (puthash 1 '(0 0) (nth 2 arxana-current-plexus))
83 (puthash 0 '((0 . 0) (1 . 0)) (nth 3 arxana-current-plexus))
84 (puthash 0 '((0 . 0) (1 . 0)) (nth 4 arxana-current-plexus))
85 (puthash 0 '"ground" (nth 5 arxana-current-plexus))
86 (puthash '"ground" 0 (nth 6 arxana-current-plexus))
87 (puthash 1 '"type" (nth 5 arxana-current-plexus))
88 (puthash '"type" 1 (nth 6 arxana-current-plexus))
89 nil)
91 ;; === Default Plexus ===
93 ;; Eventually, these operations will be taken over by the dispatcher
94 ;; but, for now, we will implement them here.
96 ;; (defvar root-level 0)
98 (defun arxana-show-plexus-registry ()
99 arxana-plexus-registry)
101 (defun arxana-set-current-plexus (plex)
102 "Examine a different plexus instead."
103 (setq arxana-current-plexus plex))
105 (defmacro arxana-with-current-plexus (plex &rest expr)
106 (append `(let ((arxana-current-plexus ,plex))) ,expr))
108 (defun arxana-show-current-plexus ()
109 "Return the plexus currently being examined."
110 arxana-current-plexus)
112 ;; Should not have this visible to user.
113 (defun arxana-next-unique-id ()
114 "Produce a yet unused unique identifier."
115 (setcar (cdr arxana-current-plexus)
116 (1+ (cadr arxana-current-plexus))))
118 ;; === Bulk operations ===
120 (defun arxana-download-en-masse ()
121 "Produce a representation of the database as quintuples."
122 (let ((plex nil))
123 (maphash (lambda (uid tplt)
124 ; Unpack triplet.
125 (let ((src (car tplt))
126 (snk (nth 1 tplt))
127 (txt (nthcdr 2 tplt)))
128 ; Obtain next label if exists.
129 (setq lbl (gethash uid
130 (nth 5 arxana-current-plexus)
131 nil))
132 ; Write data to list.
133 (setq plex (cons `(,uid ,lbl ,src ,snk . ,txt)
134 plex))))
135 (nth 2 arxana-current-plexus))
136 ; Return list of data.
137 (reverse plex)))
139 (defun arxana-upload-en-masse (plex)
140 "Load a representation of a database as quintuples into memory."
141 (dolist (qplt plex t)
142 ; unpack quintuplet
143 (let ((uid (car qplt))
144 (lbl (nth 1 qplt))
145 (src (nth 2 qplt))
146 (snk (nth 3 qplt))
147 (txt (nthcdr 4 qplt)))
148 ; plug into tables
149 (puthash uid
150 `(,src ,snk . ,txt)
151 (nth 2 arxana-current-plexus))
152 (puthash src
153 (cons `(,uid . ,snk)
154 (gethash src (nth 3 arxana-current-plexus) nil))
155 (nth 3 arxana-current-plexus))
156 (puthash snk
157 (cons
158 `(,uid . ,src)
159 (gethash snk (nth 4 arxana-current-plexus) nil))
160 (nth 4 arxana-current-plexus))
161 (when lbl
162 (progn
163 (puthash uid lbl (nth 5 arxana-current-plexus))
164 (puthash lbl uid (nth 6 arxana-current-plexus))))
165 ; Bump up nema counter if needed.
166 (when (> uid (cadr arxana-current-plexus))
167 (setcar (cdr arxana-current-plexus) uid)))))
169 (defun arxana-add-en-masse (plex)
170 "Add multiple nemata given as list of quartuplets."
171 (mapcar (lambda (qplt)
172 (let ((uid arxana-next-unique-id))
173 (put-nema (nth 1 plex)
174 (nth 2 plex)
175 (nthcar 2 plex))
176 (label-nema uid (car qplt))))
177 plex))
179 ;; === Individual operations ===
181 ;; Consider replacing the cons'es here and elewhere with
182 ;; add-to-list's so as to avoid duplication.
184 (defun arxana-add-nema (src txt snk)
185 "Enter a new nema to the database."
186 (let ((uid (arxana-next-unique-id)))
187 ;; Add record to nema table.
188 (puthash uid
189 `(,src ,snk . ,txt)
190 (nth 2 arxana-current-plexus))
191 ;; Add record to list of forward links of source.
192 (puthash src
193 (cons `(,uid . ,snk)
194 (gethash src (nth 3 arxana-current-plexus) nil))
195 (nth 3 arxana-current-plexus))
196 ;; Add record to list of backward links of sink.
197 (puthash snk
198 (cons
199 `(,uid . ,src)
200 (gethash snk (nth 4 arxana-current-plexus) nil))
201 (nth 4 arxana-current-plexus))
202 ;; Return the id of the new nema.
203 uid))
205 (defun arxana-get-content (uid)
206 "Return the content of the nema."
207 (cddr (gethash uid (nth 2 arxana-current-plexus))))
209 (defun arxana-get-source (uid)
210 "Return the source of the nema."
211 (car (gethash uid (nth 2 arxana-current-plexus))))
213 (defun arxana-get-sink (uid)
214 "Return the sink of the nema."
215 (cadr (gethash uid (nth 2 arxana-current-plexus))))
217 (defun arxana-get-forward-links (uid)
218 "Return all links having given object as source."
219 (mapcar 'car (gethash uid (nth 3 arxana-current-plexus))))
221 (defun arxana-get-backward-links (uid)
222 "Return all links having given object as sink."
223 (mapcar 'car (gethash uid (nth 4 arxana-current-plexus))))
225 (defun arxana-update-content (uid txt)
226 "Replace the content of the nema."
227 (puthash uid
228 (let ((x (gethash uid (nth 2 arxana-current-plexus))))
229 `(,(car x) ; old source
230 ,(cadr x) . ; old sink
231 ,txt)) ; new content
232 (nth 2 arxana-current-plexus)))
234 (defun arxana-update-source (uid new-src)
235 "Replace the source of the nema."
236 (let* ((x (gethash uid (nth 2 arxana-current-plexus)))
237 (old-src (car x)) ; extract current source
238 (old-snk (cadr x)) ; extract current sink
239 (old-txt (cddr x))) ; extract current content
240 ;; Update the entry in the nema table.
241 (puthash uid
242 `(,new-src ,old-snk . ,old-txt)
243 (nth 2 arxana-current-plexus))
244 ;; Remove the entry with the old source in the
245 ;; forward link table. If that is the only entry
246 ;; filed under old-src, remove it from table.
247 (let ((y (delete `(,uid . ,old-snk)
248 (gethash old-src
249 (nth 3 arxana-current-plexus)
250 nil))))
251 (if y
252 (puthash old-src y (nth 3 arxana-current-plexus))
253 (remhash old-src (nth 3 arxana-current-plexus))))
254 ;; Add an entry with the new source in the
255 ;; forward link table.
256 (puthash new-src
257 (cons `(,uid . ,old-snk)
258 (gethash old-src (nth 3 arxana-current-plexus) nil))
259 (nth 3 arxana-current-plexus))
260 ;; Update the entry in the backward link table.
261 (puthash old-snk
262 (cons `(,uid . ,new-src)
263 (delete `(,uid . ,old-src)
264 (gethash old-src
265 (nth 4 arxana-current-plexus)
266 nil)))
267 (nth 4 arxana-current-plexus))))
269 (defun arxana-update-sink (uid new-snk)
270 "Change the sink of the nema."
271 (let* ((x (gethash uid (nth 2 arxana-current-plexus)))
272 (old-src (car x)) ; extract current source
273 (old-snk (cadr x)) ; extract current sink
274 (old-txt (cddr x))) ; extract current content
275 ; Update the entry in the nema table.
276 (puthash uid
277 `(,old-src ,new-snk . ,old-txt)
278 (nth 2 arxana-current-plexus))
279 ;; Remove the entry with the old sink in the
280 ;; backward link table. If that is the only entry
281 ;; filed under old-src, remove it from table.
282 (let ((y (delete `(,uid . ,old-src)
283 (gethash old-snk
284 (nth 4 arxana-current-plexus)
285 nil))))
286 (if y
287 (puthash old-snk y (nth 4 arxana-current-plexus))
288 (remhash old-snk (nth 4 arxana-current-plexus))))
289 ;; Add an entry with the new source in the
290 ;; backward link table.
291 (puthash new-snk
292 (cons `(,uid . ,old-src)
293 (gethash old-snk
294 (nth 4 arxana-current-plexus)
295 nil))
296 (nth 4 arxana-current-plexus))
297 ;; Update the entry in the forward link table.
298 (puthash old-src
299 (cons `(,uid . ,new-snk)
300 (delete `(,uid . ,old-snk)
301 (gethash old-src
302 (nth 3 arxana-current-plexus)
303 nil)))
304 (nth 3 arxana-current-plexus))))
306 (defun arxana-remove-nema (uid)
307 "Remove this nema from the database."
308 (let ((old-src (car (gethash uid (nth 2 arxana-current-plexus))))
309 (old-snk (cadr (gethash uid (nth 2 arxana-current-plexus)))))
310 ;; Remove forward link created by nema.
311 (let ((new-fwd (delete `(,uid . ,old-snk)
312 (gethash old-src (nth 3 arxana-current-plexus)))))
313 (if new-fwd
314 (puthash old-src new-fwd (nth 3 arxana-current-plexus))
315 (remhash old-src (nth 3 arxana-current-plexus))))
316 ;; Remove backward link created by nema.
317 (let ((new-bkw (delete `(,uid . ,old-src)
318 (gethash old-snk (nth 4 arxana-current-plexus)))))
319 (if new-bkw
320 (puthash old-snk new-bkw (nth 4 arxana-current-plexus))
321 (remhash old-snk (nth 4 arxana-current-plexus))))
322 ;; Remove record from nema table.
323 (remhash uid (nth 2 arxana-current-plexus))))
325 ;; === Labelling nemata ===
327 ;; This will also move to the dispatcher in the future.
329 (defun arxana-label-nema (uid label)
330 "Assign the label to the given object."
331 (puthash uid label (nth 5 arxana-current-plexus))
332 (puthash label uid (nth 6 arxana-current-plexus)))
334 (defun arxana-label2uid (label)
335 "Return the unique identifier corresponding to a label."
336 (gethash label (nth 6 arxana-current-plexus) nil))
338 (defun arxana-uid2label (uid)
339 "Return the label associated to a unique identifier."
340 (gethash uid (nth 5 arxana-current-plexus) nil))
342 ;; === Queries ===
344 (defun arxana-uid-p (uid)
345 "Is this a valid uid?"
346 (let ((z '(())))
347 (not (eq z (gethash uid (nth 2 arxana-current-plexus) z)))))
349 (defun arxana-uid-list ()
350 "List of all valid uid's."
351 (let ((ans nil))
352 (maphash (lambda (key val)
353 (push key ans))
354 (nth 2 arxana-current-plexus))
355 ans))
357 (defun arxana-ground-p (uid)
358 "Is this nema the ground?"
359 (= uid 0))
361 (defun arxana-source-p (x y)
362 "Is the former nema the sink of the latter?"
363 (equal x (arxana-get-source y)))
365 (defun arxana-sink-p (x y)
366 "Is the former nema the sink of the latter?"
367 (equal x (arxana-get-sink y)))
369 (defun arxana-links-from (x y)
370 "Return all links from nema x to nema y."
371 (filter '(lambda (z) (source-p x z))
372 (arxana-get-backward-links y)))
374 (defun arxana-links-p (x y)
375 "Does nema x link to nema y?"
376 (when (member x (mapcar
377 'arxana-get-source
378 (arxana-get-backward-links y)))
381 (defun arxana-triple-p (x y z)
382 "Do the three items form a triplet?"
383 (and (arxana-source-p y x)
384 (arxana-sink-p y z)))
386 (defun arxana-plexus-p (x)
387 "Is this object a plexus?"
388 (let ((ans t))
389 (setq ans (and ans
390 (equal (car x) "*plexus*")))
391 (setq ans (and ans
392 (integrp (cadr x))))
393 (dotimes (n 5)
394 (setq ans (and ans (hash-table-p
395 (nth (+ n 2) x)))))
396 ans))
398 ;; Iteration
400 (defmacro arxana-do-plexus (var body res)
401 `((maphash (lambda (,var val) ,body)
402 (nth 2 arxana-current-plexus))
403 ,res))
405 (defun arxana-map-plexus (func)
406 (let ((ans nil))
407 (maphash
408 (lambda (key val)
409 (push (funcall func key) ans))
410 (nth 2 arxana-current-plexus))
411 ans))
413 (defun arxana-filter-plexus (pred)
414 (let ((ans nil))
415 (maphash
416 (lambda (key val)
417 (when (funcall pred key)
418 (push key ans)))
419 (nth 2 arxana-current-plexus))
420 ans))