[math] add basic operators
This commit is contained in:
parent
0a90e31e40
commit
7e30256efc
48
math.rkt
48
math.rkt
|
@ -1,7 +1,7 @@
|
||||||
#lang typed/racket/base
|
#lang typed/racket/base
|
||||||
|
|
||||||
(provide
|
(provide
|
||||||
+: ;-: *: /:
|
+: -: *: /:
|
||||||
;; Fold syntactic constants
|
;; Fold syntactic constants
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,38 +22,44 @@
|
||||||
#:with f: (format-id #'f "~a:" (syntax-e #'f))
|
#:with f: (format-id #'f "~a:" (syntax-e #'f))
|
||||||
#'(define-syntax f:
|
#'(define-syntax f:
|
||||||
(syntax-parser
|
(syntax-parser
|
||||||
[g:id
|
|
||||||
(syntax/loc #'g f)]
|
|
||||||
[(g e* (... ...))
|
[(g e* (... ...))
|
||||||
#:with e+* (for/list ([e (in-list (syntax->list #'(e* (... ...))))])
|
#:with e+* (for/list ([e (in-list (syntax->list #'(e* (... ...))))])
|
||||||
(expand-expr e))
|
(expand-expr e))
|
||||||
#:with e++ (reduce/op f (syntax->list #'e+*) #:src #'g)
|
(let ([e++ (reduce/op f (syntax->list #'e+*))])
|
||||||
(syntax/loc #'g e++)]
|
(if (list? e++)
|
||||||
|
(quasisyntax/loc #'g (f #,@e++))
|
||||||
|
(quasisyntax/loc #'g #,e++)))]
|
||||||
|
[g:id
|
||||||
|
(syntax/loc #'g f)]
|
||||||
[(g e* (... ...))
|
[(g e* (... ...))
|
||||||
(syntax/loc #'g (f e* (... ...)))]))]))
|
(syntax/loc #'g (f e* (... ...)))]))]))
|
||||||
|
|
||||||
(make-numeric-operator +)
|
(make-numeric-operator +)
|
||||||
|
(make-numeric-operator -)
|
||||||
|
(make-numeric-operator *)
|
||||||
|
(make-numeric-operator /)
|
||||||
|
|
||||||
;; -----------------------------------------------------------------------------
|
;; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
(define-for-syntax (reduce/op op e* #:src stx)
|
(define-for-syntax (reduce/op op e*)
|
||||||
(let loop ([prev #f]
|
(let loop ([prev #f]
|
||||||
[acc '()]
|
[acc '()]
|
||||||
[e* e*])
|
[e* e*])
|
||||||
(if (null? e*)
|
(if (null? e*)
|
||||||
;; then: combine `prev` and `acc` into a list or single number
|
;; then: finished, return a number (prev) or list of expressions (acc)
|
||||||
(cond
|
(if (null? acc)
|
||||||
[(null? acc)
|
prev
|
||||||
(quasisyntax/loc stx #,prev)]
|
(reverse (if prev (cons prev acc) acc)))
|
||||||
[else
|
|
||||||
(let ([acc+ (reverse (if prev (cons prev acc) acc))])
|
|
||||||
(quasisyntax/loc stx (#,op #,@acc+)))])
|
|
||||||
;; else: pop the next argument from e*, fold if it's a constant
|
;; else: pop the next argument from e*, fold if it's a constant
|
||||||
(syntax-parse (car e*)
|
(let ([v (quoted-stx-value? (car e*))])
|
||||||
[n:number
|
(if (number? v)
|
||||||
(if prev
|
;; then: reduce the number
|
||||||
;; eval?
|
(if prev
|
||||||
(loop (op prev (car e*)) acc (cdr e*))
|
;; Watch for division-by-zero
|
||||||
(loop (car e*) acc (cdr e*)))]
|
(if (and (zero? v) (eq? / op))
|
||||||
[e
|
(loop v (cons prev acc) (cdr e*))
|
||||||
(loop #f (cons (car e*) (if prev (cons prev acc) acc)) (cdr e*))]))))
|
(loop (op prev v) acc (cdr e*)))
|
||||||
|
(loop v acc (cdr e*)))
|
||||||
|
;; else: save value in acc
|
||||||
|
(let ([acc+ (cons (car e*) (if prev (cons prev acc) acc))])
|
||||||
|
(loop #f acc+ (cdr e*))))) )))
|
||||||
|
|
33
test/math-fail.rkt
Normal file
33
test/math-fail.rkt
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#lang racket/base
|
||||||
|
|
||||||
|
(define (expr->typed-module expr)
|
||||||
|
#`(module t typed/racket/base
|
||||||
|
(require trivial/math)
|
||||||
|
#,expr))
|
||||||
|
|
||||||
|
(define TEST-CASE* (map expr->typed-module '(
|
||||||
|
(ann (let ([n 2]) (+: n -2)) Zero)
|
||||||
|
(ann (let ([n 2]) (-: 2 n)) Zero)
|
||||||
|
(ann (let ([n 5]) (*: n 1/5 1)) One)
|
||||||
|
(ann (let ([n 4]) (/: n n)) One)
|
||||||
|
;; -- lambda => back to racket/base
|
||||||
|
(ann ((lambda ([f : (-> Natural Natural Natural)]) (f 0 0)) +:) Zero)
|
||||||
|
(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)
|
||||||
|
)))
|
||||||
|
|
||||||
|
(module+ test
|
||||||
|
(require
|
||||||
|
rackunit)
|
||||||
|
|
||||||
|
(define (format-eval stx)
|
||||||
|
(lambda () ;; For `check-exn`
|
||||||
|
(compile-syntax stx)))
|
||||||
|
|
||||||
|
(for ([rkt (in-list TEST-CASE*)])
|
||||||
|
(check-exn #rx"format::|Type Checker"
|
||||||
|
(format-eval rkt)))
|
||||||
|
)
|
85
test/math-pass.rkt
Normal file
85
test/math-pass.rkt
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
#lang typed/racket/base
|
||||||
|
|
||||||
|
(module+ test
|
||||||
|
(require
|
||||||
|
trivial/math
|
||||||
|
typed/rackunit
|
||||||
|
)
|
||||||
|
|
||||||
|
;; -- +:
|
||||||
|
(check-equal? (ann (+: 0 0) Zero) 0)
|
||||||
|
(check-equal? (ann (+: 1 0) One) 1)
|
||||||
|
(check-equal? (ann (+: 0 1) One) 1)
|
||||||
|
(check-equal? (ann (+: 3 2) 5) 5)
|
||||||
|
(check-equal? (ann (+: 3 1 1) Natural) 5)
|
||||||
|
|
||||||
|
(check-equal?
|
||||||
|
(ann ((lambda ([f : (-> Integer Integer Integer)]) (f 0 0)) +:) Integer)
|
||||||
|
0)
|
||||||
|
|
||||||
|
|
||||||
|
;; -- -:
|
||||||
|
(check-equal? (ann (-: 0 0) Zero) 0)
|
||||||
|
(check-equal? (ann (-: 1 1) Zero) 0)
|
||||||
|
(check-equal? (ann (-: 2 2) Zero) 0)
|
||||||
|
(check-equal? (ann (-: 99 97 2) Zero) 0)
|
||||||
|
(check-equal? (ann (-: 8 1 3 16) -12) -12)
|
||||||
|
|
||||||
|
(check-equal?
|
||||||
|
(ann ((lambda ([f : (-> Integer Integer Integer)]) (f 0 0)) -:) Integer)
|
||||||
|
0)
|
||||||
|
|
||||||
|
|
||||||
|
;; -- *:
|
||||||
|
(check-equal? (ann (*: 0 1315) Zero) 0)
|
||||||
|
(check-equal? (ann (*: 11 0) Zero) 0)
|
||||||
|
(check-equal? (ann (*: 3 1 3) 9) 9)
|
||||||
|
(check-equal? (ann (*: -1 8 4) Negative-Integer) -32)
|
||||||
|
(check-equal? (ann (*: 5 1/5 1) One) 1)
|
||||||
|
|
||||||
|
(check-equal?
|
||||||
|
(ann ((lambda ([f : (-> Integer Integer Integer)]) (f 0 0)) *:) Integer)
|
||||||
|
0)
|
||||||
|
|
||||||
|
|
||||||
|
;; -- /:
|
||||||
|
(check-equal? (ann (/: 0 1) Zero) 0)
|
||||||
|
(check-equal? (ann (/: 0 42) Zero) 0)
|
||||||
|
(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)
|
||||||
|
|
||||||
|
|
||||||
|
;; -- Nested
|
||||||
|
(check-equal?
|
||||||
|
(ann (+: (+: 1 1) (+: 1 1 1) 1) Index)
|
||||||
|
6)
|
||||||
|
(check-equal?
|
||||||
|
(ann (*: (+: 9 1) (-: 6 3 2 1) 1) Zero)
|
||||||
|
0)
|
||||||
|
(check-equal?
|
||||||
|
(ann (/: (+: 1 2 3 4) (+: (-: 3 2) (+: 1))) Natural)
|
||||||
|
5)
|
||||||
|
|
||||||
|
|
||||||
|
;; -- Operator works, but we can't fold constants
|
||||||
|
(let ([n 0])
|
||||||
|
(check-equal? (ann (+: n 1 2 3 4) Natural) 10)
|
||||||
|
(check-equal? (ann (-: n n) Integer) 0)
|
||||||
|
(check-equal? (ann (*: n 8 1 4 13 1) Natural) 0)
|
||||||
|
(check-equal? (ann (/: n 1) Exact-Rational) 0))
|
||||||
|
|
||||||
|
(check-equal? (ann (let ([n 2]) (+: n -2)) Integer) 0)
|
||||||
|
(check-equal? (ann (let ([n 5]) (*: n 1/5 1)) Exact-Rational) 1)
|
||||||
|
(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
|
||||||
|
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user