forked from coalton-lang/coalton
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutilities.lisp
160 lines (137 loc) · 5.37 KB
/
utilities.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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
;;;; utilities.lisp
(defpackage #:coalton-impl/util
(:documentation "Utility functions and methods used throughout COALTON.")
(:use #:cl)
(:export
#:required ; FUNCTION
#:unreachable ; MACRO
#:coalton-bug ; FUNCTION
#:debug-log ; MACRO
#:debug-tap ; MACRO
#:symbol-list ; TYPE
#:literal-value ; TYPE
#:maphash-values-new ; FUNCTION
#:find-symbol? ; FUNCTION
#:sexp-fmt ; FUNCTION
#:take-until ; FUNCTION
#:project-indicies ; FUNCTION
#:project-map ; FUNCTION
))
(in-package #:coalton-impl/util)
(defun symbol-list-p (x)
(and (alexandria:proper-list-p x)
(every #'symbolp x)))
(deftype symbol-list ()
'(satisfies symbol-list-p))
(defmacro debug-log (&rest vars)
"Log names and values of VARS to standard output"
`(format t
,(format nil "~{~A: ~~A~~%~}" vars)
,@vars))
(defmacro debug-tap (var)
(let ((var-name (gensym)))
`(let ((,var-name ,var))
(format t ,(format nil "~A: ~~A~~%" var) ,var-name)
,var-name)))
(define-condition coalton-bug (error)
((reason :initarg :reason
:reader coalton-bug-reason)
(args :initarg :args
:reader coalton-bug-args))
(:report (lambda (c s)
(format s "Internal coalton bug: ~?~%~%If you are seeing this, please file an issue on Github."
(coalton-bug-reason c)
(coalton-bug-args c)))))
(defun coalton-bug (reason &rest args)
(error 'coalton-bug
:reason reason
:args args))
(defmacro unreachable ()
"Assert that a branch of code cannot be evaluated in the course of normal execution."
;; Ideally, we would *catch* the code-deletion-note condition and signal a
;; warning if no such condition was seen (i.e., if SBCL didn't prove the
;; (UNREACHABLE) form to be prunable). As far as I can tell, though, that
;; requires wrapping the entire containing toplevel form in a HANDLER-BIND,
;; which cannot be done by the expansion of an inner macro form.
'(locally
#+sbcl (declare (sb-ext:muffle-conditions sb-ext:code-deletion-note))
(coalton-bug "This error was expected to be unreachable in the Coalton source code.")))
(defun maphash-values-new (function table)
"Map across the values of a hash-table. Returns a new hash-table with unchanged keys."
(declare (type function function)
(type hash-table table))
(let ((new (make-hash-table)))
(loop :for k :being :the :hash-keys :of table
:for v :being :the :hash-values :of table
:do (setf (gethash k new) (funcall function v)))
new))
(defun find-symbol? (name package)
(declare (type string name package)
(values symbol-list))
(unless (find-package package)
(return-from find-symbol?))
(list (alexandria:ensure-symbol name package)))
(defun required (name)
"A function to call as a slot initializer when it's required."
(declare (type symbol name))
(coalton-bug "A slot ~S (of package ~S) is required but not supplied" name (symbol-package name)))
(defun sexp-fmt (stream object &optional colon-modifier at-modifier)
"A formatter for qualified S-expressions. Use like
(format t \"~/coalton-impl::sexp-fmt/\" '(:x y 5))
and it will print a flat S-expression with all symbols qualified."
(declare (ignore colon-modifier at-modifier))
(let ((*print-pretty* nil)
(*package* (find-package "KEYWORD")))
(prin1 object stream)))
(deftype literal-value ()
"Allowed literal values as Lisp objects."
'(or integer ratio single-float double-float string character))
(defun take-until (pred list)
"Splits LIST into two lists on the element where PRED first returns true"
(declare (type list list)
(values list list))
(let (out)
(labels ((inner (xs)
(cond
((null xs) nil)
((funcall pred (car xs)) xs)
(t
(push (car xs) out)
(inner (cdr xs))))))
(declare (dynamic-extent #'inner))
(let ((result (inner list)))
(values
(nreverse out)
result)))))
(defun project-indicies (indices data)
(declare (type list indices data)
(values list))
(labels ((inner (is xs pos out)
(cond
;; Data is done, indices are not
((and is (null xs))
(error "Indices ~A extend past data" is))
;; Data or indicies are done
((or (null is)
(null xs))
out)
;; match
((eql (car is) pos)
(inner (cdr is) (cdr xs) (1+ pos) (cons (car xs) out)))
;; indicie is past pos
((> pos (car is))
(inner (cdr is) xs pos out))
(t
(inner is (cdr xs) (1+ pos) out)))))
(declare (dynamic-extent #'inner))
(nreverse (inner indices data 0 nil))))
(defun project-map (indicies map data)
(declare (type symbol-list indicies)
(type hash-table map)
(type list data))
(project-indicies
(sort
(loop :for key :in indicies
:collect (gethash key map))
#'<)
data))