3 ;;; Dealing with C types
5 ;;; (c) 2008 Straylight/Edgeware
8 ;;;----- Licensing notice ---------------------------------------------------
10 ;;; This file is part of the Simple Object Definition system.
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.
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.
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.
28 ;;;--------------------------------------------------------------------------
29 ;;; Plain old C types.
36 "Base class for C type objects."))
38 ;; Important protocol.
40 (defgeneric c-type-subtype (type)
42 "For compound types, return the base type."))
44 (defgeneric c-type-equal-p (type-a type-b)
45 (:method-combination and)
47 "Answers whether two types TYPE-A and TYPE-B are, in fact, equal.")
48 (:method and (type-a type-b)
49 (eql (class-of type-a) (class-of type-b))))
51 (defgeneric pprint-c-type (type stream kernel)
53 "Pretty-printer for C types.
55 Print TYPE to STREAM. In the middle of the declarator, call the function
56 KERNEL with one argument: whether it needs a leading space.")
57 (:method :around (type stream kernel)
59 (function (call-next-method))
60 (null (pprint-c-type type stream
61 (lambda (stream prio spacep)
62 (declare (ignore stream prio spacep))
64 (t (pprint-c-type type stream
65 (lambda (stream prio spacep)
66 (declare (ignore prio))
68 (c-type-space stream))
69 (princ kernel stream)))))))
71 (defgeneric print-c-type (stream type &optional colon atsign)
73 "Print an abbreviated syntax for TYPE to the STREAM."))
75 (defmethod print-object ((object c-type) stream)
77 (format stream "~:@<C-TYPE ~/sod::print-c-type/~:>" object)
78 (pprint-c-type object stream nil)))
80 ;; Utility functions and macros.
82 (defun c-type-space (stream)
83 "Print a space and a miser-mode newline to STREAM.
85 This is the right function to call in a PPRINT-C-TYPE kernel function when
86 the SPACEP argument is true."
87 (pprint-indent :block 2 stream)
88 (write-char #\space stream)
89 (pprint-newline :miser stream))
91 (defun maybe-in-parens* (stream condition thunk)
92 "Helper function for the MAYBE-IN-PARENS macro."
95 :prefix (if condition "(" "")
96 :suffix (if condition ")" ""))
97 (funcall thunk stream)))
99 (defmacro maybe-in-parens ((stream condition) &body body)
100 "Evaluate BODY; if CONDITION, write parens to STREAM around it.
102 This macro is useful for implementing the PPRINT-C-TYPE method on compound
103 types. The BODY is evaluated in the context of a logical block printing
104 to STREAM. If CONDITION is non-nil, then the block will have open/close
105 parens as its prefix and suffix; otherwise they will be empty.
107 The STREAM is passed to PPRINT-LOGICAL-BLOCK, so it must be a symbol."
108 `(maybe-in-parens* ,stream ,condition (lambda (,stream) ,@body)))
110 ;; S-expression syntax machinery.
112 (defun c-name-case (name)
113 "Convert NAME to suitable case.
115 Strings are returned as-is; symbols are squashed to lower-case and hyphens
116 are replaced by underscores."
118 (symbol (with-output-to-string (out)
119 (loop for ch across (symbol-name name)
120 do (cond ((alpha-char-p ch)
121 (write-char (char-downcase ch) out))
122 ((or (digit-char-p ch)
126 (write-char #\_ out))
128 (error "Bad character in C name ~S." name))))))
131 (eval-when (:compile-toplevel :load-toplevel :execute)
132 (defgeneric expand-c-type-spec (spec)
134 "Expand SPEC into Lisp code to construct a C type.")
135 (:method ((spec list))
136 (expand-c-type-form (car spec) (cdr spec))))
137 (defgeneric expand-c-type-form (head tail)
139 "Expand a C type list beginning with HEAD.")
140 (:method ((name (eql 'lisp)) tail)
143 (defmacro c-type (spec)
144 "Expands to code to construct a C type, using EXPAND-C-TYPE-SPEC."
145 (expand-c-type-spec spec))
147 (defmacro define-c-type-syntax (name bvl &rest body)
148 "Define a C-type syntax function.
150 A function defined by BODY and with lambda-list BVL is associated with the
151 NAME. When EXPAND-C-TYPE sees a list (NAME . STUFF), it will call this
152 function with the argument list STUFF."
153 (let ((headvar (gensym "HEAD"))
154 (tailvar (gensym "TAIL")))
155 `(eval-when (:compile-toplevel :load-toplevel :execute)
156 (defmethod expand-c-type-form ((,headvar (eql ',name)) ,tailvar)
157 (destructuring-bind ,bvl ,tailvar
160 (defmacro c-type-alias (original &rest aliases)
161 "Make ALIASES behave the same way as the ORIGINAL type."
162 (let ((headvar (gensym "HEAD"))
163 (tailvar (gensym "TAIL")))
164 `(eval-when (:compile-toplevel :load-toplevel :execute)
165 ,@(mapcar (lambda (alias)
166 `(defmethod expand-c-type-form
167 ((,headvar (eql ',alias)) ,tailvar)
168 (expand-c-type-form ',original ,tailvar)))
171 (defmacro defctype (names value)
172 "Define NAMES all to describe the C-type VALUE.
174 NAMES can be a symbol (treated as a singleton list), or a list of symbols.
175 The VALUE is a C type S-expression, acceptable to EXPAND-C-TYPE. It will
176 be expanded once at run-time."
177 (let* ((names (if (listp names) names (list names)))
178 (namevar (gensym "NAME"))
179 (typevar (symbolicate 'c-type- (car names))))
181 (defparameter ,typevar ,(expand-c-type-spec value))
182 (eval-when (:compile-toplevel :load-toplevel :execute)
183 ,@(mapcar (lambda (name)
184 `(defmethod expand-c-type-spec ((,namevar (eql ',name)))
188 ;;;--------------------------------------------------------------------------
189 ;;; Types which can accept qualifiers.
191 ;; Basic definitions.
193 (defclass qualifiable-c-type (c-type)
194 ((qualifiers :initarg :qualifiers :initform nil
195 :type list :accessor c-type-qualifiers))
197 "Base class for C types which can be qualified."))
199 (defun format-qualifiers (quals)
200 "Return a string listing QUALS, with a space after each."
201 (format nil "~{~(~A~) ~}" quals))
203 (defmethod c-type-equal-p and ((type-a qualifiable-c-type)
204 (type-b qualifiable-c-type))
206 (sort (copy-list (c-type-qualifiers type)) #'string<)))
207 (equal (fix type-a) (fix type-b))))
211 (let ((cache (make-hash-table :test #'equal)))
212 (defun qualify-type (c-type qualifiers)
213 "Returns a qualified version of C-TYPE.
215 Maintains a cache of qualified types so that we don't have to run out of
216 memory. This can also speed up type comparisons."
217 (if (null qualifiers)
219 (let ((key (cons c-type qualifiers)))
220 (unless (typep c-type 'qualifiable-c-type)
221 (error "~A isn't qualifiable." (class-name (class-of c-type))))
222 (or (gethash key cache)
223 (setf (gethash key cache)
224 (copy-instance c-type :qualifiers qualifiers)))))))
226 ;;;--------------------------------------------------------------------------
227 ;;; Simple C types (e.g., built-in arithmetic types).
229 (defvar *simple-type-map* (make-hash-table :test #'equal)
230 "A hash table mapping type strings to Lisp symbols naming them.")
232 ;; Basic definitions.
234 (defclass simple-c-type (qualifiable-c-type)
235 ((name :initarg :name :type string :reader c-type-name))
237 "C types with simple forms."))
239 (let ((cache (make-hash-table :test #'equal)))
240 (defun make-simple-type (name &optional qualifiers)
241 "Make a distinguished object for the simple type called NAME."
242 (qualify-type (or (gethash name cache)
243 (setf (gethash name cache)
244 (make-instance 'simple-c-type :name name)))
247 (defmethod pprint-c-type ((type simple-c-type) stream kernel)
248 (pprint-logical-block (stream nil)
249 (format stream "~{~(~A~) ~@_~}~A"
250 (c-type-qualifiers type)
252 (funcall kernel stream 0 t)))
254 (defmethod c-type-equal-p and ((type-a simple-c-type)
255 (type-b simple-c-type))
256 (string= (c-type-name type-a) (c-type-name type-b)))
258 (defmethod print-c-type (stream (type simple-c-type) &optional colon atsign)
259 (declare (ignore colon atsign))
260 (let* ((name (c-type-name type))
261 (symbol (gethash name *simple-type-map*)))
262 (format stream "~:[~S~;~:@<~S~0@*~{ ~_~S~}~:>~]"
263 (c-type-qualifiers type) (or symbol name))))
265 ;; S-expression syntax.
267 (eval-when (:compile-toplevel :load-toplevel :execute)
268 (defmethod expand-c-type-spec ((spec string))
269 `(make-simple-type ,spec))
270 (defmethod expand-c-type-form ((head string) tail)
271 `(make-simple-type ,head ,@tail)))
273 (defmacro define-simple-c-type (names type)
274 "Define each of NAMES to be a simple type called TYPE."
275 (let ((names (if (listp names) names (list names))))
277 (setf (gethash ,type *simple-type-map*) ',(car names))
278 (defctype ,names ,type)
279 (define-c-type-syntax ,(car names) (&rest quals)
280 `(make-simple-type ,',type (list ,@quals))))))
282 (define-simple-c-type void "void")
284 (define-simple-c-type char "char")
285 (define-simple-c-type (unsigned-char uchar) "unsigned char")
286 (define-simple-c-type (signed-char schar) "signed char")
288 (define-simple-c-type (int signed signed-int sint) "int")
289 (define-simple-c-type (unsigned unsigned-int uint) "unsigned")
291 (define-simple-c-type (short signed-short short-int signed-short-int sshort)
293 (define-simple-c-type (unsigned-short unsigned-short-int ushort)
296 (define-simple-c-type (long signed-long long-int signed-long-int slong)
298 (define-simple-c-type (unsigned-long unsigned-long-int ulong)
301 (define-simple-c-type (long-long signed-long-long long-long-int
302 signed-long-long-int llong sllong)
304 (define-simple-c-type (unsigned-long-long unsigned-long-long-int ullong)
305 "unsigned long long")
307 (define-simple-c-type float "float")
308 (define-simple-c-type double "double")
309 (define-simple-c-type long-double "long double")
311 (define-simple-c-type va-list "va_list")
312 (define-simple-c-type size-t "size_t")
313 (define-simple-c-type ptrdiff-t "ptrdiff_t")
315 ;;;--------------------------------------------------------------------------
316 ;;; Tag types (structs, unions and enums).
320 (defclass tagged-c-type (qualifiable-c-type)
321 ((tag :initarg :tag :type string :reader c-type-tag))
323 "C types with tags."))
325 (defgeneric c-tagged-type-kind (type)
327 "Return the kind of tagged type that TYPE is, as a keyword."))
329 (macrolet ((define-tagged-type (kind what)
330 (let ((type (symbolicate 'c- kind '-type))
331 (constructor (symbolicate 'make- kind '-type)))
333 (defclass ,type (tagged-c-type) ()
334 (:documentation ,(format nil "C ~a types." what)))
335 (defmethod c-tagged-type-kind ((type ,type))
337 (let ((cache (make-hash-table :test #'equal)))
338 (defun ,constructor (tag &optional qualifiers)
339 (qualify-type (or (gethash tag cache)
340 (setf (gethash tag cache)
341 (make-instance ',type
344 (define-c-type-syntax ,kind (tag &rest quals)
345 ,(format nil "Construct ~A type named TAG" what)
346 `(,',constructor ,tag (list ,@quals)))))))
347 (define-tagged-type enum "enumerated")
348 (define-tagged-type struct "structure")
349 (define-tagged-type union "union"))
351 (defmethod pprint-c-type ((type tagged-c-type) stream kernel)
352 (pprint-logical-block (stream nil)
353 (format stream "~{~(~A~) ~@_~}~(~A~) ~A"
354 (c-type-qualifiers type)
355 (c-tagged-type-kind type)
357 (funcall kernel stream 0 t)))
359 (defmethod c-type-equal-p and ((type-a tagged-c-type)
360 (type-b tagged-c-type))
361 (string= (c-type-tag type-a) (c-type-tag type-b)))
363 (defmethod print-c-type (stream (type tagged-c-type) &optional colon atsign)
364 (declare (ignore colon atsign))
365 (format stream "~:@<~S ~@_~S~{ ~_~S~}~:>"
366 (c-tagged-type-kind type)
368 (c-type-qualifiers type)))
370 ;;;--------------------------------------------------------------------------
375 (defclass c-pointer-type (qualifiable-c-type)
376 ((subtype :initarg :subtype :type c-type :reader c-type-subtype))
380 (let ((cache (make-hash-table :test #'eql)))
381 (defun make-pointer-type (subtype &optional qualifiers)
382 "Return a (maybe distinguished) pointer type."
383 (qualify-type (or (gethash subtype cache)
384 (make-instance 'c-pointer-type :subtype subtype))
387 (defmethod pprint-c-type ((type c-pointer-type) stream kernel)
388 (pprint-c-type (c-type-subtype type) stream
389 (lambda (stream prio spacep)
390 (when spacep (c-type-space stream))
391 (maybe-in-parens (stream (> prio 1))
392 (format stream "*~{~(~A~)~^ ~@_~}"
393 (c-type-qualifiers type))
394 (funcall kernel stream 1 (c-type-qualifiers type))))))
396 (defmethod c-type-equal-p and ((type-a c-pointer-type)
397 (type-b c-pointer-type))
398 (c-type-equal-p (c-type-subtype type-a)
399 (c-type-subtype type-b)))
401 (defmethod print-c-type (stream (type c-pointer-type) &optional colon atsign)
402 (declare (ignore colon atsign))
403 (format stream "~:@<* ~@_~/sod::print-c-type/~{ ~_~S~}~:>"
404 (c-type-subtype type)
405 (c-type-qualifiers type)))
407 ;; S-expression syntax.
409 (define-c-type-syntax * (sub &rest quals)
410 "Return the type of pointer-to-SUB."
411 `(make-pointer-type ,(expand-c-type-spec sub) (list ,@quals)))
412 (c-type-alias * pointer ptr)
414 (defctype string (* char))
415 (defctype const-string (* (char :const)))
417 ;;;--------------------------------------------------------------------------
422 (defclass c-array-type (c-type)
423 ((subtype :initarg :subtype :type c-type :reader c-type-subtype)
424 (dimensions :initarg :dimensions :type list :reader c-array-dimensions))
428 (defun make-array-type (subtype dimensions)
429 "Return a new array of SUBTYPE with given DIMENSIONS."
430 (make-instance 'c-array-type :subtype subtype
431 :dimensions (or dimensions '(nil))))
433 (defmethod pprint-c-type ((type c-array-type) stream kernel)
434 (pprint-c-type (c-type-subtype type) stream
435 (lambda (stream prio spacep)
436 (maybe-in-parens (stream (> prio 2))
437 (funcall kernel stream 2 spacep)
438 (format stream "~@<~{[~@[~A~]]~^~_~}~:>"
439 (c-array-dimensions type))))))
441 (defmethod c-type-equal-p and ((type-a c-array-type)
442 (type-b c-array-type))
443 (and (c-type-equal-p (c-type-subtype type-a)
444 (c-type-subtype type-b))
445 (equal (c-array-dimensions type-a)
446 (c-array-dimensions type-b))))
448 (defmethod print-c-type (stream (type c-array-type) &optional colon atsign)
449 (declare (ignore colon atsign))
450 (format stream "~:@<[] ~@_~:I~/sod::print-c-type/~{ ~_~S~}~:>"
451 (c-type-subtype type)
452 (c-array-dimensions type)))
454 ;; S-expression syntax.
456 (define-c-type-syntax [] (sub &rest dims)
457 "Return the type of arrays of SUB with the dimensions DIMS.
459 If the DIMS are omitted, a single unknown-length dimension is added."
460 `(make-array-type ,(expand-c-type-spec sub)
461 (list ,@(or dims '(nil)))))
462 (c-type-alias [] array vec)
464 ;;;--------------------------------------------------------------------------
469 (defstruct (argument (:constructor make-argument (name type)) (:type list))
470 "Simple list structure representing a function argument."
474 (defun arguments-lists-equal-p (list-a list-b)
475 "Return whether LIST-A and LIST-B match.
477 They must have the same number of arguments, and each argument must have
478 the same type, or be :ELLIPSIS. The argument names are not inspected."
479 (and (= (length list-a) (length list-b))
480 (every (lambda (arg-a arg-b)
481 (if (eq arg-a :ellipsis)
483 (c-type-equal-p (argument-type arg-a)
484 (argument-type arg-b))))
487 (defgeneric commentify-argument-name (name)
489 "Produce a `commentified' version of the argument.
491 The default behaviour is that temporary argument names are simply omitted
492 (NIL is returned); otherwise, `/*...*/' markers are wrapped around the
493 printable representation of the argument.")
494 (:method ((name null)) nil)
495 (:method ((name t)) (format nil "/*~A*/" name)))
497 (defun commentify-argument-names (arguments)
498 "Return an argument list with the arguments commentified.
500 That is, with each argument name passed through COMMENTIFY-ARGUMENT-NAME."
501 (mapcar (lambda (arg)
502 (if (eq arg :ellipsis)
504 (make-argument (commentify-argument-name (argument-name arg))
505 (argument-type arg))))
508 (defun commentify-function-type (type)
509 "Return a type like TYPE, but with arguments commentified.
511 This doesn't recurse into the return type or argument types."
512 (make-function-type (c-type-subtype type)
513 (commentify-argument-names
514 (c-function-arguments type))))
518 (defclass c-function-type (c-type)
519 ((subtype :initarg :subtype :type c-type :reader c-type-subtype)
520 (arguments :initarg :arguments :type list :reader c-function-arguments))
522 "C function types. The subtype is the return type, as implied by the C
523 syntax for function declarations."))
525 (defun make-function-type (subtype arguments)
526 "Return a new function type, returning SUBTYPE and accepting ARGUMENTS."
527 (make-instance 'c-function-type :subtype subtype :arguments arguments))
529 (defmethod c-type-equal-p and ((type-a c-function-type)
530 (type-b c-function-type))
531 (and (c-type-equal-p (c-type-subtype type-a)
532 (c-type-subtype type-b))
533 (arguments-lists-equal-p (c-function-arguments type-a)
534 (c-function-arguments type-b))))
536 (defmethod print-c-type
537 (stream (type c-function-type) &optional colon atsign)
538 (declare (ignore colon atsign))
540 #.(concatenate 'string
542 "FUN ~@_~:I~/sod::print-c-type/"
543 "~{ ~_~:<~S ~@_~/sod::print-c-type/~:>~}"
545 (c-type-subtype type)
546 (c-function-arguments type)))
548 (defmethod pprint-c-type ((type c-function-type) stream kernel)
549 (pprint-c-type (c-type-subtype type) stream
550 (lambda (stream prio spacep)
551 (maybe-in-parens (stream (> prio 2))
552 (when spacep (c-type-space stream))
553 (funcall kernel stream 2 nil)
554 (pprint-indent :block 4 stream)
555 ;;(pprint-newline :miser stream)
556 (pprint-logical-block
557 (stream nil :prefix "(" :suffix ")")
559 (dolist (arg (c-function-arguments type))
562 (format stream ", ~_"))
563 (if (eq arg :ellipsis)
564 (write-string "..." stream)
565 (pprint-c-type (argument-type arg)
567 (argument-name arg))))))))))
569 ;; S-expression syntax.
571 (define-c-type-syntax fun (ret &rest args)
572 "Return the type of functions which returns RET and has arguments ARGS.
574 The ARGS are a list of arguments of the form (NAME TYPE). The NAME can be
575 NIL to indicate that no name was given.
577 If an entry isn't a list, it's assumed to be the start of a Lisp
578 expression to compute the tail of the list; similarly, if the list is
579 improper, then it's considered to be a complete expression. The upshot of
580 this apparently bizarre rule is that you can say
582 (c-type (fun int (\"foo\" int) . arg-tail))
584 where ARG-TAIL is (almost) any old Lisp expression and have it tack the
585 arguments onto the end. Of course, there don't have to be any explicit
586 arguments at all. The only restriction is that the head of the Lisp form
587 can't be a list -- so ((lambda (...) ...) ...) is out, but you probably
588 wouldn't type that anyway."
590 `(make-function-type ,(expand-c-type-spec ret)
591 ,(do ((args args (cdr args))
593 (cons `(make-argument ,(caar args)
597 ((or (atom args) (atom (car args)))
598 (cond ((and (null args) (null list)) `nil)
599 ((null args) `(list ,@(nreverse list)))
601 (t `(list* ,@(nreverse list) ,args)))))))
602 (c-type-alias fun function () func fn)
604 ;;;----- That's all, folks --------------------------------------------------