Major effort to plug slot-name leaks.
[sod] / src / codegen-impl.lisp
CommitLineData
dea4d055
MW
1;;; -*-lisp-*-
2;;;
3;;; Code generation protocol implementation
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;;; Temporary names.
30
31(export '(temporary-argument temporary-function))
32(defclass temporary-argument (temporary-name) ())
33(defclass temporary-function (temporary-name) ())
34
35(export 'temporary-variable)
36(defclass temporary-variable (temporary-name)
37 ((in-use-p :initarg :in-use-p :initform nil
38 :type boolean :accessor var-in-use-p)))
39
40(defmethod commentify-argument-name ((name temporary-name))
41 nil)
42
dea4d055
MW
43(defun temporary-function ()
44 "Return a temporary function name."
45 (make-instance 'temporary-function
46 :tag (prog1 *temporary-index* (incf *temporary-index*))))
47
48(defmethod format-temporary-name ((var temporary-name) stream)
49 (format stream "~A" (temp-tag var)))
50(defmethod format-temporary-name ((var temporary-argument) stream)
51 (format stream "sod__a~A" (temp-tag var)))
52(defmethod format-temporary-name ((var temporary-variable) stream)
53 (format stream "sod__v~A" (temp-tag var)))
54(defmethod format-temporary-name ((var temporary-function) stream)
55 (format stream "sod__f~A" (temp-tag var)))
56
57(defmethod print-object ((var temporary-name) stream)
58 (if *print-escape*
59 (print-unreadable-object (var stream :type t)
60 (prin1 (temp-tag var) stream))
61 (format-temporary-name var stream)))
62
63;;;--------------------------------------------------------------------------
64;;; Instruction types.
65
66;; Compound statements.
67
4b8e5c03
MW
68;; HACK: use gensyms for the `condition' slots to avoid leaking the slot
69;; names, since the symbol `condition' actually comes from the `common-lisp'
70;; package. The `definst' machinery will symbolicate the various associated
71;; methods correctly despite this subterfuge.
72
73(definst if (stream :export t) (#1=#:condition consequent alternative)
dea4d055 74 (format-compound-statement (stream consequent alternative)
4b8e5c03 75 (format stream "if (~A)" #1#))
dea4d055
MW
76 (when alternative
77 (format-compound-statement (stream alternative)
78 (write-string "else" stream))))
79
4b8e5c03 80(definst while (stream :export t) (#1=#:condition body)
dea4d055 81 (format-compound-statement (stream body)
4b8e5c03 82 (format stream "while (~A)" #1#)))
dea4d055 83
4b8e5c03 84(definst do-while (stream :export t) (body #1=#:condition)
dea4d055
MW
85 (format-compound-statement (stream body :space)
86 (write-string "do" stream))
4b8e5c03 87 (format stream "while (~A);" #1#))
dea4d055
MW
88
89;; Special varargs hacks.
90
418752c5 91(definst va-start (stream :export t) (ap arg)
dea4d055
MW
92 (format stream "va_start(~@<~A, ~_~A~:>);" ap arg))
93
418752c5 94(definst va-copy (stream :export t) (to from)
dea4d055
MW
95 (format stream "va_copy(~@<~A, ~_~A~:>);" to from))
96
418752c5 97(definst va-end (stream :export t) (ap)
dea4d055
MW
98 (format stream "va_end(~A);" ap))
99
100;; Expressions.
101
4b8e5c03
MW
102;; HACK: use a gensym for the `func' slot to avoid leaking the slot name,
103;; since the symbol `func' is exported from our package.
104(definst call (stream :export t) (#1=#:func args)
105 (format stream "~A(~@<~{~A~^, ~_~}~:>)" #1# args))
dea4d055
MW
106
107;;;--------------------------------------------------------------------------
108;;; Code generator objects.
109
110(defclass basic-codegen ()
111 ((vars :initarg :vars :initform nil :type list :accessor codegen-vars)
112 (insts :initarg :insts :initform nil :type list :accessor codegen-insts)
113 (temp-index :initarg :temp-index :initform 0
114 :type fixnum :accessor codegen-temp-index))
115 (:documentation
116 "Base class for code generator state.
117
3109662a
MW
118 This contains the bare essentials for supporting the `emit-inst' and
119 `ensure-var' protocols; see the documentation for those generic functions
dea4d055
MW
120 for more details.
121
3109662a 122 This class isn't abstract. A full `codegen' object uses instances of this
dea4d055
MW
123 to keep track of pending functions which haven't been completed yet.
124
125 Just in case that wasn't clear enough: this is nothing to do with the
126 BASIC language."))
127
128(defmethod emit-inst ((codegen basic-codegen) inst)
129 (push inst (codegen-insts codegen)))
130
131(defmethod emit-insts ((codegen basic-codegen) insts)
132 (asetf (codegen-insts codegen) (revappend insts it)))
133
3f4ac959
MW
134(defmethod emit-decl ((codegen basic-codegen) inst)
135 (push inst (codegen-vars codegen)))
136
137(defmethod emit-decls ((codegen basic-codegen) insts)
138 (asetf (codegen-vars codegen) (revappend insts it)))
139
dea4d055
MW
140(defmethod ensure-var ((codegen basic-codegen) name type &optional init)
141 (let* ((vars (codegen-vars codegen))
66836e14
MW
142 (var (find name
143 (remove-if-not (lambda (var) (typep var 'var-inst)) vars)
144 :key #'inst-name :test #'equal)))
dea4d055
MW
145 (cond ((not var)
146 (setf (codegen-vars codegen)
147 (cons (make-var-inst name type init) vars)))
148 ((not (c-type-equal-p type (inst-type var)))
149 (error "(Internal) Redefining type for variable ~A." name)))
150 name))
151
152(export 'codegen)
153(defclass codegen (basic-codegen)
154 ((functions :initform nil :type list :accessor codegen-functions)
155 (stack :initform nil :type list :accessor codegen-stack))
156 (:documentation
157 "A full-fat code generator which can generate and track functions.
158
159 This is the real deal. Subclasses may which to attach additional state
160 for convenience's sake, but this class is self-contained. It supports the
3109662a 161 `codegen-push', `codegen-pop' and `codegen-pop-function' protocols."))
dea4d055
MW
162
163(defmethod codegen-push ((codegen codegen))
164 (with-slots (vars insts temp-index stack) codegen
165 (push (make-instance 'basic-codegen
166 :vars vars
167 :insts insts
168 :temp-index temp-index)
169 stack)
170 (setf vars nil insts nil temp-index 0)))
171
172(defmethod codegen-pop ((codegen codegen))
173 (with-slots (vars insts temp-index stack) codegen
174 (multiple-value-prog1
175 (values (nreverse vars) (nreverse insts))
176 (let ((sub (pop stack)))
177 (setf vars (codegen-vars sub)
178 insts (codegen-insts sub)
179 temp-index (codegen-temp-index sub))))))
180
181(defmethod codegen-add-function ((codegen codegen) function)
182 (with-slots (functions) codegen
183 (setf functions (nconc functions (list function)))))
184
185(defmethod temporary-var ((codegen basic-codegen) type)
186 (with-slots (vars temp-index) codegen
187 (or (some (lambda (var)
188 (let ((name (inst-name var)))
189 (if (and (not (var-in-use-p name))
190 (c-type-equal-p type (inst-type var)))
191 name
192 nil)))
66836e14 193 (remove-if-not (lambda (var) (typep var 'var-inst)) vars))
dea4d055
MW
194 (let* ((name (make-instance 'temporary-variable
195 :in-use-p t
196 :tag (prog1 temp-index
197 (incf temp-index)))))
198 (push (make-var-inst name type nil) vars)
199 name))))
200
201;;;----- That's all, folks --------------------------------------------------