1 | (defpackage :clim-lookup (:use :common-lisp :split-sequence) |
---|
2 | (:export :term-lookup :populate-table)) |
---|
3 | (in-package :clim-lookup) |
---|
4 | |
---|
5 | (defvar *clim-table*) |
---|
6 | |
---|
7 | (defvar *clim-abbrev-table*) |
---|
8 | |
---|
9 | (defparameter *clim-file* |
---|
10 | (merge-pathnames "mrindex" |
---|
11 | (make-pathname |
---|
12 | :directory |
---|
13 | (pathname-directory |
---|
14 | (or *load-truename* |
---|
15 | *default-pathname-defaults*))))) |
---|
16 | |
---|
17 | (defun merge-to-spec (url) |
---|
18 | (concatenate 'string "http://www.stud.uni-karlsruhe.de/~unk6/clim-spec/" |
---|
19 | url)) |
---|
20 | |
---|
21 | (defun set-abbrev (term) |
---|
22 | (let ((abbrev (abbrev:abbrev term))) |
---|
23 | (if abbrev |
---|
24 | (pushnew term (gethash abbrev *clim-abbrev-table* nil) |
---|
25 | :test #'string-equal)))) |
---|
26 | |
---|
27 | (defun populate-table () |
---|
28 | (setf *clim-table* (make-hash-table :test #'equalp)) |
---|
29 | (setf *clim-abbrev-table* (make-hash-table :test #'equalp)) |
---|
30 | (with-open-file (f *clim-file* :direction :input) |
---|
31 | (loop for i = (read f nil nil) |
---|
32 | while i |
---|
33 | do (destructuring-bind (ig1 (term sep (ig2 type)) url) |
---|
34 | i |
---|
35 | (declare (ignore ig1 ig2 sep)) |
---|
36 | (setf term (substitute #\space (code-char 160) term :test #'eql)) |
---|
37 | (setf type (substitute #\space (code-char 160) type :test #'eql)) |
---|
38 | (push (cons type url) |
---|
39 | (gethash term *clim-table* nil)) |
---|
40 | (set-abbrev term))))) |
---|
41 | |
---|
42 | (defun abbrev-lookup (term) |
---|
43 | (let ((found (gethash term *clim-abbrev-table* nil))) |
---|
44 | (if found |
---|
45 | (if (eql (length found) 1) |
---|
46 | (let ((r (real-term-lookup (car found)))) |
---|
47 | (and r |
---|
48 | (concatenate 'string (car found) ": " r))) |
---|
49 | (format nil "Multiple matches found. Try any of: ~{~A~^ ~}" |
---|
50 | found))))) |
---|
51 | |
---|
52 | (defun real-term-lookup (term) |
---|
53 | (destructuring-bind (real-term &optional type (index-str "0")) |
---|
54 | (split-sequence #\, term) |
---|
55 | (let ((ents (gethash real-term *clim-table* nil)) |
---|
56 | (index (parse-integer index-str :junk-allowed t))) |
---|
57 | (if type |
---|
58 | (let ((all-type (loop for ent in ents |
---|
59 | if (string-equal (car ent) type) |
---|
60 | collect ent))) |
---|
61 | (if (< index (length all-type)) |
---|
62 | (merge-to-spec (cdr (nth index all-type))) |
---|
63 | (format nil "Invalid index ~A: must be between 0 and ~A." |
---|
64 | index (1- (length all-type))))) |
---|
65 | (if (eql (length ents) 0) |
---|
66 | nil |
---|
67 | (if (eql (length ents) 1) |
---|
68 | (merge-to-spec (cdr (car ents))) |
---|
69 | (let ((unique-types nil)) |
---|
70 | (loop for ent in ents |
---|
71 | do (pushnew (car ent) unique-types :test #'string-equal)) |
---|
72 | (format nil "Multiple entries found. Try looking up one of: ~{\"~A\"~^, ~}" |
---|
73 | (mapcar #'(lambda (type) |
---|
74 | (format nil "~A,~A~A" |
---|
75 | real-term |
---|
76 | type |
---|
77 | (let ((count (count type ents :key #'car :test #'string-equal))) |
---|
78 | (if (> count 1) |
---|
79 | (format nil ",{0-~A}" |
---|
80 | (1- count)) |
---|
81 | "")))) |
---|
82 | unique-types))))))))) |
---|
83 | |
---|
84 | (defun term-lookup (term) |
---|
85 | (or (real-term-lookup term) |
---|
86 | (abbrev-lookup term))) |
---|