Now these macro helpers take in account the possibility of typedefing one type to another, and correctly find the one intended
143 lines
5.6 KiB
Scheme
143 lines
5.6 KiB
Scheme
;;; The type database.
|
|
;;;
|
|
;;; Names here are prefixed so they cannot collide with the types the
|
|
;;; other suites register: the database is one table in the linked
|
|
;;; binary, and semen fills it in whenever a suite compiles a struct.
|
|
|
|
(import types)
|
|
|
|
(test-group "types"
|
|
|
|
(add-struct 't-point '(struct t-point ((x int) (y int))))
|
|
(test "fields come back as (name type)"
|
|
'((x int) (y int))
|
|
(get-fields 't-point))
|
|
(test "and the whole entry is tagged"
|
|
'(struct t-point ((x int) (y int)))
|
|
(get-type-info 't-point))
|
|
|
|
;; Fields are written with the type last, so one entry can declare
|
|
;; several names. They come back as one field each.
|
|
(add-struct 't-settings
|
|
'(pub struct t-settings ((x y w h u32) (title (* const char)))))
|
|
(test "names sharing a type are split apart"
|
|
'((x u32) (y u32) (w u32) (h u32) (title (* const char)))
|
|
(get-fields 't-settings))
|
|
|
|
(add-struct 't-commented '(struct t-commented ((comment " hi") (a int))))
|
|
(test "a comment among the fields is not a field"
|
|
'((a int))
|
|
(get-fields 't-commented))
|
|
|
|
(add-union 't-value '(union t-value ((i int) (f float))))
|
|
(test "unions have fields too"
|
|
'((i int) (f float))
|
|
(get-fields 't-value))
|
|
|
|
(add-enum 't-color '(enum t-color (red green blue)))
|
|
(test "enums keep their values"
|
|
'(enum t-color (red green blue))
|
|
(get-type-info 't-color))
|
|
(test "but have no fields"
|
|
#f
|
|
(get-fields 't-color))
|
|
|
|
(add-typedef 't-u8 '(typedef t-u8 uint8-t))
|
|
(test "a typedef resolves to its target"
|
|
'uint8-t
|
|
(get-underlying-type 't-u8))
|
|
(add-typedef 't-byte '(typedef t-byte t-u8))
|
|
(test "and chains are followed to the end"
|
|
'uint8-t
|
|
(get-underlying-type 't-byte))
|
|
(test "a struct is not a typedef"
|
|
#f
|
|
(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.
|
|
(add-define 't-maxn '(define t-maxn 8))
|
|
(test "defines are recorded"
|
|
'(define t-maxn (8))
|
|
(get-type-info 't-maxn))
|
|
|
|
;; map-fields walks an aggregate, handing each field to a function.
|
|
(test "map-fields visits every field"
|
|
'((x int) (y int))
|
|
(map-fields 't-point (lambda (name type) (list name type))))
|
|
(test "and splits shared names apart too"
|
|
'(x y w h title)
|
|
(map-fields 't-settings (lambda (name type) name)))
|
|
;; An enumerator's type is the enum itself.
|
|
(test "enum values are fields whose type is the enum"
|
|
'((red (enum t-color)) (green (enum t-color)) (blue (enum t-color)))
|
|
(map-fields 't-color (lambda (name type) (list name type))))
|
|
(test "a typedef has no fields to map"
|
|
#f
|
|
(map-fields 't-u8 (lambda (name type) name)))
|
|
(test "nor does an undeclared name"
|
|
#f
|
|
(map-fields 't-nothing (lambda (name type) name)))
|
|
|
|
;; type-match compares whole types, since a type is a form.
|
|
(test "a bare type matches" 'yes (type-match 'int (int 'yes) (else 'no)))
|
|
(test "so does a compound one" 'yes (type-match '(* const char)
|
|
(int 'no)
|
|
((* const char) 'yes)
|
|
(else 'no)))
|
|
;; The [int 10] of the docstring is Sex notation: the Sex reader turns
|
|
;; brackets into a ¤ form, while CHICKEN reads them as plain parens.
|
|
;; In a .scm file the array type has to be written out.
|
|
(test "and an array" 'yes (type-match '(¤ int 10)
|
|
((¤ int 10) 'yes)
|
|
(else 'no)))
|
|
(test "else catches the rest" 'no (type-match '(* void) (int 'yes) (else 'no)))
|
|
(test "a near miss does not match" 'no (type-match '(¤ int 20)
|
|
((¤ int 10) 'yes)
|
|
(else 'no)))
|
|
(test "no clause matching and no else is #f"
|
|
#f
|
|
(type-match 'float (int 'yes)))
|
|
|
|
(test "an undeclared name has no entry"
|
|
#f
|
|
(get-type-info 't-never-declared))
|
|
(test "and no fields"
|
|
#f
|
|
(get-fields 't-never-declared)))
|