diff options
Diffstat (limited to 'bash/types.sh')
| -rw-r--r-- | bash/types.sh | 610 |
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 |
