[Small-cl-src] with-static-types

Peter Wilson pabw00 at gmail.com
Thu Sep 23 01:53:46 UTC 2004


;;;; WITH-STATIC-TYPES macro
;;; A shorthand macro for specifying inline static types (the type x) without
;;; cluttering the code. As usual for static typing, avoid using it until
;;; fairly late in the optimization phase of your coding.
;;;
;;; This code has not been used anywhere besides the examples below and may
;;; still have undiscovered bugs.
;;;
;;; Be aware of name collisions between functions and variables. The macro
;;; currently only supports one namespace.
;;;
;;; TODO: Handle special forms in the body. Or at least LET.
;;; TODO: When handling LET, insert declarations for variables.
;;; TODO: Handle macros in the body. Probably just macroexpand them.
;;; TODO: Remove compile-time dependency on ITERATE package.
;;; 


(defpackage #:with-static-types 
  (:use #:cl)
  (:export #:with-static-types))

(eval-when (:compile-toplevel :load-toplevel :execute)
  (in-package #:with-static-types)
  (require "iterate")
  (use-package '#:iterate))


(defun make-ring (x)
  "Create a circular list containing x."
  (let ((c (cons x nil)))
    (setf (cdr c) c)))


(defun lookup-type (typed-functions sym)
  "Deduce the type of sym using the typed-functions alist.
   Returns: t  value-or-return-type  argument-types"
  (flet ((assert-length=2 (x)
           (unless (eq (cddr x) nil)
             (error "WITH-STATIC-TYPES: Not expecting more than one
argument after ~S"
                    (car x)))))
    (let ((found (assoc sym typed-functions)))
      (if found
        (destructuring-bind (found return-type . args) found
          (let ((arg-types 
                  (if args 
                    (case (car args)
                      ((:argument-types :args)
                       (assert-length=2 args) (cadr args))
                      ((:all-arguments-type :arg)
                       (assert-length=2 args) (make-ring (cadr args)))
                      (t (if (> (list-length args) 1)
                           args
                           (make-ring (car args)))))
                    (make-ring return-type))))
            (return-from lookup-type (values found return-type arg-types))))
        nil))))

(defun apply-arg-types (typed-functions arg-types form)
"Construct (the)'s for the arguments in form using the typespecs in arg-types."
  (flet ((recurse (form) (list 'with-static-types typed-functions form)))
    (list* (car form)
           (iter (for (arg . a-rest) on (cdr form))
                 (for (type . t-rest) on arg-types)
                 (collect (list 'the type (recurse arg)))
                 (when (and (null t-rest) (not (null a-rest)))
                   (warn "WITH-STATIC-TYPES: More arguments than types
for~& form: ~S~& types: ~S"
                         form arg-types)
                   (dolist (a a-rest) (collect (recurse a))))))))

(defun with-types-helper (typed-functions body)
  (flet ((recurse (form) (list 'with-static-types typed-functions form)))
    (iter (for form in body)
          (if (consp form)
            (multiple-value-bind (found return-type arg-types)
                                 (lookup-type typed-functions (car form))
              (if found
                (collect (list 'the return-type
                               (apply-arg-types typed-functions
arg-types form)))
                ;TODO handle specials and macros here
                (collect (list* (car form) (mapcar #'recurse (cdr form))))))

            (multiple-value-bind (found return-type)
                                 (lookup-type typed-functions form)
              (if found
                (collect (list 'the return-type form))
                (collect form)))))))

(defmacro with-static-types (function-typespecs &body body)
"Insert type declarations around calls to specific functions.
Also happens to work with variables.
  
Usage: WITH-STATIC-TYPES ( spec ... ) body-form ...
 spec => ( sym result-and-arg-type )
       | ( sym result-type all-arg-type )
       | ( sym result-type arg-type-1 arg-type-2 ... )
       | ( sym result-type :ALL-ARGUMENTS-TYPE all-arg-type )
       | ( sym result-type :ARG all-arg-type )
       | ( sym result-type :ARGUMENT-TYPES ( arg-type-1 arg-type-2 ... ) )
       | ( sym result-type :ARGS ( arg-type-1 arg-type-2 ... ) )
Examples:
    (WITH-STATIC-TYPES ((+ FIXNUM)) (+ 1 2))
    --> (PROGN (THE FIXNUM (+ (THE FIXNUM 1) (THE FIXNUM 2))))
    ==> 3
    
    (WITH-STATIC-TYPES ((+ FIXNUM)
                 (/ RATIONAL FIXNUM))
                (/ 3 (+ 2 2)))
    --> (PROGN
          (THE RATIONAL
               (/ (THE FIXNUM 3) 
                  (THE FIXNUM 
                       (THE FIXNUM 
                            (+ (THE FIXNUM 1) 
                               (THE FIXNUM 2)))))))
    ==> 3/4
    
    (WITH-STATIC-TYPES ((+ FIXNUM)
                 (/ RATIONAL FIXNUM FIXNUM))
                (/ 3 (+ 2 2) 17))
    WARNING: WITH-STATIC-TYPES: More arguments than types for
     form: (/ 3 (+ 2 2) 17)
     types: (FIXNUM FIXNUM)
    --> (PROGN (THE RATIONAL
                    (/ (THE FIXNUM 3) 
                       (THE FIXNUM
                            (THE FIXNUM
                                 (+ (THE FIXNUM 1) 
                                    (THE FIXNUM 2))))
                       17)))
    ==> 3/68

    (WITH-STATIC-TYPES ((x FIXNUM)) x)
    --> (PROGN (THE FIXNUM x))
    ==> Undefined variable x
"
  (if body (list* 'progn (with-types-helper function-typespecs body))
    (warn "WITH-STATIC-TYPES: Null body. types: ~S" function-typespecs)))




More information about the Small-cl-src mailing list