~ chicken-core (chicken-5) /chicken-ffi-syntax.scm
Trap1;;;; chicken-ffi-syntax.scm
2;
3; Copyright (c) 2008-2022, The CHICKEN Team
4; Copyright (c) 2000-2007, Felix L. Winkelmann
5; All rights reserved.
6;
7; Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8; conditions are met:
9;
10; Redistributions of source code must retain the above copyright notice, this list of conditions and the following
11; disclaimer.
12; Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
13; disclaimer in the documentation and/or other materials provided with the distribution.
14; Neither the name of the author nor the names of its contributors may be used to endorse or promote
15; products derived from this software without specific prior written permission.
16;
17; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
18; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
19; AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
20; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25; POSSIBILITY OF SUCH DAMAGE.
26
27
28(declare
29 (unit chicken-ffi-syntax)
30 (uses data-structures extras internal)
31 (disable-interrupts)
32 (fixnum))
33
34#+(not debugbuild)
35(declare
36 (no-bound-checks)
37 (no-procedure-checks))
38
39(import scheme
40 chicken.base
41 chicken.format
42 chicken.internal
43 chicken.platform
44 chicken.syntax
45 chicken.string)
46
47(include "common-declarations.scm")
48(include "mini-srfi-1.scm")
49
50(define ##sys#chicken-ffi-macro-environment
51 (let ((me0 (##sys#macro-environment)))
52
53;; IMPORTANT: These macros directly call fully qualified names from
54;; the "chicken.compiler.c-backend" and "chicken.compiler.support"
55;; modules. These are unbound in the interpreter, so check first:
56(define (compiler-only-er-transformer transformer)
57 (##sys#er-transformer
58 (lambda (form r c)
59 (if (feature? 'compiling)
60 (transformer form r c)
61 (syntax-error
62 (car form) "The FFI is not supported in interpreted mode")))))
63
64(##sys#extend-macro-environment
65 'define-external
66 `((define . ,(alist-ref 'define me0)) ; Or just me0?
67 (begin . ,(alist-ref 'begin me0))
68 (lambda . ,(alist-ref 'lambda me0)))
69 (compiler-only-er-transformer
70 (lambda (form r c)
71 (let* ((form (cdr form))
72 (quals (and (pair? form) (string? (car form))))
73 (var (and (not quals) (pair? form) (symbol? (car form)))) )
74 (cond [var
75 (##sys#check-syntax 'define-external form '(symbol _ . #(_ 0 1)))
76 (let ([var (car form)])
77 `(,(r 'begin)
78 (##core#define-foreign-variable ,var ,(cadr form))
79 (##core#define-external-variable ,var ,(cadr form) #t)
80 ,@(if (pair? (cddr form))
81 `((##core#set! ,var ,(caddr form)))
82 '() ) ) ) ]
83 [else
84 (if quals
85 (##sys#check-syntax 'define-external form '(string (symbol . #((_ symbol) 0)) _ . #(_ 1)))
86 (##sys#check-syntax 'define-external form '((symbol . #((_ symbol) 0)) _ . #(_ 1))) )
87 (let* ((head (if quals (cadr form) (car form)))
88 (args (cdr head)) )
89 `(,(r 'define) ,(car head)
90 (##core#foreign-callback-wrapper
91 (##core#quote ,(car head))
92 ,(if quals (car form) "")
93 (##core#quote ,(if quals (caddr form) (cadr form)))
94 (##core#quote ,(map (lambda (a) (car a)) args))
95 (,(r 'lambda)
96 ,(map (lambda (a) (cadr a)) args)
97 ,@(if quals (cdddr form) (cddr form)) ) ) ) ) ] ) ) ) ) )
98
99
100
101;;; External locations:
102
103(##sys#extend-macro-environment
104 'location
105 '()
106 (compiler-only-er-transformer
107 (lambda (x r c)
108 (##sys#check-syntax 'location x '(location _))
109 `(##core#location ,(cadr x)))))
110
111(##sys#extend-macro-environment
112 'define-location
113 `((begin . ,(alist-ref 'begin me0)))
114 (compiler-only-er-transformer
115 (lambda (form r c)
116 (##sys#check-syntax 'define-location form '(_ variable _ . #(_ 0 1)))
117 (let ((var (cadr form))
118 (type (caddr form))
119 (init (optional (cdddr form) #f))
120 (name (r (gensym))))
121 `(,(r 'begin)
122 (##core#define-foreign-variable ,var ,type ,(symbol->string name))
123 (##core#define-external-variable ,var ,type #f ,name)
124 ,@(if (pair? init)
125 `((##core#set! ,var ,(car init)))
126 '() ) ) ) ) ) )
127
128(##sys#extend-macro-environment
129 'let-location
130 '()
131 (compiler-only-er-transformer
132 (lambda (form r c)
133 (##sys#check-syntax 'let-location form '(_ #((variable _ . #(_ 0 1)) 0) . _))
134 (let* ((bindings (cadr form))
135 (body (cddr form))
136 (aliases (map (lambda (_) (r (gensym))) bindings)))
137 `(##core#let
138 ,(append-map
139 (lambda (b a)
140 (if (pair? (cddr b))
141 (list (cons a (cddr b)))
142 '() ) )
143 bindings aliases)
144 ,(let loop ((bindings bindings) (aliases aliases))
145 (if (null? bindings)
146 `(##core#let () ,@body)
147 (let ((b (car bindings))
148 (a (car aliases))
149 (rest (loop (cdr bindings) (cdr aliases))))
150 (if (= 3 (length b))
151 `(##core#let-location
152 ,(car b)
153 ,(cadr b)
154 ,a
155 ,rest)
156 `(##core#let-location
157 ,(car b)
158 ,(cadr b)
159 ,rest) ) ))))))))
160
161
162;;; Embedding code directly:
163
164(##sys#extend-macro-environment
165 'foreign-code
166 `((declare . ,(alist-ref 'declare me0)))
167 (compiler-only-er-transformer
168 (lambda (form r c)
169 (##sys#check-syntax 'foreign-code form '(_ . #(string 0)))
170 (let ([tmp (gensym 'code_)])
171 `(##core#begin
172 (,(r 'declare)
173 (foreign-declare
174 ,(sprintf "static C_word ~A() { ~A\n; return C_SCHEME_UNDEFINED; }\n"
175 tmp
176 (string-intersperse (cdr form) "\n")) ) )
177 (##core#inline ,tmp) ) ) ) ) )
178
179(##sys#extend-macro-environment
180 'foreign-value
181 '()
182 (compiler-only-er-transformer
183 (lambda (form r c)
184 (##sys#check-syntax 'foreign-value form '(_ _ _))
185 (let ((tmp (gensym "code_"))
186 (code (cadr form)))
187 `(##core#begin
188 (##core#define-foreign-variable ,tmp
189 ,(caddr form)
190 ,(cond ((string? code) code)
191 ((symbol? code) (symbol->string code))
192 (else
193 (syntax-error
194 'foreign-value
195 "bad argument type - not a string or symbol"
196 code))))
197 (##core#the ,(chicken.compiler.support#foreign-type->scrutiny-type
198 (chicken.syntax#strip-syntax (caddr form))
199 'result)
200 #f ,tmp) ) ) ) ) )
201
202
203;;; Include foreign code fragments
204
205(##sys#extend-macro-environment
206 'foreign-declare
207 '()
208 (compiler-only-er-transformer
209 (lambda (form r c)
210 (##sys#check-syntax 'foreign-declare form '(_ . #(string 0)))
211 `(##core#declare (foreign-declare ,@(cdr form))))))
212
213
214;;; Aliases for internal forms
215
216(define (annotate-foreign-procedure e argtypes rtype)
217 (let ((scrut-atypes (map (cut chicken.compiler.support#foreign-type->scrutiny-type <> 'arg)
218 (chicken.syntax#strip-syntax argtypes)))
219 (scrut-rtype (and rtype
220 (chicken.compiler.support#foreign-type->scrutiny-type
221 (chicken.syntax#strip-syntax rtype) 'result))))
222 ;; Don't add type annotation if the scrutinizer can infer the same
223 ;; or better.
224 ;;
225 ;; At least these cases should work:
226 ;;
227 ;; (-> <some-known-type>) => annotate
228 ;; (-> *) => no annotation
229 ;; (* ... -> *) => no annotation
230 ;;
231 (if (and (or (not rtype) (eq? scrut-rtype '*))
232 (every (cut eq? '* <>) scrut-atypes))
233 e
234 `(##core#the
235 (procedure ,scrut-atypes
236 ,@(if rtype
237 (list scrut-rtype)
238 ;; Special case for C_values(...). Only
239 ;; triggered by foreign-primitive.
240 '*))
241 #f
242 ,e))))
243
244(##sys#extend-macro-environment
245 'define-foreign-type
246 '()
247 (compiler-only-er-transformer
248 (lambda (form r c)
249 (##sys#check-syntax 'define-foreign-type form '(_ symbol _ . #(_ 0 2)))
250 `(##core#define-foreign-type ,@(cdr form)))))
251
252(##sys#extend-macro-environment
253 'define-foreign-variable
254 '()
255 (compiler-only-er-transformer
256 (lambda (form r c)
257 (##sys#check-syntax 'define-foreign-variable form '(_ symbol _ . #(string 0 1)))
258 `(##core#define-foreign-variable ,@(cdr form)))))
259
260(##sys#extend-macro-environment
261 'foreign-primitive
262 '()
263 (compiler-only-er-transformer
264 (lambda (form r c)
265 (##sys#check-syntax 'foreign-primitive form '(_ _ . _))
266 (let* ((hasrtype (and (pair? (cddr form)) (not (string? (caddr form)))))
267 (rtype (and hasrtype (cadr form)))
268 (args (if hasrtype (caddr form) (cadr form)))
269 (argtypes (map car args)))
270 (annotate-foreign-procedure `(##core#foreign-primitive ,@(cdr form))
271 argtypes
272 rtype)))))
273
274(##sys#extend-macro-environment
275 'foreign-lambda
276 '()
277 (compiler-only-er-transformer
278 (lambda (form r c)
279 (##sys#check-syntax 'foreign-lambda form '(_ _ _ . _))
280 (annotate-foreign-procedure `(##core#foreign-lambda ,@(cdr form))
281 (cdddr form)
282 (cadr form)))))
283
284(##sys#extend-macro-environment
285 'foreign-lambda*
286 '()
287 (compiler-only-er-transformer
288 (lambda (form r c)
289 (##sys#check-syntax 'foreign-lambda* form '(_ _ _ _ . _))
290 (annotate-foreign-procedure `(##core#foreign-lambda* ,@(cdr form))
291 (map car (caddr form))
292 (cadr form)))))
293
294(##sys#extend-macro-environment
295 'foreign-safe-lambda
296 '()
297 (compiler-only-er-transformer
298 (lambda (form r c)
299 (##sys#check-syntax 'foreign-safe-lambda form '(_ _ _ . _))
300 (annotate-foreign-procedure `(##core#foreign-safe-lambda ,@(cdr form))
301 (cdddr form)
302 (cadr form)))))
303
304(##sys#extend-macro-environment
305 'foreign-safe-lambda*
306 '()
307 (compiler-only-er-transformer
308 (lambda (form r c)
309 (##sys#check-syntax 'foreign-safe-lambda* form '(_ _ _ _ . _))
310 (annotate-foreign-procedure `(##core#foreign-safe-lambda* ,@(cdr form))
311 (map car (caddr form))
312 (cadr form)))))
313
314(##sys#extend-macro-environment
315 'foreign-type-size
316 '()
317 (compiler-only-er-transformer
318 (lambda (form r c)
319 (##sys#check-syntax 'foreign-type-size form '(_ _))
320 (let* ((t (chicken.syntax#strip-syntax (cadr form)))
321 (tmp (gensym "code_"))
322 (decl
323 (if (string? t)
324 t
325 ;; TODO: Backend should be configurable
326 (chicken.compiler.c-backend#foreign-type-declaration t ""))))
327 `(##core#begin
328 (##core#define-foreign-variable ,tmp size_t ,(string-append "sizeof(" decl ")"))
329 (##core#the fixnum #f ,tmp))))))
330
331
332(macro-subset me0)))