root/trunk/thirdparty/defclass-star/defclass-star.lisp

Revision 2190, 10.1 KB (checked in by hhubner, 3 years ago)

add more thirdparty libs

  • Property svn:executable set to *
Line 
1;; -*- mode: Lisp; Syntax: Common-Lisp; -*-
2;;;
3;;; Copyright (c) 2006 by the authors.
4;;;
5;;; See LICENCE for details.
6
7(in-package :defclass-star)
8
9(enable-sharp-boolean-syntax)
10
11;; set up swank:*readtable-alist*
12#+#.(cl:when (cl:find-package "SWANK") '(:and))
13(unless (assoc "DEFCLASS-STAR" swank:*readtable-alist* :test #'string=)
14  (let ((*readtable* (copy-readtable)))
15    (enable-sharp-boolean-syntax)
16    (push (cons "DEFCLASS-STAR" *readtable*) swank:*readtable-alist*)
17    (push (cons "DEFCLASS-STAR.TEST" *readtable*) swank:*readtable-alist*)))
18
19(defmacro make-name-transformer (&rest elements)
20  `(lambda (name definition)
21    (declare (ignorable definition))
22    (concatenate-symbol ,@(mapcar (lambda (el)
23                                    (if (and (symbolp el)
24                                             (string= (symbol-name el) "NAME"))
25                                        'name
26                                        el))
27                                  elements))))
28
29;; more or less public vars (it's discouraged to set them globally)
30(defvar *accessor-name-package* nil
31  "A package, or :slot-name means the home-package of the slot-name symbol and nil means *package*")
32(defvar *accessor-name-transformer* 'default-accessor-name-transformer)
33(defvar *automatic-accessors-p* #t)
34
35;; these control whether the respective names should be exported from *package* (which is samples at macroexpan time)
36(defvar *export-class-name-p* nil)
37(defvar *export-accessor-names-p* nil)
38(defvar *export-slot-names-p* nil)
39
40(defvar *initarg-name-transformer* 'default-initarg-name-transformer)
41(defvar *automatic-initargs-p* #t)
42
43(defvar *slot-definition-transformer* 'default-slot-definition-transformer)
44
45(defun default-slot-definition-transformer (slot-def)
46  "Converts illegal (list foo) :type declarations into simple list declarations."
47  (let ((name (pop slot-def))
48        (type (getf slot-def :type)))
49    (when (and type (listp type) (eq (first type) 'list))
50      (setf (getf slot-def :type) 'list))
51    (push name slot-def)
52    slot-def))
53
54(defvar *allowed-slot-definition-properties* '(:documentation :type :reader :writer :allocation)
55  "Holds a list of keywords that are allowed in slot definitions (:accessor and :initarg are implicitly included).")
56
57;; expand-time temporary dynamic vars
58(defvar *accessor-names*)
59(defvar *slot-names*)
60
61(define-condition defclass-star-style-warning (simple-condition style-warning)
62  ())
63
64(defun style-warn (datum &rest args)
65  (warn 'defclass-star-style-warning :format-control datum :format-arguments args))
66
67(defun default-accessor-name-transformer (name definition)
68  (let ((type (getf definition :type))
69        (package (if (packagep *accessor-name-package*)
70                     *accessor-name-package*
71                     (case *accessor-name-package*
72                       (:slot-name (symbol-package name))
73                       (:default *package*)
74                       (t *package*)))))
75    (if (eq type 'boolean)
76        (let* ((name-string (string name))
77               (last-char (aref name-string (1- (length name-string)))))
78          (cond ((char-equal last-char #\p)
79                 name)
80                ;; i like unconditional -p postfix. ymmv.
81                #+nil((not (find #\- name-string))
82                 (concatenate-symbol name "P" package))
83                (t (concatenate-symbol name "-P" package))))
84        (concatenate-symbol name "-OF" package))))
85
86(defun default-initarg-name-transformer (name definition)
87  (declare (ignorable definition))
88  (concatenate-symbol name #.(symbol-package :asdf)))
89
90(defun process-slot-definition (definition)
91  (unless (consp definition)
92    (setf definition (list definition)))
93  (let ((name (pop definition))
94        (initform 'missing)
95        (entire-definition definition))
96    (push name *slot-names*)
97    (when (oddp (length definition))
98      (setf initform (pop definition))
99      (setf entire-definition definition)
100      (when (eq initform :unbound)
101        (setf initform 'missing)))
102    (assert (eq (getf definition :initform 'missing) 'missing) ()
103            ":initform is not allowed by the defclass-star syntax, the initform is taken from the first element of odd length slot definitions.")
104    (assert (every #'keywordp (loop for el :in definition :by #'cddr
105                                    collect el))
106            () "Found non-keywords in ~S" definition)
107    (destructuring-bind (&key (accessor 'missing) (initarg 'missing)
108                              (reader 'missing) (writer 'missing)
109                              &allow-other-keys)
110        definition
111      (remf-keywords definition :accessor :reader :writer :initform :initarg)
112      (let ((unknown-keywords (loop for el :in definition :by #'cddr
113                                    unless (or (member t *allowed-slot-definition-properties*)
114                                               (member el *allowed-slot-definition-properties*))
115                                    collect el)))
116        (when unknown-keywords
117          (style-warn "Unexpected properties in slot definition ~S.~%~
118                       The unexpected properties are ~S.~%~
119                       To avoid this warning (pushnew (or T :your-custom-keyword) defclass-star:*allowed-slot-definition-properties*)"
120                      entire-definition unknown-keywords))
121        (flet ((provided-p (value)
122                 (and value
123                      (not (eq value 'missing)))))
124          (prog1
125              (funcall *slot-definition-transformer*
126                       (append (list name)
127                               (unless (eq initform 'missing)
128                                 (list :initform initform))
129                               (if (and (eq accessor 'missing)
130                                        (eq reader 'missing)
131                                        (eq writer 'missing))
132                                   (when *automatic-accessors-p*
133                                     (setf accessor (funcall *accessor-name-transformer* name entire-definition))
134                                     (list :accessor accessor))
135                                   (append (when (provided-p accessor)
136                                             (list :accessor accessor))
137                                           (when (provided-p reader)
138                                             (list :reader reader))
139                                           (when (provided-p writer)
140                                             (list :writer writer))))
141                               (if (eq initarg 'missing)
142                                   (when *automatic-initargs-p*
143                                     (list :initarg (funcall *initarg-name-transformer* name entire-definition)))
144                                   (when initarg
145                                     (list :initarg initarg)))
146                               definition))
147            (when (provided-p accessor)
148              (pushnew accessor *accessor-names*))
149            (when (provided-p reader)
150              (pushnew reader *accessor-names*))
151            (when (provided-p writer)
152              (pushnew (second writer) *accessor-names*))))))))
153
154(defun extract-options-into-bindings (options)
155  (let ((binding-names)
156        (binding-values)
157        (clean-options))
158    (macrolet ((rebinding-table (&rest args)
159                 `(case (car option)
160                   ,@(loop for (arg-name var-name) :on args :by #'cddr
161                           collect `(,arg-name
162                                     (assert (= (length option) 2))
163                                     (push ',var-name binding-names)
164                                     (push (second option) binding-values)))
165                   (t (push option clean-options)))))
166      (dolist (option options)
167        (rebinding-table
168         :accessor-name-package *accessor-name-package*
169         :accessor-name-transformer *accessor-name-transformer*
170         :automatic-accessors-p *automatic-accessors-p*
171         :initarg-name-transformer *initarg-name-transformer*
172         :automatic-initargs-p *automatic-initargs-p*
173         :export-class-name-p *export-class-name-p*
174         :export-accessor-names-p *export-accessor-names-p*
175         :export-slot-names-p *export-slot-names-p*
176         :slot-definition-transformer *slot-definition-transformer*)))
177    (values binding-names binding-values (nreverse clean-options))))
178
179(defmacro def-star-macro (macro-name expand-to-name)
180  `(defmacro ,macro-name (name direct-superclasses direct-slots &rest options)
181    (unless (eq (symbol-package name) *package*)
182      (style-warn "defclass* for ~A while its home package is not *package* (~A)"
183                  (let ((*package* (find-package "KEYWORD")))
184                    (format nil "~S" name)) *package*))
185    (let ((*accessor-names* nil)
186          (*slot-names* nil))
187      (multiple-value-bind (binding-names binding-values clean-options)
188          (extract-options-into-bindings options)
189        (progv binding-names (mapcar #'eval binding-values)
190          (let ((result `(,',expand-to-name ,name
191                          ,direct-superclasses
192                          ,(mapcar 'process-slot-definition direct-slots)
193                          ,@clean-options)))
194            (if (or *export-class-name-p*
195                    *export-accessor-names-p*
196                    *export-slot-names-p*)
197                `(progn
198                  ,result
199                  (eval-when (:compile-toplevel :load-toplevel :execute)
200                    (export (list ,@(mapcar (lambda (el)
201                                              (list 'quote el))
202                                            (append (when *export-class-name-p*
203                                                      (list name))
204                                                    (when *export-accessor-names-p*
205                                                      (nreverse *accessor-names*))
206                                                    (when *export-slot-names-p*
207                                                      (nreverse *slot-names*)))))
208                            ,*package*))
209                  (find-class ',name nil))
210                result)))))))
211
212(def-star-macro defclass* defclass)
213(def-star-macro defcondition* define-condition)
Note: See TracBrowser for help on using the browser.