;; SRFI 9 for PLT Scheme 200
;; Mike Sperber, 12/14/2001

(module record mzscheme
	
  (provide define-record-type)
	
  (define-syntax define-record-type
	  
    (let ()
      (define (filter-map proc l)
	(if (null? l)
	    '()
	    (let ((result (proc (car l))))
	      (if result
		  (cons result (filter-map proc (cdr l)))
		  (filter-map proc (cdr l))))))
	    
	    
      (define (syntax-member? thing stuff)
	(cond
	 ((null? stuff) #f)
	 ((free-identifier=? thing (car stuff)) #t)
	 (else (syntax-member? thing (cdr stuff)))))
	    
      (lambda (x)
	(syntax-case x ()
	  ((_ type
	      (constructor constructor-tag ...)
	      predicate
	      (field-tag accessor more ...) ...)
			    
			    
	   (with-syntax
	       ((number-of-fields (length (syntax->list
					   (syntax (field-tag ...)))))
		((modifier ...)
		 (filter-map (lambda (descriptor)
			       (syntax-case descriptor ()
				 ((field-tag accessor) #f)
				 ((field-tag accessor modifier)
				  (syntax modifier))))
			     (syntax->list
			      (syntax ((field-tag accessor more ...) ...)))))
		((constructor-arg ...)
		 (map (lambda (field-tag)
			(if (syntax-member? field-tag
					    (syntax->list
					     (syntax (constructor-tag ...))))
			    field-tag
			    (syntax (void))))
		      (syntax->list
		       (syntax (field-tag ...)))))
		(generic-access (syntax generic-access))
		(generic-mutate (syntax generic-mutate)))
	     (with-syntax
		 (((accessor-proc ...)
		   (let loop ((i 0)
			      (fields (syntax->list (syntax (field-tag ...)))))
		     (if (null? fields)
			 '()
			 (cons (with-syntax
				   ((i i))
				 (syntax
				  (lambda (s)
				    (generic-access s i)))) 
			       (loop (+ 1 i)
				     (cdr fields))))))
		  ((modifier-proc ...)
		   (let loop ((i 0)
			      (descriptors
			       (syntax->list
				(syntax ((field-tag accessor more ...) ...)))))
		     (if (null? descriptors)
			 '()
			 (syntax-case (car descriptors) ()
			   ((field-tag accessor)
			    (loop (+ 1 i)
				  (cdr descriptors)))
			   ((field-tag accessor modifier)
			    (cons (with-syntax
				      ((i i))
				    (syntax
				     (lambda (s v)
				       (generic-mutate s i v))))
				  (loop (+ 1 i)
					(cdr descriptors)))))))))
	       (syntax
		(define-values (constructor
				predicate
				accessor ...
				modifier ...)
		  (let-values (((type-descriptor
				 full-constructor
				 predicate
				 generic-access
				 generic-mutate)
				(make-struct-type 'type #f number-of-fields 0)))
					     
		    (values (lambda (constructor-tag ...)
			      (full-constructor constructor-arg ...))
			    predicate
			    accessor-proc ...
			    modifier-proc ...))))))))))))
