Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Comment: | initial import |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
80c8c830348aff44fde2153299f41c63 |
User & Date: | ovenpasta@pizzahack.eu 2016-07-07 18:11:39 |
2016-07-07
| ||
18:15 | Update README.md check-in: f849a85634 user: noreply@github.com tags: trunk | |
18:11 | initial import check-in: 80c8c83034 user: ovenpasta@pizzahack.eu tags: trunk | |
18:07 | Initial commit check-in: 3012e9c8d9 user: ovenpasta@users.noreply.github.com tags: trunk | |
Added fmt/README.
> > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
fmt --- Combinator Formatting Library http://synthcode.com/scheme/fmt/ This directory contains a portable combinator-based formatting library for Scheme. It has been tested on Chicken, Gauche, MzScheme 3.x and Scheme48. Documentation is in the file fmt.html. |
Added fmt/VERSION.
> |
1 |
0.8.4
|
Added fmt/c.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
;;;; fmt-c.scm -- fmt module for emitting/pretty-printing C code ;; ;; Copyright (c) 2007 Alex Shinn. All rights reserved. ;; BSD-style license: http://synthcode.com/license.txt #!r6rs (library (fmt c) (export fmt-in-macro? fmt-expression? fmt-return? fmt-default-type fmt-newline-before-brace? fmt-braceless-bodies? fmt-indent-space fmt-switch-indent-space fmt-op fmt-gen c-in-expr c-in-stmt c-in-test c-paren c-maybe-paren c-type c-literal? c-literal char->c-char c-struct c-union c-class c-enum c-typedef c-cast c-expr c-expr/sexp c-apply c-op c-indent c-current-indent-string c-wrap-stmt c-open-brace c-close-brace c-block c-braced-block c-begin c-fun c-var c-prototype c-param c-param-list c-while c-for c-if c-switch c-case c-case/fallthrough c-default c-break c-continue c-return c-goto c-label c-static c-const c-extern c-volatile c-auto c-restrict c-inline c++ c-- c+ c- c* c/ c% c& c^ c~ c! c&& c<< c>> c== c!= ; |c\|| |c\|\|| c< c> c<= c>= c= c+= c-= c*= c/= c%= c&= c^= c<<= c>>= ;++c --c ; |c\|=| c++/post c--/post c. c-> c-bit-or c-or c-bit-or= cpp-if cpp-ifdef cpp-ifndef cpp-elif cpp-endif cpp-else cpp-undef cpp-include cpp-define cpp-wrap-header cpp-pragma cpp-line cpp-error cpp-warning cpp-stringify cpp-sym-cat c-comment c-block-comment c-attribute ) (import (chezscheme) (fmt fmt) (only (srfi s1 lists) every) (only (srfi s13 strings) substring/shared string-index string-index-right)) (include "fmt-c.scm") ) |
Added fmt/fmt-c.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 |
;;;; fmt-c.scm -- fmt module for emitting/pretty-printing C code ;; ;; Copyright (c) 2007 Alex Shinn. All rights reserved. ;; BSD-style license: http://synthcode.com/license.txt ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; additional state information (define (fmt-in-macro? st) (fmt-ref st 'in-macro?)) (define (fmt-expression? st) (fmt-ref st 'expression?)) (define (fmt-return? st) (fmt-ref st 'return?)) (define (fmt-default-type st) (fmt-ref st 'default-type 'int)) (define (fmt-newline-before-brace? st) (fmt-ref st 'newline-before-brace?)) (define (fmt-braceless-bodies? st) (fmt-ref st 'braceless-bodies?)) (define (fmt-non-spaced-ops? st) (fmt-ref st 'non-spaced-ops?)) (define (fmt-no-wrap? st) (fmt-ref st 'no-wrap?)) (define (fmt-indent-space st) (fmt-ref st 'indent-space)) (define (fmt-switch-indent-space st) (fmt-ref st 'switch-indent-space)) (define (fmt-op st) (fmt-ref st 'op 'stmt)) (define (fmt-gen st) (fmt-ref st 'gen)) (define (c-in-expr proc) (fmt-let 'expression? #t proc)) (define (c-in-stmt proc) (fmt-let 'expression? #f proc)) (define (c-in-test proc) (fmt-let 'in-cond? #t (c-in-expr proc))) (define (c-with-op op proc) (fmt-let 'op op proc)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; be smart about operator precedence (define (c-op-precedence x) (if (string? x) (cond ((or (string=? x ".") (string=? x "->")) 10) ((or (string=? x "++") (string=? x "--")) 20) ((string=? x "|") 65) ((string=? x "||") 75) ((string=? x "|=") 85) ((or (string=? x "+=") (string=? x "-=")) 85) (else 95)) (case x ;;((|::|) 5) ; C++ ((paren bracket) 5) ((dot arrow post-decrement post-increment) 10) ((**) 15) ; Perl ((unary+ unary- ! ~ cast unary-* unary-& sizeof) 20) ; ++ -- ((=~ !~) 25) ; Perl ((* / %) 30) ((+ -) 35) ((<< >>) 40) ((< > <= >=) 45) ((lt gt le ge) 45) ; Perl ((== !=) 50) ((eq ne cmp) 50) ; Perl ((&) 55) ((^) 60) ;;((|\||) 65) ((&&) 70) ;;((|\|\||) 75) ;;((.. ...) 77) ; Perl ((?) 80) ((= *= /= %= &= ^= <<= >>=) 85) ; |\|=| ; += -= ((comma) 90) ((=>) 90) ; Perl ((not) 92) ; Perl ((and) 93) ; Perl ((or xor) 94) ; Perl (else 95)))) (define (c-op< x y) (< (c-op-precedence x) (c-op-precedence y))) (define (c-paren x) (cat "(" x ")")) (define (c-maybe-paren op x) (lambda (st) ((fmt-let 'op op (if (or (fmt-in-macro? st) (c-op< (fmt-op st) op)) (c-paren x) x)) st))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; default literals writer (define (c-control-operator? x) (memq x '(if while switch repeat do for fun begin))) (define (c-literal? x) (or (number? x) (string? x) (char? x) (boolean? x))) (define (char->c-char c) (string-append "'" (c-escape-char c #\') "'")) (define (c-escape-char c quote-char) (let ((n (char->integer c))) (if (<= 32 n 126) (if (or (eqv? c quote-char) (eqv? c #\\)) (string #\\ c) (string c)) (case n ((7) "\\a") ((8) "\\b") ((9) "\\t") ((10) "\\n") ((11) "\\v") ((12) "\\f") ((13) "\\r") (else (string-append "\\x" (number->string (char->integer c) 16))))))) (define (c-format-number x) (if (and (integer? x) (exact? x)) (lambda (st) ((case (fmt-radix st) ((16) (cat "0x" (string-upcase (number->string x 16)))) ((8) (cat "0" (number->string x 8))) (else (dsp (number->string x)))) st)) (dsp (number->string x)))) (define (c-format-string x) (lambda (st) ((cat #\" (apply-cat (c-string-escaped x)) #\") st))) (define (c-string-escaped x) (let loop ((parts '()) (idx (string-length x))) (cond ((string-index-right x c-needs-string-escape? 0 idx) => (lambda (special-idx) (loop (cons (c-escape-char (string-ref x special-idx) #\") (cons (substring/shared x (+ special-idx 1) idx) parts)) special-idx))) (else (cons (substring/shared x 0 idx) parts))))) (define (c-needs-string-escape? c) (if (<= 32 (char->integer c) 127) (memv c '(#\" #\\)) #t)) (define (c-simple-literal x) (c-wrap-stmt (cond ((char? x) (dsp (char->c-char x))) ((boolean? x) (dsp (if x "1" "0"))) ((number? x) (c-format-number x)) ((string? x) (c-format-string x)) ((null? x) (dsp "NULL")) ((eof-object? x) (dsp "EOF")) (else (dsp (write-to-string x)))))) (define (c-literal x) (lambda (st) ((if (and (fmt-in-macro? st) (c-op< 'paren (fmt-op st)) (not (c-literal? x))) (c-paren (c-simple-literal x)) (c-simple-literal x)) st))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; default expression generator (define (c-expr/sexp x) (if (procedure? x) x (lambda (st) (cond ((pair? x) (case (car x) ((if) ((apply c-if (cdr x)) st)) ((for) ((apply c-for (cdr x)) st)) ((while) ((apply c-while (cdr x)) st)) ((switch) ((apply c-switch (cdr x)) st)) ((case) ((apply c-case (cdr x)) st)) ((case/fallthrough) ((apply c-case/fallthrough (cdr x)) st)) ((default) ((apply c-default (cdr x)) st)) ((break) (c-break st)) ((continue) (c-continue st)) ((return) ((apply c-return (cdr x)) st)) ((goto) ((apply c-goto (cdr x)) st)) ((typedef) ((apply c-typedef (cdr x)) st)) ((struct union class) ((apply c-struct/aux x) st)) ((enum) ((apply c-enum (cdr x)) st)) ((inline auto restrict register volatile extern static) ((cat (car x) " " (apply-cat (cdr x))) st)) ;; non C-keywords must have some character invalid in a C ;; identifier to avoid conflicts - by default we prefix % ((vector-ref) ((c-wrap-stmt (cat (c-expr (cadr x)) "[" (c-expr (caddr x)) "]")) st)) ((vector-set!) ((c= (c-in-expr (cat (c-expr (cadr x)) "[" (c-expr (caddr x)) "]")) (c-expr (cadddr x))) st)) ((extern/C) ((apply c-extern/C (cdr x)) st)) ((%apply) ((apply c-apply (cdr x)) st)) ((%define) ((apply cpp-define (cdr x)) st)) ((%include) ((apply cpp-include (cdr x)) st)) ((%fun) ((apply c-fun (cdr x)) st)) ((%cond) (let lp ((ls (cdr x)) (res '())) (if (null? ls) ((apply c-if (reverse res)) st) (lp (cdr ls) (cons (if (pair? (cddar ls)) (apply c-begin (cdar ls)) (cadar ls)) (cons (caar ls) res)))))) ((%prototype) ((apply c-prototype (cdr x)) st)) ((%var) ((apply c-var (cdr x)) st)) ((%begin) ((apply c-begin (cdr x)) st)) ((%attribute) ((apply c-attribute (cdr x)) st)) ((%line) ((apply cpp-line (cdr x)) st)) ((%pragma %error %warning) ((apply cpp-generic (substring/shared (symbol->string (car x)) 1) (cdr x)) st)) ((%if %ifdef %ifndef %elif) ((apply cpp-if/aux (substring/shared (symbol->string (car x)) 1) (cdr x)) st)) ((%endif) ((apply cpp-endif (cdr x)) st)) ((%block) ((apply c-braced-block (cdr x)) st)) ((%comment) ((apply c-comment (cdr x)) st)) ((:) ((apply c-label (cdr x)) st)) ((%cast) ((apply c-cast (cdr x)) st)) ((+ - & * / % ! ~ ^ && < > <= >= == != << >> = *= /= %= &= ^= >>= <<=) ; |\|| |\|\|| |\|=| ((apply c-op x) st)) ((bitwise-and bit-and) ((apply c-op '& (cdr x)) st)) ((bitwise-ior bit-or) ((apply c-op "|" (cdr x)) st)) ((bitwise-xor bit-xor) ((apply c-op '^ (cdr x)) st)) ((bitwise-not bit-not) ((apply c-op '~ (cdr x)) st)) ((arithmetic-shift) ((apply c-op '<< (cdr x)) st)) ((bitwise-ior= bit-or=) ((apply c-op "|=" (cdr x)) st)) ((%or) ((apply c-op "||" (cdr x)) st)) ((%. %field) ((apply c-op "." (cdr x)) st)) ((%->) ((apply c-op "->" (cdr x)) st)) (else (cond ((eq? (car x) (string->symbol ".")) ((apply c-op "." (cdr x)) st)) ((eq? (car x) (string->symbol "->")) ((apply c-op "->" (cdr x)) st)) ((eq? (car x) (string->symbol "++")) ((apply c-op "++" (cdr x)) st)) ((eq? (car x) (string->symbol "--")) ((apply c-op "--" (cdr x)) st)) ((eq? (car x) (string->symbol "+=")) ((apply c-op "+=" (cdr x)) st)) ((eq? (car x) (string->symbol "-=")) ((apply c-op "-=" (cdr x)) st)) (else ((c-apply x) st)))))) ((vector? x) ((c-wrap-stmt (fmt-try-fit (fmt-let 'no-wrap? #t (cat "{" (fmt-join c-expr (vector->list x) ", ") "}")) (lambda (st) (let* ((col (fmt-col st)) (sep (string-append "," (make-nl-space col)))) ((cat "{" (fmt-join c-expr (vector->list x) sep) "}" nl) st))))) st)) (else ((c-literal x) st)))))) (define (c-apply ls) (c-wrap-stmt (c-with-op 'paren (cat (c-expr (car ls)) (let ((flat (fmt-let 'no-wrap? #t (fmt-join c-expr (cdr ls) ", ")))) (fmt-if fmt-no-wrap? (c-paren flat) (c-paren (fmt-try-fit flat (lambda (st) (let* ((col (fmt-col st)) (sep (string-append "," (make-nl-space col)))) ((fmt-join c-expr (cdr ls) sep) st))))))))))) (define (c-expr x) (lambda (st) (((or (fmt-gen st) c-expr/sexp) x) st))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; comments, with Emacs-friendly escaping of nested comments (define (make-comment-writer st) (let ((output (fmt-ref st 'writer))) (lambda (str st) (let ((lim (- (string-length str) 1))) (let lp ((i 0) (st st)) (let ((j (string-index str #\/ i))) (if j (let ((st (if (and (> j 0) (eqv? #\* (string-ref str (- j 1)))) (output "\\/" (output (substring/shared str i j) st)) (output (substring/shared str i (+ j 1)) st)))) (lp (+ j 1) (if (and (< j lim) (eqv? #\* (string-ref str (+ j 1)))) (output "\\" st) st))) (output (substring/shared str i) st)))))))) (define (c-comment . args) (lambda (st) ((cat "/*" (fmt-let 'writer (make-comment-writer st) (apply-cat args)) "*/") st))) (define (make-block-comment-writer st) (let ((output (make-comment-writer st)) (indent (string-append (make-nl-space (+ (fmt-col st) 1)) "* "))) (lambda (str st) (let ((lim (string-length str))) (let lp ((i 0) (st st)) (let ((j (string-index str #\newline i))) (if j (lp (+ j 1) (output indent (output (substring/shared str i j) st))) (output (substring/shared str i) st)))))))) (define (c-block-comment . args) (lambda (st) (let ((col (fmt-col st)) (row (fmt-row st)) (indent (c-current-indent-string st))) ((cat "/* " (fmt-let 'writer (make-block-comment-writer st) (apply-cat args)) (lambda (st) (cond ((= row (fmt-row st)) ((dsp " */") st)) ;;((= (+ 3 col) (fmt-col st)) ((dsp "*/") st)) (else ((cat fl indent " */") st))))) st)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; preprocessor (define (make-cpp-writer st) (let ((output (fmt-ref st 'writer))) (lambda (str st) (let lp ((i 0) (st st)) (let ((j (string-index str #\newline i))) (if j (lp (+ j 1) (output nl-str (output " \\" (output (substring/shared str i j) st)))) (output (substring/shared str i) st))))))) (define (cpp-include file) (if (string? file) (cat fl "#include " (wrt file) fl) (cat fl "#include <" file ">" fl))) (define (list-dot x) (cond ((pair? x) (list-dot (cdr x))) ((null? x) #f) (else x))) (define (replace-tree from to x) (let replace ((x x)) (cond ((eq? x from) to) ((pair? x) (cons (replace (car x)) (replace (cdr x)))) (else x)))) (define (cpp-define x . body) (define (name-of x) (c-expr (if (pair? x) (cadr x) x))) (lambda (st) (let* ((body (cond ((and (pair? x) (list-dot x)) => (lambda (dot) (if (eq? dot '...) body (replace-tree dot '__VA_ARGS__ body)))) (else body))) (tail (if (pair? body) (cat " " (fmt-let 'writer (make-cpp-writer st) (fmt-let 'in-macro? (pair? x) ((if (or (not (pair? x)) (and (null? (cdr body)) (c-literal? (car body)))) (lambda (x) x) c-paren) (c-in-expr (apply c-begin body)))))) (lambda (x) x)))) ((c-in-expr (if (pair? x) (cat fl "#define " (name-of (car x)) (c-paren (fmt-join/dot name-of (lambda (dot) (dsp "...")) (cdr x) ", ")) tail fl) (cat fl "#define " (c-expr x) tail fl))) st)))) (define (cpp-expr x) (if (or (symbol? x) (string? x)) (dsp x) (c-expr x))) (define (cpp-if/aux name check . o) (let* ((pass (and (pair? o) (car o))) (comment (if (member name '("ifdef" "ifndef")) (cat " " (c-comment " " (if (equal? name "ifndef") "! " "") check " ")) "")) (endif (if pass (cat fl "#endif" comment) "")) (tail (cond ((and (pair? o) (pair? (cdr o))) (if (pair? (cddr o)) (apply cpp-elif (cdr o)) (cat (cpp-else) (cadr o) endif))) (else endif)))) (lambda (st) (let ((indent (c-current-indent-string st))) ((cat fl "#" name " " (cpp-expr check) fl (if pass (cat indent pass) "") fl tail fl) st))))) (define (cpp-if check . o) (apply cpp-if/aux "if" check o)) (define (cpp-ifdef check . o) (apply cpp-if/aux "ifdef" check o)) (define (cpp-ifndef check . o) (apply cpp-if/aux "ifndef" check o)) (define (cpp-elif check . o) (apply cpp-if/aux "elif" check o)) (define (cpp-else . o) (cat fl "#else " (if (pair? o) (c-comment (car o)) "") fl)) (define (cpp-endif . o) (cat fl "#endif " (if (pair? o) (c-comment (car o)) "") fl)) (define (cpp-wrap-header name . body) (let ((name name)) ; consider auto-mangling (cpp-ifndef name (c-begin (cpp-define name) nl (apply c-begin body) nl)))) (define (cpp-line num . o) (cat fl "#line " num (if (pair? o) (cat " " (car o)) "") fl)) (define (cpp-generic name . ls) (cat fl "#" name (apply-cat ls) fl)) (define (cpp-undef . args) (apply cpp-generic "undef" args)) (define (cpp-pragma . args) (apply cpp-generic "pragma" args)) (define (cpp-error . args) (apply cpp-generic "error" args)) (define (cpp-warning . args) (apply cpp-generic "warning" args)) (define (cpp-stringify x) (cat "#" x)) (define (cpp-sym-cat . args) (fmt-join dsp args " ## ")) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; general indentation and brace rules (define (c-current-indent-string st . o) (make-space (max 0 (+ (fmt-col st) (if (pair? o) (car o) 0))))) (define (c-indent st . o) (dsp (make-space (max 0 (+ (fmt-col st) (or (fmt-indent-space st) 4) (if (pair? o) (car o) 0)))))) (define (c-indent/switch st) (dsp (make-space (+ (fmt-col st) (or (fmt-switch-indent-space st) 4))))) (define (c-open-brace st) (if (fmt-newline-before-brace? st) (cat nl (c-current-indent-string st) "{" nl) (cat " {" nl))) (define (c-close-brace st) (dsp "}")) (define (c-wrap-stmt x) (fmt-if fmt-expression? (c-expr x) (cat (fmt-if fmt-return? "return " "") (c-in-expr (c-expr x)) ";" nl))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; code blocks (define (c-block . args) (apply c-block/aux 0 args)) (define (c-block/aux offset header body0 . body) (let ((inner (apply c-begin body0 body))) (if (or (pair? body) (not (or (c-literal? body0) (and (pair? body0) (not (c-control-operator? (car body0))))))) (c-braced-block/aux offset header inner) (lambda (st) (if (fmt-braceless-bodies? st) ((cat header fl (c-indent st offset) inner fl) st) ((c-braced-block/aux offset header inner) st)))))) (define (c-braced-block . args) (apply c-braced-block/aux 0 args)) (define (c-braced-block/aux offset header . body) (lambda (st) ((cat header (c-open-brace st) (c-indent st offset) (apply c-begin body) fl (c-current-indent-string st offset) (c-close-brace st)) st))) (define (c-begin . args) (apply c-begin/aux #f args)) (define (c-begin/aux ret? body0 . body) (if (null? body) (c-expr body0) (lambda (st) (if (fmt-expression? st) ((fmt-try-fit (fmt-let 'no-wrap? #t (fmt-join c-expr (cons body0 body) ", ")) (lambda (st) (let ((indent (c-current-indent-string st))) ((fmt-join c-expr (cons body0 body) (cat "," nl indent)) st)))) st) (let ((orig-ret? (fmt-return? st))) ((fmt-join/last c-expr (lambda (x) (fmt-let 'return? orig-ret? (c-expr x))) (cons body0 body) (cat fl (c-current-indent-string st))) (fmt-set! st 'return? (and ret? orig-ret?)))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; data structures (define (c-struct/aux type x . o) (let* ((name (if (null? o) (if (or (symbol? x) (string? x)) x #f) x)) (body (if name (car o) x)) (o (if (null? o) o (cdr o)))) (c-wrap-stmt (cat (c-braced-block (cat type (if (and name (not (equal? name ""))) (cat " " name) "")) (cat (c-in-stmt (if (list? body) (apply c-begin (map c-wrap-stmt (map c-param body))) (c-wrap-stmt (c-expr body)))))) (if (pair? o) (cat " " (apply c-begin o)) (dsp "")))))) (define (c-struct . args) (apply c-struct/aux "struct" args)) (define (c-union . args) (apply c-struct/aux "union" args)) (define (c-class . args) (apply c-struct/aux "class" args)) (define (c-enum x . o) (define (c-enum-one x) (if (pair? x) (cat (car x) " = " (c-expr (cadr x))) (dsp x))) (let* ((name (if (null? o) (if (or (symbol? x) (string? x)) x #f) x)) (vals (if name (car o) x))) (c-wrap-stmt (cat (c-braced-block (if name (cat "enum " name) (dsp "enum")) (c-in-expr (apply c-begin (map c-enum-one vals)))))))) (define (c-attribute . args) (cat "__attribute__ ((" (fmt-join c-expr args ", ") "))")) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; basic control structures (define (c-while check . body) (cat (c-block (cat "while (" (c-in-test (c-expr check)) ")") (c-in-stmt (apply c-begin body))) fl)) (define (c-for init check update . body) (cat (c-block (c-in-expr (cat "for (" (c-expr init) "; " (c-in-test (c-expr check)) "; " (c-expr update ) ")")) (c-in-stmt (apply c-begin body))) fl)) (define (c-param x) (cond ((procedure? x) x) ((pair? x) (c-type (car x) (cadr x))) (else (cat (lambda (st) ((c-type (fmt-default-type st)) st)) " " x)))) (define (c-param-list ls) (c-in-expr (fmt-join/dot c-param (lambda (dot) (dsp "...")) ls ", "))) (define (c-fun type name params . body) (cat (c-block (c-in-expr (c-prototype type name params)) (fmt-let 'return? (not (eq? 'void type)) (c-in-stmt (apply c-begin body)))) fl)) (define (c-prototype type name params . o) (c-wrap-stmt (cat (c-type type) " " (c-expr name) " (" (c-param-list params) ")" (fmt-join/prefix c-expr o " ")))) (define (c-static x) (cat "static " (c-expr x))) (define (c-const x) (cat "const " (c-expr x))) (define (c-restrict x) (cat "restrict " (c-expr x))) (define (c-volatile x) (cat "volatile " (c-expr x))) (define (c-auto x) (cat "auto " (c-expr x))) (define (c-inline x) (cat "inline " (c-expr x))) (define (c-extern x) (cat "extern " (c-expr x))) (define (c-extern/C . body) (cat "extern \"C\" {" nl (apply c-begin body) nl "}" nl)) (define (c-type type . o) (let ((name (and (pair? o) (car o)))) (cond ((pair? type) (case (car type) ((%fun) (cat (c-type (cadr type) #f) " (*" (or name "") ")(" (fmt-join (lambda (x) (c-type x #f)) (caddr type) ", ") ")")) ((%array) (let ((name (cat name "[" (if (pair? (cddr type)) (c-expr (caddr type)) "") "]"))) (c-type (cadr type) name))) ((%pointer *) (let ((name (cat "*" (if name (c-expr name) "")))) (c-type (cadr type) (if (and (pair? (cadr type)) (eq? '%array (caadr type))) (c-paren name) name)))) ((enum) (apply c-enum name (cdr type))) ((struct union class) (cat (apply c-struct/aux (car type) (cdr type)) " " name)) (else (fmt-join/last c-expr (lambda (x) (c-type x name)) type " ")))) ((not type) (lambda (st) ((c-type (or (fmt-default-type st) 'int) name) st))) (else (cat (if (eq? '%pointer type) '* type) (if name (cat " " name) "")))))) (define (c-var type name . init) (c-wrap-stmt (if (pair? init) (cat (c-type type name) " = " (c-expr (car init))) (c-type type (if (pair? name) (fmt-join c-expr name ", ") (c-expr name)))))) (define (c-cast type expr) (cat "(" (c-type type) ")" (c-expr expr))) (define (c-typedef type alias . o) (c-wrap-stmt (cat "typedef " (c-type type alias) (fmt-join/prefix c-expr o " ")))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Generalized IF: allows multiple tail forms for if/else if/.../else ;; blocks. A final ELSE can be signified with a test of #t or 'else, ;; or by simply using an odd number of expressions (by which the ;; normal 2 or 3 clause IF forms are special cases). (define (c-if/stmt c p . rest) (lambda (st) (let ((indent (c-current-indent-string st))) ((let lp ((c c) (p p) (ls rest)) (if (or (eq? c 'else) (eq? c #t)) (if (not (null? ls)) (error "forms after else clause in IF" c p ls) (cat (c-block/aux -1 " else" p) fl)) (let ((tail (if (pair? ls) (if (pair? (cdr ls)) (lp (car ls) (cadr ls) (cddr ls)) (lp 'else (car ls) '())) fl))) (cat (c-block/aux (if (eq? ls rest) 0 -1) (cat (if (eq? ls rest) (lambda (x) x) " else ") "if (" (c-in-test (c-expr c)) ")") p) tail)))) st)))) (define (c-if/expr c p . rest) (let lp ((c c) (p p) (ls rest)) (cond ((or (eq? c 'else) (eq? c #t)) (if (not (null? ls)) (error "forms after else clause in IF" c p ls) (c-expr p))) ((pair? ls) (cat (c-in-test (c-expr c)) " ? " (c-expr p) " : " (if (pair? (cdr ls)) (lp (car ls) (cadr ls) (cddr ls)) (lp 'else (car ls) '())))) (else (c-or (c-in-test (c-expr c)) (c-expr p)))))) (define (c-if . args) (fmt-if fmt-expression? (apply c-if/expr args) (apply c-if/stmt args))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; switch statements, automatic break handling (define (c-label name) (lambda (st) (let ((indent (make-space (max 0 (- (fmt-col st) 2))))) ((cat fl indent name ":" fl) st)))) (define c-break (c-wrap-stmt (dsp "break"))) (define c-continue (c-wrap-stmt (dsp "continue"))) (define (c-return . result) (if (pair? result) (c-wrap-stmt (cat "return " (c-expr (car result)))) (c-wrap-stmt (dsp "return")))) (define (c-goto label) (c-wrap-stmt (cat "goto " (c-expr label)))) (define (c-switch val . clauses) (lambda (st) ((cat "switch (" (c-in-expr val) ")" (c-open-brace st) (c-indent/switch st) (c-in-stmt (apply c-begin/aux #t (map c-switch-clause clauses))) fl (c-current-indent-string st) (c-close-brace st) fl) st))) (define (c-switch-clause/breaks x) (lambda (st) (let* ((break? (and (car x) (not (member (cadr x) '(case/fallthrough default/fallthrough else/fallthrough))))) (explicit-case? (member (cadr x) '(case case/fallthrough))) (indent (c-current-indent-string st)) (indent-body (c-indent st)) (sep (string-append ":" nl-str indent))) ((cat (c-in-expr (fmt-join/suffix dsp (cond ((pair? (cadr x)) (map (lambda (y) (cat (dsp "case ") (c-expr y))) (cadr x))) (explicit-case? (map (lambda (y) (cat (dsp "case ") (c-expr y))) (if (list? (caddr x)) (caddr x) (list (caddr x))))) ((member (cadr x) '(default else default/fallthrough else/fallthrough)) (list (dsp "default"))) (else (error "unknown switch clause, expected a list or default but got" (cadr x)))) sep)) (make-space (or (fmt-indent-space st) 4)) (fmt-join c-expr (if explicit-case? (cdddr x) (cddr x)) indent-body) (if (and break? (not (fmt-return? st))) (cat fl indent-body c-break) "")) st)))) (define (c-switch-clause x) (if (procedure? x) x (c-switch-clause/breaks (cons #t x)))) (define (c-switch-clause/no-break x) (if (procedure? x) x (c-switch-clause/breaks (cons #f x)))) (define (c-case x . body) (c-switch-clause (cons (if (pair? x) x (list x)) body))) (define (c-case/fallthrough x . body) (c-switch-clause/no-break (cons (if (pair? x) x (list x)) body))) (define (c-default . body) (c-switch-clause/breaks (cons #t (cons 'else body)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; operators (define (c-op op first . rest) (if (null? rest) (c-unary-op op first) (apply c-binary-op op first rest))) (define (c-binary-op op . ls) (define (lit-op? x) (or (c-literal? x) (symbol? x))) (let ((str (display-to-string op))) (c-wrap-stmt (c-maybe-paren op (if (or (equal? str ".") (equal? str "->")) (fmt-join c-expr ls str) (let ((flat (fmt-let 'no-wrap? #t (lambda (st) ((fmt-join c-expr ls (if (and (fmt-non-spaced-ops? st) (every lit-op? ls)) str (string-append " " str " "))) st))))) (fmt-if fmt-no-wrap? flat (fmt-try-fit flat (lambda (st) ((fmt-join c-expr ls (cat nl (make-space (+ 2 (fmt-col st))) str " ")) st)))))))))) (define (c-unary-op op x) (c-wrap-stmt (cat (display-to-string op) (c-maybe-paren op (c-expr x))))) ;; some convenience definitions (define (c++ . args) (apply c-op "++" args)) (define (c-- . args) (apply c-op "--" args)) (define (c+ . args) (apply c-op '+ args)) (define (c- . args) (apply c-op '- args)) (define (c* . args) (apply c-op '* args)) (define (c/ . args) (apply c-op '/ args)) (define (c% . args) (apply c-op '% args)) (define (c& . args) (apply c-op '& args)) ;; (define (|c\|| . args) (apply c-op '|\|| args)) (define (c^ . args) (apply c-op '^ args)) (define (c~ . args) (apply c-op '~ args)) (define (c! . args) (apply c-op '! args)) (define (c&& . args) (apply c-op '&& args)) ;; (define (|c\|\|| . args) (apply c-op '|\|\|| args)) (define (c<< . args) (apply c-op '<< args)) (define (c>> . args) (apply c-op '>> args)) (define (c== . args) (apply c-op '== args)) (define (c!= . args) (apply c-op '!= args)) (define (c< . args) (apply c-op '< args)) (define (c> . args) (apply c-op '> args)) (define (c<= . args) (apply c-op '<= args)) (define (c>= . args) (apply c-op '>= args)) (define (c= . args) (apply c-op '= args)) (define (c+= . args) (apply c-op "+=" args)) (define (c-= . args) (apply c-op "-=" args)) (define (c*= . args) (apply c-op '*= args)) (define (c/= . args) (apply c-op '/= args)) (define (c%= . args) (apply c-op '%= args)) (define (c&= . args) (apply c-op '&= args)) ;; (define (|c\|=| . args) (apply c-op '|\|=| args)) (define (c^= . args) (apply c-op '^= args)) (define (c<<= . args) (apply c-op '<<= args)) (define (c>>= . args) (apply c-op '>>= args)) (define (c. . args) (apply c-op "." args)) (define (c-> . args) (apply c-op "->" args)) (define (c-bit-or . args) (apply c-op "|" args)) (define (c-or . args) (apply c-op "||" args)) (define (c-bit-or= . args) (apply c-op "|=" args)) (define (c++/post x) (cat (c-maybe-paren 'post-increment (c-expr x)) "++")) (define (c--/post x) (cat (c-maybe-paren 'post-decrement (c-expr x)) "--")) |
Added fmt/fmt-color.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
;;;; fmt-color.scm -- colored output ;; ;; Copyright (c) 2006-2007 Alex Shinn. All rights reserved. ;; BSD-style license: http://synthcode.com/license.txt (define (fmt-color st) (fmt-ref st 'color)) (define (fmt-in-html? st) (fmt-ref st 'in-html?)) (define (fmt-use-html-font? st) (fmt-ref st 'use-html-font?)) (define (color->ansi x) (if (number? x) (let ((r (arithmetic-shift x -16)) (g (bitwise-and (arithmetic-shift x -8) #xFF)) (b (bitwise-and x #xFF))) ;; just picks the highest color value - need to detect blends (color->ansi (cond ((> r g) (if (> r b) 'red 'blue)) ((> g b) 'green) (else 'blue)))) (case x ((bold) "1") ((dark) "2") ((underline) "4") ((black) "30") ((red) "31") ((green) "32") ((yellow) "33") ((blue) "34") ((magenta) "35") ((cyan) "36") ((white) "37") (else "0")))) (define (ansi-escape color) (cat (integer->char 27) "[" (color->ansi color) "m")) (define (fmt-in-html . args) (fmt-let 'in-html? #t (apply-cat args))) (define (fmt-colored color . args) (fmt-if fmt-in-html? (cond ((eq? color 'bold) (cat "<b>" (apply-cat args) "</b>")) ((eq? color 'underline) (cat "<u>" (apply-cat args) "</u>")) (else (let ((cname (if (number? color) (cat "#" color) color))) (fmt-if fmt-use-html-font? (cat "<font color=\"" cname "\">" (apply-cat args) "</font>") (cat "<span style=color:\"" cname "\">" (apply-cat args) "</span>"))))) (lambda (st) (let ((old-color (fmt-color st))) ((fmt-let 'color color (cat (ansi-escape color) (apply-cat args) (if (or (memv color '(bold underline)) (memv old-color '(bold underline))) (ansi-escape 'reset) (lambda (st) st)) (ansi-escape old-color))) st))))) (define (fmt-red . args) (fmt-colored 'red (apply-cat args))) (define (fmt-blue . args) (fmt-colored 'blue (apply-cat args))) (define (fmt-green . args) (fmt-colored 'green (apply-cat args))) (define (fmt-cyan . args) (fmt-colored 'cyan (apply-cat args))) (define (fmt-yellow . args) (fmt-colored 'yellow (apply-cat args))) (define (fmt-magenta . args) (fmt-colored 'magenta (apply-cat args))) (define (fmt-white . args) (fmt-colored 'white (apply-cat args))) (define (fmt-black . args) (fmt-colored 'black (apply-cat args))) (define (fmt-bold . args) (fmt-colored 'bold (apply-cat args))) (define (fmt-underline . args) (fmt-colored 'underline (apply-cat args))) |
Added fmt/fmt-column.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 |
;;;; fmt-block.scm -- columnar formatting ;; ;; Copyright (c) 2006-2011 Alex Shinn. All rights reserved. ;; BSD-style license: http://synthcode.com/license.txt ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Columnar formatting ;; ;; A line-oriented formatter. Takes a list of ;; (line-fmt1 gen-fmt1 line-fmt2 gen-fmt2 ...) ;; and formats each of the gen-fmt1 formats as columns, printed ;; side-by-side, each line allowing post-processing done by line-fmt1 ;; (just use dsp if you want to display the lines verbatim). ;; Continuations come to the rescue to make this work properly, ;; letting us weave the output between different columns without ;; needing to build up intermediate strings. (define (fmt-columns . ls) (lambda (orig-st) (call-with-current-continuation (lambda (return) (define (infinite? x) (and (pair? x) (pair? (cdr x)) (pair? (cddr x)) (caddr x))) (let ((q1 '()) (q2 '()) (remaining (length (remove infinite? ls)))) (define (enq! proc) (set! q2 (cons proc q2))) (define (deq!) (let ((proc (car q1))) (set! q1 (cdr q1)) proc)) (define (line-init!) (set! q1 (reverse q2)) (set! q2 '())) (define (line-done?) (null? q1)) (define line-buf '()) (define line-non-empty? #f) (define (write-column fmt str finite?) (set! line-buf (cons (cons fmt str) line-buf)) (if finite? (set! line-non-empty? #t))) (define (write-line) (cond (line-non-empty? (for-each (lambda (x) (set! orig-st (((car x) (cdr x)) orig-st))) (reverse line-buf)) (set! orig-st (nl orig-st)))) (set! line-buf '()) (set! line-non-empty? #f) (line-init!)) (define (next cont) (enq! cont) (cond ((line-done?) (write-line) (if (not (positive? remaining)) (finish) ((deq!) #f))) (else ((deq!) #f)))) (define (finish) (write-line) (return orig-st)) (define (make-empty-col fmt) (define (blank *ignored*) (write-column fmt "" #f) (next blank)) ; infinite loop, next terminates for us blank) (define (make-col st fmt gen finite?) (let ((acc '())) ; buffer incomplete lines (lambda (*ignored*) (define (output* str st) (let lp ((i 0)) (let ((nli (string-index str #\newline i))) (cond (nli (let ((line (string-concatenate-reverse (cons (substring/shared str i nli) acc)))) (set! acc '()) (write-column fmt line finite?) (call-with-current-continuation next) (lp (+ nli 1)))) (else (set! acc (cons (substring/shared str i) acc)))))) ;; update - don't output or the string port will fill up (fmt-update str st)) ;; gen threads through it's own state, ignore result (gen (fmt-set-writer! (copy-fmt-state st) output*)) ;; reduce # of remaining finite columns (set! remaining (- remaining 1)) ;; write any remaining accumulated output (if (pair? acc) (let ((s (string-concatenate-reverse acc))) (write-column fmt s (and finite? (not (equal? s "")))))) ;; (maybe) loop with an empty column in place (if (not (positive? remaining)) (finish) (next (make-empty-col fmt)))))) ;; queue up the initial formatters (for-each (lambda (col) (let ((st (fmt-set-port! (copy-fmt-state orig-st) (open-output-string)))) (enq! (make-col st (car col) (dsp (cadr col)) (not (infinite? col)))))) ls) (line-init!) ;; start ((deq!) #f)))))) (define (columnar . ls) (define (proportional-width? w) (and (number? w) (or (< 0 w 1) (and (inexact? w) (= w 1.0))))) (define (whitespace-pad? st) (char-whitespace? (or (fmt-pad-char st) #\space))) (define (build-column ls) (let-optionals* ls ((fixed-width #f) (width #f) (last? #t) (tail '()) (gen #f) (prefix '()) (align 'left) (infinite? #f)) (define (scale-width st) (max 1 (inexact->exact (truncate (* width (- (fmt-width st) fixed-width)))))) (define (padder) (if (proportional-width? width) (case align ((right) (lambda (str) (lambda (st) ((pad/left (scale-width st) str) st)))) ((center) (lambda (str) (lambda (st) ((pad/both (scale-width st) str) st)))) (else (lambda (str) (lambda (st) ((pad/right (scale-width st) str) st))))) (case align ((right) (lambda (str) (pad/left width str))) ((center) (lambda (str) (pad/both width str))) (else (lambda (str) (pad/right width str)))))) (define (affix x) (cond ((pair? tail) (lambda (str) (cat (string-concatenate prefix) (x str) (string-concatenate tail)))) ((pair? prefix) (lambda (str) (cat (string-concatenate prefix) (x str)))) (else x))) (list ;; line formatter (affix (let ((pad (padder))) (if (and last? (not (pair? tail)) (eq? align 'left)) (lambda (str) (lambda (st) (((if (whitespace-pad? st) dsp pad) str) st))) pad))) ;; generator (if (proportional-width? width) (lambda (st) ((with-width (scale-width st) gen) st)) (with-width width gen)) infinite? ))) (define (adjust-widths ls border-width) (let* ((fixed-ls (filter (lambda (x) (and (number? (car x)) (>= (car x) 1))) ls)) (fixed-total (fold + border-width (map car fixed-ls))) (scaled-ls (filter (lambda (x) (proportional-width? (car x))) ls)) (denom (- (length ls) (+ (length fixed-ls) (length scaled-ls)))) (rest (if (zero? denom) 0 (exact->inexact (/ (- 1 (fold + 0 (map car scaled-ls))) denom))))) (if (negative? rest) (error 'columnar "fractional widths must sum to less than 1" (map car scaled-ls))) (map (lambda (col) (cons fixed-total (if (not (number? (car col))) (cons rest (cdr col)) col))) ls))) (define (finish ls border-width) (apply fmt-columns (map build-column (adjust-widths (reverse ls) border-width)))) (let lp ((ls ls) (strs '()) (align 'left) (infinite? #f) (width #t) (border-width 0) (res '())) (cond ((null? ls) (if (pair? strs) (finish (cons (cons (caar res) (cons #t (cons (append (reverse strs) (caddar res)) (cdddar res)))) (cdr res)) border-width) (finish (cons (cons (caar res) (cons #t (cddar res))) (cdr res)) border-width))) ((string? (car ls)) (if (string-index (car ls) #\newline) (error 'columnar "column string literals can't contain newlines") (lp (cdr ls) (cons (car ls) strs) align infinite? width (+ border-width (string-length (car ls))) res))) ((number? (car ls)) (lp (cdr ls) strs align infinite? (car ls) border-width res)) ((eq? (car ls) 'infinite) (lp (cdr ls) strs align #t width border-width res)) ((symbol? (car ls)) (lp (cdr ls) strs (car ls) infinite? width border-width res)) ((procedure? (car ls)) (lp (cdr ls) '() 'left #f #t border-width (cons (list width #f '() (car ls) (reverse strs) align infinite?) res))) (else (error 'columnar "invalid column" (car ls)))))) (define (max-line-width string-width str) (let lp ((i 0) (hi 0)) (let ((j (string-index str #\newline i))) (if j (lp (+ j 1) (max hi (string-width (substring str i j)))) (max hi (string-width (substring str i (string-length str)))))))) (define (pad-finite st proc width) (let* ((str ((fmt-to-string proc) (copy-fmt-state st))) (w (max-line-width (or (fmt-string-width st) string-length) str))) (list (cat str) (if (and (integer? width) (exact? width)) (max width w) w)))) (define (tabular . ls) (lambda (st) (let lp ((ls ls) (infinite? #f) (width #t) (res '())) (cond ((null? ls) ((apply columnar (reverse res)) st)) ((number? (car ls)) (lp (cdr ls) infinite? (car ls) res)) ((eq? 'infinite (car ls)) (lp (cdr ls) #t width (cons (car ls) res))) ((procedure? (car ls)) (if infinite? (if width (lp (cdr ls) #f #t (cons (car ls) (cons width res))) (lp (cdr ls) #f #t (cons (car ls) res))) (let ((gen+width (pad-finite st (car ls) width))) (lp (cdr ls) #f #t (append gen+width res))))) (else (lp (cdr ls) infinite? width (cons (car ls) res))))))) ;; break lines only, don't fmt-join short lines or justify (define (fold-lines . ls) (lambda (st) (define output (fmt-writer st)) (define (kons-in-line str st) (let ((len ((or (fmt-string-width st) string-length) str)) (space (- (fmt-width st) (fmt-col st)))) (cond ((or (<= len space) (not (positive? space))) (output str st)) (else (kons-in-line (substring/shared str space len) (output nl-str (output (substring/shared str 0 space) st))))))) ((fmt-let 'writer (lambda (str st) (let lp ((str str) (st st)) (let ((nli (string-index str #\newline))) (cond ((not nli) (kons-in-line str st)) (else (lp (substring/shared str (+ nli 1)) (output nl-str (kons-in-line (substring/shared str 0 nli) st)))))))) (apply-cat ls)) st))) (define (wrap-fold-words seq knil max-width get-width line . o) (let* ((last-line (if (pair? o) (car o) line)) (vec (if (list? seq) (list->vector seq) seq)) (len (vector-length vec)) (len-1 (- len 1)) (breaks (make-vector len #f)) (penalties (make-vector len #f)) (widths (list->vector (map get-width (if (list? seq) seq (vector->list vec)))))) (define (largest-fit i) (let lp ((j (+ i 1)) (width (vector-ref widths i))) (let ((width (+ width 1 (vector-ref widths j)))) (cond ((>= width max-width) (- j 1)) ((>= j len-1) len-1) (else (lp (+ j 1) width)))))) (define (min-penalty! i) (cond ((>= i len-1) 0) ((vector-ref penalties i)) (else (vector-set! penalties i (expt (+ max-width 1) 3)) (vector-set! breaks i i) (let ((k (largest-fit i))) (let lp ((j i) (width 0)) (if (<= j k) (let* ((width (+ width (vector-ref widths j))) (break-penalty (+ (max 0 (expt (- max-width (+ width (- j i))) 3)) (min-penalty! (+ j 1))))) (cond ((< break-penalty (vector-ref penalties i)) (vector-set! breaks i j) (vector-set! penalties i break-penalty))) (lp (+ j 1) width))))) (if (>= (vector-ref breaks i) len-1) (vector-set! penalties i 0)) (vector-ref penalties i)))) (define (sub-list i j) (let lp ((i i) (res '())) (if (> i j) (reverse res) (lp (+ i 1) (cons (vector-ref vec i) res))))) (cond ((zero? len) ;; degenerate case (last-line '() knil)) (else ;; compute optimum breaks (vector-set! breaks len-1 len-1) (vector-set! penalties len-1 0) (min-penalty! 0) ;; fold (let lp ((i 0) (acc knil)) (let ((break (vector-ref breaks i))) (if (>= break len-1) (last-line (sub-list i len-1) acc) (lp (+ break 1) (line (sub-list i break) acc))))))))) ;; XXXX don't split, traverse the string manually and keep track of ;; sentence endings so we can insert two spaces (define (wrap-fold str . o) (apply wrap-fold-words (string-tokenize str) o)) (define (wrap-lines . ls) (define (print-line ls st) (nl ((fmt-join dsp ls " ") st))) (define buffer '()) (lambda (st) ((fmt-let 'writer (lambda (str st) (set! buffer (cons str buffer)) st) (apply-cat ls)) st) (wrap-fold (string-concatenate-reverse buffer) st (fmt-width st) (or (fmt-string-width st) string-length) print-line))) (define (justify . ls) (lambda (st) (let ((width (fmt-width st)) (string-width (or (fmt-string-width st) string-length)) (output (fmt-writer st)) (buffer '())) (define (justify-line ls st) (if (null? ls) (nl st) (let* ((sum (fold (lambda (s n) (+ n (string-width s))) 0 ls)) (len (length ls)) (diff (max 0 (- width sum))) (sep (make-string (if (= len 1) 0 (quotient diff (- len 1))) #\space)) (rem (if (= len 1) diff (remainder diff (- len 1))))) (output (call-with-output-string (lambda (p) (display (car ls) p) (let lp ((ls (cdr ls)) (i 1)) (cond ((pair? ls) (display sep p) (if (<= i rem) (write-char #\space p)) (display (car ls) p) (lp (cdr ls) (+ i 1))))) (newline p))) st)))) (define (justify-last ls st) (nl ((fmt-join dsp ls " ") st))) ((fmt-let 'writer (lambda (str st) (set! buffer (cons str buffer)) st) (apply-cat ls)) st) (wrap-fold (string-concatenate-reverse buffer) st width string-width justify-line justify-last)))) (define (fmt-file path) (lambda (st) (call-with-input-file path (lambda (p) (let lp ((st st)) (let ((line (read-line p))) (if (eof-object? line) st (lp (nl ((dsp line) st)))))))))) (define (line-numbers . o) (let ((start (if (pair? o) (car o) 1))) (fmt-join/range dsp start #f nl-str))) |
Added fmt/fmt-gauche.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
;;;; fmt-gauche.scm -- Gauche fmt extension ;; ;; Copyright (c) 2006-2011 Alex Shinn. All rights reserved. ;; BSD-style license: http://synthcode.com/license.txt (define-module text.fmt (use srfi-1) (use srfi-6) (use srfi-13) (export new-fmt-state fmt fmt-start fmt-if fmt-capture fmt-let fmt-bind fmt-null fmt-ref fmt-set! fmt-add-properties! fmt-set-property! fmt-col fmt-set-col! fmt-row fmt-set-row! fmt-radix fmt-set-radix! fmt-precision fmt-set-precision! fmt-properties fmt-set-properties! fmt-width fmt-set-width! fmt-writer fmt-set-writer! fmt-port fmt-set-port! fmt-decimal-sep fmt-set-decimal-sep! fmt-file fmt-try-fit cat apply-cat nl fl nl-str fmt-join fmt-join/last fmt-join/dot fmt-join/prefix fmt-join/suffix fmt-join/range pad pad/right pad/left pad/both trim trim/left trim/both trim/length fit fit/left fit/both tab-to space-to wrt wrt/unshared dsp pretty pretty/unshared slashified maybe-slashified num num/si num/fit num/comma radix fix decimal-align ellipses upcase downcase titlecase pad-char comma-char decimal-char with-width wrap-lines fold-lines justify make-string-fmt-transformer make-space make-nl-space display-to-string write-to-string fmt-columns columnar tabular line-numbers )) (select-module text.fmt) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; SRFI-69 compatible hashtables (define (make-eq?-table) (make-hash-table 'eq?)) (define hash-table-ref/default hash-table-get) (define hash-table-set! hash-table-put!) (define (hash-table-walk tab proc) (hash-table-for-each tab proc)) (define (mantissa+exponent num) (let ((vec (decode-float num))) (list (vector-ref vec 0) (vector-ref vec 1)))) |
Added fmt/fmt-js.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
;;;; fmt-js.scm -- javascript formatting utilities ;; ;; Copyright (c) 2011-2012 Alex Shinn. All rights reserved. ;; BSD-style license: http://synthcode.com/license.txt ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (js-expr x) (fmt-let 'gen js-expr/sexp (lambda (st) (((or (fmt-gen st) js-expr/sexp) x) st)))) (define (js-expr/sexp x) (cond ((procedure? x) x) ((pair? x) (case (car x) ((%fun function) (apply js-function (cdr x))) ((%var var) (apply js-var (cdr x))) ((eq? ===) (apply js=== (cdr x))) ((>>>) (apply js>>> (cdr x))) ((%array) (js-array x)) ((%object) (js-object (cdr x))) ((%comment) (js-comment x)) (else (c-expr/sexp x)))) ((vector? x) (js-array x)) ((boolean? x) (cat (if x "true" "false"))) ((char? x) (js-expr/sexp (string x))) (else (c-expr/sexp x)))) (define (js-function . x) (let* ((name (and (symbol? (car x)) (car x))) (params (if name (cadr x) (car x))) (body (if name (cddr x) (cdr x)))) (c-block (cat "function " (dsp (or name "")) "(" (fmt-join dsp params ", ") ")") (fmt-let 'return? #t (c-in-stmt (apply c-begin body)))))) (define (js-var . args) (apply c-var 'var args)) (define (js=== . args) (apply c-op "===" args)) (define (js>>> . args) (apply c-op ">>>" args)) (define (js-comment . args) (columnar "// " (apply-cat args))) (define (js-array x) (let ((ls (vector->list x))) (c-wrap-stmt (fmt-try-fit (fmt-let 'no-wrap? #t (cat "[" (fmt-join js-expr ls ", ") "]")) (lambda (st) (let* ((col (fmt-col st)) (sep (string-append "," (make-nl-space col)))) ((cat "[" (fmt-join js-expr ls sep) "]" nl) st))))))) (define (js-pair x) (cat (js-expr (car x)) ": " (js-expr (cdr x)))) (define (js-object ls) (c-in-expr (fmt-try-fit (fmt-let 'no-wrap? #t (cat "{" (fmt-join js-pair ls ", ") "}")) (lambda (st) (let* ((col (fmt-col st)) (sep (string-append "," (make-nl-space col)))) ((cat "{" (fmt-join js-pair ls sep) "}" nl) st)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
Added fmt/fmt-pretty.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
;;;; fmt-pretty.scm -- pretty printing format combinator ;; ;; Copyright (c) 2006-2007 Alex Shinn. All rights reserved. ;; BSD-style license: http://synthcode.com/license.txt ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; additional settings (define (fmt-shares st) (fmt-ref st 'shares)) (define (fmt-set-shares! st x) (fmt-set! st 'shares x)) (define (fmt-copy-shares st) (fmt-set-shares! (copy-fmt-state st) (copy-shares (fmt-shares st)))) (define (copy-shares shares) (let ((tab (make-eq?-table))) (hash-table-walk (car shares) (lambda (obj x) (eq?-table-set! tab obj (cons (car x) (cdr x))))) (cons tab (cdr shares)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; utilities (define (fmt-shared-write obj proc) (lambda (st) (let* ((shares (fmt-shares st)) (cell (and shares (eq?-table-ref (car shares) obj)))) (if (pair? cell) (cond ((cdr cell) ((fmt-writer st) (gen-shared-ref (car cell) "#") st)) (else (set-car! cell (cdr shares)) (set-cdr! cell #t) (set-cdr! shares (+ (cdr shares) 1)) (proc ((fmt-writer st) (gen-shared-ref (car cell) "=") st)))) (proc st))))) (define (fmt-join/shares fmt ls . o) (let ((sep (dsp (if (pair? o) (car o) " ")))) (lambda (st) (if (null? ls) st (let* ((shares (fmt-shares st)) (tab (car shares)) (output (fmt-writer st))) (let lp ((ls ls) (st st)) (let ((st ((fmt (car ls)) st)) (rest (cdr ls))) (cond ((null? rest) st) ((pair? rest) (call-with-shared-ref/cdr rest st shares (lambda (st) (lp rest st)) sep)) (else ((fmt rest) (output ". " (sep st)))))))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; pretty printing (define (non-app? x) (if (pair? x) (or (not (or (null? (cdr x)) (pair? (cdr x)))) (non-app? (car x))) (not (symbol? x)))) (define syntax-abbrevs '((quote . "'") (quasiquote . "`") (unquote . ",") (unquote-splicing . ",@") )) (define (pp-let ls) (if (and (pair? (cdr ls)) (symbol? (cadr ls))) (pp-with-indent 2 ls) (pp-with-indent 1 ls))) (define indent-rules `((lambda . 1) (define . 1) (let . ,pp-let) (loop . ,pp-let) (let* . 1) (letrec . 1) (letrec* . 1) (and-let* . 1) (let1 . 2) (let-values . 1) (let*-values . 1) (receive . 2) (parameterize . 1) (let-syntax . 1) (letrec-syntax . 1) (syntax-rules . 1) (syntax-case . 2) (match . 1) (match-let . 1) (match-let* . 1) (if . 3) (when . 1) (unless . 1) (case . 1) (while . 1) (until . 1) (do . 2) (dotimes . 1) (dolist . 1) (test . 1) (condition-case . 1) (guard . 1) (rec . 1) (call-with-current-continuation . 0) )) (define indent-prefix-rules `(("with-" . -1) ("call-with-" . -1) ("define-" . 1)) ) (define indent-suffix-rules `(("-case" . 1)) ) (define (pp-indentation form) (let ((indent (cond ((assq (car form) indent-rules) => cdr) ((and (symbol? (car form)) (let ((str (symbol->string (car form)))) (or (find (lambda (rx) (string-prefix? (car rx) str)) indent-prefix-rules) (find (lambda (rx) (string-suffix? (car rx) str)) indent-suffix-rules)))) => cdr) (else #f)))) (if (and (number? indent) (negative? indent)) (max 0 (- (+ (length+ form) indent) 1)) indent))) (define (pp-with-indent indent-rule ls) (lambda (st) (let* ((col1 (fmt-col st)) (st ((cat "(" (pp-object (car ls))) st)) (col2 (fmt-col st)) (fixed (take* (cdr ls) (or indent-rule 1))) (tail (drop* (cdr ls) (or indent-rule 1))) (st2 (fmt-copy-shares st)) (first-line ((fmt-to-string (cat " " (fmt-join/shares pp-flat fixed " "))) st2)) (default (let ((sep (make-nl-space (+ col1 1)))) (cat sep (fmt-join/shares pp-object (cdr ls) sep) ")")))) (cond ((< (+ col2 (string-length first-line)) (fmt-width st2)) ;; fixed values on first line (let ((sep (make-nl-space (if indent-rule (+ col1 2) (+ col2 1))))) ((cat first-line (cond ((not (or (null? tail) (pair? tail))) (cat ". " (pp-object tail))) ((> (length+ (cdr ls)) (or indent-rule 1)) (cat sep (fmt-join/shares pp-object tail sep))) (else fmt-null)) ")") st2))) (indent-rule ;;(and indent-rule (not (pair? (car ls)))) ;; fixed values lined up, body indented two spaces ((fmt-try-fit (lambda (st) ((cat " " (fmt-join/shares pp-object fixed (make-nl-space (+ col2 1))) (if (pair? tail) (let ((sep (make-nl-space (+ col1 2)))) (cat sep (fmt-join/shares pp-object tail sep))) "") ")") (fmt-copy-shares st))) default) st)) (else ;; all on separate lines (default st)))))) (define (pp-app ls) (let ((indent-rule (pp-indentation ls))) (if (procedure? indent-rule) (indent-rule ls) (pp-with-indent indent-rule ls)))) ;; the elements may be shared, just checking the top level list ;; structure (define (proper-non-shared-list? ls shares) (let ((tab (car shares))) (let lp ((ls ls)) (or (null? ls) (and (pair? ls) (not (eq?-table-ref tab ls)) (lp (cdr ls))))))) (define (pp-flat x) (cond ((pair? x) (fmt-shared-write x (cond ((and (pair? (cdr x)) (null? (cddr x)) (assq (car x) syntax-abbrevs)) => (lambda (abbrev) (cat (cdr abbrev) (pp-flat (cadr x))))) (else (cat "(" (fmt-join/shares pp-flat x " ") ")"))))) ((vector? x) (fmt-shared-write x (cat "#(" (fmt-join/shares pp-flat (vector->list x) " ") ")"))) (else (lambda (st) ((write-with-shares x (fmt-shares st)) st))))) (define (pp-pair ls) (fmt-shared-write ls (cond ;; one element list, no lines to break ((null? (cdr ls)) (cat "(" (pp-object (car ls)) ")")) ;; quote or other abbrev ((and (pair? (cdr ls)) (null? (cddr ls)) (assq (car ls) syntax-abbrevs)) => (lambda (abbrev) (cat (cdr abbrev) (pp-object (cadr ls))))) (else (fmt-try-fit (lambda (st) ((pp-flat ls) (fmt-copy-shares st))) (lambda (st) (if (and (non-app? ls) (proper-non-shared-list? ls (fmt-shares st))) ((pp-data-list ls) st) ((pp-app ls) st)))))))) (define (pp-data-list ls) (lambda (st) (let* ((output (fmt-writer st)) (st (output "(" st)) (col (fmt-col st)) (width (- (fmt-width st) col)) (st2 (fmt-copy-shares st))) (cond ((and (pair? (cdr ls)) (pair? (cddr ls)) (pair? (cdddr ls)) ((fits-in-columns ls pp-flat width) st2)) => (lambda (ls) ;; at least four elements which can be broken into columns (let* ((prefix (make-nl-space (+ col 1))) (widest (+ 1 (car ls))) (columns (quotient width widest))) ; always >= 2 (let lp ((ls (cdr ls)) (st st2) (i 1)) (cond ((null? ls) (output ")" st)) ((null? (cdr ls)) (output ")" (output (car ls) st))) (else (let ((st (output (car ls) st))) (if (>= i columns) (lp (cdr ls) (output prefix st) 1) (let* ((pad (- widest (string-length (car ls)))) (st (output (make-space pad) st))) (lp (cdr ls) st (+ i 1))))))))))) (else ;; no room, print one per line ((cat (fmt-join pp-object ls (make-nl-space col)) ")") st)))))) (define (pp-vector vec) (fmt-shared-write vec (cat "#" (pp-data-list (vector->list vec))))) (define (pp-object obj) (cond ((pair? obj) (pp-pair obj)) ((vector? obj) (pp-vector obj)) (else (lambda (st) ((write-with-shares obj (fmt-shares st)) st))))) (define (pretty obj) (fmt-bind 'shares (cons (make-shared-ref-table obj) 0) (cat (pp-object obj) fl))) (define (pretty/unshared obj) (fmt-bind 'shares (cons (make-eq?-table) 0) (cat (pp-object obj) fl))) |
Added fmt/fmt-unicode.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
;;;; fmt-unicode.scm -- Unicode character width and ANSI escape support ;; ;; Copyright (c) 2006-2007 Alex Shinn. All rights reserved. ;; BSD-style license: http://synthcode.com/license.txt ;; a condensed non-spacing mark range from UnicodeData.txt (chars with ;; the Mn property) - generated partially by hand, should automate ;; this better (define low-non-spacing-chars (u8vector #xff #xff #xff #xff #xff #xff #xff #xff #xff #xff #xff #xff #xff #xff 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #x78 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #xfe #xff #xff #xff #xff #xff #x1f 0 0 0 0 0 0 0 0 0 #x3f 0 0 0 0 0 0 #xf8 #xff #x01 0 0 #x01 0 0 0 0 0 0 0 0 0 0 0 #xc0 #xff #xff #x3f 0 0 0 0 #x02 0 0 0 #xff #xff #xff #x07 0 0 0 0 0 0 0 0 0 0 #xc0 #xff #x01 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #x06 0 0 0 0 0 0 #x10 #xfe #x21 #x1e 0 #x0c 0 0 0 #x02 0 0 0 0 0 0 #x10 #x1e #x20 0 0 #x0c 0 0 0 #x06 0 0 0 0 0 0 #x10 #xfe #x3f 0 0 0 0 #x03 0 #x06 0 0 0 0 0 0 #x30 #xfe #x21 0 0 #x0c 0 0 0 #x02 0 0 0 0 0 0 #x90 #x0e #x20 #x40 0 0 0 0 0 #x04 0 0 0 0 0 0 0 0 #x20 0 0 0 0 0 0 0 0 0 0 0 0 0 #xc0 #xc1 #xff #x7f 0 0 0 0 0 0 0 0 0 0 0 0 #x10 #x40 #x30 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #x0e #x20 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #x04 #x7c 0 0 0 0 0 0 0 0 0 0 0 #xf2 #x07 #x80 #x7f 0 0 0 0 0 0 0 0 0 0 0 0 #xf2 #x1f 0 #x3f 0 0 0 0 0 0 0 0 0 #x03 0 0 #xa0 #x02 0 0 0 0 0 0 #xfe #x7f #xdf 0 #xff #xff #xff #xff #xff #x1f #x40 0 0 0 0 0 0 0 0 0 0 0 0 #xe0 #xfd #x02 0 0 0 #x03 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #x1c 0 0 0 #x1c 0 0 0 #x0c 0 0 0 #x0c 0 0 0 0 0 0 0 #x80 #x3f #x40 #xfe #x0f #x20 0 0 0 0 0 #x38 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #x02 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #x87 #x01 #x04 #x0e 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #xff #x1f #xe2 #x07 )) (define (unicode-char-width c) (let ((ci (char->integer c))) (cond ;; hand-checked ranges from EastAsianWidth.txt ((<= #x1100 ci #x115F) 2) ; Hangul ((<= #x2E80 ci #x4DB5) 2) ; CJK ((<= #x4E00 ci #xA4C6) 2) ((<= #xAC00 ci #xD7A3) 2) ; Hangul ((<= #xF900 ci #xFAD9) 2) ; CJK compat ((<= #xFE10 ci #xFE6B) 2) ((<= #xFF01 ci #xFF60) 2) ((<= #xFFE0 ci #xFFE6) 2) ((<= #x20000 ci #x30000) 2) ;; non-spacing mark (Mn) ranges from UnicodeData.txt ((<= #x0300 ci #x3029) ;; inlined bit-vector-ref for portability (let* ((i (- ci #x0300)) (byte (quotient i 8)) (off (remainder i 8))) (if (zero? (bitwise-and (u8vector-ref low-non-spacing-chars byte) (arithmetic-shift 1 off))) 1 0))) ((<= #x302A ci #x302F) 0) ((<= #x3099 ci #x309A) 0) ((= #xFB1E ci) 0) ((<= #xFE00 ci #xFE23) 0) ((<= #x1D167 ci #x1D169) 0) ((<= #x1D17B ci #x1D182) 0) ((<= #x1D185 ci #x1D18B) 0) ((<= #x1D1AA ci #x1D1AD) 0) ((<= #xE0100 ci #xE01EF) 0) (else 1)))) (define (unicode-string-width str . o) (let ((start (if (pair? o) (car o) 0)) (end (if (and (pair? o) (pair? (cdr o))) (cadr o) (string-length str)))) (let lp1 ((i start) (width 0)) (if (>= i end) width (let ((c (string-ref str i))) (cond ;; ANSI escapes ((and (= 27 (char->integer c)) ; esc (< (+ i 1) end) (eqv? #\[ (string-ref str (+ i 1)))) (let lp2 ((i (+ i 2))) (cond ((>= i end) width) ((memv (string-ref str i) '(#\m #\newline)) (lp1 (+ i 1) width)) (else (lp2 (+ i 1)))))) ;; unicode characters ((>= (char->integer c) #x80) (lp1 (+ i 1) (+ width (unicode-char-width c)))) ;; normal ASCII (else (lp1 (+ i 1) (+ width 1))))))))) (define (fmt-unicode . args) (fmt-let 'string-width unicode-string-width (apply-cat args))) |
Added fmt/fmt.css.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
body { color: black; background-color: white; margin-top: 2em; margin-left: 10%; width: 400pt; } pre { background-color: beige; } pre.scheme { background-color: white; } .subject { } h1 { margin-left: -5%; margin-top: 2em; font-size: large; } h2 { margin-left: -4%; margin-top: 1em; font-size: large; } h3,h4,h5,h6 { margin-left: -3%; margin-top: .5em; font-size: small; } .navigation { color: red; background-color: beige; text-align: right; font-style: italic; } .scheme { color: brown; } .scheme .keyword { color: #cc0000; font-weight: bold; } .scheme .variable { color: navy; } .scheme .global { color: purple; } .scheme .constant,.number,.char,.string,.boolean { color: green; } .scheme .comment { color: teal; } |
Added fmt/fmt.doc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 |
\title{Combinator Formatting} \eval (begin (display "<style>\n") (display (with-input-from-file "fmt.css" read-string)) (display "</style>\n")) \flushright{\urlh{http://synthcode.com/}{Alex Shinn}} \flushright{\urlh{http://synthcode.com/scheme/fmt/fmt-0.8.4.tar.gz}{Download Version 0.8.4}} \eval(display "<br /><br />\n\n") A library of procedures for formatting Scheme objects to text in various ways, and for easily concatenating, composing and extending these formatters efficiently without resorting to capturing and manipulating intermediate strings. \eval(display "<br /><br />\n\n") \section{Table of Contents} \eval(display "\n\n<!-- TOC -->\n\n") \eval(display "<br /><br />\n\n") \section{Installation} Available for Chicken as the \p{fmt} egg, providing the \q{fmt}, \q{fmt-c}, \q{fmt-color} and \q{fmt-unicode} extensions. To install manually for Chicken just run \p{"chicken-setup"} in the fmt directory. For Gauche run \p{"make gauche && make install-gauche"}. The modules are installed as \q{text.fmt}, \q{text.fmt.c}, \q{text.fmt.color} and \q{text.fmt.unicode}. For MzScheme you can download and install the latest \p{fmt.plt} yourself from: \urlh{http://synthcode.com/scheme/fmt/fmt.plt}{http://synthcode.com/scheme/fmt/fmt.plt} To build the \p{fmt.plt} for yourself you can run \p{"make mzscheme"}. For Scheme48 the package descriptions are in \p{fmt-scheme48.scm}: \q{ > ,config ,load fmt-scheme48.scm > ,open fmt } For other implementations you'll need to load SRFI's 1, 6, 13, 33 (sample provided) and 69 (also provided), and then load the following files: \q{ (load "let-optionals.scm") ; if you don't have LET-OPTIONALS* (load "read-line.scm") ; if you don't have READ-LINE (load "string-ports.scm") ; if you don't have CALL-WITH-OUTPUT-STRING (load "make-eq-table.scm") (load "mantissa.scm") (load "fmt.scm") (load "fmt-pretty.scm") ; optional pretty printing (load "fmt-column.scm") ; optional columnar output (load "fmt-c.scm") ; optional C formatting utilities (load "fmt-color.scm") ; optional color utilities (load "fmt-unicode.scm") ; optional Unicode-aware formatting, ; also requires SRFI-4 or SRFI-66 } \section{Background} There are several approaches to text formatting. Building strings to \q{display} is not acceptable, since it doesn't scale to very large output. The simplest realistic idea, and what people resort to in typical portable Scheme, is to interleave \q{display} and \q{write} and manual loops, but this is both extremely verbose and doesn't compose well. A simple concept such as padding space can't be achieved directly without somehow capturing intermediate output. The traditional approach is to use templates - typically strings, though in theory any object could be used and indeed Emacs' mode-line format templates allow arbitrary sexps. Templates can use either escape sequences (as in C's \q{printf} and \urlh{#BIBITEM_2}{CL's} \q{format}) or pattern matching (as in Visual Basic's \q{Format}, \urlh{#BIBITEM_6}{Perl6's} \q{form}, and SQL date formats). The primary disadvantage of templates is the relative difficulty (usually impossibility) of extending them, their opaqueness, and the unreadability that arises with complex formats. Templates are not without their advantages, but they are already addressed by other libraries such as \urlh{#BIBITEM_3}{SRFI-28} and \urlh{#BIBITEM_4}{SRFI-48}. This library takes a combinator approach. Formats are nested chains of closures, which are called to produce their output as needed. The primary goal of this library is to have, first and foremost, a maximally expressive and extensible formatting library. The next most important goal is scalability - to be able to handle arbitrarily large output and not build intermediate results except where necessary. The third goal is brevity and ease of use. \section{Usage} The primary interface is the \q{fmt} procedure: \q{(fmt <output-dest> <format> ...)} where \q{<output-dest>} has the same semantics as with \q{format} - specifically it can be an output-port, \q{#t} to indicate the current output port, or \q{#f} to accumulate output into a string. Each \q{<format>} should be a format closure as discussed below. As a convenience, non-procedure arguments are also allowed and are formatted similar to \q{display}, so that \q{(fmt #f "Result: " res nl)} would return the string \q{"Result: 42\n"}, assuming \q{RES} is bound to \q{42}. \q{nl} is the newline format combinator. \section{Specification} The procedure names have gone through several variations, and I'm still open to new suggestions. The current approach is to use abbreviated forms of standard output procedures when defining an equivalent format combinator (thus \q{display} becomes \q{dsp} and \q{write} becomes \q{wrt}), and to use an \q{fmt-} prefix for utilities and less common combinators. Variants of the same formatter get a \q{/<variant>} suffix. \subsection{Formatting Objects} \subsubsection*{(dsp <obj>)} Outputs \q{<obj>} using \q{display} semantics. Specifically, strings are output without surrounding quotes or escaping and characters are written as if by \q{write-char}. Other objects are written as with \q{write} (including nested strings and chars inside \q{<obj>}). This is the default behavior for top-level formats in \q{fmt}, \q{cat} and most other higher-order combinators. \subsubsection*{(wrt <obj>)} Outputs \q{<obj>} using \q{write} semantics. Handles shared structures as in \urlh{#BIBITEM_5}{SRFI-38}. \subsubsection*{(wrt/unshared <obj>)} As above, but doesn't handle shared structures. Infinite loops can still be avoided if used inside a combinator that truncates data (see \q{trim} and \q{fit} below). \subsubsection*{(pretty <obj>)} Pretty-prints \q{<obj>}. Also handles shared structures. Unlike many other pretty printers, vectors and data lists (lists that don't begin with a (nested) symbol), are printed in tabular format when there's room, greatly saving vertical space. \subsubsection*{(pretty/unshared <obj>)} As above but without sharing. \subsubsection*{(slashified <str> [<quote-ch> <esc-ch> <renamer>])} Outputs the string \q{<str>}, escaping any quote or escape characters. If \q{<esc-ch>} is \q{#f} escapes only the \q{<quote-ch>} by doubling it, as in SQL strings and CSV values. If \q{<renamer>} is provided, it should be a procedure of one character which maps that character to its escape value, e.g. \q{#\newline => #\n}, or \q{#f} if there is no escape value. \q{(fmt #f (slashified "hi, \"bob!\""))} \q{=> "hi, \"bob!\""} \subsubsection*{(maybe-slashified <str> <pred> [<quote-ch> <esc-ch> <renamer>])} Like \q{slashified}, but first checks if any quoting is required (by the existence of either any quote or escape characters, or any character matching \q{<pred>}), and if so outputs the string in quotes and with escapes. Otherwise outputs the string as is. \q{(fmt #f (maybe-slashified "foo" char-whitespace? #\"))} \q{=> "foo"} \q{(fmt #f (maybe-slashified "foo bar" char-whitespace? #\"))} \q{=> "\"foo bar\""} \q{(fmt #f (maybe-slashified "foo\"bar\"baz" char-whitespace? #\"))} \q{=> "\"foo\"bar\"baz\""} \subsection{Formatting Numbers} \subsubsection*{(num <n> [<radix> <precision> <sign> <comma> <comma-sep> <decimal-sep>])} Formats a single number \q{<n>}. You can optionally specify any \q{<radix>} from 2 to 36 (even if \q{<n>} isn't an integer). \q{<precision>} forces a fixed-point format. A \q{<sign>} of \q{#t} indicates to output a plus sign (+) for positive integers. However, if \q{<sign>} is a character, it means to wrap the number with that character and its mirror opposite if the number is negative. For example, \q{#\(} prints negative numbers in parenthesis, financial style: \q{-3.14 => (3.14)} \q{<comma>} is an integer specifying the number of digits between commas. Variable length, as in subcontinental-style, is not yet supported. \q{<comma-sep>} is the character to use for commas, defaulting to \q{#\,}. \q{<decimal-sep>} is the character to use for decimals, defaulting to \q{#\.}, or to \q{#\,} (European style) if \q{<comma-sep>} is already \q{#\.}. These parameters may seem unwieldy, but they can also take their defaults from state variables, described below. \subsubsection*{(num/comma <n> [<base> <precision> <sign>])} Shortcut for \q{num} to print with commas. \q{(fmt #f (num/comma 1234567))} \q{=> "1,234,567"} \subsubsection*{(num/si <n> [<base> <suffix>])} Abbreviates \q{<n>} with an SI suffix as in the -h or --si option to many GNU commands. The base defaults to 1024, using suffix names like Ki, Mi, Gi, etc. Other bases (e.g. the standard 1000) have the suffixes k, M, G, etc. The \q{<suffix>} argument is appended only if an abbreviation is used. \q{(fmt #f (num/si 608))} \q{=> "608"} \q{(fmt #f (num/si 3986))} \q{=> "3.9Ki"} \q{(fmt #f (num/si 3986 1000 "B"))} \q{=> "4kB"} See \urlh{http://www.bipm.org/en/si/si_brochure/chapter3/prefixes.html}{http://www.bipm.org/en/si/si_brochure/chapter3/prefixes.html}. \subsubsection*{(num/fit <width> <n> . <ARGS>)} Like \q{num}, but if the result doesn't fit in \q{<width>}, output instead a string of hashes (with the current \q{<precision>}) rather than showing an incorrectly truncated number. For example \q{(fmt #f (fix 2 (num/fit 4 12.345)))} \q{=> "#.##"} \subsubsection*{(num/roman <n>)} Formats the number as a Roman numeral: \q{(fmt #f (num/roman 1989))} \q{=> "MCMLXXXIX"} \subsubsection*{(num/old-roman <n>)} Formats the number as an old-style Roman numeral, without the subtraction abbreviation rule: \q{(fmt #f (num/old-roman 1989))} \q{=> "MDCCCCLXXXVIIII"} \subsection{Formatting Space} \subsubsection*{nl} Outputs a newline. \subsubsection*{fl} Short for "fresh line," outputs a newline only if we're not already at the start of a line. \subsubsection*{(space-to <column>)} Outputs spaces up to the given \q{<column>}. If the current column is already >= \q{<column>}, does nothing. \subsubsection*{(tab-to [<tab-width>])} Outputs spaces up to the next tab stop, using tab stops of width \q{<tab-width>}, which defaults to 8. If already on a tab stop, does nothing. If you want to ensure you always tab at least one space, you can use \q{(cat " " (tab-to width))}. \subsubsection*{fmt-null} Outputs nothing (useful in combinators and as a default noop in conditionals). \subsection{Concatenation} \subsubsection*{(cat <format> ...)} Concatenates the output of each \q{<format>}. \subsubsection*{(apply-cat <list>)} Equivalent to \q{(apply cat <list>)} but may be more efficient. \subsubsection*{(fmt-join <formatter> <list> [<sep>])} Formats each element \q{<elt>} of \q{<list>} with \q{(<formatter> <elt>)}, inserting \q{<sep>} in between. \q{<sep>} defaults to the empty string, but can be any format. \q{(fmt #f (fmt-join dsp '(a b c) ", "))} \q{=> "a, b, c"} \subsubsection*{(fmt-join/prefix <formatter> <list> [<sep>])} \subsubsection*{(fmt-join/suffix <formatter> <list> [<sep>])} \q{(fmt #f (fmt-join/prefix dsp '(usr local bin) "/"))} \q{=> "/usr/local/bin"} As \q{fmt-join}, but inserts \q{<sep>} before/after every element. \subsubsection*{(fmt-join/last <formatter> <last-formatter> <list> [<sep>])} As \q{fmt-join}, but the last element of the list is formatted with \q{<last-formatter>} instead. \subsubsection*{(fmt-join/dot <formatter> <dot-formatter> <list> [<sep>])} As \q{fmt-join}, but if the list is a dotted list, then formats the dotted value with \q{<dot-formatter>} instead. \subsection{Padding and Trimming} \subsubsection*{(pad <width> <format> ...)} \subsubsection*{(pad/left <width> <format> ...)} \subsubsection*{(pad/both <width> <format> ...)} Analogs of SRFI-13 \q{string-pad}, these add extra space to the left, right or both sides of the output generated by the \q{<format>}s to pad it to \q{<width>}. If \q{<width>} is exceeded has no effect. \q{pad/both} will include an extra space on the right side of the output if the difference is odd. \q{pad} does not accumulate any intermediate data. Note these are column-oriented padders, so won't necessarily work with multi-line output (padding doesn't seem a likely operation for multi-line output). \subsubsection*{(trim <width> <format> ...)} \subsubsection*{(trim/left <width> <format> ...)} \subsubsection*{(trim/both <width> <format> ...)} Analogs of SRFI-13 \q{string-trim}, truncates the output of the \q{<format>}s to force it in under \q{<width>} columns. As soon as any of the \q{<format>}s exceed \q{<width>}, stop formatting and truncate the result, returning control to whoever called \q{trim}. If \q{<width>} is not exceeded has no effect. If a truncation ellipse is set (e.g. with the \q{ellipses} procedure below), then when any truncation occurs \q{trim} and \q{trim/left} will append and prepend the ellipse, respectively. \q{trim/both} will both prepend and append. The length of the ellipse will be considered when truncating the original string, so that the total width will never be longer than \q{<width>}. \q{(fmt #f (ellipses "..." (trim 5 "abcde")))} \q{=> "abcde"} \q{(fmt #f (ellipses "..." (trim 5 "abcdef")))} \q{=> "ab..."} \subsubsection*{(trim/length <width> <format> ...)} A variant of \q{trim} which acts on the actual character count rather than columns, useful for truncating potentially cyclic data. \subsubsection*{(fit <width> <format> ...)} \subsubsection*{(fit/left <width> <format> ...)} \subsubsection*{(fit/both <width> <format> ...)} A combination of \q{pad} and \q{trunc}, ensures the output width is exactly \q{<width>}, truncating if it goes over and padding if it goes under. \subsection{Format Variables} You may have noticed many of the formatters are aware of the current column. This is because each combinator is actually a procedure of one argument, the current format state, which holds basic information such as the row, column, and any other information that a format combinator may want to keep track of. The basic interface is: \subsubsection*{(fmt-let <name> <value> <format> ...)} \subsubsection*{(fmt-bind <name> <value> <format> ...)} \q{fmt-let} sets the name for the duration of the \q{<format>}s, and restores it on return. \q{fmt-bind} sets it without restoring it. A convenience control structure can be useful in combination with these states: \subsubsection*{(fmt-if <pred> <pass> [<fail>])} \q{<pred>} takes one argument (the format state) and returns a boolean result. If true, the \q{<pass>} format is applied to the state, otherwise \q{<fail>} (defaulting to the identity) is applied. Many of the previously mentioned combinators have behavior which can be altered with state variables. Although \q{fmt-let} and \q{fmt-bind} could be used, these common variables have shortcuts: \subsubsection*{(radix <k> <format> ...)} \subsubsection*{(fix <k> <format> ...)} These alter the radix and fixed point precision of numbers output with \q{dsp}, \q{wrt}, \q{pretty} or \q{num}. These settings apply recursively to all output data structures, so that \q{(fmt #f (radix 16 '(70 80 90)))} will return the string \q{"(#x46 #x50 #x5a)"}. Note that read/write invariance is essential, so for \q{dsp}, \q{wrt} and \q{pretty} the radix prefix is always included when not decimal. Use \q{num} if you want to format numbers in alternate bases without this prefix. For example, \q{(fmt #f (radix 16 "(" (fmt-join num '(70 80 90) " ") ")"))} would return \q{"(46 50 5a)"}, the same output as above without the "#x" radix prefix. Note that fixed point formatting supports arbitrary precision in implementations with exact non-integral rationals. When trying to print inexact numbers more than the machine precision you will typically get results like \q{(fmt #f (fix 30 #i2/3))} \q{=> "0.666666666666666600000000000000"} but with an exact rational it will give you as many digits as you request: \q{(fmt #f (fix 30 2/3))} \q{=> "0.666666666666666666666666666667"} \subsubsection*{(decimal-align <k> <format> ...)} Specifies an alignment for the decimal place when formatting numbers, useful for outputting tables of numbers. \q{ (define (print-angles x) (fmt-join num (list x (sin x) (cos x) (tan x)) " ")) (fmt #t (decimal-align 5 (fix 3 (fmt-join/suffix print-angles (iota 5) nl)))) } would output \p{ 0.000 0.000 1.000 0.000 1.000 0.842 0.540 1.557 2.000 0.909 -0.416 -2.185 3.000 0.141 -0.990 -0.142 4.000 -0.757 -0.654 1.158 } \subsubsection*{(comma-char <k> <format> ...)} \subsubsection*{(decimal-char <k> <format> ...)} \q{comma-char} and \q{decimal-char} set the defaults for number formatting. \subsubsection*{(pad-char <k> <format> ...)} The \q{pad-char} sets the character used by \q{space-to}, \q{tab-to}, \q{pad/*}, and \q{fit/*}, and defaults to \q{#\space}. \q{ (define (print-table-of-contents alist) (define (print-line x) (cat (car x) (space-to 72) (pad/left 3 (cdr x)))) (fmt #t (pad-char #\. (fmt-join/suffix print-line alist nl)))) (print-table-of-contents '(("An Unexpected Party" . 29) ("Roast Mutton" . 60) ("A Short Rest" . 87) ("Over Hill and Under Hill" . 100) ("Riddles in the Dark" . 115))) } would output \p{ An Unexpected Party.....................................................29 Roast Mutton............................................................60 A Short Rest............................................................87 Over Hill and Under Hill...............................................100 Riddles in the Dark....................................................115 } \subsubsection*{(ellipse <ell> <format> ...)} Sets the truncation ellipse to \q{<ell>}, would should be a string or character. \subsubsection*{(with-width <width> <format> ...)} Sets the maximum column width used by some formatters. The default is 78. \subsection{Columnar Formatting} Although \q{tab-to}, \q{space-to} and padding can be used to manually align columns to produce table-like output, these can be awkward to use. The optional extensions in this section make this easier. \subsubsection*{(columnar <column> ...)} Formats each \q{<column>} side-by-side, i.e. as though each were formatted separately and then the individual lines concatenated together. The current column width is divided evenly among the columns, and all but the last column are right-padded. For example \q{(fmt #t (columnar (dsp "abc\\ndef\\n") (dsp "123\\n456\\n")))} outputs \p{ abc 123 def 456 } assuming a 16-char width (the left side gets half the width, or 8 spaces, and is left aligned). Note that we explicitly use DSP instead of the strings directly. This is because \q{columnar} treats raw strings as literals inserted into the given location on every line, to be used as borders, for example: \q{ (fmt #t (columnar "/* " (dsp "abc\\ndef\\n") " | " (dsp "123\\n456\\n") " */")) } would output \p{ /* abc | 123 */ /* def | 456 */ } You may also prefix any column with any of the symbols \q{'left}, \q{'right} or \q{'center} to control the justification. The symbol \q{'infinite} can be used to indicate the column generates an infinite stream of output. You can further prefix any column with a width modifier. Any positive integer is treated as a fixed width, ignoring the available width. Any real number between 0 and 1 indicates a fraction of the available width (after subtracting out any fixed widths). Columns with unspecified width divide up the remaining width evenly. Note that \q{columnar} builds its output incrementally, interleaving calls to the generators until each has produced a line, then concatenating that line together and outputting it. This is important because as noted above, some columns may produce an infinite stream of output, and in general you may want to format data larger than can fit into memory. Thus columnar would be suitable for line numbering a file of arbitrary size, or implementing the Unix \q{yes(1)} command, etc. As an implementation detail, \q{columnar} uses first-class continuations to interleave the column output. The core \q{fmt} itself has no knowledge of or special support for \q{columnar}, which could complicate and potentially slow down simpler \q{fmt} operations. This is a testament to the power of \q{call/cc} - it can be used to implement coroutines or arbitrary control structures even where they were not planned for. \subsubsection*{(tabular <column> ...)} Equivalent to \q{columnar} except that each column is padded at least to the minimum width required on any of its lines. Thus \q{(fmt #t (tabular "|" (dsp "a\\nbc\\ndef\\n") "|" (dsp "123\\n45\\n6\\n") "|"))} outputs \p{ |a |123| |bc |45 | |def|6 | } This makes it easier to generate tables without knowing widths in advance. However, because it requires generating the entire output in advance to determine the correct column widths, \q{tabular} cannot format a table larger than would fit in memory. \subsubsection*{(fmt-columns <column> ...)} The low-level formatter on which \q{columnar} is based. Each \q{<column>} must be a list of 2-3 elements: \q{(<line-formatter> <line-generator> [<infinite?>])} where \q{<line-generator>} is the column generator as above, and the \q{<line-formatter>} is how each line is formatted. Raw concatenation of each line is performed, without any spacing or width adjustment. \q{<infinite?>}, if true, indicates this generator produces an infinite number of lines and termination should be determined without it. \subsubsection*{(wrap-lines <format> ...)} Behaves like \q{cat}, except text is accumulated and lines are optimally wrapped to fit in the current width as in the Unix \p{fmt(1)} command. \subsubsection*{(justify <format> ...)} Like \q{wrap-lines} except the lines are full-justified. \q{ (define func '(define (fold kons knil ls) (let lp ((ls ls) (acc knil)) (if (null? ls) acc (lp (cdr ls) (kons (car ls) acc)))))) (define doc (string-append "The fundamental list iterator. Applies KONS to each element " "of LS and the result of the previous application, beginning " "with KNIL. With KONS as CONS and KNIL as '(), equivalent to REVERSE.")) (fmt #t (columnar (pretty func) " ; " (justify doc))) } outputs \p{ (define (fold kons knil ls) ; The fundamental list iterator. (let lp ((ls ls) (acc knil)) ; Applies KONS to each element of (if (null? ls) ; LS and the result of the previous acc ; application, beginning with KNIL. (lp (cdr ls) ; With KONS as CONS and KNIL as '(), (kons (car ls) acc))))) ; equivalent to REVERSE. } \subsubsection*{(fmt-file <pathname>)} Simply displayes the contents of the file \q{<pathname>} a line at a time, so that in typical formatters such as \q{columnar} only constant memory is consumed, making this suitable for formatting files of arbitrary size. \subsubsection*{(line-numbers [<start>])} A convenience utility, just formats an infinite stream of numbers (in the current radix) beginning with \q{<start>}, which defaults to \q{1}. The Unix \q{nl(1)} utility could be implemented as: \q{ (fmt #t (columnar 6 'right 'infinite (line-numbers) " " (fmt-file "read-line.scm"))) } \p{ 1 2 (define (read-line . o) 3 (let ((port (if (pair? o) (car o) (current-input-port)))) 4 (let lp ((res '())) 5 (let ((c (read-char port))) 6 (if (or (eof-object? c) (eqv? c #\newline)) 7 (list->string (reverse res)) 8 (lp (cons c res))))))) } \section{C Formatting} \subsection{C Formatting Basics} For purposes such as writing wrappers, code-generators, compilers or other language tools, people often need to generate or emit C code. Without a decent library framework it's difficult to maintain proper indentation. In addition, for the Scheme programmer it's tedious to work with all the context sensitivities of C, such as the expression vs. statement distinction, special rules for writing preprocessor macros, and when precedence rules require parenthesis. Fortunately, context is one thing this formatting library is good at keeping track of. The C formatting interface tries to make it as easy as possible to generate C code without getting in your way. There are two approaches to using the C formatting extensions - procedural and sexp-oriented (described in \ref{csexprs}). In the procedural interface, C operators are made available as formatters with a "c-" prefix, literals are converted to their C equivalents and symbols are output as-is (you're responsible for making sure they are valid C identifiers). Indentation is handled automatically. \q{(fmt #t (c-if 1 2 3))} outputs \p{ if (1) { 2; } else { 3; } } In addition, the formatter knows when you're in an expression and when you're in a statement, and behaves accordingly, so that \q{(fmt #t (c-if (c-if 1 2 3) 4 5))} outputs \p{ if (1 ? 2 : 3) { 4; } else { 5; } } Similary, \q{c-begin}, used for sequencing, will separate with semi-colons in a statement and commas in an expression. Moreover, we also keep track of the final expression in a function and insert returns for you: \q{(fmt #t (c-fun 'int 'foo '() (c-if (c-if 1 2 3) 4 5)))} outputs \p{ int foo () { if (1 ? 2 : 3) { return 4; } else { return 5; } } } although it knows that void functions don't return. Switch statements insert breaks by default if they don't return: \q{ (fmt #t (c-switch 'y (c-case 1 (c+= 'x 1)) (c-default (c+= 'x 2)))) } \p{ switch (y) { case 1: x += 1; break; default: x += 2; break; } } though you can explicitly fallthrough if you want: \q{ (fmt #t (c-switch 'y (c-case/fallthrough 1 (c+= 'x 1)) (c-default (c+= 'x 2)))) } \p{ switch (y) { case 1: x += 1; default: x += 2; break; } } Operators are available with just a "c" prefix, e.g. c+, c-, c*, c/, etc. \q{c++} is a prefix operator, \q{c++/post} is postfix. ||, | and |= are written as \q{c-or}, \q{c-bit-or} and \q{c-bit-or=} respectively. Function applications are written with \q{c-apply}. Other control structures such as \q{c-for} and \q{c-while} work as expected. The full list is in the procedure index below. When a C formatter encounters an object it doesn't know how to write (including lists and records), it outputs them according to the format state's current \q{'gen} variable. This allows you to specify generators for your own types, e.g. if you are using your own AST records in a compiler. If the \q{'gen} variable isn't set it defaults to the \q{c-expr/sexp} procedure, which formats an s-expression as if it were C code. Thus instead of \q{c-apply} you can just use a list. The full API is available via normal s-expressions - formatters that aren't keywords in C are prefixed with a % or otherwise made invalid C identifiers so that they can't be confused with function application. \subsection{C Preprocessor Formatting} C preprocessor formatters also properly handle their surrounding context, so you can safely intermix them in the normal flow of C code. \q{ (fmt #t (c-switch 'y (c-case 1 (c= 'x 1)) (cpp-ifdef 'H_TWO (c-case 2 (c= 'x 4))) (c-default (c= 'x 5)))) } \p{ switch (y) { case 1: x = 1; break; #ifdef H_TWO case 2: x = 4; break; #endif /* H_TWO */ default: x = 5; break; } } Macros can be handled with \q{cpp-define}, which knows to wrap individual variable references in parenthesis: \q{(fmt #t (cpp-define '(min x y) (c-if (c< 'x 'y) 'x 'y)))} \p{ #define min(x, y) (((x) < (y)) ? (x) : (y)) } As with all C formatters, the CPP output is pretty printed as needed, and if it wraps over several lines the lines are terminated with a backslash. To write a C header file that is included at most once, you can wrap the entire body in \q{cpp-wrap-header}: \q{ (fmt #t (cpp-wrap-header "FOO_H" (c-extern (c-prototype 'int 'foo '())))) } \p{ #ifndef FOO_H #define FOO_H extern int foo (); #endif /* ! FOO_H */ } \subsection{Customizing C Style} The output uses a simplified K&R style with 4 spaces for indentation by default. The following state variables let you override the style: \subsubsection*{'indent-space} how many spaces to indent bodies, default \q{4} \subsubsection*{'switch-indent-space} how many spaces to indent switch clauses, also defaults to \q{4} \subsubsection*{'newline-before-brace?} insert a newline before an open brace (non-K&R), defaults to \q{#f} \subsubsection*{'braceless-bodies?} omit braces when we can prove they aren't needed \subsubsection*{'non-spaced-ops?} omit spaces between operators and operands for groups of variables and literals (e.g. "a+b+3" instead of "a + b + 3"} \subsubsection*{'no-wrap?} Don't wrap function calls and long operator groups over mulitple lines. Functions and control structures will still use multiple lines. The C formatters also respect the \q{'radix} and \q{'precision} settings. \subsection{C Formatter Index} \subsubsection*{(c-if <condition> <pass> [<fail> [<condition2> <pass2> ...]])} Print a chain of if/else conditions. Use a final condition of \q{'else} for a final else clause. \subsubsection*{(c-for <init> <condition> <update> <body> ...)} \subsubsection*{(c-while <condition> <body> ...)} Basic loop constructs. \subsubsection*{(c-fun <type> <name> <params> <body> ...)} \subsubsection*{(c-prototype <type> <name> <params>)} Output a function or function prototype. The parameters should be a list 2-element lists of the form \q{(<param-type> <param-name>)}, which are output with DSP. A parameter can be abbreviated as just the symbol name, or \q{#f} can be passed as the type, in which case the \q{'default-type} state variable is used. The parameters may be a dotted list, in which case ellipses for a C variadic are inserted - the actual name of the dotted value is ignored. Types are just typically just symbols, or lists of symbols such as \q{'(const char)}. A complete description is given below in section \ref{ctypes}. These can also accessed as %fun and %prototype at the head of a list. \subsubsection*{(c-var <type> <name> [<init-value>])} Declares and optionally initializes a variable. Also accessed as %var at the head of a list. \subsubsection*{(c-begin <expr> ...)} Outputs each of the <expr>s, separated by semi-colons if in a statement or commas if in an expression. \subsubsection*{(c-switch <clause> ...)} \subsubsection*{(c-case <values> <body> ...)} \subsubsection*{(c-case/fallthrough <values> <body> ...)} \subsubsection*{(c-default <body> ...)} Switch statements. In addition to using the clause formatters, clauses inside a switch may be handled with a Scheme CASE-like list, with the car a list of case values and the cdr the body. \subsubsection*{(c-label <name>)} \subsubsection*{(c-goto <name>)} \subsubsection*{(c-return [<result>])} \subsubsection*{c-break} \subsubsection*{c-continue} Manual labels and jumps. Labels can also be accessed as a list beginning with a colon, e.g. \q{'(: label1)}. \subsubsection*{(c-const <expr>)} \subsubsection*{(c-static <expr>)} \subsubsection*{(c-volatile <expr>)} \subsubsection*{(c-restrict <expr>)} \subsubsection*{(c-register <expr>)} \subsubsection*{(c-auto <expr>)} \subsubsection*{(c-inline <expr>)} \subsubsection*{(c-extern <expr>)} Declaration modifiers. May be nested. \subsubsection*{(c-extern/C <body> ...)} Wraps body in an extern "C" { ... } for use with C++. \subsubsection*{(c-cast <type> <expr>)} Casts an expression to a type. Also %cast at the head of a list. \subsubsection*{(c-typedef <type> <new-name> ...)} Creates a new type definition with one or more names. \subsubsection*{(c-struct [<name>] <field-list> [<attributes>])} \subsubsection*{(c-union [<name>] <field-list> [<attributes>])} \subsubsection*{(c-class [<name>] <field-list> [<attributes>])} \subsubsection*{(c-attribute <values> ...)} Composite type constructors. Attributes may be accessed as %attribute at the head of a list. \q{ (fmt #f (c-struct 'employee '((short age) ((char *) name) ((struct (year month day)) dob)) (c-attribute 'packed))) } \p{ struct employee { short age; char* name; struct { int year; int month; int day; } dob; } __attribute__ ((packed)); } \subsubsection*{(c-enum [<name>] <enum-list>)} Enumerated types. \q{<enum-list>} may be strings, symbols, or lists of string or symbol followed by the enum's value. \subsubsection*{(c-comment <formatter> ...)} Outputs the \q{<formatter>}s wrapped in C's /* ... */ comment. Properly escapes nested comments inside in an Emacs-friendly style. \subsection{C Preprocessor Formatter Index} \subsubsection*{(cpp-include <file>)} If file is a string, outputs in it "quotes", otherwise (as a symbol or arbitrary formatter) it outputs it in brackets. \q{(fmt #f (cpp-include 'stdio.h))} \q{=> "#include <stdio.h>\n"} \q{(fmt #f (cpp-include "config.h"))} \q{=> "#include \"config.h\"\n"} \subsubsection*{(cpp-define <macro> [<value>])} Defines a preprocessor macro, which may be just a name or a list of name and parameters. Properly wraps the value in parenthesis and escapes newlines. A dotted parameter list will use the C99 variadic macro syntax, and will also substitute any references to the dotted name with \p{__VA_ARGS__}: \q{(fmt #t (cpp-define '(eprintf . args) '(fprintf stderr args)))} \p{ #define eprintf(...) (fprintf(stderr, __VA_ARGS__)) } \subsubsection*{(cpp-if <condition> <pass> [<fail> ...])} \subsubsection*{(cpp-ifdef <condition> <pass> [<fail> ...])} \subsubsection*{(cpp-ifndef <condition> <pass> [<fail> ...])} \subsubsection*{(cpp-elif <condition> <pass> [<fail> ...])} \subsubsection*{(cpp-else <body> ...)} Conditional compilation. \subsubsection*{(cpp-line <num> [<file>])} Line number information. \subsubsection*{(cpp-pragma <args> ...)} \subsubsection*{(cpp-error <args> ...)} \subsubsection*{(cpp-warning <args> ...)} Additional preprocessor directives. \subsubsection*{(cpp-stringify <expr>)} Stringifies \q{<expr>} by prefixing the # operator. \subsubsection*{(cpp-sym-cat <args> ...)} Joins the \q{<args>} into a single preprocessor token with the ## operator. \subsubsection*{(cpp-wrap-header <name> <body> ...)} Wrap an entire header to only be included once. \subsubsection*{Operators:} \q{ c++ c-- c+ c- c* c/ c% c& c^ c~ c! c&& c<< c>> c== c!= c< c> c<= c>= c= c+= c-= c*= c/= c%= c&= c^= c<<= c>>= c++/post c--/post c-or c-bit-or c-bit-or= } \subsection{C Types} \label{ctypes} Typically a type is just a symbol such as \q{'char} or \q{'int}. You can wrap types with modifiers such as \q{c-const}, but as a convenience you can just use a list such as in \q{'(const unsignedchar *)}. You can also nest these lists, so the previous example is equivalent to \q{'(* (const (unsigned char)))}. Pointers may be written as \q{'(%pointer <type>)} for readability - \q{%pointer} is exactly equivalent to \q{*} in types. Unamed structs, classes, unions and enums may be used directly as types, using their respective keywords at the head of a list. Two special types are the %array type and function pointer type. An array is written: \q{(%array <type> [<size>])} where \q{<type>} is any other type (including another array or function pointer), and \q{<size>}, if given, will print the array size. For example: \q{(c-var '(%array (unsigned long) SIZE) 'table '#(1 2 3 4))} \p{ unsigned long table[SIZE] = {1, 2, 3, 4}; } A function pointer is written: \q{(%fun <return-type> (<param-types> ...))} For example: \q{(c-typedef '(%fun double (double double int)) 'f)} \p{ typedef double (*f)(double, double, int); } Wherever a type is expected but not given, the value of the \q{'default-type} formatting state variable is used. By default this is just \q{'int}. Type declarations work uniformly for variables and parameters, as well for casts and typedefs. \subsection{C as S-Expressions} \label{csexprs} Rather than building formatting closures by hand, it can be more convenient to just build a normal s-expression and ask for it to be formatted as C code. This can be thought of as a simple Scheme->C compiler without any runtime support. In a s-expression, strings and characters are printed as C strings and characters, booleans are printed as 0 or 1, symbols are displayed as-is, and numbers are printed as C numbers (using the current formatting radix if specified). Vectors are printed as comma-separated lists wrapped in braces, which can be used for initializing arrays or structs. A list indicates a C expression or statement. Any of the existing C keywords can be used to pretty-print the expression as described with the c-keyword formatters above. Thus, the example above \q{(fmt #t (c-if (c-if 1 2 3) 4 5))} could also be written \q{(fmt #t (c-expr '(if (if 1 2 3) 4 5)))} C constructs that are dependent on the underlying syntax and have no keyword are written with a % prefix (\q{%fun}, \q{%var}, \q{%pointer}, \q{%array}, \q{%cast}), including C preprocessor constructs (\q{%include}, \q{%define}, \q{%pragma}, \q{%error}, \q{%warning}, \q{%if}, \q{%ifdef}, \q{%ifndef}, \q{%elif}). Labels are written as \q{(: <label-name>)}. You can write a sequence as \q{(%begin <expr> ...)}. For example, the following definition of the fibonacci sequence, which apart from the return type of \q{#f} looks like a Lisp definition: \q{(fmt #t (c-expr '(%fun #f fib (n) (if (<= n 1) 1 (+ (fib (- n 1)) (fib (- n 2)))))))} prints the working C definition: \p{ int fib (int n) { if (n <= 1) { return 1; } else { return fib((n - 1)) + fib((n - 2)); } } } \section{JavaScript Formatting} The experimental fmt-js library extends the fmt-c library with functionality for formatting JavaScript code. \subsubsection*{(js-expr x)} Formats a JavaScript expression similarly to \q{c-expr}. Inside a \q{js-expr} formatter, you can use the normal \q{c-} prefixed formatters described in the previous section, and they will format appropriately for JavaScript. Currently expressions will all be terminated with a semi-colon, but that will be made optional in a later release. \subsubsection*{(js-function [<name>] (<params>) <body> ...)} Defines a function (anonymously if no name is provided). \subsubsection*{(js-var <name> [<init-value>])} Declares a JavaScript variable, optionally with an initial value. \subsubsection*{(js-comment <formatter> ...)} Formats a comment prefixing lines with \q{"// "}. \section{Formatting with Color} The fmt-color library provides the following utilities: \q{ (fmt-red <formatter> ...) (fmt-blue <formatter> ...) (fmt-green <formatter> ...) (fmt-cyan <formatter> ...) (fmt-yellow <formatter> ...) (fmt-magenta <formatter> ...) (fmt-white <formatter> ...) (fmt-black <formatter> ...) (fmt-bold <formatter> ...) (fmt-underline <formatter> ...) } and more generally \q{(fmt-color <color> <formatter> ...)} where color can be a symbol name or \q{#xRRGGBB} numeric value. Outputs the formatters colored with ANSI escapes. In addition \q{(fmt-in-html <formatter> ...)} can be used to mark the format state as being inside HTML, which the above color formats will understand and output HTML \q{<span>} tags with the appropriate style colors, instead of ANSI escapes. \section{Unicode} The fmt-unicode library provides the \q{fmt-unicode} formatter, which just takes a list of formatters and overrides the string-length for padding and trimming, such that Unicode double or full width characters are considered 2 characters wide (as they typically are in fixed-width terminals), while treating combining and non-spacing characters as 0 characters wide. It also recognizes and ignores ANSI escapes, in particular useful if you want to combine this with the fmt-color utilities. \section{Optimizing} The library is designed for scalability and flexibility, not speed, and I'm not going to think about any fine tuning until it's more stabilised. One aspect of the design, however, was influenced for the sake of future optimizations, which is that none of the default format variables are initialized by global parameters, which leaves room for inlining and subsequent simplification of format calls. If you don't have an aggressively optimizing compiler, you can easily achieve large speedups on common cases with CL-style compiler macros. \section{Common Lisp Format Cheat Sheet} A quick reference for those of you switching over from Common Lisp's format. \table{ \b{format} | \b{fmt} ~a | \q{dsp} ~c | \q{dsp} ~s | \q{wrt/unshared} ~w | \q{wrt} ~y | \q{pretty} ~x | \q{(radix 16 ...)} or \q{(num <n> 16)} ~o | \q{(radix 8 ...)} or \q{(num <n> 8)} ~b | \q{(radix 2 ...)} or \q{(num <n> 2)} ~f | \q{(fix <digits> ...)} or \q{(num <n> <radix> <digits>)} ~% | \q{nl} ~& | \q{fl} ~[...~] | normal \q{if} or \q{fmt-if} (delayed test) ~{...~} | \q{(fmt-join ... <list> [<sep>])} } \section{References} \bibitem{R5RS} R. Kelsey, W. Clinger, J. Rees (eds.) \urlh{http://www.schemers.org/Documents/Standards/R5RS/}{Revised^5 Report on the Algorithmic Language Scheme} \bibitem{CommonLisp} Guy L. Steele Jr. (editor) \urlh{http://www.harlequin.com/education/books/HyperSpec/}{Common Lisp Hyperspec} \bibitem{SRFI-28} Scott G. Miller \urlh{http://srfi.schemers.org/srfi-28/}{SRFI-28 Basic Format Strings} \bibitem{SRFI-48} Ken Dickey \urlh{http://srfi.schemers.org/srfi-48/}{SRFI-48 Intermediate Format Strings} \bibitem{SRFI-38} Ray Dillinger \urlh{http://srfi.schemers.org/srfi-38/}{SRFI-38 External Representation for Data With Shared Structure} \bibitem{Perl6} Damian Conway \urlh{http://www.perl.com/lpt/a/819}{Perl6 Exegesis 7 - formatting} \eval(display "<br /><br /><br /><br />\n") |
Added fmt/fmt.html.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 |
<html><head><title>Combinator Formatting</title> </head><body bgcolor=white> <h1><span class=subject>Combinator Formatting</span></h1> <style> body { color: black; background-color: white; margin-top: 2em; margin-left: 10%; width: 400pt; } pre { background-color: beige; } pre.scheme { background-color: white; } .subject { } h1 { margin-left: -5%; margin-top: 2em; font-size: large; } h2 { margin-left: -4%; margin-top: 1em; font-size: large; } h3,h4,h5,h6 { margin-left: -3%; margin-top: .5em; font-size: small; } .navigation { color: red; background-color: beige; text-align: right; font-style: italic; } .scheme { color: brown; } .scheme .keyword { color: #cc0000; font-weight: bold; } .scheme .variable { color: navy; } .scheme .global { color: purple; } .scheme .constant,.number,.char,.string,.boolean { color: green; } .scheme .comment { color: teal; } </style> <div align=right><a href="http://synthcode.com/">Alex Shinn</a></div> <div align=right><a href="http://synthcode.com/scheme/fmt/fmt-0.8.4.tar.gz">Download Version 0.8.4</a></div> <p> <br /><br /> A library of procedures for formatting Scheme objects to text in various ways, and for easily concatenating, composing and extending these formatters efficiently without resorting to capturing and manipulating intermediate strings. <p> <br /><br /> <a name="SECTION_1"><h1>1 Table of Contents</h1> <ol> <li><a href="#SECTION_1">Table of Contents</a> <li><a href="#SECTION_2">Installation</a> <li><a href="#SECTION_3">Background</a> <li><a href="#SECTION_4">Usage</a> <li><a href="#SECTION_5">Specification</a> <ol> <li><a href="#SECTION_5.1">Formatting Objects</a> <li><a href="#SECTION_5.2">Formatting Numbers</a> <li><a href="#SECTION_5.3">Formatting Space</a> <li><a href="#SECTION_5.4">Concatenation</a> <li><a href="#SECTION_5.5">Padding and Trimming</a> <li><a href="#SECTION_5.6">Format Variables</a> <li><a href="#SECTION_5.7">Columnar Formatting</a> </ol> <li><a href="#SECTION_6">C Formatting</a> <ol> <li><a href="#SECTION_6.1">C Formatting Basics</a> <li><a href="#SECTION_6.2">C Preprocessor Formatting</a> <li><a href="#SECTION_6.3">Customizing C Style</a> <li><a href="#SECTION_6.4">C Formatter Index</a> <li><a href="#SECTION_6.5">C Preprocessor Formatter Index</a> <li><a href="#SECTION_6.6">C Types</a> <li><a href="#SECTION_6.7">C as S-Expressions</a> </ol> <li><a href="#SECTION_7">JavaScript Formatting</a> <li><a href="#SECTION_8">Formatting with Color</a> <li><a href="#SECTION_9">Unicode</a> <li><a href="#SECTION_10">Optimizing</a> <li><a href="#SECTION_11">Common Lisp Format Cheat Sheet</a> <li><a href="#SECTION_12">References</a> </ol> <br /><br /> <a name="SECTION_2"><h1>2 Installation</h1> Available for Chicken as the <code>fmt</code> egg, providing the <code class=scheme><span class=variable>fmt</span></code>, <code class=scheme><span class=variable>fmt-c</span></code>, <code class=scheme><span class=variable>fmt-color</span></code> and <code class=scheme><span class=variable>fmt-unicode</span></code> extensions. To install manually for Chicken just run <code>"chicken-setup"</code> in the fmt directory. <p> For Gauche run <code>"make gauche && make install-gauche"</code>. The modules are installed as <code class=scheme><span class=variable>text.fmt</span></code>, <code class=scheme><span class=variable>text.fmt.c</span></code>, <code class=scheme><span class=variable>text.fmt.color</span></code> and <code class=scheme><span class=variable>text.fmt.unicode</span></code>. <p> For MzScheme you can download and install the latest <code>fmt.plt</code> yourself from: <p> <a href="http://synthcode.com/scheme/fmt/fmt.plt">http://synthcode.com/scheme/fmt/fmt.plt</a> <p> To build the <code>fmt.plt</code> for yourself you can run <code>"make mzscheme"</code>. <p> For Scheme48 the package descriptions are in <code>fmt-scheme48.scm</code>: <p> <pre class=scheme> <span class=variable>></span> ,<span class=variable>config</span> ,<span class=variable>load</span> <span class=variable>fmt-scheme48.scm</span> <span class=variable>></span> ,<span class=variable>open</span> <span class=variable>fmt</span> </pre> <p> For other implementations you'll need to load SRFI's 1, 6, 13, 33 (sample provided) and 69 (also provided), and then load the following files: <p> <pre class=scheme> (<span class=variable>load</span> <span class=string>"let-optionals.scm"</span>) <span class=comment>; if you don't have LET-OPTIONALS*</span> (<span class=variable>load</span> <span class=string>"read-line.scm"</span>) <span class=comment>; if you don't have READ-LINE</span> (<span class=variable>load</span> <span class=string>"string-ports.scm"</span>) <span class=comment>; if you don't have CALL-WITH-OUTPUT-STRING</span> (<span class=variable>load</span> <span class=string>"make-eq-table.scm"</span>) (<span class=variable>load</span> <span class=string>"mantissa.scm"</span>) (<span class=variable>load</span> <span class=string>"fmt.scm"</span>) (<span class=variable>load</span> <span class=string>"fmt-pretty.scm"</span>) <span class=comment>; optional pretty printing</span> (<span class=variable>load</span> <span class=string>"fmt-column.scm"</span>) <span class=comment>; optional columnar output</span> (<span class=variable>load</span> <span class=string>"fmt-c.scm"</span>) <span class=comment>; optional C formatting utilities</span> (<span class=variable>load</span> <span class=string>"fmt-color.scm"</span>) <span class=comment>; optional color utilities</span> (<span class=variable>load</span> <span class=string>"fmt-unicode.scm"</span>) <span class=comment>; optional Unicode-aware formatting,</span> <span class=comment>; also requires SRFI-4 or SRFI-66</span> </pre> <p> <a name="SECTION_3"><h1>3 Background</h1> There are several approaches to text formatting. Building strings to <code class=scheme><span class=variable>display</span></code> is not acceptable, since it doesn't scale to very large output. The simplest realistic idea, and what people resort to in typical portable Scheme, is to interleave <code class=scheme><span class=variable>display</span></code> and <code class=scheme><span class=variable>write</span></code> and manual loops, but this is both extremely verbose and doesn't compose well. A simple concept such as padding space can't be achieved directly without somehow capturing intermediate output. <p> The traditional approach is to use templates - typically strings, though in theory any object could be used and indeed Emacs' mode-line format templates allow arbitrary sexps. Templates can use either escape sequences (as in C's <code class=scheme><span class=variable>printf</span></code> and <a href="#BIBITEM_2">CL's</a> <code class=scheme><span class=variable>format</span></code>) or pattern matching (as in Visual Basic's <code class=scheme><span class=variable>Format</span></code>, <a href="#BIBITEM_6">Perl6's</a> <code class=scheme><span class=variable>form</span></code>, and SQL date formats). The primary disadvantage of templates is the relative difficulty (usually impossibility) of extending them, their opaqueness, and the unreadability that arises with complex formats. Templates are not without their advantages, but they are already addressed by other libraries such as <a href="#BIBITEM_3">SRFI-28</a> and <a href="#BIBITEM_4">SRFI-48</a>. <p> This library takes a combinator approach. Formats are nested chains of closures, which are called to produce their output as needed. The primary goal of this library is to have, first and foremost, a maximally expressive and extensible formatting library. The next most important goal is scalability - to be able to handle arbitrarily large output and not build intermediate results except where necessary. The third goal is brevity and ease of use. <p> <a name="SECTION_4"><h1>4 Usage</h1> The primary interface is the <code class=scheme><span class=variable>fmt</span></code> procedure: <p> <code class=scheme>(<span class=variable>fmt</span> <span class=variable><output-dest></span> <span class=variable><format></span> ...)</code> <p> where <code class=scheme><span class=variable><output-dest></span></code> has the same semantics as with <code class=scheme><span class=variable>format</span></code> - specifically it can be an output-port, <code class=scheme><span class=boolean>#t</span></code> to indicate the current output port, or <code class=scheme><span class=boolean>#f</span></code> to accumulate output into a string. <p> Each <code class=scheme><span class=variable><format></span></code> should be a format closure as discussed below. As a convenience, non-procedure arguments are also allowed and are formatted similar to <code class=scheme><span class=variable>display</span></code>, so that <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> <span class=string>"Result: "</span> <span class=variable>res</span> <span class=variable>nl</span>)</code> <p> would return the string <code class=scheme><span class=string>"Result: 42n"</span></code>, assuming <code class=scheme><span class=variable>RES</span></code> is bound to <code class=scheme><span class=number>42</span></code>. <p> <code class=scheme><span class=variable>nl</span></code> is the newline format combinator. <p> <a name="SECTION_5"><h1>5 Specification</h1> The procedure names have gone through several variations, and I'm still open to new suggestions. The current approach is to use abbreviated forms of standard output procedures when defining an equivalent format combinator (thus <code class=scheme><span class=variable>display</span></code> becomes <code class=scheme><span class=variable>dsp</span></code> and <code class=scheme><span class=variable>write</span></code> becomes <code class=scheme><span class=variable>wrt</span></code>), and to use an <code class=scheme><span class=variable>fmt-</span></code> prefix for utilities and less common combinators. Variants of the same formatter get a <code class=scheme><span class=variable>/<variant></span></code> suffix. <p> <a name="SECTION_5.1"><h2>5.1 Formatting Objects</h2> <h3>(dsp <obj>)</h3> Outputs <code class=scheme><span class=variable><obj></span></code> using <code class=scheme><span class=variable>display</span></code> semantics. Specifically, strings are output without surrounding quotes or escaping and characters are written as if by <code class=scheme><span class=variable>write-char</span></code>. Other objects are written as with <code class=scheme><span class=variable>write</span></code> (including nested strings and chars inside <code class=scheme><span class=variable><obj></span></code>). This is the default behavior for top-level formats in <code class=scheme><span class=variable>fmt</span></code>, <code class=scheme><span class=variable>cat</span></code> and most other higher-order combinators. <p> <h3>(wrt <obj>)</h3> Outputs <code class=scheme><span class=variable><obj></span></code> using <code class=scheme><span class=variable>write</span></code> semantics. Handles shared structures as in <a href="#BIBITEM_5">SRFI-38</a>. <p> <h3>(wrt/unshared <obj>)</h3> As above, but doesn't handle shared structures. Infinite loops can still be avoided if used inside a combinator that truncates data (see <code class=scheme><span class=variable>trim</span></code> and <code class=scheme><span class=variable>fit</span></code> below). <p> <h3>(pretty <obj>)</h3> Pretty-prints <code class=scheme><span class=variable><obj></span></code>. Also handles shared structures. Unlike many other pretty printers, vectors and data lists (lists that don't begin with a (nested) symbol), are printed in tabular format when there's room, greatly saving vertical space. <p> <h3>(pretty/unshared <obj>)</h3> As above but without sharing. <p> <h3>(slashified <str> [<quote-ch> <esc-ch> <renamer>])</h3> Outputs the string <code class=scheme><span class=variable><str></span></code>, escaping any quote or escape characters. If <code class=scheme><span class=variable><esc-ch></span></code> is <code class=scheme><span class=boolean>#f</span></code> escapes only the <code class=scheme><span class=variable><quote-ch></span></code> by doubling it, as in SQL strings and CSV values. If <code class=scheme><span class=variable><renamer></span></code> is provided, it should be a procedure of one character which maps that character to its escape value, e.g. <code class=scheme><span class=char>#\newline</span> <span class=keyword>=></span> <span class=char>#\n</span></code>, or <code class=scheme><span class=boolean>#f</span></code> if there is no escape value. <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>slashified</span> <span class=string>"hi, "bob!""</span>))</code> <p> <code class=scheme><span class=keyword>=></span> <span class=string>"hi, "bob!""</span></code> <p> <h3>(maybe-slashified <str> <pred> [<quote-ch> <esc-ch> <renamer>])</h3> Like <code class=scheme><span class=variable>slashified</span></code>, but first checks if any quoting is required (by the existence of either any quote or escape characters, or any character matching <code class=scheme><span class=variable><pred></span></code>), and if so outputs the string in quotes and with escapes. Otherwise outputs the string as is. <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>maybe-slashified</span> <span class=string>"foo"</span> <span class=variable>char-whitespace?</span> <span class=char>#\"</span>))</code> <p> <code class=scheme><span class=keyword>=></span> <span class=string>"foo"</span></code> <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>maybe-slashified</span> <span class=string>"foo bar"</span> <span class=variable>char-whitespace?</span> <span class=char>#\"</span>))</code> <p> <code class=scheme><span class=keyword>=></span> <span class=string>""foo bar""</span></code> <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>maybe-slashified</span> <span class=string>"foo"bar"baz"</span> <span class=variable>char-whitespace?</span> <span class=char>#\"</span>))</code> <p> <code class=scheme><span class=keyword>=></span> <span class=string>""foo"bar"baz""</span></code> <p> <a name="SECTION_5.2"><h2>5.2 Formatting Numbers</h2> <h3>(num <n> [<radix> <precision> <sign> <comma> <comma-sep> <decimal-sep>])</h3> Formats a single number <code class=scheme><span class=variable><n></span></code>. You can optionally specify any <code class=scheme><span class=variable><radix></span></code> from 2 to 36 (even if <code class=scheme><span class=variable><n></span></code> isn't an integer). <code class=scheme><span class=variable><precision></span></code> forces a fixed-point format. <p> A <code class=scheme><span class=variable><sign></span></code> of <code class=scheme><span class=boolean>#t</span></code> indicates to output a plus sign (+) for positive integers. However, if <code class=scheme><span class=variable><sign></span></code> is a character, it means to wrap the number with that character and its mirror opposite if the number is negative. For example, <code class=scheme><span class=char>#\(</span></code> prints negative numbers in parenthesis, financial style: <code class=scheme><span class=number>-3.14</span> <span class=keyword>=></span> (<span class=number>3.14</span>)</code> <p> <code class=scheme><span class=variable><comma></span></code> is an integer specifying the number of digits between commas. Variable length, as in subcontinental-style, is not yet supported. <p> <code class=scheme><span class=variable><comma-sep></span></code> is the character to use for commas, defaulting to <code class=scheme><span class=char>#\,</span></code>. <p> <code class=scheme><span class=variable><decimal-sep></span></code> is the character to use for decimals, defaulting to <code class=scheme><span class=char>#\.</span></code>, or to <code class=scheme><span class=char>#\,</span></code> (European style) if <code class=scheme><span class=variable><comma-sep></span></code> is already <code class=scheme><span class=char>#\.</span></code>. <p> These parameters may seem unwieldy, but they can also take their defaults from state variables, described below. <p> <h3>(num/comma <n> [<base> <precision> <sign>])</h3> Shortcut for <code class=scheme><span class=variable>num</span></code> to print with commas. <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>num/comma</span> <span class=number>1234567</span>))</code> <p> <code class=scheme><span class=keyword>=></span> <span class=string>"1,234,567"</span></code> <p> <h3>(num/si <n> [<base> <suffix>])</h3> Abbreviates <code class=scheme><span class=variable><n></span></code> with an SI suffix as in the -h or --si option to many GNU commands. The base defaults to 1024, using suffix names like Ki, Mi, Gi, etc. Other bases (e.g. the standard 1000) have the suffixes k, M, G, etc. <p> The <code class=scheme><span class=variable><suffix></span></code> argument is appended only if an abbreviation is used. <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>num/si</span> <span class=number>608</span>))</code> <p> <code class=scheme><span class=keyword>=></span> <span class=string>"608"</span></code> <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>num/si</span> <span class=number>3986</span>))</code> <p> <code class=scheme><span class=keyword>=></span> <span class=string>"3.9Ki"</span></code> <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>num/si</span> <span class=number>3986</span> <span class=number>1000</span> <span class=string>"B"</span>))</code> <p> <code class=scheme><span class=keyword>=></span> <span class=string>"4kB"</span></code> <p> See <a href="http://www.bipm.org/en/si/si_brochure/chapter3/prefixes.html">http://www.bipm.org/en/si/si_brochure/chapter3/prefixes.html</a>. <p> <h3>(num/fit <width> <n> . <ARGS>)</h3> Like <code class=scheme><span class=variable>num</span></code>, but if the result doesn't fit in <code class=scheme><span class=variable><width></span></code>, output instead a string of hashes (with the current <code class=scheme><span class=variable><precision></span></code>) rather than showing an incorrectly truncated number. For example <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>fix</span> <span class=number>2</span> (<span class=variable>num/fit</span> <span class=number>4</span> <span class=number>12.345</span>)))</code> <code class=scheme><span class=keyword>=></span> <span class=string>"#.##"</span></code> <p> <h3>(num/roman <n>)</h3> Formats the number as a Roman numeral: <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>num/roman</span> <span class=number>1989</span>))</code> <code class=scheme><span class=keyword>=></span> <span class=string>"MCMLXXXIX"</span></code> <p> <h3>(num/old-roman <n>)</h3> Formats the number as an old-style Roman numeral, without the subtraction abbreviation rule: <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>num/old-roman</span> <span class=number>1989</span>))</code> <code class=scheme><span class=keyword>=></span> <span class=string>"MDCCCCLXXXVIIII"</span></code> <p> <a name="SECTION_5.3"><h2>5.3 Formatting Space</h2> <h3>nl</h3> Outputs a newline. <p> <h3>fl</h3> Short for "fresh line," outputs a newline only if we're not already at the start of a line. <p> <h3>(space-to <column>)</h3> Outputs spaces up to the given <code class=scheme><span class=variable><column></span></code>. If the current column is already >= <code class=scheme><span class=variable><column></span></code>, does nothing. <p> <h3>(tab-to [<tab-width>])</h3> Outputs spaces up to the next tab stop, using tab stops of width <code class=scheme><span class=variable><tab-width></span></code>, which defaults to 8. If already on a tab stop, does nothing. If you want to ensure you always tab at least one space, you can use <code class=scheme>(<span class=variable>cat</span> <span class=string>" "</span> (<span class=variable>tab-to</span> <span class=variable>width</span>))</code>. <p> <h3>fmt-null</h3> Outputs nothing (useful in combinators and as a default noop in conditionals). <p> <a name="SECTION_5.4"><h2>5.4 Concatenation</h2> <h3>(cat <format> ...)</h3> Concatenates the output of each <code class=scheme><span class=variable><format></span></code>. <p> <h3>(apply-cat <list>)</h3> Equivalent to <code class=scheme>(<span class=variable>apply</span> <span class=variable>cat</span> <span class=variable><list></span>)</code> but may be more efficient. <p> <h3>(fmt-join <formatter> <list> [<sep>])</h3> Formats each element <code class=scheme><span class=variable><elt></span></code> of <code class=scheme><span class=variable><list></span></code> with <code class=scheme>(<span class=variable><formatter></span> <span class=variable><elt></span>)</code>, inserting <code class=scheme><span class=variable><sep></span></code> in between. <code class=scheme><span class=variable><sep></span></code> defaults to the empty string, but can be any format. <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>fmt-join</span> <span class=variable>dsp</span> '(<span class=variable>a</span> <span class=variable>b</span> <span class=variable>c</span>) <span class=string>", "</span>))</code> <p> <code class=scheme><span class=keyword>=></span> <span class=string>"a, b, c"</span></code> <p> <h3>(fmt-join/prefix <formatter> <list> [<sep>])</h3> <h3>(fmt-join/suffix <formatter> <list> [<sep>])</h3> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>fmt-join/prefix</span> <span class=variable>dsp</span> '(<span class=variable>usr</span> <span class=variable>local</span> <span class=variable>bin</span>) <span class=string>"/"</span>))</code> <p> <code class=scheme><span class=keyword>=></span> <span class=string>"/usr/local/bin"</span></code> <p> As <code class=scheme><span class=variable>fmt-join</span></code>, but inserts <code class=scheme><span class=variable><sep></span></code> before/after every element. <p> <h3>(fmt-join/last <formatter> <last-formatter> <list> [<sep>])</h3> As <code class=scheme><span class=variable>fmt-join</span></code>, but the last element of the list is formatted with <code class=scheme><span class=variable><last-formatter></span></code> instead. <p> <h3>(fmt-join/dot <formatter> <dot-formatter> <list> [<sep>])</h3> As <code class=scheme><span class=variable>fmt-join</span></code>, but if the list is a dotted list, then formats the dotted value with <code class=scheme><span class=variable><dot-formatter></span></code> instead. <p> <a name="SECTION_5.5"><h2>5.5 Padding and Trimming</h2> <h3>(pad <width> <format> ...)</h3> <h3>(pad/left <width> <format> ...)</h3> <h3>(pad/both <width> <format> ...)</h3> Analogs of SRFI-13 <code class=scheme><span class=variable>string-pad</span></code>, these add extra space to the left, right or both sides of the output generated by the <code class=scheme><span class=variable><format></span></code>s to pad it to <code class=scheme><span class=variable><width></span></code>. If <code class=scheme><span class=variable><width></span></code> is exceeded has no effect. <code class=scheme><span class=variable>pad/both</span></code> will include an extra space on the right side of the output if the difference is odd. <p> <code class=scheme><span class=variable>pad</span></code> does not accumulate any intermediate data. <p> Note these are column-oriented padders, so won't necessarily work with multi-line output (padding doesn't seem a likely operation for multi-line output). <p> <h3>(trim <width> <format> ...)</h3> <h3>(trim/left <width> <format> ...)</h3> <h3>(trim/both <width> <format> ...)</h3> Analogs of SRFI-13 <code class=scheme><span class=variable>string-trim</span></code>, truncates the output of the <code class=scheme><span class=variable><format></span></code>s to force it in under <code class=scheme><span class=variable><width></span></code> columns. As soon as any of the <code class=scheme><span class=variable><format></span></code>s exceed <code class=scheme><span class=variable><width></span></code>, stop formatting and truncate the result, returning control to whoever called <code class=scheme><span class=variable>trim</span></code>. If <code class=scheme><span class=variable><width></span></code> is not exceeded has no effect. <p> If a truncation ellipse is set (e.g. with the <code class=scheme><span class=variable>ellipses</span></code> procedure below), then when any truncation occurs <code class=scheme><span class=variable>trim</span></code> and <code class=scheme><span class=variable>trim/left</span></code> will append and prepend the ellipse, respectively. <code class=scheme><span class=variable>trim/both</span></code> will both prepend and append. The length of the ellipse will be considered when truncating the original string, so that the total width will never be longer than <code class=scheme><span class=variable><width></span></code>. <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>ellipses</span> <span class=string>"..."</span> (<span class=variable>trim</span> <span class=number>5</span> <span class=string>"abcde"</span>)))</code> <p> <code class=scheme><span class=keyword>=></span> <span class=string>"abcde"</span></code> <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>ellipses</span> <span class=string>"..."</span> (<span class=variable>trim</span> <span class=number>5</span> <span class=string>"abcdef"</span>)))</code> <p> <code class=scheme><span class=keyword>=></span> <span class=string>"ab..."</span></code> <p> <h3>(trim/length <width> <format> ...)</h3> A variant of <code class=scheme><span class=variable>trim</span></code> which acts on the actual character count rather than columns, useful for truncating potentially cyclic data. <p> <h3>(fit <width> <format> ...)</h3> <h3>(fit/left <width> <format> ...)</h3> <h3>(fit/both <width> <format> ...)</h3> A combination of <code class=scheme><span class=variable>pad</span></code> and <code class=scheme><span class=variable>trunc</span></code>, ensures the output width is exactly <code class=scheme><span class=variable><width></span></code>, truncating if it goes over and padding if it goes under. <p> <a name="SECTION_5.6"><h2>5.6 Format Variables</h2> You may have noticed many of the formatters are aware of the current column. This is because each combinator is actually a procedure of one argument, the current format state, which holds basic information such as the row, column, and any other information that a format combinator may want to keep track of. The basic interface is: <p> <h3>(fmt-let <name> <value> <format> ...)</h3> <h3>(fmt-bind <name> <value> <format> ...)</h3> <code class=scheme><span class=variable>fmt-let</span></code> sets the name for the duration of the <code class=scheme><span class=variable><format></span></code>s, and restores it on return. <code class=scheme><span class=variable>fmt-bind</span></code> sets it without restoring it. <p> A convenience control structure can be useful in combination with these states: <p> <h3>(fmt-if <pred> <pass> [<fail>])</h3> <code class=scheme><span class=variable><pred></span></code> takes one argument (the format state) and returns a boolean result. If true, the <code class=scheme><span class=variable><pass></span></code> format is applied to the state, otherwise <code class=scheme><span class=variable><fail></span></code> (defaulting to the identity) is applied. <p> Many of the previously mentioned combinators have behavior which can be altered with state variables. Although <code class=scheme><span class=variable>fmt-let</span></code> and <code class=scheme><span class=variable>fmt-bind</span></code> could be used, these common variables have shortcuts: <p> <h3>(radix <k> <format> ...)</h3> <h3>(fix <k> <format> ...)</h3> These alter the radix and fixed point precision of numbers output with <code class=scheme><span class=variable>dsp</span></code>, <code class=scheme><span class=variable>wrt</span></code>, <code class=scheme><span class=variable>pretty</span></code> or <code class=scheme><span class=variable>num</span></code>. These settings apply recursively to all output data structures, so that <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>radix</span> <span class=number>16</span> '(<span class=number>70</span> <span class=number>80</span> <span class=number>90</span>)))</code> <p> will return the string <code class=scheme><span class=string>"(#x46 #x50 #x5a)"</span></code>. Note that read/write invariance is essential, so for <code class=scheme><span class=variable>dsp</span></code>, <code class=scheme><span class=variable>wrt</span></code> and <code class=scheme><span class=variable>pretty</span></code> the radix prefix is always included when not decimal. Use <code class=scheme><span class=variable>num</span></code> if you want to format numbers in alternate bases without this prefix. For example, <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>radix</span> <span class=number>16</span> <span class=string>"("</span> (<span class=variable>fmt-join</span> <span class=variable>num</span> '(<span class=number>70</span> <span class=number>80</span> <span class=number>90</span>) <span class=string>" "</span>) <span class=string>")"</span>))</code> <p> would return <code class=scheme><span class=string>"(46 50 5a)"</span></code>, the same output as above without the "#x" radix prefix. <p> Note that fixed point formatting supports arbitrary precision in implementations with exact non-integral rationals. When trying to print inexact numbers more than the machine precision you will typically get results like <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>fix</span> <span class=number>30</span> <span class=constant>#i2/3</span>))</code> <p> <code class=scheme><span class=keyword>=></span> <span class=string>"0.666666666666666600000000000000"</span></code> <p> but with an exact rational it will give you as many digits as you request: <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>fix</span> <span class=number>30</span> <span class=number>2/3</span>))</code> <p> <code class=scheme><span class=keyword>=></span> <span class=string>"0.666666666666666666666666666667"</span></code> <p> <h3>(decimal-align <k> <format> ...)</h3> Specifies an alignment for the decimal place when formatting numbers, useful for outputting tables of numbers. <p> <pre class=scheme> (<span class=keyword>define</span> (<span class=variable>print-angles</span> <span class=variable>x</span>) (<span class=variable>fmt-join</span> <span class=variable>num</span> (<span class=variable>list</span> <span class=variable>x</span> (<span class=variable>sin</span> <span class=variable>x</span>) (<span class=variable>cos</span> <span class=variable>x</span>) (<span class=variable>tan</span> <span class=variable>x</span>)) <span class=string>" "</span>)) (<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>decimal-align</span> <span class=number>5</span> (<span class=variable>fix</span> <span class=number>3</span> (<span class=variable>fmt-join/suffix</span> <span class=variable>print-angles</span> (<span class=variable>iota</span> <span class=number>5</span>) <span class=variable>nl</span>)))) </pre> <p> would output <p> <pre> 0.000 0.000 1.000 0.000 1.000 0.842 0.540 1.557 2.000 0.909 -0.416 -2.185 3.000 0.141 -0.990 -0.142 4.000 -0.757 -0.654 1.158 </pre> <p> <h3>(comma-char <k> <format> ...)</h3> <h3>(decimal-char <k> <format> ...)</h3> <code class=scheme><span class=variable>comma-char</span></code> and <code class=scheme><span class=variable>decimal-char</span></code> set the defaults for number formatting. <p> <h3>(pad-char <k> <format> ...)</h3> The <code class=scheme><span class=variable>pad-char</span></code> sets the character used by <code class=scheme><span class=variable>space-to</span></code>, <code class=scheme><span class=variable>tab-to</span></code>, <code class=scheme><span class=variable>pad/*</span></code>, and <code class=scheme><span class=variable>fit/*</span></code>, and defaults to <code class=scheme><span class=char>#\space</span></code>. <p> <pre class=scheme> (<span class=keyword>define</span> (<span class=variable>print-table-of-contents</span> <span class=variable>alist</span>) (<span class=keyword>define</span> (<span class=variable>print-line</span> <span class=variable>x</span>) (<span class=variable>cat</span> (<span class=variable>car</span> <span class=variable>x</span>) (<span class=variable>space-to</span> <span class=number>72</span>) (<span class=variable>pad/left</span> <span class=number>3</span> (<span class=variable>cdr</span> <span class=variable>x</span>)))) (<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>pad-char</span> <span class=char>#\.</span> (<span class=variable>fmt-join/suffix</span> <span class=variable>print-line</span> <span class=variable>alist</span> <span class=variable>nl</span>)))) (<span class=variable>print-table-of-contents</span> '((<span class=string>"An Unexpected Party"</span> . <span class=number>29</span>) (<span class=string>"Roast Mutton"</span> . <span class=number>60</span>) (<span class=string>"A Short Rest"</span> . <span class=number>87</span>) (<span class=string>"Over Hill and Under Hill"</span> . <span class=number>100</span>) (<span class=string>"Riddles in the Dark"</span> . <span class=number>115</span>))) </pre> <p> would output <p> <pre> An Unexpected Party.....................................................29 Roast Mutton............................................................60 A Short Rest............................................................87 Over Hill and Under Hill...............................................100 Riddles in the Dark....................................................115 </pre> <p> <h3>(ellipse <ell> <format> ...)</h3> Sets the truncation ellipse to <code class=scheme><span class=variable><ell></span></code>, would should be a string or character. <p> <h3>(with-width <width> <format> ...)</h3> Sets the maximum column width used by some formatters. The default is 78. <p> <a name="SECTION_5.7"><h2>5.7 Columnar Formatting</h2> Although <code class=scheme><span class=variable>tab-to</span></code>, <code class=scheme><span class=variable>space-to</span></code> and padding can be used to manually align columns to produce table-like output, these can be awkward to use. The optional extensions in this section make this easier. <p> <h3>(columnar <column> ...)</h3> Formats each <code class=scheme><span class=variable><column></span></code> side-by-side, i.e. as though each were formatted separately and then the individual lines concatenated together. The current column width is divided evenly among the columns, and all but the last column are right-padded. For example <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>columnar</span> (<span class=variable>dsp</span> <span class=string>"abcndefn"</span>) (<span class=variable>dsp</span> <span class=string>"123n456n"</span>)))</code> <p> outputs <p> <pre> abc 123 def 456 </pre> <p> assuming a 16-char width (the left side gets half the width, or 8 spaces, and is left aligned). Note that we explicitly use DSP instead of the strings directly. This is because <code class=scheme><span class=variable>columnar</span></code> treats raw strings as literals inserted into the given location on every line, to be used as borders, for example: <p> <pre class=scheme> (<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>columnar</span> <span class=string>"/* "</span> (<span class=variable>dsp</span> <span class=string>"abcndefn"</span>) <span class=string>" | "</span> (<span class=variable>dsp</span> <span class=string>"123n456n"</span>) <span class=string>" */"</span>)) </pre> <p> would output <p> <pre> /* abc | 123 */ /* def | 456 */ </pre> <p> You may also prefix any column with any of the symbols <code class=scheme>'<span class=variable>left</span></code>, <code class=scheme>'<span class=variable>right</span></code> or <code class=scheme>'<span class=variable>center</span></code> to control the justification. The symbol <code class=scheme>'<span class=variable>infinite</span></code> can be used to indicate the column generates an infinite stream of output. <p> You can further prefix any column with a width modifier. Any positive integer is treated as a fixed width, ignoring the available width. Any real number between 0 and 1 indicates a fraction of the available width (after subtracting out any fixed widths). Columns with unspecified width divide up the remaining width evenly. <p> Note that <code class=scheme><span class=variable>columnar</span></code> builds its output incrementally, interleaving calls to the generators until each has produced a line, then concatenating that line together and outputting it. This is important because as noted above, some columns may produce an infinite stream of output, and in general you may want to format data larger than can fit into memory. Thus columnar would be suitable for line numbering a file of arbitrary size, or implementing the Unix <code class=scheme><span class=variable>yes</span>(<span class=number>1</span>)</code> command, etc. <p> As an implementation detail, <code class=scheme><span class=variable>columnar</span></code> uses first-class continuations to interleave the column output. The core <code class=scheme><span class=variable>fmt</span></code> itself has no knowledge of or special support for <code class=scheme><span class=variable>columnar</span></code>, which could complicate and potentially slow down simpler <code class=scheme><span class=variable>fmt</span></code> operations. This is a testament to the power of <code class=scheme><span class=variable>call/cc</span></code> - it can be used to implement coroutines or arbitrary control structures even where they were not planned for. <p> <h3>(tabular <column> ...)</h3> Equivalent to <code class=scheme><span class=variable>columnar</span></code> except that each column is padded at least to the minimum width required on any of its lines. Thus <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>tabular</span> <span class=string>"|" (dsp "</span><span class=variable>a\\nbc\\ndef\\n</span><span class=string>") "</span>|" (<span class=variable>dsp</span> <span class=string>"123n45n6n"</span>) <span class=string>"|"))</span></code> <p> outputs <p> <pre> |a |123| |bc |45 | |def|6 | </pre> <p> This makes it easier to generate tables without knowing widths in advance. However, because it requires generating the entire output in advance to determine the correct column widths, <code class=scheme><span class=variable>tabular</span></code> cannot format a table larger than would fit in memory. <p> <h3>(fmt-columns <column> ...)</h3> The low-level formatter on which <code class=scheme><span class=variable>columnar</span></code> is based. Each <code class=scheme><span class=variable><column></span></code> must be a list of 2-3 elements: <p> <code class=scheme>(<span class=variable><line-formatter></span> <span class=variable><line-generator></span> [<span class=variable><infinite?></span>])</code> <p> where <code class=scheme><span class=variable><line-generator></span></code> is the column generator as above, and the <code class=scheme><span class=variable><line-formatter></span></code> is how each line is formatted. Raw concatenation of each line is performed, without any spacing or width adjustment. <code class=scheme><span class=variable><infinite?></span></code>, if true, indicates this generator produces an infinite number of lines and termination should be determined without it. <p> <h3>(wrap-lines <format> ...)</h3> Behaves like <code class=scheme><span class=variable>cat</span></code>, except text is accumulated and lines are optimally wrapped to fit in the current width as in the Unix <code>fmt(1)</code> command. <p> <h3>(justify <format> ...)</h3> Like <code class=scheme><span class=variable>wrap-lines</span></code> except the lines are full-justified. <p> <pre class=scheme> (<span class=keyword>define</span> <span class=variable>func</span> '(<span class=keyword>define</span> (<span class=variable>fold</span> <span class=variable>kons</span> <span class=variable>knil</span> <span class=variable>ls</span>) (<span class=keyword>let</span> <span class=variable>lp</span> ((<span class=variable>ls</span> <span class=variable>ls</span>) (<span class=variable>acc</span> <span class=variable>knil</span>)) (<span class=keyword>if</span> (<span class=variable>null?</span> <span class=variable>ls</span>) <span class=variable>acc</span> (<span class=variable>lp</span> (<span class=variable>cdr</span> <span class=variable>ls</span>) (<span class=variable>kons</span> (<span class=variable>car</span> <span class=variable>ls</span>) <span class=variable>acc</span>)))))) (<span class=keyword>define</span> <span class=variable>doc</span> (<span class=variable>string-append</span> <span class=string>"The fundamental list iterator. Applies KONS to each element "</span> <span class=string>"of LS and the result of the previous application, beginning "</span> <span class=string>"with KNIL. With KONS as CONS and KNIL as '(), equivalent to REVERSE."</span>)) (<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>columnar</span> (<span class=variable>pretty</span> <span class=variable>func</span>) <span class=string>" ; "</span> (<span class=variable>justify</span> <span class=variable>doc</span>))) </pre> <p> outputs <p> <pre> (define (fold kons knil ls) ; The fundamental list iterator. (let lp ((ls ls) (acc knil)) ; Applies KONS to each element of (if (null? ls) ; LS and the result of the previous acc ; application, beginning with KNIL. (lp (cdr ls) ; With KONS as CONS and KNIL as '(), (kons (car ls) acc))))) ; equivalent to REVERSE. </pre> <p> <h3>(fmt-file <pathname>)</h3> Simply displayes the contents of the file <code class=scheme><span class=variable><pathname></span></code> a line at a time, so that in typical formatters such as <code class=scheme><span class=variable>columnar</span></code> only constant memory is consumed, making this suitable for formatting files of arbitrary size. <p> <h3>(line-numbers [<start>])</h3> A convenience utility, just formats an infinite stream of numbers (in the current radix) beginning with <code class=scheme><span class=variable><start></span></code>, which defaults to <code class=scheme><span class=number>1</span></code>. <p> The Unix <code class=scheme><span class=variable>nl</span>(<span class=number>1</span>)</code> utility could be implemented as: <p> <pre class=scheme> (<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>columnar</span> <span class=number>6</span> '<span class=variable>right</span> '<span class=variable>infinite</span> (<span class=variable>line-numbers</span>) <span class=string>" "</span> (<span class=variable>fmt-file</span> <span class=string>"read-line.scm"</span>))) </pre> <p> <pre> 1 2 (define (read-line . o) 3 (let ((port (if (pair? o) (car o) (current-input-port)))) 4 (let lp ((res '())) 5 (let ((c (read-char port))) 6 (if (or (eof-object? c) (eqv? c #\newline)) 7 (list->string (reverse res)) 8 (lp (cons c res))))))) </pre> <p> <a name="SECTION_6"><h1>6 C Formatting</h1> <a name="SECTION_6.1"><h2>6.1 C Formatting Basics</h2> For purposes such as writing wrappers, code-generators, compilers or other language tools, people often need to generate or emit C code. Without a decent library framework it's difficult to maintain proper indentation. In addition, for the Scheme programmer it's tedious to work with all the context sensitivities of C, such as the expression vs. statement distinction, special rules for writing preprocessor macros, and when precedence rules require parenthesis. Fortunately, context is one thing this formatting library is good at keeping track of. The C formatting interface tries to make it as easy as possible to generate C code without getting in your way. <p> There are two approaches to using the C formatting extensions - procedural and sexp-oriented (described in <a href="#SECTION_6.7">6.7</a>). In the procedural interface, C operators are made available as formatters with a "c-" prefix, literals are converted to their C equivalents and symbols are output as-is (you're responsible for making sure they are valid C identifiers). Indentation is handled automatically. <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>c-if</span> <span class=number>1</span> <span class=number>2</span> <span class=number>3</span>))</code> <p> outputs <p> <pre> if (1) { 2; } else { 3; } </pre> <p> In addition, the formatter knows when you're in an expression and when you're in a statement, and behaves accordingly, so that <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>c-if</span> (<span class=variable>c-if</span> <span class=number>1</span> <span class=number>2</span> <span class=number>3</span>) <span class=number>4</span> <span class=number>5</span>))</code> <p> outputs <p> <pre> if (1 ? 2 : 3) { 4; } else { 5; } </pre> <p> Similary, <code class=scheme><span class=variable>c-begin</span></code>, used for sequencing, will separate with semi-colons in a statement and commas in an expression. <p> Moreover, we also keep track of the final expression in a function and insert returns for you: <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>c-fun</span> '<span class=variable>int</span> '<span class=variable>foo</span> '() (<span class=variable>c-if</span> (<span class=variable>c-if</span> <span class=number>1</span> <span class=number>2</span> <span class=number>3</span>) <span class=number>4</span> <span class=number>5</span>)))</code> <p> outputs <p> <pre> int foo () { if (1 ? 2 : 3) { return 4; } else { return 5; } } </pre> <p> although it knows that void functions don't return. <p> Switch statements insert breaks by default if they don't return: <p> <pre class=scheme> (<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>c-switch</span> '<span class=variable>y</span> (<span class=variable>c-case</span> <span class=number>1</span> (<span class=variable>c+=</span> '<span class=variable>x</span> <span class=number>1</span>)) (<span class=variable>c-default</span> (<span class=variable>c+=</span> '<span class=variable>x</span> <span class=number>2</span>)))) </pre> <p> <pre> switch (y) { case 1: x += 1; break; default: x += 2; break; } </pre> <p> though you can explicitly fallthrough if you want: <p> <pre class=scheme> (<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>c-switch</span> '<span class=variable>y</span> (<span class=variable>c-case/fallthrough</span> <span class=number>1</span> (<span class=variable>c+=</span> '<span class=variable>x</span> <span class=number>1</span>)) (<span class=variable>c-default</span> (<span class=variable>c+=</span> '<span class=variable>x</span> <span class=number>2</span>)))) </pre> <p> <pre> switch (y) { case 1: x += 1; default: x += 2; break; } </pre> <p> Operators are available with just a "c" prefix, e.g. c+, c-, c*, c/, etc. <code class=scheme><span class=variable>c++</span></code> is a prefix operator, <code class=scheme><span class=variable>c++/post</span></code> is postfix. ||, | and |= are written as <code class=scheme><span class=variable>c-or</span></code>, <code class=scheme><span class=variable>c-bit-or</span></code> and <code class=scheme><span class=variable>c-bit-or=</span></code> respectively. <p> Function applications are written with <code class=scheme><span class=variable>c-apply</span></code>. Other control structures such as <code class=scheme><span class=variable>c-for</span></code> and <code class=scheme><span class=variable>c-while</span></code> work as expected. The full list is in the procedure index below. <p> When a C formatter encounters an object it doesn't know how to write (including lists and records), it outputs them according to the format state's current <code class=scheme>'<span class=variable>gen</span></code> variable. This allows you to specify generators for your own types, e.g. if you are using your own AST records in a compiler. <p> If the <code class=scheme>'<span class=variable>gen</span></code> variable isn't set it defaults to the <code class=scheme><span class=variable>c-expr/sexp</span></code> procedure, which formats an s-expression as if it were C code. Thus instead of <code class=scheme><span class=variable>c-apply</span></code> you can just use a list. The full API is available via normal s-expressions - formatters that aren't keywords in C are prefixed with a % or otherwise made invalid C identifiers so that they can't be confused with function application. <p> <a name="SECTION_6.2"><h2>6.2 C Preprocessor Formatting</h2> C preprocessor formatters also properly handle their surrounding context, so you can safely intermix them in the normal flow of C code. <p> <pre class=scheme> (<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>c-switch</span> '<span class=variable>y</span> (<span class=variable>c-case</span> <span class=number>1</span> (<span class=variable>c=</span> '<span class=variable>x</span> <span class=number>1</span>)) (<span class=variable>cpp-ifdef</span> '<span class=variable>H_TWO</span> (<span class=variable>c-case</span> <span class=number>2</span> (<span class=variable>c=</span> '<span class=variable>x</span> <span class=number>4</span>))) (<span class=variable>c-default</span> (<span class=variable>c=</span> '<span class=variable>x</span> <span class=number>5</span>)))) </pre> <p> <pre> switch (y) { case 1: x = 1; break; #ifdef H_TWO case 2: x = 4; break; #endif /* H_TWO */ default: x = 5; break; } </pre> <p> Macros can be handled with <code class=scheme><span class=variable>cpp-define</span></code>, which knows to wrap individual variable references in parenthesis: <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>cpp-define</span> '(<span class=variable>min</span> <span class=variable>x</span> <span class=variable>y</span>) (<span class=variable>c-if</span> (<span class=variable>c<</span> '<span class=variable>x</span> '<span class=variable>y</span>) '<span class=variable>x</span> '<span class=variable>y</span>)))</code> <p> <pre> #define min(x, y) (((x) < (y)) ? (x) : (y)) </pre> <p> As with all C formatters, the CPP output is pretty printed as needed, and if it wraps over several lines the lines are terminated with a backslash. <p> To write a C header file that is included at most once, you can wrap the entire body in <code class=scheme><span class=variable>cpp-wrap-header</span></code>: <p> <pre class=scheme> (<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>cpp-wrap-header</span> <span class=string>"FOO_H"</span> (<span class=variable>c-extern</span> (<span class=variable>c-prototype</span> '<span class=variable>int</span> '<span class=variable>foo</span> '())))) </pre> <p> <pre> #ifndef FOO_H #define FOO_H extern int foo (); #endif /* ! FOO_H */ </pre> <p> <a name="SECTION_6.3"><h2>6.3 Customizing C Style</h2> The output uses a simplified K&R style with 4 spaces for indentation by default. The following state variables let you override the style: <p> <h3>'indent-space</h3> how many spaces to indent bodies, default <code class=scheme><span class=number>4</span></code> <p> <h3>'switch-indent-space</h3> how many spaces to indent switch clauses, also defaults to <code class=scheme><span class=number>4</span></code> <p> <h3>'newline-before-brace?</h3> insert a newline before an open brace (non-K&R), defaults to <code class=scheme><span class=boolean>#f</span></code> <p> <h3>'braceless-bodies?</h3> omit braces when we can prove they aren't needed <p> <h3>'non-spaced-ops?</h3> omit spaces between operators and operands for groups of variables and literals (e.g. "a+b+3" instead of "a + b + 3"} <p> <h3>'no-wrap?</h3> Don't wrap function calls and long operator groups over mulitple lines. Functions and control structures will still use multiple lines. <p> The C formatters also respect the <code class=scheme>'<span class=variable>radix</span></code> and <code class=scheme>'<span class=variable>precision</span></code> settings. <p> <a name="SECTION_6.4"><h2>6.4 C Formatter Index</h2> <h3>(c-if <condition> <pass> [<fail> [<condition2> <pass2> ...]])</h3> Print a chain of if/else conditions. Use a final condition of <code class=scheme>'<span class=keyword>else</span></code> for a final else clause. <p> <h3>(c-for <init> <condition> <update> <body> ...)</h3> <h3>(c-while <condition> <body> ...)</h3> Basic loop constructs. <p> <h3>(c-fun <type> <name> <params> <body> ...)</h3> <h3>(c-prototype <type> <name> <params>)</h3> Output a function or function prototype. The parameters should be a list 2-element lists of the form <code class=scheme>(<span class=variable><param-type></span> <span class=variable><param-name></span>)</code>, which are output with DSP. A parameter can be abbreviated as just the symbol name, or <code class=scheme><span class=boolean>#f</span></code> can be passed as the type, in which case the <code class=scheme>'<span class=variable>default-type</span></code> state variable is used. The parameters may be a dotted list, in which case ellipses for a C variadic are inserted - the actual name of the dotted value is ignored. <p> Types are just typically just symbols, or lists of symbols such as <code class=scheme>'(<span class=variable>const</span> <span class=variable>char</span>)</code>. A complete description is given below in section <a href="#SECTION_6.6">6.6</a>. <p> These can also accessed as %fun and %prototype at the head of a list. <p> <h3>(c-var <type> <name> [<init-value>])</h3> Declares and optionally initializes a variable. Also accessed as %var at the head of a list. <p> <h3>(c-begin <expr> ...)</h3> Outputs each of the <expr>s, separated by semi-colons if in a statement or commas if in an expression. <p> <h3>(c-switch <clause> ...)</h3> <h3>(c-case <values> <body> ...)</h3> <h3>(c-case/fallthrough <values> <body> ...)</h3> <h3>(c-default <body> ...)</h3> Switch statements. In addition to using the clause formatters, clauses inside a switch may be handled with a Scheme CASE-like list, with the car a list of case values and the cdr the body. <p> <h3>(c-label <name>)</h3> <h3>(c-goto <name>)</h3> <h3>(c-return [<result>])</h3> <h3>c-break</h3> <h3>c-continue</h3> Manual labels and jumps. Labels can also be accessed as a list beginning with a colon, e.g. <code class=scheme>'(<span class=constant>:</span> <span class=variable>label1</span>)</code>. <p> <h3>(c-const <expr>)</h3> <h3>(c-static <expr>)</h3> <h3>(c-volatile <expr>)</h3> <h3>(c-restrict <expr>)</h3> <h3>(c-register <expr>)</h3> <h3>(c-auto <expr>)</h3> <h3>(c-inline <expr>)</h3> <h3>(c-extern <expr>)</h3> Declaration modifiers. May be nested. <p> <h3>(c-extern/C <body> ...)</h3> Wraps body in an extern "C" { ... } for use with C++. <p> <h3>(c-cast <type> <expr>)</h3> Casts an expression to a type. Also %cast at the head of a list. <p> <h3>(c-typedef <type> <new-name> ...)</h3> Creates a new type definition with one or more names. <p> <h3>(c-struct [<name>] <field-list> [<attributes>])</h3> <h3>(c-union [<name>] <field-list> [<attributes>])</h3> <h3>(c-class [<name>] <field-list> [<attributes>])</h3> <h3>(c-attribute <values> ...)</h3> Composite type constructors. Attributes may be accessed as %attribute at the head of a list. <p> <pre class=scheme> (<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>c-struct</span> '<span class=variable>employee</span> '((<span class=variable>short</span> <span class=variable>age</span>) ((<span class=variable>char</span> <span class=global>*</span>) <span class=variable>name</span>) ((<span class=variable>struct</span> (<span class=variable>year</span> <span class=variable>month</span> <span class=variable>day</span>)) <span class=variable>dob</span>)) (<span class=variable>c-attribute</span> '<span class=variable>packed</span>))) </pre> <p> <pre> struct employee { short age; char* name; struct { int year; int month; int day; } dob; } __attribute__ ((packed)); </pre> <p> <h3>(c-enum [<name>] <enum-list>)</h3> Enumerated types. <code class=scheme><span class=variable><enum-list></span></code> may be strings, symbols, or lists of string or symbol followed by the enum's value. <p> <h3>(c-comment <formatter> ...)</h3> Outputs the <code class=scheme><span class=variable><formatter></span></code>s wrapped in C's /* ... */ comment. Properly escapes nested comments inside in an Emacs-friendly style. <p> <a name="SECTION_6.5"><h2>6.5 C Preprocessor Formatter Index</h2> <h3>(cpp-include <file>)</h3> If file is a string, outputs in it "quotes", otherwise (as a symbol or arbitrary formatter) it outputs it in brackets. <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>cpp-include</span> '<span class=variable>stdio.h</span>))</code> <p> <code class=scheme><span class=keyword>=></span> <span class=string>"#include <stdio.h>n"</span></code> <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>cpp-include</span> <span class=string>"config.h"</span>))</code> <p> <code class=scheme><span class=keyword>=></span> <span class=string>"#include "config.h"n"</span></code> <p> <h3>(cpp-define <macro> [<value>])</h3> Defines a preprocessor macro, which may be just a name or a list of name and parameters. Properly wraps the value in parenthesis and escapes newlines. A dotted parameter list will use the C99 variadic macro syntax, and will also substitute any references to the dotted name with <code>__VA_ARGS__</code>: <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>cpp-define</span> '(<span class=variable>eprintf</span> . <span class=variable>args</span>) '(<span class=variable>fprintf</span> <span class=variable>stderr</span> <span class=variable>args</span>)))</code> <p> <pre> #define eprintf(...) (fprintf(stderr, __VA_ARGS__)) </pre> <p> <h3>(cpp-if <condition> <pass> [<fail> ...])</h3> <h3>(cpp-ifdef <condition> <pass> [<fail> ...])</h3> <h3>(cpp-ifndef <condition> <pass> [<fail> ...])</h3> <h3>(cpp-elif <condition> <pass> [<fail> ...])</h3> <h3>(cpp-else <body> ...)</h3> Conditional compilation. <p> <h3>(cpp-line <num> [<file>])</h3> Line number information. <p> <h3>(cpp-pragma <args> ...)</h3> <h3>(cpp-error <args> ...)</h3> <h3>(cpp-warning <args> ...)</h3> Additional preprocessor directives. <p> <h3>(cpp-stringify <expr>)</h3> Stringifies <code class=scheme><span class=variable><expr></span></code> by prefixing the # operator. <p> <h3>(cpp-sym-cat <args> ...)</h3> Joins the <code class=scheme><span class=variable><args></span></code> into a single preprocessor token with the ## operator. <p> <h3>(cpp-wrap-header <name> <body> ...)</h3> Wrap an entire header to only be included once. <p> <h3>Operators:</h3> <pre class=scheme> <span class=variable>c++</span> <span class=variable>c--</span> <span class=variable>c+</span> <span class=variable>c-</span> <span class=variable>c*</span> <span class=variable>c/</span> <span class=variable>c%</span> <span class=variable>c&</span> <span class=variable>c^</span> <span class=variable>c~</span> <span class=variable>c!</span> <span class=variable>c&&</span> <span class=variable>c<<</span> <span class=variable>c>></span> <span class=variable>c==</span> <span class=variable>c!=</span> <span class=variable>c<</span> <span class=variable>c></span> <span class=variable>c<=</span> <span class=variable>c>=</span> <span class=variable>c=</span> <span class=variable>c+=</span> <span class=variable>c-=</span> <span class=variable>c*=</span> <span class=variable>c/=</span> <span class=variable>c%=</span> <span class=variable>c&=</span> <span class=variable>c^=</span> <span class=variable>c<<=</span> <span class=variable>c>>=</span> <span class=variable>c++/post</span> <span class=variable>c--/post</span> <span class=variable>c-or</span> <span class=variable>c-bit-or</span> <span class=variable>c-bit-or=</span> </pre> <p> <a name="SECTION_6.6"><h2>6.6 C Types</h2> <p> Typically a type is just a symbol such as <code class=scheme>'<span class=variable>char</span></code> or <code class=scheme>'<span class=variable>int</span></code>. You can wrap types with modifiers such as <code class=scheme><span class=variable>c-const</span></code>, but as a convenience you can just use a list such as in <code class=scheme>'(<span class=variable>const</span> <span class=variable>unsignedchar</span> <span class=global>*</span>)</code>. You can also nest these lists, so the previous example is equivalent to <code class=scheme>'(<span class=global>*</span> (<span class=variable>const</span> (<span class=variable>unsigned</span> <span class=variable>char</span>)))</code>. <p> Pointers may be written as <code class=scheme>'(<span class=variable>%pointer</span> <span class=variable><type></span>)</code> for readability - <code class=scheme><span class=variable>%pointer</span></code> is exactly equivalent to <code class=scheme><span class=global>*</span></code> in types. <p> Unamed structs, classes, unions and enums may be used directly as types, using their respective keywords at the head of a list. <p> Two special types are the %array type and function pointer type. An array is written: <p> <code class=scheme>(<span class=variable>%array</span> <span class=variable><type></span> [<span class=variable><size></span>])</code> <p> where <code class=scheme><span class=variable><type></span></code> is any other type (including another array or function pointer), and <code class=scheme><span class=variable><size></span></code>, if given, will print the array size. For example: <p> <code class=scheme>(<span class=variable>c-var</span> '(<span class=variable>%array</span> (<span class=variable>unsigned</span> <span class=variable>long</span>) <span class=variable>SIZE</span>) '<span class=variable>table</span> '<span class=constant>#</span>(<span class=number>1</span> <span class=number>2</span> <span class=number>3</span> <span class=number>4</span>))</code> <p> <pre> unsigned long table[SIZE] = {1, 2, 3, 4}; </pre> <p> A function pointer is written: <p> <code class=scheme>(<span class=variable>%fun</span> <span class=variable><return-type></span> (<span class=variable><param-types></span> ...))</code> <p> For example: <p> <code class=scheme>(<span class=variable>c-typedef</span> '(<span class=variable>%fun</span> <span class=variable>double</span> (<span class=variable>double</span> <span class=variable>double</span> <span class=variable>int</span>)) '<span class=variable>f</span>)</code> <p> <pre> typedef double (*f)(double, double, int); </pre> <p> Wherever a type is expected but not given, the value of the <code class=scheme>'<span class=variable>default-type</span></code> formatting state variable is used. By default this is just <code class=scheme>'<span class=variable>int</span></code>. <p> Type declarations work uniformly for variables and parameters, as well for casts and typedefs. <p> <a name="SECTION_6.7"><h2>6.7 C as S-Expressions</h2> <p> Rather than building formatting closures by hand, it can be more convenient to just build a normal s-expression and ask for it to be formatted as C code. This can be thought of as a simple Scheme->C compiler without any runtime support. <p> In a s-expression, strings and characters are printed as C strings and characters, booleans are printed as 0 or 1, symbols are displayed as-is, and numbers are printed as C numbers (using the current formatting radix if specified). Vectors are printed as comma-separated lists wrapped in braces, which can be used for initializing arrays or structs. <p> A list indicates a C expression or statement. Any of the existing C keywords can be used to pretty-print the expression as described with the c-keyword formatters above. Thus, the example above <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>c-if</span> (<span class=variable>c-if</span> <span class=number>1</span> <span class=number>2</span> <span class=number>3</span>) <span class=number>4</span> <span class=number>5</span>))</code> <p> could also be written <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>c-expr</span> '(<span class=keyword>if</span> (<span class=keyword>if</span> <span class=number>1</span> <span class=number>2</span> <span class=number>3</span>) <span class=number>4</span> <span class=number>5</span>)))</code> <p> C constructs that are dependent on the underlying syntax and have no keyword are written with a % prefix (<code class=scheme><span class=variable>%fun</span></code>, <code class=scheme><span class=variable>%var</span></code>, <code class=scheme><span class=variable>%pointer</span></code>, <code class=scheme><span class=variable>%array</span></code>, <code class=scheme><span class=variable>%cast</span></code>), including C preprocessor constructs (<code class=scheme><span class=variable>%include</span></code>, <code class=scheme><span class=variable>%define</span></code>, <code class=scheme><span class=variable>%pragma</span></code>, <code class=scheme><span class=variable>%error</span></code>, <code class=scheme><span class=variable>%warning</span></code>, <code class=scheme><span class=variable>%if</span></code>, <code class=scheme><span class=variable>%ifdef</span></code>, <code class=scheme><span class=variable>%ifndef</span></code>, <code class=scheme><span class=variable>%elif</span></code>). Labels are written as <code class=scheme>(<span class=constant>:</span> <span class=variable><label-name></span>)</code>. You can write a sequence as <code class=scheme>(<span class=variable>%begin</span> <span class=variable><expr></span> ...)</code>. <p> For example, the following definition of the fibonacci sequence, which apart from the return type of <code class=scheme><span class=boolean>#f</span></code> looks like a Lisp definition: <p> <code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>c-expr</span> '(<span class=variable>%fun</span> <span class=boolean>#f</span> <span class=variable>fib</span> (<span class=variable>n</span>) (<span class=keyword>if</span> (<span class=variable><=</span> <span class=variable>n</span> <span class=number>1</span>) <span class=number>1</span> (<span class=variable>+</span> (<span class=variable>fib</span> (<span class=variable>-</span> <span class=variable>n</span> <span class=number>1</span>)) (<span class=variable>fib</span> (<span class=variable>-</span> <span class=variable>n</span> <span class=number>2</span>)))))))</code> <p> prints the working C definition: <p> <pre> int fib (int n) { if (n <= 1) { return 1; } else { return fib((n - 1)) + fib((n - 2)); } } </pre> <p> <a name="SECTION_7"><h1>7 JavaScript Formatting</h1> The experimental fmt-js library extends the fmt-c library with functionality for formatting JavaScript code. <p> <h3>(js-expr x)</h3> Formats a JavaScript expression similarly to <code class=scheme><span class=variable>c-expr</span></code>. Inside a <code class=scheme><span class=variable>js-expr</span></code> formatter, you can use the normal <code class=scheme><span class=variable>c-</span></code> prefixed formatters described in the previous section, and they will format appropriately for JavaScript. <p> Currently expressions will all be terminated with a semi-colon, but that will be made optional in a later release. <p> <h3>(js-function [<name>] (<params>) <body> ...)</h3> Defines a function (anonymously if no name is provided). <p> <h3>(js-var <name> [<init-value>])</h3> Declares a JavaScript variable, optionally with an initial value. <p> <h3>(js-comment <formatter> ...)</h3> Formats a comment prefixing lines with <code class=scheme><span class=string>"// "</span></code>. <p> <a name="SECTION_8"><h1>8 Formatting with Color</h1> The fmt-color library provides the following utilities: <p> <pre class=scheme> (<span class=variable>fmt-red</span> <span class=variable><formatter></span> ...) (<span class=variable>fmt-blue</span> <span class=variable><formatter></span> ...) (<span class=variable>fmt-green</span> <span class=variable><formatter></span> ...) (<span class=variable>fmt-cyan</span> <span class=variable><formatter></span> ...) (<span class=variable>fmt-yellow</span> <span class=variable><formatter></span> ...) (<span class=variable>fmt-magenta</span> <span class=variable><formatter></span> ...) (<span class=variable>fmt-white</span> <span class=variable><formatter></span> ...) (<span class=variable>fmt-black</span> <span class=variable><formatter></span> ...) (<span class=variable>fmt-bold</span> <span class=variable><formatter></span> ...) (<span class=variable>fmt-underline</span> <span class=variable><formatter></span> ...) </pre> <p> and more generally <p> <code class=scheme>(<span class=variable>fmt-color</span> <span class=variable><color></span> <span class=variable><formatter></span> ...)</code> <p> where color can be a symbol name or <code class=scheme><span class=constant>#xRRGGBB</span></code> numeric value. Outputs the formatters colored with ANSI escapes. In addition <p> <code class=scheme>(<span class=variable>fmt-in-html</span> <span class=variable><formatter></span> ...)</code> <p> can be used to mark the format state as being inside HTML, which the above color formats will understand and output HTML <code class=scheme><span class=variable><span></span></code> tags with the appropriate style colors, instead of ANSI escapes. <p> <a name="SECTION_9"><h1>9 Unicode</h1> The fmt-unicode library provides the <code class=scheme><span class=variable>fmt-unicode</span></code> formatter, which just takes a list of formatters and overrides the string-length for padding and trimming, such that Unicode double or full width characters are considered 2 characters wide (as they typically are in fixed-width terminals), while treating combining and non-spacing characters as 0 characters wide. <p> It also recognizes and ignores ANSI escapes, in particular useful if you want to combine this with the fmt-color utilities. <p> <a name="SECTION_10"><h1>10 Optimizing</h1> The library is designed for scalability and flexibility, not speed, and I'm not going to think about any fine tuning until it's more stabilised. One aspect of the design, however, was influenced for the sake of future optimizations, which is that none of the default format variables are initialized by global parameters, which leaves room for inlining and subsequent simplification of format calls. <p> If you don't have an aggressively optimizing compiler, you can easily achieve large speedups on common cases with CL-style compiler macros. <p> <a name="SECTION_11"><h1>11 Common Lisp Format Cheat Sheet</h1> A quick reference for those of you switching over from Common Lisp's format. <p> <table> <tr><td><strong>format</strong> </td><td> <strong>fmt</strong></td></tr> <tr><td>~a </td><td> <code class=scheme><span class=variable>dsp</span></code></td></tr> <tr><td>~c </td><td> <code class=scheme><span class=variable>dsp</span></code></td></tr> <tr><td>~s </td><td> <code class=scheme><span class=variable>wrt/unshared</span></code></td></tr> <tr><td>~w </td><td> <code class=scheme><span class=variable>wrt</span></code></td></tr> <tr><td>~y </td><td> <code class=scheme><span class=variable>pretty</span></code></td></tr> <tr><td>~x </td><td> <code class=scheme>(<span class=variable>radix</span> <span class=number>16</span> ...)</code> or <code class=scheme>(<span class=variable>num</span> <span class=variable><n></span> <span class=number>16</span>)</code></td></tr> <tr><td>~o </td><td> <code class=scheme>(<span class=variable>radix</span> <span class=number>8</span> ...)</code> or <code class=scheme>(<span class=variable>num</span> <span class=variable><n></span> <span class=number>8</span>)</code></td></tr> <tr><td>~b </td><td> <code class=scheme>(<span class=variable>radix</span> <span class=number>2</span> ...)</code> or <code class=scheme>(<span class=variable>num</span> <span class=variable><n></span> <span class=number>2</span>)</code></td></tr> <tr><td>~f </td><td> <code class=scheme>(<span class=variable>fix</span> <span class=variable><digits></span> ...)</code> or <code class=scheme>(<span class=variable>num</span> <span class=variable><n></span> <span class=variable><radix></span> <span class=variable><digits></span>)</code></td></tr> <tr><td>~% </td><td> <code class=scheme><span class=variable>nl</span></code></td></tr> <tr><td>~& </td><td> <code class=scheme><span class=variable>fl</span></code></td></tr> <tr><td>~[...~] </td><td> normal <code class=scheme><span class=keyword>if</span></code> or <code class=scheme><span class=variable>fmt-if</span></code> (delayed test)</td></tr> <tr><td>~{...~} </td><td> <code class=scheme>(<span class=variable>fmt-join</span> ... <span class=variable><list></span> [<span class=variable><sep></span>])</code></td></tr> </table> <p> <a name="SECTION_12"><h1>12 References</h1> <a name="BIBITEM_1">[1] R. Kelsey, W. Clinger, J. Rees (eds.) <a href="http://www.schemers.org/Documents/Standards/R5RS/">Revised^5 Report on the Algorithmic Language Scheme</a> <p> <a name="BIBITEM_2">[2] Guy L. Steele Jr. (editor) <a href="http://www.harlequin.com/education/books/HyperSpec/">Common Lisp Hyperspec</a> <p> <a name="BIBITEM_3">[3] Scott G. Miller <a href="http://srfi.schemers.org/srfi-28/">SRFI-28 Basic Format Strings</a> <p> <a name="BIBITEM_4">[4] Ken Dickey <a href="http://srfi.schemers.org/srfi-48/">SRFI-48 Intermediate Format Strings</a> <p> <a name="BIBITEM_5">[5] Ray Dillinger <a href="http://srfi.schemers.org/srfi-38/">SRFI-38 External Representation for Data With Shared Structure</a> <p> <a name="BIBITEM_6">[6] Damian Conway <a href="http://www.perl.com/lpt/a/819">Perl6 Exegesis 7 - formatting</a> <p> <br /><br /><br /><br /> <!-- page created by Mistie, http://www.cs.rice.edu/~dorai/mistie/ --> </body></html> |
Added fmt/fmt.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 |
;;;; fmt.scm -- extensible formatting library ;; ;; Copyright (c) 2006-2009 Alex Shinn. All rights reserved. ;; BSD-style license: http://synthcode.com/license.txt ;; (require-extension (srfi 1 6 13 23 69)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; string utilities (define (write-to-string x) (call-with-output-string (lambda (p) (write x p)))) (define (display-to-string x) (if (string? x) x (call-with-output-string (lambda (p) (display x p))))) (define nl-str (call-with-output-string newline)) (define (make-space n) (make-string n #\space)) (define (make-nl-space n) (string-append nl-str (make-string n #\space))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; list utilities (define (take* ls n) ; handles dotted lists and n > length (cond ((zero? n) '()) ((pair? ls) (cons (car ls) (take* (cdr ls) (- n 1)))) (else '()))) (define (drop* ls n) ; may return the dot (cond ((zero? n) ls) ((pair? ls) (drop* (cdr ls) (- n 1))) (else ls))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; format state representation ;; Use a flexible representation optimized for common cases - ;; frequently accessed values are in fixed vector slots, with a ;; `properties' slot holding an alist for all other values. (define *default-fmt-state* (vector 0 0 10 '() #\space #f 78 #f #f #f #f #f #f)) (define fmt-state? vector?) (define (new-fmt-state . o) (let ((st (if (pair? o) (car o) (current-output-port)))) (if (vector? st) st (fmt-set-writer! (fmt-set-port! (copy-fmt-state *default-fmt-state*) st) fmt-write)))) (define (copy-fmt-state st) (let* ((len (vector-length st)) (res (make-vector len))) (do ((i 0 (+ i 1))) ((= i len)) (vector-set! res i (vector-ref st i))) (fmt-set-properties! res (map (lambda (x) (cons (car x) (cdr x))) (fmt-properties res))) res)) (define (fmt-row st) (vector-ref st 0)) (define (fmt-col st) (vector-ref st 1)) (define (fmt-radix st) (vector-ref st 2)) (define (fmt-properties st) (vector-ref st 3)) (define (fmt-pad-char st) (vector-ref st 4)) (define (fmt-precision st) (vector-ref st 5)) (define (fmt-width st) (vector-ref st 6)) (define (fmt-writer st) (vector-ref st 7)) (define (fmt-port st) (vector-ref st 8)) (define (fmt-decimal-sep st) (vector-ref st 9)) (define (fmt-decimal-align st) (vector-ref st 10)) (define (fmt-string-width st) (vector-ref st 11)) (define (fmt-ellipses st) (vector-ref st 12)) (define (fmt-set-row! st x) (vector-set! st 0 x) st) (define (fmt-set-col! st x) (vector-set! st 1 x) st) (define (fmt-set-radix! st x) (vector-set! st 2 x) st) (define (fmt-set-properties! st x) (vector-set! st 3 x) st) (define (fmt-set-pad-char! st x) (vector-set! st 4 x) st) (define (fmt-set-precision! st x) (vector-set! st 5 x) st) (define (fmt-set-width! st x) (vector-set! st 6 x) st) (define (fmt-set-writer! st x) (vector-set! st 7 x) st) (define (fmt-set-port! st x) (vector-set! st 8 x) st) (define (fmt-set-decimal-sep! st x) (vector-set! st 9 x) st) (define (fmt-set-decimal-align! st x) (vector-set! st 10 x) st) (define (fmt-set-string-width! st x) (vector-set! st 11 x) st) (define (fmt-set-ellipses! st x) (vector-set! st 12 x) st) (define (fmt-ref st key . o) (case key ((row) (fmt-row st)) ((col) (fmt-col st)) ((radix) (fmt-radix st)) ((properties) (fmt-properties st)) ((writer) (fmt-writer st)) ((port) (fmt-port st)) ((precision) (fmt-precision st)) ((pad-char) (fmt-pad-char st)) ((width) (fmt-width st)) ((decimal-sep) (fmt-decimal-sep st)) ((decimal-align) (fmt-decimal-align st)) ((string-width) (fmt-string-width st)) ((ellipses) (fmt-ellipses st)) (else (cond ((assq key (fmt-properties st)) => cdr) ((pair? o) (car o)) (else #f))))) (define (fmt-set-property! st key val) (cond ((assq key (fmt-properties st)) => (lambda (cell) (set-cdr! cell val) st)) (else (fmt-set-properties! st (cons (cons key val) (fmt-properties st)))))) (define (fmt-set! st key val) (case key ((row) (fmt-set-row! st val)) ((col) (fmt-set-col! st val)) ((radix) (fmt-set-radix! st val)) ((properties) (fmt-set-properties! st val)) ((pad-char) (fmt-set-pad-char! st val)) ((precision) (fmt-set-precision! st val)) ((writer) (fmt-set-writer! st val)) ((port) (fmt-set-port! st val)) ((width) (fmt-set-width! st val)) ((decimal-sep) (fmt-set-decimal-sep! st val)) ((decimal-align) (fmt-set-decimal-align! st val)) ((string-width) (fmt-set-string-width! st val)) ((ellipses) (fmt-set-ellipses! st val)) (else (fmt-set-property! st key val)))) (define (fmt-add-properties! st alist) (for-each (lambda (x) (fmt-set! st (car x) (cdr x))) alist) st) (define (fmt-let key val . ls) (lambda (st) (let ((orig-val (fmt-ref st key))) (fmt-set! ((apply-cat ls) (fmt-set! st key val)) key orig-val)))) (define (fmt-bind key val . ls) (lambda (st) ((apply-cat ls) (fmt-set! st key val)))) (define (fix prec . ls) (fmt-let 'precision prec (apply-cat ls))) (define (radix rad . ls) (fmt-let 'radix rad (apply-cat ls))) (define (pad-char ch . ls) (fmt-let 'pad-char ch (apply-cat ls))) (define (comma-char ch . ls) (fmt-let 'comma-char ch (apply-cat ls))) (define (decimal-char ch . ls) (fmt-let 'decimal-sep ch (apply-cat ls))) (define (decimal-align n . ls) (fmt-let 'decimal-align n (apply-cat ls))) (define (with-width w . ls) (fmt-let 'width w (apply-cat ls))) (define (ellipses ell . ls) (fmt-let 'ellipses ell (apply-cat ls))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; the basic interface (define (fmt-start st initializer proc) (cond ((or (output-port? st) (fmt-state? st)) (proc (initializer st)) (if #f #f)) ((eq? #t st) (proc (initializer (current-output-port))) (if #f #f)) ((eq? #f st) (get-output-string (fmt-port (proc (initializer (open-output-string)))))) (else (error "unknown format output" st)))) (define (fmt st . args) (fmt-start st new-fmt-state (apply-cat args))) (define (fmt-update str st) (let ((len (string-length str)) (nli (string-index-right str #\newline)) (str-width (fmt-string-width st))) (if nli (let ((row (+ (fmt-row st) 1 (string-count str #\newline 0 nli)))) (fmt-set-row! (fmt-set-col! st (if str-width (str-width str (+ nli 1) len) (- len (+ nli 1)))) row)) (fmt-set-col! st (+ (fmt-col st) (if str-width (str-width str 0 len) len)))))) (define (fmt-write str st) (display str (fmt-port st)) (fmt-update str st)) (define (apply-cat procs) (lambda (st) (let loop ((ls procs) (st st)) (if (null? ls) st (loop (cdr ls) ((dsp (car ls)) st)))))) (define (cat . ls) (apply-cat ls)) (define (fmt-null st) st) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; control structures (define (fmt-if check pass . o) (let ((fail (if (pair? o) (car o) (lambda (x) x)))) (lambda (st) (if (check st) ((dsp pass) st) ((dsp fail) st))))) (define (fmt-try-fit proc . fail) (if (null? fail) proc (lambda (orig-st) (let ((width (fmt-width orig-st)) (buffer '())) (call-with-current-continuation (lambda (return) (define (output* str st) (let lp ((i 0) (col (fmt-col st))) (let ((nli (string-index str #\newline i))) (if nli (if (> (+ (- nli i) col) width) (return ((apply fmt-try-fit fail) orig-st)) (lp (+ nli 1) 0)) (let* ((len ((or (fmt-string-width st) string-length) str)) (col (+ (- len i) col))) (if (> col width) (return ((apply fmt-try-fit fail) orig-st)) (begin (set! buffer (cons str buffer)) (fmt-update str st)))))))) (proc (fmt-set-port! (fmt-set-writer! (copy-fmt-state orig-st) output*) (open-output-string))) ((fmt-writer orig-st) (string-concatenate-reverse buffer) orig-st))))))) (define (fits-in-width gen width) (lambda (st) (let ((output (fmt-writer st)) (port (open-output-string))) (call-with-current-continuation (lambda (return) (define (output* str st) (let ((st (fmt-update str st))) (if (> (fmt-col st) width) (return #f) (begin (display str port) st)))) (gen (fmt-set-port! (fmt-set-writer! (copy-fmt-state st) output*) port)) (get-output-string port)))))) (define (fits-in-columns ls write width) (lambda (st) (let ((max-w (quotient width 2))) (let lp ((ls ls) (res '()) (widest 0)) (cond ((pair? ls) (let ((str ((fits-in-width (write (car ls)) max-w) st))) (and str (lp (cdr ls) (cons str res) (max ((or (fmt-string-width st) string-length) str) widest))))) ((null? ls) (cons widest (reverse res))) (else #f)))))) (define (fmt-capture producer consumer) (lambda (st) (let ((port (open-output-string))) (producer (fmt-set-writer! (fmt-set-port! (copy-fmt-state st) port) fmt-write)) ((consumer (get-output-string port)) st)))) (define (fmt-to-string producer) (fmt-capture producer (lambda (str) (lambda (st) str)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; standard formatters (define (nl st) ((fmt-writer st) nl-str st)) ;; output a newline iff we're not at the start of a fresh line (define (fl st) (if (zero? (fmt-col st)) st (nl st))) ;; tab to a given tab-stop (define (tab-to . o) (lambda (st) (let* ((tab-width (if (pair? o) (car o) 8)) (rem (modulo (fmt-col st) tab-width))) (if (positive? rem) ((fmt-writer st) (make-string (- tab-width rem) (fmt-pad-char st)) st) st)))) ;; move to an explicit column (define (space-to col) (lambda (st) (let ((width (- col (fmt-col st)))) (if (positive? width) ((fmt-writer st) (make-string width (fmt-pad-char st)) st) st)))) (define (fmt-join fmt ls . o) (let ((sep (dsp (if (pair? o) (car o) "")))) (lambda (st) (if (null? ls) st (let lp ((ls (cdr ls)) (st ((fmt (car ls)) st))) (if (null? ls) st (lp (cdr ls) ((fmt (car ls)) (sep st))))))))) (define (fmt-join/prefix fmt ls . o) (if (null? ls) fmt-null (let ((sep (dsp (if (pair? o) (car o) "")))) (cat sep (fmt-join fmt ls sep))))) (define (fmt-join/suffix fmt ls . o) (if (null? ls) fmt-null (let ((sep (dsp (if (pair? o) (car o) "")))) (cat (fmt-join fmt ls sep) sep)))) (define (fmt-join/last fmt fmt/last ls . o) (let ((sep (dsp (if (pair? o) (car o) "")))) (lambda (st) (cond ((null? ls) st) ((null? (cdr ls)) ((fmt/last (car ls)) (sep st))) (else (let lp ((ls (cdr ls)) (st ((fmt (car ls)) st))) (if (null? (cdr ls)) ((fmt/last (car ls)) (sep st)) (lp (cdr ls) ((fmt (car ls)) (sep st)))))))))) (define (fmt-join/dot fmt fmt/dot ls . o) (let ((sep (dsp (if (pair? o) (car o) "")))) (lambda (st) (cond ((pair? ls) (let lp ((ls (cdr ls)) (st ((fmt (car ls)) st))) (cond ((null? ls) st) ((pair? ls) (lp (cdr ls) ((fmt (car ls)) (sep st)))) (else ((fmt/dot ls) (sep st)))))) ((null? ls) st) (else ((fmt/dot ls) st)))))) (define (fmt-join/range fmt start . o) (let-optionals* o ((end #f) (sep "")) (lambda (st) (let lp ((i (+ start 1)) (st ((fmt start) st))) (if (and end (>= i end)) st (lp (+ i 1) ((fmt i) ((dsp sep) st)))))))) (define (pad/both width . ls) (fmt-capture (apply-cat ls) (lambda (str) (lambda (st) (let ((diff (- width ((or (fmt-string-width st) string-length) str))) (output (fmt-writer st))) (if (positive? diff) (let* ((diff/2 (quotient diff 2)) (left (make-string diff/2 (fmt-pad-char st))) (right (if (even? diff) left (make-string (+ 1 diff/2) (fmt-pad-char st))))) (output right (output str (output left st)))) (output str st))))))) (define (pad width . ls) (lambda (st) (let* ((col (fmt-col st)) (padder (lambda (st) (let ((diff (- width (- (fmt-col st) col)))) (if (positive? diff) ((fmt-writer st) (make-string diff (fmt-pad-char st)) st) st))))) ((cat (apply-cat ls) padder) st)))) (define pad/right pad) (define (pad/left width . ls) (fmt-capture (apply-cat ls) (lambda (str) (lambda (st) (let* ((str-width ((or (fmt-string-width st) string-length) str)) (diff (- width str-width))) ((fmt-writer st) str (if (positive? diff) ((fmt-writer st) (make-string diff (fmt-pad-char st)) st) st))))))) (define (trim/buffered width fmt proc) (fmt-capture fmt (lambda (str) (lambda (st) (let* ((str-width ((or (fmt-string-width st) string-length) str)) (diff (- str-width width))) ((fmt-writer st) (if (positive? diff) (proc str str-width diff st) str) st)))))) (define (trim width . ls) (lambda (st) (let ((ell (fmt-ellipses st))) (if ell ((trim/buffered width (apply-cat ls) (lambda (str str-width diff st) (let* ((ell (if (char? ell) (string ell) ell)) (ell-len (string-length ell)) (diff (- (+ str-width ell-len) width))) (if (negative? diff) ell (string-append (substring/shared str 0 (- (string-length str) diff)) ell))))) st) (let ((output (fmt-writer st)) (start-col (fmt-col st))) (call-with-current-continuation (lambda (return) (define (output* str st) (let* ((len ((or (fmt-string-width st) string-length) str)) (diff (- (+ (- (fmt-col st) start-col) len) width))) (if (positive? diff) (return (fmt-set-writer! (output (substring/shared str 0 (- len diff)) st) output)) (output str st)))) ((fmt-let 'writer output* (apply-cat ls)) st)))))))) (define (trim/length width . ls) (lambda (st) (call-with-current-continuation (lambda (return) (let ((output (fmt-writer st)) (sum 0)) (define (output* str st) (let ((len (string-length str))) (set! sum (+ sum len)) (if (> sum width) (return (fmt-set-writer! (output (substring/shared str 0 (- len (- sum width))) st) output)) (output str st)))) ((fmt-let 'writer output* (apply-cat ls)) st)))))) (define (trim/left width . ls) (trim/buffered width (apply-cat ls) (lambda (str str-width diff st) (let ((ell (fmt-ellipses st))) (if ell (let* ((ell (if (char? ell) (string ell) ell)) (ell-len (string-length ell)) (diff (- (+ str-width ell-len) width))) (if (negative? diff) ell (string-append ell (substring/shared str diff)))) (substring/shared str diff)))))) (define (trim/both width . ls) (trim/buffered width (apply-cat ls) (lambda (str str-width diff st) (let ((ell (fmt-ellipses st))) (if ell (let* ((ell (if (char? ell) (string ell) ell)) (ell-len (string-length ell)) (diff (- (+ str-width ell-len ell-len) width)) (left (quotient diff 2)) (right (- (string-length str) (quotient (+ diff 1) 2)))) (if (negative? diff) ell (string-append ell (substring/shared str left right) ell))) (substring/shared str (quotient (+ diff 1) 2) (- (string-length str) (quotient diff 2)))))))) (define (fit width . ls) (pad width (trim width (apply-cat ls)))) (define (fit/left width . ls) (pad/left width (trim/left width (apply-cat ls)))) (define (fit/both width . ls) (pad/both width (trim/both width (apply-cat ls)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; String-map formatters (define (make-string-fmt-transformer proc) (lambda ls (lambda (st) (let ((base-writer (fmt-writer st))) ((fmt-let 'writer (lambda (str st) (base-writer (proc str) st)) (apply-cat ls)) st))))) (define upcase (make-string-fmt-transformer string-upcase)) (define downcase (make-string-fmt-transformer string-downcase)) (define titlecase (make-string-fmt-transformer string-titlecase)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Numeric formatting (define *min-e* -1024) (define *bot-f* (expt 2 52)) ;;(define *top-f* (* 2 *bot-f*)) (define (integer-log a base) (if (zero? a) 0 (inexact->exact (ceiling (/ (log (+ a 1)) (log base)))))) (define (integer-length* a) (if (negative? a) (integer-log (- 1 a) 2) (integer-log a 2))) (define invlog2of (let ((table (make-vector 37)) (log2 (log 2))) (do ((b 2 (+ b 1))) ((= b 37)) (vector-set! table b (/ log2 (log b)))) (lambda (b) (if (<= 2 b 36) (vector-ref table b) (/ log2 (log b)))))) (define fast-expt (let ((table (make-vector 326))) (do ((k 0 (+ k 1)) (v 1 (* v 10))) ((= k 326)) (vector-set! table k v)) (lambda (b k) (if (and (= b 10) (<= 0 k 326)) (vector-ref table (inexact->exact (truncate k))) (expt b k))))) (define (mirror-of c) (case c ((#\() #\)) ((#\[) #\]) ((#\{) #\}) ((#\<) #\>) (else c))) (define default-digits (list->vector (string->list "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"))) ;; kanji (10 included for base 11 ;) ;; (vector "ï¼" "一" "二" "三" "å››" "五" "å…" "七" "å…«" "ä¹" "å") ;; old style kanji: ;; (vector "零" "壱" "å¼" "å‚" "肆" "ä¼" "陸" "柒" "æŒ" "玖" "拾") ;; General algorithm based on "Printing Floating-Point Numbers Quickly ;; and Accurately" by Burger and Dybvig (FP-Printing-PLDI96.pdf). The ;; code below will be hard to read out of that context until it's ;; cleaned up. (define (num->string n st . opt) (call-with-output-string (lambda (port) (let-optionals* opt ((base (fmt-radix st)) (digits (fmt-precision st)) (sign? #f) (commify? #f) (comma-sep (and commify? (fmt-ref st 'comma-char #\,))) (decimal-sep (or (fmt-decimal-sep st) (if (eqv? comma-sep #\.) #\, #\.))) (comma-rule (if (eq? commify? #t) 3 commify?)) (align (fmt-decimal-align st)) (digit-vec default-digits) (stack '())) (define (write-digit d) (display (vector-ref digit-vec (inexact->exact (truncate d))) port)) ;; This is ugly because we need to keep a list of all output ;; of the form x9999... in case we get to the end of the ;; precision and need to round up. Alas, if it weren't for ;; decimals and commas, we could just keep track of the last ;; non-9 digit and the number of nines seen, without any need ;; for a heap-allocated stack. (define (write-digit-list ls) (for-each (lambda (x) (if (number? x) (write-digit x) (display x port))) ls)) (define (flush) (write-digit-list (reverse stack)) (set! stack '())) (define (flush/rounded) (let lp ((ls stack) (res '())) (cond ((null? ls) (write-digit-list (cons #\1 res))) ((not (number? (car ls))) (lp (cdr ls) (cons (car ls) res))) ((= (car ls) (- base 1)) (lp (cdr ls) (cons #\0 res))) (else (write-digit-list (append (reverse (cdr ls)) (cons (+ 1 (car ls)) res)))))) (set! stack '())) (define (output digit) (if (and (number? digit) (< digit (- base 1))) (flush)) (set! stack (cons digit stack))) (define (write-prefix prefix align k) (if align (let* ((prefix (cond ((string? prefix) prefix) ((char? prefix) (string prefix)) (else ""))) (diff (- align (+ (if (<= k 0) 1 k) (string-length prefix)) 1))) (if (positive? diff) (display (make-string diff (fmt-pad-char st)) port)) (display prefix port)) (if prefix (display prefix port)))) (define (write-real n prefix align) (let* ((m+e (mantissa+exponent (exact->inexact n))) (f (car m+e)) (e (cadr m+e)) (inv-base (invlog2of base)) (round? (even? f)) (smaller (if round? <= <)) (bigger (if round? >= >))) (define (pad d i) ;; just pad 0's, not #'s (write-digit d) (let lp ((i (- i 1))) (cond ((>= i 0) (if (and commify? (if digits (and (> i digits) (zero? (modulo (- i (- digits 1)) comma-rule))) (and (positive? i) (zero? (modulo i comma-rule))))) (display comma-sep port)) (if (= i (- digits 1)) (display decimal-sep port)) (write-digit 0) (lp (- i 1)))))) (define (pad-all d i) (cond ((>= d base) (flush/rounded)) (else (flush) (write-digit d))) (let lp ((i (- i 1))) (cond ((> i 0) (if (and commify? (zero? (modulo i comma-rule))) (display comma-sep port)) (write-digit 0) (lp (- i 1))) ((and (= i 0) (inexact? n)) (display decimal-sep port) (write-digit 0))))) ;;(define (pad-sci d i k) ;; (cond ;; ((>= d base) ;; (flush/rounded)) ;; (else ;; (flush) ;; (write-digit d))) ;; (write-char #\e port) ;; (cond ;; ((positive? k) ;; (write-char #\+ port) ;; (write (- k 1) port)) ;; (else ;; (write k port)))) (define (scale r s m+ m- k f e) (let ((est (inexact->exact (ceiling (- (* (+ e (integer-length* f) -1) (invlog2of base)) 1.0e-10))))) (if (not (negative? est)) (fixup r (* s (fast-expt base est)) m+ m- est) (let ((skale (fast-expt base (- est)))) (fixup (* r skale) s (* m+ skale) (* m- skale) est))))) (define (fixup r s m+ m- k) (if (and (bigger (+ r m+) s)) ;; (or digits (>= k -4)) (lead r s m+ m- (+ k 1)) (lead (* r base) s (* m+ base) (* m- base) k))) (define (lead r s m+ m- k) (write-prefix prefix align k) (cond ((and (not digits) (or (> k 14) (< k -4))) (write n port)) ; XXXX native write for sci ;;((and (not digits) (> k 14)) ;; (generate-sci r s m+ m- k)) ;;((and (not digits) (< k -4)) ;; (if (>= (/ r s) base) ;; (generate-sci (/ r base) s (/ m+ base) (/ m- base) k) ;; (generate-sci r s m+ m- k))) (else (cond ((and (not digits) (or (negative? k) (and (zero? k) (not (integer? n))))) (write-digit 0) (display decimal-sep port) (let lp ((i 0)) (cond ((> i k) (write-digit 0) (lp (- i 1))))))) (if digits (generate-fixed r s m+ m- k) (generate-all r s m+ m- k))))) (define (generate-all r s m+ m- k) (let gen ((r r) (m+ m+) (m- m-) (i k)) (cond ((= i k)) ((zero? i) (output decimal-sep)) ((and commify? (positive? i) (zero? (modulo i comma-rule))) (output comma-sep))) (let ((d (quotient r s)) (r (remainder r s))) (if (not (smaller r m-)) (cond ((not (bigger (+ r m+) s)) (output d) (gen (* r base) (* m+ base) (* m- base) (- i 1))) (else (pad-all (+ d 1) i))) (if (not (bigger (+ r m+) s)) (pad-all d i) (pad-all (if (< (* r 2) s) d (+ d 1)) i)))))) (define (generate-fixed r s m+ m- k) (if (<= k 0) (set! stack (append (make-list (min (- k) digits) 0) (list decimal-sep 0)))) (let ((i0 (- (+ k digits) 1))) (let gen ((r r) (m+ m+) (m- m-) (i i0)) (cond ((= i i0)) ((= i (- digits 1)) (output decimal-sep)) ((and commify? (> i digits) (zero? (modulo (- i (- digits 1)) comma-rule))) (output comma-sep))) (let ((d (quotient r s)) (r (remainder r s))) (cond ((< i 0) (let ((d2 (* 2 (if (>= (* r 2) s) (+ d 1) d)))) (if (and (not (> (- k) digits)) (or (> d2 base) (and (= d2 base) (pair? stack) (number? (car stack)) (odd? (car stack))))) (flush/rounded) (flush)))) ((smaller r m-) (cond ((>= d base) (flush/rounded) (pad 0 i)) (else (flush) (if (bigger (+ r m+) s) (pad (if (< (* r 2) s) d (+ d 1)) i) (pad d i))))) ((bigger (+ r m+) s) (cond ((>= d (- base 1)) (flush/rounded) (pad 0 i)) (else (flush) (pad (+ d 1) i)))) (else (output d) (gen (* r base) (* m+ base) (* m- base) (- i 1)))))))) ;;(define (generate-sci r s m+ m- k) ;; (let gen ((r r) (m+ m+) (m- m-) (i k)) ;; (cond ((= i (- k 1)) (display decimal-sep port))) ;; (let ((d (quotient r s)) ;; (r (remainder r s))) ;; (if (not (smaller r m-)) ;; (cond ;; ((not (bigger (+ r m+) s)) ;; (output d) ;; (gen (* r base) (* m+ base) (* m- base) (- i 1))) ;; (else (pad-sci (+ d 1) i k))) ;; (if (not (bigger (+ r m+) s)) ;; (pad-sci d i k) ;; (pad-sci (if (< (* r 2) s) d (+ d 1)) i k)))))) (cond ((negative? e) (if (or (= e *min-e*) (not (= f *bot-f*))) (scale (* f 2) (* (expt 2.0 (- e)) 2) 1 1 0 f e) (scale (* f 2 2) (* (expt 2.0 (- 1 e)) 2) 2 1 0 f e))) (else (if (= f *bot-f*) (let ((be (expt 2 e))) (scale (* f be 2) 2.0 be be 0 f e)) (let* ((be (expt 2 e)) (be1 (* be 2))) (scale (* f be1 2) (* 2.0 2) be1 be 0 f e))))))) (define (write-fixed-rational p prefix align) (define (get-scale q) (expt base (- (integer-log q base) 1))) (let ((n (numerator p)) (d (denominator p)) (k (integer-log p base))) (write-prefix prefix align k) (let lp ((n n) (i (- k))) (cond ((< i digits) (if (zero? i) (output decimal-sep)) (let ((q (quotient n d))) (cond ((>= q base) (let* ((scale (get-scale q)) (digit (quotient q scale)) (n2 (- n (* d digit scale)))) (output digit) (lp n2 (+ i 1)))) (else (output q) (lp (* (remainder n d) base) (+ i 1)))))) (else (let* ((q (quotient n d)) (digit (* 2 (if (>= q base) (quotient q (get-scale q)) q)))) (if (or (> digit base) (and (= digit base) (let ((prev (find integer? stack))) (and prev (odd? prev))))) (flush/rounded) (flush)))))))) (define (wrap-sign n sign? align writer) (cond ((negative? n) (cond ((char? sign?) (writer (abs n) sign? align) (display (mirror-of sign?) port)) (else (writer (abs n) #\- align)))) (else (cond ((and sign? (not (char? sign?))) (writer n #\+ align)) (else (writer n #f align)))))) (let ((imag (imag-part n))) (cond ((and base (not (and (integer? base) (<= 2 base 36)))) (error "invalid base for numeric formatting" base)) ((zero? imag) (cond ((and (exact? n) (not (integer? n))) (cond (digits (wrap-sign n sign? align write-fixed-rational)) (else (wrap-sign (numerator n) sign? #f write-real) (write-char #\/ port) (wrap-sign (denominator n) #f #f write-real)))) (else (wrap-sign n sign? align write-real)))) (else (wrap-sign (real-part n) sign? #f write-real) (wrap-sign imag #t #f write-real) (write-char #\i port)))))))) (define (num n . opt) (lambda (st) ((fmt-writer st) (apply num->string n st opt) st))) (define (num/comma n . o) (lambda (st) (let-optionals* o ((base (fmt-radix st)) (digits (fmt-precision st)) (sign? #f) (comma-rule 3) (comma-sep (fmt-ref st 'comma-char #\,)) (decimal-sep (or (fmt-decimal-sep st) (if (eqv? comma-sep #\.) #\, #\.)))) ((num n base digits sign? comma-rule comma-sep decimal-sep) st)))) ;; SI suffix formatting, as used in --human-readable options to some ;; GNU commands (such as ls). See ;; ;; http://www.bipm.org/en/si/si_brochure/chapter3/prefixes.html ;; http://physics.nist.gov/cuu/Units/binary.html ;; ;; Note: lowercase "k" for base 10, uppercase "K" for base 2 (define num/si (let* ((names10 '#("" "k" "M" "G" "T" "E" "P" "Z" "Y")) (names2 (list->vector (cons "" (cons "Ki" (map (lambda (s) (string-append s "i")) (cddr (vector->list names10)))))))) (lambda (n . o) (let-optionals* o ((base 1024) (suffix "") (names (if (= base 1024) names2 names10))) (let* ((k (min (inexact->exact (floor (/ (log n) (log base)))) (vector-length names))) (n2 (/ (round (* (/ n (expt base k)) 10)) 10))) (cat (if (integer? n2) (number->string (inexact->exact n2)) (exact->inexact n2)) (vector-ref names k) (if (zero? k) "" suffix))))))) (define roman-numerals '((1000 . #\M) (500 . #\D) (100 . #\C) (50 . #\L) (10 . #\X) (5 . #\V) (1 . #\I))) (define (num/old-roman num) (lambda (st) (let lp ((num num) (res '())) (if (positive? num) (let ((ch (find (lambda (x) (>= num (car x))) roman-numerals))) (lp (- num (car ch)) (cons (cdr ch) res))) (fmt-write (reverse-list->string res) st))))) (define (num/roman num) (lambda (st) (let lp1 ((num num) (res '())) (if (positive? num) (let lp2 ((ls roman-numerals)) (let* ((big (car ls)) (big-n (car big))) (cond ((>= num big-n) (lp1 (- num big-n) (cons (cdr big) res))) ((and (> (* 2 num) big-n) (find (lambda (c) (let ((x (car c))) (<= (+ x 1) (- big-n x) num))) ls)) => (lambda (c) (lp1 (- num (- big-n (car c))) (cons (cdr big) (cons (cdr c) res))))) (else (lp2 (cdr ls)))))) (fmt-write (reverse-list->string res) st))))) ;; Force a number into a fixed width, print as #'s if doesn't fit. ;; Needs to be wrapped in a PAD if you want to expand to the width. (define (num/fit width n . args) (fmt-capture (apply num n args) (lambda (str) (lambda (st) (if (> (string-length str) width) (let ((prec (if (and (pair? args) (pair? (cdr args))) (cadr args) (fmt-precision st)))) (if prec (let* ((decimal-sep (or (fmt-ref st 'decimal-sep) (if (eqv? #\. (fmt-ref st 'comma-sep)) #\, #\.))) (diff (- width (+ prec (if (char? decimal-sep) 1 (string-length decimal-sep)))))) ((cat (if (positive? diff) (make-string diff #\#) "") decimal-sep (make-string prec #\#)) st)) ((fmt-writer st) (make-string width #\#) st))) ((fmt-writer st) str st)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; shared structure utilities (define (eq?-table-ref tab x) (hash-table-ref/default tab x #f)) (define (eq?-table-set! tab x v) (hash-table-set! tab x v)) ;; XXXX extend for records and other container data types (define (make-shared-ref-table obj) (let ((tab (make-eq?-table)) (res (make-eq?-table)) (index 0)) (let walk ((obj obj)) (cond ((eq?-table-ref tab obj) => (lambda (i) (eq?-table-set! tab obj (+ i 1)))) ((not (or (symbol? obj) (number? obj) (char? obj) (boolean? obj) (null? obj) (eof-object? obj))) (eq?-table-set! tab obj 1) (cond ((pair? obj) (walk (car obj)) (walk (cdr obj))) ((vector? obj) (let ((len (vector-length obj))) (do ((i 0 (+ i 1))) ((>= i len)) (walk (vector-ref obj i))))))))) (hash-table-walk tab (lambda (obj count) (if (> count 1) (begin (eq?-table-set! res obj (cons index #f)) (set! index (+ index 1)))))) res)) (define (gen-shared-ref i suffix) (string-append "#" (number->string i) suffix)) (define (maybe-gen-shared-ref st cell shares) (cond ((pair? cell) (set-car! cell (cdr shares)) (set-cdr! cell #t) (set-cdr! shares (+ (cdr shares) 1)) ((fmt-writer st) (gen-shared-ref (car cell) "=") st)) (else st))) (define (call-with-shared-ref obj st shares proc) (let ((cell (eq?-table-ref (car shares) obj))) (if (and (pair? cell) (cdr cell)) ((fmt-writer st) (gen-shared-ref (car cell) "#") st) (proc (maybe-gen-shared-ref st cell shares))))) (define (call-with-shared-ref/cdr obj st shares proc sep) (let ((cell (eq?-table-ref (car shares) obj)) (output (fmt-writer st))) (cond ((and (pair? cell) (cdr cell)) (output (gen-shared-ref (car cell) "#") (output ". " (sep st)))) ((pair? cell) (let ((st (maybe-gen-shared-ref (output ". " (sep st)) cell shares))) (output ")" (proc (output "(" st))))) (else (proc (sep st)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; sexp formatters (define (slashified str . o) (let-optionals* o ((quot #\") (esc #\\) (rename (lambda (x) #f))) (lambda (st) (let* ((len (string-length str)) (output (fmt-writer st)) (quot-str (string quot)) (esc-str (if (char? esc) (string esc) (or esc quot-str)))) (let lp ((i 0) (j 0) (st st)) (define (collect) (if (= i j) st (output (substring/shared str i j) st))) (if (>= j len) (collect) (let ((c (string-ref str j))) (cond ((or (eqv? c quot) (eqv? c esc)) (lp j (+ j 1) (output esc-str (collect)))) ((rename c) => (lambda (c2) (lp (+ j 1) (+ j 1) (output c2 (output esc-str (collect)))))) (else (lp i (+ j 1) st)))))))))) ;; Only slashify if there are special characters, in which case also ;; wrap in quotes. For writing symbols in |...| escapes, or CSV ;; fields, etc. The predicate indicates which characters cause ;; slashification - this is in addition to automatic slashifying when ;; either the quote or escape char is present. (define (maybe-slashified str pred . o) (let-optionals* o ((quot #\") (esc #\\) (rename (lambda (x) #f))) (define (esc? c) (or (eqv? c quot) (eqv? c esc) (rename c) (pred c))) (if (string-index str esc?) (cat quot (slashified str quot esc rename) quot) (dsp str)))) (define (fmt-write-string str) (define (rename c) (case c ((#\newline) "n") (else #f))) (slashified str #\" #\\ rename)) (define (dsp obj) (cond ((procedure? obj) obj) ((string? obj) (lambda (st) ((fmt-writer st) obj st))) ((char? obj) (dsp (string obj))) (else (wrt obj)))) (define (write-with-shares obj shares) (lambda (st) (let* ((output (fmt-writer st)) (wr-num (cond ((and (= 10 (fmt-radix st)) (not (fmt-precision st)) (not (fmt-decimal-align st))) (lambda (n st) (output (number->string n) st))) ((assv (fmt-radix st) '((16 . "#x") (10 . "") (8 . "#o") (2 . "#b"))) => (lambda (cell) (let ((prefix (cdr cell))) (lambda (n st) ((num n) (output prefix st)))))) (else (lambda (n st) (output (number->string n) st)))))) (let wr ((obj obj) (st st)) (call-with-shared-ref obj st shares (lambda (st) (cond ((pair? obj) (output ")" (let lp ((ls obj) (st (output "(" st))) (let ((st (wr (car ls) st)) (rest (cdr ls))) (cond ((null? rest) st) ((pair? rest) (call-with-shared-ref/cdr rest st shares (lambda (st) (lp rest st)) (dsp " "))) (else (wr rest (output " . " st)))))))) ((vector? obj) (let ((len (vector-length obj))) (if (zero? len) (output "#()" st) (let lp ((i 1) (st (wr (vector-ref obj 0) (output "#(" st)))) (if (>= i len) (output ")" st) (lp (+ i 1) (wr (vector-ref obj i) (output " " st)))))))) ((string? obj) (output "\"" ((fmt-write-string obj) (output "\"" st)))) ((number? obj) (wr-num obj st)) ((boolean? obj) (output (if obj "#t" "#f") st)) (else (output (write-to-string obj) st))))))))) (define (wrt obj) (write-with-shares obj (cons (make-shared-ref-table obj) 0))) ;; the only expensive part, in both time and memory, of handling ;; shared structures when writing is building the initial table, so ;; for the efficient version we just skip that (define (wrt/unshared obj) (write-with-shares obj (cons (make-eq?-table) 0))) |
Added fmt/fmt.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
;;;; fmt.scm -- extensible formatting library ;; ;; Copyright (c) 2006-2009 Alex Shinn. All rights reserved. ;; BSD-style license: http://synthcode.com/license.txt #!r6rs (library (fmt fmt) (export new-fmt-state fmt fmt-start fmt-if fmt-capture fmt-let fmt-bind fmt-null fmt-ref fmt-set! fmt-add-properties! fmt-set-property! fmt-col fmt-set-col! fmt-row fmt-set-row! fmt-radix fmt-set-radix! fmt-precision fmt-set-precision! fmt-properties fmt-set-properties! fmt-width fmt-set-width! fmt-writer fmt-set-writer! fmt-port fmt-set-port! fmt-decimal-sep fmt-set-decimal-sep! fmt-file fmt-try-fit cat apply-cat nl fl nl-str fmt-join fmt-join/last fmt-join/dot fmt-join/prefix fmt-join/suffix fmt-join/range pad pad/right pad/left pad/both trim trim/left trim/both trim/length fit fit/left fit/both tab-to space-to wrt wrt/unshared dsp pretty pretty/unshared slashified maybe-slashified num num/si num/fit num/comma radix fix decimal-align ellipses upcase downcase titlecase pad-char comma-char decimal-char with-width wrap-lines fold-lines justify make-string-fmt-transformer make-space make-nl-space display-to-string write-to-string fmt-columns columnar tabular line-numbers) (import (chezscheme) (only (srfi s13 strings) string-count string-index string-index-right string-concatenate string-concatenate-reverse substring/shared reverse-list->string string-tokenize string-suffix? string-prefix?) (srfi private let-opt) (only (srfi s1 lists) fold length+)) (include "hash-compat.scm") (include "mantissa.scm") (include "read-line.scm") (include "string-ports.scm") (include "fmt.scm") (include "fmt-column.scm") (include "fmt-pretty.scm") ) |
Added fmt/hash-compat.scm.
> > > > > |
1 2 3 4 5 |
(define (make-eq?-table) (make-eq-hashtable)) (define hash-table-ref/default hashtable-ref) (define hash-table-set! hashtable-set!) (define hash-table-walk hash-table-for-each) |
Added fmt/js.sls.
> > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
;;;; fmt-js.scm -- javascript formatting utilities ;; ;; Copyright (c) 2011-2012 Alex Shinn. All rights reserved. ;; BSD-style license: http://synthcode.com/license.txt #!r6rs (library (fmt js) (export js-expr js-function js-var js-comment js-array js-object js=== js>>>) (import (chezscheme) (fmt fmt) (fmt c)) (include "fmt-js.scm") ) |
Added fmt/mantissa.scm.
> > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
;; Break a positive real number down to a normalized mantissa and ;; exponent. Default base=2, mant-size=52, exp-size=11 for IEEE doubles. (define mantissa+exponent (case-lambda [(num) (mantissa+exponent num 2)] [(num base) (mantissa+exponent num base 52)] [(num base mant-size) (mantissa+exponent num base mant-size 11)] [(num base mant-size exp-size) (if (zero? num) (list 0 0) (let* ((bot (expt base mant-size)) (top (* base bot))) (let lp ((n num) (e 0)) (cond ((>= n top) (lp (quotient n base) (+ e 1))) ((< n bot) (lp (* n base) (- e 1))) (else (list n e))))))])) |
Added fmt/read-line.scm.
> > > > > > > > |
1 2 3 4 5 6 7 8 |
(define (read-line . o) (let ((port (if (pair? o) (car o) (current-input-port)))) (let lp ((res '())) (let ((c (read-char port))) (if (or (eof-object? c) (eqv? c #\newline)) (list->string (reverse res)) (lp (cons c res))))))) |
Added fmt/string-ports.scm.
> > > > > |
1 2 3 4 5 |
(define (call-with-output-string f) (let ((port (open-output-string))) (let () (f port)) (get-output-string port))) |
Added fmt/test-fmt-c.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 |
(cond-expand (chicken (use test) (load "fmt-c-chicken.scm")) (gauche (use gauche.test) (use text.fmt) (use text.fmt.c) (define test-begin test-start) (define orig-test (with-module gauche.test test)) (define-syntax test (syntax-rules () ((test name expected expr) (orig-test name expected (lambda () expr))) ((test expected expr) (orig-test (let ((s (with-output-to-string (lambda () (write 'expr))))) (substring s 0 (min 60 (string-length s)))) expected (lambda () expr))) ))) (else)) (cond-expand (chicken (import fmt fmt-c)) (else)) (test-begin "fmt-c") (test "if (1) { 2; } else { 3; } " (fmt #f (c-if 1 2 3))) (test "if (x ? y : z) { 2; } else { 3; } " (fmt #f (c-if (c-if 'x 'y 'z) 2 3))) (test "if (x ? y : z) { 2; } else { 3; } " (fmt #f (c-expr '(if (if x y z) 2 3)))) (test "if (x ? y : z) { 2; } else { 3; } " (fmt #f (c-expr '(%begin (if (if x y z) 2 3))))) (test "int square (int x) { return x * x; } " (fmt #f (c-fun 'int 'square '((int x)) (c* 'x 'x)))) (test "int foo (int x, int y, int z) { if (x ? y : z) { return 2; } else { return 3; } } " (fmt #f (c-fun 'int 'foo '((int x) (int y) (int z)) (c-if (c-if 'x 'y 'z) 2 3)))) (test "void bar (int mode, const char *msg, unsigned int arg) { if (mode == 1) { printf(msg); } else { printf(msg, arg); } } " (fmt #f (c-fun 'void 'bar '((int mode) ((%pointer (const char)) msg) ((unsigned int) arg)) (c-if (c== 'mode 1) '(printf msg) '(printf msg arg))))) (test "while ((line = readline()) != EOF) { printf(\"%s\", line); } " (fmt #f (c-while (c!= (c= 'line '(readline)) 'EOF) '(printf "%s" line)))) (test "switch (y) { case 1: x = 1; break; case 2: x = 4; break; default: x = 5; break; } " (fmt #f (c-switch 'y (c-case 1 (c= 'x 1)) (c-case 2 (c= 'x 4)) (c-default (c= 'x 5))))) (test "switch (y) { case 1: x = 1; break; case 2: x = 4; default: x = 5; break; } " (fmt #f (c-switch 'y (c-case 1 (c= 'x 1)) (c-case/fallthrough 2 (c= 'x 4)) (c-default (c= 'x 5))))) (test "switch (y) { case 1: x = 1; break; case 2: x = 4; break; default: x = 5; break; } " (fmt #f (c-switch 'y '((1) (= x 1)) '((2) (= x 4)) '(else (= x 5))))) (test "switch (y) { case 1: x = 1; break; case 2: x = 4; break; default: x = 5; break; } " (fmt #f (c-expr '(switch y ((1) (= x 1)) ((2) (= x 4)) (else (= x 5)))))) (test "int q (int x) { switch (x) { case 1: return 1; case 2: return 4; default: return 5; } } " (fmt #f (c-fun 'int 'q '(x) (c-switch 'x '((1) 1) '((2) 4) '(else 5))))) (test "switch (x) { case 1: case 2: foo(); break; default: bar(); break; } " (fmt #f (c-expr '(switch x ((1 2) (foo)) (else (bar)))))) (test "switch (x) { case 1: foo(); break; case 2: case 3: bar(); break; default: baz(); break; } " (fmt #f (c-expr '(switch x (case 1 (foo)) (case (2 3) (bar)) (else (baz)))))) (test "switch (x) { case 1: case 2: foo(); default: bar(); break; } " (fmt #f (c-expr '(switch x (case/fallthrough (1 2) (foo)) (else (bar)))))) (test "switch (x) { case 1: case 2: foo(); break; default: bar(); break; } " (fmt #f (c-expr '(switch x ((1 2) (foo)) (default (bar)))))) (test "switch (x) { default: bar(); case 1: case 2: foo(); break; } " (fmt #f (c-expr '(switch x (else/fallthrough (bar)) ((1 2) (foo)))))) (test "for (i = 0; i < n; i++) { printf(\"i: %d\"); } " (fmt #f (c-for (c= 'i 0) (c< 'i 'n) (c++/post 'i) '(printf "i: %d")))) (test "a * x + b * y == c;\n" (fmt #f (c== (c+ (c* 'a 'x) (c* 'b 'y)) 'c))) (test "a * x + b * y == c;\n" (fmt #f (c-expr '(== (+ (* a x) (* b y)) c)))) (test "(a + x) * (b + y) == c;\n" (fmt #f (c-expr '(== (* (+ a x) (+ b y)) c)))) (test "(abracadabra!!!! + xylophone????) * (bananarama____ + yellowstonepark~~~~) * (cryptoanalysis + zebramania);\n" (fmt #f (c-expr '(* (+ abracadabra!!!! xylophone????) (+ bananarama____ yellowstonepark~~~~) (+ cryptoanalysis zebramania))))) (test "abracadabra(xylophone, bananarama, yellowstonepark, cryptoanalysis, zebramania, delightful, wubbleflubbery);\n" (fmt #f (c-expr '(abracadabra xylophone bananarama yellowstonepark cryptoanalysis zebramania delightful wubbleflubbery)))) (test "#define foo(x, y) (((x) + (y)))\n" (fmt #f (cpp-define '(foo (int x) (int y)) (c+ 'x 'y)))) (test "#define min(x, y) (((x) < (y)) ? (x) : (y))\n" (fmt #f (cpp-define '(min x y) (c-if (c< 'x 'y) 'x 'y)))) (test "#define foo(x, y) (abracadabra(((x) + (y)), \\ xylophone, \\ bananarama, \\ yellowstonepark, \\ cryptoanalysis, \\ zebramania, \\ delightful, \\ wubbleflubbery))\n" (fmt #f (cpp-define '(foo x y) '(abracadabra (+ x y) xylophone bananarama yellowstonepark cryptoanalysis zebramania delightful wubbleflubbery)))) (test "#ifndef FOO_H #define FOO_H extern int foo (); #endif /* ! FOO_H */ " (fmt #f (cpp-wrap-header 'FOO_H (c-extern (c-prototype 'int 'foo '()))))) (test "#if foo 1 #elif bar 2 #elif baz 3 #else 4 #endif " (fmt #f (cpp-if 'foo 1 'bar 2 'baz 3 4))) (test "/* this is a /\\* nested *\\/ comment */" (fmt #f (c-comment " this is a " (c-comment " nested ") " comment "))) ;; the initial leading space is annoying but hard to remove at the ;; moment - the important thing is we preserve indentation in the body (test "switch (y) { case 1: x = 1; break; #ifdef H_TWO case 2: x = 4; break; #endif /* H_TWO */ default: x = 5; break; } " (fmt #f (c-expr `(switch y ((1) (= x 1)) ,(cpp-ifdef 'H_TWO (c-case '(2) '(= x 4))) (else (= x 5)))))) (test "#define eprintf(...) (fprintf(stderr, __VA_ARGS__))\n" (fmt #f (c-expr '(%define (eprintf . args) (fprintf stderr args))))) (test "struct point { int x; int y; }; " (fmt #f (c-expr `(struct point (x y))))) (test "struct employee { short age; char *name; struct { int year; int month; int day; } dob; } __attribute__ ((packed)); " (fmt #f (c-expr `(struct employee ((short age) ((%pointer char) name) ((struct (year month day)) dob)) (%attribute packed) )))) (test "class employee { short age; char *name; struct { int year; int month; int day; } dob; } __attribute__ ((packed)); " (fmt #f (c-class 'employee '((short age) ((%pointer char) name) ((struct (year month day)) dob)) (c-attribute 'packed) ))) (test "union object { char tag; struct { char tag; char *data; } string; struct { char tag; void *car; void *cdr; } pair; struct { char tag; unsigned int length; void *data; } vector; }; " (fmt #f (c-expr '(union object ((char tag) ((struct ((char tag) ((* char) data))) string) ((struct ((char tag) ((* void) car) ((* void) cdr))) pair) ((struct ((char tag) ((unsigned int) length) ((* void) data))) vector) ))))) (test "enum type_tags { TYPE_CHAR = 1, TYPE_FIXNUM, TYPE_BOOLEAN, TYPE_NULL, TYPE_EOF, TYPE_STRING, TYPE_PAIR, TYPE_VECTOR }; " (fmt #f (c-expr '(enum type_tags ((TYPE_CHAR 1) TYPE_FIXNUM TYPE_BOOLEAN TYPE_NULL TYPE_EOF TYPE_STRING TYPE_PAIR TYPE_VECTOR))))) (test "#define OP_EVAL 0xFE\n" (fmt #f (radix 16 (cpp-define 'OP_EVAL 254)))) (test "unsigned long table[SIZE] = {1, 2, 3, 4};\n" (fmt #f (c-var '(%array (unsigned long) SIZE) 'table '#(1 2 3 4)))) (test "int *array_of_ptr[];\n" (fmt #f (c-var '(%array (* int)) 'array_of_ptr))) (test "int (*ptr_to_array)[];\n" (fmt #f (c-var '(* (%array int)) 'ptr_to_array))) (test "foo **table = {{1, \"foo\"}, {2, \"bar\"}, {3, \"baz\"}, {4, \"qux\"}};\n" (fmt #f (c-var '(* (* foo)) 'table '#(#(1 "foo") #(2 "bar") #(3 "baz") #(4 "qux"))))) (test "sexp (*f)(sexp, sexp) = NULL;\n" (fmt #f (c-var '(%fun sexp (sexp sexp)) 'f 'NULL))) (test "sexp (*)(sexp) (*f)(sexp, sexp) = NULL;\n" (fmt #f (c-var '(%fun (%fun sexp (sexp)) (sexp sexp)) 'f 'NULL))) (test "typedef double (*f)(double *, double, int);\n" (fmt #f (c-typedef '(%fun double ((* double) double int)) 'f))) (test "\"foo\\tbar\";\n" (fmt #f (c-expr "foo\tbar"))) (test-end) |
Added fmt/test-fmt-js.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
(cond-expand (chicken (load "fmt-js-chicken.scm")) (else)) (cond-expand (chicken (use test) (import fmt) (import fmt-js)) (gauche (use gauche.test) (use text.fmt) (use text.fmt.js) (define test-begin test-start) (define orig-test (with-module gauche.test test)) (define-syntax test (syntax-rules () ((test name expected expr) (orig-test name expected (lambda () expr))) ((test expected expr) (orig-test (let ((s (with-output-to-string (lambda () (write 'expr))))) (substring s 0 (min 60 (string-length s)))) expected (lambda () expr))) ))) (else)) (test-begin "fmt-js") (test "var foo = 1 + 2;\n" (fmt #f (js-expr '(%var foo (+ 1 2))))) (test "var foo = 1 + 2;\n" (fmt #f (js-expr '(%begin (%var foo (+ 1 2)))))) (test "function square(x) { return x * x; }" (fmt #f (js-function 'square '(x) '(* x x)))) (test "{\"foo\": [1, 2, 3], \"bar\": \"baz\"}" (fmt #f (js-expr '(%object ("foo" . #(1 2 3)) ("bar" . "baz"))))) (test-end) |
Added fmt/test-fmt.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 |
(cond-expand (chicken (load "fmt-chicken.scm")) (else)) (cond-expand (chicken (use test) (import fmt)) (gauche (use gauche.test) (use text.fmt) (define test-begin test-start) (define orig-test (with-module gauche.test test)) (define-syntax test (syntax-rules () ((test name expected expr) (guard (e (else #f)) (orig-test name expected (lambda () expr)))) ((test expected expr) (test (let ((s (with-output-to-string (lambda () (write 'expr))))) (substring s 0 (min 60 (string-length s)))) expected expr))))) (else)) (test-begin "fmt") ;; basic data types (test "hi" (fmt #f "hi")) (test "\"hi\"" (fmt #f (wrt "hi"))) (test "\"hi \\\"bob\\\"\"" (fmt #f (wrt "hi \"bob\""))) (test "\"hello\\nworld\"" (fmt #f (wrt "hello\nworld"))) (test "ABC" (fmt #f (upcase "abc"))) (test "abc" (fmt #f (downcase "ABC"))) (test "Abc" (fmt #f (titlecase "abc"))) (test "abc def" (fmt #f "abc" (tab-to) "def")) (test "abc def" (fmt #f "abc" (tab-to 5) "def")) (test "abcdef" (fmt #f "abc" (tab-to 3) "def")) (test "-1" (fmt #f -1)) (test "0" (fmt #f 0)) (test "1" (fmt #f 1)) (test "10" (fmt #f 10)) (test "100" (fmt #f 100)) (test "-1" (fmt #f (num -1))) (test "0" (fmt #f (num 0))) (test "1" (fmt #f (num 1))) (test "10" (fmt #f (num 10))) (test "100" (fmt #f (num 100))) ;; (test "1e+15" (fmt #f (num 1e+15))) ;; (test "1e+23" (fmt #f (num 1e+23))) ;; (test "1.2e+23" (fmt #f (num 1.2e+23))) ;; (test "1e-5" (fmt #f (num 1e-5))) ;; (test "1e-6" (fmt #f (num 1e-6))) ;; (test "1e-7" (fmt #f (num 1e-7))) ;; (test "2e-6" (fmt #f (num 2e-6))) (test "57005" (fmt #f #xDEAD)) (test "#xDEAD" (fmt #f (radix 16 #xDEAD))) (test "#xDEAD1234" (fmt #f (radix 16 #xDEAD) 1234)) (test "#xDE.AD" (fmt #f (radix 16 (exact->inexact (/ #xDEAD #x100))))) (test "#xD.EAD" (fmt #f (radix 16 (exact->inexact (/ #xDEAD #x1000))))) (test "#x0.DEAD" (fmt #f (radix 16 (exact->inexact (/ #xDEAD #x10000))))) (test "1G" (fmt #f (radix 17 (num 33)))) (test "1G" (fmt #f (num 33 17))) (test "3.14159" (fmt #f 3.14159)) (test "3.14" (fmt #f (fix 2 3.14159))) (test "3.14" (fmt #f (fix 2 3.14))) (test "3.00" (fmt #f (fix 2 3.))) (test "1.10" (fmt #f (num 1.099 10 2))) (test "0.00" (fmt #f (fix 2 1e-17))) (test "0.0000000000" (fmt #f (fix 10 1e-17))) (test "0.00000000000000001000" (fmt #f (fix 20 1e-17))) ;; (test-error (fmt #f (num 1e-17 0))) (test "0.000004" (fmt #f (num 0.000004 10 6))) (test "0.0000040" (fmt #f (num 0.000004 10 7))) (test "0.00000400" (fmt #f (num 0.000004 10 8))) ;; (test "0.000004" (fmt #f (num 0.000004))) (test " 3.14159" (fmt #f (decimal-align 5 (num 3.14159)))) (test " 31.4159" (fmt #f (decimal-align 5 (num 31.4159)))) (test " 314.159" (fmt #f (decimal-align 5 (num 314.159)))) (test "3141.59" (fmt #f (decimal-align 5 (num 3141.59)))) (test "31415.9" (fmt #f (decimal-align 5 (num 31415.9)))) (test " -3.14159" (fmt #f (decimal-align 5 (num -3.14159)))) (test " -31.4159" (fmt #f (decimal-align 5 (num -31.4159)))) (test "-314.159" (fmt #f (decimal-align 5 (num -314.159)))) (test "-3141.59" (fmt #f (decimal-align 5 (num -3141.59)))) (test "-31415.9" (fmt #f (decimal-align 5 (num -31415.9)))) (cond ((exact? (/ 1 3)) ;; exact rationals (test "333.333333333333333333333333333333" (fmt #f (fix 30 1000/3))) (test "33.333333333333333333333333333333" (fmt #f (fix 30 100/3))) (test "3.333333333333333333333333333333" (fmt #f (fix 30 10/3))) (test "0.333333333333333333333333333333" (fmt #f (fix 30 1/3))) (test "0.033333333333333333333333333333" (fmt #f (fix 30 1/30))) (test "0.003333333333333333333333333333" (fmt #f (fix 30 1/300))) (test "0.000333333333333333333333333333" (fmt #f (fix 30 1/3000))) (test "0.666666666666666666666666666667" (fmt #f (fix 30 2/3))) (test "0.090909090909090909090909090909" (fmt #f (fix 30 1/11))) (test "1.428571428571428571428571428571" (fmt #f (fix 30 10/7))) (test "0.123456789012345678901234567890" (fmt #f (fix 30 (/ 123456789012345678901234567890 1000000000000000000000000000000)))) (test " 333.333333333333333333333333333333" (fmt #f (decimal-align 5 (fix 30 1000/3)))) (test " 33.333333333333333333333333333333" (fmt #f (decimal-align 5 (fix 30 100/3)))) (test " 3.333333333333333333333333333333" (fmt #f (decimal-align 5 (fix 30 10/3)))) (test " 0.333333333333333333333333333333" (fmt #f (decimal-align 5 (fix 30 1/3)))) )) (test "11.75" (fmt #f (num (/ 47 4) 10 2))) (test "-11.75" (fmt #f (num (/ -47 4) 10 2))) (test "(#x11 #x22 #x33)" (fmt #f (radix 16 '(#x11 #x22 #x33)))) (test "299,792,458" (fmt #f (num 299792458 10 #f #f #t))) (test "299,792,458" (fmt #f (num/comma 299792458))) (test "299.792.458" (fmt #f (comma-char #\. (num/comma 299792458)))) (test "299.792.458,0" (fmt #f (comma-char #\. (num/comma 299792458.0)))) (test "100,000" (fmt #f (num 100000 10 0 #f 3))) (test "100,000.0" (fmt #f (num 100000 10 1 #f 3))) (test "100,000.00" (fmt #f (num 100000 10 2 #f 3))) (test "1.23" (fmt #f (fix 2 (num/fit 4 1.2345)))) (test "1.00" (fmt #f (fix 2 (num/fit 4 1)))) (test "#.##" (fmt #f (fix 2 (num/fit 4 12.345)))) ;; (cond ;; ((feature? 'full-numeric-tower) ;; (test "1+2i" (fmt #f (string->number "1+2i"))) ;; (test "1+2i" (fmt #f (num (string->number "1+2i")))) ;; (test "1.00+2.00i" (fmt #f (fix 2 (num (string->number "1+2i"))))) ;; (test "3.14+2.00i" (fmt #f (fix 2 (num (string->number "3.14159+2i"))))))) (test "3.9Ki" (fmt #f (num/si 3986))) (test "4k" (fmt #f (num/si 3986 1000))) (test "608" (fmt #f (num/si 608))) (test "3G" (fmt #f (num/si 12345.12355 16))) ;; padding/trimming (test "abc " (fmt #f (pad 5 "abc"))) (test " abc" (fmt #f (pad/left 5 "abc"))) (test " abc " (fmt #f (pad/both 5 "abc"))) (test "abcde" (fmt #f (pad 5 "abcde"))) (test "abcdef" (fmt #f (pad 5 "abcdef"))) (test "abc" (fmt #f (trim 3 "abcde"))) (test "abc" (fmt #f (trim/length 3 "abcde"))) (test "abc" (fmt #f (trim/length 3 "abc\nde"))) (test "cde" (fmt #f (trim/left 3 "abcde"))) (test "bcd" (fmt #f (trim/both 3 "abcde"))) (test "prefix: abc" (fmt #f "prefix: " (trim 3 "abcde"))) (test "prefix: abc" (fmt #f "prefix: " (trim/length 3 "abcde"))) (test "prefix: abc" (fmt #f "prefix: " (trim/length 3 "abc\nde"))) (test "prefix: cde" (fmt #f "prefix: " (trim/left 3 "abcde"))) (test "prefix: bcd" (fmt #f "prefix: " (trim/both 3 "abcde"))) (test "abcde" (fmt #f (ellipses "..." (trim 5 "abcde")))) (test "ab..." (fmt #f (ellipses "..." (trim 5 "abcdef")))) (test "abc..." (fmt #f (ellipses "..." (trim 6 "abcdefg")))) (test "abcde" (fmt #f (ellipses "..." (trim/left 5 "abcde")))) (test "...ef" (fmt #f (ellipses "..." (trim/left 5 "abcdef")))) (test "...efg" (fmt #f (ellipses "..." (trim/left 6 "abcdefg")))) (test "abcdefg" (fmt #f (ellipses "..." (trim/both 7 "abcdefg")))) (test "...d..." (fmt #f (ellipses "..." (trim/both 7 "abcdefgh")))) (test "...e..." (fmt #f (ellipses "..." (trim/both 7 "abcdefghi")))) (test "abc " (fmt #f (fit 5 "abc"))) (test " abc" (fmt #f (fit/left 5 "abc"))) (test " abc " (fmt #f (fit/both 5 "abc"))) (test "abcde" (fmt #f (fit 5 "abcde"))) (test "abcde" (fmt #f (fit/left 5 "abcde"))) (test "abcde" (fmt #f (fit/both 5 "abcde"))) (test "abcde" (fmt #f (fit 5 "abcdefgh"))) (test "defgh" (fmt #f (fit/left 5 "abcdefgh"))) (test "cdefg" (fmt #f (fit/both 5 "abcdefgh"))) (test "prefix: abc " (fmt #f "prefix: " (fit 5 "abc"))) (test "prefix: abc" (fmt #f "prefix: " (fit/left 5 "abc"))) (test "prefix: abc " (fmt #f "prefix: " (fit/both 5 "abc"))) (test "prefix: abcde" (fmt #f "prefix: " (fit 5 "abcde"))) (test "prefix: abcde" (fmt #f "prefix: " (fit/left 5 "abcde"))) (test "prefix: abcde" (fmt #f "prefix: " (fit/both 5 "abcde"))) (test "prefix: abcde" (fmt #f "prefix: " (fit 5 "abcdefgh"))) (test "prefix: defgh" (fmt #f "prefix: " (fit/left 5 "abcdefgh"))) (test "prefix: cdefg" (fmt #f "prefix: " (fit/both 5 "abcdefgh"))) (test "abc\n123\n" (fmt #f (fmt-join/suffix (cut trim 3 <>) (string-split "abcdef\n123456\n" "\n") nl))) ;; utilities (test "1 2 3" (fmt #f (fmt-join dsp '(1 2 3) " "))) ;; shared structures (test "#0=(1 . #0#)" (fmt #f (wrt (let ((ones (list 1))) (set-cdr! ones ones) ones)))) (test "(0 . #0=(1 . #0#))" (fmt #f (wrt (let ((ones (list 1))) (set-cdr! ones ones) (cons 0 ones))))) (test "(sym . #0=(sym . #0#))" (fmt #f (wrt (let ((syms (list 'sym))) (set-cdr! syms syms) (cons 'sym syms))))) (test "(#0=(1 . #0#) #1=(2 . #1#))" (fmt #f (wrt (let ((ones (list 1)) (twos (list 2))) (set-cdr! ones ones) (set-cdr! twos twos) (list ones twos))))) ;; without shared detection (test "(1 1 1 1 1" (fmt #f (trim/length 10 (wrt/unshared (let ((ones (list 1))) (set-cdr! ones ones) ones))))) (test "(1 1 1 1 1 " (fmt #f (trim/length 11 (wrt/unshared (let ((ones (list 1))) (set-cdr! ones ones) ones))))) ;; pretty printing ;; (define-macro (test-pretty str) ;; (let ((sexp (with-input-from-string str read))) ;; `(test ,str (fmt #f (pretty ',sexp))))) (define-syntax test-pretty (syntax-rules () ((test-pretty str) (let ((sexp (with-input-from-string str read))) (test str (fmt #f (pretty sexp))))))) (test-pretty "(foo bar)\n") (test-pretty "((self . aquanet-paper-1991) (type . paper) (title . \"Aquanet: a hypertext tool to hold your\")) ") (test-pretty "(abracadabra xylophone bananarama yellowstonepark cryptoanalysis zebramania delightful wubbleflubbery)\n") (test-pretty "#(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37)\n") (test-pretty "(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37)\n") (test-pretty "(define (fold kons knil ls) (define (loop ls acc) (if (null? ls) acc (loop (cdr ls) (kons (car ls) acc)))) (loop ls knil))\n") (test-pretty "(do ((vec (make-vector 5)) (i 0 (+ i 1))) ((= i 5) vec) (vector-set! vec i i))\n") (test-pretty "(do ((vec (make-vector 5)) (i 0 (+ i 1))) ((= i 5) vec) (vector-set! vec i 'supercalifrajalisticexpialidocious))\n") (test-pretty "(do ((my-vector (make-vector 5)) (index 0 (+ index 1))) ((= index 5) my-vector) (vector-set! my-vector index index))\n") (test-pretty "(define (fold kons knil ls) (let loop ((ls ls) (acc knil)) (if (null? ls) acc (loop (cdr ls) (kons (car ls) acc)))))\n") (test-pretty "(define (file->sexp-list pathname) (call-with-input-file pathname (lambda (port) (let loop ((res '())) (let ((line (read port))) (if (eof-object? line) (reverse res) (loop (cons line res))))))))\n") (test "(let ((ones '#0=(1 . #0#))) ones)\n" (fmt #f (pretty (let ((ones (list 1))) (set-cdr! ones ones) `(let ((ones ',ones)) ones))))) '(test "(let ((zeros '(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) (ones '#0=(1 . #0#))) (append zeros ones))\n" (fmt #f (pretty (let ((ones (list 1))) (set-cdr! ones ones) `(let ((zeros '(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) (ones ',ones)) (append zeros ones)))))) ;; slashify (test "\"note\",\"very simple\",\"csv\",\"writer\",\"\"\"yay!\"\"\"" (fmt #f (fmt-join (lambda (x) (cat "\"" (slashified x #\" #f) "\"")) '("note" "very simple" "csv" "writer" "\"yay!\"") ","))) (test "note,\"very simple\",csv,writer,\"\"\"yay!\"\"\"" (fmt #f (fmt-join (cut maybe-slashified <> char-whitespace? #\" #f) '("note" "very simple" "csv" "writer" "\"yay!\"") ","))) ;; columnar formatting (test "abc\ndef\n" (fmt #f (fmt-columns (list dsp "abc\ndef\n")))) (test "abc123\ndef456\n" (fmt #f (fmt-columns (list dsp "abc\ndef\n") (list dsp "123\n456\n")))) (test "abc123\ndef456\n" (fmt #f (fmt-columns (list dsp "abc\ndef\n") (list dsp "123\n456")))) (test "abc123\ndef456\n" (fmt #f (fmt-columns (list dsp "abc\ndef") (list dsp "123\n456\n")))) (test "abc123\ndef456\nghi789\n" (fmt #f (fmt-columns (list dsp "abc\ndef\nghi\n") (list dsp "123\n456\n789\n")))) (test "abc123wuv\ndef456xyz\n" (fmt #f (fmt-columns (list dsp "abc\ndef\n") (list dsp "123\n456\n") (list dsp "wuv\nxyz\n")))) (test "abc 123\ndef 456\n" (fmt #f (fmt-columns (list (cut pad/right 5 <>) "abc\ndef\n") (list dsp "123\n456\n")))) (test "ABC 123\nDEF 456\n" (fmt #f (fmt-columns (list (compose upcase (cut pad/right 5 <>)) "abc\ndef\n") (list dsp "123\n456\n")))) (test "ABC 123\nDEF 456\n" (fmt #f (fmt-columns (list (compose (cut pad/right 5 <>) upcase) "abc\ndef\n") (list dsp "123\n456\n")))) (test "hello\nworld\n" (fmt #f (with-width 8 (wrap-lines "hello world")))) (test "\n" (fmt #f (wrap-lines " "))) (test ;; test divide by zero error "The quick brown fox jumped over the lazy dog " (fmt #f (with-width 10 (justify "The quick brown fox jumped over the lazy dog")))) (test "his message (http://lists.nongnu.org/archive/html/chicken-users/2010-10/msg00171.html) to the chicken-users (http://lists.nongnu.org/mailman/listinfo/chicken-users)\n" (fmt #f (with-width 67 (wrap-lines "his message (http://lists.nongnu.org/archive/html/chicken-users/2010-10/msg00171.html) to the chicken-users (http://lists.nongnu.org/mailman/listinfo/chicken-users)")))) (test "The fundamental list iterator. Applies KONS to each element of LS and the result of the previous application, beginning with KNIL. With KONS as CONS and KNIL as '(), equivalent to REVERSE. " (fmt #f (with-width 36 (wrap-lines "The fundamental list iterator. Applies KONS to each element of LS and the result of the previous application, beginning with KNIL. With KONS as CONS and KNIL as '(), equivalent to REVERSE.")))) (test "The fundamental list iterator. Applies KONS to each element of LS and the result of the previous application, beginning with KNIL. With KONS as CONS and KNIL as '(), equivalent to REVERSE. " (fmt #f (with-width 36 (justify "The fundamental list iterator. Applies KONS to each element of LS and the result of the previous application, beginning with KNIL. With KONS as CONS and KNIL as '(), equivalent to REVERSE.")))) (test "(define (fold kons knil ls) ; The fundamental list iterator. (let lp ((ls ls) (acc knil)) ; Applies KONS to each element of (if (null? ls) ; LS and the result of the previous acc ; application, beginning with KNIL. (lp (cdr ls) ; With KONS as CONS and KNIL as '(), (kons (car ls) acc))))) ; equivalent to REVERSE. " (fmt #f (fmt-columns (list (cut pad/right 36 <>) (with-width 36 (pretty '(define (fold kons knil ls) (let lp ((ls ls) (acc knil)) (if (null? ls) acc (lp (cdr ls) (kons (car ls) acc)))))))) (list (cut cat " ; " <>) (with-width 36 (wrap-lines "The fundamental list iterator. Applies KONS to each element of LS and the result of the previous application, beginning with KNIL. With KONS as CONS and KNIL as '(), equivalent to REVERSE.")))))) (test "(define (fold kons knil ls) ; The fundamental list iterator. (let lp ((ls ls) (acc knil)) ; Applies KONS to each element of (if (null? ls) ; LS and the result of the previous acc ; application, beginning with KNIL. (lp (cdr ls) ; With KONS as CONS and KNIL as '(), (kons (car ls) acc))))) ; equivalent to REVERSE. " (fmt #f (with-width 76 (columnar (pretty '(define (fold kons knil ls) (let lp ((ls ls) (acc knil)) (if (null? ls) acc (lp (cdr ls) (kons (car ls) acc)))))) " ; " (wrap-lines "The fundamental list iterator. Applies KONS to each element of LS and the result of the previous application, beginning with KNIL. With KONS as CONS and KNIL as '(), equivalent to REVERSE."))))) (test "- Item 1: The text here is indented according to the space \"Item 1\" takes, and one does not known what goes here. " (fmt #f (columnar 9 (dsp "- Item 1:") " " (with-width 20 (wrap-lines "The text here is indented according to the space \"Item 1\" takes, and one does not known what goes here."))))) (test "- Item 1: The text here is indented according to the space \"Item 1\" takes, and one does not known what goes here. " (fmt #f (columnar 9 (dsp "- Item 1:\n") " " (with-width 20 (wrap-lines "The text here is indented according to the space \"Item 1\" takes, and one does not known what goes here."))))) (test "- Item 1: The text here is---------------------------------------------------- --------- indented according-------------------------------------------------- --------- to the space \"Item-------------------------------------------------- --------- 1\" takes, and one--------------------------------------------------- --------- does not known what------------------------------------------------- --------- goes here.---------------------------------------------------------- " (fmt #f (pad-char #\- (columnar 9 (dsp "- Item 1:\n") " " (with-width 20 (wrap-lines "The text here is indented according to the space \"Item 1\" takes, and one does not known what goes here.")))))) (test "a | 123 bc | 45 def | 6 " (fmt #f (with-width 20 (tabular (dsp "a\nbc\ndef\n") " | " (dsp "123\n45\n6\n"))))) ;; misc extras (define (string-hide-passwords str) (string-substitute (regexp "(pass(?:w(?:or)?d)?\\s?[:=>]\\s+)\\S+" #t) "\\1******" str #t)) (define hide-passwords (make-string-fmt-transformer string-hide-passwords)) (define (string-mangle-email str) (string-substitute (regexp "\\b([-+.\\w]+)@((?:[-+\\w]+\\.)+[a-z]{2,4})\\b" #t) "\\1 _at_ \\2" str #t)) (define mangle-email (make-string-fmt-transformer string-mangle-email)) (test-end) |
Added fmt/test-round.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
(use fmt test) ;;(use numbers) ; test with and without numbers via -R numbers (define (check-representation n) (define pence (inexact->exact (round (/ (modulo n 1000) 10)))) (define pounds (quotient n 1000)) (if (> pence 99) (begin (set! pence (- 100 pence)) (set! pounds (add1 pounds)))) (define expected-result (cond ((= pence 0) (sprintf "~S.00" pounds)) ((< pence 10) (sprintf "~S.0~S" pounds pence)) (else (sprintf "~S.~S" pounds pence)))) (test (sprintf "~S = ~S?" (exact->inexact (/ n 1000)) expected-result) expected-result (fmt #f (num (/ n 1000) 10 2)))) (test-begin) (for-each check-representation (iota 10000)) (test-end) |
Added match.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 |
;;;; match.scm -- portable hygienic pattern matcher -*- coding: utf-8 -*- ;; ;; This code is written by Alex Shinn and placed in the ;; Public Domain. All warranties are disclaimed. ;;> \example-import[(srfi 9)] ;;> A portable hygienic pattern matcher. ;;> This is a full superset of the popular \hyperlink[ ;;> "http://www.cs.indiana.edu/scheme-repository/code.match.html"]{match} ;;> package by Andrew Wright, written in fully portable \scheme{syntax-rules} ;;> and thus preserving hygiene. ;;> The most notable extensions are the ability to use \emph{non-linear} ;;> patterns - patterns in which the same identifier occurs multiple ;;> times, tail patterns after ellipsis, and the experimental tree patterns. ;;> \section{Patterns} ;;> Patterns are written to look like the printed representation of ;;> the objects they match. The basic usage is ;;> \scheme{(match expr (pat body ...) ...)} ;;> where the result of \var{expr} is matched against each pattern in ;;> turn, and the corresponding body is evaluated for the first to ;;> succeed. Thus, a list of three elements matches a list of three ;;> elements. ;;> \example{(let ((ls (list 1 2 3))) (match ls ((1 2 3) #t)))} ;;> If no patterns match an error is signalled. ;;> Identifiers will match anything, and make the corresponding ;;> binding available in the body. ;;> \example{(match (list 1 2 3) ((a b c) b))} ;;> If the same identifier occurs multiple times, the first instance ;;> will match anything, but subsequent instances must match a value ;;> which is \scheme{equal?} to the first. ;;> \example{(match (list 1 2 1) ((a a b) 1) ((a b a) 2))} ;;> The special identifier \scheme{_} matches anything, no matter how ;;> many times it is used, and does not bind the result in the body. ;;> \example{(match (list 1 2 1) ((_ _ b) 1) ((a b a) 2))} ;;> To match a literal identifier (or list or any other literal), use ;;> \scheme{quote}. ;;> \example{(match 'a ('b 1) ('a 2))} ;;> Analogous to its normal usage in scheme, \scheme{quasiquote} can ;;> be used to quote a mostly literally matching object with selected ;;> parts unquoted. ;;> \example|{(match (list 1 2 3) (`(1 ,b ,c) (list b c)))}| ;;> Often you want to match any number of a repeated pattern. Inside ;;> a list pattern you can append \scheme{...} after an element to ;;> match zero or more of that pattern (like a regexp Kleene star). ;;> \example{(match (list 1 2) ((1 2 3 ...) #t))} ;;> \example{(match (list 1 2 3) ((1 2 3 ...) #t))} ;;> \example{(match (list 1 2 3 3 3) ((1 2 3 ...) #t))} ;;> Pattern variables matched inside the repeated pattern are bound to ;;> a list of each matching instance in the body. ;;> \example{(match (list 1 2) ((a b c ...) c))} ;;> \example{(match (list 1 2 3) ((a b c ...) c))} ;;> \example{(match (list 1 2 3 4 5) ((a b c ...) c))} ;;> More than one \scheme{...} may not be used in the same list, since ;;> this would require exponential backtracking in the general case. ;;> However, \scheme{...} need not be the final element in the list, ;;> and may be succeeded by a fixed number of patterns. ;;> \example{(match (list 1 2 3 4) ((a b c ... d e) c))} ;;> \example{(match (list 1 2 3 4 5) ((a b c ... d e) c))} ;;> \example{(match (list 1 2 3 4 5 6 7) ((a b c ... d e) c))} ;;> \scheme{___} is provided as an alias for \scheme{...} when it is ;;> inconvenient to use the ellipsis (as in a syntax-rules template). ;;> The \scheme{..1} syntax is exactly like the \scheme{...} except ;;> that it matches one or more repetitions (like a regexp "+"). ;;> \example{(match (list 1 2) ((a b c ..1) c))} ;;> \example{(match (list 1 2 3) ((a b c ..1) c))} ;;> The boolean operators \scheme{and}, \scheme{or} and \scheme{not} ;;> can be used to group and negate patterns analogously to their ;;> Scheme counterparts. ;;> The \scheme{and} operator ensures that all subpatterns match. ;;> This operator is often used with the idiom \scheme{(and x pat)} to ;;> bind \var{x} to the entire value that matches \var{pat} ;;> (c.f. "as-patterns" in ML or Haskell). Another common use is in ;;> conjunction with \scheme{not} patterns to match a general case ;;> with certain exceptions. ;;> \example{(match 1 ((and) #t))} ;;> \example{(match 1 ((and x) x))} ;;> \example{(match 1 ((and x 1) x))} ;;> The \scheme{or} operator ensures that at least one subpattern ;;> matches. If the same identifier occurs in different subpatterns, ;;> it is matched independently. All identifiers from all subpatterns ;;> are bound if the \scheme{or} operator matches, but the binding is ;;> only defined for identifiers from the subpattern which matched. ;;> \example{(match 1 ((or) #t) (else #f))} ;;> \example{(match 1 ((or x) x))} ;;> \example{(match 1 ((or x 2) x))} ;;> The \scheme{not} operator succeeds if the given pattern doesn't ;;> match. None of the identifiers used are available in the body. ;;> \example{(match 1 ((not 2) #t))} ;;> The more general operator \scheme{?} can be used to provide a ;;> predicate. The usage is \scheme{(? predicate pat ...)} where ;;> \var{predicate} is a Scheme expression evaluating to a predicate ;;> called on the value to match, and any optional patterns after the ;;> predicate are then matched as in an \scheme{and} pattern. ;;> \example{(match 1 ((? odd? x) x))} ;;> The field operator \scheme{=} is used to extract an arbitrary ;;> field and match against it. It is useful for more complex or ;;> conditional destructuring that can't be more directly expressed in ;;> the pattern syntax. The usage is \scheme{(= field pat)}, where ;;> \var{field} can be any expression, and should result in a ;;> procedure of one argument, which is applied to the value to match ;;> to generate a new value to match against \var{pat}. ;;> Thus the pattern \scheme{(and (= car x) (= cdr y))} is equivalent ;;> to \scheme{(x . y)}, except it will result in an immediate error ;;> if the value isn't a pair. ;;> \example{(match '(1 . 2) ((= car x) x))} ;;> \example{(match 4 ((= square x) x))} ;;> The record operator \scheme{$} is used as a concise way to match ;;> records defined by SRFI-9 (or SRFI-99). The usage is ;;> \scheme{($ rtd field ...)}, where \var{rtd} should be the record ;;> type descriptor specified as the first argument to ;;> \scheme{define-record-type}, and each \var{field} is a subpattern ;;> matched against the fields of the record in order. Not all fields ;;> must be present. ;;> \example{ ;;> (let () ;;> (define-record-type employee ;;> (make-employee name title) ;;> employee? ;;> (name get-name) ;;> (title get-title)) ;;> (match (make-employee "Bob" "Doctor") ;;> (($ employee n t) (list t n)))) ;;> } ;;> For records with more fields it can be helpful to match them by ;;> name rather than position. For this you can use the \scheme{@} ;;> operator, originally a Gauche extension: ;;> \example{ ;;> (let () ;;> (define-record-type employee ;;> (make-employee name title) ;;> employee? ;;> (name get-name) ;;> (title get-title)) ;;> (match (make-employee "Bob" "Doctor") ;;> ((@ employee (title t) (name n)) (list t n)))) ;;> } ;;> The \scheme{set!} and \scheme{get!} operators are used to bind an ;;> identifier to the setter and getter of a field, respectively. The ;;> setter is a procedure of one argument, which mutates the field to ;;> that argument. The getter is a procedure of no arguments which ;;> returns the current value of the field. ;;> \example{(let ((x (cons 1 2))) (match x ((1 . (set! s)) (s 3) x)))} ;;> \example{(match '(1 . 2) ((1 . (get! g)) (g)))} ;;> The new operator \scheme{***} can be used to search a tree for ;;> subpatterns. A pattern of the form \scheme{(x *** y)} represents ;;> the subpattern \var{y} located somewhere in a tree where the path ;;> from the current object to \var{y} can be seen as a list of the ;;> form \scheme{(x ...)}. \var{y} can immediately match the current ;;> object in which case the path is the empty list. In a sense it's ;;> a 2-dimensional version of the \scheme{...} pattern. ;;> As a common case the pattern \scheme{(_ *** y)} can be used to ;;> search for \var{y} anywhere in a tree, regardless of the path ;;> used. ;;> \example{(match '(a (a (a b))) ((x *** 'b) x))} ;;> \example{(match '(a (b) (c (d e) (f g))) ((x *** 'g) x))} ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Notes ;; The implementation is a simple generative pattern matcher - each ;; pattern is expanded into the required tests, calling a failure ;; continuation if the tests fail. This makes the logic easy to ;; follow and extend, but produces sub-optimal code in cases where you ;; have many similar clauses due to repeating the same tests. ;; Nonetheless a smart compiler should be able to remove the redundant ;; tests. For MATCH-LET and DESTRUCTURING-BIND type uses there is no ;; performance hit. ;; The original version was written on 2006/11/29 and described in the ;; following Usenet post: ;; http://groups.google.com/group/comp.lang.scheme/msg/0941234de7112ffd ;; and is still available at ;; http://synthcode.com/scheme/match-simple.scm ;; It's just 80 lines for the core MATCH, and an extra 40 lines for ;; MATCH-LET, MATCH-LAMBDA and other syntactic sugar. ;; ;; A variant of this file which uses COND-EXPAND in a few places for ;; performance can be found at ;; http://synthcode.com/scheme/match-cond-expand.scm ;; ;; 2016/03/06 - fixing named match-let (thanks to Stefan Israelsson Tampe) ;; 2015/05/09 - fixing bug in var extraction of quasiquote patterns ;; 2014/11/24 - adding Gauche's `@' pattern for named record field matching ;; 2012/12/26 - wrapping match-let&co body in lexical closure ;; 2012/11/28 - fixing typo s/vetor/vector in largely unused set! code ;; 2012/05/23 - fixing combinatorial explosion of code in certain or patterns ;; 2011/09/25 - fixing bug when directly matching an identifier repeated in ;; the pattern (thanks to Stefan Israelsson Tampe) ;; 2011/01/27 - fixing bug when matching tail patterns against improper lists ;; 2010/09/26 - adding `..1' patterns (thanks to Ludovic Courtès) ;; 2010/09/07 - fixing identifier extraction in some `...' and `***' patterns ;; 2009/11/25 - adding `***' tree search patterns ;; 2008/03/20 - fixing bug where (a ...) matched non-lists ;; 2008/03/15 - removing redundant check in vector patterns ;; 2008/03/06 - you can use `...' portably now (thanks to Taylor Campbell) ;; 2007/09/04 - fixing quasiquote patterns ;; 2007/07/21 - allowing ellipsis patterns in non-final list positions ;; 2007/04/10 - fixing potential hygiene issue in match-check-ellipsis ;; (thanks to Taylor Campbell) ;; 2007/04/08 - clean up, commenting ;; 2006/12/24 - bugfixes ;; 2006/12/01 - non-linear patterns, shared variables in OR, get!/set! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; force compile-time syntax errors with useful messages (define-syntax match-syntax-error (syntax-rules () ((_) (match-syntax-error "invalid match-syntax-error usage")))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;> \section{Syntax} ;;> \macro{(match expr (pattern . body) ...)\br{} ;;> (match expr (pattern (=> failure) . body) ...)} ;;> The result of \var{expr} is matched against each \var{pattern} in ;;> turn, according to the pattern rules described in the previous ;;> section, until the the first \var{pattern} matches. When a match is ;;> found, the corresponding \var{body}s are evaluated in order, ;;> and the result of the last expression is returned as the result ;;> of the entire \scheme{match}. If a \var{failure} is provided, ;;> then it is bound to a procedure of no arguments which continues, ;;> processing at the next \var{pattern}. If no \var{pattern} matches, ;;> an error is signalled. ;; The basic interface. MATCH just performs some basic syntax ;; validation, binds the match expression to a temporary variable `v', ;; and passes it on to MATCH-NEXT. It's a constant throughout the ;; code below that the binding `v' is a direct variable reference, not ;; an expression. (define-syntax match (syntax-rules () ((match) (match-syntax-error "missing match expression")) ((match atom) (match-syntax-error "no match clauses")) ((match (app ...) (pat . body) ...) (let ((v (app ...))) (match-next v ((app ...) (set! (app ...))) (pat . body) ...))) ((match #(vec ...) (pat . body) ...) (let ((v #(vec ...))) (match-next v (v (set! v)) (pat . body) ...))) ((match atom (pat . body) ...) (let ((v atom)) (match-next v (atom (set! atom)) (pat . body) ...))) )) ;; MATCH-NEXT passes each clause to MATCH-ONE in turn with its failure ;; thunk, which is expanded by recursing MATCH-NEXT on the remaining ;; clauses. `g+s' is a list of two elements, the get! and set! ;; expressions respectively. (define-syntax match-next (syntax-rules (=>) ;; no more clauses, the match failed ((match-next v g+s) (error 'match "no matching pattern")) ;; named failure continuation ((match-next v g+s (pat (=> failure) . body) . rest) (let ((failure (lambda () (match-next v g+s . rest)))) ;; match-one analyzes the pattern for us (match-one v pat g+s (match-drop-ids (begin . body)) (failure) ()))) ;; anonymous failure continuation, give it a dummy name ((match-next v g+s (pat . body) . rest) (match-next v g+s (pat (=> failure) . body) . rest)))) ;; MATCH-ONE first checks for ellipsis patterns, otherwise passes on to ;; MATCH-TWO. (define-syntax match-one (syntax-rules () ;; If it's a list of two or more values, check to see if the ;; second one is an ellipsis and handle accordingly, otherwise go ;; to MATCH-TWO. ((match-one v (p q . r) g+s sk fk i) (match-check-ellipsis q (match-extract-vars p (match-gen-ellipsis v p r g+s sk fk i) i ()) (match-two v (p q . r) g+s sk fk i))) ;; Go directly to MATCH-TWO. ((match-one . x) (match-two . x)))) ;; This is the guts of the pattern matcher. We are passed a lot of ;; information in the form: ;; ;; (match-two var pattern getter setter success-k fail-k (ids ...)) ;; ;; usually abbreviated ;; ;; (match-two v p g+s sk fk i) ;; ;; where VAR is the symbol name of the current variable we are ;; matching, PATTERN is the current pattern, getter and setter are the ;; corresponding accessors (e.g. CAR and SET-CAR! of the pair holding ;; VAR), SUCCESS-K is the success continuation, FAIL-K is the failure ;; continuation (which is just a thunk call and is thus safe to expand ;; multiple times) and IDS are the list of identifiers bound in the ;; pattern so far. ;; Replace '_' with ':_' as the former is forbidden as an auxiliariy ;; keyword in R6RS. (FBE) (define-syntax match-two (syntax-rules (:_ ___ ..1 *** quote quasiquote ? $ struct @ object = and or not set! get!) ((match-two v () g+s (sk ...) fk i) (if (null? v) (sk ... i) fk)) ((match-two v (quote p) g+s (sk ...) fk i) (if (equal? v 'p) (sk ... i) fk)) ((match-two v (quasiquote p) . x) (match-quasiquote v p . x)) ((match-two v (and) g+s (sk ...) fk i) (sk ... i)) ((match-two v (and p q ...) g+s sk fk i) (match-one v p g+s (match-one v (and q ...) g+s sk fk) fk i)) ((match-two v (or) g+s sk fk i) fk) ((match-two v (or p) . x) (match-one v p . x)) ((match-two v (or p ...) g+s sk fk i) (match-extract-vars (or p ...) (match-gen-or v (p ...) g+s sk fk i) i ())) ((match-two v (not p) g+s (sk ...) fk i) (match-one v p g+s (match-drop-ids fk) (sk ... i) i)) ((match-two v (get! getter) (g s) (sk ...) fk i) (let ((getter (lambda () g))) (sk ... i))) ((match-two v (set! setter) (g (s ...)) (sk ...) fk i) (let ((setter (lambda (x) (s ... x)))) (sk ... i))) ((match-two v (? pred . p) g+s sk fk i) (if (pred v) (match-one v (and . p) g+s sk fk i) fk)) ((match-two v (= proc p) . x) (let ((w (proc v))) (match-one w p . x))) ((match-two v (p ___ . r) g+s sk fk i) (match-extract-vars p (match-gen-ellipsis v p r g+s sk fk i) i ())) ((match-two v (p) g+s sk fk i) (if (and (pair? v) (null? (cdr v))) (let ((w (car v))) (match-one w p ((car v) (set-car! v)) sk fk i)) fk)) ((match-two v (p *** q) g+s sk fk i) (match-extract-vars p (match-gen-search v p q g+s sk fk i) i ())) ((match-two v (p *** . q) g+s sk fk i) (match-syntax-error "invalid use of ***" (p *** . q))) ((match-two v (p ..1) g+s sk fk i) (if (pair? v) (match-one v (p ___) g+s sk fk i) fk)) ((match-two v ($ rec p ...) g+s sk fk i) (if (is-a? v rec) (match-record-refs v rec 0 (p ...) g+s sk fk i) fk)) ((match-two v (struct rec p ...) g+s sk fk i) (if (is-a? v rec) (match-record-refs v rec 0 (p ...) g+s sk fk i) fk)) ((match-two v (@ rec p ...) g+s sk fk i) (if (is-a? v rec) (match-record-named-refs v rec (p ...) g+s sk fk i) fk)) ((match-two v (object rec p ...) g+s sk fk i) (if (is-a? v rec) (match-record-named-refs v rec (p ...) g+s sk fk i) fk)) ((match-two v (p . q) g+s sk fk i) (if (pair? v) (let ((w (car v)) (x (cdr v))) (match-one w p ((car v) (set-car! v)) (match-one x q ((cdr v) (set-cdr! v)) sk fk) fk i)) fk)) ((match-two v #(p ...) g+s . x) (match-vector v 0 () (p ...) . x)) ;; Next line: replace '_' with ':_'. (FBE) ((match-two v :_ g+s (sk ...) fk i) (sk ... i)) ;; Not a pair or vector or special literal, test to see if it's a ;; new symbol, in which case we just bind it, or if it's an ;; already bound symbol or some other literal, in which case we ;; compare it with EQUAL?. ((match-two v x g+s (sk ...) fk (id ...)) (let-syntax ((new-sym? (syntax-rules (id ...) ((new-sym? x sk2 fk2) sk2) ((new-sym? y sk2 fk2) fk2)))) (new-sym? random-sym-to-match (let ((x v)) (sk ... (id ... x))) (if (equal? v x) (sk ... (id ...)) fk)))) )) ;; QUASIQUOTE patterns (define-syntax match-quasiquote (syntax-rules (unquote unquote-splicing quasiquote) ((_ v (unquote p) g+s sk fk i) (match-one v p g+s sk fk i)) ((_ v ((unquote-splicing p) . rest) g+s sk fk i) (if (pair? v) (match-one v (p . tmp) (match-quasiquote tmp rest g+s sk fk) fk i) fk)) ((_ v (quasiquote p) g+s sk fk i . depth) (match-quasiquote v p g+s sk fk i #f . depth)) ((_ v (unquote p) g+s sk fk i x . depth) (match-quasiquote v p g+s sk fk i . depth)) ((_ v (unquote-splicing p) g+s sk fk i x . depth) (match-quasiquote v p g+s sk fk i . depth)) ((_ v (p . q) g+s sk fk i . depth) (if (pair? v) (let ((w (car v)) (x (cdr v))) (match-quasiquote w p g+s (match-quasiquote-step x q g+s sk fk depth) fk i . depth)) fk)) ((_ v #(elt ...) g+s sk fk i . depth) (if (vector? v) (let ((ls (vector->list v))) (match-quasiquote ls (elt ...) g+s sk fk i . depth)) fk)) ((_ v x g+s sk fk i . depth) (match-one v 'x g+s sk fk i)))) (define-syntax match-quasiquote-step (syntax-rules () ((match-quasiquote-step x q g+s sk fk depth i) (match-quasiquote x q g+s sk fk i . depth)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Utilities ;; Takes two values and just expands into the first. (define-syntax match-drop-ids (syntax-rules () ((_ expr ids ...) expr))) (define-syntax match-tuck-ids (syntax-rules () ((_ (letish args (expr ...)) ids ...) (letish args (expr ... ids ...))))) (define-syntax match-drop-first-arg (syntax-rules () ((_ arg expr) expr))) ;; To expand an OR group we try each clause in succession, passing the ;; first that succeeds to the success continuation. On failure for ;; any clause, we just try the next clause, finally resorting to the ;; failure continuation fk if all clauses fail. The only trick is ;; that we want to unify the identifiers, so that the success ;; continuation can refer to a variable from any of the OR clauses. (define-syntax match-gen-or (syntax-rules () ((_ v p g+s (sk ...) fk (i ...) ((id id-ls) ...)) (let ((sk2 (lambda (id ...) (sk ... (i ... id ...))))) (match-gen-or-step v p g+s (match-drop-ids (sk2 id ...)) fk (i ...)))))) (define-syntax match-gen-or-step (syntax-rules () ((_ v () g+s sk fk . x) ;; no OR clauses, call the failure continuation fk) ((_ v (p) . x) ;; last (or only) OR clause, just expand normally (match-one v p . x)) ((_ v (p . q) g+s sk fk i) ;; match one and try the remaining on failure (let ((fk2 (lambda () (match-gen-or-step v q g+s sk fk i)))) (match-one v p g+s sk (fk2) i))) )) ;; We match a pattern (p ...) by matching the pattern p in a loop on ;; each element of the variable, accumulating the bound ids into lists. ;; Look at the body of the simple case - it's just a named let loop, ;; matching each element in turn to the same pattern. The only trick ;; is that we want to keep track of the lists of each extracted id, so ;; when the loop recurses we cons the ids onto their respective list ;; variables, and on success we bind the ids (what the user input and ;; expects to see in the success body) to the reversed accumulated ;; list IDs. (define-syntax match-gen-ellipsis (syntax-rules () ((_ v p () g+s (sk ...) fk i ((id id-ls) ...)) (match-check-identifier p ;; simplest case equivalent to (p ...), just bind the list (let ((p v)) (if (list? p) (sk ... i) fk)) ;; simple case, match all elements of the list (let loop ((ls v) (id-ls '()) ...) (cond ((null? ls) (let ((id (reverse id-ls)) ...) (sk ... i))) ((pair? ls) (let ((w (car ls))) (match-one w p ((car ls) (set-car! ls)) (match-drop-ids (loop (cdr ls) (cons id id-ls) ...)) fk i))) (else fk))))) ((_ v p r g+s (sk ...) fk i ((id id-ls) ...)) ;; general case, trailing patterns to match, keep track of the ;; remaining list length so we don't need any backtracking (match-verify-no-ellipsis r (let* ((tail-len (length 'r)) (ls v) (len (and (list? ls) (length ls)))) (if (or (not len) (< len tail-len)) fk (let loop ((ls ls) (n len) (id-ls '()) ...) (cond ((= n tail-len) (let ((id (reverse id-ls)) ...) (match-one ls r (#f #f) (sk ...) fk i))) ((pair? ls) (let ((w (car ls))) (match-one w p ((car ls) (set-car! ls)) (match-drop-ids (loop (cdr ls) (- n 1) (cons id id-ls) ...)) fk i))) (else fk))))))))) ;; This is just a safety check. Although unlike syntax-rules we allow ;; trailing patterns after an ellipsis, we explicitly disable multiple ;; ellipsis at the same level. This is because in the general case ;; such patterns are exponential in the number of ellipsis, and we ;; don't want to make it easy to construct very expensive operations ;; with simple looking patterns. For example, it would be O(n^2) for ;; patterns like (a ... b ...) because we must consider every trailing ;; element for every possible break for the leading "a ...". (define-syntax match-verify-no-ellipsis (syntax-rules () ((_ (x . y) sk) (match-check-ellipsis x (match-syntax-error "multiple ellipsis patterns not allowed at same level") (match-verify-no-ellipsis y sk))) ((_ () sk) sk) ((_ x sk) (match-syntax-error "dotted tail not allowed after ellipsis" x)))) ;; To implement the tree search, we use two recursive procedures. TRY ;; attempts to match Y once, and on success it calls the normal SK on ;; the accumulated list ids as in MATCH-GEN-ELLIPSIS. On failure, we ;; call NEXT which first checks if the current value is a list ;; beginning with X, then calls TRY on each remaining element of the ;; list. Since TRY will recursively call NEXT again on failure, this ;; effects a full depth-first search. ;; ;; The failure continuation throughout is a jump to the next step in ;; the tree search, initialized with the original failure continuation ;; FK. (define-syntax match-gen-search (syntax-rules () ((match-gen-search v p q g+s sk fk i ((id id-ls) ...)) (letrec ((try (lambda (w fail id-ls ...) (match-one w q g+s (match-tuck-ids (let ((id (reverse id-ls)) ...) sk)) (next w fail id-ls ...) i))) (next (lambda (w fail id-ls ...) (if (not (pair? w)) (fail) (let ((u (car w))) (match-one u p ((car w) (set-car! w)) (match-drop-ids ;; accumulate the head variables from ;; the p pattern, and loop over the tail (let ((id-ls (cons id id-ls)) ...) (let lp ((ls (cdr w))) (if (pair? ls) (try (car ls) (lambda () (lp (cdr ls))) id-ls ...) (fail))))) (fail) i)))))) ;; the initial id-ls binding here is a dummy to get the right ;; number of '()s (let ((id-ls '()) ...) (try v (lambda () fk) id-ls ...)))))) ;; Vector patterns are just more of the same, with the slight ;; exception that we pass around the current vector index being ;; matched. (define-syntax match-vector (syntax-rules (___) ((_ v n pats (p q) . x) (match-check-ellipsis q (match-gen-vector-ellipsis v n pats p . x) (match-vector-two v n pats (p q) . x))) ((_ v n pats (p ___) sk fk i) (match-gen-vector-ellipsis v n pats p sk fk i)) ((_ . x) (match-vector-two . x)))) ;; Check the exact vector length, then check each element in turn. (define-syntax match-vector-two (syntax-rules () ((_ v n ((pat index) ...) () sk fk i) (if (vector? v) (let ((len (vector-length v))) (if (= len n) (match-vector-step v ((pat index) ...) sk fk i) fk)) fk)) ((_ v n (pats ...) (p . q) . x) (match-vector v (+ n 1) (pats ... (p n)) q . x)))) (define-syntax match-vector-step (syntax-rules () ((_ v () (sk ...) fk i) (sk ... i)) ((_ v ((pat index) . rest) sk fk i) (let ((w (vector-ref v index))) (match-one w pat ((vector-ref v index) (vector-set! v index)) (match-vector-step v rest sk fk) fk i))))) ;; With a vector ellipsis pattern we first check to see if the vector ;; length is at least the required length. (define-syntax match-gen-vector-ellipsis (syntax-rules () ((_ v n ((pat index) ...) p sk fk i) (if (vector? v) (let ((len (vector-length v))) (if (>= len n) (match-vector-step v ((pat index) ...) (match-vector-tail v p n len sk fk) fk i) fk)) fk)))) (define-syntax match-vector-tail (syntax-rules () ((_ v p n len sk fk i) (match-extract-vars p (match-vector-tail-two v p n len sk fk i) i ())))) (define-syntax match-vector-tail-two (syntax-rules () ((_ v p n len (sk ...) fk i ((id id-ls) ...)) (let loop ((j n) (id-ls '()) ...) (if (>= j len) (let ((id (reverse id-ls)) ...) (sk ... i)) (let ((w (vector-ref v j))) (match-one w p ((vector-ref v j) (vector-set! v j)) (match-drop-ids (loop (+ j 1) (cons id id-ls) ...)) fk i))))))) (define-syntax match-record-refs (syntax-rules () ((_ v rec n (p . q) g+s sk fk i) (let ((w (slot-ref rec v n))) (match-one w p ((slot-ref rec v n) (slot-set! rec v n)) (match-record-refs v rec (+ n 1) q g+s sk fk) fk i))) ((_ v rec n () g+s (sk ...) fk i) (sk ... i)))) (define-syntax match-record-named-refs (syntax-rules () ((_ v rec ((f p) . q) g+s sk fk i) (let ((w (slot-ref rec v 'f))) (match-one w p ((slot-ref rec v 'f) (slot-set! rec v 'f)) (match-record-named-refs v rec q g+s sk fk) fk i))) ((_ v rec () g+s (sk ...) fk i) (sk ... i)))) ;; Extract all identifiers in a pattern. A little more complicated ;; than just looking for symbols, we need to ignore special keywords ;; and non-pattern forms (such as the predicate expression in ? ;; patterns), and also ignore previously bound identifiers. ;; ;; Calls the continuation with all new vars as a list of the form ;; ((orig-var tmp-name) ...), where tmp-name can be used to uniquely ;; pair with the original variable (e.g. it's used in the ellipsis ;; generation for list variables). ;; ;; (match-extract-vars pattern continuation (ids ...) (new-vars ...)) ;; Replace '_' with ':_' as the former is forbidden as an auxiliariy ;; keyword in R6RS. (FBE) (define-syntax match-extract-vars (syntax-rules (:_ ___ ..1 *** ? $ struct @ object = quote quasiquote and or not get! set!) ((match-extract-vars (? pred . p) . x) (match-extract-vars p . x)) ((match-extract-vars ($ rec . p) . x) (match-extract-vars p . x)) ((match-extract-vars (struct rec . p) . x) (match-extract-vars p . x)) ((match-extract-vars (@ rec (f p) ...) . x) (match-extract-vars (p ...) . x)) ((match-extract-vars (object rec (f p) ...) . x) (match-extract-vars (p ...) . x)) ((match-extract-vars (= proc p) . x) (match-extract-vars p . x)) ((match-extract-vars (quote x) (k ...) i v) (k ... v)) ((match-extract-vars (quasiquote x) k i v) (match-extract-quasiquote-vars x k i v (#t))) ((match-extract-vars (and . p) . x) (match-extract-vars p . x)) ((match-extract-vars (or . p) . x) (match-extract-vars p . x)) ((match-extract-vars (not . p) . x) (match-extract-vars p . x)) ;; A non-keyword pair, expand the CAR with a continuation to ;; expand the CDR. ((match-extract-vars (p q . r) k i v) (match-check-ellipsis q (match-extract-vars (p . r) k i v) (match-extract-vars p (match-extract-vars-step (q . r) k i v) i ()))) ((match-extract-vars (p . q) k i v) (match-extract-vars p (match-extract-vars-step q k i v) i ())) ((match-extract-vars #(p ...) . x) (match-extract-vars (p ...) . x)) ;; Next line: replace '_' with ':_'. (FBE) ((match-extract-vars :_ (k ...) i v) (k ... v)) ((match-extract-vars ___ (k ...) i v) (k ... v)) ((match-extract-vars *** (k ...) i v) (k ... v)) ((match-extract-vars ..1 (k ...) i v) (k ... v)) ;; This is the main part, the only place where we might add a new ;; var if it's an unbound symbol. ((match-extract-vars p (k ...) (i ...) v) (let-syntax ((new-sym? (syntax-rules (i ...) ((new-sym? p sk fk) sk) ((new-sym? any sk fk) fk)))) (new-sym? random-sym-to-match (k ... ((p p-ls) . v)) (k ... v)))) )) ;; Stepper used in the above so it can expand the CAR and CDR ;; separately. (define-syntax match-extract-vars-step (syntax-rules () ((_ p k i v ((v2 v2-ls) ...)) (match-extract-vars p k (v2 ... . i) ((v2 v2-ls) ... . v))) )) (define-syntax match-extract-quasiquote-vars (syntax-rules (quasiquote unquote unquote-splicing) ((match-extract-quasiquote-vars (quasiquote x) k i v d) (match-extract-quasiquote-vars x k i v (#t . d))) ((match-extract-quasiquote-vars (unquote-splicing x) k i v d) (match-extract-quasiquote-vars (unquote x) k i v d)) ((match-extract-quasiquote-vars (unquote x) k i v (#t)) (match-extract-vars x k i v)) ((match-extract-quasiquote-vars (unquote x) k i v (#t . d)) (match-extract-quasiquote-vars x k i v d)) ((match-extract-quasiquote-vars (x . y) k i v d) (match-extract-quasiquote-vars x (match-extract-quasiquote-vars-step y k i v d) i () d)) ((match-extract-quasiquote-vars #(x ...) k i v d) (match-extract-quasiquote-vars (x ...) k i v d)) ((match-extract-quasiquote-vars x (k ...) i v d) (k ... v)) )) (define-syntax match-extract-quasiquote-vars-step (syntax-rules () ((_ x k i v d ((v2 v2-ls) ...)) (match-extract-quasiquote-vars x k (v2 ... . i) ((v2 v2-ls) ... . v) d)) )) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Gimme some sugar baby. ;;> Shortcut for \scheme{lambda} + \scheme{match}. Creates a ;;> procedure of one argument, and matches that argument against each ;;> clause. (define-syntax match-lambda (syntax-rules () ((_ (pattern . body) ...) (lambda (expr) (match expr (pattern . body) ...))))) ;;> Similar to \scheme{match-lambda}. Creates a procedure of any ;;> number of arguments, and matches the argument list against each ;;> clause. (define-syntax match-lambda* (syntax-rules () ((_ (pattern . body) ...) (lambda expr (match expr (pattern . body) ...))))) ;;> Matches each var to the corresponding expression, and evaluates ;;> the body with all match variables in scope. Raises an error if ;;> any of the expressions fail to match. Syntax analogous to named ;;> let can also be used for recursive functions which match on their ;;> arguments as in \scheme{match-lambda*}. (define-syntax match-let (syntax-rules () ((_ ((var value) ...) . body) (match-let/helper let () () ((var value) ...) . body)) ((_ loop ((var init) ...) . body) (match-named-let loop () ((var init) ...) . body)))) ;;> Similar to \scheme{match-let}, but analogously to \scheme{letrec} ;;> matches and binds the variables with all match variables in scope. (define-syntax match-letrec (syntax-rules () ((_ ((var value) ...) . body) (match-let/helper letrec () () ((var value) ...) . body)))) (define-syntax match-let/helper (syntax-rules () ((_ let ((var expr) ...) () () . body) (let ((var expr) ...) . body)) ((_ let ((var expr) ...) ((pat tmp) ...) () . body) (let ((var expr) ...) (match-let* ((pat tmp) ...) . body))) ((_ let (v ...) (p ...) (((a . b) expr) . rest) . body) (match-let/helper let (v ... (tmp expr)) (p ... ((a . b) tmp)) rest . body)) ((_ let (v ...) (p ...) ((#(a ...) expr) . rest) . body) (match-let/helper let (v ... (tmp expr)) (p ... (#(a ...) tmp)) rest . body)) ((_ let (v ...) (p ...) ((a expr) . rest) . body) (match-let/helper let (v ... (a expr)) (p ...) rest . body)))) (define-syntax match-named-let (syntax-rules () ((_ loop ((pat expr var) ...) () . body) (let loop ((var expr) ...) (match-let ((pat var) ...) . body))) ((_ loop (v ...) ((pat expr) . rest) . body) (match-named-let loop (v ... (pat expr tmp)) rest . body)))) ;;> \macro{(match-let* ((var value) ...) body ...)} ;;> Similar to \scheme{match-let}, but analogously to \scheme{let*} ;;> matches and binds the variables in sequence, with preceding match ;;> variables in scope. (define-syntax match-let* (syntax-rules () ((_ () . body) (let () . body)) ((_ ((pat expr) . rest) . body) (match expr (pat (match-let* rest . body)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Otherwise COND-EXPANDed bits. ;; To avoid depending on srfi-0 we comment the following form and copy ;; the generic version below it. (FBE) ;; (cond-expand ;; (chibi ;; (define-syntax match-check-ellipsis ;; (er-macro-transformer ;; (lambda (expr rename compare) ;; (if (compare '... (cadr expr)) ;; (car (cddr expr)) ;; (cadr (cddr expr)))))) ;; (define-syntax match-check-identifier ;; (er-macro-transformer ;; (lambda (expr rename compare) ;; (if (identifier? (cadr expr)) ;; (car (cddr expr)) ;; (cadr (cddr expr))))))) ;; (else ;; ;; Portable versions ;; ;; ;; ;; This *should* work, but doesn't :( ;; ;; (define-syntax match-check-ellipsis ;; ;; (syntax-rules (...) ;; ;; ((_ ... sk fk) sk) ;; ;; ((_ x sk fk) fk))) ;; ;; ;; ;; This is a little more complicated, and introduces a new let-syntax, ;; ;; but should work portably in any R[56]RS Scheme. Taylor Campbell ;; ;; originally came up with the idea. ;; (define-syntax match-check-ellipsis ;; (syntax-rules () ;; ;; these two aren't necessary but provide fast-case failures ;; ((match-check-ellipsis (a . b) success-k failure-k) failure-k) ;; ((match-check-ellipsis #(a ...) success-k failure-k) failure-k) ;; ;; matching an atom ;; ((match-check-ellipsis id success-k failure-k) ;; (let-syntax ((ellipsis? (syntax-rules () ;; ;; iff `id' is `...' here then this will ;; ;; match a list of any length ;; ((ellipsis? (foo id) sk fk) sk) ;; ((ellipsis? other sk fk) fk)))) ;; ;; this list of three elements will only match the (foo id) list ;; ;; above if `id' is `...' ;; (ellipsis? (a b c) success-k failure-k))))) ;; ;; This is portable but can be more efficient with non-portable ;; ;; extensions. This trick was originally discovered by Oleg Kiselyov. ;; (define-syntax match-check-identifier ;; (syntax-rules () ;; ;; fast-case failures, lists and vectors are not identifiers ;; ((_ (x . y) success-k failure-k) failure-k) ;; ((_ #(x ...) success-k failure-k) failure-k) ;; ;; x is an atom ;; ((_ x success-k failure-k) ;; (let-syntax ;; ((sym? ;; (syntax-rules () ;; ;; if the symbol `abracadabra' matches x, then x is a ;; ;; symbol ;; ((sym? x sk fk) sk) ;; ;; otherwise x is a non-symbol datum ;; ((sym? y sk fk) fk)))) ;; (sym? abracadabra success-k failure-k))))))) ;; Portable versions ;; ;; This *should* work, but doesn't :( ;; (define-syntax match-check-ellipsis ;; (syntax-rules (...) ;; ((_ ... sk fk) sk) ;; ((_ x sk fk) fk))) ;; ;; This is a little more complicated, and introduces a new let-syntax, ;; but should work portably in any R[56]RS Scheme. Taylor Campbell ;; originally came up with the idea. (define-syntax match-check-ellipsis (syntax-rules () ;; these two aren't necessary but provide fast-case failures ((match-check-ellipsis (a . b) success-k failure-k) failure-k) ((match-check-ellipsis #(a ...) success-k failure-k) failure-k) ;; matching an atom ((match-check-ellipsis id success-k failure-k) (let-syntax ((ellipsis? (syntax-rules () ;; iff `id' is `...' here then this will ;; match a list of any length ((ellipsis? (foo id) sk fk) sk) ((ellipsis? other sk fk) fk)))) ;; this list of three elements will only match the (foo id) list ;; above if `id' is `...' (ellipsis? (a b c) success-k failure-k))))) ;; This is portable but can be more efficient with non-portable ;; extensions. This trick was originally discovered by Oleg Kiselyov. (define-syntax match-check-identifier (syntax-rules () ;; fast-case failures, lists and vectors are not identifiers ((_ (x . y) success-k failure-k) failure-k) ((_ #(x ...) success-k failure-k) failure-k) ;; x is an atom ((_ x success-k failure-k) (let-syntax ((sym? (syntax-rules () ;; if the symbol `abracadabra' matches x, then x is a ;; symbol ((sym? x sk fk) sk) ;; otherwise x is a non-symbol datum ((sym? y sk fk) fk)))) (sym? abracadabra success-k failure-k))))) |
Added matchable.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
;;; Chez-Scheme Wrappers for Alex Shinn's Match (Wright Compatible) ;;; ;;; Copyright (c) 2016 Federico Beffa <beffa@fbengineering.ch> ;;; ;;; Permission to use, copy, modify, and distribute this software for ;;; any purpose with or without fee is hereby granted, provided that the ;;; above copyright notice and this permission notice appear in all ;;; copies. ;;; ;;; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL ;;; WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED ;;; WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE ;;; AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL ;;; DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA ;;; OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER ;;; TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR ;;; PERFORMANCE OF THIS SOFTWARE. ;; The reader in #!r6rs mode doesn't allow the '..1' identifier. #!chezscheme (library (matchable) (export match match-lambda match-lambda* match-let match-let* match-letrec match-named-let :_ ___ ..1 *** ? $ struct @ object) #;(import (rnrs base) (rnrs lists) (rnrs mutable-pairs) (rnrs records syntactic) (rnrs records procedural) (rnrs records inspection) (rnrs syntax-case) (only (chezscheme) iota include) ;; avoid dependence on chez-srfi (apart for tests) ;; (srfi private aux-keywords) ;; (srfi private include) ) (import (chezscheme)) ;; We declare end export the symbols used as auxiliary identifiers ;; in 'syntax-rules' to make them work in Chez Scheme's interactive ;; environment. (FBE) ;; Also we replaced '_' with ':_' as the special identifier matching ;; anything and not binding. This is because R6RS forbids its use ;; as an auxiliary literal in a syntax-rules form. (define-syntax define-auxiliary-keyword (syntax-rules () [(_ name) (define-syntax name (lambda (x) (syntax-violation #f "misplaced use of auxiliary keyword" x)))])) (define-syntax define-auxiliary-keywords (syntax-rules () [(_ name* ...) (begin (define-auxiliary-keyword name*) ...)])) (define-auxiliary-keywords :_ ___ ..1 *** ? $ struct @ object) (define-syntax is-a? (syntax-rules () ((_ rec rtn) (and (record? rec) (eq? (record-type-name (record-rtd rec)) (quote rtn)))))) (define-syntax slot-ref (syntax-rules () ((_ rtn rec n) (if (number? n) ((record-accessor (record-rtd rec) n) rec) ;; If it's not a number, then it should be a symbol with ;; the name of a field. (let* ((rtd (record-rtd rec)) (fields (record-type-field-names rtd)) (fields-idxs (map (lambda (f i) (cons f i)) (vector->list fields) (iota (vector-length fields)))) (idx (cdr (assv n fields-idxs)))) ((record-accessor rtd idx) rec)))))) (define-syntax slot-set! (syntax-rules () ((_ rtn rec n) (if (number? n) ((record-mutator (record-rtd rec) n) rec) ;; If it's not a number, then it should be a symbol with ;; the name of a field. (let* ((rtd (record-rtd rec)) (fields (record-type-field-names rtd)) (fields-idxs (map (lambda (f i) (cons f i)) (vector->list fields) (iota (vector-length fields)))) (idx (cdr (assv n fields-idxs)))) ((record-mutator rtd idx) rec)))))) (include "match.scm") ) |
Added sql-null.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
;;; "sql-null.scm" -- SQL NULL object and the ternary logic -*- Scheme -*- ;;; Ivan Shmakov, 2007 This code is in public domain. (library (sql-null) (export sql-null sql-null? sql-not sql-or sql-or sql-and sql-coalesce) (import (chezscheme)) ;;from chicken data-structures.scm Copyright (c) 2008-2014, The Chicken Team (define (constantly . xs) (if (eq? 1 (length xs)) (let ([x (car xs)]) (lambda _ x) ) (lambda _ (apply values xs)) ) ) ;; We could also (define-record sql-null) and alias sql-null to make-sql-null ;; but that implies creating many new objects, which we don't want. (define-record-type sql-null-type) (define sql-null-object (make-sql-null-type)) (define sql-null (constantly sql-null-object)) (define sql-null? sql-null-type?) (define (sql-not o) (if (sql-null? o) o (not o))) (define-syntax sql-or (syntax-rules () ((sql-or a ...) (sql-or/null #f a ...)))) (define-syntax sql-or/null (syntax-rules () ((sql-or/null null) null) ((sql-or/null null a b ...) (let ((ea a)) (cond ((sql-null? ea) (sql-or/null ea b ...)) ((not ea) (sql-or/null null b ...)) (else ea)))))) (define-syntax sql-and (syntax-rules () ((sql-and a ...) (sql-and/null #t a ...)))) (define-syntax sql-and/null (syntax-rules () ((sql-and/null null) null) ((sql-and/null null a b ...) (let ((ea a)) (cond ((sql-null? ea) (sql-and/null ea b ...)) (ea (sql-and/null null b ...)) (else ea)))))) (define-syntax sql-coalesce (syntax-rules () ((sql-coalesce) (sql-null)) ((sql-coalesce a b ...) (let ((ea a)) (if (sql-null? ea) (sql-coalesce b ...) ea))))) ) |
Added sqlite3.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 |
;;; Foreign types & values (define (hashtable-walk ht f) (vector-for-each (lambda (x) (f x (hashtable-ref ht x #f))) (hashtable-keys ht))) (define (->string x) (with-output-to-string (lambda () (display x)))) (define (conc . args) (apply string-append (map ->string args)) ) ;; Enumeration and constant definitions (define sqlite3:status '((ok . 0) ; Successful result (#f?) (error . 1) ; SQL error or missing database (internal . 2) ; NOT USED. Internal logic error in SQLite (permission . 3) ; Access permission denied (abort . 4) ; Callback routine requested an abort (busy . 5) ; The database file is locked (locked . 6) ; A table in the database is locked (no-memory . 7) ; A malloc() failed (read-only . 8) ; Attempt to write a readonly database (interrupt . 9) ; Operation terminated by sqlite3_interrupt() (io-error . 10) ; Some kind of disk I/O error occurred (corrupt . 11) ; The database disk image is malformed (not-found . 12) ; NOT USED. Table or record not found (full . 13) ; Insertion failed because database is full (cant-open . 14) ; Unable to open the database file (protocol . 15) ; NOT USED. Database lock protocol error (empty . 16) ; Database is empty (schema . 17) ; The database schema changed (too-big . 18) ; String or BLOB exceeds size limit (constraint . 19) ; Abort due to contraint violation (mismatch . 20) ; Data type mismatch (misuse . 21) ; Library used incorrectly (no-lfs . 22) ; Uses OS features not supported on host (auth . 23) ; Authorization denied (format . 24) ; Auxiliary database format error (range . 25) ; 2nd parameter to sqlite3_bind out of range (not-a-db . 26) ; File opened that is not a database file (notice . 27) ; Notifications from sqlite3_log() (warning . 28) ; Warnings from sqlite3_log() (row . 100) ; sqlite3_step() has another row ready (done . 101) ; sqlite3_step() has finished executing )) (define (number->sqlite3:status status) (let ([x (find (lambda (a) (equal? (cdr a) status)) sqlite3:status)]) (if (pair? x) (car x) #f))) (define sqlite3:type-enum (make-enumeration '(undefined integer float text blob null))) (define sqlite3:type-index (enum-set-indexer sqlite3:type-enum)) (define (sqlite3:type-ref index) (list-ref (enum-set->list sqlite3:type-enum) index)) ;; Auxiliary types (define-ftype sqlite3:context void*) (define-ftype sqlite3:value void*) ;; Types for databases and statements (define-ftype sqlite3:database* void*) (define-ftype sqlite3:database** (* sqlite3:database*)) ;(define-check+error-type database) (define-ftype sqlite3:statement* void*) (define-ftype sqlite3:statement** (* sqlite3:statement*)) (define-record-type (&sqlite3 make-sqlite3-condition $sqlite3-condition?) (parent &condition) (fields (immutable status $sqlite3-condition-status))) (define-record-type database (fields (mutable ptr) (mutable busy-handler))) (define-record-type statement (fields (mutable ptr) (mutable database))) #;(record-writer (type-descriptor statement) (lambda (r p wr) (wr (if (statement-ptr r) (format "#<sqlite3:statement sql=~s>" (source-sql r)) "#<sqlite3:statement zombie>") p))) ;(define-check+error-type statement) ;;; Helpers ;; Conditions (define rtd (record-type-descriptor &sqlite3)) (define sqlite3-condition? (condition-predicate rtd)) (define sqlite3-status (condition-accessor rtd $sqlite3-condition-status)) (define (make-sqlite3-error-condition loc msg sta . args) (condition (make-sqlite3-condition sta) (make-who-condition loc) (make-message-condition msg) (make-irritants-condition args) )) (define (make-no-data-condition loc stmt params) (make-sqlite3-error-condition loc "the statement returned no data" 'done stmt params)) ;; Errors (define (abort-sqlite3-error loc db . args) (lambda (sta) (if (not (equal? sta 0)) (raise (apply make-sqlite3-error-condition loc (if db (sqlite3-errmsg db) "sta") sta args))))) (define (print-error-message obj port str) (display obj port) (display str port) (newline port)) (define (print-error msg obj) (print-error-message obj (current-error-port) (string-append "Error: " msg))) ;;; Database interface ;; Get any error message (define sqlite3_errmsg (foreign-procedure "sqlite3_errmsg" (sqlite3:database*) string)) (define (sqlite3-errmsg db) (check-database 'sqlite3-errmsg db) (sqlite3_errmsg (database-addr db))) ;; Open a database (define (open-database path) (assert (and open-database (string? path))) (let* ([ptr (make-ftype-pointer sqlite3:database** (foreign-alloc (ftype-sizeof sqlite3:database**)))] [f (foreign-procedure "sqlite3_open" (string void*) int)] [e (f path (ftype-pointer-address ptr))]) (if (< e 0) (abort-sqlite3-error 'open-database #f path) (make-database (ftype-&ref sqlite3:database** (*) ptr) #f)))) (define (check-database context db) (assert (and context (database? db)))) (define (check-statement context db) (assert (and context (statement? db)))) ;; Set application busy handler. Does not use a callback, so it is safe ;; to yield. Handler is called with DB, COUNT and LAST (the last value ;; it returned). Return true value to continue trying, or #f to stop. (define (set-busy-handler! db handler) (check-database 'set-busy-handler! db) (database-busy-handler-set! db handler)) (define (database-addr db) (ftype-pointer-address (database-ptr db))) (define (statement-addr stmt) (ftype-pointer-address (statement-ptr stmt))) ;; Cancel any running database operation as soon as possible (define (interrupt! db) (check-database 'interrupt! db) (let ([f (foreign-procedure "sqlite3_interrupt" (sqlite3:database*) void)]) (f (database-addr db)))) ;; Check whether the database is in autocommit mode (define (auto-committing? db) (check-database 'auto-committing? db) (let ([f (foreign-procedure "sqlite3_get_autocommit" (sqlite3:database*) boolean)]) (f (database-addr db)))) ;; Get the number of changes made to the database (define change-count (case-lambda [(db) (change-count db #f)] [(db total) (check-database 'change-count db) (let ([total-changes (foreign-procedure "sqlite3_total_changes" (sqlite3:database*) int)] [changes (foreign-procedure "sqlite3_changes" (sqlite3:database*) int)]) (if total (total-changes (database-addr db)) (changes (database-addr db))))])) ;; Get the row ID of the last inserted row (define (last-insert-rowid db) (check-database 'last-insert-rowid db) (let ([f (foreign-procedure "sqlite3_last_insert_rowid" (sqlite3:database*) int)]) (f (database-addr db)))) ;; Close a database or statement handle (define (sqlite3-finalize db) (check-database 'sqlite3-finalize db) (let* ([f (foreign-procedure "sqlite3_finalize" (sqlite3:database*) int)] [n (f (database-addr db))]) (database-ptr-set! db #f) n)) (define (sqlite3-next-stmt db) (check-database 'sqlite3-next-stmt db) (let* ([f (foreign-procedure "sqlite3_next_stmt" (sqlite3:database*) sqlite3:statement*)] [stmt* (f (database-addr db))]) (make-statement (make-ftype-pointer sqlite3:statement* stmt*) db))) (define (finalize! x) (warning 'finalize! "not implemented!")) ;; #;(define finalize! ;; (match-lambda* ;; [((? database? db) . finalize-statements?) ;; (cond ;; [(not (database-ptr db)) ;; (void)] ;; [(let loop ([stmt ;; (and ;; (optional finalize-statements? #f) ;; (sqlite3_next_stmt db #f))]) ;; (if stmt ;; (or (sqlite3_finalize stmt) (loop (sqlite3_next_stmt db stmt))) ;; ((foreign-safe-lambda sqlite3:status "sqlite3_close" sqlite3:database) db))) ;; => (abort-sqlite3-error 'finalize! db db)] ;; [else ;; (let ([id (pointer->address (database-ptr db))] ;; [release-qns (lambda (_ info) (object-release (vector-ref info 0)))]) ;; (call-with/synch *collations* ;; (cute hash-table-tree-clear! <> id release-qns)) ;; (call-with/synch *functions* ;; (cute hash-table-tree-clear! <> id release-qns)) ;; (database-ptr-set! db #f) ;; (database-busy-handler-set! db #f))])] ;; [((? statement? stmt)) ;; (cond ;; [(not (statement-ptr stmt)) ;; (void)] ;; [(sqlite3_finalize (statement-ptr stmt)) ;; => (abort-sqlite3-error 'finalize! (statement-database stmt) stmt)] ;; [else ;; (statement-ptr-set! stmt #f)])] ;; [(v . _) ;; (error-argument-type 'finalize! v "database or statement")])) ;;; Statement interface (define sqlite3_prepare_v2 (foreign-procedure "sqlite3_prepare_v2" ( sqlite3:database* u8* int void* u8*) int)) (define (alloc-statement*) (make-ftype-pointer sqlite3:statement** (foreign-alloc (ftype-sizeof sqlite3:statement**)))) ;; Create a new statement (define (prepare db sql) (check-database 'prepare db) (assert (and prepare (string? sql))) (let retry ([retries 0]) (let* ([ptr (alloc-statement*)] [zSql (string->utf8 sql)] [nByte (bytevector-length zSql)] [e (sqlite3_prepare_v2 (database-addr db) zSql nByte (ftype-pointer-address ptr) #f)]) (cond [(equal? e 0) (make-statement (ftype-&ref sqlite3:statement** (*) ptr) db)] [else (case (number->sqlite3:status e) [(busy) (let ([h (database-busy-handler db)]) (cond [(and h (h db retries)) (retry (fx+ retries 1))] [else ((abort-sqlite3-error 'prepare db db sql) e)]))] [else ((abort-sqlite3-error 'prepare db db sql) e)])])))) ;; Retrieve the SQL source code of a statement (define (source-sql stmt) (check-statement 'source-sql stmt) (let* ([f (foreign-procedure "sqlite3_sql" (sqlite3:statement*) string)]) (f (statement-addr stmt)))) (define (finalize-statement! stmt) (check-statement 'finalize-statement! stmt) (let* ([f (foreign-procedure "sqlite3_finalize" (sqlite3:statement*) int)] [n (f (statement-addr stmt))]) (statement-ptr-set! stmt #f) n)) ;; Reset an existing statement to process it again (define (reset! stmt) (check-statement 'reset! stmt) (cond [((foreign-procedure "sqlite3_reset" (sqlite3:statement*) int) (statement-addr stmt)) => (abort-sqlite3-error 'reset! (statement-database stmt) stmt)])) ;; Get number of bindable parameters (define (bind-parameter-count stmt) (check-statement 'bind-parameter-count stmt) ((foreign-procedure "sqlite3_bind_parameter_count" (sqlite3:statement*) int) (statement-addr stmt))) ;; Get index of a bindable parameter or #f if no parameter with the ;; given name exists (define (bind-parameter-index stmt name) (check-statement 'bind-parameter-index stmt) (let ([i ((foreign-procedure "sqlite3_bind_parameter_index" (sqlite3:statement* string) int) (statement-addr stmt) name)]) (if (zero? i) #f (fx- i 1)))) ;; Get the name of a bindable parameter (define (bind-parameter-name stmt i) (check-statement 'bind-parameter-name stmt) ((foreign-procedure "sqlite3_bind_parameter_name" (sqlite3:statement* int) string) (statement-addr stmt) (fx+ i 1))) ;; Bind data as parameters to an existing statement (define SQLITE_TRANSIENT -1) (define (bind! stmt i v) (check-statement 'bind! stmt) (assert (and bind! (number? i) (>= i 0))) (cond [(bytevector? v) (cond [((foreign-procedure "sqlite3_bind_blob" (sqlite3:statement* int u8* int void*) int) (statement-addr stmt) (fx+ i 1) v (bytevector-length v) SQLITE_TRANSIENT) => (abort-sqlite3-error 'bind! (statement-database stmt) stmt i v)])] [(or (and (fixnum? v) v) (and (boolean? v) (if v 1 0))) => (lambda (v) (cond [((foreign-procedure "sqlite3_bind_int" (sqlite3:statement* int int) int) (statement-addr stmt) (fx+ i 1) v) => (abort-sqlite3-error 'bind! (statement-database stmt) stmt i v)]))] [(real? v) (cond [((foreign-procedure "sqlite3_bind_double" (sqlite3:statement* int double) int) (statement-addr stmt) (fx+ i 1) v) => (abort-sqlite3-error 'bind! (statement-database stmt) stmt i v)])] [(string? v) (let ([f (foreign-procedure "sqlite3_bind_text" (sqlite3:statement* int u8* int void*) int)] [s (string->utf8 v)]) (cond [(f (statement-addr stmt) (fx+ i 1) s (bytevector-length s) SQLITE_TRANSIENT) => (abort-sqlite3-error 'bind! (statement-database stmt) stmt i v)]))] [(sql-null? v) (cond [((foreign-procedure "sqlite3_bind_null" (sqlite3:statement* int) int) (statement-addr stmt) (fx+ i 1)) => (abort-sqlite3-error 'bind! (statement-database stmt) stmt i)])] [else (error 'bind! "blob, number, boolean, string or sql-null" v)])) ; Helper (define (%bind-parameters! loc stmt params) (reset! stmt) (let ([cnt (bind-parameter-count stmt)] [vs (make-eq-hashtable)]) (let loop ([i 0] [params params]) (match params [((? symbol? k) v . rest) (cond [(bind-parameter-index stmt (string-append ":" (symbol->string k))) => (lambda (j) (hashtable-set! vs j v) (loop i rest))] [else (error loc "value or keyword matching a bind parameter name" k)])] [(v . rest) (hashtable-set! vs i v) (loop (fx+ i 1) rest)] [() (void)])) (if (= (hashtable-size vs) cnt) (unless (zero? cnt) (hashtable-walk vs (cut bind! stmt <> <>))) (raise (condition (make-who-condition loc) (make-message-condition (conc "bad parameter count - received " (hashtable-size vs) " but expected " cnt)) (make-sqlite3-condition 'error)))))) (define (bind-parameters! stmt . params) (%bind-parameters! 'bind-parameters! stmt params)) ;; Single-step a prepared statement, return #t if data is available, ;; #f otherwise (define (step! stmt) (check-statement 'step! stmt) (let ([db (statement-database stmt)]) (let retry ([retries 0]) (let ([s ((foreign-procedure "sqlite3_step" (sqlite3:statement*) int) (statement-addr stmt))]) (case (number->sqlite3:status s) [(row) #t] [(done) #f] [(busy) (let ([h (database-busy-handler db)]) (cond [(and h (h db retries)) (retry (fx+ retries 1))] [else ((abort-sqlite3-error 'step! db stmt) s)]))] [else ((abort-sqlite3-error 'step! db stmt) s)]))))) ;; Retrieve information from a prepared/stepped statement (define (column-count stmt) (check-statement 'column-count stmt) ((foreign-procedure "sqlite3_column_count" (sqlite3:statement*) int) (statement-addr stmt))) (define (column-type stmt i) (check-statement 'column-type stmt) (sqlite3:type-ref ((foreign-procedure "sqlite3_column_type" (sqlite3:statement* int) int) (statement-addr stmt) i))) (define (column-declared-type stmt i) (check-statement 'column-declared-type stmt) ((foreign-procedure "sqlite3_column_decltype" (sqlite3:statement* int) string) (statement-addr stmt) i)) (define (column-name stmt i) (check-statement 'column-name stmt) ((foreign-procedure "sqlite3_column_name" (sqlite3:statement* int) string) (statement-addr stmt) i)) (define sqlite3_column_double (foreign-procedure "sqlite3_column_double" (sqlite3:statement* int) double)) (define sqlite3_column_boolean (foreign-procedure "sqlite3_column_int" (sqlite3:statement* int) boolean)) (define (sqlite3-column-bytes stmt i) ((foreign-procedure "sqlite3_column_bytes" (sqlite3:statement* int) int) (statement-addr stmt) i)) (define (void*->bytevector ptr len) (define-ftype byte-array (array 0 unsigned-8)) (let ([arr (make-ftype-pointer byte-array ptr)] [bv (make-bytevector len)]) (let loop ((i 0)) (when (< i len) (bytevector-u8-set! bv i (ftype-ref byte-array (i) arr)) (loop (fx+ 1 i)))) bv)) (define (void*->string ptr len) (utf8->string (void*->bytevector ptr len))) (define (sqlite3-column-text stmt i) (let* ([ptr ((foreign-procedure "sqlite3_column_text" (sqlite3:statement* int) void*) (statement-addr stmt) i)] [len (sqlite3-column-bytes stmt i)]) (void*->string ptr len))) (define (sqlite3-column-blob stmt i) (let* ([ptr ((foreign-procedure "sqlite3_column_blob" (sqlite3:statement* int) void*) (statement-addr stmt) i)] [len (sqlite3-column-bytes stmt i)]) (void*->bytevector ptr len))) ;; Retrieve data from a stepped statement (define (column-data stmt i) (case (column-type stmt i) [(integer) (if (and-let* ([type (column-declared-type stmt i)]) (string-contains-ci type "bool")) (sqlite3_column_boolean (statement-addr stmt) i) (sqlite3_column_double (statement-addr stmt) i))] [(float) (sqlite3_column_double (statement-addr stmt) i)] [(text) (sqlite3-column-text stmt i)] [(blob) (sqlite3-column-blob stmt i)] [else (sql-null)])) ;;; Easy statement interface ;; Compile a statement and call a procedure on it, then finalize the ;; statement in a dynamic-wind exit block if it hasn't been finalized yet. (define (call-with-temporary-statements proc db . sqls) (check-database 'call-with-temporary-statements db) (let ([stmts #f] [exn #f]) (dynamic-wind (lambda () (unless stmts (set! stmts (map (cute prepare db <>) sqls)))) (lambda () (guard (e [else (set! exn e)]) (apply proc stmts))) (lambda () (and-let* ([s stmts]) (set! stmts #f) (for-each finalize! s)) ;; leaks if error occurs before last stmt (and-let* ([e exn]) (set! exn #f) (raise e)))))) (define-syntax %define/statement+params (syntax-rules () [(%define/statement+params ((name loc) (init ...) (stmt params)) body ...) (define name (let ([impl (lambda (init ... stmt params) body ...)]) (lambda (init ... db-or-stmt . params) (cond [(database? db-or-stmt) (call-with-temporary-statements (cute impl init ... <> (cdr params)) db-or-stmt (car params))] [(statement? db-or-stmt) (impl init ... db-or-stmt params)] [else (error loc "database or statement" db-or-stmt)]))))] [(%define/statement+params (name (init ...) (stmt params)) body ...) (%define/statement+params ((name 'name) (init ...) (stmt params)) body ...)] [(%define/statement+params (name stmt params) body ...) (%define/statement+params ((name 'name) () (stmt params)) body ...)])) ; from chicken miscmacros.scm (define-syntax while (syntax-rules () ((while test body ...) (let loop () (if test (begin body ... (loop))))))) ;; Step through a statement and ignore possible results (define (%execute loc stmt params) (%bind-parameters! loc stmt params) (while (step! stmt)) (void)) (%define/statement+params (execute stmt params) (%execute 'execute stmt params)) ;; Step through a statement, ignore possible results and return the ;; count of changes performed by this statement (%define/statement+params (update stmt params) (%execute 'update stmt params) (change-count (statement-database stmt))) ;; Return only the first column of the first result row produced by this ;; statement (%define/statement+params (first-result stmt params) (%bind-parameters! 'first-result stmt params) (if (step! stmt) (let ([r (column-data stmt 0)]) (reset! stmt) r) (raise (make-no-data-condition 'first-result stmt params)))) ;; Return only the first result row produced by this statement as a list (%define/statement+params (first-row stmt params) (%bind-parameters! 'first-row stmt params) (if (step! stmt) (map (cute column-data stmt <>) (iota (column-count stmt))) (raise (make-no-data-condition 'first-row stmt params)))) ;; Apply a procedure to the values of the result columns for each result row ;; while executing the statement and accumulating results. (%define/statement+params ((%fold-row loc) (loc proc init) (stmt params)) (%bind-parameters! loc stmt params) (let ([cl (iota (column-count stmt))]) (let loop ([acc init]) (if (step! stmt) (loop (apply proc acc (map (cute column-data stmt <>) cl))) acc)))) (define-syntax check-procedure (syntax-rules () [(_ loc proc) (assert (and loc (procedure? proc)))])) (define (fold-row proc init db-or-stmt . params) (apply %fold-row 'fold-row proc init db-or-stmt params)) ;; Apply a procedure to the values of the result columns for each result row ;; while executing the statement and discard the results (define (for-each-row proc db-or-stmt . params) (check-procedure fold-row proc) (apply %fold-row 'for-each-row (lambda (acc . columns) (apply proc columns)) (void) db-or-stmt params)) ;; Apply a procedure to the values of the result columns for each result row ;; while executing the statement and accumulate the results in a list (define (map-row proc db-or-stmt . params) (check-procedure 'map-row proc) (reverse! (apply %fold-row 'map-row (lambda (acc . columns) (cons (apply proc columns) acc)) '() db-or-stmt params))) ;;; Utility procedures ;; Run a thunk within a database transaction, commit if return value is ;; true, rollback if return value is false or the thunk is interrupted by ;; an exception (define with-transaction (case-lambda ((db thunk) (with-transaction db thunk 'deferred)) ((db thunk type) (check-database 'with-transaction db) (check-procedure 'with-transaction thunk) (unless (memq type '(deferred immediate exclusive)) (error 'with-transaction "bad argument: expected deferred, immediate or exclusive") type) (let ([success? #f] [exn #f]) (dynamic-wind (lambda () (execute db (string-append "BEGIN " (symbol->string type) " TRANSACTION;"))) (lambda () (guard (e [else (begin (print-error "with-transaction" exn) (set! exn e))]) (set! success? (thunk)) success?)) (lambda () (execute db (if success? "COMMIT TRANSACTION;" "ROLLBACK TRANSACTION;")) (and-let* ([e exn]) (set! exn #f) (raise e)))))))) ;; Check if the given string is a valid SQL statement (define sql-complete? (foreign-procedure "sqlite3_complete" (string) boolean)) ;; Return a descriptive version string (define database-version (foreign-procedure "sqlite3_libversion" () string)) ;; Return the amount of memory currently allocated by the database (define database-memory-used (foreign-procedure "sqlite3_memory_used" () int)) ;; Return the maximum amount of memory allocated by the database since ;; the counter was last reset (define database-memory-highwater (case-lambda (() (database-memory-highwater #f)) ((reset?) ((foreign-procedure "sqlite3_memory_highwater" (boolean) int) reset?)))) ;; Enables (disables) the sharing of the database cache and schema data ;; structures between connections to the same database. (define (enable-shared-cache! enable?) (cond-expand [disable-shared-cache #f] [else (cond [((foreign-procedure "sqlite3_enable_shared_cache" (boolean) int) enable?) => (abort-sqlite3-error 'enable-shared-cache! #f)] [else enable?])])) ;; Enables (disables) the loading of native extensions using SQL statements. (define (enable-load-extension! db enable?) (cond-expand [disable-load-extension #f] [else (cond [((foreign-procedure "sqlite3_enable_load_extension" (sqlite3:database* boolean) int) (database-addr db) enable?) => (abort-sqlite3-error 'enable-load-extension! db)] [else enable?])])) (record-writer (type-descriptor database) (lambda (r p wr) (wr (if (database-ptr r) "#<sqlite3:database>" "#<sqlite3:database zombie>") p))) |
Added sqlite3.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
;;;; sqlite3.scm ;;;; :tabSize=2:indentSize=2:noTabs=true: ;;;; bindings to the SQLite3 database library #!chezscheme (library (sqlite3) (export ;; procedures open-database ;define-collation ;define-function set-busy-handler! ;make-busy-timeout interrupt! auto-committing? change-count last-insert-rowid finalize! prepare source-sql reset! bind-parameter-count bind-parameter-index bind-parameter-name bind! bind-parameters! step! column-count column-type column-declared-type column-name column-data call-with-temporary-statements execute update first-result first-row fold-row for-each-row map-row with-transaction sql-complete? database-version database-memory-used database-memory-highwater enable-shared-cache! enable-load-extension!) (import (chezscheme) (srfi s0 cond-expand) (srfi s2 and-let) (matchable) (only (srfi s13 strings) string-contains-ci) (srfi s11 let-values) (srfi s26 cut) (sql-null)) (include "sqlite3.scm") (load-shared-object "libsqlite3.so.0") ) ; library sqlite3 |
Added srfi/LICENSE.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
The following license applies to all files written by Derick Eddington, unless otherwise stated. =========================================================================== Copyright (c) 2008-2009 Derick Eddington Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. Except as contained in this notice, the name(s) of the above copyright holders shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. =========================================================================== Files written by others retain any copyright, license, and/or other notice they originally had. |
Added srfi/README.
> > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
There is an existing R6RS srfi project at: https://code.launchpad.net/~scheme-libraries-team/scheme-libraries/srfi In that project, the library names use the colon character. E.g.: (srfi :1 lists) Filenames with a colon are not portable across UNIX and Windows. Some Scheme implementations support an encoding whereby ':1' is mapped to '%3a1'. Chez Scheme does not perform the conversion. The surfage libraries are a port of the R6RS srfi libraries to have names such as: (surfage s1 lists) |
Added srfi/private/OS-id-features.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi private OS-id-features) (export OS-id-features) (import (rnrs)) (define (OS-id-features OS-id features-alist) (define OS-id-len (string-length OS-id)) (define (OS-id-contains? str) (define str-len (string-length str)) (let loop ((i 0)) (and (<= (+ i str-len) OS-id-len) (or (string-ci=? str (substring OS-id i (+ i str-len))) (loop (+ 1 i)))))) (apply append (map cdr (filter (lambda (x) (OS-id-contains? (car x))) features-alist)))) ) |
Added srfi/private/feature-cond.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
#!r6rs ;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi private feature-cond) (export feature-cond) (import (rnrs) (srfi private registry)) (define-syntax feature-cond (lambda (stx) (define (identifier?/name=? x n) (and (identifier? x) (symbol=? n (syntax->datum x)))) (define (make-test t) (define (invalid-test) (syntax-violation #F "invalid test syntax" stx t)) (syntax-case t () ((c x ...) (identifier?/name=? (syntax c) (quote and)) (cons (syntax and) (map make-test (syntax (x ...))))) ((c x ...) (identifier?/name=? (syntax c) (quote or)) (cons (syntax or) (map make-test (syntax (x ...))))) ((c x ...) (identifier?/name=? (syntax c) (quote not)) (if (= 1 (length (syntax (x ...)))) (list (syntax not) (make-test (car (syntax (x ...))))) (invalid-test))) (datum (not (and (identifier? (syntax datum)) (memq (syntax->datum (syntax datum)) (quote (and or not else))))) (syntax (and (member (quote datum) available-features) #T))) (_ (invalid-test)))) (syntax-case stx () ((_ (test . exprs) ... (e . eexprs)) (identifier?/name=? (syntax e) (quote else)) (with-syntax (((clause ...) (map cons (map make-test (syntax (test ...))) (syntax (exprs ...))))) (syntax (cond clause ... (else . eexprs))))) ((kw (test . exprs) ...) (syntax (kw (test . exprs) ... (else (no-clause-true)))))))) (define (no-clause-true) (assertion-violation (quote feature-cond) "no clause true")) ) |
Added srfi/private/include.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi private include) (export include/resolve) (import (rnrs) (for (srfi private include compat) expand)) (define-syntax include/resolve (lambda (stx) (define (include/lexical-context ctxt filename) (with-exception-handler (lambda (ex) (raise (condition (make-error) (make-who-condition 'include/resolve) (make-message-condition "error while trying to include") (make-irritants-condition (list filename)) (if (condition? ex) ex (make-irritants-condition (list ex)))))) (lambda () (call-with-input-file filename (lambda (fip) (let loop ([a '()]) (let ([x (read fip)]) (if (eof-object? x) (cons #'begin (datum->syntax ctxt (reverse a))) (loop (cons x a)))))))))) (syntax-case stx () [(ctxt (lib-path* ...) file-path) (for-all (lambda (s) (and (string? s) (positive? (string-length s)))) (syntax->datum #'(lib-path* ... file-path))) (let ([p (apply string-append (map (lambda (ps) (string-append "/" ps)) (syntax->datum #'(lib-path* ... file-path))))] [sp (search-paths)]) (let loop ([search sp]) (if (null? search) (error 'include/resolve "cannot find file in search paths" (substring p 1 (string-length p)) sp) (let ([full (string-append (car search) p)]) (if (file-exists? full) (include/lexical-context #'ctxt full) (loop (cdr search)))))))]))) ) |
Added srfi/private/include/compat.chezscheme.sls.
> > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 |
(library (srfi private include compat) (export search-paths) (import (chezscheme)) (define (search-paths) (map car (library-directories))) ) |
Added srfi/private/include/compat.ikarus.sls.
> > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi private include compat) (export search-paths) (import (rnrs base) (only (ikarus) library-path)) (define (search-paths) (library-path)) ) |
Added srfi/private/include/compat.larceny.sls.
> > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi private include compat) (export search-paths) (import (rnrs base) (primitives current-require-path getenv absolute-path-string?)) (define (search-paths) (let ([larceny-root (getenv "LARCENY_ROOT")]) (map (lambda (crp) (if (absolute-path-string? crp) crp (string-append larceny-root "/" crp))) (current-require-path)))) ) |
Added srfi/private/include/compat.mosh.sls.
> > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 |
(library (srfi private include compat) (export search-paths) (import (rnrs base) (only (mosh) library-path)) (define (search-paths) (library-path)) ) |
Added srfi/private/include/compat.mzscheme.sls.
> > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi private include compat) (export search-paths) (import (rnrs base) (only (scheme base) current-library-collection-paths path->string) (only (scheme mpair) list->mlist)) (define (search-paths) (map path->string (list->mlist (current-library-collection-paths)))) ) |
Added srfi/private/include/compat.ypsilon.sls.
> > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi private include compat) (export search-paths) (import (rnrs base) (only (core) scheme-library-paths)) (define (search-paths) (scheme-library-paths)) ) |
Added srfi/private/let-opt.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
#!r6rs ;;; LET-OPTIONALS macros ;;; Copyright (c) 2001 by Olin Shivers. ;;; Copyright (c) 1993-2003 Richard Kelsey and Jonathan Rees ;;; Copyright (c) 1994-2003 by Olin Shivers and Brian D. Carlstrom. ;;; Copyright (c) 1999-2003 by Martin Gasbichler. ;;; Copyright (c) 2001-2003 by Michael Sperber. ;;; ;;; All rights reserved. ;;; ;;; Redistribution and use in source and binary forms, with or without ;;; modification, are permitted provided that the following conditions ;;; are met: ;;; 1. Redistributions of source code must retain the above copyright ;;; notice, this list of conditions and the following disclaimer. ;;; 2. Redistributions in binary form must reproduce the above copyright ;;; notice, this list of conditions and the following disclaimer in the ;;; documentation and/or other materials provided with the distribution. ;;; 3. The name of the authors may not be used to endorse or promote products ;;; derived from this software without specific prior written permission. ;;; ;;; THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR ;;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ;;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ;;; IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, ;;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ;;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ;;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ;;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ;;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ;;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ;;; Made into an R6RS library by Derick Eddington. (library (srfi private let-opt) (export let-optionals* :optional) (import (rnrs)) ;;; (:optional rest-arg default-exp [test-pred]) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; This form is for evaluating optional arguments and their defaults ;;; in simple procedures that take a *single* optional argument. It is ;;; a macro so that the default will not be computed unless it is needed. ;;; ;;; REST-ARG is a rest list from a lambda -- e.g., R in ;;; (lambda (a b . r) ...) ;;; - If REST-ARG has 0 elements, evaluate DEFAULT-EXP and return that. ;;; - If REST-ARG has 1 element, return that element. ;;; - If REST-ARG has >1 element, error. ;;; ;;; If there is an TEST-PRED form, it is a predicate that is used to test ;;; a non-default value. If the predicate returns false, an error is raised. (define-syntax :optional (syntax-rules () ([_ rest default-exp] (let ((maybe-arg rest)) (if (pair? maybe-arg) (if (null? (cdr maybe-arg)) (car maybe-arg) (error ':optional "too many optional arguments" maybe-arg)) default-exp))) ([_ rest default-exp arg-test] (let ((maybe-arg rest)) (if (pair? maybe-arg) (if (null? (cdr maybe-arg)) (let ((val (car maybe-arg))) (if (arg-test val) val (error ':optional "optional argument failed test" val))) (error ':optional "too many optional arguments" maybe-arg)) default-exp))))) ; erutcurts-enifed ;;; Here is a simpler but less-efficient version of LET-OPTIONALS*. ;;; It redundantly performs end-of-list checks for every optional var, ;;; even after the list runs out. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define-syntax let-optionals* (syntax-rules () ((let-optionals* arg (opt-clause ...) body ...) (let ((rest arg)) (%let-optionals* rest (opt-clause ...) (let () body ...)))))) ;;; The arg-list expression *must* be a variable. ;;; (Or must be side-effect-free, in any event.) (define-syntax %let-optionals* (syntax-rules () ((%let-optionals* arg (((var ...) xparser) opt-clause ...) body ...) (call-with-values (lambda () (xparser arg)) (lambda (rest var ...) (%let-optionals* rest (opt-clause ...) body ...)))) ((%let-optionals* arg ((var default) opt-clause ...) body ...) (call-with-values (lambda () (if (null? arg) (values default '()) (values (car arg) (cdr arg)))) (lambda (var rest) (%let-optionals* rest (opt-clause ...) body ...)))) ((%let-optionals* arg ((var default test) opt-clause ...) body ...) (call-with-values (lambda () (if (null? arg) (values default '()) (let ((var (car arg))) (if test (values var (cdr arg)) (error 'let-optionals* "arg failed LET-OPT test" var))))) (lambda (var rest) (%let-optionals* rest (opt-clause ...) body ...)))) ((%let-optionals* arg ((var default test supplied?) opt-clause ...) body ...) (call-with-values (lambda () (if (null? arg) (values default #f '()) (let ((var (car arg))) (if test (values var #t (cdr arg)) (error 'let-optionals* "arg failed LET-OPT test" var))))) (lambda (var supplied? rest) (%let-optionals* rest (opt-clause ...) body ...)))) ((%let-optionals* arg (rest) body ...) (let ((rest arg)) body ...)) ((%let-optionals* arg () body ...) (if (null? arg) (begin body ...) (error 'let-optionals* "too many arguments in let-opt" arg))))) ; erutcurts-enifed ) |
Added srfi/private/make-aliased-libraries.sps.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
#!r6rs (import (rnrs) (only (srfi private registry) available-features) (only (xitomatl lists) map/filter) (only (xitomatl match) match-lambda) (only (xitomatl common) format fprintf printf) (only (xitomatl strings) string-intersperse) (only (xitomatl predicates) symbol<?) (only (xitomatl environments) environment environment-symbols)) (define srfi-libraries/mnemonics (map/filter (match-lambda ;; NOTE: Uses only the 3-element names. ((:and ('srfi (:symbol ":(\\d+)" num) _) name) (list (string->number (symbol->string num)) name)) (_ #F)) available-features)) (define alias-template ";; Automatically generated by ~a #!r6rs (library ~s (export ~a) (import ~s) ) ") (define program-name (car (command-line))) (for-each (lambda (x) (let* ((srfi-num (car x)) (lib-name (cadr x)) (exports (list-sort symbol<? (environment-symbols (environment lib-name)))) (alias-name `(srfi ,(string->symbol (format ":~d" srfi-num)))) (out-file (format "~d.sls" srfi-num))) (cond ((file-exists? out-file) (printf "Skipping ~a because it already exists.\n" out-file)) (else (call-with-output-file out-file (lambda (fop) (fprintf fop alias-template program-name alias-name (string-intersperse (map symbol->string exports) "\n ") lib-name))) (printf "~a\n" out-file))))) srfi-libraries/mnemonics) |
Added srfi/private/platform-features.chezscheme.sls.
> > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi private platform-features) (export OS-features implementation-features) (import (rnrs) (only (chezscheme) machine-type) (srfi private OS-id-features)) (define (OS-features) (OS-id-features (symbol->string (machine-type)) '(("i3la" linux posix)))) (define (implementation-features) '(chezscheme)) ) |
Added srfi/private/platform-features.ikarus.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi private platform-features) (export OS-features implementation-features) (import (rnrs) (only (ikarus) host-info) (srfi private OS-id-features)) (define (OS-features) (OS-id-features (host-info) '(("linux" linux posix) ("solaris" solaris posix) ("darwin" darwin posix) ("bsd" bsd) ("freebsd" freebsd posix) ("openbsd" openbsd posix) ("cygwin" cygwin posix) ;; correct? ("gnu" gnu)))) (define (implementation-features) '(ikarus)) ) |
Added srfi/private/platform-features.larceny.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi private platform-features) (export OS-features implementation-features) (import (rnrs base) (rnrs lists) (primitives system-features) (srfi private OS-id-features)) (define (OS-features) (OS-id-features (cdr (assq 'os-name (system-features))) '(("linux" linux posix) ("solaris" solaris posix) ("darwin" darwin posix) ("bsd" bsd) ("freebsd" freebsd posix) ("openbsd" openbsd posix) ("windows" windows)))) (define (implementation-features) '(larceny)) ) |
Added srfi/private/platform-features.mosh.sls.
> > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
(library (srfi private platform-features) (export OS-features implementation-features) (import (only (rnrs) define quote) (only (mosh) host-os) (srfi private OS-id-features)) (define (OS-features) (OS-id-features (host-os) '(("linux" linux posix) ("bsd" linux posix) ("darwin" darwin posix)))) (define (implementation-features) '(mosh))) |
Added srfi/private/platform-features.mzscheme.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi private platform-features) (export OS-features implementation-features) (import (rnrs) (only (scheme base) system-type) (srfi private OS-id-features)) (define (OS-features) (OS-id-features (string-append (symbol->string (system-type 'os)) " " (system-type 'machine)) '(("linux" linux posix) ("macosx" mac-os-x darwin posix) ("solaris" solaris posix) ("gnu" gnu) ("bsd" bsd) ("freebsd" freebsd posix) ("openbsd" openbsd posix) ("windows" windows)))) (define (implementation-features) '(mzscheme)) ) |
Added srfi/private/platform-features.ypsilon.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi private platform-features) (export OS-features implementation-features) (import (rnrs) (only (core) architecture-feature) (srfi private OS-id-features)) (define (OS-features) (OS-id-features (architecture-feature 'operating-system) '(("linux" linux posix) ("solaris" solaris posix) ("darwin" darwin posix) ("bsd" bsd) ("freebsd" freebsd posix) ("openbsd" openbsd posix) ("windows" windows)))) (define (implementation-features) '(ypsilon)) ) |
Added srfi/private/registry.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi private registry) (export available-features) (import (rnrs) (srfi private platform-features)) (define available-features (let-syntax ((SRFI-features (lambda (stx) (define SRFIs '((0 cond-expand) (1 lists) (2 and-let*) #;(5 let) (6 basic-string-ports) (8 receive) (9 records) (11 let-values) (13 strings) (14 char-sets) (16 case-lambda) #;(17 generalized-set!) #;(18 multithreading) (19 time) #;(21 real-time-multithreading) (23 error) (25 multi-dimensional-arrays) (26 cut) (27 random-bits) #;(28 basic-format-strings) #;(29 localization) (31 rec) (37 args-fold) (38 with-shared-structure) (39 parameters) (41 streams) (42 eager-comprehensions) (43 vectors) #;(44 collections) #;(45 lazy) #;(46 syntax-rules) #;(47 arrays) (48 intermediate-format-strings) #;(51 rest-values) #;(54 cat) #;(57 records) #;(59 vicinities) #;(60 integer-bits) (61 cond) #;(63 arrays) (64 testing) #;(66 octet-vectors) (67 compare-procedures) (69 basic-hash-tables) #;(71 let) #;(74 blobs) (78 lightweight-testing) #;(86 mu-and-nu) #;(87 case) #;(95 sorting-and-merging) (98 os-environment-variables) (99 records))) (define (make-feature-names x) (define number car) (define mnemonic cdr) (define (make-symbol . args) (string->symbol (apply string-append (map (lambda (a) (if (symbol? a) (symbol->string a) a)) args)))) (let* ((n-str (number->string (number x))) (colon-n (make-symbol ":" n-str)) (srfi-n (make-symbol "srfi-" n-str)) (srfi-n-m (apply make-symbol srfi-n (map (lambda (m) (make-symbol "-" m)) (mnemonic x))))) ;; The first two are recommended by SRFI-97. ;; The last two are the two types of SRFI-97 library name. (list srfi-n srfi-n-m `(srfi ,colon-n) `(srfi ,colon-n . ,(mnemonic x))))) (syntax-case stx () ((kw) #`(quote #,(datum->syntax #'kw (apply append (map make-feature-names SRFIs))))))))) `(,@(OS-features) ,@(implementation-features) ,@(SRFI-features) r6rs))) ) |
Added srfi/private/vanish.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#!r6rs ;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi private vanish) (export vanish-define) (import (rnrs) (for (only (rnrs base) begin) (meta -1))) #;(define (show stx) (display (make-string 60 #\-)) (newline) (write (syntax->datum stx)) (newline)) (define-syntax vanish-define (lambda (stx) (syntax-case stx () ((_ def (vanish ...)) (for-all identifier? #'(vanish ...)) #'(make-vanish-define (syntax def) (syntax vanish) ...))))) (define (make-vanish-define def . to-vanish) (lambda (stx) (define (vanish? id) (memp (lambda (x) (free-identifier=? id x)) to-vanish)) #;(show stx) (syntax-case stx () ((_ name . _) (and (identifier? #'name) (vanish? #'name)) #'(begin)) ((_ (name . _) . _) (and (identifier? #'name) (vanish? #'name)) #'(begin)) ((_ . r) (cons def #'r))))) ) |
Added srfi/s0/cond-expand.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s0 cond-expand) (export cond-expand) (import (rnrs) (for (srfi private registry) expand)) (define-syntax cond-expand (lambda (stx) (syntax-case stx (and or not else) [(_) (syntax-violation #f "Unfulfilled cond-expand" stx)] [(_ (else body ...)) #'(begin body ...)] [(_ ((and) body ...) more-clauses ...) #'(begin body ...)] [(_ ((and req1 req2 ...) body ...) more-clauses ...) #'(cond-expand (req1 (cond-expand ((and req2 ...) body ...) more-clauses ...)) more-clauses ...)] [(_ ((or) body ...) more-clauses ...) #'(cond-expand more-clauses ...)] [(_ ((or req1 req2 ...) body ...) more-clauses ...) #'(cond-expand (req1 (begin body ...)) (else (cond-expand ((or req2 ...) body ...) more-clauses ...)))] [(_ ((not req) body ...) more-clauses ...) #'(cond-expand (req (cond-expand more-clauses ...)) (else body ...))] [(_ (feature-id body ...) more-clauses ...) (if (member (syntax->datum #'feature-id) available-features) #'(begin body ...) #'(cond-expand more-clauses ...))]))) ) |
Added srfi/s1/lists.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 |
#!r6rs ;; Copyright (c) 1998, 1999 by Olin Shivers. You may do as you please with ;; this code as long as you do not remove this copyright notice or ;; hold me liable for its use. Please send bug reports to shivers@ai.mit.edu. ;; -Olin ;; Ikarus porting begun by Abdulaziz Ghuloum, ;; and continued by Derick Eddington. (library (srfi s1 lists) (export xcons make-list list-tabulate list-copy proper-list? circular-list? dotted-list? not-pair? null-list? list= circular-list length+ iota first second third fourth fifth sixth seventh eighth ninth tenth car+cdr take drop take-right drop-right take! drop-right! split-at split-at! last last-pair zip unzip1 unzip2 unzip3 unzip4 unzip5 count append! append-reverse append-reverse! concatenate concatenate! unfold fold pair-fold reduce unfold-right pair-fold-right reduce-right append-map append-map! map! pair-for-each filter-map map-in-order filter! partition! remove! find-tail any every list-index take-while drop-while take-while! span break span! break! delete delete! alist-cons alist-copy delete-duplicates delete-duplicates! alist-delete alist-delete! reverse! lset<= lset= lset-adjoin lset-union lset-intersection lset-difference lset-xor lset-diff+intersection lset-union! lset-intersection! lset-difference! lset-xor! lset-diff+intersection! ;; re-exported: append assq assv caaaar caaadr caaar caadar caaddr caadr caar cadaar cadadr cadar caddar cadddr caddr cadr car cdaaar cdaadr cdaar cdadar cdaddr cdadr cdar cddaar cddadr cddar cdddar cddddr cdddr cddr cdr cons cons* length list list-ref memq memv null? pair? reverse set-car! set-cdr! ;; different than R6RS: assoc filter find fold-right for-each map member partition remove) (import (except (rnrs) assoc error filter find fold-right for-each map member partition remove) (rnrs mutable-pairs)) (define-syntax check-arg (lambda (stx) (syntax-case stx () [(_ pred val caller) (and (identifier? #'val) (identifier? #'caller)) #'(unless (pred val) (assertion-violation 'caller "check-arg failed" val))]))) (define (error . args) (if (and (<= 2 (length args)) (symbol? (car args)) (string? (cadr args))) (apply assertion-violation args) (apply assertion-violation "(library (srfi s1 lists))" "misuse of error procedure" args))) ;; Constructors ;; ;;;;;;;;;;;;; (define (xcons d a) (cons a d)) (define (make-list len . maybe-elt) (check-arg (lambda (n) (and (integer? n) (>= n 0))) len make-list) (let ((elt (cond ((null? maybe-elt) #f) ; Default value ((null? (cdr maybe-elt)) (car maybe-elt)) (else (error 'make-list "Too many arguments" (cons len maybe-elt)))))) (do ((i len (- i 1)) (ans '() (cons elt ans))) ((<= i 0) ans)))) (define (list-tabulate len proc) (check-arg (lambda (n) (and (integer? n) (>= n 0))) len list-tabulate) (check-arg procedure? proc list-tabulate) (do ((i (- len 1) (- i 1)) (ans '() (cons (proc i) ans))) ((< i 0) ans))) (define (list-copy lis) (let recur ((lis lis)) (if (pair? lis) (cons (car lis) (recur (cdr lis))) lis))) (define iota (case-lambda [(count) (iota count 0 1)] [(count start) (iota count start 1)] [(count start step) (check-arg integer? count iota) (if (< count 0) (error 'iota "Negative step count" count)) (check-arg number? start iota) (check-arg number? step iota) (let ((last-val (+ start (* (- count 1) step)))) (do ((count count (- count 1)) (val last-val (- val step)) (ans '() (cons val ans))) ((<= count 0) ans)))])) (define (circular-list val1 . vals) (let ((ans (cons val1 vals))) (set-cdr! (last-pair ans) ans) ans)) (define (proper-list? x) (let lp ((x x) (lag x)) (if (pair? x) (let ((x (cdr x))) (if (pair? x) (let ((x (cdr x)) (lag (cdr lag))) (and (not (eq? x lag)) (lp x lag))) (null? x))) (null? x)))) (define (dotted-list? x) (let lp ((x x) (lag x)) (if (pair? x) (let ((x (cdr x))) (if (pair? x) (let ((x (cdr x)) (lag (cdr lag))) (and (not (eq? x lag)) (lp x lag))) (not (null? x)))) (not (null? x))))) (define (circular-list? x) (let lp ((x x) (lag x)) (and (pair? x) (let ((x (cdr x))) (and (pair? x) (let ((x (cdr x)) (lag (cdr lag))) (or (eq? x lag) (lp x lag)))))))) (define (not-pair? x) (not (pair? x))) ; Inline me. (define (null-list? l) (cond ((pair? l) #f) ((null? l) #t) (else (error 'null-list? "argument out of domain" l)))) (define (list= elt= . lists) (or (null? lists) ; special case (let lp1 ((list-a (car lists)) (others (cdr lists))) (or (null? others) (let ((list-b-orig (car others)) (others (cdr others))) (if (eq? list-a list-b-orig) ; EQ? => LIST= (lp1 list-b-orig others) (let lp2 ((list-a list-a) (list-b list-b-orig)) (if (null-list? list-a) (and (null-list? list-b) (lp1 list-b-orig others)) (and (not (null-list? list-b)) (elt= (car list-a) (car list-b)) (lp2 (cdr list-a) (cdr list-b))))))))))) (define (length+ x) ; Returns #f if X is circular. (let lp ((x x) (lag x) (len 0)) (if (pair? x) (let ((x (cdr x)) (len (+ len 1))) (if (pair? x) (let ((x (cdr x)) (lag (cdr lag)) (len (+ len 1))) (and (not (eq? x lag)) (lp x lag len))) len)) len))) (define (zip list1 . more-lists) (apply map list list1 more-lists)) ;; Selectors ;; ;;;;;;;;;; (define first car) (define second cadr) (define third caddr) (define fourth cadddr) (define (fifth x) (car (cddddr x))) (define (sixth x) (cadr (cddddr x))) (define (seventh x) (caddr (cddddr x))) (define (eighth x) (cadddr (cddddr x))) (define (ninth x) (car (cddddr (cddddr x)))) (define (tenth x) (cadr (cddddr (cddddr x)))) (define (car+cdr pair) (values (car pair) (cdr pair))) (define (take lis k) (check-arg integer? k take) (let recur ((lis lis) (k k)) (if (zero? k) '() (cons (car lis) (recur (cdr lis) (- k 1)))))) (define (drop lis k) (check-arg integer? k drop) (let iter ((lis lis) (k k)) (if (zero? k) lis (iter (cdr lis) (- k 1))))) (define (take! lis k) (check-arg integer? k take!) (if (zero? k) '() (begin (set-cdr! (drop lis (- k 1)) '()) lis))) (define (take-right lis k) (check-arg integer? k take-right) (let lp ((lag lis) (lead (drop lis k))) (if (pair? lead) (lp (cdr lag) (cdr lead)) lag))) (define (drop-right lis k) (check-arg integer? k drop-right) (let recur ((lag lis) (lead (drop lis k))) (if (pair? lead) (cons (car lag) (recur (cdr lag) (cdr lead))) '()))) (define (drop-right! lis k) (check-arg integer? k drop-right!) (let ((lead (drop lis k))) (if (pair? lead) (let lp ((lag lis) (lead (cdr lead))) ; Standard case (if (pair? lead) (lp (cdr lag) (cdr lead)) (begin (set-cdr! lag '()) lis))) '()))) ; Special case dropping everything -- no cons to side-effect. (define-syntax receive (syntax-rules () [(_ (id* ...) expr body body* ...) (let-values ([(id* ...) expr]) body body* ...)])) (define (split-at x k) (check-arg integer? k split-at) (let recur ((lis x) (k k)) (if (zero? k) (values '() lis) (receive (prefix suffix) (recur (cdr lis) (- k 1)) (values (cons (car lis) prefix) suffix))))) (define (split-at! x k) (check-arg integer? k split-at!) (if (zero? k) (values '() x) (let* ((prev (drop x (- k 1))) (suffix (cdr prev))) (set-cdr! prev '()) (values x suffix)))) (define (last lis) (car (last-pair lis))) (define (last-pair lis) (check-arg pair? lis last-pair) (let lp ((lis lis)) (let ((tail (cdr lis))) (if (pair? tail) (lp tail) lis)))) ;; Unzippers -- 1 through 5 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (unzip1 lis) (map car lis)) (define (unzip2 lis) (let recur ((lis lis)) (if (null-list? lis) (values lis lis) ; Use NOT-PAIR? to handle (let ((elt (car lis))) ; dotted lists. (receive (a b) (recur (cdr lis)) (values (cons (car elt) a) (cons (cadr elt) b))))))) (define (unzip3 lis) (let recur ((lis lis)) (if (null-list? lis) (values lis lis lis) (let ((elt (car lis))) (receive (a b c) (recur (cdr lis)) (values (cons (car elt) a) (cons (cadr elt) b) (cons (caddr elt) c))))))) (define (unzip4 lis) (let recur ((lis lis)) (if (null-list? lis) (values lis lis lis lis) (let ((elt (car lis))) (receive (a b c d) (recur (cdr lis)) (values (cons (car elt) a) (cons (cadr elt) b) (cons (caddr elt) c) (cons (cadddr elt) d))))))) (define (unzip5 lis) (let recur ((lis lis)) (if (null-list? lis) (values lis lis lis lis lis) (let ((elt (car lis))) (receive (a b c d e) (recur (cdr lis)) (values (cons (car elt) a) (cons (cadr elt) b) (cons (caddr elt) c) (cons (cadddr elt) d) (cons (car (cddddr elt)) e))))))) ;; append! append-reverse append-reverse! concatenate concatenate! ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (append! . lists) ;; First, scan through lists looking for a non-empty one. (let lp ((lists lists) (prev '())) (if (not (pair? lists)) prev (let ((first (car lists)) (rest (cdr lists))) (if (not (pair? first)) (lp rest first) ;; Now, do the splicing. (let lp2 ((tail-cons (last-pair first)) (rest rest)) (if (pair? rest) (let ((next (car rest)) (rest (cdr rest))) (set-cdr! tail-cons next) (lp2 (if (pair? next) (last-pair next) tail-cons) rest)) first))))))) (define (append-reverse rev-head tail) (let lp ((rev-head rev-head) (tail tail)) (if (null-list? rev-head) tail (lp (cdr rev-head) (cons (car rev-head) tail))))) (define (append-reverse! rev-head tail) (let lp ((rev-head rev-head) (tail tail)) (if (null-list? rev-head) tail (let ((next-rev (cdr rev-head))) (set-cdr! rev-head tail) (lp next-rev rev-head))))) (define (concatenate lists) (reduce-right append '() lists)) (define (concatenate! lists) (reduce-right append! '() lists)) ;; Fold/map internal utilities ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (%cdrs lists) (call-with-current-continuation (lambda (abort) (let recur ((lists lists)) (if (pair? lists) (let ((lis (car lists))) (if (null-list? lis) (abort '()) (cons (cdr lis) (recur (cdr lists))))) '()))))) (define (%cars+ lists last-elt) ; (append! (map car lists) (list last-elt)) (let recur ((lists lists)) (if (pair? lists) (cons (caar lists) (recur (cdr lists))) (list last-elt)))) (define (%cars+cdrs lists) (let f ([ls lists] [a* '()] [d* '()]) (cond [(pair? ls) (let ([a (car ls)]) (if (pair? a) (f (cdr ls) (cons (car a) a*) (cons (cdr a) d*)) (values '() '())))] [else (values (reverse a*) (reverse d*))]))) (define (%cars+cdrs+ lists cars-final) (call-with-current-continuation (lambda (abort) (let recur ((lists lists)) (if (pair? lists) (receive (list other-lists) (car+cdr lists) (if (null-list? list) (abort '() '()) ; LIST is empty -- bail out (receive (a d) (car+cdr list) (receive (cars cdrs) (recur other-lists) (values (cons a cars) (cons d cdrs)))))) (values (list cars-final) '())))))) (define (%cars+cdrs/no-test lists) (let recur ((lists lists)) (if (pair? lists) (receive (list other-lists) (car+cdr lists) (receive (a d) (car+cdr list) (receive (cars cdrs) (recur other-lists) (values (cons a cars) (cons d cdrs))))) (values '() '())))) ;; count ;; ;;;;;; (define (count pred list1 . lists) (check-arg procedure? pred count) (if (pair? lists) ;; N-ary case (let lp ((list1 list1) (lists lists) (i 0)) (if (null-list? list1) i (receive (as ds) (%cars+cdrs lists) (if (null? as) i (lp (cdr list1) ds (if (apply pred (car list1) as) (+ i 1) i)))))) ;; Fast path (let lp ((lis list1) (i 0)) (if (null-list? lis) i (lp (cdr lis) (if (pred (car lis)) (+ i 1) i)))))) ;; fold/unfold ;; ;;;;;;;;;;;; (define unfold-right (case-lambda [(p f g seed) (unfold-right p f g seed '())] [(p f g seed tail) (check-arg procedure? p unfold-right) (check-arg procedure? f unfold-right) (check-arg procedure? g unfold-right) (let lp ((seed seed) (ans tail)) (if (p seed) ans (lp (g seed) (cons (f seed) ans))))])) (define (unfold p f g seed . maybe-tail-gen) (check-arg procedure? p unfold) (check-arg procedure? f unfold) (check-arg procedure? g unfold) (if (pair? maybe-tail-gen) ;;; so much for :optional (aghuloum) (let ((tail-gen (car maybe-tail-gen))) (if (pair? (cdr maybe-tail-gen)) (apply error 'unfold "Too many arguments" p f g seed maybe-tail-gen) (let recur ((seed seed)) (if (p seed) (tail-gen seed) (cons (f seed) (recur (g seed))))))) (let recur ((seed seed)) (if (p seed) '() (cons (f seed) (recur (g seed))))))) (define (fold kons knil lis1 . lists) (check-arg procedure? kons fold) (if (pair? lists) (let lp ((lists (cons lis1 lists)) (ans knil)) ; N-ary case (receive (cars+ans cdrs) (%cars+cdrs+ lists ans) (if (null? cars+ans) ans ; Done. (lp cdrs (apply kons cars+ans))))) (let lp ((lis lis1) (ans knil)) ; Fast path (if (null-list? lis) ans (lp (cdr lis) (kons (car lis) ans)))))) (define (fold-right kons knil lis1 . lists) (check-arg procedure? kons fold-right) (if (pair? lists) (let recur ((lists (cons lis1 lists))) ; N-ary case (let ((cdrs (%cdrs lists))) (if (null? cdrs) knil (apply kons (%cars+ lists (recur cdrs)))))) (let recur ((lis lis1)) ; Fast path (if (null-list? lis) knil (let ((head (car lis))) (kons head (recur (cdr lis)))))))) (define (pair-fold-right f zero lis1 . lists) (check-arg procedure? f pair-fold-right) (if (pair? lists) (let recur ((lists (cons lis1 lists))) ; N-ary case (let ((cdrs (%cdrs lists))) (if (null? cdrs) zero (apply f (append! lists (list (recur cdrs))))))) (let recur ((lis lis1)) ; Fast path (if (null-list? lis) zero (f lis (recur (cdr lis))))))) (define (pair-fold f zero lis1 . lists) (check-arg procedure? f pair-fold) (if (pair? lists) (let lp ((lists (cons lis1 lists)) (ans zero)) ; N-ary case (let ((tails (%cdrs lists))) (if (null? tails) ans (lp tails (apply f (append! lists (list ans))))))) (let lp ((lis lis1) (ans zero)) (if (null-list? lis) ans (let ((tail (cdr lis))) ; Grab the cdr now, (lp tail (f lis ans))))))) ; in case F SET-CDR!s LIS. ;; REDUCE and REDUCE-RIGHT only use RIDENTITY in the empty-list case. ;; These cannot meaningfully be n-ary. (define (reduce f ridentity lis) (check-arg procedure? f reduce) (if (null-list? lis) ridentity (fold f (car lis) (cdr lis)))) (define (reduce-right f ridentity lis) (check-arg procedure? f reduce-right) (if (null-list? lis) ridentity (let recur ((head (car lis)) (lis (cdr lis))) (if (pair? lis) (f head (recur (car lis) (cdr lis))) head)))) ;; Mappers: append-map append-map! pair-for-each map! filter-map map-in-order ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (append-map f lis1 . lists) (check-arg procedure? f append-map) (really-append-map append f lis1 lists)) (define (append-map! f lis1 . lists) (check-arg procedure? f append-map!) (really-append-map append! f lis1 lists)) (define (really-append-map appender f lis1 lists) (if (pair? lists) (receive (cars cdrs) (%cars+cdrs (cons lis1 lists)) (if (null? cars) '() (let recur ((cars cars) (cdrs cdrs)) (let ((vals (apply f cars))) (receive (cars2 cdrs2) (%cars+cdrs cdrs) (if (null? cars2) vals (appender vals (recur cars2 cdrs2)))))))) ;; Fast path (if (null-list? lis1) '() (let recur ((elt (car lis1)) (rest (cdr lis1))) (let ((vals (f elt))) (if (null-list? rest) vals (appender vals (recur (car rest) (cdr rest))))))))) (define (pair-for-each proc lis1 . lists) (check-arg procedure? proc pair-for-each) (if (pair? lists) (let lp ((lists (cons lis1 lists))) (let ((tails (%cdrs lists))) (if (pair? tails) (begin (apply proc lists) (lp tails))))) ;; Fast path. (let lp ((lis lis1)) (if (not (null-list? lis)) (let ((tail (cdr lis))) ; Grab the cdr now, (proc lis) ; in case PROC SET-CDR!s LIS. (lp tail)))))) ;; We stop when LIS1 runs out, not when any list runs out. (define (map! f lis1 . lists) (check-arg procedure? f map!) (if (pair? lists) (let lp ((lis1 lis1) (lists lists)) (if (not (null-list? lis1)) (receive (heads tails) (%cars+cdrs/no-test lists) (set-car! lis1 (apply f (car lis1) heads)) (lp (cdr lis1) tails)))) ;; Fast path. (pair-for-each (lambda (pair) (set-car! pair (f (car pair)))) lis1)) lis1) ;; Map F across L, and save up all the non-false results. (define (filter-map f lis1 . lists) (check-arg procedure? f filter-map) (if (pair? lists) (let recur ((lists (cons lis1 lists))) (receive (cars cdrs) (%cars+cdrs lists) (if (pair? cars) (cond ((apply f cars) => (lambda (x) (cons x (recur cdrs)))) (else (recur cdrs))) ; Tail call in this arm. '()))) ;; Fast path. (let recur ((lis lis1)) (if (null-list? lis) lis (let ((tail (recur (cdr lis)))) (cond ((f (car lis)) => (lambda (x) (cons x tail))) (else tail))))))) ;; Map F across lists, guaranteeing to go left-to-right. ;; NOTE: Some implementations of R5RS MAP are compliant with this spec; ;; in which case this procedure may simply be defined as a synonym for MAP. (define (map-in-order f lis1 . lists) (check-arg procedure? f map-in-order) (if (pair? lists) (let recur ((lists (cons lis1 lists))) (receive (cars cdrs) (%cars+cdrs lists) (if (pair? cars) (let ((x (apply f cars))) ; Do head first, (cons x (recur cdrs))) ; then tail. '()))) ;; Fast path. (let recur ((lis lis1)) (if (null-list? lis) lis (let ((tail (cdr lis)) (x (f (car lis)))) ; Do head first, (cons x (recur tail))))))) ; then tail. ;; We extend MAP to handle arguments of unequal length. (define map map-in-order) ;; Contributed by Michael Sperber since it was missing from the ;; reference implementation. (define (for-each f lis1 . lists) (if (pair? lists) (let recur ((lists (cons lis1 lists))) (receive (cars cdrs) (%cars+cdrs lists) (if (pair? cars) (begin (apply f cars) ; Do head first, (recur cdrs))))) ; then tail. ;; Fast path. (let recur ((lis lis1)) (if (not (null-list? lis)) (begin (f (car lis)) ; Do head first, (recur (cdr lis))))))) ; then tail. ;; filter, remove, partition ;; ;;;;;;;;;;;;;;;;;;;;;;;;;; ;; FILTER, REMOVE, PARTITION and their destructive counterparts do not ;; disorder the elements of their argument. ;; This FILTER shares the longest tail of L that has no deleted elements. ;; If Scheme had multi-continuation calls, they could be made more efficient. (define (filter pred lis) ; Sleazing with EQ? makes this (check-arg procedure? pred filter) ; one faster. (let recur ((lis lis)) (if (null-list? lis) lis ; Use NOT-PAIR? to handle dotted lists. (let ((head (car lis)) (tail (cdr lis))) (if (pred head) (let ((new-tail (recur tail))) ; Replicate the RECUR call so (if (eq? tail new-tail) lis (cons head new-tail))) (recur tail)))))) ; this one can be a tail call. (define (filter! pred lis) (check-arg procedure? pred filter!) (let lp ((ans lis)) (cond ((null-list? ans) ans) ; Scan looking for ((not (pred (car ans))) (lp (cdr ans))) ; first cons of result. (else (letrec ((scan-in (lambda (prev lis) (if (pair? lis) (if (pred (car lis)) (scan-in lis (cdr lis)) (scan-out prev (cdr lis)))))) (scan-out (lambda (prev lis) (let lp ((lis lis)) (if (pair? lis) (if (pred (car lis)) (begin (set-cdr! prev lis) (scan-in lis (cdr lis))) (lp (cdr lis))) (set-cdr! prev lis)))))) (scan-in ans (cdr ans)) ans))))) (define (partition pred lis) (check-arg procedure? pred partition) (let recur ((lis lis)) (if (null-list? lis) (values lis lis) ; Use NOT-PAIR? to handle dotted lists. (let ((elt (car lis)) (tail (cdr lis))) (receive (in out) (recur tail) (if (pred elt) (values (if (pair? out) (cons elt in) lis) out) (values in (if (pair? in) (cons elt out) lis)))))))) (define (partition! pred lis) (check-arg procedure? pred partition!) (if (null-list? lis) (values lis lis) (letrec ((scan-in (lambda (in-prev out-prev lis) (let lp ((in-prev in-prev) (lis lis)) (if (pair? lis) (if (pred (car lis)) (lp lis (cdr lis)) (begin (set-cdr! out-prev lis) (scan-out in-prev lis (cdr lis)))) (set-cdr! out-prev lis))))) ; Done. (scan-out (lambda (in-prev out-prev lis) (let lp ((out-prev out-prev) (lis lis)) (if (pair? lis) (if (pred (car lis)) (begin (set-cdr! in-prev lis) (scan-in lis out-prev (cdr lis))) (lp lis (cdr lis))) (set-cdr! in-prev lis)))))) ; Done. ;; Crank up the scan&splice loops. (if (pred (car lis)) ;; LIS begins in-list. Search for out-list's first pair. (let lp ((prev-l lis) (l (cdr lis))) (cond ((not (pair? l)) (values lis l)) ((pred (car l)) (lp l (cdr l))) (else (scan-out prev-l l (cdr l)) (values lis l)))) ; Done. ;; LIS begins out-list. Search for in-list's first pair. (let lp ((prev-l lis) (l (cdr lis))) (cond ((not (pair? l)) (values l lis)) ((pred (car l)) (scan-in l prev-l (cdr l)) (values l lis)) ; Done. (else (lp l (cdr l))))))))) ;; Inline us, please. (define (remove pred l) (filter (lambda (x) (not (pred x))) l)) (define (remove! pred l) (filter! (lambda (x) (not (pred x))) l)) (define delete (case-lambda [(x lis) (delete x lis equal?)] [(x lis =) (filter (lambda (y) (not (= x y))) lis)])) (define delete! (case-lambda [(x lis) (delete! x lis equal?)] [(x lis =) (filter! (lambda (y) (not (= x y))) lis)])) ;; Extended from R4RS to take an optional comparison argument. (define member (case-lambda [(x lis) (member x lis equal?)] [(x lis =) (find-tail (lambda (y) (= x y)) lis)])) (define delete-duplicates (case-lambda [(lis) (delete-duplicates lis equal?)] [(lis elt=) (check-arg procedure? elt= delete-duplicates) (let recur ((lis lis)) (if (null-list? lis) lis (let* ((x (car lis)) (tail (cdr lis)) (new-tail (recur (delete x tail elt=)))) (if (eq? tail new-tail) lis (cons x new-tail)))))])) (define delete-duplicates! (case-lambda [(lis) (delete-duplicates! lis equal?)] [(lis elt=) (check-arg procedure? elt= delete-duplicates!) (let recur ((lis lis)) (if (null-list? lis) lis (let* ((x (car lis)) (tail (cdr lis)) (new-tail (recur (delete! x tail elt=)))) (when (not (eq? tail new-tail)) (set-cdr! lis new-tail)) lis)))])) ;; alist stuff ;; ;;;;;;;;;;;; (define assoc (case-lambda [(x lis) (assoc x lis equal?)] [(x lis =) (find (lambda (entry) (= x (car entry))) lis)])) (define (alist-cons key datum alist) (cons (cons key datum) alist)) (define (alist-copy alist) (map (lambda (elt) (cons (car elt) (cdr elt))) alist)) (define alist-delete (case-lambda [(key alist) (alist-delete key alist equal?)] [(key alist =) (filter (lambda (elt) (not (= key (car elt)))) alist)])) (define alist-delete! (case-lambda [(key alist) (alist-delete! key alist equal?)] [(key alist =) (filter! (lambda (elt) (not (= key (car elt)))) alist)])) ;; find find-tail take-while drop-while span break any every list-index ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (find pred list) (cond ((find-tail pred list) => car) (else #f))) (define (find-tail pred list) (check-arg procedure? pred find-tail) (let lp ((list list)) (and (not (null-list? list)) (if (pred (car list)) list (lp (cdr list)))))) (define (take-while pred lis) (check-arg procedure? pred take-while) (let recur ((lis lis)) (if (null-list? lis) '() (let ((x (car lis))) (if (pred x) (cons x (recur (cdr lis))) '()))))) (define (drop-while pred lis) (check-arg procedure? pred drop-while) (let lp ((lis lis)) (if (null-list? lis) '() (if (pred (car lis)) (lp (cdr lis)) lis)))) (define (take-while! pred lis) (check-arg procedure? pred take-while!) (if (or (null-list? lis) (not (pred (car lis)))) '() (begin (let lp ((prev lis) (rest (cdr lis))) (if (pair? rest) (let ((x (car rest))) (if (pred x) (lp rest (cdr rest)) (set-cdr! prev '()))))) lis))) (define (span pred lis) (check-arg procedure? pred span) (let recur ((lis lis)) (if (null-list? lis) (values '() '()) (let ((x (car lis))) (if (pred x) (receive (prefix suffix) (recur (cdr lis)) (values (cons x prefix) suffix)) (values '() lis)))))) (define (span! pred lis) (check-arg procedure? pred span!) (if (or (null-list? lis) (not (pred (car lis)))) (values '() lis) (let ((suffix (let lp ((prev lis) (rest (cdr lis))) (if (null-list? rest) rest (let ((x (car rest))) (if (pred x) (lp rest (cdr rest)) (begin (set-cdr! prev '()) rest))))))) (values lis suffix)))) (define (break pred lis) (span (lambda (x) (not (pred x))) lis)) (define (break! pred lis) (span! (lambda (x) (not (pred x))) lis)) (define (any pred lis1 . lists) (check-arg procedure? pred any) (if (pair? lists) ;; N-ary case (receive (heads tails) (%cars+cdrs (cons lis1 lists)) (and (pair? heads) (let lp ((heads heads) (tails tails)) (receive (next-heads next-tails) (%cars+cdrs tails) (if (pair? next-heads) (or (apply pred heads) (lp next-heads next-tails)) (apply pred heads)))))) ; Last PRED app is tail call. ;; Fast path (and (not (null-list? lis1)) (let lp ((head (car lis1)) (tail (cdr lis1))) (if (null-list? tail) (pred head) ; Last PRED app is tail call. (or (pred head) (lp (car tail) (cdr tail)))))))) (define every (case-lambda [(p ls) (or (null-list? ls) (let f ([p p] [a (car ls)] [d (cdr ls)]) (cond [(pair? d) (and (p a) (f p (car d) (cdr d)))] [else (p a)])))] [(p ls1 ls2) (cond [(and (pair? ls1) (pair? ls2)) (let f ([p p] [a1 (car ls1)] [d1 (cdr ls1)] [a2 (car ls2)] [d2 (cdr ls2)]) (cond [(and (pair? d1) (pair? d2)) (and (p a1 a2) (f p (car d1) (cdr d1) (car d2) (cdr d2)))] [else (p a1 a2)]))] [else #t])] [(pred lis1 . lists) (receive (heads tails) (%cars+cdrs (cons lis1 lists)) (or (not (pair? heads)) (let lp ((heads heads) (tails tails)) (receive (next-heads next-tails) (%cars+cdrs tails) (if (pair? next-heads) (and (apply pred heads) (lp next-heads next-tails)) (apply pred heads))))))])) (define (list-index pred lis1 . lists) (check-arg procedure? pred list-index) (if (pair? lists) ;; N-ary case (let lp ((lists (cons lis1 lists)) (n 0)) (receive (heads tails) (%cars+cdrs lists) (and (pair? heads) (if (apply pred heads) n (lp tails (+ n 1)))))) ;; Fast path (let lp ((lis lis1) (n 0)) (and (not (null-list? lis)) (if (pred (car lis)) n (lp (cdr lis) (+ n 1))))))) ;; Reverse ;; ;;;;;;;; (define (reverse! lis) (let lp ((lis lis) (ans '())) (if (null-list? lis) ans (let ((tail (cdr lis))) (set-cdr! lis ans) (lp tail lis))))) ;; Lists-as-sets ;; ;;;;;;;;;;;;;; (define (%lset2<= = lis1 lis2) (every (lambda (x) (member x lis2 =)) lis1)) (define (lset<= = . lists) (check-arg procedure? = lset<=) (or (not (pair? lists)) ; 0-ary case (let lp ((s1 (car lists)) (rest (cdr lists))) (or (not (pair? rest)) (let ((s2 (car rest)) (rest (cdr rest))) (and (or (eq? s2 s1) ; Fast path (%lset2<= = s1 s2)) ; Real test (lp s2 rest))))))) (define (lset= = . lists) (check-arg procedure? = lset=) (or (not (pair? lists)) ; 0-ary case (let lp ((s1 (car lists)) (rest (cdr lists))) (or (not (pair? rest)) (let ((s2 (car rest)) (rest (cdr rest))) (and (or (eq? s1 s2) ; Fast path (and (%lset2<= = s1 s2) (%lset2<= = s2 s1))) ; Real test (lp s2 rest))))))) (define (lset-adjoin = lis . elts) (check-arg procedure? = lset-adjoin) (fold (lambda (elt ans) (if (member elt ans =) ans (cons elt ans))) lis elts)) (define (lset-union = . lists) (check-arg procedure? = lset-union) (reduce (lambda (lis ans) ; Compute ANS + LIS. (cond ((null? lis) ans) ; Don't copy any lists ((null? ans) lis) ; if we don't have to. ((eq? lis ans) ans) (else (fold (lambda (elt ans) (if (any (lambda (x) (= x elt)) ans) ans (cons elt ans))) ans lis)))) '() lists)) (define (lset-union! = . lists) (check-arg procedure? = lset-union!) (reduce (lambda (lis ans) ; Splice new elts of LIS onto the front of ANS. (cond ((null? lis) ans) ; Don't copy any lists ((null? ans) lis) ; if we don't have to. ((eq? lis ans) ans) (else (pair-fold (lambda (pair ans) (let ((elt (car pair))) (if (any (lambda (x) (= x elt)) ans) ans (begin (set-cdr! pair ans) pair)))) ans lis)))) '() lists)) (define (lset-intersection = lis1 . lists) (check-arg procedure? = lset-intersection) (let ((lists (delete lis1 lists eq?))) ; Throw out any LIS1 vals. (cond ((any null-list? lists) '()) ; Short cut ((null? lists) lis1) ; Short cut (else (filter (lambda (x) (every (lambda (lis) (member x lis =)) lists)) lis1))))) (define (lset-intersection! = lis1 . lists) (check-arg procedure? = lset-intersection!) (let ((lists (delete lis1 lists eq?))) ; Throw out any LIS1 vals. (cond ((any null-list? lists) '()) ; Short cut ((null? lists) lis1) ; Short cut (else (filter! (lambda (x) (every (lambda (lis) (member x lis =)) lists)) lis1))))) (define (lset-difference = lis1 . lists) (check-arg procedure? = lset-difference) (let ((lists (filter pair? lists))) ; Throw out empty lists. (cond ((null? lists) lis1) ; Short cut ((memq lis1 lists) '()) ; Short cut (else (filter (lambda (x) (every (lambda (lis) (not (member x lis =))) lists)) lis1))))) (define (lset-difference! = lis1 . lists) (check-arg procedure? = lset-difference!) (let ((lists (filter pair? lists))) ; Throw out empty lists. (cond ((null? lists) lis1) ; Short cut ((memq lis1 lists) '()) ; Short cut (else (filter! (lambda (x) (every (lambda (lis) (not (member x lis =))) lists)) lis1))))) (define (lset-xor = . lists) (check-arg procedure? = lset-xor) (reduce (lambda (b a) ; Compute A xor B: ;; Note that this code relies on the constant-time ;; short-cuts provided by LSET-DIFF+INTERSECTION, ;; LSET-DIFFERENCE & APPEND to provide constant-time short ;; cuts for the cases A = (), B = (), and A eq? B. It takes ;; a careful case analysis to see it, but it's carefully ;; built in. ;; Compute a-b and a^b, then compute b-(a^b) and ;; cons it onto the front of a-b. (receive (a-b a-int-b) (lset-diff+intersection = a b) (cond ((null? a-b) (lset-difference = b a)) ((null? a-int-b) (append b a)) (else (fold (lambda (xb ans) (if (member xb a-int-b =) ans (cons xb ans))) a-b b))))) '() lists)) (define (lset-xor! = . lists) (check-arg procedure? = lset-xor!) (reduce (lambda (b a) ; Compute A xor B: ;; Note that this code relies on the constant-time ;; short-cuts provided by LSET-DIFF+INTERSECTION, ;; LSET-DIFFERENCE & APPEND to provide constant-time short ;; cuts for the cases A = (), B = (), and A eq? B. It takes ;; a careful case analysis to see it, but it's carefully ;; built in. ;; Compute a-b and a^b, then compute b-(a^b) and ;; cons it onto the front of a-b. (receive (a-b a-int-b) (lset-diff+intersection! = a b) (cond ((null? a-b) (lset-difference! = b a)) ((null? a-int-b) (append! b a)) (else (pair-fold (lambda (b-pair ans) (if (member (car b-pair) a-int-b =) ans (begin (set-cdr! b-pair ans) b-pair))) a-b b))))) '() lists)) (define (lset-diff+intersection = lis1 . lists) (check-arg procedure? = lset-diff+intersection) (cond ((every null-list? lists) (values lis1 '())) ; Short cut ((memq lis1 lists) (values '() lis1)) ; Short cut (else (partition (lambda (elt) (not (any (lambda (lis) (member elt lis =)) lists))) lis1)))) (define (lset-diff+intersection! = lis1 . lists) (check-arg procedure? = lset-diff+intersection!) (cond ((every null-list? lists) (values lis1 '())) ; Short cut ((memq lis1 lists) (values '() lis1)) ; Short cut (else (partition! (lambda (elt) (not (any (lambda (lis) (member elt lis =)) lists))) lis1)))) ;; end of library ) |
Added srfi/s1/lists.so.
cannot compute difference between binary files
Added srfi/s101/random-access-lists.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 |
#!r6rs ;; SRFI 101: Purely Functional Random-Access Pairs and Lists ;; Copyright (c) David Van Horn 2009. All Rights Reserved. ;; Permission is hereby granted, free of charge, to any person obtaining ;; a copy of this software and associated documentation ;; files (the "Software"), to deal in the Software without restriction, ;; including without limitation the rights to use, copy, modify, merge, ;; publish, distribute, sublicense, and/or sell copies of the Software, ;; and to permit persons to whom the Software is furnished to do so, ;; subject to the following conditions: ;; The above copyright notice and this permission notice shall be ;; included in all copies or substantial portions of the Software. ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ;; NONINFRINGEMENT. REMEMBER, THERE IS NO SCHEME UNDERGROUND. IN NO EVENT ;; SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, ;; DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR ;; OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR ;; THE USE OR OTHER DEALINGS IN THE SOFTWARE. (library (srfi s101 random-access-lists) (export (rename (ra:quote quote) (ra:pair? pair?) (ra:cons cons) (ra:car car) (ra:cdr cdr) (ra:caar caar) (ra:cadr cadr) (ra:cddr cddr) (ra:cdar cdar) (ra:caaar caaar) (ra:caadr caadr) (ra:caddr caddr) (ra:cadar cadar) (ra:cdaar cdaar) (ra:cdadr cdadr) (ra:cdddr cdddr) (ra:cddar cddar) (ra:caaaar caaaar) (ra:caaadr caaadr) (ra:caaddr caaddr) (ra:caadar caadar) (ra:cadaar cadaar) (ra:cadadr cadadr) (ra:cadddr cadddr) (ra:caddar caddar) (ra:cdaaar cdaaar) (ra:cdaadr cdaadr) (ra:cdaddr cdaddr) (ra:cdadar cdadar) (ra:cddaar cddaar) (ra:cddadr cddadr) (ra:cddddr cddddr) (ra:cdddar cdddar) (ra:null? null?) (ra:list? list?) (ra:list list) (ra:make-list make-list) (ra:length length) (ra:append append) (ra:reverse reverse) (ra:list-tail list-tail) (ra:list-ref list-ref) (ra:list-set list-set) (ra:list-ref/update list-ref/update) (ra:map map) (ra:for-each for-each) (ra:random-access-list->linear-access-list random-access-list->linear-access-list) (ra:linear-access-list->random-access-list linear-access-list->random-access-list))) (import (rnrs base) (rnrs lists) (rnrs control) (rnrs hashtables) (rnrs records syntactic) (rnrs arithmetic bitwise)) (define-record-type kons (fields size tree rest)) (define-record-type node (fields val left right)) ;; Nat -> Nat (define (sub1 n) (- n 1)) (define (add1 n) (+ n 1)) ;; [Tree X] -> X (define (tree-val t) (if (node? t) (node-val t) t)) ;; [X -> Y] [Tree X] -> [Tree Y] (define (tree-map f t) (if (node? t) (make-node (f (node-val t)) (tree-map f (node-left t)) (tree-map f (node-right t))) (f t))) ;; [X -> Y] [Tree X] -> unspecified (define (tree-for-each f t) (if (node? t) (begin (f (node-val t)) (tree-for-each f (node-left t)) (tree-for-each f (node-right t))) (f t))) ;; [X Y Z ... -> R] [List [Tree X] [Tree Y] [Tree Z] ...] -> [Tree R] (define (tree-map/n f ts) (let recr ((ts ts)) (if (and (pair? ts) (node? (car ts))) (make-node (apply f (map node-val ts)) (recr (map node-left ts)) (recr (map node-right ts))) (apply f ts)))) ;; [X Y Z ... -> R] [List [Tree X] [Tree Y] [Tree Z] ...] -> unspecified (define (tree-for-each/n f ts) (let recr ((ts ts)) (if (and (pair? ts) (node? (car ts))) (begin (apply f (map node-val ts)) (recr (map node-left ts)) (recr (map node-right ts))) (apply f ts)))) ;; Nat [Nat -> X] -> [Tree X] ;; like build-list, but for complete binary trees (define (build-tree i f) ;; i = 2^j-1 (let rec ((i i) (o 0)) (if (= 1 i) (f o) (let ((i/2 (half i))) (make-node (f o) (rec i/2 (add1 o)) (rec i/2 (+ 1 o i/2))))))) ;; Consumes n = 2^i-1 and produces 2^(i-1)-1. ;; Nat -> Nat (define (half n) (bitwise-arithmetic-shift n -1)) ;; Nat X -> [Tree X] (define (tr:make-tree i x) ;; i = 2^j-1 (let recr ((i i)) (if (= 1 i) x (let ((n (recr (half i)))) (make-node x n n))))) ;; Nat [Tree X] Nat [X -> X] -> X [Tree X] (define (tree-ref/update mid t i f) (cond ((zero? i) (if (node? t) (values (node-val t) (make-node (f (node-val t)) (node-left t) (node-right t))) (values t (f t)))) ((<= i mid) (let-values (((v* t*) (tree-ref/update (half (sub1 mid)) (node-left t) (sub1 i) f))) (values v* (make-node (node-val t) t* (node-right t))))) (else (let-values (((v* t*) (tree-ref/update (half (sub1 mid)) (node-right t) (sub1 (- i mid)) f))) (values v* (make-node (node-val t) (node-left t) t*)))))) ;; Special-cased above to avoid logarathmic amount of cons'ing ;; and any multi-values overhead. Operates in constant space. ;; [Tree X] Nat Nat -> X ;; invariant: (= mid (half (sub1 (tree-count t)))) (define (tree-ref/a t i mid) (cond ((zero? i) (tree-val t)) ((<= i mid) (tree-ref/a (node-left t) (sub1 i) (half (sub1 mid)))) (else (tree-ref/a (node-right t) (sub1 (- i mid)) (half (sub1 mid)))))) ;; Nat [Tree X] Nat -> X ;; invariant: (= size (tree-count t)) (define (tree-ref size t i) (if (zero? i) (tree-val t) (tree-ref/a t i (half (sub1 size))))) ;; Nat [Tree X] Nat [X -> X] -> [Tree X] (define (tree-update size t i f) (let recr ((mid (half (sub1 size))) (t t) (i i)) (cond ((zero? i) (if (node? t) (make-node (f (node-val t)) (node-left t) (node-right t)) (f t))) ((<= i mid) (make-node (node-val t) (recr (half (sub1 mid)) (node-left t) (sub1 i)) (node-right t))) (else (make-node (node-val t) (node-left t) (recr (half (sub1 mid)) (node-right t) (sub1 (- i mid)))))))) ;; ------------------------ ;; Random access lists ;; [RaListof X] (define ra:null (quote ())) ;; [Any -> Boolean] (define ra:pair? kons?) ;; [Any -> Boolean] (define ra:null? null?) ;; X [RaListof X] -> [RaListof X] /\ ;; X Y -> [RaPair X Y] (define (ra:cons x ls) (if (kons? ls) (let ((s (kons-size ls))) (if (and (kons? (kons-rest ls)) (= (kons-size (kons-rest ls)) s)) (make-kons (+ 1 s s) (make-node x (kons-tree ls) (kons-tree (kons-rest ls))) (kons-rest (kons-rest ls))) (make-kons 1 x ls))) (make-kons 1 x ls))) ;; [RaPair X Y] -> X Y (define ra:car+cdr (lambda (p) (assert (kons? p)) (if (node? (kons-tree p)) (let ((s* (half (kons-size p)))) (values (tree-val (kons-tree p)) (make-kons s* (node-left (kons-tree p)) (make-kons s* (node-right (kons-tree p)) (kons-rest p))))) (values (kons-tree p) (kons-rest p))))) ;; [RaPair X Y] -> X (define (ra:car p) (call-with-values (lambda () (ra:car+cdr p)) (lambda (car cdr) car))) ;; [RaPair X Y] -> Y (define (ra:cdr p) (call-with-values (lambda () (ra:car+cdr p)) (lambda (car cdr) cdr))) ;; [RaListof X] Nat [X -> X] -> X [RaListof X] (define (ra:list-ref/update ls i f) ;(assert (< i (ra:length ls))) (let recr ((xs ls) (j i)) (if (< j (kons-size xs)) (let-values (((v* t*) (tree-ref/update (half (sub1 (kons-size xs))) (kons-tree xs) j f))) (values v* (make-kons (kons-size xs) t* (kons-rest xs)))) (let-values (((v* r*) (recr (kons-rest xs) (- j (kons-size xs))))) (values v* (make-kons (kons-size xs) (kons-tree xs) r*)))))) ;; [RaListof X] Nat [X -> X] -> [RaListof X] (define (ra:list-update ls i f) ;(assert (< i (ra:length ls))) (let recr ((xs ls) (j i)) (let ((s (kons-size xs))) (if (< j s) (make-kons s (tree-update s (kons-tree xs) j f) (kons-rest xs)) (make-kons s (kons-tree xs) (recr (kons-rest xs) (- j s))))))) ;; [RaListof X] Nat X -> (values X [RaListof X]) (define (ra:list-ref/set ls i v) (ra:list-ref/update ls i (lambda (_) v))) ;; X ... -> [RaListof X] (define (ra:list . xs) (fold-right ra:cons ra:null xs)) ;; Nat X -> [RaListof X] (define ra:make-list (case-lambda ((k) (ra:make-list k 0)) ((k obj) (let loop ((n k) (a ra:null)) (cond ((zero? n) a) (else (let ((t (largest-skew-binary n))) (loop (- n t) (make-kons t (tr:make-tree t obj) a))))))))) ;; A Skew is a Nat 2^k-1 with k > 0. ;; Skew -> Skew (define (skew-succ t) (add1 (bitwise-arithmetic-shift t 1))) ;; Computes the largest skew binary term t <= n. ;; Nat -> Skew (define (largest-skew-binary n) (if (= 1 n) 1 (let* ((t (largest-skew-binary (half n))) (s (skew-succ t))) (if (> s n) t s)))) ;; [Any -> Boolean] ;; Is x a PROPER list? (define (ra:list? x) (or (ra:null? x) (and (kons? x) (ra:list? (kons-rest x))))) (define ra:caar (lambda (ls) (ra:car (ra:car ls)))) (define ra:cadr (lambda (ls) (ra:car (ra:cdr ls)))) (define ra:cddr (lambda (ls) (ra:cdr (ra:cdr ls)))) (define ra:cdar (lambda (ls) (ra:cdr (ra:car ls)))) (define ra:caaar (lambda (ls) (ra:car (ra:car (ra:car ls))))) (define ra:caadr (lambda (ls) (ra:car (ra:car (ra:cdr ls))))) (define ra:caddr (lambda (ls) (ra:car (ra:cdr (ra:cdr ls))))) (define ra:cadar (lambda (ls) (ra:car (ra:cdr (ra:car ls))))) (define ra:cdaar (lambda (ls) (ra:cdr (ra:car (ra:car ls))))) (define ra:cdadr (lambda (ls) (ra:cdr (ra:car (ra:cdr ls))))) (define ra:cdddr (lambda (ls) (ra:cdr (ra:cdr (ra:cdr ls))))) (define ra:cddar (lambda (ls) (ra:cdr (ra:cdr (ra:car ls))))) (define ra:caaaar (lambda (ls) (ra:car (ra:car (ra:car (ra:car ls)))))) (define ra:caaadr (lambda (ls) (ra:car (ra:car (ra:car (ra:cdr ls)))))) (define ra:caaddr (lambda (ls) (ra:car (ra:car (ra:cdr (ra:cdr ls)))))) (define ra:caadar (lambda (ls) (ra:car (ra:car (ra:cdr (ra:car ls)))))) (define ra:cadaar (lambda (ls) (ra:car (ra:cdr (ra:car (ra:car ls)))))) (define ra:cadadr (lambda (ls) (ra:car (ra:cdr (ra:car (ra:cdr ls)))))) (define ra:cadddr (lambda (ls) (ra:car (ra:cdr (ra:cdr (ra:cdr ls)))))) (define ra:caddar (lambda (ls) (ra:car (ra:cdr (ra:cdr (ra:car ls)))))) (define ra:cdaaar (lambda (ls) (ra:cdr (ra:car (ra:car (ra:car ls)))))) (define ra:cdaadr (lambda (ls) (ra:cdr (ra:car (ra:car (ra:cdr ls)))))) (define ra:cdaddr (lambda (ls) (ra:cdr (ra:car (ra:cdr (ra:cdr ls)))))) (define ra:cdadar (lambda (ls) (ra:cdr (ra:car (ra:cdr (ra:car ls)))))) (define ra:cddaar (lambda (ls) (ra:cdr (ra:cdr (ra:car (ra:car ls)))))) (define ra:cddadr (lambda (ls) (ra:cdr (ra:cdr (ra:car (ra:cdr ls)))))) (define ra:cddddr (lambda (ls) (ra:cdr (ra:cdr (ra:cdr (ra:cdr ls)))))) (define ra:cdddar (lambda (ls) (ra:cdr (ra:cdr (ra:cdr (ra:car ls)))))) ;; [RaList X] -> Nat (define (ra:length ls) (assert (ra:list? ls)) (let recr ((ls ls)) (if (kons? ls) (+ (kons-size ls) (recr (kons-rest ls))) 0))) (define (make-foldl empty? first rest) (letrec ((f (lambda (cons empty ls) (if (empty? ls) empty (f cons (cons (first ls) empty) (rest ls)))))) f)) (define (make-foldr empty? first rest) (letrec ((f (lambda (cons empty ls) (if (empty? ls) empty (cons (first ls) (f cons empty (rest ls))))))) f)) ;; [X Y -> Y] Y [RaListof X] -> Y (define ra:foldl/1 (make-foldl ra:null? ra:car ra:cdr)) (define ra:foldr/1 (make-foldr ra:null? ra:car ra:cdr)) ;; [RaListof X] ... -> [RaListof X] (define (ra:append . lss) (cond ((null? lss) ra:null) (else (let recr ((lss lss)) (cond ((null? (cdr lss)) (car lss)) (else (ra:foldr/1 ra:cons (recr (cdr lss)) (car lss)))))))) ;; [RaListof X] -> [RaListof X] (define (ra:reverse ls) (ra:foldl/1 ra:cons ra:null ls)) ;; [RaListof X] Nat -> [RaListof X] (define (ra:list-tail ls i) (let loop ((xs ls) (j i)) (cond ((zero? j) xs) (else (loop (ra:cdr xs) (sub1 j)))))) ;; [RaListof X] Nat -> X ;; Special-cased above to avoid logarathmic amount of cons'ing ;; and any multi-values overhead. Operates in constant space. (define (ra:list-ref ls i) ;(assert (< i (ra:length ls))) (let loop ((xs ls) (j i)) (if (< j (kons-size xs)) (tree-ref (kons-size xs) (kons-tree xs) j) (loop (kons-rest xs) (- j (kons-size xs)))))) ;; [RaListof X] Nat X -> [RaListof X] (define (ra:list-set ls i v) (let-values (((_ l*) (ra:list-ref/set ls i v))) l*)) ;; [X ... -> y] [RaListof X] ... -> [RaListof Y] ;; Takes advantage of the fact that map produces a list of equal size. (define ra:map (case-lambda ((f ls) (let recr ((ls ls)) (if (kons? ls) (make-kons (kons-size ls) (tree-map f (kons-tree ls)) (recr (kons-rest ls))) ra:null))) ((f . lss) ;(check-nary-loop-args 'ra:map (lambda (x) x) f lss) (let recr ((lss lss)) (cond ((ra:null? (car lss)) ra:null) (else ;; IMPROVE ME: make one pass over lss. (make-kons (kons-size (car lss)) (tree-map/n f (map kons-tree lss)) (recr (map kons-rest lss))))))))) ;; [X ... -> Y] [RaListof X] ... -> unspecified (define ra:for-each (case-lambda ((f ls) (when (kons? ls) (tree-for-each f (kons-tree ls)) (ra:for-each f (kons-rest ls)))) ((f . lss) ;(check-nary-loop-args 'ra:map (lambda (x) x) f lss) (let recr ((lss lss)) (when (ra:pair? (car lss)) (tree-map/n f (map kons-tree lss)) (recr (map kons-rest lss))))))) ;; [RaListof X] -> [Listof X] (define (ra:random-access-list->linear-access-list x) (ra:foldr/1 cons '() x)) ;; [Listof X] -> [RaListof X] (define (ra:linear-access-list->random-access-list x) (fold-right ra:cons '() x)) ;; This code based on code written by Abdulaziz Ghuloum ;; http://ikarus-scheme.org/pipermail/ikarus-users/2009-September/000595.html (define get-cached (let ((h (make-eq-hashtable))) (lambda (x) (define (f x) (cond ((pair? x) (ra:cons (f (car x)) (f (cdr x)))) ((vector? x) (vector-map f x)) (else x))) (cond ((not (or (pair? x) (vector? x))) x) ((hashtable-ref h x #f)) (else (let ((v (f x))) (hashtable-set! h x v) v)))))) (define-syntax ra:quote (syntax-rules () ((ra:quote datum) (get-cached 'datum)))) ) ; (srfi :101 random-access-lists) |
Added srfi/s101/srfi-101-tests.sps.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
#!r6rs ;; SRFI 101: Purely Functional Random-Access Pairs and Lists ;; Copyright (c) David Van Horn 2009. All Rights Reserved. ;; Permission is hereby granted, free of charge, to any person obtaining ;; a copy of this software and associated documentation ;; files (the "Software"), to deal in the Software without restriction, ;; including without limitation the rights to use, copy, modify, merge, ;; publish, distribute, sublicense, and/or sell copies of the Software, ;; and to permit persons to whom the Software is furnished to do so, ;; subject to the following conditions: ;; The above copyright notice and this permission notice shall be ;; included in all copies or substantial portions of the Software. ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ;; NONINFRINGEMENT. REMEMBER, THERE IS NO SCHEME UNDERGROUND. IN NO EVENT ;; SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, ;; DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR ;; OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR ;; THE USE OR OTHER DEALINGS IN THE SOFTWARE. ;; This test suite has been successfully run on Ikarus (0.0.3), ;; Larceny (0.97), and PLT Scheme (4.2.1.7). ;; To run: ;; cp srfi-101.sls srfi/%3A101.sls ;; ikarus --r6rs-script srfi-101-tests.sls ;; larceny -r6rs -path . -program srfi-101-tests.sls ;; plt-r6rs ++path . srfi-101-tests.sls (import (except (rnrs base) quote pair? cons car cdr caar cadr cddr cdar caaar caadr caddr cadar cdaar cdadr cdddr cddar caaaar caaadr caaddr caadar cadaar cadadr cadddr caddar cdaaar cdaadr cdaddr cdadar cddaar cddadr cddddr cdddar null? list? list length append reverse list-tail list-ref map for-each) (prefix (rnrs base) r6:) (rnrs exceptions) (surfage s101 random-access-lists)) (define (check-expect c e) (if (pair? c) (begin (assert (pair? e)) (check-expect (car c) (car e)) (check-expect (cdr c) (cdr e))) (assert (equal? c e)))) (define-syntax check-error (syntax-rules () ((_ e) (let ((f (cons 0 0))) (guard (g ((eq? f g) (assert #f)) (else 'OK)) (begin e (raise f))))))) ; quote ; Bug in Larceny prevents this from working ; https://trac.ccs.neu.edu/trac/larceny/ticket/656 ;(check-expect (quote 5) (r6:quote 5)) ;(check-expect (quote x) (r6:quote x)) (check-expect (let ((f (lambda () '(x)))) (eq? (f) (f))) #t) (check-expect '(1 2 3) (list 1 2 3)) ; pair? (check-expect (pair? (cons 'a 'b)) #t) (check-expect (pair? (list 'a 'b 'c)) #t) (check-expect (pair? '()) #f) (check-expect (pair? '#(a b)) #f) ; cons (check-expect (cons 'a '()) (list 'a)) (check-expect (cons (list 'a) (list 'b 'c 'd)) (list (list 'a) 'b 'c 'd)) (check-expect (cons "a" (list 'b 'c)) (list "a" 'b 'c)) (check-expect (cons 'a 3) (cons 'a 3)) (check-expect (cons (list 'a 'b) 'c) (cons (list 'a 'b) 'c)) ; car (check-expect (car (list 'a 'b 'c)) 'a) (check-expect (car (list (list 'a) 'b 'c 'd)) (list 'a)) (check-expect (car (cons 1 2)) 1) (check-error (car '())) ; cdr (check-expect (cdr (list (list 'a) 'b 'c 'd)) (list 'b 'c 'd)) (check-expect (cdr (cons 1 2)) 2) (check-error (cdr '())) ; null? (check-expect (eq? null? r6:null?) #t) (check-expect (null? '()) #t) (check-expect (null? (cons 1 2)) #f) (check-expect (null? 4) #f) ; list? (check-expect (list? (list 'a 'b 'c)) #t) (check-expect (list? '()) #t) (check-expect (list? (cons 'a 'b)) #f) ; list (check-expect (list 'a (+ 3 4) 'c) (list 'a 7 'c)) (check-expect (list) '()) ; make-list (check-expect (length (make-list 5)) 5) (check-expect (make-list 5 0) (list 0 0 0 0 0)) ; length (check-expect (length (list 'a 'b 'c)) 3) (check-expect (length (list 'a (list 'b) (list 'c))) 3) (check-expect (length '()) 0) ; append (check-expect (append (list 'x) (list 'y)) (list 'x 'y)) (check-expect (append (list 'a) (list 'b 'c 'd)) (list 'a 'b 'c 'd)) (check-expect (append (list 'a (list 'b)) (list (list 'c))) (list 'a (list 'b) (list 'c))) (check-expect (append (list 'a 'b) (cons 'c 'd)) (cons 'a (cons 'b (cons 'c 'd)))) (check-expect (append '() 'a) 'a) ; reverse (check-expect (reverse (list 'a 'b 'c)) (list 'c 'b 'a)) (check-expect (reverse (list 'a (list 'b 'c) 'd (list 'e (list 'f)))) (list (list 'e (list 'f)) 'd (list 'b 'c) 'a)) ; list-tail (check-expect (list-tail (list 'a 'b 'c 'd) 2) (list 'c 'd)) ; list-ref (check-expect (list-ref (list 'a 'b 'c 'd) 2) 'c) ; list-set (check-expect (list-set (list 'a 'b 'c 'd) 2 'x) (list 'a 'b 'x 'd)) ; list-ref/update (let-values (((a b) (list-ref/update (list 7 8 9 10) 2 -))) (check-expect a 9) (check-expect b (list 7 8 -9 10))) ; map (check-expect (map cadr (list (list 'a 'b) (list 'd 'e) (list 'g 'h))) (list 'b 'e 'h)) (check-expect (map (lambda (n) (expt n n)) (list 1 2 3 4 5)) (list 1 4 27 256 3125)) (check-expect (map + (list 1 2 3) (list 4 5 6)) (list 5 7 9)) ; for-each (check-expect (let ((v (make-vector 5))) (for-each (lambda (i) (vector-set! v i (* i i))) (list 0 1 2 3 4)) v) '#(0 1 4 9 16)) ; random-access-list->linear-access-list ; linear-access-list->random-access-list (check-expect (random-access-list->linear-access-list '()) '()) (check-expect (linear-access-list->random-access-list '()) '()) (check-expect (random-access-list->linear-access-list (list 1 2 3)) (r6:list 1 2 3)) (check-expect (linear-access-list->random-access-list (r6:list 1 2 3)) (list 1 2 3)) |
Added srfi/s11/let-values.sls.
> > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s11 let-values) (export let-values let*-values) (import (only (rnrs) let-values let*-values)) ) |
Added srfi/s13/srfi-13.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 |
;;; SRFI 13 string library reference implementation -*- Scheme -*- ;;; Olin Shivers 7/2000 ;;; ;;; Copyright (c) 1988-1994 Massachusetts Institute of Technology. ;;; Copyright (c) 1998, 1999, 2000 Olin Shivers. All rights reserved. ;;; The details of the copyrights appear at the end of the file. Short ;;; summary: BSD-style open source. ;;; Exports: ;;; string-map string-map! ;;; string-fold string-unfold ;;; string-fold-right string-unfold-right ;;; string-tabulate string-for-each string-for-each-index ;;; string-every string-any ;;; string-hash string-hash-ci ;;; string-compare string-compare-ci ;;; string= string< string> string<= string>= string<> ;;; string-ci= string-ci< string-ci> string-ci<= string-ci>= string-ci<> ;;; string-downcase string-upcase string-titlecase ;;; string-downcase! string-upcase! string-titlecase! ;;; string-take string-take-right ;;; string-drop string-drop-right ;;; string-pad string-pad-right ;;; string-trim string-trim-right string-trim-both ;;; string-filter string-delete ;;; string-index string-index-right ;;; string-skip string-skip-right ;;; string-count ;;; string-prefix-length string-prefix-length-ci ;;; string-suffix-length string-suffix-length-ci ;;; string-prefix? string-prefix-ci? ;;; string-suffix? string-suffix-ci? ;;; string-contains string-contains-ci ;;; string-copy! substring/shared ;;; string-reverse string-reverse! reverse-list->string ;;; string-concatenate string-concatenate/shared string-concatenate-reverse ;;; string-append/shared ;;; xsubstring string-xcopy! ;;; string-null? ;;; string-join ;;; string-tokenize ;;; string-replace ;;; ;;; R5RS extended: ;;; string->list string-copy string-fill! ;;; ;;; R5RS re-exports: ;;; string? make-string string-length string-ref string-set! ;;; ;;; R5RS re-exports (also defined here but commented-out): ;;; string string-append list->string ;;; ;;; Low-level routines: ;;; make-kmp-restart-vector string-kmp-partial-search kmp-step ;;; string-parse-start+end ;;; string-parse-final-start+end ;;; let-string-start+end ;;; check-substring-spec ;;; substring-spec-ok? ;;; Imports ;;; This is a fairly large library. While it was written for portability, you ;;; must be aware of its dependencies in order to run it in a given scheme ;;; implementation. Here is a complete list of the dependencies it has and the ;;; assumptions it makes beyond stock R5RS Scheme: ;;; ;;; This code has the following non-R5RS dependencies: ;;; - (RECEIVE (var ...) mv-exp body ...) multiple-value binding macro; ;;; ;;; - Various imports from the char-set library for the routines that can ;;; take char-set arguments; ;;; ;;; - An n-ary ERROR procedure; ;;; ;;; - BITWISE-AND for the hash functions; ;;; ;;; - A simple CHECK-ARG procedure for checking parameter values; it is ;;; (lambda (pred val proc) ;;; (if (pred val) val (error "Bad arg" val pred proc))) ;;; ;;; - :OPTIONAL and LET-OPTIONALS* macros for parsing, defaulting & ;;; type-checking optional parameters from a rest argument; ;;; ;;; - CHAR-CASED? and CHAR-TITLECASE for the STRING-TITLECASE & ;;; STRING-TITLECASE! procedures. The former returns true iff a character is ;;; one that has case distinctions; in ASCII it returns true on a-z and A-Z. ;;; CHAR-TITLECASE is analagous to CHAR-UPCASE and CHAR-DOWNCASE. In ASCII & ;;; Latin-1, it is the same as CHAR-UPCASE. ;;; ;;; The code depends upon a small set of core string primitives from R5RS: ;;; MAKE-STRING STRING-REF STRING-SET! STRING? STRING-LENGTH SUBSTRING ;;; (Actually, SUBSTRING is not a primitive, but we assume that an ;;; implementation's native version is probably faster than one we could ;;; define, so we import it from R5RS.) ;;; ;;; The code depends upon a small set of R5RS character primitives: ;;; char? char=? char-ci=? char<? char-ci<? ;;; char-upcase char-downcase ;;; char->integer (for the hash functions) ;;; ;;; We assume the following: ;;; - CHAR-DOWNCASE o CHAR-UPCASE = CHAR-DOWNCASE ;;; - CHAR-CI=? is equivalent to ;;; (lambda (c1 c2) (char=? (char-downcase (char-upcase c1)) ;;; (char-downcase (char-upcase c2)))) ;;; - CHAR-UPCASE, CHAR-DOWNCASE and CHAR-TITLECASE are locale-insensitive ;;; and consistent with Unicode's 1-1 char-mapping spec. ;;; These things are typically true, but if not, you would need to modify ;;; the case-mapping and case-insensitive routines. ;;; Enough introductory blather. On to the source code. (But see the end of ;;; the file for further notes on porting & performance tuning.) ;;; Support for START/END substring specs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; This macro parses optional start/end arguments from arg lists, defaulting ;;; them to 0/(string-length s), and checks them for correctness. (define-syntax let-string-start+end (syntax-rules () ((let-string-start+end (start end) proc s-exp args-exp body ...) (receive (start end) (string-parse-final-start+end proc s-exp args-exp) body ...)) ((let-string-start+end (start end rest) proc s-exp args-exp body ...) (receive (rest start end) (string-parse-start+end proc s-exp args-exp) body ...)))) ;;; This one parses out a *pair* of final start/end indices. ;;; Not exported; for internal use. (define-syntax let-string-start+end2 (syntax-rules () ((l-s-s+e2 (start1 end1 start2 end2) proc s1 s2 args body ...) (let ((procv proc)) ; Make sure PROC is only evaluated once. (let-string-start+end (start1 end1 rest) procv s1 args (let-string-start+end (start2 end2) procv s2 rest body ...)))))) ;;; Returns three values: rest start end (define (string-parse-start+end proc s args) (if (not (string? s)) (error "Non-string value" proc s)) (let ((slen (string-length s))) (if (pair? args) (let ((start (car args)) (args (cdr args))) (if (and (integer? start) (exact? start) (>= start 0)) (receive (end args) (if (pair? args) (let ((end (car args)) (args (cdr args))) (if (and (integer? end) (exact? end) (<= end slen)) (values end args) (error "Illegal substring END spec" proc end s))) (values slen args)) (if (<= start end) (values args start end) (error "Illegal substring START/END spec" proc start end s))) (error "Illegal substring START spec" proc start s))) (values '() 0 slen)))) (define (string-parse-final-start+end proc s args) (receive (rest start end) (string-parse-start+end proc s args) (if (pair? rest) (error "Extra arguments to procedure" proc rest) (values start end)))) (define (substring-spec-ok? s start end) (and (string? s) (integer? start) (exact? start) (integer? end) (exact? end) (<= 0 start) (<= start end) (<= end (string-length s)))) (define (check-substring-spec proc s start end) (if (not (substring-spec-ok? s start end)) (error "Illegal substring spec." proc s start end))) ;;; Defined by R5RS, so commented out here. ;(define (string . chars) ; (let* ((len (length chars)) ; (ans (make-string len))) ; (do ((i 0 (+ i 1)) ; (chars chars (cdr chars))) ; ((>= i len)) ; (string-set! ans i (car chars))) ; ans)) ; ;(define (string . chars) (string-unfold null? car cdr chars)) ;;; substring/shared S START [END] ;;; string-copy S [START END] ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; All this goop is just arg parsing & checking surrounding a call to the ;;; actual primitive, %SUBSTRING/SHARED. (define (substring/shared s start . maybe-end) (check-arg string? s substring/shared) (let ((slen (string-length s))) (check-arg (lambda (start) (and (integer? start) (exact? start) (<= 0 start))) start substring/shared) (%substring/shared s start (:optional maybe-end slen (lambda (end) (and (integer? end) (exact? end) (<= start end) (<= end slen))))))) ;;; Split out so that other routines in this library can avoid arg-parsing ;;; overhead for END parameter. (define (%substring/shared s start end) (if (and (zero? start) (= end (string-length s))) s (substring s start end))) (define (string-copy s . maybe-start+end) (let-string-start+end (start end) string-copy s maybe-start+end (substring s start end))) ;This library uses the R5RS SUBSTRING, but doesn't export it. ;Here is a definition, just for completeness. ;(define (substring s start end) ; (check-substring-spec substring s start end) ; (let* ((slen (- end start)) ; (ans (make-string slen))) ; (do ((i 0 (+ i 1)) ; (j start (+ j 1))) ; ((>= i slen) ans) ; (string-set! ans i (string-ref s j))))) ;;; Basic iterators and other higher-order abstractions ;;; (string-map proc s [start end]) ;;; (string-map! proc s [start end]) ;;; (string-fold kons knil s [start end]) ;;; (string-fold-right kons knil s [start end]) ;;; (string-unfold p f g seed [base make-final]) ;;; (string-unfold-right p f g seed [base make-final]) ;;; (string-for-each proc s [start end]) ;;; (string-for-each-index proc s [start end]) ;;; (string-every char-set/char/pred s [start end]) ;;; (string-any char-set/char/pred s [start end]) ;;; (string-tabulate proc len) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; You want compiler support for high-level transforms on fold and unfold ops. ;;; You'd at least like a lot of inlining for clients of these procedures. ;;; Don't hold your breath. (define (string-map proc s . maybe-start+end) (check-arg procedure? proc string-map) (let-string-start+end (start end) string-map s maybe-start+end (%string-map proc s start end))) (define (%string-map proc s start end) ; Internal utility (let* ((len (- end start)) (ans (make-string len))) (do ((i (- end 1) (- i 1)) (j (- len 1) (- j 1))) ((< j 0)) (string-set! ans j (proc (string-ref s i)))) ans)) (define (string-map! proc s . maybe-start+end) (check-arg procedure? proc string-map!) (let-string-start+end (start end) string-map! s maybe-start+end (%string-map! proc s start end))) (define (%string-map! proc s start end) (do ((i (- end 1) (- i 1))) ((< i start)) (string-set! s i (proc (string-ref s i))))) (define (string-fold kons knil s . maybe-start+end) (check-arg procedure? kons string-fold) (let-string-start+end (start end) string-fold s maybe-start+end (let lp ((v knil) (i start)) (if (< i end) (lp (kons (string-ref s i) v) (+ i 1)) v)))) (define (string-fold-right kons knil s . maybe-start+end) (check-arg procedure? kons string-fold-right) (let-string-start+end (start end) string-fold-right s maybe-start+end (let lp ((v knil) (i (- end 1))) (if (>= i start) (lp (kons (string-ref s i) v) (- i 1)) v)))) ;;; (string-unfold p f g seed [base make-final]) ;;; This is the fundamental constructor for strings. ;;; - G is used to generate a series of "seed" values from the initial seed: ;;; SEED, (G SEED), (G^2 SEED), (G^3 SEED), ... ;;; - P tells us when to stop -- when it returns true when applied to one ;;; of these seed values. ;;; - F maps each seed value to the corresponding character ;;; in the result string. These chars are assembled into the ;;; string in a left-to-right order. ;;; - BASE is the optional initial/leftmost portion of the constructed string; ;;; it defaults to the empty string "". ;;; - MAKE-FINAL is applied to the terminal seed value (on which P returns ;;; true) to produce the final/rightmost portion of the constructed string. ;;; It defaults to (LAMBDA (X) ""). ;;; ;;; In other words, the following (simple, inefficient) definition holds: ;;; (define (string-unfold p f g seed base make-final) ;;; (string-append base ;;; (let recur ((seed seed)) ;;; (if (p seed) (make-final seed) ;;; (string-append (string (f seed)) ;;; (recur (g seed))))))) ;;; ;;; STRING-UNFOLD is a fairly powerful constructor -- you can use it to ;;; reverse a string, copy a string, convert a list to a string, read ;;; a port into a string, and so forth. Examples: ;;; (port->string port) = ;;; (string-unfold (compose eof-object? peek-char) ;;; read-char values port) ;;; ;;; (list->string lis) = (string-unfold null? car cdr lis) ;;; ;;; (tabulate-string f size) = (string-unfold (lambda (i) (= i size)) f add1 0) ;;; A problem with the following simple formulation is that it pushes one ;;; stack frame for every char in the result string -- an issue if you are ;;; using it to read a 100kchar string. So we don't use it -- but I include ;;; it to give a clear, straightforward description of what the function ;;; does. ;(define (string-unfold p f g seed base make-final) ; (let ((ans (let recur ((seed seed) (i (string-length base))) ; (if (p seed) ; (let* ((final (make-final seed)) ; (ans (make-string (+ i (string-length final))))) ; (string-copy! ans i final) ; ans) ; ; (let* ((c (f seed)) ; (s (recur (g seed) (+ i 1)))) ; (string-set! s i c) ; s))))) ; (string-copy! ans 0 base) ; ans)) ;;; The strategy is to allocate a series of chunks into which we stash the ;;; chars as we generate them. Chunk size goes up in powers of two starting ;;; with 40 and levelling out at 4k, i.e. ;;; 40 40 80 160 320 640 1280 2560 4096 4096 4096 4096 4096... ;;; This should work pretty well for short strings, 1-line (80 char) strings, ;;; and longer ones. When done, we allocate an answer string and copy the ;;; chars over from the chunk buffers. (define (string-unfold p f g seed . base+make-final) (check-arg procedure? p string-unfold) (check-arg procedure? f string-unfold) (check-arg procedure? g string-unfold) (let-optionals* base+make-final ((base "" (string? base)) (make-final (lambda (x) "") (procedure? make-final))) (let lp ((chunks '()) ; Previously filled chunks (nchars 0) ; Number of chars in CHUNKS (chunk (make-string 40)) ; Current chunk into which we write (chunk-len 40) (i 0) ; Number of chars written into CHUNK (seed seed)) (let lp2 ((i i) (seed seed)) (if (not (p seed)) (let ((c (f seed)) (seed (g seed))) (if (< i chunk-len) (begin (string-set! chunk i c) (lp2 (+ i 1) seed)) (let* ((nchars2 (+ chunk-len nchars)) (chunk-len2 (min 4096 nchars2)) (new-chunk (make-string chunk-len2))) (string-set! new-chunk 0 c) (lp (cons chunk chunks) (+ nchars chunk-len) new-chunk chunk-len2 1 seed)))) ;; We're done. Make the answer string & install the bits. (let* ((final (make-final seed)) (flen (string-length final)) (base-len (string-length base)) (j (+ base-len nchars i)) (ans (make-string (+ j flen)))) (%string-copy! ans j final 0 flen) ; Install FINAL. (let ((j (- j i))) (%string-copy! ans j chunk 0 i) ; Install CHUNK[0,I). (let lp ((j j) (chunks chunks)) ; Install CHUNKS. (if (pair? chunks) (let* ((chunk (car chunks)) (chunks (cdr chunks)) (chunk-len (string-length chunk)) (j (- j chunk-len))) (%string-copy! ans j chunk 0 chunk-len) (lp j chunks))))) (%string-copy! ans 0 base 0 base-len) ; Install BASE. ans)))))) (define (string-unfold-right p f g seed . base+make-final) (let-optionals* base+make-final ((base "" (string? base)) (make-final (lambda (x) "") (procedure? make-final))) (let lp ((chunks '()) ; Previously filled chunks (nchars 0) ; Number of chars in CHUNKS (chunk (make-string 40)) ; Current chunk into which we write (chunk-len 40) (i 40) ; Number of chars available in CHUNK (seed seed)) (let lp2 ((i i) (seed seed)) ; Fill up CHUNK from right (if (not (p seed)) ; to left. (let ((c (f seed)) (seed (g seed))) (if (> i 0) (let ((i (- i 1))) (string-set! chunk i c) (lp2 i seed)) (let* ((nchars2 (+ chunk-len nchars)) (chunk-len2 (min 4096 nchars2)) (new-chunk (make-string chunk-len2)) (i (- chunk-len2 1))) (string-set! new-chunk i c) (lp (cons chunk chunks) (+ nchars chunk-len) new-chunk chunk-len2 i seed)))) ;; We're done. Make the answer string & install the bits. (let* ((final (make-final seed)) (flen (string-length final)) (base-len (string-length base)) (chunk-used (- chunk-len i)) (j (+ base-len nchars chunk-used)) (ans (make-string (+ j flen)))) (%string-copy! ans 0 final 0 flen) ; Install FINAL. (%string-copy! ans flen chunk i chunk-len); Install CHUNK[I,). (let lp ((j (+ flen chunk-used)) ; Install CHUNKS. (chunks chunks)) (if (pair? chunks) (let* ((chunk (car chunks)) (chunks (cdr chunks)) (chunk-len (string-length chunk))) (%string-copy! ans j chunk 0 chunk-len) (lp (+ j chunk-len) chunks)) (%string-copy! ans j base 0 base-len))); Install BASE. ans)))))) (define (string-for-each proc s . maybe-start+end) (check-arg procedure? proc string-for-each) (let-string-start+end (start end) string-for-each s maybe-start+end (let lp ((i start)) (if (< i end) (begin (proc (string-ref s i)) (lp (+ i 1))))))) (define (string-for-each-index proc s . maybe-start+end) (check-arg procedure? proc string-for-each-index) (let-string-start+end (start end) string-for-each-index s maybe-start+end (let lp ((i start)) (if (< i end) (begin (proc i) (lp (+ i 1))))))) (define (string-every criterion s . maybe-start+end) (let-string-start+end (start end) string-every s maybe-start+end (cond ((char? criterion) (let lp ((i start)) (or (>= i end) (and (char=? criterion (string-ref s i)) (lp (+ i 1)))))) ((char-set? criterion) (let lp ((i start)) (or (>= i end) (and (char-set-contains? criterion (string-ref s i)) (lp (+ i 1)))))) ((procedure? criterion) ; Slightly funky loop so that (or (= start end) ; final (PRED S[END-1]) call (let lp ((i start)) ; is a tail call. (let ((c (string-ref s i)) (i1 (+ i 1))) (if (= i1 end) (criterion c) ; Tail call. (and (criterion c) (lp i1))))))) (else (error "Second param is neither char-set, char, or predicate procedure." string-every criterion))))) (define (string-any criterion s . maybe-start+end) (let-string-start+end (start end) string-any s maybe-start+end (cond ((char? criterion) (let lp ((i start)) (and (< i end) (or (char=? criterion (string-ref s i)) (lp (+ i 1)))))) ((char-set? criterion) (let lp ((i start)) (and (< i end) (or (char-set-contains? criterion (string-ref s i)) (lp (+ i 1)))))) ((procedure? criterion) ; Slightly funky loop so that (and (< start end) ; final (PRED S[END-1]) call (let lp ((i start)) ; is a tail call. (let ((c (string-ref s i)) (i1 (+ i 1))) (if (= i1 end) (criterion c) ; Tail call (or (criterion c) (lp i1))))))) (else (error "Second param is neither char-set, char, or predicate procedure." string-any criterion))))) (define (string-tabulate proc len) (check-arg procedure? proc string-tabulate) (check-arg (lambda (val) (and (integer? val) (exact? val) (<= 0 val))) len string-tabulate) (let ((s (make-string len))) (do ((i (- len 1) (- i 1))) ((< i 0)) (string-set! s i (proc i))) s)) ;;; string-prefix-length[-ci] s1 s2 [start1 end1 start2 end2] ;;; string-suffix-length[-ci] s1 s2 [start1 end1 start2 end2] ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Find the length of the common prefix/suffix. ;;; It is not required that the two substrings passed be of equal length. ;;; This was microcode in MIT Scheme -- a very tightly bummed primitive. ;;; %STRING-PREFIX-LENGTH is the core routine of all string-comparisons, ;;; so should be as tense as possible. (define (%string-prefix-length s1 start1 end1 s2 start2 end2) (let* ((delta (min (- end1 start1) (- end2 start2))) (end1 (+ start1 delta))) (if (and (eq? s1 s2) (= start1 start2)) ; EQ fast path delta (let lp ((i start1) (j start2)) ; Regular path (if (or (>= i end1) (not (char=? (string-ref s1 i) (string-ref s2 j)))) (- i start1) (lp (+ i 1) (+ j 1))))))) (define (%string-suffix-length s1 start1 end1 s2 start2 end2) (let* ((delta (min (- end1 start1) (- end2 start2))) (start1 (- end1 delta))) (if (and (eq? s1 s2) (= end1 end2)) ; EQ fast path delta (let lp ((i (- end1 1)) (j (- end2 1))) ; Regular path (if (or (< i start1) (not (char=? (string-ref s1 i) (string-ref s2 j)))) (- (- end1 i) 1) (lp (- i 1) (- j 1))))))) (define (%string-prefix-length-ci s1 start1 end1 s2 start2 end2) (let* ((delta (min (- end1 start1) (- end2 start2))) (end1 (+ start1 delta))) (if (and (eq? s1 s2) (= start1 start2)) ; EQ fast path delta (let lp ((i start1) (j start2)) ; Regular path (if (or (>= i end1) (not (char-ci=? (string-ref s1 i) (string-ref s2 j)))) (- i start1) (lp (+ i 1) (+ j 1))))))) (define (%string-suffix-length-ci s1 start1 end1 s2 start2 end2) (let* ((delta (min (- end1 start1) (- end2 start2))) (start1 (- end1 delta))) (if (and (eq? s1 s2) (= end1 end2)) ; EQ fast path delta (let lp ((i (- end1 1)) (j (- end2 1))) ; Regular path (if (or (< i start1) (not (char-ci=? (string-ref s1 i) (string-ref s2 j)))) (- (- end1 i) 1) (lp (- i 1) (- j 1))))))) (define (string-prefix-length s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-prefix-length s1 s2 maybe-starts+ends (%string-prefix-length s1 start1 end1 s2 start2 end2))) (define (string-suffix-length s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-suffix-length s1 s2 maybe-starts+ends (%string-suffix-length s1 start1 end1 s2 start2 end2))) (define (string-prefix-length-ci s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-prefix-length-ci s1 s2 maybe-starts+ends (%string-prefix-length-ci s1 start1 end1 s2 start2 end2))) (define (string-suffix-length-ci s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-suffix-length-ci s1 s2 maybe-starts+ends (%string-suffix-length-ci s1 start1 end1 s2 start2 end2))) ;;; string-prefix? s1 s2 [start1 end1 start2 end2] ;;; string-suffix? s1 s2 [start1 end1 start2 end2] ;;; string-prefix-ci? s1 s2 [start1 end1 start2 end2] ;;; string-suffix-ci? s1 s2 [start1 end1 start2 end2] ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; These are all simple derivatives of the previous counting funs. (define (string-prefix? s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-prefix? s1 s2 maybe-starts+ends (%string-prefix? s1 start1 end1 s2 start2 end2))) (define (string-suffix? s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-suffix? s1 s2 maybe-starts+ends (%string-suffix? s1 start1 end1 s2 start2 end2))) (define (string-prefix-ci? s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-prefix-ci? s1 s2 maybe-starts+ends (%string-prefix-ci? s1 start1 end1 s2 start2 end2))) (define (string-suffix-ci? s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-suffix-ci? s1 s2 maybe-starts+ends (%string-suffix-ci? s1 start1 end1 s2 start2 end2))) ;;; Here are the internal routines that do the real work. (define (%string-prefix? s1 start1 end1 s2 start2 end2) (let ((len1 (- end1 start1))) (and (<= len1 (- end2 start2)) ; Quick check (= (%string-prefix-length s1 start1 end1 s2 start2 end2) len1)))) (define (%string-suffix? s1 start1 end1 s2 start2 end2) (let ((len1 (- end1 start1))) (and (<= len1 (- end2 start2)) ; Quick check (= len1 (%string-suffix-length s1 start1 end1 s2 start2 end2))))) (define (%string-prefix-ci? s1 start1 end1 s2 start2 end2) (let ((len1 (- end1 start1))) (and (<= len1 (- end2 start2)) ; Quick check (= len1 (%string-prefix-length-ci s1 start1 end1 s2 start2 end2))))) (define (%string-suffix-ci? s1 start1 end1 s2 start2 end2) (let ((len1 (- end1 start1))) (and (<= len1 (- end2 start2)) ; Quick check (= len1 (%string-suffix-length-ci s1 start1 end1 s2 start2 end2))))) ;;; string-compare s1 s2 proc< proc= proc> [start1 end1 start2 end2] ;;; string-compare-ci s1 s2 proc< proc= proc> [start1 end1 start2 end2] ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Primitive string-comparison functions. ;;; Continuation order is different from MIT Scheme. ;;; Continuations are applied to s1's mismatch index; ;;; in the case of equality, this is END1. (define (%string-compare s1 start1 end1 s2 start2 end2 proc< proc= proc>) (let ((size1 (- end1 start1)) (size2 (- end2 start2))) (let ((match (%string-prefix-length s1 start1 end1 s2 start2 end2))) (if (= match size1) ((if (= match size2) proc= proc<) end1) ((if (= match size2) proc> (if (char<? (string-ref s1 (+ start1 match)) (string-ref s2 (+ start2 match))) proc< proc>)) (+ match start1)))))) (define (%string-compare-ci s1 start1 end1 s2 start2 end2 proc< proc= proc>) (let ((size1 (- end1 start1)) (size2 (- end2 start2))) (let ((match (%string-prefix-length-ci s1 start1 end1 s2 start2 end2))) (if (= match size1) ((if (= match size2) proc= proc<) end1) ((if (= match size2) proc> (if (char-ci<? (string-ref s1 (+ start1 match)) (string-ref s2 (+ start2 match))) proc< proc>)) (+ start1 match)))))) (define (string-compare s1 s2 proc< proc= proc> . maybe-starts+ends) (check-arg procedure? proc< string-compare) (check-arg procedure? proc= string-compare) (check-arg procedure? proc> string-compare) (let-string-start+end2 (start1 end1 start2 end2) string-compare s1 s2 maybe-starts+ends (%string-compare s1 start1 end1 s2 start2 end2 proc< proc= proc>))) (define (string-compare-ci s1 s2 proc< proc= proc> . maybe-starts+ends) (check-arg procedure? proc< string-compare-ci) (check-arg procedure? proc= string-compare-ci) (check-arg procedure? proc> string-compare-ci) (let-string-start+end2 (start1 end1 start2 end2) string-compare-ci s1 s2 maybe-starts+ends (%string-compare-ci s1 start1 end1 s2 start2 end2 proc< proc= proc>))) ;;; string= string<> string-ci= string-ci<> ;;; string< string> string-ci< string-ci> ;;; string<= string>= string-ci<= string-ci>= ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Simple definitions in terms of the previous comparison funs. ;;; I sure hope the %STRING-COMPARE calls get integrated. (define (string= s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string= s1 s2 maybe-starts+ends (and (= (- end1 start1) (- end2 start2)) ; Quick filter (or (and (eq? s1 s2) (= start1 start2)) ; Fast path (%string-compare s1 start1 end1 s2 start2 end2 ; Real test (lambda (i) #f) values (lambda (i) #f)))))) (define (string<> s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string<> s1 s2 maybe-starts+ends (or (not (= (- end1 start1) (- end2 start2))) ; Fast path (and (not (and (eq? s1 s2) (= start1 start2))) ; Quick filter (%string-compare s1 start1 end1 s2 start2 end2 ; Real test values (lambda (i) #f) values))))) (define (string< s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string< s1 s2 maybe-starts+ends (if (and (eq? s1 s2) (= start1 start2)) ; Fast path (< end1 end2) (%string-compare s1 start1 end1 s2 start2 end2 ; Real test values (lambda (i) #f) (lambda (i) #f))))) (define (string> s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string> s1 s2 maybe-starts+ends (if (and (eq? s1 s2) (= start1 start2)) ; Fast path (> end1 end2) (%string-compare s1 start1 end1 s2 start2 end2 ; Real test (lambda (i) #f) (lambda (i) #f) values)))) (define (string<= s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string<= s1 s2 maybe-starts+ends (if (and (eq? s1 s2) (= start1 start2)) ; Fast path (<= end1 end2) (%string-compare s1 start1 end1 s2 start2 end2 ; Real test values values (lambda (i) #f))))) (define (string>= s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string>= s1 s2 maybe-starts+ends (if (and (eq? s1 s2) (= start1 start2)) ; Fast path (>= end1 end2) (%string-compare s1 start1 end1 s2 start2 end2 ; Real test (lambda (i) #f) values values)))) (define (string-ci= s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-ci= s1 s2 maybe-starts+ends (and (= (- end1 start1) (- end2 start2)) ; Quick filter (or (and (eq? s1 s2) (= start1 start2)) ; Fast path (%string-compare-ci s1 start1 end1 s2 start2 end2 ; Real test (lambda (i) #f) values (lambda (i) #f)))))) (define (string-ci<> s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-ci<> s1 s2 maybe-starts+ends (or (not (= (- end1 start1) (- end2 start2))) ; Fast path (and (not (and (eq? s1 s2) (= start1 start2))) ; Quick filter (%string-compare-ci s1 start1 end1 s2 start2 end2 ; Real test values (lambda (i) #f) values))))) (define (string-ci< s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-ci< s1 s2 maybe-starts+ends (if (and (eq? s1 s2) (= start1 start2)) ; Fast path (< end1 end2) (%string-compare-ci s1 start1 end1 s2 start2 end2 ; Real test values (lambda (i) #f) (lambda (i) #f))))) (define (string-ci> s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-ci> s1 s2 maybe-starts+ends (if (and (eq? s1 s2) (= start1 start2)) ; Fast path (> end1 end2) (%string-compare-ci s1 start1 end1 s2 start2 end2 ; Real test (lambda (i) #f) (lambda (i) #f) values)))) (define (string-ci<= s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-ci<= s1 s2 maybe-starts+ends (if (and (eq? s1 s2) (= start1 start2)) ; Fast path (<= end1 end2) (%string-compare-ci s1 start1 end1 s2 start2 end2 ; Real test values values (lambda (i) #f))))) (define (string-ci>= s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-ci>= s1 s2 maybe-starts+ends (if (and (eq? s1 s2) (= start1 start2)) ; Fast path (>= end1 end2) (%string-compare-ci s1 start1 end1 s2 start2 end2 ; Real test (lambda (i) #f) values values)))) ;;; Hash ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Compute (c + 37 c + 37^2 c + ...) modulo BOUND, with sleaze thrown in ;;; to keep the intermediate values small. (We do the calculation with just ;;; enough bits to represent BOUND, masking off high bits at each step in ;;; calculation. If this screws up any important properties of the hash ;;; function I'd like to hear about it. -Olin) ;;; ;;; If you keep BOUND small enough, the intermediate calculations will ;;; always be fixnums. How small is dependent on the underlying Scheme system; ;;; we use a default BOUND of 2^22 = 4194304, which should hack it in ;;; Schemes that give you at least 29 signed bits for fixnums. The core ;;; calculation that you don't want to overflow is, worst case, ;;; (+ 65535 (* 37 (- bound 1))) ;;; where 65535 is the max character code. Choose the default BOUND to be the ;;; biggest power of two that won't cause this expression to fixnum overflow, ;;; and everything will be copacetic. (define (%string-hash s char->int bound start end) (let ((iref (lambda (s i) (char->int (string-ref s i)))) ;; Compute a 111...1 mask that will cover BOUND-1: (mask (let lp ((i #x10000)) ; Let's skip first 16 iterations, eh? (if (>= i bound) (- i 1) (lp (+ i i)))))) (let lp ((i start) (ans 0)) (if (>= i end) (modulo ans bound) (lp (+ i 1) (bitwise-and mask (+ (* 37 ans) (iref s i)))))))) (define (string-hash s . maybe-bound+start+end) (let-optionals* maybe-bound+start+end ((bound 4194304 (and (integer? bound) (exact? bound) (<= 0 bound))) rest) (let ((bound (if (zero? bound) 4194304 bound))) ; 0 means default. (let-string-start+end (start end) string-hash s rest (%string-hash s char->integer bound start end))))) (define (string-hash-ci s . maybe-bound+start+end) (let-optionals* maybe-bound+start+end ((bound 4194304 (and (integer? bound) (exact? bound) (<= 0 bound))) rest) (let ((bound (if (zero? bound) 4194304 bound))) ; 0 means default. (let-string-start+end (start end) string-hash-ci s rest (%string-hash s (lambda (c) (char->integer (char-downcase c))) bound start end))))) ;;; Case hacking ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; string-upcase s [start end] ;;; string-upcase! s [start end] ;;; string-downcase s [start end] ;;; string-downcase! s [start end] ;;; ;;; string-titlecase s [start end] ;;; string-titlecase! s [start end] ;;; Capitalize every contiguous alpha sequence: capitalise ;;; first char, lowercase rest. (define (string-upcase s . maybe-start+end) (let-string-start+end (start end) string-upcase s maybe-start+end (%string-map char-upcase s start end))) (define (string-upcase! s . maybe-start+end) (let-string-start+end (start end) string-upcase! s maybe-start+end (%string-map! char-upcase s start end))) (define (string-downcase s . maybe-start+end) (let-string-start+end (start end) string-downcase s maybe-start+end (%string-map char-downcase s start end))) (define (string-downcase! s . maybe-start+end) (let-string-start+end (start end) string-downcase! s maybe-start+end (%string-map! char-downcase s start end))) (define (%string-titlecase! s start end) (let lp ((i start)) (cond ((string-index s char-cased? i end) => (lambda (i) (string-set! s i (char-titlecase (string-ref s i))) (let ((i1 (+ i 1))) (cond ((string-skip s char-cased? i1 end) => (lambda (j) (string-downcase! s i1 j) (lp (+ j 1)))) (else (string-downcase! s i1 end))))))))) (define (string-titlecase! s . maybe-start+end) (let-string-start+end (start end) string-titlecase! s maybe-start+end (%string-titlecase! s start end))) (define (string-titlecase s . maybe-start+end) (let-string-start+end (start end) string-titlecase! s maybe-start+end (let ((ans (substring s start end))) (%string-titlecase! ans 0 (- end start)) ans))) ;;; Cutting & pasting strings ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; string-take string nchars ;;; string-drop string nchars ;;; ;;; string-take-right string nchars ;;; string-drop-right string nchars ;;; ;;; string-pad string k [char start end] ;;; string-pad-right string k [char start end] ;;; ;;; string-trim string [char/char-set/pred start end] ;;; string-trim-right string [char/char-set/pred start end] ;;; string-trim-both string [char/char-set/pred start end] ;;; ;;; These trimmers invert the char-set meaning from MIT Scheme -- you ;;; say what you want to trim. (define (string-take s n) (check-arg string? s string-take) (check-arg (lambda (val) (and (integer? n) (exact? n) (<= 0 n (string-length s)))) n string-take) (%substring/shared s 0 n)) (define (string-take-right s n) (check-arg string? s string-take-right) (let ((len (string-length s))) (check-arg (lambda (val) (and (integer? n) (exact? n) (<= 0 n len))) n string-take-right) (%substring/shared s (- len n) len))) (define (string-drop s n) (check-arg string? s string-drop) (let ((len (string-length s))) (check-arg (lambda (val) (and (integer? n) (exact? n) (<= 0 n len))) n string-drop) (%substring/shared s n len))) (define (string-drop-right s n) (check-arg string? s string-drop-right) (let ((len (string-length s))) (check-arg (lambda (val) (and (integer? n) (exact? n) (<= 0 n len))) n string-drop-right) (%substring/shared s 0 (- len n)))) (define (string-trim s . criterion+start+end) (let-optionals* criterion+start+end ((criterion char-set:whitespace) rest) (let-string-start+end (start end) string-trim s rest (cond ((string-skip s criterion start end) => (lambda (i) (%substring/shared s i end))) (else ""))))) (define (string-trim-right s . criterion+start+end) (let-optionals* criterion+start+end ((criterion char-set:whitespace) rest) (let-string-start+end (start end) string-trim-right s rest (cond ((string-skip-right s criterion start end) => (lambda (i) (%substring/shared s 0 (+ 1 i)))) (else ""))))) (define (string-trim-both s . criterion+start+end) (let-optionals* criterion+start+end ((criterion char-set:whitespace) rest) (let-string-start+end (start end) string-trim-both s rest (cond ((string-skip s criterion start end) => (lambda (i) (%substring/shared s i (+ 1 (string-skip-right s criterion i end))))) (else ""))))) (define (string-pad-right s n . char+start+end) (let-optionals* char+start+end ((char #\space (char? char)) rest) (let-string-start+end (start end) string-pad-right s rest (check-arg (lambda (n) (and (integer? n) (exact? n) (<= 0 n))) n string-pad-right) (let ((len (- end start))) (if (<= n len) (%substring/shared s start (+ start n)) (let ((ans (make-string n char))) (%string-copy! ans 0 s start end) ans)))))) (define (string-pad s n . char+start+end) (let-optionals* char+start+end ((char #\space (char? char)) rest) (let-string-start+end (start end) string-pad s rest (check-arg (lambda (n) (and (integer? n) (exact? n) (<= 0 n))) n string-pad) (let ((len (- end start))) (if (<= n len) (%substring/shared s (- end n) end) (let ((ans (make-string n char))) (%string-copy! ans (- n len) s start end) ans)))))) ;;; Filtering strings ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; string-delete char/char-set/pred string [start end] ;;; string-filter char/char-set/pred string [start end] ;;; ;;; If the criterion is a char or char-set, we scan the string twice with ;;; string-fold -- once to determine the length of the result string, ;;; and once to do the filtered copy. ;;; If the criterion is a predicate, we don't do this double-scan strategy, ;;; because the predicate might have side-effects or be very expensive to ;;; compute. So we preallocate a temp buffer pessimistically, and only do ;;; one scan over S. This is likely to be faster and more space-efficient ;;; than consing a list. (define (string-delete criterion s . maybe-start+end) (let-string-start+end (start end) string-delete s maybe-start+end (if (procedure? criterion) (let* ((slen (- end start)) (temp (make-string slen)) (ans-len (string-fold (lambda (c i) (if (criterion c) i (begin (string-set! temp i c) (+ i 1)))) 0 s start end))) (if (= ans-len slen) temp (substring temp 0 ans-len))) (let* ((cset (cond ((char-set? criterion) criterion) ((char? criterion) (char-set criterion)) (else (error "string-delete criterion not predicate, char or char-set" criterion)))) (len (string-fold (lambda (c i) (if (char-set-contains? cset c) i (+ i 1))) 0 s start end)) (ans (make-string len))) (string-fold (lambda (c i) (if (char-set-contains? cset c) i (begin (string-set! ans i c) (+ i 1)))) 0 s start end) ans)))) (define (string-filter criterion s . maybe-start+end) (let-string-start+end (start end) string-filter s maybe-start+end (if (procedure? criterion) (let* ((slen (- end start)) (temp (make-string slen)) (ans-len (string-fold (lambda (c i) (if (criterion c) (begin (string-set! temp i c) (+ i 1)) i)) 0 s start end))) (if (= ans-len slen) temp (substring temp 0 ans-len))) (let* ((cset (cond ((char-set? criterion) criterion) ((char? criterion) (char-set criterion)) (else (error "string-delete criterion not predicate, char or char-set" criterion)))) (len (string-fold (lambda (c i) (if (char-set-contains? cset c) (+ i 1) i)) 0 s start end)) (ans (make-string len))) (string-fold (lambda (c i) (if (char-set-contains? cset c) (begin (string-set! ans i c) (+ i 1)) i)) 0 s start end) ans)))) ;;; String search ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; string-index string char/char-set/pred [start end] ;;; string-index-right string char/char-set/pred [start end] ;;; string-skip string char/char-set/pred [start end] ;;; string-skip-right string char/char-set/pred [start end] ;;; string-count string char/char-set/pred [start end] ;;; There's a lot of replicated code here for efficiency. ;;; For example, the char/char-set/pred discrimination has ;;; been lifted above the inner loop of each proc. (define (string-index str criterion . maybe-start+end) (let-string-start+end (start end) string-index str maybe-start+end (cond ((char? criterion) (let lp ((i start)) (and (< i end) (if (char=? criterion (string-ref str i)) i (lp (+ i 1)))))) ((char-set? criterion) (let lp ((i start)) (and (< i end) (if (char-set-contains? criterion (string-ref str i)) i (lp (+ i 1)))))) ((procedure? criterion) (let lp ((i start)) (and (< i end) (if (criterion (string-ref str i)) i (lp (+ i 1)))))) (else (error "Second param is neither char-set, char, or predicate procedure." string-index criterion))))) (define (string-index-right str criterion . maybe-start+end) (let-string-start+end (start end) string-index-right str maybe-start+end (cond ((char? criterion) (let lp ((i (- end 1))) (and (>= i start) (if (char=? criterion (string-ref str i)) i (lp (- i 1)))))) ((char-set? criterion) (let lp ((i (- end 1))) (and (>= i start) (if (char-set-contains? criterion (string-ref str i)) i (lp (- i 1)))))) ((procedure? criterion) (let lp ((i (- end 1))) (and (>= i start) (if (criterion (string-ref str i)) i (lp (- i 1)))))) (else (error "Second param is neither char-set, char, or predicate procedure." string-index-right criterion))))) (define (string-skip str criterion . maybe-start+end) (let-string-start+end (start end) string-skip str maybe-start+end (cond ((char? criterion) (let lp ((i start)) (and (< i end) (if (char=? criterion (string-ref str i)) (lp (+ i 1)) i)))) ((char-set? criterion) (let lp ((i start)) (and (< i end) (if (char-set-contains? criterion (string-ref str i)) (lp (+ i 1)) i)))) ((procedure? criterion) (let lp ((i start)) (and (< i end) (if (criterion (string-ref str i)) (lp (+ i 1)) i)))) (else (error "Second param is neither char-set, char, or predicate procedure." string-skip criterion))))) (define (string-skip-right str criterion . maybe-start+end) (let-string-start+end (start end) string-skip-right str maybe-start+end (cond ((char? criterion) (let lp ((i (- end 1))) (and (>= i start) (if (char=? criterion (string-ref str i)) (lp (- i 1)) i)))) ((char-set? criterion) (let lp ((i (- end 1))) (and (>= i start) (if (char-set-contains? criterion (string-ref str i)) (lp (- i 1)) i)))) ((procedure? criterion) (let lp ((i (- end 1))) (and (>= i start) (if (criterion (string-ref str i)) (lp (- i 1)) i)))) (else (error "CRITERION param is neither char-set or char." string-skip-right criterion))))) (define (string-count s criterion . maybe-start+end) (let-string-start+end (start end) string-count s maybe-start+end (cond ((char? criterion) (do ((i start (+ i 1)) (count 0 (if (char=? criterion (string-ref s i)) (+ count 1) count))) ((>= i end) count))) ((char-set? criterion) (do ((i start (+ i 1)) (count 0 (if (char-set-contains? criterion (string-ref s i)) (+ count 1) count))) ((>= i end) count))) ((procedure? criterion) (do ((i start (+ i 1)) (count 0 (if (criterion (string-ref s i)) (+ count 1) count))) ((>= i end) count))) (else (error "CRITERION param is neither char-set or char." string-count criterion))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; string-fill! string char [start end] ;;; ;;; string-copy! to tstart from [fstart fend] ;;; Guaranteed to work, even if s1 eq s2. (define (string-fill! s char . maybe-start+end) (check-arg char? char string-fill!) (let-string-start+end (start end) string-fill! s maybe-start+end (do ((i (- end 1) (- i 1))) ((< i start)) (string-set! s i char)))) (define (string-copy! to tstart from . maybe-fstart+fend) (let-string-start+end (fstart fend) string-copy! from maybe-fstart+fend (check-arg integer? tstart string-copy!) (check-substring-spec string-copy! to tstart (+ tstart (- fend fstart))) (%string-copy! to tstart from fstart fend))) ;;; Library-internal routine (define (%string-copy! to tstart from fstart fend) (if (> fstart tstart) (do ((i fstart (+ i 1)) (j tstart (+ j 1))) ((>= i fend)) (string-set! to j (string-ref from i))) (do ((i (- fend 1) (- i 1)) (j (+ -1 tstart (- fend fstart)) (- j 1))) ((< i fstart)) (string-set! to j (string-ref from i))))) ;;; Returns starting-position in STRING or #f if not true. ;;; This implementation is slow & simple. It is useful as a "spec" or for ;;; comparison testing with fancier implementations. ;;; See below for fast KMP version. ;(define (string-contains string substring . maybe-starts+ends) ; (let-string-start+end2 (start1 end1 start2 end2) ; string-contains string substring maybe-starts+ends ; (let* ((len (- end2 start2)) ; (i-bound (- end1 len))) ; (let lp ((i start1)) ; (and (< i i-bound) ; (if (string= string substring i (+ i len) start2 end2) ; i ; (lp (+ i 1)))))))) ;;; Searching for an occurrence of a substring ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (string-contains text pattern . maybe-starts+ends) (let-string-start+end2 (t-start t-end p-start p-end) string-contains text pattern maybe-starts+ends (%kmp-search pattern text char=? p-start p-end t-start t-end))) (define (string-contains-ci text pattern . maybe-starts+ends) (let-string-start+end2 (t-start t-end p-start p-end) string-contains-ci text pattern maybe-starts+ends (%kmp-search pattern text char-ci=? p-start p-end t-start t-end))) ;;; Knuth-Morris-Pratt string searching ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; See ;;; "Fast pattern matching in strings" ;;; SIAM J. Computing 6(2):323-350 1977 ;;; D. E. Knuth, J. H. Morris and V. R. Pratt ;;; also described in ;;; "Pattern matching in strings" ;;; Alfred V. Aho ;;; Formal Language Theory - Perspectives and Open Problems ;;; Ronald V. Brook (editor) ;;; This algorithm is O(m + n) where m and n are the ;;; lengths of the pattern and string respectively ;;; KMP search source[start,end) for PATTERN. Return starting index of ;;; leftmost match or #f. (define (%kmp-search pattern text c= p-start p-end t-start t-end) (let ((plen (- p-end p-start)) (rv (make-kmp-restart-vector pattern c= p-start p-end))) ;; The search loop. TJ & PJ are redundant state. (let lp ((ti t-start) (pi 0) (tj (- t-end t-start)) ; (- tlen ti) -- how many chars left. (pj plen)) ; (- plen pi) -- how many chars left. (if (= pi plen) (- ti plen) ; Win. (and (<= pj tj) ; Lose. (if (c= (string-ref text ti) ; Search. (string-ref pattern (+ p-start pi))) (lp (+ 1 ti) (+ 1 pi) (- tj 1) (- pj 1)) ; Advance. (let ((pi (vector-ref rv pi))) ; Retreat. (if (= pi -1) (lp (+ ti 1) 0 (- tj 1) plen) ; Punt. (lp ti pi tj (- plen pi)))))))))) ;;; (make-kmp-restart-vector pattern [c= start end]) -> integer-vector ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Compute the KMP restart vector RV for string PATTERN. If ;;; we have matched chars 0..i-1 of PATTERN against a search string S, and ;;; PATTERN[i] doesn't match S[k], then reset i := RV[i], and try again to ;;; match S[k]. If RV[i] = -1, then punt S[k] completely, and move on to ;;; S[k+1] and PATTERN[0] -- no possible match of PAT[0..i] contains S[k]. ;;; ;;; In other words, if you have matched the first i chars of PATTERN, but ;;; the i+1'th char doesn't match, RV[i] tells you what the next-longest ;;; prefix of PATTERN is that you have matched. ;;; ;;; - C= (default CHAR=?) is used to compare characters for equality. ;;; Pass in CHAR-CI=? for case-folded string search. ;;; ;;; - START & END restrict the pattern to the indicated substring; the ;;; returned vector will be of length END - START. The numbers stored ;;; in the vector will be values in the range [0,END-START) -- that is, ;;; they are valid indices into the restart vector; you have to add START ;;; to them to use them as indices into PATTERN. ;;; ;;; I've split this out as a separate function in case other constant-string ;;; searchers might want to use it. ;;; ;;; E.g.: ;;; a b d a b x ;;; #(-1 0 0 -1 1 2) (define (make-kmp-restart-vector pattern . maybe-c=+start+end) (let-optionals* maybe-c=+start+end ((c= char=? (procedure? c=)) ((start end) (lambda (args) (string-parse-start+end make-kmp-restart-vector pattern args)))) (let* ((rvlen (- end start)) (rv (make-vector rvlen -1))) (if (> rvlen 0) (let ((rvlen-1 (- rvlen 1)) (c0 (string-ref pattern start))) ;; Here's the main loop. We have set rv[0] ... rv[i]. ;; K = I + START -- it is the corresponding index into PATTERN. (let lp1 ((i 0) (j -1) (k start)) (if (< i rvlen-1) ;; lp2 invariant: ;; pat[(k-j) .. k-1] matches pat[start .. start+j-1] ;; or j = -1. (let lp2 ((j j)) (cond ((= j -1) (let ((i1 (+ 1 i))) (if (not (c= (string-ref pattern (+ k 1)) c0)) (vector-set! rv i1 0)) (lp1 i1 0 (+ k 1)))) ;; pat[(k-j) .. k] matches pat[start..start+j]. ((c= (string-ref pattern k) (string-ref pattern (+ j start))) (let* ((i1 (+ 1 i)) (j1 (+ 1 j))) (vector-set! rv i1 j1) (lp1 i1 j1 (+ k 1)))) (else (lp2 (vector-ref rv j))))))))) rv))) ;;; We've matched I chars from PAT. C is the next char from the search string. ;;; Return the new I after handling C. ;;; ;;; The pattern is (VECTOR-LENGTH RV) chars long, beginning at index PAT-START ;;; in PAT (PAT-START is usually 0). The I chars of the pattern we've matched ;;; are ;;; PAT[PAT-START .. PAT-START + I]. ;;; ;;; It's *not* an oversight that there is no friendly error checking or ;;; defaulting of arguments. This is a low-level, inner-loop procedure ;;; that we want integrated/inlined into the point of call. (define (kmp-step pat rv c i c= p-start) (let lp ((i i)) (if (c= c (string-ref pat (+ i p-start))) ; Match => (+ i 1) ; Done. (let ((i (vector-ref rv i))) ; Back up in PAT. (if (= i -1) 0 ; Can't back up further. (lp i)))))) ; Keep trying for match. ;;; Zip through S[start,end), looking for a match of PAT. Assume we've ;;; already matched the first I chars of PAT when we commence at S[start]. ;;; - <0: If we find a match *ending* at index J, return -J. ;;; - >=0: If we get to the end of the S[start,end) span without finding ;;; a complete match, return the number of chars from PAT we'd matched ;;; when we ran off the end. ;;; ;;; This is useful for searching *across* buffers -- that is, when your ;;; input comes in chunks of text. We hand-integrate the KMP-STEP loop ;;; for speed. (define (string-kmp-partial-search pat rv s i . c=+p-start+s-start+s-end) (check-arg vector? rv string-kmp-partial-search) (let-optionals* c=+p-start+s-start+s-end ((c= char=? (procedure? c=)) (p-start 0 (and (integer? p-start) (exact? p-start) (<= 0 p-start))) ((s-start s-end) (lambda (args) (string-parse-start+end string-kmp-partial-search s args)))) (let ((patlen (vector-length rv))) (check-arg (lambda (i) (and (integer? i) (exact? i) (<= 0 i) (< i patlen))) i string-kmp-partial-search) ;; Enough prelude. Here's the actual code. (let lp ((si s-start) ; An index into S. (vi i)) ; An index into RV. (cond ((= vi patlen) (- si)) ; Win. ((= si s-end) vi) ; Ran off the end. (else ; Match s[si] & loop. (let ((c (string-ref s si))) (lp (+ si 1) (let lp2 ((vi vi)) ; This is just KMP-STEP. (if (c= c (string-ref pat (+ vi p-start))) (+ vi 1) (let ((vi (vector-ref rv vi))) (if (= vi -1) 0 (lp2 vi))))))))))))) ;;; Misc ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; (string-null? s) ;;; (string-reverse s [start end]) ;;; (string-reverse! s [start end]) ;;; (reverse-list->string clist) ;;; (string->list s [start end]) (define (string-null? s) (zero? (string-length s))) (define (string-reverse s . maybe-start+end) (let-string-start+end (start end) string-reverse s maybe-start+end (let* ((len (- end start)) (ans (make-string len))) (do ((i start (+ i 1)) (j (- len 1) (- j 1))) ((< j 0)) (string-set! ans j (string-ref s i))) ans))) (define (string-reverse! s . maybe-start+end) (let-string-start+end (start end) string-reverse! s maybe-start+end (do ((i (- end 1) (- i 1)) (j start (+ j 1))) ((<= i j)) (let ((ci (string-ref s i))) (string-set! s i (string-ref s j)) (string-set! s j ci))))) (define (reverse-list->string clist) (let* ((len (length clist)) (s (make-string len))) (do ((i (- len 1) (- i 1)) (clist clist (cdr clist))) ((not (pair? clist))) (string-set! s i (car clist))) s)) ;(define (string->list s . maybe-start+end) ; (apply string-fold-right cons '() s maybe-start+end)) (define (string->list s . maybe-start+end) (let-string-start+end (start end) string->list s maybe-start+end (do ((i (- end 1) (- i 1)) (ans '() (cons (string-ref s i) ans))) ((< i start) ans)))) ;;; Defined by R5RS, so commented out here. ;(define (list->string lis) (string-unfold null? car cdr lis)) ;;; string-concatenate string-list -> string ;;; string-concatenate/shared string-list -> string ;;; string-append/shared s ... -> string ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; STRING-APPEND/SHARED has license to return a string that shares storage ;;; with any of its arguments. In particular, if there is only one non-empty ;;; string amongst its parameters, it is permitted to return that string as ;;; its result. STRING-APPEND, by contrast, always allocates new storage. ;;; ;;; STRING-CONCATENATE & STRING-CONCATENATE/SHARED are passed a list of ;;; strings, which they concatenate into a result string. STRING-CONCATENATE ;;; always allocates a fresh string; STRING-CONCATENATE/SHARED may (or may ;;; not) return a result that shares storage with any of its arguments. In ;;; particular, if it is applied to a singleton list, it is permitted to ;;; return the car of that list as its value. (define (string-append/shared . strings) (string-concatenate/shared strings)) (define (string-concatenate/shared strings) (let lp ((strings strings) (nchars 0) (first #f)) (cond ((pair? strings) ; Scan the args, add up total (let* ((string (car strings)) ; length, remember 1st (tail (cdr strings)) ; non-empty string. (slen (string-length string))) (if (zero? slen) (lp tail nchars first) (lp tail (+ nchars slen) (or first strings))))) ((zero? nchars) "") ;; Just one non-empty string! Return it. ((= nchars (string-length (car first))) (car first)) (else (let ((ans (make-string nchars))) (let lp ((strings first) (i 0)) (if (pair? strings) (let* ((s (car strings)) (slen (string-length s))) (%string-copy! ans i s 0 slen) (lp (cdr strings) (+ i slen))))) ans))))) ; Alas, Scheme 48's APPLY blows up if you have many, many arguments. ;(define (string-concatenate strings) (apply string-append strings)) ;;; Here it is written out. I avoid using REDUCE to add up string lengths ;;; to avoid non-R5RS dependencies. (define (string-concatenate strings) (let* ((total (do ((strings strings (cdr strings)) (i 0 (+ i (string-length (car strings))))) ((not (pair? strings)) i))) (ans (make-string total))) (let lp ((i 0) (strings strings)) (if (pair? strings) (let* ((s (car strings)) (slen (string-length s))) (%string-copy! ans i s 0 slen) (lp (+ i slen) (cdr strings))))) ans)) ;;; Defined by R5RS, so commented out here. ;(define (string-append . strings) (string-concatenate strings)) ;;; string-concatenate-reverse string-list [final-string end] -> string ;;; string-concatenate-reverse/shared string-list [final-string end] -> string ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Return ;;; (string-concatenate ;;; (reverse ;;; (cons (substring final-string 0 end) string-list))) (define (string-concatenate-reverse string-list . maybe-final+end) (let-optionals* maybe-final+end ((final "" (string? final)) (end (string-length final) (and (integer? end) (exact? end) (<= 0 end (string-length final))))) (let ((len (let lp ((sum 0) (lis string-list)) (if (pair? lis) (lp (+ sum (string-length (car lis))) (cdr lis)) sum)))) (%finish-string-concatenate-reverse len string-list final end)))) (define (string-concatenate-reverse/shared string-list . maybe-final+end) (let-optionals* maybe-final+end ((final "" (string? final)) (end (string-length final) (and (integer? end) (exact? end) (<= 0 end (string-length final))))) ;; Add up the lengths of all the strings in STRING-LIST; also get a ;; pointer NZLIST into STRING-LIST showing where the first non-zero-length ;; string starts. (let lp ((len 0) (nzlist #f) (lis string-list)) (if (pair? lis) (let ((slen (string-length (car lis)))) (lp (+ len slen) (if (or nzlist (zero? slen)) nzlist lis) (cdr lis))) (cond ((zero? len) (substring/shared final 0 end)) ;; LEN > 0, so NZLIST is non-empty. ((and (zero? end) (= len (string-length (car nzlist)))) (car nzlist)) (else (%finish-string-concatenate-reverse len nzlist final end))))))) (define (%finish-string-concatenate-reverse len string-list final end) (let ((ans (make-string (+ end len)))) (%string-copy! ans len final 0 end) (let lp ((i len) (lis string-list)) (if (pair? lis) (let* ((s (car lis)) (lis (cdr lis)) (slen (string-length s)) (i (- i slen))) (%string-copy! ans i s 0 slen) (lp i lis)))) ans)) ;;; string-replace s1 s2 start1 end1 [start2 end2] -> string ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Replace S1[START1,END1) with S2[START2,END2). (define (string-replace s1 s2 start1 end1 . maybe-start+end) (check-substring-spec string-replace s1 start1 end1) (let-string-start+end (start2 end2) string-replace s2 maybe-start+end (let* ((slen1 (string-length s1)) (sublen2 (- end2 start2)) (alen (+ (- slen1 (- end1 start1)) sublen2)) (ans (make-string alen))) (%string-copy! ans 0 s1 0 start1) (%string-copy! ans start1 s2 start2 end2) (%string-copy! ans (+ start1 sublen2) s1 end1 slen1) ans))) ;;; string-tokenize s [token-set start end] -> list ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Break S up into a list of token strings, where a token is a maximal ;;; non-empty contiguous sequence of chars belonging to TOKEN-SET. ;;; (string-tokenize "hello, world") => ("hello," "world") (define (string-tokenize s . token-chars+start+end) (let-optionals* token-chars+start+end ((token-chars char-set:graphic (char-set? token-chars)) rest) (let-string-start+end (start end) string-tokenize s rest (let lp ((i end) (ans '())) (cond ((and (< start i) (string-index-right s token-chars start i)) => (lambda (tend-1) (let ((tend (+ 1 tend-1))) (cond ((string-skip-right s token-chars start tend-1) => (lambda (tstart-1) (lp tstart-1 (cons (substring s (+ 1 tstart-1) tend) ans)))) (else (cons (substring s start tend) ans)))))) (else ans)))))) ;;; xsubstring s from [to start end] -> string ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; S is a string; START and END are optional arguments that demarcate ;;; a substring of S, defaulting to 0 and the length of S (e.g., the whole ;;; string). Replicate this substring up and down index space, in both the ;; positive and negative directions. For example, if S = "abcdefg", START=3, ;;; and END=6, then we have the conceptual bidirectionally-infinite string ;;; ... d e f d e f d e f d e f d e f d e f d e f ... ;;; ... -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 ... ;;; XSUBSTRING returns the substring of this string beginning at index FROM, ;;; and ending at TO (which defaults to FROM+(END-START)). ;;; ;;; You can use XSUBSTRING in many ways: ;;; - To rotate a string left: (xsubstring "abcdef" 2) => "cdefab" ;;; - To rotate a string right: (xsubstring "abcdef" -2) => "efabcd" ;;; - To replicate a string: (xsubstring "abc" 0 7) => "abcabca" ;;; ;;; Note that ;;; - The FROM/TO indices give a half-open range -- the characters from ;;; index FROM up to, but not including index TO. ;;; - The FROM/TO indices are not in terms of the index space for string S. ;;; They are in terms of the replicated index space of the substring ;;; defined by S, START, and END. ;;; ;;; It is an error if START=END -- although this is allowed by special ;;; dispensation when FROM=TO. (define (xsubstring s from . maybe-to+start+end) (check-arg (lambda (val) (and (integer? val) (exact? val))) from xsubstring) (receive (to start end) (if (pair? maybe-to+start+end) (let-string-start+end (start end) xsubstring s (cdr maybe-to+start+end) (let ((to (car maybe-to+start+end))) (check-arg (lambda (val) (and (integer? val) (exact? val) (<= from val))) to xsubstring) (values to start end))) (let ((slen (string-length (check-arg string? s xsubstring)))) (values (+ from slen) 0 slen))) (let ((slen (- end start)) (anslen (- to from))) (cond ((zero? anslen) "") ((zero? slen) (error "Cannot replicate empty (sub)string" xsubstring s from to start end)) ((= 1 slen) ; Fast path for 1-char replication. (make-string anslen (string-ref s start))) ;; Selected text falls entirely within one span. ((= (floor (/ from slen)) (floor (/ to slen))) (substring s (+ start (modulo from slen)) (+ start (modulo to slen)))) ;; Selected text requires multiple spans. (else (let ((ans (make-string anslen))) (%multispan-repcopy! ans 0 s from to start end) ans)))))) ;;; string-xcopy! target tstart s sfrom [sto start end] -> unspecific ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Exactly the same as xsubstring, but the extracted text is written ;;; into the string TARGET starting at index TSTART. ;;; This operation is not defined if (EQ? TARGET S) -- you cannot copy ;;; a string on top of itself. (define (string-xcopy! target tstart s sfrom . maybe-sto+start+end) (check-arg (lambda (val) (and (integer? val) (exact? val))) sfrom string-xcopy!) (receive (sto start end) (if (pair? maybe-sto+start+end) (let-string-start+end (start end) string-xcopy! s (cdr maybe-sto+start+end) (let ((sto (car maybe-sto+start+end))) (check-arg (lambda (val) (and (integer? val) (exact? val))) sto string-xcopy!) (values sto start end))) (let ((slen (string-length s))) (values (+ sfrom slen) 0 slen))) (let* ((tocopy (- sto sfrom)) (tend (+ tstart tocopy)) (slen (- end start))) (check-substring-spec string-xcopy! target tstart tend) (cond ((zero? tocopy)) ((zero? slen) (error "Cannot replicate empty (sub)string" string-xcopy! target tstart s sfrom sto start end)) ((= 1 slen) ; Fast path for 1-char replication. (string-fill! target (string-ref s start) tstart tend)) ;; Selected text falls entirely within one span. ((= (floor (/ sfrom slen)) (floor (/ sto slen))) (%string-copy! target tstart s (+ start (modulo sfrom slen)) (+ start (modulo sto slen)))) ;; Multi-span copy. (else (%multispan-repcopy! target tstart s sfrom sto start end)))))) ;;; This is the core copying loop for XSUBSTRING and STRING-XCOPY! ;;; Internal -- not exported, no careful arg checking. (define (%multispan-repcopy! target tstart s sfrom sto start end) (let* ((slen (- end start)) (i0 (+ start (modulo sfrom slen))) (total-chars (- sto sfrom))) ;; Copy the partial span @ the beginning (%string-copy! target tstart s i0 end) (let* ((ncopied (- end i0)) ; We've copied this many. (nleft (- total-chars ncopied)) ; # chars left to copy. (nspans (quotient nleft slen))) ; # whole spans to copy ;; Copy the whole spans in the middle. (do ((i (+ tstart ncopied) (+ i slen)) ; Current target index. (nspans nspans (- nspans 1))) ; # spans to copy ((zero? nspans) ;; Copy the partial-span @ the end & we're done. (%string-copy! target i s start (+ start (- total-chars (- i tstart))))) (%string-copy! target i s start end))))); Copy a whole span. ;;; (string-join string-list [delimiter grammar]) => string ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Paste strings together using the delimiter string. ;;; ;;; (join-strings '("foo" "bar" "baz") ":") => "foo:bar:baz" ;;; ;;; DELIMITER defaults to a single space " " ;;; GRAMMAR is one of the symbols {prefix, infix, strict-infix, suffix} ;;; and defaults to 'infix. ;;; ;;; I could rewrite this more efficiently -- precompute the length of the ;;; answer string, then allocate & fill it in iteratively. Using ;;; STRING-CONCATENATE is less efficient. (define (string-join strings . delim+grammar) (let-optionals* delim+grammar ((delim " " (string? delim)) (grammar 'infix)) (let ((buildit (lambda (lis final) (let recur ((lis lis)) (if (pair? lis) (cons delim (cons (car lis) (recur (cdr lis)))) final))))) (cond ((pair? strings) (string-concatenate (case grammar ((infix strict-infix) (cons (car strings) (buildit (cdr strings) '()))) ((prefix) (buildit strings '())) ((suffix) (cons (car strings) (buildit (cdr strings) (list delim)))) (else (error "Illegal join grammar" grammar string-join))))) ((not (null? strings)) (error "STRINGS parameter not list." strings string-join)) ;; STRINGS is () ((eq? grammar 'strict-infix) (error "Empty list cannot be joined with STRICT-INFIX grammar." string-join)) (else ""))))) ; Special-cased for infix grammar. ;;; Porting & performance-tuning notes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; See the section at the beginning of this file on external dependencies. ;;; ;;; The biggest issue with respect to porting is the LET-OPTIONALS* macro. ;;; There are many, many optional arguments in this library; the complexity ;;; of parsing, defaulting & type-testing these parameters is handled with the ;;; aid of this macro. There are about 15 uses of LET-OPTIONALS*. You can ;;; rewrite the uses, port the hairy macro definition (which is implemented ;;; using a Clinger-Rees low-level explicit-renaming macro system), or port ;;; the simple, high-level definition, which is less efficient. ;;; ;;; There is a fair amount of argument checking. This is, strictly speaking, ;;; unnecessary -- the actual body of the procedures will blow up if, say, a ;;; START/END index is improper. However, the error message will not be as ;;; good as if the error were caught at the "higher level." Also, a very, very ;;; smart Scheme compiler may be able to exploit having the type checks done ;;; early, so that the actual body of the procedures can assume proper values. ;;; This isn't likely; this kind of compiler technology isn't common any ;;; longer. ;;; ;;; The overhead of optional-argument parsing is irritating. The optional ;;; arguments must be consed into a rest list on entry, and then parsed out. ;;; Function call should be a matter of a few register moves and a jump; it ;;; should not involve heap allocation! Your Scheme system may have a superior ;;; non-R5RS optional-argument system that can eliminate this overhead. If so, ;;; then this is a prime candidate for optimising these procedures, ;;; *especially* the many optional START/END index parameters. ;;; ;;; Note that optional arguments are also a barrier to procedure integration. ;;; If your Scheme system permits you to specify alternate entry points ;;; for a call when the number of optional arguments is known in a manner ;;; that enables inlining/integration, this can provide performance ;;; improvements. ;;; ;;; There is enough *explicit* error checking that *all* string-index ;;; operations should *never* produce a bounds error. Period. Feel like ;;; living dangerously? *Big* performance win to be had by replacing ;;; STRING-REF's and STRING-SET!'s with unsafe equivalents in the loops. ;;; Similarly, fixnum-specific operators can speed up the arithmetic done on ;;; the index values in the inner loops. The only arguments that are not ;;; completely error checked are ;;; - string lists (complete checking requires time proportional to the ;;; length of the list) ;;; - procedure arguments, such as char->char maps & predicates. ;;; There is no way to check the range & domain of procedures in Scheme. ;;; Procedures that take these parameters cannot fully check their ;;; arguments. But all other types to all other procedures are fully ;;; checked. ;;; ;;; This does open up the alternate possibility of simply *removing* these ;;; checks, and letting the safe primitives raise the errors. On a dumb ;;; Scheme system, this would provide speed (by eliminating the redundant ;;; error checks) at the cost of error-message clarity. ;;; ;;; See the comments preceding the hash function code for notes on tuning ;;; the default bound so that the code never overflows your implementation's ;;; fixnum size into bignum calculation. ;;; ;;; In an interpreted Scheme, some of these procedures, or the internal ;;; routines with % prefixes, are excellent candidates for being rewritten ;;; in C. Consider STRING-HASH, %STRING-COMPARE, the ;;; %STRING-{SUF,PRE}FIX-LENGTH routines, STRING-COPY!, STRING-INDEX & ;;; STRING-SKIP (char-set & char cases), SUBSTRING and SUBSTRING/SHARED, ;;; %KMP-SEARCH, and %MULTISPAN-REPCOPY!. ;;; ;;; It would also be nice to have the ability to mark some of these ;;; routines as candidates for inlining/integration. ;;; ;;; All the %-prefixed routines in this source code are written ;;; to be called internally to this library. They do *not* perform ;;; friendly error checks on the inputs; they assume everything is ;;; proper. They also do not take optional arguments. These two properties ;;; save calling overhead and enable procedure integration -- but they ;;; are not appropriate for exported routines. ;;; Copyright details ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; The prefix/suffix and comparison routines in this code had (extremely ;;; distant) origins in MIT Scheme's string lib, and was substantially ;;; reworked by Olin Shivers (shivers@ai.mit.edu) 9/98. As such, it is ;;; covered by MIT Scheme's open source copyright. See below for details. ;;; ;;; The KMP string-search code was influenced by implementations written ;;; by Stephen Bevan, Brian Dehneyer and Will Fitzgerald. However, this ;;; version was written from scratch by myself. ;;; ;;; The remainder of this code was written from scratch by myself for scsh. ;;; The scsh copyright is a BSD-style open source copyright. See below for ;;; details. ;;; -Olin Shivers ;;; MIT Scheme copyright terms ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; This material was developed by the Scheme project at the Massachusetts ;;; Institute of Technology, Department of Electrical Engineering and ;;; Computer Science. Permission to copy and modify this software, to ;;; redistribute either the original software or a modified version, and ;;; to use this software for any purpose is granted, subject to the ;;; following restrictions and understandings. ;;; ;;; 1. Any copy made of this software must include this copyright notice ;;; in full. ;;; ;;; 2. Users of this software agree to make their best efforts (a) to ;;; return to the MIT Scheme project any improvements or extensions that ;;; they make, so that these may be included in future releases; and (b) ;;; to inform MIT of noteworthy uses of this software. ;;; ;;; 3. All materials developed as a consequence of the use of this ;;; software shall duly acknowledge such use, in accordance with the usual ;;; standards of acknowledging credit in academic research. ;;; ;;; 4. MIT has made no warrantee or representation that the operation of ;;; this software will be error-free, and MIT is under no obligation to ;;; provide any services, by way of maintenance, update, or otherwise. ;;; ;;; 5. In conjunction with products arising from the use of this material, ;;; there shall be no use of the name of the Massachusetts Institute of ;;; Technology nor of any adaptation thereof in any advertising, ;;; promotional, or sales literature without prior written consent from ;;; MIT in each case. ;;; Scsh copyright terms ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; All rights reserved. ;;; ;;; Redistribution and use in source and binary forms, with or without ;;; modification, are permitted provided that the following conditions ;;; are met: ;;; 1. Redistributions of source code must retain the above copyright ;;; notice, this list of conditions and the following disclaimer. ;;; 2. Redistributions in binary form must reproduce the above copyright ;;; notice, this list of conditions and the following disclaimer in the ;;; documentation and/or other materials provided with the distribution. ;;; 3. The name of the authors may not be used to endorse or promote products ;;; derived from this software without specific prior written permission. ;;; ;;; THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR ;;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ;;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ;;; IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, ;;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ;;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ;;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ;;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ;;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ;;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
Added srfi/s13/strings.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s13 strings) (export string-map string-map! string-fold string-unfold string-fold-right string-unfold-right string-tabulate string-for-each string-for-each-index string-every string-any string-hash string-hash-ci string-compare string-compare-ci string= string< string> string<= string>= string<> string-ci= string-ci< string-ci> string-ci<= string-ci>= string-ci<> string-downcase string-upcase string-titlecase string-downcase! string-upcase! string-titlecase! string-take string-take-right string-drop string-drop-right string-pad string-pad-right string-trim string-trim-right string-trim-both string-filter string-delete string-index string-index-right string-skip string-skip-right string-count string-prefix-length string-prefix-length-ci string-suffix-length string-suffix-length-ci string-prefix? string-prefix-ci? string-suffix? string-suffix-ci? string-contains string-contains-ci string-copy! substring/shared string-reverse string-reverse! reverse-list->string string-concatenate string-concatenate/shared string-concatenate-reverse string-concatenate-reverse/shared string-append/shared xsubstring string-xcopy! string-null? string-join string-tokenize string-replace ; R5RS extended: string->list string-copy string-fill! ; R5RS re-exports: string? make-string string-length string-ref string-set! string string-append list->string ; Low-level routines: #;(make-kmp-restart-vector string-kmp-partial-search kmp-step string-parse-start+end string-parse-final-start+end let-string-start+end check-substring-spec substring-spec-ok?) ) (import (except (rnrs) string-copy string-for-each string->list string-upcase string-downcase string-titlecase string-hash) (except (rnrs mutable-strings) string-fill!) (rnrs r5rs) (srfi s23 error tricks) (srfi s8 receive) (srfi s14 char-sets) (srfi private let-opt) (srfi private include)) (define-syntax check-arg (lambda (stx) (syntax-case stx () [(_ pred val caller) (and (identifier? #'val) (identifier? #'caller)) #'(unless (pred val) (assertion-violation 'caller "check-arg failed" val))]))) (define (char-cased? c) (char-upper-case? (char-upcase c))) ;; (SRFI-23-error->R6RS "(library (srfi s13 strings))" ;; (include/resolve ("srfi" "%3a13") "srfi-13.scm")) (SRFI-23-error->R6RS "(library (srfi s13 strings))" (include/resolve ("srfi" "s13") "srfi-13.scm")) ) |
Added srfi/s14/char-sets.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s14 char-sets) (export ; Predicates & comparison char-set? char-set= char-set<= char-set-hash ; Iterating over character sets char-set-cursor char-set-ref char-set-cursor-next end-of-char-set? char-set-fold char-set-unfold char-set-unfold! char-set-for-each char-set-map ; Creating character sets char-set-copy char-set list->char-set string->char-set list->char-set! string->char-set! char-set-filter ucs-range->char-set char-set-filter! ucs-range->char-set! ->char-set ; Querying character sets char-set->list char-set->string char-set-size char-set-count char-set-contains? char-set-every char-set-any ; Character-set algebra char-set-adjoin char-set-delete char-set-adjoin! char-set-delete! char-set-complement char-set-union char-set-intersection char-set-complement! char-set-union! char-set-intersection! char-set-difference char-set-xor char-set-diff+intersection char-set-difference! char-set-xor! char-set-diff+intersection! ; Standard character sets char-set:lower-case char-set:upper-case char-set:title-case char-set:letter char-set:digit char-set:letter+digit char-set:graphic char-set:printing char-set:whitespace char-set:iso-control char-set:punctuation char-set:symbol char-set:hex-digit char-set:blank char-set:ascii char-set:empty char-set:full ) (import (except (rnrs) define-record-type) (rnrs mutable-strings) (rnrs r5rs) (srfi s23 error tricks) (srfi s9 records) (srfi private let-opt) (srfi private include)) (define (%latin1->char i) (integer->char i)) (define (%char->latin1 c) (char->integer c)) (define-syntax check-arg (lambda (stx) (syntax-case stx () [(_ pred val caller) (identifier? #'val) #'(unless (pred val) (assertion-violation caller "check-arg failed" val))]))) (SRFI-23-error->R6RS "(library (srfi s14 char-sets))" (include/resolve ("srfi" "s14") "srfi-14.scm"))) |
Added srfi/s14/srfi-14.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 |
;;; SRFI-14 character-sets library -*- Scheme -*- ;;; ;;; - Ported from MIT Scheme runtime by Brian D. Carlstrom. ;;; - Massively rehacked & extended by Olin Shivers 6/98. ;;; - Massively redesigned and rehacked 5/2000 during SRFI process. ;;; At this point, the code bears the following relationship to the ;;; MIT Scheme code: "This is my grandfather's axe. My father replaced ;;; the head, and I have replaced the handle." Nonetheless, we preserve ;;; the MIT Scheme copyright: ;;; Copyright (c) 1988-1995 Massachusetts Institute of Technology ;;; The MIT Scheme license is a "free software" license. See the end of ;;; this file for the tedious details. ;;; Exports: ;;; char-set? char-set= char-set<= ;;; char-set-hash ;;; char-set-cursor char-set-ref char-set-cursor-next end-of-char-set? ;;; char-set-fold char-set-unfold char-set-unfold! ;;; char-set-for-each char-set-map ;;; char-set-copy char-set ;;; ;;; list->char-set string->char-set ;;; list->char-set! string->char-set! ;;; ;;; filterchar-set ucs-range->char-set ->char-set ;;; filterchar-set! ucs-range->char-set! ;;; ;;; char-set->list char-set->string ;;; ;;; char-set-size char-set-count char-set-contains? ;;; char-set-every char-set-any ;;; ;;; char-set-adjoin char-set-delete ;;; char-set-adjoin! char-set-delete! ;;; ;;; char-set-complement char-set-union char-set-intersection ;;; char-set-complement! char-set-union! char-set-intersection! ;;; ;;; char-set-difference char-set-xor char-set-diff+intersection ;;; char-set-difference! char-set-xor! char-set-diff+intersection! ;;; ;;; char-set:lower-case char-set:upper-case char-set:title-case ;;; char-set:letter char-set:digit char-set:letter+digit ;;; char-set:graphic char-set:printing char-set:whitespace ;;; char-set:iso-control char-set:punctuation char-set:symbol ;;; char-set:hex-digit char-set:blank char-set:ascii ;;; char-set:empty char-set:full ;;; Imports ;;; This code has the following non-R5RS dependencies: ;;; - ERROR ;;; - %LATIN1->CHAR %CHAR->LATIN1 ;;; - LET-OPTIONALS* and :OPTIONAL macros for parsing, checking & defaulting ;;; optional arguments from rest lists. ;;; - BITWISE-AND for CHAR-SET-HASH ;;; - The SRFI-19 DEFINE-RECORD-TYPE record macro ;;; - A simple CHECK-ARG procedure: ;;; (lambda (pred val caller) (if (not (pred val)) (error val caller))) ;;; This is simple code, not great code. Char sets are represented as 256-char ;;; strings. If char I is ASCII/Latin-1 0, then it isn't in the set; if char I ;;; is ASCII/Latin-1 1, then it is in the set. ;;; - Should be rewritten to use bit strings or byte vecs. ;;; - Is Latin-1 specific. Would certainly have to be rewritten for Unicode. ;;; See the end of the file for porting and performance-tuning notes. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define-record-type :char-set (make-char-set s) char-set? (s char-set:s)) (define (%string-copy s) (substring s 0 (string-length s))) ;;; Parse, type-check & default a final optional BASE-CS parameter from ;;; a rest argument. Return a *fresh copy* of the underlying string. ;;; The default is the empty set. The PROC argument is to help us ;;; generate informative error exceptions. (define (%default-base maybe-base proc) (if (pair? maybe-base) (let ((bcs (car maybe-base)) (tail (cdr maybe-base))) (if (null? tail) (if (char-set? bcs) (%string-copy (char-set:s bcs)) (assertion-violation proc "BASE-CS parameter not a char-set" bcs)) (assertion-violation proc "Expected final base char set -- too many parameters" maybe-base))) (make-string 256 (%latin1->char 0)))) ;;; If CS is really a char-set, do CHAR-SET:S, otw report an error msg on ;;; behalf of our caller, PROC. This procedure exists basically to provide ;;; explicit error-checking & reporting. (define (%char-set:s/check cs proc) (let lp ((cs cs)) (if (char-set? cs) (char-set:s cs) (lp (assertion-violation proc "Not a char-set" cs))))) ;;; These internal functions hide a lot of the dependency on the ;;; underlying string representation of char sets. They should be ;;; inlined if possible. (define (si=0? s i) (zero? (%char->latin1 (string-ref s i)))) (define (si=1? s i) (not (si=0? s i))) (define c0 (%latin1->char 0)) (define c1 (%latin1->char 1)) (define (si s i) (%char->latin1 (string-ref s i))) (define (%set0! s i) (string-set! s i c0)) (define (%set1! s i) (string-set! s i c1)) ;;; These do various "s[i] := s[i] op val" operations -- see ;;; %CHAR-SET-ALGEBRA. They are used to implement the various ;;; set-algebra procedures. (define (setv! s i v) (string-set! s i (%latin1->char v))) ; SET to a Value. (define (%not! s i v) (setv! s i (- 1 v))) (define (%and! s i v) (if (zero? v) (%set0! s i))) (define (%or! s i v) (if (not (zero? v)) (%set1! s i))) (define (%minus! s i v) (if (not (zero? v)) (%set0! s i))) (define (%xor! s i v) (if (not (zero? v)) (setv! s i (- 1 (si s i))))) (define (char-set-copy cs) (make-char-set (%string-copy (%char-set:s/check cs 'char-set-copy)))) (define (char-set= . rest) (or (null? rest) (let* ((cs1 (car rest)) (rest (cdr rest)) (s1 (%char-set:s/check cs1 'char-set=))) (let lp ((rest rest)) (or (not (pair? rest)) (and (string=? s1 (%char-set:s/check (car rest) 'char-set=)) (lp (cdr rest)))))))) (define (char-set<= . rest) (or (null? rest) (let ((cs1 (car rest)) (rest (cdr rest))) (let lp ((s1 (%char-set:s/check cs1 'char-set<=)) (rest rest)) (or (not (pair? rest)) (let ((s2 (%char-set:s/check (car rest) 'char-set<=)) (rest (cdr rest))) (if (eq? s1 s2) (lp s2 rest) ; Fast path (let lp2 ((i 255)) ; Real test (if (< i 0) (lp s2 rest) (and (<= (si s1 i) (si s2 i)) (lp2 (- i 1)))))))))))) ;;; Hash ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Compute (c + 37 c + 37^2 c + ...) modulo BOUND, with sleaze thrown in ;;; to keep the intermediate values small. (We do the calculation with just ;;; enough bits to represent BOUND, masking off high bits at each step in ;;; calculation. If this screws up any important properties of the hash ;;; function I'd like to hear about it. -Olin) ;;; ;;; If you keep BOUND small enough, the intermediate calculations will ;;; always be fixnums. How small is dependent on the underlying Scheme system; ;;; we use a default BOUND of 2^22 = 4194304, which should hack it in ;;; Schemes that give you at least 29 signed bits for fixnums. The core ;;; calculation that you don't want to overflow is, worst case, ;;; (+ 65535 (* 37 (- bound 1))) ;;; where 65535 is the max character code. Choose the default BOUND to be the ;;; biggest power of two that won't cause this expression to fixnum overflow, ;;; and everything will be copacetic. (define (char-set-hash cs . maybe-bound) (let* ((bound (:optional maybe-bound 4194304 (lambda (n) (and (integer? n) (exact? n) (<= 0 n))))) (bound (if (zero? bound) 4194304 bound)) ; 0 means default. (s (%char-set:s/check cs 'char-set-hash)) ;; Compute a 111...1 mask that will cover BOUND-1: (mask (let lp ((i #x10000)) ; Let's skip first 16 iterations, eh? (if (>= i bound) (- i 1) (lp (+ i i)))))) (let lp ((i 255) (ans 0)) (if (< i 0) (modulo ans bound) (lp (- i 1) (if (si=0? s i) ans (bitwise-and mask (+ (* 37 ans) i)))))))) (define (char-set-contains? cs char) (check-arg char? char 'char-set-contains?) (si=1? (%char-set:s/check cs 'char-set-contains?) (%char->latin1 char))) (define (char-set-size cs) (let ((s (%char-set:s/check cs 'char-set-size))) (let lp ((i 255) (size 0)) (if (< i 0) size (lp (- i 1) (+ size (si s i))))))) (define (char-set-count pred cset) (check-arg procedure? pred 'char-set-count) (let ((s (%char-set:s/check cset 'char-set-count))) (let lp ((i 255) (count 0)) (if (< i 0) count (lp (- i 1) (if (and (si=1? s i) (pred (%latin1->char i))) (+ count 1) count)))))) ;;; -- Adjoin & delete (define (%set-char-set set proc cs chars) (let ((s (%string-copy (%char-set:s/check cs proc)))) (for-each (lambda (c) (set s (%char->latin1 c))) chars) (make-char-set s))) (define (%set-char-set! set proc cs chars) (let ((s (%char-set:s/check cs proc))) (for-each (lambda (c) (set s (%char->latin1 c))) chars)) cs) (define (char-set-adjoin cs . chars) (%set-char-set %set1! 'char-set-adjoin cs chars)) (define (char-set-adjoin! cs . chars) (%set-char-set! %set1! 'char-set-adjoin! cs chars)) (define (char-set-delete cs . chars) (%set-char-set %set0! 'char-set-delete cs chars)) (define (char-set-delete! cs . chars) (%set-char-set! %set0! 'char-set-delete! cs chars)) ;;; Cursors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Simple implementation. A cursors is an integer index into the ;;; mark vector, and -1 for the end-of-char-set cursor. ;;; ;;; If we represented char sets as a bit set, we could do the following ;;; trick to pick the lowest bit out of the set: ;;; (count-bits (xor (- cset 1) cset)) ;;; (But first mask out the bits already scanned by the cursor first.) (define (char-set-cursor cset) (%char-set-cursor-next cset 256 'char-set-cursor)) (define (end-of-char-set? cursor) (< cursor 0)) (define (char-set-ref cset cursor) (%latin1->char cursor)) (define (char-set-cursor-next cset cursor) (check-arg (lambda (i) (and (integer? i) (exact? i) (<= 0 i 255))) cursor 'char-set-cursor-next) (%char-set-cursor-next cset cursor 'char-set-cursor-next)) (define (%char-set-cursor-next cset cursor proc) ; Internal (let ((s (%char-set:s/check cset proc))) (let lp ((cur cursor)) (let ((cur (- cur 1))) (if (or (< cur 0) (si=1? s cur)) cur (lp cur)))))) ;;; -- for-each map fold unfold every any (define (char-set-for-each proc cs) (check-arg procedure? proc 'char-set-for-each) (let ((s (%char-set:s/check cs 'char-set-for-each))) (let lp ((i 255)) (cond ((>= i 0) (if (si=1? s i) (proc (%latin1->char i))) (lp (- i 1))))))) (define (char-set-map proc cs) (check-arg procedure? proc 'char-set-map) (let ((s (%char-set:s/check cs 'char-set-map)) (ans (make-string 256 c0))) (let lp ((i 255)) (cond ((>= i 0) (if (si=1? s i) (%set1! ans (%char->latin1 (proc (%latin1->char i))))) (lp (- i 1))))) (make-char-set ans))) (define (char-set-fold kons knil cs) (check-arg procedure? kons 'char-set-fold) (let ((s (%char-set:s/check cs 'char-set-fold))) (let lp ((i 255) (ans knil)) (if (< i 0) ans (lp (- i 1) (if (si=0? s i) ans (kons (%latin1->char i) ans))))))) (define (char-set-every pred cs) (check-arg procedure? pred 'char-set-every) (let ((s (%char-set:s/check cs 'char-set-every))) (let lp ((i 255)) (or (< i 0) (and (or (si=0? s i) (pred (%latin1->char i))) (lp (- i 1))))))) (define (char-set-any pred cs) (check-arg procedure? pred 'char-set-any) (let ((s (%char-set:s/check cs 'char-set-any))) (let lp ((i 255)) (and (>= i 0) (or (and (si=1? s i) (pred (%latin1->char i))) (lp (- i 1))))))) (define (%char-set-unfold! proc p f g s seed) (check-arg procedure? p proc) (check-arg procedure? f proc) (check-arg procedure? g proc) (let lp ((seed seed)) (cond ((not (p seed)) ; P says we are done. (%set1! s (%char->latin1 (f seed))) ; Add (F SEED) to set. (lp (g seed)))))) ; Loop on (G SEED). (define (char-set-unfold p f g seed . maybe-base) (let ((bs (%default-base maybe-base 'char-set-unfold))) (%char-set-unfold! 'char-set-unfold p f g bs seed) (make-char-set bs))) (define (char-set-unfold! p f g seed base-cset) (%char-set-unfold! 'char-set-unfold! p f g (%char-set:s/check base-cset 'char-set-unfold!) seed) base-cset) ;;; list <--> char-set (define (%list->char-set! chars s) (for-each (lambda (char) (%set1! s (%char->latin1 char))) chars)) (define (char-set . chars) (let ((s (make-string 256 c0))) (%list->char-set! chars s) (make-char-set s))) (define (list->char-set chars . maybe-base) (let ((bs (%default-base maybe-base 'list->char-set))) (%list->char-set! chars bs) (make-char-set bs))) (define (list->char-set! chars base-cs) (%list->char-set! chars (%char-set:s/check base-cs 'list->char-set!)) base-cs) (define (char-set->list cs) (let ((s (%char-set:s/check cs 'char-set->list))) (let lp ((i 255) (ans '())) (if (< i 0) ans (lp (- i 1) (if (si=0? s i) ans (cons (%latin1->char i) ans))))))) ;;; string <--> char-set (define (%string->char-set! str bs proc) (check-arg string? str proc) (do ((i (- (string-length str) 1) (- i 1))) ((< i 0)) (%set1! bs (%char->latin1 (string-ref str i))))) (define (string->char-set str . maybe-base) (let ((bs (%default-base maybe-base 'string->char-set))) (%string->char-set! str bs 'string->char-set) (make-char-set bs))) (define (string->char-set! str base-cs) (%string->char-set! str (%char-set:s/check base-cs 'string->char-set!) 'string->char-set!) base-cs) (define (char-set->string cs) (let* ((s (%char-set:s/check cs 'char-set->string)) (ans (make-string (char-set-size cs)))) (let lp ((i 255) (j 0)) (if (< i 0) ans (let ((j (if (si=0? s i) j (begin (string-set! ans j (%latin1->char i)) (+ j 1))))) (lp (- i 1) j)))))) ;;; -- UCS-range -> char-set (define (%ucs-range->char-set! lower upper error? bs proc) (check-arg (lambda (x) (and (integer? x) (exact? x) (<= 0 x))) lower proc) (check-arg (lambda (x) (and (integer? x) (exact? x) (<= lower x))) upper proc) (if (and (< lower upper) (< 256 upper) error?) (assertion-violation proc "Requested UCS range contains unavailable characters -- this implementation only supports Latin-1" lower upper)) (let lp ((i (- (min upper 256) 1))) (cond ((<= lower i) (%set1! bs i) (lp (- i 1)))))) (define (ucs-range->char-set lower upper . rest) (let-optionals* rest ((error? #f) rest) (let ((bs (%default-base rest 'ucs-range->char-set))) (%ucs-range->char-set! lower upper error? bs 'ucs-range->char-set) (make-char-set bs)))) (define (ucs-range->char-set! lower upper error? base-cs) (%ucs-range->char-set! lower upper error? (%char-set:s/check base-cs 'ucs-range->char-set!) 'ucs-range->char-set) base-cs) ;;; -- predicate -> char-set (define (%char-set-filter! pred ds bs proc) (check-arg procedure? pred proc) (let lp ((i 255)) (cond ((>= i 0) (if (and (si=1? ds i) (pred (%latin1->char i))) (%set1! bs i)) (lp (- i 1)))))) (define (char-set-filter predicate domain . maybe-base) (let ((bs (%default-base maybe-base 'char-set-filter))) (%char-set-filter! predicate (%char-set:s/check domain 'char-set-filter!) bs 'char-set-filter) (make-char-set bs))) (define (char-set-filter! predicate domain base-cs) (%char-set-filter! predicate (%char-set:s/check domain 'char-set-filter!) (%char-set:s/check base-cs 'char-set-filter!) 'char-set-filter!) base-cs) ;;; {string, char, char-set, char predicate} -> char-set (define (->char-set x) (cond ((char-set? x) x) ((string? x) (string->char-set x)) ((char? x) (char-set x)) (else (error "Not a charset, string or char." x)))) ;;; Set algebra ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; The exported ! procs are "linear update" -- allowed, but not required, to ;;; side-effect their first argument when computing their result. In other ;;; words, you must use them as if they were completely functional, just like ;;; their non-! counterparts, and you must additionally ensure that their ;;; first arguments are "dead" at the point of call. In return, we promise a ;;; more efficient result, plus allowing you to always assume char-sets are ;;; unchangeable values. ;;; Apply P to each index and its char code in S: (P I VAL). ;;; Used by the set-algebra ops. (define (%string-iter p s) (let lp ((i (- (string-length s) 1))) (cond ((>= i 0) (p i (%char->latin1 (string-ref s i))) (lp (- i 1)))))) ;;; String S represents some initial char-set. (OP s i val) does some ;;; kind of s[i] := s[i] op val update. Do ;;; S := S OP CSETi ;;; for all the char-sets in the list CSETS. The n-ary set-algebra ops ;;; all use this internal proc. (define (%char-set-algebra s csets op proc) (for-each (lambda (cset) (let ((s2 (%char-set:s/check cset proc))) (let lp ((i 255)) (cond ((>= i 0) (op s i (si s2 i)) (lp (- i 1))))))) csets)) ;;; -- Complement (define (char-set-complement cs) (let ((s (%char-set:s/check cs 'char-set-complement)) (ans (make-string 256))) (%string-iter (lambda (i v) (%not! ans i v)) s) (make-char-set ans))) (define (char-set-complement! cset) (let ((s (%char-set:s/check cset 'char-set-complement!))) (%string-iter (lambda (i v) (%not! s i v)) s)) cset) ;;; -- Union (define (char-set-union! cset1 . csets) (%char-set-algebra (%char-set:s/check cset1 'char-set-union!) csets %or! 'char-set-union!) cset1) (define (char-set-union . csets) (if (pair? csets) (let ((s (%string-copy (%char-set:s/check (car csets) 'char-set-union)))) (%char-set-algebra s (cdr csets) %or! 'char-set-union) (make-char-set s)) (char-set-copy char-set:empty))) ;;; -- Intersection (define (char-set-intersection! cset1 . csets) (%char-set-algebra (%char-set:s/check cset1 'char-set-intersection!) csets %and! 'char-set-intersection!) cset1) (define (char-set-intersection . csets) (if (pair? csets) (let ((s (%string-copy (%char-set:s/check (car csets) 'char-set-intersection)))) (%char-set-algebra s (cdr csets) %and! 'char-set-intersection) (make-char-set s)) (char-set-copy char-set:full))) ;;; -- Difference (define (char-set-difference! cset1 . csets) (%char-set-algebra (%char-set:s/check cset1 'char-set-difference!) csets %minus! 'char-set-difference!) cset1) (define (char-set-difference cs1 . csets) (if (pair? csets) (let ((s (%string-copy (%char-set:s/check cs1 'char-set-difference)))) (%char-set-algebra s csets %minus! 'char-set-difference) (make-char-set s)) (char-set-copy cs1))) ;;; -- Xor (define (char-set-xor! cset1 . csets) (%char-set-algebra (%char-set:s/check cset1 'char-set-xor!) csets %xor! 'char-set-xor!) cset1) (define (char-set-xor . csets) (if (pair? csets) (let ((s (%string-copy (%char-set:s/check (car csets) 'char-set-xor)))) (%char-set-algebra s (cdr csets) %xor! 'char-set-xor) (make-char-set s)) (char-set-copy char-set:empty))) ;;; -- Difference & intersection (define (%char-set-diff+intersection! diff int csets proc) (for-each (lambda (cs) (%string-iter (lambda (i v) (if (not (zero? v)) (cond ((si=1? diff i) (%set0! diff i) (%set1! int i))))) (%char-set:s/check cs proc))) csets)) (define (char-set-diff+intersection! cs1 cs2 . csets) (let ((s1 (%char-set:s/check cs1 'char-set-diff+intersection!)) (s2 (%char-set:s/check cs2 'char-set-diff+intersection!))) (%string-iter (lambda (i v) (if (zero? v) (%set0! s2 i) (if (si=1? s2 i) (%set0! s1 i)))) s1) (%char-set-diff+intersection! s1 s2 csets 'char-set-diff+intersection!)) (values cs1 cs2)) (define (char-set-diff+intersection cs1 . csets) (let ((diff (string-copy (%char-set:s/check cs1 'char-set-diff+intersection))) (int (make-string 256 c0))) (%char-set-diff+intersection! diff int csets 'char-set-diff+intersection) (values (make-char-set diff) (make-char-set int)))) ;;;; System character sets ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; These definitions are for Latin-1. ;;; ;;; If your Scheme implementation allows you to mark the underlying strings ;;; as immutable, you should do so -- it would be very, very bad if a client's ;;; buggy code corrupted these constants. (define char-set:empty (char-set)) (define char-set:full (char-set-complement char-set:empty)) (define char-set:lower-case (let* ((a-z (ucs-range->char-set #x61 #x7B)) (latin1 (ucs-range->char-set! #xdf #xf7 #t a-z)) (latin2 (ucs-range->char-set! #xf8 #x100 #t latin1))) (char-set-adjoin! latin2 (%latin1->char #xb5)))) (define char-set:upper-case (let ((A-Z (ucs-range->char-set #x41 #x5B))) ;; Add in the Latin-1 upper-case chars. (ucs-range->char-set! #xd8 #xdf #t (ucs-range->char-set! #xc0 #xd7 #t A-Z)))) (define char-set:title-case char-set:empty) (define char-set:letter (let ((u/l (char-set-union char-set:upper-case char-set:lower-case))) (char-set-adjoin! u/l (%latin1->char #xaa) ; FEMININE ORDINAL INDICATOR (%latin1->char #xba)))) ; MASCULINE ORDINAL INDICATOR (define char-set:digit (string->char-set "0123456789")) (define char-set:hex-digit (string->char-set "0123456789abcdefABCDEF")) (define char-set:letter+digit (char-set-union char-set:letter char-set:digit)) (define char-set:punctuation (let ((ascii (string->char-set "!\"#%&'()*,-./:;?@[\\]_{}")) (latin-1-chars (map %latin1->char '(#xA1 ; INVERTED EXCLAMATION MARK #xAB ; LEFT-POINTING DOUBLE ANGLE QUOTATION MARK #xAD ; SOFT HYPHEN #xB7 ; MIDDLE DOT #xBB ; RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK #xBF)))) ; INVERTED QUESTION MARK (list->char-set! latin-1-chars ascii))) (define char-set:symbol (let ((ascii (string->char-set "$+<=>^`|~")) (latin-1-chars (map %latin1->char '(#x00A2 ; CENT SIGN #x00A3 ; POUND SIGN #x00A4 ; CURRENCY SIGN #x00A5 ; YEN SIGN #x00A6 ; BROKEN BAR #x00A7 ; SECTION SIGN #x00A8 ; DIAERESIS #x00A9 ; COPYRIGHT SIGN #x00AC ; NOT SIGN #x00AE ; REGISTERED SIGN #x00AF ; MACRON #x00B0 ; DEGREE SIGN #x00B1 ; PLUS-MINUS SIGN #x00B4 ; ACUTE ACCENT #x00B6 ; PILCROW SIGN #x00B8 ; CEDILLA #x00D7 ; MULTIPLICATION SIGN #x00F7)))) ; DIVISION SIGN (list->char-set! latin-1-chars ascii))) (define char-set:graphic (char-set-union char-set:letter+digit char-set:punctuation char-set:symbol)) (define char-set:whitespace (list->char-set (map %latin1->char '(#x09 ; HORIZONTAL TABULATION #x0A ; LINE FEED #x0B ; VERTICAL TABULATION #x0C ; FORM FEED #x0D ; CARRIAGE RETURN #x20 ; SPACE #xA0)))) (define char-set:printing (char-set-union char-set:whitespace char-set:graphic)) ; NO-BREAK SPACE (define char-set:blank (list->char-set (map %latin1->char '(#x09 ; HORIZONTAL TABULATION #x20 ; SPACE #xA0)))) ; NO-BREAK SPACE (define char-set:iso-control (ucs-range->char-set! #x7F #xA0 #t (ucs-range->char-set 0 32))) (define char-set:ascii (ucs-range->char-set 0 128)) ;;; Porting & performance-tuning notes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; See the section at the beginning of this file on external dependencies. ;;; ;;; First and foremost, rewrite this code to use bit vectors of some sort. ;;; This will give big speedup and memory savings. ;;; ;;; - LET-OPTIONALS* macro. ;;; This is only used once. You can rewrite the use, port the hairy macro ;;; definition (which is implemented using a Clinger-Rees low-level ;;; explicit-renaming macro system), or port the simple, high-level ;;; definition, which is less efficient. ;;; ;;; - :OPTIONAL macro ;;; Very simply defined using an R5RS high-level macro. ;;; ;;; Implementations that can arrange for the base char sets to be immutable ;;; should do so. (E.g., Scheme 48 allows one to mark a string as immutable, ;;; which can be used to protect the underlying strings.) It would be very, ;;; very bad if a client's buggy code corrupted these constants. ;;; ;;; There is a fair amount of argument checking. This is, strictly speaking, ;;; unnecessary -- the actual body of the procedures will blow up if an ;;; illegal value is passed in. However, the error message will not be as good ;;; as if the error were caught at the "higher level." Also, a very, very ;;; smart Scheme compiler may be able to exploit having the type checks done ;;; early, so that the actual body of the procedures can assume proper values. ;;; This isn't likely; this kind of compiler technology isn't common any ;;; longer. ;;; ;;; The overhead of optional-argument parsing is irritating. The optional ;;; arguments must be consed into a rest list on entry, and then parsed out. ;;; Function call should be a matter of a few register moves and a jump; it ;;; should not involve heap allocation! Your Scheme system may have a superior ;;; non-R5RS optional-argument system that can eliminate this overhead. If so, ;;; then this is a prime candidate for optimising these procedures, ;;; *especially* the many optional BASE-CS parameters. ;;; ;;; Note that optional arguments are also a barrier to procedure integration. ;;; If your Scheme system permits you to specify alternate entry points ;;; for a call when the number of optional arguments is known in a manner ;;; that enables inlining/integration, this can provide performance ;;; improvements. ;;; ;;; There is enough *explicit* error checking that *all* internal operations ;;; should *never* produce a type or index-range error. Period. Feel like ;;; living dangerously? *Big* performance win to be had by replacing string ;;; and record-field accessors and setters with unsafe equivalents in the ;;; code. Similarly, fixnum-specific operators can speed up the arithmetic ;;; done on the index values in the inner loops. The only arguments that are ;;; not completely error checked are ;;; - string lists (complete checking requires time proportional to the ;;; length of the list) ;;; - procedure arguments, such as char->char maps & predicates. ;;; There is no way to check the range & domain of procedures in Scheme. ;;; Procedures that take these parameters cannot fully check their ;;; arguments. But all other types to all other procedures are fully ;;; checked. ;;; ;;; This does open up the alternate possibility of simply *removing* these ;;; checks, and letting the safe primitives raise the errors. On a dumb ;;; Scheme system, this would provide speed (by eliminating the redundant ;;; error checks) at the cost of error-message clarity. ;;; ;;; In an interpreted Scheme, some of these procedures, or the internal ;;; routines with % prefixes, are excellent candidates for being rewritten ;;; in C. ;;; ;;; It would also be nice to have the ability to mark some of these ;;; routines as candidates for inlining/integration. ;;; ;;; See the comments preceding the hash function code for notes on tuning ;;; the default bound so that the code never overflows your implementation's ;;; fixnum size into bignum calculation. ;;; ;;; All the %-prefixed routines in this source code are written ;;; to be called internally to this library. They do *not* perform ;;; friendly error checks on the inputs; they assume everything is ;;; proper. They also do not take optional arguments. These two properties ;;; save calling overhead and enable procedure integration -- but they ;;; are not appropriate for exported routines. ;;; Copyright notice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Copyright (c) 1988-1995 Massachusetts Institute of Technology ;;; ;;; This material was developed by the Scheme project at the Massachusetts ;;; Institute of Technology, Department of Electrical Engineering and ;;; Computer Science. Permission to copy and modify this software, to ;;; redistribute either the original software or a modified version, and ;;; to use this software for any purpose is granted, subject to the ;;; following restrictions and understandings. ;;; ;;; 1. Any copy made of this software must include this copyright notice ;;; in full. ;;; ;;; 2. Users of this software agree to make their best efforts (a) to ;;; return to the MIT Scheme project any improvements or extensions that ;;; they make, so that these may be included in future releases; and (b) ;;; to inform MIT of noteworthy uses of this software. ;;; ;;; 3. All materials developed as a consequence of the use of this ;;; software shall duly acknowledge such use, in accordance with the usual ;;; standards of acknowledging credit in academic research. ;;; ;;; 4. MIT has made no warrantee or representation that the operation of ;;; this software will be error-free, and MIT is under no obligation to ;;; provide any services, by way of maintenance, update, or otherwise. ;;; ;;; 5. In conjunction with products arising from the use of this material, ;;; there shall be no use of the name of the Massachusetts Institute of ;;; Technology nor of any adaptation thereof in any advertising, ;;; promotional, or sales literature without prior written consent from ;;; MIT in each case. |
Added srfi/s16/case-lambda.sls.
> > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s16 case-lambda) (export case-lambda) (import (only (rnrs control) case-lambda)) ) |
Added srfi/s19/srfi-19.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 |
;; SRFI-19: Time Data Types and Procedures. ;; ;; Modified by Derick Eddington to be included into the (srfi :19 time) R6RS library. ;; TODO: For implementations which have threads, ;; the thread timing stuff can probably be made to work. ;; ;; Copyright (C) I/NET, Inc. (2000, 2002, 2003). All Rights Reserved. ;; ;; This document and translations of it may be copied and furnished to others, ;; and derivative works that comment on or otherwise explain it or assist in its ;; implementation may be prepared, copied, published and distributed, in whole or ;; in part, without restriction of any kind, provided that the above copyright ;; notice and this paragraph are included on all such copies and derivative works. ;; However, this document itself may not be modified in any way, such as by ;; removing the copyright notice or references to the Scheme Request For ;; Implementation process or editors, except as needed for the purpose of ;; developing SRFIs in which case the procedures for copyrights defined in the SRFI ;; process must be followed, or as required to translate it into languages other ;; than English. ;; ;; The limited permissions granted above are perpetual and will not be revoked ;; by the authors or their successors or assigns. ;; ;; This document and the information contained herein is provided on an "AS IS" ;; basis and THE AUTHOR AND THE SRFI EDITORS DISCLAIM ALL WARRANTIES, EXPRESS OR ;; IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE ;; INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF ;; MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. ;; -- Bug fixes. ;; ;; MAKE-TIME had parameters seconds and nanoseconds reversed; change all ;; references in file to match. Will F: 2002-10-15 ;; ;; DATE-YEAR-DAY returned the wrong day; tm:year-day fixed to do the right ;; thing. Will F: 2002-10-15 ;; It also called an undefined error procedure. ;; ;; DISPLAYING procedure removed. Will F: 2002-10-15. ;; ;; TM:NANO constant corrected. 2002-11-04. ;; ;; The following fixes by Will Fitzgerald, February, 2003. ;; -- Thanks to Steven Ma and others. ;; ;; (CURRENT-TIME 'TIME-THREAD) added. ;; ;; TIME-RESOLUTION for TIME-PROCESS added. ;; ;; TIME comparison procedures (time=?, etc. fixed. ;; ;; Corrected errors in converting between TAI and UTC time. ;; ;; TAI and UTC date converters no longer look at leap seconds, ;; which was an error. ;; ;; corrections to calls to tm:time-error ;; ;; timezone offset not used in date->time-utc and date->julian-day ;; ;; typos in tm:integer-reader-exact, tm:string->date, ;; time-monotonic->time-utc!, tm:char->int fixed ;; ;; corrected "~k", "~f" formatting for date->string (includes fix for ;; "~4" ;; ;; 'split-real' fixed. ;; ;; fixed julian-day->time-utc and variants. ;; ;; changes 2003-02-26, based on comments by Martin Gasbichler. ;; ;; moronic, overly complicated COPY-TIME procedure changed ;; to simple version suggested by Martin Gasbichler. ;; ;; To provide more portability, changed #\Space to #\space ;; and #\tab to #\Tab to (integer->char 9) ;; ;; changed arity-3 calls to / and - to arity 2 calls (again, ;; for more general portability). ;; ;; split-real fixed again -- by removing it, and using ;; 'fractional part'. Will Fitzgerald 5/16/2003. ;; -------------------------------------------------------------- (define-syntax receive (syntax-rules () ((receive formals expression body ...) (call-with-values (lambda () expression) (lambda formals body ...))))) ;;; -- we want receive later on for a couple of small things ;; ;; :OPTIONAL is nice, too (define-syntax :optional (syntax-rules () ((_ val default-value) (if (null? val) default-value (car val))))) (define time-tai 'time-tai) (define time-utc 'time-utc) (define time-monotonic 'time-monotonic) #|(define time-thread 'time-thread) (define time-process 'time-process)|# (define time-duration 'time-duration) ;; example of extension (MZScheme specific) ;(define time-gc 'time-gc) ;;-- LOCALE dependent constants (define tm:locale-number-separator ".") (define tm:locale-abbr-weekday-vector (vector "Sun" "Mon" "Tue" "Wed" "Thu" "Fri" "Sat")) (define tm:locale-long-weekday-vector (vector "Sunday" "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday")) ;; note empty string in 0th place. (define tm:locale-abbr-month-vector (vector "" "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec")) (define tm:locale-long-month-vector (vector "" "January" "February" "March" "April" "May" "June" "July" "August" "September" "October" "November" "December")) (define tm:locale-pm "PM") (define tm:locale-am "AM") ;; See date->string (define tm:locale-date-time-format "~a ~b ~d ~H:~M:~S~z ~Y") (define tm:locale-short-date-format "~m/~d/~y") (define tm:locale-time-format "~H:~M:~S") (define tm:iso-8601-date-time-format "~Y-~m-~dT~H:~M:~S~z") ;;-- Miscellaneous Constants. ;;-- only the tm:tai-epoch-in-jd might need changing if ;; a different epoch is used. (define tm:nano (expt 10 9)) (define tm:sid 86400) ; seconds in a day (define tm:sihd 43200) ; seconds in a half day (define tm:tai-epoch-in-jd 4881175/2) ; julian day number for 'the epoch' ;;; A Very simple Error system for the time procedures ;;; (define tm:time-error-types '((invalid-clock-type . "invalid clock type") (unsupported-clock-type . "unsupported clock type") (incompatible-time-types . "incompatible time types") (not-duration . "not duration") (dates-are-immutable . "dates are immutable") (bad-date-format-string . "bad date format string") (bad-date-template-string . "bad date template string") (invalid-month-specification . "invalid month specification") )) (define (tm:time-error caller type value) (cond [(assoc type tm:time-error-types) => (lambda (p) (if value (error caller (cdr p) value) (error caller (cdr p))))] [else (error caller "(library (srfi :19 time)) internal error: unsupported error type" type)])) ;; A table of leap seconds ;; See ftp://maia.usno.navy.mil/ser7/tai-utc.dat ;; and update as necessary. ;; this procedures reads the file in the abover ;; format and creates the leap second table ;; it also calls the almost standard, but not R5 procedures read-line ;; & open-input-string ;; ie (set! tm:leap-second-table (tm:read-tai-utc-date "tai-utc.dat")) (define (tm:read-tai-utc-data filename) (define (convert-jd jd) (* (- (inexact->exact jd) tm:tai-epoch-in-jd) tm:sid)) (define (convert-sec sec) (inexact->exact sec)) (let ( (port (open-input-file filename)) (table '()) ) (let loop ((line (read-line port))) (if (not (eq? line eof)) (begin (let* ( (data (read (open-input-string (string-append "(" line ")")))) (year (car data)) (jd (cadddr (cdr data))) (secs (cadddr (cdddr data))) ) (if (>= year 1972) (set! table (cons (cons (convert-jd jd) (convert-sec secs)) table))) (loop (read-line port)))))) table)) ;; each entry is ( utc seconds since epoch . # seconds to add for tai ) ;; note they go higher to lower, and end in 1972. (define tm:leap-second-table '((1136073600 . 33) (915148800 . 32) (867715200 . 31) (820454400 . 30) (773020800 . 29) (741484800 . 28) (709948800 . 27) (662688000 . 26) (631152000 . 25) (567993600 . 24) (489024000 . 23) (425865600 . 22) (394329600 . 21) (362793600 . 20) (315532800 . 19) (283996800 . 18) (252460800 . 17) (220924800 . 16) (189302400 . 15) (157766400 . 14) (126230400 . 13) (94694400 . 12) (78796800 . 11) (63072000 . 10))) (define (read-leap-second-table filename) (set! tm:leap-second-table (tm:read-tai-utc-data filename)) (values)) (define (tm:leap-second-delta utc-seconds) (letrec ( (lsd (lambda (table) (cond ((>= utc-seconds (caar table)) (cdar table)) (else (lsd (cdr table)))))) ) (if (< utc-seconds (* (- 1972 1970) 365 tm:sid)) 0 (lsd tm:leap-second-table)))) ;; going from tai seconds to utc seconds ... (define (tm:leap-second-neg-delta tai-seconds) (letrec ( (lsd (lambda (table) (cond ((null? table) 0) ((<= (cdar table) (- tai-seconds (caar table))) (cdar table)) (else (lsd (cdr table)))))) ) (if (< tai-seconds (* (- 1972 1970) 365 tm:sid)) 0 (lsd tm:leap-second-table)))) ;;; the time structure; creates the accessors, too. ;;; wf: changed to match srfi documentation. (define-record-type time (fields (mutable type) (mutable nanosecond) (mutable second))) ;; thanks, Martin Gasbichler ... (define (copy-time time) (make-time (time-type time) (time-nanosecond time) ; original had this mistakenly swapped with time-second (time-second time))) ;;; current-time ;;; specific time getters. ;; I'm not sure why the original was using time-nanoseconds ;; as 10000 * the milliseconds (define (tm:get-time-of-day) (let ([ct (host:current-time)]) (values (host:time-second ct) (host:time-nanosecond ct)))) (define (tm:current-time-utc) (receive (seconds nanos) (tm:get-time-of-day) (make-time time-utc nanos seconds))) (define (tm:current-time-tai) (receive (seconds nanos) (tm:get-time-of-day) (make-time time-tai nanos (+ seconds (tm:leap-second-delta seconds)) ))) #|(define (tm:current-time-ms-time time-type proc) (let ((current-ms (proc))) (make-time time-type XXX ZZZ ))) |# ;; -- we define it to be the same as tai. ;; a different implemation of current-time-montonic ;; will require rewriting all of the time-monotonic converters, ;; of course. (define (tm:current-time-monotonic) (receive (seconds nanos) (tm:get-time-of-day) (make-time time-monotonic nanos (+ seconds (tm:leap-second-delta seconds)) ))) #|(define (tm:current-time-thread) (tm:current-time-ms-time time-process current-process-milliseconds)) (define (tm:current-time-process) (tm:current-time-ms-time time-process current-process-milliseconds)) (define (tm:current-time-gc) (tm:current-time-ms-time time-gc current-gc-milliseconds)) |# (define (current-time . clock-type) (let ( (clock-type (:optional clock-type time-utc)) ) (cond ((eq? clock-type time-tai) (tm:current-time-tai)) ((eq? clock-type time-utc) (tm:current-time-utc)) ((eq? clock-type time-monotonic) (tm:current-time-monotonic)) #|((eq? clock-type time-thread) (tm:current-time-thread)) ((eq? clock-type time-process) (tm:current-time-process)) ((eq? clock-type time-gc) (tm:current-time-gc))|# (else (tm:time-error 'current-time 'invalid-clock-type clock-type))))) ;; -- time resolution ;; this is the resolution of the clock in nanoseconds. ;; this will be implementation specific. (define (time-resolution . clock-type) (let ((clock-type (:optional clock-type time-utc))) (cond ((eq? clock-type time-tai) host:time-resolution) ((eq? clock-type time-utc) host:time-resolution) ((eq? clock-type time-monotonic) host:time-resolution) #|((eq? clock-type time-thread) host:time-resolution) ((eq? clock-type time-process) host:time-resolution) ((eq? clock-type time-gc) host:time-resolution)|# (else (tm:time-error 'time-resolution 'invalid-clock-type clock-type))))) ;; -- time comparisons (define (tm:time-compare-check time1 time2 caller) (if (or (not (and (time? time1) (time? time2))) (not (eq? (time-type time1) (time-type time2)))) (tm:time-error caller 'incompatible-time-types #f) #t)) (define (time=? time1 time2) (tm:time-compare-check time1 time2 'time=?) (and (= (time-second time1) (time-second time2)) (= (time-nanosecond time1) (time-nanosecond time2)))) (define (time>? time1 time2) (tm:time-compare-check time1 time2 'time>?) (or (> (time-second time1) (time-second time2)) (and (= (time-second time1) (time-second time2)) (> (time-nanosecond time1) (time-nanosecond time2))))) (define (time<? time1 time2) (tm:time-compare-check time1 time2 'time<?) (or (< (time-second time1) (time-second time2)) (and (= (time-second time1) (time-second time2)) (< (time-nanosecond time1) (time-nanosecond time2))))) (define (time>=? time1 time2) (tm:time-compare-check time1 time2 'time>=?) (or (>= (time-second time1) (time-second time2)) (and (= (time-second time1) (time-second time2)) (>= (time-nanosecond time1) (time-nanosecond time2))))) (define (time<=? time1 time2) (tm:time-compare-check time1 time2 'time<=?) (or (<= (time-second time1) (time-second time2)) (and (= (time-second time1) (time-second time2)) (<= (time-nanosecond time1) (time-nanosecond time2))))) ;; -- time arithmetic (define (tm:time->nanoseconds time) #|(define (sign1 n) ; must be code rot (if (negative? n) -1 1))|# (+ (* (time-second time) tm:nano) (time-nanosecond time))) (define (tm:nanoseconds->time time-type nanoseconds) (make-time time-type (remainder nanoseconds tm:nano) (quotient nanoseconds tm:nano))) (define (tm:nanoseconds->values nanoseconds) (div-and-mod nanoseconds tm:nano)) (define (tm:time-difference time1 time2 time3) (if (or (not (and (time? time1) (time? time2))) (not (eq? (time-type time1) (time-type time2)))) (tm:time-error 'time-difference 'incompatible-time-types #f)) (time-type-set! time3 time-duration) (if (time=? time1 time2) (begin (time-second-set! time3 0) (time-nanosecond-set! time3 0)) (receive (secs nanos) (tm:nanoseconds->values (- (tm:time->nanoseconds time1) (tm:time->nanoseconds time2))) (time-second-set! time3 secs) (time-nanosecond-set! time3 nanos))) time3) (define (time-difference time1 time2) (tm:time-difference time1 time2 (make-time #f #f #f))) (define (time-difference! time1 time2) (tm:time-difference time1 time2 time1)) (define (tm:add-duration time1 duration time3) (if (not (and (time? time1) (time? duration))) (tm:time-error 'add-duration 'incompatible-time-types #f)) (if (not (eq? (time-type duration) time-duration)) (tm:time-error 'add-duration 'not-duration duration) (let ( (sec-plus (+ (time-second time1) (time-second duration))) (nsec-plus (+ (time-nanosecond time1) (time-nanosecond duration))) ) (let ((r (remainder nsec-plus tm:nano)) (q (quotient nsec-plus tm:nano))) ; (time-type-set! time3 (time-type time1)) (if (negative? r) (begin (time-second-set! time3 (+ sec-plus q -1)) (time-nanosecond-set! time3 (+ tm:nano r))) (begin (time-second-set! time3 (+ sec-plus q)) (time-nanosecond-set! time3 r))) time3)))) (define (add-duration time1 duration) (tm:add-duration time1 duration (make-time (time-type time1) #f #f))) (define (add-duration! time1 duration) (tm:add-duration time1 duration time1)) (define (tm:subtract-duration time1 duration time3) (if (not (and (time? time1) (time? duration))) (tm:time-error 'add-duration 'incompatible-time-types #f)) (if (not (eq? (time-type duration) time-duration)) (tm:time-error 'tm:subtract-duration 'not-duration duration) (let ( (sec-minus (- (time-second time1) (time-second duration))) (nsec-minus (- (time-nanosecond time1) (time-nanosecond duration))) ) (let ((r (remainder nsec-minus tm:nano)) (q (quotient nsec-minus tm:nano))) (if (negative? r) (begin (time-second-set! time3 (- sec-minus q 1)) (time-nanosecond-set! time3 (+ tm:nano r))) (begin (time-second-set! time3 (- sec-minus q)) (time-nanosecond-set! time3 r))) time3)))) (define (subtract-duration time1 duration) (tm:subtract-duration time1 duration (make-time (time-type time1) #f #f))) (define (subtract-duration! time1 duration) (tm:subtract-duration time1 duration time1)) ;; -- converters between types. (define (tm:time-tai->time-utc! time-in time-out caller) (if (not (eq? (time-type time-in) time-tai)) (tm:time-error caller 'incompatible-time-types time-in)) (time-type-set! time-out time-utc) (time-nanosecond-set! time-out (time-nanosecond time-in)) (time-second-set! time-out (- (time-second time-in) (tm:leap-second-neg-delta (time-second time-in)))) time-out) (define (time-tai->time-utc time-in) (tm:time-tai->time-utc! time-in (make-time #f #f #f) 'time-tai->time-utc)) (define (time-tai->time-utc! time-in) (tm:time-tai->time-utc! time-in time-in 'time-tai->time-utc!)) (define (tm:time-utc->time-tai! time-in time-out caller) (if (not (eq? (time-type time-in) time-utc)) (tm:time-error caller 'incompatible-time-types time-in)) (time-type-set! time-out time-tai) (time-nanosecond-set! time-out (time-nanosecond time-in)) (time-second-set! time-out (+ (time-second time-in) (tm:leap-second-delta (time-second time-in)))) time-out) (define (time-utc->time-tai time-in) (tm:time-utc->time-tai! time-in (make-time #f #f #f) 'time-utc->time-tai)) (define (time-utc->time-tai! time-in) (tm:time-utc->time-tai! time-in time-in 'time-utc->time-tai!)) ;; -- these depend on time-monotonic having the same definition as time-tai! (define (time-monotonic->time-utc time-in) (if (not (eq? (time-type time-in) time-monotonic)) (tm:time-error 'time-monotoinc->time-utc 'incompatible-time-types time-in)) (let ((ntime (copy-time time-in))) (time-type-set! ntime time-tai) (tm:time-tai->time-utc! ntime ntime 'time-monotonic->time-utc))) (define (time-monotonic->time-utc! time-in) (if (not (eq? (time-type time-in) time-monotonic)) (tm:time-error 'time-monotonic->time-utc! 'incompatible-time-types time-in)) (time-type-set! time-in time-tai) (tm:time-tai->time-utc! time-in time-in 'time-monotonic->time-utc)) (define (time-monotonic->time-tai time-in) (if (not (eq? (time-type time-in) time-monotonic)) (tm:time-error 'time-monotonic->time-tai 'incompatible-time-types time-in)) (let ((ntime (copy-time time-in))) (time-type-set! ntime time-tai) ntime)) (define (time-monotonic->time-tai! time-in) (if (not (eq? (time-type time-in) time-monotonic)) (tm:time-error 'time-monotonic->time-tai! 'incompatible-time-types time-in)) (time-type-set! time-in time-tai) time-in) (define (time-utc->time-monotonic time-in) (if (not (eq? (time-type time-in) time-utc)) (tm:time-error 'time-utc->time-monotonic 'incompatible-time-types time-in)) (let ((ntime (tm:time-utc->time-tai! time-in (make-time #f #f #f) 'time-utc->time-monotonic))) (time-type-set! ntime time-monotonic) ntime)) (define (time-utc->time-monotonic! time-in) (if (not (eq? (time-type time-in) time-utc)) (tm:time-error 'time-utc->time-montonic! 'incompatible-time-types time-in)) (let ((ntime (tm:time-utc->time-tai! time-in time-in 'time-utc->time-monotonic!))) (time-type-set! ntime time-monotonic) ntime)) (define (time-tai->time-monotonic time-in) (if (not (eq? (time-type time-in) time-tai)) (tm:time-error 'time-tai->time-monotonic 'incompatible-time-types time-in)) (let ((ntime (copy-time time-in))) (time-type-set! ntime time-monotonic) ntime)) (define (time-tai->time-monotonic! time-in) (if (not (eq? (time-type time-in) time-tai)) (tm:time-error 'time-tai->time-monotonic! 'incompatible-time-types time-in)) (time-type-set! time-in time-monotonic) time-in) ;; -- date structures (define-record-type date (fields (mutable nanosecond) (mutable second) (mutable minute) (mutable hour) (mutable day) (mutable month) (mutable year) (mutable zone-offset))) ;; redefine setters (in Ikarus version, only to keep names the same in below code) (define tm:set-date-nanosecond! date-nanosecond-set!) (define tm:set-date-second! date-second-set!) (define tm:set-date-minute! date-minute-set!) (define tm:set-date-hour! date-hour-set!) (define tm:set-date-day! date-day-set!) (define tm:set-date-month! date-month-set!) (define tm:set-date-year! date-year-set!) (define tm:set-date-zone-offset! date-zone-offset-set!) ;; gives the julian day which starts at noon. (define (tm:encode-julian-day-number day month year) (let* ((a (quotient (- 14 month) 12)) (y (- (- (+ year 4800) a) (if (negative? year) -1 0))) (m (- (+ month (* 12 a)) 3))) (+ day (quotient (+ (* 153 m) 2) 5) (* 365 y) (quotient y 4) (- (quotient y 100)) (quotient y 400) -32045))) (define (tm:char-pos char str index len) (cond ((>= index len) #f) ((char=? (string-ref str index) char) index) (else (tm:char-pos char str (+ index 1) len)))) (define (tm:fractional-part r) (if (integer? r) "0" (let ((str (number->string (exact->inexact r)))) (let ((ppos (tm:char-pos #\. str 0 (string-length str)))) (substring str (+ ppos 1) (string-length str)))))) ;; gives the seconds/date/month/year (define (tm:decode-julian-day-number jdn) (let* ((days (truncate jdn)) (a (+ days 32044)) (b (quotient (+ (* 4 a) 3) 146097)) (c (- a (quotient (* 146097 b) 4))) (d (quotient (+ (* 4 c) 3) 1461)) (e (- c (quotient (* 1461 d) 4))) (m (quotient (+ (* 5 e) 2) 153)) (y (+ (* 100 b) d -4800 (quotient m 10)))) (values ; seconds date month year (* (- jdn days) tm:sid) (+ e (- (quotient (+ (* 153 m) 2) 5)) 1) (+ m 3 (* -12 (quotient m 10))) (if (>= 0 y) (- y 1) y)) )) (define (tm:local-tz-offset) (host:time-gmt-offset (host:current-time))) ;; special thing -- ignores nanos (define (tm:time->julian-day-number seconds tz-offset) (+ (/ (+ seconds tz-offset tm:sihd) tm:sid) tm:tai-epoch-in-jd)) (define (tm:find proc l) (if (null? l) #f (if (proc (car l)) #t (tm:find proc (cdr l))))) (define (tm:tai-before-leap-second? second) (tm:find (lambda (x) (= second (- (+ (car x) (cdr x)) 1))) tm:leap-second-table)) (define (tm:time->date time tz-offset ttype) (if (not (eq? (time-type time) ttype)) (tm:time-error 'time->date 'incompatible-time-types time)) (let* ( (offset (:optional tz-offset (tm:local-tz-offset))) ) (receive (secs date month year) (tm:decode-julian-day-number (tm:time->julian-day-number (time-second time) offset)) (let* ( (hours (quotient secs (* 60 60))) (rem (remainder secs (* 60 60))) (minutes (quotient rem 60)) (seconds (remainder rem 60)) ) (make-date (time-nanosecond time) seconds minutes hours date month year offset))))) (define (time-tai->date time . tz-offset) (if (tm:tai-before-leap-second? (time-second time)) ;; if it's *right* before the leap, we need to pretend to subtract a second ... (let ((d (tm:time->date (subtract-duration! (time-tai->time-utc time) (make-time time-duration 0 1)) tz-offset time-utc))) (tm:set-date-second! d 60) d) (tm:time->date (time-tai->time-utc time) tz-offset time-utc))) (define (time-utc->date time . tz-offset) (tm:time->date time tz-offset time-utc)) ;; again, time-monotonic is the same as time tai (define (time-monotonic->date time . tz-offset) (tm:time->date time tz-offset time-monotonic)) (define (date->time-utc date) (let ( (nanosecond (date-nanosecond date)) (second (date-second date)) (minute (date-minute date)) (hour (date-hour date)) (day (date-day date)) (month (date-month date)) (year (date-year date)) (offset (date-zone-offset date)) ) (let ( (jdays (- (tm:encode-julian-day-number day month year) tm:tai-epoch-in-jd)) ) (make-time time-utc nanosecond (+ (* (- jdays 1/2) 24 60 60) (* hour 60 60) (* minute 60) second (- offset)) )))) (define (date->time-tai d) (if (= (date-second d) 60) (subtract-duration! (time-utc->time-tai! (date->time-utc d)) (make-time time-duration 0 1)) (time-utc->time-tai! (date->time-utc d)))) (define (date->time-monotonic date) (time-utc->time-monotonic! (date->time-utc date))) (define (tm:leap-year? year) (or (= (modulo year 400) 0) (and (= (modulo year 4) 0) (not (= (modulo year 100) 0))))) (define (leap-year? date) (tm:leap-year? (date-year date))) ;; tm:year-day fixed: adding wrong number of days. (define tm:month-assoc '((0 . 0) (1 . 31) (2 . 59) (3 . 90) (4 . 120) (5 . 151) (6 . 181) (7 . 212) (8 . 243) (9 . 273) (10 . 304) (11 . 334))) (define (tm:year-day day month year) (let ((days-pr (assoc (- month 1) tm:month-assoc))) (if (not days-pr) (tm:time-error 'date-year-day 'invalid-month-specification month)) (if (and (tm:leap-year? year) (> month 2)) (+ day (cdr days-pr) 1) (+ day (cdr days-pr))))) (define (date-year-day date) (tm:year-day (date-day date) (date-month date) (date-year date))) ;; from calendar faq (define (tm:week-day day month year) (let* ((a (quotient (- 14 month) 12)) (y (- year a)) (m (+ month (* 12 a) -2))) (modulo (+ day y (quotient y 4) (- (quotient y 100)) (quotient y 400) (quotient (* 31 m) 12)) 7))) (define (date-week-day date) (tm:week-day (date-day date) (date-month date) (date-year date))) (define (tm:days-before-first-week date day-of-week-starting-week) (let* ( (first-day (make-date 0 0 0 0 1 1 (date-year date) #f)) (fdweek-day (date-week-day first-day)) ) (modulo (- day-of-week-starting-week fdweek-day) 7))) (define (date-week-number date day-of-week-starting-week) (quotient (- (date-year-day date) (tm:days-before-first-week date day-of-week-starting-week)) 7)) (define (current-date . tz-offset) (time-utc->date (current-time time-utc) (:optional tz-offset (tm:local-tz-offset)))) ;; given a 'two digit' number, find the year within 50 years +/- (define (tm:natural-year n) (let* ( (current-year (date-year (current-date))) (current-century (* (quotient current-year 100) 100)) ) (cond ((>= n 100) n) ((< n 0) n) ((<= (- (+ current-century n) current-year) 50) (+ current-century n)) (else (+ (- current-century 100) n))))) (define (date->julian-day date) (let ( (nanosecond (date-nanosecond date)) (second (date-second date)) (minute (date-minute date)) (hour (date-hour date)) (day (date-day date)) (month (date-month date)) (year (date-year date)) (offset (date-zone-offset date)) ) (+ (tm:encode-julian-day-number day month year) (- 1/2) (+ (/ (+ (* hour 60 60) (* minute 60) second (/ nanosecond tm:nano) (- offset)) tm:sid))))) (define (date->modified-julian-day date) (- (date->julian-day date) 4800001/2)) (define (time-utc->julian-day time) (if (not (eq? (time-type time) time-utc)) (tm:time-error 'time-utc->julian-day 'incompatible-time-types time)) (+ (/ (+ (time-second time) (/ (time-nanosecond time) tm:nano)) tm:sid) tm:tai-epoch-in-jd)) (define (time-utc->modified-julian-day time) (- (time-utc->julian-day time) 4800001/2)) (define (time-tai->julian-day time) (if (not (eq? (time-type time) time-tai)) (tm:time-error 'time-tai->julian-day 'incompatible-time-types time)) (+ (/ (+ (- (time-second time) (tm:leap-second-delta (time-second time))) (/ (time-nanosecond time) tm:nano)) tm:sid) tm:tai-epoch-in-jd)) (define (time-tai->modified-julian-day time) (- (time-tai->julian-day time) 4800001/2)) ;; this is the same as time-tai->julian-day (define (time-monotonic->julian-day time) (if (not (eq? (time-type time) time-monotonic)) (tm:time-error 'time-monotonic->julian-day 'incompatible-time-types time)) (+ (/ (+ (- (time-second time) (tm:leap-second-delta (time-second time))) (/ (time-nanosecond time) tm:nano)) tm:sid) tm:tai-epoch-in-jd)) (define (time-monotonic->modified-julian-day time) (- (time-monotonic->julian-day time) 4800001/2)) (define (julian-day->time-utc jdn) (let ( (nanosecs (* tm:nano tm:sid (- jdn tm:tai-epoch-in-jd))) ) (make-time time-utc (remainder nanosecs tm:nano) (floor (/ nanosecs tm:nano))))) (define (julian-day->time-tai jdn) (time-utc->time-tai! (julian-day->time-utc jdn))) (define (julian-day->time-monotonic jdn) (time-utc->time-monotonic! (julian-day->time-utc jdn))) (define (julian-day->date jdn . tz-offset) (let ((offset (:optional tz-offset (tm:local-tz-offset)))) (time-utc->date (julian-day->time-utc jdn) offset))) (define (modified-julian-day->date jdn . tz-offset) (let ((offset (:optional tz-offset (tm:local-tz-offset)))) (julian-day->date (+ jdn 4800001/2) offset))) (define (modified-julian-day->time-utc jdn) (julian-day->time-utc (+ jdn 4800001/2))) (define (modified-julian-day->time-tai jdn) (julian-day->time-tai (+ jdn 4800001/2))) (define (modified-julian-day->time-monotonic jdn) (julian-day->time-monotonic (+ jdn 4800001/2))) (define (current-julian-day) (time-utc->julian-day (current-time time-utc))) (define (current-modified-julian-day) (time-utc->modified-julian-day (current-time time-utc))) ;; returns a string rep. of number N, of minimum LENGTH, ;; padded with character PAD-WITH. If PAD-WITH if #f, ;; no padding is done, and it's as if number->string was used. ;; if string is longer than LENGTH, it's as if number->string was used. (define (tm:padding n pad-with length) (let* ( (str (number->string n)) (str-len (string-length str)) ) (if (or (> str-len length) (not pad-with)) str (let* ( (new-str (make-string length pad-with)) (new-str-offset (- (string-length new-str) str-len)) ) (do ((i 0 (+ i 1))) ((>= i (string-length str))) (string-set! new-str (+ new-str-offset i) (string-ref str i))) new-str)))) (define (tm:last-n-digits i n) (abs (remainder i (expt 10 n)))) (define (tm:locale-abbr-weekday n) (vector-ref tm:locale-abbr-weekday-vector n)) (define (tm:locale-long-weekday n) (vector-ref tm:locale-long-weekday-vector n)) (define (tm:locale-abbr-month n) (vector-ref tm:locale-abbr-month-vector n)) (define (tm:locale-long-month n) (vector-ref tm:locale-long-month-vector n)) (define (tm:vector-find needle haystack comparator) (let ((len (vector-length haystack))) (define (tm:vector-find-int index) (cond ((>= index len) #f) ((comparator needle (vector-ref haystack index)) index) (else (tm:vector-find-int (+ index 1))))) (tm:vector-find-int 0))) (define (tm:locale-abbr-weekday->index string) (tm:vector-find string tm:locale-abbr-weekday-vector string=?)) (define (tm:locale-long-weekday->index string) (tm:vector-find string tm:locale-long-weekday-vector string=?)) (define (tm:locale-abbr-month->index string) (tm:vector-find string tm:locale-abbr-month-vector string=?)) (define (tm:locale-long-month->index string) (tm:vector-find string tm:locale-long-month-vector string=?)) ;; do nothing. ;; Your implementation might want to do something... ;; (define (tm:locale-print-time-zone date port) (values)) ;; Again, locale specific. (define (tm:locale-am/pm hr) (if (> hr 11) tm:locale-pm tm:locale-am)) (define (tm:tz-printer offset port) (cond ((= offset 0) (display "Z" port)) ((negative? offset) (display "-" port)) (else (display "+" port))) (if (not (= offset 0)) (let ( (hours (abs (quotient offset (* 60 60)))) (minutes (abs (quotient (remainder offset (* 60 60)) 60))) ) (display (tm:padding hours #\0 2) port) (display (tm:padding minutes #\0 2) port)))) ;; A table of output formatting directives. ;; the first time is the format char. ;; the second is a procedure that takes the date, a padding character ;; (which might be #f), and the output port. ;; (define tm:directives (list (cons #\~ (lambda (date pad-with port) (display #\~ port))) (cons #\a (lambda (date pad-with port) (display (tm:locale-abbr-weekday (date-week-day date)) port))) (cons #\A (lambda (date pad-with port) (display (tm:locale-long-weekday (date-week-day date)) port))) (cons #\b (lambda (date pad-with port) (display (tm:locale-abbr-month (date-month date)) port))) (cons #\B (lambda (date pad-with port) (display (tm:locale-long-month (date-month date)) port))) (cons #\c (lambda (date pad-with port) (display (date->string date tm:locale-date-time-format) port))) (cons #\d (lambda (date pad-with port) (display (tm:padding (date-day date) #\0 2) port))) (cons #\D (lambda (date pad-with port) (display (date->string date "~m/~d/~y") port))) (cons #\e (lambda (date pad-with port) (display (tm:padding (date-day date) #\space 2) port))) (cons #\f (lambda (date pad-with port) (if (> (date-nanosecond date) tm:nano) (display (tm:padding (+ (date-second date) 1) pad-with 2) port) (display (tm:padding (date-second date) pad-with 2) port)) (let* ((ns (tm:fractional-part (/ (date-nanosecond date) tm:nano 1.0))) (le (string-length ns))) (if (> le 2) (begin (display tm:locale-number-separator port) (display (substring ns 2 le) port)))))) (cons #\h (lambda (date pad-with port) (display (date->string date "~b") port))) (cons #\H (lambda (date pad-with port) (display (tm:padding (date-hour date) pad-with 2) port))) (cons #\I (lambda (date pad-with port) (let ((hr (date-hour date))) (if (> hr 12) (display (tm:padding (- hr 12) pad-with 2) port) (display (tm:padding hr pad-with 2) port))))) (cons #\j (lambda (date pad-with port) (display (tm:padding (date-year-day date) pad-with 3) port))) (cons #\k (lambda (date pad-with port) (display (tm:padding (date-hour date) #\0 2) port))) (cons #\l (lambda (date pad-with port) (let ((hr (if (> (date-hour date) 12) (- (date-hour date) 12) (date-hour date)))) (display (tm:padding hr #\space 2) port)))) (cons #\m (lambda (date pad-with port) (display (tm:padding (date-month date) pad-with 2) port))) (cons #\M (lambda (date pad-with port) (display (tm:padding (date-minute date) pad-with 2) port))) (cons #\n (lambda (date pad-with port) (newline port))) (cons #\N (lambda (date pad-with port) (display (tm:padding (date-nanosecond date) pad-with 9) port))) (cons #\p (lambda (date pad-with port) (display (tm:locale-am/pm (date-hour date)) port))) (cons #\r (lambda (date pad-with port) (display (date->string date "~I:~M:~S ~p") port))) (cons #\s (lambda (date pad-with port) (display (time-second (date->time-utc date)) port))) (cons #\S (lambda (date pad-with port) (if (> (date-nanosecond date) tm:nano) (display (tm:padding (+ (date-second date) 1) pad-with 2) port) (display (tm:padding (date-second date) pad-with 2) port)))) (cons #\t (lambda (date pad-with port) (display (integer->char 9) port))) (cons #\T (lambda (date pad-with port) (display (date->string date "~H:~M:~S") port))) (cons #\U (lambda (date pad-with port) (if (> (tm:days-before-first-week date 0) 0) (display (tm:padding (+ (date-week-number date 0) 1) #\0 2) port) (display (tm:padding (date-week-number date 0) #\0 2) port)))) (cons #\V (lambda (date pad-with port) (display (tm:padding (date-week-number date 1) #\0 2) port))) (cons #\w (lambda (date pad-with port) (display (date-week-day date) port))) (cons #\x (lambda (date pad-with port) (display (date->string date tm:locale-short-date-format) port))) (cons #\X (lambda (date pad-with port) (display (date->string date tm:locale-time-format) port))) (cons #\W (lambda (date pad-with port) (if (> (tm:days-before-first-week date 1) 0) (display (tm:padding (+ (date-week-number date 1) 1) #\0 2) port) (display (tm:padding (date-week-number date 1) #\0 2) port)))) (cons #\y (lambda (date pad-with port) (display (tm:padding (tm:last-n-digits (date-year date) 2) pad-with 2) port))) (cons #\Y (lambda (date pad-with port) (display (date-year date) port))) (cons #\z (lambda (date pad-with port) (tm:tz-printer (date-zone-offset date) port))) (cons #\Z (lambda (date pad-with port) (tm:locale-print-time-zone date port))) (cons #\1 (lambda (date pad-with port) (display (date->string date "~Y-~m-~d") port))) (cons #\2 (lambda (date pad-with port) (display (date->string date "~k:~M:~S~z") port))) (cons #\3 (lambda (date pad-with port) (display (date->string date "~k:~M:~S") port))) (cons #\4 (lambda (date pad-with port) (display (date->string date "~Y-~m-~dT~k:~M:~S~z") port))) (cons #\5 (lambda (date pad-with port) (display (date->string date "~Y-~m-~dT~k:~M:~S") port))) )) (define (tm:get-formatter char) (let ( (associated (assoc char tm:directives)) ) (if associated (cdr associated) #f))) (define (tm:date-printer date index format-string str-len port) (if (>= index str-len) (values) (let ( (current-char (string-ref format-string index)) ) (if (not (char=? current-char #\~)) (begin (display current-char port) (tm:date-printer date (+ index 1) format-string str-len port)) (if (= (+ index 1) str-len) ; bad format string. (tm:time-error 'tm:date-printer 'bad-date-format-string format-string) (let ( (pad-char? (string-ref format-string (+ index 1))) ) (cond ((char=? pad-char? #\-) (if (= (+ index 2) str-len) ; bad format string. (tm:time-error 'tm:date-printer 'bad-date-format-string format-string) (let ( (formatter (tm:get-formatter (string-ref format-string (+ index 2)))) ) (if (not formatter) (tm:time-error 'tm:date-printer 'bad-date-format-string format-string) (begin (formatter date #f port) (tm:date-printer date (+ index 3) format-string str-len port)))))) ((char=? pad-char? #\_) (if (= (+ index 2) str-len) ; bad format string. (tm:time-error 'tm:date-printer 'bad-date-format-string format-string) (let ( (formatter (tm:get-formatter (string-ref format-string (+ index 2)))) ) (if (not formatter) (tm:time-error 'tm:date-printer 'bad-date-format-string format-string) (begin (formatter date #\space port) (tm:date-printer date (+ index 3) format-string str-len port)))))) (else (let ( (formatter (tm:get-formatter (string-ref format-string (+ index 1)))) ) (if (not formatter) (tm:time-error 'tm:date-printer 'bad-date-format-string format-string) (begin (formatter date #\0 port) (tm:date-printer date (+ index 2) format-string str-len port)))))))))))) (define (date->string date . format-string) (let ( (str-port (open-output-string)) (fmt-str (:optional format-string "~c")) ) (tm:date-printer date 0 fmt-str (string-length fmt-str) str-port) (get-output-string str-port))) (define (tm:char->int ch) (cond ((char=? ch #\0) 0) ((char=? ch #\1) 1) ((char=? ch #\2) 2) ((char=? ch #\3) 3) ((char=? ch #\4) 4) ((char=? ch #\5) 5) ((char=? ch #\6) 6) ((char=? ch #\7) 7) ((char=? ch #\8) 8) ((char=? ch #\9) 9) (else (tm:time-error 'string->date 'bad-date-template-string (list "Non-integer character" ch ))))) ;; read an integer upto n characters long on port; upto -> #f if any length (define (tm:integer-reader upto port) (define (accum-int port accum nchars) (let ((ch (peek-char port))) (if (or (eof-object? ch) (not (char-numeric? ch)) (and upto (>= nchars upto ))) accum (accum-int port (+ (* accum 10) (tm:char->int (read-char port))) (+ nchars 1))))) (accum-int port 0 0)) (define (tm:make-integer-reader upto) (lambda (port) (tm:integer-reader upto port))) ;; read an fractional integer upto n characters long on port; upto -> #f if any length ;; ;; The return value is normalized to upto decimal places. For example, if upto is 9 and ;; the string read is "123", the return value is 123000000. (define (tm:fractional-integer-reader upto port) (define (accum-int port accum nchars) (let ((ch (peek-char port))) (if (or (eof-object? ch) (not (char-numeric? ch)) (and upto (>= nchars upto ))) (* accum (expt 10 (- upto nchars))) (accum-int port (+ (* accum 10) (tm:char->int (read-char port))) (+ nchars 1))))) (accum-int port 0 0)) (define (tm:make-fractional-integer-reader upto) (lambda (port) (tm:fractional-integer-reader upto port))) ;; read *exactly* n characters and convert to integer; could be padded (define (tm:integer-reader-exact n port) (let ( (padding-ok #t) ) (define (accum-int port accum nchars) (let ((ch (peek-char port))) (cond ((>= nchars n) accum) ((eof-object? ch) (tm:time-error 'string->date 'bad-date-template-string "Premature ending to integer read.")) ((char-numeric? ch) (set! padding-ok #f) (accum-int port (+ (* accum 10) (tm:char->int (read-char port))) (+ nchars 1))) (padding-ok (read-char port) ; consume padding (accum-int port accum (+ nchars 1))) (else ; padding where it shouldn't be (tm:time-error 'string->date 'bad-date-template-string "Non-numeric characters in integer read."))))) (accum-int port 0 0))) (define (tm:make-integer-exact-reader n) (lambda (port) (tm:integer-reader-exact n port))) (define (tm:zone-reader port) (let ( (offset 0) (positive? #f) ) (let ( (ch (read-char port)) ) (if (eof-object? ch) (tm:time-error 'string->date 'bad-date-template-string (list "Invalid time zone +/-" ch))) (if (or (char=? ch #\Z) (char=? ch #\z)) 0 (begin (cond ((char=? ch #\+) (set! positive? #t)) ((char=? ch #\-) (set! positive? #f)) (else (tm:time-error 'string->date 'bad-date-template-string (list "Invalid time zone +/-" ch)))) (let ((ch (read-char port))) (if (eof-object? ch) (tm:time-error 'string->date 'bad-date-template-string (list "Invalid time zone number" ch))) (set! offset (* (tm:char->int ch) 10 60 60))) (let ((ch (read-char port))) (if (eof-object? ch) (tm:time-error 'string->date 'bad-date-template-string (list "Invalid time zone number" ch))) (set! offset (+ offset (* (tm:char->int ch) 60 60)))) (let ((ch (read-char port))) (if (eof-object? ch) (tm:time-error 'string->date 'bad-date-template-string (list "Invalid time zone number" ch))) (set! offset (+ offset (* (tm:char->int ch) 10 60)))) (let ((ch (read-char port))) (if (eof-object? ch) (tm:time-error 'string->date 'bad-date-template-string (list "Invalid time zone number" ch))) (set! offset (+ offset (* (tm:char->int ch) 60)))) (if positive? offset (- offset))))))) ;; looking at a char, read the char string, run thru indexer, return index (define (tm:locale-reader port indexer) (let ( (string-port (open-output-string)) ) (define (read-char-string) (let ((ch (peek-char port))) (if (char-alphabetic? ch) (begin (write-char (read-char port) string-port) (read-char-string)) (get-output-string string-port)))) (let* ( (str (read-char-string)) (index (indexer str)) ) (if index index (tm:time-error 'string->date 'bad-date-template-string (list "Invalid string for " indexer)))))) (define (tm:make-locale-reader indexer) (lambda (port) (tm:locale-reader port indexer))) (define (tm:make-char-id-reader char) (lambda (port) (if (char=? char (read-char port)) char (tm:time-error 'string->date 'bad-date-template-string "Invalid character match.")))) ;; A List of formatted read directives. ;; Each entry is a list. ;; 1. the character directive; ;; a procedure, which takes a character as input & returns ;; 2. #t as soon as a character on the input port is acceptable ;; for input, ;; 3. a port reader procedure that knows how to read the current port ;; for a value. Its one parameter is the port. ;; 4. a action procedure, that takes the value (from 3.) and some ;; object (here, always the date) and (probably) side-effects it. ;; In some cases (e.g., ~A) the action is to do nothing (define tm:read-directives (let ( (ireader4 (tm:make-integer-reader 4)) (ireader2 (tm:make-integer-reader 2)) (fireader9 (tm:make-fractional-integer-reader 9)) (ireaderf (tm:make-integer-reader #f)) (eireader2 (tm:make-integer-exact-reader 2)) (eireader4 (tm:make-integer-exact-reader 4)) (locale-reader-abbr-weekday (tm:make-locale-reader tm:locale-abbr-weekday->index)) (locale-reader-long-weekday (tm:make-locale-reader tm:locale-long-weekday->index)) (locale-reader-abbr-month (tm:make-locale-reader tm:locale-abbr-month->index)) (locale-reader-long-month (tm:make-locale-reader tm:locale-long-month->index)) (char-fail (lambda (ch) #t)) (do-nothing (lambda (val object) (values))) ) (list (list #\~ char-fail (tm:make-char-id-reader #\~) do-nothing) (list #\a char-alphabetic? locale-reader-abbr-weekday do-nothing) (list #\A char-alphabetic? locale-reader-long-weekday do-nothing) (list #\b char-alphabetic? locale-reader-abbr-month (lambda (val object) (tm:set-date-month! object val))) (list #\B char-alphabetic? locale-reader-long-month (lambda (val object) (tm:set-date-month! object val))) (list #\d char-numeric? ireader2 (lambda (val object) (tm:set-date-day! object val))) (list #\e char-fail eireader2 (lambda (val object) (tm:set-date-day! object val))) (list #\h char-alphabetic? locale-reader-abbr-month (lambda (val object) (tm:set-date-month! object val))) (list #\H char-numeric? ireader2 (lambda (val object) (tm:set-date-hour! object val))) (list #\k char-fail eireader2 (lambda (val object) (tm:set-date-hour! object val))) (list #\m char-numeric? ireader2 (lambda (val object) (tm:set-date-month! object val))) (list #\M char-numeric? ireader2 (lambda (val object) (tm:set-date-minute! object val))) (list #\N char-numeric? fireader9 (lambda (val object) (tm:set-date-nanosecond! object val))) (list #\S char-numeric? ireader2 (lambda (val object) (tm:set-date-second! object val))) (list #\y char-fail eireader2 (lambda (val object) (tm:set-date-year! object (tm:natural-year val)))) (list #\Y char-numeric? ireader4 (lambda (val object) (tm:set-date-year! object val))) (list #\z (lambda (c) (or (char=? c #\Z) (char=? c #\z) (char=? c #\+) (char=? c #\-))) tm:zone-reader (lambda (val object) (tm:set-date-zone-offset! object val))) ))) (define (tm:string->date date index format-string str-len port template-string) (define (skip-until port skipper) (let ((ch (peek-char port))) (if (eof-object? ch) (tm:time-error 'string->date 'bad-date-format-string template-string) (if (not (skipper ch)) (begin (read-char port) (skip-until port skipper)))))) (if (>= index str-len) (begin (values)) (let ( (current-char (string-ref format-string index)) ) (if (not (char=? current-char #\~)) (let ((port-char (read-char port))) (if (or (eof-object? port-char) (not (char=? current-char port-char))) (tm:time-error 'string->date 'bad-date-format-string template-string)) (tm:string->date date (+ index 1) format-string str-len port template-string)) ;; otherwise, it's an escape, we hope (if (> (+ index 1) str-len) (tm:time-error 'string->date 'bad-date-format-string template-string) (let* ( (format-char (string-ref format-string (+ index 1))) (format-info (assoc format-char tm:read-directives)) ) (if (not format-info) (tm:time-error 'string->date 'bad-date-format-string template-string) (begin (let ((skipper (cadr format-info)) (reader (caddr format-info)) (actor (cadddr format-info))) (skip-until port skipper) (let ((val (reader port))) (if (eof-object? val) (tm:time-error 'string->date 'bad-date-format-string template-string) (actor val date))) (tm:string->date date (+ index 2) format-string str-len port template-string)))))))))) (define (string->date input-string template-string) (define (tm:date-ok? date) (and (date-nanosecond date) (date-second date) (date-minute date) (date-hour date) (date-day date) (date-month date) (date-year date) (date-zone-offset date))) (let ( (newdate (make-date 0 0 0 0 #f #f #f (tm:local-tz-offset))) ) (tm:string->date newdate 0 template-string (string-length template-string) (open-input-string input-string) template-string) (if (tm:date-ok? newdate) newdate (tm:time-error 'string->date 'bad-date-format-string (list "Incomplete date read. " newdate template-string))))) |
Added srfi/s19/time.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s19 time) (export time make-time time? time-type time-nanosecond time-second date make-date date? date-nanosecond date-second date-minute date-hour date-day date-month date-year date-zone-offset time-tai time-utc time-monotonic #|time-thread time-process|# time-duration read-leap-second-table copy-time current-time time-resolution time=? time>? time<? time>=? time<=? time-difference time-difference! add-duration add-duration! subtract-duration subtract-duration! time-tai->time-utc time-tai->time-utc! time-utc->time-tai time-utc->time-tai! time-monotonic->time-utc time-monotonic->time-utc! time-monotonic->time-tai time-monotonic->time-tai! time-utc->time-monotonic time-utc->time-monotonic! time-tai->time-monotonic time-tai->time-monotonic! time-tai->date time-utc->date time-monotonic->date date->time-utc date->time-tai date->time-monotonic leap-year? date-year-day date-week-day date-week-number current-date date->julian-day date->modified-julian-day time-utc->julian-day time-utc->modified-julian-day time-tai->julian-day time-tai->modified-julian-day time-monotonic->julian-day time-monotonic->modified-julian-day julian-day->time-utc julian-day->time-tai julian-day->time-monotonic julian-day->date modified-julian-day->date modified-julian-day->time-utc modified-julian-day->time-tai modified-julian-day->time-monotonic current-julian-day current-modified-julian-day date->string string->date) (import (rnrs) (rnrs r5rs) (rnrs mutable-strings) (srfi s19 time compat) (srfi s6 basic-string-ports) (srfi private include)) (define read-line (case-lambda [() (get-line (current-input-port))] [(port) (get-line port)])) (define eof (eof-object)) (include/resolve ("srfi" "s19") "srfi-19.scm") ) |
Added srfi/s19/time/compat.chezscheme.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
(library (srfi s19 time compat) (export format host:time-resolution host:current-time host:time-nanosecond host:time-second host:time-gmt-offset) (import (chezscheme) (prefix (only (chezscheme) current-time time-nanosecond time-second) host:)) (define host:time-resolution 1000) ;; (define (host:time-gmt-offset t) ;; (date-zone-offset t)) (define (host:time-gmt-offset t) (date-zone-offset (time-utc->date t))) ) |
Added srfi/s19/time/compat.ikarus.sls.
> > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi s19 time compat) (export format host:time-resolution host:current-time host:time-nanosecond host:time-second host:time-gmt-offset) (import (rnrs base) (only (ikarus) format) (prefix (only (ikarus) current-time time-nanosecond time-second time-gmt-offset) host:)) ;; Ikarus uses gettimeofday() which gives microseconds, ;; so our resolution is 1000 nanoseconds (define host:time-resolution 1000) ) |
Added srfi/s19/time/compat.larceny.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi s19 time compat) (export format host:time-resolution host:current-time host:time-nanosecond host:time-second host:time-gmt-offset) (import (r5rs) (rnrs) (larceny load) (primitives r5rs:require current-utc-time timezone-offset) (srfi s48 intermediate-format-strings)) (define-record-type time (fields secs usecs)) ;; Larceny uses gettimeofday() which gives microseconds, ;; so our resolution is 1000 nanoseconds (define host:time-resolution 1000) (define (host:current-time) (let-values ([(secs usecs) (current-utc-time)]) (make-time secs usecs))) (define (host:time-nanosecond t) (* (time-usecs t) 1000)) (define (host:time-second t) (time-secs t)) (define (host:time-gmt-offset t) (timezone-offset (time-secs t))) (r5rs:require 'time) ) |
Added srfi/s19/time/compat.mosh.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
;; Copyright (c) Higepon(Taro Minowa) ;; ;; Permission is hereby granted, free of charge, to any person obtaining a ;; copy of this software and associated documentation files (the "Software"), ;; to deal in the Software without restriction, including without limitation ;; the rights to use, copy, modify, merge, publish, distribute, sublicense, ;; and/or sell copies of the Software, and to permit persons to whom the ;; Software is furnished to do so, subject to the following conditions: ;; ;; The above copyright notice and this permission notice shall be included in ;; all copies or substantial portions of the Software. ;; ;; Except as contained in this notice, the name(s) of the above copyright ;; holders shall not be used in advertising or otherwise to promote the sale, ;; use or other dealings in this Software without prior written authorization. ;; ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ;; DEALINGS IN THE SOFTWARE. (library (srfi s19 time compat) (export format host:time-resolution host:current-time host:time-nanosecond host:time-second host:time-gmt-offset) (import (rnrs base) (only (mosh) format) (only (system) microseconds local-tz-offset)) (define host:time-resolution 1000) (define (host:current-time) (microseconds)) (define (host:time-nanosecond t) (* (mod t 1000000) 1000)) (define (host:time-second t) (div t 1000000)) (define (host:time-gmt-offset t) (local-tz-offset)) ) |
Added srfi/s19/time/compat.mzscheme.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s19 time compat) (export format host:time-resolution host:current-time host:time-nanosecond host:time-second host:time-gmt-offset) (import (rnrs base) (only (scheme base) format current-inexact-milliseconds date-time-zone-offset seconds->date current-seconds)) ;; MzScheme uses milliseconds, so our resolution in nanoseconds is #e1e6 (define host:time-resolution #e1e6) (define (host:current-time) (exact (floor (current-inexact-milliseconds)))) (define (host:time-nanosecond t) (* (mod t 1000) #e1e6)) (define (host:time-second t) (div t 1000)) (define (host:time-gmt-offset t) (date-time-zone-offset (seconds->date (host:time-second t)))) ) |
Added srfi/s19/time/compat.ypsilon.sls.
> > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi s19 time compat) (export format host:time-resolution host:current-time host:time-nanosecond host:time-second host:time-gmt-offset) (import (rnrs base) (only (core) format microsecond microsecond->utc)) (define host:time-resolution 1000) (define (host:current-time) (microsecond)) (define (host:time-nanosecond t) (* (mod t 1000000) 1000)) (define (host:time-second t) (div t 1000000)) (define (host:time-gmt-offset t) (/ (- t (microsecond->utc t)) 1000000)) ) |
Added srfi/s2/and-let.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s2 and-let) (export and-let*) (import (rnrs)) (define-syntax and-let* (lambda (stx) (define (get-id c) (syntax-case c () [(var expr) #'var] [_ #f])) (syntax-case stx () [(_ (clause* ...) body* ...) (for-all identifier? (filter values (map get-id #'(clause* ...)))) #'(and-let*-core #t (clause* ...) body* ...)]))) (define-syntax and-let*-core (lambda (stx) (syntax-case stx () [(kw _ ([var expr] clause* ...) body* ...) #'(let ([var expr]) (if var (kw var (clause* ...) body* ...) #f))] [(kw _ ([expr] clause* ...) body* ...) #'(let ([t expr]) (if t (kw t (clause* ...) body* ...) #f))] [(kw _ (id clause* ...) body* ...) (or (identifier? #'id) (syntax-violation #f "invalid clause" stx #'id)) #'(if id (kw id (clause* ...) body* ...) #f)] [(kw last () body* ...) (if (positive? (length #'(body* ...))) #'(begin body* ...) #'last)]))) ) |
Added srfi/s23/error.sls.
> > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#!r6rs ;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi s23 error) (export error) (import (rename (rnrs base) (error rnrs:error))) (define (error . args) (apply rnrs:error #F args)) ) |
Added srfi/s23/error/tricks.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#!r6rs ;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi s23 error tricks) (export SRFI-23-error->R6RS) (import (rnrs)) (define-syntax error-wrap (lambda (stx) (syntax-case stx () ((_ ctxt signal expr ...) (with-syntax ((e (datum->syntax #'ctxt 'error))) #'(let-syntax ((e (identifier-syntax signal))) expr ...)))))) (define (AV who) (lambda args (apply assertion-violation who args))) (define-syntax SRFI-23-error->R6RS (lambda (stx) (syntax-case stx () ((ctxt ewho expr ...) (with-syntax ((e (datum->syntax #'ctxt 'error)) (d (datum->syntax #'ctxt 'define))) #'(let-syntax ((e (identifier-syntax (AV 'ewho))) (d (lambda (stx) (syntax-case stx () ((kw (id . formals) . body) (identifier? #'id) #'(error-wrap kw (AV 'id) (d (id . formals) . body))) ((kw id . r) (identifier? #'id) #'(error-wrap kw (AV 'id) (d id . r))))))) expr ...)))))) ) |
Added srfi/s25/arlib.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 |
;;; array arlib ;;; 2001 Jussi Piitulainen ;;; This is a high level implementation of some generally useful ;;; array procedures. In addition to R5RS and SRFI-25, only one ;;; tool is used, namely array:apply-to-vector and friends. Thus ;;; this library serves to prove that the primitives really are ;;; primitives. - A lower level implementation would access some ;;; implementation details to bypass redundant checking and such. ;;; Note that these procedures are not necessarily designed with ;;; full care. Think of them as examples of what can be done. ;;; Important tools are also missing, including scans and reduces ;;; and many thinks that I have not even heard of yet. ;;; (array-shape arr) (array-length arr dim) (array-size arr) ;;; (array-equal? arr1 arr2) ;;; (shape-for-each shp proc [ind]) ;;; (array-for-each-index arr proc [ind]) ;;; (tabulate-array shp proc) (tabulate-array! shp proc ind) ;;; (array-retabulate! arr shp proc [ind]) ;;; (array-map [shp] proc arr0 arr1 ...) ;;; (array-map! arr [shp] proc arr0 arr1 ...) ;;; (array->vector arr) (array->list arr) ;;; (share-array/prefix arr k ...) (share-row arr k) (share-column arr k) ;;; (share-array/origin arr k ...) (share-array/origin arr ind) ;;; (array-append dim arr0 arr1 ...) ;;; (transpose arr dim ...) ;;; (share-nths arr dim n) ;;; Naming problem: should all those index-object using procedures ;;; bang? The main argument, like shape, is not mutated. ;;; (array-shape arr) (define (array-shape arr) (let ((r (array-rank arr))) (let ((m (make-array (shape 0 r 0 2)))) (do ((d 0 (+ d 1))) ((= d r) m) (array-set! m d 0 (array-start arr d)) (array-set! m d 1 (array-end arr d)))))) ;;; (array-length arr dim) (define (array-length arr dim) (- (array-end arr dim) (array-start arr dim))) ;;; (array-size arr) (define (array-size arr) (let ((r (array-rank arr))) (do ((k 0 (+ k 1)) (p 1 (* p (array-length arr k)))) ((= k r) p)))) ;;; (array-equal? a b) ;;; compares elements with equal? so elements better not contain ;;; arrays. (define (array-equal? a b) (let ((r (array-rank a))) (and (= r (array-rank b)) (and (do ((k 0 (+ k 1)) (true #t (and true (= (array-start a k) (array-start b k)) (= (array-end a k) (array-end b k))))) ((= k r) true)) (let ((ks (make-vector r 0))) (let wok ((d 0)) (if (< d r) (let ((e (array-end a d))) (do ((k (array-start a d) (+ k 1)) (true #t (and true (wok (+ d 1))))) ((= k e) true) (vector-set! ks d k))) (equal? (array-ref a ks) (array-ref b ks))))))))) ;;; (shape-for-each shp proc [index-object]) ;;; passes each index in shape to proc in row-major orderd, using ;;; index-object if provided. (define (shape-for-each shp proc . o) (if (null? o) (array:arlib:shape-for-each/arguments shp proc) (if (vector? (car o)) (array:arlib:shape-for-each/vector shp proc (car o)) (array:arlib:shape-for-each/array shp proc (car o))))) (define (array:arlib:shape-for-each/arguments shp proc) (let ((r (array-end shp 0))) (let ((vec (make-vector r))) (let do-dim ((d 0)) (if (= d r) (array:apply-to-vector r proc vec) (let ((e (array-ref shp d 1))) (do ((k (array-ref shp d 0) (+ k 1))) ((= k e)) (vector-set! vec d k) (do-dim (+ d 1))))))))) (define (array:arlib:shape-for-each/vector shp proc vec) (let ((r (array-end shp 0))) (let do-dim ((d 0)) (if (= d r) (proc vec) (let ((e (array-ref shp d 1))) (do ((k (array-ref shp d 0) (+ k 1))) ((= k e)) (vector-set! vec d k) (do-dim (+ d 1)))))))) (define (array:arlib:shape-for-each/array shp proc arr) ;; arr is not vector (let ((r (array-end shp 0))) (let do-dim ((d 0)) (if (= d r) (proc arr) (let ((e (array-ref shp d 1))) (do ((k (array-ref shp d 0) (+ k 1))) ((= k e)) (array-set! arr d k) (do-dim (+ d 1)))))))) ;;; (array-for-each-index arr proc [ind]) ;;; is equivalent to ;;; ;;; (shape-for-each-index (array-shape arr) proc [ind]) ;;; ;;; but is implemented without allocation of the shape, to prove ;;; that it can be so implemented. (define (array-for-each-index arr proc . o) (if (null? o) (array:arlib:array-for-each-index/arguments arr proc) (if (vector? (car o)) (array:arlib:array-for-each-index/vector arr proc (car o)) (array:arlib:array-for-each-index/array arr proc (car o))))) (define (array:arlib:array-for-each-index/arguments arr proc) (let ((r (array-rank arr))) (let ((vec (make-vector r)) (apply (array:applier-to-vector r))) (let do-dim ((d 0)) (if (= d r) (apply proc vec) (let ((e (array-end arr d))) (do ((k (array-start arr d) (+ k 1))) ((= k e)) (vector-set! vec d k) (do-dim (+ d 1))))))))) (define (array:arlib:array-for-each-index/vector arr proc ind) ;; ind is a vector (let ((r (array-rank arr))) (let do-dim ((d 0)) (if (= d r) (proc ind) (let ((e (array-end arr d))) (do ((k (array-start arr d) (+ k 1))) ((= k e)) (vector-set! ind d k) (do-dim (+ d 1)))))))) (define (array:arlib:array-for-each-index/array arr proc ind) ;; ind is an array but not a vector (let ((r (array-rank arr))) (let do-dim ((d 0)) (if (= d r) (proc ind) (let ((e (array-end arr d))) (do ((k (array-start arr d) (+ k 1))) ((= k e)) (array-set! ind d k) (do-dim (+ d 1)))))))) ;;; (tabulate-array shp proc) ;;; (tabulate-array! shp proc ind) ;;; returns a newly allocated array of the given shape with initial ;;; contents at each index whatever proc returns given the indices. ;;; The latter procedure reuses ind for package of indices. (define (tabulate-array shp proc) (let ((arr (make-array shp))) (array:arlib:shape-for-each/vector shp (let ((apply (array:applier-to-vector (array-end shp 0)))) (lambda (ix) (array-set! arr ix (apply proc ix)))) (make-vector (array-end shp 0))) arr)) (define (tabulate-array! shp proc ind) (let ((arr (make-array shp))) (if (vector? ind) (array:arlib:shape-for-each/vector shp (lambda (ix) (array-set! arr ix (proc ix))) ind) (array:arlib:shape-for-each/array shp (lambda (ix) (array-set! arr ix (proc ix))) ind)) arr)) ;;; (array-retabulate! arr shp proc [index-object]) ;;; sets the elements of arr in shape to the value of proc at that ;;; index, using index-object if provided. (define (array-retabulate! arr shp proc . o) (if (null? o) (array:arlib:shape-for-each/vector shp (let ((apply (array:applier-to-vector (array-end shp 0)))) (lambda (ix) (array-set! arr ix (apply proc ix)))) (make-vector (array-end shp 0))) (if (vector? (car o)) (array:arlib:shape-for-each/vector shp (lambda (ix) (array-set! arr ix (proc ix))) (car o)) (array:arlib:shape-for-each/array shp (lambda (ix) (array-set! arr ix (proc ix))) (car o))))) ;;; (array-map! array [shape] proc array0 array1...) ;;; stores to the elements of array (in shape) the values of proc at ;;; the contents of arrayk at corresponding index. (define (array-map! arr x y . o) (if (array:array? x) (array:arlib:map! arr x y (apply vector o)) (array:arlib:map! arr (array-shape arr) x (apply vector y o)))) (define (array:arlib:map! arr shp proc args) (let ((rank (vector-length args))) (let ((argv (make-vector rank))) (array:arlib:shape-for-each/vector shp (let ((apply (array:applier-to-vector rank))) (lambda (ix) (do ((k 0 (+ k 1))) ((= k rank)) (vector-set! argv k (array-ref (vector-ref args k) ix))) (array-set! arr ix (apply proc argv)))) (make-vector (array-end shp 0)))))) ;;; (array-map [shape] proc array0 array1 ...) ;;; creates a new array with elements initialized to the values of ;;; proc at contents of arrayk (in shape). (define (array-map x y . o) (if (array:array? x) (let ((arr (make-array x))) (array:arlib:map! arr x y (apply vector o)) arr) (let ((shp (array-shape y))) (let ((arr (make-array shp))) (array:arlib:map! arr shp x (apply vector y o)) arr)))) ;;; SRFI-25 mailing list requested array->vector; they also requested the ;;; ability to use an array as an index of an element, and array->list is ;;; an attempt to provide for that. (define (array->vector arr) (let ((vec (make-vector (array-size arr)))) (let ((k 0)) (shape-for-each (array-shape arr) (lambda (index) (vector-set! vec k (array-ref arr index)) (set! k (+ k 1))) (make-vector (array-rank arr))) vec))) ;;; It needs to be said that more efficient implementations are ;;; possible, even within SRFI-25. (define (array->list arr) (vector->list (array->vector arr))) ;;; (share-row arr k) ;;; shares whatever the first index is about. ;;; The result has one dimension less. (define (share-row arr k) (share-array arr (let ((bounds (array->list (array-shape arr)))) (apply shape (cddr bounds))) (lambda ks (apply values k ks)))) ;;; (share-array/prefix arr k ...) (define (share-array/prefix arr . js) (if (or (null? js) (integer? (car js))) (share-array arr (let ((bounds (array->list (array-shape arr)))) (apply shape (list-tail bounds (* 2 (length js))))) (lambda ks (apply values (append js ks)))) (apply (lambda (fix) (share-array/prefix! arr fix (make-vector (- (array-rank arr) (if (vector? fix) (vector-length fix) (array-end fix 0)))))) js))) (define (share-array/prefix! arr fix in . out) (let* ((out (if (pair? out) ((lambda (out) out) out) (make-vector (array-rank arr)))) (fix-ref (if (vector? fix) vector-ref array-ref)) (in-ref (if (vector? in) vector-ref array-ref)) (out-set! (if (vector? out) vector-set! array-set!)) (m (if (vector? fix) (vector-length fix) (array-end fix 0))) (n (if (vector? out) (vector-length out) (array-end out 0)))) (do ((k 0 (+ k 1))) ((= k m)) (out-set! out k (fix-ref fix k))) (share-array/index! arr (let ((bounds (array->list (array-shape arr)))) (apply shape (list-tail bounds (if (vector? fix) (* 2 (vector-length fix)) (* 2 (array-end fix 0)))))) (lambda (in) (do ((k m (+ k 1))) ((= k n)) (out-set! out k (in-ref in (- k m)))) out) in))) ;;; (share-column arr k) ;;; shares whatever the second index is about. ;;; The result has one dimension less. (define (share-column arr k) (share-array arr (let ((bounds (array->list (array-shape arr)))) (apply shape (car bounds) (cadr bounds) (cddddr bounds))) (lambda ks (apply values (car ks) k (cdr ks))))) ;;; (share-array/origin arr k ...) ;;; (share-array/origin arr index) ;;; change origin to k ..., with index a vector or zero-based ;;; one-dimensional array that contains k ... ;;; ;;; This is useful for writing array-append. Maybe for something ;;; else too - who knows. (define (share-array/origin arr . xs) (let ((new (if (or (null? xs) (integer? (car xs))) xs (apply (lambda (x) (if (vector? x) (vector->list x) (if (array? x) (array->list x) (error "share-array/origin: bad thing")))) xs)))) (do ((k (array-rank arr) (- k 1)) (old '() (cons (array-start arr (- k 1)) old))) ((= k 0) (let ((ds (map - new old))) (share-array arr (tabulate-array (shape 0 (array-rank arr) 0 2) (lambda (r k) (case k ((0) (+ (array-start arr r) (list-ref ds r))) ((1) (+ (array-end arr r) (list-ref ds r)))))) (lambda ks (apply values (map - ks ds))))))))) ;;; SRFI-25 mailing list requested making shapes their own type. Here's ;;; an example of how manipulating shapes as arrays can be useful. The ;;; example also tests that higher level libraries are indeed easy to ;;; write on top of this SRFI. ;;; (array-append arr1 arr2 dim) ;;; appends two arrays along a specified dimension. The arrays must ;;; have equally many dimensions and all other dimensions equally long. ;;; ;;; Generalize to more arrays and maybe rewrite with shape-for-each or ;;; what have you. (define (array-append dim arr . ars) (let* ((total (do ((m (array-length arr dim) (+ m (array-length (car r) dim))) (r ars (cdr r))) ((null? r) m))) (common (array-shape arr)) (origin (array->vector (share-column common 0))) (index (make-vector (array-rank arr)))) (array-set! common dim 1 (+ (array-start arr dim) total)) (let ((result (make-array common))) (array-set! common dim 1 (array-start arr dim)) (let wok ((arr arr) (ars ars)) (vector-set! origin dim (array-ref common dim 1)) (let ((arr1 (share-array/origin arr origin))) (array-set! common dim 0 (array-start arr1 dim)) (array-set! common dim 1 (array-end arr1 dim)) (shape-for-each common (lambda (index) (array-set! result index (array-ref arr1 index))) index)) (if (pair? ars) (wok (car ars) (cdr ars)))) result))) ;;; Transpose, as permutation of dimensions, is applicable to all ;;; arrays. The default is reversal. ;;; The implementation uses multiplication by permutation ;;; matrix but matrix multiplication is not exported. (define (array:arlib:matrix-times a b) (or (and (= (array-rank a) 2) (= (array-rank b) 2)) (error "times: arrays are not matrices")) (let ((r0 (array-start a 0)) (rn (array-end a 0)) (t0 (array-start a 1)) (tn (array-end a 1)) (u0 (array-start b 0)) (un (array-end b 0)) (k0 (array-start b 1)) (kn (array-end b 1))) (or (= (- tn t0) (- un u0)) (error "times: matrices are not compatible")) (let ((ab (make-array (shape r0 rn k0 kn)))) (do ((r r0 (+ r 1))) ((= r rn)) (do ((k k0 (+ k 1))) ((= k kn)) (do ((t t0 (+ t 1)) (u u0 (+ u 1)) (s 0 (+ s (* (array-ref a r t) (array-ref b u k))))) ((and (= t tn) (= u un)) (array-set! ab r k s))))) ab))) ; This is a generalized transpose. It can permute the dimensions any which ; way. The permutation is provided by a permutation matrix: a square matrix ; of zeros and ones, with exactly one one in each row and column, or a ; permutation of the rows of an identity matrix; the size of the matrix ; must match the number of dimensions of the array. ; ; The default permutation is [ 0 1 | 1 0 ] of course, but any permutation ; array can be specified, and the shape array of the original array is then ; multiplied with it, and index column vectors of the new array with its ; inverse, from left, to permute the rows appropriately. (define (array:arlib:permutation-matrix . ds) (let* ((n (length ds)) (arr (make-array (shape 0 n 0 n) 0))) (do ((k 0 (+ k 1)) (ds ds (cdr ds))) ((= k n)) (array-set! arr k (car ds) 1)) arr)) ;;; (transpose arr k ...) ;;; shares arr with permuted dimensions. Each dimension from 0 ;;; inclusive to rank exclusive must appear once in k ... (define (transpose a . p0) (let* ((r (array-rank a)) (permutation (apply array:arlib:permutation-matrix (if (pair? p0) p0 (do ((ds '() (cons d ds)) (d 0 (+ d 1))) ((= d r) ;; reverse dimensions ds))))) (inverse-permutation (share-array permutation (array-shape permutation) (lambda (r k) ;; transpose (values k r))))) (share-array a (array:arlib:matrix-times permutation (array-shape a)) (lambda ks0 (apply values (array->list (array:arlib:matrix-times inverse-permutation (apply array (shape 0 r 0 1) ks0)))))))) ;;; (share-array/index! array subshape proc index) (define (share-array/index! array subshape proc index) (array:share/index! array subshape proc index)) ;;; Take every nth slice along dimension d into a shared array. This ;;; preserves the origin. (define (share-nths arr d n) (let* ((bounds (array->vector (array-shape arr))) (b (vector-ref bounds (* 2 d))) (e (vector-ref bounds (+ (* 2 d) 1)))) (vector-set! bounds (+ (* 2 d) 1) (+ b (quotient (+ n (- e b 1)) n))) (share-array arr (apply shape (vector->list bounds)) (lambda ks (apply values (let d/nk ((u 0) (ks ks)) (if (= u d) (cons (+ b (* n (- (car ks) b))) (cdr ks)) (cons (car ks) (d/nk (+ u 1) (cdr ks)))))))))) |
Added srfi/s25/array.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 |
;;; array ;;; 1997 - 2001 Jussi Piitulainen ;;; --- Intro --- ;;; This interface to arrays is based on Alan Bawden's array.scm of ;;; 1993 (earlier version in the Internet Repository and another ;;; version in SLIB). This is a complete rewrite, to be consistent ;;; with the rest of Scheme and to make arrays independent of lists. ;;; Some modifications are due to discussion in srfi-25 mailing list. ;;; (array? obj) ;;; (make-array shape [obj]) changed arguments ;;; (shape bound ...) new ;;; (array shape obj ...) new ;;; (array-rank array) changed name back ;;; (array-start array dimension) new ;;; (array-end array dimension) new ;;; (array-ref array k ...) ;;; (array-ref array index) new variant ;;; (array-set! array k ... obj) changed argument order ;;; (array-set! array index obj) new variant ;;; (share-array array shape proc) changed arguments ;;; All other variables in this file have names in "array:". ;;; Should there be a way to make arrays with initial values mapped ;;; from indices? Sure. The current "initial object" is lame. ;;; ;;; Removed (array-shape array) from here. There is a new version ;;; in arlib though. ;;; --- Representation type dependencies --- ;;; The mapping from array indices to the index to the underlying vector ;;; is whatever array:optimize returns. The file "opt" provides three ;;; representations: ;;; ;;; mbda) mapping is a procedure that allows an optional argument ;;; tter) mapping is two procedures that takes exactly the indices ;;; ctor) mapping is a vector of a constant term and coefficients ;;; ;;; Choose one in "opt" to make the optimizer. Then choose the matching ;;; implementation of array-ref and array-set!. ;;; ;;; These should be made macros to inline them. Or have a good compiler ;;; and plant the package as a module. ;;; 1. Pick an optimizer. ;;; 2. Pick matching index representation. ;;; 3. Pick a record implementation; as-procedure is generic; syntax inlines. ;;; 3. This file is otherwise portable. ;;; --- Portable R5RS (R4RS and multiple values) --- ;;; (array? obj) ;;; returns #t if `obj' is an array and #t or #f otherwise. (define (array? obj) (array:array? obj)) ;;; (make-array shape) ;;; (make-array shape obj) ;;; makes array of `shape' with each cell containing `obj' initially. (define (make-array shape . rest) (or (array:good-shape? shape) (error "make-array: shape is not a shape")) (apply array:make-array shape rest)) (define (array:make-array shape . rest) (let ((size (array:size shape))) (array:make (if (pair? rest) (apply (lambda (o) (make-vector size o)) rest) (make-vector size)) (if (= size 0) (array:optimize-empty (vector-ref (array:shape shape) 1)) (array:optimize (array:make-index shape) (vector-ref (array:shape shape) 1))) (array:shape->vector shape)))) ;;; (shape bound ...) ;;; makes a shape. Bounds must be an even number of exact, pairwise ;;; non-decreasing integers. Note that any such array can be a shape. (define (shape . bounds) (let ((v (list->vector bounds))) (or (even? (vector-length v)) (error (string-append "shape: uneven number of bounds: " (array:list->string bounds)))) (let ((shp (array:make v (if (pair? bounds) (array:shape-index) (array:empty-shape-index)) (vector 0 (quotient (vector-length v) 2) 0 2)))) (or (array:good-shape? shp) (error (string-append "shape: bounds are not pairwise " "non-decreasing exact integers: " (array:list->string bounds)))) shp))) ;;; (array shape obj ...) ;;; is analogous to `vector'. (define (array shape . elts) (or (array:good-shape? shape) (error (string-append "array: shape " (array:thing->string shape) " is not a shape"))) (let ((size (array:size shape))) (let ((vector (list->vector elts))) (or (= (vector-length vector) size) (error (string-append "array: an array of shape " (array:shape-vector->string (array:vector shape)) " has " (number->string size) " elements but got " (number->string (vector-length vector)) " values: " (array:list->string elts)))) (array:make vector (if (= size 0) (array:optimize-empty (vector-ref (array:shape shape) 1)) (array:optimize (array:make-index shape) (vector-ref (array:shape shape) 1))) (array:shape->vector shape))))) ;;; (array-rank array) ;;; returns the number of dimensions of `array'. (define (array-rank array) (quotient (vector-length (array:shape array)) 2)) ;;; (array-start array k) ;;; returns the lower bound index of array along dimension k. This is ;;; the least valid index along that dimension if the dimension is not ;;; empty. (define (array-start array d) (vector-ref (array:shape array) (+ d d))) ;;; (array-end array k) ;;; returns the upper bound index of array along dimension k. This is ;;; not a valid index. If the dimension is empty, this is the same as ;;; the lower bound along it. (define (array-end array d) (vector-ref (array:shape array) (+ d d 1))) ;;; (share-array array shape proc) ;;; makes an array that shares elements of `array' at shape `shape'. ;;; The arguments to `proc' are indices of the result. The values of ;;; `proc' are indices of `array'. ;;; Todo: in the error message, should recognise the mapping and show it. (define (share-array array subshape f) (or (array:good-shape? subshape) (error (string-append "share-array: shape " (array:thing->string subshape) " is not a shape"))) (let ((subsize (array:size subshape))) (or (array:good-share? subshape subsize f (array:shape array)) (error (string-append "share-array: subshape " (array:shape-vector->string (array:vector subshape)) " does not map into supershape " (array:shape-vector->string (array:shape array)) " under mapping " (array:map->string f (vector-ref (array:shape subshape) 1))))) (let ((g (array:index array))) (array:make (array:vector array) (if (= subsize 0) (array:optimize-empty (vector-ref (array:shape subshape) 1)) (array:optimize (lambda ks (call-with-values (lambda () (apply f ks)) (lambda ks (array:vector-index g ks)))) (vector-ref (array:shape subshape) 1))) (array:shape->vector subshape))))) ;;; --- Hrmph --- ;;; (array:share/index! ...) ;;; reuses a user supplied index object when recognising the ;;; mapping. The mind balks at the very nasty side effect that ;;; exposes the implementation. So this is not in the spec. ;;; But letting index objects in at all creates a pressure ;;; to go the whole hog. Arf. ;;; Use array:optimize-empty for an empty array to get a ;;; clearly invalid vector index. ;;; Surely it's perverse to use an actor for index here? But ;;; the possibility is provided for completeness. (define (array:share/index! array subshape proc index) (array:make (array:vector array) (if (= (array:size subshape) 0) (array:optimize-empty (quotient (vector-length (array:shape array)) 2)) ((if (vector? index) array:optimize/vector array:optimize/actor) (lambda (subindex) (let ((superindex (proc subindex))) (if (vector? superindex) (array:index/vector (quotient (vector-length (array:shape array)) 2) (array:index array) superindex) (array:index/array (quotient (vector-length (array:shape array)) 2) (array:index array) (array:vector superindex) (array:index superindex))))) index)) (array:shape->vector subshape))) (define (array:optimize/vector f v) (let ((r (vector-length v))) (do ((k 0 (+ k 1))) ((= k r)) (vector-set! v k 0)) (let ((n0 (f v)) (cs (make-vector (+ r 1))) (apply (array:applier-to-vector (+ r 1)))) (vector-set! cs 0 n0) (let wok ((k 0)) (if (< k r) (let ((k1 (+ k 1))) (vector-set! v k 1) (let ((nk (- (f v) n0))) (vector-set! v k 0) (vector-set! cs k1 nk) (wok k1))))) (apply (array:maker r) cs)))) (define (array:optimize/actor f a) (let ((r (array-end a 0)) (v (array:vector a)) (i (array:index a))) (do ((k 0 (+ k 1))) ((= k r)) (vector-set! v (array:actor-index i k) 0)) (let ((n0 (f a)) (cs (make-vector (+ r 1))) (apply (array:applier-to-vector (+ r 1)))) (vector-set! cs 0 n0) (let wok ((k 0)) (if (< k r) (let ((k1 (+ k 1)) (t (array:actor-index i k))) (vector-set! v t 1) (let ((nk (- (f a) n0))) (vector-set! v t 0) (vector-set! cs k1 nk) (wok k1))))) (apply (array:maker r) cs)))) ;;; --- Internals --- (define (array:shape->vector shape) (let ((idx (array:index shape)) (shv (array:vector shape)) (rnk (vector-ref (array:shape shape) 1))) (let ((vec (make-vector (* rnk 2)))) (do ((k 0 (+ k 1))) ((= k rnk) vec) (vector-set! vec (+ k k) (vector-ref shv (array:shape-vector-index idx k 0))) (vector-set! vec (+ k k 1) (vector-ref shv (array:shape-vector-index idx k 1))))))) ;;; (array:size shape) ;;; returns the number of elements in arrays of shape `shape'. (define (array:size shape) (let ((idx (array:index shape)) (shv (array:vector shape)) (rnk (vector-ref (array:shape shape) 1))) (do ((k 0 (+ k 1)) (s 1 (* s (- (vector-ref shv (array:shape-vector-index idx k 1)) (vector-ref shv (array:shape-vector-index idx k 0)))))) ((= k rnk) s)))) ;;; (array:make-index shape) ;;; returns an index function for arrays of shape `shape'. This is a ;;; runtime composition of several variable arity procedures, to be ;;; passed to array:optimize for recognition as an affine function of ;;; as many variables as there are dimensions in arrays of this shape. (define (array:make-index shape) (let ((idx (array:index shape)) (shv (array:vector shape)) (rnk (vector-ref (array:shape shape) 1))) (do ((f (lambda () 0) (lambda (k . ks) (+ (* s (- k (vector-ref shv (array:shape-vector-index idx (- j 1) 0)))) (apply f ks)))) (s 1 (* s (- (vector-ref shv (array:shape-vector-index idx (- j 1) 1)) (vector-ref shv (array:shape-vector-index idx (- j 1) 0))))) (j rnk (- j 1))) ((= j 0) f)))) ;;; --- Error checking --- ;;; (array:good-shape? shape) ;;; returns true if `shape' is an array of the right shape and its ;;; elements are exact integers that pairwise bound intervals `[lo..hi)´. (define (array:good-shape? shape) (and (array:array? shape) (let ((u (array:shape shape)) (v (array:vector shape)) (x (array:index shape))) (and (= (vector-length u) 4) (= (vector-ref u 0) 0) (= (vector-ref u 2) 0) (= (vector-ref u 3) 2)) (let ((p (vector-ref u 1))) (do ((k 0 (+ k 1)) (true #t (let ((lo (vector-ref v (array:shape-vector-index x k 0))) (hi (vector-ref v (array:shape-vector-index x k 1)))) (and true (integer? lo) (exact? lo) (integer? hi) (exact? hi) (<= lo hi))))) ((= k p) true)))))) ;;; (array:good-share? subv subsize mapping superv) ;;; returns true if the extreme indices in the subshape vector map ;;; into the bounds in the supershape vector. ;;; If some interval in `subv' is empty, then `subv' is empty and its ;;; image under `f' is empty and it is trivially alright. One must ;;; not call `f', though. (define (array:good-share? subshape subsize f super) (or (zero? subsize) (letrec ((sub (array:vector subshape)) (dex (array:index subshape)) (ck (lambda (k ks) (if (zero? k) (call-with-values (lambda () (apply f ks)) (lambda qs (array:good-indices? qs super))) (and (ck (- k 1) (cons (vector-ref sub (array:shape-vector-index dex (- k 1) 0)) ks)) (ck (- k 1) (cons (- (vector-ref sub (array:shape-vector-index dex (- k 1) 1)) 1) ks))))))) (let ((rnk (vector-ref (array:shape subshape) 1))) (or (array:unchecked-share-depth? rnk) (ck rnk '())))))) ;;; Check good-share on 10 dimensions at most. The trouble is, ;;; the cost of this check is exponential in the number of dimensions. (define (array:unchecked-share-depth? rank) (if (> rank 10) (begin (display `(warning: unchecked depth in share: ,rank subdimensions)) (newline) #t) #f)) ;;; (array:check-indices caller indices shape-vector) ;;; (array:check-indices.o caller indices shape-vector) ;;; (array:check-index-vector caller index-vector shape-vector) ;;; return if the index is in bounds, else signal error. ;;; ;;; Shape-vector is the internal representation, with ;;; b and e for dimension k at 2k and 2k + 1. (define (array:check-indices who ks shv) (or (array:good-indices? ks shv) (error (array:not-in who ks shv)))) (define (array:check-indices.o who ks shv) (or (array:good-indices.o? ks shv) (error (array:not-in who (reverse (cdr (reverse ks))) shv)))) (define (array:check-index-vector who ks shv) (or (array:good-index-vector? ks shv) (error (array:not-in who (vector->list ks) shv)))) (define (array:check-index-actor who ks shv) (let ((shape (array:shape ks))) (or (and (= (vector-length shape) 2) (= (vector-ref shape 0) 0)) (error "not an actor")) (or (array:good-index-actor? (vector-ref shape 1) (array:vector ks) (array:index ks) shv) (array:not-in who (do ((k (vector-ref shape 1) (- k 1)) (m '() (cons (vector-ref (array:vector ks) (array:actor-index (array:index ks) (- k 1))) m))) ((= k 0) m)) shv)))) (define (array:good-indices? ks shv) (let ((d2 (vector-length shv))) (do ((kp ks (if (pair? kp) (cdr kp))) (k 0 (+ k 2)) (true #t (and true (pair? kp) (array:good-index? (car kp) shv k)))) ((= k d2) (and true (null? kp)))))) (define (array:good-indices.o? ks.o shv) (let ((d2 (vector-length shv))) (do ((kp ks.o (if (pair? kp) (cdr kp))) (k 0 (+ k 2)) (true #t (and true (pair? kp) (array:good-index? (car kp) shv k)))) ((= k d2) (and true (pair? kp) (null? (cdr kp))))))) (define (array:good-index-vector? ks shv) (let ((r2 (vector-length shv))) (and (= (* 2 (vector-length ks)) r2) (do ((j 0 (+ j 1)) (k 0 (+ k 2)) (true #t (and true (array:good-index? (vector-ref ks j) shv k)))) ((= k r2) true))))) (define (array:good-index-actor? r v i shv) (and (= (* 2 r) (vector-length shv)) (do ((j 0 (+ j 1)) (k 0 (+ k 2)) (true #t (and true (array:good-index? (vector-ref v (array:actor-index i j)) shv k)))) ((= j r) true)))) ;;; (array:good-index? index shape-vector 2d) ;;; returns true if index is within bounds for dimension 2d/2. (define (array:good-index? w shv k) (and (integer? w) (exact? w) (<= (vector-ref shv k) w) (< w (vector-ref shv (+ k 1))))) (define (array:not-in who ks shv) (let ((index (array:list->string ks)) (bounds (array:shape-vector->string shv))) (error (string-append who ": index " index " not in bounds " bounds)))) (define (array:list->string ks) (do ((index "" (string-append index (array:thing->string (car ks)) " ")) (ks ks (cdr ks))) ((null? ks) index))) (define (array:shape-vector->string shv) (do ((bounds "" (string-append bounds "[" (number->string (vector-ref shv t)) ".." (number->string (vector-ref shv (+ t 1))) ")" " ")) (t 0 (+ t 2))) ((= t (vector-length shv)) bounds))) (define (array:thing->string thing) (cond ((number? thing) (number->string thing)) ((symbol? thing) (string-append "#<symbol>" (symbol->string thing))) ((char? thing) "#<char>") ((string? thing) "#<string>") ((list? thing) (string-append "#" (number->string (length thing)) "<list>")) ((pair? thing) "#<pair>") ((array? thing) "#<array>") ((vector? thing) (string-append "#" (number->string (vector-length thing)) "<vector>")) ((procedure? thing) "#<procedure>") (else (case thing ((()) "()") ((#t) "#t") ((#f) "#f") (else "#<whatsit>"))))) ;;; And to grok an affine map, vector->vector type. Column k of arr ;;; will contain coefficients n0 ... nm of 1 k1 ... km for kth value. ;;; ;;; These are for the error message when share fails. (define (array:index-ref ind k) (if (vector? ind) (vector-ref ind k) (vector-ref (array:vector ind) (array:actor-index (array:index ind) k)))) (define (array:index-set! ind k o) (if (vector? ind) (vector-set! ind k o) (vector-set! (array:vector ind) (array:actor-index (array:index ind) k) o))) (define (array:index-length ind) (if (vector? ind) (vector-length ind) (vector-ref (array:shape ind) 1))) (define (array:map->string proc r) (let* ((m (array:grok/arguments proc r)) (s (vector-ref (array:shape m) 3))) (do ((i "" (string-append i c "k" (number->string k))) (c "" ", ") (k 1 (+ k 1))) ((< r k) (do ((o "" (string-append o c (array:map-column->string m r k))) (c "" ", ") (k 0 (+ k 1))) ((= k s) (string-append i " => " o))))))) (define (array:map-column->string m r k) (let ((v (array:vector m)) (i (array:index m))) (let ((n0 (vector-ref v (array:vector-index i (list 0 k))))) (let wok ((j 1) (e (if (= n0 0) "" (number->string n0)))) (if (<= j r) (let ((nj (vector-ref v (array:vector-index i (list j k))))) (if (= nj 0) (wok (+ j 1) e) (let* ((nj (if (= nj 1) "" (if (= nj -1) "-" (string-append (number->string nj) " ")))) (njkj (string-append nj "k" (number->string j)))) (if (string=? e "") (wok (+ j 1) njkj) (wok (+ j 1) (string-append e " + " njkj)))))) (if (string=? e "") "0" e)))))) (define (array:grok/arguments proc r) (array:grok/index! (lambda (vec) (call-with-values (lambda () (array:apply-to-vector r proc vec)) vector)) (make-vector r))) (define (array:grok/index! proc in) (let ((m (array:index-length in))) (do ((k 0 (+ k 1))) ((= k m)) (array:index-set! in k 0)) (let* ((n0 (proc in)) (n (array:index-length n0))) (let ((arr (make-array (shape 0 (+ m 1) 0 n)))) ; (*) (do ((k 0 (+ k 1))) ((= k n)) (array-set! arr 0 k (array:index-ref n0 k))) ; (**) (do ((j 0 (+ j 1))) ((= j m)) (array:index-set! in j 1) (let ((nj (proc in))) (array:index-set! in j 0) (do ((k 0 (+ k 1))) ((= k n)) (array-set! arr (+ j 1) k (- (array:index-ref nj k) ; (**) (array:index-ref n0 k)))))) arr)))) ;; (*) Should not use `make-array' and `shape' here ;; (**) Should not use `array-set!' here ;; Should use something internal to the library instead: either lower ;; level code (preferable but complex) or alternative names to these same. |
Added srfi/s25/ix-ctor.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
(define (array-ref a . xs) (or (array:array? a) (error "not an array")) (let ((shape (array:shape a))) (if (null? xs) (array:check-indices "array-ref" xs shape) (let ((x (car xs))) (if (vector? x) (array:check-index-vector "array-ref" x shape) (if (integer? x) (array:check-indices "array-ref" xs shape) (if (array:array? x) (array:check-index-actor "array-ref" x shape) (error "not an index object")))))) (vector-ref (array:vector a) (if (null? xs) (vector-ref (array:index a) 0) (let ((x (car xs))) (if (vector? x) (array:index/vector (quotient (vector-length shape) 2) (array:index a) x) (if (integer? x) (array:vector-index (array:index a) xs) (if (array:array? x) (array:index/array (quotient (vector-length shape) 2) (array:index a) (array:vector x) (array:index x)) (error "array-ref: bad index object"))))))))) (define (array-set! a x . xs) (or (array:array? a) (error "array-set!: not an array")) (let ((shape (array:shape a))) (if (null? xs) (array:check-indices "array-set!" '() shape) (if (vector? x) (array:check-index-vector "array-set!" x shape) (if (integer? x) (array:check-indices.o "array-set!" (cons x xs) shape) (if (array:array? x) (array:check-index-actor "array-set!" x shape) (error "not an index object"))))) (if (null? xs) (vector-set! (array:vector a) (vector-ref (array:index a) 0) x) (if (vector? x) (vector-set! (array:vector a) (array:index/vector (quotient (vector-length shape) 2) (array:index a) x) (car xs)) (if (integer? x) (let ((v (array:vector a)) (i (array:index a)) (r (quotient (vector-length shape) 2))) (do ((sum (* (vector-ref i 0) x) (+ sum (* (vector-ref i k) (car ks)))) (ks xs (cdr ks)) (k 1 (+ k 1))) ((= k r) (vector-set! v (+ sum (vector-ref i k)) (car ks))))) (if (array:array? x) (vector-set! (array:vector a) (array:index/array (quotient (vector-length shape) 2) (array:index a) (array:vector x) (array:index x)) (car xs)) (error (string-append "array-set!: bad index object: " (array:thing->string x))))))))) |
Added srfi/s25/list.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 |
;;; An identity matrix. (define i_4 (let* ((i (make-array (shape 0 4 0 4) 0)) (d (share-array i (shape 0 4) (lambda (k) (values k k))))) (do ((k 0 (+ k 1))) ((= k 4)) (array-set! d k 1)) i)) (past "i_4") (or (array-equal? i_4 (tabulate-array (shape 0 4 0 4) (lambda (j k) (if (= j k) 1 0)))) (error "failed to build i_4")) (past "i_4 vs tabulate-array") (or (array-equal? i_4 (array (shape 0 4 0 4) 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1)) (error "failed to array i_4")) (past "i_4 vs array") (or (array-equal? (share-array i_4 (shape 0 4) (lambda (k) (values k k))) (share-array (array (shape) 1) (shape 0 4) (lambda (k) (values)))) (error "failed to share diagonal of i_4 or cell of 1")) (past "i_4 diagonal") (or (array-equal? (share-array i_4 (shape 0 4) (lambda (k) (values (- 3 k) k))) (share-array (array (shape) 0) (shape 0 4) (lambda (k) (values)))) (error "failed to share codiagonal of i_4 or cell of 0")) (past "i_4 codiagonal") (or (array-equal? (share-array i_4 (shape 0 2 0 2) (lambda (j k) (values (* 3 j) (* 3 k)))) (share-array i_4 (shape 0 2 0 2) (lambda (j k) (values (+ j 1) (+ k 1))))) (error "failed to share corners or center of i_4")) (past "i_4 corners and center") (or (array-equal? i_4 (transpose i_4)) (error "failed to transpose i_4")) (past "i_4 transpose") ;;; Try a three dimensional transpose. This will also exercise matrix ;;; multiplication. (define threed123 (array (shape 0 1 0 2 0 3) 'a 'b 'c 'd 'e 'f)) (past "threed123") (define threed312 (array (shape 0 3 0 1 0 2) 'a 'd 'b 'e 'c 'f)) (past "threed312") (define rot231 (list 1 2 0)) ;; 0 1 0 ;; 0 0 1 ;; 1 0 0 (or (array-equal? threed123 (apply transpose threed312 rot231)) (error "failed to transpose three dimensions")) (past "threed123 transpose") ;;; The frivolous board game exercises share of share of share. ;;; A three dimensional chess board with two phases: piece and colour ;;; of piece. Think of pieces in a cube with height, width and depth, ;;; and piece colours in a parallel cube. We put pink jays around and ;;; grey crows inside the board proper. Later we put in a blue rook. (define board (tabulate-array (shape -1 9 -1 9 -1 9 0 2) (lambda (t u v w) (case w ((0) (if (and (< -1 u 8) (< -1 v 8) (< -1 t 8)) 'crow 'jay)) ((1) (if (and (< -1 u 8) (< -1 v 8) (< -1 t 8)) 'grey 'pink)))))) (past "board") ;;; A cylinder with height 4, width 4, depth 6, both phases, centered ;;; inside the board. Top left front corner is at 0 0 0 of cylinder but ;;; 2 2 1 of board. (define board-cylinder (share-array board (shape 0 4 0 4 0 6 0 2) (lambda (t u v w) (values (+ t 2) (+ u 2) (+ v 1) w)))) (past "board-cylinder") ;;; The center cube with side 2 of the cylinder, hence of the board, ;;; with both phases. Top left corner is 0 0 0 of center but 1 1 2 ;;; of cylinder and 3 3 3 of board. (define board-center (share-array board-cylinder (shape 0 2 0 2 0 2 0 2) (lambda (t u v w) (values (+ t 1) (+ u 1) (+ v 2) w)))) (past "board-center") ;;; Front face of center cube, in two dimensions plus phase. Top left ;;; corner is 0 0 of face but 0 0 0 of center and 1 1 2 of cylinder ;;; 3 3 3 of board. (define board-face (share-array board-center (shape 0 2 0 2 0 2) (lambda (t u w) (values t u 0 w)))) (past "board-face") ;;; Left side of face in three dimensions plus phase. Top is 0 0 0 of ;;; pillar but 0 0 of face and 0 0 0 of center and 1 1 2 of cylinder ;;; and 3 3 3 of board. Bottom is 1 0 0 of pillar but 1 0 of face and ;;; 1 0 0 of center and 2 1 2 of cylinder and 4 3 3 of board. (define board-pillar (share-array board-face (shape 0 2 0 1 0 1 0 2) (lambda (t u v w) (values t 0 w)))) (past "board-pillar") ;;; Pillar upside down. Now top 0 0 0 is 1 0 of face and 1 0 0 of center ;;; and 2 1 2 of cylinder and 4 3 3 of board. (define board-reverse-pillar (share-array board-pillar (shape 0 2 0 1 0 1 0 2) (lambda (t u v w) (values (- 1 t) u v w)))) (past "board-reverse-pillar") ;;; Bottom of pillar. (define board-cubicle (share-array board-pillar (shape 0 2) (lambda (w) (values 1 0 0 w)))) (past "board-cubicle") ;;; Top of upside down pair. (define board-reverse-cubicle (share-array board-reverse-pillar (shape 0 2) (lambda (w) (values 0 0 0 w)))) (past "board-reverse-cubicle") ;;; Piece phase of cubicle. (define board-piece (share-array board-cubicle (shape) (lambda () (values 0)))) (past "board-piece") ;;; Colour phase of the other cubicle that is actually the same cubicle. (define board-colour (share-array board-reverse-cubicle (shape) (lambda () (values 1)))) (past "board-colour") ;;; Put a blue rook at the bottom of the pillar and at the top of the ;;; upside pillar. (array-set! board-piece 'rook) (array-set! board-colour 'blue) (past "array-set! to board-piece and board-colour") ;;; Build the same chess position directly. (define board-two (tabulate-array (shape -1 9 -1 9 -1 9 0 2) (lambda (t u v w) (if (and (= t 4) (= u 3) (= v 3)) (case w ((0) 'rook) ((1) 'blue)) (case w ((0) (if (and (< -1 u 8) (< -1 v 8) (< -1 t 8)) 'crow 'jay)) ((1) (if (and (< -1 u 8) (< -1 v 8) (< -1 t 8)) 'grey 'pink))))))) (past "board-two") (or (array-equal? board board-two) (error "failed in three dimensional chess")) (past "board vs board-two") ;;; Permute the dimensions of the chess board in two different ways. ;;; The transpose also exercises matrix multiplication. (define board-three (share-array board-two (shape 0 2 -1 9 -1 9 -1 9) (lambda (w t u v) (values t u v w)))) (past "board-three") (or (array-equal? board-three (transpose board-two 3 0 1 2)) ;; 0 0 0 1 ;; 1 0 0 0 ;; 0 1 0 0 ;; 0 0 1 0 (error "failed to permute chess board dimensions")) (past "board-three vs transpose of board-two") (or (array-equal? (share-array board-two (shape -1 9 0 2 -1 9 -1 9) (lambda (t w u v) (values t u v w))) (transpose board-two 0 3 1 2)) ;; 1 0 0 0 ;; 0 0 0 1 ;; 0 1 0 0 ;; 0 0 1 0 (error "failed to permute chess board dimensions another way")) (past "board-two versus transpose of board-two") ;;; Just see that empty share does not crash. No index is valid. Just by ;;; the way. There is nothing to be done with it. (define board-nothing (share-array board (shape 0 3 1 1 0 3) (lambda (t u v) (values 0 0 0)))) (or (array-equal? board-nothing (array (array-shape board-nothing))) (error "board-nothing failed")) (past "board-nothing") ;;; --- (or (array-equal? (tabulate-array (shape 4 8 2 5 0 1) *) (tabulate-array! (shape 4 8 2 5 0 1) (lambda (v) (* (vector-ref v 0) (vector-ref v 1) (vector-ref v 2))) (vector * * *))) (error "tabulate-array! with vector failed")) (past "tabulate-array! with vector") (or (array-equal? (tabulate-array (shape 4 8 2 5 0 1) *) (let ((index (share-array (make-array (shape 0 2 0 3)) (shape 0 3) (lambda (k) (values 1 k))))) (tabulate-array! (shape 4 8 2 5 0 1) (lambda (a) (* (array-ref a 0) (array-ref a 1) (array-ref a 2))) index))) (error "tabulate-array! with array failed")) (past "tabulate-array! with array") ;;; Sum of constants (or (array-equal? (array-map + (share-array (array (shape) 0) (shape 1 2 1 4) (lambda _ (values))) (share-array (array (shape) 1) (shape 1 2 1 4) (lambda _ (values))) (share-array (array (shape) 2) (shape 1 2 1 4) (lambda _ (values)))) (array (shape 1 2 1 4) 3 3 3)) (error "failed to map constants to their constant sum")) (past "array-map sum") ;;; Multiplication table (define four-by-four (array (shape 0 4 0 4) 0 0 0 0 0 1 2 3 0 2 4 6 0 3 6 9)) (past "four-by-four") (or (array-equal? four-by-four (tabulate-array (shape 0 4 0 4) *)) (error "failed to tabulate four by four")) (past "four-by-four vs tabulate-array") (or (array-equal? four-by-four (let ((table (make-array (shape 0 4 0 4) 19101))) (array-retabulate! table (array-shape table) *) table)) (error "failed to retabulate four by four simply")) (past "four-by-four vs array-retabulate!") (or (array-equal? four-by-four (let ((table (make-array (shape 0 4 0 4) 19101))) (array-retabulate! table (shape 1 2 1 4) (lambda (v) (* (vector-ref v 0) (vector-ref v 1))) (vector - -)) (array-retabulate! table (shape 2 4 0 4) (lambda (a) (* (array-ref a (vector 0)) (array-ref a (vector 1)))) (make-array (shape 0 2))) (array-set! table 0 0 0) (array-set! table (vector 0 1) 0) (array-set! table (array (shape 0 2) 0 2) 0) (shape-for-each (shape 0 1 3 4) (lambda (v) (array-set! table v (vector-ref v 0))) (vector - -)) (let ((arr (share-array table (shape 1 2 0 1 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8) (lambda (r k . _) (values r k))))) (array-retabulate! arr (array-shape arr) *)) table)) (error "failed to retabulate four by four in a hard way")) (past "four-by-four vs array-retabulate! on parts") ;;; An argument was missing in a call in arlib when ;;; shape-for-each was called without an index object. (or (let ((em '())) (shape-for-each (shape 0 2 -2 0 0 1) (lambda (u v w) (set! em (cons (list u v w) em)))) (equal? (reverse em) '((0 -2 0) (0 -1 0) (1 -2 0) (1 -1 0)))) (error "shape-for-each without index object")) (past "shape-for-each without index object") ;;; Exercise share-array/index! (or (let ((arr (tabulate-array (shape 2 4 3 5 4 7) *))) (array-equal? (share-array/index! arr (array-shape arr) (lambda (v) v) (vector * * *)) arr)) (error "share-array/index! with identity and vector failed")) (past "share-array/index! with identity and vector") (or (let ((arr (tabulate-array (shape 2 4 3 5 4 7) *)) (ind (share-array (make-array (shape 0 2 0 3)) (shape 0 3) (lambda (k) (values 1 k))))) (array-equal? (share-array/index! arr (array-shape arr) (lambda (a) a) ind) arr)) (error "share-array/index! with identity and array failed")) (past "share-array/index! with identity and array") (or (let ((arr (tabulate-array (shape 3 5 4 5 4 7) *)) (in (vector * *)) (out (array (shape 0 3) 4 * *))) (array-equal? (share-array/index! arr (shape 4 5 4 7) (lambda (in) (array-set! out 1 (vector-ref in 0)) (array-set! out 2 (vector-ref in 1)) out) in) (share-array arr (shape 4 5 4 7) (lambda (j k) (values 4 j k))))) (error "share-array/index! with vector in array out failed")) (past "share-array/index! with vector in array out") (or (let ((arr (tabulate-array (shape 3 5 4 5 4 7) *)) (in (array (shape 0 2) * *)) (out (vector 4 * *))) (array-equal? (share-array/index! arr (shape 4 5 4 7) (lambda (in) (vector-set! out 1 (array-ref in 0)) (vector-set! out 2 (array-ref in 1)) out) in) (share-array arr (shape 4 5 4 7) (lambda (j k) (values 4 j k))))) (error "share-array/index! with array in vector out failed")) (past "share-array/index! with array in vector out") (let ((x (array (shape 2 4 3 5 4 5 5 7 6 8) 10 11 12 13 20 21 22 23 30 31 32 33 40 41 42 43))) (or (array-equal? (share-array/origin x 3 3 3 3 3) (array-append 0 (array (shape 3 3 3 5 3 4 3 5 3 5)) x)) (error "share-array/origin against empty array-append failed")) (or (array-equal? (share-array/origin x 3 3 3 3 3) (array-append 3 (array (shape 3 5 3 5 3 4 3 3 3 5)) x)) (error "share-array/origin against empty array-append failed"))) (past "share-array/origin against empty array-append") (let ((a* (make-array (shape 4 6 7 9 100 101) 'a)) (b* (make-array (shape 3 6 7 8 200 201) 'b)) (c* (make-array (shape 0 1 2 4 300 301) 'c))) (or (array-equal? (array-append 1 (array-append 0 a* c*) b* b* b*) (apply array (shape 4 7 7 12 100 101) '(a a b b b a a b b b c c b b b))) (error "array-append failed"))) (past "array-append") (let ((a* (make-array (shape 4 6 7 9 100 101) 'a)) (b* (make-array (shape 3 6 7 8 200 201) 'b)) (c* (make-array (shape 0 1 2 4 300 301) 'c))) (or (array-equal? (array-append 1 a* (transpose c* 1 0 2) (array-append 0 (transpose b* 1 0 2) (transpose b* 1 0 2))) (apply array (shape 4 6 7 13 100 101) '(a a c b b b a a c b b b))) (error "array-append with transpose failed"))) (past "array-append with transpose") ;;; Check that share-array/index! agrees with share-array. (let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd))) (or (array-equal? m (share-array m (shape 1 3 1 3) values)) (error "share-array identity failed")) (or (array-equal? m (share-array/index! m (shape 1 3 1 3) (lambda (x) x) (vector * *))) (error "share-array/index! identity with vector failed")) (or (array-equal? m (share-array/index! m (shape 1 3 1 3) (lambda (x) x) (make-array (shape 0 2)))) (error "share-array/index! identity with actor failed"))) (past "share-array/index! identity") (let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd))) (or (array-equal? (share-array m (shape 1 3) (lambda (r) (values r 1))) (share-array/index! m (shape 1 3) (lambda (x) (vector (vector-ref x 0) 1)) (vector *))) (error "share-array/index! 1-d column failed"))) (past "share-array/index! 1-d column") (let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd))) (or (array-equal? (share-array m (shape 1 3 1 3) (lambda (r k) (values r 1))) (share-array/index! m (shape 1 3 1 3) (lambda (x) (vector (vector-ref x 0) 1)) (vector * *))) (error "share-array/index! 2-d column failed"))) (past "share-array/index! 2-d column") (let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd))) (or (array-equal? (share-array m (shape 1 3) (lambda (k) (values 1 k))) (share-array/index! m (shape 1 3) (lambda (x) (vector 1 (vector-ref x 0))) (vector *))) (error "share-array/index! 1-d row failed"))) (past "share-array/index! 1-d row") (let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd))) (or (array-equal? (share-array m (shape 1 2 1 3) (lambda (r k) (values 1 k))) (share-array/index! m (shape 1 2 1 3) (lambda (x) (vector 1 (vector-ref x 1))) (vector * *))) (error "share-array/index! 2-d row failed"))) (past "share-array/index! 2-d row") (let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd))) (or (array-equal? (share-array m (shape 1 3) (lambda (r) (values r r))) (share-array/index! m (shape 1 3) (lambda (x) (vector (vector-ref x 0) (vector-ref x 0))) (vector *))) (error "share-array/index! diagonal failed"))) (past "share-array/index! diagonal") (let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd))) (or (array-equal? (share-array m (shape) (lambda () (values 1 2))) (share-array/index! m (shape) (lambda (x) (vector 1 2)) (vector))) (error "share-array/index! 0-d corner failed"))) (past "share-array/index! 0-d corner") (let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd))) (or (array-equal? (share-array m (shape 1 2) (lambda (_) (values 1 2))) (share-array/index! m (shape 1 2) (lambda (x) (vector 1 2)) (vector *))) (error "share-array/index! 1-d corner failed"))) (past "share-array/index! 1-d corner") (let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd))) (or (array-equal? (share-array m (shape 1 2 1 2) (lambda (r k) (values 1 2))) (share-array/index! m (shape 1 2 1 2) (lambda (x) (vector 1 2)) (vector * *))) (error "share-array/index! 2-d corner failed"))) (past "share-array/index! 2-d corner") (let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd))) (or (array-equal? (share-array/prefix m 1) (share-array/index! m (shape 1 3) (lambda (x) (vector 1 (vector-ref x 0))) (vector *))) (error "share-array/index! with prefix 1 failed"))) (past "share-array/{prefix,index!} 1") (let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd))) (or (array-equal? (share-array/prefix m (vector 1)) (share-array/index! m (shape 1 3) (lambda (x) (vector 1 (vector-ref x 0))) (vector *))) (error "share-array/prefix with vector failed"))) (past "share-array/prefix with vector") (let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd))) (or (array-equal? (share-array/prefix m 2) (share-array/index! m (shape 1 3) (lambda (x) (vector 2 (vector-ref x 0))) (vector *))) (error "share-array/index! with prefix 2 failed"))) (past "share-array/{prefix,index!} 2") (let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd))) (or (array-equal? (share-array/prefix m (array (shape 0 1) 2)) (share-array/index! m (shape 1 3) (lambda (x) (vector 2 (vector-ref x 0))) (vector *))) (error "share-array/prefix with array failed"))) (past "share-array/prefix with array") (let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd))) (or (array-equal? (share-array/prefix m) (share-array/index! m (shape 1 3 1 3) (lambda (x) x) (vector * *))) (error "share-array/index! with empty prefix failed"))) (past "share-array/{prefix,index!} e") (let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd))) (or (array-equal? (share-array/prefix m 1 2) (share-array/index! m (shape) (lambda (x) (vector 1 2)) (vector))) (error "share-array/index! with prefix 1 2 failed"))) (past "share-array/{prefix,index!} 1 2") ;;; Uh oh. (let* ((hape (tabulate-array (shape 0 57 0 2) (lambda (r k) (case k ((0) r) ((1) (case r ((0) (+ r 2)) ((56) (+ r 4)) (else (+ r 1)))))))) (tape (tabulate-array (shape 0 34 0 2) (lambda (r k) (case k ((0) (+ r 23)) ((1) (case r ((33) (+ r 27)) (else (+ r 24)))))))) (long (make-vector 57 *)) (shot (make-vector 34 *)) (huge (tabulate-array! hape (lambda (ix) (vector-ref '#(a b) (vector-ref ix 0))) long)) (tiny0 (share-array/index! huge tape (begin (do ((k 0 (+ k 1))) ((= k 23)) (vector-set! long k k)) (lambda (ix) (do ((k 23 (+ k 1))) ((= k 57)) (vector-set! long k (vector-ref ix (- k 23)))) long)) shot)) (tiny1 (share-array/index! huge tape (begin (vector-set! long 0 1) (do ((k 1 (+ k 1))) ((= k 23)) (vector-set! long k k)) (lambda (ix) (do ((k 23 (+ k 1))) ((= k 57)) (vector-set! long k (vector-ref ix (- k 23)))) long)) shot))) (or (and (equal? (array->vector huge) '#(a a a a b b b b)) (equal? (array->vector tiny0) '#(a a a a)) (equal? (array->vector tiny1) '#(b b b b))) (error "share-array/index! failed huge or tiny contents")) (or (array-equal? huge (share-array/index! (array (shape 4 6) 'a 'b) hape (lambda (ix) (vector-ref '#(#(4) #(5)) (vector-ref ix 0))) long)) (error "share-array/index! failed huge")) (or (array-equal? tiny0 (share-array/index! (array (shape 6 7) 'a) tape (lambda (ix) '#(6)) shot)) (error "share-array/index! failed tiny0")) (or (array-equal? tiny1 (share-array/index! (array (shape 6 7 8 9) 'b) tape (lambda (ix) '#(6 8)) shot)) (error "share-array/index! failed tiny1"))) (past "share-array/index! huge as tiny") |
Added srfi/s25/multi-dimensional-arrays.sls.
> > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#!r6rs ;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi s25 multi-dimensional-arrays) (export array? make-array shape array array-rank array-start array-end array-ref array-set! share-array) (import (srfi s25 multi-dimensional-arrays all)) ) |
Added srfi/s25/multi-dimensional-arrays/all.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
#!r6rs ;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi s25 multi-dimensional-arrays all) (export array:make array:array? array:vector array:index array:shape array-ref array-set! array:opt-args array:optimize array:optimize-empty array:coefficients array:vector-index array:shape-index array:empty-shape-index array:shape-vector-index array:actor-index array:0 array:1 array:2 array:3 array:n array:maker array:indexer/vector array:indexer/array array:applier-to-vector array:applier-to-actor array:applier-to-backing-vector array:index/vector array:index/array array:apply-to-vector array:apply-to-actor array? make-array array:make-array shape array array-rank array-start array-end share-array array:share/index! array:optimize/vector array:optimize/actor array:shape->vector array:size array:make-index array:good-shape? array:good-share? array:unchecked-share-depth? array:check-indices array:check-indices.o array:check-index-vector array:check-index-actor array:good-indices? array:good-indices.o? array:good-index-vector? array:good-index-actor? array:good-index? array:not-in array:list->string array:shape-vector->string array:thing->string array:index-ref array:index-set! array:index-length array:map->string array:map-column->string array:grok/arguments array:grok/index!) (import (rnrs) (rnrs mutable-pairs) (rnrs r5rs) (srfi s23 error tricks) (srfi private include)) (define-record-type (array-type array:make array:array?) (fields (immutable vec array:vector) (immutable ind array:index) (immutable shp array:shape))) (SRFI-23-error->R6RS "(library (srfi s25 multi-dimensional-arrays))" (include/resolve ("srfi" "s25") "ix-ctor.scm") (include/resolve ("srfi" "s25") "op-ctor.scm") (include/resolve ("srfi" "s25") "array.scm")) ) |
Added srfi/s25/multi-dimensional-arrays/arlib.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
#!r6rs ;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi s25 multi-dimensional-arrays arlib) (export array-shape array-length array-size array-equal? shape-for-each array-for-each-index tabulate-array tabulate-array! array-retabulate! array-map array-map! array->vector array->list share-array/prefix share-row share-column share-array/origin share-array/index! array-append transpose share-nths) (import (rnrs) (rnrs r5rs) (srfi s23 error tricks) (srfi s25 multi-dimensional-arrays all) (srfi private include)) (SRFI-23-error->R6RS "(library (srfi s25 multi-dimensional-arrays arlib))" (include/resolve ("srfi" "s25") "arlib.scm")) ) |
Added srfi/s25/op-ctor.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 |
(begin (define array:opt-args '(ctor (4))) (define (array:optimize f r) (case r ((0) (let ((n0 (f))) (array:0 n0))) ((1) (let ((n0 (f 0))) (array:1 n0 (- (f 1) n0)))) ((2) (let ((n0 (f 0 0))) (array:2 n0 (- (f 1 0) n0) (- (f 0 1) n0)))) ((3) (let ((n0 (f 0 0 0))) (array:3 n0 (- (f 1 0 0) n0) (- (f 0 1 0) n0) (- (f 0 0 1) n0)))) (else (let ((v (do ((k 0 (+ k 1)) (v '() (cons 0 v))) ((= k r) v)))) (let ((n0 (apply f v))) (apply array:n n0 (array:coefficients f n0 v v))))))) (define (array:optimize-empty r) (let ((x (make-vector (+ r 1) 0))) (vector-set! x r -1) x)) (define (array:coefficients f n0 vs vp) (case vp ((()) '()) (else (set-car! vp 1) (let ((n (- (apply f vs) n0))) (set-car! vp 0) (cons n (array:coefficients f n0 vs (cdr vp))))))) (define (array:vector-index x ks) (do ((sum 0 (+ sum (* (vector-ref x k) (car ks)))) (ks ks (cdr ks)) (k 0 (+ k 1))) ((null? ks) (+ sum (vector-ref x k))))) (define (array:shape-index) '#(2 1 0)) (define (array:empty-shape-index) '#(0 0 -1)) (define (array:shape-vector-index x r k) (+ (* (vector-ref x 0) r) (* (vector-ref x 1) k) (vector-ref x 2))) (define (array:actor-index x k) (+ (* (vector-ref x 0) k) (vector-ref x 1))) (define (array:0 n0) (vector n0)) (define (array:1 n0 n1) (vector n1 n0)) (define (array:2 n0 n1 n2) (vector n1 n2 n0)) (define (array:3 n0 n1 n2 n3) (vector n1 n2 n3 n0)) (define (array:n n0 n1 n2 n3 n4 . ns) (apply vector n1 n2 n3 n4 (append ns (list n0)))) (define (array:maker r) (case r ((0) array:0) ((1) array:1) ((2) array:2) ((3) array:3) (else array:n))) (define array:indexer/vector (let ((em (vector (lambda (x i) (+ (vector-ref x 0))) (lambda (x i) (+ (* (vector-ref x 0) (vector-ref i 0)) (vector-ref x 1))) (lambda (x i) (+ (* (vector-ref x 0) (vector-ref i 0)) (* (vector-ref x 1) (vector-ref i 1)) (vector-ref x 2))) (lambda (x i) (+ (* (vector-ref x 0) (vector-ref i 0)) (* (vector-ref x 1) (vector-ref i 1)) (* (vector-ref x 2) (vector-ref i 2)) (vector-ref x 3))) (lambda (x i) (+ (* (vector-ref x 0) (vector-ref i 0)) (* (vector-ref x 1) (vector-ref i 1)) (* (vector-ref x 2) (vector-ref i 2)) (* (vector-ref x 3) (vector-ref i 3)) (vector-ref x 4))) (lambda (x i) (+ (* (vector-ref x 0) (vector-ref i 0)) (* (vector-ref x 1) (vector-ref i 1)) (* (vector-ref x 2) (vector-ref i 2)) (* (vector-ref x 3) (vector-ref i 3)) (* (vector-ref x 4) (vector-ref i 4)) (vector-ref x 5))) (lambda (x i) (+ (* (vector-ref x 0) (vector-ref i 0)) (* (vector-ref x 1) (vector-ref i 1)) (* (vector-ref x 2) (vector-ref i 2)) (* (vector-ref x 3) (vector-ref i 3)) (* (vector-ref x 4) (vector-ref i 4)) (* (vector-ref x 5) (vector-ref i 5)) (vector-ref x 6))) (lambda (x i) (+ (* (vector-ref x 0) (vector-ref i 0)) (* (vector-ref x 1) (vector-ref i 1)) (* (vector-ref x 2) (vector-ref i 2)) (* (vector-ref x 3) (vector-ref i 3)) (* (vector-ref x 4) (vector-ref i 4)) (* (vector-ref x 5) (vector-ref i 5)) (* (vector-ref x 6) (vector-ref i 6)) (vector-ref x 7))) (lambda (x i) (+ (* (vector-ref x 0) (vector-ref i 0)) (* (vector-ref x 1) (vector-ref i 1)) (* (vector-ref x 2) (vector-ref i 2)) (* (vector-ref x 3) (vector-ref i 3)) (* (vector-ref x 4) (vector-ref i 4)) (* (vector-ref x 5) (vector-ref i 5)) (* (vector-ref x 6) (vector-ref i 6)) (* (vector-ref x 7) (vector-ref i 7)) (vector-ref x 8))) (lambda (x i) (+ (* (vector-ref x 0) (vector-ref i 0)) (* (vector-ref x 1) (vector-ref i 1)) (* (vector-ref x 2) (vector-ref i 2)) (* (vector-ref x 3) (vector-ref i 3)) (* (vector-ref x 4) (vector-ref i 4)) (* (vector-ref x 5) (vector-ref i 5)) (* (vector-ref x 6) (vector-ref i 6)) (* (vector-ref x 7) (vector-ref i 7)) (* (vector-ref x 8) (vector-ref i 8)) (vector-ref x 9))))) (it (lambda (w) (lambda (x i) (+ (* (vector-ref x 0) (vector-ref i 0)) (* (vector-ref x 1) (vector-ref i 1)) (* (vector-ref x 2) (vector-ref i 2)) (* (vector-ref x 3) (vector-ref i 3)) (* (vector-ref x 4) (vector-ref i 4)) (* (vector-ref x 5) (vector-ref i 5)) (* (vector-ref x 6) (vector-ref i 6)) (* (vector-ref x 7) (vector-ref i 7)) (* (vector-ref x 8) (vector-ref i 8)) (* (vector-ref x 9) (vector-ref i 9)) (do ((xi 0 (+ (* (vector-ref x u) (vector-ref i u)) xi)) (u (- w 1) (- u 1))) ((< u 10) xi)) (vector-ref x w)))))) (lambda (r) (if (< r 10) (vector-ref em r) (it r))))) (define array:indexer/array (let ((em (vector (lambda (x v i) (+ (vector-ref x 0))) (lambda (x v i) (+ (* (vector-ref x 0) (vector-ref v (array:actor-index i 0))) (vector-ref x 1))) (lambda (x v i) (+ (* (vector-ref x 0) (vector-ref v (array:actor-index i 0))) (* (vector-ref x 1) (vector-ref v (array:actor-index i 1))) (vector-ref x 2))) (lambda (x v i) (+ (* (vector-ref x 0) (vector-ref v (array:actor-index i 0))) (* (vector-ref x 1) (vector-ref v (array:actor-index i 1))) (* (vector-ref x 2) (vector-ref v (array:actor-index i 2))) (vector-ref x 3))) (lambda (x v i) (+ (* (vector-ref x 0) (vector-ref v (array:actor-index i 0))) (* (vector-ref x 1) (vector-ref v (array:actor-index i 1))) (* (vector-ref x 2) (vector-ref v (array:actor-index i 2))) (* (vector-ref x 3) (vector-ref v (array:actor-index i 3))) (vector-ref x 4))) (lambda (x v i) (+ (* (vector-ref x 0) (vector-ref v (array:actor-index i 0))) (* (vector-ref x 1) (vector-ref v (array:actor-index i 1))) (* (vector-ref x 2) (vector-ref v (array:actor-index i 2))) (* (vector-ref x 3) (vector-ref v (array:actor-index i 3))) (* (vector-ref x 4) (vector-ref v (array:actor-index i 4))) (vector-ref x 5))) (lambda (x v i) (+ (* (vector-ref x 0) (vector-ref v (array:actor-index i 0))) (* (vector-ref x 1) (vector-ref v (array:actor-index i 1))) (* (vector-ref x 2) (vector-ref v (array:actor-index i 2))) (* (vector-ref x 3) (vector-ref v (array:actor-index i 3))) (* (vector-ref x 4) (vector-ref v (array:actor-index i 4))) (* (vector-ref x 5) (vector-ref v (array:actor-index i 5))) (vector-ref x 6))) (lambda (x v i) (+ (* (vector-ref x 0) (vector-ref v (array:actor-index i 0))) (* (vector-ref x 1) (vector-ref v (array:actor-index i 1))) (* (vector-ref x 2) (vector-ref v (array:actor-index i 2))) (* (vector-ref x 3) (vector-ref v (array:actor-index i 3))) (* (vector-ref x 4) (vector-ref v (array:actor-index i 4))) (* (vector-ref x 5) (vector-ref v (array:actor-index i 5))) (* (vector-ref x 6) (vector-ref v (array:actor-index i 6))) (vector-ref x 7))) (lambda (x v i) (+ (* (vector-ref x 0) (vector-ref v (array:actor-index i 0))) (* (vector-ref x 1) (vector-ref v (array:actor-index i 1))) (* (vector-ref x 2) (vector-ref v (array:actor-index i 2))) (* (vector-ref x 3) (vector-ref v (array:actor-index i 3))) (* (vector-ref x 4) (vector-ref v (array:actor-index i 4))) (* (vector-ref x 5) (vector-ref v (array:actor-index i 5))) (* (vector-ref x 6) (vector-ref v (array:actor-index i 6))) (* (vector-ref x 7) (vector-ref v (array:actor-index i 7))) (vector-ref x 8))) (lambda (x v i) (+ (* (vector-ref x 0) (vector-ref v (array:actor-index i 0))) (* (vector-ref x 1) (vector-ref v (array:actor-index i 1))) (* (vector-ref x 2) (vector-ref v (array:actor-index i 2))) (* (vector-ref x 3) (vector-ref v (array:actor-index i 3))) (* (vector-ref x 4) (vector-ref v (array:actor-index i 4))) (* (vector-ref x 5) (vector-ref v (array:actor-index i 5))) (* (vector-ref x 6) (vector-ref v (array:actor-index i 6))) (* (vector-ref x 7) (vector-ref v (array:actor-index i 7))) (* (vector-ref x 8) (vector-ref v (array:actor-index i 8))) (vector-ref x 9))))) (it (lambda (w) (lambda (x v i) (+ (* (vector-ref x 0) (vector-ref v (array:actor-index i 0))) (* (vector-ref x 1) (vector-ref v (array:actor-index i 1))) (* (vector-ref x 2) (vector-ref v (array:actor-index i 2))) (* (vector-ref x 3) (vector-ref v (array:actor-index i 3))) (* (vector-ref x 4) (vector-ref v (array:actor-index i 4))) (* (vector-ref x 5) (vector-ref v (array:actor-index i 5))) (* (vector-ref x 6) (vector-ref v (array:actor-index i 6))) (* (vector-ref x 7) (vector-ref v (array:actor-index i 7))) (* (vector-ref x 8) (vector-ref v (array:actor-index i 8))) (* (vector-ref x 9) (vector-ref v (array:actor-index i 9))) (do ((xi 0 (+ (* (vector-ref x u) (vector-ref v (array:actor-index i u))) xi)) (u (- w 1) (- u 1))) ((< u 10) xi)) (vector-ref x w)))))) (lambda (r) (if (< r 10) (vector-ref em r) (it r))))) (define array:applier-to-vector (let ((em (vector (lambda (p v) (p)) (lambda (p v) (p (vector-ref v 0))) (lambda (p v) (p (vector-ref v 0) (vector-ref v 1))) (lambda (p v) (p (vector-ref v 0) (vector-ref v 1) (vector-ref v 2))) (lambda (p v) (p (vector-ref v 0) (vector-ref v 1) (vector-ref v 2) (vector-ref v 3))) (lambda (p v) (p (vector-ref v 0) (vector-ref v 1) (vector-ref v 2) (vector-ref v 3) (vector-ref v 4))) (lambda (p v) (p (vector-ref v 0) (vector-ref v 1) (vector-ref v 2) (vector-ref v 3) (vector-ref v 4) (vector-ref v 5))) (lambda (p v) (p (vector-ref v 0) (vector-ref v 1) (vector-ref v 2) (vector-ref v 3) (vector-ref v 4) (vector-ref v 5) (vector-ref v 6))) (lambda (p v) (p (vector-ref v 0) (vector-ref v 1) (vector-ref v 2) (vector-ref v 3) (vector-ref v 4) (vector-ref v 5) (vector-ref v 6) (vector-ref v 7))) (lambda (p v) (p (vector-ref v 0) (vector-ref v 1) (vector-ref v 2) (vector-ref v 3) (vector-ref v 4) (vector-ref v 5) (vector-ref v 6) (vector-ref v 7) (vector-ref v 8))))) (it (lambda (r) (lambda (p v) (apply p (vector-ref v 0) (vector-ref v 1) (vector-ref v 2) (vector-ref v 3) (vector-ref v 4) (vector-ref v 5) (vector-ref v 6) (vector-ref v 7) (vector-ref v 8) (vector-ref v 9) (do ((k r (- k 1)) (r '() (cons (vector-ref v (- k 1)) r))) ((= k 10) r))))))) (lambda (r) (if (< r 10) (vector-ref em r) (it r))))) (define array:applier-to-actor (let ((em (vector (lambda (p a) (p)) (lambda (p a) (p (array-ref a 0))) (lambda (p a) (p (array-ref a 0) (array-ref a 1))) (lambda (p a) (p (array-ref a 0) (array-ref a 1) (array-ref a 2))) (lambda (p a) (p (array-ref a 0) (array-ref a 1) (array-ref a 2) (array-ref a 3))) (lambda (p a) (p (array-ref a 0) (array-ref a 1) (array-ref a 2) (array-ref a 3) (array-ref a 4))) (lambda (p a) (p (array-ref a 0) (array-ref a 1) (array-ref a 2) (array-ref a 3) (array-ref a 4) (array-ref a 5))) (lambda (p a) (p (array-ref a 0) (array-ref a 1) (array-ref a 2) (array-ref a 3) (array-ref a 4) (array-ref a 5) (array-ref a 6))) (lambda (p a) (p (array-ref a 0) (array-ref a 1) (array-ref a 2) (array-ref a 3) (array-ref a 4) (array-ref a 5) (array-ref a 6) (array-ref a 7))) (lambda (p a) (p (array-ref a 0) (array-ref a 1) (array-ref a 2) (array-ref a 3) (array-ref a 4) (array-ref a 5) (array-ref a 6) (array-ref a 7) (array-ref a 8))))) (it (lambda (r) (lambda (p a) (apply a (array-ref a 0) (array-ref a 1) (array-ref a 2) (array-ref a 3) (array-ref a 4) (array-ref a 5) (array-ref a 6) (array-ref a 7) (array-ref a 8) (array-ref a 9) (do ((k r (- k 1)) (r '() (cons (array-ref a (- k 1)) r))) ((= k 10) r))))))) (lambda (r) "These are high level, hiding implementation at call site." (if (< r 10) (vector-ref em r) (it r))))) (define array:applier-to-backing-vector (let ((em (vector (lambda (p ai av) (p)) (lambda (p ai av) (p (vector-ref av (array:actor-index ai 0)))) (lambda (p ai av) (p (vector-ref av (array:actor-index ai 0)) (vector-ref av (array:actor-index ai 1)))) (lambda (p ai av) (p (vector-ref av (array:actor-index ai 0)) (vector-ref av (array:actor-index ai 1)) (vector-ref av (array:actor-index ai 2)))) (lambda (p ai av) (p (vector-ref av (array:actor-index ai 0)) (vector-ref av (array:actor-index ai 1)) (vector-ref av (array:actor-index ai 2)) (vector-ref av (array:actor-index ai 3)))) (lambda (p ai av) (p (vector-ref av (array:actor-index ai 0)) (vector-ref av (array:actor-index ai 1)) (vector-ref av (array:actor-index ai 2)) (vector-ref av (array:actor-index ai 3)) (vector-ref av (array:actor-index ai 4)))) (lambda (p ai av) (p (vector-ref av (array:actor-index ai 0)) (vector-ref av (array:actor-index ai 1)) (vector-ref av (array:actor-index ai 2)) (vector-ref av (array:actor-index ai 3)) (vector-ref av (array:actor-index ai 4)) (vector-ref av (array:actor-index ai 5)))) (lambda (p ai av) (p (vector-ref av (array:actor-index ai 0)) (vector-ref av (array:actor-index ai 1)) (vector-ref av (array:actor-index ai 2)) (vector-ref av (array:actor-index ai 3)) (vector-ref av (array:actor-index ai 4)) (vector-ref av (array:actor-index ai 5)) (vector-ref av (array:actor-index ai 6)))) (lambda (p ai av) (p (vector-ref av (array:actor-index ai 0)) (vector-ref av (array:actor-index ai 1)) (vector-ref av (array:actor-index ai 2)) (vector-ref av (array:actor-index ai 3)) (vector-ref av (array:actor-index ai 4)) (vector-ref av (array:actor-index ai 5)) (vector-ref av (array:actor-index ai 6)) (vector-ref av (array:actor-index ai 7)))) (lambda (p ai av) (p (vector-ref av (array:actor-index ai 0)) (vector-ref av (array:actor-index ai 1)) (vector-ref av (array:actor-index ai 2)) (vector-ref av (array:actor-index ai 3)) (vector-ref av (array:actor-index ai 4)) (vector-ref av (array:actor-index ai 5)) (vector-ref av (array:actor-index ai 6)) (vector-ref av (array:actor-index ai 7)) (vector-ref av (array:actor-index ai 8)))))) (it (lambda (r) (lambda (p ai av) (apply p (vector-ref av (array:actor-index ai 0)) (vector-ref av (array:actor-index ai 1)) (vector-ref av (array:actor-index ai 2)) (vector-ref av (array:actor-index ai 3)) (vector-ref av (array:actor-index ai 4)) (vector-ref av (array:actor-index ai 5)) (vector-ref av (array:actor-index ai 6)) (vector-ref av (array:actor-index ai 7)) (vector-ref av (array:actor-index ai 8)) (vector-ref av (array:actor-index ai 9)) (do ((k r (- k 1)) (r '() (cons (vector-ref av (array:actor-index ai (- k 1))) r))) ((= k 10) r))))))) (lambda (r) "These are low level, exposing implementation at call site." (if (< r 10) (vector-ref em r) (it r))))) (define (array:index/vector r x v) ((array:indexer/vector r) x v)) (define (array:index/array r x av ai) ((array:indexer/array r) x av ai)) (define (array:apply-to-vector r p v) ((array:applier-to-vector r) p v)) (define (array:apply-to-actor r p a) ((array:applier-to-actor r) p a))) |
Added srfi/s25/test.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 |
;;; array test ;;; 2001 Jussi Piitulainen (define past (let ((stones '())) (lambda stone (if (null? stone) (reverse stones) (set! stones (cons (apply (lambda (stone) stone) stone) stones)))))) (define (tail n) (if (< n (length (past))) (list-tail (past) (- (length (past)) n)) (past))) ;;; Simple tests (or (and (shape) (shape -1 -1) (shape -1 0) (shape -1 1) (shape 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8)) (error "(shape ...) failed")) (past "shape") (or (and (make-array (shape)) (make-array (shape) *) (make-array (shape -1 -1)) (make-array (shape -1 -1) *) (make-array (shape -1 1)) (make-array (shape 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4) *)) (error "(make-array (shape ...) [o]) failed")) (past "make-array") (or (and (array (shape) *) (array (shape -1 -1)) (array (shape -1 1) * *) (array (shape 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8) *)) (error "(array (shape ...) ...) failed")) (past "array") (or (and (= (array-rank (shape)) 2) (= (array-rank (shape -1 -1)) 2) (= (array-rank (shape -1 1)) 2) (= (array-rank (shape 1 2 3 4 5 6 7 8)) 2)) (error "(array-rank (shape ...)) failed")) (past "array-rank of shape") (or (and (= (array-rank (make-array (shape))) 0) (= (array-rank (make-array (shape -1 -1))) 1) (= (array-rank (make-array (shape -1 1))) 1) (= (array-rank (make-array (shape 1 2 3 4 5 6 7 8))) 4)) (error "(array-rank (make-array ...)) failed")) (past "array-rank of make-array") (or (and (= (array-rank (array (shape) *)) 0) (= (array-rank (array (shape -1 -1))) 1) (= (array-rank (array (shape -1 1) * *)) 1) (= (array-rank (array (shape 1 2 3 4 5 6 7 8) *)) 4)) (error "(array-rank (array ...)) failed")) (past "array-rank of array") (or (and (= (array-start (shape -1 -1) 0) 0) (= (array-start (shape -1 -1) 1) 0) (= (array-start (shape -1 1) 0) 0) (= (array-start (shape -1 1) 1) 0) (= (array-start (shape 1 2 3 4 5 6 7 8) 0) 0) (= (array-start (shape 1 2 3 4 5 6 7 8) 1) 0)) (error "(array-start (shape ...)) failed")) (past "array-start of shape") (or (and (= (array-end (shape -1 -1) 0) 1) (= (array-end (shape -1 -1) 1) 2) (= (array-end (shape -1 1) 0) 1) (= (array-end (shape -1 1) 1) 2) (= (array-end (shape 1 2 3 4 5 6 7 8) 0) 4) (= (array-end (shape 1 2 3 4 5 6 7 8) 1) 2)) (error "(array-end (shape ...)) failed")) (past "array-end of shape") (or (and (= (array-start (make-array (shape -1 -1)) 0) -1) (= (array-start (make-array (shape -1 1)) 0) -1) (= (array-start (make-array (shape 1 2 3 4 5 6 7 8)) 0) 1) (= (array-start (make-array (shape 1 2 3 4 5 6 7 8)) 1) 3) (= (array-start (make-array (shape 1 2 3 4 5 6 7 8)) 2) 5) (= (array-start (make-array (shape 1 2 3 4 5 6 7 8)) 3) 7)) (error "(array-start (make-array ...)) failed")) (past "array-start of make-array") (or (and (= (array-end (make-array (shape -1 -1)) 0) -1) (= (array-end (make-array (shape -1 1)) 0) 1) (= (array-end (make-array (shape 1 2 3 4 5 6 7 8)) 0) 2) (= (array-end (make-array (shape 1 2 3 4 5 6 7 8)) 1) 4) (= (array-end (make-array (shape 1 2 3 4 5 6 7 8)) 2) 6) (= (array-end (make-array (shape 1 2 3 4 5 6 7 8)) 3) 8)) (error "(array-end (make-array ...)) failed")) (past "array-end of make-array") (or (and (= (array-start (array (shape -1 -1)) 0) -1) (= (array-start (array (shape -1 1) * *) 0) -1) (= (array-start (array (shape 1 2 3 4 5 6 7 8) *) 0) 1) (= (array-start (array (shape 1 2 3 4 5 6 7 8) *) 1) 3) (= (array-start (array (shape 1 2 3 4 5 6 7 8) *) 2) 5) (= (array-start (array (shape 1 2 3 4 5 6 7 8) *) 3) 7)) (error "(array-start (array ...)) failed")) (past "array-start of array") (or (and (= (array-end (array (shape -1 -1)) 0) -1) (= (array-end (array (shape -1 1) * *) 0) 1) (= (array-end (array (shape 1 2 3 4 5 6 7 8) *) 0) 2) (= (array-end (array (shape 1 2 3 4 5 6 7 8) *) 1) 4) (= (array-end (array (shape 1 2 3 4 5 6 7 8) *) 2) 6) (= (array-end (array (shape 1 2 3 4 5 6 7 8) *) 3) 8)) (error "(array-end (array ...)) failed")) (past "array-end of array") (or (and (eq? (array-ref (make-array (shape) 'a)) 'a) (eq? (array-ref (make-array (shape -1 1) 'b) -1) 'b) (eq? (array-ref (make-array (shape -1 1) 'c) 0) 'c) (eq? (array-ref (make-array (shape 1 2 3 4 5 6 7 8) 'd) 1 3 5 7) 'd)) (error "array-ref of make-array with arguments failed")) (past "array-ref of make-array with arguments") (or (and (eq? (array-ref (make-array (shape) 'a) '#()) 'a) (eq? (array-ref (make-array (shape -1 1) 'b) '#(-1)) 'b) (eq? (array-ref (make-array (shape -1 1) 'c) '#(0)) 'c) (eq? (array-ref (make-array (shape 1 2 3 4 5 6 7 8) 'd) '#(1 3 5 7)) 'd)) (error "array-ref of make-array with vector failed")) (past "array-ref of make-array with vector") (or (and (eq? (array-ref (make-array (shape) 'a) (array (shape 0 0))) 'a) (eq? (array-ref (make-array (shape -1 1) 'b) (array (shape 0 1) -1)) 'b) (eq? (array-ref (make-array (shape -1 1) 'c) (array (shape 0 1) 0)) 'c) (eq? (array-ref (make-array (shape 1 2 3 4 5 6 7 8) 'd) (array (shape 0 4) 1 3 5 7)) 'd)) (error "(array-ref of make-array with array failed")) (past "array-ref of make-array with array") (or (and (let ((arr (make-array (shape) 'o))) (array-set! arr 'a) (eq? (array-ref arr) 'a)) (let ((arr (make-array (shape -1 1) 'o))) (array-set! arr -1 'b) (array-set! arr 0 'c) (and (eq? (array-ref arr -1) 'b) (eq? (array-ref arr 0) 'c))) (let ((arr (make-array (shape 1 2 3 4 5 6 7 8) 'o))) (array-set! arr 1 3 5 7 'd) (eq? (array-ref arr 1 3 5 7) 'd))) (error "array-set! with arguments failed")) (past "array-set! of make-array with arguments") (or (and (let ((arr (make-array (shape) 'o))) (array-set! arr '#() 'a) (eq? (array-ref arr) 'a)) (let ((arr (make-array (shape -1 1) 'o))) (array-set! arr '#(-1) 'b) (array-set! arr '#(0) 'c) (and (eq? (array-ref arr -1) 'b) (eq? (array-ref arr 0) 'c))) (let ((arr (make-array (shape 1 2 3 4 5 6 7 8) 'o))) (array-set! arr '#(1 3 5 7) 'd) (eq? (array-ref arr 1 3 5 7) 'd))) (error "array-set! with vector failed")) (past "array-set! of make-array with vector") (or (and (let ((arr (make-array (shape) 'o))) (array-set! arr 'a) (eq? (array-ref arr) 'a)) (let ((arr (make-array (shape -1 1) 'o))) (array-set! arr (array (shape 0 1) -1) 'b) (array-set! arr (array (shape 0 1) 0) 'c) (and (eq? (array-ref arr -1) 'b) (eq? (array-ref arr 0) 'c))) (let ((arr (make-array (shape 1 2 3 4 5 6 7 8) 'o))) (array-set! arr (array (shape 0 4) 1 3 5 7) 'd) (eq? (array-ref arr 1 3 5 7) 'd))) (error "array-set! with arguments failed")) (past "array-set! of make-array with array") ;;; Share and change: ;;; ;;; org brk swp box ;;; ;;; 0 1 1 2 5 6 ;;; 6 a b 2 a b 3 d c 0 2 4 6 8: e ;;; 7 c d 3 e f 4 f e ;;; 8 e f (or (let* ((org (array (shape 6 9 0 2) 'a 'b 'c 'd 'e 'f)) (brk (share-array org (shape 2 4 1 3) (lambda (r k) (values (+ 6 (* 2 (- r 2))) (- k 1))))) (swp (share-array org (shape 3 5 5 7) (lambda (r k) (values (+ 7 (- r 3)) (- 1 (- k 5)))))) (box (share-array swp (shape 0 1 2 3 4 5 6 7 8 9) (lambda _ (values 4 6)))) (org-contents (lambda () (list (array-ref org 6 0) (array-ref org 6 1) (array-ref org 7 0) (array-ref org 7 1) (array-ref org 8 0) (array-ref org 8 1)))) (brk-contents (lambda () (list (array-ref brk 2 1) (array-ref brk 2 2) (array-ref brk 3 1) (array-ref brk 3 2)))) (swp-contents (lambda () (list (array-ref swp 3 5) (array-ref swp 3 6) (array-ref swp 4 5) (array-ref swp 4 6)))) (box-contents (lambda () (list (array-ref box 0 2 4 6 8))))) (and (equal? (org-contents) '(a b c d e f)) (equal? (brk-contents) '(a b e f)) (equal? (swp-contents) '(d c f e)) (equal? (box-contents) '(e)) (begin (array-set! org 6 0 'x) #t) (equal? (org-contents) '(x b c d e f)) (equal? (brk-contents) '(x b e f)) (equal? (swp-contents) '(d c f e)) (equal? (box-contents) '(e)) (begin (array-set! brk 3 1 'y) #t) (equal? (org-contents) '(x b c d y f)) (equal? (brk-contents) '(x b y f)) (equal? (swp-contents) '(d c f y)) (equal? (box-contents) '(y)) (begin (array-set! swp 4 5 'z) #t) (equal? (org-contents) '(x b c d y z)) (equal? (brk-contents) '(x b y z)) (equal? (swp-contents) '(d c z y)) (equal? (box-contents) '(y)) (begin (array-set! box 0 2 4 6 8 'e) #t) (equal? (org-contents) '(x b c d e z)) (equal? (brk-contents) '(x b e z)) (equal? (swp-contents) '(d c z e)) (equal? (box-contents) '(e)))) (error "shared change failed")) (past "shared change") ;;; Check that arrays copy the shape specification (or (let ((shp (shape 10 12))) (let ((arr (make-array shp)) (ars (array shp * *)) (art (share-array (make-array shp) shp (lambda (k) k)))) (array-set! shp 0 0 '?) (array-set! shp 0 1 '!) (and (= (array-rank shp) 2) (= (array-start shp 0) 0) (= (array-end shp 0) 1) (= (array-start shp 1) 0) (= (array-end shp 1) 2) (eq? (array-ref shp 0 0) '?) (eq? (array-ref shp 0 1) '!) (= (array-rank arr) 1) (= (array-start arr 0) 10) (= (array-end arr 0) 12) (= (array-rank ars) 1) (= (array-start ars 0) 10) (= (array-end ars 0) 12) (= (array-rank art) 1) (= (array-start art 0) 10) (= (array-end art 0) 12)))) (error "array-set! of shape failed")) (past "array-set! of shape") ;;; Check that index arrays work even when they share ;;; ;;; arr ixn ;;; 5 6 0 1 ;;; 4 nw ne 0 4 6 ;;; 5 sw se 1 5 4 (or (let ((arr (array (shape 4 6 5 7) 'nw 'ne 'sw 'se)) (ixn (array (shape 0 2 0 2) 4 6 5 4))) (let ((col0 (share-array ixn (shape 0 2) (lambda (k) (values k 0)))) (row0 (share-array ixn (shape 0 2) (lambda (k) (values 0 k)))) (wor1 (share-array ixn (shape 0 2) (lambda (k) (values 1 (- 1 k))))) (cod (share-array ixn (shape 0 2) (lambda (k) (case k ((0) (values 1 0)) ((1) (values 0 1)))))) (box (share-array ixn (shape 0 2) (lambda (k) (values 1 0))))) (and (eq? (array-ref arr col0) 'nw) (eq? (array-ref arr row0) 'ne) (eq? (array-ref arr wor1) 'nw) (eq? (array-ref arr cod) 'se) (eq? (array-ref arr box) 'sw) (begin (array-set! arr col0 'ul) (array-set! arr row0 'ur) (array-set! arr cod 'lr) (array-set! arr box 'll) #t) (eq? (array-ref arr 4 5) 'ul) (eq? (array-ref arr 4 6) 'ur) (eq? (array-ref arr 5 5) 'll) (eq? (array-ref arr 5 6) 'lr) (begin (array-set! arr wor1 'xx) (eq? (array-ref arr 4 5) 'xx))))) (error "array access with sharing index array failed")) (past "array access with sharing index array") ;;; Check that shape arrays work even when they share ;;; ;;; arr shp shq shr shs ;;; 1 2 3 4 0 1 0 1 0 1 0 1 ;;; 1 10 12 16 20 0 10 12 0 12 20 0 10 10 0 12 12 ;;; 2 10 11 12 13 1 10 11 1 11 13 1 11 12 1 12 12 ;;; 2 12 16 ;;; 3 13 20 (or (let ((arr (array (shape 1 3 1 5) 10 12 16 20 10 11 12 13))) (let ((shp (share-array arr (shape 0 2 0 2) (lambda (r k) (values (+ r 1) (+ k 1))))) (shq (share-array arr (shape 0 2 0 2) (lambda (r k) (values (+ r 1) (* 2 (+ 1 k)))))) (shr (share-array arr (shape 0 4 0 2) (lambda (r k) (values (- 2 k) (+ r 1))))) (shs (share-array arr (shape 0 2 0 2) (lambda (r k) (values 2 3))))) (and (let ((arr-p (make-array shp))) (and (= (array-rank arr-p) 2) (= (array-start arr-p 0) 10) (= (array-end arr-p 0) 12) (= (array-start arr-p 1) 10) (= (array-end arr-p 1) 11))) (let ((arr-q (array shq * * * * * * * * * * * * * * * *))) (and (= (array-rank arr-q) 2) (= (array-start arr-q 0) 12) (= (array-end arr-q 0) 20) (= (array-start arr-q 1) 11) (= (array-end arr-q 1) 13))) (let ((arr-r (share-array (array (shape) *) shr (lambda _ (values))))) (and (= (array-rank arr-r) 4) (= (array-start arr-r 0) 10) (= (array-end arr-r 0) 10) (= (array-start arr-r 1) 11) (= (array-end arr-r 1) 12) (= (array-start arr-r 2) 12) (= (array-end arr-r 2) 16) (= (array-start arr-r 3) 13) (= (array-end arr-r 3) 20))) (let ((arr-s (make-array shs))) (and (= (array-rank arr-s) 2) (= (array-start arr-s 0) 12) (= (array-end arr-s 0) 12) (= (array-start arr-s 1) 12) (= (array-end arr-s 1) 12)))))) (error "sharing shape array failed")) (past "sharing shape array") (let ((super (array (shape 4 7 4 7) 1 * * * 2 * * * 3)) (subshape (share-array (array (shape 0 2 0 3) * 4 * * 7 *) (shape 0 1 0 2) (lambda (r k) (values k 1))))) (let ((sub (share-array super subshape (lambda (k) (values k k))))) ;(array-equal? subshape (shape 4 7)) (or (and (= (array-rank subshape) 2) (= (array-start subshape 0) 0) (= (array-end subshape 0) 1) (= (array-start subshape 1) 0) (= (array-end subshape 1) 2) (= (array-ref subshape 0 0) 4) (= (array-ref subshape 0 1) 7)) (error "sharing subshape failed")) ;(array-equal? sub (array (shape 4 7) 1 2 3)) (or (and (= (array-rank sub) 1) (= (array-start sub 0) 4) (= (array-end sub 0) 7) (= (array-ref sub 4) 1) (= (array-ref sub 5) 2) (= (array-ref sub 6) 3)) (error "sharing with sharing subshape failed")))) (past "sharing with sharing subshape") |
Added srfi/s26/cut.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
; REFERENCE IMPLEMENTATION FOR SRFI-26 "CUT" ; ========================================== ; ; Sebastian.Egner@philips.com, 5-Jun-2002. ; adapted from the posting by Al Petrofsky <al@petrofsky.org> ; placed in the public domain ; ; The code to handle the variable argument case was originally ; proposed by Michael Sperber and has been adapted to the new ; syntax of the macro using an explicit rest-slot symbol. The ; code to evaluate the non-slots for cute has been proposed by ; Dale Jordan. The code to allow a slot for the procedure position ; and to process the macro using an internal macro is based on ; a suggestion by Al Petrofsky. The code found below is, with ; exception of this header and some changes in variable names, ; entirely written by Al Petrofsky. ; ; compliance: ; Scheme R5RS (including macros). ; ; loading this file into Scheme 48 0.57: ; ,load cut.scm ; ; history of this file: ; SE, 6-Feb-2002: initial version as 'curry' with ". <>" notation ; SE, 14-Feb-2002: revised for <...> ; SE, 27-Feb-2002: revised for 'cut' ; SE, 03-Jun-2002: revised for proc-slot, cute ; SE, 04-Jun-2002: rewritten with internal transformer (no "loop" pattern) ; SE, 05-Jun-2002: replace my code by Al's; substituted "constant" etc. ; to match the convention in the SRFI-document ; (srfi-26-internal-cut slot-names combination . se) ; transformer used internally ; slot-names : the internal names of the slots ; combination : procedure being specialized, followed by its arguments ; se : slots-or-exprs, the qualifiers of the macro (define-syntax srfi-26-internal-cut (syntax-rules (<> <...>) ;; construct fixed- or variable-arity procedure: ;; (begin proc) throws an error if proc is not an <expression> ((srfi-26-internal-cut (slot-name ...) (proc arg ...)) (lambda (slot-name ...) ((begin proc) arg ...))) ((srfi-26-internal-cut (slot-name ...) (proc arg ...) <...>) (lambda (slot-name ... . rest-slot) (apply proc arg ... rest-slot))) ;; process one slot-or-expr ((srfi-26-internal-cut (slot-name ...) (position ...) <> . se) (srfi-26-internal-cut (slot-name ... x) (position ... x) . se)) ((srfi-26-internal-cut (slot-name ...) (position ...) nse . se) (srfi-26-internal-cut (slot-name ...) (position ... nse) . se)))) ; (srfi-26-internal-cute slot-names nse-bindings combination . se) ; transformer used internally ; slot-names : the internal names of the slots ; nse-bindings : let-style bindings for the non-slot expressions. ; combination : procedure being specialized, followed by its arguments ; se : slots-or-exprs, the qualifiers of the macro (define-syntax srfi-26-internal-cute (syntax-rules (<> <...>) ;; If there are no slot-or-exprs to process, then: ;; construct a fixed-arity procedure, ((srfi-26-internal-cute (slot-name ...) nse-bindings (proc arg ...)) (let nse-bindings (lambda (slot-name ...) (proc arg ...)))) ;; or a variable-arity procedure ((srfi-26-internal-cute (slot-name ...) nse-bindings (proc arg ...) <...>) (let nse-bindings (lambda (slot-name ... . x) (apply proc arg ... x)))) ;; otherwise, process one slot: ((srfi-26-internal-cute (slot-name ...) nse-bindings (position ...) <> . se) (srfi-26-internal-cute (slot-name ... x) nse-bindings (position ... x) . se)) ;; or one non-slot expression ((srfi-26-internal-cute slot-names nse-bindings (position ...) nse . se) (srfi-26-internal-cute slot-names ((x nse) . nse-bindings) (position ... x) . se)))) ; exported syntax (define-syntax cut (syntax-rules () ((cut . slots-or-exprs) (srfi-26-internal-cut () () . slots-or-exprs)))) (define-syntax cute (syntax-rules () ((cute . slots-or-exprs) (srfi-26-internal-cute () () () . slots-or-exprs)))) |
Added srfi/s26/cut.sls.
> > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s26 cut) (export cut cute) (import (rnrs) (srfi private include)) (include/resolve ("srfi" "s26") "cut.scm") ) |
Added srfi/s27/random-bits.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s27 random-bits) (export random-integer random-real default-random-source make-random-source random-source? random-source-state-ref random-source-state-set! random-source-randomize! random-source-pseudo-randomize! random-source-make-integers random-source-make-reals) (import (rnrs) (rnrs r5rs) (only (srfi s19 time) time-nanosecond current-time) (srfi s23 error tricks) (srfi private include) ) (SRFI-23-error->R6RS "(library (srfi s27 random-bits))" (include/resolve ("srfi" "s27") "random.ss")) ) |
Added srfi/s27/random.ss.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 |
;; R6RS port of the Scheme48 reference implementation of SRFI-27 ; MODULE DEFINITION FOR SRFI-27 ; ============================= ; ; Sebastian.Egner@philips.com, Mar-2002, in Scheme 48 0.57 ; 1. The core generator is implemented in 'mrg32k3a-a.scm'. ; 2. The generic parts of the interface are in 'mrg32k3a.scm'. ; 3. The non-generic parts (record type, time, error) are here. ; history of this file: ; SE, 22-Mar-2002: initial version ; SE, 27-Mar-2002: checked again ; JS, 06-Dec-2007: R6RS port (define-record-type :random-source (fields state-ref state-set! randomize! pseudo-randomize! make-integers make-reals)) (define :random-source-make make-:random-source) (define state-ref :random-source-state-ref) (define state-set! :random-source-state-set!) (define randomize! :random-source-randomize!) (define pseudo-randomize! :random-source-pseudo-randomize!) (define make-integers :random-source-make-integers) (define make-reals :random-source-make-reals) (define (:random-source-current-time) (time-nanosecond (current-time))) ;;; mrg32k3a-a.ss ; 54-BIT INTEGER IMPLEMENTATION OF THE "MRG32K3A"-GENERATOR ; ========================================================= ; ; Sebastian.Egner@philips.com, Mar-2002. ; ; This file is an implementation of Pierre L'Ecuyer's MRG32k3a ; pseudo random number generator. Please refer to 'mrg32k3a.scm' ; for more information. ; ; compliance: ; Scheme R5RS with integers covering at least {-2^53..2^53-1}. ; ; history of this file: ; SE, 18-Mar-2002: initial version ; SE, 22-Mar-2002: comments adjusted, range added ; SE, 25-Mar-2002: pack/unpack just return their argument ; the actual generator (define (mrg32k3a-random-m1 state) (let ((x11 (vector-ref state 0)) (x12 (vector-ref state 1)) (x13 (vector-ref state 2)) (x21 (vector-ref state 3)) (x22 (vector-ref state 4)) (x23 (vector-ref state 5))) (let ((x10 (modulo (- (* 1403580 x12) (* 810728 x13)) 4294967087)) (x20 (modulo (- (* 527612 x21) (* 1370589 x23)) 4294944443))) (vector-set! state 0 x10) (vector-set! state 1 x11) (vector-set! state 2 x12) (vector-set! state 3 x20) (vector-set! state 4 x21) (vector-set! state 5 x22) (modulo (- x10 x20) 4294967087)))) ; interface to the generic parts of the generator (define (mrg32k3a-pack-state unpacked-state) unpacked-state) (define (mrg32k3a-unpack-state state) state) (define (mrg32k3a-random-range) ; m1 4294967087) (define (mrg32k3a-random-integer state range) ; rejection method (let* ((q (quotient 4294967087 range)) (qn (* q range))) (do ((x (mrg32k3a-random-m1 state) (mrg32k3a-random-m1 state))) ((< x qn) (quotient x q))))) (define (mrg32k3a-random-real state) ; normalization is 1/(m1+1) (* 0.0000000002328306549295728 (+ 1.0 (mrg32k3a-random-m1 state)))) ;;; mrg32k3a.ss ; GENERIC PART OF MRG32k3a-GENERATOR FOR SRFI-27 ; ============================================== ; ; Sebastian.Egner@philips.com, 2002. ; ; This is the generic R5RS-part of the implementation of the MRG32k3a ; generator to be used in SRFI-27. It is based on a separate implementation ; of the core generator (presumably in native code) and on code to ; provide essential functionality not available in R5RS (see below). ; ; compliance: ; Scheme R5RS with integer covering at least {-2^53..2^53-1}. ; In addition, ; SRFI-23: error ; ; history of this file: ; SE, 22-Mar-2002: refactored from earlier versions ; SE, 25-Mar-2002: pack/unpack need not allocate ; SE, 27-Mar-2002: changed interface to core generator ; SE, 10-Apr-2002: updated spec of mrg32k3a-random-integer ; Generator ; ========= ; ; Pierre L'Ecuyer's MRG32k3a generator is a Combined Multiple Recursive ; Generator. It produces the sequence {(x[1,n] - x[2,n]) mod m1 : n} ; defined by the two recursive generators ; ; x[1,n] = ( a12 x[1,n-2] + a13 x[1,n-3]) mod m1, ; x[2,n] = (a21 x[2,n-1] + a23 x[2,n-3]) mod m2, ; ; where the constants are ; m1 = 4294967087 = 2^32 - 209 modulus of 1st component ; m2 = 4294944443 = 2^32 - 22853 modulus of 2nd component ; a12 = 1403580 recursion coefficients ; a13 = -810728 ; a21 = 527612 ; a23 = -1370589 ; ; The generator passes all tests of G. Marsaglia's Diehard testsuite. ; Its period is (m1^3 - 1)(m2^3 - 1)/2 which is nearly 2^191. ; L'Ecuyer reports: "This generator is well-behaved in all dimensions ; up to at least 45: ..." [with respect to the spectral test, SE]. ; ; The period is maximal for all values of the seed as long as the ; state of both recursive generators is not entirely zero. ; ; As the successor state is a linear combination of previous ; states, it is possible to advance the generator by more than one ; iteration by applying a linear transformation. The following ; publication provides detailed information on how to do that: ; ; [1] P. L'Ecuyer, R. Simard, E. J. Chen, W. D. Kelton: ; An Object-Oriented Random-Number Package With Many Long ; Streams and Substreams. 2001. ; To appear in Operations Research. ; ; Arithmetics ; =========== ; ; The MRG32k3a generator produces values in {0..2^32-209-1}. All ; subexpressions of the actual generator fit into {-2^53..2^53-1}. ; The code below assumes that Scheme's "integer" covers this range. ; In addition, it is assumed that floating point literals can be ; read and there is some arithmetics with inexact numbers. ; ; However, for advancing the state of the generator by more than ; one step at a time, the full range {0..2^32-209-1} is needed. ; Required: Backbone Generator ; ============================ ; ; At this point in the code, the following procedures are assumed ; to be defined to execute the core generator: ; ; (mrg32k3a-pack-state unpacked-state) -> packed-state ; (mrg32k3a-unpack-state packed-state) -> unpacked-state ; pack/unpack a state of the generator. The core generator works ; on packed states, passed as an explicit argument, only. This ; allows native code implementations to store their state in a ; suitable form. Unpacked states are #(x10 x11 x12 x20 x21 x22) ; with integer x_ij. Pack/unpack need not allocate new objects ; in case packed and unpacked states are identical. ; ; (mrg32k3a-random-range) -> m-max ; (mrg32k3a-random-integer packed-state range) -> x in {0..range-1} ; advance the state of the generator and return the next random ; range-limited integer. ; Note that the state is not necessarily advanced by just one ; step because we use the rejection method to avoid any problems ; with distribution anomalies. ; The range argument must be an exact integer in {1..m-max}. ; It can be assumed that range is a fixnum if the Scheme system ; has such a number representation. ; ; (mrg32k3a-random-real packed-state) -> x in (0,1) ; advance the state of the generator and return the next random ; real number between zero and one (both excluded). The type of ; the result should be a flonum if possible. ; Required: Record Data Type ; ========================== ; ; At this point in the code, the following procedures are assumed ; to be defined to create and access a new record data type: ; ; (:random-source-make a0 a1 a2 a3 a4 a5) -> s ; constructs a new random source object s consisting of the ; objects a0 .. a5 in this order. ; ; (:random-source? obj) -> bool ; tests if a Scheme object is a :random-source. ; ; (:random-source-state-ref s) -> a0 ; (:random-source-state-set! s) -> a1 ; (:random-source-randomize! s) -> a2 ; (:random-source-pseudo-randomize! s) -> a3 ; (:random-source-make-integers s) -> a4 ; (:random-source-make-reals s) -> a5 ; retrieve the values in the fields of the object s. ; Required: Current Time as an Integer ; ==================================== ; ; At this point in the code, the following procedure is assumed ; to be defined to obtain a value that is likely to be different ; for each invokation of the Scheme system: ; ; (:random-source-current-time) -> x ; an integer that depends on the system clock. It is desired ; that the integer changes as fast as possible. ; Accessing the State ; =================== (define (mrg32k3a-state-ref packed-state) (cons 'lecuyer-mrg32k3a (vector->list (mrg32k3a-unpack-state packed-state)))) (define (mrg32k3a-state-set external-state) (define (check-value x m) (if (and (integer? x) (exact? x) (<= 0 x (- m 1))) #t (error "illegal value" x))) (if (and (list? external-state) (= (length external-state) 7) (eq? (car external-state) 'lecuyer-mrg32k3a)) (let ((s (cdr external-state))) (check-value (list-ref s 0) mrg32k3a-m1) (check-value (list-ref s 1) mrg32k3a-m1) (check-value (list-ref s 2) mrg32k3a-m1) (check-value (list-ref s 3) mrg32k3a-m2) (check-value (list-ref s 4) mrg32k3a-m2) (check-value (list-ref s 5) mrg32k3a-m2) (if (or (zero? (+ (list-ref s 0) (list-ref s 1) (list-ref s 2))) (zero? (+ (list-ref s 3) (list-ref s 4) (list-ref s 5)))) (error "illegal degenerate state" external-state)) (mrg32k3a-pack-state (list->vector s))) (error "malformed state" external-state))) ; Pseudo-Randomization ; ==================== ; ; Reference [1] above shows how to obtain many long streams and ; substream from the backbone generator. ; ; The idea is that the generator is a linear operation on the state. ; Hence, we can express this operation as a 3x3-matrix acting on the ; three most recent states. Raising the matrix to the k-th power, we ; obtain the operation to advance the state by k steps at once. The ; virtual streams and substreams are now simply parts of the entire ; periodic sequence (which has period around 2^191). ; ; For the implementation it is necessary to compute with matrices in ; the ring (Z/(m1*m1)*Z)^(3x3). By the Chinese-Remainder Theorem, this ; is isomorphic to ((Z/m1*Z) x (Z/m2*Z))^(3x3). We represent such a pair ; of matrices ; [ [[x00 x01 x02], ; [x10 x11 x12], ; [x20 x21 x22]], mod m1 ; [[y00 y01 y02], ; [y10 y11 y12], ; [y20 y21 y22]] mod m2] ; as a vector of length 18 of the integers as writen above: ; #(x00 x01 x02 x10 x11 x12 x20 x21 x22 ; y00 y01 y02 y10 y11 y12 y20 y21 y22) ; ; As the implementation should only use the range {-2^53..2^53-1}, the ; fundamental operation (x*y) mod m, where x, y, m are nearly 2^32, ; is computed by breaking up x and y as x = x1*w + x0 and y = y1*w + y0 ; where w = 2^16. In this case, all operations fit the range because ; w^2 mod m is a small number. If proper multiprecision integers are ; available this is not necessary, but pseudo-randomize! is an expected ; to be called only occasionally so we do not provide this implementation. (define mrg32k3a-m1 4294967087) ; modulus of component 1 (define mrg32k3a-m2 4294944443) ; modulus of component 2 (define mrg32k3a-initial-state ; 0 3 6 9 12 15 of A^16, see below '#( 1062452522 2961816100 342112271 2854655037 3321940838 3542344109)) (define mrg32k3a-generators #f) ; computed when needed (define (mrg32k3a-pseudo-randomize-state i j) (define (product A B) ; A*B in ((Z/m1*Z) x (Z/m2*Z))^(3x3) (define w 65536) ; wordsize to split {0..2^32-1} (define w-sqr1 209) ; w^2 mod m1 (define w-sqr2 22853) ; w^2 mod m2 (define (lc i0 i1 i2 j0 j1 j2 m w-sqr) ; linear combination (let ((a0h (quotient (vector-ref A i0) w)) (a0l (modulo (vector-ref A i0) w)) (a1h (quotient (vector-ref A i1) w)) (a1l (modulo (vector-ref A i1) w)) (a2h (quotient (vector-ref A i2) w)) (a2l (modulo (vector-ref A i2) w)) (b0h (quotient (vector-ref B j0) w)) (b0l (modulo (vector-ref B j0) w)) (b1h (quotient (vector-ref B j1) w)) (b1l (modulo (vector-ref B j1) w)) (b2h (quotient (vector-ref B j2) w)) (b2l (modulo (vector-ref B j2) w))) (modulo (+ (* (+ (* a0h b0h) (* a1h b1h) (* a2h b2h)) w-sqr) (* (+ (* a0h b0l) (* a0l b0h) (* a1h b1l) (* a1l b1h) (* a2h b2l) (* a2l b2h)) w) (* a0l b0l) (* a1l b1l) (* a2l b2l)) m))) (vector (lc 0 1 2 0 3 6 mrg32k3a-m1 w-sqr1) ; (A*B)_00 mod m1 (lc 0 1 2 1 4 7 mrg32k3a-m1 w-sqr1) ; (A*B)_01 (lc 0 1 2 2 5 8 mrg32k3a-m1 w-sqr1) (lc 3 4 5 0 3 6 mrg32k3a-m1 w-sqr1) ; (A*B)_10 (lc 3 4 5 1 4 7 mrg32k3a-m1 w-sqr1) (lc 3 4 5 2 5 8 mrg32k3a-m1 w-sqr1) (lc 6 7 8 0 3 6 mrg32k3a-m1 w-sqr1) (lc 6 7 8 1 4 7 mrg32k3a-m1 w-sqr1) (lc 6 7 8 2 5 8 mrg32k3a-m1 w-sqr1) (lc 9 10 11 9 12 15 mrg32k3a-m2 w-sqr2) ; (A*B)_00 mod m2 (lc 9 10 11 10 13 16 mrg32k3a-m2 w-sqr2) (lc 9 10 11 11 14 17 mrg32k3a-m2 w-sqr2) (lc 12 13 14 9 12 15 mrg32k3a-m2 w-sqr2) (lc 12 13 14 10 13 16 mrg32k3a-m2 w-sqr2) (lc 12 13 14 11 14 17 mrg32k3a-m2 w-sqr2) (lc 15 16 17 9 12 15 mrg32k3a-m2 w-sqr2) (lc 15 16 17 10 13 16 mrg32k3a-m2 w-sqr2) (lc 15 16 17 11 14 17 mrg32k3a-m2 w-sqr2))) (define (power A e) ; A^e (cond ((zero? e) '#(1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1)) ((= e 1) A) ((even? e) (power (product A A) (quotient e 2))) (else (product (power A (- e 1)) A)))) (define (power-power A b) ; A^(2^b) (if (zero? b) A (power-power (product A A) (- b 1)))) (define A ; the MRG32k3a recursion '#( 0 1403580 4294156359 1 0 0 0 1 0 527612 0 4293573854 1 0 0 0 1 0)) ; check arguments (if (not (and (integer? i) (exact? i) (integer? j) (exact? j))) (error "i j must be exact integer" i j)) ; precompute A^(2^127) and A^(2^76) only once (if (not mrg32k3a-generators) (set! mrg32k3a-generators (list (power-power A 127) (power-power A 76) (power A 16)))) ; compute M = A^(16 + i*2^127 + j*2^76) (let ((M (product (list-ref mrg32k3a-generators 2) (product (power (list-ref mrg32k3a-generators 0) (modulo i (expt 2 28))) (power (list-ref mrg32k3a-generators 1) (modulo j (expt 2 28))))))) (mrg32k3a-pack-state (vector (vector-ref M 0) (vector-ref M 3) (vector-ref M 6) (vector-ref M 9) (vector-ref M 12) (vector-ref M 15))))) ; True Randomization ; ================== ; ; The value obtained from the system time is feed into a very ; simple pseudo random number generator. This in turn is used ; to obtain numbers to randomize the state of the MRG32k3a ; generator, avoiding period degeneration. (define (mrg32k3a-randomize-state state) ;; G. Marsaglia's simple 16-bit generator with carry (let* ((m 65536) (x (modulo (:random-source-current-time) m))) (define (random-m) (let ((y (modulo x m))) (set! x (+ (* 30903 y) (quotient x m))) y)) (define (random n) ; m < n < m^2 (modulo (+ (* (random-m) m) (random-m)) n)) ; modify the state (let ((m1 mrg32k3a-m1) (m2 mrg32k3a-m2) (s (mrg32k3a-unpack-state state))) (mrg32k3a-pack-state (vector (+ 1 (modulo (+ (vector-ref s 0) (random (- m1 1))) (- m1 1))) (modulo (+ (vector-ref s 1) (random m1)) m1) (modulo (+ (vector-ref s 2) (random m1)) m1) (+ 1 (modulo (+ (vector-ref s 3) (random (- m2 1))) (- m2 1))) (modulo (+ (vector-ref s 4) (random m2)) m2) (modulo (+ (vector-ref s 5) (random m2)) m2)))))) ; Large Integers ; ============== ; ; To produce large integer random deviates, for n > m-max, we first ; construct large random numbers in the range {0..m-max^k-1} for some ; k such that m-max^k >= n and then use the rejection method to choose ; uniformly from the range {0..n-1}. (define mrg32k3a-m-max (mrg32k3a-random-range)) (define (mrg32k3a-random-power state k) ; n = m-max^k, k >= 1 (if (= k 1) (mrg32k3a-random-integer state mrg32k3a-m-max) (+ (* (mrg32k3a-random-power state (- k 1)) mrg32k3a-m-max) (mrg32k3a-random-integer state mrg32k3a-m-max)))) (define (mrg32k3a-random-large state n) ; n > m-max (do ((k 2 (+ k 1)) (mk (* mrg32k3a-m-max mrg32k3a-m-max) (* mk mrg32k3a-m-max))) ((>= mk n) (let* ((mk-by-n (quotient mk n)) (a (* mk-by-n n))) (do ((x (mrg32k3a-random-power state k) (mrg32k3a-random-power state k))) ((< x a) (quotient x mk-by-n))))))) ; Multiple Precision Reals ; ======================== ; ; To produce multiple precision reals we produce a large integer value ; and convert it into a real value. This value is then normalized. ; The precision goal is unit <= 1/(m^k + 1), or 1/unit - 1 <= m^k. ; If you know more about the floating point number types of the ; Scheme system, this can be improved. (define (mrg32k3a-random-real-mp state unit) (do ((k 1 (+ k 1)) (u (- (/ 1 unit) 1) (/ u mrg32k3a-m1))) ((<= u 1) (/ (exact->inexact (+ (mrg32k3a-random-power state k) 1)) (exact->inexact (+ (expt mrg32k3a-m-max k) 1)))))) ; Provide the Interface as Specified in the SRFI ; ============================================== ; ; An object of type random-source is a record containing the procedures ; as components. The actual state of the generator is stored in the ; binding-time environment of make-random-source. (define (make-random-source) (let ((state (mrg32k3a-pack-state ; make a new copy (list->vector (vector->list mrg32k3a-initial-state))))) (:random-source-make (lambda () (mrg32k3a-state-ref state)) (lambda (new-state) (set! state (mrg32k3a-state-set new-state))) (lambda () (set! state (mrg32k3a-randomize-state state))) (lambda (i j) (set! state (mrg32k3a-pseudo-randomize-state i j))) (lambda () (lambda (n) (cond ((not (and (integer? n) (exact? n) (positive? n))) (error "range must be exact positive integer" n)) ((<= n mrg32k3a-m-max) (mrg32k3a-random-integer state n)) (else (mrg32k3a-random-large state n))))) (lambda args (cond ((null? args) (lambda () (mrg32k3a-random-real state))) ((null? (cdr args)) (let ((unit (car args))) (cond ((not (and (real? unit) (< 0 unit 1))) (error "unit must be real in (0,1)" unit)) ((<= (- (/ 1 unit) 1) mrg32k3a-m1) (lambda () (mrg32k3a-random-real state))) (else (lambda () (mrg32k3a-random-real-mp state unit)))))) (else (error "illegal arguments" args))))))) (define random-source? :random-source?) (define (random-source-state-ref s) ((:random-source-state-ref s))) (define (random-source-state-set! s state) ((:random-source-state-set! s) state)) (define (random-source-randomize! s) ((:random-source-randomize! s))) (define (random-source-pseudo-randomize! s i j) ((:random-source-pseudo-randomize! s) i j)) ; --- (define (random-source-make-integers s) ((:random-source-make-integers s))) (define (random-source-make-reals s . unit) (apply (:random-source-make-reals s) unit)) ; --- (define default-random-source (make-random-source)) (define random-integer (random-source-make-integers default-random-source)) (define random-real (random-source-make-reals default-random-source)) |
Added srfi/s27/readme.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
REFERENCE IMPLEMENTATIONS FOR SRFI-27 "Sources of Random Bits" ============================================================== Sebastian.Egner@philips.com, 10-Apr-2002. Files ----- readme - this file mrg32k3a.scm - generic parts of P. L' Ecuyer's MRG32k3a PRGN mrg32k3a-a.scm - core generator in Scheme integers mrg32k3a-b.c - core generator in C doubles for Scheme 48 mrg32k3a-c.scm - core generator in Gambit [Scheme] flonums srfi-27-a.scm - Scheme 48 package definition for Scheme-only impl. srfi-27-b.scm - Scheme 48 package definition for C/Scheme impl. srfi-27-c.scm - Gambit definition for Scheme-only impl. conftest.scm - confidence tests for the implementation Implementations --------------- The implementation has been factored into three parts. One part implements the core generator, one part provides the more generic functionality as specified in the SRFI, and one part combines the parts and provides the interface as specified in the SRFI. a) A Scheme-only implementation for Scheme 48 0.57: srfi-27-a.scm mrg32k3a-a.scm mrg32k3a.scm This implementation uses 54-bit Scheme integers for all arithmetics of the generator. The result are Scheme integers and inexact Scheme numbers when floating point values are requested. The implementation is slow but tries to stay away from unportable features as much as possible. b) An implementation in Scheme 48 0.57 and ANSI-C: srfi-27-b.scm mrg32k3a-b.scm mrg32k3a.scm This is a more realistic implementation using C's (double) datatype for the core generator and 54-bit Scheme integers for the more infrequent operations on the state like the random-source-pseudo-randomize! operation. This implementation is meant as an example for a realistic native code implementation of the SRFI. Performance is good. c) A Scheme-only implementation for Gambit 3.0: srfi-27-c.scm mrg32k3a-c.scm mrg32k3a.scm This implementation uses Gambit's 64-bit flonums. It is entirely written in Scheme but uses a few special features of the Gambit system to tell the compiler. This implementation is meant as an example for a realistic Scheme implementation using flonums in Scheme and no C-code. Performance is good when the code is used in compiled form; the implementation has been optimized by Brad Lucier. This has resulted in a subtantial performance gain. |
Added srfi/s31/rec.sls.
> > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s31 rec) (export rec) (import (rnrs)) ;; Taken directly from the SRFI-31 (define-syntax rec (syntax-rules () [(rec (NAME . VARIABLES) . BODY) (letrec ( (NAME (lambda VARIABLES . BODY)) ) NAME)] [(rec NAME EXPRESSION) (letrec ( (NAME EXPRESSION) ) NAME)])) ) |
Added srfi/s37/args-fold.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s37 args-fold) (export args-fold (rename (make-option option)) option? option-names option-required-arg? option-optional-arg? option-processor) (import (rnrs) (srfi private include)) (define-record-type option (fields names required-arg? optional-arg? processor) (protocol (lambda (c) (lambda (n ra oa p) (if (and (and (list? n) (positive? (length n)) (for-all (lambda (x) (or (and (string? x) (positive? (string-length x))) (char? x))) n)) (boolean? ra) (boolean? oa) (not (and ra oa)) (procedure? p)) (c n ra oa p) (assertion-violation 'option "invalid arguments" n ra oa p)))))) (define args-fold (let ([option make-option]) (include/resolve ("srfi" "%3a37") "srfi-37-reference.scm") args-fold)) ) |
Added srfi/s37/srfi-37-reference.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
;;; args-fold.scm - a program argument processor ;;; ;;; Copyright (c) 2002 Anthony Carrico ;;; ;;; All rights reserved. ;;; ;;; Redistribution and use in source and binary forms, with or without ;;; modification, are permitted provided that the following conditions ;;; are met: ;;; 1. Redistributions of source code must retain the above copyright ;;; notice, this list of conditions and the following disclaimer. ;;; 2. Redistributions in binary form must reproduce the above copyright ;;; notice, this list of conditions and the following disclaimer in the ;;; documentation and/or other materials provided with the distribution. ;;; 3. The name of the authors may not be used to endorse or promote products ;;; derived from this software without specific prior written permission. ;;; ;;; THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR ;;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ;;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ;;; IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, ;;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ;;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ;;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ;;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ;;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ;;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ;;; NOTE: This implementation uses the following SRFIs: ;;; "SRFI 9: Defining Record Types" ;;; "SRFI 11: Syntax for receiving multiple values" ;;; ;;; NOTE: The scsh-utils and Chicken implementations use regular ;;; expressions. These might be easier to read and understand. #| (define option #f) (define option-names #f) (define option-required-arg? #f) (define option-optional-arg? #f) (define option-processor #f) (define option? #f) (let () (define-record-type option-type ($option names required-arg? optional-arg? processor) $option? (names $option-names) (required-arg? $option-required-arg?) (optional-arg? $option-optional-arg?) (processor $option-processor)) (set! option $option) (set! option-names $option-names) (set! option-required-arg? $option-required-arg?) (set! option-optional-arg? $option-optional-arg?) (set! option-processor $option-processor) (set! option? $option?)) |# (define args-fold (lambda (args options unrecognized-option-proc operand-proc . seeds) (letrec ((find (lambda (l ?) (cond ((null? l) #f) ((? (car l)) (car l)) (else (find (cdr l) ?))))) (find-option ;; ISSUE: This is a brute force search. Could use a table. (lambda (name) (find options (lambda (option) (find (option-names option) (lambda (test-name) (equal? name test-name))))))) (scan-short-options (lambda (index shorts args seeds) (if (= index (string-length shorts)) (scan-args args seeds) (let* ((name (string-ref shorts index)) (option (or (find-option name) (option (list name) #f #f unrecognized-option-proc)))) (cond ((and (< (+ index 1) (string-length shorts)) (or (option-required-arg? option) (option-optional-arg? option))) (let-values ((seeds (apply (option-processor option) option name (substring shorts (+ index 1) (string-length shorts)) seeds))) (scan-args args seeds))) ((and (option-required-arg? option) (pair? args)) (let-values ((seeds (apply (option-processor option) option name (car args) seeds))) (scan-args (cdr args) seeds))) (else (let-values ((seeds (apply (option-processor option) option name #f seeds))) (scan-short-options (+ index 1) shorts args seeds)))))))) (scan-operands (lambda (operands seeds) (if (null? operands) (apply values seeds) (let-values ((seeds (apply operand-proc (car operands) seeds))) (scan-operands (cdr operands) seeds))))) (scan-args (lambda (args seeds) (if (null? args) (apply values seeds) (let ((arg (car args)) (args (cdr args))) ;; NOTE: This string matching code would be simpler ;; using a regular expression matcher. (cond (;; (rx bos "--" eos) (string=? "--" arg) ;; End option scanning: (scan-operands args seeds)) (;;(rx bos ;; "--" ;; (submatch (+ (~ "="))) ;; "=" ;; (submatch (* any))) (and (> (string-length arg) 4) (char=? #\- (string-ref arg 0)) (char=? #\- (string-ref arg 1)) (not (char=? #\= (string-ref arg 2))) (let loop ((index 3)) (cond ((= index (string-length arg)) #f) ((char=? #\= (string-ref arg index)) index) (else (loop (+ 1 index)))))) ;; Found long option with arg: => (lambda (=-index) (let*-values (((name) (substring arg 2 =-index)) ((option-arg) (substring arg (+ =-index 1) (string-length arg))) ((option) (or (find-option name) (option (list name) #t #f unrecognized-option-proc))) (seeds (apply (option-processor option) option name option-arg seeds))) (scan-args args seeds)))) (;;(rx bos "--" (submatch (+ any))) (and (> (string-length arg) 3) (char=? #\- (string-ref arg 0)) (char=? #\- (string-ref arg 1))) ;; Found long option: (let* ((name (substring arg 2 (string-length arg))) (option (or (find-option name) (option (list name) #f #f unrecognized-option-proc)))) (if (and (option-required-arg? option) (pair? args)) (let-values ((seeds (apply (option-processor option) option name (car args) seeds))) (scan-args (cdr args) seeds)) (let-values ((seeds (apply (option-processor option) option name #f seeds))) (scan-args args seeds))))) (;; (rx bos "-" (submatch (+ any))) (and (> (string-length arg) 1) (char=? #\- (string-ref arg 0))) ;; Found short options (let ((shorts (substring arg 1 (string-length arg)))) (scan-short-options 0 shorts args seeds))) (else (let-values ((seeds (apply operand-proc arg seeds))) (scan-args args seeds))))))))) (scan-args args seeds)))) |
Added srfi/s38/with-shared-structure.chezscheme.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi s38 with-shared-structure) (export write-with-shared-structure (rename (write-with-shared-structure write/ss)) read-with-shared-structure (rename (read-with-shared-structure read/ss))) (import (rnrs) (only (chezscheme) print-graph parameterize)) (define write-with-shared-structure (case-lambda [(obj) (write-with-shared-structure obj (current-output-port))] [(obj port) (parameterize ([print-graph #t]) (write obj port))] [(obj port optarg) (assertion-violation 'write-with-shared-structure "this implementation does not support optarg")])) (define read-with-shared-structure read) ) |
Added srfi/s38/with-shared-structure.ikarus.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi s38 with-shared-structure) (export write-with-shared-structure (rename (write-with-shared-structure write/ss)) read-with-shared-structure (rename (read-with-shared-structure read/ss))) (import (rnrs) (only (ikarus) print-graph parameterize)) (define write-with-shared-structure (case-lambda [(obj) (write-with-shared-structure obj (current-output-port))] [(obj port) (parameterize ([print-graph #t]) (write obj port))] [(obj port optarg) (assertion-violation 'write-with-shared-structure "this implementation does not support optarg")])) (define read-with-shared-structure read) ) |
Added srfi/s38/with-shared-structure.larceny.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 |
;;; SRFI 38: External Representation of Data With Shared Structure ;;; ;;; $Id: %3a38.sls 6149 2009-03-19 02:41:56Z will $ ;;; ;;; See <http://srfi.schemers.org/srfi-38/srfi-38.html> for the full document. ;;; ;;; This file contains code that is copyrighted by two separate ;;; SRFI-style copyrights. ;;; ;;; The code for write-with-shared-structure is attributed to ;;; Al Petrovsky and copyrighted by Ray Dillinger. ;;; ;;; All other code was written by William D Clinger. (library (srfi s38 with-shared-structure) (export write-with-shared-structure write/ss read-with-shared-structure read/ss) (import (rnrs base) (rnrs unicode) (rnrs bytevectors) (only (rnrs lists) memq) (rnrs control) (only (rnrs io ports) port? textual-port?) (rnrs io simple) (rnrs hashtables) (rnrs mutable-strings) (rnrs mutable-pairs) (srfi s99 records procedural) (only (srfi s99 records inspection) record?)) ;;; Copyright (C) Ray Dillinger 2003. All Rights Reserved. ;;; ;;; This document and translations of it may be copied and furnished to ;;; others, and derivative works that comment on or otherwise explain it ;;; or assist in its implementation may be prepared, copied, published and ;;; distributed, in whole or in part, without restriction of any kind, ;;; provided that the above copyright notice and this paragraph are ;;; included on all such copies and derivative works. However, this ;;; document itself may not be modified in any way, such as by removing ;;; the copyright notice or references to the Scheme Request For ;;; Implementation process or editors, except as needed for the purpose of ;;; developing SRFIs in which case the procedures for copyrights defined ;;; in the SRFI process must be followed, or as required to translate it ;;; into languages other than English. ;;; ;;; The limited permissions granted above are perpetual and will not be ;;; revoked by the authors or their successors or assigns. ;;; ;;; This document and the information contained herein is provided on an ;;; "AS IS" basis and THE AUTHOR AND THE SRFI EDITORS DISCLAIM ALL ;;; WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY ;;; WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY ;;; RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A ;;; PARTICULAR PURPOSE. ;;; A printer that shows all sharing of substructures. Uses the Common ;;; Lisp print-circle notation: #n# refers to a previous substructure ;;; labeled with #n=. Takes O(n^2) time. ;;; Code attributed to Al* Petrofsky, modified by Dillinger. ;;; ;;; Modified December 2008 by Will Clinger to use R6RS-style hashtables ;;; and to recognize R6RS data structures. Now runs in O(n) time if ;;; the hashtable accesses are O(1). (define (write-with-shared-structure obj . optional-port) (define (lookup key state) (hashtable-ref state key #f)) (define (present? key state) (hashtable-contains? state key)) (define (updated-state key val state) (hashtable-set! state key val) state) ;; An object is interesting if it might have a mutable state. ;; An interesting object is especially interesting if it has ;; a standard external representation (according to SRFI 38) ;; that might contain the object itself. The interesting ;; objects described by the R5RS or R6RS are: ;; ;; pairs (especially interesting) ;; vectors (especially interesting) ;; strings ;; bytevectors ;; records ;; ports ;; hashtables ;; ;; We treat zero-length vectors, strings, and bytevectors ;; as uninteresting because they don't have a mutable state ;; and the reference implementation for SRFI 38 also treated ;; them as uninteresting. (define (interesting? obj) (or (pair? obj) (and (vector? obj) (not (zero? (vector-length obj)))) (and (string? obj) (not (zero? (string-length obj)))) (bytevector? obj) (record? obj) (port? obj) (hashtable? obj))) ;; The state has an entry for each interesting part of OBJ. The ;; associated value will be: ;; -- a number if the part has been given one, ;; -- #t if the part will need to be assigned a number but has not been yet, ;; -- #f if the part will not need a number. ;; The state also associates a symbol (counter) with the most ;; recently assigned number. ;; Returns a state with new entries for any parts that had ;; numbers assigned. (define (write-obj obj state outport) (define (write-interesting state) (cond ((pair? obj) (display "(" outport) (let write-cdr ((obj (cdr obj)) (state (write-obj (car obj) state outport))) (cond ((and (pair? obj) (not (lookup obj state))) (display " " outport) (write-cdr (cdr obj) (write-obj (car obj) state outport))) ((null? obj) (display ")" outport) state) (else (display " . " outport) (let ((state (write-obj obj state outport))) (display ")" outport) state))))) ((vector? obj) (display "#(" outport) (let ((len (vector-length obj))) (let write-vec ((i 1) (state (write-obj (vector-ref obj 0) state outport))) (cond ((= i len) (display ")" outport) state) (else (display " " outport) (write-vec (+ i 1) (write-obj (vector-ref obj i) state outport))))))) ;; else it's a string or something (else (write obj outport) state))) (cond ((interesting? obj) (let ((val (lookup obj state))) (cond ((not val) (write-interesting state)) ((number? val) (begin (display "#" outport) (write val outport) (display "#" outport) state)) (else (let* ((n (+ 1 (lookup 'counter state))) (state (updated-state 'counter n state))) (begin (display "#" outport) (write n outport) (display "=" outport)) (write-interesting (updated-state obj n state))))))) (else (write obj outport) state))) ;; Scan computes the initial value of the state, which maps each ;; interesting part of the object to #t if it occurs multiple times, ;; #f if only once. (define (scan obj state) (cond ((not (interesting? obj)) state) ((present? obj state) (updated-state obj #t state)) (else (let ((state (updated-state obj #f state))) (cond ((pair? obj) (scan (car obj) (scan (cdr obj) state))) ((vector? obj) (let ((len (vector-length obj))) (do ((i 0 (+ 1 i)) (state state (scan (vector-ref obj i) state))) ((= i len) state)))) (else state)))))) (let* ((state (make-eq-hashtable)) (state (scan obj state)) (state (updated-state 'counter 0 state)) (outport (if (eq? '() optional-port) (current-output-port) (car optional-port)))) (write-obj obj state outport) ;; We don't want to return the big state that write-obj just returned. (if #f #f))) ;;; Copyright (C) William D Clinger (2008). All Rights Reserved. ;;; ;;; Permission is hereby granted, free of charge, to any ;;; person obtaining a copy of this software and associated ;;; documentation files (the "Software"), to deal in the ;;; Software without restriction, including without ;;; limitation the rights to use, copy, modify, merge, ;;; publish, distribute, sublicense, and/or sell copies of ;;; the Software, and to permit persons to whom the Software ;;; is furnished to do so, subject to the following ;;; conditions: ;;; ;;; The above copyright notice and this permission notice ;;; shall be included in all copies or substantial portions ;;; of the Software. ;;; ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ;;; ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED ;;; TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A ;;; PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT ;;; SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ;;; CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION ;;; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR ;;; IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ;;; DEALINGS IN THE SOFTWARE. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; read-with-shared-structure ; ; Based on Clinger's reference implementation of get-datum. ; ; The scanner's state machine and the recursive descent parser ; were generated by Will Clinger's LexGen and ParseGen, so the ; parser can be extended or customized by regenerating those ; parts. ; ; LexGen and ParseGen are available at ; http://www.ccs.neu.edu/home/will/Research/SW2006/*.tar.gz ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Fixup objects are used to implement SRFI 38. (define rtd:fixup-object (make-rtd 'fixup-object '#((immutable index) (mutable ready) (mutable value)))) (define make-raw-fixup-object (rtd-constructor rtd:fixup-object)) (define (make-fixup-object index) (make-raw-fixup-object index #f #f)) (define fixup-object? (rtd-predicate rtd:fixup-object)) (define fixup-ready? (rtd-accessor rtd:fixup-object 'ready)) (define fixup-index (rtd-accessor rtd:fixup-object 'index)) (define fixup-value (rtd-accessor rtd:fixup-object 'value)) (define (fixup-ready! fixup obj) (raw-fixup-value! fixup obj) (raw-fixup-ready! fixup #t)) (define raw-fixup-ready! (rtd-mutator rtd:fixup-object 'ready)) (define raw-fixup-value! (rtd-mutator rtd:fixup-object 'value)) ; The exported entry point. (define (read-with-shared-structure . rest) (cond ((null? rest) (read-with-shared-structure-local (current-input-port))) ((and (null? (cdr rest)) (input-port? (car rest)) (textual-port? (car rest))) (read-with-shared-structure-local (car rest))) (else (assertion-violation 'read-with-shared-structure "illegal argument(s)" rest)))) (define (read-with-shared-structure-local input-port) ; Constants and local variables. (let* (; Constants. ; initial length of string_accumulator (initial_accumulator_length 64) ; Encodings of error messages. (errLongToken 1) ; extremely long token (errIncompleteToken 2) ; any lexical error, really (errIllegalHexEscape 3) ; illegal \x... (errIllegalNamedChar 4) ; illegal #\... (errIllegalString 5) ; illegal string (errIllegalSymbol 6) ; illegal symbol (errNoDelimiter 7) ; missing delimiter after token (errSRFI38 8) ; illegal #...# (errBug 9) ; bug in reader, shouldn't happen (errLexGenBug 10) ; can't happen ; Important but unnamed non-Ascii characters. (char:nel (integer->char #x85)) (char:ls (integer->char #x2028)) ; State for one-token buffering in lexical analyzer. (kindOfNextToken 'z1) ; valid iff nextTokenIsReady (nextTokenIsReady #f) (tokenValue "") ; string associated with current token ; A string buffer for the characters of the current token. ; Resized as necessary. (string_accumulator (make-string initial_accumulator_length)) ; Number of characters in string_accumulator. (string_accumulator_length 0) ; Hook for recording source locations. (locationStart #f) ; Hash table for SRFI 38, or #f. (shared-structures #f) ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Hand-coding scanner0 makes a small but worthwhile difference. ; ; The most common characters are spaces, parentheses, newlines, ; semicolons, and lower case Ascii letters. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Scanning for the start of a token. (define (scanner0) (define (loop c) (cond ((not (char? c)) (accept 'eofobj)) ((or (char=? c #\space) (char=? c #\newline)) (read-char input-port) (loop (peek-char input-port))) (else (state0 c)))) (loop (peek-char input-port))) ; Consuming a semicolon comment. (define (scanner1) (define (loop c) (cond ((not (char? c)) (accept 'eofobj)) ((char=? c #\newline) (scanner0)) (else (loop (read-char input-port))))) (loop (read-char input-port))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; LexGen generated the code for the state machine. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (state0 c) (case c ((#\`) (consumeChar) (accept 'backquote)) ((#\') (consumeChar) (accept 'quote)) ((#\]) (consumeChar) (accept 'rbracket)) ((#\[) (consumeChar) (accept 'lbracket)) ((#\)) (consumeChar) (accept 'rparen)) ((#\() (consumeChar) (accept 'lparen)) ((#\tab #\newline #\vtab #\page #\return #\space) (consumeChar) (begin (set! string_accumulator_length 0) (state0 (scanChar)))) ((#\;) (consumeChar) (state213 (scanChar))) ((#\#) (consumeChar) (state212 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state141 (scanChar))) ((#\a #\b #\c #\d #\e #\f #\g #\h #\i #\j #\k #\l #\m #\n #\o #\p #\q #\r #\s #\t #\u #\v #\w #\x #\y #\z #\A #\B #\C #\D #\E #\F #\G #\H #\I #\J #\K #\L #\M #\N #\O #\P #\Q #\R #\S #\T #\U #\V #\W #\X #\Y #\Z #\! #\$ #\% #\& #\* #\/ #\: #\< #\= #\> #\? #\^ #\_ #\~) (consumeChar) (state13 (scanChar))) ((#\\) (consumeChar) (state12 (scanChar))) ((#\-) (consumeChar) (state9 (scanChar))) ((#\+) (consumeChar) (state8 (scanChar))) ((#\.) (consumeChar) (state7 (scanChar))) ((#\") (consumeChar) (state5 (scanChar))) ((#\,) (consumeChar) (state1 (scanChar))) (else (if ((lambda (c) (and (char? c) (> (char->integer c) 127) (let ((cat (char-general-category c))) (memq cat '(Lu Ll Lt Lm Lo Mn Nl No Pd Pc Po Sc Sm Sk So Co))))) c) (begin (consumeChar) (state13 (scanChar))) (if (eof-object? c) (begin (consumeChar) (accept 'eofobj)) (if ((lambda (c) (and (char? c) (char-whitespace? c))) c) (begin (consumeChar) (begin (set! string_accumulator_length 0) (state0 (scanChar)))) (if ((lambda (c) (and (char? c) (char=? c (integer->char 133)))) c) (begin (consumeChar) (begin (set! string_accumulator_length 0) (state0 (scanChar)))) (scannerError errIncompleteToken)))))))) (define (state1 c) (case c ((#\@) (consumeChar) (accept 'splicing)) (else (accept 'comma)))) (define (state2 c) (case c ((#\") (consumeChar) (accept 'string)) ((#\newline #\return) (consumeChar) (state5 (scanChar))) ((#\\) (consumeChar) (state4 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F) (consumeChar) (state2 (scanChar))) (else (if (char? c) (begin (consumeChar) (state5 (scanChar))) (if ((lambda (c) (and (char? c) (char=? c (integer->char 8232)))) c) (begin (consumeChar) (state5 (scanChar))) (if ((lambda (c) (and (char? c) (char=? c (integer->char 133)))) c) (begin (consumeChar) (state5 (scanChar))) (scannerError errIncompleteToken))))))) (define (state3 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F) (consumeChar) (state2 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state4 c) (case c ((#\a #\b #\t #\n #\v #\f #\r #\" #\\ #\newline #\return #\space) (consumeChar) (state5 (scanChar))) ((#\x) (consumeChar) (state3 (scanChar))) (else (if ((lambda (c) (and (char? c) (char=? c (integer->char 8232)))) c) (begin (consumeChar) (state5 (scanChar))) (if ((lambda (c) (and (char? c) (char=? c (integer->char 133)))) c) (begin (consumeChar) (state5 (scanChar))) (scannerError errIncompleteToken)))))) (define (state5 c) (case c ((#\") (consumeChar) (accept 'string)) ((#\newline #\return) (consumeChar) (state5 (scanChar))) ((#\\) (consumeChar) (state4 (scanChar))) (else (if (char? c) (begin (consumeChar) (state5 (scanChar))) (if ((lambda (c) (and (char? c) (char=? c (integer->char 8232)))) c) (begin (consumeChar) (state5 (scanChar))) (if ((lambda (c) (and (char? c) (char=? c (integer->char 133)))) c) (begin (consumeChar) (state5 (scanChar))) (scannerError errIncompleteToken))))))) (define (state6 c) (case c ((#\.) (consumeChar) (accept 'id)) (else (scannerError errIncompleteToken)))) (define (state7 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state130 (scanChar))) ((#\.) (consumeChar) (state6 (scanChar))) (else (accept 'period)))) (define (state8 c) (case c ((#\I) (consumeChar) (accept 'number)) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state203 (scanChar))) ((#\.) (consumeChar) (state149 (scanChar))) ((#\n) (consumeChar) (state148 (scanChar))) ((#\i) (consumeChar) (state143 (scanChar))) (else (accept 'id)))) (define (state9 c) (case c ((#\I) (consumeChar) (accept 'number)) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state203 (scanChar))) ((#\.) (consumeChar) (state149 (scanChar))) ((#\n) (consumeChar) (state148 (scanChar))) ((#\i) (consumeChar) (state143 (scanChar))) ((#\>) (consumeChar) (state13 (scanChar))) (else (accept 'id)))) (define (state10 c) (case c ((#\;) (consumeChar) (state13 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F) (consumeChar) (state10 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state11 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F) (consumeChar) (state10 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state12 c) (case c ((#\x) (consumeChar) (state11 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state13 c) (case c ((#\a #\b #\c #\d #\e #\f #\g #\h #\i #\j #\k #\l #\m #\n #\o #\p #\q #\r #\s #\t #\u #\v #\w #\x #\y #\z #\A #\B #\C #\D #\E #\F #\G #\H #\I #\J #\K #\L #\M #\N #\O #\P #\Q #\R #\S #\T #\U #\V #\W #\X #\Y #\Z #\! #\$ #\% #\& #\* #\/ #\: #\< #\= #\> #\? #\^ #\_ #\~ #\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\+ #\- #\. #\@) (consumeChar) (state13 (scanChar))) ((#\\) (consumeChar) (state12 (scanChar))) (else (if ((lambda (c) (and (char? c) (let ((cat (char-general-category c))) (memq cat '(Nd Mc Me))))) c) (begin (consumeChar) (state13 (scanChar))) (if ((lambda (c) (and (char? c) (> (char->integer c) 127) (let ((cat (char-general-category c))) (memq cat '(Lu Ll Lt Lm Lo Mn Nl No Pd Pc Po Sc Sm Sk So Co))))) c) (begin (consumeChar) (state13 (scanChar))) (accept 'id)))))) (define (state14 c) (case c ((#\#) (consumeChar) (accept 'sharinguse)) ((#\=) (consumeChar) (accept 'sharingdef)) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state14 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state15 c) (case c ((#\@) (consumeChar) (accept 'unsyntaxsplicing)) (else (accept 'unsyntax)))) (define (state16 c) (case c ((#\() (consumeChar) (accept 'bvecstart)) (else (scannerError errIncompleteToken)))) (define (state17 c) (case c ((#\8) (consumeChar) (state16 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state18 c) (case c ((#\u) (consumeChar) (state17 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state19 c) (case c ((#\a #\b #\c #\d #\e #\f #\g #\h #\i #\j #\k #\l #\m #\n #\o #\p #\q #\r #\s #\t #\u #\v #\w #\x #\y #\z #\A #\B #\C #\D #\E #\F #\G #\H #\I #\J #\K #\L #\M #\N #\O #\P #\Q #\R #\S #\T #\U #\V #\W #\X #\Y #\Z) (consumeChar) (state19 (scanChar))) (else (accept 'character)))) (define (state20 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F) (consumeChar) (state20 (scanChar))) (else (accept 'character)))) (define (state21 c) (case c ((#\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F) (consumeChar) (state21 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state20 (scanChar))) ((#\g #\h #\i #\j #\k #\l #\m #\n #\o #\p #\q #\r #\s #\t #\u #\v #\w #\x #\y #\z #\G #\H #\I #\J #\K #\L #\M #\N #\O #\P #\Q #\R #\S #\T #\U #\V #\W #\X #\Y #\Z) (consumeChar) (state19 (scanChar))) (else (accept 'character)))) (define (state22 c) (case c ((#\x) (consumeChar) (state21 (scanChar))) ((#\a #\b #\c #\d #\e #\f #\g #\h #\i #\j #\k #\l #\m #\n #\o #\p #\q #\r #\s #\t #\u #\v #\w #\y #\z #\A #\B #\C #\D #\E #\F #\G #\H #\I #\J #\K #\L #\M #\N #\O #\P #\Q #\R #\S #\T #\U #\V #\W #\X #\Y #\Z) (consumeChar) (state19 (scanChar))) (else (if (char? c) (begin (consumeChar) (accept 'character)) (scannerError errIncompleteToken))))) (define (state23 c) (case c ((#\i #\I #\e #\E) (consumeChar) (state58 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state24 c) (case c ((#\+ #\-) (consumeChar) (state57 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F) (consumeChar) (state33 (scanChar))) ((#\#) (consumeChar) (state23 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state25 c) (case c ((#\i #\I #\e #\E) (consumeChar) (state88 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state26 c) (case c ((#\+ #\-) (consumeChar) (state87 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7) (consumeChar) (state63 (scanChar))) ((#\#) (consumeChar) (state25 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state27 c) (case c ((#\i #\I #\e #\E) (consumeChar) (state126 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state28 c) (case c ((#\+ #\-) (consumeChar) (state125 (scanChar))) ((#\0 #\1) (consumeChar) (state93 (scanChar))) ((#\#) (consumeChar) (state27 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state29 c) (case c ((#\@) (consumeChar) (state51 (scanChar))) ((#\+ #\-) (consumeChar) (state45 (scanChar))) ((#\#) (consumeChar) (state29 (scanChar))) (else (accept 'number)))) (define (state30 c) (case c ((#\@) (consumeChar) (state51 (scanChar))) ((#\+ #\-) (consumeChar) (state45 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F) (consumeChar) (state30 (scanChar))) ((#\#) (consumeChar) (state29 (scanChar))) (else (accept 'number)))) (define (state31 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F) (consumeChar) (state30 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state32 c) (case c ((#\@) (consumeChar) (state51 (scanChar))) ((#\+ #\-) (consumeChar) (state45 (scanChar))) ((#\#) (consumeChar) (state32 (scanChar))) ((#\/) (consumeChar) (state31 (scanChar))) (else (accept 'number)))) (define (state33 c) (case c ((#\@) (consumeChar) (state51 (scanChar))) ((#\+ #\-) (consumeChar) (state45 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F) (consumeChar) (state33 (scanChar))) ((#\#) (consumeChar) (state32 (scanChar))) ((#\/) (consumeChar) (state31 (scanChar))) (else (accept 'number)))) (define (state34 c) (case c ((#\f) (consumeChar) (state38 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state35 c) (case c ((#\n) (consumeChar) (state34 (scanChar))) (else (accept 'number)))) (define (state36 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\@) (consumeChar) (state51 (scanChar))) ((#\+ #\-) (consumeChar) (state45 (scanChar))) (else (accept 'number)))) (define (state37 c) (case c ((#\0) (consumeChar) (state36 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state38 c) (case c ((#\.) (consumeChar) (state37 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state39 c) (case c ((#\n) (consumeChar) (state38 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state40 c) (case c ((#\a) (consumeChar) (state39 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state41 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\#) (consumeChar) (state108 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F) (consumeChar) (state41 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state42 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F) (consumeChar) (state41 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state43 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\#) (consumeChar) (state43 (scanChar))) ((#\/) (consumeChar) (state42 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state44 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F) (consumeChar) (state44 (scanChar))) ((#\#) (consumeChar) (state43 (scanChar))) ((#\/) (consumeChar) (state42 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state45 c) (case c ((#\n) (consumeChar) (state107 (scanChar))) ((#\i) (consumeChar) (state102 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F) (consumeChar) (state44 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state46 c) (case c ((#\#) (consumeChar) (state192 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F) (consumeChar) (state46 (scanChar))) (else (accept 'number)))) (define (state47 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F) (consumeChar) (state46 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state48 c) (case c ((#\#) (consumeChar) (state48 (scanChar))) ((#\/) (consumeChar) (state47 (scanChar))) (else (accept 'number)))) (define (state49 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F) (consumeChar) (state49 (scanChar))) ((#\#) (consumeChar) (state48 (scanChar))) ((#\/) (consumeChar) (state47 (scanChar))) (else (accept 'number)))) (define (state50 c) (case c ((#\n) (consumeChar) (state183 (scanChar))) ((#\i) (consumeChar) (state179 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F) (consumeChar) (state49 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state51 c) (case c ((#\+ #\-) (consumeChar) (state50 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F) (consumeChar) (state49 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state52 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\#) (consumeChar) (state52 (scanChar))) ((#\@) (consumeChar) (state51 (scanChar))) ((#\+ #\-) (consumeChar) (state45 (scanChar))) (else (accept 'number)))) (define (state53 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F) (consumeChar) (state53 (scanChar))) ((#\#) (consumeChar) (state52 (scanChar))) ((#\@) (consumeChar) (state51 (scanChar))) ((#\+ #\-) (consumeChar) (state45 (scanChar))) (else (accept 'number)))) (define (state54 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F) (consumeChar) (state53 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state55 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\#) (consumeChar) (state55 (scanChar))) ((#\/) (consumeChar) (state54 (scanChar))) ((#\@) (consumeChar) (state51 (scanChar))) ((#\+ #\-) (consumeChar) (state45 (scanChar))) (else (accept 'number)))) (define (state56 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F) (consumeChar) (state56 (scanChar))) ((#\#) (consumeChar) (state55 (scanChar))) ((#\/) (consumeChar) (state54 (scanChar))) ((#\@) (consumeChar) (state51 (scanChar))) ((#\+ #\-) (consumeChar) (state45 (scanChar))) (else (accept 'number)))) (define (state57 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F) (consumeChar) (state56 (scanChar))) ((#\n) (consumeChar) (state40 (scanChar))) ((#\i) (consumeChar) (state35 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state58 c) (case c ((#\+ #\-) (consumeChar) (state57 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F) (consumeChar) (state33 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state59 c) (case c ((#\@) (consumeChar) (state81 (scanChar))) ((#\+ #\-) (consumeChar) (state75 (scanChar))) ((#\#) (consumeChar) (state59 (scanChar))) (else (accept 'number)))) (define (state60 c) (case c ((#\@) (consumeChar) (state81 (scanChar))) ((#\+ #\-) (consumeChar) (state75 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7) (consumeChar) (state60 (scanChar))) ((#\#) (consumeChar) (state59 (scanChar))) (else (accept 'number)))) (define (state61 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7) (consumeChar) (state60 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state62 c) (case c ((#\@) (consumeChar) (state81 (scanChar))) ((#\+ #\-) (consumeChar) (state75 (scanChar))) ((#\#) (consumeChar) (state62 (scanChar))) ((#\/) (consumeChar) (state61 (scanChar))) (else (accept 'number)))) (define (state63 c) (case c ((#\@) (consumeChar) (state81 (scanChar))) ((#\+ #\-) (consumeChar) (state75 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7) (consumeChar) (state63 (scanChar))) ((#\#) (consumeChar) (state62 (scanChar))) ((#\/) (consumeChar) (state61 (scanChar))) (else (accept 'number)))) (define (state64 c) (case c ((#\f) (consumeChar) (state68 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state65 c) (case c ((#\n) (consumeChar) (state64 (scanChar))) (else (accept 'number)))) (define (state66 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\@) (consumeChar) (state81 (scanChar))) ((#\+ #\-) (consumeChar) (state75 (scanChar))) (else (accept 'number)))) (define (state67 c) (case c ((#\0) (consumeChar) (state66 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state68 c) (case c ((#\.) (consumeChar) (state67 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state69 c) (case c ((#\n) (consumeChar) (state68 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state70 c) (case c ((#\a) (consumeChar) (state69 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state71 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\#) (consumeChar) (state108 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7) (consumeChar) (state71 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state72 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7) (consumeChar) (state71 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state73 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\#) (consumeChar) (state73 (scanChar))) ((#\/) (consumeChar) (state72 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state74 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7) (consumeChar) (state74 (scanChar))) ((#\#) (consumeChar) (state73 (scanChar))) ((#\/) (consumeChar) (state72 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state75 c) (case c ((#\n) (consumeChar) (state107 (scanChar))) ((#\i) (consumeChar) (state102 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7) (consumeChar) (state74 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state76 c) (case c ((#\#) (consumeChar) (state192 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7) (consumeChar) (state76 (scanChar))) (else (accept 'number)))) (define (state77 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7) (consumeChar) (state76 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state78 c) (case c ((#\#) (consumeChar) (state78 (scanChar))) ((#\/) (consumeChar) (state77 (scanChar))) (else (accept 'number)))) (define (state79 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7) (consumeChar) (state79 (scanChar))) ((#\#) (consumeChar) (state78 (scanChar))) ((#\/) (consumeChar) (state77 (scanChar))) (else (accept 'number)))) (define (state80 c) (case c ((#\n) (consumeChar) (state183 (scanChar))) ((#\i) (consumeChar) (state179 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7) (consumeChar) (state79 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state81 c) (case c ((#\+ #\-) (consumeChar) (state80 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7) (consumeChar) (state79 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state82 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\#) (consumeChar) (state82 (scanChar))) ((#\@) (consumeChar) (state81 (scanChar))) ((#\+ #\-) (consumeChar) (state75 (scanChar))) (else (accept 'number)))) (define (state83 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7) (consumeChar) (state83 (scanChar))) ((#\#) (consumeChar) (state82 (scanChar))) ((#\@) (consumeChar) (state81 (scanChar))) ((#\+ #\-) (consumeChar) (state75 (scanChar))) (else (accept 'number)))) (define (state84 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7) (consumeChar) (state83 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state85 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\#) (consumeChar) (state85 (scanChar))) ((#\/) (consumeChar) (state84 (scanChar))) ((#\@) (consumeChar) (state81 (scanChar))) ((#\+ #\-) (consumeChar) (state75 (scanChar))) (else (accept 'number)))) (define (state86 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7) (consumeChar) (state86 (scanChar))) ((#\#) (consumeChar) (state85 (scanChar))) ((#\/) (consumeChar) (state84 (scanChar))) ((#\@) (consumeChar) (state81 (scanChar))) ((#\+ #\-) (consumeChar) (state75 (scanChar))) (else (accept 'number)))) (define (state87 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7) (consumeChar) (state86 (scanChar))) ((#\n) (consumeChar) (state70 (scanChar))) ((#\i) (consumeChar) (state65 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state88 c) (case c ((#\+ #\-) (consumeChar) (state87 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7) (consumeChar) (state63 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state89 c) (case c ((#\@) (consumeChar) (state119 (scanChar))) ((#\+ #\-) (consumeChar) (state113 (scanChar))) ((#\#) (consumeChar) (state89 (scanChar))) (else (accept 'number)))) (define (state90 c) (case c ((#\@) (consumeChar) (state119 (scanChar))) ((#\+ #\-) (consumeChar) (state113 (scanChar))) ((#\0 #\1) (consumeChar) (state90 (scanChar))) ((#\#) (consumeChar) (state89 (scanChar))) (else (accept 'number)))) (define (state91 c) (case c ((#\0 #\1) (consumeChar) (state90 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state92 c) (case c ((#\@) (consumeChar) (state119 (scanChar))) ((#\+ #\-) (consumeChar) (state113 (scanChar))) ((#\#) (consumeChar) (state92 (scanChar))) ((#\/) (consumeChar) (state91 (scanChar))) (else (accept 'number)))) (define (state93 c) (case c ((#\@) (consumeChar) (state119 (scanChar))) ((#\+ #\-) (consumeChar) (state113 (scanChar))) ((#\0 #\1) (consumeChar) (state93 (scanChar))) ((#\#) (consumeChar) (state92 (scanChar))) ((#\/) (consumeChar) (state91 (scanChar))) (else (accept 'number)))) (define (state94 c) (case c ((#\f) (consumeChar) (state98 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state95 c) (case c ((#\n) (consumeChar) (state94 (scanChar))) (else (accept 'number)))) (define (state96 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\@) (consumeChar) (state119 (scanChar))) ((#\+ #\-) (consumeChar) (state113 (scanChar))) (else (accept 'number)))) (define (state97 c) (case c ((#\0) (consumeChar) (state96 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state98 c) (case c ((#\.) (consumeChar) (state97 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state99 c) (case c ((#\n) (consumeChar) (state98 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state100 c) (case c ((#\a) (consumeChar) (state99 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state101 c) (case c ((#\f) (consumeChar) (state105 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state102 c) (case c ((#\n) (consumeChar) (state101 (scanChar))) (else (accept 'number)))) (define (state103 c) (case c ((#\i) (consumeChar) (accept 'number)) (else (scannerError errIncompleteToken)))) (define (state104 c) (case c ((#\0) (consumeChar) (state103 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state105 c) (case c ((#\.) (consumeChar) (state104 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state106 c) (case c ((#\n) (consumeChar) (state105 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state107 c) (case c ((#\a) (consumeChar) (state106 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state108 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\#) (consumeChar) (state108 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state109 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\0 #\1) (consumeChar) (state109 (scanChar))) ((#\#) (consumeChar) (state108 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state110 c) (case c ((#\0 #\1) (consumeChar) (state109 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state111 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\#) (consumeChar) (state111 (scanChar))) ((#\/) (consumeChar) (state110 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state112 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\0 #\1) (consumeChar) (state112 (scanChar))) ((#\#) (consumeChar) (state111 (scanChar))) ((#\/) (consumeChar) (state110 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state113 c) (case c ((#\0 #\1) (consumeChar) (state112 (scanChar))) ((#\n) (consumeChar) (state107 (scanChar))) ((#\i) (consumeChar) (state102 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state114 c) (case c ((#\#) (consumeChar) (state192 (scanChar))) ((#\0 #\1) (consumeChar) (state114 (scanChar))) (else (accept 'number)))) (define (state115 c) (case c ((#\0 #\1) (consumeChar) (state114 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state116 c) (case c ((#\#) (consumeChar) (state116 (scanChar))) ((#\/) (consumeChar) (state115 (scanChar))) (else (accept 'number)))) (define (state117 c) (case c ((#\0 #\1) (consumeChar) (state117 (scanChar))) ((#\#) (consumeChar) (state116 (scanChar))) ((#\/) (consumeChar) (state115 (scanChar))) (else (accept 'number)))) (define (state118 c) (case c ((#\n) (consumeChar) (state183 (scanChar))) ((#\i) (consumeChar) (state179 (scanChar))) ((#\0 #\1) (consumeChar) (state117 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state119 c) (case c ((#\+ #\-) (consumeChar) (state118 (scanChar))) ((#\0 #\1) (consumeChar) (state117 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state120 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\#) (consumeChar) (state120 (scanChar))) ((#\@) (consumeChar) (state119 (scanChar))) ((#\+ #\-) (consumeChar) (state113 (scanChar))) (else (accept 'number)))) (define (state121 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\0 #\1) (consumeChar) (state121 (scanChar))) ((#\#) (consumeChar) (state120 (scanChar))) ((#\@) (consumeChar) (state119 (scanChar))) ((#\+ #\-) (consumeChar) (state113 (scanChar))) (else (accept 'number)))) (define (state122 c) (case c ((#\0 #\1) (consumeChar) (state121 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state123 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\#) (consumeChar) (state123 (scanChar))) ((#\/) (consumeChar) (state122 (scanChar))) ((#\@) (consumeChar) (state119 (scanChar))) ((#\+ #\-) (consumeChar) (state113 (scanChar))) (else (accept 'number)))) (define (state124 c) (case c ((#\i) (consumeChar) (accept 'number)) ((#\0 #\1) (consumeChar) (state124 (scanChar))) ((#\#) (consumeChar) (state123 (scanChar))) ((#\/) (consumeChar) (state122 (scanChar))) ((#\@) (consumeChar) (state119 (scanChar))) ((#\+ #\-) (consumeChar) (state113 (scanChar))) (else (accept 'number)))) (define (state125 c) (case c ((#\0 #\1) (consumeChar) (state124 (scanChar))) ((#\n) (consumeChar) (state100 (scanChar))) ((#\i) (consumeChar) (state95 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state126 c) (case c ((#\+ #\-) (consumeChar) (state125 (scanChar))) ((#\0 #\1) (consumeChar) (state93 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state127 c) (case c ((#\d #\D) (consumeChar) (state205 (scanChar))) ((#\b #\B) (consumeChar) (state126 (scanChar))) ((#\o #\O) (consumeChar) (state88 (scanChar))) ((#\x #\X) (consumeChar) (state58 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state128 c) (case c ((#\+ #\-) (consumeChar) (state204 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state141 (scanChar))) ((#\.) (consumeChar) (state129 (scanChar))) ((#\#) (consumeChar) (state127 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state129 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state130 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state130 c) (case c ((#\@) (consumeChar) (state198 (scanChar))) ((#\+ #\-) (consumeChar) (state177 (scanChar))) ((#\#) (consumeChar) (state136 (scanChar))) ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L) (consumeChar) (state135 (scanChar))) ((#\|) (consumeChar) (state132 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state130 (scanChar))) (else (accept 'number)))) (define (state131 c) (case c ((#\@) (consumeChar) (state198 (scanChar))) ((#\+ #\-) (consumeChar) (state177 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state131 (scanChar))) (else (accept 'number)))) (define (state132 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state131 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state133 c) (case c ((#\@) (consumeChar) (state198 (scanChar))) ((#\+ #\-) (consumeChar) (state177 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state133 (scanChar))) ((#\|) (consumeChar) (state132 (scanChar))) (else (accept 'number)))) (define (state134 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state133 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state135 c) (case c ((#\+ #\-) (consumeChar) (state134 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state133 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state136 c) (case c ((#\@) (consumeChar) (state198 (scanChar))) ((#\+ #\-) (consumeChar) (state177 (scanChar))) ((#\#) (consumeChar) (state136 (scanChar))) ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L) (consumeChar) (state135 (scanChar))) ((#\|) (consumeChar) (state132 (scanChar))) (else (accept 'number)))) (define (state137 c) (case c ((#\@) (consumeChar) (state198 (scanChar))) ((#\+ #\-) (consumeChar) (state177 (scanChar))) ((#\#) (consumeChar) (state137 (scanChar))) (else (accept 'number)))) (define (state138 c) (case c ((#\@) (consumeChar) (state198 (scanChar))) ((#\+ #\-) (consumeChar) (state177 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state138 (scanChar))) ((#\#) (consumeChar) (state137 (scanChar))) (else (accept 'number)))) (define (state139 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state138 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state140 c) (case c ((#\@) (consumeChar) (state198 (scanChar))) ((#\+ #\-) (consumeChar) (state177 (scanChar))) ((#\#) (consumeChar) (state140 (scanChar))) ((#\/) (consumeChar) (state139 (scanChar))) ((#\.) (consumeChar) (state136 (scanChar))) ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L) (consumeChar) (state135 (scanChar))) ((#\|) (consumeChar) (state132 (scanChar))) (else (accept 'number)))) (define (state141 c) (case c ((#\@) (consumeChar) (state198 (scanChar))) ((#\+ #\-) (consumeChar) (state177 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state141 (scanChar))) ((#\#) (consumeChar) (state140 (scanChar))) ((#\/) (consumeChar) (state139 (scanChar))) ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L) (consumeChar) (state135 (scanChar))) ((#\|) (consumeChar) (state132 (scanChar))) ((#\.) (consumeChar) (state130 (scanChar))) (else (accept 'number)))) (define (state142 c) (case c ((#\f) (consumeChar) (state146 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state143 c) (case c ((#\n) (consumeChar) (state142 (scanChar))) (else (accept 'number)))) (define (state144 c) (case c ((#\i #\I) (consumeChar) (accept 'number)) ((#\@) (consumeChar) (state198 (scanChar))) ((#\+ #\-) (consumeChar) (state177 (scanChar))) (else (accept 'number)))) (define (state145 c) (case c ((#\0) (consumeChar) (state144 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state146 c) (case c ((#\.) (consumeChar) (state145 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state147 c) (case c ((#\n) (consumeChar) (state146 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state148 c) (case c ((#\a) (consumeChar) (state147 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state149 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state150 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state150 c) (case c ((#\i #\I) (consumeChar) (accept 'number)) ((#\@) (consumeChar) (state198 (scanChar))) ((#\+ #\-) (consumeChar) (state177 (scanChar))) ((#\#) (consumeChar) (state156 (scanChar))) ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L) (consumeChar) (state155 (scanChar))) ((#\|) (consumeChar) (state152 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state150 (scanChar))) (else (accept 'number)))) (define (state151 c) (case c ((#\i #\I) (consumeChar) (accept 'number)) ((#\@) (consumeChar) (state198 (scanChar))) ((#\+ #\-) (consumeChar) (state177 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state151 (scanChar))) (else (accept 'number)))) (define (state152 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state151 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state153 c) (case c ((#\i #\I) (consumeChar) (accept 'number)) ((#\@) (consumeChar) (state198 (scanChar))) ((#\+ #\-) (consumeChar) (state177 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state153 (scanChar))) ((#\|) (consumeChar) (state152 (scanChar))) (else (accept 'number)))) (define (state154 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state153 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state155 c) (case c ((#\+ #\-) (consumeChar) (state154 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state153 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state156 c) (case c ((#\i #\I) (consumeChar) (accept 'number)) ((#\@) (consumeChar) (state198 (scanChar))) ((#\+ #\-) (consumeChar) (state177 (scanChar))) ((#\#) (consumeChar) (state156 (scanChar))) ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L) (consumeChar) (state155 (scanChar))) ((#\|) (consumeChar) (state152 (scanChar))) (else (accept 'number)))) (define (state157 c) (case c ((#\f) (consumeChar) (state161 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state158 c) (case c ((#\n) (consumeChar) (state157 (scanChar))) (else (accept 'number)))) (define (state159 c) (case c ((#\i #\I) (consumeChar) (accept 'number)) (else (scannerError errIncompleteToken)))) (define (state160 c) (case c ((#\0) (consumeChar) (state159 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state161 c) (case c ((#\.) (consumeChar) (state160 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state162 c) (case c ((#\n) (consumeChar) (state161 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state163 c) (case c ((#\a) (consumeChar) (state162 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state164 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state165 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state165 c) (case c ((#\i #\I) (consumeChar) (accept 'number)) ((#\#) (consumeChar) (state171 (scanChar))) ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L) (consumeChar) (state170 (scanChar))) ((#\|) (consumeChar) (state167 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state165 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state166 c) (case c ((#\i #\I) (consumeChar) (accept 'number)) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state166 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state167 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state166 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state168 c) (case c ((#\i #\I) (consumeChar) (accept 'number)) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state168 (scanChar))) ((#\|) (consumeChar) (state167 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state169 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state168 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state170 c) (case c ((#\+ #\-) (consumeChar) (state169 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state168 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state171 c) (case c ((#\i #\I) (consumeChar) (accept 'number)) ((#\#) (consumeChar) (state171 (scanChar))) ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L) (consumeChar) (state170 (scanChar))) ((#\|) (consumeChar) (state167 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state172 c) (case c ((#\i #\I) (consumeChar) (accept 'number)) ((#\#) (consumeChar) (state172 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state173 c) (case c ((#\i #\I) (consumeChar) (accept 'number)) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state173 (scanChar))) ((#\#) (consumeChar) (state172 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state174 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state173 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state175 c) (case c ((#\i #\I) (consumeChar) (accept 'number)) ((#\#) (consumeChar) (state175 (scanChar))) ((#\/) (consumeChar) (state174 (scanChar))) ((#\.) (consumeChar) (state171 (scanChar))) ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L) (consumeChar) (state170 (scanChar))) ((#\|) (consumeChar) (state167 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state176 c) (case c ((#\i #\I) (consumeChar) (accept 'number)) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state176 (scanChar))) ((#\#) (consumeChar) (state175 (scanChar))) ((#\/) (consumeChar) (state174 (scanChar))) ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L) (consumeChar) (state170 (scanChar))) ((#\|) (consumeChar) (state167 (scanChar))) ((#\.) (consumeChar) (state165 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state177 c) (case c ((#\I) (consumeChar) (accept 'number)) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state176 (scanChar))) ((#\.) (consumeChar) (state164 (scanChar))) ((#\n) (consumeChar) (state163 (scanChar))) ((#\i) (consumeChar) (state158 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state178 c) (case c ((#\f) (consumeChar) (state181 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state179 c) (case c ((#\n) (consumeChar) (state178 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state180 c) (case c ((#\0) (consumeChar) (accept 'number)) (else (scannerError errIncompleteToken)))) (define (state181 c) (case c ((#\.) (consumeChar) (state180 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state182 c) (case c ((#\n) (consumeChar) (state181 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state183 c) (case c ((#\a) (consumeChar) (state182 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state184 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state185 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state185 c) (case c ((#\#) (consumeChar) (state191 (scanChar))) ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L) (consumeChar) (state190 (scanChar))) ((#\|) (consumeChar) (state187 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state185 (scanChar))) (else (accept 'number)))) (define (state186 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state186 (scanChar))) (else (accept 'number)))) (define (state187 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state186 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state188 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state188 (scanChar))) ((#\|) (consumeChar) (state187 (scanChar))) (else (accept 'number)))) (define (state189 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state188 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state190 c) (case c ((#\+ #\-) (consumeChar) (state189 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state188 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state191 c) (case c ((#\#) (consumeChar) (state191 (scanChar))) ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L) (consumeChar) (state190 (scanChar))) ((#\|) (consumeChar) (state187 (scanChar))) (else (accept 'number)))) (define (state192 c) (case c ((#\#) (consumeChar) (state192 (scanChar))) (else (accept 'number)))) (define (state193 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state193 (scanChar))) ((#\#) (consumeChar) (state192 (scanChar))) (else (accept 'number)))) (define (state194 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state193 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state195 c) (case c ((#\#) (consumeChar) (state195 (scanChar))) ((#\/) (consumeChar) (state194 (scanChar))) ((#\.) (consumeChar) (state191 (scanChar))) ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L) (consumeChar) (state190 (scanChar))) ((#\|) (consumeChar) (state187 (scanChar))) (else (accept 'number)))) (define (state196 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state196 (scanChar))) ((#\#) (consumeChar) (state195 (scanChar))) ((#\/) (consumeChar) (state194 (scanChar))) ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L) (consumeChar) (state190 (scanChar))) ((#\|) (consumeChar) (state187 (scanChar))) ((#\.) (consumeChar) (state185 (scanChar))) (else (accept 'number)))) (define (state197 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state196 (scanChar))) ((#\.) (consumeChar) (state184 (scanChar))) ((#\n) (consumeChar) (state183 (scanChar))) ((#\i) (consumeChar) (state179 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state198 c) (case c ((#\+ #\-) (consumeChar) (state197 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state196 (scanChar))) ((#\.) (consumeChar) (state184 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state199 c) (case c ((#\i #\I) (consumeChar) (accept 'number)) ((#\#) (consumeChar) (state199 (scanChar))) ((#\@) (consumeChar) (state198 (scanChar))) ((#\+ #\-) (consumeChar) (state177 (scanChar))) (else (accept 'number)))) (define (state200 c) (case c ((#\i #\I) (consumeChar) (accept 'number)) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state200 (scanChar))) ((#\#) (consumeChar) (state199 (scanChar))) ((#\@) (consumeChar) (state198 (scanChar))) ((#\+ #\-) (consumeChar) (state177 (scanChar))) (else (accept 'number)))) (define (state201 c) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state200 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state202 c) (case c ((#\i #\I) (consumeChar) (accept 'number)) ((#\#) (consumeChar) (state202 (scanChar))) ((#\/) (consumeChar) (state201 (scanChar))) ((#\@) (consumeChar) (state198 (scanChar))) ((#\+ #\-) (consumeChar) (state177 (scanChar))) ((#\.) (consumeChar) (state156 (scanChar))) ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L) (consumeChar) (state155 (scanChar))) ((#\|) (consumeChar) (state152 (scanChar))) (else (accept 'number)))) (define (state203 c) (case c ((#\i #\I) (consumeChar) (accept 'number)) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state203 (scanChar))) ((#\#) (consumeChar) (state202 (scanChar))) ((#\/) (consumeChar) (state201 (scanChar))) ((#\@) (consumeChar) (state198 (scanChar))) ((#\+ #\-) (consumeChar) (state177 (scanChar))) ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L) (consumeChar) (state155 (scanChar))) ((#\|) (consumeChar) (state152 (scanChar))) ((#\.) (consumeChar) (state150 (scanChar))) (else (accept 'number)))) (define (state204 c) (case c ((#\I) (consumeChar) (accept 'number)) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state203 (scanChar))) ((#\.) (consumeChar) (state149 (scanChar))) ((#\n) (consumeChar) (state148 (scanChar))) ((#\i) (consumeChar) (state143 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state205 c) (case c ((#\+ #\-) (consumeChar) (state204 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state141 (scanChar))) ((#\.) (consumeChar) (state129 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state206 c) (case c ((#\i #\I #\e #\E) (consumeChar) (state205 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state207 c) (case c ((#\#) (consumeChar) (state206 (scanChar))) ((#\+ #\-) (consumeChar) (state204 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state141 (scanChar))) ((#\.) (consumeChar) (state129 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state208 c) (case c ((#\s) (consumeChar) (accept 'miscflag)) (else (scannerError errIncompleteToken)))) (define (state209 c) (case c ((#\r) (consumeChar) (state208 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state210 c) (case c ((#\6) (consumeChar) (state209 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state211 c) (case c ((#\r) (consumeChar) (state210 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state212 c) (case c ((#\`) (consumeChar) (accept 'quasisyntax)) ((#\') (consumeChar) (accept 'syntax)) ((#\() (consumeChar) (accept 'vecstart)) ((#\t #\T #\f #\F) (consumeChar) (accept 'boolean)) ((#\;) (consumeChar) (accept 'commentdatum)) ((#\|) (consumeChar) (accept 'comment)) ((#\!) (consumeChar) (state211 (scanChar))) ((#\d #\D) (consumeChar) (state207 (scanChar))) ((#\i #\I #\e #\E) (consumeChar) (state128 (scanChar))) ((#\b #\B) (consumeChar) (state28 (scanChar))) ((#\o #\O) (consumeChar) (state26 (scanChar))) ((#\x #\X) (consumeChar) (state24 (scanChar))) ((#\\) (consumeChar) (state22 (scanChar))) ((#\v) (consumeChar) (state18 (scanChar))) ((#\,) (consumeChar) (state15 (scanChar))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (consumeChar) (state14 (scanChar))) (else (scannerError errIncompleteToken)))) (define (state213 c) (case c (else (if ((lambda (c) (and (char? c) (not (char=? c (integer->char 10))))) c) (begin (consumeChar) (state213 (scanChar))) (begin (set! string_accumulator_length 0) (state0 (scanChar))))))) (define (state214 c) (case c (else (begin (set! string_accumulator_length 0) (state0 (scanChar)))))) (define (state215 c) (case c (else (accept 'comment)))) (define (state216 c) (case c (else (accept 'commentdatum)))) (define (state217 c) (case c (else (accept 'miscflag)))) (define (state218 c) (case c (else (accept 'boolean)))) (define (state219 c) (case c (else (accept 'number)))) (define (state220 c) (case c (else (accept 'character)))) (define (state221 c) (case c (else (accept 'vecstart)))) (define (state222 c) (case c (else (accept 'bvecstart)))) (define (state223 c) (case c (else (accept 'syntax)))) (define (state224 c) (case c (else (accept 'quasisyntax)))) (define (state225 c) (case c (else (accept 'unsyntaxsplicing)))) (define (state226 c) (case c (else (accept 'sharingdef)))) (define (state227 c) (case c (else (accept 'sharinguse)))) (define (state228 c) (case c (else (accept 'eofobj)))) (define (state229 c) (case c (else (accept 'id)))) (define (state230 c) (case c (else (accept 'string)))) (define (state231 c) (case c (else (accept 'lparen)))) (define (state232 c) (case c (else (accept 'rparen)))) (define (state233 c) (case c (else (accept 'lbracket)))) (define (state234 c) (case c (else (accept 'rbracket)))) (define (state235 c) (case c (else (accept 'quote)))) (define (state236 c) (case c (else (accept 'backquote)))) (define (state237 c) (case c (else (accept 'splicing)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; End of state machine generated by LexGen. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ParseGen generated the code for the strong LL(1) parser. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (parse-outermost-datum) (case (next-token) ((unsyntaxsplicing unsyntax quasisyntax syntax splicing comma backquote quote lbracket lparen vecstart bvecstart miscflag id string character number boolean sharingdef sharinguse) (let ((ast1 (parse-datum))) (identity ast1))) ((eofobj) (begin (consume-token!) (makeEOF))) (else (parse-error '<outermost-datum> '(backquote boolean bvecstart character comma eofobj id lbracket lparen miscflag number quasisyntax quote sharingdef sharinguse splicing string syntax unsyntax unsyntaxsplicing vecstart))))) (define (parse-datum) (case (next-token) ((sharinguse) (let ((ast1 (parse-sharinguse))) (makeSharingUse ast1))) ((sharingdef) (let ((ast1 (parse-sharingdef))) (let ((ast2 (parse-udatum))) (makeSharingDef ast1 ast2)))) ((boolean number character string id miscflag bvecstart vecstart lparen lbracket quote backquote comma splicing syntax quasisyntax unsyntax unsyntaxsplicing) (let ((ast1 (parse-udatum))) (identity ast1))) (else (parse-error '<datum> '(backquote boolean bvecstart character comma id lbracket lparen miscflag number quasisyntax quote sharingdef sharinguse splicing string syntax unsyntax unsyntaxsplicing vecstart))))) (define (parse-udatum) (case (next-token) ((unsyntaxsplicing unsyntax quasisyntax syntax splicing comma backquote quote lbracket lparen vecstart bvecstart) (let ((ast1 (parse-location))) (let ((ast2 (parse-structured))) (makeStructured ast1 ast2)))) ((miscflag) (begin (consume-token!) (makeFlag))) ((id) (begin (consume-token!) (makeSym))) ((string) (begin (consume-token!) (makeString))) ((character) (begin (consume-token!) (makeChar))) ((number) (begin (consume-token!) (makeNum))) ((boolean) (begin (consume-token!) (makeBool))) (else (parse-error '<udatum> '(backquote boolean bvecstart character comma id lbracket lparen miscflag number quasisyntax quote splicing string syntax unsyntax unsyntaxsplicing vecstart))))) (define (parse-structured) (case (next-token) ((bvecstart) (let ((ast1 (parse-bytevector))) (identity ast1))) ((vecstart) (let ((ast1 (parse-vector))) (identity ast1))) ((lparen lbracket quote backquote comma splicing syntax quasisyntax unsyntax unsyntaxsplicing) (let ((ast1 (parse-list))) (identity ast1))) (else (parse-error '<structured> '(backquote bvecstart comma lbracket lparen quasisyntax quote splicing syntax unsyntax unsyntaxsplicing vecstart))))) (define (parse-string) (case (next-token) ((string) (begin (consume-token!) (makeString))) (else (parse-error '<string> '(string))))) (define (parse-symbol) (case (next-token) ((id) (begin (consume-token!) (makeSym))) (else (parse-error '<symbol> '(id))))) (define (parse-list) (case (next-token) ((unsyntaxsplicing unsyntax quasisyntax syntax splicing comma backquote quote) (let ((ast1 (parse-abbreviation))) (identity ast1))) ((lbracket) (begin (consume-token!) (let ((ast1 (parse-blst2))) (identity ast1)))) ((lparen) (begin (consume-token!) (let ((ast1 (parse-list2))) (identity ast1)))) (else (parse-error '<list> '(backquote comma lbracket lparen quasisyntax quote splicing syntax unsyntax unsyntaxsplicing))))) (define (parse-list2) (case (next-token) ((unsyntaxsplicing unsyntax quasisyntax syntax splicing comma backquote quote lbracket lparen vecstart bvecstart miscflag id string character number boolean sharingdef sharinguse) (let ((ast1 (parse-datum))) (let ((ast2 (parse-list3))) (cons ast1 ast2)))) ((rparen) (begin (consume-token!) (emptyList))) (else (parse-error '<list2> '(backquote boolean bvecstart character comma id lbracket lparen miscflag number quasisyntax quote rparen sharingdef sharinguse splicing string syntax unsyntax unsyntaxsplicing vecstart))))) (define (parse-list3) (case (next-token) ((rparen period sharinguse sharingdef boolean number character string id miscflag bvecstart vecstart lparen lbracket quote backquote comma splicing syntax quasisyntax unsyntax unsyntaxsplicing) (let ((ast1 (parse-data))) (let ((ast2 (parse-list4))) (pseudoAppend ast1 ast2)))) (else (parse-error '<list3> '(backquote boolean bvecstart character comma id lbracket lparen miscflag number period quasisyntax quote rparen sharingdef sharinguse splicing string syntax unsyntax unsyntaxsplicing vecstart))))) (define (parse-list4) (case (next-token) ((period) (begin (consume-token!) (let ((ast1 (parse-datum))) (if (eq? (next-token) 'rparen) (begin (consume-token!) (identity ast1)) (parse-error '<list4> '(rparen)))))) ((rparen) (begin (consume-token!) (emptyList))) (else (parse-error '<list4> '(period rparen))))) (define (parse-blst2) (case (next-token) ((unsyntaxsplicing unsyntax quasisyntax syntax splicing comma backquote quote lbracket lparen vecstart bvecstart miscflag id string character number boolean sharingdef sharinguse) (let ((ast1 (parse-datum))) (let ((ast2 (parse-blst3))) (cons ast1 ast2)))) ((rbracket) (begin (consume-token!) (emptyList))) (else (parse-error '<blst2> '(backquote boolean bvecstart character comma id lbracket lparen miscflag number quasisyntax quote rbracket sharingdef sharinguse splicing string syntax unsyntax unsyntaxsplicing vecstart))))) (define (parse-blst3) (case (next-token) ((rbracket period sharinguse sharingdef boolean number character string id miscflag bvecstart vecstart lparen lbracket quote backquote comma splicing syntax quasisyntax unsyntax unsyntaxsplicing) (let ((ast1 (parse-data))) (let ((ast2 (parse-blst4))) (pseudoAppend ast1 ast2)))) (else (parse-error '<blst3> '(backquote boolean bvecstart character comma id lbracket lparen miscflag number period quasisyntax quote rbracket sharingdef sharinguse splicing string syntax unsyntax unsyntaxsplicing vecstart))))) (define (parse-blst4) (case (next-token) ((period) (begin (consume-token!) (let ((ast1 (parse-datum))) (if (eq? (next-token) 'rbracket) (begin (consume-token!) (identity ast1)) (parse-error '<blst4> '(rbracket)))))) ((rbracket) (begin (consume-token!) (emptyList))) (else (parse-error '<blst4> '(period rbracket))))) (define (parse-abbreviation) (case (next-token) ((quote backquote comma splicing syntax quasisyntax unsyntax unsyntaxsplicing) (let ((ast1 (parse-abbrev-prefix))) (let ((ast2 (parse-datum))) (list ast1 ast2)))) (else (parse-error '<abbreviation> '(backquote comma quasisyntax quote splicing syntax unsyntax unsyntaxsplicing))))) (define (parse-abbrev-prefix) (case (next-token) ((unsyntaxsplicing) (begin (consume-token!) (symUnsyntax-splicing))) ((unsyntax) (begin (consume-token!) (symUnsyntax))) ((quasisyntax) (begin (consume-token!) (symQuasisyntax))) ((syntax) (begin (consume-token!) (symSyntax))) ((splicing) (begin (consume-token!) (symSplicing))) ((comma) (begin (consume-token!) (symUnquote))) ((backquote) (begin (consume-token!) (symBackquote))) ((quote) (begin (consume-token!) (symQuote))) (else (parse-error '<abbrev-prefix> '(backquote comma quasisyntax quote splicing syntax unsyntax unsyntaxsplicing))))) (define (parse-vector) (case (next-token) ((vecstart) (begin (consume-token!) (let ((ast1 (parse-data))) (if (eq? (next-token) 'rparen) (begin (consume-token!) (list2vector ast1)) (parse-error '<vector> '(rparen)))))) (else (parse-error '<vector> '(vecstart))))) (define (parse-bytevector) (case (next-token) ((bvecstart) (begin (consume-token!) (let ((ast1 (parse-octets))) (if (eq? (next-token) 'rparen) (begin (consume-token!) (list2bytevector ast1)) (parse-error '<bytevector> '(rparen)))))) (else (parse-error '<bytevector> '(bvecstart))))) (define (parse-data) (case (next-token) ((unsyntaxsplicing unsyntax quasisyntax syntax splicing comma backquote quote lbracket lparen vecstart bvecstart miscflag id string character number boolean sharingdef sharinguse) (let ((ast1 (parse-datum))) (let ((ast2 (parse-data))) (cons ast1 ast2)))) ((rparen period rbracket) (emptyList)) (else (parse-error '<data> '(backquote boolean bvecstart character comma id lbracket lparen miscflag number period quasisyntax quote rbracket rparen sharingdef sharinguse splicing string syntax unsyntax unsyntaxsplicing vecstart))))) (define (parse-octets) (case (next-token) ((number) (let ((ast1 (parse-octet))) (let ((ast2 (parse-octets))) (cons ast1 ast2)))) ((rparen) (emptyList)) (else (parse-error '<octets> '(number rparen))))) (define (parse-octet) (case (next-token) ((number) (begin (consume-token!) (makeOctet))) (else (parse-error '<octet> '(number))))) (define (parse-location) (case (next-token) ((unsyntaxsplicing unsyntax quasisyntax syntax splicing comma backquote quote lbracket lparen vecstart bvecstart) (sourceLocation)) (else (parse-error '<location> '(backquote bvecstart comma lbracket lparen quasisyntax quote splicing syntax unsyntax unsyntaxsplicing vecstart))))) (define (parse-sharingdef) (case (next-token) ((sharingdef) (begin (consume-token!) (sharingDef))) (else (parse-error '<sharingdef> '(sharingdef))))) (define (parse-sharinguse) (case (next-token) ((sharinguse) (begin (consume-token!) (sharingUse))) (else (parse-error '<sharinguse> '(sharinguse))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; End of LL(1) parser generated by ParseGen. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Lexical analyzer. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; next-token and consume-token! are called by the parser. ; Returns the current token. (define (next-token) (if nextTokenIsReady kindOfNextToken (begin (set! string_accumulator_length 0) (scanner0)))) ; Consumes the current token. (define (consume-token!) (set! nextTokenIsReady #f)) ; Called by the lexical analyzer's state machine. (define (scannerError msg) (define msgtxt (cond ((= msg errLongToken) "Amazingly long token") ((= msg errIncompleteToken) "Incomplete or illegal token") ((= msg errIllegalHexEscape) "Illegal hex escape") ((= msg errIllegalNamedChar) "Illegal character syntax") ((= msg errIllegalString) "Illegal string syntax") ((= msg errIllegalSymbol) "Illegal symbol syntax") ((= msg errSRFI38) "Illegal SRFI 38 syntax") ((= msg errNoDelimiter) "Missing delimiter") ((= msg errLexGenBug) "Bug in lexical analyzer (generated)") (else "Bug in lexical analyzer"))) (let* ((c (scanChar)) (next (if (char? c) (string c) "")) (msgtxt (string-append msgtxt ": " (substring string_accumulator 0 string_accumulator_length) next))) ; must avoid infinite loop on current input port (consumeChar) (error 'get-datum (string-append "Lexical Error: " msgtxt " ") input-port)) (next-token)) ; Accepts a token of the given kind, returning that kind. ; ; For some kinds of tokens, a value for the token must also ; be recorded in tokenValue. Most of those tokens must be ; followed by a delimiter. ; ; Some magical tokens require special processing. (define (accept t) (case t ((comment) ; The token is #|, which starts a nested comment. (scan-nested-comment) (next-token)) ((commentdatum) ; The token is #; so parse and ignore the next datum. (parse-datum) (next-token)) ((id boolean number character string miscflag period sharingdef sharinguse) (set! tokenValue (substring string_accumulator 0 string_accumulator_length)) (cond ((and (eq? t 'miscflag) (string=? tokenValue "#!r6rs")) (next-token)) ((or (delimiter? (scanChar)) (eq? t 'string) (eq? t 'sharingdef) ; SRFI 38 (eq? t 'sharinguse)) ; SRFI 38 (set! kindOfNextToken t) (set! nextTokenIsReady #t) t) (else (scannerError errNoDelimiter)))) (else (set! kindOfNextToken t) (set! nextTokenIsReady #t) t))) ; Having seen a #| token, scans and discards the entire comment. (define (scan-nested-comment) (define (loop depth) (let ((c (scanChar))) (cond ((= depth 0) #t) ((eof-object? c) (scannerError errIncompleteToken)) ((char=? c #\#) (consumeChar) (if (char=? (scanChar) #\|) (begin (consumeChar) (loop (+ depth 1))) (loop depth))) ((char=? c #\|) (consumeChar) (if (char=? (scanChar) #\#) (begin (consumeChar) (loop (- depth 1))) (loop depth))) (else (consumeChar) (loop depth))))) (loop 1)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Character i/o, so to speak. ; Uses the input-port as input. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (scanChar) (peek-char input-port)) ; Consumes the current character. Returns unspecified values. (define (consumeChar) (if (< string_accumulator_length (string-length string_accumulator)) (let ((c (read-char input-port))) (if (char? c) (begin (string-set! string_accumulator string_accumulator_length c) (set! string_accumulator_length (+ string_accumulator_length 1))))) (begin (expand-accumulator) (consumeChar)))) ; Doubles the size of string_accumulator while ; preserving its contents. (define (expand-accumulator) (let* ((n (string-length string_accumulator)) (new (make-string (* 2 n)))) (do ((i 0 (+ i 1))) ((= i n)) (string-set! new i (string-ref string_accumulator i))) (set! string_accumulator new))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Miscellaneous utility routines. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Determines whether its argument is a <delimiter>. (define (delimiter? c) (case c ((#\( #\) #\[ #\] #\" #\; #\#) #t) (else (or (not (char? c)) (char-whitespace? c))))) ; Given the integer parsed from a hex escape, ; returns the corresponding Unicode character. (define (checked-integer->char n) (if (or (< n #xd800) (<= #xe000 n #x10ffff)) (integer->char n) (scannerError errIllegalHexEscape))) ; Given a string and the index at the beginning of nonempty ; sequence of hexadecimal characters followed by a semicolon, ; returns two values: ; the numerical value of the hex characters ; the index following the semicolon (define (hex-escape s i) (let ((n (string-length s))) (define (loop i val) (if (>= i n) (scannerError errIllegalHexEscape) (let ((c (string-ref s i))) (case c ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (loop (+ i 1) (+ (* 16 val) (- (char->integer c) (char->integer #\0))))) ((#\a #\b #\c #\d #\e #\f) (loop (+ i 1) (+ (* 16 val) 10 (- (char->integer c) (char->integer #\a))))) ((#\A #\B #\C #\D #\E #\F) (loop (+ i 1) (+ (* 16 val) 10 (- (char->integer c) (char->integer #\A))))) ((#\;) (values val (+ i 1))) (else (scannerError errIllegalHexEscape)))))) (loop i 0))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Action procedures called by the parser. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Hook for recording source locations. ; Called by some action routines. (define (record-source-location x loc) x) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (emptyList) '()) (define (identity x) x) (define (list2bytevector octets) (u8-list->bytevector octets)) (define (list2vector vals) (list->vector vals)) (define (makeBool) (let ((x (case (string-ref tokenValue 1) ((#\t #\T) #t) ((#\f #\F) #f) (else (scannerError errBug))))) (record-source-location x locationStart))) (define (makeChar) (let* ((n (string-length tokenValue)) (x (cond ((= n 3) (string-ref tokenValue 2)) ((char=? #\x (string-ref tokenValue 2)) (checked-integer->char (string->number (substring tokenValue 3 n) 16))) (else (let* ((s (substring tokenValue 2 n)) (sym (string->symbol s))) (case sym ((nul) #\nul) ((alarm) #\alarm) ((backspace) #\backspace) ((tab) #\tab) ((linefeed newline) #\linefeed) ((vtab) #\vtab) ((page) #\page) ((return) #\return) ((esc) #\esc) ((space) #\space) ((delete) #\delete) (else (scannerError errIllegalNamedChar)))))))) (record-source-location x locationStart))) (define (makeEOF) (eof-object)) (define (makeFlag) ; The draft R6RS allows implementation-specific extensions ; of the form #!..., which are processed here. ; Note that the #!r6rs flag is a comment, handled by accept, ; so that flag will never be seen here. (accept 'miscflag) (parse-error '<miscflag> '(miscflag))) (define (makeNum) (let ((x (string->number tokenValue))) (if x (record-source-location x locationStart) (begin (accept 'number) (parse-error '<number> '(number)))))) (define (makeOctet) (let ((n (string->number tokenValue))) (if (and (exact? n) (integer? n) (<= 0 n 255)) (record-source-location n locationStart) (begin (accept 'octet) (parse-error '<octet> '(octet)))))) (define (makeString) ; Must strip off outer double quotes and deal with escapes. ; ; i is the next index into tokenValue ; n is the exclusive upper bound for i ; newstring is a string that might become the result ; j is the next index into newstring (define (loop i n newstring j) (if (>= i n) (if (= j (string-length newstring)) newstring (substring newstring 0 j)) (let ((c (string-ref tokenValue i))) (cond ((or (char=? c #\return) (char=? c #\linefeed) (char=? c char:nel) (char=? c char:ls)) (string-set! newstring j #\linefeed) (let* ((i+1 (+ i 1)) (i+1 (if (and (char=? c #\return) (< i+1 n)) (let ((c2 (string-ref tokenValue i+1))) (if (or (char=? c2 #\linefeed) (char=? c2 char:nel)) (+ i 2) i+1)) i+1))) (loop i+1 n newstring (+ j 1)))) ((char=? c #\\) (if (< (+ i 1) n) (let ((c2 (string-ref tokenValue (+ i 1)))) (case c2 ((#\a) (string-set! newstring j #\alarm) (loop (+ i 2) n newstring (+ j 1))) ((#\b) (string-set! newstring j #\backspace) (loop (+ i 2) n newstring (+ j 1))) ((#\t) (string-set! newstring j #\tab) (loop (+ i 2) n newstring (+ j 1))) ((#\n) (string-set! newstring j #\linefeed) (loop (+ i 2) n newstring (+ j 1))) ((#\v) (string-set! newstring j #\vtab) (loop (+ i 2) n newstring (+ j 1))) ((#\f) (string-set! newstring j #\page) (loop (+ i 2) n newstring (+ j 1))) ((#\r) (string-set! newstring j #\return) (loop (+ i 2) n newstring (+ j 1))) ((#\" #\\) (string-set! newstring j c2) (loop (+ i 2) n newstring (+ j 1))) ((#\x) (call-with-values (lambda () (hex-escape tokenValue (+ i 2))) (lambda (sv i) (string-set! newstring j (checked-integer->char sv)) (loop i n newstring (+ j 1))))) (else (ignore-escaped-line-ending (+ i 1) n newstring j #f)))) (scannerError errIllegalString))) (else (string-set! newstring j c) (loop (+ i 1) n newstring (+ j 1))))))) ; Ignores <intraline whitespace>* <line ending> <intraline whitespace>* ; after? is true iff the <line ending> has already been ignored. ; The other arguments are the same as for loop above. (define (ignore-escaped-line-ending i n newstring j after?) (cond ((< i n) (let ((c (string-ref tokenValue i))) (cond ((or (char=? c #\tab) (eq? 'Zs (char-general-category c))) (ignore-escaped-line-ending (+ i 1) n newstring j after?)) (after? (loop i n newstring j)) ((or (char=? c #\return) (char=? c #\linefeed) (char=? c char:nel) (char=? c char:ls)) (let* ((i+1 (+ i 1)) (i+1 (if (and (char=? c #\return) (< i+1 n)) (let ((c2 (string-ref tokenValue i+1))) (if (or (char=? c2 #\linefeed) (char=? c2 char:nel)) (+ i 2) i+1)) i+1))) (ignore-escaped-line-ending i+1 n newstring j #t))) (else (scannerError errIllegalString))))) (after? (loop i n newstring j)) (else (scannerError errIllegalString)))) (let* ((n (string-length tokenValue)) (s (loop 1 (- n 1) (make-string (- n 2)) 0))) (record-source-location s locationStart))) (define (makeStructured loc0 x) (record-source-location x loc0)) (define (makeSym) (let ((n (string-length tokenValue))) (define (return sym) sym) (define (loop i) (if (= i n) (return (string->symbol tokenValue)) (let ((c (string-ref tokenValue i))) (cond ((or (char=? c #\\) (char=? c #\#)) (slow-loop i (reverse (string->list (substring tokenValue 0 i))))) (else (loop (+ i 1))))))) (define (slow-loop i chars) (if (= i n) (return (string->symbol (list->string (reverse chars)))) (let ((c (string-ref tokenValue i))) (cond ((char=? c #\\) (cond ((and (< (+ i 1) n) (char=? (string-ref tokenValue (+ i 1)) #\x)) (call-with-values (lambda () (hex-escape tokenValue (+ i 2))) (lambda (sv i) (slow-loop i (cons (checked-integer->char sv) chars))))) (else (scannerError errIllegalSymbol)))) (else (slow-loop (+ i 1) (cons c chars))))))) (loop 0))) ; Like append, but allows the last argument to be a non-list. (define (pseudoAppend vals terminus) (if (null? vals) terminus (cons (car vals) (pseudoAppend (cdr vals) terminus)))) ; Hook for associating source locations with tokens. (define (sourceLocation) 0) (define (symBackquote) 'quasiquote) (define (symQuasisyntax) 'quasisyntax) (define (symQuote) 'quote) (define (symSplicing) 'unquote-splicing) (define (symSyntax) 'syntax) (define (symUnquote) 'unquote) (define (symUnsyntax) 'unsyntax) (define (symUnsyntax-splicing) 'unsyntax-splicing) ; Action routines for SRFI 38. ; ; The shared-structures hashtable defines a mapping from ; indexes to fixup objects. ; ; A fixup object is a record with two mutable fields: ; ready: #t if the object field is ready, else #f ; value: if ready, the object that will replace the ; fixup object during a post-pass (define (sharingDef) (let* ((index (string->number (substring tokenValue 1 (- (string-length tokenValue) 1)))) (fixup (make-fixup-object index))) (if (not shared-structures) (set! shared-structures (make-hashtable values =))) (hashtable-set! shared-structures index fixup) fixup)) (define (sharingUse) (let* ((index (string->number (substring tokenValue 1 (- (string-length tokenValue) 1))))) (if (not shared-structures) (scannerError errSRFI38)) (let ((fixup (hashtable-ref shared-structures index #f))) (if (not fixup) (scannerError errSRFI38)) fixup))) (define (makeSharingDef fixup datum) (fixup-ready! fixup datum) datum) (define (makeSharingUse fixup) fixup) ;; After everything has been read, a second pass prepares ;; and then executes the side effects needed to recreate ;; the shared structure. (define (srfi38-postpass x) (let ((fixups '())) (define (add-fixup! fixup-object kind . rest) (if (fixup-ready? fixup-object) (set! fixups (cons (cons (fixup-value fixup-object) (cons kind rest)) fixups)) (assertion-violation 'read-with-shared-structure "undefined index" (fixup-index fixup-object)))) (define (postpass x) (cond ((pair? x) (if (fixup-object? (car x)) (add-fixup! (car x) 'set-car! x) (postpass (car x))) (if (fixup-object? (cdr x)) (add-fixup! (cdr x) 'set-cdr! x) (postpass (cdr x)))) ((vector? x) (do ((n (vector-length x)) (i 0 (+ i 1))) ((= i n)) (let ((y (vector-ref x i))) (if (fixup-object? y) (add-fixup! y 'vector-set! x i) (postpass y))))) (else #f))) (define (fixup! fixup) (let ((value (car fixup)) (kind (cadr fixup)) (container (caddr fixup)) (rest (cdddr fixup))) (case kind ((set-car!) (set-car! container value)) ((set-cdr!) (set-cdr! container value)) ((vector-set!) (vector-set! container (car rest) value)) (else (assert #f))))) (if shared-structures (begin (postpass x) (for-each fixup! fixups) (if (fixup-object? x) (fixup-value x) x)) x))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Error procedure called by the parser. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (parse-error nonterminal expected-terminals) (let* ((culprit (next-token)) (culprit-as-string (symbol->string culprit)) (culprit-as-string (if (memq culprit expected-terminals) (string-append "illegal " culprit-as-string) culprit-as-string)) (msg (string-append "Syntax error while parsing " (symbol->string nonterminal) (string #\newline) " Encountered " culprit-as-string " while expecting " (case nonterminal ((<datum> <outermost-datum> <data>) "a datum") (else (string-append (string #\newline) " " (apply string-append (map (lambda (terminal) (string-append " " (symbol->string terminal))) expected-terminals))))) (string #\newline)))) (error 'get-datum msg input-port))) ; The list of tokens that can start a datum in R6RS mode. (define datum-starters '(backquote boolean bvecstart character comma id lbracket lparen miscflag number quasisyntax quote splicing string syntax unsyntax unsyntaxsplicing vecstart)) (srfi38-postpass (parse-outermost-datum)))) (define write/ss write-with-shared-structure) (define read/ss read-with-shared-structure) ) |
Added srfi/s38/with-shared-structure.ypsilon.sls.
> > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi s38 with-shared-structure) (export write-with-shared-structure (rename (write-with-shared-structure write/ss)) read-with-shared-structure (rename (read-with-shared-structure read/ss))) (import (only (core) write-with-shared-structure read-with-shared-structure)) ) |
Added srfi/s39/parameters.ikarus.sls.
> > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi s39 parameters) (export make-parameter parameterize) (import (only (ikarus) make-parameter parameterize)) ) |
Added srfi/s39/parameters.mzscheme.sls.
> > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s39 parameters) (export make-parameter parameterize) (import (only (scheme base) make-parameter parameterize)) ) |
Added srfi/s39/parameters.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. ;; Fall-back library in case the host Scheme system does not provide SRFI-39. #!r6rs (library (srfi s39 parameters) (export make-parameter parameterize) (import (rnrs)) (define make-parameter (case-lambda [(val) (make-parameter val values)] [(val guard) (unless (procedure? guard) (assertion-violation 'make-parameter "not a procedure" guard)) (let ([p (case-lambda [() val] [(x) (set! val (guard x))])]) (p val) p)])) (define-syntax parameterize ;; Derived from Ikarus's implementation of parameterize. (lambda (stx) (syntax-case stx () [(_ () b0 b ...) #'(let () b0 b ...)] [(_ ([p e] ...) b0 b ...) (with-syntax ([(tp ...) (generate-temporaries #'(p ...))] [(te ...) (generate-temporaries #'(e ...))]) #'(let ([tp p] ... [te e] ...) (let ([swap (lambda () (let ([t (tp)]) (tp te) (set! te t)) ...)]) (dynamic-wind swap (lambda () b0 b ...) swap))))]))) ) |
Added srfi/s39/parameters.ypsilon.sls.
> > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi s39 parameters) (export make-parameter parameterize) (import (only (core) make-parameter parameterize)) ) |
Added srfi/s41/streams.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#!r6rs ;;; Copyright (C) Philip L. Bewig (2007). All Rights Reserved. ;;; Permission is hereby granted, free of charge, to any person ;;; obtaining a copy of this software and associated documentation files ;;; (the "Software"), to deal in the Software without restriction, ;;; including without limitation the rights to use, copy, modify, merge, ;;; publish, distribute, sublicense, and/or sell copies of the Software, ;;; and to permit persons to whom the Software is furnished to do so, ;;; subject to the following conditions: ;;; ;;; The above copyright notice and this permission notice shall be ;;; included in all copies or substantial portions of the Software. ;;; ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS ;;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ;;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ;;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ;;; SOFTWARE. (library (srfi s41 streams) (export stream-null stream-cons stream? stream-null? stream-pair? stream-car stream-cdr stream-lambda define-stream list->stream port->stream stream stream->list stream-append stream-concat stream-constant stream-drop stream-drop-while stream-filter stream-fold stream-for-each stream-from stream-iterate stream-length stream-let stream-map stream-match stream-of stream-range stream-ref stream-reverse stream-scan stream-take stream-take-while stream-unfold stream-unfolds stream-zip) (import (srfi s41 streams primitive) (srfi s41 streams derived))) |
Added srfi/s41/streams/derived.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 |
#!r6rs ;;; Copyright (C) Philip L. Bewig (2007). All Rights Reserved. ;;; Permission is hereby granted, free of charge, to any person ;;; obtaining a copy of this software and associated documentation files ;;; (the "Software"), to deal in the Software without restriction, ;;; including without limitation the rights to use, copy, modify, merge, ;;; publish, distribute, sublicense, and/or sell copies of the Software, ;;; and to permit persons to whom the Software is furnished to do so, ;;; subject to the following conditions: ;;; ;;; The above copyright notice and this permission notice shall be ;;; included in all copies or substantial portions of the Software. ;;; ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS ;;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ;;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ;;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ;;; SOFTWARE. (library (srfi s41 streams derived) (export stream-null stream-cons stream? stream-null? stream-pair? stream-car stream-cdr stream-lambda define-stream list->stream port->stream stream stream->list stream-append stream-concat stream-constant stream-drop stream-drop-while stream-filter stream-fold stream-for-each stream-from stream-iterate stream-length stream-let stream-map stream-match stream-of stream-range stream-ref stream-reverse stream-scan stream-take stream-take-while stream-unfold stream-unfolds stream-zip) (import (rnrs) (srfi s41 streams primitive)) (define-syntax define-stream (syntax-rules () ((define-stream (name . formal) body0 body1 ...) (define name (stream-lambda formal body0 body1 ...))))) (define (list->stream objs) (define list->stream (stream-lambda (objs) (if (null? objs) stream-null (stream-cons (car objs) (list->stream (cdr objs)))))) (if (not (list? objs)) (error 'list->stream "non-list argument") (list->stream objs))) (define (port->stream . port) (define port->stream (stream-lambda (p) (let ((c (read-char p))) (if (eof-object? c) stream-null (stream-cons c (port->stream p)))))) (let ((p (if (null? port) (current-input-port) (car port)))) (if (not (input-port? p)) (error 'port->stream "non-input-port argument") (port->stream p)))) (define-syntax stream (syntax-rules () ((stream) stream-null) ((stream x y ...) (stream-cons x (stream y ...))))) (define (stream->list . args) (let ((n (if (= 1 (length args)) #f (car args))) (strm (if (= 1 (length args)) (car args) (cadr args)))) (cond ((not (stream? strm)) (error 'stream->list "non-stream argument")) ((and n (not (integer? n))) (error 'stream->list "non-integer count")) ((and n (negative? n)) (error 'stream->list "negative count")) (else (let loop ((n (if n n -1)) (strm strm)) (if (or (zero? n) (stream-null? strm)) '() (cons (stream-car strm) (loop (- n 1) (stream-cdr strm))))))))) (define (stream-append . strms) (define stream-append (stream-lambda (strms) (cond ((null? (cdr strms)) (car strms)) ((stream-null? (car strms)) (stream-append (cdr strms))) (else (stream-cons (stream-car (car strms)) (stream-append (cons (stream-cdr (car strms)) (cdr strms)))))))) (cond ((null? strms) stream-null) ((exists (lambda (x) (not (stream? x))) strms) (error 'stream-append "non-stream argument")) (else (stream-append strms)))) (define (stream-concat strms) (define stream-concat (stream-lambda (strms) (cond ((stream-null? strms) stream-null) ((not (stream? (stream-car strms))) (error 'stream-concat "non-stream object in input stream")) ((stream-null? (stream-car strms)) (stream-concat (stream-cdr strms))) (else (stream-cons (stream-car (stream-car strms)) (stream-concat (stream-cons (stream-cdr (stream-car strms)) (stream-cdr strms)))))))) (if (not (stream? strms)) (error 'stream-concat "non-stream argument") (stream-concat strms))) (define stream-constant (stream-lambda objs (cond ((null? objs) stream-null) ((null? (cdr objs)) (stream-cons (car objs) (stream-constant (car objs)))) (else (stream-cons (car objs) (apply stream-constant (append (cdr objs) (list (car objs))))))))) (define (stream-drop n strm) (define stream-drop (stream-lambda (n strm) (if (or (zero? n) (stream-null? strm)) strm (stream-drop (- n 1) (stream-cdr strm))))) (cond ((not (integer? n)) (error 'stream-drop "non-integer argument")) ((negative? n) (error 'stream-drop "negative argument")) ((not (stream? strm)) (error 'stream-drop "non-stream argument")) (else (stream-drop n strm)))) (define (stream-drop-while pred? strm) (define stream-drop-while (stream-lambda (strm) (if (and (stream-pair? strm) (pred? (stream-car strm))) (stream-drop-while (stream-cdr strm)) strm))) (cond ((not (procedure? pred?)) (error 'stream-drop-while "non-procedural argument")) ((not (stream? strm)) (error 'stream-drop-while "non-stream argument")) (else (stream-drop-while strm)))) (define (stream-filter pred? strm) (define stream-filter (stream-lambda (strm) (cond ((stream-null? strm) stream-null) ((pred? (stream-car strm)) (stream-cons (stream-car strm) (stream-filter (stream-cdr strm)))) (else (stream-filter (stream-cdr strm)))))) (cond ((not (procedure? pred?)) (error 'stream-filter "non-procedural argument")) ((not (stream? strm)) (error 'stream-filter "non-stream argument")) (else (stream-filter strm)))) (define (stream-fold proc base strm) (cond ((not (procedure? proc)) (error 'stream-fold "non-procedural argument")) ((not (stream? strm)) (error 'stream-fold "non-stream argument")) (else (let loop ((base base) (strm strm)) (if (stream-null? strm) base (loop (proc base (stream-car strm)) (stream-cdr strm))))))) (define (stream-for-each proc . strms) (define (stream-for-each strms) (if (not (exists stream-null? strms)) (begin (apply proc (map stream-car strms)) (stream-for-each (map stream-cdr strms))))) (cond ((not (procedure? proc)) (error 'stream-for-each "non-procedural argument")) ((null? strms) (error 'stream-for-each "no stream arguments")) ((exists (lambda (x) (not (stream? x))) strms) (error 'stream-for-each "non-stream argument")) (else (stream-for-each strms)))) (define (stream-from first . step) (define stream-from (stream-lambda (first delta) (stream-cons first (stream-from (+ first delta) delta)))) (let ((delta (if (null? step) 1 (car step)))) (cond ((not (number? first)) (error 'stream-from "non-numeric starting number")) ((not (number? delta)) (error 'stream-from "non-numeric step size")) (else (stream-from first delta))))) (define (stream-iterate proc base) (define stream-iterate (stream-lambda (base) (stream-cons base (stream-iterate (proc base))))) (if (not (procedure? proc)) (error 'stream-iterate "non-procedural argument") (stream-iterate base))) (define (stream-length strm) (if (not (stream? strm)) (error 'stream-length "non-stream argument") (let loop ((len 0) (strm strm)) (if (stream-null? strm) len (loop (+ len 1) (stream-cdr strm)))))) (define-syntax stream-let (syntax-rules () ((stream-let tag ((name val) ...) body1 body2 ...) ((letrec ((tag (stream-lambda (name ...) body1 body2 ...))) tag) val ...)))) (define (stream-map proc . strms) (define stream-map (stream-lambda (strms) (if (exists stream-null? strms) stream-null (stream-cons (apply proc (map stream-car strms)) (stream-map (map stream-cdr strms)))))) (cond ((not (procedure? proc)) (error 'stream-map "non-procedural argument")) ((null? strms) (error 'stream-map "no stream arguments")) ((exists (lambda (x) (not (stream? x))) strms) (error 'stream-map "non-stream argument")) (else (stream-map strms)))) (define-syntax stream-match (syntax-rules () ((stream-match strm-expr clause ...) (let ((strm strm-expr)) (cond ((not (stream? strm)) (error 'stream-match "non-stream argument")) ((stream-match-test strm clause) => car) ... (else (error 'stream-match "pattern failure"))))))) (define-syntax stream-match-test (syntax-rules () ((stream-match-test strm (pattern fender expr)) (stream-match-pattern strm pattern () (and fender (list expr)))) ((stream-match-test strm (pattern expr)) (stream-match-pattern strm pattern () (list expr))))) (define-syntax stream-match-pattern (lambda (x) (define (wildcard? x) (and (identifier? x) (free-identifier=? x (syntax _)))) (syntax-case x () ((stream-match-pattern strm () (binding ...) body) (syntax (and (stream-null? strm) (let (binding ...) body)))) ((stream-match-pattern strm (w? . rest) (binding ...) body) (wildcard? #'w?) (syntax (and (stream-pair? strm) (let ((strm (stream-cdr strm))) (stream-match-pattern strm rest (binding ...) body))))) ((stream-match-pattern strm (var . rest) (binding ...) body) (syntax (and (stream-pair? strm) (let ((temp (stream-car strm)) (strm (stream-cdr strm))) (stream-match-pattern strm rest ((var temp) binding ...) body))))) ((stream-match-pattern strm w? (binding ...) body) (wildcard? #'w?) (syntax (let (binding ...) body))) ((stream-match-pattern strm var (binding ...) body) (syntax (let ((var strm) binding ...) body)))))) (define-syntax stream-of (syntax-rules () ((_ expr rest ...) (stream-of-aux expr stream-null rest ...)))) (define-syntax stream-of-aux (syntax-rules (in is) ((stream-of-aux expr base) (stream-cons expr base)) ((stream-of-aux expr base (var in stream) rest ...) (stream-let loop ((strm stream)) (if (stream-null? strm) base (let ((var (stream-car strm))) (stream-of-aux expr (loop (stream-cdr strm)) rest ...))))) ((stream-of-aux expr base (var is exp) rest ...) (let ((var exp)) (stream-of-aux expr base rest ...))) ((stream-of-aux expr base pred? rest ...) (if pred? (stream-of-aux expr base rest ...) base)))) (define (stream-range first past . step) (define stream-range (stream-lambda (first past delta lt?) (if (lt? first past) (stream-cons first (stream-range (+ first delta) past delta lt?)) stream-null))) (cond ((not (number? first)) (error 'stream-range "non-numeric starting number")) ((not (number? past)) (error 'stream-range "non-numeric ending number")) (else (let ((delta (cond ((pair? step) (car step)) ((< first past) 1) (else -1)))) (if (not (number? delta)) (error 'stream-range "non-numeric step size") (let ((lt? (if (< 0 delta) < >))) (stream-range first past delta lt?))))))) (define (stream-ref strm n) (cond ((not (stream? strm)) (error 'stream-ref "non-stream argument")) ((not (integer? n)) (error 'stream-ref "non-integer argument")) ((negative? n) (error 'stream-ref "negative argument")) (else (let loop ((strm strm) (n n)) (cond ((stream-null? strm) (error 'stream-ref "beyond end of stream")) ((zero? n) (stream-car strm)) (else (loop (stream-cdr strm) (- n 1)))))))) (define (stream-reverse strm) (define stream-reverse (stream-lambda (strm rev) (if (stream-null? strm) rev (stream-reverse (stream-cdr strm) (stream-cons (stream-car strm) rev))))) (if (not (stream? strm)) (error 'stream-reverse "non-stream argument") (stream-reverse strm stream-null))) (define (stream-scan proc base strm) (define stream-scan (stream-lambda (base strm) (if (stream-null? strm) (stream base) (stream-cons base (stream-scan (proc base (stream-car strm)) (stream-cdr strm)))))) (cond ((not (procedure? proc)) (error 'stream-scan "non-procedural argument")) ((not (stream? strm)) (error 'stream-scan "non-stream argument")) (else (stream-scan base strm)))) (define (stream-take n strm) (define stream-take (stream-lambda (n strm) (if (or (stream-null? strm) (zero? n)) stream-null (stream-cons (stream-car strm) (stream-take (- n 1) (stream-cdr strm)))))) (cond ((not (stream? strm)) (error 'stream-take "non-stream argument")) ((not (integer? n)) (error 'stream-take "non-integer argument")) ((negative? n) (error 'stream-take "negative argument")) (else (stream-take n strm)))) (define (stream-take-while pred? strm) (define stream-take-while (stream-lambda (strm) (cond ((stream-null? strm) stream-null) ((pred? (stream-car strm)) (stream-cons (stream-car strm) (stream-take-while (stream-cdr strm)))) (else stream-null)))) (cond ((not (stream? strm)) (error 'stream-take-while "non-stream argument")) ((not (procedure? pred?)) (error 'stream-take-while "non-procedural argument")) (else (stream-take-while strm)))) (define (stream-unfold mapper pred? generator base) (define stream-unfold (stream-lambda (base) (if (pred? base) (stream-cons (mapper base) (stream-unfold (generator base))) stream-null))) (cond ((not (procedure? mapper)) (error 'stream-unfold "non-procedural mapper")) ((not (procedure? pred?)) (error 'stream-unfold "non-procedural pred?")) ((not (procedure? generator)) (error 'stream-unfold "non-procedural generator")) (else (stream-unfold base)))) (define (stream-unfolds gen seed) (define (len-values gen seed) (call-with-values (lambda () (gen seed)) (lambda vs (- (length vs) 1)))) (define unfold-result-stream (stream-lambda (gen seed) (call-with-values (lambda () (gen seed)) (lambda (next . results) (stream-cons results (unfold-result-stream gen next)))))) (define result-stream->output-stream (stream-lambda (result-stream i) (let ((result (list-ref (stream-car result-stream) (- i 1)))) (cond ((pair? result) (stream-cons (car result) (result-stream->output-stream (stream-cdr result-stream) i))) ((not result) (result-stream->output-stream (stream-cdr result-stream) i)) ((null? result) stream-null) (else (error 'stream-unfolds "can't happen")))))) (define (result-stream->output-streams result-stream) (let loop ((i (len-values gen seed)) (outputs '())) (if (zero? i) (apply values outputs) (loop (- i 1) (cons (result-stream->output-stream result-stream i) outputs))))) (if (not (procedure? gen)) (error 'stream-unfolds "non-procedural argument") (result-stream->output-streams (unfold-result-stream gen seed)))) (define (stream-zip . strms) (define stream-zip (stream-lambda (strms) (if (exists stream-null? strms) stream-null (stream-cons (map stream-car strms) (stream-zip (map stream-cdr strms)))))) (cond ((null? strms) (error 'stream-zip "no stream arguments")) ((exists (lambda (x) (not (stream? x))) strms) (error 'stream-zip "non-stream argument")) (else (stream-zip strms))))) |
Added srfi/s41/streams/primitive.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
#!r6rs ;;; Copyright (C) Philip L. Bewig (2007). All Rights Reserved. ;;; Permission is hereby granted, free of charge, to any person ;;; obtaining a copy of this software and associated documentation files ;;; (the "Software"), to deal in the Software without restriction, ;;; including without limitation the rights to use, copy, modify, merge, ;;; publish, distribute, sublicense, and/or sell copies of the Software, ;;; and to permit persons to whom the Software is furnished to do so, ;;; subject to the following conditions: ;;; ;;; The above copyright notice and this permission notice shall be ;;; included in all copies or substantial portions of the Software. ;;; ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS ;;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ;;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ;;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ;;; SOFTWARE. (library (srfi s41 streams primitive) (export stream-null stream-cons stream? stream-null? stream-pair? stream-car stream-cdr stream-lambda) (import (rnrs) (rnrs mutable-pairs)) (define-record-type (stream-type make-stream stream?) (fields (mutable box stream-promise stream-promise!))) (define-syntax stream-lazy (syntax-rules () ((lazy expr) (make-stream (cons 'lazy (lambda () expr)))))) (define (stream-eager expr) (make-stream (cons 'eager expr))) (define-syntax stream-delay (syntax-rules () ((stream-delay expr) (stream-lazy (stream-eager expr))))) (define (stream-force promise) (let ((content (stream-promise promise))) (case (car content) ((eager) (cdr content)) ((lazy) (let* ((promise* ((cdr content))) (content (stream-promise promise))) (if (not (eqv? (car content) 'eager)) (begin (set-car! content (car (stream-promise promise*))) (set-cdr! content (cdr (stream-promise promise*))) (stream-promise! promise* content))) (stream-force promise)))))) (define stream-null (stream-delay (cons 'stream 'null))) (define-record-type (stream-pare-type make-stream-pare stream-pare?) (fields (immutable kar stream-kar) (immutable kdr stream-kdr))) (define (stream-pair? obj) (and (stream? obj) (stream-pare? (stream-force obj)))) (define (stream-null? obj) (and (stream? obj) (eqv? (stream-force obj) (stream-force stream-null)))) (define-syntax stream-cons (syntax-rules () ((stream-cons obj strm) (stream-delay (make-stream-pare (stream-delay obj) (stream-lazy strm)))))) (define (stream-car strm) (cond ((not (stream? strm)) (error 'stream-car "non-stream")) ((stream-null? strm) (error 'stream-car "null stream")) (else (stream-force (stream-kar (stream-force strm)))))) (define (stream-cdr strm) (cond ((not (stream? strm)) (error 'stream-cdr "non-stream")) ((stream-null? strm) (error 'stream-cdr "null stream")) (else (stream-kdr (stream-force strm))))) (define-syntax stream-lambda (syntax-rules () ((stream-lambda formals body0 body1 ...) (lambda formals (stream-lazy (let () body0 body1 ...))))))) |
Added srfi/s41/test.sps.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 |
; #!/usr/bin/env scheme-script ; Copyright (C) 2007 by Philip L. Bewig of Saint Louis, Missouri, USA. ; All rights reserved. Permission is hereby granted, free of charge, ; to any person obtaining a copy of ; this software and associated documentation files (the "Software"), ; to deal in the Software without restriction, including without ; limitation the rights to use, copy, modify, merge, publish, ; distribute, sublicense, and/or sell copies of the Software, and to ; permit persons to whom the Software is furnished to do so, subject ; to the following conditions: The above copyright notice and this ; permission notice shall be included in all copies or substantial ; portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ; WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED ; TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR ; PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR ; COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ; ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE ; OR OTHER DEALINGS IN THE SOFTWARE. (import (surfage s41 streams) (except (rnrs) assert) (rnrs r5rs)) (define (add1 n) (+ n 1)) (define (lsec proc . args) (lambda x (apply proc (append args x)))) (define (rsec proc . args) (lambda x (apply proc (reverse (append (reverse args) (reverse x)))))) (define-stream (qsort lt? strm) (if (stream-null? strm) stream-null (let ((x (stream-car strm)) (xs (stream-cdr strm))) (stream-append (qsort lt? (stream-filter (lambda (u) (lt? u x)) xs)) (stream x) (qsort lt? (stream-filter (lambda (u) (not (lt? u x))) xs)))))) (define-stream (isort lt? strm) (define-stream (insert strm x) (stream-match strm (() (stream x)) ((y . ys) (if (lt? y x) (stream-cons y (insert ys x)) (stream-cons x strm))))) (stream-fold insert stream-null strm)) (define-stream (stream-merge lt? . strms) (define-stream (merge xx yy) (stream-match xx (() yy) ((x . xs) (stream-match yy (() xx) ((y . ys) (if (lt? y x) (stream-cons y (merge xx ys)) (stream-cons x (merge xs yy)))))))) (stream-let loop ((strms strms)) (cond ((null? strms) stream-null) ((null? (cdr strms)) (car strms)) (else (merge (car strms) (apply stream-merge lt? (cdr strms))))))) (define-stream (msort lt? strm) (let* ((n (quotient (stream-length strm) 2)) (ts (stream-take n strm)) (ds (stream-drop n strm))) (if (zero? n) strm (stream-merge lt? (msort < ts) (msort < ds))))) (define-stream (stream-unique eql? strm) (if (stream-null? strm) stream-null (stream-cons (stream-car strm) (stream-unique eql? (stream-drop-while (lambda (x) (eql? (stream-car strm) x)) strm))))) (define nats (stream-cons 0 (stream-map add1 nats))) (define hamming (stream-unique = (stream-cons 1 (stream-merge < (stream-map (lsec * 2) hamming) (stream-merge < (stream-map (lsec * 3) hamming) (stream-map (lsec * 5) hamming)))))) (define primes (let () (define-stream (next base mult strm) (let ((first (stream-car strm)) (rest (stream-cdr strm))) (cond ((< first mult) (stream-cons first (next base mult rest))) ((< mult first) (next base (+ base mult) strm)) (else (next base (+ base mult) rest))))) (define-stream (sift base strm) (next base (+ base base) strm)) (define-stream (sieve strm) (let ((first (stream-car strm)) (rest (stream-cdr strm))) (stream-cons first (sieve (sift first rest))))) (sieve (stream-from 2)))) (define (handle-assertion thunk expr result) (cond [(string? result) ;;; error case (call/cc (lambda (k) ;;; ignoring result string (with-exception-handler k thunk) (error 'test "did not fail" expr)))] [else (unless (equal? result (thunk)) (error 'test "failed" expr))])) (define-syntax assert (syntax-rules () [(_ expr result) (handle-assertion (lambda () expr) 'expr result)])) (define (unit-test) (define strm123 (stream 1 2 3)) ; stream-null (assert (stream? stream-null) #t) (assert (stream-null? stream-null) #t) (assert (stream-pair? stream-null) #f) ; stream-cons (assert (stream? (stream-cons 1 stream-null)) #t) (assert (stream-null? (stream-cons 1 stream-null)) #f) (assert (stream-pair? (stream-cons 1 stream-null)) #t) ; stream? (assert (stream? stream-null) #t) (assert (stream? (stream-cons 1 stream-null)) #t) (assert (stream? "four") #f) ; stream-null? (assert (stream-null? stream-null) #t) (assert (stream-null? (stream-cons 1 stream-null)) #f) (assert (stream-null? "four") #f) ; stream-pair? (assert (stream-pair? stream-null) #f) (assert (stream-pair? (stream-cons 1 stream-null)) #t) (assert (stream-pair? "four") #f) ; stream-car (assert (stream-car "four") "stream-car: non-stream") (assert (stream-car stream-null) "stream-car: null stream") (assert (stream-car strm123) 1) ; stream-cdr (assert (stream-cdr "four") "stream-cdr: non-stream") (assert (stream-cdr stream-null) "stream-cdr: null stream") (assert (stream-car (stream-cdr strm123)) 2) ; stream-lambda (assert (stream->list (letrec ((double (stream-lambda (strm) (if (stream-null? strm) stream-null (stream-cons (* 2 (stream-car strm)) (double (stream-cdr strm))))))) (double strm123))) '(2 4 6)) ; define-stream (assert (stream->list (let () (define-stream (double strm) (if (stream-null? strm) stream-null (stream-cons (* 2 (stream-car strm)) (double (stream-cdr strm))))) (double strm123))) '(2 4 6)) ; list->stream (assert (list->stream "four") "list->stream: non-list argument") (assert (stream->list (list->stream '())) '()) (assert (stream->list (list->stream '(1 2 3))) '(1 2 3)) ; port->stream ;; (let* ((p (open-input-file "streams.ss")) ;; (s (port->stream p))) ;; (assert (port->stream "four") "port->stream: non-input-port argument") ;; (assert (string=? (list->string (stream->list 11 s)) "; Copyright") #t) ;; (close-input-port p)) ; stream (assert (stream->list (stream)) '()) (assert (stream->list (stream 1)) '(1)) (assert (stream->list (stream 1 2 3)) '(1 2 3)) ; stream->list (assert (stream->list '()) "stream->list: non-stream argument") (assert (stream->list "four" strm123) "stream->list: non-integer count") (assert (stream->list -1 strm123) "stream->list: negative count") (assert (stream->list (stream)) '()) (assert (stream->list strm123) '(1 2 3)) (assert (stream->list 5 strm123) '(1 2 3)) (assert (stream->list 3 (stream-from 1)) '(1 2 3)) ; stream-append (assert (stream-append "four") "stream-append: non-stream argument") (assert (stream->list (stream-append strm123)) '(1 2 3)) (assert (stream->list (stream-append strm123 strm123)) '(1 2 3 1 2 3)) (assert (stream->list (stream-append strm123 strm123 strm123)) '(1 2 3 1 2 3 1 2 3)) (assert (stream->list (stream-append strm123 stream-null)) '(1 2 3)) (assert (stream->list (stream-append stream-null strm123)) '(1 2 3)) ; stream-concat (assert (stream-concat "four") "stream-concat: non-stream argument") (assert (stream->list (stream-concat (stream strm123))) '(1 2 3)) (assert (stream->list (stream-concat (stream strm123 strm123))) '(1 2 3 1 2 3)) ; stream-constant (assert (stream-ref (stream-constant 1) 100) 1) (assert (stream-ref (stream-constant 1 2) 100) 1) (assert (stream-ref (stream-constant 1 2 3) 3) 1) ; stream-drop (assert (stream-drop "four" strm123) "stream-drop: non-integer argument") (assert (stream-drop -1 strm123) "stream-drop: negative argument") (assert (stream-drop 2 "four") "stream-drop: non-stream argument") (assert (stream->list (stream-drop 0 stream-null)) '()) (assert (stream->list (stream-drop 0 strm123)) '(1 2 3)) (assert (stream->list (stream-drop 1 strm123)) '(2 3)) (assert (stream->list (stream-drop 5 strm123)) '()) ; stream-drop-while (assert (stream-drop-while "four" strm123) "stream-drop-while: non-procedural argument") (assert (stream-drop-while odd? "four") "stream-drop-while: non-stream argument") (assert (stream->list (stream-drop-while odd? stream-null)) '()) (assert (stream->list (stream-drop-while odd? strm123)) '(2 3)) (assert (stream->list (stream-drop-while even? strm123)) '(1 2 3)) (assert (stream->list (stream-drop-while positive? strm123)) '()) (assert (stream->list (stream-drop-while negative? strm123)) '(1 2 3)) ; stream-filter (assert (stream-filter "four" strm123) "stream-filter: non-procedural argument") (assert (stream-filter odd? '()) "stream-filter: non-stream argument") (assert (stream-null? (stream-filter odd? (stream))) #t) (assert (stream->list (stream-filter odd? strm123)) '(1 3)) (assert (stream->list (stream-filter even? strm123)) '(2)) (assert (stream->list (stream-filter positive? strm123)) '(1 2 3)) (assert (stream->list (stream-filter negative? strm123)) '()) (let loop ((n 10)) (assert (odd? (stream-ref (stream-filter odd? (stream-from 0)) n)) #t) (if (positive? n) (loop (- n 1)))) (let loop ((n 10)) (assert (even? (stream-ref (stream-filter odd? (stream-from 0)) n)) #f) (if (positive? n) (loop (- n 1)))) ; stream-fold (assert (stream-fold "four" 0 strm123) "stream-fold: non-procedural argument") (assert (stream-fold + 0 '()) "stream-fold: non-stream argument") (assert (stream-fold + 0 strm123) 6) ; stream-for-each (assert (stream-for-each "four" strm123) "stream-for-each: non-procedural argument") (assert (stream-for-each display) "stream-for-each: no stream arguments") (assert (stream-for-each display "four") "stream-for-each: non-stream argument") (assert (let ((sum 0)) (stream-for-each (lambda (x) (set! sum (+ sum x))) strm123) sum) 6) ; stream-from (assert (stream-from "four") "stream-from: non-numeric starting number") (assert (stream-from 1 "four") "stream-from: non-numeric step size") (assert (stream-ref (stream-from 0) 100) 100) (assert (stream-ref (stream-from 1 2) 100) 201) (assert (stream-ref (stream-from 0 -1) 100) -100) ; stream-iterate (assert (stream-iterate "four" 0) "stream-iterate: non-procedural argument") (assert (stream->list 3 (stream-iterate (lsec + 1) 1)) '(1 2 3)) ; stream-length (assert (stream-length "four") "stream-length: non-stream argument") (assert (stream-length (stream)) 0) (assert (stream-length strm123) 3) ; stream-let (assert (stream->list (stream-let loop ((strm strm123)) (if (stream-null? strm) stream-null (stream-cons (* 2 (stream-car strm)) (loop (stream-cdr strm)))))) '(2 4 6)) ; stream-map (assert (stream-map "four" strm123) "stream-map: non-procedural argument") (assert (stream-map odd?) "stream-map: no stream arguments") (assert (stream-map odd? "four") "stream-map: non-stream argument") (assert (stream->list (stream-map - strm123)) '(-1 -2 -3)) (assert (stream->list (stream-map + strm123 strm123)) '(2 4 6)) (assert (stream->list (stream-map + strm123 (stream-from 1))) '(2 4 6)) (assert (stream->list (stream-map + (stream-from 1) strm123)) '(2 4 6)) (assert (stream->list (stream-map + strm123 strm123 strm123)) '(3 6 9)) ; stream-match (assert (stream-match '(1 2 3) (_ 'ok)) "stream-match: non-stream argument") (assert (stream-match strm123 (() 42)) "stream-match: pattern failure") (assert (stream-match stream-null (() 'ok)) 'ok) (assert (stream-match strm123 (() 'no) (else 'ok)) 'ok) (assert (stream-match (stream 1) (() 'no) ((a) a)) 1) (assert (stream-match (stream 1) (() 'no) ((_) 'ok)) 'ok) (assert (stream-match strm123 ((a b c) (list a b c))) '(1 2 3)) (assert (stream-match strm123 ((a . _) a)) 1) (assert (stream-match strm123 ((a b . _) (list a b))) '(1 2)) (assert (stream-match strm123 ((a b . c) (list a b (stream-car c)))) '(1 2 3)) (assert (stream-match strm123 (s (stream->list s))) '(1 2 3)) (assert (stream-match strm123 ((a . _) (= a 1) 'ok)) 'ok) (assert (stream-match strm123 ((a . _) (= a 2) 'yes) (_ 'no)) 'no) (assert (stream-match strm123 ((a b c) (= a b) 'yes) (_ 'no)) 'no) (assert (stream-match (stream 1 1 2) ((a b c) (= a b) 'yes) (_ 'no)) 'yes) ; stream-of (assert (stream->list (stream-of (+ y 6) (x in (stream-range 1 6)) (odd? x) (y is (* x x)))) '(7 15 31)) (assert (stream->list (stream-of (* x y) (x in (stream-range 1 4)) (y in (stream-range 1 5)))) '(1 2 3 4 2 4 6 8 3 6 9 12)) (assert (stream-car (stream-of 1)) 1) ; stream-range (assert (stream-range "four" 0) "stream-range: non-numeric starting number") (assert (stream-range 0 "four") "stream-range: non-numeric ending number") (assert (stream-range 1 2 "three") "stream-range: non-numeric step size") (assert (stream->list (stream-range 0 5)) '(0 1 2 3 4)) (assert (stream->list (stream-range 5 0)) '(5 4 3 2 1)) (assert (stream->list (stream-range 0 5 2)) '(0 2 4)) (assert (stream->list (stream-range 5 0 -2)) '(5 3 1)) (assert (stream->list (stream-range 0 1 -1)) '()) ; stream-ref (assert (stream-ref '() 4) "stream-ref: non-stream argument") (assert (stream-ref nats 3.5) "stream-ref: non-integer argument") (assert (stream-ref nats -3) "stream-ref: negative argument") (assert (stream-ref strm123 5) "stream-ref: beyond end of stream") (assert (stream-ref strm123 0) 1) (assert (stream-ref strm123 1) 2) (assert (stream-ref strm123 2) 3) ; stream-reverse (assert (stream-reverse '()) "stream-reverse: non-stream argument") (assert (stream->list (stream-reverse (stream))) '()) (assert (stream->list (stream-reverse strm123)) '(3 2 1)) ; stream-scan (assert (stream-scan "four" 0 strm123) "stream-scan: non-procedural argument") (assert (stream-scan + 0 '()) "stream-scan: non-stream argument") (assert (stream->list (stream-scan + 0 strm123)) '(0 1 3 6)) ; stream-take (assert (stream-take 5 "four") "stream-take: non-stream argument") (assert (stream-take "four" strm123) "stream-take: non-integer argument") (assert (stream-take -4 strm123) "stream-take: negative argument") (assert (stream->list (stream-take 5 stream-null)) '()) (assert (stream->list (stream-take 0 stream-null)) '()) (assert (stream->list (stream-take 0 strm123)) '()) (assert (stream->list (stream-take 2 strm123)) '(1 2)) (assert (stream->list (stream-take 3 strm123)) '(1 2 3)) (assert (stream->list (stream-take 5 strm123)) '(1 2 3)) ; stream-take-while (assert (stream-take-while odd? "four") "stream-take-while: non-stream argument") (assert (stream-take-while "four" strm123) "stream-take-while: non-procedural argument") (assert (stream->list (stream-take-while odd? strm123)) '(1)) (assert (stream->list (stream-take-while even? strm123)) '()) (assert (stream->list (stream-take-while positive? strm123)) '(1 2 3)) (assert (stream->list (stream-take-while negative? strm123)) '()) ; stream-unfold (assert (stream-unfold "four" odd? + 0) "stream-unfold: non-procedural mapper") (assert (stream-unfold + "four" + 0) "stream-unfold: non-procedural pred?") (assert (stream-unfold + odd? "four" 0) "stream-unfold: non-procedural generator") (assert (stream->list (stream-unfold (rsec expt 2) (rsec < 10) (rsec + 1) 0)) '(0 1 4 9 16 25 36 49 64 81)) ; stream-unfolds (assert (stream->list (stream-unfolds (lambda (x) (let ((n (car x)) (s (cdr x))) (if (zero? n) (values 'dummy '()) (values (cons (- n 1) (stream-cdr s)) (list (stream-car s)))))) (cons 5 (stream-from 0)))) '(0 1 2 3 4)) ; stream-zip (assert (stream-zip) "stream-zip: no stream arguments") (assert (stream-zip "four") "stream-zip: non-stream argument") (assert (stream-zip strm123 "four") "stream-zip: non-stream argument") (assert (stream->list (stream-zip strm123 stream-null)) '()) (assert (stream->list (stream-zip strm123)) '((1) (2) (3))) (assert (stream->list (stream-zip strm123 strm123)) '((1 1) (2 2) (3 3))) (assert (stream->list (stream-zip strm123 (stream-from 1))) '((1 1) (2 2) (3 3))) (assert (stream->list (stream-zip strm123 strm123 strm123)) '((1 1 1) (2 2 2) (3 3 3))) ; other tests (assert (stream-car (stream-reverse (stream-take-while (rsec < 1000) primes))) 997) (assert (equal? (stream->list (qsort < (stream 3 1 5 2 4))) (stream->list (isort < (stream 2 5 1 4 3)))) #t) (assert (equal? (stream->list (msort < (stream 3 1 5 2 4))) (stream->list (isort < (stream 2 5 1 4 3)))) #t) ; http://www.research.att.com/~njas/sequences/A051037 (assert (stream-ref hamming 999) 51200000) ) (unit-test) |
Added srfi/s42/_eager-comprehensions-a.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s42 eager-comprehensions) (export do-ec list-ec append-ec string-ec string-append-ec vector-ec vector-of-length-ec sum-ec product-ec min-ec max-ec any?-ec every?-ec first-ec last-ec fold-ec fold3-ec : :list :string :vector :integers :range :real-range :char-range :port :dispatched :do :let :parallel :while :until :-dispatch-ref :-dispatch-set! make-initial-:-dispatch dispatch-union :generator-proc) (import (rnrs) (rnrs r5rs) (srfi s39 parameters) (srfi s23 error tricks) (srfi private include)) ;; (SRFI-23-error->R6RS "(library (srfi s42 eager-comprehensions))" ;; (include/resolve ("srfi" "%3a42") "ec.scm")) ; <PLAINTEXT> ; Eager Comprehensions in [outer..inner|expr]-Convention ; ====================================================== ; ; sebastian.egner@philips.com, Eindhoven, The Netherlands, 25-Apr-2005 ; Scheme R5RS (incl. macros), SRFI-23 (error). ; ; Modified by Derick Eddington to be able to be included into an R6RS library. ; ; Loading the implementation into Scheme48 0.57: ; ,open srfi-23 ; ,load ec.scm ; ; Loading the implementation into PLT/DrScheme 202: ; ; File > Open ... "ec.scm", click Execute ; ; Loading the implementation into SCM 5d7: ; (require 'macro) (require 'record) ; (load "ec.scm") ; ; Implementation comments: ; * All local (not exported) identifiers are named ec-<something>. ; * This implementation focuses on portability, performance, ; readability, and simplicity roughly in this order. Design ; decisions related to performance are taken for Scheme48. ; * Alternative implementations, Comments and Warnings are ; mentioned after the definition with a heading. ; ========================================================================== ; The fundamental comprehension do-ec ; ========================================================================== ; ; All eager comprehensions are reduced into do-ec and ; all generators are reduced to :do. ; ; We use the following short names for syntactic variables ; q - qualifier ; cc - current continuation, thing to call at the end; ; the CPS is (m (cc ...) arg ...) -> (cc ... expr ...) ; cmd - an expression being evaluated for its side-effects ; expr - an expression ; gen - a generator of an eager comprehension ; ob - outer binding ; oc - outer command ; lb - loop binding ; ne1? - not-end1? (before the payload) ; ib - inner binding ; ic - inner command ; ne2? - not-end2? (after the payload) ; ls - loop step ; etc - more arguments of mixed type ; (do-ec q ... cmd) ; handles nested, if/not/and/or, begin, :let, and calls generator ; macros in CPS to transform them into fully decorated :do. ; The code generation for a :do is delegated to do-ec:do. (define-syntax do-ec (syntax-rules (nested if not and or begin :do let) ; explicit nesting -> implicit nesting ((do-ec (nested q ...) etc ...) (do-ec q ... etc ...) ) ; implicit nesting -> fold do-ec ((do-ec q1 q2 etc1 etc ...) (do-ec q1 (do-ec q2 etc1 etc ...)) ) ; no qualifiers at all -> evaluate cmd once ((do-ec cmd) (begin cmd (if #f #f)) ) ; now (do-ec q cmd) remains ; filter -> make conditional ((do-ec (if test) cmd) (if test (do-ec cmd)) ) ((do-ec (not test) cmd) (if (not test) (do-ec cmd)) ) ((do-ec (and test ...) cmd) (if (and test ...) (do-ec cmd)) ) ((do-ec (or test ...) cmd) (if (or test ...) (do-ec cmd)) ) ; begin -> make a sequence ((do-ec (begin etc ...) cmd) (begin etc ... (do-ec cmd)) ) ; fully decorated :do-generator -> delegate to do-ec:do ((do-ec (:do olet lbs ne1? ilet ne2? lss) cmd) (do-ec:do cmd (:do olet lbs ne1? ilet ne2? lss)) ) ; anything else -> call generator-macro in CPS; reentry at (*) ((do-ec (g arg1 arg ...) cmd) (g (do-ec:do cmd) arg1 arg ...) ))) ; (do-ec:do cmd (:do olet lbs ne1? ilet ne2? lss)) ; generates code for a single fully decorated :do-generator ; with cmd as payload, taking care of special cases. (define-syntax do-ec:do (syntax-rules (:do let) ; reentry point (*) -> generate code ((do-ec:do cmd (:do (let obs oc ...) lbs ne1? (let ibs ic ...) ne2? (ls ...) )) (ec-simplify (let obs oc ... (let loop lbs (ec-simplify (if ne1? (ec-simplify (let ibs ic ... cmd (ec-simplify (if ne2? (loop ls ...) )))))))))) )) ; (ec-simplify <expression>) ; generates potentially more efficient code for <expression>. ; The macro handles if, (begin <command>*), and (let () <command>*) ; and takes care of special cases. (define-syntax ec-simplify (syntax-rules (if not let begin) ; one- and two-sided if ; literal <test> ((ec-simplify (if #t consequent)) consequent ) ((ec-simplify (if #f consequent)) (if #f #f) ) ((ec-simplify (if #t consequent alternate)) consequent ) ((ec-simplify (if #f consequent alternate)) alternate ) ; (not (not <test>)) ((ec-simplify (if (not (not test)) consequent)) (ec-simplify (if test consequent)) ) ((ec-simplify (if (not (not test)) consequent alternate)) (ec-simplify (if test consequent alternate)) ) ; (let () <command>*) ; empty <binding spec>* ((ec-simplify (let () command ...)) (ec-simplify (begin command ...)) ) ; begin ; flatten use helper (ec-simplify 1 done to-do) ((ec-simplify (begin command ...)) (ec-simplify 1 () (command ...)) ) ((ec-simplify 1 done ((begin to-do1 ...) to-do2 ...)) (ec-simplify 1 done (to-do1 ... to-do2 ...)) ) ((ec-simplify 1 (done ...) (to-do1 to-do ...)) (ec-simplify 1 (done ... to-do1) (to-do ...)) ) ; exit helper ((ec-simplify 1 () ()) (if #f #f) ) ((ec-simplify 1 (command) ()) command ) ((ec-simplify 1 (command1 command ...) ()) (begin command1 command ...) ) ; anything else ((ec-simplify expression) expression ))) ; ========================================================================== ; The special generators :do, :let, :parallel, :while, and :until ; ========================================================================== (define-syntax :do (syntax-rules () ; full decorated -> continue with cc, reentry at (*) ((:do (cc ...) olet lbs ne1? ilet ne2? lss) (cc ... (:do olet lbs ne1? ilet ne2? lss)) ) ; short form -> fill in default values ((:do cc lbs ne1? lss) (:do cc (let ()) lbs ne1? (let ()) #t lss) ))) (define-syntax :let (syntax-rules (index) ((:let cc var (index i) expression) (:do cc (let ((var expression) (i 0))) () #t (let ()) #f ()) ) ((:let cc var expression) (:do cc (let ((var expression))) () #t (let ()) #f ()) ))) (define-syntax :parallel (syntax-rules (:do) ((:parallel cc) cc ) ((:parallel cc (g arg1 arg ...) gen ...) (g (:parallel-1 cc (gen ...)) arg1 arg ...) ))) ; (:parallel-1 cc (to-do ...) result [ next ] ) ; iterates over to-do by converting the first generator into ; the :do-generator next and merging next into result. (define-syntax :parallel-1 ; used as (syntax-rules (:do let) ; process next element of to-do, reentry at (**) ((:parallel-1 cc ((g arg1 arg ...) gen ...) result) (g (:parallel-1 cc (gen ...) result) arg1 arg ...) ) ; reentry point (**) -> merge next into result ((:parallel-1 cc gens (:do (let (ob1 ...) oc1 ...) (lb1 ...) ne1?1 (let (ib1 ...) ic1 ...) ne2?1 (ls1 ...) ) (:do (let (ob2 ...) oc2 ...) (lb2 ...) ne1?2 (let (ib2 ...) ic2 ...) ne2?2 (ls2 ...) )) (:parallel-1 cc gens (:do (let (ob1 ... ob2 ...) oc1 ... oc2 ...) (lb1 ... lb2 ...) (and ne1?1 ne1?2) (let (ib1 ... ib2 ...) ic1 ... ic2 ...) (and ne2?1 ne2?2) (ls1 ... ls2 ...) ))) ; no more gens -> continue with cc, reentry at (*) ((:parallel-1 (cc ...) () result) (cc ... result) ))) (define-syntax :while (syntax-rules () ((:while cc (g arg1 arg ...) test) (g (:while-1 cc test) arg1 arg ...) ))) ; (:while-1 cc test (:do ...)) ; modifies the fully decorated :do-generator such that it ; runs while test is a true value. ; The original implementation just replaced ne1? by ; (and ne1? test) as follows: ; ; (define-syntax :while-1 ; (syntax-rules (:do) ; ((:while-1 cc test (:do olet lbs ne1? ilet ne2? lss)) ; (:do cc olet lbs (and ne1? test) ilet ne2? lss) ))) ; ; Unfortunately, this code is wrong because ne1? may depend ; in the inner bindings introduced in ilet, but ne1? is evaluated ; outside of the inner bindings. (Refer to the specification of ; :do to see the structure.) ; The problem manifests itself (as sunnan@handgranat.org ; observed) when the :list-generator is modified: ; ; (do-ec (:while (:list x '(1 2)) (= x 1)) (display x)). ; ; In order to generate proper code, we introduce temporary ; variables saving the values of the inner bindings. The inner ; bindings are executed in a new ne1?, which also evaluates ne1? ; outside the scope of the inner bindings, then the inner commands ; are executed (possibly changing the variables), and then the ; values of the inner bindings are saved and (and ne1? test) is ; returned. In the new ilet, the inner variables are bound and ; initialized and their values are restored. So we construct: ; ; (let (ob .. (ib-tmp #f) ...) ; oc ... ; (let loop (lb ...) ; (if (let (ne1?-value ne1?) ; (let ((ib-var ib-rhs) ...) ; ic ... ; (set! ib-tmp ib-var) ...) ; (and ne1?-value test)) ; (let ((ib-var ib-tmp) ...) ; /payload/ ; (if ne2? ; (loop ls ...) ))))) (define-syntax :while-1 (syntax-rules (:do let) ((:while-1 cc test (:do olet lbs ne1? ilet ne2? lss)) (:while-2 cc test () () () (:do olet lbs ne1? ilet ne2? lss))))) (define-syntax :while-2 (syntax-rules (:do let) ((:while-2 cc test (ib-let ...) (ib-save ...) (ib-restore ...) (:do olet lbs ne1? (let ((ib-var ib-rhs) ib ...) ic ...) ne2? lss)) (:while-2 cc test (ib-let ... (ib-tmp #f)) (ib-save ... (ib-var ib-rhs)) (ib-restore ... (ib-var ib-tmp)) (:do olet lbs ne1? (let (ib ...) ic ... (set! ib-tmp ib-var)) ne2? lss))) ((:while-2 cc test (ib-let ...) (ib-save ...) (ib-restore ...) (:do (let (ob ...) oc ...) lbs ne1? (let () ic ...) ne2? lss)) (:do cc (let (ob ... ib-let ...) oc ...) lbs (let ((ne1?-value ne1?)) (let (ib-save ...) ic ... (and ne1?-value test))) (let (ib-restore ...)) ne2? lss)))) (define-syntax :until (syntax-rules () ((:until cc (g arg1 arg ...) test) (g (:until-1 cc test) arg1 arg ...) ))) (define-syntax :until-1 (syntax-rules (:do) ((:until-1 cc test (:do olet lbs ne1? ilet ne2? lss)) (:do cc olet lbs ne1? ilet (and ne2? (not test)) lss) ))) ; ========================================================================== ; The typed generators :list :string :vector etc. ; ========================================================================== (define-syntax :list (syntax-rules (index) ((:list cc var (index i) arg ...) (:parallel cc (:list var arg ...) (:integers i)) ) ((:list cc var arg1 arg2 arg ...) (:list cc var (append arg1 arg2 arg ...)) ) ((:list cc var arg) (:do cc (let ()) ((t arg)) (not (null? t)) (let ((var (car t)))) #t ((cdr t)) )))) (define-syntax :string (syntax-rules (index) ((:string cc var (index i) arg) (:do cc (let ((str arg) (len 0)) (set! len (string-length str))) ((i 0)) (< i len) (let ((var (string-ref str i)))) #t ((+ i 1)) )) ((:string cc var (index i) arg1 arg2 arg ...) (:string cc var (index i) (string-append arg1 arg2 arg ...)) ) ((:string cc var arg1 arg ...) (:string cc var (index i) arg1 arg ...) ))) ; Alternative: An implementation in the style of :vector can also ; be used for :string. However, it is less interesting as the ; overhead of string-append is much less than for 'vector-append'. (define-syntax :vector (syntax-rules (index) ((:vector cc var arg) (:vector cc var (index i) arg) ) ((:vector cc var (index i) arg) (:do cc (let ((vec arg) (len 0)) (set! len (vector-length vec))) ((i 0)) (< i len) (let ((var (vector-ref vec i)))) #t ((+ i 1)) )) ((:vector cc var (index i) arg1 arg2 arg ...) (:parallel cc (:vector cc var arg1 arg2 arg ...) (:integers i)) ) ((:vector cc var arg1 arg2 arg ...) (:do cc (let ((vec #f) (len 0) (vecs (ec-:vector-filter (list arg1 arg2 arg ...))) )) ((k 0)) (if (< k len) #t (if (null? vecs) #f (begin (set! vec (car vecs)) (set! vecs (cdr vecs)) (set! len (vector-length vec)) (set! k 0) #t ))) (let ((var (vector-ref vec k)))) #t ((+ k 1)) )))) (define (ec-:vector-filter vecs) (if (null? vecs) '() (if (zero? (vector-length (car vecs))) (ec-:vector-filter (cdr vecs)) (cons (car vecs) (ec-:vector-filter (cdr vecs))) ))) ; Alternative: A simpler implementation for :vector uses vector->list ; append and :list in the multi-argument case. Please refer to the ; 'design.scm' for more details. (define-syntax :integers (syntax-rules (index) ((:integers cc var (index i)) (:do cc ((var 0) (i 0)) #t ((+ var 1) (+ i 1))) ) ((:integers cc var) (:do cc ((var 0)) #t ((+ var 1))) ))) (define-syntax :range (syntax-rules (index) ; handle index variable and add optional args ((:range cc var (index i) arg1 arg ...) (:parallel cc (:range var arg1 arg ...) (:integers i)) ) ((:range cc var arg1) (:range cc var 0 arg1 1) ) ((:range cc var arg1 arg2) (:range cc var arg1 arg2 1) ) ; special cases (partially evaluated by hand from general case) ((:range cc var 0 arg2 1) (:do cc (let ((b arg2)) (if (not (and (integer? b) (exact? b))) (error "arguments of :range are not exact integer " "(use :real-range?)" 0 b 1 ))) ((var 0)) (< var b) (let ()) #t ((+ var 1)) )) ((:range cc var 0 arg2 -1) (:do cc (let ((b arg2)) (if (not (and (integer? b) (exact? b))) (error "arguments of :range are not exact integer " "(use :real-range?)" 0 b 1 ))) ((var 0)) (> var b) (let ()) #t ((- var 1)) )) ((:range cc var arg1 arg2 1) (:do cc (let ((a arg1) (b arg2)) (if (not (and (integer? a) (exact? a) (integer? b) (exact? b) )) (error "arguments of :range are not exact integer " "(use :real-range?)" a b 1 )) ) ((var a)) (< var b) (let ()) #t ((+ var 1)) )) ((:range cc var arg1 arg2 -1) (:do cc (let ((a arg1) (b arg2) (s -1) (stop 0)) (if (not (and (integer? a) (exact? a) (integer? b) (exact? b) )) (error "arguments of :range are not exact integer " "(use :real-range?)" a b -1 )) ) ((var a)) (> var b) (let ()) #t ((- var 1)) )) ; the general case ((:range cc var arg1 arg2 arg3) (:do cc (let ((a arg1) (b arg2) (s arg3) (stop 0)) (if (not (and (integer? a) (exact? a) (integer? b) (exact? b) (integer? s) (exact? s) )) (error "arguments of :range are not exact integer " "(use :real-range?)" a b s )) (if (zero? s) (error "step size must not be zero in :range") ) (set! stop (+ a (* (max 0 (ceiling (/ (- b a) s))) s))) ) ((var a)) (not (= var stop)) (let ()) #t ((+ var s)) )))) ; Comment: The macro :range inserts some code to make sure the values ; are exact integers. This overhead has proven very helpful for ; saving users from themselves. (define-syntax :real-range (syntax-rules (index) ; add optional args and index variable ((:real-range cc var arg1) (:real-range cc var (index i) 0 arg1 1) ) ((:real-range cc var (index i) arg1) (:real-range cc var (index i) 0 arg1 1) ) ((:real-range cc var arg1 arg2) (:real-range cc var (index i) arg1 arg2 1) ) ((:real-range cc var (index i) arg1 arg2) (:real-range cc var (index i) arg1 arg2 1) ) ((:real-range cc var arg1 arg2 arg3) (:real-range cc var (index i) arg1 arg2 arg3) ) ; the fully qualified case ((:real-range cc var (index i) arg1 arg2 arg3) (:do cc (let ((a arg1) (b arg2) (s arg3) (istop 0)) (if (not (and (real? a) (real? b) (real? s))) (error "arguments of :real-range are not real" a b s) ) (if (and (exact? a) (or (not (exact? b)) (not (exact? s)))) (set! a (exact->inexact a)) ) (set! istop (/ (- b a) s)) ) ((i 0)) (< i istop) (let ((var (+ a (* s i))))) #t ((+ i 1)) )))) ; Comment: The macro :real-range adapts the exactness of the start ; value in case any of the other values is inexact. This is a ; precaution to avoid (list-ec (: x 0 3.0) x) => '(0 1.0 2.0). (define-syntax :char-range (syntax-rules (index) ((:char-range cc var (index i) arg1 arg2) (:parallel cc (:char-range var arg1 arg2) (:integers i)) ) ((:char-range cc var arg1 arg2) (:do cc (let ((imax (char->integer arg2)))) ((i (char->integer arg1))) (<= i imax) (let ((var (integer->char i)))) #t ((+ i 1)) )))) ; Warning: There is no R5RS-way to implement the :char-range generator ; because the integers obtained by char->integer are not necessarily ; consecutive. We simply assume this anyhow for illustration. (define-syntax :port (syntax-rules (index) ((:port cc var (index i) arg1 arg ...) (:parallel cc (:port var arg1 arg ...) (:integers i)) ) ((:port cc var arg) (:port cc var arg read) ) ((:port cc var arg1 arg2) (:do cc (let ((port arg1) (read-proc arg2))) ((var (read-proc port))) (not (eof-object? var)) (let ()) #t ((read-proc port)) )))) ; ========================================================================== ; The typed generator :dispatched and utilities for constructing dispatchers ; ========================================================================== (define-syntax :dispatched (syntax-rules (index) ((:dispatched cc var (index i) dispatch arg1 arg ...) (:parallel cc (:integers i) (:dispatched var dispatch arg1 arg ...) )) ((:dispatched cc var dispatch arg1 arg ...) (:do cc (let ((d dispatch) (args (list arg1 arg ...)) (g #f) (empty (list #f)) ) (set! g (d args)) (if (not (procedure? g)) (error "unrecognized arguments in dispatching" args (d '()) ))) ((var (g empty))) (not (eq? var empty)) (let ()) #t ((g empty)) )))) ; Comment: The unique object empty is created as a newly allocated ; non-empty list. It is compared using eq? which distinguishes ; the object from any other object, according to R5RS 6.1. (define-syntax :generator-proc (syntax-rules (:do let) ; call g with a variable, reentry at (**) ((:generator-proc (g arg ...)) (g (:generator-proc var) var arg ...) ) ; reentry point (**) -> make the code from a single :do ((:generator-proc var (:do (let obs oc ...) ((lv li) ...) ne1? (let ((i v) ...) ic ...) ne2? (ls ...)) ) (ec-simplify (let obs oc ... (let ((lv li) ... (ne2 #t)) (ec-simplify (let ((i #f) ...) ; v not yet valid (lambda (empty) (if (and ne1? ne2) (ec-simplify (begin (set! i v) ... ic ... (let ((value var)) (ec-simplify (if ne2? (ec-simplify (begin (set! lv ls) ...) ) (set! ne2 #f) )) value ))) empty )))))))) ; silence warnings of some macro expanders ((:generator-proc var) (error "illegal macro call") ))) (define (dispatch-union d1 d2) (lambda (args) (let ((g1 (d1 args)) (g2 (d2 args))) (if g1 (if g2 (if (null? args) (append (if (list? g1) g1 (list g1)) (if (list? g2) g2 (list g2)) ) (error "dispatching conflict" args (d1 '()) (d2 '())) ) g1 ) (if g2 g2 #f) )))) ; ========================================================================== ; The dispatching generator : ; ========================================================================== (define (make-initial-:-dispatch) (lambda (args) (case (length args) ((0) 'SRFI42) ((1) (let ((a1 (car args))) (cond ((list? a1) (:generator-proc (:list a1)) ) ((string? a1) (:generator-proc (:string a1)) ) ((vector? a1) (:generator-proc (:vector a1)) ) ((and (integer? a1) (exact? a1)) (:generator-proc (:range a1)) ) ((real? a1) (:generator-proc (:real-range a1)) ) ((input-port? a1) (:generator-proc (:port a1)) ) (else #f )))) ((2) (let ((a1 (car args)) (a2 (cadr args))) (cond ((and (list? a1) (list? a2)) (:generator-proc (:list a1 a2)) ) ((and (string? a1) (string? a2)) (:generator-proc (:string a1 a2)) ) ((and (vector? a1) (vector? a2)) (:generator-proc (:vector a1 a2)) ) ((and (integer? a1) (exact? a1) (integer? a2) (exact? a2)) (:generator-proc (:range a1 a2)) ) ((and (real? a1) (real? a2)) (:generator-proc (:real-range a1 a2)) ) ((and (char? a1) (char? a2)) (:generator-proc (:char-range a1 a2)) ) ((and (input-port? a1) (procedure? a2)) (:generator-proc (:port a1 a2)) ) (else #f )))) ((3) (let ((a1 (car args)) (a2 (cadr args)) (a3 (caddr args))) (cond ((and (list? a1) (list? a2) (list? a3)) (:generator-proc (:list a1 a2 a3)) ) ((and (string? a1) (string? a2) (string? a3)) (:generator-proc (:string a1 a2 a3)) ) ((and (vector? a1) (vector? a2) (vector? a3)) (:generator-proc (:vector a1 a2 a3)) ) ((and (integer? a1) (exact? a1) (integer? a2) (exact? a2) (integer? a3) (exact? a3)) (:generator-proc (:range a1 a2 a3)) ) ((and (real? a1) (real? a2) (real? a3)) (:generator-proc (:real-range a1 a2 a3)) ) (else #f )))) (else (letrec ((every? (lambda (pred args) (if (null? args) #t (and (pred (car args)) (every? pred (cdr args)) ))))) (cond ((every? list? args) (:generator-proc (:list (apply append args))) ) ((every? string? args) (:generator-proc (:string (apply string-append args))) ) ((every? vector? args) (:generator-proc (:list (apply append (map vector->list args)))) ) (else #f ))))))) (define :-dispatch (make-parameter (make-initial-:-dispatch) (lambda (x) (if (procedure? x) x (error "not a procedure" x))))) (define (:-dispatch-ref) (:-dispatch)) (define (:-dispatch-set! dispatch) (:-dispatch dispatch)) (define-syntax : (syntax-rules (index) ((: cc var (index i) arg1 arg ...) (:dispatched cc var (index i) (:-dispatch) arg1 arg ...) ) ((: cc var arg1 arg ...) (:dispatched cc var (:-dispatch) arg1 arg ...) ))) ; ========================================================================== ; The utility comprehensions fold-ec, fold3-ec ; ========================================================================== (define-syntax fold3-ec (syntax-rules (nested) ((fold3-ec x0 (nested q1 ...) q etc1 etc2 etc3 etc ...) (fold3-ec x0 (nested q1 ... q) etc1 etc2 etc3 etc ...) ) ((fold3-ec x0 q1 q2 etc1 etc2 etc3 etc ...) (fold3-ec x0 (nested q1 q2) etc1 etc2 etc3 etc ...) ) ((fold3-ec x0 expression f1 f2) (fold3-ec x0 (nested) expression f1 f2) ) ((fold3-ec x0 qualifier expression f1 f2) (let ((result #f) (empty #t)) (do-ec qualifier (let ((value expression)) ; don't duplicate (if empty (begin (set! result (f1 value)) (set! empty #f) ) (set! result (f2 value result)) ))) (if empty x0 result) )))) (define-syntax fold-ec (syntax-rules (nested) ((fold-ec x0 (nested q1 ...) q etc1 etc2 etc ...) (fold-ec x0 (nested q1 ... q) etc1 etc2 etc ...) ) ((fold-ec x0 q1 q2 etc1 etc2 etc ...) (fold-ec x0 (nested q1 q2) etc1 etc2 etc ...) ) ((fold-ec x0 expression f2) (fold-ec x0 (nested) expression f2) ) ((fold-ec x0 qualifier expression f2) (let ((result x0)) (do-ec qualifier (set! result (f2 expression result))) result )))) ; ========================================================================== ; The comprehensions list-ec string-ec vector-ec etc. ; ========================================================================== (define-syntax list-ec (syntax-rules () ((list-ec etc1 etc ...) (reverse (fold-ec '() etc1 etc ... cons)) ))) ; Alternative: Reverse can safely be replaced by reverse! if you have it. ; ; Alternative: It is possible to construct the result in the correct order ; using set-cdr! to add at the tail. This removes the overhead of copying ; at the end, at the cost of more book-keeping. (define-syntax append-ec (syntax-rules () ((append-ec etc1 etc ...) (apply append (list-ec etc1 etc ...)) ))) (define-syntax string-ec (syntax-rules () ((string-ec etc1 etc ...) (list->string (list-ec etc1 etc ...)) ))) ; Alternative: For very long strings, the intermediate list may be a ; problem. A more space-aware implementation collect the characters ; in an intermediate list and when this list becomes too large it is ; converted into an intermediate string. At the end, the intermediate ; strings are concatenated with string-append. (define-syntax string-append-ec (syntax-rules () ((string-append-ec etc1 etc ...) (apply string-append (list-ec etc1 etc ...)) ))) (define-syntax vector-ec (syntax-rules () ((vector-ec etc1 etc ...) (list->vector (list-ec etc1 etc ...)) ))) ; Comment: A similar approach as for string-ec can be used for vector-ec. ; However, the space overhead for the intermediate list is much lower ; than for string-ec and as there is no vector-append, the intermediate ; vectors must be copied explicitly. (define-syntax vector-of-length-ec (syntax-rules (nested) ((vector-of-length-ec k (nested q1 ...) q etc1 etc ...) (vector-of-length-ec k (nested q1 ... q) etc1 etc ...) ) ((vector-of-length-ec k q1 q2 etc1 etc ...) (vector-of-length-ec k (nested q1 q2) etc1 etc ...) ) ((vector-of-length-ec k expression) (vector-of-length-ec k (nested) expression) ) ((vector-of-length-ec k qualifier expression) (let ((len k)) (let ((vec (make-vector len)) (i 0) ) (do-ec qualifier (if (< i len) (begin (vector-set! vec i expression) (set! i (+ i 1)) ) (error "vector is too short for the comprehension") )) (if (= i len) vec (error "vector is too long for the comprehension") )))))) (define-syntax sum-ec (syntax-rules () ((sum-ec etc1 etc ...) (fold-ec (+) etc1 etc ... +) ))) (define-syntax product-ec (syntax-rules () ((product-ec etc1 etc ...) (fold-ec (*) etc1 etc ... *) ))) (define-syntax min-ec (syntax-rules () ((min-ec etc1 etc ...) (fold3-ec (min) etc1 etc ... min min) ))) (define-syntax max-ec (syntax-rules () ((max-ec etc1 etc ...) (fold3-ec (max) etc1 etc ... max max) ))) (define-syntax last-ec (syntax-rules (nested) ((last-ec default (nested q1 ...) q etc1 etc ...) (last-ec default (nested q1 ... q) etc1 etc ...) ) ((last-ec default q1 q2 etc1 etc ...) (last-ec default (nested q1 q2) etc1 etc ...) ) ((last-ec default expression) (last-ec default (nested) expression) ) ((last-ec default qualifier expression) (let ((result default)) (do-ec qualifier (set! result expression)) result )))) ; ========================================================================== ; The fundamental early-stopping comprehension first-ec ; ========================================================================== (define-syntax first-ec (syntax-rules (nested) ((first-ec default (nested q1 ...) q etc1 etc ...) (first-ec default (nested q1 ... q) etc1 etc ...) ) ((first-ec default q1 q2 etc1 etc ...) (first-ec default (nested q1 q2) etc1 etc ...) ) ((first-ec default expression) (first-ec default (nested) expression) ) ((first-ec default qualifier expression) (let ((result default) (stop #f)) (ec-guarded-do-ec stop (nested qualifier) (begin (set! result expression) (set! stop #t) )) result )))) ; (ec-guarded-do-ec stop (nested q ...) cmd) ; constructs (do-ec q ... cmd) where the generators gen in q ... are ; replaced by (:until gen stop). (define-syntax ec-guarded-do-ec (syntax-rules (nested if not and or begin) ((ec-guarded-do-ec stop (nested (nested q1 ...) q2 ...) cmd) (ec-guarded-do-ec stop (nested q1 ... q2 ...) cmd) ) ((ec-guarded-do-ec stop (nested (if test) q ...) cmd) (if test (ec-guarded-do-ec stop (nested q ...) cmd)) ) ((ec-guarded-do-ec stop (nested (not test) q ...) cmd) (if (not test) (ec-guarded-do-ec stop (nested q ...) cmd)) ) ((ec-guarded-do-ec stop (nested (and test ...) q ...) cmd) (if (and test ...) (ec-guarded-do-ec stop (nested q ...) cmd)) ) ((ec-guarded-do-ec stop (nested (or test ...) q ...) cmd) (if (or test ...) (ec-guarded-do-ec stop (nested q ...) cmd)) ) ((ec-guarded-do-ec stop (nested (begin etc ...) q ...) cmd) (begin etc ... (ec-guarded-do-ec stop (nested q ...) cmd)) ) ((ec-guarded-do-ec stop (nested gen q ...) cmd) (do-ec (:until gen stop) (ec-guarded-do-ec stop (nested q ...) cmd) )) ((ec-guarded-do-ec stop (nested) cmd) (do-ec cmd) ))) ; Alternative: Instead of modifying the generator with :until, it is ; possible to use call-with-current-continuation: ; ; (define-synatx first-ec ; ...same as above... ; ((first-ec default qualifier expression) ; (call-with-current-continuation ; (lambda (cc) ; (do-ec qualifier (cc expression)) ; default ))) )) ; ; This is much simpler but not necessarily as efficient. ; ========================================================================== ; The early-stopping comprehensions any?-ec every?-ec ; ========================================================================== (define-syntax any?-ec (syntax-rules (nested) ((any?-ec (nested q1 ...) q etc1 etc ...) (any?-ec (nested q1 ... q) etc1 etc ...) ) ((any?-ec q1 q2 etc1 etc ...) (any?-ec (nested q1 q2) etc1 etc ...) ) ((any?-ec expression) (any?-ec (nested) expression) ) ((any?-ec qualifier expression) (first-ec #f qualifier (if expression) #t) ))) (define-syntax every?-ec (syntax-rules (nested) ((every?-ec (nested q1 ...) q etc1 etc ...) (every?-ec (nested q1 ... q) etc1 etc ...) ) ((every?-ec q1 q2 etc1 etc ...) (every?-ec (nested q1 q2) etc1 etc ...) ) ((every?-ec expression) (every?-ec (nested) expression) ) ((every?-ec qualifier expression) (first-ec #t qualifier (if (not expression)) #f) ))) ) |
Added srfi/s42/_eager-comprehensions.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s42 eager-comprehensions) (export do-ec list-ec append-ec string-ec string-append-ec vector-ec vector-of-length-ec sum-ec product-ec min-ec max-ec any?-ec every?-ec first-ec last-ec fold-ec fold3-ec : :list :string :vector :integers :range :real-range :char-range :port :dispatched :do :let :parallel :while :until :-dispatch-ref :-dispatch-set! make-initial-:-dispatch dispatch-union :generator-proc) (import (rnrs) (rnrs r5rs) (srfi s39 parameters) (srfi s23 error tricks) (srfi private include)) (SRFI-23-error->R6RS "(library (srfi s42 eager-comprehensions))" (include/resolve ("srfi" "%3a42") "ec.scm")) ) |
Added srfi/s42/design.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 |
; <PLAINTEXT> ; Design Alternatives for Eager Comprehensions ; ============================================ ; ; sebastian.egner@philips.com, Eindhoven, The Netherlands, Feb-2003. ; Scheme R5RS (incl. macros), SRFI-23 (error). ; ; This file contains implementation alternatives for eager comprehensions ; and examples to find out which is better suited for a particular system. ; ; Loading the alternatives in Scheme48 (version 0.57): ; ,open srfi-23 ; ,load ec.scm ; ,load design.scm ; ; Loading the alternatives in PLT/DrScheme (version 202): ; ; open "ec.scm", click Execute ; (load "design.scm") ; ; Loading the alternatives in SCM (version 5d7): ; ; invoke SCM with -v on the command line ; (require 'macro) (require 'record) ; (load "ec.scm") ; (load "design.scm") ; ======================================================================= ; list-ec ; ======================================================================= ; list-ec1 ; uses reverse and fold-ec in the obvious way. ; + one-liner ; + reverse could allocate result contiguous ; - resulting list is allocated twice (unless reverse! is used) (define-syntax list-ec1 (syntax-rules () ((list-ec etc1 etc ...) (reverse (fold-ec '() etc1 etc ... cons)) ))) ; list-ec2 ; uses set-cdr! appending at the end of the result. ; + no copying of the result ; - more book-keeping in the inner loop (define-syntax list-ec2 (syntax-rules (nested) ((list-ec2 (nested q1 ...) q etc1 etc ...) (list-ec2 (nested q1 ... q) etc1 etc ...) ) ((list-ec2 q1 q2 etc1 etc ...) (list-ec2 (nested q1 q2) etc1 etc ...) ) ((list-ec2 expression) (list-ec2 (nested) expression) ) ((list-ec2 qualifier expression) (let ((result #f) (tail (list #f))) (set! result tail) (do-ec qualifier (begin (set-cdr! tail (list expression)) (set! tail (cdr tail)) )) (cdr result) )))) ; comparison ; * The trade-off is book-keeping overhead vs. allocation pressure. ; The difference in the inner loop is (set-cdr!, list, set!) vs. ; (set!, cons), the difference ; * Scheme48 0.57: list-ec1 seems 5% percent faster than list-ec2. ; * PLT 202: list-ec1 seems 25% faster than than list-ec2. (define (perf-list-ec1 iterations n) (do-ec (:range i iterations) (list-ec1 (:range k n) k) )) (define (perf-list-ec2 iterations n) (do-ec (:range i iterations) (list-ec2 (:range k n) k) )) ; try: ; (perf-list-ec1 100000 10) ; (perf-list-ec2 100000 10) ; (perf-list-ec1 100 10000) ; (perf-list-ec2 100 10000) ; ======================================================================= ; string-ec ; ======================================================================= ; string-ec1 ; uses list->string and list-ec in the obvious way. ; + one-liner ; - intermediate list is much bigger than result ; - inherits overhead of list-ec (define-syntax string-ec1 (syntax-rules () ((string-ec1 etc1 etc ...) (list->string (list-ec etc1 etc ...)) ))) ; string-ec2 ; uses string-append on pieces of the result of limited length; ; pieces are constructed with the method of string-ec1 ; + space-efficient for long results ; - overhead for short result ; + potentially very efficient in native code ; - more complicated book-keeping (define-syntax string-ec2 (syntax-rules () ((string-ec2 (nested q1 ...) q etc1 etc ...) (string-ec2 (nested q1 ... q) etc1 etc ...) ) ((string-ec2 q1 q2 etc1 etc ...) (string-ec2 (nested q1 q2) etc1 etc ...) ) ((string-ec2 expression) (string-ec2 (nested) expression) ) ((string-ec2 qualifier expression) (let ((result '()) (piece '()) (len 0) (max-len 1000)) (do-ec qualifier (begin (set! piece (cons expression piece)) (set! len (+ len 1)) (if (= len max-len) (begin (set! result (cons (list->string piece) result)) (set! piece '()) (set! len 0) )))) (apply string-append (reverse (cons (list->string piece) result)) ))))) ; comparison ; * The main question is whether the space overhead for an intermediate ; list is acceptable. If not, string-ec1 is no option. ; * If string-ec2 is used, the question is how to adjust max-len. It ; can either be used as an emergency brake for very long intermediate ; lists or it can be used to keep the total overhead limited. ; * Scheme48 0.57: string-ec1 is 25% faster than string-ec2 for short ; results and still 15% faster for strings of length 10^5. However, ; at 10^6 (for my test configuration) string-ec1 has 'heap overflow', ; whereas string-ec2 has no problem. ; * PLT 202: string-ec1 is 50%..70% faster than string-ec2, both for ; short and for long strings. (define (perf-string-ec1 iterations n) (do-ec (:range i iterations) (string-ec1 (:range k n) #\a) )) (define (perf-string-ec2 iterations n) (do-ec (:range i iterations) (string-ec2 (:range k n) #\a) )) ; try: ; (perf-string-ec1 100000 10) ; (perf-string-ec2 100000 10) ; (perf-string-ec1 10 100000) ; (perf-string-ec2 10 100000) ; (perf-string-ec1 1 1000000) ; (perf-string-ec2 1 1000000) ; ======================================================================= ; first-ec ; ======================================================================= ; first-ec1 ; uses a non-local exit constructed by call-with-current-continuation. ; - stack-based Schemes have problems implementing this efficiently ; + simple, straight-forward, schemeish (define-syntax first-ec1 (syntax-rules (nested) ((first-ec1 default (nested q1 ...) q etc1 etc ...) (first-ec1 default (nested q1 ... q) etc1 etc ...) ) ((first-ec1 default q1 q2 etc1 etc ...) (first-ec1 default (nested q1 q2) etc1 etc ...) ) ((first-ec1 default expression) (first-ec1 default (nested) expression) ) ((first-ec1 default qualifier expression) (call-with-current-continuation (lambda (cc) (do-ec qualifier (cc expression)) default ))))) ; first-ec2 ; uses :until to add an early termination to each generator. ; + as fast as it gets ; - copies part of the functionality of do-ec (define-syntax first-ec2 (syntax-rules (nested) ((first-ec2 default (nested q1 ...) q etc1 etc ...) (first-ec2 default (nested q1 ... q) etc1 etc ...) ) ((first-ec2 default q1 q2 etc1 etc ...) (first-ec2 default (nested q1 q2) etc1 etc ...) ) ((first-ec2 default expression) (first-ec2 default (nested) expression) ) ((first-ec2 default qualifier expression) (let ((result default) (stop #f)) (ec-guarded-do-ec stop (nested qualifier) (begin (set! result expression) (set! stop #t) )) result )))) ; (ec-guarded-do-ec stop (nested q ...) cmd) ; constructs (do-ec q ... cmd) where the generators gen in q ... are ; replaced by (:until gen stop). (define-syntax ec-guarded-do-ec (syntax-rules (nested if not and or begin) ((ec-guarded-do-ec stop (nested (nested q1 ...) q2 ...) cmd) (ec-guarded-do-ec stop (nested q1 ... q2 ...) cmd) ) ((ec-guarded-do-ec stop (nested (if test) q ...) cmd) (if test (ec-guarded-do-ec stop (nested q ...) cmd)) ) ((ec-guarded-do-ec stop (nested (not test) q ...) cmd) (if (not test) (ec-guarded-do-ec stop (nested q ...) cmd)) ) ((ec-guarded-do-ec stop (nested (and test ...) q ...) cmd) (if (and test ...) (ec-guarded-do-ec stop (nested q ...) cmd)) ) ((ec-guarded-do-ec stop (nested (or test ...) q ...) cmd) (if (or test ...) (ec-guarded-do-ec stop (nested q ...) cmd)) ) ((ec-guarded-do-ec stop (nested (begin etc ...) q ...) cmd) (begin etc ... (ec-guarded-do-ec stop (nested q ...) cmd)) ) ((ec-guarded-do-ec stop (nested gen q ...) cmd) (do-ec (:until gen stop) (ec-guarded-do-ec stop (nested q ...) cmd) )) ((ec-guarded-do-ec stop (nested) cmd) (do-ec cmd) ))) ; comparison ; * The main question is whether call/cc is efficient here. ; If it is not, first-ec1 is not an option. ; * We simply run a loop terminating after a few iterations and ; measure the time it takes in total. ; * Scheme48 0.57: first-ec2 seems to be about 15% faster than first-ec1. ; * PLT 202: first-ec2 seems to be about factor 4 faster than first-ec1. (define (perf-first-ec1 iterations) (do-ec (:range i iterations) (first-ec1 0 (:range x 10) (if (= x 5)) #t) )) (define (perf-first-ec2 iterations) (do-ec (:range i iterations) (first-ec2 0 (:range x 10) (if (= x 5)) #t) )) ; try: ; (perf-first-ec1 100000) ; (perf-first-ec2 100000) ; ======================================================================= ; :vector ; ======================================================================= ; :vector ; uses vector->list, append and :list for the multi-argument case. ; + one-liner ; - the enumerated sequence is copied ; - the intermediate list is larger than the arguments (define-syntax :vector1 (syntax-rules (index) ((:vector1 cc var (index i) arg) (:do cc (let ((vec arg) (len 0)) (set! len (vector-length vec))) ((i 0)) (< i len) (let ((var (vector-ref vec i)))) #t ((+ i 1)) )) ((:vector1 cc var (index i) arg1 arg2 arg ...) (:list cc var (index i) (apply append (map vector->list (list arg1 arg2 arg ...))) )) ((:vector1 cc var arg1 arg ...) (:vector1 cc var (index i) arg1 arg ...) ))) ; :vector2 ; runs through a list of vectors with nested loops for multi-arg. ; + no space overhead ; + no copying of the arguments ; - more complicated book-keeping (define-syntax :vector2 (syntax-rules (index) ((:vector2 cc var arg) (:vector2 cc var (index i) arg) ) ((:vector2 cc var (index i) arg) (:do cc (let ((vec arg) (len 0)) (set! len (vector-length vec))) ((i 0)) (< i len) (let ((var (vector-ref vec i)))) #t ((+ i 1)) )) ((:vector2 cc var (index i) arg1 arg2 arg ...) (:parallel cc (:vector2 cc var arg1 arg2 arg ...) (:integers i)) ) ((:vector2 cc var arg1 arg2 arg ...) (:do cc (let ((vec #f) (len 0) (vecs (ec-:vector-filter (list arg1 arg2 arg ...))) )) ((k 0)) (if (< k len) #t (if (null? vecs) #f (begin (set! vec (car vecs)) (set! vecs (cdr vecs)) (set! len (vector-length vec)) (set! k 0) #t ))) (let ((var (vector-ref vec k)))) #t ((+ k 1)) )))) (define (ec-:vector-filter vecs) (if (null? vecs) '() (if (zero? (vector-length (car vecs))) (ec-:vector-filter (cdr vecs)) (cons (car vecs) (ec-:vector-filter (cdr vecs))) ))) ; comparison ; * The trade-off is book-keeping overhead vs. allocation overhead. ; * Scheme48 0.57: For short vectors :vector1 is 20% faster than ; :vector2. For long vectors (10^4) :vector2 is factor 2.8 ; times faster. Break-even around n = 2. ; * PLT 202: For short vectors, :vector1 is factor 2 faster than ; :vector2, for long vectors (10^4) factor 1.6 slower. ; Break-even is around n = 3. (define (perf-:vector1 iterations n) (do-ec (:let v (vector-of-length-ec n (:range i n) i)) (:range i iterations) (do-ec (:vector1 x v v v v v v v v v v) x) )) (define (perf-:vector2 iterations n) (do-ec (:let v (vector-of-length-ec n (:range i n) i)) (:range i iterations) (do-ec (:vector2 x v v v v v v v v v v) x) )) ; try: ; (perf-:vector1 100000 1) ; (perf-:vector2 100000 1) ; (perf-:vector1 100 10000) ; (perf-:vector2 100 10000) |
Added srfi/s42/eager-comprehensions.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s42 eager-comprehensions) (export do-ec list-ec append-ec string-ec string-append-ec vector-ec vector-of-length-ec sum-ec product-ec min-ec max-ec any?-ec every?-ec first-ec last-ec fold-ec fold3-ec : :list :string :vector :integers :range :real-range :char-range :port :dispatched :do :let :parallel :while :until :-dispatch-ref :-dispatch-set! make-initial-:-dispatch dispatch-union :generator-proc) (import (except (rnrs) error) (rnrs r5rs) (srfi s39 parameters) ;; (srfi s23 error tricks) (srfi s23 error) (srfi private include)) ;; (SRFI-23-error->R6RS "(library (srfi s42 eager-comprehensions))" ;; (include/resolve ("srfi" "s42") "ec.scm")) ; <PLAINTEXT> ; Eager Comprehensions in [outer..inner|expr]-Convention ; ====================================================== ; ; sebastian.egner@philips.com, Eindhoven, The Netherlands, 25-Apr-2005 ; Scheme R5RS (incl. macros), SRFI-23 (error). ; ; Modified by Derick Eddington to be able to be included into an R6RS library. ; ; Loading the implementation into Scheme48 0.57: ; ,open srfi-23 ; ,load ec.scm ; ; Loading the implementation into PLT/DrScheme 202: ; ; File > Open ... "ec.scm", click Execute ; ; Loading the implementation into SCM 5d7: ; (require 'macro) (require 'record) ; (load "ec.scm") ; ; Implementation comments: ; * All local (not exported) identifiers are named ec-<something>. ; * This implementation focuses on portability, performance, ; readability, and simplicity roughly in this order. Design ; decisions related to performance are taken for Scheme48. ; * Alternative implementations, Comments and Warnings are ; mentioned after the definition with a heading. ; ========================================================================== ; The fundamental comprehension do-ec ; ========================================================================== ; ; All eager comprehensions are reduced into do-ec and ; all generators are reduced to :do. ; ; We use the following short names for syntactic variables ; q - qualifier ; cc - current continuation, thing to call at the end; ; the CPS is (m (cc ...) arg ...) -> (cc ... expr ...) ; cmd - an expression being evaluated for its side-effects ; expr - an expression ; gen - a generator of an eager comprehension ; ob - outer binding ; oc - outer command ; lb - loop binding ; ne1? - not-end1? (before the payload) ; ib - inner binding ; ic - inner command ; ne2? - not-end2? (after the payload) ; ls - loop step ; etc - more arguments of mixed type ; (do-ec q ... cmd) ; handles nested, if/not/and/or, begin, :let, and calls generator ; macros in CPS to transform them into fully decorated :do. ; The code generation for a :do is delegated to do-ec:do. (define-syntax do-ec (syntax-rules (nested if not and or begin :do let) ; explicit nesting -> implicit nesting ((do-ec (nested q ...) etc ...) (do-ec q ... etc ...) ) ; implicit nesting -> fold do-ec ((do-ec q1 q2 etc1 etc ...) (do-ec q1 (do-ec q2 etc1 etc ...)) ) ; no qualifiers at all -> evaluate cmd once ((do-ec cmd) (begin cmd (if #f #f)) ) ; now (do-ec q cmd) remains ; filter -> make conditional ((do-ec (if test) cmd) (if test (do-ec cmd)) ) ((do-ec (not test) cmd) (if (not test) (do-ec cmd)) ) ((do-ec (and test ...) cmd) (if (and test ...) (do-ec cmd)) ) ((do-ec (or test ...) cmd) (if (or test ...) (do-ec cmd)) ) ; begin -> make a sequence ((do-ec (begin etc ...) cmd) (begin etc ... (do-ec cmd)) ) ; fully decorated :do-generator -> delegate to do-ec:do ((do-ec (:do olet lbs ne1? ilet ne2? lss) cmd) (do-ec:do cmd (:do olet lbs ne1? ilet ne2? lss)) ) ; anything else -> call generator-macro in CPS; reentry at (*) ((do-ec (g arg1 arg ...) cmd) (g (do-ec:do cmd) arg1 arg ...) ))) ; (do-ec:do cmd (:do olet lbs ne1? ilet ne2? lss)) ; generates code for a single fully decorated :do-generator ; with cmd as payload, taking care of special cases. (define-syntax do-ec:do (syntax-rules (:do let) ; reentry point (*) -> generate code ((do-ec:do cmd (:do (let obs oc ...) lbs ne1? (let ibs ic ...) ne2? (ls ...) )) (ec-simplify (let obs oc ... (let loop lbs (ec-simplify (if ne1? (ec-simplify (let ibs ic ... cmd (ec-simplify (if ne2? (loop ls ...) )))))))))) )) ; (ec-simplify <expression>) ; generates potentially more efficient code for <expression>. ; The macro handles if, (begin <command>*), and (let () <command>*) ; and takes care of special cases. (define-syntax ec-simplify (syntax-rules (if not let begin) ; one- and two-sided if ; literal <test> ((ec-simplify (if #t consequent)) consequent ) ((ec-simplify (if #f consequent)) (if #f #f) ) ((ec-simplify (if #t consequent alternate)) consequent ) ((ec-simplify (if #f consequent alternate)) alternate ) ; (not (not <test>)) ((ec-simplify (if (not (not test)) consequent)) (ec-simplify (if test consequent)) ) ((ec-simplify (if (not (not test)) consequent alternate)) (ec-simplify (if test consequent alternate)) ) ; (let () <command>*) ; empty <binding spec>* ((ec-simplify (let () command ...)) (ec-simplify (begin command ...)) ) ; begin ; flatten use helper (ec-simplify 1 done to-do) ((ec-simplify (begin command ...)) (ec-simplify 1 () (command ...)) ) ((ec-simplify 1 done ((begin to-do1 ...) to-do2 ...)) (ec-simplify 1 done (to-do1 ... to-do2 ...)) ) ((ec-simplify 1 (done ...) (to-do1 to-do ...)) (ec-simplify 1 (done ... to-do1) (to-do ...)) ) ; exit helper ((ec-simplify 1 () ()) (if #f #f) ) ((ec-simplify 1 (command) ()) command ) ((ec-simplify 1 (command1 command ...) ()) (begin command1 command ...) ) ; anything else ((ec-simplify expression) expression ))) ; ========================================================================== ; The special generators :do, :let, :parallel, :while, and :until ; ========================================================================== (define-syntax :do (syntax-rules () ; full decorated -> continue with cc, reentry at (*) ((:do (cc ...) olet lbs ne1? ilet ne2? lss) (cc ... (:do olet lbs ne1? ilet ne2? lss)) ) ; short form -> fill in default values ((:do cc lbs ne1? lss) (:do cc (let ()) lbs ne1? (let ()) #t lss) ))) (define-syntax :let (syntax-rules (index) ((:let cc var (index i) expression) (:do cc (let ((var expression) (i 0))) () #t (let ()) #f ()) ) ((:let cc var expression) (:do cc (let ((var expression))) () #t (let ()) #f ()) ))) (define-syntax :parallel (syntax-rules (:do) ((:parallel cc) cc ) ((:parallel cc (g arg1 arg ...) gen ...) (g (:parallel-1 cc (gen ...)) arg1 arg ...) ))) ; (:parallel-1 cc (to-do ...) result [ next ] ) ; iterates over to-do by converting the first generator into ; the :do-generator next and merging next into result. (define-syntax :parallel-1 ; used as (syntax-rules (:do let) ; process next element of to-do, reentry at (**) ((:parallel-1 cc ((g arg1 arg ...) gen ...) result) (g (:parallel-1 cc (gen ...) result) arg1 arg ...) ) ; reentry point (**) -> merge next into result ((:parallel-1 cc gens (:do (let (ob1 ...) oc1 ...) (lb1 ...) ne1?1 (let (ib1 ...) ic1 ...) ne2?1 (ls1 ...) ) (:do (let (ob2 ...) oc2 ...) (lb2 ...) ne1?2 (let (ib2 ...) ic2 ...) ne2?2 (ls2 ...) )) (:parallel-1 cc gens (:do (let (ob1 ... ob2 ...) oc1 ... oc2 ...) (lb1 ... lb2 ...) (and ne1?1 ne1?2) (let (ib1 ... ib2 ...) ic1 ... ic2 ...) (and ne2?1 ne2?2) (ls1 ... ls2 ...) ))) ; no more gens -> continue with cc, reentry at (*) ((:parallel-1 (cc ...) () result) (cc ... result) ))) (define-syntax :while (syntax-rules () ((:while cc (g arg1 arg ...) test) (g (:while-1 cc test) arg1 arg ...) ))) ; (:while-1 cc test (:do ...)) ; modifies the fully decorated :do-generator such that it ; runs while test is a true value. ; The original implementation just replaced ne1? by ; (and ne1? test) as follows: ; ; (define-syntax :while-1 ; (syntax-rules (:do) ; ((:while-1 cc test (:do olet lbs ne1? ilet ne2? lss)) ; (:do cc olet lbs (and ne1? test) ilet ne2? lss) ))) ; ; Unfortunately, this code is wrong because ne1? may depend ; in the inner bindings introduced in ilet, but ne1? is evaluated ; outside of the inner bindings. (Refer to the specification of ; :do to see the structure.) ; The problem manifests itself (as sunnan@handgranat.org ; observed) when the :list-generator is modified: ; ; (do-ec (:while (:list x '(1 2)) (= x 1)) (display x)). ; ; In order to generate proper code, we introduce temporary ; variables saving the values of the inner bindings. The inner ; bindings are executed in a new ne1?, which also evaluates ne1? ; outside the scope of the inner bindings, then the inner commands ; are executed (possibly changing the variables), and then the ; values of the inner bindings are saved and (and ne1? test) is ; returned. In the new ilet, the inner variables are bound and ; initialized and their values are restored. So we construct: ; ; (let (ob .. (ib-tmp #f) ...) ; oc ... ; (let loop (lb ...) ; (if (let (ne1?-value ne1?) ; (let ((ib-var ib-rhs) ...) ; ic ... ; (set! ib-tmp ib-var) ...) ; (and ne1?-value test)) ; (let ((ib-var ib-tmp) ...) ; /payload/ ; (if ne2? ; (loop ls ...) ))))) (define-syntax :while-1 (syntax-rules (:do let) ((:while-1 cc test (:do olet lbs ne1? ilet ne2? lss)) (:while-2 cc test () () () (:do olet lbs ne1? ilet ne2? lss))))) (define-syntax :while-2 (syntax-rules (:do let) ((:while-2 cc test (ib-let ...) (ib-save ...) (ib-restore ...) (:do olet lbs ne1? (let ((ib-var ib-rhs) ib ...) ic ...) ne2? lss)) (:while-2 cc test (ib-let ... (ib-tmp #f)) (ib-save ... (ib-var ib-rhs)) (ib-restore ... (ib-var ib-tmp)) (:do olet lbs ne1? (let (ib ...) ic ... (set! ib-tmp ib-var)) ne2? lss))) ((:while-2 cc test (ib-let ...) (ib-save ...) (ib-restore ...) (:do (let (ob ...) oc ...) lbs ne1? (let () ic ...) ne2? lss)) (:do cc (let (ob ... ib-let ...) oc ...) lbs (let ((ne1?-value ne1?)) (let (ib-save ...) ic ... (and ne1?-value test))) (let (ib-restore ...)) ne2? lss)))) (define-syntax :until (syntax-rules () ((:until cc (g arg1 arg ...) test) (g (:until-1 cc test) arg1 arg ...) ))) (define-syntax :until-1 (syntax-rules (:do) ((:until-1 cc test (:do olet lbs ne1? ilet ne2? lss)) (:do cc olet lbs ne1? ilet (and ne2? (not test)) lss) ))) ; ========================================================================== ; The typed generators :list :string :vector etc. ; ========================================================================== (define-syntax :list (syntax-rules (index) ((:list cc var (index i) arg ...) (:parallel cc (:list var arg ...) (:integers i)) ) ((:list cc var arg1 arg2 arg ...) (:list cc var (append arg1 arg2 arg ...)) ) ((:list cc var arg) (:do cc (let ()) ((t arg)) (not (null? t)) (let ((var (car t)))) #t ((cdr t)) )))) (define-syntax :string (syntax-rules (index) ((:string cc var (index i) arg) (:do cc (let ((str arg) (len 0)) (set! len (string-length str))) ((i 0)) (< i len) (let ((var (string-ref str i)))) #t ((+ i 1)) )) ((:string cc var (index i) arg1 arg2 arg ...) (:string cc var (index i) (string-append arg1 arg2 arg ...)) ) ((:string cc var arg1 arg ...) (:string cc var (index i) arg1 arg ...) ))) ; Alternative: An implementation in the style of :vector can also ; be used for :string. However, it is less interesting as the ; overhead of string-append is much less than for 'vector-append'. (define-syntax :vector (syntax-rules (index) ((:vector cc var arg) (:vector cc var (index i) arg) ) ((:vector cc var (index i) arg) (:do cc (let ((vec arg) (len 0)) (set! len (vector-length vec))) ((i 0)) (< i len) (let ((var (vector-ref vec i)))) #t ((+ i 1)) )) ((:vector cc var (index i) arg1 arg2 arg ...) (:parallel cc (:vector cc var arg1 arg2 arg ...) (:integers i)) ) ((:vector cc var arg1 arg2 arg ...) (:do cc (let ((vec #f) (len 0) (vecs (ec-:vector-filter (list arg1 arg2 arg ...))) )) ((k 0)) (if (< k len) #t (if (null? vecs) #f (begin (set! vec (car vecs)) (set! vecs (cdr vecs)) (set! len (vector-length vec)) (set! k 0) #t ))) (let ((var (vector-ref vec k)))) #t ((+ k 1)) )))) (define (ec-:vector-filter vecs) (if (null? vecs) '() (if (zero? (vector-length (car vecs))) (ec-:vector-filter (cdr vecs)) (cons (car vecs) (ec-:vector-filter (cdr vecs))) ))) ; Alternative: A simpler implementation for :vector uses vector->list ; append and :list in the multi-argument case. Please refer to the ; 'design.scm' for more details. (define-syntax :integers (syntax-rules (index) ((:integers cc var (index i)) (:do cc ((var 0) (i 0)) #t ((+ var 1) (+ i 1))) ) ((:integers cc var) (:do cc ((var 0)) #t ((+ var 1))) ))) (define-syntax :range (syntax-rules (index) ; handle index variable and add optional args ((:range cc var (index i) arg1 arg ...) (:parallel cc (:range var arg1 arg ...) (:integers i)) ) ((:range cc var arg1) (:range cc var 0 arg1 1) ) ((:range cc var arg1 arg2) (:range cc var arg1 arg2 1) ) ; special cases (partially evaluated by hand from general case) ((:range cc var 0 arg2 1) (:do cc (let ((b arg2)) (if (not (and (integer? b) (exact? b))) (error "arguments of :range are not exact integer " "(use :real-range?)" 0 b 1 ))) ((var 0)) (< var b) (let ()) #t ((+ var 1)) )) ((:range cc var 0 arg2 -1) (:do cc (let ((b arg2)) (if (not (and (integer? b) (exact? b))) (error "arguments of :range are not exact integer " "(use :real-range?)" 0 b 1 ))) ((var 0)) (> var b) (let ()) #t ((- var 1)) )) ((:range cc var arg1 arg2 1) (:do cc (let ((a arg1) (b arg2)) (if (not (and (integer? a) (exact? a) (integer? b) (exact? b) )) (error "arguments of :range are not exact integer " "(use :real-range?)" a b 1 )) ) ((var a)) (< var b) (let ()) #t ((+ var 1)) )) ((:range cc var arg1 arg2 -1) (:do cc (let ((a arg1) (b arg2) (s -1) (stop 0)) (if (not (and (integer? a) (exact? a) (integer? b) (exact? b) )) (error "arguments of :range are not exact integer " "(use :real-range?)" a b -1 )) ) ((var a)) (> var b) (let ()) #t ((- var 1)) )) ; the general case ((:range cc var arg1 arg2 arg3) (:do cc (let ((a arg1) (b arg2) (s arg3) (stop 0)) (if (not (and (integer? a) (exact? a) (integer? b) (exact? b) (integer? s) (exact? s) )) (error "arguments of :range are not exact integer " "(use :real-range?)" a b s )) (if (zero? s) (error "step size must not be zero in :range") ) (set! stop (+ a (* (max 0 (ceiling (/ (- b a) s))) s))) ) ((var a)) (not (= var stop)) (let ()) #t ((+ var s)) )))) ; Comment: The macro :range inserts some code to make sure the values ; are exact integers. This overhead has proven very helpful for ; saving users from themselves. (define-syntax :real-range (syntax-rules (index) ; add optional args and index variable ((:real-range cc var arg1) (:real-range cc var (index i) 0 arg1 1) ) ((:real-range cc var (index i) arg1) (:real-range cc var (index i) 0 arg1 1) ) ((:real-range cc var arg1 arg2) (:real-range cc var (index i) arg1 arg2 1) ) ((:real-range cc var (index i) arg1 arg2) (:real-range cc var (index i) arg1 arg2 1) ) ((:real-range cc var arg1 arg2 arg3) (:real-range cc var (index i) arg1 arg2 arg3) ) ; the fully qualified case ((:real-range cc var (index i) arg1 arg2 arg3) (:do cc (let ((a arg1) (b arg2) (s arg3) (istop 0)) (if (not (and (real? a) (real? b) (real? s))) (error "arguments of :real-range are not real" a b s) ) (if (and (exact? a) (or (not (exact? b)) (not (exact? s)))) (set! a (exact->inexact a)) ) (set! istop (/ (- b a) s)) ) ((i 0)) (< i istop) (let ((var (+ a (* s i))))) #t ((+ i 1)) )))) ; Comment: The macro :real-range adapts the exactness of the start ; value in case any of the other values is inexact. This is a ; precaution to avoid (list-ec (: x 0 3.0) x) => '(0 1.0 2.0). (define-syntax :char-range (syntax-rules (index) ((:char-range cc var (index i) arg1 arg2) (:parallel cc (:char-range var arg1 arg2) (:integers i)) ) ((:char-range cc var arg1 arg2) (:do cc (let ((imax (char->integer arg2)))) ((i (char->integer arg1))) (<= i imax) (let ((var (integer->char i)))) #t ((+ i 1)) )))) ; Warning: There is no R5RS-way to implement the :char-range generator ; because the integers obtained by char->integer are not necessarily ; consecutive. We simply assume this anyhow for illustration. (define-syntax :port (syntax-rules (index) ((:port cc var (index i) arg1 arg ...) (:parallel cc (:port var arg1 arg ...) (:integers i)) ) ((:port cc var arg) (:port cc var arg read) ) ((:port cc var arg1 arg2) (:do cc (let ((port arg1) (read-proc arg2))) ((var (read-proc port))) (not (eof-object? var)) (let ()) #t ((read-proc port)) )))) ; ========================================================================== ; The typed generator :dispatched and utilities for constructing dispatchers ; ========================================================================== (define-syntax :dispatched (syntax-rules (index) ((:dispatched cc var (index i) dispatch arg1 arg ...) (:parallel cc (:integers i) (:dispatched var dispatch arg1 arg ...) )) ((:dispatched cc var dispatch arg1 arg ...) (:do cc (let ((d dispatch) (args (list arg1 arg ...)) (g #f) (empty (list #f)) ) (set! g (d args)) (if (not (procedure? g)) (error "unrecognized arguments in dispatching" args (d '()) ))) ((var (g empty))) (not (eq? var empty)) (let ()) #t ((g empty)) )))) ; Comment: The unique object empty is created as a newly allocated ; non-empty list. It is compared using eq? which distinguishes ; the object from any other object, according to R5RS 6.1. (define-syntax :generator-proc (syntax-rules (:do let) ; call g with a variable, reentry at (**) ((:generator-proc (g arg ...)) (g (:generator-proc var) var arg ...) ) ; reentry point (**) -> make the code from a single :do ((:generator-proc var (:do (let obs oc ...) ((lv li) ...) ne1? (let ((i v) ...) ic ...) ne2? (ls ...)) ) (ec-simplify (let obs oc ... (let ((lv li) ... (ne2 #t)) (ec-simplify (let ((i #f) ...) ; v not yet valid (lambda (empty) (if (and ne1? ne2) (ec-simplify (begin (set! i v) ... ic ... (let ((value var)) (ec-simplify (if ne2? (ec-simplify (begin (set! lv ls) ...) ) (set! ne2 #f) )) value ))) empty )))))))) ; silence warnings of some macro expanders ((:generator-proc var) (error "illegal macro call") ))) (define (dispatch-union d1 d2) (lambda (args) (let ((g1 (d1 args)) (g2 (d2 args))) (if g1 (if g2 (if (null? args) (append (if (list? g1) g1 (list g1)) (if (list? g2) g2 (list g2)) ) (error "dispatching conflict" args (d1 '()) (d2 '())) ) g1 ) (if g2 g2 #f) )))) ; ========================================================================== ; The dispatching generator : ; ========================================================================== (define (make-initial-:-dispatch) (lambda (args) (case (length args) ((0) 'SRFI42) ((1) (let ((a1 (car args))) (cond ((list? a1) (:generator-proc (:list a1)) ) ((string? a1) (:generator-proc (:string a1)) ) ((vector? a1) (:generator-proc (:vector a1)) ) ((and (integer? a1) (exact? a1)) (:generator-proc (:range a1)) ) ((real? a1) (:generator-proc (:real-range a1)) ) ((input-port? a1) (:generator-proc (:port a1)) ) (else #f )))) ((2) (let ((a1 (car args)) (a2 (cadr args))) (cond ((and (list? a1) (list? a2)) (:generator-proc (:list a1 a2)) ) ((and (string? a1) (string? a2)) (:generator-proc (:string a1 a2)) ) ((and (vector? a1) (vector? a2)) (:generator-proc (:vector a1 a2)) ) ((and (integer? a1) (exact? a1) (integer? a2) (exact? a2)) (:generator-proc (:range a1 a2)) ) ((and (real? a1) (real? a2)) (:generator-proc (:real-range a1 a2)) ) ((and (char? a1) (char? a2)) (:generator-proc (:char-range a1 a2)) ) ((and (input-port? a1) (procedure? a2)) (:generator-proc (:port a1 a2)) ) (else #f )))) ((3) (let ((a1 (car args)) (a2 (cadr args)) (a3 (caddr args))) (cond ((and (list? a1) (list? a2) (list? a3)) (:generator-proc (:list a1 a2 a3)) ) ((and (string? a1) (string? a2) (string? a3)) (:generator-proc (:string a1 a2 a3)) ) ((and (vector? a1) (vector? a2) (vector? a3)) (:generator-proc (:vector a1 a2 a3)) ) ((and (integer? a1) (exact? a1) (integer? a2) (exact? a2) (integer? a3) (exact? a3)) (:generator-proc (:range a1 a2 a3)) ) ((and (real? a1) (real? a2) (real? a3)) (:generator-proc (:real-range a1 a2 a3)) ) (else #f )))) (else (letrec ((every? (lambda (pred args) (if (null? args) #t (and (pred (car args)) (every? pred (cdr args)) ))))) (cond ((every? list? args) (:generator-proc (:list (apply append args))) ) ((every? string? args) (:generator-proc (:string (apply string-append args))) ) ((every? vector? args) (:generator-proc (:list (apply append (map vector->list args)))) ) (else #f ))))))) (define :-dispatch (make-parameter (make-initial-:-dispatch) (lambda (x) (if (procedure? x) x (error "not a procedure" x))))) (define (:-dispatch-ref) (:-dispatch)) (define (:-dispatch-set! dispatch) (:-dispatch dispatch)) (define-syntax : (syntax-rules (index) ((: cc var (index i) arg1 arg ...) (:dispatched cc var (index i) (:-dispatch) arg1 arg ...) ) ((: cc var arg1 arg ...) (:dispatched cc var (:-dispatch) arg1 arg ...) ))) ; ========================================================================== ; The utility comprehensions fold-ec, fold3-ec ; ========================================================================== (define-syntax fold3-ec (syntax-rules (nested) ((fold3-ec x0 (nested q1 ...) q etc1 etc2 etc3 etc ...) (fold3-ec x0 (nested q1 ... q) etc1 etc2 etc3 etc ...) ) ((fold3-ec x0 q1 q2 etc1 etc2 etc3 etc ...) (fold3-ec x0 (nested q1 q2) etc1 etc2 etc3 etc ...) ) ((fold3-ec x0 expression f1 f2) (fold3-ec x0 (nested) expression f1 f2) ) ((fold3-ec x0 qualifier expression f1 f2) (let ((result #f) (empty #t)) (do-ec qualifier (let ((value expression)) ; don't duplicate (if empty (begin (set! result (f1 value)) (set! empty #f) ) (set! result (f2 value result)) ))) (if empty x0 result) )))) (define-syntax fold-ec (syntax-rules (nested) ((fold-ec x0 (nested q1 ...) q etc1 etc2 etc ...) (fold-ec x0 (nested q1 ... q) etc1 etc2 etc ...) ) ((fold-ec x0 q1 q2 etc1 etc2 etc ...) (fold-ec x0 (nested q1 q2) etc1 etc2 etc ...) ) ((fold-ec x0 expression f2) (fold-ec x0 (nested) expression f2) ) ((fold-ec x0 qualifier expression f2) (let ((result x0)) (do-ec qualifier (set! result (f2 expression result))) result )))) ; ========================================================================== ; The comprehensions list-ec string-ec vector-ec etc. ; ========================================================================== (define-syntax list-ec (syntax-rules () ((list-ec etc1 etc ...) (reverse (fold-ec '() etc1 etc ... cons)) ))) ; Alternative: Reverse can safely be replaced by reverse! if you have it. ; ; Alternative: It is possible to construct the result in the correct order ; using set-cdr! to add at the tail. This removes the overhead of copying ; at the end, at the cost of more book-keeping. (define-syntax append-ec (syntax-rules () ((append-ec etc1 etc ...) (apply append (list-ec etc1 etc ...)) ))) (define-syntax string-ec (syntax-rules () ((string-ec etc1 etc ...) (list->string (list-ec etc1 etc ...)) ))) ; Alternative: For very long strings, the intermediate list may be a ; problem. A more space-aware implementation collect the characters ; in an intermediate list and when this list becomes too large it is ; converted into an intermediate string. At the end, the intermediate ; strings are concatenated with string-append. (define-syntax string-append-ec (syntax-rules () ((string-append-ec etc1 etc ...) (apply string-append (list-ec etc1 etc ...)) ))) (define-syntax vector-ec (syntax-rules () ((vector-ec etc1 etc ...) (list->vector (list-ec etc1 etc ...)) ))) ; Comment: A similar approach as for string-ec can be used for vector-ec. ; However, the space overhead for the intermediate list is much lower ; than for string-ec and as there is no vector-append, the intermediate ; vectors must be copied explicitly. (define-syntax vector-of-length-ec (syntax-rules (nested) ((vector-of-length-ec k (nested q1 ...) q etc1 etc ...) (vector-of-length-ec k (nested q1 ... q) etc1 etc ...) ) ((vector-of-length-ec k q1 q2 etc1 etc ...) (vector-of-length-ec k (nested q1 q2) etc1 etc ...) ) ((vector-of-length-ec k expression) (vector-of-length-ec k (nested) expression) ) ((vector-of-length-ec k qualifier expression) (let ((len k)) (let ((vec (make-vector len)) (i 0) ) (do-ec qualifier (if (< i len) (begin (vector-set! vec i expression) (set! i (+ i 1)) ) (error "vector is too short for the comprehension") )) (if (= i len) vec (error "vector is too long for the comprehension") )))))) (define-syntax sum-ec (syntax-rules () ((sum-ec etc1 etc ...) (fold-ec (+) etc1 etc ... +) ))) (define-syntax product-ec (syntax-rules () ((product-ec etc1 etc ...) (fold-ec (*) etc1 etc ... *) ))) (define-syntax min-ec (syntax-rules () ((min-ec etc1 etc ...) (fold3-ec (min) etc1 etc ... min min) ))) (define-syntax max-ec (syntax-rules () ((max-ec etc1 etc ...) (fold3-ec (max) etc1 etc ... max max) ))) (define-syntax last-ec (syntax-rules (nested) ((last-ec default (nested q1 ...) q etc1 etc ...) (last-ec default (nested q1 ... q) etc1 etc ...) ) ((last-ec default q1 q2 etc1 etc ...) (last-ec default (nested q1 q2) etc1 etc ...) ) ((last-ec default expression) (last-ec default (nested) expression) ) ((last-ec default qualifier expression) (let ((result default)) (do-ec qualifier (set! result expression)) result )))) ; ========================================================================== ; The fundamental early-stopping comprehension first-ec ; ========================================================================== (define-syntax first-ec (syntax-rules (nested) ((first-ec default (nested q1 ...) q etc1 etc ...) (first-ec default (nested q1 ... q) etc1 etc ...) ) ((first-ec default q1 q2 etc1 etc ...) (first-ec default (nested q1 q2) etc1 etc ...) ) ((first-ec default expression) (first-ec default (nested) expression) ) ((first-ec default qualifier expression) (let ((result default) (stop #f)) (ec-guarded-do-ec stop (nested qualifier) (begin (set! result expression) (set! stop #t) )) result )))) ; (ec-guarded-do-ec stop (nested q ...) cmd) ; constructs (do-ec q ... cmd) where the generators gen in q ... are ; replaced by (:until gen stop). (define-syntax ec-guarded-do-ec (syntax-rules (nested if not and or begin) ((ec-guarded-do-ec stop (nested (nested q1 ...) q2 ...) cmd) (ec-guarded-do-ec stop (nested q1 ... q2 ...) cmd) ) ((ec-guarded-do-ec stop (nested (if test) q ...) cmd) (if test (ec-guarded-do-ec stop (nested q ...) cmd)) ) ((ec-guarded-do-ec stop (nested (not test) q ...) cmd) (if (not test) (ec-guarded-do-ec stop (nested q ...) cmd)) ) ((ec-guarded-do-ec stop (nested (and test ...) q ...) cmd) (if (and test ...) (ec-guarded-do-ec stop (nested q ...) cmd)) ) ((ec-guarded-do-ec stop (nested (or test ...) q ...) cmd) (if (or test ...) (ec-guarded-do-ec stop (nested q ...) cmd)) ) ((ec-guarded-do-ec stop (nested (begin etc ...) q ...) cmd) (begin etc ... (ec-guarded-do-ec stop (nested q ...) cmd)) ) ((ec-guarded-do-ec stop (nested gen q ...) cmd) (do-ec (:until gen stop) (ec-guarded-do-ec stop (nested q ...) cmd) )) ((ec-guarded-do-ec stop (nested) cmd) (do-ec cmd) ))) ; Alternative: Instead of modifying the generator with :until, it is ; possible to use call-with-current-continuation: ; ; (define-synatx first-ec ; ...same as above... ; ((first-ec default qualifier expression) ; (call-with-current-continuation ; (lambda (cc) ; (do-ec qualifier (cc expression)) ; default ))) )) ; ; This is much simpler but not necessarily as efficient. ; ========================================================================== ; The early-stopping comprehensions any?-ec every?-ec ; ========================================================================== (define-syntax any?-ec (syntax-rules (nested) ((any?-ec (nested q1 ...) q etc1 etc ...) (any?-ec (nested q1 ... q) etc1 etc ...) ) ((any?-ec q1 q2 etc1 etc ...) (any?-ec (nested q1 q2) etc1 etc ...) ) ((any?-ec expression) (any?-ec (nested) expression) ) ((any?-ec qualifier expression) (first-ec #f qualifier (if expression) #t) ))) (define-syntax every?-ec (syntax-rules (nested) ((every?-ec (nested q1 ...) q etc1 etc ...) (every?-ec (nested q1 ... q) etc1 etc ...) ) ((every?-ec q1 q2 etc1 etc ...) (every?-ec (nested q1 q2) etc1 etc ...) ) ((every?-ec expression) (every?-ec (nested) expression) ) ((every?-ec qualifier expression) (first-ec #t qualifier (if (not expression)) #f) ))) ) |
Added srfi/s42/ec.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 |
; <PLAINTEXT> ; Eager Comprehensions in [outer..inner|expr]-Convention ; ====================================================== ; ; sebastian.egner@philips.com, Eindhoven, The Netherlands, 25-Apr-2005 ; Scheme R5RS (incl. macros), SRFI-23 (error). ; ; Modified by Derick Eddington to be able to be included into an R6RS library. ; ; Loading the implementation into Scheme48 0.57: ; ,open srfi-23 ; ,load ec.scm ; ; Loading the implementation into PLT/DrScheme 202: ; ; File > Open ... "ec.scm", click Execute ; ; Loading the implementation into SCM 5d7: ; (require 'macro) (require 'record) ; (load "ec.scm") ; ; Implementation comments: ; * All local (not exported) identifiers are named ec-<something>. ; * This implementation focuses on portability, performance, ; readability, and simplicity roughly in this order. Design ; decisions related to performance are taken for Scheme48. ; * Alternative implementations, Comments and Warnings are ; mentioned after the definition with a heading. ; ========================================================================== ; The fundamental comprehension do-ec ; ========================================================================== ; ; All eager comprehensions are reduced into do-ec and ; all generators are reduced to :do. ; ; We use the following short names for syntactic variables ; q - qualifier ; cc - current continuation, thing to call at the end; ; the CPS is (m (cc ...) arg ...) -> (cc ... expr ...) ; cmd - an expression being evaluated for its side-effects ; expr - an expression ; gen - a generator of an eager comprehension ; ob - outer binding ; oc - outer command ; lb - loop binding ; ne1? - not-end1? (before the payload) ; ib - inner binding ; ic - inner command ; ne2? - not-end2? (after the payload) ; ls - loop step ; etc - more arguments of mixed type ; (do-ec q ... cmd) ; handles nested, if/not/and/or, begin, :let, and calls generator ; macros in CPS to transform them into fully decorated :do. ; The code generation for a :do is delegated to do-ec:do. (define-syntax do-ec (syntax-rules (nested if not and or begin :do let) ; explicit nesting -> implicit nesting ((do-ec (nested q ...) etc ...) (do-ec q ... etc ...) ) ; implicit nesting -> fold do-ec ((do-ec q1 q2 etc1 etc ...) (do-ec q1 (do-ec q2 etc1 etc ...)) ) ; no qualifiers at all -> evaluate cmd once ((do-ec cmd) (begin cmd (if #f #f)) ) ; now (do-ec q cmd) remains ; filter -> make conditional ((do-ec (if test) cmd) (if test (do-ec cmd)) ) ((do-ec (not test) cmd) (if (not test) (do-ec cmd)) ) ((do-ec (and test ...) cmd) (if (and test ...) (do-ec cmd)) ) ((do-ec (or test ...) cmd) (if (or test ...) (do-ec cmd)) ) ; begin -> make a sequence ((do-ec (begin etc ...) cmd) (begin etc ... (do-ec cmd)) ) ; fully decorated :do-generator -> delegate to do-ec:do ((do-ec (:do olet lbs ne1? ilet ne2? lss) cmd) (do-ec:do cmd (:do olet lbs ne1? ilet ne2? lss)) ) ; anything else -> call generator-macro in CPS; reentry at (*) ((do-ec (g arg1 arg ...) cmd) (g (do-ec:do cmd) arg1 arg ...) ))) ; (do-ec:do cmd (:do olet lbs ne1? ilet ne2? lss)) ; generates code for a single fully decorated :do-generator ; with cmd as payload, taking care of special cases. (define-syntax do-ec:do (syntax-rules (:do let) ; reentry point (*) -> generate code ((do-ec:do cmd (:do (let obs oc ...) lbs ne1? (let ibs ic ...) ne2? (ls ...) )) (ec-simplify (let obs oc ... (let loop lbs (ec-simplify (if ne1? (ec-simplify (let ibs ic ... cmd (ec-simplify (if ne2? (loop ls ...) )))))))))) )) ; (ec-simplify <expression>) ; generates potentially more efficient code for <expression>. ; The macro handles if, (begin <command>*), and (let () <command>*) ; and takes care of special cases. (define-syntax ec-simplify (syntax-rules (if not let begin) ; one- and two-sided if ; literal <test> ((ec-simplify (if #t consequent)) consequent ) ((ec-simplify (if #f consequent)) (if #f #f) ) ((ec-simplify (if #t consequent alternate)) consequent ) ((ec-simplify (if #f consequent alternate)) alternate ) ; (not (not <test>)) ((ec-simplify (if (not (not test)) consequent)) (ec-simplify (if test consequent)) ) ((ec-simplify (if (not (not test)) consequent alternate)) (ec-simplify (if test consequent alternate)) ) ; (let () <command>*) ; empty <binding spec>* ((ec-simplify (let () command ...)) (ec-simplify (begin command ...)) ) ; begin ; flatten use helper (ec-simplify 1 done to-do) ((ec-simplify (begin command ...)) (ec-simplify 1 () (command ...)) ) ((ec-simplify 1 done ((begin to-do1 ...) to-do2 ...)) (ec-simplify 1 done (to-do1 ... to-do2 ...)) ) ((ec-simplify 1 (done ...) (to-do1 to-do ...)) (ec-simplify 1 (done ... to-do1) (to-do ...)) ) ; exit helper ((ec-simplify 1 () ()) (if #f #f) ) ((ec-simplify 1 (command) ()) command ) ((ec-simplify 1 (command1 command ...) ()) (begin command1 command ...) ) ; anything else ((ec-simplify expression) expression ))) ; ========================================================================== ; The special generators :do, :let, :parallel, :while, and :until ; ========================================================================== (define-syntax :do (syntax-rules () ; full decorated -> continue with cc, reentry at (*) ((:do (cc ...) olet lbs ne1? ilet ne2? lss) (cc ... (:do olet lbs ne1? ilet ne2? lss)) ) ; short form -> fill in default values ((:do cc lbs ne1? lss) (:do cc (let ()) lbs ne1? (let ()) #t lss) ))) (define-syntax :let (syntax-rules (index) ((:let cc var (index i) expression) (:do cc (let ((var expression) (i 0))) () #t (let ()) #f ()) ) ((:let cc var expression) (:do cc (let ((var expression))) () #t (let ()) #f ()) ))) (define-syntax :parallel (syntax-rules (:do) ((:parallel cc) cc ) ((:parallel cc (g arg1 arg ...) gen ...) (g (:parallel-1 cc (gen ...)) arg1 arg ...) ))) ; (:parallel-1 cc (to-do ...) result [ next ] ) ; iterates over to-do by converting the first generator into ; the :do-generator next and merging next into result. (define-syntax :parallel-1 ; used as (syntax-rules (:do let) ; process next element of to-do, reentry at (**) ((:parallel-1 cc ((g arg1 arg ...) gen ...) result) (g (:parallel-1 cc (gen ...) result) arg1 arg ...) ) ; reentry point (**) -> merge next into result ((:parallel-1 cc gens (:do (let (ob1 ...) oc1 ...) (lb1 ...) ne1?1 (let (ib1 ...) ic1 ...) ne2?1 (ls1 ...) ) (:do (let (ob2 ...) oc2 ...) (lb2 ...) ne1?2 (let (ib2 ...) ic2 ...) ne2?2 (ls2 ...) )) (:parallel-1 cc gens (:do (let (ob1 ... ob2 ...) oc1 ... oc2 ...) (lb1 ... lb2 ...) (and ne1?1 ne1?2) (let (ib1 ... ib2 ...) ic1 ... ic2 ...) (and ne2?1 ne2?2) (ls1 ... ls2 ...) ))) ; no more gens -> continue with cc, reentry at (*) ((:parallel-1 (cc ...) () result) (cc ... result) ))) (define-syntax :while (syntax-rules () ((:while cc (g arg1 arg ...) test) (g (:while-1 cc test) arg1 arg ...) ))) ; (:while-1 cc test (:do ...)) ; modifies the fully decorated :do-generator such that it ; runs while test is a true value. ; The original implementation just replaced ne1? by ; (and ne1? test) as follows: ; ; (define-syntax :while-1 ; (syntax-rules (:do) ; ((:while-1 cc test (:do olet lbs ne1? ilet ne2? lss)) ; (:do cc olet lbs (and ne1? test) ilet ne2? lss) ))) ; ; Unfortunately, this code is wrong because ne1? may depend ; in the inner bindings introduced in ilet, but ne1? is evaluated ; outside of the inner bindings. (Refer to the specification of ; :do to see the structure.) ; The problem manifests itself (as sunnan@handgranat.org ; observed) when the :list-generator is modified: ; ; (do-ec (:while (:list x '(1 2)) (= x 1)) (display x)). ; ; In order to generate proper code, we introduce temporary ; variables saving the values of the inner bindings. The inner ; bindings are executed in a new ne1?, which also evaluates ne1? ; outside the scope of the inner bindings, then the inner commands ; are executed (possibly changing the variables), and then the ; values of the inner bindings are saved and (and ne1? test) is ; returned. In the new ilet, the inner variables are bound and ; initialized and their values are restored. So we construct: ; ; (let (ob .. (ib-tmp #f) ...) ; oc ... ; (let loop (lb ...) ; (if (let (ne1?-value ne1?) ; (let ((ib-var ib-rhs) ...) ; ic ... ; (set! ib-tmp ib-var) ...) ; (and ne1?-value test)) ; (let ((ib-var ib-tmp) ...) ; /payload/ ; (if ne2? ; (loop ls ...) ))))) (define-syntax :while-1 (syntax-rules (:do let) ((:while-1 cc test (:do olet lbs ne1? ilet ne2? lss)) (:while-2 cc test () () () (:do olet lbs ne1? ilet ne2? lss))))) (define-syntax :while-2 (syntax-rules (:do let) ((:while-2 cc test (ib-let ...) (ib-save ...) (ib-restore ...) (:do olet lbs ne1? (let ((ib-var ib-rhs) ib ...) ic ...) ne2? lss)) (:while-2 cc test (ib-let ... (ib-tmp #f)) (ib-save ... (ib-var ib-rhs)) (ib-restore ... (ib-var ib-tmp)) (:do olet lbs ne1? (let (ib ...) ic ... (set! ib-tmp ib-var)) ne2? lss))) ((:while-2 cc test (ib-let ...) (ib-save ...) (ib-restore ...) (:do (let (ob ...) oc ...) lbs ne1? (let () ic ...) ne2? lss)) (:do cc (let (ob ... ib-let ...) oc ...) lbs (let ((ne1?-value ne1?)) (let (ib-save ...) ic ... (and ne1?-value test))) (let (ib-restore ...)) ne2? lss)))) (define-syntax :until (syntax-rules () ((:until cc (g arg1 arg ...) test) (g (:until-1 cc test) arg1 arg ...) ))) (define-syntax :until-1 (syntax-rules (:do) ((:until-1 cc test (:do olet lbs ne1? ilet ne2? lss)) (:do cc olet lbs ne1? ilet (and ne2? (not test)) lss) ))) ; ========================================================================== ; The typed generators :list :string :vector etc. ; ========================================================================== (define-syntax :list (syntax-rules (index) ((:list cc var (index i) arg ...) (:parallel cc (:list var arg ...) (:integers i)) ) ((:list cc var arg1 arg2 arg ...) (:list cc var (append arg1 arg2 arg ...)) ) ((:list cc var arg) (:do cc (let ()) ((t arg)) (not (null? t)) (let ((var (car t)))) #t ((cdr t)) )))) (define-syntax :string (syntax-rules (index) ((:string cc var (index i) arg) (:do cc (let ((str arg) (len 0)) (set! len (string-length str))) ((i 0)) (< i len) (let ((var (string-ref str i)))) #t ((+ i 1)) )) ((:string cc var (index i) arg1 arg2 arg ...) (:string cc var (index i) (string-append arg1 arg2 arg ...)) ) ((:string cc var arg1 arg ...) (:string cc var (index i) arg1 arg ...) ))) ; Alternative: An implementation in the style of :vector can also ; be used for :string. However, it is less interesting as the ; overhead of string-append is much less than for 'vector-append'. (define-syntax :vector (syntax-rules (index) ((:vector cc var arg) (:vector cc var (index i) arg) ) ((:vector cc var (index i) arg) (:do cc (let ((vec arg) (len 0)) (set! len (vector-length vec))) ((i 0)) (< i len) (let ((var (vector-ref vec i)))) #t ((+ i 1)) )) ((:vector cc var (index i) arg1 arg2 arg ...) (:parallel cc (:vector cc var arg1 arg2 arg ...) (:integers i)) ) ((:vector cc var arg1 arg2 arg ...) (:do cc (let ((vec #f) (len 0) (vecs (ec-:vector-filter (list arg1 arg2 arg ...))) )) ((k 0)) (if (< k len) #t (if (null? vecs) #f (begin (set! vec (car vecs)) (set! vecs (cdr vecs)) (set! len (vector-length vec)) (set! k 0) #t ))) (let ((var (vector-ref vec k)))) #t ((+ k 1)) )))) (define (ec-:vector-filter vecs) (if (null? vecs) '() (if (zero? (vector-length (car vecs))) (ec-:vector-filter (cdr vecs)) (cons (car vecs) (ec-:vector-filter (cdr vecs))) ))) ; Alternative: A simpler implementation for :vector uses vector->list ; append and :list in the multi-argument case. Please refer to the ; 'design.scm' for more details. (define-syntax :integers (syntax-rules (index) ((:integers cc var (index i)) (:do cc ((var 0) (i 0)) #t ((+ var 1) (+ i 1))) ) ((:integers cc var) (:do cc ((var 0)) #t ((+ var 1))) ))) (define-syntax :range (syntax-rules (index) ; handle index variable and add optional args ((:range cc var (index i) arg1 arg ...) (:parallel cc (:range var arg1 arg ...) (:integers i)) ) ((:range cc var arg1) (:range cc var 0 arg1 1) ) ((:range cc var arg1 arg2) (:range cc var arg1 arg2 1) ) ; special cases (partially evaluated by hand from general case) ((:range cc var 0 arg2 1) (:do cc (let ((b arg2)) (if (not (and (integer? b) (exact? b))) (error "arguments of :range are not exact integer " "(use :real-range?)" 0 b 1 ))) ((var 0)) (< var b) (let ()) #t ((+ var 1)) )) ((:range cc var 0 arg2 -1) (:do cc (let ((b arg2)) (if (not (and (integer? b) (exact? b))) (error "arguments of :range are not exact integer " "(use :real-range?)" 0 b 1 ))) ((var 0)) (> var b) (let ()) #t ((- var 1)) )) ((:range cc var arg1 arg2 1) (:do cc (let ((a arg1) (b arg2)) (if (not (and (integer? a) (exact? a) (integer? b) (exact? b) )) (error "arguments of :range are not exact integer " "(use :real-range?)" a b 1 )) ) ((var a)) (< var b) (let ()) #t ((+ var 1)) )) ((:range cc var arg1 arg2 -1) (:do cc (let ((a arg1) (b arg2) (s -1) (stop 0)) (if (not (and (integer? a) (exact? a) (integer? b) (exact? b) )) (error "arguments of :range are not exact integer " "(use :real-range?)" a b -1 )) ) ((var a)) (> var b) (let ()) #t ((- var 1)) )) ; the general case ((:range cc var arg1 arg2 arg3) (:do cc (let ((a arg1) (b arg2) (s arg3) (stop 0)) (if (not (and (integer? a) (exact? a) (integer? b) (exact? b) (integer? s) (exact? s) )) (error "arguments of :range are not exact integer " "(use :real-range?)" a b s )) (if (zero? s) (error "step size must not be zero in :range") ) (set! stop (+ a (* (max 0 (ceiling (/ (- b a) s))) s))) ) ((var a)) (not (= var stop)) (let ()) #t ((+ var s)) )))) ; Comment: The macro :range inserts some code to make sure the values ; are exact integers. This overhead has proven very helpful for ; saving users from themselves. (define-syntax :real-range (syntax-rules (index) ; add optional args and index variable ((:real-range cc var arg1) (:real-range cc var (index i) 0 arg1 1) ) ((:real-range cc var (index i) arg1) (:real-range cc var (index i) 0 arg1 1) ) ((:real-range cc var arg1 arg2) (:real-range cc var (index i) arg1 arg2 1) ) ((:real-range cc var (index i) arg1 arg2) (:real-range cc var (index i) arg1 arg2 1) ) ((:real-range cc var arg1 arg2 arg3) (:real-range cc var (index i) arg1 arg2 arg3) ) ; the fully qualified case ((:real-range cc var (index i) arg1 arg2 arg3) (:do cc (let ((a arg1) (b arg2) (s arg3) (istop 0)) (if (not (and (real? a) (real? b) (real? s))) (error "arguments of :real-range are not real" a b s) ) (if (and (exact? a) (or (not (exact? b)) (not (exact? s)))) (set! a (exact->inexact a)) ) (set! istop (/ (- b a) s)) ) ((i 0)) (< i istop) (let ((var (+ a (* s i))))) #t ((+ i 1)) )))) ; Comment: The macro :real-range adapts the exactness of the start ; value in case any of the other values is inexact. This is a ; precaution to avoid (list-ec (: x 0 3.0) x) => '(0 1.0 2.0). (define-syntax :char-range (syntax-rules (index) ((:char-range cc var (index i) arg1 arg2) (:parallel cc (:char-range var arg1 arg2) (:integers i)) ) ((:char-range cc var arg1 arg2) (:do cc (let ((imax (char->integer arg2)))) ((i (char->integer arg1))) (<= i imax) (let ((var (integer->char i)))) #t ((+ i 1)) )))) ; Warning: There is no R5RS-way to implement the :char-range generator ; because the integers obtained by char->integer are not necessarily ; consecutive. We simply assume this anyhow for illustration. (define-syntax :port (syntax-rules (index) ((:port cc var (index i) arg1 arg ...) (:parallel cc (:port var arg1 arg ...) (:integers i)) ) ((:port cc var arg) (:port cc var arg read) ) ((:port cc var arg1 arg2) (:do cc (let ((port arg1) (read-proc arg2))) ((var (read-proc port))) (not (eof-object? var)) (let ()) #t ((read-proc port)) )))) ; ========================================================================== ; The typed generator :dispatched and utilities for constructing dispatchers ; ========================================================================== (define-syntax :dispatched (syntax-rules (index) ((:dispatched cc var (index i) dispatch arg1 arg ...) (:parallel cc (:integers i) (:dispatched var dispatch arg1 arg ...) )) ((:dispatched cc var dispatch arg1 arg ...) (:do cc (let ((d dispatch) (args (list arg1 arg ...)) (g #f) (empty (list #f)) ) (set! g (d args)) (if (not (procedure? g)) (error "unrecognized arguments in dispatching" args (d '()) ))) ((var (g empty))) (not (eq? var empty)) (let ()) #t ((g empty)) )))) ; Comment: The unique object empty is created as a newly allocated ; non-empty list. It is compared using eq? which distinguishes ; the object from any other object, according to R5RS 6.1. (define-syntax :generator-proc (syntax-rules (:do let) ; call g with a variable, reentry at (**) ((:generator-proc (g arg ...)) (g (:generator-proc var) var arg ...) ) ; reentry point (**) -> make the code from a single :do ((:generator-proc var (:do (let obs oc ...) ((lv li) ...) ne1? (let ((i v) ...) ic ...) ne2? (ls ...)) ) (ec-simplify (let obs oc ... (let ((lv li) ... (ne2 #t)) (ec-simplify (let ((i #f) ...) ; v not yet valid (lambda (empty) (if (and ne1? ne2) (ec-simplify (begin (set! i v) ... ic ... (let ((value var)) (ec-simplify (if ne2? (ec-simplify (begin (set! lv ls) ...) ) (set! ne2 #f) )) value ))) empty )))))))) ; silence warnings of some macro expanders ((:generator-proc var) (error "illegal macro call") ))) (define (dispatch-union d1 d2) (lambda (args) (let ((g1 (d1 args)) (g2 (d2 args))) (if g1 (if g2 (if (null? args) (append (if (list? g1) g1 (list g1)) (if (list? g2) g2 (list g2)) ) (error "dispatching conflict" args (d1 '()) (d2 '())) ) g1 ) (if g2 g2 #f) )))) ; ========================================================================== ; The dispatching generator : ; ========================================================================== (define (make-initial-:-dispatch) (lambda (args) (case (length args) ((0) 'SRFI42) ((1) (let ((a1 (car args))) (cond ((list? a1) (:generator-proc (:list a1)) ) ((string? a1) (:generator-proc (:string a1)) ) ((vector? a1) (:generator-proc (:vector a1)) ) ((and (integer? a1) (exact? a1)) (:generator-proc (:range a1)) ) ((real? a1) (:generator-proc (:real-range a1)) ) ((input-port? a1) (:generator-proc (:port a1)) ) (else #f )))) ((2) (let ((a1 (car args)) (a2 (cadr args))) (cond ((and (list? a1) (list? a2)) (:generator-proc (:list a1 a2)) ) ((and (string? a1) (string? a2)) (:generator-proc (:string a1 a2)) ) ((and (vector? a1) (vector? a2)) (:generator-proc (:vector a1 a2)) ) ((and (integer? a1) (exact? a1) (integer? a2) (exact? a2)) (:generator-proc (:range a1 a2)) ) ((and (real? a1) (real? a2)) (:generator-proc (:real-range a1 a2)) ) ((and (char? a1) (char? a2)) (:generator-proc (:char-range a1 a2)) ) ((and (input-port? a1) (procedure? a2)) (:generator-proc (:port a1 a2)) ) (else #f )))) ((3) (let ((a1 (car args)) (a2 (cadr args)) (a3 (caddr args))) (cond ((and (list? a1) (list? a2) (list? a3)) (:generator-proc (:list a1 a2 a3)) ) ((and (string? a1) (string? a2) (string? a3)) (:generator-proc (:string a1 a2 a3)) ) ((and (vector? a1) (vector? a2) (vector? a3)) (:generator-proc (:vector a1 a2 a3)) ) ((and (integer? a1) (exact? a1) (integer? a2) (exact? a2) (integer? a3) (exact? a3)) (:generator-proc (:range a1 a2 a3)) ) ((and (real? a1) (real? a2) (real? a3)) (:generator-proc (:real-range a1 a2 a3)) ) (else #f )))) (else (letrec ((every? (lambda (pred args) (if (null? args) #t (and (pred (car args)) (every? pred (cdr args)) ))))) (cond ((every? list? args) (:generator-proc (:list (apply append args))) ) ((every? string? args) (:generator-proc (:string (apply string-append args))) ) ((every? vector? args) (:generator-proc (:list (apply append (map vector->list args)))) ) (else #f ))))))) (define :-dispatch (make-parameter (make-initial-:-dispatch) (lambda (x) (if (procedure? x) x (error "not a procedure" x))))) (define (:-dispatch-ref) (:-dispatch)) (define (:-dispatch-set! dispatch) (:-dispatch dispatch)) (define-syntax : (syntax-rules (index) ((: cc var (index i) arg1 arg ...) (:dispatched cc var (index i) (:-dispatch) arg1 arg ...) ) ((: cc var arg1 arg ...) (:dispatched cc var (:-dispatch) arg1 arg ...) ))) ; ========================================================================== ; The utility comprehensions fold-ec, fold3-ec ; ========================================================================== (define-syntax fold3-ec (syntax-rules (nested) ((fold3-ec x0 (nested q1 ...) q etc1 etc2 etc3 etc ...) (fold3-ec x0 (nested q1 ... q) etc1 etc2 etc3 etc ...) ) ((fold3-ec x0 q1 q2 etc1 etc2 etc3 etc ...) (fold3-ec x0 (nested q1 q2) etc1 etc2 etc3 etc ...) ) ((fold3-ec x0 expression f1 f2) (fold3-ec x0 (nested) expression f1 f2) ) ((fold3-ec x0 qualifier expression f1 f2) (let ((result #f) (empty #t)) (do-ec qualifier (let ((value expression)) ; don't duplicate (if empty (begin (set! result (f1 value)) (set! empty #f) ) (set! result (f2 value result)) ))) (if empty x0 result) )))) (define-syntax fold-ec (syntax-rules (nested) ((fold-ec x0 (nested q1 ...) q etc1 etc2 etc ...) (fold-ec x0 (nested q1 ... q) etc1 etc2 etc ...) ) ((fold-ec x0 q1 q2 etc1 etc2 etc ...) (fold-ec x0 (nested q1 q2) etc1 etc2 etc ...) ) ((fold-ec x0 expression f2) (fold-ec x0 (nested) expression f2) ) ((fold-ec x0 qualifier expression f2) (let ((result x0)) (do-ec qualifier (set! result (f2 expression result))) result )))) ; ========================================================================== ; The comprehensions list-ec string-ec vector-ec etc. ; ========================================================================== (define-syntax list-ec (syntax-rules () ((list-ec etc1 etc ...) (reverse (fold-ec '() etc1 etc ... cons)) ))) ; Alternative: Reverse can safely be replaced by reverse! if you have it. ; ; Alternative: It is possible to construct the result in the correct order ; using set-cdr! to add at the tail. This removes the overhead of copying ; at the end, at the cost of more book-keeping. (define-syntax append-ec (syntax-rules () ((append-ec etc1 etc ...) (apply append (list-ec etc1 etc ...)) ))) (define-syntax string-ec (syntax-rules () ((string-ec etc1 etc ...) (list->string (list-ec etc1 etc ...)) ))) ; Alternative: For very long strings, the intermediate list may be a ; problem. A more space-aware implementation collect the characters ; in an intermediate list and when this list becomes too large it is ; converted into an intermediate string. At the end, the intermediate ; strings are concatenated with string-append. (define-syntax string-append-ec (syntax-rules () ((string-append-ec etc1 etc ...) (apply string-append (list-ec etc1 etc ...)) ))) (define-syntax vector-ec (syntax-rules () ((vector-ec etc1 etc ...) (list->vector (list-ec etc1 etc ...)) ))) ; Comment: A similar approach as for string-ec can be used for vector-ec. ; However, the space overhead for the intermediate list is much lower ; than for string-ec and as there is no vector-append, the intermediate ; vectors must be copied explicitly. (define-syntax vector-of-length-ec (syntax-rules (nested) ((vector-of-length-ec k (nested q1 ...) q etc1 etc ...) (vector-of-length-ec k (nested q1 ... q) etc1 etc ...) ) ((vector-of-length-ec k q1 q2 etc1 etc ...) (vector-of-length-ec k (nested q1 q2) etc1 etc ...) ) ((vector-of-length-ec k expression) (vector-of-length-ec k (nested) expression) ) ((vector-of-length-ec k qualifier expression) (let ((len k)) (let ((vec (make-vector len)) (i 0) ) (do-ec qualifier (if (< i len) (begin (vector-set! vec i expression) (set! i (+ i 1)) ) (error "vector is too short for the comprehension") )) (if (= i len) vec (error "vector is too long for the comprehension") )))))) (define-syntax sum-ec (syntax-rules () ((sum-ec etc1 etc ...) (fold-ec (+) etc1 etc ... +) ))) (define-syntax product-ec (syntax-rules () ((product-ec etc1 etc ...) (fold-ec (*) etc1 etc ... *) ))) (define-syntax min-ec (syntax-rules () ((min-ec etc1 etc ...) (fold3-ec (min) etc1 etc ... min min) ))) (define-syntax max-ec (syntax-rules () ((max-ec etc1 etc ...) (fold3-ec (max) etc1 etc ... max max) ))) (define-syntax last-ec (syntax-rules (nested) ((last-ec default (nested q1 ...) q etc1 etc ...) (last-ec default (nested q1 ... q) etc1 etc ...) ) ((last-ec default q1 q2 etc1 etc ...) (last-ec default (nested q1 q2) etc1 etc ...) ) ((last-ec default expression) (last-ec default (nested) expression) ) ((last-ec default qualifier expression) (let ((result default)) (do-ec qualifier (set! result expression)) result )))) ; ========================================================================== ; The fundamental early-stopping comprehension first-ec ; ========================================================================== (define-syntax first-ec (syntax-rules (nested) ((first-ec default (nested q1 ...) q etc1 etc ...) (first-ec default (nested q1 ... q) etc1 etc ...) ) ((first-ec default q1 q2 etc1 etc ...) (first-ec default (nested q1 q2) etc1 etc ...) ) ((first-ec default expression) (first-ec default (nested) expression) ) ((first-ec default qualifier expression) (let ((result default) (stop #f)) (ec-guarded-do-ec stop (nested qualifier) (begin (set! result expression) (set! stop #t) )) result )))) ; (ec-guarded-do-ec stop (nested q ...) cmd) ; constructs (do-ec q ... cmd) where the generators gen in q ... are ; replaced by (:until gen stop). (define-syntax ec-guarded-do-ec (syntax-rules (nested if not and or begin) ((ec-guarded-do-ec stop (nested (nested q1 ...) q2 ...) cmd) (ec-guarded-do-ec stop (nested q1 ... q2 ...) cmd) ) ((ec-guarded-do-ec stop (nested (if test) q ...) cmd) (if test (ec-guarded-do-ec stop (nested q ...) cmd)) ) ((ec-guarded-do-ec stop (nested (not test) q ...) cmd) (if (not test) (ec-guarded-do-ec stop (nested q ...) cmd)) ) ((ec-guarded-do-ec stop (nested (and test ...) q ...) cmd) (if (and test ...) (ec-guarded-do-ec stop (nested q ...) cmd)) ) ((ec-guarded-do-ec stop (nested (or test ...) q ...) cmd) (if (or test ...) (ec-guarded-do-ec stop (nested q ...) cmd)) ) ((ec-guarded-do-ec stop (nested (begin etc ...) q ...) cmd) (begin etc ... (ec-guarded-do-ec stop (nested q ...) cmd)) ) ((ec-guarded-do-ec stop (nested gen q ...) cmd) (do-ec (:until gen stop) (ec-guarded-do-ec stop (nested q ...) cmd) )) ((ec-guarded-do-ec stop (nested) cmd) (do-ec cmd) ))) ; Alternative: Instead of modifying the generator with :until, it is ; possible to use call-with-current-continuation: ; ; (define-synatx first-ec ; ...same as above... ; ((first-ec default qualifier expression) ; (call-with-current-continuation ; (lambda (cc) ; (do-ec qualifier (cc expression)) ; default ))) )) ; ; This is much simpler but not necessarily as efficient. ; ========================================================================== ; The early-stopping comprehensions any?-ec every?-ec ; ========================================================================== (define-syntax any?-ec (syntax-rules (nested) ((any?-ec (nested q1 ...) q etc1 etc ...) (any?-ec (nested q1 ... q) etc1 etc ...) ) ((any?-ec q1 q2 etc1 etc ...) (any?-ec (nested q1 q2) etc1 etc ...) ) ((any?-ec expression) (any?-ec (nested) expression) ) ((any?-ec qualifier expression) (first-ec #f qualifier (if expression) #t) ))) (define-syntax every?-ec (syntax-rules (nested) ((every?-ec (nested q1 ...) q etc1 etc ...) (every?-ec (nested q1 ... q) etc1 etc ...) ) ((every?-ec q1 q2 etc1 etc ...) (every?-ec (nested q1 q2) etc1 etc ...) ) ((every?-ec expression) (every?-ec (nested) expression) ) ((every?-ec qualifier expression) (first-ec #t qualifier (if (not expression)) #f) ))) |
Added srfi/s42/extension.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
; <PLAINTEXT> ; Examples for Application Specific Extensions of Eager Comprehensions ; ==================================================================== ; ; sebastian.egner@philips.com, Eindhoven, The Netherlands, Feb-2003. ; Scheme R5RS (incl. macros), SRFI-23 (error). ; ; Running the extensions in Scheme48 (version 0.57): ; ; load "examples.scm" as described there ; ,load extension.scm ; ; Running the extensions in PLT (version 202): ; ; load "examples.scm" as described there ; (load "extension.scm") ; ; Running the extensions in SCM (version 5d7): ; ; load "examples.scm" as described there ; (load "extension.scm") ; reset SRFI (set! :-dispatch (make-initial-:-dispatch)) (define my-check-correct 0) (define my-check-wrong 0) ; ========================================================================== ; Extending the predefined dispatching generator ; ========================================================================== ; example from SRFI document (for :dispatch) (define (example-dispatch args) (cond ((null? args) 'example ) ((and (= (length args) 1) (symbol? (car args)) ) (:generator-proc (:string (symbol->string (car args)))) ) (else #f ))) (:-dispatch-set! (dispatch-union (:-dispatch-ref) example-dispatch)) ; run the example (my-check (list-ec (: c 'abc) c) => '(#\a #\b #\c)) ; ========================================================================== ; Adding an application specific dispatching generator ; ========================================================================== ; example from SRFI document (for :dispatch) (define (:my-dispatch args) (case (length args) ((0) 'example) ((1) (let ((a1 (car args))) (cond ((list? a1) (:generator-proc (:list a1)) ) ((string? a1) (:generator-proc (:string a1)) ) ; ...more unary cases... (else #f )))) ((2) (let ((a1 (car args)) (a2 (cadr args))) (cond ((and (list? a1) (list? a2)) (:generator-proc (:list a1 a2)) ) ; ...more binary cases... (else #f )))) ; ...more arity cases... (else (cond ((every?-ec (:list a args) (list? a)) (:generator-proc (:list (apply append args))) ) ; ...more large variable arity cases... (else #f ))))) (define-syntax :my (syntax-rules (index) ((:my cc var (index i) arg1 arg ...) (:dispatched cc var (index i) :my-dispatch arg1 arg ...) ) ((:my cc var arg1 arg ...) (:dispatched cc var :my-dispatch arg1 arg ...) ))) ; run the example (my-check (list-ec (:my x "abc") x) => '(#\a #\b #\c)) (my-check (list-ec (:my x '(1) '(2) '(3)) x) => '(1 2 3)) (my-check (list-ec (:my x (index i) "abc") (list x i)) => '((#\a 0) (#\b 1) (#\c 2)) ) ; ========================================================================== ; Adding an application specific typed generator ; ========================================================================== ; example from SRFI document (define-syntax :mygen (syntax-rules () ((:mygen cc var arg) (:list cc var (reverse arg)) ))) ; run the example (my-check (list-ec (:mygen x '(1 2 3)) x) => '(3 2 1)) ; ========================================================================== ; Adding application specific comprehensions ; ========================================================================== ; example from SRFI document (define-syntax new-list-ec (syntax-rules () ((new-list-ec etc1 etc ...) (reverse (fold-ec '() etc1 etc ... cons)) ))) (define-syntax new-min-ec (syntax-rules () ((new-min-ec etc1 etc ...) (fold3-ec (min) etc1 etc ... min min) ))) (define-syntax new-fold3-ec (syntax-rules (nested) ((new-fold3-ec x0 (nested q1 ...) q etc1 etc2 etc3 etc ...) (new-fold3-ec x0 (nested q1 ... q) etc1 etc2 etc3 etc ...) ) ((new-fold3-ec x0 q1 q2 etc1 etc2 etc3 etc ...) (new-fold3-ec x0 (nested q1 q2) etc1 etc2 etc3 etc ...) ) ((new-fold3-ec x0 expression f1 f2) (new-fold3-ec x0 (nested) expression f1 f2) ) ((new-fold3-ec x0 qualifier expression f1 f2) (let ((result #f) (empty #t)) (do-ec qualifier (let ((value expression)) ; don't duplicate (if empty (begin (set! result (f1 value)) (set! empty #f) ) (set! result (f2 value result)) ))) (if empty x0 result) )))) ; run the example (my-check (new-list-ec (: i 5) i) => '(0 1 2 3 4)) (my-check (new-min-ec (: i 5) i) => 0) (my-check (let ((f1 (lambda (x) (list 'f1 x))) (f2 (lambda (x result) (list 'f2 x result))) ) (new-fold3-ec (error "bad") (: i 5) i f1 f2) ) => '(f2 4 (f2 3 (f2 2 (f2 1 (f1 0))))) ) ; ========================================================================== ; Summary ; ========================================================================== (begin (newline) (newline) (display "correct examples : ") (display my-check-correct) (newline) (display "wrong examples : ") (display my-check-wrong) (newline) (newline) ) |
Added srfi/s42/timing.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
; <PLAINTEXT> ; Timing for Eager Comprehensions in [outer..inner|expr]-Convention ; ================================================================= ; ; sebastian.egner@philips.com, Eindhoven, The Netherlands, Feb-2003. ; Scheme R5RS (incl. macros), SRFI-23 (error). ; ; Running the examples in Scheme48 (version 0.57): ; ,open srfi-23 ; ,load ec.scm ; ,load timing.scm ; ; Running the examples in PLT/DrScheme (version 202): ; ; open "ec.scm", click Execute ; (load "timing.scm") ; ; Running the examples in SCM (version 5d7): ; ; invoke SCM with -v on the command line ; (require 'macro) (require 'record) ; (load "ec.scm") ; (load "timing.scm") ; ======================================================================= ; Basic loops ; ======================================================================= ; ; We measure execution times for (:range var n) and for (: var n), ; both as an outer loop (to measure iteration speed) or as an inner ; loop (to measure start up overhead). For comparison the same is ; measured for a hand-coded DO-loop. (define (perf0 n) ; reference for loop duration (do ((i 0 (+ i 1))) ((= i n)) i)) (define (perf1 n) (do-ec (:range i n) i)) (define (perf2 n) (do-ec (: i n) i)) (define (perf0s n) ; reference for startup delay (do-ec (:range i n) (do ((i 0 (+ i 1))) ((= i 1)) i))) (define (perf1s n) (do-ec (:range i n) (:range j 1) i)) (define (perf2s n) (do-ec (:range i n) (: j 1) i)) (define n-perf 10000000) ; Scheme48 0.57 on HP 9000/800 server running HP-UX ; ------------------------------------------------- ; ; ,time (perf0 n-perf) 19.3 ; built-in do ; ,time (perf1 n-perf) 17.0 ; faster than built-in do (?) ; ,time (perf2 n-perf) 40.4 ; due to calling the generator as procedure ; ; ,time (perf0s n-perf) 57.5 ; built-in do in the inner loop ; ,time (perf1s n-perf) 78.5 ; due to checking exact? integer? ; ,time (perf2s n-perf) 274.0 ; due to dispatch mechanism ; ; [All times are CPU time in seconds for n-perf iterations.] ; PLT 202 on Pentium III Mobile, 1 GHz, 1 GB RAM, Windows 2k ; ---------------------------------------------------------- ; ; (time (perf0 n-perf)) 11.1 ; (time (perf1 n-perf)) 7.8 ; (time (perf2 n-perf)) 18.1 ; ; (time (perf0s n-perf)) 35.2 ; (time (perf1s n-perf)) 42.9 ; (time (perf2s n-perf)) 147.8 ; ; [All times are CPU time in seconds for n-perf iterations.] ; SCM 5d7 on Pentium III Mobile, 1 GHz, 1 GB RAM, Windows 2k ; ---------------------------------------------------------- ; ; (perf0 n-perf) 29.1 ; (perf1 n-perf) 30.0 ; (perf2 n-perf) 45.5 ; ; (perf0s n-perf) 79.2 ; (perf1s n-perf) 448.6 ; (perf2s n-perf) 756.2 ; ; [All times are CPU time in seconds for n-perf iterations.] |
Added srfi/s43/_vectors-a.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s43 vectors) (export ;;; * Constructors make-vector vector vector-unfold vector-unfold-right vector-copy vector-reverse-copy vector-append vector-concatenate ;;; * Predicates vector? vector-empty? vector= ;;; * Selectors vector-ref vector-length ;;; * Iteration vector-fold vector-fold-right vector-map vector-map! vector-for-each vector-count ;;; * Searching vector-index vector-skip vector-index-right vector-skip-right vector-binary-search vector-any vector-every ;;; * Mutators vector-set! vector-swap! ;; (rename (my:vector-fill! vector-fill!)) vector-fill! vector-reverse! vector-copy! vector-reverse-copy! ;;; * Conversion ;; (rename (my:vector->list vector->list)) vector->list reverse-vector->list ;; (rename (my:list->vector list->vector)) list->vector reverse-list->vector ) (import (except (rnrs) vector-map vector-for-each vector-fill! vector->list list->vector) (rnrs r5rs) ;; (srfi s23 error tricks) (srfi s8 receive) ;; (for (srfi private vanish) expand) ;; (srfi private include) ) ;; I do these let-syntax tricks so the original vector-lib.scm file does ;; not have to be modified at all. ;; (let-syntax ;; ((define ;; (let ((vd (vanish-define define ;; (make-vector vector vector? vector-ref vector-set! vector-length)))) ;; (lambda (stx) ;; (define (rename? id) ;; (memp (lambda (x) (free-identifier=? id x)) ;; (list #'vector-fill! #'vector->list #'list->vector))) ;; (define (rename id) ;; (datum->syntax id ;; (string->symbol ;; (string-append "my:" (symbol->string (syntax->datum id)))))) ;; (syntax-case stx () ;; ((_ name . r) ;; (and (identifier? #'name) ;; (rename? #'name)) ;; #`(define #,(rename #'name) . r)) ;; (_ (vd stx)))))) ;; (define-syntax ;; (vanish-define define-syntax ;; (receive)))) ;; (SRFI-23-error->R6RS "(library (srfi s43 vectors))" ;; (include/resolve ("srfi" "s43") "vector-lib.scm"))) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;; SRFI 43: Vector library -*- Scheme -*- ;;; ;;; Taylor Campbell wrote this code; he places it in the public domain. ;;; Will Clinger [wdc] made some corrections, also in the public domain. ;;; -------------------- ;;; Exported procedure index ;;; ;;; * Constructors ;;; make-vector vector ;;; vector-unfold vector-unfold-right ;;; vector-copy vector-reverse-copy ;;; vector-append vector-concatenate ;;; ;;; * Predicates ;;; vector? ;;; vector-empty? ;;; vector= ;;; ;;; * Selectors ;;; vector-ref ;;; vector-length ;;; ;;; * Iteration ;;; vector-fold vector-fold-right ;;; vector-map vector-map! ;;; vector-for-each ;;; vector-count ;;; ;;; * Searching ;;; vector-index vector-skip ;;; vector-index-right vector-skip-right ;;; vector-binary-search ;;; vector-any vector-every ;;; ;;; * Mutators ;;; vector-set! ;;; vector-swap! ;;; vector-fill! ;;; vector-reverse! ;;; vector-copy! vector-reverse-copy! ;;; vector-reverse! ;;; ;;; * Conversion ;;; vector->list reverse-vector->list ;;; list->vector reverse-list->vector ;;; -------------------- ;;; Commentary on efficiency of the code ;;; This code is somewhat tuned for efficiency. There are several ;;; internal routines that can be optimized greatly to greatly improve ;;; the performance of much of the library. These internal procedures ;;; are already carefully tuned for performance, and lambda-lifted by ;;; hand. Some other routines are lambda-lifted by hand, but only the ;;; loops are lambda-lifted, and only if some routine has two possible ;;; loops -- a fast path and an n-ary case --, whereas _all_ of the ;;; internal routines' loops are lambda-lifted so as to never cons a ;;; closure in their body (VECTOR-PARSE-START+END doesn't have a loop), ;;; even in Scheme systems that perform no loop optimization (which is ;;; most of them, unfortunately). ;;; ;;; Fast paths are provided for common cases in most of the loops in ;;; this library. ;;; ;;; All calls to primitive vector operations are protected by a prior ;;; type check; they can be safely converted to use unsafe equivalents ;;; of the operations, if available. Ideally, the compiler should be ;;; able to determine this, but the state of Scheme compilers today is ;;; not a happy one. ;;; ;;; Efficiency of the actual algorithms is a rather mundane point to ;;; mention; vector operations are rarely beyond being straightforward. ;;; -------------------- ;;; Utilities ;;; SRFI 8, too trivial to put in the dependencies list. ;; (define-syntax receive ;; (syntax-rules () ;; ((receive ?formals ?producer ?body1 ?body2 ...) ;; (call-with-values (lambda () ?producer) ;; (lambda ?formals ?body1 ?body2 ...))))) ;;; Not the best LET*-OPTIONALS, but not the worst, either. Use Olin's ;;; if it's available to you. (define-syntax let*-optionals (syntax-rules () ((let*-optionals (?x ...) ((?var ?default) ...) ?body1 ?body2 ...) (let ((args (?x ...))) (let*-optionals args ((?var ?default) ...) ?body1 ?body2 ...))) ((let*-optionals ?args ((?var ?default) ...) ?body1 ?body2 ...) (let*-optionals:aux ?args ?args ((?var ?default) ...) ?body1 ?body2 ...)))) (define-syntax let*-optionals:aux (syntax-rules () ((aux ?orig-args-var ?args-var () ?body1 ?body2 ...) (if (null? ?args-var) (let () ?body1 ?body2 ...) (error "too many arguments" (length ?orig-args-var) ?orig-args-var))) ((aux ?orig-args-var ?args-var ((?var ?default) ?more ...) ?body1 ?body2 ...) (if (null? ?args-var) (let* ((?var ?default) ?more ...) ?body1 ?body2 ...) (let ((?var (car ?args-var)) (new-args (cdr ?args-var))) (let*-optionals:aux ?orig-args-var new-args (?more ...) ?body1 ?body2 ...)))))) (define (nonneg-int? x) (and (integer? x) (not (negative? x)))) (define (between? x y z) (and (< x y) (<= y z))) (define (unspecified-value) (if #f #f)) ;++ This should be implemented more efficiently. It shouldn't cons a ;++ closure, and the cons cells used in the loops when using this could ;++ be reused. (define (vectors-ref vectors i) (map (lambda (v) (vector-ref v i)) vectors)) ;;; -------------------- ;;; Error checking ;;; Error signalling (not checking) is done in a way that tries to be ;;; as helpful to the person who gets the debugging prompt as possible. ;;; That said, error _checking_ tries to be as unredundant as possible. ;;; I don't use any sort of general condition mechanism; I use simply ;;; SRFI 23's ERROR, even in cases where it might be better to use such ;;; a general condition mechanism. Fix that when porting this to a ;;; Scheme implementation that has its own condition system. ;;; In argument checks, upon receiving an invalid argument, the checker ;;; procedure recursively calls itself, but in one of the arguments to ;;; itself is a call to ERROR; this mechanism is used in the hopes that ;;; the user may be thrown into a debugger prompt, proceed with another ;;; value, and let it be checked again. ;;; Type checking is pretty basic, but easily factored out and replaced ;;; with whatever your implementation's preferred type checking method ;;; is. I doubt there will be many other methods of index checking, ;;; though the index checkers might be better implemented natively. ;;; (CHECK-TYPE <type-predicate?> <value> <callee>) -> value ;;; Ensure that VALUE satisfies TYPE-PREDICATE?; if not, signal an ;;; error stating that VALUE did not satisfy TYPE-PREDICATE?, showing ;;; that this happened while calling CALLEE. Return VALUE if no ;;; error was signalled. (define (check-type pred? value callee) (if (pred? value) value ;; Recur: when (or if) the user gets a debugger prompt, he can ;; proceed where the call to ERROR was with the correct value. (check-type pred? (error "erroneous value" (list pred? value) `(while calling ,callee)) callee))) ;;; (CHECK-INDEX <vector> <index> <callee>) -> index ;;; Ensure that INDEX is a valid index into VECTOR; if not, signal an ;;; error stating that it is not and that this happened in a call to ;;; CALLEE. Return INDEX when it is valid. (Note that this does NOT ;;; check that VECTOR is indeed a vector.) (define (check-index vec index callee) (let ((index (check-type integer? index callee))) (cond ((< index 0) (check-index vec (error "vector index too low" index `(into vector ,vec) `(while calling ,callee)) callee)) ((>= index (vector-length vec)) (check-index vec (error "vector index too high" index `(into vector ,vec) `(while calling ,callee)) callee)) (else index)))) ;;; (CHECK-INDICES <vector> ;;; <start> <start-name> ;;; <end> <end-name> ;;; <caller>) -> [start end] ;;; Ensure that START and END are valid bounds of a range within ;;; VECTOR; if not, signal an error stating that they are not, with ;;; the message being informative about what the argument names were ;;; called -- by using START-NAME & END-NAME --, and that it occurred ;;; while calling CALLEE. Also ensure that VEC is in fact a vector. ;;; Returns no useful value. (define (check-indices vec start start-name end end-name callee) (let ((lose (lambda things (apply error "vector range out of bounds" (append things `(vector was ,vec) `(,start-name was ,start) `(,end-name was ,end) `(while calling ,callee))))) (start (check-type integer? start callee)) (end (check-type integer? end callee))) (cond ((> start end) ;; I'm not sure how well this will work. The intent is that ;; the programmer tells the debugger to proceed with both a ;; new START & a new END by returning multiple values ;; somewhere. (receive (new-start new-end) (lose `(,end-name < ,start-name)) (check-indices vec new-start start-name new-end end-name callee))) ((< start 0) (check-indices vec (lose `(,start-name < 0)) start-name end end-name callee)) ((>= start (vector-length vec)) (check-indices vec (lose `(,start-name > len) `(len was ,(vector-length vec))) start-name end end-name callee)) ((> end (vector-length vec)) (check-indices vec start start-name (lose `(,end-name > len) `(len was ,(vector-length vec))) end-name callee)) (else (values start end))))) ;;; -------------------- ;;; Internal routines ;;; These should all be integrated, native, or otherwise optimized -- ;;; they're used a _lot_ --. All of the loops and LETs inside loops ;;; are lambda-lifted by hand, just so as not to cons closures in the ;;; loops. (If your compiler can do better than that if they're not ;;; lambda-lifted, then lambda-drop (?) them.) ;;; (VECTOR-PARSE-START+END <vector> <arguments> ;;; <start-name> <end-name> ;;; <callee>) ;;; -> [start end] ;;; Return two values, composing a valid range within VECTOR, as ;;; extracted from ARGUMENTS or defaulted from VECTOR -- 0 for START ;;; and the length of VECTOR for END --; START-NAME and END-NAME are ;;; purely for error checking. (define (vector-parse-start+end vec args start-name end-name callee) (let ((len (vector-length vec))) (cond ((null? args) (values 0 len)) ((null? (cdr args)) (check-indices vec (car args) start-name len end-name callee)) ((null? (cddr args)) (check-indices vec (car args) start-name (cadr args) end-name callee)) (else (error "too many arguments" `(extra args were ,(cddr args)) `(while calling ,callee)))))) (define-syntax let-vector-start+end (syntax-rules () ((let-vector-start+end ?callee ?vec ?args (?start ?end) ?body1 ?body2 ...) (let ((?vec (check-type vector? ?vec ?callee))) (receive (?start ?end) (vector-parse-start+end ?vec ?args '?start '?end ?callee) ?body1 ?body2 ...))))) ;;; (%SMALLEST-LENGTH <vector-list> <default-length> <callee>) ;;; -> exact, nonnegative integer ;;; Compute the smallest length of VECTOR-LIST. DEFAULT-LENGTH is ;;; the length that is returned if VECTOR-LIST is empty. Common use ;;; of this is in n-ary vector routines: ;;; (define (f vec . vectors) ;;; (let ((vec (check-type vector? vec f))) ;;; ...(%smallest-length vectors (vector-length vec) f)...)) ;;; %SMALLEST-LENGTH takes care of the type checking -- which is what ;;; the CALLEE argument is for --; thus, the design is tuned for ;;; avoiding redundant type checks. (define %smallest-length (letrec ((loop (lambda (vector-list length callee) (if (null? vector-list) length (loop (cdr vector-list) (min (vector-length (check-type vector? (car vector-list) callee)) length) callee))))) loop)) ;;; (%VECTOR-COPY! <target> <tstart> <source> <sstart> <send>) ;;; Copy elements at locations SSTART to SEND from SOURCE to TARGET, ;;; starting at TSTART in TARGET. ;;; ;;; Optimize this! Probably with some combination of: ;;; - Force it to be integrated. ;;; - Let it use unsafe vector element dereferencing routines: bounds ;;; checking already happens outside of it. (Or use a compiler ;;; that figures this out, but Olin Shivers' PhD thesis seems to ;;; have been largely ignored in actual implementations...) ;;; - Implement it natively as a VM primitive: the VM can undoubtedly ;;; perform much faster than it can make Scheme perform, even with ;;; bounds checking. ;;; - Implement it in assembly: you _want_ the fine control that ;;; assembly can give you for this. ;;; I already lambda-lift it by hand, but you should be able to make it ;;; even better than that. (define %vector-copy! (letrec ((loop/l->r (lambda (target source send i j) (cond ((< i send) (vector-set! target j (vector-ref source i)) (loop/l->r target source send (+ i 1) (+ j 1)))))) (loop/r->l (lambda (target source sstart i j) (cond ((>= i sstart) (vector-set! target j (vector-ref source i)) (loop/r->l target source sstart (- i 1) (- j 1))))))) (lambda (target tstart source sstart send) (if (> sstart tstart) ; Make sure we don't copy over ; ourselves. (loop/l->r target source send sstart tstart) (loop/r->l target source sstart (- send 1) (+ -1 tstart send (- sstart))))))) ;;; (%VECTOR-REVERSE-COPY! <target> <tstart> <source> <sstart> <send>) ;;; Copy elements from SSTART to SEND from SOURCE to TARGET, in the ;;; reverse order. (define %vector-reverse-copy! (letrec ((loop (lambda (target source sstart i j) (cond ((>= i sstart) (vector-set! target j (vector-ref source i)) (loop target source sstart (- i 1) (+ j 1))))))) (lambda (target tstart source sstart send) (loop target source sstart (- send 1) tstart)))) ;;; (%VECTOR-REVERSE! <vector>) (define %vector-reverse! (letrec ((loop (lambda (vec i j) (cond ((<= i j) (let ((v (vector-ref vec i))) (vector-set! vec i (vector-ref vec j)) (vector-set! vec j v) (loop vec (+ i 1) (- j 1)))))))) (lambda (vec start end) (loop vec start (- end 1))))) ;;; (%VECTOR-FOLD1 <kons> <knil> <vector>) -> knil' ;;; (KONS <index> <knil> <elt>) -> knil' (define %vector-fold1 (letrec ((loop (lambda (kons knil len vec i) (if (= i len) knil (loop kons (kons i knil (vector-ref vec i)) len vec (+ i 1)))))) (lambda (kons knil len vec) (loop kons knil len vec 0)))) ;;; (%VECTOR-FOLD2+ <kons> <knil> <vector> ...) -> knil' ;;; (KONS <index> <knil> <elt> ...) -> knil' (define %vector-fold2+ (letrec ((loop (lambda (kons knil len vectors i) (if (= i len) knil (loop kons (apply kons i knil (vectors-ref vectors i)) len vectors (+ i 1)))))) (lambda (kons knil len vectors) (loop kons knil len vectors 0)))) ;;; (%VECTOR-MAP! <f> <target> <length> <vector>) -> target ;;; (F <index> <elt>) -> elt' (define %vector-map1! (letrec ((loop (lambda (f target vec i) (if (zero? i) target (let ((j (- i 1))) (vector-set! target j (f j (vector-ref vec j))) (loop f target vec j)))))) (lambda (f target vec len) (loop f target vec len)))) ;;; (%VECTOR-MAP2+! <f> <target> <vectors> <len>) -> target ;;; (F <index> <elt> ...) -> elt' (define %vector-map2+! (letrec ((loop (lambda (f target vectors i) (if (zero? i) target (let ((j (- i 1))) (vector-set! target j (apply f j (vectors-ref vectors j))) (loop f target vectors j)))))) (lambda (f target vectors len) (loop f target vectors len)))) ;;;;;;;;;;;;;;;;;;;;;;;; ***** vector-lib ***** ;;;;;;;;;;;;;;;;;;;;;;; ;;; -------------------- ;;; Constructors ;;; (MAKE-VECTOR <size> [<fill>]) -> vector ;;; [R5RS] Create a vector of length LENGTH. If FILL is present, ;;; initialize each slot in the vector with it; if not, the vector's ;;; initial contents are unspecified. ;; (define make-vector make-vector) ;;; (VECTOR <elt> ...) -> vector ;;; [R5RS] Create a vector containing ELEMENT ..., in order. ;; (define vector vector) ;;; This ought to be able to be implemented much more efficiently -- if ;;; we have the number of arguments available to us, we can create the ;;; vector without using LENGTH to determine the number of elements it ;;; should have. ;(define (vector . elements) (list->vector elements)) ;;; (VECTOR-UNFOLD <f> <length> <initial-seed> ...) -> vector ;;; (F <index> <seed> ...) -> [elt seed' ...] ;;; The fundamental vector constructor. Creates a vector whose ;;; length is LENGTH and iterates across each index K between 0 and ;;; LENGTH, applying F at each iteration to the current index and the ;;; current seeds to receive N+1 values: first, the element to put in ;;; the Kth slot and then N new seeds for the next iteration. (define vector-unfold (letrec ((tabulate! ; Special zero-seed case. (lambda (f vec i len) (cond ((< i len) (vector-set! vec i (f i)) (tabulate! f vec (+ i 1) len))))) (unfold1! ; Fast path for one seed. (lambda (f vec i len seed) (if (< i len) (receive (elt new-seed) (f i seed) (vector-set! vec i elt) (unfold1! f vec (+ i 1) len new-seed))))) (unfold2+! ; Slower variant for N seeds. (lambda (f vec i len seeds) (if (< i len) (receive (elt . new-seeds) (apply f i seeds) (vector-set! vec i elt) (unfold2+! f vec (+ i 1) len new-seeds)))))) (lambda (f len . initial-seeds) (let ((f (check-type procedure? f vector-unfold)) (len (check-type nonneg-int? len vector-unfold))) (let ((vec (make-vector len))) (cond ((null? initial-seeds) (tabulate! f vec 0 len)) ((null? (cdr initial-seeds)) (unfold1! f vec 0 len (car initial-seeds))) (else (unfold2+! f vec 0 len initial-seeds))) vec))))) ;;; (VECTOR-UNFOLD-RIGHT <f> <length> <initial-seed> ...) -> vector ;;; (F <seed> ...) -> [seed' ...] ;;; Like VECTOR-UNFOLD, but it generates elements from LENGTH to 0 ;;; (still exclusive with LENGTH and inclusive with 0), not 0 to ;;; LENGTH as with VECTOR-UNFOLD. (define vector-unfold-right (letrec ((tabulate! (lambda (f vec i) (cond ((>= i 0) (vector-set! vec i (f i)) (tabulate! f vec (- i 1)))))) (unfold1! (lambda (f vec i seed) (if (>= i 0) (receive (elt new-seed) (f i seed) (vector-set! vec i elt) (unfold1! f vec (- i 1) new-seed))))) (unfold2+! (lambda (f vec i seeds) (if (>= i 0) (receive (elt . new-seeds) (apply f i seeds) (vector-set! vec i elt) (unfold2+! f vec (- i 1) new-seeds)))))) (lambda (f len . initial-seeds) (let ((f (check-type procedure? f vector-unfold-right)) (len (check-type nonneg-int? len vector-unfold-right))) (let ((vec (make-vector len)) (i (- len 1))) (cond ((null? initial-seeds) (tabulate! f vec i)) ((null? (cdr initial-seeds)) (unfold1! f vec i (car initial-seeds))) (else (unfold2+! f vec i initial-seeds))) vec))))) ;;; (VECTOR-COPY <vector> [<start> <end> <fill>]) -> vector ;;; Create a newly allocated vector containing the elements from the ;;; range [START,END) in VECTOR. START defaults to 0; END defaults ;;; to the length of VECTOR. END may be greater than the length of ;;; VECTOR, in which case the vector is enlarged; if FILL is passed, ;;; the new locations from which there is no respective element in ;;; VECTOR are filled with FILL. (define (vector-copy vec . args) (let ((vec (check-type vector? vec vector-copy))) ;; We can't use LET-VECTOR-START+END, because we have one more ;; argument, and we want finer control, too. ;; ;; Olin's implementation of LET*-OPTIONALS would prove useful here: ;; the built-in argument-checks-as-you-go-along produces almost ;; _exactly_ the same code as VECTOR-COPY:PARSE-ARGS. (receive (start end fill) (vector-copy:parse-args vec args) (let ((new-vector (make-vector (- end start) fill))) (%vector-copy! new-vector 0 vec start (if (> end (vector-length vec)) (vector-length vec) end)) new-vector)))) ;;; Auxiliary for VECTOR-COPY. ;;; [wdc] Corrected to allow 0 <= start <= (vector-length vec). (define (vector-copy:parse-args vec args) (define (parse-args start end n fill) (let ((start (check-type nonneg-int? start vector-copy)) (end (check-type nonneg-int? end vector-copy))) (cond ((and (<= 0 start end) (<= start n)) (values start end fill)) (else (error "illegal arguments" `(while calling ,vector-copy) `(start was ,start) `(end was ,end) `(vector was ,vec)))))) (let ((n (vector-length vec))) (cond ((null? args) (parse-args 0 n n (unspecified-value))) ((null? (cdr args)) (parse-args (car args) n n (unspecified-value))) ((null? (cddr args)) (parse-args (car args) (cadr args) n (unspecified-value))) ((null? (cdddr args)) (parse-args (car args) (cadr args) n (caddr args))) (else (error "too many arguments" vector-copy (cdddr args)))))) ;;; (VECTOR-REVERSE-COPY <vector> [<start> <end>]) -> vector ;;; Create a newly allocated vector whose elements are the reversed ;;; sequence of elements between START and END in VECTOR. START's ;;; default is 0; END's default is the length of VECTOR. (define (vector-reverse-copy vec . maybe-start+end) (let-vector-start+end vector-reverse-copy vec maybe-start+end (start end) (let ((new (make-vector (- end start)))) (%vector-reverse-copy! new 0 vec start end) new))) ;;; (VECTOR-APPEND <vector> ...) -> vector ;;; Append VECTOR ... into a newly allocated vector and return that ;;; new vector. (define (vector-append . vectors) (vector-concatenate:aux vectors vector-append)) ;;; (VECTOR-CONCATENATE <vector-list>) -> vector ;;; Concatenate the vectors in VECTOR-LIST. This is equivalent to ;;; (apply vector-append VECTOR-LIST) ;;; but VECTOR-APPEND tends to be implemented in terms of ;;; VECTOR-CONCATENATE, and some Schemes bork when the list to apply ;;; a function to is too long. ;;; ;;; Actually, they're both implemented in terms of an internal routine. (define (vector-concatenate vector-list) (vector-concatenate:aux vector-list vector-concatenate)) ;;; Auxiliary for VECTOR-APPEND and VECTOR-CONCATENATE (define vector-concatenate:aux (letrec ((compute-length (lambda (vectors len callee) (if (null? vectors) len (let ((vec (check-type vector? (car vectors) callee))) (compute-length (cdr vectors) (+ (vector-length vec) len) callee))))) (concatenate! (lambda (vectors target to) (if (null? vectors) target (let* ((vec1 (car vectors)) (len (vector-length vec1))) (%vector-copy! target to vec1 0 len) (concatenate! (cdr vectors) target (+ to len))))))) (lambda (vectors callee) (cond ((null? vectors) ;+++ (make-vector 0)) ((null? (cdr vectors)) ;+++ ;; Blech, we still have to allocate a new one. (let* ((vec (check-type vector? (car vectors) callee)) (len (vector-length vec)) (new (make-vector len))) (%vector-copy! new 0 vec 0 len) new)) (else (let ((new-vector (make-vector (compute-length vectors 0 callee)))) (concatenate! vectors new-vector 0) new-vector)))))) ;;; -------------------- ;;; Predicates ;;; (VECTOR? <value>) -> boolean ;;; [R5RS] Return #T if VALUE is a vector and #F if not. ;; (define vector? vector?) ;;; (VECTOR-EMPTY? <vector>) -> boolean ;;; Return #T if VECTOR has zero elements in it, i.e. VECTOR's length ;;; is 0, and #F if not. (define (vector-empty? vec) (let ((vec (check-type vector? vec vector-empty?))) (zero? (vector-length vec)))) ;;; (VECTOR= <elt=?> <vector> ...) -> boolean ;;; (ELT=? <value> <value>) -> boolean ;;; Determine vector equality generalized across element comparators. ;;; Vectors A and B are equal iff their lengths are the same and for ;;; each respective elements E_a and E_b (element=? E_a E_b) returns ;;; a true value. ELT=? is always applied to two arguments. Element ;;; comparison must be consistent wtih EQ?; that is, if (eq? E_a E_b) ;;; results in a true value, then (ELEMENT=? E_a E_b) must result in a ;;; true value. This may be exploited to avoid multiple unnecessary ;;; element comparisons. (This implementation does, but does not deal ;;; with the situation that ELEMENT=? is EQ? to avoid more unnecessary ;;; comparisons, but I believe this optimization is probably fairly ;;; insignificant.) ;;; ;;; If the number of vector arguments is zero or one, then #T is ;;; automatically returned. If there are N vector arguments, ;;; VECTOR_1 VECTOR_2 ... VECTOR_N, then VECTOR_1 & VECTOR_2 are ;;; compared; if they are equal, the vectors VECTOR_2 ... VECTOR_N ;;; are compared. The precise order in which ELT=? is applied is not ;;; specified. (define (vector= elt=? . vectors) (let ((elt=? (check-type procedure? elt=? vector=))) (cond ((null? vectors) #t) ((null? (cdr vectors)) (check-type vector? (car vectors) vector=) #t) (else (let loop ((vecs vectors)) (let ((vec1 (check-type vector? (car vecs) vector=)) (vec2+ (cdr vecs))) (or (null? vec2+) (and (binary-vector= elt=? vec1 (car vec2+)) (loop vec2+))))))))) (define (binary-vector= elt=? vector-a vector-b) (or (eq? vector-a vector-b) ;+++ (let ((length-a (vector-length vector-a)) (length-b (vector-length vector-b))) (letrec ((loop (lambda (i) (or (= i length-a) (and (< i length-b) (test (vector-ref vector-a i) (vector-ref vector-b i) i))))) (test (lambda (elt-a elt-b i) (and (or (eq? elt-a elt-b) ;+++ (elt=? elt-a elt-b)) (loop (+ i 1)))))) (and (= length-a length-b) (loop 0)))))) ;;; -------------------- ;;; Selectors ;;; (VECTOR-REF <vector> <index>) -> value ;;; [R5RS] Return the value that the location in VECTOR at INDEX is ;;; mapped to in the store. ;; (define vector-ref vector-ref) ;;; (VECTOR-LENGTH <vector>) -> exact, nonnegative integer ;;; [R5RS] Return the length of VECTOR. ;; (define vector-length vector-length) ;;; -------------------- ;;; Iteration ;;; (VECTOR-FOLD <kons> <initial-knil> <vector> ...) -> knil ;;; (KONS <knil> <elt> ...) -> knil' ; N vectors -> N+1 args ;;; The fundamental vector iterator. KONS is iterated over each ;;; index in all of the vectors in parallel, stopping at the end of ;;; the shortest; KONS is applied to an argument list of (list I ;;; STATE (vector-ref VEC I) ...), where STATE is the current state ;;; value -- the state value begins with KNIL and becomes whatever ;;; KONS returned at the respective iteration --, and I is the ;;; current index in the iteration. The iteration is strictly left- ;;; to-right. ;;; (vector-fold KONS KNIL (vector E_1 E_2 ... E_N)) ;;; <=> ;;; (KONS (... (KONS (KONS KNIL E_1) E_2) ... E_N-1) E_N) (define (vector-fold kons knil vec . vectors) (let ((kons (check-type procedure? kons vector-fold)) (vec (check-type vector? vec vector-fold))) (if (null? vectors) (%vector-fold1 kons knil (vector-length vec) vec) (%vector-fold2+ kons knil (%smallest-length vectors (vector-length vec) vector-fold) (cons vec vectors))))) ;;; (VECTOR-FOLD-RIGHT <kons> <initial-knil> <vector> ...) -> knil ;;; (KONS <knil> <elt> ...) -> knil' ; N vectors => N+1 args ;;; The fundamental vector recursor. Iterates in parallel across ;;; VECTOR ... right to left, applying KONS to the elements and the ;;; current state value; the state value becomes what KONS returns ;;; at each next iteration. KNIL is the initial state value. ;;; (vector-fold-right KONS KNIL (vector E_1 E_2 ... E_N)) ;;; <=> ;;; (KONS (... (KONS (KONS KNIL E_N) E_N-1) ... E_2) E_1) ;;; ;;; Not implemented in terms of a more primitive operations that might ;;; called %VECTOR-FOLD-RIGHT due to the fact that it wouldn't be very ;;; useful elsewhere. (define vector-fold-right (letrec ((loop1 (lambda (kons knil vec i) (if (negative? i) knil (loop1 kons (kons i knil (vector-ref vec i)) vec (- i 1))))) (loop2+ (lambda (kons knil vectors i) (if (negative? i) knil (loop2+ kons (apply kons i knil (vectors-ref vectors i)) vectors (- i 1)))))) (lambda (kons knil vec . vectors) (let ((kons (check-type procedure? kons vector-fold-right)) (vec (check-type vector? vec vector-fold-right))) (if (null? vectors) (loop1 kons knil vec (- (vector-length vec) 1)) (loop2+ kons knil (cons vec vectors) (- (%smallest-length vectors (vector-length vec) vector-fold-right) 1))))))) ;;; (VECTOR-MAP <f> <vector> ...) -> vector ;;; (F <elt> ...) -> value ; N vectors -> N args ;;; Constructs a new vector of the shortest length of the vector ;;; arguments. Each element at index I of the new vector is mapped ;;; from the old vectors by (F I (vector-ref VECTOR I) ...). The ;;; dynamic order of application of F is unspecified. (define (vector-map f vec . vectors) (let ((f (check-type procedure? f vector-map)) (vec (check-type vector? vec vector-map))) (if (null? vectors) (let ((len (vector-length vec))) (%vector-map1! f (make-vector len) vec len)) (let ((len (%smallest-length vectors (vector-length vec) vector-map))) (%vector-map2+! f (make-vector len) (cons vec vectors) len))))) ;;; (VECTOR-MAP! <f> <vector> ...) -> unspecified ;;; (F <elt> ...) -> element' ; N vectors -> N args ;;; Similar to VECTOR-MAP, but rather than mapping the new elements ;;; into a new vector, the new mapped elements are destructively ;;; inserted into the first vector. Again, the dynamic order of ;;; application of F is unspecified, so it is dangerous for F to ;;; manipulate the first VECTOR. (define (vector-map! f vec . vectors) (let ((f (check-type procedure? f vector-map!)) (vec (check-type vector? vec vector-map!))) (if (null? vectors) (%vector-map1! f vec vec (vector-length vec)) (%vector-map2+! f vec (cons vec vectors) (%smallest-length vectors (vector-length vec) vector-map!))) (unspecified-value))) ;;; (VECTOR-FOR-EACH <f> <vector> ...) -> unspecified ;;; (F <elt> ...) ; N vectors -> N args ;;; Simple vector iterator: applies F to each index in the range [0, ;;; LENGTH), where LENGTH is the length of the smallest vector ;;; argument passed, and the respective element at that index. In ;;; contrast with VECTOR-MAP, F is reliably applied to each ;;; subsequent elements, starting at index 0 from left to right, in ;;; the vectors. (define vector-for-each (letrec ((for-each1 (lambda (f vec i len) (cond ((< i len) (f i (vector-ref vec i)) (for-each1 f vec (+ i 1) len))))) (for-each2+ (lambda (f vecs i len) (cond ((< i len) (apply f i (vectors-ref vecs i)) (for-each2+ f vecs (+ i 1) len)))))) (lambda (f vec . vectors) (let ((f (check-type procedure? f vector-for-each)) (vec (check-type vector? vec vector-for-each))) (if (null? vectors) (for-each1 f vec 0 (vector-length vec)) (for-each2+ f (cons vec vectors) 0 (%smallest-length vectors (vector-length vec) vector-for-each))))))) ;;; (VECTOR-COUNT <predicate?> <vector> ...) ;;; -> exact, nonnegative integer ;;; (PREDICATE? <index> <value> ...) ; N vectors -> N+1 args ;;; PREDICATE? is applied element-wise to the elements of VECTOR ..., ;;; and a count is tallied of the number of elements for which a ;;; true value is produced by PREDICATE?. This count is returned. (define (vector-count pred? vec . vectors) (let ((pred? (check-type procedure? pred? vector-count)) (vec (check-type vector? vec vector-count))) (if (null? vectors) (%vector-fold1 (lambda (index count elt) (if (pred? index elt) (+ count 1) count)) 0 (vector-length vec) vec) (%vector-fold2+ (lambda (index count . elts) (if (apply pred? index elts) (+ count 1) count)) 0 (%smallest-length vectors (vector-length vec) vector-count) (cons vec vectors))))) ;;; -------------------- ;;; Searching ;;; (VECTOR-INDEX <predicate?> <vector> ...) ;;; -> exact, nonnegative integer or #F ;;; (PREDICATE? <elt> ...) -> boolean ; N vectors -> N args ;;; Search left-to-right across VECTOR ... in parallel, returning the ;;; index of the first set of values VALUE ... such that (PREDICATE? ;;; VALUE ...) returns a true value; if no such set of elements is ;;; reached, return #F. (define (vector-index pred? vec . vectors) (vector-index/skip pred? vec vectors vector-index)) ;;; (VECTOR-SKIP <predicate?> <vector> ...) ;;; -> exact, nonnegative integer or #F ;;; (PREDICATE? <elt> ...) -> boolean ; N vectors -> N args ;;; (vector-index (lambda elts (not (apply PREDICATE? elts))) ;;; VECTOR ...) ;;; Like VECTOR-INDEX, but find the index of the first set of values ;;; that do _not_ satisfy PREDICATE?. (define (vector-skip pred? vec . vectors) (vector-index/skip (lambda elts (not (apply pred? elts))) vec vectors vector-skip)) ;;; Auxiliary for VECTOR-INDEX & VECTOR-SKIP (define vector-index/skip (letrec ((loop1 (lambda (pred? vec len i) (cond ((= i len) #f) ((pred? (vector-ref vec i)) i) (else (loop1 pred? vec len (+ i 1)))))) (loop2+ (lambda (pred? vectors len i) (cond ((= i len) #f) ((apply pred? (vectors-ref vectors i)) i) (else (loop2+ pred? vectors len (+ i 1))))))) (lambda (pred? vec vectors callee) (let ((pred? (check-type procedure? pred? callee)) (vec (check-type vector? vec callee))) (if (null? vectors) (loop1 pred? vec (vector-length vec) 0) (loop2+ pred? (cons vec vectors) (%smallest-length vectors (vector-length vec) callee) 0)))))) ;;; (VECTOR-INDEX-RIGHT <predicate?> <vector> ...) ;;; -> exact, nonnegative integer or #F ;;; (PREDICATE? <elt> ...) -> boolean ; N vectors -> N args ;;; Right-to-left variant of VECTOR-INDEX. (define (vector-index-right pred? vec . vectors) (vector-index/skip-right pred? vec vectors vector-index-right)) ;;; (VECTOR-SKIP-RIGHT <predicate?> <vector> ...) ;;; -> exact, nonnegative integer or #F ;;; (PREDICATE? <elt> ...) -> boolean ; N vectors -> N args ;;; Right-to-left variant of VECTOR-SKIP. (define (vector-skip-right pred? vec . vectors) (vector-index/skip-right (lambda elts (not (apply pred? elts))) vec vectors vector-index-right)) (define vector-index/skip-right (letrec ((loop1 (lambda (pred? vec i) (cond ((negative? i) #f) ((pred? (vector-ref vec i)) i) (else (loop1 pred? vec (- i 1)))))) (loop2+ (lambda (pred? vectors i) (cond ((negative? i) #f) ((apply pred? (vectors-ref vectors i)) i) (else (loop2+ pred? vectors (- i 1))))))) (lambda (pred? vec vectors callee) (let ((pred? (check-type procedure? pred? callee)) (vec (check-type vector? vec callee))) (if (null? vectors) (loop1 pred? vec (- (vector-length vec) 1)) (loop2+ pred? (cons vec vectors) (- (%smallest-length vectors (vector-length vec) callee) 1))))))) ;;; (VECTOR-BINARY-SEARCH <vector> <value> <cmp> [<start> <end>]) ;;; -> exact, nonnegative integer or #F ;;; (CMP <value1> <value2>) -> integer ;;; positive -> VALUE1 > VALUE2 ;;; zero -> VALUE1 = VALUE2 ;;; negative -> VALUE1 < VALUE2 ;;; Perform a binary search through VECTOR for VALUE, comparing each ;;; element to VALUE with CMP. (define (vector-binary-search vec value cmp . maybe-start+end) (let ((cmp (check-type procedure? cmp vector-binary-search))) (let-vector-start+end vector-binary-search vec maybe-start+end (start end) (let loop ((start start) (end end) (j #f)) (let ((i (quotient (+ start end) 2))) (if (or (= start end) (and j (= i j))) #f (let ((comparison (check-type integer? (cmp (vector-ref vec i) value) `(,cmp for ,vector-binary-search)))) (cond ((zero? comparison) i) ((positive? comparison) (loop start i i)) (else (loop i end i)))))))))) ;;; (VECTOR-ANY <pred?> <vector> ...) -> value ;;; Apply PRED? to each parallel element in each VECTOR ...; if PRED? ;;; should ever return a true value, immediately stop and return that ;;; value; otherwise, when the shortest vector runs out, return #F. ;;; The iteration and order of application of PRED? across elements ;;; is of the vectors is strictly left-to-right. (define vector-any (letrec ((loop1 (lambda (pred? vec i len len-1) (and (not (= i len)) (if (= i len-1) (pred? (vector-ref vec i)) (or (pred? (vector-ref vec i)) (loop1 pred? vec (+ i 1) len len-1)))))) (loop2+ (lambda (pred? vectors i len len-1) (and (not (= i len)) (if (= i len-1) (apply pred? (vectors-ref vectors i)) (or (apply pred? (vectors-ref vectors i)) (loop2+ pred? vectors (+ i 1) len len-1))))))) (lambda (pred? vec . vectors) (let ((pred? (check-type procedure? pred? vector-any)) (vec (check-type vector? vec vector-any))) (if (null? vectors) (let ((len (vector-length vec))) (loop1 pred? vec 0 len (- len 1))) (let ((len (%smallest-length vectors (vector-length vec) vector-any))) (loop2+ pred? (cons vec vectors) 0 len (- len 1)))))))) ;;; (VECTOR-EVERY <pred?> <vector> ...) -> value ;;; Apply PRED? to each parallel value in each VECTOR ...; if PRED? ;;; should ever return #F, immediately stop and return #F; otherwise, ;;; if PRED? should return a true value for each element, stopping at ;;; the end of the shortest vector, return the last value that PRED? ;;; returned. In the case that there is an empty vector, return #T. ;;; The iteration and order of application of PRED? across elements ;;; is of the vectors is strictly left-to-right. (define vector-every (letrec ((loop1 (lambda (pred? vec i len len-1) (or (= i len) (if (= i len-1) (pred? (vector-ref vec i)) (and (pred? (vector-ref vec i)) (loop1 pred? vec (+ i 1) len len-1)))))) (loop2+ (lambda (pred? vectors i len len-1) (or (= i len) (if (= i len-1) (apply pred? (vectors-ref vectors i)) (and (apply pred? (vectors-ref vectors i)) (loop2+ pred? vectors (+ i 1) len len-1))))))) (lambda (pred? vec . vectors) (let ((pred? (check-type procedure? pred? vector-every)) (vec (check-type vector? vec vector-every))) (if (null? vectors) (let ((len (vector-length vec))) (loop1 pred? vec 0 len (- len 1))) (let ((len (%smallest-length vectors (vector-length vec) vector-every))) (loop2+ pred? (cons vec vectors) 0 len (- len 1)))))))) ;;; -------------------- ;;; Mutators ;;; (VECTOR-SET! <vector> <index> <value>) -> unspecified ;;; [R5RS] Assign the location at INDEX in VECTOR to VALUE. ;; (define vector-set! vector-set!) ;;; (VECTOR-SWAP! <vector> <index1> <index2>) -> unspecified ;;; Swap the values in the locations at INDEX1 and INDEX2. (define (vector-swap! vec i j) (let ((vec (check-type vector? vec vector-swap!))) (let ((i (check-index vec i vector-swap!)) (j (check-index vec j vector-swap!))) (let ((x (vector-ref vec i))) (vector-set! vec i (vector-ref vec j)) (vector-set! vec j x))))) ;;; (VECTOR-FILL! <vector> <value> [<start> <end>]) -> unspecified ;;; [R5RS+] Fill the locations in VECTOR between START, whose default ;;; is 0, and END, whose default is the length of VECTOR, with VALUE. ;;; ;;; This one can probably be made really fast natively. (define vector-fill! (let ((%vector-fill! vector-fill!)) ; Take the native one, under ; the assumption that it's ; faster, so we can use it if ; there are no optional ; arguments. (lambda (vec value . maybe-start+end) (if (null? maybe-start+end) (%vector-fill! vec value) ;+++ (let-vector-start+end vector-fill! vec maybe-start+end (start end) (do ((i start (+ i 1))) ((= i end)) (vector-set! vec i value))))))) ;;; (VECTOR-COPY! <target> <tstart> <source> [<sstart> <send>]) ;;; -> unspecified ;;; Copy the values in the locations in [SSTART,SEND) from SOURCE to ;;; to TARGET, starting at TSTART in TARGET. ;;; [wdc] Corrected to allow 0 <= sstart <= send <= (vector-length source). (define (vector-copy! target tstart source . maybe-sstart+send) (define (doit! sstart send source-length) (let ((tstart (check-type nonneg-int? tstart vector-copy!)) (sstart (check-type nonneg-int? sstart vector-copy!)) (send (check-type nonneg-int? send vector-copy!))) (cond ((and (<= 0 sstart send source-length) (<= (+ tstart (- send sstart)) (vector-length target))) (%vector-copy! target tstart source sstart send)) (else (error "illegal arguments" `(while calling ,vector-copy!) `(target was ,target) `(target-length was ,(vector-length target)) `(tstart was ,tstart) `(source was ,source) `(source-length was ,source-length) `(sstart was ,sstart) `(send was ,send)))))) (let ((n (vector-length source))) (cond ((null? maybe-sstart+send) (doit! 0 n n)) ((null? (cdr maybe-sstart+send)) (doit! (car maybe-sstart+send) n n)) ((null? (cddr maybe-sstart+send)) (doit! (car maybe-sstart+send) (cadr maybe-sstart+send) n)) (else (error "too many arguments" vector-copy! (cddr maybe-sstart+send)))))) ;;; (VECTOR-REVERSE-COPY! <target> <tstart> <source> [<sstart> <send>]) ;;; [wdc] Corrected to allow 0 <= sstart <= send <= (vector-length source). (define (vector-reverse-copy! target tstart source . maybe-sstart+send) (define (doit! sstart send source-length) (let ((tstart (check-type nonneg-int? tstart vector-reverse-copy!)) (sstart (check-type nonneg-int? sstart vector-reverse-copy!)) (send (check-type nonneg-int? send vector-reverse-copy!))) (cond ((and (eq? target source) (or (between? sstart tstart send) (between? tstart sstart (+ tstart (- send sstart))))) (error "vector range for self-copying overlaps" vector-reverse-copy! `(vector was ,target) `(tstart was ,tstart) `(sstart was ,sstart) `(send was ,send))) ((and (<= 0 sstart send source-length) (<= (+ tstart (- send sstart)) (vector-length target))) (%vector-reverse-copy! target tstart source sstart send)) (else (error "illegal arguments" `(while calling ,vector-reverse-copy!) `(target was ,target) `(target-length was ,(vector-length target)) `(tstart was ,tstart) `(source was ,source) `(source-length was ,source-length) `(sstart was ,sstart) `(send was ,send)))))) (let ((n (vector-length source))) (cond ((null? maybe-sstart+send) (doit! 0 n n)) ((null? (cdr maybe-sstart+send)) (doit! (car maybe-sstart+send) n n)) ((null? (cddr maybe-sstart+send)) (doit! (car maybe-sstart+send) (cadr maybe-sstart+send) n)) (else (error "too many arguments" vector-reverse-copy! (cddr maybe-sstart+send)))))) ;;; (VECTOR-REVERSE! <vector> [<start> <end>]) -> unspecified ;;; Destructively reverse the contents of the sequence of locations ;;; in VECTOR between START, whose default is 0, and END, whose ;;; default is the length of VECTOR. (define (vector-reverse! vec . start+end) (let-vector-start+end vector-reverse! vec start+end (start end) (%vector-reverse! vec start end))) ;;; -------------------- ;;; Conversion ;;; (VECTOR->LIST <vector> [<start> <end>]) -> list ;;; [R5RS+] Produce a list containing the elements in the locations ;;; between START, whose default is 0, and END, whose default is the ;;; length of VECTOR, from VECTOR. (define vector->list (let ((%vector->list vector->list)) (lambda (vec . maybe-start+end) (if (null? maybe-start+end) ; Oughta use CASE-LAMBDA. (%vector->list vec) ;+++ (let-vector-start+end vector->list vec maybe-start+end (start end) ;(unfold (lambda (i) ; No SRFI 1. ; (< i start)) ; (lambda (i) (vector-ref vec i)) ; (lambda (i) (- i 1)) ; (- end 1)) (do ((i (- end 1) (- i 1)) (result '() (cons (vector-ref vec i) result))) ((< i start) result))))))) ;;; (REVERSE-VECTOR->LIST <vector> [<start> <end>]) -> list ;;; Produce a list containing the elements in the locations between ;;; START, whose default is 0, and END, whose default is the length ;;; of VECTOR, from VECTOR, in reverse order. (define (reverse-vector->list vec . maybe-start+end) (let-vector-start+end reverse-vector->list vec maybe-start+end (start end) ;(unfold (lambda (i) (= i end)) ; No SRFI 1. ; (lambda (i) (vector-ref vec i)) ; (lambda (i) (+ i 1)) ; start) (do ((i start (+ i 1)) (result '() (cons (vector-ref vec i) result))) ((= i end) result)))) ;;; (LIST->VECTOR <list> [<start> <end>]) -> vector ;;; [R5RS+] Produce a vector containing the elements in LIST, which ;;; must be a proper list, between START, whose default is 0, & END, ;;; whose default is the length of LIST. It is suggested that if the ;;; length of LIST is known in advance, the START and END arguments ;;; be passed, so that LIST->VECTOR need not call LENGTH to determine ;;; the the length. ;;; ;;; This implementation diverges on circular lists, unless LENGTH fails ;;; and causes - to fail as well. Given a LENGTH* that computes the ;;; length of a list's cycle, this wouldn't diverge, and would work ;;; great for circular lists. (define list->vector (let ((%list->vector list->vector)) (lambda (lst . maybe-start+end) ;; Checking the type of a proper list is expensive, so we do it ;; amortizedly, or let %LIST->VECTOR or LIST-TAIL do it. (if (null? maybe-start+end) ; Oughta use CASE-LAMBDA. (%list->vector lst) ;+++ ;; We can't use LET-VECTOR-START+END, because we're using the ;; bounds of a _list_, not a vector. (let*-optionals maybe-start+end ((start 0) (end (length lst))) ; Ugh -- LENGTH (let ((start (check-type nonneg-int? start list->vector)) (end (check-type nonneg-int? end list->vector))) ((lambda (f) (vector-unfold f (- end start) (list-tail lst start))) (lambda (index l) (cond ((null? l) (error "list was too short" `(list was ,lst) `(attempted end was ,end) `(while calling ,list->vector))) ((pair? l) (values (car l) (cdr l))) (else ;; Make this look as much like what CHECK-TYPE ;; would report as possible. (error "erroneous value" ;; We want SRFI 1's PROPER-LIST?, but it ;; would be a waste to link all of SRFI ;; 1 to this module for only the single ;; function PROPER-LIST?. (list list? lst) `(while calling ,list->vector)))))))))))) ;;; (REVERSE-LIST->VECTOR <list> [<start> <end>]) -> vector ;;; Produce a vector containing the elements in LIST, which must be a ;;; proper list, between START, whose default is 0, and END, whose ;;; default is the length of LIST, in reverse order. It is suggested ;;; that if the length of LIST is known in advance, the START and END ;;; arguments be passed, so that REVERSE-LIST->VECTOR need not call ;;; LENGTH to determine the the length. ;;; ;;; This also diverges on circular lists unless, again, LENGTH returns ;;; something that makes - bork. (define (reverse-list->vector lst . maybe-start+end) (let*-optionals maybe-start+end ((start 0) (end (length lst))) ; Ugh -- LENGTH (let ((start (check-type nonneg-int? start reverse-list->vector)) (end (check-type nonneg-int? end reverse-list->vector))) ((lambda (f) (vector-unfold-right f (- end start) (list-tail lst start))) (lambda (index l) (cond ((null? l) (error "list too short" `(list was ,lst) `(attempted end was ,end) `(while calling ,reverse-list->vector))) ((pair? l) (values (car l) (cdr l))) (else (error "erroneous value" (list list? lst) `(while calling ,reverse-list->vector))))))))) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ) |
Added srfi/s43/_vectors.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s43 vectors) (export ;;; * Constructors make-vector vector vector-unfold vector-unfold-right vector-copy vector-reverse-copy vector-append vector-concatenate ;;; * Predicates vector? vector-empty? vector= ;;; * Selectors vector-ref vector-length ;;; * Iteration vector-fold vector-fold-right vector-map vector-map! vector-for-each vector-count ;;; * Searching vector-index vector-skip vector-index-right vector-skip-right vector-binary-search vector-any vector-every ;;; * Mutators vector-set! vector-swap! (rename (my:vector-fill! vector-fill!)) vector-reverse! vector-copy! vector-reverse-copy! ;;; * Conversion (rename (my:vector->list vector->list)) reverse-vector->list (rename (my:list->vector list->vector)) reverse-list->vector ) (import (except (rnrs) vector-map vector-for-each) (rnrs r5rs) (srfi s23 error tricks) (srfi s8 receive) (for (srfi private vanish) expand) (srfi private include)) ;; I do these let-syntax tricks so the original vector-lib.scm file does ;; not have to be modified at all. (let-syntax ((define (let ((vd (vanish-define define (make-vector vector vector? vector-ref vector-set! vector-length)))) (lambda (stx) (define (rename? id) (memp (lambda (x) (free-identifier=? id x)) (list #'vector-fill! #'vector->list #'list->vector))) (define (rename id) (datum->syntax id (string->symbol (string-append "my:" (symbol->string (syntax->datum id)))))) (syntax-case stx () ((_ name . r) (and (identifier? #'name) (rename? #'name)) #`(define #,(rename #'name) . r)) (_ (vd stx)))))) (define-syntax (vanish-define define-syntax (receive)))) (SRFI-23-error->R6RS "(library (srfi s43 vectors))" (include/resolve ("srfi" "s43") "vector-lib.scm"))) ) |
Added srfi/s43/vector-lib.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 |
;;;;;; SRFI 43: Vector library -*- Scheme -*- ;;; ;;; $Id: vector-lib.scm,v 1.7 2009/03/29 09:46:03 sperber Exp $ ;;; ;;; Taylor Campbell wrote this code; he places it in the public domain. ;;; Will Clinger [wdc] made some corrections, also in the public domain. ;;; -------------------- ;;; Exported procedure index ;;; ;;; * Constructors ;;; make-vector vector ;;; vector-unfold vector-unfold-right ;;; vector-copy vector-reverse-copy ;;; vector-append vector-concatenate ;;; ;;; * Predicates ;;; vector? ;;; vector-empty? ;;; vector= ;;; ;;; * Selectors ;;; vector-ref ;;; vector-length ;;; ;;; * Iteration ;;; vector-fold vector-fold-right ;;; vector-map vector-map! ;;; vector-for-each ;;; vector-count ;;; ;;; * Searching ;;; vector-index vector-skip ;;; vector-index-right vector-skip-right ;;; vector-binary-search ;;; vector-any vector-every ;;; ;;; * Mutators ;;; vector-set! ;;; vector-swap! ;;; vector-fill! ;;; vector-reverse! ;;; vector-copy! vector-reverse-copy! ;;; vector-reverse! ;;; ;;; * Conversion ;;; vector->list reverse-vector->list ;;; list->vector reverse-list->vector ;;; -------------------- ;;; Commentary on efficiency of the code ;;; This code is somewhat tuned for efficiency. There are several ;;; internal routines that can be optimized greatly to greatly improve ;;; the performance of much of the library. These internal procedures ;;; are already carefully tuned for performance, and lambda-lifted by ;;; hand. Some other routines are lambda-lifted by hand, but only the ;;; loops are lambda-lifted, and only if some routine has two possible ;;; loops -- a fast path and an n-ary case --, whereas _all_ of the ;;; internal routines' loops are lambda-lifted so as to never cons a ;;; closure in their body (VECTOR-PARSE-START+END doesn't have a loop), ;;; even in Scheme systems that perform no loop optimization (which is ;;; most of them, unfortunately). ;;; ;;; Fast paths are provided for common cases in most of the loops in ;;; this library. ;;; ;;; All calls to primitive vector operations are protected by a prior ;;; type check; they can be safely converted to use unsafe equivalents ;;; of the operations, if available. Ideally, the compiler should be ;;; able to determine this, but the state of Scheme compilers today is ;;; not a happy one. ;;; ;;; Efficiency of the actual algorithms is a rather mundane point to ;;; mention; vector operations are rarely beyond being straightforward. ;;; -------------------- ;;; Utilities ;;; SRFI 8, too trivial to put in the dependencies list. (define-syntax receive (syntax-rules () ((receive ?formals ?producer ?body1 ?body2 ...) (call-with-values (lambda () ?producer) (lambda ?formals ?body1 ?body2 ...))))) ;;; Not the best LET*-OPTIONALS, but not the worst, either. Use Olin's ;;; if it's available to you. (define-syntax let*-optionals (syntax-rules () ((let*-optionals (?x ...) ((?var ?default) ...) ?body1 ?body2 ...) (let ((args (?x ...))) (let*-optionals args ((?var ?default) ...) ?body1 ?body2 ...))) ((let*-optionals ?args ((?var ?default) ...) ?body1 ?body2 ...) (let*-optionals:aux ?args ?args ((?var ?default) ...) ?body1 ?body2 ...)))) (define-syntax let*-optionals:aux (syntax-rules () ((aux ?orig-args-var ?args-var () ?body1 ?body2 ...) (if (null? ?args-var) (let () ?body1 ?body2 ...) (error "too many arguments" (length ?orig-args-var) ?orig-args-var))) ((aux ?orig-args-var ?args-var ((?var ?default) ?more ...) ?body1 ?body2 ...) (if (null? ?args-var) (let* ((?var ?default) ?more ...) ?body1 ?body2 ...) (let ((?var (car ?args-var)) (new-args (cdr ?args-var))) (let*-optionals:aux ?orig-args-var new-args (?more ...) ?body1 ?body2 ...)))))) (define (nonneg-int? x) (and (integer? x) (not (negative? x)))) (define (between? x y z) (and (< x y) (<= y z))) (define (unspecified-value) (if #f #f)) ;++ This should be implemented more efficiently. It shouldn't cons a ;++ closure, and the cons cells used in the loops when using this could ;++ be reused. (define (vectors-ref vectors i) (map (lambda (v) (vector-ref v i)) vectors)) ;;; -------------------- ;;; Error checking ;;; Error signalling (not checking) is done in a way that tries to be ;;; as helpful to the person who gets the debugging prompt as possible. ;;; That said, error _checking_ tries to be as unredundant as possible. ;;; I don't use any sort of general condition mechanism; I use simply ;;; SRFI 23's ERROR, even in cases where it might be better to use such ;;; a general condition mechanism. Fix that when porting this to a ;;; Scheme implementation that has its own condition system. ;;; In argument checks, upon receiving an invalid argument, the checker ;;; procedure recursively calls itself, but in one of the arguments to ;;; itself is a call to ERROR; this mechanism is used in the hopes that ;;; the user may be thrown into a debugger prompt, proceed with another ;;; value, and let it be checked again. ;;; Type checking is pretty basic, but easily factored out and replaced ;;; with whatever your implementation's preferred type checking method ;;; is. I doubt there will be many other methods of index checking, ;;; though the index checkers might be better implemented natively. ;;; (CHECK-TYPE <type-predicate?> <value> <callee>) -> value ;;; Ensure that VALUE satisfies TYPE-PREDICATE?; if not, signal an ;;; error stating that VALUE did not satisfy TYPE-PREDICATE?, showing ;;; that this happened while calling CALLEE. Return VALUE if no ;;; error was signalled. (define (check-type pred? value callee) (if (pred? value) value ;; Recur: when (or if) the user gets a debugger prompt, he can ;; proceed where the call to ERROR was with the correct value. (check-type pred? (error "erroneous value" (list pred? value) `(while calling ,callee)) callee))) ;;; (CHECK-INDEX <vector> <index> <callee>) -> index ;;; Ensure that INDEX is a valid index into VECTOR; if not, signal an ;;; error stating that it is not and that this happened in a call to ;;; CALLEE. Return INDEX when it is valid. (Note that this does NOT ;;; check that VECTOR is indeed a vector.) (define (check-index vec index callee) (let ((index (check-type integer? index callee))) (cond ((< index 0) (check-index vec (error "vector index too low" index `(into vector ,vec) `(while calling ,callee)) callee)) ((>= index (vector-length vec)) (check-index vec (error "vector index too high" index `(into vector ,vec) `(while calling ,callee)) callee)) (else index)))) ;;; (CHECK-INDICES <vector> ;;; <start> <start-name> ;;; <end> <end-name> ;;; <caller>) -> [start end] ;;; Ensure that START and END are valid bounds of a range within ;;; VECTOR; if not, signal an error stating that they are not, with ;;; the message being informative about what the argument names were ;;; called -- by using START-NAME & END-NAME --, and that it occurred ;;; while calling CALLEE. Also ensure that VEC is in fact a vector. ;;; Returns no useful value. (define (check-indices vec start start-name end end-name callee) (let ((lose (lambda things (apply error "vector range out of bounds" (append things `(vector was ,vec) `(,start-name was ,start) `(,end-name was ,end) `(while calling ,callee))))) (start (check-type integer? start callee)) (end (check-type integer? end callee))) (cond ((> start end) ;; I'm not sure how well this will work. The intent is that ;; the programmer tells the debugger to proceed with both a ;; new START & a new END by returning multiple values ;; somewhere. (receive (new-start new-end) (lose `(,end-name < ,start-name)) (check-indices vec new-start start-name new-end end-name callee))) ((< start 0) (check-indices vec (lose `(,start-name < 0)) start-name end end-name callee)) ((>= start (vector-length vec)) (check-indices vec (lose `(,start-name > len) `(len was ,(vector-length vec))) start-name end end-name callee)) ((> end (vector-length vec)) (check-indices vec start start-name (lose `(,end-name > len) `(len was ,(vector-length vec))) end-name callee)) (else (values start end))))) ;;; -------------------- ;;; Internal routines ;;; These should all be integrated, native, or otherwise optimized -- ;;; they're used a _lot_ --. All of the loops and LETs inside loops ;;; are lambda-lifted by hand, just so as not to cons closures in the ;;; loops. (If your compiler can do better than that if they're not ;;; lambda-lifted, then lambda-drop (?) them.) ;;; (VECTOR-PARSE-START+END <vector> <arguments> ;;; <start-name> <end-name> ;;; <callee>) ;;; -> [start end] ;;; Return two values, composing a valid range within VECTOR, as ;;; extracted from ARGUMENTS or defaulted from VECTOR -- 0 for START ;;; and the length of VECTOR for END --; START-NAME and END-NAME are ;;; purely for error checking. (define (vector-parse-start+end vec args start-name end-name callee) (let ((len (vector-length vec))) (cond ((null? args) (values 0 len)) ((null? (cdr args)) (check-indices vec (car args) start-name len end-name callee)) ((null? (cddr args)) (check-indices vec (car args) start-name (cadr args) end-name callee)) (else (error "too many arguments" `(extra args were ,(cddr args)) `(while calling ,callee)))))) (define-syntax let-vector-start+end (syntax-rules () ((let-vector-start+end ?callee ?vec ?args (?start ?end) ?body1 ?body2 ...) (let ((?vec (check-type vector? ?vec ?callee))) (receive (?start ?end) (vector-parse-start+end ?vec ?args '?start '?end ?callee) ?body1 ?body2 ...))))) ;;; (%SMALLEST-LENGTH <vector-list> <default-length> <callee>) ;;; -> exact, nonnegative integer ;;; Compute the smallest length of VECTOR-LIST. DEFAULT-LENGTH is ;;; the length that is returned if VECTOR-LIST is empty. Common use ;;; of this is in n-ary vector routines: ;;; (define (f vec . vectors) ;;; (let ((vec (check-type vector? vec f))) ;;; ...(%smallest-length vectors (vector-length vec) f)...)) ;;; %SMALLEST-LENGTH takes care of the type checking -- which is what ;;; the CALLEE argument is for --; thus, the design is tuned for ;;; avoiding redundant type checks. (define %smallest-length (letrec ((loop (lambda (vector-list length callee) (if (null? vector-list) length (loop (cdr vector-list) (min (vector-length (check-type vector? (car vector-list) callee)) length) callee))))) loop)) ;;; (%VECTOR-COPY! <target> <tstart> <source> <sstart> <send>) ;;; Copy elements at locations SSTART to SEND from SOURCE to TARGET, ;;; starting at TSTART in TARGET. ;;; ;;; Optimize this! Probably with some combination of: ;;; - Force it to be integrated. ;;; - Let it use unsafe vector element dereferencing routines: bounds ;;; checking already happens outside of it. (Or use a compiler ;;; that figures this out, but Olin Shivers' PhD thesis seems to ;;; have been largely ignored in actual implementations...) ;;; - Implement it natively as a VM primitive: the VM can undoubtedly ;;; perform much faster than it can make Scheme perform, even with ;;; bounds checking. ;;; - Implement it in assembly: you _want_ the fine control that ;;; assembly can give you for this. ;;; I already lambda-lift it by hand, but you should be able to make it ;;; even better than that. (define %vector-copy! (letrec ((loop/l->r (lambda (target source send i j) (cond ((< i send) (vector-set! target j (vector-ref source i)) (loop/l->r target source send (+ i 1) (+ j 1)))))) (loop/r->l (lambda (target source sstart i j) (cond ((>= i sstart) (vector-set! target j (vector-ref source i)) (loop/r->l target source sstart (- i 1) (- j 1))))))) (lambda (target tstart source sstart send) (if (> sstart tstart) ; Make sure we don't copy over ; ourselves. (loop/l->r target source send sstart tstart) (loop/r->l target source sstart (- send 1) (+ -1 tstart send (- sstart))))))) ;;; (%VECTOR-REVERSE-COPY! <target> <tstart> <source> <sstart> <send>) ;;; Copy elements from SSTART to SEND from SOURCE to TARGET, in the ;;; reverse order. (define %vector-reverse-copy! (letrec ((loop (lambda (target source sstart i j) (cond ((>= i sstart) (vector-set! target j (vector-ref source i)) (loop target source sstart (- i 1) (+ j 1))))))) (lambda (target tstart source sstart send) (loop target source sstart (- send 1) tstart)))) ;;; (%VECTOR-REVERSE! <vector>) (define %vector-reverse! (letrec ((loop (lambda (vec i j) (cond ((<= i j) (let ((v (vector-ref vec i))) (vector-set! vec i (vector-ref vec j)) (vector-set! vec j v) (loop vec (+ i 1) (- j 1)))))))) (lambda (vec start end) (loop vec start (- end 1))))) ;;; (%VECTOR-FOLD1 <kons> <knil> <vector>) -> knil' ;;; (KONS <index> <knil> <elt>) -> knil' (define %vector-fold1 (letrec ((loop (lambda (kons knil len vec i) (if (= i len) knil (loop kons (kons i knil (vector-ref vec i)) len vec (+ i 1)))))) (lambda (kons knil len vec) (loop kons knil len vec 0)))) ;;; (%VECTOR-FOLD2+ <kons> <knil> <vector> ...) -> knil' ;;; (KONS <index> <knil> <elt> ...) -> knil' (define %vector-fold2+ (letrec ((loop (lambda (kons knil len vectors i) (if (= i len) knil (loop kons (apply kons i knil (vectors-ref vectors i)) len vectors (+ i 1)))))) (lambda (kons knil len vectors) (loop kons knil len vectors 0)))) ;;; (%VECTOR-MAP! <f> <target> <length> <vector>) -> target ;;; (F <index> <elt>) -> elt' (define %vector-map1! (letrec ((loop (lambda (f target vec i) (if (zero? i) target (let ((j (- i 1))) (vector-set! target j (f j (vector-ref vec j))) (loop f target vec j)))))) (lambda (f target vec len) (loop f target vec len)))) ;;; (%VECTOR-MAP2+! <f> <target> <vectors> <len>) -> target ;;; (F <index> <elt> ...) -> elt' (define %vector-map2+! (letrec ((loop (lambda (f target vectors i) (if (zero? i) target (let ((j (- i 1))) (vector-set! target j (apply f j (vectors-ref vectors j))) (loop f target vectors j)))))) (lambda (f target vectors len) (loop f target vectors len)))) ;;;;;;;;;;;;;;;;;;;;;;;; ***** vector-lib ***** ;;;;;;;;;;;;;;;;;;;;;;; ;;; -------------------- ;;; Constructors ;;; (MAKE-VECTOR <size> [<fill>]) -> vector ;;; [R5RS] Create a vector of length LENGTH. If FILL is present, ;;; initialize each slot in the vector with it; if not, the vector's ;;; initial contents are unspecified. (define make-vector make-vector) ;;; (VECTOR <elt> ...) -> vector ;;; [R5RS] Create a vector containing ELEMENT ..., in order. (define vector vector) ;;; This ought to be able to be implemented much more efficiently -- if ;;; we have the number of arguments available to us, we can create the ;;; vector without using LENGTH to determine the number of elements it ;;; should have. ;(define (vector . elements) (list->vector elements)) ;;; (VECTOR-UNFOLD <f> <length> <initial-seed> ...) -> vector ;;; (F <index> <seed> ...) -> [elt seed' ...] ;;; The fundamental vector constructor. Creates a vector whose ;;; length is LENGTH and iterates across each index K between 0 and ;;; LENGTH, applying F at each iteration to the current index and the ;;; current seeds to receive N+1 values: first, the element to put in ;;; the Kth slot and then N new seeds for the next iteration. (define vector-unfold (letrec ((tabulate! ; Special zero-seed case. (lambda (f vec i len) (cond ((< i len) (vector-set! vec i (f i)) (tabulate! f vec (+ i 1) len))))) (unfold1! ; Fast path for one seed. (lambda (f vec i len seed) (if (< i len) (receive (elt new-seed) (f i seed) (vector-set! vec i elt) (unfold1! f vec (+ i 1) len new-seed))))) (unfold2+! ; Slower variant for N seeds. (lambda (f vec i len seeds) (if (< i len) (receive (elt . new-seeds) (apply f i seeds) (vector-set! vec i elt) (unfold2+! f vec (+ i 1) len new-seeds)))))) (lambda (f len . initial-seeds) (let ((f (check-type procedure? f vector-unfold)) (len (check-type nonneg-int? len vector-unfold))) (let ((vec (make-vector len))) (cond ((null? initial-seeds) (tabulate! f vec 0 len)) ((null? (cdr initial-seeds)) (unfold1! f vec 0 len (car initial-seeds))) (else (unfold2+! f vec 0 len initial-seeds))) vec))))) ;;; (VECTOR-UNFOLD-RIGHT <f> <length> <initial-seed> ...) -> vector ;;; (F <seed> ...) -> [seed' ...] ;;; Like VECTOR-UNFOLD, but it generates elements from LENGTH to 0 ;;; (still exclusive with LENGTH and inclusive with 0), not 0 to ;;; LENGTH as with VECTOR-UNFOLD. (define vector-unfold-right (letrec ((tabulate! (lambda (f vec i) (cond ((>= i 0) (vector-set! vec i (f i)) (tabulate! f vec (- i 1)))))) (unfold1! (lambda (f vec i seed) (if (>= i 0) (receive (elt new-seed) (f i seed) (vector-set! vec i elt) (unfold1! f vec (- i 1) new-seed))))) (unfold2+! (lambda (f vec i seeds) (if (>= i 0) (receive (elt . new-seeds) (apply f i seeds) (vector-set! vec i elt) (unfold2+! f vec (- i 1) new-seeds)))))) (lambda (f len . initial-seeds) (let ((f (check-type procedure? f vector-unfold-right)) (len (check-type nonneg-int? len vector-unfold-right))) (let ((vec (make-vector len)) (i (- len 1))) (cond ((null? initial-seeds) (tabulate! f vec i)) ((null? (cdr initial-seeds)) (unfold1! f vec i (car initial-seeds))) (else (unfold2+! f vec i initial-seeds))) vec))))) ;;; (VECTOR-COPY <vector> [<start> <end> <fill>]) -> vector ;;; Create a newly allocated vector containing the elements from the ;;; range [START,END) in VECTOR. START defaults to 0; END defaults ;;; to the length of VECTOR. END may be greater than the length of ;;; VECTOR, in which case the vector is enlarged; if FILL is passed, ;;; the new locations from which there is no respective element in ;;; VECTOR are filled with FILL. (define (vector-copy vec . args) (let ((vec (check-type vector? vec vector-copy))) ;; We can't use LET-VECTOR-START+END, because we have one more ;; argument, and we want finer control, too. ;; ;; Olin's implementation of LET*-OPTIONALS would prove useful here: ;; the built-in argument-checks-as-you-go-along produces almost ;; _exactly_ the same code as VECTOR-COPY:PARSE-ARGS. (receive (start end fill) (vector-copy:parse-args vec args) (let ((new-vector (make-vector (- end start) fill))) (%vector-copy! new-vector 0 vec start (if (> end (vector-length vec)) (vector-length vec) end)) new-vector)))) ;;; Auxiliary for VECTOR-COPY. ;;; [wdc] Corrected to allow 0 <= start <= (vector-length vec). (define (vector-copy:parse-args vec args) (define (parse-args start end n fill) (let ((start (check-type nonneg-int? start vector-copy)) (end (check-type nonneg-int? end vector-copy))) (cond ((and (<= 0 start end) (<= start n)) (values start end fill)) (else (error "illegal arguments" `(while calling ,vector-copy) `(start was ,start) `(end was ,end) `(vector was ,vec)))))) (let ((n (vector-length vec))) (cond ((null? args) (parse-args 0 n n (unspecified-value))) ((null? (cdr args)) (parse-args (car args) n n (unspecified-value))) ((null? (cddr args)) (parse-args (car args) (cadr args) n (unspecified-value))) ((null? (cdddr args)) (parse-args (car args) (cadr args) n (caddr args))) (else (error "too many arguments" vector-copy (cdddr args)))))) ;;; (VECTOR-REVERSE-COPY <vector> [<start> <end>]) -> vector ;;; Create a newly allocated vector whose elements are the reversed ;;; sequence of elements between START and END in VECTOR. START's ;;; default is 0; END's default is the length of VECTOR. (define (vector-reverse-copy vec . maybe-start+end) (let-vector-start+end vector-reverse-copy vec maybe-start+end (start end) (let ((new (make-vector (- end start)))) (%vector-reverse-copy! new 0 vec start end) new))) ;;; (VECTOR-APPEND <vector> ...) -> vector ;;; Append VECTOR ... into a newly allocated vector and return that ;;; new vector. (define (vector-append . vectors) (vector-concatenate:aux vectors vector-append)) ;;; (VECTOR-CONCATENATE <vector-list>) -> vector ;;; Concatenate the vectors in VECTOR-LIST. This is equivalent to ;;; (apply vector-append VECTOR-LIST) ;;; but VECTOR-APPEND tends to be implemented in terms of ;;; VECTOR-CONCATENATE, and some Schemes bork when the list to apply ;;; a function to is too long. ;;; ;;; Actually, they're both implemented in terms of an internal routine. (define (vector-concatenate vector-list) (vector-concatenate:aux vector-list vector-concatenate)) ;;; Auxiliary for VECTOR-APPEND and VECTOR-CONCATENATE (define vector-concatenate:aux (letrec ((compute-length (lambda (vectors len callee) (if (null? vectors) len (let ((vec (check-type vector? (car vectors) callee))) (compute-length (cdr vectors) (+ (vector-length vec) len) callee))))) (concatenate! (lambda (vectors target to) (if (null? vectors) target (let* ((vec1 (car vectors)) (len (vector-length vec1))) (%vector-copy! target to vec1 0 len) (concatenate! (cdr vectors) target (+ to len))))))) (lambda (vectors callee) (cond ((null? vectors) ;+++ (make-vector 0)) ((null? (cdr vectors)) ;+++ ;; Blech, we still have to allocate a new one. (let* ((vec (check-type vector? (car vectors) callee)) (len (vector-length vec)) (new (make-vector len))) (%vector-copy! new 0 vec 0 len) new)) (else (let ((new-vector (make-vector (compute-length vectors 0 callee)))) (concatenate! vectors new-vector 0) new-vector)))))) ;;; -------------------- ;;; Predicates ;;; (VECTOR? <value>) -> boolean ;;; [R5RS] Return #T if VALUE is a vector and #F if not. (define vector? vector?) ;;; (VECTOR-EMPTY? <vector>) -> boolean ;;; Return #T if VECTOR has zero elements in it, i.e. VECTOR's length ;;; is 0, and #F if not. (define (vector-empty? vec) (let ((vec (check-type vector? vec vector-empty?))) (zero? (vector-length vec)))) ;;; (VECTOR= <elt=?> <vector> ...) -> boolean ;;; (ELT=? <value> <value>) -> boolean ;;; Determine vector equality generalized across element comparators. ;;; Vectors A and B are equal iff their lengths are the same and for ;;; each respective elements E_a and E_b (element=? E_a E_b) returns ;;; a true value. ELT=? is always applied to two arguments. Element ;;; comparison must be consistent wtih EQ?; that is, if (eq? E_a E_b) ;;; results in a true value, then (ELEMENT=? E_a E_b) must result in a ;;; true value. This may be exploited to avoid multiple unnecessary ;;; element comparisons. (This implementation does, but does not deal ;;; with the situation that ELEMENT=? is EQ? to avoid more unnecessary ;;; comparisons, but I believe this optimization is probably fairly ;;; insignificant.) ;;; ;;; If the number of vector arguments is zero or one, then #T is ;;; automatically returned. If there are N vector arguments, ;;; VECTOR_1 VECTOR_2 ... VECTOR_N, then VECTOR_1 & VECTOR_2 are ;;; compared; if they are equal, the vectors VECTOR_2 ... VECTOR_N ;;; are compared. The precise order in which ELT=? is applied is not ;;; specified. (define (vector= elt=? . vectors) (let ((elt=? (check-type procedure? elt=? vector=))) (cond ((null? vectors) #t) ((null? (cdr vectors)) (check-type vector? (car vectors) vector=) #t) (else (let loop ((vecs vectors)) (let ((vec1 (check-type vector? (car vecs) vector=)) (vec2+ (cdr vecs))) (or (null? vec2+) (and (binary-vector= elt=? vec1 (car vec2+)) (loop vec2+))))))))) (define (binary-vector= elt=? vector-a vector-b) (or (eq? vector-a vector-b) ;+++ (let ((length-a (vector-length vector-a)) (length-b (vector-length vector-b))) (letrec ((loop (lambda (i) (or (= i length-a) (and (< i length-b) (test (vector-ref vector-a i) (vector-ref vector-b i) i))))) (test (lambda (elt-a elt-b i) (and (or (eq? elt-a elt-b) ;+++ (elt=? elt-a elt-b)) (loop (+ i 1)))))) (and (= length-a length-b) (loop 0)))))) ;;; -------------------- ;;; Selectors ;;; (VECTOR-REF <vector> <index>) -> value ;;; [R5RS] Return the value that the location in VECTOR at INDEX is ;;; mapped to in the store. (define vector-ref vector-ref) ;;; (VECTOR-LENGTH <vector>) -> exact, nonnegative integer ;;; [R5RS] Return the length of VECTOR. (define vector-length vector-length) ;;; -------------------- ;;; Iteration ;;; (VECTOR-FOLD <kons> <initial-knil> <vector> ...) -> knil ;;; (KONS <knil> <elt> ...) -> knil' ; N vectors -> N+1 args ;;; The fundamental vector iterator. KONS is iterated over each ;;; index in all of the vectors in parallel, stopping at the end of ;;; the shortest; KONS is applied to an argument list of (list I ;;; STATE (vector-ref VEC I) ...), where STATE is the current state ;;; value -- the state value begins with KNIL and becomes whatever ;;; KONS returned at the respective iteration --, and I is the ;;; current index in the iteration. The iteration is strictly left- ;;; to-right. ;;; (vector-fold KONS KNIL (vector E_1 E_2 ... E_N)) ;;; <=> ;;; (KONS (... (KONS (KONS KNIL E_1) E_2) ... E_N-1) E_N) (define (vector-fold kons knil vec . vectors) (let ((kons (check-type procedure? kons vector-fold)) (vec (check-type vector? vec vector-fold))) (if (null? vectors) (%vector-fold1 kons knil (vector-length vec) vec) (%vector-fold2+ kons knil (%smallest-length vectors (vector-length vec) vector-fold) (cons vec vectors))))) ;;; (VECTOR-FOLD-RIGHT <kons> <initial-knil> <vector> ...) -> knil ;;; (KONS <knil> <elt> ...) -> knil' ; N vectors => N+1 args ;;; The fundamental vector recursor. Iterates in parallel across ;;; VECTOR ... right to left, applying KONS to the elements and the ;;; current state value; the state value becomes what KONS returns ;;; at each next iteration. KNIL is the initial state value. ;;; (vector-fold-right KONS KNIL (vector E_1 E_2 ... E_N)) ;;; <=> ;;; (KONS (... (KONS (KONS KNIL E_N) E_N-1) ... E_2) E_1) ;;; ;;; Not implemented in terms of a more primitive operations that might ;;; called %VECTOR-FOLD-RIGHT due to the fact that it wouldn't be very ;;; useful elsewhere. (define vector-fold-right (letrec ((loop1 (lambda (kons knil vec i) (if (negative? i) knil (loop1 kons (kons i knil (vector-ref vec i)) vec (- i 1))))) (loop2+ (lambda (kons knil vectors i) (if (negative? i) knil (loop2+ kons (apply kons i knil (vectors-ref vectors i)) vectors (- i 1)))))) (lambda (kons knil vec . vectors) (let ((kons (check-type procedure? kons vector-fold-right)) (vec (check-type vector? vec vector-fold-right))) (if (null? vectors) (loop1 kons knil vec (- (vector-length vec) 1)) (loop2+ kons knil (cons vec vectors) (- (%smallest-length vectors (vector-length vec) vector-fold-right) 1))))))) ;;; (VECTOR-MAP <f> <vector> ...) -> vector ;;; (F <elt> ...) -> value ; N vectors -> N args ;;; Constructs a new vector of the shortest length of the vector ;;; arguments. Each element at index I of the new vector is mapped ;;; from the old vectors by (F I (vector-ref VECTOR I) ...). The ;;; dynamic order of application of F is unspecified. (define (vector-map f vec . vectors) (let ((f (check-type procedure? f vector-map)) (vec (check-type vector? vec vector-map))) (if (null? vectors) (let ((len (vector-length vec))) (%vector-map1! f (make-vector len) vec len)) (let ((len (%smallest-length vectors (vector-length vec) vector-map))) (%vector-map2+! f (make-vector len) (cons vec vectors) len))))) ;;; (VECTOR-MAP! <f> <vector> ...) -> unspecified ;;; (F <elt> ...) -> element' ; N vectors -> N args ;;; Similar to VECTOR-MAP, but rather than mapping the new elements ;;; into a new vector, the new mapped elements are destructively ;;; inserted into the first vector. Again, the dynamic order of ;;; application of F is unspecified, so it is dangerous for F to ;;; manipulate the first VECTOR. (define (vector-map! f vec . vectors) (let ((f (check-type procedure? f vector-map!)) (vec (check-type vector? vec vector-map!))) (if (null? vectors) (%vector-map1! f vec vec (vector-length vec)) (%vector-map2+! f vec (cons vec vectors) (%smallest-length vectors (vector-length vec) vector-map!))) (unspecified-value))) ;;; (VECTOR-FOR-EACH <f> <vector> ...) -> unspecified ;;; (F <elt> ...) ; N vectors -> N args ;;; Simple vector iterator: applies F to each index in the range [0, ;;; LENGTH), where LENGTH is the length of the smallest vector ;;; argument passed, and the respective element at that index. In ;;; contrast with VECTOR-MAP, F is reliably applied to each ;;; subsequent elements, starting at index 0 from left to right, in ;;; the vectors. (define vector-for-each (letrec ((for-each1 (lambda (f vec i len) (cond ((< i len) (f i (vector-ref vec i)) (for-each1 f vec (+ i 1) len))))) (for-each2+ (lambda (f vecs i len) (cond ((< i len) (apply f i (vectors-ref vecs i)) (for-each2+ f vecs (+ i 1) len)))))) (lambda (f vec . vectors) (let ((f (check-type procedure? f vector-for-each)) (vec (check-type vector? vec vector-for-each))) (if (null? vectors) (for-each1 f vec 0 (vector-length vec)) (for-each2+ f (cons vec vectors) 0 (%smallest-length vectors (vector-length vec) vector-for-each))))))) ;;; (VECTOR-COUNT <predicate?> <vector> ...) ;;; -> exact, nonnegative integer ;;; (PREDICATE? <index> <value> ...) ; N vectors -> N+1 args ;;; PREDICATE? is applied element-wise to the elements of VECTOR ..., ;;; and a count is tallied of the number of elements for which a ;;; true value is produced by PREDICATE?. This count is returned. (define (vector-count pred? vec . vectors) (let ((pred? (check-type procedure? pred? vector-count)) (vec (check-type vector? vec vector-count))) (if (null? vectors) (%vector-fold1 (lambda (index count elt) (if (pred? index elt) (+ count 1) count)) 0 (vector-length vec) vec) (%vector-fold2+ (lambda (index count . elts) (if (apply pred? index elts) (+ count 1) count)) 0 (%smallest-length vectors (vector-length vec) vector-count) (cons vec vectors))))) ;;; -------------------- ;;; Searching ;;; (VECTOR-INDEX <predicate?> <vector> ...) ;;; -> exact, nonnegative integer or #F ;;; (PREDICATE? <elt> ...) -> boolean ; N vectors -> N args ;;; Search left-to-right across VECTOR ... in parallel, returning the ;;; index of the first set of values VALUE ... such that (PREDICATE? ;;; VALUE ...) returns a true value; if no such set of elements is ;;; reached, return #F. (define (vector-index pred? vec . vectors) (vector-index/skip pred? vec vectors vector-index)) ;;; (VECTOR-SKIP <predicate?> <vector> ...) ;;; -> exact, nonnegative integer or #F ;;; (PREDICATE? <elt> ...) -> boolean ; N vectors -> N args ;;; (vector-index (lambda elts (not (apply PREDICATE? elts))) ;;; VECTOR ...) ;;; Like VECTOR-INDEX, but find the index of the first set of values ;;; that do _not_ satisfy PREDICATE?. (define (vector-skip pred? vec . vectors) (vector-index/skip (lambda elts (not (apply pred? elts))) vec vectors vector-skip)) ;;; Auxiliary for VECTOR-INDEX & VECTOR-SKIP (define vector-index/skip (letrec ((loop1 (lambda (pred? vec len i) (cond ((= i len) #f) ((pred? (vector-ref vec i)) i) (else (loop1 pred? vec len (+ i 1)))))) (loop2+ (lambda (pred? vectors len i) (cond ((= i len) #f) ((apply pred? (vectors-ref vectors i)) i) (else (loop2+ pred? vectors len (+ i 1))))))) (lambda (pred? vec vectors callee) (let ((pred? (check-type procedure? pred? callee)) (vec (check-type vector? vec callee))) (if (null? vectors) (loop1 pred? vec (vector-length vec) 0) (loop2+ pred? (cons vec vectors) (%smallest-length vectors (vector-length vec) callee) 0)))))) ;;; (VECTOR-INDEX-RIGHT <predicate?> <vector> ...) ;;; -> exact, nonnegative integer or #F ;;; (PREDICATE? <elt> ...) -> boolean ; N vectors -> N args ;;; Right-to-left variant of VECTOR-INDEX. (define (vector-index-right pred? vec . vectors) (vector-index/skip-right pred? vec vectors vector-index-right)) ;;; (VECTOR-SKIP-RIGHT <predicate?> <vector> ...) ;;; -> exact, nonnegative integer or #F ;;; (PREDICATE? <elt> ...) -> boolean ; N vectors -> N args ;;; Right-to-left variant of VECTOR-SKIP. (define (vector-skip-right pred? vec . vectors) (vector-index/skip-right (lambda elts (not (apply pred? elts))) vec vectors vector-index-right)) (define vector-index/skip-right (letrec ((loop1 (lambda (pred? vec i) (cond ((negative? i) #f) ((pred? (vector-ref vec i)) i) (else (loop1 pred? vec (- i 1)))))) (loop2+ (lambda (pred? vectors i) (cond ((negative? i) #f) ((apply pred? (vectors-ref vectors i)) i) (else (loop2+ pred? vectors (- i 1))))))) (lambda (pred? vec vectors callee) (let ((pred? (check-type procedure? pred? callee)) (vec (check-type vector? vec callee))) (if (null? vectors) (loop1 pred? vec (- (vector-length vec) 1)) (loop2+ pred? (cons vec vectors) (- (%smallest-length vectors (vector-length vec) callee) 1))))))) ;;; (VECTOR-BINARY-SEARCH <vector> <value> <cmp> [<start> <end>]) ;;; -> exact, nonnegative integer or #F ;;; (CMP <value1> <value2>) -> integer ;;; positive -> VALUE1 > VALUE2 ;;; zero -> VALUE1 = VALUE2 ;;; negative -> VALUE1 < VALUE2 ;;; Perform a binary search through VECTOR for VALUE, comparing each ;;; element to VALUE with CMP. (define (vector-binary-search vec value cmp . maybe-start+end) (let ((cmp (check-type procedure? cmp vector-binary-search))) (let-vector-start+end vector-binary-search vec maybe-start+end (start end) (let loop ((start start) (end end) (j #f)) (let ((i (quotient (+ start end) 2))) (if (or (= start end) (and j (= i j))) #f (let ((comparison (check-type integer? (cmp (vector-ref vec i) value) `(,cmp for ,vector-binary-search)))) (cond ((zero? comparison) i) ((positive? comparison) (loop start i i)) (else (loop i end i)))))))))) ;;; (VECTOR-ANY <pred?> <vector> ...) -> value ;;; Apply PRED? to each parallel element in each VECTOR ...; if PRED? ;;; should ever return a true value, immediately stop and return that ;;; value; otherwise, when the shortest vector runs out, return #F. ;;; The iteration and order of application of PRED? across elements ;;; is of the vectors is strictly left-to-right. (define vector-any (letrec ((loop1 (lambda (pred? vec i len len-1) (and (not (= i len)) (if (= i len-1) (pred? (vector-ref vec i)) (or (pred? (vector-ref vec i)) (loop1 pred? vec (+ i 1) len len-1)))))) (loop2+ (lambda (pred? vectors i len len-1) (and (not (= i len)) (if (= i len-1) (apply pred? (vectors-ref vectors i)) (or (apply pred? (vectors-ref vectors i)) (loop2+ pred? vectors (+ i 1) len len-1))))))) (lambda (pred? vec . vectors) (let ((pred? (check-type procedure? pred? vector-any)) (vec (check-type vector? vec vector-any))) (if (null? vectors) (let ((len (vector-length vec))) (loop1 pred? vec 0 len (- len 1))) (let ((len (%smallest-length vectors (vector-length vec) vector-any))) (loop2+ pred? (cons vec vectors) 0 len (- len 1)))))))) ;;; (VECTOR-EVERY <pred?> <vector> ...) -> value ;;; Apply PRED? to each parallel value in each VECTOR ...; if PRED? ;;; should ever return #F, immediately stop and return #F; otherwise, ;;; if PRED? should return a true value for each element, stopping at ;;; the end of the shortest vector, return the last value that PRED? ;;; returned. In the case that there is an empty vector, return #T. ;;; The iteration and order of application of PRED? across elements ;;; is of the vectors is strictly left-to-right. (define vector-every (letrec ((loop1 (lambda (pred? vec i len len-1) (or (= i len) (if (= i len-1) (pred? (vector-ref vec i)) (and (pred? (vector-ref vec i)) (loop1 pred? vec (+ i 1) len len-1)))))) (loop2+ (lambda (pred? vectors i len len-1) (or (= i len) (if (= i len-1) (apply pred? (vectors-ref vectors i)) (and (apply pred? (vectors-ref vectors i)) (loop2+ pred? vectors (+ i 1) len len-1))))))) (lambda (pred? vec . vectors) (let ((pred? (check-type procedure? pred? vector-every)) (vec (check-type vector? vec vector-every))) (if (null? vectors) (let ((len (vector-length vec))) (loop1 pred? vec 0 len (- len 1))) (let ((len (%smallest-length vectors (vector-length vec) vector-every))) (loop2+ pred? (cons vec vectors) 0 len (- len 1)))))))) ;;; -------------------- ;;; Mutators ;;; (VECTOR-SET! <vector> <index> <value>) -> unspecified ;;; [R5RS] Assign the location at INDEX in VECTOR to VALUE. (define vector-set! vector-set!) ;;; (VECTOR-SWAP! <vector> <index1> <index2>) -> unspecified ;;; Swap the values in the locations at INDEX1 and INDEX2. (define (vector-swap! vec i j) (let ((vec (check-type vector? vec vector-swap!))) (let ((i (check-index vec i vector-swap!)) (j (check-index vec j vector-swap!))) (let ((x (vector-ref vec i))) (vector-set! vec i (vector-ref vec j)) (vector-set! vec j x))))) ;;; (VECTOR-FILL! <vector> <value> [<start> <end>]) -> unspecified ;;; [R5RS+] Fill the locations in VECTOR between START, whose default ;;; is 0, and END, whose default is the length of VECTOR, with VALUE. ;;; ;;; This one can probably be made really fast natively. (define vector-fill! (let ((%vector-fill! vector-fill!)) ; Take the native one, under ; the assumption that it's ; faster, so we can use it if ; there are no optional ; arguments. (lambda (vec value . maybe-start+end) (if (null? maybe-start+end) (%vector-fill! vec value) ;+++ (let-vector-start+end vector-fill! vec maybe-start+end (start end) (do ((i start (+ i 1))) ((= i end)) (vector-set! vec i value))))))) ;;; (VECTOR-COPY! <target> <tstart> <source> [<sstart> <send>]) ;;; -> unspecified ;;; Copy the values in the locations in [SSTART,SEND) from SOURCE to ;;; to TARGET, starting at TSTART in TARGET. ;;; [wdc] Corrected to allow 0 <= sstart <= send <= (vector-length source). (define (vector-copy! target tstart source . maybe-sstart+send) (define (doit! sstart send source-length) (let ((tstart (check-type nonneg-int? tstart vector-copy!)) (sstart (check-type nonneg-int? sstart vector-copy!)) (send (check-type nonneg-int? send vector-copy!))) (cond ((and (<= 0 sstart send source-length) (<= (+ tstart (- send sstart)) (vector-length target))) (%vector-copy! target tstart source sstart send)) (else (error "illegal arguments" `(while calling ,vector-copy!) `(target was ,target) `(target-length was ,(vector-length target)) `(tstart was ,tstart) `(source was ,source) `(source-length was ,source-length) `(sstart was ,sstart) `(send was ,send)))))) (let ((n (vector-length source))) (cond ((null? maybe-sstart+send) (doit! 0 n n)) ((null? (cdr maybe-sstart+send)) (doit! (car maybe-sstart+send) n n)) ((null? (cddr maybe-sstart+send)) (doit! (car maybe-sstart+send) (cadr maybe-sstart+send) n)) (else (error "too many arguments" vector-copy! (cddr maybe-sstart+send)))))) ;;; (VECTOR-REVERSE-COPY! <target> <tstart> <source> [<sstart> <send>]) ;;; [wdc] Corrected to allow 0 <= sstart <= send <= (vector-length source). (define (vector-reverse-copy! target tstart source . maybe-sstart+send) (define (doit! sstart send source-length) (let ((tstart (check-type nonneg-int? tstart vector-reverse-copy!)) (sstart (check-type nonneg-int? sstart vector-reverse-copy!)) (send (check-type nonneg-int? send vector-reverse-copy!))) (cond ((and (eq? target source) (or (between? sstart tstart send) (between? tstart sstart (+ tstart (- send sstart))))) (error "vector range for self-copying overlaps" vector-reverse-copy! `(vector was ,target) `(tstart was ,tstart) `(sstart was ,sstart) `(send was ,send))) ((and (<= 0 sstart send source-length) (<= (+ tstart (- send sstart)) (vector-length target))) (%vector-reverse-copy! target tstart source sstart send)) (else (error "illegal arguments" `(while calling ,vector-reverse-copy!) `(target was ,target) `(target-length was ,(vector-length target)) `(tstart was ,tstart) `(source was ,source) `(source-length was ,source-length) `(sstart was ,sstart) `(send was ,send)))))) (let ((n (vector-length source))) (cond ((null? maybe-sstart+send) (doit! 0 n n)) ((null? (cdr maybe-sstart+send)) (doit! (car maybe-sstart+send) n n)) ((null? (cddr maybe-sstart+send)) (doit! (car maybe-sstart+send) (cadr maybe-sstart+send) n)) (else (error "too many arguments" vector-reverse-copy! (cddr maybe-sstart+send)))))) ;;; (VECTOR-REVERSE! <vector> [<start> <end>]) -> unspecified ;;; Destructively reverse the contents of the sequence of locations ;;; in VECTOR between START, whose default is 0, and END, whose ;;; default is the length of VECTOR. (define (vector-reverse! vec . start+end) (let-vector-start+end vector-reverse! vec start+end (start end) (%vector-reverse! vec start end))) ;;; -------------------- ;;; Conversion ;;; (VECTOR->LIST <vector> [<start> <end>]) -> list ;;; [R5RS+] Produce a list containing the elements in the locations ;;; between START, whose default is 0, and END, whose default is the ;;; length of VECTOR, from VECTOR. (define vector->list (let ((%vector->list vector->list)) (lambda (vec . maybe-start+end) (if (null? maybe-start+end) ; Oughta use CASE-LAMBDA. (%vector->list vec) ;+++ (let-vector-start+end vector->list vec maybe-start+end (start end) ;(unfold (lambda (i) ; No SRFI 1. ; (< i start)) ; (lambda (i) (vector-ref vec i)) ; (lambda (i) (- i 1)) ; (- end 1)) (do ((i (- end 1) (- i 1)) (result '() (cons (vector-ref vec i) result))) ((< i start) result))))))) ;;; (REVERSE-VECTOR->LIST <vector> [<start> <end>]) -> list ;;; Produce a list containing the elements in the locations between ;;; START, whose default is 0, and END, whose default is the length ;;; of VECTOR, from VECTOR, in reverse order. (define (reverse-vector->list vec . maybe-start+end) (let-vector-start+end reverse-vector->list vec maybe-start+end (start end) ;(unfold (lambda (i) (= i end)) ; No SRFI 1. ; (lambda (i) (vector-ref vec i)) ; (lambda (i) (+ i 1)) ; start) (do ((i start (+ i 1)) (result '() (cons (vector-ref vec i) result))) ((= i end) result)))) ;;; (LIST->VECTOR <list> [<start> <end>]) -> vector ;;; [R5RS+] Produce a vector containing the elements in LIST, which ;;; must be a proper list, between START, whose default is 0, & END, ;;; whose default is the length of LIST. It is suggested that if the ;;; length of LIST is known in advance, the START and END arguments ;;; be passed, so that LIST->VECTOR need not call LENGTH to determine ;;; the the length. ;;; ;;; This implementation diverges on circular lists, unless LENGTH fails ;;; and causes - to fail as well. Given a LENGTH* that computes the ;;; length of a list's cycle, this wouldn't diverge, and would work ;;; great for circular lists. (define list->vector (let ((%list->vector list->vector)) (lambda (lst . maybe-start+end) ;; Checking the type of a proper list is expensive, so we do it ;; amortizedly, or let %LIST->VECTOR or LIST-TAIL do it. (if (null? maybe-start+end) ; Oughta use CASE-LAMBDA. (%list->vector lst) ;+++ ;; We can't use LET-VECTOR-START+END, because we're using the ;; bounds of a _list_, not a vector. (let*-optionals maybe-start+end ((start 0) (end (length lst))) ; Ugh -- LENGTH (let ((start (check-type nonneg-int? start list->vector)) (end (check-type nonneg-int? end list->vector))) ((lambda (f) (vector-unfold f (- end start) (list-tail lst start))) (lambda (index l) (cond ((null? l) (error "list was too short" `(list was ,lst) `(attempted end was ,end) `(while calling ,list->vector))) ((pair? l) (values (car l) (cdr l))) (else ;; Make this look as much like what CHECK-TYPE ;; would report as possible. (error "erroneous value" ;; We want SRFI 1's PROPER-LIST?, but it ;; would be a waste to link all of SRFI ;; 1 to this module for only the single ;; function PROPER-LIST?. (list list? lst) `(while calling ,list->vector)))))))))))) ;;; (REVERSE-LIST->VECTOR <list> [<start> <end>]) -> vector ;;; Produce a vector containing the elements in LIST, which must be a ;;; proper list, between START, whose default is 0, and END, whose ;;; default is the length of LIST, in reverse order. It is suggested ;;; that if the length of LIST is known in advance, the START and END ;;; arguments be passed, so that REVERSE-LIST->VECTOR need not call ;;; LENGTH to determine the the length. ;;; ;;; This also diverges on circular lists unless, again, LENGTH returns ;;; something that makes - bork. (define (reverse-list->vector lst . maybe-start+end) (let*-optionals maybe-start+end ((start 0) (end (length lst))) ; Ugh -- LENGTH (let ((start (check-type nonneg-int? start reverse-list->vector)) (end (check-type nonneg-int? end reverse-list->vector))) ((lambda (f) (vector-unfold-right f (- end start) (list-tail lst start))) (lambda (index l) (cond ((null? l) (error "list too short" `(list was ,lst) `(attempted end was ,end) `(while calling ,reverse-list->vector))) ((pair? l) (values (car l) (cdr l))) (else (error "erroneous value" (list list? lst) `(while calling ,reverse-list->vector))))))))) |
Added srfi/s43/vectors.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s43 vectors) (export ;;; * Constructors make-vector vector vector-unfold vector-unfold-right vector-copy vector-reverse-copy vector-append vector-concatenate ;;; * Predicates vector? vector-empty? vector= ;;; * Selectors vector-ref vector-length ;;; * Iteration vector-fold vector-fold-right vector-map vector-map! vector-for-each vector-count ;;; * Searching vector-index vector-skip vector-index-right vector-skip-right vector-binary-search vector-any vector-every ;;; * Mutators vector-set! vector-swap! (rename (my:vector-fill! vector-fill!)) vector-reverse! vector-copy! vector-reverse-copy! ;;; * Conversion (rename (my:vector->list vector->list)) reverse-vector->list (rename (my:list->vector list->vector)) reverse-list->vector ) (import (except (rnrs) vector-map vector-for-each) (rnrs r5rs) (srfi s23 error tricks) (srfi s8 receive) (for (srfi private vanish) expand) (srfi private include)) ;; I do these let-syntax tricks so the original vector-lib.scm file does ;; not have to be modified at all. (let-syntax ((define (let ((vd (vanish-define define (make-vector vector vector? vector-ref vector-set! vector-length)))) (lambda (stx) (define (rename? id) (memp (lambda (x) (free-identifier=? id x)) (list #'vector-fill! #'vector->list #'list->vector))) (define (rename id) (datum->syntax id (string->symbol (string-append "my:" (symbol->string (syntax->datum id)))))) (syntax-case stx () ((_ name . r) (and (identifier? #'name) (rename? #'name)) #`(define #,(rename #'name) . r)) (_ (vd stx)))))) (define-syntax (vanish-define define-syntax (receive)))) (SRFI-23-error->R6RS "(library (srfi s43 vectors))" (include/resolve ("srfi" "s43") "vector-lib.scm"))) ) |
Added srfi/s48/intermediate-format-strings.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 |
#!r6rs ;;; FILE "intermediate-format-strings.sls" ;;; IMPLEMENTS SRFI-48: Intermediary format strings ;;; http://srfi.schemers.org/srfi-48/srfi-48.html ;;; AUTHOR Ken Dickey ;;; UPDATED Syntax updated for R6RS February 2008 by Ken Dickey ;;; LANGUAGE R6RS but specific to Ikarus Scheme ;; Small changes by Derick Eddington to make the begining of `format' ;; more effecient and more abstracted. ;;;Copyright (C) Kenneth A Dickey (2003). All Rights Reserved. ;;; ;;;Permission is hereby granted, free of charge, to any person ;;;obtaining a copy of this software and associated documentation ;;;files (the "Software"), to deal in the Software without ;;;restriction, including without limitation the rights to use, ;;;copy, modify, merge, publish, distribute, sublicense, and/or ;;;sell copies of the Software, and to permit persons to whom ;;;the Software is furnished to do so, subject to the following ;;;conditions: ;;; ;;;The above copyright notice and this permission notice shall ;;;be included in all copies or substantial portions of the Software. ;;; ;;;THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ;;;EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES ;;;OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ;;;NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT ;;;HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, ;;;WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ;;;FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ;;;OTHER DEALINGS IN THE SOFTWARE. ; The implementation below requires SRFI-6 (Basic string ports), ; and SRFI-38 (External Representation for Data With Shared Structure). (library (srfi s48 intermediate-format-strings) (export format) (import (rnrs) (srfi s48 intermediate-format-strings compat) (srfi s6 basic-string-ports) (srfi s38 with-shared-structure)) (define ascii-tab #\tab) (define (format arg0 . arg*) (define (problem msg . irts) (apply assertion-violation 'format msg irts)) (define (_format port format-string args return-value) (define (string-index str c) (let ( [len (string-length str)] ) (let loop ( [i 0] ) (cond ((= i len) #f) ((eqv? c (string-ref str i)) i) (else (loop (+ i 1))))))) (define (string-grow str len char) (let ( [off (- len (string-length str))] ) (if (positive? off) (string-append (make-string off char) str) str))) (define (compose-with-digits digits pre-str frac-str exp-str) (let ( [frac-len (string-length frac-str)] ) ;;@@DEBUG ;;(format #t "~%@@(compose-with-digits digits=~s pre-str=~s frac-str=~s exp-str=~s ) ~%" digits pre-str frac-str exp-str) (cond [(< frac-len digits) ;; grow frac part, pad with zeros (string-append pre-str "." frac-str (make-string (- digits frac-len) #\0) exp-str) ] [(= frac-len digits) ;; frac-part is exactly the right size (string-append pre-str "." frac-str exp-str) ] [else ;; must round to shrink frac-part (let* ( [first-part (substring frac-str 0 digits)] [last-part (substring frac-str digits frac-len)] ;; NB: Scheme uses "Round to Even Rule" for .5 [rounded-frac ;; NB: exact is r6; r5 is inexact->exact (exact (round (string->number (string-append first-part "." last-part)))) ] [rounded-frac-str (number->string rounded-frac)] [rounded-frac-len (string-length rounded-frac-str)] [carry? (and (not (zero? rounded-frac)) (> rounded-frac-len digits)) ] [new-frac (let ( (pre-frac (if carry? ;; trim leading "1" (substring rounded-frac-str 1 (min rounded-frac-len digits)) (substring rounded-frac-str 0 (min rounded-frac-len digits))) ;; may be zero length ) ) (if (< (string-length pre-frac) digits) (string-grow pre-frac digits #\0) pre-frac)) ] ) ;;@@DEBUG ;;(format #t "@@ first-part=~s last-part=~s rounded-frac=~s carry?=~s ~%" first-part last-part rounded-frac carry?) (string-append (if carry? (number->string (+ 1 (string->number pre-str))) pre-str) "." new-frac exp-str))] ) ) ) (define (format-fixed number-or-string width digits) ; returns a string ;;@@DEBUG ;;(format #t "~%(format-fixed number-or-string=~s width=~s digits=~s)~%" number-or-string width digits) (cond [(string? number-or-string) (string-grow number-or-string width #\space) ] [(number? number-or-string) (let* ( [num (real-part number-or-string)] [real (if digits (+ 0.0 num) num)] [imag (imag-part number-or-string)] ) (cond [(not (zero? imag)) (string-grow (string-append (format-fixed real 0 digits) (if (negative? imag) "" "+") (format-fixed imag 0 digits) "i") width #\space) ] [digits (let* ( [num-str (number->string (if (rational? real) (+ 0.0 real) real))] [dot-index (string-index num-str #\.)] [exp-index (string-index num-str #\e)] [length (string-length num-str)] [pre-string (cond ((and exp-index (not dot-index)) (substring num-str 0 exp-index) ) (dot-index (substring num-str 0 dot-index) ) (else num-str)) ] [exp-string (if exp-index (substring num-str exp-index length) "") ] [frac-string (let ( (dot-idx (if dot-index dot-index -1)) ) (if exp-index (substring num-str (+ dot-idx 1) exp-index) (substring num-str (+ dot-idx 1) length))) ] ) (string-grow (if dot-index (compose-with-digits digits pre-string frac-string exp-string) (string-append pre-string exp-string)) width #\space) )] [else ;; no digits (string-grow (number->string real) width #\space)]) )] [else (error 'format "~F requires a number or a string" number-or-string)]) ) (define documentation-string "(format [<port>] <format-string> [<arg>...]) -- <port> is #t, #f or an output-port OPTION [MNEMONIC] DESCRIPTION -- Implementation Assumes ASCII Text Encoding ~H [Help] output this text ~A [Any] (display arg) for humans ~S [Slashified] (write arg) for parsers ~W [WriteCircular] like ~s but outputs circular and recursive data structures ~~ [tilde] output a tilde ~T [Tab] output a tab character ~% [Newline] output a newline character ~& [Freshline] output a newline character if the previous output was not a newline ~D [Decimal] the arg is a number which is output in decimal radix ~X [heXadecimal] the arg is a number which is output in hexdecimal radix ~O [Octal] the arg is a number which is output in octal radix ~B [Binary] the arg is a number which is output in binary radix ~w,dF [Fixed] the arg is a string or number which has width w and d digits after the decimal ~C [Character] charater arg is output by write-char ~_ [Space] a single space character is output ~Y [Yuppify] the list arg is pretty-printed to the output ~? [Indirection] recursive format: next 2 args are format-string and list of arguments ~K [Indirection] same as ~? " ) (define (require-an-arg args) (when (null? args) (problem "too few arguments")) ) (define (format-help p format-strg arglist) (letrec ( [length-of-format-string (string-length format-strg)] [anychar-dispatch (lambda (pos arglist last-was-newline) (if (>= pos length-of-format-string) arglist ; return unused args (let ( [char (string-ref format-strg pos)] ) (cond [(eqv? char #\~) (tilde-dispatch (+ pos 1) arglist last-was-newline)] [else (write-char char p) (anychar-dispatch (+ pos 1) arglist #f) ]) ))) ] ; end anychar-dispatch [has-newline? (lambda (whatever last-was-newline) (or (eqv? whatever #\newline) (and (string? whatever) (let ( [len (string-length whatever)] ) (if (zero? len) last-was-newline (eqv? #\newline (string-ref whatever (- len 1))))))) )] ; end has-newline? [tilde-dispatch (lambda (pos arglist last-was-newline) (cond ((>= pos length-of-format-string) (write-char #\~ p) ; tilde at end of string is just output arglist ; return unused args ) (else (case (char-upcase (string-ref format-strg pos)) ((#\A) ; Any -- for humans (require-an-arg arglist) (let ( [whatever (car arglist)] ) (display whatever p) (anychar-dispatch (+ pos 1) (cdr arglist) (has-newline? whatever last-was-newline)) )) ((#\S) ; Slashified -- for parsers (require-an-arg arglist) (let ( [whatever (car arglist)] ) (write whatever p) (anychar-dispatch (+ pos 1) (cdr arglist) (has-newline? whatever last-was-newline)) )) ((#\W) (require-an-arg arglist) (let ( [whatever (car arglist)] ) (write-with-shared-structure whatever p) ;; srfi-38 (anychar-dispatch (+ pos 1) (cdr arglist) (has-newline? whatever last-was-newline)) )) ((#\D) ; Decimal (require-an-arg arglist) (display (number->string (car arglist) 10) p) (anychar-dispatch (+ pos 1) (cdr arglist) #f) ) ((#\X) ; HeXadecimal (require-an-arg arglist) (display (number->string (car arglist) 16) p) (anychar-dispatch (+ pos 1) (cdr arglist) #f) ) ((#\O) ; Octal (require-an-arg arglist) (display (number->string (car arglist) 8) p) (anychar-dispatch (+ pos 1) (cdr arglist) #f) ) ((#\B) ; Binary (require-an-arg arglist) (display (number->string (car arglist) 2) p) (anychar-dispatch (+ pos 1) (cdr arglist) #f) ) ((#\C) ; Character (require-an-arg arglist) (write-char (car arglist) p) (anychar-dispatch (+ pos 1) (cdr arglist) (eqv? (car arglist) #\newline)) ) ((#\~) ; Tilde (write-char #\~ p) (anychar-dispatch (+ pos 1) arglist #f) ) ((#\%) ; Newline (newline p) (anychar-dispatch (+ pos 1) arglist #t) ) ((#\&) ; Freshline (if (not last-was-newline) ;; (unless last-was-newline .. (newline p)) (anychar-dispatch (+ pos 1) arglist #t) ) ((#\_) ; Space (write-char #\space p) (anychar-dispatch (+ pos 1) arglist #f) ) ((#\T) ; Tab -- IMPLEMENTATION DEPENDENT ENCODING (write-char ascii-tab p) (anychar-dispatch (+ pos 1) arglist #f) ) ((#\Y) ; Pretty-print (pretty-print (car arglist) p) (anychar-dispatch (+ pos 1) (cdr arglist) #f) ) ((#\F) (require-an-arg arglist) (display (format-fixed (car arglist) 0 #f) p) (anychar-dispatch (+ pos 1) (cdr arglist) #f) ) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) ;; gather "~w[,d]F" w and d digits (let loop ( [index (+ pos 1)] [w-digits (list (string-ref format-strg pos))] [d-digits '()] [in-width? #t] ) (if (>= index length-of-format-string) (problem "improper numeric format directive" format-strg) (let ( [next-char (string-ref format-strg index)] ) (cond [(char-numeric? next-char) (if in-width? (loop (+ index 1) (cons next-char w-digits) d-digits in-width?) (loop (+ index 1) w-digits (cons next-char d-digits) in-width?)) ] [(char=? (char-upcase next-char) #\F) (let ( [width (string->number (list->string (reverse w-digits))) ] [digits (if (zero? (length d-digits)) #f (string->number (list->string (reverse d-digits))))] ) (display (format-fixed (car arglist) width digits) p) (anychar-dispatch (+ index 1) (cdr arglist) #f)) ] [(char=? next-char #\,) (if in-width? (loop (+ index 1) w-digits d-digits #f) (problem "too many commas in directive" format-strg)) ] [else (problem "~w,dF directive ill-formed" format-strg)]))) )) ((#\? #\K) ; indirection -- take next arg as format string (cond ; and following arg as list of format args ((< (length arglist) 2) (problem "less arguments than specified for ~?" arglist) ) ((not (string? (car arglist))) (problem "~? requires a string" (car arglist)) ) (else (format-help p (car arglist) (cadr arglist)) (anychar-dispatch (+ pos 1) (cddr arglist) #f) ))) ((#\H) ; Help (display documentation-string p) (anychar-dispatch (+ pos 1) arglist #t) ) (else (problem "unknown tilde escape" (string-ref format-strg pos))) ))) )] ; end tilde-dispatch ) ; end letrec ; format-help body (anychar-dispatch 0 arglist #f) )) ; end format-help ; _format body (let ( [unused-args (format-help port format-string args)] ) (if (not (null? unused-args)) (problem "unused arguments" unused-args) (return-value port)))) ; format body (if (string? arg0) (_format (open-output-string) arg0 arg* get-output-string) (if (null? arg*) (problem "too few arguments" (list arg0)) (let ([port (cond [(eq? arg0 #f) (open-output-string)] [(eq? arg0 #t) (current-output-port)] [(output-port? arg0) arg0] [else (problem "bad output-port argument" arg0)])] [arg1 (car arg*)]) (if (string? arg1) (_format port arg1 (cdr arg*) (if arg0 (lambda (ignore) (values)) get-output-string)) (problem "not a string" arg1)))))) ) |
Added srfi/s48/intermediate-format-strings/compat.chezscheme.sls.
> > > > > > > > > |
1 2 3 4 5 6 7 8 9 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi s48 intermediate-format-strings compat) (export pretty-print) (import (chezscheme))) |
Added srfi/s48/intermediate-format-strings/compat.ikarus.sls.
> > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi s48 intermediate-format-strings compat) (export pretty-print) (import (only (ikarus) pretty-print)) ) |
Added srfi/s48/intermediate-format-strings/compat.larceny.sls.
> > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi s48 intermediate-format-strings compat) (export pretty-print) (import (primitives pretty-print)) ) |
Added srfi/s48/intermediate-format-strings/compat.mzscheme.sls.
> > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s48 intermediate-format-strings compat) (export pretty-print) (import (only (scheme pretty) pretty-print)) ) |
Added srfi/s48/intermediate-format-strings/compat.ypsilon.sls.
> > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi s48 intermediate-format-strings compat) (export pretty-print) (import (only (core) pretty-print)) ) |
Added srfi/s6/basic-string-ports.mzscheme.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s6 basic-string-ports) (export (rename (open-string-input-port open-input-string)) open-output-string get-output-string) (import (rnrs) (only (scheme base) make-weak-hasheq hash-ref hash-set!)) (define accumed-ht (make-weak-hasheq)) (define (open-output-string) (letrec ([sop (make-custom-textual-output-port "string-output-port" (lambda (string start count) ; write! (when (positive? count) (let ([al (hash-ref accumed-ht sop)]) (hash-set! accumed-ht sop (cons (substring string start (+ start count)) al)))) count) #f ; get-position TODO? #f ; set-position! TODO? #f #| closed TODO? |# )]) (hash-set! accumed-ht sop '()) sop)) (define (get-output-string sop) (if (output-port? sop) (cond [(hash-ref accumed-ht sop #f) => (lambda (al) (apply string-append (reverse al)))] [else (assertion-violation 'get-output-string "not a string-output-port" sop)]) (assertion-violation 'get-output-string "not an output-port" sop))) ) |
Added srfi/s6/basic-string-ports.sls.
> > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s6 basic-string-ports) (export open-input-string open-output-string get-output-string) (import (rnrs base) (only (rnrs io ports) open-string-input-port) (srfi s6 basic-string-ports compat)) (define (open-input-string str) (open-string-input-port str)) ) |
Added srfi/s6/basic-string-ports/compat.chezscheme.sls.
> > > > > > |
1 2 3 4 5 6 |
(library (srfi s6 basic-string-ports compat) (export open-output-string get-output-string) (import (only (chezscheme) open-output-string get-output-string))) |
Added srfi/s6/basic-string-ports/compat.ikarus.sls.
> > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi s6 basic-string-ports compat) (export open-output-string get-output-string) (import (only (ikarus) open-output-string get-output-string))) |
Added srfi/s6/basic-string-ports/compat.larceny.sls.
> > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi s6 basic-string-ports compat) (export open-output-string get-output-string) (import (primitives open-output-string get-output-string)) ) |
Added srfi/s6/basic-string-ports/compat.mosh.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
;; Copyright (c) 2009 Higepon(Taro Minowa) ;; ;; Permission is hereby granted, free of charge, to any person obtaining a ;; copy of this software and associated documentation files (the "Software"), ;; to deal in the Software without restriction, including without limitation ;; the rights to use, copy, modify, merge, publish, distribute, sublicense, ;; and/or sell copies of the Software, and to permit persons to whom the ;; Software is furnished to do so, subject to the following conditions: ;; ;; The above copyright notice and this permission notice shall be included in ;; all copies or substantial portions of the Software. ;; ;; Except as contained in this notice, the name(s) of the above copyright ;; holders shall not be used in advertising or otherwise to promote the sale, ;; use or other dealings in this Software without prior written authorization. ;; ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ;; DEALINGS IN THE SOFTWARE. #!r6rs (library (srfi s6 basic-string-ports compat) (export get-output-string open-output-string) (import (only (system) get-output-string open-output-string)) ) |
Added srfi/s6/basic-string-ports/compat.ypsilon.sls.
> > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s6 basic-string-ports compat) (export (rename (make-string-output-port open-output-string) (get-accumulated-string get-output-string))) (import (only (core) make-string-output-port get-accumulated-string)) ) |
Added srfi/s61/cond.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s61 cond) (export (rename (general-cond cond))) (import (rnrs)) (define-syntax general-cond (lambda (stx) (syntax-case stx () [(_ clauses ...) (with-syntax ([(ours ...) (map (lambda (c) (syntax-case c (=>) [(generator guard => receiver) #'((let-values ([vals generator]) (and (apply guard vals) vals)) => (lambda (vals) (apply receiver vals)))] [_ c])) #'(clauses ...))]) #'(cond ours ...))]))) ) |
Added srfi/s64/testing.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 |
;; Copyright (c) 2005, 2006 Per Bothner ;; ;; Permission is hereby granted, free of charge, to any person ;; obtaining a copy of this software and associated documentation ;; files (the "Software"), to deal in the Software without ;; restriction, including without limitation the rights to use, copy, ;; modify, merge, publish, distribute, sublicense, and/or sell copies ;; of the Software, and to permit persons to whom the Software is ;; furnished to do so, subject to the following conditions: ;; ;; The above copyright notice and this permission notice shall be ;; included in all copies or substantial portions of the Software. ;; ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS ;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ;; SOFTWARE. (cond-expand (r6rs) (chicken (require-extension syntax-case)) (guile (use-modules (ice-9 syncase) (srfi srfi-9) ;;(srfi srfi-34) (srfi srfi-35) - not in Guile 1.6.7 (srfi srfi-39))) (sisc (require-extension (srfi 9 34 35 39))) (kawa (module-compile-options warn-undefined-variable: #t warn-invoke-unknown-method: #t) (provide 'srfi-64) (provide 'testing) (require 'srfi-34) (require 'srfi-35)) (else () )) (cond-expand (r6rs (define-syntax %test-export (syntax-rules () ((%test-export . names) (begin))))) (kawa (define-syntax %test-export (syntax-rules () ((%test-export test-begin . other-names) (module-export %test-begin . other-names))))) (else (define-syntax %test-export (syntax-rules () ((%test-export . names) (if #f #f)))))) ;; List of exported names (%test-export test-begin ;; must be listed first, since in Kawa (at least) it is "magic". test-end test-assert test-eqv test-eq test-equal test-approximate test-assert test-error test-apply test-with-runner test-match-nth test-match-all test-match-any test-match-name test-skip test-expect-fail test-read-eval-string test-runner-group-path test-group-with-cleanup test-result-ref test-result-set! test-result-clear test-result-remove test-result-kind test-passed? test-log-to-file ; Misc test-runner functions test-runner? test-runner-reset test-runner-null test-runner-simple test-runner-current test-runner-factory test-runner-get test-runner-create test-runner-test-name ;; test-runner field setter and getter functions - see %test-record-define: test-runner-pass-count test-runner-pass-count! test-runner-fail-count test-runner-fail-count! test-runner-xpass-count test-runner-xpass-count! test-runner-xfail-count test-runner-xfail-count! test-runner-skip-count test-runner-skip-count! test-runner-group-stack test-runner-group-stack! test-runner-on-test-begin test-runner-on-test-begin! test-runner-on-test-end test-runner-on-test-end! test-runner-on-group-begin test-runner-on-group-begin! test-runner-on-group-end test-runner-on-group-end! test-runner-on-final test-runner-on-final! test-runner-on-bad-count test-runner-on-bad-count! test-runner-on-bad-end-name test-runner-on-bad-end-name! test-result-alist test-result-alist! test-runner-aux-value test-runner-aux-value! ;; default/simple call-back functions, used in default test-runner, ;; but can be called to construct more complex ones. test-on-group-begin-simple test-on-group-end-simple test-on-bad-count-simple test-on-bad-end-name-simple test-on-final-simple test-on-test-end-simple test-on-final-simple) (cond-expand (srfi-9 (define-syntax %test-record-define (syntax-rules () ((%test-record-define alloc runner? (name index getter setter) ...) (define-record-type test-runner (alloc) runner? (name getter setter) ...))))) (else (define %test-runner-cookie (list "test-runner")) (define-syntax %test-record-define (syntax-rules () ((%test-record-define alloc runner? (name index getter setter) ...) (begin (define (runner? obj) (and (vector? obj) (> (vector-length obj) 1) (eq (vector-ref obj 0) %test-runner-cookie))) (define (alloc) (let ((runner (make-vector 22))) (vector-set! runner 0 %test-runner-cookie) runner)) (begin (define (getter runner) (vector-ref runner index)) ...) (begin (define (setter runner value) (vector-set! runner index value)) ...))))))) (%test-record-define %test-runner-alloc test-runner? ;; Cumulate count of all tests that have passed and were expected to. (pass-count 1 test-runner-pass-count test-runner-pass-count!) (fail-count 2 test-runner-fail-count test-runner-fail-count!) (xpass-count 3 test-runner-xpass-count test-runner-xpass-count!) (xfail-count 4 test-runner-xfail-count test-runner-xfail-count!) (skip-count 5 test-runner-skip-count test-runner-skip-count!) (skip-list 6 %test-runner-skip-list %test-runner-skip-list!) (fail-list 7 %test-runner-fail-list %test-runner-fail-list!) ;; Normally #t, except when in a test-apply. (run-list 8 %test-runner-run-list %test-runner-run-list!) (skip-save 9 %test-runner-skip-save %test-runner-skip-save!) (fail-save 10 %test-runner-fail-save %test-runner-fail-save!) (group-stack 11 test-runner-group-stack test-runner-group-stack!) (on-test-begin 12 test-runner-on-test-begin test-runner-on-test-begin!) (on-test-end 13 test-runner-on-test-end test-runner-on-test-end!) ;; Call-back when entering a group. Takes (runner suite-name count). (on-group-begin 14 test-runner-on-group-begin test-runner-on-group-begin!) ;; Call-back when leaving a group. (on-group-end 15 test-runner-on-group-end test-runner-on-group-end!) ;; Call-back when leaving the outermost group. (on-final 16 test-runner-on-final test-runner-on-final!) ;; Call-back when expected number of tests was wrong. (on-bad-count 17 test-runner-on-bad-count test-runner-on-bad-count!) ;; Call-back when name in test=end doesn't match test-begin. (on-bad-end-name 18 test-runner-on-bad-end-name test-runner-on-bad-end-name!) ;; Cumulate count of all tests that have been done. (total-count 19 %test-runner-total-count %test-runner-total-count!) ;; Stack (list) of (count-at-start . expected-count): (count-list 20 %test-runner-count-list %test-runner-count-list!) (result-alist 21 test-result-alist test-result-alist!) ;; Field can be used by test-runner for any purpose. ;; test-runner-simple uses it for a log file. (aux-value 22 test-runner-aux-value test-runner-aux-value!) ) (define (test-runner-reset runner) (test-result-alist! runner '()) (test-runner-pass-count! runner 0) (test-runner-fail-count! runner 0) (test-runner-xpass-count! runner 0) (test-runner-xfail-count! runner 0) (test-runner-skip-count! runner 0) (%test-runner-total-count! runner 0) (%test-runner-count-list! runner '()) (%test-runner-run-list! runner #t) (%test-runner-skip-list! runner '()) (%test-runner-fail-list! runner '()) (%test-runner-skip-save! runner '()) (%test-runner-fail-save! runner '()) (test-runner-group-stack! runner '())) (define (test-runner-group-path runner) (reverse (test-runner-group-stack runner))) (define (%test-null-callback runner) #f) (define (test-runner-null) (let ((runner (%test-runner-alloc))) (test-runner-reset runner) (test-runner-on-group-begin! runner (lambda (runner name count) #f)) (test-runner-on-group-end! runner %test-null-callback) (test-runner-on-final! runner %test-null-callback) (test-runner-on-test-begin! runner %test-null-callback) (test-runner-on-test-end! runner %test-null-callback) (test-runner-on-bad-count! runner (lambda (runner count expected) #f)) (test-runner-on-bad-end-name! runner (lambda (runner begin end) #f)) runner)) ;; Not part of the specification. FIXME ;; Controls whether a log file is generated. (define test-log-to-file #F) (define (test-runner-simple) (let ((runner (%test-runner-alloc))) (test-runner-reset runner) (test-runner-on-group-begin! runner test-on-group-begin-simple) (test-runner-on-group-end! runner test-on-group-end-simple) (test-runner-on-final! runner test-on-final-simple) (test-runner-on-test-begin! runner test-on-test-begin-simple) (test-runner-on-test-end! runner test-on-test-end-simple) (test-runner-on-bad-count! runner test-on-bad-count-simple) (test-runner-on-bad-end-name! runner test-on-bad-end-name-simple) runner)) (cond-expand (srfi-39 (define test-runner-current (make-parameter #f)) (define test-runner-factory (make-parameter test-runner-simple))) (else (define %test-runner-current #f) (define-syntax test-runner-current (syntax-rules () ((test-runner-current) %test-runner-current) ((test-runner-current runner) (set! %test-runner-current runner)))) (define %test-runner-factory test-runner-simple) (define-syntax test-runner-factory (syntax-rules () ((test-runner-factory) %test-runner-factory) ((test-runner-factory runner) (set! %test-runner-factory runner)))))) ;; A safer wrapper to test-runner-current. (define (test-runner-get) (let ((r (test-runner-current))) (if (not r) (cond-expand (srfi-23 (error "test-runner not initialized - test-begin missing?")) (else #t))) r)) (define (%test-specificier-matches spec runner) (spec runner)) (define (test-runner-create) ((test-runner-factory))) (define (%test-any-specifier-matches list runner) (let ((result #f)) (let loop ((l list)) (cond ((null? l) result) (else (if (%test-specificier-matches (car l) runner) (set! result #t)) (loop (cdr l))))))) ;; Returns #f, #t, or 'xfail. (define (%test-should-execute runner) (let ((run (%test-runner-run-list runner))) (cond ((or (not (or (eqv? run #t) (%test-any-specifier-matches run runner))) (%test-any-specifier-matches (%test-runner-skip-list runner) runner)) (test-result-set! runner 'result-kind 'skip) #f) ((%test-any-specifier-matches (%test-runner-fail-list runner) runner) (test-result-set! runner 'result-kind 'xfail) 'xfail) (else #t)))) (define (%test-begin suite-name count) (if (not (test-runner-current)) (test-runner-current (test-runner-create))) (let ((runner (test-runner-current))) ((test-runner-on-group-begin runner) runner suite-name count) (%test-runner-skip-save! runner (cons (%test-runner-skip-list runner) (%test-runner-skip-save runner))) (%test-runner-fail-save! runner (cons (%test-runner-fail-list runner) (%test-runner-fail-save runner))) (%test-runner-count-list! runner (cons (cons (%test-runner-total-count runner) count) (%test-runner-count-list runner))) (test-runner-group-stack! runner (cons suite-name (test-runner-group-stack runner))))) (cond-expand ((and (not r6rs) kawa) ;; Kawa has test-begin built in, implemented as: ;; (begin ;; (cond-expand (srfi-64 #!void) (else (require 'srfi-64))) ;; (%test-begin suite-name [count])) ;; This puts test-begin but only test-begin in the default environment., ;; which makes normal test suites loadable without non-portable commands. ) (else (define-syntax test-begin (syntax-rules () ((test-begin suite-name) (%test-begin suite-name #f)) ((test-begin suite-name count) (%test-begin suite-name count)))))) (define (test-on-group-begin-simple runner suite-name count) (if (null? (test-runner-group-stack runner)) (begin (display "%%%% Starting test ") (display suite-name) (if test-log-to-file (let* ((log-file-name (if (string? test-log-to-file) test-log-to-file (string-append suite-name ".log"))) (log-file (cond-expand ((and (not r6rs) mzscheme) (open-output-file log-file-name 'truncate/replace)) (else (open-output-file log-file-name))))) (display "%%%% Starting test " log-file) (display suite-name log-file) (newline log-file) (test-runner-aux-value! runner log-file) (display " (Writing full log to \"") (display log-file-name) (display "\")"))) (newline))) (let ((log (test-runner-aux-value runner))) (if (output-port? log) (begin (display "Group begin: " log) (display suite-name log) (newline log)))) #f) (define (test-on-group-end-simple runner) (let ((log (test-runner-aux-value runner))) (if (output-port? log) (begin (display "Group end: " log) (display (car (test-runner-group-stack runner)) log) (newline log)))) #f) (define (%test-on-bad-count-write runner count expected-count port) (display "*** Total number of tests was " port) (display count port) (display " but should be " port) (display expected-count port) (display ". ***" port) (newline port) (display "*** Discrepancy indicates testsuite error or exceptions. ***" port) (newline port)) (define (test-on-bad-count-simple runner count expected-count) (%test-on-bad-count-write runner count expected-count (current-output-port)) (let ((log (test-runner-aux-value runner))) (if (output-port? log) (%test-on-bad-count-write runner count expected-count log)))) (define (test-on-bad-end-name-simple runner begin-name end-name) (let ((msg (string-append (%test-format-line runner) "test-end " begin-name " does not match test-begin " end-name))) (cond-expand (srfi-23 (error msg)) (else (display msg) (newline))))) (define (%test-final-report1 value label port) (if (> value 0) (begin (display label port) (display value port) (newline port)))) (define (%test-final-report-simple runner port) (%test-final-report1 (test-runner-pass-count runner) "# of expected passes " port) (%test-final-report1 (test-runner-xfail-count runner) "# of expected failures " port) (%test-final-report1 (test-runner-xpass-count runner) "# of unexpected successes " port) (%test-final-report1 (test-runner-fail-count runner) "# of unexpected failures " port) (%test-final-report1 (test-runner-skip-count runner) "# of skipped tests " port)) (define (test-on-final-simple runner) (%test-final-report-simple runner (current-output-port)) (let ((log (test-runner-aux-value runner))) (if (output-port? log) (%test-final-report-simple runner log)))) (define (%test-format-line runner) (let* ((line-info (test-result-alist runner)) (source-file (assq 'source-file line-info)) (source-line (assq 'source-line line-info)) (file (if source-file (cdr source-file) ""))) (if source-line (string-append file ":" (number->string (cdr source-line)) ": ") ""))) (define (%test-end suite-name line-info) (let* ((r (test-runner-get)) (groups (test-runner-group-stack r)) (line (%test-format-line r))) (test-result-alist! r line-info) (if (null? groups) (let ((msg (string-append line "test-end not in a group"))) (cond-expand (srfi-23 (error msg)) (else (display msg) (newline))))) (if (and suite-name (not (equal? suite-name (car groups)))) ((test-runner-on-bad-end-name r) r suite-name (car groups))) (let* ((count-list (%test-runner-count-list r)) (expected-count (cdar count-list)) (saved-count (caar count-list)) (group-count (- (%test-runner-total-count r) saved-count))) (if (and expected-count (not (= expected-count group-count))) ((test-runner-on-bad-count r) r group-count expected-count)) ((test-runner-on-group-end r) r) (test-runner-group-stack! r (cdr (test-runner-group-stack r))) (%test-runner-skip-list! r (car (%test-runner-skip-save r))) (%test-runner-skip-save! r (cdr (%test-runner-skip-save r))) (%test-runner-fail-list! r (car (%test-runner-fail-save r))) (%test-runner-fail-save! r (cdr (%test-runner-fail-save r))) (%test-runner-count-list! r (cdr count-list)) (if (null? (test-runner-group-stack r)) ((test-runner-on-final r) r))))) (define-syntax test-group (syntax-rules () ((test-group suite-name . body) (let ((r (test-runner-current))) ;; Ideally should also set line-number, if available. (test-result-alist! r (list (cons 'test-name suite-name))) (if (%test-should-execute r) (dynamic-wind (lambda () (test-begin suite-name)) (lambda () . body) (lambda () (test-end suite-name)))))))) (define-syntax test-group-with-cleanup (syntax-rules () ((test-group-with-cleanup suite-name form cleanup-form) (test-group suite-name (dynamic-wind (lambda () #f) (lambda () form) (lambda () cleanup-form)))) ((test-group-with-cleanup suite-name cleanup-form) (test-group-with-cleanup suite-name #f cleanup-form)) ((test-group-with-cleanup suite-name form1 form2 form3 . rest) (test-group-with-cleanup suite-name (begin form1 form2) form3 . rest)))) (define (test-on-test-begin-simple runner) (let ((log (test-runner-aux-value runner))) (if (output-port? log) (let* ((results (test-result-alist runner)) (source-file (assq 'source-file results)) (source-line (assq 'source-line results)) (source-form (assq 'source-form results)) (test-name (assq 'test-name results))) (display "Test begin:" log) (newline log) (if test-name (%test-write-result1 test-name log)) (if source-file (%test-write-result1 source-file log)) (if source-line (%test-write-result1 source-line log)) (if source-form (%test-write-result1 source-form log)))))) (define-syntax test-result-ref (syntax-rules () ((test-result-ref runner pname) (test-result-ref runner pname #f)) ((test-result-ref runner pname default) (let ((p (assq pname (test-result-alist runner)))) (if p (cdr p) default))))) (define (test-on-test-end-simple runner) (let ((log (test-runner-aux-value runner)) (kind (test-result-ref runner 'result-kind))) (if (memq kind '(fail xpass)) (let* ((results (test-result-alist runner)) (source-file (assq 'source-file results)) (source-line (assq 'source-line results)) (test-name (assq 'test-name results))) (if (or source-file source-line) (begin (if source-file (display (cdr source-file))) (display ":") (if source-line (display (cdr source-line))) (display ": "))) (display (if (eq? kind 'xpass) "XPASS" "FAIL")) (if test-name (begin (display " ") (display (cdr test-name)))) (newline))) (if (output-port? log) (begin (display "Test end:" log) (newline log) (let loop ((list (test-result-alist runner))) (if (pair? list) (let ((pair (car list))) ;; Write out properties not written out by on-test-begin. (if (not (memq (car pair) '(test-name source-file source-line source-form))) (%test-write-result1 pair log)) (loop (cdr list))))))))) (define (%test-write-result1 pair port) (display " " port) (display (car pair) port) (display ": " port) (write (cdr pair) port) (newline port)) (define (test-result-set! runner pname value) (let* ((alist (test-result-alist runner)) (p (assq pname alist))) (if p (set-cdr! p value) (test-result-alist! runner (cons (cons pname value) alist))))) (define (test-result-clear runner) (test-result-alist! runner '())) (define (test-result-remove runner pname) (let* ((alist (test-result-alist runner)) (p (assq pname alist))) (if p (test-result-alist! runner (let loop ((r alist)) (if (eq? r p) (cdr r) (cons (car r) (loop (cdr r))))))))) (define (test-result-kind . rest) (let ((runner (if (pair? rest) (car rest) (test-runner-current)))) (test-result-ref runner 'result-kind))) (define (test-passed? . rest) (let ((runner (if (pair? rest) (car rest) (test-runner-get)))) (memq (test-result-ref runner 'result-kind) '(pass xpass)))) (define (%test-report-result) (let* ((r (test-runner-get)) (result-kind (test-result-kind r))) (case result-kind ((pass) (test-runner-pass-count! r (+ 1 (test-runner-pass-count r)))) ((fail) (test-runner-fail-count! r (+ 1 (test-runner-fail-count r)))) ((xpass) (test-runner-xpass-count! r (+ 1 (test-runner-xpass-count r)))) ((xfail) (test-runner-xfail-count! r (+ 1 (test-runner-xfail-count r)))) (else (test-runner-skip-count! r (+ 1 (test-runner-skip-count r))))) (%test-runner-total-count! r (+ 1 (%test-runner-total-count r))) ((test-runner-on-test-end r) r))) (cond-expand (r6rs (define-syntax %test-evaluate-with-catch (syntax-rules () ((%test-evaluate-with-catch test-expression) (guard (ex (else #F)) test-expression))))) (guile (define-syntax %test-evaluate-with-catch (syntax-rules () ((%test-evaluate-with-catch test-expression) (catch #t (lambda () test-expression) (lambda (key . args) #f)))))) (kawa (define-syntax %test-evaluate-with-catch (syntax-rules () ((%test-evaluate-with-catch test-expression) (try-catch test-expression (ex <java.lang.Throwable> (test-result-set! (test-runner-current) 'actual-error ex) #f)))))) (srfi-34 (define-syntax %test-evaluate-with-catch (syntax-rules () ((%test-evaluate-with-catch test-expression) (guard (err (else #f)) test-expression))))) (chicken (define-syntax %test-evaluate-with-catch (syntax-rules () ((%test-evaluate-with-catch test-expression) (condition-case test-expression (ex () #f)))))) (else (define-syntax %test-evaluate-with-catch (syntax-rules () ((%test-evaluate-with-catch test-expression) test-expression))))) (cond-expand ((and (not r6rs) (or kawa mzscheme)) (cond-expand (mzscheme (define-for-syntax (%test-syntax-file form) (let ((source (syntax-source form))) (cond ((string? source) file) ((path? source) (path->string source)) (else #f))))) (kawa (define (%test-syntax-file form) (syntax-source form)))) (define-for-syntax (%test-source-line2 form) (let* ((line (syntax-line form)) (file (%test-syntax-file form)) (line-pair (if line (list (cons 'source-line line)) '()))) (cons (cons 'source-form (syntax-object->datum form)) (if file (cons (cons 'source-file file) line-pair) line-pair))))) (else (define (%test-source-line2 form) '()))) (define (%test-on-test-begin r) (%test-should-execute r) ((test-runner-on-test-begin r) r) (not (eq? 'skip (test-result-ref r 'result-kind)))) (define (%test-on-test-end r result) (test-result-set! r 'result-kind (if (eq? (test-result-ref r 'result-kind) 'xfail) (if result 'xpass 'xfail) (if result 'pass 'fail)))) (define (test-runner-test-name runner) (test-result-ref runner 'test-name "")) (define-syntax %test-comp2body (syntax-rules () ((%test-comp2body r comp expected expr) (let () (if (%test-on-test-begin r) (let ((exp expected)) (test-result-set! r 'expected-value exp) (let ((res (%test-evaluate-with-catch expr))) (test-result-set! r 'actual-value res) (%test-on-test-end r (comp exp res))))) (%test-report-result))))) (define (%test-approximimate= error) (lambda (value expected) (and (>= value (- expected error)) (<= value (+ expected error))))) (define-syntax %test-comp1body (syntax-rules () ((%test-comp1body r expr) (let () (if (%test-on-test-begin r) (let () (let ((res (%test-evaluate-with-catch expr))) (test-result-set! r 'actual-value res) (%test-on-test-end r res)))) (%test-report-result))))) (cond-expand ((and (not r6rs) (or kawa mzscheme)) ;; Should be made to work for any Scheme with syntax-case ;; However, I haven't gotten the quoting working. FIXME. (define-syntax test-end (lambda (x) (syntax-case (list x (list 'quote (%test-source-line2 x))) () (((mac suite-name) line) (syntax (%test-end suite-name line))) (((mac) line) (syntax (%test-end #f line)))))) (define-syntax test-assert (lambda (x) (syntax-case (list x (list 'quote (%test-source-line2 x))) () (((mac tname expr) line) (syntax (let* ((r (test-runner-get)) (name tname)) (test-result-alist! r (cons (cons 'test-name tname) line)) (%test-comp1body r expr)))) (((mac expr) line) (syntax (let* ((r (test-runner-get))) (test-result-alist! r line) (%test-comp1body r expr))))))) (define-for-syntax (%test-comp2 comp x) (syntax-case (list x (list 'quote (%test-source-line2 x)) comp) () (((mac tname expected expr) line comp) (syntax (let* ((r (test-runner-get)) (name tname)) (test-result-alist! r (cons (cons 'test-name tname) line)) (%test-comp2body r comp expected expr)))) (((mac expected expr) line comp) (syntax (let* ((r (test-runner-get))) (test-result-alist! r line) (%test-comp2body r comp expected expr)))))) (define-syntax test-eqv (lambda (x) (%test-comp2 (syntax eqv?) x))) (define-syntax test-eq (lambda (x) (%test-comp2 (syntax eq?) x))) (define-syntax test-equal (lambda (x) (%test-comp2 (syntax equal?) x))) (define-syntax test-approximate ;; FIXME - needed for non-Kawa (lambda (x) (syntax-case (list x (list 'quote (%test-source-line2 x))) () (((mac tname expected expr error) line) (syntax (let* ((r (test-runner-get)) (name tname)) (test-result-alist! r (cons (cons 'test-name tname) line)) (%test-comp2body r (%test-approximimate= error) expected expr)))) (((mac expected expr error) line) (syntax (let* ((r (test-runner-get))) (test-result-alist! r line) (%test-comp2body r (%test-approximimate= error) expected expr)))))))) (else (define-syntax test-end (syntax-rules () ((test-end) (%test-end #f '())) ((test-end suite-name) (%test-end suite-name '())))) (define-syntax test-assert (syntax-rules () ((test-assert tname test-expression) (let ((r (test-runner-get))) (test-result-alist! r `((test-name . ,tname) (source-form . test-expression))) (%test-comp1body r test-expression))) ((test-assert test-expression) (let ((r (test-runner-get))) (test-result-alist! r '((source-form . test-expression))) (%test-comp1body r test-expression))))) (define-syntax %test-comp2 (syntax-rules () ((%test-comp2 comp tname expected expr) (let ((r (test-runner-get))) (test-result-alist! r `((test-name . ,tname) (source-form . expr))) (%test-comp2body r comp expected expr))) ((%test-comp2 comp expected expr) (let ((r (test-runner-get))) (test-result-alist! r '((source-form . expr))) (%test-comp2body r comp expected expr))))) (define-syntax test-equal (syntax-rules () ((test-equal . rest) (%test-comp2 equal? . rest)))) (define-syntax test-eqv (syntax-rules () ((test-eqv . rest) (%test-comp2 eqv? . rest)))) (define-syntax test-eq (syntax-rules () ((test-eq . rest) (%test-comp2 eq? . rest)))) (define-syntax test-approximate (syntax-rules () ((test-approximate tname expected expr error) (%test-comp2 (%test-approximimate= error) tname expected expr)) ((test-approximate expected expr error) (%test-comp2 (%test-approximimate= error) expected expr)))))) (cond-expand (r6rs (define-syntax %test-error (syntax-rules () ((%test-error etype expr) (let ((t etype)) (when (procedure? t) (test-result-set! (test-runner-get) 'expected-error t)) (guard (ex (else (test-result-set! (test-runner-get) 'actual-error ex) (if (procedure? t) (t ex) #T))) expr #F)))))) (guile (define-syntax %test-error (syntax-rules () ((%test-error r etype expr) (%test-comp1body r (catch #t (lambda () expr) (lambda (key . args) #t))))))) (mzscheme (define-syntax %test-error (syntax-rules () ((%test-error r etype expr) (%test-comp1body r (with-handlers (((lambda (h) #t) (lambda (h) #t))) (let () (test-result-set! r 'actual-value expr) #f))))))) (chicken (define-syntax %test-error (syntax-rules () ((%test-error r etype expr) (%test-comp1body r (condition-case expr (ex () #t))))))) (kawa (define-syntax %test-error (syntax-rules () ((%test-error r etype expr) (let () (if (%test-on-test-begin r) (let ((et etype)) (test-result-set! r 'expected-error et) (%test-on-test-end r (try-catch (let () (test-result-set! r 'actual-value expr) #f) (ex <java.lang.Throwable> (test-result-set! r 'actual-error ex) (cond ((and (instance? et <gnu.bytecode.ClassType>) (gnu.bytecode.ClassType:isSubclass et <java.lang.Throwable>)) (instance? ex et)) (else #t))))) (%test-report-result)))))))) ((and srfi-34 srfi-35) (define-syntax %test-error (syntax-rules () ((%test-error r etype expr) (%test-comp1body r (guard (ex ((condition-type? etype) (and (condition? ex) (condition-has-type? ex etype))) ((procedure? etype) (etype ex)) ((equal? type #t) #t) (else #t)) expr)))))) (srfi-34 (define-syntax %test-error (syntax-rules () ((%test-error r etype expr) (%test-comp1body r (guard (ex (else #t)) expr)))))) (else (define-syntax %test-error (syntax-rules () ((%test-error r etype expr) (begin ((test-runner-on-test-begin r) r) (test-result-set! r 'result-kind 'skip) (%test-report-result))))))) (cond-expand ((and (not r6rs) (or kawa mzscheme)) (define-syntax test-error (lambda (x) (syntax-case (list x (list 'quote (%test-source-line2 x))) () (((mac tname etype expr) line) (syntax (let* ((r (test-runner-get)) (name tname)) (test-result-alist! r (cons (cons 'test-name tname) line)) (%test-error r etype expr)))) (((mac etype expr) line) (syntax (let* ((r (test-runner-get))) (test-result-alist! r line) (%test-error r etype expr)))) (((mac expr) line) (syntax (let* ((r (test-runner-get))) (test-result-alist! r line) (%test-error r #t expr)))))))) (else (define-syntax test-error (syntax-rules () ((test-error name etype expr) (test-assert name (%test-error etype expr))) ((test-error etype expr) (test-assert (%test-error etype expr))) ((test-error expr) (test-assert (%test-error #t expr))))))) (define (test-apply first . rest) (if (test-runner? first) (test-with-runner first (apply test-apply rest)) (let ((r (test-runner-current))) (if r (let ((run-list (%test-runner-run-list r))) (cond ((null? rest) (%test-runner-run-list! r (reverse! run-list)) (first)) ;; actually apply procedure thunk (else (%test-runner-run-list! r (if (eq? run-list #t) (list first) (cons first run-list))) (apply test-apply rest) (%test-runner-run-list! r run-list)))) (let ((r (test-runner-create))) (test-with-runner r (apply test-apply first rest)) ((test-runner-on-final r) r)))))) (define-syntax test-with-runner (syntax-rules () ((test-with-runner runner form ...) (let ((saved-runner (test-runner-current))) (dynamic-wind (lambda () (test-runner-current runner)) (lambda () form ...) (lambda () (test-runner-current saved-runner))))))) ;;; Predicates (define (%test-match-nth n count) (let ((i 0)) (lambda (runner) (set! i (+ i 1)) (and (>= i n) (< i (+ n count)))))) (define-syntax test-match-nth (syntax-rules () ((test-match-nth n) (test-match-nth n 1)) ((test-match-nth n count) (%test-match-nth n count)))) (define (%test-match-all . pred-list) (lambda (runner) (let ((result #t)) (let loop ((l pred-list)) (if (null? l) result (begin (if (not ((car l) runner)) (set! result #f)) (loop (cdr l)))))))) (define-syntax test-match-all (syntax-rules () ((test-match-all pred ...) (%test-match-all (%test-as-specifier pred) ...)))) (define (%test-match-any . pred-list) (lambda (runner) (let ((result #f)) (let loop ((l pred-list)) (if (null? l) result (begin (if ((car l) runner) (set! result #t)) (loop (cdr l)))))))) (define-syntax test-match-any (syntax-rules () ((test-match-any pred ...) (%test-match-any (%test-as-specifier pred) ...)))) ;; Coerce to a predicate function: (define (%test-as-specifier specifier) (cond ((procedure? specifier) specifier) ((integer? specifier) (test-match-nth 1 specifier)) ((string? specifier) (test-match-name specifier)) (else (error "not a valid test specifier")))) (define-syntax test-skip (syntax-rules () ((test-skip pred ...) (let ((runner (test-runner-get))) (%test-runner-skip-list! runner (cons (test-match-all (%test-as-specifier pred) ...) (%test-runner-skip-list runner))))))) (define-syntax test-expect-fail (syntax-rules () ((test-expect-fail pred ...) (let ((runner (test-runner-get))) (%test-runner-fail-list! runner (cons (test-match-all (%test-as-specifier pred) ...) (%test-runner-fail-list runner))))))) (define (test-match-name name) (lambda (runner) (equal? name (test-runner-test-name runner)))) (define (test-read-eval-string string) (let* ((port (open-input-string string)) (form (read port))) (if (eof-object? (read-char port)) (eval form) (cond-expand (srfi-23 (error "(not at eof)")) (else "error"))))) |
Added srfi/s64/testing.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s64 testing) (export test-begin test-end test-assert test-eqv test-eq test-equal test-approximate test-error test-apply test-with-runner test-match-nth test-match-all test-match-any test-match-name test-skip test-expect-fail test-read-eval-string test-group test-runner-group-path test-group-with-cleanup test-result-ref test-result-set! test-result-clear test-result-remove test-result-kind test-passed? (rename (%test-log-to-file test-log-to-file)) ; Misc test-runner functions test-runner? test-runner-reset test-runner-null test-runner-simple test-runner-current test-runner-factory test-runner-get test-runner-create test-runner-test-name ;; test-runner field setter and getter functions - see %test-record-define: test-runner-pass-count test-runner-pass-count! test-runner-fail-count test-runner-fail-count! test-runner-xpass-count test-runner-xpass-count! test-runner-xfail-count test-runner-xfail-count! test-runner-skip-count test-runner-skip-count! test-runner-group-stack test-runner-group-stack! test-runner-on-test-begin test-runner-on-test-begin! test-runner-on-test-end test-runner-on-test-end! test-runner-on-group-begin test-runner-on-group-begin! test-runner-on-group-end test-runner-on-group-end! test-runner-on-final test-runner-on-final! test-runner-on-bad-count test-runner-on-bad-count! test-runner-on-bad-end-name test-runner-on-bad-end-name! test-result-alist test-result-alist! test-runner-aux-value test-runner-aux-value! ;; default/simple call-back functions, used in default test-runner, ;; but can be called to construct more complex ones. test-on-group-begin-simple test-on-group-end-simple test-on-bad-count-simple test-on-bad-end-name-simple test-on-final-simple test-on-test-end-simple) (import (rnrs base) (rnrs control) (rnrs exceptions) (rnrs io simple) (rnrs lists) (rename (rnrs eval) (eval rnrs:eval)) (rnrs mutable-pairs) (srfi s0 cond-expand) (only (srfi s1 lists) reverse!) (srfi s6 basic-string-ports) (srfi s9 records) (srfi s39 parameters) (srfi s23 error tricks) (srfi private include)) (define (eval form) (rnrs:eval form (environment '(rnrs) '(rnrs eval) '(rnrs mutable-pairs) '(rnrs mutable-strings) '(rnrs r5rs)))) (define %test-log-to-file (case-lambda (() test-log-to-file) ((val) (set! test-log-to-file val)))) (SRFI-23-error->R6RS "(library (srfi s64 testing))" (include/resolve ("srfi" "s64") "testing.scm")) ) |
Added srfi/s67/_compare-procedures.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s67 compare-procedures) (export </<=? </<? <=/<=? <=/<? <=? <? =? >/>=? >/>? >=/>=? >=/>? >=? >? boolean-compare chain<=? chain<? chain=? chain>=? chain>? char-compare char-compare-ci compare-by< compare-by<= compare-by=/< compare-by=/> compare-by> compare-by>= complex-compare cond-compare debug-compare default-compare if-not=? if3 if<=? if<? if=? if>=? if>? integer-compare kth-largest list-compare list-compare-as-vector max-compare min-compare not=? number-compare pair-compare pair-compare-car pair-compare-cdr pairwise-not=? rational-compare real-compare refine-compare select-compare string-compare string-compare-ci symbol-compare vector-compare vector-compare-as-list) (import (except (rnrs) error) (rnrs r5rs) ; for modulo (srfi s27 random-bits) ; for random-integer (srfi s23 error) ;; (srfi s23 error tricks) (srfi private include)) ;; (SRFI-23-error->R6RS "(library (srfi s67 compare-procedures))" ;; (include/resolve ("srfi" "s67") "compare.ss")) ; (define current-compare (make-parameter default-compare)) ; (provide current-compare) ; Copyright (c) 2005 Sebastian Egner and Jens Axel S{\o}gaard. ; ; Permission is hereby granted, free of charge, to any person obtaining ; a copy of this software and associated documentation files (the ; ``Software''), to deal in the Software without restriction, including ; without limitation the rights to use, copy, modify, merge, publish, ; distribute, sublicense, and/or sell copies of the Software, and to ; permit persons to whom the Software is furnished to do so, subject to ; the following conditions: ; ; The above copyright notice and this permission notice shall be ; included in all copies or substantial portions of the Software. ; ; THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, ; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION ; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION ; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ; ; ----------------------------------------------------------------------- ; ; Compare procedures SRFI (reference implementation) ; Sebastian.Egner@philips.com, Jensaxel@soegaard.net ; history of this file: ; SE, 14-Oct-2004: first version ; SE, 18-Oct-2004: 1st redesign: axioms for 'compare function' ; SE, 29-Oct-2004: 2nd redesign: higher order reverse/map/refine/unite ; SE, 2-Nov-2004: 3rd redesign: macros cond/refine-compare replace h.o.f's ; SE, 10-Nov-2004: (im,re) replaced by (re,im) in complex-compare ; SE, 11-Nov-2004: case-compare by case (not by cond); select-compare added ; SE, 12-Jan-2005: pair-compare-cdr ; SE, 15-Feb-2005: stricter typing for compare-<type>; pairwise-not=? ; SE, 16-Feb-2005: case-compare -> if-compare -> if3; <? </<? chain<? etc. ; JS, 24-Feb-2005: selection-compare added ; SE, 25-Feb-2005: selection-compare -> kth-largest modified; if<? etc. ; JS, 28-Feb-2005: kth-largest modified - is "stable" now ; SE, 28-Feb-2005: simplified pairwise-not=?/kth-largest; min/max debugged ; SE, 07-Apr-2005: compare-based type checks made explicit ; SE, 18-Apr-2005: added (rel? compare) and eq?-test ; SE, 16-May-2005: naming convention changed; compare-by< etc. optional x y ; ============================================================================= ; Reference Implementation ; ======================== ; ; in R5RS (including hygienic macros) ; + SRFI-16 (case-lambda) ; + SRFI-23 (error) ; + SRFI-27 (random-integer) ; Implementation remarks: ; * In general, the emphasis of this implementation is on correctness ; and portability, not on efficiency. ; * Variable arity procedures are expressed in terms of case-lambda ; in the hope that this will produce efficient code for the case ; where the arity is statically known at the call site. ; * In procedures that are required to type-check their arguments, ; we use (compare x x) for executing extra checks. This relies on ; the assumption that eq? is used to catch this case quickly. ; * Care has been taken to reference comparison procedures of R5RS ; only at the time the operations here are being defined. This ; makes it possible to redefine these operations, if need be. ; * For the sake of efficiency, some inlining has been done by hand. ; This is mainly expressed by macros producing defines. ; * Identifiers of the form compare:<something> are private. ; ; Hints for low-level implementation: ; * The basis of this SRFI are the atomic compare procedures, ; i.e. boolean-compare, char-compare, etc. and the conditionals ; if3, if=?, if<? etc., and default-compare. These should make ; optimal use of the available type information. ; * For the sake of speed, the reference implementation does not ; use a LET to save the comparison value c for the ERROR call. ; This can be fixed in a low-level implementation at no cost. ; * Type-checks based on (compare x x) are made explicit by the ; expression (compare:check result compare x ...). ; * Eq? should can used to speed up built-in compare procedures, ; but it can only be used after type-checking at least one of ; the arguments. (define (compare:checked result compare . args) (for-each (lambda (x) (compare x x)) args) result) ; 3-sided conditional (define-syntax if3 (syntax-rules () ((if3 c less equal greater) (case c ((-1) less) (( 0) equal) (( 1) greater) (else (error "comparison value not in {-1,0,1}")))))) ; 2-sided conditionals for comparisons (define-syntax compare:if-rel? (syntax-rules () ((compare:if-rel? c-cases a-cases c consequence) (compare:if-rel? c-cases a-cases c consequence (if #f #f))) ((compare:if-rel? c-cases a-cases c consequence alternate) (case c (c-cases consequence) (a-cases alternate) (else (error "comparison value not in {-1,0,1}")))))) (define-syntax if=? (syntax-rules () ((if=? arg ...) (compare:if-rel? (0) (-1 1) arg ...)))) (define-syntax if<? (syntax-rules () ((if<? arg ...) (compare:if-rel? (-1) (0 1) arg ...)))) (define-syntax if>? (syntax-rules () ((if>? arg ...) (compare:if-rel? (1) (-1 0) arg ...)))) (define-syntax if<=? (syntax-rules () ((if<=? arg ...) (compare:if-rel? (-1 0) (1) arg ...)))) (define-syntax if>=? (syntax-rules () ((if>=? arg ...) (compare:if-rel? (0 1) (-1) arg ...)))) (define-syntax if-not=? (syntax-rules () ((if-not=? arg ...) (compare:if-rel? (-1 1) (0) arg ...)))) ; predicates from compare procedures (define-syntax compare:define-rel? (syntax-rules () ((compare:define-rel? rel? if-rel?) (define rel? (case-lambda (() (lambda (x y) (if-rel? (default-compare x y) #t #f))) ((compare) (lambda (x y) (if-rel? (compare x y) #t #f))) ((x y) (if-rel? (default-compare x y) #t #f)) ((compare x y) (if (procedure? compare) (if-rel? (compare x y) #t #f) (error "not a procedure (Did you mean rel/rel??): " compare)))))))) (compare:define-rel? =? if=?) (compare:define-rel? <? if<?) (compare:define-rel? >? if>?) (compare:define-rel? <=? if<=?) (compare:define-rel? >=? if>=?) (compare:define-rel? not=? if-not=?) ; chains of length 3 (define-syntax compare:define-rel1/rel2? (syntax-rules () ((compare:define-rel1/rel2? rel1/rel2? if-rel1? if-rel2?) (define rel1/rel2? (case-lambda (() (lambda (x y z) (if-rel1? (default-compare x y) (if-rel2? (default-compare y z) #t #f) (compare:checked #f default-compare z)))) ((compare) (lambda (x y z) (if-rel1? (compare x y) (if-rel2? (compare y z) #t #f) (compare:checked #f compare z)))) ((x y z) (if-rel1? (default-compare x y) (if-rel2? (default-compare y z) #t #f) (compare:checked #f default-compare z))) ((compare x y z) (if-rel1? (compare x y) (if-rel2? (compare y z) #t #f) (compare:checked #f compare z)))))))) (compare:define-rel1/rel2? </<? if<? if<?) (compare:define-rel1/rel2? </<=? if<? if<=?) (compare:define-rel1/rel2? <=/<? if<=? if<?) (compare:define-rel1/rel2? <=/<=? if<=? if<=?) (compare:define-rel1/rel2? >/>? if>? if>?) (compare:define-rel1/rel2? >/>=? if>? if>=?) (compare:define-rel1/rel2? >=/>? if>=? if>?) (compare:define-rel1/rel2? >=/>=? if>=? if>=?) ; chains of arbitrary length (define-syntax compare:define-chain-rel? (syntax-rules () ((compare:define-chain-rel? chain-rel? if-rel?) (define chain-rel? (case-lambda ((compare) #t) ((compare x1) (compare:checked #t compare x1)) ((compare x1 x2) (if-rel? (compare x1 x2) #t #f)) ((compare x1 x2 x3) (if-rel? (compare x1 x2) (if-rel? (compare x2 x3) #t #f) (compare:checked #f compare x3))) ((compare x1 x2 . x3+) (if-rel? (compare x1 x2) (let chain? ((head x2) (tail x3+)) (if (null? tail) #t (if-rel? (compare head (car tail)) (chain? (car tail) (cdr tail)) (apply compare:checked #f compare (cdr tail))))) (apply compare:checked #f compare x3+)))))))) (compare:define-chain-rel? chain=? if=?) (compare:define-chain-rel? chain<? if<?) (compare:define-chain-rel? chain>? if>?) (compare:define-chain-rel? chain<=? if<=?) (compare:define-chain-rel? chain>=? if>=?) ; pairwise inequality (define pairwise-not=? (let ((= =) (<= <=)) (case-lambda ((compare) #t) ((compare x1) (compare:checked #t compare x1)) ((compare x1 x2) (if-not=? (compare x1 x2) #t #f)) ((compare x1 x2 x3) (if-not=? (compare x1 x2) (if-not=? (compare x2 x3) (if-not=? (compare x1 x3) #t #f) #f) (compare:checked #f compare x3))) ((compare . x1+) (let unequal? ((x x1+) (n (length x1+)) (unchecked? #t)) (if (< n 2) (if (and unchecked? (= n 1)) (compare:checked #t compare (car x)) #t) (let* ((i-pivot (random-integer n)) (x-pivot (list-ref x i-pivot))) (let split ((i 0) (x x) (x< '()) (x> '())) (if (null? x) (and (unequal? x< (length x<) #f) (unequal? x> (length x>) #f)) (if (= i i-pivot) (split (+ i 1) (cdr x) x< x>) (if3 (compare (car x) x-pivot) (split (+ i 1) (cdr x) (cons (car x) x<) x>) (if unchecked? (apply compare:checked #f compare (cdr x)) #f) (split (+ i 1) (cdr x) x< (cons (car x) x>))))))))))))) ; min/max (define min-compare (case-lambda ((compare x1) (compare:checked x1 compare x1)) ((compare x1 x2) (if<=? (compare x1 x2) x1 x2)) ((compare x1 x2 x3) (if<=? (compare x1 x2) (if<=? (compare x1 x3) x1 x3) (if<=? (compare x2 x3) x2 x3))) ((compare x1 x2 x3 x4) (if<=? (compare x1 x2) (if<=? (compare x1 x3) (if<=? (compare x1 x4) x1 x4) (if<=? (compare x3 x4) x3 x4)) (if<=? (compare x2 x3) (if<=? (compare x2 x4) x2 x4) (if<=? (compare x3 x4) x3 x4)))) ((compare x1 x2 . x3+) (let min ((xmin (if<=? (compare x1 x2) x1 x2)) (xs x3+)) (if (null? xs) xmin (min (if<=? (compare xmin (car xs)) xmin (car xs)) (cdr xs))))))) (define max-compare (case-lambda ((compare x1) (compare:checked x1 compare x1)) ((compare x1 x2) (if>=? (compare x1 x2) x1 x2)) ((compare x1 x2 x3) (if>=? (compare x1 x2) (if>=? (compare x1 x3) x1 x3) (if>=? (compare x2 x3) x2 x3))) ((compare x1 x2 x3 x4) (if>=? (compare x1 x2) (if>=? (compare x1 x3) (if>=? (compare x1 x4) x1 x4) (if>=? (compare x3 x4) x3 x4)) (if>=? (compare x2 x3) (if>=? (compare x2 x4) x2 x4) (if>=? (compare x3 x4) x3 x4)))) ((compare x1 x2 . x3+) (let max ((xmax (if>=? (compare x1 x2) x1 x2)) (xs x3+)) (if (null? xs) xmax (max (if>=? (compare xmax (car xs)) xmax (car xs)) (cdr xs))))))) ; kth-largest (define kth-largest (let ((= =) (< <)) (case-lambda ((compare k x0) (case (modulo k 1) ((0) (compare:checked x0 compare x0)) (else (error "bad index" k)))) ((compare k x0 x1) (case (modulo k 2) ((0) (if<=? (compare x0 x1) x0 x1)) ((1) (if<=? (compare x0 x1) x1 x0)) (else (error "bad index" k)))) ((compare k x0 x1 x2) (case (modulo k 3) ((0) (if<=? (compare x0 x1) (if<=? (compare x0 x2) x0 x2) (if<=? (compare x1 x2) x1 x2))) ((1) (if3 (compare x0 x1) (if<=? (compare x1 x2) x1 (if<=? (compare x0 x2) x2 x0)) (if<=? (compare x0 x2) x1 x0) (if<=? (compare x0 x2) x0 (if<=? (compare x1 x2) x2 x1)))) ((2) (if<=? (compare x0 x1) (if<=? (compare x1 x2) x2 x1) (if<=? (compare x0 x2) x2 x0))) (else (error "bad index" k)))) ((compare k x0 . x1+) ; |x1+| >= 1 (if (not (and (integer? k) (exact? k))) (error "bad index" k)) (let ((n (+ 1 (length x1+)))) (let kth ((k (modulo k n)) (n n) ; = |x| (rev #t) ; are x<, x=, x> reversed? (x (cons x0 x1+))) (let ((pivot (list-ref x (random-integer n)))) (let split ((x x) (x< '()) (n< 0) (x= '()) (n= 0) (x> '()) (n> 0)) (if (null? x) (cond ((< k n<) (kth k n< (not rev) x<)) ((< k (+ n< n=)) (if rev (list-ref x= (- (- n= 1) (- k n<))) (list-ref x= (- k n<)))) (else (kth (- k (+ n< n=)) n> (not rev) x>))) (if3 (compare (car x) pivot) (split (cdr x) (cons (car x) x<) (+ n< 1) x= n= x> n>) (split (cdr x) x< n< (cons (car x) x=) (+ n= 1) x> n>) (split (cdr x) x< n< x= n= (cons (car x) x>) (+ n> 1)))))))))))) ; compare functions from predicates (define compare-by< (case-lambda ((lt) (lambda (x y) (if (lt x y) -1 (if (lt y x) 1 0)))) ((lt x y) (if (lt x y) -1 (if (lt y x) 1 0))))) (define compare-by> (case-lambda ((gt) (lambda (x y) (if (gt x y) 1 (if (gt y x) -1 0)))) ((gt x y) (if (gt x y) 1 (if (gt y x) -1 0))))) (define compare-by<= (case-lambda ((le) (lambda (x y) (if (le x y) (if (le y x) 0 -1) 1))) ((le x y) (if (le x y) (if (le y x) 0 -1) 1)))) (define compare-by>= (case-lambda ((ge) (lambda (x y) (if (ge x y) (if (ge y x) 0 1) -1))) ((ge x y) (if (ge x y) (if (ge y x) 0 1) -1)))) (define compare-by=/< (case-lambda ((eq lt) (lambda (x y) (if (eq x y) 0 (if (lt x y) -1 1)))) ((eq lt x y) (if (eq x y) 0 (if (lt x y) -1 1))))) (define compare-by=/> (case-lambda ((eq gt) (lambda (x y) (if (eq x y) 0 (if (gt x y) 1 -1)))) ((eq gt x y) (if (eq x y) 0 (if (gt x y) 1 -1))))) ; refine and extend construction (define-syntax refine-compare (syntax-rules () ((refine-compare) 0) ((refine-compare c1) c1) ((refine-compare c1 c2 cs ...) (if3 c1 -1 (refine-compare c2 cs ...) 1)))) (define-syntax select-compare (syntax-rules (else) ((select-compare x y clause ...) (let ((x-val x) (y-val y)) (select-compare (x-val y-val clause ...)))) ; used internally: (select-compare (x y clause ...)) ((select-compare (x y)) 0) ((select-compare (x y (else c ...))) (refine-compare c ...)) ((select-compare (x y (t? c ...) clause ...)) (let ((t?-val t?)) (let ((tx (t?-val x)) (ty (t?-val y))) (if tx (if ty (refine-compare c ...) -1) (if ty 1 (select-compare (x y clause ...))))))))) (define-syntax cond-compare (syntax-rules (else) ((cond-compare) 0) ((cond-compare (else cs ...)) (refine-compare cs ...)) ((cond-compare ((tx ty) cs ...) clause ...) (let ((tx-val tx) (ty-val ty)) (if tx-val (if ty-val (refine-compare cs ...) -1) (if ty-val 1 (cond-compare clause ...))))))) ; R5RS atomic types (define-syntax compare:type-check (syntax-rules () ((compare:type-check type? type-name x) (if (not (type? x)) (error (string-append "not " type-name ":") x))) ((compare:type-check type? type-name x y) (begin (compare:type-check type? type-name x) (compare:type-check type? type-name y))))) (define-syntax compare:define-by=/< (syntax-rules () ((compare:define-by=/< compare = < type? type-name) (define compare (let ((= =) (< <)) (lambda (x y) (if (type? x) (if (eq? x y) 0 (if (type? y) (if (= x y) 0 (if (< x y) -1 1)) (error (string-append "not " type-name ":") y))) (error (string-append "not " type-name ":") x)))))))) (define (boolean-compare x y) (compare:type-check boolean? "boolean" x y) (if x (if y 0 1) (if y -1 0))) (compare:define-by=/< char-compare char=? char<? char? "char") (compare:define-by=/< char-compare-ci char-ci=? char-ci<? char? "char") (compare:define-by=/< string-compare string=? string<? string? "string") (compare:define-by=/< string-compare-ci string-ci=? string-ci<? string? "string") (define (symbol-compare x y) (compare:type-check symbol? "symbol" x y) (string-compare (symbol->string x) (symbol->string y))) (compare:define-by=/< integer-compare = < integer? "integer") (compare:define-by=/< rational-compare = < rational? "rational") (compare:define-by=/< real-compare = < real? "real") (define (complex-compare x y) (compare:type-check complex? "complex" x y) (if (and (real? x) (real? y)) (real-compare x y) (refine-compare (real-compare (real-part x) (real-part y)) (real-compare (imag-part x) (imag-part y))))) (define (number-compare x y) (compare:type-check number? "number" x y) (complex-compare x y)) ; R5RS compound data structures: dotted pair, list, vector (define (pair-compare-car compare) (lambda (x y) (compare (car x) (car y)))) (define (pair-compare-cdr compare) (lambda (x y) (compare (cdr x) (cdr y)))) (define pair-compare (case-lambda ; dotted pair ((pair-compare-car pair-compare-cdr x y) (refine-compare (pair-compare-car (car x) (car y)) (pair-compare-cdr (cdr x) (cdr y)))) ; possibly improper lists ((compare x y) (cond-compare (((null? x) (null? y)) 0) (((pair? x) (pair? y)) (compare (car x) (car y)) (pair-compare compare (cdr x) (cdr y))) (else (compare x y)))) ; for convenience ((x y) (pair-compare default-compare x y)))) (define list-compare (case-lambda ((compare x y empty? head tail) (cond-compare (((empty? x) (empty? y)) 0) (else (compare (head x) (head y)) (list-compare compare (tail x) (tail y) empty? head tail)))) ; for convenience (( x y empty? head tail) (list-compare default-compare x y empty? head tail)) ((compare x y ) (list-compare compare x y null? car cdr)) (( x y ) (list-compare default-compare x y null? car cdr)))) (define list-compare-as-vector (case-lambda ((compare x y empty? head tail) (refine-compare (let compare-length ((x x) (y y)) (cond-compare (((empty? x) (empty? y)) 0) (else (compare-length (tail x) (tail y))))) (list-compare compare x y empty? head tail))) ; for convenience (( x y empty? head tail) (list-compare-as-vector default-compare x y empty? head tail)) ((compare x y ) (list-compare-as-vector compare x y null? car cdr)) (( x y ) (list-compare-as-vector default-compare x y null? car cdr)))) (define vector-compare (let ((= =)) (case-lambda ((compare x y size ref) (let ((n (size x)) (m (size y))) (refine-compare (integer-compare n m) (let compare-rest ((i 0)) ; compare x[i..n-1] y[i..n-1] (if (= i n) 0 (refine-compare (compare (ref x i) (ref y i)) (compare-rest (+ i 1)))))))) ; for convenience (( x y size ref) (vector-compare default-compare x y size ref)) ((compare x y ) (vector-compare compare x y vector-length vector-ref)) (( x y ) (vector-compare default-compare x y vector-length vector-ref))))) (define vector-compare-as-list (let ((= =)) (case-lambda ((compare x y size ref) (let ((nx (size x)) (ny (size y))) (let ((n (min nx ny))) (let compare-rest ((i 0)) ; compare x[i..n-1] y[i..n-1] (if (= i n) (integer-compare nx ny) (refine-compare (compare (ref x i) (ref y i)) (compare-rest (+ i 1)))))))) ; for convenience (( x y size ref) (vector-compare-as-list default-compare x y size ref)) ((compare x y ) (vector-compare-as-list compare x y vector-length vector-ref)) (( x y ) (vector-compare-as-list default-compare x y vector-length vector-ref))))) ; default compare (define (default-compare x y) (select-compare x y (null? 0) (pair? (default-compare (car x) (car y)) (default-compare (cdr x) (cdr y))) (boolean? (boolean-compare x y)) (char? (char-compare x y)) (string? (string-compare x y)) (symbol? (symbol-compare x y)) (number? (number-compare x y)) (vector? (vector-compare default-compare x y)) (else (error "unrecognized type in default-compare" x y)))) ; Note that we pass default-compare to compare-{pair,vector} explictly. ; This makes sure recursion proceeds with this default-compare, which ; need not be the one in the lexical scope of compare-{pair,vector}. ; debug compare (define (debug-compare c) (define (checked-value c x y) (let ((c-xy (c x y))) (if (or (eqv? c-xy -1) (eqv? c-xy 0) (eqv? c-xy 1)) c-xy (error "compare value not in {-1,0,1}" c-xy (list c x y))))) (define (random-boolean) (zero? (random-integer 2))) (define q ; (u v w) such that u <= v, v <= w, and not u <= w '#( ;x < y x = y x > y [x < z] 0 0 0 ; y < z 0 (z y x) (z y x) ; y = z 0 (z y x) (z y x) ; y > z ;x < y x = y x > y [x = z] (y z x) (z x y) 0 ; y < z (y z x) 0 (x z y) ; y = z 0 (y x z) (x z y) ; y > z ;x < y x = y x > y [x > z] (x y z) (x y z) 0 ; y < z (x y z) (x y z) 0 ; y = z 0 0 0 ; y > z )) (let ((z? #f) (z #f)) ; stored element from previous call (lambda (x y) (let ((c-xx (checked-value c x x)) (c-yy (checked-value c y y)) (c-xy (checked-value c x y)) (c-yx (checked-value c y x))) (if (not (zero? c-xx)) (error "compare error: not reflexive" c x)) (if (not (zero? c-yy)) (error "compare error: not reflexive" c y)) (if (not (zero? (+ c-xy c-yx))) (error "compare error: not anti-symmetric" c x y)) (if z? (let ((c-xz (checked-value c x z)) (c-zx (checked-value c z x)) (c-yz (checked-value c y z)) (c-zy (checked-value c z y))) (if (not (zero? (+ c-xz c-zx))) (error "compare error: not anti-symmetric" c x z)) (if (not (zero? (+ c-yz c-zy))) (error "compare error: not anti-symmetric" c y z)) (let ((ijk (vector-ref q (+ c-xy (* 3 c-yz) (* 9 c-xz) 13)))) (if (list? ijk) (apply error "compare error: not transitive" c (map (lambda (i) (case i ((x) x) ((y) y) ((z) z))) ijk))))) (set! z? #t)) (set! z (if (random-boolean) x y)) ; randomized testing c-xy)))) ) |
Added srfi/s67/compare-procedures.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s67 compare-procedures) (export </<=? </<? <=/<=? <=/<? <=? <? =? >/>=? >/>? >=/>=? >=/>? >=? >? boolean-compare chain<=? chain<? chain=? chain>=? chain>? char-compare char-compare-ci compare-by< compare-by<= compare-by=/< compare-by=/> compare-by> compare-by>= complex-compare cond-compare debug-compare default-compare if-not=? if3 if<=? if<? if=? if>=? if>? integer-compare kth-largest list-compare list-compare-as-vector max-compare min-compare not=? number-compare pair-compare pair-compare-car pair-compare-cdr pairwise-not=? rational-compare real-compare refine-compare select-compare string-compare string-compare-ci symbol-compare vector-compare vector-compare-as-list) (import (except (rnrs) error) (rnrs r5rs) ; for modulo (srfi s27 random-bits) ; for random-integer (srfi s23 error) ;; (srfi s23 error tricks) (srfi private include)) (define (default-compare x y) (select-compare x y (null? 0) (pair? (default-compare (car x) (car y)) (default-compare (cdr x) (cdr y))) (boolean? (boolean-compare x y)) (char? (char-compare x y)) (string? (string-compare x y)) (symbol? (symbol-compare x y)) (number? (number-compare x y)) (vector? (vector-compare default-compare x y)) (else (error "unrecognized type in default-compare" x y)))) ;; (SRFI-23-error->R6RS "(library (srfi s67 compare-procedures))" ;; (include/resolve ("srfi" "s67") "compare.ss")) ; (define current-compare (make-parameter default-compare)) ; (provide current-compare) ; Copyright (c) 2005 Sebastian Egner and Jens Axel S{\o}gaard. ; ; Permission is hereby granted, free of charge, to any person obtaining ; a copy of this software and associated documentation files (the ; ``Software''), to deal in the Software without restriction, including ; without limitation the rights to use, copy, modify, merge, publish, ; distribute, sublicense, and/or sell copies of the Software, and to ; permit persons to whom the Software is furnished to do so, subject to ; the following conditions: ; ; The above copyright notice and this permission notice shall be ; included in all copies or substantial portions of the Software. ; ; THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, ; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION ; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION ; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ; ; ----------------------------------------------------------------------- ; ; Compare procedures SRFI (reference implementation) ; Sebastian.Egner@philips.com, Jensaxel@soegaard.net ; history of this file: ; SE, 14-Oct-2004: first version ; SE, 18-Oct-2004: 1st redesign: axioms for 'compare function' ; SE, 29-Oct-2004: 2nd redesign: higher order reverse/map/refine/unite ; SE, 2-Nov-2004: 3rd redesign: macros cond/refine-compare replace h.o.f's ; SE, 10-Nov-2004: (im,re) replaced by (re,im) in complex-compare ; SE, 11-Nov-2004: case-compare by case (not by cond); select-compare added ; SE, 12-Jan-2005: pair-compare-cdr ; SE, 15-Feb-2005: stricter typing for compare-<type>; pairwise-not=? ; SE, 16-Feb-2005: case-compare -> if-compare -> if3; <? </<? chain<? etc. ; JS, 24-Feb-2005: selection-compare added ; SE, 25-Feb-2005: selection-compare -> kth-largest modified; if<? etc. ; JS, 28-Feb-2005: kth-largest modified - is "stable" now ; SE, 28-Feb-2005: simplified pairwise-not=?/kth-largest; min/max debugged ; SE, 07-Apr-2005: compare-based type checks made explicit ; SE, 18-Apr-2005: added (rel? compare) and eq?-test ; SE, 16-May-2005: naming convention changed; compare-by< etc. optional x y ; ============================================================================= ; Reference Implementation ; ======================== ; ; in R5RS (including hygienic macros) ; + SRFI-16 (case-lambda) ; + SRFI-23 (error) ; + SRFI-27 (random-integer) ; Implementation remarks: ; * In general, the emphasis of this implementation is on correctness ; and portability, not on efficiency. ; * Variable arity procedures are expressed in terms of case-lambda ; in the hope that this will produce efficient code for the case ; where the arity is statically known at the call site. ; * In procedures that are required to type-check their arguments, ; we use (compare x x) for executing extra checks. This relies on ; the assumption that eq? is used to catch this case quickly. ; * Care has been taken to reference comparison procedures of R5RS ; only at the time the operations here are being defined. This ; makes it possible to redefine these operations, if need be. ; * For the sake of efficiency, some inlining has been done by hand. ; This is mainly expressed by macros producing defines. ; * Identifiers of the form compare:<something> are private. ; ; Hints for low-level implementation: ; * The basis of this SRFI are the atomic compare procedures, ; i.e. boolean-compare, char-compare, etc. and the conditionals ; if3, if=?, if<? etc., and default-compare. These should make ; optimal use of the available type information. ; * For the sake of speed, the reference implementation does not ; use a LET to save the comparison value c for the ERROR call. ; This can be fixed in a low-level implementation at no cost. ; * Type-checks based on (compare x x) are made explicit by the ; expression (compare:check result compare x ...). ; * Eq? should can used to speed up built-in compare procedures, ; but it can only be used after type-checking at least one of ; the arguments. (define (compare:checked result compare . args) (for-each (lambda (x) (compare x x)) args) result) ; 3-sided conditional (define-syntax if3 (syntax-rules () ((if3 c less equal greater) (case c ((-1) less) (( 0) equal) (( 1) greater) (else (error "comparison value not in {-1,0,1}")))))) ; 2-sided conditionals for comparisons (define-syntax compare:if-rel? (syntax-rules () ((compare:if-rel? c-cases a-cases c consequence) (compare:if-rel? c-cases a-cases c consequence (if #f #f))) ((compare:if-rel? c-cases a-cases c consequence alternate) (case c (c-cases consequence) (a-cases alternate) (else (error "comparison value not in {-1,0,1}")))))) (define-syntax if=? (syntax-rules () ((if=? arg ...) (compare:if-rel? (0) (-1 1) arg ...)))) (define-syntax if<? (syntax-rules () ((if<? arg ...) (compare:if-rel? (-1) (0 1) arg ...)))) (define-syntax if>? (syntax-rules () ((if>? arg ...) (compare:if-rel? (1) (-1 0) arg ...)))) (define-syntax if<=? (syntax-rules () ((if<=? arg ...) (compare:if-rel? (-1 0) (1) arg ...)))) (define-syntax if>=? (syntax-rules () ((if>=? arg ...) (compare:if-rel? (0 1) (-1) arg ...)))) (define-syntax if-not=? (syntax-rules () ((if-not=? arg ...) (compare:if-rel? (-1 1) (0) arg ...)))) ; predicates from compare procedures (define-syntax compare:define-rel? (syntax-rules () ((compare:define-rel? rel? if-rel?) (define rel? (case-lambda (() (lambda (x y) (if-rel? (default-compare x y) #t #f))) ((compare) (lambda (x y) (if-rel? (compare x y) #t #f))) ((x y) (if-rel? (default-compare x y) #t #f)) ((compare x y) (if (procedure? compare) (if-rel? (compare x y) #t #f) (error "not a procedure (Did you mean rel/rel??): " compare)))))))) (compare:define-rel? =? if=?) (compare:define-rel? <? if<?) (compare:define-rel? >? if>?) (compare:define-rel? <=? if<=?) (compare:define-rel? >=? if>=?) (compare:define-rel? not=? if-not=?) ; chains of length 3 (define-syntax compare:define-rel1/rel2? (syntax-rules () ((compare:define-rel1/rel2? rel1/rel2? if-rel1? if-rel2?) (define rel1/rel2? (case-lambda (() (lambda (x y z) (if-rel1? (default-compare x y) (if-rel2? (default-compare y z) #t #f) (compare:checked #f default-compare z)))) ((compare) (lambda (x y z) (if-rel1? (compare x y) (if-rel2? (compare y z) #t #f) (compare:checked #f compare z)))) ((x y z) (if-rel1? (default-compare x y) (if-rel2? (default-compare y z) #t #f) (compare:checked #f default-compare z))) ((compare x y z) (if-rel1? (compare x y) (if-rel2? (compare y z) #t #f) (compare:checked #f compare z)))))))) (compare:define-rel1/rel2? </<? if<? if<?) (compare:define-rel1/rel2? </<=? if<? if<=?) (compare:define-rel1/rel2? <=/<? if<=? if<?) (compare:define-rel1/rel2? <=/<=? if<=? if<=?) (compare:define-rel1/rel2? >/>? if>? if>?) (compare:define-rel1/rel2? >/>=? if>? if>=?) (compare:define-rel1/rel2? >=/>? if>=? if>?) (compare:define-rel1/rel2? >=/>=? if>=? if>=?) ; chains of arbitrary length (define-syntax compare:define-chain-rel? (syntax-rules () ((compare:define-chain-rel? chain-rel? if-rel?) (define chain-rel? (case-lambda ((compare) #t) ((compare x1) (compare:checked #t compare x1)) ((compare x1 x2) (if-rel? (compare x1 x2) #t #f)) ((compare x1 x2 x3) (if-rel? (compare x1 x2) (if-rel? (compare x2 x3) #t #f) (compare:checked #f compare x3))) ((compare x1 x2 . x3+) (if-rel? (compare x1 x2) (let chain? ((head x2) (tail x3+)) (if (null? tail) #t (if-rel? (compare head (car tail)) (chain? (car tail) (cdr tail)) (apply compare:checked #f compare (cdr tail))))) (apply compare:checked #f compare x3+)))))))) (compare:define-chain-rel? chain=? if=?) (compare:define-chain-rel? chain<? if<?) (compare:define-chain-rel? chain>? if>?) (compare:define-chain-rel? chain<=? if<=?) (compare:define-chain-rel? chain>=? if>=?) ; pairwise inequality (define pairwise-not=? (let ((= =) (<= <=)) (case-lambda ((compare) #t) ((compare x1) (compare:checked #t compare x1)) ((compare x1 x2) (if-not=? (compare x1 x2) #t #f)) ((compare x1 x2 x3) (if-not=? (compare x1 x2) (if-not=? (compare x2 x3) (if-not=? (compare x1 x3) #t #f) #f) (compare:checked #f compare x3))) ((compare . x1+) (let unequal? ((x x1+) (n (length x1+)) (unchecked? #t)) (if (< n 2) (if (and unchecked? (= n 1)) (compare:checked #t compare (car x)) #t) (let* ((i-pivot (random-integer n)) (x-pivot (list-ref x i-pivot))) (let split ((i 0) (x x) (x< '()) (x> '())) (if (null? x) (and (unequal? x< (length x<) #f) (unequal? x> (length x>) #f)) (if (= i i-pivot) (split (+ i 1) (cdr x) x< x>) (if3 (compare (car x) x-pivot) (split (+ i 1) (cdr x) (cons (car x) x<) x>) (if unchecked? (apply compare:checked #f compare (cdr x)) #f) (split (+ i 1) (cdr x) x< (cons (car x) x>))))))))))))) ; min/max (define min-compare (case-lambda ((compare x1) (compare:checked x1 compare x1)) ((compare x1 x2) (if<=? (compare x1 x2) x1 x2)) ((compare x1 x2 x3) (if<=? (compare x1 x2) (if<=? (compare x1 x3) x1 x3) (if<=? (compare x2 x3) x2 x3))) ((compare x1 x2 x3 x4) (if<=? (compare x1 x2) (if<=? (compare x1 x3) (if<=? (compare x1 x4) x1 x4) (if<=? (compare x3 x4) x3 x4)) (if<=? (compare x2 x3) (if<=? (compare x2 x4) x2 x4) (if<=? (compare x3 x4) x3 x4)))) ((compare x1 x2 . x3+) (let min ((xmin (if<=? (compare x1 x2) x1 x2)) (xs x3+)) (if (null? xs) xmin (min (if<=? (compare xmin (car xs)) xmin (car xs)) (cdr xs))))))) (define max-compare (case-lambda ((compare x1) (compare:checked x1 compare x1)) ((compare x1 x2) (if>=? (compare x1 x2) x1 x2)) ((compare x1 x2 x3) (if>=? (compare x1 x2) (if>=? (compare x1 x3) x1 x3) (if>=? (compare x2 x3) x2 x3))) ((compare x1 x2 x3 x4) (if>=? (compare x1 x2) (if>=? (compare x1 x3) (if>=? (compare x1 x4) x1 x4) (if>=? (compare x3 x4) x3 x4)) (if>=? (compare x2 x3) (if>=? (compare x2 x4) x2 x4) (if>=? (compare x3 x4) x3 x4)))) ((compare x1 x2 . x3+) (let max ((xmax (if>=? (compare x1 x2) x1 x2)) (xs x3+)) (if (null? xs) xmax (max (if>=? (compare xmax (car xs)) xmax (car xs)) (cdr xs))))))) ; kth-largest (define kth-largest (let ((= =) (< <)) (case-lambda ((compare k x0) (case (modulo k 1) ((0) (compare:checked x0 compare x0)) (else (error "bad index" k)))) ((compare k x0 x1) (case (modulo k 2) ((0) (if<=? (compare x0 x1) x0 x1)) ((1) (if<=? (compare x0 x1) x1 x0)) (else (error "bad index" k)))) ((compare k x0 x1 x2) (case (modulo k 3) ((0) (if<=? (compare x0 x1) (if<=? (compare x0 x2) x0 x2) (if<=? (compare x1 x2) x1 x2))) ((1) (if3 (compare x0 x1) (if<=? (compare x1 x2) x1 (if<=? (compare x0 x2) x2 x0)) (if<=? (compare x0 x2) x1 x0) (if<=? (compare x0 x2) x0 (if<=? (compare x1 x2) x2 x1)))) ((2) (if<=? (compare x0 x1) (if<=? (compare x1 x2) x2 x1) (if<=? (compare x0 x2) x2 x0))) (else (error "bad index" k)))) ((compare k x0 . x1+) ; |x1+| >= 1 (if (not (and (integer? k) (exact? k))) (error "bad index" k)) (let ((n (+ 1 (length x1+)))) (let kth ((k (modulo k n)) (n n) ; = |x| (rev #t) ; are x<, x=, x> reversed? (x (cons x0 x1+))) (let ((pivot (list-ref x (random-integer n)))) (let split ((x x) (x< '()) (n< 0) (x= '()) (n= 0) (x> '()) (n> 0)) (if (null? x) (cond ((< k n<) (kth k n< (not rev) x<)) ((< k (+ n< n=)) (if rev (list-ref x= (- (- n= 1) (- k n<))) (list-ref x= (- k n<)))) (else (kth (- k (+ n< n=)) n> (not rev) x>))) (if3 (compare (car x) pivot) (split (cdr x) (cons (car x) x<) (+ n< 1) x= n= x> n>) (split (cdr x) x< n< (cons (car x) x=) (+ n= 1) x> n>) (split (cdr x) x< n< x= n= (cons (car x) x>) (+ n> 1)))))))))))) ; compare functions from predicates (define compare-by< (case-lambda ((lt) (lambda (x y) (if (lt x y) -1 (if (lt y x) 1 0)))) ((lt x y) (if (lt x y) -1 (if (lt y x) 1 0))))) (define compare-by> (case-lambda ((gt) (lambda (x y) (if (gt x y) 1 (if (gt y x) -1 0)))) ((gt x y) (if (gt x y) 1 (if (gt y x) -1 0))))) (define compare-by<= (case-lambda ((le) (lambda (x y) (if (le x y) (if (le y x) 0 -1) 1))) ((le x y) (if (le x y) (if (le y x) 0 -1) 1)))) (define compare-by>= (case-lambda ((ge) (lambda (x y) (if (ge x y) (if (ge y x) 0 1) -1))) ((ge x y) (if (ge x y) (if (ge y x) 0 1) -1)))) (define compare-by=/< (case-lambda ((eq lt) (lambda (x y) (if (eq x y) 0 (if (lt x y) -1 1)))) ((eq lt x y) (if (eq x y) 0 (if (lt x y) -1 1))))) (define compare-by=/> (case-lambda ((eq gt) (lambda (x y) (if (eq x y) 0 (if (gt x y) 1 -1)))) ((eq gt x y) (if (eq x y) 0 (if (gt x y) 1 -1))))) ; refine and extend construction (define-syntax refine-compare (syntax-rules () ((refine-compare) 0) ((refine-compare c1) c1) ((refine-compare c1 c2 cs ...) (if3 c1 -1 (refine-compare c2 cs ...) 1)))) (define-syntax select-compare (syntax-rules (else) ((select-compare x y clause ...) (let ((x-val x) (y-val y)) (select-compare (x-val y-val clause ...)))) ; used internally: (select-compare (x y clause ...)) ((select-compare (x y)) 0) ((select-compare (x y (else c ...))) (refine-compare c ...)) ((select-compare (x y (t? c ...) clause ...)) (let ((t?-val t?)) (let ((tx (t?-val x)) (ty (t?-val y))) (if tx (if ty (refine-compare c ...) -1) (if ty 1 (select-compare (x y clause ...))))))))) (define-syntax cond-compare (syntax-rules (else) ((cond-compare) 0) ((cond-compare (else cs ...)) (refine-compare cs ...)) ((cond-compare ((tx ty) cs ...) clause ...) (let ((tx-val tx) (ty-val ty)) (if tx-val (if ty-val (refine-compare cs ...) -1) (if ty-val 1 (cond-compare clause ...))))))) ; R5RS atomic types (define-syntax compare:type-check (syntax-rules () ((compare:type-check type? type-name x) (if (not (type? x)) (error (string-append "not " type-name ":") x))) ((compare:type-check type? type-name x y) (begin (compare:type-check type? type-name x) (compare:type-check type? type-name y))))) (define-syntax compare:define-by=/< (syntax-rules () ((compare:define-by=/< compare = < type? type-name) (define compare (let ((= =) (< <)) (lambda (x y) (if (type? x) (if (eq? x y) 0 (if (type? y) (if (= x y) 0 (if (< x y) -1 1)) (error (string-append "not " type-name ":") y))) (error (string-append "not " type-name ":") x)))))))) (define (boolean-compare x y) (compare:type-check boolean? "boolean" x y) (if x (if y 0 1) (if y -1 0))) (compare:define-by=/< char-compare char=? char<? char? "char") (compare:define-by=/< char-compare-ci char-ci=? char-ci<? char? "char") (compare:define-by=/< string-compare string=? string<? string? "string") (compare:define-by=/< string-compare-ci string-ci=? string-ci<? string? "string") (define (symbol-compare x y) (compare:type-check symbol? "symbol" x y) (string-compare (symbol->string x) (symbol->string y))) (compare:define-by=/< integer-compare = < integer? "integer") (compare:define-by=/< rational-compare = < rational? "rational") (compare:define-by=/< real-compare = < real? "real") (define (complex-compare x y) (compare:type-check complex? "complex" x y) (if (and (real? x) (real? y)) (real-compare x y) (refine-compare (real-compare (real-part x) (real-part y)) (real-compare (imag-part x) (imag-part y))))) (define (number-compare x y) (compare:type-check number? "number" x y) (complex-compare x y)) ; R5RS compound data structures: dotted pair, list, vector (define (pair-compare-car compare) (lambda (x y) (compare (car x) (car y)))) (define (pair-compare-cdr compare) (lambda (x y) (compare (cdr x) (cdr y)))) (define pair-compare (case-lambda ; dotted pair ((pair-compare-car pair-compare-cdr x y) (refine-compare (pair-compare-car (car x) (car y)) (pair-compare-cdr (cdr x) (cdr y)))) ; possibly improper lists ((compare x y) (cond-compare (((null? x) (null? y)) 0) (((pair? x) (pair? y)) (compare (car x) (car y)) (pair-compare compare (cdr x) (cdr y))) (else (compare x y)))) ; for convenience ((x y) (pair-compare default-compare x y)))) (define list-compare (case-lambda ((compare x y empty? head tail) (cond-compare (((empty? x) (empty? y)) 0) (else (compare (head x) (head y)) (list-compare compare (tail x) (tail y) empty? head tail)))) ; for convenience (( x y empty? head tail) (list-compare default-compare x y empty? head tail)) ((compare x y ) (list-compare compare x y null? car cdr)) (( x y ) (list-compare default-compare x y null? car cdr)))) (define list-compare-as-vector (case-lambda ((compare x y empty? head tail) (refine-compare (let compare-length ((x x) (y y)) (cond-compare (((empty? x) (empty? y)) 0) (else (compare-length (tail x) (tail y))))) (list-compare compare x y empty? head tail))) ; for convenience (( x y empty? head tail) (list-compare-as-vector default-compare x y empty? head tail)) ((compare x y ) (list-compare-as-vector compare x y null? car cdr)) (( x y ) (list-compare-as-vector default-compare x y null? car cdr)))) (define vector-compare (let ((= =)) (case-lambda ((compare x y size ref) (let ((n (size x)) (m (size y))) (refine-compare (integer-compare n m) (let compare-rest ((i 0)) ; compare x[i..n-1] y[i..n-1] (if (= i n) 0 (refine-compare (compare (ref x i) (ref y i)) (compare-rest (+ i 1)))))))) ; for convenience (( x y size ref) (vector-compare default-compare x y size ref)) ((compare x y ) (vector-compare compare x y vector-length vector-ref)) (( x y ) (vector-compare default-compare x y vector-length vector-ref))))) (define vector-compare-as-list (let ((= =)) (case-lambda ((compare x y size ref) (let ((nx (size x)) (ny (size y))) (let ((n (min nx ny))) (let compare-rest ((i 0)) ; compare x[i..n-1] y[i..n-1] (if (= i n) (integer-compare nx ny) (refine-compare (compare (ref x i) (ref y i)) (compare-rest (+ i 1)))))))) ; for convenience (( x y size ref) (vector-compare-as-list default-compare x y size ref)) ((compare x y ) (vector-compare-as-list compare x y vector-length vector-ref)) (( x y ) (vector-compare-as-list default-compare x y vector-length vector-ref))))) ; default compare ;; (define (default-compare x y) ;; (select-compare ;; x y ;; (null? 0) ;; (pair? (default-compare (car x) (car y)) ;; (default-compare (cdr x) (cdr y))) ;; (boolean? (boolean-compare x y)) ;; (char? (char-compare x y)) ;; (string? (string-compare x y)) ;; (symbol? (symbol-compare x y)) ;; (number? (number-compare x y)) ;; (vector? (vector-compare default-compare x y)) ;; (else (error "unrecognized type in default-compare" x y)))) ; Note that we pass default-compare to compare-{pair,vector} explictly. ; This makes sure recursion proceeds with this default-compare, which ; need not be the one in the lexical scope of compare-{pair,vector}. ; debug compare (define (debug-compare c) (define (checked-value c x y) (let ((c-xy (c x y))) (if (or (eqv? c-xy -1) (eqv? c-xy 0) (eqv? c-xy 1)) c-xy (error "compare value not in {-1,0,1}" c-xy (list c x y))))) (define (random-boolean) (zero? (random-integer 2))) (define q ; (u v w) such that u <= v, v <= w, and not u <= w '#( ;x < y x = y x > y [x < z] 0 0 0 ; y < z 0 (z y x) (z y x) ; y = z 0 (z y x) (z y x) ; y > z ;x < y x = y x > y [x = z] (y z x) (z x y) 0 ; y < z (y z x) 0 (x z y) ; y = z 0 (y x z) (x z y) ; y > z ;x < y x = y x > y [x > z] (x y z) (x y z) 0 ; y < z (x y z) (x y z) 0 ; y = z 0 0 0 ; y > z )) (let ((z? #f) (z #f)) ; stored element from previous call (lambda (x y) (let ((c-xx (checked-value c x x)) (c-yy (checked-value c y y)) (c-xy (checked-value c x y)) (c-yx (checked-value c y x))) (if (not (zero? c-xx)) (error "compare error: not reflexive" c x)) (if (not (zero? c-yy)) (error "compare error: not reflexive" c y)) (if (not (zero? (+ c-xy c-yx))) (error "compare error: not anti-symmetric" c x y)) (if z? (let ((c-xz (checked-value c x z)) (c-zx (checked-value c z x)) (c-yz (checked-value c y z)) (c-zy (checked-value c z y))) (if (not (zero? (+ c-xz c-zx))) (error "compare error: not anti-symmetric" c x z)) (if (not (zero? (+ c-yz c-zy))) (error "compare error: not anti-symmetric" c y z)) (let ((ijk (vector-ref q (+ c-xy (* 3 c-yz) (* 9 c-xz) 13)))) (if (list? ijk) (apply error "compare error: not transitive" c (map (lambda (i) (case i ((x) x) ((y) y) ((z) z))) ijk))))) (set! z? #t)) (set! z (if (random-boolean) x y)) ; randomized testing c-xy)))) ) |
Added srfi/s67/compare.ss.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 |
; (define current-compare (make-parameter default-compare)) ; (provide current-compare) ; Copyright (c) 2005 Sebastian Egner and Jens Axel S{\o}gaard. ; ; Permission is hereby granted, free of charge, to any person obtaining ; a copy of this software and associated documentation files (the ; ``Software''), to deal in the Software without restriction, including ; without limitation the rights to use, copy, modify, merge, publish, ; distribute, sublicense, and/or sell copies of the Software, and to ; permit persons to whom the Software is furnished to do so, subject to ; the following conditions: ; ; The above copyright notice and this permission notice shall be ; included in all copies or substantial portions of the Software. ; ; THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, ; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION ; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION ; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ; ; ----------------------------------------------------------------------- ; ; Compare procedures SRFI (reference implementation) ; Sebastian.Egner@philips.com, Jensaxel@soegaard.net ; history of this file: ; SE, 14-Oct-2004: first version ; SE, 18-Oct-2004: 1st redesign: axioms for 'compare function' ; SE, 29-Oct-2004: 2nd redesign: higher order reverse/map/refine/unite ; SE, 2-Nov-2004: 3rd redesign: macros cond/refine-compare replace h.o.f's ; SE, 10-Nov-2004: (im,re) replaced by (re,im) in complex-compare ; SE, 11-Nov-2004: case-compare by case (not by cond); select-compare added ; SE, 12-Jan-2005: pair-compare-cdr ; SE, 15-Feb-2005: stricter typing for compare-<type>; pairwise-not=? ; SE, 16-Feb-2005: case-compare -> if-compare -> if3; <? </<? chain<? etc. ; JS, 24-Feb-2005: selection-compare added ; SE, 25-Feb-2005: selection-compare -> kth-largest modified; if<? etc. ; JS, 28-Feb-2005: kth-largest modified - is "stable" now ; SE, 28-Feb-2005: simplified pairwise-not=?/kth-largest; min/max debugged ; SE, 07-Apr-2005: compare-based type checks made explicit ; SE, 18-Apr-2005: added (rel? compare) and eq?-test ; SE, 16-May-2005: naming convention changed; compare-by< etc. optional x y ; ============================================================================= ; Reference Implementation ; ======================== ; ; in R5RS (including hygienic macros) ; + SRFI-16 (case-lambda) ; + SRFI-23 (error) ; + SRFI-27 (random-integer) ; Implementation remarks: ; * In general, the emphasis of this implementation is on correctness ; and portability, not on efficiency. ; * Variable arity procedures are expressed in terms of case-lambda ; in the hope that this will produce efficient code for the case ; where the arity is statically known at the call site. ; * In procedures that are required to type-check their arguments, ; we use (compare x x) for executing extra checks. This relies on ; the assumption that eq? is used to catch this case quickly. ; * Care has been taken to reference comparison procedures of R5RS ; only at the time the operations here are being defined. This ; makes it possible to redefine these operations, if need be. ; * For the sake of efficiency, some inlining has been done by hand. ; This is mainly expressed by macros producing defines. ; * Identifiers of the form compare:<something> are private. ; ; Hints for low-level implementation: ; * The basis of this SRFI are the atomic compare procedures, ; i.e. boolean-compare, char-compare, etc. and the conditionals ; if3, if=?, if<? etc., and default-compare. These should make ; optimal use of the available type information. ; * For the sake of speed, the reference implementation does not ; use a LET to save the comparison value c for the ERROR call. ; This can be fixed in a low-level implementation at no cost. ; * Type-checks based on (compare x x) are made explicit by the ; expression (compare:check result compare x ...). ; * Eq? should can used to speed up built-in compare procedures, ; but it can only be used after type-checking at least one of ; the arguments. (define (compare:checked result compare . args) (for-each (lambda (x) (compare x x)) args) result) ; 3-sided conditional (define-syntax if3 (syntax-rules () ((if3 c less equal greater) (case c ((-1) less) (( 0) equal) (( 1) greater) (else (error "comparison value not in {-1,0,1}")))))) ; 2-sided conditionals for comparisons (define-syntax compare:if-rel? (syntax-rules () ((compare:if-rel? c-cases a-cases c consequence) (compare:if-rel? c-cases a-cases c consequence (if #f #f))) ((compare:if-rel? c-cases a-cases c consequence alternate) (case c (c-cases consequence) (a-cases alternate) (else (error "comparison value not in {-1,0,1}")))))) (define-syntax if=? (syntax-rules () ((if=? arg ...) (compare:if-rel? (0) (-1 1) arg ...)))) (define-syntax if<? (syntax-rules () ((if<? arg ...) (compare:if-rel? (-1) (0 1) arg ...)))) (define-syntax if>? (syntax-rules () ((if>? arg ...) (compare:if-rel? (1) (-1 0) arg ...)))) (define-syntax if<=? (syntax-rules () ((if<=? arg ...) (compare:if-rel? (-1 0) (1) arg ...)))) (define-syntax if>=? (syntax-rules () ((if>=? arg ...) (compare:if-rel? (0 1) (-1) arg ...)))) (define-syntax if-not=? (syntax-rules () ((if-not=? arg ...) (compare:if-rel? (-1 1) (0) arg ...)))) ; predicates from compare procedures (define-syntax compare:define-rel? (syntax-rules () ((compare:define-rel? rel? if-rel?) (define rel? (case-lambda (() (lambda (x y) (if-rel? (default-compare x y) #t #f))) ((compare) (lambda (x y) (if-rel? (compare x y) #t #f))) ((x y) (if-rel? (default-compare x y) #t #f)) ((compare x y) (if (procedure? compare) (if-rel? (compare x y) #t #f) (error "not a procedure (Did you mean rel/rel??): " compare)))))))) (compare:define-rel? =? if=?) (compare:define-rel? <? if<?) (compare:define-rel? >? if>?) (compare:define-rel? <=? if<=?) (compare:define-rel? >=? if>=?) (compare:define-rel? not=? if-not=?) ; chains of length 3 (define-syntax compare:define-rel1/rel2? (syntax-rules () ((compare:define-rel1/rel2? rel1/rel2? if-rel1? if-rel2?) (define rel1/rel2? (case-lambda (() (lambda (x y z) (if-rel1? (default-compare x y) (if-rel2? (default-compare y z) #t #f) (compare:checked #f default-compare z)))) ((compare) (lambda (x y z) (if-rel1? (compare x y) (if-rel2? (compare y z) #t #f) (compare:checked #f compare z)))) ((x y z) (if-rel1? (default-compare x y) (if-rel2? (default-compare y z) #t #f) (compare:checked #f default-compare z))) ((compare x y z) (if-rel1? (compare x y) (if-rel2? (compare y z) #t #f) (compare:checked #f compare z)))))))) (compare:define-rel1/rel2? </<? if<? if<?) (compare:define-rel1/rel2? </<=? if<? if<=?) (compare:define-rel1/rel2? <=/<? if<=? if<?) (compare:define-rel1/rel2? <=/<=? if<=? if<=?) (compare:define-rel1/rel2? >/>? if>? if>?) (compare:define-rel1/rel2? >/>=? if>? if>=?) (compare:define-rel1/rel2? >=/>? if>=? if>?) (compare:define-rel1/rel2? >=/>=? if>=? if>=?) ; chains of arbitrary length (define-syntax compare:define-chain-rel? (syntax-rules () ((compare:define-chain-rel? chain-rel? if-rel?) (define chain-rel? (case-lambda ((compare) #t) ((compare x1) (compare:checked #t compare x1)) ((compare x1 x2) (if-rel? (compare x1 x2) #t #f)) ((compare x1 x2 x3) (if-rel? (compare x1 x2) (if-rel? (compare x2 x3) #t #f) (compare:checked #f compare x3))) ((compare x1 x2 . x3+) (if-rel? (compare x1 x2) (let chain? ((head x2) (tail x3+)) (if (null? tail) #t (if-rel? (compare head (car tail)) (chain? (car tail) (cdr tail)) (apply compare:checked #f compare (cdr tail))))) (apply compare:checked #f compare x3+)))))))) (compare:define-chain-rel? chain=? if=?) (compare:define-chain-rel? chain<? if<?) (compare:define-chain-rel? chain>? if>?) (compare:define-chain-rel? chain<=? if<=?) (compare:define-chain-rel? chain>=? if>=?) ; pairwise inequality (define pairwise-not=? (let ((= =) (<= <=)) (case-lambda ((compare) #t) ((compare x1) (compare:checked #t compare x1)) ((compare x1 x2) (if-not=? (compare x1 x2) #t #f)) ((compare x1 x2 x3) (if-not=? (compare x1 x2) (if-not=? (compare x2 x3) (if-not=? (compare x1 x3) #t #f) #f) (compare:checked #f compare x3))) ((compare . x1+) (let unequal? ((x x1+) (n (length x1+)) (unchecked? #t)) (if (< n 2) (if (and unchecked? (= n 1)) (compare:checked #t compare (car x)) #t) (let* ((i-pivot (random-integer n)) (x-pivot (list-ref x i-pivot))) (let split ((i 0) (x x) (x< '()) (x> '())) (if (null? x) (and (unequal? x< (length x<) #f) (unequal? x> (length x>) #f)) (if (= i i-pivot) (split (+ i 1) (cdr x) x< x>) (if3 (compare (car x) x-pivot) (split (+ i 1) (cdr x) (cons (car x) x<) x>) (if unchecked? (apply compare:checked #f compare (cdr x)) #f) (split (+ i 1) (cdr x) x< (cons (car x) x>))))))))))))) ; min/max (define min-compare (case-lambda ((compare x1) (compare:checked x1 compare x1)) ((compare x1 x2) (if<=? (compare x1 x2) x1 x2)) ((compare x1 x2 x3) (if<=? (compare x1 x2) (if<=? (compare x1 x3) x1 x3) (if<=? (compare x2 x3) x2 x3))) ((compare x1 x2 x3 x4) (if<=? (compare x1 x2) (if<=? (compare x1 x3) (if<=? (compare x1 x4) x1 x4) (if<=? (compare x3 x4) x3 x4)) (if<=? (compare x2 x3) (if<=? (compare x2 x4) x2 x4) (if<=? (compare x3 x4) x3 x4)))) ((compare x1 x2 . x3+) (let min ((xmin (if<=? (compare x1 x2) x1 x2)) (xs x3+)) (if (null? xs) xmin (min (if<=? (compare xmin (car xs)) xmin (car xs)) (cdr xs))))))) (define max-compare (case-lambda ((compare x1) (compare:checked x1 compare x1)) ((compare x1 x2) (if>=? (compare x1 x2) x1 x2)) ((compare x1 x2 x3) (if>=? (compare x1 x2) (if>=? (compare x1 x3) x1 x3) (if>=? (compare x2 x3) x2 x3))) ((compare x1 x2 x3 x4) (if>=? (compare x1 x2) (if>=? (compare x1 x3) (if>=? (compare x1 x4) x1 x4) (if>=? (compare x3 x4) x3 x4)) (if>=? (compare x2 x3) (if>=? (compare x2 x4) x2 x4) (if>=? (compare x3 x4) x3 x4)))) ((compare x1 x2 . x3+) (let max ((xmax (if>=? (compare x1 x2) x1 x2)) (xs x3+)) (if (null? xs) xmax (max (if>=? (compare xmax (car xs)) xmax (car xs)) (cdr xs))))))) ; kth-largest (define kth-largest (let ((= =) (< <)) (case-lambda ((compare k x0) (case (modulo k 1) ((0) (compare:checked x0 compare x0)) (else (error "bad index" k)))) ((compare k x0 x1) (case (modulo k 2) ((0) (if<=? (compare x0 x1) x0 x1)) ((1) (if<=? (compare x0 x1) x1 x0)) (else (error "bad index" k)))) ((compare k x0 x1 x2) (case (modulo k 3) ((0) (if<=? (compare x0 x1) (if<=? (compare x0 x2) x0 x2) (if<=? (compare x1 x2) x1 x2))) ((1) (if3 (compare x0 x1) (if<=? (compare x1 x2) x1 (if<=? (compare x0 x2) x2 x0)) (if<=? (compare x0 x2) x1 x0) (if<=? (compare x0 x2) x0 (if<=? (compare x1 x2) x2 x1)))) ((2) (if<=? (compare x0 x1) (if<=? (compare x1 x2) x2 x1) (if<=? (compare x0 x2) x2 x0))) (else (error "bad index" k)))) ((compare k x0 . x1+) ; |x1+| >= 1 (if (not (and (integer? k) (exact? k))) (error "bad index" k)) (let ((n (+ 1 (length x1+)))) (let kth ((k (modulo k n)) (n n) ; = |x| (rev #t) ; are x<, x=, x> reversed? (x (cons x0 x1+))) (let ((pivot (list-ref x (random-integer n)))) (let split ((x x) (x< '()) (n< 0) (x= '()) (n= 0) (x> '()) (n> 0)) (if (null? x) (cond ((< k n<) (kth k n< (not rev) x<)) ((< k (+ n< n=)) (if rev (list-ref x= (- (- n= 1) (- k n<))) (list-ref x= (- k n<)))) (else (kth (- k (+ n< n=)) n> (not rev) x>))) (if3 (compare (car x) pivot) (split (cdr x) (cons (car x) x<) (+ n< 1) x= n= x> n>) (split (cdr x) x< n< (cons (car x) x=) (+ n= 1) x> n>) (split (cdr x) x< n< x= n= (cons (car x) x>) (+ n> 1)))))))))))) ; compare functions from predicates (define compare-by< (case-lambda ((lt) (lambda (x y) (if (lt x y) -1 (if (lt y x) 1 0)))) ((lt x y) (if (lt x y) -1 (if (lt y x) 1 0))))) (define compare-by> (case-lambda ((gt) (lambda (x y) (if (gt x y) 1 (if (gt y x) -1 0)))) ((gt x y) (if (gt x y) 1 (if (gt y x) -1 0))))) (define compare-by<= (case-lambda ((le) (lambda (x y) (if (le x y) (if (le y x) 0 -1) 1))) ((le x y) (if (le x y) (if (le y x) 0 -1) 1)))) (define compare-by>= (case-lambda ((ge) (lambda (x y) (if (ge x y) (if (ge y x) 0 1) -1))) ((ge x y) (if (ge x y) (if (ge y x) 0 1) -1)))) (define compare-by=/< (case-lambda ((eq lt) (lambda (x y) (if (eq x y) 0 (if (lt x y) -1 1)))) ((eq lt x y) (if (eq x y) 0 (if (lt x y) -1 1))))) (define compare-by=/> (case-lambda ((eq gt) (lambda (x y) (if (eq x y) 0 (if (gt x y) 1 -1)))) ((eq gt x y) (if (eq x y) 0 (if (gt x y) 1 -1))))) ; refine and extend construction (define-syntax refine-compare (syntax-rules () ((refine-compare) 0) ((refine-compare c1) c1) ((refine-compare c1 c2 cs ...) (if3 c1 -1 (refine-compare c2 cs ...) 1)))) (define-syntax select-compare (syntax-rules (else) ((select-compare x y clause ...) (let ((x-val x) (y-val y)) (select-compare (x-val y-val clause ...)))) ; used internally: (select-compare (x y clause ...)) ((select-compare (x y)) 0) ((select-compare (x y (else c ...))) (refine-compare c ...)) ((select-compare (x y (t? c ...) clause ...)) (let ((t?-val t?)) (let ((tx (t?-val x)) (ty (t?-val y))) (if tx (if ty (refine-compare c ...) -1) (if ty 1 (select-compare (x y clause ...))))))))) (define-syntax cond-compare (syntax-rules (else) ((cond-compare) 0) ((cond-compare (else cs ...)) (refine-compare cs ...)) ((cond-compare ((tx ty) cs ...) clause ...) (let ((tx-val tx) (ty-val ty)) (if tx-val (if ty-val (refine-compare cs ...) -1) (if ty-val 1 (cond-compare clause ...))))))) ; R5RS atomic types (define-syntax compare:type-check (syntax-rules () ((compare:type-check type? type-name x) (if (not (type? x)) (error (string-append "not " type-name ":") x))) ((compare:type-check type? type-name x y) (begin (compare:type-check type? type-name x) (compare:type-check type? type-name y))))) (define-syntax compare:define-by=/< (syntax-rules () ((compare:define-by=/< compare = < type? type-name) (define compare (let ((= =) (< <)) (lambda (x y) (if (type? x) (if (eq? x y) 0 (if (type? y) (if (= x y) 0 (if (< x y) -1 1)) (error (string-append "not " type-name ":") y))) (error (string-append "not " type-name ":") x)))))))) (define (boolean-compare x y) (compare:type-check boolean? "boolean" x y) (if x (if y 0 1) (if y -1 0))) (compare:define-by=/< char-compare char=? char<? char? "char") (compare:define-by=/< char-compare-ci char-ci=? char-ci<? char? "char") (compare:define-by=/< string-compare string=? string<? string? "string") (compare:define-by=/< string-compare-ci string-ci=? string-ci<? string? "string") (define (symbol-compare x y) (compare:type-check symbol? "symbol" x y) (string-compare (symbol->string x) (symbol->string y))) (compare:define-by=/< integer-compare = < integer? "integer") (compare:define-by=/< rational-compare = < rational? "rational") (compare:define-by=/< real-compare = < real? "real") (define (complex-compare x y) (compare:type-check complex? "complex" x y) (if (and (real? x) (real? y)) (real-compare x y) (refine-compare (real-compare (real-part x) (real-part y)) (real-compare (imag-part x) (imag-part y))))) (define (number-compare x y) (compare:type-check number? "number" x y) (complex-compare x y)) ; R5RS compound data structures: dotted pair, list, vector (define (pair-compare-car compare) (lambda (x y) (compare (car x) (car y)))) (define (pair-compare-cdr compare) (lambda (x y) (compare (cdr x) (cdr y)))) (define pair-compare (case-lambda ; dotted pair ((pair-compare-car pair-compare-cdr x y) (refine-compare (pair-compare-car (car x) (car y)) (pair-compare-cdr (cdr x) (cdr y)))) ; possibly improper lists ((compare x y) (cond-compare (((null? x) (null? y)) 0) (((pair? x) (pair? y)) (compare (car x) (car y)) (pair-compare compare (cdr x) (cdr y))) (else (compare x y)))) ; for convenience ((x y) (pair-compare default-compare x y)))) (define list-compare (case-lambda ((compare x y empty? head tail) (cond-compare (((empty? x) (empty? y)) 0) (else (compare (head x) (head y)) (list-compare compare (tail x) (tail y) empty? head tail)))) ; for convenience (( x y empty? head tail) (list-compare default-compare x y empty? head tail)) ((compare x y ) (list-compare compare x y null? car cdr)) (( x y ) (list-compare default-compare x y null? car cdr)))) (define list-compare-as-vector (case-lambda ((compare x y empty? head tail) (refine-compare (let compare-length ((x x) (y y)) (cond-compare (((empty? x) (empty? y)) 0) (else (compare-length (tail x) (tail y))))) (list-compare compare x y empty? head tail))) ; for convenience (( x y empty? head tail) (list-compare-as-vector default-compare x y empty? head tail)) ((compare x y ) (list-compare-as-vector compare x y null? car cdr)) (( x y ) (list-compare-as-vector default-compare x y null? car cdr)))) (define vector-compare (let ((= =)) (case-lambda ((compare x y size ref) (let ((n (size x)) (m (size y))) (refine-compare (integer-compare n m) (let compare-rest ((i 0)) ; compare x[i..n-1] y[i..n-1] (if (= i n) 0 (refine-compare (compare (ref x i) (ref y i)) (compare-rest (+ i 1)))))))) ; for convenience (( x y size ref) (vector-compare default-compare x y size ref)) ((compare x y ) (vector-compare compare x y vector-length vector-ref)) (( x y ) (vector-compare default-compare x y vector-length vector-ref))))) (define vector-compare-as-list (let ((= =)) (case-lambda ((compare x y size ref) (let ((nx (size x)) (ny (size y))) (let ((n (min nx ny))) (let compare-rest ((i 0)) ; compare x[i..n-1] y[i..n-1] (if (= i n) (integer-compare nx ny) (refine-compare (compare (ref x i) (ref y i)) (compare-rest (+ i 1)))))))) ; for convenience (( x y size ref) (vector-compare-as-list default-compare x y size ref)) ((compare x y ) (vector-compare-as-list compare x y vector-length vector-ref)) (( x y ) (vector-compare-as-list default-compare x y vector-length vector-ref))))) ; default compare (define (default-compare x y) (select-compare x y (null? 0) (pair? (default-compare (car x) (car y)) (default-compare (cdr x) (cdr y))) (boolean? (boolean-compare x y)) (char? (char-compare x y)) (string? (string-compare x y)) (symbol? (symbol-compare x y)) (number? (number-compare x y)) (vector? (vector-compare default-compare x y)) (else (error "unrecognized type in default-compare" x y)))) ; Note that we pass default-compare to compare-{pair,vector} explictly. ; This makes sure recursion proceeds with this default-compare, which ; need not be the one in the lexical scope of compare-{pair,vector}. ; debug compare (define (debug-compare c) (define (checked-value c x y) (let ((c-xy (c x y))) (if (or (eqv? c-xy -1) (eqv? c-xy 0) (eqv? c-xy 1)) c-xy (error "compare value not in {-1,0,1}" c-xy (list c x y))))) (define (random-boolean) (zero? (random-integer 2))) (define q ; (u v w) such that u <= v, v <= w, and not u <= w '#( ;x < y x = y x > y [x < z] 0 0 0 ; y < z 0 (z y x) (z y x) ; y = z 0 (z y x) (z y x) ; y > z ;x < y x = y x > y [x = z] (y z x) (z x y) 0 ; y < z (y z x) 0 (x z y) ; y = z 0 (y x z) (x z y) ; y > z ;x < y x = y x > y [x > z] (x y z) (x y z) 0 ; y < z (x y z) (x y z) 0 ; y = z 0 0 0 ; y > z )) (let ((z? #f) (z #f)) ; stored element from previous call (lambda (x y) (let ((c-xx (checked-value c x x)) (c-yy (checked-value c y y)) (c-xy (checked-value c x y)) (c-yx (checked-value c y x))) (if (not (zero? c-xx)) (error "compare error: not reflexive" c x)) (if (not (zero? c-yy)) (error "compare error: not reflexive" c y)) (if (not (zero? (+ c-xy c-yx))) (error "compare error: not anti-symmetric" c x y)) (if z? (let ((c-xz (checked-value c x z)) (c-zx (checked-value c z x)) (c-yz (checked-value c y z)) (c-zy (checked-value c z y))) (if (not (zero? (+ c-xz c-zx))) (error "compare error: not anti-symmetric" c x z)) (if (not (zero? (+ c-yz c-zy))) (error "compare error: not anti-symmetric" c y z)) (let ((ijk (vector-ref q (+ c-xy (* 3 c-yz) (* 9 c-xz) 13)))) (if (list? ijk) (apply error "compare error: not transitive" c (map (lambda (i) (case i ((x) x) ((y) y) ((z) z))) ijk))))) (set! z? #t)) (set! z (if (random-boolean) x y)) ; randomized testing c-xy)))) |
Added srfi/s69/basic-hash-tables.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
#!r6rs ;; Copyright (C) 2009 Andreas Rottmann. All rights reserved. Licensed ;; under an MIT-style license. See the file LICENSE in the original ;; collection this file is distributed with. (library (srfi s69 basic-hash-tables) (export ;; Type constructors and predicate make-hash-table hash-table? alist->hash-table ;; Reflective queries hash-table-equivalence-function hash-table-hash-function ;; Dealing with single elements hash-table-ref hash-table-ref/default hash-table-set! hash-table-delete! hash-table-exists? hash-table-update! hash-table-update!/default ;; Dealing with the whole contents hash-table-size hash-table-keys hash-table-values hash-table-walk hash-table-fold hash-table->alist hash-table-copy hash-table-merge! ;; Hashing hash string-hash string-ci-hash hash-by-identity) (import (rename (rnrs) (string-hash rnrs:string-hash) (string-ci-hash rnrs:string-ci-hash))) (define make-hash-table (case-lambda ((eql? hash) (make-hashtable hash eql?)) ((eql?) (cond ((eq? eql? eq?) (make-eq-hashtable)) ((eq? eql? eqv?) (make-eqv-hashtable)) ((eq? eql? equal?) (make-hashtable equal-hash eql?)) ((eq? eql? string=?) (make-hashtable rnrs:string-hash eql?)) ((eq? eql? string-ci=?) (make-hashtable rnrs:string-ci-hash eql?)) (else (assertion-violation 'make-hash-table "unrecognized equivalence predicate" eql?)))) (() (make-hashtable equal-hash equal?)))) (define hash-table? hashtable?) (define not-there (list 'not-there)) (define (alist->hash-table alist . args) (let ((table (apply make-hash-table args))) (for-each (lambda (entry) (hashtable-update! table (car entry) (lambda (x) (if (eq? x not-there) (cdr entry) x)) not-there)) alist) table)) (define hash-table-equivalence-function hashtable-equivalence-function) (define hash-table-hash-function hashtable-hash-function) (define (failure-thunk who key) (lambda () (assertion-violation who "no association for key" key))) (define hash-table-ref (case-lambda ((table key thunk) (let ((val (hashtable-ref table key not-there))) (if (eq? val not-there) (thunk) val))) ((table key) (hash-table-ref table key (failure-thunk 'hash-table-ref key))))) (define hash-table-ref/default hashtable-ref) (define hash-table-set! hashtable-set!) (define hash-table-delete! hashtable-delete!) (define hash-table-exists? hashtable-contains?) (define hash-table-update! (case-lambda ((table key proc thunk) (hashtable-update! table key (lambda (val) (if (eq? val not-there) (thunk) (proc val))) not-there)) ((table key proc) (hash-table-update! table key proc (failure-thunk 'hash-table-update! key))))) (define hash-table-update!/default hashtable-update!) (define hash-table-size hashtable-size) (define (hash-table-keys table) (vector->list (hashtable-keys table))) (define (hash-table-values table) (let-values (((keys values) (hashtable-entries table))) (vector->list values))) (define (hash-table-walk table proc) (let-values (((keys values) (hashtable-entries table))) (vector-for-each proc keys values))) (define (hash-table-fold table kons knil) (let-values (((keys values) (hashtable-entries table))) (let ((size (vector-length keys))) (let loop ((i 0) (val knil)) (if (>= i size) val (loop (+ i 1) (kons (vector-ref keys i) (vector-ref values i) val))))))) (define (hash-table->alist table) (hash-table-fold table (lambda (k v l) (cons (cons k v) l)) '())) (define hash-table-copy hashtable-copy) (define (hash-table-merge! table1 table2) (hash-table-walk table2 (lambda (k v) (hashtable-set! table1 k v))) table1) (define (make-hasher hash-proc) (case-lambda ((obj) ;; R6RS doesn't guarantee that the result of the hash procedure ;; is non-negative, so we use mod. (mod (hash-proc obj) (greatest-fixnum))) ((obj bound) (mod (hash-proc obj) bound)))) (define hash (make-hasher equal-hash)) (define hash-by-identity (make-hasher equal-hash)) ;; Very slow. (define string-hash (make-hasher rnrs:string-hash)) (define string-ci-hash (make-hasher rnrs:string-ci-hash)) ) |
Added srfi/s78/check.scm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
; <PLAINTEXT> ; Copyright (c) 2005-2006 Sebastian Egner. ; ; Permission is hereby granted, free of charge, to any person obtaining ; a copy of this software and associated documentation files (the ; ``Software''), to deal in the Software without restriction, including ; without limitation the rights to use, copy, modify, merge, publish, ; distribute, sublicense, and/or sell copies of the Software, and to ; permit persons to whom the Software is furnished to do so, subject to ; the following conditions: ; ; The above copyright notice and this permission notice shall be ; included in all copies or substantial portions of the Software. ; ; THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, ; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION ; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION ; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ; ; ----------------------------------------------------------------------- ; Modified by Derick Eddington to print things a little differently. ; ; Lightweight testing (reference implementation) ; ============================================== ; ; Sebastian.Egner@philips.com ; in R5RS + SRFI 23 (error) + SRFI 42 (comprehensions) ; ; history of this file: ; SE, 25-Oct-2004: first version based on code used in SRFIs 42 and 67 ; SE, 19-Jan-2006: (arg ...) made optional in check-ec ; ; Naming convention "check:<identifier>" is used only internally. ; -- portability -- ; PLT: (require (lib "23.ss" "srfi") (lib "42.ss" "srfi")) ; Scheme48: ,open srfi-23 srfi-42 ; -- utilities -- #;(define check:write write) (define (print/header/padded x header padding) (define (print/lines) (let* ((str (call-with-string-output-port (lambda (sop) (check:write x sop)))) (sip (open-string-input-port str))) (let loop ((lines '())) (let ((l (get-line sip))) (if (eof-object? l) (reverse lines) (loop (cons l lines))))))) (let ((lines (print/lines))) (display header) (display (car lines)) (let loop ((lines (cdr lines))) (unless (null? lines) (newline) (display padding) (display (car lines)) (loop (cdr lines)))))) ; You can also use a pretty printer if you have one. ; However, the output might not improve for most cases ; because the pretty printers usually output a trailing ; newline. ; PLT: (require (lib "pretty.ss")) (define check:write pretty-print) ; Scheme48: ,open pp (define check:write p) ; -- mode -- (define check:mode (make-parameter 'report (lambda (v) (case v ((off) 0) ((summary) 1) ((report-failed) 10) ((report) 100) (else (error "unrecognized mode" v)))))) (define (check-set-mode! mode) (check:mode mode)) ; -- state -- (define check:correct 0) (define check:failed '()) (define (check-reset!) (set! check:correct 0) (set! check:failed '())) (define (check:add-correct!) (set! check:correct (+ check:correct 1))) (define (check:add-failed! expression actual-result expected-result pred) (set! check:failed (cons (list expression actual-result expected-result pred) check:failed))) ; -- reporting -- (define (check:report-expression expression pred) (newline) (check:write expression) (if pred (begin (print/header/padded pred "(=> " " ") (display ")\n")) (display "=>\n"))) (define (check:report-actual-result actual-result) (check:write actual-result)) (define (check:report-correct cases) (display "; correct") (if (not (= cases 1)) (begin (display " (") (display cases) (display " cases checked)"))) (newline)) (define (check:report-failed expected-result) (display "; *** failed ***\n") (print/header/padded expected-result "; expected result: " "; ") (newline)) (define (check-report) (if (>= (check:mode) 1) (begin (newline) (display "; *** checks *** : ") (display check:correct) (display " correct, ") (display (length check:failed)) (display " failed.") (if (or (null? check:failed) (<= (check:mode) 1)) (newline) (let* ((w (car (reverse check:failed))) (expression (car w)) (actual-result (cadr w)) (expected-result (caddr w)) (pred (cadddr w))) (display " First failed example:") (newline) (check:report-expression expression pred) (check:report-actual-result actual-result) (check:report-failed expected-result)))))) (define (check-passed? expected-total-count) (and (= (length check:failed) 0) (= check:correct expected-total-count))) ; -- simple checks -- (define (check:proc expression thunk equal equal-expr expected-result) (case (check:mode) ((0) #f) ((1) (let ((actual-result (thunk))) (if (equal actual-result expected-result) (check:add-correct!) (check:add-failed! expression actual-result expected-result equal-expr)))) ((10) (let ((actual-result (thunk))) (if (equal actual-result expected-result) (check:add-correct!) (begin (check:report-expression expression equal-expr) (check:report-actual-result actual-result) (check:report-failed expected-result) (check:add-failed! expression actual-result expected-result equal-expr))))) ((100) (check:report-expression expression equal-expr) (let ((actual-result (thunk))) (check:report-actual-result actual-result) (if (equal actual-result expected-result) (begin (check:report-correct 1) (check:add-correct!)) (begin (check:report-failed expected-result) (check:add-failed! expression actual-result expected-result equal-expr))))) (else (error "unrecognized check:mode" (check:mode)))) (if #f #f)) (define-syntax check (syntax-rules (=>) ((check expr => expected) (if (>= (check:mode) 1) (check:proc 'expr (lambda () expr) equal? #F expected))) ((check expr (=> equal) expected) (if (>= (check:mode) 1) (check:proc 'expr (lambda () expr) equal 'equal expected))))) ; -- parametric checks -- (define (check:proc-ec w) (let ((correct? (car w)) (expression (cadr w)) (actual-result (caddr w)) (expected-result (cadddr w)) (cases (car (cddddr w))) (equal-expr (cadr (cddddr w)))) (if correct? (begin (if (>= (check:mode) 100) (begin (check:report-expression expression equal-expr) (check:report-actual-result actual-result) (check:report-correct cases))) (check:add-correct!)) (begin (if (>= (check:mode) 10) (begin (check:report-expression expression equal-expr) (check:report-actual-result actual-result) (check:report-failed expected-result))) (check:add-failed! expression actual-result expected-result equal-expr))))) (define-syntax check-ec:make (syntax-rules (=>) ((check-ec:make qualifiers expr (=> equal) expected (arg ...)) (if (>= (check:mode) 1) (check:proc-ec (let ((cases 0)) (let ((w (first-ec #f qualifiers (:let equal-pred equal) (:let expected-result expected) (:let actual-result (let ((arg arg) ...) ; (*) expr)) (begin (set! cases (+ cases 1))) (if (not (equal-pred actual-result expected-result))) (list (list 'let (list (list 'arg arg) ...) 'expr) actual-result expected-result cases 'equal)))) (if w (cons #f w) (list #t '(check-ec qualifiers expr (=> equal) expected (arg ...)) (if #f #f) (if #f #f) cases 'equal))))))))) ; (*) is a compile-time check that (arg ...) is a list ; of pairwise disjoint bound variables at this point. (define-syntax check-ec (syntax-rules (nested =>) ((check-ec expr => expected) (check-ec:make (nested) expr (=> equal?) expected ())) ((check-ec expr (=> equal) expected) (check-ec:make (nested) expr (=> equal) expected ())) ((check-ec expr => expected (arg ...)) (check-ec:make (nested) expr (=> equal?) expected (arg ...))) ((check-ec expr (=> equal) expected (arg ...)) (check-ec:make (nested) expr (=> equal) expected (arg ...))) ((check-ec qualifiers expr => expected) (check-ec:make qualifiers expr (=> equal?) expected ())) ((check-ec qualifiers expr (=> equal) expected) (check-ec:make qualifiers expr (=> equal) expected ())) ((check-ec qualifiers expr => expected (arg ...)) (check-ec:make qualifiers expr (=> equal?) expected (arg ...))) ((check-ec qualifiers expr (=> equal) expected (arg ...)) (check-ec:make qualifiers expr (=> equal) expected (arg ...))) ((check-ec (nested q1 ...) q etc ...) (check-ec (nested q1 ... q) etc ...)) ((check-ec q1 q2 etc ...) (check-ec (nested q1 q2) etc ...)))) |
Added srfi/s78/lightweight-testing.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s78 lightweight-testing) (export check check-ec check-report check-set-mode! check-reset! check-passed?) (import (rnrs) (srfi s78 lightweight-testing compat) (srfi s39 parameters) (srfi private include) (srfi s23 error tricks) (srfi s42 eager-comprehensions)) ;; (SRFI-23-error->R6RS "(library (srfi s78 lightweight-testing))" ;; (include/resolve ("srfi" "%3a78") "check.scm")) (SRFI-23-error->R6RS "(library (srfi s78 lightweight-testing))" (include/resolve ("srfi" "s78") "check.scm")) ) |
Added srfi/s78/lightweight-testing/compat.chezscheme.sls.
> > > > > > > > > |
1 2 3 4 5 6 7 8 9 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi s78 lightweight-testing compat) (export (rename (pretty-print check:write))) (import (chezscheme))) |
Added srfi/s78/lightweight-testing/compat.ikarus.sls.
> > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi s78 lightweight-testing compat) (export (rename (pretty-print check:write))) (import (only (ikarus) pretty-print)) ) |
Added srfi/s78/lightweight-testing/compat.larceny.sls.
> > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi s78 lightweight-testing compat) (export (rename (pretty-print check:write))) (import (primitives pretty-print)) ) |
Added srfi/s78/lightweight-testing/compat.mosh.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
;; Copyright (c) 2009 Derick Eddington ;; ;; Permission is hereby granted, free of charge, to any person obtaining a ;; copy of this software and associated documentation files (the "Software"), ;; to deal in the Software without restriction, including without limitation ;; the rights to use, copy, modify, merge, publish, distribute, sublicense, ;; and/or sell copies of the Software, and to permit persons to whom the ;; Software is furnished to do so, subject to the following conditions: ;; ;; The above copyright notice and this permission notice shall be included in ;; all copies or substantial portions of the Software. ;; ;; Except as contained in this notice, the name(s) of the above copyright ;; holders shall not be used in advertising or otherwise to promote the sale, ;; use or other dealings in this Software without prior written authorization. ;; ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ;; DEALINGS IN THE SOFTWARE. (library (srfi s78 lightweight-testing compat) (export (rename (pretty-print check:write))) (import (only (mosh pp) pretty-print))) |
Added srfi/s78/lightweight-testing/compat.mzscheme.sls.
> > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s78 lightweight-testing compat) (export (rename (pretty-print check:write))) (import (only (scheme pretty) pretty-print)) ) |
Added srfi/s78/lightweight-testing/compat.ypsilon.sls.
> > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. (library (srfi s78 lightweight-testing compat) (export check:write) (import (rnrs) (only (core) pretty-print)) (define check:write (case-lambda ((x) (check:write x (current-output-port))) ((x p) (pretty-print x p) (newline p)))) ) |
Added srfi/s8/receive.sls.
> > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s8 receive) (export receive) (import (rnrs)) (define-syntax receive (syntax-rules () [(_ formals expression b b* ...) (call-with-values (lambda () expression) (lambda formals b b* ...))])) ) |
Added srfi/s9/records.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. #!r6rs (library (srfi s9 records) (export (rename (srfi:define-record-type define-record-type))) (import (rnrs)) (define-syntax srfi:define-record-type (lambda (stx) (syntax-case stx () [(_ type (constructor constructor-tag ...) predicate (field-tag accessor setter ...) ...) (and (for-all identifier? #'(type constructor predicate constructor-tag ... field-tag ... accessor ...)) (for-all (lambda (s) (or (and (= 1 (length s)) (identifier? (car s))) (= 0 (length s)))) #'((setter ...) ...)) (for-all (lambda (ct) (memp (lambda (ft) (bound-identifier=? ct ft)) #'(field-tag ...))) #'(constructor-tag ...))) (with-syntax ([(field-clause ...) (map (lambda (clause) (if (= 2 (length clause)) #`(immutable . #,clause) #`(mutable . #,clause))) #'((field-tag accessor setter ...) ...))] [(unspec-tag ...) (remp (lambda (ft) (memp (lambda (ct) (bound-identifier=? ft ct)) #'(constructor-tag ...))) #'(field-tag ...))]) #'(define-record-type (type constructor predicate) (sealed #t) (protocol (lambda (ctor) (lambda (constructor-tag ...) (define unspec-tag) ... (ctor field-tag ...)))) (fields field-clause ...)))]))) ) |
Added srfi/s98/os-environment-variables.chezscheme.sls.
> > > > > |
1 2 3 4 5 |
(library (srfi s98 os-environment-variables) (export (rename (getenv get-environment-variable) ;;(... get-environment-variables) )) (import (chezscheme))) |
Added srfi/s98/os-environment-variables.ikarus.sls.
> > > > > > |
1 2 3 4 5 6 |
(library (srfi s98 os-environment-variables) (export (rename (getenv get-environment-variable) (environ get-environment-variables))) (import (only (ikarus) getenv environ))) |
Added srfi/s98/os-environment-variables.larceny.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. ;; NOTE: I believe this currently works only on Linux. ;; NOTE: If Larceny's FFI changes, this may no longer work. (library (srfi s98 os-environment-variables) (export get-environment-variable get-environment-variables) (import (rnrs base) (rnrs control) (rnrs bytevectors) (rnrs io ports) (primitives foreign-procedure #;foreign-variable foreign-null-pointer? sizeof:pointer %peek-pointer %peek8u void*->address ffi/dlopen ffi/dlsym) (srfi private feature-cond)) ;; TODO: Will the convenient string converters use the native transcoder in ;; the future? So that scheme-str->c-str-bv and c-str-ptr->scheme-str ;; won't be needed. (define (scheme-str->c-str-bv x) (let* ((bv (string->bytevector x (native-transcoder))) (len (bytevector-length bv)) (bv/z (make-bytevector (+ 1 len)))) (bytevector-copy! bv 0 bv/z 0 len) (bytevector-u8-set! bv/z len 0) bv/z)) (define (c-str-ptr->scheme-str x) (let loop ((x x) (a '())) (let ((b (%peek8u x))) (if (zero? b) (bytevector->string (u8-list->bytevector (reverse a)) (native-transcoder)) (loop (+ 1 x) (cons b a)))))) (define getenv (foreign-procedure "getenv" '(boxed) 'void*)) (define (get-environment-variable name) (unless (string? name) (assertion-violation 'get-environment-variable "not a string" name)) (let ((p (getenv (scheme-str->c-str-bv name)))) (and p (c-str-ptr->scheme-str (void*->address p))))) ;; TODO: Will foreign-variable support a pointer type in the future? ;; Would this be the correct way to use it? #;(define environ (foreign-variable "environ" 'void*)) ;; TODO: Is (ffi/dlopen "") okay? It works for me on Ubuntu Linux 8.10. (define environ (feature-cond (linux (%peek-pointer (ffi/dlsym (ffi/dlopen "") "environ"))))) (define (get-environment-variables) (define (entry->pair x) (let* ((s (c-str-ptr->scheme-str x)) (len (string-length s))) (let loop ((i 0)) (if (< i len) (if (char=? #\= (string-ref s i)) (cons (substring s 0 i) (substring s (+ 1 i) len)) (loop (+ 1 i))) (cons s #F))))) (let loop ((e environ) (a '())) (let ((entry (%peek-pointer e))) (if (foreign-null-pointer? entry) a (loop (+ sizeof:pointer e) (cons (entry->pair entry) a)))))) ) |
Added srfi/s98/os-environment-variables.mzscheme.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. ;; Licensed under an MIT-style license. My license is in the file ;; named LICENSE from the original collection this file is distributed ;; with. If this file is redistributed with some other collection, my ;; license must also be included. ;; Inspired by Danny Yoo's get-environment PLaneT package. #!r6rs (library (srfi s98 os-environment-variables) (export (rename (getenv get-environment-variable)) get-environment-variables) (import (rnrs base) (only (scheme base) getenv) (scheme foreign)) (unsafe!) (define environ (get-ffi-obj "environ" (ffi-lib #F) _pointer)) (define (get-environment-variables) (let loop ((i 0) (accum '())) (let ((next (ptr-ref environ _string/locale i))) (if next (loop (+ 1 i) (cons (let loop ((i 0) (len (string-length next))) (if (< i len) (if (char=? #\= (string-ref next i)) (cons (substring next 0 i) (substring next (+ 1 i) len)) (loop (+ 1 i) len)) (cons next #F))) accum)) accum)))) ) |
Added srfi/s98/os-environment-variables.ypsilon.sls.
> > > > > > |
1 2 3 4 5 6 |
(library (srfi s98 os-environment-variables) (export (rename (lookup-process-environment get-environment-variable) (process-environment->alist get-environment-variables))) (import (only (core) lookup-process-environment process-environment->alist))) |
Added srfi/s99/records.sls.
> > > > > > > > > |
1 2 3 4 5 6 7 8 9 |
(library (srfi s99 records) (export make-rtd rtd? rtd-constructor rtd-predicate rtd-accessor rtd-mutator record? record-rtd rtd-name rtd-parent rtd-field-names rtd-all-field-names rtd-field-mutable? define-record-type) (import (srfi s99 records procedural) (srfi s99 records inspection) (srfi s99 records syntactic))) |
Added srfi/s99/records/helper.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
;; Copyright (C) William D Clinger 2008. All Rights Reserved. ;; ;; Permission is hereby granted, free of charge, to any person obtaining a copy ;; of this software and associated documentation files (the "Software"), to deal ;; in the Software without restriction, including without limitation the rights ;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ;; copies of the Software, and to permit persons to whom the Software is ;; furnished to do so, subject to the following conditions: ;; ;; The above copyright notice and this permission notice shall be included in ;; all copies or substantial portions of the Software. ;; ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. REMEMBER, THERE IS NO ;; SCHEME UNDERGROUND. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ;; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF ;; CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ; This library breaks a circular interdependence between the ; procedural and inspection libraries. #!r6rs (library (srfi s99 records helper) (export rtd?) (import (rnrs base) (rnrs records procedural)) (define rtd? record-type-descriptor?) ) |
Added srfi/s99/records/inspection.larceny.sls.
> > > > > |
1 2 3 4 5 |
(library (srfi s99 records inspection) (export record? record-rtd rtd-name rtd-parent rtd-field-names rtd-all-field-names rtd-field-mutable?) (import (err5rs records inspection))) |
Added srfi/s99/records/inspection.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
;; Copyright (C) William D Clinger 2008. All Rights Reserved. ;; ;; Permission is hereby granted, free of charge, to any person obtaining a copy ;; of this software and associated documentation files (the "Software"), to deal ;; in the Software without restriction, including without limitation the rights ;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ;; copies of the Software, and to permit persons to whom the Software is ;; furnished to do so, subject to the following conditions: ;; ;; The above copyright notice and this permission notice shall be included in ;; all copies or substantial portions of the Software. ;; ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. REMEMBER, THERE IS NO ;; SCHEME UNDERGROUND. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ;; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF ;; CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #!r6rs (library (srfi s99 records inspection) (export record? record-rtd rtd-name rtd-parent rtd-field-names rtd-all-field-names rtd-field-mutable?) (import (rnrs base) (rnrs lists) (rnrs records inspection) (srfi s99 records helper)) ; The record? predicate is already defined by (rnrs records inspection). ; The record-rtd procedure is already defined by (rnrs records inspection). (define rtd-name record-type-name) (define rtd-parent record-type-parent) (define rtd-field-names record-type-field-names) (define (rtd-all-field-names rtd) (define (loop rtd othernames) (let ((parent (rtd-parent rtd)) (names (append (vector->list (rtd-field-names rtd)) othernames))) (if parent (loop parent names) (list->vector names)))) (loop rtd '())) (define (rtd-field-mutable? rtd0 fieldname) (define (loop rtd) (if (rtd? rtd) (let* ((names (vector->list (rtd-field-names rtd))) (probe (memq fieldname names))) (if probe (record-field-mutable? rtd (- (length names) (length probe))) (loop (rtd-parent rtd)))) (assertion-violation 'rtd-field-mutable? "illegal argument" rtd0 fieldname))) (loop rtd0)) ) |
Added srfi/s99/records/procedural.larceny.sls.
> > > > |
1 2 3 4 |
(library (srfi s99 records procedural) (export make-rtd rtd? rtd-constructor rtd-predicate rtd-accessor rtd-mutator) (import (err5rs records procedural))) |
Added srfi/s99/records/procedural.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
;; Copyright (C) William D Clinger 2008. All Rights Reserved. ;; ;; Permission is hereby granted, free of charge, to any person obtaining a copy ;; of this software and associated documentation files (the "Software"), to deal ;; in the Software without restriction, including without limitation the rights ;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ;; copies of the Software, and to permit persons to whom the Software is ;; furnished to do so, subject to the following conditions: ;; ;; The above copyright notice and this permission notice shall be included in ;; all copies or substantial portions of the Software. ;; ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. REMEMBER, THERE IS NO ;; SCHEME UNDERGROUND. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ;; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF ;; CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ERR5RS Records. ; ; This is a quick-and-dirty reference implementation that favors ; simplicity over quality error messages and performance. It is ; implemented using the R6RS procedural and inspection layers, ; with which it interoperates nicely. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #!r6rs (library (srfi s99 records procedural) (export make-rtd rtd? rtd-constructor rtd-predicate rtd-accessor rtd-mutator) (import (rnrs base) (rnrs lists) (rnrs records procedural) (srfi s99 records inspection)) ; Note: the options are permitted by ERR5RS, ; but are not part of ERR5RS. (define (make-rtd name fieldspecs . rest) (let* ((parent (if (null? rest) #f (car rest))) (options (if (null? rest) '() (cdr rest))) (sealed? (and (memq 'sealed options) #t)) (opaque? (and (memq 'opaque options) #t)) (uid (let ((probe (memq 'uid options))) (if (and probe (not (null? (cdr probe)))) (cadr probe) #f)))) (make-record-type-descriptor name parent uid sealed? opaque? (vector-map (lambda (fieldspec) (if (symbol? fieldspec) (list 'mutable fieldspec) fieldspec)) fieldspecs)))) (define rtd? record-type-descriptor?) (define (rtd-constructor rtd . rest) ; Computes permutation and allocates permutation buffer ; when the constructor is created, not when the constructor ; is called. More error checking is recommended. (define (make-constructor fieldspecs allnames maker) (let* ((k (length fieldspecs)) (n (length allnames)) (buffer (make-vector n)) (reverse-all-names (reverse allnames))) (define (position fieldname) (let ((names (memq fieldname reverse-all-names))) (assert names) (- (length names) 1))) (let ((indexes (map position fieldspecs))) ; The following can be made quite efficient by ; hand-coding it in some lower-level language, ; e.g. Larceny's mal. Even case-lambda would ; be good enough in most systems. (lambda args (assert (= (length args) k)) (for-each (lambda (arg posn) (vector-set! buffer posn arg)) args indexes) (apply maker (vector->list buffer)))))) (if (null? rest) (record-constructor (make-record-constructor-descriptor rtd #f #f)) (begin (assert (null? (cdr rest))) (make-constructor (vector->list (car rest)) (vector->list (rtd-all-field-names rtd)) (record-constructor (make-record-constructor-descriptor rtd #f #f)))))) (define rtd-predicate record-predicate) (define (rtd-accessor rtd0 fieldname) (define (loop rtd) (if (rtd? rtd) (let* ((names (vector->list (rtd-field-names rtd))) (probe (memq fieldname names))) (if probe (record-accessor rtd (- (length names) (length probe))) (loop (rtd-parent rtd)))) (assertion-violation 'rtd-accessor "illegal argument" rtd0 fieldname))) (loop rtd0)) (define (rtd-mutator rtd0 fieldname) (define (loop rtd) (if (rtd? rtd) (let* ((names (vector->list (rtd-field-names rtd))) (probe (memq fieldname names))) (if probe (record-mutator rtd (- (length names) (length probe))) (loop (rtd-parent rtd)))) (assertion-violation 'rtd-mutator "illegal argument" rtd0 fieldname))) (loop rtd0)) ) |
Added srfi/s99/records/syntactic.larceny.sls.
> > > |
1 2 3 |
(library (srfi s99 records syntactic) (export define-record-type) (import (err5rs records syntactic))) |
Added srfi/s99/records/syntactic.sls.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
;; Copyright (C) William D Clinger 2008. All Rights Reserved. ;; ;; Permission is hereby granted, free of charge, to any person obtaining a copy ;; of this software and associated documentation files (the "Software"), to deal ;; in the Software without restriction, including without limitation the rights ;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ;; copies of the Software, and to permit persons to whom the Software is ;; furnished to do so, subject to the following conditions: ;; ;; The above copyright notice and this permission notice shall be included in ;; all copies or substantial portions of the Software. ;; ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. REMEMBER, THERE IS NO ;; SCHEME UNDERGROUND. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ;; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF ;; CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #!r6rs (library (srfi s99 records syntactic) (export define-record-type) (import (for (rnrs base) run expand) (for (rnrs lists) run expand) (for (rnrs syntax-case) run expand) (srfi s99 records procedural)) (define-syntax define-record-type (syntax-rules () ((_ (type-name parent) constructor-spec predicate-spec . field-specs) (define-record-type-helper0 type-name parent constructor-spec predicate-spec . field-specs)) ((_ type-name constructor-spec predicate-spec . field-specs) (define-record-type-helper0 type-name #f constructor-spec predicate-spec . field-specs)))) (define-syntax define-record-type-helper0 (lambda (x) ; Given syntax objects, passes them to helper macro. (define (construct-record-type-definitions tname fields parent cspec pred afields mfields) (let () (define (frob x) (cond ((identifier? x) x) ((pair? x) (cons (frob (car x)) (frob (cdr x)))) ((vector? x) (vector-map frob x)) ((symbol? x) (datum->syntax tname x)) (else x))) #`(#,(frob #'define-record-type-helper) #,(frob tname) #,(frob fields) #,(frob parent) #,(frob cspec) #,(frob pred) #,(frob afields) #,(frob mfields)))) ; Given a syntax object that represents a non-empty list, ; returns the syntax object for its first element. (define (syntax-car x) (syntax-case x () ((x0 x1 ...) #'x0))) ; Given a syntax object that represents a non-empty list, ; returns the syntax object obtained by omitting the first ; element of that list. (define (syntax-cdr x) (syntax-case x () ((x0 x1 ...) #'(x1 ...)))) ; Given a syntax object that represents a non-empty list, ; returns the corresponding list of syntax objects. (define (syntax->list x) (syntax-case x () (() '()) ((x0 . x1) (cons #'x0 (syntax->list #'x1))))) (define (complain) (syntax-violation 'define-record-type "illegal syntax" x)) ; tname and pname are always identifiers here. (syntax-case x () ((_ tname pname constructor-spec predicate-spec . field-specs) (let* ((type-name (syntax->datum #'tname)) (cspec (syntax->datum #'constructor-spec)) (pspec (syntax->datum #'predicate-spec)) (fspecs (syntax->datum #'field-specs)) (type-name-string (begin (if (not (symbol? type-name)) (complain)) (symbol->string type-name))) (constructor-name (cond ((eq? cspec #f) #'constructor-spec) ((eq? cspec #t) (datum->syntax #'tname (string->symbol (string-append "make-" type-name-string)))) ((symbol? cspec) #'constructor-spec) ((and (pair? cspec) (symbol? (car cspec))) (syntax-car #'constructor-spec)) (else (complain)))) (constructor-args (cond ((pair? cspec) (if (not (for-all symbol? cspec)) (complain) (list->vector (syntax->list (syntax-cdr #'constructor-spec))))) (else #f))) (new-constructor-spec (if constructor-args (list constructor-name constructor-args) constructor-name)) (predicate-name (cond ((eq? pspec #f) #'predicate-spec) ((eq? pspec #t) (datum->syntax #'tname (string->symbol (string-append type-name-string "?")))) ((symbol? pspec) #'predicate-spec) (else (complain)))) (field-specs (map (lambda (fspec field-spec) (cond ((symbol? fspec) (list 'immutable fspec (string->symbol (string-append type-name-string "-" (symbol->string fspec))))) ((not (pair? fspec)) (complain)) ((not (list? fspec)) (complain)) ((not (for-all symbol? fspec)) (complain)) ((null? (cdr fspec)) (list 'mutable (car fspec) (string->symbol (string-append type-name-string "-" (symbol->string (car fspec)))) (string->symbol (string-append type-name-string "-" (symbol->string (car fspec)) "-set!")))) ((null? (cddr fspec)) (list 'immutable (car fspec) (syntax-car (syntax-cdr field-spec)))) ((null? (cdddr fspec)) (list 'mutable (car fspec) (syntax-car (syntax-cdr field-spec)) (syntax-car (syntax-cdr (syntax-cdr field-spec))))) (else (complain)))) fspecs (syntax->list #'field-specs))) (fields (list->vector (map cadr field-specs))) (accessor-fields (map (lambda (x) (list (caddr x) (cadr x))) (filter (lambda (x) (>= (length x) 3)) field-specs))) (mutator-fields (map (lambda (x) (list (cadddr x) (cadr x))) (filter (lambda (x) (= (length x) 4)) field-specs)))) (construct-record-type-definitions #'tname fields #'pname new-constructor-spec predicate-name accessor-fields mutator-fields)))))) (define-syntax define-record-type-helper (syntax-rules () ((_ type-name fields parent #f predicate ((accessor field) ...) ((mutator mutable-field) ...)) (define-record-type-helper type-name fields parent ignored predicate ((accessor field) ...) ((mutator mutable-field) ...))) ((_ type-name fields parent constructor #f ((accessor field) ...) ((mutator mutable-field) ...)) (define-record-type-helper type-name fields parent constructor ignored ((accessor field) ...) ((mutator mutable-field) ...))) ((_ type-name fields parent (constructor args) predicate ((accessor field) ...) ((mutator mutable-field) ...)) (begin (define type-name (make-rtd 'type-name 'fields parent)) (define constructor (rtd-constructor type-name 'args)) (define predicate (rtd-predicate type-name)) (define accessor (rtd-accessor type-name 'field)) ... (define mutator (rtd-mutator type-name 'mutable-field)) ...)) ((_ type-name fields parent constructor predicate ((accessor field) ...) ((mutator mutable-field) ...)) (begin (define type-name (make-rtd 'type-name 'fields parent)) (define constructor (rtd-constructor type-name)) (define predicate (rtd-predicate type-name)) (define accessor (rtd-accessor type-name 'field)) ... (define mutator (rtd-mutator type-name 'mutable-field)) ...)))) ) |
Added srfi/tests/and-let.sps.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. Licensed under an ;; MIT-style license. My license is in the file named LICENSE from the original ;; collection this file is distributed with. If this file is redistributed with ;; some other collection, my license must also be included. #!r6rs (import (rnrs) (rnrs eval) (surfage s2 and-let) (surfage s78 lightweight-testing)) (define-syntax expect (syntax-rules () [(_ expr result) (check expr => result)])) (define-syntax must-be-a-syntax-error (syntax-rules () [(_ expr) (check (guard (ex [#t (syntax-violation? ex)]) (eval 'expr (environment '(rnrs) '(surfage s2 and-let)))) => #t)])) ;; Taken straight from the reference implementation tests (expect (and-let* () 1) 1) (expect (and-let* () 1 2) 2) (expect (and-let* () ) #t) (expect (let ((x #f)) (and-let* (x))) #f) (expect (let ((x 1)) (and-let* (x))) 1) (expect (and-let* ((x #f)) ) #f) (expect (and-let* ((x 1)) ) 1) (must-be-a-syntax-error (and-let* ( #f (x 1))) ) (expect (and-let* ( (#f) (x 1)) ) #f) (must-be-a-syntax-error (and-let* (2 (x 1))) ) (expect (and-let* ( (2) (x 1)) ) 1) (expect (and-let* ( (x 1) (2)) ) 2) (expect (let ((x #f)) (and-let* (x) x)) #f) (expect (let ((x "")) (and-let* (x) x)) "") (expect (let ((x "")) (and-let* (x) )) "") (expect (let ((x 1)) (and-let* (x) (+ x 1))) 2) (expect (let ((x #f)) (and-let* (x) (+ x 1))) #f) (expect (let ((x 1)) (and-let* (((positive? x))) (+ x 1))) 2) (expect (let ((x 1)) (and-let* (((positive? x))) )) #t) (expect (let ((x 0)) (and-let* (((positive? x))) (+ x 1))) #f) (expect (let ((x 1)) (and-let* (((positive? x)) (x (+ x 1))) (+ x 1))) 3) ;; This next one is from the reference implementation tests ;; but I can't see how it "must be a syntax-error". #;(must-be-a-syntax-error (let ((x 1)) (and-let* (((positive? x)) (x (+ x 1)) (x (+ x 1))) (+ x 1)))) (expect (let ((x 1)) (and-let* (x ((positive? x))) (+ x 1))) 2) (expect (let ((x 1)) (and-let* ( ((begin x)) ((positive? x))) (+ x 1))) 2) (expect (let ((x 0)) (and-let* (x ((positive? x))) (+ x 1))) #f) (expect (let ((x #f)) (and-let* (x ((positive? x))) (+ x 1))) #f) (expect (let ((x #f)) (and-let* ( ((begin x)) ((positive? x))) (+ x 1))) #f) (expect (let ((x 1)) (and-let* (x (y (- x 1)) ((positive? y))) (/ x y))) #f) (expect (let ((x 0)) (and-let* (x (y (- x 1)) ((positive? y))) (/ x y))) #f) (expect (let ((x #f)) (and-let* (x (y (- x 1)) ((positive? y))) (/ x y))) #f) (expect (let ((x 3)) (and-let* (x (y (- x 1)) ((positive? y))) (/ x y))) 3/2) (check-report) |
Added srfi/tests/compare-procedures.sps.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 |
#!r6rs ;; NOTE: All tests of complex numbers have been commented out, ;; since many complex primitives are(were?) missing from Ikarus. (import (rnrs) (rnrs r5rs) (rename (only (rnrs) write) (write pretty-print)) (surfage s27 random-bits) (surfage s42 eager-comprehensions) (surfage s67 compare-procedures)) ; Copyright (c) 2005 Sebastian Egner and Jens Axel S{\o}gaard. ; ; Permission is hereby granted, free of charge, to any person obtaining ; a copy of this software and associated documentation files (the ; ``Software''), to deal in the Software without restriction, including ; without limitation the rights to use, copy, modify, merge, publish, ; distribute, sublicense, and/or sell copies of the Software, and to ; permit persons to whom the Software is furnished to do so, subject to ; the following conditions: ; ; The above copyright notice and this permission notice shall be ; included in all copies or substantial portions of the Software. ; ; THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, ; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION ; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION ; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ; ; ----------------------------------------------------------------------- ; ; Compare procedures SRFI (confidence tests) ; Sebastian.Egner@philips.com, Jensaxel@soegaard.net, 2005 ; ; history of this file: ; SE, 14-Oct-2004: first version ; .. ; SE, 28-Feb-2005: adapted to make it one-source PLT,S48,Chicken ; JS, 01-Mar-2005: first version ; SE, 18-Apr-2005: added (<? [c] [x y]) and (</<? [c] [x y z]) ; SE, 13-May-2005: included examples for <? etc. ; SE, 16-May-2005: naming convention changed; compare-by< optional x y ; ; This program runs some examples on 'compare.scm'. ; It has been tested under ; * PLT 208p1 ; * Scheme 48 1.1 ; * Chicken 1.70. ; Portability workarounds ; ======================= ; ; The purpose of these procedures is to push the examples ; through a Scheme system with severe limitations. It is ; not the intention to supply the functionality. ; poor man's complex (define (pm-complex? z) (or (real? z) (and (pair? z) (eq? (car z) 'complex)))) (define (pm-number? z) (or (real? z) (pm-complex? z))) (define (pm-make-rectangular re im) (list 'complex re im)) (define (pm-real-part z) (if (pm-complex? z) (cadr z) z)) (define (pm-imag-part z) (if (pm-complex? z) (caddr z) z)) ; apply on truncated argument list (define (make-apply limit) (let ((original-apply apply)) (lambda (f . xs) (let ((args (let loop ((xs xs) (rev-args '())) (cond ((null? xs) (reverse rev-args)) ((null? (cdr xs)) (append (reverse rev-args) (car xs))) (else (loop (cdr xs) (cons (car xs) rev-args))))))) (if (<= (length args) limit) (original-apply f args) (original-apply f (begin (display "*** warning: truncated apply") (newline) (let truncate ((n 0) (rev-args '()) (xs args)) (if (= n limit) (reverse rev-args) (truncate (+ n 1) (cons (car xs) rev-args) (cdr xs))))))))))) ; ============================================================================= ; Running the examples in Ikarus ; ====================================== ; ; 1. Uncomment the following lines: ; (define pretty-write pretty-print) ; 2. Run this file. ; Running the examples in PLT (DrScheme) ; ====================================== ; ; 1. Uncomment the following lines: ; ;plt (require ;plt (lib "16.ss" "srfi") ; case-lambda ;plt (lib "23.ss" "srfi") ; error ;plt (lib "27.ss" "srfi") ; random-integer ;plt (lib "42.ss" "srfi") ; eager comprehensions list-ec etc. ;plt (lib "pretty.ss")) ; pretty-print ;plt (define pretty-write pretty-print) ;plt (load "compare.scm") ; ; 2. Run this file. ; Running the examples in Scheme-48 ; ================================= ; ; 1. Invoke scheme48 with sufficient heap size (-h <words>). ; 2. Paste this into the REPL: ; ,open srfi-16 srfi-23 srfi-27 srfi-42 pp ; (define pretty-write p) ; ,load compare.scm examples.scm ; Running the examples in the Chicken Scheme Interpreter ; ====================================================== ; ; 1. Fetch and install the srfi-42 egg from the Chicken homepage ; 2. Uncomment the following lines: ; (require-extension srfi-23) ; (define random-integer random) ; (require-extension srfi-42) ; (define pretty-write display) ; (define complex? pm-complex?) ; (define number? pm-number?) ; (define make-rectangular pm-make-rectangular) ; (define real-part pm-real-part) ; (define imag-part pm-imag-part) ; (define apply (make-apply 126)) ; Grrr... ; (load "compare.scm") ; 3. Invoke csi with: ; csi -syntax examples.scm ; ; Note: Chicken doesn't have complex numbers and has a ; severe limit on the number of arguments for apply. ; ============================================================================= ; Test engine ; =========== ; ; We use an extended version of the the checker of SRFI-42 (with ; Felix' reduction on codesize) for running a batch of tests for ; the various procedures of 'compare.scm'. Moreover, we use the ; comprehensions of SRFI-42 to generate examples systematically. (define my-equal? equal?) (define my-pretty-write pretty-write) (define my-check-correct 0) (define my-check-wrong 0) (define (my-check-reset) (set! my-check-correct 0) (set! my-check-wrong 0)) ; (my-check expr => desired-result) ; evaluates expr and compares the value with desired-result. (define-syntax my-check (syntax-rules (=>) ((my-check expr => desired-result) (my-check-proc 'expr (lambda () expr) desired-result)))) (define (my-check-proc expr thunk desired-result) (newline) (my-pretty-write expr) (display " => ") (let ((actual-result (thunk))) (write actual-result) (if (my-equal? actual-result desired-result) (begin (display " ; correct") (set! my-check-correct (+ my-check-correct 1)) ) (begin (display " ; *** wrong ***, desired result:") (newline) (display " => ") (write desired-result) (set! my-check-wrong (+ my-check-wrong 1)))) (newline))) ; (my-check-ec <qualifier>* <ok?> <expr>) ; runs (every?-ec <qualifier>* <ok?>), counting the times <ok?> ; is evaluated as a correct example, and stopping at the first ; counter example for which <expr> provides the argument. (define-syntax my-check-ec (syntax-rules (nested) ((my-check-ec (nested q1 ...) q etc1 etc2 etc ...) (my-check-ec (nested q1 ... q) etc1 etc2 etc ...)) ((my-check-ec q1 q2 etc1 etc2 etc ...) (my-check-ec (nested q1 q2) etc1 etc2 etc ...)) ((my-check-ec ok? expr) (my-check-ec (nested) ok? expr)) ((my-check-ec (nested q ...) ok? expr) (my-check-ec-proc '(every?-ec q ... ok?) (lambda () (first-ec 'ok (nested q ...) (:let ok ok?) (begin (if ok (set! my-check-correct (+ my-check-correct 1)) (set! my-check-wrong (+ my-check-wrong 1)))) (if (not ok)) (list expr))) 'expr)) ((my-check-ec q ok? expr) (my-check-ec (nested q) ok? expr)))) (define (my-check-ec-proc expr thunk arg-counter-example) (let ((my-check-correct-save my-check-correct)) (newline) (my-pretty-write expr) (display " => ") (let ((result (thunk))) (if (eqv? result 'ok) (begin (display "#t ; correct (") (write (- my-check-correct my-check-correct-save)) (display " examples)") (newline)) (begin (display "#f ; *** wrong *** (after ") (write (- my-check-correct my-check-correct-save)) (display " correct examples).") (newline) (display " ; Argument of the first counter example:") (newline) (display " ; ") (write arg-counter-example) (display " = ") (write (car result))))))) (define (my-check-summary) (begin (newline) (newline) (display "*** correct examples: ") (display my-check-correct) (newline) (display "*** wrong examples: ") (display my-check-wrong) (newline) (newline))) ; ============================================================================= ; Abstractions etc. ; ================= (define ci integer-compare) ; very frequently used ; (result-ok? actual desired) ; tests if actual and desired specify the same ordering. (define (result-ok? actual desired) (eqv? actual desired)) ; (my-check-compare compare increasing-elements) ; evaluates (compare x y) for x, y in increasing-elements ; and checks the result against -1, 0, or 1 depending on ; the position of x and y in the list increasing-elements. (define-syntax my-check-compare (syntax-rules () ((my-check-compare compare increasing-elements) (my-check-ec (:list x (index ix) increasing-elements) (:list y (index iy) increasing-elements) (result-ok? (compare x y) (ci ix iy)) (list x y))))) ; sorted lists (define my-booleans '(#f #t)) (define my-chars '(#\a #\b #\c)) (define my-chars-ci '(#\a #\B #\c #\D)) (define my-strings '("" "a" "aa" "ab" "b" "ba" "bb")) (define my-strings-ci '("" "a" "aA" "Ab" "B" "bA" "BB")) (define my-symbols '(a aa ab b ba bb)) (define my-reals (append-ec (:range xn -6 7) (:let x (/ xn 3)) (list x (+ x (exact->inexact (/ 1 100)))))) (define my-rationals (list-ec (:list x my-reals) (and (exact? x) (rational? x)) x)) (define my-integers (list-ec (:list x my-reals) (if (and (exact? x) (integer? x))) x)) #;(define my-complexes (list-ec (:list re-x my-reals) (if (inexact? re-x)) (:list im-x my-reals) (if (inexact? im-x)) (make-rectangular re-x im-x))) (define my-lists '(() (1) (1 1) (1 2) (2) (2 1) (2 2))) (define my-vector-as-lists (map list->vector my-lists)) (define my-list-as-vectors '(() (1) (2) (1 1) (1 2) (2 1) (2 2))) (define my-vectors (map list->vector my-list-as-vectors)) (define my-null-or-pairs '(() (1) (1 1) (1 2) (1 . 1) (1 . 2) (2) (2 1) (2 2) (2 . 1) (2 . 2))) (define my-objects (append my-null-or-pairs my-booleans my-chars my-strings my-symbols my-integers my-vectors)) ; ============================================================================= ; The checks ; ========== (define (check:if3) ; basic functionality (my-check (if3 -1 'n 'z 'p) => 'n) (my-check (if3 0 'n 'z 'p) => 'z) (my-check (if3 1 'n 'z 'p) => 'p) ; check arguments are evaluated only once (my-check (let ((x -1)) (if3 (let ((x0 x)) (set! x (+ x 1)) x0) 'n 'z 'p)) => 'n) (my-check (let ((x -1) (y 0)) (if3 (let ((x0 x)) (set! x (+ x 1)) x0) (begin (set! y (+ y 1)) y) (begin (set! y (+ y 10)) y) (begin (set! y (+ y 100)) y))) => 1) (my-check (let ((x 0) (y 0)) (if3 (let ((x0 x)) (set! x (+ x 1)) x0) (begin (set! y (+ y 1)) y) (begin (set! y (+ y 10)) y) (begin (set! y (+ y 100)) y))) => 10) (my-check (let ((x 1) (y 0)) (if3 (let ((x0 x)) (set! x (+ x 1)) x0) (begin (set! y (+ y 1)) y) (begin (set! y (+ y 10)) y) (begin (set! y (+ y 100)) y))) => 100) ) ; check:if3 (define-syntax my-check-if2 (syntax-rules () ((my-check-if2 if-rel? rel) (begin ; check result (my-check (if-rel? -1 'yes 'no) => (if (rel -1 0) 'yes 'no)) (my-check (if-rel? 0 'yes 'no) => (if (rel 0 0) 'yes 'no)) (my-check (if-rel? 1 'yes 'no) => (if (rel 1 0) 'yes 'no)) ; check result of 'laterally challenged if' (my-check (let ((x #f)) (if-rel? -1 (set! x #t)) x) => (rel -1 0)) (my-check (let ((x #f)) (if-rel? 0 (set! x #t)) x) => (rel 0 0)) (my-check (let ((x #f)) (if-rel? 1 (set! x #t)) x) => (rel 1 0)) ; check that <c> is evaluated exactly once (my-check (let ((n 0)) (if-rel? (begin (set! n (+ n 1)) -1) #t #f) n) => 1) (my-check (let ((n 0)) (if-rel? (begin (set! n (+ n 1)) 0) #t #f) n) => 1) (my-check (let ((n 0)) (if-rel? (begin (set! n (+ n 1)) 1) #t #f) n) => 1) (my-check (let ((n 0)) (if-rel? (begin (set! n (+ n 1)) -1) #t) n) => 1) (my-check (let ((n 0)) (if-rel? (begin (set! n (+ n 1)) 0) #t) n) => 1) (my-check (let ((n 0)) (if-rel? (begin (set! n (+ n 1)) 1) #t) n) => 1) )))) (define (check:ifs) (my-check-if2 if=? =) (my-check-if2 if<? <) (my-check-if2 if>? >) (my-check-if2 if<=? <=) (my-check-if2 if>=? >=) (my-check-if2 if-not=? (lambda (x y) (not (= x y)))) ) ; check:if2 ; <? etc. macros (define-syntax my-check-chain2 (syntax-rules () ((my-check-chain2 rel? rel) (begin ; all chains of length 2 (my-check (rel? ci 0 0) => (rel 0 0)) (my-check (rel? ci 0 1) => (rel 0 1)) (my-check (rel? ci 1 0) => (rel 1 0)) ; using default-compare (my-check (rel? 0 0) => (rel 0 0)) (my-check (rel? 0 1) => (rel 0 1)) (my-check (rel? 1 0) => (rel 1 0)) ; as a combinator (my-check ((rel? ci) 0 0) => (rel 0 0)) (my-check ((rel? ci) 0 1) => (rel 0 1)) (my-check ((rel? ci) 1 0) => (rel 1 0)) ; using default-compare as a combinator (my-check ((rel?) 0 0) => (rel 0 0)) (my-check ((rel?) 0 1) => (rel 0 1)) (my-check ((rel?) 1 0) => (rel 1 0)) )))) (define (list->set xs) ; xs a list of integers (if (null? xs) '() (let ((max-xs (let max-without-apply ((m 1) (xs xs)) (if (null? xs) m (max-without-apply (max m (car xs)) (cdr xs)))))) (let ((in-xs? (make-vector (+ max-xs 1) #f))) (do-ec (:list x xs) (vector-set! in-xs? x #t)) (list-ec (:vector in? (index x) in-xs?) (if in?) x))))) (define-syntax arguments-used ; set of arguments (integer, >=0) used in compare (syntax-rules () ((arguments-used (rel1/rel2 compare arg ...)) (let ((used '())) (rel1/rel2 (lambda (x y) (set! used (cons x (cons y used))) (compare x y)) arg ...) (list->set used))))) (define-syntax my-check-chain3 (syntax-rules () ((my-check-chain3 rel1/rel2? rel1 rel2) (begin ; all chains of length 3 (my-check (rel1/rel2? ci 0 0 0) => (and (rel1 0 0) (rel2 0 0))) (my-check (rel1/rel2? ci 0 0 1) => (and (rel1 0 0) (rel2 0 1))) (my-check (rel1/rel2? ci 0 1 0) => (and (rel1 0 1) (rel2 1 0))) (my-check (rel1/rel2? ci 1 0 0) => (and (rel1 1 0) (rel2 0 0))) (my-check (rel1/rel2? ci 1 1 0) => (and (rel1 1 1) (rel2 1 0))) (my-check (rel1/rel2? ci 1 0 1) => (and (rel1 1 0) (rel2 0 1))) (my-check (rel1/rel2? ci 0 1 1) => (and (rel1 0 1) (rel2 1 1))) (my-check (rel1/rel2? ci 0 1 2) => (and (rel1 0 1) (rel2 1 2))) (my-check (rel1/rel2? ci 0 2 1) => (and (rel1 0 2) (rel2 2 1))) (my-check (rel1/rel2? ci 1 2 0) => (and (rel1 1 2) (rel2 2 0))) (my-check (rel1/rel2? ci 1 0 2) => (and (rel1 1 0) (rel2 0 2))) (my-check (rel1/rel2? ci 2 0 1) => (and (rel1 2 0) (rel2 0 1))) (my-check (rel1/rel2? ci 2 1 0) => (and (rel1 2 1) (rel2 1 0))) ; using default-compare (my-check (rel1/rel2? 0 0 0) => (and (rel1 0 0) (rel2 0 0))) (my-check (rel1/rel2? 0 0 1) => (and (rel1 0 0) (rel2 0 1))) (my-check (rel1/rel2? 0 1 0) => (and (rel1 0 1) (rel2 1 0))) (my-check (rel1/rel2? 1 0 0) => (and (rel1 1 0) (rel2 0 0))) (my-check (rel1/rel2? 1 1 0) => (and (rel1 1 1) (rel2 1 0))) (my-check (rel1/rel2? 1 0 1) => (and (rel1 1 0) (rel2 0 1))) (my-check (rel1/rel2? 0 1 1) => (and (rel1 0 1) (rel2 1 1))) (my-check (rel1/rel2? 0 1 2) => (and (rel1 0 1) (rel2 1 2))) (my-check (rel1/rel2? 0 2 1) => (and (rel1 0 2) (rel2 2 1))) (my-check (rel1/rel2? 1 2 0) => (and (rel1 1 2) (rel2 2 0))) (my-check (rel1/rel2? 1 0 2) => (and (rel1 1 0) (rel2 0 2))) (my-check (rel1/rel2? 2 0 1) => (and (rel1 2 0) (rel2 0 1))) (my-check (rel1/rel2? 2 1 0) => (and (rel1 2 1) (rel2 1 0))) ; as a combinator (my-check ((rel1/rel2? ci) 0 0 0) => (and (rel1 0 0) (rel2 0 0))) (my-check ((rel1/rel2? ci) 0 0 1) => (and (rel1 0 0) (rel2 0 1))) (my-check ((rel1/rel2? ci) 0 1 0) => (and (rel1 0 1) (rel2 1 0))) (my-check ((rel1/rel2? ci) 1 0 0) => (and (rel1 1 0) (rel2 0 0))) (my-check ((rel1/rel2? ci) 1 1 0) => (and (rel1 1 1) (rel2 1 0))) (my-check ((rel1/rel2? ci) 1 0 1) => (and (rel1 1 0) (rel2 0 1))) (my-check ((rel1/rel2? ci) 0 1 1) => (and (rel1 0 1) (rel2 1 1))) (my-check ((rel1/rel2? ci) 0 1 2) => (and (rel1 0 1) (rel2 1 2))) (my-check ((rel1/rel2? ci) 0 2 1) => (and (rel1 0 2) (rel2 2 1))) (my-check ((rel1/rel2? ci) 1 2 0) => (and (rel1 1 2) (rel2 2 0))) (my-check ((rel1/rel2? ci) 1 0 2) => (and (rel1 1 0) (rel2 0 2))) (my-check ((rel1/rel2? ci) 2 0 1) => (and (rel1 2 0) (rel2 0 1))) (my-check ((rel1/rel2? ci) 2 1 0) => (and (rel1 2 1) (rel2 1 0))) ; as a combinator using default-compare (my-check ((rel1/rel2?) 0 0 0) => (and (rel1 0 0) (rel2 0 0))) (my-check ((rel1/rel2?) 0 0 1) => (and (rel1 0 0) (rel2 0 1))) (my-check ((rel1/rel2?) 0 1 0) => (and (rel1 0 1) (rel2 1 0))) (my-check ((rel1/rel2?) 1 0 0) => (and (rel1 1 0) (rel2 0 0))) (my-check ((rel1/rel2?) 1 1 0) => (and (rel1 1 1) (rel2 1 0))) (my-check ((rel1/rel2?) 1 0 1) => (and (rel1 1 0) (rel2 0 1))) (my-check ((rel1/rel2?) 0 1 1) => (and (rel1 0 1) (rel2 1 1))) (my-check ((rel1/rel2?) 0 1 2) => (and (rel1 0 1) (rel2 1 2))) (my-check ((rel1/rel2?) 0 2 1) => (and (rel1 0 2) (rel2 2 1))) (my-check ((rel1/rel2?) 1 2 0) => (and (rel1 1 2) (rel2 2 0))) (my-check ((rel1/rel2?) 1 0 2) => (and (rel1 1 0) (rel2 0 2))) (my-check ((rel1/rel2?) 2 0 1) => (and (rel1 2 0) (rel2 0 1))) (my-check ((rel1/rel2?) 2 1 0) => (and (rel1 2 1) (rel2 1 0))) ; test if all arguments are type checked (my-check (arguments-used (rel1/rel2? ci 0 1 2)) => '(0 1 2)) (my-check (arguments-used (rel1/rel2? ci 0 2 1)) => '(0 1 2)) (my-check (arguments-used (rel1/rel2? ci 1 2 0)) => '(0 1 2)) (my-check (arguments-used (rel1/rel2? ci 1 0 2)) => '(0 1 2)) (my-check (arguments-used (rel1/rel2? ci 2 0 1)) => '(0 1 2)) (my-check (arguments-used (rel1/rel2? ci 2 1 0)) => '(0 1 2)) )))) (define-syntax my-check-chain (syntax-rules () ((my-check-chain chain-rel? rel) (begin ; the chain of length 0 (my-check (chain-rel? ci) => #t) ; a chain of length 1 (my-check (chain-rel? ci 0) => #t) ; all chains of length 2 (my-check (chain-rel? ci 0 0) => (rel 0 0)) (my-check (chain-rel? ci 0 1) => (rel 0 1)) (my-check (chain-rel? ci 1 0) => (rel 1 0)) ; all chains of length 3 (my-check (chain-rel? ci 0 0 0) => (rel 0 0 0)) (my-check (chain-rel? ci 0 0 1) => (rel 0 0 1)) (my-check (chain-rel? ci 0 1 0) => (rel 0 1 0)) (my-check (chain-rel? ci 1 0 0) => (rel 1 0 0)) (my-check (chain-rel? ci 1 1 0) => (rel 1 1 0)) (my-check (chain-rel? ci 1 0 1) => (rel 1 0 1)) (my-check (chain-rel? ci 0 1 1) => (rel 0 1 1)) (my-check (chain-rel? ci 0 1 2) => (rel 0 1 2)) (my-check (chain-rel? ci 0 2 1) => (rel 0 2 1)) (my-check (chain-rel? ci 1 2 0) => (rel 1 2 0)) (my-check (chain-rel? ci 1 0 2) => (rel 1 0 2)) (my-check (chain-rel? ci 2 0 1) => (rel 2 0 1)) (my-check (chain-rel? ci 2 1 0) => (rel 2 1 0)) ; check if all arguments are used (my-check (arguments-used (chain-rel? ci 0)) => '(0)) (my-check (arguments-used (chain-rel? ci 0 1)) => '(0 1)) (my-check (arguments-used (chain-rel? ci 1 0)) => '(0 1)) (my-check (arguments-used (chain-rel? ci 0 1 2)) => '(0 1 2)) (my-check (arguments-used (chain-rel? ci 0 2 1)) => '(0 1 2)) (my-check (arguments-used (chain-rel? ci 1 2 0)) => '(0 1 2)) (my-check (arguments-used (chain-rel? ci 1 0 2)) => '(0 1 2)) (my-check (arguments-used (chain-rel? ci 2 0 1)) => '(0 1 2)) (my-check (arguments-used (chain-rel? ci 2 1 0)) => '(0 1 2)) )))) (define (check:predicates-from-compare) (my-check-chain2 =? =) (my-check-chain2 <? <) (my-check-chain2 >? >) (my-check-chain2 <=? <=) (my-check-chain2 >=? >=) (my-check-chain2 not=? (lambda (x y) (not (= x y)))) (my-check-chain3 </<? < <) (my-check-chain3 </<=? < <=) (my-check-chain3 <=/<? <= <) (my-check-chain3 <=/<=? <= <=) (my-check-chain3 >/>? > >) (my-check-chain3 >/>=? > >=) (my-check-chain3 >=/>? >= >) (my-check-chain3 >=/>=? >= >=) (my-check-chain chain=? =) (my-check-chain chain<? <) (my-check-chain chain>? >) (my-check-chain chain<=? <=) (my-check-chain chain>=? >=) ) ; check:predicates-from-compare ; pairwise-not=? (define pairwise-not=?:long-sequences (let () (define (extremal-pivot-sequence r) ; The extremal pivot sequence of order r is a ; permutation of {0..2^(r+1)-2} such that the ; middle element is minimal, and this property ; holds recursively for each binary subdivision. ; This sequence exposes a naive implementation of ; pairwise-not=? chosing the middle element as pivot. (if (zero? r) '(0) (let* ((s (extremal-pivot-sequence (- r 1))) (ns (length s))) (append (list-ec (:list x s) (+ x 1)) '(0) (list-ec (:list x s) (+ x ns 1)))))) (list (list-ec (: i 4096) i) (list-ec (: i 4097 0 -1) i) (list-ec (: i 4099) (modulo (* 1003 i) 4099)) (extremal-pivot-sequence 11)))) (define pairwise-not=?:short-sequences (let () (define (combinations/repeats n l) ; return list of all sublists of l of size n, ; the order of the elements occur in the sublists ; of the output is the same as in the input (let ((len (length l))) (cond ((= n 0) '()) ((= n 1) (map list l)) ((= len 1) (do ((r '() (cons (car l) r)) (i n (- i 1))) ((= i 0) (list r)))) (else (append (combinations/repeats n (cdr l)) (map (lambda (c) (cons (car l) c)) (combinations/repeats (- n 1) l))))))) (define (permutations l) ; return a list of all permutations of l (let ((len (length l))) (cond ((= len 0) '(())) ((= len 1) (list l)) (else (apply append (map (lambda (p) (insert-every-where (car l) p)) (permutations (cdr l)))))))) (define (insert-every-where x xs) (let loop ((result '()) (before '()) (after xs)) (let ((new (append before (cons x after)))) (cond ((null? after) (cons new result)) (else (loop (cons new result) (append before (list (car after))) (cdr after))))))) (define (sequences n max) (apply append (map permutations (combinations/repeats n (list-ec (: i max) i))))) (append-ec (: n 5) (sequences n 5)))) (define (colliding-compare x y) (ci (modulo x 3) (modulo y 3))) (define (naive-pairwise-not=? compare . xs) (let ((xs (list->vector xs))) (every?-ec (:range i (- (vector-length xs) 1)) (:let xs-i (vector-ref xs i)) (:range j (+ i 1) (vector-length xs)) (:let xs-j (vector-ref xs j)) (not=? compare xs-i xs-j)))) (define (check:pairwise-not=?) ; 0-ary, 1-ary (my-check (pairwise-not=? ci) => #t) (my-check (pairwise-not=? ci 0) => #t) ; 2-ary (my-check (pairwise-not=? ci 0 0) => #f) (my-check (pairwise-not=? ci 0 1) => #t) (my-check (pairwise-not=? ci 1 0) => #t) ; 3-ary (my-check (pairwise-not=? ci 0 0 0) => #f) (my-check (pairwise-not=? ci 0 0 1) => #f) (my-check (pairwise-not=? ci 0 1 0) => #f) (my-check (pairwise-not=? ci 1 0 0) => #f) (my-check (pairwise-not=? ci 1 1 0) => #f) (my-check (pairwise-not=? ci 1 0 1) => #f) (my-check (pairwise-not=? ci 0 1 1) => #f) (my-check (pairwise-not=? ci 0 1 2) => #t) (my-check (pairwise-not=? ci 0 2 1) => #t) (my-check (pairwise-not=? ci 1 2 0) => #t) (my-check (pairwise-not=? ci 1 0 2) => #t) (my-check (pairwise-not=? ci 2 0 1) => #t) (my-check (pairwise-not=? ci 2 1 0) => #t) ; n-ary, n large: [0..n-1], [n,n-1..1], 5^[0..96] mod 97 (my-check (apply pairwise-not=? ci (list-ec (: i 10) i)) => #t) (my-check (apply pairwise-not=? ci (list-ec (: i 100) i)) => #t) (my-check (apply pairwise-not=? ci (list-ec (: i 1000) i)) => #t) (my-check (apply pairwise-not=? ci (list-ec (: i 10 0 -1) i)) => #t) (my-check (apply pairwise-not=? ci (list-ec (: i 100 0 -1) i)) => #t) (my-check (apply pairwise-not=? ci (list-ec (: i 1000 0 -1) i)) => #t) (my-check (apply pairwise-not=? ci (list-ec (: i 97) (modulo (* 5 i) 97))) => #t) ; bury another copy of 72 = 5^50 mod 97 in 5^[0..96] mod 97 (my-check (apply pairwise-not=? ci (append (list-ec (: i 0 23) (modulo (* 5 i) 97)) '(72) (list-ec (: i 23 97) (modulo (* 5 i) 97)))) => #f) (my-check (apply pairwise-not=? ci (append (list-ec (: i 0 75) (modulo (* 5 i) 97)) '(72) (list-ec (: i 75 97) (modulo (* 5 i) 97)))) => #f) ; check if all arguments are used (my-check (arguments-used (pairwise-not=? ci 0)) => '(0)) (my-check (arguments-used (pairwise-not=? ci 0 1)) => '(0 1)) (my-check (arguments-used (pairwise-not=? ci 1 0)) => '(0 1)) (my-check (arguments-used (pairwise-not=? ci 0 2 1)) => '(0 1 2)) (my-check (arguments-used (pairwise-not=? ci 1 2 0)) => '(0 1 2)) (my-check (arguments-used (pairwise-not=? ci 1 0 2)) => '(0 1 2)) (my-check (arguments-used (pairwise-not=? ci 2 0 1)) => '(0 1 2)) (my-check (arguments-used (pairwise-not=? ci 2 1 0)) => '(0 1 2)) (my-check (arguments-used (pairwise-not=? ci 0 0 0 1 0 0 0 2 0 0 0 3)) => '(0 1 2 3)) ; Guess if the implementation is O(n log n): ; The test is run for 2^e pairwise unequal inputs, e >= 1, ; and the number of calls to the compare procedure is counted. ; all pairs: A = Binomial[2^e, 2] = 2^(2 e - 1) * (1 - 2^-e). ; divide and conquer: D = e 2^e. ; Since an implementation can be randomized, the actual count may ; be a random number. We put a threshold at 100 e 2^e and choose ; e such that A/D >= 150, i.e. e >= 12. ; The test is applied to several inputs that are known to cause ; trouble in simplistic sorting algorithms: (0..2^e-1), (2^e+1,2^e..1), ; a pseudo-random permutation, and a sequence with an extremal pivot ; at the center of each subsequence. (my-check-ec (:list input pairwise-not=?:long-sequences) (let ((compares 0)) (apply pairwise-not=? (lambda (x y) (set! compares (+ compares 1)) (ci x y)) input) ; (display compares) (newline) (< compares (* 100 12 4096))) (length input)) ; check many short sequences (my-check-ec (:list input pairwise-not=?:short-sequences) (eq? (apply pairwise-not=? colliding-compare input) (apply naive-pairwise-not=? colliding-compare input)) input) ; check if the arguments are used for short sequences (my-check-ec (:list input pairwise-not=?:short-sequences) (let ((args '())) (apply pairwise-not=? (lambda (x y) (set! args (cons x (cons y args))) (colliding-compare x y)) input) (equal? (list->set args) (list->set input))) input) ) ; check:pairwise-not=? ; min/max (define min/max:sequences (append pairwise-not=?:short-sequences pairwise-not=?:long-sequences)) (define (check:min/max) ; all lists of length 1,2,3 (my-check (min-compare ci 0) => 0) (my-check (min-compare ci 0 0) => 0) (my-check (min-compare ci 0 1) => 0) (my-check (min-compare ci 1 0) => 0) (my-check (min-compare ci 0 0 0) => 0) (my-check (min-compare ci 0 0 1) => 0) (my-check (min-compare ci 0 1 0) => 0) (my-check (min-compare ci 1 0 0) => 0) (my-check (min-compare ci 1 1 0) => 0) (my-check (min-compare ci 1 0 1) => 0) (my-check (min-compare ci 0 1 1) => 0) (my-check (min-compare ci 0 1 2) => 0) (my-check (min-compare ci 0 2 1) => 0) (my-check (min-compare ci 1 2 0) => 0) (my-check (min-compare ci 1 0 2) => 0) (my-check (min-compare ci 2 0 1) => 0) (my-check (min-compare ci 2 1 0) => 0) (my-check (max-compare ci 0) => 0) (my-check (max-compare ci 0 0) => 0) (my-check (max-compare ci 0 1) => 1) (my-check (max-compare ci 1 0) => 1) (my-check (max-compare ci 0 0 0) => 0) (my-check (max-compare ci 0 0 1) => 1) (my-check (max-compare ci 0 1 0) => 1) (my-check (max-compare ci 1 0 0) => 1) (my-check (max-compare ci 1 1 0) => 1) (my-check (max-compare ci 1 0 1) => 1) (my-check (max-compare ci 0 1 1) => 1) (my-check (max-compare ci 0 1 2) => 2) (my-check (max-compare ci 0 2 1) => 2) (my-check (max-compare ci 1 2 0) => 2) (my-check (max-compare ci 1 0 2) => 2) (my-check (max-compare ci 2 0 1) => 2) (my-check (max-compare ci 2 1 0) => 2) ; check that the first minimal value is returned (my-check (min-compare (pair-compare-car ci) '(0 1) '(0 2) '(0 3)) => '(0 1)) (my-check (max-compare (pair-compare-car ci) '(0 1) '(0 2) '(0 3)) => '(0 1)) ; check for many inputs (my-check-ec (:list input min/max:sequences) (= (apply min-compare ci input) (apply min (apply max input) input)) input) (my-check-ec (:list input min/max:sequences) (= (apply max-compare ci input) (apply max (apply min input) input)) input) ; Note the stupid extra argument in the apply for ; the standard min/max makes sure the elements are ; identical when apply truncates the arglist. ) ; check:min/max ; kth-largest (define kth-largest:sequences pairwise-not=?:short-sequences) (define (naive-kth-largest compare k . xs) (let ((vec (list->vector xs))) ; bubble sort: simple, stable, O(|xs|^2) (do-ec (:range n (- (vector-length vec) 1)) (:range i 0 (- (- (vector-length vec) 1) n)) (if>? (compare (vector-ref vec i) (vector-ref vec (+ i 1))) (let ((vec-i (vector-ref vec i))) (vector-set! vec i (vector-ref vec (+ i 1))) (vector-set! vec (+ i 1) vec-i)))) (vector-ref vec (modulo k (vector-length vec))))) (define (check:kth-largest) ; check extensively against naive-kth-largest (my-check-ec (:list input kth-largest:sequences) (: k (- -2 (length input)) (+ (length input) 2)) (= (apply naive-kth-largest colliding-compare k input) (apply kth-largest colliding-compare k input)) (list input k)) ) ;check:kth-largest ; compare-by< etc. procedures (define (check:compare-from-predicates) (my-check-compare (compare-by< <) my-integers) (my-check-compare (compare-by> >) my-integers) (my-check-compare (compare-by<= <=) my-integers) (my-check-compare (compare-by>= >=) my-integers) (my-check-compare (compare-by=/< = <) my-integers) (my-check-compare (compare-by=/> = >) my-integers) ; with explicit arguments (my-check-compare (lambda (x y) (compare-by< < x y)) my-integers) (my-check-compare (lambda (x y) (compare-by> > x y)) my-integers) (my-check-compare (lambda (x y) (compare-by<= <= x y)) my-integers) (my-check-compare (lambda (x y) (compare-by>= >= x y)) my-integers) (my-check-compare (lambda (x y) (compare-by=/< = < x y)) my-integers) (my-check-compare (lambda (x y) (compare-by=/> = > x y)) my-integers) ) ; check:compare-from-predicates (define (check:atomic) (my-check-compare boolean-compare my-booleans) (my-check-compare char-compare my-chars) (my-check-compare char-compare-ci my-chars-ci) (my-check-compare string-compare my-strings) (my-check-compare string-compare-ci my-strings-ci) (my-check-compare symbol-compare my-symbols) (my-check-compare integer-compare my-integers) (my-check-compare rational-compare my-rationals) (my-check-compare real-compare my-reals) #;(my-check-compare complex-compare my-complexes) #;(my-check-compare number-compare my-complexes) ) ; check:atomic (define (check:refine-select-cond) ; refine-compare (my-check-compare (lambda (x y) (refine-compare)) '(#f)) (my-check-compare (lambda (x y) (refine-compare (integer-compare x y))) my-integers) (my-check-compare (lambda (x y) (refine-compare (integer-compare (car x) (car y)) (symbol-compare (cdr x) (cdr y)))) '((1 . a) (1 . b) (2 . b) (2 . c) (3 . a) (3 . c))) (my-check-compare (lambda (x y) (refine-compare (integer-compare (car x) (car y)) (symbol-compare (cadr x) (cadr y)) (string-compare (caddr x) (caddr y)))) '((1 a "a") (1 b "a") (1 b "b") (2 b "c") (2 c "a") (3 a "b") (3 c "b"))) ; select-compare (my-check-compare (lambda (x y) (select-compare x y)) '(#f)) (my-check-compare (lambda (x y) (select-compare x y (integer? (ci x y)))) my-integers) (my-check-compare (lambda (x y) (select-compare x y (pair? (integer-compare (car x) (car y)) (symbol-compare (cdr x) (cdr y))))) '((1 . a) (1 . b) (2 . b) (2 . c) (3 . a) (3 . c))) (my-check-compare (lambda (x y) (select-compare x y (else (integer-compare x y)))) my-integers) (my-check-compare (lambda (x y) (select-compare x y (else (integer-compare (car x) (car y)) (symbol-compare (cdr x) (cdr y))))) '((1 . a) (1 . b) (2 . b) (2 . c) (3 . a) (3 . c))) (my-check-compare (lambda (x y) (select-compare x y (symbol? (symbol-compare x y)) (string? (string-compare x y)))) '(a b c "a" "b" "c" 1)) ; implicit (else 0) (my-check-compare (lambda (x y) (select-compare x y (symbol? (symbol-compare x y)) (else (string-compare x y)))) '(a b c "a" "b" "c")) ; test if arguments are only evaluated once (my-check (let ((nx 0) (ny 0) (nt 0)) (select-compare (begin (set! nx (+ nx 1)) 1) (begin (set! ny (+ ny 1)) 2) ((lambda (z) (set! nt (+ nt 1)) #f) 0) ((lambda (z) (set! nt (+ nt 10)) #f) 0) ((lambda (z) (set! nt (+ nt 100)) #f) 0) (else 0)) (list nx ny nt)) => '(1 1 222)) ; cond-compare (my-check-compare (lambda (x y) (cond-compare)) '(#f)) (my-check-compare (lambda (x y) (cond-compare (((integer? x) (integer? y)) (integer-compare x y)))) my-integers) (my-check-compare (lambda (x y) (cond-compare (((pair? x) (pair? y)) (integer-compare (car x) (car y)) (symbol-compare (cdr x) (cdr y))))) '((1 . a) (1 . b) (2 . b) (2 . c) (3 . a) (3 . c))) (my-check-compare (lambda (x y) (cond-compare (else (integer-compare x y)))) my-integers) (my-check-compare (lambda (x y) (cond-compare (else (integer-compare (car x) (car y)) (symbol-compare (cdr x) (cdr y))))) '((1 . a) (1 . b) (2 . b) (2 . c) (3 . a) (3 . c))) (my-check-compare (lambda (x y) (cond-compare (((symbol? x) (symbol? y)) (symbol-compare x y)) (((string? x) (string? y)) (string-compare x y)))) '(a b c "a" "b" "c" 1)) ; implicit (else 0) (my-check-compare (lambda (x y) (cond-compare (((symbol? x) (symbol? y)) (symbol-compare x y)) (else (string-compare x y)))) '(a b c "a" "b" "c")) ) ; check:refine-select-cond ; We define our own list/vector data structure ; as '(my-list x[1] .. x[n]), n >= 0, in order ; to make sure the default ops don't work on it. (define (my-list-checked obj) (if (and (list? obj) (eqv? (car obj) 'my-list)) obj (error "expected my-list but received" obj))) (define (list->my-list list) (cons 'my-list list)) (define (my-empty? x) (null? (cdr (my-list-checked x)))) (define (my-head x) (cadr (my-list-checked x))) (define (my-tail x) (cons 'my-list (cddr (my-list-checked x)))) (define (my-size x) (- (length (my-list-checked x)) 1)) (define (my-ref x i) (list-ref (my-list-checked x) (+ i 1))) (define (check:data-structures) (my-check-compare (pair-compare-car ci) '((1 . b) (2 . a) (3 . c))) (my-check-compare (pair-compare-cdr ci) '((b . 1) (a . 2) (c . 3))) ; pair-compare (my-check-compare pair-compare my-null-or-pairs) (my-check-compare (lambda (x y) (pair-compare ci x y)) my-null-or-pairs) (my-check-compare (lambda (x y) (pair-compare ci symbol-compare x y)) '((1 . a) (1 . b) (2 . b) (2 . c) (3 . a))) ; list-compare (my-check-compare list-compare my-lists) (my-check-compare (lambda (x y) (list-compare ci x y)) my-lists) (my-check-compare (lambda (x y) (list-compare x y my-empty? my-head my-tail)) (map list->my-list my-lists)) (my-check-compare (lambda (x y) (list-compare ci x y my-empty? my-head my-tail)) (map list->my-list my-lists)) ; list-compare-as-vector (my-check-compare list-compare-as-vector my-list-as-vectors) (my-check-compare (lambda (x y) (list-compare-as-vector ci x y)) my-list-as-vectors) (my-check-compare (lambda (x y) (list-compare-as-vector x y my-empty? my-head my-tail)) (map list->my-list my-list-as-vectors)) (my-check-compare (lambda (x y) (list-compare-as-vector ci x y my-empty? my-head my-tail)) (map list->my-list my-list-as-vectors)) ; vector-compare (my-check-compare vector-compare my-vectors) (my-check-compare (lambda (x y) (vector-compare ci x y)) my-vectors) (my-check-compare (lambda (x y) (vector-compare x y my-size my-ref)) (map list->my-list my-list-as-vectors)) (my-check-compare (lambda (x y) (vector-compare ci x y my-size my-ref)) (map list->my-list my-list-as-vectors)) ; vector-compare-as-list (my-check-compare vector-compare-as-list my-vector-as-lists) (my-check-compare (lambda (x y) (vector-compare-as-list ci x y)) my-vector-as-lists) (my-check-compare (lambda (x y) (vector-compare-as-list x y my-size my-ref)) (map list->my-list my-lists)) (my-check-compare (lambda (x y) (vector-compare-as-list ci x y my-size my-ref)) (map list->my-list my-lists)) ) ; check:data-structures (define (check:default-compare) (my-check-compare default-compare my-objects) ; check if default-compare refines pair-compare (my-check-ec (:list x (index ix) my-objects) (:list y (index iy) my-objects) (:let c-coarse (pair-compare x y)) (:let c-fine (default-compare x y)) (or (eqv? c-coarse 0) (eqv? c-fine c-coarse)) (list x y)) ; check if default-compare passes on debug-compare (my-check-compare (debug-compare default-compare) my-objects) ) ; check:default-compare (define (sort-by-less xs pred) ; trivial quicksort (if (or (null? xs) (null? (cdr xs))) xs (append (sort-by-less (list-ec (:list x (cdr xs)) (if (pred x (car xs))) x) pred) (list (car xs)) (sort-by-less (list-ec (:list x (cdr xs)) (if (not (pred x (car xs)))) x) pred)))) (define (check:more-examples) ; define recursive order on tree type (nodes are dotted pairs) (my-check-compare (letrec ((c (lambda (x y) (cond-compare (((null? x) (null? y)) 0) (else (pair-compare c c x y)))))) c) (list '() (list '()) (list '() '()) (list (list '()))) ;'(() (() . ()) (() . (() . ())) ((() . ()) . ())) ; Chicken can't parse this ? ) ; redefine default-compare using select-compare (my-check-compare (letrec ((c (lambda (x y) (select-compare x y (null? 0) (pair? (pair-compare c c x y)) (boolean? (boolean-compare x y)) (char? (char-compare x y)) (string? (string-compare x y)) (symbol? (symbol-compare x y)) (number? (number-compare x y)) (vector? (vector-compare c x y)) (else (error "unrecognized type in c" x y)))))) c) my-objects) ; redefine default-compare using cond-compare (my-check-compare (letrec ((c (lambda (x y) (cond-compare (((null? x) (null? y)) 0) (((pair? x) (pair? y)) (pair-compare c c x y)) (((boolean? x) (boolean? y)) (boolean-compare x y)) (((char? x) (char? y)) (char-compare x y)) (((string? x) (string? y)) (string-compare x y)) (((symbol? x) (symbol? y)) (symbol-compare x y)) (((number? x) (number? y)) (number-compare x y)) (((vector? x) (vector? y)) (vector-compare c x y)) (else (error "unrecognized type in c" x y)))))) c) my-objects) ; compare strings with character order reversed (my-check-compare (lambda (x y) (vector-compare-as-list (lambda (x y) (char-compare y x)) x y string-length string-ref)) '("" "b" "bb" "ba" "a" "ab" "aa")) ; examples from SRFI text for <? etc. (my-check (>? "laugh" "LOUD") => #t) (my-check (<? string-compare-ci "laugh" "LOUD") => #t) (my-check (sort-by-less '(1 a "b") (<?)) => '("b" a 1)) (my-check (sort-by-less '(1 a "b") (>?)) => '(1 a "b")) ) ; check:more-examples ; Real life examples ; ================== ; (update/insert compare x s) ; inserts x into list s, or updates an equivalent element by x. ; It is assumed that s is sorted with respect to compare, ; i.e. (apply chain<=? compare s). The result is a list with x ; replacing the first element s[i] for which (=? compare s[i] x), ; or with x inserted in the proper place. ; The algorithm uses linear insertion from the front. (define (insert/update compare x s) ; insert x into list s, or update (if (null? s) (list x) (if3 (compare x (car s)) (cons x s) (cons x (cdr s)) (cons (car s) (insert/update compare x (cdr s)))))) ; (index-in-vector compare vec x) ; an index i such that (=? compare vec[i] x), or #f if there is none. ; It is assumed that s is sorted with respect to compare, ; i.e. (apply chain<=? compare (vector->list s)). If there are ; several elements equivalent to x then it is unspecified which ; these is chosen. ; The algorithm uses binary search. (define (index-in-vector compare vec x) (let binary-search ((lo -1) (hi (vector-length vec))) ; invariant: vec[lo] < x < vec[hi] (if (=? (- hi lo) 1) #f (let ((mi (quotient (+ lo hi) 2))) (if3 (compare x (vector-ref vec mi)) (binary-search lo mi) mi (binary-search mi hi)))))) ; Run the checks ; ============== (my-check-reset) ; comment in/out as needed (check:atomic) (check:if3) (check:ifs) (check:predicates-from-compare) (check:pairwise-not=?) (check:min/max) ; (check:kth-largest) (check:compare-from-predicates) (check:refine-select-cond) (check:data-structures) (check:default-compare) (check:more-examples) (my-check-summary) ; all examples (99486) correct? |
Added srfi/tests/eager-comprehensions.sps.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 |
#!r6rs ; <PLAINTEXT> ; Examples for Eager Comprehensions in [outer..inner|expr]-Convention ; =================================================================== ; ; sebastian.egner@philips.com, Eindhoven, The Netherlands, 25-Apr-2005. ; Scheme R5RS (incl. macros), SRFI-23 (error). ; ; Running the examples in Scheme48 (version 1.1): ; ,open srfi-23 ; ,load ec.scm ; (define my-open-output-file open-output-file) ; (define my-call-with-input-file call-with-input-file) ; ,load examples.scm ; ; Running the examples in PLT/DrScheme (version 208): ; (load "ec.scm") ; (define (my-open-output-file filename) ; (open-output-file filename 'replace 'text) ) ; (define (my-call-with-input-file filename thunk) ; (call-with-input-file filename thunk 'text) ) ; (load "examples.scm") ; ; Running the examples in SCM (version 5d7): ; (require 'macro) (require 'record) ; (load "ec.scm") ; (define my-open-output-file open-output-file) ; (define my-call-with-input-file call-with-input-file) ; (load "examples.scm") (import (except (rnrs) error) (rnrs mutable-strings) (surfage s42 eager-comprehensions) (surfage s23 error)) (define (my-open-output-file filename) (open-file-output-port filename (file-options no-fail) 'block (native-transcoder))) (define (my-call-with-input-file filename thunk) (call-with-input-file filename thunk)) ; Tools for checking results ; ========================== (define (my-equal? x y) (cond ((or (boolean? x) (null? x) (symbol? x) (char? x) (input-port? x) (output-port? x) ) (eqv? x y) ) ((string? x) (and (string? y) (string=? x y)) ) ((vector? x) (and (vector? y) (my-equal? (vector->list x) (vector->list y)) )) ((pair? x) (and (pair? y) (my-equal? (car x) (car y)) (my-equal? (cdr x) (cdr y)) )) ((real? x) (and (real? y) (eqv? (exact? x) (exact? y)) (if (exact? x) (= x y) (< (abs (- x y)) (/ 1 (expt 10 6))) ))) ; will do here (else (error "unrecognized type" x) ))) (define my-check-correct 0) (define my-check-wrong 0) (define-syntax my-check (syntax-rules (=>) ((my-check ec => desired-result) (begin (newline) (write (quote ec)) (newline) (let ((actual-result ec)) (display " => ") (write actual-result) (if (my-equal? actual-result desired-result) (begin (display " ; correct") (set! my-check-correct (+ my-check-correct 1)) ) (begin (display " ; *** wrong ***, desired result:") (newline) (display " => ") (write desired-result) (set! my-check-wrong (+ my-check-wrong 1)) )) (newline) ))))) ; ========================================================================== ; do-ec ; ========================================================================== (my-check (let ((x 0)) (do-ec (set! x (+ x 1))) x) => 1) (my-check (let ((x 0)) (do-ec (:range i 10) (set! x (+ x 1))) x) => 10) (my-check (let ((x 0)) (do-ec (:range n 10) (:range k n) (set! x (+ x 1))) x) => 45) ; ========================================================================== ; list-ec and basic qualifiers ; ========================================================================== (my-check (list-ec 1) => '(1)) (my-check (list-ec (:range i 4) i) => '(0 1 2 3)) (my-check (list-ec (:range n 3) (:range k (+ n 1)) (list n k)) => '((0 0) (1 0) (1 1) (2 0) (2 1) (2 2)) ) (my-check (list-ec (:range n 5) (if (even? n)) (:range k (+ n 1)) (list n k)) => '((0 0) (2 0) (2 1) (2 2) (4 0) (4 1) (4 2) (4 3) (4 4)) ) (my-check (list-ec (:range n 5) (not (even? n)) (:range k (+ n 1)) (list n k)) => '((1 0) (1 1) (3 0) (3 1) (3 2) (3 3)) ) (my-check (list-ec (:range n 5) (and (even? n) (> n 2)) (:range k (+ n 1)) (list n k) ) => '((4 0) (4 1) (4 2) (4 3) (4 4)) ) (my-check (list-ec (:range n 5) (or (even? n) (> n 3)) (:range k (+ n 1)) (list n k) ) => '((0 0) (2 0) (2 1) (2 2) (4 0) (4 1) (4 2) (4 3) (4 4)) ) (my-check (let ((x 0)) (list-ec (:range n 10) (begin (set! x (+ x 1))) n) x) => 10 ) (my-check (list-ec (nested (:range n 3) (:range k n)) k) => '(0 0 1) ) ; ========================================================================== ; Other comprehensions ; ========================================================================== (my-check (append-ec '(a b)) => '(a b)) (my-check (append-ec (:range i 0) '(a b)) => '()) (my-check (append-ec (:range i 1) '(a b)) => '(a b)) (my-check (append-ec (:range i 2) '(a b)) => '(a b a b)) (my-check (string-ec #\a) => (string #\a)) (my-check (string-ec (:range i 0) #\a) => "") (my-check (string-ec (:range i 1) #\a) => "a") (my-check (string-ec (:range i 2) #\a) => "aa") (my-check (string-append-ec "ab") => "ab") (my-check (string-append-ec (:range i 0) "ab") => "") (my-check (string-append-ec (:range i 1) "ab") => "ab") (my-check (string-append-ec (:range i 2) "ab") => "abab") (my-check (vector-ec 1) => (vector 1)) (my-check (vector-ec (:range i 0) i) => (vector)) (my-check (vector-ec (:range i 1) i) => (vector 0)) (my-check (vector-ec (:range i 2) i) => (vector 0 1)) (my-check (vector-of-length-ec 1 1) => (vector 1)) (my-check (vector-of-length-ec 0 (:range i 0) i) => (vector)) (my-check (vector-of-length-ec 1 (:range i 1) i) => (vector 0)) (my-check (vector-of-length-ec 2 (:range i 2) i) => (vector 0 1)) (my-check (sum-ec 1) => 1) (my-check (sum-ec (:range i 0) i) => 0) (my-check (sum-ec (:range i 1) i) => 0) (my-check (sum-ec (:range i 2) i) => 1) (my-check (sum-ec (:range i 3) i) => 3) (my-check (product-ec 1) => 1) (my-check (product-ec (:range i 1 0) i) => 1) (my-check (product-ec (:range i 1 1) i) => 1) (my-check (product-ec (:range i 1 2) i) => 1) (my-check (product-ec (:range i 1 3) i) => 2) (my-check (product-ec (:range i 1 4) i) => 6) (my-check (min-ec 1) => 1) (my-check (min-ec (:range i 1) i) => 0) (my-check (min-ec (:range i 2) i) => 0) (my-check (max-ec 1) => 1) (my-check (max-ec (:range i 1) i) => 0) (my-check (max-ec (:range i 2) i) => 1) (my-check (first-ec #f 1) => 1) (my-check (first-ec #f (:range i 0) i) => #f) (my-check (first-ec #f (:range i 1) i) => 0) (my-check (first-ec #f (:range i 2) i) => 0) (my-check (let ((last-i -1)) (first-ec #f (:range i 10) (begin (set! last-i i)) i) last-i ) => 0 ) (my-check (last-ec #f 1) => 1) (my-check (last-ec #f (:range i 0) i) => #f) (my-check (last-ec #f (:range i 1) i) => 0) (my-check (last-ec #f (:range i 2) i) => 1) (my-check (any?-ec #f) => #f) (my-check (any?-ec #t) => #t) (my-check (any?-ec (:range i 2 2) (even? i)) => #f) (my-check (any?-ec (:range i 2 3) (even? i)) => #t) (my-check (every?-ec #f) => #f) (my-check (every?-ec #t) => #t) (my-check (every?-ec (:range i 2 2) (even? i)) => #t) (my-check (every?-ec (:range i 2 3) (even? i)) => #t) (my-check (every?-ec (:range i 2 4) (even? i)) => #f) (my-check (let ((sum-sqr (lambda (x result) (+ result (* x x))))) (fold-ec 0 (:range i 10) i sum-sqr) ) => 285 ) (my-check (let ((minus-1 (lambda (x) (- x 1))) (sum-sqr (lambda (x result) (+ result (* x x))))) (fold3-ec (error "wrong") (:range i 10) i minus-1 sum-sqr) ) => 284 ) (my-check (fold3-ec 'infinity (:range i 0) i min min) => 'infinity ) ; ========================================================================== ; Typed generators ; ========================================================================== (my-check (list-ec (:list x '()) x) => '()) (my-check (list-ec (:list x '(1)) x) => '(1)) (my-check (list-ec (:list x '(1 2 3)) x) => '(1 2 3)) (my-check (list-ec (:list x '(1) '(2)) x) => '(1 2)) (my-check (list-ec (:list x '(1) '(2) '(3)) x) => '(1 2 3)) (my-check (list-ec (:string c "") c) => '()) (my-check (list-ec (:string c "1") c) => '(#\1)) (my-check (list-ec (:string c "123") c) => '(#\1 #\2 #\3)) (my-check (list-ec (:string c "1" "2") c) => '(#\1 #\2)) (my-check (list-ec (:string c "1" "2" "3") c) => '(#\1 #\2 #\3)) (my-check (list-ec (:vector x (vector)) x) => '()) (my-check (list-ec (:vector x (vector 1)) x) => '(1)) (my-check (list-ec (:vector x (vector 1 2 3)) x) => '(1 2 3)) (my-check (list-ec (:vector x (vector 1) (vector 2)) x) => '(1 2)) (my-check (list-ec (:vector x (vector 1) (vector 2) (vector 3)) x) => '(1 2 3)) (my-check (list-ec (:range x -2) x) => '()) (my-check (list-ec (:range x -1) x) => '()) (my-check (list-ec (:range x 0) x) => '()) (my-check (list-ec (:range x 1) x) => '(0)) (my-check (list-ec (:range x 2) x) => '(0 1)) (my-check (list-ec (:range x 0 3) x) => '(0 1 2)) (my-check (list-ec (:range x 1 3) x) => '(1 2)) (my-check (list-ec (:range x -2 -1) x) => '(-2)) (my-check (list-ec (:range x -2 -2) x) => '()) (my-check (list-ec (:range x 1 5 2) x) => '(1 3)) (my-check (list-ec (:range x 1 6 2) x) => '(1 3 5)) (my-check (list-ec (:range x 5 1 -2) x) => '(5 3)) (my-check (list-ec (:range x 6 1 -2) x) => '(6 4 2)) (my-check (list-ec (:real-range x 0.0 3.0) x) => '(0. 1. 2.)) (my-check (list-ec (:real-range x 0 3.0) x) => '(0. 1. 2.)) (my-check (list-ec (:real-range x 0 3 1.0) x) => '(0. 1. 2.)) (my-check (string-ec (:char-range c #\a #\z) c) => "abcdefghijklmnopqrstuvwxyz" ) (my-check (begin (let ((f (my-open-output-file "tmp1"))) (do-ec (:range n 10) (begin (write n f) (newline f))) (close-output-port f)) (my-call-with-input-file "tmp1" (lambda (port) (list-ec (:port x port read) x)) )) => (list-ec (:range n 10) n) ) (my-check (begin (let ((f (my-open-output-file "tmp1"))) (do-ec (:range n 10) (begin (write n f) (newline f))) (close-output-port f)) (my-call-with-input-file "tmp1" (lambda (port) (list-ec (:port x port) x)) )) => (list-ec (:range n 10) n) ) ; ========================================================================== ; The special generators :do :let :parallel :while :until ; ========================================================================== (my-check (list-ec (:do ((i 0)) (< i 4) ((+ i 1))) i) => '(0 1 2 3)) (my-check (list-ec (:do (let ((x 'x))) ((i 0)) (< i 4) (let ((j (- 10 i)))) #t ((+ i 1)) ) j ) => '(10 9 8 7) ) (my-check (list-ec (:let x 1) x) => '(1)) (my-check (list-ec (:let x 1) (:let y (+ x 1)) y) => '(2)) (my-check (list-ec (:let x 1) (:let x (+ x 1)) x) => '(2)) (my-check (list-ec (:parallel (:range i 1 10) (:list x '(a b c))) (list i x)) => '((1 a) (2 b) (3 c)) ) (my-check (list-ec (:while (:range i 1 10) (< i 5)) i) => '(1 2 3 4) ) (my-check (list-ec (:until (:range i 1 10) (>= i 5)) i) => '(1 2 3 4 5) ) ; with generator that might use inner bindings (my-check (list-ec (:while (:list i '(1 2 3 4 5 6 7 8 9)) (< i 5)) i) => '(1 2 3 4) ) ; Was broken in original reference implementation as pointed ; out by sunnan@handgranat.org on 24-Apr-2005 comp.lang.scheme. ; Refer to http://groups-beta.google.com/group/comp.lang.scheme/ ; browse_thread/thread/f5333220eaeeed66/75926634cf31c038#75926634cf31c038 (my-check (list-ec (:until (:list i '(1 2 3 4 5 6 7 8 9)) (>= i 5)) i) => '(1 2 3 4 5) ) ; combine :while/:until and :parallel (my-check (list-ec (:while (:parallel (:range i 1 10) (:list j '(1 2 3 4 5 6 7 8 9))) (< i 5)) (list i j)) => '((1 1) (2 2) (3 3) (4 4))) (my-check (list-ec (:until (:parallel (:range i 1 10) (:list j '(1 2 3 4 5 6 7 8 9))) (>= i 5)) (list i j)) => '((1 1) (2 2) (3 3) (4 4) (5 5))) ; check that :while/:until really stop the generator (my-check (let ((n 0)) (do-ec (:while (:range i 1 10) (begin (set! n (+ n 1)) (< i 5))) (if #f #f)) n) => 5) (my-check (let ((n 0)) (do-ec (:until (:range i 1 10) (begin (set! n (+ n 1)) (>= i 5))) (if #f #f)) n) => 5) (my-check (let ((n 0)) (do-ec (:while (:parallel (:range i 1 10) (:do () (begin (set! n (+ n 1)) #t) ())) (< i 5)) (if #f #f)) n) => 5) (my-check (let ((n 0)) (do-ec (:until (:parallel (:range i 1 10) (:do () (begin (set! n (+ n 1)) #t) ())) (>= i 5)) (if #f #f)) n) => 5) ; ========================================================================== ; The dispatching generator ; ========================================================================== (my-check (list-ec (: c '(a b)) c) => '(a b)) (my-check (list-ec (: c '(a b) '(c d)) c) => '(a b c d)) (my-check (list-ec (: c "ab") c) => '(#\a #\b)) (my-check (list-ec (: c "ab" "cd") c) => '(#\a #\b #\c #\d)) (my-check (list-ec (: c (vector 'a 'b)) c) => '(a b)) (my-check (list-ec (: c (vector 'a 'b) (vector 'c)) c) => '(a b c)) (my-check (list-ec (: i 0) i) => '()) (my-check (list-ec (: i 1) i) => '(0)) (my-check (list-ec (: i 10) i) => '(0 1 2 3 4 5 6 7 8 9)) (my-check (list-ec (: i 1 2) i) => '(1)) (my-check (list-ec (: i 1 2 3) i) => '(1)) (my-check (list-ec (: i 1 9 3) i) => '(1 4 7)) (my-check (list-ec (: i 0.0 1.0 0.2) i) => '(0. 0.2 0.4 0.6 0.8)) (my-check (list-ec (: c #\a #\c) c) => '(#\a #\b #\c)) (my-check (begin (let ((f (my-open-output-file "tmp1"))) (do-ec (:range n 10) (begin (write n f) (newline f))) (close-output-port f)) (my-call-with-input-file "tmp1" (lambda (port) (list-ec (: x port read) x)) )) => (list-ec (:range n 10) n) ) (my-check (begin (let ((f (my-open-output-file "tmp1"))) (do-ec (:range n 10) (begin (write n f) (newline f))) (close-output-port f)) (my-call-with-input-file "tmp1" (lambda (port) (list-ec (: x port) x)) )) => (list-ec (:range n 10) n) ) ; ========================================================================== ; With index variable ; ========================================================================== (my-check (list-ec (:list c (index i) '(a b)) (list c i)) => '((a 0) (b 1))) (my-check (list-ec (:string c (index i) "a") (list c i)) => '((#\a 0))) (my-check (list-ec (:vector c (index i) (vector 'a)) (list c i)) => '((a 0))) (my-check (list-ec (:range i (index j) 0 -3 -1) (list i j)) => '((0 0) (-1 1) (-2 2)) ) (my-check (list-ec (:real-range i (index j) 0 1 0.2) (list i j)) => '((0. 0) (0.2 1) (0.4 2) (0.6 3) (0.8 4)) ) (my-check (list-ec (:char-range c (index i) #\a #\c) (list c i)) => '((#\a 0) (#\b 1) (#\c 2)) ) (my-check (list-ec (: x (index i) '(a b c d)) (list x i)) => '((a 0) (b 1) (c 2) (d 3)) ) (my-check (begin (let ((f (my-open-output-file "tmp1"))) (do-ec (:range n 10) (begin (write n f) (newline f))) (close-output-port f)) (my-call-with-input-file "tmp1" (lambda (port) (list-ec (: x (index i) port) (list x i))) )) => '((0 0) (1 1) (2 2) (3 3) (4 4) (5 5) (6 6) (7 7) (8 8) (9 9)) ) ; ========================================================================== ; The examples from the SRFI document ; ========================================================================== ; from Abstract (my-check (list-ec (: i 5) (* i i)) => '(0 1 4 9 16)) (my-check (list-ec (: n 1 4) (: i n) (list n i)) => '((1 0) (2 0) (2 1) (3 0) (3 1) (3 2)) ) ; from Generators (my-check (list-ec (: x (index i) "abc") (list x i)) => '((#\a 0) (#\b 1) (#\c 2)) ) (my-check (list-ec (:string c (index i) "a" "b") (cons c i)) => '((#\a . 0) (#\b . 1)) ) ; ========================================================================== ; Little Shop of Horrors ; ========================================================================== (my-check (list-ec (:range x 5) (:range x x) x) => '(0 0 1 0 1 2 0 1 2 3)) (my-check (list-ec (:list x '(2 "23" (4))) (: y x) y) => '(0 1 #\2 #\3 4)) (my-check (list-ec (:parallel (:integers x) (:do ((i 10)) (< x i) ((- i 1)))) (list x i)) => '((0 10) (1 9) (2 8) (3 7) (4 6)) ) ; ========================================================================== ; Less artificial examples ; ========================================================================== (define (factorial n) ; n * (n-1) * .. * 1 for n >= 0 (product-ec (:range k 2 (+ n 1)) k) ) (my-check (factorial 0) => 1) (my-check (factorial 1) => 1) (my-check (factorial 3) => 6) (my-check (factorial 5) => 120) (define (eratosthenes n) ; primes in {2..n-1} for n >= 1 (let ((p? (make-string n #\1))) (do-ec (:range k 2 n) (if (char=? (string-ref p? k) #\1)) (:range i (* 2 k) n k) (string-set! p? i #\0) ) (list-ec (:range k 2 n) (if (char=? (string-ref p? k) #\1)) k) )) (my-check (eratosthenes 50) => '(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47) ) (my-check (length (eratosthenes 100000)) => 9592 ) ; we expect 10^5/ln(10^5) (define (pythagoras n) ; a, b, c s.t. 1 <= a <= b <= c <= n, a^2 + b^2 = c^2 (list-ec (:let sqr-n (* n n)) (:range a 1 (+ n 1)) ; (begin (display a) (display " ")) (:let sqr-a (* a a)) (:range b a (+ n 1)) (:let sqr-c (+ sqr-a (* b b))) (if (<= sqr-c sqr-n)) (:range c b (+ n 1)) (if (= (* c c) sqr-c)) (list a b c) )) (my-check (pythagoras 15) => '((3 4 5) (5 12 13) (6 8 10) (9 12 15)) ) (my-check (length (pythagoras 200)) => 127 ) (define (qsort xs) ; stable (if (null? xs) '() (let ((pivot (car xs)) (xrest (cdr xs))) (append (qsort (list-ec (:list x xrest) (if (< x pivot)) x)) (list pivot) (qsort (list-ec (:list x xrest) (if (>= x pivot)) x)) )))) (my-check (qsort '(1 5 4 2 4 5 3 2 1 3)) => '(1 1 2 2 3 3 4 4 5 5) ) (define (pi-BBP m) ; approx. of pi within 16^-m (Bailey-Borwein-Plouffe) (sum-ec (:range n 0 (+ m 1)) (:let n8 (* 8 n)) (* (- (/ 4 (+ n8 1)) (+ (/ 2 (+ n8 4)) (/ 1 (+ n8 5)) (/ 1 (+ n8 6)))) (/ 1 (expt 16 n)) ))) (my-check (pi-BBP 5) => (/ 40413742330349316707 12864093722915635200) ) (define (read-lines filename) ; list of all lines (my-call-with-input-file filename (lambda (port) (list-ec (:port line port get-line) line) ))) (my-check (begin (let ((f (my-open-output-file "tmp1"))) (do-ec (:range n 10) (begin (write n f) (newline f))) (close-output-port f)) (read-lines "tmp1") ) => (list-ec (:char-range c #\0 #\9) (string c)) ) ; ========================================================================== ; Summary ; ========================================================================== (begin (newline) (newline) (display "correct examples : ") (display my-check-correct) (newline) (display "wrong examples : ") (display my-check-wrong) (newline) (newline) ) |
Added srfi/tests/intermediate-format-strings.sps.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
#!r6rs (import (rnrs) (rnrs mutable-pairs) (surfage s48 intermediate-format-strings) (surfage s78 lightweight-testing)) #;(define (format-lots n f fmt-str . args) (let loop ([i 0] [r #f]) (if (= i n) r (loop (+ 1 i) (apply f fmt-str args))))) (define-syntax expect (syntax-rules () [(_ expected expr) (check expr => expected)])) ;;;=================================================== (expect (format "test ~s" 'me) (format #f "test ~a" "me")) (check (format "~6,3F" 1/3) (=> member) '(" 0.333" " .333")) (expect " 12" (format "~4F" 12)) (expect " 12.346" (format "~8,3F" 12.3456)) (expect "123.346" (format "~6,3F" 123.3456)) (expect "123.346" (format "~4,3F" 123.3456)) (expect "0.000+1.949i" (format "~8,3F" (sqrt -3.8))) (expect " 32.00" (format "~6,2F" 32)) (expect " 32" (format "~6F" 32)) (check (format "~6F" 32.) ;; NB: (not (and (exact? 32.) (integer? 32.))) (=> member) '(" 32.0" " 32.")) (check (format "~8F" 32e45) (=> member) '(" 3.2e46" " 3.2e+46")) (expect " 3.2e-44" (format "~8,1F" 32e-45)) (check (format "~8F" 32e20) (=> member) '(" 3.2e21" " 3.2e+21")) (check (format "~8F" 32e5) (=> member) '("3200000.0" " 3.2e6" " 3.2e+6")) (check (format "~8F" 32e2) (=> member) '(" 3200.0" " 3200.")) (check (format "~8,2F" 32e10) (=> member) '(" 3.20e11" "3.20e+11" "320000000000.00")) (check (format "~0,3F" 20263/2813) (=> member) '( "7.203" )) (check (format "~0,2F" 20263/2813) (=> member) '( "7.20" )) (expect " 1.2345" (format "~12F" 1.2345)) (expect " 1.23" (format "~12,2F" 1.2345)) (expect " 1.234" (format "~12,3F" 1.2345)) ;; "round to even" (expect " 0.000+1.949i" (format "~20,3F" (sqrt -3.8))) (expect "0.000+1.949i" (format "~8,3F" (sqrt -3.8))) (check (format "~8,2F" 3.4567e11) (=> member) '(" 3.46e11" "3.46e+11" "345670000000.00")) (check (format "~w" (let ( (c (list 'a 'b 'c)) ) (set-cdr! (cddr c) c) c)) (=> member) '("#0=(a b c . #0#)" "#1=(a b c . #1#)")) (expect " " (format "~A~A~&" (list->string (list #\newline)) "")) (expect "a new test" (format "~a ~? ~a" 'a "~a" '(new) 'test)) (expect "a \"new\" test" (format "~a ~? ~a" 'a "~s" '("new") 'test)) ;; from SLIB (define-syntax test (syntax-rules () [(test <format-args> <expected>) (check (apply format <format-args>) => <expected>)])) (test '("abc") "abc") (test '("~a" 10) "10") (test '("~a" -1.2) "-1.2") (test '("~a" a) "a") (test '("~a" #t) "#t") (test '("~a" #f) "#f") (test '("~a" "abc") "abc") (test '("~a" #(1 2 3)) "#(1 2 3)") (test '("~a" ()) "()") (test '("~a" (a)) "(a)") (test '("~a" (a b)) "(a b)") (test '("~a" (a (b c) d)) "(a (b c) d)") (test '("~a" (a . b)) "(a . b)") (test '("~a" (a (b c . d))) "(a (b c . d))") ; # argument test (test '("~a ~a" 10 20) "10 20") (test '("~a abc ~a def" 10 20) "10 abc 20 def") ; numerical test (test '("~d" 100) "100") (test '("~x" 100) "64") (test '("~o" 100) "144") (test '("~b" 100) "1100100") ; character test (test '("~c" #\a) "a") ; tilde test (test '("~~~~") "~~") ; whitespace character test (test '("~%") " ") (test '("~&") " ") (test '("abc~&") "abc ") (test '("abc~&def") "abc def") (test '("~&") " ") (test '("~_~_~_") " ") ; indirection test (test '("~a ~? ~a" 10 "~a ~a" (20 30) 40) "10 20 30 40") ; slashify test (test '("~s" "abc") "\"abc\"") (test '("~s" "abc \\ abc") "\"abc \\\\ abc\"") (test '("~a" "abc \\ abc") "abc \\ abc") (test '("~s" "abc \" abc") "\"abc \\\" abc\"") (test '("~a" "abc \" abc") "abc \" abc") (test '("~s" #\space) "#\\space") ;(test '("~s" #\newline) "#\\newline") (test '("~s" #\a) "#\\a") (test '("~s" (a "b" c)) "(a \"b\" c)") (test '("~a" (a "b" c)) "(a b c)") ; fixed floating points (test '("~6,2f" 3.14159) " 3.14") (test '("~6,1f" 3.14159) " 3.1") (test '("~6,0f" 3.14159) " 3.") (test '("~5,1f" 0) " 0.0") (test '("~10,7f" 3.14159) " 3.1415900") (test '("~10,7f" -3.14159) "-3.1415900") (test '("~6,3f" 0.0) " 0.000") (check (format "~6,4f" 0.007) (=> member) '(" 7e-3" "0.0070" ".0070")) (check (format "~6,3f" 0.007) (=> member) '(" 7e-3" " 0.007")) (check (format "~6,2f" 0.007) (=> member) '(" 7e-3" " 0.01")) (check (format "~3,2f" 0.007) (=> member) '("7e-3" ".01" "0.01")) (check (format "~3,2f" -0.007) (=> member) '("-7e-3" "-.01" "-0.01")) (test '("~6,3f" 12345.6789) "12345.679") (test '("~6f" 23.4) " 23.4") (test '("~6f" 1234.5) "1234.5") (test '("~6f" 12345678) "12345678") (test '("~6,2f" 123.56789) "123.57") (test '("~6f" 123.0) " 123.0") (test '("~6f" -123.0) "-123.0") (test '("~6f" 0.0) " 0.0") (test '("~3,1f" 3.141) "3.1") (test '("~2,0f" 3.141) "3.") (test '("~1f" 3.141) "3.141") (test '("~f" 123.56789) "123.56789") (test '("~f" -314.0) "-314.0") (check (format "~f" 1e4) (=> member) '("1e4" "10000.0")) (check (format "~f" -1.23e10) (=> member) '("-1.23e10" "-1.23e+10" "-12300000000.0" "-12300000000.")) (check (format "~f" 1e-4) (=> member) '("1e-4" "0.0001" ".0001")) (check (format "~f" -1.23e-10) (=> member) '("-0.000000000123" "-1.23e-10")) (check-report) ;; #!eof |
Added srfi/tests/lists.sps.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 |
; Test suite for SRFI-1 ; 2003-12-29 / lth ; ; Note: In Larceny, we require that the procedures designated as ; "linear update" variants in the spec (eg append!) side-effect their ; arguments, and there are tests here that check that side-effecting ; occurs. ; ; For linear update we only require that the cells of the result are ; taken from the cells of the input. We could be stricter and require ; that the cells of the results are the cells of the input with only ; the CDR changed, ie, values are never moved from one cell to another. (import (except (rnrs base) map for-each) (rnrs io simple) (rnrs r5rs) (surfage s1 lists)) (define (writeln . xs) (for-each display xs) (newline)) (define (fail token . more) (writeln "Error: test failed: " token) #f) ; Test cases are ordered as in the spec. R5RS procedures are left out. (or (equal? (xcons 1 2) '(2 . 1)) (fail 'xcons:1)) (or (equal? (cons* 1) 1) (fail 'cons*:1)) (or (equal? (cons* 1 2 3 4 5) '(1 2 3 4 . 5)) (fail 'cons*:2)) (or (equal? (make-list 5 #t) '(#t #t #t #t #t)) (fail 'make-list:1)) (or (equal? (make-list 0 #f) '()) (fail 'make-list:2)) (or (equal? (length (make-list 3)) 3) (fail 'make-list:3)) (or (equal? (list-tabulate 5 (lambda (x) x)) '(0 1 2 3 4)) (fail 'list-tabulate:1)) (or (equal? (list-tabulate 0 (lambda (x) (error #f "FOO!"))) '()) (fail 'list-tabluate:2)) (or (call-with-current-continuation (lambda (abort) (let* ((c (list 1 2 3 4 5)) (cp (list-copy c))) (or (equal? c cp) (abort #f)) (let loop ((c c) (cp cp)) (if (not (null? c)) (begin (or (not (eq? c cp)) (abort #f)) (loop (cdr c) (cdr cp))))) #t))) (fail 'list-copy:1)) (or (equal? (list-copy '(1 2 3 . 4)) '(1 2 3 . 4)) (fail 'list-copy:2)) (or (not (list? (circular-list 1 2 3))) (fail 'circular-list:1)) (or (let* ((a (list 'a)) (b (list 'b)) (c (list 'c)) (x (circular-list a b c))) (and (eq? a (car x)) (eq? b (cadr x)) (eq? c (caddr x)) (eq? a (cadddr x)))) (fail 'circular-list:2)) (or (equal? (iota 0) '()) (fail 'iota:1)) (or (equal? (iota 5 2 3) '(2 5 8 11 14)) (fail 'iota:2)) (or (equal? (iota 5 2) '(2 3 4 5 6)) (fail 'iota:3)) (or (proper-list? '(1 2 3 4 5)) (fail 'proper-list?:1)) (or (proper-list? '()) (fail 'proper-list?:2)) (or (not (proper-list? '(1 2 . 3))) (fail 'proper-list?:3)) (or (not (proper-list? (circular-list 1 2 3))) (fail 'proper-list:4)) (or (not (circular-list? '(1 2 3 4 5))) (fail 'circular-list?:1)) (or (not (circular-list? '())) (fail 'circular-list?:2)) (or (not (circular-list? '(1 2 . 3))) (fail 'circular-list?:3)) (or (circular-list? (circular-list 1 2 3)) (fail 'circular-list:4)) (or (not (dotted-list? '(1 2 3 4 5))) (fail 'dotted-list?:1)) (or (not (dotted-list? '())) (fail 'dotted-list?:2)) (or (dotted-list? '(1 2 . 3)) (fail 'dotted-list?:3)) (or (not (dotted-list? (circular-list 1 2 3))) (fail 'dotted-list:4)) (or (null-list? '()) (fail 'null-list?:1)) (or (not (null-list? '(1 2))) (fail 'null-list?:2)) (or (not (null-list? (circular-list 1 2))) (fail 'null-list?:3)) (or (not-pair? 1) (fail 'not-pair:1)) (or (not (not-pair? (cons 1 2))) (fail 'not-pair:2)) (or (list= = '(1 2 3) '(1 2 3) '(1 2 3)) (fail 'list=:1)) (or (not (list= = '(1 2 3) '(1 2 3) '(1 4 3))) (fail 'list=:2)) ; Checks that l0 is not being used when testing l2, cf spec (or (list= (lambda (a b) (not (eq? a b))) '(#f #f #f) '(#t #t #t) '(#f #f #f)) (fail 'list=:3)) (or (list= =) (fail 'list=:4)) (or (= (first '(1 2 3 4 5 6 7 8 9 10)) 1) (fail 'first)) (or (= (second '(1 2 3 4 5 6 7 8 9 10)) 2) (fail 'second)) (or (= (third '(1 2 3 4 5 6 7 8 9 10)) 3) (fail 'third)) (or (= (fourth '(1 2 3 4 5 6 7 8 9 10)) 4) (fail 'fourth)) (or (= (fifth '(1 2 3 4 5 6 7 8 9 10)) 5) (fail 'fifth)) (or (= (sixth '(1 2 3 4 5 6 7 8 9 10)) 6) (fail 'sixth)) (or (= (seventh '(1 2 3 4 5 6 7 8 9 10)) 7) (fail 'seventh)) (or (= (eighth '(1 2 3 4 5 6 7 8 9 10)) 8) (fail 'eighth)) (or (= (ninth '(1 2 3 4 5 6 7 8 9 10)) 9) (fail 'ninth)) (or (= (tenth '(1 2 3 4 5 6 7 8 9 10)) 10) (fail 'tenth)) (let-values (((a b) (car+cdr '(1 . 2)))) (or (and (= a 1) (= b 2)) (fail 'car+cdr:1))) (or (equal? '(1 2 3) (take '(1 2 3 4 5 6) 3)) (fail 'take:1)) (or (equal? '(1) (take '(1) 1)) (fail 'take:2)) (or (let ((x (list 1 2 3 4 5 6))) (eq? (cdddr x) (drop x 3))) (fail 'drop:1)) (or (let ((x (list 1 2 3))) (eq? x (drop x 0))) (fail 'drop:2)) (or (equal? '(4 5 6) (take-right '(1 2 3 4 5 6) 3)) (fail 'take-right:1)) (or (null? (take-right '(1 2 3 4 5 6) 0)) (fail 'take-right:2)) (or (equal? '(2 3 . 4) (take-right '(1 2 3 . 4) 2)) (fail 'take-right:3)) (or (equal? 4 (take-right '(1 2 3 . 4) 0)) (fail 'take-right:4)) (or (equal? '(1 2 3) (drop-right '(1 2 3 4 5 6) 3)) (fail 'drop-right:1)) (or (equal? '(1 2 3) (drop-right '(1 2 3) 0)) (fail 'drop-right:2)) (or (equal? '(1 2 3) (drop-right '(1 2 3 . 4) 0)) (fail 'drop-right:3)) (or (let ((x (list 1 2 3 4 5 6))) (let ((y (take! x 3))) (and (eq? x y) (eq? (cdr x) (cdr y)) (eq? (cddr x) (cddr y)) (equal? y '(1 2 3))))) (fail 'take!:1)) (or (let ((x (list 1 2 3 4 5 6))) (let ((y (drop-right! x 3))) (and (eq? x y) (eq? (cdr x) (cdr y)) (eq? (cddr x) (cddr y)) (equal? y '(1 2 3))))) (fail 'drop-right!:1)) (or (let-values (((a b) (split-at '(1 2 3 4 5 6) 2))) (and (equal? a '(1 2)) (equal? b '(3 4 5 6)))) (fail 'split-at:1)) (or (let* ((x (list 1 2 3 4 5 6)) (y (cddr x))) (let-values (((a b) (split-at! x 2))) (and (equal? a '(1 2)) (eq? a x) (equal? b '(3 4 5 6)) (eq? b y)))) (fail 'split-at!:1)) (or (eqv? 37 (last '(1 2 3 37))) (fail 'last:1)) (or (not (length+ (circular-list 1 2 3))) (fail 'length+:1)) (or (equal? 4 (length+ '(1 2 3 4))) (fail 'length+:2)) (or (let ((x (list 1 2)) (y (list 3 4)) (z (list 5 6))) (let ((r (append! x y '() z))) (and (equal? r '(1 2 3 4 5 6)) (eq? r x) (eq? (cdr r) (cdr x)) (eq? (cddr r) y) (eq? (cdddr r) (cdr y)) (eq? (cddddr r) z) (eq? (cdr (cddddr r)) (cdr z))))) (fail 'append!:1)) (or (equal? (concatenate '((1 2 3) (4 5 6) () (7 8 9))) '(1 2 3 4 5 6 7 8 9)) (fail 'concatenate:1)) (or (equal? (concatenate! `(,(list 1 2 3) ,(list 4 5 6) () ,(list 7 8 9))) '(1 2 3 4 5 6 7 8 9)) (fail 'concatenate!:1)) (or (equal? (append-reverse '(3 2 1) '(4 5 6)) '(1 2 3 4 5 6)) (fail 'append-reverse:1)) (or (equal? (append-reverse! (list 3 2 1) (list 4 5 6)) '(1 2 3 4 5 6)) (fail 'append-reverse!:1)) (or (equal? (zip '(1 2 3) '(4 5 6)) '((1 4) (2 5) (3 6))) (fail 'zip:1)) (or (equal? (zip '() '() '() '()) '()) (fail 'zip:2)) (or (equal? (zip '(1) (circular-list 1 2)) '((1 1))) (fail 'zip:3)) (or (equal? '(1 2 3 4 5) (unzip1 '((1) (2) (3) (4) (5)))) (fail 'unzip1:1)) (or (let-values (((a b) (unzip2 '((10 11) (20 21) (30 31))))) (and (equal? a '(10 20 30)) (equal? b '(11 21 31)))) (fail 'unzip2:1)) (or (let-values (((a b c) (unzip3 '((10 11 12) (20 21 22) (30 31 32))))) (and (equal? a '(10 20 30)) (equal? b '(11 21 31)) (equal? c '(12 22 32)))) (fail 'unzip3:1)) (or (let-values (((a b c d) (unzip4 '((10 11 12 13) (20 21 22 23) (30 31 32 33))))) (and (equal? a '(10 20 30)) (equal? b '(11 21 31)) (equal? c '(12 22 32)) (equal? d '(13 23 33)))) (fail 'unzip4:1)) (or (let-values (((a b c d e) (unzip5 '((10 11 12 13 14) (20 21 22 23 24) (30 31 32 33 34))))) (and (equal? a '(10 20 30)) (equal? b '(11 21 31)) (equal? c '(12 22 32)) (equal? d '(13 23 33)) (equal? e '(14 24 34)))) (fail 'unzip5:1)) (or (equal? 3 (count even? '(3 1 4 1 5 9 2 5 6))) (fail 'count:1)) (or (equal? 3 (count < '(1 2 4 8) '(2 4 6 8 10 12 14 16))) (fail 'count:2)) (or (equal? 2 (count < '(3 1 4 1) (circular-list 1 10))) (fail 'count:3)) (or (equal? '(c 3 b 2 a 1) (fold cons* '() '(a b c) '(1 2 3 4 5))) (fail 'fold:1)) (or (equal? '(a 1 b 2 c 3) (fold-right cons* '() '(a b c) '(1 2 3 4 5))) (fail 'fold-right:1)) (or (let* ((x (list 1 2 3)) (r (list x (cdr x) (cddr x))) (y (pair-fold (lambda (pair tail) (set-cdr! pair tail) pair) '() x))) (and (equal? y '(3 2 1)) (every (lambda (c) (memq c r)) (list y (cdr y) (cddr y))))) (fail 'pair-fold:1)) (or (equal? '((a b c) (b c) (c)) (pair-fold-right cons '() '(a b c))) (fail 'pair-fold-right:1)) (or (equal? 5 (reduce max 'illegal '(1 2 3 4 5))) (fail 'reduce:1)) (or (equal? 0 (reduce max 0 '())) (fail 'reduce:2)) (or (equal? '(1 2 3 4 5) (reduce-right append 'illegal '((1 2) () (3 4 5)))) (fail 'reduce-right:1)) (or (equal? '(1 4 9 16 25 36 49 64 81 100) (unfold (lambda (x) (> x 10)) (lambda (x) (* x x)) (lambda (x) (+ x 1)) 1)) (fail 'unfold:1)) (or (equal? '(1 4 9 16 25 36 49 64 81 100) (unfold-right zero? (lambda (x) (* x x)) (lambda (x) (- x 1)) 10)) (fail 'unfold-right:1)) (or (equal? '(4 1 5 1) (map + '(3 1 4 1) (circular-list 1 0))) (fail 'map:1)) (or (equal? '(5 4 3 2 1) (let ((v 1) (l '())) (for-each (lambda (x y) (let ((n v)) (set! v (+ v 1)) (set! l (cons n l)))) '(0 0 0 0 0) (circular-list 1 2)) l)) (fail 'for-each:1)) (or (equal? '(1 -1 3 -3 8 -8) (append-map (lambda (x) (list x (- x))) '(1 3 8))) (fail 'append-map:1)) (or (equal? '(1 -1 3 -3 8 -8) (append-map! (lambda (x) (list x (- x))) '(1 3 8))) (fail 'append-map!:1)) (or (let* ((l (list 1 2 3)) (m (map! (lambda (x) (* x x)) l))) (and (equal? m '(1 4 9)) (equal? l '(1 4 9)))) (fail 'map!:1)) (or (equal? '(1 2 3 4 5) (let ((v 1)) (map-in-order (lambda (x) (let ((n v)) (set! v (+ v 1)) n)) '(0 0 0 0 0)))) (fail 'map-in-order:1)) (or (equal? '((3) (2 3) (1 2 3)) (let ((xs (list 1 2 3)) (l '())) (pair-for-each (lambda (x) (set! l (cons x l))) xs) l)) (fail 'pair-for-each:1)) (or (equal? '(1 9 49) (filter-map (lambda (x y) (and (number? x) (* x x))) '(a 1 b 3 c 7) (circular-list 1 2))) (fail 'filter-map:1)) (or (equal? '(0 8 8 -4) (filter even? '(0 7 8 8 43 -4))) (fail 'filter:1)) (or (let-values (((a b) (partition symbol? '(one 2 3 four five 6)))) (and (equal? a '(one four five)) (equal? b '(2 3 6)))) (fail 'partition:1)) (or (equal? '(7 43) (remove even? '(0 7 8 8 43 -4))) (fail 'remove:1)) (or (let* ((x (list 0 7 8 8 43 -4)) (y (pair-fold cons '() x)) (r (filter! even? x))) (and (equal? '(0 8 8 -4) r) (every (lambda (c) (memq c y)) (pair-fold cons '() r)))) (fail 'filter!:1)) (or (let* ((x (list 'one 2 3 'four 'five 6)) (y (pair-fold cons '() x))) (let-values (((a b) (partition! symbol? x))) (and (equal? a '(one four five)) (equal? b '(2 3 6)) (every (lambda (c) (memq c y)) (pair-fold cons '() a)) (every (lambda (c) (memq c y)) (pair-fold cons '() b))))) (fail 'partition!:1)) (or (let* ((x (list 0 7 8 8 43 -4)) (y (pair-fold cons '() x)) (r (remove! even? x))) (and (equal? '(7 43) r) (every (lambda (c) (memq c y)) (pair-fold cons '() r)))) (fail 'remove!:1)) (or (equal? 4 (find even? '(3 1 4 1 5 9 8))) (fail 'find:1)) (or (equal? '(4 1 5 9 8) (find-tail even? '(3 1 4 1 5 9 8))) (fail 'find-tail:1)) (or (equal? '#f (find-tail even? '(1 3 5 7))) (fail 'find-tail:2)) (or (equal? '(2 18) (take-while even? '(2 18 3 10 22 9))) (fail 'take-while:1)) (or (let* ((x (list 2 18 3 10 22 9)) (r (take-while! even? x))) (and (equal? r '(2 18)) (eq? r x) (eq? (cdr r) (cdr x)))) (fail 'take-while!:1)) (or (equal? '(3 10 22 9) (drop-while even? '(2 18 3 10 22 9))) (fail 'drop-while:1)) (or (let-values (((a b) (span even? '(2 18 3 10 22 9)))) (and (equal? a '(2 18)) (equal? b '(3 10 22 9)))) (fail 'span:1)) (or (let-values (((a b) (break even? '(3 1 4 1 5 9)))) (and (equal? a '(3 1)) (equal? b '(4 1 5 9)))) (fail 'break:1)) (or (let* ((x (list 2 18 3 10 22 9)) (cells (pair-fold cons '() x))) (let-values (((a b) (span! even? x))) (and (equal? a '(2 18)) (equal? b '(3 10 22 9)) (every (lambda (x) (memq x cells)) (pair-fold cons '() a)) (every (lambda (x) (memq x cells)) (pair-fold cons '() b))))) (fail 'span!:1)) (or (let* ((x (list 3 1 4 1 5 9)) (cells (pair-fold cons '() x))) (let-values (((a b) (break! even? x))) (and (equal? a '(3 1)) (equal? b '(4 1 5 9)) (every (lambda (x) (memq x cells)) (pair-fold cons '() a)) (every (lambda (x) (memq x cells)) (pair-fold cons '() b))))) (fail 'break!:1)) (or (any integer? '(a 3 b 2.7)) (fail 'any:1)) (or (not (any integer? '(a 3.1 b 2.7))) (fail 'any:2)) (or (any < '(3 1 4 1 5) (circular-list 2 7 1 8 2)) (fail 'any:3)) (or (equal? 'yes (any (lambda (a b) (if (< a b) 'yes #f)) '(1 2 3) '(0 1 4))) (fail 'any:4)) (or (every integer? '(1 2 3)) (fail 'every:1)) (or (not (every integer? '(3 4 5.1))) (fail 'every:2)) (or (every < '(1 2 3) (circular-list 2 3 4)) (fail 'every:3)) (or (equal? 2 (list-index even? '(3 1 4 1 5 9))) (fail 'list-index:1)) (or (equal? 1 (list-index < '(3 1 4 1 5 9 2 5 6) '(2 7 1 8 2))) (fail 'list-index:2)) (or (not (list-index = '(3 1 4 1 5 9 2 5 6) '(2 7 1 8 2))) (fail 'list-index:3)) (or (equal? '(37 48) (member 5 '(1 2 5 37 48) <)) (fail 'member:1)) (or (equal? '(1 2 5) (delete 5 '(1 48 2 5 37) <)) (fail 'delete:1)) (or (equal? '(1 2 7) (delete 5 '(1 5 2 5 7))) (fail 'delete:2)) (or (let* ((x (list 1 48 2 5 37)) (cells (pair-fold cons '() x)) (r (delete! 5 x <))) (and (equal? r '(1 2 5)) (every (lambda (x) (memq x cells)) (pair-fold cons '() r)))) (fail 'delete!:1)) (or (equal? '((a . 3) (b . 7) (c . 1)) (delete-duplicates '((a . 3) (b . 7) (a . 9) (c . 1)) (lambda (x y) (eq? (car x) (car y))))) (fail 'delete-duplicates:1)) (or (equal? '(a b c z) (delete-duplicates '(a b a c a b c z) eq?)) (fail 'delete-duplicates:2)) (or (let* ((x (list 'a 'b 'a 'c 'a 'b 'c 'z)) (cells (pair-fold cons '() x)) (r (delete-duplicates! x))) (and (equal? '(a b c z) r) (every (lambda (x) (memq x cells)) (pair-fold cons '() r)))) (fail 'delete-duplicates!:1)) (or (equal? '(3 . #t) (assoc 6 '((4 . #t) (3 . #t) (5 . #t)) (lambda (x y) (zero? (remainder x y))))) (fail 'assoc:1)) (or (equal? '((1 . #t) (2 . #f)) (alist-cons 1 #t '((2 . #f)))) (fail 'alist-cons:1)) (or (let* ((a (list (cons 1 2) (cons 3 4))) (b (alist-copy a))) (and (equal? a b) (every (lambda (x) (not (memq x b))) a) (every (lambda (x) (not (memq x a))) b))) (fail 'alist-copy:1)) (or (equal? '((1 . #t) (2 . #t) (4 . #t)) (alist-delete 5 '((1 . #t) (2 . #t) (37 . #t) (4 . #t) (48 #t)) <)) (fail 'alist-delete:1)) (or (equal? '((1 . #t) (2 . #t) (4 . #t)) (alist-delete 7 '((1 . #t) (2 . #t) (7 . #t) (4 . #t) (7 #t)))) (fail 'alist-delete:2)) (or (let* ((x (list-copy '((1 . #t) (2 . #t) (7 . #t) (4 . #t) (7 #t)))) (y (list-copy x)) (cells (pair-fold cons '() x)) (r (alist-delete! 7 x))) (and (equal? r '((1 . #t) (2 . #t) (4 . #t))) (every (lambda (x) (memq x cells)) (pair-fold cons '() r)) (every (lambda (x) (memq x y)) r))) (fail 'alist-delete!:1)) (or (lset<= eq? '(a) '(a b a) '(a b c c)) (fail 'lset<=:1)) (or (not (lset<= eq? '(a) '(a b a) '(a))) (fail 'lset<=:2)) (or (lset<= eq?) (fail 'lset<=:3)) (or (lset<= eq? '(a)) (fail 'lset<=:4)) (or (lset= eq? '(b e a) '(a e b) '(e e b a)) (fail 'lset=:1)) (or (not (lset= eq? '(b e a) '(a e b) '(e e b a c))) (fail 'lset=:2)) (or (lset= eq?) (fail 'lset=:3)) (or (lset= eq? '(a)) (fail 'lset=:4)) (or (equal? '(u o i a b c d c e) (lset-adjoin eq? '(a b c d c e) 'a 'e 'i 'o 'u)) (fail 'lset-adjoin:1)) (or (equal? '(u o i a b c d e) (lset-union eq? '(a b c d e) '(a e i o u))) (fail 'lset-union:1)) (or (equal? '(x a a c) (lset-union eq? '(a a c) '(x a x))) (fail 'lset-union:2)) (or (null? (lset-union eq?)) (fail 'lset-union:3)) (or (equal? '(a b c) (lset-union eq? '(a b c))) (fail 'lset-union:4)) (or (equal? '(a e) (lset-intersection eq? '(a b c d e) '(a e i o u))) (fail 'lset-intersection:1)) (or (equal? '(a x a) (lset-intersection eq? '(a x y a) '(x a x z))) (fail 'lset-intersection:2)) (or (equal? '(a b c) (lset-intersection eq? '(a b c))) (fail 'lset-intersection:3)) (or (equal? '(b c d) (lset-difference eq? '(a b c d e) '(a e i o u))) (fail 'lset-difference:1)) (or (equal? '(a b c) (lset-difference eq? '(a b c))) (fail 'lset-difference:2)) (or (lset= eq? '(d c b i o u) (lset-xor eq? '(a b c d e) '(a e i o u))) (fail 'lset-xor:1)) (or (lset= eq? '() (lset-xor eq?)) (fail 'lset-xor:2)) (or (lset= eq? '(a b c d e) (lset-xor eq? '(a b c d e))) (fail 'lset-xor:3)) (or (let-values (((d i) (lset-diff+intersection eq? '(a b c d e) '(c d f)))) (and (equal? d '(a b e)) (equal? i '(c d)))) (fail 'lset-diff+intersection:1)) ; FIXME: For the following five procedures, need to check that cells ; returned are from the arguments. (or (equal? '(u o i a b c d e) (lset-union! eq? (list 'a 'b 'c 'd 'e) (list 'a 'e 'i 'o 'u))) (fail 'lset-union!:1)) (or (equal? '(x a a c) (lset-union! eq? (list 'a 'a 'c) (list 'x 'a 'x))) (fail 'lset-union!:2)) (or (null? (lset-union! eq?)) (fail 'lset-union!:3)) (or (equal? '(a b c) (lset-union! eq? (list 'a 'b 'c))) (fail 'lset-union!:4)) (or (equal? '(a e) (lset-intersection! eq? (list 'a 'b 'c 'd 'e) (list 'a 'e 'i 'o 'u))) (fail 'lset-intersection!:1)) (or (equal? '(a x a) (lset-intersection! eq? (list 'a 'x 'y 'a) (list 'x 'a 'x 'z))) (fail 'lset-intersection!:2)) (or (equal? '(a b c) (lset-intersection! eq? (list 'a 'b 'c))) (fail 'lset-intersection!:3)) (or (equal? '(b c d) (lset-difference! eq? (list 'a 'b 'c 'd 'e) (list 'a 'e 'i 'o 'u))) (fail 'lset-difference!:1)) (or (equal? '(a b c) (lset-difference! eq? (list 'a 'b 'c))) (fail 'lset-difference!:2)) (or (lset= eq? '(d c b i o u) (lset-xor! eq? (list 'a 'b 'c 'd 'e) (list 'a 'e 'i 'o 'u))) (fail 'lset-xor!:1)) (or (lset= eq? '() (lset-xor! eq?)) (fail 'lset-xor!:2)) (or (lset= eq? '(a b c d e) (lset-xor! eq? (list 'a 'b 'c 'd 'e))) (fail 'lset-xor!:3)) (or (let-values (((d i) (lset-diff+intersection! eq? (list 'a 'b 'c 'd 'e) (list 'c 'd 'f)))) (and (equal? d '(a b e)) (equal? i '(c d)))) (fail 'lset-diff+intersection!:1)) (writeln "Done.") |
Added srfi/tests/multi-dimensional-arrays--arlib.sps.
> > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#!r6rs ;; Copyright (c) 2009 Derick Eddington. All rights reserved. Licensed under an ;; MIT-style license. My license is in the file named LICENSE from the original ;; collection this file is distributed with. If this file is redistributed with ;; some other collection, my license must also be included. (import (rnrs) (surfage s25 multi-dimensional-arrays) (surfage s25 multi-dimensional-arrays arlib) (surfage s78 lightweight-testing) (srfi private include)) (define-syntax past (syntax-rules () ((_ . r) (begin)))) (let-syntax ((or (syntax-rules (error) ((_ expr (error msg)) (check expr => #T)) ((_ . r) (or . r))))) (include/resolve ("srfi" "%3a25") "list.scm")) (check-report) |
Added srfi/tests/multi-dimensional-arrays.sps.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 |
#!r6rs ;; Copyright (c) 2009 Derick Eddington. All rights reserved. Licensed under an ;; MIT-style license. My license is in the file named LICENSE from the original ;; collection this file is distributed with. If this file is redistributed with ;; some other collection, my license must also be included. (import (rnrs) (surfage s25 multi-dimensional-arrays) (surfage s78 lightweight-testing) (surfage private include)) ;; (let-syntax ((or ;; (syntax-rules (error) ;; ((_ expr (error msg)) ;; (check (and expr #T) => #T)) ;; ((_ . r) (or . r)))) ;; (past ;; (syntax-rules () ;; ((_ . r) (values))))) ;; (include/resolve ("surfage" "s25") "test.scm")) (let-syntax ((or (syntax-rules (error) ((_ expr (error msg)) (check (and expr #T) => #T)) ((_ . r) (or . r)))) (past (syntax-rules () ((_ . r) (values)))) ) ;; (include/resolve ("surfage" "s25") "test.scm") ;;; array test ;;; 2001 Jussi Piitulainen ;; (define past ;; (let ((stones '())) ;; (lambda stone ;; (if (null? stone) ;; (reverse stones) ;; (set! stones (cons (apply (lambda (stone) stone) stone) stones)))))) (define (tail n) (if (< n (length (past))) (list-tail (past) (- (length (past)) n)) (past))) ;;; Simple tests (or (and (shape) (shape -1 -1) (shape -1 0) (shape -1 1) (shape 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8)) (error "(shape ...) failed")) (past "shape") (or (and (make-array (shape)) (make-array (shape) *) (make-array (shape -1 -1)) (make-array (shape -1 -1) *) (make-array (shape -1 1)) (make-array (shape 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4) *)) (error "(make-array (shape ...) [o]) failed")) (past "make-array") (or (and (array (shape) *) (array (shape -1 -1)) (array (shape -1 1) * *) (array (shape 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8) *)) (error "(array (shape ...) ...) failed")) (past "array") (or (and (= (array-rank (shape)) 2) (= (array-rank (shape -1 -1)) 2) (= (array-rank (shape -1 1)) 2) (= (array-rank (shape 1 2 3 4 5 6 7 8)) 2)) (error "(array-rank (shape ...)) failed")) (past "array-rank of shape") (or (and (= (array-rank (make-array (shape))) 0) (= (array-rank (make-array (shape -1 -1))) 1) (= (array-rank (make-array (shape -1 1))) 1) (= (array-rank (make-array (shape 1 2 3 4 5 6 7 8))) 4)) (error "(array-rank (make-array ...)) failed")) (past "array-rank of make-array") (or (and (= (array-rank (array (shape) *)) 0) (= (array-rank (array (shape -1 -1))) 1) (= (array-rank (array (shape -1 1) * *)) 1) (= (array-rank (array (shape 1 2 3 4 5 6 7 8) *)) 4)) (error "(array-rank (array ...)) failed")) (past "array-rank of array") (or (and (= (array-start (shape -1 -1) 0) 0) (= (array-start (shape -1 -1) 1) 0) (= (array-start (shape -1 1) 0) 0) (= (array-start (shape -1 1) 1) 0) (= (array-start (shape 1 2 3 4 5 6 7 8) 0) 0) (= (array-start (shape 1 2 3 4 5 6 7 8) 1) 0)) (error "(array-start (shape ...)) failed")) (past "array-start of shape") (or (and (= (array-end (shape -1 -1) 0) 1) (= (array-end (shape -1 -1) 1) 2) (= (array-end (shape -1 1) 0) 1) (= (array-end (shape -1 1) 1) 2) (= (array-end (shape 1 2 3 4 5 6 7 8) 0) 4) (= (array-end (shape 1 2 3 4 5 6 7 8) 1) 2)) (error "(array-end (shape ...)) failed")) (past "array-end of shape") (or (and (= (array-start (make-array (shape -1 -1)) 0) -1) (= (array-start (make-array (shape -1 1)) 0) -1) (= (array-start (make-array (shape 1 2 3 4 5 6 7 8)) 0) 1) (= (array-start (make-array (shape 1 2 3 4 5 6 7 8)) 1) 3) (= (array-start (make-array (shape 1 2 3 4 5 6 7 8)) 2) 5) (= (array-start (make-array (shape 1 2 3 4 5 6 7 8)) 3) 7)) (error "(array-start (make-array ...)) failed")) (past "array-start of make-array") (or (and (= (array-end (make-array (shape -1 -1)) 0) -1) (= (array-end (make-array (shape -1 1)) 0) 1) (= (array-end (make-array (shape 1 2 3 4 5 6 7 8)) 0) 2) (= (array-end (make-array (shape 1 2 3 4 5 6 7 8)) 1) 4) (= (array-end (make-array (shape 1 2 3 4 5 6 7 8)) 2) 6) (= (array-end (make-array (shape 1 2 3 4 5 6 7 8)) 3) 8)) (error "(array-end (make-array ...)) failed")) (past "array-end of make-array") (or (and (= (array-start (array (shape -1 -1)) 0) -1) (= (array-start (array (shape -1 1) * *) 0) -1) (= (array-start (array (shape 1 2 3 4 5 6 7 8) *) 0) 1) (= (array-start (array (shape 1 2 3 4 5 6 7 8) *) 1) 3) (= (array-start (array (shape 1 2 3 4 5 6 7 8) *) 2) 5) (= (array-start (array (shape 1 2 3 4 5 6 7 8) *) 3) 7)) (error "(array-start (array ...)) failed")) (past "array-start of array") (or (and (= (array-end (array (shape -1 -1)) 0) -1) (= (array-end (array (shape -1 1) * *) 0) 1) (= (array-end (array (shape 1 2 3 4 5 6 7 8) *) 0) 2) (= (array-end (array (shape 1 2 3 4 5 6 7 8) *) 1) 4) (= (array-end (array (shape 1 2 3 4 5 6 7 8) *) 2) 6) (= (array-end (array (shape 1 2 3 4 5 6 7 8) *) 3) 8)) (error "(array-end (array ...)) failed")) (past "array-end of array") (or (and (eq? (array-ref (make-array (shape) 'a)) 'a) (eq? (array-ref (make-array (shape -1 1) 'b) -1) 'b) (eq? (array-ref (make-array (shape -1 1) 'c) 0) 'c) (eq? (array-ref (make-array (shape 1 2 3 4 5 6 7 8) 'd) 1 3 5 7) 'd)) (error "array-ref of make-array with arguments failed")) (past "array-ref of make-array with arguments") (or (and (eq? (array-ref (make-array (shape) 'a) '#()) 'a) (eq? (array-ref (make-array (shape -1 1) 'b) '#(-1)) 'b) (eq? (array-ref (make-array (shape -1 1) 'c) '#(0)) 'c) (eq? (array-ref (make-array (shape 1 2 3 4 5 6 7 8) 'd) '#(1 3 5 7)) 'd)) (error "array-ref of make-array with vector failed")) (past "array-ref of make-array with vector") (or (and (eq? (array-ref (make-array (shape) 'a) (array (shape 0 0))) 'a) (eq? (array-ref (make-array (shape -1 1) 'b) (array (shape 0 1) -1)) 'b) (eq? (array-ref (make-array (shape -1 1) 'c) (array (shape 0 1) 0)) 'c) (eq? (array-ref (make-array (shape 1 2 3 4 5 6 7 8) 'd) (array (shape 0 4) 1 3 5 7)) 'd)) (error "(array-ref of make-array with array failed")) (past "array-ref of make-array with array") (or (and (let ((arr (make-array (shape) 'o))) (array-set! arr 'a) (eq? (array-ref arr) 'a)) (let ((arr (make-array (shape -1 1) 'o))) (array-set! arr -1 'b) (array-set! arr 0 'c) (and (eq? (array-ref arr -1) 'b) (eq? (array-ref arr 0) 'c))) (let ((arr (make-array (shape 1 2 3 4 5 6 7 8) 'o))) (array-set! arr 1 3 5 7 'd) (eq? (array-ref arr 1 3 5 7) 'd))) (error "array-set! with arguments failed")) (past "array-set! of make-array with arguments") (or (and (let ((arr (make-array (shape) 'o))) (array-set! arr '#() 'a) (eq? (array-ref arr) 'a)) (let ((arr (make-array (shape -1 1) 'o))) (array-set! arr '#(-1) 'b) (array-set! arr '#(0) 'c) (and (eq? (array-ref arr -1) 'b) (eq? (array-ref arr 0) 'c))) (let ((arr (make-array (shape 1 2 3 4 5 6 7 8) 'o))) (array-set! arr '#(1 3 5 7) 'd) (eq? (array-ref arr 1 3 5 7) 'd))) (error "array-set! with vector failed")) (past "array-set! of make-array with vector") (or (and (let ((arr (make-array (shape) 'o))) (array-set! arr 'a) (eq? (array-ref arr) 'a)) (let ((arr (make-array (shape -1 1) 'o))) (array-set! arr (array (shape 0 1) -1) 'b) (array-set! arr (array (shape 0 1) 0) 'c) (and (eq? (array-ref arr -1) 'b) (eq? (array-ref arr 0) 'c))) (let ((arr (make-array (shape 1 2 3 4 5 6 7 8) 'o))) (array-set! arr (array (shape 0 4) 1 3 5 7) 'd) (eq? (array-ref arr 1 3 5 7) 'd))) (error "array-set! with arguments failed")) (past "array-set! of make-array with array") ;;; Share and change: ;;; ;;; org brk swp box ;;; ;;; 0 1 1 2 5 6 ;;; 6 a b 2 a b 3 d c 0 2 4 6 8: e ;;; 7 c d 3 e f 4 f e ;;; 8 e f (or (let* ((org (array (shape 6 9 0 2) 'a 'b 'c 'd 'e 'f)) (brk (share-array org (shape 2 4 1 3) (lambda (r k) (values (+ 6 (* 2 (- r 2))) (- k 1))))) (swp (share-array org (shape 3 5 5 7) (lambda (r k) (values (+ 7 (- r 3)) (- 1 (- k 5)))))) (box (share-array swp (shape 0 1 2 3 4 5 6 7 8 9) (lambda _ (values 4 6)))) (org-contents (lambda () (list (array-ref org 6 0) (array-ref org 6 1) (array-ref org 7 0) (array-ref org 7 1) (array-ref org 8 0) (array-ref org 8 1)))) (brk-contents (lambda () (list (array-ref brk 2 1) (array-ref brk 2 2) (array-ref brk 3 1) (array-ref brk 3 2)))) (swp-contents (lambda () (list (array-ref swp 3 5) (array-ref swp 3 6) (array-ref swp 4 5) (array-ref swp 4 6)))) (box-contents (lambda () (list (array-ref box 0 2 4 6 8))))) (and (equal? (org-contents) '(a b c d e f)) (equal? (brk-contents) '(a b e f)) (equal? (swp-contents) '(d c f e)) (equal? (box-contents) '(e)) (begin (array-set! org 6 0 'x) #t) (equal? (org-contents) '(x b c d e f)) (equal? (brk-contents) '(x b e f)) (equal? (swp-contents) '(d c f e)) (equal? (box-contents) '(e)) (begin (array-set! brk 3 1 'y) #t) (equal? (org-contents) '(x b c d y f)) (equal? (brk-contents) '(x b y f)) (equal? (swp-contents) '(d c f y)) (equal? (box-contents) '(y)) (begin (array-set! swp 4 5 'z) #t) (equal? (org-contents) '(x b c d y z)) (equal? (brk-contents) '(x b y z)) (equal? (swp-contents) '(d c z y)) (equal? (box-contents) '(y)) (begin (array-set! box 0 2 4 6 8 'e) #t) (equal? (org-contents) '(x b c d e z)) (equal? (brk-contents) '(x b e z)) (equal? (swp-contents) '(d c z e)) (equal? (box-contents) '(e)))) (error "shared change failed")) (past "shared change") ;;; Check that arrays copy the shape specification (or (let ((shp (shape 10 12))) (let ((arr (make-array shp)) (ars (array shp * *)) (art (share-array (make-array shp) shp (lambda (k) k)))) (array-set! shp 0 0 '?) (array-set! shp 0 1 '!) (and (= (array-rank shp) 2) (= (array-start shp 0) 0) (= (array-end shp 0) 1) (= (array-start shp 1) 0) (= (array-end shp 1) 2) (eq? (array-ref shp 0 0) '?) (eq? (array-ref shp 0 1) '!) (= (array-rank arr) 1) (= (array-start arr 0) 10) (= (array-end arr 0) 12) (= (array-rank ars) 1) (= (array-start ars 0) 10) (= (array-end ars 0) 12) (= (array-rank art) 1) (= (array-start art 0) 10) (= (array-end art 0) 12)))) (error "array-set! of shape failed")) (past "array-set! of shape") ;;; Check that index arrays work even when they share ;;; ;;; arr ixn ;;; 5 6 0 1 ;;; 4 nw ne 0 4 6 ;;; 5 sw se 1 5 4 (or (let ((arr (array (shape 4 6 5 7) 'nw 'ne 'sw 'se)) (ixn (array (shape 0 2 0 2) 4 6 5 4))) (let ((col0 (share-array ixn (shape 0 2) (lambda (k) (values k 0)))) (row0 (share-array ixn (shape 0 2) (lambda (k) (values 0 k)))) (wor1 (share-array ixn (shape 0 2) (lambda (k) (values 1 (- 1 k))))) (cod (share-array ixn (shape 0 2) (lambda (k) (case k ((0) (values 1 0)) ((1) (values 0 1)))))) (box (share-array ixn (shape 0 2) (lambda (k) (values 1 0))))) (and (eq? (array-ref arr col0) 'nw) (eq? (array-ref arr row0) 'ne) (eq? (array-ref arr wor1) 'nw) (eq? (array-ref arr cod) 'se) (eq? (array-ref arr box) 'sw) (begin (array-set! arr col0 'ul) (array-set! arr row0 'ur) (array-set! arr cod 'lr) (array-set! arr box 'll) #t) (eq? (array-ref arr 4 5) 'ul) (eq? (array-ref arr 4 6) 'ur) (eq? (array-ref arr 5 5) 'll) (eq? (array-ref arr 5 6) 'lr) (begin (array-set! arr wor1 'xx) (eq? (array-ref arr 4 5) 'xx))))) (error "array access with sharing index array failed")) (past "array access with sharing index array") ;;; Check that shape arrays work even when they share ;;; ;;; arr shp shq shr shs ;;; 1 2 3 4 0 1 0 1 0 1 0 1 ;;; 1 10 12 16 20 0 10 12 0 12 20 0 10 10 0 12 12 ;;; 2 10 11 12 13 1 10 11 1 11 13 1 11 12 1 12 12 ;;; 2 12 16 ;;; 3 13 20 (or (let ((arr (array (shape 1 3 1 5) 10 12 16 20 10 11 12 13))) (let ((shp (share-array arr (shape 0 2 0 2) (lambda (r k) (values (+ r 1) (+ k 1))))) (shq (share-array arr (shape 0 2 0 2) (lambda (r k) (values (+ r 1) (* 2 (+ 1 k)))))) (shr (share-array arr (shape 0 4 0 2) (lambda (r k) (values (- 2 k) (+ r 1))))) (shs (share-array arr (shape 0 2 0 2) (lambda (r k) (values 2 3))))) (and (let ((arr-p (make-array shp))) (and (= (array-rank arr-p) 2) (= (array-start arr-p 0) 10) (= (array-end arr-p 0) 12) (= (array-start arr-p 1) 10) (= (array-end arr-p 1) 11))) (let ((arr-q (array shq * * * * * * * * * * * * * * * *))) (and (= (array-rank arr-q) 2) (= (array-start arr-q 0) 12) (= (array-end arr-q 0) 20) (= (array-start arr-q 1) 11) (= (array-end arr-q 1) 13))) (let ((arr-r (share-array (array (shape) *) shr (lambda _ (values))))) (and (= (array-rank arr-r) 4) (= (array-start arr-r 0) 10) (= (array-end arr-r 0) 10) (= (array-start arr-r 1) 11) (= (array-end arr-r 1) 12) (= (array-start arr-r 2) 12) (= (array-end arr-r 2) 16) (= (array-start arr-r 3) 13) (= (array-end arr-r 3) 20))) (let ((arr-s (make-array shs))) (and (= (array-rank arr-s) 2) (= (array-start arr-s 0) 12) (= (array-end arr-s 0) 12) (= (array-start arr-s 1) 12) (= (array-end arr-s 1) 12)))))) (error "sharing shape array failed")) (past "sharing shape array") (let ((super (array (shape 4 7 4 7) 1 * * * 2 * * * 3)) (subshape (share-array (array (shape 0 2 0 3) * 4 * * 7 *) (shape 0 1 0 2) (lambda (r k) (values k 1))))) (let ((sub (share-array super subshape (lambda (k) (values k k))))) ;(array-equal? subshape (shape 4 7)) (or (and (= (array-rank subshape) 2) (= (array-start subshape 0) 0) (= (array-end subshape 0) 1) (= (array-start subshape 1) 0) (= (array-end subshape 1) 2) (= (array-ref subshape 0 0) 4) (= (array-ref subshape 0 1) 7)) (error "sharing subshape failed")) ;(array-equal? sub (array (shape 4 7) 1 2 3)) (or (and (= (array-rank sub) 1) (= (array-start sub 0) 4) (= (array-end sub 0) 7) (= (array-ref sub 4) 1) (= (array-ref sub 5) 2) (= (array-ref sub 6) 3)) (error "sharing with sharing subshape failed")))) (past "sharing with sharing subshape") ) (check-report) |
Added srfi/tests/os-environment-variables.sps.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. Licensed under an ;; MIT-style license. My license is in the file named LICENSE from the original ;; collection this file is distributed with. If this file is redistributed with ;; some other collection, my license must also be included. #!r6rs (import (rename (rnrs) (for-all andmap)) (surfage s78 lightweight-testing) (surfage s98 os-environment-variables)) (check (list? (get-environment-variables)) => #T) (check (andmap (lambda (a) (and (pair? a) (string? (car a)) (positive? (string-length (car a))) (string? (cdr a)))) (get-environment-variables)) => #T) (check (andmap (lambda (a) (let ((v (get-environment-variable (car a)))) (and (string? v) (string=? v (cdr a))))) (get-environment-variables)) => #T) (assert (not (assoc "BLAH" (get-environment-variables)))) (check (get-environment-variable "BLAH") => #F) (check-report) |
Added srfi/tests/print-ascii.sps.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
;;; Test format import of implementation ;;; specific routine: pretty-print #| LARCENY USAGE: ==> larceny -r6rs -program print-ascii.ss IKARUS USAGE ==> ikarus --r6rs-script print-ascii.ss |# (import (rnrs (6)) (surfage s48 intermediate-format-strings)) (define pa '(define (print-ascii-chart . radix+port) (let ( (radix (if (null? radix+port) 16 (car radix+port))) (port (if (or (null? radix+port) (null? (cdr radix+port))) (current-output-port) (cadr radix+port))) (max-row 15) (max-col 7) (max-ascii 127) (max-control 31) ; [0..31] are control codes ) (define (printable? N) ; N.B.: integer input (< max-control N max-ascii)) ; control or DEL (define (print-a-char N) (if (printable? N) (begin (display #\' port) (display (integer->char N) port) (display #\' port) ) (cond ; print a control character ((= N max-ascii) (display "DEL" port)) (else (display #\^ port) (display (integer->char (+ (char->integer #\@) N)) port) ) ) ) (display " = " port) (display (number->string N radix) port) (display #\space port) (display #\space port) (display #\space port) ) ; output the chart... (newline port) (let row-loop ( (row 0) ) (if (> row max-row) (newline port) ; done (let column-loop ( (col 0) ) (print-a-char (+ row (* col (+ max-row 1)))) (if (< col max-col) (column-loop (+ col 1)) (begin (newline port) (row-loop (+ row 1)) ) ) ) ) )) ) ) (format #t "~Y~%" pa) ;; --- E O F --- ;; |
Added srfi/tests/random-conftest.sps.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
#!r6rs (import (rnrs) (rnrs r5rs) (surfage s27 random-bits)) ; CONFIDENCE TESTS FOR SRFI-27 "Sources of Random Bits" ; ===================================================== ; ; Sebastian.Egner@philips.com, 2002. ; ; This file contains a small collection of checks for the ; implementation of SRFI-27. It is not meant to be complete ; or to test the actual properties of the underlying generator. ; It is merely meant to run the code and to check some of the ; assumptions made by specification. There is an interface to ; G. Marsaglia's DIEHARD battery of tests for random number ; generators, though. ; History of this file: ; SE, 19-Mar-2002: initial version, based on earlier tests ; SE, 22-Mar-2002: adapted to new procedure names ; SE, 25-Mar-2002: more descriptive output ; SE, 04-Apr-2002: some quick timings; check up ; (check expr) ; evals expr and issues an error if it is not #t. #; (define (check expr) (if (not (eq? (eval expr (interaction-environment)) #t)) (error "check failed" expr))) ; Basic Tests of the Interface ; ============================ (define (my-random-integer n) (let ((x (random-integer n))) (if (<= 0 x (- n 1)) x (error "(random-integer n) returned illegal value" x)))) (define (my-random-real) (let ((x (random-real))) (if (< 0 x 1) x (error "(random-real) returned illegal value" x)))) (define (check-basics-1) ; generate increasingly large numbers (display "; generating large numbers [bits]: ") (do ((k 0 (+ k 1)) (n 1 (* n 2))) ((> k 1024)) (display k) (display " ") (my-random-integer n)) (display "ok") (newline) ; generate some reals (display "; generating reals [1000 times]: ") (do ((k 0 (+ k 1)) (x (my-random-real) (+ x (my-random-real)))) ((= k 1000) x)) (display "ok") (newline) ; get/set the state (display "; get/set state: ") (let* ((state1 (random-source-state-ref default-random-source)) (x1 (my-random-integer (expt 2 32))) (state2 (random-source-state-ref default-random-source)) (x2 (my-random-integer (expt 2 32)))) (random-source-state-set! default-random-source state1) (let ((y1 (my-random-integer (expt 2 32)))) (if (not (= x1 y1)) (error "state get/set doesn't work" x1 y1 state1))) (random-source-state-set! default-random-source state2) (let ((y2 (my-random-integer (expt 2 32)))) (if (not (= x2 y2)) (error "state get/set doesn't work" x2 y2 state2)))) (display "ok") (newline) ; randomize! (display "; randomize!: ") (let* ((state1 (random-source-state-ref default-random-source)) (x1 (my-random-integer (expt 2 32)))) (random-source-state-set! default-random-source state1) (random-source-randomize! default-random-source) (let ((y1 (my-random-integer (expt 2 32)))) (if (= x1 y1) (error "random-source-randomize! didn't work" x1 state1)))) (display "ok") (newline) ; pseudo-randomize! (display "; pseudo-randomize!: ") (let* ((state1 (random-source-state-ref default-random-source)) (x1 (my-random-integer (expt 2 32)))) (random-source-state-set! default-random-source state1) (random-source-pseudo-randomize! default-random-source 0 1) (let ((y1 (my-random-integer (expt 2 32)))) (if (= x1 y1) (error "random-source-pseudo-randomize! didn't work" x1 state1))) (random-source-state-set! default-random-source state1) (random-source-pseudo-randomize! default-random-source 1 0) (let ((y1 (my-random-integer (expt 2 32)))) (if (= x1 y1) (error "random-source-pseudo-randomize! didn't work" x1 state1)))) (display "ok") (newline) (newline)) ; Testing the MRG32k3a Generator (if implemented) ; =============================================== ; (check-mrg32k3a) ; tests if the underlying generator is the MRG32k3a generator ; as implemented in the reference implementation. This function ; is useful to check whether the reference implementation computes ; the right numbers. (define (check-mrg32k3a) ; check if the initial state is A^16 * (1 0 0 1 0 0) (display "; check A^16 * (1 0 0 1 0 0)") (let* ((s (make-random-source)) (state1 (random-source-state-ref s)) (rand (random-source-make-reals s))) (random-source-state-set! s '(lecuyer-mrg32k3a 1 0 0 1 0 0)) (do ((k 0 (+ k 1))) ((= k 16) (let ((state2 (random-source-state-ref s))) (if (not (equal? state1 state2)) (error "16-th state after (1 0 0 1 0 0) is wrong")))) (rand))) (display "ok") (newline) ; check if pseudo-randomize! advances properly (display "; checking (random-source-pseudo-randomize! s 1 2)") (let ((s (make-random-source))) (random-source-pseudo-randomize! s 1 2) (if (not (equal? (random-source-state-ref s) '(lecuyer-mrg32k3a 1250826159 3004357423 431373563 3322526864 623307378 2983662421))) (error "pseudo-randomize! gives wrong result"))) (display "ok") (newline) ; run the check published by Pierre L'Ecuyer: ; Note that the reference implementation deals slightly different ; with reals mapping m1-1 into 1-1/(m1+1) and not into 0 as in ; L'Ecuyer's original proposal. However, for the first 10^7 reals ; that makes no difference as m1-1 is not generated. (display "; checking (random-source-pseudo-randomize! s 1 2)...") (let* ((x 0.0) (s (make-random-source)) (rand (random-source-make-reals s))) (random-source-state-set! s '(lecuyer-mrg32k3a 12345 12345 12345 12345 12345 12345)) (do ((k 0 (+ k 1))) ((= k 10000000) (if (not (< (abs (- x 5001090.95)) 0.01)) (error "bad sum over 10^7 reals" x))) (set! x (+ x (rand))))) (display "ok") (newline)) ; Writing Data to DIEHARD ; ======================= ; (write-diehard filename s bytes-per-call calls) ; creates a binary file to which bytes-per-call * calls bytes are ; written. The bytes are obtained from the random source s using ; the range n = (expt 256 bytes-per-call). ; The intention of write-diehard is to give implementors a ; '15 min.'-way of running their favourite random number generator ; through a pretty tough testsuite. ; ; try: For the reference implementation, the call ; ; (write-diehard "outfile" (make-random-source) 4 2867200) ; ; should create a file that looks as follows (od -A x -t x1 outfile): ; ; 0000000 92 bb 7e db 1b 14 f6 bb bb 54 a1 55 c2 3e cd ca ; 0000010 23 01 20 35 06 47 65 b0 52 4c b8 c0 21 48 af 67 ; 0000020 63 a9 8c 78 50 73 29 08 62 d1 22 7f a6 89 96 77 ; 0000030 98 28 65 2d 2d 8b f9 52 41 be 8e 3f c5 84 0f ca ; 0000040 c0 fa 03 d6 f0 65 9d 3a 9b ab 6f fe d1 aa 5f 92 ; 0000050 0f ea f6 3b 78 b9 fe ad 63 5e 49 f1 9d c9 8e 2f ; 0000060 53 a9 5d 32 d4 20 51 1d 1c 2e 82 f0 8b 26 40 c0 ; ...total length is 11468800 bytes. ; ; The message digest is md5sum = 4df554f56cb5ed251bd04b0d50767443. ; ; try: For the reference implementation, the call ; ; (write-diehard "outfile" (make-random-source) 3 3822934) ; ; should create a file that looks as follows (od -A x -t x1 outfile): ; ; 000000 bb 7e db 30 a3 49 14 f6 bb d0 f2 d0 54 a1 55 8b ; 000010 8c 03 3e cd ca a3 88 1d 01 20 35 e8 50 c8 47 65 ; 000020 b0 e7 d9 28 4c b8 c0 f2 82 35 48 af 67 42 3e 8a ; 000030 a9 8c 78 12 ef b6 73 29 08 ff e9 71 d1 22 7f 52 ; 000040 b8 f0 89 96 77 dc 71 86 28 65 2d c2 82 fc 8b f9 ; 000050 52 d7 23 2a be 8e 3f 61 a8 99 84 0f ca 44 83 65 ; 000060 fa 03 d6 c2 11 c0 65 9d 3a c2 7a dd ab 6f fe 1c ; ...total length is 11468802 bytes. ; ; The message digest is md5sum = 750ac219ff40c50bb2d04ff5eff9b24c. (define (write-diehard filename s bytes-per-call calls) (let ((port (open-output-file filename)) (rand (random-source-make-integers s)) (n (expt 256 bytes-per-call))) (do ((i 0 (+ i 1))) ((= i calls) (close-output-port port)) (let ((x (rand n))) (do ((k 0 (+ k 1))) ((= k bytes-per-call)) (put-u8 port (modulo x 256)) (set! x (quotient x 256))))))) ; run some tests (check-basics-1) (display "passed (check-basics-1)") (newline) (check-mrg32k3a) (display "passed (check-mrg32k3a)") (newline) ; (display "Generating diehard1 with expected MD5=4df554f56cb5ed251bd04b0d50767443\n") ; (write-diehard "diehard1" (make-random-source) 4 2867200) ;(display "Generating diehard2 with expected MD5=750ac219ff40c50bb2d04ff5eff9b24c\n") ; (display "Generating diehard2 with expected MD5=9c4cb1f6251efa301a98f226a76de5b9") ; (write-diehard "diehard2" (make-random-source) 3 3822934) |
Added srfi/tests/random.sps.
> > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. Licensed under an ;; MIT-style license. My license is in the file named LICENSE from the original ;; collection this file is distributed with. If this file is redistributed with ;; some other collection, my license must also be included. #!r6rs (import (rnrs) (surfage s27 random-bits)) (do ((i 0 (+ i 1))) ((= i 10) 'done) (display (random-integer 100)) (newline)) (do ((i 0 (+ i 1))) ((= i 10) 'done) (display (random-real)) (newline)) |
Added srfi/tests/rec-factorial.sps.
> > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. Licensed under an ;; MIT-style license. My license is in the file named LICENSE from the original ;; collection this file is distributed with. If this file is redistributed with ;; some other collection, my license must also be included. #!r6rs (import (rnrs) (surfage s31 rec)) (display ((rec (F N) (if (zero? N) 1 (* N (F (- N 1))))) 10)) (newline) |
Added srfi/tests/records.sps.
> > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
;; Copyright (c) 2009 Derick Eddington. All rights reserved. Licensed under an ;; MIT-style license. My license is in the file named LICENSE from the original ;; collection this file is distributed with. If this file is redistributed with ;; some other collection, my license must also be included. #!r6rs (import (rnrs base) ; no R6RS records (only (rnrs io simple) display write newline) (surfage s9 records)) (define-record-type thing (make-thing x) thing? (x thing-x) (y thing-y set-thing-y!)) (define t (make-thing 123)) (display "t => ") (write t) (newline) (set-thing-y! t 'blah) (display "t => ") (write t) (newline) |
Added srfi/tests/s26-cut.sps.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
#!r6rs ; CONFIDENCE TEST FOR IMPLEMENTATION OF SRFI-26 ; ============================================= ; ; Sebastian.Egner@philips.com, 3-Jun-2002. ; ; This file checks a few assertions about the implementation. ; If you run it and no error message is issued, the implementation ; is correct on the cases that have been tested. ; ; compliance: ; Scheme R5RS with ; SRFI-23: error ; ; loading this file into Scheme 48 0.57 after 'cut.scm' has been loaded: ; ,open srfi-23 ; ,load check.scm ; (check expr) ; evals expr and issues an error if it is not #t. ;; Extended by Derick Eddington to test free-identifier=? of <> and <...>. (import (rnrs) (rnrs eval)) (define (check expr) (if (not (eq? (eval expr (environment '(rnrs) '(surfage s26 cut))) #t)) (assertion-violation 'check "check failed" expr))) ; (check-all) ; runs several tests on cut and reports. (define (check-all) (for-each check '( ; cuts (equal? ((cut list)) '()) (equal? ((cut list <...>)) '()) (equal? ((cut list 1)) '(1)) (equal? ((cut list <>) 1) '(1)) (equal? ((cut list <...>) 1) '(1)) (equal? ((cut list 1 2)) '(1 2)) (equal? ((cut list 1 <>) 2) '(1 2)) (equal? ((cut list 1 <...>) 2) '(1 2)) (equal? ((cut list 1 <...>) 2 3 4) '(1 2 3 4)) (equal? ((cut list 1 <> 3 <>) 2 4) '(1 2 3 4)) (equal? ((cut list 1 <> 3 <...>) 2 4 5 6) '(1 2 3 4 5 6)) (equal? (let* ((x 'wrong) (y (cut list x))) (set! x 'ok) (y)) '(ok)) (equal? (let ((a 0)) (map (cut + (begin (set! a (+ a 1)) a) <>) '(1 2)) a) 2) (equal? (let* ((<> 'wrong) (f (cut list <> <...>))) (set! <> 'ok) (f 1 2)) '(ok 1 2)) (equal? (let* ((<...> 'wrong) (f (cut list <> <...>))) (set! <...> 'ok) (f 1)) '(1 ok)) ; cutes (equal? ((cute list)) '()) (equal? ((cute list <...>)) '()) (equal? ((cute list 1)) '(1)) (equal? ((cute list <>) 1) '(1)) (equal? ((cute list <...>) 1) '(1)) (equal? ((cute list 1 2)) '(1 2)) (equal? ((cute list 1 <>) 2) '(1 2)) (equal? ((cute list 1 <...>) 2) '(1 2)) (equal? ((cute list 1 <...>) 2 3 4) '(1 2 3 4)) (equal? ((cute list 1 <> 3 <>) 2 4) '(1 2 3 4)) (equal? ((cute list 1 <> 3 <...>) 2 4 5 6) '(1 2 3 4 5 6)) (equal? (let ((a 0)) (map (cute + (begin (set! a (+ a 1)) a) <>) '(1 2)) a) 1) (equal? (let* ((<> 'ok) (f (cute list <> <...>))) (set! <> 'wrong) (f 1 2)) '(ok 1 2)) (equal? (let* ((<...> 'ok) (f (cute list <> <...>))) (set! <...> 'wrong) (f 1)) '(1 ok)) ))) ; run the checks when loading (check-all) ;; (display "passed") ;; (newline) |
Added srfi/tests/s78-lightweight-testing.sps.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
#!r6rs ; <PLAINTEXT> ; Copyright (c) 2005-2006 Sebastian Egner. ; ; Permission is hereby granted, free of charge, to any person obtaining ; a copy of this software and associated documentation files (the ; ``Software''), to deal in the Software without restriction, including ; without limitation the rights to use, copy, modify, merge, publish, ; distribute, sublicense, and/or sell copies of the Software, and to ; permit persons to whom the Software is furnished to do so, subject to ; the following conditions: ; ; The above copyright notice and this permission notice shall be ; included in all copies or substantial portions of the Software. ; ; THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, ; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION ; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION ; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ; ; ----------------------------------------------------------------------- ; Lightweight testing (examples) ; ============================== ; ; Sebastian.Egner@philips.com ; in R5RS + SRFI 23 (error) + SRFI 42 (comprehensions) ; ; history of this file: ; SE, 25-Oct-2004: first version ; -- portability -- ; PLT: ; (require (lib "23.ss" "srfi") (lib "42.ss" "srfi")) (load "check.scm") ; (load "examples.scm") ; Scheme48: ; ,open srfi-23 srfi-42 ; ,load check.scm examples.scm (import (rnrs) (rnrs r5rs) (surfage s42 eager-comprehensions) (surfage s78 lightweight-testing)) (check-set-mode! 'report-failed) ; -- simple test -- (check (+ 1 1) => 2) ;; (check (+ 1 1) => 3) ; fails ; -- different equality predicate -- (check (vector 1) => (vector 1)) ;; (check (vector 1) (=> eq?) (vector 1)) ; fails ; -- parametric tests -- (check-ec (+ 1 1) => 2) (check-ec (: x 10) (+ x 1) => (+ x 1) (x)) ;; (check-ec (: e 100) (positive? (expt 2 e)) => #t (e)) ; fails on fixnums ;; (check-ec (: e 100) (:let x (expt 2.0 e)) (= (+ x 1) x) => #f (x)) ; fails ;; (check-ec (: e 100) (:let x (expt 2.0 e)) (= (+ x 1) x) => #f) (check-ec (: x 10) (: y 10) (: z 10) (* x (+ y z)) => (+ (* x y) (* x z)) (x y z)) ; passes with 10^3 cases checked ; -- toy examples -- (define (fib n) (if (<= n 2) 1 (+ (fib (- n 1)) (fib (- n 2))))) (check (fib 1) => 1) (check (fib 2) => 1) (check-ec (: n 1 31) (even? (fib n)) => (= (modulo n 3) 0) (n)) ; -- reporting -- ;; (check-report) |
Added srfi/tests/testing.sps.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 |
;;; ;;; This is a test suite written in the notation of ;;; SRFI-64, A Scheme API for test suites ;;; #!r6rs (import (except (rnrs base) error) (rnrs lists) (surfage s64 testing)) (define (error msg) (assertion-violation "(surfage s64 testing) test program" msg)) (test-begin "SRFI 64 - Meta-Test Suite") ;;; ;;; Ironically, in order to set up the meta-test environment, ;;; we have to invoke one of the most sophisticated features: ;;; custom test runners ;;; ;;; The `prop-runner' invokes `thunk' in the context of a new ;;; test runner, and returns the indicated properties of the ;;; last-executed test result. (define (prop-runner props thunk) (let ((r (test-runner-null)) (plist '())) ;; (test-runner-on-test-end! r (lambda (runner) (set! plist (test-result-alist runner)))) ;; (test-with-runner r (thunk)) ;; reorder the properties so they are in the order ;; given by `props'. Note that any property listed in `props' ;; that is not in the property alist will occur as #f (map (lambda (k) (assq k plist)) props))) ;;; `on-test-runner' creates a null test runner and then ;;; arranged for `visit' to be called with the runner ;;; whenever a test is run. The results of the calls to ;;; `visit' are returned in a list (define (on-test-runner thunk visit) (let ((r (test-runner-null)) (results '())) ;; (test-runner-on-test-end! r (lambda (runner) (set! results (cons (visit r) results)))) ;; (test-with-runner r (thunk)) (reverse results))) ;;; ;;; The `triv-runner' invokes `thunk' ;;; and returns a list of 6 lists, the first 5 of which ;;; are a list of the names of the tests that, respectively, ;;; PASS, FAIL, XFAIL, XPASS, and SKIP. ;;; The last item is a list of counts. ;;; (define (triv-runner thunk) (let ((r (test-runner-null)) (accum-pass '()) (accum-fail '()) (accum-xfail '()) (accum-xpass '()) (accum-skip '())) ;; (test-runner-on-bad-count! r (lambda (runner count expected-count) (error (string-append "bad count " (number->string count) " but expected " (number->string expected-count))))) (test-runner-on-bad-end-name! r (lambda (runner begin end) (error (string-append "bad end grojup name " end " but expected " begin)))) (test-runner-on-test-end! r (lambda (runner) (let ((n (test-runner-test-name runner))) (case (test-result-kind runner) ((pass) (set! accum-pass (cons n accum-pass))) ((fail) (set! accum-fail (cons n accum-fail))) ((xpass) (set! accum-xpass (cons n accum-xpass))) ((xfail) (set! accum-xfail (cons n accum-xfail))) ((skip) (set! accum-skip (cons n accum-skip))))))) ;; (test-with-runner r (thunk)) (list (reverse accum-pass) ; passed as expected (reverse accum-fail) ; failed, but was expected to pass (reverse accum-xfail) ; failed as expected (reverse accum-xpass) ; passed, but was expected to fail (reverse accum-skip) ; was not executed (list (test-runner-pass-count r) (test-runner-fail-count r) (test-runner-xfail-count r) (test-runner-xpass-count r) (test-runner-skip-count r))))) (define (path-revealing-runner thunk) (let ((r (test-runner-null)) (seq '())) ;; (test-runner-on-test-end! r (lambda (runner) (set! seq (cons (list (test-runner-group-path runner) (test-runner-test-name runner)) seq)))) (test-with-runner r (thunk)) (reverse seq))) ;;; ;;; Now we can start testing compliance with SRFI-64 ;;; (test-begin "1. Simple test-cases") (test-begin "1.1. test-assert") (define (t) (triv-runner (lambda () (test-assert "a" #t) (test-assert "b" #f)))) (test-equal "1.1.1. Very simple" '(("a") ("b") () () () (1 1 0 0 0)) (t)) (test-equal "1.1.2. A test with no name" '(("a") ("") () () () (1 1 0 0 0)) (triv-runner (lambda () (test-assert "a" #t) (test-assert #f)))) (test-equal "1.1.3. Tests can have the same name" '(("a" "a") () () () () (2 0 0 0 0)) (triv-runner (lambda () (test-assert "a" #t) (test-assert "a" #t)))) (define (choke) (vector-ref '#(1 2) 3)) (test-equal "1.1.4. One way to FAIL is to throw an error" '(() ("a") () () () (0 1 0 0 0)) (triv-runner (lambda () (test-assert "a" (choke))))) (test-end);1.1 (test-begin "1.2. test-eqv") (define (mean x y) (/ (+ x y) 2.0)) (test-equal "1.2.1. Simple numerical equivalence" '(("c") ("a" "b") () () () (1 2 0 0 0)) (triv-runner (lambda () (test-eqv "a" (mean 3 5) 4) (test-eqv "b" (mean 3 5) 4.5) (test-eqv "c" (mean 3 5) 4.0)))) (test-end);1.2 (test-end "1. Simple test-cases") ;;; ;;; ;;; (test-begin "2. Tests for catching errors") (test-begin "2.1. test-error") (test-equal "2.1.1. Baseline test; PASS with no optional args" '(("") () () () () (1 0 0 0 0)) (triv-runner (lambda () ;; PASS (test-error (vector-ref '#(1 2) 9))))) (test-equal "2.1.2. Baseline test; FAIL with no optional args" '(() ("") () () () (0 1 0 0 0)) (triv-runner (lambda () ;; FAIL: the expr does not raise an error and `test-error' is ;; claiming that it will, so this test should FAIL (test-error (vector-ref '#(1 2) 0))))) (test-equal "2.1.3. PASS with a test name and error type" '(("a") () () () () (1 0 0 0 0)) (triv-runner (lambda () ;; PASS (test-error "a" #t (vector-ref '#(1 2) 9))))) (test-end "2.1. test-error") (test-end "2. Tests for catching errors") ;;; ;;; ;;; (test-begin "3. Test groups and paths") (test-equal "3.1. test-begin with unspecific test-end" '(("b") () () () () (1 0 0 0 0)) (triv-runner (lambda () (test-begin "a") (test-assert "b" #t) (test-end)))) (test-equal "3.2. test-begin with name-matching test-end" '(("b") () () () () (1 0 0 0 0)) (triv-runner (lambda () (test-begin "a") (test-assert "b" #t) (test-end "a")))) ;;; since the error raised by `test-end' on a mismatch is not a test ;;; error, we actually expect the triv-runner itself to fail (test-error "3.3. test-begin with mismatched test-end" #t (triv-runner (lambda () (test-begin "a") (test-assert "b" #t) (test-end "x")))) (test-equal "3.4. test-begin with name and count" '(("b" "c") () () () () (2 0 0 0 0)) (triv-runner (lambda () (test-begin "a" 2) (test-assert "b" #t) (test-assert "c" #t) (test-end "a")))) ;; similarly here, a mismatched count is a lexical error ;; and not a test failure... (test-error "3.5. test-begin with mismatched count" #t (triv-runner (lambda () (test-begin "a" 99) (test-assert "b" #t) (test-end "a")))) (test-equal "3.6. introspecting on the group path" '((() "w") (("a" "b") "x") (("a" "b") "y") (("a") "z")) ;; ;; `path-revealing-runner' is designed to return a list ;; of the tests executed, in order. Each entry is a list ;; (GROUP-PATH TEST-NAME), and each GROUP-PATH is a list ;; of test groups starting from the topmost ;; (path-revealing-runner (lambda () (test-assert "w" #t) (test-begin "a") (test-begin "b") (test-assert "x" #t) (test-assert "y" #t) (test-end) (test-assert "z" #t)))) (test-end "3. Test groups and paths") ;;; ;;; ;;; (test-begin "4. Handling set-up and cleanup") (test-equal "4.1. Normal exit path" '(in 1 2 out) (let ((ex '())) (define (do s) (set! ex (cons s ex))) ;; (triv-runner (lambda () (test-group-with-cleanup "foo" (do 'in) (do 1) (do 2) (do 'out)))) (reverse ex))) (test-equal "4.2. Exception exit path" '(in 1 out) (let ((ex '())) (define (do s) (set! ex (cons s ex))) ;; ;; the outer runner is to run the `test-error' in, to ;; catch the exception raised in the inner runner, ;; since we don't want to depend on any other ;; exception-catching support ;; (triv-runner (lambda () (test-error (triv-runner (lambda () (test-group-with-cleanup "foo" (do 'in) (test-assert #t) (do 1) (test-assert #t) (choke) (test-assert #t) (do 2) (test-assert #t) (do 'out))))))) (reverse ex))) (test-end "4. Handling set-up and cleanup") ;;; ;;; ;;; (test-begin "5. Test specifiers") (test-begin "5.1. test-match-named") (test-equal "5.1.1. match test names" '(("y") () () () ("x") (1 0 0 0 1)) (triv-runner (lambda () (test-skip (test-match-name "x")) (test-assert "x" #t) (test-assert "y" #t)))) (test-equal "5.1.2. but not group names" '(("z") () () () () (1 0 0 0 0)) (triv-runner (lambda () (test-skip (test-match-name "x")) (test-begin "x") (test-assert "z" #t) (test-end)))) (test-end) (test-begin "5.2. test-match-nth") ;; See also: [6.4. Short-circuit evaluation] (test-equal "5.2.1. skip the nth one after" '(("v" "w" "y" "z") () () () ("x") (4 0 0 0 1)) (triv-runner (lambda () (test-assert "v" #t) (test-skip (test-match-nth 2)) (test-assert "w" #t) ; 1 (test-assert "x" #t) ; 2 SKIP (test-assert "y" #t) ; 3 (test-assert "z" #t)))) ; 4 (test-equal "5.2.2. skip m, starting at n" '(("v" "w" "z") () () () ("x" "y") (3 0 0 0 2)) (triv-runner (lambda () (test-assert "v" #t) (test-skip (test-match-nth 2 2)) (test-assert "w" #t) ; 1 (test-assert "x" #t) ; 2 SKIP (test-assert "y" #t) ; 3 SKIP (test-assert "z" #t)))) ; 4 (test-end) (test-begin "5.3. test-match-any") (test-equal "5.3.1. basic disjunction" '(("v" "w" "z") () () () ("x" "y") (3 0 0 0 2)) (triv-runner (lambda () (test-assert "v" #t) (test-skip (test-match-any (test-match-nth 3) (test-match-name "x"))) (test-assert "w" #t) ; 1 (test-assert "x" #t) ; 2 SKIP(NAME) (test-assert "y" #t) ; 3 SKIP(COUNT) (test-assert "z" #t)))) ; 4 (test-equal "5.3.2. disjunction is commutative" '(("v" "w" "z") () () () ("x" "y") (3 0 0 0 2)) (triv-runner (lambda () (test-assert "v" #t) (test-skip (test-match-any (test-match-name "x") (test-match-nth 3))) (test-assert "w" #t) ; 1 (test-assert "x" #t) ; 2 SKIP(NAME) (test-assert "y" #t) ; 3 SKIP(COUNT) (test-assert "z" #t)))) ; 4 (test-end) (test-begin "5.4. test-match-all") (test-equal "5.4.1. basic conjunction" '(("v" "w" "y" "z") () () () ("x") (4 0 0 0 1)) (triv-runner (lambda () (test-assert "v" #t) (test-skip (test-match-all (test-match-nth 2 2) (test-match-name "x"))) (test-assert "w" #t) ; 1 (test-assert "x" #t) ; 2 SKIP(NAME) & SKIP(COUNT) (test-assert "y" #t) ; 3 SKIP(COUNT) (test-assert "z" #t)))) ; 4 (test-equal "5.4.2. conjunction is commutative" '(("v" "w" "y" "z") () () () ("x") (4 0 0 0 1)) (triv-runner (lambda () (test-assert "v" #t) (test-skip (test-match-all (test-match-name "x") (test-match-nth 2 2))) (test-assert "w" #t) ; 1 (test-assert "x" #t) ; 2 SKIP(NAME) & SKIP(COUNT) (test-assert "y" #t) ; 3 SKIP(COUNT) (test-assert "z" #t)))) ; 4 (test-end) (test-end "5. Test specifiers") ;;; ;;; ;;; (test-begin "6. Skipping selected tests") (test-equal "6.1. Skip by specifier - match-name" '(("x") () () () ("y") (1 0 0 0 1)) (triv-runner (lambda () (test-begin "a") (test-skip (test-match-name "y")) (test-assert "x" #t) ; PASS (test-assert "y" #f) ; SKIP (test-end)))) (test-equal "6.2. Shorthand specifiers" '(("x") () () () ("y") (1 0 0 0 1)) (triv-runner (lambda () (test-begin "a") (test-skip "y") (test-assert "x" #t) ; PASS (test-assert "y" #f) ; SKIP (test-end)))) (test-begin "6.3. Specifier Stack") (test-equal "6.3.1. Clearing the Specifier Stack" '(("x" "x") ("y") () () ("y") (2 1 0 0 1)) (triv-runner (lambda () (test-begin "a") (test-skip "y") (test-assert "x" #t) ; PASS (test-assert "y" #f) ; SKIP (test-end) (test-begin "b") (test-assert "x" #t) ; PASS (test-assert "y" #f) ; FAIL (test-end)))) (test-equal "6.3.2. Inheriting the Specifier Stack" '(("x" "x") () () () ("y" "y") (2 0 0 0 2)) (triv-runner (lambda () (test-skip "y") (test-begin "a") (test-assert "x" #t) ; PASS (test-assert "y" #f) ; SKIP (test-end) (test-begin "b") (test-assert "x" #t) ; PASS (test-assert "y" #f) ; SKIP (test-end)))) (test-end);6.3 (test-begin "6.4. Short-circuit evaluation") (test-equal "6.4.1. In test-match-all" '(("x") ("y" "x" "z") () () ("y") (1 3 0 0 1)) (triv-runner (lambda () (test-begin "a") (test-skip (test-match-all "y" (test-match-nth 2))) ;; let's label the substructure forms so we can ;; see which one `test-match-nth' is going to skip ;; ; # "y" 2 result (test-assert "x" #t) ; 1 - #f #f PASS (test-assert "y" #f) ; 2 - #t #t SKIP (test-assert "y" #f) ; 3 - #t #f FAIL (test-assert "x" #f) ; 4 - #f #f FAIL (test-assert "z" #f) ; 5 - #f #f FAIL (test-end)))) (test-equal "6.4.2. In separate skip-list entries" '(("x") ("x" "z") () () ("y" "y") (1 2 0 0 2)) (triv-runner (lambda () (test-begin "a") (test-skip "y") (test-skip (test-match-nth 2)) ;; let's label the substructure forms so we can ;; see which one `test-match-nth' is going to skip ;; ; # "y" 2 result (test-assert "x" #t) ; 1 - #f #f PASS (test-assert "y" #f) ; 2 - #t #t SKIP (test-assert "y" #f) ; 3 - #t #f SKIP (test-assert "x" #f) ; 4 - #f #f FAIL (test-assert "z" #f) ; 5 - #f #f FAIL (test-end)))) (test-begin "6.4.3. Skipping test suites") (test-equal "6.4.3.1. Introduced using 'test-begin'" '(("x") () () () () (1 0 0 0 0)) (triv-runner (lambda () (test-begin "a") (test-skip "b") (test-begin "b") ; not skipped (test-assert "x" #t) (test-end "b") (test-end "a")))) (test-expect-fail 1) ;; ??? (test-equal "6.4.3.2. Introduced using 'test-group'" '(() () () () () (0 0 0 0 1)) (triv-runner (lambda () (test-begin "a") (test-skip "b") (test-group "b" ; skipped (test-assert "x" #t)) (test-end "a")))) (test-equal "6.4.3.3. Non-skipped 'test-group'" '(("x") () () () () (1 0 0 0 0)) (triv-runner (lambda () (test-begin "a") (test-skip "c") (test-group "b" (test-assert "x" #t)) (test-end "a")))) (test-end) ; 6.4.3 (test-end);6.4 (test-end "6. Skipping selected tests") ;;; ;;; ;;; (test-begin "7. Expected failures") (test-equal "7.1. Simple example" '(() ("x") ("z") () () (0 1 1 0 0)) (triv-runner (lambda () (test-assert "x" #f) (test-expect-fail "z") (test-assert "z" #f)))) (test-equal "7.2. Expected exception" '(() ("x") ("z") () () (0 1 1 0 0)) (triv-runner (lambda () (test-assert "x" #f) (test-expect-fail "z") (test-assert "z" (choke))))) (test-equal "7.3. Unexpectedly PASS" '(() () ("y") ("x") () (0 0 1 1 0)) (triv-runner (lambda () (test-expect-fail "x") (test-expect-fail "y") (test-assert "x" #t) (test-assert "y" #f)))) (test-end "7. Expected failures") ;;; ;;; ;;; (test-begin "8. Test-runner") ;;; ;;; Because we want this test suite to be accurate even ;;; when the underlying implementation chooses to use, e.g., ;;; a global variable to implement what could be thread variables ;;; or SRFI-39 parameter objects, we really need to save and restore ;;; their state ourselves ;;; (define (with-factory-saved thunk) (let* ((saved (test-runner-factory)) (result (thunk))) (test-runner-factory saved) result)) (test-begin "8.1. test-runner-current") (test-assert "8.1.1. automatically restored" (let ((a 0) (b 1) (c 2)) ; (triv-runner (lambda () (set! a (test-runner-current)) ;; (triv-runner (lambda () (set! b (test-runner-current)))) ;; (set! c (test-runner-current)))) ;; (and (eq? a c) (not (eq? a b))))) (test-end) (test-begin "8.2. test-runner-simple") (test-assert "8.2.1. default on-test hook" (eq? (test-runner-on-test-end (test-runner-simple)) test-on-test-end-simple)) (test-assert "8.2.2. default on-final hook" (eq? (test-runner-on-final (test-runner-simple)) test-on-final-simple)) (test-end) (test-begin "8.3. test-runner-factory") (test-assert "8.3.1. default factory" (eq? (test-runner-factory) test-runner-simple)) (test-assert "8.3.2. settable factory" (with-factory-saved (lambda () (test-runner-factory test-runner-null) ;; we have no way, without bringing in other SRFIs, ;; to make sure the following doesn't print anything, ;; but it shouldn't: (test-with-runner (test-runner-create) (lambda () (test-begin "a") (test-assert #t) ; pass (test-assert #f) ; fail (test-assert (vector-ref '#(3) 10)) ; fail with error (test-end "a"))) (eq? (test-runner-factory) test-runner-null)))) (test-end) ;;; This got tested about as well as it could in 8.3.2 (test-begin "8.4. test-runner-create") (test-end) ;;; This got tested about as well as it could in 8.3.2 (test-begin "8.5. test-runner-factory") (test-end) (test-begin "8.6. test-apply") (test-equal "8.6.1. Simple (form 1) test-apply" '(("w" "p" "v") () () () ("x") (3 0 0 0 1)) (triv-runner (lambda () (test-begin "a") (test-assert "w" #t) (test-apply (test-match-name "p") (lambda () (test-begin "p") (test-assert "x" #t) (test-end) (test-begin "z") (test-assert "p" #t) ; only this one should execute in here (test-end))) (test-assert "v" #t)))) (test-equal "8.6.2. Simple (form 2) test-apply" '(("w" "p" "v") () () () ("x") (3 0 0 0 1)) (triv-runner (lambda () (test-begin "a") (test-assert "w" #t) (test-apply (test-runner-current) (test-match-name "p") (lambda () (test-begin "p") (test-assert "x" #t) (test-end) (test-begin "z") (test-assert "p" #t) ; only this one should execute in here (test-end))) (test-assert "v" #t)))) (test-expect-fail 1) ;; depends on all test-match-nth being called. (test-equal "8.6.3. test-apply with skips" '(("w" "q" "v") () () () ("x" "p" "x") (3 0 0 0 3)) (triv-runner (lambda () (test-begin "a") (test-assert "w" #t) (test-skip (test-match-nth 2)) (test-skip (test-match-nth 4)) (test-apply (test-runner-current) (test-match-name "p") (test-match-name "q") (lambda () ; only execute if SKIP=no and APPLY=yes (test-assert "x" #t) ; # 1 SKIP=no APPLY=no (test-assert "p" #t) ; # 2 SKIP=yes APPLY=yes (test-assert "q" #t) ; # 3 SKIP=no APPLY=yes (test-assert "x" #f) ; # 4 SKIP=yes APPLY=no 0)) (test-assert "v" #t)))) ;;; Unfortunately, since there is no way to UNBIND the current test runner, ;;; there is no way to test the behavior of `test-apply' in the absence ;;; of a current runner within our little meta-test framework. ;;; ;;; To test the behavior manually, you should be able to invoke: ;;; ;;; (test-apply "a" (lambda () (test-assert "a" #t))) ;;; ;;; from the top level (with SRFI 64 available) and it should create a ;;; new, default (simple) test runner. (test-end) ;;; This entire suite depends heavily on 'test-with-runner'. If it didn't ;;; work, this suite would probably go down in flames (test-begin "8.7. test-with-runner") (test-end) ;;; Again, this suite depends heavily on many of the test-runner ;;; components. We'll just test those that aren't being exercised ;;; by the meta-test framework (test-begin "8.8. test-runner components") (define (auxtrack-runner thunk) (let ((r (test-runner-null))) (test-runner-aux-value! r '()) (test-runner-on-test-end! r (lambda (r) (test-runner-aux-value! r (cons (test-runner-test-name r) (test-runner-aux-value r))))) (test-with-runner r (thunk)) (reverse (test-runner-aux-value r)))) (test-equal "8.8.1. test-runner-aux-value" '("x" "" "y") (auxtrack-runner (lambda () (test-assert "x" #t) (test-begin "a") (test-assert #t) (test-end) (test-assert "y" #f)))) (test-end) ; 8.8 (test-end "8. Test-runner") (test-begin "9. Test Result Properties") (test-begin "9.1. test-result-alist") (define (symbol-alist? l) (if (null? l) #t (and (pair? l) (pair? (car l)) (symbol? (caar l)) (symbol-alist? (cdr l))))) ;;; check the various syntactic forms (test-assert (symbol-alist? (car (on-test-runner (lambda () (test-assert #t)) (lambda (r) (test-result-alist r)))))) (test-assert (symbol-alist? (car (on-test-runner (lambda () (test-assert #t)) (lambda (r) (test-result-alist r)))))) ;;; check to make sure the required properties are returned (test-equal '((result-kind . pass)) (prop-runner '(result-kind) (lambda () (test-assert #t))) ) (test-equal '((result-kind . fail) (expected-value . 2) (actual-value . 3)) (prop-runner '(result-kind expected-value actual-value) (lambda () (test-equal 2 (+ 1 2))))) (test-end "9.1. test-result-alist") (test-begin "9.2. test-result-ref") (test-equal '(pass) (on-test-runner (lambda () (test-assert #t)) (lambda (r) (test-result-ref r 'result-kind)))) (test-equal '(pass) (on-test-runner (lambda () (test-assert #t)) (lambda (r) (test-result-ref r 'result-kind)))) (test-equal '(fail pass) (on-test-runner (lambda () (test-assert (= 1 2)) (test-assert (= 1 1))) (lambda (r) (test-result-ref r 'result-kind)))) (test-end "9.2. test-result-ref") (test-begin "9.3. test-result-set!") (test-equal '(100 100) (on-test-runner (lambda () (test-assert (= 1 2)) (test-assert (= 1 1))) (lambda (r) (test-result-set! r 'foo 100) (test-result-ref r 'foo)))) (test-end "9.3. test-result-set!") (test-end "9. Test Result Properties") ;;; ;;; ;;; #| Time to stop having fun... (test-begin "9. For fun, some meta-test errors") (test-equal "9.1. Really PASSes, but test like it should FAIL" '(() ("b") () () ()) (triv-runner (lambda () (test-assert "b" #t)))) (test-expect-fail "9.2. Expect to FAIL and do so") (test-expect-fail "9.3. Expect to FAIL but PASS") (test-skip "9.4. SKIP this one") (test-assert "9.2. Expect to FAIL and do so" #f) (test-assert "9.3. Expect to FAIL but PASS" #t) (test-assert "9.4. SKIP this one" #t) (test-end) |# (test-end "SRFI 64 - Meta-Test Suite") ;;; |
Added srfi/tests/time.sps.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
;;; simple test procedures #!r6rs (import (rnrs) (rnrs mutable-pairs) (surfage s19 time) (surfage s48 intermediate-format-strings)) (define (printf fmt-str . args) (display (apply format fmt-str args))) (define s19-tests (list)) (define (define-s19-test! name thunk) (let ((name (if (symbol? name) name (string->symbol name))) (pr (assoc name s19-tests))) (if pr (set-cdr! pr thunk) (set! s19-tests (append s19-tests (list (cons name thunk))))))) (define (run-s19-test name thunk verbose) (if verbose (begin (display ";;; Running ") (display name))) (let ((result (thunk))) (if verbose (begin (display ": ") (display (not (not result))) (newline))) result)) (define (run-s19-tests . verbose) (let ((runs 0) (goods 0) (bads 0) (verbose (if (cdr verbose) (cdr verbose) #f))) (for-each (lambda (pr) (set! runs (+ runs 1)) (if (run-s19-test (car pr) (cdr pr) verbose) (set! goods (+ goods 1)) (set! bads (+ bads 1)))) s19-tests) (if verbose (begin (display ";;; Results: Runs: ") (display runs) (display "; Goods: ") (display goods) (display "; Bads: ") (display bads) (if (> runs 0) (begin (display "; Pass rate: ") (display (/ goods runs))) (display "; No tests.")) (newline))) (values runs goods bads))) (set! s19-tests (list)) (define-s19-test! "Creating time structures" (lambda () (not (null? (list (current-time 'time-tai) (current-time 'time-utc) (current-time 'time-monotonic) #|(current-time 'time-thread) (current-time 'time-process)|#))))) (define-s19-test! "Testing time resolutions" (lambda () (not (null? (list (time-resolution 'time-tai) (time-resolution 'time-utc) (time-resolution 'time-monotonic) #|(time-resolution 'time-thread) (time-resolution 'time-process)|#))))) (define-s19-test! "Time comparisons (time=?, etc.)" (lambda () (let ((t1 (make-time 'time-utc 0 1)) (t2 (make-time 'time-utc 0 1)) (t3 (make-time 'time-utc 0 2)) (t11 (make-time 'time-utc 1001 1)) (t12 (make-time 'time-utc 1001 1)) (t13 (make-time 'time-utc 1001 2)) ) (and (time=? t1 t2) (time>? t3 t2) (time<? t2 t3) (time>=? t1 t2) (time>=? t3 t2) (time<=? t1 t2) (time<=? t2 t3) (time=? t11 t12) (time>? t13 t12) (time<? t12 t13) (time>=? t11 t12) (time>=? t13 t12) (time<=? t11 t12) (time<=? t12 t13) )))) (define-s19-test! "Time difference" (lambda () (let ((t1 (make-time 'time-utc 0 3000)) (t2 (make-time 'time-utc 0 1000)) (t3 (make-time 'time-duration 0 2000)) (t4 (make-time 'time-duration 0 -2000))) (and (time=? t3 (time-difference t1 t2)) (time=? t4 (time-difference t2 t1)))))) (define-s19-test! "Time difference, nanoseconds" (lambda () (let ((t1 (make-time time-utc 1000 3000)) (t2 (make-time time-utc 0 3000)) (t3 (make-time time-duration 1000 0)) (t4 (make-time time-duration 999999000 -1))) (and (time=? t3 (time-difference t1 t2)) (time=? t4 (time-difference t2 t1)))))) (define (test-one-utc-tai-edge utc tai-diff tai-last-diff) (let* (;; right on the edge they should be the same (utc-basic (make-time 'time-utc 0 utc)) (tai-basic (make-time 'time-tai 0 (+ utc tai-diff))) (utc->tai-basic (time-utc->time-tai utc-basic)) (tai->utc-basic (time-tai->time-utc tai-basic)) ;; a second before they should be the old diff (utc-basic-1 (make-time 'time-utc 0 (- utc 1))) (tai-basic-1 (make-time 'time-tai 0 (- (+ utc tai-last-diff) 1))) (utc->tai-basic-1 (time-utc->time-tai utc-basic-1)) (tai->utc-basic-1 (time-tai->time-utc tai-basic-1)) ;; a second later they should be the new diff (utc-basic+1 (make-time 'time-utc 0 (+ utc 1))) (tai-basic+1 (make-time 'time-tai 0 (+ (+ utc tai-diff) 1))) (utc->tai-basic+1 (time-utc->time-tai utc-basic+1)) (tai->utc-basic+1 (time-tai->time-utc tai-basic+1)) ;; ok, let's move the clock half a month or so plus half a second (shy (* 15 24 60 60)) (hs (/ (expt 10 9) 2)) ;; a second later they should be the new diff (utc-basic+2 (make-time 'time-utc hs (+ utc shy))) (tai-basic+2 (make-time 'time-tai hs (+ (+ utc tai-diff) shy))) (utc->tai-basic+2 (time-utc->time-tai utc-basic+2)) (tai->utc-basic+2 (time-tai->time-utc tai-basic+2)) ) (and (time=? utc-basic tai->utc-basic) (time=? tai-basic utc->tai-basic) (time=? utc-basic-1 tai->utc-basic-1) (time=? tai-basic-1 utc->tai-basic-1) (time=? utc-basic+1 tai->utc-basic+1) (time=? tai-basic+1 utc->tai-basic+1) (time=? utc-basic+2 tai->utc-basic+2) (time=? tai-basic+2 utc->tai-basic+2) ))) (define-s19-test! "TAI-UTC Conversions" (lambda () (and (test-one-utc-tai-edge 915148800 32 31) (test-one-utc-tai-edge 867715200 31 30) (test-one-utc-tai-edge 820454400 30 29) (test-one-utc-tai-edge 773020800 29 28) (test-one-utc-tai-edge 741484800 28 27) (test-one-utc-tai-edge 709948800 27 26) (test-one-utc-tai-edge 662688000 26 25) (test-one-utc-tai-edge 631152000 25 24) (test-one-utc-tai-edge 567993600 24 23) (test-one-utc-tai-edge 489024000 23 22) (test-one-utc-tai-edge 425865600 22 21) (test-one-utc-tai-edge 394329600 21 20) (test-one-utc-tai-edge 362793600 20 19) (test-one-utc-tai-edge 315532800 19 18) (test-one-utc-tai-edge 283996800 18 17) (test-one-utc-tai-edge 252460800 17 16) (test-one-utc-tai-edge 220924800 16 15) (test-one-utc-tai-edge 189302400 15 14) (test-one-utc-tai-edge 157766400 14 13) (test-one-utc-tai-edge 126230400 13 12) (test-one-utc-tai-edge 94694400 12 11) (test-one-utc-tai-edge 78796800 11 10) (test-one-utc-tai-edge 63072000 10 0) (test-one-utc-tai-edge 0 0 0) ;; at the epoch (test-one-utc-tai-edge 10 0 0) ;; close to it ... (test-one-utc-tai-edge 1045789645 32 32) ;; about now ... ))) (define (tm:date= d1 d2) (and (= (date-year d1) (date-year d2)) (= (date-month d1) (date-month d2)) (= (date-day d1) (date-day d2)) (= (date-hour d1) (date-hour d2)) (= (date-second d1) (date-second d2)) (= (date-nanosecond d1) (date-nanosecond d2)) (= (date-zone-offset d1) (date-zone-offset d2)))) (define-s19-test! "TAI-Date Conversions" (lambda () (and (tm:date= (time-tai->date (make-time time-tai 0 (+ 915148800 29)) 0) (make-date 0 58 59 23 31 12 1998 0)) (tm:date= (time-tai->date (make-time time-tai 0 (+ 915148800 30)) 0) (make-date 0 59 59 23 31 12 1998 0)) (tm:date= (time-tai->date (make-time time-tai 0 (+ 915148800 31)) 0) (make-date 0 60 59 23 31 12 1998 0)) (tm:date= (time-tai->date (make-time time-tai 0 (+ 915148800 32)) 0) (make-date 0 0 0 0 1 1 1999 0))))) (define-s19-test! "Date-UTC Conversions" (lambda () (and (time=? (make-time time-utc 0 (- 915148800 2)) (date->time-utc (make-date 0 58 59 23 31 12 1998 0))) (time=? (make-time time-utc 0 (- 915148800 1)) (date->time-utc (make-date 0 59 59 23 31 12 1998 0))) ;; yes, I think this is acutally right. (time=? (make-time time-utc 0 (- 915148800 0)) (date->time-utc (make-date 0 60 59 23 31 12 1998 0))) (time=? (make-time time-utc 0 (- 915148800 0)) (date->time-utc (make-date 0 0 0 0 1 1 1999 0))) (time=? (make-time time-utc 0 (+ 915148800 1)) (date->time-utc (make-date 0 1 0 0 1 1 1999 0)))))) (define-s19-test! "TZ Offset conversions" (lambda () (let ((ct-utc (make-time time-utc 6320000 1045944859)) (ct-tai (make-time time-tai 6320000 1045944891)) (cd (make-date 6320000 19 14 15 22 2 2003 -18000))) (and (time=? ct-utc (date->time-utc cd)) (time=? ct-tai (date->time-tai cd)))))) (begin (newline) (run-s19-tests #t)) (define (date->string/all-formats) ;; TODO: figure out why ~f isn't working ;; TODO: figure out why ~x and ~X aren't doing what the srfi-19 doc says they do (define fs '("~~" "~a" "~A" "~b" "~B" "~c" "~d" "~D" "~e" #;"~f" "~h" "~H" "~I" "~j" "~k" "~l" "~m" "~M" "~n" "~N" "~p" "~r" "~s" "~S" "~t" "~T" "~U" "~V" "~w" "~W" "~x" "~X" "~y" "~Y" "~z" "~Z" "~1" "~2" "~3" "~4" "~5")) (define cd (current-date)) (display "\n;;; Running date->string format exercise\n") (printf "(current-date)\n => ~s\n" cd) (for-each (lambda (f) (printf "\n--- Format: ~a ----------------------------------------\n" f) (display (date->string cd f)) (newline)) fs)) ;;TODO #;(define (string->date/all-formats) ) (date->string/all-formats) #;(string->date/all-formats) |
Added srfi/tests/tmp1.
> > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 |
0 1 2 3 4 5 6 7 8 9 |
Added srfi/tests/vectors.sps.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 |
; Test suite for SRFI 43 ; ; $Id: srfi-43-test.sps 6152 2009-03-19 22:30:05Z will $ (import (except (rnrs base) vector-fill! vector->list list->vector vector-map vector-for-each) (rnrs lists) (rnrs io simple) (surfage s6 basic-string-ports) (surfage s43 vectors)) (define (writeln . xs) (for-each display xs) (newline)) (define (fail token . more) (writeln "Error: test failed: " token) #f) (or (vector? (make-vector 0)) (fail 'make-vector:0)) (or (= 10 (vector-length (make-vector 10))) (fail 'vector-length:basic)) (or (= 97 (vector-ref (make-vector 500 97) 499)) (fail 'vector-ref:basic)) (or (equal? (vector) '#()) (fail 'vector:0)) (or (equal? (vector 'a 'b 97) '#(a b 97)) (fail 'vector)) (or (equal? (vector-unfold (lambda (i x) (values x (- x 1))) 10 0) '#(0 -1 -2 -3 -4 -5 -6 -7 -8 -9)) ; but SRFI 43 says -8 -8 at end (fail 'vector-unfold:1)) (or (equal? (vector-unfold values 10) '#(0 1 2 3 4 5 6 7 8 9)) (fail 'vector-unfold:2)) (or (let ((vector '#(a b 97))) (equal? (vector-unfold (lambda (i) (vector-ref vector i)) (vector-length vector)) vector)) (fail 'vector-unfold:3)) (or (equal? (vector-unfold-right (lambda (i x) (values x (+ x 1))) 8 0) '#(7 6 5 4 3 2 1 0)) (fail 'vector-unfold-right:1)) (or (let ((vector '#(3 1 4 5 9))) (equal? (vector-unfold-right (lambda (i x) (values (vector-ref vector x) (+ x 1))) (vector-length vector) 0) '#(9 5 4 1 3))) (fail 'vector-unfold-right:2)) (or (equal? (vector-copy '#(a b c d e f g h i)) '#(a b c d e f g h i)) (fail 'vector-copy:1)) (or (equal? (vector-copy '#(a b c d e f g h i) 6) '#(g h i)) (fail 'vector-copy:2)) (or (equal? (vector-copy '#(a b c d e f g h i) 3 6) '#(d e f)) (fail 'vector-copy:3)) (or (equal? (vector-copy '#(a b c d e f g h i) 6 12 'x) '#(g h i x x x)) (fail 'vector-copy:4)) (or (equal? (vector-reverse-copy '#(5 4 3 2 1 0) 1 5) '#(1 2 3 4)) (fail 'vector-reverse-copy)) (or (equal? (vector-append '#(x) '#(y)) '#(x y)) (fail 'vector-append:1)) (or (equal? (vector-append '#(a) '#(b c d)) '#(a b c d)) (fail 'vector-append:2)) (or (equal? (vector-append '#(a #(b)) '#(#(c))) '#(a #(b) #(c))) (fail 'vector-append:3)) (or (equal? (vector-concatenate '(#(a b) #(c d))) '#(a b c d)) (fail 'vector-concatenate)) (or (and (eq? (vector? '#(a b c)) #t) (eq? (vector? '(a b c)) #f) (eq? (vector? #t) #f) (eq? (vector? '#()) #t) (eq? (vector? '()) #f)) (fail 'vector?)) (or (and (eq? (vector-empty? '#(a)) #f) (eq? (vector-empty? '#(())) #f) (eq? (vector-empty? '#(#())) #f) (eq? (vector-empty? '#()) #t)) (fail 'vector-empty?)) (or (and (eq? (vector= eq? '#(a b c d) '#(a b c d)) #t) (eq? (vector= eq? '#(a b c d) '#(a b d c)) #f) (eq? (vector= = '#(1 2 3 4 5) '#(1 2 3 4)) #f) (eq? (vector= = '#(1 2 3 4) '#(1 2 3 4)) #t) (eq? (vector= eq?) #t) (eq? (vector= eq? '#(a)) #t) (eq? (vector= eq? (vector (vector 'a)) (vector (vector 'a))) #f) (eq? (vector= equal? (vector (vector 'a)) (vector (vector 'a))) #t)) (fail 'vector=)) (or (eq? (vector-ref '#(a b c d) 2) 'c) (fail 'vector-ref)) (or (eq? (vector-length '#(a b c)) 3) (fail 'vector-length)) (or (equal? (vector-fold (lambda (index len str) (max (string-length str) len)) 0 '#("a" "b" "" "dd" "e")) 2) (fail 'vector-fold:1)) (or (equal? (vector-fold (lambda (index tail elt) (cons elt tail)) '() '#(0 1 2 3 4)) '(4 3 2 1 0)) (fail 'vector-fold:2)) (or (equal? (vector-fold (lambda (index counter n) (if (even? n) (+ counter 1) counter)) 0 '#(0 1 2 3 4 4 4 5 6 7)) 6) (fail 'vector-fold:3)) (or (equal? (vector-fold-right (lambda (index tail elt) (cons elt tail)) '() '#(a b c d)) '(a b c d)) (fail 'vector-fold-right)) (or (equal? (vector-map (lambda (i x) (* x x)) (vector-unfold (lambda (i x) (values x (+ x 1))) 4 1)) '#(1 4 9 16)) (fail 'vector-map:1)) (or (equal? (vector-map (lambda (i x y) (* x y)) (vector-unfold (lambda (i x) (values x (+ x 1))) 5 1) (vector-unfold (lambda (i x) (values x (- x 1))) 5 5)) '#(5 8 9 8 5)) (fail 'vector-map:2)) (or (member (let ((count 0)) (vector-map (lambda (ignored-index ignored-elt) (set! count (+ count 1)) count) '#(a b))) '(#(1 2) #(2 1))) (fail 'vector-map:3)) (or (equal? (let ((v (vector 1 2 3 4))) (vector-map! (lambda (i elt) (+ i elt)) v) v) '#(1 3 5 7)) (fail 'vector-map!)) (or (equal? (let ((p (open-output-string))) (vector-for-each (lambda (i x) (display x p) (newline p)) '#("foo" "bar" "baz" "quux" "zot")) (get-output-string p)) "foo\nbar\nbaz\nquux\nzot\n") (fail 'vector-for-each)) (or (equal? (vector-count (lambda (i elt) (even? elt)) '#(3 1 4 1 5 9 2 5 6)) 3) (fail 'vector-count:1)) (or (equal? (vector-count (lambda (i x y) (< x y)) '#(1 3 6 9) '#(2 4 6 8 10 12)) 2) (fail 'vector-count:2)) (or (equal? (vector-index even? '#(3 1 4 1 5 9)) 2) (fail 'vector-index:1)) (or (equal? (vector-index < '#(3 1 4 1 5 9 2 5 6) '#(2 7 1 8 2)) 1) (fail 'vector-index:2)) (or (equal? (vector-index = '#(3 1 4 1 5 9 2 5 6) '#(2 7 1 8 2)) #f) (fail 'vector-index:3)) (or (equal? (vector-index-right even? '#(3 1 4 1 5 9)) 2) (fail 'vector-index-right:1)) (or (equal? (vector-index-right < '#(3 1 4 1 5) '#(2 7 1 8 2)) 3) (fail 'vector-index-right:2)) (or (equal? (vector-index-right = '#(3 1 4 1 5) '#(2 7 1 8 2)) #f) (fail 'vector-index-right:3)) (or (equal? (vector-skip even? '#(3 1 4 1 5 9)) 0) (fail 'vector-skip:1)) (or (equal? (vector-skip < '#(3 1 4 1 5 9 2 5 6) '#(2 7 1 8 2)) 0) (fail 'vector-skip:2)) (or (equal? (vector-skip = '#(3 1 4 1 5 9 2 5 6) '#(2 7 1 8 2)) 0) (fail 'vector-skip:3)) (or (equal? (vector-skip > '#(3 1 4 1 5 9 2 5 6) '#(2 7 1 8 2)) 1) (fail 'vector-skip:4)) (or (equal? (vector-skip-right even? '#(3 1 4 1 5 9)) 5) (fail 'vector-skip-right:1)) (or (equal? (vector-skip-right < '#(3 1 4 1 5) '#(2 7 1 8 2)) 4) (fail 'vector-skip-right:2)) (or (equal? (vector-skip-right = '#(3 1 4 1 5) '#(2 7 1 8 2)) 4) (fail 'vector-skip-right:3)) (or (equal? (vector-skip-right > '#(3 1 4 1 5) '#(2 7 1 8 2)) 3) (fail 'vector-skip-right:4)) (define (string-comparator s1 s2) (cond ((< (string-length s1) (string-length s2)) -1) ((> (string-length s1) (string-length s2)) +1) ((string<? s1 s2) -1) ((string>? s1 s2) +1) (else 0))) (or (equal? (vector-binary-search '#() "bad" string-comparator) #f) (fail 'vector-binary-search:0)) (or (equal? (vector-binary-search '#("ab" "cd" "ef" "bcd" "cde" "aaaa") "bad" string-comparator) #f) (fail 'vector-binary-search:1)) (or (equal? (vector-binary-search '#("ab" "cd" "ef" "bcd" "cde" "aaaa") "" string-comparator) #f) (fail 'vector-binary-search:2)) (or (equal? (vector-binary-search '#("ab" "cd" "ef" "bcd" "cde" "aaaa") "hello" string-comparator) #f) (fail 'vector-binary-search:3)) (or (equal? (vector-binary-search '#("ab" "cd" "ef" "bcd" "cde" "aaaa") "ab" string-comparator) 0) (fail 'vector-binary-search:4)) (or (equal? (vector-binary-search '#("ab" "cd" "ef" "bcd" "cde" "aaaa") "aaaa" string-comparator) 5) (fail 'vector-binary-search:5)) (or (equal? (vector-binary-search '#("ab" "cd" "ef" "bcd" "cde" "aaaa") "bcd" string-comparator) 3) (fail 'vector-binary-search:6)) (or (equal? (vector-any list '#() '#(a b c)) #f) (fail 'vector-any:0)) (or (equal? (vector-any list '#(a b c) '#()) #f) (fail 'vector-any:1)) (or (equal? (vector-any list '#(a b c) '#(d)) '(a d)) (fail 'vector-any:2)) (or (equal? (vector-any memq '#(a b c) '#(() (c d e) (b c 97))) '(c 97)) (fail 'vector-any:3)) (or (equal? (vector-every list '#() '#(a b c)) #t) (fail 'vector-every:0)) (or (equal? (vector-every list '#(a b c) '#()) #t) (fail 'vector-every:1)) (or (equal? (vector-every list '#(a b c) '#(d)) '(a d)) (fail 'vector-every:2)) (or (equal? (vector-every memq '#(a b c) '#(() (c d e) (b c 97))) #f) (fail 'vector-every:3)) (or (equal? (let ((v (vector 0 1 2 3))) (vector-set! v 1 11) v) '#(0 11 2 3)) (fail 'vector-set!)) (or (equal? (let ((v (vector 0 1 2 3))) (vector-swap! v 1 3) v) '#(0 3 2 1)) (fail 'vector-swap!)) (or (equal? (let ((v (vector))) (vector-fill! v 97) v) '#()) (fail 'vector-fill!:0)) (or (equal? (let ((v (vector 0 1 2 3))) (vector-fill! v 97) v) '#(97 97 97 97)) (fail 'vector-fill!:1)) (or (equal? (let ((v (vector 0 1 2 3))) (vector-fill! v 97 1) v) '#(0 97 97 97)) (fail 'vector-fill!:2)) (or (equal? (let ((v (vector 0 1 2 3))) (vector-fill! v 97 1 2) v) '#(0 97 2 3)) (fail 'vector-fill!:3)) (or (equal? (let ((v (vector))) (vector-reverse! v) v) '#()) (fail 'vector-reverse!:0)) (or (equal? (let ((v (vector 0 1 2 3))) (vector-reverse! v) v) '#(3 2 1 0)) (fail 'vector-reverse!:1)) (or (equal? (let ((v (vector 0 1 2 3))) (vector-reverse! v 1) v) '#(0 3 2 1)) (fail 'vector-reverse!:2)) (or (equal? (let ((v (vector 0 1 2 3))) (vector-reverse! v 1 3) v) '#(0 2 1 3)) (fail 'vector-reverse!:3)) (or (equal? (let ((v (vector)) (src '#(100 101 102 103 104 105))) (vector-copy! v 0 v) v) '#()) (fail 'vector-copy!:0)) (or (equal? (let ((v (vector 0 1 2 3 4 5)) (src '#(100 101 102 103 104 105))) (vector-copy! v 0 src) v) '#(100 101 102 103 104 105)) (fail 'vector-copy!:1)) (or (equal? (let ((v (vector 0 1 2 3)) (src '#(100 101 102 103 104 105))) (vector-copy! v 1 src 4) v) '#(0 104 105 3)) (fail 'vector-copy!:2)) (or (equal? (let ((v (vector 0 1 2 3)) (src '#(100 101 102 103 104 105))) (vector-copy! v 1 src 2 4) v) '#(0 102 103 3)) (fail 'vector-copy!:3)) (or (equal? (let ((v (vector)) (src '#(100 101 102 103 104 105))) (vector-reverse-copy! v 0 v) v) '#()) (fail 'vector-reverse-copy!:0)) (or (equal? (let ((v (vector 0 1 2 3 4 5)) (src '#(100 101 102 103 104 105))) (vector-reverse-copy! v 0 src) v) '#(105 104 103 102 101 100)) (fail 'vector-reverse-copy!:1)) (or (equal? (let ((v (vector 0 1 2 3)) (src '#(100 101 102 103 104 105))) (vector-reverse-copy! v 1 src 4) v) '#(0 105 104 3)) (fail 'vector-reverse-copy!:2)) (or (equal? (let ((v (vector 0 1 2 3)) (src '#(100 101 102 103 104 105))) (vector-reverse-copy! v 1 src 2 4) v) '#(0 103 102 3)) (fail 'vector-reverse-copy!:3)) (or (equal? (vector->list '#()) '()) (fail 'vector->list:0)) (or (equal? (vector->list '#(a b c)) '(a b c)) (fail 'vector->list:1)) (or (equal? (vector->list '#(a b c d e) 1) '(b c d e)) (fail 'vector->list:2)) (or (equal? (vector->list '#(a b c d e) 1 4) '(b c d)) (fail 'vector->list:3)) (or (equal? (reverse-vector->list '#()) '()) (fail 'reverse-vector->list:0)) (or (equal? (reverse-vector->list '#(a b c)) '(c b a)) (fail 'reverse-vector->list:1)) (or (equal? (reverse-vector->list '#(a b c d e) 1) '(e d c b)) (fail 'reverse-vector->list:2)) (or (equal? (reverse-vector->list '#(a b c d e) 1 3) '(c b)) (fail 'reverse-vector->list:3)) (or (equal? (list->vector '()) '#()) (fail 'list->vector:0)) (or (equal? (list->vector '(a b c)) '#(a b c)) (fail 'list->vector:1)) (or (equal? (reverse-list->vector '()) '#()) (fail 'reverse-list->vector:0)) (or (equal? (reverse-list->vector '(a b c)) '#(c b a)) (fail 'reverse-list->vector:1)) (writeln "Done.") |