; Copyright (c) 1993, 1994 Richard Kelsey and Jonathan Rees.  See file COPYING.

; schemify


; Flush nodes and generated names in favor of something a little more
; readable.  Eventually, (schemify node) ought to produce an
; s-expression that has the same semantics as node, when node is fully
; expanded.

(define (schemify node . env-option)
  (schemify1 node (if (null? env-option) #f (car env-option))))

(define (schemify1 node env)
  (if (node? node)
      (or (node-ref node 'schemify)
	  (let ((form ((operator-table-ref schemifiers (node-operator-id node))
		       node env)))
	    (node-set! node 'schemify form)
	    form))
      (schemify-sexp node env)))

(define schemifiers
  (make-operator-table (lambda (node env)
			 (let ((form (node-form node)))
			   (if (list? form)
			       (map (lambda (f) (schemify1 f env)) form)
			       form)))))

(define (define-schemifier name type proc)
  (operator-define! schemifiers name type proc))

(define-schemifier 'name 'leaf
  (lambda (node env)
    (name->qualified (node-form node) env)))

(define-schemifier 'quote syntax-type
  (lambda (node env)
    (let ((form (node-form node)))
      (list (schemify1 (car form) env) (cadr form)))))

; Convert an alias (generated name) to S-expression form ("qualified name").
;
; As an optimization, we elide intermediate steps in the lookup path
; when possible.  E.g.
;     #(>> #(>> #(>> define-record-type define-accessors)
;		define-accessor)
;	   record-ref)
; is replaced with
;     #(>> define-record-type record-ref)

(define (name->qualified name env)
  (if env
      (if (generated? name)
	  (if (same-denotation? (lookup env name)
				(lookup env (generated-symbol name)))
	      (generated-symbol name)	;+++
	      (make-qualified
	       (let recur ((name (generated-parent-name name)))
		 (if (generated? name)
		     (let ((parent (generated-parent-name name)))
		       (if (let ((b1 (lookup env name))
				 (b2 (lookup env parent)))
			     (or (same-denotation? b1 b2)
				 (and (binding? b1)
				      (binding? b2)
				      (let ((s1 (binding-static b1))
					    (s2 (binding-static b2)))
					(and (transform? s1)
					     (transform? s2)
					     (eq? (transform-env s1)
						  (transform-env s2)))))))
			   (recur parent) ;+++
			   `#(>> ,(recur parent)
				 ,(generated-symbol name))))
		     name))
	       (generated-symbol name)))
	  name)
      (desyntaxify name)))

; lambda, let-syntax, letrec-syntax...

(define-schemifier 'letrec syntax-type
  (lambda (node env)
    (let ((form (node-form node)))
      `(letrec ,(map (lambda (spec)
		       `(,(car spec) ,(schemify1 (cadr spec) env)))
		     (cadr form))
	 ,@(map (lambda (f) (schemify1 f env))
		(cddr form))))))

(define (schemify-sexp thing env)
  (cond ((name? thing)
	 (name->qualified thing env))
	((pair? thing)
	 (let ((x (schemify-sexp (car thing) env))
	       (y (schemify-sexp (cdr thing) env)))
	   (if (and (eq? x (car thing))
		    (eq? y (cdr thing)))
	       thing			;+++
	       (cons x y))))
	((vector? thing)
	 (let ((new (make-vector (vector-length thing) #f)))
	   (let loop ((i 0) (same? #t))
	     (if (>= i (vector-length thing))
		 (if same? thing new)	;+++
		 (let ((x (schemify-sexp (vector-ref thing i) env)))
		   (vector-set! new i x)
		   (loop (+ i 1)
			 (and same? (eq? x (vector-ref thing i)))))))))
	(else thing)))


; Qualified names

(define (make-qualified transform-name sym)
  (vector '>> transform-name sym))

(define (qualified? thing)
  (and (vector? thing)
       (= (vector-length thing) 3)
       (eq? (vector-ref thing 0) '>>)))

(define (qualified-parent-name q) (vector-ref q 1))
(define (qualified-symbol q) (vector-ref q 2))
