Merge pull request #514 from alimpfard/jq-fix
[jackhill/mal.git] / impls / bash / types.sh
CommitLineData
31690700 1#
ea81a808 2# mal (Make a Lisp) object types
31690700
JM
3#
4
ea81a808
JM
5if [ -z "${__mal_types_included__}" ]; then
6__mal_types_included=true
7
31690700
JM
8declare -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 366fi