Merge pull request #383 from asarhaddon/ada2tco-do
[jackhill/mal.git] / bash / core.sh
CommitLineData
ea81a808
JM
1#
2# mal (Make a Lisp) object types
3#
4
5if [ -z "${__mal_core_included__}" ]; then
6__mal_core_included=true
7
8source $(dirname $0)/types.sh
8cb5cda4 9source $(dirname $0)/reader.sh
ea81a808
JM
10source $(dirname $0)/printer.sh
11
12# Exceptions/Errors
13
14throw() {
15 __ERROR="${1}"
16 r=
17}
18
19
20# General functions
21
22obj_type () {
23 _obj_type "${1}"
24 _string "${r}"
25}
26
27equal? () {
28 _equal? "${1}" "${2}" && r="${__true}" || r="${__false}"
29}
30
31
32# Scalar functions
33
34nil? () { _nil? "${1}" && r="${__true}" || r="${__false}"; }
35true? () { _true? "${1}" && r="${__true}" || r="${__false}"; }
36false? () { _false? "${1}" && r="${__true}" || r="${__false}"; }
37
38
39# Symbol functions
40
dbac60df
JM
41symbol () { _symbol "${ANON["${1}"]}"; }
42
ea81a808
JM
43symbol? () { _symbol? "${1}" && r="${__true}" || r="${__false}"; }
44
45
b8ee29b2
JM
46# Keyword functions
47
dbac60df
JM
48keyword () { _keyword "${ANON["${1}"]}"; }
49
b8ee29b2
JM
50keyword? () { _keyword? "${1}" && r="${__true}" || r="${__false}"; }
51
52
ea81a808
JM
53# Number functions
54
55number? () { _number? "${1}" && r="${__true}" || r="${__false}"; }
56
57num_plus () { r=$(( ${ANON["${1}"]} + ${ANON["${2}"]} )); _number "${r}"; }
58num_minus () { r=$(( ${ANON["${1}"]} - ${ANON["${2}"]} )); _number "${r}"; }
59num_multiply () { r=$(( ${ANON["${1}"]} * ${ANON["${2}"]} )); _number "${r}"; }
60num_divide () { r=$(( ${ANON["${1}"]} / ${ANON["${2}"]} )); _number "${r}"; }
61
62_num_bool () { [[ "${1}" = "1" ]] && r="${__true}" || r="${__false}"; }
63num_gt () { r=$(( ${ANON["${1}"]} > ${ANON["${2}"]} )); _num_bool "${r}"; }
64num_gte () { r=$(( ${ANON["${1}"]} >= ${ANON["${2}"]} )); _num_bool "${r}"; }
65num_lt () { r=$(( ${ANON["${1}"]} < ${ANON["${2}"]} )); _num_bool "${r}"; }
66num_lte () { r=$(( ${ANON["${1}"]} <= ${ANON["${2}"]} )); _num_bool "${r}"; }
67
db4c329a
JM
68# return number of milliseconds since epoch
69time_ms () {
70 local ms=$(date +%s%3N)
71 _number "${ms}"
72}
73
ea81a808
JM
74
75# String functions
76
4c77d216 77string? () { _string? "${1}" && ( ! _keyword? "${1}" ) && r="${__true}" || r="${__false}"; }
ea81a808
JM
78
79pr_str () {
80 local res=""
81 for x in "${@}"; do _pr_str "${x}" yes; res="${res} ${r}"; done
82 _string "${res:1}"
83}
84
85str () {
86 local res=""
87 for x in "${@}"; do _pr_str "${x}"; res="${res}${r}"; done
88 _string "${res}"
89}
90
91prn () {
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
98println () {
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
105readline () {
106 READLINE "${ANON["${1}"]}" && _string "${r}" || r="${__nil}"
107}
108
109read_string () {
110 READ_STR "${ANON["${1}"]}"
111}
112
113slurp () {
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
122function? () { _function? "${1}" && [ -z "${ANON["${1}_ismacro_"]}" ] && r="${__true}" || r="${__false}"; }
123macro? () { _function? "${1}" && [ "${ANON["${1}_ismacro_"]}" ] && r="${__true}" || r="${__false}"; }
ea81a808
JM
124
125
126# List functions
127list? () { _list? "${1}" && r="${__true}" || r="${__false}"; }
128
129
130# Vector functions (same as lists for now)
131vector? () { _vector? "${1}" && r="${__true}" || r="${__false}"; }
132
133
134# Hash map (associative array) functions
135hash_map? () { _hash_map? "${1}" && r="${__true}" || r="${__false}"; }
136
137# Return new hash map with keys/values updated
138assoc () {
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
155dissoc () {
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}
184get () {
185 _get "${1}" "${ANON["${2}"]}"
186 [[ "${r}" ]] || r="${__nil}"
187}
188
ea81a808
JM
189contains? () { _contains? "${1}" "${ANON["${2}"]}" && r="${__true}" || r="${__false}"; }
190
191keys () {
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
205vals () {
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
223sequential? () {
224 _sequential? "${1}" && r="${__true}" || r="${__false}"
225}
226
227cons () {
228 _list ${1} ${ANON["${2}"]}
229}
230
231concat () {
232 _list
233 local acc=""
234 for item in "${@}"; do
235 acc="${acc} ${ANON["${item}"]}"
236 done
237 ANON["${r}"]="${acc:1}"
238}
239
240nth () {
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
248empty? () { _empty? "${1}" && r="${__true}" || r="${__false}"; }
249
250count () {
251 _count "${1}"
252 _number "${r}"
253}
254
4c14a8b8
JM
255apply () {
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.
263map () {
264 local f="${ANON["${1}"]}"; shift
265 #echo _map "${f}" "${@}"
266 _map "${f}" "${@}"
267}
268
ea81a808
JM
269conj () {
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
284seq () {
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
318with_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
327meta () {
328 r="${ANON["meta_${1#*_}"]}"
329 [[ "${r}" ]] || r="${__nil}"
330}
331
332
db4c329a 333# Atom functions
ea81a808
JM
334
335atom? () { _atom? "${1}" && r="${__true}" || r="${__false}"; }
336deref () {
337 # TODO: double-check atom type
338 r=${ANON["${1}"]}
339}
340reset_BANG () {
341 local atm="${1}"; shift
342 ANON["${atm}"]="${*}"
343 r="${*}"
344}
345swap_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
356declare -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
424fi