Add type db, some nice macro features and a brand new SDL3 example #27

Merged
pkulev merged 14 commits from sdl-example into main 2026-09-18 00:11:05 +02:00
5 changed files with 76 additions and 11 deletions
Showing only changes of commit 97ef62b7f4 - Show all commits

View File

@@ -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

View File

@@ -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))

View File

@@ -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.

View File

@@ -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"

View File

@@ -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)))))