;;; 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))) )