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