Add type db, some nice macro features and a brand new SDL3 example #27
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
SEXC ?= ../../sexc
|
SEXC ?= ../../sexc
|
||||||
|
|
||||||
EXPECTED = hello, world\nhello, sex\n2 greetings\ntext times \nmood 1
|
EXPECTED = hello, world\nhello, sex\n2 greetings\ntext times \ntext times \nmood 1
|
||||||
|
|
||||||
check:
|
check:
|
||||||
@$(SEXC) greet.sex -c -o greet.o
|
@$(SEXC) greet.sex -c -o greet.o
|
||||||
|
|||||||
@@ -21,6 +21,9 @@
|
|||||||
(printf "%d greetings\n" greet-count)
|
(printf "%d greetings\n" greet-count)
|
||||||
(describe-fields greeting)
|
(describe-fields greeting)
|
||||||
(printf "\n")
|
(printf "\n")
|
||||||
|
;; ...and through the imported typedef for it
|
||||||
|
(describe-fields greeting-t)
|
||||||
|
(printf "\n")
|
||||||
(var m (enum mood) grumpy)
|
(var m (enum mood) grumpy)
|
||||||
(printf "mood %d\n" m)
|
(printf "mood %d\n" m)
|
||||||
(return 0))
|
(return 0))
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
|
|
||||||
(pub enum mood (cheerful grumpy))
|
(pub enum mood (cheerful grumpy))
|
||||||
|
|
||||||
|
(pub typedef greeting-t (struct greeting))
|
||||||
|
|
||||||
;;; Compile-time reflection across the module boundary: both the macro
|
;;; Compile-time reflection across the module boundary: both the macro
|
||||||
;;; and the struct it asks about are exported, and the importing unit
|
;;; and the struct it asks about are exported, and the importing unit
|
||||||
;;; has to know the struct's fields to expand this.
|
;;; has to know the struct's fields to expand this.
|
||||||
|
|||||||
@@ -54,6 +54,42 @@
|
|||||||
#f
|
#f
|
||||||
(get-underlying-type 't-point))
|
(get-underlying-type 't-point))
|
||||||
|
|
||||||
|
;; Reflection through an alias. Both spellings of the target occur:
|
||||||
|
;; (typedef point-t point) and (typedef point-t (struct point)).
|
||||||
|
(add-typedef 't-point-t '(typedef t-point-t t-point))
|
||||||
|
(test "a typedef to a struct has the struct's fields"
|
||||||
|
'((x int) (y int))
|
||||||
|
(get-fields 't-point-t))
|
||||||
|
(add-typedef 't-point-s '(typedef t-point-s (struct t-point)))
|
||||||
|
(test "written the other way round too"
|
||||||
|
'((x int) (y int))
|
||||||
|
(get-fields 't-point-s))
|
||||||
|
(add-typedef 't-point-2 '(typedef t-point-2 t-point-t))
|
||||||
|
(test "and through a chain of them"
|
||||||
|
'((x int) (y int))
|
||||||
|
(get-fields 't-point-2))
|
||||||
|
(test "map-fields follows an alias as well"
|
||||||
|
'((x int) (y int))
|
||||||
|
(map-fields 't-point-t (lambda (name type) (list name type))))
|
||||||
|
(add-typedef 't-color-t '(typedef t-color-t t-color))
|
||||||
|
(test "an aliased enum is still named as it was declared"
|
||||||
|
'((red (enum t-color)) (green (enum t-color)) (blue (enum t-color)))
|
||||||
|
(map-fields 't-color-t (lambda (name type) (list name type))))
|
||||||
|
(test "a typedef to a primitive has no fields"
|
||||||
|
#f
|
||||||
|
(get-fields 't-u8))
|
||||||
|
|
||||||
|
;; A typedef can be written to lead back to itself. Resolving it must
|
||||||
|
;; stop rather than spin
|
||||||
|
(add-typedef 't-loop-a '(typedef t-loop-a t-loop-b))
|
||||||
|
(add-typedef 't-loop-b '(typedef t-loop-b t-loop-a))
|
||||||
|
(test "a typedef cycle terminates"
|
||||||
|
't-loop-a
|
||||||
|
(get-underlying-type 't-loop-a))
|
||||||
|
(test "and has no fields"
|
||||||
|
#f
|
||||||
|
(get-fields 't-loop-a))
|
||||||
|
|
||||||
;; A #define keeps its value forms -- there can be more than one.
|
;; A #define keeps its value forms -- there can be more than one.
|
||||||
(add-define 't-maxn '(define t-maxn 8))
|
(add-define 't-maxn '(define t-maxn 8))
|
||||||
(test "defines are recorded"
|
(test "defines are recorded"
|
||||||
|
|||||||
44
types.scm
44
types.scm
@@ -76,21 +76,44 @@
|
|||||||
;;; ((name type) ...) for a struct or union, #f for anything else --
|
;;; ((name type) ...) for a struct or union, #f for anything else --
|
||||||
;;; including a name that was never declared. Callers give the better
|
;;; including a name that was never declared. Callers give the better
|
||||||
;;; error, since they know what they wanted it for.
|
;;; error, since they know what they wanted it for.
|
||||||
|
;;;
|
||||||
|
;;; A typedef is followed to what it stands for, so reflection over an
|
||||||
|
;;; alias works exactly as it does over the name it aliases.
|
||||||
(define (get-fields name)
|
(define (get-fields name)
|
||||||
(let ((info (get-type-info name)))
|
(let ((info (resolve-type-info name)))
|
||||||
(and info
|
(and info
|
||||||
(memq (car info) '(struct union))
|
(memq (car info) '(struct union))
|
||||||
(caddr info))))
|
(caddr info))))
|
||||||
|
|
||||||
;;; Follow a typedef chain to the name it ultimately stands for. #f if
|
;;; Follow a typedef chain to the name it stands for. #f if NAME is
|
||||||
;;; NAME is not a typedef.
|
;;; not a typedef. A typedef that leads back to itself stops rather
|
||||||
|
;;; than spinning: nothing prevents one from being written.
|
||||||
(define (get-underlying-type name)
|
(define (get-underlying-type name)
|
||||||
|
(let follow ((name name) (seen (list)))
|
||||||
|
(and (not (member name seen))
|
||||||
|
(let ((info (get-type-info name)))
|
||||||
|
(and info
|
||||||
|
(eq? (car info) 'typedef)
|
||||||
|
(let ((target (caddr info)))
|
||||||
|
(or (and (symbol? target) (follow target (cons name seen)))
|
||||||
|
target)))))))
|
||||||
|
|
||||||
|
;;; The declaration NAME ultimately names. For a typedef that is the
|
||||||
|
;;; entry of whatever it stands for, and for anything else it is
|
||||||
|
;;; NAME's own info.
|
||||||
|
(define (resolve-type-info name)
|
||||||
(let ((info (get-type-info name)))
|
(let ((info (get-type-info name)))
|
||||||
(and info
|
(and info
|
||||||
(eq? (car info) 'typedef)
|
(if (eq? (car info) 'typedef)
|
||||||
(let ((target (caddr info)))
|
(let* ((target (get-underlying-type name))
|
||||||
(or (and (symbol? target) (get-underlying-type target))
|
(tag (cond ((symbol? target) target)
|
||||||
target)))))
|
((and (pair? target)
|
||||||
|
(pair? (cdr target))
|
||||||
|
(symbol? (cadr target)))
|
||||||
|
(cadr target))
|
||||||
|
(else #f))))
|
||||||
|
(and tag (get-type-info tag)))
|
||||||
|
info))))
|
||||||
|
|
||||||
;;; Type matcher macro
|
;;; Type matcher macro
|
||||||
;;; (type-match type
|
;;; (type-match type
|
||||||
@@ -119,15 +142,16 @@
|
|||||||
;;;
|
;;;
|
||||||
;;; Returns #f if nothing of that name was declared
|
;;; Returns #f if nothing of that name was declared
|
||||||
(define (map-fields struct-union-enum fn)
|
(define (map-fields struct-union-enum fn)
|
||||||
(let ((info (get-type-info struct-union-enum)))
|
(let ((info (resolve-type-info struct-union-enum)))
|
||||||
(and info
|
(and info
|
||||||
(case (car info)
|
(case (car info)
|
||||||
((struct union)
|
((struct union)
|
||||||
(map (lambda (field) (fn (car field) (cadr field)))
|
(map (lambda (field) (fn (car field) (cadr field)))
|
||||||
(caddr info)))
|
(caddr info)))
|
||||||
;; An enumerator's type is the enum itself.
|
;; An enumerator's type is the enum itself -- named as it was
|
||||||
|
;; declared, since `enum some-typedef' is not a C type.
|
||||||
((enum)
|
((enum)
|
||||||
(let ((type (list 'enum struct-union-enum)))
|
(let ((type (list 'enum (cadr info))))
|
||||||
(map (lambda (value) (fn value type))
|
(map (lambda (value) (fn value type))
|
||||||
(caddr info))))
|
(caddr info))))
|
||||||
(else #f)))))
|
(else #f)))))
|
||||||
|
|||||||
Reference in New Issue
Block a user