colorize-lisp-examples.lisp 43 KB

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