-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy patharray.clj
192 lines (177 loc) · 6.09 KB
/
array.clj
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
(ns piplin.types.array
"This namespace provides a fixed-length homogenous array type."
(:refer-clojure :exclude [cast])
(:use [slingshot.slingshot])
(:use [piplin protocols types])
(:use [clojure.core.incubator :only [seqable?]])
(:use [piplin.types bits [uintm :only [uintm]]]))
(defpiplintype Array [array-len array-type])
(defn array
[type length]
(when-not (integer? length)
(throw+ "Length must be an integer"))
(when-not (:kind type)
(throw+ (str type " must be a piplin type")))
(merge (Array. length type)
{:kind :array}))
(defmethod promote
:array
[type obj]
;TODO: if type or obj is an error, return it instead, or maybe do best effort, or ...
(if (and (pipinst? obj)
(= (kindof obj) :array))
(let [{atype :array-type alen :array-len} (typeof obj)
s (value obj)]
(if-not (= alen (:array-len type))
(throw+ (error (str "Array is not of length"
(:array-len type)
"=>" obj)))
(instance type (into {}
(map (fn [[k v]]
[k (promote (:array-type type) v)])
s)))))
(do
(when-not (seqable? obj)
(throw+ "Can only promote seqables to piplin array"))
(let [s (seq obj)
{array-type :array-type array-len :array-len} type]
(when-not (= (count s) array-len)
(throw+ (str "Expected " array-len
" elements, but got "
(count s) "elements")))
(let [casted-obj (zipmap (->> (range array-len)
(map (comp keyword str)))
(map (partial cast array-type)
s))]
(if (every? pipinst? (vals casted-obj))
(instance type casted-obj :constrain)
(mkast-explicit-keys type :make-array
(map (comp keyword str) (range array-len))
casted-obj
(fn [& args]
(promote type args)))))))))
(defmethod check
:array
[obj]
(when (and (pipinst? obj)
(some (comp not pipinst?) (vals (value obj))))
(throw+ (error "Array looks like a pipinst, but contains non-pipinst values:" (vals (value obj)))))
obj)
(defmethod bit-width-of
:array
[{:keys [array-type array-len]}]
(* array-len (bit-width-of array-type)))
(defmethod get-bits
:array
[expr]
(let [inst (value expr)
keys (->> (typeof expr)
:array-len
range
(map (comp keyword str)))]
(->> keys
(map #(serialize (inst %1)))
(apply bit-cat)
value)))
(defmethod from-bits
:array
[type bs]
(let [{array-type :array-type array-len :array-len} type
objs (partition (bit-width-of array-type) bs)]
(mapv (partial from-bits array-type) objs)))
(defmethod piplin.types/nth-multi
:array
([array i]
(piplin.types/nth-multi array i nil))
([array i notfound]
(get array i)))
;TODO: ensure that i is the correct bit width, or a constant
(defmethod piplin.types/valAt-multi
:array
([array i]
(piplin.types/valAt-multi array i nil))
([array i notfound]
(let [i (cast (uintm (log2 (-> array typeof :array-len))) i)]
(if (and (pipinst? i)
(pipinst? array))
(get (value array) (-> i value str keyword) notfound)
(mkast (:array-type (typeof array))
:array-get
[array i]
piplin.types/valAt-multi)))))
(defmethod piplin.types/entryAt-multi
:array
([array i]
(let [alen (:array-len (typeof array))]
(when (>= i alen)
(throw+ (str "Array is only " alen
"long; tried to access index " i)))
(get array i))))
(defmethod piplin.types/assoc-multi
:array
[array index v]
(let [{:keys [array-type array-len]} (typeof array)
v (cast array-type v)]
(if (and (pipinst? array)
(pipinst? index)
(pipinst? v))
(do
(when (or (neg? (value index))
(>= (value index) array-len))
(throw+ "Index must be between 0 and" array-len
"but was" (value index)))
((typeof array) (assoc (value array) ((comp keyword str) (value index)) v)))
(mkast (typeof array)
:array-assoc
[array index v]
assoc))))
(defmethod piplin.types/count-multi
:array
[array]
(:array-len (typeof array)))
(defmethod piplin.types/seq-multi
:array
[array]
(when-not (pipinst? array)
(throw+ (error "Can only use seq on pipinst arrays")))
(let [{:keys [array-len]} (typeof array)
val-map (value array)]
(->> (range array-len)
(map (comp keyword str))
(map val-map))))
(defn empty-array
"Takes an array type and returns an empty array,
as if all the memory was initialized to zero."
[type]
(let [{:keys [array-len array-type]} type
width (bit-width-of array-type)
bits (cast (bits width) 0)
template (deserialize array-type bits)]
(cast type (vec (repeat array-len template)))))
(defn store
([array write-enable index v]
(assert (= :boolean (kindof write-enable)) "Write enable must be a boolean")
(let [{:keys [array-type array-len]} (typeof array)
v (cast array-type v)]
(if (and (pipinst? array)
(pipinst? index)
(pipinst? write-enable)
(pipinst? v))
(if write-enable
(assoc array index v)
array)
(mkast (typeof array)
:array-store
[array write-enable index v]
store))))
([a we i v & more]
(assert (zero? (mod (count more) 3)) "Inputs must come in groups of 3")
(when (every? pipinst? (list* we i v more))
(let [indices (conj i (take-nth 2 more))]
(assert (= (count indices) (count (distinct indices)))
"Must assign to different indices")))
(apply store (store a we i v) more)))
(defn array?
"Predicate to determine if the argument is an array"
[a]
(= (kindof a) :array))