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 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 | 352 | fi |