git.fiddlerwoaroof.com
Raw Blame History
;;; utils.scm -- utility functions
;;;
;;; author :  Sandra Loosemore
;;; date   :  18 Nov 1991
;;;
;;; This file contains miscellaneous functions that are generally useful.
;;; If you find some missing feature from the base language, this is
;;; a good place to put it.  Common Lisp-style sequence functions are 
;;; an example of the sort of thing found here.


;;;=====================================================================
;;; Sequence functions
;;;=====================================================================

(define (vector-replace to-vec from-vec to start end)
  (declare (type fixnum to start end)
	   (type vector to-vec from-vec))
  (if (and (eq? to-vec from-vec)
	   (> to start))
      ;; Right shift in place
      (do ((from  (1- end) (1- from))
	   (to    (1- (+ to (- end start)))))
	  ((< from start) to-vec)
	  (declare (type fixnum from to))
	  (setf (vector-ref to-vec to) (vector-ref from-vec from))
	  (decf to))
      ;; Normal case, left-to-right
      (do ((from  start (1+ from)))
	  ((= from end) to-vec)
	  (declare (type fixnum from))
	  (setf (vector-ref to-vec to) (vector-ref from-vec from))
	  (incf to))))

(define (string-replace to-vec from-vec to start end)
  (declare (type fixnum to start end)
	   (type string to-vec from-vec))
  (if (and (eq? to-vec from-vec)
	   (> to start))
      ;; Right shift in place
      (do ((from  (1- end) (1- from))
	   (to    (1- (+ to (- end start)))))
	  ((< from start) to-vec)
	  (declare (type fixnum from to))
	  (setf (string-ref to-vec to) (string-ref from-vec from))
	  (decf to))
      ;; Normal case, left-to-right
      (do ((from  start (1+ from)))
	  ((= from end) to-vec)
	  (declare (type fixnum from))
	  (setf (string-ref to-vec to) (string-ref from-vec from))
	  (incf to))))

(define (string-fill string c start end)
  (declare (type fixnum start end)
	   (type string string)
	   (type char c))
  (do ((i start (1+ i)))
      ((= i end) string)
      (declare (type fixnum i))
      (setf (string-ref string i) c)))

(define (string-position c string start end)
  (declare (type fixnum start end)
	   (type string string)
	   (type char c))
  (cond ((= start end) '#f)
	((char=? (string-ref string start) c) start)
	(else
	 (string-position c string (1+ start) end))))

(define (string-position-not-from-end c string start end)
  (declare (type fixnum start end)
	   (type string string)
	   (type char c))
  (cond ((= start end) '#f)
	((not (char=? (string-ref string (setf end (1- end))) c))
	 end)
	(else
	 (string-position-not-from-end c string start end))))

(define (string-nreverse string start end)
  (declare (type fixnum start end)
	   (type string string))
  (do ((i start (1+ i))
       (j (1- end) (1- j)))
      ((not (< i j)) string)
      (declare (type fixnum i j))
    (let ((c (string-ref string i)))
      (setf (string-ref string i) (string-ref string j))
      (setf (string-ref string j) c))))


(define (string-starts? s1 s2)  ; true is s1 begins s2
  (and (>= (string-length s2) (string-length s1))
       (string=? s1 (substring s2 0 (string-length s1)))))


;;;=====================================================================
;;; Table utilities
;;;=====================================================================


(define (table->list table)
  (let ((l '()))
       (table-for-each
	(lambda (key val) (push (cons key val) l)) table)
       l))

(define (list->table l)
  (let ((table (make-table)))
     (dolist (p l)
	(setf (table-entry table (car p)) (cdr p)))
     table))



;;;=====================================================================
;;; Tuple utilities
;;;=====================================================================

;;; For future compatibility with a typed language, define 2 tuples with
;;; a few functions:  (maybe add 3 tuples someday!)

(define-integrable (tuple x y)
  (cons x y))

(define-integrable (tuple-2-1 x) (car x))  ; Flic-like notation
(define-integrable (tuple-2-2 x) (cdr x))

(define (map-tuple-2-1 f l)
  (map (lambda (x) (tuple (funcall f (tuple-2-1 x)) (tuple-2-2 x))) l))

(define (map-tuple-2-2 f l)
  (map (lambda (x) (tuple (tuple-2-1 x) (funcall f (tuple-2-2 x)))) l))


;;;=====================================================================
;;; List utilities
;;;=====================================================================

;;; This does an assq using the second half of the tuple as the key.

(define (rassq x l)
  (if (null? l)
      '#f
      (if (eq? x (tuple-2-2 (car l)))
	  (car l)
	  (rassq x (cdr l)))))

;;; This is an assoc with an explicit test

(define (assoc/test test-fn x l)
  (if (null? l)
      '#f
      (if (funcall test-fn x (tuple-2-1 (car l)))
	  (car l)
	  (assoc/test test-fn x (cdr l)))))




;;; Stupid position function works only on lists, uses eqv?

(define (position item list)
  (position-aux item list 0))

(define (position-aux item list index)
  (declare (type fixnum index))
  (cond ((null? list)
	 '#f)
	((eqv? item (car list))
	 index)
	(else
	 (position-aux item (cdr list) (1+ index)))
	))


;;; Destructive delete-if function

(define (list-delete-if f l)
  (list-delete-if-aux f l l '#f))

(define (list-delete-if-aux f head next last)
  (cond ((null? next)
	 ;; No more elements.
	 head)
	((not (funcall f (car next)))
	 ;; Leave this element and do the next.
	 (list-delete-if-aux f head (cdr next) next))
	(last
	 ;; Delete element from middle of list.
	 (setf (cdr last) (cdr next))
	 (list-delete-if-aux f head (cdr next) last))
	(else
	 ;; Delete element from head of list.
	 (list-delete-if-aux f (cdr next) (cdr next) last))))


;;; Same as the haskell function

(define (concat lists)
  (if (null? lists)
      '()
      (append (car lists) (concat (cdr lists)))))


;;; This is a quick & dirty list sort function.

(define (sort-list l compare-fn)
  (if (or (null? l) (null? (cdr l)))
      l
      (insert-sorted compare-fn (car l) (sort-list (cdr l) compare-fn))))

(define (insert-sorted compare-fn e l)
  (if (null? l)
      (list e)
      (if (funcall compare-fn e (car l))
	  (cons e l)
	  (cons (car l) (insert-sorted compare-fn e (cdr l))))))

(define (find-duplicates l)
  (cond ((null? l)
	 '())
	((memq (car l) (cdr l))
	 (cons (car l)
	       (find-duplicates (cdr l))))
	(else (find-duplicates (cdr l)))))

;;;  A simple & slow topsort routine.
;;;  Input:  A list of lists.  Each list is a object consed onto the
;;;          list of objects it preceeds.
;;;  Output: Two values: SORTED / CYCLIC & a list of either sorted objects
;;;                      or a set of components containing the cycle.

(define (topsort l)
  (let ((changed? '#t)
	(sorted '())
	(next '()))
    (do () ((not changed?) 
	    (if (null? next)
		(values 'sorted (nreverse sorted))
		(values 'cyclic (map (function car) next))))
      (setf changed? '#f)
      (setf next '())
      (dolist (x l)
        (cond ((topsort-aux (cdr x) sorted)
	       (push (car x) sorted)
	       (setf changed? '#t))
	      (else
	       (push x next))))
      (setf l next))))


;;; Returns true if x doesn't contain any elements that aren't in sorted.
;;; equivalent to (null? (set-intersection x sorted)), but doesn't cons
;;; and doesn't traverse the whole list in the failure case.

(define (topsort-aux x sorted)
  (cond ((null? x)
	 '#t)
	((memq (car x) sorted)
	 (topsort-aux (cdr x) sorted))
	(else
	 '#f)))

(define (set-intersection s1 s2)
  (if (null? s1)
      '()
      (let ((rest (set-intersection (cdr s1) s2)))
	(if (memq (car s1) s2)
	    (cons (car s1) rest)
	    rest))))

;;; remove s2 elements from s1

(define (set-difference s1 s2)
  (if (null? s1)
      '()
      (let ((rest (set-difference (cdr s1) s2)))
	(if (memq (car s1) s2)
	    rest
	    (cons (car s1) rest)))))


(define (set-union s1 s2)
  (if (null? s2)
      s1
      (if (memq (car s2) s1)
	  (set-union s1 (cdr s2))
	  (cons (car s2) (set-union s1 (cdr s2))))))


;;; Destructive list splitter

(define (split-list list n)
  (declare (type fixnum n))
  (let ((tail1  (list-tail list (1- n))))
    (if (null? tail1)
	(values list '())
	(let ((tail2  (cdr tail1)))
	  (setf (cdr tail1) '())
	  (values list tail2)))))


;;; Some string utils

(define (mem-string s l)
  (and (not (null? l)) (or (string=? s (car l))
			   (mem-string s (cdr l)))))

(define (ass-string k l)
  (cond ((null? l)
	 '#f)
	((string=? k (caar l))
	 (car l))
	(else
	 (ass-string k (cdr l)))))


;;;=====================================================================
;;; Syntax extensions
;;;=====================================================================

;;; The mlet macro combines let* and multiple-value-bind into a single
;;; syntax.

(define-syntax (mlet binders . body)
  (mlet-body binders body))

(define (mlet-body binders body)
  (if (null? binders)
      `(begin ,@body)
      (let* ((b (car binders))
	     (var (car b))
	     (init (cadr b))
	     (inner-body (mlet-body (cdr binders) body)))
	(if (pair? var)
	    (multiple-value-bind (new-vars ignore-decl)
				 (remove-underlines var)
	       `(multiple-value-bind ,new-vars
				     ,init ,@ignore-decl ,inner-body))
	    `(let ((,var ,init)) ,inner-body)))))

(define (remove-underlines vars)
  (if (null? vars)
      (values '() '())
      (multiple-value-bind (rest ignore-decl) (remove-underlines (cdr vars))
	(if (not (eq? (car vars) '_))
	    (values (cons (car vars) rest) ignore-decl)
	    (let ((var (gensym)))
	      (values (cons var rest)
		      `((declare (ignore ,var)) ,@ignore-decl)))))))




;;;=====================================================================
;;; Other utilities
;;;=====================================================================

(define (add-extension name ext)
  (assemble-filename (filename-place name) (filename-name name) ext))

(define (time-execution thunk)
  (let* ((start-time (get-run-time))
	 (res (funcall thunk))
	 (end-time (get-run-time)))
    (values res (- end-time start-time))))

(define (pprint-flatten code . maybe-port)
  (pprint-flatten-aux
    code
    (if (null? maybe-port) (current-output-port) (car maybe-port))))

(define (pprint-flatten-aux code port)
  (if (and (pair? code)
	   (eq? (car code) 'begin))
      (dolist (c (cdr code))
	(pprint-flatten-aux c port))
      (pprint*-aux code port)))

(define (print-flatten code port)
  (if (and (pair? code)
	   (eq? (car code) 'begin))
      (dolist (c (cdr code))
	(print-flatten c port))
      (begin
	(internal-write code port)
	(internal-newline port))))


;;; Like pprint, but print newline after instead of before.

(define (pprint* object . maybe-port)
  (pprint*-aux
    object
    (if (null? maybe-port) (current-output-port) (car maybe-port))))

(define (pprint*-aux object port)
  (dynamic-let ((*print-pretty*  '#t))
    (prin1 object port))
  (terpri port))

;;; This reads stuff from a string.  (Better error checks needed!)

(define (read-lisp-object str)
  (call-with-input-string str (lambda (port) (read port))))