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