lib/keyword.c (kw_parseempty): Use correct variable scanning `kwval' list.
[sod] / src / c-types-proto.lisp
CommitLineData
dea4d055
MW
1;;; -*-lisp-*-
2;;;
3;;; Protocol for C type representation
4;;;
5;;; (c) 2009 Straylight/Edgeware
6;;;
7
8;;;----- Licensing notice ---------------------------------------------------
9;;;
e0808c47 10;;; This file is part of the Sensible Object Design, an object system for C.
dea4d055
MW
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;;; Root classes and common access protocol.
30
31;; It seems more useful to put the root class here, so that we can provide
32;; methods specialized on it, e.g., PRINT-OBJECT.
33
34(export 'c-type)
35(defclass c-type ()
36 ()
37 (:documentation
38 "Base class for C type objects."))
39
40(export '(qualifiable-c-type c-type-qualifiers))
41(defclass qualifiable-c-type (c-type)
42 ((qualifiers :initarg :qualifiers :initform nil
43 :type list :reader c-type-qualifiers))
44 (:documentation
45 "Base class for C types which can be qualified."))
46
47(export 'canonify-qualifiers)
48(defun canonify-qualifiers (qualifiers)
2c615337
MW
49 "Return a canonical list of qualifiers.
50
51 Duplicates and `nil' entries are deleted, and the remaining entries are
52 sorted."
53 (sort (delete-duplicates (delete nil (copy-list qualifiers))) #'string<))
dea4d055 54
bf090e02
MW
55(export 'qualify-c-type)
56(defgeneric qualify-c-type (type qualifiers)
57 (:documentation
58 "Return a type like TYPE but with the specified QUALIFIERS.
59
60 The qualifiers of the returned type are the union of the requested
61 QUALIFIERS and the qualifiers already applied to TYPE."))
62
ff4e398b
MW
63(export 'c-qualifier-keyword)
64(defgeneric c-qualifier-keyword (qualifier)
65 (:documentation "Return the C keyword for the QUALIFIER (a Lisp keyword).")
66 (:method ((qualifier symbol)) (string-downcase qualifier)))
67
68(export 'c-type-qualifier-keywords)
69(defun c-type-qualifier-keywords (c-type)
70 "Return the type's qualifiers, as a list of C keyword names."
71 (mapcar #'c-qualifier-keyword (c-type-qualifiers c-type)))
72
dea4d055
MW
73(export 'c-type-subtype)
74(defgeneric c-type-subtype (type)
75 (:documentation
76 "For compound types, return the base type."))
77
78;;;--------------------------------------------------------------------------
79;;; Comparison protocol.
80
81(export 'c-type-equal-p)
82(defgeneric c-type-equal-p (type-a type-b)
83 (:method-combination and)
84 (:documentation
85 "Answers whether two types TYPE-A and TYPE-B are structurally equal.
86
87 Here, `structurally equal' means that they have the same qualifiers,
88 similarly spelt names, and structurally equal components.")
89 (:method and (type-a type-b)
90 (eql (class-of type-a) (class-of type-b))))
91
92(defmethod c-type-equal-p and ((type-a qualifiable-c-type)
93 (type-b qualifiable-c-type))
94 (equal (canonify-qualifiers (c-type-qualifiers type-a))
95 (canonify-qualifiers (c-type-qualifiers type-b))))
96
97;;;--------------------------------------------------------------------------
98;;; C syntax output protocol.
99
100(export 'pprint-c-type)
101(defgeneric pprint-c-type (type stream kernel)
102 (:documentation
103 "Pretty-printer for C types.
104
105 Print TYPE to STREAM. In the middle of the declarator, call the function
106 KERNEL with one argument: whether it needs a leading space.")
107 (:method :around (type stream kernel)
108 (typecase kernel
109 (null (pprint-c-type type stream
110 (lambda (stream prio spacep)
111 (declare (ignore stream prio spacep))
112 nil)))
113 ((or function symbol) (call-next-method))
114 (t (pprint-c-type type stream
115 (lambda (stream prio spacep)
116 (declare (ignore prio))
117 (when spacep
118 (c-type-space stream))
119 (princ kernel stream)))))))
120
121(export 'c-type-space)
122(defun c-type-space (stream)
123 "Print a space and a miser-mode newline to STREAM.
124
3109662a
MW
125 This is the right function to call in a `pprint-c-type' kernel function
126 when the SPACEP argument is true."
dea4d055
MW
127 (pprint-indent :block 2 stream)
128 (write-char #\space stream)
129 (pprint-newline :miser stream))
130
131(defun maybe-in-parens* (stream condition thunk)
3109662a 132 "Helper function for the `maybe-in-parens' macro."
dea4d055
MW
133 (multiple-value-bind (prefix suffix)
134 (if condition (values "(" ")") (values "" ""))
135 (pprint-logical-block (stream nil :prefix prefix :suffix suffix)
136 (funcall thunk stream))))
137
138(export 'maybe-in-parens)
139(defmacro maybe-in-parens ((stream condition) &body body)
140 "Evaluate BODY; if CONDITION, write parens to STREAM around it.
141
3109662a
MW
142 This macro is useful for implementing the `pprint-c-type' method on
143 compound types. The BODY is evaluated in the context of a logical block
144 printing to STREAM. If CONDITION is non-nil, then the block will have
145 open/close parens as its prefix and suffix; otherwise they will be empty.
dea4d055 146
3109662a 147 The STREAM is passed to `pprint-logical-block', so it must be a symbol."
dea4d055
MW
148 `(maybe-in-parens* ,stream ,condition (lambda (,stream) ,@body)))
149
150(export 'format-qualifiers)
151(defun format-qualifiers (quals)
152 "Return a string listing QUALS, with a space after each."
153 (format nil "~{~(~A~) ~}" quals))
154
155;;;--------------------------------------------------------------------------
156;;; S-expression notation protocol.
157
158(export 'print-c-type)
159(defgeneric print-c-type (stream type &optional colon atsign)
160 (:documentation
161 "Print an abbreviated syntax for TYPE to the STREAM.
162
3109662a 163 This function is suitable for use in `format's ~/.../ command."))
dea4d055 164
93348ae9 165(export '(expand-c-type-spec expand-c-type-form))
dea4d055
MW
166(eval-when (:compile-toplevel :load-toplevel :execute)
167 (defgeneric expand-c-type-spec (spec)
8d3d1674 168 (:documentation "Expand SPEC into Lisp code to construct a C type.")
dea4d055
MW
169 (:method ((spec list))
170 (expand-c-type-form (car spec) (cdr spec))))
171 (defgeneric expand-c-type-form (head tail)
8d3d1674 172 (:documentation "Expand a C type list beginning with HEAD.")
dea4d055
MW
173 (:method ((name (eql 'lisp)) tail)
174 `(progn ,@tail))))
175
176(export 'c-type)
177(defmacro c-type (spec)
3109662a 178 "Expands to code to construct a C type, using `expand-c-type-spec'."
dea4d055
MW
179 (expand-c-type-spec spec))
180
684d95c7
MW
181(defmethod documentation ((symbol symbol) (doc-type (eql 'c-type)))
182 (let ((method (find-eql-specialized-method #'expand-c-type-spec 0 symbol)))
183 (and method (documentation method t))))
184(defmethod (setf documentation)
185 (string (symbol symbol) (doc-type (eql 'c-type)))
186 (let ((method (find-eql-specialized-method #'expand-c-type-spec 0 symbol)))
187 (unless method (error "No C type spec found with name `~S'." symbol))
188 (setf (documentation method t) string)))
189
dea4d055 190(export 'define-c-type-syntax)
8d3d1674 191(defmacro define-c-type-syntax (name bvl &body body)
dea4d055
MW
192 "Define a C-type syntax function.
193
194 A function defined by BODY and with lambda-list BVL is associated with the
8d3d1674
MW
195 NAME. When `expand-c-type-spec' sees a list (NAME . STUFF), it will call
196 this function with the argument list STUFF."
dea4d055
MW
197 (with-gensyms (head tail)
198 (multiple-value-bind (doc decls body) (parse-body body)
199 `(eval-when (:compile-toplevel :load-toplevel :execute)
200 (defmethod expand-c-type-form ((,head (eql ',name)) ,tail)
201 ,@doc
202 (destructuring-bind ,bvl ,tail
203 ,@decls
fc09e191 204 (block ,name ,@body)))
dea4d055
MW
205 ',name))))
206
684d95c7
MW
207(export 'c-type-form)
208(defmethod documentation ((symbol symbol) (doc-type (eql 'c-type-form)))
209 (let ((method (find-eql-specialized-method #'expand-c-type-form 0 symbol)))
210 (and method (documentation method t))))
211(defmethod (setf documentation)
212 (string (symbol symbol) (doc-type (eql 'c-type-form)))
213 (let ((method (find-eql-specialized-method #'expand-c-type-form 0 symbol)))
214 (unless method (error "No C type spec found with name `~S'." symbol))
215 (setf (documentation method t) string)))
216
dea4d055
MW
217(export 'c-type-alias)
218(defmacro c-type-alias (original &rest aliases)
219 "Make ALIASES behave the same way as the ORIGINAL type."
220 (with-gensyms (head tail)
221 `(eval-when (:compile-toplevel :load-toplevel :execute)
222 ,@(mapcar (lambda (alias)
223 `(defmethod expand-c-type-form
224 ((,head (eql ',alias)) ,tail)
684d95c7 225 ,(format nil "Alias for `~(~S~)'." original)
dea4d055
MW
226 (expand-c-type-form ',original ,tail)))
227 aliases)
228 ',aliases)))
229
230(export 'defctype)
e43d3532 231(defmacro defctype (names value &key export)
dea4d055
MW
232 "Define NAMES all to describe the C-type VALUE.
233
234 NAMES can be a symbol (treated as a singleton list), or a list of symbols.
8d3d1674
MW
235 The VALUE is a C type S-expression, acceptable to `expand-c-type-spec'.
236 It will be expanded once at run-time."
dea4d055 237 (let* ((names (if (listp names) names (list names)))
684d95c7
MW
238 (namevar (gensym "NAME-"))
239 (avar (gensym "A"))
240 (tvar (gensym "T"))
241 (svar (gensym "S"))
dea4d055
MW
242 (typevar (symbolicate 'c-type- (car names))))
243 `(progn
e43d3532
MW
244 ,@(and export
245 `((export '(,typevar ,@names))))
dea4d055
MW
246 (defparameter ,typevar ,(expand-c-type-spec value))
247 (eval-when (:compile-toplevel :load-toplevel :execute)
248 ,@(mapcar (lambda (name)
249 `(defmethod expand-c-type-spec ((,namevar (eql ',name)))
250 ',typevar))
251 names))
684d95c7
MW
252 (dolist (,avar '(,@names))
253 (let ((,tvar (format nil "Return a C `~A' type."
254 (with-output-to-string (,svar)
255 (pprint-c-type ,typevar ,svar nil)))))
256 (setf (documentation ,avar 'c-type) ,tvar)))
dea4d055
MW
257 'names)))
258
259(export 'c-name-case)
260(defun c-name-case (name)
261 "Convert NAME to suitable case.
262
263 Strings are returned as-is; symbols are squashed to lower-case and hyphens
264 are replaced by underscores."
265 (typecase name
266 (symbol (with-output-to-string (out)
267 (loop for ch across (symbol-name name)
268 do (cond ((alpha-char-p ch)
269 (write-char (char-downcase ch) out))
270 ((or (digit-char-p ch)
271 (char= ch #\_))
272 (write-char ch out))
273 ((char= ch #\-)
274 (write-char #\_ out))
275 (t
a1985b3c 276 (error "Bad character in C name ~S" name))))))
dea4d055
MW
277 (t name)))
278
279;;;--------------------------------------------------------------------------
b7fcf941
MW
280;;; Storage specifier protocol.
281
282(export 'pprint-c-storage-specifier)
283(defgeneric pprint-c-storage-specifier (spec stream)
284 (:documentation "Print the storage specifier SPEC to STREAM, as C syntax.")
285 (:method ((spec symbol) stream) (princ (string-downcase spec) stream)))
286
287(export 'print-c-storage-specifier)
288(defgeneric print-c-storage-specifier (stream spec &optional colon atsign)
289 (:documentation
290 "Print the storage specifier SPEC to STREAM, as an S-expression.
291
292 This function is suitable for use in `format's ~/.../ command.")
293 (:method (stream (spec t) &optional colon atsign)
294 (declare (ignore colon atsign))
295 (prin1 spec stream))
296 (:method (stream (spec symbol) &optional colon atsign)
297 (declare (ignore colon atsign))
298 (princ (string-downcase spec) stream)))
299
300(export '(expand-c-storage-specifier expand-c-storage-specifier-form))
301(eval-when (:compile-toplevel :load-toplevel :execute)
302 (defgeneric expand-c-storage-specifier (spec)
303 (:documentation
304 "Expand SPEC into Lisp code to construct a storage specifier.")
305 (:method ((spec list))
306 (expand-c-storage-specifier-form (car spec) (cdr spec)))
307 (:method ((spec symbol))
308 (if (keywordp spec) spec
309 (expand-c-storage-specifier-form spec nil))))
310 (defgeneric expand-c-storage-specifier-form (head tail)
311 (:documentation
312 "Expand a C storage-specifier form beginning with HEAD.")
313 (:method ((name (eql 'lisp)) tail)
314 `(progn ,@tail))))
315
316(export 'define-c-storage-specifier-syntax)
317(defmacro define-c-storage-specifier-syntax (name bvl &body body)
318 "Define a C storage-specifier syntax function.
319
320 A function defined by BODY and with lambda-list BVL is associated wth the
321 NAME. When `expand-c-storage-specifier' sees a list (NAME . STUFF), it
322 will call this function with the argument list STUFF."
323 (with-gensyms (head tail)
324 (multiple-value-bind (doc decls body) (parse-body body)
325 `(eval-when (:compile-toplevel :load-toplevel :execute)
326 (defmethod expand-c-storage-specifier-form
327 ((,head (eql ',name)) ,tail)
328 ,@doc
329 (destructuring-bind ,bvl ,tail
330 ,@decls
331 (block ,name ,@body)))
332 ',name))))
333
334;;;--------------------------------------------------------------------------
335;;; A type for carrying storage specifiers.
336
337(export '(c-storage-specifiers-type c-type-specifiers))
338(defclass c-storage-specifiers-type (c-type)
339 ((specifiers :initarg :specifiers :type list :reader c-type-specifiers)
340 (subtype :initarg :subtype :type c-type :reader c-type-subtype))
341 (:documentation
342 "A type for carrying storage specifiers.
343
344 Properly, storage specifiers should only appear on an outermost type.
345 This fake C type is a handy marker for the presence of storage specifiers,
346 so that they can be hoisted properly when constructing derived types."))
347
348(export 'wrap-c-type)
349(defun wrap-c-type (wrapper-func base-type)
350 "Handle storage specifiers correctly when making a derived type.
351
352 WRAPPER-FUNC should be a function which will return some derived type of
353 BASE-TYPE. This function differs from `funcall' only when BASE-TYPE is
354 actually a `c-storage-specifiers-type', in which case it invokes
355 WRAPPER-FUNC on the underlying type, and re-attaches the storage
356 specifiers to the derived type."
357 (if (typep base-type 'c-storage-specifiers-type)
358 (let* ((unwrapped-type (c-type-subtype base-type))
359 (wrapped-type (funcall wrapper-func unwrapped-type))
360 (specifiers (c-type-specifiers base-type)))
361 (make-or-intern-c-type 'c-storage-specifiers-type unwrapped-type
362 :specifiers specifiers
363 :subtype wrapped-type))
364 (funcall wrapper-func base-type)))
365
366;;;--------------------------------------------------------------------------
dea4d055
MW
367;;; Function arguments.
368
ced609b8
MW
369(export '(argument argumentp make-argument
370 argument-name argument-type argument-default))
371(defstruct (argument (:constructor make-argument (name type &optional default
4b8e5c03 372 &aux (%type type)))
dea4d055
MW
373 (:predicate argumentp))
374 "Simple structure representing a function argument."
1db50cbf 375 (name nil :type t :read-only t)
ced609b8
MW
376 (%type nil :type c-type :read-only t)
377 (default nil :type t :read-only t))
1db50cbf 378(define-access-wrapper argument-type argument-%type :read-only t)
dea4d055
MW
379
380(export 'commentify-argument-name)
381(defgeneric commentify-argument-name (name)
382 (:documentation
383 "Produce a `commentified' version of the argument.
384
385 The default behaviour is that temporary argument names are simply omitted
05b7480d 386 (nil is returned); otherwise, `/*...*/' markers are wrapped around the
dea4d055
MW
387 printable representation of the argument.")
388 (:method ((name null)) nil)
389 (:method ((name t)) (format nil "/*~A*/" name)))
390
391;;;--------------------------------------------------------------------------
392;;; Printing objects.
393
394(defmethod print-object ((object c-type) stream)
395 (if *print-escape*
396 (format stream "~:@<C-TYPE ~/sod:print-c-type/~:>" object)
397 (pprint-c-type object stream nil)))
398
399;;;----- That's all, folks --------------------------------------------------