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 ;; See honey-demo.tex for background.
24 (defvar plexus-registry
'(0 nil
))
27 "Create a new plexus."
28 (let ((newbie (list '*plexus
*
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.
48 `(,(+ (car plexus-registry
) 1)
50 (cdr plexus-registry
)))
53 (defun remove-plexus (plex)
55 ;; Wipe out the hash tables
57 (clrhash (nth (+ i
2) plex
)))
58 ;; Remove the entry from the registry.
64 (cdr plexus-registry
))
65 (cdr plexus-registry
)))))
67 (defun show-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."
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)
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
))
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
))))
108 (defun download-en-masse ()
109 "Produce a representation of the database as quintuples."
111 (maphash (lambda (uid tplt
)
113 (let ((src (car tplt
))
115 (txt (nthcdr 2 tplt
)))
116 ; Obtain next label if exists.
117 (setq lbl
(gethash uid
118 (nth 5 current-plexus
)
120 ; Write data to list.
121 (setq plex
(cons `(,uid
,lbl
,src
,snk .
,txt
)
123 (nth 2 current-plexus
))
124 ; Return list of data.
127 (defun upload-en-masse (plex)
128 "Load a representation of a database as quintuples into memory."
129 (dolist (qplt plex t
)
131 (let ((uid (car qplt
))
135 (txt (nthcdr 4 qplt
)))
139 (nth 2 current-plexus
))
142 (gethash src
(nth 3 current-plexus
) nil
))
143 (nth 3 current-plexus
))
147 (gethash snk
(nth 4 current-plexus
) nil
))
148 (nth 4 current-plexus
))
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
)
164 (label-nema uid
(car qplt
))))
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.
178 (nth 2 current-plexus
))
179 ;; Add record to list of forward links of source.
182 (gethash src
(nth 3 current-plexus
) nil
))
183 (nth 3 current-plexus
))
184 ;; Add record to list of backward links of sink.
188 (gethash snk
(nth 4 current-plexus
) nil
))
189 (nth 4 current-plexus
))
190 ;; Return the id of the new nema.
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."
216 (let ((x (gethash uid
(nth 2 current-plexus
))))
217 `(,(car x
) ; old source
218 ,(cadr x
) .
; old sink
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.
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
)
237 (nth 3 current-plexus
)
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.
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.
250 (cons `(,uid .
,new-src
)
251 (delete `(,uid .
,old-src
)
253 (nth 4 current-plexus
)
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.
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
)
272 (nth 4 current-plexus
)
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.
280 (cons `(,uid .
,old-src
)
282 (nth 4 current-plexus
)
284 (nth 4 current-plexus
))
285 ;; Update the entry in the forward link table.
287 (cons `(,uid .
,new-snk
)
288 (delete `(,uid .
,old-snk
)
290 (nth 3 current-plexus
)
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
)))))
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
)))))
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
))))
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
))
331 "Is this a valid uid?"
333 (not (eq z
(gethash uid
(nth 2 current-plexus
) z
)))))
336 "List of all valid uid's."
338 (maphash (lambda (key val
)
340 (nth 2 current-plexus
))
343 (defun ground-p (uid)
344 "Is this nema the ground?"
347 (defun source-p (x y
)
348 "Is the former nema the sink of the latter?"
349 (equal x
(get-source 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
)))
361 "Does nema x link to nema y?"
362 (when (member x
(mapcar
364 (get-backward-links y
)))
367 (defun triple-p (x y z
)
368 "Do the three items form a triplet?"
373 "Is this object a plexus?"
376 (equal (car x
) "*plexus*")))
380 (setq ans
(and ans
(hash-table-p
386 (defmacro do-plexus
(var body res
)
387 `((maphash (lambda (,var val
) ,body
)
388 (nth 2 current-plexus
))
391 (defun map-plexus (func)
395 (push (funcall func key
) ans
))
396 (nth 2 current-plexus
))
399 (defun filter-plexus (pred)
403 (when (funcall pred key
)
405 (nth 2 current-plexus
))