diff --git a/math.rkt b/math.rkt index 662ee39..06040f4 100644 --- a/math.rkt +++ b/math.rkt @@ -24,12 +24,12 @@ (syntax-parser [(_ f:id) #:with f: (format-id #'f "~a:" (syntax-e #'f)) - #'(define-syntax f: - (syntax-parser + #'(define-syntax (f: stx) + (syntax-parse stx [(g e* (... ...)) #:with e+* (for/list ([e (in-list (syntax->list #'(e* (... ...))))]) (expand-expr e)) - (let ([e++ (reduce/op f (syntax->list #'e+*))]) + (let ([e++ (reduce/op f (syntax->list #'e+*) #:src stx)]) (if (list? e++) (quasisyntax/loc #'g (f #,@e++)) (quasisyntax/loc #'g #,e++)))] @@ -45,14 +45,17 @@ ;; ----------------------------------------------------------------------------- +(define-for-syntax (division-by-zero stx) + (raise-syntax-error '/ "division by zero" stx)) + ;; Simplify a list of expressions using an associative binary operator. ;; Return either: ;; - A numeric value ;; - A list of syntax objects, to be spliced back in the source code -(define-for-syntax (reduce/op op e*) - (let loop ([prev #f] ;; (U #f Number), candidate for reduction - [acc '()] ;; (Listof Syntax), irreducible arguments - [e* e*]) ;; (Listof Syntax), arguments to process +(define-for-syntax (reduce/op op expr* #:src stx) + (let loop ([prev #f] ;; (U #f Number), candidate for reduction + [acc '()] ;; (Listof Syntax), irreducible arguments + [e* expr*]) ;; (Listof Syntax), arguments to process (if (null? e*) ;; then: finished, return a number (prev) or list of expressions (acc) (if (null? acc) @@ -65,7 +68,8 @@ (if prev ;; Watch for division-by-zero (if (and (zero? v) (eq? / op)) - (loop v (cons prev acc) (cdr e*)) + (division-by-zero stx) + ;(loop v (cons prev acc) (cdr e*)) (loop (op prev v) acc (cdr e*))) (loop v acc (cdr e*))) ;; else: save value in acc diff --git a/test/math-fail.rkt b/test/math-fail.rkt index 720d9a9..824ea3c 100644 --- a/test/math-fail.rkt +++ b/test/math-fail.rkt @@ -17,8 +17,9 @@ (ann ((lambda ([f : (-> Natural Natural Integer)]) (f 0 0)) -:) Zero) (ann ((lambda ([f : (-> Natural Natural Natural)]) (f 0 0)) *:) Zero) (ann ((lambda ([f : (-> Natural Natural Exact-Rational)]) (f 0 0)) /:) Zero) - ;; -- dividing by zero => fall back to racket/base - (ann (/: 1 1 0) One) + ;; -- dividing by zero => caught statically + (/: 1 1 0) + (/: 1 1 (+: 4 -2 -2)) ))) ;; ----------------------------------------------------------------------------- @@ -27,11 +28,11 @@ (require rackunit) - (define (format-eval stx) + (define (math-eval stx) (lambda () ;; For `check-exn` (compile-syntax stx))) (for ([rkt (in-list TEST-CASE*)]) - (check-exn #rx"format::|Type Checker" - (format-eval rkt))) + (check-exn #rx"/:|Type Checker" + (math-eval rkt))) ) diff --git a/test/math-pass.rkt b/test/math-pass.rkt index d5b4aac..c8ceb91 100644 --- a/test/math-pass.rkt +++ b/test/math-pass.rkt @@ -50,10 +50,6 @@ (check-equal? (ann (/: 0 1 2 3 4) Zero) 0) (check-equal? (ann (/: 9 9) One) 1) - ;; We do not catch this statically - (check-exn exn:fail:contract? - (lambda () (/: 3 0))) - (check-equal? (ann ((lambda ([f : (-> Integer Integer Exact-Rational)]) (f 1 1)) /:) Real) 1) @@ -82,7 +78,5 @@ (check-equal? (ann (let ([n 5]) (*: n 1/5 1)) Exact-Rational) 1) (check-equal? (ann (let ([n 5]) (*: 3 n (+: -1 2))) Natural) 15) (check-equal? (ann (let ([n 4]) (/: n n)) Positive-Exact-Rational) 1) - (check-exn #rx"division by zero" - (lambda () (ann (/: 0 0) Zero))) ;; Same for racket/base )