mirror of
https://github.com/ashinn/chibi-scheme.git
synced 2025-05-20 22:29:16 +02:00
1300 lines
48 KiB
Scheme
Executable file
1300 lines
48 KiB
Scheme
Executable file
#! /usr/bin/env chibi-scheme
|
|
|
|
;; Note: this evolved as a throw-away script to provide certain core
|
|
;; modules, and so is a mess. Tread carefully.
|
|
|
|
;; Simple C FFI. "genstubs.scm file.stub" will read in the C function
|
|
;; FFI definitions from file.stub and output the appropriate C
|
|
;; wrappers into file.c. You can then compile that file with:
|
|
;;
|
|
;; cc -fPIC -shared file.c -lchibi-scheme
|
|
;;
|
|
;; (or using whatever flags are appropriate to generate shared libs on
|
|
;; your platform) and then the generated .so file can be loaded
|
|
;; directly with load, or portably using (include-shared "file") in a
|
|
;; module definition (note that include-shared uses no suffix).
|
|
|
|
;; The goal of this interface is to make access to C types and
|
|
;; functions easy, without requiring the user to write any C code.
|
|
;; That means the stubber needs to be intelligent about various C
|
|
;; calling conventions and idioms, such as return values passed in
|
|
;; actual parameters. Writing C by hand is still possible, and
|
|
;; several of the core modules provide C interfaces directly without
|
|
;; using the stubber.
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; Struct Interface
|
|
;;
|
|
;; (define-c-struct struct-name
|
|
;; [predicate: predicate-name]
|
|
;; [constructor: constructor-name]
|
|
;; [finalizer: c_finalizer_name]
|
|
;; (type c_field_name getter-name setter-name) ...)
|
|
;;
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; Function Interface
|
|
;;
|
|
;; (define-c return-type name-spec (arg-type ...))
|
|
;;
|
|
;; where name-space is either a symbol name, or a list of
|
|
;; (scheme-name c_name). If just a symbol, the C name is taken
|
|
;; to be the same with -'s replaced by _'s.
|
|
;;
|
|
;; arg-type is a type suitable for input validation and conversion.
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; Types
|
|
;;
|
|
;; Types
|
|
;;
|
|
;; Basic Types
|
|
;; void
|
|
;; boolean
|
|
;; char
|
|
;; sexp (no conversions)
|
|
;;
|
|
;; Integer Types:
|
|
;; signed-char short int long
|
|
;; unsigned-char unsigned-short unsigned-int unsigned-long size_t pid_t
|
|
;; time_t (in seconds, but using the chibi epoch of 2010/01/01)
|
|
;; errno (as a return type returns #f on error)
|
|
;;
|
|
;; Float Types:
|
|
;; float double long-double
|
|
;;
|
|
;; String Types:
|
|
;; string - a null-terminated char*
|
|
;; env-string - a VAR=VALUE string represented as a (VAR . VALUE) pair inScheme
|
|
;; in addition you can use (array char) as a string
|
|
;;
|
|
;; Port Types:
|
|
;; input-port output-port
|
|
;; port-or-fd - an fd-backed port or a fixnum
|
|
;;
|
|
;; Struct Types:
|
|
;;
|
|
;; Struct types are by default just referred to by the bare
|
|
;; struct-name from define-c-struct, and it is assumed you want a
|
|
;; pointer to that type. To refer to the full struct, use the struct
|
|
;; modifier, as in (struct struct-name).
|
|
|
|
;; Type modifiers
|
|
;;
|
|
;; Any type may also be written as a list of modifiers followed by the
|
|
;; type itself. The supported modifiers are:
|
|
;;
|
|
;; const: prepends the "const" C type modifier
|
|
;; * as a return or result parameter, makes non-immediates immutable
|
|
;;
|
|
;; free: it's Scheme's responsibility to "free" this resource
|
|
;; * as a return or result parameter, registers the freep flag
|
|
;; this causes the type finalizer to be run when GCed
|
|
;;
|
|
;; maybe-null: this pointer type may be NULL
|
|
;; * as a result parameter, NULL is translated to #f
|
|
;; normally this would just return a wrapped NULL pointer
|
|
;; * as an input parameter, #f is translated to NULL
|
|
;; normally this would be a type error
|
|
;;
|
|
;; pointer: create a pointer to this type
|
|
;; * as a return parameter, wraps the result in a vanilla cpointer
|
|
;; * as a result parameter, boxes then unboxes the value
|
|
;;
|
|
;; struct: treat this struct type as a struct, not a pointer
|
|
;; * as an input parameter, dereferences the pointer
|
|
;; * as a type field, indicates a nested struct
|
|
;;
|
|
;; link: add a gc link
|
|
;; * as a field getter, link to the parent object, so the
|
|
;; parent won't be GCed so long as we have a reference
|
|
;; to the child. this behavior is automatic for nested
|
|
;; structs.
|
|
;;
|
|
;; result: return a result in this parameter
|
|
;; * if there are multiple results (including the return type),
|
|
;; they are all returned in a list
|
|
;; * if there are any result parameters, a return type
|
|
;; of errno returns #f on failure, and as eliminated
|
|
;; from the list of results otherwise
|
|
;;
|
|
;; (value <expr>): specify a fixed value
|
|
;; * as an input parameter, this parameter is not provided
|
|
;; in the Scheme API but always passed as <expr>
|
|
;;
|
|
;; (default <expr>): specify a default value
|
|
;; * as the final input parameter, makes the Scheme parameter
|
|
;; optional, defaulting to <expr>
|
|
;;
|
|
;; (array <type> [<length>]) an array type
|
|
;; * length must be specified for return and result parameters
|
|
;; * if specified, length can be any of
|
|
;; ** an integer, for a fixed size
|
|
;; ** the symbol null, indicating a NULL-terminated array
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; globals
|
|
|
|
(define *types* '())
|
|
(define *funcs* '())
|
|
(define *consts* '())
|
|
(define *inits* '())
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; type objects
|
|
|
|
(define (parse-type type . o)
|
|
(cond
|
|
((vector? type)
|
|
type)
|
|
(else
|
|
(let lp ((type type) (free? #f) (const? #f) (null-ptr? #f)
|
|
(ptr? #f) (struct? #f) (link? #f) (result? #f) (array #f)
|
|
(value #f) (default? #f))
|
|
(define (next) (if (null? (cddr type)) (cadr type) (cdr type)))
|
|
(case (and (pair? type) (car type))
|
|
((free)
|
|
(lp (next) #t const? null-ptr? ptr? struct? link? result? array value default?))
|
|
((const)
|
|
(lp (next) free? #t null-ptr? ptr? struct? link? result? array value default?))
|
|
((maybe-null)
|
|
(lp (next) free? const? #t ptr? struct? link? result? array value default?))
|
|
((pointer)
|
|
(lp (next) free? const? null-ptr? #t struct? link? result? array value default?))
|
|
((struct)
|
|
(lp (next) free? const? null-ptr? ptr? #t link? result? array value default?))
|
|
((link)
|
|
(lp (next) free? const? null-ptr? ptr? struct? #t result? array value default?))
|
|
((result)
|
|
(lp (next) free? const? null-ptr? ptr? struct? link? #t array value default?))
|
|
((array)
|
|
(lp (cadr type) free? const? null-ptr? ptr? struct? link? result? (if (pair? (cddr type)) (caddr type) #t) value default?))
|
|
((value)
|
|
(lp (cddr type) free? const? null-ptr? ptr? struct? link? result? array (cadr type) default?))
|
|
((default)
|
|
(lp (cddr type) free? const? null-ptr? ptr? struct? link? result? array (cadr type) #t))
|
|
(else
|
|
(vector (if (and (pair? type) (null? (cdr type))) (car type) type) free? const? null-ptr? ptr? struct? link? result? array value default? (and (pair? o) (car o)))))))))
|
|
|
|
(define (type-base type) (vector-ref type 0))
|
|
(define (type-free? type) (vector-ref type 1))
|
|
(define (type-const? type) (vector-ref type 2))
|
|
(define (type-null? type) (vector-ref type 3))
|
|
(define (type-pointer? type) (vector-ref type 4))
|
|
(define (type-struct? type) (vector-ref type 5))
|
|
(define (type-link? type) (vector-ref type 6))
|
|
(define (type-result? type) (vector-ref type 7))
|
|
(define (type-array type) (vector-ref type 8))
|
|
(define (type-value type) (vector-ref type 9))
|
|
(define (type-default? type) (vector-ref type 10))
|
|
(define (type-index type) (vector-ref type 11))
|
|
|
|
(define (type-auto-expand? type)
|
|
(and (pair? (type-array type))
|
|
(memq 'auto-expand (type-array type))))
|
|
|
|
(define (type-index-string type)
|
|
(if (integer? (type-index type))
|
|
(number->string (type-index type))
|
|
""))
|
|
|
|
(define (struct-fields ls)
|
|
(let lp ((ls ls) (res '()))
|
|
(cond ((null? ls) (reverse res))
|
|
((symbol? (car ls)) (lp (cddr ls) res))
|
|
(else (lp (cdr ls) (cons (car ls) res))))))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; type predicates
|
|
|
|
(define (signed-int-type? type)
|
|
(memq type '(signed-char short int long boolean)))
|
|
|
|
(define (unsigned-int-type? type)
|
|
(memq type '(unsigned-char unsigned-short unsigned unsigned-int unsigned-long
|
|
size_t off_t time_t clock_t dev_t ino_t mode_t nlink_t
|
|
uid_t gid_t pid_t blksize_t blkcnt_t sigval_t)))
|
|
|
|
(define (int-type? type)
|
|
(or (signed-int-type? type) (unsigned-int-type? type)))
|
|
|
|
(define (float-type? type)
|
|
(memq type '(float double long-double long-long-double)))
|
|
|
|
(define (string-type? type)
|
|
(or (memq type '(char* string env-string non-null-string))
|
|
(and (vector? type)
|
|
(type-array type)
|
|
(not (type-pointer? type))
|
|
(eq? 'char (type-base type)))))
|
|
|
|
(define (port-type? type)
|
|
(memq type '(port input-port output-port)))
|
|
|
|
(define (error-type? type)
|
|
(memq type '(errno non-null-string non-null-pointer)))
|
|
|
|
(define (array-type? type)
|
|
(and (type-array type) (not (eq? 'char (type-base type)))))
|
|
|
|
(define (basic-type? type)
|
|
(let ((type (parse-type type)))
|
|
(and (not (type-array type))
|
|
(not (void-pointer-type? type))
|
|
(not (assq (type-base type) *types*)))))
|
|
|
|
(define (void-pointer-type? type)
|
|
(or (and (eq? 'void (type-base type)) (type-pointer? type))
|
|
(eq? 'void* (type-base type))))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; function objects
|
|
|
|
(define (parse-func func)
|
|
(if (not (and (= 3 (length func))
|
|
(or (identifier? (cadr func)) (list (cadr func)))
|
|
(list (caddr func))))
|
|
(error "bad function definition" func))
|
|
(let* ((ret-type (parse-type (car func)))
|
|
(scheme-name (if (pair? (cadr func)) (caadr func) (cadr func)))
|
|
(c-name (if (pair? (cadr func))
|
|
(cadadr func)
|
|
(mangle scheme-name)))
|
|
(stub-name (if (and (pair? (cadr func)) (pair? (cddadr func)))
|
|
(car (cddadr func))
|
|
(generate-stub-name scheme-name))))
|
|
(let lp ((ls (caddr func))
|
|
(i 0)
|
|
(results '())
|
|
(c-args '())
|
|
(s-args '()))
|
|
(cond
|
|
((null? ls)
|
|
(vector scheme-name c-name stub-name ret-type
|
|
(reverse results) (reverse c-args) (reverse s-args)))
|
|
(else
|
|
(let ((type (parse-type (car ls) i)))
|
|
(cond
|
|
((type-result? type)
|
|
(lp (cdr ls) (+ i 1) (cons type results) (cons type c-args) s-args))
|
|
((and (type-value type) (not (type-default? type)))
|
|
(lp (cdr ls) (+ i 1) results (cons type c-args) s-args))
|
|
(else
|
|
(lp (cdr ls) (+ i 1) results (cons type c-args) (cons type s-args)))
|
|
)))))))
|
|
|
|
(define (func-scheme-name func) (vector-ref func 0))
|
|
(define (func-c-name func) (vector-ref func 1))
|
|
(define (func-stub-name func) (vector-ref func 2))
|
|
(define (func-ret-type func) (vector-ref func 3))
|
|
(define (func-results func) (vector-ref func 4))
|
|
(define (func-c-args func) (vector-ref func 5))
|
|
(define (func-scheme-args func) (vector-ref func 6))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; utilities
|
|
|
|
(define (cat . args)
|
|
(for-each (lambda (x) (if (procedure? x) (x) (display x))) args))
|
|
|
|
(define (x->string x)
|
|
(cond ((string? x) x)
|
|
((symbol? x) (symbol->string x))
|
|
((number? x) (number->string x))
|
|
(else (error "non-stringable object" x))))
|
|
|
|
(define (filter pred ls)
|
|
(cond ((null? ls) '())
|
|
((pred (car ls)) (cons (car ls) (filter pred (cdr ls))))
|
|
(else (filter pred (cdr ls)))))
|
|
|
|
(define (remove pred ls)
|
|
(cond ((null? ls) '())
|
|
((pred (car ls)) (filter pred (cdr ls)))
|
|
(else (cons (car ls) (filter pred (cdr ls))))))
|
|
|
|
(define (strip-extension path)
|
|
(let lp ((i (- (string-length path) 1)))
|
|
(cond ((<= i 0) path)
|
|
((eq? #\. (string-ref path i)) (substring path 0 i))
|
|
(else (lp (- i 1))))))
|
|
|
|
(define (string-concatenate-reverse ls)
|
|
(cond ((null? ls) "")
|
|
((null? (cdr ls)) (car ls))
|
|
(else (string-concatenate (reverse ls)))))
|
|
|
|
(define (string-replace str c r)
|
|
(let ((len (string-length str)))
|
|
(let lp ((from 0) (i 0) (res '()))
|
|
(define (collect) (if (= i from) res (cons (substring str from i) res)))
|
|
(cond
|
|
((>= i len) (string-concatenate-reverse (collect)))
|
|
((eqv? c (string-ref str i)) (lp (+ i 1) (+ i 1) (cons r (collect))))
|
|
(else (lp from (+ i 1) res))))))
|
|
|
|
(define (string-scan c str . o)
|
|
(let ((limit (string-length str)))
|
|
(let lp ((i (if (pair? o) (car o) 0)))
|
|
(cond ((>= i limit) #f)
|
|
((eqv? c (string-ref str i)) i)
|
|
(else (lp (+ i 1)))))))
|
|
|
|
(define (string-downcase str)
|
|
(list->string (map char-downcase (string->list str))))
|
|
|
|
(define (with-output-to-string thunk)
|
|
(call-with-output-string
|
|
(lambda (out)
|
|
(let ((old-out (current-output-port)))
|
|
(current-output-port out)
|
|
(thunk)
|
|
(current-output-port old-out)))))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; naming
|
|
|
|
(define (c-char? c)
|
|
(or (char-alphabetic? c) (char-numeric? c) (memv c '(#\_ #\- #\! #\?))))
|
|
|
|
(define (c-escape str)
|
|
(let ((len (string-length str)))
|
|
(let lp ((from 0) (i 0) (res '()))
|
|
(define (collect) (if (= i from) res (cons (substring str from i) res)))
|
|
(cond
|
|
((>= i len) (string-concatenate-reverse (collect)))
|
|
((not (c-char? (string-ref str i))) (lp (+ i 1) (+ i 1) (cons "_" (cons (number->string (char->integer (string-ref str i)) 16) (collect)))))
|
|
(else (lp from (+ i 1) res))))))
|
|
|
|
(define (mangle x)
|
|
(string-replace
|
|
(string-replace (string-replace (c-escape (x->string x)) #\- "_") #\? "_p")
|
|
#\! "_x"))
|
|
|
|
(define (generate-stub-name sym)
|
|
(string-append "sexp_" (mangle sym) "_stub"))
|
|
|
|
(define (type-id-name sym)
|
|
(string-append "sexp_" (mangle sym) "_type_t"))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; .stub file interface
|
|
|
|
(define (c-declare . args)
|
|
(apply cat args)
|
|
(newline))
|
|
|
|
(define (c-include header)
|
|
(cat "\n#include \"" header "\"\n"))
|
|
|
|
(define (c-system-include header)
|
|
(cat "\n#include <" header ">\n"))
|
|
|
|
(define (c-init x)
|
|
(set! *inits* (cons x *inits*)))
|
|
|
|
(define (parse-struct-like ls)
|
|
(let lp ((ls ls) (res '()))
|
|
(cond
|
|
((null? ls)
|
|
(reverse res))
|
|
((symbol? (car ls))
|
|
(lp (cddr ls) (cons (cadr ls) (cons (car ls) res))))
|
|
((pair? (car ls))
|
|
(lp (cdr ls) (cons (cons (parse-type (caar ls)) (cdar ls)) res)))
|
|
(else
|
|
(lp (cdr ls) (cons (car ls) res))))))
|
|
|
|
(define-syntax define-struct-like
|
|
(er-macro-transformer
|
|
(lambda (expr rename compare)
|
|
(set! *types*
|
|
`((,(cadr expr)
|
|
,@(parse-struct-like (cddr expr)))
|
|
,@*types*))
|
|
`(cat "\nstatic sexp " ,(type-id-name (cadr expr)) ";\n"))))
|
|
|
|
(define-syntax define-c-struct
|
|
(er-macro-transformer
|
|
(lambda (expr rename compare)
|
|
`(define-struct-like ,(cadr expr) type: struct ,@(cddr expr)))))
|
|
|
|
(define-syntax define-c-class
|
|
(er-macro-transformer
|
|
(lambda (expr rename compare)
|
|
`(define-struct-like ,(cadr expr) type: class ,@(cddr expr)))))
|
|
|
|
(define-syntax define-c-union
|
|
(er-macro-transformer
|
|
(lambda (expr rename compare)
|
|
`(define-struct-like ,(cadr expr) type: union ,@(cddr expr)))))
|
|
|
|
(define-syntax define-c-type
|
|
(er-macro-transformer
|
|
(lambda (expr rename compare)
|
|
`(define-struct-like ,(cadr expr) ,@(cddr expr)))))
|
|
|
|
(define-syntax define-c
|
|
(er-macro-transformer
|
|
(lambda (expr rename compare)
|
|
(set! *funcs* (cons (parse-func (cdr expr)) *funcs*))
|
|
#f)))
|
|
|
|
(define-syntax define-c-const
|
|
(er-macro-transformer
|
|
(lambda (expr rename compare)
|
|
(let ((type (parse-type (cadr expr))))
|
|
(for-each (lambda (x) (set! *consts* (cons (list type x) *consts*)))
|
|
(cddr expr))))))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; C code generation
|
|
|
|
(define (c->scheme-converter type val . o)
|
|
(let ((base (type-base type)))
|
|
(cond
|
|
((and (eq? base 'void) (not (type-pointer? type)))
|
|
(cat "((" val "), SEXP_VOID)"))
|
|
((or (eq? base 'sexp) (error-type? base))
|
|
(cat val))
|
|
((eq? base 'boolean)
|
|
(cat "sexp_make_boolean(" val ")"))
|
|
((eq? base 'time_t)
|
|
(cat "sexp_make_integer(ctx, sexp_shift_epoch(" val "))"))
|
|
((unsigned-int-type? base)
|
|
(cat "sexp_make_unsigned_integer(ctx, " val ")"))
|
|
((signed-int-type? base)
|
|
(cat "sexp_make_integer(ctx, " val ")"))
|
|
((float-type? base)
|
|
(cat "sexp_make_flonum(ctx, " val ")"))
|
|
((eq? base 'char)
|
|
(if (type-array type)
|
|
(cat "sexp_c_string(ctx, " val ", -1)")
|
|
(cat "sexp_make_character(ctx, " val ")")))
|
|
((eq? 'env-string base)
|
|
(cat "(p=strchr(" val ", '=') ? "
|
|
"sexp_cons(ctx, str=sexp_c_string(ctx, " val ", p - " val "), str=sexp_c_string(ctx, p, -1))"
|
|
" : sexp_cons(ctx, str=" val ", SEXP_FALSE)"))
|
|
((string-type? base)
|
|
(cat "sexp_c_string(ctx, " val ", -1)"))
|
|
((eq? 'input-port base)
|
|
(cat "sexp_make_input_port(ctx, " val ", SEXP_FALSE)"))
|
|
((eq? 'output-port base)
|
|
(cat "sexp_make_output_port(ctx, " val ", SEXP_FALSE)"))
|
|
(else
|
|
(let ((ctype (assq base *types*))
|
|
(void*? (void-pointer-type? type)))
|
|
(cond
|
|
((or ctype void*?)
|
|
(cat "sexp_make_cpointer(ctx, "
|
|
(if void*?
|
|
"SEXP_CPOINTER"
|
|
(string-append "sexp_type_tag(" (type-id-name base) ")"))
|
|
", "
|
|
val ", " (or (and (pair? o) (car o)) "SEXP_FALSE") ", "
|
|
(if (or (type-free? type)
|
|
(and (type-result? type) (not (basic-type? type))))
|
|
1
|
|
0)
|
|
")"))
|
|
(else
|
|
(error "unknown type" base))))))))
|
|
|
|
(define (scheme->c-converter type val)
|
|
(let* ((type (parse-type type))
|
|
(base (type-base type)))
|
|
(cond
|
|
((eq? base 'sexp)
|
|
(cat val))
|
|
((eq? base 'boolean)
|
|
(cat "sexp_truep(" val ")"))
|
|
((eq? base 'time_t)
|
|
(cat "sexp_unshift_epoch(sexp_uint_value(" val "))"))
|
|
((signed-int-type? base)
|
|
(cat "sexp_sint_value(" val ")"))
|
|
((unsigned-int-type? base)
|
|
(cat "sexp_uint_value(" val ")"))
|
|
((float-type? base)
|
|
(cat "sexp_flonum_value(" val ")"))
|
|
((eq? base 'char)
|
|
(cat "sexp_unbox_character(" val ")"))
|
|
((eq? base 'env-string)
|
|
(cat "sexp_concat_env_string(" val ")"))
|
|
((string-type? base)
|
|
(cat "sexp_string_data(" val ")"))
|
|
((eq? base 'port-or-fd)
|
|
(cat "(sexp_portp(" val ") ? fileno(sexp_port_stream(" val "))"
|
|
" : sexp_unbox_fixnum(" val "))"))
|
|
((port-type? base)
|
|
(cat "sexp_port_stream(" val ")"))
|
|
(else
|
|
(let ((ctype (assq base *types*))
|
|
(void*? (void-pointer-type? type)))
|
|
(cond
|
|
((or ctype void*?)
|
|
(cat "(" (type-c-name type) ")"
|
|
(if (type-null? type)
|
|
"sexp_cpointer_maybe_null_value"
|
|
"sexp_cpointer_value")
|
|
"(" val ")"))
|
|
(else
|
|
(error "unknown type" base))))))))
|
|
|
|
(define (type-predicate type)
|
|
(let ((base (type-base (parse-type type))))
|
|
(cond
|
|
((int-type? base) "sexp_exact_integerp")
|
|
((float-type? base) "sexp_flonump")
|
|
((string-type? base) "sexp_stringp")
|
|
((eq? base 'char) "sexp_charp")
|
|
((eq? base 'boolean) "sexp_booleanp")
|
|
((eq? base 'port) "sexp_portp")
|
|
((eq? base 'input-port) "sexp_iportp")
|
|
((eq? base 'output-port) "sexp_oportp")
|
|
(else #f))))
|
|
|
|
(define (type-name type)
|
|
(let ((base (type-base (parse-type type))))
|
|
(cond
|
|
((int-type? base) "integer")
|
|
((float-type? base) "flonum")
|
|
((eq? 'boolean base) "int")
|
|
(else base))))
|
|
|
|
(define (base-type-c-name base)
|
|
(case base
|
|
((string env-string non-null-string) "char*")
|
|
(else (symbol->string base))))
|
|
|
|
(define (type-struct-type type)
|
|
(let ((type-spec (assq (if (vector? type) (type-base type) type) *types*)))
|
|
(cond ((and type-spec (memq 'type: type-spec)) => cadr)
|
|
(else #f))))
|
|
|
|
(define (type-c-name type)
|
|
(let* ((type (parse-type type))
|
|
(base (type-base type))
|
|
(type-spec (assq base *types*))
|
|
(struct-type (type-struct-type type)))
|
|
(string-append
|
|
(if (type-const? type) "const " "")
|
|
(if struct-type (string-append (symbol->string struct-type) " ") "")
|
|
(string-replace (base-type-c-name base) #\- " ")
|
|
(if struct-type "*" "")
|
|
(if (type-pointer? type) "*" ""))))
|
|
|
|
(define (check-type arg type)
|
|
(let* ((type (parse-type type))
|
|
(base (type-base type)))
|
|
(cond
|
|
((eq? base 'env-string)
|
|
(cat "(sexp_pairp(" arg ") && sexp_stringp(sexp_car(" arg
|
|
")) && sexp_stringp(sexp_cdr(" arg ")))"))
|
|
((or (int-type? base) (float-type? base)
|
|
(string-type? base) (port-type? base))
|
|
(cat (type-predicate type) "(" arg ")"))
|
|
((or (assq base *types*) (void-pointer-type? type))
|
|
(cat
|
|
(if (type-null? type) "(" "")
|
|
"(sexp_pointerp(" arg ")"
|
|
" && (sexp_pointer_tag(" arg ") == "
|
|
(if (void-pointer-type? type)
|
|
"SEXP_CPOINTER"
|
|
(string-append "sexp_type_tag(" (type-id-name base) ")"))
|
|
"))"
|
|
(lambda () (if (type-null? type) (cat " || sexp_not(" arg "))")))))
|
|
(else
|
|
(display "WARNING: don't know how to check: " (current-error-port))
|
|
(write type (current-error-port))
|
|
(newline (current-error-port))
|
|
(cat "1")))))
|
|
|
|
(define (type-id-number type)
|
|
(let ((base (type-base type)))
|
|
(cond
|
|
((int-type? base) "SEXP_FIXNUM")
|
|
((float-type? base) "SEXP_FLONUM")
|
|
((string-type? base) "SEXP_STRING")
|
|
((eq? base 'char) "SEXP_CHAR")
|
|
((eq? base 'boolean) "SEXP_BOOLEAN")
|
|
((eq? base 'port) "SEXP_IPORT")
|
|
((eq? base 'input-port) "SEXP_IPORT")
|
|
((eq? base 'output-port) "SEXP_OPORT")
|
|
((void-pointer-type? type) "SEXP_CPOINTER")
|
|
(else (string-append "sexp_type_tag(" (type-id-name base) ")")))))
|
|
|
|
(define (write-validator arg type)
|
|
(let* ((type (parse-type type))
|
|
(array (type-array type))
|
|
(base-type (type-base type)))
|
|
(cond
|
|
((and array (not (string-type? type)))
|
|
(cond
|
|
((number? array)
|
|
(cat " if (!sexp_listp(ctx, " arg ")"
|
|
" || sexp_unbox_fixnum(sexp_length(" arg ")) != " array ")\n"
|
|
" return sexp_type_exception(ctx, self, SEXP_PAIR, " arg ");\n")))
|
|
(cat " for (res=" arg "; sexp_pairp(res); res=sexp_cdr(res))\n"
|
|
" if (! " (lambda () (check-type "sexp_car(res)" type)) ")\n"
|
|
" return sexp_xtype_exception(ctx, self, \"not a list of "
|
|
(type-name type) "s\", " arg ");\n")
|
|
(if (not (number? array))
|
|
(cat " if (! sexp_nullp(res))\n"
|
|
" return sexp_xtype_exception(ctx, self, \"not a list of "
|
|
(type-name type) "s\", " arg ");\n")))
|
|
((eq? base-type 'port-or-fd)
|
|
(cat " if (! (sexp_portp(" arg ") || sexp_fixnump(" arg ")))\n"
|
|
" return sexp_xtype_exception(ctx, self, \"not a port or file descriptor\"," arg ");\n"))
|
|
((or (int-type? base-type)
|
|
(float-type? base-type)
|
|
(string-type? base-type)
|
|
(port-type? base-type))
|
|
(cat
|
|
" if (! " (lambda () (check-type arg type)) ")\n"
|
|
" return sexp_type_exception(ctx, self, "
|
|
(type-id-number type) ", " arg ");\n"))
|
|
((or (assq base-type *types*) (void-pointer-type? type))
|
|
(cat
|
|
" if (! " (lambda () (check-type arg type)) ")\n"
|
|
" return sexp_type_exception(ctx, self, "
|
|
(type-id-number type) ", " arg ");\n"))
|
|
((eq? 'sexp base-type))
|
|
((string-type? type)
|
|
(write-validator arg 'string))
|
|
(else
|
|
(display "WARNING: don't know how to validate: " (current-error-port))
|
|
(write type (current-error-port))
|
|
(newline (current-error-port))))))
|
|
|
|
(define (write-parameters args)
|
|
(lambda () (for-each (lambda (a) (cat ", sexp arg" (type-index a))) args)))
|
|
|
|
(define (get-array-length func x)
|
|
(let ((len (if (pair? (type-array x))
|
|
(car (reverse (type-array x)))
|
|
(type-array x))))
|
|
(if (number? len)
|
|
len
|
|
(and (symbol? len)
|
|
(let* ((str (symbol->string len))
|
|
(len2 (string-length str)))
|
|
(and (> len2 3)
|
|
(string=? "arg" (substring str 0 3))
|
|
(let ((i (string->number (substring str 3 len2))))
|
|
(if i
|
|
(let ((y (list-ref (func-c-args func) i)))
|
|
(or (type-value y) len))))))))))
|
|
|
|
(define (write-locals func)
|
|
(define (arg-res x)
|
|
(string-append "res" (type-index-string x)))
|
|
(let* ((ret-type (func-ret-type func))
|
|
(results (func-results func))
|
|
(scheme-args (func-scheme-args func))
|
|
(return-res? (not (error-type? (type-base ret-type))))
|
|
(preserve-res? (> (+ (length results)) (if return-res? 0 1)))
|
|
(single-res? (and (= 1 (length results)) (not return-res?)))
|
|
(tmp-string? (any (lambda (a)
|
|
(and (type-array a)
|
|
(string-type? (type-base a))))
|
|
(cons ret-type results)))
|
|
(gc-vars (map arg-res results))
|
|
(gc-vars (if tmp-string? (cons "str" gc-vars) gc-vars))
|
|
(gc-vars (if preserve-res? (cons "res" gc-vars) gc-vars))
|
|
(sexps (if preserve-res? '() '("res")))
|
|
(num-gc-vars (length gc-vars))
|
|
(ints (if (or return-res?
|
|
(memq (type-base ret-type)
|
|
'(non-null-string non-null-pointer)))
|
|
'()
|
|
'("err")))
|
|
(ints (if (or (array-type? ret-type)
|
|
(any array-type? results)
|
|
(any array-type? scheme-args))
|
|
(cons "i" ints)
|
|
ints)))
|
|
(case (type-base ret-type)
|
|
((non-null-string) (cat " char *err;\n"))
|
|
((non-null-pointer) (cat " void *err;\n")))
|
|
(cond
|
|
((pair? ints)
|
|
(cat " int " (car ints))
|
|
(for-each (lambda (x) (display ", ") (display x)) (cdr ints))
|
|
(cat ";\n")))
|
|
(if (any (lambda (a) (eq? 'env-string (type-base a)))
|
|
(cons ret-type results))
|
|
(cat " char *p;\n"))
|
|
(for-each
|
|
(lambda (x)
|
|
(let ((len (get-array-length func x)))
|
|
(cat " " (type-c-name (type-base x)) " ")
|
|
(if (or (and (type-array x) (not (number? len))) (type-pointer? x))
|
|
(cat "*"))
|
|
(cat (if (type-auto-expand? x) "buf" "tmp") (type-index-string x))
|
|
(if (number? len)
|
|
(cat "[" len "]"))
|
|
(cat ";\n")
|
|
(if (or (vector? len) (type-auto-expand? x))
|
|
(cat " int len" (type-index x) ";\n"))
|
|
(if (type-auto-expand? x)
|
|
(cat " " (type-c-name (type-base x))
|
|
" *tmp" (type-index-string x) ";\n"))))
|
|
(append (if (or (type-array ret-type) (type-pointer? ret-type))
|
|
(list ret-type)
|
|
'())
|
|
results
|
|
(remove type-result? (filter type-array scheme-args))))
|
|
(for-each
|
|
(lambda (arg)
|
|
(cond
|
|
((and (type-pointer? arg) (basic-type? arg))
|
|
(cat " " (type-c-name (type-base arg))
|
|
" tmp" (type-index arg) ";\n"))))
|
|
scheme-args)
|
|
(cond
|
|
((pair? sexps)
|
|
(cat " sexp " (car sexps))
|
|
(for-each (lambda (x) (display ", ") (display x)) (cdr sexps))
|
|
(cat ";\n")))
|
|
(cond
|
|
((pair? gc-vars)
|
|
(cat " sexp_gc_var" num-gc-vars "(")
|
|
(display (car gc-vars))
|
|
(for-each (lambda (x) (display ", ") (display x)) (cdr gc-vars))
|
|
(cat ");\n")
|
|
(cat " sexp_gc_preserve" num-gc-vars "(ctx")
|
|
(for-each (lambda (x) (display ", ") (display x)) gc-vars)
|
|
(cat ");\n")))))
|
|
|
|
(define (write-validators args)
|
|
(for-each
|
|
(lambda (a)
|
|
(write-validator (string-append "arg" (type-index-string a)) a))
|
|
args))
|
|
|
|
(define (write-additional-checks args)
|
|
(for-each
|
|
(lambda (a)
|
|
(if (eq? 'input-port (type-base a))
|
|
(cat " sexp_check_block_port(ctx, arg" (type-index a) ", 0);\n")))
|
|
args))
|
|
|
|
(define (write-temporaries func)
|
|
(for-each
|
|
(lambda (a)
|
|
(let ((len (and (type-array a) (get-array-length func a))))
|
|
(cond
|
|
((and (type-array a) (or (vector? len) (type-auto-expand? a)))
|
|
(cat " len" (type-index a) " = "
|
|
(lambda ()
|
|
(if (number? len) (cat len) (scheme->c-converter 'int len)))
|
|
";\n"
|
|
" tmp" (type-index a) " = buf" (type-index a) ";\n")))
|
|
(cond
|
|
((and (not (type-result? a)) (type-array a) (not (string-type? a)))
|
|
(if (not (number? (type-array a)))
|
|
(cat " tmp" (type-index a)
|
|
" = (" (type-c-name (type-base a)) "*) malloc("
|
|
"(sexp_unbox_fixnum(sexp_length(ctx, arg" (type-index a)
|
|
"))+1) * sizeof(tmp" (type-index a) "[0]));\n"))
|
|
(cat " for (i=0, res=arg" (type-index a)
|
|
"; sexp_pairp(res); res=sexp_cdr(res), i++) {\n"
|
|
" tmp" (type-index a) "[i] = "
|
|
(lambda () (scheme->c-converter (type-base a) "sexp_car(res)"))
|
|
";\n"
|
|
" }\n")
|
|
(if (not (number? (type-array a)))
|
|
(cat " tmp" (type-index a) "[i] = NULL;\n")))
|
|
((and (type-result? a) (not (basic-type? a))
|
|
(not (type-free? a)) ;;(not (type-pointer? a))
|
|
(not (type-auto-expand? a))
|
|
(or (not (type-array a))
|
|
(not (integer? len))))
|
|
(cat " tmp" (type-index a) " = malloc("
|
|
(if (and (symbol? len) (not (eq? len 'null)))
|
|
(lambda () (cat (lambda () (scheme->c-converter 'unsigned-int len))
|
|
"*sizeof(tmp" (type-index a) "[0])"))
|
|
(lambda () (cat "sizeof(tmp" (type-index a) "[0])")))
|
|
");\n"))
|
|
((and (type-pointer? a) (basic-type? a))
|
|
(cat " tmp" (type-index a) " = "
|
|
(lambda ()
|
|
(scheme->c-converter
|
|
a
|
|
(string-append "arg" (type-index-string a))))
|
|
";\n")))))
|
|
(func-c-args func)))
|
|
|
|
(define (write-actual-parameter func arg)
|
|
(cond
|
|
((and (not (type-default? arg)) (type-value arg))
|
|
=> (lambda (x)
|
|
(cond
|
|
((any (lambda (y)
|
|
(and (type-array y)
|
|
(type-auto-expand? y)
|
|
(eq? x (get-array-length func y))))
|
|
(func-c-args func))
|
|
=> (lambda (y) (cat "len" (type-index y))))
|
|
(else (write x)))))
|
|
((or (type-result? arg) (type-array arg))
|
|
(cat (if (or (type-free? arg) (basic-type? arg)) ;; (type-pointer? arg)
|
|
"&"
|
|
"")
|
|
"tmp" (type-index arg)))
|
|
((and (type-pointer? arg) (basic-type? arg))
|
|
(cat "&tmp" (type-index arg)))
|
|
(else
|
|
(scheme->c-converter
|
|
arg
|
|
(string-append "arg" (type-index-string arg))))))
|
|
|
|
(define (write-call func)
|
|
(let ((ret-type (func-ret-type func))
|
|
(c-name (func-c-name func))
|
|
(c-args (func-c-args func)))
|
|
(if (any type-auto-expand? (func-c-args func))
|
|
(cat " loop:\n"))
|
|
(cat (cond ((error-type? (type-base ret-type)) " err = ")
|
|
((type-array ret-type) " tmp = ")
|
|
(else " res = ")))
|
|
((if (type-array ret-type)
|
|
(lambda (t f x) (f))
|
|
c->scheme-converter)
|
|
ret-type
|
|
(lambda ()
|
|
(cat c-name "(")
|
|
(for-each
|
|
(lambda (arg)
|
|
(if (> (type-index arg) 0) (cat ", "))
|
|
(write-actual-parameter func arg))
|
|
c-args)
|
|
(cat ")"))
|
|
(cond
|
|
((any type-link? (func-c-args func))
|
|
=> (lambda (a) (string-append "arg" (type-index-string a))))
|
|
(else #f)))
|
|
(cat ";\n")
|
|
(if (type-array ret-type)
|
|
(write-result ret-type))))
|
|
|
|
(define (write-result result)
|
|
(let ((res (string-append "res" (type-index-string result)))
|
|
(tmp (string-append "tmp" (type-index-string result))))
|
|
(cond
|
|
((and (type-array result) (eq? 'char (type-base result)))
|
|
(cat " " res " = " (lambda () (c->scheme-converter result tmp)) ";\n"))
|
|
((type-array result)
|
|
(cat " " res " = SEXP_NULL;\n")
|
|
(let ((auto-expand?
|
|
(and (pair? (type-array result))
|
|
(memq 'auto-expand (type-array result))))
|
|
(len (if (pair? (type-array result))
|
|
(car (reverse (type-array result)))
|
|
(type-array result))))
|
|
(cond
|
|
((eq? 'null len)
|
|
(cat " for (i=0; " tmp "[i]; i++) {\n"
|
|
" sexp_push(ctx, " res ", "
|
|
(if (eq? 'string (type-base result))
|
|
"str="
|
|
(lambda () (cat "SEXP_VOID);\n sexp_car(" res ") = ")))
|
|
(lambda () (c->scheme-converter result (lambda () (cat tmp "[i]"))))
|
|
");\n"
|
|
" }\n"
|
|
" " res " = sexp_nreverse(ctx, " res ");\n"))
|
|
(else
|
|
(cat " for (i=" len "-1; i>=0; i--) {\n"
|
|
" sexp_push(ctx, " res ", SEXP_VOID);\n"
|
|
" sexp_car(" res ") = "
|
|
(lambda () (c->scheme-converter result (lambda () (cat tmp "[i]"))))
|
|
";\n"
|
|
" }\n")))))
|
|
(else
|
|
(cat " " res " = ")
|
|
(c->scheme-converter
|
|
result
|
|
(string-append "tmp" (type-index-string result)))
|
|
(cat ";\n")))))
|
|
|
|
(define (write-results func)
|
|
(let ((error-res? (error-type? (type-base (func-ret-type func))))
|
|
(results (func-results func)))
|
|
(if error-res?
|
|
(cat " if ("
|
|
(if (memq (type-base (func-ret-type func))
|
|
'(non-null-string non-null-pointer))
|
|
"!"
|
|
"")
|
|
"err) {\n"
|
|
(cond
|
|
((any type-auto-expand? (func-c-args func))
|
|
=> (lambda (a)
|
|
(lambda ()
|
|
(let ((len (get-array-length func a))
|
|
(i (type-index a)))
|
|
(if (number? len)
|
|
(cat " if (len" i " != " len ")\n"
|
|
" free(tmp" i ");\n"))
|
|
(cat " len" i " *= 2;\n"
|
|
" tmp" i
|
|
" = malloc(len" i "*sizeof(tmp" i "[0]));\n"
|
|
" goto loop;\n")))))
|
|
(else
|
|
" res = SEXP_FALSE;\n"))
|
|
" } else {\n"))
|
|
(if (null? results)
|
|
(if error-res?
|
|
(cat " res = SEXP_TRUE;\n"))
|
|
(for-each write-result results))
|
|
(cond
|
|
((> (length results) (if error-res? 1 0))
|
|
(if error-res?
|
|
(cat " res = SEXP_NULL;\n")
|
|
(cat " res = sexp_cons(ctx, res, SEXP_NULL);\n"))
|
|
(for-each
|
|
(lambda (x)
|
|
(if error-res?
|
|
(cat " sexp_push(ctx, res, res" (type-index x) ");\n")
|
|
(cat " sexp_push(ctx, res, sexp_car(res));\n"
|
|
" sexp_cadr(res) = res" (type-index x) ";\n")))
|
|
(reverse results)))
|
|
((pair? results)
|
|
(cat " res = res" (type-index (car results)) ";\n")))
|
|
(if error-res?
|
|
(cat " }\n"))))
|
|
|
|
(define (write-free type)
|
|
(if (type-array type)
|
|
(cat " free(tmp" (type-index-string type) ");\n")))
|
|
|
|
(define (write-cleanup func)
|
|
(for-each write-free (func-scheme-args func))
|
|
(for-each
|
|
(lambda (a)
|
|
(cond
|
|
((type-auto-expand? a)
|
|
(let ((len (get-array-length func a))
|
|
(i (type-index a)))
|
|
(if (number? len)
|
|
(cat " if (len" i " != " len ")\n"
|
|
" free(tmp" i ");\n"))))
|
|
((eq? (type-base a) 'input-port)
|
|
(cat " sexp_maybe_unblock_port(ctx, arg" (type-index a) ");\n"))
|
|
((and (type-result? a) (not (basic-type? a))
|
|
(not (assq (type-base a) *types*))
|
|
(not (type-free? a)) (not (type-pointer? a))
|
|
(or (not (type-array a))
|
|
(not (integer? (get-array-length func a)))))
|
|
;; the above is hairy - basically this frees temporary strings
|
|
(cat " free(tmp" (type-index a) ");\n"))))
|
|
(func-c-args func))
|
|
(let* ((results (func-results func))
|
|
(return-res? (not (error-type? (type-base (func-ret-type func)))))
|
|
(preserve-res? (> (+ (length results)) (if return-res? 0 1)))
|
|
(single-res? (and (= 1 (length results)) (not return-res?)))
|
|
(tmp-string? (any (lambda (a)
|
|
(and (type-array a)
|
|
(string-type? (type-base a))))
|
|
(cons (func-ret-type func)
|
|
(func-results func))))
|
|
(gc-vars results)
|
|
(gc-vars (if tmp-string? (cons "str" gc-vars) gc-vars))
|
|
(gc-vars (if preserve-res? (cons "res" gc-vars) gc-vars))
|
|
(num-gc-vars (length gc-vars)))
|
|
(cond
|
|
((pair? gc-vars)
|
|
(cat " sexp_gc_release" num-gc-vars "(ctx);\n")))))
|
|
|
|
(define (write-func func)
|
|
(cat "static sexp " (func-stub-name func)
|
|
" (sexp ctx sexp_api_params(self, n)"
|
|
(write-parameters (func-scheme-args func)) ") {\n")
|
|
(write-locals func)
|
|
(write-validators (func-scheme-args func))
|
|
(write-additional-checks (func-c-args func))
|
|
(write-temporaries func)
|
|
(write-call func)
|
|
(write-results func)
|
|
(write-cleanup func)
|
|
(cat " return res;\n"
|
|
"}\n\n"))
|
|
|
|
(define (parameter-default? x)
|
|
(and (pair? x)
|
|
(member x '((current-input-port)
|
|
(current-output-port)
|
|
(current-error-port)))))
|
|
|
|
(define (write-default x) ;; this is a hack but very convenient
|
|
(lambda ()
|
|
(let ((value (type-value x)))
|
|
(cond
|
|
((equal? value '(current-input-port))
|
|
(cat "\"current-input-port\""))
|
|
((equal? value '(current-output-port))
|
|
(cat "\"current-output-port\""))
|
|
((equal? value '(current-error-port))
|
|
(cat "\"current-error-port\""))
|
|
(else
|
|
(c->scheme-converter x value))))))
|
|
|
|
(define (write-func-binding func)
|
|
(let ((default (and (pair? (func-scheme-args func))
|
|
(type-default? (car (reverse (func-scheme-args func))))
|
|
(car (reverse (func-scheme-args func))))))
|
|
(cat (if default
|
|
(if (parameter-default? (type-value default))
|
|
" sexp_define_foreign_param(ctx, env, "
|
|
" sexp_define_foreign_opt(ctx, env, ")
|
|
" sexp_define_foreign(ctx, env, ")
|
|
(lambda () (write (symbol->string (func-scheme-name func))))
|
|
", " (length (func-scheme-args func)) ", "
|
|
(if default "(sexp_proc1)" "")
|
|
(func-stub-name func)
|
|
(if default ", " "")
|
|
(if default (write-default default) "")
|
|
");\n")))
|
|
|
|
(define (write-type type)
|
|
(let ((name (car type))
|
|
(type (cdr type)))
|
|
(cat " name = sexp_c_string(ctx, \"" (type-name name) "\", -1);\n"
|
|
" " (type-id-name name)
|
|
" = sexp_register_c_type(ctx, name, "
|
|
(cond ((memq 'finalizer: type)
|
|
=> (lambda (x) (generate-stub-name (cadr x))))
|
|
(else "sexp_finalize_c_type"))
|
|
");\n")
|
|
(cond
|
|
((memq 'predicate: type)
|
|
=> (lambda (x)
|
|
(let ((pred (cadr x)))
|
|
(cat " tmp = sexp_make_type_predicate(ctx, name, "
|
|
(type-id-name name) ");\n"
|
|
" name = sexp_intern(ctx, \"" pred "\", "
|
|
(string-length (x->string pred)) ");\n"
|
|
" sexp_env_define(ctx, env, name, tmp);\n")))))))
|
|
|
|
(define (type-getter-name type name field)
|
|
(string-append "sexp_" (x->string (type-name (parse-type name)))
|
|
"_get_" (x->string (type-base (parse-type (cadr field))))))
|
|
|
|
(define (write-type-getter type name field)
|
|
(cat "static sexp " (type-getter-name type name field)
|
|
" (sexp ctx sexp_api_params(self, n), sexp x) {\n"
|
|
(lambda () (write-validator "x" name))
|
|
" return "
|
|
(lambda ()
|
|
(c->scheme-converter
|
|
(car field)
|
|
(string-append (if (type-struct? (car field)) "&" "")
|
|
"((" (x->string (or (type-struct-type name) ""))
|
|
" " (mangle name) "*)"
|
|
"sexp_cpointer_value(x))" "->"
|
|
(x->string (cadr field)))
|
|
(and (or (type-struct? (car field)) (type-link? (car field))) "x")))
|
|
";\n"
|
|
"}\n\n"))
|
|
|
|
(define (type-setter-name type name field)
|
|
(string-append "sexp_" (x->string (type-name (parse-type name)))
|
|
"_set_" (x->string (type-base (parse-type (cadr field))))))
|
|
|
|
(define (write-type-setter-assignment type name field dst val)
|
|
(cond
|
|
((type-struct? (car field))
|
|
;; assign to a nested struct - copy field-by-field
|
|
(let ((field-type
|
|
(cond ((assq (type-name (car field)) *types*) => cdddr)
|
|
(else (cdr field)))))
|
|
(lambda ()
|
|
(for-each
|
|
(lambda (subfield)
|
|
(let ((subname (x->string (cadr subfield))))
|
|
(cat
|
|
" "
|
|
(string-append dst "." (x->string (cadr subfield)))
|
|
" = "
|
|
(string-append
|
|
"((" (x->string (or (type-struct-type (type-name (car field))) ""))
|
|
" " (mangle (type-name (car field))) "*)" "sexp_cpointer_value(" val "))"
|
|
"->" (x->string (cadr subfield)))
|
|
";\n")))
|
|
(struct-fields field-type)))))
|
|
(else
|
|
(lambda ()
|
|
(cat " " dst " = " (lambda () (scheme->c-converter (car field) val)) ";\n")))))
|
|
|
|
(define (write-type-setter type name field)
|
|
(cat "static sexp " (type-setter-name type name field)
|
|
" (sexp ctx sexp_api_params(self, n), sexp x, sexp v) {\n"
|
|
(lambda () (write-validator "x" name))
|
|
(lambda () (write-validator "v" (car field)))
|
|
(write-type-setter-assignment
|
|
type name field
|
|
(string-append "((" (x->string (or (type-struct-type name) ""))
|
|
" " (mangle name) "*)" "sexp_cpointer_value(x))"
|
|
"->" (x->string (cadr field)))
|
|
"v")
|
|
" return SEXP_VOID;\n"
|
|
"}\n\n"))
|
|
|
|
(define (write-type-funcs orig-type)
|
|
(let ((name (car orig-type))
|
|
(type (cdr orig-type)))
|
|
;; maybe write finalizer
|
|
(cond
|
|
((memq 'finalizer: type)
|
|
=> (lambda (x)
|
|
(cat "static sexp " (generate-stub-name (cadr x))
|
|
" (sexp ctx sexp_api_params(self, n), sexp x) {\n"
|
|
" if (sexp_cpointer_freep(x))\n"
|
|
" " (cadr x) "(sexp_cpointer_value(x));\n"
|
|
" return SEXP_VOID;\n"
|
|
"}\n\n"))))
|
|
;; maybe write constructor
|
|
(cond
|
|
((memq 'constructor: type)
|
|
=> (lambda (x)
|
|
(let ((make (caadr x))
|
|
(args (cdadr x)))
|
|
(cat "static sexp " (generate-stub-name make)
|
|
" (sexp ctx sexp_api_params(self, n)"
|
|
(lambda ()
|
|
(let lp ((ls args) (i 0))
|
|
(cond ((pair? ls)
|
|
(cat ", sexp arg" i)
|
|
(lp (cdr ls) (+ i 1))))))
|
|
") {\n"
|
|
" " (or (type-struct-type name) "") " " (type-name name) " *r;\n"
|
|
" sexp_gc_var1(res);\n"
|
|
" sexp_gc_preserve1(ctx, res);\n"
|
|
;; " res = sexp_alloc_tagged(ctx, sexp_sizeof(cpointer) + sizeof(struct " (type-name name) "), "
|
|
;; (type-id-name name)
|
|
;; ");\n"
|
|
;; " r = sexp_cpointer_value(res) = sexp_cpointer_body(res);\n"
|
|
" res = sexp_alloc_tagged(ctx, sexp_sizeof(cpointer), sexp_type_tag("
|
|
(type-id-name name)
|
|
"));\n"
|
|
" r = sexp_cpointer_value(res) = malloc(sizeof("
|
|
(or (type-struct-type name) "") " " (type-name name) "));\n"
|
|
" sexp_freep(res) = 1;\n"
|
|
(lambda ()
|
|
(let lp ((ls args) (i 0))
|
|
(cond
|
|
((pair? ls)
|
|
(let* ((a (car ls))
|
|
(field
|
|
(any (lambda (f) (and (pair? f) (eq? a (cadr f))))
|
|
(cddr x))))
|
|
(if field
|
|
(cat " r->" (cadr field) " = "
|
|
(lambda ()
|
|
(scheme->c-converter
|
|
(car field)
|
|
(string-append "arg"
|
|
(number->string i))))
|
|
";\n"))
|
|
(lp (cdr ls) (+ i 1)))))))
|
|
" sexp_gc_release1(ctx);\n"
|
|
" return res;\n"
|
|
"}\n\n")
|
|
(set! *funcs*
|
|
(cons (parse-func `(void ,make ,args)) *funcs*))))))
|
|
;; write field accessors
|
|
(for-each
|
|
(lambda (field)
|
|
(cond
|
|
((and (pair? field) (pair? (cdr field)))
|
|
(cond
|
|
((and (pair? (cddr field)) (caddr field))
|
|
(write-type-getter type name field)
|
|
(set! *funcs*
|
|
(cons (parse-func
|
|
`(,(car field)
|
|
(,(caddr field)
|
|
#f
|
|
,(type-getter-name type name field))
|
|
(,name)))
|
|
*funcs*))))
|
|
(cond
|
|
((and (pair? (cddr field))
|
|
(pair? (cdddr field))
|
|
(car (cdddr field)))
|
|
(write-type-setter type name field)
|
|
(set! *funcs*
|
|
(cons (parse-func
|
|
`(,(car field)
|
|
(,(car (cdddr field))
|
|
#f
|
|
,(type-setter-name type name field))
|
|
(,name ,(car field))))
|
|
*funcs*)))))))
|
|
(struct-fields type))))
|
|
|
|
(define (write-const const)
|
|
(let ((scheme-name (if (pair? (cadr const)) (caadr const) (cadr const)))
|
|
(c-name (if (pair? (cadr const)) (cadadr const) (mangle (cadr const)))))
|
|
(cat " name = sexp_intern(ctx, \"" scheme-name "\", "
|
|
(string-length (x->string scheme-name)) ");\n"
|
|
" sexp_env_define(ctx, env, name, tmp="
|
|
(lambda () (c->scheme-converter (car const) c-name)) ");\n")))
|
|
|
|
(define (write-utilities)
|
|
(define (input-env-string? x)
|
|
(and (eq? 'env-string (type-base x)) (not (type-result? x))))
|
|
(cond
|
|
((any (lambda (f)
|
|
(or (any input-env-string? (func-results f))
|
|
(any input-env-string? (func-scheme-args f))))
|
|
*funcs*)
|
|
(cat "static char* sexp_concat_env_string (sexp x) {\n"
|
|
" int klen=sexp_string_length(sexp_car(x)), vlen=sexp_string_length(sexp_cdr(x));\n"
|
|
" char *res = (char*) malloc(klen+vlen+2);\n"
|
|
" strncpy(res, sexp_string_data(sexp_car(x)), klen);\n"
|
|
" res[sexp_string_length(sexp_car(x))] = '=';\n"
|
|
" strncpy(res+sexp_string_length(sexp_car(x)), sexp_string_data(sexp_cdr(x)), vlen);\n"
|
|
" res[len-1] = '\\0';\n"
|
|
" return res;\n"
|
|
"}\n\n"))))
|
|
|
|
(define (write-init)
|
|
(newline)
|
|
(write-utilities)
|
|
(for-each write-func *funcs*)
|
|
(for-each write-type-funcs *types*)
|
|
(cat "sexp sexp_init_library (sexp ctx sexp_api_params(self, n), sexp env) {\n"
|
|
" sexp_gc_var2(name, tmp);\n"
|
|
" sexp_gc_preserve2(ctx, name, tmp);\n")
|
|
(for-each write-const *consts*)
|
|
(for-each write-type *types*)
|
|
(for-each write-func-binding *funcs*)
|
|
(for-each (lambda (x) (cat " " x "\n")) (reverse *inits*))
|
|
(cat " sexp_gc_release2(ctx);\n"
|
|
" return SEXP_VOID;\n"
|
|
"}\n\n"))
|
|
|
|
(define (generate file)
|
|
(display "/* automatically generated by chibi genstubs */\n")
|
|
(c-system-include "chibi/eval.h")
|
|
(load file)
|
|
(write-init))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; main
|
|
|
|
(define (main args)
|
|
(case (length args)
|
|
((1)
|
|
(with-output-to-file (string-append (strip-extension (car args)) ".c")
|
|
(lambda () (generate (car args)))))
|
|
((2)
|
|
(if (equal? "-" (cadr args))
|
|
(generate (car args))
|
|
(with-output-to-file (cadr args) (lambda () (generate (car args))))))
|
|
(else
|
|
(error "usage: genstubs <file.stub> [<output.c>]"))))
|