From b8ee29b22fbaa7a01f2754b4d6dd9af52e02017c Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Thu, 18 Dec 2014 20:33:49 -0600 Subject: [PATCH] All: add keywords. Also, fix nth and count to match cloure. --- Makefile | 2 +- bash/core.sh | 12 +++ bash/env.sh | 10 +- bash/printer.sh | 26 +++-- bash/reader.sh | 3 +- bash/step3_env.sh | 20 ++-- bash/step4_if_fn_do.sh | 19 ++-- bash/step5_tco.sh | 19 ++-- bash/step6_file.sh | 22 ++-- bash/step7_quote.sh | 25 +++-- bash/step8_macros.sh | 36 ++++--- bash/step9_try.sh | 44 ++++---- bash/stepA_interop.sh | 45 ++++---- bash/types.sh | 33 +++++- c/core.c | 22 +++- c/core.h | 2 +- c/env.c | 18 ++-- c/printer.c | 21 +++- c/reader.c | 7 +- c/step3_env.c | 14 +-- c/step4_if_fn_do.c | 17 +-- c/step5_tco.c | 15 +-- c/step6_file.c | 20 ++-- c/step7_quote.c | 20 ++-- c/step8_macros.c | 29 +++--- c/step9_try.c | 31 +++--- c/stepA_interop.c | 29 +++--- c/types.c | 8 +- c/types.h | 7 +- clojure/src/core.clj | 20 ++-- coffee/core.coffee | 7 +- coffee/env.coffee | 15 ++- coffee/printer.coffee | 1 + coffee/reader.coffee | 9 +- coffee/step3_env.coffee | 14 +-- coffee/step4_if_fn_do.coffee | 8 +- coffee/step5_tco.coffee | 8 +- coffee/step6_file.coffee | 14 +-- coffee/step7_quote.coffee | 14 +-- coffee/step8_macros.coffee | 20 ++-- coffee/step9_try.coffee | 22 ++-- coffee/stepA_interop.coffee | 20 ++-- coffee/types.coffee | 9 +- cs/core.cs | 31 +++++- cs/env.cs | 14 +-- cs/printer.cs | 4 +- cs/reader.cs | 6 +- cs/step2_eval.cs | 2 +- cs/step3_env.cs | 21 ++-- cs/step4_if_fn_do.cs | 11 +- cs/step5_tco.cs | 11 +- cs/step6_file.cs | 16 +-- cs/step7_quote.cs | 16 +-- cs/step8_macros.cs | 24 ++--- cs/step9_try.cs | 24 ++--- cs/stepA_interop.cs | 24 ++--- cs/types.cs | 4 +- docs/TODO | 116 +++++++++------------ go/src/core/core.go | 12 ++- go/src/env/env.go | 14 +-- go/src/printer/printer.go | 4 +- go/src/reader/reader.go | 2 + go/src/step3_env/step3_env.go | 14 +-- go/src/step4_if_fn_do/step4_if_fn_do.go | 8 +- go/src/step5_tco/step5_tco.go | 8 +- go/src/step6_file/step6_file.go | 14 +-- go/src/step7_quote/step7_quote.go | 14 +-- go/src/step8_macros/step8_macros.go | 22 ++-- go/src/step9_try/step9_try.go | 24 ++--- go/src/stepA_interop/stepA_interop.go | 22 ++-- go/src/types/types.go | 22 +++- java/src/main/java/mal/core.java | 36 ++++++- java/src/main/java/mal/env.java | 15 +-- java/src/main/java/mal/printer.java | 5 +- java/src/main/java/mal/reader.java | 6 +- java/src/main/java/mal/step3_env.java | 18 ++-- java/src/main/java/mal/step4_if_fn_do.java | 9 +- java/src/main/java/mal/step5_tco.java | 9 +- java/src/main/java/mal/step6_file.java | 13 ++- java/src/main/java/mal/step7_quote.java | 13 ++- java/src/main/java/mal/step8_macros.java | 21 ++-- java/src/main/java/mal/step9_try.java | 23 ++-- java/src/main/java/mal/stepA_interop.java | 21 ++-- java/src/main/java/mal/types.java | 5 +- js/core.js | 10 +- js/env.js | 20 +++- js/printer.js | 8 +- js/reader.js | 2 + js/step3_env.js | 10 +- js/step4_if_fn_do.js | 4 +- js/step5_tco.js | 4 +- js/step6_file.js | 9 +- js/step7_quote.js | 9 +- js/step8_macros.js | 15 +-- js/step9_try.js | 17 ++- js/stepA_interop.js | 15 +-- js/types.js | 16 ++- make/core.mk | 19 +++- make/printer.mk | 6 +- make/reader.mk | 16 ++- make/step3_env.mk | 3 +- make/step4_if_fn_do.mk | 3 +- make/step6_file.mk | 3 +- make/step7_quote.mk | 3 +- make/step8_macros.mk | 3 +- make/step9_try.mk | 3 +- make/stepA_interop.mk | 3 +- make/types.mk | 18 +++- make/util.mk | 1 + mal/core.mal | 2 + perl/core.pm | 25 ++++- perl/env.pm | 8 +- perl/printer.pm | 4 +- perl/reader.pm | 3 +- perl/readline.pm | 31 ++++-- perl/step0.5_repl.pl | 33 ++++++ perl/step1_read_print.pl | 5 +- perl/step2_eval.pl | 5 +- perl/step3_env.pl | 19 ++-- perl/step4_if_fn_do.pl | 15 ++- perl/step5_tco.pl | 15 ++- perl/step6_file.pl | 20 ++-- perl/step7_quote.pl | 20 ++-- perl/step8_macros.pl | 31 ++++-- perl/step9_try.pl | 30 +++--- perl/stepA_interop.pl | 28 +++-- perl/types.pm | 12 ++- php/core.php | 8 +- php/env.php | 8 +- php/printer.php | 4 +- php/reader.php | 2 + php/step3_env.php | 14 +-- php/step4_if_fn_do.php | 8 +- php/step5_tco.php | 8 +- php/step6_file.php | 12 +-- php/step7_quote.php | 12 +-- php/step8_macros.php | 20 ++-- php/step9_try.php | 22 ++-- php/stepA_interop.php | 20 ++-- php/types.php | 7 ++ ps/core.ps | 20 +++- ps/printer.ps | 25 +++-- ps/reader.ps | 32 +++++- ps/types.ps | 23 ++++ python/core.py | 10 +- python/mal_types.py | 7 ++ python/printer.py | 4 +- python/reader.py | 3 +- python/step3_env.py | 8 +- python/step4_if_fn_do.py | 2 +- python/step5_tco.py | 2 +- python/step6_file.py | 6 +- python/step7_quote.py | 6 +- python/step8_macros.py | 6 +- python/step9_try.py | 8 +- python/stepA_interop.py | 6 +- r/core.r | 14 ++- r/printer.r | 4 +- r/reader.r | 2 + r/types.r | 5 + ruby/core.rb | 8 +- ruby/printer.rb | 4 +- ruby/reader.rb | 1 + rust/src/core.rs | 6 +- rust/src/env.rs | 57 ++++++---- rust/src/reader.rs | 2 + rust/src/step3_env.rs | 22 ++-- rust/src/step4_if_fn_do.rs | 18 ++-- rust/src/step5_tco.rs | 18 ++-- rust/src/step6_file.rs | 22 ++-- rust/src/step7_quote.rs | 22 ++-- rust/src/step8_macros.rs | 40 +++---- rust/src/step9_try.rs | 47 ++++----- rust/src/stepA_interop.rs | 45 ++++---- rust/src/types.rs | 50 ++++++++- tests/step1_read_print.mal | 7 ++ tests/step2_eval.mal | 3 + tests/step4_if_fn_do.mal | 12 +++ tests/step8_macros.mal | 16 +-- tests/step9_try.mal | 46 ++++++++ tests/test.txt | 1 + vb/core.vb | 34 +++++- vb/env.vb | 14 +-- vb/printer.vb | 4 +- vb/reader.vb | 6 +- vb/step3_env.vb | 15 ++- vb/step4_if_fn_do.vb | 9 +- vb/step5_tco.vb | 9 +- vb/step6_file.vb | 13 ++- vb/step7_quote.vb | 13 ++- vb/step8_macros.vb | 21 ++-- vb/step9_try.vb | 21 ++-- vb/stepA_interop.vb | 21 ++-- vb/types.vb | 4 +- 194 files changed, 1836 insertions(+), 1114 deletions(-) create mode 100644 perl/step0.5_repl.pl create mode 100644 tests/test.txt diff --git a/Makefile b/Makefile index 0a01e01f..bd25ad7e 100644 --- a/Makefile +++ b/Makefile @@ -78,7 +78,7 @@ java_RUNSTEP = mvn -quiet exec:java -Dexec.mainClass="mal.$($(1))" -Dexec.arg js_RUNSTEP = node ../$(2) $(3) make_RUNSTEP = make -f ../$(2) $(3) mal_RUNSTEP = $(call $(MAL_IMPL)_RUNSTEP,$(1),$(call $(MAL_IMPL)_STEP_TO_PROG,stepA),../$(2),") #" -perl_RUNSTEP = perl ../$(2) $(3) +perl_RUNSTEP = perl ../$(2) --raw $(3) php_RUNSTEP = php ../$(2) $(3) ps_RUNSTEP = $(4)gs -q -I./ -dNODISPLAY -- ../$(2) $(3)$(4) python_RUNSTEP = $(PYTHON) ../$(2) $(3) diff --git a/bash/core.sh b/bash/core.sh index c8a02618..ca53c434 100644 --- a/bash/core.sh +++ b/bash/core.sh @@ -41,6 +41,11 @@ false? () { _false? "${1}" && r="${__true}" || r="${__false}"; } symbol? () { _symbol? "${1}" && r="${__true}" || r="${__false}"; } +# Keyword functions + +keyword? () { _keyword? "${1}" && r="${__true}" || r="${__false}"; } + + # Number functions number? () { _number? "${1}" && r="${__true}" || r="${__false}"; } @@ -230,6 +235,10 @@ concat () { nth () { _nth "${1}" "${ANON["${2}"]}" + if [ -z "${r}" ]; then + _error "nth: index out of bounds" + return + fi } empty? () { _empty? "${1}" && r="${__true}" || r="${__false}"; } @@ -316,7 +325,10 @@ declare -A core_ns=( [nil?]=nil? [true?]=true? [false?]=false? + [symbol]=_symbol [symbol?]=symbol? + [keyword]=_keyword + [keyword?]=keyword? [pr-str]=pr_str [str]=str diff --git a/bash/env.sh b/bash/env.sh index 2eabe8b2..9595aa25 100644 --- a/bash/env.sh +++ b/bash/env.sh @@ -44,7 +44,7 @@ ENV () { # Find the environment with the key set and return the environment ENV_FIND () { - if _contains? "${1}" "${2}"; then + if _contains? "${1}" "${ANON["${2}"]}"; then r="${1}" else local obj="${ANON["${1}"]}" @@ -63,16 +63,18 @@ ENV_FIND () { ENV_GET () { ENV_FIND "${1}" "${2}" local env="${r}" + local key="${ANON["${2}"]}" if [[ "${r}" ]]; then local obj="${ANON["${env}"]}" - eval r="\${${obj}["${2}"]}" + eval r="\${${obj}["${key}"]}" else - _error "'${2}' not found" + _error "'${key}' not found" fi } ENV_SET () { - _assoc! "${1}" "${2}" "${3}" + local key="${ANON["${2}"]}" + _assoc! "${1}" "${key}" "${3}" } fi diff --git a/bash/printer.sh b/bash/printer.sh index 911db17d..0d230282 100644 --- a/bash/printer.sh +++ b/bash/printer.sh @@ -29,28 +29,42 @@ symbol_pr_str () { r="${r//__STAR__/*}" } -string_pr_str () { +keyword_pr_str () { + string_pr_str "${1}" +} + +_raw_string_pr_str () { + local s="${1}" local print_readably="${2}" - if [ "${print_readably}" == "yes" ]; then - local s="${ANON["${1}"]}" + if [[ "${s:0:1}" = "${__keyw}" ]]; then + r=":${s:1}" + elif [ "${print_readably}" == "yes" ]; then s="${s//\\/\\\\}" r="\"${s//\"/\\\"}\"" else - r="${ANON["${1}"]}" + r="${s}" fi r="${r//__STAR__/$'*'}" } +string_pr_str () { + _raw_string_pr_str "${ANON["${1}"]}" "${2}" +} + function_pr_str () { r="${ANON["${1}"]}"; } +bash_pr_str () { + r="$(declare -f -p ${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__/$'*'}\"" + _raw_string_pr_str "${key}" "${print_readably}" + res="${res} ${r}" eval val="\${${hm}[\"${key}\"]}" _pr_str "${val}" "${print_readably}" res="${res} ${r}" diff --git a/bash/reader.sh b/bash/reader.sh index ee7e5059..a00e7a1b 100644 --- a/bash/reader.sh +++ b/bash/reader.sh @@ -15,6 +15,7 @@ READ_ATOM () { \"*) token="${token:1:-1}" token="${token//\\\"/\"}" _string "${token}" ;; + :*) _keyword "${token:1}" ;; nil) r="${__nil}" ;; true) r="${__true}" ;; false) r="${__false}" ;; @@ -135,7 +136,7 @@ READ_STR () { declare -a __reader_tokens TOKENIZE "${*}" || return 1 # sets __reader_tokens #set | grep ^__reader_tokens - if [ -z "${__reader_tokens[k]}" ]; then + if [ -z "${__reader_tokens[0]}" ]; then r= return 1 # No tokens fi diff --git a/bash/step3_env.sh b/bash/step3_env.sh index a837e00f..d924b039 100755 --- a/bash/step3_env.sh +++ b/bash/step3_env.sh @@ -17,8 +17,7 @@ EVAL_AST () { _obj_type "${ast}"; local ot="${r}" case "${ot}" in symbol) - local val="${ANON["${ast}"]}" - ENV_GET "${env}" "${val}" + ENV_GET "${env}" "${ast}" return ;; list) _map_with_type _list EVAL "${ast}" "${env}" ;; @@ -55,10 +54,9 @@ EVAL () { _nth "${ast}" 1; local a1="${r}" _nth "${ast}" 2; local a2="${r}" case "${ANON["${a0}"]}" in - def!) local k="${ANON["${a1}"]}" - #echo "def! ${k} to ${a2} in ${env}" - EVAL "${a2}" "${env}" - ENV_SET "${env}" "${k}" "${r}" + def!) EVAL "${a2}" "${env}" + [[ "${__ERROR}" ]] && return 1 + ENV_SET "${env}" "${a1}" "${r}" return ;; let*) ENV "${env}"; local let_env="${r}" local let_pairs=(${ANON["${a1}"]}) @@ -66,7 +64,7 @@ EVAL () { #echo "let: [${let_pairs[*]}] for ${a2}" while [[ "${let_pairs["${idx}"]}" ]]; do EVAL "${let_pairs[$(( idx + 1))]}" "${let_env}" - ENV_SET "${let_env}" "${ANON["${let_pairs[${idx}]}"]}" "${r}" + ENV_SET "${let_env}" "${let_pairs[${idx}]}" "${r}" idx=$(( idx + 2)) done EVAL "${a2}" "${let_env}" @@ -107,10 +105,10 @@ minus () { r=$(( ${ANON["${1}"]} - ${ANON["${2}"]} )); _number "${r}"; } multiply () { r=$(( ${ANON["${1}"]} * ${ANON["${2}"]} )); _number "${r}"; } divide () { r=$(( ${ANON["${1}"]} / ${ANON["${2}"]} )); _number "${r}"; } -ENV_SET "${REPL_ENV}" "+" plus -ENV_SET "${REPL_ENV}" "-" minus -ENV_SET "${REPL_ENV}" "__STAR__" multiply -ENV_SET "${REPL_ENV}" "/" divide +_symbol "+"; ENV_SET "${REPL_ENV}" "${r}" plus +_symbol "-"; ENV_SET "${REPL_ENV}" "${r}" minus +_symbol "__STAR__"; ENV_SET "${REPL_ENV}" "${r}" multiply +_symbol "/"; ENV_SET "${REPL_ENV}" "${r}" divide # repl loop while true; do diff --git a/bash/step4_if_fn_do.sh b/bash/step4_if_fn_do.sh index 6fd73015..dd0db030 100755 --- a/bash/step4_if_fn_do.sh +++ b/bash/step4_if_fn_do.sh @@ -18,8 +18,7 @@ EVAL_AST () { _obj_type "${ast}"; local ot="${r}" case "${ot}" in symbol) - local val="${ANON["${ast}"]}" - ENV_GET "${env}" "${val}" + ENV_GET "${env}" "${ast}" return ;; list) _map_with_type _list EVAL "${ast}" "${env}" ;; @@ -56,10 +55,9 @@ EVAL () { _nth "${ast}" 1; local a1="${r}" _nth "${ast}" 2; local a2="${r}" case "${ANON["${a0}"]}" in - def!) local k="${ANON["${a1}"]}" - #echo "def! ${k} to ${a2} in ${env}" - EVAL "${a2}" "${env}" - ENV_SET "${env}" "${k}" "${r}" + def!) EVAL "${a2}" "${env}" + [[ "${__ERROR}" ]] && return 1 + ENV_SET "${env}" "${a1}" "${r}" return ;; let*) ENV "${env}"; local let_env="${r}" local let_pairs=(${ANON["${a1}"]}) @@ -67,7 +65,7 @@ EVAL () { #echo "let: [${let_pairs[*]}] for ${a2}" while [[ "${let_pairs["${idx}"]}" ]]; do EVAL "${let_pairs[$(( idx + 1))]}" "${let_env}" - ENV_SET "${let_env}" "${ANON["${let_pairs[${idx}]}"]}" "${r}" + ENV_SET "${let_env}" "${let_pairs[${idx}]}" "${r}" idx=$(( idx + 2)) done EVAL "${a2}" "${let_env}" @@ -78,6 +76,7 @@ EVAL () { _last "${r}" return ;; if) EVAL "${a1}" "${env}" + [[ "${__ERROR}" ]] && return 1 if [[ "${r}" == "${__false}" || "${r}" == "${__nil}" ]]; then # eval false form _nth "${ast}" 3; local a3="${r}" @@ -126,7 +125,11 @@ REP () { } # core.sh: defined using bash -_fref () { _function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } +_fref () { + _symbol "${1}"; local sym="${r}" + _function "${2} \"\${@}\"" + ENV_SET "${REPL_ENV}" "${sym}" "${r}" +} for n in "${!core_ns[@]}"; do _fref "${n}" "${core_ns["${n}"]}"; done # core.mal: defined using the language itself diff --git a/bash/step5_tco.sh b/bash/step5_tco.sh index fc36b46b..f282e445 100755 --- a/bash/step5_tco.sh +++ b/bash/step5_tco.sh @@ -18,8 +18,7 @@ EVAL_AST () { _obj_type "${ast}"; local ot="${r}" case "${ot}" in symbol) - local val="${ANON["${ast}"]}" - ENV_GET "${env}" "${val}" + ENV_GET "${env}" "${ast}" return ;; list) _map_with_type _list EVAL "${ast}" "${env}" ;; @@ -57,10 +56,9 @@ EVAL () { _nth "${ast}" 1; local a1="${r}" _nth "${ast}" 2; local a2="${r}" case "${ANON["${a0}"]}" in - def!) local k="${ANON["${a1}"]}" - #echo "def! ${k} to ${a2} in ${env}" - EVAL "${a2}" "${env}" - ENV_SET "${env}" "${k}" "${r}" + def!) EVAL "${a2}" "${env}" + [[ "${__ERROR}" ]] && return 1 + ENV_SET "${env}" "${a1}" "${r}" return ;; let*) ENV "${env}"; local let_env="${r}" local let_pairs=(${ANON["${a1}"]}) @@ -68,7 +66,7 @@ EVAL () { #echo "let: [${let_pairs[*]}] for ${a2}" while [[ "${let_pairs["${idx}"]}" ]]; do EVAL "${let_pairs[$(( idx + 1))]}" "${let_env}" - ENV_SET "${let_env}" "${ANON["${let_pairs[${idx}]}"]}" "${r}" + ENV_SET "${let_env}" "${let_pairs[${idx}]}" "${r}" idx=$(( idx + 2)) done ast="${a2}" @@ -84,6 +82,7 @@ EVAL () { # Continue loop ;; if) EVAL "${a1}" "${env}" + [[ "${__ERROR}" ]] && return 1 if [[ "${r}" == "${__false}" || "${r}" == "${__nil}" ]]; then # eval false form _nth "${ast}" 3; local a3="${r}" @@ -145,7 +144,11 @@ REP () { } # core.sh: defined using bash -_fref () { _function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } +_fref () { + _symbol "${1}"; local sym="${r}" + _function "${2} \"\${@}\"" + ENV_SET "${REPL_ENV}" "${sym}" "${r}" +} for n in "${!core_ns[@]}"; do _fref "${n}" "${core_ns["${n}"]}"; done # core.mal: defined using the language itself diff --git a/bash/step6_file.sh b/bash/step6_file.sh index e6ee5710..c6558532 100755 --- a/bash/step6_file.sh +++ b/bash/step6_file.sh @@ -18,8 +18,7 @@ EVAL_AST () { _obj_type "${ast}"; local ot="${r}" case "${ot}" in symbol) - local val="${ANON["${ast}"]}" - ENV_GET "${env}" "${val}" + ENV_GET "${env}" "${ast}" return ;; list) _map_with_type _list EVAL "${ast}" "${env}" ;; @@ -57,10 +56,9 @@ EVAL () { _nth "${ast}" 1; local a1="${r}" _nth "${ast}" 2; local a2="${r}" case "${ANON["${a0}"]}" in - def!) local k="${ANON["${a1}"]}" - #echo "def! ${k} to ${a2} in ${env}" - EVAL "${a2}" "${env}" - ENV_SET "${env}" "${k}" "${r}" + def!) EVAL "${a2}" "${env}" + [[ "${__ERROR}" ]] && return 1 + ENV_SET "${env}" "${a1}" "${r}" return ;; let*) ENV "${env}"; local let_env="${r}" local let_pairs=(${ANON["${a1}"]}) @@ -68,7 +66,7 @@ EVAL () { #echo "let: [${let_pairs[*]}] for ${a2}" while [[ "${let_pairs["${idx}"]}" ]]; do EVAL "${let_pairs[$(( idx + 1))]}" "${let_env}" - ENV_SET "${let_env}" "${ANON["${let_pairs[${idx}]}"]}" "${r}" + ENV_SET "${let_env}" "${let_pairs[${idx}]}" "${r}" idx=$(( idx + 2)) done ast="${a2}" @@ -84,6 +82,7 @@ EVAL () { # Continue loop ;; if) EVAL "${a1}" "${env}" + [[ "${__ERROR}" ]] && return 1 if [[ "${r}" == "${__false}" || "${r}" == "${__nil}" ]]; then # eval false form _nth "${ast}" 3; local a3="${r}" @@ -145,13 +144,18 @@ REP () { } # core.sh: defined using bash -_fref () { _function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } +_fref () { + _symbol "${1}"; local sym="${r}" + _function "${2} \"\${@}\"" + ENV_SET "${REPL_ENV}" "${sym}" "${r}" +} for n in "${!core_ns[@]}"; do _fref "${n}" "${core_ns["${n}"]}"; done _eval () { EVAL "${1}" "${REPL_ENV}"; } _fref "eval" _eval _list; argv="${r}" for _arg in "${@:2}"; do _string "${_arg}"; _conj! "${argv}" "${r}"; done -ENV_SET "${REPL_ENV}" "__STAR__ARGV__STAR__" "${argv}"; +_symbol "__STAR__ARGV__STAR__" +ENV_SET "${REPL_ENV}" "${r}" "${argv}"; # core.mal: defined using the language itself REP "(def! not (fn* (a) (if a false true)))" diff --git a/bash/step7_quote.sh b/bash/step7_quote.sh index d71e24be..2f2e67eb 100755 --- a/bash/step7_quote.sh +++ b/bash/step7_quote.sh @@ -56,8 +56,7 @@ EVAL_AST () { _obj_type "${ast}"; local ot="${r}" case "${ot}" in symbol) - local val="${ANON["${ast}"]}" - ENV_GET "${env}" "${val}" + ENV_GET "${env}" "${ast}" return ;; list) _map_with_type _list EVAL "${ast}" "${env}" ;; @@ -84,8 +83,7 @@ EVAL () { r= [[ "${__ERROR}" ]] && return 1 #_pr_str "${ast}"; echo "EVAL '${r} / ${env}'" - _obj_type "${ast}"; local ot="${r}" - if [[ "${ot}" != "list" ]]; then + if ! _list? "${ast}"; then EVAL_AST "${ast}" "${env}" return fi @@ -95,10 +93,9 @@ EVAL () { _nth "${ast}" 1; local a1="${r}" _nth "${ast}" 2; local a2="${r}" case "${ANON["${a0}"]}" in - def!) local k="${ANON["${a1}"]}" - #echo "def! ${k} to ${a2} in ${env}" - EVAL "${a2}" "${env}" - ENV_SET "${env}" "${k}" "${r}" + def!) EVAL "${a2}" "${env}" + [[ "${__ERROR}" ]] && return 1 + ENV_SET "${env}" "${a1}" "${r}" return ;; let*) ENV "${env}"; local let_env="${r}" local let_pairs=(${ANON["${a1}"]}) @@ -106,7 +103,7 @@ EVAL () { #echo "let: [${let_pairs[*]}] for ${a2}" while [[ "${let_pairs["${idx}"]}" ]]; do EVAL "${let_pairs[$(( idx + 1))]}" "${let_env}" - ENV_SET "${let_env}" "${ANON["${let_pairs[${idx}]}"]}" "${r}" + ENV_SET "${let_env}" "${let_pairs[${idx}]}" "${r}" idx=$(( idx + 2)) done ast="${a2}" @@ -130,6 +127,7 @@ EVAL () { # Continue loop ;; if) EVAL "${a1}" "${env}" + [[ "${__ERROR}" ]] && return 1 if [[ "${r}" == "${__false}" || "${r}" == "${__nil}" ]]; then # eval false form _nth "${ast}" 3; local a3="${r}" @@ -191,13 +189,18 @@ REP () { } # core.sh: defined using bash -_fref () { _function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } +_fref () { + _symbol "${1}"; local sym="${r}" + _function "${2} \"\${@}\"" + ENV_SET "${REPL_ENV}" "${sym}" "${r}" +} for n in "${!core_ns[@]}"; do _fref "${n}" "${core_ns["${n}"]}"; done _eval () { EVAL "${1}" "${REPL_ENV}"; } _fref "eval" _eval _list; argv="${r}" for _arg in "${@:2}"; do _string "${_arg}"; _conj! "${argv}" "${r}"; done -ENV_SET "${REPL_ENV}" "__STAR__ARGV__STAR__" "${argv}"; +_symbol "__STAR__ARGV__STAR__" +ENV_SET "${REPL_ENV}" "${r}" "${argv}"; # core.mal: defined using the language itself REP "(def! not (fn* (a) (if a false true)))" diff --git a/bash/step8_macros.sh b/bash/step8_macros.sh index 3be3651c..51d83bdf 100755 --- a/bash/step8_macros.sh +++ b/bash/step8_macros.sh @@ -54,9 +54,11 @@ IS_MACRO_CALL () { if ! _list? "${1}"; then return 1; fi _nth "${1}" 0; local a0="${r}" if _symbol? "${a0}"; then - ENV_FIND "${2}" "${ANON["${a0}"]}_ismacro_" + ENV_FIND "${2}" "${a0}" if [[ "${r}" ]]; then - return 0 + ENV_GET "${2}" "${a0}" + [ "${ANON["${r}_ismacro_"]}" ] + return $? fi fi return 1 @@ -66,7 +68,7 @@ MACROEXPAND () { local ast="${1}" env="${2}" while IS_MACRO_CALL "${ast}" "${env}"; do _nth "${ast}" 0; local a0="${r}" - ENV_GET "${env}" "${ANON["${a0}"]}"; local mac="${ANON["${r}"]}" + ENV_GET "${env}" "${a0}"; local mac="${ANON["${r}"]}" _rest "${ast}" ${mac%%@*} ${ANON["${r}"]} ast="${r}" @@ -81,8 +83,7 @@ EVAL_AST () { _obj_type "${ast}"; local ot="${r}" case "${ot}" in symbol) - local val="${ANON["${ast}"]}" - ENV_GET "${env}" "${val}" + ENV_GET "${env}" "${ast}" return ;; list) _map_with_type _list EVAL "${ast}" "${env}" ;; @@ -122,10 +123,9 @@ EVAL () { _nth "${ast}" 1; local a1="${r}" _nth "${ast}" 2; local a2="${r}" case "${ANON["${a0}"]}" in - def!) local k="${ANON["${a1}"]}" - #echo "def! ${k} to ${a2} in ${env}" - EVAL "${a2}" "${env}" - ENV_SET "${env}" "${k}" "${r}" + def!) EVAL "${a2}" "${env}" + [[ "${__ERROR}" ]] && return 1 + ENV_SET "${env}" "${a1}" "${r}" return ;; let*) ENV "${env}"; local let_env="${r}" local let_pairs=(${ANON["${a1}"]}) @@ -133,7 +133,7 @@ EVAL () { #echo "let: [${let_pairs[*]}] for ${a2}" while [[ "${let_pairs["${idx}"]}" ]]; do EVAL "${let_pairs[$(( idx + 1))]}" "${let_env}" - ENV_SET "${let_env}" "${ANON["${let_pairs[${idx}]}"]}" "${r}" + ENV_SET "${let_env}" "${let_pairs[${idx}]}" "${r}" idx=$(( idx + 2)) done ast="${a2}" @@ -149,10 +149,10 @@ EVAL () { # Continue loop ;; defmacro!) - local k="${ANON["${a1}"]}" EVAL "${a2}" "${env}" - ENV_SET "${env}" "${k}" "${r}" - ENV_SET "${env}" "${k}_ismacro_" "yes" + [[ "${__ERROR}" ]] && return 1 + ANON["${r}_ismacro_"]="yes" + ENV_SET "${env}" "${a1}" "${r}" return ;; macroexpand) MACROEXPAND "${a1}" "${env}" @@ -166,6 +166,7 @@ EVAL () { # Continue loop ;; if) EVAL "${a1}" "${env}" + [[ "${__ERROR}" ]] && return 1 if [[ "${r}" == "${__false}" || "${r}" == "${__nil}" ]]; then # eval false form _nth "${ast}" 3; local a3="${r}" @@ -227,13 +228,18 @@ REP () { } # core.sh: defined using bash -_fref () { _function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } +_fref () { + _symbol "${1}"; local sym="${r}" + _function "${2} \"\${@}\"" + ENV_SET "${REPL_ENV}" "${sym}" "${r}" +} for n in "${!core_ns[@]}"; do _fref "${n}" "${core_ns["${n}"]}"; done _eval () { EVAL "${1}" "${REPL_ENV}"; } _fref "eval" _eval _list; argv="${r}" for _arg in "${@:2}"; do _string "${_arg}"; _conj! "${argv}" "${r}"; done -ENV_SET "${REPL_ENV}" "__STAR__ARGV__STAR__" "${argv}"; +_symbol "__STAR__ARGV__STAR__" +ENV_SET "${REPL_ENV}" "${r}" "${argv}"; # core.mal: defined using the language itself REP "(def! not (fn* (a) (if a false true)))" diff --git a/bash/step9_try.sh b/bash/step9_try.sh index db0b5a7b..85698b4c 100755 --- a/bash/step9_try.sh +++ b/bash/step9_try.sh @@ -54,9 +54,11 @@ IS_MACRO_CALL () { if ! _list? "${1}"; then return 1; fi _nth "${1}" 0; local a0="${r}" if _symbol? "${a0}"; then - ENV_FIND "${2}" "${ANON["${a0}"]}_ismacro_" + ENV_FIND "${2}" "${a0}" if [[ "${r}" ]]; then - return 0 + ENV_GET "${2}" "${a0}" + [ "${ANON["${r}_ismacro_"]}" ] + return $? fi fi return 1 @@ -66,7 +68,7 @@ MACROEXPAND () { local ast="${1}" env="${2}" while IS_MACRO_CALL "${ast}" "${env}"; do _nth "${ast}" 0; local a0="${r}" - ENV_GET "${env}" "${ANON["${a0}"]}"; local mac="${ANON["${r}"]}" + ENV_GET "${env}" "${a0}"; local mac="${ANON["${r}"]}" _rest "${ast}" ${mac%%@*} ${ANON["${r}"]} ast="${r}" @@ -81,8 +83,7 @@ EVAL_AST () { _obj_type "${ast}"; local ot="${r}" case "${ot}" in symbol) - local val="${ANON["${ast}"]}" - ENV_GET "${env}" "${val}" + ENV_GET "${env}" "${ast}" return ;; list) _map_with_type _list EVAL "${ast}" "${env}" ;; @@ -122,10 +123,9 @@ EVAL () { _nth "${ast}" 1; local a1="${r}" _nth "${ast}" 2; local a2="${r}" case "${ANON["${a0}"]}" in - def!) local k="${ANON["${a1}"]}" - #echo "def! ${k} to ${a2} in ${env}" - EVAL "${a2}" "${env}" - ENV_SET "${env}" "${k}" "${r}" + def!) EVAL "${a2}" "${env}" + [[ "${__ERROR}" ]] && return 1 + ENV_SET "${env}" "${a1}" "${r}" return ;; let*) ENV "${env}"; local let_env="${r}" local let_pairs=(${ANON["${a1}"]}) @@ -133,7 +133,7 @@ EVAL () { #echo "let: [${let_pairs[*]}] for ${a2}" while [[ "${let_pairs["${idx}"]}" ]]; do EVAL "${let_pairs[$(( idx + 1))]}" "${let_env}" - ENV_SET "${let_env}" "${ANON["${let_pairs[${idx}]}"]}" "${r}" + ENV_SET "${let_env}" "${let_pairs[${idx}]}" "${r}" idx=$(( idx + 2)) done ast="${a2}" @@ -149,16 +149,15 @@ EVAL () { # Continue loop ;; defmacro!) - local k="${ANON["${a1}"]}" EVAL "${a2}" "${env}" - ENV_SET "${env}" "${k}" "${r}" - ENV_SET "${env}" "${k}_ismacro_" "yes" + [[ "${__ERROR}" ]] && return 1 + ANON["${r}_ismacro_"]="yes" + ENV_SET "${env}" "${a1}" "${r}" return ;; macroexpand) MACROEXPAND "${a1}" "${env}" return ;; - try*) MACROEXPAND "${a1}" "${env}" - EVAL "${r}" "${env}" + try*) EVAL "${a1}" "${env}" [[ -z "${__ERROR}" ]] && return _nth "${a2}" 0; local a20="${r}" if [ "${ANON["${a20}"]}" == "catch__STAR__" ]; then @@ -168,8 +167,7 @@ EVAL () { ENV "${env}" "${binds}" "${__ERROR}" local try_env="${r}" __ERROR= - MACROEXPAND "${a22}" "${try_env}" - EVAL "${r}" "${try_env}" + EVAL "${a22}" "${try_env}" fi # if no catch* clause, just propagate __ERROR return ;; do) _count "${ast}" @@ -181,6 +179,7 @@ EVAL () { # Continue loop ;; if) EVAL "${a1}" "${env}" + [[ "${__ERROR}" ]] && return 1 if [[ "${r}" == "${__false}" || "${r}" == "${__nil}" ]]; then # eval false form _nth "${ast}" 3; local a3="${r}" @@ -242,16 +241,20 @@ REP () { } # core.sh: defined using bash -_fref () { _function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } +_fref () { + _symbol "${1}"; local sym="${r}" + _function "${2} \"\${@}\"" + ENV_SET "${REPL_ENV}" "${sym}" "${r}" +} for n in "${!core_ns[@]}"; do _fref "${n}" "${core_ns["${n}"]}"; done _eval () { EVAL "${1}" "${REPL_ENV}"; } _fref "eval" _eval _list; argv="${r}" for _arg in "${@:2}"; do _string "${_arg}"; _conj! "${argv}" "${r}"; done -ENV_SET "${REPL_ENV}" "__STAR__ARGV__STAR__" "${argv}"; +_symbol "__STAR__ARGV__STAR__" +ENV_SET "${REPL_ENV}" "${r}" "${argv}"; # core.mal: defined using the language itself -REP "(def! *host-language* \"bash\")" REP "(def! not (fn* (a) (if a false true)))" REP "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" REP "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))" @@ -264,7 +267,6 @@ if [[ "${1}" ]]; then fi # repl loop -REP "(println (str \"Mal [\" *host-language* \"]\"))" while true; do READLINE "user> " || exit "$?" [[ "${r}" ]] && REP "${r}" && echo "${r}" diff --git a/bash/stepA_interop.sh b/bash/stepA_interop.sh index 2422643f..9a760e4c 100755 --- a/bash/stepA_interop.sh +++ b/bash/stepA_interop.sh @@ -54,9 +54,11 @@ IS_MACRO_CALL () { if ! _list? "${1}"; then return 1; fi _nth "${1}" 0; local a0="${r}" if _symbol? "${a0}"; then - ENV_FIND "${2}" "${ANON["${a0}"]}_ismacro_" + ENV_FIND "${2}" "${a0}" if [[ "${r}" ]]; then - return 0 + ENV_GET "${2}" "${a0}" + [ "${ANON["${r}_ismacro_"]}" ] + return $? fi fi return 1 @@ -66,7 +68,7 @@ MACROEXPAND () { local ast="${1}" env="${2}" while IS_MACRO_CALL "${ast}" "${env}"; do _nth "${ast}" 0; local a0="${r}" - ENV_GET "${env}" "${ANON["${a0}"]}"; local mac="${ANON["${r}"]}" + ENV_GET "${env}" "${a0}"; local mac="${ANON["${r}"]}" _rest "${ast}" ${mac%%@*} ${ANON["${r}"]} ast="${r}" @@ -81,8 +83,7 @@ EVAL_AST () { _obj_type "${ast}"; local ot="${r}" case "${ot}" in symbol) - local val="${ANON["${ast}"]}" - ENV_GET "${env}" "${val}" + ENV_GET "${env}" "${ast}" return ;; list) _map_with_type _list EVAL "${ast}" "${env}" ;; @@ -122,10 +123,9 @@ EVAL () { _nth "${ast}" 1; local a1="${r}" _nth "${ast}" 2; local a2="${r}" case "${ANON["${a0}"]}" in - def!) local k="${ANON["${a1}"]}" - #echo "def! ${k} to ${a2} in ${env}" - EVAL "${a2}" "${env}" - ENV_SET "${env}" "${k}" "${r}" + def!) EVAL "${a2}" "${env}" + [[ "${__ERROR}" ]] && return 1 + ENV_SET "${env}" "${a1}" "${r}" return ;; let*) ENV "${env}"; local let_env="${r}" local let_pairs=(${ANON["${a1}"]}) @@ -133,7 +133,7 @@ EVAL () { #echo "let: [${let_pairs[*]}] for ${a2}" while [[ "${let_pairs["${idx}"]}" ]]; do EVAL "${let_pairs[$(( idx + 1))]}" "${let_env}" - ENV_SET "${let_env}" "${ANON["${let_pairs[${idx}]}"]}" "${r}" + ENV_SET "${let_env}" "${let_pairs[${idx}]}" "${r}" idx=$(( idx + 2)) done ast="${a2}" @@ -149,16 +149,15 @@ EVAL () { # Continue loop ;; defmacro!) - local k="${ANON["${a1}"]}" EVAL "${a2}" "${env}" - ENV_SET "${env}" "${k}" "${r}" - ENV_SET "${env}" "${k}_ismacro_" "yes" + [[ "${__ERROR}" ]] && return 1 + ANON["${r}_ismacro_"]="yes" + ENV_SET "${env}" "${a1}" "${r}" return ;; macroexpand) MACROEXPAND "${a1}" "${env}" return ;; - sh*) MACROEXPAND "${a1}" "${env}" - EVAL "${r}" "${env}" + sh*) EVAL "${a1}" "${env}" local output="" local line="" while read line; do @@ -166,8 +165,7 @@ EVAL () { done < <(eval ${ANON["${r}"]}) _string "${output%\\n}" return ;; - try*) MACROEXPAND "${a1}" "${env}" - EVAL "${r}" "${env}" + try*) EVAL "${a1}" "${env}" [[ -z "${__ERROR}" ]] && return _nth "${a2}" 0; local a20="${r}" if [ "${ANON["${a20}"]}" == "catch__STAR__" ]; then @@ -177,8 +175,7 @@ EVAL () { ENV "${env}" "${binds}" "${__ERROR}" local try_env="${r}" __ERROR= - MACROEXPAND "${a22}" "${try_env}" - EVAL "${r}" "${try_env}" + EVAL "${a22}" "${try_env}" fi # if no catch* clause, just propagate __ERROR return ;; do) _count "${ast}" @@ -190,6 +187,7 @@ EVAL () { # Continue loop ;; if) EVAL "${a1}" "${env}" + [[ "${__ERROR}" ]] && return 1 if [[ "${r}" == "${__false}" || "${r}" == "${__nil}" ]]; then # eval false form _nth "${ast}" 3; local a3="${r}" @@ -251,13 +249,18 @@ REP () { } # core.sh: defined using bash -_fref () { _function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } +_fref () { + _symbol "${1}"; local sym="${r}" + _function "${2} \"\${@}\"" + ENV_SET "${REPL_ENV}" "${sym}" "${r}" +} for n in "${!core_ns[@]}"; do _fref "${n}" "${core_ns["${n}"]}"; done _eval () { EVAL "${1}" "${REPL_ENV}"; } _fref "eval" _eval _list; argv="${r}" for _arg in "${@:2}"; do _string "${_arg}"; _conj! "${argv}" "${r}"; done -ENV_SET "${REPL_ENV}" "__STAR__ARGV__STAR__" "${argv}"; +_symbol "__STAR__ARGV__STAR__" +ENV_SET "${REPL_ENV}" "${r}" "${argv}"; # core.mal: defined using the language itself REP "(def! *host-language* \"bash\")" diff --git a/bash/types.sh b/bash/types.sh index 5cdc14a0..4c2c824a 100644 --- a/bash/types.sh +++ b/bash/types.sh @@ -8,6 +8,7 @@ __mal_types_included=true declare -A ANON __obj_magic=__5bal7 +__keyw=$(echo -en "\u029e") __obj_hash_code=${__obj_hash_code:-0} __new_obj_hash_code () { @@ -50,7 +51,9 @@ _obj_type () { list) r="list" ;; numb) r="number" ;; func) r="function" ;; - strn) r="string" ;; + strn) + local s="${ANON["${1}"]}" + [[ "${s:0:1}" = "${__keyw}" ]] && r="keyword" || r="string" ;; _nil) r="nil" ;; true) r="true" ;; fals) r="false" ;; @@ -71,7 +74,7 @@ _equal? () { fi fi case "${ot1}" in - string|symbol|number) + string|symbol|keyword|number) [[ "${ANON["${1}"]}" == "${ANON["${2}"]}" ]] ;; list|vector|hash_map) _count "${1}"; local sz1="${r}" @@ -109,6 +112,22 @@ _symbol () { _symbol? () { [[ ${1} =~ ^symb_ ]]; } +# Keywords + +_keyword () { + local k="${1}" + __new_obj_hash_code + r="strn_${r}" + [[ "${1:1:1}" = "${__keyw}" ]] || k="${__keyw}${1}" + ANON["${r}"]="${k//\*/__STAR__}" +} +_keyword? () { + [[ ${1} =~ ^strn_ ]] || return 1 + local s="${ANON["${1}"]}" + [[ "${s:0:1}" = "${__keyw}" ]] +} + + # Numbers _number () { @@ -245,7 +264,7 @@ _sequential? () { _nth () { local temp=(${ANON["${1}"]}) - r=${temp[${2}]} + r="${temp[${2}]}" } _first () { @@ -285,8 +304,12 @@ _conj! () { _count () { - local temp=(${ANON["${1}"]}) - r=${#temp[*]} + if _nil? "${1}"; then + r="0" + else + local temp=(${ANON["${1}"]}) + r=${#temp[*]} + fi } # Slice a sequence object $1 starting at $2 of length $3 diff --git a/c/core.c b/c/core.c index 10c9fc95..8e420e98 100644 --- a/c/core.c +++ b/c/core.c @@ -40,7 +40,23 @@ MalVal *symbol(MalVal *args) { return args; } -MalVal *symbol_Q(MalVal *seq) { return seq->type & MAL_SYMBOL ? &mal_true : &mal_false; } +MalVal *symbol_Q(MalVal *seq) { + return seq->type & MAL_SYMBOL ? &mal_true : &mal_false; } + + +// Keyword functions + +MalVal *keyword(MalVal *args) { + assert_type(args, MAL_STRING, + "keyword called with non-string value"); + return malval_new_keyword(args->val.string); +} + +MalVal *keyword_Q(MalVal *seq) { + return seq->type & MAL_STRING && seq->val.string[0] == '\x7f' + ? &mal_true + : &mal_false; +} // String functions @@ -431,7 +447,7 @@ MalVal *swap_BANG(MalVal *args) { -core_ns_entry core_ns[54] = { +core_ns_entry core_ns[56] = { {"=", (void*(*)(void*))equal_Q, 2}, {"throw", (void*(*)(void*))throw, 1}, {"nil?", (void*(*)(void*))nil_Q, 1}, @@ -439,6 +455,8 @@ core_ns_entry core_ns[54] = { {"false?", (void*(*)(void*))false_Q, 1}, {"symbol", (void*(*)(void*))symbol, 1}, {"symbol?", (void*(*)(void*))symbol_Q, 1}, + {"keyword", (void*(*)(void*))keyword, 1}, + {"keyword?", (void*(*)(void*))keyword_Q, 1}, {"pr-str", (void*(*)(void*))pr_str, -1}, {"str", (void*(*)(void*))str, -1}, diff --git a/c/core.h b/c/core.h index 49f8beec..82070ffe 100644 --- a/c/core.h +++ b/c/core.h @@ -10,6 +10,6 @@ typedef struct { int arg_cnt; } core_ns_entry; -extern core_ns_entry core_ns[54]; +extern core_ns_entry core_ns[56]; #endif diff --git a/c/env.c b/c/env.c index d4b8f320..0114d1e9 100644 --- a/c/env.c +++ b/c/env.c @@ -25,10 +25,10 @@ Env *new_env(Env *outer, MalVal* binds, MalVal *exprs) { 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))); + env_set(e, _nth(binds, i+1), _slice(exprs, i, _count(exprs))); break; } else { - env_set(e, _nth(binds, i)->val.string, _nth(exprs, i)); + env_set(e, _nth(binds, i), _nth(exprs, i)); } } assert(varargs || (binds_len == exprs_len), @@ -39,8 +39,8 @@ Env *new_env(Env *outer, MalVal* binds, MalVal *exprs) { return e; } -Env *env_find(Env *env, char *key) { - void *val = g_hash_table_lookup(env->table, key); +Env *env_find(Env *env, MalVal *key) { + void *val = g_hash_table_lookup(env->table, key->val.string); if (val) { return env; } else if (env->outer) { @@ -50,13 +50,13 @@ Env *env_find(Env *env, char *key) { } } -MalVal *env_get(Env *env, char *key) { +MalVal *env_get(Env *env, MalVal *key) { Env *e = env_find(env, key); - assert(e, "'%s' not found", key); - return g_hash_table_lookup(e->table, key); + assert(e, "'%s' not found", key->val.string); + return g_hash_table_lookup(e->table, key->val.string); } -Env *env_set(Env *env, char *key, MalVal *val) { - g_hash_table_insert(env->table, key, val); +Env *env_set(Env *env, MalVal *key, MalVal *val) { + g_hash_table_insert(env->table, key->val.string, val); return env; } diff --git a/c/printer.c b/c/printer.c index 0669cf60..786d89e6 100644 --- a/c/printer.c +++ b/c/printer.c @@ -5,7 +5,8 @@ char *_pr_str_hash_map(MalVal *obj, int print_readably) { int start = 1; - char *repr = NULL, *repr_tmp1 = NULL, *repr_tmp2 = NULL; + char *repr = NULL, *repr_tmp1 = NULL, *repr_tmp2 = NULL, + *key2 = NULL; GHashTableIter iter; gpointer key, value; @@ -14,14 +15,20 @@ char *_pr_str_hash_map(MalVal *obj, int print_readably) { 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); + if (((char*)key)[0] == '\x7f') { + key2 = g_strdup_printf("%s", (char*)key); + key2[0] = ':'; + } else { + key2 = g_strdup_printf("\"%s\"", (char*)key); + } repr_tmp1 = _pr_str((MalVal*)value, print_readably); if (start) { start = 0; - repr = g_strdup_printf("{\"%s\" %s", (char *)key, repr_tmp1); + repr = g_strdup_printf("{%s %s", (char*)key2, repr_tmp1); } else { repr_tmp2 = repr; - repr = g_strdup_printf("%s \"%s\" %s", repr_tmp2, (char *)key, repr_tmp1); + repr = g_strdup_printf("%s %s %s", repr_tmp2, (char*)key2, repr_tmp1); free(repr_tmp2); } free(repr_tmp1); @@ -70,7 +77,11 @@ char *_pr_str(MalVal *obj, int print_readably) { repr = g_strdup_printf("false"); break; case MAL_STRING: - if (print_readably) { + if (obj->val.string[0] == '\x7f') { + // Keyword + repr = g_strdup_printf("%s", obj->val.string); + repr[0] = ':'; + } else if (print_readably) { char *repr_tmp = g_strescape(obj->val.string, ""); repr = g_strdup_printf("\"%s\"", repr_tmp); free(repr_tmp); @@ -121,7 +132,7 @@ 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(""), + char *repr = g_strdup_printf("%s", ""), *repr2 = NULL; for (i=0; i<_count(args); i++) { MalVal *obj = g_array_index(args->val.array, MalVal*, i); diff --git a/c/reader.c b/c/reader.c index d9b75b7d..ae163216 100644 --- a/c/reader.c +++ b/c/reader.c @@ -122,7 +122,7 @@ MalVal *read_atom(Reader *reader) { token = reader_next(reader); //g_print("read_atom token: %s\n", token); - regex = g_regex_new ("(^-?[0-9]+$)|(^-?[0-9][0-9.]*$)|(^nil$)|(^true$)|(^false$)|^\"(.*)\"$|(^[^\"]*$)", 0, 0, &err); + regex = g_regex_new ("(^-?[0-9]+$)|(^-?[0-9][0-9.]*$)|(^nil$)|(^true$)|(^false$)|^\"(.*)\"$|:(.*)|(^[^\"]*$)", 0, 0, &err); g_regex_match (regex, token, 0, &matchInfo); if (g_match_info_fetch_pos(matchInfo, 1, &pos, NULL) && pos != -1) { @@ -145,8 +145,11 @@ MalVal *read_atom(Reader *reader) { char *str_tmp = replace_str(g_match_info_fetch(matchInfo, 6), "\\\"", "\""); atom = malval_new_string(str_tmp); } else if (g_match_info_fetch_pos(matchInfo, 7, &pos, NULL) && pos != -1) { + //g_print("read_atom keyword\n"); + atom = malval_new_keyword(g_match_info_fetch(matchInfo, 7)); + } else if (g_match_info_fetch_pos(matchInfo, 8, &pos, NULL) && pos != -1) { //g_print("read_atom symbol\n"); - atom = malval_new_symbol(g_match_info_fetch(matchInfo, 7)); + atom = malval_new_symbol(g_match_info_fetch(matchInfo, 8)); } else { malval_free(atom); atom = NULL; diff --git a/c/step3_env.c b/c/step3_env.c index 2f41bc0e..cacf9d72 100644 --- a/c/step3_env.c +++ b/c/step3_env.c @@ -32,7 +32,7 @@ MalVal *eval_ast(MalVal *ast, Env *env) { if (!ast || mal_error) return NULL; if (ast->type == MAL_SYMBOL) { //g_print("EVAL symbol: %s\n", ast->val.string); - return env_get(env, ast->val.string); + return env_get(env, ast); } else if ((ast->type == MAL_LIST) || (ast->type == MAL_VECTOR)) { //g_print("EVAL sequential: %s\n", _pr_str(ast,1)); MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env); @@ -79,7 +79,7 @@ MalVal *EVAL(MalVal *ast, Env *env) { MalVal *a1 = _nth(ast, 1), *a2 = _nth(ast, 2); MalVal *res = EVAL(a2, env); - env_set(env, a1->val.string, res); + env_set(env, a1, res); return res; } else if (strcmp("let*", a0->val.string) == 0) { //g_print("eval apply let*\n"); @@ -95,7 +95,7 @@ MalVal *EVAL(MalVal *ast, Env *env) { key = g_array_index(a1->val.array, MalVal*, i); val = g_array_index(a1->val.array, MalVal*, i+1); assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); - env_set(let_env, key->val.string, EVAL(val, let_env)); + env_set(let_env, key, EVAL(val, let_env)); } return EVAL(a2, let_env); } else { @@ -143,10 +143,10 @@ void init_repl_env() { WRAP_INTEGER_OP(multiply,*) WRAP_INTEGER_OP(divide,/) - env_set(repl_env, "+", (MalVal *)int_plus); - env_set(repl_env, "-", (MalVal *)int_minus); - env_set(repl_env, "*", (MalVal *)int_multiply); - env_set(repl_env, "/", (MalVal *)int_divide); + env_set(repl_env, malval_new_symbol("+"), (MalVal *)int_plus); + env_set(repl_env, malval_new_symbol("-"), (MalVal *)int_minus); + env_set(repl_env, malval_new_symbol("*"), (MalVal *)int_multiply); + env_set(repl_env, malval_new_symbol("/"), (MalVal *)int_divide); } int main() diff --git a/c/step4_if_fn_do.c b/c/step4_if_fn_do.c index 84fb7608..413bcd6b 100644 --- a/c/step4_if_fn_do.c +++ b/c/step4_if_fn_do.c @@ -33,7 +33,7 @@ MalVal *eval_ast(MalVal *ast, Env *env) { if (!ast || mal_error) return NULL; if (ast->type == MAL_SYMBOL) { //g_print("EVAL symbol: %s\n", ast->val.string); - return env_get(env, ast->val.string); + return env_get(env, ast); } else if ((ast->type == MAL_LIST) || (ast->type == MAL_VECTOR)) { //g_print("EVAL sequential: %s\n", _pr_str(ast,1)); MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env); @@ -80,7 +80,8 @@ MalVal *EVAL(MalVal *ast, Env *env) { MalVal *a1 = _nth(ast, 1), *a2 = _nth(ast, 2); MalVal *res = EVAL(a2, env); - env_set(env, a1->val.string, res); + if (mal_error) return NULL; + env_set(env, a1, res); return res; } else if ((a0->type & MAL_SYMBOL) && strcmp("let*", a0->val.string) == 0) { @@ -97,7 +98,7 @@ MalVal *EVAL(MalVal *ast, Env *env) { key = g_array_index(a1->val.array, MalVal*, i); val = g_array_index(a1->val.array, MalVal*, i+1); assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); - env_set(let_env, key->val.string, EVAL(val, let_env)); + env_set(let_env, key, EVAL(val, let_env)); } return EVAL(a2, let_env); } else if ((a0->type & MAL_SYMBOL) && @@ -110,12 +111,11 @@ MalVal *EVAL(MalVal *ast, Env *env) { //g_print("eval apply if\n"); MalVal *a1 = _nth(ast, 1); MalVal *cond = EVAL(a1, env); - if (!ast || mal_error) return NULL; + if (!cond || mal_error) return NULL; if (cond->type & (MAL_FALSE|MAL_NIL)) { // eval false slot form - MalVal *a3 = _nth(ast, 3); - if (a3) { - return EVAL(a3, env); + if (ast->val.array->len > 3) { + return EVAL(_nth(ast, 3), env); } else { return &mal_nil; } @@ -179,7 +179,8 @@ void init_repl_env() { // core.c: defined using C int i; for(i=0; i < (sizeof(core_ns) / sizeof(core_ns[0])); i++) { - env_set(repl_env, core_ns[i].name, + env_set(repl_env, + malval_new_symbol(core_ns[i].name), malval_new_function(core_ns[i].func, core_ns[i].arg_cnt)); } diff --git a/c/step5_tco.c b/c/step5_tco.c index edca21b2..a1762c8a 100644 --- a/c/step5_tco.c +++ b/c/step5_tco.c @@ -33,7 +33,7 @@ MalVal *eval_ast(MalVal *ast, Env *env) { if (!ast || mal_error) return NULL; if (ast->type == MAL_SYMBOL) { //g_print("EVAL symbol: %s\n", ast->val.string); - return env_get(env, ast->val.string); + return env_get(env, ast); } else if ((ast->type == MAL_LIST) || (ast->type == MAL_VECTOR)) { //g_print("EVAL sequential: %s\n", _pr_str(ast,1)); MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env); @@ -82,7 +82,8 @@ MalVal *EVAL(MalVal *ast, Env *env) { MalVal *a1 = _nth(ast, 1), *a2 = _nth(ast, 2); MalVal *res = EVAL(a2, env); - env_set(env, a1->val.string, res); + if (mal_error) return NULL; + env_set(env, a1, res); return res; } else if ((a0->type & MAL_SYMBOL) && strcmp("let*", a0->val.string) == 0) { @@ -99,7 +100,7 @@ MalVal *EVAL(MalVal *ast, Env *env) { key = g_array_index(a1->val.array, MalVal*, i); val = g_array_index(a1->val.array, MalVal*, i+1); assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); - env_set(let_env, key->val.string, EVAL(val, let_env)); + env_set(let_env, key, EVAL(val, let_env)); } ast = a2; env = let_env; @@ -118,8 +119,9 @@ MalVal *EVAL(MalVal *ast, Env *env) { if (!cond || mal_error) return NULL; if (cond->type & (MAL_FALSE|MAL_NIL)) { // eval false slot form - ast = _nth(ast, 3); - if (!ast) { + if (ast->val.array->len > 3) { + ast = _nth(ast, 3); + } else { return &mal_nil; } } else { @@ -190,7 +192,8 @@ void init_repl_env() { // core.c: defined using C int i; for(i=0; i < (sizeof(core_ns) / sizeof(core_ns[0])); i++) { - env_set(repl_env, core_ns[i].name, + env_set(repl_env, + malval_new_symbol(core_ns[i].name), malval_new_function(core_ns[i].func, core_ns[i].arg_cnt)); } diff --git a/c/step6_file.c b/c/step6_file.c index 9ff62a92..409e221c 100644 --- a/c/step6_file.c +++ b/c/step6_file.c @@ -33,7 +33,7 @@ MalVal *eval_ast(MalVal *ast, Env *env) { if (!ast || mal_error) return NULL; if (ast->type == MAL_SYMBOL) { //g_print("EVAL symbol: %s\n", ast->val.string); - return env_get(env, ast->val.string); + return env_get(env, ast); } else if ((ast->type == MAL_LIST) || (ast->type == MAL_VECTOR)) { //g_print("EVAL sequential: %s\n", _pr_str(ast,1)); MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env); @@ -82,7 +82,8 @@ MalVal *EVAL(MalVal *ast, Env *env) { MalVal *a1 = _nth(ast, 1), *a2 = _nth(ast, 2); MalVal *res = EVAL(a2, env); - env_set(env, a1->val.string, res); + if (mal_error) return NULL; + env_set(env, a1, res); return res; } else if ((a0->type & MAL_SYMBOL) && strcmp("let*", a0->val.string) == 0) { @@ -99,7 +100,7 @@ MalVal *EVAL(MalVal *ast, Env *env) { key = g_array_index(a1->val.array, MalVal*, i); val = g_array_index(a1->val.array, MalVal*, i+1); assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); - env_set(let_env, key->val.string, EVAL(val, let_env)); + env_set(let_env, key, EVAL(val, let_env)); } ast = a2; env = let_env; @@ -118,8 +119,9 @@ MalVal *EVAL(MalVal *ast, Env *env) { if (!cond || mal_error) return NULL; if (cond->type & (MAL_FALSE|MAL_NIL)) { // eval false slot form - ast = _nth(ast, 3); - if (!ast) { + if (ast->val.array->len > 3) { + ast = _nth(ast, 3); + } else { return &mal_nil; } } else { @@ -190,11 +192,13 @@ void init_repl_env(int argc, char *argv[]) { // core.c: defined using C int i; for(i=0; i < (sizeof(core_ns) / sizeof(core_ns[0])); i++) { - env_set(repl_env, core_ns[i].name, + env_set(repl_env, + malval_new_symbol(core_ns[i].name), malval_new_function(core_ns[i].func, core_ns[i].arg_cnt)); } MalVal *do_eval(MalVal *ast) { return EVAL(ast, repl_env); } - env_set(repl_env, "eval", + env_set(repl_env, + malval_new_symbol("eval"), malval_new_function((void*(*)(void *))do_eval, 1)); MalVal *_argv = _listX(0); @@ -202,7 +206,7 @@ void init_repl_env(int argc, char *argv[]) { MalVal *arg = malval_new_string(argv[i]); g_array_append_val(_argv->val.array, arg); } - env_set(repl_env, "*ARGV*", _argv); + env_set(repl_env, malval_new_symbol("*ARGV*"), _argv); // core.mal: defined using the language itself RE(repl_env, "", "(def! not (fn* (a) (if a false true)))"); diff --git a/c/step7_quote.c b/c/step7_quote.c index d0d1d3d4..73250e38 100644 --- a/c/step7_quote.c +++ b/c/step7_quote.c @@ -60,7 +60,7 @@ MalVal *eval_ast(MalVal *ast, Env *env) { if (!ast || mal_error) return NULL; if (ast->type == MAL_SYMBOL) { //g_print("EVAL symbol: %s\n", ast->val.string); - return env_get(env, ast->val.string); + return env_get(env, ast); } else if ((ast->type == MAL_LIST) || (ast->type == MAL_VECTOR)) { //g_print("EVAL sequential: %s\n", _pr_str(ast,1)); MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env); @@ -109,7 +109,8 @@ MalVal *EVAL(MalVal *ast, Env *env) { MalVal *a1 = _nth(ast, 1), *a2 = _nth(ast, 2); MalVal *res = EVAL(a2, env); - env_set(env, a1->val.string, res); + if (mal_error) return NULL; + env_set(env, a1, res); return res; } else if ((a0->type & MAL_SYMBOL) && strcmp("let*", a0->val.string) == 0) { @@ -126,7 +127,7 @@ MalVal *EVAL(MalVal *ast, Env *env) { key = g_array_index(a1->val.array, MalVal*, i); val = g_array_index(a1->val.array, MalVal*, i+1); assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); - env_set(let_env, key->val.string, EVAL(val, let_env)); + env_set(let_env, key, EVAL(val, let_env)); } ast = a2; env = let_env; @@ -155,8 +156,9 @@ MalVal *EVAL(MalVal *ast, Env *env) { if (!cond || mal_error) return NULL; if (cond->type & (MAL_FALSE|MAL_NIL)) { // eval false slot form - ast = _nth(ast, 3); - if (!ast) { + if (ast->val.array->len > 3) { + ast = _nth(ast, 3); + } else { return &mal_nil; } } else { @@ -227,11 +229,13 @@ void init_repl_env(int argc, char *argv[]) { // core.c: defined using C int i; for(i=0; i < (sizeof(core_ns) / sizeof(core_ns[0])); i++) { - env_set(repl_env, core_ns[i].name, + env_set(repl_env, + malval_new_symbol(core_ns[i].name), malval_new_function(core_ns[i].func, core_ns[i].arg_cnt)); } MalVal *do_eval(MalVal *ast) { return EVAL(ast, repl_env); } - env_set(repl_env, "eval", + env_set(repl_env, + malval_new_symbol("eval"), malval_new_function((void*(*)(void *))do_eval, 1)); MalVal *_argv = _listX(0); @@ -239,7 +243,7 @@ void init_repl_env(int argc, char *argv[]) { MalVal *arg = malval_new_string(argv[i]); g_array_append_val(_argv->val.array, arg); } - env_set(repl_env, "*ARGV*", _argv); + env_set(repl_env, malval_new_symbol("*ARGV*"), _argv); // core.mal: defined using the language itself RE(repl_env, "", "(def! not (fn* (a) (if a false true)))"); diff --git a/c/step8_macros.c b/c/step8_macros.c index 3558cafb..55c6988f 100644 --- a/c/step8_macros.c +++ b/c/step8_macros.c @@ -61,15 +61,15 @@ int is_macro_call(MalVal *ast, Env *env) { if (!ast || ast->type != MAL_LIST) { return 0; } MalVal *a0 = _nth(ast, 0); return (a0->type & MAL_SYMBOL) && - env_find(env, a0->val.string) && - env_get(env, a0->val.string)->ismacro; + env_find(env, a0) && + env_get(env, a0)->ismacro; } MalVal *macroexpand(MalVal *ast, Env *env) { if (!ast || mal_error) return NULL; while (is_macro_call(ast, env)) { MalVal *a0 = _nth(ast, 0); - MalVal *mac = env_get(env, a0->val.string); + MalVal *mac = env_get(env, a0); // TODO: this is weird and limits it to 20. FIXME ast = _apply(mac, _rest(ast)); } @@ -80,7 +80,7 @@ MalVal *eval_ast(MalVal *ast, Env *env) { if (!ast || mal_error) return NULL; if (ast->type == MAL_SYMBOL) { //g_print("EVAL symbol: %s\n", ast->val.string); - return env_get(env, ast->val.string); + return env_get(env, ast); } else if ((ast->type == MAL_LIST) || (ast->type == MAL_VECTOR)) { //g_print("EVAL sequential: %s\n", _pr_str(ast,1)); MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env); @@ -133,7 +133,8 @@ MalVal *EVAL(MalVal *ast, Env *env) { MalVal *a1 = _nth(ast, 1), *a2 = _nth(ast, 2); MalVal *res = EVAL(a2, env); - env_set(env, a1->val.string, res); + if (mal_error) return NULL; + env_set(env, a1, res); return res; } else if ((a0->type & MAL_SYMBOL) && strcmp("let*", a0->val.string) == 0) { @@ -150,7 +151,7 @@ MalVal *EVAL(MalVal *ast, Env *env) { key = g_array_index(a1->val.array, MalVal*, i); val = g_array_index(a1->val.array, MalVal*, i+1); assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); - env_set(let_env, key->val.string, EVAL(val, let_env)); + env_set(let_env, key, EVAL(val, let_env)); } ast = a2; env = let_env; @@ -171,8 +172,9 @@ MalVal *EVAL(MalVal *ast, Env *env) { MalVal *a1 = _nth(ast, 1), *a2 = _nth(ast, 2); MalVal *res = EVAL(a2, env); + if (mal_error) return NULL; res->ismacro = TRUE; - env_set(env, a1->val.string, res); + env_set(env, a1, res); return res; } else if ((a0->type & MAL_SYMBOL) && strcmp("macroexpand", a0->val.string) == 0) { @@ -193,8 +195,9 @@ MalVal *EVAL(MalVal *ast, Env *env) { if (!cond || mal_error) return NULL; if (cond->type & (MAL_FALSE|MAL_NIL)) { // eval false slot form - ast = _nth(ast, 3); - if (!ast) { + if (ast->val.array->len > 3) { + ast = _nth(ast, 3); + } else { return &mal_nil; } } else { @@ -266,11 +269,13 @@ void init_repl_env(int argc, char *argv[]) { // core.c: defined using C int i; for(i=0; i < (sizeof(core_ns) / sizeof(core_ns[0])); i++) { - env_set(repl_env, core_ns[i].name, + env_set(repl_env, + malval_new_symbol(core_ns[i].name), malval_new_function(core_ns[i].func, core_ns[i].arg_cnt)); } MalVal *do_eval(MalVal *ast) { return EVAL(ast, repl_env); } - env_set(repl_env, "eval", + env_set(repl_env, + malval_new_symbol("eval"), malval_new_function((void*(*)(void *))do_eval, 1)); MalVal *_argv = _listX(0); @@ -278,7 +283,7 @@ void init_repl_env(int argc, char *argv[]) { MalVal *arg = malval_new_string(argv[i]); g_array_append_val(_argv->val.array, arg); } - env_set(repl_env, "*ARGV*", _argv); + env_set(repl_env, malval_new_symbol("*ARGV*"), _argv); // core.mal: defined using the language itself RE(repl_env, "", "(def! not (fn* (a) (if a false true)))"); diff --git a/c/step9_try.c b/c/step9_try.c index 395a7f00..ffba2f96 100644 --- a/c/step9_try.c +++ b/c/step9_try.c @@ -62,15 +62,15 @@ int is_macro_call(MalVal *ast, Env *env) { if (!ast || ast->type != MAL_LIST) { return 0; } MalVal *a0 = _nth(ast, 0); return (a0->type & MAL_SYMBOL) && - env_find(env, a0->val.string) && - env_get(env, a0->val.string)->ismacro; + env_find(env, a0) && + env_get(env, a0)->ismacro; } MalVal *macroexpand(MalVal *ast, Env *env) { if (!ast || mal_error) return NULL; while (is_macro_call(ast, env)) { MalVal *a0 = _nth(ast, 0); - MalVal *mac = env_get(env, a0->val.string); + MalVal *mac = env_get(env, a0); // TODO: this is weird and limits it to 20. FIXME ast = _apply(mac, _rest(ast)); } @@ -81,7 +81,7 @@ MalVal *eval_ast(MalVal *ast, Env *env) { if (!ast || mal_error) return NULL; if (ast->type == MAL_SYMBOL) { //g_print("EVAL symbol: %s\n", ast->val.string); - return env_get(env, ast->val.string); + return env_get(env, ast); } else if ((ast->type == MAL_LIST) || (ast->type == MAL_VECTOR)) { //g_print("EVAL sequential: %s\n", _pr_str(ast,1)); MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env); @@ -134,7 +134,8 @@ MalVal *EVAL(MalVal *ast, Env *env) { MalVal *a1 = _nth(ast, 1), *a2 = _nth(ast, 2); MalVal *res = EVAL(a2, env); - env_set(env, a1->val.string, res); + if (mal_error) return NULL; + env_set(env, a1, res); return res; } else if ((a0->type & MAL_SYMBOL) && strcmp("let*", a0->val.string) == 0) { @@ -151,7 +152,7 @@ MalVal *EVAL(MalVal *ast, Env *env) { key = g_array_index(a1->val.array, MalVal*, i); val = g_array_index(a1->val.array, MalVal*, i+1); assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); - env_set(let_env, key->val.string, EVAL(val, let_env)); + env_set(let_env, key, EVAL(val, let_env)); } ast = a2; env = let_env; @@ -172,8 +173,9 @@ MalVal *EVAL(MalVal *ast, Env *env) { MalVal *a1 = _nth(ast, 1), *a2 = _nth(ast, 2); MalVal *res = EVAL(a2, env); + if (mal_error) return NULL; res->ismacro = TRUE; - env_set(env, a1->val.string, res); + env_set(env, a1, res); return res; } else if ((a0->type & MAL_SYMBOL) && strcmp("macroexpand", a0->val.string) == 0) { @@ -215,8 +217,9 @@ MalVal *EVAL(MalVal *ast, Env *env) { if (!cond || mal_error) return NULL; if (cond->type & (MAL_FALSE|MAL_NIL)) { // eval false slot form - ast = _nth(ast, 3); - if (!ast) { + if (ast->val.array->len > 3) { + ast = _nth(ast, 3); + } else { return &mal_nil; } } else { @@ -288,11 +291,13 @@ void init_repl_env(int argc, char *argv[]) { // core.c: defined using C int i; for(i=0; i < (sizeof(core_ns) / sizeof(core_ns[0])); i++) { - env_set(repl_env, core_ns[i].name, + env_set(repl_env, + malval_new_symbol(core_ns[i].name), malval_new_function(core_ns[i].func, core_ns[i].arg_cnt)); } MalVal *do_eval(MalVal *ast) { return EVAL(ast, repl_env); } - env_set(repl_env, "eval", + env_set(repl_env, + malval_new_symbol("eval"), malval_new_function((void*(*)(void *))do_eval, 1)); MalVal *_argv = _listX(0); @@ -300,10 +305,9 @@ void init_repl_env(int argc, char *argv[]) { MalVal *arg = malval_new_string(argv[i]); g_array_append_val(_argv->val.array, arg); } - env_set(repl_env, "*ARGV*", _argv); + env_set(repl_env, malval_new_symbol("*ARGV*"), _argv); // core.mal: defined using the language itself - RE(repl_env, "", "(def! *host-language* \"c\")"); RE(repl_env, "", "(def! not (fn* (a) (if a false true)))"); RE(repl_env, "", "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); @@ -328,7 +332,6 @@ int main(int argc, char *argv[]) } // repl loop - RE(repl_env, "", "(println (str \"Mal [\" *host-language* \"]\"))"); for(;;) { exp = RE(repl_env, prompt, NULL); if (mal_error && strcmp("EOF", mal_error->val.string) == 0) { diff --git a/c/stepA_interop.c b/c/stepA_interop.c index b4b74314..05e9f651 100644 --- a/c/stepA_interop.c +++ b/c/stepA_interop.c @@ -62,15 +62,15 @@ int is_macro_call(MalVal *ast, Env *env) { if (!ast || ast->type != MAL_LIST) { return 0; } MalVal *a0 = _nth(ast, 0); return (a0->type & MAL_SYMBOL) && - env_find(env, a0->val.string) && - env_get(env, a0->val.string)->ismacro; + env_find(env, a0) && + env_get(env, a0)->ismacro; } MalVal *macroexpand(MalVal *ast, Env *env) { if (!ast || mal_error) return NULL; while (is_macro_call(ast, env)) { MalVal *a0 = _nth(ast, 0); - MalVal *mac = env_get(env, a0->val.string); + MalVal *mac = env_get(env, a0); // TODO: this is weird and limits it to 20. FIXME ast = _apply(mac, _rest(ast)); } @@ -81,7 +81,7 @@ MalVal *eval_ast(MalVal *ast, Env *env) { if (!ast || mal_error) return NULL; if (ast->type == MAL_SYMBOL) { //g_print("EVAL symbol: %s\n", ast->val.string); - return env_get(env, ast->val.string); + return env_get(env, ast); } else if ((ast->type == MAL_LIST) || (ast->type == MAL_VECTOR)) { //g_print("EVAL sequential: %s\n", _pr_str(ast,1)); MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env); @@ -134,7 +134,8 @@ MalVal *EVAL(MalVal *ast, Env *env) { MalVal *a1 = _nth(ast, 1), *a2 = _nth(ast, 2); MalVal *res = EVAL(a2, env); - env_set(env, a1->val.string, res); + if (mal_error) return NULL; + env_set(env, a1, res); return res; } else if ((a0->type & MAL_SYMBOL) && strcmp("let*", a0->val.string) == 0) { @@ -151,7 +152,7 @@ MalVal *EVAL(MalVal *ast, Env *env) { key = g_array_index(a1->val.array, MalVal*, i); val = g_array_index(a1->val.array, MalVal*, i+1); assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); - env_set(let_env, key->val.string, EVAL(val, let_env)); + env_set(let_env, key, EVAL(val, let_env)); } ast = a2; env = let_env; @@ -172,8 +173,9 @@ MalVal *EVAL(MalVal *ast, Env *env) { MalVal *a1 = _nth(ast, 1), *a2 = _nth(ast, 2); MalVal *res = EVAL(a2, env); + if (mal_error) return NULL; res->ismacro = TRUE; - env_set(env, a1->val.string, res); + env_set(env, a1, res); return res; } else if ((a0->type & MAL_SYMBOL) && strcmp("macroexpand", a0->val.string) == 0) { @@ -220,8 +222,9 @@ MalVal *EVAL(MalVal *ast, Env *env) { if (!cond || mal_error) return NULL; if (cond->type & (MAL_FALSE|MAL_NIL)) { // eval false slot form - ast = _nth(ast, 3); - if (!ast) { + if (ast->val.array->len > 3) { + ast = _nth(ast, 3); + } else { return &mal_nil; } } else { @@ -293,11 +296,13 @@ void init_repl_env(int argc, char *argv[]) { // core.c: defined using C int i; for(i=0; i < (sizeof(core_ns) / sizeof(core_ns[0])); i++) { - env_set(repl_env, core_ns[i].name, + env_set(repl_env, + malval_new_symbol(core_ns[i].name), malval_new_function(core_ns[i].func, core_ns[i].arg_cnt)); } MalVal *do_eval(MalVal *ast) { return EVAL(ast, repl_env); } - env_set(repl_env, "eval", + env_set(repl_env, + malval_new_symbol("eval"), malval_new_function((void*(*)(void *))do_eval, 1)); MalVal *_argv = _listX(0); @@ -305,7 +310,7 @@ void init_repl_env(int argc, char *argv[]) { MalVal *arg = malval_new_string(argv[i]); g_array_append_val(_argv->val.array, arg); } - env_set(repl_env, "*ARGV*", _argv); + env_set(repl_env, malval_new_symbol("*ARGV*"), _argv); // core.mal: defined using the language itself RE(repl_env, "", "(def! *host-language* \"c\")"); diff --git a/c/types.c b/c/types.c index 3f1771ba..a6c84929 100644 --- a/c/types.c +++ b/c/types.c @@ -105,6 +105,12 @@ MalVal *malval_new_symbol(char *val) { return mv; } +MalVal *malval_new_keyword(char *val) { + MalVal *mv = malval_new(MAL_STRING, NULL); + mv->val.string = g_strdup_printf("\x7f%s", val); + return mv; +} + MalVal *malval_new_list(MalType type, GArray *val) { MalVal *mv = malval_new(type, NULL); mv->val.array = val; @@ -422,7 +428,7 @@ MalVal *_nth(MalVal *seq, int idx) { assert_type(seq, MAL_LIST|MAL_VECTOR, "_nth called with non-sequential"); if (idx >= _count(seq)) { - return &mal_nil; + abort("nth: index out of range"); } return g_array_index(seq->val.array, MalVal*, idx); } diff --git a/c/types.h b/c/types.h index aa6c5e30..80a40ac7 100644 --- a/c/types.h +++ b/c/types.h @@ -15,9 +15,9 @@ typedef struct Env { } 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); +Env *env_find(Env *env, struct MalVal *key); +struct MalVal *env_get(Env *env, struct MalVal *key); +Env *env_set(Env *env, struct MalVal *key, struct MalVal *val); // Utility functiosn @@ -130,6 +130,7 @@ MalVal *malval_new_integer(gint64 val); MalVal *malval_new_float(gdouble val); MalVal *malval_new_string(char *val); MalVal *malval_new_symbol(char *val); +MalVal *malval_new_keyword(char *val); MalVal *malval_new_list(MalType type, GArray *val); MalVal *malval_new_hash_map(GHashTable *val); MalVal *malval_new_atom(MalVal *val); diff --git a/clojure/src/core.clj b/clojure/src/core.clj index 6b0d9b5e..4438e29d 100644 --- a/clojure/src/core.clj +++ b/clojure/src/core.clj @@ -5,11 +5,6 @@ (defn mal_throw [obj] (throw (ex-info "mal exception" {:data obj}))) -;; Number functions - -(defn time-ms [] - (System/currentTimeMillis)) - ;; Metadata functions ;; - store metadata at :meta key of the real metadata (defn mal_with_meta [obj m] @@ -19,10 +14,6 @@ (defn mal_meta [obj] (:meta (meta obj))) -;; Atom functions -(defn atom? [atm] - (= (type atm) clojure.lang.Atom)) - ;; core_ns is core namespaces functions (def core_ns [['= =] @@ -30,7 +21,10 @@ ['nil? nil?] ['true? true?] ['false? false?] + ['symbol symbol] ['symbol? symbol?] + ['keyword keyword] + ['keyword? keyword?] ['pr-str pr-str] ['str str] @@ -47,7 +41,7 @@ ['- -] ['* *] ['/ /] - ['time-ms time-ms] + ['time-ms (fn time-ms [] (System/currentTimeMillis))] ['list list] ['list? seq?] @@ -59,8 +53,8 @@ ['dissoc dissoc] ['get get] ['contains? contains?] - ['keys keys] - ['vals vals] + ['keys (fn [hm] (let [ks (keys hm)] (if (nil? ks) '() ks)))] + ['vals (fn [hm] (let [vs (vals hm)] (if (nil? vs) '() vs)))] ['sequential? sequential?] ['cons cons] @@ -77,7 +71,7 @@ ['with-meta mal_with_meta] ['meta mal_meta] ['atom atom] - ['atom? atom?] + ['atom? (fn atom? [atm] (= (type atm) clojure.lang.Atom))] ['deref deref] ['reset! reset!] ['swap! swap!]]) diff --git a/coffee/core.coffee b/coffee/core.coffee index d01e76fd..8bb6958c 100644 --- a/coffee/core.coffee +++ b/coffee/core.coffee @@ -32,6 +32,8 @@ exports.ns = { 'false?': types._false_Q, 'symbol': types._symbol, 'symbol?': types._symbol_Q, + 'keyword': types._keyword, + 'keyword?': types._keyword_Q, 'pr-str': (a...) -> a.map((exp) -> _pr_str(exp,true)).join(" "), 'str': (a...) -> a.map((exp) -> _pr_str(exp,false)).join(""), @@ -66,11 +68,12 @@ exports.ns = { 'sequential?': types._sequential_Q, 'cons': (a,b) -> [a].concat(b), 'concat': (a=[],b...) -> a.concat(b...), - 'nth': (a,b) -> if a.length > b then a[b] else null, + 'nth': (a,b) -> if a.length > b then a[b] else + throw new Error "nth: index out of bounds", 'first': (a) -> if a.length > 0 then a[0] else null, 'rest': (a) -> a[1..], 'empty?': (a) -> a.length == 0, - 'count': (a) -> a.length, + 'count': (a) -> if a == null then 0 else a.length, 'apply': (a,b...) -> a(b[0..-2].concat(b[b.length-1])...), 'map': (a,b) -> b.map((x) -> a(x)), 'conj': conj, diff --git a/coffee/env.coffee b/coffee/env.coffee index 667e4675..80fbf122 100644 --- a/coffee/env.coffee +++ b/coffee/env.coffee @@ -12,13 +12,20 @@ exports.Env = class Env else @data[b.name] = exprs[i] find: (key) -> - if key of @data then @ + if not types._symbol_Q(key) + throw new Error("env.find key must be symbol") + if key.name of @data then @ else if @outer then @outer.find(key) else null - set: (key, value) -> @data[key] = value + set: (key, value) -> + if not types._symbol_Q(key) + throw new Error("env.set key must be symbol") + @data[key.name] = value get: (key) -> + if not types._symbol_Q(key) + throw new Error("env.get key must be symbol") env = @find(key) - throw new Error("'" + key + "' not found") if !env - env.data[key] + throw new Error("'" + key.name + "' not found") if !env + env.data[key.name] # vim: ts=2:sw=2 diff --git a/coffee/printer.coffee b/coffee/printer.coffee index 78444161..9f56e2e3 100644 --- a/coffee/printer.coffee +++ b/coffee/printer.coffee @@ -16,6 +16,7 @@ exports._pr_str = _pr_str = (obj, print_readably=true) -> .replace(/"/g, '\\"') .replace(/\n/g, '\\n')) + '"' else obj + when 'keyword' then ":" + obj.slice(1) when 'symbol' then obj.name when 'nil' then 'nil' when 'atom' then "(atom " + _pr_str(obj.val,_r) + ")" diff --git a/coffee/reader.coffee b/coffee/reader.coffee index 5882b43b..83d24d23 100644 --- a/coffee/reader.coffee +++ b/coffee/reader.coffee @@ -1,7 +1,5 @@ types = require "./types.coffee" -[_symbol, _vector, _hash_map] = [types._symbol, - types._vector, - types._hash_map] +_symbol = types._symbol class Reader @@ -28,6 +26,7 @@ read_atom = (rdr) -> token.slice(1, token.length-1) .replace(/\\"/g, '"') .replace(/\\n/g, "\n") + else if token[0] == ':' then types._keyword(token[1..]) else if token == "nil" then null else if token == "true" then true else if token == "false" then false @@ -44,10 +43,10 @@ read_list = (rdr, start='(', end=')') -> ast read_vector = (rdr) -> - _vector(read_list(rdr, '[', ']')...) + types._vector(read_list(rdr, '[', ']')...) read_hash_map = (rdr) -> - _hash_map(read_list(rdr, '{', '}')...) + types._hash_map(read_list(rdr, '{', '}')...) read_form = (rdr) -> token = rdr.peek() diff --git a/coffee/step3_env.coffee b/coffee/step3_env.coffee index a5398e78..1446197a 100644 --- a/coffee/step3_env.coffee +++ b/coffee/step3_env.coffee @@ -9,7 +9,7 @@ READ = (str) -> reader.read_str str # eval eval_ast = (ast, env) -> - if types._symbol_Q(ast) then env.get ast.name + if types._symbol_Q(ast) then env.get ast else if types._list_Q(ast) then ast.map((a) -> EVAL(a, env)) else if types._vector_Q(ast) types._vector(ast.map((a) -> EVAL(a, env))...) @@ -27,11 +27,11 @@ EVAL = (ast, env) -> [a0, a1, a2, a3] = ast switch a0.name when "def!" - env.set(a1.name, EVAL(a2, env)) + env.set(a1, EVAL(a2, env)) when "let*" let_env = new Env(env) for k,i in a1 when i %% 2 == 0 - let_env.set(a1[i].name, EVAL(a1[i+1], let_env)) + let_env.set(a1[i], EVAL(a1[i+1], let_env)) EVAL(a2, let_env) else [f, args...] = eval_ast ast, env @@ -45,10 +45,10 @@ PRINT = (exp) -> printer._pr_str exp, true repl_env = new Env() rep = (str) -> PRINT(EVAL(READ(str), repl_env)) -repl_env.set "+", (a,b) -> a+b -repl_env.set "-", (a,b) -> a-b -repl_env.set "*", (a,b) -> a*b -repl_env.set "/", (a,b) -> a/b +repl_env.set types._symbol("+"), (a,b) -> a+b +repl_env.set types._symbol("-"), (a,b) -> a-b +repl_env.set types._symbol("*"), (a,b) -> a*b +repl_env.set types._symbol("/"), (a,b) -> a/b # repl loop while (line = readline.readline("user> ")) != null diff --git a/coffee/step4_if_fn_do.coffee b/coffee/step4_if_fn_do.coffee index 037b58a4..ef334784 100644 --- a/coffee/step4_if_fn_do.coffee +++ b/coffee/step4_if_fn_do.coffee @@ -10,7 +10,7 @@ READ = (str) -> reader.read_str str # eval eval_ast = (ast, env) -> - if types._symbol_Q(ast) then env.get ast.name + if types._symbol_Q(ast) then env.get ast else if types._list_Q(ast) then ast.map((a) -> EVAL(a, env)) else if types._vector_Q(ast) types._vector(ast.map((a) -> EVAL(a, env))...) @@ -28,11 +28,11 @@ EVAL = (ast, env) -> [a0, a1, a2, a3] = ast switch a0.name when "def!" - env.set(a1.name, EVAL(a2, env)) + env.set(a1, EVAL(a2, env)) when "let*" let_env = new Env(env) for k,i in a1 when i %% 2 == 0 - let_env.set(a1[i].name, EVAL(a1[i+1], let_env)) + let_env.set(a1[i], EVAL(a1[i+1], let_env)) EVAL(a2, let_env) when "do" el = eval_ast(ast[1..], env) @@ -58,7 +58,7 @@ repl_env = new Env() rep = (str) -> PRINT(EVAL(READ(str), repl_env)) # core.coffee: defined using CoffeeScript -repl_env.set k, v for k,v of core.ns +repl_env.set types._symbol(k), v for k,v of core.ns # core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); diff --git a/coffee/step5_tco.coffee b/coffee/step5_tco.coffee index 4003e796..fa5acedd 100644 --- a/coffee/step5_tco.coffee +++ b/coffee/step5_tco.coffee @@ -10,7 +10,7 @@ READ = (str) -> reader.read_str str # eval eval_ast = (ast, env) -> - if types._symbol_Q(ast) then env.get ast.name + if types._symbol_Q(ast) then env.get ast else if types._list_Q(ast) then ast.map((a) -> EVAL(a, env)) else if types._vector_Q(ast) types._vector(ast.map((a) -> EVAL(a, env))...) @@ -29,11 +29,11 @@ EVAL = (ast, env) -> [a0, a1, a2, a3] = ast switch a0.name when "def!" - return env.set(a1.name, EVAL(a2, env)) + return env.set(a1, EVAL(a2, env)) when "let*" let_env = new Env(env) for k,i in a1 when i %% 2 == 0 - let_env.set(a1[i].name, EVAL(a1[i+1], let_env)) + let_env.set(a1[i], EVAL(a1[i+1], let_env)) ast = a2 env = let_env when "do" @@ -64,7 +64,7 @@ repl_env = new Env() rep = (str) -> PRINT(EVAL(READ(str), repl_env)) # core.coffee: defined using CoffeeScript -repl_env.set k, v for k,v of core.ns +repl_env.set types._symbol(k), v for k,v of core.ns # core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); diff --git a/coffee/step6_file.coffee b/coffee/step6_file.coffee index 449b03e6..feafc634 100644 --- a/coffee/step6_file.coffee +++ b/coffee/step6_file.coffee @@ -10,7 +10,7 @@ READ = (str) -> reader.read_str str # eval eval_ast = (ast, env) -> - if types._symbol_Q(ast) then env.get ast.name + if types._symbol_Q(ast) then env.get ast else if types._list_Q(ast) then ast.map((a) -> EVAL(a, env)) else if types._vector_Q(ast) types._vector(ast.map((a) -> EVAL(a, env))...) @@ -29,11 +29,11 @@ EVAL = (ast, env) -> [a0, a1, a2, a3] = ast switch a0.name when "def!" - return env.set(a1.name, EVAL(a2, env)) + return env.set(a1, EVAL(a2, env)) when "let*" let_env = new Env(env) for k,i in a1 when i %% 2 == 0 - let_env.set(a1[i].name, EVAL(a1[i+1], let_env)) + let_env.set(a1[i], EVAL(a1[i+1], let_env)) ast = a2 env = let_env when "do" @@ -64,16 +64,16 @@ repl_env = new Env() rep = (str) -> PRINT(EVAL(READ(str), repl_env)) # core.coffee: defined using CoffeeScript -repl_env.set k, v for k,v of core.ns -repl_env.set 'eval', (ast) -> EVAL(ast, repl_env) -repl_env.set '*ARGV*', [] +repl_env.set types._symbol(k), v for k,v of core.ns +repl_env.set types._symbol('eval'), (ast) -> EVAL(ast, repl_env) +repl_env.set types._symbol('*ARGV*'), [] # core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); if process? && process.argv.length > 2 - repl_env.set '*ARGV*', process.argv[3..] + repl_env.set types._symbol('*ARGV*'), process.argv[3..] rep('(load-file "' + process.argv[2] + '")') process.exit 0 diff --git a/coffee/step7_quote.coffee b/coffee/step7_quote.coffee index 404e684d..7652a794 100644 --- a/coffee/step7_quote.coffee +++ b/coffee/step7_quote.coffee @@ -22,7 +22,7 @@ quasiquote = (ast) -> eval_ast = (ast, env) -> - if types._symbol_Q(ast) then env.get ast.name + if types._symbol_Q(ast) then env.get ast else if types._list_Q(ast) then ast.map((a) -> EVAL(a, env)) else if types._vector_Q(ast) types._vector(ast.map((a) -> EVAL(a, env))...) @@ -41,11 +41,11 @@ EVAL = (ast, env) -> [a0, a1, a2, a3] = ast switch a0.name when "def!" - return env.set(a1.name, EVAL(a2, env)) + return env.set(a1, EVAL(a2, env)) when "let*" let_env = new Env(env) for k,i in a1 when i %% 2 == 0 - let_env.set(a1[i].name, EVAL(a1[i+1], let_env)) + let_env.set(a1[i], EVAL(a1[i+1], let_env)) ast = a2 env = let_env when "quote" @@ -80,16 +80,16 @@ repl_env = new Env() rep = (str) -> PRINT(EVAL(READ(str), repl_env)) # core.coffee: defined using CoffeeScript -repl_env.set k, v for k,v of core.ns -repl_env.set 'eval', (ast) -> EVAL(ast, repl_env) -repl_env.set '*ARGV*', [] +repl_env.set types._symbol(k), v for k,v of core.ns +repl_env.set types._symbol('eval'), (ast) -> EVAL(ast, repl_env) +repl_env.set types._symbol('*ARGV*'), [] # core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); if process? && process.argv.length > 2 - repl_env.set '*ARGV*', process.argv[3..] + repl_env.set types._symbol('*ARGV*'), process.argv[3..] rep('(load-file "' + process.argv[2] + '")') process.exit 0 diff --git a/coffee/step8_macros.coffee b/coffee/step8_macros.coffee index 39404e79..6150d2e8 100644 --- a/coffee/step8_macros.coffee +++ b/coffee/step8_macros.coffee @@ -21,17 +21,17 @@ quasiquote = (ast) -> is_macro_call = (ast, env) -> return types._list_Q(ast) && types._symbol_Q(ast[0]) && - env.find(ast[0].name) && env.get(ast[0].name).__ismacro__ + env.find(ast[0]) && env.get(ast[0]).__ismacro__ macroexpand = (ast, env) -> while is_macro_call(ast, env) - ast = env.get(ast[0].name)(ast[1..]...) + ast = env.get(ast[0])(ast[1..]...) ast eval_ast = (ast, env) -> - if types._symbol_Q(ast) then env.get ast.name + if types._symbol_Q(ast) then env.get ast else if types._list_Q(ast) then ast.map((a) -> EVAL(a, env)) else if types._vector_Q(ast) types._vector(ast.map((a) -> EVAL(a, env))...) @@ -53,11 +53,11 @@ EVAL = (ast, env) -> [a0, a1, a2, a3] = ast switch a0.name when "def!" - return env.set(a1.name, EVAL(a2, env)) + return env.set(a1, EVAL(a2, env)) when "let*" let_env = new Env(env) for k,i in a1 when i %% 2 == 0 - let_env.set(a1[i].name, EVAL(a1[i+1], let_env)) + let_env.set(a1[i], EVAL(a1[i+1], let_env)) ast = a2 env = let_env when "quote" @@ -67,7 +67,7 @@ EVAL = (ast, env) -> when "defmacro!" f = EVAL(a2, env) f.__ismacro__ = true - return env.set(a1.name, f) + return env.set(a1, f) when "macroexpand" return macroexpand(a1, env) when "do" @@ -98,9 +98,9 @@ repl_env = new Env() rep = (str) -> PRINT(EVAL(READ(str), repl_env)) # core.coffee: defined using CoffeeScript -repl_env.set k, v for k,v of core.ns -repl_env.set 'eval', (ast) -> EVAL(ast, repl_env) -repl_env.set '*ARGV*', [] +repl_env.set types._symbol(k), v for k,v of core.ns +repl_env.set types._symbol('eval'), (ast) -> EVAL(ast, repl_env) +repl_env.set types._symbol('*ARGV*'), [] # core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); @@ -109,7 +109,7 @@ rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if ( rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))") if process? && process.argv.length > 2 - repl_env.set '*ARGV*', process.argv[3..] + repl_env.set types._symbol('*ARGV*'), process.argv[3..] rep('(load-file "' + process.argv[2] + '")') process.exit 0 diff --git a/coffee/step9_try.coffee b/coffee/step9_try.coffee index d7919e71..d8f6f43c 100644 --- a/coffee/step9_try.coffee +++ b/coffee/step9_try.coffee @@ -21,17 +21,17 @@ quasiquote = (ast) -> is_macro_call = (ast, env) -> return types._list_Q(ast) && types._symbol_Q(ast[0]) && - env.find(ast[0].name) && env.get(ast[0].name).__ismacro__ + env.find(ast[0]) && env.get(ast[0]).__ismacro__ macroexpand = (ast, env) -> while is_macro_call(ast, env) - ast = env.get(ast[0].name)(ast[1..]...) + ast = env.get(ast[0])(ast[1..]...) ast eval_ast = (ast, env) -> - if types._symbol_Q(ast) then env.get ast.name + if types._symbol_Q(ast) then env.get ast else if types._list_Q(ast) then ast.map((a) -> EVAL(a, env)) else if types._vector_Q(ast) types._vector(ast.map((a) -> EVAL(a, env))...) @@ -53,11 +53,11 @@ EVAL = (ast, env) -> [a0, a1, a2, a3] = ast switch a0.name when "def!" - return env.set(a1.name, EVAL(a2, env)) + return env.set(a1, EVAL(a2, env)) when "let*" let_env = new Env(env) for k,i in a1 when i %% 2 == 0 - let_env.set(a1[i].name, EVAL(a1[i+1], let_env)) + let_env.set(a1[i], EVAL(a1[i+1], let_env)) ast = a2 env = let_env when "quote" @@ -67,7 +67,7 @@ EVAL = (ast, env) -> when "defmacro!" f = EVAL(a2, env) f.__ismacro__ = true - return env.set(a1.name, f) + return env.set(a1, f) when "macroexpand" return macroexpand(a1, env) when "try*" @@ -106,24 +106,22 @@ repl_env = new Env() rep = (str) -> PRINT(EVAL(READ(str), repl_env)) # core.coffee: defined using CoffeeScript -repl_env.set k, v for k,v of core.ns -repl_env.set 'eval', (ast) -> EVAL(ast, repl_env) -repl_env.set '*ARGV*', [] +repl_env.set types._symbol(k), v for k,v of core.ns +repl_env.set types._symbol('eval'), (ast) -> EVAL(ast, repl_env) +repl_env.set types._symbol('*ARGV*'), [] # core.mal: defined using the language itself -rep("(def! *host-language* \"CoffeeScript\")") rep("(def! not (fn* (a) (if a false true)))"); rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))") rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))") if process? && process.argv.length > 2 - repl_env.set '*ARGV*', process.argv[3..] + repl_env.set types._symbol('*ARGV*'), process.argv[3..] rep('(load-file "' + process.argv[2] + '")') process.exit 0 # repl loop -rep("(println (str \"Mal [\" *host-language* \"]\"))") while (line = readline.readline("user> ")) != null continue if line == "" try diff --git a/coffee/stepA_interop.coffee b/coffee/stepA_interop.coffee index aa9c5cc8..751f9add 100644 --- a/coffee/stepA_interop.coffee +++ b/coffee/stepA_interop.coffee @@ -21,17 +21,17 @@ quasiquote = (ast) -> is_macro_call = (ast, env) -> return types._list_Q(ast) && types._symbol_Q(ast[0]) && - env.find(ast[0].name) && env.get(ast[0].name).__ismacro__ + env.find(ast[0]) && env.get(ast[0]).__ismacro__ macroexpand = (ast, env) -> while is_macro_call(ast, env) - ast = env.get(ast[0].name)(ast[1..]...) + ast = env.get(ast[0])(ast[1..]...) ast eval_ast = (ast, env) -> - if types._symbol_Q(ast) then env.get ast.name + if types._symbol_Q(ast) then env.get ast else if types._list_Q(ast) then ast.map((a) -> EVAL(a, env)) else if types._vector_Q(ast) types._vector(ast.map((a) -> EVAL(a, env))...) @@ -53,11 +53,11 @@ EVAL = (ast, env) -> [a0, a1, a2, a3] = ast switch a0.name when "def!" - return env.set(a1.name, EVAL(a2, env)) + return env.set(a1, EVAL(a2, env)) when "let*" let_env = new Env(env) for k,i in a1 when i %% 2 == 0 - let_env.set(a1[i].name, EVAL(a1[i+1], let_env)) + let_env.set(a1[i], EVAL(a1[i+1], let_env)) ast = a2 env = let_env when "quote" @@ -67,7 +67,7 @@ EVAL = (ast, env) -> when "defmacro!" f = EVAL(a2, env) f.__ismacro__ = true - return env.set(a1.name, f) + return env.set(a1, f) when "macroexpand" return macroexpand(a1, env) when "try*" @@ -112,9 +112,9 @@ repl_env = new Env() rep = (str) -> PRINT(EVAL(READ(str), repl_env)) # core.coffee: defined using CoffeeScript -repl_env.set k, v for k,v of core.ns -repl_env.set 'eval', (ast) -> EVAL(ast, repl_env) -repl_env.set '*ARGV*', [] +repl_env.set types._symbol(k), v for k,v of core.ns +repl_env.set types._symbol('eval'), (ast) -> EVAL(ast, repl_env) +repl_env.set types._symbol('*ARGV*'), [] # core.mal: defined using the language itself rep("(def! *host-language* \"CoffeeScript\")") @@ -124,7 +124,7 @@ rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if ( rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))") if process? && process.argv.length > 2 - repl_env.set '*ARGV*', process.argv[3..] + repl_env.set types._symbol('*ARGV*'), process.argv[3..] rep('(load-file "' + process.argv[2] + '")') process.exit 0 diff --git a/coffee/types.coffee b/coffee/types.coffee index 8982120a..d7320644 100644 --- a/coffee/types.coffee +++ b/coffee/types.coffee @@ -16,7 +16,8 @@ E._obj_type = _obj_type = (obj) -> switch typeof obj when 'number' then 'number' when 'function' then 'function' - when 'string' then 'string' + when 'string' + if obj[0] == '\u029e' then 'keyword' else 'string' else throw new Error "Unknown type '" + typeof(obj) + "'" E._sequential_Q = _sequential_Q = (o) -> _list_Q(o) or _vector_Q(o) @@ -69,6 +70,11 @@ class Symbol E._symbol = (str) -> new Symbol str E._symbol_Q = _symbol_Q = (o) -> o instanceof Symbol +# Keywords +E._keyword = _keyword = (str) -> "\u029e" + str +E._keyword_Q = _keyword_Q = (o) -> + typeof o == 'string' && o[0] == "\u029e" + # Functions E._function = (evalfn, ast, env, params) -> fn = (args...) -> evalfn(ast, new Env(env, params, args)) @@ -103,6 +109,7 @@ E._dissoc_BANG = (hm, args...) -> E._hash_map_Q = _hash_map_Q = (o) -> typeof o == "object" && !Array.isArray(o) && !(o == null) && + !(o instanceof Symbol) && !(o instanceof Atom) diff --git a/cs/core.cs b/cs/core.cs index 8bbf6beb..3e40681b 100644 --- a/cs/core.cs +++ b/cs/core.cs @@ -35,6 +35,19 @@ namespace Mal { static MalFunc symbol_Q = new MalFunc( a => a[0] is MalSymbol ? True : False); + static MalFunc keyword = new MalFunc( + a => new MalString("\u029e" + ((MalString)a[0]).getValue())); + + static MalFunc keyword_Q = new MalFunc( + a => { + if (a[0] is MalString && + ((MalString)a[0]).getValue()[0] == '\u029e') { + return True; + } else { + return False; + } + } ); + // Number functions static MalFunc time_ms = new MalFunc( @@ -159,7 +172,15 @@ namespace Mal { }); static MalFunc nth = new MalFunc( - a => ((MalList)a[0])[ (int)((MalInt)a[1]).getValue() ]); + a => { + var idx = (int)((MalInt)a[1]).getValue(); + if (idx < ((MalList)a[0]).size()) { + return ((MalList)a[0])[idx]; + } else { + throw new Mal.types.MalException( + "nth: index out of range"); + } + }); static MalFunc first = new MalFunc( a => ((MalList)a[0])[0]); @@ -171,7 +192,11 @@ namespace Mal { a => ((MalList)a[0]).size() == 0 ? True : False); static MalFunc count = new MalFunc( - a => new MalInt(((MalList)a[0]).size())); + a => { + return (a[0] == Nil) + ? new MalInt(0) + :new MalInt(((MalList)a[0]).size()); + }); static MalFunc conj = new MalFunc( a => { @@ -254,6 +279,8 @@ namespace Mal { {"false?", false_Q}, {"symbol", new MalFunc(a => new MalSymbol((MalString)a[0]))}, {"symbol?", symbol_Q}, + {"keyword", keyword}, + {"keyword?", keyword_Q}, {"pr-str", pr_str}, {"str", str}, diff --git a/cs/env.cs b/cs/env.cs index cb5318fe..39ab100e 100644 --- a/cs/env.cs +++ b/cs/env.cs @@ -26,8 +26,8 @@ namespace Mal { } } - public Env find(string key) { - if (data.ContainsKey(key)) { + public Env find(MalSymbol key) { + if (data.ContainsKey(key.getName())) { return this; } else if (outer != null) { return outer.find(key); @@ -36,18 +36,18 @@ namespace Mal { } } - public MalVal get(string key) { + public MalVal get(MalSymbol key) { Env e = find(key); if (e == null) { throw new Mal.types.MalException( - "'" + key + "' not found"); + "'" + key.getName() + "' not found"); } else { - return e.data[key]; + return e.data[key.getName()]; } } - public Env set(string key, MalVal value) { - data[key] = value; + public Env set(MalSymbol key, MalVal value) { + data[key.getName()] = value; return this; } } diff --git a/cs/printer.cs b/cs/printer.cs index b5e8c636..8b10dd17 100644 --- a/cs/printer.cs +++ b/cs/printer.cs @@ -20,7 +20,9 @@ namespace Mal { string delim, bool print_readably) { List strs = new List(); foreach (KeyValuePair entry in value) { - if (print_readably) { + if (entry.Key.Length > 0 && entry.Key[0] == '\u029e') { + strs.Add(":" + entry.Key.Substring(1)); + } else if (print_readably) { strs.Add("\"" + entry.Key.ToString() + "\""); } else { strs.Add(entry.Key.ToString()); diff --git a/cs/reader.cs b/cs/reader.cs index dbc74af2..10973aaa 100644 --- a/cs/reader.cs +++ b/cs/reader.cs @@ -53,7 +53,7 @@ namespace Mal { public static MalVal read_atom(Reader rdr) { string token = rdr.next(); - string pattern = @"(^-?[0-9]+$)|(^-?[0-9][0-9.]*$)|(^nil$)|(^true$)|(^false$)|^("".*"")$|(^[^""]*$)"; + string pattern = @"(^-?[0-9]+$)|(^-?[0-9][0-9.]*$)|(^nil$)|(^true$)|(^false$)|^("".*"")$|:(.*)|(^[^""]*$)"; Regex regex = new Regex(pattern); Match match = regex.Match(token); //Console.WriteLine("token: ^" + token + "$"); @@ -75,7 +75,9 @@ namespace Mal { .Replace("\\n", "\n"); return new Mal.types.MalString(str); } else if (match.Groups[7].Value != String.Empty) { - return new Mal.types.MalSymbol(match.Groups[7].Value); + return new Mal.types.MalString("\u029e" + match.Groups[7].Value); + } else if (match.Groups[8].Value != String.Empty) { + return new Mal.types.MalSymbol(match.Groups[8].Value); } else { throw new ParseError("unrecognized '" + match.Groups[0] + "'"); } diff --git a/cs/step2_eval.cs b/cs/step2_eval.cs index 1e3866eb..8c683364 100644 --- a/cs/step2_eval.cs +++ b/cs/step2_eval.cs @@ -45,7 +45,7 @@ namespace Mal { static MalVal EVAL(MalVal orig_ast, Dictionary env) { MalVal a0; - //System.out.println("EVAL: " + printer._pr_str(orig_ast, true)); + //Console.WriteLine("EVAL: " + printer._pr_str(orig_ast, true)); if (!orig_ast.list_Q()) { return eval_ast(orig_ast, env); } diff --git a/cs/step3_env.cs b/cs/step3_env.cs index 99778119..6c4f2fe1 100644 --- a/cs/step3_env.cs +++ b/cs/step3_env.cs @@ -22,8 +22,7 @@ namespace Mal { // eval static MalVal eval_ast(MalVal ast, Env env) { if (ast is MalSymbol) { - MalSymbol sym = (MalSymbol)ast; - return env.get(sym.getName()); + return env.get((MalSymbol)ast); } else if (ast is MalList) { MalList old_lst = (MalList)ast; MalList new_lst = ast.list_Q() ? new MalList() @@ -47,7 +46,7 @@ namespace Mal { static MalVal EVAL(MalVal orig_ast, Env env) { MalVal a0, a1, a2, res; MalList el; - //System.out.println("EVAL: " + printer._pr_str(orig_ast, true)); + //Console.WriteLine("EVAL: " + printer._pr_str(orig_ast, true)); if (!orig_ast.list_Q()) { return eval_ast(orig_ast, env); } @@ -66,7 +65,7 @@ namespace Mal { a1 = ast[1]; a2 = ast[2]; res = EVAL(a2, env); - env.set(((MalSymbol)a1).getName(), res); + env.set((MalSymbol)a1, res); return res; case "let*": a1 = ast[1]; @@ -77,7 +76,7 @@ namespace Mal { for(int i=0; i<((MalList)a1).size(); i+=2) { key = (MalSymbol)((MalList)a1)[i]; val = ((MalList)a1)[i+1]; - let_env.set(key.getName(), EVAL(val, let_env)); + let_env.set(key, EVAL(val, let_env)); } return EVAL(a2, let_env); default: @@ -96,10 +95,14 @@ namespace Mal { static void Main(string[] args) { var repl_env = new Mal.env.Env(null); Func RE = (string str) => EVAL(READ(str), repl_env); - repl_env.set("+", new MalFunc(a => (MalInt)a[0] + (MalInt)a[1]) ); - repl_env.set("-", new MalFunc(a => (MalInt)a[0] - (MalInt)a[1]) ); - repl_env.set("*", new MalFunc(a => (MalInt)a[0] * (MalInt)a[1]) ); - repl_env.set("/", new MalFunc(a => (MalInt)a[0] / (MalInt)a[1]) ); + repl_env.set(new MalSymbol("+"), new MalFunc( + a => (MalInt)a[0] + (MalInt)a[1]) ); + repl_env.set(new MalSymbol("-"), new MalFunc( + a => (MalInt)a[0] - (MalInt)a[1]) ); + repl_env.set(new MalSymbol("*"), new MalFunc( + a => (MalInt)a[0] * (MalInt)a[1]) ); + repl_env.set(new MalSymbol("/"), new MalFunc( + a => (MalInt)a[0] / (MalInt)a[1]) ); if (args.Length > 0 && args[0] == "--raw") { Mal.readline.mode = Mal.readline.Mode.Raw; diff --git a/cs/step4_if_fn_do.cs b/cs/step4_if_fn_do.cs index ff8c3b94..aefe2265 100644 --- a/cs/step4_if_fn_do.cs +++ b/cs/step4_if_fn_do.cs @@ -22,8 +22,7 @@ namespace Mal { // eval static MalVal eval_ast(MalVal ast, Env env) { if (ast is MalSymbol) { - MalSymbol sym = (MalSymbol)ast; - return env.get(sym.getName()); + return env.get((MalSymbol)ast); } else if (ast is MalList) { MalList old_lst = (MalList)ast; MalList new_lst = ast.list_Q() ? new MalList() @@ -47,7 +46,7 @@ namespace Mal { static MalVal EVAL(MalVal orig_ast, Env env) { MalVal a0, a1, a2, a3, res; MalList el; - //System.out.println("EVAL: " + printer._pr_str(orig_ast, true)); + //Console.WriteLine("EVAL: " + printer._pr_str(orig_ast, true)); if (!orig_ast.list_Q()) { return eval_ast(orig_ast, env); } @@ -65,7 +64,7 @@ namespace Mal { a1 = ast[1]; a2 = ast[2]; res = EVAL(a2, env); - env.set(((MalSymbol)a1).getName(), res); + env.set((MalSymbol)a1, res); return res; case "let*": a1 = ast[1]; @@ -76,7 +75,7 @@ namespace Mal { for(int i=0; i<((MalList)a1).size(); i+=2) { key = (MalSymbol)((MalList)a1)[i]; val = ((MalList)a1)[i+1]; - let_env.set(key.getName(), EVAL(val, let_env)); + let_env.set(key, EVAL(val, let_env)); } return EVAL(a2, let_env); case "do": @@ -123,7 +122,7 @@ namespace Mal { // core.cs: defined using C# foreach (var entry in core.ns) { - repl_env.set(entry.Key, entry.Value); + repl_env.set(new MalSymbol(entry.Key), entry.Value); } // core.mal: defined using the language itself diff --git a/cs/step5_tco.cs b/cs/step5_tco.cs index f1307f02..55d414aa 100644 --- a/cs/step5_tco.cs +++ b/cs/step5_tco.cs @@ -22,8 +22,7 @@ namespace Mal { // eval static MalVal eval_ast(MalVal ast, Env env) { if (ast is MalSymbol) { - MalSymbol sym = (MalSymbol)ast; - return env.get(sym.getName()); + return env.get((MalSymbol)ast); } else if (ast is MalList) { MalList old_lst = (MalList)ast; MalList new_lst = ast.list_Q() ? new MalList() @@ -50,7 +49,7 @@ namespace Mal { while (true) { - //System.out.println("EVAL: " + printer._pr_str(orig_ast, true)); + //Console.WriteLine("EVAL: " + printer._pr_str(orig_ast, true)); if (!orig_ast.list_Q()) { return eval_ast(orig_ast, env); } @@ -68,7 +67,7 @@ namespace Mal { a1 = ast[1]; a2 = ast[2]; res = EVAL(a2, env); - env.set(((MalSymbol)a1).getName(), res); + env.set((MalSymbol)a1, res); return res; case "let*": a1 = ast[1]; @@ -79,7 +78,7 @@ namespace Mal { for(int i=0; i<((MalList)a1).size(); i+=2) { key = (MalSymbol)((MalList)a1)[i]; val = ((MalList)a1)[i+1]; - let_env.set(key.getName(), EVAL(val, let_env)); + let_env.set(key, EVAL(val, let_env)); } orig_ast = a2; env = let_env; @@ -137,7 +136,7 @@ namespace Mal { // core.cs: defined using C# foreach (var entry in core.ns) { - repl_env.set(entry.Key, entry.Value); + repl_env.set(new MalSymbol(entry.Key), entry.Value); } // core.mal: defined using the language itself diff --git a/cs/step6_file.cs b/cs/step6_file.cs index c8859224..a84e87c9 100644 --- a/cs/step6_file.cs +++ b/cs/step6_file.cs @@ -23,8 +23,7 @@ namespace Mal { // eval static MalVal eval_ast(MalVal ast, Env env) { if (ast is MalSymbol) { - MalSymbol sym = (MalSymbol)ast; - return env.get(sym.getName()); + return env.get((MalSymbol)ast); } else if (ast is MalList) { MalList old_lst = (MalList)ast; MalList new_lst = ast.list_Q() ? new MalList() @@ -51,7 +50,7 @@ namespace Mal { while (true) { - //System.out.println("EVAL: " + printer._pr_str(orig_ast, true)); + //Console.WriteLine("EVAL: " + printer._pr_str(orig_ast, true)); if (!orig_ast.list_Q()) { return eval_ast(orig_ast, env); } @@ -69,7 +68,7 @@ namespace Mal { a1 = ast[1]; a2 = ast[2]; res = EVAL(a2, env); - env.set(((MalSymbol)a1).getName(), res); + env.set((MalSymbol)a1, res); return res; case "let*": a1 = ast[1]; @@ -80,7 +79,7 @@ namespace Mal { for(int i=0; i<((MalList)a1).size(); i+=2) { key = (MalSymbol)((MalList)a1)[i]; val = ((MalList)a1)[i+1]; - let_env.set(key.getName(), EVAL(val, let_env)); + let_env.set(key, EVAL(val, let_env)); } orig_ast = a2; env = let_env; @@ -138,9 +137,10 @@ namespace Mal { // core.cs: defined using C# foreach (var entry in core.ns) { - repl_env.set(entry.Key, entry.Value); + repl_env.set(new MalSymbol(entry.Key), entry.Value); } - repl_env.set("eval", new MalFunc(a => EVAL(a[0], repl_env))); + repl_env.set(new MalSymbol("eval"), new MalFunc( + a => EVAL(a[0], repl_env))); int fileIdx = 1; if (args.Length > 0 && args[0] == "--raw") { Mal.readline.mode = Mal.readline.Mode.Raw; @@ -150,7 +150,7 @@ namespace Mal { for (int i=fileIdx; i < args.Length; i++) { _argv.conj_BANG(new MalString(args[i])); } - repl_env.set("*ARGV*", _argv); + repl_env.set(new MalSymbol("*ARGV*"), _argv); // core.mal: defined using the language itself RE("(def! not (fn* (a) (if a false true)))"); diff --git a/cs/step7_quote.cs b/cs/step7_quote.cs index c25498f7..7b031521 100644 --- a/cs/step7_quote.cs +++ b/cs/step7_quote.cs @@ -50,8 +50,7 @@ namespace Mal { static MalVal eval_ast(MalVal ast, Env env) { if (ast is MalSymbol) { - MalSymbol sym = (MalSymbol)ast; - return env.get(sym.getName()); + return env.get((MalSymbol)ast); } else if (ast is MalList) { MalList old_lst = (MalList)ast; MalList new_lst = ast.list_Q() ? new MalList() @@ -78,7 +77,7 @@ namespace Mal { while (true) { - //System.out.println("EVAL: " + printer._pr_str(orig_ast, true)); + //Console.WriteLine("EVAL: " + printer._pr_str(orig_ast, true)); if (!orig_ast.list_Q()) { return eval_ast(orig_ast, env); } @@ -96,7 +95,7 @@ namespace Mal { a1 = ast[1]; a2 = ast[2]; res = EVAL(a2, env); - env.set(((MalSymbol)a1).getName(), res); + env.set((MalSymbol)a1, res); return res; case "let*": a1 = ast[1]; @@ -107,7 +106,7 @@ namespace Mal { for(int i=0; i<((MalList)a1).size(); i+=2) { key = (MalSymbol)((MalList)a1)[i]; val = ((MalList)a1)[i+1]; - let_env.set(key.getName(), EVAL(val, let_env)); + let_env.set(key, EVAL(val, let_env)); } orig_ast = a2; env = let_env; @@ -170,9 +169,10 @@ namespace Mal { // core.cs: defined using C# foreach (var entry in core.ns) { - repl_env.set(entry.Key, entry.Value); + repl_env.set(new MalSymbol(entry.Key), entry.Value); } - repl_env.set("eval", new MalFunc(a => EVAL(a[0], repl_env))); + repl_env.set(new MalSymbol("eval"), new MalFunc( + a => EVAL(a[0], repl_env))); int fileIdx = 1; if (args.Length > 0 && args[0] == "--raw") { Mal.readline.mode = Mal.readline.Mode.Raw; @@ -182,7 +182,7 @@ namespace Mal { for (int i=fileIdx; i < args.Length; i++) { _argv.conj_BANG(new MalString(args[i])); } - repl_env.set("*ARGV*", _argv); + repl_env.set(new MalSymbol("*ARGV*"), _argv); // core.mal: defined using the language itself RE("(def! not (fn* (a) (if a false true)))"); diff --git a/cs/step8_macros.cs b/cs/step8_macros.cs index ccc0242b..cac14113 100644 --- a/cs/step8_macros.cs +++ b/cs/step8_macros.cs @@ -52,8 +52,8 @@ namespace Mal { if (ast is MalList) { MalVal a0 = ((MalList)ast)[0]; if (a0 is MalSymbol && - env.find(((MalSymbol)a0).getName()) != null) { - MalVal mac = env.get(((MalSymbol)a0).getName()); + env.find((MalSymbol)a0) != null) { + MalVal mac = env.get((MalSymbol)a0); if (mac is MalFunc && ((MalFunc)mac).isMacro()) { return true; @@ -66,7 +66,7 @@ namespace Mal { public static MalVal macroexpand(MalVal ast, Env env) { while (is_macro_call(ast, env)) { MalSymbol a0 = (MalSymbol)((MalList)ast)[0]; - MalFunc mac = (MalFunc) env.get(a0.getName()); + MalFunc mac = (MalFunc) env.get(a0); ast = mac.apply(((MalList)ast).rest()); } return ast; @@ -74,8 +74,7 @@ namespace Mal { static MalVal eval_ast(MalVal ast, Env env) { if (ast is MalSymbol) { - MalSymbol sym = (MalSymbol)ast; - return env.get(sym.getName()); + return env.get((MalSymbol)ast); } else if (ast is MalList) { MalList old_lst = (MalList)ast; MalList new_lst = ast.list_Q() ? new MalList() @@ -102,7 +101,7 @@ namespace Mal { while (true) { - //System.out.println("EVAL: " + printer._pr_str(orig_ast, true)); + //Console.WriteLine("EVAL: " + printer._pr_str(orig_ast, true)); if (!orig_ast.list_Q()) { return eval_ast(orig_ast, env); } @@ -123,7 +122,7 @@ namespace Mal { a1 = ast[1]; a2 = ast[2]; res = EVAL(a2, env); - env.set(((MalSymbol)a1).getName(), res); + env.set((MalSymbol)a1, res); return res; case "let*": a1 = ast[1]; @@ -134,7 +133,7 @@ namespace Mal { for(int i=0; i<((MalList)a1).size(); i+=2) { key = (MalSymbol)((MalList)a1)[i]; val = ((MalList)a1)[i+1]; - let_env.set(key.getName(), EVAL(val, let_env)); + let_env.set(key, EVAL(val, let_env)); } orig_ast = a2; env = let_env; @@ -149,7 +148,7 @@ namespace Mal { a2 = ast[2]; res = EVAL(a2, env); ((MalFunc)res).setMacro(); - env.set(((MalSymbol)a1).getName(), res); + env.set(((MalSymbol)a1), res); return res; case "macroexpand": a1 = ast[1]; @@ -207,9 +206,10 @@ namespace Mal { // core.cs: defined using C# foreach (var entry in core.ns) { - repl_env.set(entry.Key, entry.Value); + repl_env.set(new MalSymbol(entry.Key), entry.Value); } - repl_env.set("eval", new MalFunc(a => EVAL(a[0], repl_env))); + repl_env.set(new MalSymbol("eval"), new MalFunc( + a => EVAL(a[0], repl_env))); int fileIdx = 1; if (args.Length > 0 && args[0] == "--raw") { Mal.readline.mode = Mal.readline.Mode.Raw; @@ -219,7 +219,7 @@ namespace Mal { for (int i=fileIdx; i < args.Length; i++) { _argv.conj_BANG(new MalString(args[i])); } - repl_env.set("*ARGV*", _argv); + repl_env.set(new MalSymbol("*ARGV*"), _argv); // core.mal: defined using the language itself RE("(def! not (fn* (a) (if a false true)))"); diff --git a/cs/step9_try.cs b/cs/step9_try.cs index 1d724d18..bea360cc 100644 --- a/cs/step9_try.cs +++ b/cs/step9_try.cs @@ -52,8 +52,8 @@ namespace Mal { if (ast is MalList) { MalVal a0 = ((MalList)ast)[0]; if (a0 is MalSymbol && - env.find(((MalSymbol)a0).getName()) != null) { - MalVal mac = env.get(((MalSymbol)a0).getName()); + env.find((MalSymbol)a0) != null) { + MalVal mac = env.get((MalSymbol)a0); if (mac is MalFunc && ((MalFunc)mac).isMacro()) { return true; @@ -66,7 +66,7 @@ namespace Mal { public static MalVal macroexpand(MalVal ast, Env env) { while (is_macro_call(ast, env)) { MalSymbol a0 = (MalSymbol)((MalList)ast)[0]; - MalFunc mac = (MalFunc) env.get(a0.getName()); + MalFunc mac = (MalFunc) env.get(a0); ast = mac.apply(((MalList)ast).rest()); } return ast; @@ -74,8 +74,7 @@ namespace Mal { static MalVal eval_ast(MalVal ast, Env env) { if (ast is MalSymbol) { - MalSymbol sym = (MalSymbol)ast; - return env.get(sym.getName()); + return env.get((MalSymbol)ast); } else if (ast is MalList) { MalList old_lst = (MalList)ast; MalList new_lst = ast.list_Q() ? new MalList() @@ -102,7 +101,7 @@ namespace Mal { while (true) { - //System.out.println("EVAL: " + printer._pr_str(orig_ast, true)); + //Console.WriteLine("EVAL: " + printer._pr_str(orig_ast, true)); if (!orig_ast.list_Q()) { return eval_ast(orig_ast, env); } @@ -123,7 +122,7 @@ namespace Mal { a1 = ast[1]; a2 = ast[2]; res = EVAL(a2, env); - env.set(((MalSymbol)a1).getName(), res); + env.set((MalSymbol)a1, res); return res; case "let*": a1 = ast[1]; @@ -134,7 +133,7 @@ namespace Mal { for(int i=0; i<((MalList)a1).size(); i+=2) { key = (MalSymbol)((MalList)a1)[i]; val = ((MalList)a1)[i+1]; - let_env.set(key.getName(), EVAL(val, let_env)); + let_env.set(key, EVAL(val, let_env)); } orig_ast = a2; env = let_env; @@ -149,7 +148,7 @@ namespace Mal { a2 = ast[2]; res = EVAL(a2, env); ((MalFunc)res).setMacro(); - env.set(((MalSymbol)a1).getName(), res); + env.set(((MalSymbol)a1), res); return res; case "macroexpand": a1 = ast[1]; @@ -228,9 +227,10 @@ namespace Mal { // core.cs: defined using C# foreach (var entry in core.ns) { - repl_env.set(entry.Key, entry.Value); + repl_env.set(new MalSymbol(entry.Key), entry.Value); } - repl_env.set("eval", new MalFunc(a => EVAL(a[0], repl_env))); + repl_env.set(new MalSymbol("eval"), new MalFunc( + a => EVAL(a[0], repl_env))); int fileIdx = 1; if (args.Length > 0 && args[0] == "--raw") { Mal.readline.mode = Mal.readline.Mode.Raw; @@ -240,7 +240,7 @@ namespace Mal { for (int i=fileIdx; i < args.Length; i++) { _argv.conj_BANG(new MalString(args[i])); } - repl_env.set("*ARGV*", _argv); + repl_env.set(new MalSymbol("*ARGV*"), _argv); // core.mal: defined using the language itself RE("(def! not (fn* (a) (if a false true)))"); diff --git a/cs/stepA_interop.cs b/cs/stepA_interop.cs index f51f824a..2e8fc124 100644 --- a/cs/stepA_interop.cs +++ b/cs/stepA_interop.cs @@ -52,8 +52,8 @@ namespace Mal { if (ast is MalList) { MalVal a0 = ((MalList)ast)[0]; if (a0 is MalSymbol && - env.find(((MalSymbol)a0).getName()) != null) { - MalVal mac = env.get(((MalSymbol)a0).getName()); + env.find((MalSymbol)a0) != null) { + MalVal mac = env.get((MalSymbol)a0); if (mac is MalFunc && ((MalFunc)mac).isMacro()) { return true; @@ -66,7 +66,7 @@ namespace Mal { public static MalVal macroexpand(MalVal ast, Env env) { while (is_macro_call(ast, env)) { MalSymbol a0 = (MalSymbol)((MalList)ast)[0]; - MalFunc mac = (MalFunc) env.get(a0.getName()); + MalFunc mac = (MalFunc) env.get(a0); ast = mac.apply(((MalList)ast).rest()); } return ast; @@ -74,8 +74,7 @@ namespace Mal { static MalVal eval_ast(MalVal ast, Env env) { if (ast is MalSymbol) { - MalSymbol sym = (MalSymbol)ast; - return env.get(sym.getName()); + return env.get((MalSymbol)ast); } else if (ast is MalList) { MalList old_lst = (MalList)ast; MalList new_lst = ast.list_Q() ? new MalList() @@ -102,7 +101,7 @@ namespace Mal { while (true) { - //System.out.println("EVAL: " + printer._pr_str(orig_ast, true)); + //Console.WriteLine("EVAL: " + printer._pr_str(orig_ast, true)); if (!orig_ast.list_Q()) { return eval_ast(orig_ast, env); } @@ -123,7 +122,7 @@ namespace Mal { a1 = ast[1]; a2 = ast[2]; res = EVAL(a2, env); - env.set(((MalSymbol)a1).getName(), res); + env.set((MalSymbol)a1, res); return res; case "let*": a1 = ast[1]; @@ -134,7 +133,7 @@ namespace Mal { for(int i=0; i<((MalList)a1).size(); i+=2) { key = (MalSymbol)((MalList)a1)[i]; val = ((MalList)a1)[i+1]; - let_env.set(key.getName(), EVAL(val, let_env)); + let_env.set(key, EVAL(val, let_env)); } orig_ast = a2; env = let_env; @@ -149,7 +148,7 @@ namespace Mal { a2 = ast[2]; res = EVAL(a2, env); ((MalFunc)res).setMacro(); - env.set(((MalSymbol)a1).getName(), res); + env.set(((MalSymbol)a1), res); return res; case "macroexpand": a1 = ast[1]; @@ -228,9 +227,10 @@ namespace Mal { // core.cs: defined using C# foreach (var entry in core.ns) { - repl_env.set(entry.Key, entry.Value); + repl_env.set(new MalSymbol(entry.Key), entry.Value); } - repl_env.set("eval", new MalFunc(a => EVAL(a[0], repl_env))); + repl_env.set(new MalSymbol("eval"), new MalFunc( + a => EVAL(a[0], repl_env))); int fileIdx = 1; if (args.Length > 0 && args[0] == "--raw") { Mal.readline.mode = Mal.readline.Mode.Raw; @@ -240,7 +240,7 @@ namespace Mal { for (int i=fileIdx; i < args.Length; i++) { _argv.conj_BANG(new MalString(args[i])); } - repl_env.set("*ARGV*", _argv); + repl_env.set(new MalSymbol("*ARGV*"), _argv); // core.mal: defined using the language itself RE("(def! *host-language* \"c#\")"); diff --git a/cs/types.cs b/cs/types.cs index c2f46c96..b96a9f4e 100644 --- a/cs/types.cs +++ b/cs/types.cs @@ -160,7 +160,9 @@ namespace Mal { return "\"" + value + "\""; } public override string ToString(bool print_readably) { - if (print_readably) { + if (value.Length > 0 && value[0] == '\u029e') { + return ":" + value.Substring(1); + } else if (print_readably) { return "\"" + value.Replace("\\", "\\\\") .Replace("\"", "\\\"") .Replace("\n", "\\n") + "\""; diff --git a/docs/TODO b/docs/TODO index f4efd325..0ff76a50 100644 --- a/docs/TODO +++ b/docs/TODO @@ -1,12 +1,12 @@ All: - - multi-line read - - loop/recur ? - - hash-maps with non-string keys - - hash-map with space in key string (make) - - keyword type - - gensym reader inside quasiquote - - - per impl tests for step5_tco (if possible) + - add license file + - add re (with rep) and use that (to avoid printing) + - keyword type (with hash-map key support) + - remove conj and sequential? as necessary elements + - redefine (defmacro!) as (def! (macro*)) + - Move *host-language* from step9 to stepA + - Implement/fix interop: C#, Java, Mal, PHP, Postscript, Ruby + - fix long lines in runtext/expect - regular expression matching in runtest - Print full exception when test gets EOF from expect @@ -17,7 +17,13 @@ All: - Move try* to step6 - Remove macros from mal - - Implement/fix interop: C#, Java, Mal, PHP, Postscript, Ruby + - multi-line REPL read + - loop/recur ? + - hash-maps with non-string keys + - hash-map with space in key string (make) + - gensym reader inside quasiquote + + - per impl tests for step5_tco (if possible) --------------------------------------------- @@ -33,6 +39,9 @@ C#: Clojure: +CoffeeScript: + - make target to compile to JS + Go: - consider variable arguments in places where it makes sense https://gobyexample.com/variadic-functions @@ -46,6 +55,9 @@ Javascript: Make: - allow '_' in make variable names + - Fix: make -f stepA_interop.mk ../mal/step6_file.mal + (slurp "../tests/incA.mal") + (read-string "(+ 2 3)") - errors should propagate up from within load-file Mal: @@ -54,6 +66,7 @@ Mal: Perl: - fix metadata on native functions + - implement conj PHP: @@ -65,18 +78,25 @@ Python: - interop tests R: - - readline history - tracebacks in errors Ruby: +Rust: + - use built-in regex once fixed: + https://github.com/rust-lang/rust/issues/18034 + https://github.com/rust-lang/rust/issues/18035 + +VB.Net + - convert readline.cs to readline.vb + --------------------------------------------- Future Implementations: - - Rust: + * Rust: - http://doc.rust-lang.org/index.html - http://doc.rust-lang.org/intro.html - http://doc.rust-lang.org/guide.html @@ -93,64 +113,26 @@ Future Implementations: - https://github.com/shaleh/rust-readline/blob/master/src/lib.rs - http://stackoverflow.com/questions/23942627/does-rust-0-10-have-a-rl-package - http://blog.skylight.io/rust-means-never-having-to-close-a-socket/ - - - R + * R - https://stat.ethz.ch/R-manual/R-devel/library/base/html/readline.html - http://dssm.unipa.it/CRAN/web/packages/rdyncall/rdyncall.pdf - http://www.dyncall.org/docs/FFI.pdf - - Redmonk languages from Jan 2014: - http://sogrady-media.redmonk.com/sogrady/files/2014/01/lang-rank-114-wm.png - - - Tier 1 - * JavaScript - * Java - * PHP - * Python - * C# - - C++ - * Ruby - * C - - Objective-C - * Shell (Bash 4) - * Perl - - - Tier 2 - - R - - Scala - - Haskell - - Visual Basic - - CoffeeScript - * Clojure - - Groovy - * Go - - Lua - - Erlang - - Emacs Lisp - - Assembly - - Scheme - - FORTRAN - - Dart - - F# - - D - - - Tier 3 - - TypeScript - - Racket - - HaXe - - Pascal - - VimL - - https://github.com/tpope/timl - - Common Lisp - - Rust - - M (OpenM/MUMPS) - - Factor (Stack-based) - - - Others: - - Forth (Stack-based) - - BF (Crazy) - - TeX/LaTeX - - Basic interpreter in TeX: http://ctanhg.scharrer-online.de/pkg/basix.html - - Cheat Sheet: http://www.stdout.org/~winston/latex/latexsheet.pd - - latex '\nonstopmode\input' blah.tex + - Groovy + - http://groovy-lang.org/learn.html + - http://groovy-lang.org/structure.html + + - Visual Basic + aptitude install mono-vbnc + + - VimL + - https://github.com/tpope/timl + + - TeX/LaTeX + - Basic interpreter in TeX: http://ctanhg.scharrer-online.de/pkg/basix.html + - Cheat Sheet: http://www.stdout.org/~winston/latex/latexsheet.pd + - latex '\nonstopmode\input' blah.tex + - VB.Net + http://www.codeproject.com/Articles/9978/Complete-Comparison-for-VB-NET-and-C + http://msdn.microsoft.com/en-us/library/8hb2a397.aspx diff --git a/go/src/core/core.go b/go/src/core/core.go index 17e92f05..2acca696 100644 --- a/go/src/core/core.go +++ b/go/src/core/core.go @@ -140,7 +140,11 @@ func concat(a []MalType) (MalType, error) { func nth(a []MalType) (MalType, error) { slc, e := GetSlice(a[0]); if e != nil { return nil, e } idx := a[1].(int) - return slc[idx], nil + if idx < len(slc) { + return slc[idx], nil + } else { + return nil, errors.New("nth: index out of range") + } } func first(a []MalType) (MalType, error) { @@ -294,8 +298,14 @@ var NS = map[string]MalType{ return True_Q(a[0]), nil }, "false?": func(a []MalType) (MalType, error) { return False_Q(a[0]), nil }, + "symbol": func(a []MalType) (MalType, error) { + return Symbol{a[0].(string)}, nil }, "symbol?": func(a []MalType) (MalType, error) { return Symbol_Q(a[0]), nil }, + "keyword": func(a []MalType) (MalType, error) { + return NewKeyword(a[0].(string)) }, + "keyword?": func(a []MalType) (MalType, error) { + return Keyword_Q(a[0]), nil }, "pr-str": func(a []MalType) (MalType, error) { return pr_str(a) }, "str": func(a []MalType) (MalType, error) { return str(a) }, diff --git a/go/src/env/env.go b/go/src/env/env.go index 4d208571..584c9a73 100644 --- a/go/src/env/env.go +++ b/go/src/env/env.go @@ -35,8 +35,8 @@ func NewEnv(outer EnvType, binds_mt MalType, exprs_mt MalType) (EnvType, error) return env, nil } -func (e Env) Find(key string) EnvType { - if _, ok := e.data[key]; ok { +func (e Env) Find(key Symbol) EnvType { + if _, ok := e.data[key.Val]; ok { return e } else if (e.outer != nil) { return e.outer.Find(key) @@ -45,15 +45,15 @@ func (e Env) Find(key string) EnvType { } } -func (e Env) Set(key string, value MalType) MalType { - e.data[key] = value +func (e Env) Set(key Symbol, value MalType) MalType { + e.data[key.Val] = value return value } -func (e Env) Get(key string) (MalType, error) { +func (e Env) Get(key Symbol) (MalType, error) { env := e.Find(key) if env == nil { - return nil, errors.New("'" + key + "' not found") + return nil, errors.New("'" + key.Val + "' not found") } - return env.(Env).data[key], nil + return env.(Env).data[key.Val], nil } diff --git a/go/src/printer/printer.go b/go/src/printer/printer.go index 428958ef..1b8e9feb 100644 --- a/go/src/printer/printer.go +++ b/go/src/printer/printer.go @@ -32,7 +32,9 @@ func Pr_str(obj types.MalType, print_readably bool) string { } return "{" + strings.Join(str_list, " ") + "}" case string: - if print_readably { + if strings.HasPrefix(tobj, "\u029e") { + return ":" + tobj[2:len(tobj)] + } else if print_readably { return `"` + strings.Replace( strings.Replace( strings.Replace(tobj,`\`,`\\`, -1), diff --git a/go/src/reader/reader.go b/go/src/reader/reader.go index 6fbe3118..9cacb8b3 100644 --- a/go/src/reader/reader.go +++ b/go/src/reader/reader.go @@ -64,6 +64,8 @@ func read_atom(rdr Reader) (MalType, error) { return strings.Replace( strings.Replace(str, `\"`, `"`, -1), `\n`, "\n", -1), nil + } else if (*token)[0] == ':' { + return NewKeyword((*token)[1:len(*token)]) } else if *token == "nil" { return nil, nil } else if *token == "true" { diff --git a/go/src/step3_env/step3_env.go b/go/src/step3_env/step3_env.go index 0d975ba4..0c45bf27 100644 --- a/go/src/step3_env/step3_env.go +++ b/go/src/step3_env/step3_env.go @@ -23,7 +23,7 @@ func READ(str string) (MalType, error) { func eval_ast(ast MalType, env EnvType) (MalType, error) { //fmt.Printf("eval_ast: %#v\n", ast) if Symbol_Q(ast) { - return env.Get(ast.(Symbol).Val) + return env.Get(ast.(Symbol)) } else if List_Q(ast) { lst := []MalType{} for _, a := range ast.(List).Val { @@ -83,7 +83,7 @@ func EVAL(ast MalType, env EnvType) (MalType, error) { case "def!": res, e := EVAL(a2, env) if e != nil { return nil, e } - return env.Set(a1.(Symbol).Val, res), nil + return env.Set(a1.(Symbol), res), nil case "let*": let_env, e := NewEnv(env, nil, nil) if e != nil { return nil, e } @@ -95,7 +95,7 @@ func EVAL(ast MalType, env EnvType) (MalType, error) { } exp, e := EVAL(arr1[i+1], let_env) if e != nil { return nil, e } - let_env.Set(arr1[i].(Symbol).Val, exp) + let_env.Set(arr1[i].(Symbol), exp) } return EVAL(a2, let_env) default: @@ -127,13 +127,13 @@ func rep(str string) (MalType, error) { } func main() { - repl_env.Set("+", func(a []MalType) (MalType, error) { + repl_env.Set(Symbol{"+"}, func(a []MalType) (MalType, error) { return a[0].(int) + a[1].(int), nil }) - repl_env.Set("-", func(a []MalType) (MalType, error) { + repl_env.Set(Symbol{"-"}, func(a []MalType) (MalType, error) { return a[0].(int) - a[1].(int), nil }) - repl_env.Set("*", func(a []MalType) (MalType, error) { + repl_env.Set(Symbol{"*"}, func(a []MalType) (MalType, error) { return a[0].(int) * a[1].(int), nil }) - repl_env.Set("/", func(a []MalType) (MalType, error) { + repl_env.Set(Symbol{"/"}, func(a []MalType) (MalType, error) { return a[0].(int) / a[1].(int), nil }) // repl loop diff --git a/go/src/step4_if_fn_do/step4_if_fn_do.go b/go/src/step4_if_fn_do/step4_if_fn_do.go index f63017b4..d90a720a 100644 --- a/go/src/step4_if_fn_do/step4_if_fn_do.go +++ b/go/src/step4_if_fn_do/step4_if_fn_do.go @@ -24,7 +24,7 @@ func READ(str string) (MalType, error) { func eval_ast(ast MalType, env EnvType) (MalType, error) { //fmt.Printf("eval_ast: %#v\n", ast) if Symbol_Q(ast) { - return env.Get(ast.(Symbol).Val) + return env.Get(ast.(Symbol)) } else if List_Q(ast) { lst := []MalType{} for _, a := range ast.(List).Val { @@ -84,7 +84,7 @@ func EVAL(ast MalType, env EnvType) (MalType, error) { case "def!": res, e := EVAL(a2, env) if e != nil { return nil, e } - return env.Set(a1.(Symbol).Val, res), nil + return env.Set(a1.(Symbol), res), nil case "let*": let_env, e := NewEnv(env, nil, nil) if e != nil { return nil, e } @@ -96,7 +96,7 @@ func EVAL(ast MalType, env EnvType) (MalType, error) { } exp, e := EVAL(arr1[i+1], let_env) if e != nil { return nil, e } - let_env.Set(arr1[i].(Symbol).Val, exp) + let_env.Set(arr1[i].(Symbol), exp) } return EVAL(a2, let_env) case "do": @@ -154,7 +154,7 @@ func rep(str string) (MalType, error) { func main() { // core.go: defined using go for k, v := range core.NS { - repl_env.Set(k, v) + repl_env.Set(Symbol{k}, v) } // core.mal: defined using the language itself diff --git a/go/src/step5_tco/step5_tco.go b/go/src/step5_tco/step5_tco.go index 9e176bb8..8c6ff37d 100644 --- a/go/src/step5_tco/step5_tco.go +++ b/go/src/step5_tco/step5_tco.go @@ -24,7 +24,7 @@ func READ(str string) (MalType, error) { func eval_ast(ast MalType, env EnvType) (MalType, error) { //fmt.Printf("eval_ast: %#v\n", ast) if Symbol_Q(ast) { - return env.Get(ast.(Symbol).Val) + return env.Get(ast.(Symbol)) } else if List_Q(ast) { lst := []MalType{} for _, a := range ast.(List).Val { @@ -86,7 +86,7 @@ func EVAL(ast MalType, env EnvType) (MalType, error) { case "def!": res, e := EVAL(a2, env) if e != nil { return nil, e } - return env.Set(a1.(Symbol).Val, res), nil + return env.Set(a1.(Symbol), res), nil case "let*": let_env, e := NewEnv(env, nil, nil) if e != nil { return nil, e } @@ -98,7 +98,7 @@ func EVAL(ast MalType, env EnvType) (MalType, error) { } exp, e := EVAL(arr1[i+1], let_env) if e != nil { return nil, e } - let_env.Set(arr1[i].(Symbol).Val, exp) + let_env.Set(arr1[i].(Symbol), exp) } ast = a2 env = let_env @@ -164,7 +164,7 @@ func rep(str string) (MalType, error) { func main() { // core.go: defined using go for k, v := range core.NS { - repl_env.Set(k, Func{v.(func([]MalType)(MalType,error)),nil}) + repl_env.Set(Symbol{k}, Func{v.(func([]MalType)(MalType,error)),nil}) } // core.mal: defined using the language itself diff --git a/go/src/step6_file/step6_file.go b/go/src/step6_file/step6_file.go index 748d158d..701ac47b 100644 --- a/go/src/step6_file/step6_file.go +++ b/go/src/step6_file/step6_file.go @@ -25,7 +25,7 @@ func READ(str string) (MalType, error) { func eval_ast(ast MalType, env EnvType) (MalType, error) { //fmt.Printf("eval_ast: %#v\n", ast) if Symbol_Q(ast) { - return env.Get(ast.(Symbol).Val) + return env.Get(ast.(Symbol)) } else if List_Q(ast) { lst := []MalType{} for _, a := range ast.(List).Val { @@ -87,7 +87,7 @@ func EVAL(ast MalType, env EnvType) (MalType, error) { case "def!": res, e := EVAL(a2, env) if e != nil { return nil, e } - return env.Set(a1.(Symbol).Val, res), nil + return env.Set(a1.(Symbol), res), nil case "let*": let_env, e := NewEnv(env, nil, nil) if e != nil { return nil, e } @@ -99,7 +99,7 @@ func EVAL(ast MalType, env EnvType) (MalType, error) { } exp, e := EVAL(arr1[i+1], let_env) if e != nil { return nil, e } - let_env.Set(arr1[i].(Symbol).Val, exp) + let_env.Set(arr1[i].(Symbol), exp) } ast = a2 env = let_env @@ -165,11 +165,11 @@ func rep(str string) (MalType, error) { func main() { // core.go: defined using go for k, v := range core.NS { - repl_env.Set(k, Func{v.(func([]MalType)(MalType,error)),nil}) + repl_env.Set(Symbol{k}, Func{v.(func([]MalType)(MalType,error)),nil}) } - repl_env.Set("eval", Func{func(a []MalType) (MalType, error) { + repl_env.Set(Symbol{"eval"}, Func{func(a []MalType) (MalType, error) { return EVAL(a[0], repl_env) },nil}) - repl_env.Set("*ARGV*", List{}) + repl_env.Set(Symbol{"*ARGV*"}, List{}) // core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))") @@ -181,7 +181,7 @@ func main() { for _,a := range os.Args[2:] { args = append(args, a) } - repl_env.Set("*ARGV*", List{args,nil}) + repl_env.Set(Symbol{"*ARGV*"}, List{args,nil}) if _,e := rep("(load-file \"" + os.Args[1] + "\")"); e != nil { fmt.Printf("Error: %v\n", e) os.Exit(1) diff --git a/go/src/step7_quote/step7_quote.go b/go/src/step7_quote/step7_quote.go index 999129e3..d93f6202 100644 --- a/go/src/step7_quote/step7_quote.go +++ b/go/src/step7_quote/step7_quote.go @@ -54,7 +54,7 @@ func quasiquote(ast MalType) MalType { func eval_ast(ast MalType, env EnvType) (MalType, error) { //fmt.Printf("eval_ast: %#v\n", ast) if Symbol_Q(ast) { - return env.Get(ast.(Symbol).Val) + return env.Get(ast.(Symbol)) } else if List_Q(ast) { lst := []MalType{} for _, a := range ast.(List).Val { @@ -116,7 +116,7 @@ func EVAL(ast MalType, env EnvType) (MalType, error) { case "def!": res, e := EVAL(a2, env) if e != nil { return nil, e } - return env.Set(a1.(Symbol).Val, res), nil + return env.Set(a1.(Symbol), res), nil case "let*": let_env, e := NewEnv(env, nil, nil) if e != nil { return nil, e } @@ -128,7 +128,7 @@ func EVAL(ast MalType, env EnvType) (MalType, error) { } exp, e := EVAL(arr1[i+1], let_env) if e != nil { return nil, e } - let_env.Set(arr1[i].(Symbol).Val, exp) + let_env.Set(arr1[i].(Symbol), exp) } ast = a2 env = let_env @@ -198,11 +198,11 @@ func rep(str string) (MalType, error) { func main() { // core.go: defined using go for k, v := range core.NS { - repl_env.Set(k, Func{v.(func([]MalType)(MalType,error)),nil}) + repl_env.Set(Symbol{k}, Func{v.(func([]MalType)(MalType,error)),nil}) } - repl_env.Set("eval", Func{func(a []MalType) (MalType, error) { + repl_env.Set(Symbol{"eval"}, Func{func(a []MalType) (MalType, error) { return EVAL(a[0], repl_env) },nil}) - repl_env.Set("*ARGV*", List{}) + repl_env.Set(Symbol{"*ARGV*"}, List{}) // core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))") @@ -214,7 +214,7 @@ func main() { for _,a := range os.Args[2:] { args = append(args, a) } - repl_env.Set("*ARGV*", List{args,nil}) + repl_env.Set(Symbol{"*ARGV*"}, List{args,nil}) if _,e := rep("(load-file \"" + os.Args[1] + "\")"); e != nil { fmt.Printf("Error: %v\n", e) os.Exit(1) diff --git a/go/src/step8_macros/step8_macros.go b/go/src/step8_macros/step8_macros.go index 3264159b..86db33be 100644 --- a/go/src/step8_macros/step8_macros.go +++ b/go/src/step8_macros/step8_macros.go @@ -55,8 +55,8 @@ func is_macro_call(ast MalType, env EnvType) bool { if List_Q(ast) { slc, _ := GetSlice(ast) a0 := slc[0] - if Symbol_Q(a0) && env.Find(a0.(Symbol).Val) != nil { - mac, e := env.Get(a0.(Symbol).Val) + if Symbol_Q(a0) && env.Find(a0.(Symbol)) != nil { + mac, e := env.Get(a0.(Symbol)) if e != nil { return false } if MalFunc_Q(mac) { return mac.(MalFunc).GetMacro() @@ -72,7 +72,7 @@ func macroexpand(ast MalType, env EnvType) (MalType, error) { for ; is_macro_call(ast, env) ; { slc, _ := GetSlice(ast) a0 := slc[0] - mac, e = env.Get(a0.(Symbol).Val); if e != nil { return nil, e } + mac, e = env.Get(a0.(Symbol)); if e != nil { return nil, e } fn := mac.(MalFunc) ast, e = Apply(fn, slc[1:]); if e != nil { return nil, e } } @@ -82,7 +82,7 @@ func macroexpand(ast MalType, env EnvType) (MalType, error) { func eval_ast(ast MalType, env EnvType) (MalType, error) { //fmt.Printf("eval_ast: %#v\n", ast) if Symbol_Q(ast) { - return env.Get(ast.(Symbol).Val) + return env.Get(ast.(Symbol)) } else if List_Q(ast) { lst := []MalType{} for _, a := range ast.(List).Val { @@ -148,7 +148,7 @@ func EVAL(ast MalType, env EnvType) (MalType, error) { case "def!": res, e := EVAL(a2, env) if e != nil { return nil, e } - return env.Set(a1.(Symbol).Val, res), nil + return env.Set(a1.(Symbol), res), nil case "let*": let_env, e := NewEnv(env, nil, nil) if e != nil { return nil, e } @@ -160,7 +160,7 @@ func EVAL(ast MalType, env EnvType) (MalType, error) { } exp, e := EVAL(arr1[i+1], let_env) if e != nil { return nil, e } - let_env.Set(arr1[i].(Symbol).Val, exp) + let_env.Set(arr1[i].(Symbol), exp) } ast = a2 env = let_env @@ -172,7 +172,7 @@ func EVAL(ast MalType, env EnvType) (MalType, error) { fn, e := EVAL(a2, env) fn = fn.(MalFunc).SetMacro() if e != nil { return nil, e } - return env.Set(a1.(Symbol).Val, fn), nil + return env.Set(a1.(Symbol), fn), nil case "macroexpand": return macroexpand(a1, env) case "do": @@ -237,11 +237,11 @@ func rep(str string) (MalType, error) { func main() { // core.go: defined using go for k, v := range core.NS { - repl_env.Set(k, Func{v.(func([]MalType)(MalType,error)),nil}) + repl_env.Set(Symbol{k}, Func{v.(func([]MalType)(MalType,error)),nil}) } - repl_env.Set("eval", Func{func(a []MalType) (MalType, error) { + repl_env.Set(Symbol{"eval"}, Func{func(a []MalType) (MalType, error) { return EVAL(a[0], repl_env) },nil}) - repl_env.Set("*ARGV*", List{}) + repl_env.Set(Symbol{"*ARGV*"}, List{}) // core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))") @@ -255,7 +255,7 @@ func main() { for _,a := range os.Args[2:] { args = append(args, a) } - repl_env.Set("*ARGV*", List{args,nil}) + repl_env.Set(Symbol{"*ARGV*"}, List{args,nil}) if _,e := rep("(load-file \"" + os.Args[1] + "\")"); e != nil { fmt.Printf("Error: %v\n", e) os.Exit(1) diff --git a/go/src/step9_try/step9_try.go b/go/src/step9_try/step9_try.go index 322ee36f..18f3c9ce 100644 --- a/go/src/step9_try/step9_try.go +++ b/go/src/step9_try/step9_try.go @@ -55,8 +55,8 @@ func is_macro_call(ast MalType, env EnvType) bool { if List_Q(ast) { slc, _ := GetSlice(ast) a0 := slc[0] - if Symbol_Q(a0) && env.Find(a0.(Symbol).Val) != nil { - mac, e := env.Get(a0.(Symbol).Val) + if Symbol_Q(a0) && env.Find(a0.(Symbol)) != nil { + mac, e := env.Get(a0.(Symbol)) if e != nil { return false } if MalFunc_Q(mac) { return mac.(MalFunc).GetMacro() @@ -72,7 +72,7 @@ func macroexpand(ast MalType, env EnvType) (MalType, error) { for ; is_macro_call(ast, env) ; { slc, _ := GetSlice(ast) a0 := slc[0] - mac, e = env.Get(a0.(Symbol).Val); if e != nil { return nil, e } + mac, e = env.Get(a0.(Symbol)); if e != nil { return nil, e } fn := mac.(MalFunc) ast, e = Apply(fn, slc[1:]); if e != nil { return nil, e } } @@ -82,7 +82,7 @@ func macroexpand(ast MalType, env EnvType) (MalType, error) { func eval_ast(ast MalType, env EnvType) (MalType, error) { //fmt.Printf("eval_ast: %#v\n", ast) if Symbol_Q(ast) { - return env.Get(ast.(Symbol).Val) + return env.Get(ast.(Symbol)) } else if List_Q(ast) { lst := []MalType{} for _, a := range ast.(List).Val { @@ -148,7 +148,7 @@ func EVAL(ast MalType, env EnvType) (MalType, error) { case "def!": res, e := EVAL(a2, env) if e != nil { return nil, e } - return env.Set(a1.(Symbol).Val, res), nil + return env.Set(a1.(Symbol), res), nil case "let*": let_env, e := NewEnv(env, nil, nil) if e != nil { return nil, e } @@ -160,7 +160,7 @@ func EVAL(ast MalType, env EnvType) (MalType, error) { } exp, e := EVAL(arr1[i+1], let_env) if e != nil { return nil, e } - let_env.Set(arr1[i].(Symbol).Val, exp) + let_env.Set(arr1[i].(Symbol), exp) } ast = a2 env = let_env @@ -172,7 +172,7 @@ func EVAL(ast MalType, env EnvType) (MalType, error) { fn, e := EVAL(a2, env) fn = fn.(MalFunc).SetMacro() if e != nil { return nil, e } - return env.Set(a1.(Symbol).Val, fn), nil + return env.Set(a1.(Symbol), fn), nil case "macroexpand": return macroexpand(a1, env) case "try*": @@ -259,14 +259,13 @@ func rep(str string) (MalType, error) { func main() { // core.go: defined using go for k, v := range core.NS { - repl_env.Set(k, Func{v.(func([]MalType)(MalType,error)),nil}) + repl_env.Set(Symbol{k}, Func{v.(func([]MalType)(MalType,error)),nil}) } - repl_env.Set("eval", Func{func(a []MalType) (MalType, error) { + repl_env.Set(Symbol{"eval"}, Func{func(a []MalType) (MalType, error) { return EVAL(a[0], repl_env) },nil}) - repl_env.Set("*ARGV*", List{}) + repl_env.Set(Symbol{"*ARGV*"}, List{}) // core.mal: defined using the language itself - rep("(def! *host-language* \"go\")") rep("(def! not (fn* (a) (if a false true)))") rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))") @@ -278,7 +277,7 @@ func main() { for _,a := range os.Args[2:] { args = append(args, a) } - repl_env.Set("*ARGV*", List{args,nil}) + repl_env.Set(Symbol{"*ARGV*"}, List{args,nil}) if _,e := rep("(load-file \"" + os.Args[1] + "\")"); e != nil { fmt.Printf("Error: %v\n", e) os.Exit(1) @@ -287,7 +286,6 @@ func main() { } // repl loop - rep("(println (str \"Mal [\" *host-language* \"]\"))") for { text, err := readline.Readline("user> ") text = strings.TrimRight(text, "\n"); diff --git a/go/src/stepA_interop/stepA_interop.go b/go/src/stepA_interop/stepA_interop.go index 322ee36f..808022ac 100644 --- a/go/src/stepA_interop/stepA_interop.go +++ b/go/src/stepA_interop/stepA_interop.go @@ -55,8 +55,8 @@ func is_macro_call(ast MalType, env EnvType) bool { if List_Q(ast) { slc, _ := GetSlice(ast) a0 := slc[0] - if Symbol_Q(a0) && env.Find(a0.(Symbol).Val) != nil { - mac, e := env.Get(a0.(Symbol).Val) + if Symbol_Q(a0) && env.Find(a0.(Symbol)) != nil { + mac, e := env.Get(a0.(Symbol)) if e != nil { return false } if MalFunc_Q(mac) { return mac.(MalFunc).GetMacro() @@ -72,7 +72,7 @@ func macroexpand(ast MalType, env EnvType) (MalType, error) { for ; is_macro_call(ast, env) ; { slc, _ := GetSlice(ast) a0 := slc[0] - mac, e = env.Get(a0.(Symbol).Val); if e != nil { return nil, e } + mac, e = env.Get(a0.(Symbol)); if e != nil { return nil, e } fn := mac.(MalFunc) ast, e = Apply(fn, slc[1:]); if e != nil { return nil, e } } @@ -82,7 +82,7 @@ func macroexpand(ast MalType, env EnvType) (MalType, error) { func eval_ast(ast MalType, env EnvType) (MalType, error) { //fmt.Printf("eval_ast: %#v\n", ast) if Symbol_Q(ast) { - return env.Get(ast.(Symbol).Val) + return env.Get(ast.(Symbol)) } else if List_Q(ast) { lst := []MalType{} for _, a := range ast.(List).Val { @@ -148,7 +148,7 @@ func EVAL(ast MalType, env EnvType) (MalType, error) { case "def!": res, e := EVAL(a2, env) if e != nil { return nil, e } - return env.Set(a1.(Symbol).Val, res), nil + return env.Set(a1.(Symbol), res), nil case "let*": let_env, e := NewEnv(env, nil, nil) if e != nil { return nil, e } @@ -160,7 +160,7 @@ func EVAL(ast MalType, env EnvType) (MalType, error) { } exp, e := EVAL(arr1[i+1], let_env) if e != nil { return nil, e } - let_env.Set(arr1[i].(Symbol).Val, exp) + let_env.Set(arr1[i].(Symbol), exp) } ast = a2 env = let_env @@ -172,7 +172,7 @@ func EVAL(ast MalType, env EnvType) (MalType, error) { fn, e := EVAL(a2, env) fn = fn.(MalFunc).SetMacro() if e != nil { return nil, e } - return env.Set(a1.(Symbol).Val, fn), nil + return env.Set(a1.(Symbol), fn), nil case "macroexpand": return macroexpand(a1, env) case "try*": @@ -259,11 +259,11 @@ func rep(str string) (MalType, error) { func main() { // core.go: defined using go for k, v := range core.NS { - repl_env.Set(k, Func{v.(func([]MalType)(MalType,error)),nil}) + repl_env.Set(Symbol{k}, Func{v.(func([]MalType)(MalType,error)),nil}) } - repl_env.Set("eval", Func{func(a []MalType) (MalType, error) { + repl_env.Set(Symbol{"eval"}, Func{func(a []MalType) (MalType, error) { return EVAL(a[0], repl_env) },nil}) - repl_env.Set("*ARGV*", List{}) + repl_env.Set(Symbol{"*ARGV*"}, List{}) // core.mal: defined using the language itself rep("(def! *host-language* \"go\")") @@ -278,7 +278,7 @@ func main() { for _,a := range os.Args[2:] { args = append(args, a) } - repl_env.Set("*ARGV*", List{args,nil}) + repl_env.Set(Symbol{"*ARGV*"}, List{args,nil}) if _,e := rep("(load-file \"" + os.Args[1] + "\")"); e != nil { fmt.Printf("Error: %v\n", e) os.Exit(1) diff --git a/go/src/types/types.go b/go/src/types/types.go index 613bfb52..54e41767 100644 --- a/go/src/types/types.go +++ b/go/src/types/types.go @@ -4,6 +4,7 @@ import ( "reflect" "errors" "fmt" + "strings" ) // Errors/Exceptions @@ -21,10 +22,9 @@ type MalType interface { } type EnvType interface { - //Find(key string) *EnvType - Find(key string) EnvType - Set(key string, value MalType) MalType - Get(key string) (MalType, error) + Find(key Symbol) EnvType + Set(key Symbol, value MalType) MalType + Get(key Symbol) (MalType, error) } // Scalars @@ -57,6 +57,20 @@ func Symbol_Q(obj MalType) bool { } +// Keywords +func NewKeyword(s string) (MalType, error) { + return "\u029e" + s, nil; +} + +func Keyword_Q(obj MalType) bool { + if obj == nil { return false } + switch s := obj.(type) { + case string: return strings.HasPrefix(s, "\u029e") + default: return false + } +} + + // Strings func String_Q(obj MalType) bool { if obj == nil { return false } diff --git a/java/src/main/java/mal/core.java b/java/src/main/java/mal/core.java index 7af2f72c..facaeb13 100644 --- a/java/src/main/java/mal/core.java +++ b/java/src/main/java/mal/core.java @@ -49,11 +49,32 @@ public class core { return args.nth(0) == False ? True : False; } }; + static MalFunction symbol = new MalFunction() { + public MalVal apply(MalList args) throws MalThrowable { + return new MalSymbol((MalString)args.nth(0)); + } + }; static MalFunction symbol_Q = new MalFunction() { public MalVal apply(MalList args) throws MalThrowable { return args.nth(0) instanceof MalSymbol ? True : False; } }; + static MalFunction keyword = new MalFunction() { + public MalVal apply(MalList args) throws MalThrowable { + return new MalString( + "\u029e" + ((MalString)args.nth(0)).getValue()); + } + }; + static MalFunction keyword_Q = new MalFunction() { + public MalVal apply(MalList args) throws MalThrowable { + if (args.nth(0) instanceof MalString && + (((MalString)args.nth(0)).getValue().charAt(0) == '\u029e')) { + return True; + } else { + return False; + } + } + }; // String functions @@ -304,7 +325,11 @@ public class core { static MalFunction count = new MalFunction() { public MalVal apply(MalList a) throws MalThrowable { - return new MalInteger(((MalList)a.nth(0)).size()); + if (a.nth(0) == Nil) { + return new MalInteger(0); + } else { + return new MalInteger(((MalList)a.nth(0)).size()); + } } }; @@ -358,7 +383,11 @@ public class core { 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); + if (idx < ((MalList)a.nth(0)).size()) { + return ((MalList)a.nth(0)).nth(idx); + } else { + throw new MalError("nth: index out of range"); + } } }; @@ -471,7 +500,10 @@ public class core { .put("nil?", nil_Q) .put("true?", true_Q) .put("false?", false_Q) + .put("symbol", symbol) .put("symbol?", symbol_Q) + .put("keyword", keyword) + .put("keyword?", keyword_Q) .put("pr-str", pr_str) .put("str", str) diff --git a/java/src/main/java/mal/env.java b/java/src/main/java/mal/env.java index 8a1913ea..711a9eee 100644 --- a/java/src/main/java/mal/env.java +++ b/java/src/main/java/mal/env.java @@ -30,8 +30,8 @@ public class env { } } - public Env find(String key) { - if (data.containsKey(key)) { + public Env find(MalSymbol key) { + if (data.containsKey(key.getName())) { return this; } else if (outer != null) { return outer.find(key); @@ -40,17 +40,18 @@ public class env { } } - public MalVal get(String key) throws MalThrowable { + public MalVal get(MalSymbol key) throws MalThrowable { Env e = find(key); if (e == null) { - throw new MalException("'" + key + "' not found"); + throw new MalException( + "'" + key.getName() + "' not found"); } else { - return e.data.get(key); + return e.data.get(key.getName()); } } - public Env set(String key, MalVal value) { - data.put(key, value); + public Env set(MalSymbol key, MalVal value) { + data.put(key.getName(), value); return this; } } diff --git a/java/src/main/java/mal/printer.java b/java/src/main/java/mal/printer.java index 73dfca32..fe3c5c4b 100644 --- a/java/src/main/java/mal/printer.java +++ b/java/src/main/java/mal/printer.java @@ -24,7 +24,10 @@ public class printer { String delim, Boolean print_readably) { ArrayList strs = new ArrayList(); for (Map.Entry entry : value.entrySet()) { - if (print_readably) { + if (entry.getKey().length() > 0 && + entry.getKey().charAt(0) == '\u029e') { + strs.add(":" + entry.getKey().substring(1)); + } else if (print_readably) { strs.add("\"" + entry.getKey().toString() + "\""); } else { strs.add(entry.getKey().toString()); diff --git a/java/src/main/java/mal/reader.java b/java/src/main/java/mal/reader.java index 6bae5069..7c9d3aa0 100644 --- a/java/src/main/java/mal/reader.java +++ b/java/src/main/java/mal/reader.java @@ -51,7 +51,7 @@ public class reader { public static MalVal read_atom(Reader rdr) throws ParseError { String token = rdr.next(); - Pattern pattern = Pattern.compile("(^-?[0-9]+$)|(^-?[0-9][0-9.]*$)|(^nil$)|(^true$)|(^false$)|^\"(.*)\"$|(^[^\"]*$)"); + Pattern pattern = Pattern.compile("(^-?[0-9]+$)|(^-?[0-9][0-9.]*$)|(^nil$)|(^true$)|(^false$)|^\"(.*)\"$|:(.*)|(^[^\"]*$)"); Matcher matcher = pattern.matcher(token); if (!matcher.find()) { throw new ParseError("unrecognized token '" + token + "'"); @@ -67,7 +67,9 @@ public class reader { } else if (matcher.group(6) != null) { return new MalString(StringEscapeUtils.unescapeJson(matcher.group(6))); } else if (matcher.group(7) != null) { - return new MalSymbol(matcher.group(7)); + return new MalString("\u029e" + matcher.group(7)); + } else if (matcher.group(8) != null) { + return new MalSymbol(matcher.group(8)); } else { throw new ParseError("unrecognized '" + matcher.group(0) + "'"); } diff --git a/java/src/main/java/mal/step3_env.java b/java/src/main/java/mal/step3_env.java index a88dc132..d3e221b3 100644 --- a/java/src/main/java/mal/step3_env.java +++ b/java/src/main/java/mal/step3_env.java @@ -21,8 +21,7 @@ public class step3_env { // eval public static MalVal eval_ast(MalVal ast, Env env) throws MalThrowable { if (ast instanceof MalSymbol) { - MalSymbol sym = (MalSymbol)ast; - return env.get(sym.getName()); + return env.get((MalSymbol)ast); } else if (ast instanceof MalList) { MalList old_lst = (MalList)ast; MalList new_lst = ast.list_Q() ? new MalList() @@ -65,7 +64,7 @@ public class step3_env { a1 = ast.nth(1); a2 = ast.nth(2); res = EVAL(a2, env); - env.set(((MalSymbol)a1).getName(), res); + env.set(((MalSymbol)a1), res); return res; case "let*": a1 = ast.nth(1); @@ -76,13 +75,12 @@ public class step3_env { for(int i=0; i<((MalList)a1).size(); i+=2) { key = (MalSymbol)((MalList)a1).nth(i); val = ((MalList)a1).nth(i+1); - let_env.set(key.getName(), EVAL(val, let_env)); + let_env.set(key, EVAL(val, let_env)); } return EVAL(a2, let_env); default: MalVal args = eval_ast(ast.rest(), env); - MalSymbol fsym = (MalSymbol)a0; - ILambda f = (ILambda)env.get(fsym.getName()); + ILambda f = (ILambda)env.get((MalSymbol)a0); return f.apply((MalList)args); } } @@ -123,10 +121,10 @@ public class step3_env { String prompt = "user> "; Env repl_env = new Env(null); - repl_env.set("+", add); - repl_env.set("-", subtract); - repl_env.set("*", multiply); - repl_env.set("/", divide); + repl_env.set(new MalSymbol("+"), add); + repl_env.set(new MalSymbol("-"), subtract); + repl_env.set(new MalSymbol("*"), multiply); + repl_env.set(new MalSymbol("/"), divide); if (args.length > 0 && args[0].equals("--raw")) { readline.mode = readline.Mode.JAVA; 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 ce9043da..ff15709f 100644 --- a/java/src/main/java/mal/step4_if_fn_do.java +++ b/java/src/main/java/mal/step4_if_fn_do.java @@ -22,8 +22,7 @@ public class step4_if_fn_do { // eval public static MalVal eval_ast(MalVal ast, Env env) throws MalThrowable { if (ast instanceof MalSymbol) { - MalSymbol sym = (MalSymbol)ast; - return env.get(sym.getName()); + return env.get((MalSymbol)ast); } else if (ast instanceof MalList) { MalList old_lst = (MalList)ast; MalList new_lst = ast.list_Q() ? new MalList() @@ -64,7 +63,7 @@ public class step4_if_fn_do { a1 = ast.nth(1); a2 = ast.nth(2); res = EVAL(a2, env); - env.set(((MalSymbol)a1).getName(), res); + env.set(((MalSymbol)a1), res); return res; case "let*": a1 = ast.nth(1); @@ -75,7 +74,7 @@ public class step4_if_fn_do { for(int i=0; i<((MalList)a1).size(); i+=2) { key = (MalSymbol)((MalList)a1).nth(i); val = ((MalList)a1).nth(i+1); - let_env.set(key.getName(), EVAL(val, let_env)); + let_env.set(key, EVAL(val, let_env)); } return EVAL(a2, let_env); case "do": @@ -130,7 +129,7 @@ public class step4_if_fn_do { // core.java: defined using Java for (String key : core.ns.keySet()) { - repl_env.set(key, core.ns.get(key)); + repl_env.set(new MalSymbol(key), core.ns.get(key)); } // core.mal: defined using the language itself diff --git a/java/src/main/java/mal/step5_tco.java b/java/src/main/java/mal/step5_tco.java index ef56083b..43c87b73 100644 --- a/java/src/main/java/mal/step5_tco.java +++ b/java/src/main/java/mal/step5_tco.java @@ -22,8 +22,7 @@ public class step5_tco { // eval public static MalVal eval_ast(MalVal ast, Env env) throws MalThrowable { if (ast instanceof MalSymbol) { - MalSymbol sym = (MalSymbol)ast; - return env.get(sym.getName()); + return env.get((MalSymbol)ast); } else if (ast instanceof MalList) { MalList old_lst = (MalList)ast; MalList new_lst = ast.list_Q() ? new MalList() @@ -67,7 +66,7 @@ public class step5_tco { a1 = ast.nth(1); a2 = ast.nth(2); res = EVAL(a2, env); - env.set(((MalSymbol)a1).getName(), res); + env.set(((MalSymbol)a1), res); return res; case "let*": a1 = ast.nth(1); @@ -78,7 +77,7 @@ public class step5_tco { for(int i=0; i<((MalList)a1).size(); i+=2) { key = (MalSymbol)((MalList)a1).nth(i); val = ((MalList)a1).nth(i+1); - let_env.set(key.getName(), EVAL(val, let_env)); + let_env.set(key, EVAL(val, let_env)); } orig_ast = a2; env = let_env; @@ -143,7 +142,7 @@ public class step5_tco { // core.java: defined using Java for (String key : core.ns.keySet()) { - repl_env.set(key, core.ns.get(key)); + repl_env.set(new MalSymbol(key), core.ns.get(key)); } // core.mal: defined using the language itself diff --git a/java/src/main/java/mal/step6_file.java b/java/src/main/java/mal/step6_file.java index 56bcdf71..19c4c1cd 100644 --- a/java/src/main/java/mal/step6_file.java +++ b/java/src/main/java/mal/step6_file.java @@ -22,8 +22,7 @@ public class step6_file { // eval public static MalVal eval_ast(MalVal ast, Env env) throws MalThrowable { if (ast instanceof MalSymbol) { - MalSymbol sym = (MalSymbol)ast; - return env.get(sym.getName()); + return env.get((MalSymbol)ast); } else if (ast instanceof MalList) { MalList old_lst = (MalList)ast; MalList new_lst = ast.list_Q() ? new MalList() @@ -67,7 +66,7 @@ public class step6_file { a1 = ast.nth(1); a2 = ast.nth(2); res = EVAL(a2, env); - env.set(((MalSymbol)a1).getName(), res); + env.set(((MalSymbol)a1), res); return res; case "let*": a1 = ast.nth(1); @@ -78,7 +77,7 @@ public class step6_file { for(int i=0; i<((MalList)a1).size(); i+=2) { key = (MalSymbol)((MalList)a1).nth(i); val = ((MalList)a1).nth(i+1); - let_env.set(key.getName(), EVAL(val, let_env)); + let_env.set(key, EVAL(val, let_env)); } orig_ast = a2; env = let_env; @@ -143,9 +142,9 @@ public class step6_file { // core.java: defined using Java for (String key : core.ns.keySet()) { - repl_env.set(key, core.ns.get(key)); + repl_env.set(new MalSymbol(key), core.ns.get(key)); } - repl_env.set("eval", new MalFunction() { + repl_env.set(new MalSymbol("eval"), new MalFunction() { public MalVal apply(MalList args) throws MalThrowable { return EVAL(args.nth(0), repl_env); } @@ -154,7 +153,7 @@ public class step6_file { for (Integer i=1; i < args.length; i++) { _argv.conj_BANG(new MalString(args[i])); } - repl_env.set("*ARGV*", _argv); + repl_env.set(new MalSymbol("*ARGV*"), _argv); // core.mal: defined using the language itself diff --git a/java/src/main/java/mal/step7_quote.java b/java/src/main/java/mal/step7_quote.java index 8c3766a8..6d015b48 100644 --- a/java/src/main/java/mal/step7_quote.java +++ b/java/src/main/java/mal/step7_quote.java @@ -49,8 +49,7 @@ public class step7_quote { public static MalVal eval_ast(MalVal ast, Env env) throws MalThrowable { if (ast instanceof MalSymbol) { - MalSymbol sym = (MalSymbol)ast; - return env.get(sym.getName()); + return env.get((MalSymbol)ast); } else if (ast instanceof MalList) { MalList old_lst = (MalList)ast; MalList new_lst = ast.list_Q() ? new MalList() @@ -94,7 +93,7 @@ public class step7_quote { a1 = ast.nth(1); a2 = ast.nth(2); res = EVAL(a2, env); - env.set(((MalSymbol)a1).getName(), res); + env.set(((MalSymbol)a1), res); return res; case "let*": a1 = ast.nth(1); @@ -105,7 +104,7 @@ public class step7_quote { for(int i=0; i<((MalList)a1).size(); i+=2) { key = (MalSymbol)((MalList)a1).nth(i); val = ((MalList)a1).nth(i+1); - let_env.set(key.getName(), EVAL(val, let_env)); + let_env.set(key, EVAL(val, let_env)); } orig_ast = a2; env = let_env; @@ -175,9 +174,9 @@ public class step7_quote { // core.java: defined using Java for (String key : core.ns.keySet()) { - repl_env.set(key, core.ns.get(key)); + repl_env.set(new MalSymbol(key), core.ns.get(key)); } - repl_env.set("eval", new MalFunction() { + repl_env.set(new MalSymbol("eval"), new MalFunction() { public MalVal apply(MalList args) throws MalThrowable { return EVAL(args.nth(0), repl_env); } @@ -186,7 +185,7 @@ public class step7_quote { for (Integer i=1; i < args.length; i++) { _argv.conj_BANG(new MalString(args[i])); } - repl_env.set("*ARGV*", _argv); + repl_env.set(new MalSymbol("*ARGV*"), _argv); // core.mal: defined using the language itself diff --git a/java/src/main/java/mal/step8_macros.java b/java/src/main/java/mal/step8_macros.java index 4c893563..38a4aef7 100644 --- a/java/src/main/java/mal/step8_macros.java +++ b/java/src/main/java/mal/step8_macros.java @@ -52,8 +52,8 @@ public class step8_macros { if (ast instanceof MalList) { MalVal a0 = ((MalList)ast).nth(0); if (a0 instanceof MalSymbol && - env.find(((MalSymbol)a0).getName()) != null) { - MalVal mac = env.get(((MalSymbol)a0).getName()); + env.find(((MalSymbol)a0)) != null) { + MalVal mac = env.get(((MalSymbol)a0)); if (mac instanceof MalFunction && ((MalFunction)mac).isMacro()) { return true; @@ -67,7 +67,7 @@ public class step8_macros { throws MalThrowable { while (is_macro_call(ast, env)) { MalSymbol a0 = (MalSymbol)((MalList)ast).nth(0); - MalFunction mac = (MalFunction) env.get(a0.getName()); + MalFunction mac = (MalFunction) env.get(a0); ast = mac.apply(((MalList)ast).rest()); } return ast; @@ -75,8 +75,7 @@ public class step8_macros { public static MalVal eval_ast(MalVal ast, Env env) throws MalThrowable { if (ast instanceof MalSymbol) { - MalSymbol sym = (MalSymbol)ast; - return env.get(sym.getName()); + return env.get((MalSymbol)ast); } else if (ast instanceof MalList) { MalList old_lst = (MalList)ast; MalList new_lst = ast.list_Q() ? new MalList() @@ -122,7 +121,7 @@ public class step8_macros { a1 = ast.nth(1); a2 = ast.nth(2); res = EVAL(a2, env); - env.set(((MalSymbol)a1).getName(), res); + env.set(((MalSymbol)a1), res); return res; case "let*": a1 = ast.nth(1); @@ -133,7 +132,7 @@ public class step8_macros { for(int i=0; i<((MalList)a1).size(); i+=2) { key = (MalSymbol)((MalList)a1).nth(i); val = ((MalList)a1).nth(i+1); - let_env.set(key.getName(), EVAL(val, let_env)); + let_env.set(key, EVAL(val, let_env)); } orig_ast = a2; env = let_env; @@ -148,7 +147,7 @@ public class step8_macros { a2 = ast.nth(2); res = EVAL(a2, env); ((MalFunction)res).setMacro(); - env.set(((MalSymbol)a1).getName(), res); + env.set((MalSymbol)a1, res); return res; case "macroexpand": a1 = ast.nth(1); @@ -213,9 +212,9 @@ public class step8_macros { // core.java: defined using Java for (String key : core.ns.keySet()) { - repl_env.set(key, core.ns.get(key)); + repl_env.set(new MalSymbol(key), core.ns.get(key)); } - repl_env.set("eval", new MalFunction() { + repl_env.set(new MalSymbol("eval"), new MalFunction() { public MalVal apply(MalList args) throws MalThrowable { return EVAL(args.nth(0), repl_env); } @@ -224,7 +223,7 @@ public class step8_macros { for (Integer i=1; i < args.length; i++) { _argv.conj_BANG(new MalString(args[i])); } - repl_env.set("*ARGV*", _argv); + repl_env.set(new MalSymbol("*ARGV*"), _argv); // core.mal: defined using the language itself diff --git a/java/src/main/java/mal/step9_try.java b/java/src/main/java/mal/step9_try.java index c3b0e9fe..ceeff274 100644 --- a/java/src/main/java/mal/step9_try.java +++ b/java/src/main/java/mal/step9_try.java @@ -54,8 +54,8 @@ public class step9_try { if (ast instanceof MalList) { MalVal a0 = ((MalList)ast).nth(0); if (a0 instanceof MalSymbol && - env.find(((MalSymbol)a0).getName()) != null) { - MalVal mac = env.get(((MalSymbol)a0).getName()); + env.find(((MalSymbol)a0)) != null) { + MalVal mac = env.get(((MalSymbol)a0)); if (mac instanceof MalFunction && ((MalFunction)mac).isMacro()) { return true; @@ -69,7 +69,7 @@ public class step9_try { throws MalThrowable { while (is_macro_call(ast, env)) { MalSymbol a0 = (MalSymbol)((MalList)ast).nth(0); - MalFunction mac = (MalFunction) env.get(a0.getName()); + MalFunction mac = (MalFunction) env.get(a0); ast = mac.apply(((MalList)ast).rest()); } return ast; @@ -77,8 +77,7 @@ public class step9_try { public static MalVal eval_ast(MalVal ast, Env env) throws MalThrowable { if (ast instanceof MalSymbol) { - MalSymbol sym = (MalSymbol)ast; - return env.get(sym.getName()); + return env.get((MalSymbol)ast); } else if (ast instanceof MalList) { MalList old_lst = (MalList)ast; MalList new_lst = ast.list_Q() ? new MalList() @@ -124,7 +123,7 @@ public class step9_try { a1 = ast.nth(1); a2 = ast.nth(2); res = EVAL(a2, env); - env.set(((MalSymbol)a1).getName(), res); + env.set(((MalSymbol)a1), res); return res; case "let*": a1 = ast.nth(1); @@ -135,7 +134,7 @@ public class step9_try { for(int i=0; i<((MalList)a1).size(); i+=2) { key = (MalSymbol)((MalList)a1).nth(i); val = ((MalList)a1).nth(i+1); - let_env.set(key.getName(), EVAL(val, let_env)); + let_env.set(key, EVAL(val, let_env)); } orig_ast = a2; env = let_env; @@ -150,7 +149,7 @@ public class step9_try { a2 = ast.nth(2); res = EVAL(a2, env); ((MalFunction)res).setMacro(); - env.set(((MalSymbol)a1).getName(), res); + env.set((MalSymbol)a1, res); return res; case "macroexpand": a1 = ast.nth(1); @@ -239,9 +238,9 @@ public class step9_try { // core.java: defined using Java for (String key : core.ns.keySet()) { - repl_env.set(key, core.ns.get(key)); + repl_env.set(new MalSymbol(key), core.ns.get(key)); } - repl_env.set("eval", new MalFunction() { + repl_env.set(new MalSymbol("eval"), new MalFunction() { public MalVal apply(MalList args) throws MalThrowable { return EVAL(args.nth(0), repl_env); } @@ -250,11 +249,10 @@ public class step9_try { for (Integer i=1; i < args.length; i++) { _argv.conj_BANG(new MalString(args[i])); } - repl_env.set("*ARGV*", _argv); + repl_env.set(new MalSymbol("*ARGV*"), _argv); // core.mal: defined using the language itself - RE(repl_env, "(def! *host-language* \"java\")"); RE(repl_env, "(def! not (fn* (a) (if a false true)))"); RE(repl_env, "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); RE(repl_env, "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); @@ -271,7 +269,6 @@ public class step9_try { } // repl loop - RE(repl_env, "(println (str \"Mal [\" *host-language* \"]\"))"); while (true) { String line; try { diff --git a/java/src/main/java/mal/stepA_interop.java b/java/src/main/java/mal/stepA_interop.java index 75c04024..1fa11250 100644 --- a/java/src/main/java/mal/stepA_interop.java +++ b/java/src/main/java/mal/stepA_interop.java @@ -54,8 +54,8 @@ public class stepA_interop { if (ast instanceof MalList) { MalVal a0 = ((MalList)ast).nth(0); if (a0 instanceof MalSymbol && - env.find(((MalSymbol)a0).getName()) != null) { - MalVal mac = env.get(((MalSymbol)a0).getName()); + env.find(((MalSymbol)a0)) != null) { + MalVal mac = env.get(((MalSymbol)a0)); if (mac instanceof MalFunction && ((MalFunction)mac).isMacro()) { return true; @@ -69,7 +69,7 @@ public class stepA_interop { throws MalThrowable { while (is_macro_call(ast, env)) { MalSymbol a0 = (MalSymbol)((MalList)ast).nth(0); - MalFunction mac = (MalFunction) env.get(a0.getName()); + MalFunction mac = (MalFunction) env.get(a0); ast = mac.apply(((MalList)ast).rest()); } return ast; @@ -77,8 +77,7 @@ public class stepA_interop { public static MalVal eval_ast(MalVal ast, Env env) throws MalThrowable { if (ast instanceof MalSymbol) { - MalSymbol sym = (MalSymbol)ast; - return env.get(sym.getName()); + return env.get((MalSymbol)ast); } else if (ast instanceof MalList) { MalList old_lst = (MalList)ast; MalList new_lst = ast.list_Q() ? new MalList() @@ -124,7 +123,7 @@ public class stepA_interop { a1 = ast.nth(1); a2 = ast.nth(2); res = EVAL(a2, env); - env.set(((MalSymbol)a1).getName(), res); + env.set(((MalSymbol)a1), res); return res; case "let*": a1 = ast.nth(1); @@ -135,7 +134,7 @@ public class stepA_interop { for(int i=0; i<((MalList)a1).size(); i+=2) { key = (MalSymbol)((MalList)a1).nth(i); val = ((MalList)a1).nth(i+1); - let_env.set(key.getName(), EVAL(val, let_env)); + let_env.set(key, EVAL(val, let_env)); } orig_ast = a2; env = let_env; @@ -150,7 +149,7 @@ public class stepA_interop { a2 = ast.nth(2); res = EVAL(a2, env); ((MalFunction)res).setMacro(); - env.set(((MalSymbol)a1).getName(), res); + env.set((MalSymbol)a1, res); return res; case "macroexpand": a1 = ast.nth(1); @@ -239,9 +238,9 @@ public class stepA_interop { // core.java: defined using Java for (String key : core.ns.keySet()) { - repl_env.set(key, core.ns.get(key)); + repl_env.set(new MalSymbol(key), core.ns.get(key)); } - repl_env.set("eval", new MalFunction() { + repl_env.set(new MalSymbol("eval"), new MalFunction() { public MalVal apply(MalList args) throws MalThrowable { return EVAL(args.nth(0), repl_env); } @@ -250,7 +249,7 @@ public class stepA_interop { for (Integer i=1; i < args.length; i++) { _argv.conj_BANG(new MalString(args[i])); } - repl_env.set("*ARGV*", _argv); + repl_env.set(new MalSymbol("*ARGV*"), _argv); // core.mal: defined using the language itself diff --git a/java/src/main/java/mal/types.java b/java/src/main/java/mal/types.java index 7ad419a9..a8a2dfa6 100644 --- a/java/src/main/java/mal/types.java +++ b/java/src/main/java/mal/types.java @@ -134,6 +134,7 @@ public class types { public static class MalSymbol extends MalVal { String value; public MalSymbol(String v) { value = v; } + public MalSymbol(MalString v) { value = v.getValue(); } public MalSymbol copy() throws MalThrowable { return this; } public String getName() { return value; } @@ -152,7 +153,9 @@ public class types { return "\"" + value + "\""; } public String toString(Boolean print_readably) { - if (print_readably) { + if (value.length() > 0 && value.charAt(0) == '\u029e') { + return ":" + value.substring(1); + } else if (print_readably) { return "\"" + printer.escapeString(value) + "\""; } else { return value; diff --git a/js/core.js b/js/core.js index 3ab21177..d2be63b7 100644 --- a/js/core.js +++ b/js/core.js @@ -95,7 +95,10 @@ function concat(lst) { return lst.concat.apply(lst, Array.prototype.slice.call(arguments, 1)); } -function nth(lst, idx) { return lst[idx]; } +function nth(lst, idx) { + if (idx < lst.length) { return lst[idx]; } + else { throw new Error("nth: index out of range"); } +} function first(lst) { return lst[0]; } @@ -105,7 +108,8 @@ function empty_Q(lst) { return lst.length === 0; } function count(s) { if (Array.isArray(s)) { return s.length; } - else { return Object.keys(s).length; } + else if (s === null) { return 0; } + else { return Object.keys(s).length; } } function conj(lst) { @@ -165,6 +169,8 @@ var ns = {'type': types._obj_type, 'false?': types._false_Q, 'symbol': types._symbol, 'symbol?': types._symbol_Q, + 'keyword': types._keyword, + 'keyword?': types._keyword_Q, 'pr-str': pr_str, 'str': str, diff --git a/js/env.js b/js/env.js index 3c9eac86..421b2200 100644 --- a/js/env.js +++ b/js/env.js @@ -26,15 +26,27 @@ function Env(outer, binds, exprs) { return this; } Env.prototype.find = function (key) { - if (key in this.data) { return this; } + if (!key.constructor || key.constructor.name !== 'Symbol') { + throw new Error("env.find key must be a symbol") + } + if (key.value 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.set = function(key, value) { + if (!key.constructor || key.constructor.name !== 'Symbol') { + throw new Error("env.set key must be a symbol") + } + this.data[key.value] = value; + return value; +}; Env.prototype.get = function(key) { + if (!key.constructor || key.constructor.name !== 'Symbol') { + throw new Error("env.get key must be a symbol") + } var env = this.find(key); - if (!env) { throw new Error("'" + key + "' not found"); } - return env.data[key]; + if (!env) { throw new Error("'" + key.value + "' not found"); } + return env.data[key.value]; }; exports.Env = env.Env = Env; diff --git a/js/printer.js b/js/printer.js index f3836e00..4f267e73 100644 --- a/js/printer.js +++ b/js/printer.js @@ -26,13 +26,17 @@ function _pr_str(obj, print_readably) { } return "{" + ret.join(' ') + "}"; case 'string': - if (_r) { - return '"' + obj.replace(/\\/, "\\\\") + if (obj[0] === '\u029e') { + return ':' + obj.slice(1); + } else if (_r) { + return '"' + obj.replace(/\\/g, "\\\\") .replace(/"/g, '\\"') .replace(/\n/g, "\\n") + '"'; // string } else { return obj; } + case 'keyword': + return ':' + obj.slice(1); case 'nil': return "nil"; case 'atom': diff --git a/js/reader.js b/js/reader.js index 3f2f6ca4..dd4de9a6 100644 --- a/js/reader.js +++ b/js/reader.js @@ -35,6 +35,8 @@ function read_atom (reader) { return token.slice(1,token.length-1) .replace(/\\"/g, '"') .replace(/\\n/g, "\n"); // string + } else if (token[0] === ":") { + return types._keyword(token.slice(1)); } else if (token === "nil") { return null; } else if (token === "true") { diff --git a/js/step3_env.js b/js/step3_env.js index 1f5efb76..ca8f8180 100644 --- a/js/step3_env.js +++ b/js/step3_env.js @@ -47,7 +47,7 @@ function _EVAL(ast, env) { 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)); + let_env.set(a1[i], EVAL(a1[i+1], let_env)); } return EVAL(a2, let_env); default: @@ -70,10 +70,10 @@ function PRINT(exp) { var repl_env = new Env(); var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); }; -repl_env.set('+', function(a,b){return a+b;}); -repl_env.set('-', function(a,b){return a-b;}); -repl_env.set('*', function(a,b){return a*b;}); -repl_env.set('/', function(a,b){return a/b;}); +repl_env.set(types._symbol('+'), function(a,b){return a+b;}); +repl_env.set(types._symbol('-'), function(a,b){return a-b;}); +repl_env.set(types._symbol('*'), function(a,b){return a*b;}); +repl_env.set(types._symbol('/'), function(a,b){return a/b;}); // repl loop if (typeof require !== 'undefined' && require.main === module) { diff --git a/js/step4_if_fn_do.js b/js/step4_if_fn_do.js index 27715fa0..937d0eab 100644 --- a/js/step4_if_fn_do.js +++ b/js/step4_if_fn_do.js @@ -48,7 +48,7 @@ function _EVAL(ast, env) { 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)); + let_env.set(a1[i], EVAL(a1[i+1], let_env)); } return EVAL(a2, let_env); case "do": @@ -86,7 +86,7 @@ var repl_env = new Env(); var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); }; // core.js: defined using javascript -for (var n in core.ns) { repl_env.set(n, core.ns[n]); } +for (var n in core.ns) { repl_env.set(types._symbol(n), core.ns[n]); } // core.mal: 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 659ac408..03de2cca 100644 --- a/js/step5_tco.js +++ b/js/step5_tco.js @@ -50,7 +50,7 @@ function _EVAL(ast, env) { 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)); + let_env.set(a1[i], EVAL(a1[i+1], let_env)); } ast = a2; env = let_env; @@ -97,7 +97,7 @@ var repl_env = new Env(); var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); }; // core.js: defined using javascript -for (var n in core.ns) { repl_env.set(n, core.ns[n]); } +for (var n in core.ns) { repl_env.set(types._symbol(n), core.ns[n]); } // core.mal: 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 4c8ed179..813c66d9 100644 --- a/js/step6_file.js +++ b/js/step6_file.js @@ -50,7 +50,7 @@ function _EVAL(ast, env) { 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)); + let_env.set(a1[i], EVAL(a1[i+1], let_env)); } ast = a2; env = let_env; @@ -97,9 +97,10 @@ var repl_env = new Env(); var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); }; // core.js: defined using javascript -for (var n in core.ns) { repl_env.set(n, core.ns[n]); } -repl_env.set('eval', function(ast) { return EVAL(ast, repl_env); }); -repl_env.set('*ARGV*', []); +for (var n in core.ns) { repl_env.set(types._symbol(n), core.ns[n]); } +repl_env.set(types._symbol('eval'), function(ast) { + return EVAL(ast, repl_env); }); +repl_env.set(types._symbol('*ARGV*'), []); // core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); diff --git a/js/step7_quote.js b/js/step7_quote.js index 62596726..b39ebbd4 100644 --- a/js/step7_quote.js +++ b/js/step7_quote.js @@ -70,7 +70,7 @@ function _EVAL(ast, env) { 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)); + let_env.set(a1[i], EVAL(a1[i+1], let_env)); } ast = a2; env = let_env; @@ -122,9 +122,10 @@ var repl_env = new Env(); var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); }; // core.js: defined using javascript -for (var n in core.ns) { repl_env.set(n, core.ns[n]); } -repl_env.set('eval', function(ast) { return EVAL(ast, repl_env); }); -repl_env.set('*ARGV*', []); +for (var n in core.ns) { repl_env.set(types._symbol(n), core.ns[n]); } +repl_env.set(types._symbol('eval'), function(ast) { + return EVAL(ast, repl_env); }); +repl_env.set(types._symbol('*ARGV*'), []); // core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); diff --git a/js/step8_macros.js b/js/step8_macros.js index f51592bd..397379e7 100644 --- a/js/step8_macros.js +++ b/js/step8_macros.js @@ -36,8 +36,8 @@ function quasiquote(ast) { function is_macro_call(ast, env) { return types._list_Q(ast) && types._symbol_Q(ast[0]) && - env.find(ast[0].value) && - env.get(ast[0].value)._ismacro_; + env.find(ast[0]) && + env.get(ast[0])._ismacro_; } function macroexpand(ast, env) { @@ -88,7 +88,7 @@ function _EVAL(ast, env) { 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)); + let_env.set(a1[i], EVAL(a1[i+1], let_env)); } ast = a2; env = let_env; @@ -146,9 +146,10 @@ var repl_env = new Env(); var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); }; // core.js: defined using javascript -for (var n in core.ns) { repl_env.set(n, core.ns[n]); } -repl_env.set('eval', function(ast) { return EVAL(ast, repl_env); }); -repl_env.set('*ARGV*', []); +for (var n in core.ns) { repl_env.set(types._symbol(n), core.ns[n]); } +repl_env.set(types._symbol('eval'), function(ast) { + return EVAL(ast, repl_env); }); +repl_env.set(types._symbol('*ARGV*'), []); // core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); @@ -157,7 +158,7 @@ rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if ( rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); if (typeof process !== 'undefined' && process.argv.length > 2) { - repl_env.set('*ARGV*', process.argv.slice(3)); + repl_env.set(types._symbol('*ARGV*'), process.argv.slice(3)); rep('(load-file "' + process.argv[2] + '")'); process.exit(0); } diff --git a/js/step9_try.js b/js/step9_try.js index ff02f725..6be44749 100644 --- a/js/step9_try.js +++ b/js/step9_try.js @@ -36,8 +36,8 @@ function quasiquote(ast) { function is_macro_call(ast, env) { return types._list_Q(ast) && types._symbol_Q(ast[0]) && - env.find(ast[0].value) && - env.get(ast[0].value)._ismacro_; + env.find(ast[0]) && + env.get(ast[0])._ismacro_; } function macroexpand(ast, env) { @@ -88,7 +88,7 @@ function _EVAL(ast, env) { 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)); + let_env.set(a1[i], EVAL(a1[i+1], let_env)); } ast = a2; env = let_env; @@ -157,19 +157,19 @@ var repl_env = new Env(); var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); }; // core.js: defined using javascript -for (var n in core.ns) { repl_env.set(n, core.ns[n]); } -repl_env.set('eval', function(ast) { return EVAL(ast, repl_env); }); -repl_env.set('*ARGV*', []); +for (var n in core.ns) { repl_env.set(types._symbol(n), core.ns[n]); } +repl_env.set(types._symbol('eval'), function(ast) { + return EVAL(ast, repl_env); }); +repl_env.set(types._symbol('*ARGV*'), []); // core.mal: defined using the language itself -rep("(def! *host-language* \"javascript\")") rep("(def! not (fn* (a) (if a false true)))"); rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); if (typeof process !== 'undefined' && process.argv.length > 2) { - repl_env.set('*ARGV*', process.argv.slice(3)); + repl_env.set(types._symbol('*ARGV*'), process.argv.slice(3)); rep('(load-file "' + process.argv[2] + '")'); process.exit(0); } @@ -177,7 +177,6 @@ if (typeof process !== 'undefined' && process.argv.length > 2) { // repl loop if (typeof require !== 'undefined' && require.main === module) { // Synchronous node.js commandline mode - rep("(println (str \"Mal [\" *host-language* \"]\"))"); while (true) { var line = readline.readline("user> "); if (line === null) { break; } diff --git a/js/stepA_interop.js b/js/stepA_interop.js index 0955b7f7..456c006d 100644 --- a/js/stepA_interop.js +++ b/js/stepA_interop.js @@ -36,8 +36,8 @@ function quasiquote(ast) { function is_macro_call(ast, env) { return types._list_Q(ast) && types._symbol_Q(ast[0]) && - env.find(ast[0].value) && - env.get(ast[0].value)._ismacro_; + env.find(ast[0]) && + env.get(ast[0])._ismacro_; } function macroexpand(ast, env) { @@ -88,7 +88,7 @@ function _EVAL(ast, env) { 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)); + let_env.set(a1[i], EVAL(a1[i+1], let_env)); } ast = a2; env = let_env; @@ -163,9 +163,10 @@ var repl_env = new Env(); var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); }; // core.js: defined using javascript -for (var n in core.ns) { repl_env.set(n, core.ns[n]); } -repl_env.set('eval', function(ast) { return EVAL(ast, repl_env); }); -repl_env.set('*ARGV*', []); +for (var n in core.ns) { repl_env.set(types._symbol(n), core.ns[n]); } +repl_env.set(types._symbol('eval'), function(ast) { + return EVAL(ast, repl_env); }); +repl_env.set(types._symbol('*ARGV*'), []); // core.mal: defined using the language itself rep("(def! *host-language* \"javascript\")") @@ -175,7 +176,7 @@ rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if ( rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); if (typeof process !== 'undefined' && process.argv.length > 2) { - repl_env.set('*ARGV*', process.argv.slice(3)); + repl_env.set(types._symbol('*ARGV*'), process.argv.slice(3)); rep('(load-file "' + process.argv[2] + '")'); process.exit(0); } diff --git a/js/types.js b/js/types.js index de90d547..848a4845 100644 --- a/js/types.js +++ b/js/types.js @@ -19,7 +19,7 @@ function _obj_type(obj) { switch (typeof(obj)) { case 'number': return 'number'; case 'function': return 'function'; - case 'string': return 'string'; + case 'string': return obj[0] == '\u029e' ? 'keyword' : 'string'; default: throw new Error("Unknown type '" + typeof(obj) + "'"); } } @@ -99,6 +99,13 @@ function _symbol(name) { return new Symbol(name); } function _symbol_Q(obj) { return obj instanceof Symbol; } +// Keywords +function _keyword(name) { return "\u029e" + name; } +function _keyword_Q(obj) { + return typeof obj === 'string' && obj[0] === '\u029e'; +} + + // Functions function _function(Eval, Env, ast, env, params) { var fn = function() { @@ -148,6 +155,7 @@ function _hash_map_Q(hm) { return typeof hm === "object" && !Array.isArray(hm) && !(hm === null) && + !(hm instanceof Symbol) && !(hm instanceof Atom); } function _assoc_BANG(hm) { @@ -157,10 +165,6 @@ function _assoc_BANG(hm) { for (var i=1; i @@ -38,7 +40,7 @@ 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)))} +hash_map_pr_str = {$(foreach v,$(call __get_obj_values,$(1)),$(foreach vval,$(foreach hcode,$(word 3,$(subst _, ,$(1))),$(patsubst $(1)_%,%,$(v:%_value=%))),$(if $(filter $(__keyword)%,$(vval)),$(patsubst $(__keyword)%,$(COLON)%,$(vval)),"$(vval)")) $(call _pr_str,$($(v)),$(2)))} atom_pr_str = (atom $(call _pr_str,$($(1)_value),$(2))) diff --git a/make/reader.mk b/make/reader.mk index 8d045961..85717858 100755 --- a/make/reader.mk +++ b/make/reader.mk @@ -52,6 +52,17 @@ $(foreach ch,$(word 1,$($(1))),\ )) endef +define READ_KEYWORD +$(foreach ch,$(word 1,$($(1))),\ + $(if $(ch),\ + $(if $(filter $(_TOKEN_DELIMS),$(ch)),\ + ,\ + $(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\ + $(and $(READER_DEBUG),$(info READ_KEYWORD ch: $(ch) | $($(1))))\ + $(ch)$(strip $(call READ_KEYWORD,$(1)))),\ + )) +endef + define READ_ATOM $(foreach ch,$(word 1,$($(1))),\ $(if $(filter $(NUMBERS),$(ch)),\ @@ -62,6 +73,9 @@ $(foreach ch,$(word 1,$($(1))),\ $(eval $(if $(filter $(DQUOTE),$(word 1,$($(1)))),\ $(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1)))),\ $(call _error,Expected '$(DQUOTE)' in; $($(1))))),\ + $(if $(filter $(COLON),$(ch)),\ + $(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\ + $(call _keyword,$(call READ_KEYWORD,$(1))),\ $(foreach sym,$(call READ_SYMBOL,$(1)),\ $(if $(call _EQ,nil,$(sym)),\ $(__nil),\ @@ -69,7 +83,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 diff --git a/make/step3_env.mk b/make/step3_env.mk index b548874c..ddcb70fe 100644 --- a/make/step3_env.mk +++ b/make/step3_env.mk @@ -57,7 +57,8 @@ $(if $(__ERROR),,\ $(foreach a1,$(call _nth,$(1),1),\ $(foreach a2,$(call _nth,$(1),2),\ $(foreach res,$(call EVAL,$(a2),$(2)),\ - $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),)))),\ + $(if $(__ERROR),,\ + $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),))))),\ $(if $(call _EQ,let*,$($(a0)_value)),\ $(foreach a1,$(call _nth,$(1),1),\ $(foreach a2,$(call _nth,$(1),2),\ diff --git a/make/step4_if_fn_do.mk b/make/step4_if_fn_do.mk index 47f7fe40..22d1d214 100644 --- a/make/step4_if_fn_do.mk +++ b/make/step4_if_fn_do.mk @@ -57,7 +57,8 @@ $(if $(__ERROR),,\ $(foreach a1,$(call _nth,$(1),1),\ $(foreach a2,$(call _nth,$(1),2),\ $(foreach res,$(call EVAL,$(a2),$(2)),\ - $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),)))),\ + $(if $(__ERROR),,\ + $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),))))),\ $(if $(call _EQ,let*,$($(a0)_value)),\ $(foreach a1,$(call _nth,$(1),1),\ $(foreach a2,$(call _nth,$(1),2),\ diff --git a/make/step6_file.mk b/make/step6_file.mk index f0298a98..b05f723e 100644 --- a/make/step6_file.mk +++ b/make/step6_file.mk @@ -57,7 +57,8 @@ $(if $(__ERROR),,\ $(foreach a1,$(call _nth,$(1),1),\ $(foreach a2,$(call _nth,$(1),2),\ $(foreach res,$(call EVAL,$(a2),$(2)),\ - $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),)))),\ + $(if $(__ERROR),,\ + $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),))))),\ $(if $(call _EQ,let*,$($(a0)_value)),\ $(foreach a1,$(call _nth,$(1),1),\ $(foreach a2,$(call _nth,$(1),2),\ diff --git a/make/step7_quote.mk b/make/step7_quote.mk index df3745fd..2af02488 100644 --- a/make/step7_quote.mk +++ b/make/step7_quote.mk @@ -70,7 +70,8 @@ $(if $(__ERROR),,\ $(foreach a1,$(call _nth,$(1),1),\ $(foreach a2,$(call _nth,$(1),2),\ $(foreach res,$(call EVAL,$(a2),$(2)),\ - $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),)))),\ + $(if $(__ERROR),,\ + $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),))))),\ $(if $(call _EQ,let*,$($(a0)_value)),\ $(foreach a1,$(call _nth,$(1),1),\ $(foreach a2,$(call _nth,$(1),2),\ diff --git a/make/step8_macros.mk b/make/step8_macros.mk index 96e5d313..172e64d3 100644 --- a/make/step8_macros.mk +++ b/make/step8_macros.mk @@ -82,7 +82,8 @@ $(if $(__ERROR),,\ $(foreach a1,$(call _nth,$(1),1),\ $(foreach a2,$(call _nth,$(1),2),\ $(foreach res,$(call EVAL,$(a2),$(2)),\ - $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),)))),\ + $(if $(__ERROR),,\ + $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),))))),\ $(if $(call _EQ,let*,$($(a0)_value)),\ $(foreach a1,$(call _nth,$(1),1),\ $(foreach a2,$(call _nth,$(1),2),\ diff --git a/make/step9_try.mk b/make/step9_try.mk index 4c5b8c11..14743029 100644 --- a/make/step9_try.mk +++ b/make/step9_try.mk @@ -82,7 +82,8 @@ $(if $(__ERROR),,\ $(foreach a1,$(call _nth,$(1),1),\ $(foreach a2,$(call _nth,$(1),2),\ $(foreach res,$(call EVAL,$(a2),$(2)),\ - $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),)))),\ + $(if $(__ERROR),,\ + $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),))))),\ $(if $(call _EQ,let*,$($(a0)_value)),\ $(foreach a1,$(call _nth,$(1),1),\ $(foreach a2,$(call _nth,$(1),2),\ diff --git a/make/stepA_interop.mk b/make/stepA_interop.mk index 050366c8..80909c77 100644 --- a/make/stepA_interop.mk +++ b/make/stepA_interop.mk @@ -82,7 +82,8 @@ $(if $(__ERROR),,\ $(foreach a1,$(call _nth,$(1),1),\ $(foreach a2,$(call _nth,$(1),2),\ $(foreach res,$(call EVAL,$(a2),$(2)),\ - $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),)))),\ + $(if $(__ERROR),,\ + $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),))))),\ $(if $(call _EQ,let*,$($(a0)_value)),\ $(foreach a1,$(call _nth,$(1),1),\ $(foreach a2,$(call _nth,$(1),2),\ diff --git a/make/types.mk b/make/types.mk index b971fb12..02588f76 100644 --- a/make/types.mk +++ b/make/types.mk @@ -15,6 +15,7 @@ include $(_TOP_DIR)util.mk __obj_magic = ⍄⁊ # \u2256 __equal = ≛ +__keyword = ʞ __obj_hash_code = 0 __new_obj_hash_code = $(eval __obj_hash_code := $(call gmsl_plus,1,$(__obj_hash_code)))$(__obj_hash_code) @@ -50,6 +51,8 @@ __var_print = $(foreach v,$(1),\ $(call __var_print,$($(vkey)),$(2)$(SPACE)$(SPACE)$(SPACE)$(SPACE)))),\ $(if $(call _symbol?,$(v)),\ $(info $(2)$(var): $($(v)_value)),\ + $(if $(call _keyword?,$(v)),\ + $(info $(2)$(var): $($(v)_value)),\ $(if $(call _number?,$(v)),\ $(info $(2)$(var): $(call int_decode,$($(v)_value))),\ $(if $(call _nil?,$(v)),\ @@ -58,7 +61,7 @@ __var_print = $(foreach v,$(1),\ $(if $(word 6,$(value $(v)_value)),\ $(info $(2)$(var): $(wordlist 1,5,$(value $(v)_value))...),\ $(info $(2)$(var): $(value $(v)_value))),\ - $(info $(2)$(var): ...))))))))) + $(info $(2)$(var): ...)))))))))) _visualize_memory = $(foreach var,$(sort $(foreach vv,$(filter $(__obj_magic)_%,$(.VARIABLES)),$(call __var_name,$(vv)))),$(call __var_print,$(__obj_magic)_$(var))) @@ -83,7 +86,8 @@ _obj_type = $(strip \ $(if $(filter $(__obj_magic)_list_%,$(1)),list,\ $(if $(filter $(__obj_magic)_numb_%,$(1)),number,\ $(if $(filter $(__obj_magic)_func_%,$(1)),function,\ - $(if $(filter $(__obj_magic)_strn_%,$(1)),string,\ + $(if $(filter $(__obj_magic)_strn_%,$(1)),\ + $(if $(filter $(__keyword)%,$($(1)_value)),keyword,string),\ $(if $(filter $(__obj_magic)__nil_%,$(1)),nil,\ $(if $(filter $(__obj_magic)_true_%,$(1)),true,\ $(if $(filter $(__obj_magic)_fals_%,$(1)),false,\ @@ -109,7 +113,7 @@ _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))),\ + $(if $(or $(call _string?,$(1)),$(call _symbol?,$(1)),$(call _keyword?,$(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))),\ @@ -130,6 +134,11 @@ _symbol = $(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_symb_$(hcod _symbol? = $(if $(filter $(__obj_magic)_symb_%,$(1)),$(__true),) +# Keywords +_keyword = $(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_strn_$(hcode)$(eval $(__obj_magic)_strn_$(hcode)_value := $(__keyword)$(1))) +_keyword? = $(if $(filter $(__obj_magic)_strn_%,$(1)),$(if $(filter $(__keyword)%,$($(1)_value)),$(__true),)) + + # 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))) @@ -176,6 +185,9 @@ _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))),) +_dissoc_seq! = $(foreach key,$(2),\ + $(call _dissoc!,$(1),$(call str_decode,$($(key)_value)))) + # 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)) diff --git a/make/util.mk b/make/util.mk index 43923fc0..eff258aa 100644 --- a/make/util.mk +++ b/make/util.mk @@ -10,6 +10,7 @@ include $(_TOP_DIR)gmsl.mk SEMI := ; COMMA := , +COLON := : LCURLY := { RCURLY := } LPAREN := ( diff --git a/mal/core.mal b/mal/core.mal index a12c7e4b..a6b6bb90 100644 --- a/mal/core.mal +++ b/mal/core.mal @@ -6,6 +6,8 @@ ["false?" false?] ["symbol" symbol] ["symbol?" symbol?] + ["keyword" keyword] + ["keyword?" keyword?] ["pr-str" pr-str] ["str" str] diff --git a/perl/core.pm b/perl/core.pm index eeee77eb..7d70278c 100644 --- a/perl/core.pm +++ b/perl/core.pm @@ -7,7 +7,8 @@ use Time::HiRes qw(time); use readline; use types qw(_sequential_Q _equal_Q _clone $nil $true $false - _symbol_Q _nil_Q _true_Q _false_Q _list_Q _vector_Q + _nil_Q _true_Q _false_Q + _symbol _symbol_Q _keyword _keyword_Q _list_Q _vector_Q _hash_map _hash_map_Q _assoc_BANG _dissoc_BANG _atom_Q); use reader qw(read_str); use printer qw(_pr_str); @@ -101,12 +102,27 @@ sub concat { List->new(\@new_arr); } -sub nth { my ($seq,$i) = @_; return scalar(@{$seq->{val}}) > $i ? $seq->nth($i) : $nil; } +sub nth { + my ($seq,$i) = @_; + if (@{$seq->{val}} > $i) { + return scalar($seq->nth($i)); + } else { + die "nth: index out of bounds"; + } +} sub first { my ($seq) = @_; return scalar(@{$seq->{val}}) > 0 ? $seq->nth(0) : $nil; } sub rest { return $_[0]->rest(); } +sub count { + if (_nil_Q($_[0])) { + return Integer->new(0); + } else { + return Integer->new(scalar(@{$_[0]->{val}})) + } +} + sub apply { my @all_args = @{$_[0]->{val}}; my $f = $all_args[0]; @@ -167,7 +183,10 @@ our $core_ns = { 'nil?' => sub { _nil_Q($_[0]->nth(0)) ? $true : $false }, 'true?' => sub { _true_Q($_[0]->nth(0)) ? $true : $false }, 'false?' => sub { _false_Q($_[0]->nth(0)) ? $true : $false }, + 'symbol' => sub { Symbol->new(${$_[0]->nth(0)}) }, 'symbol?' => sub { _symbol_Q($_[0]->nth(0)) ? $true : $false }, + 'keyword' => sub { _keyword(${$_[0]->nth(0)}) }, + 'keyword?' => sub { _keyword_Q($_[0]->nth(0)) ? $true : $false }, 'pr-str' => sub { pr_str($_[0]) }, 'str' => sub { str($_[0]) }, @@ -206,7 +225,7 @@ our $core_ns = { 'cons' => sub { cons($_[0]->nth(0), $_[0]->nth(1)) }, 'concat' => sub { concat(@{$_[0]->{val}}) }, 'empty?' => sub { scalar(@{$_[0]->nth(0)->{val}}) == 0 ? $true : $false }, - 'count' => sub { Integer->new(scalar(@{$_[0]->nth(0)->{val}})) }, + 'count' => sub { count($_[0]->nth(0)) }, 'apply' => sub { apply($_[0]) }, 'map' => sub { mal_map($_[0]->nth(0), $_[0]->nth(1)) }, 'conj' => sub { die "not implemented\n"; }, diff --git a/perl/env.pm b/perl/env.pm index 372eecd5..80125652 100644 --- a/perl/env.pm +++ b/perl/env.pm @@ -28,20 +28,20 @@ use Exporter 'import'; } sub find { my ($self, $key) = @_; - if (exists $self->{$key}) { return $self; } + if (exists $self->{$$key}) { return $self; } elsif ($self->{__outer__}) { return $self->{__outer__}->find($key); } else { return undef; } } sub set { my ($self, $key, $value) = @_; - $self->{$key} = $value; + $self->{$$key} = $value; return $value } sub get { my ($self, $key) = @_; my $env = $self->find($key); - die "'" . $key . "' not found\n" unless $env; - return $env->{$key}; + die "'" . $$key . "' not found\n" unless $env; + return $env->{$$key}; } } diff --git a/perl/printer.pm b/perl/printer.pm index 9ce67078..7b00b1e9 100644 --- a/perl/printer.pm +++ b/perl/printer.pm @@ -30,7 +30,9 @@ sub _pr_str { return '{' . join(' ', @elems) . '}'; } when(/^String/) { - if ($_r) { + if ($$obj =~ /^\x{029e}/) { + return ':' . substr($$obj,1); + } elsif ($_r) { my $str = $$obj; $str =~ s/\\/\\\\/g; $str =~ s/"/\\"/g; diff --git a/perl/reader.pm b/perl/reader.pm index cd4c5653..501f9924 100644 --- a/perl/reader.pm +++ b/perl/reader.pm @@ -6,7 +6,7 @@ no if $] >= 5.018, warnings => "experimental::smartmatch"; use Exporter 'import'; our @EXPORT_OK = qw( read_str ); -use types qw($nil $true $false _hash_map); +use types qw($nil $true $false _keyword _hash_map); use Data::Dumper; @@ -37,6 +37,7 @@ sub read_atom { $str =~ s/\\n/\n/g; return String->new($str) } + when(/^:/) { return _keyword(substr($token,1)) } when(/^nil$/) { return $nil } when(/^true$/) { return $true } when(/^false$/) { return $false } diff --git a/perl/readline.pm b/perl/readline.pm index f0710b1e..0629f395 100644 --- a/perl/readline.pm +++ b/perl/readline.pm @@ -1,12 +1,12 @@ # To get readline line editing functionality, please install -# Term::ReadLine::Gnu (GPL) or Term::ReadLine::Perl (GPL, Artistic) -# from CPAN. +# Term::ReadKey and either Term::ReadLine::Gnu (GPL) or +# Term::ReadLine::Perl (GPL, Artistic) from CPAN. package readline; use strict; use warnings; use Exporter 'import'; -our @EXPORT_OK = qw( mal_readline ); +our @EXPORT_OK = qw( mal_readline set_rl_mode ); use Term::ReadLine; @@ -36,6 +36,13 @@ sub load_history { close $fh; } +my $rl_mode = "terminal"; + +sub set_rl_mode { + my($mode) = @_; + $rl_mode = $mode; +} + sub mal_readline { my($prompt) = @_; my $line = undef; @@ -44,11 +51,21 @@ sub mal_readline { load_history(); } - if (defined ($line = $_rl->readline($prompt))) { - save_line($line); - return $line; + if ($rl_mode eq "terminal") { + if (defined ($line = $_rl->readline($prompt))) { + save_line($line); + return $line; + } else { + return undef; + } } else { - return undef; + print "$prompt"; + if (defined ($line = readline(*STDIN))) { + save_line($line); + return $line; + } else { + return undef; + } } } 1; diff --git a/perl/step0.5_repl.pl b/perl/step0.5_repl.pl new file mode 100644 index 00000000..d8a9d9fd --- /dev/null +++ b/perl/step0.5_repl.pl @@ -0,0 +1,33 @@ +use strict; +use warnings FATAL => qw(all); +use readline qw(readline); + +# read +sub READ { + my $str = shift; + return $str; +} + +# eval +sub EVAL { + my($ast, $env) = @_; + return eval($ast); +} + +# print +sub PRINT { + my $exp = shift; + return $exp; +} + +# repl +sub REP { + my $str = shift; + return PRINT(EVAL(READ($str), {})); +} + +while (1) { + my $line = readline("user> "); + if (! defined $line) { last; } + print(REP($line), "\n"); +} diff --git a/perl/step1_read_print.pl b/perl/step1_read_print.pl index 82883368..26c7bbf7 100644 --- a/perl/step1_read_print.pl +++ b/perl/step1_read_print.pl @@ -3,7 +3,7 @@ use warnings FATAL => qw(all); no if $] >= 5.018, warnings => "experimental::smartmatch"; use File::Basename; use lib dirname (__FILE__); -use readline qw(mal_readline); +use readline qw(mal_readline set_rl_mode); use feature qw(switch); use reader; @@ -33,6 +33,9 @@ sub REP { return PRINT(EVAL(READ($str), {})); } +if (scalar(@ARGV) > 0 && $ARGV[0] eq "--raw") { + set_rl_mode("raw"); +} while (1) { my $line = mal_readline("user> "); if (! defined $line) { last; } diff --git a/perl/step2_eval.pl b/perl/step2_eval.pl index c3759a52..858a385d 100644 --- a/perl/step2_eval.pl +++ b/perl/step2_eval.pl @@ -3,7 +3,7 @@ use warnings FATAL => qw(all); no if $] >= 5.018, warnings => "experimental::smartmatch"; use File::Basename; use lib dirname (__FILE__); -use readline qw(mal_readline); +use readline qw(mal_readline set_rl_mode); use feature qw(switch); use Data::Dumper; @@ -80,6 +80,9 @@ $repl_env->{'-'} = sub { Integer->new(${$_[0]->nth(0)} - ${$_[0]->nth(1)}) }; $repl_env->{'*'} = sub { Integer->new(${$_[0]->nth(0)} * ${$_[0]->nth(1)}) }; $repl_env->{'/'} = sub { Integer->new(${$_[0]->nth(0)} / ${$_[0]->nth(1)}) }; +if (scalar(@ARGV) > 0 && $ARGV[0] eq "--raw") { + set_rl_mode("raw"); +} while (1) { my $line = mal_readline("user> "); if (! defined $line) { last; } diff --git a/perl/step3_env.pl b/perl/step3_env.pl index f63443df..1c34ab62 100644 --- a/perl/step3_env.pl +++ b/perl/step3_env.pl @@ -3,7 +3,7 @@ use warnings FATAL => qw(all); no if $] >= 5.018, warnings => "experimental::smartmatch"; use File::Basename; use lib dirname (__FILE__); -use readline qw(mal_readline); +use readline qw(mal_readline set_rl_mode); use feature qw(switch); use Data::Dumper; @@ -23,7 +23,7 @@ sub eval_ast { my($ast, $env) = @_; given (ref $ast) { when (/^Symbol/) { - $env->get($$ast); + $env->get($ast); } when (/^List/) { my @lst = map {EVAL($_, $env)} @{$ast->{val}}; @@ -58,12 +58,12 @@ sub EVAL { given ($$a0) { when (/^def!$/) { my $res = EVAL($a2, $env); - return $env->set($$a1, $res); + return $env->set($a1, $res); } when (/^let\*$/) { my $let_env = Env->new($env); for(my $i=0; $i < scalar(@{$a1->{val}}); $i+=2) { - $let_env->set(${$a1->nth($i)}, EVAL($a1->nth($i+1), $let_env)); + $let_env->set($a1->nth($i), EVAL($a1->nth($i+1), $let_env)); } return EVAL($a2, $let_env); } @@ -88,11 +88,14 @@ sub REP { return PRINT(EVAL(READ($str), $repl_env)); } -$repl_env->set('+', sub { Integer->new(${$_[0]->nth(0)} + ${$_[0]->nth(1)}) } ); -$repl_env->set('-', sub { Integer->new(${$_[0]->nth(0)} - ${$_[0]->nth(1)}) } ); -$repl_env->set('*', sub { Integer->new(${$_[0]->nth(0)} * ${$_[0]->nth(1)}) } ); -$repl_env->set('/', sub { Integer->new(${$_[0]->nth(0)} / ${$_[0]->nth(1)}) } ); +$repl_env->set(Symbol->new('+'), sub { Integer->new(${$_[0]->nth(0)} + ${$_[0]->nth(1)}) } ); +$repl_env->set(Symbol->new('-'), sub { Integer->new(${$_[0]->nth(0)} - ${$_[0]->nth(1)}) } ); +$repl_env->set(Symbol->new('*'), sub { Integer->new(${$_[0]->nth(0)} * ${$_[0]->nth(1)}) } ); +$repl_env->set(Symbol->new('/'), sub { Integer->new(${$_[0]->nth(0)} / ${$_[0]->nth(1)}) } ); +if (scalar(@ARGV) > 0 && $ARGV[0] eq "--raw") { + set_rl_mode("raw"); +} while (1) { my $line = mal_readline("user> "); if (! defined $line) { last; } diff --git a/perl/step4_if_fn_do.pl b/perl/step4_if_fn_do.pl index abf0c67f..64ad314d 100644 --- a/perl/step4_if_fn_do.pl +++ b/perl/step4_if_fn_do.pl @@ -3,7 +3,7 @@ use warnings FATAL => qw(all); no if $] >= 5.018, warnings => "experimental::smartmatch"; use File::Basename; use lib dirname (__FILE__); -use readline qw(mal_readline); +use readline qw(mal_readline set_rl_mode); use feature qw(switch); use Data::Dumper; @@ -24,7 +24,7 @@ sub eval_ast { my($ast, $env) = @_; given (ref $ast) { when (/^Symbol/) { - $env->get($$ast); + $env->get($ast); } when (/^List/) { my @lst = map {EVAL($_, $env)} @{$ast->{val}}; @@ -59,12 +59,12 @@ sub EVAL { given ((ref $a0) =~ /^Symbol/ ? $$a0 : $a0) { when (/^def!$/) { my $res = EVAL($a2, $env); - return $env->set($$a1, $res); + return $env->set($a1, $res); } when (/^let\*$/) { my $let_env = Env->new($env); for(my $i=0; $i < scalar(@{$a1->{val}}); $i+=2) { - $let_env->set(${$a1->nth($i)}, EVAL($a1->nth($i+1), $let_env)); + $let_env->set($a1->nth($i), EVAL($a1->nth($i+1), $let_env)); } return EVAL($a2, $let_env); } @@ -109,11 +109,16 @@ sub REP { } # core.pl: defined using perl -foreach my $n (%$core_ns) { $repl_env->set($n, $core_ns->{$n}); } +foreach my $n (%$core_ns) { + $repl_env->set(Symbol->new($n), $core_ns->{$n}); +} # core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))"); +if (scalar(@ARGV) > 0 && $ARGV[0] eq "--raw") { + set_rl_mode("raw"); +} while (1) { my $line = mal_readline("user> "); if (! defined $line) { last; } diff --git a/perl/step5_tco.pl b/perl/step5_tco.pl index 60dc13ad..53255c22 100644 --- a/perl/step5_tco.pl +++ b/perl/step5_tco.pl @@ -3,7 +3,7 @@ use warnings FATAL => qw(all); no if $] >= 5.018, warnings => "experimental::smartmatch"; use File::Basename; use lib dirname (__FILE__); -use readline qw(mal_readline); +use readline qw(mal_readline set_rl_mode); use feature qw(switch); use Data::Dumper; @@ -24,7 +24,7 @@ sub eval_ast { my($ast, $env) = @_; given (ref $ast) { when (/^Symbol/) { - $env->get($$ast); + $env->get($ast); } when (/^List/) { my @lst = map {EVAL($_, $env)} @{$ast->{val}}; @@ -62,12 +62,12 @@ sub EVAL { given ((ref $a0) =~ /^Symbol/ ? $$a0 : $a0) { when (/^def!$/) { my $res = EVAL($a2, $env); - return $env->set($$a1, $res); + return $env->set($a1, $res); } when (/^let\*$/) { my $let_env = Env->new($env); for(my $i=0; $i < scalar(@{$a1->{val}}); $i+=2) { - $let_env->set(${$a1->nth($i)}, EVAL($a1->nth($i+1), $let_env)); + $let_env->set($a1->nth($i), EVAL($a1->nth($i+1), $let_env)); } $ast = $a2; $env = $let_env; @@ -120,11 +120,16 @@ sub REP { } # core.pl: defined using perl -foreach my $n (%$core_ns) { $repl_env->set($n, $core_ns->{$n}); } +foreach my $n (%$core_ns) { + $repl_env->set(Symbol->new($n), $core_ns->{$n}); +} # core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))"); +if (scalar(@ARGV) > 0 && $ARGV[0] eq "--raw") { + set_rl_mode("raw"); +} while (1) { my $line = mal_readline("user> "); if (! defined $line) { last; } diff --git a/perl/step6_file.pl b/perl/step6_file.pl index a95197ad..48b835f4 100644 --- a/perl/step6_file.pl +++ b/perl/step6_file.pl @@ -3,7 +3,7 @@ use warnings FATAL => qw(all); no if $] >= 5.018, warnings => "experimental::smartmatch"; use File::Basename; use lib dirname (__FILE__); -use readline qw(mal_readline); +use readline qw(mal_readline set_rl_mode); use feature qw(switch); use Data::Dumper; @@ -24,7 +24,7 @@ sub eval_ast { my($ast, $env) = @_; given (ref $ast) { when (/^Symbol/) { - $env->get($$ast); + $env->get($ast); } when (/^List/) { my @lst = map {EVAL($_, $env)} @{$ast->{val}}; @@ -62,12 +62,12 @@ sub EVAL { given ((ref $a0) =~ /^Symbol/ ? $$a0 : $a0) { when (/^def!$/) { my $res = EVAL($a2, $env); - return $env->set($$a1, $res); + return $env->set($a1, $res); } when (/^let\*$/) { my $let_env = Env->new($env); for(my $i=0; $i < scalar(@{$a1->{val}}); $i+=2) { - $let_env->set(${$a1->nth($i)}, EVAL($a1->nth($i+1), $let_env)); + $let_env->set($a1->nth($i), EVAL($a1->nth($i+1), $let_env)); } $ast = $a2; $env = $let_env; @@ -120,15 +120,21 @@ sub REP { } # core.pl: defined using perl -foreach my $n (%$core_ns) { $repl_env->set($n, $core_ns->{$n}); } -$repl_env->set('eval', sub { EVAL($_[0]->nth(0), $repl_env); }); +foreach my $n (%$core_ns) { + $repl_env->set(Symbol->new($n), $core_ns->{$n}); +} +$repl_env->set(Symbol->new('eval'), sub { EVAL($_[0]->nth(0), $repl_env); }); my @_argv = map {String->new($_)} @ARGV[1..$#ARGV]; -$repl_env->set('*ARGV*', List->new(\@_argv)); +$repl_env->set(Symbol->new('*ARGV*'), List->new(\@_argv)); # core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))"); REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); +if (scalar(@ARGV) > 0 && $ARGV[0] eq "--raw") { + set_rl_mode("raw"); + shift @ARGV; +} if (scalar(@ARGV) > 0) { REP("(load-file \"" . $ARGV[0] . "\")"); exit 0; diff --git a/perl/step7_quote.pl b/perl/step7_quote.pl index 5ce91996..89133a20 100644 --- a/perl/step7_quote.pl +++ b/perl/step7_quote.pl @@ -3,7 +3,7 @@ use warnings FATAL => qw(all); no if $] >= 5.018, warnings => "experimental::smartmatch"; use File::Basename; use lib dirname (__FILE__); -use readline qw(mal_readline); +use readline qw(mal_readline set_rl_mode); use feature qw(switch); use Data::Dumper; @@ -47,7 +47,7 @@ sub eval_ast { my($ast, $env) = @_; given (ref $ast) { when (/^Symbol/) { - $env->get($$ast); + $env->get($ast); } when (/^List/) { my @lst = map {EVAL($_, $env)} @{$ast->{val}}; @@ -85,12 +85,12 @@ sub EVAL { given ((ref $a0) =~ /^Symbol/ ? $$a0 : $a0) { when (/^def!$/) { my $res = EVAL($a2, $env); - return $env->set($$a1, $res); + return $env->set($a1, $res); } when (/^let\*$/) { my $let_env = Env->new($env); for(my $i=0; $i < scalar(@{$a1->{val}}); $i+=2) { - $let_env->set(${$a1->nth($i)}, EVAL($a1->nth($i+1), $let_env)); + $let_env->set($a1->nth($i), EVAL($a1->nth($i+1), $let_env)); } $ast = $a2; $env = $let_env; @@ -150,15 +150,21 @@ sub REP { } # core.pl: defined using perl -foreach my $n (%$core_ns) { $repl_env->set($n, $core_ns->{$n}); } -$repl_env->set('eval', sub { EVAL($_[0]->nth(0), $repl_env); }); +foreach my $n (%$core_ns) { + $repl_env->set(Symbol->new($n), $core_ns->{$n}); +} +$repl_env->set(Symbol->new('eval'), sub { EVAL($_[0]->nth(0), $repl_env); }); my @_argv = map {String->new($_)} @ARGV[1..$#ARGV]; -$repl_env->set('*ARGV*', List->new(\@_argv)); +$repl_env->set(Symbol->new('*ARGV*'), List->new(\@_argv)); # core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))"); REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); +if (scalar(@ARGV) > 0 && $ARGV[0] eq "--raw") { + set_rl_mode("raw"); + shift @ARGV; +} if (scalar(@ARGV) > 0) { REP("(load-file \"" . $ARGV[0] . "\")"); exit 0; diff --git a/perl/step8_macros.pl b/perl/step8_macros.pl index d95e0328..4e4a48d4 100644 --- a/perl/step8_macros.pl +++ b/perl/step8_macros.pl @@ -3,7 +3,7 @@ use warnings FATAL => qw(all); no if $] >= 5.018, warnings => "experimental::smartmatch"; use File::Basename; use lib dirname (__FILE__); -use readline qw(mal_readline); +use readline qw(mal_readline set_rl_mode); use feature qw(switch); use Data::Dumper; @@ -47,8 +47,8 @@ sub is_macro_call { my ($ast, $env) = @_; if (_list_Q($ast) && _symbol_Q($ast->nth(0)) && - $env->find(${$ast->nth(0)})) { - my ($f) = $env->get(${$ast->nth(0)}); + $env->find($ast->nth(0))) { + my ($f) = $env->get($ast->nth(0)); if ((ref $f) =~ /^Function/) { return $f->{ismacro}; } @@ -59,7 +59,7 @@ sub is_macro_call { sub macroexpand { my ($ast, $env) = @_; while (is_macro_call($ast, $env)) { - my $mac = $env->get(${$ast->nth(0)}); + my $mac = $env->get($ast->nth(0)); $ast = $mac->apply($ast->rest()); } return $ast; @@ -70,7 +70,7 @@ sub eval_ast { my($ast, $env) = @_; given (ref $ast) { when (/^Symbol/) { - $env->get($$ast); + $env->get($ast); } when (/^List/) { my @lst = map {EVAL($_, $env)} @{$ast->{val}}; @@ -111,12 +111,12 @@ sub EVAL { given ((ref $a0) =~ /^Symbol/ ? $$a0 : $a0) { when (/^def!$/) { my $res = EVAL($a2, $env); - return $env->set($$a1, $res); + return $env->set($a1, $res); } when (/^let\*$/) { my $let_env = Env->new($env); for(my $i=0; $i < scalar(@{$a1->{val}}); $i+=2) { - $let_env->set(${$a1->nth($i)}, EVAL($a1->nth($i+1), $let_env)); + $let_env->set($a1->nth($i), EVAL($a1->nth($i+1), $let_env)); } $ast = $a2; $env = $let_env; @@ -132,7 +132,7 @@ sub EVAL { when (/^defmacro!$/) { my $func = EVAL($a2, $env); $func->{ismacro} = 1; - return $env->set($$a1, $func); + return $env->set($a1, $func); } when (/^macroexpand$/) { return macroexpand($a1, $env); @@ -184,15 +184,24 @@ sub REP { } # core.pl: defined using perl -foreach my $n (%$core_ns) { $repl_env->set($n, $core_ns->{$n}); } -$repl_env->set('eval', sub { EVAL($_[0]->nth(0), $repl_env); }); +foreach my $n (%$core_ns) { + $repl_env->set(Symbol->new($n), $core_ns->{$n}); +} +$repl_env->set(Symbol->new('eval'), sub { EVAL($_[0]->nth(0), $repl_env); }); my @_argv = map {String->new($_)} @ARGV[1..$#ARGV]; -$repl_env->set('*ARGV*', List->new(\@_argv)); +$repl_env->set(Symbol->new('*ARGV*'), List->new(\@_argv)); # core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))"); REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); +REP("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); +REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); + +if (scalar(@ARGV) > 0 && $ARGV[0] eq "--raw") { + set_rl_mode("raw"); + shift @ARGV; +} if (scalar(@ARGV) > 0) { REP("(load-file \"" . $ARGV[0] . "\")"); exit 0; diff --git a/perl/step9_try.pl b/perl/step9_try.pl index 5862ef1c..ec823bcb 100644 --- a/perl/step9_try.pl +++ b/perl/step9_try.pl @@ -3,7 +3,7 @@ use warnings FATAL => qw(all); no if $] >= 5.018, warnings => "experimental::smartmatch"; use File::Basename; use lib dirname (__FILE__); -use readline qw(mal_readline); +use readline qw(mal_readline set_rl_mode); use feature qw(switch); use Data::Dumper; @@ -48,8 +48,8 @@ sub is_macro_call { my ($ast, $env) = @_; if (_list_Q($ast) && _symbol_Q($ast->nth(0)) && - $env->find(${$ast->nth(0)})) { - my ($f) = $env->get(${$ast->nth(0)}); + $env->find($ast->nth(0))) { + my ($f) = $env->get($ast->nth(0)); if ((ref $f) =~ /^Function/) { return $f->{ismacro}; } @@ -60,7 +60,7 @@ sub is_macro_call { sub macroexpand { my ($ast, $env) = @_; while (is_macro_call($ast, $env)) { - my $mac = $env->get(${$ast->nth(0)}); + my $mac = $env->get($ast->nth(0)); $ast = $mac->apply($ast->rest()); } return $ast; @@ -71,7 +71,7 @@ sub eval_ast { my($ast, $env) = @_; given (ref $ast) { when (/^Symbol/) { - $env->get($$ast); + $env->get($ast); } when (/^List/) { my @lst = map {EVAL($_, $env)} @{$ast->{val}}; @@ -112,12 +112,12 @@ sub EVAL { given ((ref $a0) =~ /^Symbol/ ? $$a0 : $a0) { when (/^def!$/) { my $res = EVAL($a2, $env); - return $env->set($$a1, $res); + return $env->set($a1, $res); } when (/^let\*$/) { my $let_env = Env->new($env); for(my $i=0; $i < scalar(@{$a1->{val}}); $i+=2) { - $let_env->set(${$a1->nth($i)}, EVAL($a1->nth($i+1), $let_env)); + $let_env->set($a1->nth($i), EVAL($a1->nth($i+1), $let_env)); } $ast = $a2; $env = $let_env; @@ -133,7 +133,7 @@ sub EVAL { when (/^defmacro!$/) { my $func = EVAL($a2, $env); $func->{ismacro} = 1; - return $env->set($$a1, $func); + return $env->set($a1, $func); } when (/^macroexpand$/) { return macroexpand($a1, $env); @@ -212,24 +212,28 @@ sub REP { } # core.pl: defined using perl -foreach my $n (%$core_ns) { $repl_env->set($n, $core_ns->{$n}); } -$repl_env->set('eval', sub { EVAL($_[0]->nth(0), $repl_env); }); +foreach my $n (%$core_ns) { + $repl_env->set(Symbol->new($n), $core_ns->{$n}); +} +$repl_env->set(Symbol->new('eval'), sub { EVAL($_[0]->nth(0), $repl_env); }); my @_argv = map {String->new($_)} @ARGV[1..$#ARGV]; -$repl_env->set('*ARGV*', List->new(\@_argv)); +$repl_env->set(Symbol->new('*ARGV*'), List->new(\@_argv)); # core.mal: defined using the language itself -REP("(def! *host-language* \"javascript\")"); REP("(def! not (fn* (a) (if a false true)))"); REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); REP("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); +if (scalar(@ARGV) > 0 && $ARGV[0] eq "--raw") { + set_rl_mode("raw"); + shift @ARGV; +} if (scalar(@ARGV) > 0) { REP("(load-file \"" . $ARGV[0] . "\")"); exit 0; } -REP("(println (str \"Mal [\" *host-language* \"]\"))"); while (1) { my $line = mal_readline("user> "); if (! defined $line) { last; } diff --git a/perl/stepA_interop.pl b/perl/stepA_interop.pl index 0605d572..79936355 100644 --- a/perl/stepA_interop.pl +++ b/perl/stepA_interop.pl @@ -3,7 +3,7 @@ use warnings FATAL => qw(all); no if $] >= 5.018, warnings => "experimental::smartmatch"; use File::Basename; use lib dirname (__FILE__); -use readline qw(mal_readline); +use readline qw(mal_readline set_rl_mode); use feature qw(switch); use Data::Dumper; @@ -48,8 +48,8 @@ sub is_macro_call { my ($ast, $env) = @_; if (_list_Q($ast) && _symbol_Q($ast->nth(0)) && - $env->find(${$ast->nth(0)})) { - my ($f) = $env->get(${$ast->nth(0)}); + $env->find($ast->nth(0))) { + my ($f) = $env->get($ast->nth(0)); if ((ref $f) =~ /^Function/) { return $f->{ismacro}; } @@ -60,7 +60,7 @@ sub is_macro_call { sub macroexpand { my ($ast, $env) = @_; while (is_macro_call($ast, $env)) { - my $mac = $env->get(${$ast->nth(0)}); + my $mac = $env->get($ast->nth(0)); $ast = $mac->apply($ast->rest()); } return $ast; @@ -71,7 +71,7 @@ sub eval_ast { my($ast, $env) = @_; given (ref $ast) { when (/^Symbol/) { - $env->get($$ast); + $env->get($ast); } when (/^List/) { my @lst = map {EVAL($_, $env)} @{$ast->{val}}; @@ -112,12 +112,12 @@ sub EVAL { given ((ref $a0) =~ /^Symbol/ ? $$a0 : $a0) { when (/^def!$/) { my $res = EVAL($a2, $env); - return $env->set($$a1, $res); + return $env->set($a1, $res); } when (/^let\*$/) { my $let_env = Env->new($env); for(my $i=0; $i < scalar(@{$a1->{val}}); $i+=2) { - $let_env->set(${$a1->nth($i)}, EVAL($a1->nth($i+1), $let_env)); + $let_env->set($a1->nth($i), EVAL($a1->nth($i+1), $let_env)); } $ast = $a2; $env = $let_env; @@ -133,7 +133,7 @@ sub EVAL { when (/^defmacro!$/) { my $func = EVAL($a2, $env); $func->{ismacro} = 1; - return $env->set($$a1, $func); + return $env->set($a1, $func); } when (/^macroexpand$/) { return macroexpand($a1, $env); @@ -215,10 +215,12 @@ sub REP { } # core.pl: defined using perl -foreach my $n (%$core_ns) { $repl_env->set($n, $core_ns->{$n}); } -$repl_env->set('eval', sub { EVAL($_[0]->nth(0), $repl_env); }); +foreach my $n (%$core_ns) { + $repl_env->set(Symbol->new($n), $core_ns->{$n}); +} +$repl_env->set(Symbol->new('eval'), sub { EVAL($_[0]->nth(0), $repl_env); }); my @_argv = map {String->new($_)} @ARGV[1..$#ARGV]; -$repl_env->set('*ARGV*', List->new(\@_argv)); +$repl_env->set(Symbol->new('*ARGV*'), List->new(\@_argv)); # core.mal: defined using the language itself REP("(def! *host-language* \"javascript\")"); @@ -228,6 +230,10 @@ REP("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if ( REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); +if (scalar(@ARGV) > 0 && $ARGV[0] eq "--raw") { + set_rl_mode("raw"); + shift @ARGV; +} if (scalar(@ARGV) > 0) { REP("(load-file \"" . $ARGV[0] . "\")"); exit 0; diff --git a/perl/types.pm b/perl/types.pm index 356f8c6e..e2d919c2 100644 --- a/perl/types.pm +++ b/perl/types.pm @@ -5,10 +5,9 @@ no if $] >= 5.018, warnings => "experimental::smartmatch"; use feature qw(switch); use Exporter 'import'; our @EXPORT_OK = qw(_sequential_Q _equal_Q _clone - $nil $true $false - _symbol_Q _nil_Q _true_Q _false_Q _list_Q _vector_Q - _hash_map _hash_map_Q _assoc_BANG _dissoc_BANG - _atom_Q); + $nil $true $false _nil_Q _true_Q _false_Q + _symbol _symbol_Q _keyword _keyword_Q _list_Q _vector_Q + _hash_map _hash_map_Q _assoc_BANG _dissoc_BANG _atom_Q); use Data::Dumper; @@ -111,10 +110,13 @@ sub _false_Q { return $_[0] eq $false } package Symbol; sub new { my $class = shift; bless \$_[0] => $class } } - sub _symbol_Q { (ref $_[0]) =~ /^Symbol/ } +sub _keyword { return String->new(("\x{029e}".$_[0])); } +sub _keyword_Q { ((ref $_[0]) =~ /^String/) && ${$_[0]} =~ /^\x{029e}/; } + + { package String; sub new { my $class = shift; bless \$_[0] => $class } diff --git a/php/core.php b/php/core.php index 4cb46673..96129b1d 100644 --- a/php/core.php +++ b/php/core.php @@ -97,7 +97,11 @@ function concat() { } function nth($seq, $idx) { - return $seq[$idx]; + if ($idx < $seq->count()) { + return $seq[$idx]; + } else { + throw new Exception("nth: index out of range"); + } } function first($seq) { @@ -177,6 +181,8 @@ $core_ns = array( '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); }, + 'keyword'=> function () { return call_user_func_array('_keyword', func_get_args()); }, + 'keyword?'=> function ($a) { return _keyword_Q($a); }, 'string?'=> function ($a) { return _string_Q($a); }, 'pr-str'=> function () { return call_user_func_array('pr_str', func_get_args()); }, diff --git a/php/env.php b/php/env.php index 61bedafb..a660d3b9 100644 --- a/php/env.php +++ b/php/env.php @@ -31,7 +31,7 @@ class Env { } } public function find($key) { - if (array_key_exists($key, $this->data)) { + if (array_key_exists($key->value, $this->data)) { return $this; } elseif ($this->outer) { return $this->outer->find($key); @@ -40,15 +40,15 @@ class Env { } } public function set($key, $value) { - $this->data[$key] = $value; + $this->data[$key->value] = $value; return $value; } public function get($key) { $env = $this->find($key); if (!$env) { - throw new Exception("'" . $key . "' not found"); + throw new Exception("'" . $key->value . "' not found"); } else { - return $env->data[$key]; + return $env->data[$key->value]; } } } diff --git a/php/printer.php b/php/printer.php index 38399318..130d31ba 100644 --- a/php/printer.php +++ b/php/printer.php @@ -23,7 +23,9 @@ function _pr_str($obj, $print_readably=True) { } return "{" . implode(" ", $ret) . "}"; } elseif (is_string($obj)) { - if ($print_readably) { + if (strpos($obj, chr(0x7f)) === 0) { + return ":".substr($obj,1); + } elseif ($print_readably) { $obj = preg_replace('/"/', '\\"', preg_replace('/\\\\/', '\\\\\\\\', $obj)); return '"' . $obj . '"'; } else { diff --git a/php/reader.php b/php/reader.php index 83e0cff0..ed9063f3 100644 --- a/php/reader.php +++ b/php/reader.php @@ -40,6 +40,8 @@ function read_atom($reader) { $str = substr($token, 1, -1); $str = preg_replace('/\\\\"/', '"', $str); return $str; + } elseif ($token[0] === ":") { + return _keyword(substr($token,1)); } elseif ($token === "nil") { return NULL; } elseif ($token === "true") { diff --git a/php/step3_env.php b/php/step3_env.php index 6585fe71..a31c298e 100644 --- a/php/step3_env.php +++ b/php/step3_env.php @@ -14,7 +14,7 @@ function READ($str) { // eval function eval_ast($ast, $env) { if (_symbol_Q($ast)) { - return $env->get($ast->value); + return $env->get($ast); } elseif (_sequential_Q($ast)) { if (_list_Q($ast)) { $el = _list(); @@ -46,12 +46,12 @@ function MAL_EVAL($ast, $env) { switch ($a0v) { case "def!": $res = MAL_EVAL($ast[2], $env); - return $env->set($ast[1]->value, $res); + return $env->set($ast[1], $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)); + $let_env->set($a1[$i], MAL_EVAL($a1[$i+1], $let_env)); } return MAL_EVAL($ast[2], $let_env); default: @@ -73,10 +73,10 @@ function rep($str) { return MAL_PRINT(MAL_EVAL(READ($str), $repl_env)); } -$repl_env->set('+', function ($a, $b) { return intval($a + $b,10); }); -$repl_env->set('-', function ($a, $b) { return intval($a - $b,10); }); -$repl_env->set('*', function ($a, $b) { return intval($a * $b,10); }); -$repl_env->set('/', function ($a, $b) { return intval($a / $b,10); }); +$repl_env->set(_symbol('+'), function ($a, $b) { return intval($a + $b,10); }); +$repl_env->set(_symbol('-'), function ($a, $b) { return intval($a - $b,10); }); +$repl_env->set(_symbol('*'), function ($a, $b) { return intval($a * $b,10); }); +$repl_env->set(_symbol('/'), function ($a, $b) { return intval($a / $b,10); }); // repl loop do { diff --git a/php/step4_if_fn_do.php b/php/step4_if_fn_do.php index 1e88c6a7..72662613 100644 --- a/php/step4_if_fn_do.php +++ b/php/step4_if_fn_do.php @@ -15,7 +15,7 @@ function READ($str) { // eval function eval_ast($ast, $env) { if (_symbol_Q($ast)) { - return $env->get($ast->value); + return $env->get($ast); } elseif (_sequential_Q($ast)) { if (_list_Q($ast)) { $el = _list(); @@ -47,12 +47,12 @@ function MAL_EVAL($ast, $env) { switch ($a0v) { case "def!": $res = MAL_EVAL($ast[2], $env); - return $env->set($ast[1]->value, $res); + return $env->set($ast[1], $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)); + $let_env->set($a1[$i], MAL_EVAL($a1[$i+1], $let_env)); } return MAL_EVAL($ast[2], $let_env); case "do": @@ -93,7 +93,7 @@ function rep($str) { // core.php: defined using PHP foreach ($core_ns as $k=>$v) { - $repl_env->set($k, _function($v)); + $repl_env->set(_symbol($k), _function($v)); } // core.mal: defined using the language itself diff --git a/php/step5_tco.php b/php/step5_tco.php index 88557b55..a3785cff 100644 --- a/php/step5_tco.php +++ b/php/step5_tco.php @@ -15,7 +15,7 @@ function READ($str) { // eval function eval_ast($ast, $env) { if (_symbol_Q($ast)) { - return $env->get($ast->value); + return $env->get($ast); } elseif (_sequential_Q($ast)) { if (_list_Q($ast)) { $el = _list(); @@ -49,12 +49,12 @@ function MAL_EVAL($ast, $env) { switch ($a0v) { case "def!": $res = MAL_EVAL($ast[2], $env); - return $env->set($ast[1]->value, $res); + return $env->set($ast[1], $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)); + $let_env->set($a1[$i], MAL_EVAL($a1[$i+1], $let_env)); } $ast = $ast[2]; $env = $let_env; @@ -105,7 +105,7 @@ function rep($str) { // core.php: defined using PHP foreach ($core_ns as $k=>$v) { - $repl_env->set($k, _function($v)); + $repl_env->set(_symbol($k), _function($v)); } // core.mal: defined using the language itself diff --git a/php/step6_file.php b/php/step6_file.php index 1e83c28c..a27acb8a 100644 --- a/php/step6_file.php +++ b/php/step6_file.php @@ -15,7 +15,7 @@ function READ($str) { // eval function eval_ast($ast, $env) { if (_symbol_Q($ast)) { - return $env->get($ast->value); + return $env->get($ast); } elseif (_sequential_Q($ast)) { if (_list_Q($ast)) { $el = _list(); @@ -49,12 +49,12 @@ function MAL_EVAL($ast, $env) { switch ($a0v) { case "def!": $res = MAL_EVAL($ast[2], $env); - return $env->set($ast[1]->value, $res); + return $env->set($ast[1], $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)); + $let_env->set($a1[$i], MAL_EVAL($a1[$i+1], $let_env)); } $ast = $ast[2]; $env = $let_env; @@ -105,16 +105,16 @@ function rep($str) { // core.php: defined using PHP foreach ($core_ns as $k=>$v) { - $repl_env->set($k, _function($v)); + $repl_env->set(_symbol($k), _function($v)); } -$repl_env->set('eval', _function(function($ast) { +$repl_env->set(_symbol('eval'), _function(function($ast) { global $repl_env; return MAL_EVAL($ast, $repl_env); })); $_argv = _list(); for ($i=2; $i < count($argv); $i++) { $_argv->append($argv[$i]); } -$repl_env->set('*ARGV*', $_argv); +$repl_env->set(_symbol('*ARGV*'), $_argv); // core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); diff --git a/php/step7_quote.php b/php/step7_quote.php index 07d3d2a9..37903d0b 100644 --- a/php/step7_quote.php +++ b/php/step7_quote.php @@ -34,7 +34,7 @@ function quasiquote($ast) { function eval_ast($ast, $env) { if (_symbol_Q($ast)) { - return $env->get($ast->value); + return $env->get($ast); } elseif (_sequential_Q($ast)) { if (_list_Q($ast)) { $el = _list(); @@ -68,12 +68,12 @@ function MAL_EVAL($ast, $env) { switch ($a0v) { case "def!": $res = MAL_EVAL($ast[2], $env); - return $env->set($ast[1]->value, $res); + return $env->set($ast[1], $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)); + $let_env->set($a1[$i], MAL_EVAL($a1[$i+1], $let_env)); } $ast = $ast[2]; $env = $let_env; @@ -129,16 +129,16 @@ function rep($str) { // core.php: defined using PHP foreach ($core_ns as $k=>$v) { - $repl_env->set($k, _function($v)); + $repl_env->set(_symbol($k), _function($v)); } -$repl_env->set('eval', _function(function($ast) { +$repl_env->set(_symbol('eval'), _function(function($ast) { global $repl_env; return MAL_EVAL($ast, $repl_env); })); $_argv = _list(); for ($i=2; $i < count($argv); $i++) { $_argv->append($argv[$i]); } -$repl_env->set('*ARGV*', $_argv); +$repl_env->set(_symbol('*ARGV*'), $_argv); // core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); diff --git a/php/step8_macros.php b/php/step8_macros.php index aacf33ef..c7e01756 100644 --- a/php/step8_macros.php +++ b/php/step8_macros.php @@ -35,13 +35,13 @@ function quasiquote($ast) { function is_macro_call($ast, $env) { return is_pair($ast) && _symbol_Q($ast[0]) && - $env->find($ast[0]->value) && - $env->get($ast[0]->value)->ismacro; + $env->find($ast[0]) && + $env->get($ast[0])->ismacro; } function macroexpand($ast, $env) { while (is_macro_call($ast, $env)) { - $mac = $env->get($ast[0]->value); + $mac = $env->get($ast[0]); $args = array_slice($ast->getArrayCopy(),1); $ast = $mac->apply($args); } @@ -50,7 +50,7 @@ function macroexpand($ast, $env) { function eval_ast($ast, $env) { if (_symbol_Q($ast)) { - return $env->get($ast->value); + return $env->get($ast); } elseif (_sequential_Q($ast)) { if (_list_Q($ast)) { $el = _list(); @@ -87,12 +87,12 @@ function MAL_EVAL($ast, $env) { switch ($a0v) { case "def!": $res = MAL_EVAL($ast[2], $env); - return $env->set($ast[1]->value, $res); + return $env->set($ast[1], $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)); + $let_env->set($a1[$i], MAL_EVAL($a1[$i+1], $let_env)); } $ast = $ast[2]; $env = $let_env; @@ -105,7 +105,7 @@ function MAL_EVAL($ast, $env) { case "defmacro!": $func = MAL_EVAL($ast[2], $env); $func->ismacro = true; - return $env->set($ast[1]->value, $func); + return $env->set($ast[1], $func); case "macroexpand": return macroexpand($ast[1], $env); case "do": @@ -154,16 +154,16 @@ function rep($str) { // core.php: defined using PHP foreach ($core_ns as $k=>$v) { - $repl_env->set($k, _function($v)); + $repl_env->set(_symbol($k), _function($v)); } -$repl_env->set('eval', _function(function($ast) { +$repl_env->set(_symbol('eval'), _function(function($ast) { global $repl_env; return MAL_EVAL($ast, $repl_env); })); $_argv = _list(); for ($i=2; $i < count($argv); $i++) { $_argv->append($argv[$i]); } -$repl_env->set('*ARGV*', $_argv); +$repl_env->set(_symbol('*ARGV*'), $_argv); // core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); diff --git a/php/step9_try.php b/php/step9_try.php index 9343deac..04707630 100644 --- a/php/step9_try.php +++ b/php/step9_try.php @@ -35,13 +35,13 @@ function quasiquote($ast) { function is_macro_call($ast, $env) { return is_pair($ast) && _symbol_Q($ast[0]) && - $env->find($ast[0]->value) && - $env->get($ast[0]->value)->ismacro; + $env->find($ast[0]) && + $env->get($ast[0])->ismacro; } function macroexpand($ast, $env) { while (is_macro_call($ast, $env)) { - $mac = $env->get($ast[0]->value); + $mac = $env->get($ast[0]); $args = array_slice($ast->getArrayCopy(),1); $ast = $mac->apply($args); } @@ -50,7 +50,7 @@ function macroexpand($ast, $env) { function eval_ast($ast, $env) { if (_symbol_Q($ast)) { - return $env->get($ast->value); + return $env->get($ast); } elseif (_sequential_Q($ast)) { if (_list_Q($ast)) { $el = _list(); @@ -87,12 +87,12 @@ function MAL_EVAL($ast, $env) { switch ($a0v) { case "def!": $res = MAL_EVAL($ast[2], $env); - return $env->set($ast[1]->value, $res); + return $env->set($ast[1], $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)); + $let_env->set($a1[$i], MAL_EVAL($a1[$i+1], $let_env)); } $ast = $ast[2]; $env = $let_env; @@ -105,7 +105,7 @@ function MAL_EVAL($ast, $env) { case "defmacro!": $func = MAL_EVAL($ast[2], $env); $func->ismacro = true; - return $env->set($ast[1]->value, $func); + return $env->set($ast[1], $func); case "macroexpand": return macroexpand($ast[1], $env); case "try*": @@ -172,19 +172,18 @@ function rep($str) { // core.php: defined using PHP foreach ($core_ns as $k=>$v) { - $repl_env->set($k, _function($v)); + $repl_env->set(_symbol($k), _function($v)); } -$repl_env->set('eval', _function(function($ast) { +$repl_env->set(_symbol('eval'), _function(function($ast) { global $repl_env; return MAL_EVAL($ast, $repl_env); })); $_argv = _list(); for ($i=2; $i < count($argv); $i++) { $_argv->append($argv[$i]); } -$repl_env->set('*ARGV*', $_argv); +$repl_env->set(_symbol('*ARGV*'), $_argv); // core.mal: defined using the language itself -rep("(def! *host-language* \"php\")"); rep("(def! not (fn* (a) (if a false true)))"); rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); @@ -196,7 +195,6 @@ if (count($argv) > 1) { } // repl loop -rep("(println (str \"Mal [\" *host-language* \"]\"))"); do { try { $line = mal_readline("user> "); diff --git a/php/stepA_interop.php b/php/stepA_interop.php index f38656f6..8c67c66b 100644 --- a/php/stepA_interop.php +++ b/php/stepA_interop.php @@ -35,13 +35,13 @@ function quasiquote($ast) { function is_macro_call($ast, $env) { return is_pair($ast) && _symbol_Q($ast[0]) && - $env->find($ast[0]->value) && - $env->get($ast[0]->value)->ismacro; + $env->find($ast[0]) && + $env->get($ast[0])->ismacro; } function macroexpand($ast, $env) { while (is_macro_call($ast, $env)) { - $mac = $env->get($ast[0]->value); + $mac = $env->get($ast[0]); $args = array_slice($ast->getArrayCopy(),1); $ast = $mac->apply($args); } @@ -50,7 +50,7 @@ function macroexpand($ast, $env) { function eval_ast($ast, $env) { if (_symbol_Q($ast)) { - return $env->get($ast->value); + return $env->get($ast); } elseif (_sequential_Q($ast)) { if (_list_Q($ast)) { $el = _list(); @@ -87,12 +87,12 @@ function MAL_EVAL($ast, $env) { switch ($a0v) { case "def!": $res = MAL_EVAL($ast[2], $env); - return $env->set($ast[1]->value, $res); + return $env->set($ast[1], $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)); + $let_env->set($a1[$i], MAL_EVAL($a1[$i+1], $let_env)); } $ast = $ast[2]; $env = $let_env; @@ -105,7 +105,7 @@ function MAL_EVAL($ast, $env) { case "defmacro!": $func = MAL_EVAL($ast[2], $env); $func->ismacro = true; - return $env->set($ast[1]->value, $func); + return $env->set($ast[1], $func); case "macroexpand": return macroexpand($ast[1], $env); case "php*": @@ -174,16 +174,16 @@ function rep($str) { // core.php: defined using PHP foreach ($core_ns as $k=>$v) { - $repl_env->set($k, _function($v)); + $repl_env->set(_symbol($k), _function($v)); } -$repl_env->set('eval', _function(function($ast) { +$repl_env->set(_symbol('eval'), _function(function($ast) { global $repl_env; return MAL_EVAL($ast, $repl_env); })); $_argv = _list(); for ($i=2; $i < count($argv); $i++) { $_argv->append($argv[$i]); } -$repl_env->set('*ARGV*', $_argv); +$repl_env->set(_symbol('*ARGV*'), $_argv); // core.mal: defined using the language itself rep("(def! *host-language* \"php\")"); diff --git a/php/types.php b/php/types.php index e3df3ac8..fa871973 100644 --- a/php/types.php +++ b/php/types.php @@ -53,6 +53,13 @@ class SymbolClass { function _symbol($name) { return new SymbolClass($name); } function _symbol_Q($obj) { return ($obj instanceof SymbolClass); } +// Keywords +function _keyword($name) { return chr(0x7f).$name; } +function _keyword_Q($obj) { + return is_string($obj) && strpos($obj, chr(0x7f)) === 0; +} + + // Functions class FunctionClass { diff --git a/ps/core.ps b/ps/core.ps index 191e5c39..52c9b055 100644 --- a/ps/core.ps +++ b/ps/core.ps @@ -87,8 +87,8 @@ end } def _list_from_array end } def -% [listA listB] -> concat -> [listA... listB...] -/concat { % replaces matric concat +% [listA listB] -> do_concat -> [listA... listB...] +/do_concat { dup _count 0 eq { %if just concat pop 0 _list }{ dup _count 1 eq { %elseif concat of single item @@ -102,6 +102,15 @@ end } def } ifelse } ifelse } def +% [obj] -> do_count -> number +/do_count { + 0 _nth dup _nil? { + pop 0 + }{ + _count + } ifelse +} def + % [obj ...] -> first -> obj /first { 0 _nth _first @@ -220,7 +229,10 @@ end } def (nil?) { 0 _nth _nil? } (true?) { 0 _nth _true? } (false?) { 0 _nth _false? } + (symbol) { 0 _nth _symbol } (symbol?) { 0 _nth _symbol? } + (keyword) { 0 _nth _keyword } + (keyword?) { 0 _nth _keyword? } (pr-str) { /data get ( ) true _pr_str_args } (str) { /data get () false _pr_str_args } @@ -254,12 +266,12 @@ end } def (sequential?) { 0 _nth _sequential? } (cons) { cons } - (concat) { concat } + (concat) { do_concat } (nth) { dup 0 _nth exch 1 _nth _nth } (first) { first } (rest) { rest } (empty?) { 0 _nth _count 0 eq } - (count) { 0 _nth _count } + (count) { do_count } (conj) { conj } (apply) { apply } (map) { map } diff --git a/ps/printer.ps b/ps/printer.ps index 3062e2d0..52d6c1ec 100644 --- a/ps/printer.ps +++ b/ps/printer.ps @@ -45,13 +45,24 @@ /slen obj 10 add log ceiling cvi def obj 10 slen string cvrs }{ /stringtype obj type eq { % if string - print_readably { - (") - obj (\\) (\\\\) replace - (") (\\") replace - (") concatenate concatenate - }{ - obj + obj length 0 gt { % if string length > 0 + obj 0 get 127 eq { %if starts with 0x7f (keyword) + obj dup length string copy + dup 0 58 put % 58 is ':' + }{ print_readably { + (") + obj (\\) (\\\\) replace + (") (\\") replace + (") concatenate concatenate + }{ + obj + } ifelse } ifelse + }{ % else empty string + print_readably { + ("") + }{ + obj + } ifelse } ifelse }{ null obj eq { % if nil (nil) diff --git a/ps/reader.ps b/ps/reader.ps index f1f63f69..4b268c04 100644 --- a/ps/reader.ps +++ b/ps/reader.ps @@ -52,6 +52,32 @@ end } def end } def +% read_keyword: read a single keyword from string/idx +% string idx -> read_keyword -> name string new_idx +/read_keyword { 5 dict begin + %(in read_keyword\n) print + /idx exch def + /str exch def + /start idx def + /cnt 0 def + { % loop + idx str length ge { exit } if % EOF, break loop + /ch str idx 1 getinterval def + token_delim ch search { % if token delimeter + pop pop pop exit + }{ % else not a delim + pop + /cnt cnt 1 add def + } ifelse + /idx idx 1 add def % increment idx + } loop + + str start cnt getinterval % the matched keyword string + dup 0 127 put % TODO: something like (\x029e) would be better + str idx % return: keyword string new_idx +end } def + + % read_string: read a single string from string/idx % string idx -> read_string -> new_string string new_idx /read_string { 5 dict begin @@ -94,8 +120,10 @@ end } def %ch 48 ge ch 57 le and 45 ch eq or { %if number ch 48 ge ch 57 le and { %if number str idx read_number - }{ ch 34 eq { %elseif double-quote + }{ ch 34 eq { %elseif double-quote (string) str idx read_string + }{ ch 58 eq { %elseif colon (keyword) + str idx read_keyword }{ str idx read_symbol /idx exch def pop @@ -108,7 +136,7 @@ end } def }{ %else str idx % return the original symbol/name } ifelse } ifelse } ifelse - } ifelse } ifelse + } ifelse } ifelse } ifelse }ifelse % return: atom string new_idx diff --git a/ps/types.ps b/ps/types.ps index 82be9c23..1f6903ee 100644 --- a/ps/types.ps +++ b/ps/types.ps @@ -173,11 +173,34 @@ end } def % Symbols +/_symbol { + dup length string copy cvn +} def + /_symbol? { type /nametype eq } def +% Keywords + +/_keyword { 1 dict begin + /str exch def + str length 1 add string % str2 + dup 1 str putinterval + dup 0 127 put % TODO: something like (\x029e) would be better +end } def + +/_keyword? { + dup type /stringtype eq { + 0 get 127 eq + }{ + false + } ifelse +} def + + + % Functions % block -> _function -> boxed_function diff --git a/python/core.py b/python/core.py index d10047db..4a645940 100644 --- a/python/core.py +++ b/python/core.py @@ -60,7 +60,9 @@ def cons(x, seq): return List([x]) + List(seq) def concat(*lsts): return List(chain(*lsts)) -def nth(lst, idx): return lst[idx] +def nth(lst, idx): + if idx < len(lst): return lst[idx] + else: throw("nth: index out of range") def first(lst): return lst[0] @@ -68,7 +70,9 @@ def rest(lst): return List(lst[1:]) def empty_Q(lst): return len(lst) == 0 -def count(lst): return len(lst) +def count(lst): + if types._nil_Q(lst): return 0 + else: return len(lst) # retains metadata def conj(lst, *args): @@ -114,6 +118,8 @@ ns = { 'false?': types._false_Q, 'symbol': types._symbol, 'symbol?': types._symbol_Q, + 'keyword': types._keyword, + 'keyword?': types._keyword_Q, 'pr-str': pr_str, 'str': do_str, diff --git a/python/mal_types.py b/python/mal_types.py index e6bd70db..5fb9bbac 100644 --- a/python/mal_types.py +++ b/python/mal_types.py @@ -58,6 +58,13 @@ class Symbol(str): pass def _symbol(str): return Symbol(str) def _symbol_Q(exp): return type(exp) == Symbol +# Keywords +# A specially prefixed string +def _keyword(str): + if str[0] == u"\u029e": return str + else: return u"\u029e" + str +def _keyword_Q(exp): return _string_Q(exp) and exp[0] == u"\u029e" + # Functions def _function(Eval, Env, ast, env, params): def fn(*args): diff --git a/python/printer.py b/python/printer.py index 65bf2566..78a2fcfe 100644 --- a/python/printer.py +++ b/python/printer.py @@ -12,7 +12,9 @@ def _pr_str(obj, print_readably=True): ret.extend((_pr_str(k), _pr_str(obj[k],_r))) return "{" + " ".join(ret) + "}" elif types._string_Q(obj): - if print_readably: + if len(obj) > 0 and obj[0] == u'\u029e': + return ':' + obj[1:] + elif print_readably: return '"' + obj.encode('unicode_escape').decode('latin1').replace('"', '\\"') + '"' else: return obj diff --git a/python/reader.py b/python/reader.py index 13b1f7bd..71ad3d64 100644 --- a/python/reader.py +++ b/python/reader.py @@ -1,5 +1,5 @@ import re -from mal_types import (_symbol, _list, _vector, _hash_map) +from mal_types import (_symbol, _keyword, _list, _vector, _hash_map) class Blank(Exception): pass @@ -29,6 +29,7 @@ def read_atom(reader): if re.match(int_re, token): return int(token) elif re.match(float_re, token): return int(token) elif token[0] == '"': return token[1:-1].replace('\\"', '"') + elif token[0] == ':': return _keyword(token[1:]) elif token == "nil": return None elif token == "true": return True elif token == "false": return False diff --git a/python/step3_env.py b/python/step3_env.py index 4565011f..86dc176d 100644 --- a/python/step3_env.py +++ b/python/step3_env.py @@ -58,10 +58,10 @@ repl_env = Env() def REP(str): return PRINT(EVAL(READ(str), repl_env)) -repl_env.set('+', lambda a,b: a+b) -repl_env.set('-', lambda a,b: a-b) -repl_env.set('*', lambda a,b: a*b) -repl_env.set('/', lambda a,b: int(a/b)) +repl_env.set(types._symbol('+'), lambda a,b: a+b) +repl_env.set(types._symbol('-'), lambda a,b: a-b) +repl_env.set(types._symbol('*'), lambda a,b: a*b) +repl_env.set(types._symbol('/'), lambda a,b: int(a/b)) # repl loop while True: diff --git a/python/step4_if_fn_do.py b/python/step4_if_fn_do.py index e99cfcfa..39b9dd66 100644 --- a/python/step4_if_fn_do.py +++ b/python/step4_if_fn_do.py @@ -74,7 +74,7 @@ def REP(str): return PRINT(EVAL(READ(str), repl_env)) # core.py: defined using python -for k, v in core.ns.items(): repl_env.set(k, v) +for k, v in core.ns.items(): repl_env.set(types._symbol(k), v) # core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") diff --git a/python/step5_tco.py b/python/step5_tco.py index cbb92c97..da338d41 100644 --- a/python/step5_tco.py +++ b/python/step5_tco.py @@ -83,7 +83,7 @@ def REP(str): return PRINT(EVAL(READ(str), repl_env)) # core.py: defined using python -for k, v in core.ns.items(): repl_env.set(k, v) +for k, v in core.ns.items(): repl_env.set(types._symbol(k), v) # core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") diff --git a/python/step6_file.py b/python/step6_file.py index 9d84d1fb..5d10b469 100644 --- a/python/step6_file.py +++ b/python/step6_file.py @@ -83,9 +83,9 @@ def REP(str): return PRINT(EVAL(READ(str), repl_env)) # core.py: defined using python -for k, v in core.ns.items(): repl_env.set(k, v) -repl_env.set('eval', lambda ast: EVAL(ast, repl_env)) -repl_env.set('*ARGV*', types._list(*sys.argv[2:])) +for k, v in core.ns.items(): repl_env.set(types._symbol(k), v) +repl_env.set(types._symbol('eval'), lambda ast: EVAL(ast, repl_env)) +repl_env.set(types._symbol('*ARGV*'), types._list(*sys.argv[2:])) # core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") diff --git a/python/step7_quote.py b/python/step7_quote.py index de19c6dc..7c97c237 100644 --- a/python/step7_quote.py +++ b/python/step7_quote.py @@ -106,9 +106,9 @@ def REP(str): return PRINT(EVAL(READ(str), repl_env)) # core.py: defined using python -for k, v in core.ns.items(): repl_env.set(k, v) -repl_env.set('eval', lambda ast: EVAL(ast, repl_env)) -repl_env.set('*ARGV*', types._list(*sys.argv[2:])) +for k, v in core.ns.items(): repl_env.set(types._symbol(k), v) +repl_env.set(types._symbol('eval'), lambda ast: EVAL(ast, repl_env)) +repl_env.set(types._symbol('*ARGV*'), types._list(*sys.argv[2:])) # core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") diff --git a/python/step8_macros.py b/python/step8_macros.py index 80ca2394..da863c1e 100644 --- a/python/step8_macros.py +++ b/python/step8_macros.py @@ -126,9 +126,9 @@ def REP(str): return PRINT(EVAL(READ(str), repl_env)) # core.py: defined using python -for k, v in core.ns.items(): repl_env.set(k, v) -repl_env.set('eval', lambda ast: EVAL(ast, repl_env)) -repl_env.set('*ARGV*', types._list(*sys.argv[2:])) +for k, v in core.ns.items(): repl_env.set(types._symbol(k), v) +repl_env.set(types._symbol('eval'), lambda ast: EVAL(ast, repl_env)) +repl_env.set(types._symbol('*ARGV*'), types._list(*sys.argv[2:])) # core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") diff --git a/python/step9_try.py b/python/step9_try.py index 65da08cf..e01f42c9 100644 --- a/python/step9_try.py +++ b/python/step9_try.py @@ -143,12 +143,11 @@ def REP(str): return PRINT(EVAL(READ(str), repl_env)) # core.py: defined using python -for k, v in core.ns.items(): repl_env.set(k, v) -repl_env.set('eval', lambda ast: EVAL(ast, repl_env)) -repl_env.set('*ARGV*', types._list(*sys.argv[2:])) +for k, v in core.ns.items(): repl_env.set(types._symbol(k), v) +repl_env.set(types._symbol('eval'), lambda ast: EVAL(ast, repl_env)) +repl_env.set(types._symbol('*ARGV*'), types._list(*sys.argv[2:])) # core.mal: defined using the language itself -REP("(def! *host-language* \"python\")") REP("(def! not (fn* (a) (if a false true)))") REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") REP("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))") @@ -159,7 +158,6 @@ if len(sys.argv) >= 2: sys.exit(0) # repl loop -REP("(println (str \"Mal [\" *host-language* \"]\"))") while True: try: line = mal_readline.readline("user> ") diff --git a/python/stepA_interop.py b/python/stepA_interop.py index 723f0ed4..93cdb2e3 100644 --- a/python/stepA_interop.py +++ b/python/stepA_interop.py @@ -149,9 +149,9 @@ def REP(str): return PRINT(EVAL(READ(str), repl_env)) # core.py: defined using python -for k, v in core.ns.items(): repl_env.set(k, v) -repl_env.set('eval', lambda ast: EVAL(ast, repl_env)) -repl_env.set('*ARGV*', types._list(*sys.argv[2:])) +for k, v in core.ns.items(): repl_env.set(types._symbol(k), v) +repl_env.set(types._symbol('eval'), lambda ast: EVAL(ast, repl_env)) +repl_env.set(types._symbol('*ARGV*'), types._list(*sys.argv[2:])) # core.mal: defined using the language itself REP("(def! *host-language* \"python\")") diff --git a/r/core.r b/r/core.r index 74c8f6db..dab36e36 100644 --- a/r/core.r +++ b/r/core.r @@ -46,6 +46,13 @@ cons <- function(a,b) { new.listl(new_lst) } +nth <- function(a,b) { + if (b < length(a)) + a[[b+1]] + else + throw("nth: index out of range") +} + do_concat <- function(...) { new_lst <- list() for(l in list(...)) { @@ -118,9 +125,10 @@ core_ns <- list( "nil?"=.nil_q, "true?"=.true_q, "false?"=.false_q, - "symbol?"=.symbol_q, "symbol"=new.symbol, "symbol?"=.symbol_q, + "keyword"=new.keyword, + "keyword?"=.keyword_q, "pr-str"=pr_str, "str"=str, @@ -155,11 +163,11 @@ core_ns <- list( "sequential?"=.sequential_q, "cons"=cons, "concat"=do_concat, - "nth"=function(a,b) if (length(a) < b+1) nil else a[[b+1]], + "nth"=nth, "first"=function(a) if (length(a) < 1) nil else a[[1]], "rest"=function(a) new.listl(slice(a,2)), "empty?"=function(a) .sequential_q(a) && length(a) == 0, - "count"=function(a) length(a), + "count"=function(a) if (.nil_q(a)) 0 else length(a), "apply"=do_apply, "map"=map, "conj"=conj, diff --git a/r/printer.r b/r/printer.r index e684b034..71deef7d 100644 --- a/r/printer.r +++ b/r/printer.r @@ -27,7 +27,9 @@ if(!exists("..types..")) source("types.r") paste("{", .pr_list(hlst, pr, " "), "}", sep="", collapse="") }, "character"={ - if (print_readably) { + if (substring(exp,1,1) == "\u029e") { + concat(":", substring(exp,2)) + } else if (print_readably) { paste("\"", gsub("\\n", "\\\\n", gsub("\\\"", "\\\\\"", diff --git a/r/reader.r b/r/reader.r index 780dd4d0..7f20288d 100644 --- a/r/reader.r +++ b/r/reader.r @@ -46,6 +46,8 @@ read_atom <- function(rdr) { gsub("\\\\n", "\\n", gsub("\\\\\"", "\"", substr(token, 2, nchar(token)-1))) + } else if (substr(token,1,1) == ":") { + new.keyword(substring(token,2)) } else if (token == "nil") { nil } else if (token == "true") { diff --git a/r/types.r b/r/types.r index 1f749a5a..92d13579 100644 --- a/r/types.r +++ b/r/types.r @@ -80,7 +80,12 @@ nil <- structure("malnil", class="nil") .true_q <- function(obj) "logical" == class(obj) && obj == TRUE .false_q <- function(obj) "logical" == class(obj) && obj == FALSE new.symbol <- function(name) structure(name, class="Symbol") + .symbol_q <- function(obj) "Symbol" == class(obj) +new.keyword <- function(name) concat("\u029e", name) +.keyword_q <- function(obj) { + "character" == class(obj) && "\u029e" == substr(obj,1,1) +} # Functions diff --git a/ruby/core.rb b/ruby/core.rb index 6b127bac..d55100cf 100644 --- a/ruby/core.rb +++ b/ruby/core.rb @@ -8,8 +8,10 @@ $core_ns = { :nil? => lambda {|a| a == nil}, :true? => lambda {|a| a == true}, :false? => lambda {|a| a == false}, + :symbol => lambda {|a| a.to_sym}, :symbol? => lambda {|a| a.is_a? Symbol}, - :symbol? => lambda {|a| a.is_a? Symbol}, + :keyword => lambda {|a| "\u029e"+a}, + :keyword? => lambda {|a| (a.is_a? String) && "\u029e" == a[0]}, :"pr-str" => lambda {|*a| a.map {|e| _pr_str(e, true)}.join(" ")}, :str => lambda {|*a| a.map {|e| _pr_str(e, false)}.join("")}, @@ -44,11 +46,11 @@ $core_ns = { :sequential? => lambda {|a| sequential?(a)}, :cons => lambda {|a,b| List.new(b.clone.insert(0,a))}, :concat => lambda {|*a| List.new(a && a.reduce(:concat) || [])}, - :nth => lambda {|a,b| a[b]}, + :nth => lambda {|a,b| raise "nth: index out of range" if b >= a.size; a[b]}, :first => lambda {|a| a[0]}, :rest => lambda {|a| List.new(a.size > 0 && a.drop(1) || [])}, :empty? => lambda {|a| a.size == 0}, - :count => lambda {|a| a.size}, + :count => lambda {|a| return 0 if a == nil; a.size}, :conj => lambda {|*a| a[0].clone.conj(a.drop(1))}, :apply => lambda {|*a| a[0][*a[1..-2].concat(a[-1])]}, :map => lambda {|a,b| List.new(b.map {|e| a[e]})}, diff --git a/ruby/printer.rb b/ruby/printer.rb index cfcd064b..37d338af 100644 --- a/ruby/printer.rb +++ b/ruby/printer.rb @@ -12,7 +12,9 @@ def _pr_str(obj, print_readably=true) obj.each{|k,v| ret.push(_pr_str(k,_r), _pr_str(v,_r))} "{" + ret.join(" ") + "}" when String - if _r + if obj[0] == "\u029e" + ":" + obj[1..-1] + elsif _r obj.inspect # escape special characters else obj diff --git a/ruby/reader.rb b/ruby/reader.rb index 1039105b..eb9ae7b7 100644 --- a/ruby/reader.rb +++ b/ruby/reader.rb @@ -32,6 +32,7 @@ def read_atom(rdr) when /^-?[0-9]+$/ then token.to_i # integer when /^-?[0-9][0-9.]*$/ then token.to_f # float when /^"/ then parse_str(token) # string + when /^:/ then "\u029e" + token[1..-1] # keyword when "nil" then nil when "true" then true when "false" then false diff --git a/rust/src/core.rs b/rust/src/core.rs index e751f881..2bc3c39d 100644 --- a/rust/src/core.rs +++ b/rust/src/core.rs @@ -281,7 +281,7 @@ pub fn nth(a:Vec) -> MalRet { _ => return err_str("nth called with non-integer index"), }; if idx >= seq.len() { - Ok(_nil()) + return err_str("nth: index out of range") } else { Ok(seq[idx].clone()) } @@ -342,6 +342,7 @@ pub fn count(a:Vec) -> MalRet { List(ref v,_) | Vector(ref v,_) => { Ok(_int(v.len().to_int().unwrap())) }, + Nil => Ok(_int(0)), _ => err_str("count called on non-sequence"), } } @@ -500,7 +501,10 @@ pub fn ns() -> HashMap { ns.insert("nil?".to_string(), func(types::nil_q)); ns.insert("true?".to_string(), func(types::true_q)); ns.insert("false?".to_string(), func(types::false_q)); + ns.insert("symbol".to_string(), func(types::_symbol)); ns.insert("symbol?".to_string(), func(types::symbol_q)); + ns.insert("keyword".to_string(), func(types::_keyword)); + ns.insert("keyword?".to_string(), func(types::keyword_q)); ns.insert("pr-str".to_string(), func(pr_str)); ns.insert("str".to_string(), func(str)); diff --git a/rust/src/env.rs b/rust/src/env.rs index cf8aa0f7..e9af154e 100644 --- a/rust/src/env.rs +++ b/rust/src/env.rs @@ -34,7 +34,7 @@ pub fn env_bind(env: &Env, variadic = true; break; } else { - env_set(env, strn.clone(), exprs[i].clone()); + env_set(env, b.clone(), exprs[i].clone()); } } _ => return Err("non-symbol bind".to_string()), @@ -43,9 +43,9 @@ pub fn env_bind(env: &Env, if variadic { let (i, sym) = it.next().unwrap(); match **sym { - Sym(ref s) => { + Sym(_) => { let rest = exprs.slice(i-1,exprs.len()).to_vec(); - env_set(env, s.clone(), list(rest)); + env_set(env, sym.clone(), list(rest)); } _ => return Err("& bind to non-symbol".to_string()), } @@ -59,14 +59,19 @@ pub fn env_bind(env: &Env, } } -pub fn env_find(env: Env, key: String) -> Option { - if env.borrow().data.contains_key(&key) { - Some(env) - } else { - match env.borrow().outer { - Some(ref e) => env_find(e.clone(), key), - None => None, - } +pub fn env_find(env: Env, key: MalVal) -> Option { + match *key { + Sym(ref k) => { + if env.borrow().data.contains_key(k) { + Some(env) + } else { + match env.borrow().outer { + Some(ref e) => env_find(e.clone(), key.clone()), + None => None, + } + } + }, + _ => None } } @@ -77,19 +82,29 @@ pub fn env_root(env: &Env) -> Env { } } -pub fn env_set(env: &Env, key: String, val: MalVal) { - env.borrow_mut().data.insert(key, val.clone()); +pub fn env_set(env: &Env, key: MalVal, val: MalVal) { + match *key { + Sym(ref k) => { + env.borrow_mut().data.insert(k.to_string(), val.clone()); + }, + _ => {}, + } } -pub fn env_get(env: Env, key: String) -> MalRet { - match env_find(env, key.clone()) { - Some(e) => { - match e.borrow().data.find_copy(&key) { - Some(v) => Ok(v), - None => Ok(_nil()), +pub fn env_get(env: Env, key: MalVal) -> MalRet { + match *key { + Sym(ref k) => { + match env_find(env, key.clone()) { + Some(e) => { + match e.borrow().data.find_copy(k) { + Some(v) => Ok(v), + None => Ok(_nil()), + } + }, + None => err_string("'".to_string() + k.to_string() + "' not found".to_string()), } - }, - None => err_string("'".to_string() + key + "' not found".to_string()), + } + _ => err_string("env_get called with non-symbol key".to_string()), } } diff --git a/rust/src/reader.rs b/rust/src/reader.rs index af496fff..d7b2b4c7 100644 --- a/rust/src/reader.rs +++ b/rust/src/reader.rs @@ -68,6 +68,8 @@ fn read_atom(rdr : &mut Reader) -> MalRet { } else if regex!(r#"^".*"$"#).is_match(token) { let new_str = token.slice(1,token.len()-1); Ok(string(unescape_str(new_str))) + } else if regex!(r#"^:"#).is_match(token) { + Ok(string("\u029e".to_string() + token.slice(1,token.len()))) } else if token == "nil" { Ok(_nil()) } else if token == "true" { diff --git a/rust/src/step3_env.rs b/rust/src/step3_env.rs index cb809478..b2d49cd1 100644 --- a/rust/src/step3_env.rs +++ b/rust/src/step3_env.rs @@ -8,7 +8,7 @@ use std::collections::HashMap; use types::{MalVal,MalRet,MalError,ErrString,ErrMalVal,err_str, Int,Sym,List,Vector,Hash_Map, - _int,list,vector,hash_map,func}; + symbol,_int,list,vector,hash_map,func}; use env::{Env,env_new,env_set,env_get}; mod readline; mod types; @@ -26,8 +26,8 @@ fn eval_ast(ast: MalVal, env: Env) -> MalRet { let ast2 = ast.clone(); match *ast2 { //match *ast { - Sym(ref sym) => { - env_get(env.clone(), sym.clone()) + Sym(_) => { + env_get(env.clone(), ast) }, List(ref a,_) | Vector(ref a,_) => { let mut ast_vec : Vec = vec![]; @@ -94,8 +94,8 @@ fn eval(ast: MalVal, env: Env) -> MalRet { match res { Ok(r) => { match *a1 { - Sym(ref s) => { - env_set(&env.clone(), s.clone(), r.clone()); + Sym(_) => { + env_set(&env.clone(), a1.clone(), r.clone()); return Ok(r); }, _ => { @@ -117,10 +117,10 @@ fn eval(ast: MalVal, env: Env) -> MalRet { let b = it.next().unwrap(); let exp = it.next().unwrap(); match **b { - Sym(ref bstr) => { + Sym(_) => { match eval(exp.clone(), let_env.clone()) { Ok(r) => { - env_set(&let_env, bstr.clone(), r); + env_set(&let_env, b.clone(), r); }, Err(e) => { return Err(e); @@ -187,10 +187,10 @@ fn div(a:Vec) -> MalRet { int_op(|i,j| { i/j }, a) } fn main() { let repl_env = env_new(None); - env_set(&repl_env, "+".to_string(), func(add)); - env_set(&repl_env, "-".to_string(), func(sub)); - env_set(&repl_env, "*".to_string(), func(mul)); - env_set(&repl_env, "/".to_string(), func(div)); + env_set(&repl_env, symbol("+"), func(add)); + env_set(&repl_env, symbol("-"), func(sub)); + env_set(&repl_env, symbol("*"), func(mul)); + env_set(&repl_env, symbol("/"), func(div)); loop { let line = readline::mal_readline("user> "); diff --git a/rust/src/step4_if_fn_do.rs b/rust/src/step4_if_fn_do.rs index 1a8d271b..92abf927 100644 --- a/rust/src/step4_if_fn_do.rs +++ b/rust/src/step4_if_fn_do.rs @@ -8,7 +8,7 @@ use std::collections::HashMap; use types::{MalVal,MalRet,MalError,ErrString,ErrMalVal,err_str, Nil,False,Sym,List,Vector,Hash_Map, - _nil,list,vector,hash_map,malfunc}; + symbol,_nil,list,vector,hash_map,malfunc}; use env::{Env,env_new,env_set,env_get}; mod readline; mod types; @@ -27,8 +27,8 @@ fn eval_ast(ast: MalVal, env: Env) -> MalRet { let ast2 = ast.clone(); match *ast2 { //match *ast { - Sym(ref sym) => { - env_get(env.clone(), sym.clone()) + Sym(_) => { + env_get(env.clone(), ast) }, List(ref a,_) | Vector(ref a,_) => { let mut ast_vec : Vec = vec![]; @@ -95,8 +95,8 @@ fn eval(ast: MalVal, env: Env) -> MalRet { match res { Ok(r) => { match *a1 { - Sym(ref s) => { - env_set(&env.clone(), s.clone(), r.clone()); + Sym(_) => { + env_set(&env.clone(), a1.clone(), r.clone()); return Ok(r); }, _ => { @@ -118,10 +118,10 @@ fn eval(ast: MalVal, env: Env) -> MalRet { let b = it.next().unwrap(); let exp = it.next().unwrap(); match **b { - Sym(ref bstr) => { + Sym(_) => { match eval(exp.clone(), let_env.clone()) { Ok(r) => { - env_set(&let_env, bstr.clone(), r); + env_set(&let_env, b.clone(), r); }, Err(e) => { return Err(e); @@ -216,7 +216,9 @@ fn rep(str: &str, env: Env) -> Result { fn main() { // core.rs: defined using rust let repl_env = env_new(None); - for (k, v) in core::ns().into_iter() { env_set(&repl_env, k, v); } + for (k, v) in core::ns().into_iter() { + env_set(&repl_env, symbol(k.as_slice()), v); + } // core.mal: defined using the language itself let _ = rep("(def! not (fn* (a) (if a false true)))", repl_env.clone()); diff --git a/rust/src/step5_tco.rs b/rust/src/step5_tco.rs index 0fdc5978..9223cbf7 100644 --- a/rust/src/step5_tco.rs +++ b/rust/src/step5_tco.rs @@ -8,7 +8,7 @@ use std::collections::HashMap; use types::{MalVal,MalRet,MalError,ErrString,ErrMalVal,err_str, Nil,False,Sym,List,Vector,Hash_Map,Func,MalFunc, - _nil,list,vector,hash_map,malfunc}; + symbol,_nil,list,vector,hash_map,malfunc}; use env::{Env,env_new,env_bind,env_set,env_get}; mod readline; mod types; @@ -27,8 +27,8 @@ fn eval_ast(ast: MalVal, env: Env) -> MalRet { let ast2 = ast.clone(); match *ast2 { //match *ast { - Sym(ref sym) => { - env_get(env.clone(), sym.clone()) + Sym(_) => { + env_get(env.clone(), ast) }, List(ref a,_) | Vector(ref a,_) => { let mut ast_vec : Vec = vec![]; @@ -98,8 +98,8 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { match res { Ok(r) => { match *a1 { - Sym(ref s) => { - env_set(&env.clone(), s.clone(), r.clone()); + Sym(_) => { + env_set(&env.clone(), a1.clone(), r.clone()); return Ok(r); }, _ => { @@ -121,10 +121,10 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { let b = it.next().unwrap(); let exp = it.next().unwrap(); match **b { - Sym(ref bstr) => { + Sym(_) => { match eval(exp.clone(), let_env.clone()) { Ok(r) => { - env_set(&let_env, bstr.clone(), r); + env_set(&let_env, b.clone(), r); }, Err(e) => { return Err(e); @@ -238,7 +238,9 @@ fn rep(str: &str, env: Env) -> Result { fn main() { // core.rs: defined using rust let repl_env = env_new(None); - for (k, v) in core::ns().into_iter() { env_set(&repl_env, k, v); } + for (k, v) in core::ns().into_iter() { + env_set(&repl_env, symbol(k.as_slice()), v); + } // core.mal: defined using the language itself let _ = rep("(def! not (fn* (a) (if a false true)))", repl_env.clone()); diff --git a/rust/src/step6_file.rs b/rust/src/step6_file.rs index e1198bb2..1e871166 100644 --- a/rust/src/step6_file.rs +++ b/rust/src/step6_file.rs @@ -9,7 +9,7 @@ use std::os; use types::{MalVal,MalRet,MalError,ErrString,ErrMalVal,err_str, Nil,False,Sym,List,Vector,Hash_Map,Func,MalFunc, - _nil,string,list,vector,hash_map,malfunc}; + symbol,_nil,string,list,vector,hash_map,malfunc}; use env::{Env,env_new,env_bind,env_root,env_set,env_get}; mod readline; mod types; @@ -28,8 +28,8 @@ fn eval_ast(ast: MalVal, env: Env) -> MalRet { let ast2 = ast.clone(); match *ast2 { //match *ast { - Sym(ref sym) => { - env_get(env.clone(), sym.clone()) + Sym(_) => { + env_get(env.clone(), ast) }, List(ref a,_) | Vector(ref a,_) => { let mut ast_vec : Vec = vec![]; @@ -99,8 +99,8 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { match res { Ok(r) => { match *a1 { - Sym(ref s) => { - env_set(&env.clone(), s.clone(), r.clone()); + Sym(_) => { + env_set(&env.clone(), a1.clone(), r.clone()); return Ok(r); }, _ => { @@ -122,10 +122,10 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { let b = it.next().unwrap(); let exp = it.next().unwrap(); match **b { - Sym(ref bstr) => { + Sym(_) => { match eval(exp.clone(), let_env.clone()) { Ok(r) => { - env_set(&let_env, bstr.clone(), r); + env_set(&let_env, b.clone(), r); }, Err(e) => { return Err(e); @@ -250,9 +250,11 @@ fn rep(str: &str, env: Env) -> Result { fn main() { // core.rs: defined using rust let repl_env = env_new(None); - for (k, v) in core::ns().into_iter() { env_set(&repl_env, k, v); } + for (k, v) in core::ns().into_iter() { + env_set(&repl_env, symbol(k.as_slice()), v); + } // see eval() for definition of "eval" - env_set(&repl_env, "*ARGV*".to_string(), list(vec![])); + env_set(&repl_env, symbol("*ARGV*".as_slice()), list(vec![])); // core.mal: defined using the language itself let _ = rep("(def! not (fn* (a) (if a false true)))", repl_env.clone()); @@ -264,7 +266,7 @@ fn main() { let mv_args = args.slice(2,args.len()).iter() .map(|a| string(a.to_string())) .collect::>(); - env_set(&repl_env, "*ARGV*".to_string(), list(mv_args)); + env_set(&repl_env, symbol("*ARGV*".as_slice()), list(mv_args)); let lf = "(load-file \"".to_string() + args[1] + "\")".to_string(); match rep(lf.as_slice(), repl_env.clone()) { Ok(_) => { diff --git a/rust/src/step7_quote.rs b/rust/src/step7_quote.rs index a46f5489..b113920e 100644 --- a/rust/src/step7_quote.rs +++ b/rust/src/step7_quote.rs @@ -9,7 +9,7 @@ use std::os; use types::{MalVal,MalRet,MalError,ErrString,ErrMalVal,err_str, Nil,False,Sym,List,Vector,Hash_Map,Func,MalFunc, - _nil,symbol,string,list,vector,hash_map,malfunc}; + symbol,_nil,string,list,vector,hash_map,malfunc}; use env::{Env,env_new,env_bind,env_root,env_set,env_get}; mod readline; mod types; @@ -79,8 +79,8 @@ fn eval_ast(ast: MalVal, env: Env) -> MalRet { let ast2 = ast.clone(); match *ast2 { //match *ast { - Sym(ref sym) => { - env_get(env.clone(), sym.clone()) + Sym(_) => { + env_get(env.clone(), ast) }, List(ref a,_) | Vector(ref a,_) => { let mut ast_vec : Vec = vec![]; @@ -150,8 +150,8 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { match res { Ok(r) => { match *a1 { - Sym(ref s) => { - env_set(&env.clone(), s.clone(), r.clone()); + Sym(_) => { + env_set(&env.clone(), a1.clone(), r.clone()); return Ok(r); }, _ => { @@ -173,10 +173,10 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { let b = it.next().unwrap(); let exp = it.next().unwrap(); match **b { - Sym(ref bstr) => { + Sym(_) => { match eval(exp.clone(), let_env.clone()) { Ok(r) => { - env_set(&let_env, bstr.clone(), r); + env_set(&let_env, b.clone(), r); }, Err(e) => { return Err(e); @@ -309,9 +309,11 @@ fn rep(str: &str, env: Env) -> Result { fn main() { // core.rs: defined using rust let repl_env = env_new(None); - for (k, v) in core::ns().into_iter() { env_set(&repl_env, k, v); } + for (k, v) in core::ns().into_iter() { + env_set(&repl_env, symbol(k.as_slice()), v); + } // see eval() for definition of "eval" - env_set(&repl_env, "*ARGV*".to_string(), list(vec![])); + env_set(&repl_env, symbol("*ARGV*".as_slice()), list(vec![])); // core.mal: defined using the language itself let _ = rep("(def! not (fn* (a) (if a false true)))", repl_env.clone()); @@ -323,7 +325,7 @@ fn main() { let mv_args = args.slice(2,args.len()).iter() .map(|a| string(a.to_string())) .collect::>(); - env_set(&repl_env, "*ARGV*".to_string(), list(mv_args)); + env_set(&repl_env, symbol("*ARGV*".as_slice()), list(mv_args)); let lf = "(load-file \"".to_string() + args[1] + "\")".to_string(); match rep(lf.as_slice(), repl_env.clone()) { Ok(_) => { diff --git a/rust/src/step8_macros.rs b/rust/src/step8_macros.rs index fbbaf358..7450de8b 100644 --- a/rust/src/step8_macros.rs +++ b/rust/src/step8_macros.rs @@ -9,7 +9,7 @@ use std::os; use types::{MalVal,MalRet,MalError,ErrString,ErrMalVal,err_str, Nil,False,Sym,List,Vector,Hash_Map,Func,MalFunc, - _nil,symbol,string,list,vector,hash_map,malfunc,malfuncd}; + symbol,_nil,string,list,vector,hash_map,malfunc,malfuncd}; use env::{Env,env_new,env_bind,env_root,env_find,env_set,env_get}; mod readline; mod types; @@ -78,11 +78,10 @@ fn quasiquote(ast: MalVal) -> MalVal { fn is_macro_call(ast: MalVal, env: Env) -> bool { match *ast { List(ref lst,_) => { - let ref a0 = *lst[0]; - match *a0 { - Sym(ref a0sym) => { - if env_find(env.clone(), a0sym.to_string()).is_some() { - match env_get(env, a0sym.to_string()) { + match *lst[0] { + Sym(_) => { + if env_find(env.clone(), lst[0].clone()).is_some() { + match env_get(env, lst[0].clone()) { Ok(f) => { match *f { MalFunc(ref mfd,_) => { @@ -113,8 +112,8 @@ fn macroexpand(mut ast: MalVal, env: Env) -> MalRet { }; let ref a0 = args[0]; let mf = match **a0 { - Sym(ref s) => { - match env_get(env.clone(), s.to_string()) { + Sym(_) => { + match env_get(env.clone(), a0.clone()) { Ok(mf) => mf, Err(e) => return Err(e), } @@ -138,8 +137,8 @@ fn eval_ast(ast: MalVal, env: Env) -> MalRet { let ast2 = ast.clone(); match *ast2 { //match *ast { - Sym(ref sym) => { - env_get(env.clone(), sym.clone()) + Sym(_) => { + env_get(env.clone(), ast) }, List(ref a,_) | Vector(ref a,_) => { let mut ast_vec : Vec = vec![]; @@ -215,8 +214,8 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { match res { Ok(r) => { match *a1 { - Sym(ref s) => { - env_set(&env.clone(), s.clone(), r.clone()); + Sym(_) => { + env_set(&env.clone(), a1.clone(), r.clone()); return Ok(r); }, _ => { @@ -238,10 +237,10 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { let b = it.next().unwrap(); let exp = it.next().unwrap(); match **b { - Sym(ref bstr) => { + Sym(_) => { match eval(exp.clone(), let_env.clone()) { Ok(r) => { - env_set(&let_env, bstr.clone(), r); + env_set(&let_env, b.clone(), r); }, Err(e) => { return Err(e); @@ -276,11 +275,11 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { match *r { MalFunc(ref mfd,_) => { match *a1 { - Sym(ref s) => { + Sym(_) => { let mut new_mfd = mfd.clone(); new_mfd.is_macro = true; let mf = malfuncd(new_mfd,_nil()); - env_set(&env.clone(), s.clone(), mf.clone()); + env_set(&env.clone(), a1.clone(), mf.clone()); return Ok(mf); }, _ => return err_str("def! of non-symbol"), @@ -402,9 +401,11 @@ fn rep(str: &str, env: Env) -> Result { fn main() { // core.rs: defined using rust let repl_env = env_new(None); - for (k, v) in core::ns().into_iter() { env_set(&repl_env, k, v); } + for (k, v) in core::ns().into_iter() { + env_set(&repl_env, symbol(k.as_slice()), v); + } // see eval() for definition of "eval" - env_set(&repl_env, "*ARGV*".to_string(), list(vec![])); + env_set(&repl_env, symbol("*ARGV*".as_slice()), list(vec![])); // core.mal: defined using the language itself let _ = rep("(def! not (fn* (a) (if a false true)))", repl_env.clone()); @@ -418,7 +419,7 @@ fn main() { let mv_args = args.slice(2,args.len()).iter() .map(|a| string(a.to_string())) .collect::>(); - env_set(&repl_env, "*ARGV*".to_string(), list(mv_args)); + env_set(&repl_env, symbol("*ARGV*".as_slice()), list(mv_args)); let lf = "(load-file \"".to_string() + args[1] + "\")".to_string(); match rep(lf.as_slice(), repl_env.clone()) { Ok(_) => { @@ -433,6 +434,7 @@ fn main() { } } + // repl loop loop { let line = readline::mal_readline("user> "); match line { None => break, _ => () } diff --git a/rust/src/step9_try.rs b/rust/src/step9_try.rs index 4449661d..0f6bd88e 100644 --- a/rust/src/step9_try.rs +++ b/rust/src/step9_try.rs @@ -9,7 +9,7 @@ use std::os; use types::{MalVal,MalRet,MalError,ErrString,ErrMalVal,err_str, Nil,False,Sym,List,Vector,Hash_Map,Func,MalFunc, - _nil,symbol,string,list,vector,hash_map,malfunc,malfuncd}; + symbol,_nil,string,list,vector,hash_map,malfunc,malfuncd}; use env::{Env,env_new,env_bind,env_root,env_find,env_set,env_get}; mod readline; mod types; @@ -78,11 +78,10 @@ fn quasiquote(ast: MalVal) -> MalVal { fn is_macro_call(ast: MalVal, env: Env) -> bool { match *ast { List(ref lst,_) => { - let ref a0 = *lst[0]; - match *a0 { - Sym(ref a0sym) => { - if env_find(env.clone(), a0sym.to_string()).is_some() { - match env_get(env, a0sym.to_string()) { + match *lst[0] { + Sym(_) => { + if env_find(env.clone(), lst[0].clone()).is_some() { + match env_get(env, lst[0].clone()) { Ok(f) => { match *f { MalFunc(ref mfd,_) => { @@ -113,8 +112,8 @@ fn macroexpand(mut ast: MalVal, env: Env) -> MalRet { }; let ref a0 = args[0]; let mf = match **a0 { - Sym(ref s) => { - match env_get(env.clone(), s.to_string()) { + Sym(_) => { + match env_get(env.clone(), a0.clone()) { Ok(mf) => mf, Err(e) => return Err(e), } @@ -138,8 +137,8 @@ fn eval_ast(ast: MalVal, env: Env) -> MalRet { let ast2 = ast.clone(); match *ast2 { //match *ast { - Sym(ref sym) => { - env_get(env.clone(), sym.clone()) + Sym(_) => { + env_get(env.clone(), ast) }, List(ref a,_) | Vector(ref a,_) => { let mut ast_vec : Vec = vec![]; @@ -215,8 +214,8 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { match res { Ok(r) => { match *a1 { - Sym(ref s) => { - env_set(&env.clone(), s.clone(), r.clone()); + Sym(_) => { + env_set(&env.clone(), a1.clone(), r.clone()); return Ok(r); }, _ => { @@ -238,10 +237,10 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { let b = it.next().unwrap(); let exp = it.next().unwrap(); match **b { - Sym(ref bstr) => { + Sym(_) => { match eval(exp.clone(), let_env.clone()) { Ok(r) => { - env_set(&let_env, bstr.clone(), r); + env_set(&let_env, b.clone(), r); }, Err(e) => { return Err(e); @@ -276,11 +275,11 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { match *r { MalFunc(ref mfd,_) => { match *a1 { - Sym(ref s) => { + Sym(_) => { let mut new_mfd = mfd.clone(); new_mfd.is_macro = true; let mf = malfuncd(new_mfd,_nil()); - env_set(&env.clone(), s.clone(), mf.clone()); + env_set(&env.clone(), a1.clone(), mf.clone()); return Ok(mf); }, _ => return err_str("def! of non-symbol"), @@ -311,8 +310,8 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { return err_str("wrong arity to catch* clause"); } let c1 = (*cat)[1].clone(); - let bstr = match *c1 { - Sym(ref s) => s, + match *c1 { + Sym(_) => {}, _ => return err_str("invalid catch* binding"), }; let exc = match err { @@ -320,7 +319,7 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { ErrString(s) => string(s), }; let bind_env = env_new(Some(env.clone())); - env_set(&bind_env, bstr.to_string(), exc); + env_set(&bind_env, c1.clone(), exc); let c2 = (*cat)[2].clone(); return eval(c2, bind_env); }, @@ -432,12 +431,13 @@ fn rep(str: &str, env: Env) -> Result { fn main() { // core.rs: defined using rust let repl_env = env_new(None); - for (k, v) in core::ns().into_iter() { env_set(&repl_env, k, v); } + for (k, v) in core::ns().into_iter() { + env_set(&repl_env, symbol(k.as_slice()), v); + } // see eval() for definition of "eval" - env_set(&repl_env, "*ARGV*".to_string(), list(vec![])); + env_set(&repl_env, symbol("*ARGV*".as_slice()), list(vec![])); // core.mal: defined using the language itself - let _ = rep("(def! *host-language* \"rust\")", repl_env.clone()); let _ = rep("(def! not (fn* (a) (if a false true)))", repl_env.clone()); let _ = rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env.clone()); let _ = rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", repl_env.clone()); @@ -449,7 +449,7 @@ fn main() { let mv_args = args.slice(2,args.len()).iter() .map(|a| string(a.to_string())) .collect::>(); - env_set(&repl_env, "*ARGV*".to_string(), list(mv_args)); + env_set(&repl_env, symbol("*ARGV*".as_slice()), list(mv_args)); let lf = "(load-file \"".to_string() + args[1] + "\")".to_string(); match rep(lf.as_slice(), repl_env.clone()) { Ok(_) => { @@ -465,7 +465,6 @@ fn main() { } // repl loop - let _ = rep("(println (str \"Mal [\" *host-language* \"]\"))", repl_env.clone()); loop { let line = readline::mal_readline("user> "); match line { None => break, _ => () } diff --git a/rust/src/stepA_interop.rs b/rust/src/stepA_interop.rs index 4449661d..8e30867c 100644 --- a/rust/src/stepA_interop.rs +++ b/rust/src/stepA_interop.rs @@ -9,7 +9,7 @@ use std::os; use types::{MalVal,MalRet,MalError,ErrString,ErrMalVal,err_str, Nil,False,Sym,List,Vector,Hash_Map,Func,MalFunc, - _nil,symbol,string,list,vector,hash_map,malfunc,malfuncd}; + symbol,_nil,string,list,vector,hash_map,malfunc,malfuncd}; use env::{Env,env_new,env_bind,env_root,env_find,env_set,env_get}; mod readline; mod types; @@ -78,11 +78,10 @@ fn quasiquote(ast: MalVal) -> MalVal { fn is_macro_call(ast: MalVal, env: Env) -> bool { match *ast { List(ref lst,_) => { - let ref a0 = *lst[0]; - match *a0 { - Sym(ref a0sym) => { - if env_find(env.clone(), a0sym.to_string()).is_some() { - match env_get(env, a0sym.to_string()) { + match *lst[0] { + Sym(_) => { + if env_find(env.clone(), lst[0].clone()).is_some() { + match env_get(env, lst[0].clone()) { Ok(f) => { match *f { MalFunc(ref mfd,_) => { @@ -113,8 +112,8 @@ fn macroexpand(mut ast: MalVal, env: Env) -> MalRet { }; let ref a0 = args[0]; let mf = match **a0 { - Sym(ref s) => { - match env_get(env.clone(), s.to_string()) { + Sym(_) => { + match env_get(env.clone(), a0.clone()) { Ok(mf) => mf, Err(e) => return Err(e), } @@ -138,8 +137,8 @@ fn eval_ast(ast: MalVal, env: Env) -> MalRet { let ast2 = ast.clone(); match *ast2 { //match *ast { - Sym(ref sym) => { - env_get(env.clone(), sym.clone()) + Sym(_) => { + env_get(env.clone(), ast) }, List(ref a,_) | Vector(ref a,_) => { let mut ast_vec : Vec = vec![]; @@ -215,8 +214,8 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { match res { Ok(r) => { match *a1 { - Sym(ref s) => { - env_set(&env.clone(), s.clone(), r.clone()); + Sym(_) => { + env_set(&env.clone(), a1.clone(), r.clone()); return Ok(r); }, _ => { @@ -238,10 +237,10 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { let b = it.next().unwrap(); let exp = it.next().unwrap(); match **b { - Sym(ref bstr) => { + Sym(_) => { match eval(exp.clone(), let_env.clone()) { Ok(r) => { - env_set(&let_env, bstr.clone(), r); + env_set(&let_env, b.clone(), r); }, Err(e) => { return Err(e); @@ -276,11 +275,11 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { match *r { MalFunc(ref mfd,_) => { match *a1 { - Sym(ref s) => { + Sym(_) => { let mut new_mfd = mfd.clone(); new_mfd.is_macro = true; let mf = malfuncd(new_mfd,_nil()); - env_set(&env.clone(), s.clone(), mf.clone()); + env_set(&env.clone(), a1.clone(), mf.clone()); return Ok(mf); }, _ => return err_str("def! of non-symbol"), @@ -311,8 +310,8 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { return err_str("wrong arity to catch* clause"); } let c1 = (*cat)[1].clone(); - let bstr = match *c1 { - Sym(ref s) => s, + match *c1 { + Sym(_) => {}, _ => return err_str("invalid catch* binding"), }; let exc = match err { @@ -320,7 +319,7 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { ErrString(s) => string(s), }; let bind_env = env_new(Some(env.clone())); - env_set(&bind_env, bstr.to_string(), exc); + env_set(&bind_env, c1.clone(), exc); let c2 = (*cat)[2].clone(); return eval(c2, bind_env); }, @@ -432,9 +431,11 @@ fn rep(str: &str, env: Env) -> Result { fn main() { // core.rs: defined using rust let repl_env = env_new(None); - for (k, v) in core::ns().into_iter() { env_set(&repl_env, k, v); } + for (k, v) in core::ns().into_iter() { + env_set(&repl_env, symbol(k.as_slice()), v); + } // see eval() for definition of "eval" - env_set(&repl_env, "*ARGV*".to_string(), list(vec![])); + env_set(&repl_env, symbol("*ARGV*".as_slice()), list(vec![])); // core.mal: defined using the language itself let _ = rep("(def! *host-language* \"rust\")", repl_env.clone()); @@ -449,7 +450,7 @@ fn main() { let mv_args = args.slice(2,args.len()).iter() .map(|a| string(a.to_string())) .collect::>(); - env_set(&repl_env, "*ARGV*".to_string(), list(mv_args)); + env_set(&repl_env, symbol("*ARGV*".as_slice()), list(mv_args)); let lf = "(load-file \"".to_string() + args[1] + "\")".to_string(); match rep(lf.as_slice(), repl_env.clone()) { Ok(_) => { diff --git a/rust/src/types.rs b/rust/src/types.rs index 0b59716f..141c3db7 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -78,7 +78,10 @@ impl MalType { Int(v) => res.push_str(v.to_string().as_slice()), Sym(ref v) => res.push_str((*v).as_slice()), Strn(ref v) => { - if print_readably { + if v.as_slice().starts_with("\u029e") { + res.push_str(":"); + res.push_str(v.as_slice().slice(2,v.len())) + } else if print_readably { res.push_str(escape_str((*v).as_slice()).as_slice()) } else { res.push_str(v.as_slice()) @@ -95,7 +98,10 @@ impl MalType { res.push_str("{"); for (key, value) in v.iter() { if first { first = false; } else { res.push_str(" "); } - if print_readably { + if key.as_slice().starts_with("\u029e") { + res.push_str(":"); + res.push_str(key.as_slice().slice(2,key.len())) + } else if print_readably { res.push_str(escape_str(key.as_slice()).as_slice()) } else { res.push_str(key.as_slice()) @@ -205,6 +211,17 @@ pub fn _int(i: int) -> MalVal { Rc::new(Int(i)) } // Symbols pub fn symbol(strn: &str) -> MalVal { Rc::new(Sym(strn.to_string())) } +pub fn _symbol(a: Vec) -> MalRet { + if a.len() != 1 { + return err_str("Wrong arity to symbol call"); + } + match *a[0].clone() { + Strn(ref s) => { + Ok(Rc::new(Sym(s.to_string()))) + }, + _ => return err_str("symbol called on non-string"), + } +} pub fn symbol_q(a:Vec) -> MalRet { if a.len() != 1 { return err_str("Wrong arity to symbol? call"); @@ -215,6 +232,35 @@ pub fn symbol_q(a:Vec) -> MalRet { } } +// Keywords +pub fn _keyword(a: Vec) -> MalRet { + if a.len() != 1 { + return err_str("Wrong arity to keyword call"); + } + match *a[0].clone() { + Strn(ref s) => { + Ok(Rc::new(Strn("\u029e".to_string() + s.to_string()))) + }, + _ => return err_str("keyword called on non-string"), + } +} +pub fn keyword_q(a:Vec) -> MalRet { + if a.len() != 1 { + return err_str("Wrong arity to keyword? call"); + } + match *a[0].clone() { + Strn(ref s) => { + if s.as_slice().starts_with("\u029e") { + Ok(_true()) + } else { + Ok(_false()) + } + }, + _ => Ok(_false()), + } +} + + // Strings pub fn strn(strn: &str) -> MalVal { Rc::new(Strn(strn.to_string())) } pub fn string(strn: String) -> MalVal { Rc::new(Strn(strn)) } diff --git a/tests/step1_read_print.mal b/tests/step1_read_print.mal index 3dc40230..57b2e34c 100644 --- a/tests/step1_read_print.mal +++ b/tests/step1_read_print.mal @@ -73,6 +73,12 @@ abc-def ;; ;; -------- Optional Functionality -------- +;; Testing keywords +:kw +;=>:kw +(:kw1 :kw2 :kw3) +;=>(:kw1 :kw2 :kw3) + ;; Testing read of vectors [+ 1 2] ;=>[+ 1 2] @@ -92,6 +98,7 @@ abc-def ;=>{"a" {"b" {"c" 3}}} { "a" {"b" { "cde" 3 } }} ;=>{"a" {"b" {"cde" 3}}} +;=>{:a {:b {:cde 3}}} ;; Testing read of comments ;; whole line comment (not an exception) diff --git a/tests/step2_eval.mal b/tests/step2_eval.mal index eb355925..2f48c738 100644 --- a/tests/step2_eval.mal +++ b/tests/step2_eval.mal @@ -23,3 +23,6 @@ {"a" (+ 7 8)} ;=>{"a" 15} + +{:a (+ 7 8)} +;=>{:a 15} diff --git a/tests/step4_if_fn_do.mal b/tests/step4_if_fn_do.mal index ee30ea31..46efeae8 100644 --- a/tests/step4_if_fn_do.mal +++ b/tests/step4_if_fn_do.mal @@ -14,6 +14,10 @@ ;=>(1 2 3) (count (list 1 2 3)) ;=>3 +(count (list)) +;=>0 +(count nil) +;=>0 (if (> (count (list 1 2 3)) 3) "yes" "no") ;=>"no" (if (>= (count (list 1 2 3)) 3) "yes" "no") @@ -335,6 +339,14 @@ a ;; ;; -------- Optional Functionality -------- +;; Testing keywords +(= :abc :abc) +;=>true +(= :abc :def) +;=>false +(= :abc ":abc") +;=>false + ;; Testing vector truthiness (if [] 7 8) ;=>7 diff --git a/tests/step8_macros.mal b/tests/step8_macros.mal index 8773ba82..cf8f5d19 100644 --- a/tests/step8_macros.mal +++ b/tests/step8_macros.mal @@ -1,13 +1,13 @@ ;; Testing nth, first and rest functions -(nth '() 0) -;=>nil (nth '(1) 0) ;=>1 (nth '(1 2) 1) ;=>2 -(nth '(1 2) 2) -;=>nil +(def! x "x") +(def! x (nth '(1 2) 2)) +x +;=>"x" (first '()) ;=>nil @@ -132,14 +132,14 @@ ;; Testing nth, first, rest with vectors -(nth [] 0) -;=>nil (nth [1] 0) ;=>1 (nth [1 2] 1) ;=>2 -(nth [1 2] 2) -;=>nil +(def! x "x") +(def! x (nth [1 2] 2)) +x +;=>"x" (first []) ;=>nil diff --git a/tests/step9_try.mal b/tests/step9_try.mal index aee79089..3781e4dc 100644 --- a/tests/step9_try.mal +++ b/tests/step9_try.mal @@ -88,6 +88,24 @@ ;; ;; -------- Optional Functionality -------- +;; Testing symbol and keyword functions +(symbol? :abc) +;=>false +(symbol? 'abc) +;=>true +(symbol? "abc") +;=>false +(symbol? (symbol "abc")) +;=>true +(keyword? :abc) +;=>true +(keyword? 'abc) +;=>false +(keyword? "abc") +;=>false +(keyword? (keyword "abc")) +;=>true + ;; Testing sequential? function (sequential? (list 1 2 3)) @@ -110,8 +128,16 @@ (vector 3 4 5) ;=>[3 4 5] +(map? {}) +;=>true +(map? '()) +;=>false (map? []) ;=>false +(map? 'abc) +;=>false +(map? :abc) +;=>false ;; ;; Testing hash-maps @@ -194,6 +220,26 @@ (count (keys hm3)) ;=>2 +;; Testing keywords as hash-map keys +(get {:abc 123} :abc) +;=>123 +(contains? {:abc 123} :abc) +;=>true +(contains? {:abcd 123} :abc) +;=>false +(assoc {} :bcd 234) +;=>{:bcd 234} +(dissoc {:cde 345 :fgh 456} :cde) +;=>{:fgh 456} +(keyword? (nth (keys {:abc 123 :def 456}) 0)) +;=>true +;;; TODO: support : in strings in make impl +;;;(keyword? (nth (keys {":abc" 123 ":def" 456}) 0)) +;;;;=>false +(keyword? (nth (vals {"a" :abc "b" :def}) 0)) +;=>true + + ;; ;; Testing conj function diff --git a/tests/test.txt b/tests/test.txt new file mode 100644 index 00000000..0f24bc04 --- /dev/null +++ b/tests/test.txt @@ -0,0 +1 @@ +A line of text diff --git a/vb/core.vb b/vb/core.vb index 43fc30ff..771d0cce 100644 --- a/vb/core.vb +++ b/vb/core.vb @@ -69,6 +69,24 @@ Namespace Mal End If End Function + Shared Function keyword(a As MalList) As MalVal + Dim s As String = DirectCast(a(0),MalString).getValue() + return new MalString(ChrW(&H029e) & s) + End Function + + Shared Function keyword_Q(a As MalList) As MalVal + If TypeOf a(0) Is MalString Then + Dim s As String = DirectCast(a(0),MalString).getValue() + If s.Substring(0,1) = Strings.ChrW(&H029e) Then + return MalTrue + Else + return MalFalse + End If + Else + return MalFalse + End If + End Function + ' Number functions Shared Function lt(a As MalList) As MalVal @@ -258,7 +276,13 @@ Namespace Mal End Function Shared Function nth(a As MalList) As MalVal - return DirectCast(a(0),MalList)( DirectCast(a(1),MalInt).getValue() ) + Dim idx As Integer = DirectCast(a(1),MalInt).getValue() + If (idx < DirectCast(a(0),MalList).size()) Then + return DirectCast(a(0),MalList)( idx ) + Else + throw new Mal.types.MalException( + "nth: index out of range") + End If End Function Shared Function first(a As MalList) As MalVal @@ -278,7 +302,11 @@ Namespace Mal End Function Shared Function count(a As MalList) As MalVal - return new MalInt(DirectCast(a(0),MalList).size()) + If a(0) Is Nil Then + return new MalInt(0) + Else + return new MalInt(DirectCast(a(0),MalList).size()) + End If End Function Shared Function conj(a As MalList) As MalVal @@ -371,6 +399,8 @@ Namespace Mal ns.Add("false?", New MalFunc(AddressOf false_Q)) ns.Add("symbol", new MalFunc(AddressOf symbol)) ns.Add("symbol?", New MalFunc(AddressOf symbol_Q)) + ns.Add("keyword", new MalFunc(AddressOf keyword)) + ns.Add("keyword?", New MalFunc(AddressOf keyword_Q)) ns.Add("pr-str",New MalFunc(AddressOf pr_str)) ns.Add("str", New MalFunc(AddressOf str)) diff --git a/vb/env.vb b/vb/env.vb index 30953440..a2c46289 100644 --- a/vb/env.vb +++ b/vb/env.vb @@ -26,8 +26,8 @@ Namespace Mal Next End Sub - Public Function find(key As String) As Env - If data.ContainsKey(key) Then + Public Function find(key As MalSymbol) As Env + If data.ContainsKey(key.getName()) Then return Me Else If outer IsNot Nothing Then return outer.find(key) @@ -36,18 +36,18 @@ Namespace Mal End If End Function - Public Function do_get(key As String) As MalVal + Public Function do_get(key As MalSymbol) As MalVal Dim e As Env = find(key) If e Is Nothing Then throw New Mal.types.MalException( - "'" & key & "' not found") + "'" & key.getName() & "' not found") Else - return e.data(key) + return e.data(key.getName()) End If End Function - Public Function do_set(key As String, value As MalVal) As Env - data(key) = value + Public Function do_set(key As MalSymbol, value As MalVal) As Env + data(key.getName()) = value return Me End Function End Class diff --git a/vb/printer.vb b/vb/printer.vb index 212b89c5..3f3e6e26 100644 --- a/vb/printer.vb +++ b/vb/printer.vb @@ -22,7 +22,9 @@ Namespace Mal print_readably As Boolean) As String Dim strs As New List(Of String) For Each entry As KeyValuePair(Of String, MalVal) In value - If print_readably Then + If entry.Key.Length > 0 and entry.Key(0) = ChrW(&H029e) Then + strs.Add(":" & entry.Key.Substring(1)) + Else If print_readably Then strs.Add("""" & entry.Key.ToString() & """") Else strs.Add(entry.Key.ToString()) diff --git a/vb/reader.vb b/vb/reader.vb index 121aac26..9d4e03dd 100644 --- a/vb/reader.vb +++ b/vb/reader.vb @@ -64,7 +64,7 @@ Namespace Mal Shared Function read_atom(rdr As Reader) As MalVal Dim token As String = rdr.get_next() - Dim pattern As String = "(^-?[0-9]+$)|(^-?[0-9][0-9.]*$)|(^nil$)|(^true$)|(^false$)|^("".*"")$|(^[^""]*$)" + Dim pattern As String = "(^-?[0-9]+$)|(^-?[0-9][0-9.]*$)|(^nil$)|(^true$)|(^false$)|^("".*"")$|^:(.*)|(^[^""]*$)" Dim regex As Regex = New Regex(pattern) Dim match As Match = regex.Match(token) 'Console.WriteLine("token: ^" + token + "$") @@ -86,7 +86,9 @@ Namespace Mal .Replace("\""", """") _ .Replace("\n", Environment.NewLine)) Else If match.Groups(7).Value <> String.Empty Then - return New Mal.types.MalSymbol(match.Groups(7).Value) + return New Mal.types.MalString(ChrW(&H029e) & match.Groups(7).Value) + Else If match.Groups(8).Value <> String.Empty Then + return New Mal.types.MalSymbol(match.Groups(8).Value) Else throw New ParseError("unrecognized '" & match.Groups(0).Value & "'") End If diff --git a/vb/step3_env.vb b/vb/step3_env.vb index 5793fdb1..dfee614b 100644 --- a/vb/step3_env.vb +++ b/vb/step3_env.vb @@ -21,8 +21,7 @@ Namespace Mal ' eval Shared Function eval_ast(ast As MalVal, env As MalEnv) As MalVal If TypeOf ast Is MalSymbol Then - Dim sym As MalSymbol = DirectCast(ast, MalSymbol) - return env.do_get(sym.getName()) + return env.do_get(DirectCast(ast, MalSymbol)) Else If TypeOf ast Is MalList Then Dim old_lst As MalList = DirectCast(ast, MalList) Dim new_lst As MalList @@ -66,7 +65,7 @@ Namespace Mal Dim a1 As MalVal = ast(1) Dim a2 As MalVal = ast(2) Dim res As MalVal = EVAL(a2, env) - env.do_set(DirectCast(a1,MalSymbol).getName(), res) + env.do_set(DirectCast(a1,MalSymbol), res) return res Case "let*" Dim a1 As MalVal = ast(1) @@ -77,7 +76,7 @@ Namespace Mal For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2 key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol) val = DirectCast(a1,MalList)(i+1) - let_env.do_set(key.getName(), EVAL(val, let_env)) + let_env.do_set(key, EVAL(val, let_env)) Next return EVAL(a2, let_env) Case Else @@ -119,10 +118,10 @@ Namespace Mal Dim args As String() = Environment.GetCommandLineArgs() repl_env = New MalEnv(Nothing) - repl_env.do_set("+", New MalFunc(AddressOf add)) - repl_env.do_set("-", New MalFunc(AddressOf minus)) - repl_env.do_set("*", New MalFunc(AddressOf mult)) - repl_env.do_set("/", New MalFunc(AddressOf div)) + repl_env.do_set(new MalSymbol("+"), New MalFunc(AddressOf add)) + repl_env.do_set(new MalSymbol("-"), New MalFunc(AddressOf minus)) + repl_env.do_set(new MalSymbol("*"), New MalFunc(AddressOf mult)) + repl_env.do_set(new MalSymbol("/"), New MalFunc(AddressOf div)) If args.Length > 1 AndAlso args(1) = "--raw" Then diff --git a/vb/step4_if_fn_do.vb b/vb/step4_if_fn_do.vb index 4cf8721a..470ae866 100644 --- a/vb/step4_if_fn_do.vb +++ b/vb/step4_if_fn_do.vb @@ -21,8 +21,7 @@ Namespace Mal ' eval Shared Function eval_ast(ast As MalVal, env As MalEnv) As MalVal If TypeOf ast Is MalSymbol Then - Dim sym As MalSymbol = DirectCast(ast, MalSymbol) - return env.do_get(sym.getName()) + return env.do_get(DirectCast(ast, MalSymbol)) Else If TypeOf ast Is MalList Then Dim old_lst As MalList = DirectCast(ast, MalList) Dim new_lst As MalList @@ -83,7 +82,7 @@ Namespace Mal Dim a1 As MalVal = ast(1) Dim a2 As MalVal = ast(2) Dim res As MalVal = EVAL(a2, env) - env.do_set(DirectCast(a1,MalSymbol).getName(), res) + env.do_set(DirectCast(a1,MalSymbol), res) return res Case "let*" Dim a1 As MalVal = ast(1) @@ -94,7 +93,7 @@ Namespace Mal For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2 key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol) val = DirectCast(a1,MalList)(i+1) - let_env.do_set(key.getName(), EVAL(val, let_env)) + let_env.do_set(key, EVAL(val, let_env)) Next return EVAL(a2, let_env) Case "do" @@ -152,7 +151,7 @@ Namespace Mal ' core.vb: defined using VB.NET For Each entry As KeyValuePair(Of String,MalVal) In core.ns() - repl_env.do_set(entry.Key, entry.Value) + repl_env.do_set(new MalSymbol(entry.Key), entry.Value) Next ' core.mal: defined using the language itself diff --git a/vb/step5_tco.vb b/vb/step5_tco.vb index 3c1f51cd..bb36b22b 100644 --- a/vb/step5_tco.vb +++ b/vb/step5_tco.vb @@ -21,8 +21,7 @@ Namespace Mal ' eval Shared Function eval_ast(ast As MalVal, env As MalEnv) As MalVal If TypeOf ast Is MalSymbol Then - Dim sym As MalSymbol = DirectCast(ast, MalSymbol) - return env.do_get(sym.getName()) + return env.do_get(DirectCast(ast, MalSymbol)) Else If TypeOf ast Is MalList Then Dim old_lst As MalList = DirectCast(ast, MalList) Dim new_lst As MalList @@ -85,7 +84,7 @@ Namespace Mal Dim a1 As MalVal = ast(1) Dim a2 As MalVal = ast(2) Dim res As MalVal = EVAL(a2, env) - env.do_set(DirectCast(a1,MalSymbol).getName(), res) + env.do_set(DirectCast(a1,MalSymbol), res) return res Case "let*" Dim a1 As MalVal = ast(1) @@ -96,7 +95,7 @@ Namespace Mal For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2 key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol) val = DirectCast(a1,MalList)(i+1) - let_env.do_set(key.getName(), EVAL(val, let_env)) + let_env.do_set(key, EVAL(val, let_env)) Next orig_ast = a2 env = let_env @@ -161,7 +160,7 @@ Namespace Mal ' core.vb: defined using VB.NET For Each entry As KeyValuePair(Of String,MalVal) In core.ns() - repl_env.do_set(entry.Key, entry.Value) + repl_env.do_set(new MalSymbol(entry.Key), entry.Value) Next ' core.mal: defined using the language itself diff --git a/vb/step6_file.vb b/vb/step6_file.vb index 58f8cd24..9ea0e9f8 100644 --- a/vb/step6_file.vb +++ b/vb/step6_file.vb @@ -22,8 +22,7 @@ Namespace Mal ' eval Shared Function eval_ast(ast As MalVal, env As MalEnv) As MalVal If TypeOf ast Is MalSymbol Then - Dim sym As MalSymbol = DirectCast(ast, MalSymbol) - return env.do_get(sym.getName()) + return env.do_get(DirectCast(ast, MalSymbol)) Else If TypeOf ast Is MalList Then Dim old_lst As MalList = DirectCast(ast, MalList) Dim new_lst As MalList @@ -86,7 +85,7 @@ Namespace Mal Dim a1 As MalVal = ast(1) Dim a2 As MalVal = ast(2) Dim res As MalVal = EVAL(a2, env) - env.do_set(DirectCast(a1,MalSymbol).getName(), res) + env.do_set(DirectCast(a1,MalSymbol), res) return res Case "let*" Dim a1 As MalVal = ast(1) @@ -97,7 +96,7 @@ Namespace Mal For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2 key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol) val = DirectCast(a1,MalList)(i+1) - let_env.do_set(key.getName(), EVAL(val, let_env)) + let_env.do_set(key, EVAL(val, let_env)) Next orig_ast = a2 env = let_env @@ -166,9 +165,9 @@ Namespace Mal ' core.vb: defined using VB.NET For Each entry As KeyValuePair(Of String,MalVal) In core.ns() - repl_env.do_set(entry.Key, entry.Value) + repl_env.do_set(new MalSymbol(entry.Key), entry.Value) Next - repl_env.do_set("eval", new MalFunc(AddressOf do_eval)) + repl_env.do_set(new MalSymbol("eval"), new MalFunc(AddressOf do_eval)) Dim fileIdx As Integer = 1 If args.Length > 1 AndAlso args(1) = "--raw" Then Mal.readline.SetMode(Mal.readline.Modes.Raw) @@ -178,7 +177,7 @@ Namespace Mal For i As Integer = fileIdx+1 To args.Length-1 argv.conj_BANG(new MalString(args(i))) Next - repl_env.do_set("*ARGV*", argv) + repl_env.do_set(new MalSymbol("*ARGV*"), argv) ' core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") diff --git a/vb/step7_quote.vb b/vb/step7_quote.vb index 3f2d614c..f38741ef 100644 --- a/vb/step7_quote.vb +++ b/vb/step7_quote.vb @@ -51,8 +51,7 @@ Namespace Mal Shared Function eval_ast(ast As MalVal, env As MalEnv) As MalVal If TypeOf ast Is MalSymbol Then - Dim sym As MalSymbol = DirectCast(ast, MalSymbol) - return env.do_get(sym.getName()) + return env.do_get(DirectCast(ast, MalSymbol)) Else If TypeOf ast Is MalList Then Dim old_lst As MalList = DirectCast(ast, MalList) Dim new_lst As MalList @@ -115,7 +114,7 @@ Namespace Mal Dim a1 As MalVal = ast(1) Dim a2 As MalVal = ast(2) Dim res As MalVal = EVAL(a2, env) - env.do_set(DirectCast(a1,MalSymbol).getName(), res) + env.do_set(DirectCast(a1,MalSymbol), res) return res Case "let*" Dim a1 As MalVal = ast(1) @@ -126,7 +125,7 @@ Namespace Mal For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2 key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol) val = DirectCast(a1,MalList)(i+1) - let_env.do_set(key.getName(), EVAL(val, let_env)) + let_env.do_set(key, EVAL(val, let_env)) Next orig_ast = a2 env = let_env @@ -199,9 +198,9 @@ Namespace Mal ' core.vb: defined using VB.NET For Each entry As KeyValuePair(Of String,MalVal) In core.ns() - repl_env.do_set(entry.Key, entry.Value) + repl_env.do_set(new MalSymbol(entry.Key), entry.Value) Next - repl_env.do_set("eval", new MalFunc(AddressOf do_eval)) + repl_env.do_set(new MalSymbol("eval"), new MalFunc(AddressOf do_eval)) Dim fileIdx As Integer = 1 If args.Length > 1 AndAlso args(1) = "--raw" Then Mal.readline.SetMode(Mal.readline.Modes.Raw) @@ -211,7 +210,7 @@ Namespace Mal For i As Integer = fileIdx+1 To args.Length-1 argv.conj_BANG(new MalString(args(i))) Next - repl_env.do_set("*ARGV*", argv) + repl_env.do_set(new MalSymbol("*ARGV*"), argv) ' core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") diff --git a/vb/step8_macros.vb b/vb/step8_macros.vb index 1a7f2110..400dbe78 100644 --- a/vb/step8_macros.vb +++ b/vb/step8_macros.vb @@ -52,8 +52,8 @@ Namespace Mal If TypeOf ast Is MalList Then Dim a0 As MalVal = DirectCast(ast,MalList)(0) If TypeOf a0 Is MalSymbol AndAlso _ - env.find(DirectCast(a0,MalSymbol).getName()) IsNot Nothing Then - Dim mac As MalVal = env.do_get(DirectCast(a0,MalSymbol).getName()) + env.find(DirectCast(a0,MalSymbol)) IsNot Nothing Then + Dim mac As MalVal = env.do_get(DirectCast(a0,MalSymbol)) If TypeOf mac Is MalFunc AndAlso _ DirectCast(mac,MalFunc).isMacro() Then return True @@ -66,7 +66,7 @@ Namespace Mal Shared Function macroexpand(ast As MalVal, env As MalEnv) As MalVal While is_macro_call(ast, env) Dim a0 As MalSymbol = DirectCast(DirectCast(ast,MalList)(0),MalSymbol) - Dim mac As MalFunc = DirectCast(env.do_get(a0.getName()),MalFunc) + Dim mac As MalFunc = DirectCast(env.do_get(a0),MalFunc) ast = mac.apply(DirectCast(ast,MalList).rest()) End While return ast @@ -74,8 +74,7 @@ Namespace Mal Shared Function eval_ast(ast As MalVal, env As MalEnv) As MalVal If TypeOf ast Is MalSymbol Then - Dim sym As MalSymbol = DirectCast(ast, MalSymbol) - return env.do_get(sym.getName()) + return env.do_get(DirectCast(ast, MalSymbol)) Else If TypeOf ast Is MalList Then Dim old_lst As MalList = DirectCast(ast, MalList) Dim new_lst As MalList @@ -143,7 +142,7 @@ Namespace Mal Dim a1 As MalVal = ast(1) Dim a2 As MalVal = ast(2) Dim res As MalVal = EVAL(a2, env) - env.do_set(DirectCast(a1,MalSymbol).getName(), res) + env.do_set(DirectCast(a1,MalSymbol), res) return res Case "let*" Dim a1 As MalVal = ast(1) @@ -154,7 +153,7 @@ Namespace Mal For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2 key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol) val = DirectCast(a1,MalList)(i+1) - let_env.do_set(key.getName(), EVAL(val, let_env)) + let_env.do_set(key, EVAL(val, let_env)) Next orig_ast = a2 env = let_env @@ -167,7 +166,7 @@ Namespace Mal Dim a2 As MalVal = ast(2) Dim res As MalVal = EVAL(a2, env) DirectCast(res,MalFunc).setMacro() - env.do_set(DirectCast(a1,MalSymbol).getName(), res) + env.do_set(DirectCast(a1,MalSymbol), res) return res Case "macroexpand" Dim a1 As MalVal = ast(1) @@ -237,9 +236,9 @@ Namespace Mal ' core.vb: defined using VB.NET For Each entry As KeyValuePair(Of String,MalVal) In core.ns() - repl_env.do_set(entry.Key, entry.Value) + repl_env.do_set(new MalSymbol(entry.Key), entry.Value) Next - repl_env.do_set("eval", new MalFunc(AddressOf do_eval)) + repl_env.do_set(new MalSymbol("eval"), new MalFunc(AddressOf do_eval)) Dim fileIdx As Integer = 1 If args.Length > 1 AndAlso args(1) = "--raw" Then Mal.readline.SetMode(Mal.readline.Modes.Raw) @@ -249,7 +248,7 @@ Namespace Mal For i As Integer = fileIdx+1 To args.Length-1 argv.conj_BANG(new MalString(args(i))) Next - repl_env.do_set("*ARGV*", argv) + repl_env.do_set(new MalSymbol("*ARGV*"), argv) ' core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") diff --git a/vb/step9_try.vb b/vb/step9_try.vb index 5bf600de..d4a7af46 100644 --- a/vb/step9_try.vb +++ b/vb/step9_try.vb @@ -52,8 +52,8 @@ Namespace Mal If TypeOf ast Is MalList Then Dim a0 As MalVal = DirectCast(ast,MalList)(0) If TypeOf a0 Is MalSymbol AndAlso _ - env.find(DirectCast(a0,MalSymbol).getName()) IsNot Nothing Then - Dim mac As MalVal = env.do_get(DirectCast(a0,MalSymbol).getName()) + env.find(DirectCast(a0,MalSymbol)) IsNot Nothing Then + Dim mac As MalVal = env.do_get(DirectCast(a0,MalSymbol)) If TypeOf mac Is MalFunc AndAlso _ DirectCast(mac,MalFunc).isMacro() Then return True @@ -66,7 +66,7 @@ Namespace Mal Shared Function macroexpand(ast As MalVal, env As MalEnv) As MalVal While is_macro_call(ast, env) Dim a0 As MalSymbol = DirectCast(DirectCast(ast,MalList)(0),MalSymbol) - Dim mac As MalFunc = DirectCast(env.do_get(a0.getName()),MalFunc) + Dim mac As MalFunc = DirectCast(env.do_get(a0),MalFunc) ast = mac.apply(DirectCast(ast,MalList).rest()) End While return ast @@ -74,8 +74,7 @@ Namespace Mal Shared Function eval_ast(ast As MalVal, env As MalEnv) As MalVal If TypeOf ast Is MalSymbol Then - Dim sym As MalSymbol = DirectCast(ast, MalSymbol) - return env.do_get(sym.getName()) + return env.do_get(DirectCast(ast, MalSymbol)) Else If TypeOf ast Is MalList Then Dim old_lst As MalList = DirectCast(ast, MalList) Dim new_lst As MalList @@ -143,7 +142,7 @@ Namespace Mal Dim a1 As MalVal = ast(1) Dim a2 As MalVal = ast(2) Dim res As MalVal = EVAL(a2, env) - env.do_set(DirectCast(a1,MalSymbol).getName(), res) + env.do_set(DirectCast(a1,MalSymbol), res) return res Case "let*" Dim a1 As MalVal = ast(1) @@ -154,7 +153,7 @@ Namespace Mal For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2 key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol) val = DirectCast(a1,MalList)(i+1) - let_env.do_set(key.getName(), EVAL(val, let_env)) + let_env.do_set(key, EVAL(val, let_env)) Next orig_ast = a2 env = let_env @@ -167,7 +166,7 @@ Namespace Mal Dim a2 As MalVal = ast(2) Dim res As MalVal = EVAL(a2, env) DirectCast(res,MalFunc).setMacro() - env.do_set(DirectCast(a1,MalSymbol).getName(), res) + env.do_set(DirectCast(a1,MalSymbol), res) return res Case "macroexpand" Dim a1 As MalVal = ast(1) @@ -260,9 +259,9 @@ Namespace Mal ' core.vb: defined using VB.NET For Each entry As KeyValuePair(Of String,MalVal) In core.ns() - repl_env.do_set(entry.Key, entry.Value) + repl_env.do_set(new MalSymbol(entry.Key), entry.Value) Next - repl_env.do_set("eval", new MalFunc(AddressOf do_eval)) + repl_env.do_set(new MalSymbol("eval"), new MalFunc(AddressOf do_eval)) Dim fileIdx As Integer = 1 If args.Length > 1 AndAlso args(1) = "--raw" Then Mal.readline.SetMode(Mal.readline.Modes.Raw) @@ -272,7 +271,7 @@ Namespace Mal For i As Integer = fileIdx+1 To args.Length-1 argv.conj_BANG(new MalString(args(i))) Next - repl_env.do_set("*ARGV*", argv) + repl_env.do_set(new MalSymbol("*ARGV*"), argv) ' core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") diff --git a/vb/stepA_interop.vb b/vb/stepA_interop.vb index 01702fa4..f9af0382 100644 --- a/vb/stepA_interop.vb +++ b/vb/stepA_interop.vb @@ -52,8 +52,8 @@ Namespace Mal If TypeOf ast Is MalList Then Dim a0 As MalVal = DirectCast(ast,MalList)(0) If TypeOf a0 Is MalSymbol AndAlso _ - env.find(DirectCast(a0,MalSymbol).getName()) IsNot Nothing Then - Dim mac As MalVal = env.do_get(DirectCast(a0,MalSymbol).getName()) + env.find(DirectCast(a0,MalSymbol)) IsNot Nothing Then + Dim mac As MalVal = env.do_get(DirectCast(a0,MalSymbol)) If TypeOf mac Is MalFunc AndAlso _ DirectCast(mac,MalFunc).isMacro() Then return True @@ -66,7 +66,7 @@ Namespace Mal Shared Function macroexpand(ast As MalVal, env As MalEnv) As MalVal While is_macro_call(ast, env) Dim a0 As MalSymbol = DirectCast(DirectCast(ast,MalList)(0),MalSymbol) - Dim mac As MalFunc = DirectCast(env.do_get(a0.getName()),MalFunc) + Dim mac As MalFunc = DirectCast(env.do_get(a0),MalFunc) ast = mac.apply(DirectCast(ast,MalList).rest()) End While return ast @@ -74,8 +74,7 @@ Namespace Mal Shared Function eval_ast(ast As MalVal, env As MalEnv) As MalVal If TypeOf ast Is MalSymbol Then - Dim sym As MalSymbol = DirectCast(ast, MalSymbol) - return env.do_get(sym.getName()) + return env.do_get(DirectCast(ast, MalSymbol)) Else If TypeOf ast Is MalList Then Dim old_lst As MalList = DirectCast(ast, MalList) Dim new_lst As MalList @@ -143,7 +142,7 @@ Namespace Mal Dim a1 As MalVal = ast(1) Dim a2 As MalVal = ast(2) Dim res As MalVal = EVAL(a2, env) - env.do_set(DirectCast(a1,MalSymbol).getName(), res) + env.do_set(DirectCast(a1,MalSymbol), res) return res Case "let*" Dim a1 As MalVal = ast(1) @@ -154,7 +153,7 @@ Namespace Mal For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2 key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol) val = DirectCast(a1,MalList)(i+1) - let_env.do_set(key.getName(), EVAL(val, let_env)) + let_env.do_set(key, EVAL(val, let_env)) Next orig_ast = a2 env = let_env @@ -167,7 +166,7 @@ Namespace Mal Dim a2 As MalVal = ast(2) Dim res As MalVal = EVAL(a2, env) DirectCast(res,MalFunc).setMacro() - env.do_set(DirectCast(a1,MalSymbol).getName(), res) + env.do_set(DirectCast(a1,MalSymbol), res) return res Case "macroexpand" Dim a1 As MalVal = ast(1) @@ -260,9 +259,9 @@ Namespace Mal ' core.vb: defined using VB.NET For Each entry As KeyValuePair(Of String,MalVal) In core.ns() - repl_env.do_set(entry.Key, entry.Value) + repl_env.do_set(new MalSymbol(entry.Key), entry.Value) Next - repl_env.do_set("eval", new MalFunc(AddressOf do_eval)) + repl_env.do_set(new MalSymbol("eval"), new MalFunc(AddressOf do_eval)) Dim fileIdx As Integer = 1 If args.Length > 1 AndAlso args(1) = "--raw" Then Mal.readline.SetMode(Mal.readline.Modes.Raw) @@ -272,7 +271,7 @@ Namespace Mal For i As Integer = fileIdx+1 To args.Length-1 argv.conj_BANG(new MalString(args(i))) Next - repl_env.do_set("*ARGV*", argv) + repl_env.do_set(new MalSymbol("*ARGV*"), argv) ' core.mal: defined using the language itself REP("(def! *host-language* ""VB.NET"")") diff --git a/vb/types.vb b/vb/types.vb index 303a02e8..4da75bdc 100644 --- a/vb/types.vb +++ b/vb/types.vb @@ -232,7 +232,9 @@ namespace Mal return """" & value & """" End Function Public Overrides Function ToString(print_readably As Boolean) As String - If print_readably Then + If value.Length > 0 AndAlso value(0) = ChrW(&H029e) Then + return ":" & value.Substring(1) + Else If print_readably Then return """" & _ value.Replace("\", "\\") _ .Replace("""", "\""") _ -- 2.20.1