diff options
Diffstat (limited to 'bash')
| -rw-r--r-- | bash/Makefile | 2 | ||||
| -rw-r--r-- | bash/core.sh | 370 | ||||
| -rw-r--r-- | bash/env.sh | 78 | ||||
| -rw-r--r-- | bash/printer.sh | 87 | ||||
| -rw-r--r-- | bash/reader.sh | 41 | ||||
| -rwxr-xr-x | bash/step1_read_print.sh | 1 | ||||
| -rwxr-xr-x | bash/step2_eval.sh | 10 | ||||
| -rwxr-xr-x | bash/step3_env.sh | 11 | ||||
| -rwxr-xr-x | bash/step4_if_fn_do.sh | 19 | ||||
| -rwxr-xr-x | bash/step5_tco.sh | 21 | ||||
| -rwxr-xr-x | bash/step6_file.sh | 23 | ||||
| -rwxr-xr-x | bash/step7_quote.sh | 37 | ||||
| -rwxr-xr-x | bash/step8_macros.sh | 40 | ||||
| -rwxr-xr-x | bash/step9_interop.sh | 42 | ||||
| -rwxr-xr-x | bash/stepA_more.sh | 46 | ||||
| -rw-r--r-- | bash/tests/types.sh | 6 | ||||
| -rw-r--r-- | bash/types.sh | 610 |
17 files changed, 780 insertions, 664 deletions
diff --git a/bash/Makefile b/bash/Makefile index 53f0d09..65694ee 100644 --- a/bash/Makefile +++ b/bash/Makefile @@ -1,6 +1,6 @@ TESTS = tests/types.sh tests/reader.sh -SOURCES = types.sh reader.sh stepA_more.sh +SOURCES = types.sh reader.sh printer.sh env.sh core.sh stepA_more.sh all: mal.sh diff --git a/bash/core.sh b/bash/core.sh new file mode 100644 index 0000000..0da820c --- /dev/null +++ b/bash/core.sh @@ -0,0 +1,370 @@ +# +# mal (Make a Lisp) object types +# + +if [ -z "${__mal_core_included__}" ]; then +__mal_core_included=true + +source $(dirname $0)/types.sh +source $(dirname $0)/printer.sh + +# Exceptions/Errors + +throw() { + __ERROR="${1}" + r= +} + + +# General functions + +obj_type () { + _obj_type "${1}" + _string "${r}" +} + +equal? () { + _equal? "${1}" "${2}" && r="${__true}" || r="${__false}" +} + + +# Scalar functions + +nil? () { _nil? "${1}" && r="${__true}" || r="${__false}"; } +true? () { _true? "${1}" && r="${__true}" || r="${__false}"; } +false? () { _false? "${1}" && r="${__true}" || r="${__false}"; } + + +# Symbol functions + +symbol? () { _symbol? "${1}" && r="${__true}" || r="${__false}"; } + + +# Number functions + +number? () { _number? "${1}" && r="${__true}" || r="${__false}"; } + +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}"; } + + +# String functions + +string? () { _string? "${1}" && r="${__true}" || r="${__false}"; } + +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}"; +} + + +# Function functions +function? () { _function? "${1}" && r="${__true}" || r="${__false}"; } + + +# List functions +list? () { _list? "${1}" && r="${__true}" || r="${__false}"; } + + +# Vector functions (same as lists for now) +vector? () { _vector? "${1}" && r="${__true}" || r="${__false}"; } + + +# Hash map (associative array) functions +hash_map? () { _hash_map? "${1}" && r="${__true}" || r="${__false}"; } + +# 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}" +} + + +# sequence operations + +sequential? () { + _sequential? "${1}" && r="${__true}" || r="${__false}" +} + +cons () { + _list ${1} ${ANON["${2}"]} +} + +concat () { + _list + local acc="" + for item in "${@}"; do + acc="${acc} ${ANON["${item}"]}" + done + ANON["${r}"]="${acc:1}" +} + +nth () { + _nth "${1}" "${ANON["${2}"]}" +} + +first () { + local temp="${ANON["${1}"]}" + r="${temp%% *}" + [ "${r}" ] || r="${__nil}" +} + +# Creates a new vector/list of the everything after but the first +# element +rest () { + local temp="${ANON["${1}"]}" + _list + if [[ "${temp#* }" == "${temp}" ]]; then + ANON["${r}"]= + else + ANON["${r}"]="${temp#* }" + fi +} + +last () { + local temp="${ANON["${1}"]}" + r="${temp##* }" +} + +empty? () { _empty? "${1}" && r="${__true}" || r="${__false}"; } + +count () { + _count "${1}" + _number "${r}" +} + +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 +} + +apply () { + local f="${ANON["${1}"]}"; shift + local items="${@:1:$(( ${#@} -1 ))} ${ANON["${!#}"]}" + eval ${f%%@*} ${items} +} + +# 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}" "${@}" +} + + +# Metadata functions + +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}" +} + + +# atoms + +atom? () { _atom? "${1}" && r="${__true}" || r="${__false}"; } +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}" +} + + + +# Namespace of core functions + +declare -A core_ns=( + [type]=obj_type + [=]=equal? + [throw]=throw + [nil?]=nil? + [true?]=true? + [false?]=false? + [symbol?]=symbol? + [pr-str]=pr_str + [str]=str + [prn]=prn + [println]=println + [<]=num_lt + [<=]=num_lte + [>]=num_gt + [>=]=num_gte + [+]=num_plus + [-]=num_minus + [__STAR__]=num_multiply + [/]=num_divide + + [list]=_list + [list?]=list? + [vector]=_vector + [vector?]=vector? + [hash-map]=_hash_map + [map?]=hash_map? + [assoc]=assoc + [dissoc]=dissoc + [get]=get + [contains?]=contains? + [keys]=keys + [vals]=vals + + [sequential?]=sequential? + [cons]=cons + [concat]=concat + [nth]=nth + [first]=first + [rest]=rest + [empty?]=empty? + [count]=count + [conj]=conj + [apply]=apply + [map]=map + + [with-meta]=with_meta + [meta]=meta + [atom]=_atom + [atom?]=atom? + [deref]=deref + [reset!]=reset_BANG + [swap!]=swap_BANG) + +fi diff --git a/bash/env.sh b/bash/env.sh new file mode 100644 index 0000000..2eabe8b --- /dev/null +++ b/bash/env.sh @@ -0,0 +1,78 @@ +# +# mal (Make a Lisp) environment definition +# + +if [ -z "${__mal_env_included__}" ]; then +__mal_env_included=true + +source $(dirname $0)/types.sh + +# 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}" +} + +fi diff --git a/bash/printer.sh b/bash/printer.sh new file mode 100644 index 0000000..911db17 --- /dev/null +++ b/bash/printer.sh @@ -0,0 +1,87 @@ +# +# mal (Make a Lisp) printer +# + +if [ -z "${__mal_printer_included__}" ]; then +__mal_printer_included=true + +source $(dirname $0)/types.sh + +_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}" + fi +} + +nil_pr_str () { r="nil"; } +true_pr_str () { r="true"; } +false_pr_str () { r="false"; } + +number_pr_str () { r="${ANON["${1}"]}"; } + +symbol_pr_str () { + r="${ANON["${1}"]}" + r="${r//__STAR__/*}" +} + +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__/$'*'}" +} + +function_pr_str () { r="${ANON["${1}"]}"; } + +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}}" +} + +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_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})" +} + +atom_pr_str () { + local print_readably="${2}" + _pr_str "${ANON["${1}"]}" "${print_readably}" + r="(atom ${r})"; +} + +fi diff --git a/bash/reader.sh b/bash/reader.sh index bc32fa7..585b152 100644 --- a/bash/reader.sh +++ b/bash/reader.sh @@ -2,20 +2,23 @@ # mal (Make Lisp) Parser/Reader # +if [ -z "${__mal_readerr_included__}" ]; then +__mal_readerr_included=true + source $(dirname $0)/types.sh READ_ATOM () { local token=${__reader_tokens[${__reader_idx}]} __reader_idx=$(( __reader_idx + 1 )) case "${token}" in - [0-9]*) number "${token}" ;; + [0-9]*) _number "${token}" ;; \"*) token="${token:1:-1}" token="${token//\\\"/\"}" - string "${token}" ;; + _string "${token}" ;; nil) r="${__nil}" ;; true) r="${__true}" ;; false) r="${__false}" ;; - *) symbol "${token}" ;; + *) _symbol "${token}" ;; esac } @@ -54,39 +57,39 @@ READ_FORM () { local token=${__reader_tokens[${__reader_idx}]} case "${token}" in \') __reader_idx=$(( __reader_idx + 1 )) - symbol quote; local q="${r}" + _symbol quote; local q="${r}" READ_FORM; local f="${r}" - list "${q}" "${f}" ;; + _list "${q}" "${f}" ;; \`) __reader_idx=$(( __reader_idx + 1 )) - symbol quasiquote; local q="${r}" + _symbol quasiquote; local q="${r}" READ_FORM; local f="${r}" - list "${q}" "${f}" ;; + _list "${q}" "${f}" ;; \~) __reader_idx=$(( __reader_idx + 1 )) - symbol unquote; local q="${r}" + _symbol unquote; local q="${r}" READ_FORM; local f="${r}" - list "${q}" "${f}" ;; + _list "${q}" "${f}" ;; \~\@) __reader_idx=$(( __reader_idx + 1 )) - symbol splice-unquote; local q="${r}" + _symbol splice-unquote; local q="${r}" READ_FORM; local f="${r}" - list "${q}" "${f}" ;; + _list "${q}" "${f}" ;; ^) __reader_idx=$(( __reader_idx + 1 )) - symbol with-meta; local wm="${r}" + _symbol with-meta; local wm="${r}" READ_FORM; local meta="${r}" READ_FORM; local obj="${r}" - list "${wm}" "${obj}" "${meta}" ;; + _list "${wm}" "${obj}" "${meta}" ;; @) __reader_idx=$(( __reader_idx + 1 )) - symbol deref; local d="${r}" + _symbol deref; local d="${r}" READ_FORM; local f="${r}" - list "${d}" "${f}" ;; + _list "${d}" "${f}" ;; \)) _error "unexpected ')'" ;; \() READ_SEQ "(" ")" - list ${r} ;; + _list ${r} ;; \]) _error "unexpected ']'" ;; \[) READ_SEQ "[" "]" - vector ${r} ;; + _vector ${r} ;; \}) _error "unexpected '}'" ;; \{) READ_SEQ "{" "}" - hash_map ${r} ;; + _hash_map ${r} ;; *) READ_ATOM esac } @@ -151,3 +154,5 @@ READLINE () { history -s -- "${r}" history -a "${READLINE_HISTORY_FILE}" } + +fi diff --git a/bash/step1_read_print.sh b/bash/step1_read_print.sh index ba94208..ca852ed 100755 --- a/bash/step1_read_print.sh +++ b/bash/step1_read_print.sh @@ -3,6 +3,7 @@ INTERACTIVE=${INTERACTIVE-yes} source $(dirname $0)/reader.sh +source $(dirname $0)/printer.sh # READ: read and parse input READ () { diff --git a/bash/step2_eval.sh b/bash/step2_eval.sh index 4d571e4..0f03a79 100755 --- a/bash/step2_eval.sh +++ b/bash/step2_eval.sh @@ -3,6 +3,8 @@ INTERACTIVE=${INTERACTIVE-yes} source $(dirname $0)/reader.sh +source $(dirname $0)/printer.sh +source $(dirname $0)/core.sh # READ: read and parse input READ () { @@ -20,17 +22,17 @@ EVAL_AST () { eval r="\${${env}["${val}"]}" [ "${r}" ] || _error "'${val}' not found" ;; list) - _map_with_type list EVAL "${ast}" "${env}" ;; + _map_with_type _list EVAL "${ast}" "${env}" ;; vector) - _map_with_type vector EVAL "${ast}" "${env}" ;; + _map_with_type _vector EVAL "${ast}" "${env}" ;; hash_map) local res="" val="" hm="${ANON["${ast}"]}" - hash_map; local new_hm="${r}" + _hash_map; local new_hm="${r}" eval local keys="\${!${hm}[@]}" for key in ${keys}; do eval val="\${${hm}[\"${key}\"]}" EVAL "${val}" "${env}" - assoc! "${new_hm}" "${key}" "${r}" + _assoc! "${new_hm}" "${key}" "${r}" done r="${new_hm}" ;; *) diff --git a/bash/step3_env.sh b/bash/step3_env.sh index cbc0867..28106ee 100755 --- a/bash/step3_env.sh +++ b/bash/step3_env.sh @@ -3,6 +3,9 @@ INTERACTIVE=${INTERACTIVE-yes} source $(dirname $0)/reader.sh +source $(dirname $0)/printer.sh +source $(dirname $0)/core.sh +source $(dirname $0)/env.sh # READ: read and parse input READ () { @@ -20,17 +23,17 @@ EVAL_AST () { ENV_GET "${env}" "${val}" return ;; list) - _map_with_type list EVAL "${ast}" "${env}" ;; + _map_with_type _list EVAL "${ast}" "${env}" ;; vector) - _map_with_type vector EVAL "${ast}" "${env}" ;; + _map_with_type _vector EVAL "${ast}" "${env}" ;; hash_map) local res="" val="" hm="${ANON["${ast}"]}" - hash_map; local new_hm="${r}" + _hash_map; local new_hm="${r}" eval local keys="\${!${hm}[@]}" for key in ${keys}; do eval val="\${${hm}[\"${key}\"]}" EVAL "${val}" "${env}" - assoc! "${new_hm}" "${key}" "${r}" + _assoc! "${new_hm}" "${key}" "${r}" done r="${new_hm}" ;; *) diff --git a/bash/step4_if_fn_do.sh b/bash/step4_if_fn_do.sh index fedb324..d22ade6 100755 --- a/bash/step4_if_fn_do.sh +++ b/bash/step4_if_fn_do.sh @@ -3,6 +3,9 @@ INTERACTIVE=${INTERACTIVE-yes} source $(dirname $0)/reader.sh +source $(dirname $0)/printer.sh +source $(dirname $0)/core.sh +source $(dirname $0)/env.sh # READ: read and parse input READ () { @@ -20,17 +23,17 @@ EVAL_AST () { ENV_GET "${env}" "${val}" return ;; list) - _map_with_type list EVAL "${ast}" "${env}" ;; + _map_with_type _list EVAL "${ast}" "${env}" ;; vector) - _map_with_type vector EVAL "${ast}" "${env}" ;; + _map_with_type _vector EVAL "${ast}" "${env}" ;; hash_map) local res="" val="" hm="${ANON["${ast}"]}" - hash_map; local new_hm="${r}" + _hash_map; local new_hm="${r}" eval local keys="\${!${hm}[@]}" for key in ${keys}; do eval val="\${${hm}[\"${key}\"]}" EVAL "${val}" "${env}" - assoc! "${new_hm}" "${key}" "${r}" + _assoc! "${new_hm}" "${key}" "${r}" done r="${new_hm}" ;; *) @@ -90,8 +93,8 @@ EVAL () { EVAL "${a2}" "${env}" fi return ;; - fn*) new_function "ENV \"${env}\" \"${a1}\" \"\${@}\"; \ - EVAL \"${a2}\" \"\${r}\"" + fn*) _function "ENV \"${env}\" \"${a1}\" \"\${@}\"; \ + EVAL \"${a2}\" \"\${r}\"" return ;; *) EVAL_AST "${ast}" "${env}" [[ "${__ERROR}" ]] && r= && return 1 @@ -124,10 +127,10 @@ REP () { PRINT "${r}" } -_fref () { new_function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } +_fref () { _function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } # Import types functions -for n in "${!types_ns[@]}"; do _fref "${n}" "${types_ns["${n}"]}"; done +for n in "${!core_ns[@]}"; do _fref "${n}" "${core_ns["${n}"]}"; done # Defined using the language itself REP "(def! not (fn* (a) (if a false true)))" diff --git a/bash/step5_tco.sh b/bash/step5_tco.sh index 409ec87..2d81d84 100755 --- a/bash/step5_tco.sh +++ b/bash/step5_tco.sh @@ -3,6 +3,9 @@ INTERACTIVE=${INTERACTIVE-yes} source $(dirname $0)/reader.sh +source $(dirname $0)/printer.sh +source $(dirname $0)/core.sh +source $(dirname $0)/env.sh # READ: read and parse input READ () { @@ -20,17 +23,17 @@ EVAL_AST () { ENV_GET "${env}" "${val}" return ;; list) - _map_with_type list EVAL "${ast}" "${env}" ;; + _map_with_type _list EVAL "${ast}" "${env}" ;; vector) - _map_with_type vector EVAL "${ast}" "${env}" ;; + _map_with_type _vector EVAL "${ast}" "${env}" ;; hash_map) local res="" val="" hm="${ANON["${ast}"]}" - hash_map; local new_hm="${r}" + _hash_map; local new_hm="${r}" eval local keys="\${!${hm}[@]}" for key in ${keys}; do eval val="\${${hm}[\"${key}\"]}" EVAL "${val}" "${env}" - assoc! "${new_hm}" "${key}" "${r}" + _assoc! "${new_hm}" "${key}" "${r}" done r="${new_hm}" ;; *) @@ -96,9 +99,9 @@ EVAL () { fi # Continue loop ;; - fn*) new_function "ENV \"${env}\" \"${a1}\" \"\${@}\"; \ - EVAL \"${a2}\" \"\${r}\"" \ - "${a2}" "${env}" "${a1}" + fn*) _function "ENV \"${env}\" \"${a1}\" \"\${@}\"; \ + EVAL \"${a2}\" \"\${r}\"" \ + "${a2}" "${env}" "${a1}" return ;; *) EVAL_AST "${ast}" "${env}" [[ "${__ERROR}" ]] && r= && return 1 @@ -141,10 +144,10 @@ REP () { PRINT "${r}" } -_fref () { new_function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } +_fref () { _function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } # Import types functions -for n in "${!types_ns[@]}"; do _fref "${n}" "${types_ns["${n}"]}"; done +for n in "${!core_ns[@]}"; do _fref "${n}" "${core_ns["${n}"]}"; done # Defined using the language itself REP "(def! not (fn* (a) (if a false true)))" diff --git a/bash/step6_file.sh b/bash/step6_file.sh index 1c8fab5..1ebba64 100755 --- a/bash/step6_file.sh +++ b/bash/step6_file.sh @@ -3,6 +3,9 @@ INTERACTIVE=${INTERACTIVE-yes} source $(dirname $0)/reader.sh +source $(dirname $0)/printer.sh +source $(dirname $0)/core.sh +source $(dirname $0)/env.sh # READ: read and parse input READ () { @@ -20,17 +23,17 @@ EVAL_AST () { ENV_GET "${env}" "${val}" return ;; list) - _map_with_type list EVAL "${ast}" "${env}" ;; + _map_with_type _list EVAL "${ast}" "${env}" ;; vector) - _map_with_type vector EVAL "${ast}" "${env}" ;; + _map_with_type _vector EVAL "${ast}" "${env}" ;; hash_map) local res="" val="" hm="${ANON["${ast}"]}" - hash_map; local new_hm="${r}" + _hash_map; local new_hm="${r}" eval local keys="\${!${hm}[@]}" for key in ${keys}; do eval val="\${${hm}[\"${key}\"]}" EVAL "${val}" "${env}" - assoc! "${new_hm}" "${key}" "${r}" + _assoc! "${new_hm}" "${key}" "${r}" done r="${new_hm}" ;; *) @@ -96,9 +99,9 @@ EVAL () { fi # Continue loop ;; - fn*) new_function "ENV \"${env}\" \"${a1}\" \"\${@}\"; \ - EVAL \"${a2}\" \"\${r}\"" \ - "${a2}" "${env}" "${a1}" + fn*) _function "ENV \"${env}\" \"${a1}\" \"\${@}\"; \ + EVAL \"${a2}\" \"\${r}\"" \ + "${a2}" "${env}" "${a1}" return ;; *) EVAL_AST "${ast}" "${env}" [[ "${__ERROR}" ]] && r= && return 1 @@ -141,10 +144,10 @@ REP () { PRINT "${r}" } -_fref () { new_function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } +_fref () { _function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } # Import types functions -for n in "${!types_ns[@]}"; do _fref "${n}" "${types_ns["${n}"]}"; done +for n in "${!core_ns[@]}"; do _fref "${n}" "${core_ns["${n}"]}"; done read_string () { READ_STR "${ANON["${1}"]}"; } _fref "read-string" read_string @@ -154,7 +157,7 @@ slurp () { local lines mapfile lines < "${ANON["${1}"]}" local text="${lines[*]}"; text=${text//$'\n' /$'\n'} - string "${text}" + _string "${text}" } _fref "slurp" slurp diff --git a/bash/step7_quote.sh b/bash/step7_quote.sh index cecc3b8..5aa2991 100755 --- a/bash/step7_quote.sh +++ b/bash/step7_quote.sh @@ -3,6 +3,9 @@ INTERACTIVE=${INTERACTIVE-yes} source $(dirname $0)/reader.sh +source $(dirname $0)/printer.sh +source $(dirname $0)/core.sh +source $(dirname $0)/env.sh # READ: read and parse input READ () { @@ -20,8 +23,8 @@ IS_PAIR () { QUASIQUOTE () { if ! IS_PAIR "${1}"; then - symbol quote - list "${r}" "${1}" + _symbol quote + _list "${r}" "${1}" return else _nth "${1}" 0; local a0="${r}" @@ -31,20 +34,20 @@ QUASIQUOTE () { elif IS_PAIR "${a0}"; then _nth "${a0}" 0; local a00="${r}" if [[ "${ANON["${a00}"]}" == "splice-unquote" ]]; then - symbol concat; local a="${r}" + _symbol concat; local a="${r}" _nth "${a0}" 1; local b="${r}" rest "${1}" QUASIQUOTE "${r}"; local c="${r}" - list "${a}" "${b}" "${c}" + _list "${a}" "${b}" "${c}" return fi fi fi - symbol cons; local a="${r}" + _symbol cons; local a="${r}" QUASIQUOTE "${a0}"; local b="${r}" rest "${1}" QUASIQUOTE "${r}"; local c="${r}" - list "${a}" "${b}" "${c}" + _list "${a}" "${b}" "${c}" return } @@ -58,17 +61,17 @@ EVAL_AST () { ENV_GET "${env}" "${val}" return ;; list) - _map_with_type list EVAL "${ast}" "${env}" ;; + _map_with_type _list EVAL "${ast}" "${env}" ;; vector) - _map_with_type vector EVAL "${ast}" "${env}" ;; + _map_with_type _vector EVAL "${ast}" "${env}" ;; hash_map) local res="" val="" hm="${ANON["${ast}"]}" - hash_map; local new_hm="${r}" + _hash_map; local new_hm="${r}" eval local keys="\${!${hm}[@]}" for key in ${keys}; do eval val="\${${hm}[\"${key}\"]}" EVAL "${val}" "${env}" - assoc! "${new_hm}" "${key}" "${r}" + _assoc! "${new_hm}" "${key}" "${r}" done r="${new_hm}" ;; *) @@ -141,9 +144,9 @@ EVAL () { fi # Continue loop ;; - fn*) new_function "ENV \"${env}\" \"${a1}\" \"\${@}\"; \ - EVAL \"${a2}\" \"\${r}\"" \ - "${a2}" "${env}" "${a1}" + fn*) _function "ENV \"${env}\" \"${a1}\" \"\${@}\"; \ + EVAL \"${a2}\" \"\${r}\"" \ + "${a2}" "${env}" "${a1}" return ;; *) EVAL_AST "${ast}" "${env}" [[ "${__ERROR}" ]] && r= && return 1 @@ -182,14 +185,14 @@ ENV; REPL_ENV="${r}" REP () { r= READ_STR "${1}" - EVAL "${r}" ${REPL_ENV} + EVAL "${r}" "${REPL_ENV}" PRINT "${r}" } -_fref () { new_function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } +_fref () { _function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } # Import types functions -for n in "${!types_ns[@]}"; do _fref "${n}" "${types_ns["${n}"]}"; done +for n in "${!core_ns[@]}"; do _fref "${n}" "${core_ns["${n}"]}"; done read_string () { READ_STR "${ANON["${1}"]}"; } _fref "read-string" read_string @@ -199,7 +202,7 @@ slurp () { local lines mapfile lines < "${ANON["${1}"]}" local text="${lines[*]}"; text=${text//$'\n' /$'\n'} - string "${text}" + _string "${text}" } _fref "slurp" slurp diff --git a/bash/step8_macros.sh b/bash/step8_macros.sh index a905958..a32e7f9 100755 --- a/bash/step8_macros.sh +++ b/bash/step8_macros.sh @@ -3,6 +3,9 @@ INTERACTIVE=${INTERACTIVE-yes} source $(dirname $0)/reader.sh +source $(dirname $0)/printer.sh +source $(dirname $0)/core.sh +source $(dirname $0)/env.sh # READ: read and parse input READ () { @@ -20,8 +23,8 @@ IS_PAIR () { QUASIQUOTE () { if ! IS_PAIR "${1}"; then - symbol quote - list "${r}" "${1}" + _symbol quote + _list "${r}" "${1}" return else _nth "${1}" 0; local a0="${r}" @@ -31,20 +34,20 @@ QUASIQUOTE () { elif IS_PAIR "${a0}"; then _nth "${a0}" 0; local a00="${r}" if [[ "${ANON["${a00}"]}" == "splice-unquote" ]]; then - symbol concat; local a="${r}" + _symbol concat; local a="${r}" _nth "${a0}" 1; local b="${r}" rest "${1}" QUASIQUOTE "${r}"; local c="${r}" - list "${a}" "${b}" "${c}" + _list "${a}" "${b}" "${c}" return fi fi fi - symbol cons; local a="${r}" + _symbol cons; local a="${r}" QUASIQUOTE "${a0}"; local b="${r}" rest "${1}" QUASIQUOTE "${r}"; local c="${r}" - list "${a}" "${b}" "${c}" + _list "${a}" "${b}" "${c}" return } @@ -83,17 +86,17 @@ EVAL_AST () { ENV_GET "${env}" "${val}" return ;; list) - _map_with_type list EVAL "${ast}" "${env}" ;; + _map_with_type _list EVAL "${ast}" "${env}" ;; vector) - _map_with_type vector EVAL "${ast}" "${env}" ;; + _map_with_type _vector EVAL "${ast}" "${env}" ;; hash_map) local res="" val="" hm="${ANON["${ast}"]}" - hash_map; local new_hm="${r}" + _hash_map; local new_hm="${r}" eval local keys="\${!${hm}[@]}" for key in ${keys}; do eval val="\${${hm}[\"${key}\"]}" EVAL "${val}" "${env}" - assoc! "${new_hm}" "${key}" "${r}" + _assoc! "${new_hm}" "${key}" "${r}" done r="${new_hm}" ;; *) @@ -177,9 +180,9 @@ EVAL () { fi # Continue loop ;; - fn*) new_function "ENV \"${env}\" \"${a1}\" \"\${@}\"; \ - EVAL \"${a2}\" \"\${r}\"" \ - "${a2}" "${env}" "${a1}" + fn*) _function "ENV \"${env}\" \"${a1}\" \"\${@}\"; \ + EVAL \"${a2}\" \"\${r}\"" \ + "${a2}" "${env}" "${a1}" return ;; *) EVAL_AST "${ast}" "${env}" [[ "${__ERROR}" ]] && r= && return 1 @@ -201,6 +204,7 @@ EVAL () { esac done } + # PRINT: PRINT () { if [[ "${__ERROR}" ]]; then @@ -221,22 +225,20 @@ REP () { PRINT "${r}" } -_fref () { new_function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } +_fref () { _function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } # Import types functions -for n in "${!types_ns[@]}"; do _fref "${n}" "${types_ns["${n}"]}"; done +for n in "${!core_ns[@]}"; do _fref "${n}" "${core_ns["${n}"]}"; done read_string () { READ_STR "${ANON["${1}"]}"; } _fref "read-string" read_string -_eval () { - EVAL "${1}" "${REPL_ENV}" -} +_eval () { EVAL "${1}" "${REPL_ENV}"; } _fref "eval" _eval slurp () { local lines mapfile lines < "${ANON["${1}"]}" local text="${lines[*]}"; text=${text//$'\n' /$'\n'} - string "${text}" + _string "${text}" } _fref "slurp" slurp diff --git a/bash/step9_interop.sh b/bash/step9_interop.sh index e1d57f5..dfa1e2c 100755 --- a/bash/step9_interop.sh +++ b/bash/step9_interop.sh @@ -3,6 +3,9 @@ INTERACTIVE=${INTERACTIVE-yes} source $(dirname $0)/reader.sh +source $(dirname $0)/printer.sh +source $(dirname $0)/core.sh +source $(dirname $0)/env.sh # READ: read and parse input READ () { @@ -20,8 +23,8 @@ IS_PAIR () { QUASIQUOTE () { if ! IS_PAIR "${1}"; then - symbol quote - list "${r}" "${1}" + _symbol quote + _list "${r}" "${1}" return else _nth "${1}" 0; local a0="${r}" @@ -31,20 +34,20 @@ QUASIQUOTE () { elif IS_PAIR "${a0}"; then _nth "${a0}" 0; local a00="${r}" if [[ "${ANON["${a00}"]}" == "splice-unquote" ]]; then - symbol concat; local a="${r}" + _symbol concat; local a="${r}" _nth "${a0}" 1; local b="${r}" rest "${1}" QUASIQUOTE "${r}"; local c="${r}" - list "${a}" "${b}" "${c}" + _list "${a}" "${b}" "${c}" return fi fi fi - symbol cons; local a="${r}" + _symbol cons; local a="${r}" QUASIQUOTE "${a0}"; local b="${r}" rest "${1}" QUASIQUOTE "${r}"; local c="${r}" - list "${a}" "${b}" "${c}" + _list "${a}" "${b}" "${c}" return } @@ -83,17 +86,17 @@ EVAL_AST () { ENV_GET "${env}" "${val}" return ;; list) - _map_with_type list EVAL "${ast}" "${env}" ;; + _map_with_type _list EVAL "${ast}" "${env}" ;; vector) - _map_with_type vector EVAL "${ast}" "${env}" ;; + _map_with_type _vector EVAL "${ast}" "${env}" ;; hash_map) local res="" val="" hm="${ANON["${ast}"]}" - hash_map; local new_hm="${r}" + _hash_map; local new_hm="${r}" eval local keys="\${!${hm}[@]}" for key in ${keys}; do eval val="\${${hm}[\"${key}\"]}" EVAL "${val}" "${env}" - assoc! "${new_hm}" "${key}" "${r}" + _assoc! "${new_hm}" "${key}" "${r}" done r="${new_hm}" ;; *) @@ -160,7 +163,7 @@ EVAL () { while read line; do output="${output}${line}\n" done < <(eval ${ANON["${r}"]}) - string "${output}" + _string "${output}" return ;; do) _count "${ast}" _slice "${ast}" 1 $(( ${r} - 2 )) @@ -186,9 +189,9 @@ EVAL () { fi # Continue loop ;; - fn*) new_function "ENV \"${env}\" \"${a1}\" \"\${@}\"; \ - EVAL \"${a2}\" \"\${r}\"" \ - "${a2}" "${env}" "${a1}" + fn*) _function "ENV \"${env}\" \"${a1}\" \"\${@}\"; \ + EVAL \"${a2}\" \"\${r}\"" \ + "${a2}" "${env}" "${a1}" return ;; *) EVAL_AST "${ast}" "${env}" [[ "${__ERROR}" ]] && r= && return 1 @@ -210,6 +213,7 @@ EVAL () { esac done } + # PRINT: PRINT () { if [[ "${__ERROR}" ]]; then @@ -230,22 +234,20 @@ REP () { PRINT "${r}" } -_fref () { new_function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } +_fref () { _function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } # Import types functions -for n in "${!types_ns[@]}"; do _fref "${n}" "${types_ns["${n}"]}"; done +for n in "${!core_ns[@]}"; do _fref "${n}" "${core_ns["${n}"]}"; done read_string () { READ_STR "${ANON["${1}"]}"; } _fref "read-string" read_string -_eval () { - EVAL "${1}" "${REPL_ENV}" -} +_eval () { EVAL "${1}" "${REPL_ENV}"; } _fref "eval" _eval slurp () { local lines mapfile lines < "${ANON["${1}"]}" local text="${lines[*]}"; text=${text//$'\n' /$'\n'} - string "${text}" + _string "${text}" } _fref "slurp" slurp diff --git a/bash/stepA_more.sh b/bash/stepA_more.sh index 0902b57..605ca7f 100755 --- a/bash/stepA_more.sh +++ b/bash/stepA_more.sh @@ -3,6 +3,9 @@ INTERACTIVE=${INTERACTIVE-yes} source $(dirname $0)/reader.sh +source $(dirname $0)/printer.sh +source $(dirname $0)/core.sh +source $(dirname $0)/env.sh # READ: read and parse input READ () { @@ -20,8 +23,8 @@ IS_PAIR () { QUASIQUOTE () { if ! IS_PAIR "${1}"; then - symbol quote - list "${r}" "${1}" + _symbol quote + _list "${r}" "${1}" return else _nth "${1}" 0; local a0="${r}" @@ -31,20 +34,20 @@ QUASIQUOTE () { elif IS_PAIR "${a0}"; then _nth "${a0}" 0; local a00="${r}" if [[ "${ANON["${a00}"]}" == "splice-unquote" ]]; then - symbol concat; local a="${r}" + _symbol concat; local a="${r}" _nth "${a0}" 1; local b="${r}" rest "${1}" QUASIQUOTE "${r}"; local c="${r}" - list "${a}" "${b}" "${c}" + _list "${a}" "${b}" "${c}" return fi fi fi - symbol cons; local a="${r}" + _symbol cons; local a="${r}" QUASIQUOTE "${a0}"; local b="${r}" rest "${1}" QUASIQUOTE "${r}"; local c="${r}" - list "${a}" "${b}" "${c}" + _list "${a}" "${b}" "${c}" return } @@ -83,17 +86,17 @@ EVAL_AST () { ENV_GET "${env}" "${val}" return ;; list) - _map_with_type list EVAL "${ast}" "${env}" ;; + _map_with_type _list EVAL "${ast}" "${env}" ;; vector) - _map_with_type vector EVAL "${ast}" "${env}" ;; + _map_with_type _vector EVAL "${ast}" "${env}" ;; hash_map) local res="" val="" hm="${ANON["${ast}"]}" - hash_map; local new_hm="${r}" + _hash_map; local new_hm="${r}" eval local keys="\${!${hm}[@]}" for key in ${keys}; do eval val="\${${hm}[\"${key}\"]}" EVAL "${val}" "${env}" - assoc! "${new_hm}" "${key}" "${r}" + _assoc! "${new_hm}" "${key}" "${r}" done r="${new_hm}" ;; *) @@ -160,7 +163,7 @@ EVAL () { while read line; do output="${output}${line}\n" done < <(eval ${ANON["${r}"]}) - string "${output}" + _string "${output}" return ;; try*) MACROEXPAND "${a1}" "${env}" EVAL "${r}" "${env}" @@ -169,7 +172,7 @@ EVAL () { if [ "${ANON["${a20}"]}" == "catch__STAR__" ]; then _nth "${a2}" 1; local a21="${r}" _nth "${a2}" 2; local a22="${r}" - list "${a21}"; local binds="${r}" + _list "${a21}"; local binds="${r}" ENV "${env}" "${binds}" "${__ERROR}" local try_env="${r}" __ERROR= @@ -201,9 +204,9 @@ EVAL () { fi # Continue loop ;; - fn*) new_function "ENV \"${env}\" \"${a1}\" \"\${@}\"; \ - EVAL \"${a2}\" \"\${r}\"" \ - "${a2}" "${env}" "${a1}" + fn*) _function "ENV \"${env}\" \"${a1}\" \"\${@}\"; \ + EVAL \"${a2}\" \"\${r}\"" \ + "${a2}" "${env}" "${a1}" return ;; *) EVAL_AST "${ast}" "${env}" [[ "${__ERROR}" ]] && r= && return 1 @@ -225,6 +228,7 @@ EVAL () { esac done } + # PRINT: PRINT () { if [[ "${__ERROR}" ]]; then @@ -245,26 +249,24 @@ REP () { PRINT "${r}" } -_fref () { new_function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } +_fref () { _function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } # Import types functions -for n in "${!types_ns[@]}"; do _fref "${n}" "${types_ns["${n}"]}"; done +for n in "${!core_ns[@]}"; do _fref "${n}" "${core_ns["${n}"]}"; done readline () { - READLINE "${ANON["${1}"]}" && string "${r}" || r="${__nil}"; + READLINE "${ANON["${1}"]}" && _string "${r}" || r="${__nil}"; } _fref "readline" readline read_string () { READ_STR "${ANON["${1}"]}"; } _fref "read-string" read_string -_eval () { - EVAL "${1}" "${REPL_ENV}" -} +_eval () { EVAL "${1}" "${REPL_ENV}"; } _fref "eval" _eval slurp () { local lines mapfile lines < "${ANON["${1}"]}" local text="${lines[*]}"; text=${text//$'\n' /$'\n'} - string "${text}" + _string "${text}" } _fref "slurp" slurp diff --git a/bash/tests/types.sh b/bash/tests/types.sh index 7ce1ce4..0e073c5 100644 --- a/bash/tests/types.sh +++ b/bash/tests/types.sh @@ -39,8 +39,8 @@ assert_eq $LINENO ${__true} "string? ${STR2}" echo "Testing function objects" -assert_eq $LINENO "function" "new_function \"echo hello\"; _obj_type \$r" -new_function "r=\"arg1:'\$1' arg2:'\$2'\""; FN1="${r}" +assert_eq $LINENO "function" "_function \"echo hello\"; _obj_type \$r" +_function "r=\"arg1:'\$1' arg2:'\$2'\""; FN1="${r}" assert_eq $LINENO ${__true} "function? ${FN1}" assert_eq $LINENO "arg1:'A' arg2:'B'" "${ANON["${FN1}"]} A B" @@ -105,7 +105,7 @@ echo "Testing _map/map function" list; conj "${r}" 1 2 3; L5="${r}" inc () { r=$(( ${1} + 1)); } assert_eq $LINENO "2 3 4" "_map inc ${L5}; r=\${ANON[\$r]}" -new_function "r=\$(( \$1 + 1 ));"; inc_func="${r}" +_function "r=\$(( \$1 + 1 ));"; inc_func="${r}" assert_eq $LINENO "2 3 4" "map ${inc_func} ${L5}; r=\${ANON[\$r]}" 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 |
