forked from mbattyani/cl-typesetting
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpprint.lisp
148 lines (133 loc) · 6.71 KB
/
pprint.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
;;; cl-typesetting copyright 2003-2004 Marc Battyani see license.txt for the details
;;; You can reach me at [email protected] or [email protected]
;;; The homepage of cl-typesetting is here: http://www.fractalconcept.com/asp/html/cl-typesetting.html
(in-package #:typeset)
;;; This is a module to typeset Common Lisp code with some syntax coloring
;;; The syntax coloring is too simple to be 100% accurate:
;;; Improvements welcomed!
(defparameter *pp-font-size* 9)
(defparameter *pp-default-decoration* '("courier" (0.0 0.0 0.0)))
(defparameter *pp-keyword-decoration* '("courier" (0.8 0.0 0.0)))
(defparameter *pp-common-lisp-decoration* '("courier" (0.0 0.0 0.4)))
(defparameter *pp-string-decoration* '("courier" (0.0 0.5 0.0)))
(defparameter *pp-comment-decoration* '("courier" (0.2 0.2 0.6)))
(defparameter *pp-symbol-decoration-table* (make-hash-table))
(defparameter *exceptions* '())
(defun add-symbol-decoration (symbol decoration)
(setf (gethash symbol *pp-symbol-decoration-table*) decoration))
;(loop for (symbol . decoration) in '((defvar "courier-bold" (0.0 0.0 0.5))
; (defun "courier-bold" (0.0 0.2 0.5))
; (defmethod "courier-bold" (0.0 0.2 0.5)))
; do (add-symbol-decoration symbol decoration))
(loop for symbol being the external-symbols of 'common-lisp
when (eql (search "DEF" (symbol-name symbol)) 0)
do (add-symbol-decoration symbol '("courier-bold" (0.0 0.2 0.5))))
(defun split-comment (line)
(let ((comment-pos (position #\; line)))
(if comment-pos
(values (subseq line 0 comment-pos)(subseq line comment-pos))
line)))
(defun clean-line (line)
(setf line (copy-seq line))
(map-into line #'(lambda (char)
(if (find char "()'`# ")
#\Space
char))
line))
(defun process-lisp-line (line)
(multiple-value-bind (code comment)(split-comment line)
(let* ((cleaned-line (clean-line code))
(cl-package (find-package 'common-lisp))
(decorations '())
(start 0)
(trimmed 0)
(length (length cleaned-line)))
(iter
(setf trimmed (position #\Space cleaned-line :start start :test #'char/=))
(while (and trimmed (< trimmed length)))
(for (values obj end) = (ignore-errors
(read-from-string
cleaned-line nil nil
:start trimmed :preserve-whitespace t)))
(unless (numberp end)
(setf end (position #\Space cleaned-line :start trimmed :test #'char=)))
(while (and (numberp end) (< end length)))
(cond ((keywordp obj)
(push (list* trimmed end *pp-keyword-decoration*) decorations))
((stringp obj)
(push (list* trimmed end *pp-string-decoration*) decorations))
((gethash obj *pp-symbol-decoration-table*)
(push (list* trimmed end (gethash obj *pp-symbol-decoration-table*)) decorations))
((and (symbolp obj)
(or (eq (symbol-package obj) cl-package)
(member (symbol-name obj)
'("FOR" "THEN" "WHILE" "COLLECT" "IN" "WITH" "FINALLY")
:test #'string=))
(not (member (symbol-name obj) *exceptions* :test #'string=)))
(push (list* trimmed end *pp-common-lisp-decoration*) decorations)))
(setf start end))
(setf start 0)
(loop for (start-tok end-tok font-name color) in (nreverse decorations) do
(when (/= start start-tok)
(with-text-compilation
(verbatim (subseq line start start-tok))))
(with-text-compilation
(with-style (:font font-name :font-size *pp-font-size* :color color)
(verbatim (subseq line start-tok end-tok))))
(setf start end-tok))
(with-text-compilation
(when (< start length)
(verbatim (subseq code start)))
(with-style (:font (first *pp-comment-decoration*)
:font-size *pp-font-size*
:color (second *pp-comment-decoration*))
(verbatim comment)
(when (zerop length) (verbatim " ")) :eol)))))
(defmethod process-lisp-code ((s stream))
(with-text-compilation
(paragraph (:h-align :left :top-margin 10
:left-margin 5 :right-margin 5
:font "courier" :font-size *pp-font-size*)
(loop for line = (read-line s nil)
while line
do (with-text-compilation
(process-lisp-line line))))))
(defmethod process-lisp-code ((lisp-file pathname))
(with-open-file (s lisp-file :direction :input)
(process-lisp-code s)))
(defmethod process-lisp-code ((lisp-string string))
(with-input-from-string (s lisp-string)
(process-lisp-code s)))
(defmethod process-lisp-code ((sexpr t))
(process-lisp-code
(with-output-to-string (s)
(pprint sexpr s))))
(defun pprint-lisp-file (lisp-code pdf-file &optional title *exceptions*)
(with-document ()
(let* ((margins '(30 50 30 50))
(print-stamp (multiple-value-bind (second minute hour date month year)
(get-decoded-time)
(declare (ignore second))
(format nil "Printed on ~4D-~2,'0D-~2,'0D ~2,'0D:~2,'0D"
year month date hour minute)))
(header (compile-text ()
(paragraph (:h-align :center
:font "Helvetica-BoldOblique" :font-size 12)
(put-string (cond
(title title)
((pathnamep lisp-code)(namestring lisp-code))
(t "Lisp Source Code"))))
(vspace 1)
(hrule :dy 0.5)))
(footer (lambda (pdf:*page*)
(compile-text (:font "Helvetica" :font-size 10)
(hrule :dy 1/2)
(hbox (:align :center :adjustable-p t)
(verbatim print-stamp)
:hfill
(verbatim
(format nil "Page ~d" pdf:*page-number*))))))
(content (compile-text () (process-lisp-code lisp-code))))
(draw-pages content :margins margins :header header :footer footer)
(when pdf:*page* (finalize-page pdf:*page*))
(pdf:write-document pdf-file))))