aboutsummaryrefslogtreecommitdiff
path: root/bash/types.sh
diff options
context:
space:
mode:
Diffstat (limited to 'bash/types.sh')
-rw-r--r--bash/types.sh610
1 files changed, 81 insertions, 529 deletions
diff --git a/bash/types.sh b/bash/types.sh
index 33278da..8e256be 100644
--- a/bash/types.sh
+++ b/bash/types.sh
@@ -1,7 +1,10 @@
#
-# mal: Object Types and Functions
+# mal (Make a Lisp) object types
#
+if [ -z "${__mal_types_included__}" ]; then
+__mal_types_included=true
+
declare -A ANON
__obj_magic=__5bal7
@@ -22,7 +25,16 @@ __new_obj_like () {
r="${1%_*}_${r}"
}
+
+# Errors/Exceptions
+
__ERROR=
+_error() {
+ _string "${1}"
+ __ERROR="${r}"
+ r=
+}
+
#
@@ -50,162 +62,77 @@ _obj_type () {
esac
}
-obj_type () {
- _obj_type "${1}"
- string "${r}"
-}
-
-_pr_str () {
- local print_readably="${2}"
- _obj_type "${1}"; local ot="${r}"
- if [[ -z "${ot}" ]]; then
- _error "_pr_str failed on '${1}'"
- r="<${1}>"
- else
- eval ${ot}_pr_str "${1}" "${print_readably}"
+_equal? () {
+ _obj_type "${1}"; local ot1="${r}"
+ _obj_type "${2}"; local ot2="${r}"
+ if [[ "${ot1}" != "${ot2}" ]]; then
+ if ! _sequential? "${1}" || ! _sequential? "${2}"; then
+ return 1
+ fi
fi
+ case "${ot1}" in
+ string|symbol|number)
+ [[ "${ANON["${1}"]}" == "${ANON["${2}"]}" ]] ;;
+ list|vector|hash_map)
+ _count "${1}"; local sz1="${r}"
+ _count "${2}"; local sz2="${r}"
+ [[ "${sz1}" == "${sz2}" ]] || return 1
+ local a1=(${ANON["${1}"]})
+ local a2=(${ANON["${2}"]})
+ for ((i=0;i<${#a1[*]};i++)); do
+ _equal? "${a1[${i}]}" "${a2[${i}]}" || return 1
+ done
+ ;;
+ *)
+ [[ "${1}" == "${2}" ]] ;;
+ esac
}
-pr_str () {
- local res=""
- for x in "${@}"; do _pr_str "${x}" yes; res="${res} ${r}"; done
- string "${res:1}"
-}
-
-str () {
- local res=""
- for x in "${@}"; do _pr_str "${x}"; res="${res}${r}"; done
- string "${res}"
-}
-
-prn () {
- local res=""
- for x in "${@}"; do _pr_str "${x}" yes; res="${res} ${r}"; done
- echo "${res:1}"
- r="${__nil}";
-}
-
-println () {
- local res=""
- for x in "${@}"; do _pr_str "${x}"; res="${res} ${r}"; done
- res="${res//\\n/$'\n'}"
- echo -e "${res:1}"
- r="${__nil}";
-}
-
-with_meta () {
- local obj="${1}"; shift
- local meta_data="${1}"; shift
- __new_obj_like "${obj}"
- ANON["${r}"]="${ANON["${obj}"]}"
- local meta_obj="meta_${r#*_}"
- ANON["${meta_obj}"]="${meta_data}"
-}
-
-meta () {
- r="${ANON["meta_${1#*_}"]}"
- [[ "${r}" ]] || r="${__nil}"
-}
-
-#
# Constant atomic values
-#
-__undefined=undf_0
__nil=_nil_0
__true=true_0
__false=fals_0
-_undefined? () { [[ ${1} =~ ^undf_ ]]; }
-undefined? () { _undefined? "${1}" && r="${__true}" || r="${__false}"; }
-
_nil? () { [[ ${1} =~ ^_nil_ ]]; }
-nil? () { _nil? "${1}" && r="${__true}" || r="${__false}"; }
-nil_pr_str () { r="nil"; }
-
_true? () { [[ ${1} =~ ^true_ ]]; }
-true? () { _true? "${1}" && r="${__true}" || r="${__false}"; }
-true_pr_str () { r="true"; }
-
_false? () { [[ ${1} =~ ^fals_ ]]; }
-false? () { _false? "${1}" && r="${__true}" || r="${__false}"; }
-false_pr_str () { r="false"; }
-
-
-#
-# Numbers
-#
-number () {
- __new_obj_hash_code
- r="numb_${r}"
- ANON["${r}"]="${1}"
-}
-_number? () { [[ ${1} =~ ^numb_ ]]; }
-number? () { _number? "${1}" && r="${__true}" || r="${__false}"; }
-number_pr_str () { r="${ANON["${1}"]}"; }
-
-num_plus () { r=$(( ${ANON["${1}"]} + ${ANON["${2}"]} )); number "${r}"; }
-num_minus () { r=$(( ${ANON["${1}"]} - ${ANON["${2}"]} )); number "${r}"; }
-num_multiply () { r=$(( ${ANON["${1}"]} * ${ANON["${2}"]} )); number "${r}"; }
-num_divide () { r=$(( ${ANON["${1}"]} / ${ANON["${2}"]} )); number "${r}"; }
-_num_bool () { [[ "${1}" = "1" ]] && r="${__true}" || r="${__false}"; }
-num_gt () { r=$(( ${ANON["${1}"]} > ${ANON["${2}"]} )); _num_bool "${r}"; }
-num_gte () { r=$(( ${ANON["${1}"]} >= ${ANON["${2}"]} )); _num_bool "${r}"; }
-num_lt () { r=$(( ${ANON["${1}"]} < ${ANON["${2}"]} )); _num_bool "${r}"; }
-num_lte () { r=$(( ${ANON["${1}"]} <= ${ANON["${2}"]} )); _num_bool "${r}"; }
-
-#
# Symbols
-#
-symbol () {
+_symbol () {
__new_obj_hash_code
r="symb_${r}"
ANON["${r}"]="${1//$'\*'/__STAR__}"
}
_symbol? () { [[ ${1} =~ ^symb_ ]]; }
-symbol? () { _symbol? "${1}" && r="${__true}" || r="${__false}"; }
-symbol_pr_str () {
- r="${ANON["${1}"]}"
- r="${r//__STAR__/*}"
+
+
+# Numbers
+
+_number () {
+ __new_obj_hash_code
+ r="numb_${r}"
+ ANON["${r}"]="${1}"
}
+_number? () { [[ ${1} =~ ^numb_ ]]; }
-#
# Strings
-#
-string () {
+_string () {
__new_obj_hash_code
r="strn_${r}"
ANON["${r}"]="${1//$'\*'/__STAR__}"
}
_string? () { [[ ${1} =~ ^strn_ ]]; }
-string? () { _string? "${1}" && r="${__true}" || r="${__false}"; }
-string_pr_str () {
- local print_readably="${2}"
- if [ "${print_readably}" == "yes" ]; then
- local s="${ANON["${1}"]}"
- s="${s//\\/\\\\}"
- r="\"${s//\"/\\\"}\""
- else
- r="${ANON["${1}"]}"
- fi
- r="${r//__STAR__/$'*'}"
-}
-
-# TODO: subs
-#
-# Function objects
-#
-
+# Functions
# Return a function object. The first parameter is the
# function 'source'.
-new_function () {
+_function () {
__new_obj_hash_code
eval "function ${__obj_magic}_func_${r} () { ${1%;} ; }"
r="func_${r}"
@@ -218,15 +145,31 @@ new_function () {
fi
}
_function? () { [[ ${1} =~ ^func_ ]]; }
-function? () { _function? "${1}" && r="${__true}" || r="${__false}"; }
-function_pr_str () { r="${ANON["${1}"]}"; }
-#
+# Lists
+
+_list () {
+ __new_obj_hash_code
+ r="list_${r}"
+ ANON["${r}"]="${*}"
+}
+_list? () { [[ ${1} =~ ^list_ ]]; }
+
+
+# Vectors
+
+_vector () {
+ __new_obj_hash_code
+ r="vector_${r}"
+ ANON["${r}"]="${*}"
+}
+_vector? () { [[ ${1} =~ ^vector_ ]]; }
+
+
# hash maps (associative arrays)
-#
-hash_map () {
+_hash_map () {
__new_obj_hash_code
local name="hmap_${r}"
local obj="${__obj_magic}_${name}"
@@ -241,26 +184,10 @@ hash_map () {
r="${name}"
}
_hash_map? () { [[ ${1} =~ ^hmap_ ]]; }
-hash_map? () { _hash_map? "${1}" && r="${__true}" || r="${__false}"; }
-
-hash_map_pr_str () {
- local print_readably="${2}"
- local res=""; local val=""
- local hm="${ANON["${1}"]}"
- eval local keys="\${!${hm}[@]}"
- for key in ${keys}; do
- #res="${res} \"${ANON["${key}"]}\""
- res="${res} \"${key//__STAR__/$'*'}\""
- eval val="\${${hm}[\"${key}\"]}"
- _pr_str "${val}" "${print_readably}"
- res="${res} ${r}"
- done
- r="{${res:1}}"
-}
_copy_hash_map () {
local orig_obj="${ANON["${1}"]}"
- hash_map
+ _hash_map
local name="${r}"
local obj="${ANON["${name}"]}"
@@ -271,7 +198,7 @@ _copy_hash_map () {
}
# Return same hash map with keys/values added/mutated in place
-assoc! () {
+_assoc! () {
local obj=${ANON["${1}"]}; shift
declare -A -g ${obj}
@@ -283,7 +210,7 @@ assoc! () {
}
# Return same hash map with keys/values deleted/mutated in place
-dissoc! () {
+_dissoc! () {
local obj=${ANON["${1}"]}; shift
declare -A -g ${obj}
@@ -294,241 +221,32 @@ dissoc! () {
done
}
-# Return new hash map with keys/values updated
-assoc () {
- if ! _hash_map? "${1}"; then
- _error "assoc onto non-hash-map"
- return
- fi
- _copy_hash_map "${1}"; shift
- local name="${r}"
- local obj=${ANON["${name}"]}
- declare -A -g ${obj}
-
- while [[ "${1}" ]]; do
- eval ${obj}[\"${ANON["${1}"]}\"]=\"${2}\"
- shift; shift
- done
- r="${name}"
-}
-
-dissoc () {
- if ! _hash_map? "${1}"; then
- _error "dissoc from non-hash-map"
- return
- fi
- _copy_hash_map "${1}"; shift
- local name="${r}"
- local obj=${ANON["${name}"]}
- declare -A -g ${obj}
-
- while [[ "${1}" ]]; do
- eval unset ${obj}[\"${ANON["${1}"]}\"]
- shift
- done
- r="${name}"
-}
-
-_get () {
- _obj_type "${1}"; local ot="${r}"
- case "${ot}" in
- hash_map)
- local obj="${ANON["${1}"]}"
- eval r="\${${obj}[\"${2}\"]}" ;;
- list|vector)
- _nth "${1}" "${2}"
- esac
-}
-get () {
- _get "${1}" "${ANON["${2}"]}"
- [[ "${r}" ]] || r="${__nil}"
-}
-
-_contains? () {
- local obj="${ANON["${1}"]}"
- #echo "_contains? ${1} ${2} -> \${${obj}[\"${2}\"]+isset}"
- eval [[ "\${${obj}[\"${2}\"]+isset}" ]]
-}
-contains? () { _contains? "${1}" "${ANON["${2}"]}" && r="${__true}" || r="${__false}"; }
-
-keys () {
- local obj="${ANON["${1}"]}"
- local kstrs=
- eval local keys="\${!${obj}[@]}"
- for k in ${keys}; do
- string "${k}"
- kstrs="${kstrs} ${r}"
- done
-
- __new_obj_hash_code
- r="list_${r}"
- ANON["${r}"]="${kstrs:1}"
-}
-
-vals () {
- local obj="${ANON["${1}"]}"
- local kvals=
- local val=
- eval local keys="\${!${obj}[@]}"
- for k in ${keys}; do
- eval val="\${${obj}["\${k}"]}"
- kvals="${kvals} ${val}"
- done
- __new_obj_hash_code
- r="list_${r}"
- ANON["${r}"]="${kvals:1}"
-}
+# Atoms
-#
-# Exceptions/Errors
-#
-
-_error() {
- string "${1}"
- __ERROR="${r}"
- r=
-}
-throw() {
- __ERROR="${1}"
- r=
-}
-
-#
-# vectors
-#
-
-#
-# vector (same as lists for now)
-#
-
-vector () {
- __new_obj_hash_code
- r="vector_${r}"
- ANON["${r}"]="${*}"
-}
-_vector? () { [[ ${1} =~ ^vector_ ]]; }
-vector? () { _vector? "${1}" && r="${__true}" || r="${__false}"; }
-
-vector_pr_str () {
- local print_readably="${2}"
- local res=""
- for elem in ${ANON["${1}"]}; do
- _pr_str "${elem}" "${print_readably}"
- res="${res} ${r}"
- done
- r="[${res:1}]"
-}
-
-
-#
-# list (same as vectors for now)
-#
-
-list () {
- __new_obj_hash_code
- r="list_${r}"
- ANON["${r}"]="${*}"
-}
-_list? () { [[ ${1} =~ ^list_ ]]; }
-list? () { _list? "${1}" && r="${__true}" || r="${__false}"; }
-
-list_pr_str () {
- local print_readably="${2}"
- local res=""
- for elem in ${ANON["${1}"]}; do
- _pr_str "${elem}" "${print_readably}"
- res="${res} ${r}"
- done
- r="(${res:1})"
-}
-
-cons () {
- list ${1} ${ANON["${2}"]}
-}
-
-
-#
-# atoms
-#
-atom() {
+_atom() {
__new_obj_hash_code
r="atom_${r}"
ANON["${r}"]="${*}"
}
_atom? () { [[ ${1} =~ ^atom_ ]]; }
-atom? () { _atom? "${1}" && r="${__true}" || r="${__false}"; }
-atom_pr_str () {
- local print_readably="${2}"
- _pr_str "${ANON["${1}"]}" "${print_readably}"
- r="(atom ${r})";
-}
-deref () {
- # TODO: double-check atom type
- r=${ANON["${1}"]}
-}
-reset_BANG () {
- local atm="${1}"; shift
- ANON["${atm}"]="${*}"
- r="${*}"
-}
-swap_BANG () {
- local atm="${1}"; shift
- local f="${ANON["${1}"]}"; shift
- ${f%%@*} "${ANON["${atm}"]}" "${@}"
- ANON["${atm}"]="${r}"
-}
-#
# sequence operations
-#
_sequential? () {
_list? "${1}" || _vector? "${1}"
}
-sequential? () {
- _sequential? "${1}" && r="${__true}" || r="${__false}"
-}
_nth () {
local temp=(${ANON["${1}"]})
r=${temp[${2}]}
}
-nth () {
- _nth "${1}" "${ANON["${2}"]}"
-}
-
_empty? () { [[ -z "${ANON["${1}"]}" ]]; }
-empty? () { _empty? "${1}" && r="${__true}" || r="${__false}"; }
-
-concat () {
- list
- local acc=""
- for item in "${@}"; do
- acc="${acc} ${ANON["${item}"]}"
- done
- ANON["${r}"]="${acc:1}"
-}
-
-conj () {
- local obj="${1}"; shift
- local obj_data="${ANON["${obj}"]}"
- __new_obj_like "${obj}"
- if _list? "${obj}"; then
- ANON["${r}"]="${obj_data:+${obj_data}}"
- for elem in ${@}; do
- ANON["${r}"]="${elem} ${ANON["${r}"]}"
- done
-
- else
- ANON["${r}"]="${obj_data:+${obj_data} }${*}"
- fi
-}
# conj that mutates in place (and always appends)
-conj! () {
+_conj! () {
local obj="${1}"; shift
local obj_data="${ANON["${obj}"]}"
ANON["${obj}"]="${obj_data:+${obj_data} }${*}"
@@ -541,21 +259,6 @@ _count () {
local temp=(${ANON["${1}"]})
r=${#temp[*]}
}
-count () {
- _count "${1}"
- number "${r}"
-}
-
-first () {
- local temp="${ANON["${1}"]}"
- r="${temp%% *}"
- [ "${r}" ] || r="${__nil}"
-}
-
-last () {
- local temp="${ANON["${1}"]}"
- r="${temp##* }"
-}
# Slice a sequence object $1 starting at $2 of length $3
_slice () {
@@ -564,175 +267,24 @@ _slice () {
ANON["${r}"]="${temp[@]:${2}:${3}}"
}
-# Creates a new vector/list of the everything after but the first
-# element
-rest () {
- local temp="${ANON["${1}"]}"
- __new_obj list
- if [[ "${temp#* }" == "${temp}" ]]; then
- ANON["${r}"]=
- else
- ANON["${r}"]="${temp#* }"
- fi
-}
-
-apply () {
- local f="${ANON["${1}"]}"; shift
- local items="${@:1:$(( ${#@} -1 ))} ${ANON["${!#}"]}"
- eval ${f%%@*} ${items}
-}
-
# Takes a bash function and an list object and invokes the function on
# each element of the list, returning a new list (or vector) of the results.
_map_with_type () {
- local ot="${1}"; shift
+ local constructor="${1}"; shift
local f="${1}"; shift
local items="${ANON["${1}"]}"; shift
- eval "${ot}"; local new_seq="${r}"
+ eval "${constructor}"; local new_seq="${r}"
for v in ${items}; do
#echo eval ${f%%@*} "${v}" "${@}"
eval ${f%%@*} "${v}" "${@}"
[[ "${__ERROR}" ]] && r= && return 1
- conj! "${new_seq}" "${r}"
+ _conj! "${new_seq}" "${r}"
done
r="${new_seq}"
}
_map () {
- _map_with_type list "${@}"
-}
-
-# Takes a function object and an list object and invokes the function
-# on each element of the list, returning a new list of the results.
-map () {
- local f="${ANON["${1}"]}"; shift
- #echo _map "${f}" "${@}"
- _map "${f}" "${@}"
-}
-
-_equal? () {
- _obj_type "${1}"; local ot1="${r}"
- _obj_type "${2}"; local ot2="${r}"
- if [[ "${ot1}" != "${ot2}" ]]; then
- if ! _sequential? "${1}" || ! _sequential? "${2}"; then
- return 1
- fi
- fi
- case "${ot1}" in
- string|symbol|number)
- [[ "${ANON["${1}"]}" == "${ANON["${2}"]}" ]] ;;
- list|vector|hash_map)
- _count "${1}"; local sz1="${r}"
- _count "${2}"; local sz2="${r}"
- [[ "${sz1}" == "${sz2}" ]] || return 1
- local a1=(${ANON["${1}"]})
- local a2=(${ANON["${2}"]})
- for ((i=0;i<${#a1[*]};i++)); do
- _equal? "${a1[${i}]}" "${a2[${i}]}" || return 1
- done
- ;;
- *)
- [[ "${1}" == "${2}" ]] ;;
- esac
-}
-equal? () {
- _equal? "${1}" "${2}" && r="${__true}" || r="${__false}"
-}
-
-#
-# ENV
-#
-
-# Any environment is a hash_map with an __outer__ key that refers to
-# a parent environment (or nil)
-ENV () {
- r=
- hash_map
- local env="${r}"
- if [[ "${1}" ]]; then
- outer="${1}"; shift
- assoc! "${env}" "__outer__" "${outer}"
- else
- assoc! "${env}" "__outer__" "${__nil}"
- fi
- r="${env}"
-
- if [[ "${1}" && "${@}" ]]; then
- local binds=(${ANON["${1}"]}); shift
- local idx=0
- while [[ "${binds["${idx}"]}" ]]; do
- local fp="${ANON["${binds["${idx}"]}"]}"
- if [[ "${fp}" == "&" ]]; then
- idx=$(( idx + 1 ))
- fp="${ANON["${binds["${idx}"]}"]}"
- list "${@}"
- assoc! "${env}" "${fp}" "${r}"
- break
- else
- assoc! "${env}" "${fp}" "${1}"
- shift
- idx=$(( idx + 1 ))
- fi
- done
- fi
- r="${env}"
-}
-
-# Find the environment with the key set and return the environment
-ENV_FIND () {
- if _contains? "${1}" "${2}"; then
- r="${1}"
- else
- local obj="${ANON["${1}"]}"
- eval local outer="\${${obj}["__outer__"]}"
- if [[ "${outer}" && "${outer}" != "${__nil}" ]]; then
- ENV_FIND "${outer}" "${2}"
- else
- r=
- fi
- fi
-}
-
-# Find the environment with the key set and return the value of the
-# key in that environment. If no environment contains the key then
-# return an error
-ENV_GET () {
- ENV_FIND "${1}" "${2}"
- local env="${r}"
- if [[ "${r}" ]]; then
- local obj="${ANON["${env}"]}"
- eval r="\${${obj}["${2}"]}"
- else
- _error "'${2}' not found"
- fi
-}
-
-ENV_SET () {
- assoc! "${1}" "${2}" "${3}"
+ _map_with_type _list "${@}"
}
-# TODO: memory visualizer (like Make implementation)
-
-# Namespace of type functions
-
-declare -A types_ns=(
- [type]=obj_type
- [pr-str]=pr_str [str]=str [prn]=prn [println]=println
- [with-meta]=with_meta [meta]=meta
- [=]=equal?
- [nil?]=nil? [true?]=true? [false?]=false?
- [symbol?]=symbol?
- [>]=num_gt [>=]=num_gte [<]=num_lt [<=]=num_lte
- [+]=num_plus [-]=num_minus [__STAR__]=num_multiply [/]=num_divide
- [hash-map]=hash_map [map?]=hash_map?
- [assoc]=assoc [dissoc]=dissoc [get]=get
- [contains?]=contains? [keys]=keys [vals]=vals
- [throw]=throw
- [list]=list [list?]=list?
- [vector]=vector [vector?]=vector?
- [atom]=atom [atom?]=atom? [deref]=deref
- [reset!]=reset_BANG [swap!]=swap_BANG
- [sequential?]=sequential?
- [cons]=cons [nth]=nth [count]=count [empty?]=empty?
- [concat]=concat [conj]=conj [first]=first [rest]=rest
- [apply]=apply [map]=map)
+fi