263 lines
8.5 KiB
Racket
263 lines
8.5 KiB
Racket
#lang scheme/base
|
|
|
|
(provide :
|
|
contract contract/arbitrary
|
|
define-contract
|
|
define/contract define-values/contract
|
|
-> mixed one-of predicate combined property)
|
|
|
|
(require deinprogramm/contract/contract
|
|
scheme/promise
|
|
(for-syntax scheme/base)
|
|
(for-syntax syntax/stx)
|
|
(for-syntax stepper/private/shared)
|
|
(only-in lang/private/teachprims beginner-equal?))
|
|
|
|
(define-for-syntax (phase-lift stx)
|
|
(with-syntax ((?stx stx))
|
|
(with-syntax ((?stx1 (syntax/loc stx #'?stx))) ; attach the occurrence position to the syntax object
|
|
#'?stx1)))
|
|
|
|
(define-for-syntax (parse-contract name stx)
|
|
(syntax-case* stx
|
|
(mixed one-of predicate list -> combined property reference at contract)
|
|
module-or-top-identifier=?
|
|
((mixed ?contract ...)
|
|
(with-syntax ((?stx (phase-lift stx))
|
|
(?name name)
|
|
((?contract-expr ...) (map (lambda (ctr)
|
|
(parse-contract #f ctr))
|
|
(syntax->list #'(?contract ...)))))
|
|
#'(make-mixed-contract '?name
|
|
(list ?contract-expr ...)
|
|
?stx)))
|
|
((one-of ?exp ...)
|
|
(with-syntax ((((?temp ?exp) ...)
|
|
(map list
|
|
(generate-temporaries #'(?exp ...)) (syntax->list #'(?exp ...))))
|
|
(?stx (phase-lift stx))
|
|
(?name name))
|
|
(with-syntax (((?check ...)
|
|
(map (lambda (lis)
|
|
(with-syntax (((?temp ?exp) lis))
|
|
(with-syntax ((?raise
|
|
(syntax/loc
|
|
#'?exp
|
|
(error 'contracts "hier kein Vertrag zulässig, nur normaler Wert"))))
|
|
#'(when (contract? ?temp)
|
|
?raise))))
|
|
(syntax->list #'((?temp ?exp) ...)))))
|
|
#'(let ((?temp ?exp) ...)
|
|
?check ...
|
|
(make-case-contract '?name (list ?temp ...) beginner-equal? ?stx)))))
|
|
((predicate ?exp)
|
|
(with-syntax ((?stx (phase-lift stx))
|
|
(?name name))
|
|
#'(make-predicate-contract '?name (delay ?exp) ?stx)))
|
|
((list ?contract)
|
|
(with-syntax ((?stx (phase-lift stx))
|
|
(?name name)
|
|
(?contract-expr (parse-contract #f #'?contract)))
|
|
#'(make-list-contract '?name ?contract-expr ?stx)))
|
|
((list ?contract1 ?rest ...)
|
|
(raise-syntax-error #f
|
|
"list-Vertrag darf nur einen Operanden haben."
|
|
(syntax ?contract1)))
|
|
((?arg-contract ... -> ?return-contract)
|
|
(with-syntax ((?stx (phase-lift stx))
|
|
(?name name)
|
|
((?arg-contract-exprs ...) (map (lambda (ctr)
|
|
(parse-contract #f ctr))
|
|
(syntax->list #'(?arg-contract ...))))
|
|
(?return-contract-expr (parse-contract #f #'?return-contract)))
|
|
#'(make-procedure-contract '?name (list ?arg-contract-exprs ...) ?return-contract-expr ?stx)))
|
|
((?arg-contract ... -> ?return-contract1 ?return-contract2 . ?_)
|
|
(raise-syntax-error #f
|
|
"Nach dem -> darf nur ein Vertrag stehen."
|
|
(syntax ?return-contract2)))
|
|
((at ?loc ?ctr)
|
|
(with-syntax ((?ctr-expr (parse-contract #f #'?ctr)))
|
|
#'(contract-update-syntax ?ctr-expr #'?loc)))
|
|
(contract
|
|
(with-syntax ((?stx (phase-lift stx)))
|
|
#'(contract-update-syntax contract/contract #'?loc)))
|
|
(?id
|
|
(identifier? #'?id)
|
|
(with-syntax ((?stx (phase-lift stx))
|
|
(?name name))
|
|
(let ((name (symbol->string (syntax->datum #'?id))))
|
|
(if (char=? #\% (string-ref name 0))
|
|
#'(make-type-variable-contract '?id ?stx)
|
|
(with-syntax
|
|
((?raise
|
|
(syntax/loc #'?stx
|
|
(error 'contracts "expected a contract, found ~e" ?id))))
|
|
(with-syntax
|
|
((?ctr
|
|
#'(make-delayed-contract '?name
|
|
(delay
|
|
(begin
|
|
(when (not (contract? ?id))
|
|
?raise)
|
|
?id)))))
|
|
;; for local variables (parameters, most probably),
|
|
;; we want the value to determine the blame location
|
|
(if (eq? (identifier-binding #'?id) 'lexical)
|
|
#'?ctr
|
|
#'(contract-update-syntax ?ctr #'?stx))))))))
|
|
((combined ?contract ...)
|
|
(with-syntax ((?stx (phase-lift stx))
|
|
(?name name)
|
|
((?contract-expr ...) (map (lambda (ctr)
|
|
(parse-contract #f ctr))
|
|
(syntax->list #'(?contract ...)))))
|
|
#'(make-combined-contract '?name
|
|
(list ?contract-expr ...)
|
|
?stx)))
|
|
((property ?access ?contract)
|
|
(with-syntax ((?stx (phase-lift stx))
|
|
(?name name)
|
|
(?contract-expr (parse-contract #f #'?contract)))
|
|
#'(make-property-contract '?name
|
|
?access
|
|
?contract-expr
|
|
?stx)))
|
|
((?contract-abstr ?contract ...)
|
|
(identifier? #'?contract-abstr)
|
|
(with-syntax ((?stx (phase-lift stx))
|
|
(?name name)
|
|
((?contract-expr ...) (map (lambda (ctr)
|
|
(parse-contract #f ctr))
|
|
(syntax->list #'(?contract ...)))))
|
|
(with-syntax
|
|
((?call (syntax/loc stx (?contract-abstr ?contract-expr ...))))
|
|
#'(make-call-contract '?name
|
|
(delay ?call)
|
|
(delay ?contract-abstr) (delay (list ?contract-expr ...))
|
|
?stx))))
|
|
(else
|
|
(raise-syntax-error 'contract
|
|
"ungültiger Vertrag" stx))))
|
|
|
|
; regrettable
|
|
(define contract/contract
|
|
(make-predicate-contract 'contract
|
|
(delay contract?)
|
|
#f))
|
|
|
|
|
|
(define-syntax contract
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
((_ ?contr)
|
|
#'(contract #f ?contr))
|
|
((_ ?name ?contr)
|
|
(stepper-syntax-property
|
|
(parse-contract (syntax->datum #'?name) #'?contr)
|
|
'stepper-skip-completely #t)))))
|
|
|
|
(define-syntax contract/arbitrary
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
((_ ?arb ?contr . ?rest)
|
|
#'(let ((contr (contract ?contr . ?rest)))
|
|
(set-contract-arbitrary! contr ?arb)
|
|
contr)))))
|
|
|
|
(define-syntax define-contract
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
((_ ?name ?ctr)
|
|
(identifier? #'?name)
|
|
(stepper-syntax-property #'(define ?name (contract ?name ?ctr))
|
|
'stepper-skip-completely
|
|
#t))
|
|
((_ (?name ?param ...) ?ctr)
|
|
(and (identifier? #'?name)
|
|
(andmap identifier? (syntax->list #'(?param ...))))
|
|
(stepper-syntax-property #'(define (?name ?param ...) (contract ?name ?ctr))
|
|
'stepper-skip-completely
|
|
#t)))))
|
|
|
|
(define-syntax define/contract
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
((_ ?name ?cnt ?expr)
|
|
(with-syntax ((?enforced
|
|
(stepper-syntax-property #'(attach-name '?name (apply-contract/blame ?cnt ?expr))
|
|
'stepper-skipto/discard
|
|
;; apply-contract/blame takes care of itself
|
|
;; remember there's an implicit #%app
|
|
'(syntax-e cdr syntax-e cdr cdr car))))
|
|
|
|
#'(define ?name ?enforced))))))
|
|
|
|
(define-syntax define-values/contract
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
((_ (?id ...) ?expr)
|
|
(andmap identifier? (syntax->list #'(?id ...)))
|
|
(syntax-track-origin
|
|
#'(define-values (?id ...) ?expr)
|
|
stx
|
|
(car (syntax-e stx))))
|
|
((_ ((?id ?cnt)) ?expr)
|
|
(identifier? #'?id)
|
|
#'(define/contract ?id ?cnt ?expr)) ; works with stepper
|
|
((_ (?bind ...) ?expr)
|
|
(let ((ids+enforced
|
|
(map (lambda (bind)
|
|
(syntax-case bind ()
|
|
(?id
|
|
(identifier? #'?id)
|
|
(cons #'?id #'?id))
|
|
((?id ?cnt)
|
|
(identifier? #'?id)
|
|
(cons #'?id
|
|
#'(attach-name '?id (apply-contract/blame ?cnt ?id))))))
|
|
(syntax->list #'(?bind ...)))))
|
|
(with-syntax (((?id ...) (map car ids+enforced))
|
|
((?enforced ...) (map cdr ids+enforced)))
|
|
(stepper-syntax-property
|
|
#'(define-values (?id ...)
|
|
(call-with-values
|
|
(lambda () ?expr)
|
|
(lambda (?id ...)
|
|
(values ?enforced ...))))
|
|
'stepper-skip-completely #t)))))))
|
|
|
|
;; Matthew has promised a better way of doing this in the future.
|
|
(define (attach-name name thing)
|
|
(if (procedure? thing)
|
|
(procedure-rename thing name)
|
|
thing))
|
|
|
|
(define-syntax :
|
|
(syntax-rules ()
|
|
((: ?id ?ctr) (begin)))) ; probably never used, we're only interested in the binding for :
|
|
|
|
(define-for-syntax (within-contract-syntax-error stx name)
|
|
(raise-syntax-error #f
|
|
"darf nur in Verträgen vorkommen"
|
|
name))
|
|
|
|
;; Expression -> Expression
|
|
;; Transforms unfinished code (... and the like) to code
|
|
;; raising an appropriate error.
|
|
(define-for-syntax within-contract-syntax-transformer
|
|
(make-set!-transformer
|
|
(lambda (stx)
|
|
(syntax-case stx (set!)
|
|
[(set! form expr) (within-contract-syntax-error stx (syntax form))]
|
|
[(form . rest) (within-contract-syntax-error stx (syntax form))]
|
|
[form (within-contract-syntax-error stx stx)]))))
|
|
|
|
(define-syntax -> within-contract-syntax-transformer)
|
|
(define-syntax mixed within-contract-syntax-transformer)
|
|
(define-syntax one-of within-contract-syntax-transformer)
|
|
(define-syntax predicate within-contract-syntax-transformer)
|
|
(define-syntax combined within-contract-syntax-transformer)
|
|
(define-syntax property within-contract-syntax-transformer)
|
|
; not a good idea:
|
|
; (define-syntax list within-contract-syntax-transformer)
|