; Copyright (c) 1993-1999 by Richard Kelsey and Jonathan Rees. See file COPYING.

; This is the main entry point to the compiler.  It returns a template
; that will execute the forms (each of which is a node).
;
; This is written in a somewhat odd fashion to make sure that the forms are
; not retained once they have been compiled.

;(define (compile-forms forms name)
;  (if (null? forms)
;      (segment->template (sequentially
;                           (instruction (enum op protocol) 0)
;                           (deliver-value (instruction (enum op unspecific))
;                                          (return-cont #f)))
;                         name
;                         #f             ;pc-in-segment
;                         #f)            ;debug data
;      (really-compile-forms forms
;                            (instruction (enum op protocol) 0)
;                            name)))
;      
;(define (really-compile-forms forms segment name)
;  (if (null? (cdr forms))
;      (segment->template (sequentially segment
;                                       (compile-form (car forms)
;                                                     (return-cont #f)))
;                         name
;                         #f             ;pc-in-segment
;                         #f)            ;debug data
;      (really-compile-forms (cdr forms)
;                            (sequentially segment
;                                          (compile-form (car forms)
;                                                        an-ignore-values-cont))
;                            name)))

(define (compile-forms forms name)
  (if (null? forms)
      (segment->template (sequentially
                           (instruction (enum op protocol) 0)
                           (deliver-value (instruction (enum op unspecific))
                                          (return-cont #f)))
                         name
                         #f             ;pc-in-segment
                         #f)            ;debug data
      (compile-forms-loop (reverse forms) name #f)))

(define (compile-forms-loop forms name next)
  (if (null? forms)
      next
      (compile-forms-loop (cdr forms)
			  name
			  (compile-form (car forms) name next))))

; Compile a single top-level form, returning a template.  NEXT is either #F or
; a template; if it is a template we jump to it after FORM.
  
(define (compile-form form name next)
  (segment->template (sequentially
		      (instruction (enum op protocol) 0)
		      (let ((node (force-node form))
			    (cont (if next
				      an-ignore-values-cont
				      (return-cont #f))))
			(if (define-node? node)
			    (compile-definition node cont)
			    (compile-expression node 0 cont)))
		      (if next
			  (instruction-with-literal (enum op call-template)
						    next
						    0)
			  empty-segment))
		     name
		     #f		;pc-in-segment
		     #f))	;debug data

(define define-node? (node-predicate 'define syntax-type))

; Definitions must be treated differently from assignments: we must
; use SET-CONTENTS! instead of SET-GLOBAL! because the SET-GLOBAL!
; instruction traps if an attempt is made to store into an undefined
; location.

(define (compile-definition node cont)
  (let* ((form (node-form node))
	 (name (cadr form)))
    (sequentially (instruction-with-location (enum op literal)
					     (node-ref name 'binding)
					     (node-form name)
					     value-type)
		  (instruction (enum op push))
		  (compile-expression (caddr form)
				      1
				      (named-cont (node-form name)))
		  (deliver-value
		   (instruction (enum op stored-object-set!)
				(enum stob location)
				location-contents-offset)
		   cont))))

(define location-contents-offset
  (cond ((assq 'location stob-data)
	 => (lambda (stuff)
	      (let loop ((slots (cdddr stuff)) (i 0))
		(if (eq? (caar slots) 'contents)
		    i
		    (loop (cdr slots) (+ i 1))))))
	(else
	 (error "can't find location data in STOB-DATA"))))

;----------------
; Make a startup procedure from a list of initialization templates.  This
; is only used by the static linker.  RESUMER should be a template that
; returns a procedure that takes 5 arguments (the number the VM passes to
; the startup procedure).

(define (make-startup-procedure inits resumer)
  (let ((nargs 5))
    (append-templates inits
		      nargs
		      (sequentially
		        (maybe-push-continuation
			  (instruction-with-literal (enum op call-template)
						    resumer
						    0)
			  nargs
			  (fall-through-cont #f #f))
			(instruction (enum op call) nargs)))))

; Return a template that accepts NARGS arguments, invokes TEMPLATES in turn,
; and then calls template FINAL on the arguments.

(define (append-templates templates nargs final)
  (segment->template
    (sequentially
      (instruction (enum op protocol) nargs)
      (reduce (lambda (template seg)
		(sequentially
		  (maybe-push-continuation
		    (instruction-with-literal (enum op call-template)
					      template
					      0)
		    nargs
		    an-ignore-values-cont)
		  seg))
	      final
	      templates))
    #f		; no name
    #f		; pc-in-segment = #f
    #f))	; no debug data

(define an-ignore-values-cont (ignore-values-cont #f #f))