;;;----- Licensing notice ---------------------------------------------------
;;;
-;;; This file is part of the Sensble Object Design, an object system for C.
+;;; This file is part of the Sensible Object Design, an object system for C.
;;;
;;; SOD is free software; you can redistribute it and/or modify
;;; it under the terms of the GNU General Public License as published by
(export 'canonify-qualifiers)
(defun canonify-qualifiers (qualifiers)
- "Return a canonical list of qualifiers."
- (delete-duplicates (sort (copy-list qualifiers) #'string<)))
+ "Return a canonical list of qualifiers.
+
+ Duplicates and `nil' entries are deleted, and the remaining entries are
+ sorted."
+ (sort (delete-duplicates (delete nil (copy-list qualifiers))) #'string<))
(export 'qualify-c-type)
(defgeneric qualify-c-type (type qualifiers)
The qualifiers of the returned type are the union of the requested
QUALIFIERS and the qualifiers already applied to TYPE."))
+(export 'c-qualifier-keyword)
+(defgeneric c-qualifier-keyword (qualifier)
+ (:documentation "Return the C keyword for the QUALIFIER (a Lisp keyword).")
+ (:method ((qualifier symbol)) (string-downcase qualifier)))
+
+(export 'c-type-qualifier-keywords)
+(defun c-type-qualifier-keywords (c-type)
+ "Return the type's qualifiers, as a list of C keyword names."
+ (mapcar #'c-qualifier-keyword (c-type-qualifiers c-type)))
+
(export 'c-type-subtype)
(defgeneric c-type-subtype (type)
(:documentation
(export '(expand-c-type-spec expand-c-type-form))
(eval-when (:compile-toplevel :load-toplevel :execute)
(defgeneric expand-c-type-spec (spec)
- (:documentation
- "Expand SPEC into Lisp code to construct a C type.")
+ (: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.")
+ (:documentation "Expand a C type list beginning with HEAD.")
(:method ((name (eql 'lisp)) tail)
`(progn ,@tail))))
(expand-c-type-spec spec))
(export 'define-c-type-syntax)
-(defmacro define-c-type-syntax (name bvl &rest body)
+(defmacro define-c-type-syntax (name bvl &body 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."
+ NAME. When `expand-c-type-spec' sees a list (NAME . STUFF), it will call
+ this function with the argument list STUFF."
(with-gensyms (head tail)
(multiple-value-bind (doc decls body) (parse-body body)
`(eval-when (:compile-toplevel :load-toplevel :execute)
,@doc
(destructuring-bind ,bvl ,tail
,@decls
- ,@body))
+ (block ,name ,@body)))
',name))))
(export 'c-type-alias)
',aliases)))
(export 'defctype)
-(defmacro defctype (names value)
+(defmacro defctype (names value &key export)
"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."
+ The VALUE is a C type S-expression, acceptable to `expand-c-type-spec'.
+ It will be expanded once at run-time."
(let* ((names (if (listp names) names (list names)))
(namevar (gensym "NAME"))
(typevar (symbolicate 'c-type- (car names))))
`(progn
+ ,@(and export
+ `((export '(,typevar ,@names))))
(defparameter ,typevar ,(expand-c-type-spec value))
(eval-when (:compile-toplevel :load-toplevel :execute)
,@(mapcar (lambda (name)
((char= ch #\-)
(write-char #\_ out))
(t
- (error "Bad character in C name ~S." name))))))
+ (error "Bad character in C name ~S" name))))))
(t name)))
;;;--------------------------------------------------------------------------
+;;; Storage specifier protocol.
+
+(export 'pprint-c-storage-specifier)
+(defgeneric pprint-c-storage-specifier (spec stream)
+ (:documentation "Print the storage specifier SPEC to STREAM, as C syntax.")
+ (:method ((spec symbol) stream) (princ (string-downcase spec) stream)))
+
+(export 'print-c-storage-specifier)
+(defgeneric print-c-storage-specifier (stream spec &optional colon atsign)
+ (:documentation
+ "Print the storage specifier SPEC to STREAM, as an S-expression.
+
+ This function is suitable for use in `format's ~/.../ command.")
+ (:method (stream (spec t) &optional colon atsign)
+ (declare (ignore colon atsign))
+ (prin1 spec stream))
+ (:method (stream (spec symbol) &optional colon atsign)
+ (declare (ignore colon atsign))
+ (princ (string-downcase spec) stream)))
+
+(export '(expand-c-storage-specifier expand-c-storage-specifier-form))
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (defgeneric expand-c-storage-specifier (spec)
+ (:documentation
+ "Expand SPEC into Lisp code to construct a storage specifier.")
+ (:method ((spec list))
+ (expand-c-storage-specifier-form (car spec) (cdr spec)))
+ (:method ((spec symbol))
+ (if (keywordp spec) spec
+ (expand-c-storage-specifier-form spec nil))))
+ (defgeneric expand-c-storage-specifier-form (head tail)
+ (:documentation
+ "Expand a C storage-specifier form beginning with HEAD.")
+ (:method ((name (eql 'lisp)) tail)
+ `(progn ,@tail))))
+
+(export 'define-c-storage-specifier-syntax)
+(defmacro define-c-storage-specifier-syntax (name bvl &body body)
+ "Define a C storage-specifier syntax function.
+
+ A function defined by BODY and with lambda-list BVL is associated wth the
+ NAME. When `expand-c-storage-specifier' sees a list (NAME . STUFF), it
+ will call this function with the argument list STUFF."
+ (with-gensyms (head tail)
+ (multiple-value-bind (doc decls body) (parse-body body)
+ `(eval-when (:compile-toplevel :load-toplevel :execute)
+ (defmethod expand-c-storage-specifier-form
+ ((,head (eql ',name)) ,tail)
+ ,@doc
+ (destructuring-bind ,bvl ,tail
+ ,@decls
+ (block ,name ,@body)))
+ ',name))))
+
+;;;--------------------------------------------------------------------------
+;;; A type for carrying storage specifiers.
+
+(export '(c-storage-specifiers-type c-type-specifiers))
+(defclass c-storage-specifiers-type (c-type)
+ ((specifiers :initarg :specifiers :type list :reader c-type-specifiers)
+ (subtype :initarg :subtype :type c-type :reader c-type-subtype))
+ (:documentation
+ "A type for carrying storage specifiers.
+
+ Properly, storage specifiers should only appear on an outermost type.
+ This fake C type is a handy marker for the presence of storage specifiers,
+ so that they can be hoisted properly when constructing derived types."))
+
+(export 'wrap-c-type)
+(defun wrap-c-type (wrapper-func base-type)
+ "Handle storage specifiers correctly when making a derived type.
+
+ WRAPPER-FUNC should be a function which will return some derived type of
+ BASE-TYPE. This function differs from `funcall' only when BASE-TYPE is
+ actually a `c-storage-specifiers-type', in which case it invokes
+ WRAPPER-FUNC on the underlying type, and re-attaches the storage
+ specifiers to the derived type."
+ (if (typep base-type 'c-storage-specifiers-type)
+ (let* ((unwrapped-type (c-type-subtype base-type))
+ (wrapped-type (funcall wrapper-func unwrapped-type))
+ (specifiers (c-type-specifiers base-type)))
+ (make-or-intern-c-type 'c-storage-specifiers-type unwrapped-type
+ :specifiers specifiers
+ :subtype wrapped-type))
+ (funcall wrapper-func base-type)))
+
+;;;--------------------------------------------------------------------------
;;; Function arguments.
-(export '(argument argumentp make-argument argument-name argument-type))
-(defstruct (argument (:constructor make-argument (name type))
+(export '(argument argumentp make-argument
+ argument-name argument-type argument-default))
+(defstruct (argument (:constructor make-argument (name type &optional default
+ &aux (%type type)))
(:predicate argumentp))
"Simple structure representing a function argument."
- name
- type)
+ (name nil :type t :read-only t)
+ (%type nil :type c-type :read-only t)
+ (default nil :type t :read-only t))
+(define-access-wrapper argument-type argument-%type :read-only t)
(export 'commentify-argument-name)
(defgeneric commentify-argument-name (name)
"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
+ (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)))