original commit: 6b84101c1f7996bd678cb93f4cc250d948bbe8a5
This commit is contained in:
Robby Findler 2002-10-10 15:21:42 +00:00
parent 02422a012b
commit 62b7af443d
2 changed files with 293 additions and 100 deletions

View File

@ -19,6 +19,7 @@
(require (lib "class.ss")) (require (lib "class.ss"))
(require (lib "contract-helpers.scm" "mzlib" "private")) (require (lib "contract-helpers.scm" "mzlib" "private"))
(require-for-syntax (prefix a: (lib "contract-helpers.scm" "mzlib" "private")))
;; (define/contract id contract expr) ;; (define/contract id contract expr)
;; defines `id' with `contract'; initially binding ;; defines `id' with `contract'; initially binding
@ -93,99 +94,249 @@
[(_ name contract-expr expr) [(_ name contract-expr expr)
(raise-syntax-error 'define/contract "expected identifier in first position" (raise-syntax-error 'define/contract "expected identifier in first position"
define-stx define-stx
(syntax name))])) (syntax name))]))
(require-for-syntax (prefix a: (lib "contract-helpers.scm" "mzlib" "private"))) ;; (provide/contract p/c-ele ...)
;; p/c-ele = (id expr) | (struct (id expr) ...)
;; (provide/contract (id expr) ...)
;; provides each `id' with the contract `expr'. ;; provides each `id' with the contract `expr'.
(define-syntax (provide/contract provide-stx) (define-syntax (provide/contract provide-stx)
(syntax-case provide-stx () (syntax-case provide-stx (struct)
[(_) (raise-syntax-error 'provide/contract "must provide at least one id")] [(_ p/c-ele ...)
[(_ (id ctrct) ...) (let ()
(andmap identifier? (syntax->list (syntax (id ...))))
(with-syntax ([(id-rename ...) ;; code-for-each-clause : (listof syntax) -> (listof syntax)
(map (lambda (x) ;; constructs code for each clause of a provide/contract
(datum->syntax-object (define (code-for-each-clause clauses)
provide-stx (cond
(string->symbol [(null? clauses) null]
(format "provide/contract-id-~a-ACK-DONT_USE_ME" [else
(syntax-object->datum x))))) (let ([clause (car clauses)])
(syntax->list (syntax (id ...))))] (syntax-case clause (struct)
[(contract-id ...) [(struct struct-name ((field-name contract) ...))
(map (lambda (x) (and (identifier? (syntax struct-name))
(datum->syntax-object (andmap identifier? (syntax->list (syntax (field-name ...)))))
provide-stx (build-struct-code (syntax struct-name)
(string->symbol (syntax->list (syntax (field-name ...)))
(format "provide/contract-contract-id-~a-ACK-DONT_USE_ME" (syntax->list (syntax (contract ...))))]
(syntax-object->datum x))))) [(struct name)
(syntax->list (syntax (id ...))))] (identifier? (syntax name))
[pos-module-source (datum->syntax-object (raise-syntax-error 'provide/contract
provide-stx "missing fields"
(string->symbol provide-stx
(format clause)]
"provide/contract-pos-module-source-~a-ACK-DONT_USE_ME" [(struct name (fields ...))
(car (syntax->list (syntax (id ...)))))))] (for-each (lambda (field)
[pos-stx (datum->syntax-object provide-stx 'here)] (syntax-case field ()
[module-source-as-symbol (datum->syntax-object provide-stx 'module-source-as-symbol)]) [(x y)
(syntax (identifier? (syntax x))
(begin (void)]
(provide (rename id-rename id) ...) [(x y)
(require (lib "contract-helpers.scm" "mzlib" "private")) (raise-syntax-error 'provide/contract
"malformed struct field, expected identifier"
(define pos-module-source (module-source-as-symbol #'pos-stx)) provide-stx
;; this is here to check for unbound ids. (syntax x))]
(if #f (begin (void) id ...)) [else
(raise-syntax-error 'provide/contract
(define contract-id ctrct) ... "malformed struct field"
(define-syntax id-rename provide-stx
(make-set!-transformer field)]))
(lambda (stx) (syntax->list (syntax (fields ...))))
(with-syntax ([neg-stx (datum->syntax-object stx 'here)])
(syntax-case stx (set!) ;; if we didn't find a bad field something is wrong!
[(set! _ body) (raise-syntax-error (raise-syntax-error 'provide/contract "internal error" provide-stx clause)]
#f [(struct name . fields)
"cannot set! provide/contract identifier" (raise-syntax-error 'provide/contract
stx "malformed struct fields"
(syntax _))] provide-stx
[(_ arg (... ...)) clause)]
(syntax [(name contract)
((-contract contract-id (identifier? (syntax name))
id (cons (code-for-one-id (syntax name) (syntax contract))
pos-module-source (code-for-each-clause (cdr clauses)))]
(module-source-as-symbol #'neg-stx) [(name contract)
(quote-syntax _)) (raise-syntax-error 'provide/contract
arg "expected identifier"
(... ...)))] provide-stx
[_ (syntax name))]
(identifier? (syntax _)) [unk
(syntax (raise-syntax-error 'provide/contract
(-contract contract-id "malformed clause"
id provide-stx
pos-module-source (syntax unk))]))]))
(module-source-as-symbol #'neg-stx)
(quote-syntax _)))]))))) ;; build-struct-code : syntax (listof syntax) (listof syntax) -> syntax
...)))] ;; constructs the code for a struct clause
[(_ clauses ...) (define (build-struct-code struct-name field-names field-contracts)
(for-each (let* ([field-contract-ids (map (lambda (field-name)
(lambda (clause) (mangle-id field-name "provide/contract-field-contract"))
(syntax-case clause () field-names)]
[(x y) [selector-ids (map (lambda (field-contract-id)
(identifier? (syntax x)) (build-selector-id struct-name field-contract-id))
(void)] field-contract-ids)]
[(x y) [mutator-ids (map (lambda (field-contract-id)
(raise-syntax-error (build-mutator-id struct-name field-contract-id))
'provide/contract field-contract-ids)]
"malformed clause (expected an identifier as first item in clause)" [predicate-id (build-predicate-id struct-name)])
provide-stx (with-syntax ([(selector-codes ...)
(syntax x))] (map (lambda (field-name field-contract-id)
[_ (raise-syntax-error (code-for-one-id (build-selector-id struct-name
'provide/contract field-contract-id)
"malformed clause (expected two items in each clause)" (build-selector-contract struct-name
provide-stx predicate-id
clause)])) field-contract-id)))
(syntax->list (syntax (clauses ...))))])) field-names
field-contract-ids)]
[(mutator-codes ...)
(map (lambda (mutator-id field-contract-id)
(code-for-one-id mutator-id
(build-mutator-contract struct-name
predicate-id
field-contract-id)))
mutator-ids
field-contract-ids)]
[predicate-code (code-for-one-id predicate-id (syntax (-> any? boolean?)))]
[constructor-code (code-for-one-id
(build-constructor-id struct-name)
(build-constructor-contract field-contract-ids
predicate-id))]
[(field-contracts ...) field-contracts]
[(field-contract-ids ...) field-contract-ids])
(syntax
(begin
(define field-contract-ids field-contracts) ...
selector-codes ...
mutator-codes ...
predicate-code
constructor-code)))))
;; build-constructor-contract : (listof syntax) syntax -> syntax
(define (build-constructor-contract field-contract-ids predicate-id)
(with-syntax ([(field-contract-ids ...) field-contract-ids]
[predicate-id predicate-id])
(syntax (-> field-contract-ids ... predicate-id))))
;; build-selector-contract : syntax syntax -> syntax
;; constructs the contract for a selector
(define (build-selector-contract struct-name predicate-id field-contract-id)
(with-syntax ([field-contract-id field-contract-id]
[predicate-id predicate-id])
(syntax (-> predicate-id field-contract-id))))
;; build-mutator-contract : syntax syntax -> syntax
;; constructs the contract for a selector
(define (build-mutator-contract struct-name predicate-id field-contract-id)
(with-syntax ([field-contract-id field-contract-id]
[predicate-id predicate-id])
(syntax (-> predicate-id contract-id void?))))
;; build-constructor-id : syntax -> syntax
;; constructs the name of the selector for a particular field of a struct
(define (build-constructor-id struct-name)
(datum->syntax-object
struct-name
(string->symbol
(string-append
"make-"
(symbol->string (syntax-object->datum struct-name))))))
;; build-predicate-id : syntax -> syntax
;; constructs the name of the selector for a particular field of a struct
(define (build-predicate-id struct-name)
(datum->syntax-object
struct-name
(string->symbol
(string-append
(symbol->string (syntax-object->datum struct-name))
"?"))))
;; build-selector-id : syntax syntax -> syntax
;; constructs the name of the selector for a particular field of a struct
(define (build-selector-id struct-name field-name)
(datum->syntax-object
struct-name
(string->symbol
(string-append
(symbol->string (syntax-object->datum struct-name))
"-"
(symbol->string (syntax-object->datum field-name))))))
;; build-mutator-id : syntax syntax -> syntax
;; constructs the name of the selector for a particular field of a struct
(define (build-mutator-id struct-name field-name)
(datum->syntax-object
struct-name
(string->symbol
(string-append
"set-"
(symbol->string (syntax-object->datum struct-name))
"-"
(symbol->string (syntax-object->datum field-name))
"!"))))
;; code-for-one-id : syntax syntax -> syntax
;; given the syntax for an identifier and a contract,
;; builds a begin expression for the entire contract and provide
(define (code-for-one-id id ctrct)
(with-syntax ([id-rename (mangle-id id "provide/contract-id")]
[contract-id (mangle-id id "provide/contract-contract-id")]
[pos-module-source (mangle-id id "provide/contract-pos-module-source")]
[pos-stx (datum->syntax-object provide-stx 'here)]
[module-source-as-symbol (datum->syntax-object provide-stx 'module-source-as-symbol)]
[id id]
[ctrct ctrct])
(syntax
(begin
(provide (rename id-rename id))
;; unbound id check
(if #f id)
(define pos-module-source (module-source-as-symbol #'pos-stx))
(define contract-id ctrct)
(define-syntax id-rename
(make-set!-transformer
(lambda (stx)
(with-syntax ([neg-stx (datum->syntax-object stx 'here)])
(syntax-case stx (set!)
[(set! _ body) (raise-syntax-error
#f
"cannot set! provide/contract identifier"
stx
(syntax _))]
[(_ arg (... ...))
(syntax
((-contract contract-id
id
pos-module-source
(module-source-as-symbol #'neg-stx)
(quote-syntax _))
arg
(... ...)))]
[_
(identifier? (syntax _))
(syntax
(-contract contract-id
id
pos-module-source
(module-source-as-symbol #'neg-stx)
(quote-syntax _)))])))))))))
;; mangle-id : syntax string -> syntax
;; constructs a mangled name of an identifier from an identifier
;; the name isn't fresh, so `id' must already be unique.
(define (mangle-id id prefix)
(datum->syntax-object
provide-stx
(string->symbol
(string-append
prefix
(format "-~a-ACK-DONT_USE_ME" (syntax-object->datum id))))))
(with-syntax ([(bodies ...) (code-for-each-clause (syntax->list (syntax (p/c-ele ...))))])
(syntax
(begin
(require (lib "contract-helpers.scm" "mzlib" "private"))
bodies ...))))]))
;; raise-contract-error : (union syntax #f) symbol symbol string args ... -> alpha ;; raise-contract-error : (union syntax #f) symbol symbol string args ... -> alpha
;; doesn't return ;; doesn't return
(define (raise-contract-error src-info to-blame other-party fmt . args) (define (raise-contract-error src-info to-blame other-party fmt . args)

View File

@ -4,13 +4,14 @@
(SECTION 'contracts) (SECTION 'contracts)
(parameterize ([error-print-width 200])
(let () (let ()
;; test/spec-passed : symbol sexp -> void ;; test/spec-passed : symbol sexp -> void
;; tests a passing specification ;; tests a passing specification
(define (test/spec-passed name expression) (define (test/spec-passed name expression)
(test 'passed (test (void)
eval (let ([for-each-eval (lambda (l) (for-each eval l))]) for-each-eval)
`(let () ,expression 'passed))) (list expression '(void))))
;; test/spec-failed : symbol sexp string -> void ;; test/spec-failed : symbol sexp string -> void
;; tests a failing specification with blame assigned to `blame' ;; tests a failing specification with blame assigned to `blame'
@ -21,12 +22,11 @@
(and m (cadr m))))) (and m (cadr m)))))
(test blame (test blame
failed-contract failed-contract
(eval (with-handlers ([(lambda (x) (and (not-break-exn? x) (exn? x)))
`(with-handlers ([(lambda (x) (and (not-break-exn? x) (exn? x))) exn-message])
exn-message]) (eval expression)
,expression 'failed/expected-exn-got-normal-termination)))
'failed/expected-exn-got-normal-termination))))
(test/spec-passed (test/spec-passed
'contract-flat1 'contract-flat1
'(contract not #f 'pos 'neg)) '(contract not #f 'pos 'neg))
@ -457,6 +457,48 @@
'(let () '(let ()
(define/contract i (-> integer? integer?) (lambda (x) (i #t))) (define/contract i (-> integer? integer?) (lambda (x) (i #t)))
(i 1)) (i 1))
"<<unknown>>")) "<<unknown>>")
(test/spec-passed
'provide/contract1
'(let ()
(eval '(module contract-test-suite1 mzscheme
(require (lib "contracts.ss"))
(provide/contract (x integer?))
(define x 1)))
(eval '(require contract-test-suite1))
(eval 'x)))
(test/spec-passed
'provide/contract2
'(let ()
(eval '(module contract-test-suite2 mzscheme
(require (lib "contracts.ss"))
(provide/contract)))
(eval '(require contract-test-suite2))))
(test/spec-failed
'provide/contract3
'(let ()
(eval '(module contract-test-suite3 mzscheme
(require (lib "contracts.ss"))
(provide/contract (x integer?))
(define x #f)))
(eval '(require contract-test-suite3))
(eval 'x))
"contract-test-suite3")
(test/spec-passed
'provide/contract4
'(let ()
(eval '(module contract-test-suite4 mzscheme
(require (lib "contracts.ss"))
(provide/contract (struct s ((a any?))))
(define-struct s (a))))
(eval '(require contract-test-suite4))
(eval '(list make-s s-a s? set-s-a!))))
))
(report-errs) (report-errs)