422 lines
15 KiB
Scheme
422 lines
15 KiB
Scheme
|
;;; 4.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.
|
||
|
|
||
|
(begin
|
||
|
(define-who apply
|
||
|
(let ()
|
||
|
(define-syntax build-apply
|
||
|
(lambda (x)
|
||
|
(syntax-case x ()
|
||
|
[(_ () cl ...)
|
||
|
#'(case-lambda
|
||
|
[(p r)
|
||
|
(unless (procedure? p)
|
||
|
($oops #f "attempt to apply non-procedure ~s" p))
|
||
|
(let ([n ($list-length r who)])
|
||
|
(case n
|
||
|
[(0) (p)]
|
||
|
[(1) (p (car r))]
|
||
|
[(2) (p (car r) (cadr r))]
|
||
|
[(3) (let ([y1 (cdr r)]) (p (car r) (car y1) (cadr y1)))]
|
||
|
[else ($apply p n r)]))]
|
||
|
cl ...
|
||
|
[(p x . r)
|
||
|
(unless (procedure? p)
|
||
|
($oops #f "attempt to apply non-procedure ~s" p))
|
||
|
(let ([r (cons x ($apply list* ($list-length r who) r))])
|
||
|
($apply p ($list-length r who) r))])]
|
||
|
[(_ (s1 s2 ...) cl ...)
|
||
|
(with-syntax ((m (length #'(s1 s2 ...))))
|
||
|
#'(build-apply
|
||
|
(s2 ...)
|
||
|
[(p s1 s2 ... r)
|
||
|
(unless (procedure? p)
|
||
|
($oops #f "attempt to apply non-procedure ~s" p))
|
||
|
(let ([n ($list-length r who)])
|
||
|
(case n
|
||
|
[(0) (p s1 s2 ...)]
|
||
|
[(1) (p s1 s2 ... (car r))]
|
||
|
[(2) (p s1 s2 ... (car r) (cadr r))]
|
||
|
[(3) (let ([y1 (cdr r)])
|
||
|
(p s1 s2 ... (car r) (car y1) (cadr y1)))]
|
||
|
[else ($apply p (fx+ n m) (list* s1 s2 ... r))]))]
|
||
|
cl ...))])))
|
||
|
(build-apply (x1 x2 x3 x4))))
|
||
|
|
||
|
(let ()
|
||
|
(define length-error
|
||
|
(lambda (who l1 l2)
|
||
|
($oops who "lists ~s and ~s differ in length" l1 l2)))
|
||
|
|
||
|
(define nonprocedure-error
|
||
|
(lambda (who what)
|
||
|
($oops who "~s is not a procedure" what)))
|
||
|
|
||
|
(define length-check
|
||
|
(lambda (who first rest)
|
||
|
(let ([n ($list-length first who)])
|
||
|
(let loop ([rest rest])
|
||
|
(cond
|
||
|
[(null? rest) n]
|
||
|
[(fx= ($list-length (car rest) who) n) (loop (cdr rest))]
|
||
|
[else (length-error who first (car rest))])))))
|
||
|
|
||
|
(define mutation-error
|
||
|
(lambda (who)
|
||
|
($oops who "input list was altered during operation")))
|
||
|
|
||
|
; getcxrs returns the cdrs of ls and their cars
|
||
|
(define getcxrs
|
||
|
(lambda (ls who)
|
||
|
(if (null? ls)
|
||
|
(values '() '())
|
||
|
(let-values ([(cdrs cars) (getcxrs (cdr ls) who)])
|
||
|
(let ([d (cdar ls)])
|
||
|
(unless (pair? d) (mutation-error who))
|
||
|
(values (cons d cdrs) (cons (car d) cars)))))))
|
||
|
|
||
|
(let ()
|
||
|
(define-syntax do-ormap
|
||
|
(syntax-rules ()
|
||
|
[(_ who)
|
||
|
(case-lambda
|
||
|
[(f ls)
|
||
|
(unless (procedure? f) (nonprocedure-error who f))
|
||
|
(and (not (null? ls))
|
||
|
(let ormap ([n ($list-length ls who)] [ls ls])
|
||
|
(if (fx= n 1)
|
||
|
(f (car ls))
|
||
|
(or (f (car ls))
|
||
|
(let ([ls (cdr ls)])
|
||
|
(unless (pair? ls) (mutation-error who))
|
||
|
(ormap (fx- n 1) ls))))))]
|
||
|
[(f ls . more)
|
||
|
(unless (procedure? f) (nonprocedure-error who f))
|
||
|
(let ([n (length-check who ls more)])
|
||
|
(and (not (fx= n 0))
|
||
|
(let ormap ([n n] [ls ls] [more more] [cars (map car more)])
|
||
|
(if (fx= n 1)
|
||
|
(apply f (car ls) cars)
|
||
|
(or (apply f (car ls) cars)
|
||
|
(let ([ls (cdr ls)])
|
||
|
(unless (pair? ls) (mutation-error who))
|
||
|
(let-values ([(cdrs cars) (getcxrs more who)])
|
||
|
(ormap (fx- n 1) ls cdrs cars))))))))])]))
|
||
|
(set-who! ormap (do-ormap who))
|
||
|
(set-who! exists (do-ormap who)))
|
||
|
|
||
|
(let ()
|
||
|
(define-syntax do-andmap
|
||
|
(syntax-rules ()
|
||
|
[(_ who)
|
||
|
(case-lambda
|
||
|
[(f ls)
|
||
|
(unless (procedure? f) (nonprocedure-error who f))
|
||
|
(or (null? ls)
|
||
|
(let andmap ([n ($list-length ls who)] [ls ls])
|
||
|
(if (fx= n 1)
|
||
|
(f (car ls))
|
||
|
(and (f (car ls))
|
||
|
(let ([ls (cdr ls)])
|
||
|
(unless (pair? ls) (mutation-error who))
|
||
|
(andmap (fx- n 1) ls))))))]
|
||
|
[(f ls . more)
|
||
|
(unless (procedure? f) (nonprocedure-error who f))
|
||
|
(let ([n (length-check who ls more)])
|
||
|
(or (fx= n 0)
|
||
|
(let andmap ([n n] [ls ls] [more more] [cars (map car more)])
|
||
|
(if (fx= n 1)
|
||
|
(apply f (car ls) cars)
|
||
|
(and (apply f (car ls) cars)
|
||
|
(let ([ls (cdr ls)])
|
||
|
(unless (pair? ls) (mutation-error who))
|
||
|
(let-values ([(cdrs cars) (getcxrs more who)])
|
||
|
(andmap (fx- n 1) ls cdrs cars))))))))])]))
|
||
|
(set-who! andmap (do-andmap who))
|
||
|
(set-who! for-all (do-andmap who)))
|
||
|
|
||
|
(set-who! map
|
||
|
(case-lambda
|
||
|
[(f ls)
|
||
|
(unless (procedure? f) (nonprocedure-error who f))
|
||
|
($list-length ls who)
|
||
|
; library map cdrs first to avoid getting sick if f mutates input
|
||
|
(#3%map f ls)]
|
||
|
[(f ls1 ls2)
|
||
|
(unless (procedure? f) (nonprocedure-error who f))
|
||
|
(unless (fx= ($list-length ls1 who) ($list-length ls2 who))
|
||
|
(length-error who ls1 ls2))
|
||
|
; library map cdrs first to avoid getting sick if f mutates input
|
||
|
(#3%map f ls1 ls2)]
|
||
|
[(f ls . more)
|
||
|
(unless (procedure? f) (nonprocedure-error who f))
|
||
|
(length-check who ls more)
|
||
|
(let map ([f f] [ls ls] [more more])
|
||
|
(if (null? ls)
|
||
|
'()
|
||
|
; cdr first to avoid getting sick if f mutates input
|
||
|
(let ([tail (map f (cdr ls) (#3%map cdr more))])
|
||
|
(cons (apply f (car ls) (#3%map car more)) tail))))]))
|
||
|
|
||
|
(set! $map
|
||
|
; same as map but errors are reported as coming from who
|
||
|
(case-lambda
|
||
|
[(who f ls)
|
||
|
(unless (procedure? f) (nonprocedure-error who f))
|
||
|
($list-length ls who)
|
||
|
; library map cdrs first to avoid getting sick if f mutates input
|
||
|
(#3%map f ls)]
|
||
|
[(who f ls1 ls2)
|
||
|
(unless (procedure? f) (nonprocedure-error who f))
|
||
|
(unless (fx= ($list-length ls1 who) ($list-length ls2 who))
|
||
|
(length-error who ls1 ls2))
|
||
|
; library map cdrs first to avoid getting sick if f mutates input
|
||
|
(#3%map f ls1 ls2)]
|
||
|
[(who f ls . more)
|
||
|
(unless (procedure? f) (nonprocedure-error who f))
|
||
|
(length-check who ls more)
|
||
|
(let map ([f f] [ls ls] [more more])
|
||
|
(if (null? ls)
|
||
|
'()
|
||
|
; cdr first to avoid getting sick if f mutates input
|
||
|
(let ([tail (map f (cdr ls) (#3%map cdr more))])
|
||
|
(cons (apply f (car ls) (#3%map car more)) tail))))]))
|
||
|
|
||
|
(set-who! for-each
|
||
|
(case-lambda
|
||
|
[(f ls)
|
||
|
(unless (procedure? f) (nonprocedure-error who f))
|
||
|
(unless (null? ls)
|
||
|
(let for-each ([n ($list-length ls who)] [ls ls])
|
||
|
(if (fx= n 1)
|
||
|
(f (car ls))
|
||
|
(begin
|
||
|
(f (car ls))
|
||
|
(let ([ls (cdr ls)])
|
||
|
(unless (pair? ls) (mutation-error who))
|
||
|
(for-each (fx- n 1) ls))))))]
|
||
|
[(f ls . more)
|
||
|
(unless (procedure? f) (nonprocedure-error who f))
|
||
|
(let ([n (length-check who ls more)])
|
||
|
(unless (fx= n 0)
|
||
|
(let for-each ([n n] [ls ls] [more more] [cars (map car more)])
|
||
|
(if (fx= n 1)
|
||
|
(apply f (car ls) cars)
|
||
|
(begin
|
||
|
(apply f (car ls) cars)
|
||
|
(let ([ls (cdr ls)])
|
||
|
(unless (pair? ls) (mutation-error who))
|
||
|
(let-values ([(cdrs cars) (getcxrs more who)])
|
||
|
(for-each (fx- n 1) ls cdrs cars))))))))]))
|
||
|
|
||
|
(set-who! fold-left
|
||
|
(case-lambda
|
||
|
[(combine nil ls)
|
||
|
(unless (procedure? combine) (nonprocedure-error who combine))
|
||
|
(cond
|
||
|
[(null? ls) nil]
|
||
|
[else
|
||
|
($list-length ls who)
|
||
|
(let fold-left ([ls ls] [acc nil])
|
||
|
(let ([cdrls (cdr ls)])
|
||
|
(if (pair? cdrls)
|
||
|
(fold-left cdrls (combine acc (car ls)))
|
||
|
(if (null? cdrls)
|
||
|
(combine acc (car ls))
|
||
|
(mutation-error who)))))])]
|
||
|
[(combine nil ls . more)
|
||
|
(unless (procedure? combine) (nonprocedure-error who combine))
|
||
|
(length-check who ls more)
|
||
|
(if (null? ls)
|
||
|
nil
|
||
|
(let fold-left ([ls ls] [more more] [cars (map car more)] [acc nil])
|
||
|
(let ([cdrls (cdr ls)])
|
||
|
(if (null? cdrls)
|
||
|
(apply combine acc (car ls) cars)
|
||
|
(let ([acc (apply combine acc (car ls) cars)])
|
||
|
(unless (pair? cdrls) (mutation-error who))
|
||
|
(let-values ([(cdrs cars) (getcxrs more who)])
|
||
|
(fold-left cdrls cdrs cars acc)))))))]))
|
||
|
|
||
|
(set-who! fold-right
|
||
|
(case-lambda
|
||
|
[(combine nil ls)
|
||
|
(unless (procedure? combine) (nonprocedure-error who combine))
|
||
|
($list-length ls who)
|
||
|
; #3%fold-right naturally does cdrs first to avoid mutation sickness
|
||
|
(#3%fold-right combine nil ls)]
|
||
|
[(combine nil ls1 ls2)
|
||
|
(unless (procedure? combine) (nonprocedure-error who combine))
|
||
|
(unless (fx= ($list-length ls1 who) ($list-length ls2 who))
|
||
|
(length-error who ls1 ls2))
|
||
|
; #3%fold-right naturally does cdrs first to avoid mutation sickness
|
||
|
(#3%fold-right combine nil ls1 ls2)]
|
||
|
[(combine nil ls . more)
|
||
|
(unless (procedure? combine) (nonprocedure-error who combine))
|
||
|
(length-check who ls more)
|
||
|
(let fold-right ([combine combine] [nil nil] [ls ls] [more more])
|
||
|
(if (null? ls)
|
||
|
nil
|
||
|
(apply combine (car ls)
|
||
|
(#3%fold-right cons
|
||
|
(list (fold-right combine nil (cdr ls) (map cdr more)))
|
||
|
(map car more)))))]))
|
||
|
)
|
||
|
|
||
|
(let ()
|
||
|
(define disable/enable (make-winder #f disable-interrupts enable-interrupts))
|
||
|
|
||
|
(define (dwind in body out)
|
||
|
(let ((old-winders ($current-winders)))
|
||
|
(in)
|
||
|
($current-winders (cons (make-winder #f in out) old-winders))
|
||
|
(call-with-values
|
||
|
body
|
||
|
(case-lambda
|
||
|
[(x)
|
||
|
($current-winders old-winders)
|
||
|
(out)
|
||
|
x]
|
||
|
[args
|
||
|
($current-winders old-winders)
|
||
|
(out)
|
||
|
(apply values args)]))))
|
||
|
|
||
|
(define (cwind in body out)
|
||
|
(let* ((old-winders ($current-winders))
|
||
|
[d/e+old-winders (cons disable/enable old-winders)])
|
||
|
(disable-interrupts)
|
||
|
($current-winders d/e+old-winders)
|
||
|
(in)
|
||
|
($current-winders (cons (make-winder #t in out) old-winders))
|
||
|
(enable-interrupts)
|
||
|
(call-with-values
|
||
|
body
|
||
|
(case-lambda
|
||
|
[(x)
|
||
|
(disable-interrupts)
|
||
|
($current-winders d/e+old-winders)
|
||
|
(out)
|
||
|
($current-winders old-winders)
|
||
|
(enable-interrupts)
|
||
|
x]
|
||
|
[args
|
||
|
(disable-interrupts)
|
||
|
($current-winders d/e+old-winders)
|
||
|
(out)
|
||
|
($current-winders old-winders)
|
||
|
(enable-interrupts)
|
||
|
(apply values args)]))))
|
||
|
|
||
|
(define (check-args in body out)
|
||
|
(unless (procedure? in)
|
||
|
($oops 'dynamic-wind "~s is not a procedure" in))
|
||
|
(unless (procedure? body)
|
||
|
($oops 'dynamic-wind "~s is not a procedure" body))
|
||
|
(unless (procedure? out)
|
||
|
($oops 'dynamic-wind "~s is not a procedure" out)))
|
||
|
|
||
|
(set! dynamic-wind
|
||
|
(case-lambda
|
||
|
[(in body out)
|
||
|
(check-args in body out)
|
||
|
(dwind in body out)]
|
||
|
[(critical? in body out)
|
||
|
(check-args in body out)
|
||
|
(if critical?
|
||
|
(cwind in body out)
|
||
|
(dwind in body out))]))
|
||
|
|
||
|
(set-who! #(r6rs: dynamic-wind)
|
||
|
(lambda (in body out)
|
||
|
(#2%dynamic-wind in body out)))
|
||
|
|
||
|
(set! $do-wind
|
||
|
(lambda (old new)
|
||
|
(define common-tail
|
||
|
(lambda (x y)
|
||
|
(let ([lx (length x)] [ly (length y)])
|
||
|
(do ([x (if (fx> lx ly) (list-tail x (fx- lx ly)) x) (cdr x)]
|
||
|
[y (if (fx> ly lx) (list-tail y (fx- ly lx)) y) (cdr y)])
|
||
|
((eq? x y) x)))))
|
||
|
(let ([tail (common-tail old new)])
|
||
|
(let f ((old old))
|
||
|
(unless (eq? old tail)
|
||
|
(let ([w (car old)] [old (cdr old)])
|
||
|
(if (winder-critical? w)
|
||
|
(begin
|
||
|
(disable-interrupts)
|
||
|
($current-winders (cons disable/enable old))
|
||
|
((winder-out w))
|
||
|
($current-winders old)
|
||
|
(enable-interrupts))
|
||
|
(begin
|
||
|
($current-winders old)
|
||
|
((winder-out w))))
|
||
|
(f old))))
|
||
|
(let f ([new new])
|
||
|
(unless (eq? new tail)
|
||
|
(let ([w (car new)])
|
||
|
(f (cdr new))
|
||
|
(if (winder-critical? w)
|
||
|
(begin
|
||
|
(disable-interrupts)
|
||
|
($current-winders (cons disable/enable (cdr new)))
|
||
|
((winder-in w))
|
||
|
($current-winders new)
|
||
|
(enable-interrupts))
|
||
|
(begin
|
||
|
((winder-in w))
|
||
|
($current-winders new)))))))))
|
||
|
)
|
||
|
|
||
|
|
||
|
;;; make-promise and force
|
||
|
|
||
|
(define-who $make-promise
|
||
|
(lambda (thunk)
|
||
|
(unless (procedure? thunk)
|
||
|
($oops who "~s is not a procedure" thunk))
|
||
|
(let ([value (void)] [set? #f])
|
||
|
(lambda ()
|
||
|
(case set?
|
||
|
[(single) value]
|
||
|
[(multiple) (apply values value)]
|
||
|
[else
|
||
|
(call-with-values
|
||
|
thunk
|
||
|
(case-lambda
|
||
|
[(x)
|
||
|
(case set?
|
||
|
[(single) value]
|
||
|
[(multiple) (apply values value)]
|
||
|
[(#f) (set! value x)
|
||
|
(set! set? 'single)
|
||
|
x])]
|
||
|
[x
|
||
|
(case set?
|
||
|
[(single) value]
|
||
|
[(multiple) (apply values value)]
|
||
|
[(#f) (set! value x)
|
||
|
(set! set? 'multiple)
|
||
|
(apply values x)])]))])))))
|
||
|
|
||
|
(define-who force
|
||
|
(lambda (promise)
|
||
|
(unless (procedure? promise)
|
||
|
($oops who "~s is not a procedure" promise))
|
||
|
(promise)))
|
||
|
)
|