git.fiddlerwoaroof.com
runtime/runtime-utils.scm
4e987026
 ;;; 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))))))