public
Description: BDD for Common Lisp
Homepage:
Clone URL: git://github.com/osteele/cl-spec.git
osteele (author)
Tue Jan 15 07:31:35 -0800 2008
commit  86ec419166158682ab6de9dbe6e4d8458435ddca
tree    98705085f67e3a78d86bbd57d4313892bb1eb478
parent  90633abd1672f695d57edb3e1e06830213ac1a7f
cl-spec / utilities.lisp
100644 73 lines (64 sloc) 3.067 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
;;; Copyright 2008 by Oliver Steele. Released under the MIT License.
 
;; this doesn't really test that every element has type TYPE, but
;; I don't think there's a way to do that in CL
(deftype list-type (&optional (type t))
  "(LIST-TYPE {type}) is a LIST whose elements are type {type}."
  `(and list (or null (cons ,type))))
 
(defmethod trim ((s string))
  "Return S without initial or final whitespace characters.
A whitespace character is a space, or a non-graphic character.
If S is all whitespace, the result is the empty string.
Non-consing if the result is STRING= to S."
  (flet ((whitespace-char-p (char)
           (or (char= #\space char) (not (graphic-char-p char)))))
    (let ((start (position-if-not #'whitespace-char-p s))
          (end (position-if-not #'whitespace-char-p s :from-end t)))
      (cond ((not start)
             "")
            ((and (= 0 start) (= end (1- (length s))))
             s)
            (t
             (subseq s start (1+ end)))))))
 
(defsetf rref setref
    "A setter for dictionaries.")
 
;; TODO: default slot-names by introspection
(defmethod object->dictionary ((object t) reader-names
                               &key (basename (type-of object)))
  "Create a DICTIONARY whose keys are the names in READER-NAMES, and
whose values are the applications to OBJECT of the functions named by
those objects, prefixed by BASENAME (which defaults the type of OBJECT)."
  (plist->dictionary
   (loop for reader-name in reader-names
      collect reader-name
      collect (funcall (concatenate-symbol basename "-" reader-name)
                       object))))
 
(defun map-lines (fn input-stream)
  "Apply FN to each line from INPUT-STREAM."
  (do ((line (read-line input-stream) (read-line input-stream nil 'eof)))
      ((eq line 'eof))
    (funcall fn line)))
 
(defmacro with-elapsed-time (&body body)
  "Same as PROGN, but returns the elapsed time in seconds as a second value."
  (let ((t0 (gensym "t0")))
    `(let* ((,t0 (get-internal-real-time))
           (value (progn ,@body))
           (elapsed-time
            (coerce
             (/ (- (get-internal-real-time) ,t0) internal-time-units-per-second)
                'float)))
       (values value elapsed-time))))
 
(defmacro define-accumulating-method ((function-name (self type))
                                      accumulation-construct
                                      &key child-reader)
  "Defines a method on FUNCTION-NAME, specialized on a first argument of TYPE,
that uses ACCUMALATION-CONSTRUCT (a LOOP-friendly accumulation keyword) to
accumulate the values of the projections of its children.
 
Children are accessed via CHILD-READER, which defaults to {TYPE}-CHILDREN,
and projected via FUNCTION-NAME."
  (let ((value-reader function-name)
        (child-reader (or child-reader
                          (concatenate-symbol type "-CHILDREN")))
        (child (gensym "child")))
    `(defmethod ,function-name ((,self ,type))
       (loop for ,child in (,child-reader ,self)
            ,accumulation-construct (,value-reader ,child)))))