229 lines
5.8 KiB
Scheme
229 lines
5.8 KiB
Scheme
;;; Environment manipulation
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;;; (var . val) / "var=val" rep conversion:
|
|
|
|
|
|
|
|
(define (split-env-string var=val)
|
|
(let ((i (string-index var=val #\=)))
|
|
(if i (values (substring var=val 0 i)
|
|
(substring var=val (+ i 1) (string-length var=val)))
|
|
(error "No \"=\" in environment string" var=val))))
|
|
|
|
(define (env-list->alist env-list)
|
|
(map (lambda (var=val)
|
|
(call-with-values (lambda () (split-env-string var=val))
|
|
cons))
|
|
env-list))
|
|
|
|
(define (alist->env-vec alist)
|
|
(list->vector (map (lambda (var.val)
|
|
(string-append (car var.val) "="
|
|
(let ((val (cdr var.val)))
|
|
(if (string? val) val
|
|
(string-join val ":")))))
|
|
alist)))
|
|
|
|
;;; ENV->ALIST
|
|
|
|
(import-os-error-syscall %load-env () "scm_envvec")
|
|
|
|
(define (environ-env->alist)
|
|
(let ((env-list.envvec (%load-env)))
|
|
(cons (env-list->alist (car env-list.envvec))
|
|
(cdr env-list.envvec))))
|
|
|
|
|
|
;;; ALIST->ENV
|
|
|
|
;;; (%create-env ((vector 'X) -> address))
|
|
(import-os-error-syscall %create-env (envvec) "create_env")
|
|
|
|
;;; assumes aligned env
|
|
(define (envvec-alist->env alist)
|
|
(%create-env (alist->env-vec alist)))
|
|
|
|
(import-os-error-syscall %align-env (envvec) "align_env")
|
|
|
|
(import-os-error-syscall %free-env (envvec) "free_envvec")
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;; Environment per thread
|
|
(define *env-cache* 'uninitialized)
|
|
(define env-lock (make-lock))
|
|
|
|
(define (install-env)
|
|
(set! *env-cache* (environ**-read))
|
|
(set! $env ;;; TODO The old thread-fluid will remain
|
|
(make-preserved-thread-fluid
|
|
(env-cache))))
|
|
; (set! env-lock (make-lock)))
|
|
|
|
(define (env-cache)
|
|
*env-cache*)
|
|
|
|
;; Actually do the syscall and update the cache
|
|
;; assumes the env lock obtained
|
|
(define (change-and-cache-env env)
|
|
(environ**-set env)
|
|
(set! *env-cache* env))
|
|
|
|
;; The thread-specific env: A thread fluid
|
|
|
|
(define $env 'empty-env-value)
|
|
|
|
(define (thread-read-env) (thread-fluid $env))
|
|
(define (thread-set-env! res) (set-thread-fluid! $env res))
|
|
(define (let-env res thunk)
|
|
(let-thread-fluid $env res thunk))
|
|
|
|
(define (really-with-env* env thunk)
|
|
(with-lock env-lock
|
|
(lambda ()
|
|
(change-and-cache-env env)))
|
|
(let-env env thunk))
|
|
|
|
(define (align-env!)
|
|
(let ((res (thread-read-env)))
|
|
(if (not (env=? res (env-cache)))
|
|
(change-and-cache-env res))))
|
|
|
|
(define (thread-change-env res)
|
|
(with-lock env-lock
|
|
(lambda ()
|
|
(change-and-cache-env res)
|
|
(thread-set-env! (env-cache)))))
|
|
|
|
(define environ-resource (make-resource align-env! env-lock))
|
|
|
|
(define env-reinitializer
|
|
(make-reinitializer install-env))
|
|
|
|
(define-record env
|
|
envvec
|
|
alist) ; Corresponding alist
|
|
|
|
(define-record-resumer type/env
|
|
(lambda (env)
|
|
(set-env:envvec env #f)))
|
|
|
|
(define (env=? e1 e2)
|
|
(and (env:envvec e1)
|
|
(eq? (env:envvec e1)
|
|
(env:envvec e2))))
|
|
|
|
(define-record envvec
|
|
environ ;; char**
|
|
)
|
|
|
|
(define (add-envvec-finalizer! envvec)
|
|
(add-finalizer! envvec envvec-finalizer))
|
|
|
|
(define-exported-binding "envvec-record-type" type/envvec)
|
|
(define-exported-binding "add-envvec-finalizer!" add-envvec-finalizer!)
|
|
|
|
(define (envvec-finalizer envvec)
|
|
(%free-env envvec))
|
|
|
|
(define (environ**-read)
|
|
(let ((alist.envvec (environ-env->alist)))
|
|
(make-env (cdr alist.envvec) (car alist.envvec))))
|
|
|
|
(define (environ**-set env)
|
|
(if (env:envvec env)
|
|
(%align-env (env:envvec env))
|
|
(set-env:envvec env (envvec-alist->env (env:alist env)))))
|
|
|
|
(define (getenv var)
|
|
(let* ((env (thread-read-env))
|
|
(res (assoc var (env:alist env))))
|
|
(if res (cdr res) res)))
|
|
|
|
(define (env->alist)
|
|
(env:alist (thread-read-env)))
|
|
|
|
(define (setenv var val)
|
|
(let* ((env (thread-read-env))
|
|
(alist (if val
|
|
(alist-update
|
|
var
|
|
val
|
|
(env:alist env))
|
|
(alist-delete
|
|
var
|
|
(env:alist env)))))
|
|
(thread-set-env!
|
|
(make-env
|
|
#f
|
|
alist))))
|
|
|
|
(define (alist->env alist)
|
|
(thread-set-env!
|
|
(make-env
|
|
#f
|
|
alist)))
|
|
|
|
(define (with-env* alist-delta thunk)
|
|
(let ((new-env (fold (lambda (key/val alist)
|
|
(alist-update (car key/val) (cdr key/val) alist))
|
|
(env->alist)
|
|
alist-delta)))
|
|
(with-total-env* new-env thunk)))
|
|
|
|
(define (with-total-env* alist thunk)
|
|
(really-with-env* (make-env #f alist) thunk))
|
|
|
|
(define (alist-delete key alist)
|
|
(filter (lambda (key/val) (not (equal? key (car key/val)))) alist))
|
|
|
|
(define (alist-update key val alist)
|
|
(cons (cons key val)
|
|
(alist-delete key alist)))
|
|
|
|
;;; Remove shadowed entries from ALIST. Preserves element order.
|
|
;;; (This version shares no structure.)
|
|
|
|
(define (alist-compress alist)
|
|
(reverse (let compress ((alist alist) (ans '()))
|
|
(if (pair? alist)
|
|
(let ((key/val (car alist))
|
|
(alist (cdr alist)))
|
|
(compress alist (if (assoc (car key/val) ans) ans
|
|
(cons key/val ans))))
|
|
ans))))
|
|
|
|
(define (add-before elt before list)
|
|
(let rec ((list list))
|
|
(if (pair? list)
|
|
(let ((x (car list)))
|
|
(if (equal? x before)
|
|
(cons elt list)
|
|
(cons x (rec (cdr list)))))
|
|
(cons elt list))))
|
|
|
|
;;; In ADD-AFTER, the labelled LET adds ELT after the last occurrence of AFTER
|
|
;;; in LIST, and returns the list. However, if the LET finds no occurrence
|
|
;;; of AFTER in LIST, it returns #F instead.
|
|
|
|
(define (add-after elt after list)
|
|
(or (let rec ((list list))
|
|
(if (pair? list)
|
|
(let* ((x (car list))
|
|
(tail (cdr list))
|
|
(ans (rec tail))) ; #f if AFTER wasn't encountered.
|
|
(cond (ans (cons x ans))
|
|
((equal? x after)
|
|
(cons x (cons elt tail)))
|
|
(else #f))) ; AFTER doesn't appear in LIST.
|
|
#f)) ; AFTER doesn't appear in LIST.
|
|
(cons elt list)))
|
|
|
|
(define-simple-syntax (with-env delta . body)
|
|
(with-env* `delta (lambda () . body)))
|
|
|
|
(define-simple-syntax (with-total-env env . body)
|
|
(with-total-env* `env (lambda () . body)))
|
|
|
|
(install-env)
|