racket/s/enum.ss
dybvig 0a5700cef6 support for internal fasl compression to allow seeking past compile-time info at run time and run-time info at compile time
- the collector now releases bignum temporaries in the collector
  rather than relocating them so we don't keep around huge bignum
  temporaries forever.
     gc.c
- removed the presumably useless vector-handling code from load()
  which used to be required to handle fasl groups.
     scheme.c
- object files are no longer compressed as a whole, and the parameter
  compile-compressed is no longer defined.  instead, the individual
  fasl objects within an object file are compressed whenever the
  new parameter fasl-compressed is set to its default value, #t.
  this allows the fasl reader to seek past portions of an object
  file that are not of interest, i.e., visit-only code and data
  when "revisiting" an object file and revisit-only code and data
  when "visiting" an object file.  the compressed portions are
  compressed using the format and level specified by the compress-format
  and compress-level parameters.  the C-coded fasl reader and
  boot-file loader no longer handle compressed files; these are
  handled, less efficiently, by the Scheme entry point (fasl-read).
  a warning exception is raised the first time a program attempts
  to create or read a compressed fasl file.
    7.ss, s/Mf-base, back.ss, bytevector.ss, cmacros.ss, compile.ss,
    fasl-helpers.ss, fasl.ss, primdata.ss, strip.ss, syntax.ss,
    externs.h, fasl.c, gc.c, scheme.c, thread.c,
    mats/6.ms, mats/7.ms, mats/bytevector.ms, mats/misc.ms, patch*,
    root-experr*,
    intro.stex, use.stex, io.stex, system.stex,
    release_notes.stex
- added begin wrappers around many of the Scheme source files that
  contained multiple expressions to cut down the number of top-level
  fasl objects and increase compressibility.  also removed the
  string filenames for debugging at the start of each file that had
  one---these are best inserted universally by a modified compile-file
  during a debugging session when desired.  also removed unnecessary
  top-level placeholder definitions for the assignments that follow.
    4.ss, 5_1.ss, 5_2.ss, 5_3.ss, 5_7.ss, 6.ss, 7.ss, bytevector.ss,
    cafe.ss, cback.ss, compile.ss, cp0.ss, cpcommonize.ss, cpletrec.ss,
    cpnanopass.ss, cprep.ss, cpvalid.ss, date.ss, engine.ss, enum.ss,
    env.ss, event.ss, exceptions.ss, expeditor.ss, fasl.ss, foreign.ss,
    format.ss, front.ss, ftype.ss, inspect.ss, interpret.ss, io.ss,
    library.ss, mathprims.ss, newhash.ss, pdhtml.ss, pretty.ss,
    prims.ss, primvars.ss, print.ss, read.ss, record.ss, reloc.ss,
    strnum.ss, syntax.ss, trace.ss

original commit: b7f161bf2939dfedce8accbfa82b92dbe011d32a
2020-03-04 16:53:35 -05:00

299 lines
12 KiB
Scheme

;;; enum.ss
;;; Copyright 1984-2017 Cisco Systems, Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;; NOTES:
;; This implementation assume the universe is small
;; and the algorithms used by this implementation may be
;; up to linear in the universe
;;
;; This code is a good candidate for partial-static-structure optimization
;; Right now the define-enumeration macro is doing optimizations
;; that could be automatically performed by PSS if PSS worked on enums
;;
;; The R6RS standard is unclear whether the function returned by enum-set-indexer
;; should throw an error if its argument is not a symbol. We have chosen to
;; not include that check, but if the standard is updated, this may need to be changed.
(let ()
;;;;;;;;
#| Low-level enum-set definition and operations
The structure is as follows:
-------------------------------------------------------------------------------
The following records are created once:
enum-base-rtd:
+-----------------+--------------------+--------------------------------+-----+
| rtd:#!base-rtd | parent:#!base-rtd | fields:(index->sym sym->index) | ... |
+-----------------+--------------------+--------------------------------+-----+
enum-parent-rtd:
+-----------------+--------------------+--------------------------------+-----+
| rtd:#!base-rtd | parent: #f | fields:(members) | ... |
+-----------------+--------------------+--------------------------------+-----+
-------------------------------------------------------------------------------
The following record is created per enum-type and it stored the mappings
between symbols and their corresponding bits in the bit mask:
this-enum-rtd:
+-------------------+------------------------+-----------+-----
| rtd:enum-base-rtd | parent:enum-parent-rtd | fields:() | ...
+-------------------+------------------------+-----------+-----
----+------------+------------+
...| index->sym | sym->index |
----+------------+------------+
-------------------------------------------------------------------------------
The following record is created per enum-set:
an-enum-set:
+-------------------+--------------------------------+
| rtd:this-enum-rtd | members: 17 (integer bit mask) |
+-------------------+--------------------------------+
|#
(define enum-base-rtd
(make-record-type ; not sealed, not opaque
#!base-rtd ; undocumented #!base-rtd
'#{enum b9s78zmm79qs7j22-a} ; make enum-base-rtd type nongenerative
'((immutable sym->index) ; static (per enumeration type) fields
(immutable index->sym))))
(define enum-parent-rtd ; not sealed, not opaque, nongenerative
(make-record-type
'#{enum-parent dwwi4y1kribh7mif58yoxe-0}
'((immutable members))))
(define get-sym->index (csv7:record-field-accessor enum-base-rtd 'sym->index))
(define get-index->sym (csv7:record-field-accessor enum-base-rtd 'index->sym))
(define get-members (csv7:record-field-accessor enum-parent-rtd 'members))
(define members-universe -1) ;; All bits set
;;;;;;;;
;; Make a new enum-set using the rtd and the new set of members
(define (make-enum-set enum-set-rtd members)
#;((record-constructor enum-set-rtd) members)
; breaking the abstraction to avoid significant efficiency hit
($record enum-set-rtd members))
;; Perform type check for enum-set and return its RTD
(define (enum-set-rtd who enum-set)
(or (and (record? enum-set)
(let ([rtd (record-rtd enum-set)])
(and (eq? (record-rtd rtd) enum-base-rtd)
rtd)))
($oops who "~s is not an enumeration" enum-set)))
(define (assert-symbol-list who symbol-list)
(unless (and (list? symbol-list)
(for-all symbol? symbol-list))
($oops who "~s is not a list of symbols" symbol-list)))
(define (assert-symbol who symbol)
(unless (symbol? symbol)
($oops who "~s is not a symbol" symbol)))
(define rtd&list->enum-set
(lambda (who rtd symbol-list)
(let ([sym->index (get-sym->index rtd)])
(let loop ([members 0]
[symbol-list symbol-list])
(if (null? symbol-list)
(make-enum-set rtd members)
(let ([index (symbol-hashtable-ref sym->index (car symbol-list) #f)])
(if (not index)
(if who
($oops who "universe does not include specified symbol ~s"
(car symbol-list))
(loop members (cdr symbol-list)))
(loop (logbit1 index members) (cdr symbol-list)))))))))
(define $enum-set->list
(lambda (who enum-set)
(let ([rtd (enum-set-rtd who enum-set)])
(let ([index->sym (get-index->sym rtd)]
[members (get-members enum-set)])
(let loop ([i (fx1- (vector-length index->sym))]
[lst '()])
(if (fx< i 0)
lst
(loop (fx1- i)
(if (logbit? i members)
(cons (vector-ref index->sym i) lst)
lst))))))))
(record-writer enum-parent-rtd (lambda (x p wr) (display "#<enum-set>" p)))
;;;;;;;;
;; Constructor
(let ()
;; Takes lst and assigns indexes to each element of lst
;; lst :: symbol-list
;; index :: fixnum
;; symbol->index :: hashtable from symbols to fixnum
;; rev-lst :: symbol-list (stored in reverse order)
;;
;; Result :: (values fixnum (vector of symbols))
(define (make-symbol->index lst index symbol->index rev-lst)
(cond
[(null? lst)
(let ([index->symbol (make-vector index)])
(let loop ([i (fx1- index)]
[rev-lst rev-lst])
(unless (null? rev-lst) ;; or (< i 0)
(vector-set! index->symbol i (car rev-lst))
(loop (fx1- i) (cdr rev-lst))))
(values index index->symbol))]
[(symbol-hashtable-contains? symbol->index (car lst))
(make-symbol->index (cdr lst) index symbol->index rev-lst)]
[else
(symbol-hashtable-set! symbol->index (car lst) index)
(make-symbol->index (cdr lst) (fx1+ index) symbol->index (cons (car lst) rev-lst))]))
(set! make-enumeration
(lambda (symbol-list)
(assert-symbol-list 'make-enumeration symbol-list)
(let ([sym->index (make-hashtable symbol-hash eq?)])
(let-values ([(index index->sym) (make-symbol->index symbol-list 0 sym->index '())])
(let ([this-enum-rtd
($make-record-type
enum-base-rtd enum-parent-rtd "enum-type"
'() ; no fields to add
#t ; sealed
#f ; not opaque
sym->index
index->sym)])
(make-enum-set this-enum-rtd members-universe)))))))
;;;;;;;;;
;; Misc functions
(set! $enum-set-members get-members)
(set! enum-set-universe
(lambda (enum-set)
(make-enum-set (enum-set-rtd 'enum-set-universe enum-set) -1)))
(set! enum-set-indexer
(lambda (enum-set)
(let ([sym->index (get-sym->index (enum-set-rtd 'enum-set-indexer enum-set))])
(lambda (x)
(assert-symbol 'enum-set-indexer x)
(symbol-hashtable-ref sym->index x #f)))))
(set! enum-set-constructor
(lambda (enum-set)
(let ([rtd (enum-set-rtd 'enum-set-constructor enum-set)])
(lambda (symbol-list)
(assert-symbol-list 'enum-set-constructor symbol-list)
(rtd&list->enum-set 'enum-set-constructor rtd symbol-list)))))
(set! enum-set->list
(lambda (enum-set)
($enum-set->list 'enum-set->list enum-set)))
;;;;;;;;;
;; Predicates
(set! enum-set?
(lambda (enum-set)
(and (record? enum-set)
(let ([rtd (record-rtd enum-set)])
(eq? (record-rtd rtd) enum-base-rtd)))))
(let ()
(define (enum-set-subset-aux? enum-set1 enum-set2 rtd1 rtd2)
(let ([index->sym1 (get-index->sym rtd1)]
[members1 (get-members enum-set1)]
[sym->index2 (get-sym->index rtd2)]
[members2 (get-members enum-set2)])
(let loop ([index1 0])
(or (fx= index1 (vector-length index->sym1))
(let ([index2 (symbol-hashtable-ref
sym->index2
(vector-ref index->sym1 index1) #f)])
(and index2
(or (not (logbit? index1 members1))
(logbit? index2 members2))
(loop (fx1+ index1))))))))
(set! enum-set-member?
(lambda (symbol enum-set)
(assert-symbol 'enum-set-member? symbol)
(let ([sym->index (get-sym->index
(enum-set-rtd 'enum-set-member? enum-set))])
(let ([index (symbol-hashtable-ref sym->index symbol #f)])
(and index
(logbit? index (get-members enum-set)))))))
(set! enum-set-subset?
(lambda (enum-set1 enum-set2)
(let ([rtd1 (enum-set-rtd 'enum-set-subset? enum-set1)]
[rtd2 (enum-set-rtd 'enum-set-subset? enum-set2)])
(if (eq? rtd1 rtd2)
(let ([members2 (get-members enum-set2)])
(= members2 (logor (get-members enum-set1) members2)))
(enum-set-subset-aux? enum-set1 enum-set2 rtd1 rtd2)))))
(set! enum-set=?
(lambda (enum-set1 enum-set2)
(let ([rtd1 (enum-set-rtd 'enum-set=? enum-set1)]
[rtd2 (enum-set-rtd 'enum-set=? enum-set2)])
(if (eq? rtd1 rtd2)
(= (get-members enum-set1) (get-members enum-set2))
(and (enum-set-subset-aux? enum-set1 enum-set2 rtd1 rtd2)
(enum-set-subset-aux? enum-set2 enum-set1 rtd2 rtd1))))))
)
;;;;;;;;
;; Set-like functions
(let ()
(define-syntax enum-bin-op
(syntax-rules ()
[(_ name (members1 members2) members-expr)
(set! name
(lambda (enum-set1 enum-set2)
(let ([rtd1 (enum-set-rtd 'name enum-set1)]
[rtd2 (enum-set-rtd 'name enum-set2)])
(unless (eq? rtd1 rtd2)
($oops 'name "~s and ~s have different enumeration types"
enum-set1 enum-set2))
(make-enum-set rtd1 (let ([members1 (get-members enum-set1)]
[members2 (get-members enum-set2)])
members-expr)))))]))
(enum-bin-op enum-set-union (members1 members2) (logor members1 members2))
(enum-bin-op enum-set-intersection (members1 members2) (logand members1 members2))
(enum-bin-op enum-set-difference (members1 members2) (logand members1 (lognot members2)))
)
;;;;;;;;
;; Other functions
(set! enum-set-complement
(lambda (enum-set)
(let ([rtd (enum-set-rtd 'enum-set-complement enum-set)])
(make-enum-set rtd (lognot (get-members enum-set))))))
(set! enum-set-projection
(lambda (enum-set1 enum-set2)
(rtd&list->enum-set #f
(enum-set-rtd 'enum-set-projection enum-set2)
($enum-set->list 'enum-set-projection enum-set1))))
)