git.fiddlerwoaroof.com
Raw Blame History
;;; runtime-utils.scm -- basic runtime support
;;;
;;; author :  Sandra Loosemore
;;; date   :  9 Jun 1992
;;;
;;; This file contains definitions (beyond the normal mumble stuff)
;;; that is referenced directly in code built by the code generator.
;;; See backend/codegen.scm.
;;;



;;; (delay form)
;;;   returns a delay object with unevaluated "form".

(define-syntax (delay form)
  `(cons '#f (lambda () ,form)))


;;; (box form)
;;;   returns a delay object with evaluated "form".

(define-syntax (box form)
  (cond ((number? form)
	 `(quote ,(cons '#t form)))
	((and (pair? form) (eq? (car form) 'quote))
	 `(quote ,(cons '#t (cadr form))))
	(else
	 `(cons '#t ,form))))

(define-syntax (unbox form)
  `(cdr ,form))

(define-syntax (forced? form)
  `(car ,form))


;;; (force delay)
;;;   return the value of the delay object.

(define (force delay-object)
  (declare (type pair delay-object))
  (if (car delay-object)
      (cdr delay-object)
      (begin
        (let ((result  (funcall (cdr delay-object))))
	  (setf (car delay-object) '#t)
	  (setf (cdr delay-object) result)))))

;;; Inline version of the above.  Not good to use everywhere because
;;; of code bloat problems, but handy for helper functions.

(define-syntax (force-inline delay-object)
  (let ((temp1  (gensym))
	(temp2  (gensym)))
    `(let ((,temp1  ,delay-object))
       (declare (type pair ,temp1))
       (if (car ,temp1)
	   (cdr ,temp1)
	   (let ((,temp2  (funcall (cdr ,temp1))))
	     (setf (car ,temp1) '#t)
	     (setf (cdr ,temp1) ,temp2))))))


;;; (make-curried-fn opt-fn strictness)
;;; The basic idea is to compare the number of arguments received against
;;; the number expected.
;;; If the same, call the optimized entry point opt-fn.
;;; If more, apply the result of calling the optimized entry to the
;;;   leftover arguments.
;;; If less, make a closure that accepts the additional arguments.

(define (make-curried-fn opt-fn strictness)
  (lambda args
    (curried-fn-body '() args opt-fn strictness)))

(define (curried-fn-body previous-args args opt-fn strictness)
  (multiple-value-bind
      (saturated? actual-args leftover-args leftover-strictness)
      (process-curried-fn-args strictness args '())
    (setf actual-args (append previous-args actual-args))
    (if saturated?
	(if (null? leftover-args)
	    (apply opt-fn actual-args)
	    (apply (apply opt-fn actual-args) leftover-args))
	(lambda more-args
	  (curried-fn-body actual-args more-args opt-fn leftover-strictness)))
    ))

(define (process-curried-fn-args strictness args actual-args)
  (cond ((null? strictness)
	 ;; At least as many arguments as expected.
	 (values '#t (nreverse actual-args) args strictness))
	((null? args)
	 ;; Not enough arguments supplied.
  	 (values '#f (nreverse actual-args) args strictness))
	(else
	 ;; Process the next argument.
	 (if (car strictness)
	     (push (force-inline (car args)) actual-args)
	     (push (car args) actual-args))
 	 (process-curried-fn-args (cdr strictness) (cdr args) actual-args))
	))


;;; Special cases of the above.

(define (make-curried-fn-1-strict opt-fn)
  (lambda (arg1 . moreargs)
    (setf arg1 (force-inline arg1))
    (if (null? moreargs)
	(funcall opt-fn arg1)
	(apply (funcall opt-fn arg1) moreargs))))

(define (make-curried-fn-1-nonstrict opt-fn)
  (lambda (arg1 . moreargs)
    (if (null? moreargs)
	(funcall opt-fn arg1)
	(apply (funcall opt-fn arg1) moreargs))))


;;; Here's a similar helper function used for making data constructors.

(define (constructor-body previous-args args arity fn)
  (declare (type fixnum arity))
  (let ((n  (length args)))
    (declare (type fixnum n))
    (setf args (append previous-args args))
    (cond ((eqv? n arity)
	   (apply fn args))
	  ((< n arity)
	   (lambda more-args
	     (constructor-body args more-args (- arity n) fn)))
	  (else
	   (error "Too many arguments supplied to constructor.")))))


;;; Special case for cons constructor

(define (make-cons-constructor . args)
  (constructor-body '() args 2 (function cons)))


;;; (make-tuple-constructor arity)
;;;   return a function that makes an untagged data structure with "arity" 
;;;   slots.  "arity" is a constant.

(define-integrable *max-predefined-tuple-arity* 10)

(define (make-tuple-constructor-aux arity)
  (cond ((eqv? arity 0)
	 ;; Actually, should never happen -- this is the unit constructor
	 0)
	((eqv? arity 1)
	 (lambda args
	   (constructor-body '() args 2 (lambda (x) x))))
	((eqv? arity 2)
	 (lambda args
	   (constructor-body '() args 2 (function cons))))
	(else
	 (lambda args
	   (constructor-body '() args arity (function vector))))))

(define *predefined-tuple-constructors*
  (let ((result  '()))
    (dotimes (i *max-predefined-tuple-arity*)
      (push (make-tuple-constructor-aux i) result))
    (list->vector (nreverse result))))

(define-syntax (make-tuple-constructor arity)
  (declare (type fixnum arity))
  (if (< arity *max-predefined-tuple-arity*)
      `(vector-ref *predefined-tuple-constructors* ,arity)
      `(make-tuple-constructor-aux ,arity)))


;;; (make-tuple . args)
;;;   uncurried version of the above

(define-syntax (make-tuple . args)
  (let ((arity  (length args)))
    (cond ((eqv? arity 0)
	   ;; Actually, should never happen -- this is the unit constructor
	   0)
	  ((eqv? arity 1)
	   (car args))
	  ((eqv? arity 2)
	   `(cons ,@args))
	  (else
	   `(vector ,@args)))))


;;; (make-tagged-data-constructor n arity)
;;;   return a function that makes a data structure with tag "n" and
;;;   "arity" slots.

(define-integrable *max-predefined-tagged-data-tag* 10)
(define-integrable *max-predefined-tagged-data-arity* 10)

(define (make-tagged-data-constructor-aux n arity)
  (if (eqv? arity 0)
      (vector n)
      (lambda args
	(constructor-body (list n) args arity (function vector)))))

(define *predefined-tagged-data-constructors*
  (let ((result  '()))
    (dotimes (i *max-predefined-tagged-data-arity*)
      (let ((inner-result  '()))
	(dotimes (j *max-predefined-tagged-data-tag*)
	  (push (make-tagged-data-constructor-aux j i) inner-result))
	(push (list->vector (nreverse inner-result)) result)))
    (list->vector (nreverse result))))

(define-syntax (make-tagged-data-constructor n arity)
  (declare (type fixnum arity n))
  (if (and (< arity *max-predefined-tagged-data-arity*)
	   (< n *max-predefined-tagged-data-tag*))
      `(vector-ref (vector-ref *predefined-tagged-data-constructors* ,arity)
		   ,n)
      `(make-tagged-data-constructor-aux ,n ,arity)))


;;; (make-tagged-data n . args)
;;;   uncurried version of the above

(define-syntax (make-tagged-data n . args)
  `(vector ,n ,@args))


;;; (tuple-select arity i object)
;;;   extract component "i" from untagged "object"

(define-syntax (tuple-select arity i object)
  (cond ((eqv? arity 1)
	 object)
	((eqv? arity 2)
	 (if (eqv? i 0)
	     `(car ,object)
	     `(cdr ,object)))
	(else
	 `(vector-ref (the vector ,object) (the fixnum ,i)))))


;;; (tagged-data-select arity i object)
;;;   extract component "i" from tagged "object"

(define-syntax (tagged-data-select arity i object)
  (declare (ignore arity))
  `(vector-ref (the vector ,object) (the fixnum ,(1+ i))))


;;; (constructor-number object)
;;;   return the tag from "object"

(define-syntax (constructor-number object)
  `(vector-ref (the vector ,object) 0))

(define-syntax (funcall-force fn . args)
  (let* ((n    (length args))
	 (junk (assv n '((1 . funcall-force-1)
			 (2 . funcall-force-2)
			 (3 . funcall-force-3)
			 (4 . funcall-force-4)))))
    `(,(if junk (cdr junk) 'funcall-force-n) ,fn ,@args)))

(define (funcall-force-1 fn a1)
  (funcall (force-inline fn) a1))
(define (funcall-force-2 fn a1 a2)
  (funcall (force-inline fn) a1 a2))
(define (funcall-force-3 fn a1 a2 a3)
  (funcall (force-inline fn) a1 a2 a3))
(define (funcall-force-4 fn a1 a2 a3 a4)
  (funcall (force-inline fn) a1 a2 a3 a4))
(define-syntax (funcall-force-n fn . args)
  `(funcall (force ,fn) ,@args))


;;; (make-haskell-string string)
;;;   Converts a Lisp string lazily to a boxed haskell string (makes
;;;   a delay with a magic function).  Returns an unboxed result.

(define (make-haskell-string string)
  (declare (type string string))
  (let ((index   1)
	(size    (string-length string)))
    (declare (type fixnum index size))
    (cond ((eqv? size 0)
	   '())
	  ((eqv? size 1)
	   (cons (box (char->integer (string-ref string 0)))
		 (box '())))
	  (else
	   (letrec ((next-fn
		      (lambda ()
			(let ((ch  (char->integer (string-ref string index))))
			  (incf index)
			  (cons (box ch)
				(if (eqv? index size)
				    (box '())
				    (cons '#f next-fn)))))))
	     (cons (box (char->integer (string-ref string 0)))
		   (cons '#f next-fn))))
	  )))


;;; Similar, but accepts an arbitrary tail (which must be a delay object)

(define (make-haskell-string-tail string tail-delay)
  (declare (type string string))
  (let ((index   1)
	(size    (string-length string)))
    (declare (type fixnum index size))
    (cond ((eqv? size 0)
	   (force-inline tail-delay))
	  ((eqv? size 1)
	   (cons (box (char->integer (string-ref string 0)))
		 tail-delay))
	  (else
	   (letrec ((next-fn
		      (lambda ()
			(let ((ch  (char->integer (string-ref string index))))
			  (incf index)
			  (cons (box ch)
				(if (eqv? index size)
				    tail-delay
				    (cons '#f next-fn)))))))
	     (cons (box (char->integer (string-ref string 0)))
		   (cons '#f next-fn))))
	  )))


(define (haskell-string->string s)
  (let ((length  0))
    (declare (type fixnum length))
    (do ((s s (force (cdr s))))
	((null? s))
	(setf length (+ length 1)))
    (let ((result  (make-string length)))
      (declare (type string result))
      (do ((s s (unbox (cdr s)))
	   (i 0 (+ i 1)))
	  ((null? s))
	  (declare (type fixnum i))
	  (setf (string-ref result i) (integer->char (force (car s)))))
      result)))


(define (print-haskell-string s port)
   (do ((s1 s (force (cdr s1))))
       ((null? s1))
     (write-char (integer->char (force (car s1))) port)))

;;; This explicates the value returned by a proc (the IO () type).

(define (insert-unit-value x)
  (declare (ignore x))
  0)

;;; These handle list conversions

(define (haskell-list->list fn l)
  (if (null? l)
      '()
      (cons (funcall fn (force (car l))) 
	    (haskell-list->list fn (force (cdr l))))))

(define (list->haskell-list fn l)
  (if (null? l)
      '()
      (cons (box (funcall fn (car l)))
	    (box (list->haskell-list fn (cdr l))))))

(define (haskell-list->list/identity l)
  (if (null? l)
      '()
      (cons (force (car l))
	    (haskell-list->list/identity (force (cdr l))))))

(define (list->haskell-list/identity l)
  (if (null? l)
      '()
      (cons (box (car l))
	    (box (list->haskell-list/identity (cdr l))))))