aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Martin <github@martintribe.org>2014-12-18 20:33:49 -0600
committerJoel Martin <github@martintribe.org>2015-01-09 16:16:50 -0600
commitb8ee29b22fbaa7a01f2754b4d6dd9af52e02017c (patch)
treef4d977ed220e9a3f665cfbf4f68770a81e4c2095
parentaaba249304b184e12e2445ab22d66df1f39a51a5 (diff)
downloadmal-b8ee29b22fbaa7a01f2754b4d6dd9af52e02017c.tar.gz
mal-b8ee29b22fbaa7a01f2754b4d6dd9af52e02017c.zip
All: add keywords.
Also, fix nth and count to match cloure.
-rw-r--r--Makefile2
-rw-r--r--bash/core.sh12
-rw-r--r--bash/env.sh10
-rw-r--r--bash/printer.sh26
-rw-r--r--bash/reader.sh3
-rwxr-xr-xbash/step3_env.sh20
-rwxr-xr-xbash/step4_if_fn_do.sh19
-rwxr-xr-xbash/step5_tco.sh19
-rwxr-xr-xbash/step6_file.sh22
-rwxr-xr-xbash/step7_quote.sh25
-rwxr-xr-xbash/step8_macros.sh36
-rwxr-xr-xbash/step9_try.sh44
-rwxr-xr-xbash/stepA_interop.sh45
-rw-r--r--bash/types.sh33
-rw-r--r--c/core.c22
-rw-r--r--c/core.h2
-rw-r--r--c/env.c18
-rw-r--r--c/printer.c21
-rw-r--r--c/reader.c7
-rw-r--r--c/step3_env.c14
-rw-r--r--c/step4_if_fn_do.c17
-rw-r--r--c/step5_tco.c15
-rw-r--r--c/step6_file.c20
-rw-r--r--c/step7_quote.c20
-rw-r--r--c/step8_macros.c29
-rw-r--r--c/step9_try.c31
-rw-r--r--c/stepA_interop.c29
-rw-r--r--c/types.c8
-rw-r--r--c/types.h7
-rw-r--r--clojure/src/core.clj20
-rw-r--r--coffee/core.coffee7
-rw-r--r--coffee/env.coffee15
-rw-r--r--coffee/printer.coffee1
-rw-r--r--coffee/reader.coffee9
-rw-r--r--coffee/step3_env.coffee14
-rw-r--r--coffee/step4_if_fn_do.coffee8
-rw-r--r--coffee/step5_tco.coffee8
-rw-r--r--coffee/step6_file.coffee14
-rw-r--r--coffee/step7_quote.coffee14
-rw-r--r--coffee/step8_macros.coffee20
-rw-r--r--coffee/step9_try.coffee22
-rw-r--r--coffee/stepA_interop.coffee20
-rw-r--r--coffee/types.coffee9
-rw-r--r--cs/core.cs31
-rw-r--r--cs/env.cs14
-rw-r--r--cs/printer.cs4
-rw-r--r--cs/reader.cs6
-rw-r--r--cs/step2_eval.cs2
-rw-r--r--cs/step3_env.cs21
-rw-r--r--cs/step4_if_fn_do.cs11
-rw-r--r--cs/step5_tco.cs11
-rw-r--r--cs/step6_file.cs16
-rw-r--r--cs/step7_quote.cs16
-rw-r--r--cs/step8_macros.cs24
-rw-r--r--cs/step9_try.cs24
-rw-r--r--cs/stepA_interop.cs24
-rw-r--r--cs/types.cs4
-rw-r--r--docs/TODO116
-rw-r--r--go/src/core/core.go12
-rw-r--r--go/src/env/env.go14
-rw-r--r--go/src/printer/printer.go4
-rw-r--r--go/src/reader/reader.go2
-rw-r--r--go/src/step3_env/step3_env.go14
-rw-r--r--go/src/step4_if_fn_do/step4_if_fn_do.go8
-rw-r--r--go/src/step5_tco/step5_tco.go8
-rw-r--r--go/src/step6_file/step6_file.go14
-rw-r--r--go/src/step7_quote/step7_quote.go14
-rw-r--r--go/src/step8_macros/step8_macros.go22
-rw-r--r--go/src/step9_try/step9_try.go24
-rw-r--r--go/src/stepA_interop/stepA_interop.go22
-rw-r--r--go/src/types/types.go22
-rw-r--r--java/src/main/java/mal/core.java36
-rw-r--r--java/src/main/java/mal/env.java15
-rw-r--r--java/src/main/java/mal/printer.java5
-rw-r--r--java/src/main/java/mal/reader.java6
-rw-r--r--java/src/main/java/mal/step3_env.java18
-rw-r--r--java/src/main/java/mal/step4_if_fn_do.java9
-rw-r--r--java/src/main/java/mal/step5_tco.java9
-rw-r--r--java/src/main/java/mal/step6_file.java13
-rw-r--r--java/src/main/java/mal/step7_quote.java13
-rw-r--r--java/src/main/java/mal/step8_macros.java21
-rw-r--r--java/src/main/java/mal/step9_try.java23
-rw-r--r--java/src/main/java/mal/stepA_interop.java21
-rw-r--r--java/src/main/java/mal/types.java5
-rw-r--r--js/core.js10
-rw-r--r--js/env.js20
-rw-r--r--js/printer.js8
-rw-r--r--js/reader.js2
-rw-r--r--js/step3_env.js10
-rw-r--r--js/step4_if_fn_do.js4
-rw-r--r--js/step5_tco.js4
-rw-r--r--js/step6_file.js9
-rw-r--r--js/step7_quote.js9
-rw-r--r--js/step8_macros.js15
-rw-r--r--js/step9_try.js17
-rw-r--r--js/stepA_interop.js15
-rw-r--r--js/types.js16
-rw-r--r--make/core.mk19
-rw-r--r--make/printer.mk6
-rwxr-xr-xmake/reader.mk16
-rw-r--r--make/step3_env.mk3
-rw-r--r--make/step4_if_fn_do.mk3
-rw-r--r--make/step6_file.mk3
-rw-r--r--make/step7_quote.mk3
-rw-r--r--make/step8_macros.mk3
-rw-r--r--make/step9_try.mk3
-rw-r--r--make/stepA_interop.mk3
-rw-r--r--make/types.mk18
-rw-r--r--make/util.mk1
-rw-r--r--mal/core.mal2
-rw-r--r--perl/core.pm25
-rw-r--r--perl/env.pm8
-rw-r--r--perl/printer.pm4
-rw-r--r--perl/reader.pm3
-rw-r--r--perl/readline.pm31
-rw-r--r--perl/step0.5_repl.pl33
-rw-r--r--perl/step1_read_print.pl5
-rw-r--r--perl/step2_eval.pl5
-rw-r--r--perl/step3_env.pl19
-rw-r--r--perl/step4_if_fn_do.pl15
-rw-r--r--perl/step5_tco.pl15
-rw-r--r--perl/step6_file.pl20
-rw-r--r--perl/step7_quote.pl20
-rw-r--r--perl/step8_macros.pl31
-rw-r--r--perl/step9_try.pl30
-rw-r--r--perl/stepA_interop.pl28
-rw-r--r--perl/types.pm12
-rw-r--r--php/core.php8
-rw-r--r--php/env.php8
-rw-r--r--php/printer.php4
-rw-r--r--php/reader.php2
-rw-r--r--php/step3_env.php14
-rw-r--r--php/step4_if_fn_do.php8
-rw-r--r--php/step5_tco.php8
-rw-r--r--php/step6_file.php12
-rw-r--r--php/step7_quote.php12
-rw-r--r--php/step8_macros.php20
-rw-r--r--php/step9_try.php22
-rw-r--r--php/stepA_interop.php20
-rw-r--r--php/types.php7
-rw-r--r--ps/core.ps20
-rw-r--r--ps/printer.ps25
-rw-r--r--ps/reader.ps32
-rw-r--r--ps/types.ps23
-rw-r--r--python/core.py10
-rw-r--r--python/mal_types.py7
-rw-r--r--python/printer.py4
-rw-r--r--python/reader.py3
-rw-r--r--python/step3_env.py8
-rw-r--r--python/step4_if_fn_do.py2
-rw-r--r--python/step5_tco.py2
-rw-r--r--python/step6_file.py6
-rw-r--r--python/step7_quote.py6
-rw-r--r--python/step8_macros.py6
-rw-r--r--python/step9_try.py8
-rw-r--r--python/stepA_interop.py6
-rw-r--r--r/core.r14
-rw-r--r--r/printer.r4
-rw-r--r--r/reader.r2
-rw-r--r--r/types.r5
-rw-r--r--ruby/core.rb8
-rw-r--r--ruby/printer.rb4
-rw-r--r--ruby/reader.rb1
-rw-r--r--rust/src/core.rs6
-rw-r--r--rust/src/env.rs57
-rw-r--r--rust/src/reader.rs2
-rw-r--r--rust/src/step3_env.rs22
-rw-r--r--rust/src/step4_if_fn_do.rs18
-rw-r--r--rust/src/step5_tco.rs18
-rw-r--r--rust/src/step6_file.rs22
-rw-r--r--rust/src/step7_quote.rs22
-rw-r--r--rust/src/step8_macros.rs40
-rw-r--r--rust/src/step9_try.rs47
-rw-r--r--rust/src/stepA_interop.rs45
-rw-r--r--rust/src/types.rs50
-rw-r--r--tests/step1_read_print.mal7
-rw-r--r--tests/step2_eval.mal3
-rw-r--r--tests/step4_if_fn_do.mal12
-rw-r--r--tests/step8_macros.mal16
-rw-r--r--tests/step9_try.mal46
-rw-r--r--tests/test.txt1
-rw-r--r--vb/core.vb34
-rw-r--r--vb/env.vb14
-rw-r--r--vb/printer.vb4
-rw-r--r--vb/reader.vb6
-rw-r--r--vb/step3_env.vb15
-rw-r--r--vb/step4_if_fn_do.vb9
-rw-r--r--vb/step5_tco.vb9
-rw-r--r--vb/step6_file.vb13
-rw-r--r--vb/step7_quote.vb13
-rw-r--r--vb/step8_macros.vb21
-rw-r--r--vb/step9_try.vb21
-rw-r--r--vb/stepA_interop.vb21
-rw-r--r--vb/types.vb4
194 files changed, 1836 insertions, 1114 deletions
diff --git a/Makefile b/Makefile
index 0a01e01..bd25ad7 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 c8a0261..ca53c43 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 2eabe8b..9595aa2 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 911db17..0d23028 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 ee7e505..a00e7a1 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 a837e00..d924b03 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 6fd7301..dd0db03 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 fc36b46..f282e44 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 e6ee571..c655853 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 d71e24b..2f2e67e 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 3be3651..51d83bd 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 db0b5a7..85698b4 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 2422643..9a760e4 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 5cdc14a..4c2c824 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 10c9fc9..8e420e9 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 49f8bee..82070ff 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 d4b8f32..0114d1e 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 0669cf6..786d89e 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 d9b75b7..ae16321 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 2f41bc0..cacf9d7 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 84fb760..413bcd6 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 edca21b..a1762c8 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 9ff62a9..409e221 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 d0d1d3d..73250e3 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 3558caf..55c6988 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 395a7f0..ffba2f9 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 b4b7431..05e9f65 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 3f1771b..a6c8492 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 aa6c5e3..80a40ac 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 6b0d9b5..4438e29 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 d01e76f..8bb6958 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 667e467..80fbf12 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 7844416..9f56e2e 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 5882b43..83d24d2 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 a5398e7..1446197 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 037b58a..ef33478 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 4003e79..fa5aced 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 449b03e..feafc63 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 404e684..7652a79 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 39404e7..6150d2e 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 d7919e7..d8f6f43 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 aa9c5cc..751f9ad 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 8982120..d732064 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 8bbf6be..3e40681 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 cb5318f..39ab100 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 b5e8c63..8b10dd1 100644
--- a/cs/printer.cs
+++ b/cs/printer.cs
@@ -20,7 +20,9 @@ namespace Mal {
string delim, bool print_readably) {
List<string> strs = new List<string>();
foreach (KeyValuePair<string, MalVal> 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 dbc74af..10973aa 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 1e3866e..8c68336 100644
--- a/cs/step2_eval.cs
+++ b/cs/step2_eval.cs
@@ -45,7 +45,7 @@ namespace Mal {
static MalVal EVAL(MalVal orig_ast, Dictionary<string, MalVal> 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 9977811..6c4f2fe 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<string, MalVal> 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 ff8c3b9..aefe226 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 f1307f0..55d414a 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 c885922..a84e87c 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 c25498f..7b03152 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 ccc0242..cac1411 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 1d724d1..bea360c 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 f51f824..2e8fc12 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 c2f46c9..b96a9f4 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 f4efd32..0ff76a5 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 17e92f0..2acca69 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 4d20857..584c9a7 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 428958e..1b8e9fe 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 6fbe311..9cacb8b 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 0d975ba..0c45bf2 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 f63017b..d90a720 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 9e176bb..8c6ff37 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 748d158..701ac47 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 999129e..d93f620 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 3264159..86db33b 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 322ee36..18f3c9c 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 322ee36..808022a 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 613bfb5..54e4176 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 7af2f72..facaeb1 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 8a1913e..711a9ee 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 73dfca3..fe3c5c4 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<String> strs = new ArrayList<String>();
for (Map.Entry<String, MalVal> 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 6bae506..7c9d3aa 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 a88dc13..d3e221b 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 ce9043d..ff15709 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 ef56083..43c87b7 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 56bcdf7..19c4c1c 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 8c3766a..6d015b4 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 4c89356..38a4aef 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 c3b0e9f..ceeff27 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 75c0402..1fa1125 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 7ad419a..a8a2dfa 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 3ab2117..d2be63b 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 3c9eac8..421b220 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 f3836e0..4f267e7 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 3f2f6ca..dd4de9a 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 1f5efb7..ca8f818 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 27715fa..937d0ea 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 659ac40..03de2cc 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 4c8ed17..813c66d 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 6259672..b39ebbd 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 f51592b..397379e 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 ff02f72..6be4474 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 0955b7f..456c006 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 de90d54..848a484 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<arguments.length; i+=2) {
var ktoken = arguments[i],
vtoken = arguments[i+1];
- // TODO: support more than string keys
- //if (list_Q(ktoken) && hash_map_Q(ktoken)) {
- // throw new Error("expected hash-map key atom, got collection");
- //}
if (typeof ktoken !== "string") {
throw new Error("expected hash-map key string, got: " + (typeof ktoken));
}
@@ -193,6 +197,8 @@ exports._true_Q = types._true_Q = _true_Q;
exports._false_Q = types._false_Q = _false_Q;
exports._symbol = types._symbol = _symbol;
exports._symbol_Q = types._symbol_Q = _symbol_Q;
+exports._keyword = types._keyword = _keyword;
+exports._keyword_Q = types._keyword_Q = _keyword_Q;
exports._function = types._function = _function;
exports._function_Q = types._function_Q = _function_Q;
exports._list = types._list = _list;
diff --git a/make/core.mk b/make/core.mk
index dc55ed5..0bb17e6 100644
--- a/make/core.mk
+++ b/make/core.mk
@@ -32,8 +32,13 @@ false? = $(if $(call _false?,$(1)),$(__true),$(__false))
# Symbol functions
+symbol = $(call _symbol,$(call str_decode,$($(1)_value)))
symbol? = $(if $(call _symbol?,$(1)),$(__true),$(__false))
+# Keyword functions
+keyword = $(call _keyword,$(call str_decode,$($(1)_value)))
+keyword? = $(if $(call _keyword?,$(1)),$(__true),$(__false))
+
# Number functions
number? = $(if $(call _number?,$(1)),$(__true),$(__false))
@@ -98,10 +103,9 @@ assoc = $(word 1,\
dissoc = $(word 1,\
$(foreach hm,$(call _clone_obj,$(word 1,$(1))),\
$(hm) \
- $(foreach key,$(wordlist 2,$(words $(1)),$(1)),\
- $(call _dissoc!,$(hm),$(call str_decode,$($(key)_value))))))
+ $(call _dissoc_seq!,$(hm),$(wordlist 2,$(words $(1)),$(1)))))
-keys = $(foreach new_list,$(call _list),$(new_list)$(eval $(new_list)_value := $(foreach v,$(call __get_obj_values,$(1)),$(call _string,$(word 4,$(subst _, ,$(v)))))))
+keys = $(foreach new_list,$(call _list),$(new_list)$(eval $(new_list)_value := $(foreach v,$(call __get_obj_values,$(1)),$(foreach vval,$(word 4,$(subst _, ,$(v))),$(if $(filter $(__keyword)%,$(vval)),$(call _keyword,$(patsubst $(__keyword)%,%,$(vval))),$(call _string,$(vval)))))))
vals = $(foreach new_list,$(call _list),$(new_list)$(eval $(new_list)_value := $(foreach v,$(call __get_obj_values,$(1)),$($(v)))))
@@ -127,7 +131,10 @@ cons = $(word 1,$(foreach new_list,$(call _list),$(new_list) $(eval $(new_list)_
concat = $(word 1,$(foreach new_list,$(call _list),$(new_list) $(eval $(new_list)_value := $(strip $(foreach lst,$1,$(call __get_obj_values,$(lst)))))))
-nth = $(word $(call gmsl_plus,1,$(call int_decode,$($(word 2,$(1))_value))),$($(word 1,$(1))_value))
+nth = $(strip \
+ $(if $(call int_lt,$($(word 2,$(1))_value),$(call int_encode,$(call _count,$(word 1,$(1))))),\
+ $(word $(call gmsl_plus,1,$(call int_decode,$($(word 2,$(1))_value))),$($(word 1,$(1))_value)),\
+ $(call _error,nth: index out of range)))
sfirst = $(word 1,$($(1)_value))
@@ -209,8 +216,10 @@ core_ns = type obj_type \
nil? nil? \
true? true? \
false? false? \
- symbol _symbol \
+ symbol symbol \
symbol? symbol? \
+ keyword keyword \
+ keyword? keyword? \
function? function? \
string? string? \
\
diff --git a/make/printer.mk b/make/printer.mk
index a1f2559..10b9789 100644
--- a/make/printer.mk
+++ b/make/printer.mk
@@ -30,7 +30,9 @@ number_pr_str = $(call int_decode,$($(1)_value))
symbol_pr_str = $($(1)_value)
-string_pr_str = $(if $(2),"$(subst $(DQUOTE),$(ESC_DQUOTE),$(subst $(SLASH),$(SLASH)$(SLASH),$(call str_decode,$($(1)_value))))",$(call str_decode,$($(1)_value)))
+keyword_pr_str = $(COLON)$(patsubst $(__keyword)%,%,$(call str_decode,$($(1)_value)))
+
+string_pr_str = $(if $(filter $(__keyword)%,$(call str_decode,$($(1)_value))),$(COLON)$(patsubst $(__keyword)%,%,$(call str_decode,$($(1)_value))),$(if $(2),"$(subst $(DQUOTE),$(ESC_DQUOTE),$(subst $(SLASH),$(SLASH)$(SLASH),$(call str_decode,$($(1)_value))))",$(call str_decode,$($(1)_value))))
function_pr_str = <$(if $(word 6,$(value $(1)_value)),$(wordlist 1,5,$(value $(1)_value))...,$(value $(1)_value))>
@@ -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 8d04596..8571785 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 b548874..ddcb70f 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 47f7fe4..22d1d21 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 f0298a9..b05f723 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 df3745f..2af0248 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 96e5d31..172e64d 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 4c5b8c1..1474302 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 050366c..80909c7 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 b971fb1..02588f7 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 43923fc..eff258a 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 a12c7e4..a6b6bb9 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 eeee77e..7d70278 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 372eecd..8012565 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 9ce6707..7b00b1e 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 cd4c565..501f992 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 f0710b1..0629f39 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 0000000..d8a9d9f
--- /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 8288336..26c7bbf 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 c3759a5..858a385 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 f63443d..1c34ab6 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 abf0c67..64ad314 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 60dc13a..53255c2 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 a95197a..48b835f 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 5ce9199..89133a2 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 d95e032..4e4a48d 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 5862ef1..ec823bc 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 0605d57..7993635 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 356f8c6..e2d919c 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 4cb4667..96129b1 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 61bedaf..a660d3b 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 3839931..130d31b 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 83e0cff..ed9063f 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 6585fe7..a31c298 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 1e88c6a..7266261 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 88557b5..a3785cf 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 1e83c28..a27acb8 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 07d3d2a..37903d0 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 aacf33e..c7e0175 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 9343dea..0470763 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 f38656f..8c67c66 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 e3df3ac..fa87197 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 191e5c3..52c9b05 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 3062e2d..52d6c1e 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 f1f63f6..4b268c0 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 82be9c2..1f6903e 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 d10047d..4a64594 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 e6bd70d..5fb9bba 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 65bf256..78a2fcf 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 13b1f7b..71ad3d6 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 4565011..86dc176 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 e99cfcf..39b9dd6 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 cbb92c9..da338d4 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 9d84d1f..5d10b46 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 de19c6d..7c97c23 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 80ca239..da863c1 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 65da08c..e01f42c 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 723f0ed..93cdb2e 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 74c8f6d..dab36e3 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 e684b03..71deef7 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 780dd4d..7f20288 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 1f749a5..92d1357 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 6b127ba..d55100c 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 cfcd064..37d338a 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 1039105..eb9ae7b 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 e751f88..2bc3c39 100644
--- a/rust/src/core.rs
+++ b/rust/src/core.rs
@@ -281,7 +281,7 @@ pub fn nth(a:Vec<MalVal>) -> 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<MalVal>) -> 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<String,MalVal> {
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 cf8aa0f..e9af154 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<Env> {
- 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<Env> {
+ 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 af496ff..d7b2b4c 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 cb80947..b2d49cd 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<MalVal> = 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<MalVal>) -> 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 1a8d271..92abf92 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<MalVal> = 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<String,MalError> {
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 0fdc597..9223cbf 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<MalVal> = 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<String,MalError> {
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 e1198bb..1e87116 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<MalVal> = 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<String,MalError> {
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::<Vec<MalVal>>();
- 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 a46f548..b113920 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<MalVal> = 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<String,MalError> {
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::<Vec<MalVal>>();
- 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 fbbaf35..7450de8 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<MalVal> = 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<String,MalError> {
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::<Vec<MalVal>>();
- 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 4449661..0f6bd88 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<MalVal> = 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<String,MalError> {
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::<Vec<MalVal>>();
- 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 4449661..8e30867 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<MalVal> = 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<String,MalError> {
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::<Vec<MalVal>>();
- 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 0b59716..141c3db 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<MalVal>) -> 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<MalVal>) -> MalRet {
if a.len() != 1 {
return err_str("Wrong arity to symbol? call");
@@ -215,6 +232,35 @@ pub fn symbol_q(a:Vec<MalVal>) -> MalRet {
}
}
+// Keywords
+pub fn _keyword(a: Vec<MalVal>) -> 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<MalVal>) -> 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 3dc4023..57b2e34 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 eb35592..2f48c73 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 ee30ea3..46efeae 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 8773ba8..cf8f5d1 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 aee7908..3781e4d 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 0000000..0f24bc0
--- /dev/null
+++ b/tests/test.txt
@@ -0,0 +1 @@
+A line of text
diff --git a/vb/core.vb b/vb/core.vb
index 43fc30f..771d0cc 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 3095344..a2c4628 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 212b89c..3f3e6e2 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 121aac2..9d4e03d 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 5793fdb..dfee614 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 4cf8721..470ae86 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 3c1f51c..bb36b22 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 58f8cd2..9ea0e9f 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 3f2d614..f38741e 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 1a7f211..400dbe7 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 5bf600d..d4a7af4 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 01702fa..f9af038 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 303a02e..4da75bd 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("""", "\""") _