2 ;; Copyright (c) 2006, Cyrus Harmon
4 ;; filter.lisp - filters are used to process markup document
5 ;; sexps. The intent is that these filters are independent from the
6 ;; final rendering. Think of this as a set of preprocessing stages
7 ;; after document parsing. So now we have a markup document which gets
8 ;; parsed, filtered and then rendered.
10 ;; Yes, perhaps the name "filter" could be better. I'm open to
15 (defgeneric filter-gf
(filter car list
))
16 (defgeneric filter
(type sexp
))
18 (defmethod filter-gf (filter car list
)
19 (cond ((null car
) (when (cdr list
) (filter filter
(cdr list
))))
20 ((atom car
) (cons car
(filter filter
(cdr list
))))
21 (t (cons (filter filter
(car list
))
22 (filter filter
(cdr list
))))))
24 (defmethod filter (type sexp
)
25 (filter-gf type
(car sexp
) sexp
))
27 (defun apply-filters (sexp filters
)
28 (cond ((null filters
) sexp
)
29 ((listp filters
) (apply-filters (filter (car filters
) sexp
) (cdr filters
)))
30 (t (filter filters sexp
))))
33 ;;; test filter. can reverse strings
35 (defun reverse-strings (sexp)
36 (cond ((null sexp
) nil
)
37 ((atom sexp
) (if (stringp sexp
)
40 (t (cons (reverse-strings (car sexp
))
41 (reverse-strings (cdr sexp
))))))
43 (defmethod filter-gf ((filter (eql :test
)) (car (eql :reverse
)) list
)
44 `(:span
,@(filter :test
(reverse-strings (reverse (cdr list
))))))
47 ;;; lisp filter. can eval lisp forms
49 (defun collect-string (str-list)
50 (declare (optimize (debug 2)))
51 (apply #'concatenate
'string
54 (unless (equal x
'blank-line
)
58 (defun lc-format (dest ctrl-string
&rest args
)
59 (let ((*print-case
* :downcase
))
60 (apply #'format dest ctrl-string args
)))
62 (defun eval-lisp (tag body
&key
(show-commands t
) (show-results t
))
63 (declare (ignore tag
))
65 (with-input-from-string (ifs (collect-string body
))
66 (loop for line
= (read ifs nil
)
67 while line collect line
))))
68 (if (not (or show-commands show-results
))
69 (progn (mapcar #'eval lines
)
71 `((:div
:class
"lisp")
73 ,@(mapcan #'(lambda (x)
76 (lc-format nil
"~W~%" x
)))
77 (let ((output (eval x
)))
81 (lc-format nil
"~W~%" output
)
82 (format nil
"~S~%" output
)))
87 (defmethod filter-gf ((filter (eql :lisp
)) (car (eql :lisp
)) list
)
88 (eval-lisp car
(cdr list
)))
90 (defmethod filter-gf ((filter (eql :lisp
)) (car (eql :lisp-no-results
)) list
)
91 (eval-lisp car
(cdr list
) :show-results nil
))
93 (defmethod filter-gf ((filter (eql :lisp
)) (car (eql :lisp-no-commands
)) list
)
94 (eval-lisp car
(cdr list
) :show-commands nil
))
96 (defmethod filter-gf ((filter (eql :lisp
)) (car (eql :lisp-silent
)) list
)
97 (eval-lisp car
(cdr list
) :show-results nil
:show-commands nil
))
99 (defmethod filter-gf ((filter (eql :lisp
)) (car (eql :lisp-no-eval
)) list
)
100 `((:div
:class
"lisp") (:pre
,@(cdr list
))))
102 (defmethod filter-gf ((filter (eql :lisp
)) (car (eql :lisp-value
)) list
)
103 (eval (read-from-string (car (cdr list
)))))
105 (defmethod filter-gf ((filter (eql :lisp
)) (car (eql :setf-lisp-value
)) list
)
106 (let ((sexp (read-from-string (car (cdr list
)))))
107 (setf (symbol-value (car sexp
)) (cadr sexp
))
110 (defmethod filter-gf ((filter (eql :lisp
)) (car (eql :code-block
)) list
)
111 `((:div
:class
"lisp") (:pre
,@(cdr list
))))
114 ;;; markup-metadata filter. sets various special variables with
115 ;;; document metadata info
117 (defmethod filter-gf ((filter (eql :smarkup-metadata
)) (car (eql :copyright
)) list
)
118 (setf *copyright
* (cadr list
))
121 (defmethod filter-gf ((filter (eql :smarkup-metadata
)) (car (eql :title
)) list
)
122 (setf *document-title
* (cadr list
))
125 (defmethod filter-gf ((filter (eql :smarkup-metadata
)) (car (eql :titlerunning
)) list
)
126 (setf *document-titlerunning
* (cadr list
))
129 (defmethod filter-gf ((filter (eql :smarkup-metadata
)) (car (eql :subtitle
)) list
)
130 (setf *document-subtitle
* (cadr list
))
133 (defmethod filter-gf ((filter (eql :smarkup-metadata
)) (car (eql :author
)) list
)
134 (setf *document-author
* (cadr list
))
137 (defmethod filter-gf ((filter (eql :smarkup-metadata
)) (car (eql :tocauthor
)) list
)
138 (setf *document-tocauthor
* (cadr list
))
141 (defmethod filter-gf ((filter (eql :smarkup-metadata
)) (car (eql :authorrunning
)) list
)
142 (setf *document-authorrunning
* (cadr list
))
145 (defmethod filter-gf ((filter (eql :smarkup-metadata
)) (car (eql :address
)) list
)
146 (setf *document-address
* (cadr list
))
149 (defmethod filter-gf ((filter (eql :smarkup-metadata
)) (car (eql :institute
)) list
)
150 (setf *document-institute
* (cadr list
))
153 (defparameter *bibtex-macros
* (make-hash-table :test
#'equalp
))
154 (defparameter *bibtex-database
* (make-hash-table :test
#'equalp
))
155 (defparameter *bibtex-style
* nil
)
156 (defparameter *cite-keys
* nil
)
157 (defparameter *cite-order
* (make-hash-table :test
#'equalp
))
158 (defparameter *current-citation
* 0)
160 (defmethod filter-gf ((filter (eql :smarkup-metadata
)) (car (eql :bibtex-database
)) list
)
161 (setf *bibtex-database
* (make-hash-table :test
#'equalp
))
162 (setf *cite-keys
* nil
)
163 (setf *cite-order
* (make-hash-table :test
#'equalp
))
164 (setf *current-citation
* 0)
165 (cond ((and (stringp (cadr list
))
166 (eql (char (cadr list
) 0) #\
())
167 (let ((database-spec-list
168 (let ((*read-eval
* nil
))
169 (read-from-string (cadr list
)))))
170 (loop for database-spec in database-spec-list
172 (let ((database-uri (puri:parse-uri database-spec
)))
173 (cond ((eql (puri:uri-scheme database-uri
) :asdf
)
175 (asdf:component-pathname
176 (ch-asdf::asdf-lookup database-spec
))))
177 (let ((bibtex-runtime::*bib-database
* *bibtex-database
*)
178 (bibtex-runtime::*bib-macros
* *bibtex-macros
*))
179 (with-open-file (f database
)
180 (bibtex-runtime:read-bib-database f
)))))
182 (let ((database (puri:uri-path database-uri
)))
183 (let ((bibtex-runtime::*bib-database
* *bibtex-database
*)
184 (bibtex-runtime::*bib-macros
* *bibtex-macros
*))
185 (with-open-file (f database
)
186 (bibtex-runtime:read-bib-database f
)))))))))
188 (t (warn "Unsupported bibilography database~&"))))
190 (defmethod filter-gf ((filter (eql :smarkup-metadata
))
191 (car (eql :bibtex-style
))
193 (let ((bst (cadr list
)))
194 (setf *bibtex-style
* bst
)))
196 (defmethod filter-gf ((filter (eql :smarkup-metadata
)) (car (eql :smarkup-metadata
)) list
)
197 (filter-gf filter
(cadr list
) (cdr list
))
200 (defparameter *html-css-stylesheet-url
* "style.css")
202 (defmethod filter-gf ((filter (eql :html-metadata
)) (car (eql :htmlcss
)) list
)
203 (setf *html-css-stylesheet-url
* (cadr list
))
206 (defmethod filter-gf ((filter (eql :html-metadata
)) (car (eql :html-metadata
)) list
)
207 (filter-gf filter
(cadr list
) (cdr list
))
218 ;;; Ok, need to get the bibtex-style and then call bibtex or its guts to get the references.
221 (defun get-bib-entry (entry)
222 (with-output-to-string (stream)
223 (bibtex-runtime::write-bib-entry entry stream
)))
225 (defun get-bib-order (entry)
226 (gethash entry
*cite-order
*))
228 (defmethod filter-gf ((filter (eql :ref
)) (car (eql :bibcite
)) list
)
229 (loop for cite-key in
(cdr list
)
231 (unless (member cite-key
*cite-keys
* :test
'string
=)
232 (push cite-key
*cite-keys
*)
233 (setf (gethash cite-key
*cite-order
*) (incf *current-citation
*)))
234 (let ((v (gethash cite-key
*bibtex-database
*)))
236 (bibtex-runtime::write-bib-entry v
)
237 (warn "~%bibliography entry ~A not found~%" cite-key
))))
245 (defparameter *outline-elements
* '(:h1
:h2
:h3
:h4
:h5
:h6
))
247 (defparameter *outline-level
* 6)
249 (defmethod filter-gf ((filter (eql :outline
)) car list
)
250 (let ((outline-elements
251 (subseq *outline-elements
*
252 0 (min (length *outline-elements
*)
254 (remove-if-not #'(lambda (x) (member (car x
) outline-elements
))