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