2 # mal (Make a Lisp) object types
5 if [ -z "${__mal_core_included__}" ]; then
6 __mal_core_included
=true
8 source $
(dirname $0)/types.sh
9 source $
(dirname $0)/reader.sh
10 source $
(dirname $0)/printer.sh
28 _equal?
"${1}" "${2}" && r="${__true}" || r="${__false}"
34 nil?
() { _nil?
"${1}" && r="${__true}" || r="${__false}"; }
35 true? () { _true? "${1}" && r="${__true}" || r="${__false}"; }
36 false?
() { _false?
"${1}" && r="${__true}" || r="${__false}"; }
41 symbol () { _symbol "${ANON["${1}"]}"; }
43 symbol? () { _symbol? "${1}" && r="${__true}" || r="${__false}"; }
48 keyword
() { _keyword
"${ANON["${1}"]}"; }
50 keyword?
() { _keyword?
"${1}" && r="${__true}" || r="${__false}"; }
55 number? () { _number? "${1}" && r="${__true}" || r="${__false}"; }
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}"; }
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}"; }
68 # return number of milliseconds since epoch
70 local ms=$(date +%s%3N)
77 string? () { _string? "${1}" && ( ! _keyword? "${1}" ) && r="${__true}" || r="${__false}"; }
81 for x
in "${@}"; do _pr_str "${x}" yes; res="${res} ${r}"; done
87 for x
in "${@}"; do _pr_str "${x}"; res="${res}${r}"; done
93 for x
in "${@}"; do _pr_str "${x}" yes; res="${res} ${r}"; done
100 for x
in "${@}"; do _pr_str "${x}"; res="${res} ${r}"; done
106 READLINE
"${ANON["${1}"]}" && _string "${r}" || r="${__nil}"
110 READ_STR
"${ANON["${1}"]}"
115 mapfile lines
< "${ANON["${1}"]}"
116 local text
="${lines[*]}"; text
=${text//$'\n' /$'\n'}
122 function?
() { _function?
"${1}" && [ -z "${ANON["${1}_ismacro_"]}" ] && r="${__true}" || r="${__false}"; }
123 macro? () { _function? "${1}" && [ "${ANON["${1}_ismacro_"]}" ] && r="${__true}" || r="${__false}"; }
127 list?
() { _list?
"${1}" && r="${__true}" || r="${__false}"; }
130 # Vector functions (same as lists for now)
131 vector? () { _vector? "${1}" && r="${__true}" || r="${__false}"; }
134 # Hash map (associative array) functions
135 hash_map?
() { _hash_map?
"${1}" && r="${__true}" || r="${__false}"; }
137 # Return new hash map with keys/values updated
139 if ! _hash_map? "${1}"; then
140 _error "assoc onto non-hash-map
"
143 _copy_hash_map "${1}"; shift
145 local obj=${ANON["${name}"]}
148 while [[ "${1}" ]]; do
149 eval ${obj}[\"${ANON["${1}"]}\"]=\"${2}\"
156 if ! _hash_map? "${1}"; then
157 _error "dissoc from non-hash-map
"
160 _copy_hash_map "${1}"; shift
162 local obj=${ANON["${name}"]}
165 while [[ "${1}" ]]; do
166 eval unset ${obj}[\"${ANON["${1}"]}\"]
173 _obj_type "${1}"; local ot="${r}"
176 local obj="${ANON["${1}"]}"
177 eval r="\
${${obj}[\"${2}\"]}" ;;
179 _nth "${1}" "${2}" ;;
185 _get "${1}" "${ANON["${2}"]}"
186 [[ "${r}" ]] || r="${__nil}"
189 contains? () { _contains? "${1}" "${ANON["${2}"]}" && r="${__true}" || r="${__false}"; }
192 local obj
="${ANON["${1}"]}"
194 eval local keys
="\${!${obj}[@]}"
197 kstrs
="${kstrs} ${r}"
202 ANON
["${r}"]="${kstrs:1}"
206 local obj
="${ANON["${1}"]}"
209 eval local keys
="\${!${obj}[@]}"
211 eval val
="\${${obj}["\${k}"]}"
212 kvals
="${kvals} ${val}"
217 ANON
["${r}"]="${kvals:1}"
221 # sequence operations
224 _sequential?
"${1}" && r="${__true}" || r="${__false}"
228 _list
${1} ${ANON["${2}"]}
234 for item
in "${@}"; do
235 acc
="${acc} ${ANON["${item}"]}"
237 ANON
["${r}"]="${acc:1}"
241 _nth
"${1}" "${ANON["${2}"]}"
242 if [ -z "${r}" ]; then
243 _error
"nth: index out of bounds"
248 empty?
() { _empty?
"${1}" && r="${__true}" || r="${__false}"; }
256 local f="${ANON["${1}"]}"; shift
257 local items="${@:1:$(( ${#@} -1 ))} ${ANON["${!#}"]}"
258 eval ${f%%@*} ${items}
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.
264 local f="${ANON["${1}"]}"; shift
265 #echo _map "${f}" "${@}"
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}}"
276 ANON["${r}"]="${elem} ${ANON["${r}"]}"
280 ANON["${r}"]="${obj_data:+${obj_data} }${*}"
285 local obj="${1}"; shift
286 local obj_data="${ANON["${obj}"]}"
289 if _list? "${obj}"; then
291 if [ "${r}" -eq 0 ]; then r="${__nil}"; return; fi
293 elif _vector? "${obj}"; then
295 if [ "${r}" -eq 0 ]; then r="${__nil}"; return; fi
298 ANON["${r}"]="${obj_data}"
299 elif _string? "${obj}"; then
300 if [ "${#obj_data}" -eq 0 ]; then r="${__nil}"; return; fi
302 for (( i=0; i < ${#obj_data}; i++ )); do
303 _string "${obj_data:$i:1}"
307 ANON["${r}"]="${acc:1}"
308 elif _nil? "${obj}"; then
311 throw "seq: called on non-sequence
"
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}"
328 r="${ANON["meta_${1#*_}"]}"
329 [[ "${r}" ]] || r="${__nil}"
335 atom? () { _atom? "${1}" && r="${__true}" || r="${__false}"; }
337 # TODO: double-check atom type
341 local atm
="${1}"; shift
342 ANON
["${atm}"]="${*}"
346 local atm
="${1}"; shift
347 local f
="${ANON["${1}"]}"; shift
348 ${f%%@*} "${ANON["${atm}"]}" "${@}"
349 ANON["${atm}"]="${r}"
354 # Namespace of core functions
377 [read-string]=read_string
385 [__STAR__]=num_multiply
398 [contains?]=contains?
402 [sequential?]=sequential?
416 [with-meta]=with_meta