chiark / gitweb /
Massive reorganization in progress.
[sod] / src / proto-c-types.lisp
1 ;;; -*-lisp-*-
2 ;;;
3 ;;; Protocol for C type representation
4 ;;;
5 ;;; (c) 2009 Straylight/Edgeware
6 ;;;
7
8 ;;;----- Licensing notice ---------------------------------------------------
9 ;;;
10 ;;; This file is part of the Sensble Object Design, an object system for C.
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)
49   "Return a canonical list of qualifiers."
50   (delete-duplicates (sort (copy-list qualifiers) #'string<)))
51
52 (export 'c-type-subtype)
53 (defgeneric c-type-subtype (type)
54   (:documentation
55    "For compound types, return the base type."))
56
57 ;;;--------------------------------------------------------------------------
58 ;;; Comparison protocol.
59
60 (export 'c-type-equal-p)
61 (defgeneric c-type-equal-p (type-a type-b)
62   (:method-combination and)
63   (:documentation
64    "Answers whether two types TYPE-A and TYPE-B are structurally equal.
65
66    Here, `structurally equal' means that they have the same qualifiers,
67    similarly spelt names, and structurally equal components.")
68   (:method and (type-a type-b)
69     (eql (class-of type-a) (class-of type-b))))
70
71 (defmethod c-type-equal-p and ((type-a qualifiable-c-type)
72                                (type-b qualifiable-c-type))
73   (equal (canonify-qualifiers (c-type-qualifiers type-a))
74          (canonify-qualifiers (c-type-qualifiers type-b))))
75
76 ;;;--------------------------------------------------------------------------
77 ;;; C syntax output protocol.
78
79 (export 'pprint-c-type)
80 (defgeneric pprint-c-type (type stream kernel)
81   (:documentation
82    "Pretty-printer for C types.
83
84    Print TYPE to STREAM.  In the middle of the declarator, call the function
85    KERNEL with one argument: whether it needs a leading space.")
86   (:method :around (type stream kernel)
87     (typecase kernel
88       (null (pprint-c-type type stream
89                            (lambda (stream prio spacep)
90                              (declare (ignore stream prio spacep))
91                              nil)))
92       ((or function symbol) (call-next-method))
93       (t (pprint-c-type type stream
94                         (lambda (stream prio spacep)
95                           (declare (ignore prio))
96                           (when spacep
97                             (c-type-space stream))
98                           (princ kernel stream)))))))
99
100 (export 'c-type-space)
101 (defun c-type-space (stream)
102   "Print a space and a miser-mode newline to STREAM.
103
104    This is the right function to call in a PPRINT-C-TYPE kernel function when
105    the SPACEP argument is true."
106   (pprint-indent :block 2 stream)
107   (write-char #\space stream)
108   (pprint-newline :miser stream))
109
110 (defun maybe-in-parens* (stream condition thunk)
111   "Helper function for the MAYBE-IN-PARENS macro."
112   (multiple-value-bind (prefix suffix)
113       (if condition (values "(" ")") (values "" ""))
114     (pprint-logical-block (stream nil :prefix prefix :suffix suffix)
115       (funcall thunk stream))))
116
117 (export 'maybe-in-parens)
118 (defmacro maybe-in-parens ((stream condition) &body body)
119   "Evaluate BODY; if CONDITION, write parens to STREAM around it.
120
121    This macro is useful for implementing the PPRINT-C-TYPE method on compound
122    types.  The BODY is evaluated in the context of a logical block printing
123    to STREAM.  If CONDITION is non-nil, then the block will have open/close
124    parens as its prefix and suffix; otherwise they will be empty.
125
126    The STREAM is passed to PPRINT-LOGICAL-BLOCK, so it must be a symbol."
127   `(maybe-in-parens* ,stream ,condition (lambda (,stream) ,@body)))
128
129 (export 'format-qualifiers)
130 (defun format-qualifiers (quals)
131   "Return a string listing QUALS, with a space after each."
132   (format nil "~{~(~A~) ~}" quals))
133
134 ;;;--------------------------------------------------------------------------
135 ;;; S-expression notation protocol.
136
137 (export 'print-c-type)
138 (defgeneric print-c-type (stream type &optional colon atsign)
139   (:documentation
140    "Print an abbreviated syntax for TYPE to the STREAM.
141
142    This function is suitable for use in FORMAT's ~/.../ command."))
143
144 (export 'expand-c-type-spec)
145 (eval-when (:compile-toplevel :load-toplevel :execute)
146   (defgeneric expand-c-type-spec (spec)
147     (:documentation
148      "Expand SPEC into Lisp code to construct a C type.")
149     (:method ((spec list))
150       (expand-c-type-form (car spec) (cdr spec))))
151   (defgeneric expand-c-type-form (head tail)
152     (:documentation
153      "Expand a C type list beginning with HEAD.")
154     (:method ((name (eql 'lisp)) tail)
155       `(progn ,@tail))))
156
157 (export 'c-type)
158 (defmacro c-type (spec)
159   "Expands to code to construct a C type, using EXPAND-C-TYPE-SPEC."
160   (expand-c-type-spec spec))
161
162 (export 'define-c-type-syntax)
163 (defmacro define-c-type-syntax (name bvl &rest body)
164   "Define a C-type syntax function.
165
166    A function defined by BODY and with lambda-list BVL is associated with the
167    NAME.  When EXPAND-C-TYPE sees a list (NAME . STUFF), it will call this
168    function with the argument list STUFF."
169   (with-gensyms (head tail)
170     (multiple-value-bind (doc decls body) (parse-body body)
171       `(eval-when (:compile-toplevel :load-toplevel :execute)
172          (defmethod expand-c-type-form ((,head (eql ',name)) ,tail)
173            ,@doc
174            (destructuring-bind ,bvl ,tail
175              ,@decls
176              ,@body))
177          ',name))))
178
179 (export 'c-type-alias)
180 (defmacro c-type-alias (original &rest aliases)
181   "Make ALIASES behave the same way as the ORIGINAL type."
182   (with-gensyms (head tail)
183     `(eval-when (:compile-toplevel :load-toplevel :execute)
184        ,@(mapcar (lambda (alias)
185                    `(defmethod expand-c-type-form
186                         ((,head (eql ',alias)) ,tail)
187                       (expand-c-type-form ',original ,tail)))
188                  aliases)
189        ',aliases)))
190
191 (export 'defctype)
192 (defmacro defctype (names value)
193   "Define NAMES all to describe the C-type VALUE.
194
195    NAMES can be a symbol (treated as a singleton list), or a list of symbols.
196    The VALUE is a C type S-expression, acceptable to EXPAND-C-TYPE.  It will
197    be expanded once at run-time."
198   (let* ((names (if (listp names) names (list names)))
199          (namevar (gensym "NAME"))
200          (typevar (symbolicate 'c-type- (car names))))
201     `(progn
202        (defparameter ,typevar ,(expand-c-type-spec value))
203        (eval-when (:compile-toplevel :load-toplevel :execute)
204          ,@(mapcar (lambda (name)
205                      `(defmethod expand-c-type-spec ((,namevar (eql ',name)))
206                         ',typevar))
207                    names))
208        'names)))
209
210 (export 'c-name-case)
211 (defun c-name-case (name)
212   "Convert NAME to suitable case.
213
214    Strings are returned as-is; symbols are squashed to lower-case and hyphens
215    are replaced by underscores."
216   (typecase name
217     (symbol (with-output-to-string (out)
218               (loop for ch across (symbol-name name)
219                     do (cond ((alpha-char-p ch)
220                               (write-char (char-downcase ch) out))
221                              ((or (digit-char-p ch)
222                                   (char= ch #\_))
223                               (write-char ch out))
224                              ((char= ch #\-)
225                               (write-char #\_ out))
226                              (t
227                               (error "Bad character in C name ~S." name))))))
228     (t name)))
229
230 ;;;--------------------------------------------------------------------------
231 ;;; Function arguments.
232
233 (export '(argument argumentp make-argument argument-name argument-type))
234 (defstruct (argument (:constructor make-argument (name type))
235                      (:predicate argumentp))
236   "Simple structure representing a function argument."
237   name
238   type)
239
240 (export 'commentify-argument-name)
241 (defgeneric commentify-argument-name (name)
242   (:documentation
243    "Produce a `commentified' version of the argument.
244
245    The default behaviour is that temporary argument names are simply omitted
246    (NIL is returned); otherwise, `/*...*/' markers are wrapped around the
247    printable representation of the argument.")
248   (:method ((name null)) nil)
249   (:method ((name t)) (format nil "/*~A*/" name)))
250
251 ;;;--------------------------------------------------------------------------
252 ;;; Printing objects.
253
254 (defmethod print-object ((object c-type) stream)
255   (if *print-escape*
256       (format stream "~:@<C-TYPE ~/sod:print-c-type/~:>" object)
257       (pprint-c-type object stream nil)))
258
259 ;;;----- That's all, folks --------------------------------------------------