;; typedproc.ss ;; ;; @author Morgan McGuire, morgan@cs.brown.edu ;; ;; @created 2002-01-15 ;; @edited 2002-01-15 ;; ;; Includes basic scheme and adds proc ;; (module typedproc (file "basescheme.ss") (require (lib "string.ss") (lib "list.ss") (lib "etc.ss")) (require-for-syntax (lib "list.ss") (file "basescheme.ss")) (provide (all-from (file "basescheme.ss")) symbol-append proc define-proc not-equal? mem? list-copy assert type-check) ;; ;; Generalized memq; uses an arbitrary equality proc. ;; (define (mem? x lst =?) (and (not (empty? lst)) (or (=? (first lst) x) (mem? x (rest lst) =?)))) ;; Shallow copy (define (list-copy lst) (apply list lst)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Throws an exn:user? exception if the ;; argument is not true. (define-macro (assert expr) `(if (not ,(first expr)) (error "Assertion failure: " ,(format "~s" (first expr))) void)) ;; (type-check test? object) (define-macro (type-check expr) `(if (not (,(first expr) ,(second expr))) (error (format "Type check failed: Argument ~v has value ~v, which does not satisfy ~v" ,(format "~s" (second expr)) ,(second expr) ,(format "~s" (first expr)))) void)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; symbol-append: symbol x symbol -> symbol (define (symbol-append a b) (string->symbol (string-append (symbol->string a) (symbol->string b)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (not-equal? a b) (not (equal? a b))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; (proc (arg1 : type1 arg2 : type2 ...) body1 body2 ...):return-type ;; ;; Procedure with run-time type checking. ;; ;; You won't get type checking on assignment (e.g. set!), only on the ;; initial argument values and the return value. The "type" can be ;; any function of one argument; e.g. integer?, list?, ;; (lambda (x) (and (integer? x) (odd? x) (> x 4))) (define-macro (proc expr) (let* ( ;; Extract and reparse the argument list so that colons are handled [arg-list (reparse-for-colons (retokenize-for-colons (first expr)))] ;; Extract the procedure body and type, if specified. maybe-body ;; contains the type declaration, if there is one. [maybe-body (cdr expr)] ;; See if the first symbol begins with a ":" [proc-type-specified (and (not (eq? maybe-body '())) (symbol? (first maybe-body)) (eq? (string-ref (symbol->string (first maybe-body)) 0) #\:))] ;; There's a space after the colon if the first symbol is exactly ': [proc-type-has-space (and proc-type-specified (eq? (first maybe-body) ':))] ;; Extract the type if it exists [proc-type (if proc-type-specified (if proc-type-has-space ;; pull out the second thing (cadr maybe-body) ;; parse the name out of the first (symbol-remove-first (first maybe-body))) void)] ;; The identifier used for the return value of the procedure [temp-id (gensym)] [body (if proc-type-specified ;; There is an extra set of parens around let ;; because the code below expects a list of expressions ;; in body, not a single expression. ;; Parsing the return type is tricky because we ;; don't know if it is one symbol (:x) or two (: x) `((let ([,temp-id (begin ,@(if proc-type-has-space (cddr maybe-body) (rest maybe-body)))]) (type-check ,proc-type ,temp-id) ,temp-id)) maybe-body)] ;; Create a regular scheme proc list from the arg list by ;; stripping the types off [simple-arg-list (map (lambda (arg-decl) (if (list? arg-decl) (second arg-decl) arg-decl)) arg-list)] ;; Make type checks from the arg-list by turning each (: v t) into ;; (type-check t? v) [type-checks (map (lambda (binding) (let ([var (second binding)] [type (third binding)]) `(type-check ,type ,var))) (filter list? arg-list))]) `(lambda ,simple-arg-list ,@type-checks ,@body) )) ;(define-syntax (define-proc stx) ; (syntax-case stx () ; [(_ (proc-name arg1 arg2 ...) body1 body2 ...) ; (syntax/loc ; stx ; (define proc-name (proc (arg1 arg2 ...) body1 body2 ...)))])) (define-macro (define-proc expr) (let ([name (first (first expr))] [args (rest (first expr))] [body (rest expr)]) `(define ,name (proc ,args ,@body)))) )