fix for property accessors generators; sort interface list in generated classes
[cl-gtk2.git] / glib / gobject.generating.lisp
1 (in-package :gobject)
2
3 (defvar *lisp-name-package* (find-package :gobject))
4 (defvar *strip-prefix* "")
5 (defvar *lisp-name-exceptions* nil)
6 (defvar *generation-exclusions* nil)
7 (defvar *known-interfaces* (make-hash-table :test 'equal))
8 (defvar *additional-properties* nil)
9
10 (defun name->supplied-p (name)
11   (intern (format nil "~A-SUPPLIED-P" (symbol-name name))
12           *lisp-name-package*))
13
14 (defstruct property name accessor-name readable writable)
15
16 (defstruct (gobject-property (:include property)) gname type)
17
18 (defstruct (cffi-property (:include property)) type reader writer)
19
20 (defmethod make-load-form ((object gobject-property) &optional env)
21   (declare (ignore env))
22   `(make-gobject-property :name ',(property-name object)
23                           :accessor-name ',(property-accessor-name object)
24                           :readable ',(property-readable object)
25                           :writable ',(property-writable object)
26                           :gname ',(gobject-property-gname object)
27                           :type ',(gobject-property-type object)))
28
29 (defmethod make-load-form ((object cffi-property) &optional env)
30   (declare (ignore env))
31   `(make-cffi-property :name ',(property-name object)
32                        :accessor-name ',(property-accessor-name object)
33                        :readable ',(property-readable object)
34                        :writable ',(property-writable object)
35                        :type ',(cffi-property-type object)
36                        :reader ',(cffi-property-reader object)
37                        :writer ',(cffi-property-writer object)))
38
39 (defun parse-accessor (spec)
40   (ecase (first spec)
41     (:cffi (destructuring-bind (&key reader writer) (rest spec)
42              (make-cffi-property-accessor :reader reader :writer writer)))
43     (:gobject (destructuring-bind (property-name) (rest spec)
44                 (make-gobject-property-accessor :property-name property-name)))))
45
46 (defun parse-gobject-property (spec)
47   (destructuring-bind (name accessor-name gname type readable writable) spec
48       (make-gobject-property :name name
49                              :accessor-name accessor-name
50                              :gname gname
51                              :type type
52                              :readable readable
53                              :writable writable)))
54
55 (defun parse-cffi-property (spec)
56   (destructuring-bind (name accessor-name type reader writer) spec
57     (make-cffi-property :name name
58                         :accessor-name accessor-name
59                         :type type
60                         :reader reader
61                         :writer writer
62                         :readable (not (null reader))
63                         :writable (not (null writer)))))
64
65 (defun parse-property (spec)
66   (cond
67     ((eq (first spec) :cffi) (parse-cffi-property (rest spec)))
68     (t (parse-gobject-property spec))))
69
70 (defun property->method-arg (property)
71   (when (or (gobject-property-p property)
72             (and (cffi-property-p property)
73                  (property-writable property)))
74     (let ((name (property-name property)))
75       `(,name nil ,(name->supplied-p name)))))
76
77 (defun gobject-property->arg-push (property)
78   (assert (typep property 'gobject-property))
79   (with-slots (name type gname) property
80     `(when ,(name->supplied-p name)
81        (push ,gname arg-names)
82        (push ,type arg-types)
83        (push ,name arg-values))))
84
85 (defun cffi-property->initarg (property)
86   (assert (typep property 'cffi-property))
87   (when (property-writable property)
88     (with-slots (accessor-name name type writer) property
89       `(when ,(name->supplied-p name)
90          (setf (,accessor-name object) ,name)))))
91
92 (defun accessor-name (class-name property-name)
93   (intern (format nil "~A-~A" (symbol-name class-name)
94                   (lispify-name property-name))
95           *lisp-name-package*))
96
97 (defgeneric property->reader (property))
98 (defgeneric property->writer (property))
99
100 (defmethod property->reader ((property gobject-property))
101   (with-slots (accessor-name type gname) property
102    `(defun ,accessor-name (object)
103       (g-object-call-get-property object ,gname ,type))))
104
105 (defmethod property->reader ((property cffi-property))
106   (with-slots (accessor-name type reader) property
107     (etypecase reader
108       (string `(defun ,accessor-name (object)
109                  (foreign-funcall ,reader g-object object ,type)))
110       (symbol `(defun ,accessor-name (object)
111                  (funcall ',reader object))))))
112
113 (defmethod property->writer ((property gobject-property))
114   (with-slots (accessor-name type gname) property
115     `(defun (setf ,accessor-name) (new-value object)
116        (g-object-call-set-property object ,gname new-value ,type)
117        new-value)))
118
119 (defmethod property->writer ((property cffi-property))
120   (with-slots (accessor-name type writer) property
121     (etypecase writer
122       (string `(defun (setf ,accessor-name) (new-value object)
123                  (foreign-funcall ,writer g-object object ,type new-value :void)
124                  new-value))
125       (symbol `(defun (setf ,accessor-name) (new-value object)
126                  (funcall ',writer object new-value)
127                  new-value)))))
128
129 (defun property->accessors (property export)
130   (append (when (property-readable property)
131             (list (property->reader property)))
132           (when (property-writable property)
133             (list (property->writer property)))
134           (when export
135             (list `(export ',(property-accessor-name property)
136                            (find-package ,(package-name (symbol-package (property-accessor-name property)))))))))
137
138 (defun interface->lisp-class-name (interface)
139   (etypecase interface
140     (symbol interface)
141     (string (or (gethash interface *known-interfaces*)
142                 (error "Unknown interface ~A" interface)))))
143
144 (defun type-initializer-call (type-initializer)
145   (etypecase type-initializer
146     (string `(foreign-funcall ,type-initializer g-type))
147     (symbol `(funcall ',type-initializer))))
148
149 (defmacro define-g-object-class (g-type-name name
150                                  (&key (superclass 'g-object)
151                                        (export t)
152                                        interfaces
153                                        type-initializer)
154                                  (&rest properties))
155   (setf properties (mapcar #'parse-property properties))
156   (let* ((superclass-properties (get superclass 'properties))
157          (combined-properties (append superclass-properties properties)))
158     `(progn
159        (defclass ,name (,superclass ,@(mapcar #'interface->lisp-class-name interfaces)) ())
160        (register-object-type ,g-type-name ',name)
161        ,@(when type-initializer
162                (list (type-initializer-call type-initializer)))
163        ,@(when export
164                (list `(export ',name (find-package ,(package-name (symbol-package name)))))) 
165        (defmethod initialize-instance :before 
166            ((object ,name) &key pointer
167             ,@(remove nil (mapcar #'property->method-arg
168                                   combined-properties)))
169          (unless (or pointer (and (slot-boundp object 'pointer)
170                                   (not (null-pointer-p (pointer object)))))
171            (let (arg-names arg-values arg-types)
172              ,@(mapcar #'gobject-property->arg-push (remove-if-not #'gobject-property-p combined-properties))
173              (setf (pointer object)
174                    (g-object-call-constructor ,g-type-name
175                                               arg-names
176                                               arg-values
177                                               arg-types)
178                    (g-object-has-reference object) t)
179              ,@(mapcar #'cffi-property->initarg (remove-if-not #'cffi-property-p combined-properties)))))
180        ,@(loop
181             for property in properties
182             append (property->accessors property export))
183        
184        (eval-when (:compile-toplevel :load-toplevel :execute)
185          (setf (get ',name 'superclass) ',superclass
186                (get ',name 'properties) ',combined-properties)))))
187
188 (defmacro define-g-interface (g-name name (&key (export t) type-initializer) &body properties)
189   (setf properties (mapcar #'parse-property properties))
190   `(progn
191      (defclass ,name () ())
192      ,@(when export
193              (list `(export ',name (find-package ,(package-name (symbol-package name))))))
194      ,@(when type-initializer
195              (list (type-initializer-call type-initializer)))
196      ,@(loop
197           for property in properties
198           append (property->accessors property export))
199      (eval-when (:compile-toplevel :load-toplevel :execute)
200        (setf (get ',name 'properties) ',properties)
201        (setf (gethash ,g-name *known-interfaces*) ',name))))
202
203 (defun starts-with (name prefix)
204   (and prefix (> (length name) (length prefix)) (string= (subseq name 0 (length prefix)) prefix)))
205
206 (defun strip-start (name prefix)
207   (if (starts-with name prefix)
208       (subseq name (length prefix))
209       name))
210
211 (defun lispify-name (name)
212   (with-output-to-string (stream)
213     (loop for c across (strip-start name *strip-prefix*)
214        for firstp = t then nil
215        do (when (and (not firstp) (upper-case-p c)) (write-char #\- stream))
216        do (write-char (char-upcase c) stream))))
217
218 (defun g-name->name (name)
219   (or (second (assoc name *lisp-name-exceptions* :test 'equal))
220       (intern (string-upcase (lispify-name name)) *lisp-name-package*)))
221
222 (defun property->property-definition (class-name property)
223   (let ((name (g-name->name (g-class-property-definition-name property)))
224         (accessor-name (accessor-name class-name (g-class-property-definition-name property)))
225         (g-name (g-class-property-definition-name property))
226         (type (g-type-name (g-class-property-definition-type property)))
227         (readable (g-class-property-definition-readable property))
228         (writable (and (g-class-property-definition-writable property)
229                        (not (g-class-property-definition-constructor-only property)))))
230     `(,name ,accessor-name ,g-name ,type ,readable ,writable)))
231
232 (defun probable-type-init-name (type-name)
233   (with-output-to-string (stream)
234     (iter (for c in-string type-name)
235           (for prev-c previous c)
236           (when (and (not (first-iteration-p))
237                      (upper-case-p c)
238                      (not (upper-case-p prev-c))
239                      (not (char= prev-c #\_)))
240             (write-char #\_ stream))
241           (write-char (char-downcase c) stream))
242     (write-string "_get_type" stream)))
243
244 (defun get-g-class-definition (type)
245   (let* ((g-type (ensure-g-type type))
246          (g-name (g-type-name g-type))
247          (name (g-name->name g-name))
248          (superclass-g-type (g-type-parent g-type))
249          (superclass-name (g-name->name (g-type-name superclass-g-type)))
250          (interfaces (g-type-interfaces g-type))
251          (properties (class-properties g-type))
252          (type-init-name (probable-type-init-name g-name))
253          (own-properties
254           (remove-if-not (lambda (property)
255                            (= g-type
256                               (g-class-property-definition-owner-type property)))
257                          properties)))
258     `(define-g-object-class ,g-name ,name 
259          (:superclass ,superclass-name
260                       :export t
261                       :interfaces (,@(sort (mapcar #'g-type-name interfaces) 'string<))
262                       ,@(when (and (foreign-symbol-pointer type-init-name)
263                                    (not (null-pointer-p (foreign-symbol-pointer type-init-name))))
264                               `(:type-initializer ,type-init-name)))
265        (,@(mapcar (lambda (property)
266                     (property->property-definition name property))
267                   own-properties)
268           ,@(cdr (find g-name *additional-properties* :key 'car :test 'string=))))))
269
270 (defun get-g-interface-definition (interface)
271   (let* ((type (ensure-g-type interface))
272          (g-name (g-type-name type))
273          (name (g-name->name g-name))
274          (properties (interface-properties type))
275          (probable-type-initializer (probable-type-init-name g-name)))
276     `(define-g-interface ,g-name ,name
277          (:export t
278                   ,@(when (foreign-symbol-pointer probable-type-initializer)
279                           `(:type-initializer ,probable-type-initializer)))
280        ,@(mapcar (lambda (property)
281                    (property->property-definition name property))
282                  properties))))
283
284 (defun get-g-class-definitions-for-root-1 (type)
285   (unless (member (ensure-g-type type) *generation-exclusions* :test '=)
286     (cons (get-g-class-definition type)
287           (reduce #'append
288                   (mapcar #'get-g-class-definitions-for-root-1
289                           (g-type-children type))))))
290
291 (defun get-g-class-definitions-for-root (type)
292   (setf type (ensure-g-type type))
293   (get-g-class-definitions-for-root-1 type))
294
295 (defvar *referenced-types*)
296
297 (defun class-or-interface-properties (type)
298   (setf type (ensure-g-type type))
299   (cond 
300     ((= (g-type-fundamental type) +g-type-object+) (class-properties type))
301     ((= (g-type-fundamental type) +g-type-interface+) (interface-properties type))))
302
303 (defun get-shallow-referenced-types (type)
304   (setf type (ensure-g-type type))
305   (remove-duplicates (sort (loop
306                               for property in (class-or-interface-properties type)
307                               when (= type (g-class-property-definition-owner-type property))
308                               collect (g-class-property-definition-type property))
309                            #'<)
310                      :test 'equal))
311
312 (defun get-referenced-types-1 (type)
313   (setf type (ensure-g-type type))
314   (loop
315      for property-type in (get-shallow-referenced-types type)
316      do (pushnew property-type *referenced-types* :test '=))
317   (loop
318      for type in (g-type-children type)
319      do (get-referenced-types-1 type)))
320
321 (defun get-referenced-types (root-type)
322   (let (*referenced-types*)
323     (get-referenced-types-1 (ensure-g-type root-type))
324     *referenced-types*))
325
326 (defun filter-types-by-prefix (types prefix)
327   (remove-if-not
328    (lambda (type)
329      (starts-with (g-type-name (ensure-g-type type)) prefix))
330    types))
331
332 (defun filter-types-by-fund-type (types fund-type)
333   (setf fund-type (ensure-g-type fund-type))
334   (remove-if-not
335    (lambda (type)
336      (equal (g-type-fundamental (ensure-g-type type)) fund-type))
337    types))
338
339 (defmacro define-g-enum (g-name name (&key (export t) type-initializer)  &body values)
340   `(progn
341      (defcenum ,name ,@values)
342      (register-enum-type ,g-name ',name)
343      ,@(when export
344              (list `(export ',name (find-package ,(package-name (symbol-package name))))))
345      ,@(when type-initializer
346              (list (type-initializer-call type-initializer)))))
347
348 (defun enum-value->definition (enum-value)
349   (let ((value-name (intern (lispify-name (enum-item-nick enum-value))
350                             (find-package :keyword)))
351         (numeric-value (enum-item-value enum-value)))
352     `(,value-name ,numeric-value)))
353
354 (defun get-g-enum-definition (type)
355   (let* ((g-type (ensure-g-type type))
356          (g-name (g-type-name g-type))
357          (name (g-name->name g-name))
358          (items (get-enum-items g-type))
359          (probable-type-initializer (probable-type-init-name g-name)))
360     `(define-g-enum ,g-name ,name
361          (:export t
362                   ,@(when (foreign-symbol-pointer probable-type-initializer)
363                           (list :type-initializer
364                                 probable-type-initializer)))
365        ,@(mapcar #'enum-value->definition items))))
366
367 (defmacro define-g-flags (g-name name (&key (export t) type-initializer) &body values)
368   `(progn
369      (defbitfield ,name ,@values)
370      (register-enum-type ,g-name ',name)
371      ,@(when export
372              (list `(export ',name (find-package ,(package-name (symbol-package name))))))
373      ,@(when type-initializer
374              (list (type-initializer-call type-initializer)))))
375
376 (defun flags-value->definition (flags-value)
377   (let ((value-name (intern (lispify-name (flags-item-nick flags-value))
378                             (find-package :keyword)))
379         (numeric-value (flags-item-value flags-value)))
380     `(,value-name ,numeric-value)))
381
382 (defun get-g-flags-definition (type)
383   (let* ((g-type (ensure-g-type type))
384          (g-name (g-type-name g-type))
385          (name (g-name->name g-name))
386          (items (get-flags-items g-type))
387          (probable-type-initializer (probable-type-init-name g-name)))
388     `(define-g-flags ,g-name ,name
389          (:export t
390                   ,@(when (foreign-symbol-pointer probable-type-initializer)
391                           (list :type-initializer
392                                 probable-type-initializer)))
393        ,@(mapcar #'flags-value->definition items))))
394
395 (defun generate-types-hierarchy-to-file (file root-type &key include-referenced prefix package exceptions prologue interfaces enums flags objects exclusions additional-properties)
396   (if (not (streamp file))
397       (with-open-file (stream file :direction :output :if-exists :supersede)
398         (generate-types-hierarchy-to-file stream root-type
399                                           :prefix prefix
400                                           :package package
401                                           :exceptions exceptions
402                                           :prologue prologue
403                                           :include-referenced include-referenced
404                                           :interfaces interfaces
405                                           :enums enums
406                                           :flags flags
407                                           :objects objects
408                                           :exclusions exclusions
409                                           :additional-properties additional-properties))
410       (let* ((*generation-exclusions* (mapcar #'ensure-g-type exclusions))
411              (*lisp-name-package* (or package *package*))
412              (*package* *lisp-name-package*)
413              (*strip-prefix* (or prefix ""))
414              (*lisp-name-exceptions* exceptions)
415              (*print-case* :downcase)
416              (*additional-properties* additional-properties)
417              (referenced-types (and include-referenced
418                                     (filter-types-by-prefix
419                                      (get-referenced-types root-type)
420                                      prefix))))
421         (setf exclusions (mapcar #'ensure-g-type exclusions))
422         (when prologue
423           (write-string prologue file)
424           (terpri file))
425         (when include-referenced
426           (loop
427              for interface in interfaces
428              do (loop
429                    for referenced-type in (get-shallow-referenced-types interface)
430                    do (pushnew referenced-type referenced-types :test 'equal)))
431           (loop
432              for object in objects
433              do (loop
434                    for referenced-type in (get-shallow-referenced-types object)
435                    do (pushnew referenced-type referenced-types :test 'equal)))
436           (loop
437              for enum-type in (filter-types-by-fund-type
438                                referenced-types "GEnum")
439              for def = (get-g-enum-definition enum-type)
440              unless (member (ensure-g-type enum-type) exclusions :test '=)
441              do (format file "~S~%~%" def))
442             
443           (loop
444              for flags-type in (filter-types-by-fund-type
445                                 referenced-types "GFlags")
446              for def = (get-g-flags-definition flags-type)
447              unless (member (ensure-g-type flags-type) exclusions :test '=)
448              do (format file "~S~%~%" def)))
449         (loop
450            with auto-enums = (and include-referenced
451                                   (filter-types-by-fund-type
452                                    referenced-types "GEnum"))
453            for enum in enums
454            for def = (get-g-enum-definition enum)
455            unless (find (ensure-g-type enum) auto-enums :test 'equal)
456            do (format file "~S~%~%" def))
457         (loop
458            with auto-flags = (and include-referenced
459                                   (filter-types-by-fund-type
460                                    referenced-types "GFlags"))
461            for flags-type in flags
462            for def = (get-g-flags-definition flags-type)
463            unless (find (ensure-g-type flags-type) auto-flags :test 'equal)
464            do (format file "~S~%~%" def))
465         (loop
466            for interface in interfaces
467            for def = (get-g-interface-definition interface)
468            do (format file "~S~%~%" def))
469         (loop
470            for def in (get-g-class-definitions-for-root root-type)
471            do (format file "~S~%~%" def))
472         (loop
473            for object in objects
474            for def = (get-g-class-definition object)
475            do (format file "~S~%~%" def)))))