Very ragged work-in-progress.
[sod] / c-types.lisp
index acf2db8..fe56ecd 100644 (file)
 
 ;; Important protocol.
 
-(defgeneric c-declaration (type decl)
-  (:documentation
-   "Computes a declaration for a C type.
-
-   Returns two strings, a type and a declarator, suitable for declaring an
-   object with the inner declarator DECL."))
-
 (defgeneric c-type-subtype (type)
   (:documentation
    "For compound types, return the base type."))
   (:method and (type-a type-b)
     (eql (class-of type-a) (class-of type-b))))
 
-(defgeneric c-declarator-priority (type)
+(defgeneric pprint-c-type (type stream kernel)
   (:documentation
-   "Returns the priority for the declarator of TYPE.
-
-   Used to decide when to insert parentheses into the C representation.")
-
-  (:method ((type c-type))
-    0))
+   "Pretty-printer for C types.
+
+   Print TYPE to STREAM.  In the middle of the declarator, call the function
+   KERNEL with one argument: whether it needs a leading space.")
+  (:method :around (type stream kernel)
+    (typecase kernel
+      (function (call-next-method))
+      (null (pprint-c-type type stream
+                          (lambda (stream prio spacep)
+                            (declare (ignore stream prio spacep))
+                            nil)))
+      (t (pprint-c-type type stream
+                       (lambda (stream prio spacep)
+                         (declare (ignore prio))
+                         (when spacep
+                           (c-type-space stream))
+                         (princ kernel stream)))))))
 
 (defgeneric print-c-type (stream type &optional colon atsign)
   (:documentation
 (defmethod print-object ((object c-type) stream)
   (if *print-escape*
       (format stream "~:@<C-TYPE ~/sod::print-c-type/~:>" object)
-      (multiple-value-bind (base decl) (c-declaration object "")
-       (format stream "~A~:[~; ~A~]" base (plusp (length decl)) decl))))
+      (pprint-c-type object stream nil)))
 
-;; Utility functions.
+;; Utility functions and macros.
 
-(defun maybe-parenthesize (decl me him)
-  "Wrap parens around DECL, maybe, according to priorities of ME and HIM.
+(defun c-type-space (stream)
+  "Print a space and a miser-mode newline to STREAM.
 
-   If the declarator for HIM has a higher priority than that of ME (as C
-   types) then return DECL with parens wrapped around it; otherwise just
-   return DECL."
-  (if (<= (c-declarator-priority him)
-         (c-declarator-priority me))
-      decl
-      (format nil "(~A)" decl)))
+   This is the right function to call in a PPRINT-C-TYPE kernel function when
+   the SPACEP argument is true."
+  (pprint-indent :block 2 stream)
+  (write-char #\space stream)
+  (pprint-newline :miser stream))
 
-(defun compound-type-declaration (type format-control &rest format-args)
-  "Convenience function for implementating compound types.
+(defun maybe-in-parens* (stream condition thunk)
+  "Helper function for the MAYBE-IN-PARENS macro."
+  (pprint-logical-block
+      (stream nil
+             :prefix (if condition "(" "")
+             :suffix (if condition ")" ""))
+    (funcall thunk stream)))
 
-   The declaration is formed from the type's subtype and by processing the
-   given format string."
-  (let ((subty (c-type-subtype type))
-       (subdecl (format nil "~?" format-control format-args)))
-    (c-declaration subty (maybe-parenthesize subdecl type subty))))
+(defmacro maybe-in-parens ((stream condition) &body body)
+  "Evaluate BODY; if CONDITION, write parens to STREAM around it.
+
+   This macro is useful for implementing the PPRINT-C-TYPE method on compound
+   types.  The BODY is evaluated in the context of a logical block printing
+   to STREAM.  If CONDITION is non-nil, then the block will have open/close
+   parens as its prefix and suffix; otherwise they will be empty.
+
+   The STREAM is passed to PPRINT-LOGICAL-BLOCK, so it must be a symbol."
+  `(maybe-in-parens* ,stream ,condition (lambda (,stream) ,@body)))
 
 ;; S-expression syntax machinery.
 
                              (error "Bad character in C name ~S." name))))))
     (t name)))
 
-(defun expand-c-type (spec)
-  "Parse SPEC as a C type and return the result.
-
-   The SPEC can be one of the following.
-
-     * A C-TYPE object, which is returned immediately.
-
-     * A list, (OPERATOR . ARGUMENTS), where OPERATOR is a symbol: a parser
-       function associated with the OPERATOR symbol by DEFINE-C-TYPE-SYNTAX
-       or some other means is invoked on the ARGUMENTS, and the result is
-       returned.
-
-     * A symbol, which is treated the same way as a singleton list would be."
-
-  (flet ((interp (sym)
-          (or (get sym 'c-type)
-              (error "Unknown C type operator ~S." sym))))
-    (etypecase spec
-      (c-type spec)
-      (symbol (funcall (interp spec)))
-      (list (apply (interp (car spec)) (cdr spec))))))
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (defgeneric expand-c-type-spec (spec)
+    (:documentation
+     "Expand SPEC into Lisp code to construct a C type.")
+    (:method ((spec list))
+      (expand-c-type-form (car spec) (cdr spec))))
+  (defgeneric expand-c-type-form (head tail)
+    (:documentation
+     "Expand a C type list beginning with HEAD.")
+    (:method ((name (eql 'lisp)) tail)
+      `(progn ,@tail))))
 
 (defmacro c-type (spec)
-  "Evaluates to the type that EXPAND-C-TYPE would return.
-
-   Currently this just quotes SPEC and calls EXPAND-C-TYPE at runtime.  Maybe
-   later it will do something more clever."
-  `(expand-c-type ',spec))
+  "Expands to code to construct a C type, using EXPAND-C-TYPE-SPEC."
+  (expand-c-type-spec spec))
 
 (defmacro define-c-type-syntax (name bvl &rest body)
   "Define a C-type syntax function.
    A function defined by BODY and with lambda-list BVL is associated with the
    NAME.  When EXPAND-C-TYPE sees a list (NAME . STUFF), it will call this
    function with the argument list STUFF."
-  `(progn
-     (setf (get ',name 'c-type) (lambda ,bvl ,@body))
-     ',name))
+  (let ((headvar (gensym "HEAD"))
+       (tailvar (gensym "TAIL")))
+    `(eval-when (:compile-toplevel :load-toplevel :execute)
+       (defmethod expand-c-type-form ((,headvar (eql ',name)) ,tailvar)
+        (destructuring-bind ,bvl ,tailvar
+          ,@body)))))
 
 (defmacro c-type-alias (original &rest aliases)
   "Make ALIASES behave the same way as the ORIGINAL type."
-  (let ((i (gensym)) (orig (gensym)))
-    `(let ((,orig (get ',original 'c-type)))
-       (dolist (,i ',aliases)
-        (setf (get ,i 'c-type) ,orig)))))
+  (let ((headvar (gensym "HEAD"))
+       (tailvar (gensym "TAIL")))
+    `(eval-when (:compile-toplevel :load-toplevel :execute)
+       ,@(mapcar (lambda (alias)
+                  `(defmethod expand-c-type-form
+                       ((,headvar (eql ',alias)) ,tailvar)
+                     (expand-c-type-form ',original ,tailvar)))
+                aliases))))
 
 (defmacro defctype (names value)
   "Define NAMES all to describe the C-type VALUE.
    NAMES can be a symbol (treated as a singleton list), or a list of symbols.
    The VALUE is a C type S-expression, acceptable to EXPAND-C-TYPE.  It will
    be expanded once at run-time."
-  (unless (listp names)
-    (setf names (list names)))
-  (let ((ty (gensym)))
-    `(let ((,ty (expand-c-type ',value)))
-       (setf (get ',(car names) 'c-type) (lambda () ,ty))
-       ,@(and (cdr names)
-             `((c-type-alias ,(car names) ,@(cdr names)))))))
+  (let* ((names (if (listp names) names (list names)))
+        (namevar (gensym "NAME"))
+        (typevar (symbolicate 'c-type- (car names))))
+    `(progn
+       (defparameter ,typevar ,(expand-c-type-spec value))
+       (eval-when (:compile-toplevel :load-toplevel :execute)
+        ,@(mapcar (lambda (name)
+                    `(defmethod expand-c-type-spec ((,namevar (eql ',name)))
+                       ',typevar))
+                  names)))))
 
 ;;;--------------------------------------------------------------------------
 ;;; Types which can accept qualifiers.
           (sort (copy-list (c-type-qualifiers type)) #'string<)))
     (equal (fix type-a) (fix type-b))))
 
-(defmethod print-c-type :around
-    (stream (type qualifiable-c-type) &optional colon atsign)
-  (if (c-type-qualifiers type)
-      (pprint-logical-block (stream nil :prefix "(" :suffix ")")
-       (format stream "QUALIFIER~{ ~:_~:I~A~} ~:_"
-               (c-type-qualifiers type))
-       (call-next-method stream type colon atsign))
-      (call-next-method)))
-
 ;; A handy utility.
 
 (let ((cache (make-hash-table :test #'equal)))
              (setf (gethash key cache)
                    (copy-instance c-type :qualifiers qualifiers)))))))
 
-;; S-expression machinery.  Qualifiers have hairy syntax and need to be
-;; implemented by hand.
-
-(defun qualifier (qual &rest args)
-  "Parse a qualified C type.
-
-   The ARGS consist of a number of qualifiers and exactly one C-type
-   S-expression.  The result is a qualified version of this type, with the
-   given qualifiers attached."
-  (if (null args)
-      qual
-      (let* ((things (mapcar #'expand-c-type args))
-            (quals (delete-duplicates
-                    (sort (cons qual (remove-if-not #'keywordp things))
-                          #'string<)))
-            (types (remove-if-not (lambda (thing) (typep thing 'c-type))
-                                  things)))
-       (when (or (null types)
-                 (not (null (cdr types))))
-         (error "Only one proper type expected in ~S." args))
-       (qualify-type (car types) quals))))
-(setf (get 'qualifier 'c-type) #'qualifier)
-
-(defun declare-qualifier (qual)
-  "Defines QUAL as being a type qualifier.
-
-   When used as a C-type operator, it applies that qualifier to the type that
-   is its argument."
-  (let ((kw (intern (string qual) :keyword)))
-    (setf (get qual 'c-type)
-         (lambda (&rest args)
-           (apply #'qualifier kw args)))))
-
-;; Define some initial qualifiers.
-(dolist (qual '(const volatile restrict))
-  (declare-qualifier qual))
-
 ;;;--------------------------------------------------------------------------
 ;;; Simple C types (e.g., built-in arithmetic types).
 
    "C types with simple forms."))
 
 (let ((cache (make-hash-table :test #'equal)))
-  (defun make-simple-type (name)
+  (defun make-simple-type (name &optional qualifiers)
     "Make a distinguished object for the simple type called NAME."
-    (or (gethash name cache)
-       (setf (gethash name cache)
-             (make-instance 'simple-c-type :name name)))))
-
-(defmethod c-declaration ((type simple-c-type) decl)
-  (values (concatenate 'string
-                      (format-qualifiers (c-type-qualifiers type))
-                      (c-type-name type))
-         decl))
+    (qualify-type (or (gethash name cache)
+                     (setf (gethash name cache)
+                           (make-instance 'simple-c-type :name name)))
+                 qualifiers)))
+
+(defmethod pprint-c-type ((type simple-c-type) stream kernel)
+  (pprint-logical-block (stream nil)
+    (format stream "~{~(~A~) ~@_~}~A"
+           (c-type-qualifiers type)
+           (c-type-name type))
+    (funcall kernel stream 0 t)))
 
 (defmethod c-type-equal-p and ((type-a simple-c-type)
                               (type-b simple-c-type))
   (declare (ignore colon atsign))
   (let* ((name (c-type-name type))
         (symbol (gethash name *simple-type-map*)))
-    (if symbol
-       (princ symbol stream)
-       (format stream "~:@<SIMPLE-C-TYPE ~@_~S~:>" name))))
+    (format stream "~:[~S~;~:@<~S~0@*~{ ~_~S~}~:>~]"
+           (c-type-qualifiers type) (or symbol name))))
 
 ;; S-expression syntax.
 
-(define-c-type-syntax simple-c-type (name)
-  "Constructs a simple C type called NAME (a string or symbol)."
-  (make-simple-type (c-name-case name)))
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (defmethod expand-c-type-spec ((spec string))
+    `(make-simple-type ,spec))
+  (defmethod expand-c-type-form ((head string) tail)
+    `(make-simple-type ,head ,@tail)))
 
 (defmacro define-simple-c-type (names type)
   "Define each of NAMES to be a simple type called TYPE."
-  `(progn
-     (setf (gethash ,type *simple-type-map*)
-          ',(if (listp names) (car names) names))
-     (defctype ,names (simple-c-type ,type))))
+  (let ((names (if (listp names) names (list names))))
+    `(progn
+       (setf (gethash ,type *simple-type-map*) ',(car names))
+       (defctype ,names ,type)
+       (define-c-type-syntax ,(car names) (&rest quals)
+        `(make-simple-type ,',type (list ,@quals))))))
 
 (define-simple-c-type void "void")
 
    "Return the kind of tagged type that TYPE is, as a keyword."))
 
 (macrolet ((define-tagged-type (kind what)
-            (let ((type (intern (format nil "C-~A-TYPE" (string kind))))
-                  (constructor (intern (format nil "MAKE-~A-TYPE"
-                                               (string kind)))))
+            (let ((type (symbolicate 'c- kind '-type))
+                  (constructor (symbolicate 'make- kind '-type)))
               `(progn
                  (defclass ,type (tagged-c-type) ()
                    (:documentation ,(format nil "C ~a types." what)))
                  (defmethod c-tagged-type-kind ((type ,type))
-                   ,kind)
+                   ',kind)
                  (let ((cache (make-hash-table :test #'equal)))
-                   (defun ,constructor (tag)
-                     (or (gethash tag cache)
-                         (setf (gethash tag cache)
-                               (make-instance ',type :tag tag)))))
-                   (define-c-type-syntax ,(intern (string kind)) (tag)
-                     ,(format nil "Construct ~A type named TAG" what)
-                     (,constructor tag))))))
-  (define-tagged-type :enum "enumerated")
-  (define-tagged-type :struct "structure")
-  (define-tagged-type :union "union"))
-
-(defclass c-enum-type (tagged-c-type)
-  ()
-  (:documentation
-   "C enumeration types."))
-(defclass c-struct-type (tagged-c-type)
-  ()
-  (:documentation
-   "C structure types."))
-(defclass c-union-type (tagged-c-type)
-  ()
-  (:documentation
-   "C union types."))
-
-(defmethod c-declaration ((type tagged-c-type) decl)
-  (values (concatenate 'string
-                      (format-qualifiers (c-type-qualifiers type))
-                      (string-downcase (c-tagged-type-kind type))
-                      " "
-                      (c-type-tag type))
-         decl))
+                   (defun ,constructor (tag &optional qualifiers)
+                     (qualify-type (or (gethash tag cache)
+                                       (setf (gethash tag cache)
+                                             (make-instance ',type
+                                                            :tag tag)))
+                                   qualifiers)))
+                 (define-c-type-syntax ,kind (tag &rest quals)
+                   ,(format nil "Construct ~A type named TAG" what)
+                   `(,',constructor ,tag (list ,@quals)))))))
+  (define-tagged-type enum "enumerated")
+  (define-tagged-type struct "structure")
+  (define-tagged-type union "union"))
+
+(defmethod pprint-c-type ((type tagged-c-type) stream kernel)
+  (pprint-logical-block (stream nil)
+    (format stream "~{~(~A~) ~@_~}~(~A~) ~A"
+           (c-type-qualifiers type)
+           (c-tagged-type-kind type)
+           (c-type-tag type))
+    (funcall kernel stream 0 t)))
 
 (defmethod c-type-equal-p and ((type-a tagged-c-type)
                               (type-b tagged-c-type))
 
 (defmethod print-c-type (stream (type tagged-c-type) &optional colon atsign)
   (declare (ignore colon atsign))
-  (format stream "~:@<~A ~A~:>"
+  (format stream "~:@<~S ~@_~S~{ ~_~S~}~:>"
          (c-tagged-type-kind type)
-         (c-type-tag type)))
-
-;; S-expression syntax.
-
-(define-c-type-syntax enum (tag)
-  "Construct an enumeration type named TAG."
-  (make-instance 'c-enum-type :tag (c-name-case tag)))
-(define-c-type-syntax struct (tag)
-  "Construct a structure type named TAG."
-  (make-instance 'c-struct-type :tag (c-name-case tag)))
-(define-c-type-syntax union (tag)
-  "Construct a union type named TAG."
-  (make-instance 'c-union-type :tag (c-name-case tag)))
+         (c-type-tag type)
+         (c-type-qualifiers type)))
 
 ;;;--------------------------------------------------------------------------
 ;;; Pointer types.
   (:documentation
    "C pointer types."))
 
-(defmethod c-declarator-priority ((type c-pointer-type)) 1)
-
-(defmethod c-declaration ((type c-pointer-type) decl)
-  (compound-type-declaration type
-                            "*~A~A"
-                            (format-qualifiers (c-type-qualifiers type))
-                            decl))
+(let ((cache (make-hash-table :test #'eql)))
+  (defun make-pointer-type (subtype &optional qualifiers)
+    "Return a (maybe distinguished) pointer type."
+    (qualify-type (or (gethash subtype cache)
+                     (make-instance 'c-pointer-type :subtype subtype))
+                 qualifiers)))
+
+(defmethod pprint-c-type ((type c-pointer-type) stream kernel)
+  (pprint-c-type (c-type-subtype type) stream
+                (lambda (stream prio spacep)
+                  (when spacep (c-type-space stream))
+                  (maybe-in-parens (stream (> prio 1))
+                    (format stream "*~{~(~A~)~^ ~@_~}"
+                            (c-type-qualifiers type))
+                    (funcall kernel stream 1 (c-type-qualifiers type))))))
 
 (defmethod c-type-equal-p and ((type-a c-pointer-type)
                               (type-b c-pointer-type))
 
 (defmethod print-c-type (stream (type c-pointer-type) &optional colon atsign)
   (declare (ignore colon atsign))
-  (format stream "~:@<* ~@_~/sod::print-c-type/~:>"
-         (c-type-subtype type)))
+  (format stream "~:@<* ~@_~/sod::print-c-type/~{ ~_~S~}~:>"
+         (c-type-subtype type)
+         (c-type-qualifiers type)))
 
 ;; S-expression syntax.
 
-(define-c-type-syntax pointer (sub)
+(define-c-type-syntax * (sub &rest quals)
   "Return the type of pointer-to-SUB."
-  (make-instance 'c-pointer-type :subtype (expand-c-type sub)))
-(c-type-alias pointer * ptr)
+  `(make-pointer-type ,(expand-c-type-spec sub) (list ,@quals)))
+(c-type-alias * pointer ptr)
 
 (defctype string (* char))
+(defctype const-string (* (char :const)))
 
 ;;;--------------------------------------------------------------------------
 ;;; Array types.
   (:documentation
    "C array types."))
 
-(defmethod c-declarator-priority ((type c-array-type)) 2)
+(defun make-array-type (subtype dimensions)
+  "Return a new array of SUBTYPE with given DIMENSIONS."
+  (make-instance 'c-array-type :subtype subtype
+                :dimensions (or dimensions '(nil))))
 
-(defmethod c-declaration ((type c-array-type) decl)
-  (compound-type-declaration type
-                            "~A~{[~@[~A~]]~}"
-                            decl
-                            (c-array-dimensions type)))
+(defmethod pprint-c-type ((type c-array-type) stream kernel)
+  (pprint-c-type (c-type-subtype type) stream
+                (lambda (stream prio spacep)
+                  (maybe-in-parens (stream (> prio 2))
+                    (funcall kernel stream 2 spacep)
+                    (format stream "~@<~{[~@[~A~]]~^~_~}~:>"
+                            (c-array-dimensions type))))))
 
 (defmethod c-type-equal-p and ((type-a c-array-type)
                               (type-b c-array-type))
 
 (defmethod print-c-type (stream (type c-array-type) &optional colon atsign)
   (declare (ignore colon atsign))
-  (format stream "~:@<[] ~@_~:I~/sod::print-c-type/~{ ~_~A~}~:>"
+  (format stream "~:@<[] ~@_~:I~/sod::print-c-type/~{ ~_~S~}~:>"
          (c-type-subtype type)
          (c-array-dimensions type)))
 
 ;; S-expression syntax.
 
-(define-c-type-syntax array (sub &rest dims)
+(define-c-type-syntax [] (sub &rest dims)
   "Return the type of arrays of SUB with the dimensions DIMS.
 
    If the DIMS are omitted, a single unknown-length dimension is added."
-  (make-instance 'c-array-type
-                :subtype (expand-c-type sub)
-                :dimensions (or dims '(nil))))
-(c-type-alias array [] vec)
+  `(make-array-type ,(expand-c-type-spec sub)
+                   (list ,@(or dims '(nil)))))
+(c-type-alias [] array vec)
 
 ;;;--------------------------------------------------------------------------
 ;;; Function types.
 
-;; Definitions.
-
-(defclass c-function-type (c-type)
-  ((subtype :initarg :subtype
-           :type c-type
-           :reader c-type-subtype)
-   (arguments :initarg :arguments
-             :type list
-             :reader c-function-arguments))
-  (:documentation
-   "C function types.  The subtype is the return type, as implied by the C
-    syntax for function declarations."))
-
-(defmethod c-declarator-priority ((type c-function-type)) 2)
+;; Arguments.
 
 (defstruct (argument (:constructor make-argument (name type)) (:type list))
   "Simple list structure representing a function argument."
   name
   type)
 
-(defmethod c-declaration ((type c-function-type) decl)
-  (compound-type-declaration type
-                            "~A(~:[void~;~:*~{~A~^, ~}~])"
-                            decl
-                            (mapcar (lambda (arg)
-                                      (if (eq arg :ellipsis)
-                                          "..."
-                                          (multiple-value-bind
-                                              (typestr declstr)
-                                              (c-declaration
-                                               (argument-type arg)
-                                               (or (argument-name arg) ""))
-                                            (format nil "~A~:[~; ~A~]"
-                                                    typestr
-                                                    (plusp (length declstr))
-                                                    declstr))))
-                                    (c-function-arguments type))))
-
 (defun arguments-lists-equal-p (list-a list-b)
+  "Return whether LIST-A and LIST-B match.
+
+   They must have the same number of arguments, and each argument must have
+   the same type, or be :ELLIPSIS.  The argument names are not inspected."
   (and (= (length list-a) (length list-b))
        (every (lambda (arg-a arg-b)
                (if (eq arg-a :ellipsis)
                                    (argument-type arg-b))))
              list-a list-b)))
 
+(defgeneric commentify-argument-name (name)
+  (:documentation
+   "Produce a `commentified' version of the argument.
+
+   The default behaviour is that temporary argument names are simply omitted
+   (NIL is returned); otherwise, `/*...*/' markers are wrapped around the
+   printable representation of the argument.")
+  (:method ((name null)) nil)
+  (:method ((name t)) (format nil "/*~A*/" name)))
+
+(defun commentify-argument-names (arguments)
+  "Return an argument list with the arguments commentified.
+
+   That is, with each argument name passed through COMMENTIFY-ARGUMENT-NAME."
+  (mapcar (lambda (arg)
+           (if (eq arg :ellipsis)
+               arg
+               (make-argument (commentify-argument-name (argument-name arg))
+                              (argument-type arg))))
+         arguments))
+
+(defun commentify-function-type (type)
+  "Return a type like TYPE, but with arguments commentified.
+
+   This doesn't recurse into the return type or argument types."
+  (make-function-type (c-type-subtype type)
+                     (commentify-argument-names
+                      (c-function-arguments type))))
+
+;; Definitions.
+
+(defclass c-function-type (c-type)
+  ((subtype :initarg :subtype
+           :type c-type
+           :reader c-type-subtype)
+   (arguments :initarg :arguments
+             :type list
+             :reader c-function-arguments))
+  (:documentation
+   "C function types.  The subtype is the return type, as implied by the C
+    syntax for function declarations."))
+
+(defun make-function-type (subtype arguments)
+  "Return a new function type, returning SUBTYPE and accepting ARGUMENTS."
+  (make-instance 'c-function-type :subtype subtype :arguments arguments))
+
 (defmethod c-type-equal-p and ((type-a c-function-type)
                               (type-b c-function-type))
   (and (c-type-equal-p (c-type-subtype type-a)
          #.(concatenate 'string
                         "~:@<"
                         "FUN ~@_~:I~/sod::print-c-type/"
-                        "~{ ~_~:<~A ~@_~/sod::print-c-type/~:>~}"
+                        "~{ ~_~:<~S ~@_~/sod::print-c-type/~:>~}"
                         "~:>")
          (c-type-subtype type)
          (c-function-arguments type)))
 
+(defmethod pprint-c-type ((type c-function-type) stream kernel)
+  (pprint-c-type (c-type-subtype type) stream
+                (lambda (stream prio spacep)
+                  (maybe-in-parens (stream (> prio 2))
+                    (when spacep (c-type-space stream))
+                    (funcall kernel stream 2 nil)
+                    (pprint-indent :block 4 stream)
+                    ;;(pprint-newline :miser stream)
+                    (pprint-logical-block
+                        (stream nil :prefix "(" :suffix ")")
+                      (let ((firstp t))
+                        (dolist (arg (c-function-arguments type))
+                          (if firstp
+                              (setf firstp nil)
+                              (format stream ", ~_"))
+                          (if (eq arg :ellipsis)
+                              (write-string "..." stream)
+                              (pprint-c-type (argument-type arg)
+                                             stream
+                                             (argument-name arg))))))))))
+
 ;; S-expression syntax.
 
-(define-c-type-syntax function (ret &rest args)
+(define-c-type-syntax fun (ret &rest args)
   "Return the type of functions which returns RET and has arguments ARGS.
 
-   The ARGS are a list (NAME TYPE).  The NAME can be NIL to indicate that no
-   name was given."
-  (make-instance 'c-function-type
-                :subtype (expand-c-type ret)
-                :arguments (mapcar (lambda (arg)
-                                     (make-argument (car arg)
-                                                    (expand-c-type
-                                                     (cadr arg))))
-                                   args)))
-(c-type-alias function () func fun fn)
+   The ARGS are a list of arguments of the form (NAME TYPE).  The NAME can be
+   NIL to indicate that no name was given.
+
+   If an entry isn't a list, it's assumed to be the start of a Lisp
+   expression to compute the tail of the list; similarly, if the list is
+   improper, then it's considered to be a complete expression.  The upshot of
+   this apparently bizarre rule is that you can say
+
+     (c-type (fun int (\"foo\" int) . arg-tail))
+
+   where ARG-TAIL is (almost) any old Lisp expression and have it tack the
+   arguments onto the end.  Of course, there don't have to be any explicit
+   arguments at all.  The only restriction is that the head of the Lisp form
+   can't be a list -- so ((lambda (...) ...) ...) is out, but you probably
+   wouldn't type that anyway."
+
+  `(make-function-type ,(expand-c-type-spec ret)
+                      ,(do ((args args (cdr args))
+                            (list nil
+                                  (cons `(make-argument ,(caar args)
+                                                        ,(expand-c-type-spec
+                                                          (cadar args)))
+                                        list)))
+                           ((or (atom args) (atom (car args)))
+                            (cond ((and (null args) (null list)) `nil)
+                                  ((null args) `(list ,@(nreverse list)))
+                                  ((null list) `,args)
+                                  (t `(list* ,@(nreverse list) ,args)))))))
+(c-type-alias fun function () func fn)
 
 ;;;----- That's all, folks --------------------------------------------------