forked from fredokun/lisp-lazy-seq
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutils.lisp
140 lines (115 loc) · 4.94 KB
/
utils.lisp
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
(in-package #:fredokun-utilities)
#|
# CommonTypes: Utilities #
|#
;; To activate the inline examples
(eval-when (:compile-toplevel :load-toplevel :execute)
(defparameter *example-enabled* t) ;; nil in production / t for self-testing
(defparameter *example-equal-predicate* #'equal)
(defparameter *example-with-echo* nil)
)
(defmacro example (expr arrow expected &key (warn-only nil))
"Show an evaluation example, useful for documentation and lightweight testing.
(example `EXPR` => `EXPECTED`) evaluates `EXPR` and compare, wrt. `EQUIV`
(EQUAL by default) to `EXPECTED` and raise an error if inequal.
Set `WARN-ONLY` to T for warning instead of error.
"
(if (not *example-enabled*)
(progn
(when *example-with-echo*
(format t "------------------~%")
(format t "Example:~%~A~%=?= ~A~%" (format nil "~A" expr) expected)
(format t " ===> SKIP~%"))
(values));; synonymous of nil if disabled
;; when enabled
(let ((result-var (gensym "result-"))
(expected-var (gensym "expected-"))
(err-fun-var (gensym "err-fun-"))
(expr-str (format nil "~A" expr)))
`(progn
(when *example-with-echo*
(format t "------------------~%")
(format t "Example:~%~A~%=?= ~A~%" ,expr-str ,expected))
(let ((,err-fun-var (if ,warn-only #'warn #'error))
(,result-var ,expr)
(,expected-var ,expected))
(if (not (equal (symbol-name (quote ,arrow)) "=>"))
(error "Missing arrow '=>' in example expression"))
(if (funcall *example-equal-predicate* ,result-var ,expected-var)
(progn (if *example-with-echo*
(format t " ===> PASS~%"))
t)
(funcall ,err-fun-var "Failed example:~% Expression: ~S~% ==> expected: ~A~% ==> evaluated: ~A~%"
,expr-str ,expected-var ,result-var)))))))
(defmacro example-progn (&body body)
"The toplevel forms of BODY are evaluated only if examples are enabled"
(if *example-enabled*
`(progn ,@body)
(values)))
(defmacro logg (level fmt &rest args)
"Log the passed ARGS using the format string FMT and its
arguments ARGS."
(if (or (not *log-enabled*)
(< level *log-level*))
(values);; disabled
;; when enabled
`(progn (format ,*log-out-stream* "[LOG]:")
(format ,*log-out-stream* ,fmt ,@args)
(format ,*log-out-stream* "~%"))))
(defmacro vbinds (binders expr &body body)
"An abbreviation for MULTIPLE-VALUE-BIND."
(labels ((replace-underscores (bs &optional (result nil) (fresh-vars nil) (replaced nil))
(if (null bs)
(let ((nresult (nreverse result))
(nfresh (nreverse fresh-vars)))
(values replaced nresult nfresh))
(if (equal (symbol-name (car bs)) "_")
(let ((fresh-var (gensym "underscore-")))
(replace-underscores (cdr bs) (cons fresh-var result) (cons fresh-var fresh-vars) t))
(replace-underscores (cdr bs) (cons (car bs) result) fresh-vars replaced)))))
(multiple-value-bind (has-underscore nbinders fresh-vars) (replace-underscores binders)
(if has-underscore
`(multiple-value-bind ,nbinders ,expr
(declare (ignore ,@fresh-vars))
,@body)
`(multiple-value-bind ,binders ,expr ,@body)))))
(example (vbinds (a _ b) (values 1 2 3)
(cons a b))
=> '(1 . 3)) ;; without a warning
(example (vbinds (a _ b _) (values 1 2 3 4)
(cons a b))
=> '(1 . 3)) ;; without a warning
(defun afetch (comp alist &key (test #'eql))
(let ((binding (assoc comp alist :test test)))
(if binding
(cdr binding)
(error "No such key: ~A" comp))))
(defmacro while (condition &body body)
(let ((eval-cond-var (gensym "eval-cond-"))
(body-val-var (gensym "body-val-")))
`(flet ((,eval-cond-var () ,`,condition))
(do ((,body-val-var nil (progn ,@body)))
((not (,eval-cond-var))
,body-val-var)))))
(example (let ((count 0))
(while (< count 10)
;;(format t "~A " count)
(incf count)
count))
=> 10)
(defun read-file-lines (filename)
(with-open-file (input filename)
(loop
for line = (read-line input nil 'eof)
until (eq line 'eof)
collect line)))
(defun read-binary-file (filename)
(with-open-file (stream filename :element-type '(unsigned-byte 8))
(let ((bytes (make-array (file-length stream) :element-type '(unsigned-byte 8))))
(read-sequence bytes stream)
bytes)))
(defun read-string-file (filename)
(with-open-file (stream filename)
(let ((str (make-array (file-length stream) :element-type 'character :fill-pointer t)))
(setf (fill-pointer str) (read-sequence str stream))
str)))