racket/collects/lang/private/contracts/contract-transformers.ss
2005-05-27 18:56:37 +00:00

203 lines
8.8 KiB
Scheme

(module contract-transformers mzscheme
(require-for-template mzscheme
"contracts-helpers.ss"
"beginner-contracts.ss"
"intermediate-contracts.ss"
"advanced-contracts.ss")
(provide (all-defined))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; COMMON STUFF- Translators
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; We really should compare with module-identifier=",
;; but importing the right identifiers here is a pain.
;; As it happens, the teaching languages disable shadowing,
;; so we can safely use symbol equality.
(define (contract-id=? a b)
(eq? (syntax-e a) (syntax-e b)))
;; a translator is a function that translates syntax for contracts
;; (like: number, (number -> string), (number -> (number -> boolean)) )
;; and converts it into the necesary function calls to enforce those contracts
;; translate-arrow-contract : syntax (syntax -> syntax) -> syntax
;; parses contracts for (domain ... -> range) type contracts
;; the first argument is the syntax object to parse
;; the second argument is translator that should be used for recursive calls
(define translate-arrow-contract
(lambda (stx translator)
(syntax-case stx (->)
[(domain ... -> range)
(with-syntax
([(parsed-domain ...) (map translator (syntax-e (syntax (domain ...))))]
[parsed-range (translator (syntax range))]
[ret stx])
(syntax (->-contract (list parsed-domain ...) parsed-range #'ret)))]
[else-stx (raise-syntax-error 'contracts "unknown contract" (syntax else-stx))])))
;; beginners cant use higher order contracts, so only allow func contracts in the top level
(define beginner-translate-contract
(case-lambda
[(stx) (beginner-translate-contract stx beginner-translate-flat-contract)]
[(stx recur)
(syntax-case stx (->)
[(domain ... -> range) (translate-arrow-contract stx recur)]
[_else-stx (beginner-translate-flat-contract stx recur)])]))
;; syntax definitions for beginner language contracts (from beginner-contracts.scm)
(define beginner-translate-flat-contract
(case-lambda
[(stx) (beginner-translate-flat-contract stx beginner-translate-flat-contract)]
[(stx recur)
(syntax-case* stx (-> add1 quote cons number any integer
exact-number inexact-number posn boolean
true false
string empty symbol list)
contract-id=?
[(cons a b) (with-syntax ([car-contract (recur (syntax a))]
[cdr-contract (recur (syntax b))]
[ret stx])
(syntax/loc stx (cons-contract car-contract cdr-contract #'ret)))]
[(list a ...) (with-syntax ([(translated ...) (map recur (syntax-e (syntax/loc stx (a ...))))]
[ret stx])
(syntax/loc stx (args-contract (list translated ...) #'ret)))]
[(add1 a) (with-syntax ([translated (recur (syntax/loc stx a))]
[ret stx])
(syntax/loc stx (add1-contract translated #'ret)))]
[empty (with-syntax ([ret stx])
(syntax/loc stx (empty-contract #'ret)))]
[(quote n) (with-syntax ([ret stx])
(syntax/loc stx (build-flat-contract (lambda (x) (eq? x 'n)) 'n #'ret)))]
[number (with-syntax ([ret stx])
(syntax/loc stx (number-contract #'ret)))]
[any (with-syntax ([ret stx])
(syntax/loc stx (any-contract #'ret)))]
[symbol (with-syntax ([ret stx])
(syntax/loc stx (symbol-contract #'ret)))]
[integer (with-syntax ([ret stx])
(syntax/loc stx (integer-contract #'ret)))]
[exact-number (with-syntax ([ret stx])
(syntax/loc stx (exact-number-contract #'ret)))]
[inexact-number (with-syntax ([ret stx])
(syntax/loc stx (inexact-number-contract #'ret)))]
[boolean (with-syntax ([ret stx])
(syntax/loc stx (boolean-contract #'ret)))]
[true (with-syntax ([ret stx])
(syntax/loc stx (true-contract #'ret)))]
[false (with-syntax ([ret stx])
(syntax/loc stx (false-contract #'ret)))]
[string(with-syntax ([ret stx])
(syntax/loc stx (string-contract #'ret)))]
[posn (with-syntax ([ret stx])
(syntax/loc stx (posn-contract #'ret)))]
[(domain ... -> range)
(raise-syntax-error 'contracts "functions in the beginner language can't take other functions as input" stx)]
[(make-struct e1 e2 ...)
(pair? (regexp-match "make-(.*)" (symbol->string (syntax-object->datum (syntax make-struct)))))
(let ([make (regexp-match "make-(.*)" (symbol->string (syntax-object->datum (syntax make-struct))))])
(let ([struct (datum->syntax-object stx (string->symbol (cadr make)))])
(if (define-struct? struct)
(with-syntax ([pred (get-predicate-from-struct struct)]
[(accessors ...) (reverse (get-accessors-from-struct struct))]
[name (syntax-object->datum struct)]
[(translated ...) (map recur (syntax-e (syntax (e1 e2 ...))))]
[ret stx]) ; we wrap with lambdas so that beginner doesnt complain about using them without a (
(syntax/loc stx (struct-contract 'name (lambda (x) (pred x)) (list (lambda (x) (accessors x)) ...) (list translated ...) #'ret)))
(raise-syntax-error 'contracts (format "unknown structure type: ~e"
(syntax-object->datum struct)) stx))))]
[-> (raise-syntax-error 'contracts "found a lone '->', check your parentheses!" stx)]
[name
(number? (syntax-object->datum (syntax name)))
(with-syntax ([num (syntax-object->datum (syntax name))]
[ret stx])
(syntax/loc stx (build-flat-contract (lambda (x) (eq? num x)) num #'ret)))]
[name
(define-data? (syntax name)) ; things from a define data
(with-syntax ([cnt (get-cnt-from-dd (syntax name))])
(syntax/loc stx cnt))]
[name
(define-struct? (syntax name)) ; generic structs
(with-syntax ([pred (get-predicate-from-struct (syntax name))]
[type-name (get-name-from-struct (syntax name))]
[ret stx])
(syntax/loc stx (build-flat-contract (lambda (x) (pred x)) 'type-name #'ret)))]
[name (raise-syntax-error 'contracts "unknown contract" (syntax name))])]))
;; stx definitions for intermediate language contracts (intermediate-contracts.scm)
(define intermediate-translate-contract
(case-lambda
[(stx) (intermediate-translate-contract stx intermediate-translate-contract)]
[(stx recur)
(syntax-case* stx (-> listof quote vectorof boxof)
contract-id=?
[(listof type) (with-syntax ([ret stx]
[trans-type (recur (syntax type))])
(syntax/loc stx (listof-contract trans-type #'ret)))]
[(vectorof type) (with-syntax ([ret stx]
[trans-type (recur (syntax type))])
(syntax/loc stx (vectorof-contract trans-type #'ret)))]
[(boxof type) (with-syntax ([ret stx]
[trans-type (recur (syntax type))])
(syntax/loc stx (boxof-contract trans-type #'ret)))]
[(quote n) (with-syntax ([ret stx])
(syntax/loc stx (build-flat-contract (lambda (x) (eq? x 'n)) 'n #'ret)))]
[(domain ... -> range) (translate-arrow-contract stx intermediate-translate-contract)]
[else-stx (beginner-translate-contract (syntax else-stx) intermediate-translate-contract)])]))
;; stx definitions for advanced language contracts (advanced-contracts.scm)
(define advanced-translate-contract
(case-lambda
[(stx) (advanced-translate-contract stx advanced-translate-contract)]
[(stx recur)
(syntax-case* stx (-> void) contract-id=?
[void (with-syntax ([ret stx])
(syntax/loc stx (void-contract #'ret)))]
[(domain ... -> range) (translate-arrow-contract stx advanced-translate-contract)]
[else-stx (intermediate-translate-contract (syntax else-stx))])]))
;; helper functions for these
;;get-predicate-from-struct : stx -> stx
;;returns the predicate for the given stx, which needs to be an object defined via define-struct/define-data
(define (get-predicate-from-struct stx)
(caddr (syntax-local-value stx)))
;;get-cnt-from-dd : stx -> stx
;;returns the syntax contract that corresponds to the current define-data object
(define (get-cnt-from-dd stx)
(if (define-data? stx)
(caddr (syntax-local-value stx))))
;;get-name-from-struct : stx -> stx
;;returns the name for the given stx, which needs to be an object defined via define-struct/define-data
(define (get-name-from-struct stx)
(car (syntax-local-value stx)))
;;get-accessors-from-struct : stx -> list of stx
;;returns the name for the given stx, which needs to be an object defined via define-struct/define-data
(define (get-accessors-from-struct stx)
(cadddr (syntax-local-value stx)))
;;define-struct? : stx -> boolean
; was this thing defined in a define-struct?
(define (define-struct? stx)
(and (identifier? stx) (pair? (syntax-local-value stx (lambda () #f)))))
;;define-data? : stx -> boolean
; was this thing defined in a define-data?
(define (define-data? stx)
(and (identifier? stx)
(let ([data (syntax-local-value stx (lambda () #f))])
(and data (pair? data) (eq? (cadr data) 'define-data)))))
)