Mal space cleanup. Ruby Makefile. TODO updates.
[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
9source $(dirname $0)/printer.sh
10
11# Exceptions/Errors
12
13throw() {
14 __ERROR="${1}"
15 r=
16}
17
18
19# General functions
20
21obj_type () {
22 _obj_type "${1}"
23 _string "${r}"
24}
25
26equal? () {
27 _equal? "${1}" "${2}" && r="${__true}" || r="${__false}"
28}
29
30
31# Scalar functions
32
33nil? () { _nil? "${1}" && r="${__true}" || r="${__false}"; }
34true? () { _true? "${1}" && r="${__true}" || r="${__false}"; }
35false? () { _false? "${1}" && r="${__true}" || r="${__false}"; }
36
37
38# Symbol functions
39
40symbol? () { _symbol? "${1}" && r="${__true}" || r="${__false}"; }
41
42
43# Number functions
44
45number? () { _number? "${1}" && r="${__true}" || r="${__false}"; }
46
47num_plus () { r=$(( ${ANON["${1}"]} + ${ANON["${2}"]} )); _number "${r}"; }
48num_minus () { r=$(( ${ANON["${1}"]} - ${ANON["${2}"]} )); _number "${r}"; }
49num_multiply () { r=$(( ${ANON["${1}"]} * ${ANON["${2}"]} )); _number "${r}"; }
50num_divide () { r=$(( ${ANON["${1}"]} / ${ANON["${2}"]} )); _number "${r}"; }
51
52_num_bool () { [[ "${1}" = "1" ]] && r="${__true}" || r="${__false}"; }
53num_gt () { r=$(( ${ANON["${1}"]} > ${ANON["${2}"]} )); _num_bool "${r}"; }
54num_gte () { r=$(( ${ANON["${1}"]} >= ${ANON["${2}"]} )); _num_bool "${r}"; }
55num_lt () { r=$(( ${ANON["${1}"]} < ${ANON["${2}"]} )); _num_bool "${r}"; }
56num_lte () { r=$(( ${ANON["${1}"]} <= ${ANON["${2}"]} )); _num_bool "${r}"; }
57
58
59# String functions
60
61string? () { _string? "${1}" && r="${__true}" || r="${__false}"; }
62
63pr_str () {
64 local res=""
65 for x in "${@}"; do _pr_str "${x}" yes; res="${res} ${r}"; done
66 _string "${res:1}"
67}
68
69str () {
70 local res=""
71 for x in "${@}"; do _pr_str "${x}"; res="${res}${r}"; done
72 _string "${res}"
73}
74
75prn () {
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
82println () {
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
92function? () { _function? "${1}" && r="${__true}" || r="${__false}"; }
93
94
95# List functions
96list? () { _list? "${1}" && r="${__true}" || r="${__false}"; }
97
98
99# Vector functions (same as lists for now)
100vector? () { _vector? "${1}" && r="${__true}" || r="${__false}"; }
101
102
103# Hash map (associative array) functions
104hash_map? () { _hash_map? "${1}" && r="${__true}" || r="${__false}"; }
105
106# Return new hash map with keys/values updated
107assoc () {
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
124dissoc () {
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}
151get () {
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}
161contains? () { _contains? "${1}" "${ANON["${2}"]}" && r="${__true}" || r="${__false}"; }
162
163keys () {
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
177vals () {
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
195sequential? () {
196 _sequential? "${1}" && r="${__true}" || r="${__false}"
197}
198
199cons () {
200 _list ${1} ${ANON["${2}"]}
201}
202
203concat () {
204 _list
205 local acc=""
206 for item in "${@}"; do
207 acc="${acc} ${ANON["${item}"]}"
208 done
209 ANON["${r}"]="${acc:1}"
210}
211
212nth () {
213 _nth "${1}" "${ANON["${2}"]}"
214}
215
216first () {
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
224rest () {
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
234last () {
235 local temp="${ANON["${1}"]}"
236 r="${temp##* }"
237}
238
239empty? () { _empty? "${1}" && r="${__true}" || r="${__false}"; }
240
241count () {
242 _count "${1}"
243 _number "${r}"
244}
245
246conj () {
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
261apply () {
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.
269map () {
270 local f="${ANON["${1}"]}"; shift
271 #echo _map "${f}" "${@}"
272 _map "${f}" "${@}"
273}
274
275
276# Metadata functions
277
278with_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
287meta () {
288 r="${ANON["meta_${1#*_}"]}"
289 [[ "${r}" ]] || r="${__nil}"
290}
291
292
293# atoms
294
295atom? () { _atom? "${1}" && r="${__true}" || r="${__false}"; }
296deref () {
297 # TODO: double-check atom type
298 r=${ANON["${1}"]}
299}
300reset_BANG () {
301 local atm="${1}"; shift
302 ANON["${atm}"]="${*}"
303 r="${*}"
304}
305swap_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
316declare -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
370fi