aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Martin <github@martintribe.org>2014-04-23 21:59:50 -0500
committerJoel Martin <github@martintribe.org>2014-04-23 21:59:50 -0500
commit6301e0b6374cecc5599665be14d6ddc6a31ce1e8 (patch)
treedbf1dc2ff6c682fd87c72a7907e7f6e59c8d4c03
parent89bd4de1e2704c1bc562788b2c5e4fc08b71a538 (diff)
downloadmal-6301e0b6374cecc5599665be14d6ddc6a31ce1e8.tar.gz
mal-6301e0b6374cecc5599665be14d6ddc6a31ce1e8.zip
All: TCO let* and quasiquote.
-rw-r--r--Makefile2
-rw-r--r--README.md3
-rwxr-xr-xbash/step5_tco.sh6
-rwxr-xr-xbash/step6_file.sh6
-rwxr-xr-xbash/step7_quote.sh11
-rwxr-xr-xbash/step8_macros.sh11
-rwxr-xr-xbash/step9_interop.sh11
-rwxr-xr-xbash/stepA_more.sh11
-rw-r--r--c/step5_tco.c4
-rw-r--r--c/step6_file.c4
-rw-r--r--c/step7_quote.c7
-rw-r--r--c/step8_macros.c7
-rw-r--r--c/step9_interop.c7
-rw-r--r--c/stepA_more.c7
-rw-r--r--clojure/src/step5_tco.clj2
-rw-r--r--clojure/src/step6_file.clj2
-rw-r--r--clojure/src/step7_quote.clj4
-rw-r--r--clojure/src/step8_macros.clj4
-rw-r--r--clojure/src/step9_interop.clj4
-rw-r--r--clojure/src/stepA_more.clj4
-rw-r--r--cs/step5_tco.cs4
-rw-r--r--cs/step6_file.cs4
-rw-r--r--cs/step7_quote.cs7
-rw-r--r--cs/step8_macros.cs7
-rw-r--r--cs/stepA_more.cs7
-rw-r--r--docs/TODO8
-rw-r--r--java/src/main/java/mal/step5_tco.java4
-rw-r--r--java/src/main/java/mal/step6_file.java4
-rw-r--r--java/src/main/java/mal/step7_quote.java7
-rw-r--r--java/src/main/java/mal/step8_macros.java7
-rw-r--r--java/src/main/java/mal/stepA_more.java7
-rw-r--r--js/core.js2
-rw-r--r--js/step5_tco.js4
-rw-r--r--js/step6_file.js4
-rw-r--r--js/step7_quote.js7
-rw-r--r--js/step8_macros.js7
-rw-r--r--js/step9_interop.js7
-rw-r--r--js/stepA_more.js7
-rw-r--r--js/types.js3
-rw-r--r--perl/step5_tco.pl4
-rw-r--r--perl/step6_file.pl4
-rw-r--r--perl/step7_quote.pl7
-rw-r--r--perl/step8_macros.pl7
-rw-r--r--perl/step9_interop.pl7
-rw-r--r--perl/stepA_more.pl7
-rw-r--r--php/step5_tco.php9
-rw-r--r--php/step6_file.php9
-rw-r--r--php/step7_quote.php12
-rw-r--r--php/step8_macros.php12
-rw-r--r--php/step9_interop.php12
-rw-r--r--php/stepA_more.php12
-rw-r--r--ps/step5_tco.ps4
-rw-r--r--ps/step6_file.ps4
-rw-r--r--ps/step7_quote.ps8
-rw-r--r--ps/step8_macros.ps8
-rw-r--r--ps/step9_interop.ps8
-rw-r--r--ps/stepA_more.ps8
-rw-r--r--python/step5_tco.py4
-rw-r--r--python/step6_file.py4
-rw-r--r--python/step7_quote.py7
-rw-r--r--python/step8_macros.py7
-rw-r--r--python/step9_interop.py7
-rw-r--r--python/stepA_more.py7
-rw-r--r--ruby/step5_tco.rb11
-rw-r--r--ruby/step6_file.rb11
-rw-r--r--ruby/step7_quote.rb13
-rw-r--r--ruby/step8_macros.rb13
-rw-r--r--ruby/step9_interop.rb13
-rw-r--r--ruby/stepA_more.rb13
-rw-r--r--tests/stepA_more.mal8
70 files changed, 326 insertions, 158 deletions
diff --git a/Makefile b/Makefile
index 394c288..e264235 100644
--- a/Makefile
+++ b/Makefile
@@ -64,7 +64,7 @@ ruby_RUNSTEP = ruby ../$(2) $(3)
# Extra options to pass to runtest.py
cs_TEST_OPTS = --redirect
-mal_TEST_OPTS = --start-timeout 30 --test-timeout 120
+mal_TEST_OPTS = --start-timeout 60 --test-timeout 120
# Derived lists
diff --git a/README.md b/README.md
index 2688f21..ce50b10 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
## Description
Mal is an interpreter for a subset of the Clojure programming
-language. Mal is implemented from scratch in 12 different languages:
+language. Mal is implemented from scratch in 13 different languages:
* Bash shell
* C
@@ -13,6 +13,7 @@ language. Mal is implemented from scratch in 12 different languages:
* Javascript
* GNU Make
* mal itself
+* Perl
* PHP
* Postscript
* Python
diff --git a/bash/step5_tco.sh b/bash/step5_tco.sh
index b57c1f7..ea5b72d 100755
--- a/bash/step5_tco.sh
+++ b/bash/step5_tco.sh
@@ -71,8 +71,10 @@ EVAL () {
ENV_SET "${let_env}" "${ANON["${let_pairs[${idx}]}"]}" "${r}"
idx=$(( idx + 2))
done
- EVAL "${a2}" "${let_env}"
- return ;;
+ ast="${a2}"
+ env="${let_env}"
+ # Continue loop
+ ;;
do) _count "${ast}"
_slice "${ast}" 1 $(( ${r} - 2 ))
EVAL_AST "${r}" "${env}"
diff --git a/bash/step6_file.sh b/bash/step6_file.sh
index 5f26335..168698a 100755
--- a/bash/step6_file.sh
+++ b/bash/step6_file.sh
@@ -71,8 +71,10 @@ EVAL () {
ENV_SET "${let_env}" "${ANON["${let_pairs[${idx}]}"]}" "${r}"
idx=$(( idx + 2))
done
- EVAL "${a2}" "${let_env}"
- return ;;
+ ast="${a2}"
+ env="${let_env}"
+ # Continue loop
+ ;;
do) _count "${ast}"
_slice "${ast}" 1 $(( ${r} - 2 ))
EVAL_AST "${r}" "${env}"
diff --git a/bash/step7_quote.sh b/bash/step7_quote.sh
index f1a91ca..8319f64 100755
--- a/bash/step7_quote.sh
+++ b/bash/step7_quote.sh
@@ -109,15 +109,18 @@ EVAL () {
ENV_SET "${let_env}" "${ANON["${let_pairs[${idx}]}"]}" "${r}"
idx=$(( idx + 2))
done
- EVAL "${a2}" "${let_env}"
- return ;;
+ ast="${a2}"
+ env="${let_env}"
+ # Continue loop
+ ;;
quote)
r="${a1}"
return ;;
quasiquote)
QUASIQUOTE "${a1}"
- EVAL "${r}" "${env}"
- return ;;
+ ast="${r}"
+ # Continue loop
+ ;;
do) _count "${ast}"
_slice "${ast}" 1 $(( ${r} - 2 ))
EVAL_AST "${r}" "${env}"
diff --git a/bash/step8_macros.sh b/bash/step8_macros.sh
index 7826bf2..b21c25f 100755
--- a/bash/step8_macros.sh
+++ b/bash/step8_macros.sh
@@ -136,15 +136,18 @@ EVAL () {
ENV_SET "${let_env}" "${ANON["${let_pairs[${idx}]}"]}" "${r}"
idx=$(( idx + 2))
done
- EVAL "${a2}" "${let_env}"
- return ;;
+ ast="${a2}"
+ env="${let_env}"
+ # Continue loop
+ ;;
quote)
r="${a1}"
return ;;
quasiquote)
QUASIQUOTE "${a1}"
- EVAL "${r}" "${env}"
- return ;;
+ ast="${r}"
+ # Continue loop
+ ;;
defmacro!)
local k="${ANON["${a1}"]}"
EVAL "${a2}" "${env}"
diff --git a/bash/step9_interop.sh b/bash/step9_interop.sh
index 04d3413..5e4828f 100755
--- a/bash/step9_interop.sh
+++ b/bash/step9_interop.sh
@@ -136,15 +136,18 @@ EVAL () {
ENV_SET "${let_env}" "${ANON["${let_pairs[${idx}]}"]}" "${r}"
idx=$(( idx + 2))
done
- EVAL "${a2}" "${let_env}"
- return ;;
+ ast="${a2}"
+ env="${let_env}"
+ # Continue loop
+ ;;
quote)
r="${a1}"
return ;;
quasiquote)
QUASIQUOTE "${a1}"
- EVAL "${r}" "${env}"
- return ;;
+ ast="${r}"
+ # Continue loop
+ ;;
defmacro!)
local k="${ANON["${a1}"]}"
EVAL "${a2}" "${env}"
diff --git a/bash/stepA_more.sh b/bash/stepA_more.sh
index 88f2d4f..7f582c3 100755
--- a/bash/stepA_more.sh
+++ b/bash/stepA_more.sh
@@ -136,15 +136,18 @@ EVAL () {
ENV_SET "${let_env}" "${ANON["${let_pairs[${idx}]}"]}" "${r}"
idx=$(( idx + 2))
done
- EVAL "${a2}" "${let_env}"
- return ;;
+ ast="${a2}"
+ env="${let_env}"
+ # Continue loop
+ ;;
quote)
r="${a1}"
return ;;
quasiquote)
QUASIQUOTE "${a1}"
- EVAL "${r}" "${env}"
- return ;;
+ ast="${r}"
+ # Continue loop
+ ;;
defmacro!)
local k="${ANON["${a1}"]}"
EVAL "${a2}" "${env}"
diff --git a/c/step5_tco.c b/c/step5_tco.c
index 3a46bd2..edca21b 100644
--- a/c/step5_tco.c
+++ b/c/step5_tco.c
@@ -101,7 +101,9 @@ MalVal *EVAL(MalVal *ast, Env *env) {
assert_type(key, MAL_SYMBOL, "let* bind to non-symbol");
env_set(let_env, key->val.string, EVAL(val, let_env));
}
- return EVAL(a2, let_env);
+ ast = a2;
+ env = let_env;
+ // Continue loop
} else if ((a0->type & MAL_SYMBOL) &&
strcmp("do", a0->val.string) == 0) {
//g_print("eval apply do\n");
diff --git a/c/step6_file.c b/c/step6_file.c
index d73acfe..9ff62a9 100644
--- a/c/step6_file.c
+++ b/c/step6_file.c
@@ -101,7 +101,9 @@ MalVal *EVAL(MalVal *ast, Env *env) {
assert_type(key, MAL_SYMBOL, "let* bind to non-symbol");
env_set(let_env, key->val.string, EVAL(val, let_env));
}
- return EVAL(a2, let_env);
+ ast = a2;
+ env = let_env;
+ // Continue loop
} else if ((a0->type & MAL_SYMBOL) &&
strcmp("do", a0->val.string) == 0) {
//g_print("eval apply do\n");
diff --git a/c/step7_quote.c b/c/step7_quote.c
index b66bde6..d0d1d3d 100644
--- a/c/step7_quote.c
+++ b/c/step7_quote.c
@@ -128,7 +128,9 @@ MalVal *EVAL(MalVal *ast, Env *env) {
assert_type(key, MAL_SYMBOL, "let* bind to non-symbol");
env_set(let_env, key->val.string, EVAL(val, let_env));
}
- return EVAL(a2, let_env);
+ ast = a2;
+ env = let_env;
+ // Continue loop
} else if ((a0->type & MAL_SYMBOL) &&
strcmp("quote", a0->val.string) == 0) {
//g_print("eval apply quote\n");
@@ -137,7 +139,8 @@ MalVal *EVAL(MalVal *ast, Env *env) {
strcmp("quasiquote", a0->val.string) == 0) {
//g_print("eval apply quasiquote\n");
MalVal *a1 = _nth(ast, 1);
- return EVAL(quasiquote(a1), env);
+ ast = quasiquote(a1);
+ // Continue loop
} else if ((a0->type & MAL_SYMBOL) &&
strcmp("do", a0->val.string) == 0) {
//g_print("eval apply do\n");
diff --git a/c/step8_macros.c b/c/step8_macros.c
index e7aebf2..3558caf 100644
--- a/c/step8_macros.c
+++ b/c/step8_macros.c
@@ -152,7 +152,9 @@ MalVal *EVAL(MalVal *ast, Env *env) {
assert_type(key, MAL_SYMBOL, "let* bind to non-symbol");
env_set(let_env, key->val.string, EVAL(val, let_env));
}
- return EVAL(a2, let_env);
+ ast = a2;
+ env = let_env;
+ // Continue loop
} else if ((a0->type & MAL_SYMBOL) &&
strcmp("quote", a0->val.string) == 0) {
//g_print("eval apply quote\n");
@@ -161,7 +163,8 @@ MalVal *EVAL(MalVal *ast, Env *env) {
strcmp("quasiquote", a0->val.string) == 0) {
//g_print("eval apply quasiquote\n");
MalVal *a1 = _nth(ast, 1);
- return EVAL(quasiquote(a1), env);
+ ast = quasiquote(a1);
+ // Continue loop
} else if ((a0->type & MAL_SYMBOL) &&
strcmp("defmacro!", a0->val.string) == 0) {
//g_print("eval apply defmacro!\n");
diff --git a/c/step9_interop.c b/c/step9_interop.c
index f248c55..6ba594e 100644
--- a/c/step9_interop.c
+++ b/c/step9_interop.c
@@ -153,7 +153,9 @@ MalVal *EVAL(MalVal *ast, Env *env) {
assert_type(key, MAL_SYMBOL, "let* bind to non-symbol");
env_set(let_env, key->val.string, EVAL(val, let_env));
}
- return EVAL(a2, let_env);
+ ast = a2;
+ env = let_env;
+ // Continue loop
} else if ((a0->type & MAL_SYMBOL) &&
strcmp("quote", a0->val.string) == 0) {
//g_print("eval apply quote\n");
@@ -162,7 +164,8 @@ MalVal *EVAL(MalVal *ast, Env *env) {
strcmp("quasiquote", a0->val.string) == 0) {
//g_print("eval apply quasiquote\n");
MalVal *a1 = _nth(ast, 1);
- return EVAL(quasiquote(a1), env);
+ ast = quasiquote(a1);
+ // Continue loop
} else if ((a0->type & MAL_SYMBOL) &&
strcmp("defmacro!", a0->val.string) == 0) {
//g_print("eval apply defmacro!\n");
diff --git a/c/stepA_more.c b/c/stepA_more.c
index de49568..b4b7431 100644
--- a/c/stepA_more.c
+++ b/c/stepA_more.c
@@ -153,7 +153,9 @@ MalVal *EVAL(MalVal *ast, Env *env) {
assert_type(key, MAL_SYMBOL, "let* bind to non-symbol");
env_set(let_env, key->val.string, EVAL(val, let_env));
}
- return EVAL(a2, let_env);
+ ast = a2;
+ env = let_env;
+ // Continue loop
} else if ((a0->type & MAL_SYMBOL) &&
strcmp("quote", a0->val.string) == 0) {
//g_print("eval apply quote\n");
@@ -162,7 +164,8 @@ MalVal *EVAL(MalVal *ast, Env *env) {
strcmp("quasiquote", a0->val.string) == 0) {
//g_print("eval apply quasiquote\n");
MalVal *a1 = _nth(ast, 1);
- return EVAL(quasiquote(a1), env);
+ ast = quasiquote(a1);
+ // Continue loop
} else if ((a0->type & MAL_SYMBOL) &&
strcmp("defmacro!", a0->val.string) == 0) {
//g_print("eval apply defmacro!\n");
diff --git a/clojure/src/step5_tco.clj b/clojure/src/step5_tco.clj
index da47669..ed19b8e 100644
--- a/clojure/src/step5_tco.clj
+++ b/clojure/src/step5_tco.clj
@@ -43,7 +43,7 @@
(let [let-env (env/env env)]
(doseq [[b e] (partition 2 a1)]
(env/env-set let-env b (EVAL e let-env)))
- (EVAL a2 let-env))
+ (recur a2 let-env))
'do
(do (eval-ast (->> ast (drop-last) (drop 1)) env)
diff --git a/clojure/src/step6_file.clj b/clojure/src/step6_file.clj
index 5d080b9..2d5e05d 100644
--- a/clojure/src/step6_file.clj
+++ b/clojure/src/step6_file.clj
@@ -43,7 +43,7 @@
(let [let-env (env/env env)]
(doseq [[b e] (partition 2 a1)]
(env/env-set let-env b (EVAL e let-env)))
- (EVAL a2 let-env))
+ (recur a2 let-env))
'do
(do (eval-ast (->> ast (drop-last) (drop 1)) env)
diff --git a/clojure/src/step7_quote.clj b/clojure/src/step7_quote.clj
index ffcca24..633a81c 100644
--- a/clojure/src/step7_quote.clj
+++ b/clojure/src/step7_quote.clj
@@ -60,13 +60,13 @@
(let [let-env (env/env env)]
(doseq [[b e] (partition 2 a1)]
(env/env-set let-env b (EVAL e let-env)))
- (EVAL a2 let-env))
+ (recur a2 let-env))
'quote
a1
'quasiquote
- (EVAL (quasiquote a1) env)
+ (recur (quasiquote a1) env)
'do
(do (eval-ast (->> ast (drop-last) (drop 1)) env)
diff --git a/clojure/src/step8_macros.clj b/clojure/src/step8_macros.clj
index 6f51415..b8b55dc 100644
--- a/clojure/src/step8_macros.clj
+++ b/clojure/src/step8_macros.clj
@@ -78,13 +78,13 @@
(let [let-env (env/env env)]
(doseq [[b e] (partition 2 a1)]
(env/env-set let-env b (EVAL e let-env)))
- (EVAL a2 let-env))
+ (recur a2 let-env))
'quote
a1
'quasiquote
- (EVAL (quasiquote a1) env)
+ (recur (quasiquote a1) env)
'defmacro!
(let [func (with-meta (EVAL a2 env)
diff --git a/clojure/src/step9_interop.clj b/clojure/src/step9_interop.clj
index 677599c..c4d67e5 100644
--- a/clojure/src/step9_interop.clj
+++ b/clojure/src/step9_interop.clj
@@ -78,13 +78,13 @@
(let [let-env (env/env env)]
(doseq [[b e] (partition 2 a1)]
(env/env-set let-env b (EVAL e let-env)))
- (EVAL a2 let-env))
+ (recur a2 let-env))
'quote
a1
'quasiquote
- (EVAL (quasiquote a1) env)
+ (recur (quasiquote a1) env)
'defmacro!
(let [func (with-meta (EVAL a2 env)
diff --git a/clojure/src/stepA_more.clj b/clojure/src/stepA_more.clj
index e233089..fc7451f 100644
--- a/clojure/src/stepA_more.clj
+++ b/clojure/src/stepA_more.clj
@@ -78,13 +78,13 @@
(let [let-env (env/env env)]
(doseq [[b e] (partition 2 a1)]
(env/env-set let-env b (EVAL e let-env)))
- (EVAL a2 let-env))
+ (recur a2 let-env))
'quote
a1
'quasiquote
- (EVAL (quasiquote a1) env)
+ (recur (quasiquote a1) env)
'defmacro!
(let [func (with-meta (EVAL a2 env)
diff --git a/cs/step5_tco.cs b/cs/step5_tco.cs
index 95df860..e243153 100644
--- a/cs/step5_tco.cs
+++ b/cs/step5_tco.cs
@@ -81,7 +81,9 @@ namespace Mal {
val = ((MalList)a1)[i+1];
let_env.set(key.getName(), EVAL(val, let_env));
}
- return EVAL(a2, let_env);
+ orig_ast = a2;
+ env = let_env;
+ break;
case "do":
eval_ast(ast.slice(1, ast.size()-1), env);
orig_ast = ast[ast.size()-1];
diff --git a/cs/step6_file.cs b/cs/step6_file.cs
index db1a7b2..7e3bf7e 100644
--- a/cs/step6_file.cs
+++ b/cs/step6_file.cs
@@ -82,7 +82,9 @@ namespace Mal {
val = ((MalList)a1)[i+1];
let_env.set(key.getName(), EVAL(val, let_env));
}
- return EVAL(a2, let_env);
+ orig_ast = a2;
+ env = let_env;
+ break;
case "do":
eval_ast(ast.slice(1, ast.size()-1), env);
orig_ast = ast[ast.size()-1];
diff --git a/cs/step7_quote.cs b/cs/step7_quote.cs
index d803ac2..537c20c 100644
--- a/cs/step7_quote.cs
+++ b/cs/step7_quote.cs
@@ -109,11 +109,14 @@ namespace Mal {
val = ((MalList)a1)[i+1];
let_env.set(key.getName(), EVAL(val, let_env));
}
- return EVAL(a2, let_env);
+ orig_ast = a2;
+ env = let_env;
+ break;
case "quote":
return ast[1];
case "quasiquote":
- return EVAL(quasiquote(ast[1]), env);
+ orig_ast = quasiquote(ast[1]);
+ break;
case "do":
eval_ast(ast.slice(1, ast.size()-1), env);
orig_ast = ast[ast.size()-1];
diff --git a/cs/step8_macros.cs b/cs/step8_macros.cs
index af98fe9..b06b1fb 100644
--- a/cs/step8_macros.cs
+++ b/cs/step8_macros.cs
@@ -136,11 +136,14 @@ namespace Mal {
val = ((MalList)a1)[i+1];
let_env.set(key.getName(), EVAL(val, let_env));
}
- return EVAL(a2, let_env);
+ orig_ast = a2;
+ env = let_env;
+ break;
case "quote":
return ast[1];
case "quasiquote":
- return EVAL(quasiquote(ast[1]), env);
+ orig_ast = quasiquote(ast[1]);
+ break;
case "defmacro!":
a1 = ast[1];
a2 = ast[2];
diff --git a/cs/stepA_more.cs b/cs/stepA_more.cs
index 4fa8387..486c344 100644
--- a/cs/stepA_more.cs
+++ b/cs/stepA_more.cs
@@ -136,11 +136,14 @@ namespace Mal {
val = ((MalList)a1)[i+1];
let_env.set(key.getName(), EVAL(val, let_env));
}
- return EVAL(a2, let_env);
+ orig_ast = a2;
+ env = let_env;
+ break;
case "quote":
return ast[1];
case "quasiquote":
- return EVAL(quasiquote(ast[1]), env);
+ orig_ast = quasiquote(ast[1]);
+ break;
case "defmacro!":
a1 = ast[1];
a2 = ast[2];
diff --git a/docs/TODO b/docs/TODO
index 534adbc..0d5d28b 100644
--- a/docs/TODO
+++ b/docs/TODO
@@ -5,7 +5,6 @@ All:
- hash-map with space in key string (make)
- keyword type
- gensym reader inside quasiquote
- - quasiquote be TCO'd ?
- per impl tests for step5_tco, step9_interop (if possible)
- regular expression matching in runtest
@@ -14,15 +13,15 @@ All:
- Break out impl eval into step0.5
- Fix quasiquoting of vectors
- - TCO for let*
-
---------------------------------------------
Bash:
- explore using ${!prefix*} syntax (more like make impl)
+ - GC
C:
- come up with better way to do 20 vararg code
+ - GC
C#:
- step9_interop
@@ -44,7 +43,6 @@ Mal:
- step9_interop
Perl:
- - object exceptions: http://perldoc.perl.org/functions/die.html
PHP:
@@ -95,7 +93,7 @@ Future Implementations:
* Ruby
* C
- Objective-C
- - Perl
+ * Perl
* Shell (Bash 4)
- Tier 2
diff --git a/java/src/main/java/mal/step5_tco.java b/java/src/main/java/mal/step5_tco.java
index dd1dc89..aa3f7a7 100644
--- a/java/src/main/java/mal/step5_tco.java
+++ b/java/src/main/java/mal/step5_tco.java
@@ -80,7 +80,9 @@ public class step5_tco {
val = ((MalList)a1).nth(i+1);
let_env.set(key.getName(), EVAL(val, let_env));
}
- return EVAL(a2, let_env);
+ orig_ast = a2;
+ env = let_env;
+ break;
case "do":
eval_ast(ast.slice(1, ast.size()-1), env);
orig_ast = ast.nth(ast.size()-1);
diff --git a/java/src/main/java/mal/step6_file.java b/java/src/main/java/mal/step6_file.java
index 4bf0f0e..d3f4914 100644
--- a/java/src/main/java/mal/step6_file.java
+++ b/java/src/main/java/mal/step6_file.java
@@ -80,7 +80,9 @@ public class step6_file {
val = ((MalList)a1).nth(i+1);
let_env.set(key.getName(), EVAL(val, let_env));
}
- return EVAL(a2, let_env);
+ orig_ast = a2;
+ env = let_env;
+ break;
case "do":
eval_ast(ast.slice(1, ast.size()-1), env);
orig_ast = ast.nth(ast.size()-1);
diff --git a/java/src/main/java/mal/step7_quote.java b/java/src/main/java/mal/step7_quote.java
index da0781b..639f8b9 100644
--- a/java/src/main/java/mal/step7_quote.java
+++ b/java/src/main/java/mal/step7_quote.java
@@ -107,11 +107,14 @@ public class step7_quote {
val = ((MalList)a1).nth(i+1);
let_env.set(key.getName(), EVAL(val, let_env));
}
- return EVAL(a2, let_env);
+ orig_ast = a2;
+ env = let_env;
+ break;
case "quote":
return ast.nth(1);
case "quasiquote":
- return EVAL(quasiquote(ast.nth(1)), env);
+ orig_ast = quasiquote(ast.nth(1));
+ break;
case "do":
eval_ast(ast.slice(1, ast.size()-1), env);
orig_ast = ast.nth(ast.size()-1);
diff --git a/java/src/main/java/mal/step8_macros.java b/java/src/main/java/mal/step8_macros.java
index c4cb1de..fb3ffdc 100644
--- a/java/src/main/java/mal/step8_macros.java
+++ b/java/src/main/java/mal/step8_macros.java
@@ -135,11 +135,14 @@ public class step8_macros {
val = ((MalList)a1).nth(i+1);
let_env.set(key.getName(), EVAL(val, let_env));
}
- return EVAL(a2, let_env);
+ orig_ast = a2;
+ env = let_env;
+ break;
case "quote":
return ast.nth(1);
case "quasiquote":
- return EVAL(quasiquote(ast.nth(1)), env);
+ orig_ast = quasiquote(ast.nth(1));
+ break;
case "defmacro!":
a1 = ast.nth(1);
a2 = ast.nth(2);
diff --git a/java/src/main/java/mal/stepA_more.java b/java/src/main/java/mal/stepA_more.java
index 20dee3f..75ec301 100644
--- a/java/src/main/java/mal/stepA_more.java
+++ b/java/src/main/java/mal/stepA_more.java
@@ -137,11 +137,14 @@ public class stepA_more {
val = ((MalList)a1).nth(i+1);
let_env.set(key.getName(), EVAL(val, let_env));
}
- return EVAL(a2, let_env);
+ orig_ast = a2;
+ env = let_env;
+ break;
case "quote":
return ast.nth(1);
case "quasiquote":
- return EVAL(quasiquote(ast.nth(1)), env);
+ orig_ast = quasiquote(ast.nth(1));
+ break;
case "defmacro!":
a1 = ast.nth(1);
a2 = ast.nth(2);
diff --git a/js/core.js b/js/core.js
index f9e478e..b5a52e0 100644
--- a/js/core.js
+++ b/js/core.js
@@ -193,9 +193,9 @@ var ns = {'type': types._obj_type,
'rest': rest,
'empty?': empty_Q,
'count': count,
- 'conj': conj,
'apply': apply,
'map': map,
+ 'conj': conj,
'with-meta': with_meta,
'meta': meta,
diff --git a/js/step5_tco.js b/js/step5_tco.js
index 9335aa8..7640327 100644
--- a/js/step5_tco.js
+++ b/js/step5_tco.js
@@ -52,7 +52,9 @@ function _EVAL(ast, env) {
for (var i=0; i < a1.length; i+=2) {
let_env.set(a1[i].value, EVAL(a1[i+1], let_env));
}
- return EVAL(a2, let_env);
+ ast = a2;
+ env = let_env;
+ break;
case "do":
eval_ast(ast.slice(1, -1), env);
ast = ast[ast.length-1];
diff --git a/js/step6_file.js b/js/step6_file.js
index 85de49e..0dfe71e 100644
--- a/js/step6_file.js
+++ b/js/step6_file.js
@@ -52,7 +52,9 @@ function _EVAL(ast, env) {
for (var i=0; i < a1.length; i+=2) {
let_env.set(a1[i].value, EVAL(a1[i+1], let_env));
}
- return EVAL(a2, let_env);
+ ast = a2;
+ env = let_env;
+ break;
case "do":
eval_ast(ast.slice(1, -1), env);
ast = ast[ast.length-1];
diff --git a/js/step7_quote.js b/js/step7_quote.js
index 8676d07..067d68e 100644
--- a/js/step7_quote.js
+++ b/js/step7_quote.js
@@ -72,11 +72,14 @@ function _EVAL(ast, env) {
for (var i=0; i < a1.length; i+=2) {
let_env.set(a1[i].value, EVAL(a1[i+1], let_env));
}
- return EVAL(a2, let_env);
+ ast = a2;
+ env = let_env;
+ break;
case "quote":
return a1;
case "quasiquote":
- return EVAL(quasiquote(a1), env);
+ ast = quasiquote(a1);
+ break;
case "do":
eval_ast(ast.slice(1, -1), env);
ast = ast[ast.length-1];
diff --git a/js/step8_macros.js b/js/step8_macros.js
index dca7beb..2ddb56c 100644
--- a/js/step8_macros.js
+++ b/js/step8_macros.js
@@ -90,11 +90,14 @@ function _EVAL(ast, env) {
for (var i=0; i < a1.length; i+=2) {
let_env.set(a1[i].value, EVAL(a1[i+1], let_env));
}
- return EVAL(a2, let_env);
+ ast = a2;
+ env = let_env;
+ break;
case "quote":
return a1;
case "quasiquote":
- return EVAL(quasiquote(a1), env);
+ ast = quasiquote(a1);
+ break;
case 'defmacro!':
var func = EVAL(a2, env);
func._ismacro_ = true;
diff --git a/js/step9_interop.js b/js/step9_interop.js
index e95b4ca..3988da1 100644
--- a/js/step9_interop.js
+++ b/js/step9_interop.js
@@ -90,11 +90,14 @@ function _EVAL(ast, env) {
for (var i=0; i < a1.length; i+=2) {
let_env.set(a1[i].value, EVAL(a1[i+1], let_env));
}
- return EVAL(a2, let_env);
+ ast = a2;
+ env = let_env;
+ break;
case "quote":
return a1;
case "quasiquote":
- return EVAL(quasiquote(a1), env);
+ ast = quasiquote(a1);
+ break;
case 'defmacro!':
var func = EVAL(a2, env);
func._ismacro_ = true;
diff --git a/js/stepA_more.js b/js/stepA_more.js
index 58840eb..6058da1 100644
--- a/js/stepA_more.js
+++ b/js/stepA_more.js
@@ -90,11 +90,14 @@ function _EVAL(ast, env) {
for (var i=0; i < a1.length; i+=2) {
let_env.set(a1[i].value, EVAL(a1[i+1], let_env));
}
- return EVAL(a2, let_env);
+ ast = a2;
+ env = let_env;
+ break;
case "quote":
return a1;
case "quasiquote":
- return EVAL(quasiquote(a1), env);
+ ast = quasiquote(a1);
+ break;
case 'defmacro!':
var func = EVAL(a2, env);
func._ismacro_ = true;
diff --git a/js/types.js b/js/types.js
index 889e154..d288231 100644
--- a/js/types.js
+++ b/js/types.js
@@ -107,6 +107,7 @@ function _function(Eval, Env, ast, env, params) {
fn.__meta__ = null;
fn.__ast__ = ast;
fn.__gen_env__ = function(args) { return new Env(env, params, args); };
+ fn._ismacro_ = false;
return fn;
}
function _function_Q(obj) { return typeof obj == "function"; }
@@ -131,7 +132,7 @@ function _vector() {
v.__isvector__ = true;
return v;
}
-function _vector_Q(obj) { return Array.isArray(obj) && obj.__isvector__; }
+function _vector_Q(obj) { return Array.isArray(obj) && !!obj.__isvector__; }
diff --git a/perl/step5_tco.pl b/perl/step5_tco.pl
index b041110..44de718 100644
--- a/perl/step5_tco.pl
+++ b/perl/step5_tco.pl
@@ -68,10 +68,12 @@ sub EVAL {
}
$ast = $a2;
$env = $let_env;
+ # Continue loop (TCO)
}
when (/^do$/) {
eval_ast($ast->slice(1, $#{$ast->{val}}-1), $env);
$ast = $ast->nth($#{$ast->{val}});
+ # Continue loop (TCO)
}
when (/^if$/) {
my $cond = EVAL($a1, $env);
@@ -80,6 +82,7 @@ sub EVAL {
} else {
$ast = $a2;
}
+ # Continue loop (TCO)
}
when (/^fn\*$/) {
return Function->new(\&EVAL, $a2, $env, $a1);
@@ -90,6 +93,7 @@ sub EVAL {
if ((ref $f) =~ /^Function/) {
$ast = $f->{ast};
$env = $f->gen_env($el->rest());
+ # Continue loop (TCO)
} else {
return &{ $f }($el->rest());
}
diff --git a/perl/step6_file.pl b/perl/step6_file.pl
index a5f7791..9fcac1d 100644
--- a/perl/step6_file.pl
+++ b/perl/step6_file.pl
@@ -68,10 +68,12 @@ sub EVAL {
}
$ast = $a2;
$env = $let_env;
+ # Continue loop (TCO)
}
when (/^do$/) {
eval_ast($ast->slice(1, $#{$ast->{val}}-1), $env);
$ast = $ast->nth($#{$ast->{val}});
+ # Continue loop (TCO)
}
when (/^if$/) {
my $cond = EVAL($a1, $env);
@@ -80,6 +82,7 @@ sub EVAL {
} else {
$ast = $a2;
}
+ # Continue loop (TCO)
}
when (/^fn\*$/) {
return Function->new(\&EVAL, $a2, $env, $a1);
@@ -90,6 +93,7 @@ sub EVAL {
if ((ref $f) =~ /^Function/) {
$ast = $f->{ast};
$env = $f->gen_env($el->rest());
+ # Continue loop (TCO)
} else {
return &{ $f }($el->rest());
}
diff --git a/perl/step7_quote.pl b/perl/step7_quote.pl
index 9654cc8..19c0599 100644
--- a/perl/step7_quote.pl
+++ b/perl/step7_quote.pl
@@ -91,16 +91,19 @@ sub EVAL {
}
$ast = $a2;
$env = $let_env;
+ # Continue loop (TCO)
}
when (/^quote$/) {
return $a1;
}
when (/^quasiquote$/) {
- return EVAL(quasiquote($a1), $env);
+ $ast = quasiquote($a1);
+ # Continue loop (TCO)
}
when (/^do$/) {
eval_ast($ast->slice(1, $#{$ast->{val}}-1), $env);
$ast = $ast->nth($#{$ast->{val}});
+ # Continue loop (TCO)
}
when (/^if$/) {
my $cond = EVAL($a1, $env);
@@ -109,6 +112,7 @@ sub EVAL {
} else {
$ast = $a2;
}
+ # Continue loop (TCO)
}
when (/^fn\*$/) {
return Function->new(\&EVAL, $a2, $env, $a1);
@@ -119,6 +123,7 @@ sub EVAL {
if ((ref $f) =~ /^Function/) {
$ast = $f->{ast};
$env = $f->gen_env($el->rest());
+ # Continue loop (TCO)
} else {
return &{ $f }($el->rest());
}
diff --git a/perl/step8_macros.pl b/perl/step8_macros.pl
index 22f078f..47004a2 100644
--- a/perl/step8_macros.pl
+++ b/perl/step8_macros.pl
@@ -117,12 +117,14 @@ sub EVAL {
}
$ast = $a2;
$env = $let_env;
+ # Continue loop (TCO)
}
when (/^quote$/) {
return $a1;
}
when (/^quasiquote$/) {
- return EVAL(quasiquote($a1), $env);
+ $ast = quasiquote($a1);
+ # Continue loop (TCO)
}
when (/^defmacro!$/) {
my $func = EVAL($a2, $env);
@@ -135,6 +137,7 @@ sub EVAL {
when (/^do$/) {
eval_ast($ast->slice(1, $#{$ast->{val}}-1), $env);
$ast = $ast->nth($#{$ast->{val}});
+ # Continue loop (TCO)
}
when (/^if$/) {
my $cond = EVAL($a1, $env);
@@ -143,6 +146,7 @@ sub EVAL {
} else {
$ast = $a2;
}
+ # Continue loop (TCO)
}
when (/^fn\*$/) {
return Function->new(\&EVAL, $a2, $env, $a1);
@@ -153,6 +157,7 @@ sub EVAL {
if ((ref $f) =~ /^Function/) {
$ast = $f->{ast};
$env = $f->gen_env($el->rest());
+ # Continue loop (TCO)
} else {
return &{ $f }($el->rest());
}
diff --git a/perl/step9_interop.pl b/perl/step9_interop.pl
index 2c20e89..45dd4af 100644
--- a/perl/step9_interop.pl
+++ b/perl/step9_interop.pl
@@ -118,12 +118,14 @@ sub EVAL {
}
$ast = $a2;
$env = $let_env;
+ # Continue loop (TCO)
}
when (/^quote$/) {
return $a1;
}
when (/^quasiquote$/) {
- return EVAL(quasiquote($a1), $env);
+ $ast = quasiquote($a1);
+ # Continue loop (TCO)
}
when (/^defmacro!$/) {
my $func = EVAL($a2, $env);
@@ -139,6 +141,7 @@ sub EVAL {
when (/^do$/) {
eval_ast($ast->slice(1, $#{$ast->{val}}-1), $env);
$ast = $ast->nth($#{$ast->{val}});
+ # Continue loop (TCO)
}
when (/^if$/) {
my $cond = EVAL($a1, $env);
@@ -147,6 +150,7 @@ sub EVAL {
} else {
$ast = $a2;
}
+ # Continue loop (TCO)
}
when (/^fn\*$/) {
return Function->new(\&EVAL, $a2, $env, $a1);
@@ -157,6 +161,7 @@ sub EVAL {
if ((ref $f) =~ /^Function/) {
$ast = $f->{ast};
$env = $f->gen_env($el->rest());
+ # Continue loop (TCO)
} else {
return &{ $f }($el->rest());
}
diff --git a/perl/stepA_more.pl b/perl/stepA_more.pl
index 8520480..eca2b6e 100644
--- a/perl/stepA_more.pl
+++ b/perl/stepA_more.pl
@@ -118,12 +118,14 @@ sub EVAL {
}
$ast = $a2;
$env = $let_env;
+ # Continue loop (TCO)
}
when (/^quote$/) {
return $a1;
}
when (/^quasiquote$/) {
- return EVAL(quasiquote($a1), $env);
+ $ast = quasiquote($a1);
+ # Continue loop (TCO)
}
when (/^defmacro!$/) {
my $func = EVAL($a2, $env);
@@ -166,6 +168,7 @@ sub EVAL {
when (/^do$/) {
eval_ast($ast->slice(1, $#{$ast->{val}}-1), $env);
$ast = $ast->nth($#{$ast->{val}});
+ # Continue loop (TCO)
}
when (/^if$/) {
my $cond = EVAL($a1, $env);
@@ -174,6 +177,7 @@ sub EVAL {
} else {
$ast = $a2;
}
+ # Continue loop (TCO)
}
when (/^fn\*$/) {
return Function->new(\&EVAL, $a2, $env, $a1);
@@ -184,6 +188,7 @@ sub EVAL {
if ((ref $f) =~ /^Function/) {
$ast = $f->{ast};
$env = $f->gen_env($el->rest());
+ # Continue loop (TCO)
} else {
return &{ $f }($el->rest());
}
diff --git a/php/step5_tco.php b/php/step5_tco.php
index 5ae29a7..88557b5 100644
--- a/php/step5_tco.php
+++ b/php/step5_tco.php
@@ -56,11 +56,13 @@ function MAL_EVAL($ast, $env) {
for ($i=0; $i < count($a1); $i+=2) {
$let_env->set($a1[$i]->value, MAL_EVAL($a1[$i+1], $let_env));
}
- return MAL_EVAL($ast[2], $let_env);
+ $ast = $ast[2];
+ $env = $let_env;
+ break; // Continue loop (TCO)
case "do":
eval_ast($ast->slice(1, -1), $env);
$ast = $ast[count($ast)-1];
- break;
+ break; // Continue loop (TCO)
case "if":
$cond = MAL_EVAL($ast[1], $env);
if ($cond === NULL || $cond === false) {
@@ -69,7 +71,7 @@ function MAL_EVAL($ast, $env) {
} else {
$ast = $ast[2];
}
- break;
+ break; // Continue loop (TCO)
case "fn*":
return _function('MAL_EVAL', 'native',
$ast[2], $env, $ast[1]);
@@ -80,6 +82,7 @@ function MAL_EVAL($ast, $env) {
if ($f->type === 'native') {
$ast = $f->ast;
$env = $f->gen_env($args);
+ // Continue loop (TCO)
} else {
return $f->apply($args);
}
diff --git a/php/step6_file.php b/php/step6_file.php
index b7cdcc3..1e83c28 100644
--- a/php/step6_file.php
+++ b/php/step6_file.php
@@ -56,11 +56,13 @@ function MAL_EVAL($ast, $env) {
for ($i=0; $i < count($a1); $i+=2) {
$let_env->set($a1[$i]->value, MAL_EVAL($a1[$i+1], $let_env));
}
- return MAL_EVAL($ast[2], $let_env);
+ $ast = $ast[2];
+ $env = $let_env;
+ break; // Continue loop (TCO)
case "do":
eval_ast($ast->slice(1, -1), $env);
$ast = $ast[count($ast)-1];
- break;
+ break; // Continue loop (TCO)
case "if":
$cond = MAL_EVAL($ast[1], $env);
if ($cond === NULL || $cond === false) {
@@ -69,7 +71,7 @@ function MAL_EVAL($ast, $env) {
} else {
$ast = $ast[2];
}
- break;
+ break; // Continue loop (TCO)
case "fn*":
return _function('MAL_EVAL', 'native',
$ast[2], $env, $ast[1]);
@@ -80,6 +82,7 @@ function MAL_EVAL($ast, $env) {
if ($f->type === 'native') {
$ast = $f->ast;
$env = $f->gen_env($args);
+ // Continue loop (TCO)
} else {
return $f->apply($args);
}
diff --git a/php/step7_quote.php b/php/step7_quote.php
index fd1c922..07d3d2a 100644
--- a/php/step7_quote.php
+++ b/php/step7_quote.php
@@ -75,15 +75,18 @@ function MAL_EVAL($ast, $env) {
for ($i=0; $i < count($a1); $i+=2) {
$let_env->set($a1[$i]->value, MAL_EVAL($a1[$i+1], $let_env));
}
- return MAL_EVAL($ast[2], $let_env);
+ $ast = $ast[2];
+ $env = $let_env;
+ break; // Continue loop (TCO)
case "quote":
return $ast[1];
case "quasiquote":
- return MAL_EVAL(quasiquote($ast[1]), $env);
+ $ast = quasiquote($ast[1]);
+ break; // Continue loop (TCO)
case "do":
eval_ast($ast->slice(1, -1), $env);
$ast = $ast[count($ast)-1];
- break;
+ break; // Continue loop (TCO)
case "if":
$cond = MAL_EVAL($ast[1], $env);
if ($cond === NULL || $cond === false) {
@@ -92,7 +95,7 @@ function MAL_EVAL($ast, $env) {
} else {
$ast = $ast[2];
}
- break;
+ break; // Continue loop (TCO)
case "fn*":
return _function('MAL_EVAL', 'native',
$ast[2], $env, $ast[1]);
@@ -103,6 +106,7 @@ function MAL_EVAL($ast, $env) {
if ($f->type === 'native') {
$ast = $f->ast;
$env = $f->gen_env($args);
+ // Continue loop (TCO)
} else {
return $f->apply($args);
}
diff --git a/php/step8_macros.php b/php/step8_macros.php
index b33111d..aacf33e 100644
--- a/php/step8_macros.php
+++ b/php/step8_macros.php
@@ -94,11 +94,14 @@ function MAL_EVAL($ast, $env) {
for ($i=0; $i < count($a1); $i+=2) {
$let_env->set($a1[$i]->value, MAL_EVAL($a1[$i+1], $let_env));
}
- return MAL_EVAL($ast[2], $let_env);
+ $ast = $ast[2];
+ $env = $let_env;
+ break; // Continue loop (TCO)
case "quote":
return $ast[1];
case "quasiquote":
- return MAL_EVAL(quasiquote($ast[1]), $env);
+ $ast = quasiquote($ast[1]);
+ break; // Continue loop (TCO)
case "defmacro!":
$func = MAL_EVAL($ast[2], $env);
$func->ismacro = true;
@@ -108,7 +111,7 @@ function MAL_EVAL($ast, $env) {
case "do":
eval_ast($ast->slice(1, -1), $env);
$ast = $ast[count($ast)-1];
- break;
+ break; // Continue loop (TCO)
case "if":
$cond = MAL_EVAL($ast[1], $env);
if ($cond === NULL || $cond === false) {
@@ -117,7 +120,7 @@ function MAL_EVAL($ast, $env) {
} else {
$ast = $ast[2];
}
- break;
+ break; // Continue loop (TCO)
case "fn*":
return _function('MAL_EVAL', 'native',
$ast[2], $env, $ast[1]);
@@ -128,6 +131,7 @@ function MAL_EVAL($ast, $env) {
if ($f->type === 'native') {
$ast = $f->ast;
$env = $f->gen_env($args);
+ // Continue loop (TCO)
} else {
return $f->apply($args);
}
diff --git a/php/step9_interop.php b/php/step9_interop.php
index f35244c..d4a59c7 100644
--- a/php/step9_interop.php
+++ b/php/step9_interop.php
@@ -94,11 +94,14 @@ function MAL_EVAL($ast, $env) {
for ($i=0; $i < count($a1); $i+=2) {
$let_env->set($a1[$i]->value, MAL_EVAL($a1[$i+1], $let_env));
}
- return MAL_EVAL($ast[2], $let_env);
+ $ast = $ast[2];
+ $env = $let_env;
+ break; // Continue loop (TCO)
case "quote":
return $ast[1];
case "quasiquote":
- return MAL_EVAL(quasiquote($ast[1]), $env);
+ $ast = quasiquote($ast[1]);
+ break; // Continue loop (TCO)
case "defmacro!":
$func = MAL_EVAL($ast[2], $env);
$func->ismacro = true;
@@ -110,7 +113,7 @@ function MAL_EVAL($ast, $env) {
case "do":
eval_ast($ast->slice(1, -1), $env);
$ast = $ast[count($ast)-1];
- break;
+ break; // Continue loop (TCO)
case "if":
$cond = MAL_EVAL($ast[1], $env);
if ($cond === NULL || $cond === false) {
@@ -119,7 +122,7 @@ function MAL_EVAL($ast, $env) {
} else {
$ast = $ast[2];
}
- break;
+ break; // Continue loop (TCO)
case "fn*":
return _function('MAL_EVAL', 'native',
$ast[2], $env, $ast[1]);
@@ -130,6 +133,7 @@ function MAL_EVAL($ast, $env) {
if ($f->type === 'native') {
$ast = $f->ast;
$env = $f->gen_env($args);
+ // Continue loop (TCO)
} else {
return $f->apply($args);
}
diff --git a/php/stepA_more.php b/php/stepA_more.php
index 0f4c8ae..f38656f 100644
--- a/php/stepA_more.php
+++ b/php/stepA_more.php
@@ -94,11 +94,14 @@ function MAL_EVAL($ast, $env) {
for ($i=0; $i < count($a1); $i+=2) {
$let_env->set($a1[$i]->value, MAL_EVAL($a1[$i+1], $let_env));
}
- return MAL_EVAL($ast[2], $let_env);
+ $ast = $ast[2];
+ $env = $let_env;
+ break; // Continue loop (TCO)
case "quote":
return $ast[1];
case "quasiquote":
- return MAL_EVAL(quasiquote($ast[1]), $env);
+ $ast = quasiquote($ast[1]);
+ break; // Continue loop (TCO)
case "defmacro!":
$func = MAL_EVAL($ast[2], $env);
$func->ismacro = true;
@@ -128,7 +131,7 @@ function MAL_EVAL($ast, $env) {
case "do":
eval_ast($ast->slice(1, -1), $env);
$ast = $ast[count($ast)-1];
- break;
+ break; // Continue loop (TCO)
case "if":
$cond = MAL_EVAL($ast[1], $env);
if ($cond === NULL || $cond === false) {
@@ -137,7 +140,7 @@ function MAL_EVAL($ast, $env) {
} else {
$ast = $ast[2];
}
- break;
+ break; // Continue loop (TCO)
case "fn*":
return _function('MAL_EVAL', 'native',
$ast[2], $env, $ast[1]);
@@ -148,6 +151,7 @@ function MAL_EVAL($ast, $env) {
if ($f->type === 'native') {
$ast = $f->ast;
$env = $f->gen_env($args);
+ // Continue loop (TCO)
} else {
return $f->apply($args);
}
diff --git a/ps/step5_tco.ps b/ps/step5_tco.ps
index c1698d6..83fd43b 100644
--- a/ps/step5_tco.ps
+++ b/ps/step5_tco.ps
@@ -65,7 +65,9 @@ end } def
env_set
pop % discard the return value
} for
- a2 let_env EVAL
+ a2
+ let_env
+ /loop? true def % loop
}{ /do a0 eq { %if do
ast _count 2 gt { %if ast has more than 2 elements
ast 1 ast _count 2 sub _slice env eval_ast pop
diff --git a/ps/step6_file.ps b/ps/step6_file.ps
index 00598be..7d1c876 100644
--- a/ps/step6_file.ps
+++ b/ps/step6_file.ps
@@ -65,7 +65,9 @@ end } def
env_set
pop % discard the return value
} for
- a2 let_env EVAL
+ a2
+ let_env
+ /loop? true def % loop
}{ /do a0 eq { %if do
ast _count 2 gt { %if ast has more than 2 elements
ast 1 ast _count 2 sub _slice env eval_ast pop
diff --git a/ps/step7_quote.ps b/ps/step7_quote.ps
index 10c8089..d7340fd 100644
--- a/ps/step7_quote.ps
+++ b/ps/step7_quote.ps
@@ -93,11 +93,15 @@ end } def
env_set
pop % discard the return value
} for
- a2 let_env EVAL
+ a2
+ let_env
+ /loop? true def % loop
}{ /quote a0 eq { %if quote
ast 1 _nth
}{ /quasiquote a0 eq { %if quasiquote
- ast 1 _nth quasiquote env EVAL
+ ast 1 _nth quasiquote
+ env
+ /loop? true def % loop
}{ /do a0 eq { %if do
ast _count 2 gt { %if ast has more than 2 elements
ast 1 ast _count 2 sub _slice env eval_ast pop
diff --git a/ps/step8_macros.ps b/ps/step8_macros.ps
index 74cf50f..3bf304c 100644
--- a/ps/step8_macros.ps
+++ b/ps/step8_macros.ps
@@ -126,11 +126,15 @@ end } def
env_set
pop % discard the return value
} for
- a2 let_env EVAL
+ a2
+ let_env
+ /loop? true def % loop
}{ /quote a0 eq { %if quote
ast 1 _nth
}{ /quasiquote a0 eq { %if quasiquote
- ast 1 _nth quasiquote env EVAL
+ ast 1 _nth quasiquote
+ env
+ /loop? true def % loop
}{ /defmacro! a0 eq { %if defmacro!
/a1 ast 1 _nth def
/a2 ast 2 _nth def
diff --git a/ps/step9_interop.ps b/ps/step9_interop.ps
index 4f324d5..de3d2af 100644
--- a/ps/step9_interop.ps
+++ b/ps/step9_interop.ps
@@ -126,11 +126,15 @@ end } def
env_set
pop % discard the return value
} for
- a2 let_env EVAL
+ a2
+ let_env
+ /loop? true def % loop
}{ /quote a0 eq { %if quote
ast 1 _nth
}{ /quasiquote a0 eq { %if quasiquote
- ast 1 _nth quasiquote env EVAL
+ ast 1 _nth quasiquote
+ env
+ /loop? true def % loop
}{ /defmacro! a0 eq { %if defmacro!
/a1 ast 1 _nth def
/a2 ast 2 _nth def
diff --git a/ps/stepA_more.ps b/ps/stepA_more.ps
index 744a092..76d0a86 100644
--- a/ps/stepA_more.ps
+++ b/ps/stepA_more.ps
@@ -126,11 +126,15 @@ end } def
env_set
pop % discard the return value
} for
- a2 let_env EVAL
+ a2
+ let_env
+ /loop? true def % loop
}{ /quote a0 eq { %if quote
ast 1 _nth
}{ /quasiquote a0 eq { %if quasiquote
- ast 1 _nth quasiquote env EVAL
+ ast 1 _nth quasiquote
+ env
+ /loop? true def % loop
}{ /defmacro! a0 eq { %if defmacro!
/a1 ast 1 _nth def
/a2 ast 2 _nth def
diff --git a/python/step5_tco.py b/python/step5_tco.py
index 72457e9..cbb92c9 100644
--- a/python/step5_tco.py
+++ b/python/step5_tco.py
@@ -45,7 +45,9 @@ def EVAL(ast, env):
let_env = Env(env)
for i in range(0, len(a1), 2):
let_env.set(a1[i], EVAL(a1[i+1], let_env))
- return EVAL(a2, let_env)
+ ast = a2
+ env = let_env
+ # Continue loop (TCO)
elif "do" == a0:
eval_ast(ast[1:-1], env)
ast = ast[-1]
diff --git a/python/step6_file.py b/python/step6_file.py
index 764eb53..9d84d1f 100644
--- a/python/step6_file.py
+++ b/python/step6_file.py
@@ -45,7 +45,9 @@ def EVAL(ast, env):
let_env = Env(env)
for i in range(0, len(a1), 2):
let_env.set(a1[i], EVAL(a1[i+1], let_env))
- return EVAL(a2, let_env)
+ ast = a2
+ env = let_env
+ # Continue loop (TCO)
elif "do" == a0:
eval_ast(ast[1:-1], env)
ast = ast[-1]
diff --git a/python/step7_quote.py b/python/step7_quote.py
index 1002613..de19c6d 100644
--- a/python/step7_quote.py
+++ b/python/step7_quote.py
@@ -63,11 +63,14 @@ def EVAL(ast, env):
let_env = Env(env)
for i in range(0, len(a1), 2):
let_env.set(a1[i], EVAL(a1[i+1], let_env))
- return EVAL(a2, let_env)
+ ast = a2
+ env = let_env
+ # Continue loop (TCO)
elif "quote" == a0:
return ast[1]
elif "quasiquote" == a0:
- return EVAL(quasiquote(ast[1]), env)
+ ast = quasiquote(ast[1]);
+ # Continue loop (TCO)
elif "do" == a0:
eval_ast(ast[1:-1], env)
ast = ast[-1]
diff --git a/python/step8_macros.py b/python/step8_macros.py
index 28b68fb..80ca239 100644
--- a/python/step8_macros.py
+++ b/python/step8_macros.py
@@ -77,11 +77,14 @@ def EVAL(ast, env):
let_env = Env(env)
for i in range(0, len(a1), 2):
let_env.set(a1[i], EVAL(a1[i+1], let_env))
- return EVAL(a2, let_env)
+ ast = a2
+ env = let_env
+ # Continue loop (TCO)
elif "quote" == a0:
return ast[1]
elif "quasiquote" == a0:
- return EVAL(quasiquote(ast[1]), env)
+ ast = quasiquote(ast[1]);
+ # Continue loop (TCO)
elif 'defmacro!' == a0:
func = EVAL(ast[2], env)
func._ismacro_ = True
diff --git a/python/step9_interop.py b/python/step9_interop.py
index 2f7c5e0..7cacf1f 100644
--- a/python/step9_interop.py
+++ b/python/step9_interop.py
@@ -77,11 +77,14 @@ def EVAL(ast, env):
let_env = Env(env)
for i in range(0, len(a1), 2):
let_env.set(a1[i], EVAL(a1[i+1], let_env))
- return EVAL(a2, let_env)
+ ast = a2
+ env = let_env
+ # Continue loop (TCO)
elif "quote" == a0:
return ast[1]
elif "quasiquote" == a0:
- return EVAL(quasiquote(ast[1]), env)
+ ast = quasiquote(ast[1]);
+ # Continue loop (TCO)
elif 'defmacro!' == a0:
func = EVAL(ast[2], env)
func._ismacro_ = True
diff --git a/python/stepA_more.py b/python/stepA_more.py
index b9d8152..723f0ed 100644
--- a/python/stepA_more.py
+++ b/python/stepA_more.py
@@ -77,11 +77,14 @@ def EVAL(ast, env):
let_env = Env(env)
for i in range(0, len(a1), 2):
let_env.set(a1[i], EVAL(a1[i+1], let_env))
- return EVAL(a2, let_env)
+ ast = a2
+ env = let_env
+ # Continue loop (TCO)
elif "quote" == a0:
return ast[1]
elif "quasiquote" == a0:
- return EVAL(quasiquote(ast[1]), env)
+ ast = quasiquote(ast[1]);
+ # Continue loop (TCO)
elif 'defmacro!' == a0:
func = EVAL(ast[2], env)
func._ismacro_ = True
diff --git a/ruby/step5_tco.rb b/ruby/step5_tco.rb
index 6be04ae..38bb204 100644
--- a/ruby/step5_tco.rb
+++ b/ruby/step5_tco.rb
@@ -48,17 +48,18 @@ def EVAL(ast, env)
a1.each_slice(2) do |a,e|
let_env.set(a, EVAL(e, let_env))
end
- return EVAL(a2, let_env)
+ env = let_env
+ ast = a2 # Continue loop (TCO)
when :do
eval_ast(ast[1..-2], env)
- ast = ast.last
+ ast = ast.last # Continue loop (TCO)
when :if
cond = EVAL(a1, env)
if not cond
return nil if a3 == nil
- ast = a3
+ ast = a3 # Continue loop (TCO)
else
- ast = a2
+ ast = a2 # Continue loop (TCO)
end
when :"fn*"
return Function.new(a2, env, a1) {|*args|
@@ -69,7 +70,7 @@ def EVAL(ast, env)
f = el[0]
if f.class == Function
ast = f.ast
- env = f.gen_env(el.drop(1))
+ env = f.gen_env(el.drop(1)) # Continue loop (TCO)
else
return f[*el.drop(1)]
end
diff --git a/ruby/step6_file.rb b/ruby/step6_file.rb
index 191febe..0c99cee 100644
--- a/ruby/step6_file.rb
+++ b/ruby/step6_file.rb
@@ -48,17 +48,18 @@ def EVAL(ast, env)
a1.each_slice(2) do |a,e|
let_env.set(a, EVAL(e, let_env))
end
- return EVAL(a2, let_env)
+ env = let_env
+ ast = a2 # Continue loop (TCO)
when :do
eval_ast(ast[1..-2], env)
- ast = ast.last
+ ast = ast.last # Continue loop (TCO)
when :if
cond = EVAL(a1, env)
if not cond
return nil if a3 == nil
- ast = a3
+ ast = a3 # Continue loop (TCO)
else
- ast = a2
+ ast = a2 # Continue loop (TCO)
end
when :"fn*"
return Function.new(a2, env, a1) {|*args|
@@ -69,7 +70,7 @@ def EVAL(ast, env)
f = el[0]
if f.class == Function
ast = f.ast
- env = f.gen_env(el.drop(1))
+ env = f.gen_env(el.drop(1)) # Continue loop (TCO)
else
return f[*el.drop(1)]
end
diff --git a/ruby/step7_quote.rb b/ruby/step7_quote.rb
index 18b7db3..48385f1 100644
--- a/ruby/step7_quote.rb
+++ b/ruby/step7_quote.rb
@@ -64,21 +64,22 @@ def EVAL(ast, env)
a1.each_slice(2) do |a,e|
let_env.set(a, EVAL(e, let_env))
end
- return EVAL(a2, let_env)
+ env = let_env
+ ast = a2 # Continue loop (TCO)
when :quote
return a1
when :quasiquote
- return EVAL(quasiquote(a1), env)
+ ast = quasiquote(a1); # Continue loop (TCO)
when :do
eval_ast(ast[1..-2], env)
- ast = ast.last
+ ast = ast.last # Continue loop (TCO)
when :if
cond = EVAL(a1, env)
if not cond
return nil if a3 == nil
- ast = a3
+ ast = a3 # Continue loop (TCO)
else
- ast = a2
+ ast = a2 # Continue loop (TCO)
end
when :"fn*"
return Function.new(a2, env, a1) {|*args|
@@ -89,7 +90,7 @@ def EVAL(ast, env)
f = el[0]
if f.class == Function
ast = f.ast
- env = f.gen_env(el.drop(1))
+ env = f.gen_env(el.drop(1)) # Continue loop (TCO)
else
return f[*el.drop(1)]
end
diff --git a/ruby/step8_macros.rb b/ruby/step8_macros.rb
index 3c99cce..58adaea 100644
--- a/ruby/step8_macros.rb
+++ b/ruby/step8_macros.rb
@@ -83,11 +83,12 @@ def EVAL(ast, env)
a1.each_slice(2) do |a,e|
let_env.set(a, EVAL(e, let_env))
end
- return EVAL(a2, let_env)
+ env = let_env
+ ast = a2 # Continue loop (TCO)
when :quote
return a1
when :quasiquote
- return EVAL(quasiquote(a1), env)
+ ast = quasiquote(a1); # Continue loop (TCO)
when :defmacro!
func = EVAL(a2, env)
func.is_macro = true
@@ -96,14 +97,14 @@ def EVAL(ast, env)
return macroexpand(a1, env)
when :do
eval_ast(ast[1..-2], env)
- ast = ast.last
+ ast = ast.last # Continue loop (TCO)
when :if
cond = EVAL(a1, env)
if not cond
return nil if a3 == nil
- ast = a3
+ ast = a3 # Continue loop (TCO)
else
- ast = a2
+ ast = a2 # Continue loop (TCO)
end
when :"fn*"
return Function.new(a2, env, a1) {|*args|
@@ -114,7 +115,7 @@ def EVAL(ast, env)
f = el[0]
if f.class == Function
ast = f.ast
- env = f.gen_env(el.drop(1))
+ env = f.gen_env(el.drop(1)) # Continue loop (TCO)
else
return f[*el.drop(1)]
end
diff --git a/ruby/step9_interop.rb b/ruby/step9_interop.rb
index 507114e..6d2cbe2 100644
--- a/ruby/step9_interop.rb
+++ b/ruby/step9_interop.rb
@@ -83,11 +83,12 @@ def EVAL(ast, env)
a1.each_slice(2) do |a,e|
let_env.set(a, EVAL(e, let_env))
end
- return EVAL(a2, let_env)
+ env = let_env
+ ast = a2 # Continue loop (TCO)
when :quote
return a1
when :quasiquote
- return EVAL(quasiquote(a1), env)
+ ast = quasiquote(a1); # Continue loop (TCO)
when :defmacro!
func = EVAL(a2, env)
func.is_macro = true
@@ -98,14 +99,14 @@ def EVAL(ast, env)
return eval(a1)
when :do
eval_ast(ast[1..-2], env)
- ast = ast.last
+ ast = ast.last # Continue loop (TCO)
when :if
cond = EVAL(a1, env)
if not cond
return nil if a3 == nil
- ast = a3
+ ast = a3 # Continue loop (TCO)
else
- ast = a2
+ ast = a2 # Continue loop (TCO)
end
when :"fn*"
return Function.new(a2, env, a1) {|*args|
@@ -116,7 +117,7 @@ def EVAL(ast, env)
f = el[0]
if f.class == Function
ast = f.ast
- env = f.gen_env(el.drop(1))
+ env = f.gen_env(el.drop(1)) # Continue loop (TCO)
else
return f[*el.drop(1)]
end
diff --git a/ruby/stepA_more.rb b/ruby/stepA_more.rb
index 101dee3..6123293 100644
--- a/ruby/stepA_more.rb
+++ b/ruby/stepA_more.rb
@@ -83,11 +83,12 @@ def EVAL(ast, env)
a1.each_slice(2) do |a,e|
let_env.set(a, EVAL(e, let_env))
end
- return EVAL(a2, let_env)
+ env = let_env
+ ast = a2 # Continue loop (TCO)
when :quote
return a1
when :quasiquote
- return EVAL(quasiquote(a1), env)
+ ast = quasiquote(a1); # Continue loop (TCO)
when :defmacro!
func = EVAL(a2, env)
func.is_macro = true
@@ -113,14 +114,14 @@ def EVAL(ast, env)
end
when :do
eval_ast(ast[1..-2], env)
- ast = ast.last
+ ast = ast.last # Continue loop (TCO)
when :if
cond = EVAL(a1, env)
if not cond
return nil if a3 == nil
- ast = a3
+ ast = a3 # Continue loop (TCO)
else
- ast = a2
+ ast = a2 # Continue loop (TCO)
end
when :"fn*"
return Function.new(a2, env, a1) {|*args|
@@ -131,7 +132,7 @@ def EVAL(ast, env)
f = el[0]
if f.class == Function
ast = f.ast
- env = f.gen_env(el.drop(1))
+ env = f.gen_env(el.drop(1)) # Continue loop (TCO)
else
return f[*el.drop(1)]
end
diff --git a/tests/stepA_more.mal b/tests/stepA_more.mal
index 0378c58..7b7dac5 100644
--- a/tests/stepA_more.mal
+++ b/tests/stepA_more.mal
@@ -6,8 +6,9 @@
;=>nil
;;;TODO: fix so long lines don't trigger ANSI escape codes ;;;(try*
-(throw {"data" "foo"}) (catch* exc (do (prn "exc is:" exc) 7))) ;;;;
-"exc is:" {"data" "foo"} ;;;;=>7
+;;;(try* (throw {"data" "foo"}) (catch* exc (do (prn "exc is:" exc) 7))) ;;;;
+;;;; "exc is:" {"data" "foo"} ;;;;=>7
+;;;;=>7
(try* (throw {"data" "foo"}) (catch* exc (do (prn "err:" exc) 7)))
; "err:" {"data" "foo"}
@@ -69,8 +70,9 @@
(read-string "7 ;; comment")
;=>7
+;;; Differing output, but make sure no fatal error
(read-string ";; comment")
-;=>nil
+
(eval (read-string "(+ 4 5)"))
;=>9