Commit | Line | Data |
---|---|---|
31690700 | 1 | # |
ea81a808 | 2 | # mal (Make a Lisp) object types |
31690700 JM |
3 | # |
4 | ||
ea81a808 JM |
5 | if [ -z "${__mal_types_included__}" ]; then |
6 | __mal_types_included=true | |
7 | ||
31690700 JM |
8 | declare -A ANON |
9 | ||
10 | __obj_magic=__5bal7 | |
c9de2e82 | 11 | __keyw=$(echo -en "\xCA\x9E") # \u029E |
31690700 JM |
12 | __obj_hash_code=${__obj_hash_code:-0} |
13 | ||
14 | __new_obj_hash_code () { | |
15 | __obj_hash_code=$(( __obj_hash_code + 1)) | |
16 | r="${__obj_hash_code}" | |
17 | } | |
18 | ||
19 | __new_obj () { | |
20 | __new_obj_hash_code | |
21 | r="${1}_${r}" | |
22 | } | |
23 | ||
24 | __new_obj_like () { | |
25 | __new_obj_hash_code | |
26 | r="${1%_*}_${r}" | |
27 | } | |
28 | ||
ea81a808 JM |
29 | |
30 | # Errors/Exceptions | |
31 | ||
31690700 | 32 | __ERROR= |
ea81a808 JM |
33 | _error() { |
34 | _string "${1}" | |
35 | __ERROR="${r}" | |
36 | r= | |
37 | } | |
38 | ||
31690700 JM |
39 | |
40 | ||
41 | # | |
42 | # General functions | |
43 | # | |
44 | ||
45 | # Return the type of the object (or "make" if it's not a object | |
46 | _obj_type () { | |
47 | local type="${1:0:4}" | |
48 | r= | |
49 | case "${type}" in | |
50 | symb) r="symbol" ;; | |
51 | list) r="list" ;; | |
52 | numb) r="number" ;; | |
53 | func) r="function" ;; | |
b8ee29b2 JM |
54 | strn) |
55 | local s="${ANON["${1}"]}" | |
dbac60df JM |
56 | if [[ "${1:0:1}" = "${__keyw}" ]] \ |
57 | || [[ "${1:0:2}" = "${__keyw}" ]]; then | |
58 | r="keyword" | |
59 | else | |
60 | r="string" | |
61 | fi ;; | |
31690700 JM |
62 | _nil) r="nil" ;; |
63 | true) r="true" ;; | |
64 | fals) r="false" ;; | |
65 | vect) r="vector" ;; | |
66 | hmap) r="hash_map" ;; | |
67 | atom) r="atom" ;; | |
68 | undf) r="undefined" ;; | |
69 | *) r="bash" ;; | |
70 | esac | |
71 | } | |
72 | ||
ea81a808 JM |
73 | _equal? () { |
74 | _obj_type "${1}"; local ot1="${r}" | |
75 | _obj_type "${2}"; local ot2="${r}" | |
76 | if [[ "${ot1}" != "${ot2}" ]]; then | |
77 | if ! _sequential? "${1}" || ! _sequential? "${2}"; then | |
78 | return 1 | |
79 | fi | |
31690700 | 80 | fi |
ea81a808 | 81 | case "${ot1}" in |
b8ee29b2 | 82 | string|symbol|keyword|number) |
ea81a808 | 83 | [[ "${ANON["${1}"]}" == "${ANON["${2}"]}" ]] ;; |
a0b63ee4 | 84 | list|vector) |
ea81a808 JM |
85 | _count "${1}"; local sz1="${r}" |
86 | _count "${2}"; local sz2="${r}" | |
87 | [[ "${sz1}" == "${sz2}" ]] || return 1 | |
88 | local a1=(${ANON["${1}"]}) | |
89 | local a2=(${ANON["${2}"]}) | |
90 | for ((i=0;i<${#a1[*]};i++)); do | |
91 | _equal? "${a1[${i}]}" "${a2[${i}]}" || return 1 | |
92 | done | |
93 | ;; | |
a0b63ee4 JM |
94 | hash_map) |
95 | local hm1="${ANON["${1}"]}" | |
96 | eval local ks1="\${!${hm1}[@]}" | |
97 | local hm2="${ANON["${2}"]}" | |
98 | eval local ks2="\${!${hm2}[@]}" | |
99 | [[ "${#ks1}" == "${#ks2}" ]] || return 1 | |
100 | for k in ${ks1}; do | |
101 | eval v1="\${${hm1}[\"${k}\"]}" | |
102 | eval v2="\${${hm2}[\"${k}\"]}" | |
103 | [ "${v1}" ] || return 1 | |
104 | [ "${v2}" ] || return 1 | |
105 | _equal? "${v1}" "${v2}" || return 1 | |
106 | done | |
107 | ;; | |
ea81a808 JM |
108 | *) |
109 | [[ "${1}" == "${2}" ]] ;; | |
110 | esac | |
31690700 JM |
111 | } |
112 | ||
31690700 | 113 | # Constant atomic values |
31690700 | 114 | |
31690700 JM |
115 | __nil=_nil_0 |
116 | __true=true_0 | |
117 | __false=fals_0 | |
118 | ||
31690700 | 119 | _nil? () { [[ ${1} =~ ^_nil_ ]]; } |
31690700 | 120 | _true? () { [[ ${1} =~ ^true_ ]]; } |
31690700 | 121 | _false? () { [[ ${1} =~ ^fals_ ]]; } |
31690700 | 122 | |
31690700 | 123 | |
31690700 | 124 | # Symbols |
31690700 | 125 | |
ea81a808 | 126 | _symbol () { |
31690700 JM |
127 | __new_obj_hash_code |
128 | r="symb_${r}" | |
01c97316 | 129 | ANON["${r}"]="${1//\*/__STAR__}" |
31690700 JM |
130 | } |
131 | _symbol? () { [[ ${1} =~ ^symb_ ]]; } | |
ea81a808 JM |
132 | |
133 | ||
b8ee29b2 JM |
134 | # Keywords |
135 | ||
136 | _keyword () { | |
137 | local k="${1}" | |
138 | __new_obj_hash_code | |
139 | r="strn_${r}" | |
dbac60df JM |
140 | if [[ "${1:0:1}" = "${__keyw}" ]] \ |
141 | || [[ "${1:0:2}" = "${__keyw}" ]]; then | |
142 | true | |
143 | else | |
144 | k="${__keyw}${1}" | |
145 | fi | |
b8ee29b2 JM |
146 | ANON["${r}"]="${k//\*/__STAR__}" |
147 | } | |
148 | _keyword? () { | |
149 | [[ ${1} =~ ^strn_ ]] || return 1 | |
150 | local s="${ANON["${1}"]}" | |
dbac60df | 151 | [[ "${s:0:1}" = "${__keyw}" ]] || [[ "${s:0:2}" = "${__keyw}" ]] |
b8ee29b2 JM |
152 | } |
153 | ||
154 | ||
ea81a808 JM |
155 | # Numbers |
156 | ||
157 | _number () { | |
158 | __new_obj_hash_code | |
159 | r="numb_${r}" | |
160 | ANON["${r}"]="${1}" | |
31690700 | 161 | } |
ea81a808 | 162 | _number? () { [[ ${1} =~ ^numb_ ]]; } |
31690700 JM |
163 | |
164 | ||
31690700 | 165 | # Strings |
31690700 | 166 | |
ea81a808 | 167 | _string () { |
31690700 JM |
168 | __new_obj_hash_code |
169 | r="strn_${r}" | |
01c97316 | 170 | ANON["${r}"]="${1//\*/__STAR__}" |
31690700 JM |
171 | } |
172 | _string? () { [[ ${1} =~ ^strn_ ]]; } | |
31690700 JM |
173 | |
174 | ||
ea81a808 | 175 | # Functions |
31690700 JM |
176 | # Return a function object. The first parameter is the |
177 | # function 'source'. | |
ea81a808 | 178 | _function () { |
31690700 JM |
179 | __new_obj_hash_code |
180 | eval "function ${__obj_magic}_func_${r} () { ${1%;} ; }" | |
181 | r="func_${r}" | |
182 | if [[ "${2}" ]]; then | |
183 | # Native function | |
184 | ANON["${r}"]="${__obj_magic}_${r}@${2}@${3}@${4}" | |
185 | else | |
186 | # Bash function | |
187 | ANON["${r}"]="${__obj_magic}_${r}" | |
188 | fi | |
189 | } | |
190 | _function? () { [[ ${1} =~ ^func_ ]]; } | |
31690700 JM |
191 | |
192 | ||
ea81a808 JM |
193 | # Lists |
194 | ||
195 | _list () { | |
196 | __new_obj_hash_code | |
197 | r="list_${r}" | |
198 | ANON["${r}"]="${*}" | |
199 | } | |
200 | _list? () { [[ ${1} =~ ^list_ ]]; } | |
201 | ||
202 | ||
203 | # Vectors | |
204 | ||
205 | _vector () { | |
206 | __new_obj_hash_code | |
207 | r="vector_${r}" | |
208 | ANON["${r}"]="${*}" | |
209 | } | |
210 | _vector? () { [[ ${1} =~ ^vector_ ]]; } | |
211 | ||
212 | ||
31690700 | 213 | # hash maps (associative arrays) |
31690700 | 214 | |
ea81a808 | 215 | _hash_map () { |
31690700 JM |
216 | __new_obj_hash_code |
217 | local name="hmap_${r}" | |
218 | local obj="${__obj_magic}_${name}" | |
01c97316 | 219 | declare -A -g ${obj}; eval "${obj}=()" |
31690700 JM |
220 | ANON["${name}"]="${obj}" |
221 | ||
222 | while [[ "${1}" ]]; do | |
223 | eval ${obj}[\"${ANON["${1}"]}\"]=\"${2}\" | |
224 | shift; shift | |
225 | done | |
226 | ||
227 | r="${name}" | |
228 | } | |
229 | _hash_map? () { [[ ${1} =~ ^hmap_ ]]; } | |
31690700 | 230 | |
8cb5cda4 JM |
231 | _contains? () { |
232 | local obj="${ANON["${1}"]}" | |
233 | eval [[ "\${${obj}[\"${2}\"]+isset}" ]] | |
234 | } | |
235 | ||
31690700 JM |
236 | _copy_hash_map () { |
237 | local orig_obj="${ANON["${1}"]}" | |
ea81a808 | 238 | _hash_map |
31690700 JM |
239 | local name="${r}" |
240 | local obj="${ANON["${name}"]}" | |
241 | ||
242 | # Copy the existing key/values to the new object | |
243 | local temp=$(typeset -p ${orig_obj}) | |
244 | eval ${temp/#declare -A ${orig_obj}=/declare -A -g ${obj}=} | |
245 | r="${name}" | |
246 | } | |
247 | ||
248 | # Return same hash map with keys/values added/mutated in place | |
ea81a808 | 249 | _assoc! () { |
31690700 JM |
250 | local obj=${ANON["${1}"]}; shift |
251 | declare -A -g ${obj} | |
252 | ||
253 | # Set the key/values specified | |
254 | while [[ "${1}" ]]; do | |
255 | eval ${obj}[\"${1}\"]=\"${2}\" | |
256 | shift; shift | |
257 | done | |
258 | } | |
259 | ||
260 | # Return same hash map with keys/values deleted/mutated in place | |
ea81a808 | 261 | _dissoc! () { |
31690700 JM |
262 | local obj=${ANON["${1}"]}; shift |
263 | declare -A -g ${obj} | |
264 | ||
265 | # Delete the key/values specified | |
266 | while [[ "${1}" ]]; do | |
267 | eval unset ${obj}[\"${1}\"] | |
268 | shift | |
269 | done | |
270 | } | |
271 | ||
31690700 | 272 | |
ea81a808 | 273 | # Atoms |
31690700 | 274 | |
ea81a808 | 275 | _atom() { |
31690700 JM |
276 | __new_obj_hash_code |
277 | r="atom_${r}" | |
278 | ANON["${r}"]="${*}" | |
279 | } | |
280 | _atom? () { [[ ${1} =~ ^atom_ ]]; } | |
31690700 JM |
281 | |
282 | ||
31690700 | 283 | # sequence operations |
31690700 JM |
284 | |
285 | _sequential? () { | |
286 | _list? "${1}" || _vector? "${1}" | |
287 | } | |
31690700 JM |
288 | |
289 | _nth () { | |
290 | local temp=(${ANON["${1}"]}) | |
b8ee29b2 | 291 | r="${temp[${2}]}" |
31690700 | 292 | } |
31690700 | 293 | |
8cb5cda4 JM |
294 | _first () { |
295 | local temp="${ANON["${1}"]}" | |
296 | r="${temp%% *}" | |
297 | [ "${r}" ] || r="${__nil}" | |
298 | } | |
299 | ||
300 | _last () { | |
301 | local temp="${ANON["${1}"]}" | |
302 | r="${temp##* }" | |
303 | } | |
304 | ||
305 | # Creates a new vector/list of the everything after but the first | |
306 | # element | |
307 | _rest () { | |
308 | local temp="${ANON["${1}"]}" | |
309 | _list | |
310 | if [[ "${temp#* }" == "${temp}" ]]; then | |
311 | ANON["${r}"]= | |
312 | else | |
313 | ANON["${r}"]="${temp#* }" | |
314 | fi | |
315 | } | |
316 | ||
317 | ||
31690700 | 318 | _empty? () { [[ -z "${ANON["${1}"]}" ]]; } |
31690700 | 319 | |
9528bb14 | 320 | # conj that mutates in place (and always appends) |
ea81a808 | 321 | _conj! () { |
31690700 JM |
322 | local obj="${1}"; shift |
323 | local obj_data="${ANON["${obj}"]}" | |
324 | ANON["${obj}"]="${obj_data:+${obj_data} }${*}" | |
325 | r="${1}" | |
326 | } | |
327 | ||
328 | ||
329 | ||
330 | _count () { | |
b8ee29b2 JM |
331 | if _nil? "${1}"; then |
332 | r="0" | |
333 | else | |
334 | local temp=(${ANON["${1}"]}) | |
335 | r=${#temp[*]} | |
336 | fi | |
31690700 | 337 | } |
31690700 JM |
338 | |
339 | # Slice a sequence object $1 starting at $2 of length $3 | |
340 | _slice () { | |
341 | local temp=(${ANON["${1}"]}) | |
342 | __new_obj_like "${1}" | |
343 | ANON["${r}"]="${temp[@]:${2}:${3}}" | |
344 | } | |
345 | ||
31690700 JM |
346 | # Takes a bash function and an list object and invokes the function on |
347 | # each element of the list, returning a new list (or vector) of the results. | |
348 | _map_with_type () { | |
ea81a808 | 349 | local constructor="${1}"; shift |
31690700 JM |
350 | local f="${1}"; shift |
351 | local items="${ANON["${1}"]}"; shift | |
ea81a808 | 352 | eval "${constructor}"; local new_seq="${r}" |
31690700 JM |
353 | for v in ${items}; do |
354 | #echo eval ${f%%@*} "${v}" "${@}" | |
355 | eval ${f%%@*} "${v}" "${@}" | |
356 | [[ "${__ERROR}" ]] && r= && return 1 | |
ea81a808 | 357 | _conj! "${new_seq}" "${r}" |
31690700 JM |
358 | done |
359 | r="${new_seq}" | |
360 | } | |
361 | ||
362 | _map () { | |
ea81a808 | 363 | _map_with_type _list "${@}" |
31690700 JM |
364 | } |
365 | ||
ea81a808 | 366 | fi |