cffi-toolchain: don't reintroduce bugs to ECL's ASDF
[cffi.git] / doc / colorize-lisp-examples.lisp
blob3f37e439da29964ae15fc31dfb497cc4174ddf10
1 ;;; This is code was taken from lisppaste2 and is a quick hack
2 ;;; to colorize lisp examples in the html generated by Texinfo.
3 ;;; It is not general-purpose utility, though it could easily be
4 ;;; turned into one.
6 ;;;; colorize-package.lisp
8 (defpackage :colorize
9 (:use :common-lisp)
10 (:export :scan-string :format-scan :html-colorization
11 :find-coloring-type :autodetect-coloring-type
12 :coloring-types :scan :scan-any :advance :call-parent-formatter
13 :*coloring-css* :make-background-css :*css-background-class*
14 :colorize-file :colorize-file-to-stream :*version-token*))
16 ;;;; coloring-css.lisp
18 (in-package :colorize)
20 (defparameter *coloring-css*
21 ".symbol { color: #770055; background-color: transparent; border: 0px; margin: 0px;}
22 a.symbol:link { color: #229955; background-color : transparent; text-decoration: none; border: 0px; margin: 0px; }
23 a.symbol:active { color : #229955; background-color : transparent; text-decoration: none; border: 0px; margin: 0px; }
24 a.symbol:visited { color : #229955; background-color : transparent; text-decoration: none; border: 0px; margin: 0px; }
25 a.symbol:hover { color : #229955; background-color : transparent; text-decoration: none; border: 0px; margin: 0px; }
26 .special { color : #FF5000; background-color : inherit; }
27 .keyword { color : #770000; background-color : inherit; }
28 .comment { color : #007777; background-color : inherit; }
29 .string { color : #777777; background-color : inherit; }
30 .character { color : #0055AA; background-color : inherit; }
31 .syntaxerror { color : #FF0000; background-color : inherit; }
32 span.paren1:hover { color : inherit; background-color : #BAFFFF; }
33 span.paren2:hover { color : inherit; background-color : #FFCACA; }
34 span.paren3:hover { color : inherit; background-color : #FFFFBA; }
35 span.paren4:hover { color : inherit; background-color : #CACAFF; }
36 span.paren5:hover { color : inherit; background-color : #CAFFCA; }
37 span.paren6:hover { color : inherit; background-color : #FFBAFF; }
40 (defvar *css-background-class* "lisp-bg")
42 (defun for-css (thing)
43 (if (symbolp thing) (string-downcase (symbol-name thing))
44 thing))
46 (defun make-background-css (color &key (class *css-background-class*) (extra nil))
47 (format nil ".~A { background-color: ~A; color: black; ~{~A; ~}}~:*~:*~:*
48 .~A:hover { background-color: ~A; color: black; ~{~A; ~}}~%"
49 class color
50 (mapcar #'(lambda (extra)
51 (format nil "~A : ~{~A ~}"
52 (for-css (first extra))
53 (mapcar #'for-css (cdr extra))))
54 extra)))
56 ;;;; colorize.lisp
58 ;(in-package :colorize)
60 (eval-when (:compile-toplevel :load-toplevel :execute)
61 (defparameter *coloring-types* nil)
62 (defparameter *version-token* (gensym)))
64 (defclass coloring-type ()
65 ((modes :initarg :modes :accessor coloring-type-modes)
66 (default-mode :initarg :default-mode :accessor coloring-type-default-mode)
67 (transition-functions :initarg :transition-functions :accessor coloring-type-transition-functions)
68 (fancy-name :initarg :fancy-name :accessor coloring-type-fancy-name)
69 (term-formatter :initarg :term-formatter :accessor coloring-type-term-formatter)
70 (formatter-initial-values :initarg :formatter-initial-values :accessor coloring-type-formatter-initial-values :initform nil)
71 (formatter-after-hook :initarg :formatter-after-hook :accessor coloring-type-formatter-after-hook :initform (constantly ""))
72 (autodetect-function :initarg :autodetect-function :accessor coloring-type-autodetect-function
73 :initform (constantly nil))
74 (parent-type :initarg :parent-type :accessor coloring-type-parent-type
75 :initform nil)
76 (visible :initarg :visible :accessor coloring-type-visible
77 :initform t)))
79 (defun find-coloring-type (type)
80 (if (typep type 'coloring-type)
81 type
82 (cdr (assoc (symbol-name type) *coloring-types* :test #'string-equal :key #'symbol-name))))
84 (defun autodetect-coloring-type (name)
85 (car
86 (find name *coloring-types*
87 :key #'cdr
88 :test #'(lambda (name type)
89 (and (coloring-type-visible type)
90 (funcall (coloring-type-autodetect-function type) name))))))
92 (defun coloring-types ()
93 (loop for type-pair in *coloring-types*
94 if (coloring-type-visible (cdr type-pair))
95 collect (cons (car type-pair)
96 (coloring-type-fancy-name (cdr type-pair)))))
98 (defun (setf find-coloring-type) (new-value type)
99 (if new-value
100 (let ((found (assoc type *coloring-types*)))
101 (if found
102 (setf (cdr found) new-value)
103 (setf *coloring-types*
104 (nconc *coloring-types*
105 (list (cons type new-value))))))
106 (setf *coloring-types* (remove type *coloring-types* :key #'car))))
108 (defvar *scan-calls* 0)
110 (defvar *reset-position* nil)
112 (defmacro with-gensyms ((&rest names) &body body)
113 `(let ,(mapcar #'(lambda (name)
114 (list name `(make-symbol ,(symbol-name name)))) names)
115 ,@body))
117 (defmacro with-scanning-functions (string-param position-place mode-place mode-wait-place &body body)
118 (with-gensyms (num items position not-preceded-by string item new-mode until advancing)
119 `(labels ((advance (,num)
120 (setf ,position-place (+ ,position-place ,num))
122 (peek-any (,items &key ,not-preceded-by)
123 (incf *scan-calls*)
124 (let* ((,items (if (stringp ,items)
125 (coerce ,items 'list) ,items))
126 (,not-preceded-by (if (characterp ,not-preceded-by)
127 (string ,not-preceded-by) ,not-preceded-by))
128 (,position ,position-place)
129 (,string ,string-param))
130 (let ((,item (and
131 (< ,position (length ,string))
132 (find ,string ,items
133 :test #'(lambda (,string ,item)
134 #+nil
135 (format t "looking for ~S in ~S starting at ~S~%"
136 ,item ,string ,position)
137 (if (characterp ,item)
138 (char= (elt ,string ,position)
139 ,item)
140 (search ,item ,string :start2 ,position
141 :end2 (min (length ,string)
142 (+ ,position (length ,item))))))))))
143 (if (characterp ,item)
144 (setf ,item (string ,item)))
146 (if ,item
147 (if ,not-preceded-by
148 (if (>= (- ,position (length ,not-preceded-by)) 0)
149 (not (string= (subseq ,string
150 (- ,position (length ,not-preceded-by))
151 ,position)
152 ,not-preceded-by))
155 nil)
156 ,item
157 (progn
158 (and *reset-position*
159 (setf ,position-place *reset-position*))
160 nil)))))
161 (scan-any (,items &key ,not-preceded-by)
162 (let ((,item (peek-any ,items :not-preceded-by ,not-preceded-by)))
163 (and ,item (advance (length ,item)))))
164 (peek (,item &key ,not-preceded-by)
165 (peek-any (list ,item) :not-preceded-by ,not-preceded-by))
166 (scan (,item &key ,not-preceded-by)
167 (scan-any (list ,item) :not-preceded-by ,not-preceded-by)))
168 (macrolet ((set-mode (,new-mode &key ,until (,advancing t))
169 (list 'progn
170 (list 'setf ',mode-place ,new-mode)
171 (list 'setf ',mode-wait-place
172 (list 'lambda (list ',position)
173 (list 'let (list (list '*reset-position* ',position))
174 (list 'values ,until ,advancing)))))))
175 ,@body))))
177 (defvar *formatter-local-variables*)
179 (defmacro define-coloring-type (name fancy-name &key modes default-mode transitions formatters
180 autodetect parent formatter-variables (formatter-after-hook '(constantly ""))
181 invisible)
182 (with-gensyms (parent-type term type string current-mode position position-foobage mode-wait new-position advance)
183 `(let ((,parent-type (or (find-coloring-type ,parent)
184 (and ,parent
185 (error "No such coloring type: ~S" ,parent)))))
186 (setf (find-coloring-type ,name)
187 (make-instance 'coloring-type
188 :fancy-name ',fancy-name
189 :modes (append ',modes (if ,parent-type (coloring-type-modes ,parent-type)))
190 :default-mode (or ',default-mode
191 (if ,parent-type (coloring-type-default-mode ,parent-type)))
192 ,@(if autodetect
193 `(:autodetect-function ,autodetect))
194 :parent-type ,parent-type
195 :visible (not ,invisible)
196 :formatter-initial-values (lambda nil
197 (list* ,@(mapcar #'(lambda (e)
198 `(cons ',(car e) ,(second e)))
199 formatter-variables)
200 (if ,parent-type
201 (funcall (coloring-type-formatter-initial-values ,parent-type))
202 nil)))
203 :formatter-after-hook (lambda nil
204 (symbol-macrolet ,(mapcar #'(lambda (e)
205 `(,(car e) (cdr (assoc ',(car e) *formatter-local-variables*))))
206 formatter-variables)
207 (concatenate 'string
208 (funcall ,formatter-after-hook)
209 (if ,parent-type
210 (funcall (coloring-type-formatter-after-hook ,parent-type))
211 ""))))
212 :term-formatter
213 (symbol-macrolet ,(mapcar #'(lambda (e)
214 `(,(car e) (cdr (assoc ',(car e) *formatter-local-variables*))))
215 formatter-variables)
216 (lambda (,term)
217 (labels ((call-parent-formatter (&optional (,type (car ,term))
218 (,string (cdr ,term)))
219 (if ,parent-type
220 (funcall (coloring-type-term-formatter ,parent-type)
221 (cons ,type ,string))))
222 (call-formatter (&optional (,type (car ,term))
223 (,string (cdr ,term)))
224 (funcall
225 (case (first ,type)
226 ,@formatters
227 (t (lambda (,type text)
228 (call-parent-formatter ,type text))))
229 ,type ,string)))
230 (call-formatter))))
231 :transition-functions
232 (list
233 ,@(loop for transition in transitions
234 collect (destructuring-bind (mode &rest table) transition
235 `(cons ',mode
236 (lambda (,current-mode ,string ,position)
237 (let ((,mode-wait (constantly nil))
238 (,position-foobage ,position))
239 (with-scanning-functions ,string ,position-foobage
240 ,current-mode ,mode-wait
241 (let ((*reset-position* ,position))
242 (cond ,@table))
243 (values ,position-foobage ,current-mode
244 (lambda (,new-position)
245 (setf ,position-foobage ,new-position)
246 (let ((,advance (nth-value 1 (funcall ,mode-wait ,position-foobage))))
247 (values ,position-foobage ,advance)))))
248 )))))))))))
250 (defun full-transition-table (coloring-type-object)
251 (let ((parent (coloring-type-parent-type coloring-type-object)))
252 (if parent
253 (append (coloring-type-transition-functions coloring-type-object)
254 (full-transition-table parent))
255 (coloring-type-transition-functions coloring-type-object))))
257 (defun scan-string (coloring-type string)
258 (let* ((coloring-type-object (or (find-coloring-type coloring-type)
259 (error "No such coloring type: ~S" coloring-type)))
260 (transitions (full-transition-table coloring-type-object))
261 (result nil)
262 (low-bound 0)
263 (current-mode (coloring-type-default-mode coloring-type-object))
264 (mode-stack nil)
265 (current-wait (constantly nil))
266 (wait-stack nil)
267 (current-position 0)
268 (*scan-calls* 0))
269 (flet ((finish-current (new-position new-mode new-wait &key (extend t) push pop)
270 (let ((to (if extend new-position current-position)))
271 (if (> to low-bound)
272 (setf result (nconc result
273 (list (cons (cons current-mode mode-stack)
274 (subseq string low-bound
275 to))))))
276 (setf low-bound to)
277 (when pop
278 (pop mode-stack)
279 (pop wait-stack))
280 (when push
281 (push current-mode mode-stack)
282 (push current-wait wait-stack))
283 (setf current-mode new-mode
284 current-position new-position
285 current-wait new-wait))))
286 (loop
287 (if (> current-position (length string))
288 (return-from scan-string
289 (progn
290 (format *trace-output* "Scan was called ~S times.~%"
291 *scan-calls*)
292 (finish-current (length string) nil (constantly nil))
293 result))
295 (loop for transition in
296 (mapcar #'cdr
297 (remove current-mode transitions
298 :key #'car
299 :test-not #'(lambda (a b)
300 (or (eql a b)
301 (if (listp b)
302 (member a b))))))
304 (and transition
305 (multiple-value-bind
306 (new-position new-mode new-wait)
307 (funcall transition current-mode string current-position)
308 (when (> new-position current-position)
309 (finish-current new-position new-mode new-wait :extend nil :push t)
310 t)))
311 return t)
312 (multiple-value-bind
313 (pos advance)
314 (funcall current-wait current-position)
315 #+nil
316 (format t "current-wait returns ~S ~S (mode is ~S, pos is ~S)~%" pos advance current-mode current-position)
317 (and pos
318 (when (> pos current-position)
319 (finish-current (if advance
321 current-position)
322 (car mode-stack)
323 (car wait-stack)
324 :extend advance
325 :pop t)
326 t)))
327 (progn
328 (incf current-position)))
329 )))))
331 (defun format-scan (coloring-type scan)
332 (let* ((coloring-type-object (or (find-coloring-type coloring-type)
333 (error "No such coloring type: ~S" coloring-type)))
334 (color-formatter (coloring-type-term-formatter coloring-type-object))
335 (*formatter-local-variables* (funcall (coloring-type-formatter-initial-values coloring-type-object))))
336 (format nil "~{~A~}~A"
337 (mapcar color-formatter scan)
338 (funcall (coloring-type-formatter-after-hook coloring-type-object)))))
340 (defun encode-for-pre (string)
341 (declare (simple-string string))
342 (let ((output (make-array (truncate (length string) 2/3)
343 :element-type 'character
344 :adjustable t
345 :fill-pointer 0)))
346 (with-output-to-string (out output)
347 (loop for char across string
348 do (case char
349 ((#\&) (write-string "&amp;" out))
350 ((#\<) (write-string "&lt;" out))
351 ((#\>) (write-string "&gt;" out))
352 ((#\") (write-string "&quot;" out))
353 ((#\RIGHTWARDS_DOUBLE_ARROW) (write-string "&rArr;" out))
354 (t (write-char char out)))))
355 (coerce output 'simple-string)))
357 (defun string-substitute (string substring replacement-string)
358 "String substitute by Larry Hunter. Obtained from Google"
359 (let ((substring-length (length substring))
360 (last-end 0)
361 (new-string ""))
362 (do ((next-start
363 (search substring string)
364 (search substring string :start2 last-end)))
365 ((null next-start)
366 (concatenate 'string new-string (subseq string last-end)))
367 (setq new-string
368 (concatenate 'string
369 new-string
370 (subseq string last-end next-start)
371 replacement-string))
372 (setq last-end (+ next-start substring-length)))))
374 (defun decode-from-tt (string)
375 (string-substitute
376 (string-substitute
377 (string-substitute
378 (string-substitute
379 (string-substitute string "&amp;" "&")
380 "&lt;" "<")
381 "&gt;" ">")
382 "&rArr;" (string #\RIGHTWARDS_DOUBLE_ARROW))
383 "&quot;" "\""))
385 (defun html-colorization (coloring-type string)
386 (format-scan coloring-type
387 (mapcar #'(lambda (p)
388 (cons (car p)
389 (let ((tt (encode-for-pre (cdr p))))
390 (if (and (> (length tt) 0)
391 (char= (elt tt (1- (length tt))) #\>))
392 (format nil "~A~%" tt) tt))))
393 (scan-string coloring-type string))))
395 (defun colorize-file-to-stream (coloring-type input-file-name s2 &key (wrap t) (css-background "default"))
396 (let* ((input-file (if (pathname-type (merge-pathnames input-file-name))
397 (merge-pathnames input-file-name)
398 (make-pathname :type "lisp"
399 :defaults (merge-pathnames input-file-name))))
400 (*css-background-class* css-background))
401 (with-open-file (s input-file :direction :input)
402 (let ((lines nil)
403 (string nil))
404 (block done
405 (loop (let ((line (read-line s nil nil)))
406 (if line
407 (push line lines)
408 (return-from done)))))
409 (setf string (format nil "~{~A~%~}"
410 (nreverse lines)))
411 (if wrap
412 (format s2
413 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">
414 <html><head><style type=\"text/css\">~A~%~A</style><body>
415 <table width=\"100%\"><tr><td class=\"~A\">
416 <tt>~A</tt>
417 </tr></td></table></body></html>"
418 *coloring-css*
419 (make-background-css "white")
420 *css-background-class*
421 (html-colorization coloring-type string))
422 (write-string (html-colorization coloring-type string) s2))))))
424 (defun colorize-file (coloring-type input-file-name &optional output-file-name)
425 (let* ((input-file (if (pathname-type (merge-pathnames input-file-name))
426 (merge-pathnames input-file-name)
427 (make-pathname :type "lisp"
428 :defaults (merge-pathnames input-file-name))))
429 (output-file (or output-file-name
430 (make-pathname :type "html"
431 :defaults input-file))))
432 (with-open-file (s2 output-file :direction :output :if-exists :supersede)
433 (colorize-file-to-stream coloring-type input-file-name s2))))
435 ;; coloring-types.lisp
437 ;(in-package :colorize)
439 (eval-when (:compile-toplevel :load-toplevel :execute)
440 (defparameter *version-token* (gensym)))
442 (defparameter *symbol-characters*
443 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*!%$&+-1234567890")
445 (defparameter *non-constituent*
446 '(#\space #\tab #\newline #\linefeed #\page #\return
447 #\" #\' #\( #\) #\, #\; #\` #\[ #\]))
449 (defparameter *special-forms*
450 '("let" "load-time-value" "quote" "macrolet" "progn" "progv" "go" "flet" "the"
451 "if" "throw" "eval-when" "multiple-value-prog1" "unwind-protect" "let*"
452 "labels" "function" "symbol-macrolet" "block" "tagbody" "catch" "locally"
453 "return-from" "setq" "multiple-value-call"))
455 (defparameter *common-macros*
456 '("loop" "cond" "lambda"))
458 (defparameter *open-parens* '(#\())
459 (defparameter *close-parens* '(#\)))
461 (define-coloring-type :lisp "Basic Lisp"
462 :modes (:first-char-on-line :normal :symbol :escaped-symbol :keyword :string :comment
463 :multiline :character
464 :single-escaped :in-list :syntax-error)
465 :default-mode :first-char-on-line
466 :transitions
467 (((:in-list)
468 ((or
469 (scan-any *symbol-characters*)
470 (and (scan #\.) (scan-any *symbol-characters*))
471 (and (scan #\\) (advance 1)))
472 (set-mode :symbol
473 :until (scan-any *non-constituent*)
474 :advancing nil))
475 ((or (scan #\:) (scan "#:"))
476 (set-mode :keyword
477 :until (scan-any *non-constituent*)
478 :advancing nil))
479 ((scan "#\\")
480 (let ((count 0))
481 (set-mode :character
482 :until (progn
483 (incf count)
484 (if (> count 1)
485 (scan-any *non-constituent*)))
486 :advancing nil)))
487 ((scan #\")
488 (set-mode :string
489 :until (scan #\")))
490 ((scan #\;)
491 (set-mode :comment
492 :until (scan #\newline)))
493 ((scan "#|")
494 (set-mode :multiline
495 :until (scan "|#")))
496 ((scan #\()
497 (set-mode :in-list
498 :until (scan #\)))))
499 ((:normal :first-char-on-line)
500 ((scan #\()
501 (set-mode :in-list
502 :until (scan #\)))))
503 (:first-char-on-line
504 ((scan #\;)
505 (set-mode :comment
506 :until (scan #\newline)))
507 ((scan "#|")
508 (set-mode :multiline
509 :until (scan "|#")))
510 ((advance 1)
511 (set-mode :normal
512 :until (scan #\newline))))
513 (:multiline
514 ((scan "#|")
515 (set-mode :multiline
516 :until (scan "|#"))))
517 ((:symbol :keyword :escaped-symbol :string)
518 ((scan #\\)
519 (let ((count 0))
520 (set-mode :single-escaped
521 :until (progn
522 (incf count)
523 (if (< count 2)
524 (advance 1))))))))
525 :formatter-variables ((paren-counter 0))
526 :formatter-after-hook (lambda nil
527 (format nil "~{~A~}"
528 (loop for i from paren-counter downto 1
529 collect "</span></span>")))
530 :formatters
531 (((:normal :first-char-on-line)
532 (lambda (type s)
533 (declare (ignore type))
535 ((:in-list)
536 (lambda (type s)
537 (declare (ignore type))
538 (labels ((color-parens (s)
539 (let ((paren-pos (find-if-not #'null
540 (mapcar #'(lambda (c)
541 (position c s))
542 (append *open-parens*
543 *close-parens*)))))
544 (if paren-pos
545 (let ((before-paren (subseq s 0 paren-pos))
546 (after-paren (subseq s (1+ paren-pos)))
547 (paren (elt s paren-pos))
548 (open nil)
549 (count 0))
550 (when (member paren *open-parens* :test #'char=)
551 (setf count (mod paren-counter 6))
552 (incf paren-counter)
553 (setf open t))
554 (when (member paren *close-parens* :test #'char=)
555 (decf paren-counter))
556 (if open
557 (format nil "~A<span class=\"paren~A\">~C<span class=\"~A\">~A"
558 before-paren
559 (1+ count)
560 paren *css-background-class*
561 (color-parens after-paren))
562 (format nil "~A</span>~C</span>~A"
563 before-paren
564 paren (color-parens after-paren))))
565 s))))
566 (color-parens s))))
567 ((:symbol :escaped-symbol)
568 (lambda (type s)
569 (declare (ignore type))
570 (let* ((colon (position #\: s :from-end t))
571 (new-s (or (and colon (subseq s (1+ colon))) s)))
572 (cond
573 ((or
574 (member new-s *common-macros* :test #'string-equal)
575 (member new-s *special-forms* :test #'string-equal)
576 (some #'(lambda (e)
577 (and (> (length new-s) (length e))
578 (string-equal e (subseq new-s 0 (length e)))))
579 '("WITH-" "DEF")))
580 (format nil "<i><span class=\"symbol\">~A</span></i>" s))
581 ((and (> (length new-s) 2)
582 (char= (elt new-s 0) #\*)
583 (char= (elt new-s (1- (length new-s))) #\*))
584 (format nil "<span class=\"special\">~A</span>" s))
585 (t s)))))
586 (:keyword (lambda (type s)
587 (declare (ignore type))
588 (format nil "<span class=\"keyword\">~A</span>"
589 s)))
590 ((:comment :multiline)
591 (lambda (type s)
592 (declare (ignore type))
593 (format nil "<span class=\"comment\">~A</span>"
594 s)))
595 ((:character)
596 (lambda (type s)
597 (declare (ignore type))
598 (format nil "<span class=\"character\">~A</span>"
599 s)))
600 ((:string)
601 (lambda (type s)
602 (declare (ignore type))
603 (format nil "<span class=\"string\">~A</span>"
604 s)))
605 ((:single-escaped)
606 (lambda (type s)
607 (call-formatter (cdr type) s)))
608 ((:syntax-error)
609 (lambda (type s)
610 (declare (ignore type))
611 (format nil "<span class=\"syntaxerror\">~A</span>"
612 s)))))
614 (define-coloring-type :scheme "Scheme"
615 :autodetect (lambda (text)
617 (search "scheme" text :test #'char-equal)
618 (search "chicken" text :test #'char-equal)))
619 :parent :lisp
620 :transitions
621 (((:normal :in-list)
622 ((scan "...")
623 (set-mode :symbol
624 :until (scan-any *non-constituent*)
625 :advancing nil))
626 ((scan #\[)
627 (set-mode :in-list
628 :until (scan #\])))))
629 :formatters
630 (((:in-list)
631 (lambda (type s)
632 (declare (ignore type s))
633 (let ((*open-parens* (cons #\[ *open-parens*))
634 (*close-parens* (cons #\] *close-parens*)))
635 (call-parent-formatter))))
636 ((:symbol :escaped-symbol)
637 (lambda (type s)
638 (declare (ignore type))
639 (let ((result (if (find-package :r5rs-lookup)
640 (funcall (symbol-function (intern "SYMBOL-LOOKUP" :r5rs-lookup))
641 s))))
642 (if result
643 (format nil "<a href=\"~A\" class=\"symbol\">~A</a>"
644 result (call-parent-formatter))
645 (call-parent-formatter)))))))
647 (define-coloring-type :elisp "Emacs Lisp"
648 :autodetect (lambda (name)
649 (member name '("emacs")
650 :test #'(lambda (name ext)
651 (search ext name :test #'char-equal))))
652 :parent :lisp
653 :formatters
654 (((:symbol :escaped-symbol)
655 (lambda (type s)
656 (declare (ignore type))
657 (let ((result (if (find-package :elisp-lookup)
658 (funcall (symbol-function (intern "SYMBOL-LOOKUP" :elisp-lookup))
659 s))))
660 (if result
661 (format nil "<a href=\"~A\" class=\"symbol\">~A</a>"
662 result (call-parent-formatter))
663 (call-parent-formatter)))))))
665 (define-coloring-type :common-lisp "Common Lisp"
666 :autodetect (lambda (text)
667 (search "lisp" text :test #'char-equal))
668 :parent :lisp
669 :transitions
670 (((:normal :in-list)
671 ((scan #\|)
672 (set-mode :escaped-symbol
673 :until (scan #\|)))))
674 :formatters
675 (((:symbol :escaped-symbol)
676 (lambda (type s)
677 (declare (ignore type))
678 (let* ((colon (position #\: s :from-end t :test #'char=))
679 (to-lookup (if colon (subseq s (1+ colon)) s))
680 (result (if (find-package :clhs-lookup)
681 (funcall (symbol-function (intern "SYMBOL-LOOKUP" :clhs-lookup))
682 to-lookup))))
683 (if result
684 (format nil "<a href=\"~A\" class=\"symbol\">~A</a>"
685 result (call-parent-formatter))
686 (call-parent-formatter)))))))
688 (define-coloring-type :common-lisp-file "Common Lisp File"
689 :parent :common-lisp
690 :default-mode :in-list
691 :invisible t)
693 (defvar *c-open-parens* "([{")
694 (defvar *c-close-parens* ")]}")
696 (defvar *c-reserved-words*
697 '("auto" "break" "case" "char" "const"
698 "continue" "default" "do" "double" "else"
699 "enum" "extern" "float" "for" "goto"
700 "if" "int" "long" "register" "return"
701 "short" "signed" "sizeof" "static" "struct"
702 "switch" "typedef" "union" "unsigned" "void"
703 "volatile" "while" "__restrict" "_Bool"))
705 (defparameter *c-begin-word* "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789")
706 (defparameter *c-terminators* '(#\space #\return #\tab #\newline #\. #\/ #\- #\* #\+ #\{ #\} #\( #\) #\' #\" #\[ #\] #\< #\> #\#))
708 (define-coloring-type :basic-c "Basic C"
709 :modes (:normal :comment :word-ish :paren-ish :string :char :single-escape :preprocessor)
710 :default-mode :normal
711 :invisible t
712 :transitions
713 ((:normal
714 ((scan-any *c-begin-word*)
715 (set-mode :word-ish
716 :until (scan-any *c-terminators*)
717 :advancing nil))
718 ((scan "/*")
719 (set-mode :comment
720 :until (scan "*/")))
721 ((or
722 (scan-any *c-open-parens*)
723 (scan-any *c-close-parens*))
724 (set-mode :paren-ish
725 :until (advance 1)
726 :advancing nil))
727 ((scan #\")
728 (set-mode :string
729 :until (scan #\")))
730 ((or (scan "'\\")
731 (scan #\'))
732 (set-mode :character
733 :until (advance 2))))
734 (:string
735 ((scan #\\)
736 (set-mode :single-escape
737 :until (advance 1)))))
738 :formatter-variables
739 ((paren-counter 0))
740 :formatter-after-hook (lambda nil
741 (format nil "~{~A~}"
742 (loop for i from paren-counter downto 1
743 collect "</span></span>")))
744 :formatters
745 ((:normal
746 (lambda (type s)
747 (declare (ignore type))
749 (:comment
750 (lambda (type s)
751 (declare (ignore type))
752 (format nil "<span class=\"comment\">~A</span>"
753 s)))
754 (:string
755 (lambda (type s)
756 (declare (ignore type))
757 (format nil "<span class=\"string\">~A</span>"
758 s)))
759 (:character
760 (lambda (type s)
761 (declare (ignore type))
762 (format nil "<span class=\"character\">~A</span>"
763 s)))
764 (:single-escape
765 (lambda (type s)
766 (call-formatter (cdr type) s)))
767 (:paren-ish
768 (lambda (type s)
769 (declare (ignore type))
770 (let ((open nil)
771 (count 0))
772 (if (eql (length s) 1)
773 (progn
774 (when (member (elt s 0) (coerce *c-open-parens* 'list))
775 (setf open t)
776 (setf count (mod paren-counter 6))
777 (incf paren-counter))
778 (when (member (elt s 0) (coerce *c-close-parens* 'list))
779 (setf open nil)
780 (decf paren-counter)
781 (setf count (mod paren-counter 6)))
782 (if open
783 (format nil "<span class=\"paren~A\">~A<span class=\"~A\">"
784 (1+ count) s *css-background-class*)
785 (format nil "</span>~A</span>"
786 s)))
787 s))))
788 (:word-ish
789 (lambda (type s)
790 (declare (ignore type))
791 (if (member s *c-reserved-words* :test #'string=)
792 (format nil "<span class=\"symbol\">~A</span>" s)
793 s)))
796 (define-coloring-type :c "C"
797 :parent :basic-c
798 :transitions
799 ((:normal
800 ((scan #\#)
801 (set-mode :preprocessor
802 :until (scan-any '(#\return #\newline))))))
803 :formatters
804 ((:preprocessor
805 (lambda (type s)
806 (declare (ignore type))
807 (format nil "<span class=\"special\">~A</span>" s)))))
809 (defvar *c++-reserved-words*
810 '("asm" "auto" "bool" "break" "case"
811 "catch" "char" "class" "const" "const_cast"
812 "continue" "default" "delete" "do" "double"
813 "dynamic_cast" "else" "enum" "explicit" "export"
814 "extern" "false" "float" "for" "friend"
815 "goto" "if" "inline" "int" "long"
816 "mutable" "namespace" "new" "operator" "private"
817 "protected" "public" "register" "reinterpret_cast" "return"
818 "short" "signed" "sizeof" "static" "static_cast"
819 "struct" "switch" "template" "this" "throw"
820 "true" "try" "typedef" "typeid" "typename"
821 "union" "unsigned" "using" "virtual" "void"
822 "volatile" "wchar_t" "while"))
824 (define-coloring-type :c++ "C++"
825 :parent :c
826 :transitions
827 ((:normal
828 ((scan "//")
829 (set-mode :comment
830 :until (scan-any '(#\return #\newline))))))
831 :formatters
832 ((:word-ish
833 (lambda (type s)
834 (declare (ignore type))
835 (if (member s *c++-reserved-words* :test #'string=)
836 (format nil "<span class=\"symbol\">~A</span>"
838 s)))))
840 (defvar *java-reserved-words*
841 '("abstract" "boolean" "break" "byte" "case"
842 "catch" "char" "class" "const" "continue"
843 "default" "do" "double" "else" "extends"
844 "final" "finally" "float" "for" "goto"
845 "if" "implements" "import" "instanceof" "int"
846 "interface" "long" "native" "new" "package"
847 "private" "protected" "public" "return" "short"
848 "static" "strictfp" "super" "switch" "synchronized"
849 "this" "throw" "throws" "transient" "try"
850 "void" "volatile" "while"))
852 (define-coloring-type :java "Java"
853 :parent :c++
854 :formatters
855 ((:word-ish
856 (lambda (type s)
857 (declare (ignore type))
858 (if (member s *java-reserved-words* :test #'string=)
859 (format nil "<span class=\"symbol\">~A</span>"
861 s)))))
863 (let ((terminate-next nil))
864 (define-coloring-type :objective-c "Objective C"
865 :autodetect (lambda (text) (search "mac" text :test #'char=))
866 :modes (:begin-message-send :end-message-send)
867 :transitions
868 ((:normal
869 ((scan #\[)
870 (set-mode :begin-message-send
871 :until (advance 1)
872 :advancing nil))
873 ((scan #\])
874 (set-mode :end-message-send
875 :until (advance 1)
876 :advancing nil))
877 ((scan-any *c-begin-word*)
878 (set-mode :word-ish
879 :until (or
880 (and (peek-any '(#\:))
881 (setf terminate-next t))
882 (and terminate-next (progn
883 (setf terminate-next nil)
884 (advance 1)))
885 (scan-any *c-terminators*))
886 :advancing nil)))
887 (:word-ish
888 #+nil
889 ((scan #\:)
890 (format t "hi~%")
891 (set-mode :word-ish :until (advance 1) :advancing nil)
892 (setf terminate-next t))))
893 :parent :c++
894 :formatter-variables ((is-keyword nil) (in-message-send nil))
895 :formatters
896 ((:begin-message-send
897 (lambda (type s)
898 (setf is-keyword nil)
899 (setf in-message-send t)
900 (call-formatter (cons :paren-ish type) s)))
901 (:end-message-send
902 (lambda (type s)
903 (setf is-keyword nil)
904 (setf in-message-send nil)
905 (call-formatter (cons :paren-ish type) s)))
906 (:word-ish
907 (lambda (type s)
908 (declare (ignore type))
909 (prog1
910 (let ((result (if (find-package :cocoa-lookup)
911 (funcall (symbol-function (intern "SYMBOL-LOOKUP" :cocoa-lookup))
912 s))))
913 (if result
914 (format nil "<a href=\"~A\" class=\"symbol\">~A</a>"
915 result s)
916 (if (member s *c-reserved-words* :test #'string=)
917 (format nil "<span class=\"symbol\">~A</span>" s)
918 (if in-message-send
919 (if is-keyword
920 (format nil "<span class=\"keyword\">~A</span>" s)
922 s))))
923 (setf is-keyword (not is-keyword))))))))
926 ;#!/usr/bin/clisp
927 ;#+sbcl
928 ;(require :asdf)
929 ;(asdf:oos 'asdf:load-op :colorize)
931 (defmacro with-each-stream-line ((var stream) &body body)
932 (let ((eof (gensym))
933 (eof-value (gensym))
934 (strm (gensym)))
935 `(let ((,strm ,stream)
936 (,eof ',eof-value))
937 (do ((,var (read-line ,strm nil ,eof) (read-line ,strm nil ,eof)))
938 ((eql ,var ,eof))
939 ,@body))))
941 (defun system (control-string &rest args)
942 "Interpolate ARGS into CONTROL-STRING as if by FORMAT, and
943 synchronously execute the result using a Bourne-compatible shell, with
944 output to *verbose-out*. Returns the shell's exit code."
945 (let ((command (apply #'format nil control-string args)))
946 (format t "; $ ~A~%" command)
947 #+sbcl
948 (sb-impl::process-exit-code
949 (sb-ext:run-program
950 "/bin/sh"
951 (list "-c" command)
952 :input nil :output *standard-output*))
953 #+(or cmucl scl)
954 (ext:process-exit-code
955 (ext:run-program
956 "/bin/sh"
957 (list "-c" command)
958 :input nil :output *verbose-out*))
959 #+clisp ;XXX not exactly *verbose-out*, I know
960 (ext:run-shell-command command :output :terminal :wait t)
963 (defun strcat (&rest strings)
964 (apply #'concatenate 'string strings))
966 (defun string-starts-with (start str)
967 (and (>= (length str) (length start))
968 (string-equal start str :end2 (length start))))
970 (defmacro string-append (outputstr &rest args)
971 `(setq ,outputstr (concatenate 'string ,outputstr ,@args)))
973 (defconstant +indent+ 0
974 "Indentation used in the examples.")
976 (defun texinfo->raw-lisp (code)
977 "Answer CODE with spurious Texinfo output removed. For use in
978 preprocessing output in a @lisp block before passing to colorize."
979 (decode-from-tt
980 (with-output-to-string (output)
981 (do* ((last-position 0)
982 (next-position
983 #0=(search #1="<span class=\"roman\">" code
984 :start2 last-position :test #'char-equal)
985 #0#))
986 ((eq nil next-position)
987 (write-string code output :start last-position))
988 (write-string code output :start last-position :end next-position)
989 (let ((end (search #2="</span>" code
990 :start2 (+ next-position (length #1#))
991 :test #'char-equal)))
992 (assert (integerp end) ()
993 "Missing ~A tag in HTML for @lisp block~%~
994 HTML contents of block:~%~A" #2# code)
995 (write-string code output
996 :start (+ next-position (length #1#))
997 :end end)
998 (setf last-position (+ end (length #2#))))))))
1000 (defun process-file (from to)
1001 (with-open-file (output to :direction :output :if-exists :error)
1002 (with-open-file (input from :direction :input)
1003 (let ((line-processor nil)
1004 (piece-of-code '()))
1005 (labels
1006 ((process-line-inside-pre (line)
1007 (cond ((string-starts-with "</pre>" line)
1008 (with-input-from-string
1009 (stream (colorize:html-colorization
1010 :common-lisp
1011 (texinfo->raw-lisp
1012 (apply #'concatenate 'string
1013 (nreverse piece-of-code)))))
1014 (with-each-stream-line (cline stream)
1015 (format output " ~A~%" cline)))
1016 (write-line line output)
1017 (setq piece-of-code '()
1018 line-processor #'process-regular-line))
1019 (t (let ((to-append (subseq line +indent+)))
1020 (push (if (string= "" to-append)
1022 to-append) piece-of-code)
1023 (push (string #\Newline) piece-of-code)))))
1024 (process-regular-line (line)
1025 (let ((len (some (lambda (test-string)
1026 (when (string-starts-with test-string line)
1027 (length test-string)))
1028 '("<pre class=\"lisp\">"
1029 "<pre class=\"smalllisp\">"))))
1030 (cond (len
1031 (setq line-processor #'process-line-inside-pre)
1032 (write-string "<pre class=\"lisp\">" output)
1033 (push (subseq line (+ len +indent+)) piece-of-code)
1034 (push (string #\Newline) piece-of-code))
1035 (t (write-line line output))))))
1036 (setf line-processor #'process-regular-line)
1037 (with-each-stream-line (line input)
1038 (funcall line-processor line)))))))
1040 (defun process-dir (dir)
1041 (dolist (html-file (directory dir))
1042 (let* ((name (namestring html-file))
1043 (temp-name (strcat name ".temp")))
1044 (process-file name temp-name)
1045 (system "mv ~A ~A" temp-name name))))
1047 ;; (go "/tmp/doc/manual/html_node/*.html")
1049 #+clisp
1050 (progn
1051 (assert (first ext:*args*))
1052 (process-dir (first ext:*args*)))
1054 #+sbcl
1055 (progn
1056 (assert (second sb-ext:*posix-argv*))
1057 (process-dir (second sb-ext:*posix-argv*))
1058 (sb-ext:quit))