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