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 --------------------------------------------------