; Copyright (c) 1993-2001 by Richard Kelsey and Jonathan Rees. See file COPYING. ; Sets over finite types. ; ; (define-enum-set-type id type-name predicate constructor ; element-syntax element-predicate all-elements element-index-ref) ; ; Defines ID to be syntax for constructing sets, PREDICATE to be a predicate ; for those sets, and CONSTRUCTOR an procedure for constructing one ; from a list. ; ; (enum-set->list ) -> ; (enum-set-member? ) -> ; (enum-set=? ) -> ; (enum-set-union ) -> ; (enum-set-intersection ) -> ; (enum-set-negation ) -> ; ; Given an enumerated type: ; (define-enumerated-type color :color ; color? ; colors ; color-name ; color-index ; (red blue green)) ; we can define sets of colors: ; (define-enum-set-type color-set :color-set ; color-set? ; make-color-set ; color color? colors color-index) ; ; (enum-set->list (color-set red blue)) ; -> (#{Color red} #{Color blue}) ; (enum-set->list (enum-set-negation (color-set red blue))) ; -> (#{Color green}) ; (enum-set-member? (color-set red blue) (color blue)) ; -> #t (define-syntax define-enum-set-type (syntax-rules () ((define-enum-set-type id type predicate constructor element-syntax element-predicate all-elements element-index-ref) (begin (define type (make-enum-set-type 'id element-predicate all-elements element-index-ref)) (define (predicate x) (and (enum-set? x) (eq? (enum-set-type x) type))) (define (constructor elements) (if (every element-predicate elements) (make-enum-set type (elements->mask elements element-index-ref)) (error "invalid set elements" element-predicate elements))) (define-enum-set-maker id constructor element-syntax))))) ; (define-enum-set-maker id constructor element-syntax) (define-syntax define-enum-set-maker (lambda (e r c) (let ((id (list-ref e 1)) (constructor (list-ref e 2)) (element-syntax (list-ref e 3)) (%define-syntax (r 'define-syntax))) `(,%define-syntax ,id (syntax-rules () ((,id element ...) (,constructor (list (,element-syntax element) ...)))))))) (define-record-type enum-set-type :enum-set-type (make-enum-set-type id predicate values index-ref) enum-set-type? (id enum-set-type-id) (predicate enum-set-type-predicate) (values enum-set-type-values) (index-ref enum-set-type-index-ref)) (define-record-discloser :enum-set-type (lambda (e-s-t) (list 'enum-set-type (enum-set-type-id e-s-t)))) ; The mask is settable to allow for destructive operations. There aren't ; any such yet. (define-record-type enum-set :enum-set (make-enum-set type mask) enum-set? (type enum-set-type) (mask enum-set-mask set-enum-set-mask!)) (define-record-discloser :enum-set (lambda (e-s) (cons (enum-set-type-id (enum-set-type e-s)) (enum-set->list e-s)))) (define (enum-set-has-type? enum-set enum-set-type) (eq? (enum-set-type enum-set) enum-set-type)) (define enum-set->integer enum-set-mask) (define integer->enum-set make-enum-set) (define-exported-binding "enum-set?" enum-set?) (define-exported-binding "enum-set->integer" enum-set->integer) (define-exported-binding "integer->enum-set" integer->enum-set) (define-exported-binding "enum-set-has-type?" enum-set-has-type?) (define (make-set-constructor id predicate values index-ref) (let ((type (make-enum-set-type id predicate values index-ref))) (lambda elements (if (every predicate elements) (make-enum-set type (elements->mask elements index-ref)) (error "invalid set elements" predicate elements))))) (define (elements->mask elements index-ref) (do ((elements elements (cdr elements)) (mask 0 (bitwise-ior mask (arithmetic-shift 1 (index-ref (car elements)))))) ((null? elements) mask))) (define (enum-set-member? enum-set element) (if ((enum-set-type-predicate (enum-set-type enum-set)) element) (not (= (bitwise-and (enum-set-mask enum-set) (element-mask element (enum-set-type enum-set))) 0)) (call-error "invalid arguments" enum-set-member? enum-set element))) (define (enum-set=? enum-set0 enum-set1) (if (eq? (enum-set-type enum-set0) (enum-set-type enum-set1)) (= (enum-set-mask enum-set0) (enum-set-mask enum-set1)) (call-error "invalid arguments" enum-set=? enum-set0 enum-set1))) (define (element-mask element enum-set-type) (arithmetic-shift 1 ((enum-set-type-index-ref enum-set-type) element))) ; To reduce the number of bitwise operations required we bite off two bytes ; at a time. (define (enum-set->list enum-set) (let ((values (enum-set-type-values (enum-set-type enum-set)))) (do ((i 0 (+ i 16)) (mask (enum-set-mask enum-set) (arithmetic-shift mask -16)) (elts '() (do ((m (bitwise-and mask #xFFFF) (arithmetic-shift m -1)) (i i (+ i 1)) (elts elts (if (odd? m) (cons (vector-ref values i) elts) elts))) ((= m 0) elts)))) ((= mask 0) (reverse elts))))) (define (enum-set-union enum-set0 enum-set1) (if (eq? (enum-set-type enum-set0) (enum-set-type enum-set1)) (make-enum-set (enum-set-type enum-set0) (bitwise-ior (enum-set-mask enum-set0) (enum-set-mask enum-set1))) (call-error "invalid arguments" enum-set-union enum-set0 enum-set1))) (define (enum-set-intersection enum-set0 enum-set1) (if (eq? (enum-set-type enum-set0) (enum-set-type enum-set1)) (make-enum-set (enum-set-type enum-set0) (bitwise-and (enum-set-mask enum-set0) (enum-set-mask enum-set1))) (call-error "invalid arguments" enum-set-union enum-set0 enum-set1))) (define (enum-set-negation enum-set) (let* ((type (enum-set-type enum-set)) (mask (- (arithmetic-shift 1 (vector-length (enum-set-type-values type))) 1))) (make-enum-set type (bitwise-and (bitwise-not (enum-set-mask enum-set)) mask))))