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