git.fiddlerwoaroof.com
type/type-macros.scm
4e987026
 
 ;;; This file also contains some random globals for the type checker:
 
 (define-walker type ast-td-type-walker)
 
 ;;; Some pre-defined types
 (define *bool-type* '())
 (define *char-type* '())
 (define *string-type* '())
 (define *int-type* '())
 (define *integer-type* '())
 (define *rational-type* '())
 
 ;;; These two globals are used throughout the typechecker to avoid
 ;;; passing lots of stuff in each function call.
 
 (define *placeholders* '())
 (define *non-generic-tyvars* '())
 (define *enclosing-decls* '())
 
 ;;; Used by the defaulting mechanism
 
 (define *default-decls* '())
 
 ;;; Used in error handling & recovery
 
 (define *type-error-handlers* '())
 (define *type-error-recovery* '())
 
 
 ;;; This associates a type checker function with an ast type.  The variable
 ;;; `object' is bound to the value being types.
 
 (define-syntax (define-type-checker ast-type . cont)
   `(define-walker-method type ,ast-type (object)
      ,@cont))
 
 ;;; This recursively type checks a structure slot in the current object.
 ;;; This updates the ast in the slot (since type checking rewrites the ast)
 ;;; and binds the computed type to a variable.  The slot must contain an
 ;;; expression.
 
 (define-syntax (type-check struct slot var . cont)
   `(mlet ((($$$ast$$$ ,var)
 	   (dispatch-type-check (struct-slot ',struct ',slot object))))
 	 (setf (struct-slot ',struct ',slot object) $$$ast$$$)
 	 ,@cont))
 
 ;;; This is used to scope decls.
 
 (define-syntax (with-new-tyvars . cont)
   `(dynamic-let ((*non-generic-tyvars* (dynamic *non-generic-tyvars*)))
      ,@cont))
 
 
 ;;; Similar to type-check, the slot must contain a list of decls.
 ;;; This must be done before any reference to a variable defined in the
 ;;; decls is typechecked.
 		
 (define-syntax (type-check/decls struct slot . cont)
   `(with-new-tyvars
     (let (($$$decls$$$
 	  (type-decls (struct-slot ',struct ',slot object))))
      (setf (struct-slot ',struct ',slot object) $$$decls$$$)
      ,@cont)))
 
 ;;; The type checker returns an expression / type pair.  This
 ;;; abstracts the returned value.
 
 (define-syntax (return-type object type)
   `(values ,object ,type))
 
 ;;; When an ast slot contains a list of expressions, there are two
 ;;; possibilities: the expressions all share the same type or each has
 ;;; an independant type.  In the first case, a single type (computed
 ;;; by unifying all types in the list) is bound to a variable.
 
 (define-syntax (type-check/unify-list struct slot var error-handler . cont)
   `(mlet ((($$$ast$$$ $$$types$$$)
 	   (do-type-check/list (struct-slot ',struct ',slot object))))
     (setf (struct-slot ',struct ',slot object) $$$ast$$$)
     (with-type-error-handler ,error-handler ($$$types$$$)
        (unify-list/single-type $$$types$$$)
        (let ((,var (car $$$types$$$)))
 	 ,@cont))))
 
 ;;; When a list of expressions does not share a common type, the result is
 ;;; a list of types.
 
 (define-syntax (type-check/list struct slot var . cont)
   `(mlet ((($$$ast$$$ ,var)
 	   (do-type-check/list (struct-slot ',struct ',slot object))))
     (setf (struct-slot ',struct ',slot object) $$$ast$$$)
     ,@cont))
 
 ;;; This creates a fresh tyvar and binds it to a variable.
 
 (define-syntax (fresh-type var . cont)
   `(let ((,var (**ntyvar)))
      ,@cont))
 
 ;;; This drives the unification routine.  Two types are unified and the
 ;;; context is updated.  Currently no error handling is implemented to
 ;;; deal with unification errors.
 
 (define-syntax (type-unify type1 type2 error-handler)
   `(with-type-error-handler ,error-handler ()
      (unify ,type1 ,type2)))
 
 ;;; This generates a fresh set of monomorphic type variables.
 
 (define-syntax (fresh-monomorphic-types n vars . cont)
   `(with-new-tyvars
      (let ((,vars '()))
        (dotimes (i ,n)
 	   (let ((tv (**ntyvar)))
 	     (push tv ,vars)
 	     (push tv (dynamic *non-generic-tyvars*))))
        ,@cont)))
 
 ;;; This creates a single monomorphic type variable.
 
 (define-syntax (fresh-monomorphic-type var . cont)
   `(let* ((,var (**ntyvar)))
      (with-new-tyvars
        (push ,var (dynamic *non-generic-tyvars*))
        ,@cont)))
 
 ;;; This is used to rewrite the current ast as a new ast and then
 ;;; recursively type check the new ast.  The original ast is saved for
 ;;; error message printouts.
 
 (define-syntax (type-rewrite ast)
   `(mlet (((res-ast type) (dispatch-type-check ,ast))
 	  (res (**save-old-exp object res-ast)))
       (return-type res type)))
 
 ;;; These are the type error handlers
 
 (define-syntax (recover-type-error error-handler . body)
  (let ((temp (gensym))
        (err-fn (gensym)))
   `(let/cc ,temp
     (let ((,err-fn ,error-handler))
      (dynamic-let ((*type-error-recovery*
 		    (cons (lambda ()
 			    (funcall ,err-fn ,temp))
 			  (dynamic *type-error-recovery*))))
         ,@body)))))
 
 (define-syntax (with-type-error-handler handler extra-args . body)
   (if (eq? handler '#f)
       `(begin ,@body)
       `(dynamic-let ((*type-error-handlers*
 		      (cons (lambda ()
 			     (,(car handler) ,@extra-args ,@(cdr handler)))
 			    (dynamic *type-error-handlers*))))
 	    ,@body)))