This repository has been archived on 2022-08-10. You can view files and clone it, but cannot push or open issues or pull requests.
chez-openbsd/ta6ob/examples/foreign.ss
2022-08-09 23:28:25 +02:00

180 lines
6.7 KiB
Scheme

;;; foreign.ss
;;; Copyright (c) 1997 R. Kent Dybvig
;;; Permission is hereby granted, free of charge, to any person obtaining a
;;; copy of this software and associated documentation files (the "Software"),
;;; to deal in the Software without restriction, including without limitation
;;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
;;; and/or sell copies of the Software, and to permit persons to whom the
;;; Software is furnished to do so, subject to the following conditions:
;;;
;;; The above copyright notice and this permission notice shall be included in
;;; all copies or substantial portions of the Software.
;;;
;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
;;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
;;; DEALINGS IN THE SOFTWARE.
;;; Prototype code for converting ``foreign-callable'' declarations into
;;; C interface routines to support C calls to Scheme procedures with
;;; automatic datatype conversion analogous to that provided for Scheme
;;; calls to C procedures via foreign-procedure.
;;; Todo
;;; - support for foreign-pointer and foreign-object
;;; - similar support for foreign-procedure declarations
(define spec->decl
(lambda (spec)
(case spec
[(integer-32 boolean) "int"]
[(unsigned-32) "unsigned int"]
[(char) "char"]
[(string) "char *"]
[(fixnum) "int"]
[(double-float) "double"]
[(single-float) "float"]
[(void) "void"]
[(scheme-object) "ptr"]
[else
(record-case spec
[(foreign-pointer foreign-object) ()
(error 'spec->decl "unsupported specifier ~s" spec)]
[else (error 'spec->decl "unexpected specifier ~s" spec)])])))
(define C->Scheme
(lambda (spec id)
(case spec
[(boolean) (format "Sboolean(~a)" id)]
[(char) (format "Schar(~a)" id)]
[(fixnum) (format "Sfixnum(~a)" id)]
[(integer-32) (format "Sinteger(~a)" id)]
[(unsigned-32) (format "Sunsigned(~a)" id)]
[(single-float) (format "Sflonum((double)~a)" id)]
[(double-float) (format "Sflonum(~a)" id)]
[(scheme-object) id]
[(string) (format "Sstring(~a)" id)]
[else
(record-case spec
[(foreign-pointer foreign-object) ()
(error 'C->Scheme "unsupported specifier ~s" spec)]
[else (error 'C->Scheme "unexpected specifier ~s" spec)])])))
(define Scheme->C
(lambda (op spec src)
(case spec
[(boolean) (fprintf op "Sboolean_value(~a)" src)]
[(char) (fprintf op "Schar_value(~a)" src)]
[(fixnum) (fprintf op "Sfixnum_value(~a)" src)]
[(integer-32) (fprintf op "Sinteger_value(~a)" src)]
[(unsigned-32) (fprintf op "Sunsigned_value(~a)" src)]
[(single-float) (fprintf op "(float)Sflonum_value(~a)" src)]
[(double-float) (fprintf op "Sflonum_value(~a)" src)]
[(scheme-object) (display src op)]
[(string) (fprintf op "Sstring_value(~a)" src)]
[else
(record-case spec
[(foreign-pointer foreign-object) ()
(error 'Scheme->C "unsupported specifier ~s" spec)]
[else (error 'Scheme->C "unexpected specifier ~s" spec)])])))
(define gen-fcallable
(case-lambda
[(cname arg-specs res-spec)
(gen-fcallable (current-output-port) cname arg-specs res-spec)]
[(op cname arg-specs res-spec)
(let ((names (let loop ((ls arg-specs) (i 1))
(if (null? ls)
'()
(cons (format "x~d" i) (loop (cdr ls) (+ i 1))))))
(count (length arg-specs)))
(newline op)
(fprintf op "~a ~a(ptr proc" (spec->decl res-spec) cname) ;)
(let loop ((arg-specs arg-specs) (names names))
(unless (null? arg-specs)
(fprintf op ", ~a ~a" (spec->decl (car arg-specs)) (car names))
(loop (cdr arg-specs) (cdr names)))) ;(
(fprintf op ") {~%")
(if (<= 0 count 3)
(begin
(display " return " op)
(Scheme->C op res-spec
(let ((op (open-output-string)))
(fprintf op "Scall~d(proc" count) ;)
(let loop ((arg-specs arg-specs) (names names))
(unless (null? arg-specs)
(display ", " op)
(display (C->Scheme (car arg-specs) (car names)) op)
(loop (cdr arg-specs) (cdr names)))) ;(
(fprintf op ")")
(get-output-string op))))
(begin
(fprintf op " Sinitframe(~d);~%" count)
(let loop ([arg-specs arg-specs] [names names] [num 1])
(unless (null? arg-specs)
(fprintf op " Sput_arg(~d, ~a);~%"
num (C->Scheme (car arg-specs) (car names)))
(loop (cdr arg-specs) (cdr names) (+ num 1))))
(fprintf op " return ")
(Scheme->C op res-spec
(format "Scall(proc, ~d)" count))))
(fprintf op ";~%}~%"))]))
(define-syntax foreign-callable
(syntax-rules ()
((_ n args res)
(gen-fcallable n 'args 'res))))
(define gen-file
(lambda (fnroot)
(let ((ifn (format "~a.ss" fnroot))
(ofn (format "~a.xx" fnroot)))
(with-output-to-file ofn
(lambda () (load ifn))
'replace))))
#!eof ; cut off the input here so we can give examples w/o comment chars
Example input file:
------------------------------------------------------------------------
(foreign-callable "foo"
(boolean single-float double-float)
scheme-object)
(foreign-callable "bar"
(boolean char integer-32 unsigned-32 single-float
double-float scheme-object)
string)
(foreign-callable "baz" () fixnum)
------------------------------------------------------------------------
Generated output file:
------------------------------------------------------------------------
ptr foo(ptr proc, int x1, float x2, double x3) {
return Scall3(proc, Sboolean(x1), Sflonum((double)x2), Sflonum(x3));
}
char * bar(ptr proc, int x1, char x2, int x3, unsigned int x4, float x5, double x6, ptr x7) {
Sinitframe(7);
Sput_arg(1, Sboolean(x1));
Sput_arg(2, Schar(x2));
Sput_arg(3, Sinteger(x3));
Sput_arg(4, Sunsigned(x4));
Sput_arg(5, Sflonum((double)x5));
Sput_arg(6, Sflonum(x6));
Sput_arg(7, x7);
return Sstring_value(Scall(proc, 7));
}
int baz(ptr proc) {
return Sfixnum_value(Scall0(proc));
}
------------------------------------------------------------------------