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/>.
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.
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
*
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
55 `(,(+ (car arxana-plexus-registry
) 1)
57 (cdr arxana-plexus-registry
)))
60 (defun arxana-remove-plexus (plex)
62 ;; Wipe out the hash tables
64 (clrhash (nth (+ i
2) plex
)))
65 ;; Remove the entry from the registry.
66 (setq arxana-plexus-registry
68 (car arxana-plexus-registry
)
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)
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
))
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."
123 (maphash (lambda (uid tplt
)
125 (let ((src (car tplt
))
127 (txt (nthcdr 2 tplt
)))
128 ; Obtain next label if exists.
129 (setq lbl
(gethash uid
130 (nth 5 arxana-current-plexus
)
132 ; Write data to list.
133 (setq plex
(cons `(,uid
,lbl
,src
,snk .
,txt
)
135 (nth 2 arxana-current-plexus
))
136 ; Return list of data.
139 (defun arxana-upload-en-masse (plex)
140 "Load a representation of a database as quintuples into memory."
141 (dolist (qplt plex t
)
143 (let ((uid (car qplt
))
147 (txt (nthcdr 4 qplt
)))
151 (nth 2 arxana-current-plexus
))
154 (gethash src
(nth 3 arxana-current-plexus
) nil
))
155 (nth 3 arxana-current-plexus
))
159 (gethash snk
(nth 4 arxana-current-plexus
) nil
))
160 (nth 4 arxana-current-plexus
))
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
)
176 (label-nema uid
(car qplt
))))
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.
190 (nth 2 arxana-current-plexus
))
191 ;; Add record to list of forward links of source.
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.
200 (gethash snk
(nth 4 arxana-current-plexus
) nil
))
201 (nth 4 arxana-current-plexus
))
202 ;; Return the id of the new nema.
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."
228 (let ((x (gethash uid
(nth 2 arxana-current-plexus
))))
229 `(,(car x
) ; old source
230 ,(cadr x
) .
; old sink
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.
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
)
249 (nth 3 arxana-current-plexus
)
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.
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.
262 (cons `(,uid .
,new-src
)
263 (delete `(,uid .
,old-src
)
265 (nth 4 arxana-current-plexus
)
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.
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
)
284 (nth 4 arxana-current-plexus
)
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.
292 (cons `(,uid .
,old-src
)
294 (nth 4 arxana-current-plexus
)
296 (nth 4 arxana-current-plexus
))
297 ;; Update the entry in the forward link table.
299 (cons `(,uid .
,new-snk
)
300 (delete `(,uid .
,old-snk
)
302 (nth 3 arxana-current-plexus
)
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
)))))
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
)))))
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
))
344 (defun arxana-uid-p (uid)
345 "Is this a valid uid?"
347 (not (eq z
(gethash uid
(nth 2 arxana-current-plexus
) z
)))))
349 (defun arxana-uid-list ()
350 "List of all valid uid's."
352 (maphash (lambda (key val
)
354 (nth 2 arxana-current-plexus
))
357 (defun arxana-ground-p (uid)
358 "Is this nema the ground?"
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
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?"
390 (equal (car x
) "*plexus*")))
394 (setq ans
(and ans
(hash-table-p
400 (defmacro arxana-do-plexus
(var body res
)
401 `((maphash (lambda (,var val
) ,body
)
402 (nth 2 arxana-current-plexus
))
405 (defun arxana-map-plexus (func)
409 (push (funcall func key
) ans
))
410 (nth 2 arxana-current-plexus
))
413 (defun arxana-filter-plexus (pred)
417 (when (funcall pred key
)
419 (nth 2 arxana-current-plexus
))