aboutsummaryrefslogtreecommitdiff
path: root/bash
diff options
context:
space:
mode:
Diffstat (limited to 'bash')
-rw-r--r--bash/Makefile2
-rw-r--r--bash/core.sh370
-rw-r--r--bash/env.sh78
-rw-r--r--bash/printer.sh87
-rw-r--r--bash/reader.sh41
-rwxr-xr-xbash/step1_read_print.sh1
-rwxr-xr-xbash/step2_eval.sh10
-rwxr-xr-xbash/step3_env.sh11
-rwxr-xr-xbash/step4_if_fn_do.sh19
-rwxr-xr-xbash/step5_tco.sh21
-rwxr-xr-xbash/step6_file.sh23
-rwxr-xr-xbash/step7_quote.sh37
-rwxr-xr-xbash/step8_macros.sh40
-rwxr-xr-xbash/step9_interop.sh42
-rwxr-xr-xbash/stepA_more.sh46
-rw-r--r--bash/tests/types.sh6
-rw-r--r--bash/types.sh610
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