More WIP.
[sod] / src / class-output.lisp
1 ;;; -*-lisp-*-
2 ;;;
3 ;;; Output for classes
4 ;;;
5 ;;; (c) 2009 Straylight/Edgeware
6 ;;;
7
8 ;;;----- Licensing notice ---------------------------------------------------
9 ;;;
10 ;;; This file is part of the Sensble Object Design, an object system for C.
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 ;;;--------------------------------------------------------------------------
29 ;;; Classes.
30
31 (defmethod hook-output progn ((class sod-class) (reason (eql :h)) sequencer)
32
33 ;; Main output sequencing.
34 (sequence-output (stream sequencer)
35
36 :constraint
37 ((:classes :start)
38 (class :banner)
39 (class :islots :start) (class :islots :slots) (class :islots :end)
40 (class :vtmsgs :start) (class :vtmsgs :end)
41 (class :vtables :start) (class :vtables :end)
42 (class :vtable-externs) (class :vtable-externs-after)
43 (class :methods :start) (class :methods) (class :methods :end)
44 (class :ichains :start) (class :ichains :end)
45 (class :ilayout :start) (class :ilayout :slots) (class :ilayout :end)
46 (class :conversions)
47 (class :object)
48 (:classes :end))
49
50 (:typedefs
51 (format stream "typedef struct ~A ~A;~%"
52 (ichain-struct-tag class (sod-class-chain-head class)) class))
53
54 ((class :banner)
55 (banner (format nil "Class ~A" class) stream))
56 ((class :vtable-externs-after)
57 (terpri stream))
58
59 ((class :vtable-externs)
60 (format stream "/* Vtable structures. */~%"))
61
62 ((class :object)
63 (let ((metaclass (sod-class-metaclass class))
64 (metaroot (find-root-metaclass class)))
65 (format stream "/* The class object. */~@
66 extern const struct ~A ~A__classobj;~@
67 #define ~:*~A__class (&~:*~A__classobj.~A.~A)~2%"
68 (ilayout-struct-tag metaclass) class
69 (sod-class-nickname (sod-class-chain-head metaroot))
70 (sod-class-nickname metaroot)))))
71
72 ;; Maybe generate an islots structure.
73 (when (sod-class-slots class)
74 (dolist (slot (sod-class-slots class))
75 (hook-output slot 'islots sequencer))
76 (sequence-output (stream sequencer)
77 ((class :islots :start)
78 (format stream "/* Instance slots. */~@
79 struct ~A {~%"
80 (islots-struct-tag class)))
81 ((class :islots :end)
82 (format stream "};~2%"))))
83
84 ;; Declare the direct methods.
85 (when (sod-class-methods class)
86 (sequence-output (stream sequencer)
87 ((class :methods :start)
88 (format stream "/* Direct methods. */~%"))
89 ((class :methods :end)
90 (terpri stream))))
91
92 ;; Provide upcast macros which do the right thing.
93 (when (sod-class-direct-superclasses class)
94 (sequence-output (stream sequencer)
95 ((class :conversions)
96 (let ((chain-head (sod-class-chain-head class)))
97 (format stream "/* Conversion macros. */~%")
98 (dolist (super (cdr (sod-class-precedence-list class)))
99 (let ((super-head (sod-class-chain-head super)))
100 (format stream "#define ~:@(~A__CONV_~A~)(p) ((~A *)~
101 ~:[SOD_XCHAIN(~A, (p))~;(p)~])~%"
102 class (sod-class-nickname super) super
103 (eq chain-head super-head)
104 (sod-class-nickname super-head))))
105 (terpri stream)))))
106
107 ;; Generate vtmsgs structure for all superclasses.
108 (hook-output (car (sod-class-vtables class))
109 'vtmsgs
110 sequencer))
111
112 (defmethod hook-output progn ((class sod-class) reason sequencer)
113 (with-slots (ilayout vtables methods effective-methods) class
114 (hook-output ilayout reason sequencer)
115 (dolist (method methods) (hook-output method reason sequencer))
116 (dolist (method effective-methods)
117 (hook-output method reason sequencer))
118 (dolist (vtable vtables) (hook-output vtable reason sequencer))))
119
120 ;;;--------------------------------------------------------------------------
121 ;;; Instance structure.
122
123 (defmethod hook-output progn ((slot sod-slot)
124 (reason (eql 'islots))
125 sequencer)
126 (sequence-output (stream sequencer)
127 (((sod-slot-class slot) :islots :slots)
128 (pprint-logical-block (stream nil :prefix " " :suffix ";")
129 (pprint-c-type (sod-slot-type slot) stream (sod-slot-name slot)))
130 (terpri stream))))
131
132 (defmethod hook-output progn ((ilayout ilayout) reason sequencer)
133 (with-slots (ichains) ilayout
134 (dolist (ichain ichains) (hook-output ichain reason sequencer))))
135
136 (defmethod hook-output progn ((ichain ichain) reason sequencer)
137 (dolist (item (ichain-body ichain))
138 (hook-output item reason sequencer)))
139
140 (defmethod hook-output progn ((ilayout ilayout) (reason (eql :h)) sequencer)
141 (with-slots (class ichains) ilayout
142 (sequence-output (stream sequencer)
143 ((class :ilayout :start)
144 (format stream "/* Instance layout. */~@
145 struct ~A {~%"
146 (ilayout-struct-tag class)))
147 ((class :ilayout :end)
148 (format stream "};~2%")))
149 (dolist (ichain ichains)
150 (hook-output ichain 'ilayout sequencer))))
151
152 (defmethod hook-output progn ((ichain ichain) (reason (eql :h)) sequencer)
153 (with-slots (class chain-head chain-tail) ichain
154 (when (eq class chain-tail)
155 (sequence-output (stream sequencer)
156 :constraint ((class :ichains :start)
157 (class :ichain chain-head :start)
158 (class :ichain chain-head :slots)
159 (class :ichain chain-head :end)
160 (class :ichains :end))
161 ((class :ichain chain-head :start)
162 (format stream "/* Instance chain structure. */~@
163 struct ~A {~%"
164 (ichain-struct-tag chain-tail chain-head)))
165 ((class :ichain chain-head :end)
166 (format stream "};~2%")
167 (format stream "/* Union of equivalent superclass chains. */~@
168 union ~A {~@
169 ~:{ struct ~A ~A;~%~}~
170 };~2%"
171 (ichain-union-tag chain-tail chain-head)
172
173 ;; Make sure the most specific class is first: only the
174 ;; first element of a union can be statically initialized in
175 ;; C90.
176 (mapcar (lambda (super)
177 (list (ichain-struct-tag super chain-head)
178 (sod-class-nickname super)))
179 (sod-class-chain chain-tail))))))))
180
181 (defmethod hook-output progn ((ichain ichain)
182 (reason (eql 'ilayout))
183 sequencer)
184 (with-slots (class chain-head chain-tail) ichain
185 (sequence-output (stream sequencer)
186 ((class :ilayout :slots)
187 (format stream " union ~A ~A;~%"
188 (ichain-union-tag chain-tail chain-head)
189 (sod-class-nickname chain-head))))))
190
191 (defmethod hook-output progn ((vtptr vtable-pointer)
192 (reason (eql :h))
193 sequencer)
194 (with-slots (class chain-head chain-tail) vtptr
195 (sequence-output (stream sequencer)
196 ((class :ichain chain-head :slots)
197 (format stream " const struct ~A *_vt;~%"
198 (vtable-struct-tag chain-tail chain-head))))))
199
200 (defmethod hook-output progn ((islots islots) reason sequencer)
201 (dolist (slot (islots-slots islots))
202 (hook-output slot reason sequencer)))
203
204 (defmethod hook-output progn ((islots islots) (reason (eql :h)) sequencer)
205 (with-slots (class subclass slots) islots
206 (sequence-output (stream sequencer)
207 ((subclass :ichain (sod-class-chain-head class) :slots)
208 (format stream " struct ~A ~A;~%"
209 (islots-struct-tag class)
210 (sod-class-nickname class))))))
211
212 ;;;--------------------------------------------------------------------------
213 ;;; Vtable structure.
214
215 (defmethod hook-output progn ((vtable vtable) reason sequencer)
216 (with-slots (body) vtable
217 (dolist (item body) (hook-output item reason sequencer))))
218
219 (defmethod hook-output progn ((method sod-method)
220 (reason (eql :h))
221 sequencer)
222 (with-slots (class) method
223 (sequence-output (stream sequencer)
224 ((class :methods)
225 (let ((type (sod-method-function-type method)))
226 (princ "extern " stream)
227 (pprint-c-type (commentify-function-type type) stream
228 (sod-method-function-name method))
229 (format stream ";~%"))))))
230
231 (defmethod hook-output progn ((vtable vtable) (reason (eql :h)) sequencer)
232 (with-slots (class chain-head chain-tail) vtable
233 (when (eq class chain-tail)
234 (sequence-output (stream sequencer)
235 :constraint ((class :vtables :start)
236 (class :vtable chain-head :start)
237 (class :vtable chain-head :slots)
238 (class :vtable chain-head :end)
239 (class :vtables :end))
240 ((class :vtable chain-head :start)
241 (format stream "/* Vtable structure. */~@
242 struct ~A {~%"
243 (vtable-struct-tag chain-tail chain-head)))
244 ((class :vtable chain-head :end)
245 (format stream "};~2%"))))
246 (sequence-output (stream sequencer)
247 ((class :vtable-externs)
248 (format stream "~@<extern struct ~A ~2I~_~A__vtable_~A;~:>~%"
249 (vtable-struct-tag chain-tail chain-head)
250 class (sod-class-nickname chain-head))))))
251
252 (defmethod hook-output progn ((vtmsgs vtmsgs) (reason (eql :h)) sequencer)
253 (with-slots (class subclass chain-head chain-tail) vtmsgs
254 (sequence-output (stream sequencer)
255 ((subclass :vtable chain-head :slots)
256 (format stream " struct ~A ~A;~%"
257 (vtmsgs-struct-tag subclass class)
258 (sod-class-nickname class))))))
259
260 (defmethod hook-output progn ((vtmsgs vtmsgs)
261 (reason (eql 'vtmsgs))
262 sequencer)
263 (when (vtmsgs-entries vtmsgs)
264 (with-slots (class subclass) vtmsgs
265 (sequence-output (stream sequencer)
266 :constraint ((subclass :vtmsgs :start)
267 (subclass :vtmsgs class :start)
268 (subclass :vtmsgs class :slots)
269 (subclass :vtmsgs class :end)
270 (subclass :vtmsgs :end))
271 ((subclass :vtmsgs class :start)
272 (format stream "/* Messages protocol from class ~A */~@
273 struct ~A {~%"
274 class
275 (vtmsgs-struct-tag subclass class)))
276 ((subclass :vtmsgs class :end)
277 (format stream "};~2%"))))))
278
279 (defmethod hook-output progn ((vtmsgs vtmsgs) reason sequencer)
280 (with-slots (entries) vtmsgs
281 (dolist (entry entries) (hook-output entry reason sequencer))))
282
283 (defmethod hook-output progn ((entry method-entry) reason sequencer)
284 (with-slots (method) entry
285 (hook-output method reason sequencer)))
286
287 (defmethod hook-output progn ((entry method-entry)
288 (reason (eql 'vtmsgs))
289 sequencer)
290 (let* ((method (method-entry-effective-method entry))
291 (message (effective-method-message method))
292 (class (effective-method-class method))
293 (type (method-entry-function-type entry))
294 (commented-type (commentify-function-type type)))
295 (sequence-output (stream sequencer)
296 ((class :vtmsgs (sod-message-class message) :slots)
297 (pprint-logical-block (stream nil :prefix " " :suffix ";")
298 (pprint-c-type commented-type stream (sod-message-name message)))
299 (terpri stream)))))
300
301 (defmethod hook-output progn ((cptr class-pointer)
302 (reason (eql :h))
303 sequencer)
304 (with-slots (class chain-head metaclass meta-chain-head) cptr
305 (sequence-output (stream sequencer)
306 ((class :vtable chain-head :slots)
307 (format stream " const ~A *~:[_class~;~:*_cls_~A~];~%"
308 metaclass
309 (if (sod-class-direct-superclasses meta-chain-head)
310 (sod-class-nickname meta-chain-head)
311 nil))))))
312
313 (defmethod hook-output progn ((boff base-offset) (reason (eql :h)) sequencer)
314 (with-slots (class chain-head) boff
315 (sequence-output (stream sequencer)
316 ((class :vtable chain-head :slots)
317 (write-line " size_t _base;" stream)))))
318
319 (defmethod hook-output progn ((choff chain-offset)
320 (reason (eql :h))
321 sequencer)
322 (with-slots (class chain-head target-head) choff
323 (sequence-output (stream sequencer)
324 ((class :vtable chain-head :slots)
325 (format stream " ptrdiff_t _off_~A;~%"
326 (sod-class-nickname target-head))))))
327
328 ;;;--------------------------------------------------------------------------
329 ;;; Implementation output.
330
331 (defvar *instance-class*)
332
333 (defmethod hook-output progn ((class sod-class) (reason (eql :c)) sequencer)
334 (sequence-output (stream sequencer)
335
336 :constraint
337 ((:classes :start)
338 (class :banner)
339 (class :direct-methods :start) (class :direct-methods :end)
340 (class :effective-methods)
341 (class :vtables :start) (class :vtables :end)
342 (class :object :prepare) (class :object :start) (class :object :end)
343 (:classes :end))
344
345 ((class :banner)
346 (banner (format nil "Class ~A" class) stream))
347
348 ((class :object :start)
349 (format stream "~
350 /* The class object. */
351 const struct ~A ~A__classobj = {~%"
352 (ilayout-struct-tag (sod-class-metaclass class))
353 class))
354 ((class :object :end)
355 (format stream "};~2%")))
356
357 (let ((*instance-class* class))
358 (hook-output (sod-class-ilayout (sod-class-metaclass class))
359 'class
360 sequencer)))
361
362 ;;;--------------------------------------------------------------------------
363 ;;; Direct methods.
364
365 (defmethod hook-output progn ((method delegating-direct-method)
366 (reason (eql :c))
367 sequencer)
368 (with-slots (class body) method
369 (unless body
370 (return-from hook-output))
371 (sequence-output (stream sequencer)
372 ((class :direct-method method :start)
373 (format stream "#define CALL_NEXT_METHOD (next_method(~{~A~^, ~}))~%"
374 (mapcar #'argument-name
375 (c-function-arguments (sod-method-next-method-type
376 method)))))
377 ((class :direct-method method :end)
378 (format stream "#undef CALL_NEXT_METHOD~%")))))
379
380 (defmethod hook-output progn ((method sod-method)
381 (reason (eql :c))
382 sequencer)
383 (with-slots (class body) method
384 (unless body
385 (return-from hook-output))
386 (sequence-output (stream sequencer)
387 :constraint ((class :direct-methods :start)
388 (class :direct-method method :start)
389 (class :direct-method method :body)
390 (class :direct-method method :end)
391 (class :direct-methods :end))
392 ((class :direct-method method :body)
393 (pprint-c-type (sod-method-function-type method)
394 stream
395 (sod-method-function-name method))
396 (format stream "~&{~%")
397 (write body :stream stream :pretty nil :escape nil)
398 (format stream "~&}~%"))
399 ((class :direct-method method :end)
400 (terpri stream)))))
401
402 (defmethod hook-output progn ((method basic-effective-method)
403 (reason (eql :c))
404 sequencer)
405 (with-slots (class functions) method
406 (sequence-output (stream sequencer)
407 ((class :effective-methods)
408 (dolist (func functions)
409 (write func :stream stream :escape nil :circle nil))))))
410
411 ;;;--------------------------------------------------------------------------
412 ;;; Vtables.
413
414 (defmethod hook-output progn ((vtable vtable) (reason (eql :c)) sequencer)
415 (with-slots (class chain-head chain-tail) vtable
416 (sequence-output (stream sequencer)
417 :constraint ((class :vtables :start)
418 (class :vtable chain-head :start)
419 (class :vtable chain-head :end)
420 (class :vtables :end))
421 ((class :vtable chain-head :start)
422 (format stream "/* Vtable for ~A chain. */~@
423 static const struct ~A ~A = {~%"
424 chain-head
425 (vtable-struct-tag chain-tail chain-head)
426 (vtable-name chain-tail chain-head)))
427 ((class :vtable chain-head :end)
428 (format stream "};~2%")))))
429
430 (defmethod hook-output progn ((cptr class-pointer)
431 (reason (eql :c))
432 sequencer)
433 (with-slots (class chain-head metaclass meta-chain-head) cptr
434 (sequence-output (stream sequencer)
435 :constraint ((class :vtable chain-head :start)
436 (class :vtable chain-head :class-pointer metaclass)
437 (class :vtable chain-head :end))
438 ((class :vtable chain-head :class-pointer metaclass)
439 (format stream " &~A__classobj.~A.~A,~%"
440 (sod-class-metaclass class)
441 (sod-class-nickname meta-chain-head)
442 (sod-class-nickname metaclass))))))
443
444 (defmethod hook-output progn ((boff base-offset) (reason (eql :c)) sequencer)
445 (with-slots (class chain-head) boff
446 (sequence-output (stream sequencer)
447 :constraint ((class :vtable chain-head :start)
448 (class :vtable chain-head :base-offset)
449 (class :vtable chain-head :end))
450 ((class :vtable chain-head :base-offset)
451 (format stream " offsetof(struct ~A, ~A),~%"
452 (ilayout-struct-tag class)
453 (sod-class-nickname chain-head))))))
454
455 (defmethod hook-output progn ((choff chain-offset)
456 (reason (eql :c))
457 sequencer)
458 (with-slots (class chain-head target-head) choff
459 (sequence-output (stream sequencer)
460 :constraint ((class :vtable chain-head :start)
461 (class :vtable chain-head :chain-offset target-head)
462 (class :vtable chain-head :end))
463 ((class :vtable chain-head :chain-offset target-head)
464 (format stream " SOD_OFFSETDIFF(struct ~A, ~A, ~A),~%"
465 (ilayout-struct-tag class)
466 (sod-class-nickname chain-head)
467 (sod-class-nickname target-head))))))
468
469 (defmethod hook-output progn ((vtmsgs vtmsgs) (reason (eql :c)) sequencer)
470 (with-slots (class subclass chain-head) vtmsgs
471 (sequence-output (stream sequencer)
472 :constraint ((subclass :vtable chain-head :start)
473 (subclass :vtable chain-head :vtmsgs class :start)
474 (subclass :vtable chain-head :vtmsgs class :slots)
475 (subclass :vtable chain-head :vtmsgs class :end)
476 (subclass :vtable chain-head :end))
477 ((subclass :vtable chain-head :vtmsgs class :start)
478 (format stream " { /* Method entries for ~A messages. */~%"
479 class))
480 ((subclass :vtable chain-head :vtmsgs class :end)
481 (format stream " },~%")))))
482
483 (defmethod hook-output progn ((entry method-entry)
484 (reason (eql :c))
485 sequencer)
486 (with-slots (method chain-head chain-tail) entry
487 (let* ((message (effective-method-message method))
488 (class (effective-method-class method))
489 (super (sod-message-class message)))
490 (sequence-output (stream sequencer)
491 ((class :vtable chain-head :vtmsgs super :slots)
492 (format stream " ~A,~%"
493 (method-entry-function-name method chain-head)))))))
494
495 ;;;--------------------------------------------------------------------------
496 ;;; Filling in the class object.
497
498 (defmethod hook-output progn ((ichain ichain)
499 (reason (eql 'class))
500 sequencer)
501 (with-slots (class chain-head) ichain
502 (sequence-output (stream sequencer)
503 :constraint ((*instance-class* :object :start)
504 (*instance-class* :object chain-head :ichain :start)
505 (*instance-class* :object chain-head :ichain :end)
506 (*instance-class* :object :end))
507 ((*instance-class* :object chain-head :ichain :start)
508 (format stream " { { /* ~A ichain */~%"
509 (sod-class-nickname chain-head)))
510 ((*instance-class* :object chain-head :ichain :end)
511 (format stream " } },~%")))))
512
513 (defmethod hook-output progn ((islots islots)
514 (reason (eql 'class))
515 sequencer)
516 (with-slots (class) islots
517 (let ((chain-head (sod-class-chain-head class)))
518 (sequence-output (stream sequencer)
519 :constraint ((*instance-class* :object chain-head :ichain :start)
520 (*instance-class* :object class :slots :start)
521 (*instance-class* :object class :slots)
522 (*instance-class* :object class :slots :end)
523 (*instance-class* :object chain-head :ichain :end))
524 ((*instance-class* :object class :slots :start)
525 (format stream " { /* Class ~A */~%" class))
526 ((*instance-class* :object class :slots :end)
527 (format stream " },~%"))))))
528
529 (defmethod hook-output progn ((vtptr vtable-pointer)
530 (reason (eql 'class))
531 sequencer)
532 (with-slots (class chain-head chain-tail) vtptr
533 (sequence-output (stream sequencer)
534 :constraint ((*instance-class* :object chain-head :ichain :start)
535 (*instance-class* :object chain-head :vtable)
536 (*instance-class* :object chain-head :ichain :end))
537 ((*instance-class* :object chain-head :vtable)
538 (format stream " &~A__vtable_~A,~%"
539 class (sod-class-nickname chain-head))))))
540
541 (defgeneric find-class-initializer (slot class)
542 (:method ((slot effective-slot) (class sod-class))
543 (let ((dslot (effective-slot-direct-slot slot)))
544 (or (some (lambda (super)
545 (find dslot (sod-class-class-initializers super)
546 :test #'sod-initializer-slot))
547 (sod-class-precedence-list class))
548 (effective-slot-initializer slot)))))
549
550 (defgeneric output-class-initializer (slot instance stream)
551 (:method ((slot sod-class-effective-slot) (instance sod-class) stream)
552 (let ((func (effective-slot-initializer-function slot)))
553 (if func
554 (format stream " ~A,~%" (funcall func instance))
555 (call-next-method))))
556 (:method ((slot effective-slot) (instance sod-class) stream)
557 (let ((init (find-class-initializer slot instance)))
558 (ecase (sod-initializer-value-kind init)
559 (:simple (format stream " ~A,~%"
560 (sod-initializer-value-form init)))
561 (:compound (format stream " ~@<{ ~;~A~; },~:>~%"
562 (sod-initializer-value-form init)))))))
563
564 (defmethod hook-output progn ((slot sod-class-effective-slot)
565 (reason (eql 'class))
566 sequencer)
567 (let ((instance *instance-class*)
568 (func (effective-slot-prepare-function slot)))
569 (when func
570 (sequence-output (stream sequencer)
571 ((instance :object :prepare)
572 (funcall func instance stream))))))
573
574 (defmethod hook-output progn ((slot effective-slot)
575 (reason (eql 'class))
576 sequencer)
577 (with-slots (class (dslot slot)) slot
578 (let ((instance *instance-class*)
579 (super (sod-slot-class dslot)))
580 (sequence-output (stream sequencer)
581 ((instance :object super :slots)
582 (output-class-initializer slot instance stream))))))
583
584 ;;;----- That's all, folks --------------------------------------------------