They are not equivalent, but it'll be allright in this case. Probably. Passes tests at least.
267 lines
7.7 KiB
Scheme
267 lines
7.7 KiB
Scheme
;;; Sex fmt-c output writer
|
|
|
|
(declare (unit fmt-c-writer)
|
|
(uses fmt-c
|
|
semen
|
|
utils))
|
|
|
|
(import (chicken string)
|
|
brev-separate
|
|
fmt
|
|
matchable
|
|
regex
|
|
srfi-1 ; lists
|
|
srfi-13 ; strings
|
|
tree
|
|
)
|
|
|
|
(define (unkebabify sym)
|
|
(case sym
|
|
((-) sym)
|
|
((--) sym)
|
|
((->) sym)
|
|
((-=) sym)
|
|
(else
|
|
(string->symbol
|
|
(string-substitute "-(?!>)" "_"
|
|
(symbol->string sym) #t)))))
|
|
|
|
(define (atom-to-fmt-c atom)
|
|
(case atom
|
|
((fn) '%fun)
|
|
((prototype) '%prototype)
|
|
((begin) '%block-begin)
|
|
((define) '%define)
|
|
((pointer) '%pointer)
|
|
((array) '%array)
|
|
((attribute) '%attribute)
|
|
((¤) 'vector-ref)
|
|
((include) '%include)
|
|
;; uh things we do for c89 compatibility
|
|
((bool) 'int)
|
|
((true) 1)
|
|
((false) 0)
|
|
(else
|
|
(if (symbol? atom)
|
|
(unkebabify atom)
|
|
atom))))
|
|
|
|
(define (maybe-unwrap-type type)
|
|
(if (and (list? type)
|
|
(= 1 (length type)))
|
|
(car type)
|
|
type))
|
|
|
|
(define (make-field-access form)
|
|
(assert
|
|
(= 2 (length form)) "Wrong field access format")
|
|
(unkebabify
|
|
(string->symbol
|
|
(fmt #f (cadr form) (car form)))))
|
|
|
|
(define (walk-generic-toplevel form)
|
|
(cond ((atom? form) (atom-to-fmt-c form))
|
|
((list? form) (map walk-generic-toplevel form))
|
|
(else (error "Malformed form " form))))
|
|
|
|
(define (field-access-form? form)
|
|
(and (symbol? (car form))
|
|
(char=? #\. (string-ref (symbol->string (car form)) 0))))
|
|
|
|
(define (walk-expr form)
|
|
(match form
|
|
((? vector?)
|
|
(list->vector
|
|
(walk-expr (vector->list form))))
|
|
((? atom?)
|
|
(atom-to-fmt-c form))
|
|
((? field-access-form?)
|
|
(make-field-access form))
|
|
(('var . _) (walk-var form))
|
|
(('cast expr type) (list '%cast
|
|
(walk-type type)
|
|
(walk-expr expr)))
|
|
(('enum . _) (walk-enum form))
|
|
;; | is problematic... And c-or/bit-or/etc are actually
|
|
;; procedures, so we have to call the procedure itself
|
|
(('c-or . rest) (apply c-or (map walk-expr rest)))
|
|
(('c-bit-or . rest) (apply c-bit-or (map walk-expr rest)))
|
|
(('c-bit-or= . rest) (apply c-bit-or= (map walk-expr rest)))
|
|
(else (map walk-expr form))))
|
|
|
|
(define (walk-var form)
|
|
;; (var a int) -> (%var int a)
|
|
;; (var a (const int) 32) -> (%var (const int) a 32)
|
|
;; (var b [const char 512]) -> (%var (%array (const char) 512) b)
|
|
;; note: [...] is actually (¤ ...) after reading
|
|
;; (var c (fn ((int) (float)) void)) -> (%var (%fun void ((int) (float))) c)
|
|
`(%var
|
|
,(walk-type (third form))
|
|
,(atom-to-fmt-c (second form))
|
|
.
|
|
,(if (null? (drop form 3))
|
|
(list)
|
|
(walk-expr (drop form 3))) ; optional init expression
|
|
))
|
|
|
|
(define (walk-type form)
|
|
;; int -> int
|
|
;; (const int) -> const int
|
|
;; [const char 512] ->(¤ (const char) 512) -> (%array (const char) 512)
|
|
;; [float 8] -> (%array float 8)
|
|
;; (* const char) -> (const char *)
|
|
;; (const * const * const char) -> (const char * const * const)
|
|
;; (fn ((int) (float)) void) -> (%fun void ((int) (float)))
|
|
(match form
|
|
(('¤ . array-type)
|
|
(if (integer? (last array-type))
|
|
;; sized array
|
|
(let* ((type-list (drop-right array-type 1))
|
|
(type (maybe-unwrap-type type-list))
|
|
(size (last array-type)))
|
|
`(%array ,(walk-type type)
|
|
,size))
|
|
;; sugar for pointer... Do we really need it? Guess why not,
|
|
;; it's a strong semantic cue
|
|
`(%array ,(walk-type (maybe-unwrap-type array-type)))))
|
|
(('fn arglist ret-type)
|
|
`(%fun ,(walk-type ret-type) ,(walk-arglist arglist)))
|
|
(('fn . _)
|
|
(assert #f "Malformed function type form"))
|
|
|
|
;; Special case: nested structs/unions
|
|
((or ('struct . _)
|
|
('union . _)) (walk-struct form))
|
|
|
|
(('enum . _) (walk-enum form))
|
|
(else
|
|
(type-convert-to-c form))))
|
|
|
|
(define (type-convert-to-c type)
|
|
;; Our pointers to C pointers
|
|
;; int -> int
|
|
;; * const char -> const char *
|
|
;; const * const char -> const char * const
|
|
(if (atom? type) (atom-to-fmt-c type)
|
|
(flatten
|
|
(tree-map atom-to-fmt-c
|
|
(flatten
|
|
(list-join (reverse (list-split type '*))
|
|
'(*)))))))
|
|
|
|
(define (walk-fn-def form)
|
|
(match form
|
|
(('fn name args ret-type . maybe-body)
|
|
`(%fun
|
|
,(walk-type ret-type)
|
|
,(atom-to-fmt-c name)
|
|
,(walk-arglist args)
|
|
.
|
|
,(walk-expr maybe-body)))))
|
|
|
|
;;; TODO: isn't there a better way?
|
|
(define (is-probably-type form)
|
|
(case (car form)
|
|
((¤ * const volatile struct union) #t)
|
|
(else #f)))
|
|
|
|
(define (walk-arglist form)
|
|
;; E.g.:
|
|
;; ((float) (int) (const char) (* const char) (¤ (* const struct res) 32))
|
|
;; ((f1 float) (f2 float) (f3 float) (res (¤ float 4)))
|
|
(map (fn
|
|
(match x
|
|
(('¤ . _) (walk-type x))
|
|
|
|
;; yeah shitty, but I don't know yet how to determine if the
|
|
;; first entry is part of the type and not an argument name
|
|
;; :(
|
|
((? is-probably-type) (walk-type x))
|
|
|
|
;; 1 element args are always type
|
|
((_) (walk-type x))
|
|
|
|
((var . type) (append (list (walk-type (maybe-unwrap-type type)))
|
|
(list (walk-type var))))))
|
|
form))
|
|
|
|
(define (walk-function form)
|
|
;; (fn ret-type name arglist body) -> normal function
|
|
;; (fn ret-type name arglist) -> prototype
|
|
(if (>= (length form) 5)
|
|
(walk-fn-def form)
|
|
(cons '%prototype (cdr (walk-fn-def form)))))
|
|
|
|
(define (process-struct-fields fields)
|
|
(map (fn
|
|
(let ((type (walk-type (last x))))
|
|
(cons type (map atom-to-fmt-c (drop-right x 1)))))
|
|
fields))
|
|
|
|
(define (walk-struct form)
|
|
(match form
|
|
((type (fields ...) . attrs) ; anonymous struct
|
|
`(,type ,(process-struct-fields fields)
|
|
. ,(tree-map atom-to-fmt-c attrs)))
|
|
((type name) ; simple 'struct whatever', like in variable def
|
|
`(,type ,(atom-to-fmt-c name)))
|
|
((type name (fields ...) . attrs)
|
|
`(,type ,(atom-to-fmt-c name)
|
|
,(process-struct-fields fields)
|
|
. ,(tree-map atom-to-fmt-c attrs)))
|
|
(else (error "Malformed aggregate definition " form))))
|
|
|
|
(define (walk-enum form)
|
|
(match form
|
|
(('enum (values ...))
|
|
`(enum ,(map atom-to-fmt-c values)))
|
|
(('enum name (values ...))
|
|
`(enum ,(atom-to-fmt-c name) ,(map atom-to-fmt-c values)))))
|
|
|
|
(define (walk-extern form)
|
|
(match form
|
|
(('fn . _)
|
|
;; extern function?.. What
|
|
(list 'extern (walk-function form)))
|
|
(('var . _)
|
|
(list 'extern (walk-var form)))
|
|
(else (error "Extern what?"))))
|
|
|
|
(define (walk-public form)
|
|
(match form
|
|
(('fn . _)
|
|
(walk-function form))
|
|
(('var . _)
|
|
(walk-var form))
|
|
((or ('define . _)
|
|
('defmacro . _)
|
|
|
|
('import . _)
|
|
('include . _)
|
|
|
|
('struct . _)
|
|
('union . _)
|
|
|
|
('typedef . _))
|
|
;; ignore here, used in generating public interface
|
|
(process-toplevel-form form))
|
|
(else
|
|
(error "Pub what?" (cadr form)))))
|
|
|
|
(define (process-toplevel-form form)
|
|
(match form
|
|
(('fn . _) (list 'static (walk-function form)))
|
|
(('var . _) (list 'static (walk-var form)))
|
|
(('extern . rest) (walk-extern rest))
|
|
(('pub . rest) (walk-public rest))
|
|
((or ('struct . _)
|
|
('union . _)) (walk-struct form))
|
|
(('enum . _) (walk-enum form))
|
|
(('define name . rest) `(%define ,(atom-to-fmt-c name) ,@rest))
|
|
(else (walk-expr form))))
|
|
|
|
(define (emit-c sex-forms)
|
|
(for-each (lambda (form)
|
|
(fmt #t (c-expr (process-toplevel-form form)) nl))
|
|
sex-forms))
|