Commit | Line | Data |
---|---|---|
ea81a808 JM |
1 | # |
2 | # mal (Make a Lisp) object types | |
3 | # | |
4 | ||
5 | if [ -z "${__mal_core_included__}" ]; then | |
6 | __mal_core_included=true | |
7 | ||
8 | source $(dirname $0)/types.sh | |
9 | source $(dirname $0)/printer.sh | |
10 | ||
11 | # Exceptions/Errors | |
12 | ||
13 | throw() { | |
14 | __ERROR="${1}" | |
15 | r= | |
16 | } | |
17 | ||
18 | ||
19 | # General functions | |
20 | ||
21 | obj_type () { | |
22 | _obj_type "${1}" | |
23 | _string "${r}" | |
24 | } | |
25 | ||
26 | equal? () { | |
27 | _equal? "${1}" "${2}" && r="${__true}" || r="${__false}" | |
28 | } | |
29 | ||
30 | ||
31 | # Scalar functions | |
32 | ||
33 | nil? () { _nil? "${1}" && r="${__true}" || r="${__false}"; } | |
34 | true? () { _true? "${1}" && r="${__true}" || r="${__false}"; } | |
35 | false? () { _false? "${1}" && r="${__true}" || r="${__false}"; } | |
36 | ||
37 | ||
38 | # Symbol functions | |
39 | ||
40 | symbol? () { _symbol? "${1}" && r="${__true}" || r="${__false}"; } | |
41 | ||
42 | ||
43 | # Number functions | |
44 | ||
45 | number? () { _number? "${1}" && r="${__true}" || r="${__false}"; } | |
46 | ||
47 | num_plus () { r=$(( ${ANON["${1}"]} + ${ANON["${2}"]} )); _number "${r}"; } | |
48 | num_minus () { r=$(( ${ANON["${1}"]} - ${ANON["${2}"]} )); _number "${r}"; } | |
49 | num_multiply () { r=$(( ${ANON["${1}"]} * ${ANON["${2}"]} )); _number "${r}"; } | |
50 | num_divide () { r=$(( ${ANON["${1}"]} / ${ANON["${2}"]} )); _number "${r}"; } | |
51 | ||
52 | _num_bool () { [[ "${1}" = "1" ]] && r="${__true}" || r="${__false}"; } | |
53 | num_gt () { r=$(( ${ANON["${1}"]} > ${ANON["${2}"]} )); _num_bool "${r}"; } | |
54 | num_gte () { r=$(( ${ANON["${1}"]} >= ${ANON["${2}"]} )); _num_bool "${r}"; } | |
55 | num_lt () { r=$(( ${ANON["${1}"]} < ${ANON["${2}"]} )); _num_bool "${r}"; } | |
56 | num_lte () { r=$(( ${ANON["${1}"]} <= ${ANON["${2}"]} )); _num_bool "${r}"; } | |
57 | ||
58 | ||
59 | # String functions | |
60 | ||
61 | string? () { _string? "${1}" && r="${__true}" || r="${__false}"; } | |
62 | ||
63 | pr_str () { | |
64 | local res="" | |
65 | for x in "${@}"; do _pr_str "${x}" yes; res="${res} ${r}"; done | |
66 | _string "${res:1}" | |
67 | } | |
68 | ||
69 | str () { | |
70 | local res="" | |
71 | for x in "${@}"; do _pr_str "${x}"; res="${res}${r}"; done | |
72 | _string "${res}" | |
73 | } | |
74 | ||
75 | prn () { | |
76 | local res="" | |
77 | for x in "${@}"; do _pr_str "${x}" yes; res="${res} ${r}"; done | |
78 | echo "${res:1}" | |
79 | r="${__nil}"; | |
80 | } | |
81 | ||
82 | println () { | |
83 | local res="" | |
84 | for x in "${@}"; do _pr_str "${x}"; res="${res} ${r}"; done | |
85 | res="${res//\\n/$'\n'}" | |
86 | echo -e "${res:1}" | |
87 | r="${__nil}"; | |
88 | } | |
89 | ||
90 | ||
91 | # Function functions | |
92 | function? () { _function? "${1}" && r="${__true}" || r="${__false}"; } | |
93 | ||
94 | ||
95 | # List functions | |
96 | list? () { _list? "${1}" && r="${__true}" || r="${__false}"; } | |
97 | ||
98 | ||
99 | # Vector functions (same as lists for now) | |
100 | vector? () { _vector? "${1}" && r="${__true}" || r="${__false}"; } | |
101 | ||
102 | ||
103 | # Hash map (associative array) functions | |
104 | hash_map? () { _hash_map? "${1}" && r="${__true}" || r="${__false}"; } | |
105 | ||
106 | # Return new hash map with keys/values updated | |
107 | assoc () { | |
108 | if ! _hash_map? "${1}"; then | |
109 | _error "assoc onto non-hash-map" | |
110 | return | |
111 | fi | |
112 | _copy_hash_map "${1}"; shift | |
113 | local name="${r}" | |
114 | local obj=${ANON["${name}"]} | |
115 | declare -A -g ${obj} | |
116 | ||
117 | while [[ "${1}" ]]; do | |
118 | eval ${obj}[\"${ANON["${1}"]}\"]=\"${2}\" | |
119 | shift; shift | |
120 | done | |
121 | r="${name}" | |
122 | } | |
123 | ||
124 | dissoc () { | |
125 | if ! _hash_map? "${1}"; then | |
126 | _error "dissoc from non-hash-map" | |
127 | return | |
128 | fi | |
129 | _copy_hash_map "${1}"; shift | |
130 | local name="${r}" | |
131 | local obj=${ANON["${name}"]} | |
132 | declare -A -g ${obj} | |
133 | ||
134 | while [[ "${1}" ]]; do | |
135 | eval unset ${obj}[\"${ANON["${1}"]}\"] | |
136 | shift | |
137 | done | |
138 | r="${name}" | |
139 | } | |
140 | ||
141 | _get () { | |
142 | _obj_type "${1}"; local ot="${r}" | |
143 | case "${ot}" in | |
144 | hash_map) | |
145 | local obj="${ANON["${1}"]}" | |
146 | eval r="\${${obj}[\"${2}\"]}" ;; | |
147 | list|vector) | |
148 | _nth "${1}" "${2}" | |
149 | esac | |
150 | } | |
151 | get () { | |
152 | _get "${1}" "${ANON["${2}"]}" | |
153 | [[ "${r}" ]] || r="${__nil}" | |
154 | } | |
155 | ||
156 | _contains? () { | |
157 | local obj="${ANON["${1}"]}" | |
158 | #echo "_contains? ${1} ${2} -> \${${obj}[\"${2}\"]+isset}" | |
159 | eval [[ "\${${obj}[\"${2}\"]+isset}" ]] | |
160 | } | |
161 | contains? () { _contains? "${1}" "${ANON["${2}"]}" && r="${__true}" || r="${__false}"; } | |
162 | ||
163 | keys () { | |
164 | local obj="${ANON["${1}"]}" | |
165 | local kstrs= | |
166 | eval local keys="\${!${obj}[@]}" | |
167 | for k in ${keys}; do | |
168 | _string "${k}" | |
169 | kstrs="${kstrs} ${r}" | |
170 | done | |
171 | ||
172 | __new_obj_hash_code | |
173 | r="list_${r}" | |
174 | ANON["${r}"]="${kstrs:1}" | |
175 | } | |
176 | ||
177 | vals () { | |
178 | local obj="${ANON["${1}"]}" | |
179 | local kvals= | |
180 | local val= | |
181 | eval local keys="\${!${obj}[@]}" | |
182 | for k in ${keys}; do | |
183 | eval val="\${${obj}["\${k}"]}" | |
184 | kvals="${kvals} ${val}" | |
185 | done | |
186 | ||
187 | __new_obj_hash_code | |
188 | r="list_${r}" | |
189 | ANON["${r}"]="${kvals:1}" | |
190 | } | |
191 | ||
192 | ||
193 | # sequence operations | |
194 | ||
195 | sequential? () { | |
196 | _sequential? "${1}" && r="${__true}" || r="${__false}" | |
197 | } | |
198 | ||
199 | cons () { | |
200 | _list ${1} ${ANON["${2}"]} | |
201 | } | |
202 | ||
203 | concat () { | |
204 | _list | |
205 | local acc="" | |
206 | for item in "${@}"; do | |
207 | acc="${acc} ${ANON["${item}"]}" | |
208 | done | |
209 | ANON["${r}"]="${acc:1}" | |
210 | } | |
211 | ||
212 | nth () { | |
213 | _nth "${1}" "${ANON["${2}"]}" | |
214 | } | |
215 | ||
216 | first () { | |
217 | local temp="${ANON["${1}"]}" | |
218 | r="${temp%% *}" | |
219 | [ "${r}" ] || r="${__nil}" | |
220 | } | |
221 | ||
222 | # Creates a new vector/list of the everything after but the first | |
223 | # element | |
224 | rest () { | |
225 | local temp="${ANON["${1}"]}" | |
226 | _list | |
227 | if [[ "${temp#* }" == "${temp}" ]]; then | |
228 | ANON["${r}"]= | |
229 | else | |
230 | ANON["${r}"]="${temp#* }" | |
231 | fi | |
232 | } | |
233 | ||
234 | last () { | |
235 | local temp="${ANON["${1}"]}" | |
236 | r="${temp##* }" | |
237 | } | |
238 | ||
239 | empty? () { _empty? "${1}" && r="${__true}" || r="${__false}"; } | |
240 | ||
241 | count () { | |
242 | _count "${1}" | |
243 | _number "${r}" | |
244 | } | |
245 | ||
246 | conj () { | |
247 | local obj="${1}"; shift | |
248 | local obj_data="${ANON["${obj}"]}" | |
249 | __new_obj_like "${obj}" | |
250 | if _list? "${obj}"; then | |
251 | ANON["${r}"]="${obj_data:+${obj_data}}" | |
252 | for elem in ${@}; do | |
253 | ANON["${r}"]="${elem} ${ANON["${r}"]}" | |
254 | done | |
255 | ||
256 | else | |
257 | ANON["${r}"]="${obj_data:+${obj_data} }${*}" | |
258 | fi | |
259 | } | |
260 | ||
261 | apply () { | |
262 | local f="${ANON["${1}"]}"; shift | |
263 | local items="${@:1:$(( ${#@} -1 ))} ${ANON["${!#}"]}" | |
264 | eval ${f%%@*} ${items} | |
265 | } | |
266 | ||
267 | # Takes a function object and an list object and invokes the function | |
268 | # on each element of the list, returning a new list of the results. | |
269 | map () { | |
270 | local f="${ANON["${1}"]}"; shift | |
271 | #echo _map "${f}" "${@}" | |
272 | _map "${f}" "${@}" | |
273 | } | |
274 | ||
275 | ||
276 | # Metadata functions | |
277 | ||
278 | with_meta () { | |
279 | local obj="${1}"; shift | |
280 | local meta_data="${1}"; shift | |
281 | __new_obj_like "${obj}" | |
282 | ANON["${r}"]="${ANON["${obj}"]}" | |
283 | local meta_obj="meta_${r#*_}" | |
284 | ANON["${meta_obj}"]="${meta_data}" | |
285 | } | |
286 | ||
287 | meta () { | |
288 | r="${ANON["meta_${1#*_}"]}" | |
289 | [[ "${r}" ]] || r="${__nil}" | |
290 | } | |
291 | ||
292 | ||
293 | # atoms | |
294 | ||
295 | atom? () { _atom? "${1}" && r="${__true}" || r="${__false}"; } | |
296 | deref () { | |
297 | # TODO: double-check atom type | |
298 | r=${ANON["${1}"]} | |
299 | } | |
300 | reset_BANG () { | |
301 | local atm="${1}"; shift | |
302 | ANON["${atm}"]="${*}" | |
303 | r="${*}" | |
304 | } | |
305 | swap_BANG () { | |
306 | local atm="${1}"; shift | |
307 | local f="${ANON["${1}"]}"; shift | |
308 | ${f%%@*} "${ANON["${atm}"]}" "${@}" | |
309 | ANON["${atm}"]="${r}" | |
310 | } | |
311 | ||
312 | ||
313 | ||
314 | # Namespace of core functions | |
315 | ||
316 | declare -A core_ns=( | |
317 | [type]=obj_type | |
318 | [=]=equal? | |
319 | [throw]=throw | |
320 | [nil?]=nil? | |
321 | [true?]=true? | |
322 | [false?]=false? | |
323 | [symbol?]=symbol? | |
324 | [pr-str]=pr_str | |
325 | [str]=str | |
326 | [prn]=prn | |
327 | [println]=println | |
328 | [<]=num_lt | |
329 | [<=]=num_lte | |
330 | [>]=num_gt | |
331 | [>=]=num_gte | |
332 | [+]=num_plus | |
333 | [-]=num_minus | |
334 | [__STAR__]=num_multiply | |
335 | [/]=num_divide | |
336 | ||
337 | [list]=_list | |
338 | [list?]=list? | |
339 | [vector]=_vector | |
340 | [vector?]=vector? | |
341 | [hash-map]=_hash_map | |
342 | [map?]=hash_map? | |
343 | [assoc]=assoc | |
344 | [dissoc]=dissoc | |
345 | [get]=get | |
346 | [contains?]=contains? | |
347 | [keys]=keys | |
348 | [vals]=vals | |
349 | ||
350 | [sequential?]=sequential? | |
351 | [cons]=cons | |
352 | [concat]=concat | |
353 | [nth]=nth | |
354 | [first]=first | |
355 | [rest]=rest | |
356 | [empty?]=empty? | |
357 | [count]=count | |
358 | [conj]=conj | |
359 | [apply]=apply | |
360 | [map]=map | |
361 | ||
362 | [with-meta]=with_meta | |
363 | [meta]=meta | |
364 | [atom]=_atom | |
365 | [atom?]=atom? | |
366 | [deref]=deref | |
367 | [reset!]=reset_BANG | |
368 | [swap!]=swap_BANG) | |
369 | ||
370 | fi |