Travis: split build and test into separate scripts.
[jackhill/mal.git] / 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
JM
83 [[ "${ANON["${1}"]}" == "${ANON["${2}"]}" ]] ;;
84 list|vector|hash_map)
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 ;;
94 *)
95 [[ "${1}" == "${2}" ]] ;;
96 esac
31690700
JM
97}
98
31690700 99# Constant atomic values
31690700 100
31690700
JM
101__nil=_nil_0
102__true=true_0
103__false=fals_0
104
31690700 105_nil? () { [[ ${1} =~ ^_nil_ ]]; }
31690700 106_true? () { [[ ${1} =~ ^true_ ]]; }
31690700 107_false? () { [[ ${1} =~ ^fals_ ]]; }
31690700 108
31690700 109
31690700 110# Symbols
31690700 111
ea81a808 112_symbol () {
31690700
JM
113 __new_obj_hash_code
114 r="symb_${r}"
01c97316 115 ANON["${r}"]="${1//\*/__STAR__}"
31690700
JM
116}
117_symbol? () { [[ ${1} =~ ^symb_ ]]; }
ea81a808
JM
118
119
b8ee29b2
JM
120# Keywords
121
122_keyword () {
123 local k="${1}"
124 __new_obj_hash_code
125 r="strn_${r}"
dbac60df
JM
126 if [[ "${1:0:1}" = "${__keyw}" ]] \
127 || [[ "${1:0:2}" = "${__keyw}" ]]; then
128 true
129 else
130 k="${__keyw}${1}"
131 fi
b8ee29b2
JM
132 ANON["${r}"]="${k//\*/__STAR__}"
133}
134_keyword? () {
135 [[ ${1} =~ ^strn_ ]] || return 1
136 local s="${ANON["${1}"]}"
dbac60df 137 [[ "${s:0:1}" = "${__keyw}" ]] || [[ "${s:0:2}" = "${__keyw}" ]]
b8ee29b2
JM
138}
139
140
ea81a808
JM
141# Numbers
142
143_number () {
144 __new_obj_hash_code
145 r="numb_${r}"
146 ANON["${r}"]="${1}"
31690700 147}
ea81a808 148_number? () { [[ ${1} =~ ^numb_ ]]; }
31690700
JM
149
150
31690700 151# Strings
31690700 152
ea81a808 153_string () {
31690700
JM
154 __new_obj_hash_code
155 r="strn_${r}"
01c97316 156 ANON["${r}"]="${1//\*/__STAR__}"
31690700
JM
157}
158_string? () { [[ ${1} =~ ^strn_ ]]; }
31690700
JM
159
160
ea81a808 161# Functions
31690700
JM
162# Return a function object. The first parameter is the
163# function 'source'.
ea81a808 164_function () {
31690700
JM
165 __new_obj_hash_code
166 eval "function ${__obj_magic}_func_${r} () { ${1%;} ; }"
167 r="func_${r}"
168 if [[ "${2}" ]]; then
169 # Native function
170 ANON["${r}"]="${__obj_magic}_${r}@${2}@${3}@${4}"
171 else
172 # Bash function
173 ANON["${r}"]="${__obj_magic}_${r}"
174 fi
175}
176_function? () { [[ ${1} =~ ^func_ ]]; }
31690700
JM
177
178
ea81a808
JM
179# Lists
180
181_list () {
182 __new_obj_hash_code
183 r="list_${r}"
184 ANON["${r}"]="${*}"
185}
186_list? () { [[ ${1} =~ ^list_ ]]; }
187
188
189# Vectors
190
191_vector () {
192 __new_obj_hash_code
193 r="vector_${r}"
194 ANON["${r}"]="${*}"
195}
196_vector? () { [[ ${1} =~ ^vector_ ]]; }
197
198
31690700 199# hash maps (associative arrays)
31690700 200
ea81a808 201_hash_map () {
31690700
JM
202 __new_obj_hash_code
203 local name="hmap_${r}"
204 local obj="${__obj_magic}_${name}"
01c97316 205 declare -A -g ${obj}; eval "${obj}=()"
31690700
JM
206 ANON["${name}"]="${obj}"
207
208 while [[ "${1}" ]]; do
209 eval ${obj}[\"${ANON["${1}"]}\"]=\"${2}\"
210 shift; shift
211 done
212
213 r="${name}"
214}
215_hash_map? () { [[ ${1} =~ ^hmap_ ]]; }
31690700 216
8cb5cda4
JM
217_contains? () {
218 local obj="${ANON["${1}"]}"
219 eval [[ "\${${obj}[\"${2}\"]+isset}" ]]
220}
221
31690700
JM
222_copy_hash_map () {
223 local orig_obj="${ANON["${1}"]}"
ea81a808 224 _hash_map
31690700
JM
225 local name="${r}"
226 local obj="${ANON["${name}"]}"
227
228 # Copy the existing key/values to the new object
229 local temp=$(typeset -p ${orig_obj})
230 eval ${temp/#declare -A ${orig_obj}=/declare -A -g ${obj}=}
231 r="${name}"
232}
233
234# Return same hash map with keys/values added/mutated in place
ea81a808 235_assoc! () {
31690700
JM
236 local obj=${ANON["${1}"]}; shift
237 declare -A -g ${obj}
238
239 # Set the key/values specified
240 while [[ "${1}" ]]; do
241 eval ${obj}[\"${1}\"]=\"${2}\"
242 shift; shift
243 done
244}
245
246# Return same hash map with keys/values deleted/mutated in place
ea81a808 247_dissoc! () {
31690700
JM
248 local obj=${ANON["${1}"]}; shift
249 declare -A -g ${obj}
250
251 # Delete the key/values specified
252 while [[ "${1}" ]]; do
253 eval unset ${obj}[\"${1}\"]
254 shift
255 done
256}
257
31690700 258
ea81a808 259# Atoms
31690700 260
ea81a808 261_atom() {
31690700
JM
262 __new_obj_hash_code
263 r="atom_${r}"
264 ANON["${r}"]="${*}"
265}
266_atom? () { [[ ${1} =~ ^atom_ ]]; }
31690700
JM
267
268
31690700 269# sequence operations
31690700
JM
270
271_sequential? () {
272 _list? "${1}" || _vector? "${1}"
273}
31690700
JM
274
275_nth () {
276 local temp=(${ANON["${1}"]})
b8ee29b2 277 r="${temp[${2}]}"
31690700 278}
31690700 279
8cb5cda4
JM
280_first () {
281 local temp="${ANON["${1}"]}"
282 r="${temp%% *}"
283 [ "${r}" ] || r="${__nil}"
284}
285
286_last () {
287 local temp="${ANON["${1}"]}"
288 r="${temp##* }"
289}
290
291# Creates a new vector/list of the everything after but the first
292# element
293_rest () {
294 local temp="${ANON["${1}"]}"
295 _list
296 if [[ "${temp#* }" == "${temp}" ]]; then
297 ANON["${r}"]=
298 else
299 ANON["${r}"]="${temp#* }"
300 fi
301}
302
303
31690700 304_empty? () { [[ -z "${ANON["${1}"]}" ]]; }
31690700 305
9528bb14 306# conj that mutates in place (and always appends)
ea81a808 307_conj! () {
31690700
JM
308 local obj="${1}"; shift
309 local obj_data="${ANON["${obj}"]}"
310 ANON["${obj}"]="${obj_data:+${obj_data} }${*}"
311 r="${1}"
312}
313
314
315
316_count () {
b8ee29b2
JM
317 if _nil? "${1}"; then
318 r="0"
319 else
320 local temp=(${ANON["${1}"]})
321 r=${#temp[*]}
322 fi
31690700 323}
31690700
JM
324
325# Slice a sequence object $1 starting at $2 of length $3
326_slice () {
327 local temp=(${ANON["${1}"]})
328 __new_obj_like "${1}"
329 ANON["${r}"]="${temp[@]:${2}:${3}}"
330}
331
31690700
JM
332# Takes a bash function and an list object and invokes the function on
333# each element of the list, returning a new list (or vector) of the results.
334_map_with_type () {
ea81a808 335 local constructor="${1}"; shift
31690700
JM
336 local f="${1}"; shift
337 local items="${ANON["${1}"]}"; shift
ea81a808 338 eval "${constructor}"; local new_seq="${r}"
31690700
JM
339 for v in ${items}; do
340 #echo eval ${f%%@*} "${v}" "${@}"
341 eval ${f%%@*} "${v}" "${@}"
342 [[ "${__ERROR}" ]] && r= && return 1
ea81a808 343 _conj! "${new_seq}" "${r}"
31690700
JM
344 done
345 r="${new_seq}"
346}
347
348_map () {
ea81a808 349 _map_with_type _list "${@}"
31690700
JM
350}
351
ea81a808 352fi