Very ragged work-in-progress.
[sod] / output.lisp
CommitLineData
abdf50aa
MW
1;;; -*-lisp-*-
2;;;
3;;; Output driver for SOD translator
4;;;
5;;; (c) 2009 Straylight/Edgeware
6;;;
7
8;;;----- Licensing notice ---------------------------------------------------
9;;;
10;;; This file is part of the Simple Object Definition system.
11;;;
12;;; SOD is free software; you can redistribute it and/or modify
13;;; it under the terms of the GNU General Public License as published by
14;;; the Free Software Foundation; either version 2 of the License, or
15;;; (at your option) any later version.
16;;;
17;;; SOD is distributed in the hope that it will be useful,
18;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;;; GNU General Public License for more details.
21;;;
22;;; You should have received a copy of the GNU General Public License
23;;; along with SOD; if not, write to the Free Software Foundation,
24;;; Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25
26(cl:in-package #:sod)
27
28;;;--------------------------------------------------------------------------
1f1d88f5
MW
29;;; Sequencing machinery.
30
31(defclass sequencer-item ()
32 ((name :initarg :name
33 :reader sequencer-item-name)
34 (functions :initarg :functions
35 :initform nil
36 :type list
37 :accessor sequencer-item-functions))
38 (:documentation
39 "Represents a distinct item to be sequenced by a SEQUENCER.
40
41 A SEQUENCER-ITEM maintains a list of FUNCTIONS which are invoked when the
42 sequencer is invoked. This class is not intended to be subclassed."))
43
44(defmethod print-object ((item sequencer-item) stream)
45 (print-unreadable-object (item stream :type t)
46 (prin1 (sequencer-item-name item) stream)))
47
48(defclass sequencer ()
49 ((constraints :initarg :constraints
50 :initform nil
51 :type list
52 :accessor sequencer-constraints)
53 (table :initform (make-hash-table :test #'equal)
54 :reader sequencer-table))
55 (:documentation
56 "A sequencer tracks items and invokes them in the proper order.
57
58 The job of a SEQUENCER object is threefold. Firstly, it collects
59 sequencer items and stores them in its table indexed by name. Secondly,
60 it gathers CONSTRAINTS, which impose an ordering on the items. Thirdly,
61 it can be instructed to invoke the items in an order compatible with the
62 established constraints.
63
64 Sequencer item names may may any kind of object which can be compared with
65 EQUAL. In particular, symbols, integers and strings are reasonable
66 choices for atomic names, and lists work well for compound names -- so
67 it's possible to construct a hierarchy."))
68
69(defgeneric ensure-sequencer-item (sequencer name)
70 (:documentation
71 "Arrange that SEQUENCER has a sequencer-item called NAME.
72
73 Returns the corresponding SEQUENCER-ITEM object."))
74
75(defgeneric add-sequencer-constraint (sequencer constraint)
76 (:documentation
77 "Attach the given CONSTRAINT to an SEQUENCER.
78
79 The CONSTRAINT should be a list of sequencer-item names; see
80 ENSURE-SEQUENCER-ITEM for what they look like. Note that the names
81 needn't have been declared in advance; indeed, they needn't be mentioned
82 anywhere else at all."))
83
84(defgeneric add-sequencer-item-function (sequencer name function)
85 (:documentation
86 "Arranges to call FUNCTION when the item called NAME is traversed.
87
88 More than one function can be associated with a given sequencer item.
89 They are called in the same order in which they were added.
90
91 Note that an item must be mentioned in at least one constraint in order to
92 be traversed by INVOKE-SEQUENCER-ITEMS. If there are no special ordering
93 requirments for a particular item, then the trivial constraint (NAME) will
94 suffice."))
95
96(defgeneric invoke-sequencer-items (sequencer &rest arguments)
97 (:documentation
98 "Invoke functions attached to the SEQUENCER's items in the right order.
99
100 Each function is invoked in turn with the list of ARGUMENTS. The return
101 values of the functions are discarded."))
102
103(defmethod ensure-sequencer-item ((sequencer sequencer) name)
104 (with-slots (table) sequencer
105 (or (gethash name table)
106 (setf (gethash name table)
107 (make-instance 'sequencer-item :name name)))))
108
109(defmethod add-sequencer-constraint ((sequencer sequencer) (constraint list))
110 (let ((converted-constraint (mapcar (lambda (name)
111 (ensure-sequencer-item sequencer
112 name))
113 constraint)))
114 (with-slots (constraints) sequencer
115 (pushnew converted-constraint constraints :test #'equal))))
116
117(defmethod add-sequencer-item-function ((sequencer sequencer) name function)
118 (let ((item (ensure-sequencer-item sequencer name)))
119 (pushnew function (sequencer-item-functions item))))
120
121(defmethod invoke-sequencer-items ((sequencer sequencer) &rest arguments)
122 (dolist (item (merge-lists (reverse (sequencer-constraints sequencer))))
123 (dolist (function (reverse (sequencer-item-functions item)))
124 (apply function arguments))))
125
126;;;--------------------------------------------------------------------------
127;;; Output preparation.
128
129(defgeneric add-output-hooks (object reason sequencer)
130 (:documentation
131 "Announces the intention to write SEQUENCER, with a particular REASON.
132
133 The SEQUENCER is an SEQUENCER instance; the REASON will be a symbol which
134 can be matched using an EQL-specializer. In response, OBJECT should add
135 any constrains and item functions that it wishes, and pass the
136 announcement to its sub-objects.")
137 (:method-combination progn)
138 (:method progn (object reason sequencer)
139 nil))
140
141(defvar *seen-announcement*) ;Keep me unbound!
142#+hmm
143(defmethod add-output-hooks :around (object reason sequencer &rest stuff)
144 "Arrange not to invoke any object more than once during a particular
145 announcement."
146 (declare (ignore stuff))
147 (cond ((not (boundp '*seen-announcement*))
148 (let ((*seen-announcement* (make-hash-table)))
149 (setf (gethash object *seen-announcement*) t)
150 (call-next-method)))
151 ((gethash object *seen-announcement*)
152 nil)
153 (t
154 (setf (gethash object *seen-announcement*) t)
155 (call-next-method))))
156
157;;;--------------------------------------------------------------------------
abdf50aa
MW
158;;; Utilities.
159
160(defun banner (title output &key (blank-line-p t))
161 (format output "~&~%/*----- ~A ~A*/~%"
162 title
163 (make-string (- 77 2 5 1 (length title) 1 2)
164 :initial-element #\-))
165 (when blank-line-p
166 (terpri output)))
167
168;;;--------------------------------------------------------------------------
169;;; Header output.
170
171(defun write-module-header (module)
172 (let* ((file (merge-pathnames (make-pathname :type "H" :case :common)
173 (module-name module)))
174 (fakename (make-pathname :name (pathname-name file)
175 :type (pathname-type file))))
176 (with-open-file (uoutput file
177 :direction :output
178 :if-exists :supersede
179 :if-does-not-exist :create)
180 (let ((output (make-instance 'position-aware-output-stream
181 :stream uoutput
182 :file fakename)))
183
184 ;; Format the header and guards.
185 (format output "~
186/* -*-c-*-
187 *
188 * Header file generated by SOD for ~A
189 */
190
191#ifndef ~A
192#define ~:*~A
193
194#ifdef __cplusplus
195 extern \"C\" {
196#endif~%"
197 (namestring (module-name module))
198 (or (getf (module-plist module) 'include-guard)
199 (with-output-to-string (guard)
200 (let ((name (namestring file))
201 (uscore t))
202 (dotimes (i (length name))
203 (let ((ch (char name i)))
204 (cond ((alphanumericp ch)
205 (write-char (char-upcase ch) guard)
206 (setf uscore nil))
207 ((not uscore)
208 (write-char #\_ guard)
209 (setf uscore t)))))))))
210
211 ;; Forward declarations of all the structures and types. Nothing
212 ;; interesting gets said here; this is just so that the user code
213 ;; can talk meainingfully about the things we're meant to be
214 ;; defining here.
215 ;;
216 ;; FIXME
217
218 ;; The user fragments.
219 (when (module-header-fragments module)
220 (banner "User code" output)
221 (dolist (frag (module-header-fragments module))
1f1d88f5 222 (princ frag output)))
abdf50aa
MW
223
224 ;; The definitions of the necessary structures.
225 ;;
226 ;; FIXME
227
228 ;; The definitions of the necessary direct-methods.
229 ;;
230 ;; FIXME
231
232 ;; The trailer section.
233 (banner "That's all, folks" output)
234 (format output "~
235#ifdef __cplusplus
236 }
237#endif
238
239#endif~%")))))
240
241;;;--------------------------------------------------------------------------
242;;; Source output.
243
244(defun write-module-source (module)
245 (let* ((file (merge-pathnames (make-pathname :type "C" :case :common)
246 (module-name module)))
247 (fakename (make-pathname :name (pathname-name file)
248 :type (pathname-type file))))
249 (with-open-file (uoutput file
250 :direction :output
251 :if-exists :supersede
252 :if-does-not-exist :create)
253 (let ((output (make-instance 'position-aware-output-stream
254 :stream uoutput
255 :file fakename)))
256
257 ;; Format the header.
258 (format output "~
259/* -*-c-*-
260 *
261 * Source file generated by SOD for ~A
262 */~%"
263 (namestring (module-name module)))
264
265 ;; The user fragments.
266 (when (module-source-fragments module)
267 (banner "User code" output)
268 (dolist (frag (module-source-fragments module))
1f1d88f5 269 (princ frag output)))
abdf50aa
MW
270
271 ;; The definitions of the necessary tables.
272 ;;
273 ;; FIXME
274
275 ;; The definitions of the necessary effective-methods.
276 ;;
277 ;; FIXME
278
279 ;; The trailer section.
280 (banner "That's all, folks" output :blank-line-p nil)))))
281
282;;;----- That's all, folks --------------------------------------------------