(module environment mzscheme
(require (lib "contract.ss")
(lib "etc.ss")
(lib "list.ss")
(planet "contract-utils.ss" ("cobbe" "contract-utils.plt" 1 0)))
(define-struct env (key-eq? keys vals))
(define-struct (exn:env:unbound exn:fail:contract) (key) (make-inspector))
(define-struct (exn:env:shadow exn:fail:contract) (key) #f)
(define valid-env?
(lambda (env)
(let ([key-eq? (env-key-eq? env)])
(and (= (length (env-keys env)) (length (env-vals env)))
(recur loop ([keys (env-keys env)])
(cond
[(null? keys) #t]
[(memf (lambda (k) (key-eq? (car keys) k)) (cdr keys)) #f]
[else (loop (cdr keys))]))))))
(define make-empty-env
(opt-lambda ([key-eq? eq?])
(make-env key-eq? null null)))
(define extend-env
(lambda (keys vals env)
(let ([key-eq? (env-key-eq? env)]
[keys (append keys (env-keys env))]
[vals (append vals (env-vals env))])
(let-values ([(keys vals)
(remove-shadowed key-eq? keys vals)])
(make-env key-eq? keys vals)))))
(define remove-shadowed
(lambda (key-eq? keys vals)
(let ([f (lambda (key1)
(lambda (key2)
(key-eq? key1 key2)))])
(recur loop ([keys keys]
[vals vals]
[keys-seen null]
[keys-result null]
[vals-result null])
(cond
[(null? keys)
(values (reverse keys-result)
(reverse vals-result))]
[(memf (f (car keys)) keys-seen)
(loop (cdr keys) (cdr vals) keys-seen keys-result vals-result)]
[else
(loop (cdr keys) (cdr vals)
(cons (car keys) keys-seen)
(cons (car keys) keys-result)
(cons (car vals) vals-result))])))))
(define extend-unique
(lambda (keys0 vals env)
(let ([key-eq? (env-key-eq? env)])
(recur loop ([keys keys0])
(cond
[(null? keys) (extend-env keys0 vals env)]
[(bound? env (car keys))
(raise (make-exn:env:shadow
"extend-unique: argument shadows binding in environment"
(current-continuation-marks)
(car keys)))]
[(memf (lambda (key) (key-eq? key (car keys))) (cdr keys))
(raise (make-exn:env:shadow
"extend-unique: duplicate key in argument"
(current-continuation-marks)
(car keys)))]
[else (loop (cdr keys))])))))
(define-syntax env-macro
(syntax-rules ()
[(_ key-eq? (key val) ...)
(extend-env (list key ...) (list val ...) (make-empty-env key-eq?))]))
(define-syntax symbol-env
(syntax-rules ()
[(_ (id val) ...)
(extend-env (list (quote id) ...)
(list val ...)
(make-empty-env eq?))]))
(define weaken-env
(lambda (env new-eq?)
(let* ([old-eq? (env-key-eq? env)]
[wrapped-eq?
(lambda (x y)
(let ([old (old-eq? x y)]
[new (new-eq? x y)])
(if (and old (not new))
(raise (make-exn:fail:contract
"env's equality predicate improperly weakened"
(current-continuation-marks)))
new)))])
(let-values ([(keys vals)
(remove-shadowed wrapped-eq?
(env-keys env)
(env-vals env))])
(make-env wrapped-eq? keys vals)))))
(define lookup
(opt-lambda (env key
[fk
(lambda ()
(raise (make-exn:env:unbound
"unbound identifier"
(current-continuation-marks)
key)))])
(let ([key-eq? (env-key-eq? env)])
(recur loop ([keys (env-keys env)]
[vals (env-vals env)])
(cond
[(null? keys) (fk)]
[(key-eq? key (car keys)) (car vals)]
[else (loop (cdr keys) (cdr vals))])))))
(define lookup/eq
(case-lambda
[(env key key-eq?) (lookup (weaken-env env key-eq?) key)]
[(env key key-eq? fk) (lookup (weaken-env env key-eq?) key fk)]))
(define env-map
(lambda (f env)
(make-env (env-key-eq? env)
(env-keys env)
(map f (env-vals env)))))
(define env-foldr
(lambda (f base env)
(foldr f base (env-vals env))))
(define env-foldl
(lambda (f base env)
(foldl f base (env-vals env))))
(define bound?
(lambda (env key)
(let ([key-eq? (env-key-eq? env)])
(and (memf (lambda (k) (key-eq? k key)) (env-keys env))
#t))))
(define env-domain
(lambda (env)
(apply list (env-keys env))))
(define restrict-domain
(lambda (env pred?)
(recur loop ([keys (env-keys env)]
[vals (env-vals env)]
[key-accum null]
[val-accum null])
(cond
[(null? keys) (make-env (env-key-eq? env)
(reverse key-accum)
(reverse val-accum))]
[(pred? (car keys)) (loop (cdr keys) (cdr vals)
(cons (car keys) key-accum)
(cons (car vals) val-accum))]
[else (loop (cdr keys) (cdr vals) key-accum val-accum)]))))
(define env->sexp
(lambda (env)
(map list (env-keys env) (env-vals env))))
(define env->alist
(lambda (env)
(map cons (env-keys env) (env-vals env))))
(define fast-env/c
(lambda (key/c val/c)
(and/c env?
(struct/c env any/c (listof key/c) (listof val/c)))))
(define env/c
(lambda (key/c val/c)
(and/c (fast-env/c key/c val/c) valid-env?)))
(define binary-pred/c (-> any/c any/c boolean?))
(provide [rename env-macro env]
symbol-env)
(provide/contract [make-empty-env (opt-> () [binary-pred/c]
env?)]
[extend-env (->r ([keys list?]
[vals
(lambda (v)
(and (list? v)
(= (length keys)
(length v))))]
[env env?])
env?)]
[extend-unique (->r ([keys list?]
[vals
(lambda (v)
(and (list? v)
(= (length keys)
(length v))))]
[env env?])
env?)]
[weaken-env (-> env? binary-pred/c env?)]
[lookup (opt-> (env? any/c)
[(-> any)]
any)]
[lookup/eq (case-> (-> env? any/c binary-pred/c any)
(-> env? any/c binary-pred/c (-> any)
any))]
[env-map (-> (-> any/c any) env? env?)]
[env-foldr (-> (-> any/c any/c any)
any/c
env?
any)]
[env-foldl (-> (-> any/c any/c any)
any/c
env?
any)]
[bound? (-> env? any/c boolean?)]
[env-domain (-> env? list?)]
[restrict-domain (-> env? predicate/c env?)]
[env->sexp (-> env? (listof (list/c any/c any/c)))]
[env->alist (-> env? (listof pair?))]
[env? predicate/c]
[env/c (-> (union contract? predicate/c)
(union contract? predicate/c)
contract?)]
[fast-env/c (-> (union contract? predicate/c)
(union contract? predicate/c)
contract?)]
[struct (exn:env:unbound exn:fail:contract)
([message string?]
[continuation-marks continuation-mark-set?]
[key any/c])]
[struct (exn:env:shadow exn:fail:contract)
([message string?]
[continuation-marks continuation-mark-set?]
[key any/c])]))