aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
-rw-r--r--c/Makefile10
-rw-r--r--c/core.c464
-rw-r--r--c/core.h29
-rw-r--r--c/env.c62
-rw-r--r--c/printer.c140
-rw-r--r--c/printer.h9
-rw-r--r--c/reader.c26
-rw-r--r--c/step2_eval.c1
-rw-r--r--c/step3_env.c1
-rw-r--r--c/step4_if_fn_do.c9
-rw-r--r--c/step5_tco.c9
-rw-r--r--c/step6_file.c9
-rw-r--r--c/step7_quote.c25
-rw-r--r--c/step8_macros.c25
-rw-r--r--c/step9_interop.c25
-rw-r--r--c/stepA_more.c31
-rw-r--r--c/types.c753
-rw-r--r--c/types.h74
-rw-r--r--clojure/Makefile3
-rw-r--r--clojure/src/core.clj63
-rw-r--r--clojure/src/env.clj35
-rw-r--r--clojure/src/printer.clj7
-rw-r--r--clojure/src/step1_read_print.clj4
-rw-r--r--clojure/src/step2_eval.clj7
-rw-r--r--clojure/src/step3_env.clj20
-rw-r--r--clojure/src/step4_if_fn_do.clj25
-rw-r--r--clojure/src/step5_tco.clj27
-rw-r--r--clojure/src/step6_file.clj29
-rw-r--r--clojure/src/step7_quote.clj27
-rw-r--r--clojure/src/step8_macros.clj35
-rw-r--r--clojure/src/step9_interop.clj35
-rw-r--r--clojure/src/stepA_more.clj47
-rw-r--r--clojure/src/types.clj71
-rw-r--r--java/Makefile6
-rw-r--r--java/src/main/java/mal/core.java470
-rw-r--r--java/src/main/java/mal/env.java57
-rw-r--r--java/src/main/java/mal/printer.java50
-rw-r--r--java/src/main/java/mal/step1_read_print.java5
-rw-r--r--java/src/main/java/mal/step2_eval.java20
-rw-r--r--java/src/main/java/mal/step3_env.java27
-rw-r--r--java/src/main/java/mal/step4_if_fn_do.java23
-rw-r--r--java/src/main/java/mal/step5_tco.java27
-rw-r--r--java/src/main/java/mal/step6_file.java25
-rw-r--r--java/src/main/java/mal/step7_quote.java29
-rw-r--r--java/src/main/java/mal/step8_macros.java33
-rw-r--r--java/src/main/java/mal/stepA_more.java35
-rw-r--r--java/src/main/java/mal/types.java654
-rw-r--r--js/Makefile3
-rw-r--r--js/core.js193
-rw-r--r--js/env.js40
-rw-r--r--js/printer.js44
-rw-r--r--js/reader.js18
-rw-r--r--js/step1_read_print.js3
-rw-r--r--js/step2_eval.js13
-rw-r--r--js/step3_env.js18
-rw-r--r--js/step4_if_fn_do.js25
-rw-r--r--js/step5_tco.js96
-rw-r--r--js/step6_file.js96
-rw-r--r--js/step7_quote.js113
-rw-r--r--js/step8_macros.js135
-rw-r--r--js/step9_interop.js147
-rw-r--r--js/stepA_more.js165
-rw-r--r--js/types.js423
-rw-r--r--make/Makefile3
-rw-r--r--make/core.mk255
-rw-r--r--make/env.mk50
-rw-r--r--make/printer.mk45
-rwxr-xr-xmake/reader.mk20
-rw-r--r--make/step1_read_print.mk1
-rw-r--r--make/step2_eval.mk2
-rw-r--r--make/step3_env.mk3
-rw-r--r--make/step4_if_fn_do.mk13
-rw-r--r--make/step6_file.mk21
-rw-r--r--make/step7_quote.mk27
-rw-r--r--make/step8_macros.mk27
-rw-r--r--make/step9_interop.mk27
-rw-r--r--make/stepA_more.mk31
-rw-r--r--make/tests/types.mk10
-rw-r--r--make/types.mk420
-rw-r--r--mal/Makefile2
-rw-r--r--mal/core.mal52
-rw-r--r--mal/step4_if_fn_do.mal6
-rw-r--r--mal/step6_file.mal6
-rw-r--r--mal/step7_quote.mal6
-rw-r--r--mal/step8_macros.mal6
-rw-r--r--mal/stepA_more.mal6
-rw-r--r--mal/types.mal16
-rw-r--r--php/Makefile16
-rw-r--r--php/core.php221
-rw-r--r--php/env.php56
-rw-r--r--php/printer.php53
-rw-r--r--php/reader.php26
-rw-r--r--php/step1_read_print.php1
-rw-r--r--php/step2_eval.php17
-rw-r--r--php/step3_env.php21
-rw-r--r--php/step4_if_fn_do.php25
-rw-r--r--php/step5_tco.php117
-rw-r--r--php/step6_file.php117
-rw-r--r--php/step7_quote.php141
-rw-r--r--php/step8_macros.php161
-rw-r--r--php/step9_interop.php165
-rw-r--r--php/stepA_more.php197
-rw-r--r--php/types.php420
-rw-r--r--ps/Makefile2
-rw-r--r--ps/core.ps126
-rw-r--r--ps/printer.ps61
-rw-r--r--ps/reader.ps1
-rw-r--r--ps/step1_read_print.ps1
-rw-r--r--ps/step2_eval.ps1
-rw-r--r--ps/step3_env.ps2
-rw-r--r--ps/step4_if_fn_do.ps5
-rw-r--r--ps/step5_tco.ps5
-rw-r--r--ps/step6_file.ps5
-rw-r--r--ps/step7_quote.ps5
-rw-r--r--ps/step8_macros.ps5
-rw-r--r--ps/step9_interop.ps5
-rw-r--r--ps/stepA_more.ps5
-rw-r--r--ps/types.ps310
-rw-r--r--python/Makefile3
-rw-r--r--python/core.py159
-rw-r--r--python/env.py28
-rw-r--r--python/mal_types.py251
-rw-r--r--python/printer.py29
-rw-r--r--python/reader.py22
-rw-r--r--python/step1_read_print.py16
-rw-r--r--python/step2_eval.py40
-rw-r--r--python/step3_env.py67
-rw-r--r--python/step4_if_fn_do.py96
-rw-r--r--python/step5_tco.py40
-rw-r--r--python/step6_file.py42
-rw-r--r--python/step7_quote.py55
-rw-r--r--python/step8_macros.py59
149 files changed, 5738 insertions, 5091 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
diff --git a/c/Makefile b/c/Makefile
index 397bcbf..fab4447 100644
--- a/c/Makefile
+++ b/c/Makefile
@@ -6,8 +6,10 @@ LDFLAGS += -g
TESTS =
-SOURCES = types.h types.c readline.h readline.c reader.h reader.c \
- interop.h interop.c stepA_more.c
+SOURCES = readline.h readline.c types.h types.c \
+ reader.h reader.c printer.h printer.c \
+ env.c core.h core.c interop.h interop.c \
+ stepA_more.c
#####################
@@ -16,8 +18,8 @@ SRCS = step0_repl.c step1_read_print.c step2_eval.c step3_env.c \
step8_macros.c step9_interop.c stepA_more.c
OBJS = $(SRCS:%.c=%.o)
BINS = $(OBJS:%.o=%)
-OTHER_OBJS = types.o readline.o reader.o interop.o
-OTHER_HDRS = types.h readline.h reader.h interop.h
+OTHER_OBJS = types.o readline.o reader.o printer.o env.o core.o interop.o
+OTHER_HDRS = types.h readline.h reader.h printer.h core.h interop.h
GLIB_CFLAGS ?= $(shell pkg-config --cflags glib-2.0)
GLIB_LDFLAGS ?= $(shell pkg-config --libs glib-2.0)
diff --git a/c/core.c b/c/core.c
new file mode 100644
index 0000000..abdf755
--- /dev/null
+++ b/c/core.c
@@ -0,0 +1,464 @@
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "types.h"
+#include "core.h"
+#include "printer.h"
+
+// Errors/Exceptions
+void throw(MalVal *obj) {
+ mal_error = obj;
+}
+
+
+// General functions
+
+MalVal *equal_Q(MalVal *a, MalVal *b) {
+ if (_equal_Q(a, b)) { return &mal_true; }
+ else { return &mal_false; }
+}
+
+
+// Scalar functions
+
+MalVal *nil_Q(MalVal *seq) { return seq->type & MAL_NIL ? &mal_true : &mal_false; }
+MalVal *true_Q(MalVal *seq) { return seq->type & MAL_TRUE ? &mal_true : &mal_false; }
+MalVal *false_Q(MalVal *seq) { return seq->type & MAL_FALSE ? &mal_true : &mal_false; }
+
+
+// Symbol functions
+
+MalVal *symbol(MalVal *args) {
+ assert_type(args, MAL_STRING,
+ "symbol called with non-string value");
+ args->type = MAL_SYMBOL; // change string to symbol
+ return args;
+}
+
+MalVal *symbol_Q(MalVal *seq) { return seq->type & MAL_SYMBOL ? &mal_true : &mal_false; }
+
+
+// String functions
+
+// Return a string representation of a MalVal sequence (in a format that can
+// be read by the reader). Returned string must be freed by caller.
+MalVal *pr_str(MalVal *args) {
+ assert_type(args, MAL_LIST|MAL_VECTOR,
+ "pr_str called with non-sequential args");
+ return malval_new_string(_pr_str_args(args, " ", 1));
+}
+
+// Return a string representation of a MalVal sequence with every item
+// concatenated together. Returned string must be freed by caller.
+MalVal *str(MalVal *args) {
+ assert_type(args, MAL_LIST|MAL_VECTOR,
+ "str called with non-sequential args");
+ return malval_new_string(_pr_str_args(args, "", 0));
+}
+
+// Print a string representation of a MalVal sequence (in a format that can
+// be read by the reader) followed by a newline. Returns nil.
+MalVal *prn(MalVal *args) {
+ assert_type(args, MAL_LIST|MAL_VECTOR,
+ "prn called with non-sequential args");
+ char *repr = _pr_str_args(args, " ", 1);
+ g_print("%s\n", repr);
+ free(repr);
+ return &mal_nil;
+}
+
+// Print a string representation of a MalVal sequence (for human consumption)
+// followed by a newline. Returns nil.
+MalVal *println(MalVal *args) {
+ assert_type(args, MAL_LIST|MAL_VECTOR,
+ "println called with non-sequential args");
+ char *repr = _pr_str_args(args, " ", 0);
+ g_print("%s\n", repr);
+ free(repr);
+ return &mal_nil;
+}
+
+
+// Number functions
+
+#define WRAP_INTEGER_OP(name, op) \
+ MalVal *int_ ## name(MalVal *a, MalVal *b) { \
+ return malval_new_integer(a->val.intnum op b->val.intnum); \
+ }
+#define WRAP_INTEGER_CMP_OP(name, op) \
+ MalVal *int_ ## name(MalVal *a, MalVal *b) { \
+ return a->val.intnum op b->val.intnum ? &mal_true : &mal_false; \
+ }
+WRAP_INTEGER_OP(plus,+)
+WRAP_INTEGER_OP(minus,-)
+WRAP_INTEGER_OP(multiply,*)
+WRAP_INTEGER_OP(divide,/)
+WRAP_INTEGER_CMP_OP(gt,>)
+WRAP_INTEGER_CMP_OP(gte,>=)
+WRAP_INTEGER_CMP_OP(lt,<)
+WRAP_INTEGER_CMP_OP(lte,<=)
+
+
+// List functions
+
+MalVal *list(MalVal *args) { return _list(args); }
+MalVal *list_Q(MalVal *seq) { return _list_Q(seq) ? &mal_true : &mal_false; }
+
+
+// Vector functions
+
+MalVal *vector(MalVal *args) { return _vector(args); }
+MalVal *vector_Q(MalVal *seq) { return _vector_Q(seq) ? &mal_true : &mal_false; }
+
+
+// Hash map functions
+
+MalVal *hash_map(MalVal *args) { return _hash_map(args); }
+MalVal *hash_map_Q(MalVal *seq) { return _hash_map_Q(seq) ? &mal_true : &mal_false; }
+
+MalVal *assoc(MalVal *args) {
+ assert_type(args, MAL_LIST|MAL_VECTOR,
+ "assoc called with non-sequential arguments");
+ assert(_count(args) >= 2,
+ "assoc needs at least 2 arguments");
+ GHashTable *htable = g_hash_table_copy(first(args)->val.hash_table);
+ MalVal *hm = malval_new_hash_map(htable);
+ return _assoc_BANG(hm, rest(args));
+}
+
+MalVal *dissoc(MalVal* args) {
+ GHashTable *htable = g_hash_table_copy(first(args)->val.hash_table);
+ MalVal *hm = malval_new_hash_map(htable);
+ return _dissoc_BANG(hm, rest(args));
+}
+
+MalVal *keys(MalVal *obj) {
+ assert_type(obj, MAL_HASH_MAP,
+ "keys called on non-hash-map");
+
+ GHashTableIter iter;
+ gpointer key, value;
+ MalVal *seq = malval_new_list(MAL_LIST,
+ g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),
+ _count(obj)));
+ g_hash_table_iter_init (&iter, obj->val.hash_table);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ MalVal *kname = malval_new_string((char *)key);
+ g_array_append_val(seq->val.array, kname);
+ }
+ return seq;
+}
+
+MalVal *vals(MalVal *obj) {
+ assert_type(obj, MAL_HASH_MAP,
+ "vals called on non-hash-map");
+
+ GHashTableIter iter;
+ gpointer key, value;
+ MalVal *seq = malval_new_list(MAL_LIST,
+ g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),
+ _count(obj)));
+ g_hash_table_iter_init (&iter, obj->val.hash_table);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ g_array_append_val(seq->val.array, value);
+ }
+ return seq;
+}
+
+
+// hash map and vector functions
+MalVal *get(MalVal *obj, MalVal *key) {
+ MalVal *val;
+ switch (obj->type) {
+ case MAL_VECTOR:
+ return _nth(obj, key->val.intnum);
+ case MAL_HASH_MAP:
+ if (g_hash_table_lookup_extended(obj->val.hash_table,
+ key->val.string,
+ NULL, (gpointer*)&val)) {
+ return val;
+ } else {
+ return &mal_nil;
+ }
+ default:
+ abort("get called on unsupported type %d", obj->type);
+ }
+}
+
+MalVal *contains_Q(MalVal *obj, MalVal *key) {
+ switch (obj->type) {
+ case MAL_VECTOR:
+ if (key->val.intnum < obj->val.array->len) {
+ return &mal_true;
+ } else {
+ return &mal_false;
+ }
+ case MAL_HASH_MAP:
+ if (g_hash_table_contains(obj->val.hash_table, key->val.string)) {
+ return &mal_true;
+ } else {
+ return &mal_false;
+ }
+ default:
+ abort("contains? called on unsupported type %d", obj->type);
+ }
+}
+
+
+// Sequence functions
+
+MalVal *sequential_Q(MalVal *seq) {
+ return _sequential_Q(seq) ? &mal_true : &mal_false;
+}
+
+MalVal *cons(MalVal *x, MalVal *seq) {
+ assert_type(seq, MAL_LIST|MAL_VECTOR,
+ "second argument to cons is non-sequential");
+ int i, len = _count(seq);
+ GArray *new_arr = g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),
+ len+1);
+ g_array_append_val(new_arr, x);
+ for (i=0; i<len; i++) {
+ g_array_append_val(new_arr, g_array_index(seq->val.array, MalVal*, i));
+ }
+ return malval_new_list(MAL_LIST, new_arr);
+}
+
+MalVal *concat(MalVal *args) {
+ MalVal *arg, *e, *lst;
+ int i, j, arg_cnt = _count(args);
+ lst = malval_new_list(MAL_LIST,
+ g_array_sized_new(TRUE, TRUE, sizeof(MalVal*), arg_cnt));
+ for (i=0; i<arg_cnt; i++) {
+ arg = g_array_index(args->val.array, MalVal*, i);
+ assert_type(arg, MAL_LIST|MAL_VECTOR,
+ "concat called with non-sequential");
+ for (j=0; j<_count(arg); j++) {
+ e = g_array_index(arg->val.array, MalVal*, j);
+ g_array_append_val(lst->val.array, e);
+ }
+ }
+ return lst;
+}
+
+MalVal *nth(MalVal *seq, MalVal *idx) {
+ return _nth(seq, idx->val.intnum);
+}
+
+MalVal *first(MalVal *seq) {
+ assert_type(seq, MAL_LIST|MAL_VECTOR,
+ "first called with non-sequential");
+ if (_count(seq) == 0) {
+ return &mal_nil;
+ }
+ return g_array_index(seq->val.array, MalVal*, 0);
+}
+
+MalVal *rest(MalVal *seq) {
+ return _slice(seq, 1, _count(seq));
+}
+
+MalVal *empty_Q(MalVal *seq) {
+ assert_type(seq, MAL_LIST|MAL_VECTOR,
+ "empty? called with non-sequential");
+ return (seq->val.array->len == 0) ? &mal_true : &mal_false;
+}
+
+MalVal *count(MalVal *seq) {
+ return malval_new_integer(_count(seq));
+}
+
+MalVal *sconj(MalVal *args) {
+ assert_type(args, MAL_LIST|MAL_VECTOR,
+ "conj called with non-sequential");
+ MalVal *src_lst = _nth(args, 0);
+ assert_type(args, MAL_LIST|MAL_VECTOR,
+ "first argument to conj is non-sequential");
+ int i, len = _count(src_lst) + _count(args) - 1;
+ GArray *new_arr = g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),
+ len);
+ // Copy in src_lst
+ for (i=0; i<_count(src_lst); i++) {
+ g_array_append_val(new_arr, g_array_index(src_lst->val.array, MalVal*, i));
+ }
+ // Conj extra args
+ for (i=1; i<_count(args); i++) {
+ if (src_lst->type & MAL_LIST) {
+ g_array_prepend_val(new_arr, g_array_index(args->val.array, MalVal*, i));
+ } else {
+ g_array_append_val(new_arr, g_array_index(args->val.array, MalVal*, i));
+ }
+ }
+ return malval_new_list(src_lst->type, new_arr);
+}
+
+MalVal *last(MalVal *seq) {
+ assert_type(seq, MAL_LIST|MAL_VECTOR,
+ "last called with non-sequential");
+ if (_count(seq) == 0) {
+ return &mal_nil;
+ }
+ return g_array_index(seq->val.array, MalVal*, _count(seq)-1);
+}
+
+MalVal *apply(MalVal *args) {
+ assert_type(args, MAL_LIST|MAL_VECTOR,
+ "apply called with non-sequential");
+ MalVal *f = _nth(args, 0);
+ MalVal *last_arg = last(args);
+ assert_type(last_arg, MAL_LIST|MAL_VECTOR,
+ "last argument to apply is non-sequential");
+ int i, len = _count(args) - 2 + _count(last_arg);
+ GArray *new_arr = g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),
+ len);
+ // Initial arguments
+ for (i=1; i<_count(args)-1; i++) {
+ g_array_append_val(new_arr, g_array_index(args->val.array, MalVal*, i));
+ }
+ // Add arguments from last_arg
+ for (i=0; i<_count(last_arg); i++) {
+ g_array_append_val(new_arr, g_array_index(last_arg->val.array, MalVal*, i));
+ }
+ return _apply(f, malval_new_list(MAL_LIST, new_arr));
+}
+
+MalVal *map(MalVal *mvf, MalVal *lst) {
+ MalVal *res, *el;
+ assert_type(mvf, MAL_FUNCTION_C|MAL_FUNCTION_MAL,
+ "map called with non-function");
+ assert_type(lst, MAL_LIST|MAL_VECTOR,
+ "map called with non-sequential");
+ int i, len = _count(lst);
+ el = malval_new_list(MAL_LIST,
+ g_array_sized_new(TRUE, TRUE, sizeof(MalVal*), len));
+ for (i=0; i<len; i++) {
+ // TODO: this is replicating some of apply functionality
+ if (mvf->type & MAL_FUNCTION_MAL) {
+ Env *fn_env = new_env(mvf->val.func.env,
+ mvf->val.func.args,
+ _slice(lst, i, i+1));
+ res = mvf->val.func.evaluator(mvf->val.func.body, fn_env);
+ } else {
+ res = mvf->val.f1(g_array_index(lst->val.array, MalVal*, i));
+ }
+ if (!res || mal_error) return NULL;
+ g_array_append_val(el->val.array, res);
+ }
+ return el;
+}
+
+
+// Metadata functions
+
+MalVal *with_meta(MalVal *obj, MalVal *meta) {
+ MalVal *new_obj = malval_new(obj->type, meta);
+ new_obj->val = obj->val;
+ return new_obj;
+}
+
+MalVal *meta(MalVal *obj) {
+ assert_type(obj, MAL_LIST|MAL_VECTOR|MAL_HASH_MAP|MAL_FUNCTION_C|MAL_FUNCTION_MAL,
+ "attempt to get metadata from non-collection type");
+ if (obj->metadata == NULL) {
+ return &mal_nil;
+ } else {
+ return obj->metadata;
+ }
+}
+
+
+// Atoms
+
+MalVal *atom(MalVal *val) {
+ return malval_new_atom(val);
+}
+
+MalVal *atom_Q(MalVal *exp) { return _atom_Q(exp) ? &mal_true : &mal_false; }
+
+MalVal *deref(MalVal *atm) {
+ assert_type(atm, MAL_ATOM,
+ "deref called on non-atom");
+ return atm->val.atom_val;
+}
+
+MalVal *reset_BANG(MalVal *atm, MalVal *val) {
+ assert_type(atm, MAL_ATOM,
+ "reset! called with non-atom");
+ atm->val.atom_val = val;
+ return val;
+}
+
+MalVal *swap_BANG(MalVal *args) {
+ assert_type(args, MAL_LIST|MAL_VECTOR,
+ "swap! called with invalid arguments");
+ assert(_count(args) >= 2,
+ "swap! called with %d args, needs at least 2", _count(args));
+ MalVal *atm = _nth(args, 0),
+ *f = _nth(args, 1),
+ *sargs = _slice(args, 2, _count(args)),
+ *fargs = cons(atm->val.atom_val, sargs),
+ *new_val = _apply(f, fargs);
+ if (mal_error) { return NULL; }
+ atm->val.atom_val = new_val;
+ return new_val;
+}
+
+
+
+core_ns_entry core_ns[50] = {
+ {"=", (void*(*)(void*))equal_Q, 2},
+ {"throw", (void*(*)(void*))throw, 1},
+ {"nil?", (void*(*)(void*))nil_Q, 1},
+ {"true?", (void*(*)(void*))true_Q, 1},
+ {"false?", (void*(*)(void*))false_Q, 1},
+ {"symbol", (void*(*)(void*))symbol, 1},
+ {"symbol?", (void*(*)(void*))symbol_Q, 1},
+ {"pr-str", (void*(*)(void*))pr_str, -1},
+ {"str", (void*(*)(void*))str, -1},
+ {"prn", (void*(*)(void*))prn, -1},
+ {"println", (void*(*)(void*))println, -1},
+ {"<", (void*(*)(void*))int_lt, 2},
+ {"<=", (void*(*)(void*))int_lte, 2},
+ {">", (void*(*)(void*))int_gt, 2},
+ {">=", (void*(*)(void*))int_gte, 2},
+ {"+", (void*(*)(void*))int_plus, 2},
+ {"-", (void*(*)(void*))int_minus, 2},
+ {"*", (void*(*)(void*))int_multiply, 2},
+ {"/", (void*(*)(void*))int_divide, 2},
+
+ {"list", (void*(*)(void*))list, -1},
+ {"list?", (void*(*)(void*))list_Q, 1},
+ {"vector", (void*(*)(void*))vector, -1},
+ {"vector?", (void*(*)(void*))vector_Q, 1},
+ {"hash-map", (void*(*)(void*))hash_map, -1},
+ {"map?", (void*(*)(void*))hash_map_Q, 1},
+ {"assoc", (void*(*)(void*))assoc, -1},
+ {"dissoc", (void*(*)(void*))dissoc, -1},
+ {"get", (void*(*)(void*))get, 2},
+ {"contains?", (void*(*)(void*))contains_Q, 2},
+ {"keys", (void*(*)(void*))keys, 1},
+ {"vals", (void*(*)(void*))vals, 1},
+
+ {"sequential?", (void*(*)(void*))sequential_Q, 1},
+ {"cons", (void*(*)(void*))cons, 2},
+ {"concat", (void*(*)(void*))concat, -1},
+ {"nth", (void*(*)(void*))nth, 2},
+ {"first", (void*(*)(void*))first, 1},
+ {"rest", (void*(*)(void*))rest, 1},
+ {"last", (void*(*)(void*))last, 1},
+ {"empty?", (void*(*)(void*))empty_Q, 1},
+ {"count", (void*(*)(void*))count, 1},
+ {"conj", (void*(*)(void*))sconj, -1},
+ {"apply", (void*(*)(void*))apply, -1},
+ {"map", (void*(*)(void*))map, 2},
+
+ {"with-meta", (void*(*)(void*))with_meta, 2},
+ {"meta", (void*(*)(void*))meta, 1},
+ {"atom", (void*(*)(void*))atom, 1},
+ {"atom?", (void*(*)(void*))atom_Q, 1},
+ {"deref", (void*(*)(void*))deref, 1},
+ {"reset!", (void*(*)(void*))reset_BANG, 2},
+ {"swap!", (void*(*)(void*))swap_BANG, -1},
+ };
diff --git a/c/core.h b/c/core.h
new file mode 100644
index 0000000..6668c53
--- /dev/null
+++ b/c/core.h
@@ -0,0 +1,29 @@
+#ifndef __MAL_CORE__
+#define __MAL_CORE__
+
+#include <glib.h>
+
+// These are just used by step2 and step3 before then core_ns environment is
+// imported
+
+MalVal *int_plus(MalVal *a, MalVal *b);
+MalVal *int_minus(MalVal *a, MalVal *b);
+MalVal *int_multiply(MalVal *a, MalVal *b);
+MalVal *int_divide(MalVal *a, MalVal *b);
+
+// Useful for step implementation
+MalVal *first(MalVal *seq);
+MalVal *rest(MalVal *seq);
+MalVal *last(MalVal *seq);
+MalVal *hash_map(MalVal *args);
+
+// namespace of type functions
+typedef struct {
+ char *name;
+ void *(*func)(void*);
+ int arg_cnt;
+} core_ns_entry;
+
+extern core_ns_entry core_ns[50];
+
+#endif
diff --git a/c/env.c b/c/env.c
new file mode 100644
index 0000000..d4b8f32
--- /dev/null
+++ b/c/env.c
@@ -0,0 +1,62 @@
+/*
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+*/
+#include <stdlib.h>
+#include "types.h"
+
+// Env
+
+Env *new_env(Env *outer, MalVal* binds, MalVal *exprs) {
+ Env *e = malloc(sizeof(Env));
+ e->table = g_hash_table_new(g_str_hash, g_str_equal);
+ e->outer = outer;
+
+ if (binds && exprs) {
+ assert_type(binds, MAL_LIST|MAL_VECTOR,
+ "new_env called with non-sequential bindings");
+ assert_type(exprs, MAL_LIST|MAL_VECTOR,
+ "new_env called with non-sequential expressions");
+ int binds_len = _count(binds),
+ exprs_len = _count(exprs),
+ varargs = 0, i;
+ for (i=0; i<binds_len; i++) {
+ if (i > exprs_len) { break; }
+ if (_nth(binds, i)->val.string[0] == '&') {
+ varargs = 1;
+ env_set(e, _nth(binds, i+1)->val.string, _slice(exprs, i, _count(exprs)));
+ break;
+ } else {
+ env_set(e, _nth(binds, i)->val.string, _nth(exprs, i));
+ }
+ }
+ assert(varargs || (binds_len == exprs_len),
+ "Arity mismatch: %d formal params vs %d actual params",
+ binds_len, exprs_len);
+
+ }
+ return e;
+}
+
+Env *env_find(Env *env, char *key) {
+ void *val = g_hash_table_lookup(env->table, key);
+ if (val) {
+ return env;
+ } else if (env->outer) {
+ return env_find(env->outer, key);
+ } else {
+ return NULL;
+ }
+}
+
+MalVal *env_get(Env *env, char *key) {
+ Env *e = env_find(env, key);
+ assert(e, "'%s' not found", key);
+ return g_hash_table_lookup(e->table, key);
+}
+
+Env *env_set(Env *env, char *key, MalVal *val) {
+ g_hash_table_insert(env->table, key, val);
+ return env;
+}
diff --git a/c/printer.c b/c/printer.c
new file mode 100644
index 0000000..0669cf6
--- /dev/null
+++ b/c/printer.c
@@ -0,0 +1,140 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include "types.h"
+#include "printer.h"
+
+char *_pr_str_hash_map(MalVal *obj, int print_readably) {
+ int start = 1;
+ char *repr = NULL, *repr_tmp1 = NULL, *repr_tmp2 = NULL;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ repr = g_strdup_printf("{");
+
+ g_hash_table_iter_init (&iter, obj->val.hash_table);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ //g_print ("%s/%p ", (const char *) key, (void *) value);
+
+ repr_tmp1 = _pr_str((MalVal*)value, print_readably);
+ if (start) {
+ start = 0;
+ repr = g_strdup_printf("{\"%s\" %s", (char *)key, repr_tmp1);
+ } else {
+ repr_tmp2 = repr;
+ repr = g_strdup_printf("%s \"%s\" %s", repr_tmp2, (char *)key, repr_tmp1);
+ free(repr_tmp2);
+ }
+ free(repr_tmp1);
+ }
+ repr_tmp2 = repr;
+ repr = g_strdup_printf("%s}", repr_tmp2);
+ free(repr_tmp2);
+ return repr;
+}
+
+char *_pr_str_list(MalVal *obj, int print_readably, char start, char end) {
+ int i;
+ char *repr = NULL, *repr_tmp1 = NULL, *repr_tmp2 = NULL;
+ repr = g_strdup_printf("%c", start);
+ for (i=0; i<_count(obj); i++) {
+ repr_tmp1 = _pr_str(g_array_index(obj->val.array, MalVal*, i),
+ print_readably);
+ if (i == 0) {
+ repr = g_strdup_printf("%c%s", start, repr_tmp1);
+ } else {
+ repr_tmp2 = repr;
+ repr = g_strdup_printf("%s %s", repr_tmp2, repr_tmp1);
+ free(repr_tmp2);
+ }
+ free(repr_tmp1);
+ }
+ repr_tmp2 = repr;
+ repr = g_strdup_printf("%s%c", repr_tmp2, end);
+ free(repr_tmp2);
+ return repr;
+}
+
+// Return a string representation of the MalVal object. Returned string must
+// be freed by caller.
+char *_pr_str(MalVal *obj, int print_readably) {
+ char *repr = NULL;
+ if (obj == NULL) { return NULL; }
+ switch (obj->type) {
+ case MAL_NIL:
+ repr = g_strdup_printf("nil");
+ break;
+ case MAL_TRUE:
+ repr = g_strdup_printf("true");
+ break;
+ case MAL_FALSE:
+ repr = g_strdup_printf("false");
+ break;
+ case MAL_STRING:
+ if (print_readably) {
+ char *repr_tmp = g_strescape(obj->val.string, "");
+ repr = g_strdup_printf("\"%s\"", repr_tmp);
+ free(repr_tmp);
+ } else {
+ repr = g_strdup_printf("%s", obj->val.string);
+ }
+ break;
+ case MAL_SYMBOL:
+ repr = g_strdup_printf("%s", obj->val.string);
+ break;
+ case MAL_INTEGER:
+ repr = g_strdup_printf("%" G_GINT64_FORMAT, obj->val.intnum);
+ break;
+ case MAL_FLOAT:
+ repr = g_strdup_printf("%f", obj->val.floatnum);
+ break;
+ case MAL_HASH_MAP:
+ repr = _pr_str_hash_map(obj, print_readably);
+ break;
+ case MAL_LIST:
+ repr = _pr_str_list(obj, print_readably, '(', ')');
+ break;
+ case MAL_VECTOR:
+ repr = _pr_str_list(obj, print_readably, '[', ']');
+ break;
+ case MAL_ATOM:
+ repr = g_strdup_printf("(atom %s)",
+ _pr_str(obj->val.atom_val, print_readably));
+ break;
+ case MAL_FUNCTION_C:
+ repr = g_strdup_printf("#<function@%p>", obj->val.f0);
+ break;
+ case MAL_FUNCTION_MAL:
+ repr = g_strdup_printf("#<Function: (fn* %s %s)>",
+ _pr_str(obj->val.func.args, print_readably),
+ _pr_str(obj->val.func.body, print_readably));
+ break;
+ default:
+ printf("pr_str unknown type %d\n", obj->type);
+ repr = g_strdup_printf("<unknown>");
+ }
+ return repr;
+}
+
+// Return a string representation of the MalVal arguments. Returned string must
+// be freed by caller.
+char *_pr_str_args(MalVal *args, char *sep, int print_readably) {
+ assert_type(args, MAL_LIST|MAL_VECTOR,
+ "_pr_str called with non-sequential args");
+ int i;
+ char *repr = g_strdup_printf(""),
+ *repr2 = NULL;
+ for (i=0; i<_count(args); i++) {
+ MalVal *obj = g_array_index(args->val.array, MalVal*, i);
+ if (i != 0) {
+ repr2 = repr;
+ repr = g_strdup_printf("%s%s", repr2, sep);
+ free(repr2);
+ }
+ repr2 = repr;
+ repr = g_strdup_printf("%s%s",
+ repr2, _pr_str(obj, print_readably));
+ free(repr2);
+ }
+ return repr;
+}
+
diff --git a/c/printer.h b/c/printer.h
new file mode 100644
index 0000000..b3f389a
--- /dev/null
+++ b/c/printer.h
@@ -0,0 +1,9 @@
+#ifndef __MAL_PRINTER__
+#define __MAL_PRINTER__
+
+#include "types.h"
+
+char *_pr_str_args(MalVal *args, char *sep, int print_readably);
+char *_pr_str(MalVal *obj, int print_readably);
+
+#endif
diff --git a/c/reader.c b/c/reader.c
index 044bb84..dbb7335 100644
--- a/c/reader.c
+++ b/c/reader.c
@@ -181,7 +181,7 @@ MalVal *read_list(Reader *reader, MalType type, char start, char end) {
MalVal *read_hash_map(Reader *reader) {
MalVal *lst = read_list(reader, MAL_LIST, '{', '}');
- MalVal *hm = hash_map(lst);
+ MalVal *hm = _hash_map(lst);
malval_free(lst);
return hm;
}
@@ -207,34 +207,34 @@ MalVal *read_form(Reader *reader) {
break;
case '\'':
reader_next(reader);
- form = _list(2, malval_new_symbol("quote"),
- read_form(reader));
+ form = _listX(2, malval_new_symbol("quote"),
+ read_form(reader));
break;
case '`':
reader_next(reader);
- form = _list(2, malval_new_symbol("quasiquote"),
- read_form(reader));
+ form = _listX(2, malval_new_symbol("quasiquote"),
+ read_form(reader));
break;
case '~':
reader_next(reader);
if (token[1] == '@') {
- form = _list(2, malval_new_symbol("splice-unquote"),
- read_form(reader));
+ form = _listX(2, malval_new_symbol("splice-unquote"),
+ read_form(reader));
} else {
- form = _list(2, malval_new_symbol("unquote"),
- read_form(reader));
+ form = _listX(2, malval_new_symbol("unquote"),
+ read_form(reader));
};
break;
case '^':
reader_next(reader);
MalVal *meta = read_form(reader);
- form = _list(3, malval_new_symbol("with-meta"),
- read_form(reader), meta);
+ form = _listX(3, malval_new_symbol("with-meta"),
+ read_form(reader), meta);
break;
case '@':
reader_next(reader);
- form = _list(2, malval_new_symbol("deref"),
- read_form(reader));
+ form = _listX(2, malval_new_symbol("deref"),
+ read_form(reader));
break;
diff --git a/c/step2_eval.c b/c/step2_eval.c
index 509e795..85746f8 100644
--- a/c/step2_eval.c
+++ b/c/step2_eval.c
@@ -4,6 +4,7 @@
#include "types.h"
#include "readline.h"
#include "reader.h"
+#include "core.h"
// Declarations
MalVal *EVAL(MalVal *ast, GHashTable *env);
diff --git a/c/step3_env.c b/c/step3_env.c
index bc645b8..4abf4d6 100644
--- a/c/step3_env.c
+++ b/c/step3_env.c
@@ -4,6 +4,7 @@
#include "types.h"
#include "readline.h"
#include "reader.h"
+#include "core.h"
// Declarations
MalVal *EVAL(MalVal *ast, Env *env);
diff --git a/c/step4_if_fn_do.c b/c/step4_if_fn_do.c
index a96641e..3662816 100644
--- a/c/step4_if_fn_do.c
+++ b/c/step4_if_fn_do.c
@@ -4,6 +4,7 @@
#include "types.h"
#include "readline.h"
#include "reader.h"
+#include "core.h"
// Declarations
MalVal *EVAL(MalVal *ast, Env *env);
@@ -139,7 +140,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
*args = rest(el);
assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL,
"cannot apply '%s'", _pr_str(f,1));
- return apply(f, args);
+ return _apply(f, args);
}
}
@@ -179,9 +180,9 @@ void init_repl_env() {
repl_env = new_env(NULL, NULL, NULL);
int i;
- for(i=0; i< (sizeof(types_ns) / sizeof(types_ns[0])); i++) {
- MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))types_ns[i].func;
- _ref(types_ns[i].name, f, types_ns[i].arg_cnt);
+ for(i=0; i< (sizeof(core_ns) / sizeof(core_ns[0])); i++) {
+ MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))core_ns[i].func;
+ _ref(core_ns[i].name, f, core_ns[i].arg_cnt);
}
RE(repl_env, "", "(def! not (fn* (a) (if a false true)))");
diff --git a/c/step5_tco.c b/c/step5_tco.c
index dc0b28e..99d6826 100644
--- a/c/step5_tco.c
+++ b/c/step5_tco.c
@@ -4,6 +4,7 @@
#include "types.h"
#include "readline.h"
#include "reader.h"
+#include "core.h"
// Declarations
MalVal *EVAL(MalVal *ast, Env *env);
@@ -144,7 +145,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
env = new_env(f->val.func.env, f->val.func.args, args);
// Continue loop
} else {
- return apply(f, args);
+ return _apply(f, args);
}
}
}
@@ -186,9 +187,9 @@ void init_repl_env() {
repl_env = new_env(NULL, NULL, NULL);
int i;
- for(i=0; i< (sizeof(types_ns) / sizeof(types_ns[0])); i++) {
- MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))types_ns[i].func;
- _ref(types_ns[i].name, f, types_ns[i].arg_cnt);
+ for(i=0; i< (sizeof(core_ns) / sizeof(core_ns[0])); i++) {
+ MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))core_ns[i].func;
+ _ref(core_ns[i].name, f, core_ns[i].arg_cnt);
}
RE(repl_env, "", "(def! not (fn* (a) (if a false true)))");
diff --git a/c/step6_file.c b/c/step6_file.c
index bfd81fd..acde758 100644
--- a/c/step6_file.c
+++ b/c/step6_file.c
@@ -6,6 +6,7 @@
#include "types.h"
#include "readline.h"
#include "reader.h"
+#include "core.h"
#include "interop.h"
// Declarations
@@ -147,7 +148,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
env = new_env(f->val.func.env, f->val.func.args, args);
// Continue loop
} else {
- return apply(f, args);
+ return _apply(f, args);
}
}
}
@@ -209,9 +210,9 @@ void init_repl_env() {
repl_env = new_env(NULL, NULL, NULL);
int i;
- for(i=0; i< (sizeof(types_ns) / sizeof(types_ns[0])); i++) {
- MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))types_ns[i].func;
- _ref(types_ns[i].name, f, types_ns[i].arg_cnt);
+ for(i=0; i< (sizeof(core_ns) / sizeof(core_ns[0])); i++) {
+ MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))core_ns[i].func;
+ _ref(core_ns[i].name, f, core_ns[i].arg_cnt);
}
MalVal *read_string(MalVal *str) {
diff --git a/c/step7_quote.c b/c/step7_quote.c
index 5e6de17..7da47ee 100644
--- a/c/step7_quote.c
+++ b/c/step7_quote.c
@@ -6,6 +6,7 @@
#include "types.h"
#include "readline.h"
#include "reader.h"
+#include "core.h"
#include "interop.h"
// Declarations
@@ -36,7 +37,7 @@ int is_pair(MalVal *x) {
MalVal *quasiquote(MalVal *ast) {
if (!is_pair(ast)) {
- return _list(2, malval_new_symbol("quote"), ast);
+ return _listX(2, malval_new_symbol("quote"), ast);
} else {
MalVal *a0 = _nth(ast, 0);
if ((a0->type & MAL_SYMBOL) &&
@@ -46,14 +47,14 @@ MalVal *quasiquote(MalVal *ast) {
MalVal *a00 = _nth(a0, 0);
if ((a00->type & MAL_SYMBOL) &&
strcmp("splice-unquote", a00->val.string) == 0) {
- return _list(3, malval_new_symbol("concat"),
- _nth(a0, 1),
- quasiquote(rest(ast)));
+ return _listX(3, malval_new_symbol("concat"),
+ _nth(a0, 1),
+ quasiquote(rest(ast)));
}
}
- return _list(3, malval_new_symbol("cons"),
- quasiquote(a0),
- quasiquote(rest(ast)));
+ return _listX(3, malval_new_symbol("cons"),
+ quasiquote(a0),
+ quasiquote(rest(ast)));
}
}
@@ -91,8 +92,8 @@ MalVal *eval_ast(MalVal *ast, Env *env) {
MalVal *EVAL(MalVal *ast, Env *env) {
while (TRUE) {
- //g_print("EVAL: %s\n", _pr_str(ast,1));
if (!ast || mal_error) return NULL;
+ //g_print("EVAL: %s\n", _pr_str(ast,1));
if (ast->type != MAL_LIST) {
return eval_ast(ast, env);
}
@@ -183,7 +184,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
env = new_env(f->val.func.env, f->val.func.args, args);
// Continue loop
} else {
- return apply(f, args);
+ return _apply(f, args);
}
}
}
@@ -245,9 +246,9 @@ void init_repl_env() {
repl_env = new_env(NULL, NULL, NULL);
int i;
- for(i=0; i< (sizeof(types_ns) / sizeof(types_ns[0])); i++) {
- MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))types_ns[i].func;
- _ref(types_ns[i].name, f, types_ns[i].arg_cnt);
+ for(i=0; i< (sizeof(core_ns) / sizeof(core_ns[0])); i++) {
+ MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))core_ns[i].func;
+ _ref(core_ns[i].name, f, core_ns[i].arg_cnt);
}
MalVal *read_string(MalVal *str) {
diff --git a/c/step8_macros.c b/c/step8_macros.c
index 97da0ec..eb715b1 100644
--- a/c/step8_macros.c
+++ b/c/step8_macros.c
@@ -6,6 +6,7 @@
#include "types.h"
#include "readline.h"
#include "reader.h"
+#include "core.h"
#include "interop.h"
// Declarations
@@ -37,7 +38,7 @@ int is_pair(MalVal *x) {
MalVal *quasiquote(MalVal *ast) {
if (!is_pair(ast)) {
- return _list(2, malval_new_symbol("quote"), ast);
+ return _listX(2, malval_new_symbol("quote"), ast);
} else {
MalVal *a0 = _nth(ast, 0);
if ((a0->type & MAL_SYMBOL) &&
@@ -47,14 +48,14 @@ MalVal *quasiquote(MalVal *ast) {
MalVal *a00 = _nth(a0, 0);
if ((a00->type & MAL_SYMBOL) &&
strcmp("splice-unquote", a00->val.string) == 0) {
- return _list(3, malval_new_symbol("concat"),
- _nth(a0, 1),
- quasiquote(rest(ast)));
+ return _listX(3, malval_new_symbol("concat"),
+ _nth(a0, 1),
+ quasiquote(rest(ast)));
}
}
- return _list(3, malval_new_symbol("cons"),
- quasiquote(a0),
- quasiquote(rest(ast)));
+ return _listX(3, malval_new_symbol("cons"),
+ quasiquote(a0),
+ quasiquote(rest(ast)));
}
}
@@ -72,7 +73,7 @@ MalVal *macroexpand(MalVal *ast, Env *env) {
MalVal *a0 = _nth(ast, 0);
MalVal *mac = env_get(env, a0->val.string);
// TODO: this is weird and limits it to 20. FIXME
- ast = apply(mac, rest(ast));
+ ast = _apply(mac, rest(ast));
}
return ast;
}
@@ -222,7 +223,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
env = new_env(f->val.func.env, f->val.func.args, args);
// Continue loop
} else {
- return apply(f, args);
+ return _apply(f, args);
}
}
}
@@ -284,9 +285,9 @@ void init_repl_env() {
repl_env = new_env(NULL, NULL, NULL);
int i;
- for(i=0; i< (sizeof(types_ns) / sizeof(types_ns[0])); i++) {
- MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))types_ns[i].func;
- _ref(types_ns[i].name, f, types_ns[i].arg_cnt);
+ for(i=0; i< (sizeof(core_ns) / sizeof(core_ns[0])); i++) {
+ MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))core_ns[i].func;
+ _ref(core_ns[i].name, f, core_ns[i].arg_cnt);
}
MalVal *read_string(MalVal *str) {
diff --git a/c/step9_interop.c b/c/step9_interop.c
index 9c25b40..dcd1526 100644
--- a/c/step9_interop.c
+++ b/c/step9_interop.c
@@ -6,6 +6,7 @@
#include "types.h"
#include "readline.h"
#include "reader.h"
+#include "core.h"
#include "interop.h"
// Declarations
@@ -37,7 +38,7 @@ int is_pair(MalVal *x) {
MalVal *quasiquote(MalVal *ast) {
if (!is_pair(ast)) {
- return _list(2, malval_new_symbol("quote"), ast);
+ return _listX(2, malval_new_symbol("quote"), ast);
} else {
MalVal *a0 = _nth(ast, 0);
if ((a0->type & MAL_SYMBOL) &&
@@ -47,14 +48,14 @@ MalVal *quasiquote(MalVal *ast) {
MalVal *a00 = _nth(a0, 0);
if ((a00->type & MAL_SYMBOL) &&
strcmp("splice-unquote", a00->val.string) == 0) {
- return _list(3, malval_new_symbol("concat"),
- _nth(a0, 1),
- quasiquote(rest(ast)));
+ return _listX(3, malval_new_symbol("concat"),
+ _nth(a0, 1),
+ quasiquote(rest(ast)));
}
}
- return _list(3, malval_new_symbol("cons"),
- quasiquote(a0),
- quasiquote(rest(ast)));
+ return _listX(3, malval_new_symbol("cons"),
+ quasiquote(a0),
+ quasiquote(rest(ast)));
}
}
@@ -72,7 +73,7 @@ MalVal *macroexpand(MalVal *ast, Env *env) {
MalVal *a0 = _nth(ast, 0);
MalVal *mac = env_get(env, a0->val.string);
// TODO: this is weird and limits it to 20. FIXME
- ast = apply(mac, rest(ast));
+ ast = _apply(mac, rest(ast));
}
return ast;
}
@@ -227,7 +228,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
env = new_env(f->val.func.env, f->val.func.args, args);
// Continue loop
} else {
- return apply(f, args);
+ return _apply(f, args);
}
}
}
@@ -289,9 +290,9 @@ void init_repl_env() {
repl_env = new_env(NULL, NULL, NULL);
int i;
- for(i=0; i< (sizeof(types_ns) / sizeof(types_ns[0])); i++) {
- MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))types_ns[i].func;
- _ref(types_ns[i].name, f, types_ns[i].arg_cnt);
+ for(i=0; i< (sizeof(core_ns) / sizeof(core_ns[0])); i++) {
+ MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))core_ns[i].func;
+ _ref(core_ns[i].name, f, core_ns[i].arg_cnt);
}
MalVal *read_string(MalVal *str) {
diff --git a/c/stepA_more.c b/c/stepA_more.c
index f5a53ca..4e4152c 100644
--- a/c/stepA_more.c
+++ b/c/stepA_more.c
@@ -6,6 +6,7 @@
#include "types.h"
#include "readline.h"
#include "reader.h"
+#include "core.h"
#include "interop.h"
// Declarations
@@ -37,7 +38,7 @@ int is_pair(MalVal *x) {
MalVal *quasiquote(MalVal *ast) {
if (!is_pair(ast)) {
- return _list(2, malval_new_symbol("quote"), ast);
+ return _listX(2, malval_new_symbol("quote"), ast);
} else {
MalVal *a0 = _nth(ast, 0);
if ((a0->type & MAL_SYMBOL) &&
@@ -47,14 +48,14 @@ MalVal *quasiquote(MalVal *ast) {
MalVal *a00 = _nth(a0, 0);
if ((a00->type & MAL_SYMBOL) &&
strcmp("splice-unquote", a00->val.string) == 0) {
- return _list(3, malval_new_symbol("concat"),
- _nth(a0, 1),
- quasiquote(rest(ast)));
+ return _listX(3, malval_new_symbol("concat"),
+ _nth(a0, 1),
+ quasiquote(rest(ast)));
}
}
- return _list(3, malval_new_symbol("cons"),
- quasiquote(a0),
- quasiquote(rest(ast)));
+ return _listX(3, malval_new_symbol("cons"),
+ quasiquote(a0),
+ quasiquote(rest(ast)));
}
}
@@ -72,7 +73,7 @@ MalVal *macroexpand(MalVal *ast, Env *env) {
MalVal *a0 = _nth(ast, 0);
MalVal *mac = env_get(env, a0->val.string);
// TODO: this is weird and limits it to 20. FIXME
- ast = apply(mac, rest(ast));
+ ast = _apply(mac, rest(ast));
}
return ast;
}
@@ -193,8 +194,8 @@ MalVal *EVAL(MalVal *ast, Env *env) {
MalVal *a21 = _nth(a2, 1);
MalVal *a22 = _nth(a2, 2);
Env *catch_env = new_env(env,
- _list(1, a21),
- _list(1, mal_error));
+ _listX(1, a21),
+ _listX(1, mal_error));
//malval_free(mal_error);
mal_error = NULL;
res = EVAL(a22, catch_env);
@@ -248,12 +249,12 @@ MalVal *EVAL(MalVal *ast, Env *env) {
env = new_env(f->val.func.env, f->val.func.args, args);
// Continue loop
} else {
- return apply(f, args);
+ return _apply(f, args);
}
}
}
}
-
+
// print
char *PRINT(MalVal *exp) {
if (mal_error) {
@@ -310,9 +311,9 @@ void init_repl_env() {
repl_env = new_env(NULL, NULL, NULL);
int i;
- for(i=0; i< (sizeof(types_ns) / sizeof(types_ns[0])); i++) {
- MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))types_ns[i].func;
- _ref(types_ns[i].name, f, types_ns[i].arg_cnt);
+ for(i=0; i< (sizeof(core_ns) / sizeof(core_ns[0])); i++) {
+ MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))core_ns[i].func;
+ _ref(core_ns[i].name, f, core_ns[i].arg_cnt);
}
MalVal *readline(MalVal *str) {
diff --git a/c/types.c b/c/types.c
index a6bfbf6..5c06d9d 100644
--- a/c/types.c
+++ b/c/types.c
@@ -3,11 +3,17 @@
#include <stdlib.h>
#include <string.h>
#include "types.h"
+#include "printer.h"
-// State
-MalVal *mal_error = NULL;
+// Errors/Exceptions
+MalVal *mal_error = NULL; // WARNGIN: global state
+void _error(const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ mal_error = malval_new_string(g_strdup_vprintf(fmt, args));
+}
// Constant atomic values
@@ -16,10 +22,6 @@ MalVal mal_true = {MAL_TRUE, NULL, {0}, 0};
MalVal mal_false = {MAL_FALSE, NULL, {0}, 0};
-// Pre-declarations
-
-MalVal *cons(MalVal *x, MalVal *seq);
-
// General Functions
// Print a hash table
@@ -103,18 +105,18 @@ MalVal *malval_new_symbol(char *val) {
return mv;
}
-MalVal *malval_new_hash_map(GHashTable *val) {
- MalVal *mv = malval_new(MAL_HASH_MAP, NULL);
- mv->val.hash_table = val;
- return mv;
-}
-
MalVal *malval_new_list(MalType type, GArray *val) {
MalVal *mv = malval_new(type, NULL);
mv->val.array = val;
return mv;
}
+MalVal *malval_new_hash_map(GHashTable *val) {
+ MalVal *mv = malval_new(MAL_HASH_MAP, NULL);
+ mv->val.hash_table = val;
+ return mv;
+}
+
MalVal *malval_new_atom(MalVal *val) {
MalVal *mv = malval_new(MAL_ATOM, NULL);
mv->val.atom_val = val;
@@ -186,7 +188,7 @@ MalVal *malval_new_function(void *(*func)(void *), int arg_cnt, MalVal* metadata
return mv;
}
-MalVal *apply(MalVal *f, MalVal *args) {
+MalVal *_apply(MalVal *f, MalVal *args) {
MalVal *res;
assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL,
"Cannot invoke %s", _pr_str(f,1));
@@ -259,196 +261,6 @@ MalVal *apply(MalVal *f, MalVal *args) {
}
-char *_pr_str_hash_map(MalVal *obj, int print_readably) {
- int start = 1;
- char *repr = NULL, *repr_tmp1 = NULL, *repr_tmp2 = NULL;
- GHashTableIter iter;
- gpointer key, value;
-
- repr = g_strdup_printf("{");
-
- g_hash_table_iter_init (&iter, obj->val.hash_table);
- while (g_hash_table_iter_next (&iter, &key, &value)) {
- //g_print ("%s/%p ", (const char *) key, (void *) value);
-
- repr_tmp1 = _pr_str((MalVal*)value, print_readably);
- if (start) {
- start = 0;
- repr = g_strdup_printf("{\"%s\" %s", (char *)key, repr_tmp1);
- } else {
- repr_tmp2 = repr;
- repr = g_strdup_printf("%s \"%s\" %s", repr_tmp2, (char *)key, repr_tmp1);
- free(repr_tmp2);
- }
- free(repr_tmp1);
- }
- repr_tmp2 = repr;
- repr = g_strdup_printf("%s}", repr_tmp2);
- free(repr_tmp2);
- return repr;
-}
-
-char *_pr_str_list(MalVal *obj, int print_readably, char start, char end) {
- int i;
- char *repr = NULL, *repr_tmp1 = NULL, *repr_tmp2 = NULL;
- repr = g_strdup_printf("%c", start);
- for (i=0; i<_count(obj); i++) {
- repr_tmp1 = _pr_str(g_array_index(obj->val.array, MalVal*, i),
- print_readably);
- if (i == 0) {
- repr = g_strdup_printf("%c%s", start, repr_tmp1);
- } else {
- repr_tmp2 = repr;
- repr = g_strdup_printf("%s %s", repr_tmp2, repr_tmp1);
- free(repr_tmp2);
- }
- free(repr_tmp1);
- }
- repr_tmp2 = repr;
- repr = g_strdup_printf("%s%c", repr_tmp2, end);
- free(repr_tmp2);
- return repr;
-}
-
-// Return a string representation of the MalVal object. Returned string must
-// be freed by caller.
-char *_pr_str(MalVal *obj, int print_readably) {
- char *repr = NULL;
- if (obj == NULL) { return NULL; }
- switch (obj->type) {
- case MAL_NIL:
- repr = g_strdup_printf("nil");
- break;
- case MAL_TRUE:
- repr = g_strdup_printf("true");
- break;
- case MAL_FALSE:
- repr = g_strdup_printf("false");
- break;
- case MAL_STRING:
- if (print_readably) {
- char *repr_tmp = g_strescape(obj->val.string, "");
- repr = g_strdup_printf("\"%s\"", repr_tmp);
- free(repr_tmp);
- } else {
- repr = g_strdup_printf("%s", obj->val.string);
- }
- break;
- case MAL_SYMBOL:
- repr = g_strdup_printf("%s", obj->val.string);
- break;
- case MAL_INTEGER:
- repr = g_strdup_printf("%" G_GINT64_FORMAT, obj->val.intnum);
- break;
- case MAL_FLOAT:
- repr = g_strdup_printf("%f", obj->val.floatnum);
- break;
- case MAL_HASH_MAP:
- repr = _pr_str_hash_map(obj, print_readably);
- break;
- case MAL_LIST:
- repr = _pr_str_list(obj, print_readably, '(', ')');
- break;
- case MAL_VECTOR:
- repr = _pr_str_list(obj, print_readably, '[', ']');
- break;
- case MAL_ATOM:
- repr = g_strdup_printf("(atom %s)",
- _pr_str(obj->val.atom_val, print_readably));
- break;
- case MAL_FUNCTION_C:
- repr = g_strdup_printf("#<function@%p>", obj->val.f0);
- break;
- case MAL_FUNCTION_MAL:
- repr = g_strdup_printf("#<Function: (fn* %s %s)>",
- _pr_str(obj->val.func.args, print_readably),
- _pr_str(obj->val.func.body, print_readably));
- break;
- default:
- printf("pr_str unknown type %d\n", obj->type);
- repr = g_strdup_printf("<unknown>");
- }
- return repr;
-}
-
-// Return a string representation of the MalVal arguments. Returned string must
-// be freed by caller.
-char *_pr_str_args(MalVal *args, char *sep, int print_readably) {
- assert_type(args, MAL_LIST|MAL_VECTOR,
- "_pr_str called with non-sequential args");
- int i;
- char *repr = g_strdup_printf(""),
- *repr2 = NULL;
- for (i=0; i<_count(args); i++) {
- MalVal *obj = g_array_index(args->val.array, MalVal*, i);
- if (i != 0) {
- repr2 = repr;
- repr = g_strdup_printf("%s%s", repr2, sep);
- free(repr2);
- }
- repr2 = repr;
- repr = g_strdup_printf("%s%s",
- repr2, _pr_str(obj, print_readably));
- free(repr2);
- }
- return repr;
-}
-
-// Return a string representation of a MalVal sequence (in a format that can
-// be read by the reader). Returned string must be freed by caller.
-MalVal *pr_str(MalVal *args) {
- assert_type(args, MAL_LIST|MAL_VECTOR,
- "pr_str called with non-sequential args");
- return malval_new_string(_pr_str_args(args, " ", 1));
-}
-
-// Return a string representation of a MalVal sequence with every item
-// concatenated together. Returned string must be freed by caller.
-MalVal *str(MalVal *args) {
- assert_type(args, MAL_LIST|MAL_VECTOR,
- "str called with non-sequential args");
- return malval_new_string(_pr_str_args(args, "", 0));
-}
-
-// Print a string representation of a MalVal sequence (in a format that can
-// be read by the reader) followed by a newline. Returns nil.
-MalVal *prn(MalVal *args) {
- assert_type(args, MAL_LIST|MAL_VECTOR,
- "prn called with non-sequential args");
- char *repr = _pr_str_args(args, " ", 1);
- g_print("%s\n", repr);
- free(repr);
- return &mal_nil;
-}
-
-// Print a string representation of a MalVal sequence (for human consumption)
-// followed by a newline. Returns nil.
-MalVal *println(MalVal *args) {
- assert_type(args, MAL_LIST|MAL_VECTOR,
- "println called with non-sequential args");
- char *repr = _pr_str_args(args, " ", 0);
- g_print("%s\n", repr);
- free(repr);
- return &mal_nil;
-}
-
-MalVal *with_meta(MalVal *obj, MalVal *meta) {
- MalVal *new_obj = malval_new(obj->type, meta);
- new_obj->val = obj->val;
- return new_obj;
-}
-
-MalVal *meta(MalVal *obj) {
- assert_type(obj, MAL_LIST|MAL_VECTOR|MAL_HASH_MAP|MAL_FUNCTION_C|MAL_FUNCTION_MAL,
- "attempt to get metadata from non-collection type");
- if (obj->metadata == NULL) {
- return &mal_nil;
- } else {
- return obj->metadata;
- }
-}
-
-
int _equal_Q(MalVal *a, MalVal *b) {
if (a == NULL || b == NULL) { return FALSE; }
@@ -498,170 +310,9 @@ int _equal_Q(MalVal *a, MalVal *b) {
}
}
-MalVal *equal_Q(MalVal *a, MalVal *b) {
- if (_equal_Q(a, b)) { return &mal_true; }
- else { return &mal_false; }
-}
-
-//
-// nil, true, false, string
-MalVal *nil_Q(MalVal *seq) { return seq->type & MAL_NIL ? &mal_true : &mal_false; }
-MalVal *true_Q(MalVal *seq) { return seq->type & MAL_TRUE ? &mal_true : &mal_false; }
-MalVal *false_Q(MalVal *seq) { return seq->type & MAL_FALSE ? &mal_true : &mal_false; }
-MalVal *string_Q(MalVal *seq) { return seq->type & MAL_STRING ? &mal_true : &mal_false; }
-
-//
-// Numbers
-#define WRAP_INTEGER_OP(name, op) \
- MalVal *int_ ## name(MalVal *a, MalVal *b) { \
- return malval_new_integer(a->val.intnum op b->val.intnum); \
- }
-#define WRAP_INTEGER_CMP_OP(name, op) \
- MalVal *int_ ## name(MalVal *a, MalVal *b) { \
- return a->val.intnum op b->val.intnum ? &mal_true : &mal_false; \
- }
-WRAP_INTEGER_OP(plus,+)
-WRAP_INTEGER_OP(minus,-)
-WRAP_INTEGER_OP(multiply,*)
-WRAP_INTEGER_OP(divide,/)
-WRAP_INTEGER_CMP_OP(gt,>)
-WRAP_INTEGER_CMP_OP(gte,>=)
-WRAP_INTEGER_CMP_OP(lt,<)
-WRAP_INTEGER_CMP_OP(lte,<=)
-
-
-//
-// Symbols
-MalVal *symbol_Q(MalVal *seq) { return seq->type & MAL_SYMBOL ? &mal_true : &mal_false; }
-
-
-// Hash maps
-//
-MalVal *_hash_map(int count, ...) {
- assert((count % 2) == 0,
- "odd number of parameters to hash-map");
- GHashTable *htable = g_hash_table_new(g_str_hash, g_str_equal);
- MalVal *hm = malval_new_hash_map(htable);
- char *k;
- MalVal *v;
- va_list ap;
- va_start(ap, count);
- while (count > 0) {
- k = va_arg(ap, char*);
- v = va_arg(ap, MalVal*);
- g_hash_table_insert(htable, k, v);
- count = count - 2;
- }
- va_end(ap);
- return hm;
-}
-
-MalVal *_assoc_BANG(MalVal* hm, MalVal *args) {
- assert((_count(args) % 2) == 0,
- "odd number of parameters to assoc!");
- GHashTable *htable = hm->val.hash_table;
- int i;
- MalVal *k, *v;
- for (i=0; i<_count(args); i+=2) {
- k = g_array_index(args->val.array, MalVal*, i);
- assert_type(k, MAL_STRING,
- "assoc! called with non-string key");
- v = g_array_index(args->val.array, MalVal*, i+1);
- g_hash_table_insert(htable, k->val.string, v);
- }
- return hm;
-}
-
-MalVal *_dissoc_BANG(MalVal* hm, MalVal *args) {
- GHashTable *htable = hm->val.hash_table;
- int i;
- MalVal *k, *v;
- for (i=0; i<_count(args); i++) {
- k = g_array_index(args->val.array, MalVal*, i);
- assert_type(k, MAL_STRING,
- "dissoc! called with non-string key");
- g_hash_table_remove(htable, k->val.string);
- }
- return hm;
-}
-
-MalVal *hash_map(MalVal *args) {
- assert_type(args, MAL_LIST|MAL_VECTOR,
- "hash-map called with non-sequential arguments");
- GHashTable *htable = g_hash_table_new(g_str_hash, g_str_equal);
- MalVal *hm = malval_new_hash_map(htable);
- return _assoc_BANG(hm, args);
-}
-
-int _hash_map_Q(MalVal *seq) {
- return seq->type & MAL_HASH_MAP;
-}
-MalVal *hash_map_Q(MalVal *seq) { return _hash_map_Q(seq) ? &mal_true : &mal_false; }
-
-MalVal *assoc(MalVal *args) {
- assert_type(args, MAL_LIST|MAL_VECTOR,
- "assoc called with non-sequential arguments");
- assert(_count(args) >= 2,
- "assoc needs at least 2 arguments");
- GHashTable *htable = g_hash_table_copy(_nth(args,0)->val.hash_table);
- MalVal *hm = malval_new_hash_map(htable);
- return _assoc_BANG(hm, rest(args));
-}
-
-MalVal *dissoc(MalVal* args) {
- GHashTable *htable = g_hash_table_copy(_nth(args,0)->val.hash_table);
- MalVal *hm = malval_new_hash_map(htable);
- return _dissoc_BANG(hm, rest(args));
-}
-
-MalVal *keys(MalVal *obj) {
- assert_type(obj, MAL_HASH_MAP,
- "keys called on non-hash-map");
-
- GHashTableIter iter;
- gpointer key, value;
- MalVal *seq = malval_new_list(MAL_LIST,
- g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),
- _count(obj)));
- g_hash_table_iter_init (&iter, obj->val.hash_table);
- while (g_hash_table_iter_next (&iter, &key, &value)) {
- MalVal *kname = malval_new_string((char *)key);
- g_array_append_val(seq->val.array, kname);
- }
- return seq;
-}
-
-MalVal *vals(MalVal *obj) {
- assert_type(obj, MAL_HASH_MAP,
- "vals called on non-hash-map");
-
- GHashTableIter iter;
- gpointer key, value;
- MalVal *seq = malval_new_list(MAL_LIST,
- g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),
- _count(obj)));
- g_hash_table_iter_init (&iter, obj->val.hash_table);
- while (g_hash_table_iter_next (&iter, &key, &value)) {
- g_array_append_val(seq->val.array, value);
- }
- return seq;
-}
-
-
-// Errors/Exceptions
-void _error(const char *fmt, ...) {
- va_list args;
- va_start(args, fmt);
- mal_error = malval_new_string(g_strdup_vprintf(fmt, args));
-}
-void throw(MalVal *obj) {
- mal_error = obj;
-}
-
// Lists
-
-MalVal *_list(int count, ...) {
+MalVal *_listX(int count, ...) {
MalVal *seq = malval_new_list(MAL_LIST,
g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),
count));
@@ -675,7 +326,8 @@ MalVal *_list(int count, ...) {
va_end(ap);
return seq;
}
-MalVal *list(MalVal *args) {
+
+MalVal *_list(MalVal *args) {
assert_type(args, MAL_LIST|MAL_VECTOR,
"list called with invalid arguments");
args->type = MAL_LIST;
@@ -685,116 +337,68 @@ MalVal *list(MalVal *args) {
int _list_Q(MalVal *seq) {
return seq->type & MAL_LIST;
}
-MalVal *list_Q(MalVal *seq) { return _list_Q(seq) ? &mal_true : &mal_false; }
// Vectors
-
-MalVal *_vector(int count, ...) {
- MalVal *seq = malval_new_list(MAL_VECTOR,
- g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),
- count));
- MalVal *v;
- va_list ap;
- va_start(ap, count);
- while (count-- > 0) {
- v = va_arg(ap, MalVal*);
- g_array_append_val(seq->val.array, v);
- }
- va_end(ap);
- return seq;
-}
-MalVal *vector(MalVal *args) {
+MalVal *_vector(MalVal *args) {
assert_type(args, MAL_LIST|MAL_VECTOR,
"vector called with invalid arguments");
args->type = MAL_VECTOR;
return args;
}
-
int _vector_Q(MalVal *seq) {
return seq->type & MAL_VECTOR;
}
-MalVal *vector_Q(MalVal *seq) { return _vector_Q(seq) ? &mal_true : &mal_false; }
-// hash map and vector functions
-MalVal *get(MalVal *obj, MalVal *key) {
- MalVal *val;
- switch (obj->type) {
- case MAL_VECTOR:
- return _nth(obj, key->val.intnum);
- case MAL_HASH_MAP:
- if (g_hash_table_lookup_extended(obj->val.hash_table,
- key->val.string,
- NULL, (gpointer*)&val)) {
- return val;
- } else {
- return &mal_nil;
- }
- default:
- abort("get called on unsupported type %d", obj->type);
+// Hash maps
+MalVal *_hash_map(MalVal *args) {
+ assert_type(args, MAL_LIST|MAL_VECTOR,
+ "hash-map called with non-sequential arguments");
+ GHashTable *htable = g_hash_table_new(g_str_hash, g_str_equal);
+ MalVal *hm = malval_new_hash_map(htable);
+ return _assoc_BANG(hm, args);
+}
+
+int _hash_map_Q(MalVal *seq) {
+ return seq->type & MAL_HASH_MAP;
+}
+
+MalVal *_assoc_BANG(MalVal* hm, MalVal *args) {
+ assert((_count(args) % 2) == 0,
+ "odd number of parameters to assoc!");
+ GHashTable *htable = hm->val.hash_table;
+ int i;
+ MalVal *k, *v;
+ for (i=0; i<_count(args); i+=2) {
+ k = g_array_index(args->val.array, MalVal*, i);
+ assert_type(k, MAL_STRING,
+ "assoc! called with non-string key");
+ v = g_array_index(args->val.array, MalVal*, i+1);
+ g_hash_table_insert(htable, k->val.string, v);
}
+ return hm;
}
-MalVal *contains_Q(MalVal *obj, MalVal *key) {
- switch (obj->type) {
- case MAL_VECTOR:
- if (key->val.intnum < obj->val.array->len) {
- return &mal_true;
- } else {
- return &mal_false;
- }
- case MAL_HASH_MAP:
- if (g_hash_table_contains(obj->val.hash_table, key->val.string)) {
- return &mal_true;
- } else {
- return &mal_false;
- }
- default:
- abort("contains? called on unsupported type %d", obj->type);
+MalVal *_dissoc_BANG(MalVal* hm, MalVal *args) {
+ GHashTable *htable = hm->val.hash_table;
+ int i;
+ MalVal *k, *v;
+ for (i=0; i<_count(args); i++) {
+ k = g_array_index(args->val.array, MalVal*, i);
+ assert_type(k, MAL_STRING,
+ "dissoc! called with non-string key");
+ g_hash_table_remove(htable, k->val.string);
}
+ return hm;
}
// Atoms
-MalVal *atom(MalVal *val) {
- return malval_new_atom(val);
-}
-
int _atom_Q(MalVal *exp) {
return exp->type & MAL_ATOM;
}
-MalVal *atom_Q(MalVal *exp) { return _atom_Q(exp) ? &mal_true : &mal_false; }
-
-MalVal *deref(MalVal *atm) {
- assert_type(atm, MAL_ATOM,
- "deref called on non-atom");
- return atm->val.atom_val;
-}
-
-MalVal *reset_BANG(MalVal *atm, MalVal *val) {
- assert_type(atm, MAL_ATOM,
- "reset! called with non-atom");
- atm->val.atom_val = val;
- return val;
-}
-
-MalVal *swap_BANG(MalVal *args) {
- assert_type(args, MAL_LIST|MAL_VECTOR,
- "swap! called with invalid arguments");
- assert(_count(args) >= 2,
- "swap! called with %d args, needs at least 2", _count(args));
- MalVal *atm = _nth(args, 0),
- *f = _nth(args, 1),
- *sargs = _slice(args, 2, _count(args)),
- *fargs = cons(atm->val.atom_val, sargs),
- *new_val = apply(f, fargs);
- if (mal_error) { return NULL; }
- atm->val.atom_val = new_val;
- return new_val;
-}
-
// Sequence functions
@@ -813,96 +417,6 @@ MalVal *_slice(MalVal *seq, int start, int end) {
int _sequential_Q(MalVal *seq) {
return seq->type & (MAL_LIST|MAL_VECTOR);
}
-MalVal *sequential_Q(MalVal *seq) {
- return _sequential_Q(seq) ? &mal_true : &mal_false;
-}
-
-MalVal *cons(MalVal *x, MalVal *seq) {
- assert_type(seq, MAL_LIST|MAL_VECTOR,
- "second argument to cons is non-sequential");
- int i, len = _count(seq);
- GArray *new_arr = g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),
- len+1);
- g_array_append_val(new_arr, x);
- for (i=0; i<len; i++) {
- g_array_append_val(new_arr, g_array_index(seq->val.array, MalVal*, i));
- }
- return malval_new_list(MAL_LIST, new_arr);
-}
-
-MalVal *count(MalVal *seq) {
- return malval_new_integer(_count(seq));
-}
-
-MalVal *empty_Q(MalVal *seq) {
- assert_type(seq, MAL_LIST|MAL_VECTOR,
- "empty? called with non-sequential");
- return (seq->val.array->len == 0) ? &mal_true : &mal_false;
-}
-
-MalVal *concat(MalVal *args) {
- MalVal *arg, *e, *lst;
- int i, j, arg_cnt = _count(args);
- lst = malval_new_list(MAL_LIST,
- g_array_sized_new(TRUE, TRUE, sizeof(MalVal*), arg_cnt));
- for (i=0; i<arg_cnt; i++) {
- arg = g_array_index(args->val.array, MalVal*, i);
- assert_type(arg, MAL_LIST|MAL_VECTOR,
- "concat called with non-sequential");
- for (j=0; j<_count(arg); j++) {
- e = g_array_index(arg->val.array, MalVal*, j);
- g_array_append_val(lst->val.array, e);
- }
- }
-
- return lst;
-}
-
-MalVal *sconj(MalVal *args) {
- assert_type(args, MAL_LIST|MAL_VECTOR,
- "conj called with non-sequential");
- MalVal *src_lst = _nth(args, 0);
- assert_type(args, MAL_LIST|MAL_VECTOR,
- "first argument to conj is non-sequential");
- int i, len = _count(src_lst) + _count(args) - 1;
- GArray *new_arr = g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),
- len);
- // Copy in src_lst
- for (i=0; i<_count(src_lst); i++) {
- g_array_append_val(new_arr, g_array_index(src_lst->val.array, MalVal*, i));
- }
- // Conj extra args
- for (i=1; i<_count(args); i++) {
- if (src_lst->type & MAL_LIST) {
- g_array_prepend_val(new_arr, g_array_index(args->val.array, MalVal*, i));
- } else {
- g_array_append_val(new_arr, g_array_index(args->val.array, MalVal*, i));
- }
- }
- return malval_new_list(src_lst->type, new_arr);
-}
-
-MalVal *first(MalVal *seq) {
- assert_type(seq, MAL_LIST|MAL_VECTOR,
- "first called with non-sequential");
- if (_count(seq) == 0) {
- return &mal_nil;
- }
- return g_array_index(seq->val.array, MalVal*, 0);
-}
-
-MalVal *last(MalVal *seq) {
- assert_type(seq, MAL_LIST|MAL_VECTOR,
- "last called with non-sequential");
- if (_count(seq) == 0) {
- return &mal_nil;
- }
- return g_array_index(seq->val.array, MalVal*, _count(seq)-1);
-}
-
-MalVal *rest(MalVal *seq) {
- return _slice(seq, 1, _count(seq));
-}
MalVal *_nth(MalVal *seq, int idx) {
assert_type(seq, MAL_LIST|MAL_VECTOR,
@@ -912,30 +426,6 @@ MalVal *_nth(MalVal *seq, int idx) {
}
return g_array_index(seq->val.array, MalVal*, idx);
}
-MalVal *nth(MalVal *seq, MalVal *idx) {
- return _nth(seq, idx->val.intnum);
-}
-
-MalVal *sapply(MalVal *args) {
- assert_type(args, MAL_LIST|MAL_VECTOR,
- "apply called with non-sequential");
- MalVal *f = _nth(args, 0);
- MalVal *last_arg = _nth(args, _count(args)-1);
- assert_type(last_arg, MAL_LIST|MAL_VECTOR,
- "last argument to apply is non-sequential");
- int i, len = _count(args) - 2 + _count(last_arg);
- GArray *new_arr = g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),
- len);
- // Initial arguments
- for (i=1; i<_count(args)-1; i++) {
- g_array_append_val(new_arr, g_array_index(args->val.array, MalVal*, i));
- }
- // Add arguments from last_arg
- for (i=0; i<_count(last_arg); i++) {
- g_array_append_val(new_arr, g_array_index(last_arg->val.array, MalVal*, i));
- }
- return apply(f, malval_new_list(MAL_LIST, new_arr));
-}
MalVal *_map2(MalVal *(*func)(void*, void*), MalVal *lst, void *arg2) {
MalVal *e, *el;
@@ -951,136 +441,3 @@ MalVal *_map2(MalVal *(*func)(void*, void*), MalVal *lst, void *arg2) {
}
return el;
}
-
-MalVal *map(MalVal *mvf, MalVal *lst) {
- MalVal *res, *el;
- assert_type(mvf, MAL_FUNCTION_C|MAL_FUNCTION_MAL,
- "map called with non-function");
- assert_type(lst, MAL_LIST|MAL_VECTOR,
- "map called with non-sequential");
- int i, len = _count(lst);
- el = malval_new_list(MAL_LIST,
- g_array_sized_new(TRUE, TRUE, sizeof(MalVal*), len));
- for (i=0; i<len; i++) {
- // TODO: this is replicating some of apply functionality
- if (mvf->type & MAL_FUNCTION_MAL) {
- Env *fn_env = new_env(mvf->val.func.env,
- mvf->val.func.args,
- _slice(lst, i, i+1));
- res = mvf->val.func.evaluator(mvf->val.func.body, fn_env);
- } else {
- res = mvf->val.f1(g_array_index(lst->val.array, MalVal*, i));
- }
- if (!res || mal_error) return NULL;
- g_array_append_val(el->val.array, res);
- }
- return el;
-}
-
-
-// Env
-
-Env *new_env(Env *outer, MalVal* binds, MalVal *exprs) {
- Env *e = malloc(sizeof(Env));
- e->table = g_hash_table_new(g_str_hash, g_str_equal);
- e->outer = outer;
-
- if (binds && exprs) {
- assert_type(binds, MAL_LIST|MAL_VECTOR,
- "new_env called with non-sequential bindings");
- assert_type(exprs, MAL_LIST|MAL_VECTOR,
- "new_env called with non-sequential expressions");
- int binds_len = _count(binds),
- exprs_len = _count(exprs),
- varargs = 0, i;
- for (i=0; i<binds_len; i++) {
- if (i > exprs_len) { break; }
- if (_nth(binds, i)->val.string[0] == '&') {
- varargs = 1;
- env_set(e, _nth(binds, i+1)->val.string, _slice(exprs, i, _count(exprs)));
- break;
- } else {
- env_set(e, _nth(binds, i)->val.string, _nth(exprs, i));
- }
- }
- assert(varargs || (binds_len == exprs_len),
- "Arity mismatch: %d formal params vs %d actual params",
- binds_len, exprs_len);
-
- }
- return e;
-}
-
-Env *env_find(Env *env, char *key) {
- void *val = g_hash_table_lookup(env->table, key);
- if (val) {
- return env;
- } else if (env->outer) {
- return env_find(env->outer, key);
- } else {
- return NULL;
- }
-}
-
-MalVal *env_get(Env *env, char *key) {
- Env *e = env_find(env, key);
- assert(e, "'%s' not found", key);
- return g_hash_table_lookup(e->table, key);
-}
-
-Env *env_set(Env *env, char *key, MalVal *val) {
- g_hash_table_insert(env->table, key, val);
- return env;
-}
-
-types_ns_entry types_ns[49] = {
- {"pr-str", (void*(*)(void*))pr_str, -1},
- {"str", (void*(*)(void*))str, -1},
- {"prn", (void*(*)(void*))prn, -1},
- {"println", (void*(*)(void*))println, -1},
- {"with-meta", (void*(*)(void*))with_meta, 2},
- {"meta", (void*(*)(void*))meta, 1},
- {"=", (void*(*)(void*))equal_Q, 2},
- {"symbol?", (void*(*)(void*))symbol_Q, 1},
- {"nil?", (void*(*)(void*))nil_Q, 1},
- {"true?", (void*(*)(void*))true_Q, 1},
- {"false?", (void*(*)(void*))false_Q, 1},
- {"+", (void*(*)(void*))int_plus, 2},
- {"-", (void*(*)(void*))int_minus, 2},
- {"*", (void*(*)(void*))int_multiply, 2},
- {"/", (void*(*)(void*))int_divide, 2},
- {">", (void*(*)(void*))int_gt, 2},
- {">=", (void*(*)(void*))int_gte, 2},
- {"<", (void*(*)(void*))int_lt, 2},
- {"<=", (void*(*)(void*))int_lte, 2},
- {"hash-map", (void*(*)(void*))hash_map, -1},
- {"map?", (void*(*)(void*))hash_map_Q, 1},
- {"assoc", (void*(*)(void*))assoc, -1},
- {"dissoc", (void*(*)(void*))dissoc, -1},
- {"get", (void*(*)(void*))get, 2},
- {"contains?", (void*(*)(void*))contains_Q, 2},
- {"keys", (void*(*)(void*))keys, 1},
- {"vals", (void*(*)(void*))vals, 1},
- {"throw", (void*(*)(void*))throw, 1},
- {"list", (void*(*)(void*))list, -1},
- {"list?", (void*(*)(void*))list_Q, 1},
- {"vector", (void*(*)(void*))vector, -1},
- {"vector?", (void*(*)(void*))vector_Q, 1},
- {"atom", (void*(*)(void*))atom, 1},
- {"atom?", (void*(*)(void*))atom_Q, 1},
- {"deref", (void*(*)(void*))deref, 1},
- {"reset!", (void*(*)(void*))reset_BANG, 2},
- {"swap!", (void*(*)(void*))swap_BANG, -1},
- {"sequential?", (void*(*)(void*))sequential_Q, 1},
- {"cons", (void*(*)(void*))cons, 2},
- {"count", (void*(*)(void*))count, 1},
- {"empty?", (void*(*)(void*))empty_Q, 1},
- {"concat", (void*(*)(void*))concat, -1},
- {"conj", (void*(*)(void*))sconj, -1},
- {"first", (void*(*)(void*))first, 1},
- {"last", (void*(*)(void*))last, 1},
- {"rest", (void*(*)(void*))rest, 1},
- {"nth", (void*(*)(void*))nth, 2},
- {"apply", (void*(*)(void*))sapply, -1},
- {"map", (void*(*)(void*))map, 2},
- };
diff --git a/c/types.h b/c/types.h
index 271a899..d65e4ef 100644
--- a/c/types.h
+++ b/c/types.h
@@ -3,10 +3,32 @@
#include <glib.h>
-// State
struct MalVal; // pre-declare
+
+
+// Env (implentation in env.c)
+
+typedef struct Env {
+ struct Env *outer;
+ GHashTable *table;
+} Env;
+
+Env *new_env(Env *outer, struct MalVal* binds, struct MalVal *exprs);
+Env *env_find(Env *env, char *key);
+struct MalVal *env_get(Env *env, char *key);
+Env *env_set(Env *env, char *key, struct MalVal *val);
+
+
+// Utility functiosn
+void g_hash_table_print(GHashTable *hash_table);
+GHashTable *g_hash_table_copy(GHashTable *src_table);
+
+
+// Errors/exceptions
+
extern struct MalVal *mal_error;
+void _error(const char *fmt, ...);
#define abort(format, ...) \
{ _error(format, ##__VA_ARGS__); return NULL; }
@@ -23,6 +45,7 @@ extern struct MalVal *mal_error;
return NULL; \
}
+
typedef enum {
MAL_NIL = 1,
MAL_TRUE = 2,
@@ -39,10 +62,6 @@ typedef enum {
MAL_FUNCTION_MAL = 4096,
} MalType;
-
-// Predeclare Env
-typedef struct Env Env;
-
typedef struct MalVal {
MalType type;
struct MalVal *metadata;
@@ -112,51 +131,24 @@ MalVal *malval_new_float(gdouble val);
MalVal *malval_new_string(char *val);
MalVal *malval_new_symbol(char *val);
MalVal *malval_new_list(MalType type, GArray *val);
+MalVal *malval_new_hash_map(GHashTable *val);
+MalVal *malval_new_atom(MalVal *val);
MalVal *malval_new_function(void *(*func)(void *), int arg_cnt, MalVal* metadata);
-MalVal *hash_map(MalVal *args);
-void _error(const char *fmt, ...);
-MalVal *_list(int count, ...);
+MalVal *_listX(int count, ...);
+MalVal *_list(MalVal *args);
+MalVal *_vector(MalVal *args);
+MalVal *_hash_map(MalVal *args);
+MalVal *_assoc_BANG(MalVal* hm, MalVal *args);
+MalVal *_dissoc_BANG(MalVal* hm, MalVal *args);
-MalVal *apply(MalVal *f, MalVal *el);
+MalVal *_apply(MalVal *f, MalVal *el);
char *_pr_str(MalVal *args, int print_readably);
-MalVal *first(MalVal* seq);
-MalVal *last(MalVal* seq);
MalVal *_slice(MalVal *seq, int start, int end);
MalVal *_nth(MalVal *seq, int idx);
-MalVal *rest(MalVal *seq);
MalVal *_map2(MalVal *(*func)(void*, void*), MalVal *lst, void *arg2);
-// These are just used by step2 and step3 before then type_ns environment is
-// imported
-
-MalVal *int_plus(MalVal *a, MalVal *b);
-MalVal *int_minus(MalVal *a, MalVal *b);
-MalVal *int_multiply(MalVal *a, MalVal *b);
-MalVal *int_divide(MalVal *a, MalVal *b);
-
-// Env
-
-typedef struct Env {
- struct Env *outer;
- GHashTable *table;
-} Env;
-
-Env *new_env(Env *outer, MalVal* binds, MalVal *exprs);
-Env *env_find(Env *env, char *key);
-MalVal *env_get(Env *env, char *key);
-Env *env_set(Env *env, char *key, MalVal *val);
-
-// namespace of type functions
-typedef struct {
- char *name;
- void *(*func)(void*);
- int arg_cnt;
-} types_ns_entry;
-
-extern types_ns_entry types_ns[49];
-
#endif
diff --git a/clojure/Makefile b/clojure/Makefile
index d18eb50..4bc289e 100644
--- a/clojure/Makefile
+++ b/clojure/Makefile
@@ -1,7 +1,8 @@
TESTS =
-SOURCES = src/types.clj src/readline.clj src/reader.clj src/stepA_more.clj
+SOURCES = src/readline.clj src/reader.clj src/printer.clj \
+ src/env.clj src/core.clj src/stepA_more.clj
all:
diff --git a/clojure/src/core.clj b/clojure/src/core.clj
new file mode 100644
index 0000000..23dfc55
--- /dev/null
+++ b/clojure/src/core.clj
@@ -0,0 +1,63 @@
+(ns core)
+
+;; Errors/exceptions
+(defn mal_throw [obj]
+ (throw (ex-info "mal exception" {:data obj})))
+
+;; Atoms
+(defn atom? [atm]
+ (= (type atm) clojure.lang.Atom))
+
+;; core_ns is core namespaces functions
+(def core_ns
+ [['= =]
+ ['throw mal_throw]
+ ['nil? nil?]
+ ['true? true?]
+ ['false? false?]
+ ['symbol? symbol?]
+ ['pr-str pr-str]
+ ['str str]
+ ['prn prn]
+ ['println println]
+ ['< <]
+ ['<= <=]
+ ['> >]
+ ['>= >=]
+ ['+ +]
+ ['- -]
+ ['* *]
+ ['/ /]
+
+ ['list list]
+ ['list? seq?]
+ ['vector vector]
+ ['vector? vector?]
+ ['hash-map hash-map]
+ ['map? 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 #(doall (map %1 %2))]
+
+ ['with-meta with-meta]
+ ['meta meta]
+ ['atom atom]
+ ['atom? atom?]
+ ['deref deref]
+ ['reset! reset!]
+ ['swap! swap!]])
diff --git a/clojure/src/env.clj b/clojure/src/env.clj
new file mode 100644
index 0000000..7f1f934
--- /dev/null
+++ b/clojure/src/env.clj
@@ -0,0 +1,35 @@
+(ns env)
+
+(defn env [& [outer binds exprs]]
+ ;;(prn "env" binds exprs)
+ ;; (when (not= (count binds) (count exprs))
+ ;; (throw (Exception. "Arity mistmatch in env call")))
+ (atom
+ (loop [env {:outer outer}
+ b binds
+ e exprs]
+ (cond
+ (= nil b)
+ env
+
+ (= '& (first b))
+ (assoc env (nth b 1) e)
+
+ :else
+ (recur (assoc env (first b) (first e)) (next b) (next e))))))
+
+(defn env-find [env k]
+ (cond
+ (contains? @env k) env
+ (:outer @env) (env-find (:outer @env) k)
+ :else nil))
+
+(defn env-get [env k]
+ (let [e (env-find env k)]
+ (when-not e
+ (throw (Exception. (str "'" k "' not found"))))
+ (get @e k)))
+
+(defn env-set [env k v]
+ (swap! env assoc k v)
+ v)
diff --git a/clojure/src/printer.clj b/clojure/src/printer.clj
new file mode 100644
index 0000000..656914f
--- /dev/null
+++ b/clojure/src/printer.clj
@@ -0,0 +1,7 @@
+(ns printer)
+
+(defmethod clojure.core/print-method clojure.lang.Atom [a writer]
+ (.write writer "(atom ")
+ (.write writer (pr-str @a))
+ (.write writer ")"))
+
diff --git a/clojure/src/step1_read_print.clj b/clojure/src/step1_read_print.clj
index a99a0ed..31b2ac6 100644
--- a/clojure/src/step1_read_print.clj
+++ b/clojure/src/step1_read_print.clj
@@ -1,8 +1,8 @@
(ns step1-read-print
(:require [clojure.repl]
- [types]
[readline]
- [reader]))
+ [reader]
+ [printer]))
;; read
(defn READ [& [strng]]
diff --git a/clojure/src/step2_eval.clj b/clojure/src/step2_eval.clj
index 6ff9eb3..581bf3f 100644
--- a/clojure/src/step2_eval.clj
+++ b/clojure/src/step2_eval.clj
@@ -1,10 +1,8 @@
(ns step2-eval
(:require [clojure.repl]
- [types]
[readline]
- [reader]))
-
-(declare EVAL)
+ [reader]
+ [printer]))
;; read
(defn READ [& [strng]]
@@ -12,6 +10,7 @@
(reader/read-string strng)))
;; eval
+(declare EVAL)
(defn eval-ast [ast env]
(cond
(symbol? ast) (or (get env ast)
diff --git a/clojure/src/step3_env.clj b/clojure/src/step3_env.clj
index c0c4e8e..449bf79 100644
--- a/clojure/src/step3_env.clj
+++ b/clojure/src/step3_env.clj
@@ -1,10 +1,9 @@
(ns step3-env
(:require [clojure.repl]
- [types]
[readline]
- [reader]))
-
-(declare EVAL)
+ [reader]
+ [printer]
+ [env]))
;; read
(defn READ [& [strng]]
@@ -12,9 +11,10 @@
(reader/read-string strng)))
;; eval
+(declare EVAL)
(defn eval-ast [ast env]
(cond
- (symbol? ast) (types/env-get env ast)
+ (symbol? ast) (env/env-get env ast)
(seq? ast) (doall (map #(EVAL % env) ast))
@@ -34,12 +34,12 @@
(let [[a0 a1 a2 a3] ast]
(condp = a0
'def!
- (types/env-set env a1 (EVAL a2 env))
+ (env/env-set env a1 (EVAL a2 env))
'let*
- (let [let-env (types/env env)]
+ (let [let-env (env/env env)]
(doseq [[b e] (partition 2 a1)]
- (types/env-set let-env b (EVAL e let-env)))
+ (env/env-set let-env b (EVAL e let-env)))
(EVAL a2 let-env))
;; apply
@@ -52,12 +52,12 @@
(defn PRINT [exp] (pr-str exp))
;; repl
-(def repl-env (types/env))
+(def repl-env (env/env))
(defn rep
[strng]
(PRINT (EVAL (READ strng), repl-env)))
-(defn _ref [k,v] (types/env-set repl-env k v))
+(defn _ref [k,v] (env/env-set repl-env k v))
(_ref '+ +)
(_ref '- -)
(_ref '* *)
diff --git a/clojure/src/step4_if_fn_do.clj b/clojure/src/step4_if_fn_do.clj
index 4171848..b36f482 100644
--- a/clojure/src/step4_if_fn_do.clj
+++ b/clojure/src/step4_if_fn_do.clj
@@ -1,10 +1,10 @@
(ns step4-if-fn-do
(:require [clojure.repl]
- [types]
[readline]
- [reader]))
-
-(declare EVAL)
+ [reader]
+ [printer]
+ [env]
+ [core]))
;; read
(defn READ [& [strng]]
@@ -12,9 +12,10 @@
(reader/read-string strng)))
;; eval
+(declare EVAL)
(defn eval-ast [ast env]
(cond
- (symbol? ast) (types/env-get env ast)
+ (symbol? ast) (env/env-get env ast)
(seq? ast) (doall (map #(EVAL % env) ast))
@@ -34,12 +35,12 @@
(let [[a0 a1 a2 a3] ast]
(condp = a0
'def!
- (types/env-set env a1 (EVAL a2 env))
+ (env/env-set env a1 (EVAL a2 env))
'let*
- (let [let-env (types/env env)]
+ (let [let-env (env/env env)]
(doseq [[b e] (partition 2 a1)]
- (types/env-set let-env b (EVAL e let-env)))
+ (env/env-set let-env b (EVAL e let-env)))
(EVAL a2 let-env))
'do
@@ -55,7 +56,7 @@
'fn*
(fn [& args]
- (EVAL a2 (types/env env a1 args)))
+ (EVAL a2 (env/env env a1 args)))
;; apply
(let [el (eval-ast ast env)
@@ -67,15 +68,15 @@
(defn PRINT [exp] (pr-str exp))
;; repl
-(def repl-env (types/env))
+(def repl-env (env/env))
(defn rep
[strng]
(PRINT (EVAL (READ strng), repl-env)))
-(defn _ref [k,v] (types/env-set repl-env k v))
+(defn _ref [k,v] (env/env-set repl-env k v))
;; Import types related functions
-(doseq [[k v] types/types_ns] (_ref k v))
+(doseq [[k v] core/core_ns] (_ref k v))
;; Defined using the language itself
(rep "(def! not (fn* [a] (if a false true)))")
diff --git a/clojure/src/step5_tco.clj b/clojure/src/step5_tco.clj
index 2ed07b4..7739029 100644
--- a/clojure/src/step5_tco.clj
+++ b/clojure/src/step5_tco.clj
@@ -1,10 +1,10 @@
(ns step5-tco
(:require [clojure.repl]
- [types]
[readline]
- [reader]))
-
-(declare EVAL)
+ [reader]
+ [printer]
+ [env]
+ [core]))
;; read
(defn READ [& [strng]]
@@ -12,9 +12,10 @@
(reader/read-string strng)))
;; eval
+(declare EVAL)
(defn eval-ast [ast env]
(cond
- (symbol? ast) (types/env-get env ast)
+ (symbol? ast) (env/env-get env ast)
(seq? ast) (doall (map #(EVAL % env) ast))
@@ -36,12 +37,12 @@
(let [[a0 a1 a2 a3] ast]
(condp = a0
'def!
- (types/env-set env a1 (EVAL a2 env))
+ (env/env-set env a1 (EVAL a2 env))
'let*
- (let [let-env (types/env env)]
+ (let [let-env (env/env env)]
(doseq [[b e] (partition 2 a1)]
- (types/env-set let-env b (EVAL e let-env)))
+ (env/env-set let-env b (EVAL e let-env)))
(EVAL a2 let-env))
'do
@@ -61,7 +62,7 @@
:environment env
:parameters a1}
(fn [& args]
- (EVAL a2 (types/env env a1 args)))
+ (EVAL a2 (env/env env a1 args)))
;; apply
(let [el (eval-ast ast env)
@@ -69,22 +70,22 @@
args (rest el)
{:keys [expression environment parameters]} (meta f)]
(if expression
- (recur expression (types/env environment parameters args))
+ (recur expression (env/env environment parameters args))
(apply f args))))))))
;; print
(defn PRINT [exp] (pr-str exp))
;; repl
-(def repl-env (types/env))
+(def repl-env (env/env))
(defn rep
[strng]
(PRINT (EVAL (READ strng), repl-env)))
-(defn _ref [k,v] (types/env-set repl-env k v))
+(defn _ref [k,v] (env/env-set repl-env k v))
;; Import types related functions
-(doseq [[k v] types/types_ns] (_ref k v))
+(doseq [[k v] core/core_ns] (_ref k v))
;; Defined using the language itself
(rep "(def! not (fn* [a] (if a false true)))")
diff --git a/clojure/src/step6_file.clj b/clojure/src/step6_file.clj
index 2eb4e3d..a65b741 100644
--- a/clojure/src/step6_file.clj
+++ b/clojure/src/step6_file.clj
@@ -1,10 +1,10 @@
(ns step6-file
(:require [clojure.repl]
- [types]
[readline]
- [reader]))
-
-(declare EVAL)
+ [reader]
+ [printer]
+ [env]
+ [core]))
;; read
(defn READ [& [strng]]
@@ -12,9 +12,10 @@
(reader/read-string strng)))
;; eval
+(declare EVAL)
(defn eval-ast [ast env]
(cond
- (symbol? ast) (types/env-get env ast)
+ (symbol? ast) (env/env-get env ast)
(seq? ast) (doall (map #(EVAL % env) ast))
@@ -36,12 +37,12 @@
(let [[a0 a1 a2 a3] ast]
(condp = a0
'def!
- (types/env-set env a1 (EVAL a2 env))
+ (env/env-set env a1 (EVAL a2 env))
'let*
- (let [let-env (types/env env)]
+ (let [let-env (env/env env)]
(doseq [[b e] (partition 2 a1)]
- (types/env-set let-env b (EVAL e let-env)))
+ (env/env-set let-env b (EVAL e let-env)))
(EVAL a2 let-env))
'do
@@ -61,7 +62,7 @@
:environment env
:parameters a1}
(fn [& args]
- (EVAL a2 (types/env env a1 args)))
+ (EVAL a2 (env/env env a1 args)))
;; apply
(let [el (eval-ast ast env)
@@ -69,22 +70,22 @@
args (rest el)
{:keys [expression environment parameters]} (meta f)]
(if expression
- (recur expression (types/env environment parameters args))
+ (recur expression (env/env environment parameters args))
(apply f args))))))))
;; print
(defn PRINT [exp] (pr-str exp))
;; repl
-(def repl-env (types/env))
+(def repl-env (env/env))
(defn rep
[strng]
- (PRINT (EVAL (READ strng), repl-env)))
+ (PRINT (EVAL (READ strng) repl-env)))
-(defn _ref [k,v] (types/env-set repl-env k v))
+(defn _ref [k,v] (env/env-set repl-env k v))
;; Import types related functions
-(doseq [[k v] types/types_ns] (_ref k v))
+(doseq [[k v] core/core_ns] (_ref k v))
;; Defined using the language itself
(_ref 'read-string reader/read-string)
diff --git a/clojure/src/step7_quote.clj b/clojure/src/step7_quote.clj
index 398b7d2..e68a4cc 100644
--- a/clojure/src/step7_quote.clj
+++ b/clojure/src/step7_quote.clj
@@ -1,10 +1,10 @@
(ns step7-quote
(:require [clojure.repl]
- [types]
[readline]
- [reader]))
-
-(declare EVAL)
+ [reader]
+ [printer]
+ [env]
+ [core]))
;; read
(defn READ [& [strng]]
@@ -12,6 +12,7 @@
(reader/read-string strng)))
;; eval
+(declare EVAL)
(defn is-pair [x]
(and (sequential? x) (> (count x) 0)))
@@ -31,7 +32,7 @@
(defn eval-ast [ast env]
(cond
- (symbol? ast) (types/env-get env ast)
+ (symbol? ast) (env/env-get env ast)
(seq? ast) (doall (map #(EVAL % env) ast))
@@ -53,12 +54,12 @@
(let [[a0 a1 a2 a3] ast]
(condp = a0
'def!
- (types/env-set env a1 (EVAL a2 env))
+ (env/env-set env a1 (EVAL a2 env))
'let*
- (let [let-env (types/env env)]
+ (let [let-env (env/env env)]
(doseq [[b e] (partition 2 a1)]
- (types/env-set let-env b (EVAL e let-env)))
+ (env/env-set let-env b (EVAL e let-env)))
(EVAL a2 let-env))
'quote
@@ -84,7 +85,7 @@
:environment env
:parameters a1}
(fn [& args]
- (EVAL a2 (types/env env a1 args)))
+ (EVAL a2 (env/env env a1 args)))
;; apply
(let [el (eval-ast ast env)
@@ -92,22 +93,22 @@
args (rest el)
{:keys [expression environment parameters]} (meta f)]
(if expression
- (recur expression (types/env environment parameters args))
+ (recur expression (env/env environment parameters args))
(apply f args))))))))
;; print
(defn PRINT [exp] (pr-str exp))
;; repl
-(def repl-env (types/env))
+(def repl-env (env/env))
(defn rep
[strng]
(PRINT (EVAL (READ strng) repl-env)))
-(defn _ref [k,v] (types/env-set repl-env k v))
+(defn _ref [k,v] (env/env-set repl-env k v))
;; Import types related functions
-(doseq [[k v] types/types_ns] (_ref k v))
+(doseq [[k v] core/core_ns] (_ref k v))
;; Defined using the language itself
(_ref 'read-string reader/read-string)
diff --git a/clojure/src/step8_macros.clj b/clojure/src/step8_macros.clj
index 6b696e7..ed5638b 100644
--- a/clojure/src/step8_macros.clj
+++ b/clojure/src/step8_macros.clj
@@ -1,11 +1,11 @@
(ns step8-macros
(:refer-clojure :exclude [macroexpand])
(:require [clojure.repl]
- [types]
[readline]
- [reader]))
-
-(declare EVAL)
+ [reader]
+ [printer]
+ [env]
+ [core]))
;; read
(defn READ [& [strng]]
@@ -13,6 +13,7 @@
(reader/read-string strng)))
;; eval
+(declare EVAL)
(defn is-pair [x]
(and (sequential? x) (> (count x) 0)))
@@ -33,19 +34,19 @@
(defn is-macro-call [ast env]
(and (seq? ast)
(symbol? (first ast))
- (types/env-find env (first ast))
- (:ismacro (meta (types/env-get env (first ast))))))
+ (env/env-find env (first ast))
+ (:ismacro (meta (env/env-get env (first ast))))))
(defn macroexpand [ast env]
(loop [ast ast]
(if (is-macro-call ast env)
- (let [mac (types/env-get env (first ast))]
+ (let [mac (env/env-get env (first ast))]
(recur (apply mac (rest ast))))
ast)))
(defn eval-ast [ast env]
(cond
- (symbol? ast) (types/env-get env ast)
+ (symbol? ast) (env/env-get env ast)
(seq? ast) (doall (map #(EVAL % env) ast))
@@ -71,12 +72,12 @@
(let [[a0 a1 a2 a3] ast]
(condp = a0
'def!
- (types/env-set env a1 (EVAL a2 env))
+ (env/env-set env a1 (EVAL a2 env))
'let*
- (let [let-env (types/env env)]
+ (let [let-env (env/env env)]
(doseq [[b e] (partition 2 a1)]
- (types/env-set let-env b (EVAL e let-env)))
+ (env/env-set let-env b (EVAL e let-env)))
(EVAL a2 let-env))
'quote
@@ -88,7 +89,7 @@
'defmacro!
(let [func (with-meta (EVAL a2 env)
{:ismacro true})]
- (types/env-set env a1 func))
+ (env/env-set env a1 func))
'macroexpand
(macroexpand a1 env)
@@ -110,7 +111,7 @@
:environment env
:parameters a1}
(fn [& args]
- (EVAL a2 (types/env env a1 args)))
+ (EVAL a2 (env/env env a1 args)))
;; apply
(let [el (eval-ast ast env)
@@ -118,22 +119,22 @@
args (rest el)
{:keys [expression environment parameters]} (meta f)]
(if expression
- (recur expression (types/env environment parameters args))
+ (recur expression (env/env environment parameters args))
(apply f args))))))))))
;; print
(defn PRINT [exp] (pr-str exp))
;; repl
-(def repl-env (types/env))
+(def repl-env (env/env))
(defn rep
[strng]
(PRINT (EVAL (READ strng) repl-env)))
-(defn _ref [k,v] (types/env-set repl-env k v))
+(defn _ref [k,v] (env/env-set repl-env k v))
;; Import types related functions
-(doseq [[k v] types/types_ns] (_ref k v))
+(doseq [[k v] core/core_ns] (_ref k v))
;; Defined using the language itself
(_ref 'read-string reader/read-string)
diff --git a/clojure/src/step9_interop.clj b/clojure/src/step9_interop.clj
index d78aed7..da37574 100644
--- a/clojure/src/step9_interop.clj
+++ b/clojure/src/step9_interop.clj
@@ -1,11 +1,11 @@
(ns step9-interop
(:refer-clojure :exclude [macroexpand])
(:require [clojure.repl]
- [types]
[readline]
- [reader]))
-
-(declare EVAL)
+ [reader]
+ [printer]
+ [env]
+ [core]))
;; read
(defn READ [& [strng]]
@@ -13,6 +13,7 @@
(reader/read-string strng)))
;; eval
+(declare EVAL)
(defn is-pair [x]
(and (sequential? x) (> (count x) 0)))
@@ -33,19 +34,19 @@
(defn is-macro-call [ast env]
(and (seq? ast)
(symbol? (first ast))
- (types/env-find env (first ast))
- (:ismacro (meta (types/env-get env (first ast))))))
+ (env/env-find env (first ast))
+ (:ismacro (meta (env/env-get env (first ast))))))
(defn macroexpand [ast env]
(loop [ast ast]
(if (is-macro-call ast env)
- (let [mac (types/env-get env (first ast))]
+ (let [mac (env/env-get env (first ast))]
(recur (apply mac (rest ast))))
ast)))
(defn eval-ast [ast env]
(cond
- (symbol? ast) (types/env-get env ast)
+ (symbol? ast) (env/env-get env ast)
(seq? ast) (doall (map #(EVAL % env) ast))
@@ -71,12 +72,12 @@
(let [[a0 a1 a2 a3] ast]
(condp = a0
'def!
- (types/env-set env a1 (EVAL a2 env))
+ (env/env-set env a1 (EVAL a2 env))
'let*
- (let [let-env (types/env env)]
+ (let [let-env (env/env env)]
(doseq [[b e] (partition 2 a1)]
- (types/env-set let-env b (EVAL e let-env)))
+ (env/env-set let-env b (EVAL e let-env)))
(EVAL a2 let-env))
'quote
@@ -88,7 +89,7 @@
'defmacro!
(let [func (with-meta (EVAL a2 env)
{:ismacro true})]
- (types/env-set env a1 func))
+ (env/env-set env a1 func))
'macroexpand
(macroexpand a1 env)
@@ -113,7 +114,7 @@
:environment env
:parameters a1}
(fn [& args]
- (EVAL a2 (types/env env a1 args)))
+ (EVAL a2 (env/env env a1 args)))
;; apply
(let [el (eval-ast ast env)
@@ -121,22 +122,22 @@
args (rest el)
{:keys [expression environment parameters]} (meta f)]
(if expression
- (recur expression (types/env environment parameters args))
+ (recur expression (env/env environment parameters args))
(apply f args))))))))))
;; print
(defn PRINT [exp] (pr-str exp))
;; repl
-(def repl-env (types/env))
+(def repl-env (env/env))
(defn rep
[strng]
(PRINT (EVAL (READ strng) repl-env)))
-(defn _ref [k,v] (types/env-set repl-env k v))
+(defn _ref [k,v] (env/env-set repl-env k v))
;; Import types related functions
-(doseq [[k v] types/types_ns] (_ref k v))
+(doseq [[k v] core/core_ns] (_ref k v))
;; Defined using the language itself
(_ref 'read-string reader/read-string)
diff --git a/clojure/src/stepA_more.clj b/clojure/src/stepA_more.clj
index d5d80bf..77b8cd7 100644
--- a/clojure/src/stepA_more.clj
+++ b/clojure/src/stepA_more.clj
@@ -1,11 +1,11 @@
(ns stepA-more
(:refer-clojure :exclude [macroexpand])
(:require [clojure.repl]
- [types]
[readline]
- [reader]))
-
-(declare EVAL)
+ [reader]
+ [printer]
+ [env]
+ [core]))
;; read
(defn READ [& [strng]]
@@ -13,6 +13,7 @@
(reader/read-string strng)))
;; eval
+(declare EVAL)
(defn is-pair [x]
(and (sequential? x) (> (count x) 0)))
@@ -33,19 +34,19 @@
(defn is-macro-call [ast env]
(and (seq? ast)
(symbol? (first ast))
- (types/env-find env (first ast))
- (:ismacro (meta (types/env-get env (first ast))))))
+ (env/env-find env (first ast))
+ (:ismacro (meta (env/env-get env (first ast))))))
(defn macroexpand [ast env]
(loop [ast ast]
(if (is-macro-call ast env)
- (let [mac (types/env-get env (first ast))]
+ (let [mac (env/env-get env (first ast))]
(recur (apply mac (rest ast))))
ast)))
(defn eval-ast [ast env]
(cond
- (symbol? ast) (types/env-get env ast)
+ (symbol? ast) (env/env-get env ast)
(seq? ast) (doall (map #(EVAL % env) ast))
@@ -71,12 +72,12 @@
(let [[a0 a1 a2 a3] ast]
(condp = a0
'def!
- (types/env-set env a1 (EVAL a2 env))
+ (env/env-set env a1 (EVAL a2 env))
'let*
- (let [let-env (types/env env)]
+ (let [let-env (env/env env)]
(doseq [[b e] (partition 2 a1)]
- (types/env-set let-env b (EVAL e let-env)))
+ (env/env-set let-env b (EVAL e let-env)))
(EVAL a2 let-env))
'quote
@@ -88,7 +89,7 @@
'defmacro!
(let [func (with-meta (EVAL a2 env)
{:ismacro true})]
- (types/env-set env a1 func))
+ (env/env-set env a1 func))
'macroexpand
(macroexpand a1 env)
@@ -101,13 +102,13 @@
(try
(EVAL a1 env)
(catch clojure.lang.ExceptionInfo ei
- (EVAL (nth a2 2) (types/env env
- [(nth a2 1)]
- [(:data (ex-data ei))])))
+ (EVAL (nth a2 2) (env/env env
+ [(nth a2 1)]
+ [(:data (ex-data ei))])))
(catch Throwable t
- (EVAL (nth a2 2) (types/env env
- [(nth a2 1)]
- [(.getMessage t)]))))
+ (EVAL (nth a2 2) (env/env env
+ [(nth a2 1)]
+ [(.getMessage t)]))))
(EVAL a1 env))
'do
@@ -127,7 +128,7 @@
:environment env
:parameters a1}
(fn [& args]
- (EVAL a2 (types/env env a1 args)))
+ (EVAL a2 (env/env env a1 args)))
;; apply
(let [el (eval-ast ast env)
@@ -135,22 +136,22 @@
args (rest el)
{:keys [expression environment parameters]} (meta f)]
(if expression
- (recur expression (types/env environment parameters args))
+ (recur expression (env/env environment parameters args))
(apply f args))))))))))
;; print
(defn PRINT [exp] (pr-str exp))
;; repl
-(def repl-env (types/env))
+(def repl-env (env/env))
(defn rep
[strng]
(PRINT (EVAL (READ strng) repl-env)))
-(defn _ref [k,v] (types/env-set repl-env k v))
+(defn _ref [k,v] (env/env-set repl-env k v))
;; Import types related functions
-(doseq [[k v] types/types_ns] (_ref k v))
+(doseq [[k v] core/core_ns] (_ref k v))
;; Defined using the language itself
(_ref 'readline readline/readline)
diff --git a/clojure/src/types.clj b/clojure/src/types.clj
deleted file mode 100644
index 922cf79..0000000
--- a/clojure/src/types.clj
+++ /dev/null
@@ -1,71 +0,0 @@
-(ns types)
-
-;; Custom printing
-
-(defmethod clojure.core/print-method clojure.lang.Atom [a writer]
- (.write writer "(atom ")
- (.write writer (pr-str @a))
- (.write writer ")"))
-
-;; Errors/exceptions
-(defn mal_throw [obj]
- (throw (ex-info "mal exception" {:data obj})))
-
-
-;; Atoms
-(defn atom? [atm]
- (= (type atm) clojure.lang.Atom))
-
-
-;; env
-
-(defn env [& [outer binds exprs]]
- ;;(prn "env" binds exprs)
- ;; (when (not= (count binds) (count exprs))
- ;; (throw (Exception. "Arity mistmatch in env call")))
- (atom
- (loop [env {:outer outer}
- b binds
- e exprs]
- (cond
- (= nil b)
- env
-
- (= '& (first b))
- (assoc env (nth b 1) e)
-
- :else
- (recur (assoc env (first b) (first e)) (next b) (next e))))))
-
-(defn env-find [env k]
- (cond
- (contains? @env k) env
- (:outer @env) (env-find (:outer @env) k)
- :else nil))
-
-(defn env-get [env k]
- (let [e (env-find env k)]
- (when-not e
- (throw (Exception. (str "'" k "' not found"))))
- (get @e k)))
-
-(defn env-set [env k v]
- (swap! env assoc k v)
- v)
-
-(def types_ns
- [['pr-str pr-str] ['str str] ['prn prn] ['println println]
- ['with-meta with-meta] ['meta meta] ['= =]
- ['nil? nil?] ['true? true?] ['false? false?] ['symbol? symbol?]
- ['> >] ['>= >=] ['< <] ['<= <=] ['+ +] ['- -] ['* *] ['/ /]
- ['hash-map hash-map] ['map? map?]
- ['assoc assoc] ['dissoc dissoc] ['get get]
- ['contains? contains?] ['keys keys] ['vals vals]
- ['throw mal_throw]
- ['list list] ['list? seq?] ['vector vector] ['vector? vector?]
- ['atom atom] ['atom? atom?] ['deref deref]
- ['reset! reset!] ['swap! swap!]
- ['sequential? sequential?] ['cons cons] ['nth nth]
- ['empty? empty?] ['count count] ['concat concat]
- ['conj conj] ['first first] ['rest rest]
- ['apply apply] ['map #(doall (map %1 %2))]])
diff --git a/java/Makefile b/java/Makefile
index 8559a05..e6312a3 100644
--- a/java/Makefile
+++ b/java/Makefile
@@ -2,8 +2,10 @@
TESTS =
-SOURCES = src/main/java/mal/types.java src/main/java/mal/readline.java \
- src/main/java/mal/reader.java src/main/java/mal/stepA_more.java
+SOURCES = src/main/java/mal/readline.java src/main/java/mal/types.java \
+ src/main/java/mal/reader.java src/main/java/mal/printer.java \
+ src/main/java/mal/env.java src/main/java/mal/core.java \
+ src/main/java/mal/stepA_more.java
#.PHONY: stats tests $(TESTS)
.PHONY: stats
diff --git a/java/src/main/java/mal/core.java b/java/src/main/java/mal/core.java
new file mode 100644
index 0000000..3200c0e
--- /dev/null
+++ b/java/src/main/java/mal/core.java
@@ -0,0 +1,470 @@
+package mal;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.Map;
+import java.util.HashMap;
+import com.google.common.collect.ImmutableMap;
+
+import mal.types.*;
+import mal.printer;
+
+public class core {
+ // Local references for convenience
+ static MalConstant Nil = mal.types.Nil;
+ static MalConstant True = mal.types.True;
+ static MalConstant False = mal.types.False;
+
+
+ // Errors/Exceptions
+ static MalFunction mal_throw = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ throw new MalException(a.nth(0));
+ }
+ };
+
+
+ // Scalar functions
+ static MalFunction nil_Q = new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return args.nth(0) == Nil ? True : False;
+ }
+ };
+
+ static MalFunction true_Q = new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return args.nth(0) == True ? True : False;
+ }
+ };
+
+ static MalFunction false_Q = new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return args.nth(0) == False ? True : False;
+ }
+ };
+ static MalFunction symbol_Q = new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return args.nth(0) instanceof MalSymbol ? True : False;
+ }
+ };
+
+
+ // String functions
+ static MalFunction pr_str = new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return new MalString(printer._pr_str_args(args, " ", true));
+ }
+ };
+
+ static MalFunction str = new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return new MalString(printer._pr_str_args(args, "", false));
+ }
+ };
+
+ static MalFunction prn = new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ System.out.println(printer._pr_str_args(args, " ", true));
+ return Nil;
+ }
+ };
+
+ static MalFunction println = new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ System.out.println(printer._pr_str_args(args, " ", false));
+ return Nil;
+ }
+ };
+
+
+ static MalFunction equal_Q = new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return types._equal_Q(args.nth(0), args.nth(1)) ? True : False;
+ }
+ };
+
+
+ // Number functions
+ static MalFunction add = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return ((MalInteger)a.nth(0)).add((MalInteger)a.nth(1));
+ }
+ };
+ static MalFunction subtract = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return ((MalInteger)a.nth(0)).subtract((MalInteger)a.nth(1));
+ }
+ };
+ static MalFunction multiply = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return ((MalInteger)a.nth(0)).multiply((MalInteger)a.nth(1));
+ }
+ };
+ static MalFunction divide = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return ((MalInteger)a.nth(0)).divide((MalInteger)a.nth(1));
+ }
+ };
+
+ static MalFunction lt = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return ((MalInteger)a.nth(0)).lt((MalInteger)a.nth(1));
+ }
+ };
+ static MalFunction lte = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return ((MalInteger)a.nth(0)).lte((MalInteger)a.nth(1));
+ }
+ };
+ static MalFunction gt = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return ((MalInteger)a.nth(0)).gt((MalInteger)a.nth(1));
+ }
+ };
+ static MalFunction gte = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return ((MalInteger)a.nth(0)).gte((MalInteger)a.nth(1));
+ }
+ };
+
+
+ // List functions
+ static MalFunction new_list = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return new MalList(a.value);
+ }
+ };
+
+ static public Boolean _list_Q(MalVal mv) {
+ return mv.getClass().equals(MalList.class);
+ }
+ static MalFunction list_Q = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return _list_Q(a.nth(0)) ? True : False;
+ }
+ };
+
+
+ // Vector functions
+ static MalFunction new_vector = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return new MalVector(a.value);
+ }
+ };
+
+ static public Boolean _vector_Q(MalVal mv) {
+ return mv.getClass().equals(MalVector.class);
+ }
+ static MalFunction vector_Q = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return _vector_Q(a.nth(0)) ? True : False;
+ }
+ };
+
+ //
+ // Hash map operations
+ //
+ static MalFunction new_hash_map = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return new MalHashMap(a);
+ }
+ };
+ static MalFunction hash_map_Q = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return a.nth(0) instanceof MalHashMap ? True : False;
+ }
+ };
+
+ static MalFunction contains_Q = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ String key = ((MalString)a.nth(1)).getValue();
+ MalHashMap mhm = (MalHashMap)a.nth(0);
+ HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;
+ return hm.containsKey(key) ? True : False;
+ }
+ };
+
+ static MalFunction assoc = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ MalHashMap mhm = (MalHashMap)a.nth(0);
+ HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;
+ MalHashMap new_mhm = new MalHashMap((Map)hm.clone());
+ new_mhm.assoc_BANG((MalList)a.slice(1));
+ return new_mhm;
+ }
+ };
+
+ static MalFunction dissoc = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ MalHashMap mhm = (MalHashMap)a.nth(0);
+ HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;
+ MalHashMap new_mhm = new MalHashMap((Map)hm.clone());
+ new_mhm.dissoc_BANG((MalList)a.slice(1));
+ return new_mhm;
+ }
+ };
+
+ static MalFunction get = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ String key = ((MalString)a.nth(1)).getValue();
+ MalHashMap mhm = (MalHashMap)a.nth(0);
+ HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;
+ if (hm.containsKey(key)) {
+ return hm.get(key);
+ } else {
+ return Nil;
+ }
+ }
+ };
+
+ static MalFunction keys = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ MalHashMap mhm = (MalHashMap)a.nth(0);
+ HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;
+ MalList key_lst = new MalList();
+ for (String key : hm.keySet()) {
+ key_lst.conj_BANG(new MalString(key));
+ }
+ return key_lst;
+ }
+ };
+
+ static MalFunction vals = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ MalHashMap mhm = (MalHashMap)a.nth(0);
+ HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;
+ //return new ArrayList<MalVal>(((HashMap<String,MalVal>)hm).values());
+ MalList val_lst = new MalList();
+ for (MalVal val : hm.values()) {
+ val_lst.conj_BANG(val);
+ }
+ return val_lst;
+ }
+ };
+
+
+ // Sequence functions
+ static MalFunction sequential_Q = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return a.nth(0) instanceof MalList ? True : False;
+ }
+ };
+
+ static MalFunction count = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return new MalInteger(((MalList)a.nth(0)).size());
+ }
+ };
+
+ static MalFunction empty_Q = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ MalVal exp = a.nth(0);
+ if (exp == Nil || (exp instanceof MalList &&
+ ((MalList)exp).size() == 0)) {
+ return True;
+ } else {
+ return False;
+ }
+ }
+ };
+
+ static MalFunction cons = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ MalList lst = new MalList();
+ lst.value.addAll(((MalList)a.nth(1)).value);
+ lst.value.add(0, a.nth(0));
+ return (MalVal) lst;
+ }
+ };
+
+ static MalFunction concat = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ if (a.size() == 0) { return new MalList(); }
+ MalList lst = new MalList();
+ lst.value.addAll(((MalList)a.nth(0)).value);
+ for(Integer i=1; i<a.size(); i++) {
+ lst.value.addAll(((MalList)a.nth(i)).value);
+ }
+ return (MalVal) lst;
+ }
+ };
+
+ static MalFunction conj = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ MalList src_seq = (MalList)a.nth(0), new_seq;
+ if (a.nth(0) instanceof MalVector) {
+ new_seq = new MalVector();
+ new_seq.value.addAll(src_seq.value);
+ for(Integer i=1; i<a.size(); i++) {
+ new_seq.value.add(a.nth(i));
+ }
+ } else {
+ new_seq = new MalList();
+ new_seq.value.addAll(src_seq.value);
+ for(Integer i=1; i<a.size(); i++) {
+ new_seq.value.add(0, a.nth(i));
+ }
+ }
+ return (MalVal) new_seq;
+ }
+ };
+
+ static MalFunction first = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ MalList ml = ((MalList)a.nth(0));
+ return ml.size() > 0 ? ml.nth(0) : Nil;
+ }
+ };
+
+ static MalFunction rest = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ MalList ml = ((MalList)a.nth(0));
+ return ml.rest();
+ }
+ };
+
+ static MalFunction nth = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ Integer idx = ((MalInteger)a.nth(1)).getValue();
+ return ((MalList)a.nth(0)).nth(idx);
+ }
+ };
+
+ // General list related functions
+ static MalFunction apply = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ MalFunction f = (MalFunction)a.nth(0);
+ MalList args = a.slice(1,a.size()-1);
+ args.value.addAll( ((MalList)a.nth(a.size()-1)).value);
+ return f.apply(args);
+ }
+ };
+
+ static MalFunction map = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ MalFunction f = (MalFunction) a.nth(0);
+ MalList src_lst = (MalList) a.nth(1);
+ MalList new_lst = new MalList();
+ for(Integer i=0; i<src_lst.size(); i++) {
+ new_lst.value.add(
+ f.apply(new MalList(src_lst.nth(i))));
+ }
+ return new_lst;
+ }
+ };
+
+
+ // Metadata functions
+
+ static MalFunction meta = new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return args.nth(0).getMeta();
+ }
+ };
+
+ static MalFunction with_meta = new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ MalVal new_mv = ((MalVal)args.nth(0)).copy();
+ new_mv.setMeta(args.nth(1));
+ return new_mv;
+ }
+ };
+
+
+ // Atom functions
+ static MalFunction new_atom = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return new MalAtom(a.nth(0));
+ }
+ };
+
+ static MalFunction atom_Q = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return a.nth(0) instanceof MalAtom ? True : False;
+ }
+ };
+
+ static MalFunction deref = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return ((MalAtom)a.nth(0)).value;
+ }
+ };
+
+ static MalFunction reset_BANG = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return ((MalAtom)a.nth(0)).value = a.nth(1);
+ }
+ };
+
+ static MalFunction swap_BANG = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ MalAtom atm = (MalAtom)a.nth(0);
+ MalFunction f = (MalFunction)a.nth(1);
+ MalList new_args = new MalList();
+ new_args.value.addAll(((MalList)a.slice(2)).value);
+ new_args.value.add(0, atm.value);
+ atm.value = f.apply(new_args);
+ return atm.value;
+ }
+ };
+
+
+
+
+
+ // types_ns is namespace of type functions
+ static Map<String, MalVal> ns = ImmutableMap.<String, MalVal>builder()
+ .put("=", equal_Q)
+ .put("throw", mal_throw)
+ .put("nil?", nil_Q)
+ .put("true?", true_Q)
+ .put("false?", false_Q)
+ .put("symbol?", symbol_Q)
+ .put("pr-str", pr_str)
+ .put("str", str)
+ .put("prn", prn)
+ .put("println", println)
+ .put("<", lt)
+ .put("<=", lte)
+ .put(">", gt)
+ .put(">=", gte)
+ .put("+", add)
+ .put("-", subtract)
+ .put("*", multiply)
+ .put("/", divide)
+
+ .put("list", new_list)
+ .put("list?", list_Q)
+ .put("vector", new_vector)
+ .put("vector?", vector_Q)
+ .put("hash-map", new_hash_map)
+ .put("map?", hash_map_Q)
+ .put("assoc", assoc)
+ .put("dissoc", dissoc)
+ .put("contains?", contains_Q)
+ .put("get", get)
+ .put("keys", keys)
+ .put("vals", vals)
+
+ .put("sequential?", sequential_Q)
+ .put("cons", cons)
+ .put("concat", concat)
+ .put("nth", nth)
+ .put("first", first)
+ .put("rest", rest)
+ .put("empty?", empty_Q)
+ .put("count", count)
+ .put("conj", conj)
+ .put("apply", apply)
+ .put("map", map)
+
+ .put("with-meta", with_meta)
+ .put("meta", meta)
+ .put("atom", new_atom)
+ .put("atom?", atom_Q)
+ .put("deref", deref)
+ .put("reset!", reset_BANG)
+ .put("swap!", swap_BANG)
+ .build();
+}
diff --git a/java/src/main/java/mal/env.java b/java/src/main/java/mal/env.java
new file mode 100644
index 0000000..8a1913e
--- /dev/null
+++ b/java/src/main/java/mal/env.java
@@ -0,0 +1,57 @@
+package mal;
+
+import java.util.HashMap;
+
+import mal.types.MalThrowable;
+import mal.types.MalException;
+import mal.types.MalVal;
+import mal.types.MalSymbol;
+import mal.types.MalList;
+
+public class env {
+ public static class Env {
+ Env outer = null;
+ HashMap<String,MalVal> data = new HashMap<String,MalVal>();
+
+ public Env(Env outer) {
+ this.outer = outer;
+ }
+ public Env(Env outer, MalList binds, MalList exprs) {
+ this.outer = outer;
+ for (Integer i=0; i<binds.size(); i++) {
+ String sym = ((MalSymbol)binds.nth(i)).getName();
+ if (sym.equals("&")) {
+ data.put(((MalSymbol)binds.nth(i+1)).getName(),
+ exprs.slice(i));
+ break;
+ } else {
+ data.put(sym, exprs.nth(i));
+ }
+ }
+ }
+
+ public Env find(String key) {
+ if (data.containsKey(key)) {
+ return this;
+ } else if (outer != null) {
+ return outer.find(key);
+ } else {
+ return null;
+ }
+ }
+
+ public MalVal get(String key) throws MalThrowable {
+ Env e = find(key);
+ if (e == null) {
+ throw new MalException("'" + key + "' not found");
+ } else {
+ return e.data.get(key);
+ }
+ }
+
+ public Env set(String key, MalVal value) {
+ data.put(key, value);
+ return this;
+ }
+ }
+}
diff --git a/java/src/main/java/mal/printer.java b/java/src/main/java/mal/printer.java
new file mode 100644
index 0000000..73dfca3
--- /dev/null
+++ b/java/src/main/java/mal/printer.java
@@ -0,0 +1,50 @@
+package mal;
+
+import java.util.List;
+import java.util.ArrayList;
+import com.google.common.base.Joiner;
+import java.util.Map;
+import org.apache.commons.lang3.StringEscapeUtils;
+
+import mal.types.MalVal;
+import mal.types.MalList;
+
+public class printer {
+
+ public static String join(List<MalVal> value,
+ String delim, Boolean print_readably) {
+ ArrayList<String> strs = new ArrayList<String>();
+ for (MalVal mv : value) {
+ strs.add(mv.toString(print_readably));
+ }
+ return Joiner.on(delim).join(strs);
+ }
+
+ public static String join(Map<String,MalVal> value,
+ String delim, Boolean print_readably) {
+ ArrayList<String> strs = new ArrayList<String>();
+ for (Map.Entry<String, MalVal> entry : value.entrySet()) {
+ if (print_readably) {
+ strs.add("\"" + entry.getKey().toString() + "\"");
+ } else {
+ strs.add(entry.getKey().toString());
+ }
+ strs.add(entry.getValue().toString(print_readably));
+ }
+ return Joiner.on(" ").join(strs);
+ }
+
+ public static String _pr_str(MalVal mv,
+ Boolean print_readably) {
+ return mv.toString(print_readably);
+ }
+
+ public static String _pr_str_args(MalList args,
+ String sep, Boolean print_readably) {
+ return join(args.getList(), sep, print_readably);
+ }
+
+ public static String escapeString(String value) {
+ return StringEscapeUtils.escapeJson(value);
+ }
+}
diff --git a/java/src/main/java/mal/step1_read_print.java b/java/src/main/java/mal/step1_read_print.java
index 447afc5..3f7e3fb 100644
--- a/java/src/main/java/mal/step1_read_print.java
+++ b/java/src/main/java/mal/step1_read_print.java
@@ -5,6 +5,7 @@ import java.io.IOException;
import mal.types.*;
import mal.readline;
import mal.reader;
+import mal.printer;
public class step1_read_print {
// read
@@ -19,7 +20,7 @@ public class step1_read_print {
// print
public static String PRINT(MalVal exp) {
- return types._pr_str(exp, true);
+ return printer._pr_str(exp, true);
}
// REPL
@@ -30,7 +31,7 @@ public class step1_read_print {
public static void main(String[] args) throws MalThrowable {
String prompt = "user> ";
- if (args[0].equals("--raw")) {
+ if (args.length > 0 && args[0].equals("--raw")) {
readline.mode = readline.Mode.JAVA;
}
while (true) {
diff --git a/java/src/main/java/mal/step2_eval.java b/java/src/main/java/mal/step2_eval.java
index e1b30a9..3d425db 100644
--- a/java/src/main/java/mal/step2_eval.java
+++ b/java/src/main/java/mal/step2_eval.java
@@ -9,6 +9,7 @@ import java.util.Iterator;
import mal.types.*;
import mal.readline;
import mal.reader;
+import mal.printer;
public class step2_eval {
// read
@@ -23,8 +24,8 @@ public class step2_eval {
return (MalVal)env.get(sym.getName());
} else if (ast instanceof MalList) {
MalList old_lst = (MalList)ast;
- MalList new_lst = types._list_Q(ast) ? new MalList()
- : (MalList)new MalVector();
+ MalList new_lst = ast.list_Q() ? new MalList()
+ : (MalList)new MalVector();
for (MalVal mv : (List<MalVal>)old_lst.value) {
new_lst.conj_BANG(EVAL(mv, env));
}
@@ -44,8 +45,8 @@ public class step2_eval {
public static MalVal EVAL(MalVal orig_ast, HashMap env) throws MalThrowable {
MalVal a0;
- //System.out.println("EVAL: " + types._pr_str(orig_ast, true));
- if (!(types._list_Q(orig_ast))) {
+ //System.out.println("EVAL: " + printer._pr_str(orig_ast, true));
+ if (!orig_ast.list_Q()) {
return eval_ast(orig_ast, env);
}
@@ -55,17 +56,20 @@ public class step2_eval {
a0 = ast.nth(0);
if (!(a0 instanceof MalSymbol)) {
throw new MalError("attempt to apply on non-symbol '"
- + types._pr_str(a0,true) + "'");
+ + printer._pr_str(a0,true) + "'");
}
- MalVal args = eval_ast(types._rest(ast), env);
+ MalVal args = eval_ast(ast.rest(), env);
MalSymbol fsym = (MalSymbol)a0;
ILambda f = (ILambda)env.get(fsym.getName());
+ if (f == null) {
+ throw new MalError("'" + fsym.getName() + "' not found");
+ }
return f.apply((MalList)args);
}
// print
public static String PRINT(MalVal exp) {
- return types._pr_str(exp, true);
+ return printer._pr_str(exp, true);
}
// REPL
@@ -110,7 +114,7 @@ public class step2_eval {
repl_env.put("*", new multiply());
repl_env.put("/", new divide());
- if (args[0].equals("--raw")) {
+ if (args.length > 0 && args[0].equals("--raw")) {
readline.mode = readline.Mode.JAVA;
}
while (true) {
diff --git a/java/src/main/java/mal/step3_env.java b/java/src/main/java/mal/step3_env.java
index 867dba1..690469e 100644
--- a/java/src/main/java/mal/step3_env.java
+++ b/java/src/main/java/mal/step3_env.java
@@ -9,6 +9,9 @@ import java.util.Iterator;
import mal.types.*;
import mal.readline;
import mal.reader;
+import mal.printer;
+import mal.env.Env;
+import mal.core;
public class step3_env {
// read
@@ -23,8 +26,8 @@ public class step3_env {
return env.get(sym.getName());
} else if (ast instanceof MalList) {
MalList old_lst = (MalList)ast;
- MalList new_lst = types._list_Q(ast) ? new MalList()
- : (MalList)new MalVector();
+ MalList new_lst = ast.list_Q() ? new MalList()
+ : (MalList)new MalVector();
for (MalVal mv : (List<MalVal>)old_lst.value) {
new_lst.conj_BANG(EVAL(mv, env));
}
@@ -44,8 +47,8 @@ public class step3_env {
public static MalVal EVAL(MalVal orig_ast, Env env) throws MalThrowable {
MalVal a0, a1,a2, res;
- //System.out.println("EVAL: " + types._pr_str(orig_ast, true));
- if (!(types._list_Q(orig_ast))) {
+ //System.out.println("EVAL: " + printer._pr_str(orig_ast, true));
+ if (!orig_ast.list_Q()) {
return eval_ast(orig_ast, env);
}
@@ -55,7 +58,7 @@ public class step3_env {
a0 = ast.nth(0);
if (!(a0 instanceof MalSymbol)) {
throw new MalError("attempt to apply on non-symbol '"
- + types._pr_str(a0,true) + "'");
+ + printer._pr_str(a0,true) + "'");
}
switch (((MalSymbol)a0).getName()) {
@@ -78,7 +81,7 @@ public class step3_env {
}
return EVAL(a2, let_env);
default:
- MalVal args = eval_ast(types._rest(ast), env);
+ MalVal args = eval_ast(ast.rest(), env);
MalSymbol fsym = (MalSymbol)a0;
ILambda f = (ILambda)env.get(fsym.getName());
return f.apply((MalList)args);
@@ -87,7 +90,7 @@ public class step3_env {
// print
public static String PRINT(MalVal exp) {
- return types._pr_str(exp, true);
+ return printer._pr_str(exp, true);
}
// REPL
@@ -102,12 +105,12 @@ public class step3_env {
String prompt = "user> ";
Env repl_env = new Env(null);
- _ref(repl_env, "+", types.add);
- _ref(repl_env, "-", types.subtract);
- _ref(repl_env, "*", types.multiply);
- _ref(repl_env, "/", types.divide);
+ _ref(repl_env, "+", core.add);
+ _ref(repl_env, "-", core.subtract);
+ _ref(repl_env, "*", core.multiply);
+ _ref(repl_env, "/", core.divide);
- if (args[0].equals("--raw")) {
+ if (args.length > 0 && args[0].equals("--raw")) {
readline.mode = readline.Mode.JAVA;
}
while (true) {
diff --git a/java/src/main/java/mal/step4_if_fn_do.java b/java/src/main/java/mal/step4_if_fn_do.java
index 7501b50..4d064cf 100644
--- a/java/src/main/java/mal/step4_if_fn_do.java
+++ b/java/src/main/java/mal/step4_if_fn_do.java
@@ -9,6 +9,9 @@ import java.util.Iterator;
import mal.types.*;
import mal.readline;
import mal.reader;
+import mal.printer;
+import mal.env.Env;
+import mal.core;
public class step4_if_fn_do {
// read
@@ -23,8 +26,8 @@ public class step4_if_fn_do {
return env.get(sym.getName());
} else if (ast instanceof MalList) {
MalList old_lst = (MalList)ast;
- MalList new_lst = types._list_Q(ast) ? new MalList()
- : (MalList)new MalVector();
+ MalList new_lst = ast.list_Q() ? new MalList()
+ : (MalList)new MalVector();
for (MalVal mv : (List<MalVal>)old_lst.value) {
new_lst.conj_BANG(EVAL(mv, env));
}
@@ -45,8 +48,8 @@ public class step4_if_fn_do {
public static MalVal EVAL(MalVal orig_ast, Env env) throws MalThrowable {
MalVal a0, a1,a2, a3, res;
MalList el;
- //System.out.println("EVAL: " + types._pr_str(orig_ast, true));
- if (!(types._list_Q(orig_ast))) {
+ //System.out.println("EVAL: " + printer._pr_str(orig_ast, true));
+ if (!orig_ast.list_Q()) {
return eval_ast(orig_ast, env);
}
@@ -76,7 +79,7 @@ public class step4_if_fn_do {
}
return EVAL(a2, let_env);
case "do":
- el = (MalList)eval_ast(types._rest(ast), env);
+ el = (MalList)eval_ast(ast.rest(), env);
return el.nth(el.size()-1);
case "if":
a1 = ast.nth(1);
@@ -106,13 +109,13 @@ public class step4_if_fn_do {
default:
el = (MalList)eval_ast(ast, env);
MalFunction f = (MalFunction)el.nth(0);
- return f.apply(types._rest(el));
+ return f.apply(el.rest());
}
}
// print
public static String PRINT(MalVal exp) {
- return types._pr_str(exp, true);
+ return printer._pr_str(exp, true);
}
// REPL
@@ -127,13 +130,13 @@ public class step4_if_fn_do {
String prompt = "user> ";
Env repl_env = new Env(null);
- for (String key : types.types_ns.keySet()) {
- _ref(repl_env, key, types.types_ns.get(key));
+ for (String key : core.ns.keySet()) {
+ _ref(repl_env, key, core.ns.get(key));
}
RE(repl_env, "(def! not (fn* (a) (if a false true)))");
- if (args[0].equals("--raw")) {
+ if (args.length > 0 && args[0].equals("--raw")) {
readline.mode = readline.Mode.JAVA;
}
while (true) {
diff --git a/java/src/main/java/mal/step5_tco.java b/java/src/main/java/mal/step5_tco.java
index 41b295b..43b93c6 100644
--- a/java/src/main/java/mal/step5_tco.java
+++ b/java/src/main/java/mal/step5_tco.java
@@ -9,6 +9,9 @@ import java.util.Iterator;
import mal.types.*;
import mal.readline;
import mal.reader;
+import mal.printer;
+import mal.env.Env;
+import mal.core;
public class step5_tco {
// read
@@ -23,8 +26,8 @@ public class step5_tco {
return env.get(sym.getName());
} else if (ast instanceof MalList) {
MalList old_lst = (MalList)ast;
- MalList new_lst = types._list_Q(ast) ? new MalList()
- : (MalList)new MalVector();
+ MalList new_lst = ast.list_Q() ? new MalList()
+ : (MalList)new MalVector();
for (MalVal mv : (List<MalVal>)old_lst.value) {
new_lst.conj_BANG(EVAL(mv, env));
}
@@ -43,20 +46,20 @@ public class step5_tco {
}
public static MalVal EVAL(MalVal orig_ast, Env env) throws MalThrowable {
- MalVal a1,a2, a3, res;
+ MalVal a0, a1,a2, a3, res;
MalList el;
while (true) {
- //System.out.println("EVAL: " + types._pr_str(orig_ast, true));
- if (!(types._list_Q(orig_ast))) {
+ //System.out.println("EVAL: " + printer._pr_str(orig_ast, true));
+ if (!orig_ast.list_Q()) {
return eval_ast(orig_ast, env);
}
// apply list
MalList ast = (MalList)orig_ast;
if (ast.size() == 0) { return ast; }
- MalVal a0 = ast.nth(0);
+ a0 = ast.nth(0);
String a0sym = a0 instanceof MalSymbol ? ((MalSymbol)a0).getName()
: "__<*fn*>__";
switch (a0sym) {
@@ -101,7 +104,7 @@ public class step5_tco {
final MalList a1f = (MalList)ast.nth(1);
final MalVal a2f = ast.nth(2);
final Env cur_env = env;
- return new MalFunction (a2f, (mal.types.Env)env, a1f) {
+ return new MalFunction (a2f, (mal.env.Env)env, a1f) {
public MalVal apply(MalList args) throws MalThrowable {
return EVAL(a2f, new Env(cur_env, a1f, args));
}
@@ -114,7 +117,7 @@ public class step5_tco {
orig_ast = fnast;
env = new Env(f.getEnv(), f.getParams(), el.slice(1));
} else {
- return f.apply(types._rest(el));
+ return f.apply(el.rest());
}
}
@@ -123,7 +126,7 @@ public class step5_tco {
// print
public static String PRINT(MalVal exp) {
- return types._pr_str(exp, true);
+ return printer._pr_str(exp, true);
}
// REPL
@@ -138,13 +141,13 @@ public class step5_tco {
String prompt = "user> ";
Env repl_env = new Env(null);
- for (String key : types.types_ns.keySet()) {
- _ref(repl_env, key, types.types_ns.get(key));
+ for (String key : core.ns.keySet()) {
+ _ref(repl_env, key, core.ns.get(key));
}
RE(repl_env, "(def! not (fn* (a) (if a false true)))");
- if (args[0].equals("--raw")) {
+ if (args.length > 0 && args[0].equals("--raw")) {
readline.mode = readline.Mode.JAVA;
}
while (true) {
diff --git a/java/src/main/java/mal/step6_file.java b/java/src/main/java/mal/step6_file.java
index 65d9d38..0a1a99b 100644
--- a/java/src/main/java/mal/step6_file.java
+++ b/java/src/main/java/mal/step6_file.java
@@ -12,6 +12,9 @@ import java.util.Iterator;
import mal.types.*;
import mal.readline;
import mal.reader;
+import mal.printer;
+import mal.env.Env;
+import mal.core;
public class step6_file {
// read
@@ -26,8 +29,8 @@ public class step6_file {
return env.get(sym.getName());
} else if (ast instanceof MalList) {
MalList old_lst = (MalList)ast;
- MalList new_lst = types._list_Q(ast) ? new MalList()
- : (MalList)new MalVector();
+ MalList new_lst = ast.list_Q() ? new MalList()
+ : (MalList)new MalVector();
for (MalVal mv : (List<MalVal>)old_lst.value) {
new_lst.conj_BANG(EVAL(mv, env));
}
@@ -46,20 +49,20 @@ public class step6_file {
}
public static MalVal EVAL(MalVal orig_ast, Env env) throws MalThrowable {
- MalVal a1,a2, a3, res;
+ MalVal a0, a1,a2, a3, res;
MalList el;
while (true) {
- //System.out.println("EVAL: " + types._pr_str(orig_ast, true));
- if (!(types._list_Q(orig_ast))) {
+ //System.out.println("EVAL: " + printer._pr_str(orig_ast, true));
+ if (!orig_ast.list_Q()) {
return eval_ast(orig_ast, env);
}
// apply list
MalList ast = (MalList)orig_ast;
if (ast.size() == 0) { return ast; }
- MalVal a0 = ast.nth(0);
+ a0 = ast.nth(0);
String a0sym = a0 instanceof MalSymbol ? ((MalSymbol)a0).getName()
: "__<*fn*>__";
switch (a0sym) {
@@ -104,7 +107,7 @@ public class step6_file {
final MalList a1f = (MalList)ast.nth(1);
final MalVal a2f = ast.nth(2);
final Env cur_env = env;
- return new MalFunction (a2f, (mal.types.Env)env, a1f) {
+ return new MalFunction (a2f, (mal.env.Env)env, a1f) {
public MalVal apply(MalList args) throws MalThrowable {
return EVAL(a2f, new Env(cur_env, a1f, args));
}
@@ -117,7 +120,7 @@ public class step6_file {
orig_ast = fnast;
env = new Env(f.getEnv(), f.getParams(), el.slice(1));
} else {
- return f.apply(types._rest(el));
+ return f.apply(el.rest());
}
}
@@ -126,7 +129,7 @@ public class step6_file {
// print
public static String PRINT(MalVal exp) {
- return types._pr_str(exp, true);
+ return printer._pr_str(exp, true);
}
// REPL
@@ -149,8 +152,8 @@ public class step6_file {
String prompt = "user> ";
final Env repl_env = new Env(null);
- for (String key : types.types_ns.keySet()) {
- _ref(repl_env, key, types.types_ns.get(key));
+ for (String key : core.ns.keySet()) {
+ _ref(repl_env, key, core.ns.get(key));
}
_ref(repl_env, "read-string", new MalFunction() {
public MalVal apply(MalList args) throws MalThrowable {
diff --git a/java/src/main/java/mal/step7_quote.java b/java/src/main/java/mal/step7_quote.java
index 2a09d3e..7105671 100644
--- a/java/src/main/java/mal/step7_quote.java
+++ b/java/src/main/java/mal/step7_quote.java
@@ -12,6 +12,9 @@ import java.util.Iterator;
import mal.types.*;
import mal.readline;
import mal.reader;
+import mal.printer;
+import mal.env.Env;
+import mal.core;
public class step7_quote {
// read
@@ -38,12 +41,12 @@ public class step7_quote {
(((MalSymbol)a00).getName() == "splice-unquote")) {
return new MalList(new MalSymbol("concat"),
((MalList)a0).nth(1),
- quasiquote(types._rest((MalList)ast)));
+ quasiquote(((MalList)ast).rest()));
}
}
return new MalList(new MalSymbol("cons"),
quasiquote(a0),
- quasiquote(types._rest((MalList)ast)));
+ quasiquote(((MalList)ast).rest()));
}
}
@@ -53,8 +56,8 @@ public class step7_quote {
return env.get(sym.getName());
} else if (ast instanceof MalList) {
MalList old_lst = (MalList)ast;
- MalList new_lst = types._list_Q(ast) ? new MalList()
- : (MalList)new MalVector();
+ MalList new_lst = ast.list_Q() ? new MalList()
+ : (MalList)new MalVector();
for (MalVal mv : (List<MalVal>)old_lst.value) {
new_lst.conj_BANG(EVAL(mv, env));
}
@@ -73,20 +76,20 @@ public class step7_quote {
}
public static MalVal EVAL(MalVal orig_ast, Env env) throws MalThrowable {
- MalVal a1,a2, a3, res;
+ MalVal a0, a1,a2, a3, res;
MalList el;
while (true) {
- //System.out.println("EVAL: " + types._pr_str(orig_ast, true));
- if (!(types._list_Q(orig_ast))) {
+ //System.out.println("EVAL: " + printer._pr_str(orig_ast, true));
+ if (!orig_ast.list_Q()) {
return eval_ast(orig_ast, env);
}
// apply list
MalList ast = (MalList)orig_ast;
if (ast.size() == 0) { return ast; }
- MalVal a0 = ast.nth(0);
+ a0 = ast.nth(0);
String a0sym = a0 instanceof MalSymbol ? ((MalSymbol)a0).getName()
: "__<*fn*>__";
switch (a0sym) {
@@ -135,7 +138,7 @@ public class step7_quote {
final MalList a1f = (MalList)ast.nth(1);
final MalVal a2f = ast.nth(2);
final Env cur_env = env;
- return new MalFunction (a2f, (mal.types.Env)env, a1f) {
+ return new MalFunction (a2f, (mal.env.Env)env, a1f) {
public MalVal apply(MalList args) throws MalThrowable {
return EVAL(a2f, new Env(cur_env, a1f, args));
}
@@ -148,7 +151,7 @@ public class step7_quote {
orig_ast = fnast;
env = new Env(f.getEnv(), f.getParams(), el.slice(1));
} else {
- return f.apply(types._rest(el));
+ return f.apply(el.rest());
}
}
@@ -157,7 +160,7 @@ public class step7_quote {
// print
public static String PRINT(MalVal exp) {
- return types._pr_str(exp, true);
+ return printer._pr_str(exp, true);
}
// REPL
@@ -180,8 +183,8 @@ public class step7_quote {
String prompt = "user> ";
final Env repl_env = new Env(null);
- for (String key : types.types_ns.keySet()) {
- _ref(repl_env, key, types.types_ns.get(key));
+ for (String key : core.ns.keySet()) {
+ _ref(repl_env, key, core.ns.get(key));
}
_ref(repl_env, "read-string", new MalFunction() {
public MalVal apply(MalList args) throws MalThrowable {
diff --git a/java/src/main/java/mal/step8_macros.java b/java/src/main/java/mal/step8_macros.java
index 47732dd..c834119 100644
--- a/java/src/main/java/mal/step8_macros.java
+++ b/java/src/main/java/mal/step8_macros.java
@@ -12,6 +12,9 @@ import java.util.Iterator;
import mal.types.*;
import mal.readline;
import mal.reader;
+import mal.printer;
+import mal.env.Env;
+import mal.core;
public class step8_macros {
// read
@@ -38,12 +41,12 @@ public class step8_macros {
(((MalSymbol)a00).getName() == "splice-unquote")) {
return new MalList(new MalSymbol("concat"),
((MalList)a0).nth(1),
- quasiquote(types._rest((MalList)ast)));
+ quasiquote(((MalList)ast).rest()));
}
}
return new MalList(new MalSymbol("cons"),
quasiquote(a0),
- quasiquote(types._rest((MalList)ast)));
+ quasiquote(((MalList)ast).rest()));
}
}
@@ -68,7 +71,7 @@ public class step8_macros {
while (is_macro_call(ast, env)) {
MalSymbol a0 = (MalSymbol)((MalList)ast).nth(0);
MalFunction mac = (MalFunction) env.get(a0.getName());
- ast = mac.apply(types._rest((MalList)ast));
+ ast = mac.apply(((MalList)ast).rest());
}
return ast;
}
@@ -79,8 +82,8 @@ public class step8_macros {
return env.get(sym.getName());
} else if (ast instanceof MalList) {
MalList old_lst = (MalList)ast;
- MalList new_lst = types._list_Q(ast) ? new MalList()
- : (MalList)new MalVector();
+ MalList new_lst = ast.list_Q() ? new MalList()
+ : (MalList)new MalVector();
for (MalVal mv : (List<MalVal>)old_lst.value) {
new_lst.conj_BANG(EVAL(mv, env));
}
@@ -99,22 +102,22 @@ public class step8_macros {
}
public static MalVal EVAL(MalVal orig_ast, Env env) throws MalThrowable {
- MalVal a1,a2, a3, res;
+ MalVal a0, a1,a2, a3, res;
MalList el;
while (true) {
- //System.out.println("EVAL: " + types._pr_str(orig_ast, true));
- if (!(types._list_Q(orig_ast))) {
+ //System.out.println("EVAL: " + printer._pr_str(orig_ast, true));
+ if (!orig_ast.list_Q()) {
return eval_ast(orig_ast, env);
}
// apply list
MalVal expanded = macroexpand(orig_ast, env);
- if (!types._list_Q(expanded)) { return expanded; }
+ if (!expanded.list_Q()) { return expanded; }
MalList ast = (MalList) expanded;
if (ast.size() == 0) { return ast; }
- MalVal a0 = ast.nth(0);
+ a0 = ast.nth(0);
String a0sym = a0 instanceof MalSymbol ? ((MalSymbol)a0).getName()
: "__<*fn*>__";
switch (a0sym) {
@@ -173,7 +176,7 @@ public class step8_macros {
final MalList a1f = (MalList)ast.nth(1);
final MalVal a2f = ast.nth(2);
final Env cur_env = env;
- return new MalFunction (a2f, (mal.types.Env)env, a1f) {
+ return new MalFunction (a2f, (mal.env.Env)env, a1f) {
public MalVal apply(MalList args) throws MalThrowable {
return EVAL(a2f, new Env(cur_env, a1f, args));
}
@@ -186,7 +189,7 @@ public class step8_macros {
orig_ast = fnast;
env = new Env(f.getEnv(), f.getParams(), el.slice(1));
} else {
- return f.apply(types._rest(el));
+ return f.apply(el.rest());
}
}
@@ -195,7 +198,7 @@ public class step8_macros {
// print
public static String PRINT(MalVal exp) {
- return types._pr_str(exp, true);
+ return printer._pr_str(exp, true);
}
// REPL
@@ -218,8 +221,8 @@ public class step8_macros {
String prompt = "user> ";
final Env repl_env = new Env(null);
- for (String key : types.types_ns.keySet()) {
- _ref(repl_env, key, types.types_ns.get(key));
+ for (String key : core.ns.keySet()) {
+ _ref(repl_env, key, core.ns.get(key));
}
_ref(repl_env, "read-string", new MalFunction() {
public MalVal apply(MalList args) throws MalThrowable {
diff --git a/java/src/main/java/mal/stepA_more.java b/java/src/main/java/mal/stepA_more.java
index d3bb161..9f4fb07 100644
--- a/java/src/main/java/mal/stepA_more.java
+++ b/java/src/main/java/mal/stepA_more.java
@@ -14,6 +14,9 @@ import java.util.Iterator;
import mal.types.*;
import mal.readline;
import mal.reader;
+import mal.printer;
+import mal.env.Env;
+import mal.core;
public class stepA_more {
// read
@@ -40,12 +43,12 @@ public class stepA_more {
(((MalSymbol)a00).getName() == "splice-unquote")) {
return new MalList(new MalSymbol("concat"),
((MalList)a0).nth(1),
- quasiquote(types._rest((MalList)ast)));
+ quasiquote(((MalList)ast).rest()));
}
}
return new MalList(new MalSymbol("cons"),
quasiquote(a0),
- quasiquote(types._rest((MalList)ast)));
+ quasiquote(((MalList)ast).rest()));
}
}
@@ -70,7 +73,7 @@ public class stepA_more {
while (is_macro_call(ast, env)) {
MalSymbol a0 = (MalSymbol)((MalList)ast).nth(0);
MalFunction mac = (MalFunction) env.get(a0.getName());
- ast = mac.apply(types._rest((MalList)ast));
+ ast = mac.apply(((MalList)ast).rest());
}
return ast;
}
@@ -81,8 +84,8 @@ public class stepA_more {
return env.get(sym.getName());
} else if (ast instanceof MalList) {
MalList old_lst = (MalList)ast;
- MalList new_lst = types._list_Q(ast) ? new MalList()
- : (MalList)new MalVector();
+ MalList new_lst = ast.list_Q() ? new MalList()
+ : (MalList)new MalVector();
for (MalVal mv : (List<MalVal>)old_lst.value) {
new_lst.conj_BANG(EVAL(mv, env));
}
@@ -101,22 +104,22 @@ public class stepA_more {
}
public static MalVal EVAL(MalVal orig_ast, Env env) throws MalThrowable {
- MalVal a1,a2, a3, res;
+ MalVal a0, a1,a2, a3, res;
MalList el;
while (true) {
- //System.out.println("EVAL: " + types._pr_str(orig_ast, true));
- if (!(types._list_Q(orig_ast))) {
+ //System.out.println("EVAL: " + printer._pr_str(orig_ast, true));
+ if (!orig_ast.list_Q()) {
return eval_ast(orig_ast, env);
}
// apply list
MalVal expanded = macroexpand(orig_ast, env);
- if (!types._list_Q(expanded)) { return expanded; }
+ if (!expanded.list_Q()) { return expanded; }
MalList ast = (MalList) expanded;
if (ast.size() == 0) { return ast; }
- MalVal a0 = ast.nth(0);
+ a0 = ast.nth(0);
String a0sym = a0 instanceof MalSymbol ? ((MalSymbol)a0).getName()
: "__<*fn*>__";
switch (a0sym) {
@@ -199,7 +202,7 @@ public class stepA_more {
final MalList a1f = (MalList)ast.nth(1);
final MalVal a2f = ast.nth(2);
final Env cur_env = env;
- return new MalFunction (a2f, (mal.types.Env)env, a1f) {
+ return new MalFunction (a2f, (mal.env.Env)env, a1f) {
public MalVal apply(MalList args) throws MalThrowable {
return EVAL(a2f, new Env(cur_env, a1f, args));
}
@@ -212,7 +215,7 @@ public class stepA_more {
orig_ast = fnast;
env = new Env(f.getEnv(), f.getParams(), el.slice(1));
} else {
- return f.apply(types._rest(el));
+ return f.apply(el.rest());
}
}
@@ -221,7 +224,7 @@ public class stepA_more {
// print
public static String PRINT(MalVal exp) {
- return types._pr_str(exp, true);
+ return printer._pr_str(exp, true);
}
// REPL
@@ -244,8 +247,8 @@ public class stepA_more {
String prompt = "user> ";
final Env repl_env = new Env(null);
- for (String key : types.types_ns.keySet()) {
- _ref(repl_env, key, types.types_ns.get(key));
+ for (String key : core.ns.keySet()) {
+ _ref(repl_env, key, core.ns.get(key));
}
_ref(repl_env, "readline", new MalFunction() {
public MalVal apply(MalList args) throws MalThrowable {
@@ -315,7 +318,7 @@ public class stepA_more {
System.out.println(e.getMessage());
continue;
} catch (MalException e) {
- System.out.println("Error: " + types._pr_str(e.getValue(), false));
+ System.out.println("Error: " + printer._pr_str(e.getValue(), false));
continue;
} catch (MalThrowable t) {
System.out.println("Error: " + t.getMessage());
diff --git a/java/src/main/java/mal/types.java b/java/src/main/java/mal/types.java
index 8a4910b..a53c0e9 100644
--- a/java/src/main/java/mal/types.java
+++ b/java/src/main/java/mal/types.java
@@ -2,12 +2,12 @@ package mal;
import java.util.List;
import java.util.ArrayList;
-import com.google.common.base.Joiner;
import java.util.Set;
import java.util.Map;
import java.util.HashMap;
-import com.google.common.collect.ImmutableMap;
-import org.apache.commons.lang3.StringEscapeUtils;
+
+import mal.printer;
+import mal.env.Env;
public class types {
//
@@ -34,6 +34,41 @@ public class types {
public MalVal getValue() { return value; }
}
+ //
+ // General functions
+ //
+
+ public static Boolean _equal_Q(MalVal a, MalVal b) {
+ Class ota = a.getClass(), otb = b.getClass();
+ if (!((ota == otb) ||
+ (a instanceof MalList && b instanceof MalList))) {
+ return false;
+ } else {
+ if (a instanceof MalInteger) {
+ return ((MalInteger)a).getValue() ==
+ ((MalInteger)b).getValue();
+ } else if (a instanceof MalSymbol) {
+ return ((MalSymbol)a).getName().equals(
+ ((MalSymbol)b).getName());
+ } else if (a instanceof MalString) {
+ return ((MalString)a).getValue().equals(
+ ((MalString)b).getValue());
+ } else if (a instanceof MalList) {
+ if (((MalList)a).size() != ((MalList)b).size()) {
+ return false;
+ }
+ for (Integer i=0; i<((MalList)a).size(); i++) {
+ if (! _equal_Q(((MalList)a).nth(i),
+ ((MalList)b).nth(i))) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return a == b;
+ }
+ }
+ }
//
// Mal boxed types
@@ -48,6 +83,7 @@ public class types {
}
public MalVal getMeta() { return meta; }
public void setMeta(MalVal m) { meta = m; }
+ public Boolean list_Q() { return false; }
}
public static class MalConstant extends MalVal {
String value;
@@ -56,9 +92,9 @@ public class types {
public String toString() { return value; }
}
- static MalConstant Nil = new MalConstant("nil");
- static MalConstant True = new MalConstant("true");
- static MalConstant False = new MalConstant("false");
+ public static MalConstant Nil = new MalConstant("nil");
+ public static MalConstant True = new MalConstant("true");
+ public static MalConstant False = new MalConstant("false");
public static class MalInteger extends MalVal {
Integer value;
@@ -117,7 +153,7 @@ public class types {
}
public String toString(Boolean print_readably) {
if (print_readably) {
- return "\"" + StringEscapeUtils.escapeJson(value) + "\"";
+ return "\"" + printer.escapeString(value) + "\"";
} else {
return value;
}
@@ -141,19 +177,15 @@ public class types {
return new_ml;
}
- String _join(String delim, Boolean print_readably) {
- ArrayList<String> strs = new ArrayList<String>();
- for (MalVal mv : (List<MalVal>)value) {
- strs.add(mv.toString(print_readably));
- }
- return Joiner.on(delim).join(strs);
- }
@Override public String toString() {
- return start + _join(" ", true) + end;
+ return start + printer.join(value, " ", true) + end;
}
public String toString(Boolean print_readably) {
- return start + _join(" ", print_readably) + end;
+ return start + printer.join(value, " ", print_readably) + end;
}
+
+ public List getList() { return value; }
+ public Boolean list_Q() { return true; }
public MalList conj_BANG(MalVal... mvs) {
for (MalVal mv : mvs) {
@@ -169,6 +201,14 @@ public class types {
public MalVal nth(Integer idx) {
return (MalVal)value.get(idx);
}
+ public MalList rest () {
+ if (size() > 0) {
+ return new MalList(value.subList(1, value.size()));
+ } else {
+ return new MalList();
+ }
+ }
+
public MalList slice(Integer start, Integer end) {
return new MalList(value.subList(start, end));
@@ -197,6 +237,8 @@ public class types {
return new_mv;
}
+ public Boolean list_Q() { return false; }
+
public MalVector slice(Integer start, Integer end) {
return new MalVector(value.subList(start, end));
}
@@ -223,24 +265,11 @@ public class types {
return new_hm;
}
- String _join(Boolean print_readably) {
- ArrayList<String> strs = new ArrayList<String>();
- for (Map.Entry<String, MalVal> entry :
- ((Map<String,MalVal>)value).entrySet()) {
- if (print_readably) {
- strs.add("\"" + entry.getKey().toString() + "\"");
- } else {
- strs.add(entry.getKey().toString());
- }
- strs.add(entry.getValue().toString(print_readably));
- }
- return Joiner.on(" ").join(strs);
- }
@Override public String toString() {
- return "{" + _join(true) + "}";
+ return "{" + printer.join(value, " ", true) + "}";
}
public String toString(Boolean print_readably) {
- return "{" + _join(print_readably) + "}";
+ return "{" + printer.join(value, " ", print_readably) + "}";
}
public Set _entries() {
@@ -280,10 +309,10 @@ public class types {
public MalAtom(MalVal value) { this.value = value; }
public MalAtom copy() throws MalThrowable { return new MalAtom(value); }
@Override public String toString() {
- return "(atom " + _pr_str(value, true) + ")";
+ return "(atom " + printer._pr_str(value, true) + ")";
}
public String toString(Boolean print_readably) {
- return "(atom " + _pr_str(value, print_readably) + ")";
+ return "(atom " + printer._pr_str(value, print_readably) + ")";
}
}
@@ -329,563 +358,4 @@ public class types {
public Boolean isMacro() { return macro; }
public void setMacro() { macro = true; }
}
-
-
- //
- // General functions
- //
- public static String _pr_str(MalVal mv, Boolean print_readably) {
- return mv.toString(print_readably);
- }
-
- public static String _pr_str_args(MalList args, String sep, Boolean print_readably) {
- return args._join(sep, print_readably);
- }
-
- static MalFunction pr_str = new MalFunction() {
- public MalVal apply(MalList args) throws MalThrowable {
- return new MalString(_pr_str_args(args, " ", true));
- }
- };
-
- static MalFunction str = new MalFunction() {
- public MalVal apply(MalList args) throws MalThrowable {
- return new MalString(_pr_str_args(args, "", false));
- }
- };
-
- static MalFunction prn = new MalFunction() {
- public MalVal apply(MalList args) throws MalThrowable {
- System.out.println(_pr_str_args(args, " ", true));
- return Nil;
- }
- };
-
- static MalFunction println = new MalFunction() {
- public MalVal apply(MalList args) throws MalThrowable {
- System.out.println(_pr_str_args(args, " ", false));
- return Nil;
- }
- };
-
-
- static MalFunction meta = new MalFunction() {
- public MalVal apply(MalList args) throws MalThrowable {
- return args.nth(0).getMeta();
- }
- };
-
- static MalFunction with_meta = new MalFunction() {
- public MalVal apply(MalList args) throws MalThrowable {
- MalVal new_mv = ((MalVal)args.nth(0)).copy();
- new_mv.setMeta(args.nth(1));
- return new_mv;
- }
- };
-
-
- public static Boolean _equal_Q(MalVal a, MalVal b) {
- Class ota = a.getClass(), otb = b.getClass();
- if (!((ota == otb) ||
- (a instanceof MalList && b instanceof MalList))) {
- return false;
- } else {
- if (a instanceof MalInteger) {
- return ((MalInteger)a).getValue() ==
- ((MalInteger)b).getValue();
- } else if (a instanceof MalSymbol) {
- return ((MalSymbol)a).getName().equals(
- ((MalSymbol)b).getName());
- } else if (a instanceof MalString) {
- return ((MalString)a).getValue().equals(
- ((MalString)b).getValue());
- } else if (a instanceof MalList) {
- if (((MalList)a).size() != ((MalList)b).size()) {
- return false;
- }
- for (Integer i=0; i<((MalList)a).size(); i++) {
- if (! _equal_Q(((MalList)a).nth(i),
- ((MalList)b).nth(i))) {
- return false;
- }
- }
- return true;
- } else {
- return a == b;
- }
- }
- }
-
- static MalFunction equal_Q = new MalFunction() {
- public MalVal apply(MalList args) throws MalThrowable {
- return _equal_Q(args.nth(0), args.nth(1)) ? True : False;
- }
- };
-
-
- //
- // Constants operations
- //
- static MalFunction symbol_Q = new MalFunction() {
- public MalVal apply(MalList args) throws MalThrowable {
- return args.nth(0) instanceof MalSymbol ? True : False;
- }
- };
-
- static MalFunction nil_Q = new MalFunction() {
- public MalVal apply(MalList args) throws MalThrowable {
- return args.nth(0) == Nil ? True : False;
- }
- };
-
- static MalFunction true_Q = new MalFunction() {
- public MalVal apply(MalList args) throws MalThrowable {
- return args.nth(0) == True ? True : False;
- }
- };
-
- static MalFunction false_Q = new MalFunction() {
- public MalVal apply(MalList args) throws MalThrowable {
- return args.nth(0) == False ? True : False;
- }
- };
-
-
- //
- // Number operations
- //
- static MalFunction add = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- return ((MalInteger)a.nth(0)).add((MalInteger)a.nth(1));
- }
- };
- static MalFunction subtract = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- return ((MalInteger)a.nth(0)).subtract((MalInteger)a.nth(1));
- }
- };
- static MalFunction multiply = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- return ((MalInteger)a.nth(0)).multiply((MalInteger)a.nth(1));
- }
- };
- static MalFunction divide = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- return ((MalInteger)a.nth(0)).divide((MalInteger)a.nth(1));
- }
- };
-
- static MalFunction lt = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- return ((MalInteger)a.nth(0)).lt((MalInteger)a.nth(1));
- }
- };
- static MalFunction lte = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- return ((MalInteger)a.nth(0)).lte((MalInteger)a.nth(1));
- }
- };
- static MalFunction gt = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- return ((MalInteger)a.nth(0)).gt((MalInteger)a.nth(1));
- }
- };
- static MalFunction gte = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- return ((MalInteger)a.nth(0)).gte((MalInteger)a.nth(1));
- }
- };
-
- //
- // Errors/Exceptions
- //
- static MalFunction mal_throw = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- throw new MalException(a.nth(0));
- }
- };
-
- //
- // List operations
- //
- static MalFunction new_list = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- return new MalList(a.value);
- }
- };
-
- static public Boolean _list_Q(MalVal mv) {
- return mv.getClass().equals(MalList.class);
- }
- static MalFunction list_Q = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- return _list_Q(a.nth(0)) ? True : False;
- }
- };
-
- //
- // Vector operations
- //
- static MalFunction new_vector = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- return new MalVector(a.value);
- }
- };
-
- static public Boolean _vector_Q(MalVal mv) {
- return mv.getClass().equals(MalVector.class);
- }
- static MalFunction vector_Q = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- return _vector_Q(a.nth(0)) ? True : False;
- }
- };
-
- //
- // Hash map operations
- //
- static MalFunction new_hash_map = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- return new MalHashMap(a);
- }
- };
-
- static MalFunction hash_map_Q = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- return a.nth(0) instanceof MalHashMap ? True : False;
- }
- };
-
- static MalFunction contains_Q = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- String key = ((MalString)a.nth(1)).getValue();
- MalHashMap mhm = (MalHashMap)a.nth(0);
- HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;
- return hm.containsKey(key) ? True : False;
- }
- };
-
- static MalFunction assoc = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- MalHashMap mhm = (MalHashMap)a.nth(0);
- HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;
- MalHashMap new_mhm = new MalHashMap((Map)hm.clone());
- new_mhm.assoc_BANG((MalList)a.slice(1));
- return new_mhm;
- }
- };
-
- static MalFunction dissoc = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- MalHashMap mhm = (MalHashMap)a.nth(0);
- HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;
- MalHashMap new_mhm = new MalHashMap((Map)hm.clone());
- new_mhm.dissoc_BANG((MalList)a.slice(1));
- return new_mhm;
- }
- };
-
- static MalFunction get = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- String key = ((MalString)a.nth(1)).getValue();
- MalHashMap mhm = (MalHashMap)a.nth(0);
- HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;
- if (hm.containsKey(key)) {
- return hm.get(key);
- } else {
- return Nil;
- }
- }
- };
-
- static MalFunction keys = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- MalHashMap mhm = (MalHashMap)a.nth(0);
- HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;
- MalList key_lst = new MalList();
- for (String key : hm.keySet()) {
- key_lst.conj_BANG(new MalString(key));
- }
- return key_lst;
- }
- };
-
- static MalFunction vals = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- MalHashMap mhm = (MalHashMap)a.nth(0);
- HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;
- //return new ArrayList<MalVal>(((HashMap<String,MalVal>)hm).values());
- MalList val_lst = new MalList();
- for (MalVal val : hm.values()) {
- val_lst.conj_BANG(val);
- }
- return val_lst;
- }
- };
-
-
- //
- // Atoms
- //
- static MalFunction new_atom = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- return new MalAtom(a.nth(0));
- }
- };
-
- static MalFunction atom_Q = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- return a.nth(0) instanceof MalAtom ? True : False;
- }
- };
-
- static MalFunction deref = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- return ((MalAtom)a.nth(0)).value;
- }
- };
-
- static MalFunction reset_BANG = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- return ((MalAtom)a.nth(0)).value = a.nth(1);
- }
- };
-
- static MalFunction swap_BANG = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- MalAtom atm = (MalAtom)a.nth(0);
- MalFunction f = (MalFunction)a.nth(1);
- MalList new_args = new MalList();
- new_args.value.addAll(((MalList)a.slice(2)).value);
- new_args.value.add(0, atm.value);
- atm.value = f.apply(new_args);
- return atm.value;
- }
- };
-
-
-
-
- //
- // Sequence operations
- //
- static MalFunction sequential_Q = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- return a.nth(0) instanceof MalList ? True : False;
- }
- };
-
- static MalFunction count = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- return new MalInteger(((MalList)a.nth(0)).size());
- }
- };
-
- static MalFunction empty_Q = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- MalVal exp = a.nth(0);
- if (exp == Nil || (exp instanceof MalList &&
- ((MalList)exp).size() == 0)) {
- return True;
- } else {
- return False;
- }
- }
- };
-
- static MalFunction cons = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- MalList lst = new MalList();
- lst.value.addAll(((MalList)a.nth(1)).value);
- lst.value.add(0, a.nth(0));
- return (MalVal) lst;
- }
- };
-
- static MalFunction concat = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- if (a.size() == 0) { return new MalList(); }
- MalList lst = new MalList();
- lst.value.addAll(((MalList)a.nth(0)).value);
- for(Integer i=1; i<a.size(); i++) {
- lst.value.addAll(((MalList)a.nth(i)).value);
- }
- return (MalVal) lst;
- }
- };
-
- static MalFunction conj = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- MalList src_seq = (MalList)a.nth(0), new_seq;
- if (a.nth(0) instanceof MalVector) {
- new_seq = new MalVector();
- new_seq.value.addAll(src_seq.value);
- for(Integer i=1; i<a.size(); i++) {
- new_seq.value.add(a.nth(i));
- }
- } else {
- new_seq = new MalList();
- new_seq.value.addAll(src_seq.value);
- for(Integer i=1; i<a.size(); i++) {
- new_seq.value.add(0, a.nth(i));
- }
- }
- return (MalVal) new_seq;
- }
- };
-
- static MalFunction first = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- MalList ml = ((MalList)a.nth(0));
- return ml.size() > 0 ? ml.nth(0) : Nil;
- }
- };
-
- static MalList _rest (MalList ml) {
- if (ml.size() > 0) {
- return new MalList(ml.value.subList(1, ml.value.size()));
- } else {
- return new MalList();
- }
- }
-
- static MalFunction rest = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- MalList ml = ((MalList)a.nth(0));
- return _rest(ml);
- }
- };
-
- static MalFunction nth = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- Integer idx = ((MalInteger)a.nth(1)).getValue();
- return ((MalList)a.nth(0)).nth(idx);
- }
- };
-
- // General list related functions
- static MalFunction apply = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- MalFunction f = (MalFunction)a.nth(0);
- MalList args = a.slice(1,a.size()-1);
- args.value.addAll( ((MalList)a.nth(a.size()-1)).value);
- return f.apply(args);
- }
- };
-
- static MalFunction map = new MalFunction() {
- public MalVal apply(MalList a) throws MalThrowable {
- MalFunction f = (MalFunction) a.nth(0);
- MalList src_lst = (MalList) a.nth(1);
- MalList new_lst = new MalList();
- for(Integer i=0; i<src_lst.size(); i++) {
- new_lst.value.add(
- f.apply(new MalList(src_lst.nth(i))));
- }
- return new_lst;
- }
- };
-
-
-
- //
- // Env implementation
- //
- public static class Env {
- Env outer = null;
- HashMap<String,MalVal> data = new HashMap<String,MalVal>();
-
- public Env(Env outer) {
- this.outer = outer;
- }
- public Env(Env outer, MalList binds, MalList exprs) {
- this.outer = outer;
- for (Integer i=0; i<binds.size(); i++) {
- String sym = ((MalSymbol)binds.nth(i)).getName();
- if (sym.equals("&")) {
- data.put(((MalSymbol)binds.nth(i+1)).getName(),
- exprs.slice(i));
- break;
- } else {
- data.put(sym, exprs.nth(i));
- }
- }
- }
-
- public Env find(String key) {
- if (data.containsKey(key)) {
- return this;
- } else if (outer != null) {
- return outer.find(key);
- } else {
- return null;
- }
- }
-
- public MalVal get(String key) throws MalThrowable {
- Env e = find(key);
- if (e == null) {
- throw new MalException("'" + key + "' not found");
- } else {
- return e.data.get(key);
- }
- }
-
- public Env set(String key, MalVal value) {
- data.put(key, value);
- return this;
- }
- }
-
- // types_ns is namespace of type functions
- static Map<String, MalVal> types_ns = ImmutableMap.<String, MalVal>builder()
- .put("pr-str", pr_str)
- .put("str", str)
- .put("prn", prn)
- .put("println", println)
- .put("meta", meta)
- .put("with-meta", with_meta)
- .put("=", equal_Q)
- .put("symbol?", symbol_Q)
- .put("nil?", nil_Q)
- .put("true?", true_Q)
- .put("false?", false_Q)
- .put("<", lt)
- .put("<=", lte)
- .put(">", gt)
- .put(">=", gte)
- .put("+", add)
- .put("-", subtract)
- .put("*", multiply)
- .put("/", divide)
- .put("throw", mal_throw)
- .put("list", new_list)
- .put("list?", list_Q)
- .put("vector", new_vector)
- .put("vector?", vector_Q)
- .put("hash-map", new_hash_map)
- .put("map?", hash_map_Q)
- .put("assoc", assoc)
- .put("dissoc", dissoc)
- .put("contains?", contains_Q)
- .put("get", get)
- .put("keys", keys)
- .put("vals", vals)
- .put("atom", new_atom)
- .put("atom?", atom_Q)
- .put("deref", deref)
- .put("reset!", reset_BANG)
- .put("swap!", swap_BANG)
- .put("sequential?", sequential_Q)
- .put("cons", cons)
- .put("concat", concat)
- .put("conj", conj)
- .put("first", first)
- .put("rest", rest)
- .put("nth", nth)
- .put("count", count)
- .put("empty?", empty_Q)
- .put("apply", apply)
- .put("map", map)
- .build();
}
diff --git a/js/Makefile b/js/Makefile
index cb57644..2b7aa88 100644
--- a/js/Makefile
+++ b/js/Makefile
@@ -1,7 +1,8 @@
TESTS = tests/types.js tests/reader.js tests/step5_tco.js
-SOURCES = node_readline.js types.js reader.js stepA_more.js
+SOURCES = node_readline.js types.js reader.js printer.js \
+ env.js core.js stepA_more.js
WEB_SOURCES = $(SOURCES:node_readline.js=josh_readline.js)
all: mal.js mal_web.js
diff --git a/js/core.js b/js/core.js
new file mode 100644
index 0000000..48bbe16
--- /dev/null
+++ b/js/core.js
@@ -0,0 +1,193 @@
+// Node vs browser behavior
+var core = {};
+if (typeof module === 'undefined') {
+ var exports = core;
+} else {
+ var types = require('./types'),
+ printer = require('./printer');
+}
+
+// Errors/Exceptions
+function mal_throw(exc) { throw exc; }
+
+
+// String functions
+function pr_str() {
+ return Array.prototype.map.call(arguments,function(exp) {
+ return printer._pr_str(exp, true);
+ }).join(" ");
+}
+
+function str() {
+ return Array.prototype.map.call(arguments,function(exp) {
+ return printer._pr_str(exp, false);
+ }).join("");
+}
+
+function prn() {
+ printer.print.apply({}, Array.prototype.map.call(arguments,function(exp) {
+ return printer._pr_str(exp, true);
+ }));
+}
+
+function println() {
+ printer.print.apply({}, Array.prototype.map.call(arguments,function(exp) {
+ return printer._pr_str(exp, false);
+ }));
+}
+
+
+// Hash Map functions
+function assoc(src_hm) {
+ var hm = types._clone(src_hm);
+ var args = [hm].concat(Array.prototype.slice.call(arguments, 1));
+ return types._assoc_BANG.apply(null, args);
+}
+
+function dissoc(src_hm) {
+ var hm = types._clone(src_hm);
+ var args = [hm].concat(Array.prototype.slice.call(arguments, 1));
+ return types._dissoc_BANG.apply(null, args);
+}
+
+function get(hm, key) {
+ if (key in hm) {
+ return hm[key];
+ } else {
+ return null;
+ }
+}
+
+function contains_Q(hm, key) {
+ if (key in hm) { return true; } else { return false; }
+}
+
+function keys(hm) { return Object.keys(hm); }
+function vals(hm) { return Object.keys(hm).map(function(k) { return hm[k]; }); }
+
+
+// Sequence functions
+function cons(a, b) { return [a].concat(b); }
+
+function concat(lst) {
+ lst = lst || [];
+ return lst.concat.apply(lst, Array.prototype.slice.call(arguments, 1));
+}
+
+function nth(lst, idx) { return lst[idx]; }
+
+function first(lst) { return lst[0]; }
+
+function rest(lst) { return lst.slice(1); }
+
+function empty_Q(lst) { return lst.length === 0; }
+
+function count(s) {
+ if (Array.isArray(s)) { return s.length; }
+ else { return Object.keys(s).length; }
+}
+
+function conj(lst) {
+ if (types._list_Q(lst)) {
+ return Array.prototype.slice.call(arguments, 1).reverse().concat(lst);
+ } else {
+ var v = lst.concat(Array.prototype.slice.call(arguments, 1));
+ v.__isvector__ = true;
+ return v;
+ }
+}
+
+function apply(f) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ return f.apply(f, args.slice(0, args.length-1).concat(args[args.length-1]));
+}
+
+function map(f, lst) {
+ return lst.map(function(el){ return f(el); });
+}
+
+
+// Metadata functions
+function with_meta(obj, m) {
+ var new_obj = types._clone(obj);
+ new_obj.__meta__ = m;
+ return new_obj;
+}
+
+function meta(obj) {
+ // TODO: support symbols and atoms
+ if ((!types._sequential_Q(obj)) &&
+ (!(types._hash_map_Q(obj))) &&
+ (!(types._function_Q(obj)))) {
+ throw new Error("attempt to get metadata from: " + types._obj_type(obj));
+ }
+ return obj.__meta__;
+}
+
+
+// Atom functions
+function deref(atm) { return atm.val; }
+function reset_BANG(atm, val) { return atm.val = val; }
+function swap_BANG(atm, f) {
+ var args = [atm.val].concat(Array.prototype.slice.call(arguments, 2));
+ atm.val = f.apply(f, args);
+ return atm.val;
+}
+
+
+// types.ns is namespace of type functions
+var ns = {'type': types._obj_type,
+ '=': types._equal_Q,
+ 'throw': mal_throw,
+ 'nil?': types._nil_Q,
+ 'true?': types._true_Q,
+ 'false?': types._false_Q,
+ 'symbol': types._symbol,
+ 'symbol?': types._symbol_Q,
+ 'pr-str': pr_str,
+ 'str': str,
+ 'prn': prn,
+ 'println': println,
+ '<' : function(a,b){return a<b;},
+ '<=' : function(a,b){return a<=b;},
+ '>' : function(a,b){return a>b;},
+ '>=' : function(a,b){return a>=b;},
+ '+' : function(a,b){return a+b;},
+ '-' : function(a,b){return a-b;},
+ '*' : function(a,b){return a*b;},
+ '/' : function(a,b){return a/b;},
+
+ 'list': types._list,
+ 'list?': types._list_Q,
+ 'vector': types._vector,
+ 'vector?': types._vector_Q,
+ 'hash-map': types._hash_map,
+ 'map?': types._hash_map_Q,
+ 'assoc': assoc,
+ 'dissoc': dissoc,
+ 'get': get,
+ 'contains?': contains_Q,
+ 'keys': keys,
+ 'vals': vals,
+
+ 'sequential?': types._sequential_Q,
+ 'cons': cons,
+ 'concat': concat,
+ 'nth': nth,
+ 'first': first,
+ 'rest': rest,
+ 'empty?': empty_Q,
+ 'count': count,
+ 'conj': conj,
+ 'apply': apply,
+ 'map': map,
+
+ 'with-meta': with_meta,
+ 'meta': meta,
+ 'atom': types._atom,
+ 'atom?': types._atom_Q,
+ "deref": deref,
+ "reset!": reset_BANG,
+ "swap!": swap_BANG};
+
+exports.ns = core.ns = ns;
diff --git a/js/env.js b/js/env.js
new file mode 100644
index 0000000..3c9eac8
--- /dev/null
+++ b/js/env.js
@@ -0,0 +1,40 @@
+// Node vs browser behavior
+var env = {};
+if (typeof module === 'undefined') {
+ var exports = env;
+}
+
+// Env implementation
+function Env(outer, binds, exprs) {
+ this.data = {};
+ this.outer = outer || null;
+
+ if (binds && exprs) {
+ // Returns a new Env with symbols in binds bound to
+ // corresponding values in exprs
+ // TODO: check types of binds and exprs and compare lengths
+ for (var i=0; i<binds.length;i++) {
+ if (binds[i].value === "&") {
+ // variable length arguments
+ this.data[binds[i+1].value] = Array.prototype.slice.call(exprs, i);
+ break;
+ } else {
+ this.data[binds[i].value] = exprs[i];
+ }
+ }
+ }
+ return this;
+}
+Env.prototype.find = function (key) {
+ if (key in this.data) { return this; }
+ else if (this.outer) { return this.outer.find(key); }
+ else { return null; }
+};
+Env.prototype.set = function(key, value) { this.data[key] = value; return value; },
+Env.prototype.get = function(key) {
+ var env = this.find(key);
+ if (!env) { throw new Error("'" + key + "' not found"); }
+ return env.data[key];
+};
+
+exports.Env = env.Env = Env;
diff --git a/js/printer.js b/js/printer.js
new file mode 100644
index 0000000..0d84cc6
--- /dev/null
+++ b/js/printer.js
@@ -0,0 +1,44 @@
+// Node vs browser behavior
+var printer = {};
+if (typeof module !== 'undefined') {
+ var types = require('./types');
+ // map output/print to console.log
+ var print = exports.print = function () { console.log.apply(console, arguments); };
+} else {
+ var exports = printer;
+}
+
+function _pr_str(obj, print_readably) {
+ if (typeof print_readably === 'undefined') { print_readably = true; }
+ var _r = print_readably;
+ var ot = types._obj_type(obj);
+ switch (ot) {
+ case 'list':
+ var ret = obj.map(function(e) { return _pr_str(e,_r); });
+ return "(" + ret.join(' ') + ")";
+ case 'vector':
+ var ret = obj.map(function(e) { return _pr_str(e,_r); });
+ return "[" + ret.join(' ') + "]";
+ case 'hash-map':
+ var ret = [];
+ for (var k in obj) {
+ ret.push(_pr_str(k,_r), _pr_str(obj[k],_r));
+ }
+ return "{" + ret.join(' ') + "}";
+ case 'string':
+ if (print_readably) {
+ return '"' + obj.replace(/\\/, "\\\\").replace(/"/g, '\\"') + '"';
+ } else {
+ return obj;
+ }
+ case 'nil':
+ return "nil";
+ case 'atom':
+ return "(atom " + _pr_str(obj.val,_r) + ")";
+ default:
+ return obj.toString();
+ }
+}
+
+exports._pr_str = printer._pr_str = _pr_str;
+
diff --git a/js/reader.js b/js/reader.js
index da51088..f19010d 100644
--- a/js/reader.js
+++ b/js/reader.js
@@ -40,7 +40,7 @@ function read_atom (reader) {
} else if (token === "false") {
return false;
} else {
- return types.symbol(token); // symbol
+ return types._symbol(token); // symbol
}
}
@@ -66,13 +66,13 @@ function read_list(reader, start, end) {
// read vector of tokens
function read_vector(reader) {
var lst = read_list(reader, '[', ']');
- return types.vector.apply(types.vector, lst);
+ return types._vector.apply(null, lst);
}
// read hash-map key/value pairs
function read_hash_map(reader) {
var lst = read_list(reader, '{', '}');
- return types.hash_map.apply(types.hash_map, lst);
+ return types._hash_map.apply(null, lst);
}
function read_form(reader) {
@@ -81,18 +81,18 @@ function read_form(reader) {
// reader macros/transforms
case ';': return null; // Ignore comments
case '\'': reader.next();
- return [types.symbol('quote'), read_form(reader)];
+ return [types._symbol('quote'), read_form(reader)];
case '`': reader.next();
- return [types.symbol('quasiquote'), read_form(reader)];
+ return [types._symbol('quasiquote'), read_form(reader)];
case '~': reader.next();
- return [types.symbol('unquote'), read_form(reader)];
+ return [types._symbol('unquote'), read_form(reader)];
case '~@': reader.next();
- return [types.symbol('splice-unquote'), read_form(reader)];
+ return [types._symbol('splice-unquote'), read_form(reader)];
case '^': reader.next();
var meta = read_form(reader);
- return [types.symbol('with-meta'), read_form(reader), meta];
+ return [types._symbol('with-meta'), read_form(reader), meta];
case '@': reader.next();
- return [types.symbol('deref'), read_form(reader)];
+ return [types._symbol('deref'), read_form(reader)];
// list
case ')': throw new Error("unexpected ')'");
diff --git a/js/step1_read_print.js b/js/step1_read_print.js
index ee027d7..264b6c6 100644
--- a/js/step1_read_print.js
+++ b/js/step1_read_print.js
@@ -1,5 +1,6 @@
var types = require('./types');
var reader = require('./reader');
+var printer = require('./printer');
if (typeof module !== 'undefined') {
var readline = require('./node_readline');
}
@@ -16,7 +17,7 @@ function EVAL(ast, env) {
// print
function PRINT(exp) {
- return types._pr_str(exp, true);
+ return printer._pr_str(exp, true);
}
// repl
diff --git a/js/step2_eval.js b/js/step2_eval.js
index f2cb8b1..f5efa2c 100644
--- a/js/step2_eval.js
+++ b/js/step2_eval.js
@@ -1,5 +1,6 @@
var types = require('./types');
var reader = require('./reader');
+var printer = require('./printer');
if (typeof module !== 'undefined') {
var readline = require('./node_readline');
}
@@ -11,15 +12,15 @@ function READ(str) {
// eval
function eval_ast(ast, env) {
- if (types.symbol_Q(ast)) {
+ if (types._symbol_Q(ast)) {
return env[ast];
- } else if (types.list_Q(ast)) {
+ } else if (types._list_Q(ast)) {
return ast.map(function(a) { return EVAL(a, env); });
- } else if (types.vector_Q(ast)) {
+ } else if (types._vector_Q(ast)) {
var v = ast.map(function(a) { return EVAL(a, env); });
v.__isvector__ = true;
return v;
- } else if (types.hash_map_Q(ast)) {
+ } else if (types._hash_map_Q(ast)) {
var new_hm = {};
for (k in ast) {
new_hm[EVAL(k, env)] = EVAL(ast[k], env);
@@ -31,7 +32,7 @@ function eval_ast(ast, env) {
}
function _EVAL(ast, env) {
- if (!types.list_Q(ast)) {
+ if (!types._list_Q(ast)) {
return eval_ast(ast, env);
}
@@ -47,7 +48,7 @@ function EVAL(ast, env) {
// print
function PRINT(exp) {
- return types._pr_str(exp, true);
+ return printer._pr_str(exp, true);
}
// repl
diff --git a/js/step3_env.js b/js/step3_env.js
index 5b6e802..41e21c1 100644
--- a/js/step3_env.js
+++ b/js/step3_env.js
@@ -1,5 +1,7 @@
var types = require('./types');
var reader = require('./reader');
+var printer = require('./printer');
+var Env = require('./env').Env;
if (typeof module !== 'undefined') {
var readline = require('./node_readline');
}
@@ -11,15 +13,15 @@ function READ(str) {
// eval
function eval_ast(ast, env) {
- if (types.symbol_Q(ast)) {
+ if (types._symbol_Q(ast)) {
return env.get(ast);
- } else if (types.list_Q(ast)) {
+ } else if (types._list_Q(ast)) {
return ast.map(function(a) { return EVAL(a, env); });
- } else if (types.vector_Q(ast)) {
+ } else if (types._vector_Q(ast)) {
var v = ast.map(function(a) { return EVAL(a, env); });
v.__isvector__ = true;
return v;
- } else if (types.hash_map_Q(ast)) {
+ } else if (types._hash_map_Q(ast)) {
var new_hm = {};
for (k in ast) {
new_hm[EVAL(k, env)] = EVAL(ast[k], env);
@@ -31,7 +33,7 @@ function eval_ast(ast, env) {
}
function _EVAL(ast, env) {
- if (!types.list_Q(ast)) {
+ if (!types._list_Q(ast)) {
return eval_ast(ast, env);
}
@@ -42,7 +44,7 @@ function _EVAL(ast, env) {
var res = EVAL(a2, env);
return env.set(a1, res);
case "let*":
- var let_env = new types.Env(env);
+ var let_env = new Env(env);
for (var i=0; i < a1.length; i+=2) {
let_env.set(a1[i].value, EVAL(a1[i+1], let_env));
}
@@ -60,11 +62,11 @@ function EVAL(ast, env) {
// print
function PRINT(exp) {
- return types._pr_str(exp, true);
+ return printer._pr_str(exp, true);
}
// repl
-var repl_env = new types.Env();
+var repl_env = new Env();
var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); };
_ref = function (k,v) { repl_env.set(k, v); }
diff --git a/js/step4_if_fn_do.js b/js/step4_if_fn_do.js
index d33ec04..37803ef 100644
--- a/js/step4_if_fn_do.js
+++ b/js/step4_if_fn_do.js
@@ -1,5 +1,8 @@
var types = require('./types');
var reader = require('./reader');
+var printer = require('./printer');
+var Env = require('./env').Env;
+var core = require('./core');
if (typeof module !== 'undefined') {
var readline = require('./node_readline');
}
@@ -11,15 +14,15 @@ function READ(str) {
// eval
function eval_ast(ast, env) {
- if (types.symbol_Q(ast)) {
+ if (types._symbol_Q(ast)) {
return env.get(ast);
- } else if (types.list_Q(ast)) {
+ } else if (types._list_Q(ast)) {
return ast.map(function(a) { return EVAL(a, env); });
- } else if (types.vector_Q(ast)) {
+ } else if (types._vector_Q(ast)) {
var v = ast.map(function(a) { return EVAL(a, env); });
v.__isvector__ = true;
return v;
- } else if (types.hash_map_Q(ast)) {
+ } else if (types._hash_map_Q(ast)) {
var new_hm = {};
for (k in ast) {
new_hm[EVAL(k, env)] = EVAL(ast[k], env);
@@ -31,7 +34,7 @@ function eval_ast(ast, env) {
}
function _EVAL(ast, env) {
- if (!types.list_Q(ast)) {
+ if (!types._list_Q(ast)) {
return eval_ast(ast, env);
}
@@ -42,7 +45,7 @@ function _EVAL(ast, env) {
var res = EVAL(a2, env);
return env.set(a1, res);
case "let*":
- var let_env = new types.Env(env);
+ var let_env = new Env(env);
for (var i=0; i < a1.length; i+=2) {
let_env.set(a1[i].value, EVAL(a1[i+1], let_env));
}
@@ -59,7 +62,7 @@ function _EVAL(ast, env) {
}
case "fn*":
return function() {
- return EVAL(a2, new types.Env(env, a1, arguments));
+ return EVAL(a2, new Env(env, a1, arguments));
};
default:
var el = eval_ast(ast, env), f = el[0];
@@ -74,16 +77,16 @@ function EVAL(ast, env) {
// print
function PRINT(exp) {
- return types._pr_str(exp, true);
+ return printer._pr_str(exp, true);
}
// repl
-var repl_env = new types.Env();
+var repl_env = new Env();
var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); };
_ref = function (k,v) { repl_env.set(k, v); }
-// Import types functions
-for (var n in types.ns) { repl_env.set(n, types.ns[n]); }
+// Import core functions
+for (var n in core.ns) { repl_env.set(n, core.ns[n]); }
// Defined using the language itself
rep("(def! not (fn* (a) (if a false true)))");
diff --git a/js/step5_tco.js b/js/step5_tco.js
index 20a9583..2d1793d 100644
--- a/js/step5_tco.js
+++ b/js/step5_tco.js
@@ -1,5 +1,8 @@
var types = require('./types');
var reader = require('./reader');
+var printer = require('./printer');
+var Env = require('./env').Env;
+var core = require('./core');
if (typeof module !== 'undefined') {
var readline = require('./node_readline');
}
@@ -11,15 +14,15 @@ function READ(str) {
// eval
function eval_ast(ast, env) {
- if (types.symbol_Q(ast)) {
+ if (types._symbol_Q(ast)) {
return env.get(ast);
- } else if (types.list_Q(ast)) {
+ } else if (types._list_Q(ast)) {
return ast.map(function(a) { return EVAL(a, env); });
- } else if (types.vector_Q(ast)) {
+ } else if (types._vector_Q(ast)) {
var v = ast.map(function(a) { return EVAL(a, env); });
v.__isvector__ = true;
return v;
- } else if (types.hash_map_Q(ast)) {
+ } else if (types._hash_map_Q(ast)) {
var new_hm = {};
for (k in ast) {
new_hm[EVAL(k, env)] = EVAL(ast[k], env);
@@ -32,45 +35,48 @@ function eval_ast(ast, env) {
function _EVAL(ast, env) {
while (true) {
- if (!types.list_Q(ast)) {
- return eval_ast(ast, env);
+
+ //console.log("EVAL:", types._pr_str(ast, true));
+ if (!types._list_Q(ast)) {
+ return eval_ast(ast, env);
+ }
+
+ // apply list
+ var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3];
+ switch (a0.value) {
+ case "def!":
+ var res = EVAL(a2, env);
+ return env.set(a1, res);
+ case "let*":
+ var let_env = new Env(env);
+ for (var i=0; i < a1.length; i+=2) {
+ let_env.set(a1[i].value, EVAL(a1[i+1], let_env));
}
-
- // apply list
- var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3];
- switch (a0.value) {
- case "def!":
- var res = EVAL(a2, env);
- return env.set(a1, res);
- case "let*":
- var let_env = new types.Env(env);
- for (var i=0; i < a1.length; i+=2) {
- let_env.set(a1[i].value, EVAL(a1[i+1], let_env));
- }
- return EVAL(a2, let_env);
- case "do":
- eval_ast(ast.slice(1, -1), env);
- ast = ast[ast.length-1];
- break;
- case "if":
- var cond = EVAL(a1, env);
- if (cond === null || cond === false) {
- ast = (typeof a3 !== "undefined") ? a3 : null;
- } else {
- ast = a2;
- }
- break;
- case "fn*":
- return types.new_function(EVAL, a2, env, a1);
- default:
- var el = eval_ast(ast, env), f = el[0], meta = f.__meta__;
- if (meta && meta.exp) {
- ast = meta.exp;
- env = new types.Env(meta.env, meta.params, el.slice(1));
- } else {
- return f.apply(f, el.slice(1));
- }
+ return EVAL(a2, let_env);
+ case "do":
+ eval_ast(ast.slice(1, -1), env);
+ ast = ast[ast.length-1];
+ break;
+ case "if":
+ var cond = EVAL(a1, env);
+ if (cond === null || cond === false) {
+ ast = (typeof a3 !== "undefined") ? a3 : null;
+ } else {
+ ast = a2;
}
+ break;
+ case "fn*":
+ return types._function(EVAL, Env, a2, env, a1);
+ default:
+ var el = eval_ast(ast, env), f = el[0], meta = f.__meta__;
+ if (meta && meta.exp) {
+ ast = meta.exp;
+ env = new Env(meta.env, meta.params, el.slice(1));
+ } else {
+ return f.apply(f, el.slice(1));
+ }
+ }
+
}
}
@@ -81,16 +87,16 @@ function EVAL(ast, env) {
// print
function PRINT(exp) {
- return types._pr_str(exp, true);
+ return printer._pr_str(exp, true);
}
// repl
-var repl_env = new types.Env();
+var repl_env = new Env();
var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); };
_ref = function (k,v) { repl_env.set(k, v); }
-// Import types functions
-for (var n in types.ns) { repl_env.set(n, types.ns[n]); }
+// Import core functions
+for (var n in core.ns) { repl_env.set(n, core.ns[n]); }
// Defined using the language itself
rep("(def! not (fn* (a) (if a false true)))");
diff --git a/js/step6_file.js b/js/step6_file.js
index 6a014eb..df216da 100644
--- a/js/step6_file.js
+++ b/js/step6_file.js
@@ -1,5 +1,8 @@
var types = require('./types');
var reader = require('./reader');
+var printer = require('./printer');
+var Env = require('./env').Env;
+var core = require('./core');
if (typeof module !== 'undefined') {
var readline = require('./node_readline');
}
@@ -11,15 +14,15 @@ function READ(str) {
// eval
function eval_ast(ast, env) {
- if (types.symbol_Q(ast)) {
+ if (types._symbol_Q(ast)) {
return env.get(ast);
- } else if (types.list_Q(ast)) {
+ } else if (types._list_Q(ast)) {
return ast.map(function(a) { return EVAL(a, env); });
- } else if (types.vector_Q(ast)) {
+ } else if (types._vector_Q(ast)) {
var v = ast.map(function(a) { return EVAL(a, env); });
v.__isvector__ = true;
return v;
- } else if (types.hash_map_Q(ast)) {
+ } else if (types._hash_map_Q(ast)) {
var new_hm = {};
for (k in ast) {
new_hm[EVAL(k, env)] = EVAL(ast[k], env);
@@ -32,45 +35,48 @@ function eval_ast(ast, env) {
function _EVAL(ast, env) {
while (true) {
- if (!types.list_Q(ast)) {
- return eval_ast(ast, env);
+
+ //console.log("EVAL:", types._pr_str(ast, true));
+ if (!types._list_Q(ast)) {
+ return eval_ast(ast, env);
+ }
+
+ // apply list
+ var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3];
+ switch (a0.value) {
+ case "def!":
+ var res = EVAL(a2, env);
+ return env.set(a1, res);
+ case "let*":
+ var let_env = new Env(env);
+ for (var i=0; i < a1.length; i+=2) {
+ let_env.set(a1[i].value, EVAL(a1[i+1], let_env));
}
-
- // apply list
- var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3];
- switch (a0.value) {
- case "def!":
- var res = EVAL(a2, env);
- return env.set(a1, res);
- case "let*":
- var let_env = new types.Env(env);
- for (var i=0; i < a1.length; i+=2) {
- let_env.set(a1[i].value, EVAL(a1[i+1], let_env));
- }
- return EVAL(a2, let_env);
- case "do":
- eval_ast(ast.slice(1, -1), env);
- ast = ast[ast.length-1];
- break;
- case "if":
- var cond = EVAL(a1, env);
- if (cond === null || cond === false) {
- ast = (typeof a3 !== "undefined") ? a3 : null;
- } else {
- ast = a2;
- }
- break;
- case "fn*":
- return types.new_function(EVAL, a2, env, a1);
- default:
- var el = eval_ast(ast, env), f = el[0], meta = f.__meta__;
- if (meta && meta.exp) {
- ast = meta.exp;
- env = new types.Env(meta.env, meta.params, el.slice(1));
- } else {
- return f.apply(f, el.slice(1));
- }
+ return EVAL(a2, let_env);
+ case "do":
+ eval_ast(ast.slice(1, -1), env);
+ ast = ast[ast.length-1];
+ break;
+ case "if":
+ var cond = EVAL(a1, env);
+ if (cond === null || cond === false) {
+ ast = (typeof a3 !== "undefined") ? a3 : null;
+ } else {
+ ast = a2;
}
+ break;
+ case "fn*":
+ return types._function(EVAL, Env, a2, env, a1);
+ default:
+ var el = eval_ast(ast, env), f = el[0], meta = f.__meta__;
+ if (meta && meta.exp) {
+ ast = meta.exp;
+ env = new Env(meta.env, meta.params, el.slice(1));
+ } else {
+ return f.apply(f, el.slice(1));
+ }
+ }
+
}
}
@@ -81,16 +87,16 @@ function EVAL(ast, env) {
// print
function PRINT(exp) {
- return types._pr_str(exp, true);
+ return printer._pr_str(exp, true);
}
// repl
-var repl_env = new types.Env();
+var repl_env = new Env();
var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); };
_ref = function (k,v) { repl_env.set(k, v); }
-// Import types functions
-for (var n in types.ns) { repl_env.set(n, types.ns[n]); }
+// Import core functions
+for (var n in core.ns) { repl_env.set(n, core.ns[n]); }
_ref('read-string', reader.read_str);
_ref('eval', function(ast) { return EVAL(ast, repl_env); });
diff --git a/js/step7_quote.js b/js/step7_quote.js
index 6d23595..9721d59 100644
--- a/js/step7_quote.js
+++ b/js/step7_quote.js
@@ -1,5 +1,8 @@
var types = require('./types');
var reader = require('./reader');
+var printer = require('./printer');
+var Env = require('./env').Env;
+var core = require('./core');
if (typeof module !== 'undefined') {
var readline = require('./node_readline');
}
@@ -11,31 +14,31 @@ function READ(str) {
// eval
function is_pair(x) {
- return types.sequential_Q(x) && x.length > 0;
+ return types._sequential_Q(x) && x.length > 0;
}
function quasiquote(ast) {
if (!is_pair(ast)) {
- return [types.symbol("quote"), ast];
+ return [types._symbol("quote"), ast];
} else if (ast[0].value === 'unquote') {
return ast[1];
} else if (is_pair(ast[0]) && ast[0][0].value === 'splice-unquote') {
- return [types.symbol("concat"), ast[0][1], quasiquote(ast.slice(1))];
+ return [types._symbol("concat"), ast[0][1], quasiquote(ast.slice(1))];
} else {
- return [types.symbol("cons"), quasiquote(ast[0]), quasiquote(ast.slice(1))];
+ return [types._symbol("cons"), quasiquote(ast[0]), quasiquote(ast.slice(1))];
}
}
function eval_ast(ast, env) {
- if (types.symbol_Q(ast)) {
+ if (types._symbol_Q(ast)) {
return env.get(ast);
- } else if (types.list_Q(ast)) {
+ } else if (types._list_Q(ast)) {
return ast.map(function(a) { return EVAL(a, env); });
- } else if (types.vector_Q(ast)) {
+ } else if (types._vector_Q(ast)) {
var v = ast.map(function(a) { return EVAL(a, env); });
v.__isvector__ = true;
return v;
- } else if (types.hash_map_Q(ast)) {
+ } else if (types._hash_map_Q(ast)) {
var new_hm = {};
for (k in ast) {
new_hm[EVAL(k, env)] = EVAL(ast[k], env);
@@ -48,50 +51,52 @@ function eval_ast(ast, env) {
function _EVAL(ast, env) {
while (true) {
- //console.log("EVAL:", types._pr_str(ast, true));
- if (!types.list_Q(ast)) {
- return eval_ast(ast, env);
+
+ //console.log("EVAL:", types._pr_str(ast, true));
+ if (!types._list_Q(ast)) {
+ return eval_ast(ast, env);
+ }
+
+ // apply list
+ var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3];
+ switch (a0.value) {
+ case "def!":
+ var res = EVAL(a2, env);
+ return env.set(a1, res);
+ case "let*":
+ var let_env = new Env(env);
+ for (var i=0; i < a1.length; i+=2) {
+ let_env.set(a1[i].value, EVAL(a1[i+1], let_env));
}
-
- // apply list
- var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3];
- switch (a0.value) {
- case "def!":
- var res = EVAL(a2, env);
- return env.set(a1, res);
- case "let*":
- var let_env = new types.Env(env);
- for (var i=0; i < a1.length; i+=2) {
- let_env.set(a1[i].value, EVAL(a1[i+1], let_env));
- }
- return EVAL(a2, let_env);
- case "quote":
- return a1;
- case "quasiquote":
- return EVAL(quasiquote(a1), env);
- case "do":
- eval_ast(ast.slice(1, -1), env);
- ast = ast[ast.length-1];
- break;
- case "if":
- var cond = EVAL(a1, env);
- if (cond === null || cond === false) {
- ast = (typeof a3 !== "undefined") ? a3 : null;
- } else {
- ast = a2;
- }
- break;
- case "fn*":
- return types.new_function(EVAL, a2, env, a1);
- default:
- var el = eval_ast(ast, env), f = el[0], meta = f.__meta__;
- if (meta && meta.exp) {
- ast = meta.exp;
- env = new types.Env(meta.env, meta.params, el.slice(1));
- } else {
- return f.apply(f, el.slice(1));
- }
+ return EVAL(a2, let_env);
+ case "quote":
+ return a1;
+ case "quasiquote":
+ return EVAL(quasiquote(a1), env);
+ case "do":
+ eval_ast(ast.slice(1, -1), env);
+ ast = ast[ast.length-1];
+ break;
+ case "if":
+ var cond = EVAL(a1, env);
+ if (cond === null || cond === false) {
+ ast = (typeof a3 !== "undefined") ? a3 : null;
+ } else {
+ ast = a2;
}
+ break;
+ case "fn*":
+ return types._function(EVAL, Env, a2, env, a1);
+ default:
+ var el = eval_ast(ast, env), f = el[0], meta = f.__meta__;
+ if (meta && meta.exp) {
+ ast = meta.exp;
+ env = new Env(meta.env, meta.params, el.slice(1));
+ } else {
+ return f.apply(f, el.slice(1));
+ }
+ }
+
}
}
@@ -102,16 +107,16 @@ function EVAL(ast, env) {
// print
function PRINT(exp) {
- return types._pr_str(exp, true);
+ return printer._pr_str(exp, true);
}
// repl
-var repl_env = new types.Env();
+var repl_env = new Env();
var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); };
_ref = function (k,v) { repl_env.set(k, v); }
-// Import types functions
-for (var n in types.ns) { repl_env.set(n, types.ns[n]); }
+// Import core functions
+for (var n in core.ns) { repl_env.set(n, core.ns[n]); }
_ref('read-string', reader.read_str);
_ref('eval', function(ast) { return EVAL(ast, repl_env); });
diff --git a/js/step8_macros.js b/js/step8_macros.js
index 65d7a87..3ad3e31 100644
--- a/js/step8_macros.js
+++ b/js/step8_macros.js
@@ -1,5 +1,8 @@
var types = require('./types');
var reader = require('./reader');
+var printer = require('./printer');
+var Env = require('./env').Env;
+var core = require('./core');
if (typeof module !== 'undefined') {
var readline = require('./node_readline');
}
@@ -11,24 +14,24 @@ function READ(str) {
// eval
function is_pair(x) {
- return types.sequential_Q(x) && x.length > 0;
+ return types._sequential_Q(x) && x.length > 0;
}
function quasiquote(ast) {
if (!is_pair(ast)) {
- return [types.symbol("quote"), ast];
+ return [types._symbol("quote"), ast];
} else if (ast[0].value === 'unquote') {
return ast[1];
} else if (is_pair(ast[0]) && ast[0][0].value === 'splice-unquote') {
- return [types.symbol("concat"), ast[0][1], quasiquote(ast.slice(1))];
+ return [types._symbol("concat"), ast[0][1], quasiquote(ast.slice(1))];
} else {
- return [types.symbol("cons"), quasiquote(ast[0]), quasiquote(ast.slice(1))];
+ return [types._symbol("cons"), quasiquote(ast[0]), quasiquote(ast.slice(1))];
}
}
function is_macro_call(ast, env) {
- return types.list_Q(ast) &&
- types.symbol_Q(ast[0]) &&
+ return types._list_Q(ast) &&
+ types._symbol_Q(ast[0]) &&
env.find(ast[0].value) &&
env.get(ast[0].value)._ismacro_;
}
@@ -42,15 +45,15 @@ function macroexpand(ast, env) {
}
function eval_ast(ast, env) {
- if (types.symbol_Q(ast)) {
+ if (types._symbol_Q(ast)) {
return env.get(ast);
- } else if (types.list_Q(ast)) {
+ } else if (types._list_Q(ast)) {
return ast.map(function(a) { return EVAL(a, env); });
- } else if (types.vector_Q(ast)) {
+ } else if (types._vector_Q(ast)) {
var v = ast.map(function(a) { return EVAL(a, env); });
v.__isvector__ = true;
return v;
- } else if (types.hash_map_Q(ast)) {
+ } else if (types._hash_map_Q(ast)) {
var new_hm = {};
for (k in ast) {
new_hm[EVAL(k, env)] = EVAL(ast[k], env);
@@ -63,59 +66,61 @@ function eval_ast(ast, env) {
function _EVAL(ast, env) {
while (true) {
- //console.log("EVAL:", types._pr_str(ast, true));
- if (!types.list_Q(ast)) {
- return eval_ast(ast, env);
+
+ //console.log("EVAL:", types._pr_str(ast, true));
+ if (!types._list_Q(ast)) {
+ return eval_ast(ast, env);
+ }
+
+ // apply list
+ ast = macroexpand(ast, env);
+ if (!types._list_Q(ast)) { return ast; }
+
+ var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3];
+ switch (a0.value) {
+ case "def!":
+ var res = EVAL(a2, env);
+ return env.set(a1, res);
+ case "let*":
+ var let_env = new Env(env);
+ for (var i=0; i < a1.length; i+=2) {
+ let_env.set(a1[i].value, EVAL(a1[i+1], let_env));
}
-
- // apply list
- ast = macroexpand(ast, env);
- if (!types.list_Q(ast)) { return ast; }
-
- var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3];
- switch (a0.value) {
- case "def!":
- var res = EVAL(a2, env);
- return env.set(a1, res);
- case "let*":
- var let_env = new types.Env(env);
- for (var i=0; i < a1.length; i+=2) {
- let_env.set(a1[i].value, EVAL(a1[i+1], let_env));
- }
- return EVAL(a2, let_env);
- case "quote":
- return a1;
- case "quasiquote":
- return EVAL(quasiquote(a1), env);
- case 'defmacro!':
- var func = EVAL(a2, env);
- func._ismacro_ = true;
- return env.set(a1, func);
- case 'macroexpand':
- return macroexpand(a1, env);
- case "do":
- eval_ast(ast.slice(1, -1), env);
- ast = ast[ast.length-1];
- break;
- case "if":
- var cond = EVAL(a1, env);
- if (cond === null || cond === false) {
- ast = (typeof a3 !== "undefined") ? a3 : null;
- } else {
- ast = a2;
- }
- break;
- case "fn*":
- return types.new_function(EVAL, a2, env, a1);
- default:
- var el = eval_ast(ast, env), f = el[0], meta = f.__meta__;
- if (meta && meta.exp) {
- ast = meta.exp;
- env = new types.Env(meta.env, meta.params, el.slice(1));
- } else {
- return f.apply(f, el.slice(1));
- }
+ return EVAL(a2, let_env);
+ case "quote":
+ return a1;
+ case "quasiquote":
+ return EVAL(quasiquote(a1), env);
+ case 'defmacro!':
+ var func = EVAL(a2, env);
+ func._ismacro_ = true;
+ return env.set(a1, func);
+ case 'macroexpand':
+ return macroexpand(a1, env);
+ case "do":
+ eval_ast(ast.slice(1, -1), env);
+ ast = ast[ast.length-1];
+ break;
+ case "if":
+ var cond = EVAL(a1, env);
+ if (cond === null || cond === false) {
+ ast = (typeof a3 !== "undefined") ? a3 : null;
+ } else {
+ ast = a2;
}
+ break;
+ case "fn*":
+ return types._function(EVAL, Env, a2, env, a1);
+ default:
+ var el = eval_ast(ast, env), f = el[0], meta = f.__meta__;
+ if (meta && meta.exp) {
+ ast = meta.exp;
+ env = new Env(meta.env, meta.params, el.slice(1));
+ } else {
+ return f.apply(f, el.slice(1));
+ }
+ }
+
}
}
@@ -126,16 +131,16 @@ function EVAL(ast, env) {
// print
function PRINT(exp) {
- return types._pr_str(exp, true);
+ return printer._pr_str(exp, true);
}
// repl
-var repl_env = new types.Env();
+var repl_env = new Env();
var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); };
_ref = function (k,v) { repl_env.set(k, v); }
-// Import types functions
-for (var n in types.ns) { repl_env.set(n, types.ns[n]); }
+// Import core functions
+for (var n in core.ns) { repl_env.set(n, core.ns[n]); }
_ref('read-string', reader.read_str);
_ref('eval', function(ast) { return EVAL(ast, repl_env); });
diff --git a/js/step9_interop.js b/js/step9_interop.js
index bfc01cb..3c83e51 100644
--- a/js/step9_interop.js
+++ b/js/step9_interop.js
@@ -1,5 +1,8 @@
var types = require('./types');
var reader = require('./reader');
+var printer = require('./printer');
+var Env = require('./env').Env;
+var core = require('./core');
if (typeof module !== 'undefined') {
var readline = require('./node_readline');
}
@@ -11,24 +14,24 @@ function READ(str) {
// eval
function is_pair(x) {
- return types.sequential_Q(x) && x.length > 0;
+ return types._sequential_Q(x) && x.length > 0;
}
function quasiquote(ast) {
if (!is_pair(ast)) {
- return [types.symbol("quote"), ast];
+ return [types._symbol("quote"), ast];
} else if (ast[0].value === 'unquote') {
return ast[1];
} else if (is_pair(ast[0]) && ast[0][0].value === 'splice-unquote') {
- return [types.symbol("concat"), ast[0][1], quasiquote(ast.slice(1))];
+ return [types._symbol("concat"), ast[0][1], quasiquote(ast.slice(1))];
} else {
- return [types.symbol("cons"), quasiquote(ast[0]), quasiquote(ast.slice(1))];
+ return [types._symbol("cons"), quasiquote(ast[0]), quasiquote(ast.slice(1))];
}
}
function is_macro_call(ast, env) {
- return types.list_Q(ast) &&
- types.symbol_Q(ast[0]) &&
+ return types._list_Q(ast) &&
+ types._symbol_Q(ast[0]) &&
env.find(ast[0].value) &&
env.get(ast[0].value)._ismacro_;
}
@@ -42,15 +45,15 @@ function macroexpand(ast, env) {
}
function eval_ast(ast, env) {
- if (types.symbol_Q(ast)) {
+ if (types._symbol_Q(ast)) {
return env.get(ast);
- } else if (types.list_Q(ast)) {
+ } else if (types._list_Q(ast)) {
return ast.map(function(a) { return EVAL(a, env); });
- } else if (types.vector_Q(ast)) {
+ } else if (types._vector_Q(ast)) {
var v = ast.map(function(a) { return EVAL(a, env); });
v.__isvector__ = true;
return v;
- } else if (types.hash_map_Q(ast)) {
+ } else if (types._hash_map_Q(ast)) {
var new_hm = {};
for (k in ast) {
new_hm[EVAL(k, env)] = EVAL(ast[k], env);
@@ -63,65 +66,67 @@ function eval_ast(ast, env) {
function _EVAL(ast, env) {
while (true) {
- //console.log("EVAL:", types._pr_str(ast, true));
- if (!types.list_Q(ast)) {
- return eval_ast(ast, env);
+
+ //console.log("EVAL:", types._pr_str(ast, true));
+ if (!types._list_Q(ast)) {
+ return eval_ast(ast, env);
+ }
+
+ // apply list
+ ast = macroexpand(ast, env);
+ if (!types._list_Q(ast)) { return ast; }
+
+ var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3];
+ switch (a0.value) {
+ case "def!":
+ var res = EVAL(a2, env);
+ return env.set(a1, res);
+ case "let*":
+ var let_env = new Env(env);
+ for (var i=0; i < a1.length; i+=2) {
+ let_env.set(a1[i].value, EVAL(a1[i+1], let_env));
}
-
- // apply list
- ast = macroexpand(ast, env);
- if (!types.list_Q(ast)) { return ast; }
-
- var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3];
- switch (a0.value) {
- case "def!":
- var res = EVAL(a2, env);
- return env.set(a1, res);
- case "let*":
- var let_env = new types.Env(env);
- for (var i=0; i < a1.length; i+=2) {
- let_env.set(a1[i].value, EVAL(a1[i+1], let_env));
- }
- return EVAL(a2, let_env);
- case "quote":
- return a1;
- case "quasiquote":
- return EVAL(quasiquote(a1), env);
- case 'defmacro!':
- var func = EVAL(a2, env);
- func._ismacro_ = true;
- return env.set(a1, func);
- case 'macroexpand':
- return macroexpand(a1, env);
- case "js*":
- return eval(a1.toString());
- case ".":
- var el = eval_ast(ast.slice(2), env),
- f = eval(a1.toString());
- return f.apply(f, el);
- case "do":
- eval_ast(ast.slice(1, -1), env);
- ast = ast[ast.length-1];
- break;
- case "if":
- var cond = EVAL(a1, env);
- if (cond === null || cond === false) {
- ast = (typeof a3 !== "undefined") ? a3 : null;
- } else {
- ast = a2;
- }
- break;
- case "fn*":
- return types.new_function(EVAL, a2, env, a1);
- default:
- var el = eval_ast(ast, env), f = el[0], meta = f.__meta__;
- if (meta && meta.exp) {
- ast = meta.exp;
- env = new types.Env(meta.env, meta.params, el.slice(1));
- } else {
- return f.apply(f, el.slice(1));
- }
+ return EVAL(a2, let_env);
+ case "quote":
+ return a1;
+ case "quasiquote":
+ return EVAL(quasiquote(a1), env);
+ case 'defmacro!':
+ var func = EVAL(a2, env);
+ func._ismacro_ = true;
+ return env.set(a1, func);
+ case 'macroexpand':
+ return macroexpand(a1, env);
+ case "js*":
+ return eval(a1.toString());
+ case ".":
+ var el = eval_ast(ast.slice(2), env),
+ f = eval(a1.toString());
+ return f.apply(f, el);
+ case "do":
+ eval_ast(ast.slice(1, -1), env);
+ ast = ast[ast.length-1];
+ break;
+ case "if":
+ var cond = EVAL(a1, env);
+ if (cond === null || cond === false) {
+ ast = (typeof a3 !== "undefined") ? a3 : null;
+ } else {
+ ast = a2;
}
+ break;
+ case "fn*":
+ return types._function(EVAL, Env, a2, env, a1);
+ default:
+ var el = eval_ast(ast, env), f = el[0], meta = f.__meta__;
+ if (meta && meta.exp) {
+ ast = meta.exp;
+ env = new Env(meta.env, meta.params, el.slice(1));
+ } else {
+ return f.apply(f, el.slice(1));
+ }
+ }
+
}
}
@@ -132,16 +137,16 @@ function EVAL(ast, env) {
// print
function PRINT(exp) {
- return types._pr_str(exp, true);
+ return printer._pr_str(exp, true);
}
// repl
-var repl_env = new types.Env();
+var repl_env = new Env();
var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); };
_ref = function (k,v) { repl_env.set(k, v); }
-// Import types functions
-for (var n in types.ns) { repl_env.set(n, types.ns[n]); }
+// Import core functions
+for (var n in core.ns) { repl_env.set(n, core.ns[n]); }
_ref('read-string', reader.read_str);
_ref('eval', function(ast) { return EVAL(ast, repl_env); });
diff --git a/js/stepA_more.js b/js/stepA_more.js
index 2778649..a4e1bda 100644
--- a/js/stepA_more.js
+++ b/js/stepA_more.js
@@ -1,5 +1,8 @@
var types = require('./types');
var reader = require('./reader');
+var printer = require('./printer');
+var Env = require('./env').Env;
+var core = require('./core');
if (typeof module !== 'undefined') {
var readline = require('./node_readline');
}
@@ -11,24 +14,24 @@ function READ(str) {
// eval
function is_pair(x) {
- return types.sequential_Q(x) && x.length > 0;
+ return types._sequential_Q(x) && x.length > 0;
}
function quasiquote(ast) {
if (!is_pair(ast)) {
- return [types.symbol("quote"), ast];
+ return [types._symbol("quote"), ast];
} else if (ast[0].value === 'unquote') {
return ast[1];
} else if (is_pair(ast[0]) && ast[0][0].value === 'splice-unquote') {
- return [types.symbol("concat"), ast[0][1], quasiquote(ast.slice(1))];
+ return [types._symbol("concat"), ast[0][1], quasiquote(ast.slice(1))];
} else {
- return [types.symbol("cons"), quasiquote(ast[0]), quasiquote(ast.slice(1))];
+ return [types._symbol("cons"), quasiquote(ast[0]), quasiquote(ast.slice(1))];
}
}
function is_macro_call(ast, env) {
- return types.list_Q(ast) &&
- types.symbol_Q(ast[0]) &&
+ return types._list_Q(ast) &&
+ types._symbol_Q(ast[0]) &&
env.find(ast[0].value) &&
env.get(ast[0].value)._ismacro_;
}
@@ -42,15 +45,15 @@ function macroexpand(ast, env) {
}
function eval_ast(ast, env) {
- if (types.symbol_Q(ast)) {
+ if (types._symbol_Q(ast)) {
return env.get(ast);
- } else if (types.list_Q(ast)) {
+ } else if (types._list_Q(ast)) {
return ast.map(function(a) { return EVAL(a, env); });
- } else if (types.vector_Q(ast)) {
+ } else if (types._vector_Q(ast)) {
var v = ast.map(function(a) { return EVAL(a, env); });
v.__isvector__ = true;
return v;
- } else if (types.hash_map_Q(ast)) {
+ } else if (types._hash_map_Q(ast)) {
var new_hm = {};
for (k in ast) {
new_hm[EVAL(k, env)] = EVAL(ast[k], env);
@@ -63,76 +66,78 @@ function eval_ast(ast, env) {
function _EVAL(ast, env) {
while (true) {
- //console.log("EVAL:", types._pr_str(ast, true));
- if (!types.list_Q(ast)) {
- return eval_ast(ast, env);
+
+ //console.log("EVAL:", types._pr_str(ast, true));
+ if (!types._list_Q(ast)) {
+ return eval_ast(ast, env);
+ }
+
+ // apply list
+ ast = macroexpand(ast, env);
+ if (!types._list_Q(ast)) { return ast; }
+
+ var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3];
+ switch (a0.value) {
+ case "def!":
+ var res = EVAL(a2, env);
+ return env.set(a1, res);
+ case "let*":
+ var let_env = new Env(env);
+ for (var i=0; i < a1.length; i+=2) {
+ let_env.set(a1[i].value, EVAL(a1[i+1], let_env));
}
-
- // apply list
- ast = macroexpand(ast, env);
- if (!types.list_Q(ast)) { return ast; }
-
- var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3];
- switch (a0.value) {
- case "def!":
- var res = EVAL(a2, env);
- return env.set(a1, res);
- case "let*":
- var let_env = new types.Env(env);
- for (var i=0; i < a1.length; i+=2) {
- let_env.set(a1[i].value, EVAL(a1[i+1], let_env));
- }
- return EVAL(a2, let_env);
- case "quote":
- return a1;
- case "quasiquote":
- return EVAL(quasiquote(a1), env);
- case 'defmacro!':
- var func = EVAL(a2, env);
- func._ismacro_ = true;
- return env.set(a1, func);
- case 'macroexpand':
- return macroexpand(a1, env);
- case "js*":
- return eval(a1.toString());
- case ".":
- var el = eval_ast(ast.slice(2), env),
- f = eval(a1.toString());
- return f.apply(f, el);
- case "try*":
- try {
- return EVAL(a1, env);
- } catch (exc) {
- if (a2 && a2[0].value === "catch*") {
- if (exc instanceof Error) { exc = exc.message; }
- return EVAL(a2[2], new types.Env(env, [a2[1]], [exc]));
- } else {
- throw exc;
- }
- }
- case "do":
- eval_ast(ast.slice(1, -1), env);
- ast = ast[ast.length-1];
- break;
- case "if":
- var cond = EVAL(a1, env);
- if (cond === null || cond === false) {
- ast = (typeof a3 !== "undefined") ? a3 : null;
- } else {
- ast = a2;
- }
- break;
- case "fn*":
- return types.new_function(EVAL, a2, env, a1);
- default:
- var el = eval_ast(ast, env), f = el[0], meta = f.__meta__;
- if (meta && meta.exp) {
- ast = meta.exp;
- env = new types.Env(meta.env, meta.params, el.slice(1));
+ return EVAL(a2, let_env);
+ case "quote":
+ return a1;
+ case "quasiquote":
+ return EVAL(quasiquote(a1), env);
+ case 'defmacro!':
+ var func = EVAL(a2, env);
+ func._ismacro_ = true;
+ return env.set(a1, func);
+ case 'macroexpand':
+ return macroexpand(a1, env);
+ case "js*":
+ return eval(a1.toString());
+ case ".":
+ var el = eval_ast(ast.slice(2), env),
+ f = eval(a1.toString());
+ return f.apply(f, el);
+ case "try*":
+ try {
+ return EVAL(a1, env);
+ } catch (exc) {
+ if (a2 && a2[0].value === "catch*") {
+ if (exc instanceof Error) { exc = exc.message; }
+ return EVAL(a2[2], new Env(env, [a2[1]], [exc]));
} else {
- return f.apply(f, el.slice(1));
+ throw exc;
}
}
+ case "do":
+ eval_ast(ast.slice(1, -1), env);
+ ast = ast[ast.length-1];
+ break;
+ case "if":
+ var cond = EVAL(a1, env);
+ if (cond === null || cond === false) {
+ ast = (typeof a3 !== "undefined") ? a3 : null;
+ } else {
+ ast = a2;
+ }
+ break;
+ case "fn*":
+ return types._function(EVAL, Env, a2, env, a1);
+ default:
+ var el = eval_ast(ast, env), f = el[0], meta = f.__meta__;
+ if (meta && meta.exp) {
+ ast = meta.exp;
+ env = new Env(meta.env, meta.params, el.slice(1));
+ } else {
+ return f.apply(f, el.slice(1));
+ }
+ }
+
}
}
@@ -143,16 +148,16 @@ function EVAL(ast, env) {
// print
function PRINT(exp) {
- return types._pr_str(exp, true);
+ return printer._pr_str(exp, true);
}
// repl
-var repl_env = new types.Env();
+var repl_env = new Env();
var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); };
_ref = function (k,v) { repl_env.set(k, v); }
-// Import types functions
-for (var n in types.ns) { repl_env.set(n, types.ns[n]); }
+// Import core functions
+for (var n in core.ns) { repl_env.set(n, core.ns[n]); }
_ref('readline', readline.readline)
_ref('read-string', reader.read_str);
diff --git a/js/types.js b/js/types.js
index 18fad0a..7fd2962 100644
--- a/js/types.js
+++ b/js/types.js
@@ -7,59 +7,17 @@ if (typeof module === 'undefined') {
var print = exports.print = function () { console.log.apply(console, arguments); };
}
-// General utility functions
-
-// Clone a function
-Function.prototype.clone = function() {
- var that = this;
- var temp = function () { return that.apply(this, arguments); };
- for( key in this ) {
- temp[key] = this[key];
- }
- return temp;
-};
-
-function _clone (obj) {
- var new_obj;
- switch (obj_type(obj)) {
- case 'list':
- new_obj = obj.slice(0);
- break;
- case 'vector':
- new_obj = obj.slice(0);
- new_obj.__isvector__ = true;
- break;
- case 'hash-map':
- new_obj = {};
- for (var k in obj) {
- if (obj.hasOwnProperty(k)) { new_obj[k] = obj[k]; }
- }
- break;
- case 'function':
- new_obj = obj.clone();
- break;
- default:
- throw new Error("clone of non-collection: " + obj_type(obj));
- }
- return new_obj;
-}
-
-
-
-
-function nil_Q(a) { return a === null ? true : false; }
-function true_Q(a) { return a === true ? true : false; }
-function false_Q(a) { return a === false ? true : false; }
-
-function obj_type(obj) {
- if (symbol_Q(obj)) { return 'symbol'; }
- else if (list_Q(obj)) { return 'list'; }
- else if (vector_Q(obj)) { return 'vector'; }
- else if (hash_map_Q(obj)) { return 'hash-map'; }
- else if (nil_Q(obj)) { return 'nil'; }
- else if (true_Q(obj)) { return 'true'; }
- else if (false_Q(obj)) { return 'false'; }
- else if (atom_Q(obj)) { return 'atom'; }
+// General fucnctions
+
+function _obj_type(obj) {
+ if (_symbol_Q(obj)) { return 'symbol'; }
+ else if (_list_Q(obj)) { return 'list'; }
+ else if (_vector_Q(obj)) { return 'vector'; }
+ else if (_hash_map_Q(obj)) { return 'hash-map'; }
+ else if (_nil_Q(obj)) { return 'nil'; }
+ else if (_true_Q(obj)) { return 'true'; }
+ else if (_false_Q(obj)) { return 'false'; }
+ else if (_atom_Q(obj)) { return 'atom'; }
else {
switch (typeof(obj)) {
case 'number': return 'number';
@@ -70,82 +28,12 @@ function obj_type(obj) {
}
}
-function _pr_str(obj, print_readably) {
- if (typeof print_readably === 'undefined') { print_readably = true; }
- var _r = print_readably;
- var ot = obj_type(obj);
- switch (ot) {
- case 'list':
- var ret = obj.map(function(e) { return _pr_str(e,_r); });
- return "(" + ret.join(' ') + ")";
- case 'vector':
- var ret = obj.map(function(e) { return _pr_str(e,_r); });
- return "[" + ret.join(' ') + "]";
- case 'hash-map':
- var ret = [];
- for (var k in obj) {
- ret.push(_pr_str(k,_r), _pr_str(obj[k],_r));
- }
- return "{" + ret.join(' ') + "}";
- case 'string':
- if (print_readably) {
- return '"' + obj.replace(/\\/, "\\\\").replace(/"/g, '\\"') + '"';
- } else {
- return obj;
- }
- case 'nil':
- return "nil";
- case 'atom':
- return "(atom " + _pr_str(obj.val,_r) + ")";
- default:
- return obj.toString();
- }
-}
-
-function pr_str() {
- return Array.prototype.map.call(arguments,function(exp) {
- return _pr_str(exp, true);
- }).join(" ");
-}
-
-function str() {
- return Array.prototype.map.call(arguments,function(exp) {
- return _pr_str(exp, false);
- }).join("");
-}
-
-function prn() {
- print.apply({}, Array.prototype.map.call(arguments,function(exp) {
- return _pr_str(exp, true);
- }));
-}
-
-function println() {
- print.apply({}, Array.prototype.map.call(arguments,function(exp) {
- return _pr_str(exp, false);
- }));
-}
-
-function with_meta(obj, m) {
- var new_obj = _clone(obj);
- new_obj.__meta__ = m;
- return new_obj;
-}
-
-function meta(obj) {
- // TODO: support symbols and atoms
- if ((!sequential_Q(obj)) &&
- (!(hash_map_Q(obj))) &&
- (!(function_Q(obj)))) {
- throw new Error("attempt to get metadata from: " + obj_type(obj));
- }
- return obj.__meta__;
-}
+function _sequential_Q(lst) { return _list_Q(lst) || _vector_Q(lst); }
-function equal_Q (a, b) {
- var ota = obj_type(a), otb = obj_type(b);
- if (!(ota === otb || (sequential_Q(a) && sequential_Q(b)))) {
+function _equal_Q (a, b) {
+ var ota = _obj_type(a), otb = _obj_type(b);
+ if (!(ota === otb || (_sequential_Q(a) && _sequential_Q(b)))) {
return false;
}
switch (ota) {
@@ -154,7 +42,7 @@ function equal_Q (a, b) {
case 'vector':
if (a.length !== b.length) { return false; }
for (var i=0; i<a.length; i++) {
- if (! equal_Q(a[i], b[i])) { return false; }
+ if (! _equal_Q(a[i], b[i])) { return false; }
}
return true;
case 'hash-map':
@@ -172,6 +60,37 @@ function equal_Q (a, b) {
}
+function _clone (obj) {
+ var new_obj;
+ switch (_obj_type(obj)) {
+ case 'list':
+ new_obj = obj.slice(0);
+ break;
+ case 'vector':
+ new_obj = obj.slice(0);
+ new_obj.__isvector__ = true;
+ break;
+ case 'hash-map':
+ new_obj = {};
+ for (var k in obj) {
+ if (obj.hasOwnProperty(k)) { new_obj[k] = obj[k]; }
+ }
+ break;
+ case 'function':
+ new_obj = obj.clone();
+ break;
+ default:
+ throw new Error("clone of non-collection: " + _obj_type(obj));
+ }
+ return new_obj;
+}
+
+
+// Scalars
+function _nil_Q(a) { return a === null ? true : false; }
+function _true_Q(a) { return a === true ? true : false; }
+function _false_Q(a) { return a === false ? true : false; }
+
// Symbols
function Symbol(name) {
@@ -179,68 +98,61 @@ function Symbol(name) {
return this;
}
Symbol.prototype.toString = function() { return this.value; }
-
-function symbol(name) { return new Symbol(name); }
-
-function symbol_Q(obj) { return obj instanceof Symbol; }
+function _symbol(name) { return new Symbol(name); }
+function _symbol_Q(obj) { return obj instanceof Symbol; }
// Functions
-function new_function(func, exp, env, params) {
+function _function(Eval, Env, exp, env, params) {
var f = function() {
// TODO: figure out why this throws with 'and' macro
//throw new Error("Attempt to invoke mal function directly");
- return func(exp, new Env(env, params, arguments));
+ return Eval(exp, new Env(env, params, arguments));
};
f.__meta__ = {exp: exp, env: env, params: params};
return f;
-
}
-function function_Q(f) { return typeof f == "function"; }
-
+function _function_Q(obj) { return typeof obj == "function"; }
+Function.prototype.clone = function() {
+ var that = this;
+ var temp = function () { return that.apply(this, arguments); };
+ for( key in this ) {
+ temp[key] = this[key];
+ }
+ return temp;
+};
-// Errors/Exceptions
-function mal_throw(exc) { throw exc; }
+// Lists
+function _list() { return Array.prototype.slice.call(arguments, 0); }
+function _list_Q(obj) { return Array.isArray(obj) && !obj.__isvector__; }
// Vectors
-function vector() {
+function _vector() {
var v = Array.prototype.slice.call(arguments, 0);
v.__isvector__ = true;
return v;
}
+function _vector_Q(obj) { return Array.isArray(obj) && obj.__isvector__; }
-function vector_Q(v) { return Array.isArray(v) && v.__isvector__; }
-
-
-// Lists
-
-function list() {
- return Array.prototype.slice.call(arguments, 0);
-}
-
-function list_Q(lst) { return Array.isArray(lst) && !lst.__isvector__; }
// Hash Maps
-
-function hash_map() {
+function _hash_map() {
if (arguments.length % 2 === 1) {
throw new Error("Odd number of hash map arguments");
}
var args = [{}].concat(Array.prototype.slice.call(arguments, 0));
- return assoc_BANG.apply(null, args);
+ return _assoc_BANG.apply(null, args);
}
-
-function hash_map_Q(hm) {
+function _hash_map_Q(hm) {
return typeof hm === "object" &&
!Array.isArray(hm) &&
- !(hm === null) &&
+ !(hm === null) &&
!(hm instanceof Atom);
}
-
-function assoc_BANG(hm) {
+function _assoc_BANG(hm) {
if (arguments.length % 2 !== 1) {
throw new Error("Odd number of assoc arguments");
}
@@ -258,14 +170,7 @@ function assoc_BANG(hm) {
}
return hm;
}
-
-function assoc(src_hm) {
- var hm = _clone(src_hm);
- var args = [hm].concat(Array.prototype.slice.call(arguments, 1));
- return assoc_BANG.apply(null, args);
-}
-
-function dissoc_BANG(hm) {
+function _dissoc_BANG(hm) {
for (var i=1; i<arguments.length; i++) {
var ktoken = arguments[i];
delete hm[ktoken];
@@ -273,166 +178,32 @@ function dissoc_BANG(hm) {
return hm;
}
-function dissoc(src_hm) {
- var hm = _clone(src_hm);
- var args = [hm].concat(Array.prototype.slice.call(arguments, 1));
- return dissoc_BANG.apply(null, args);
-}
-
-function get(hm, key) {
- if (key in hm) {
- return hm[key];
- } else {
- return null;
- }
-}
-
-function contains_Q(hm, key) {
- if (key in hm) { return true; } else { return false; }
-}
-
-function keys(hm) { return Object.keys(hm); }
-function vals(hm) { return Object.keys(hm).map(function(k) { return hm[k]; }); }
-
// Atoms
function Atom(val) { this.val = val; }
-function atom(val) { return new Atom(val); }
-function atom_Q(atm) { return atm instanceof Atom; }
-function deref(atm) { return atm.val; }
-function reset_BANG(atm, val) { return atm.val = val; }
-function swap_BANG(atm, f) {
- var args = [atm.val].concat(Array.prototype.slice.call(arguments, 2));
- atm.val = f.apply(f, args);
- return atm.val;
-}
-
-
-// Sequence operations
-function sequential_Q(lst) { return list_Q(lst) || vector_Q(lst); }
-
-function nth(lst, idx) { return lst[idx]; }
-
-function count(s) {
- if (Array.isArray(s)) { return s.length; }
- else { return Object.keys(s).length; }
-}
-
-function empty_Q(lst) { return lst.length === 0; }
-
-function cons(a, b) { return [a].concat(b); }
-
-function concat(lst) {
- lst = lst || [];
- return lst.concat.apply(lst, Array.prototype.slice.call(arguments, 1));
-}
-
-function conj(lst) {
- if (list_Q(lst)) {
- return Array.prototype.slice.call(arguments, 1).reverse().concat(lst);
- } else {
- var v = lst.concat(Array.prototype.slice.call(arguments, 1));
- v.__isvector__ = true;
- return v;
- }
-}
-
-function first(lst) { return lst[0]; }
-
-function rest(lst) { return lst.slice(1); }
-
-
-
-// General list related functions
-function apply(f) {
- var args = Array.prototype.slice.call(arguments, 1);
- return f.apply(f, args.slice(0, args.length-1).concat(args[args.length-1]));
-}
-
-function map(f, lst) {
- return lst.map(function(el){ return f(el); });
-}
-
-
-// Env implementation
-function Env(outer, binds, exprs) {
- this.data = {};
- this.outer = outer || null;
-
- if (binds && exprs) {
- // Returns a new Env with symbols in binds bound to
- // corresponding values in exprs
- // TODO: check types of binds and exprs and compare lengths
- for (var i=0; i<binds.length;i++) {
- if (binds[i].value === "&") {
- // variable length arguments
- this.data[binds[i+1].value] = Array.prototype.slice.call(exprs, i);
- break;
- } else {
- this.data[binds[i].value] = exprs[i];
- }
- }
- }
- return this;
-}
-Env.prototype.find = function (key) {
- if (key in this.data) { return this; }
- else if (this.outer) { return this.outer.find(key); }
- else { return null; }
-};
-Env.prototype.set = function(key, value) { this.data[key] = value; return value; },
-Env.prototype.get = function(key) {
- var env = this.find(key);
- if (!env) { throw new Error("'" + key + "' not found"); }
- return env.data[key];
-};
-
-// types.ns is namespace of type functions
-var ns = {'pr-str': pr_str, 'str': str, 'prn': prn, 'println': println,
- 'with-meta': with_meta, 'meta': meta,
- type: obj_type, '=': equal_Q,
- symbol: symbol, 'symbol?': symbol_Q,
- 'nil?': nil_Q, 'true?': true_Q, 'false?': false_Q,
- '<' : function(a,b){return a<b;},
- '<=' : function(a,b){return a<=b;},
- '>' : function(a,b){return a>b;},
- '>=' : function(a,b){return a>=b;},
- '+' : function(a,b){return a+b;},
- '-' : function(a,b){return a-b;},
- '*' : function(a,b){return a*b;},
- '/' : function(a,b){return a/b;},
- 'throw': mal_throw,
- 'list': list, 'list?': list_Q,
- 'vector': vector, 'vector?': vector_Q,
- 'hash-map': hash_map, 'map?': hash_map_Q,
- 'assoc': assoc, 'dissoc': dissoc, 'get': get,
- 'contains?': contains_Q, 'keys': keys, 'vals': vals,
- 'atom': atom, 'atom?': atom_Q,
- "deref": deref, "reset!": reset_BANG, "swap!": swap_BANG,
- 'sequential?': sequential_Q, 'cons': cons, 'nth': nth,
- 'empty?': empty_Q, 'count': count, 'concat': concat,
- 'conj': conj, 'first': first, 'rest': rest,
- 'apply': apply, 'map': map};
-
-exports.ns = types.ns = ns;
-exports._pr_str = types._pr_str = _pr_str;
-exports.prn = types.prn = prn;
-exports.Env = types.Env = Env;
-
-exports.symbol = types.symbol = symbol;
-exports.symbol_Q = types.symbol_Q = symbol_Q;
-exports.hash_map = types.hash_map = hash_map;
-exports.hash_map_Q = types.hash_map_Q = hash_map_Q;
-exports.new_function = types.new_function = new_function;
-exports.list = types.list = list;
-exports.list_Q = types.list_Q = list_Q;
-exports.vector = types.vector = vector;
-exports.vector_Q = types.vector_Q = vector_Q;
-
-exports.sequential_Q = types.sequential_Q = sequential_Q;
-exports.cons = types.cons = cons;
-exports.concat = types.concat = concat;
-exports.first = types.first = first;
-exports.rest = types.rest = rest;
-exports.apply = types.apply = apply;
-exports.map = types.map = map;
+function _atom(val) { return new Atom(val); }
+function _atom_Q(atm) { return atm instanceof Atom; }
+
+
+// Exports
+exports._obj_type = types._obj_type = _obj_type;
+exports._sequential_Q = types._sequential_Q = _sequential_Q;
+exports._equal_Q = types._equal_Q = _equal_Q;
+exports._clone = types._clone = _clone;
+exports._nil_Q = types._nil_Q = _nil_Q;
+exports._true_Q = types._true_Q = _true_Q;
+exports._false_Q = types._false_Q = _false_Q;
+exports._symbol = types._symbol = _symbol;
+exports._symbol_Q = types._symbol_Q = _symbol_Q;
+exports._function = types._function = _function;
+exports._function_Q = types._function_Q = _function_Q;
+exports._list = types._list = _list;
+exports._list_Q = types._list_Q = _list_Q;
+exports._vector = types._vector = _vector;
+exports._vector_Q = types._vector_Q = _vector_Q;
+exports._hash_map = types._hash_map = _hash_map;
+exports._hash_map_Q = types._hash_map_Q = _hash_map_Q;
+exports._assoc_BANG = types._assoc_BANG = _assoc_BANG;
+exports._dissoc_BANG = types._dissoc_BANG = _dissoc_BANG;
+exports._atom = types._atom = _atom;
+exports._atom_Q = types._atom_Q = _atom_Q;
diff --git a/make/Makefile b/make/Makefile
index 1110397..2bb1e1e 100644
--- a/make/Makefile
+++ b/make/Makefile
@@ -1,7 +1,8 @@
TESTS = tests/types.mk tests/reader.mk tests/step9_interop.mk
-SOURCES = util.mk readline.mk gmsl.mk types.mk reader.mk stepA_more.mk
+SOURCES = util.mk readline.mk gmsl.mk types.mk reader.mk printer.mk \
+ env.mk core.mk stepA_more.mk
mal.mk: $(SOURCES)
echo "#!/usr/bin/make -f" > $@
diff --git a/make/core.mk b/make/core.mk
new file mode 100644
index 0000000..aaba3fd
--- /dev/null
+++ b/make/core.mk
@@ -0,0 +1,255 @@
+#
+# mal (Make a Lisp) Core functions
+#
+
+ifndef __mal_core_included
+__mal_core_included := true
+
+_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
+include $(_TOP_DIR)util.mk
+include $(_TOP_DIR)types.mk
+include $(_TOP_DIR)printer.mk
+
+
+# Errors/Exceptions
+throw = $(eval __ERROR := $(1))
+
+
+# General functions
+
+# Return the type of the object (or "make" if it's not a object
+obj_type = $(call _string,$(call _obj_type,$(1)))
+
+equal? = $(if $(call _equal?,$(word 1,$(1)),$(word 2,$(1))),$(__true),$(__false))
+
+
+# Scalar functions
+nil? = $(if $(call _nil?,$(1)),$(__true),$(__false))
+true? = $(if $(call _true?,$(1)),$(__true),$(__false))
+false? = $(if $(call _false?,$(1)),$(__true),$(__false))
+
+
+# Symbol functions
+symbol? = $(if $(call _symbol?,$(1)),$(__true),$(__false))
+
+
+# Number functions
+number? = $(if $(call _number?,$(1)),$(__true),$(__false))
+
+number_lt = $(if $(call int_lt,$($(word 1,$(1))_value),$($(word 2,$(1))_value)),$(__true),$(__false))
+number_lte = $(if $(call int_lte,$($(word 1,$(1))_value),$($(word 2,$(1))_value)),$(__true),$(__false))
+number_gt = $(if $(call int_gt,$($(word 1,$(1))_value),$($(word 2,$(1))_value)),$(__true),$(__false))
+number_gte = $(if $(call int_gte,$($(word 1,$(1))_value),$($(word 2,$(1))_value)),$(__true),$(__false))
+
+number_plus = $(call _pnumber,$(call int_plus,$($(word 1,$(1))_value),$($(word 2,$(1))_value)))
+number_subtract = $(call _pnumber,$(call int_subtract,$($(word 1,$(1))_value),$($(word 2,$(1))_value)))
+number_multiply = $(call _pnumber,$(call int_multiply,$($(word 1,$(1))_value),$($(word 2,$(1))_value)))
+number_divide = $(call _pnumber,$(call int_divide,$($(word 1,$(1))_value),$($(word 2,$(1))_value)))
+
+
+# String functions
+
+pr_str = $(call _string,$(call _pr_str_mult,$(1),yes, ))
+str = $(call _string,$(call _pr_str_mult,$(1),,))
+prn = $(info $(call _pr_str_mult,$(1),yes, ))
+println = $(info $(subst \n,$(NEWLINE),$(call _pr_str_mult,$(1),, )))
+
+string? = $(if $(call _string?,$(1)),$(__true),$(__false))
+
+subs = $(strip \
+ $(foreach start,$(call gmsl_plus,1,$(call int_decode,$($(word 2,$(1))_value))),\
+ $(foreach end,$(if $(3),$(call int_decode,$($(3)_value)),$(words $($(word 1,$(1))_value))),\
+ $(call _string,$(wordlist $(start),$(end),$($(word 1,$(1))_value))))))
+
+
+
+# Function functions
+function? = $(if $(call _function?,$(1)),$(__true),$(__false))
+
+
+# List functions
+list? = $(if $(call _list?,$(1)),$(__true),$(__false))
+
+
+# Vector functions
+vector? = $(if $(call _vector?,$(1)),$(__true),$(__false))
+
+
+# Hash map (associative array) functions
+hash_map? = $(if $(call _hash_map?,$(1)),$(__true),$(__false))
+
+# set a key/value in a copy of the hash map
+assoc = $(word 1,\
+ $(foreach hm,$(call _clone_obj,$(word 1,$(1))),\
+ $(hm) \
+ $(call _assoc_seq!,$(hm),$(wordlist 2,$(words $(1)),$(1)))))
+
+# unset keys in a copy of the hash map
+# TODO: this could be made more efficient by copying only the
+# keys that not being removed.
+dissoc = $(word 1,\
+ $(foreach hm,$(call _clone_obj,$(word 1,$(1))),\
+ $(hm) \
+ $(foreach key,$(wordlist 2,$(words $(1)),$(1)),\
+ $(call _dissoc!,$(hm),$(call str_decode,$($(key)_value))))))
+
+keys = $(foreach new_list,$(call _list),$(new_list)$(eval $(new_list)_value := $(foreach v,$(call __get_obj_values,$(1)),$(call _string,$(word 4,$(subst _, ,$(v)))))))
+
+vals = $(foreach new_list,$(call _list),$(new_list)$(eval $(new_list)_value := $(foreach v,$(call __get_obj_values,$(1)),$($(v)))))
+
+# Hash map and vector functions
+
+# retrieve the value of a string key object from the hash map, or
+# retrive a vector by number object index
+get = $(strip \
+ $(if $(call _hash_map?,$(word 1,$(1))),\
+ $(call _get,$(word 1,$(1)),$(call str_decode,$($(word 2,$(1))_value))),\
+ $(call _get,$(word 1,$(1)),$(call int_decode,$($(word 2,$(1))_value)))))
+
+contains? = $(if $(call _contains?,$(word 1,$(1)),$(call str_decode,$($(word 2,$(1))_value))),$(__true),$(__false))
+
+
+# sequence operations
+
+sequential? = $(if $(call _sequential?,$(1)),$(__true),$(__false))
+
+cons = $(word 1,$(foreach new_list,$(call _list),$(new_list) $(eval $(new_list)_value := $(strip $(word 1,$(1)) $(call __get_obj_values,$(word 2,$(1)))))))
+
+concat = $(word 1,$(foreach new_list,$(call _list),$(new_list) $(eval $(new_list)_value := $(strip $(foreach lst,$1,$(call __get_obj_values,$(lst)))))))
+
+nth = $(word $(call gmsl_plus,1,$(call int_decode,$($(word 2,$(1))_value))),$($(word 1,$(1))_value))
+
+sfirst = $(word 1,$($(1)_value))
+
+slast = $(word $(words $($(1)_value)),$($(1)_value))
+
+empty? = $(if $(call _EQ,0,$(if $(call _hash_map?,$(1)),$($(1)_size),$(words $($(1)_value)))),$(__true),$(__false))
+
+count = $(call _number,$(call _count,$(1)))
+
+conj = $(word 1,$(foreach new_list,$(call __new_obj_like,$(word 1,$(1))),\
+ $(new_list) \
+ $(eval $(new_list)_value := $(strip $($(word 1,$(1))_value))) \
+ $(if $(call _list?,$(new_list)),\
+ $(foreach elem,$(wordlist 2,$(words $(1)),$(1)),\
+ $(eval $(new_list)_value := $(strip $(elem) $($(new_list)_value)))),\
+ $(eval $(new_list)_value := $(strip $($(new_list)_value) $(wordlist 2,$(words $(1)),$(1)))))))
+
+# Creates a new vector/list of the everything after but the first
+# element
+srest = $(word 1,$(foreach new_list,$(call _list),\
+ $(new_list) \
+ $(eval $(new_list)_value := $(wordlist 2,$(words $($(1)_value)),$($(1)_value)))))
+
+# Takes a space separated arguments and invokes the first argument
+# (function object) using the remaining arguments.
+sapply = $(call $(word 1,$(1))_value,\
+ $(strip \
+ $(wordlist 2,$(call gmsl_subtract,$(words $(1)),1),$(1)) \
+ $($(word $(words $(1)),$(1))_value)))
+
+# Map a function object over a list object
+smap = $(strip\
+ $(foreach func,$(word 1,$(1)),\
+ $(foreach lst,$(word 2,$(1)),\
+ $(foreach type,$(word 2,$(subst _, ,$(lst))),\
+ $(foreach new_hcode,$(call __new_obj_hash_code),\
+ $(foreach sz,$(words $(call __get_obj_values,$(lst))),\
+ $(eval $(__obj_magic)_$(type)_$(new_hcode)_value := $(strip \
+ $(foreach val,$(call __get_obj_values,$(lst)),\
+ $(call $(func)_value,$(val))))))\
+ $(__obj_magic)_$(type)_$(new_hcode))))))
+
+
+# Metadata functions
+
+with_meta = $(strip \
+ $(foreach new_obj,$(call _clone_obj,$(word 1,$(1))),\
+ $(eval $(new_obj)_meta := $(strip $(word 2,$(1))))\
+ $(new_obj)))
+
+meta = $(strip $($(1)_meta))
+
+
+# Atom functions
+
+atom = $(strip \
+ $(foreach hcode,$(call __new_obj_hash_code),\
+ $(foreach new_atom,$(__obj_magic)_atom_$(hcode),\
+ $(new_atom)\
+ $(eval $(new_atom)_value := $(1)))))
+atom? = $(if $(call _atom?,$(1)),$(__true),$(__false))
+
+deref = $($(1)_value)
+
+reset! = $(eval $(word 1,$(1))_value := $(word 2,$(1)))$(word 2,$(1))
+
+swap! = $(foreach resp,$(call $(word 2,$(1))_value,$($(word 1,$(1))_value) $(wordlist 3,$(words $(1)),$(1))),\
+ $(eval $(word 1,$(1))_value := $(resp))\
+ $(resp))
+
+
+
+
+# Namespace of core functions
+
+core_ns = type obj_type \
+ = equal? \
+ throw throw \
+ nil? nil? \
+ true? true? \
+ false? false? \
+ symbol _symbol \
+ symbol? symbol? \
+ function? function? \
+ string? string? \
+ pr-str pr_str \
+ str str \
+ prn prn \
+ println println \
+ subs subs \
+ number? number? \
+ < number_lt \
+ <= number_lte \
+ > number_gt \
+ >= number_gte \
+ + number_plus \
+ - number_subtract \
+ * number_multiply \
+ / number_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 sfirst \
+ rest srest \
+ last slast \
+ empty? empty? \
+ count count \
+ conj conj \
+ apply sapply \
+ map smap \
+ \
+ with-meta with_meta \
+ meta meta \
+ atom atom \
+ atom? atom? \
+ deref deref \
+ reset! reset! \
+ swap! swap!
+
+endif
diff --git a/make/env.mk b/make/env.mk
new file mode 100644
index 0000000..d508283
--- /dev/null
+++ b/make/env.mk
@@ -0,0 +1,50 @@
+#
+# mal (Make Lisp) Object Types and Functions
+#
+
+ifndef __mal_env_included
+__mal_env_included := true
+
+_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
+include $(_TOP_DIR)types.mk
+
+#
+# ENV
+#
+
+# An ENV environment is a hash-map with an __outer__ reference to an
+# outer environment
+define BIND_ARGS
+$(strip \
+ $(word 1,$(1) \
+ $(foreach fparam,$(call _nth,$(2),0),\
+ $(if $(call _EQ,&,$($(fparam)_value)),
+ $(call ENV_SET,$(1),$($(call _nth,$(2),1)_value),$(strip \
+ $(foreach new_list,$(call _list),
+ $(word 1,$(new_list) \
+ $(foreach val,$(3),$(call _conj!,$(new_list),$(val))))))),\
+ $(foreach val,$(word 1,$(3)),\
+ $(call ENV_SET,$(1),$($(fparam)_value),$(val))\
+ $(foreach left,$(call srest,$(2)),\
+ $(if $(call _EQ,0,$(call _count,$(left))),\
+ ,\
+ $(call BIND_ARGS,$(1),$(left),$(wordlist 2,$(words $(3)),$(3))))))))))
+endef
+
+# Create a new ENV and optional bind values in it
+# $(1): outer environment (set as a key named __outer__)
+# $(2): list/vector object of bind forms
+# $(3): space separated list of expressions to bind
+ENV = $(strip $(foreach new_env,$(call _assoc!,$(call _hash_map),__outer__,$(if $(1),$(1),$(__nil))),$(if $(2),$(call BIND_ARGS,$(new_env),$(2),$(3)),$(new_env))))
+ENV_FIND = $(strip \
+ $(if $(call _contains?,$(1),$(subst =,$(__equal),$(2))),\
+ $(1),\
+ $(if $(call _EQ,$(__nil),$(call _get,$(1),__outer__)),\
+ ,\
+ $(call ENV_FIND,$(call _get,$(1),__outer__),$(2)))))
+
+ENV_GET = $(foreach env,|$(call ENV_FIND,$(1),$(2))|,$(if $(call _EQ,||,$(env)),$(call _error,'$(2)' not found),$(call _get,$(strip $(subst |,,$(env))),$(subst =,$(__equal),$(2)))))
+
+ENV_SET = $(if $(call _assoc!,$(1),$(subst =,$(__equal),$(2)),$(3)),$(1),)
+
+endif
diff --git a/make/printer.mk b/make/printer.mk
new file mode 100644
index 0000000..a1f2559
--- /dev/null
+++ b/make/printer.mk
@@ -0,0 +1,45 @@
+#
+# mal (Make a Lisp) printer
+#
+
+ifndef __mal_printer_included
+__mal_printer_included := true
+
+_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
+include $(_TOP_DIR)util.mk
+include $(_TOP_DIR)types.mk
+
+# return a printable form of the argument, the second parameter is
+# 'print_readably' which backslashes quotes in string values
+_pr_str = $(if $(1),$(foreach ot,$(call _obj_type,$(1)),$(if $(call _EQ,make,$(ot)),$(call _error,_pr_str failed on $(1)),$(call $(ot)_pr_str,$(1),$(2)))),)
+
+# Like _pr_str but takes multiple values in first argument, the second
+# parameter is 'print_readably' which backslashes quotes in string
+# values, the third parameter is the delimeter to use between each
+# _pr_str'd value
+_pr_str_mult = $(call _pr_str,$(word 1,$(1)),$(2))$(if $(word 2,$(1)),$(3)$(call _pr_str_mult,$(wordlist 2,$(words $(1)),$(1)),$(2),$(3)),)
+
+
+# Type specific printing
+
+nil_pr_str = nil
+true_pr_str = true
+false_pr_str = false
+
+number_pr_str = $(call int_decode,$($(1)_value))
+
+symbol_pr_str = $($(1)_value)
+
+string_pr_str = $(if $(2),"$(subst $(DQUOTE),$(ESC_DQUOTE),$(subst $(SLASH),$(SLASH)$(SLASH),$(call str_decode,$($(1)_value))))",$(call str_decode,$($(1)_value)))
+
+function_pr_str = <$(if $(word 6,$(value $(1)_value)),$(wordlist 1,5,$(value $(1)_value))...,$(value $(1)_value))>
+
+list_pr_str = ($(foreach v,$(call __get_obj_values,$(1)),$(call _pr_str,$(v),$(2))))
+
+vector_pr_str = [$(foreach v,$(call __get_obj_values,$(1)),$(call _pr_str,$(v),$(2)))]
+
+hash_map_pr_str = {$(foreach v,$(call __get_obj_values,$(1)),"$(foreach hcode,$(word 3,$(subst _, ,$(1))),$(patsubst $(1)_%,%,$(v:%_value=%)))" $(call _pr_str,$($(v)),$(2)))}
+
+atom_pr_str = (atom $(call _pr_str,$($(1)_value),$(2)))
+
+endif
diff --git a/make/reader.mk b/make/reader.mk
index ce3b078..8d04596 100755
--- a/make/reader.mk
+++ b/make/reader.mk
@@ -55,10 +55,10 @@ endef
define READ_ATOM
$(foreach ch,$(word 1,$($(1))),\
$(if $(filter $(NUMBERS),$(ch)),\
- $(call number,$(call READ_NUMBER,$(1))),\
+ $(call _number,$(call READ_NUMBER,$(1))),\
$(if $(filter $(DQUOTE),$(ch)),\
$(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\
- $(call _string,$(strip $(call READ_STRING,$(1))))\
+ $(call __string,$(strip $(call READ_STRING,$(1))))\
$(eval $(if $(filter $(DQUOTE),$(word 1,$($(1)))),\
$(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1)))),\
$(call _error,Expected '$(DQUOTE)' in; $($(1))))),\
@@ -69,7 +69,7 @@ $(foreach ch,$(word 1,$($(1))),\
$(__true),\
$(if $(call _EQ,false,$(sym)),\
$(__false),\
- $(call symbol,$(sym)))))))))
+ $(call _symbol,$(sym)))))))))
endef
# read and return tokens until $(2) found
@@ -111,28 +111,28 @@ $(foreach ch,$(word 1,$($(1))),\
$(call DROP_UNTIL,$(1),$(_NL)),\
$(if $(filter $(SQUOTE),$(ch)),\
$(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\
- $(call list,$(call symbol,quote) $(strip $(call READ_FORM,$(1)))),\
+ $(call _list,$(call _symbol,quote) $(strip $(call READ_FORM,$(1)))),\
$(if $(filter $(QQUOTE),$(ch)),\
$(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\
- $(call list,$(call symbol,quasiquote) $(strip $(call READ_FORM,$(1)))),\
+ $(call _list,$(call _symbol,quasiquote) $(strip $(call READ_FORM,$(1)))),\
$(if $(filter $(UNQUOTE),$(ch)),\
$(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\
- $(call list,$(call symbol,unquote) $(strip $(call READ_FORM,$(1)))),\
+ $(call _list,$(call _symbol,unquote) $(strip $(call READ_FORM,$(1)))),\
$(if $(filter $(_SUQ),$(ch)),\
$(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\
- $(call list,$(call symbol,splice-unquote) $(strip $(call READ_FORM,$(1)))),\
+ $(call _list,$(call _symbol,splice-unquote) $(strip $(call READ_FORM,$(1)))),\
$(if $(filter $(CARET),$(ch)),\
$(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\
$(foreach meta,$(strip $(call READ_FORM,$(1))),\
- $(call list,$(call symbol,with-meta) $(strip $(call READ_FORM,$(1))) $(meta))),\
+ $(call _list,$(call _symbol,with-meta) $(strip $(call READ_FORM,$(1))) $(meta))),\
$(if $(filter $(ATSIGN),$(ch)),\
$(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\
- $(call list,$(call symbol,deref) $(strip $(call READ_FORM,$(1)))),\
+ $(call _list,$(call _symbol,deref) $(strip $(call READ_FORM,$(1)))),\
$(if $(filter $(_RC),$(ch)),\
$(call _error,Unexpected '$(RCURLY)'),\
$(if $(filter $(_LC),$(ch)),\
$(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\
- $(foreach thm,$(call hash_map),\
+ $(foreach thm,$(call _hash_map),\
$(call do,$(call _assoc_seq!,$(thm),$(strip $(call READ_UNTIL,$(1),$(_RC),$(RCURLY)))))\
$(eval $(if $(filter $(_RC),$(word 1,$($(1)))),\
$(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1)))),\
diff --git a/make/step1_read_print.mk b/make/step1_read_print.mk
index 710cd1d..a5fe2d2 100644
--- a/make/step1_read_print.mk
+++ b/make/step1_read_print.mk
@@ -4,6 +4,7 @@
_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
include $(_TOP_DIR)types.mk
include $(_TOP_DIR)reader.mk
+include $(_TOP_DIR)printer.mk
SHELL := /bin/bash
INTERACTIVE ?= yes
diff --git a/make/step2_eval.mk b/make/step2_eval.mk
index 62cd415..96cd98f 100644
--- a/make/step2_eval.mk
+++ b/make/step2_eval.mk
@@ -4,6 +4,8 @@
_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
include $(_TOP_DIR)types.mk
include $(_TOP_DIR)reader.mk
+include $(_TOP_DIR)printer.mk
+include $(_TOP_DIR)core.mk
SHELL := /bin/bash
INTERACTIVE ?= yes
diff --git a/make/step3_env.mk b/make/step3_env.mk
index 4f3f070..7c505cf 100644
--- a/make/step3_env.mk
+++ b/make/step3_env.mk
@@ -4,6 +4,9 @@
_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
include $(_TOP_DIR)types.mk
include $(_TOP_DIR)reader.mk
+include $(_TOP_DIR)printer.mk
+include $(_TOP_DIR)env.mk
+include $(_TOP_DIR)core.mk
SHELL := /bin/bash
INTERACTIVE ?= yes
diff --git a/make/step4_if_fn_do.mk b/make/step4_if_fn_do.mk
index d08998d..f45cfa9 100644
--- a/make/step4_if_fn_do.mk
+++ b/make/step4_if_fn_do.mk
@@ -4,6 +4,9 @@
_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
include $(_TOP_DIR)types.mk
include $(_TOP_DIR)reader.mk
+include $(_TOP_DIR)printer.mk
+include $(_TOP_DIR)env.mk
+include $(_TOP_DIR)core.mk
SHELL := /bin/bash
INTERACTIVE ?= yes
@@ -71,7 +74,7 @@ $(if $(__ERROR),,\
$(if $(call _EQ,fn*,$($(a0)_value)),\
$(foreach a1,$(call _nth,$(1),1),\
$(foreach a2,$(call _nth,$(1),2),\
- $(call function,$$(call EVAL,$(a2),$$(call ENV,$(2),$(a1),$$1))))),\
+ $(call _function,$$(call EVAL,$(a2),$$(call ENV,$(2),$(a1),$$1))))),\
$(foreach el,$(call EVAL_AST,$(1),$(2)),\
$(and $(EVAL_DEBUG),$(info invoke: $(call _pr_str,$(el))))\
$(foreach f,$(call sfirst,$(el)),\
@@ -99,11 +102,11 @@ REP = $(call PRINT,$(strip $(call EVAL,$(strip $(call READ,$(1))),$(REPL_ENV))))
REPL = $(info $(call REP,$(call READLINE,"user> ")))$(if $(READLINE_EOF),,$(call REPL))
# Setup the environment
-_fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call function,$$(call $(2),$$1))))
+_fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call _function,$$(call $(2),$$1))))
-# Import types functions
-_import_types = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_types,$(wordlist 3,$(words $(1)),$(1))),)
-$(call _import_types,$(types_ns))
+# Import core namespace
+_import_core = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_core,$(wordlist 3,$(words $(1)),$(1))),)
+$(call _import_core,$(core_ns))
# Defined in terms of the language itself
$(call do,$(call REP, (def! not (fn* (a) (if a false true))) ))
diff --git a/make/step6_file.mk b/make/step6_file.mk
index 0c72a9c..c791b33 100644
--- a/make/step6_file.mk
+++ b/make/step6_file.mk
@@ -4,6 +4,9 @@
_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
include $(_TOP_DIR)types.mk
include $(_TOP_DIR)reader.mk
+include $(_TOP_DIR)printer.mk
+include $(_TOP_DIR)env.mk
+include $(_TOP_DIR)core.mk
SHELL := /bin/bash
INTERACTIVE ?= yes
@@ -71,7 +74,7 @@ $(if $(__ERROR),,\
$(if $(call _EQ,fn*,$($(a0)_value)),\
$(foreach a1,$(call _nth,$(1),1),\
$(foreach a2,$(call _nth,$(1),2),\
- $(call function,$$(call EVAL,$(a2),$$(call ENV,$(2),$(a1),$$1))))),\
+ $(call _function,$$(call EVAL,$(a2),$$(call ENV,$(2),$(a1),$$1))))),\
$(foreach el,$(call EVAL_AST,$(1),$(2)),\
$(and $(EVAL_DEBUG),$(info invoke: $(call _pr_str,$(el))))\
$(foreach f,$(call sfirst,$(el)),\
@@ -100,17 +103,17 @@ REPL = $(info $(call REP,$(call READLINE,"user> ")))$(if $(READLINE_EOF),,$(call
# Setup the environment
_ref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(if $(2),$(2),$(1))))
-_fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call function,$$(call $(2),$$1))))
+_fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call _function,$$(call $(2),$$1))))
-# Import types functions
-_import_types = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_types,$(wordlist 3,$(words $(1)),$(1))),)
-$(call _import_types,$(types_ns))
+# Import core namespace
+_import_core = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_core,$(wordlist 3,$(words $(1)),$(1))),)
+$(call _import_core,$(core_ns))
-$(call _ref,read-string,$(call function,$$(call READ_STR,$$(1))))
-$(call _ref,eval,$(call function,$$(call EVAL,$$(1),$$(REPL_ENV))))
+$(call _ref,read-string,$(call _function,$$(call READ_STR,$$(1))))
+$(call _ref,eval,$(call _function,$$(call EVAL,$$(1),$$(REPL_ENV))))
-_slurp = $(call string,$(call _read_file,$(1)))
-$(call _ref,slurp,$(call function,$$(call _slurp,$$(call str_decode,$$($$(1)_value)))))
+_slurp = $(call _string,$(call _read_file,$(1)))
+$(call _ref,slurp,$(call _function,$$(call _slurp,$$(call str_decode,$$($$(1)_value)))))
# Defined in terms of the language itself
$(call do,$(call REP, (def! not (fn* (a) (if a false true))) ))
diff --git a/make/step7_quote.mk b/make/step7_quote.mk
index 2a14c11..46e5130 100644
--- a/make/step7_quote.mk
+++ b/make/step7_quote.mk
@@ -4,6 +4,9 @@
_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
include $(_TOP_DIR)types.mk
include $(_TOP_DIR)reader.mk
+include $(_TOP_DIR)printer.mk
+include $(_TOP_DIR)env.mk
+include $(_TOP_DIR)core.mk
SHELL := /bin/bash
INTERACTIVE ?= yes
@@ -20,12 +23,12 @@ IS_PAIR = $(if $(call _EQ,list,$(call _obj_type,$(1))),$(if $(call _EQ,0,$(call
define QUASIQUOTE
$(strip \
$(if $(call _NOT,$(call IS_PAIR,$(1))),\
- $(call list,$(call symbol,quote) $(1)),\
+ $(call _list,$(call _symbol,quote) $(1)),\
$(if $(call _EQ,unquote,$($(call _nth,$(1),0)_value)),\
$(call _nth,$(1),1),\
$(if $(and $(call IS_PAIR,$(call _nth,$(1),0)),$(call _EQ,splice-unquote,$($(call _nth,$(call _nth,$(1),0),0)_value))),\
- $(call list,$(call symbol,concat) $(call _nth,$(call _nth,$(1),0),1) $(call QUASIQUOTE,$(call srest,$(1)))),\
- $(call list,$(call symbol,cons) $(call QUASIQUOTE,$(call _nth,$(1),0)) $(call QUASIQUOTE,$(call srest,$(1))))))))
+ $(call _list,$(call _symbol,concat) $(call _nth,$(call _nth,$(1),0),1) $(call QUASIQUOTE,$(call srest,$(1)))),\
+ $(call _list,$(call _symbol,cons) $(call QUASIQUOTE,$(call _nth,$(1),0)) $(call QUASIQUOTE,$(call srest,$(1))))))))
endef
define LET
@@ -88,7 +91,7 @@ $(if $(__ERROR),,\
$(if $(call _EQ,fn*,$($(a0)_value)),\
$(foreach a1,$(call _nth,$(1),1),\
$(foreach a2,$(call _nth,$(1),2),\
- $(call function,$$(call EVAL,$(a2),$$(call ENV,$(2),$(a1),$$1))))),\
+ $(call _function,$$(call EVAL,$(a2),$$(call ENV,$(2),$(a1),$$1))))),\
$(foreach el,$(call EVAL_AST,$(1),$(2)),\
$(and $(EVAL_DEBUG),$(info invoke: $(call _pr_str,$(el))))\
$(foreach f,$(call sfirst,$(el)),\
@@ -117,17 +120,17 @@ REPL = $(info $(call REP,$(call READLINE,"user> ")))$(if $(READLINE_EOF),,$(call
# Setup the environment
_ref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(if $(2),$(2),$(1))))
-_fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call function,$$(call $(2),$$1))))
+_fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call _function,$$(call $(2),$$1))))
-# Import types functions
-_import_types = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_types,$(wordlist 3,$(words $(1)),$(1))),)
-$(call _import_types,$(types_ns))
+# Import core namespace
+_import_core = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_core,$(wordlist 3,$(words $(1)),$(1))),)
+$(call _import_core,$(core_ns))
-$(call _ref,read-string,$(call function,$$(call READ_STR,$$(1))))
-$(call _ref,eval,$(call function,$$(call EVAL,$$(1),$$(REPL_ENV))))
+$(call _ref,read-string,$(call _function,$$(call READ_STR,$$(1))))
+$(call _ref,eval,$(call _function,$$(call EVAL,$$(1),$$(REPL_ENV))))
-_slurp = $(call string,$(call _read_file,$(1)))
-$(call _ref,slurp,$(call function,$$(call _slurp,$$(call str_decode,$$($$(1)_value)))))
+_slurp = $(call _string,$(call _read_file,$(1)))
+$(call _ref,slurp,$(call _function,$$(call _slurp,$$(call str_decode,$$($$(1)_value)))))
# Defined in terms of the language itself
$(call do,$(call REP, (def! not (fn* (a) (if a false true))) ))
diff --git a/make/step8_macros.mk b/make/step8_macros.mk
index 910acb0..fee4bb0 100644
--- a/make/step8_macros.mk
+++ b/make/step8_macros.mk
@@ -4,6 +4,9 @@
_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
include $(_TOP_DIR)types.mk
include $(_TOP_DIR)reader.mk
+include $(_TOP_DIR)printer.mk
+include $(_TOP_DIR)env.mk
+include $(_TOP_DIR)core.mk
SHELL := /bin/bash
INTERACTIVE ?= yes
@@ -20,12 +23,12 @@ IS_PAIR = $(if $(call _EQ,list,$(call _obj_type,$(1))),$(if $(call _EQ,0,$(call
define QUASIQUOTE
$(strip \
$(if $(call _NOT,$(call IS_PAIR,$(1))),\
- $(call list,$(call symbol,quote) $(1)),\
+ $(call _list,$(call _symbol,quote) $(1)),\
$(if $(call _EQ,unquote,$($(call _nth,$(1),0)_value)),\
$(call _nth,$(1),1),\
$(if $(and $(call IS_PAIR,$(call _nth,$(1),0)),$(call _EQ,splice-unquote,$($(call _nth,$(call _nth,$(1),0),0)_value))),\
- $(call list,$(call symbol,concat) $(call _nth,$(call _nth,$(1),0),1) $(call QUASIQUOTE,$(call srest,$(1)))),\
- $(call list,$(call symbol,cons) $(call QUASIQUOTE,$(call _nth,$(1),0)) $(call QUASIQUOTE,$(call srest,$(1))))))))
+ $(call _list,$(call _symbol,concat) $(call _nth,$(call _nth,$(1),0),1) $(call QUASIQUOTE,$(call srest,$(1)))),\
+ $(call _list,$(call _symbol,cons) $(call QUASIQUOTE,$(call _nth,$(1),0)) $(call QUASIQUOTE,$(call srest,$(1))))))))
endef
define IS_MACRO_CALL
@@ -108,7 +111,7 @@ $(if $(__ERROR),,\
$(if $(call _EQ,fn*,$($(a0)_value)),\
$(foreach a1,$(call _nth,$(1),1),\
$(foreach a2,$(call _nth,$(1),2),\
- $(call function,$$(call EVAL,$(a2),$$(call ENV,$(2),$(a1),$$1))))),\
+ $(call _function,$$(call EVAL,$(a2),$$(call ENV,$(2),$(a1),$$1))))),\
$(foreach el,$(call EVAL_AST,$(1),$(2)),\
$(and $(EVAL_DEBUG),$(info invoke: $(call _pr_str,$(el))))\
$(foreach f,$(call sfirst,$(el)),\
@@ -140,17 +143,17 @@ REPL = $(info $(call REP,$(call READLINE,"user> ")))$(if $(READLINE_EOF),,$(call
# Setup the environment
_ref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(if $(2),$(2),$(1))))
-_fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call function,$$(call $(2),$$1))))
+_fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call _function,$$(call $(2),$$1))))
-# Import types functions
-_import_types = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_types,$(wordlist 3,$(words $(1)),$(1))),)
-$(call _import_types,$(types_ns))
+# Import core namespace
+_import_core = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_core,$(wordlist 3,$(words $(1)),$(1))),)
+$(call _import_core,$(core_ns))
-$(call _ref,read-string,$(call function,$$(call READ_STR,$$(1))))
-$(call _ref,eval,$(call function,$$(call EVAL,$$(1),$$(REPL_ENV))))
+$(call _ref,read-string,$(call _function,$$(call READ_STR,$$(1))))
+$(call _ref,eval,$(call _function,$$(call EVAL,$$(1),$$(REPL_ENV))))
-_slurp = $(call string,$(call _read_file,$(1)))
-$(call _ref,slurp,$(call function,$$(call _slurp,$$(call str_decode,$$($$(1)_value)))))
+_slurp = $(call _string,$(call _read_file,$(1)))
+$(call _ref,slurp,$(call _function,$$(call _slurp,$$(call str_decode,$$($$(1)_value)))))
# Defined in terms of the language itself
$(call do,$(call REP, (def! not (fn* (a) (if a false true))) ))
diff --git a/make/step9_interop.mk b/make/step9_interop.mk
index 6de228d..4b7eaff 100644
--- a/make/step9_interop.mk
+++ b/make/step9_interop.mk
@@ -4,6 +4,9 @@
_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
include $(_TOP_DIR)types.mk
include $(_TOP_DIR)reader.mk
+include $(_TOP_DIR)printer.mk
+include $(_TOP_DIR)env.mk
+include $(_TOP_DIR)core.mk
SHELL := /bin/bash
INTERACTIVE ?= yes
@@ -20,12 +23,12 @@ IS_PAIR = $(if $(call _EQ,list,$(call _obj_type,$(1))),$(if $(call _EQ,0,$(call
define QUASIQUOTE
$(strip \
$(if $(call _NOT,$(call IS_PAIR,$(1))),\
- $(call list,$(call symbol,quote) $(1)),\
+ $(call _list,$(call _symbol,quote) $(1)),\
$(if $(call _EQ,unquote,$($(call _nth,$(1),0)_value)),\
$(call _nth,$(1),1),\
$(if $(and $(call IS_PAIR,$(call _nth,$(1),0)),$(call _EQ,splice-unquote,$($(call _nth,$(call _nth,$(1),0),0)_value))),\
- $(call list,$(call symbol,concat) $(call _nth,$(call _nth,$(1),0),1) $(call QUASIQUOTE,$(call srest,$(1)))),\
- $(call list,$(call symbol,cons) $(call QUASIQUOTE,$(call _nth,$(1),0)) $(call QUASIQUOTE,$(call srest,$(1))))))))
+ $(call _list,$(call _symbol,concat) $(call _nth,$(call _nth,$(1),0),1) $(call QUASIQUOTE,$(call srest,$(1)))),\
+ $(call _list,$(call _symbol,cons) $(call QUASIQUOTE,$(call _nth,$(1),0)) $(call QUASIQUOTE,$(call srest,$(1))))))))
endef
define IS_MACRO_CALL
@@ -112,7 +115,7 @@ $(if $(__ERROR),,\
$(if $(call _EQ,fn*,$($(a0)_value)),\
$(foreach a1,$(call _nth,$(1),1),\
$(foreach a2,$(call _nth,$(1),2),\
- $(call function,$$(call EVAL,$(a2),$$(call ENV,$(2),$(a1),$$1))))),\
+ $(call _function,$$(call EVAL,$(a2),$$(call ENV,$(2),$(a1),$$1))))),\
$(foreach el,$(call EVAL_AST,$(1),$(2)),\
$(and $(EVAL_DEBUG),$(info invoke: $(call _pr_str,$(el))))\
$(foreach f,$(call sfirst,$(el)),\
@@ -144,17 +147,17 @@ REPL = $(info $(call REP,$(call READLINE,"user> ")))$(if $(READLINE_EOF),,$(call
# Setup the environment
_ref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(if $(2),$(2),$(1))))
-_fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call function,$$(call $(2),$$1))))
+_fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call _function,$$(call $(2),$$1))))
-# Import types functions
-_import_types = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_types,$(wordlist 3,$(words $(1)),$(1))),)
-$(call _import_types,$(types_ns))
+# Import core namespace
+_import_core = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_core,$(wordlist 3,$(words $(1)),$(1))),)
+$(call _import_core,$(core_ns))
-$(call _ref,read-string,$(call function,$$(call READ_STR,$$(1))))
-$(call _ref,eval,$(call function,$$(call EVAL,$$(1),$$(REPL_ENV))))
+$(call _ref,read-string,$(call _function,$$(call READ_STR,$$(1))))
+$(call _ref,eval,$(call _function,$$(call EVAL,$$(1),$$(REPL_ENV))))
-_slurp = $(call string,$(call _read_file,$(1)))
-$(call _ref,slurp,$(call function,$$(call _slurp,$$(call str_decode,$$($$(1)_value)))))
+_slurp = $(call _string,$(call _read_file,$(1)))
+$(call _ref,slurp,$(call _function,$$(call _slurp,$$(call str_decode,$$($$(1)_value)))))
# Defined in terms of the language itself
$(call do,$(call REP, (def! not (fn* (a) (if a false true))) ))
diff --git a/make/stepA_more.mk b/make/stepA_more.mk
index 48c0cb2..3cd0d2a 100644
--- a/make/stepA_more.mk
+++ b/make/stepA_more.mk
@@ -4,6 +4,9 @@
_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
include $(_TOP_DIR)types.mk
include $(_TOP_DIR)reader.mk
+include $(_TOP_DIR)printer.mk
+include $(_TOP_DIR)env.mk
+include $(_TOP_DIR)core.mk
SHELL := /bin/bash
INTERACTIVE ?= yes
@@ -20,12 +23,12 @@ IS_PAIR = $(if $(call _EQ,list,$(call _obj_type,$(1))),$(if $(call _EQ,0,$(call
define QUASIQUOTE
$(strip \
$(if $(call _NOT,$(call IS_PAIR,$(1))),\
- $(call list,$(call symbol,quote) $(1)),\
+ $(call _list,$(call _symbol,quote) $(1)),\
$(if $(call _EQ,unquote,$($(call _nth,$(1),0)_value)),\
$(call _nth,$(1),1),\
$(if $(and $(call IS_PAIR,$(call _nth,$(1),0)),$(call _EQ,splice-unquote,$($(call _nth,$(call _nth,$(1),0),0)_value))),\
- $(call list,$(call symbol,concat) $(call _nth,$(call _nth,$(1),0),1) $(call QUASIQUOTE,$(call srest,$(1)))),\
- $(call list,$(call symbol,cons) $(call QUASIQUOTE,$(call _nth,$(1),0)) $(call QUASIQUOTE,$(call srest,$(1))))))))
+ $(call _list,$(call _symbol,concat) $(call _nth,$(call _nth,$(1),0),1) $(call QUASIQUOTE,$(call srest,$(1)))),\
+ $(call _list,$(call _symbol,cons) $(call QUASIQUOTE,$(call _nth,$(1),0)) $(call QUASIQUOTE,$(call srest,$(1))))))))
endef
define IS_MACRO_CALL
@@ -109,7 +112,7 @@ $(if $(__ERROR),,\
$(if $(call _EQ,catch*,$($(a20)_value)),\
$(foreach a21,$(call _nth,$(a2),1),\
$(foreach a22,$(call _nth,$(a2),2),\
- $(foreach binds,$(call list,$(a21)),\
+ $(foreach binds,$(call _list,$(a21)),\
$(foreach catch_env,$(call ENV,$(2),$(binds),$(__ERROR)),\
$(eval __ERROR :=)\
$(call EVAL,$(a22),$(catch_env)))))),\
@@ -127,7 +130,7 @@ $(if $(__ERROR),,\
$(if $(call _EQ,fn*,$($(a0)_value)),\
$(foreach a1,$(call _nth,$(1),1),\
$(foreach a2,$(call _nth,$(1),2),\
- $(call function,$$(call EVAL,$(a2),$$(call ENV,$(2),$(a1),$$1))))),\
+ $(call _function,$$(call EVAL,$(a2),$$(call ENV,$(2),$(a1),$$1))))),\
$(foreach el,$(call EVAL_AST,$(1),$(2)),\
$(and $(EVAL_DEBUG),$(info invoke: $(call _pr_str,$(el))))\
$(foreach f,$(call sfirst,$(el)),\
@@ -159,18 +162,18 @@ REPL = $(info $(call REP,$(call READLINE,"user> ")))$(if $(READLINE_EOF),,$(call
# Setup the environment
_ref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(if $(2),$(2),$(1))))
-_fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call function,$$(call $(2),$$1))))
+_fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call _function,$$(call $(2),$$1))))
-# Import types functions
-_import_types = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_types,$(wordlist 3,$(words $(1)),$(1))),)
-$(call _import_types,$(types_ns))
+# Import core namespace
+_import_core = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_core,$(wordlist 3,$(words $(1)),$(1))),)
+$(call _import_core,$(core_ns))
-$(call _ref,readline,$(call function,$$(foreach res,$$(call string,$$(call READLINE,"$$(call str_decode,$$($$(1)_value))")),$$(if $$(READLINE_EOF),$$(__nil),$$(res)))))
-$(call _ref,read-string,$(call function,$$(call READ_STR,$$(1))))
-$(call _ref,eval,$(call function,$$(call EVAL,$$(1),$$(REPL_ENV))))
+$(call _ref,readline,$(call _function,$$(foreach res,$$(call _string,$$(call READLINE,"$$(call str_decode,$$($$(1)_value))")),$$(if $$(READLINE_EOF),$$(__nil),$$(res)))))
+$(call _ref,read-string,$(call _function,$$(call READ_STR,$$(1))))
+$(call _ref,eval,$(call _function,$$(call EVAL,$$(1),$$(REPL_ENV))))
-_slurp = $(call string,$(call _read_file,$(1)))
-$(call _ref,slurp,$(call function,$$(call _slurp,$$(call str_decode,$$($$(1)_value)))))
+_slurp = $(call _string,$(call _read_file,$(1)))
+$(call _ref,slurp,$(call _function,$$(call _slurp,$$(call str_decode,$$($$(1)_value)))))
# Defined in terms of the language itself
$(call do,$(call REP, (def! not (fn* (a) (if a false true))) ))
diff --git a/make/tests/types.mk b/make/tests/types.mk
index c1c1849..a100afe 100644
--- a/make/tests/types.mk
+++ b/make/tests/types.mk
@@ -63,9 +63,9 @@ $(call assert_eq,a string value - a string (with parens),$(call str_decode,$($(c
$(info Testing function objects)
-$(call assert_eq,function,$(call _obj_type,$(call function,abc)),\
+$(call assert_eq,function,$(call _obj_type,$(call _function,abc)),\
(type (function abc)) is not 'function')
-FN1 := $(call function,arg1:'$$(word 1,$$(1))' arg2:'$$(word 2,$$(1))')
+FN1 := $(call _function,arg1:'$$(word 1,$$(1))' arg2:'$$(word 2,$$(1))')
$(call assert_eq,$(__true),$(call function?,$(FN1)))
$(call assert_eq,arg1:'A' arg2:'B',$(call apply,$(FN1),$(call list,A B)))
@@ -113,7 +113,7 @@ $(call assert_eq,A,$(call sfirst,$(call _nth,$(L4),1)),\
$(info Testing hash_maps)
-X := $(call hash_map)
+X := $(call _hash_map)
$(call assert_eq,$(__true),$(call hash_map?,$(X)),\
(hash_map? $$(X)))
$(call assert_eq,$(__false),$(call vector?,$(X)),\
@@ -130,7 +130,7 @@ $(call assert_eq,value of X a,$(call _get,$(X),a),\
$(call assert_eq,$(__true),$(call contains?,$(X) $(mykey)),\
(contains? $$(X),a))
-Y := $(call hash_map)
+Y := $(call _hash_map)
$(call assert_eq,0,$(call _count,$(Y)),\
(_count $$(Y)))
$(call do,$(call _assoc!,$(Y),a,value of Y a))
@@ -241,7 +241,7 @@ $(info Testing smap function)
L5 := $(call _conj!,$(call list),$(call number,1) $(call number,2) $(call number,3))
inc = $(call number_plus,$(call number,1) $(1))
$(call assert_eq,(2 3 4),$(call _pr_str,$(call _smap,inc,$(L5))))
-inc_func := $(call function,$$(call number_plus,$$(call number,1) $$(1)))
+inc_func := $(call _function,$$(call number_plus,$$(call number,1) $$(1)))
$(call assert_eq,(2 3 4),$(call _pr_str,$(call smap,$(inc_func) $(L5))))
diff --git a/make/types.mk b/make/types.mk
index 9f03530..b971fb1 100644
--- a/make/types.mk
+++ b/make/types.mk
@@ -1,5 +1,5 @@
#
-# mal (Make Lisp) Object Types and Functions
+# mal (Make a Lisp) object types
#
ifndef __mal_types_included
@@ -8,6 +8,9 @@ __mal_types_included := true
_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
include $(_TOP_DIR)util.mk
+
+# Low-level type implemenation
+
# magic is \u2344 \u204a
__obj_magic = ⍄⁊
# \u2256
@@ -25,11 +28,54 @@ __get_obj_values = $(strip \
$(sort $(foreach v,$(filter %_value,$(filter $(1)_%,$(.VARIABLES))),$(if $(call _undefined?,$(v)),,$(v)))),\
$($(1)_value)))
+
+# Visualize Objects in memory
+__var_name = $(word 2,$(subst _, ,$(1)))_$(word 3,$(subst _, ,$(1)))
+__var_idx := 0
+__var_print = $(foreach v,$(1),\
+ $(foreach var,$(call __var_name,$(v)),\
+ $(if $(or $(call _list?,$(v)),$(call _vector?,$(v))),\
+ $(info $(2)$(var):)\
+ $(eval __var_idx := $(call gmsl_plus,1,$(__var_idx)))\
+ $(foreach lidx,__lidx_$(__var_idx),\
+ $(eval $(lidx) := 0)\
+ $(foreach val,$($(v)_value),\
+ $(call __var_print,$(val),$(2)$(SPACE)$(SPACE)$($(lidx)): )\
+ $(eval $(lidx) := $(call gmsl_plus,1,$($(lidx)))))),\
+ $(if $(call _hash_map?,$(v)),\
+ $(info $(2)$(var):)\
+ $(foreach vkey,$(filter $(v)_%,$(.VARIABLES)),\
+ $(foreach key,$(word 4,$(subst _, ,$(vkey))),\
+ $(info $(2)$(SPACE)$(SPACE)$(subst $(__equal),=,$(key)): )\
+ $(call __var_print,$($(vkey)),$(2)$(SPACE)$(SPACE)$(SPACE)$(SPACE)))),\
+ $(if $(call _symbol?,$(v)),\
+ $(info $(2)$(var): $($(v)_value)),\
+ $(if $(call _number?,$(v)),\
+ $(info $(2)$(var): $(call int_decode,$($(v)_value))),\
+ $(if $(call _nil?,$(v)),\
+ $(info $(2)nil),\
+ $(if $(call _function?,$(v)),\
+ $(if $(word 6,$(value $(v)_value)),\
+ $(info $(2)$(var): $(wordlist 1,5,$(value $(v)_value))...),\
+ $(info $(2)$(var): $(value $(v)_value))),\
+ $(info $(2)$(var): ...)))))))))
+
+_visualize_memory = $(foreach var,$(sort $(foreach vv,$(filter $(__obj_magic)_%,$(.VARIABLES)),$(call __var_name,$(vv)))),$(call __var_print,$(__obj_magic)_$(var)))
+
+
+# Errors/Exceptions
__ERROR :=
+_error = $(eval __ERROR := $(call _string,$(1)))
+
+
+# Constant atomic values
+__undefined = $(__obj_magic)_undf_0
+__nil = $(__obj_magic)__nil_0
+__true = $(__obj_magic)_true_0
+__false = $(__obj_magic)_fals_0
+
-#
# General functions
-#
# Return the type of the object (or "make" if it's not a object
_obj_type = $(strip \
@@ -46,22 +92,6 @@ _obj_type = $(strip \
$(if $(filter $(__obj_magic)_hmap_%,$(1)),hash_map,\
$(if $(filter $(__obj_magic)_undf_%,$(1)),undefined,\
make)))))))))))))
-obj_type = $(call string,$(call _obj_type,$(1)))
-
-# return a printable form of the argument, the second parameter is
-# 'print_readably' which backslashes quotes in string values
-_pr_str = $(if $(1),$(foreach ot,$(call _obj_type,$(1)),$(if $(call _EQ,make,$(ot)),$(call _error,_pr_str failed on $(1)),$(call $(ot)_pr_str,$(1),$(2)))),)
-
-# Like _pr_str but takes multiple values in first argument, the second
-# parameter is 'print_readably' which backslashes quotes in string
-# values, the third parameter is the delimeter to use between each
-# _pr_str'd value
-_pr_str_mult = $(call _pr_str,$(word 1,$(1)),$(2))$(if $(word 2,$(1)),$(3)$(call _pr_str_mult,$(wordlist 2,$(words $(1)),$(1)),$(2),$(3)),)
-
-pr_str = $(call string,$(call _pr_str_mult,$(1),yes, ))
-str = $(call string,$(call _pr_str_mult,$(1),,))
-prn = $(info $(call _pr_str_mult,$(1),yes, ))
-println = $(info $(subst \n,$(NEWLINE),$(call _pr_str_mult,$(1),, )))
_clone_obj = $(strip \
$(foreach new_hcode,$(call __new_obj_hash_code),\
@@ -75,101 +105,50 @@ _clone_obj = $(strip \
$(eval $(new_obj)_value := $(strip $($(1)_value)))))\
$(new_obj))))
-with_meta = $(strip \
- $(foreach new_obj,$(call _clone_obj,$(word 1,$(1))),\
- $(eval $(new_obj)_meta := $(strip $(word 2,$(1))))\
- $(new_obj)))
-
-meta = $(strip $($(1)_meta))
-
-
-#
-# Special atomic values
-#
-__undefined = $(__obj_magic)_undf_0
-__nil = $(__obj_magic)__nil_0
-__true = $(__obj_magic)_true_0
-__false = $(__obj_magic)_fals_0
+_equal? = $(strip \
+ $(foreach ot1,$(call _obj_type,$(1)),$(foreach ot2,$(call _obj_type,$(2)),\
+ $(if $(or $(call _EQ,$(ot1),$(ot2)),\
+ $(and $(call _sequential?,$(1)),$(call _sequential?,$(2)))),\
+ $(if $(or $(call _string?,$(1)),$(call _symbol?,$(1)),$(call _number?,$(1))),\
+ $(call _EQ,$($(1)_value),$($(2)_value)),\
+ $(if $(or $(call _vector?,$(1)),$(call _list?,$(1)),$(call _hash_map?,$(1))),\
+ $(if $(and $(call _EQ,$(call _count,$(1)),$(call _count,$(2))),\
+ $(call _EQ,$(call _count,$(1)),$(words $(call gmsl_pairmap,_equal?,$(call __get_obj_values,$(1)),$(call __get_obj_values,$(2)))))),$(__true),),\
+ $(call _EQ,$(1),$(2))))))))
_undefined? = $(or $(call _EQ,undefined,$(origin $(1))),$(filter $(__undefined),$($(1))))
-undefined? = $(if $(call _undefined?,$(1)),$(__true),$(__false))
_nil? = $(if $(filter $(__obj_magic)__nil_%,$(1)),$(__true),)
-nil? = $(if $(call _nil?,$(1)),$(__true),$(__false))
-nil_pr_str = nil
_true? = $(if $(filter $(__obj_magic)_true_%,$(1)),$(__true),)
-true? = $(if $(call _true?,$(1)),$(__true),$(__false))
-true_pr_str = true
_false? = $(if $(filter $(__obj_magic)_fals_%,$(1)),$(__true),)
-false? = $(if $(call _false?,$(1)),$(__true),$(__false))
-false_pr_str = false
-#
-# Numbers
-#
+# Symbols
+_symbol = $(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_symb_$(hcode)$(eval $(__obj_magic)_symb_$(hcode)_value := $(1)))
+_symbol? = $(if $(filter $(__obj_magic)_symb_%,$(1)),$(__true),)
-_pnumber = $(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_numb_$(hcode)$(eval $(__obj_magic)_numb_$(hcode)_value := $(1)))
-number = $(call _pnumber,$(call int_encode,$(1)))
+# Numbers
+_pnumber = $(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_numb_$(hcode)$(eval $(__obj_magic)_numb_$(hcode)_value := $(1)))
+_number = $(call _pnumber,$(call int_encode,$(1)))
_number? = $(if $(filter $(__obj_magic)_numb_%,$(1)),$(__true),)
-number? = $(if $(call _number?,$(1)),$(__true),$(__false))
-
-number_pr_str = $(call int_decode,$($(1)_value))
-
-number_plus = $(call _pnumber,$(call int_plus,$($(word 1,$(1))_value),$($(word 2,$(1))_value)))
-number_subtract = $(call _pnumber,$(call int_subtract,$($(word 1,$(1))_value),$($(word 2,$(1))_value)))
-number_multiply = $(call _pnumber,$(call int_multiply,$($(word 1,$(1))_value),$($(word 2,$(1))_value)))
-number_divide = $(call _pnumber,$(call int_divide,$($(word 1,$(1))_value),$($(word 2,$(1))_value)))
-number_gt = $(if $(call int_gt,$($(word 1,$(1))_value),$($(word 2,$(1))_value)),$(__true),$(__false))
-number_gte = $(if $(call int_gte,$($(word 1,$(1))_value),$($(word 2,$(1))_value)),$(__true),$(__false))
-number_lt = $(if $(call int_lt,$($(word 1,$(1))_value),$($(word 2,$(1))_value)),$(__true),$(__false))
-number_lte = $(if $(call int_lte,$($(word 1,$(1))_value),$($(word 2,$(1))_value)),$(__true),$(__false))
-
-#
-# Symbols
-#
-symbol = $(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_symb_$(hcode)$(eval $(__obj_magic)_symb_$(hcode)_value := $(1)))
-
-_symbol? = $(if $(filter $(__obj_magic)_symb_%,$(1)),$(__true),)
-symbol? = $(if $(call _symbol?,$(1)),$(__true),$(__false))
-
-symbol_pr_str = $($(1)_value)
-
-#
# Strings
-#
-_string = $(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_strn_$(hcode)$(eval $(__obj_magic)_strn_$(hcode)_value := $(1)))
-string = $(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_strn_$(hcode)$(eval $(__obj_magic)_strn_$(hcode)_value := $(call str_encode,$(1))))
-
+__string = $(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_strn_$(hcode)$(eval $(__obj_magic)_strn_$(hcode)_value := $(1)))
+_string = $(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_strn_$(hcode)$(eval $(__obj_magic)_strn_$(hcode)_value := $(call str_encode,$(1))))
_string? = $(if $(filter $(__obj_magic)_strn_%,$(1)),$(__true),)
-string? = $(if $(call _string?,$(1)),$(__true),$(__false))
-
-string_pr_str = $(if $(2),"$(subst $(DQUOTE),$(ESC_DQUOTE),$(subst $(SLASH),$(SLASH)$(SLASH),$(call str_decode,$($(1)_value))))",$(call str_decode,$($(1)_value)))
-
-subs = $(strip \
- $(foreach start,$(call gmsl_plus,1,$(call int_decode,$($(word 2,$(1))_value))),\
- $(foreach end,$(if $(3),$(call int_decode,$($(3)_value)),$(words $($(word 1,$(1))_value))),\
- $(call _string,$(wordlist $(start),$(end),$($(word 1,$(1))_value))))))
-#
-# Function objects
-#
+# Functions
# Return a function object. The first parameter is the
# function/macro 'source'. Note that any $ must be escaped as $$ to be
# preserved and become positional arguments for when the
# function/macro is later invoked.
-function = $(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_func_$(hcode)$(eval $(__obj_magic)_func_$(hcode)_value = $(1)))
-
+_function = $(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_func_$(hcode)$(eval $(__obj_magic)_func_$(hcode)_value = $(1)))
_function? = $(if $(filter $(__obj_magic)_func_%,$(1)),$(__true),)
-function? = $(if $(call _function?,$(1)),$(__true),$(__false))
-
-function_pr_str = <$(if $(word 6,$(value $(1)_value)),$(wordlist 1,5,$(value $(1)_value))...,$(value $(1)_value))>
# Takes a function name and a list object of arguments and invokes
# the function with space separated arguments
@@ -179,25 +158,20 @@ _apply = $(call $(1),$($(2)_value))
# the function with space separated arguments
apply = $(call $(1)_value,$($(2)_value))
-# Takes a space separated arguments and invokes the first argument
-# (function object) using the remaining arguments.
-sapply = $(call $(word 1,$(1))_value,\
- $(strip \
- $(wordlist 2,$(call gmsl_subtract,$(words $(1)),1),$(1)) \
- $($(word $(words $(1)),$(1))_value)))
-#
-# hash maps (associative arrays)
-#
+# Lists
+_list = $(word 1,$(foreach new_list,$(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_list_$(hcode)),$(new_list) $(eval $(new_list)_value := $1)))
+_list? = $(if $(filter $(__obj_magic)_list_%,$(1)),$(__true),)
-# create a new anonymous empty hash map
-_hash_map = $(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_hmap_$(hcode)$(eval $(__obj_magic)_hmap_$(hcode)_size := 0))
-hash_map = $(word 1,$(foreach new_hmap,$(call _hash_map),$(new_hmap) $(if $(1),$(call _assoc_seq!,$(new_hmap),$(1)))))
-_hash_map? = $(if $(filter $(__obj_magic)_hmap_%,$(1)),$(__true),)
-hash_map? = $(if $(call _hash_map?,$(1)),$(__true),$(__false))
+# Vectors (same as lists for now)
+_vector = $(word 1,$(foreach new_vect,$(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_vect_$(hcode)),$(new_vect) $(eval $(new_vect)_value := $1)))
+_vector? = $(if $(filter $(__obj_magic)_vect_%,$(1)),$(__true),)
+
-hash_map_pr_str = {$(foreach v,$(call __get_obj_values,$(1)),"$(foreach hcode,$(word 3,$(subst _, ,$(1))),$(patsubst $(1)_%,%,$(v:%_value=%)))" $(call _pr_str,$($(v)),$(2)))}
+# Hash maps (associative arrays)
+_hash_map = $(word 1,$(foreach hcode,$(call __new_obj_hash_code),$(foreach new_hmap,$(__obj_magic)_hmap_$(hcode),$(new_hmap) $(eval $(new_hmap)_size := 0) $(if $(1),$(call _assoc_seq!,$(new_hmap),$(1))))))
+_hash_map? = $(if $(filter $(__obj_magic)_hmap_%,$(1)),$(__true),)
# Set multiple key/values in a map
_assoc_seq! = $(call _assoc!,$(1),$(call str_decode,$($(word 1,$(2))_value)),$(word 2,$(2)))$(if $(word 3,$(2)),$(call _assoc_seq!,$(1),$(wordlist 3,$(words $(2)),$(2))),)
@@ -205,30 +179,9 @@ _assoc_seq! = $(call _assoc!,$(1),$(call str_decode,$($(word 1,$(2))_value)),$(w
# set a key/value in the hash map
_assoc! = $(foreach k,$(subst =,$(__equal),$(2)),$(if $(call _undefined?,$(1)_$(k)_value),$(eval $(1)_size := $(call gmsl_plus,$($(1)_size),1)),)$(eval $(1)_$(k)_value := $(3))$(1))
-# set a key/value in a copy of the hash map
-assoc = $(word 1,\
- $(foreach hm,$(call _clone_obj,$(word 1,$(1))),\
- $(hm) \
- $(call _assoc_seq!,$(hm),$(wordlist 2,$(words $(1)),$(1)))))
-
# unset a key in the hash map
_dissoc! = $(foreach k,$(subst =,$(__equal),$(2)),$(if $(call _undefined?,$(1)_$(k)_value),,$(eval $(1)_$(k)_value := $(__undefined))$(eval $(1)_size := $(call gmsl_subtract,$($(1)_size),1))))$(1)
-# unset keys in a copy of the hash map
-# TODO: this could be made more efficient by copying only the
-# keys that not being removed.
-dissoc = $(word 1,\
- $(foreach hm,$(call _clone_obj,$(word 1,$(1))),\
- $(hm) \
- $(foreach key,$(wordlist 2,$(words $(1)),$(1)),\
- $(call _dissoc!,$(hm),$(call str_decode,$($(key)_value))))))
-
-keys = $(foreach new_list,$(call _list),$(new_list)$(eval $(new_list)_value := $(foreach v,$(call __get_obj_values,$(1)),$(call string,$(word 4,$(subst _, ,$(v)))))))
-
-vals = $(foreach new_list,$(call _list),$(new_list)$(eval $(new_list)_value := $(foreach v,$(call __get_obj_values,$(1)),$($(v)))))
-
-
-
# Hash map and vector functions
# retrieve the value of a plain string key from the hash map, or
@@ -240,104 +193,20 @@ _get = $(strip \
$(word $(call gmsl_plus,1,$(2)),$($(1)_value)),\
,)))
-# retrieve the value of a string key object from the hash map, or
-# retrive a vector by number object index
-get = $(strip \
- $(if $(call _hash_map?,$(word 1,$(1))),\
- $(call _get,$(word 1,$(1)),$(call str_decode,$($(word 2,$(1))_value))),\
- $(call _get,$(word 1,$(1)),$(call number_pr_str,$(word 2,$(1))))))
-
_contains? = $(strip \
$(if $(call _hash_map?,$(1)),\
$(foreach k,$(subst =,$(__equal),$(2)),$(if $(call _undefined?,$(1)_$(k)_value),,$(__true))),\
$(if $(call _vector?,$(1)),\
$(if $(word $(call gmsl_plus,1,$(2)),$($(1)_value)),$(__true),),\
,)))
-contains? = $(if $(call _contains?,$(word 1,$(1)),$(call str_decode,$($(word 2,$(1))_value))),$(__true),$(__false))
-
-
-#
-# Errors/Exceptions
-#
-_error = $(eval __ERROR := $(call string,$(1)))
-throw = $(eval __ERROR := $(1))
-
-
-#
-# vectors
-#
-
-_vector = $(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_vect_$(hcode))
-vector = $(word 1,$(foreach new_vect,$(call _vector),$(new_vect) $(eval $(new_vect)_value := $1)))
-
-_vector? = $(if $(filter $(__obj_magic)_vect_%,$(1)),$(__true),)
-vector? = $(if $(call _vector?,$(1)),$(__true),$(__false))
-
-vector_pr_str = [$(foreach v,$(call __get_obj_values,$(1)),$(call _pr_str,$(v),$(2)))]
-
-#
-# list (same as vectors for now)
-#
-
-_list = $(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_list_$(hcode))
-list = $(word 1,$(foreach new_list,$(call _list),$(new_list) $(eval $(new_list)_value := $1)))
-
-_list? = $(if $(filter $(__obj_magic)_list_%,$(1)),$(__true),)
-list? = $(if $(call _list?,$(1)),$(__true),$(__false))
-
-list_pr_str = ($(foreach v,$(call __get_obj_values,$(1)),$(call _pr_str,$(v),$(2))))
-
-cons = $(word 1,$(foreach new_list,$(call _list),$(new_list) $(eval $(new_list)_value := $(strip $(word 1,$(1)) $(call __get_obj_values,$(word 2,$(1)))))))
-#
-# atoms
-#
-atom = $(strip \
- $(foreach hcode,$(call __new_obj_hash_code),\
- $(foreach new_atom,$(__obj_magic)_atom_$(hcode),\
- $(new_atom)\
- $(eval $(new_atom)_value := $(1)))))
-
-_atom? = $(if $(filter $(__obj_magic)_atom_%,$(1)),$(__true),)
-atom? = $(if $(call _atom?,$(1)),$(__true),$(__false))
-
-atom_pr_str = (atom $(call _pr_str,$($(1)_value),$(2)))
-
-deref = $($(1)_value)
-
-reset! = $(eval $(word 1,$(1))_value := $(word 2,$(1)))$(word 2,$(1))
-
-swap! = $(foreach resp,$(call $(word 2,$(1))_value,$($(word 1,$(1))_value) $(wordlist 3,$(words $(1)),$(1))),\
- $(eval $(word 1,$(1))_value := $(resp))\
- $(resp))
-
-
-#
# sequence operations
-#
_sequential? = $(if $(filter $(__obj_magic)_list_% $(__obj_magic)_vect_%,$(1)),$(__true),)
-sequential? = $(if $(call _sequential?,$(1)),$(__true),$(__false))
-
-raw_flat = $(foreach val,$(call __get_obj_values,$(1)),$($(val)))
_nth = $(word $(call gmsl_plus,1,$(2)),$($(1)_value))
-nth = $(word $(call gmsl_plus,1,$(call int_decode,$($(word 2,$(1))_value))),$($(word 1,$(1))_value))
-
-empty? = $(if $(call _EQ,0,$(if $(call _hash_map?,$(1)),$($(1)_size),$(words $($(1)_value)))),$(__true),$(__false))
-
-concat = $(word 1,$(foreach new_list,$(call _list),$(new_list) $(eval $(new_list)_value := $(strip $(foreach lst,$1,$(call __get_obj_values,$(lst)))))))
-
-conj = $(word 1,$(foreach new_list,$(call __new_obj_like,$(word 1,$(1))),\
- $(new_list) \
- $(eval $(new_list)_value := $(strip $($(word 1,$(1))_value))) \
- $(if $(call _list?,$(new_list)),\
- $(foreach elem,$(wordlist 2,$(words $(1)),$(1)),\
- $(eval $(new_list)_value := $(strip $(elem) $($(new_list)_value)))),\
- $(eval $(new_list)_value := $(strip $($(new_list)_value) $(wordlist 2,$(words $(1)),$(1)))))))
-
# conj that mutates a sequence in-place to append the call arguments
_conj! = $(eval $(1)_value := $(strip $($(1)_value) $2 $3 $4 $5 $6 $7 $8 $9 $(10) $(11) $(12) $(13) $(14) $(15) $(16) $(17) $(18) $(19) $(20)))$(1)
@@ -345,11 +214,6 @@ _count = $(strip \
$(if $(call _hash_map?,$(1)),\
$($(1)_size),\
$(words $($(1)_value))))
-count = $(call number,$(call _count,$(1)))
-
-sfirst = $(word 1,$($(1)_value))
-
-slast = $(word $(words $($(1)_value)),$($(1)_value))
# Creates a new vector/list of the everything after but the first
# element
@@ -366,135 +230,15 @@ _smap = $(word 1,\
# Same as _smap but returns a vector
_smap_vec = $(word 1,\
- $(foreach new_vector,$(call vector),\
+ $(foreach new_vector,$(call _vector),\
$(new_vector)\
$(foreach v,$(call __get_obj_values,$(2)),\
$(call _conj!,$(new_vector),$(call $(1),$(v),$(3),$(4))))))
-# Map a function object over a list object
-smap = $(strip\
- $(foreach func,$(word 1,$(1)),\
- $(foreach lst,$(word 2,$(1)),\
- $(foreach type,$(word 2,$(subst _, ,$(lst))),\
- $(foreach new_hcode,$(call __new_obj_hash_code),\
- $(foreach sz,$(words $(call __get_obj_values,$(lst))),\
- $(eval $(__obj_magic)_$(type)_$(new_hcode)_value := $(strip \
- $(foreach val,$(call __get_obj_values,$(lst)),\
- $(call $(func)_value,$(val))))))\
- $(__obj_magic)_$(type)_$(new_hcode))))))
-
-
-
-_equal? = $(strip \
- $(foreach ot1,$(call _obj_type,$(1)),$(foreach ot2,$(call _obj_type,$(2)),\
- $(if $(or $(call _EQ,$(ot1),$(ot2)),\
- $(and $(call _sequential?,$(1)),$(call _sequential?,$(2)))),\
- $(if $(or $(call _string?,$(1)),$(call _symbol?,$(1)),$(call _number?,$(1))),\
- $(call _EQ,$($(1)_value),$($(2)_value)),\
- $(if $(or $(call _vector?,$(1)),$(call _list?,$(1)),$(call _hash_map?,$(1))),\
- $(if $(and $(call _EQ,$(call _count,$(1)),$(call _count,$(2))),\
- $(call _EQ,$(call _count,$(1)),$(words $(call gmsl_pairmap,_equal?,$(call __get_obj_values,$(1)),$(call __get_obj_values,$(2)))))),$(__true),),\
- $(call _EQ,$(1),$(2))))))))
-
-equal? = $(if $(call _equal?,$(word 1,$(1)),$(word 2,$(1))),$(__true),$(__false))
-
-#
-# ENV
-#
-
-# An ENV environment is a hash-map with an __outer__ reference to an
-# outer environment
-define BIND_ARGS
-$(strip \
- $(word 1,$(1) \
- $(foreach fparam,$(call _nth,$(2),0),\
- $(if $(call _EQ,&,$($(fparam)_value)),
- $(call ENV_SET,$(1),$($(call _nth,$(2),1)_value),$(strip \
- $(foreach new_list,$(call _list),
- $(word 1,$(new_list) \
- $(foreach val,$(3),$(call _conj!,$(new_list),$(val))))))),\
- $(foreach val,$(word 1,$(3)),\
- $(call ENV_SET,$(1),$($(fparam)_value),$(val))\
- $(foreach left,$(call srest,$(2)),\
- $(if $(call _EQ,0,$(call _count,$(left))),\
- ,\
- $(call BIND_ARGS,$(1),$(left),$(wordlist 2,$(words $(3)),$(3))))))))))
-endef
-
-# Create a new ENV and optional bind values in it
-# $(1): outer environment (set as a key named __outer__)
-# $(2): list/vector object of bind forms
-# $(3): space separated list of expressions to bind
-ENV = $(strip $(foreach new_env,$(call _assoc!,$(call _hash_map),__outer__,$(if $(1),$(1),$(__nil))),$(if $(2),$(call BIND_ARGS,$(new_env),$(2),$(3)),$(new_env))))
-ENV_FIND = $(strip \
- $(if $(call _contains?,$(1),$(subst =,$(__equal),$(2))),\
- $(1),\
- $(if $(call _EQ,$(__nil),$(call _get,$(1),__outer__)),\
- ,\
- $(call ENV_FIND,$(call _get,$(1),__outer__),$(2)))))
-
-ENV_GET = $(foreach env,|$(call ENV_FIND,$(1),$(2))|,$(if $(call _EQ,||,$(env)),$(call _error,'$(2)' not found),$(call _get,$(strip $(subst |,,$(env))),$(subst =,$(__equal),$(2)))))
-
-ENV_SET = $(if $(call _assoc!,$(1),$(subst =,$(__equal),$(2)),$(3)),$(1),)
-
-#
-# Visualize Objects in memory
-#
-
-__var_name = $(word 2,$(subst _, ,$(1)))_$(word 3,$(subst _, ,$(1)))
-__var_idx := 0
-__var_print = $(foreach v,$(1),\
- $(foreach var,$(call __var_name,$(v)),\
- $(if $(or $(call _list?,$(v)),$(call _vector?,$(v))),\
- $(info $(2)$(var):)\
- $(eval __var_idx := $(call gmsl_plus,1,$(__var_idx)))\
- $(foreach lidx,__lidx_$(__var_idx),\
- $(eval $(lidx) := 0)\
- $(foreach val,$($(v)_value),\
- $(call __var_print,$(val),$(2)$(SPACE)$(SPACE)$($(lidx)): )\
- $(eval $(lidx) := $(call gmsl_plus,1,$($(lidx)))))),\
- $(if $(call _hash_map?,$(v)),\
- $(info $(2)$(var):)\
- $(foreach vkey,$(filter $(v)_%,$(.VARIABLES)),\
- $(foreach key,$(word 4,$(subst _, ,$(vkey))),\
- $(info $(2)$(SPACE)$(SPACE)$(subst $(__equal),=,$(key)): )\
- $(call __var_print,$($(vkey)),$(2)$(SPACE)$(SPACE)$(SPACE)$(SPACE)))),\
- $(if $(call _symbol?,$(v)),\
- $(info $(2)$(var): $($(v)_value)),\
- $(if $(call _number?,$(v)),\
- $(info $(2)$(var): $(call int_decode,$($(v)_value))),\
- $(if $(call _nil?,$(v)),\
- $(info $(2)nil),\
- $(if $(call _function?,$(v)),\
- $(if $(word 6,$(value $(v)_value)),\
- $(info $(2)$(var): $(wordlist 1,5,$(value $(v)_value))...),\
- $(info $(2)$(var): $(value $(v)_value))),\
- $(info $(2)$(var): ...)))))))))
+# atoms
-visualize_memory = $(foreach var,$(sort $(foreach vv,$(filter $(__obj_magic)_%,$(.VARIABLES)),$(call __var_name,$(vv)))),$(call __var_print,$(__obj_magic)_$(var)))
+_atom? = $(if $(filter $(__obj_magic)_atom_%,$(1)),$(__true),)
-#
-# Namespace of type functions
-#
-types_ns = pr-str pr_str str str prn prn println println \
- with-meta with_meta meta meta \
- type obj_type = equal? \
- nil? nil? true? true? false? false? \
- number? number? \
- > number_gt >= number_gte < number_lt <= number_lte \
- + number_plus - number_subtract * number_multiply / number_divide \
- symbol? symbol? function? function? \
- string? string? subs subs \
- 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! swap! swap! \
- sequential? sequential? \
- cons cons nth nth empty? empty? count count concat concat \
- conj conj first sfirst last slast rest srest \
- apply sapply map smap \
endif
diff --git a/mal/Makefile b/mal/Makefile
index f90f574..799b691 100644
--- a/mal/Makefile
+++ b/mal/Makefile
@@ -2,7 +2,7 @@
TESTS =
-SOURCES = types.mal env.mal stepA_more.mal
+SOURCES = env.mal core.mal stepA_more.mal
#.PHONY: stats tests $(TESTS)
.PHONY: stats
diff --git a/mal/core.mal b/mal/core.mal
new file mode 100644
index 0000000..b616603
--- /dev/null
+++ b/mal/core.mal
@@ -0,0 +1,52 @@
+(def! core_ns
+ [["=" =]
+ ["throw" throw]
+ ["nil?" nil?]
+ ["true?" true?]
+ ["false?" false?]
+ ["symbol?" symbol?]
+ ["pr-str" pr-str]
+ ["str" str]
+ ["prn" prn]
+ ["println" println]
+ ["<" <]
+ ["<=" <=]
+ [">" >]
+ [">=" >=]
+ ["+" +]
+ ["-" -]
+ ["*" *]
+ ["/" /]
+
+ ["list" list]
+ ["list?" list?]
+ ["vector" vector]
+ ["vector?" vector?]
+ ["hash-map" hash-map]
+ ["map?" 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!]
+ ["swap!" swap!]])
diff --git a/mal/step4_if_fn_do.mal b/mal/step4_if_fn_do.mal
index 76a3c3a..7107ac4 100644
--- a/mal/step4_if_fn_do.mal
+++ b/mal/step4_if_fn_do.mal
@@ -1,5 +1,5 @@
-(load-file "../mal/types.mal")
(load-file "../mal/env.mal")
+(load-file "../mal/core.mal")
;; read
(def! READ (fn* [strng]
@@ -79,8 +79,8 @@
(def! _ref (fn* [k v] (env-set repl-env k v)))
-;; Import types related functions
-(map (fn* [data] (_ref (nth data 0) (nth data 1))) types_ns)
+;; Import core namespace functions
+(map (fn* [data] (_ref (nth data 0) (nth data 1))) core_ns)
;; Defined using the language itself
(rep "(def! not (fn* [a] (if a false true)))")
diff --git a/mal/step6_file.mal b/mal/step6_file.mal
index b5db13b..2f53c4d 100644
--- a/mal/step6_file.mal
+++ b/mal/step6_file.mal
@@ -1,5 +1,5 @@
-(load-file "../mal/types.mal")
(load-file "../mal/env.mal")
+(load-file "../mal/core.mal")
;; read
(def! READ (fn* [strng]
@@ -79,8 +79,8 @@
(def! _ref (fn* [k v] (env-set repl-env k v)))
-;; Import types related functions
-(map (fn* [data] (_ref (nth data 0) (nth data 1))) types_ns)
+;; Import core namespace functions
+(map (fn* [data] (_ref (nth data 0) (nth data 1))) core_ns)
;; Defined using the language itself
(_ref 'read-string read-string)
diff --git a/mal/step7_quote.mal b/mal/step7_quote.mal
index 1ea989c..553248c 100644
--- a/mal/step7_quote.mal
+++ b/mal/step7_quote.mal
@@ -1,5 +1,5 @@
-(load-file "../mal/types.mal")
(load-file "../mal/env.mal")
+(load-file "../mal/core.mal")
;; read
(def! READ (fn* [strng]
@@ -107,8 +107,8 @@
(def! _ref (fn* [k v] (env-set repl-env k v)))
-;; Import types related functions
-(map (fn* [data] (_ref (nth data 0) (nth data 1))) types_ns)
+;; Import core namespace functions
+(map (fn* [data] (_ref (nth data 0) (nth data 1))) core_ns)
;; Defined using the language itself
(_ref 'read-string read-string)
diff --git a/mal/step8_macros.mal b/mal/step8_macros.mal
index c07c9b7..5c81138 100644
--- a/mal/step8_macros.mal
+++ b/mal/step8_macros.mal
@@ -1,5 +1,5 @@
-(load-file "../mal/types.mal")
(load-file "../mal/env.mal")
+(load-file "../mal/core.mal")
;; read
(def! READ (fn* [strng]
@@ -139,8 +139,8 @@
(def! _ref (fn* [k v] (env-set repl-env k v)))
-;; Import types related functions
-(map (fn* [data] (_ref (nth data 0) (nth data 1))) types_ns)
+;; Import core namespace functions
+(map (fn* [data] (_ref (nth data 0) (nth data 1))) core_ns)
;; Defined using the language itself
(_ref 'read-string read-string)
diff --git a/mal/stepA_more.mal b/mal/stepA_more.mal
index 013c1bc..cd1e570 100644
--- a/mal/stepA_more.mal
+++ b/mal/stepA_more.mal
@@ -1,5 +1,5 @@
-(load-file "../mal/types.mal")
(load-file "../mal/env.mal")
+(load-file "../mal/core.mal")
;; read
(def! READ (fn* [strng]
@@ -150,8 +150,8 @@
(def! _ref (fn* [k v] (env-set repl-env k v)))
-;; Import types related functions
-(map (fn* [data] (_ref (nth data 0) (nth data 1))) types_ns)
+;; Import core namespace functions
+(map (fn* [data] (_ref (nth data 0) (nth data 1))) core_ns)
;; Defined using the language itself
(_ref 'readline readline)
diff --git a/mal/types.mal b/mal/types.mal
deleted file mode 100644
index 6eaa388..0000000
--- a/mal/types.mal
+++ /dev/null
@@ -1,16 +0,0 @@
-(def! types_ns
- [["pr-str" pr-str] ["str" str] ["prn" prn] ["println" println]
- ["with-meta" with-meta] ["meta" meta] ["=" =]
- ["nil?" nil?] ["true?" true?] ["false?" false?] ["symbol?" symbol?]
- [">" >] [">=" >=] ["<" <] ["<=" <=] ["+" +] ["-" -] ["*" *] ["/" /]
- ["hash-map" hash-map] ["map?" 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!] ["swap!" swap!]
- ["sequential?" sequential?] ["cons" cons] ["nth" nth]
- ["empty?" empty?] ["count" count] ["concat" concat]
- ["conj" conj] ["first" first] ["rest" rest]
- ["apply" apply] ["map" map]])
diff --git a/php/Makefile b/php/Makefile
new file mode 100644
index 0000000..e7ea51d
--- /dev/null
+++ b/php/Makefile
@@ -0,0 +1,16 @@
+
+TESTS =
+
+SOURCES = readline.php types.php reader.php printer.php \
+ env.php core.php stepA_more.php
+
+.PHONY: stats tests $(TESTS)
+
+stats: $(SOURCES)
+ @wc $^
+
+tests: $(TESTS)
+
+$(TESTS):
+ @echo "Running $@"; \
+ php $@ || exit 1; \
diff --git a/php/core.php b/php/core.php
new file mode 100644
index 0000000..16d34f8
--- /dev/null
+++ b/php/core.php
@@ -0,0 +1,221 @@
+<?php
+
+require_once 'types.php';
+require_once 'printer.php';
+
+// Error/Exception functions
+function mal_throw($obj) { throw new Error($obj); }
+
+
+// String functions
+function pr_str() {
+ $ps = array_map(function ($obj) { return _pr_str($obj, True); },
+ func_get_args());
+ return implode(" ", $ps);
+}
+
+function str() {
+ $ps = array_map(function ($obj) { return _pr_str($obj, False); },
+ func_get_args());
+ return implode("", $ps);
+}
+
+function prn() {
+ $ps = array_map(function ($obj) { return _pr_str($obj, True); },
+ func_get_args());
+ print implode(" ", $ps) . "\n";
+ return null;
+}
+
+function println() {
+ $ps = array_map(function ($obj) { return _pr_str($obj, False); },
+ func_get_args());
+ print implode(" ", $ps) . "\n";
+ return null;
+}
+
+
+// Hash Map functions
+function assoc($src_hm) {
+ $args = func_get_args();
+ $hm = clone $src_hm;
+ $args[0] = $hm;
+ return call_user_func_array('_assoc_BANG', $args);
+}
+
+function dissoc($src_hm) {
+ $args = func_get_args();
+ $hm = clone $src_hm;
+ $args[0] = $hm;
+ return call_user_func_array('_dissoc_BANG', $args);
+}
+
+function get($hm, $k) {
+ if ($hm && $hm->offsetExists($k)) {
+ return $hm[$k];
+ } else {
+ return NULL;
+ }
+}
+
+function contains_Q($hm, $k) { return array_key_exists($k, $hm); }
+
+function keys($hm) {
+ return call_user_func_array('_list', array_keys($hm->getArrayCopy()));
+}
+function vals($hm) {
+ return call_user_func_array('_list', array_values($hm->getArrayCopy()));
+}
+
+
+// Sequence functions
+function cons($a, $b) {
+ $tmp = $b->getArrayCopy();
+ array_unshift($tmp, $a);
+ $l = new ListClass();
+ $l->exchangeArray($tmp);
+ return $l;
+}
+
+function concat() {
+ $args = func_get_args();
+ $tmp = array();
+ foreach ($args as $arg) {
+ $tmp = array_merge($tmp, $arg->getArrayCopy());
+ }
+ $l = new ListClass();
+ $l->exchangeArray($tmp);
+ return $l;
+}
+
+function nth($seq, $idx) {
+ return $seq[$idx];
+}
+
+function first($seq) {
+ if (count($seq) === 0) {
+ return NULL;
+ } else {
+ return $seq[0];
+ }
+}
+
+function rest($seq) {
+ $l = new ListClass();
+ $l->exchangeArray(array_slice($seq->getArrayCopy(), 1));
+ return $l;
+}
+
+function empty_Q($seq) { return $seq->count() === 0; }
+
+function scount($seq) { return ($seq === NULL ? 0 : $seq->count()); }
+
+function conj($src) {
+ $args = array_slice(func_get_args(), 1);
+ $tmp = $src->getArrayCopy();
+ if (_list_Q($src)) {
+ foreach ($args as $arg) { array_unshift($tmp, $arg); }
+ $s = new ListClass();
+ } else {
+ foreach ($args as $arg) { $tmp[] = $arg; }
+ $s = new VectorClass();
+ }
+ $s->exchangeArray($tmp);
+ return $s;
+}
+
+function apply($f) {
+ $args = array_slice(func_get_args(), 1);
+ $last_arg = array_pop($args)->getArrayCopy();
+ return $f->apply(array_merge($args, $last_arg));
+}
+
+function map($f, $seq) {
+ $l = new ListClass();
+ $l->exchangeArray(array_map($f, $seq->getArrayCopy()));
+ return $l;
+}
+
+
+// Metadata functions
+function with_meta($obj, $m) {
+ $new_obj = clone $obj;
+ $new_obj->meta = $m;
+ return $new_obj;
+}
+
+function meta($obj) {
+ return $obj->meta;
+}
+
+
+// Atom functions
+function deref($atm) { return $atm->value; }
+function reset_BANG($atm, $val) { return $atm->value = $val; }
+function swap_BANG($atm, $f) {
+ $args = array_slice(func_get_args(),2);
+ array_unshift($args, $atm->value);
+ $atm->value = call_user_func_array($f, $args);
+ return $atm->value;
+}
+
+
+// core_ns is namespace of type functions
+$core_ns = array(
+ '='=> function ($a, $b) { return _equal_Q($a, $b); },
+ 'throw'=> function ($a) { return mal_throw($a); },
+ 'nil?'=> function ($a) { return _nil_Q($a); },
+ 'true?'=> function ($a) { return _true_Q($a); },
+ 'false?'=> function ($a) { return _false_Q($a); },
+ 'symbol'=> function () { return call_user_func_array('_symbol', func_get_args()); },
+ 'symbol?'=> function ($a) { return _symbol_Q($a); },
+ 'string?'=> function ($a) { return _string_Q($a); },
+ 'pr-str'=> function () { return call_user_func_array('pr_str', func_get_args()); },
+ 'str'=> function () { return call_user_func_array('str', func_get_args()); },
+ 'prn'=> function () { return call_user_func_array('prn', func_get_args()); },
+ 'println'=>function () { return call_user_func_array('println', func_get_args()); },
+ '<'=> function ($a, $b) { return $a < $b; },
+ '<='=> function ($a, $b) { return $a <= $b; },
+ '>'=> function ($a, $b) { return $a > $b; },
+ '>='=> function ($a, $b) { return $a >= $b; },
+ '+'=> function ($a, $b) { return intval($a + $b,10); },
+ '-'=> function ($a, $b) { return intval($a - $b,10); },
+ '*'=> function ($a, $b) { return intval($a * $b,10); },
+ '/'=> function ($a, $b) { return intval($a / $b,10); },
+
+ 'list'=> function () { return call_user_func_array('_list', func_get_args()); },
+ 'list?'=> function ($a) { return _list_Q($a); },
+ 'vector'=> function () { return call_user_func_array('_vector', func_get_args()); },
+ 'vector?'=> function ($a) { return _vector_Q($a); },
+ 'hash-map' => function () { return call_user_func_array('_hash_map', func_get_args()); },
+ 'map?'=> function ($a) { return _hash_map_Q($a); },
+ 'assoc' => function () { return call_user_func_array('assoc', func_get_args()); },
+ 'dissoc' => function () { return call_user_func_array('dissoc', func_get_args()); },
+ 'get' => function ($a, $b) { return get($a, $b); },
+ 'contains?' => function ($a, $b) { return contains_Q($a, $b); },
+ 'keys' => function ($a) { return keys($a); },
+ 'vals' => function ($a) { return vals($a); },
+
+ 'sequential?'=> function ($a) { return _sequential_Q($a); },
+ 'cons'=> function ($a, $b) { return cons($a, $b); },
+ 'concat'=> function () { return call_user_func_array('concat', func_get_args()); },
+ 'nth'=> function ($a, $b) { return nth($a, $b); },
+ 'first'=> function ($a) { return first($a); },
+ 'rest'=> function ($a) { return rest($a); },
+ 'empty?'=> function ($a) { return empty_Q($a); },
+ 'count'=> function ($a) { return scount($a); },
+ 'conj'=> function () { return call_user_func_array('conj', func_get_args()); },
+ 'apply'=> function () { return call_user_func_array('apply', func_get_args()); },
+ 'map'=> function ($a, $b) { return map($a, $b); },
+
+ 'with-meta'=> function ($a, $b) { return with_meta($a, $b); },
+ 'meta'=> function ($a) { return meta($a); },
+ 'atom'=> function ($a) { return _atom($a); },
+ 'atom?'=> function ($a) { return _atom_Q($a); },
+ 'deref'=> function ($a) { return deref($a); },
+ 'reset!'=> function ($a, $b) { return reset_BANG($a, $b); },
+ 'swap!'=> function () { return call_user_func_array('swap_BANG', func_get_args()); },
+);
+
+
+?>
diff --git a/php/env.php b/php/env.php
new file mode 100644
index 0000000..61bedaf
--- /dev/null
+++ b/php/env.php
@@ -0,0 +1,56 @@
+<?php
+
+require_once 'types.php';
+
+class Env {
+ public $data = array();
+ public $outer = NULL;
+ public function __construct($outer, $binds=NULL, $exprs=NULL) {
+ $this->outer = $outer;
+ if ($binds) {
+ if (_sequential_Q($exprs)) {
+ $exprs = $exprs->getArrayCopy();
+ }
+ for ($i=0; $i<count($binds); $i++) {
+ if ($binds[$i]->value === "&") {
+ if ($exprs !== NULL && $i < count($exprs)) {
+ $lst = call_user_func_array('_list', array_slice($exprs, $i));
+ } else {
+ $lst = _list();
+ }
+ $this->data[$binds[$i+1]->value] = $lst;
+ break;
+ } else {
+ if ($exprs !== NULL && $i < count($exprs)) {
+ $this->data[$binds[$i]->value] = $exprs[$i];
+ } else {
+ $this->data[$binds[$i]->value] = NULL;
+ }
+ }
+ }
+ }
+ }
+ public function find($key) {
+ if (array_key_exists($key, $this->data)) {
+ return $this;
+ } elseif ($this->outer) {
+ return $this->outer->find($key);
+ } else {
+ return NULL;
+ }
+ }
+ public function set($key, $value) {
+ $this->data[$key] = $value;
+ return $value;
+ }
+ public function get($key) {
+ $env = $this->find($key);
+ if (!$env) {
+ throw new Exception("'" . $key . "' not found");
+ } else {
+ return $env->data[$key];
+ }
+ }
+}
+
+?>
diff --git a/php/printer.php b/php/printer.php
new file mode 100644
index 0000000..3839931
--- /dev/null
+++ b/php/printer.php
@@ -0,0 +1,53 @@
+<?php
+
+require_once 'types.php';
+
+function _pr_str($obj, $print_readably=True) {
+ if (_list_Q($obj)) {
+ $ret = array();
+ foreach ($obj as $e) {
+ array_push($ret, _pr_str($e, $print_readably));
+ }
+ return "(" . implode(" ", $ret) . ")";
+ } elseif (_vector_Q($obj)) {
+ $ret = array();
+ foreach ($obj as $e) {
+ array_push($ret, _pr_str($e, $print_readably));
+ }
+ return "[" . implode(" ", $ret) . "]";
+ } elseif (_hash_map_Q($obj)) {
+ $ret = array();
+ foreach (array_keys($obj->getArrayCopy()) as $k) {
+ $ret[] = _pr_str($k, $print_readably);
+ $ret[] = _pr_str($obj[$k], $print_readably);
+ }
+ return "{" . implode(" ", $ret) . "}";
+ } elseif (is_string($obj)) {
+ if ($print_readably) {
+ $obj = preg_replace('/"/', '\\"', preg_replace('/\\\\/', '\\\\\\\\', $obj));
+ return '"' . $obj . '"';
+ } else {
+ return $obj;
+ }
+ } elseif (is_integer($obj)) {
+ return $obj;
+ } elseif ($obj === NULL) {
+ return "nil";
+ } elseif ($obj === true) {
+ return "true";
+ } elseif ($obj === false) {
+ return "false";
+ } elseif (_symbol_Q($obj)) {
+ return $obj->value;
+ } elseif (_atom_Q($obj)) {
+ return "(atom " . _pr_str($obj->value, $print_readably) . ")";
+ } elseif (_function_Q($obj)) {
+ return "(fn* [...] ...)";
+ } elseif (is_callable($obj)) { // only step4 and below
+ return "#<function ...>";
+ } else {
+ throw new Exception("_pr_str unknown type: " . gettype($obj));
+ }
+}
+
+?>
diff --git a/php/reader.php b/php/reader.php
index 0524b31..83e0cff 100644
--- a/php/reader.php
+++ b/php/reader.php
@@ -10,9 +10,11 @@ class Reader {
$this->position = 0;
}
public function next() {
+ if ($this->position >= count($this->tokens)) { return null; }
return $this->tokens[$this->position++];
}
public function peek() {
+ if ($this->position >= count($this->tokens)) { return null; }
return $this->tokens[$this->position];
}
}
@@ -45,18 +47,18 @@ function read_atom($reader) {
} elseif ($token === "false") {
return false;
} else {
- return new_symbol($token);
+ return _symbol($token);
}
}
-function read_list($reader, $constr='new_list', $start='(', $end=')') {
+function read_list($reader, $constr='_list', $start='(', $end=')') {
$ast = $constr();
$token = $reader->next();
if ($token !== $start) {
throw new Exception("expected '" . $start . "'");
}
while (($token = $reader->peek()) !== $end) {
- if ($token === "") {
+ if ($token === "" || $token === null) {
throw new Exception("expected '" . $end . "', got EOF");
}
$ast[] = read_form($reader);
@@ -66,39 +68,39 @@ function read_list($reader, $constr='new_list', $start='(', $end=')') {
}
function read_hash_map($reader) {
- $lst = read_list($reader, 'new_list', '{', '}');
- return call_user_func_array('new_hash_map', $lst->getArrayCopy());
+ $lst = read_list($reader, '_list', '{', '}');
+ return call_user_func_array('_hash_map', $lst->getArrayCopy());
}
function read_form($reader) {
$token = $reader->peek();
switch ($token) {
case '\'': $reader->next();
- return new_list(new_symbol('quote'),
+ return _list(_symbol('quote'),
read_form($reader));
case '`': $reader->next();
- return new_list(new_symbol('quasiquote'),
+ return _list(_symbol('quasiquote'),
read_form($reader));
case '~': $reader->next();
- return new_list(new_symbol('unquote'),
+ return _list(_symbol('unquote'),
read_form($reader));
case '~@': $reader->next();
- return new_list(new_symbol('splice-unquote'),
+ return _list(_symbol('splice-unquote'),
read_form($reader));
case '^': $reader->next();
$meta = read_form($reader);
- return new_list(new_symbol('with-meta'),
+ return _list(_symbol('with-meta'),
read_form($reader),
$meta);
case '@': $reader->next();
- return new_list(new_symbol('deref'),
+ return _list(_symbol('deref'),
read_form($reader));
case ')': throw new Exception("unexpected ')'");
case '(': return read_list($reader);
case ']': throw new Exception("unexpected ']'");
- case '[': return read_list($reader, 'new_vector', '[', ']');
+ case '[': return read_list($reader, '_vector', '[', ']');
case '}': throw new Exception("unexpected '}'");
case '{': return read_hash_map($reader);
diff --git a/php/step1_read_print.php b/php/step1_read_print.php
index 01334e0..808ea09 100644
--- a/php/step1_read_print.php
+++ b/php/step1_read_print.php
@@ -3,6 +3,7 @@
require_once 'readline.php';
require_once 'types.php';
require_once 'reader.php';
+require_once 'printer.php';
// read
function READ($str) {
diff --git a/php/step2_eval.php b/php/step2_eval.php
index c9c3562..0ef184a 100644
--- a/php/step2_eval.php
+++ b/php/step2_eval.php
@@ -3,6 +3,7 @@
require_once 'readline.php';
require_once 'types.php';
require_once 'reader.php';
+require_once 'printer.php';
// read
function READ($str) {
@@ -11,18 +12,18 @@ function READ($str) {
// eval
function eval_ast($ast, $env) {
- if (symbol_Q($ast)) {
+ if (_symbol_Q($ast)) {
return $env[$ast->value];
- } elseif (list_Q($ast) || vector_Q($ast)) {
- if (list_Q($ast)) {
- $el = new_list();
+ } elseif (_sequential_Q($ast)) {
+ if (_list_Q($ast)) {
+ $el = _list();
} else {
- $el = new_vector();
+ $el = _vector();
}
foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); }
return $el;
- } elseif (hash_map_Q($ast)) {
- $new_hm = new_hash_map();
+ } elseif (_hash_map_Q($ast)) {
+ $new_hm = _hash_map();
foreach (array_keys($ast->getArrayCopy()) as $key) {
$new_hm[$key] = MAL_EVAL($ast[$key], $env);
}
@@ -33,7 +34,7 @@ function eval_ast($ast, $env) {
}
function MAL_EVAL($ast, $env) {
- if (!list_Q($ast)) {
+ if (!_list_Q($ast)) {
return eval_ast($ast, $env);
}
diff --git a/php/step3_env.php b/php/step3_env.php
index 15d7c5c..83ced32 100644
--- a/php/step3_env.php
+++ b/php/step3_env.php
@@ -3,6 +3,8 @@
require_once 'readline.php';
require_once 'types.php';
require_once 'reader.php';
+require_once 'printer.php';
+require_once 'env.php';
// read
function READ($str) {
@@ -11,18 +13,18 @@ function READ($str) {
// eval
function eval_ast($ast, $env) {
- if (symbol_Q($ast)) {
+ if (_symbol_Q($ast)) {
return $env->get($ast->value);
- } elseif (list_Q($ast) || vector_Q($ast)) {
- if (list_Q($ast)) {
- $el = new_list();
+ } elseif (_sequential_Q($ast)) {
+ if (_list_Q($ast)) {
+ $el = _list();
} else {
- $el = new_vector();
+ $el = _vector();
}
foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); }
return $el;
- } elseif (hash_map_Q($ast)) {
- $new_hm = new_hash_map();
+ } elseif (_hash_map_Q($ast)) {
+ $new_hm = _hash_map();
foreach (array_keys($ast->getArrayCopy()) as $key) {
$new_hm[$key] = MAL_EVAL($ast[$key], $env);
}
@@ -33,13 +35,14 @@ function eval_ast($ast, $env) {
}
function MAL_EVAL($ast, $env) {
- if (!list_Q($ast)) {
+ #echo "MAL_EVAL: " . _pr_str($ast) . "\n";
+ if (!_list_Q($ast)) {
return eval_ast($ast, $env);
}
// apply list
$a0 = $ast[0];
- $a0v = (symbol_Q($a0) ? $a0->value : $a0);
+ $a0v = (_symbol_Q($a0) ? $a0->value : $a0);
switch ($a0v) {
case "def!":
$res = MAL_EVAL($ast[2], $env);
diff --git a/php/step4_if_fn_do.php b/php/step4_if_fn_do.php
index 3b9593d..25ca7c5 100644
--- a/php/step4_if_fn_do.php
+++ b/php/step4_if_fn_do.php
@@ -3,6 +3,9 @@
require_once 'readline.php';
require_once 'types.php';
require_once 'reader.php';
+require_once 'printer.php';
+require_once 'env.php';
+require_once 'core.php';
// read
function READ($str) {
@@ -11,18 +14,18 @@ function READ($str) {
// eval
function eval_ast($ast, $env) {
- if (symbol_Q($ast)) {
+ if (_symbol_Q($ast)) {
return $env->get($ast->value);
- } elseif (list_Q($ast) || vector_Q($ast)) {
- if (list_Q($ast)) {
- $el = new_list();
+ } elseif (_sequential_Q($ast)) {
+ if (_list_Q($ast)) {
+ $el = _list();
} else {
- $el = new_vector();
+ $el = _vector();
}
foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); }
return $el;
- } elseif (hash_map_Q($ast)) {
- $new_hm = new_hash_map();
+ } elseif (_hash_map_Q($ast)) {
+ $new_hm = _hash_map();
foreach (array_keys($ast->getArrayCopy()) as $key) {
$new_hm[$key] = MAL_EVAL($ast[$key], $env);
}
@@ -34,13 +37,13 @@ function eval_ast($ast, $env) {
function MAL_EVAL($ast, $env) {
#echo "MAL_EVAL: " . _pr_str($ast) . "\n";
- if (!list_Q($ast)) {
+ if (!_list_Q($ast)) {
return eval_ast($ast, $env);
}
// apply list
$a0 = $ast[0];
- $a0v = (symbol_Q($a0) ? $a0->value : $a0);
+ $a0v = (_symbol_Q($a0) ? $a0->value : $a0);
switch ($a0v) {
case "def!":
$res = MAL_EVAL($ast[2], $env);
@@ -88,8 +91,8 @@ function rep($str) {
return MAL_PRINT(MAL_EVAL(READ($str), $repl_env));
}
function _ref($k, $v) { global $repl_env; $repl_env->set($k, $v); }
-// Import types functions
-foreach ($types_ns as $k=>$v) { _ref($k, $v); }
+// Import core functions
+foreach ($core_ns as $k=>$v) { _ref($k, $v); }
// Defined using the language itself
rep("(def! not (fn* (a) (if a false true)))");
diff --git a/php/step5_tco.php b/php/step5_tco.php
index 54d7699..0bf55ee 100644
--- a/php/step5_tco.php
+++ b/php/step5_tco.php
@@ -3,6 +3,9 @@
require_once 'readline.php';
require_once 'types.php';
require_once 'reader.php';
+require_once 'printer.php';
+require_once 'env.php';
+require_once 'core.php';
// read
function READ($str) {
@@ -11,18 +14,18 @@ function READ($str) {
// eval
function eval_ast($ast, $env) {
- if (symbol_Q($ast)) {
+ if (_symbol_Q($ast)) {
return $env->get($ast->value);
- } elseif (list_Q($ast) || vector_Q($ast)) {
- if (list_Q($ast)) {
- $el = new_list();
+ } elseif (_sequential_Q($ast)) {
+ if (_list_Q($ast)) {
+ $el = _list();
} else {
- $el = new_vector();
+ $el = _vector();
}
foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); }
return $el;
- } elseif (hash_map_Q($ast)) {
- $new_hm = new_hash_map();
+ } elseif (_hash_map_Q($ast)) {
+ $new_hm = _hash_map();
foreach (array_keys($ast->getArrayCopy()) as $key) {
$new_hm[$key] = MAL_EVAL($ast[$key], $env);
}
@@ -34,54 +37,56 @@ function eval_ast($ast, $env) {
function MAL_EVAL($ast, $env) {
while (true) {
- #echo "MAL_EVAL: " . _pr_str($ast) . "\n";
- if (!list_Q($ast)) {
- return eval_ast($ast, $env);
- }
- // apply list
- $a0 = $ast[0];
- $a0v = (symbol_Q($a0) ? $a0->value : $a0);
- switch ($a0v) {
- case "def!":
- $res = MAL_EVAL($ast[2], $env);
- return $env->set($ast[1]->value, $res);
- case "let*":
- $a1 = $ast[1];
- $let_env = new Env($env);
- for ($i=0; $i < count($a1); $i+=2) {
- $let_env->set($a1[$i]->value, MAL_EVAL($a1[$i+1], $let_env));
- }
- return MAL_EVAL($ast[2], $let_env);
- case "do":
- eval_ast($ast->slice(1, -1), $env);
- $ast = $ast[count($ast)-1];
- break;
- case "if":
- $cond = MAL_EVAL($ast[1], $env);
- if ($cond === NULL || $cond === false) {
- if (count($ast) === 4) { $ast = $ast[3]; }
- else { $ast = NULL; }
- } else {
- $ast = $ast[2];
- }
- break;
- case "fn*":
- return new_function('MAL_EVAL', 'native',
- new_hash_map('exp', $ast[2],
- 'env', $env,
- 'params', $ast[1]));
- default:
- $el = eval_ast($ast, $env);
- $f = $el[0];
- $args = array_slice($el->getArrayCopy(), 1);
- if ($f->type === 'native') {
- $ast = $f->meta['exp'];
- $env = new Env($f->meta['env'], $f->meta['params'], $args);
- } else {
- return $f->apply($args);
- }
+ #echo "MAL_EVAL: " . _pr_str($ast) . "\n";
+ if (!_list_Q($ast)) {
+ return eval_ast($ast, $env);
+ }
+
+ // apply list
+ $a0 = $ast[0];
+ $a0v = (_symbol_Q($a0) ? $a0->value : $a0);
+ switch ($a0v) {
+ case "def!":
+ $res = MAL_EVAL($ast[2], $env);
+ return $env->set($ast[1]->value, $res);
+ case "let*":
+ $a1 = $ast[1];
+ $let_env = new Env($env);
+ for ($i=0; $i < count($a1); $i+=2) {
+ $let_env->set($a1[$i]->value, MAL_EVAL($a1[$i+1], $let_env));
+ }
+ return MAL_EVAL($ast[2], $let_env);
+ case "do":
+ eval_ast($ast->slice(1, -1), $env);
+ $ast = $ast[count($ast)-1];
+ break;
+ case "if":
+ $cond = MAL_EVAL($ast[1], $env);
+ if ($cond === NULL || $cond === false) {
+ if (count($ast) === 4) { $ast = $ast[3]; }
+ else { $ast = NULL; }
+ } else {
+ $ast = $ast[2];
}
+ break;
+ case "fn*":
+ return _function('MAL_EVAL', 'native',
+ _hash_map('exp', $ast[2],
+ 'env', $env,
+ 'params', $ast[1]));
+ default:
+ $el = eval_ast($ast, $env);
+ $f = $el[0];
+ $args = array_slice($el->getArrayCopy(), 1);
+ if ($f->type === 'native') {
+ $ast = $f->meta['exp'];
+ $env = new Env($f->meta['env'], $f->meta['params'], $args);
+ } else {
+ return $f->apply($args);
+ }
+ }
+
}
}
@@ -98,10 +103,10 @@ function rep($str) {
}
function _ref($k, $v) {
global $repl_env;
- $repl_env->set($k, new_function($v));
+ $repl_env->set($k, _function($v));
}
-// Import types functions
-foreach ($types_ns as $k=>$v) { _ref($k, $v); }
+// Import core functions
+foreach ($core_ns as $k=>$v) { _ref($k, $v); }
// Defined using the language itself
rep("(def! not (fn* (a) (if a false true)))");
diff --git a/php/step6_file.php b/php/step6_file.php
index 37ea3c6..965ff88 100644
--- a/php/step6_file.php
+++ b/php/step6_file.php
@@ -3,6 +3,9 @@
require_once 'readline.php';
require_once 'types.php';
require_once 'reader.php';
+require_once 'printer.php';
+require_once 'env.php';
+require_once 'core.php';
// read
function READ($str) {
@@ -11,18 +14,18 @@ function READ($str) {
// eval
function eval_ast($ast, $env) {
- if (symbol_Q($ast)) {
+ if (_symbol_Q($ast)) {
return $env->get($ast->value);
- } elseif (list_Q($ast) || vector_Q($ast)) {
- if (list_Q($ast)) {
- $el = new_list();
+ } elseif (_sequential_Q($ast)) {
+ if (_list_Q($ast)) {
+ $el = _list();
} else {
- $el = new_vector();
+ $el = _vector();
}
foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); }
return $el;
- } elseif (hash_map_Q($ast)) {
- $new_hm = new_hash_map();
+ } elseif (_hash_map_Q($ast)) {
+ $new_hm = _hash_map();
foreach (array_keys($ast->getArrayCopy()) as $key) {
$new_hm[$key] = MAL_EVAL($ast[$key], $env);
}
@@ -34,54 +37,56 @@ function eval_ast($ast, $env) {
function MAL_EVAL($ast, $env) {
while (true) {
- #echo "MAL_EVAL: " . _pr_str($ast) . "\n";
- if (!list_Q($ast)) {
- return eval_ast($ast, $env);
- }
- // apply list
- $a0 = $ast[0];
- $a0v = (symbol_Q($a0) ? $a0->value : $a0);
- switch ($a0v) {
- case "def!":
- $res = MAL_EVAL($ast[2], $env);
- return $env->set($ast[1]->value, $res);
- case "let*":
- $a1 = $ast[1];
- $let_env = new Env($env);
- for ($i=0; $i < count($a1); $i+=2) {
- $let_env->set($a1[$i]->value, MAL_EVAL($a1[$i+1], $let_env));
- }
- return MAL_EVAL($ast[2], $let_env);
- case "do":
- eval_ast($ast->slice(1, -1), $env);
- $ast = $ast[count($ast)-1];
- break;
- case "if":
- $cond = MAL_EVAL($ast[1], $env);
- if ($cond === NULL || $cond === false) {
- if (count($ast) === 4) { $ast = $ast[3]; }
- else { $ast = NULL; }
- } else {
- $ast = $ast[2];
- }
- break;
- case "fn*":
- return new_function('MAL_EVAL', 'native',
- new_hash_map('exp', $ast[2],
- 'env', $env,
- 'params', $ast[1]));
- default:
- $el = eval_ast($ast, $env);
- $f = $el[0];
- $args = array_slice($el->getArrayCopy(), 1);
- if ($f->type === 'native') {
- $ast = $f->meta['exp'];
- $env = new Env($f->meta['env'], $f->meta['params'], $args);
- } else {
- return $f->apply($args);
- }
+ #echo "MAL_EVAL: " . _pr_str($ast) . "\n";
+ if (!_list_Q($ast)) {
+ return eval_ast($ast, $env);
+ }
+
+ // apply list
+ $a0 = $ast[0];
+ $a0v = (_symbol_Q($a0) ? $a0->value : $a0);
+ switch ($a0v) {
+ case "def!":
+ $res = MAL_EVAL($ast[2], $env);
+ return $env->set($ast[1]->value, $res);
+ case "let*":
+ $a1 = $ast[1];
+ $let_env = new Env($env);
+ for ($i=0; $i < count($a1); $i+=2) {
+ $let_env->set($a1[$i]->value, MAL_EVAL($a1[$i+1], $let_env));
}
+ return MAL_EVAL($ast[2], $let_env);
+ case "do":
+ eval_ast($ast->slice(1, -1), $env);
+ $ast = $ast[count($ast)-1];
+ break;
+ case "if":
+ $cond = MAL_EVAL($ast[1], $env);
+ if ($cond === NULL || $cond === false) {
+ if (count($ast) === 4) { $ast = $ast[3]; }
+ else { $ast = NULL; }
+ } else {
+ $ast = $ast[2];
+ }
+ break;
+ case "fn*":
+ return _function('MAL_EVAL', 'native',
+ _hash_map('exp', $ast[2],
+ 'env', $env,
+ 'params', $ast[1]));
+ default:
+ $el = eval_ast($ast, $env);
+ $f = $el[0];
+ $args = array_slice($el->getArrayCopy(), 1);
+ if ($f->type === 'native') {
+ $ast = $f->meta['exp'];
+ $env = new Env($f->meta['env'], $f->meta['params'], $args);
+ } else {
+ return $f->apply($args);
+ }
+ }
+
}
}
@@ -98,10 +103,10 @@ function rep($str) {
}
function _ref($k, $v) {
global $repl_env;
- $repl_env->set($k, new_function($v));
+ $repl_env->set($k, _function($v));
}
-// Import types functions
-foreach ($types_ns as $k=>$v) { _ref($k, $v); }
+// Import core functions
+foreach ($core_ns as $k=>$v) { _ref($k, $v); }
_ref('read-string', 'read_str');
_ref('eval', function($ast) {
diff --git a/php/step7_quote.php b/php/step7_quote.php
index b035be0..450f2b5 100644
--- a/php/step7_quote.php
+++ b/php/step7_quote.php
@@ -3,6 +3,9 @@
require_once 'readline.php';
require_once 'types.php';
require_once 'reader.php';
+require_once 'printer.php';
+require_once 'env.php';
+require_once 'core.php';
// read
function READ($str) {
@@ -11,37 +14,37 @@ function READ($str) {
// eval
function is_pair($x) {
- return sequential_Q($x) and count($x) > 0;
+ return _sequential_Q($x) and count($x) > 0;
}
function quasiquote($ast) {
if (!is_pair($ast)) {
- return new_list(new_symbol("quote"), $ast);
- } elseif (symbol_Q($ast[0]) && $ast[0]->value === 'unquote') {
+ return _list(_symbol("quote"), $ast);
+ } elseif (_symbol_Q($ast[0]) && $ast[0]->value === 'unquote') {
return $ast[1];
- } elseif (is_pair($ast[0]) && symbol_Q($ast[0][0]) &&
+ } elseif (is_pair($ast[0]) && _symbol_Q($ast[0][0]) &&
$ast[0][0]->value === 'splice-unquote') {
- return new_list(new_symbol("concat"), $ast[0][1],
- quasiquote($ast->slice(1)));
+ return _list(_symbol("concat"), $ast[0][1],
+ quasiquote($ast->slice(1)));
} else {
- return new_list(new_symbol("cons"), quasiquote($ast[0]),
- quasiquote($ast->slice(1)));
+ return _list(_symbol("cons"), quasiquote($ast[0]),
+ quasiquote($ast->slice(1)));
}
}
function eval_ast($ast, $env) {
- if (symbol_Q($ast)) {
+ if (_symbol_Q($ast)) {
return $env->get($ast->value);
- } elseif (list_Q($ast) || vector_Q($ast)) {
- if (list_Q($ast)) {
- $el = new_list();
+ } elseif (_sequential_Q($ast)) {
+ if (_list_Q($ast)) {
+ $el = _list();
} else {
- $el = new_vector();
+ $el = _vector();
}
foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); }
return $el;
- } elseif (hash_map_Q($ast)) {
- $new_hm = new_hash_map();
+ } elseif (_hash_map_Q($ast)) {
+ $new_hm = _hash_map();
foreach (array_keys($ast->getArrayCopy()) as $key) {
$new_hm[$key] = MAL_EVAL($ast[$key], $env);
}
@@ -53,58 +56,60 @@ function eval_ast($ast, $env) {
function MAL_EVAL($ast, $env) {
while (true) {
- #echo "MAL_EVAL: " . _pr_str($ast) . "\n";
- if (!list_Q($ast)) {
- return eval_ast($ast, $env);
- }
- // apply list
- $a0 = $ast[0];
- $a0v = (symbol_Q($a0) ? $a0->value : $a0);
- switch ($a0v) {
- case "def!":
- $res = MAL_EVAL($ast[2], $env);
- return $env->set($ast[1]->value, $res);
- case "let*":
- $a1 = $ast[1];
- $let_env = new Env($env);
- for ($i=0; $i < count($a1); $i+=2) {
- $let_env->set($a1[$i]->value, MAL_EVAL($a1[$i+1], $let_env));
- }
- return MAL_EVAL($ast[2], $let_env);
- case "quote":
- return $ast[1];
- case "quasiquote":
- return MAL_EVAL(quasiquote($ast[1]), $env);
- case "do":
- eval_ast($ast->slice(1, -1), $env);
- $ast = $ast[count($ast)-1];
- break;
- case "if":
- $cond = MAL_EVAL($ast[1], $env);
- if ($cond === NULL || $cond === false) {
- if (count($ast) === 4) { $ast = $ast[3]; }
- else { $ast = NULL; }
- } else {
- $ast = $ast[2];
- }
- break;
- case "fn*":
- return new_function('MAL_EVAL', 'native',
- new_hash_map('exp', $ast[2],
- 'env', $env,
- 'params', $ast[1]));
- default:
- $el = eval_ast($ast, $env);
- $f = $el[0];
- $args = array_slice($el->getArrayCopy(), 1);
- if ($f->type === 'native') {
- $ast = $f->meta['exp'];
- $env = new Env($f->meta['env'], $f->meta['params'], $args);
- } else {
- return $f->apply($args);
- }
+ #echo "MAL_EVAL: " . _pr_str($ast) . "\n";
+ if (!_list_Q($ast)) {
+ return eval_ast($ast, $env);
+ }
+
+ // apply list
+ $a0 = $ast[0];
+ $a0v = (_symbol_Q($a0) ? $a0->value : $a0);
+ switch ($a0v) {
+ case "def!":
+ $res = MAL_EVAL($ast[2], $env);
+ return $env->set($ast[1]->value, $res);
+ case "let*":
+ $a1 = $ast[1];
+ $let_env = new Env($env);
+ for ($i=0; $i < count($a1); $i+=2) {
+ $let_env->set($a1[$i]->value, MAL_EVAL($a1[$i+1], $let_env));
}
+ return MAL_EVAL($ast[2], $let_env);
+ case "quote":
+ return $ast[1];
+ case "quasiquote":
+ return MAL_EVAL(quasiquote($ast[1]), $env);
+ case "do":
+ eval_ast($ast->slice(1, -1), $env);
+ $ast = $ast[count($ast)-1];
+ break;
+ case "if":
+ $cond = MAL_EVAL($ast[1], $env);
+ if ($cond === NULL || $cond === false) {
+ if (count($ast) === 4) { $ast = $ast[3]; }
+ else { $ast = NULL; }
+ } else {
+ $ast = $ast[2];
+ }
+ break;
+ case "fn*":
+ return _function('MAL_EVAL', 'native',
+ _hash_map('exp', $ast[2],
+ 'env', $env,
+ 'params', $ast[1]));
+ default:
+ $el = eval_ast($ast, $env);
+ $f = $el[0];
+ $args = array_slice($el->getArrayCopy(), 1);
+ if ($f->type === 'native') {
+ $ast = $f->meta['exp'];
+ $env = new Env($f->meta['env'], $f->meta['params'], $args);
+ } else {
+ return $f->apply($args);
+ }
+ }
+
}
}
@@ -121,10 +126,10 @@ function rep($str) {
}
function _ref($k, $v) {
global $repl_env;
- $repl_env->set($k, new_function($v));
+ $repl_env->set($k, _function($v));
}
-// Import types functions
-foreach ($types_ns as $k=>$v) { _ref($k, $v); }
+// Import core functions
+foreach ($core_ns as $k=>$v) { _ref($k, $v); }
_ref('read-string', 'read_str');
_ref('eval', function($ast) {
diff --git a/php/step8_macros.php b/php/step8_macros.php
index 28014cd..3dea855 100644
--- a/php/step8_macros.php
+++ b/php/step8_macros.php
@@ -3,6 +3,9 @@
require_once 'readline.php';
require_once 'types.php';
require_once 'reader.php';
+require_once 'printer.php';
+require_once 'env.php';
+require_once 'core.php';
// read
function READ($str) {
@@ -11,27 +14,27 @@ function READ($str) {
// eval
function is_pair($x) {
- return sequential_Q($x) and count($x) > 0;
+ return _sequential_Q($x) and count($x) > 0;
}
function quasiquote($ast) {
if (!is_pair($ast)) {
- return new_list(new_symbol("quote"), $ast);
- } elseif (symbol_Q($ast[0]) && $ast[0]->value === 'unquote') {
+ return _list(_symbol("quote"), $ast);
+ } elseif (_symbol_Q($ast[0]) && $ast[0]->value === 'unquote') {
return $ast[1];
- } elseif (is_pair($ast[0]) && symbol_Q($ast[0][0]) &&
+ } elseif (is_pair($ast[0]) && _symbol_Q($ast[0][0]) &&
$ast[0][0]->value === 'splice-unquote') {
- return new_list(new_symbol("concat"), $ast[0][1],
- quasiquote($ast->slice(1)));
+ return _list(_symbol("concat"), $ast[0][1],
+ quasiquote($ast->slice(1)));
} else {
- return new_list(new_symbol("cons"), quasiquote($ast[0]),
- quasiquote($ast->slice(1)));
+ return _list(_symbol("cons"), quasiquote($ast[0]),
+ quasiquote($ast->slice(1)));
}
}
function is_macro_call($ast, $env) {
return is_pair($ast) &&
- symbol_Q($ast[0]) &&
+ _symbol_Q($ast[0]) &&
$env->find($ast[0]->value) &&
$env->get($ast[0]->value)->ismacro;
}
@@ -46,18 +49,18 @@ function macroexpand($ast, $env) {
}
function eval_ast($ast, $env) {
- if (symbol_Q($ast)) {
+ if (_symbol_Q($ast)) {
return $env->get($ast->value);
- } elseif (list_Q($ast) || vector_Q($ast)) {
- if (list_Q($ast)) {
- $el = new_list();
+ } elseif (_sequential_Q($ast)) {
+ if (_list_Q($ast)) {
+ $el = _list();
} else {
- $el = new_vector();
+ $el = _vector();
}
foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); }
return $el;
- } elseif (hash_map_Q($ast)) {
- $new_hm = new_hash_map();
+ } elseif (_hash_map_Q($ast)) {
+ $new_hm = _hash_map();
foreach (array_keys($ast->getArrayCopy()) as $key) {
$new_hm[$key] = MAL_EVAL($ast[$key], $env);
}
@@ -69,67 +72,69 @@ function eval_ast($ast, $env) {
function MAL_EVAL($ast, $env) {
while (true) {
- #echo "MAL_EVAL: " . _pr_str($ast) . "\n";
- if (!list_Q($ast)) {
- return eval_ast($ast, $env);
- }
- // apply list
- $ast = macroexpand($ast, $env);
- if (!list_Q($ast)) { return $ast; }
-
- $a0 = $ast[0];
- $a0v = (symbol_Q($a0) ? $a0->value : $a0);
- switch ($a0v) {
- case "def!":
- $res = MAL_EVAL($ast[2], $env);
- return $env->set($ast[1]->value, $res);
- case "let*":
- $a1 = $ast[1];
- $let_env = new Env($env);
- for ($i=0; $i < count($a1); $i+=2) {
- $let_env->set($a1[$i]->value, MAL_EVAL($a1[$i+1], $let_env));
- }
- return MAL_EVAL($ast[2], $let_env);
- case "quote":
- return $ast[1];
- case "quasiquote":
- return MAL_EVAL(quasiquote($ast[1]), $env);
- case "defmacro!":
- $func = MAL_EVAL($ast[2], $env);
- $func->ismacro = true;
- return $env->set($ast[1]->value, $func);
- case "macroexpand":
- return macroexpand($ast[1], $env);
- case "do":
- eval_ast($ast->slice(1, -1), $env);
- $ast = $ast[count($ast)-1];
- break;
- case "if":
- $cond = MAL_EVAL($ast[1], $env);
- if ($cond === NULL || $cond === false) {
- if (count($ast) === 4) { $ast = $ast[3]; }
- else { $ast = NULL; }
- } else {
- $ast = $ast[2];
- }
- break;
- case "fn*":
- return new_function('MAL_EVAL', 'native',
- new_hash_map('exp', $ast[2],
- 'env', $env,
- 'params', $ast[1]));
- default:
- $el = eval_ast($ast, $env);
- $f = $el[0];
- $args = array_slice($el->getArrayCopy(), 1);
- if ($f->type === 'native') {
- $ast = $f->meta['exp'];
- $env = new Env($f->meta['env'], $f->meta['params'], $args);
- } else {
- return $f->apply($args);
- }
+ #echo "MAL_EVAL: " . _pr_str($ast) . "\n";
+ if (!_list_Q($ast)) {
+ return eval_ast($ast, $env);
+ }
+
+ // apply list
+ $ast = macroexpand($ast, $env);
+ if (!_list_Q($ast)) { return $ast; }
+
+ $a0 = $ast[0];
+ $a0v = (_symbol_Q($a0) ? $a0->value : $a0);
+ switch ($a0v) {
+ case "def!":
+ $res = MAL_EVAL($ast[2], $env);
+ return $env->set($ast[1]->value, $res);
+ case "let*":
+ $a1 = $ast[1];
+ $let_env = new Env($env);
+ for ($i=0; $i < count($a1); $i+=2) {
+ $let_env->set($a1[$i]->value, MAL_EVAL($a1[$i+1], $let_env));
}
+ return MAL_EVAL($ast[2], $let_env);
+ case "quote":
+ return $ast[1];
+ case "quasiquote":
+ return MAL_EVAL(quasiquote($ast[1]), $env);
+ case "defmacro!":
+ $func = MAL_EVAL($ast[2], $env);
+ $func->ismacro = true;
+ return $env->set($ast[1]->value, $func);
+ case "macroexpand":
+ return macroexpand($ast[1], $env);
+ case "do":
+ eval_ast($ast->slice(1, -1), $env);
+ $ast = $ast[count($ast)-1];
+ break;
+ case "if":
+ $cond = MAL_EVAL($ast[1], $env);
+ if ($cond === NULL || $cond === false) {
+ if (count($ast) === 4) { $ast = $ast[3]; }
+ else { $ast = NULL; }
+ } else {
+ $ast = $ast[2];
+ }
+ break;
+ case "fn*":
+ return _function('MAL_EVAL', 'native',
+ _hash_map('exp', $ast[2],
+ 'env', $env,
+ 'params', $ast[1]));
+ default:
+ $el = eval_ast($ast, $env);
+ $f = $el[0];
+ $args = array_slice($el->getArrayCopy(), 1);
+ if ($f->type === 'native') {
+ $ast = $f->meta['exp'];
+ $env = new Env($f->meta['env'], $f->meta['params'], $args);
+ } else {
+ return $f->apply($args);
+ }
+ }
+
}
}
@@ -146,10 +151,10 @@ function rep($str) {
}
function _ref($k, $v) {
global $repl_env;
- $repl_env->set($k, new_function($v));
+ $repl_env->set($k, _function($v));
}
-// Import types functions
-foreach ($types_ns as $k=>$v) { _ref($k, $v); }
+// Import core functions
+foreach ($core_ns as $k=>$v) { _ref($k, $v); }
_ref('read-string', 'read_str');
_ref('eval', function($ast) {
diff --git a/php/step9_interop.php b/php/step9_interop.php
index 26e89f0..a699109 100644
--- a/php/step9_interop.php
+++ b/php/step9_interop.php
@@ -3,6 +3,9 @@
require_once 'readline.php';
require_once 'types.php';
require_once 'reader.php';
+require_once 'printer.php';
+require_once 'env.php';
+require_once 'core.php';
// read
function READ($str) {
@@ -11,27 +14,27 @@ function READ($str) {
// eval
function is_pair($x) {
- return sequential_Q($x) and count($x) > 0;
+ return _sequential_Q($x) and count($x) > 0;
}
function quasiquote($ast) {
if (!is_pair($ast)) {
- return new_list(new_symbol("quote"), $ast);
- } elseif (symbol_Q($ast[0]) && $ast[0]->value === 'unquote') {
+ return _list(_symbol("quote"), $ast);
+ } elseif (_symbol_Q($ast[0]) && $ast[0]->value === 'unquote') {
return $ast[1];
- } elseif (is_pair($ast[0]) && symbol_Q($ast[0][0]) &&
+ } elseif (is_pair($ast[0]) && _symbol_Q($ast[0][0]) &&
$ast[0][0]->value === 'splice-unquote') {
- return new_list(new_symbol("concat"), $ast[0][1],
- quasiquote($ast->slice(1)));
+ return _list(_symbol("concat"), $ast[0][1],
+ quasiquote($ast->slice(1)));
} else {
- return new_list(new_symbol("cons"), quasiquote($ast[0]),
- quasiquote($ast->slice(1)));
+ return _list(_symbol("cons"), quasiquote($ast[0]),
+ quasiquote($ast->slice(1)));
}
}
function is_macro_call($ast, $env) {
return is_pair($ast) &&
- symbol_Q($ast[0]) &&
+ _symbol_Q($ast[0]) &&
$env->find($ast[0]->value) &&
$env->get($ast[0]->value)->ismacro;
}
@@ -46,18 +49,18 @@ function macroexpand($ast, $env) {
}
function eval_ast($ast, $env) {
- if (symbol_Q($ast)) {
+ if (_symbol_Q($ast)) {
return $env->get($ast->value);
- } elseif (list_Q($ast) || vector_Q($ast)) {
- if (list_Q($ast)) {
- $el = new_list();
+ } elseif (_sequential_Q($ast)) {
+ if (_list_Q($ast)) {
+ $el = _list();
} else {
- $el = new_vector();
+ $el = _vector();
}
foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); }
return $el;
- } elseif (hash_map_Q($ast)) {
- $new_hm = new_hash_map();
+ } elseif (_hash_map_Q($ast)) {
+ $new_hm = _hash_map();
foreach (array_keys($ast->getArrayCopy()) as $key) {
$new_hm[$key] = MAL_EVAL($ast[$key], $env);
}
@@ -69,69 +72,71 @@ function eval_ast($ast, $env) {
function MAL_EVAL($ast, $env) {
while (true) {
- #echo "MAL_EVAL: " . _pr_str($ast) . "\n";
- if (!list_Q($ast)) {
- return eval_ast($ast, $env);
- }
- // apply list
- $ast = macroexpand($ast, $env);
- if (!list_Q($ast)) { return $ast; }
-
- $a0 = $ast[0];
- $a0v = (symbol_Q($a0) ? $a0->value : $a0);
- switch ($a0v) {
- case "def!":
- $res = MAL_EVAL($ast[2], $env);
- return $env->set($ast[1]->value, $res);
- case "let*":
- $a1 = $ast[1];
- $let_env = new Env($env);
- for ($i=0; $i < count($a1); $i+=2) {
- $let_env->set($a1[$i]->value, MAL_EVAL($a1[$i+1], $let_env));
- }
- return MAL_EVAL($ast[2], $let_env);
- case "quote":
- return $ast[1];
- case "quasiquote":
- return MAL_EVAL(quasiquote($ast[1]), $env);
- case "defmacro!":
- $func = MAL_EVAL($ast[2], $env);
- $func->ismacro = true;
- return $env->set($ast[1]->value, $func);
- case "macroexpand":
- return macroexpand($ast[1], $env);
- case "php*":
- return eval($ast[1]);
- case "do":
- eval_ast($ast->slice(1, -1), $env);
- $ast = $ast[count($ast)-1];
- break;
- case "if":
- $cond = MAL_EVAL($ast[1], $env);
- if ($cond === NULL || $cond === false) {
- if (count($ast) === 4) { $ast = $ast[3]; }
- else { $ast = NULL; }
- } else {
- $ast = $ast[2];
- }
- break;
- case "fn*":
- return new_function('MAL_EVAL', 'native',
- new_hash_map('exp', $ast[2],
- 'env', $env,
- 'params', $ast[1]));
- default:
- $el = eval_ast($ast, $env);
- $f = $el[0];
- $args = array_slice($el->getArrayCopy(), 1);
- if ($f->type === 'native') {
- $ast = $f->meta['exp'];
- $env = new Env($f->meta['env'], $f->meta['params'], $args);
- } else {
- return $f->apply($args);
- }
+ #echo "MAL_EVAL: " . _pr_str($ast) . "\n";
+ if (!_list_Q($ast)) {
+ return eval_ast($ast, $env);
+ }
+
+ // apply list
+ $ast = macroexpand($ast, $env);
+ if (!_list_Q($ast)) { return $ast; }
+
+ $a0 = $ast[0];
+ $a0v = (_symbol_Q($a0) ? $a0->value : $a0);
+ switch ($a0v) {
+ case "def!":
+ $res = MAL_EVAL($ast[2], $env);
+ return $env->set($ast[1]->value, $res);
+ case "let*":
+ $a1 = $ast[1];
+ $let_env = new Env($env);
+ for ($i=0; $i < count($a1); $i+=2) {
+ $let_env->set($a1[$i]->value, MAL_EVAL($a1[$i+1], $let_env));
}
+ return MAL_EVAL($ast[2], $let_env);
+ case "quote":
+ return $ast[1];
+ case "quasiquote":
+ return MAL_EVAL(quasiquote($ast[1]), $env);
+ case "defmacro!":
+ $func = MAL_EVAL($ast[2], $env);
+ $func->ismacro = true;
+ return $env->set($ast[1]->value, $func);
+ case "macroexpand":
+ return macroexpand($ast[1], $env);
+ case "php*":
+ return eval($ast[1]);
+ case "do":
+ eval_ast($ast->slice(1, -1), $env);
+ $ast = $ast[count($ast)-1];
+ break;
+ case "if":
+ $cond = MAL_EVAL($ast[1], $env);
+ if ($cond === NULL || $cond === false) {
+ if (count($ast) === 4) { $ast = $ast[3]; }
+ else { $ast = NULL; }
+ } else {
+ $ast = $ast[2];
+ }
+ break;
+ case "fn*":
+ return _function('MAL_EVAL', 'native',
+ _hash_map('exp', $ast[2],
+ 'env', $env,
+ 'params', $ast[1]));
+ default:
+ $el = eval_ast($ast, $env);
+ $f = $el[0];
+ $args = array_slice($el->getArrayCopy(), 1);
+ if ($f->type === 'native') {
+ $ast = $f->meta['exp'];
+ $env = new Env($f->meta['env'], $f->meta['params'], $args);
+ } else {
+ return $f->apply($args);
+ }
+ }
+
}
}
@@ -148,10 +153,10 @@ function rep($str) {
}
function _ref($k, $v) {
global $repl_env;
- $repl_env->set($k, new_function($v));
+ $repl_env->set($k, _function($v));
}
-// Import types functions
-foreach ($types_ns as $k=>$v) { _ref($k, $v); }
+// Import core functions
+foreach ($core_ns as $k=>$v) { _ref($k, $v); }
_ref('read-string', 'read_str');
_ref('eval', function($ast) {
diff --git a/php/stepA_more.php b/php/stepA_more.php
index dd004cf..4b8a270 100644
--- a/php/stepA_more.php
+++ b/php/stepA_more.php
@@ -3,6 +3,9 @@
require_once 'readline.php';
require_once 'types.php';
require_once 'reader.php';
+require_once 'printer.php';
+require_once 'env.php';
+require_once 'core.php';
// read
function READ($str) {
@@ -11,27 +14,27 @@ function READ($str) {
// eval
function is_pair($x) {
- return sequential_Q($x) and count($x) > 0;
+ return _sequential_Q($x) and count($x) > 0;
}
function quasiquote($ast) {
if (!is_pair($ast)) {
- return new_list(new_symbol("quote"), $ast);
- } elseif (symbol_Q($ast[0]) && $ast[0]->value === 'unquote') {
+ return _list(_symbol("quote"), $ast);
+ } elseif (_symbol_Q($ast[0]) && $ast[0]->value === 'unquote') {
return $ast[1];
- } elseif (is_pair($ast[0]) && symbol_Q($ast[0][0]) &&
+ } elseif (is_pair($ast[0]) && _symbol_Q($ast[0][0]) &&
$ast[0][0]->value === 'splice-unquote') {
- return new_list(new_symbol("concat"), $ast[0][1],
- quasiquote($ast->slice(1)));
+ return _list(_symbol("concat"), $ast[0][1],
+ quasiquote($ast->slice(1)));
} else {
- return new_list(new_symbol("cons"), quasiquote($ast[0]),
- quasiquote($ast->slice(1)));
+ return _list(_symbol("cons"), quasiquote($ast[0]),
+ quasiquote($ast->slice(1)));
}
}
function is_macro_call($ast, $env) {
return is_pair($ast) &&
- symbol_Q($ast[0]) &&
+ _symbol_Q($ast[0]) &&
$env->find($ast[0]->value) &&
$env->get($ast[0]->value)->ismacro;
}
@@ -46,18 +49,18 @@ function macroexpand($ast, $env) {
}
function eval_ast($ast, $env) {
- if (symbol_Q($ast)) {
+ if (_symbol_Q($ast)) {
return $env->get($ast->value);
- } elseif (list_Q($ast) || vector_Q($ast)) {
- if (list_Q($ast)) {
- $el = new_list();
+ } elseif (_sequential_Q($ast)) {
+ if (_list_Q($ast)) {
+ $el = _list();
} else {
- $el = new_vector();
+ $el = _vector();
}
foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); }
return $el;
- } elseif (hash_map_Q($ast)) {
- $new_hm = new_hash_map();
+ } elseif (_hash_map_Q($ast)) {
+ $new_hm = _hash_map();
foreach (array_keys($ast->getArrayCopy()) as $key) {
$new_hm[$key] = MAL_EVAL($ast[$key], $env);
}
@@ -69,87 +72,89 @@ function eval_ast($ast, $env) {
function MAL_EVAL($ast, $env) {
while (true) {
- #echo "MAL_EVAL: " . _pr_str($ast) . "\n";
- if (!list_Q($ast)) {
- return eval_ast($ast, $env);
- }
- // apply list
- $ast = macroexpand($ast, $env);
- if (!list_Q($ast)) { return $ast; }
-
- $a0 = $ast[0];
- $a0v = (symbol_Q($a0) ? $a0->value : $a0);
- switch ($a0v) {
- case "def!":
- $res = MAL_EVAL($ast[2], $env);
- return $env->set($ast[1]->value, $res);
- case "let*":
- $a1 = $ast[1];
- $let_env = new Env($env);
- for ($i=0; $i < count($a1); $i+=2) {
- $let_env->set($a1[$i]->value, MAL_EVAL($a1[$i+1], $let_env));
- }
- return MAL_EVAL($ast[2], $let_env);
- case "quote":
- return $ast[1];
- case "quasiquote":
- return MAL_EVAL(quasiquote($ast[1]), $env);
- case "defmacro!":
- $func = MAL_EVAL($ast[2], $env);
- $func->ismacro = true;
- return $env->set($ast[1]->value, $func);
- case "macroexpand":
- return macroexpand($ast[1], $env);
- case "php*":
- return eval($ast[1]);
- case "try*":
- $a1 = $ast[1];
- $a2 = $ast[2];
- if ($a2[0]->value === "catch*") {
- try {
- return MAL_EVAL($a1, $env);
- } catch (Error $e) {
- $catch_env = new Env($env, array($a2[1]),
- array($e->obj));
- return MAL_EVAL($a2[2], $catch_env);
- } catch (Exception $e) {
- $catch_env = new Env($env, array($a2[1]),
- array($e->getMessage()));
- return MAL_EVAL($a2[2], $catch_env);
- }
- } else {
+ #echo "MAL_EVAL: " . _pr_str($ast) . "\n";
+ if (!_list_Q($ast)) {
+ return eval_ast($ast, $env);
+ }
+
+ // apply list
+ $ast = macroexpand($ast, $env);
+ if (!_list_Q($ast)) { return $ast; }
+
+ $a0 = $ast[0];
+ $a0v = (_symbol_Q($a0) ? $a0->value : $a0);
+ switch ($a0v) {
+ case "def!":
+ $res = MAL_EVAL($ast[2], $env);
+ return $env->set($ast[1]->value, $res);
+ case "let*":
+ $a1 = $ast[1];
+ $let_env = new Env($env);
+ for ($i=0; $i < count($a1); $i+=2) {
+ $let_env->set($a1[$i]->value, MAL_EVAL($a1[$i+1], $let_env));
+ }
+ return MAL_EVAL($ast[2], $let_env);
+ case "quote":
+ return $ast[1];
+ case "quasiquote":
+ return MAL_EVAL(quasiquote($ast[1]), $env);
+ case "defmacro!":
+ $func = MAL_EVAL($ast[2], $env);
+ $func->ismacro = true;
+ return $env->set($ast[1]->value, $func);
+ case "macroexpand":
+ return macroexpand($ast[1], $env);
+ case "php*":
+ return eval($ast[1]);
+ case "try*":
+ $a1 = $ast[1];
+ $a2 = $ast[2];
+ if ($a2[0]->value === "catch*") {
+ try {
return MAL_EVAL($a1, $env);
+ } catch (Error $e) {
+ $catch_env = new Env($env, array($a2[1]),
+ array($e->obj));
+ return MAL_EVAL($a2[2], $catch_env);
+ } catch (Exception $e) {
+ $catch_env = new Env($env, array($a2[1]),
+ array($e->getMessage()));
+ return MAL_EVAL($a2[2], $catch_env);
}
- case "do":
- eval_ast($ast->slice(1, -1), $env);
- $ast = $ast[count($ast)-1];
- break;
- case "if":
- $cond = MAL_EVAL($ast[1], $env);
- if ($cond === NULL || $cond === false) {
- if (count($ast) === 4) { $ast = $ast[3]; }
- else { $ast = NULL; }
- } else {
- $ast = $ast[2];
- }
- break;
- case "fn*":
- return new_function('MAL_EVAL', 'native',
- new_hash_map('exp', $ast[2],
- 'env', $env,
- 'params', $ast[1]));
- default:
- $el = eval_ast($ast, $env);
- $f = $el[0];
- $args = array_slice($el->getArrayCopy(), 1);
- if ($f->type === 'native') {
- $ast = $f->meta['exp'];
- $env = new Env($f->meta['env'], $f->meta['params'], $args);
- } else {
- return $f->apply($args);
- }
+ } else {
+ return MAL_EVAL($a1, $env);
+ }
+ case "do":
+ eval_ast($ast->slice(1, -1), $env);
+ $ast = $ast[count($ast)-1];
+ break;
+ case "if":
+ $cond = MAL_EVAL($ast[1], $env);
+ if ($cond === NULL || $cond === false) {
+ if (count($ast) === 4) { $ast = $ast[3]; }
+ else { $ast = NULL; }
+ } else {
+ $ast = $ast[2];
}
+ break;
+ case "fn*":
+ return _function('MAL_EVAL', 'native',
+ _hash_map('exp', $ast[2],
+ 'env', $env,
+ 'params', $ast[1]));
+ default:
+ $el = eval_ast($ast, $env);
+ $f = $el[0];
+ $args = array_slice($el->getArrayCopy(), 1);
+ if ($f->type === 'native') {
+ $ast = $f->meta['exp'];
+ $env = new Env($f->meta['env'], $f->meta['params'], $args);
+ } else {
+ return $f->apply($args);
+ }
+ }
+
}
}
@@ -166,10 +171,10 @@ function rep($str) {
}
function _ref($k, $v) {
global $repl_env;
- $repl_env->set($k, new_function($v));
+ $repl_env->set($k, _function($v));
}
-// Import types functions
-foreach ($types_ns as $k=>$v) { _ref($k, $v); }
+// Import core functions
+foreach ($core_ns as $k=>$v) { _ref($k, $v); }
_ref('readline', 'mal_readline');
_ref('read-string', 'read_str');
diff --git a/php/types.php b/php/types.php
index 4486a18..6094558 100644
--- a/php/types.php
+++ b/php/types.php
@@ -1,101 +1,30 @@
<?php
-function _pr_str($obj, $print_readably=True) {
- if (list_Q($obj)) {
- $ret = array();
- foreach ($obj as $e) {
- array_push($ret, _pr_str($e, $print_readably));
- }
- return "(" . implode(" ", $ret) . ")";
- } elseif (vector_Q($obj)) {
- $ret = array();
- foreach ($obj as $e) {
- array_push($ret, _pr_str($e, $print_readably));
- }
- return "[" . implode(" ", $ret) . "]";
- } elseif (hash_map_Q($obj)) {
- $ret = array();
- foreach (array_keys($obj->getArrayCopy()) as $k) {
- $ret[] = _pr_str($k, $print_readably);
- $ret[] = _pr_str($obj[$k], $print_readably);
- }
- return "{" . implode(" ", $ret) . "}";
- } elseif (is_string($obj)) {
- if ($print_readably) {
- $obj = preg_replace('/"/', '\\"', preg_replace('/\\\\/', '\\\\\\\\', $obj));
- return '"' . $obj . '"';
- } else {
- return $obj;
- }
- } elseif (is_integer($obj)) {
- return $obj;
- } elseif ($obj === NULL) {
- return "nil";
- } elseif ($obj === true) {
- return "true";
- } elseif ($obj === false) {
- return "false";
- } elseif (symbol_Q($obj)) {
- return $obj->value;
- } elseif (atom_Q($obj)) {
- return "(atom " . _pr_str($obj->value, $print_readably) . ")";
- } elseif (function_Q($obj)) {
- return "(fn* [...] ...)";
- } elseif (is_callable($obj)) { // only step4 and below
- return "#<function ...>";
- } else {
- throw new Exception("_pr_str unknown type: " . gettype($obj));
- }
-}
-function pr_str() {
- $ps = array_map(function ($obj) { return _pr_str($obj, True); },
- func_get_args());
- return implode(" ", $ps);
-}
-
-function str() {
- $ps = array_map(function ($obj) { return _pr_str($obj, False); },
- func_get_args());
- return implode("", $ps);
-}
-
-function prn() {
- $ps = array_map(function ($obj) { return _pr_str($obj, True); },
- func_get_args());
- print implode(" ", $ps) . "\n";
- return null;
-}
-
-function println() {
- $ps = array_map(function ($obj) { return _pr_str($obj, False); },
- func_get_args());
- print implode(" ", $ps) . "\n";
- return null;
+// Errors/Exceptions
+class Error extends Exception {
+ public $obj = null;
+ public function __construct($obj) {
+ parent::__construct("Mal Error", 0, null);
+ $this->obj = $obj;
+ }
}
-function with_meta($obj, $m) {
- $new_obj = clone $obj;
- $new_obj->meta = $m;
- return $new_obj;
-}
-function meta($obj) {
- return $obj->meta;
-}
+// General functions
-function equal_Q($a, $b) {
+function _equal_Q($a, $b) {
$ota = gettype($a) === "object" ? get_class($a) : gettype($a);
$otb = gettype($b) === "object" ? get_class($b) : gettype($b);
- if (!($ota === $otb or (sequential_Q($a) and sequential_Q($b)))) {
+ if (!($ota === $otb or (_sequential_Q($a) and _sequential_Q($b)))) {
return false;
- } elseif (symbol_Q($a)) {
+ } elseif (_symbol_Q($a)) {
#print "ota: $ota, otb: $otb\n";
return $a->value === $b->value;
- } elseif (list_Q($a) or vector_Q($a)) {
+ } elseif (_list_Q($a) or _vector_Q($a)) {
if ($a->count() !== $b->count()) { return false; }
for ($i=0; $i<$a->count(); $i++) {
- if (!equal_Q($a[$i], $b[$i])) { return false; }
+ if (!_equal_Q($a[$i], $b[$i])) { return false; }
}
return true;
} else {
@@ -103,14 +32,17 @@ function equal_Q($a, $b) {
}
}
-// nil, true, false, string
-function nil_Q($obj) { return $obj === NULL; }
-function true_Q($obj) { return $obj === true; }
-function false_Q($obj) { return $obj === false; }
-function string_Q($obj) { return is_string($obj); }
+function _sequential_Q($seq) { return _list_Q($seq) or _vector_Q($seq); }
-// symbols
+// Scalars
+function _nil_Q($obj) { return $obj === NULL; }
+function _true_Q($obj) { return $obj === true; }
+function _false_Q($obj) { return $obj === false; }
+function _string_Q($obj) { return is_string($obj); }
+
+
+// Symbols
class SymbolClass {
public $value = NULL;
public $meta = NULL;
@@ -118,10 +50,8 @@ class SymbolClass {
$this->value = $value;
}
}
-
-function new_symbol($name) { return new SymbolClass($name); }
-
-function symbol_Q($obj) { return ($obj instanceof SymbolClass); }
+function _symbol($name) { return new SymbolClass($name); }
+function _symbol_Q($obj) { return ($obj instanceof SymbolClass); }
// Functions
@@ -152,11 +82,11 @@ class FunctionClass {
}
}
-function new_function($func, $type='platform', $meta=NULL, $ismacro=False) {
+function _function($func, $type='platform', $meta=NULL, $ismacro=False) {
return new FunctionClass($func, $type, $meta, $ismacro);
}
+function _function_Q($obj) { return $obj instanceof FunctionClass; }
-function function_Q($obj) { return $obj instanceof FunctionClass; }
// Parent class of list, vector, hash-map
// http://www.php.net/manual/en/class.arrayobject.php
@@ -174,24 +104,49 @@ class SeqClass extends ArrayObject {
}
+// Lists
+class ListClass extends SeqClass {
+ public $meta = NULL;
+}
+
+function _list() {
+ $v = new ListClass();
+ $v->exchangeArray(func_get_args());
+ return $v;
+}
+function _list_Q($obj) { return $obj instanceof ListClass; }
+
+
+// Vectors
+class VectorClass extends SeqClass {
+ public $meta = NULL;
+}
+
+function _vector() {
+ $v = new VectorClass();
+ $v->exchangeArray(func_get_args());
+ return $v;
+}
+function _vector_Q($obj) { return $obj instanceof VectorClass; }
+
+
// Hash Maps
class HashMapClass extends ArrayObject {
public $meta = NULL;
}
-function new_hash_map() {
+function _hash_map() {
$args = func_get_args();
if (count($args) % 2 === 1) {
throw new Exception("Odd number of hash map arguments");
}
$hm = new HashMapClass();
array_unshift($args, $hm);
- return call_user_func_array('assoc_BANG', $args);
+ return call_user_func_array('_assoc_BANG', $args);
}
+function _hash_map_Q($obj) { return $obj instanceof HashMapClass; }
-function hash_map_Q($obj) { return $obj instanceof HashMapClass; }
-
-function assoc_BANG($hm) {
+function _assoc_BANG($hm) {
$args = func_get_args();
if (count($args) % 2 !== 1) {
throw new Exception("Odd number of assoc arguments");
@@ -208,14 +163,7 @@ function assoc_BANG($hm) {
return $hm;
}
-function assoc($src_hm) {
- $args = func_get_args();
- $hm = clone $src_hm;
- $args[0] = $hm;
- return call_user_func_array('assoc_BANG', $args);
-}
-
-function dissoc_BANG($hm) {
+function _dissoc_BANG($hm) {
$args = func_get_args();
for ($i=1; $i<count($args); $i++) {
$ktoken = $args[$i];
@@ -224,72 +172,8 @@ function dissoc_BANG($hm) {
return $hm;
}
-function dissoc($src_hm) {
- $args = func_get_args();
- $hm = clone $src_hm;
- $args[0] = $hm;
- return call_user_func_array('dissoc_BANG', $args);
-}
-
-function get($hm, $k) {
- if ($hm && $hm->offsetExists($k)) {
- return $hm[$k];
- } else {
- return NULL;
- }
-}
-
-function contains_Q($hm, $k) { return array_key_exists($k, $hm); }
-
-function keys($hm) {
- return call_user_func_array('new_list', array_keys($hm->getArrayCopy()));
-}
-function vals($hm) {
- return call_user_func_array('new_list', array_values($hm->getArrayCopy()));
-}
-
-
-// errors/exceptions
-class Error extends Exception {
- public $obj = null;
- public function __construct($obj) {
- parent::__construct("Mal Error", 0, null);
- $this->obj = $obj;
- }
-}
-
-function mal_throw($obj) { throw new Error($obj); }
-
-
-// lists
-class ListClass extends SeqClass {
- public $meta = NULL;
-}
-
-function new_list() {
- $v = new ListClass();
- $v->exchangeArray(func_get_args());
- return $v;
-}
-
-function list_Q($obj) { return $obj instanceof ListClass; }
-
-// vectors
-class VectorClass extends SeqClass {
- public $meta = NULL;
-}
-
-function new_vector() {
- $v = new VectorClass();
- $v->exchangeArray(func_get_args());
- return $v;
-}
-
-function vector_Q($obj) { return $obj instanceof VectorClass; }
-
// Atoms
-
class Atom {
public $value = NULL;
public $meta = NULL;
@@ -297,193 +181,7 @@ class Atom {
$this->value = $value;
}
}
-function new_atom($val) { return new Atom($val); }
-function atom_Q($atm) { return $atm instanceof Atom; }
-function deref($atm) { return $atm->value; }
-function reset_BANG($atm, $val) { return $atm->value = $val; }
-function swap_BANG($atm, $f) {
- $args = array_slice(func_get_args(),2);
- array_unshift($args, $atm->value);
- $atm->value = call_user_func_array($f, $args);
- return $atm->value;
-}
-
-
-// Sequence operations
-function sequential_Q($seq) { return list_Q($seq) or vector_Q($seq); }
-
-function scount($seq) { return ($seq === NULL ? 0 : $seq->count()); }
-
-function empty_Q($seq) { return $seq->count() === 0; }
-
-function cons($a, $b) {
- $tmp = $b->getArrayCopy();
- array_unshift($tmp, $a);
- $l = new ListClass();
- $l->exchangeArray($tmp);
- return $l;
-}
-
-function concat() {
- $args = func_get_args();
- $tmp = array();
- foreach ($args as $arg) {
- $tmp = array_merge($tmp, $arg->getArrayCopy());
- }
- $l = new ListClass();
- $l->exchangeArray($tmp);
- return $l;
-}
-
-function conj($src) {
- $args = array_slice(func_get_args(), 1);
- $tmp = $src->getArrayCopy();
- if (list_Q($src)) {
- foreach ($args as $arg) { array_unshift($tmp, $arg); }
- $s = new ListClass();
- } else {
- foreach ($args as $arg) { $tmp[] = $arg; }
- $s = new VectorClass();
- }
- $s->exchangeArray($tmp);
- return $s;
-}
-
-function first($seq) {
- if (count($seq) === 0) {
- return NULL;
- } else {
- return $seq[0];
- }
-}
-
-function rest($seq) {
- $l = new ListClass();
- $l->exchangeArray(array_slice($seq->getArrayCopy(), 1));
- return $l;
-}
-
-function nth($seq, $idx) {
- return $seq[$idx];
-}
-
-function apply($f) {
- $args = array_slice(func_get_args(), 1);
- $last_arg = array_pop($args)->getArrayCopy();
- return $f->apply(array_merge($args, $last_arg));
-}
-
-function map($f, $seq) {
- $l = new ListClass();
- $l->exchangeArray(array_map($f, $seq->getArrayCopy()));
- return $l;
-}
-
-
-// Environment
-class Env {
- public $data = array();
- public $outer = NULL;
- public function __construct($outer, $binds=NULL, $exprs=NULL) {
- $this->outer = $outer;
- if ($binds) {
- if (sequential_Q($exprs)) {
- $exprs = $exprs->getArrayCopy();
- }
- for ($i=0; $i<count($binds); $i++) {
- if ($binds[$i]->value === "&") {
- if ($exprs !== NULL && $i < count($exprs)) {
- $lst = call_user_func_array('new_list', array_slice($exprs, $i));
- } else {
- $lst = new_list();
- }
- $this->data[$binds[$i+1]->value] = $lst;
- break;
- } else {
- if ($exprs !== NULL && $i < count($exprs)) {
- $this->data[$binds[$i]->value] = $exprs[$i];
- } else {
- $this->data[$binds[$i]->value] = NULL;
- }
- }
- }
- }
- }
- public function find($key) {
- if (array_key_exists($key, $this->data)) {
- return $this;
- } elseif ($this->outer) {
- return $this->outer->find($key);
- } else {
- return NULL;
- }
- }
- public function set($key, $value) {
- $this->data[$key] = $value;
- return $value;
- }
- public function get($key) {
- $env = $this->find($key);
- if (!$env) {
- throw new Exception("'" . $key . "' not found");
- } else {
- return $env->data[$key];
- }
- }
-}
-
-// types_ns is namespace of type functions
-$types_ns = array(
- 'pr-str'=> function () { return call_user_func_array('pr_str', func_get_args()); },
- 'str'=> function () { return call_user_func_array('str', func_get_args()); },
- 'prn'=> function () { return call_user_func_array('prn', func_get_args()); },
- 'println'=>function () { return call_user_func_array('println', func_get_args()); },
- 'with-meta'=> function ($a, $b) { return with_meta($a, $b); },
- 'meta'=> function ($a) { return meta($a); },
- '='=> function ($a, $b) { return equal_Q($a, $b); },
- 'nil?'=> function ($a) { return nil_Q($a); },
- 'true?'=> function ($a) { return true_Q($a); },
- 'false?'=> function ($a) { return false_Q($a); },
- '+'=> function ($a, $b) { return intval($a + $b,10); },
- '-'=> function ($a, $b) { return intval($a - $b,10); },
- '*'=> function ($a, $b) { return intval($a * $b,10); },
- '/'=> function ($a, $b) { return intval($a / $b,10); },
- '<'=> function ($a, $b) { return $a < $b; },
- '<='=> function ($a, $b) { return $a <= $b; },
- '>'=> function ($a, $b) { return $a > $b; },
- '>='=> function ($a, $b) { return $a >= $b; },
- 'symbol?'=> function ($a) { return symbol_Q($a); },
- 'string?'=> function ($a) { return string_Q($a); },
- 'hash-map' => function () { return call_user_func_array('new_hash_map', func_get_args()); },
- 'map?'=> function ($a) { return hash_map_Q($a); },
- 'assoc' => function () { return call_user_func_array('assoc', func_get_args()); },
- 'dissoc' => function () { return call_user_func_array('dissoc', func_get_args()); },
- 'get' => function ($a, $b) { return get($a, $b); },
- 'contains?' => function ($a, $b) { return contains_Q($a, $b); },
- 'keys' => function ($a) { return keys($a); },
- 'vals' => function ($a) { return vals($a); },
- 'throw'=> function ($a) { return mal_throw($a); },
- 'list'=> function () { return call_user_func_array('new_list', func_get_args()); },
- 'list?'=> function ($a) { return list_Q($a); },
- 'vector'=> function () { return call_user_func_array('new_vector', func_get_args()); },
- 'vector?'=> function ($a) { return vector_Q($a); },
- 'atom'=> function ($a) { return new_atom($a); },
- 'atom?'=> function ($a) { return atom_Q($a); },
- 'deref'=> function ($a) { return deref($a); },
- 'reset!'=> function ($a, $b) { return reset_BANG($a, $b); },
- 'swap!'=> function () { return call_user_func_array('swap_BANG', func_get_args()); },
- 'sequential?'=> function ($a) { return sequential_Q($a); },
- 'count'=> function ($a) { return scount($a); },
- 'empty?'=> function ($a) { return empty_Q($a); },
- 'cons'=> function ($a, $b) { return cons($a, $b); },
- 'concat'=> function () { return call_user_func_array('concat', func_get_args()); },
- 'conj'=> function () { return call_user_func_array('conj', func_get_args()); },
- 'first'=> function ($a) { return first($a); },
- 'rest'=> function ($a) { return rest($a); },
- 'nth'=> function ($a, $b) { return nth($a, $b); },
- 'apply'=> function () { return call_user_func_array('apply', func_get_args()); },
- 'map'=> function ($a, $b) { return map($a, $b); }
-);
-
+function _atom($val) { return new Atom($val); }
+function _atom_Q($atm) { return $atm instanceof Atom; }
?>
diff --git a/ps/Makefile b/ps/Makefile
index 231e8aa..fd5ca70 100644
--- a/ps/Makefile
+++ b/ps/Makefile
@@ -1,7 +1,7 @@
TESTS =
-SOURCES = types.ps reader.ps step8_macros.ps
+SOURCES = types.ps reader.ps printer.ps env.ps core.ps stepA_more.ps
.PHONY: stats tests $(TESTS)
diff --git a/ps/core.ps b/ps/core.ps
new file mode 100644
index 0000000..09bfe2b
--- /dev/null
+++ b/ps/core.ps
@@ -0,0 +1,126 @@
+(in core.ps\n) print
+
+% requires types.ps
+
+% Errors/Exceptions
+
+% data -> throw ->
+% Takes an arbitrary data and puts it in $error:/errorinfo. Then calls
+% stop to transfer control to end of nearest stopped context.
+/throw {
+ $error exch /errorinfo exch put
+ $error /command /throw put
+ stop
+} def
+
+
+% sequence functions
+
+% [obj list] -> cons -> new_list
+/cons {
+ /args exch def
+ /elem args 0 get def
+ /lst args 1 get def
+ lst length 1 add array
+ dup 0 elem put % first element
+ dup 1 lst putinterval % rest of the elements
+} def
+
+% [listA listB] -> concat -> [listA... listB...]
+/concat { % replaces matric concat
+ dup length 0 eq { %if just concat
+ 0 _list
+ }{ dup length 1 eq { %elseif concat of single item
+ 0 get % noop
+ }{ % else
+ [] exch
+ {
+ concatenate
+ } forall
+ } ifelse } ifelse
+} def
+
+% [obj ...] -> first -> obj
+/first {
+ 0 get _first
+} def
+
+% [obj objs...] -> first -> [objs..]
+/rest {
+ 0 get _rest
+} def
+
+% [function args... arg_list] -> apply -> result
+/apply { 1 dict begin
+ /args exch def
+ args 0 get callable % make sure function is callable
+ args 1 args length 2 sub getinterval
+ args args length 1 sub get
+ concatenate args 0 get % stack: args function
+ exec
+end } def
+
+% [function list] -> _map -> new_list
+/map { 1 dict begin
+ dup 0 get exch 1 get % stack: function list
+ /args exch def
+ callable % make sure function is callable
+ %/new_list args length array def
+ args {
+ 1 array astore
+ exch dup 3 1 roll % stack: fn arg fn
+ exec exch % stack: result fn
+ } forall
+ pop % remove the function
+ args length array astore
+end } def
+
+/conj { 5 dict begin
+ /args exch def
+ /src_list args 0 get def
+ /new_len src_list length args length 1 sub add def
+ /new_list new_len array def
+ new_list new_len src_list length sub src_list putinterval
+ args length 1 sub -1 1 {
+ /idx exch def
+ new_list args length idx sub 1 sub args idx get put
+ } for
+ new_list
+end } def
+
+
+% core_ns is namespace of core functions
+
+/core_ns <<
+ (pr-str) { ( ) true _pr_str_args }
+ (str) { () false _pr_str_args }
+ (prn) { ( ) true _pr_str_args print (\n) print null }
+ (println) { () false _pr_str_args print (\n) print null }
+ (=) { dup 0 get exch 1 get _equal? }
+ (symbol?) { 0 get _symbol? }
+ (nil?) { 0 get _nil? }
+ (true?) { 0 get _true? }
+ (false?) { 0 get _false? }
+ (<) { dup 0 get exch 1 get lt }
+ (<=) { dup 0 get exch 1 get le }
+ (>) { dup 0 get exch 1 get gt }
+ (>=) { dup 0 get exch 1 get ge }
+ (+) { dup 0 get exch 1 get add }
+ (-) { dup 0 get exch 1 get sub }
+ (*) { dup 0 get exch 1 get mul }
+ (/) { dup 0 get exch 1 get idiv }
+ (throw) { 0 get throw }
+ (list) { dup pop } % noop
+ (list?) { 0 get _list? }
+ (cons) { cons }
+ (concat) { concat }
+ (sequential?) { 0 get _sequential? }
+ (empty?) { 0 get length 0 eq }
+ (count) { 0 get length }
+ (nth) { dup 0 get exch 1 get _nth }
+ (first) { first }
+ (rest) { rest }
+ (apply) { apply }
+ (map) { map }
+ (conj) { conj }
+>> def
diff --git a/ps/printer.ps b/ps/printer.ps
new file mode 100644
index 0000000..c2e42a5
--- /dev/null
+++ b/ps/printer.ps
@@ -0,0 +1,61 @@
+(in types.ps\n) print
+
+% requires types.ps to be included first
+
+/_pr_str { 4 dict begin
+ /print_readably exch def
+ dup
+ /func? exch xcheck def % executable function
+ /obj exch cvlit def
+ obj _mal_function? { % if user defined function
+ (<\(fn* )
+ obj /params get print_readably _pr_str
+ ( )
+ obj /ast get print_readably _pr_str
+ (\)>)
+ concatenate concatenate concatenate concatenate
+ }{ /arraytype obj type eq { % if list or code block
+ % accumulate an array of strings
+ func? { (<builtin_fn* { ) }{ (\() } ifelse
+ obj ( ) print_readably _pr_str_args
+ concatenate
+ func? { ( } >) }{ (\)) } ifelse
+ concatenate
+ }{ /integertype obj type eq { % if number
+ /slen obj 10 add log ceiling cvi def
+ obj 10 slen string cvrs
+ }{ /stringtype obj type eq { % if string
+ print_readably {
+ (") obj (") concatenate concatenate
+ }{
+ obj
+ } ifelse
+ }{ null obj eq { % if nil
+ (nil)
+ }{ true obj eq { % if true
+ (true)
+ }{ false obj eq { % if false
+ (false)
+ }{ /nametype obj type eq { % if symbol
+ obj dup length string cvs
+ }{
+ (<unknown>)
+ } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse
+end } def
+
+% array delim print_readably -> _pr_str_args -> new_string
+/_pr_str_args { 3 dict begin
+ /print_readably exch def
+ /delim exch def
+ /args exch def
+ ()
+ args length 0 gt { %if any elements
+ [
+ args { %foreach argument in array
+ print_readably _pr_str
+ } forall
+ ]
+ { concatenate delim concatenate } forall
+ dup length delim length sub 0 exch getinterval % strip off final delim
+ } if
+end } def
diff --git a/ps/reader.ps b/ps/reader.ps
index dba2a4a..948bf3b 100644
--- a/ps/reader.ps
+++ b/ps/reader.ps
@@ -1,5 +1,6 @@
(in reader\n) print
+% requires types.ps to be included first
/token_delim (;,"` \n{}\(\)[]) def
/token_number (0123456789-) def
diff --git a/ps/step1_read_print.ps b/ps/step1_read_print.ps
index aa2ce25..b4c6275 100644
--- a/ps/step1_read_print.ps
+++ b/ps/step1_read_print.ps
@@ -1,5 +1,6 @@
(types.ps) run
(reader.ps) run
+(printer.ps) run
% read
/_readline { print flush (%stdin) (r) file 99 string readline } def
diff --git a/ps/step2_eval.ps b/ps/step2_eval.ps
index b353f8e..7b03a99 100644
--- a/ps/step2_eval.ps
+++ b/ps/step2_eval.ps
@@ -1,5 +1,6 @@
(types.ps) run
(reader.ps) run
+(printer.ps) run
% read
/_readline { print flush (%stdin) (r) file 99 string readline } def
diff --git a/ps/step3_env.ps b/ps/step3_env.ps
index a86e036..49d37c4 100644
--- a/ps/step3_env.ps
+++ b/ps/step3_env.ps
@@ -1,5 +1,7 @@
(types.ps) run
(reader.ps) run
+(printer.ps) run
+(env.ps) run
% read
/_readline { print flush (%stdin) (r) file 99 string readline } def
diff --git a/ps/step4_if_fn_do.ps b/ps/step4_if_fn_do.ps
index 11c475f..f703830 100644
--- a/ps/step4_if_fn_do.ps
+++ b/ps/step4_if_fn_do.ps
@@ -1,5 +1,8 @@
(types.ps) run
(reader.ps) run
+(printer.ps) run
+(env.ps) run
+(core.ps) run
% read
/_readline { print flush (%stdin) (r) file 99 string readline } def
@@ -113,7 +116,7 @@ end } def
/REP { READ repl_env EVAL PRINT } def
/_ref { repl_env 3 1 roll env_set pop } def
-types_ns { _ref } forall
+core_ns { _ref } forall
(\(def! not \(fn* \(a\) \(if a false true\)\)\)) RE pop
diff --git a/ps/step5_tco.ps b/ps/step5_tco.ps
index a501b06..2bc898a 100644
--- a/ps/step5_tco.ps
+++ b/ps/step5_tco.ps
@@ -1,5 +1,8 @@
(types.ps) run
(reader.ps) run
+(printer.ps) run
+(env.ps) run
+(core.ps) run
% read
/_readline { print flush (%stdin) (r) file 99 string readline } def
@@ -123,7 +126,7 @@ end } def
/REP { READ repl_env EVAL PRINT } def
/_ref { repl_env 3 1 roll env_set pop } def
-types_ns { _ref } forall
+core_ns { _ref } forall
(\(def! not \(fn* \(a\) \(if a false true\)\)\)) RE pop
diff --git a/ps/step6_file.ps b/ps/step6_file.ps
index b90bac5..f6f4377 100644
--- a/ps/step6_file.ps
+++ b/ps/step6_file.ps
@@ -1,5 +1,8 @@
(types.ps) run
(reader.ps) run
+(printer.ps) run
+(env.ps) run
+(core.ps) run
% read
/_readline { print flush (%stdin) (r) file 99 string readline } def
@@ -123,7 +126,7 @@ end } def
/REP { READ repl_env EVAL PRINT } def
/_ref { repl_env 3 1 roll env_set pop } def
-types_ns { _ref } forall
+core_ns { _ref } forall
(read-string) { 0 get read_str } _ref
(eval) { 0 get repl_env EVAL } _ref
diff --git a/ps/step7_quote.ps b/ps/step7_quote.ps
index 9ca3eb5..9858b4f 100644
--- a/ps/step7_quote.ps
+++ b/ps/step7_quote.ps
@@ -1,5 +1,8 @@
(types.ps) run
(reader.ps) run
+(printer.ps) run
+(env.ps) run
+(core.ps) run
% read
/_readline { print flush (%stdin) (r) file 99 string readline } def
@@ -155,7 +158,7 @@ end } def
/REP { READ repl_env EVAL PRINT } def
/_ref { repl_env 3 1 roll env_set pop } def
-types_ns { _ref } forall
+core_ns { _ref } forall
(read-string) { 0 get read_str } _ref
(eval) { 0 get repl_env EVAL } _ref
diff --git a/ps/step8_macros.ps b/ps/step8_macros.ps
index cfce140..869bf96 100644
--- a/ps/step8_macros.ps
+++ b/ps/step8_macros.ps
@@ -1,5 +1,8 @@
(types.ps) run
(reader.ps) run
+(printer.ps) run
+(env.ps) run
+(core.ps) run
% read
/_readline { print flush (%stdin) (r) file 99 string readline } def
@@ -198,7 +201,7 @@ end } def
/REP { READ repl_env EVAL PRINT } def
/_ref { repl_env 3 1 roll env_set pop } def
-types_ns { _ref } forall
+core_ns { _ref } forall
(read-string) { 0 get read_str } _ref
(eval) { 0 get repl_env EVAL } _ref
diff --git a/ps/step9_interop.ps b/ps/step9_interop.ps
index 743422b..f8d3250 100644
--- a/ps/step9_interop.ps
+++ b/ps/step9_interop.ps
@@ -1,5 +1,8 @@
(types.ps) run
(reader.ps) run
+(printer.ps) run
+(env.ps) run
+(core.ps) run
% read
/_readline { print flush (%stdin) (r) file 99 string readline } def
@@ -212,7 +215,7 @@ end } def
/REP { READ repl_env EVAL PRINT } def
/_ref { repl_env 3 1 roll env_set pop } def
-types_ns { _ref } forall
+core_ns { _ref } forall
(read-string) { 0 get read_str } _ref
(eval) { 0 get repl_env EVAL } _ref
diff --git a/ps/stepA_more.ps b/ps/stepA_more.ps
index 66494cc..a273c02 100644
--- a/ps/stepA_more.ps
+++ b/ps/stepA_more.ps
@@ -1,5 +1,8 @@
(types.ps) run
(reader.ps) run
+(printer.ps) run
+(env.ps) run
+(core.ps) run
% read
/_readline { print flush (%stdin) (r) file 99 string readline } def
@@ -254,7 +257,7 @@ end } def
/REP { READ repl_env EVAL PRINT } def
/_ref { repl_env 3 1 roll env_set pop } def
-types_ns { _ref } forall
+core_ns { _ref } forall
(readline) { 0 get _readline not { null } if } _ref
(read-string) { 0 get read_str } _ref
diff --git a/ps/types.ps b/ps/types.ps
index 30019b9..c696d35 100644
--- a/ps/types.ps
+++ b/ps/types.ps
@@ -1,5 +1,7 @@
(in types.ps\n) print
+% General functions
+
% concatenate: concatenate two strings or two arrays
% From Thinking in PostScript 1990 Reid
% (string1) (string2) concatenate string3
@@ -30,64 +32,6 @@
]
} bind def
-/_pr_str { 4 dict begin
- /print_readably exch def
- dup
- /func? exch xcheck def % executable function
- /obj exch cvlit def
- obj _mal_function? { % if user defined function
- (<\(fn* )
- obj /params get print_readably _pr_str
- ( )
- obj /ast get print_readably _pr_str
- (\)>)
- concatenate concatenate concatenate concatenate
- }{ /arraytype obj type eq { % if list or code block
- % accumulate an array of strings
- func? { (<builtin_fn* { ) }{ (\() } ifelse
- obj ( ) print_readably _pr_str_args
- concatenate
- func? { ( } >) }{ (\)) } ifelse
- concatenate
- }{ /integertype obj type eq { % if number
- /slen obj 10 add log ceiling cvi def
- obj 10 slen string cvrs
- }{ /stringtype obj type eq { % if string
- print_readably {
- (") obj (") concatenate concatenate
- }{
- obj
- } ifelse
- }{ null obj eq { % if nil
- (nil)
- }{ true obj eq { % if true
- (true)
- }{ false obj eq { % if false
- (false)
- }{ /nametype obj type eq { % if symbol
- obj dup length string cvs
- }{
- (<unknown>)
- } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse
-end } def
-
-% array delim print_readably -> _pr_str_args -> new_string
-/_pr_str_args { 3 dict begin
- /print_readably exch def
- /delim exch def
- /args exch def
- ()
- args length 0 gt { %if any elements
- [
- args { %foreach argument in array
- print_readably _pr_str
- } forall
- ]
- { concatenate delim concatenate } forall
- dup length delim length sub 0 exch getinterval % strip off final delim
- } if
-end } def
-
% objA objB -> _equal? -> bool
/_equal? { 6 dict begin
/b exch def
@@ -120,54 +64,22 @@ end } def
} ifelse
end } def
-/_nil? { null eq } def
-/_true? { true eq } def
-/_false? { false eq } def
-
+/_sequential? { _list? } def
-%
-% Symbols
-%
-/_symbol? {
- type /nametype eq
+/_first {
+ dup length 0 gt { 0 get }{ pop null } ifelse
} def
-
-%
-% Functions
-%
-/_mal_function? {
- dup type /dicttype eq {
- /type get /_maltype_function eq
+/_rest {
+ dup length 0 gt {
+ dup length 1 sub 1 exch getinterval
}{
- pop false
+ pop 0 array
} ifelse
} def
-% args mal_function -> fload -> ast new_env
-% fload: sets up arguments on the stack for an EVAL call
-/fload {
- dup /ast get 3 1 roll % stack: ast args mal_function
- dup /env get 3 1 roll % stack: ast env args mal_function
- /params get exch % stack: ast env params args
- env_new % stack: ast new_env
-} def
-% function_or_block -> callable -> block
-% if this is a user defined mal function, get its executable block
-/callable { dup _mal_function? { /data get } if } def
-%
% Errors/Exceptions
-%
-
-% data -> throw ->
-% Takes an arbitrary data and puts it in $error:/errorinfo. Then calls
-% stop to transfer control to end of nearest stopped context.
-/throw {
- $error exch /errorinfo exch put
- $error /command /throw put
- stop
-} def
/errorinfo? {
$error /errorinfo known { % if set
@@ -198,187 +110,51 @@ end } def
} def
+% Scalars
-%
-% list operations
-%
-/_list {
- array astore
-} def
-/_list? {
- dup xcheck not exch type /arraytype eq and
-} def
-/_nth { get } def
+/_nil? { null eq } def
+/_true? { true eq } def
+/_false? { false eq } def
-/_cons {
- /lst exch def
- /elem exch def
- lst length 1 add array
- dup 0 elem put % first element
- dup 1 lst putinterval % rest of the elements
-} def
-/concat { % replaces matric concat
- dup length 0 eq { %if just concat
- 0 _list
- }{ dup length 1 eq { %elseif concat of single item
- 0 get % noop
- }{ % else
- [] exch
- {
- concatenate
- } forall
- } ifelse } ifelse
-} def
+% Symbols
-%
-% Sequence operations
-%
-/_first {
- dup length 0 gt { 0 get }{ pop null } ifelse
-} def
-/_rest {
- dup length 0 gt {
- dup length 1 sub 1 exch getinterval
- }{
- pop 0 array
- } ifelse
+/_symbol? {
+ type /nametype eq
} def
-% [function args... arg_list] -> apply -> result
-/apply { 1 dict begin
- /args exch def
- args 0 get callable % make sure function is callable
- args 1 args length 2 sub getinterval
- args args length 1 sub get
- concatenate args 0 get % stack: args function
- exec
-end } def
-
-% function list -> _map -> new_list
-/_map { 1 dict begin
- /args exch def
- callable % make sure function is callable
- %/new_list args length array def
- args {
- 1 array astore
- exch dup 3 1 roll % stack: fn arg fn
- exec exch % stack: result fn
- } forall
- pop % remove the function
- args length array astore
-end } def
-/_sequential? { _list? } def
+% Functions
-/conj { 5 dict begin
- /args exch def
- /src_list args 0 get def
- /new_len src_list length args length 1 sub add def
- /new_list new_len array def
- new_list new_len src_list length sub src_list putinterval
- args length 1 sub -1 1 {
- /idx exch def
- new_list args length idx sub 1 sub args idx get put
- } for
- new_list
-end } def
+/_mal_function? {
+ dup type /dicttype eq {
+ /type get /_maltype_function eq
+ }{
+ pop false
+ } ifelse
+} def
+% args mal_function -> fload -> ast new_env
+% fload: sets up arguments on the stack for an EVAL call
+/fload {
+ dup /ast get 3 1 roll % stack: ast args mal_function
+ dup /env get 3 1 roll % stack: ast env args mal_function
+ /params get exch % stack: ast env params args
+ env_new % stack: ast new_env
+} def
-%
-% Env implementation
-%
-% outer binds exprs -> env_new -> new_env
-/env_new { 3 dict begin
- %(in env_new\n) print
- /exprs exch def
- /binds exch def
- /outer exch def
- <<
- /__outer__ outer
- 0 1 binds length 1 sub {
- /idx exch def
- binds idx get (&) eq { %if &
- binds idx 1 add get % key
- exprs idx exprs length idx sub getinterval % value
- exit
- } if
- binds idx get % key
- exprs idx get % value
- } for
- >>
-end } def
+% function_or_block -> callable -> block
+% if this is a user defined mal function, get its executable block
+/callable { dup _mal_function? { /data get } if } def
-/env_find { 2 dict begin
- /key exch def
- /env exch def
- env key known { %if key in env
- env
- }{ env /__outer__ get null ne { %elseif __outer__ not null
- env /__outer__ get key env_find
- }{ %else
- null
- } ifelse } ifelse
-end } def
-/env_set { 4 dict begin
- dup
- /func? exch xcheck def % executable function
- /val exch cvlit def
- /key exch def
- /env exch def
- env key val func? { cvx } if put
- val func? { cvx } if
-end } def
+% Lists
-/env_get { 2 dict begin
- /key exch def
- /env exch def
- env key env_find
- dup null eq {
- (')
- key 99 string cvs
- (' not found)
- concatenate concatenate
- throw
- }{
- key get
- } ifelse
-end } def
+/_list {
+ array astore
+} def
+/_list? {
+ dup xcheck not exch type /arraytype eq and
+} def
+/_nth { get } def
-%
-% types_ns is namespace of type functions
-%
-/types_ns <<
- (pr-str) { ( ) true _pr_str_args }
- (str) { () false _pr_str_args }
- (prn) { ( ) true _pr_str_args print (\n) print null }
- (println) { () false _pr_str_args print (\n) print null }
- (=) { dup 0 get exch 1 get _equal? }
- (symbol?) { 0 get _symbol? }
- (nil?) { 0 get _nil? }
- (true?) { 0 get _true? }
- (false?) { 0 get _false? }
- (<) { dup 0 get exch 1 get lt }
- (<=) { dup 0 get exch 1 get le }
- (>) { dup 0 get exch 1 get gt }
- (>=) { dup 0 get exch 1 get ge }
- (+) { dup 0 get exch 1 get add }
- (-) { dup 0 get exch 1 get sub }
- (*) { dup 0 get exch 1 get mul }
- (/) { dup 0 get exch 1 get idiv }
- (throw) { 0 get throw }
- (list) { dup pop } % noop
- (list?) { 0 get _list? }
- (cons) { dup 0 get exch 1 get _cons }
- (concat) { concat }
- (sequential?) { 0 get _sequential? }
- (empty?) { 0 get length 0 eq }
- (count) { 0 get length }
- (nth) { dup 0 get exch 1 get _nth }
- (first) { 0 get _first }
- (rest) { 0 get _rest }
- (apply) { apply }
- (map) { dup 0 get exch 1 get _map }
- (conj) { conj }
->> def
diff --git a/python/Makefile b/python/Makefile
index 1c8e467..3985d14 100644
--- a/python/Makefile
+++ b/python/Makefile
@@ -2,7 +2,8 @@
TESTS =
-SOURCES = mal_types.py mal_readline.py reader.py stepA_more.py
+SOURCES = mal_readline.py mal_types.py reader.py printer.py \
+ env.py core.py stepA_more.py
#all: mal.sh
#
diff --git a/python/core.py b/python/core.py
new file mode 100644
index 0000000..92ae3d9
--- /dev/null
+++ b/python/core.py
@@ -0,0 +1,159 @@
+import copy
+from itertools import chain
+
+import mal_types as types
+from mal_types import List, Vector
+import printer
+
+# Errors/Exceptions
+def throw(exc): raise Exception(exc)
+
+
+# String functions
+def pr_str(*args):
+ return " ".join(map(lambda exp: printer._pr_str(exp, True), args))
+
+def do_str(*args):
+ return "".join(map(lambda exp: printer._pr_str(exp, False), args))
+
+def prn(*args):
+ print " ".join(map(lambda exp: printer._pr_str(exp, True), args))
+ return None
+
+def println(*args):
+ line = " ".join(map(lambda exp: printer._pr_str(exp, False), args))
+ print line.replace('\\n', '\n')
+ return None
+
+
+# Hash map functions
+def assoc(src_hm, *key_vals):
+ hm = copy.copy(src_hm)
+ for i in range(0,len(key_vals),2): hm[key_vals[i]] = key_vals[i+1]
+ return hm
+
+def dissoc(src_hm, *keys):
+ hm = copy.copy(src_hm)
+ for key in keys: del hm[key]
+ return hm
+
+def get(hm, key):
+ if key in hm:
+ return hm[key]
+ else:
+ return None
+
+def contains_Q(hm, key): return key in hm
+
+def keys(hm): return types._list(*hm.keys())
+
+def vals(hm): return types._list(*hm.values())
+
+
+# Sequence functions
+def coll_Q(coll): return sequential_Q(coll) or hash_map_Q(coll)
+
+def cons(x, seq): return List([x]) + List(seq)
+
+def concat(*lsts): return List(chain(*lsts))
+
+def nth(lst, idx): return lst[idx]
+
+def first(lst): return lst[0]
+
+def rest(lst): return List(lst[1:])
+
+def empty_Q(lst): return len(lst) == 0
+
+def count(lst): return len(lst)
+
+# retains metadata
+def conj(lst, *args):
+ if types._list_Q(lst):
+ new_lst = List(list(reversed(list(args))) + lst)
+ else:
+ new_lst = Vector(lst + list(args))
+ if hasattr(lst, "__meta__"):
+ new_lst.__meta__ = lst.__meta__
+ return new_lst
+
+def apply(f, *args): return f(*(list(args[0:-1])+args[-1]))
+
+def mapf(f, lst): return List(map(f, lst))
+
+
+# Metadata functions
+def with_meta(obj, meta):
+ new_obj = copy.copy(obj)
+ new_obj.__meta__ = meta
+ return new_obj
+
+def meta(obj):
+ if hasattr(obj, "__meta__"): return obj.__meta__
+ else: return None
+
+
+# Atoms functions
+def deref(atm): return atm.val
+def reset_BANG(atm,val):
+ atm.val = val
+ return atm.val
+def swap_BANG(atm,f,*args):
+ atm.val = f(atm.val,*args)
+ return atm.val
+
+
+ns = {
+ '=': types._equal_Q,
+ 'throw': throw,
+ 'nil?': types._nil_Q,
+ 'true?': types._true_Q,
+ 'false?': types._false_Q,
+ 'symbol': types._symbol,
+ 'symbol?': types._symbol_Q,
+ 'pr-str': pr_str,
+ 'str': do_str,
+ 'prn': prn,
+ 'println': println,
+ '<': lambda a,b: a<b,
+ '<=': lambda a,b: a<=b,
+ '>': lambda a,b: a>b,
+ '>=': lambda a,b: a>=b,
+ '+': lambda a,b: a+b,
+ '-': lambda a,b: a-b,
+ '*': lambda a,b: a*b,
+ '/': lambda a,b: a/b,
+
+ 'list': types._list,
+ 'list?': types._list_Q,
+ 'vector': types._vector,
+ 'vector?': types._vector_Q,
+ 'hash-map': types._hash_map,
+ 'map?': types._hash_map_Q,
+ 'assoc': assoc,
+ 'dissoc': dissoc,
+ 'get': get,
+ 'contains?': contains_Q,
+ 'keys': keys,
+ 'vals': vals,
+
+ 'sequential?': types._sequential_Q,
+ 'cons': cons,
+ 'concat': concat,
+ 'nth': nth,
+ 'first': first,
+ 'rest': rest,
+ 'empty?': empty_Q,
+ 'count': count,
+ 'conj': conj,
+ 'apply': apply,
+ 'map': mapf,
+
+ 'with-meta': with_meta,
+ 'meta': meta,
+ 'atom': types._atom,
+ 'atom?': types._atom_Q,
+ 'deref': deref,
+ 'reset!': reset_BANG,
+ 'swap!': swap_BANG}
+
diff --git a/python/env.py b/python/env.py
new file mode 100644
index 0000000..4cd8e05
--- /dev/null
+++ b/python/env.py
@@ -0,0 +1,28 @@
+# Environment
+
+class Env():
+ def __init__(self, outer=None, binds=None, exprs=None):
+ self.data = {}
+ self.outer = outer or None
+
+ if binds:
+ for i in range(len(binds)):
+ if binds[i] == "&":
+ self.data[binds[i+1]] = exprs[i:]
+ break
+ else:
+ self.data[binds[i]] = exprs[i]
+
+ def find(self, key):
+ if key in self.data: return self
+ elif self.outer: return self.outer.find(key)
+ else: return None
+
+ def set(self, key, value):
+ self.data[key] = value
+ return value
+
+ def get(self, key):
+ env = self.find(key)
+ if not env: raise Exception("'" + key + "' not found")
+ return env.data[key]
diff --git a/python/mal_types.py b/python/mal_types.py
index 401a03b..15e4b6b 100644
--- a/python/mal_types.py
+++ b/python/mal_types.py
@@ -1,71 +1,17 @@
-import copy
-from itertools import chain
-
# General functions
-def _pr_str(obj, print_readably=True):
- _r = print_readably
- if list_Q(obj):
- return "(" + " ".join(map(lambda e: _pr_str(e,_r), obj)) + ")"
- elif vector_Q(obj):
- return "[" + " ".join(map(lambda e: _pr_str(e,_r), obj)) + "]"
- elif hash_map_Q(obj):
- ret = []
- for k in obj.keys():
- ret.extend((_pr_str(k), _pr_str(obj[k],_r)))
- return "{" + " ".join(ret) + "}"
- elif string_Q(obj):
- if print_readably:
- return '"' + obj.encode('unicode_escape').replace('"', '\\"') + '"'
- else:
- return obj
- elif nil_Q(obj):
- return "nil"
- elif true_Q(obj):
- return "true"
- elif false_Q(obj):
- return "false"
- elif atom_Q(obj):
- return "(atom " + _pr_str(obj.val,_r) + ")"
- else:
- return obj.__str__()
-
-def pr_str(*args):
- return " ".join(map(lambda exp: _pr_str(exp, True), args))
-
-def do_str(*args):
- return "".join(map(lambda exp: _pr_str(exp, False), args))
-
-def prn(*args):
- print " ".join(map(lambda exp: _pr_str(exp, True), args))
- return None
-
-def println(*args):
- line = " ".join(map(lambda exp: _pr_str(exp, False), args))
- print line.replace('\\n', '\n')
- return None
-
-def with_meta(obj, meta):
- new_obj = copy.copy(obj)
- new_obj.__meta__ = meta
- return new_obj
-
-def meta(obj):
- if hasattr(obj, "__meta__"): return obj.__meta__
- else: return None
-
-def equal_Q(a, b):
+def _equal_Q(a, b):
ota, otb = type(a), type(b)
- if not (ota == otb or (sequential_Q(a) and sequential_Q(b))):
+ if not (ota == otb or (_sequential_Q(a) and _sequential_Q(b))):
return False;
- if symbol_Q(a):
+ if _symbol_Q(a):
return a == b
- elif list_Q(a) or vector_Q(a):
+ elif _list_Q(a) or _vector_Q(a):
if len(a) != len(b): return False
for i in range(len(a)):
- if not equal_Q(a[i], b[i]): return False
+ if not _equal_Q(a[i], b[i]): return False
return True
- elif hash_map_Q(a):
+ elif _hash_map_Q(a):
akeys = a.keys()
akeys.sort()
bkeys = b.keys()
@@ -78,70 +24,26 @@ def equal_Q(a, b):
else:
return a == b
-# nil, true, false
-def nil_Q(exp): return exp is None
-def true_Q(exp): return exp is True
-def false_Q(exp): return exp is False
-def string_Q(exp): return type(exp) in [str, unicode]
+def _sequential_Q(seq): return _list_Q(seq) or _vector_Q(seq)
-# numbers
-int_plus = lambda a,b: a+b
-int_minus = lambda a,b: a-b
-int_multiply = lambda a,b: a*b
-int_divide = lambda a,b: a/b
-int_lt = lambda a,b: a<b
-int_lte = lambda a,b: a<=b
-int_gt = lambda a,b: a>b
-int_gte = lambda a,b: a>=b
+# Scalars
+def _nil_Q(exp): return exp is None
+def _true_Q(exp): return exp is True
+def _false_Q(exp): return exp is False
+def _string_Q(exp): return type(exp) in [str, unicode]
-# symbols
+# Symbols
class Symbol(str): pass
-def new_symbol(str): return Symbol(str)
-def symbol_Q(exp): return type(exp) == Symbol
+def _symbol(str): return Symbol(str)
+def _symbol_Q(exp): return type(exp) == Symbol
-
-# functions
-def new_function(func, exp, env, params):
+# Functions
+def _function(Eval, Env, exp, env, params):
def f(*args):
- return func(exp, Env(env, params, args))
+ return Eval(exp, Env(env, params, args))
f.__meta__ = {"exp": exp, "env": env, "params": params}
return f
-def function_Q(f): return type(f) == type(function_Q)
-
-# hash maps
-class Hash_Map(dict): pass
-def new_hash_map(*key_vals):
- hm = Hash_Map()
- for i in range(0,len(key_vals),2): hm[key_vals[i]] = key_vals[i+1]
- return hm
-def hash_map_Q(exp): return type(exp) == Hash_Map
-
-def assoc(src_hm, *key_vals):
- hm = copy.copy(src_hm)
- for i in range(0,len(key_vals),2): hm[key_vals[i]] = key_vals[i+1]
- return hm
-
-def dissoc(src_hm, *keys):
- hm = copy.copy(src_hm)
- for key in keys: del hm[key]
- return hm
-
-def get(hm, key):
- if key in hm:
- return hm[key]
- else:
- return None
-
-def contains_Q(hm, key): return key in hm
-
-def keys(hm): return new_list(*hm.keys())
-
-def vals(hm): return new_list(*hm.values())
-
-
-# errors/exceptions
-def throw(exc): raise Exception(exc)
-
+def _function_Q(f): return type(f) == type(function_Q)
# lists
class List(list):
@@ -151,8 +53,8 @@ class List(list):
elif i >= len(self): return None
else: return list.__getitem__(self, i)
def __getslice__(self, *a): return List(list.__getslice__(self, *a))
-def new_list(*vals): return List(vals)
-def list_Q(exp): return type(exp) == List
+def _list(*vals): return List(vals)
+def _list_Q(exp): return type(exp) == List
# vectors
@@ -163,109 +65,20 @@ class Vector(list):
elif i >= len(self): return None
else: return list.__getitem__(self, i)
def __getslice__(self, *a): return Vector(list.__getslice__(self, *a))
-def new_vector(*vals): return Vector(vals)
-def vector_Q(exp): return type(exp) == Vector
+def _vector(*vals): return Vector(vals)
+def _vector_Q(exp): return type(exp) == Vector
+# Hash maps
+class Hash_Map(dict): pass
+def _hash_map(*key_vals):
+ hm = Hash_Map()
+ for i in range(0,len(key_vals),2): hm[key_vals[i]] = key_vals[i+1]
+ return hm
+def _hash_map_Q(exp): return type(exp) == Hash_Map
# atoms
class Atom(object):
def __init__(self, val):
self.val = val
-def new_atom(val): return Atom(val)
-def atom_Q(exp): return type(exp) == Atom
-def deref(atm): return atm.val
-def reset_BANG(atm,val):
- atm.val = val
- return atm.val
-def swap_BANG(atm,f,*args):
- atm.val = f(atm.val,*args)
- return atm.val
-
-
-
-# Sequence operations
-def sequential_Q(seq): return list_Q(seq) or vector_Q(seq)
-
-def coll_Q(coll): return sequential_Q(coll) or hash_map_Q(coll)
-
-def cons(x, seq): return List([x]) + List(seq)
-
-def nth(lst, idx): return lst[idx]
-
-def count(lst): return len(lst)
-
-def empty_Q(lst): return len(lst) == 0
-
-def concat(*lsts): return List(chain(*lsts))
-
-# retains metadata
-def conj(lst, *args):
- if list_Q(lst):
- new_lst = List(list(reversed(list(args))) + lst)
- else:
- new_lst = Vector(lst + list(args))
- if hasattr(lst, "__meta__"):
- new_lst.__meta__ = lst.__meta__
- return new_lst
-
-def first(lst): return lst[0]
-
-def rest(lst): return List(lst[1:])
-
-def apply(f, *args):
- return f(*(list(args[0:-1])+args[-1]))
-
-def mapf(f, lst):
- return List(map(f, lst))
-
-
-# Environment
-
-class Env():
- def __init__(self, outer=None, binds=None, exprs=None):
- self.data = {}
- self.outer = outer or None
-
- if binds:
- for i in range(len(binds)):
- if binds[i] == "&":
- self.data[binds[i+1]] = exprs[i:]
- break
- else:
- self.data[binds[i]] = exprs[i]
-
- def find(self, key):
- if key in self.data: return self
- elif self.outer: return self.outer.find(key)
- else: return None
-
- def set(self, key, value):
- self.data[key] = value
- return value
-
- def get(self, key):
- env = self.find(key)
- if not env: raise Exception("'" + key + "' not found")
- return env.data[key]
-
-types_ns = {
- 'pr-str': pr_str, 'str': do_str, 'prn': prn, 'println': println,
- 'with-meta': with_meta, 'meta': meta,
- '=': equal_Q,
- 'nil?': nil_Q, 'true?': true_Q, 'false?': false_Q,
- 'symbol?': symbol_Q,
- '<': int_lt, '<=': int_lte, '>': int_gt, '>=': int_gte,
- '+': int_plus, '-': int_minus, '*': int_multiply, '/': int_divide,
- 'hash-map': new_hash_map, 'map?': hash_map_Q,
- 'assoc': assoc, 'dissoc': dissoc, 'get': get,
- 'contains?': contains_Q, 'keys': keys, 'vals': vals,
- 'throw': throw,
- 'list': new_list, 'list?': list_Q,
- 'vector': new_vector, 'vector?': vector_Q,
- 'atom': new_atom, 'atom?': atom_Q, 'deref': deref,
- 'reset!': reset_BANG, 'swap!': swap_BANG,
- 'sequential?': sequential_Q,
- 'cons': cons, 'nth': nth, 'count': count, 'empty?': empty_Q,
- 'concat': concat, "conj": conj, "first": first, "rest": rest,
- 'apply': apply, 'map': mapf}
-
+def _atom(val): return Atom(val)
+def _atom_Q(exp): return type(exp) == Atom
diff --git a/python/printer.py b/python/printer.py
new file mode 100644
index 0000000..d501d60
--- /dev/null
+++ b/python/printer.py
@@ -0,0 +1,29 @@
+import mal_types as types
+
+def _pr_str(obj, print_readably=True):
+ _r = print_readably
+ if types._list_Q(obj):
+ return "(" + " ".join(map(lambda e: _pr_str(e,_r), obj)) + ")"
+ elif types._vector_Q(obj):
+ return "[" + " ".join(map(lambda e: _pr_str(e,_r), obj)) + "]"
+ elif types._hash_map_Q(obj):
+ ret = []
+ for k in obj.keys():
+ ret.extend((_pr_str(k), _pr_str(obj[k],_r)))
+ return "{" + " ".join(ret) + "}"
+ elif types._string_Q(obj):
+ if print_readably:
+ return '"' + obj.encode('unicode_escape').replace('"', '\\"') + '"'
+ else:
+ return obj
+ elif types._nil_Q(obj):
+ return "nil"
+ elif types._true_Q(obj):
+ return "true"
+ elif types._false_Q(obj):
+ return "false"
+ elif types._atom_Q(obj):
+ return "(atom " + _pr_str(obj.val,_r) + ")"
+ else:
+ return obj.__str__()
+
diff --git a/python/reader.py b/python/reader.py
index ddd6a32..846e2a8 100644
--- a/python/reader.py
+++ b/python/reader.py
@@ -1,5 +1,5 @@
import re
-from mal_types import (new_symbol, Symbol, new_hash_map, List, new_list, Vector)
+from mal_types import (_symbol, _list, _vector, _hash_map)
class Blank(Exception): pass
@@ -32,7 +32,7 @@ def read_atom(reader):
elif token == "nil": return None
elif token == "true": return True
elif token == "false": return False
- else: return Symbol(token)
+ else: return _symbol(token)
def read_sequence(reader, typ=list, start='(', end=')'):
ast = typ()
@@ -49,13 +49,13 @@ def read_sequence(reader, typ=list, start='(', end=')'):
def read_hash_map(reader):
lst = read_sequence(reader, list, '{', '}')
- return new_hash_map(*lst)
+ return _hash_map(*lst)
def read_list(reader):
- return read_sequence(reader, List, '(', ')')
+ return read_sequence(reader, _list, '(', ')')
def read_vector(reader):
- return read_sequence(reader, Vector, '[', ']')
+ return read_sequence(reader, _vector, '[', ']')
def read_form(reader):
token = reader.peek()
@@ -65,23 +65,23 @@ def read_form(reader):
return None
elif token == '\'':
reader.next()
- return new_list(Symbol('quote'), read_form(reader))
+ return _list(_symbol('quote'), read_form(reader))
elif token == '`':
reader.next()
- return new_list(Symbol('quasiquote'), read_form(reader))
+ return _list(_symbol('quasiquote'), read_form(reader))
elif token == '~':
reader.next()
- return new_list(Symbol('unquote'), read_form(reader))
+ return _list(_symbol('unquote'), read_form(reader))
elif token == '~@':
reader.next()
- return new_list(Symbol('splice-unquote'), read_form(reader))
+ return _list(_symbol('splice-unquote'), read_form(reader))
elif token == '^':
reader.next()
meta = read_form(reader)
- return new_list(Symbol('with-meta'), read_form(reader), meta)
+ return _list(_symbol('with-meta'), read_form(reader), meta)
elif token == '@':
reader.next()
- return new_list(Symbol('deref'), read_form(reader))
+ return _list(_symbol('deref'), read_form(reader))
# list
elif token == ')': raise Exception("unexpected ')'")
diff --git a/python/step1_read_print.py b/python/step1_read_print.py
index 165dfa3..0315cf0 100644
--- a/python/step1_read_print.py
+++ b/python/step1_read_print.py
@@ -1,21 +1,19 @@
import sys, traceback
import mal_readline
-from mal_types import (pr_str, sequential_Q, symbol_Q, coll_Q, list_Q,
- vector_Q, hash_map_Q, new_symbol, new_function,
- new_list, new_vector, new_hash_map, Env, types_ns)
-from reader import (read_str, Blank)
+import mal_types as types
+import reader, printer
# read
def READ(str):
- return read_str(str)
+ return reader.read_str(str)
# eval
def EVAL(ast, env):
- #print("EVAL %s" % ast)
- return ast
+ #print("EVAL %s" % ast)
+ return ast
def PRINT(exp):
- return pr_str(exp)
+ return printer._pr_str(exp)
# repl
def REP(str):
@@ -27,6 +25,6 @@ while True:
if line == None: break
if line == "": continue
print(REP(line))
- except Blank: continue
+ except reader.Blank: continue
except Exception as e:
print "".join(traceback.format_exception(*sys.exc_info()))
diff --git a/python/step2_eval.py b/python/step2_eval.py
index 65972a2..e50d231 100644
--- a/python/step2_eval.py
+++ b/python/step2_eval.py
@@ -1,46 +1,44 @@
import sys, traceback
import mal_readline
-from mal_types import (pr_str, sequential_Q, symbol_Q, coll_Q, list_Q,
- vector_Q, hash_map_Q, new_symbol, new_function,
- new_list, new_vector, new_hash_map, Env, types_ns)
-from reader import (read_str, Blank)
+import mal_types as types
+import reader, printer
# read
def READ(str):
- return read_str(str)
+ return reader.read_str(str)
# eval
def eval_ast(ast, env):
- if symbol_Q(ast):
+ if types._symbol_Q(ast):
try:
return env[ast]
except:
raise Exception("'" + ast + "' not found")
- elif list_Q(ast):
- return new_list(*map(lambda a: EVAL(a, env), ast))
- elif vector_Q(ast):
- return new_vector(*map(lambda a: EVAL(a, env), ast))
- elif hash_map_Q(ast):
+ elif types._list_Q(ast):
+ return types._list(*map(lambda a: EVAL(a, env), ast))
+ elif types._vector_Q(ast):
+ return types._vector(*map(lambda a: EVAL(a, env), ast))
+ elif types._hash_map_Q(ast):
keyvals = []
for k in ast.keys():
keyvals.append(EVAL(k, env))
keyvals.append(EVAL(ast[k], env))
- return new_hash_map(*keyvals)
+ return types._hash_map(*keyvals)
else:
return ast # primitive value, return unchanged
def EVAL(ast, env):
- #print("EVAL %s" % ast)
- if not list_Q(ast):
- return eval_ast(ast, env)
+ #print("EVAL %s" % ast)
+ if not types._list_Q(ast):
+ return eval_ast(ast, env)
- # apply list
- el = eval_ast(ast, env)
- f = el[0]
- return f(*el[1:])
+ # apply list
+ el = eval_ast(ast, env)
+ f = el[0]
+ return f(*el[1:])
def PRINT(exp):
- return pr_str(exp)
+ return printer._pr_str(exp)
# repl
repl_env = {}
@@ -58,6 +56,6 @@ while True:
if line == None: break
if line == "": continue
print(REP(line))
- except Blank: continue
+ except reader.Blank: continue
except Exception as e:
print "".join(traceback.format_exception(*sys.exc_info()))
diff --git a/python/step3_env.py b/python/step3_env.py
index f95a978..21879d6 100644
--- a/python/step3_env.py
+++ b/python/step3_env.py
@@ -1,58 +1,57 @@
import sys, traceback
import mal_readline
-from mal_types import (pr_str, sequential_Q, symbol_Q, coll_Q, list_Q,
- vector_Q, hash_map_Q, new_symbol, new_function,
- new_list, new_vector, new_hash_map, Env, types_ns)
-from reader import (read_str, Blank)
+import mal_types as types
+import reader, printer
+from env import Env
# read
def READ(str):
- return read_str(str)
+ return reader.read_str(str)
# eval
def eval_ast(ast, env):
- if symbol_Q(ast):
+ if types._symbol_Q(ast):
return env.get(ast)
- elif list_Q(ast):
- return new_list(*map(lambda a: EVAL(a, env), ast))
- elif vector_Q(ast):
- return new_vector(*map(lambda a: EVAL(a, env), ast))
- elif hash_map_Q(ast):
+ elif types._list_Q(ast):
+ return types._list(*map(lambda a: EVAL(a, env), ast))
+ elif types._vector_Q(ast):
+ return types._vector(*map(lambda a: EVAL(a, env), ast))
+ elif types._hash_map_Q(ast):
keyvals = []
for k in ast.keys():
keyvals.append(EVAL(k, env))
keyvals.append(EVAL(ast[k], env))
- return new_hash_map(*keyvals)
+ return types._hash_map(*keyvals)
else:
return ast # primitive value, return unchanged
def EVAL(ast, env):
- #print("EVAL %s" % ast)
- if not list_Q(ast):
- return eval_ast(ast, env)
+ #print("EVAL %s" % ast)
+ if not types._list_Q(ast):
+ return eval_ast(ast, env)
- # apply list
- if len(ast) == 0: return ast
- a0 = ast[0]
+ # apply list
+ if len(ast) == 0: return ast
+ a0 = ast[0]
- if "def!" == a0:
- a1, a2 = ast[1], ast[2]
- res = EVAL(a2, env)
- return env.set(a1, res)
- elif "let*" == a0:
- a1, a2 = ast[1], ast[2]
- let_env = Env(env)
- for i in range(0, len(a1), 2):
- let_env.set(a1[i], EVAL(a1[i+1], let_env))
- return EVAL(a2, let_env)
- else:
- el = eval_ast(ast, env)
- f = el[0]
- return f(*el[1:])
+ if "def!" == a0:
+ a1, a2 = ast[1], ast[2]
+ res = EVAL(a2, env)
+ return env.set(a1, res)
+ elif "let*" == a0:
+ a1, a2 = ast[1], ast[2]
+ let_env = Env(env)
+ for i in range(0, len(a1), 2):
+ let_env.set(a1[i], EVAL(a1[i+1], let_env))
+ return EVAL(a2, let_env)
+ else:
+ el = eval_ast(ast, env)
+ f = el[0]
+ return f(*el[1:])
# print
def PRINT(exp):
- return pr_str(exp)
+ return printer._pr_str(exp)
# repl
repl_env = Env()
@@ -71,6 +70,6 @@ while True:
if line == None: break
if line == "": continue
print(REP(line))
- except Blank: continue
+ except reader.Blank: continue
except Exception as e:
print "".join(traceback.format_exception(*sys.exc_info()))
diff --git a/python/step4_if_fn_do.py b/python/step4_if_fn_do.py
index 4b54d8f..d0a7fc3 100644
--- a/python/step4_if_fn_do.py
+++ b/python/step4_if_fn_do.py
@@ -1,72 +1,72 @@
import sys, traceback
import mal_readline
-from mal_types import (pr_str, sequential_Q, symbol_Q, coll_Q, list_Q,
- vector_Q, hash_map_Q, new_symbol, new_function,
- new_list, new_vector, new_hash_map, Env, types_ns)
-from reader import (read_str, Blank)
+import mal_types as types
+import reader, printer
+from env import Env
+import core
# read
def READ(str):
- return read_str(str)
+ return reader.read_str(str)
# eval
def eval_ast(ast, env):
- if symbol_Q(ast):
+ if types._symbol_Q(ast):
return env.get(ast)
- elif list_Q(ast):
- return new_list(*map(lambda a: EVAL(a, env), ast))
- elif vector_Q(ast):
- return new_vector(*map(lambda a: EVAL(a, env), ast))
- elif hash_map_Q(ast):
+ elif types._list_Q(ast):
+ return types._list(*map(lambda a: EVAL(a, env), ast))
+ elif types._vector_Q(ast):
+ return types._vector(*map(lambda a: EVAL(a, env), ast))
+ elif types._hash_map_Q(ast):
keyvals = []
for k in ast.keys():
keyvals.append(EVAL(k, env))
keyvals.append(EVAL(ast[k], env))
- return new_hash_map(*keyvals)
+ return types._hash_map(*keyvals)
else:
return ast # primitive value, return unchanged
def EVAL(ast, env):
- #print("EVAL %s" % ast)
- if not list_Q(ast):
- return eval_ast(ast, env)
+ #print("EVAL %s" % ast)
+ if not types._list_Q(ast):
+ return eval_ast(ast, env)
- # apply list
- if len(ast) == 0: return ast
- a0 = ast[0]
+ # apply list
+ if len(ast) == 0: return ast
+ a0 = ast[0]
- if "def!" == a0:
- a1, a2 = ast[1], ast[2]
- res = EVAL(a2, env)
- return env.set(a1, res)
- elif "let*" == a0:
- a1, a2 = ast[1], ast[2]
- let_env = Env(env)
- for i in range(0, len(a1), 2):
- let_env.set(a1[i], EVAL(a1[i+1], let_env))
- return EVAL(a2, let_env)
- elif "do" == a0:
- el = eval_ast(ast[1:], env)
- return el[-1]
- elif "if" == a0:
- a1, a2 = ast[1], ast[2]
- cond = EVAL(a1, env)
- if cond is None or cond is False:
- if len(ast) > 3: return EVAL(ast[3], env)
- else: return None
+ if "def!" == a0:
+ a1, a2 = ast[1], ast[2]
+ res = EVAL(a2, env)
+ return env.set(a1, res)
+ elif "let*" == a0:
+ a1, a2 = ast[1], ast[2]
+ let_env = Env(env)
+ for i in range(0, len(a1), 2):
+ let_env.set(a1[i], EVAL(a1[i+1], let_env))
+ return EVAL(a2, let_env)
+ elif "do" == a0:
+ el = eval_ast(ast[1:], env)
+ return el[-1]
+ elif "if" == a0:
+ a1, a2 = ast[1], ast[2]
+ cond = EVAL(a1, env)
+ if cond is None or cond is False:
+ if len(ast) > 3: return EVAL(ast[3], env)
+ else: return None
+ else:
+ return EVAL(a2, env)
+ elif "fn*" == a0:
+ a1, a2 = ast[1], ast[2]
+ return types._function(EVAL, Env, a2, env, a1)
else:
- return EVAL(a2, env)
- elif "fn*" == a0:
- a1, a2 = ast[1], ast[2]
- return new_function(EVAL, a2, env, a1)
- else:
- el = eval_ast(ast, env)
- f = el[0]
- return f(*el[1:])
+ el = eval_ast(ast, env)
+ f = el[0]
+ return f(*el[1:])
# print
def PRINT(exp):
- return pr_str(exp)
+ return printer._pr_str(exp)
# repl
repl_env = Env()
@@ -75,7 +75,7 @@ def REP(str):
def _ref(k,v): repl_env.set(k, v)
# Import types functions
-for name, val in types_ns.items(): _ref(name, val)
+for name, val in core.ns.items(): _ref(name, val)
# Defined using the language itself
REP("(def! not (fn* (a) (if a false true)))")
@@ -86,6 +86,6 @@ while True:
if line == None: break
if line == "": continue
print(REP(line))
- except Blank: continue
+ except reader.Blank: continue
except Exception as e:
print "".join(traceback.format_exception(*sys.exc_info()))
diff --git a/python/step5_tco.py b/python/step5_tco.py
index ffde863..4335a96 100644
--- a/python/step5_tco.py
+++ b/python/step5_tco.py
@@ -1,41 +1,41 @@
import sys, traceback
import mal_readline
-from mal_types import (pr_str, sequential_Q, symbol_Q, coll_Q, list_Q,
- vector_Q, hash_map_Q, new_symbol, new_function,
- new_list, new_vector, new_hash_map, Env, types_ns)
-from reader import (read_str, Blank)
+import mal_types as types
+import reader, printer
+from env import Env
+import core
# read
def READ(str):
- return read_str(str)
+ return reader.read_str(str)
# eval
def eval_ast(ast, env):
- if symbol_Q(ast):
+ if types._symbol_Q(ast):
return env.get(ast)
- elif list_Q(ast):
- return new_list(*map(lambda a: EVAL(a, env), ast))
- elif vector_Q(ast):
- return new_vector(*map(lambda a: EVAL(a, env), ast))
- elif hash_map_Q(ast):
+ elif types._list_Q(ast):
+ return types._list(*map(lambda a: EVAL(a, env), ast))
+ elif types._vector_Q(ast):
+ return types._vector(*map(lambda a: EVAL(a, env), ast))
+ elif types._hash_map_Q(ast):
keyvals = []
for k in ast.keys():
keyvals.append(EVAL(k, env))
keyvals.append(EVAL(ast[k], env))
- return new_hash_map(*keyvals)
+ return types._hash_map(*keyvals)
else:
return ast # primitive value, return unchanged
def EVAL(ast, env):
while True:
#print("EVAL %s" % ast)
- if not list_Q(ast):
+ if not types._list_Q(ast):
return eval_ast(ast, env)
-
+
# apply list
if len(ast) == 0: return ast
- a0 = ast[0]
-
+ a0 = ast[0]
+
if "def!" == a0:
a1, a2 = ast[1], ast[2]
res = EVAL(a2, env)
@@ -61,7 +61,7 @@ def EVAL(ast, env):
# Continue loop (TCO)
elif "fn*" == a0:
a1, a2 = ast[1], ast[2]
- return new_function(EVAL, a2, env, a1)
+ return types._function(EVAL, Env, a2, env, a1)
else:
el = eval_ast(ast, env)
f = el[0]
@@ -74,7 +74,7 @@ def EVAL(ast, env):
# print
def PRINT(exp):
- return pr_str(exp)
+ return printer._pr_str(exp)
# repl
repl_env = Env()
@@ -83,7 +83,7 @@ def REP(str):
def _ref(k,v): repl_env.set(k, v)
# Import types functions
-for name, val in types_ns.items(): _ref(name, val)
+for name, val in core.ns.items(): _ref(name, val)
# Defined using the language itself
REP("(def! not (fn* (a) (if a false true)))")
@@ -94,6 +94,6 @@ while True:
if line == None: break
if line == "": continue
print(REP(line))
- except Blank: continue
+ except reader.Blank: continue
except Exception as e:
print "".join(traceback.format_exception(*sys.exc_info()))
diff --git a/python/step6_file.py b/python/step6_file.py
index 8a3432d..9e0b8cd 100644
--- a/python/step6_file.py
+++ b/python/step6_file.py
@@ -1,41 +1,41 @@
import sys, traceback
import mal_readline
-from mal_types import (pr_str, sequential_Q, symbol_Q, coll_Q, list_Q,
- vector_Q, hash_map_Q, new_symbol, new_function,
- new_list, new_vector, new_hash_map, Env, types_ns)
-from reader import (read_str, Blank)
+import mal_types as types
+import reader, printer
+from env import Env
+import core
# read
def READ(str):
- return read_str(str)
+ return reader.read_str(str)
# eval
def eval_ast(ast, env):
- if symbol_Q(ast):
+ if types._symbol_Q(ast):
return env.get(ast)
- elif list_Q(ast):
- return new_list(*map(lambda a: EVAL(a, env), ast))
- elif vector_Q(ast):
- return new_vector(*map(lambda a: EVAL(a, env), ast))
- elif hash_map_Q(ast):
+ elif types._list_Q(ast):
+ return types._list(*map(lambda a: EVAL(a, env), ast))
+ elif types._vector_Q(ast):
+ return types._vector(*map(lambda a: EVAL(a, env), ast))
+ elif types._hash_map_Q(ast):
keyvals = []
for k in ast.keys():
keyvals.append(EVAL(k, env))
keyvals.append(EVAL(ast[k], env))
- return new_hash_map(*keyvals)
+ return types._hash_map(*keyvals)
else:
return ast # primitive value, return unchanged
def EVAL(ast, env):
while True:
#print("EVAL %s" % ast)
- if not list_Q(ast):
+ if not types._list_Q(ast):
return eval_ast(ast, env)
-
+
# apply list
if len(ast) == 0: return ast
- a0 = ast[0]
-
+ a0 = ast[0]
+
if "def!" == a0:
a1, a2 = ast[1], ast[2]
res = EVAL(a2, env)
@@ -61,7 +61,7 @@ def EVAL(ast, env):
# Continue loop (TCO)
elif "fn*" == a0:
a1, a2 = ast[1], ast[2]
- return new_function(EVAL, a2, env, a1)
+ return types._function(EVAL, Env, a2, env, a1)
else:
el = eval_ast(ast, env)
f = el[0]
@@ -74,7 +74,7 @@ def EVAL(ast, env):
# print
def PRINT(exp):
- return pr_str(exp)
+ return printer._pr_str(exp)
# repl
repl_env = Env()
@@ -83,9 +83,9 @@ def REP(str):
def _ref(k,v): repl_env.set(k, v)
# Import types functions
-for name, val in types_ns.items(): _ref(name, val)
+for name, val in core.ns.items(): _ref(name, val)
-_ref('read-string', read_str)
+_ref('read-string', reader.read_str)
_ref('eval', lambda ast: EVAL(ast, repl_env))
_ref('slurp', lambda file: open(file).read())
@@ -102,6 +102,6 @@ else:
if line == None: break
if line == "": continue
print(REP(line))
- except Blank: continue
+ except reader.Blank: continue
except Exception as e:
print "".join(traceback.format_exception(*sys.exc_info()))
diff --git a/python/step7_quote.py b/python/step7_quote.py
index 7acc322..3ed6965 100644
--- a/python/step7_quote.py
+++ b/python/step7_quote.py
@@ -1,54 +1,59 @@
import sys, traceback
import mal_readline
-from mal_types import (pr_str, sequential_Q, symbol_Q, coll_Q, list_Q,
- vector_Q, hash_map_Q, new_symbol, new_function,
- new_list, new_vector, new_hash_map, Env, types_ns)
-from reader import (read_str, Blank)
+import mal_types as types
+import reader, printer
+from env import Env
+import core
# read
def READ(str):
- return read_str(str)
+ return reader.read_str(str)
# eval
def is_pair(x):
- return sequential_Q(x) and len(x) > 0
+ return types._sequential_Q(x) and len(x) > 0
def quasiquote(ast):
if not is_pair(ast):
- return new_list(new_symbol("quote"), ast)
+ return types._list(types._symbol("quote"),
+ ast)
elif ast[0] == 'unquote':
return ast[1]
elif is_pair(ast[0]) and ast[0][0] == 'splice-unquote':
- return new_list(new_symbol("concat"), ast[0][1], quasiquote(ast[1:]))
+ return types._list(types._symbol("concat"),
+ ast[0][1],
+ quasiquote(ast[1:]))
else:
- return new_list(new_symbol("cons"), quasiquote(ast[0]), quasiquote(ast[1:]))
+ return types._list(types._symbol("cons"),
+ quasiquote(ast[0]),
+ quasiquote(ast[1:]))
def eval_ast(ast, env):
- if symbol_Q(ast):
+ if types._symbol_Q(ast):
return env.get(ast)
- elif list_Q(ast):
- return new_list(*map(lambda a: EVAL(a, env), ast))
- elif vector_Q(ast):
- return new_vector(*map(lambda a: EVAL(a, env), ast))
- elif hash_map_Q(ast):
+ elif types._list_Q(ast):
+ return types._list(*map(lambda a: EVAL(a, env), ast))
+ elif types._vector_Q(ast):
+ return types._vector(*map(lambda a: EVAL(a, env), ast))
+ elif types._hash_map_Q(ast):
keyvals = []
for k in ast.keys():
keyvals.append(EVAL(k, env))
keyvals.append(EVAL(ast[k], env))
- return new_hash_map(*keyvals)
+ return types._hash_map(*keyvals)
else:
return ast # primitive value, return unchanged
def EVAL(ast, env):
while True:
#print("EVAL %s" % ast)
- if not list_Q(ast):
+ if not types._list_Q(ast):
return eval_ast(ast, env)
-
+
# apply list
if len(ast) == 0: return ast
- a0 = ast[0]
-
+ a0 = ast[0]
+
if "def!" == a0:
a1, a2 = ast[1], ast[2]
res = EVAL(a2, env)
@@ -78,7 +83,7 @@ def EVAL(ast, env):
# Continue loop (TCO)
elif "fn*" == a0:
a1, a2 = ast[1], ast[2]
- return new_function(EVAL, a2, env, a1)
+ return types._function(EVAL, Env, a2, env, a1)
else:
el = eval_ast(ast, env)
f = el[0]
@@ -91,7 +96,7 @@ def EVAL(ast, env):
# print
def PRINT(exp):
- return pr_str(exp)
+ return printer._pr_str(exp)
# repl
repl_env = Env()
@@ -100,9 +105,9 @@ def REP(str):
def _ref(k,v): repl_env.set(k, v)
# Import types functions
-for name, val in types_ns.items(): _ref(name, val)
+for name, val in core.ns.items(): _ref(name, val)
-_ref('read-string', read_str)
+_ref('read-string', reader.read_str)
_ref('eval', lambda ast: EVAL(ast, repl_env))
_ref('slurp', lambda file: open(file).read())
@@ -119,6 +124,6 @@ else:
if line == None: break
if line == "": continue
print(REP(line))
- except Blank: continue
+ except reader.Blank: continue
except Exception as e:
print "".join(traceback.format_exception(*sys.exc_info()))
diff --git a/python/step8_macros.py b/python/step8_macros.py
index e09942c..6e2bd45 100644
--- a/python/step8_macros.py
+++ b/python/step8_macros.py
@@ -1,31 +1,36 @@
import sys, traceback
import mal_readline
-from mal_types import (pr_str, sequential_Q, symbol_Q, coll_Q, list_Q,
- vector_Q, hash_map_Q, new_symbol, new_function,
- new_list, new_vector, new_hash_map, Env, types_ns)
-from reader import (read_str, Blank)
+import mal_types as types
+import reader, printer
+from env import Env
+import core
# read
def READ(str):
- return read_str(str)
+ return reader.read_str(str)
# eval
def is_pair(x):
- return sequential_Q(x) and len(x) > 0
+ return types._sequential_Q(x) and len(x) > 0
def quasiquote(ast):
if not is_pair(ast):
- return new_list(new_symbol("quote"), ast)
+ return types._list(types._symbol("quote"),
+ ast)
elif ast[0] == 'unquote':
return ast[1]
elif is_pair(ast[0]) and ast[0][0] == 'splice-unquote':
- return new_list(new_symbol("concat"), ast[0][1], quasiquote(ast[1:]))
+ return types._list(types._symbol("concat"),
+ ast[0][1],
+ quasiquote(ast[1:]))
else:
- return new_list(new_symbol("cons"), quasiquote(ast[0]), quasiquote(ast[1:]))
+ return types._list(types._symbol("cons"),
+ quasiquote(ast[0]),
+ quasiquote(ast[1:]))
def is_macro_call(ast, env):
- return (list_Q(ast) and
- symbol_Q(ast[0]) and
+ return (types._list_Q(ast) and
+ types._symbol_Q(ast[0]) and
env.find(ast[0]) and
hasattr(env.get(ast[0]), '_ismacro_'))
@@ -36,33 +41,33 @@ def macroexpand(ast, env):
return ast
def eval_ast(ast, env):
- if symbol_Q(ast):
+ if types._symbol_Q(ast):
return env.get(ast)
- elif list_Q(ast):
- return new_list(*map(lambda a: EVAL(a, env), ast))
- elif vector_Q(ast):
- return new_vector(*map(lambda a: EVAL(a, env), ast))
- elif hash_map_Q(ast):
+ elif types._list_Q(ast):
+ return types._list(*map(lambda a: EVAL(a, env), ast))
+ elif types._vector_Q(ast):
+ return types._vector(*map(lambda a: EVAL(a, env), ast))
+ elif types._hash_map_Q(ast):
keyvals = []
for k in ast.keys():
keyvals.append(EVAL(k, env))
keyvals.append(EVAL(ast[k], env))
- return new_hash_map(*keyvals)
+ return types._hash_map(*keyvals)
else:
return ast # primitive value, return unchanged
def EVAL(ast, env):
while True:
#print("EVAL %s" % ast)
- if not list_Q(ast):
+ if not types._list_Q(ast):
return eval_ast(ast, env)
-
+
# apply list
ast = macroexpand(ast, env)
- if not list_Q(ast): return ast
+ if not types._list_Q(ast): return ast
if len(ast) == 0: return ast
+ a0 = ast[0]
- a0 = ast[0]
if "def!" == a0:
a1, a2 = ast[1], ast[2]
res = EVAL(a2, env)
@@ -98,7 +103,7 @@ def EVAL(ast, env):
# Continue loop (TCO)
elif "fn*" == a0:
a1, a2 = ast[1], ast[2]
- return new_function(EVAL, a2, env, a1)
+ return types._function(EVAL, Env, a2, env, a1)
else:
el = eval_ast(ast, env)
f = el[0]
@@ -111,7 +116,7 @@ def EVAL(ast, env):
# print
def PRINT(exp):
- return pr_str(exp)
+ return printer._pr_str(exp)
# repl
repl_env = Env()
@@ -120,9 +125,9 @@ def REP(str):
def _ref(k,v): repl_env.set(k, v)
# Import types functions
-for name, val in types_ns.items(): _ref(name, val)
+for name, val in core.ns.items(): _ref(name, val)
-_ref('read-string', read_str)
+_ref('read-string', reader.read_str)
_ref('eval', lambda ast: EVAL(ast, repl_env))
_ref('slurp', lambda file: open(file).read())
@@ -139,6 +144,6 @@ else:
if line == None: break
if line == "": continue
print(REP(line))
- except Blank: continue
+ except reader.Blank: continue
except Exception as e:
print "".join(traceback.format_exception(*sys.exc_info()))