;;  ClassicJava: an implementation of the ClassicJava programming language
;; Implementation of a functional store.
;;  Copyright (C) 2005  Richard Cobbe
;; This store is implemented as an alist(-ish), but note that store-update
;; (functionally) replaces the value in the existing alist rather than
;; returning a longer alist.  This is helpful for debugging and testing.
;; =================================================================
;;  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

(module store mzscheme

  (require (lib "")
           (lib "")
           (planet "" ("dherman" "inspector.plt" 1 0)))

   (define-struct store (top contents)))
  ;; (Store X) ::= (make-store Number (Listof (List Number X)))

  ;; store-alloc :: (Store X) X -> Number (Store X)
  ;; allocates & inits new entry in store; returns new location and new store
  (define store-alloc
    (lambda (store new-val)
      (let* ([addr (store-top store)]
             [next-top (add1 addr)])
        (values addr
                (make-store next-top (cons (list addr new-val)
                                           (store-contents store)))))))

  ;; store-ref :: (Store X) Number -> X
  ;; looks up value at locn in store; invokes FK if bad address.
  ;; Default fk raises exn:application:mismatch.
  (define store-ref
    (opt-lambda (store locn [fk (lambda ()
                                  (raise (make-exn:fail:contract
                                           "address ~a not in domain of store"
      (let loop ([entries (store-contents store)])
          [(null? entries) (fk)]
          [(= locn (caar entries)) (cadar entries)]
          [else (loop (cdr entries))]))))

  ;; store-update :: (Store X) Number X -> (Store X)
  ;; Updates store at locn.  If locn invalid, raises exn:application:mismatch.
  (define store-update
    (lambda (store locn val)
      (make-store (store-top store)
                  (let loop ([entries (store-contents store)])
                      [(null? entries)
                       (raise (make-exn:fail:contract
                                "attempted to update address (~a) not in store"
                      [(= locn (caar entries))
                       (cons (list locn val) (cdr entries))]
                      [else (cons (car entries) (loop (cdr entries)))])))))

  (define empty-store (make-store 0 null))

  ;; create-store :: (List Number X)* -> (Store X)
  ;; Creates a store from a sequence of address/value lists.
  (define create-store
    (lambda entries
      (let ([max-addr (apply max (cons -1 (map car entries)))])
        (make-store (add1 max-addr) entries))))

  ;; builds a store out of a list of address/value pairs (much like the
  ;; hash-table macro in
  (define-syntax build-store
    (syntax-rules ()
      [(_ (addr value) ...)
       (create-store (list addr value) ...)]))

  ;; (Store X) -> (AList Number X))
  ;; converts store to alist for display
  (define store->alist
    (lambda (store)
      (map (lambda (entry) (cons (car entry) (cadr entry)))
           (store-contents store))))

   [store-alloc     (-> store? any/c (values number? store?))]
   [store-ref       (opt->* (store? number?)
                            ((-> any))
   [store-update    (-> store? number? any/c store?)]
   [empty-store     store?]
   [store?          (-> any/c boolean?)]
   [store->alist    (-> store? (listof (cons/c number? any/c)))])

  (provide [rename build-store store]))