|
4 | 4 | "reduce.rkt"
|
5 | 5 | (except-in redex/reduction-semantics plug)
|
6 | 6 | racket/runtime-path)
|
| 7 | +(module+ test (require rackunit)) |
7 | 8 |
|
8 | 9 | (provide (all-defined-out))
|
9 | 10 |
|
|
585 | 586 | ;; it can. redex-check doesn't currently have support for shrinkers, so
|
586 | 587 | ;; this is just left on the side to be used when a counterexample is found.
|
587 | 588 |
|
588 |
| -(define (shrink p) |
589 |
| - (let loop ([p p]) |
590 |
| - (cond |
591 |
| - [(same-behavior? p) |
592 |
| - #f] |
593 |
| - [else |
594 |
| - (or (for/or ([candidate (in-list (shrink/fixed-candidates p))]) |
595 |
| - (loop candidate)) |
596 |
| - p)]))) |
| 589 | +(define (shrink p #:looks-buggy? [looks-buggy? (λ (x) (not (same-behavior? x)))]) |
| 590 | + (cond |
| 591 | + [(looks-buggy? p) |
| 592 | + (let known-buggy-loop ([p p]) |
| 593 | + ;; p is known to have a bug |
| 594 | + (let shrunk-candidates-loop ([shrunk-ps (shrink/fixed-candidates p)]) |
| 595 | + (cond |
| 596 | + [(null? shrunk-ps) |
| 597 | + ;; none of the candidates were buggy, so `p` |
| 598 | + ;; is a shrunken as we can make it |
| 599 | + p] |
| 600 | + [else |
| 601 | + (define candidate-p (car shrunk-ps)) |
| 602 | + ;; candidate-p is a shrunk version of `p` |
| 603 | + (cond |
| 604 | + [(looks-buggy? candidate-p) |
| 605 | + ;; greedily take the first buggy-looking |
| 606 | + ;; candidate and try to shrink it further |
| 607 | + (known-buggy-loop candidate-p)] |
| 608 | + [else |
| 609 | + ;; keep looking at the shrunken candidates |
| 610 | + (shrunk-candidates-loop (cdr shrunk-ps))])])))] |
| 611 | + [else |
| 612 | + (raise-argument-error 'shrink |
| 613 | + "a term that looks buggy" |
| 614 | + p)])) |
597 | 615 |
|
598 | 616 | ;; shrink/fixed-candidates : p -> (listof p)
|
599 | 617 | (define (shrink/fixed-candidates p)
|
|
602 | 620 | (define orig-size (size p))
|
603 | 621 | (define shrunk-es (shrink-candidates e))
|
604 | 622 | (define shrunk-ps '())
|
605 |
| - (for ([shrunk-e shrunk-es]) |
| 623 | + (for ([shrunk-e (in-list shrunk-es)]) |
606 | 624 | (define shrunk-p (fix-prog `(<> ,s ,o ,shrunk-e)))
|
607 | 625 | (when (< (size shrunk-p) orig-size)
|
608 | 626 | ;; this `<` guards against a shrinking
|
|
678 | 696 |
|
679 | 697 | (define (shrink-rule e)
|
680 | 698 | (match e
|
681 |
| - [`(wcm ((,v1 ,v2) ,more ...) ,e) |
682 |
| - (list `(wcm ,more ,e))] |
683 | 699 | [`(wcm () ,e) (list e)]
|
| 700 | + [`(wcm ,bindings ,e) |
| 701 | + (for/list ([i (in-range (length bindings))]) |
| 702 | + `(wcm ,(remove-ith bindings i) ,e))] |
684 | 703 | [`(% ,e1 ,e2 ,e3) (those-with-holes-or-all (list e1 e2 e3))]
|
685 | 704 | [`(set! ,x ,e) (list e)]
|
686 | 705 | [`(if ,e1 ,e2 ,e3) (those-with-holes-or-all (list e1 e2 e3))]
|
687 | 706 | [`(dw ,x ,e1 ,e2 ,e3) (those-with-holes-or-all (list e1 e2 e3))]
|
688 | 707 | [`(list ,es ...) (those-with-holes-or-all es)]
|
689 | 708 | [`(λ (,x ...) ,e) (list e)]
|
690 | 709 | [`(cont ,v ,e) (those-with-holes-or-all (list v e))]
|
691 |
| - [`(comp ,e) e] |
| 710 | + [`(comp ,e) (list e)] |
692 | 711 | [`(,f ,x ...)
|
693 | 712 | (those-with-holes-or-all (cons f x))]
|
694 | 713 | [(? x?) (list 0)]
|
695 | 714 | [_ '()]))
|
696 | 715 |
|
| 716 | +(define (remove-ith l i) |
| 717 | + (append (take l i) |
| 718 | + (drop l (+ i 1)))) |
| 719 | +(module+ test |
| 720 | + (check-equal? (remove-ith (list #f) 0) '()) |
| 721 | + (check-equal? (remove-ith (list 0 1 2) 0) (list 1 2)) |
| 722 | + (check-equal? (remove-ith (list 0 1 2) 1) (list 0 2)) |
| 723 | + (check-equal? (remove-ith (list 0 1 2) 2) (list 0 1))) |
| 724 | + |
697 | 725 | (define (those-with-holes-or-all lst)
|
698 | 726 | (cond
|
699 | 727 | [(has-hole? lst)
|
|
709 | 737 | [(list? e) (for/or ([e (in-list e)])
|
710 | 738 | (loop e))]
|
711 | 739 | [else #f])))
|
| 740 | + |
| 741 | +(module+ test |
| 742 | + (define (test-shrink-candidates p) |
| 743 | + (define candidates (set)) |
| 744 | + (define first-call? #t) |
| 745 | + (shrink p |
| 746 | + #:looks-buggy? |
| 747 | + (λ (x) |
| 748 | + (cond |
| 749 | + [first-call? |
| 750 | + (set! first-call? #f) |
| 751 | + #t] |
| 752 | + [else |
| 753 | + (set! candidates (set-add candidates x)) |
| 754 | + #f]))) |
| 755 | + candidates) |
| 756 | + |
| 757 | + (check-equal? (test-shrink-candidates `(<> () () 1)) |
| 758 | + (set)) |
| 759 | + (check-equal? (test-shrink-candidates `(<> () () (+ 1 2))) |
| 760 | + (set `(<> () () +) |
| 761 | + `(<> () () 1) |
| 762 | + `(<> () () 2))) |
| 763 | + (check-equal? (test-shrink-candidates `(<> () () ((1 2) (3 4)))) |
| 764 | + (set `(<> () () (1 2)) |
| 765 | + `(<> () () (3 4)) |
| 766 | + `(<> () () (1 (3 4))) |
| 767 | + `(<> () () (2 (3 4))) |
| 768 | + `(<> () () ((1 2) 3)) |
| 769 | + `(<> () () ((1 2) 4))))) |
0 commit comments