private/bindings.ss
#lang scheme
;;; PLT Scheme Inference Collection
;;; bindings.ss
;;; Copyright (c) 2006-2008 M. Douglas Williams
;;;
;;; This library is free software; you can redistribute it and/or
;;; modify it under the terms of the GNU Lesser General Public
;;; License as published by the Free Software Foundation; either
;;; version 2.1 of the License, or (at your option) any later version.
;;;
;;; This library is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;;; Lesser General Public License for more details.
;;;
;;; You should have received a copy of the GNU Lesser General Public
;;; License along with this library; if not, write to the Free
;;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
;;; 02111-1307 USA.
;;;
;;; -----------------------------------------------------------------------------
;;;
;;; Bindings are represented by association lists. Originally, I had converted
;;; them mutable list, but have changed them back and removed bindings-update!
;;; and bindings-remove!, which were never used.
;;;
;;; Verified the use of immutable lists.
;;;
;;; Todo:
;;;  - Since we are once again using immutable lists, bindings must
;;;    be merged using append, which can be slow.  Check if this is
;;;    a significant issue.
;;;  - Would hash tables be a better data structure to use?
;;;
;;; Version  Date      Description
;;; 2.0.0    06/26/08  Changes for V4.0.  Changed implementation to use mutable
;;;                    lists. (Doug Williams)
;;; 2.0.1    07/02/08  Reverted to immutable lists. (Doug Williams)
;;; 2.0.2    12/24/08  Added contracts and cleaned up the code. (Doug Williams)

(require (only-in srfi/1 every))

;;; -----------------------------------------------------------------------------
;;;                                  Bindings
;;; -----------------------------------------------------------------------------

;;; (bindings? x) -> boolean?
;;;   x : any/c
;;; Returns #t if x is a bindings object.  This code makes sure the bindings are
;;; an association list with every key being a symbol?
(define (bindings? x)
  (and (list? x)
       (every
        (lambda (element)
          (and (pair? element)
               (symbol? (car element))))
        x)))

;;; (make-bindings) -> bindings?
;;; Returns an empty bindings object.
(define (make-bindings)
  '())

;;; (bindings-bound? bindings key) -> boolean?
;;;   bindings : bindings?
;;;   key : symbol?
;;; Returns #t if the key is bound in bindings.
(define (bindings-bound? bindings key)
  (let ((binding (assq key bindings)))
    (if binding #t #f)))

;;; (bindings-put! bindings key value) -> bindings?
;;;   bindings : bindings?
;;;   key : symbol?
;;;   value : any/c
;;; Binds key to datum in bindings.
(define (bindings-put! bindings key value)
  (cons (cons key value) bindings))

;;; (bindings-ref bindings key) -> any
;;;   bindings : bindings?
;;;   key : symbol?
;;; Returns the value of the key in bindings.  An error is signaled if the key is
;;; not bound in bindings.
(define (bindings-ref bindings key)
  (let ((binding (assq key bindings)))
    (if binding
        (cdr binding)
        (error 'bindings-ref
               "No binding for ~a in ~a" key bindings))))

;;; (bindings-map bindings proc) -> list?
;;;   bindings : bindings?
;;;   proc : (-> symbol? any/c any)
;;; Applies proc to all of the bindings and returns a list of the results.
(define (bindings-map bindings proc)
  (map
   (lambda (binding)
     (proc (car binding) (cdr binding)))
   bindings))

;;; (bindings-for-each bindings proc) -> void?
;;;   bindings : bindings?
;;;   proc : (-> symbol? any/c any)
;;; Applies proc to all of the bindings.
(define (bindings-for-each bindings proc)
  (for-each
   (lambda (binding)
     (proc (car binding) (cdr binding)))
   bindings))

;;; (bindings-keys: bindings) -> (listof symbol?)
;;;   bindings : bindings?
;;; Returns a list of the keys in the bindings.
(define (bindings-keys bindings)
  (bindings-map bindings
   (lambda (key datum)
      key)))

;;; bindings-values: bindings? -> list?
;;;   bindings : bindings?
;;; Returns a list of the values in the bindings.
(define (bindings-values bindings)
  (bindings-map bindings
   (lambda (key value)
     value)))

;;; bindings-keys-values: bindings? -> (listof symbol?) list?
;;;   bindings : bindings?
;;; Returns a list of the keys and a list of the values in the bindings.
(define (bindings-keys-values bindings)
  (let ((keys (bindings-keys bindings))
        (data (bindings-values bindings)))
    (values keys data)))

;;; -----------------------------------------------------------------------------
;;;                               Module Contracts
;;; -----------------------------------------------------------------------------

(provide/contract
 (bindings?
  (-> any/c boolean?))
 (make-bindings
  (-> bindings?))
 (bindings-bound?
  (-> bindings? symbol? boolean?))
 (bindings-put!
  (-> bindings? symbol? any/c bindings?))
 (bindings-ref
  (-> bindings? symbol? any))
 (bindings-map
  (-> bindings? (-> symbol? any/c any) list?))
 (bindings-for-each
  (-> bindings? (-> symbol? any/c any) void?))
 (bindings-keys
  (-> bindings? (listof symbol?)))
 (bindings-values
  (-> bindings? list?))
 (bindings-keys-values
  (-> bindings? (values (listof symbol?) list))))