diff --git a/.travis.yml b/.travis.yml index 59a15b62..d9ab35ad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -109,4 +109,5 @@ script: # Build, test, perf - ./.travis_test.sh build ${IMPL} - ./.travis_test.sh test ${IMPL} + - STEP=stepA REGRESS=1 HARD=1 OPTIONAL=0 ./.travis_test.sh test ${IMPL} - ./.travis_test.sh perf ${IMPL} diff --git a/.travis_test.sh b/.travis_test.sh index aa814e52..c41cf6b6 100755 --- a/.travis_test.sh +++ b/.travis_test.sh @@ -61,6 +61,9 @@ test|perf) if ! ${MAKE} TEST_OPTS="${TEST_OPTS}" \ ${MAL_IMPL:+MAL_IMPL=${MAL_IMPL}} \ ${REGRESS:+REGRESS=${REGRESS}} \ + ${HARD:+HARD=${HARD}} \ + ${DEFERRABLE:+DEFERRABLE=${DEFERRABLE}} \ + ${OPTIONAL:+OPTIONAL=${OPTIONAL}} \ ${ACTION}^${IMPL}${STEP:+^${STEP}}; then # print debug-file on error cat ${ACTION}.err diff --git a/Makefile b/Makefile index 832194e9..cdbaa7c3 100644 --- a/Makefile +++ b/Makefile @@ -77,6 +77,7 @@ TEST_OPTS = # later steps. REGRESS = +HARD= DEFERRABLE=1 OPTIONAL=1 @@ -142,6 +143,8 @@ dist_EXCLUDES += guile io julia matlab swift # Extra options to pass to runtest.py bbc-basic_TEST_OPTS = --test-timeout 60 +guile_TEST_OPTS = --test-timeout 120 +io_TEST_OPTS = --test-timeout 120 logo_TEST_OPTS = --start-timeout 60 --test-timeout 120 mal_TEST_OPTS = --start-timeout 60 --test-timeout 120 miniMAL_TEST_OPTS = --start-timeout 60 --test-timeout 120 @@ -270,6 +273,7 @@ noop = SPACE = $(noop) $(noop) export FACTOR_ROOTS := . +opt_HARD = $(if $(strip $(HARD)),$(if $(filter t true T True TRUE 1 y yes Yes YES,$(HARD)),--hard,),) opt_DEFERRABLE = $(if $(strip $(DEFERRABLE)),$(if $(filter t true T True TRUE 1 y yes Yes YES,$(DEFERRABLE)),--deferrable,--no-deferrable),--no-deferrable) opt_OPTIONAL = $(if $(strip $(OPTIONAL)),$(if $(filter t true T True TRUE 1 y yes Yes YES,$(OPTIONAL)),--optional,--no-optional),--no-optional) @@ -328,7 +332,7 @@ get_run_prefix = $(strip $(foreach mode,$(call actual_impl,$(1))_MODE, \ # Takes impl and step # Returns the runtest command prefix (with runtest options) for testing the given step get_runtest_cmd = $(call get_run_prefix,$(1),$(2),$(if $(filter cs fsharp mal tcl vb,$(1)),RAW=1,)) \ - ../runtest.py $(opt_DEFERRABLE) $(opt_OPTIONAL) $(call $(1)_TEST_OPTS) $(TEST_OPTS) + ../runtest.py $(opt_HARD) $(opt_DEFERRABLE) $(opt_OPTIONAL) $(call $(1)_TEST_OPTS) $(TEST_OPTS) # Takes impl and step # Returns the runtest command prefix (with runtest options) for testing the given step diff --git a/ada.2/core.adb b/ada.2/core.adb index a9814c7d..07652aeb 100644 --- a/ada.2/core.adb +++ b/ada.2/core.adb @@ -156,8 +156,9 @@ package body Core is function Keyword (Args : in Types.T_Array) return Types.T is begin - Err.Check (Args'Length = 1 and then Args (Args'First).Kind = Kind_String, - "expected a string"); + Err.Check (Args'Length = 1 + and then Args (Args'First).Kind in Types.Kind_Key, + "expected a keyword or a string"); return (Kind_Keyword, Args (Args'First).Str); end Keyword; diff --git a/ada.2/step8_macros.adb b/ada.2/step8_macros.adb index 3786b8ed..4e9db3ec 100644 --- a/ada.2/step8_macros.adb +++ b/ada.2/step8_macros.adb @@ -401,12 +401,7 @@ procedure Step8_Macros is & " (list 'if (first xs)" & " (if (> (count xs) 1) (nth xs 1)" & " (throw ""odd number of forms to cond""))" - & " (cons 'cond (rest (rest xs)))))))" - & "(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))))))))"; + & " (cons 'cond (rest (rest xs)))))))"; Repl : constant Envs.Ptr := Envs.New_Env; function Eval_Builtin (Args : in Types.T_Array) return Types.T is begin diff --git a/ada.2/step9_try.adb b/ada.2/step9_try.adb index bc26dd0f..162eec10 100644 --- a/ada.2/step9_try.adb +++ b/ada.2/step9_try.adb @@ -431,12 +431,7 @@ procedure Step9_Try is & " (list 'if (first xs)" & " (if (> (count xs) 1) (nth xs 1)" & " (throw ""odd number of forms to cond""))" - & " (cons 'cond (rest (rest xs)))))))" - & "(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))))))))"; + & " (cons 'cond (rest (rest xs)))))))"; Repl : constant Envs.Ptr := Envs.New_Env; function Eval_Builtin (Args : in Types.T_Array) return Types.T is begin diff --git a/ada.2/stepa_mal.adb b/ada.2/stepa_mal.adb index 0c665904..ba52c2c0 100644 --- a/ada.2/stepa_mal.adb +++ b/ada.2/stepa_mal.adb @@ -438,15 +438,6 @@ procedure StepA_Mal is & " (if (> (count xs) 1) (nth xs 1)" & " (throw ""odd number of forms to cond""))" & " (cons 'cond (rest (rest xs)))))))" - & "(def! inc (fn* [x] (+ x 1)))" - & "(def! gensym (let* [counter (atom 0)]" - & " (fn* [] (symbol (str ""G__"" (swap! counter inc))))))" - & "(defmacro! or (fn* (& xs)" - & " (if (empty? xs) nil" - & " (if (= 1 (count xs)) (first xs)" - & " (let* (condvar (gensym))" - & " `(let* (~condvar ~(first xs))" - & " (if ~condvar ~condvar (or ~@(rest xs)))))))))" & "(def! *host-language* ""ada.2"")"; Repl : constant Envs.Ptr := Envs.New_Env; function Eval_Builtin (Args : in Types.T_Array) return Types.T is diff --git a/ada/step8_macros.adb b/ada/step8_macros.adb index 25cc8f42..40e4624c 100644 --- a/ada/step8_macros.adb +++ b/ada/step8_macros.adb @@ -527,7 +527,6 @@ begin RE ("(def! not (fn* (a) (if a false true)))"); RE ("(def! load-file (fn* (f) (eval (read-string (str ""(do "" (slurp f) "")"")))))"); RE ("(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)))))))"); - RE ("(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))))))))"); -- Command line processing. diff --git a/ada/step9_try.adb b/ada/step9_try.adb index bd7fcd5c..fcbba5c5 100644 --- a/ada/step9_try.adb +++ b/ada/step9_try.adb @@ -580,7 +580,6 @@ begin RE ("(def! not (fn* (a) (if a false true)))"); RE ("(def! load-file (fn* (f) (eval (read-string (str ""(do "" (slurp f) "")"")))))"); RE ("(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)))))))"); - RE ("(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))))))))"); -- Command line processing. diff --git a/ada/stepa_mal.adb b/ada/stepa_mal.adb index 1f1c049a..b0c9c040 100644 --- a/ada/stepa_mal.adb +++ b/ada/stepa_mal.adb @@ -580,9 +580,6 @@ begin RE ("(def! not (fn* (a) (if a false true)))"); RE ("(def! load-file (fn* (f) (eval (read-string (str ""(do "" (slurp f) "")"")))))"); RE ("(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)))))))"); - RE ("(def! inc (fn* [x] (+ x 1)))"); - RE ("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str ""G__"" (swap! counter inc))))))"); - RE ("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))"); -- Command line processing. diff --git a/awk/step8_macros.awk b/awk/step8_macros.awk index 50a7fde3..85fe014c 100644 --- a/awk/step8_macros.awk +++ b/awk/step8_macros.awk @@ -507,7 +507,6 @@ function main(str, ret, i, idx) 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))))))))") idx = types_allocate() env_set(repl_env, "'*ARGV*", "(" idx) diff --git a/awk/step9_try.awk b/awk/step9_try.awk index 79e9ed1a..18583c67 100644 --- a/awk/step9_try.awk +++ b/awk/step9_try.awk @@ -569,7 +569,6 @@ function main(str, ret, i, idx) 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))))))))") idx = types_allocate() env_set(repl_env, "'*ARGV*", "(" idx) diff --git a/awk/stepA_mal.awk b/awk/stepA_mal.awk index 8e7fae0d..ce5773b5 100644 --- a/awk/stepA_mal.awk +++ b/awk/stepA_mal.awk @@ -572,9 +572,6 @@ function main(str, ret, i, idx) 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("(def! inc (fn* [x] (+ x 1)))") - rep("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))") - rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") idx = types_allocate() env_set(repl_env, "'*ARGV*", "(" idx) diff --git a/bash/step8_macros.sh b/bash/step8_macros.sh index d86cdd64..72b010da 100755 --- a/bash/step8_macros.sh +++ b/bash/step8_macros.sh @@ -250,7 +250,6 @@ ENV_SET "${REPL_ENV}" "${r}" "${argv}"; 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))))))))" # load/run file from command line (then exit) if [[ "${1}" ]]; then diff --git a/bash/step9_try.sh b/bash/step9_try.sh index 7567050a..29fa329b 100755 --- a/bash/step9_try.sh +++ b/bash/step9_try.sh @@ -263,7 +263,6 @@ ENV_SET "${REPL_ENV}" "${r}" "${argv}"; 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))))))))" # load/run file from command line (then exit) if [[ "${1}" ]]; then diff --git a/bash/stepA_mal.sh b/bash/stepA_mal.sh index d3df641b..e414b7ab 100755 --- a/bash/stepA_mal.sh +++ b/bash/stepA_mal.sh @@ -272,9 +272,6 @@ 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)))))))" -REP "(def! inc (fn* [x] (+ x 1)))" -REP "(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))" -REP "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) \`(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))" # load/run file from command line (then exit) if [[ "${1}" ]]; then diff --git a/basic/step8_macros.in.bas b/basic/step8_macros.in.bas index c2c6f8ed..09397a18 100755 --- a/basic/step8_macros.in.bas +++ b/basic/step8_macros.in.bas @@ -537,10 +537,6 @@ MAIN: A$=A$+" forms to cond"+CHR$(34)+")) (cons 'cond (rest (rest xs)))))))" GOSUB RE:AY=R:GOSUB RELEASE - A$="(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs)" - A$=A$+" `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))" - GOSUB RE:AY=R:GOSUB RELEASE - REM load the args file A$="(def! -*ARGS*- (load-file "+CHR$(34)+".args.mal"+CHR$(34)+"))" GOSUB RE:AY=R:GOSUB RELEASE diff --git a/basic/step9_try.in.bas b/basic/step9_try.in.bas index 26a18d77..d5e76c3c 100755 --- a/basic/step9_try.in.bas +++ b/basic/step9_try.in.bas @@ -570,10 +570,6 @@ MAIN: A$=A$+" forms to cond"+CHR$(34)+")) (cons 'cond (rest (rest xs)))))))" GOSUB RE:AY=R:GOSUB RELEASE - A$="(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs)" - A$=A$+" `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))" - GOSUB RE:AY=R:GOSUB RELEASE - REM load the args file A$="(def! -*ARGS*- (load-file "+CHR$(34)+".args.mal"+CHR$(34)+"))" GOSUB RE:AY=R:GOSUB RELEASE diff --git a/basic/stepA_mal.in.bas b/basic/stepA_mal.in.bas index 9e6297dc..bfbef857 100755 --- a/basic/stepA_mal.in.bas +++ b/basic/stepA_mal.in.bas @@ -569,18 +569,6 @@ MAIN: A$=A$+" forms to cond"+CHR$(34)+")) (cons 'cond (rest (rest xs)))))))" GOSUB RE:AY=R:GOSUB RELEASE - A$="(def! inc (fn* [x] (+ x 1)))" - GOSUB RE:AY=R:GOSUB RELEASE - - A$="(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str "+CHR$(34) - A$=A$+"G__"+CHR$(34)+" (swap! counter inc))))))" - GOSUB RE:AY=R:GOSUB RELEASE - - A$="(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs)" - A$=A$+" (let* (c (gensym)) `(let* (~c ~(first xs))" - A$=A$+" (if ~c ~c (or ~@(rest xs)))))))))" - GOSUB RE:AY=R:GOSUB RELEASE - REM load the args file A$="(def! -*ARGS*- (load-file "+CHR$(34)+".args.mal"+CHR$(34)+"))" GOSUB RE:AY=R:GOSUB RELEASE diff --git a/bbc-basic/step8_macros.bbc b/bbc-basic/step8_macros.bbc index a71954d0..5e9be483 100644 --- a/bbc-basic/step8_macros.bbc +++ b/bbc-basic/step8_macros.bbc @@ -22,7 +22,6 @@ RESTORE +0 DATA (def! not (fn* (a) (if a false true))) DATA (def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")"))))) DATA (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))))))) -DATA (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)))))))) DATA "" REPEAT READ form$ diff --git a/bbc-basic/step9_try.bbc b/bbc-basic/step9_try.bbc index 278556b3..f6524e3a 100644 --- a/bbc-basic/step9_try.bbc +++ b/bbc-basic/step9_try.bbc @@ -22,7 +22,6 @@ RESTORE +0 DATA (def! not (fn* (a) (if a false true))) DATA (def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")"))))) DATA (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))))))) -DATA (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)))))))) DATA "" REPEAT READ form$ diff --git a/bbc-basic/stepA_mal.bbc b/bbc-basic/stepA_mal.bbc index 062a7fc4..44c990cb 100644 --- a/bbc-basic/stepA_mal.bbc +++ b/bbc-basic/stepA_mal.bbc @@ -22,9 +22,6 @@ RESTORE +0 DATA (def! not (fn* (a) (if a false true))) DATA (def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")"))))) DATA (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))))))) -DATA (def! inc (fn* [x] (+ x 1))) -DATA (def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str "G__" (swap! counter inc)))))) -DATA (defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs))))))))) DATA (def! *host-language* "BBC BASIC V") DATA "" REPEAT diff --git a/c/step8_macros.c b/c/step8_macros.c index 335642ba..5dede971 100644 --- a/c/step8_macros.c +++ b/c/step8_macros.c @@ -291,7 +291,6 @@ void init_repl_env(int argc, char *argv[]) { 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)))))))"); - RE(repl_env, "", "(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))))))))"); } int main(int argc, char *argv[]) diff --git a/c/step9_try.c b/c/step9_try.c index 9c555aca..c4d262fe 100644 --- a/c/step9_try.c +++ b/c/step9_try.c @@ -316,7 +316,6 @@ void init_repl_env(int argc, char *argv[]) { 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)))))))"); - RE(repl_env, "", "(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))))))))"); } int main(int argc, char *argv[]) diff --git a/c/stepA_mal.c b/c/stepA_mal.c index 4960cb51..2c8f6b0d 100644 --- a/c/stepA_mal.c +++ b/c/stepA_mal.c @@ -322,9 +322,6 @@ void init_repl_env(int argc, char *argv[]) { 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)))))))"); - RE(repl_env, "", "(def! inc (fn* [x] (+ x 1)))"); - RE(repl_env, "", "(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))"); - RE(repl_env, "", "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))"); } int main(int argc, char *argv[]) diff --git a/chuck/step8_macros.ck b/chuck/step8_macros.ck index f52e0de4..5c185d91 100644 --- a/chuck/step8_macros.ck +++ b/chuck/step8_macros.ck @@ -435,9 +435,7 @@ fun string rep(string input) 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))))))))"); fun void main() { diff --git a/chuck/step9_try.ck b/chuck/step9_try.ck index 539d1d8f..ecc45d8b 100644 --- a/chuck/step9_try.ck +++ b/chuck/step9_try.ck @@ -452,9 +452,7 @@ fun string rep(string input) 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))))))))"); fun void main() { diff --git a/chuck/stepA_mal.ck b/chuck/stepA_mal.ck index 51d1cf47..afe6b8d7 100644 --- a/chuck/stepA_mal.ck +++ b/chuck/stepA_mal.ck @@ -454,13 +454,8 @@ fun string rep(string input) 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("(def! inc (fn* [x] (+ x 1)))"); -rep("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))"); -rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))"); - fun void main() { int done; diff --git a/clojure/src/mal/step8_macros.cljc b/clojure/src/mal/step8_macros.cljc index 9cf70991..d0d56741 100644 --- a/clojure/src/mal/step8_macros.cljc +++ b/clojure/src/mal/step8_macros.cljc @@ -153,7 +153,6 @@ (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))))))))") ;; repl loop (defn repl-loop [] diff --git a/clojure/src/mal/step9_try.cljc b/clojure/src/mal/step9_try.cljc index e6ff0671..fd172375 100644 --- a/clojure/src/mal/step9_try.cljc +++ b/clojure/src/mal/step9_try.cljc @@ -170,7 +170,6 @@ (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))))))))") ;; repl loop (defn repl-loop [] diff --git a/clojure/src/mal/stepA_mal.cljc b/clojure/src/mal/stepA_mal.cljc index 6cb5f3e9..5a26159a 100644 --- a/clojure/src/mal/stepA_mal.cljc +++ b/clojure/src/mal/stepA_mal.cljc @@ -180,9 +180,6 @@ (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 "(def! inc (fn* [x] (+ x 1)))") -(rep "(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))") -(rep "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") ;; repl loop (defn repl-loop [] diff --git a/coffee/step8_macros.coffee b/coffee/step8_macros.coffee index 3b552f8d..98319a10 100644 --- a/coffee/step8_macros.coffee +++ b/coffee/step8_macros.coffee @@ -107,7 +107,6 @@ repl_env.set types._symbol('*ARGV*'), [] 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 types._symbol('*ARGV*'), process.argv[3..] diff --git a/coffee/step9_try.coffee b/coffee/step9_try.coffee index d5bbe010..71d479ed 100644 --- a/coffee/step9_try.coffee +++ b/coffee/step9_try.coffee @@ -116,7 +116,6 @@ repl_env.set types._symbol('*ARGV*'), [] 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 types._symbol('*ARGV*'), process.argv[3..] diff --git a/coffee/stepA_mal.coffee b/coffee/stepA_mal.coffee index 5a18714e..7f0030b3 100644 --- a/coffee/stepA_mal.coffee +++ b/coffee/stepA_mal.coffee @@ -123,9 +123,6 @@ 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("(def! inc (fn* [x] (+ x 1)))"); -rep("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))"); -rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") if process? && process.argv.length > 2 repl_env.set types._symbol('*ARGV*'), process.argv[3..] diff --git a/common-lisp/src/step8_macros.lisp b/common-lisp/src/step8_macros.lisp index e27ac6cc..ab75d9c6 100644 --- a/common-lisp/src/step8_macros.lisp +++ b/common-lisp/src/step8_macros.lisp @@ -228,7 +228,6 @@ (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))))))))") (defvar *use-readline-p* nil) diff --git a/common-lisp/src/step9_try.lisp b/common-lisp/src/step9_try.lisp index 87cc341f..d8bd04d8 100644 --- a/common-lisp/src/step9_try.lisp +++ b/common-lisp/src/step9_try.lisp @@ -251,7 +251,6 @@ (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))))))))") (defvar *use-readline-p* nil) diff --git a/common-lisp/src/stepA_mal.lisp b/common-lisp/src/stepA_mal.lisp index c9d7c334..adb17bfb 100644 --- a/common-lisp/src/stepA_mal.lisp +++ b/common-lisp/src/stepA_mal.lisp @@ -259,9 +259,6 @@ (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 "(def! *host-language* \"common-lisp\")") -(rep "(def! inc (fn* [x] (+ x 1)))") -(rep "(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))") -(rep "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") (defvar *use-readline-p* nil) diff --git a/cpp/step8_macros.cpp b/cpp/step8_macros.cpp index 32d450de..183039d2 100644 --- a/cpp/step8_macros.cpp +++ b/cpp/step8_macros.cpp @@ -280,7 +280,6 @@ static malValuePtr macroExpand(malValuePtr obj, malEnvPtr env) static const char* malFunctionTable[] = { "(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)))))))", - "(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))))))))", "(def! not (fn* (cond) (if cond false true)))", "(def! load-file (fn* (filename) \ (eval (read-string (str \"(do \" (slurp filename) \")\")))))", diff --git a/cpp/step9_try.cpp b/cpp/step9_try.cpp index 7f776bd6..ea9f8ce5 100644 --- a/cpp/step9_try.cpp +++ b/cpp/step9_try.cpp @@ -329,7 +329,6 @@ static malValuePtr macroExpand(malValuePtr obj, malEnvPtr env) static const char* malFunctionTable[] = { "(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)))))))", - "(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))))))))", "(def! not (fn* (cond) (if cond false true)))", "(def! load-file (fn* (filename) \ (eval (read-string (str \"(do \" (slurp filename) \")\")))))", diff --git a/cpp/stepA_mal.cpp b/cpp/stepA_mal.cpp index 93a6f1d9..15ab8192 100644 --- a/cpp/stepA_mal.cpp +++ b/cpp/stepA_mal.cpp @@ -330,12 +330,9 @@ static malValuePtr macroExpand(malValuePtr obj, malEnvPtr env) static const char* malFunctionTable[] = { "(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)))))))", - "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))", "(def! not (fn* (cond) (if cond false true)))", "(def! load-file (fn* (filename) \ (eval (read-string (str \"(do \" (slurp filename) \")\")))))", - "(def! inc (fn* [x] (+ x 1)))", - "(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))", "(def! *host-language* \"C++\")", }; diff --git a/crystal/step8_macros.cr b/crystal/step8_macros.cr index cca37102..d118c15f 100755 --- a/crystal/step8_macros.cr +++ b/crystal/step8_macros.cr @@ -231,7 +231,6 @@ REPL_ENV.set("eval", Mal::Type.new ->(args : Array(Mal::Type)) { Mal.eval(args[0 Mal.rep "(def! not (fn* (a) (if a false true)))" Mal.rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" Mal.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)))))))" -Mal.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))))))))" argv = Mal::List.new REPL_ENV.set("*ARGV*", Mal::Type.new argv) diff --git a/crystal/step9_try.cr b/crystal/step9_try.cr index 5d63bc85..455c31e1 100755 --- a/crystal/step9_try.cr +++ b/crystal/step9_try.cr @@ -248,7 +248,6 @@ REPL_ENV.set("eval", Mal::Type.new ->(args : Array(Mal::Type)) { Mal.eval(args[0 Mal.rep "(def! not (fn* (a) (if a false true)))" Mal.rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" Mal.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)))))))" -Mal.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))))))))" argv = Mal::List.new REPL_ENV.set("*ARGV*", Mal::Type.new argv) diff --git a/crystal/stepA_mal.cr b/crystal/stepA_mal.cr index 701ffb6e..2d7b39b8 100755 --- a/crystal/stepA_mal.cr +++ b/crystal/stepA_mal.cr @@ -254,9 +254,6 @@ REPL_ENV.set("eval", Mal::Type.new ->(args : Array(Mal::Type)) { Mal.eval(args[0 Mal.rep "(def! not (fn* (a) (if a false true)))" Mal.rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" Mal.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)))))))" -Mal.rep "(def! inc (fn* [x] (+ x 1)))" -Mal.rep "(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))" -Mal.rep "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))" Mal.rep("(def! *host-language* \"crystal\")") argv = Mal::List.new diff --git a/cs/step8_macros.cs b/cs/step8_macros.cs index 3ec240a3..6c1d7062 100644 --- a/cs/step8_macros.cs +++ b/cs/step8_macros.cs @@ -227,7 +227,6 @@ namespace Mal { RE("(def! not (fn* (a) (if a false true)))"); RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); RE("(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)))))))"); - RE("(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 (args.Length > fileIdx) { RE("(load-file \"" + args[fileIdx] + "\")"); diff --git a/cs/step9_try.cs b/cs/step9_try.cs index 0e37436a..0a8f746f 100644 --- a/cs/step9_try.cs +++ b/cs/step9_try.cs @@ -248,7 +248,6 @@ namespace Mal { RE("(def! not (fn* (a) (if a false true)))"); RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); RE("(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)))))))"); - RE("(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 (args.Length > fileIdx) { RE("(load-file \"" + args[fileIdx] + "\")"); diff --git a/cs/stepA_mal.cs b/cs/stepA_mal.cs index 1f14be86..70fc0d22 100644 --- a/cs/stepA_mal.cs +++ b/cs/stepA_mal.cs @@ -249,9 +249,6 @@ namespace Mal { RE("(def! not (fn* (a) (if a false true)))"); RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); RE("(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)))))))"); - RE("(def! inc (fn* [x] (+ x 1)))"); - RE("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))"); - RE("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))"); if (args.Length > fileIdx) { RE("(load-file \"" + args[fileIdx] + "\")"); diff --git a/d/step8_macros.d b/d/step8_macros.d index 48b3d2b2..a39ff89d 100644 --- a/d/step8_macros.d +++ b/d/step8_macros.d @@ -263,7 +263,6 @@ void main(string[] args) re("(def! not (fn* (a) (if a false true)))", repl_env); re("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env); re("(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); - re("(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))))))))", repl_env); if (args.length > 1) { diff --git a/d/step9_try.d b/d/step9_try.d index 9070dfa1..054b38e1 100644 --- a/d/step9_try.d +++ b/d/step9_try.d @@ -292,7 +292,6 @@ void main(string[] args) re("(def! not (fn* (a) (if a false true)))", repl_env); re("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env); re("(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); - re("(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))))))))", repl_env); if (args.length > 1) { diff --git a/d/stepA_mal.d b/d/stepA_mal.d index a669dde7..31658e53 100644 --- a/d/stepA_mal.d +++ b/d/stepA_mal.d @@ -294,9 +294,6 @@ void main(string[] args) re("(def! not (fn* (a) (if a false true)))", repl_env); re("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env); re("(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); - re("(def! inc (fn* [x] (+ x 1)))", repl_env); - re("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))", repl_env); - re("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))", repl_env); if (args.length > 1) { diff --git a/dart/step8_macros.dart b/dart/step8_macros.dart index de9fbbf0..a8baada8 100644 --- a/dart/step8_macros.dart +++ b/dart/step8_macros.dart @@ -27,12 +27,6 @@ void setupEnv(List argv) { " (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))))))))"); } /// Returns `true` if [ast] is a macro call. diff --git a/dart/step9_try.dart b/dart/step9_try.dart index 09bedbeb..76cd7523 100644 --- a/dart/step9_try.dart +++ b/dart/step9_try.dart @@ -27,12 +27,6 @@ void setupEnv(List argv) { " (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))))))))"); } /// Returns `true` if [ast] is a macro call. @@ -194,8 +188,7 @@ MalType EVAL(MalType ast, Env env) { ast = quasiquote(args.first); continue; } else if (symbol.value == 'macroexpand') { - ast = macroexpand(args.first, env); - continue; + return macroexpand(args.first, env); } else if (symbol.value == 'try*') { var body = args.first; if (args.length < 2) { diff --git a/dart/stepA_mal.dart b/dart/stepA_mal.dart index a1bacce6..72ff326a 100644 --- a/dart/stepA_mal.dart +++ b/dart/stepA_mal.dart @@ -29,20 +29,6 @@ void setupEnv(List argv) { " (nth xs 1) " " (throw \"odd number of forms to cond\")) " " (cons 'cond (rest (rest xs)))))))"); - rep("(def! inc (fn* [x] (+ x 1)))"); - rep("(def! gensym" - " (let* [counter (atom 0)]" - " (fn* []" - " (symbol (str \"G__\" (swap! counter inc))))))"); - rep("(defmacro! or " - " (fn* (& xs) " - " (if (empty? xs) " - " nil " - " (if (= 1 (count xs)) " - " (first xs) " - " (let* (condvar (gensym)) " - " `(let* (~condvar ~(first xs)) " - " (if ~condvar ~condvar (or ~@(rest xs)))))))))"); } /// Returns `true` if [ast] is a macro call. @@ -204,8 +190,7 @@ MalType EVAL(MalType ast, Env env) { ast = quasiquote(args.first); continue; } else if (symbol.value == 'macroexpand') { - ast = macroexpand(args.first, env); - continue; + return macroexpand(args.first, env); } else if (symbol.value == 'try*') { var body = args.first; if (args.length < 2) { diff --git a/docs/cheatsheet.html b/docs/cheatsheet.html index f3124f3c..28719052 100644 --- a/docs/cheatsheet.html +++ b/docs/cheatsheet.html @@ -247,9 +247,6 @@ step9_try.EXT: EVAL(ast, env): - set *host-language* in repl_env to host language name - - inc: define (using rep()) a function incrementing an integer - - gensym: define using rep()), return unique symbol - - or: use gensym to fix or macro main(args): rep("(println (str \"Mal [\" *host-language* \"]\"))") diff --git a/docs/exercises.md b/docs/exercises.md new file mode 100644 index 00000000..2f5cb2c1 --- /dev/null +++ b/docs/exercises.md @@ -0,0 +1,129 @@ +# Exercises to learn MAL + +The process introduces LISP by describing the internals of selected +low-level constructs. As a complementary and more traditional +approach, you may want to solve the following exercises in the MAL +language itself, using any of the existing implementations. + +You are encouraged to use the shortcuts defined in the step files +(`not`...) and `the `lib/` subdirectory (`reduce`...) whenever you +find that they increase the readability. + +The difficulty is progressive in each section, but they focus on +related topics and it is recommended to start them in parallel. + +Some solutions are given in the `examples` directory. Feel free to +submit new solutions, or new exercises. + +## Replace parts of the process with native constructs + +Once you have a working implementation, you may want to implement +parts of the process inside the MAL language itself. This has no other +purpose than learning the MAL language. Once it exists, a built-in +implementation will always be more efficient than a native +implementation. Also, the functions described in MAL process are +selected for educative purposes, so portability accross +implementations does not matter much. + +You may easily check your answers by passing them directly to the +interpreter. They will hide the built-in functions carrying the same +names, and the usual tests will check them. +``` +make REGRESS=1 TEST_OPTS='--hard --pre-eval=\(load-file\ \"../answer.mal\"\)' test^IMPL^stepA +``` + +- Implement `nil?`, `true?`, `false?`, `empty?` and `sequential` with + another built-in function. + +- Implement `>`, `<=` and `>=` with `<`. + +- Implement `hash-map`, `list`, `prn` and `swap!` as non-recursive + functions. + +- Implement `count`, `nth`, `map`, `concat` and `conj` with the empty + constructor `()`, `empty?`, `cons`, `first` and `rest`. + + You may use `or` to make the definition of `nth` a bit less ugly, + but avoid `cond` because its definition refers to `nth`. + + Let `count` and `nth` benefit from tail call optimization. + + Try to replace explicit recursions with calls to `reduce` and `foldr`. + + Once you have tested your solution, you should comment at least + `nth`. Many implementations, for example `foldr` in `core.mal`, + rely on an efficient `nth` built-in function. + +- Implement the `do` special as a non-recursive function. The special + form will hide your implementation, so in order to test it, you will + need to give it another name and adapt the test accordingly. + +- Implement quoting with macros. + The same remark applies. + +- Implement most of `let*` as a macro that uses `fn*` and recursion. + The same remark applies. + A macro is necessary because a function would attempt to evaluate + the first argument. + + Once your answer passes most tests and you understand which part is + tricky, you should search for black magic recipes on the web. Few of + us mortals are known to have invented a full solution on their own. + +- Implement `apply`. + +- Implement maps using lists. + - Recall how maps must be evaluated. + - In the tests, you may want to replace `{...}` with `(hash-map ...)`. + - An easy solution relies on lists alterning keys and values, so + that the `hash-map` is only a list in reverse order so that the + last definition takes precedence during searches. + - As a more performant solution will use lists to construct trees, + and ideally keep them balanced. You will find examples in most + teaching material about functional languages. + - Recall that `dissoc` is an optional feature. One you can implement + dissoc is by assoc'ing a replacement value that is a magic delete + keyword (e.g.: `__..DELETED..__`) which allows you to shadow + values in the lower levels of the structure. The hash map + functions have to detect that and do the right thing. e.g. `(keys + ...)` might have to keep track of deleted values as it is scanning + the tree and not add those keys when it finds them further down + the tree. + +- Implement macros within MAL. + +## More folds + +- Compute the sum of a sequence of numbers. +- Compute the product of a sequence of numbers. + +- Compute the logical conjunction ("and") and disjunction ("or") of a + sequence of MAL values interpreted as boolean values. For example, + `(conjunction [true 1 0 "" "a" nil true {}])` + should evaluate to `false` or `nil` because of the `nil` element. + + Why are folds not the best solution here, in terms of average + performances? + +- Does "-2-3-4" translate to `(reduce - 0 [2 3 4])`? + +- Suggest better solutions for + `(reduce str "" xs)` and + `(reduce concat [] xs)`. + +- What does `(reduce (fn* [acc _] acc) xs)` nil answer? + +- The answer is `(fn* [xs] (reduce (fn* [_ x] x) nil xs))`. + What was the question? + +- What is the intent of + `(reduce (fn* [acc x] (if (< acc x) x acc)) 0 xs)`? + + Why is it the wrong answer? + +- Though `(sum (map count xs))` or `(count (apply concat xs))` can be + considered more readable, implement the same effect with a single loop. +- Compute the maximal length in a list of lists. + +- How would you name + `(fn* [& fs] (foldr (fn* [f acc] (fn* [x] (f (acc x)))) identity fs))`? diff --git a/elisp/step8_macros.el b/elisp/step8_macros.el index d9ed1da3..26c5f445 100644 --- a/elisp/step8_macros.el +++ b/elisp/step8_macros.el @@ -181,9 +181,7 @@ (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))))))))") (defun readln (prompt) ;; C-d throws an error diff --git a/elisp/step9_try.el b/elisp/step9_try.el index 7dc47a96..289478d9 100644 --- a/elisp/step9_try.el +++ b/elisp/step9_try.el @@ -197,9 +197,7 @@ (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))))))))") (defun readln (prompt) ;; C-d throws an error diff --git a/elisp/stepA_mal.el b/elisp/stepA_mal.el index c8a31e57..c4ae1cc4 100644 --- a/elisp/stepA_mal.el +++ b/elisp/stepA_mal.el @@ -198,12 +198,7 @@ (rep "(def! not (fn* (a) (if a false true)))") (rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") - -(rep "(def! inc (fn* [x] (+ x 1)))") -(rep "(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))") - (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* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") (defun readln (prompt) ;; C-d throws an error diff --git a/elixir/lib/mal/core.ex b/elixir/lib/mal/core.ex index ca651a4b..8acbca11 100644 --- a/elixir/lib/mal/core.ex +++ b/elixir/lib/mal/core.ex @@ -203,6 +203,7 @@ defmodule Mal.Core do defp with_meta([{type, ast, _old_meta}, meta]), do: {type, ast, meta} defp with_meta([%Function{} = func, meta]), do: %{func | meta: meta} + defp with_meta(_), do: nil defp deref(args) do apply(&Mal.Atom.deref/1, args) diff --git a/elixir/lib/mix/tasks/step8_macros.ex b/elixir/lib/mix/tasks/step8_macros.ex index abb6e6df..45edae19 100644 --- a/elixir/lib/mix/tasks/step8_macros.ex +++ b/elixir/lib/mix/tasks/step8_macros.ex @@ -42,17 +42,6 @@ defmodule Mix.Tasks.Step8Macros do (cons 'cond (rest (rest xs)))))))" """, env) - # or: - read_eval_print(""" - (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)))))))) - """, env) - Mal.Env.set(env, "eval", %Function{value: fn [ast] -> eval(ast, env) end}) diff --git a/elixir/lib/mix/tasks/step9_try.ex b/elixir/lib/mix/tasks/step9_try.ex index b1b94c8e..917b2a2e 100644 --- a/elixir/lib/mix/tasks/step9_try.ex +++ b/elixir/lib/mix/tasks/step9_try.ex @@ -42,17 +42,6 @@ defmodule Mix.Tasks.Step9Try do (cons 'cond (rest (rest xs)))))))" """, env) - # or: - read_eval_print(""" - (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)))))))) - """, env) - Mal.Env.set(env, "eval", %Function{value: fn [ast] -> eval(ast, env) end}) diff --git a/elixir/lib/mix/tasks/stepA_mal.ex b/elixir/lib/mix/tasks/stepA_mal.ex index b5665ba4..340be5a3 100644 --- a/elixir/lib/mix/tasks/stepA_mal.ex +++ b/elixir/lib/mix/tasks/stepA_mal.ex @@ -50,28 +50,6 @@ defmodule Mix.Tasks.StepAMal do (cons 'cond (rest (rest xs)))))))" """, env) - # gensym - read_eval_print("(def! inc (fn* [x] (+ x 1)))", env) - read_eval_print(""" - (def! gensym - (let* [counter (atom 0)] - (fn* [] - (symbol (str \"G__\" (swap! counter inc)))))) - """, env) - - # or: - read_eval_print(""" - (defmacro! or - (fn* (& xs) - (if (empty? xs) - nil - (if (= 1 (count xs)) - (first xs) - (let* (condvar (gensym)) - `(let* (~condvar ~(first xs)) - (if ~condvar ~condvar (or ~@(rest xs))))))))) - """, env) - Mal.Env.set(env, "eval", %Function{value: fn [ast] -> eval(ast, env) end}) diff --git a/elm/step8_macros.elm b/elm/step8_macros.elm index 023d1179..85d57ce9 100644 --- a/elm/step8_macros.elm +++ b/elm/step8_macros.elm @@ -80,14 +80,6 @@ malInit = (nth xs 1) (throw "odd number of forms to cond")) (cons 'cond (rest (rest xs)))))))""" - , """(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))))))))""" ] diff --git a/elm/step9_try.elm b/elm/step9_try.elm index 44b3180c..ed7d9f71 100644 --- a/elm/step9_try.elm +++ b/elm/step9_try.elm @@ -80,14 +80,6 @@ malInit = (nth xs 1) (throw "odd number of forms to cond")) (cons 'cond (rest (rest xs)))))))""" - , """(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))))))))""" ] diff --git a/elm/stepA_mal.elm b/elm/stepA_mal.elm index fa0e4c03..2c7f72f7 100644 --- a/elm/stepA_mal.elm +++ b/elm/stepA_mal.elm @@ -81,22 +81,6 @@ malInit = (nth xs 1) (throw "odd number of forms to cond")) (cons 'cond (rest (rest xs)))))))""" - , """(def! inc (fn* [x] (+ x 1)))""" - , """(def! gensym - (let* [counter (atom 0)] - (fn* [] - (symbol (str "G__" (swap! counter inc))))))""" - , """(defmacro! or - (fn* (& xs) - (if (empty? xs) - nil - (if (= 1 (count xs)) - (first xs) - (let* (condvar (gensym)) - `(let* (~condvar ~(first xs)) - (if ~condvar - ~condvar - (or ~@(rest xs)))))))))""" ] diff --git a/erlang/src/step8_macros.erl b/erlang/src/step8_macros.erl index 8f7bccaf..07d0d5f9 100644 --- a/erlang/src/step8_macros.erl +++ b/erlang/src/step8_macros.erl @@ -20,7 +20,6 @@ init() -> eval(read("(def! not (fn* (a) (if a false true)))"), Env), eval(read("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"), Env), eval(read("(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)))))))"), Env), - eval(read("(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))))))))"), Env), Env. loop(Env) -> diff --git a/erlang/src/step9_try.erl b/erlang/src/step9_try.erl index c35da92f..8211f76b 100644 --- a/erlang/src/step9_try.erl +++ b/erlang/src/step9_try.erl @@ -20,7 +20,6 @@ init() -> eval(read("(def! not (fn* (a) (if a false true)))"), Env), eval(read("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"), Env), eval(read("(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)))))))"), Env), - eval(read("(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))))))))"), Env), Env. loop(Env) -> diff --git a/erlang/src/stepA_mal.erl b/erlang/src/stepA_mal.erl index 4ead8e9b..d4668fe4 100644 --- a/erlang/src/stepA_mal.erl +++ b/erlang/src/stepA_mal.erl @@ -22,9 +22,6 @@ init() -> eval(read("(def! not (fn* (a) (if a false true)))"), Env), eval(read("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"), Env), eval(read("(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)))))))"), Env), - eval(read("(def! inc (fn* [x] (+ x 1)))"), Env), - eval(read("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))"), Env), - eval(read("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (\~condvar \~(first xs)) (if \~condvar \~condvar (or \~@(rest xs)))))))))"), Env), Env. loop(Env) -> diff --git a/es6/step8_macros.mjs b/es6/step8_macros.mjs index 5857c7e2..45195158 100644 --- a/es6/step8_macros.mjs +++ b/es6/step8_macros.mjs @@ -128,7 +128,6 @@ env_set(repl_env, Symbol.for('*ARGV*'), []) 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.argv.length > 2) { env_set(repl_env, Symbol.for('*ARGV*'), process.argv.slice(3)) diff --git a/es6/step9_try.mjs b/es6/step9_try.mjs index 73706c3d..7cc6e962 100644 --- a/es6/step9_try.mjs +++ b/es6/step9_try.mjs @@ -139,7 +139,6 @@ env_set(repl_env, Symbol.for('*ARGV*'), []) 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.argv.length > 2) { env_set(repl_env, Symbol.for('*ARGV*'), process.argv.slice(3)) diff --git a/es6/stepA_mal.mjs b/es6/stepA_mal.mjs index 936a787f..4ae066d8 100644 --- a/es6/stepA_mal.mjs +++ b/es6/stepA_mal.mjs @@ -140,9 +140,6 @@ REP('(def! *host-language* "ecmascript6")') 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('(def! inc (fn* [x] (+ x 1)))') -REP('(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))') -REP('(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))') if (process.argv.length > 2) { env_set(repl_env, Symbol.for('*ARGV*'), process.argv.slice(3)) diff --git a/examples/exercises.mal b/examples/exercises.mal new file mode 100644 index 00000000..51e59e39 --- /dev/null +++ b/examples/exercises.mal @@ -0,0 +1,170 @@ +;; These are the answers to the questions in ../docs/exercise.md. + +;; In order to avoid unexpected circular dependencies among solutions, +;; this files attempts to be self-contained. +(def! identity (fn* [x] x)) +(def! reduce (fn* (f init xs) + (if (empty? xs) init (reduce f (f init (first xs)) (rest xs))))) +(def! foldr (fn* [f init xs] + (if (empty? xs) init (f (first xs) (foldr f init (rest xs)))))) + +;; Reimplementations. + +(def! nil? (fn* [x] (= x nil ))) +(def! true? (fn* [x] (= x true ))) +(def! false? (fn* [x] (= x false))) +(def! empty? (fn* [x] (= x [] ))) + +(def! sequential? + (fn* [x] + (if (list? x) true (vector? x)))) + +(def! > (fn* [a b] (< b a) )) +(def! <= (fn* [a b] (not (< b a)))) +(def! >= (fn* [a b] (not (< a b)))) + +(def! hash-map (fn* [& xs] (apply assoc {} xs))) +(def! list (fn* [& xs] xs)) +(def! prn (fn* [& xs] (println (apply pr-str xs)))) +(def! swap! (fn* [a f & xs] (reset! a (apply f (deref a) xs)))) + +(def! count + (fn* [xs] + (if (nil? xs) + 0 + (reduce (fn* [acc _] (+ 1 acc)) 0 xs)))) +(def! nth + (fn* [xs index] + (if (if (<= 0 index) (not (empty? xs))) ; logical and + (if (= 0 index) + (first xs) + (nth (rest xs) (- index 1))) + (throw "nth: index out of range")))) +(def! map + (fn* [f xs] + (foldr (fn* [x acc] (cons (f x) acc)) () xs))) +(def! concat + (fn* [& xs] + (foldr (fn* [xs ys] (foldr cons ys xs)) () xs))) +(def! conj + (fn* [xs & ys] + (if (vector? xs) + (apply vector (concat xs ys)) + (reduce (fn* [xs x] (cons x xs)) xs ys)))) + +(def! do2 (fn* [& xs] (nth xs (- (count xs) 1)))) +(def! do3 (fn* [& xs] (reduce (fn* [acc x] x) nil xs))) +;; do2 will probably be more efficient when lists are implemented as +;; arrays with direct indexing, but when they are implemented as +;; linked lists, do3 may win because it only does one traversal. + +(defmacro! quote (fn* [ast] (list (fn* [] ast)))) +(def! _quasiquote_iter (fn* [x acc] + (if (if (list? x) (= (first x) 'splice-unquote)) ; logical and + (list 'concat (first (rest x)) acc) + (list 'cons (list 'quasiquote x) acc)))) +(defmacro! quasiquote (fn* [ast] + (if (list? ast) + (if (= (first ast) 'unquote) + (first (rest ast)) + (foldr _quasiquote_iter () ast)) + (if (vector? ast) + ;; TODO: once tests are fixed, replace 'list with 'vector. + (list 'apply 'list (foldr _quasiquote_iter () ast)) + (list 'quote ast))))) + +(def! _letA_keys (fn* [binds] + (if (empty? binds) + () + (cons (first binds) (_letA_keys (rest (rest binds))))))) +(def! _letA_values (fn* [binds] + (if (empty? binds) + () + (_letA_keys (rest binds))))) +(def! _letA (fn* [binds form] + (cons (list 'fn* (_letA_keys binds) form) (_letA_values binds)))) +;; Fails for (let* [a 1 b (+ 1 a)] b) +(def! _letB (fn* [binds form] + (if (empty? binds) + form + (list (list 'fn* [(first binds)] (_letB (rest (rest binds)) form)) + (first (rest binds)))))) +;; Fails for (let* (cst (fn* (n) (if (= n 0) nil (cst (- n 1))))) (cst 1)) +(def! _c_combinator (fn* [x] (x x))) +(def! _d_combinator (fn* [f] (fn* [x] (f (fn* [v] ((x x) v)))))) +(def! _Y_combinator (fn* [x] (_c_combinator (_d_combinator x)))) +(def! _letC + (fn* [binds form] + (if (empty? binds) + form + (list (list 'fn* [(first binds)] (_letC (rest (rest binds)) form)) + (list '_Y_combinator (list 'fn* [(first binds)] (first (rest binds)))))))) +;; Fails for mutual recursion. +;; See http://okmij.org/ftp/Computation/fixed-point-combinators.html +;; if you are motivated to implement solution D. +(defmacro! let* _letC) + +(def! apply + ;; Replace (f a b [c d]) with ('f 'a 'b 'c 'd) then evaluate the + ;; resulting function call (the surrounding environment does not + ;; matter when evaluating a function call). + ;; Use nil as marker to detect deepest recursive call. + (let* [q (fn* [x] (list 'quote x)) + iter (fn* [x acc] + (if (nil? acc) ; x is the last element (a sequence) + (map q x) + (cons (q x) acc)))] + (fn* [& xs] (eval (foldr iter nil xs))))) + +;; Folds + +(def! sum (fn* [xs] (reduce + 0 xs))) +(def! product (fn* [xs] (reduce * 1 xs))) + +(def! conjunction + (let* [and2 (fn* [acc x] (if acc x false))] + (fn* [xs] + (reduce and2 true xs)))) +(def! disjunction + (let* [or2 (fn* [acc x] (if acc true x))] + (fn* [xs] + (reduce or2 false xs)))) +;; It would be faster to stop the iteration on first failure +;; (conjunction) or success (disjunction). Even better, `or` in the +;; stepA and `and` in `core.mal` stop evaluating their arguments. + +;; Yes, -2-3-4 means (((0-2)-3)-4). + +;; `(reduce str "" xs)` is equivalent to `apply str xs` +;; and `(reduce concat () xs)` is equivalent to `apply concat xs`. +;; The built-in iterations are probably faster. + +;; `(reduce (fn* [acc _] acc) nil xs)` is equivalent to `nil`. + +;; For (reduce (fn* [acc x] x) nil xs))), see do3 above. + +;; `(reduce (fn* [acc x] (if (< acc x) x acc)) 0 xs)` computes the +;; maximum of a list of non-negative integers. It is hard to find an +;; initial value fitting all purposes. + +(def! sum_len + (let* [add_len (fn* [acc x] (+ acc (count x)))] + (fn* [xs] + (reduce add_len 0 xs)))) +(def! max_len + (let* [update_max (fn* [acc x] (let* [l (count x)] (if (< acc l) l acc)))] + (fn* [xs] + (reduce update_max 0 xs)))) + +(def! compose + (let* [compose2 (fn* [f acc] (fn* [x] (f (acc x))))] + (fn* [& fs] + (foldr compose2 identity fs)))) +;; ((compose f1 f2) x) is equivalent to (f1 (f2 x)) +;; This is the mathematical composition. For practical purposes, `->` +;; and `->>` defined in `core.mal` are more efficient and general. + +;; This `nil` is intentional so that the result of doing `load-file` is +;; `nil` instead of whatever happens to be the last definiton. +;; FIXME: can be removed after merge of load-file-trailing-new-line-nil +nil diff --git a/factor/step8_macros/step8_macros.factor b/factor/step8_macros/step8_macros.factor index 7dda02ed..330ccb70 100755 --- a/factor/step8_macros/step8_macros.factor +++ b/factor/step8_macros/step8_macros.factor @@ -141,7 +141,6 @@ command-line get dup empty? [ rest ] unless "*ARGV*" pick set-at (def! not (fn* (a) (if a false true))) (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\"))))) (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))))))) -(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)))))))) " string-lines harvest [ REP drop ] each MAIN: main diff --git a/factor/step9_try/step9_try.factor b/factor/step9_try/step9_try.factor index 92fae103..1a596c89 100755 --- a/factor/step9_try/step9_try.factor +++ b/factor/step9_try/step9_try.factor @@ -153,7 +153,6 @@ command-line get dup empty? [ rest ] unless "*ARGV*" pick set-at (def! not (fn* (a) (if a false true))) (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\"))))) (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))))))) -(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)))))))) " string-lines harvest [ REP drop ] each MAIN: main diff --git a/factor/stepA_mal/stepA_mal.factor b/factor/stepA_mal/stepA_mal.factor index 4f964dd3..25c91d8c 100755 --- a/factor/stepA_mal/stepA_mal.factor +++ b/factor/stepA_mal/stepA_mal.factor @@ -50,8 +50,12 @@ DEFER: EVAL :: eval-try* ( params env -- maltype ) [ params first env EVAL ] [ - params second second env new-env [ env-set ] keep - params second third swap EVAL + params length 1 > [ + params second second env new-env [ env-set ] keep + params second third swap EVAL + ] [ + throw + ] if ] recover ; : args-split ( bindlist -- bindlist restbinding/f ) @@ -121,7 +125,11 @@ M: callable apply call( x -- y ) f ; : PRINT ( maltype -- str ) pr-str ; : REP ( str -- str ) - [ READ repl-env get EVAL ] [ nip ] recover PRINT ; + [ + READ repl-env get EVAL PRINT + ] [ + nip pr-str "Error: " swap append + ] recover ; : REPL ( -- ) "(println (str \"Mal [\" *host-language* \"]\"))" REP drop @@ -147,9 +155,6 @@ command-line get dup empty? [ rest ] unless "*ARGV*" pick set-at (def! not (fn* (a) (if a false true))) (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\"))))) (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))))))) -(def! inc (fn* [x] (+ x 1))) -(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc)))))) -(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs))))))))) " string-lines harvest [ READ repl-env get EVAL drop ] each MAIN: main diff --git a/fantom/src/step8_macros/fan/main.fan b/fantom/src/step8_macros/fan/main.fan index 243295c4..73310b1e 100644 --- a/fantom/src/step8_macros/fan/main.fan +++ b/fantom/src/step8_macros/fan/main.fan @@ -153,7 +153,6 @@ class Main REP("(def! not (fn* (a) (if a false true)))", repl_env) REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env) 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) - 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))))))))", repl_env) if (!args.isEmpty) { diff --git a/fantom/src/step9_try/fan/main.fan b/fantom/src/step9_try/fan/main.fan index 5a7332cc..b9f314df 100644 --- a/fantom/src/step9_try/fan/main.fan +++ b/fantom/src/step9_try/fan/main.fan @@ -165,7 +165,6 @@ class Main REP("(def! not (fn* (a) (if a false true)))", repl_env) REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env) 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) - 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))))))))", repl_env) if (!args.isEmpty) { diff --git a/fantom/src/stepA_mal/fan/main.fan b/fantom/src/stepA_mal/fan/main.fan index c50e947d..72587905 100644 --- a/fantom/src/stepA_mal/fan/main.fan +++ b/fantom/src/stepA_mal/fan/main.fan @@ -166,9 +166,6 @@ class Main REP("(def! not (fn* (a) (if a false true)))", repl_env) REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env) 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) - REP("(def! inc (fn* [x] (+ x 1)))", repl_env) - REP("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))", repl_env) - REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))", repl_env) if (!args.isEmpty) { diff --git a/forth/step8_macros.fs b/forth/step8_macros.fs index bbdc4526..fffee717 100644 --- a/forth/step8_macros.fs +++ b/forth/step8_macros.fs @@ -310,7 +310,6 @@ defcore swap! { argv argc -- val } s\" (def! not (fn* (x) (if x false true)))" rep 2drop s\" (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" rep 2drop s\" (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 2drop -s\" (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))))))))" rep 2drop : repl ( -- ) begin diff --git a/forth/step9_try.fs b/forth/step9_try.fs index 07ee8b8b..d30afadb 100644 --- a/forth/step9_try.fs +++ b/forth/step9_try.fs @@ -353,7 +353,6 @@ defcore map ( argv argc -- list ) s\" (def! not (fn* (x) (if x false true)))" rep 2drop s\" (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" rep 2drop s\" (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 2drop -s\" (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))))))))" rep 2drop : repl ( -- ) begin diff --git a/forth/stepA_mal.fs b/forth/stepA_mal.fs index 994a9a0d..28979483 100644 --- a/forth/stepA_mal.fs +++ b/forth/stepA_mal.fs @@ -361,9 +361,6 @@ s\" (def! *host-language* \"forth\")" rep 2drop s\" (def! not (fn* (x) (if x false true)))" rep 2drop s\" (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" rep 2drop s\" (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 2drop -s\" (def! inc (fn* [x] (+ x 1)))" rep 2drop -s\" (def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))" rep 2drop -s\" (defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))" rep 2drop : repl ( -- ) s\" (println (str \"Mal [\" *host-language* \"]\"))" rep 2drop diff --git a/fsharp/step8_macros.fs b/fsharp/step8_macros.fs index f893f52b..644114b4 100644 --- a/fsharp/step8_macros.fs +++ b/fsharp/step8_macros.fs @@ -186,7 +186,6 @@ module REPL RE env """ (def! not (fn* (a) (if a false true))) (def! load-file (fn* (f) (eval (read-string (slurp f))))) - (defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_ ~(first xs)) (if or_ or_ (or ~@(rest xs)))))))) (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))))))) """ |> Seq.iter ignore diff --git a/fsharp/step9_try.fs b/fsharp/step9_try.fs index 6bf6549e..c001836b 100644 --- a/fsharp/step9_try.fs +++ b/fsharp/step9_try.fs @@ -206,7 +206,6 @@ module REPL RE env """ (def! not (fn* (a) (if a false true))) (def! load-file (fn* (f) (eval (read-string (slurp f))))) - (defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_ ~(first xs)) (if or_ or_ (or ~@(rest xs)))))))) (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))))))) """ |> Seq.iter ignore diff --git a/fsharp/stepA_mal.fs b/fsharp/stepA_mal.fs index f2d40c57..621657b2 100644 --- a/fsharp/stepA_mal.fs +++ b/fsharp/stepA_mal.fs @@ -218,9 +218,6 @@ module REPL (def! not (fn* (a) (if a false true))) (def! load-file (fn* (f) (eval (read-string (slurp f))))) (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))))))) - (def! inc (fn* [x] (+ x 1))) - (def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str "G__" (swap! counter inc)))))) - (defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs))))))))) """ |> Seq.iter ignore env diff --git a/gnu-smalltalk/step8_macros.st b/gnu-smalltalk/step8_macros.st index 602e4441..cd45f841 100644 --- a/gnu-smalltalk/step8_macros.st +++ b/gnu-smalltalk/step8_macros.st @@ -270,9 +270,7 @@ replEnv set: #'*ARGV*' value: (MALList new: argv). MAL rep: '(def! not (fn* (a) (if a false true)))' env: replEnv. MAL rep: '(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")")))))' env: replEnv. - MAL 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)))))))' env: replEnv. -MAL 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))))))))' env: replEnv. Smalltalk arguments notEmpty ifTrue: [ MAL rep: '(load-file "', Smalltalk arguments first, '")' env: replEnv diff --git a/gnu-smalltalk/step9_try.st b/gnu-smalltalk/step9_try.st index 39022321..c5e22fcd 100644 --- a/gnu-smalltalk/step9_try.st +++ b/gnu-smalltalk/step9_try.st @@ -291,9 +291,7 @@ replEnv set: #'*ARGV*' value: (MALList new: argv). MAL rep: '(def! not (fn* (a) (if a false true)))' env: replEnv. MAL rep: '(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")")))))' env: replEnv. - MAL 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)))))))' env: replEnv. -MAL 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))))))))' env: replEnv. Smalltalk arguments notEmpty ifTrue: [ MAL rep: '(load-file "', Smalltalk arguments first, '")' env: replEnv diff --git a/gnu-smalltalk/stepA_mal.st b/gnu-smalltalk/stepA_mal.st index dd8db1da..67dcd2de 100644 --- a/gnu-smalltalk/stepA_mal.st +++ b/gnu-smalltalk/stepA_mal.st @@ -292,11 +292,7 @@ replEnv set: #'*host-language*' value: (MALString new: 'smalltalk'). MAL rep: '(def! not (fn* (a) (if a false true)))' env: replEnv. MAL rep: '(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")")))))' env: replEnv. - MAL 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)))))))' env: replEnv. -MAL rep: '(def! inc (fn* [x] (+ x 1)))' env: replEnv. -MAL rep: '(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str "G__" (swap! counter inc))))))' env: replEnv. -MAL rep: '(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))' env: replEnv. Smalltalk arguments notEmpty ifTrue: [ MAL rep: '(load-file "', Smalltalk arguments first, '")' env: replEnv diff --git a/go/src/step8_macros/step8_macros.go b/go/src/step8_macros/step8_macros.go index 4b20938b..9a8ffa6d 100644 --- a/go/src/step8_macros/step8_macros.go +++ b/go/src/step8_macros/step8_macros.go @@ -311,7 +311,6 @@ func main() { 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))))))))") // called with mal script to load and eval if len(os.Args) > 1 { diff --git a/go/src/step9_try/step9_try.go b/go/src/step9_try/step9_try.go index 7902889e..52f939a0 100644 --- a/go/src/step9_try/step9_try.go +++ b/go/src/step9_try/step9_try.go @@ -339,7 +339,6 @@ func main() { 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))))))))") // called with mal script to load and eval if len(os.Args) > 1 { diff --git a/go/src/stepA_mal/stepA_mal.go b/go/src/stepA_mal/stepA_mal.go index 60a29084..223f46d9 100644 --- a/go/src/stepA_mal/stepA_mal.go +++ b/go/src/stepA_mal/stepA_mal.go @@ -340,9 +340,6 @@ func main() { 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("(def! inc (fn* [x] (+ x 1)))") - rep("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))") - rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") // called with mal script to load and eval if len(os.Args) > 1 { diff --git a/groovy/step8_macros.groovy b/groovy/step8_macros.groovy index 24b1a90a..8ede17d8 100644 --- a/groovy/step8_macros.groovy +++ b/groovy/step8_macros.groovy @@ -150,7 +150,6 @@ repl_env.set(new MalSymbol("*ARGV*"), this.args as List) 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 (this.args.size() > 0) { diff --git a/groovy/step9_try.groovy b/groovy/step9_try.groovy index c921bd6d..bef6e2c7 100644 --- a/groovy/step9_try.groovy +++ b/groovy/step9_try.groovy @@ -168,7 +168,6 @@ repl_env.set(new MalSymbol("*ARGV*"), this.args as List) 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 (this.args.size() > 0) { diff --git a/groovy/stepA_mal.groovy b/groovy/stepA_mal.groovy index 14c01a3c..8485e40c 100644 --- a/groovy/stepA_mal.groovy +++ b/groovy/stepA_mal.groovy @@ -169,10 +169,6 @@ REP("(def! *host-language* \"groovy\")") 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("(def! inc (fn* [x] (+ x 1)))"); -REP("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))"); -REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))"); - if (this.args.size() > 0) { repl_env.set(new MalSymbol("*ARGV*"), this.args.drop(1) as List) diff --git a/guile/core.scm b/guile/core.scm index fca20dbe..5831bc7d 100644 --- a/guile/core.scm +++ b/guile/core.scm @@ -150,7 +150,7 @@ ((callable? c) (let ((cc (make-callable ht (callable-unbox c) - (and (hash-table? ht) (hash-ref ht "ismacro")) + #f (callable-closure c)))) cc)) (else diff --git a/guile/step5_tco.scm b/guile/step5_tco.scm index 909aa8b5..67a29638 100644 --- a/guile/step5_tco.scm +++ b/guile/step5_tco.scm @@ -130,9 +130,4 @@ (EVAL-string "(def! not (fn* (x) (if x false true)))") -;; NOTE: we have to reduce stack size to pass step5 test -((@ (system vm vm) call-with-stack-overflow-handler) - 1024 - (lambda () (REPL)) - (lambda k (throw 'mal-error "stack overflow"))) - +(REPL) diff --git a/guile/step8_macros.scm b/guile/step8_macros.scm index 77f1a1ac..39c46895 100644 --- a/guile/step8_macros.scm +++ b/guile/step8_macros.scm @@ -163,7 +163,6 @@ (EVAL-string "(def! not (fn* (x) (if x false true)))") (EVAL-string "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") (EVAL-string "(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)))))))") -(EVAL-string "(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))))))))") (let ((args (cdr (command-line)))) (cond diff --git a/guile/step9_try.scm b/guile/step9_try.scm index 83b90751..5aec65b1 100644 --- a/guile/step9_try.scm +++ b/guile/step9_try.scm @@ -186,7 +186,6 @@ (EVAL-string "(def! not (fn* (x) (if x false true)))") (EVAL-string "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") (EVAL-string "(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)))))))") -(EVAL-string "(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))))))))") (let ((args (cdr (command-line)))) (cond diff --git a/guile/stepA_mal.scm b/guile/stepA_mal.scm index 17438a9e..894f1478 100644 --- a/guile/stepA_mal.scm +++ b/guile/stepA_mal.scm @@ -183,9 +183,6 @@ (EVAL-string "(def! not (fn* (x) (if x false true)))") (EVAL-string "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") (EVAL-string "(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)))))))") -(EVAL-string "(def! inc (fn* [x] (+ x 1)))") -(EVAL-string "(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))") -(EVAL-string "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") (EVAL-string "(def! *host-language* \"guile\")") (let ((args (cdr (command-line)))) diff --git a/guile/tests/step5_tco.mal b/guile/tests/step5_tco.mal deleted file mode 100644 index d20df25d..00000000 --- a/guile/tests/step5_tco.mal +++ /dev/null @@ -1,15 +0,0 @@ -;; Test recursive non-tail call function - -(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1)))))) - -(sum-to 10) -;=>55 - -;;; no try* yet, so test completion of side-effects -(def! res1 nil) -;=>nil -;;; For implementations without their own TCO this should fail and -;;; leave res1 unchanged -(def! res1 (sum-to 10000)) -res1 -;=>nil diff --git a/haskell/Core.hs b/haskell/Core.hs index 995e4dc4..456ff3f1 100644 --- a/haskell/Core.hs +++ b/haskell/Core.hs @@ -3,313 +3,384 @@ module Core where import System.IO (hFlush, stdout) -import Control.Exception (catch) +import Control.Monad.Except (throwError) import Control.Monad.Trans (liftIO) import qualified Data.Map as Map +import Data.Foldable (foldlM) import Data.Time.Clock.POSIX (getPOSIXTime) -import Data.IORef (IORef, newIORef, readIORef, writeIORef) +import Data.IORef (newIORef, readIORef, writeIORef) import Readline (readline) import Reader (read_str) import Types -import Printer (_pr_str, _pr_list) +import Printer (_pr_list) -- General functions -equal_Q [a, b] = return $ if a == b then MalTrue else MalFalse +equal_Q :: Fn +equal_Q [a, b] = return $ MalBoolean $ a == b equal_Q _ = throwStr "illegal arguments to =" -run_1 :: (MalVal -> MalVal) -> [MalVal] -> IOThrows MalVal -run_1 f (x:[]) = return $ f x -run_1 _ _ = throwStr "function takes a single argument" - -run_2 :: (MalVal -> MalVal -> MalVal) -> [MalVal] -> IOThrows MalVal -run_2 f (x:y:[]) = return $ f x y -run_2 _ _ = throwStr "function takes a two arguments" - -- Error/Exception functions -throw (mv:[]) = throwMalVal mv +throw :: Fn +throw [mv] = throwError mv throw _ = throwStr "illegal arguments to throw" +-- Unary predicates + +pred1 :: (MalVal -> Bool) -> Fn +pred1 hostPred [x] = return $ MalBoolean $ hostPred x +pred1 _ _ = throwStr "illegal call to unary predicate" + +atom_Q :: MalVal -> Bool +atom_Q (MalAtom _ _) = True +atom_Q _ = False + +false_Q :: MalVal -> Bool +false_Q (MalBoolean False) = True +false_Q _ = False + +fn_Q :: MalVal -> Bool +fn_Q (MalFunction {macro=False}) = True +fn_Q _ = False + +macro_Q :: MalVal -> Bool +macro_Q (MalFunction {macro=True}) = True +macro_Q _ = False + +map_Q :: MalVal -> Bool +map_Q (MalHashMap _ _) = True +map_Q _ = False + +keyword_Q :: MalVal -> Bool +keyword_Q (MalString (c : _)) = c == keywordMagic +keyword_Q _ = False + +list_Q :: MalVal -> Bool +list_Q (MalSeq _ (Vect False) _) = True +list_Q _ = False + +nil_Q :: MalVal -> Bool +nil_Q Nil = True +nil_Q _ = False + +number_Q :: MalVal -> Bool +number_Q (MalNumber _) = True +number_Q _ = False + +string_Q :: MalVal -> Bool +string_Q (MalString "") = True +string_Q (MalString (c : _)) = c /= keywordMagic +string_Q _ = False + +symbol_Q :: MalVal -> Bool +symbol_Q (MalSymbol _) = True +symbol_Q _ = False + +true_Q :: MalVal -> Bool +true_Q (MalBoolean True) = True +true_Q _ = False + +vector_Q :: MalVal -> Bool +vector_Q (MalSeq _ (Vect True) _) = True +vector_Q _ = False + -- Scalar functions -symbol (MalString str:[]) = return $ MalSymbol str +symbol :: Fn +symbol [MalString s] = return $ MalSymbol s symbol _ = throwStr "symbol called with non-string" -keyword (MalString ('\x029e':str):[]) = return $ MalString $ "\x029e" ++ str -keyword (MalString str:[]) = return $ MalString $ "\x029e" ++ str +keyword :: Fn +keyword [kw@(MalString (c : _))] | c == keywordMagic = return kw +keyword [MalString s] = return $ MalString (keywordMagic : s) keyword _ = throwStr "keyword called with non-string" - -- String functions -pr_str args = do - return $ MalString $ _pr_list True " " args +pr_str :: Fn +pr_str args = liftIO $ MalString <$> _pr_list True " " args -str args = do - return $ MalString $ _pr_list False "" args +str :: Fn +str args = liftIO $ MalString <$> _pr_list False "" args -prn args = do - liftIO $ putStrLn $ _pr_list True " " args - liftIO $ hFlush stdout +prn :: Fn +prn args = liftIO $ do + putStrLn =<< _pr_list True " " args + hFlush stdout return Nil -println args = do - liftIO $ putStrLn $ _pr_list False " " args - liftIO $ hFlush stdout +println :: Fn +println args = liftIO $ do + putStrLn =<< _pr_list False " " args + hFlush stdout return Nil -slurp ([MalString path]) = do - str <- liftIO $ readFile path - return $ MalString str +slurp :: Fn +slurp [MalString path] = MalString <$> liftIO (readFile path) slurp _ = throwStr "invalid arguments to slurp" -do_readline ([MalString prompt]) = do - str <- liftIO $ readline prompt - case str of +do_readline :: Fn +do_readline [MalString prompt] = do + maybeLine <- liftIO $ readline prompt + case maybeLine of Nothing -> throwStr "readline failed" - Just str -> return $ MalString str + Just line -> return $ MalString line do_readline _ = throwStr "invalid arguments to readline" +read_string :: Fn +read_string [MalString s] = read_str s +read_string _ = throwStr "invalid read-string" + -- Numeric functions -num_op op [MalNumber a, MalNumber b] = do - return $ MalNumber $ op a b +num_op :: (Int -> Int -> Int) -> Fn +num_op op [MalNumber a, MalNumber b] = return $ MalNumber $ op a b num_op _ _ = throwStr "illegal arguments to number operation" -cmp_op op [MalNumber a, MalNumber b] = do - return $ if op a b then MalTrue else MalFalse +cmp_op :: (Int -> Int -> Bool) -> Fn +cmp_op op [MalNumber a, MalNumber b] = return $ MalBoolean $ op a b cmp_op _ _ = throwStr "illegal arguments to comparison operation" -time_ms _ = do - t <- liftIO $ getPOSIXTime - return $ MalNumber $ round (t * 1000) +time_ms :: Fn +time_ms [] = MalNumber . round . (* 1000) <$> liftIO getPOSIXTime +time_ms _ = throwStr "invalid time-ms" -- List functions -list args = return $ MalList args Nil +list :: Fn +list = return . toList -- Vector functions -vector args = return $ MalVector args Nil +vector :: Fn +vector = return . MalSeq (MetaData Nil) (Vect True) -- Hash Map functions -_pairup [x] = throwStr "Odd number of elements to _pairup" -_pairup [] = return [] -_pairup (MalString x:y:xs) = do - rest <- _pairup xs - return $ (x,y):rest +hash_map :: Fn +hash_map kvs = + case keyValuePairs kvs of + Just pairs -> return $ MalHashMap (MetaData Nil) $ Map.fromList pairs + Nothing -> throwStr "invalid call to hash-map" -hash_map args = do - pairs <- _pairup args - return $ MalHashMap (Map.fromList pairs) Nil - -assoc (MalHashMap hm _:kvs) = do - pairs <- _pairup kvs - return $ MalHashMap (Map.union (Map.fromList pairs) hm) Nil +assoc :: Fn +assoc (MalHashMap _ hm : kvs) = + case keyValuePairs kvs of + Just pairs -> return $ MalHashMap (MetaData Nil) $ Map.union (Map.fromList pairs) hm + Nothing -> throwStr "invalid assoc" assoc _ = throwStr "invalid call to assoc" -dissoc (MalHashMap hm _:ks) = do - let remover = (\hm (MalString k) -> Map.delete k hm) in - return $ MalHashMap (foldl remover hm ks) Nil +remover :: Map.Map String MalVal -> MalVal -> IOThrows (Map.Map String MalVal) +remover m (MalString k) = return $ Map.delete k m +remover _ _ = throwStr "invalid dissoc" + +dissoc :: Fn +dissoc (MalHashMap _ hm : ks) = MalHashMap (MetaData Nil) <$> foldlM remover hm ks dissoc _ = throwStr "invalid call to dissoc" -get (MalHashMap hm _:MalString k:[]) = do +get :: Fn +get [MalHashMap _ hm, MalString k] = case Map.lookup k hm of Just mv -> return mv Nothing -> return Nil -get (Nil:MalString k:[]) = return Nil +get [Nil, MalString _] = return Nil get _ = throwStr "invalid call to get" -contains_Q (MalHashMap hm _:MalString k:[]) = do - if Map.member k hm then return MalTrue - else return MalFalse -contains_Q (Nil:MalString k:[]) = return MalFalse +contains_Q :: Fn +contains_Q [MalHashMap _ hm, MalString k] = return $ MalBoolean $ Map.member k hm +contains_Q [Nil, MalString _] = return $ MalBoolean False contains_Q _ = throwStr "invalid call to contains?" -keys (MalHashMap hm _:[]) = do - return $ MalList (map MalString (Map.keys hm)) Nil +keys :: Fn +keys [MalHashMap _ hm] = return $ toList $ MalString <$> Map.keys hm keys _ = throwStr "invalid call to keys" -vals (MalHashMap hm _:[]) = do - return $ MalList (Map.elems hm) Nil +vals :: Fn +vals [MalHashMap _ hm] = return $ toList $ Map.elems hm vals _ = throwStr "invalid call to vals" - -- Sequence functions -_sequential_Q (MalList _ _) = MalTrue -_sequential_Q (MalVector _ _) = MalTrue -_sequential_Q _ = MalFalse +sequential_Q :: MalVal -> Bool +sequential_Q (MalSeq _ _ _) = True +sequential_Q _ = False -cons x Nil = MalList [x] Nil -cons x (MalList lst _) = MalList (x:lst) Nil -cons x (MalVector lst _) = MalList (x:lst) Nil +cons :: Fn +cons [x, Nil ] = return $ toList [x] +cons [x, MalSeq _ _ lst] = return $ toList (x : lst) +cons _ = throwStr "illegal call to cons" -concat1 a (MalList lst _) = a ++ lst -concat1 a (MalVector lst _) = a ++ lst -do_concat args = return $ MalList (foldl concat1 [] args) Nil +unwrapSeq :: MalVal -> IOThrows [MalVal] +unwrapSeq (MalSeq _ _ xs) = return xs +unwrapSeq _ = throwStr "invalid concat" -nth ((MalList lst _):(MalNumber idx):[]) = do - if idx < length lst then return $ lst !! idx - else throwStr "nth: index out of range" -nth ((MalVector lst _):(MalNumber idx):[]) = do - if idx < length lst then return $ lst !! idx - else throwStr "nth: index out of range" +do_concat :: Fn +do_concat args = toList . concat <$> mapM unwrapSeq args + +nth :: Fn +nth [MalSeq _ _ lst, MalNumber idx] = + case drop idx lst of + x : _ -> return x + [] -> throwStr "nth: index out of range" +-- See https://wiki.haskell.org/Avoiding_partial_functions nth _ = throwStr "invalid call to nth" -first Nil = Nil -first (MalList lst _) = if length lst > 0 then lst !! 0 else Nil -first (MalVector lst _) = if length lst > 0 then lst !! 0 else Nil +first :: Fn +first [Nil ] = return Nil +first [MalSeq _ _ [] ] = return Nil +first [MalSeq _ _ (x : _)] = return x +first _ = throwStr "illegal call to first" -rest Nil = MalList [] Nil -rest (MalList lst _) = MalList (drop 1 lst) Nil -rest (MalVector lst _) = MalList (drop 1 lst) Nil +rest :: Fn +rest [Nil ] = return $ toList [] +rest [MalSeq _ _ [] ] = return $ toList [] +rest [MalSeq _ _ (_ : xs)] = return $ toList xs +rest _ = throwStr "illegal call to rest" -empty_Q Nil = MalTrue -empty_Q (MalList [] _) = MalTrue -empty_Q (MalVector [] _) = MalTrue -empty_Q _ = MalFalse +empty_Q :: MalVal -> Bool +empty_Q Nil = True +empty_Q (MalSeq _ _ []) = True +empty_Q _ = False -count (Nil:[]) = return $ MalNumber 0 -count (MalList lst _:[]) = return $ MalNumber $ length lst -count (MalVector lst _:[]) = return $ MalNumber $ length lst -count _ = throwStr $ "non-sequence passed to count" +count :: Fn +count [Nil ] = return $ MalNumber 0 +count [MalSeq _ _ lst] = return $ MalNumber $ length lst +count _ = throwStr "non-sequence passed to count" -apply args = do - f <- _get_call args - lst <- _to_list (last args) - f $ (init (drop 1 args)) ++ lst +concatLast :: [MalVal] -> IOThrows [MalVal] +concatLast [MalSeq _ _ lst] = return lst +concatLast (a : as) = (a :) <$> concatLast as +concatLast _ = throwStr "last argument of apply must be a sequence" -do_map args = do - f <- _get_call args - lst <- _to_list (args !! 1) - do new_lst <- mapM (\x -> f [x]) lst - return $ MalList new_lst Nil +apply :: Fn +apply (MalFunction {fn=f} : xs) = f =<< concatLast xs +apply _ = throwStr "Illegal call to apply" -conj ((MalList lst _):args) = return $ MalList ((reverse args) ++ lst) Nil -conj ((MalVector lst _):args) = return $ MalVector (lst ++ args) Nil -conj _ = throwStr $ "illegal arguments to conj" +do_map :: Fn +do_map [MalFunction {fn=f}, MalSeq _ _ args] = toList <$> mapM (\x -> f [x]) args +do_map _ = throwStr "Illegal call to map" -do_seq (l@(MalList [] _):[]) = return $ Nil -do_seq (l@(MalList lst m):[]) = return $ l -do_seq (MalVector [] _:[]) = return $ Nil -do_seq (MalVector lst _:[]) = return $ MalList lst Nil -do_seq (MalString []:[]) = return $ Nil -do_seq (MalString s:[]) = return $ MalList [MalString [c] | c <- s] Nil -do_seq (Nil:[]) = return $ Nil -do_seq _ = throwStr $ "seq: called on non-sequence" +conj :: Fn +conj (MalSeq _ (Vect False) lst : args) = return $ toList $ reverse args ++ lst +conj (MalSeq _ (Vect True) lst : args) = return $ MalSeq (MetaData Nil) (Vect True) $ lst ++ args +conj _ = throwStr "illegal arguments to conj" + +do_seq :: Fn +do_seq [Nil ] = return Nil +do_seq [MalSeq _ _ [] ] = return Nil +do_seq [MalSeq _ _ lst ] = return $ toList lst +do_seq [MalString "" ] = return Nil +do_seq [MalString s ] = return $ toList $ MalString <$> pure <$> s +do_seq _ = throwStr "seq: called on non-sequence" -- Metadata functions -with_meta ((MalList lst _):m:[]) = return $ MalList lst m -with_meta ((MalVector lst _):m:[]) = return $ MalVector lst m -with_meta ((MalHashMap hm _):m:[]) = return $ MalHashMap hm m -with_meta ((MalAtom atm _):m:[]) = return $ MalAtom atm m -with_meta ((Func f _):m:[]) = return $ Func f m -with_meta ((MalFunc {fn=f, ast=a, env=e, params=p, macro=mc}):m:[]) = do - return $ MalFunc {fn=f, ast=a, env=e, params=p, macro=mc, meta=m} -with_meta _ = throwStr $ "invalid with-meta call" +with_meta :: Fn +with_meta [MalSeq _ v x, m] = return $ MalSeq (MetaData m) v x +with_meta [MalHashMap _ x, m] = return $ MalHashMap (MetaData m) x +with_meta [MalAtom _ x, m] = return $ MalAtom (MetaData m) x +with_meta [f@(MalFunction {}), m] = return $ f {meta=m} +with_meta _ = throwStr "invalid with-meta call" -do_meta ((MalList _ m):[]) = return m -do_meta ((MalVector _ m):[]) = return m -do_meta ((MalHashMap _ m):[]) = return m -do_meta ((MalAtom _ m):[]) = return m -do_meta ((Func _ m):[]) = return m -do_meta ((MalFunc {meta=m}):[]) = return m -do_meta _ = throwStr $ "invalid meta call" +do_meta :: Fn +do_meta [MalSeq (MetaData m) _ _ ] = return m +do_meta [MalHashMap (MetaData m) _] = return m +do_meta [MalAtom (MetaData m) _ ] = return m +do_meta [MalFunction {meta=m} ] = return m +do_meta _ = throwStr "invalid meta call" -- Atom functions -atom (val:[]) = do - ref <- liftIO $ newIORef val - return $ MalAtom ref Nil +atom :: Fn +atom [val] = MalAtom (MetaData Nil) <$> liftIO (newIORef val) atom _ = throwStr "invalid atom call" -deref (MalAtom ref _:[]) = do - val <- liftIO $ readIORef ref - return val +deref :: Fn +deref [MalAtom _ ref] = liftIO $ readIORef ref deref _ = throwStr "invalid deref call" -reset_BANG (MalAtom ref _:val:[]) = do +reset_BANG :: Fn +reset_BANG [MalAtom _ ref, val] = do liftIO $ writeIORef ref $ val return val -reset_BANG _ = throwStr "invalid deref call" +reset_BANG _ = throwStr "invalid reset!" -swap_BANG (MalAtom ref _:args) = do +swap_BANG :: Fn +swap_BANG (MalAtom _ ref : MalFunction {fn=f} : args) = do val <- liftIO $ readIORef ref - f <- _get_call args - new_val <- f $ [val] ++ (tail args) - _ <- liftIO $ writeIORef ref $ new_val + new_val <- f (val : args) + liftIO $ writeIORef ref new_val return new_val +swap_BANG _ = throwStr "Illegal swap!" +ns :: [(String, Fn)] ns = [ - ("=", _func equal_Q), - ("throw", _func throw), - ("nil?", _func $ run_1 $ _nil_Q), - ("true?", _func $ run_1 $ _true_Q), - ("false?", _func $ run_1 $ _false_Q), - ("string?", _func $ run_1 $ _string_Q), - ("symbol", _func $ symbol), - ("symbol?", _func $ run_1 $ _symbol_Q), - ("keyword", _func $ keyword), - ("keyword?", _func $ run_1 $ _keyword_Q), - ("number?", _func $ run_1 $ _number_Q), - ("fn?", _func $ run_1 $ _fn_Q), - ("macro?", _func $ run_1 $ _macro_Q), + ("=", equal_Q), + ("throw", throw), + ("nil?", pred1 nil_Q), + ("true?", pred1 true_Q), + ("false?", pred1 false_Q), + ("string?", pred1 string_Q), + ("symbol", symbol), + ("symbol?", pred1 symbol_Q), + ("keyword", keyword), + ("keyword?", pred1 keyword_Q), + ("number?", pred1 number_Q), + ("fn?", pred1 fn_Q), + ("macro?", pred1 macro_Q), - ("pr-str", _func pr_str), - ("str", _func str), - ("prn", _func prn), - ("println", _func println), - ("readline", _func do_readline), - ("read-string", _func (\[(MalString s)] -> read_str s)), - ("slurp", _func slurp), + ("pr-str", pr_str), + ("str", str), + ("prn", prn), + ("println", println), + ("readline", do_readline), + ("read-string", read_string), + ("slurp", slurp), - ("<", _func $ cmp_op (<)), - ("<=", _func $ cmp_op (<=)), - (">", _func $ cmp_op (>)), - (">=", _func $ cmp_op (>=)), - ("+", _func $ num_op (+)), - ("-", _func $ num_op (-)), - ("*", _func $ num_op (*)), - ("/", _func $ num_op (div)), - ("time-ms", _func $ time_ms), + ("<", cmp_op (<)), + ("<=", cmp_op (<=)), + (">", cmp_op (>)), + (">=", cmp_op (>=)), + ("+", num_op (+)), + ("-", num_op (-)), + ("*", num_op (*)), + ("/", num_op (div)), + ("time-ms", time_ms), - ("list", _func $ list), - ("list?", _func $ run_1 _list_Q), - ("vector", _func $ vector), - ("vector?", _func $ run_1 _vector_Q), - ("hash-map", _func $ hash_map), - ("map?", _func $ run_1 _hash_map_Q), - ("assoc", _func $ assoc), - ("dissoc", _func $ dissoc), - ("get", _func $ get), - ("contains?",_func $ contains_Q), - ("keys", _func $ keys), - ("vals", _func $ vals), + ("list", list), + ("list?", pred1 list_Q), + ("vector", vector), + ("vector?", pred1 vector_Q), + ("hash-map", hash_map), + ("map?", pred1 map_Q), + ("assoc", assoc), + ("dissoc", dissoc), + ("get", get), + ("contains?", contains_Q), + ("keys", keys), + ("vals", vals), - ("sequential?", _func $ run_1 _sequential_Q), - ("cons", _func $ run_2 $ cons), - ("concat", _func $ do_concat), - ("nth", _func nth), - ("first", _func $ run_1 $ first), - ("rest", _func $ run_1 $ rest), - ("empty?", _func $ run_1 $ empty_Q), - ("count", _func $ count), - ("apply", _func $ apply), - ("map", _func $ do_map), + ("sequential?", pred1 sequential_Q), + ("cons", cons), + ("concat", do_concat), + ("nth", nth), + ("first", first), + ("rest", rest), + ("empty?", pred1 empty_Q), + ("count", count), + ("apply", apply), + ("map", do_map), - ("conj", _func $ conj), - ("seq", _func $ do_seq), + ("conj", conj), + ("seq", do_seq), - ("with-meta", _func $ with_meta), - ("meta", _func $ do_meta), - ("atom", _func $ atom), - ("atom?", _func $ run_1 _atom_Q), - ("deref", _func $ deref), - ("reset!", _func $ reset_BANG), - ("swap!", _func $ swap_BANG)] + ("with-meta", with_meta), + ("meta", do_meta), + ("atom", atom), + ("atom?", pred1 atom_Q), + ("deref", deref), + ("reset!", reset_BANG), + ("swap!", swap_BANG)] diff --git a/haskell/Env.hs b/haskell/Env.hs index 3dfd2c83..a760d197 100644 --- a/haskell/Env.hs +++ b/haskell/Env.hs @@ -1,65 +1,36 @@ module Env -( Env, env_new, null_env, env_bind, env_find, env_get, env_set ) +( Env, env_new, env_bind, env_get, env_set ) where -import Data.IORef (IORef, newIORef, readIORef, writeIORef) -import Control.Monad.Trans (liftIO) -import Data.List (elemIndex) +import Data.IORef (modifyIORef, newIORef, readIORef) import qualified Data.Map as Map import Types -import Printer --- These Env types are defined in Types module to avoid dep cycle ---data EnvData = EnvPair (Maybe Env, (Map.Map String MalVal)) ---type Env = IORef EnvData +-- The Env type si defined in Types module to avoid dep cycle. -env_new :: Maybe Env -> IO Env -env_new outer = newIORef $ EnvPair (outer, (Map.fromList [])) +env_new :: Env -> IO Env +env_new outer = (: outer) <$> newIORef (Map.fromList []) -null_env = env_new Nothing +-- True means that the actual arguments match the signature. +env_bind :: Env -> [String] -> [MalVal] -> IO Bool +env_bind env (k : ks) (v : vs) | k /= "&" = do + env_set env k v + env_bind env ks vs +env_bind env ["&", k] vs = do + env_set env k $ toList vs + return True +env_bind _ [] [] = return True +env_bind _ _ _ = return False -env_bind :: Env -> [MalVal] -> [MalVal] -> IO Env -env_bind envRef binds exprs = do - case (elemIndex (MalSymbol "&") binds) of - Nothing -> do - -- bind binds to exprs - _ <- mapM (\(b,e) -> env_set envRef b e) $ zip binds exprs - return envRef - Just idx -> do - -- Varargs binding - _ <- mapM (\(b,e) -> env_set envRef b e) $ - zip (take idx binds) (take idx exprs) - _ <- env_set envRef (binds !! (idx + 1)) - (MalList (drop idx exprs) Nil) - return envRef +env_get :: Env -> String -> IO (Maybe MalVal) +env_get [] _ = return Nothing +env_get (ref : outer) key = do + hm <- readIORef ref + case Map.lookup key hm of + Nothing -> env_get outer key + justVal -> return justVal -env_find :: Env -> MalVal -> IO (Maybe Env) -env_find envRef sym@(MalSymbol key) = do - e <- readIORef envRef - case e of - EnvPair (o, m) -> case Map.lookup key m of - Nothing -> case o of - Nothing -> return Nothing - Just o -> env_find o sym - Just val -> return $ Just envRef - -env_get :: Env -> MalVal -> IOThrows MalVal -env_get envRef sym@(MalSymbol key) = do - e1 <- liftIO $ env_find envRef sym - case e1 of - Nothing -> throwStr $ "'" ++ key ++ "' not found" - Just eRef -> do - e2 <- liftIO $ readIORef eRef - case e2 of - EnvPair (o,m) -> case Map.lookup key m of - Nothing -> throwStr $ "env_get error" - Just val -> return val - - -env_set :: Env -> MalVal -> MalVal -> IO MalVal -env_set envRef (MalSymbol key) val = do - e <- readIORef envRef - case e of - EnvPair (o,m) -> writeIORef envRef $ EnvPair (o, (Map.insert key val m)) - return val +env_set :: Env -> String -> MalVal -> IO () +env_set (ref : _) key val = modifyIORef ref $ Map.insert key val +env_set [] _ _ = error "assertion failed in env_set" diff --git a/haskell/Makefile b/haskell/Makefile index 6b1fd07a..dc4c3100 100644 --- a/haskell/Makefile +++ b/haskell/Makefile @@ -3,6 +3,7 @@ SRCS = step0_repl.hs step1_read_print.hs step2_eval.hs step3_env.hs \ step8_macros.hs step9_try.hs stepA_mal.hs OTHER_SRCS = Readline.hs Types.hs Reader.hs Printer.hs Env.hs Core.hs BINS = $(SRCS:%.hs=%) +ghc_flags = -Wall ##################### @@ -14,7 +15,7 @@ mal: $(word $(words $(BINS)),$(BINS)) cp $< $@ $(BINS): %: %.hs $(OTHER_SRCS) - ghc --make $< -o $@ + ghc ${ghc_flags} --make $< -o $@ clean: rm -f $(BINS) mal *.hi *.o diff --git a/haskell/Printer.hs b/haskell/Printer.hs index e24695fd..ae41623e 100644 --- a/haskell/Printer.hs +++ b/haskell/Printer.hs @@ -4,44 +4,45 @@ where import qualified Data.Map as Map import Data.IORef (readIORef) -import System.IO.Unsafe (unsafePerformIO) import Types ---concat (map (++ delim) list) ---join [] delim = [] ---join (x:xs) delim = x ++ delim ++ join xs delim - - -_pr_list :: Bool -> String -> [MalVal] -> String -_pr_list pr sep [] = [] -_pr_list pr sep (x:[]) = (_pr_str pr x) -_pr_list pr sep (x:xs) = (_pr_str pr x) ++ sep ++ (_pr_list pr sep xs) +_pr_list :: Bool -> String -> [MalVal] -> IO String +_pr_list _ _ [] = return $ [] +_pr_list pr _ [x] = _pr_str pr x +_pr_list pr sep (x:xs) = format <$> _pr_str pr x <*> _pr_list pr sep xs where + format l r = l ++ sep ++ r +_flatTuples :: [(String, MalVal)] -> [MalVal] _flatTuples ((a,b):xs) = MalString a : b : _flatTuples xs _flatTuples _ = [] -unescape chr = case chr of - '\n' -> "\\n" - '\\' -> "\\\\" - '"' -> "\\\"" - c -> [c] - -_pr_str :: Bool -> MalVal -> String -_pr_str _ (MalString ('\x029e':str)) = ":" ++ str -_pr_str True (MalString str) = "\"" ++ concatMap unescape str ++ "\"" -_pr_str False (MalString str) = str -_pr_str _ (MalSymbol name) = name -_pr_str _ (MalNumber num) = show num -_pr_str _ (MalTrue) = "true" -_pr_str _ (MalFalse) = "false" -_pr_str _ (Nil) = "nil" -_pr_str pr (MalList items _) = "(" ++ (_pr_list pr " " items) ++ ")" -_pr_str pr (MalVector items _) = "[" ++ (_pr_list pr " " items) ++ "]" -_pr_str pr (MalHashMap m _) = "{" ++ (_pr_list pr " " (_flatTuples $ Map.assocs m)) ++ "}" -_pr_str pr (MalAtom r _) = "(atom " ++ (_pr_str pr (unsafePerformIO (readIORef r))) ++ ")" -_pr_str _ (Func f _) = "#" -_pr_str _ (MalFunc {ast=ast, env=fn_env, params=params}) = "(fn* " ++ (show params) ++ " " ++ (show ast) ++ ")" - -instance Show MalVal where show = _pr_str True +unescape :: Char -> String +unescape '\n' = "\\n" +unescape '\\' = "\\\\" +unescape '"' = "\\\"" +unescape c = [c] +_pr_str :: Bool -> MalVal -> IO String +_pr_str _ (MalString (c : cs)) | c == keywordMagic + = return $ ':' : cs +_pr_str True (MalString str) = return $ "\"" ++ concatMap unescape str ++ "\"" +_pr_str False (MalString str) = return str +_pr_str _ (MalSymbol name) = return name +_pr_str _ (MalNumber num) = return $ show num +_pr_str _ (MalBoolean True) = return "true" +_pr_str _ (MalBoolean False) = return $ "false" +_pr_str _ Nil = return "nil" +_pr_str pr (MalSeq _ (Vect False) items) = format <$> _pr_list pr " " items where + format x = "(" ++ x ++ ")" +_pr_str pr (MalSeq _ (Vect True) items) = format <$> _pr_list pr " " items where + format x = "[" ++ x ++ "]" +_pr_str pr (MalHashMap _ m) = format <$> _pr_list pr " " (_flatTuples $ Map.assocs m) where + format x = "{" ++ x ++ "}" +_pr_str pr (MalAtom _ r) = format <$> (_pr_str pr =<< readIORef r) where + format x = "(atom " ++ x ++ ")" +_pr_str _ (MalFunction {f_ast=Nil}) = pure "#" +_pr_str _ (MalFunction {f_ast=a, f_params=p, macro=False}) = format <$> _pr_str True a where + format x = "(fn* " ++ show p ++ " -> " ++ x ++ ")" +_pr_str _ (MalFunction {f_ast=a, f_params=p, macro=True}) = format <$> _pr_str True a where + format x = "(macro* " ++ show p ++ " -> " ++ x ++ ")" diff --git a/haskell/Reader.hs b/haskell/Reader.hs index bacf8b47..e65ae556 100644 --- a/haskell/Reader.hs +++ b/haskell/Reader.hs @@ -3,8 +3,8 @@ module Reader where import Text.ParserCombinators.Parsec ( - Parser, parse, space, char, digit, letter, try, - (<|>), oneOf, noneOf, many, many1, skipMany, skipMany1, sepEndBy) + Parser, parse, char, digit, letter, try, + (<|>), oneOf, noneOf, many, many1, skipMany, skipMany1, sepEndBy, string) import qualified Data.Map as Map import Types @@ -13,9 +13,7 @@ spaces :: Parser () spaces = skipMany1 (oneOf ", \n") comment :: Parser () -comment = do - char ';' - skipMany (noneOf "\r\n") +comment = char ';' *> skipMany (noneOf "\r\n") ignored :: Parser () ignored = skipMany (spaces <|> comment) @@ -24,47 +22,30 @@ symbol :: Parser Char symbol = oneOf "!#$%&|*+-/:<=>?@^_~" escaped :: Parser Char -escaped = do - char '\\' - x <- oneOf "\\\"n" - case x of - 'n' -> return '\n' - _ -> return x +escaped = f <$> (char '\\' *> oneOf "\\\"n") + where f 'n' = '\n' + f x = x read_number :: Parser MalVal -read_number = do - x <- many1 digit - return $ MalNumber $ read x +read_number = MalNumber . read <$> many1 digit read_negative_number :: Parser MalVal -read_negative_number = do - sign <- char '-' - rest <- many1 digit - return $ MalNumber $ read $ sign:rest +read_negative_number = f <$> char '-' <*> many1 digit + where f sign rest = MalNumber $ read $ sign : rest read_string :: Parser MalVal -read_string = do - char '"' - x <- many (escaped <|> noneOf "\\\"") - char '"' - return $ MalString x +read_string = MalString <$> (char '"' *> many (escaped <|> noneOf "\\\"") <* char '"') read_symbol :: Parser MalVal -read_symbol = do - first <- letter <|> symbol - rest <- many (letter <|> digit <|> symbol) - let str = first:rest - return $ case str of - "true" -> MalTrue - "false" -> MalFalse - "nil" -> Nil - _ -> MalSymbol str +read_symbol = f <$> (letter <|> symbol) <*> many (letter <|> digit <|> symbol) + where f first rest = g (first : rest) + g "true" = MalBoolean True + g "false" = MalBoolean False + g "nil" = Nil + g s = MalSymbol s read_keyword :: Parser MalVal -read_keyword = do - char ':' - x <- many (letter <|> digit <|> symbol) - return $ MalString $ "\x029e" ++ x +read_keyword = MalString . (:) keywordMagic <$> (char ':' *> many (letter <|> digit <|> symbol)) read_atom :: Parser MalVal read_atom = read_number @@ -74,72 +55,38 @@ read_atom = read_number <|> read_symbol read_list :: Parser MalVal -read_list = do - char '(' - ignored - x <- sepEndBy read_form ignored - char ')' - return $ MalList x Nil +read_list = toList <$> (char '(' *> ignored *> sepEndBy read_form ignored <* char ')') read_vector :: Parser MalVal -read_vector = do - char '[' - ignored - x <- sepEndBy read_form ignored - char ']' - return $ MalVector x Nil - --- TODO: propagate error properly -_pairs [x] = error "Odd number of elements to _pairs" -_pairs [] = [] -_pairs (MalString x:y:xs) = (x,y):_pairs xs +read_vector = MalSeq (MetaData Nil) (Vect True) <$> (char '[' *> ignored *> sepEndBy read_form ignored <* char ']') read_hash_map :: Parser MalVal -read_hash_map = do - char '{' - ignored - x <- sepEndBy read_form ignored - char '}' - return $ MalHashMap (Map.fromList $ _pairs x) Nil +read_hash_map = g . keyValuePairs =<< (char '{' *> ignored *> sepEndBy read_form ignored <* char '}') + where g (Just pairs) = return $ MalHashMap (MetaData Nil) (Map.fromList pairs) + g Nothing = fail "invalid contents inside map braces" -- reader macros +addPrefix :: String -> MalVal -> MalVal +addPrefix s x = toList [MalSymbol s, x] + read_quote :: Parser MalVal -read_quote = do - char '\'' - x <- read_form - return $ MalList [MalSymbol "quote", x] Nil +read_quote = addPrefix "quote" <$> (char '\'' *> read_form) read_quasiquote :: Parser MalVal -read_quasiquote = do - char '`' - x <- read_form - return $ MalList [MalSymbol "quasiquote", x] Nil +read_quasiquote = addPrefix "quasiquote" <$> (char '`' *> read_form) read_splice_unquote :: Parser MalVal -read_splice_unquote = do - char '~' - char '@' - x <- read_form - return $ MalList [MalSymbol "splice-unquote", x] Nil +read_splice_unquote = addPrefix "splice-unquote" <$> (string "~@" *> read_form) read_unquote :: Parser MalVal -read_unquote = do - char '~' - x <- read_form - return $ MalList [MalSymbol "unquote", x] Nil +read_unquote = addPrefix "unquote" <$> (char '~' *> read_form) read_deref :: Parser MalVal -read_deref = do - char '@' - x <- read_form - return $ MalList [MalSymbol "deref", x] Nil +read_deref = addPrefix "deref" <$> (char '@' *> read_form) read_with_meta :: Parser MalVal -read_with_meta = do - char '^' - m <- read_form - x <- read_form - return $ MalList [MalSymbol "with-meta", x, m] Nil +read_with_meta = f <$> (char '^' *> read_form) <*> read_form + where f m x = toList [MalSymbol "with-meta", x, m] read_macro :: Parser MalVal read_macro = read_quote @@ -151,14 +98,12 @@ read_macro = read_quote -- read_form :: Parser MalVal -read_form = do - ignored - x <- read_macro +read_form = ignored *> ( + read_macro <|> read_list <|> read_vector <|> read_hash_map - <|> read_atom - return $ x + <|> read_atom) read_str :: String -> IOThrows MalVal read_str str = case parse read_form "Mal" str of diff --git a/haskell/Readline.hs b/haskell/Readline.hs index 077f26f6..3eca2921 100644 --- a/haskell/Readline.hs +++ b/haskell/Readline.hs @@ -1,5 +1,5 @@ module Readline -( readline, load_history ) +( addHistory, readline, load_history ) where -- Pick one of these: @@ -10,29 +10,26 @@ import qualified System.Console.Readline as RL import Control.Monad (when) import System.Directory (getHomeDirectory, doesFileExist) - -import System.IO (hGetLine, hFlush, hIsEOF, stdin, stdout) import System.IO.Error (tryIOError) +history_file :: IO String history_file = do home <- getHomeDirectory return $ home ++ "/.mal-history" +load_history :: IO () load_history = do hfile <- history_file fileExists <- doesFileExist hfile when fileExists $ do content <- readFile hfile - mapM RL.addHistory (lines content) - return () - return () + mapM_ RL.addHistory (lines content) -readline prompt = do +readline :: String -> IO (Maybe String) +readline = RL.readline + +addHistory :: String -> IO () +addHistory line = do hfile <- history_file - maybeLine <- RL.readline prompt - case maybeLine of - Just line -> do - RL.addHistory line - res <- tryIOError (appendFile hfile (line ++ "\n")) - return maybeLine - _ -> return maybeLine + _ <- tryIOError (appendFile hfile (line ++ "\n")) + RL.addHistory line diff --git a/haskell/Types.hs b/haskell/Types.hs index 8cf413cd..480195d7 100644 --- a/haskell/Types.hs +++ b/haskell/Types.hs @@ -1,49 +1,46 @@ module Types -(MalVal (..), MalError (..), IOThrows (..), Fn (..), EnvData (..), Env, - throwStr, throwMalVal, _get_call, _to_list, - _func, _malfunc, _fn_Q, _macro_Q, - _nil_Q, _true_Q, _false_Q, _string_Q, _symbol_Q, _keyword_Q, _number_Q, - _list_Q, _vector_Q, _hash_map_Q, _atom_Q) +( MalVal (..), IOThrows, Fn, Env, MetaData (..), Vect (..), + keyValuePairs, throwStr, toList, keywordMagic) where import Data.IORef (IORef) import qualified Data.Map as Map -import Control.Exception as CE -import Control.Monad.Except +import Control.Monad.Except (ExceptT, throwError) -- Base Mal types -- -newtype Fn = Fn ([MalVal] -> IOThrows MalVal) +type Fn = [MalVal] -> IOThrows MalVal + +-- Use type safety for unnamed components, without runtime penalty. +newtype MetaData = MetaData MalVal +newtype Vect = Vect Bool + data MalVal = Nil - | MalFalse - | MalTrue + | MalBoolean Bool | MalNumber Int | MalString String | MalSymbol String - | MalList [MalVal] MalVal - | MalVector [MalVal] MalVal - | MalHashMap (Map.Map String MalVal) MalVal - | MalAtom (IORef MalVal) MalVal - | Func Fn MalVal - | MalFunc {fn :: Fn, - ast :: MalVal, - env :: Env, - params :: MalVal, + | MalSeq MetaData Vect [MalVal] + | MalHashMap MetaData (Map.Map String MalVal) + | MalAtom MetaData (IORef MalVal) + | MalFunction {fn :: Fn, + f_ast :: MalVal, + f_params :: [String], macro :: Bool, meta :: MalVal} +keywordMagic :: Char +keywordMagic = '\x029e' + +_equal_Q :: MalVal -> MalVal -> Bool _equal_Q Nil Nil = True -_equal_Q MalFalse MalFalse = True -_equal_Q MalTrue MalTrue = True +_equal_Q (MalBoolean a) (MalBoolean b) = a == b _equal_Q (MalNumber a) (MalNumber b) = a == b _equal_Q (MalString a) (MalString b) = a == b _equal_Q (MalSymbol a) (MalSymbol b) = a == b -_equal_Q (MalList a _) (MalList b _) = a == b -_equal_Q (MalList a _) (MalVector b _) = a == b -_equal_Q (MalVector a _) (MalList b _) = a == b -_equal_Q (MalVector a _) (MalVector b _) = a == b -_equal_Q (MalHashMap a _) (MalHashMap b _) = a == b -_equal_Q (MalAtom a _) (MalAtom b _) = a == b +_equal_Q (MalSeq _ _ a) (MalSeq _ _ b) = a == b +_equal_Q (MalHashMap _ a) (MalHashMap _ b) = a == b +_equal_Q (MalAtom _ a) (MalAtom _ b) = a == b _equal_Q _ _ = False instance Eq MalVal where @@ -52,99 +49,21 @@ instance Eq MalVal where --- Errors/Exceptions --- -data MalError = StringError String - | MalValError MalVal - -type IOThrows = ExceptT MalError IO +type IOThrows = ExceptT MalVal IO throwStr :: String -> IOThrows a -throwStr str = throwError $ StringError str -throwMalVal :: MalVal -> IOThrows a -throwMalVal mv = throwError $ MalValError mv +throwStr = throwError . MalString -- Env types -- -- Note: Env functions are in Env module -data EnvData = EnvPair (Maybe Env, (Map.Map String MalVal)) -type Env = IORef EnvData +type Env = [IORef (Map.Map String MalVal)] +-- Convenient shortcuts for common situations. +toList :: [MalVal] -> MalVal +toList = MalSeq (MetaData Nil) (Vect False) ----------------------------------------------------------- - --- General functions -- - -_get_call ((Func (Fn f) _) : _) = return f -_get_call (MalFunc {fn=(Fn f)} : _) = return f -_get_call _ = throwStr "_get_call first parameter is not a function " - -_to_list (MalList lst _) = return lst -_to_list (MalVector lst _) = return lst -_to_list _ = throwStr "_to_list expected a MalList or MalVector" - --- Errors - ---catchAny :: IO a -> (CE.SomeException -> IO a) -> IO a ---catchAny = CE.catch - --- Functions - -_func fn = Func (Fn fn) Nil -_func_meta fn meta = Func (Fn fn) meta - -_malfunc ast env params fn = MalFunc {fn=(Fn fn), ast=ast, - env=env, params=params, - macro=False, meta=Nil} -_malfunc_meta ast env params fn meta = MalFunc {fn=(Fn fn), ast=ast, - env=env, params=params, - macro=False, meta=meta} - -_fn_Q (MalFunc {macro=False}) = MalTrue -_fn_Q (Func _ _) = MalTrue -_fn_Q _ = MalFalse - -_macro_Q (MalFunc {macro=True}) = MalTrue -_macro_Q _ = MalFalse - - --- Scalars -_nil_Q Nil = MalTrue -_nil_Q _ = MalFalse - -_true_Q MalTrue = MalTrue -_true_Q _ = MalFalse - -_false_Q MalFalse = MalTrue -_false_Q _ = MalFalse - -_symbol_Q (MalSymbol _) = MalTrue -_symbol_Q _ = MalFalse - -_string_Q (MalString ('\x029e':_)) = MalFalse -_string_Q (MalString _) = MalTrue -_string_Q _ = MalFalse - -_keyword_Q (MalString ('\x029e':_)) = MalTrue -_keyword_Q _ = MalFalse - -_number_Q (MalNumber _) = MalTrue -_number_Q _ = MalFalse - --- Lists - -_list_Q (MalList _ _) = MalTrue -_list_Q _ = MalFalse - --- Vectors - -_vector_Q (MalVector _ _) = MalTrue -_vector_Q _ = MalFalse - --- Hash Maps - -_hash_map_Q (MalHashMap _ _) = MalTrue -_hash_map_Q _ = MalFalse - --- Atoms - -_atom_Q (MalAtom _ _) = MalTrue -_atom_Q _ = MalFalse +keyValuePairs :: [MalVal] -> Maybe [(String, MalVal)] +keyValuePairs [] = pure [] +keyValuePairs (MalString k : v : kvs) = ((k, v) :) <$> keyValuePairs kvs +keyValuePairs _ = Nothing diff --git a/haskell/step0_repl.hs b/haskell/step0_repl.hs index 63964005..b92ea735 100644 --- a/haskell/step0_repl.hs +++ b/haskell/step0_repl.hs @@ -1,28 +1,43 @@ import System.IO (hFlush, stdout) -import Readline (readline, load_history) +import Readline (addHistory, readline, load_history) + +type MalVal = String -- read -mal_read str = str + +mal_read :: String -> MalVal +mal_read = id -- eval -eval ast env = ast + +eval :: MalVal -> MalVal +eval = id -- print -mal_print exp = exp + +mal_print :: MalVal -> String +mal_print = id -- repl -rep line = mal_print $ eval (mal_read line) "" +rep :: String -> String +rep = mal_print . eval . mal_read + +repl_loop :: IO () repl_loop = do line <- readline "user> " case line of Nothing -> return () Just "" -> repl_loop Just str -> do + addHistory str putStrLn $ rep str + hFlush stdout repl_loop +main :: IO () main = do load_history + repl_loop diff --git a/haskell/step1_read_print.hs b/haskell/step1_read_print.hs index 4f396d69..9835f425 100644 --- a/haskell/step1_read_print.hs +++ b/haskell/step1_read_print.hs @@ -1,28 +1,31 @@ import System.IO (hFlush, stdout) import Control.Monad.Except (runExceptT) +import Control.Monad.Trans (liftIO) -import Readline (readline, load_history) +import Readline (addHistory, readline, load_history) import Types import Reader (read_str) import Printer (_pr_str) -- read + mal_read :: String -> IOThrows MalVal -mal_read str = read_str str +mal_read = read_str -- eval -eval :: MalVal -> String -> MalVal -eval ast env = ast + +eval :: MalVal -> MalVal +eval = id -- print -mal_print :: MalVal -> String -mal_print exp = show exp + +mal_print :: MalVal -> IOThrows String +mal_print = liftIO. Printer._pr_str True -- repl + rep :: String -> IOThrows String -rep line = do - ast <- mal_read line - return $ mal_print (eval ast "") +rep line = mal_print =<< (eval <$> mal_read line) repl_loop :: IO () repl_loop = do @@ -31,15 +34,17 @@ repl_loop = do Nothing -> return () Just "" -> repl_loop Just str -> do + addHistory str res <- runExceptT $ rep str out <- case res of - Left (StringError str) -> return $ "Error: " ++ str - Left (MalValError mv) -> return $ "Error: " ++ (show mv) + Left mv -> (++) "Error: " <$> liftIO (Printer._pr_str True mv) Right val -> return val putStrLn out hFlush stdout repl_loop +main :: IO () main = do load_history + repl_loop diff --git a/haskell/step2_eval.hs b/haskell/step2_eval.hs index e02e21c8..70605c06 100644 --- a/haskell/step2_eval.hs +++ b/haskell/step2_eval.hs @@ -1,64 +1,63 @@ import System.IO (hFlush, stdout) -import Control.Monad (mapM) +import Control.Monad ((<=<)) import Control.Monad.Except (runExceptT) +import Control.Monad.Trans (liftIO) import qualified Data.Map as Map -import qualified Data.Traversable as DT -import Readline (readline, load_history) +import Readline (addHistory, readline, load_history) import Types import Reader (read_str) import Printer (_pr_str) -- read + mal_read :: String -> IOThrows MalVal -mal_read str = read_str str +mal_read = read_str -- eval -eval_ast :: MalVal -> (Map.Map String MalVal) -> IOThrows MalVal -eval_ast (MalSymbol sym) env = do - case Map.lookup sym env of - Nothing -> throwStr $ "'" ++ sym ++ "' not found" - Just v -> return v -eval_ast ast@(MalList lst m) env = do - new_lst <- mapM (\x -> (eval x env)) lst - return $ MalList new_lst m -eval_ast ast@(MalVector lst m) env = do - new_lst <- mapM (\x -> (eval x env)) lst - return $ MalVector new_lst m -eval_ast ast@(MalHashMap lst m) env = do - new_hm <- DT.mapM (\x -> (eval x env)) lst - return $ MalHashMap new_hm m -eval_ast ast env = return ast -apply_ast :: MalVal -> (Map.Map String MalVal) -> IOThrows MalVal -apply_ast ast@(MalList [] _) env = do - return ast -apply_ast ast@(MalList _ _) env = do - el <- eval_ast ast env - case el of - (MalList ((Func (Fn f) _) : rest) _) -> - f $ rest - el -> - throwStr $ "invalid apply: " ++ (show el) +-- eval_ast is replaced with pattern matching. -eval :: MalVal -> (Map.Map String MalVal) -> IOThrows MalVal -eval ast env = do - case ast of - (MalList _ _) -> apply_ast ast env - _ -> eval_ast ast env +apply_ast :: [MalVal] -> IOThrows MalVal +apply_ast [] = return $ toList [] + +apply_ast ast = do + evd <- mapM eval ast + case evd of + MalFunction {fn=f} : args -> f args + _ -> throwStr . (++) "invalid apply: " =<< liftIO (Printer._pr_str True (toList ast)) + +eval :: MalVal -> IOThrows MalVal +eval (MalSymbol sym) = do + case Map.lookup sym repl_env of + Nothing -> throwStr $ "'" ++ sym ++ "' not found" + Just val -> return val +eval (MalSeq _ (Vect False) xs) = apply_ast xs +eval (MalSeq m (Vect True) xs) = MalSeq m (Vect True) <$> mapM eval xs +eval (MalHashMap m xs) = MalHashMap m <$> mapM eval xs +eval ast = return ast -- print -mal_print :: MalVal -> String -mal_print exp = show exp + +mal_print :: MalVal -> IOThrows String +mal_print = liftIO. Printer._pr_str True -- repl + +add :: Fn add [MalNumber a, MalNumber b] = return $ MalNumber $ a + b add _ = throwStr $ "illegal arguments to +" + +sub :: Fn sub [MalNumber a, MalNumber b] = return $ MalNumber $ a - b sub _ = throwStr $ "illegal arguments to -" + +mult :: Fn mult [MalNumber a, MalNumber b] = return $ MalNumber $ a * b mult _ = throwStr $ "illegal arguments to *" + +divd :: Fn divd [MalNumber a, MalNumber b] = return $ MalNumber $ a `div` b divd _ = throwStr $ "illegal arguments to /" @@ -69,10 +68,7 @@ repl_env = Map.fromList [("+", _func add), ("/", _func divd)] rep :: String -> IOThrows String -rep line = do - ast <- mal_read line - exp <- eval ast repl_env - return $ mal_print exp +rep = mal_print <=< eval <=< mal_read repl_loop :: IO () repl_loop = do @@ -81,15 +77,20 @@ repl_loop = do Nothing -> return () Just "" -> repl_loop Just str -> do + addHistory str res <- runExceptT $ rep str out <- case res of - Left (StringError str) -> return $ "Error: " ++ str - Left (MalValError mv) -> return $ "Error: " ++ (show mv) + Left mv -> (++) "Error: " <$> liftIO (Printer._pr_str True mv) Right val -> return val putStrLn out hFlush stdout repl_loop +_func :: Fn -> MalVal +_func f = MalFunction {fn=f, f_ast=Nil, f_params=[], macro=False, meta=Nil} + +main :: IO () main = do load_history + repl_loop diff --git a/haskell/step3_env.hs b/haskell/step3_env.hs index be065fa1..308a09a3 100644 --- a/haskell/step3_env.hs +++ b/haskell/step3_env.hs @@ -1,92 +1,88 @@ import System.IO (hFlush, stdout) -import Control.Monad (mapM) +import Control.Monad ((<=<)) import Control.Monad.Except (runExceptT) import Control.Monad.Trans (liftIO) -import qualified Data.Map as Map -import qualified Data.Traversable as DT -import Readline (readline, load_history) +import Readline (addHistory, readline, load_history) import Types import Reader (read_str) import Printer (_pr_str) -import Env (Env, env_new, env_get, env_set) +import Env (env_new, env_get, env_set) -- read + mal_read :: String -> IOThrows MalVal -mal_read str = read_str str +mal_read = read_str -- eval -eval_ast :: MalVal -> Env -> IOThrows MalVal -eval_ast sym@(MalSymbol _) env = env_get env sym -eval_ast ast@(MalList lst m) env = do - new_lst <- mapM (\x -> (eval x env)) lst - return $ MalList new_lst m -eval_ast ast@(MalVector lst m) env = do - new_lst <- mapM (\x -> (eval x env)) lst - return $ MalVector new_lst m -eval_ast ast@(MalHashMap lst m) env = do - new_hm <- DT.mapM (\x -> (eval x env)) lst - return $ MalHashMap new_hm m -eval_ast ast env = return ast -let_bind :: Env -> [MalVal] -> IOThrows Env -let_bind env [] = return env -let_bind env (b:e:xs) = do - evaled <- eval e env - x <- liftIO $ env_set env b evaled +-- eval_ast is replaced with pattern matching. + +let_bind :: Env -> [MalVal] -> IOThrows () +let_bind _ [] = return () +let_bind env (MalSymbol b : e : xs) = do + liftIO . env_set env b =<< eval env e let_bind env xs +let_bind _ _ = throwStr "invalid let*" -apply_ast :: MalVal -> Env -> IOThrows MalVal -apply_ast ast@(MalList [] _) env = do - return ast -apply_ast ast@(MalList (MalSymbol "def!" : args) _) env = do - case args of - (a1@(MalSymbol _): a2 : []) -> do - evaled <- eval a2 env - liftIO $ env_set env a1 evaled - _ -> throwStr "invalid def!" -apply_ast ast@(MalList (MalSymbol "let*" : args) _) env = do - case args of - (a1 : a2 : []) -> do - params <- (_to_list a1) - let_env <- liftIO $ env_new $ Just env - let_bind let_env params - eval a2 let_env - _ -> throwStr "invalid let*" -apply_ast ast@(MalList _ _) env = do - el <- eval_ast ast env - case el of - (MalList ((Func (Fn f) _) : rest) _) -> - f $ rest - el -> - throwStr $ "invalid apply: " ++ (show el) +apply_ast :: [MalVal] -> Env -> IOThrows MalVal -eval :: MalVal -> Env -> IOThrows MalVal -eval ast env = do - case ast of - (MalList _ _) -> apply_ast ast env - _ -> eval_ast ast env +apply_ast [] _ = return $ toList [] +apply_ast [MalSymbol "def!", MalSymbol a1, a2] env = do + evd <- eval env a2 + liftIO $ env_set env a1 evd + return evd +apply_ast (MalSymbol "def!" : _) _ = throwStr "invalid def!" + +apply_ast [MalSymbol "let*", MalSeq _ _ params, a2] env = do + let_env <- liftIO $ env_new env + let_bind let_env params + eval let_env a2 +apply_ast (MalSymbol "let*" : _) _ = throwStr "invalid let*" + +apply_ast ast env = do + evd <- mapM (eval env) ast + case evd of + MalFunction {fn=f} : args -> f args + _ -> throwStr . (++) "invalid apply: " =<< liftIO (Printer._pr_str True (toList ast)) + +eval :: Env -> MalVal -> IOThrows MalVal +eval env (MalSymbol sym) = do + maybeVal <- liftIO $ env_get env sym + case maybeVal of + Nothing -> throwStr $ "'" ++ sym ++ "' not found" + Just val -> return val +eval env (MalSeq _ (Vect False) xs) = apply_ast xs env +eval env (MalSeq m (Vect True) xs) = MalSeq m (Vect True) <$> mapM (eval env) xs +eval env (MalHashMap m xs) = MalHashMap m <$> mapM (eval env) xs +eval _ ast = return ast -- print -mal_print :: MalVal -> String -mal_print exp = show exp + +mal_print :: MalVal -> IOThrows String +mal_print = liftIO. Printer._pr_str True -- repl + +add :: Fn add [MalNumber a, MalNumber b] = return $ MalNumber $ a + b add _ = throwStr $ "illegal arguments to +" + +sub :: Fn sub [MalNumber a, MalNumber b] = return $ MalNumber $ a - b sub _ = throwStr $ "illegal arguments to -" + +mult :: Fn mult [MalNumber a, MalNumber b] = return $ MalNumber $ a * b mult _ = throwStr $ "illegal arguments to *" + +divd :: Fn divd [MalNumber a, MalNumber b] = return $ MalNumber $ a `div` b divd _ = throwStr $ "illegal arguments to /" rep :: Env -> String -> IOThrows String -rep env line = do - ast <- mal_read line - exp <- eval ast env - return $ mal_print exp +rep env = mal_print <=< eval env <=< mal_read repl_loop :: Env -> IO () repl_loop env = do @@ -95,21 +91,28 @@ repl_loop env = do Nothing -> return () Just "" -> repl_loop env Just str -> do + addHistory str res <- runExceptT $ rep env str out <- case res of - Left (StringError str) -> return $ "Error: " ++ str - Left (MalValError mv) -> return $ "Error: " ++ (show mv) + Left mv -> (++) "Error: " <$> liftIO (Printer._pr_str True mv) Right val -> return val putStrLn out hFlush stdout repl_loop env +defBuiltIn :: Env -> String -> Fn -> IO () +defBuiltIn env sym f = + env_set env sym $ MalFunction {fn=f, f_ast=Nil, f_params=[], macro=False, meta=Nil} + +main :: IO () main = do load_history - repl_env <- env_new Nothing - env_set repl_env (MalSymbol "+") $ _func add - env_set repl_env (MalSymbol "-") $ _func sub - env_set repl_env (MalSymbol "*") $ _func mult - env_set repl_env (MalSymbol "/") $ _func divd + repl_env <- env_new [] + + defBuiltIn repl_env "+" add + defBuiltIn repl_env "-" sub + defBuiltIn repl_env "*" mult + defBuiltIn repl_env "/" divd + repl_loop repl_env diff --git a/haskell/step4_if_fn_do.hs b/haskell/step4_if_fn_do.hs index 526f99e6..032fc903 100644 --- a/haskell/step4_if_fn_do.hs +++ b/haskell/step4_if_fn_do.hs @@ -1,116 +1,106 @@ import System.IO (hFlush, stdout) -import Control.Monad (mapM) +import Control.Monad ((<=<)) import Control.Monad.Except (runExceptT) import Control.Monad.Trans (liftIO) -import qualified Data.Map as Map -import qualified Data.Traversable as DT +import Data.Foldable (foldlM) -import Readline (readline, load_history) +import Readline (addHistory, readline, load_history) import Types import Reader (read_str) import Printer (_pr_str) -import Env (Env, env_new, env_bind, env_get, env_set) -import Core as Core +import Env (env_new, env_bind, env_get, env_set) +import Core (ns) -- read + mal_read :: String -> IOThrows MalVal -mal_read str = read_str str +mal_read = read_str -- eval -eval_ast :: MalVal -> Env -> IOThrows MalVal -eval_ast sym@(MalSymbol _) env = env_get env sym -eval_ast ast@(MalList lst m) env = do - new_lst <- mapM (\x -> (eval x env)) lst - return $ MalList new_lst m -eval_ast ast@(MalVector lst m) env = do - new_lst <- mapM (\x -> (eval x env)) lst - return $ MalVector new_lst m -eval_ast ast@(MalHashMap lst m) env = do - new_hm <- DT.mapM (\x -> (eval x env)) lst - return $ MalHashMap new_hm m -eval_ast ast env = return ast -let_bind :: Env -> [MalVal] -> IOThrows Env -let_bind env [] = return env -let_bind env (b:e:xs) = do - evaled <- eval e env - x <- liftIO $ env_set env b evaled +-- eval_ast is replaced with pattern matching. + +let_bind :: Env -> [MalVal] -> IOThrows () +let_bind _ [] = return () +let_bind env (MalSymbol b : e : xs) = do + liftIO . env_set env b =<< eval env e let_bind env xs +let_bind _ _ = throwStr "invalid let*" -apply_ast :: MalVal -> Env -> IOThrows MalVal -apply_ast ast@(MalList [] _) env = do - return ast -apply_ast ast@(MalList (MalSymbol "def!" : args) _) env = do - case args of - (a1@(MalSymbol _): a2 : []) -> do - evaled <- eval a2 env - liftIO $ env_set env a1 evaled - _ -> throwStr "invalid def!" -apply_ast ast@(MalList (MalSymbol "let*" : args) _) env = do - case args of - (a1 : a2 : []) -> do - params <- (_to_list a1) - let_env <- liftIO $ env_new $ Just env - let_bind let_env params - eval a2 let_env - _ -> throwStr "invalid let*" -apply_ast ast@(MalList (MalSymbol "do" : args) _) env = do - case args of - ([]) -> return Nil - _ -> do - el <- eval_ast (MalList args Nil) env - case el of - (MalList lst _) -> return $ last lst +unWrapSymbol :: MalVal -> IOThrows String +unWrapSymbol (MalSymbol s) = return s +unWrapSymbol _ = throwStr "fn* parameter must be symbols" -apply_ast ast@(MalList (MalSymbol "if" : args) _) env = do - case args of - (a1 : a2 : a3 : []) -> do - cond <- eval a1 env - if cond == MalFalse || cond == Nil - then eval a3 env - else eval a2 env - (a1 : a2 : []) -> do - cond <- eval a1 env - if cond == MalFalse || cond == Nil - then return Nil - else eval a2 env - _ -> throwStr "invalid if" -apply_ast ast@(MalList (MalSymbol "fn*" : args) _) env = do - case args of - (a1 : a2 : []) -> do - params <- (_to_list a1) - return $ (_func - (\args -> do - fn_env1 <- liftIO $ env_new $ Just env - fn_env2 <- liftIO $ env_bind fn_env1 params args - eval a2 fn_env2)) - _ -> throwStr "invalid fn*" -apply_ast ast@(MalList _ _) env = do - el <- eval_ast ast env - case el of - (MalList ((Func (Fn f) _) : rest) _) -> - f $ rest - el -> - throwStr $ "invalid apply: " ++ (show el) +newFunction :: MalVal -> Env -> [String] -> MalVal +newFunction a env p = MalFunction {f_ast=a, f_params=p, macro=False, meta=Nil, + fn=(\args -> do + fn_env <- liftIO $ env_new env + ok <- liftIO $ env_bind fn_env p args + case ok of + True -> eval fn_env a + False -> throwStr $ "actual parameters do not match signature " ++ show p)} -eval :: MalVal -> Env -> IOThrows MalVal -eval ast env = do - case ast of - (MalList _ _) -> apply_ast ast env - _ -> eval_ast ast env +apply_ast :: [MalVal] -> Env -> IOThrows MalVal +apply_ast [] _ = return $ toList [] + +apply_ast [MalSymbol "def!", MalSymbol a1, a2] env = do + evd <- eval env a2 + liftIO $ env_set env a1 evd + return evd +apply_ast (MalSymbol "def!" : _) _ = throwStr "invalid def!" + +apply_ast [MalSymbol "let*", MalSeq _ _ params, a2] env = do + let_env <- liftIO $ env_new env + let_bind let_env params + eval let_env a2 +apply_ast (MalSymbol "let*" : _) _ = throwStr "invalid let*" + +apply_ast (MalSymbol "do" : args) env = foldlM (const $ eval env) Nil args + +apply_ast [MalSymbol "if", a1, a2, a3] env = do + cond <- eval env a1 + eval env $ case cond of + Nil -> a3 + MalBoolean False -> a3 + _ -> a2 +apply_ast [MalSymbol "if", a1, a2] env = do + cond <- eval env a1 + case cond of + Nil -> return Nil + MalBoolean False -> return Nil + _ -> eval env a2 +apply_ast (MalSymbol "if" : _) _ = throwStr "invalid if" + +apply_ast [MalSymbol "fn*", MalSeq _ _ params, ast] env = newFunction ast env <$> mapM unWrapSymbol params +apply_ast (MalSymbol "fn*" : _) _ = throwStr "invalid fn*" + +apply_ast ast env = do + evd <- mapM (eval env) ast + case evd of + MalFunction {fn=f} : args -> f args + _ -> throwStr . (++) "invalid apply: " =<< liftIO (Printer._pr_str True (toList ast)) + +eval :: Env -> MalVal -> IOThrows MalVal +eval env (MalSymbol sym) = do + maybeVal <- liftIO $ env_get env sym + case maybeVal of + Nothing -> throwStr $ "'" ++ sym ++ "' not found" + Just val -> return val +eval env (MalSeq _ (Vect False) xs) = apply_ast xs env +eval env (MalSeq m (Vect True) xs) = MalSeq m (Vect True) <$> mapM (eval env) xs +eval env (MalHashMap m xs) = MalHashMap m <$> mapM (eval env) xs +eval _ ast = return ast -- print -mal_print :: MalVal -> String -mal_print exp = show exp + +mal_print :: MalVal -> IOThrows String +mal_print = liftIO. Printer._pr_str True -- repl rep :: Env -> String -> IOThrows String -rep env line = do - ast <- mal_read line - exp <- eval ast env - return $ mal_print exp +rep env = mal_print <=< eval env <=< mal_read repl_loop :: Env -> IO () repl_loop env = do @@ -119,24 +109,38 @@ repl_loop env = do Nothing -> return () Just "" -> repl_loop env Just str -> do + addHistory str res <- runExceptT $ rep env str out <- case res of - Left (StringError str) -> return $ "Error: " ++ str - Left (MalValError mv) -> return $ "Error: " ++ (show mv) + Left mv -> (++) "Error: " <$> liftIO (Printer._pr_str True mv) Right val -> return val putStrLn out hFlush stdout repl_loop env +-- Read and evaluate a line. Ignore successful results, but crash in +-- case of error. This is intended for the startup procedure. +re :: Env -> String -> IO () +re repl_env line = do + res <- runExceptT $ eval repl_env =<< mal_read line + case res of + Left mv -> error . (++) "Startup failed: " <$> Printer._pr_str True mv + Right _ -> return () + +defBuiltIn :: Env -> (String, Fn) -> IO () +defBuiltIn env (sym, f) = + env_set env sym $ MalFunction {fn=f, f_ast=Nil, f_params=[], macro=False, meta=Nil} + +main :: IO () main = do load_history - repl_env <- env_new Nothing + repl_env <- env_new [] -- core.hs: defined using Haskell - (mapM (\(k,v) -> (env_set repl_env (MalSymbol k) v)) Core.ns) + mapM_ (defBuiltIn repl_env) Core.ns -- core.mal: defined using the language itself - runExceptT $ rep repl_env "(def! not (fn* (a) (if a false true)))" + re repl_env "(def! not (fn* (a) (if a false true)))" repl_loop repl_env diff --git a/haskell/step5_tco.hs b/haskell/step5_tco.hs index 8b9ef32c..032fc903 100644 --- a/haskell/step5_tco.hs +++ b/haskell/step5_tco.hs @@ -1,120 +1,106 @@ import System.IO (hFlush, stdout) -import Control.Monad (mapM) +import Control.Monad ((<=<)) import Control.Monad.Except (runExceptT) import Control.Monad.Trans (liftIO) -import qualified Data.Map as Map -import qualified Data.Traversable as DT +import Data.Foldable (foldlM) -import Readline (readline, load_history) +import Readline (addHistory, readline, load_history) import Types import Reader (read_str) import Printer (_pr_str) -import Env (Env, env_new, env_bind, env_get, env_set) -import Core as Core +import Env (env_new, env_bind, env_get, env_set) +import Core (ns) -- read + mal_read :: String -> IOThrows MalVal -mal_read str = read_str str +mal_read = read_str -- eval -eval_ast :: MalVal -> Env -> IOThrows MalVal -eval_ast sym@(MalSymbol _) env = env_get env sym -eval_ast ast@(MalList lst m) env = do - new_lst <- mapM (\x -> (eval x env)) lst - return $ MalList new_lst m -eval_ast ast@(MalVector lst m) env = do - new_lst <- mapM (\x -> (eval x env)) lst - return $ MalVector new_lst m -eval_ast ast@(MalHashMap lst m) env = do - new_hm <- DT.mapM (\x -> (eval x env)) lst - return $ MalHashMap new_hm m -eval_ast ast env = return ast -let_bind :: Env -> [MalVal] -> IOThrows Env -let_bind env [] = return env -let_bind env (b:e:xs) = do - evaled <- eval e env - x <- liftIO $ env_set env b evaled +-- eval_ast is replaced with pattern matching. + +let_bind :: Env -> [MalVal] -> IOThrows () +let_bind _ [] = return () +let_bind env (MalSymbol b : e : xs) = do + liftIO . env_set env b =<< eval env e let_bind env xs +let_bind _ _ = throwStr "invalid let*" -apply_ast :: MalVal -> Env -> IOThrows MalVal -apply_ast ast@(MalList [] _) env = do - return ast -apply_ast ast@(MalList (MalSymbol "def!" : args) _) env = do - case args of - (a1@(MalSymbol _): a2 : []) -> do - evaled <- eval a2 env - liftIO $ env_set env a1 evaled - _ -> throwStr "invalid def!" -apply_ast ast@(MalList (MalSymbol "let*" : args) _) env = do - case args of - (a1 : a2 : []) -> do - params <- (_to_list a1) - let_env <- liftIO $ env_new $ Just env - let_bind let_env params - eval a2 let_env - _ -> throwStr "invalid let*" -apply_ast ast@(MalList (MalSymbol "do" : args) _) env = do - case args of - ([]) -> return Nil - _ -> do - el <- eval_ast (MalList args Nil) env - case el of - (MalList lst _) -> return $ last lst +unWrapSymbol :: MalVal -> IOThrows String +unWrapSymbol (MalSymbol s) = return s +unWrapSymbol _ = throwStr "fn* parameter must be symbols" -apply_ast ast@(MalList (MalSymbol "if" : args) _) env = do - case args of - (a1 : a2 : a3 : []) -> do - cond <- eval a1 env - if cond == MalFalse || cond == Nil - then eval a3 env - else eval a2 env - (a1 : a2 : []) -> do - cond <- eval a1 env - if cond == MalFalse || cond == Nil - then return Nil - else eval a2 env - _ -> throwStr "invalid if" -apply_ast ast@(MalList (MalSymbol "fn*" : args) _) env = do - case args of - (a1 : a2 : []) -> do - params <- (_to_list a1) - return $ (_malfunc a2 env (MalList params Nil) - (\args -> do - fn_env1 <- liftIO $ env_new $ Just env - fn_env2 <- liftIO $ env_bind fn_env1 params args - eval a2 fn_env2)) - _ -> throwStr "invalid fn*" -apply_ast ast@(MalList _ _) env = do - el <- eval_ast ast env - case el of - (MalList ((Func (Fn f) _) : rest) _) -> - f $ rest - (MalList ((MalFunc {ast=ast, env=fn_env, params=(MalList params Nil)}) : rest) _) -> do - fn_env1 <- liftIO $ env_new $ Just fn_env - fn_env2 <- liftIO $ env_bind fn_env1 params rest - eval ast fn_env2 - el -> - throwStr $ "invalid apply: " ++ (show el) +newFunction :: MalVal -> Env -> [String] -> MalVal +newFunction a env p = MalFunction {f_ast=a, f_params=p, macro=False, meta=Nil, + fn=(\args -> do + fn_env <- liftIO $ env_new env + ok <- liftIO $ env_bind fn_env p args + case ok of + True -> eval fn_env a + False -> throwStr $ "actual parameters do not match signature " ++ show p)} -eval :: MalVal -> Env -> IOThrows MalVal -eval ast env = do - case ast of - (MalList _ _) -> apply_ast ast env - _ -> eval_ast ast env +apply_ast :: [MalVal] -> Env -> IOThrows MalVal +apply_ast [] _ = return $ toList [] + +apply_ast [MalSymbol "def!", MalSymbol a1, a2] env = do + evd <- eval env a2 + liftIO $ env_set env a1 evd + return evd +apply_ast (MalSymbol "def!" : _) _ = throwStr "invalid def!" + +apply_ast [MalSymbol "let*", MalSeq _ _ params, a2] env = do + let_env <- liftIO $ env_new env + let_bind let_env params + eval let_env a2 +apply_ast (MalSymbol "let*" : _) _ = throwStr "invalid let*" + +apply_ast (MalSymbol "do" : args) env = foldlM (const $ eval env) Nil args + +apply_ast [MalSymbol "if", a1, a2, a3] env = do + cond <- eval env a1 + eval env $ case cond of + Nil -> a3 + MalBoolean False -> a3 + _ -> a2 +apply_ast [MalSymbol "if", a1, a2] env = do + cond <- eval env a1 + case cond of + Nil -> return Nil + MalBoolean False -> return Nil + _ -> eval env a2 +apply_ast (MalSymbol "if" : _) _ = throwStr "invalid if" + +apply_ast [MalSymbol "fn*", MalSeq _ _ params, ast] env = newFunction ast env <$> mapM unWrapSymbol params +apply_ast (MalSymbol "fn*" : _) _ = throwStr "invalid fn*" + +apply_ast ast env = do + evd <- mapM (eval env) ast + case evd of + MalFunction {fn=f} : args -> f args + _ -> throwStr . (++) "invalid apply: " =<< liftIO (Printer._pr_str True (toList ast)) + +eval :: Env -> MalVal -> IOThrows MalVal +eval env (MalSymbol sym) = do + maybeVal <- liftIO $ env_get env sym + case maybeVal of + Nothing -> throwStr $ "'" ++ sym ++ "' not found" + Just val -> return val +eval env (MalSeq _ (Vect False) xs) = apply_ast xs env +eval env (MalSeq m (Vect True) xs) = MalSeq m (Vect True) <$> mapM (eval env) xs +eval env (MalHashMap m xs) = MalHashMap m <$> mapM (eval env) xs +eval _ ast = return ast -- print -mal_print :: MalVal -> String -mal_print exp = show exp + +mal_print :: MalVal -> IOThrows String +mal_print = liftIO. Printer._pr_str True -- repl rep :: Env -> String -> IOThrows String -rep env line = do - ast <- mal_read line - exp <- eval ast env - return $ mal_print exp +rep env = mal_print <=< eval env <=< mal_read repl_loop :: Env -> IO () repl_loop env = do @@ -123,24 +109,38 @@ repl_loop env = do Nothing -> return () Just "" -> repl_loop env Just str -> do + addHistory str res <- runExceptT $ rep env str out <- case res of - Left (StringError str) -> return $ "Error: " ++ str - Left (MalValError mv) -> return $ "Error: " ++ (show mv) + Left mv -> (++) "Error: " <$> liftIO (Printer._pr_str True mv) Right val -> return val putStrLn out hFlush stdout repl_loop env +-- Read and evaluate a line. Ignore successful results, but crash in +-- case of error. This is intended for the startup procedure. +re :: Env -> String -> IO () +re repl_env line = do + res <- runExceptT $ eval repl_env =<< mal_read line + case res of + Left mv -> error . (++) "Startup failed: " <$> Printer._pr_str True mv + Right _ -> return () + +defBuiltIn :: Env -> (String, Fn) -> IO () +defBuiltIn env (sym, f) = + env_set env sym $ MalFunction {fn=f, f_ast=Nil, f_params=[], macro=False, meta=Nil} + +main :: IO () main = do load_history - repl_env <- env_new Nothing + repl_env <- env_new [] -- core.hs: defined using Haskell - (mapM (\(k,v) -> (env_set repl_env (MalSymbol k) v)) Core.ns) + mapM_ (defBuiltIn repl_env) Core.ns -- core.mal: defined using the language itself - runExceptT $ rep repl_env "(def! not (fn* (a) (if a false true)))" + re repl_env "(def! not (fn* (a) (if a false true)))" repl_loop repl_env diff --git a/haskell/step6_file.hs b/haskell/step6_file.hs index 69207772..f989f6bd 100644 --- a/haskell/step6_file.hs +++ b/haskell/step6_file.hs @@ -1,121 +1,107 @@ import System.IO (hFlush, stdout) import System.Environment (getArgs) -import Control.Monad (mapM) +import Control.Monad ((<=<)) import Control.Monad.Except (runExceptT) import Control.Monad.Trans (liftIO) -import qualified Data.Map as Map -import qualified Data.Traversable as DT +import Data.Foldable (foldlM) -import Readline (readline, load_history) +import Readline (addHistory, readline, load_history) import Types import Reader (read_str) import Printer (_pr_str) -import Env (Env, env_new, env_bind, env_get, env_set) -import Core as Core +import Env (env_new, env_bind, env_get, env_set) +import Core (ns) -- read + mal_read :: String -> IOThrows MalVal -mal_read str = read_str str +mal_read = read_str -- eval -eval_ast :: MalVal -> Env -> IOThrows MalVal -eval_ast sym@(MalSymbol _) env = env_get env sym -eval_ast ast@(MalList lst m) env = do - new_lst <- mapM (\x -> (eval x env)) lst - return $ MalList new_lst m -eval_ast ast@(MalVector lst m) env = do - new_lst <- mapM (\x -> (eval x env)) lst - return $ MalVector new_lst m -eval_ast ast@(MalHashMap lst m) env = do - new_hm <- DT.mapM (\x -> (eval x env)) lst - return $ MalHashMap new_hm m -eval_ast ast env = return ast -let_bind :: Env -> [MalVal] -> IOThrows Env -let_bind env [] = return env -let_bind env (b:e:xs) = do - evaled <- eval e env - x <- liftIO $ env_set env b evaled +-- eval_ast is replaced with pattern matching. + +let_bind :: Env -> [MalVal] -> IOThrows () +let_bind _ [] = return () +let_bind env (MalSymbol b : e : xs) = do + liftIO . env_set env b =<< eval env e let_bind env xs +let_bind _ _ = throwStr "invalid let*" -apply_ast :: MalVal -> Env -> IOThrows MalVal -apply_ast ast@(MalList [] _) env = do - return ast -apply_ast ast@(MalList (MalSymbol "def!" : args) _) env = do - case args of - (a1@(MalSymbol _): a2 : []) -> do - evaled <- eval a2 env - liftIO $ env_set env a1 evaled - _ -> throwStr "invalid def!" -apply_ast ast@(MalList (MalSymbol "let*" : args) _) env = do - case args of - (a1 : a2 : []) -> do - params <- (_to_list a1) - let_env <- liftIO $ env_new $ Just env - let_bind let_env params - eval a2 let_env - _ -> throwStr "invalid let*" -apply_ast ast@(MalList (MalSymbol "do" : args) _) env = do - case args of - ([]) -> return Nil - _ -> do - el <- eval_ast (MalList args Nil) env - case el of - (MalList lst _) -> return $ last lst +unWrapSymbol :: MalVal -> IOThrows String +unWrapSymbol (MalSymbol s) = return s +unWrapSymbol _ = throwStr "fn* parameter must be symbols" -apply_ast ast@(MalList (MalSymbol "if" : args) _) env = do - case args of - (a1 : a2 : a3 : []) -> do - cond <- eval a1 env - if cond == MalFalse || cond == Nil - then eval a3 env - else eval a2 env - (a1 : a2 : []) -> do - cond <- eval a1 env - if cond == MalFalse || cond == Nil - then return Nil - else eval a2 env - _ -> throwStr "invalid if" -apply_ast ast@(MalList (MalSymbol "fn*" : args) _) env = do - case args of - (a1 : a2 : []) -> do - params <- (_to_list a1) - return $ (_malfunc a2 env (MalList params Nil) - (\args -> do - fn_env1 <- liftIO $ env_new $ Just env - fn_env2 <- liftIO $ env_bind fn_env1 params args - eval a2 fn_env2)) - _ -> throwStr "invalid fn*" -apply_ast ast@(MalList _ _) env = do - el <- eval_ast ast env - case el of - (MalList ((Func (Fn f) _) : rest) _) -> - f $ rest - (MalList ((MalFunc {ast=ast, env=fn_env, params=(MalList params Nil)}) : rest) _) -> do - fn_env1 <- liftIO $ env_new $ Just fn_env - fn_env2 <- liftIO $ env_bind fn_env1 params rest - eval ast fn_env2 - el -> - throwStr $ "invalid apply: " ++ (show el) +newFunction :: MalVal -> Env -> [String] -> MalVal +newFunction a env p = MalFunction {f_ast=a, f_params=p, macro=False, meta=Nil, + fn=(\args -> do + fn_env <- liftIO $ env_new env + ok <- liftIO $ env_bind fn_env p args + case ok of + True -> eval fn_env a + False -> throwStr $ "actual parameters do not match signature " ++ show p)} -eval :: MalVal -> Env -> IOThrows MalVal -eval ast env = do - case ast of - (MalList _ _) -> apply_ast ast env - _ -> eval_ast ast env +apply_ast :: [MalVal] -> Env -> IOThrows MalVal +apply_ast [] _ = return $ toList [] + +apply_ast [MalSymbol "def!", MalSymbol a1, a2] env = do + evd <- eval env a2 + liftIO $ env_set env a1 evd + return evd +apply_ast (MalSymbol "def!" : _) _ = throwStr "invalid def!" + +apply_ast [MalSymbol "let*", MalSeq _ _ params, a2] env = do + let_env <- liftIO $ env_new env + let_bind let_env params + eval let_env a2 +apply_ast (MalSymbol "let*" : _) _ = throwStr "invalid let*" + +apply_ast (MalSymbol "do" : args) env = foldlM (const $ eval env) Nil args + +apply_ast [MalSymbol "if", a1, a2, a3] env = do + cond <- eval env a1 + eval env $ case cond of + Nil -> a3 + MalBoolean False -> a3 + _ -> a2 +apply_ast [MalSymbol "if", a1, a2] env = do + cond <- eval env a1 + case cond of + Nil -> return Nil + MalBoolean False -> return Nil + _ -> eval env a2 +apply_ast (MalSymbol "if" : _) _ = throwStr "invalid if" + +apply_ast [MalSymbol "fn*", MalSeq _ _ params, ast] env = newFunction ast env <$> mapM unWrapSymbol params +apply_ast (MalSymbol "fn*" : _) _ = throwStr "invalid fn*" + +apply_ast ast env = do + evd <- mapM (eval env) ast + case evd of + MalFunction {fn=f} : args -> f args + _ -> throwStr . (++) "invalid apply: " =<< liftIO (Printer._pr_str True (toList ast)) + +eval :: Env -> MalVal -> IOThrows MalVal +eval env (MalSymbol sym) = do + maybeVal <- liftIO $ env_get env sym + case maybeVal of + Nothing -> throwStr $ "'" ++ sym ++ "' not found" + Just val -> return val +eval env (MalSeq _ (Vect False) xs) = apply_ast xs env +eval env (MalSeq m (Vect True) xs) = MalSeq m (Vect True) <$> mapM (eval env) xs +eval env (MalHashMap m xs) = MalHashMap m <$> mapM (eval env) xs +eval _ ast = return ast -- print -mal_print :: MalVal -> String -mal_print exp = show exp + +mal_print :: MalVal -> IOThrows String +mal_print = liftIO. Printer._pr_str True -- repl rep :: Env -> String -> IOThrows String -rep env line = do - ast <- mal_read line - exp <- eval ast env - return $ mal_print exp +rep env = mal_print <=< eval env <=< mal_read repl_loop :: Env -> IO () repl_loop env = do @@ -124,33 +110,51 @@ repl_loop env = do Nothing -> return () Just "" -> repl_loop env Just str -> do + addHistory str res <- runExceptT $ rep env str out <- case res of - Left (StringError str) -> return $ "Error: " ++ str - Left (MalValError mv) -> return $ "Error: " ++ (show mv) + Left mv -> (++) "Error: " <$> liftIO (Printer._pr_str True mv) Right val -> return val putStrLn out hFlush stdout repl_loop env +-- Read and evaluate a line. Ignore successful results, but crash in +-- case of error. This is intended for the startup procedure. +re :: Env -> String -> IO () +re repl_env line = do + res <- runExceptT $ eval repl_env =<< mal_read line + case res of + Left mv -> error . (++) "Startup failed: " <$> Printer._pr_str True mv + Right _ -> return () + +defBuiltIn :: Env -> (String, Fn) -> IO () +defBuiltIn env (sym, f) = + env_set env sym $ MalFunction {fn=f, f_ast=Nil, f_params=[], macro=False, meta=Nil} + +evalFn :: Env -> Fn +evalFn env [ast] = eval env ast +evalFn _ _ = throwStr "illegal call of eval" + +main :: IO () main = do args <- getArgs load_history - repl_env <- env_new Nothing + repl_env <- env_new [] -- core.hs: defined using Haskell - (mapM (\(k,v) -> (env_set repl_env (MalSymbol k) v)) Core.ns) - env_set repl_env (MalSymbol "eval") (_func (\[ast] -> eval ast repl_env)) - env_set repl_env (MalSymbol "*ARGV*") (MalList [] Nil) + mapM_ (defBuiltIn repl_env) Core.ns + defBuiltIn repl_env ("eval", evalFn repl_env) -- core.mal: defined using the language itself - runExceptT $ rep repl_env "(def! not (fn* (a) (if a false true)))" - runExceptT $ rep repl_env "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" + 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) \")\")))))" - if length args > 0 then do - env_set repl_env (MalSymbol "*ARGV*") (MalList (map MalString (drop 1 args)) Nil) - runExceptT $ rep repl_env $ "(load-file \"" ++ (args !! 0) ++ "\")" - return () - else - repl_loop repl_env + case args of + script : scriptArgs -> do + env_set repl_env "*ARGV*" $ toList $ MalString <$> scriptArgs + re repl_env $ "(load-file \"" ++ script ++ "\")" + [] -> do + env_set repl_env "*ARGV*" $ toList [] + repl_loop repl_env diff --git a/haskell/step7_quote.hs b/haskell/step7_quote.hs index b944c74a..c6c44834 100644 --- a/haskell/step7_quote.hs +++ b/haskell/step7_quote.hs @@ -1,150 +1,132 @@ import System.IO (hFlush, stdout) import System.Environment (getArgs) -import Control.Monad (mapM) +import Control.Monad ((<=<)) import Control.Monad.Except (runExceptT) import Control.Monad.Trans (liftIO) -import qualified Data.Map as Map -import qualified Data.Traversable as DT +import Data.Foldable (foldlM, foldrM) -import Readline (readline, load_history) +import Readline (addHistory, readline, load_history) import Types import Reader (read_str) import Printer (_pr_str) -import Env (Env, env_new, env_bind, env_get, env_set) -import Core as Core +import Env (env_new, env_bind, env_get, env_set) +import Core (ns) -- read + mal_read :: String -> IOThrows MalVal -mal_read str = read_str str +mal_read = read_str -- eval -is_pair (MalList x _:xs) = True -is_pair (MalVector x _:xs) = True -is_pair _ = False -quasiquote :: MalVal -> MalVal -quasiquote ast = - case ast of - (MalList (MalSymbol "unquote" : a1 : []) _) -> a1 - (MalList (MalList (MalSymbol "splice-unquote" : a01 : []) _ : rest) _) -> - MalList [(MalSymbol "concat"), a01, quasiquote (MalList rest Nil)] Nil - (MalVector (MalList (MalSymbol "splice-unquote" : a01 : []) _ : rest) _) -> - MalList [(MalSymbol "concat"), a01, quasiquote (MalVector rest Nil)] Nil - (MalList (a0 : rest) _) -> MalList [(MalSymbol "cons"), - quasiquote a0, - quasiquote (MalList rest Nil)] Nil - (MalVector (a0 : rest) _) -> MalList [(MalSymbol "cons"), - quasiquote a0, - quasiquote (MalVector rest Nil)] Nil - _ -> MalList [(MalSymbol "quote"), ast] Nil +-- starts-with is replaced with pattern matching. +qqIter :: Env -> MalVal -> [MalVal] -> IOThrows [MalVal] +qqIter env (MalSeq _ (Vect False) [MalSymbol "splice-unquote", x]) acc = do + evaluated <- eval env x + case evaluated of + MalSeq _ (Vect False) xs -> return $ xs ++ acc + _ -> throwStr "invalid splice-unquote argument" +qqIter _ (MalSeq _ (Vect False) (MalSymbol "splice-unquote" : _)) _ = throwStr "invalid splice-unquote" +qqIter env x acc = (: acc) <$> quasiquote x env -eval_ast :: MalVal -> Env -> IOThrows MalVal -eval_ast sym@(MalSymbol _) env = env_get env sym -eval_ast ast@(MalList lst m) env = do - new_lst <- mapM (\x -> (eval x env)) lst - return $ MalList new_lst m -eval_ast ast@(MalVector lst m) env = do - new_lst <- mapM (\x -> (eval x env)) lst - return $ MalVector new_lst m -eval_ast ast@(MalHashMap lst m) env = do - new_hm <- DT.mapM (\x -> (eval x env)) lst - return $ MalHashMap new_hm m -eval_ast ast env = return ast +quasiquote :: MalVal -> Env -> IOThrows MalVal +quasiquote (MalSeq _ (Vect False) [MalSymbol "unquote", x]) env = eval env x +-- FIXME This line +quasiquote (MalSeq m _ ys) env = MalSeq m (Vect False) <$> foldrM (qqIter env) [] ys +-- is adapted to broken tests. It should be: +-- quasiquote (MalSeq m v ys) env = MalSeq m v <$> foldrM (qqIter env) [] ys +quasiquote ast _ = return ast -let_bind :: Env -> [MalVal] -> IOThrows Env -let_bind env [] = return env -let_bind env (b:e:xs) = do - evaled <- eval e env - x <- liftIO $ env_set env b evaled +-- eval_ast is replaced with pattern matching. + +let_bind :: Env -> [MalVal] -> IOThrows () +let_bind _ [] = return () +let_bind env (MalSymbol b : e : xs) = do + liftIO . env_set env b =<< eval env e let_bind env xs +let_bind _ _ = throwStr "invalid let*" -apply_ast :: MalVal -> Env -> IOThrows MalVal -apply_ast ast@(MalList [] _) env = do - return ast -apply_ast ast@(MalList (MalSymbol "def!" : args) _) env = do - case args of - (a1@(MalSymbol _): a2 : []) -> do - evaled <- eval a2 env - liftIO $ env_set env a1 evaled - _ -> throwStr "invalid def!" -apply_ast ast@(MalList (MalSymbol "let*" : args) _) env = do - case args of - (a1 : a2 : []) -> do - params <- (_to_list a1) - let_env <- liftIO $ env_new $ Just env - let_bind let_env params - eval a2 let_env - _ -> throwStr "invalid let*" -apply_ast ast@(MalList (MalSymbol "quote" : args) _) env = do - case args of - a1 : [] -> return a1 - _ -> throwStr "invalid quote" -apply_ast ast@(MalList (MalSymbol "quasiquote" : args) _) env = do - case args of - a1 : [] -> eval (quasiquote a1) env - _ -> throwStr "invalid quasiquote" -apply_ast ast@(MalList (MalSymbol "do" : args) _) env = do - case args of - ([]) -> return Nil - _ -> do - el <- eval_ast (MalList args Nil) env - case el of - (MalList lst _) -> return $ last lst +unWrapSymbol :: MalVal -> IOThrows String +unWrapSymbol (MalSymbol s) = return s +unWrapSymbol _ = throwStr "fn* parameter must be symbols" -apply_ast ast@(MalList (MalSymbol "if" : args) _) env = do - case args of - (a1 : a2 : a3 : []) -> do - cond <- eval a1 env - if cond == MalFalse || cond == Nil - then eval a3 env - else eval a2 env - (a1 : a2 : []) -> do - cond <- eval a1 env - if cond == MalFalse || cond == Nil - then return Nil - else eval a2 env - _ -> throwStr "invalid if" -apply_ast ast@(MalList (MalSymbol "fn*" : args) _) env = do - case args of - (a1 : a2 : []) -> do - params <- (_to_list a1) - return $ (_malfunc a2 env (MalList params Nil) - (\args -> do - fn_env1 <- liftIO $ env_new $ Just env - fn_env2 <- liftIO $ env_bind fn_env1 params args - eval a2 fn_env2)) - _ -> throwStr "invalid fn*" -apply_ast ast@(MalList _ _) env = do - el <- eval_ast ast env - case el of - (MalList ((Func (Fn f) _) : rest) _) -> - f $ rest - (MalList ((MalFunc {ast=ast, env=fn_env, params=(MalList params Nil)}) : rest) _) -> do - fn_env1 <- liftIO $ env_new $ Just fn_env - fn_env2 <- liftIO $ env_bind fn_env1 params rest - eval ast fn_env2 - el -> - throwStr $ "invalid apply: " ++ (show el) +newFunction :: MalVal -> Env -> [String] -> MalVal +newFunction a env p = MalFunction {f_ast=a, f_params=p, macro=False, meta=Nil, + fn=(\args -> do + fn_env <- liftIO $ env_new env + ok <- liftIO $ env_bind fn_env p args + case ok of + True -> eval fn_env a + False -> throwStr $ "actual parameters do not match signature " ++ show p)} -eval :: MalVal -> Env -> IOThrows MalVal -eval ast env = do - case ast of - (MalList _ _) -> apply_ast ast env - _ -> eval_ast ast env +apply_ast :: [MalVal] -> Env -> IOThrows MalVal +apply_ast [] _ = return $ toList [] + +apply_ast [MalSymbol "def!", MalSymbol a1, a2] env = do + evd <- eval env a2 + liftIO $ env_set env a1 evd + return evd +apply_ast (MalSymbol "def!" : _) _ = throwStr "invalid def!" + +apply_ast [MalSymbol "let*", MalSeq _ _ params, a2] env = do + let_env <- liftIO $ env_new env + let_bind let_env params + eval let_env a2 +apply_ast (MalSymbol "let*" : _) _ = throwStr "invalid let*" + +apply_ast [MalSymbol "quote", a1] _ = return a1 +apply_ast (MalSymbol "quote" : _) _ = throwStr "invalid quote" + +apply_ast [MalSymbol "quasiquote", a1] env = quasiquote a1 env +apply_ast (MalSymbol "quasiquote" : _) _ = throwStr "invalid quasiquote" + +apply_ast (MalSymbol "do" : args) env = foldlM (const $ eval env) Nil args + +apply_ast [MalSymbol "if", a1, a2, a3] env = do + cond <- eval env a1 + eval env $ case cond of + Nil -> a3 + MalBoolean False -> a3 + _ -> a2 +apply_ast [MalSymbol "if", a1, a2] env = do + cond <- eval env a1 + case cond of + Nil -> return Nil + MalBoolean False -> return Nil + _ -> eval env a2 +apply_ast (MalSymbol "if" : _) _ = throwStr "invalid if" + +apply_ast [MalSymbol "fn*", MalSeq _ _ params, ast] env = newFunction ast env <$> mapM unWrapSymbol params +apply_ast (MalSymbol "fn*" : _) _ = throwStr "invalid fn*" + +apply_ast ast env = do + evd <- mapM (eval env) ast + case evd of + MalFunction {fn=f} : args -> f args + _ -> throwStr . (++) "invalid apply: " =<< liftIO (Printer._pr_str True (toList ast)) + +eval :: Env -> MalVal -> IOThrows MalVal +eval env (MalSymbol sym) = do + maybeVal <- liftIO $ env_get env sym + case maybeVal of + Nothing -> throwStr $ "'" ++ sym ++ "' not found" + Just val -> return val +eval env (MalSeq _ (Vect False) xs) = apply_ast xs env +eval env (MalSeq m (Vect True) xs) = MalSeq m (Vect True) <$> mapM (eval env) xs +eval env (MalHashMap m xs) = MalHashMap m <$> mapM (eval env) xs +eval _ ast = return ast -- print -mal_print :: MalVal -> String -mal_print exp = show exp + +mal_print :: MalVal -> IOThrows String +mal_print = liftIO. Printer._pr_str True -- repl rep :: Env -> String -> IOThrows String -rep env line = do - ast <- mal_read line - exp <- eval ast env - return $ mal_print exp +rep env = mal_print <=< eval env <=< mal_read repl_loop :: Env -> IO () repl_loop env = do @@ -153,33 +135,51 @@ repl_loop env = do Nothing -> return () Just "" -> repl_loop env Just str -> do + addHistory str res <- runExceptT $ rep env str out <- case res of - Left (StringError str) -> return $ "Error: " ++ str - Left (MalValError mv) -> return $ "Error: " ++ (show mv) + Left mv -> (++) "Error: " <$> liftIO (Printer._pr_str True mv) Right val -> return val putStrLn out hFlush stdout repl_loop env +-- Read and evaluate a line. Ignore successful results, but crash in +-- case of error. This is intended for the startup procedure. +re :: Env -> String -> IO () +re repl_env line = do + res <- runExceptT $ eval repl_env =<< mal_read line + case res of + Left mv -> error . (++) "Startup failed: " <$> Printer._pr_str True mv + Right _ -> return () + +defBuiltIn :: Env -> (String, Fn) -> IO () +defBuiltIn env (sym, f) = + env_set env sym $ MalFunction {fn=f, f_ast=Nil, f_params=[], macro=False, meta=Nil} + +evalFn :: Env -> Fn +evalFn env [ast] = eval env ast +evalFn _ _ = throwStr "illegal call of eval" + +main :: IO () main = do args <- getArgs load_history - repl_env <- env_new Nothing + repl_env <- env_new [] -- core.hs: defined using Haskell - (mapM (\(k,v) -> (env_set repl_env (MalSymbol k) v)) Core.ns) - env_set repl_env (MalSymbol "eval") (_func (\[ast] -> eval ast repl_env)) - env_set repl_env (MalSymbol "*ARGV*") (MalList [] Nil) + mapM_ (defBuiltIn repl_env) Core.ns + defBuiltIn repl_env ("eval", evalFn repl_env) -- core.mal: defined using the language itself - runExceptT $ rep repl_env "(def! not (fn* (a) (if a false true)))" - runExceptT $ rep repl_env "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" + 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) \")\")))))" - if length args > 0 then do - env_set repl_env (MalSymbol "*ARGV*") (MalList (map MalString (drop 1 args)) Nil) - runExceptT $ rep repl_env $ "(load-file \"" ++ (args !! 0) ++ "\")" - return () - else - repl_loop repl_env + case args of + script : scriptArgs -> do + env_set repl_env "*ARGV*" $ toList $ MalString <$> scriptArgs + re repl_env $ "(load-file \"" ++ script ++ "\")" + [] -> do + env_set repl_env "*ARGV*" $ toList [] + repl_loop repl_env diff --git a/haskell/step8_macros.hs b/haskell/step8_macros.hs index 9c313915..411b4408 100644 --- a/haskell/step8_macros.hs +++ b/haskell/step8_macros.hs @@ -1,203 +1,158 @@ import System.IO (hFlush, stdout) import System.Environment (getArgs) -import Control.Monad (mapM) +import Control.Monad ((<=<)) import Control.Monad.Except (runExceptT) import Control.Monad.Trans (liftIO) -import qualified Data.Map as Map -import qualified Data.Traversable as DT +import Data.Foldable (foldlM, foldrM) -import Readline (readline, load_history) +import Readline (addHistory, readline, load_history) import Types import Reader (read_str) import Printer (_pr_str) -import Env (Env, env_new, env_bind, env_find, env_get, env_set) -import Core as Core +import Env (env_new, env_bind, env_get, env_set) +import Core (ns) -- read + mal_read :: String -> IOThrows MalVal -mal_read str = read_str str +mal_read = read_str -- eval -is_pair (MalList x _:xs) = True -is_pair (MalVector x _:xs) = True -is_pair _ = False -quasiquote :: MalVal -> MalVal -quasiquote ast = - case ast of - (MalList (MalSymbol "unquote" : a1 : []) _) -> a1 - (MalList (MalList (MalSymbol "splice-unquote" : a01 : []) _ : rest) _) -> - MalList [(MalSymbol "concat"), a01, quasiquote (MalList rest Nil)] Nil - (MalVector (MalList (MalSymbol "splice-unquote" : a01 : []) _ : rest) _) -> - MalList [(MalSymbol "concat"), a01, quasiquote (MalVector rest Nil)] Nil - (MalList (a0 : rest) _) -> MalList [(MalSymbol "cons"), - quasiquote a0, - quasiquote (MalList rest Nil)] Nil - (MalVector (a0 : rest) _) -> MalList [(MalSymbol "cons"), - quasiquote a0, - quasiquote (MalVector rest Nil)] Nil - _ -> MalList [(MalSymbol "quote"), ast] Nil +-- starts-with is replaced with pattern matching. -is_macro_call :: MalVal -> Env -> IOThrows Bool -is_macro_call (MalList (a0@(MalSymbol _) : rest) _) env = do - e <- liftIO $ env_find env a0 - case e of - Just e -> do - f <- env_get e a0 - case f of - MalFunc {macro=True} -> return True - _ -> return False - Nothing -> return False -is_macro_call _ _ = return False +qqIter :: Env -> MalVal -> [MalVal] -> IOThrows [MalVal] +qqIter env (MalSeq _ (Vect False) [MalSymbol "splice-unquote", x]) acc = do + evaluated <- eval env x + case evaluated of + MalSeq _ (Vect False) xs -> return $ xs ++ acc + _ -> throwStr "invalid splice-unquote argument" +qqIter _ (MalSeq _ (Vect False) (MalSymbol "splice-unquote" : _)) _ = throwStr "invalid splice-unquote" +qqIter env x acc = (: acc) <$> quasiquote x env -macroexpand :: MalVal -> Env -> IOThrows MalVal -macroexpand ast@(MalList (a0 : args) _) env = do - mc <- is_macro_call ast env - if mc then do - mac <- env_get env a0 - case mac of - MalFunc {fn=(Fn f)} -> do - new_ast <- f args - macroexpand new_ast env - _ -> - return ast - else - return ast -macroexpand ast _ = return ast +quasiquote :: MalVal -> Env -> IOThrows MalVal +quasiquote (MalSeq _ (Vect False) [MalSymbol "unquote", x]) env = eval env x +-- FIXME This line +quasiquote (MalSeq m _ ys) env = MalSeq m (Vect False) <$> foldrM (qqIter env) [] ys +-- is adapted to broken tests. It should be: +-- quasiquote (MalSeq m v ys) env = MalSeq m v <$> foldrM (qqIter env) [] ys +quasiquote ast _ = return ast -eval_ast :: MalVal -> Env -> IOThrows MalVal -eval_ast sym@(MalSymbol _) env = env_get env sym -eval_ast ast@(MalList lst m) env = do - new_lst <- mapM (\x -> (eval x env)) lst - return $ MalList new_lst m -eval_ast ast@(MalVector lst m) env = do - new_lst <- mapM (\x -> (eval x env)) lst - return $ MalVector new_lst m -eval_ast ast@(MalHashMap lst m) env = do - new_hm <- DT.mapM (\x -> (eval x env)) lst - return $ MalHashMap new_hm m -eval_ast ast env = return ast +-- is-macro-call is replaced with pattern matching. -let_bind :: Env -> [MalVal] -> IOThrows Env -let_bind env [] = return env -let_bind env (b:e:xs) = do - evaled <- eval e env - x <- liftIO $ env_set env b evaled +macroexpand :: Env -> MalVal -> IOThrows MalVal +macroexpand env ast@(MalSeq _ (Vect False) (MalSymbol a0 : args)) = do + maybeMacro <- liftIO $ env_get env a0 + case maybeMacro of + Just (MalFunction {fn=f, macro=True}) -> macroexpand env =<< f args + _ -> return ast +macroexpand _ ast = return ast + +-- eval_ast is replaced with pattern matching. + +let_bind :: Env -> [MalVal] -> IOThrows () +let_bind _ [] = return () +let_bind env (MalSymbol b : e : xs) = do + liftIO . env_set env b =<< eval env e let_bind env xs +let_bind _ _ = throwStr "invalid let*" -apply_ast :: MalVal -> Env -> IOThrows MalVal -apply_ast ast@(MalList [] _) env = do - return ast -apply_ast ast@(MalList (MalSymbol "def!" : args) _) env = do - case args of - (a1@(MalSymbol _): a2 : []) -> do - evaled <- eval a2 env - liftIO $ env_set env a1 evaled - _ -> throwStr "invalid def!" -apply_ast ast@(MalList (MalSymbol "let*" : args) _) env = do - case args of - (a1 : a2 : []) -> do - params <- (_to_list a1) - let_env <- liftIO $ env_new $ Just env - let_bind let_env params - eval a2 let_env - _ -> throwStr "invalid let*" -apply_ast ast@(MalList (MalSymbol "quote" : args) _) env = do - case args of - a1 : [] -> return a1 - _ -> throwStr "invalid quote" -apply_ast ast@(MalList (MalSymbol "quasiquote" : args) _) env = do - case args of - a1 : [] -> eval (quasiquote a1) env - _ -> throwStr "invalid quasiquote" +unWrapSymbol :: MalVal -> IOThrows String +unWrapSymbol (MalSymbol s) = return s +unWrapSymbol _ = throwStr "fn* parameter must be symbols" -apply_ast ast@(MalList (MalSymbol "defmacro!" : args) _) env = do - case args of - (a1 : a2 : []) -> do - func <- eval a2 env - case func of - MalFunc {fn=f, ast=a, env=e, params=p} -> do - let new_func = MalFunc {fn=f, ast=a, env=e, - params=p, macro=True, - meta=Nil} in - liftIO $ env_set env a1 new_func - _ -> throwStr "defmacro! on non-function" - _ -> throwStr "invalid defmacro!" -apply_ast ast@(MalList (MalSymbol "macroexpand" : args) _) env = do - case args of - (a1 : []) -> macroexpand a1 env - _ -> throwStr "invalid macroexpand" -apply_ast ast@(MalList (MalSymbol "do" : args) _) env = do - case args of - ([]) -> return Nil - _ -> do - el <- eval_ast (MalList args Nil) env - case el of - (MalList lst _) -> return $ last lst +newFunction :: MalVal -> Env -> [String] -> MalVal +newFunction a env p = MalFunction {f_ast=a, f_params=p, macro=False, meta=Nil, + fn=(\args -> do + fn_env <- liftIO $ env_new env + ok <- liftIO $ env_bind fn_env p args + case ok of + True -> eval fn_env a + False -> throwStr $ "actual parameters do not match signature " ++ show p)} -apply_ast ast@(MalList (MalSymbol "if" : args) _) env = do - case args of - (a1 : a2 : a3 : []) -> do - cond <- eval a1 env - if cond == MalFalse || cond == Nil - then eval a3 env - else eval a2 env - (a1 : a2 : []) -> do - cond <- eval a1 env - if cond == MalFalse || cond == Nil - then return Nil - else eval a2 env - _ -> throwStr "invalid if" -apply_ast ast@(MalList (MalSymbol "fn*" : args) _) env = do - case args of - (a1 : a2 : []) -> do - params <- (_to_list a1) - return $ (_malfunc a2 env (MalList params Nil) - (\args -> do - fn_env1 <- liftIO $ env_new $ Just env - fn_env2 <- liftIO $ env_bind fn_env1 params args - eval a2 fn_env2)) - _ -> throwStr "invalid fn*" -apply_ast ast@(MalList _ _) env = do - mc <- is_macro_call ast env - if mc then do - new_ast <- macroexpand ast env - eval new_ast env - else - case ast of - MalList _ _ -> do - el <- eval_ast ast env - case el of - (MalList ((Func (Fn f) _) : rest) _) -> - f $ rest - (MalList ((MalFunc {ast=ast, - env=fn_env, - params=(MalList params Nil)} : rest)) _) -> do - fn_env1 <- liftIO $ env_new $ Just fn_env - fn_env2 <- liftIO $ env_bind fn_env1 params rest - eval ast fn_env2 - el -> - throwStr $ "invalid apply: " ++ (show el) - _ -> return ast +apply_ast :: [MalVal] -> Env -> IOThrows MalVal -eval :: MalVal -> Env -> IOThrows MalVal -eval ast env = do - case ast of - (MalList _ _) -> apply_ast ast env - _ -> eval_ast ast env +apply_ast [] _ = return $ toList [] +apply_ast [MalSymbol "def!", MalSymbol a1, a2] env = do + evd <- eval env a2 + liftIO $ env_set env a1 evd + return evd +apply_ast (MalSymbol "def!" : _) _ = throwStr "invalid def!" + +apply_ast [MalSymbol "let*", MalSeq _ _ params, a2] env = do + let_env <- liftIO $ env_new env + let_bind let_env params + eval let_env a2 +apply_ast (MalSymbol "let*" : _) _ = throwStr "invalid let*" + +apply_ast [MalSymbol "quote", a1] _ = return a1 +apply_ast (MalSymbol "quote" : _) _ = throwStr "invalid quote" + +apply_ast [MalSymbol "quasiquote", a1] env = quasiquote a1 env +apply_ast (MalSymbol "quasiquote" : _) _ = throwStr "invalid quasiquote" + +apply_ast [MalSymbol "defmacro!", MalSymbol a1, a2] env = do + func <- eval env a2 + case func of + MalFunction {macro=False} -> do + let m = func {macro=True} + liftIO $ env_set env a1 m + return m + _ -> throwStr "defmacro! on non-function" +apply_ast (MalSymbol "defmacro!" : _) _ = throwStr "invalid defmacro!" + +apply_ast [MalSymbol "macroexpand", a1] env = macroexpand env a1 +apply_ast (MalSymbol "macroexpand" : _) _ = throwStr "invalid macroexpand" + +apply_ast (MalSymbol "do" : args) env = foldlM (const $ eval env) Nil args + +apply_ast [MalSymbol "if", a1, a2, a3] env = do + cond <- eval env a1 + eval env $ case cond of + Nil -> a3 + MalBoolean False -> a3 + _ -> a2 +apply_ast [MalSymbol "if", a1, a2] env = do + cond <- eval env a1 + case cond of + Nil -> return Nil + MalBoolean False -> return Nil + _ -> eval env a2 +apply_ast (MalSymbol "if" : _) _ = throwStr "invalid if" + +apply_ast [MalSymbol "fn*", MalSeq _ _ params, ast] env = newFunction ast env <$> mapM unWrapSymbol params +apply_ast (MalSymbol "fn*" : _) _ = throwStr "invalid fn*" + +apply_ast ast env = do + evd <- mapM (eval env) ast + case evd of + MalFunction {fn=f, macro=False} : args -> f args + _ -> throwStr . (++) "invalid apply: " =<< liftIO (Printer._pr_str True (toList ast)) + +eval :: Env -> MalVal -> IOThrows MalVal +eval env ast = do + newAst <- macroexpand env ast + case newAst of + MalSymbol sym -> do + maybeVal <- liftIO $ env_get env sym + case maybeVal of + Nothing -> throwStr $ "'" ++ sym ++ "' not found" + Just val -> return val + MalSeq _ (Vect False) xs -> apply_ast xs env + MalSeq m (Vect True) xs -> MalSeq m (Vect True) <$> mapM (eval env) xs + MalHashMap m xs -> MalHashMap m <$> mapM (eval env) xs + _ -> return newAst -- print -mal_print :: MalVal -> String -mal_print exp = show exp + +mal_print :: MalVal -> IOThrows String +mal_print = liftIO. Printer._pr_str True -- repl rep :: Env -> String -> IOThrows String -rep env line = do - ast <- mal_read line - exp <- eval ast env - return $ mal_print exp +rep env = mal_print <=< eval env <=< mal_read repl_loop :: Env -> IO () repl_loop env = do @@ -206,35 +161,52 @@ repl_loop env = do Nothing -> return () Just "" -> repl_loop env Just str -> do + addHistory str res <- runExceptT $ rep env str out <- case res of - Left (StringError str) -> return $ "Error: " ++ str - Left (MalValError mv) -> return $ "Error: " ++ (show mv) + Left mv -> (++) "Error: " <$> liftIO (Printer._pr_str True mv) Right val -> return val putStrLn out hFlush stdout repl_loop env +-- Read and evaluate a line. Ignore successful results, but crash in +-- case of error. This is intended for the startup procedure. +re :: Env -> String -> IO () +re repl_env line = do + res <- runExceptT $ eval repl_env =<< mal_read line + case res of + Left mv -> error . (++) "Startup failed: " <$> Printer._pr_str True mv + Right _ -> return () + +defBuiltIn :: Env -> (String, Fn) -> IO () +defBuiltIn env (sym, f) = + env_set env sym $ MalFunction {fn=f, f_ast=Nil, f_params=[], macro=False, meta=Nil} + +evalFn :: Env -> Fn +evalFn env [ast] = eval env ast +evalFn _ _ = throwStr "illegal call of eval" + +main :: IO () main = do args <- getArgs load_history - repl_env <- env_new Nothing + repl_env <- env_new [] -- core.hs: defined using Haskell - (mapM (\(k,v) -> (env_set repl_env (MalSymbol k) v)) Core.ns) - env_set repl_env (MalSymbol "eval") (_func (\[ast] -> eval ast repl_env)) - env_set repl_env (MalSymbol "*ARGV*") (MalList [] Nil) + mapM_ (defBuiltIn repl_env) Core.ns + defBuiltIn repl_env ("eval", evalFn repl_env) -- core.mal: defined using the language itself - runExceptT $ rep repl_env "(def! not (fn* (a) (if a false true)))" - runExceptT $ rep repl_env "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" - runExceptT $ rep 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)))))))" - runExceptT $ rep repl_env "(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))))))))" + 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)))))))" - if length args > 0 then do - env_set repl_env (MalSymbol "*ARGV*") (MalList (map MalString (drop 1 args)) Nil) - runExceptT $ rep repl_env $ "(load-file \"" ++ (args !! 0) ++ "\")" - return () - else - repl_loop repl_env + case args of + script : scriptArgs -> do + env_set repl_env "*ARGV*" $ toList $ MalString <$> scriptArgs + re repl_env $ "(load-file \"" ++ script ++ "\")" + [] -> do + env_set repl_env "*ARGV*" $ toList [] + repl_loop repl_env diff --git a/haskell/step9_try.hs b/haskell/step9_try.hs index f4688e7c..ab94c996 100644 --- a/haskell/step9_try.hs +++ b/haskell/step9_try.hs @@ -1,218 +1,169 @@ import System.IO (hFlush, stdout) import System.Environment (getArgs) -import Control.Monad (mapM) +import Control.Monad ((<=<)) import Control.Monad.Except (runExceptT) import Control.Monad.Trans (liftIO) -import qualified Data.Map as Map -import qualified Data.Traversable as DT +import Data.Foldable (foldlM, foldrM) -import Readline (readline, load_history) +import Readline (addHistory, readline, load_history) import Types import Reader (read_str) import Printer (_pr_str) -import Env (Env, env_new, env_bind, env_find, env_get, env_set) -import Core as Core +import Env (env_new, env_bind, env_get, env_set) +import Core (ns) -- read + mal_read :: String -> IOThrows MalVal -mal_read str = read_str str +mal_read = read_str -- eval -is_pair (MalList x _:xs) = True -is_pair (MalVector x _:xs) = True -is_pair _ = False -quasiquote :: MalVal -> MalVal -quasiquote ast = - case ast of - (MalList (MalSymbol "unquote" : a1 : []) _) -> a1 - (MalList (MalList (MalSymbol "splice-unquote" : a01 : []) _ : rest) _) -> - MalList [(MalSymbol "concat"), a01, quasiquote (MalList rest Nil)] Nil - (MalVector (MalList (MalSymbol "splice-unquote" : a01 : []) _ : rest) _) -> - MalList [(MalSymbol "concat"), a01, quasiquote (MalVector rest Nil)] Nil - (MalList (a0 : rest) _) -> MalList [(MalSymbol "cons"), - quasiquote a0, - quasiquote (MalList rest Nil)] Nil - (MalVector (a0 : rest) _) -> MalList [(MalSymbol "cons"), - quasiquote a0, - quasiquote (MalVector rest Nil)] Nil - _ -> MalList [(MalSymbol "quote"), ast] Nil +-- starts-with is replaced with pattern matching. -is_macro_call :: MalVal -> Env -> IOThrows Bool -is_macro_call (MalList (a0@(MalSymbol _) : rest) _) env = do - e <- liftIO $ env_find env a0 - case e of - Just e -> do - f <- env_get e a0 - case f of - MalFunc {macro=True} -> return True - _ -> return False - Nothing -> return False -is_macro_call _ _ = return False +qqIter :: Env -> MalVal -> [MalVal] -> IOThrows [MalVal] +qqIter env (MalSeq _ (Vect False) [MalSymbol "splice-unquote", x]) acc = do + evaluated <- eval env x + case evaluated of + MalSeq _ (Vect False) xs -> return $ xs ++ acc + _ -> throwStr "invalid splice-unquote argument" +qqIter _ (MalSeq _ (Vect False) (MalSymbol "splice-unquote" : _)) _ = throwStr "invalid splice-unquote" +qqIter env x acc = (: acc) <$> quasiquote x env -macroexpand :: MalVal -> Env -> IOThrows MalVal -macroexpand ast@(MalList (a0 : args) _) env = do - mc <- is_macro_call ast env - if mc then do - mac <- env_get env a0 - case mac of - MalFunc {fn=(Fn f)} -> do - new_ast <- f args - macroexpand new_ast env - _ -> - return ast - else - return ast -macroexpand ast _ = return ast +quasiquote :: MalVal -> Env -> IOThrows MalVal +quasiquote (MalSeq _ (Vect False) [MalSymbol "unquote", x]) env = eval env x +-- FIXME This line +quasiquote (MalSeq m _ ys) env = MalSeq m (Vect False) <$> foldrM (qqIter env) [] ys +-- is adapted to broken tests. It should be: +-- quasiquote (MalSeq m v ys) env = MalSeq m v <$> foldrM (qqIter env) [] ys +quasiquote ast _ = return ast -eval_ast :: MalVal -> Env -> IOThrows MalVal -eval_ast sym@(MalSymbol _) env = env_get env sym -eval_ast ast@(MalList lst m) env = do - new_lst <- mapM (\x -> (eval x env)) lst - return $ MalList new_lst m -eval_ast ast@(MalVector lst m) env = do - new_lst <- mapM (\x -> (eval x env)) lst - return $ MalVector new_lst m -eval_ast ast@(MalHashMap lst m) env = do - new_hm <- DT.mapM (\x -> (eval x env)) lst - return $ MalHashMap new_hm m -eval_ast ast env = return ast +-- is-macro-call is replaced with pattern matching. -let_bind :: Env -> [MalVal] -> IOThrows Env -let_bind env [] = return env -let_bind env (b:e:xs) = do - evaled <- eval e env - x <- liftIO $ env_set env b evaled +macroexpand :: Env -> MalVal -> IOThrows MalVal +macroexpand env ast@(MalSeq _ (Vect False) (MalSymbol a0 : args)) = do + maybeMacro <- liftIO $ env_get env a0 + case maybeMacro of + Just (MalFunction {fn=f, macro=True}) -> macroexpand env =<< f args + _ -> return ast +macroexpand _ ast = return ast + +-- eval_ast is replaced with pattern matching. + +let_bind :: Env -> [MalVal] -> IOThrows () +let_bind _ [] = return () +let_bind env (MalSymbol b : e : xs) = do + liftIO . env_set env b =<< eval env e let_bind env xs +let_bind _ _ = throwStr "invalid let*" -apply_ast :: MalVal -> Env -> IOThrows MalVal -apply_ast ast@(MalList [] _) env = do - return ast -apply_ast ast@(MalList (MalSymbol "def!" : args) _) env = do - case args of - (a1@(MalSymbol _): a2 : []) -> do - evaled <- eval a2 env - liftIO $ env_set env a1 evaled - _ -> throwStr "invalid def!" -apply_ast ast@(MalList (MalSymbol "let*" : args) _) env = do - case args of - (a1 : a2 : []) -> do - params <- (_to_list a1) - let_env <- liftIO $ env_new $ Just env - let_bind let_env params - eval a2 let_env - _ -> throwStr "invalid let*" -apply_ast ast@(MalList (MalSymbol "quote" : args) _) env = do - case args of - a1 : [] -> return a1 - _ -> throwStr "invalid quote" -apply_ast ast@(MalList (MalSymbol "quasiquote" : args) _) env = do - case args of - a1 : [] -> eval (quasiquote a1) env - _ -> throwStr "invalid quasiquote" +unWrapSymbol :: MalVal -> IOThrows String +unWrapSymbol (MalSymbol s) = return s +unWrapSymbol _ = throwStr "fn* parameter must be symbols" -apply_ast ast@(MalList (MalSymbol "defmacro!" : args) _) env = do - case args of - (a1 : a2 : []) -> do - func <- eval a2 env - case func of - MalFunc {fn=f, ast=a, env=e, params=p} -> do - let new_func = MalFunc {fn=f, ast=a, env=e, - params=p, macro=True, - meta=Nil} in - liftIO $ env_set env a1 new_func - _ -> throwStr "defmacro! on non-function" - _ -> throwStr "invalid defmacro!" -apply_ast ast@(MalList (MalSymbol "macroexpand" : args) _) env = do - case args of - (a1 : []) -> macroexpand a1 env - _ -> throwStr "invalid macroexpand" -apply_ast ast@(MalList (MalSymbol "try*" : args) _) env = do - case args of - (a1 : []) -> eval a1 env - (a1 : (MalList ((MalSymbol "catch*") : a21 : a22 : []) _) : []) -> do - res <- liftIO $ runExceptT $ eval a1 env - case res of - Right val -> return val - Left err -> do - exc <- case err of - (StringError str) -> return $ MalString str - (MalValError mv) -> return $ mv - try_env <- liftIO $ env_new $ Just env - liftIO $ env_set try_env a21 exc - eval a22 try_env - _ -> throwStr "invalid try*" -apply_ast ast@(MalList (MalSymbol "do" : args) _) env = do - case args of - ([]) -> return Nil - _ -> do - el <- eval_ast (MalList args Nil) env - case el of - (MalList lst _) -> return $ last lst +newFunction :: MalVal -> Env -> [String] -> MalVal +newFunction a env p = MalFunction {f_ast=a, f_params=p, macro=False, meta=Nil, + fn=(\args -> do + fn_env <- liftIO $ env_new env + ok <- liftIO $ env_bind fn_env p args + case ok of + True -> eval fn_env a + False -> throwStr $ "actual parameters do not match signature " ++ show p)} -apply_ast ast@(MalList (MalSymbol "if" : args) _) env = do - case args of - (a1 : a2 : a3 : []) -> do - cond <- eval a1 env - if cond == MalFalse || cond == Nil - then eval a3 env - else eval a2 env - (a1 : a2 : []) -> do - cond <- eval a1 env - if cond == MalFalse || cond == Nil - then return Nil - else eval a2 env - _ -> throwStr "invalid if" -apply_ast ast@(MalList (MalSymbol "fn*" : args) _) env = do - case args of - (a1 : a2 : []) -> do - params <- (_to_list a1) - return $ (_malfunc a2 env (MalList params Nil) - (\args -> do - fn_env1 <- liftIO $ env_new $ Just env - fn_env2 <- liftIO $ env_bind fn_env1 params args - eval a2 fn_env2)) - _ -> throwStr "invalid fn*" -apply_ast ast@(MalList _ _) env = do - mc <- is_macro_call ast env - if mc then do - new_ast <- macroexpand ast env - eval new_ast env - else - case ast of - MalList _ _ -> do - el <- eval_ast ast env - case el of - (MalList ((Func (Fn f) _) : rest) _) -> - f $ rest - (MalList ((MalFunc {ast=ast, - env=fn_env, - params=(MalList params Nil)} : rest)) _) -> do - fn_env1 <- liftIO $ env_new $ Just fn_env - fn_env2 <- liftIO $ env_bind fn_env1 params rest - eval ast fn_env2 - el -> - throwStr $ "invalid apply: " ++ (show el) - _ -> return ast +apply_ast :: [MalVal] -> Env -> IOThrows MalVal -eval :: MalVal -> Env -> IOThrows MalVal -eval ast env = do - case ast of - (MalList _ _) -> apply_ast ast env - _ -> eval_ast ast env +apply_ast [] _ = return $ toList [] +apply_ast [MalSymbol "def!", MalSymbol a1, a2] env = do + evd <- eval env a2 + liftIO $ env_set env a1 evd + return evd +apply_ast (MalSymbol "def!" : _) _ = throwStr "invalid def!" + +apply_ast [MalSymbol "let*", MalSeq _ _ params, a2] env = do + let_env <- liftIO $ env_new env + let_bind let_env params + eval let_env a2 +apply_ast (MalSymbol "let*" : _) _ = throwStr "invalid let*" + +apply_ast [MalSymbol "quote", a1] _ = return a1 +apply_ast (MalSymbol "quote" : _) _ = throwStr "invalid quote" + +apply_ast [MalSymbol "quasiquote", a1] env = quasiquote a1 env +apply_ast (MalSymbol "quasiquote" : _) _ = throwStr "invalid quasiquote" + +apply_ast [MalSymbol "defmacro!", MalSymbol a1, a2] env = do + func <- eval env a2 + case func of + MalFunction {macro=False} -> do + let m = func {macro=True} + liftIO $ env_set env a1 m + return m + _ -> throwStr "defmacro! on non-function" +apply_ast (MalSymbol "defmacro!" : _) _ = throwStr "invalid defmacro!" + +apply_ast [MalSymbol "macroexpand", a1] env = macroexpand env a1 +apply_ast (MalSymbol "macroexpand" : _) _ = throwStr "invalid macroexpand" + +apply_ast [MalSymbol "try*", a1] env = eval env a1 +apply_ast [MalSymbol "try*", a1, MalSeq _ (Vect False) [MalSymbol "catch*", MalSymbol a21, a22]] env = do + res <- liftIO $ runExceptT $ eval env a1 + case res of + Right val -> return val + Left exc -> do + try_env <- liftIO $ env_new env + liftIO $ env_set try_env a21 exc + eval try_env a22 +apply_ast (MalSymbol "try*" : _) _ = throwStr "invalid try*" + +apply_ast (MalSymbol "do" : args) env = foldlM (const $ eval env) Nil args + +apply_ast [MalSymbol "if", a1, a2, a3] env = do + cond <- eval env a1 + eval env $ case cond of + Nil -> a3 + MalBoolean False -> a3 + _ -> a2 +apply_ast [MalSymbol "if", a1, a2] env = do + cond <- eval env a1 + case cond of + Nil -> return Nil + MalBoolean False -> return Nil + _ -> eval env a2 +apply_ast (MalSymbol "if" : _) _ = throwStr "invalid if" + +apply_ast [MalSymbol "fn*", MalSeq _ _ params, ast] env = newFunction ast env <$> mapM unWrapSymbol params +apply_ast (MalSymbol "fn*" : _) _ = throwStr "invalid fn*" + +apply_ast ast env = do + evd <- mapM (eval env) ast + case evd of + MalFunction {fn=f, macro=False} : args -> f args + _ -> throwStr . (++) "invalid apply: " =<< liftIO (Printer._pr_str True (toList ast)) + +eval :: Env -> MalVal -> IOThrows MalVal +eval env ast = do + newAst <- macroexpand env ast + case newAst of + MalSymbol sym -> do + maybeVal <- liftIO $ env_get env sym + case maybeVal of + Nothing -> throwStr $ "'" ++ sym ++ "' not found" + Just val -> return val + MalSeq _ (Vect False) xs -> apply_ast xs env + MalSeq m (Vect True) xs -> MalSeq m (Vect True) <$> mapM (eval env) xs + MalHashMap m xs -> MalHashMap m <$> mapM (eval env) xs + _ -> return newAst -- print -mal_print :: MalVal -> String -mal_print exp = show exp + +mal_print :: MalVal -> IOThrows String +mal_print = liftIO. Printer._pr_str True -- repl rep :: Env -> String -> IOThrows String -rep env line = do - ast <- mal_read line - exp <- eval ast env - return $ mal_print exp +rep env = mal_print <=< eval env <=< mal_read repl_loop :: Env -> IO () repl_loop env = do @@ -221,35 +172,52 @@ repl_loop env = do Nothing -> return () Just "" -> repl_loop env Just str -> do + addHistory str res <- runExceptT $ rep env str out <- case res of - Left (StringError str) -> return $ "Error: " ++ str - Left (MalValError mv) -> return $ "Error: " ++ (show mv) + Left mv -> (++) "Error: " <$> liftIO (Printer._pr_str True mv) Right val -> return val putStrLn out hFlush stdout repl_loop env +-- Read and evaluate a line. Ignore successful results, but crash in +-- case of error. This is intended for the startup procedure. +re :: Env -> String -> IO () +re repl_env line = do + res <- runExceptT $ eval repl_env =<< mal_read line + case res of + Left mv -> error . (++) "Startup failed: " <$> Printer._pr_str True mv + Right _ -> return () + +defBuiltIn :: Env -> (String, Fn) -> IO () +defBuiltIn env (sym, f) = + env_set env sym $ MalFunction {fn=f, f_ast=Nil, f_params=[], macro=False, meta=Nil} + +evalFn :: Env -> Fn +evalFn env [ast] = eval env ast +evalFn _ _ = throwStr "illegal call of eval" + +main :: IO () main = do args <- getArgs load_history - repl_env <- env_new Nothing + repl_env <- env_new [] -- core.hs: defined using Haskell - (mapM (\(k,v) -> (env_set repl_env (MalSymbol k) v)) Core.ns) - env_set repl_env (MalSymbol "eval") (_func (\[ast] -> eval ast repl_env)) - env_set repl_env (MalSymbol "*ARGV*") (MalList [] Nil) + mapM_ (defBuiltIn repl_env) Core.ns + defBuiltIn repl_env ("eval", evalFn repl_env) -- core.mal: defined using the language itself - runExceptT $ rep repl_env "(def! not (fn* (a) (if a false true)))" - runExceptT $ rep repl_env "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" - runExceptT $ rep 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)))))))" - runExceptT $ rep repl_env "(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))))))))" + 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)))))))" - if length args > 0 then do - env_set repl_env (MalSymbol "*ARGV*") (MalList (map MalString (drop 1 args)) Nil) - runExceptT $ rep repl_env $ "(load-file \"" ++ (args !! 0) ++ "\")" - return () - else - repl_loop repl_env + case args of + script : scriptArgs -> do + env_set repl_env "*ARGV*" $ toList $ MalString <$> scriptArgs + re repl_env $ "(load-file \"" ++ script ++ "\")" + [] -> do + env_set repl_env "*ARGV*" $ toList [] + repl_loop repl_env diff --git a/haskell/stepA_mal.hs b/haskell/stepA_mal.hs index ba1ef978..04a44a8e 100644 --- a/haskell/stepA_mal.hs +++ b/haskell/stepA_mal.hs @@ -1,218 +1,169 @@ import System.IO (hFlush, stdout) import System.Environment (getArgs) -import Control.Monad (mapM) +import Control.Monad ((<=<)) import Control.Monad.Except (runExceptT) import Control.Monad.Trans (liftIO) -import qualified Data.Map as Map -import qualified Data.Traversable as DT +import Data.Foldable (foldlM, foldrM) -import Readline (readline, load_history) +import Readline (addHistory, readline, load_history) import Types import Reader (read_str) import Printer (_pr_str) -import Env (Env, env_new, env_bind, env_find, env_get, env_set) -import Core as Core +import Env (env_new, env_bind, env_get, env_set) +import Core (ns) -- read + mal_read :: String -> IOThrows MalVal -mal_read str = read_str str +mal_read = read_str -- eval -is_pair (MalList x _:xs) = True -is_pair (MalVector x _:xs) = True -is_pair _ = False -quasiquote :: MalVal -> MalVal -quasiquote ast = - case ast of - (MalList (MalSymbol "unquote" : a1 : []) _) -> a1 - (MalList (MalList (MalSymbol "splice-unquote" : a01 : []) _ : rest) _) -> - MalList [(MalSymbol "concat"), a01, quasiquote (MalList rest Nil)] Nil - (MalVector (MalList (MalSymbol "splice-unquote" : a01 : []) _ : rest) _) -> - MalList [(MalSymbol "concat"), a01, quasiquote (MalVector rest Nil)] Nil - (MalList (a0 : rest) _) -> MalList [(MalSymbol "cons"), - quasiquote a0, - quasiquote (MalList rest Nil)] Nil - (MalVector (a0 : rest) _) -> MalList [(MalSymbol "cons"), - quasiquote a0, - quasiquote (MalVector rest Nil)] Nil - _ -> MalList [(MalSymbol "quote"), ast] Nil +-- starts-with is replaced with pattern matching. -is_macro_call :: MalVal -> Env -> IOThrows Bool -is_macro_call (MalList (a0@(MalSymbol _) : rest) _) env = do - e <- liftIO $ env_find env a0 - case e of - Just e -> do - f <- env_get e a0 - case f of - MalFunc {macro=True} -> return True - _ -> return False - Nothing -> return False -is_macro_call _ _ = return False +qqIter :: Env -> MalVal -> [MalVal] -> IOThrows [MalVal] +qqIter env (MalSeq _ (Vect False) [MalSymbol "splice-unquote", x]) acc = do + evaluated <- eval env x + case evaluated of + MalSeq _ (Vect False) xs -> return $ xs ++ acc + _ -> throwStr "invalid splice-unquote argument" +qqIter _ (MalSeq _ (Vect False) (MalSymbol "splice-unquote" : _)) _ = throwStr "invalid splice-unquote" +qqIter env x acc = (: acc) <$> quasiquote x env -macroexpand :: MalVal -> Env -> IOThrows MalVal -macroexpand ast@(MalList (a0 : args) _) env = do - mc <- is_macro_call ast env - if mc then do - mac <- env_get env a0 - case mac of - MalFunc {fn=(Fn f)} -> do - new_ast <- f args - macroexpand new_ast env - _ -> - return ast - else - return ast -macroexpand ast _ = return ast +quasiquote :: MalVal -> Env -> IOThrows MalVal +quasiquote (MalSeq _ (Vect False) [MalSymbol "unquote", x]) env = eval env x +-- FIXME This line +quasiquote (MalSeq m _ ys) env = MalSeq m (Vect False) <$> foldrM (qqIter env) [] ys +-- is adapted to broken tests. It should be: +-- quasiquote (MalSeq m v ys) env = MalSeq m v <$> foldrM (qqIter env) [] ys +quasiquote ast _ = return ast -eval_ast :: MalVal -> Env -> IOThrows MalVal -eval_ast sym@(MalSymbol _) env = env_get env sym -eval_ast ast@(MalList lst m) env = do - new_lst <- mapM (\x -> (eval x env)) lst - return $ MalList new_lst m -eval_ast ast@(MalVector lst m) env = do - new_lst <- mapM (\x -> (eval x env)) lst - return $ MalVector new_lst m -eval_ast ast@(MalHashMap lst m) env = do - new_hm <- DT.mapM (\x -> (eval x env)) lst - return $ MalHashMap new_hm m -eval_ast ast env = return ast +-- is-macro-call is replaced with pattern matching. -let_bind :: Env -> [MalVal] -> IOThrows Env -let_bind env [] = return env -let_bind env (b:e:xs) = do - evaled <- eval e env - x <- liftIO $ env_set env b evaled +macroexpand :: Env -> MalVal -> IOThrows MalVal +macroexpand env ast@(MalSeq _ (Vect False) (MalSymbol a0 : args)) = do + maybeMacro <- liftIO $ env_get env a0 + case maybeMacro of + Just (MalFunction {fn=f, macro=True}) -> macroexpand env =<< f args + _ -> return ast +macroexpand _ ast = return ast + +-- eval_ast is replaced with pattern matching. + +let_bind :: Env -> [MalVal] -> IOThrows () +let_bind _ [] = return () +let_bind env (MalSymbol b : e : xs) = do + liftIO . env_set env b =<< eval env e let_bind env xs +let_bind _ _ = throwStr "invalid let*" -apply_ast :: MalVal -> Env -> IOThrows MalVal -apply_ast ast@(MalList [] _) env = do - return ast -apply_ast ast@(MalList (MalSymbol "def!" : args) _) env = do - case args of - (a1@(MalSymbol _): a2 : []) -> do - evaled <- eval a2 env - liftIO $ env_set env a1 evaled - _ -> throwStr "invalid def!" -apply_ast ast@(MalList (MalSymbol "let*" : args) _) env = do - case args of - (a1 : a2 : []) -> do - params <- (_to_list a1) - let_env <- liftIO $ env_new $ Just env - let_bind let_env params - eval a2 let_env - _ -> throwStr "invalid let*" -apply_ast ast@(MalList (MalSymbol "quote" : args) _) env = do - case args of - a1 : [] -> return a1 - _ -> throwStr "invalid quote" -apply_ast ast@(MalList (MalSymbol "quasiquote" : args) _) env = do - case args of - a1 : [] -> eval (quasiquote a1) env - _ -> throwStr "invalid quasiquote" +unWrapSymbol :: MalVal -> IOThrows String +unWrapSymbol (MalSymbol s) = return s +unWrapSymbol _ = throwStr "fn* parameter must be symbols" -apply_ast ast@(MalList (MalSymbol "defmacro!" : args) _) env = do - case args of - (a1 : a2 : []) -> do - func <- eval a2 env - case func of - MalFunc {fn=f, ast=a, env=e, params=p} -> do - let new_func = MalFunc {fn=f, ast=a, env=e, - params=p, macro=True, - meta=Nil} in - liftIO $ env_set env a1 new_func - _ -> throwStr "defmacro! on non-function" - _ -> throwStr "invalid defmacro!" -apply_ast ast@(MalList (MalSymbol "macroexpand" : args) _) env = do - case args of - (a1 : []) -> macroexpand a1 env - _ -> throwStr "invalid macroexpand" -apply_ast ast@(MalList (MalSymbol "try*" : args) _) env = do - case args of - (a1 : []) -> eval a1 env - (a1 : (MalList ((MalSymbol "catch*") : a21 : a22 : []) _) : []) -> do - res <- liftIO $ runExceptT $ eval a1 env - case res of - Right val -> return val - Left err -> do - exc <- case err of - (StringError str) -> return $ MalString str - (MalValError mv) -> return $ mv - try_env <- liftIO $ env_new $ Just env - liftIO $ env_set try_env a21 exc - eval a22 try_env - _ -> throwStr "invalid try*" -apply_ast ast@(MalList (MalSymbol "do" : args) _) env = do - case args of - ([]) -> return Nil - _ -> do - el <- eval_ast (MalList args Nil) env - case el of - (MalList lst _) -> return $ last lst +newFunction :: MalVal -> Env -> [String] -> MalVal +newFunction a env p = MalFunction {f_ast=a, f_params=p, macro=False, meta=Nil, + fn=(\args -> do + fn_env <- liftIO $ env_new env + ok <- liftIO $ env_bind fn_env p args + case ok of + True -> eval fn_env a + False -> throwStr $ "actual parameters do not match signature " ++ show p)} -apply_ast ast@(MalList (MalSymbol "if" : args) _) env = do - case args of - (a1 : a2 : a3 : []) -> do - cond <- eval a1 env - if cond == MalFalse || cond == Nil - then eval a3 env - else eval a2 env - (a1 : a2 : []) -> do - cond <- eval a1 env - if cond == MalFalse || cond == Nil - then return Nil - else eval a2 env - _ -> throwStr "invalid if" -apply_ast ast@(MalList (MalSymbol "fn*" : args) _) env = do - case args of - (a1 : a2 : []) -> do - params <- (_to_list a1) - return $ (_malfunc a2 env (MalList params Nil) - (\args -> do - fn_env1 <- liftIO $ env_new $ Just env - fn_env2 <- liftIO $ env_bind fn_env1 params args - eval a2 fn_env2)) - _ -> throwStr "invalid fn*" -apply_ast ast@(MalList _ _) env = do - mc <- is_macro_call ast env - if mc then do - new_ast <- macroexpand ast env - eval new_ast env - else - case ast of - MalList _ _ -> do - el <- eval_ast ast env - case el of - (MalList ((Func (Fn f) _) : rest) _) -> - f $ rest - (MalList ((MalFunc {ast=ast, - env=fn_env, - params=(MalList params Nil)} : rest)) _) -> do - fn_env1 <- liftIO $ env_new $ Just fn_env - fn_env2 <- liftIO $ env_bind fn_env1 params rest - eval ast fn_env2 - el -> - throwStr $ "invalid apply: " ++ (show el) - _ -> return ast +apply_ast :: [MalVal] -> Env -> IOThrows MalVal -eval :: MalVal -> Env -> IOThrows MalVal -eval ast env = do - case ast of - (MalList _ _) -> apply_ast ast env - _ -> eval_ast ast env +apply_ast [] _ = return $ toList [] +apply_ast [MalSymbol "def!", MalSymbol a1, a2] env = do + evd <- eval env a2 + liftIO $ env_set env a1 evd + return evd +apply_ast (MalSymbol "def!" : _) _ = throwStr "invalid def!" + +apply_ast [MalSymbol "let*", MalSeq _ _ params, a2] env = do + let_env <- liftIO $ env_new env + let_bind let_env params + eval let_env a2 +apply_ast (MalSymbol "let*" : _) _ = throwStr "invalid let*" + +apply_ast [MalSymbol "quote", a1] _ = return a1 +apply_ast (MalSymbol "quote" : _) _ = throwStr "invalid quote" + +apply_ast [MalSymbol "quasiquote", a1] env = quasiquote a1 env +apply_ast (MalSymbol "quasiquote" : _) _ = throwStr "invalid quasiquote" + +apply_ast [MalSymbol "defmacro!", MalSymbol a1, a2] env = do + func <- eval env a2 + case func of + MalFunction {macro=False} -> do + let m = func {macro=True} + liftIO $ env_set env a1 m + return m + _ -> throwStr "defmacro! on non-function" +apply_ast (MalSymbol "defmacro!" : _) _ = throwStr "invalid defmacro!" + +apply_ast [MalSymbol "macroexpand", a1] env = macroexpand env a1 +apply_ast (MalSymbol "macroexpand" : _) _ = throwStr "invalid macroexpand" + +apply_ast [MalSymbol "try*", a1] env = eval env a1 +apply_ast [MalSymbol "try*", a1, MalSeq _ (Vect False) [MalSymbol "catch*", MalSymbol a21, a22]] env = do + res <- liftIO $ runExceptT $ eval env a1 + case res of + Right val -> return val + Left exc -> do + try_env <- liftIO $ env_new env + liftIO $ env_set try_env a21 exc + eval try_env a22 +apply_ast (MalSymbol "try*" : _) _ = throwStr "invalid try*" + +apply_ast (MalSymbol "do" : args) env = foldlM (const $ eval env) Nil args + +apply_ast [MalSymbol "if", a1, a2, a3] env = do + cond <- eval env a1 + eval env $ case cond of + Nil -> a3 + MalBoolean False -> a3 + _ -> a2 +apply_ast [MalSymbol "if", a1, a2] env = do + cond <- eval env a1 + case cond of + Nil -> return Nil + MalBoolean False -> return Nil + _ -> eval env a2 +apply_ast (MalSymbol "if" : _) _ = throwStr "invalid if" + +apply_ast [MalSymbol "fn*", MalSeq _ _ params, ast] env = newFunction ast env <$> mapM unWrapSymbol params +apply_ast (MalSymbol "fn*" : _) _ = throwStr "invalid fn*" + +apply_ast ast env = do + evd <- mapM (eval env) ast + case evd of + MalFunction {fn=f, macro=False} : args -> f args + _ -> throwStr . (++) "invalid apply: " =<< liftIO (Printer._pr_str True (toList ast)) + +eval :: Env -> MalVal -> IOThrows MalVal +eval env ast = do + newAst <- macroexpand env ast + case newAst of + MalSymbol sym -> do + maybeVal <- liftIO $ env_get env sym + case maybeVal of + Nothing -> throwStr $ "'" ++ sym ++ "' not found" + Just val -> return val + MalSeq _ (Vect False) xs -> apply_ast xs env + MalSeq m (Vect True) xs -> MalSeq m (Vect True) <$> mapM (eval env) xs + MalHashMap m xs -> MalHashMap m <$> mapM (eval env) xs + _ -> return newAst -- print -mal_print :: MalVal -> String -mal_print exp = show exp + +mal_print :: MalVal -> IOThrows String +mal_print = liftIO. Printer._pr_str True -- repl rep :: Env -> String -> IOThrows String -rep env line = do - ast <- mal_read line - exp <- eval ast env - return $ mal_print exp +rep env = mal_print <=< eval env <=< mal_read repl_loop :: Env -> IO () repl_loop env = do @@ -221,39 +172,54 @@ repl_loop env = do Nothing -> return () Just "" -> repl_loop env Just str -> do + addHistory str res <- runExceptT $ rep env str out <- case res of - Left (StringError str) -> return $ "Error: " ++ str - Left (MalValError mv) -> return $ "Error: " ++ (show mv) + Left mv -> (++) "Error: " <$> liftIO (Printer._pr_str True mv) Right val -> return val putStrLn out hFlush stdout repl_loop env +-- Read and evaluate a line. Ignore successful results, but crash in +-- case of error. This is intended for the startup procedure. +re :: Env -> String -> IO () +re repl_env line = do + res <- runExceptT $ eval repl_env =<< mal_read line + case res of + Left mv -> error . (++) "Startup failed: " <$> Printer._pr_str True mv + Right _ -> return () + +defBuiltIn :: Env -> (String, Fn) -> IO () +defBuiltIn env (sym, f) = + env_set env sym $ MalFunction {fn=f, f_ast=Nil, f_params=[], macro=False, meta=Nil} + +evalFn :: Env -> Fn +evalFn env [ast] = eval env ast +evalFn _ _ = throwStr "illegal call of eval" + +main :: IO () main = do args <- getArgs load_history - repl_env <- env_new Nothing + repl_env <- env_new [] -- core.hs: defined using Haskell - (mapM (\(k,v) -> (env_set repl_env (MalSymbol k) v)) Core.ns) - env_set repl_env (MalSymbol "eval") (_func (\[ast] -> eval ast repl_env)) - env_set repl_env (MalSymbol "*ARGV*") (MalList [] Nil) + mapM_ (defBuiltIn repl_env) Core.ns + defBuiltIn repl_env ("eval", evalFn repl_env) -- core.mal: defined using the language itself - runExceptT $ rep repl_env "(def! *host-language* \"haskell\")" - runExceptT $ rep repl_env "(def! not (fn* (a) (if a false true)))" - runExceptT $ rep repl_env "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" - runExceptT $ rep 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)))))))" - runExceptT $ rep repl_env "(def! inc (fn* [x] (+ x 1)))" - runExceptT $ rep repl_env "(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))" - runExceptT $ rep repl_env "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))" + re repl_env "(def! *host-language* \"haskell\")" + 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)))))))" - if length args > 0 then do - env_set repl_env (MalSymbol "*ARGV*") (MalList (map MalString (drop 1 args)) Nil) - runExceptT $ rep repl_env $ "(load-file \"" ++ (args !! 0) ++ "\")" - return () - else do - runExceptT $ rep repl_env "(println (str \"Mal [\" *host-language* \"]\"))" - repl_loop repl_env + case args of + script : scriptArgs -> do + env_set repl_env "*ARGV*" $ toList $ MalString <$> scriptArgs + re repl_env $ "(load-file \"" ++ script ++ "\")" + [] -> do + env_set repl_env "*ARGV*" $ toList [] + re repl_env "(println (str \"Mal [\" *host-language* \"]\"))" + repl_loop repl_env diff --git a/haxe/Step8_macros.hx b/haxe/Step8_macros.hx index dc115bd7..0f63a64a 100644 --- a/haxe/Step8_macros.hx +++ b/haxe/Step8_macros.hx @@ -192,7 +192,6 @@ class Step8_macros { 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 (cmdargs.length > 0) { diff --git a/haxe/Step9_try.hx b/haxe/Step9_try.hx index 07b7f060..13aa3208 100644 --- a/haxe/Step9_try.hx +++ b/haxe/Step9_try.hx @@ -214,7 +214,6 @@ class Step9_try { 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 (cmdargs.length > 0) { diff --git a/haxe/StepA_mal.hx b/haxe/StepA_mal.hx index bf548daa..958efce1 100644 --- a/haxe/StepA_mal.hx +++ b/haxe/StepA_mal.hx @@ -215,9 +215,6 @@ class StepA_mal { 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("(def! inc (fn* [x] (+ x 1)))"); - rep("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))"); - rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))"); if (cmdargs.length > 0) { diff --git a/hy/step8_macros.hy b/hy/step8_macros.hy index 7297a2a8..5089a201 100755 --- a/hy/step8_macros.hy +++ b/hy/step8_macros.hy @@ -159,7 +159,6 @@ (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))))))))") (defmain [&rest args] (if (>= (len args) 2) diff --git a/hy/step9_try.hy b/hy/step9_try.hy index d436aa70..de2c9348 100755 --- a/hy/step9_try.hy +++ b/hy/step9_try.hy @@ -171,7 +171,6 @@ (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))))))))") (defmain [&rest args] (if (>= (len args) 2) diff --git a/hy/stepA_mal.hy b/hy/stepA_mal.hy index f3c7ad68..42cd81dd 100755 --- a/hy/stepA_mal.hy +++ b/hy/stepA_mal.hy @@ -171,9 +171,6 @@ (REP "(def! *host-language* \"Hy\")") (REP "(def! not (fn* [a] (if a false true)))") (REP "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") -(REP "(def! inc (fn* [x] (+ x 1)))") -(REP "(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))") -(REP "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") (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)))))))") (defmain [&rest args] diff --git a/io/MalTypes.io b/io/MalTypes.io index 97569633..a5b7c0c6 100644 --- a/io/MalTypes.io +++ b/io/MalTypes.io @@ -7,7 +7,9 @@ Number malPrint := method(readable, self asString) // Io strings are of type Sequence Sequence malPrint := method(readable, - if(readable, self asString asJson, self asString) + if(readable, + "\"" .. (self asString asMutable replaceSeq("\\", "\\\\") replaceSeq("\"", "\\\"") replaceSeq("\n", "\\n")) .. "\"", + self asString) ) MalMeta := Object clone do( @@ -16,20 +18,28 @@ MalMeta := Object clone do( MalSymbol := Object clone appendProto(MalMeta) do ( val ::= nil - with := method(str, self clone setVal(str)) + with := method(str, self clone setVal(if(str ?val, str val, str))) malPrint := method(readable, val) == := method(other, (self type == other type) and (val == other val)) ) MalKeyword := Object clone do ( val ::= nil - with := method(str, self clone setVal(str)) + with := method(str, self clone setVal(if(str ?val, str val, str))) malPrint := method(readable, ":" .. val) == := method(other, (self type == other type) and (val == other val)) ) MalSequential := Object clone do( isSequential := method(true) + equalSequence := method(other, + if((other ?isSequential) not, return false) + if(self size != other size, return false) + unequalElement := self detect(i, valA, + (valA == (other at(i))) not + ) + if(unequalElement, false, true) + ) ) MalList := List clone appendProto(MalSequential) appendProto(MalMeta) do ( @@ -39,6 +49,7 @@ MalList := List clone appendProto(MalSequential) appendProto(MalMeta) do ( ) rest := method(MalList with(resend)) slice := method(MalList with(resend)) + == := method(other, equalSequence(other)) ) MalVector := List clone appendProto(MalSequential) appendProto(MalMeta) do ( @@ -48,6 +59,7 @@ MalVector := List clone appendProto(MalSequential) appendProto(MalMeta) do ( ) rest := method(MalList with(resend)) slice := method(MalList with(resend)) + == := method(other, equalSequence(other)) ) MalMap := Map clone appendProto(MalMeta) do ( @@ -109,7 +121,7 @@ MalFunc := Object clone appendProto(MalMeta) do ( call := method(args, blk call(args)) ) -MalAtom := Object clone do ( +MalAtom := Object clone appendProto(MalMeta) do ( val ::= nil with := method(str, self clone setVal(str)) malPrint := method(readable, "(atom " .. (val malPrint(true)) .. ")") diff --git a/io/step8_macros.io b/io/step8_macros.io index b451019f..5f90678d 100644 --- a/io/step8_macros.io +++ b/io/step8_macros.io @@ -88,7 +88,7 @@ EVAL := method(ast, env, ast = quasiquote(ast at(1)) continue, // TCO "defmacro!", - return(env set(ast at(1), EVAL(ast at(2), env) setIsMacro(true))), + return(env set(ast at(1), EVAL(ast at(2), env) clone setIsMacro(true))), "macroexpand", return(macroexpand(ast at(1), env)) ) @@ -126,7 +126,6 @@ repl_env set(MalSymbol with("*ARGV*"), MalList with(System args slice(2))) RE("(def! not (fn* (a) (if a false true)))") RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") RE("(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)))))))") -RE("(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(System args size > 1, REP("(load-file \"" .. (System args at(1)) .. "\")") diff --git a/io/step9_try.io b/io/step9_try.io index 6c56b109..ed286bc2 100644 --- a/io/step9_try.io +++ b/io/step9_try.io @@ -88,7 +88,7 @@ EVAL := method(ast, env, ast = quasiquote(ast at(1)) continue, // TCO "defmacro!", - return(env set(ast at(1), EVAL(ast at(2), env) setIsMacro(true))), + return(env set(ast at(1), EVAL(ast at(2), env) clone setIsMacro(true))), "macroexpand", return(macroexpand(ast at(1), env)), "try*", @@ -137,7 +137,6 @@ repl_env set(MalSymbol with("*ARGV*"), MalList with(System args slice(2))) RE("(def! not (fn* (a) (if a false true)))") RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") RE("(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)))))))") -RE("(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(System args size > 1, REP("(load-file \"" .. (System args at(1)) .. "\")") diff --git a/io/stepA_mal.io b/io/stepA_mal.io index 9dd4a56d..c3ca0d80 100644 --- a/io/stepA_mal.io +++ b/io/stepA_mal.io @@ -88,7 +88,7 @@ EVAL := method(ast, env, ast = quasiquote(ast at(1)) continue, // TCO "defmacro!", - return(env set(ast at(1), EVAL(ast at(2), env) setIsMacro(true))), + return(env set(ast at(1), EVAL(ast at(2), env) clone setIsMacro(true))), "macroexpand", return(macroexpand(ast at(1), env)), "try*", @@ -138,9 +138,6 @@ RE("(def! *host-language* \"io\")") RE("(def! not (fn* (a) (if a false true)))") RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") RE("(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)))))))") -RE("(def! inc (fn* [x] (+ x 1)))") -RE("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))") -RE("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") if(System args size > 1, REP("(load-file \"" .. (System args at(1)) .. "\")") diff --git a/java/src/main/java/mal/step8_macros.java b/java/src/main/java/mal/step8_macros.java index c2fec4f3..24d29208 100644 --- a/java/src/main/java/mal/step8_macros.java +++ b/java/src/main/java/mal/step8_macros.java @@ -233,7 +233,6 @@ public class step8_macros { 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)))))))"); - RE(repl_env, "(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))))))))"); Integer fileIdx = 0; if (args.length > 0 && args[0].equals("--raw")) { diff --git a/java/src/main/java/mal/step9_try.java b/java/src/main/java/mal/step9_try.java index 1262bd89..ba45f9f9 100644 --- a/java/src/main/java/mal/step9_try.java +++ b/java/src/main/java/mal/step9_try.java @@ -259,7 +259,6 @@ public class step9_try { 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)))))))"); - RE(repl_env, "(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))))))))"); Integer fileIdx = 0; if (args.length > 0 && args[0].equals("--raw")) { diff --git a/java/src/main/java/mal/stepA_mal.java b/java/src/main/java/mal/stepA_mal.java index 744ccc7e..bc964a69 100644 --- a/java/src/main/java/mal/stepA_mal.java +++ b/java/src/main/java/mal/stepA_mal.java @@ -260,9 +260,6 @@ public class stepA_mal { 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)))))))"); - RE(repl_env, "(def! inc (fn* [x] (+ x 1)))"); - RE(repl_env, "(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))"); - RE(repl_env, "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))"); Integer fileIdx = 0; if (args.length > 0 && args[0].equals("--raw")) { diff --git a/js/step8_macros.js b/js/step8_macros.js index 54e7a22a..fc090fe1 100644 --- a/js/step8_macros.js +++ b/js/step8_macros.js @@ -160,7 +160,6 @@ repl_env.set(types._symbol('*ARGV*'), []); 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(types._symbol('*ARGV*'), process.argv.slice(3)); diff --git a/js/step9_try.js b/js/step9_try.js index 211e82ae..fc7c5f9d 100644 --- a/js/step9_try.js +++ b/js/step9_try.js @@ -171,7 +171,6 @@ repl_env.set(types._symbol('*ARGV*'), []); 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(types._symbol('*ARGV*'), process.argv.slice(3)); diff --git a/js/stepA_mal.js b/js/stepA_mal.js index b997cff4..622248c2 100644 --- a/js/stepA_mal.js +++ b/js/stepA_mal.js @@ -172,9 +172,6 @@ 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("(def! inc (fn* [x] (+ x 1)))"); -rep("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))"); -rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))"); if (typeof process !== 'undefined' && process.argv.length > 2) { repl_env.set(types._symbol('*ARGV*'), process.argv.slice(3)); diff --git a/julia/step8_macros.jl b/julia/step8_macros.jl index e7b42c76..db10a846 100755 --- a/julia/step8_macros.jl +++ b/julia/step8_macros.jl @@ -145,7 +145,6 @@ env_set(repl_env, symbol("*ARGV*"), ARGS[2:end]) 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 length(ARGS) > 0 diff --git a/julia/step9_try.jl b/julia/step9_try.jl index 868069a2..166b4e0e 100755 --- a/julia/step9_try.jl +++ b/julia/step9_try.jl @@ -163,7 +163,6 @@ env_set(repl_env, symbol("*ARGV*"), ARGS[2:end]) 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 length(ARGS) > 0 diff --git a/julia/stepA_mal.jl b/julia/stepA_mal.jl index 9aed2a2d..32764a05 100755 --- a/julia/stepA_mal.jl +++ b/julia/stepA_mal.jl @@ -164,9 +164,6 @@ REP("(def! *host-language* \"julia\")") 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("(def! inc (fn* [x] (+ x 1)))") -REP("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))") -REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") if length(ARGS) > 0 diff --git a/kotlin/src/mal/step8_macros.kt b/kotlin/src/mal/step8_macros.kt index 929ccfb2..8d223c5d 100644 --- a/kotlin/src/mal/step8_macros.kt +++ b/kotlin/src/mal/step8_macros.kt @@ -155,7 +155,6 @@ fun main(args: Array) { rep("(def! not (fn* (a) (if a false true)))", repl_env) rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env) 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) - 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))))))))", repl_env) if (args.any()) { rep("(load-file \"${args[0]}\")", repl_env) diff --git a/kotlin/src/mal/step9_try.kt b/kotlin/src/mal/step9_try.kt index 03d44f4e..722de183 100644 --- a/kotlin/src/mal/step9_try.kt +++ b/kotlin/src/mal/step9_try.kt @@ -171,7 +171,6 @@ fun main(args: Array) { rep("(def! not (fn* (a) (if a false true)))", repl_env) rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env) 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) - 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))))))))", repl_env) if (args.any()) { rep("(load-file \"${args[0]}\")", repl_env) diff --git a/kotlin/src/mal/stepA_mal.kt b/kotlin/src/mal/stepA_mal.kt index 32c3268f..93abe0fd 100644 --- a/kotlin/src/mal/stepA_mal.kt +++ b/kotlin/src/mal/stepA_mal.kt @@ -172,9 +172,6 @@ fun main(args: Array) { rep("(def! not (fn* (a) (if a false true)))", repl_env) rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env) 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) - rep("(def! inc (fn* [x] (+ x 1)))", repl_env) - rep("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))", repl_env) - rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))", repl_env) if (args.any()) { rep("(load-file \"${args[0]}\")", repl_env) diff --git a/lib/README.md b/lib/README.md index b43a0826..f40bf654 100644 --- a/lib/README.md +++ b/lib/README.md @@ -17,9 +17,6 @@ However, here are some guidelines. is not possible, for example for macros, give them a name starting with an underscore. -- Support successive imports safely by giving the same definitions - again. - If a module provides tests, you may run against an implementation IMPL with these commands. ``` @@ -27,3 +24,12 @@ make IMPL^stepA cd tests python ../runtest.py lib/MODULE.mal ../IMPL/run ``` + +Users and implementors should use the following syntax in order to +ensure that the same file is only loaded once. + +``` +(load-file "../lib/load-file-once.mal") +(load-file-once "../lib/foo.mal") +(load-file-once "../lib/bar.mal") +``` diff --git a/lib/equality.mal b/lib/equality.mal index 8dabb13f..5f4adaa9 100644 --- a/lib/equality.mal +++ b/lib/equality.mal @@ -10,30 +10,30 @@ (def! scalar-equal? =) ;; A faster `and` macro which doesn't use `=` internally. -(defmacro! and2 ; boolean +(defmacro! bool-and ; boolean (fn* [& xs] ; interpreted as logical values (if (empty? xs) true - `(if ~(first xs) (and2 ~@(rest xs)) false)))) -(defmacro! or2 ; boolean + `(if ~(first xs) (bool-and ~@(rest xs)) false)))) +(defmacro! bool-or ; boolean (fn* [& xs] ; interpreted as logical values (if (empty? xs) false - `(if ~(first xs) true (or2 ~@(rest xs)))))) + `(if ~(first xs) true (bool-or ~@(rest xs)))))) (def! starts-with? (fn* [a b] - (or2 (empty? a) - (and2 (mal-equal? (first a) (first b)) - (starts-with? (rest a) (rest b)))))) + (bool-or (empty? a) + (bool-and (mal-equal? (first a) (first b)) + (starts-with? (rest a) (rest b)))))) (def! hash-map-vals-equal? (fn* [a b map-keys] - (or2 (empty? map-keys) - (let* [key (first map-keys)] - (and2 (contains? b key) - (mal-equal? (get a key) (get b key)) - (hash-map-vals-equal? a b (rest map-keys))))))) + (bool-or (empty? map-keys) + (let* [key (first map-keys)] + (bool-and (contains? b key) + (mal-equal? (get a key) (get b key)) + (hash-map-vals-equal? a b (rest map-keys))))))) ;; This implements = in pure mal (using only scalar-equal? as native impl) (def! mal-equal? @@ -41,15 +41,15 @@ (cond (sequential? a) - (and2 (sequential? b) - (scalar-equal? (count a) (count b)) - (starts-with? a b)) + (bool-and (sequential? b) + (scalar-equal? (count a) (count b)) + (starts-with? a b)) (map? a) (let* [keys-a (keys a)] - (and2 (map? b) - (scalar-equal? (count keys-a) (count (keys b))) - (hash-map-vals-equal? a b keys-a))) + (bool-and (map? b) + (scalar-equal? (count keys-a) (count (keys b))) + (hash-map-vals-equal? a b keys-a))) true (scalar-equal? a b)))) @@ -57,20 +57,21 @@ (def! hash-map-equality-correct? (fn* [] (try* - (and2 (= {:a 1} {:a 1}) - (not (= {:a 1} {:a 1 :b 2}))) + (bool-and (= {:a 1} {:a 1}) + (not (= {:a 1} {:a 1 :b 2}))) (catch* _ false)))) (def! sequence-equality-correct? (fn* [] (try* - (and2 (= [:a :b] (list :a :b)) - (not (= [:a :b] [:a :b :c]))) + (bool-and (= [:a :b] (list :a :b)) + (not (= [:a :b] [:a :b :c]))) (catch* _ false)))) ;; If the native `=` implementation doesn't support sequences or hash-maps ;; correctly, replace it with the pure mal implementation -(if (not (and2 (hash-map-equality-correct?) (sequence-equality-correct?))) +(if (not (bool-and (hash-map-equality-correct?) + (sequence-equality-correct?))) (do (def! = mal-equal?) (println "equality.mal: Replaced = with pure mal implementation"))) diff --git a/lib/load-file-once.mal b/lib/load-file-once.mal new file mode 100644 index 00000000..0c0967b4 --- /dev/null +++ b/lib/load-file-once.mal @@ -0,0 +1,18 @@ +;; Like load-file, but will never load the same path twice. + +;; This file is normally loaded with `load-file`, so it needs a +;; different mechanism to neutralize multiple inclusions of +;; itself. Moreover, the file list should never be reset. + +(def! load-file-once + (try* + load-file-once + (catch* _ + (let* [seen (atom {"../lib/load-file-once.mal" nil})] + (fn* [filename] + (if (not (contains? @seen filename)) + (do + (swap! seen assoc filename nil) + (load-file filename)))))))) + +nil diff --git a/lib/perf.mal b/lib/perf.mal index c01f5177..9867b7c0 100644 --- a/lib/perf.mal +++ b/lib/perf.mal @@ -1,14 +1,17 @@ ;; Mesure performances. +(load-file "../lib/load-file-once.mal") +(load-file-once "../lib/trivial.mal") ; gensym inc + ;; Evaluate an expression, but report the time spent (defmacro! time (fn* (exp) (let* [start (gensym) ret (gensym)] - `(let* [~start (time-ms) - ~ret ~exp] + `(let* (~start (time-ms) + ~ret ~exp) (do - (prn (str "Elapsed time: " (- (time-ms) ~start) " msecs")) + (println "Elapsed time:" (- (time-ms) ~start) "msecs") ~ret))))) ;; Count evaluations of a function during a given time frame. @@ -19,7 +22,7 @@ (let* [start (time-ms) _ (fn) elapsed (- (time-ms) start) - iters (+ 1 last-iters) + iters (inc last-iters) new-acc-ms (+ acc-ms elapsed)] ;; (do (prn "new-acc-ms:" new-acc-ms "iters:" iters)) (if (>= new-acc-ms max-ms) diff --git a/lib/reducers.mal b/lib/reducers.mal index 76a3cace..943a325c 100644 --- a/lib/reducers.mal +++ b/lib/reducers.mal @@ -12,7 +12,8 @@ (reduce f (f init (first xs)) (rest xs))))) ;; Right fold (f x1 (f x2 (.. (f xn init)) ..)) -;; The natural implementation for `foldr` is not tail-recursive, so we +;; The natural implementation for `foldr` is not tail-recursive, and +;; the one based on `reduce` constructs many intermediate functions, so we ;; rely on efficient `nth` and `count`. (def! foldr diff --git a/lib/test_cascade.mal b/lib/test_cascade.mal index cabe5333..680206a9 100644 --- a/lib/test_cascade.mal +++ b/lib/test_cascade.mal @@ -1,5 +1,24 @@ ;; Iteration on evaluations interpreted as boolean values. +(load-file "../lib/load-file-once.mal") +(load-file-once "../lib/trivial.mal") ; gensym + +;; `(cond test1 result1 test2 result2 .. testn resultn)` +;; is rewritten (in the step files) as +;; `(if test1 result1 (if test2 result2 (.. (if testn resultn nil))))` +;; It is common that `testn` is `"else"`, `:else`, `true` or similar. + +;; `(or x1 x2 .. xn x)` +;; is almost rewritten as +;; `(if x1 x1 (if x2 x2 (.. (if xn xn x))))` +;; except that each argument is evaluated at most once. +;; Without arguments, returns `nil`. +(defmacro! or (fn* [& xs] + (if (< (count xs) 2) + (first xs) + (let* [r (gensym)] + `(let* (~r ~(first xs)) (if ~r ~r (or ~@(rest xs)))))))) + ;; Conjonction of predicate values (pred x1) and .. and (pred xn) ;; Evaluate `pred x` for each `x` in turn. Return `false` if a result ;; is `nil` or `false`, without evaluating the predicate for the diff --git a/lib/threading.mal b/lib/threading.mal index 580b2b5f..a9d60e60 100644 --- a/lib/threading.mal +++ b/lib/threading.mal @@ -1,6 +1,7 @@ ;; Composition of partially applied functions. -(load-file "../lib/reducers.mal") ; reduce +(load-file "../lib/load-file-once.mal") +(load-file-once "../lib/reducers.mal") ; reduce ;; Rewrite x (a a1 a2) .. (b b1 b2) as ;; (b (.. (a x a1 a2) ..) b1 b2) diff --git a/lib/trivial.mal b/lib/trivial.mal index 8c4f6b6b..209693fd 100644 --- a/lib/trivial.mal +++ b/lib/trivial.mal @@ -1,5 +1,8 @@ ;; Trivial but convenient functions. +;; Integer predecessor (number -> number) +(def! inc (fn* [a] (+ a 1))) + ;; Integer predecessor (number -> number) (def! dec (fn* (a) (- a 1))) @@ -9,4 +12,11 @@ ;; Returns the unchanged argument. (def! identity (fn* (x) x)) +;; Generate a hopefully unique symbol. See section "Plugging the Leaks" +;; of http://www.gigamonkeys.com/book/macros-defining-your-own.html +(def! gensym + (let* [counter (atom 0)] + (fn* [] + (symbol (str "G__" (swap! counter inc)))))) + nil diff --git a/livescript/step8_macros.ls b/livescript/step8_macros.ls index f6da156c..b5ac5706 100644 --- a/livescript/step8_macros.ls +++ b/livescript/step8_macros.ls @@ -321,17 +321,6 @@ rep ' (throw "odd number of forms to cond")) (cons \'cond (rest (rest xs)))))))' -# Define or. -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))))))))' - # Parse program arguments. # The first two (exe and core-file) are, respectively, # the interpreter executable (nodejs or lsc) and the diff --git a/livescript/step9_try.ls b/livescript/step9_try.ls index a77686ca..abd3b8ec 100644 --- a/livescript/step9_try.ls +++ b/livescript/step9_try.ls @@ -352,17 +352,6 @@ rep ' (throw "odd number of forms to cond")) (cons \'cond (rest (rest xs)))))))' -# Define or. -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))))))))' - # Parse program arguments. # The first two (exe and core-file) are, respectively, # the interpreter executable (nodejs or lsc) and the diff --git a/livescript/stepA_mal.ls b/livescript/stepA_mal.ls index e6c9561a..08fbd81e 100644 --- a/livescript/stepA_mal.ls +++ b/livescript/stepA_mal.ls @@ -352,25 +352,6 @@ rep ' (throw "odd number of forms to cond")) (cons \'cond (rest (rest xs)))))))' -rep '(def! inc (fn* [x] (+ x 1)))' - -rep ' -(def! gensym - (let* [counter (atom 0)] - (fn* [] - (symbol (str "G__" (swap! counter inc))))))' - -rep ' -(defmacro! or - (fn* (& xs) - (if (empty? xs) - nil - (if (= 1 (count xs)) - (first xs) - (let* (condvar (gensym)) - `(let* (~condvar ~(first xs)) - (if ~condvar ~condvar (or ~@(rest xs)))))))))' - # Parse program arguments. # The first two (exe and core-file) are, respectively, # the interpreter executable (nodejs or lsc) and the diff --git a/logo/step8_macros.lg b/logo/step8_macros.lg index 885eff3f..d45eb827 100644 --- a/logo/step8_macros.lg +++ b/logo/step8_macros.lg @@ -201,7 +201,6 @@ ignore env_set :repl_env [symbol *ARGV*] argv_list ignore re "|(def! not (fn* (a) (if a false true)))| ignore re "|(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))| ignore re "|(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)))))))| -ignore re "|(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 not emptyp :command.line [ catch "error [ignore re (word "\(load-file :space_char "\" first :command.line "\"\) )] diff --git a/logo/step9_try.lg b/logo/step9_try.lg index b5e0e7c4..ac02d269 100644 --- a/logo/step9_try.lg +++ b/logo/step9_try.lg @@ -220,7 +220,6 @@ ignore env_set :repl_env [symbol *ARGV*] argv_list ignore re "|(def! not (fn* (a) (if a false true)))| ignore re "|(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))| ignore re "|(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)))))))| -ignore re "|(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 not emptyp :command.line [ catch "error [ignore re (word "\(load-file :space_char "\" first :command.line "\"\) )] diff --git a/logo/stepA_mal.lg b/logo/stepA_mal.lg index 6daba153..6c842fbe 100644 --- a/logo/stepA_mal.lg +++ b/logo/stepA_mal.lg @@ -221,9 +221,6 @@ ignore re "|(def! *host-language* "logo")| ignore re "|(def! not (fn* (a) (if a false true)))| ignore re "|(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))| ignore re "|(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)))))))| -ignore re "|(def! inc (fn* [x] (+ x 1)))| -ignore re "|(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str "G__" (swap! counter inc))))))| -ignore re "|(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))| if not emptyp :command.line [ catch "error [ignore re (word "\(load-file :space_char "\" first :command.line "\"\) )] diff --git a/lua/step8_macros.lua b/lua/step8_macros.lua index 33538154..cb49946b 100755 --- a/lua/step8_macros.lua +++ b/lua/step8_macros.lua @@ -155,7 +155,6 @@ repl_env:set(types.Symbol:new('*ARGV*'), types.List:new(types.slice(arg,2))) 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 #arg > 0 and arg[1] == "--raw" then readline.raw = true diff --git a/lua/step9_try.lua b/lua/step9_try.lua index 2cb58171..b9620bfb 100755 --- a/lua/step9_try.lua +++ b/lua/step9_try.lua @@ -173,7 +173,6 @@ repl_env:set(types.Symbol:new('*ARGV*'), types.List:new(types.slice(arg,2))) 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))))))))") function print_exception(exc) if exc then diff --git a/lua/stepA_mal.lua b/lua/stepA_mal.lua index dd9ab81d..47c35ca8 100755 --- a/lua/stepA_mal.lua +++ b/lua/stepA_mal.lua @@ -175,9 +175,6 @@ rep("(def! *host-language* \"lua\")") 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("(def! inc (fn* [x] (+ x 1)))") -rep("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))") -rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") function print_exception(exc) if exc then diff --git a/make/readline.mk b/make/readline.mk index 69f59607..39918c52 100644 --- a/make/readline.mk +++ b/make/readline.mk @@ -10,6 +10,14 @@ __mal_readline_included := true # have readline history. READLINE_EOF := READLINE_HISTORY_FILE := $${HOME}/.mal-history -READLINE = $(eval __readline_temp := $(shell history -r $(READLINE_HISTORY_FILE); read -u 0 -r -e -p $(if $(1),$(1),"user> ") line && history -s -- "$${line}" && echo "$${line}" || echo "__||EOF||__"; history -a $(READLINE_HISTORY_FILE) 2>/dev/null || true))$(if $(filter __||EOF||__,$(__readline_temp)),$(eval READLINE_EOF := yes),$(__readline_temp)) +READLINE = $(eval __readline_temp := $(shell \ + history -r $(READLINE_HISTORY_FILE); \ + read -u 0 -r -e -p $(if $(1),$(1),"user> ") line && \ + history -s -- "$${line}" && \ + echo "$${line}" || \ + echo "__||EOF||__"; \ + history -a $(READLINE_HISTORY_FILE) 2>/dev/null || \ + true \ +))$(if $(filter __||EOF||__,$(__readline_temp)),$(eval READLINE_EOF := yes),$(__readline_temp)) endif diff --git a/make/step0_repl.mk b/make/step0_repl.mk index b8b1309e..46b4756a 100644 --- a/make/step0_repl.mk +++ b/make/step0_repl.mk @@ -11,8 +11,7 @@ $(call READLINE) endef define EVAL -$(if $(READLINE_EOF),,\ - $(if $(findstring =,$(1)),$(eval $(1))$($(word 1,$(1))),$(eval __return := $(1))$(__return))) +$(if $(READLINE_EOF),,$(1)) endef define PRINT diff --git a/make/step8_macros.mk b/make/step8_macros.mk index 7ee0a944..310d6fda 100644 --- a/make/step8_macros.mk +++ b/make/step8_macros.mk @@ -156,7 +156,6 @@ REPL_ENV := $(call ENV_SET,$(REPL_ENV),*ARGV*,$(_argv)) $(call do,$(call REP, (def! not (fn* (a) (if a false true))) )) $(call do,$(call REP, (def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")"))))) )) $(call do,$(call 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))))))) )) -$(call do,$(call 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)))))))) )) # Load and eval any files specified on the command line $(if $(MAKECMDGOALS),\ diff --git a/make/step9_try.mk b/make/step9_try.mk index 20667956..46a292dc 100644 --- a/make/step9_try.mk +++ b/make/step9_try.mk @@ -171,7 +171,6 @@ REPL_ENV := $(call ENV_SET,$(REPL_ENV),*ARGV*,$(_argv)) $(call do,$(call REP, (def! not (fn* (a) (if a false true))) )) $(call do,$(call REP, (def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")"))))) )) $(call do,$(call 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))))))) )) -$(call do,$(call 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)))))))) )) # Load and eval any files specified on the command line $(if $(MAKECMDGOALS),\ diff --git a/make/stepA_mal.mk b/make/stepA_mal.mk index 9c013e10..22840993 100644 --- a/make/stepA_mal.mk +++ b/make/stepA_mal.mk @@ -176,9 +176,6 @@ $(call do,$(call REP, (def! *host-language* "make") )) $(call do,$(call REP, (def! not (fn* (a) (if a false true))) )) $(call do,$(call REP, (def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")"))))) )) $(call do,$(call 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))))))) )) -$(call do,$(call REP, (def! inc (fn* [x] (+ x 1))) )) -$(call do,$(call REP, (def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str "G__" (swap! counter inc)))))) )) -$(call do,$(call REP, (defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs))))))))) )) # Load and eval any files specified on the command line $(if $(MAKECMDGOALS),\ diff --git a/mal/core.mal b/mal/core.mal index 15bcac3e..5137f502 100644 --- a/mal/core.mal +++ b/mal/core.mal @@ -1,80 +1,76 @@ -(def! _fn? (fn* [x] - (if (fn? x) - (if (get (meta x) "ismacro") - false - true) +(def! _map? (fn* [x] + (if (map? x) + (not (contains? x :__MAL_MACRO__)) false))) -(def! macro? (fn* [x] - (if (fn? x) - (if (get (meta x) "ismacro") - true - false) +(def! _macro? (fn* [x] + (if (map? x) + (contains? x :__MAL_MACRO__) false))) (def! core_ns - [["=" =] - ["throw" throw] - ["nil?" nil?] - ["true?" true?] - ["false?" false?] - ["number?" number?] - ["string?" string?] - ["symbol" symbol] - ["symbol?" symbol?] - ["keyword" keyword] - ["keyword?" keyword?] - ["fn?" _fn?] - ["macro?" macro?] + [['= =] + ['throw throw] + ['nil? nil?] + ['true? true?] + ['false? false?] + ['number? number?] + ['string? string?] + ['symbol symbol] + ['symbol? symbol?] + ['keyword keyword] + ['keyword? keyword?] + ['fn? fn?] + ['macro? _macro?] - ["pr-str" pr-str] - ["str" str] - ["prn" prn] - ["println" println] - ["readline" readline] - ["read-string" read-string] - ["slurp" slurp] - ["<" <] - ["<=" <=] - [">" >] - [">=" >=] - ["+" +] - ["-" -] - ["*" *] - ["/" /] - ["time-ms" time-ms] + ['pr-str pr-str] + ['str str] + ['prn prn] + ['println println] + ['readline readline] + ['read-string read-string] + ['slurp slurp] + ['< <] + ['<= <=] + ['> >] + ['>= >=] + ['+ +] + ['- -] + ['* *] + ['/ /] + ['time-ms time-ms] - ["list" list] - ["list?" list?] - ["vector" vector] - ["vector?" vector?] - ["hash-map" hash-map] - ["map?" map?] - ["assoc" assoc] - ["dissoc" dissoc] - ["get" get] - ["contains?" contains?] - ["keys" keys] - ["vals" vals] + ['list list] + ['list? list?] + ['vector vector] + ['vector? vector?] + ['hash-map hash-map] + ['map? _map?] + ['assoc assoc] + ['dissoc dissoc] + ['get get] + ['contains? contains?] + ['keys keys] + ['vals vals] - ["sequential?" sequential?] - ["cons" cons] - ["concat" concat] - ["nth" nth] - ["first" first] - ["rest" rest] - ["empty?" empty?] - ["count" count] - ["apply" apply] - ["map" map] + ['sequential? sequential?] + ['cons cons] + ['concat concat] + ['nth nth] + ['first first] + ['rest rest] + ['empty? empty?] + ['count count] + ['apply apply] + ['map map] - ["conj" conj] - ["seq" seq] + ['conj conj] + ['seq seq] - ["with-meta" with-meta] - ["meta" meta] - ["atom" atom] - ["atom?" atom?] - ["deref" deref] - ["reset!" reset!] - ["swap!" swap!]]) + ['with-meta with-meta] + ['meta meta] + ['atom atom] + ['atom? atom?] + ['deref deref] + ['reset! reset!] + ['swap! swap!]]) diff --git a/mal/env.mal b/mal/env.mal index bec21c37..d0ddee26 100644 --- a/mal/env.mal +++ b/mal/env.mal @@ -1,29 +1,23 @@ -;; env - (def! bind-env (fn* [env b e] (if (empty? b) env - - (if (= "&" (str (first b))) - (assoc env (str (nth b 1)) e) - - (bind-env (assoc env (str (first b)) (first e)) - (rest b) (rest e)))))) + (let* [b0 (first b)] + (if (= '& b0) + (assoc env (str (nth b 1)) e) + (bind-env (assoc env (str b0) (first e)) (rest b) (rest e))))))) (def! new-env (fn* [& args] (if (<= (count args) 1) (atom {:outer (first args)}) - (atom (bind-env {:outer (first args)} - (nth args 1) (nth args 2)))))) + (atom (apply bind-env {:outer (first args)} (rest args)))))) (def! env-find (fn* [env k] - (let* [ks (str k) - data @env] - (if (contains? data ks) - env - (if (get data :outer) - (env-find (get data :outer) ks) - nil))))) + (if env + (let* [ks (str k) + data @env] + (if (contains? data ks) + env + (env-find (get data :outer) ks)))))) (def! env-get (fn* [env k] (let* [ks (str k) @@ -36,5 +30,3 @@ (do (swap! env assoc (str k) v) v))) - -;;(prn "loaded env.mal") diff --git a/mal/step0_repl.mal b/mal/step0_repl.mal index 723c83c4..d4a7be83 100644 --- a/mal/step0_repl.mal +++ b/mal/step0_repl.mal @@ -3,7 +3,7 @@ strng)) ;; eval -(def! EVAL (fn* [ast env] +(def! EVAL (fn* [ast] ast)) ;; print @@ -11,20 +11,18 @@ ;; repl (def! rep (fn* [strng] - (PRINT (EVAL (READ strng) {})))) + (PRINT (EVAL (READ strng))))) ;; repl loop -(def! repl-loop (fn* [] - (let* [line (readline "mal-user> ")] - (if line - (do - (if (not (= "" line)) - (try* - (println (rep line)) - (catch* exc - (println "Uncaught exception:" exc)))) - (repl-loop)))))) +(def! repl-loop (fn* [line] + (if line + (do + (if (not (= "" line)) + (try* + (println (rep line)) + (catch* exc + (println "Uncaught exception:" exc)))) + (repl-loop (readline "mal-user> ")))))) -(def! -main (fn* [& args] - (repl-loop))) -(-main) +;; main +(repl-loop "") diff --git a/mal/step1_read_print.mal b/mal/step1_read_print.mal index 991a745f..dd541faa 100644 --- a/mal/step1_read_print.mal +++ b/mal/step1_read_print.mal @@ -1,30 +1,28 @@ ;; read -(def! READ (fn* [strng] - (read-string strng))) +(def! READ read-string) + ;; eval -(def! EVAL (fn* [ast env] +(def! EVAL (fn* [ast] ast)) ;; print -(def! PRINT (fn* [exp] (pr-str exp))) +(def! PRINT pr-str) ;; repl (def! rep (fn* [strng] - (PRINT (EVAL (READ strng) {})))) + (PRINT (EVAL (READ strng))))) ;; repl loop -(def! repl-loop (fn* [] - (let* [line (readline "mal-user> ")] - (if line - (do - (if (not (= "" line)) - (try* - (println (rep line)) - (catch* exc - (println "Uncaught exception:" exc)))) - (repl-loop)))))) +(def! repl-loop (fn* [line] + (if line + (do + (if (not (= "" line)) + (try* + (println (rep line)) + (catch* exc + (println "Uncaught exception:" exc)))) + (repl-loop (readline "mal-user> ")))))) -(def! -main (fn* [& args] - (repl-loop))) -(-main) +;; main +(repl-loop "") diff --git a/mal/step2_eval.mal b/mal/step2_eval.mal index 499ff94e..995c80dd 100644 --- a/mal/step2_eval.mal +++ b/mal/step2_eval.mal @@ -1,11 +1,10 @@ ;; read -(def! READ (fn* [strng] - (read-string strng))) +(def! READ read-string) ;; eval -(def! eval-ast (fn* [ast env] (do - ;;(do (prn "eval-ast" ast "/" (keys env)) ) +(def! eval-ast (fn* [ast env] + ;; (do (prn "eval-ast" ast "/" (keys env)) ) (cond (symbol? ast) (let* [res (get env (str ast))] (if res res (throw (str ast " not found")))) @@ -19,25 +18,23 @@ (map (fn* [k] [k (EVAL (get ast k) env)]) (keys ast)))) - "else" ast)))) + "else" ast))) -(def! EVAL (fn* [ast env] (do - ;;(do (prn "EVAL" ast "/" (keys @env)) ) +(def! EVAL (fn* [ast env] + ;; (do (prn "EVAL" ast) ) (if (not (list? ast)) (eval-ast ast env) ;; apply list (if (empty? ast) ast - (let* [el (eval-ast ast env) - f (first el) - args (rest el)] - (apply f args))))))) + (let* [el (eval-ast ast env)] + (apply (first el) (rest el))))))) ;; print -(def! PRINT (fn* [exp] (pr-str exp))) +(def! PRINT pr-str) ;; repl (def! repl-env {"+" + @@ -48,17 +45,15 @@ (PRINT (EVAL (READ strng) repl-env)))) ;; repl loop -(def! repl-loop (fn* [] - (let* [line (readline "mal-user> ")] - (if line - (do - (if (not (= "" line)) - (try* - (println (rep line)) - (catch* exc - (println "Uncaught exception:" exc)))) - (repl-loop)))))) +(def! repl-loop (fn* [line] + (if line + (do + (if (not (= "" line)) + (try* + (println (rep line)) + (catch* exc + (println "Uncaught exception:" exc)))) + (repl-loop (readline "mal-user> ")))))) -(def! -main (fn* [& args] - (repl-loop))) -(-main) +;; main +(repl-loop "") diff --git a/mal/step3_env.mal b/mal/step3_env.mal index 985e644d..d37cb07f 100644 --- a/mal/step3_env.mal +++ b/mal/step3_env.mal @@ -1,13 +1,12 @@ (load-file "../mal/env.mal") ;; read -(def! READ (fn* [strng] - (read-string strng))) +(def! READ read-string) ;; eval -(def! eval-ast (fn* [ast env] (do - ;;(do (prn "eval-ast" ast "/" (keys env)) ) +(def! eval-ast (fn* [ast env] + ;; (do (prn "eval-ast" ast "/" (keys env)) ) (cond (symbol? ast) (env-get env ast) @@ -20,43 +19,39 @@ (map (fn* [k] [k (EVAL (get ast k) env)]) (keys ast)))) - "else" ast)))) + "else" ast))) -(def! LET (fn* [env args] - (if (> (count args) 0) +(def! LET (fn* [env binds form] + (if (empty? binds) + (EVAL form env) (do - (env-set env (nth args 0) (EVAL (nth args 1) env)) - (LET env (rest (rest args))))))) + (env-set env (first binds) (EVAL (nth binds 1) env)) + (LET env (rest (rest binds)) form))))) -(def! EVAL (fn* [ast env] (do - ;;(do (prn "EVAL" ast "/" (keys @env)) ) +(def! EVAL (fn* [ast env] + ;; (do (prn "EVAL" ast "/" (keys @env)) ) (if (not (list? ast)) (eval-ast ast env) ;; apply list (let* [a0 (first ast)] (cond - (nil? a0) + (empty? ast) ast (= 'def! a0) (env-set env (nth ast 1) (EVAL (nth ast 2) env)) (= 'let* a0) - (let* [let-env (new-env env)] - (do - (LET let-env (nth ast 1)) - (EVAL (nth ast 2) let-env))) + (LET (new-env env) (nth ast 1) (nth ast 2)) "else" - (let* [el (eval-ast ast env) - f (first el) - args (rest el)] - (apply f args)))))))) + (let* [el (eval-ast ast env)] + (apply (first el) (rest el)))))))) ;; print -(def! PRINT (fn* [exp] (pr-str exp))) +(def! PRINT pr-str) ;; repl (def! repl-env (new-env)) @@ -69,17 +64,15 @@ (env-set repl-env "/" /) ;; repl loop -(def! repl-loop (fn* [] - (let* [line (readline "mal-user> ")] - (if line - (do - (if (not (= "" line)) - (try* - (println (rep line)) - (catch* exc - (println "Uncaught exception:" exc)))) - (repl-loop)))))) +(def! repl-loop (fn* [line] + (if line + (do + (if (not (= "" line)) + (try* + (println (rep line)) + (catch* exc + (println "Uncaught exception:" exc)))) + (repl-loop (readline "mal-user> ")))))) -(def! -main (fn* [& args] - (repl-loop))) -(-main) +;; main +(repl-loop "") diff --git a/mal/step4_if_fn_do.mal b/mal/step4_if_fn_do.mal index b72cd83e..05297be2 100644 --- a/mal/step4_if_fn_do.mal +++ b/mal/step4_if_fn_do.mal @@ -2,13 +2,12 @@ (load-file "../mal/core.mal") ;; read -(def! READ (fn* [strng] - (read-string strng))) +(def! READ read-string) ;; eval -(def! eval-ast (fn* [ast env] (do - ;;(do (prn "eval-ast" ast "/" (keys env)) ) +(def! eval-ast (fn* [ast env] + ;; (do (prn "eval-ast" ast "/" (keys env)) ) (cond (symbol? ast) (env-get env ast) @@ -21,59 +20,52 @@ (map (fn* [k] [k (EVAL (get ast k) env)]) (keys ast)))) - "else" ast)))) + "else" ast))) -(def! LET (fn* [env args] - (if (> (count args) 0) +(def! LET (fn* [env binds form] + (if (empty? binds) + (EVAL form env) (do - (env-set env (nth args 0) (EVAL (nth args 1) env)) - (LET env (rest (rest args))))))) + (env-set env (first binds) (EVAL (nth binds 1) env)) + (LET env (rest (rest binds)) form))))) -(def! EVAL (fn* [ast env] (do - ;;(do (prn "EVAL" ast "/" (keys @env)) ) +(def! EVAL (fn* [ast env] + ;; (do (prn "EVAL" ast "/" (keys @env)) ) (if (not (list? ast)) (eval-ast ast env) ;; apply list (let* [a0 (first ast)] (cond - (nil? a0) + (empty? ast) ast (= 'def! a0) (env-set env (nth ast 1) (EVAL (nth ast 2) env)) (= 'let* a0) - (let* [let-env (new-env env)] - (do - (LET let-env (nth ast 1)) - (EVAL (nth ast 2) let-env))) + (LET (new-env env) (nth ast 1) (nth ast 2)) (= 'do a0) (let* [el (eval-ast (rest ast) env)] (nth el (- (count el) 1))) (= 'if a0) - (let* [cond (EVAL (nth ast 1) env)] - (if (or (= cond nil) (= cond false)) - (if (> (count ast) 3) - (EVAL (nth ast 3) env) - nil) - (EVAL (nth ast 2) env))) + (if (EVAL (nth ast 1) env) + (EVAL (nth ast 2) env) + (if (> (count ast) 3) + (EVAL (nth ast 3) env))) (= 'fn* a0) - (fn* [& args] - (EVAL (nth ast 2) (new-env env (nth ast 1) args))) + (fn* [& args] (EVAL (nth ast 2) (new-env env (nth ast 1) args))) "else" - (let* [el (eval-ast ast env) - f (first el) - args (rest el)] - (apply f args)))))))) + (let* [el (eval-ast ast env)] + (apply (first el) (rest el)))))))) ;; print -(def! PRINT (fn* [exp] (pr-str exp))) +(def! PRINT pr-str) ;; repl (def! repl-env (new-env)) @@ -81,23 +73,21 @@ (PRINT (EVAL (READ strng) repl-env)))) ;; core.mal: defined directly using mal -(map (fn* [data] (env-set repl-env (nth data 0) (nth data 1))) core_ns) +(map (fn* [data] (apply env-set repl-env data)) core_ns) -;; core.mal: defined using the new language itself +;; core.mal: defined using the new language itself (rep "(def! not (fn* [a] (if a false true)))") ;; repl loop -(def! repl-loop (fn* [] - (let* [line (readline "mal-user> ")] - (if line - (do - (if (not (= "" line)) - (try* - (println (rep line)) - (catch* exc - (println "Uncaught exception:" exc)))) - (repl-loop)))))) +(def! repl-loop (fn* [line] + (if line + (do + (if (not (= "" line)) + (try* + (println (rep line)) + (catch* exc + (println "Uncaught exception:" exc)))) + (repl-loop (readline "mal-user> ")))))) -(def! -main (fn* [& args] - (repl-loop))) -(-main) +;; main +(repl-loop "") diff --git a/mal/step6_file.mal b/mal/step6_file.mal index 23df09aa..fbfeb897 100644 --- a/mal/step6_file.mal +++ b/mal/step6_file.mal @@ -2,13 +2,12 @@ (load-file "../mal/core.mal") ;; read -(def! READ (fn* [strng] - (read-string strng))) +(def! READ read-string) ;; eval -(def! eval-ast (fn* [ast env] (do - ;;(do (prn "eval-ast" ast "/" (keys env)) ) +(def! eval-ast (fn* [ast env] + ;; (do (prn "eval-ast" ast "/" (keys env)) ) (cond (symbol? ast) (env-get env ast) @@ -21,59 +20,52 @@ (map (fn* [k] [k (EVAL (get ast k) env)]) (keys ast)))) - "else" ast)))) + "else" ast))) -(def! LET (fn* [env args] - (if (> (count args) 0) +(def! LET (fn* [env binds form] + (if (empty? binds) + (EVAL form env) (do - (env-set env (nth args 0) (EVAL (nth args 1) env)) - (LET env (rest (rest args))))))) + (env-set env (first binds) (EVAL (nth binds 1) env)) + (LET env (rest (rest binds)) form))))) -(def! EVAL (fn* [ast env] (do - ;;(do (prn "EVAL" ast "/" (keys @env)) ) +(def! EVAL (fn* [ast env] + ;; (do (prn "EVAL" ast "/" (keys @env)) ) (if (not (list? ast)) (eval-ast ast env) ;; apply list (let* [a0 (first ast)] (cond - (nil? a0) + (empty? ast) ast (= 'def! a0) (env-set env (nth ast 1) (EVAL (nth ast 2) env)) (= 'let* a0) - (let* [let-env (new-env env)] - (do - (LET let-env (nth ast 1)) - (EVAL (nth ast 2) let-env))) + (LET (new-env env) (nth ast 1) (nth ast 2)) (= 'do a0) (let* [el (eval-ast (rest ast) env)] (nth el (- (count el) 1))) (= 'if a0) - (let* [cond (EVAL (nth ast 1) env)] - (if (or (= cond nil) (= cond false)) - (if (> (count ast) 3) - (EVAL (nth ast 3) env) - nil) - (EVAL (nth ast 2) env))) + (if (EVAL (nth ast 1) env) + (EVAL (nth ast 2) env) + (if (> (count ast) 3) + (EVAL (nth ast 3) env))) (= 'fn* a0) - (fn* [& args] - (EVAL (nth ast 2) (new-env env (nth ast 1) args))) + (fn* [& args] (EVAL (nth ast 2) (new-env env (nth ast 1) args))) "else" - (let* [el (eval-ast ast env) - f (first el) - args (rest el)] - (apply f args)))))))) + (let* [el (eval-ast ast env)] + (apply (first el) (rest el)))))))) ;; print -(def! PRINT (fn* [exp] (pr-str exp))) +(def! PRINT pr-str) ;; repl (def! repl-env (new-env)) @@ -81,28 +73,26 @@ (PRINT (EVAL (READ strng) repl-env)))) ;; core.mal: defined directly using mal -(map (fn* [data] (env-set repl-env (nth data 0) (nth data 1))) core_ns) +(map (fn* [data] (apply env-set repl-env data)) core_ns) (env-set repl-env 'eval (fn* [ast] (EVAL ast repl-env))) (env-set repl-env '*ARGV* (rest *ARGV*)) -;; core.mal: defined using the new language itself +;; core.mal: defined using the new language itself (rep "(def! not (fn* [a] (if a false true)))") (rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") ;; repl loop -(def! repl-loop (fn* [] - (let* [line (readline "mal-user> ")] - (if line - (do - (if (not (= "" line)) - (try* - (println (rep line)) - (catch* exc - (println "Uncaught exception:" exc)))) - (repl-loop)))))) +(def! repl-loop (fn* [line] + (if line + (do + (if (not (= "" line)) + (try* + (println (rep line)) + (catch* exc + (println "Uncaught exception:" exc)))) + (repl-loop (readline "mal-user> ")))))) -(def! -main (fn* [& args] - (if (> (count args) 0) - (rep (str "(load-file \"" (first args) "\")")) - (repl-loop)))) -(apply -main *ARGV*) +;; main +(if (empty? *ARGV*) + (repl-loop "") + (rep (str "(load-file \"" (first *ARGV*) "\")"))) diff --git a/mal/step7_quote.mal b/mal/step7_quote.mal index 85a74234..483898b6 100644 --- a/mal/step7_quote.mal +++ b/mal/step7_quote.mal @@ -2,34 +2,30 @@ (load-file "../mal/core.mal") ;; read -(def! READ (fn* [strng] - (read-string strng))) +(def! READ read-string) ;; eval (def! is-pair (fn* [x] (if (sequential? x) - (if (> (count x) 0) - true)))) + (not (empty? x))))) (def! QUASIQUOTE (fn* [ast] - (cond - (not (is-pair ast)) + (if (not (is-pair ast)) (list 'quote ast) + (let* [a0 (first ast)] + (cond + (= 'unquote a0) + (nth ast 1) - (= 'unquote (first ast)) - (nth ast 1) + (if (is-pair a0) (= 'splice-unquote (first a0))) ; `if` means `and` + (list 'concat (nth a0 1) (QUASIQUOTE (rest ast))) - (if (is-pair (first ast)) - (if (= 'splice-unquote (first (first ast))) - true)) - (list 'concat (nth (first ast) 1) (QUASIQUOTE (rest ast))) + "else" + (list 'cons (QUASIQUOTE a0) (QUASIQUOTE (rest ast)))))))) - "else" - (list 'cons (QUASIQUOTE (first ast)) (QUASIQUOTE (rest ast)))))) - -(def! eval-ast (fn* [ast env] (do - ;;(do (prn "eval-ast" ast "/" (keys env)) ) +(def! eval-ast (fn* [ast env] + ;; (do (prn "eval-ast" ast "/" (keys env)) ) (cond (symbol? ast) (env-get env ast) @@ -42,66 +38,58 @@ (map (fn* [k] [k (EVAL (get ast k) env)]) (keys ast)))) - "else" ast)))) + "else" ast))) -(def! LET (fn* [env args] - (if (> (count args) 0) +(def! LET (fn* [env binds form] + (if (empty? binds) + (EVAL form env) (do - (env-set env (nth args 0) (EVAL (nth args 1) env)) - (LET env (rest (rest args))))))) + (env-set env (first binds) (EVAL (nth binds 1) env)) + (LET env (rest (rest binds)) form))))) -(def! EVAL (fn* [ast env] (do - ;;(do (prn "EVAL" ast "/" (keys @env)) ) +(def! EVAL (fn* [ast env] + ;; (do (prn "EVAL" ast "/" (keys @env)) ) (if (not (list? ast)) (eval-ast ast env) ;; apply list (let* [a0 (first ast)] (cond - (nil? a0) + (empty? ast) ast (= 'def! a0) (env-set env (nth ast 1) (EVAL (nth ast 2) env)) (= 'let* a0) - (let* [let-env (new-env env)] - (do - (LET let-env (nth ast 1)) - (EVAL (nth ast 2) let-env))) + (LET (new-env env) (nth ast 1) (nth ast 2)) (= 'quote a0) (nth ast 1) (= 'quasiquote a0) - (let* [a1 (nth ast 1)] - (EVAL (QUASIQUOTE a1) env)) + (EVAL (QUASIQUOTE (nth ast 1)) env) (= 'do a0) (let* [el (eval-ast (rest ast) env)] (nth el (- (count el) 1))) (= 'if a0) - (let* [cond (EVAL (nth ast 1) env)] - (if (or (= cond nil) (= cond false)) - (if (> (count ast) 3) - (EVAL (nth ast 3) env) - nil) - (EVAL (nth ast 2) env))) + (if (EVAL (nth ast 1) env) + (EVAL (nth ast 2) env) + (if (> (count ast) 3) + (EVAL (nth ast 3) env))) (= 'fn* a0) - (fn* [& args] - (EVAL (nth ast 2) (new-env env (nth ast 1) args))) + (fn* [& args] (EVAL (nth ast 2) (new-env env (nth ast 1) args))) "else" - (let* [el (eval-ast ast env) - f (first el) - args (rest el)] - (apply f args)))))))) + (let* [el (eval-ast ast env)] + (apply (first el) (rest el)))))))) ;; print -(def! PRINT (fn* [exp] (pr-str exp))) +(def! PRINT pr-str) ;; repl (def! repl-env (new-env)) @@ -109,28 +97,26 @@ (PRINT (EVAL (READ strng) repl-env)))) ;; core.mal: defined directly using mal -(map (fn* [data] (env-set repl-env (nth data 0) (nth data 1))) core_ns) +(map (fn* [data] (apply env-set repl-env data)) core_ns) (env-set repl-env 'eval (fn* [ast] (EVAL ast repl-env))) (env-set repl-env '*ARGV* (rest *ARGV*)) -;; core.mal: defined using the new language itself +;; core.mal: defined using the new language itself (rep "(def! not (fn* [a] (if a false true)))") (rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") ;; repl loop -(def! repl-loop (fn* [] - (let* [line (readline "mal-user> ")] - (if line - (do - (if (not (= "" line)) - (try* - (println (rep line)) - (catch* exc - (println "Uncaught exception:" exc)))) - (repl-loop)))))) +(def! repl-loop (fn* [line] + (if line + (do + (if (not (= "" line)) + (try* + (println (rep line)) + (catch* exc + (println "Uncaught exception:" exc)))) + (repl-loop (readline "mal-user> ")))))) -(def! -main (fn* [& args] - (if (> (count args) 0) - (rep (str "(load-file \"" (first args) "\")")) - (repl-loop)))) -(apply -main *ARGV*) +;; main +(if (empty? *ARGV*) + (repl-loop "") + (rep (str "(load-file \"" (first *ARGV*) "\")"))) diff --git a/mal/step8_macros.mal b/mal/step8_macros.mal index ece64b77..0018d4c7 100644 --- a/mal/step8_macros.mal +++ b/mal/step8_macros.mal @@ -2,50 +2,45 @@ (load-file "../mal/core.mal") ;; read -(def! READ (fn* [strng] - (read-string strng))) +(def! READ read-string) ;; eval (def! is-pair (fn* [x] (if (sequential? x) - (if (> (count x) 0) - true)))) + (not (empty? x))))) (def! QUASIQUOTE (fn* [ast] - (cond - (not (is-pair ast)) + (if (not (is-pair ast)) (list 'quote ast) + (let* [a0 (first ast)] + (cond + (= 'unquote a0) + (nth ast 1) - (= 'unquote (first ast)) - (nth ast 1) + (if (is-pair a0) (= 'splice-unquote (first a0))) ; `if` means `and` + (list 'concat (nth a0 1) (QUASIQUOTE (rest ast))) - (if (is-pair (first ast)) - (if (= 'splice-unquote (first (first ast))) - true)) - (list 'concat (nth (first ast) 1) (QUASIQUOTE (rest ast))) - - "else" - (list 'cons (QUASIQUOTE (first ast)) (QUASIQUOTE (rest ast)))))) + "else" + (list 'cons (QUASIQUOTE a0) (QUASIQUOTE (rest ast)))))))) (def! is-macro-call (fn* [ast env] (if (list? ast) (let* [a0 (first ast)] (if (symbol? a0) (if (env-find env a0) - (let* [m (meta (env-get env a0))] - (if m - (if (get m "ismacro") - true))))))))) + (let* [m (env-get env a0)] + (if (_macro? m) + (get m :__MAL_MACRO__))))))))) (def! MACROEXPAND (fn* [ast env] - (if (is-macro-call ast env) - (let* [mac (env-get env (first ast))] - (MACROEXPAND (apply mac (rest ast)) env)) - ast))) + (let* [m (is-macro-call ast env)] + (if m + (MACROEXPAND (apply m (rest ast)) env) + ast)))) -(def! eval-ast (fn* [ast env] (do - ;;(do (prn "eval-ast" ast "/" (keys env)) ) +(def! eval-ast (fn* [ast env] + ;; (do (prn "eval-ast" ast "/" (keys env)) ) (cond (symbol? ast) (env-get env ast) @@ -53,87 +48,70 @@ (vector? ast) (apply vector (map (fn* [exp] (EVAL exp env)) ast)) - (map? ast) (apply hash-map + (_map? ast) (apply hash-map (apply concat (map (fn* [k] [k (EVAL (get ast k) env)]) (keys ast)))) - "else" ast)))) + "else" ast))) -(def! LET (fn* [env args] - (if (> (count args) 0) +(def! LET (fn* [env binds form] + (if (empty? binds) + (EVAL form env) (do - (env-set env (nth args 0) (EVAL (nth args 1) env)) - (LET env (rest (rest args))))))) + (env-set env (first binds) (EVAL (nth binds 1) env)) + (LET env (rest (rest binds)) form))))) -(def! EVAL (fn* [ast env] (do - ;;(do (prn "EVAL" ast "/" (keys @env)) ) - (if (not (list? ast)) - (eval-ast ast env) +(def! EVAL (fn* [ast env] + ;; (do (prn "EVAL" ast "/" (keys @env)) ) + (let* [ast (MACROEXPAND ast env)] + (if (not (list? ast)) + (eval-ast ast env) - ;; apply list - (let* [ast (MACROEXPAND ast env)] - (if (not (list? ast)) - (eval-ast ast env) + ;; apply list + (let* [a0 (first ast)] + (cond + (empty? ast) + ast - (let* [a0 (first ast)] - (cond - (nil? a0) - ast + (= 'def! a0) + (env-set env (nth ast 1) (EVAL (nth ast 2) env)) - (= 'def! a0) - (env-set env (nth ast 1) (EVAL (nth ast 2) env)) + (= 'let* a0) + (LET (new-env env) (nth ast 1) (nth ast 2)) - (= 'let* a0) - (let* [let-env (new-env env)] - (do - (LET let-env (nth ast 1)) - (EVAL (nth ast 2) let-env))) + (= 'quote a0) + (nth ast 1) - (= 'quote a0) - (nth ast 1) + (= 'quasiquote a0) + (EVAL (QUASIQUOTE (nth ast 1)) env) - (= 'quasiquote a0) - (let* [a1 (nth ast 1)] - (EVAL (QUASIQUOTE a1) env)) + (= 'defmacro! a0) + (env-set env (nth ast 1) {:__MAL_MACRO__ (EVAL (nth ast 2) env)}) - (= 'defmacro! a0) - (let* [a1 (nth ast 1) - a2 (nth ast 2) - f (EVAL a2 env) - m (or (meta f) {}) - mac (with-meta f (assoc m "ismacro" true))] - (env-set env a1 mac)) + (= 'macroexpand a0) + (MACROEXPAND (nth ast 1) env) - (= 'macroexpand a0) - (let* [a1 (nth ast 1)] - (MACROEXPAND a1 env)) + (= 'do a0) + (let* [el (eval-ast (rest ast) env)] + (nth el (- (count el) 1))) - (= 'do a0) - (let* [el (eval-ast (rest ast) env)] - (nth el (- (count el) 1))) + (= 'if a0) + (if (EVAL (nth ast 1) env) + (EVAL (nth ast 2) env) + (if (> (count ast) 3) + (EVAL (nth ast 3) env))) - (= 'if a0) - (let* [cond (EVAL (nth ast 1) env)] - (if (or (= cond nil) (= cond false)) - (if (> (count ast) 3) - (EVAL (nth ast 3) env) - nil) - (EVAL (nth ast 2) env))) + (= 'fn* a0) + (fn* [& args] (EVAL (nth ast 2) (new-env env (nth ast 1) args))) - (= 'fn* a0) - (fn* [& args] - (EVAL (nth ast 2) (new-env env (nth ast 1) args))) - - "else" - (let* [el (eval-ast ast env) - f (first el) - args (rest el)] - (apply f args)))))))))) + "else" + (let* [el (eval-ast ast env)] + (apply (first el) (rest el))))))))) ;; print -(def! PRINT (fn* [exp] (pr-str exp))) +(def! PRINT pr-str) ;; repl (def! repl-env (new-env)) @@ -141,30 +119,27 @@ (PRINT (EVAL (READ strng) repl-env)))) ;; core.mal: defined directly using mal -(map (fn* [data] (env-set repl-env (nth data 0) (nth data 1))) core_ns) +(map (fn* [data] (apply env-set repl-env data)) core_ns) (env-set repl-env 'eval (fn* [ast] (EVAL ast repl-env))) (env-set repl-env '*ARGV* (rest *ARGV*)) -;; core.mal: defined using the new language itself +;; core.mal: defined using the new 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))))))))") ;; repl loop -(def! repl-loop (fn* [] - (let* [line (readline "mal-user> ")] - (if line - (do - (if (not (= "" line)) - (try* - (println (rep line)) - (catch* exc - (println "Uncaught exception:" exc)))) - (repl-loop)))))) +(def! repl-loop (fn* [line] + (if line + (do + (if (not (= "" line)) + (try* + (println (rep line)) + (catch* exc + (println "Uncaught exception:" exc)))) + (repl-loop (readline "mal-user> ")))))) -(def! -main (fn* [& args] - (if (> (count args) 0) - (rep (str "(load-file \"" (first args) "\")")) - (repl-loop)))) -(apply -main *ARGV*) +;; main +(if (empty? *ARGV*) + (repl-loop "") + (rep (str "(load-file \"" (first *ARGV*) "\")"))) diff --git a/mal/step9_try.mal b/mal/step9_try.mal index 2975a504..676e93e7 100644 --- a/mal/step9_try.mal +++ b/mal/step9_try.mal @@ -2,50 +2,45 @@ (load-file "../mal/core.mal") ;; read -(def! READ (fn* [strng] - (read-string strng))) +(def! READ read-string) ;; eval (def! is-pair (fn* [x] (if (sequential? x) - (if (> (count x) 0) - true)))) + (not (empty? x))))) (def! QUASIQUOTE (fn* [ast] - (cond - (not (is-pair ast)) + (if (not (is-pair ast)) (list 'quote ast) + (let* [a0 (first ast)] + (cond + (= 'unquote a0) + (nth ast 1) - (= 'unquote (first ast)) - (nth ast 1) + (if (is-pair a0) (= 'splice-unquote (first a0))) ; `if` means `and` + (list 'concat (nth a0 1) (QUASIQUOTE (rest ast))) - (if (is-pair (first ast)) - (if (= 'splice-unquote (first (first ast))) - true)) - (list 'concat (nth (first ast) 1) (QUASIQUOTE (rest ast))) - - "else" - (list 'cons (QUASIQUOTE (first ast)) (QUASIQUOTE (rest ast)))))) + "else" + (list 'cons (QUASIQUOTE a0) (QUASIQUOTE (rest ast)))))))) (def! is-macro-call (fn* [ast env] (if (list? ast) (let* [a0 (first ast)] (if (symbol? a0) (if (env-find env a0) - (let* [m (meta (env-get env a0))] - (if m - (if (get m "ismacro") - true))))))))) + (let* [m (env-get env a0)] + (if (_macro? m) + (get m :__MAL_MACRO__))))))))) (def! MACROEXPAND (fn* [ast env] - (if (is-macro-call ast env) - (let* [mac (env-get env (first ast))] - (MACROEXPAND (apply mac (rest ast)) env)) - ast))) + (let* [m (is-macro-call ast env)] + (if m + (MACROEXPAND (apply m (rest ast)) env) + ast)))) -(def! eval-ast (fn* [ast env] (do - ;;(do (prn "eval-ast" ast "/" (keys env)) ) +(def! eval-ast (fn* [ast env] + ;; (do (prn "eval-ast" ast "/" (keys env)) ) (cond (symbol? ast) (env-get env ast) @@ -53,99 +48,79 @@ (vector? ast) (apply vector (map (fn* [exp] (EVAL exp env)) ast)) - (map? ast) (apply hash-map + (_map? ast) (apply hash-map (apply concat (map (fn* [k] [k (EVAL (get ast k) env)]) (keys ast)))) - "else" ast)))) + "else" ast))) -(def! LET (fn* [env args] - (if (> (count args) 0) +(def! LET (fn* [env binds form] + (if (empty? binds) + (EVAL form env) (do - (env-set env (nth args 0) (EVAL (nth args 1) env)) - (LET env (rest (rest args))))))) + (env-set env (first binds) (EVAL (nth binds 1) env)) + (LET env (rest (rest binds)) form))))) -(def! EVAL (fn* [ast env] (do - ;;(do (prn "EVAL" ast "/" (keys @env)) ) - (if (not (list? ast)) - (eval-ast ast env) +(def! EVAL (fn* [ast env] + ;; (do (prn "EVAL" ast "/" (keys @env)) ) + (let* [ast (MACROEXPAND ast env)] + (if (not (list? ast)) + (eval-ast ast env) - ;; apply list - (let* [ast (MACROEXPAND ast env)] - (if (not (list? ast)) - (eval-ast ast env) + ;; apply list + (let* [a0 (first ast)] + (cond + (empty? ast) + ast - (let* [a0 (first ast)] - (cond - (nil? a0) - ast + (= 'def! a0) + (env-set env (nth ast 1) (EVAL (nth ast 2) env)) - (= 'def! a0) - (env-set env (nth ast 1) (EVAL (nth ast 2) env)) + (= 'let* a0) + (LET (new-env env) (nth ast 1) (nth ast 2)) - (= 'let* a0) - (let* [let-env (new-env env)] - (do - (LET let-env (nth ast 1)) - (EVAL (nth ast 2) let-env))) + (= 'quote a0) + (nth ast 1) - (= 'quote a0) - (nth ast 1) + (= 'quasiquote a0) + (EVAL (QUASIQUOTE (nth ast 1)) env) - (= 'quasiquote a0) - (let* [a1 (nth ast 1)] - (EVAL (QUASIQUOTE a1) env)) + (= 'defmacro! a0) + (env-set env (nth ast 1) {:__MAL_MACRO__ (EVAL (nth ast 2) env)}) - (= 'defmacro! a0) - (let* [a1 (nth ast 1) - a2 (nth ast 2) - f (EVAL a2 env) - m (or (meta f) {}) - mac (with-meta f (assoc m "ismacro" true))] - (env-set env a1 mac)) + (= 'macroexpand a0) + (MACROEXPAND (nth ast 1) env) - (= 'macroexpand a0) - (let* [a1 (nth ast 1)] - (MACROEXPAND a1 env)) - - (= 'try* a0) - (if (or (< (count ast) 3) - (not (= 'catch* (nth (nth ast 2) 0)))) + (= 'try* a0) + (if (< (count ast) 3) + (EVAL (nth ast 1) env) + (try* (EVAL (nth ast 1) env) - (try* - (EVAL (nth ast 1) env) - (catch* exc - (EVAL (nth (nth ast 2) 2) - (new-env env - [(nth (nth ast 2)1)] - [exc]))))) + (catch* exc + (let* [a2 (nth ast 2)] + (EVAL (nth a2 2) (new-env env [(nth a2 1)] [exc])))))) - (= 'do a0) - (let* [el (eval-ast (rest ast) env)] - (nth el (- (count el) 1))) + (= 'do a0) + (let* [el (eval-ast (rest ast) env)] + (nth el (- (count el) 1))) - (= 'if a0) - (let* [cond (EVAL (nth ast 1) env)] - (if (or (= cond nil) (= cond false)) - (if (> (count ast) 3) - (EVAL (nth ast 3) env) - nil) - (EVAL (nth ast 2) env))) + (= 'if a0) + (if (EVAL (nth ast 1) env) + (EVAL (nth ast 2) env) + (if (> (count ast) 3) + (EVAL (nth ast 3) env))) - (= 'fn* a0) - (fn* [& args] - (EVAL (nth ast 2) (new-env env (nth ast 1) args))) + (= 'fn* a0) + (fn* [& args] (EVAL (nth ast 2) (new-env env (nth ast 1) args))) - "else" - (let* [el (eval-ast ast env) - f (first el) - args (rest el)] - (apply f args)))))))))) + "else" + (let* [el (eval-ast ast env)] + (apply (first el) (rest el))))))))) ;; print -(def! PRINT (fn* [exp] (pr-str exp))) +(def! PRINT pr-str) ;; repl (def! repl-env (new-env)) @@ -153,30 +128,27 @@ (PRINT (EVAL (READ strng) repl-env)))) ;; core.mal: defined directly using mal -(map (fn* [data] (env-set repl-env (nth data 0) (nth data 1))) core_ns) +(map (fn* [data] (apply env-set repl-env data)) core_ns) (env-set repl-env 'eval (fn* [ast] (EVAL ast repl-env))) (env-set repl-env '*ARGV* (rest *ARGV*)) -;; core.mal: defined using the new language itself +;; core.mal: defined using the new 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))))))))") ;; repl loop -(def! repl-loop (fn* [] - (let* [line (readline "mal-user> ")] - (if line - (do - (if (not (= "" line)) - (try* - (println (rep line)) - (catch* exc - (println "Uncaught exception:" exc)))) - (repl-loop)))))) +(def! repl-loop (fn* [line] + (if line + (do + (if (not (= "" line)) + (try* + (println (rep line)) + (catch* exc + (println "Uncaught exception:" exc)))) + (repl-loop (readline "mal-user> ")))))) -(def! -main (fn* [& args] - (if (> (count args) 0) - (rep (str "(load-file \"" (first args) "\")")) - (repl-loop)))) -(apply -main *ARGV*) +;; main +(if (empty? *ARGV*) + (repl-loop "") + (rep (str "(load-file \"" (first *ARGV*) "\")"))) diff --git a/mal/stepA_mal.mal b/mal/stepA_mal.mal index 09d7ce84..26feecf2 100644 --- a/mal/stepA_mal.mal +++ b/mal/stepA_mal.mal @@ -2,50 +2,45 @@ (load-file "../mal/core.mal") ;; read -(def! READ (fn* [strng] - (read-string strng))) +(def! READ read-string) ;; eval (def! is-pair (fn* [x] (if (sequential? x) - (if (> (count x) 0) - true)))) + (not (empty? x))))) (def! QUASIQUOTE (fn* [ast] - (cond - (not (is-pair ast)) + (if (not (is-pair ast)) (list 'quote ast) + (let* [a0 (first ast)] + (cond + (= 'unquote a0) + (nth ast 1) - (= 'unquote (first ast)) - (nth ast 1) + (if (is-pair a0) (= 'splice-unquote (first a0))) ; `if` means `and` + (list 'concat (nth a0 1) (QUASIQUOTE (rest ast))) - (if (is-pair (first ast)) - (if (= 'splice-unquote (first (first ast))) - true)) - (list 'concat (nth (first ast) 1) (QUASIQUOTE (rest ast))) - - "else" - (list 'cons (QUASIQUOTE (first ast)) (QUASIQUOTE (rest ast)))))) + "else" + (list 'cons (QUASIQUOTE a0) (QUASIQUOTE (rest ast)))))))) (def! is-macro-call (fn* [ast env] (if (list? ast) (let* [a0 (first ast)] (if (symbol? a0) (if (env-find env a0) - (let* [m (meta (env-get env a0))] - (if m - (if (get m "ismacro") - true))))))))) + (let* [m (env-get env a0)] + (if (_macro? m) + (get m :__MAL_MACRO__))))))))) (def! MACROEXPAND (fn* [ast env] - (if (is-macro-call ast env) - (let* [mac (env-get env (first ast))] - (MACROEXPAND (apply mac (rest ast)) env)) - ast))) + (let* [m (is-macro-call ast env)] + (if m + (MACROEXPAND (apply m (rest ast)) env) + ast)))) -(def! eval-ast (fn* [ast env] (do - ;;(do (prn "eval-ast" ast "/" (keys env)) ) +(def! eval-ast (fn* [ast env] + ;; (do (prn "eval-ast" ast "/" (keys env)) ) (cond (symbol? ast) (env-get env ast) @@ -53,99 +48,79 @@ (vector? ast) (apply vector (map (fn* [exp] (EVAL exp env)) ast)) - (map? ast) (apply hash-map + (_map? ast) (apply hash-map (apply concat (map (fn* [k] [k (EVAL (get ast k) env)]) (keys ast)))) - "else" ast)))) + "else" ast))) -(def! LET (fn* [env args] - (if (> (count args) 0) +(def! LET (fn* [env binds form] + (if (empty? binds) + (EVAL form env) (do - (env-set env (nth args 0) (EVAL (nth args 1) env)) - (LET env (rest (rest args))))))) + (env-set env (first binds) (EVAL (nth binds 1) env)) + (LET env (rest (rest binds)) form))))) -(def! EVAL (fn* [ast env] (do - ;;(do (prn "EVAL" ast "/" (keys @env)) ) - (if (not (list? ast)) - (eval-ast ast env) +(def! EVAL (fn* [ast env] + ;; (do (prn "EVAL" ast "/" (keys @env)) ) + (let* [ast (MACROEXPAND ast env)] + (if (not (list? ast)) + (eval-ast ast env) - ;; apply list - (let* [ast (MACROEXPAND ast env)] - (if (not (list? ast)) - (eval-ast ast env) + ;; apply list + (let* [a0 (first ast)] + (cond + (empty? ast) + ast - (let* [a0 (first ast)] - (cond - (nil? a0) - ast + (= 'def! a0) + (env-set env (nth ast 1) (EVAL (nth ast 2) env)) - (= 'def! a0) - (env-set env (nth ast 1) (EVAL (nth ast 2) env)) + (= 'let* a0) + (LET (new-env env) (nth ast 1) (nth ast 2)) - (= 'let* a0) - (let* [let-env (new-env env)] - (do - (LET let-env (nth ast 1)) - (EVAL (nth ast 2) let-env))) + (= 'quote a0) + (nth ast 1) - (= 'quote a0) - (nth ast 1) + (= 'quasiquote a0) + (EVAL (QUASIQUOTE (nth ast 1)) env) - (= 'quasiquote a0) - (let* [a1 (nth ast 1)] - (EVAL (QUASIQUOTE a1) env)) + (= 'defmacro! a0) + (env-set env (nth ast 1) {:__MAL_MACRO__ (EVAL (nth ast 2) env)}) - (= 'defmacro! a0) - (let* [a1 (nth ast 1) - a2 (nth ast 2) - f (EVAL a2 env) - m (or (meta f) {}) - mac (with-meta f (assoc m "ismacro" true))] - (env-set env a1 mac)) + (= 'macroexpand a0) + (MACROEXPAND (nth ast 1) env) - (= 'macroexpand a0) - (let* [a1 (nth ast 1)] - (MACROEXPAND a1 env)) - - (= 'try* a0) - (if (or (< (count ast) 3) - (not (= 'catch* (nth (nth ast 2) 0)))) + (= 'try* a0) + (if (< (count ast) 3) + (EVAL (nth ast 1) env) + (try* (EVAL (nth ast 1) env) - (try* - (EVAL (nth ast 1) env) - (catch* exc - (EVAL (nth (nth ast 2) 2) - (new-env env - [(nth (nth ast 2)1)] - [exc]))))) + (catch* exc + (let* [a2 (nth ast 2)] + (EVAL (nth a2 2) (new-env env [(nth a2 1)] [exc])))))) - (= 'do a0) - (let* [el (eval-ast (rest ast) env)] - (nth el (- (count el) 1))) + (= 'do a0) + (let* [el (eval-ast (rest ast) env)] + (nth el (- (count el) 1))) - (= 'if a0) - (let* [cond (EVAL (nth ast 1) env)] - (if (or (= cond nil) (= cond false)) - (if (> (count ast) 3) - (EVAL (nth ast 3) env) - nil) - (EVAL (nth ast 2) env))) + (= 'if a0) + (if (EVAL (nth ast 1) env) + (EVAL (nth ast 2) env) + (if (> (count ast) 3) + (EVAL (nth ast 3) env))) - (= 'fn* a0) - (fn* [& args] - (EVAL (nth ast 2) (new-env env (nth ast 1) args))) + (= 'fn* a0) + (fn* [& args] (EVAL (nth ast 2) (new-env env (nth ast 1) args))) - "else" - (let* [el (eval-ast ast env) - f (first el) - args (rest el)] - (apply f args)))))))))) + "else" + (let* [el (eval-ast ast env)] + (apply (first el) (rest el))))))))) ;; print -(def! PRINT (fn* [exp] (pr-str exp))) +(def! PRINT pr-str) ;; repl (def! repl-env (new-env)) @@ -153,35 +128,28 @@ (PRINT (EVAL (READ strng) repl-env)))) ;; core.mal: defined directly using mal -(map (fn* [data] (env-set repl-env (nth data 0) (nth data 1))) core_ns) +(map (fn* [data] (apply env-set repl-env data)) core_ns) (env-set repl-env 'eval (fn* [ast] (EVAL ast repl-env))) (env-set repl-env '*ARGV* (rest *ARGV*)) -;; core.mal: defined using the new language itself +;; core.mal: defined using the new language itself (rep (str "(def! *host-language* \"" *host-language* "-mal\")")) (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 "(def! inc (fn* [x] (+ x 1)))") -(rep "(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))") -(rep "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") ;; repl loop -(def! repl-loop (fn* [] - (let* [line (readline "mal-user> ")] - (if line - (do - (if (not (= "" line)) - (try* - (println (rep line)) - (catch* exc - (println "Uncaught exception:" exc)))) - (repl-loop)))))) - -(def! -main (fn* [& args] - (if (> (count args) 0) - (rep (str "(load-file \"" (first args) "\")")) +(def! repl-loop (fn* [line] + (if line (do - (rep "(println (str \"Mal [\" *host-language* \"]\"))") - (repl-loop))))) -(apply -main *ARGV*) + (if (not (= "" line)) + (try* + (println (rep line)) + (catch* exc + (println "Uncaught exception:" exc)))) + (repl-loop (readline "mal-user> ")))))) + +;; main +(if (empty? *ARGV*) + (repl-loop "(println (str \"Mal [\" *host-language* \"]\"))") + (rep (str "(load-file \"" (first *ARGV*) "\")"))) diff --git a/matlab/step8_macros.m b/matlab/step8_macros.m index 7e0b46ab..db638262 100644 --- a/matlab/step8_macros.m +++ b/matlab/step8_macros.m @@ -184,7 +184,6 @@ function main(args) rep('(def! not (fn* (a) (if a false true)))', repl_env); rep('(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")")))))"', repl_env); 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); - 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))))))))', repl_env); if ~isempty(args) rep(sprintf('(load-file "%s")', args{1}), repl_env); diff --git a/matlab/step9_try.m b/matlab/step9_try.m index da6447a7..a24d2a5d 100644 --- a/matlab/step9_try.m +++ b/matlab/step9_try.m @@ -208,7 +208,6 @@ function main(args) rep('(def! not (fn* (a) (if a false true)))', repl_env); rep('(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")")))))"', repl_env); 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); - 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))))))))', repl_env); if ~isempty(args) rep(sprintf('(load-file "%s")', args{1}), repl_env); diff --git a/matlab/stepA_mal.m b/matlab/stepA_mal.m index 67e6bf56..a882cfad 100644 --- a/matlab/stepA_mal.m +++ b/matlab/stepA_mal.m @@ -209,9 +209,6 @@ function main(args) rep('(def! not (fn* (a) (if a false true)))', repl_env); rep('(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")")))))"', repl_env); 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); - rep('(def! inc (fn* [x] (+ x 1)))', repl_env); - rep('(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str "G__" (swap! counter inc))))))', repl_env); - rep('(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))', repl_env); if ~isempty(args) rep(sprintf('(load-file "%s")', args{1}), repl_env); diff --git a/miniMAL/step8_macros.json b/miniMAL/step8_macros.json index b0b894d6..418806d1 100644 --- a/miniMAL/step8_macros.json +++ b/miniMAL/step8_macros.json @@ -152,7 +152,6 @@ ["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", ["not", ["empty?", "ARGS"]], ["rep", ["str", ["`", "(load-file \""], ["get", "ARGS", 0], ["`", "\")"]]], diff --git a/miniMAL/step9_try.json b/miniMAL/step9_try.json index 8b05eb8d..c57a1868 100644 --- a/miniMAL/step9_try.json +++ b/miniMAL/step9_try.json @@ -165,7 +165,6 @@ ["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", ["not", ["empty?", "ARGS"]], ["rep", ["str", ["`", "(load-file \""], ["get", "ARGS", 0], ["`", "\")"]]], diff --git a/miniMAL/stepA_mal.json b/miniMAL/stepA_mal.json index 401fa1ef..f7028535 100644 --- a/miniMAL/stepA_mal.json +++ b/miniMAL/stepA_mal.json @@ -166,9 +166,6 @@ ["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", ["`", "(def! inc (fn* [x] (+ x 1)))"]], -["rep", ["`", "(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))"]], -["rep", ["`", "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))"]], ["if", ["not", ["empty?", "ARGS"]], ["rep", ["str", ["`", "(load-file \""], ["get", "ARGS", 0], ["`", "\")"]]], diff --git a/nasm/step8_macros.asm b/nasm/step8_macros.asm index 04723181..06bc9167 100644 --- a/nasm/step8_macros.asm +++ b/nasm/step8_macros.asm @@ -74,7 +74,6 @@ section .data (def! not (fn* (a) (if a false true))) \ (def! load-file (fn* (f) (eval (read-string (str ",34,"(do",34," (slurp f) ",34,")",34," ))))) \ (defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw ",34,"odd number of forms to cond",34,")) (cons 'cond (rest (rest xs))))))) \ -(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)))))))) \ )" ;; Command to run, appending the name of the script to run diff --git a/nasm/step9_try.asm b/nasm/step9_try.asm index 8f2ffbcc..a92b03e5 100644 --- a/nasm/step9_try.asm +++ b/nasm/step9_try.asm @@ -80,7 +80,6 @@ section .data (def! not (fn* (a) (if a false true))) \ (def! load-file (fn* (f) (eval (read-string (str ",34,"(do",34," (slurp f) ",34,")",34," ))))) \ (defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw ",34,"odd number of forms to cond",34,")) (cons 'cond (rest (rest xs))))))) \ -(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)))))))) \ )" ;; Command to run, appending the name of the script to run diff --git a/nasm/stepA_mal.asm b/nasm/stepA_mal.asm index a574177a..4cdf8236 100644 --- a/nasm/stepA_mal.asm +++ b/nasm/stepA_mal.asm @@ -80,9 +80,6 @@ section .data (def! not (fn* (a) (if a false true))) \ (def! load-file (fn* (f) (eval (read-string (str ",34,"(do",34," (slurp f) ",34,")",34," ))))) \ (defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw ",34,"odd number of forms to cond",34,")) (cons 'cond (rest (rest xs))))))) \ -(def! inc (fn* [x] (+ x 1))) \ -(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str ",34,"G__",34," (swap! counter inc)))))) \ -(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)))))))) \ (def! *host-language* ",34,"nasm",34,")\ (def! conj nil)\ )" diff --git a/nasm/types.asm b/nasm/types.asm index c29d8fa3..56de3cb1 100644 --- a/nasm/types.asm +++ b/nasm/types.asm @@ -745,6 +745,10 @@ string_append_string: ; Source end address mov r11d, DWORD [rbx + Array.length] ; Length of the array add r11, r10 + + ; Check if the next array is empty + cmp r10, r11 + je .finished .source_ok: diff --git a/nim/step8_macros.nim b/nim/step8_macros.nim index 6de153b2..0e22bd70 100644 --- a/nim/step8_macros.nim +++ b/nim/step8_macros.nim @@ -155,7 +155,6 @@ proc rep(str: string): string {.discardable.} = 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 paramCount() >= 1: rep "(load-file \"" & paramStr(1) & "\")" diff --git a/nim/step9_try.nim b/nim/step9_try.nim index 314a42c9..f32d8d51 100644 --- a/nim/step9_try.nim +++ b/nim/step9_try.nim @@ -174,7 +174,6 @@ proc rep(str: string): string {.discardable.} = 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 paramCount() >= 1: rep "(load-file \"" & paramStr(1) & "\")" diff --git a/nim/stepA_mal.nim b/nim/stepA_mal.nim index 92e84a8e..bf47e2ee 100644 --- a/nim/stepA_mal.nim +++ b/nim/stepA_mal.nim @@ -174,9 +174,6 @@ proc rep(str: string): string {.discardable.} = 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 "(def! inc (fn* [x] (+ x 1)))" -rep "(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))" -rep "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))" rep "(def! *host-language* \"nim\")" if paramCount() >= 1: diff --git a/objc/step8_macros.m b/objc/step8_macros.m index 5c3f69e9..00e138c6 100644 --- a/objc/step8_macros.m +++ b/objc/step8_macros.m @@ -206,7 +206,6 @@ int main () { REP(@"(def! not (fn* (a) (if a false true)))", repl_env); REP(@"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env); 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); - 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))))))))", repl_env); if ([args count] > 1) { diff --git a/objc/step9_try.m b/objc/step9_try.m index 23fe89f0..5b4d278c 100644 --- a/objc/step9_try.m +++ b/objc/step9_try.m @@ -225,7 +225,6 @@ int main () { REP(@"(def! not (fn* (a) (if a false true)))", repl_env); REP(@"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env); 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); - 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))))))))", repl_env); if ([args count] > 1) { diff --git a/objc/stepA_mal.m b/objc/stepA_mal.m index 6b688104..fe21e1ca 100644 --- a/objc/stepA_mal.m +++ b/objc/stepA_mal.m @@ -226,9 +226,6 @@ int main () { REP(@"(def! not (fn* (a) (if a false true)))", repl_env); REP(@"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env); 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); - REP(@"(def! inc (fn* [x] (+ x 1)))", repl_env); - REP(@"(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))", repl_env); - REP(@"(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))", repl_env); if ([args count] > 1) { diff --git a/objpascal/step8_macros.pas b/objpascal/step8_macros.pas index a0d9dba6..e814663e 100644 --- a/objpascal/step8_macros.pas +++ b/objpascal/step8_macros.pas @@ -291,7 +291,6 @@ begin 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 ParamCount >= 1 then diff --git a/objpascal/step9_try.pas b/objpascal/step9_try.pas index d8c5960f..c93dbef4 100644 --- a/objpascal/step9_try.pas +++ b/objpascal/step9_try.pas @@ -313,7 +313,6 @@ begin 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 ParamCount >= 1 then diff --git a/objpascal/stepA_mal.pas b/objpascal/stepA_mal.pas index f77d0def..f71b8fa7 100644 --- a/objpascal/stepA_mal.pas +++ b/objpascal/stepA_mal.pas @@ -315,9 +315,6 @@ begin 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('(def! inc (fn* [x] (+ x 1)))'); - REP('(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str "G__" (swap! counter inc))))))'); - REP('(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))'); if ParamCount >= 1 then diff --git a/ocaml/reader.ml b/ocaml/reader.ml index 24cd1e92..b9e2bce7 100644 --- a/ocaml/reader.ml +++ b/ocaml/reader.ml @@ -19,6 +19,7 @@ let gsub re f str = (Str.full_split re str)) let token_re = (Str.regexp "~@\\|[][{}()'`~^@]\\|\"\\(\\\\.\\|[^\"]\\)*\"?\\|;.*\\|[^][ \n{}('\"`,;)]*") +let string_re = (Str.regexp "\"\\(\\\\.\\|[^\\\\\"]\\)*\"") type reader = { form : Types.mal_type; @@ -30,6 +31,18 @@ type list_reader = { tokens : string list; } +let unescape_string token = + if Str.string_match string_re token 0 + then + let without_quotes = String.sub token 1 ((String.length token) - 2) in + gsub (Str.regexp "\\\\.") + (function | "\\n" -> "\n" | x -> String.sub x 1 1) + without_quotes + else + (output_string stderr ("expected '\"', got EOF\n"); + flush stderr; + raise End_of_file) + let read_atom token = match token with | "nil" -> T.Nil @@ -43,15 +56,7 @@ let read_atom token = | _ -> (match token.[1] with | '0'..'9' -> T.Int (int_of_string token) | _ -> Types.symbol token)) - | '"' -> (match token.[String.length token - 1] with - | '"' -> T.String (gsub (Str.regexp "\\\\.") - (function - | "\\n" -> "\n" - | x -> String.sub x 1 1) - (String.sub token 1 ((String.length token) - 2))) - | _ -> output_string stderr ("expected '\"', got EOF\n"); - flush stderr; - raise End_of_file) + | '"' -> T.String (unescape_string token) | ':' -> T.Keyword (Str.replace_first (Str.regexp "^:") "" token) | _ -> Types.symbol token diff --git a/ocaml/step8_macros.ml b/ocaml/step8_macros.ml index 92ee6308..8b24abff 100644 --- a/ocaml/step8_macros.ml +++ b/ocaml/step8_macros.ml @@ -122,7 +122,6 @@ let rec main = ignore (rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" repl_env); ignore (rep "(def! not (fn* (a) (if a false true)))" repl_env); ignore (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); - ignore (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))))))))" repl_env); if Array.length Sys.argv > 1 then ignore (rep ("(load-file \"" ^ Sys.argv.(1) ^ "\")") repl_env) diff --git a/ocaml/step9_try.ml b/ocaml/step9_try.ml index daea9d8e..7557d82a 100644 --- a/ocaml/step9_try.ml +++ b/ocaml/step9_try.ml @@ -136,7 +136,6 @@ let rec main = ignore (rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" repl_env); ignore (rep "(def! not (fn* (a) (if a false true)))" repl_env); ignore (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); - ignore (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))))))))" repl_env); if Array.length Sys.argv > 1 then try diff --git a/ocaml/stepA_mal.ml b/ocaml/stepA_mal.ml index e1e42b29..41624214 100644 --- a/ocaml/stepA_mal.ml +++ b/ocaml/stepA_mal.ml @@ -137,9 +137,6 @@ let rec main = ignore (rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" repl_env); ignore (rep "(def! not (fn* (a) (if a false true)))" repl_env); ignore (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); - ignore (rep "(def! inc (fn* [x] (+ x 1)))" repl_env); - ignore (rep "(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))" repl_env); - ignore (rep "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))" repl_env); if Array.length Sys.argv > 1 then try diff --git a/perl/step8_macros.pl b/perl/step8_macros.pl index 62b4723c..3c8826ca 100644 --- a/perl/step8_macros.pl +++ b/perl/step8_macros.pl @@ -198,7 +198,6 @@ $repl_env->set(Symbol->new('*ARGV*'), List->new(\@_argv)); 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") { diff --git a/perl/step9_try.pl b/perl/step9_try.pl index 17ce2d4d..2eee0470 100644 --- a/perl/step9_try.pl +++ b/perl/step9_try.pl @@ -226,7 +226,6 @@ $repl_env->set(Symbol->new('*ARGV*'), List->new(\@_argv)); 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") { diff --git a/perl/stepA_mal.pl b/perl/stepA_mal.pl index 991c988b..2071c656 100644 --- a/perl/stepA_mal.pl +++ b/perl/stepA_mal.pl @@ -230,10 +230,6 @@ REP("(def! *host-language* \"perl\")"); 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("(def! inc (fn* [x] (+ x 1)))"); -REP("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))"); -REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))"); - if (scalar(@ARGV) > 0 && $ARGV[0] eq "--raw") { set_rl_mode("raw"); diff --git a/perl6/step8_macros.pl b/perl6/step8_macros.pl index 39491593..375388c8 100644 --- a/perl6/step8_macros.pl +++ b/perl6/step8_macros.pl @@ -127,7 +127,6 @@ sub MAIN ($source_file?, *@args) { rep(q{(def! not (fn* (a) (if a false true)))}); rep(q{(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")")))))}); rep(q{(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(q{(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 ($source_file.defined) { rep("(load-file \"$source_file\")"); diff --git a/perl6/step9_try.pl b/perl6/step9_try.pl index 23615889..6ce28129 100644 --- a/perl6/step9_try.pl +++ b/perl6/step9_try.pl @@ -137,7 +137,6 @@ sub MAIN ($source_file?, *@args) { rep(q{(def! not (fn* (a) (if a false true)))}); rep(q{(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")")))))}); rep(q{(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(q{(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 ($source_file.defined) { rep("(load-file \"$source_file\")"); diff --git a/perl6/stepA_mal.pl b/perl6/stepA_mal.pl index 2f558232..c70a96a6 100644 --- a/perl6/stepA_mal.pl +++ b/perl6/stepA_mal.pl @@ -138,9 +138,6 @@ sub MAIN ($source_file?, *@args) { rep(q{(def! not (fn* (a) (if a false true)))}); rep(q{(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")")))))}); rep(q{(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(q{(def! inc (fn* [x] (+ x 1)))}); - rep(q{(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str "G__" (swap! counter inc))))))}); - rep(q{(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))}); if ($source_file.defined) { rep("(load-file \"$source_file\")"); diff --git a/php/core.php b/php/core.php index 245b938e..7fe6d353 100644 --- a/php/core.php +++ b/php/core.php @@ -69,7 +69,8 @@ function get($hm, $k) { function contains_Q($hm, $k) { return array_key_exists($k, $hm); } function keys($hm) { - return call_user_func_array('_list', array_keys($hm->getArrayCopy())); + return call_user_func_array('_list', + array_map('strval', array_keys($hm->getArrayCopy()))); } function vals($hm) { return call_user_func_array('_list', array_values($hm->getArrayCopy())); diff --git a/php/printer.php b/php/printer.php index c82cbf31..d70d4ed7 100644 --- a/php/printer.php +++ b/php/printer.php @@ -18,7 +18,7 @@ function _pr_str($obj, $print_readably=True) { } elseif (_hash_map_Q($obj)) { $ret = array(); foreach (array_keys($obj->getArrayCopy()) as $k) { - $ret[] = _pr_str($k, $print_readably); + $ret[] = _pr_str("$k", $print_readably); $ret[] = _pr_str($obj[$k], $print_readably); } return "{" . implode(" ", $ret) . "}"; diff --git a/php/step8_macros.php b/php/step8_macros.php index 0537d61b..81ab58b4 100644 --- a/php/step8_macros.php +++ b/php/step8_macros.php @@ -174,7 +174,6 @@ $repl_env->set(_symbol('*ARGV*'), $_argv); 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 (count($argv) > 1) { rep('(load-file "' . $argv[1] . '")'); diff --git a/php/step9_try.php b/php/step9_try.php index 7f87f1b7..323ec3ce 100644 --- a/php/step9_try.php +++ b/php/step9_try.php @@ -192,7 +192,6 @@ $repl_env->set(_symbol('*ARGV*'), $_argv); 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 (count($argv) > 1) { rep('(load-file "' . $argv[1] . '")'); diff --git a/php/stepA_mal.php b/php/stepA_mal.php index c0cb0d48..dc89ab7d 100644 --- a/php/stepA_mal.php +++ b/php/stepA_mal.php @@ -201,9 +201,6 @@ 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)))))))"); -rep("(def! inc (fn* [x] (+ x 1)))"); -rep("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))"); -rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))"); // run mal file if (count($argv) > 1) { diff --git a/php/types.php b/php/types.php index 57157b31..d467cf4f 100644 --- a/php/types.php +++ b/php/types.php @@ -118,7 +118,7 @@ function _function_Q($obj) { return $obj instanceof FunctionClass; } function _fn_Q($obj) { return $obj instanceof Closure; } -// Parent class of list, vector, hash-map +// Parent class of list, vector // http://www.php.net/manual/en/class.arrayobject.php class SeqClass extends ArrayObject { public function slice($start, $length=NULL) { diff --git a/picolisp/step8_macros.l b/picolisp/step8_macros.l index 8bef4bb0..b1445d86 100644 --- a/picolisp/step8_macros.l +++ b/picolisp/step8_macros.l @@ -131,7 +131,7 @@ (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))))))))") + (load-history ".mal_history") diff --git a/picolisp/step9_try.l b/picolisp/step9_try.l index c5e42d43..74fe8c27 100644 --- a/picolisp/step9_try.l +++ b/picolisp/step9_try.l @@ -144,7 +144,6 @@ (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))))))))") (load-history ".mal_history") diff --git a/picolisp/stepA_mal.l b/picolisp/stepA_mal.l index 629562fd..94b5fd2f 100644 --- a/picolisp/stepA_mal.l +++ b/picolisp/stepA_mal.l @@ -146,10 +146,6 @@ (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 "(def! inc (fn* [x] (+ x 1)))") -(rep "(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))") -(rep "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") - (load-history ".mal_history") (if (argv) diff --git a/plpgsql/step8_macros.sql b/plpgsql/step8_macros.sql index c89acfdf..fe935af7 100644 --- a/plpgsql/step8_macros.sql +++ b/plpgsql/step8_macros.sql @@ -295,7 +295,6 @@ SELECT envs.vset(0, '*ARGV*', mal.READ('()')) \g '/dev/null' SELECT mal.REP('(def! not (fn* (a) (if a false true)))') \g '/dev/null' SELECT mal.REP('(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")")))))') \g '/dev/null' SELECT mal.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)))))))') \g '/dev/null' -SELECT mal.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))))))))') \g '/dev/null' CREATE FUNCTION mal.MAIN(pwd varchar, argstring varchar DEFAULT NULL) RETURNS integer AS $$ diff --git a/plpgsql/step9_try.sql b/plpgsql/step9_try.sql index 291cdaac..d3623bd2 100644 --- a/plpgsql/step9_try.sql +++ b/plpgsql/step9_try.sql @@ -314,7 +314,6 @@ SELECT envs.vset(0, '*ARGV*', mal.READ('()')) \g '/dev/null' SELECT mal.REP('(def! not (fn* (a) (if a false true)))') \g '/dev/null' SELECT mal.REP('(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")")))))') \g '/dev/null' SELECT mal.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)))))))') \g '/dev/null' -SELECT mal.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))))))))') \g '/dev/null' CREATE FUNCTION mal.MAIN(pwd varchar, argstring varchar DEFAULT NULL) RETURNS integer AS $$ diff --git a/plpgsql/stepA_mal.sql b/plpgsql/stepA_mal.sql index 448d812e..eb44d4f4 100644 --- a/plpgsql/stepA_mal.sql +++ b/plpgsql/stepA_mal.sql @@ -315,9 +315,6 @@ SELECT mal.REP('(def! *host-language* "plpqsql")') \g '/dev/null' SELECT mal.REP('(def! not (fn* (a) (if a false true)))') \g '/dev/null' SELECT mal.REP('(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")")))))') \g '/dev/null' SELECT mal.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)))))))') \g '/dev/null' -SELECT mal.REP('(def! inc (fn* [x] (+ x 1)))') \g '/dev/null' -SELECT mal.REP('(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str "G__" (swap! counter inc))))))') \g '/dev/null' -SELECT mal.REP('(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))') \g '/dev/null' CREATE FUNCTION mal.MAIN(pwd varchar, argstring varchar DEFAULT NULL) RETURNS integer AS $$ diff --git a/plsql/step8_macros.sql b/plsql/step8_macros.sql index c05b10a9..cee72aab 100644 --- a/plsql/step8_macros.sql +++ b/plsql/step8_macros.sql @@ -325,7 +325,6 @@ BEGIN line := REP('(def! not (fn* (a) (if a false true)))'); line := REP('(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")")))))'); line := 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)))))))'); - line := 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 argv.COUNT() > 0 THEN BEGIN diff --git a/plsql/step9_try.sql b/plsql/step9_try.sql index d69eee37..2e2bb521 100644 --- a/plsql/step9_try.sql +++ b/plsql/step9_try.sql @@ -411,7 +411,6 @@ BEGIN line := REP('(def! not (fn* (a) (if a false true)))'); line := REP('(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")")))))'); line := 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)))))))'); - line := 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 argv.COUNT() > 0 THEN BEGIN diff --git a/plsql/stepA_mal.sql b/plsql/stepA_mal.sql index 6c5f6c9e..9df1750d 100644 --- a/plsql/stepA_mal.sql +++ b/plsql/stepA_mal.sql @@ -412,9 +412,6 @@ BEGIN line := REP('(def! not (fn* (a) (if a false true)))'); line := REP('(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")")))))'); line := 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)))))))'); - line := REP('(def! inc (fn* [x] (+ x 1)))'); - line := REP('(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str "G__" (swap! counter inc))))))'); - line := REP('(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))'); IF argv.COUNT() > 0 THEN BEGIN diff --git a/powershell/core.psm1 b/powershell/core.psm1 index b2e592de..2c64dcdb 100644 --- a/powershell/core.psm1 +++ b/powershell/core.psm1 @@ -115,8 +115,8 @@ $core_ns = @{ "pr-str" = { pr_seq $args $true " " }; "str" = { pr_seq $args $false "" }; - "prn" = { Write-Host (pr_seq $args $true " ") }; - "println" = { Write-Host (pr_seq $args $false " ") }; + "prn" = { Write-Host (pr_seq $args $true " "); $null }; + "println" = { Write-Host (pr_seq $args $false " "); $null }; "read-string" = { read_str $args[0] }; "readline" = { Write-Host $args[0] -NoNewline; [Console]::Readline() }; "slurp" = { Get-Content -Path $args[0] -Raw }; diff --git a/powershell/step8_macros.ps1 b/powershell/step8_macros.ps1 index 295c91bd..243131f3 100644 --- a/powershell/step8_macros.ps1 +++ b/powershell/step8_macros.ps1 @@ -171,7 +171,6 @@ $_ = $repl_env.set((new-symbol "*ARGV*"), (new-list $args[1..$args.Count])) $_ = 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 ($args.Count -gt 0) { diff --git a/powershell/step9_try.ps1 b/powershell/step9_try.ps1 index e913d14f..e2f8f07f 100644 --- a/powershell/step9_try.ps1 +++ b/powershell/step9_try.ps1 @@ -187,7 +187,6 @@ $_ = $repl_env.set((new-symbol "*ARGV*"), (new-list $args[1..$args.Count])) $_ = 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 ($args.Count -gt 0) { diff --git a/powershell/stepA_mal.ps1 b/powershell/stepA_mal.ps1 index 1e616e79..be46d061 100644 --- a/powershell/stepA_mal.ps1 +++ b/powershell/stepA_mal.ps1 @@ -188,9 +188,6 @@ $_ = REP('(def! *host-language* "powershell")') $_ = 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('(def! inc (fn* [x] (+ x 1)))') -$_ = REP('(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str "G__" (swap! counter inc))))))') -$_ = REP('(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))') if ($args.Count -gt 0) { diff --git a/process/guide.md b/process/guide.md index 0e9c571b..9aa8a15b 100644 --- a/process/guide.md +++ b/process/guide.md @@ -1331,15 +1331,15 @@ implementation. Let us continue! * `rest`: this function takes a list (or vector) as its argument and returns a new list containing all the elements except the first. -* In the main program, use the `rep` function to define two new - control structures macros. Here are the string arguments for `rep` - to define these macros: - * `cond`: "(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)))))))" +* In the main program, call the `rep` function with the following + string argument to define a new control structure. +``` +"(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)))))))" +``` * Note that `cond` calls the `throw` function when `cond` is called with an odd number of args. The `throw` function is implemented in the next step, but it will still serve it's purpose here by causing an undefined symbol error. - * `or`: "(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))))))))" @@ -1521,27 +1521,6 @@ diff -urp ../process/step9_try.txt ../process/stepA_mal.txt entered by the user is returned as a string. If the user sends an end-of-file (usually Ctrl-D), then nil is returned. -* Add meta-data support to mal functions by adding a new metadata - attribute on mal functions that refers to another mal value/type - (nil by default). Add the following metadata related core functions: - * `meta`: this takes a single mal function argument and returns the - value of the metadata attribute. - * `with-meta`: this function takes two arguments. The first argument - is a mal function and the second argument is another mal - value/type to set as metadata. A copy of the mal function is - returned that has its `meta` attribute set to the second argument. - Note that it is important that the environment and macro attribute - of mal function are retained when it is copied. - * Add a reader-macro that expands the token "^" to - return a new list that contains the symbol "with-meta" and the - result of reading the next next form (2nd argument) (`read_form`) and the - next form (1st argument) in that order - (metadata comes first with the ^ macro and the function second). - * If you implemented as `defmacro!` to mutate an existing function - without copying it, you can now use the function copying mechanism - used for metadata to make functions immutable even in the - defmacro! case... - * Add a new "\*host-language\*" (symbol) entry to your REPL environment. The value of this entry should be a mal string containing the name of the current implementation. @@ -1552,6 +1531,7 @@ diff -urp ../process/step9_try.txt ../process/stepA_mal.txt "(println (str \"Mal [\" \*host-language\* \"]\"))". * Ensure that the REPL environment contains definitions for `time-ms`, + `meta`, `with-meta`, `fn?` `string?`, `number?`, `seq`, and `conj`. It doesn't really matter what they do at this stage: they just need to be defined. Making them functions that raise a "not implemented" exception would be @@ -1608,37 +1588,31 @@ implementation to run a mal implementation which itself runs the mal implementation. -#### Optional: gensym - -The `or` macro we introduced at step 8 has a bug. It defines a -variable called `or_FIXME`, which "shadows" such a binding from the -user's code (which uses the macro). If a user has a variable called -`or_FIXME`, it cannot be used as an `or` macro argument. In order to -fix that, we'll introduce `gensym`: a function which returns a symbol -which was never used before anywhere in the program. This is also an -example for the use of mal atoms to keep state (the state here being -the number of symbols produced by `gensym` so far). - -Previously you used `rep` to define the `or` macro. Remove that -definition and use `rep` to define the new counter, `gensym` function -and the clean `or` macro. Here are the string arguments you need to -pass to `rep`: -``` -"(def! inc (fn* [x] (+ x 1)))" - -"(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))" - -"(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))" -``` - -For extra information read [Peter Seibel's thorough discussion about -`gensym` and leaking macros in Common Lisp](http://www.gigamonkeys.com/book/macros-defining-your-own.html#plugging-the-leaks). - - #### Optional additions -* Add metadata support to other composite data types (lists, vectors - and hash-maps), and to native functions. +* Add meta-data support to composite data types (lists, vectors + and hash-maps), and to functions (native or not), by adding a new + metadata attribute that refers to another mal value/type + (nil by default). Add the following metadata related core functions + (and remove any stub versions): + * `meta`: this takes a single mal function argument and returns the + value of the metadata attribute. + * `with-meta`: this function takes two arguments. The first argument + is a mal function and the second argument is another mal + value/type to set as metadata. A copy of the mal function is + returned that has its `meta` attribute set to the second argument. + Note that it is important that the environment and macro attribute + of mal function are retained when it is copied. + * Add a reader-macro that expands the token "^" to + return a new list that contains the symbol "with-meta" and the + result of reading the next next form (2nd argument) (`read_form`) and the + next form (1st argument) in that order + (metadata comes first with the ^ macro and the function second). + * If you implemented as `defmacro!` to mutate an existing function + without copying it, you can now use the function copying mechanism + used for metadata to make functions immutable even in the + defmacro! case... + * Add the following new core functions (and remove any stub versions): * `time-ms`: takes no arguments and returns the number of milliseconds since epoch (00:00:00 UTC January 1, 1970), or, if diff --git a/process/step8_macros.txt b/process/step8_macros.txt index b84b73a3..93da781a 100644 --- a/process/step8_macros.txt +++ b/process/step8_macros.txt @@ -52,7 +52,6 @@ repl_env.set('*ARGV*, cmdline_args[1..]) 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 cmdline_args: rep("(load-file \"" + args[0] + "\")"); exit 0 diff --git a/process/step9_try.txt b/process/step9_try.txt index 0c070e8d..26418054 100644 --- a/process/step9_try.txt +++ b/process/step9_try.txt @@ -53,7 +53,6 @@ repl_env.set('*ARGV*, cmdline_args[1..]) 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 cmdline_args: rep("(load-file \"" + args[0] + "\")"); exit 0 diff --git a/process/stepA_mal.txt b/process/stepA_mal.txt index bbb8b405..b88a5afa 100644 --- a/process/stepA_mal.txt +++ b/process/stepA_mal.txt @@ -54,9 +54,6 @@ rep("(def! *host-language* \"racket\")") 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("(def! inc (fn* [x] (+ x 1)))") -rep("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))") -rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") if cmdline_args: rep("(load-file \"" + args[0] + "\")"); exit 0 diff --git a/ps/step8_macros.ps b/ps/step8_macros.ps index 51eabc98..925b1796 100644 --- a/ps/step8_macros.ps +++ b/ps/step8_macros.ps @@ -213,7 +213,6 @@ core_ns { _function _ref } forall (\(def! not \(fn* \(a\) \(if a false true\)\)\)) RE pop (\(def! load-file \(fn* \(f\) \(eval \(read-string \(str "\(do " \(slurp f\) "\)"\)\)\)\)\)) RE pop (\(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\)\)\)\)\)\)\)) RE pop -(\(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\)\)\)\)\)\)\)\)) RE pop userdict /ARGUMENTS known { %if command line arguments ARGUMENTS length 0 gt { %if more than 0 arguments diff --git a/ps/step9_try.ps b/ps/step9_try.ps index 207b6f65..c25168f1 100644 --- a/ps/step9_try.ps +++ b/ps/step9_try.ps @@ -253,7 +253,6 @@ core_ns { _function _ref } forall (\(def! not \(fn* \(a\) \(if a false true\)\)\)) RE pop (\(def! load-file \(fn* \(f\) \(eval \(read-string \(str "\(do " \(slurp f\) "\)"\)\)\)\)\)) RE pop (\(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\)\)\)\)\)\)\)) RE pop -(\(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\)\)\)\)\)\)\)\)) RE pop userdict /ARGUMENTS known { %if command line arguments ARGUMENTS length 0 gt { %if more than 0 arguments diff --git a/ps/stepA_mal.ps b/ps/stepA_mal.ps index 7cecd70b..f6032980 100644 --- a/ps/stepA_mal.ps +++ b/ps/stepA_mal.ps @@ -263,9 +263,6 @@ core_ns { _function _ref } forall (\(def! not \(fn* \(a\) \(if a false true\)\)\)) RE pop (\(def! load-file \(fn* \(f\) \(eval \(read-string \(str "\(do " \(slurp f\) "\)"\)\)\)\)\)) RE pop (\(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\)\)\)\)\)\)\)) RE pop -(\(def! inc \(fn* [x] \(+ x 1\)\)\)) RE pop -(\(def! gensym \(let* [counter \(atom 0\)] \(fn* [] \(symbol \(str "G__" \(swap! counter inc\)\)\)\)\)\)) RE pop -(\(defmacro! or \(fn* \(& xs\) \(if \(empty? xs\) nil \(if \(= 1 \(count xs\)\) \(first xs\) \(let* \(condvar \(gensym\)\) `\(let* \(~condvar ~\(first xs\)\) \(if ~condvar ~condvar \(or ~@\(rest xs\)\)\)\)\)\)\)\)\)) RE pop userdict /ARGUMENTS known { %if command line arguments ARGUMENTS length 0 gt { %if more than 0 arguments diff --git a/python/step8_macros.py b/python/step8_macros.py index cdf79693..9c8d392a 100644 --- a/python/step8_macros.py +++ b/python/step8_macros.py @@ -135,7 +135,6 @@ repl_env.set(types._symbol('*ARGV*'), types._list(*sys.argv[2:])) 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 len(sys.argv) >= 2: REP('(load-file "' + sys.argv[1] + '")') diff --git a/python/step9_try.py b/python/step9_try.py index a6fb4290..b1b591bc 100644 --- a/python/step9_try.py +++ b/python/step9_try.py @@ -157,7 +157,6 @@ repl_env.set(types._symbol('*ARGV*'), types._list(*sys.argv[2:])) 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 len(sys.argv) >= 2: REP('(load-file "' + sys.argv[1] + '")') diff --git a/python/stepA_mal.py b/python/stepA_mal.py index 0cada9c4..c9bae23e 100644 --- a/python/stepA_mal.py +++ b/python/stepA_mal.py @@ -161,9 +161,6 @@ 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)))))))") -REP("(def! inc (fn* [x] (+ x 1)))") -REP("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))") -REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") if len(sys.argv) >= 2: REP('(load-file "' + sys.argv[1] + '")') diff --git a/r/Makefile b/r/Makefile index f9ec4a74..1b7e65e2 100644 --- a/r/Makefile +++ b/r/Makefile @@ -2,6 +2,10 @@ SOURCES_BASE = readline.r types.r reader.r printer.r SOURCES_LISP = env.r core.r stepA_mal.r SOURCES = $(SOURCES_BASE) $(SOURCES_LISP) +STEPS = step0_repl.r step1_read_print.r step2_eval.r step3_env.r \ + step4_if_fn_do.r step5_tco.r step6_file.r \ + step7_quote.r step8_macros.r step9_try.r stepA_mal.r + all: libs dist: mal.r mal @@ -14,8 +18,7 @@ mal: mal.r cat $< >> $@ chmod +x $@ -clean: - rm -f mal.r mal +$(STEPS): libs .PHONY: libs: lib/rdyncall @@ -25,3 +28,8 @@ lib/rdyncall: mkdir -p lib R CMD INSTALL rdyncall_0.7.5.tar.gz -l lib/ rm rdyncall_0.7.5.tar.gz + +clean: + rm -f mal.r mal + + diff --git a/r/step8_macros.r b/r/step8_macros.r index d8d3ba19..33d251f7 100644 --- a/r/step8_macros.r +++ b/r/step8_macros.r @@ -154,7 +154,6 @@ Env.set(repl_env, "*ARGV*", new.list()) . <- 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))))))))") args <- commandArgs(trailingOnly = TRUE) diff --git a/r/step9_try.r b/r/step9_try.r index 049d6605..f3c5d421 100644 --- a/r/step9_try.r +++ b/r/step9_try.r @@ -168,7 +168,6 @@ Env.set(repl_env, "*ARGV*", new.list()) . <- 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))))))))") args <- commandArgs(trailingOnly = TRUE) diff --git a/r/stepA_mal.r b/r/stepA_mal.r index b448ff98..2d610526 100644 --- a/r/stepA_mal.r +++ b/r/stepA_mal.r @@ -169,9 +169,6 @@ Env.set(repl_env, "*ARGV*", new.list()) . <- 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("(def! inc (fn* [x] (+ x 1)))") -. <- rep("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))") -. <- rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") args <- commandArgs(trailingOnly = TRUE) diff --git a/racket/step8_macros.rkt b/racket/step8_macros.rkt index ca281057..ebcb71e8 100755 --- a/racket/step8_macros.rkt +++ b/racket/step8_macros.rkt @@ -126,7 +126,6 @@ (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))))))))") ) diff --git a/racket/step9_try.rkt b/racket/step9_try.rkt index 79f21b63..06ec7581 100755 --- a/racket/step9_try.rkt +++ b/racket/step9_try.rkt @@ -142,7 +142,6 @@ (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))))))))") ) diff --git a/racket/stepA_mal.rkt b/racket/stepA_mal.rkt index aea36db7..dfdfb3ad 100755 --- a/racket/stepA_mal.rkt +++ b/racket/stepA_mal.rkt @@ -143,9 +143,6 @@ (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 "(def! inc (fn* [x] (+ x 1)))") -(rep "(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))") -(rep "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") ) diff --git a/rexx/core.rexx b/rexx/core.rexx index 647317a3..63fe5551 100644 --- a/rexx/core.rexx +++ b/rexx/core.rexx @@ -99,7 +99,7 @@ mal_readline: procedure expose values. /* mal_readline(prompt) */ return new_nil() mal_slurp: procedure expose values. /* mal_read_string(filename) */ - file_content = charin(obj_val(arg(1)), , 100000) + file_content = charin(obj_val(arg(1)), 1, 100000) return new_string(file_content) mal_lt: procedure expose values. /* mal_lt(a, b) */ diff --git a/rexx/step8_macros.rexx b/rexx/step8_macros.rexx index 7430a0b0..60482a09 100644 --- a/rexx/step8_macros.rexx +++ b/rexx/step8_macros.rexx @@ -245,7 +245,6 @@ main: x = re("(def! not (fn* (a) (if a false true)))") x = re('(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")")))))') x = re("(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)))))))"); - x = re("(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))))))))"); err = "" if command_line_args.0 > 0 then do diff --git a/rexx/step9_try.rexx b/rexx/step9_try.rexx index 86a6d6e9..d201b37a 100644 --- a/rexx/step9_try.rexx +++ b/rexx/step9_try.rexx @@ -261,7 +261,6 @@ main: x = re("(def! not (fn* (a) (if a false true)))") x = re('(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")")))))') x = re("(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)))))))"); - x = re("(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))))))))"); err = "" if command_line_args.0 > 0 then do diff --git a/rexx/stepA_mal.rexx b/rexx/stepA_mal.rexx index 04c2da19..3c127a3f 100644 --- a/rexx/stepA_mal.rexx +++ b/rexx/stepA_mal.rexx @@ -263,9 +263,6 @@ main: x = re("(def! not (fn* (a) (if a false true)))") x = re('(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")")))))') x = re("(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)))))))"); - x = re("(def! inc (fn* [x] (+ x 1)))") - x = re('(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str "G__" (swap! counter inc))))))') - x = re("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") err = "" if command_line_args.0 > 0 then do diff --git a/rpython/step8_macros.py b/rpython/step8_macros.py index c231b10d..811cd14b 100644 --- a/rpython/step8_macros.py +++ b/rpython/step8_macros.py @@ -168,7 +168,6 @@ def entry_point(argv): REP("(def! not (fn* (a) (if a false true)))", repl_env) REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env) 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) - 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))))))))", repl_env) if len(argv) >= 2: REP('(load-file "' + argv[1] + '")', repl_env) diff --git a/rpython/step9_try.py b/rpython/step9_try.py index 16a40622..9c58d7b2 100644 --- a/rpython/step9_try.py +++ b/rpython/step9_try.py @@ -186,7 +186,6 @@ def entry_point(argv): REP("(def! not (fn* (a) (if a false true)))", repl_env) REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env) 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) - 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))))))))", repl_env) if len(argv) >= 2: REP('(load-file "' + argv[1] + '")', repl_env) diff --git a/rpython/stepA_mal.py b/rpython/stepA_mal.py index 5fd5592b..6c73a48c 100644 --- a/rpython/stepA_mal.py +++ b/rpython/stepA_mal.py @@ -196,9 +196,6 @@ def entry_point(argv): REP("(def! not (fn* (a) (if a false true)))", repl_env) REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env) 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) - REP("(def! inc (fn* [x] (+ x 1)))", repl_env) - REP("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))", repl_env) - REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))", repl_env) if len(argv) >= 2: REP('(load-file "' + argv[1] + '")', repl_env) diff --git a/ruby/step8_macros.rb b/ruby/step8_macros.rb index e29e1e09..46a64e53 100644 --- a/ruby/step8_macros.rb +++ b/ruby/step8_macros.rb @@ -147,7 +147,6 @@ repl_env.set(:"*ARGV*", List.new(ARGV.slice(1,ARGV.length) || [])) RE["(def! not (fn* (a) (if a false true)))"] RE["(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"] RE["(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)))))))"] -RE["(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 ARGV.size > 0 RE["(load-file \"" + ARGV[0] + "\")"] diff --git a/ruby/step9_try.rb b/ruby/step9_try.rb index 3a004912..96fc5ee2 100644 --- a/ruby/step9_try.rb +++ b/ruby/step9_try.rb @@ -162,7 +162,6 @@ repl_env.set(:"*ARGV*", List.new(ARGV.slice(1,ARGV.length) || [])) RE["(def! not (fn* (a) (if a false true)))"] RE["(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"] RE["(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)))))))"] -RE["(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 ARGV.size > 0 RE["(load-file \"" + ARGV[0] + "\")"] diff --git a/ruby/stepA_mal.rb b/ruby/stepA_mal.rb index 756811a6..14f7f785 100644 --- a/ruby/stepA_mal.rb +++ b/ruby/stepA_mal.rb @@ -169,9 +169,6 @@ RE["(def! *host-language* \"ruby\")"] RE["(def! not (fn* (a) (if a false true)))"] RE["(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"] RE["(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)))))))"] -RE["(def! inc (fn* [x] (+ x 1)))"] -RE["(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))"] -RE["(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))"] if ARGV.size > 0 RE["(load-file \"" + ARGV[0] + "\")"] diff --git a/rust/step8_macros.rs b/rust/step8_macros.rs index 2c3bd341..02cb6de1 100644 --- a/rust/step8_macros.rs +++ b/rust/step8_macros.rs @@ -303,7 +303,6 @@ fn main() { let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env); let _ = rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", &repl_env); 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); - let _ = 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))))))))", &repl_env); // Invoked with arguments diff --git a/rust/step9_try.rs b/rust/step9_try.rs index 3c7874ff..1d91d762 100644 --- a/rust/step9_try.rs +++ b/rust/step9_try.rs @@ -324,7 +324,6 @@ fn main() { let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env); let _ = rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", &repl_env); 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); - let _ = 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))))))))", &repl_env); // Invoked with arguments diff --git a/rust/stepA_mal.rs b/rust/stepA_mal.rs index b65359ae..2692fdde 100644 --- a/rust/stepA_mal.rs +++ b/rust/stepA_mal.rs @@ -327,9 +327,6 @@ fn main() { let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env); let _ = rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", &repl_env); 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); - let _ = rep("(def! inc (fn* [x] (+ x 1)))", &repl_env); - let _ = rep("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))", &repl_env); - let _ = rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))", &repl_env); // Invoked with arguments diff --git a/scala/step8_macros.scala b/scala/step8_macros.scala index 48d15c25..06206125 100644 --- a/scala/step8_macros.scala +++ b/scala/step8_macros.scala @@ -186,7 +186,6 @@ object step8_macros { 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 (args.length > 0) { diff --git a/scala/step9_try.scala b/scala/step9_try.scala index cc997bbe..fe6df011 100644 --- a/scala/step9_try.scala +++ b/scala/step9_try.scala @@ -207,7 +207,6 @@ object step9_try { 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 (args.length > 0) { diff --git a/scala/stepA_mal.scala b/scala/stepA_mal.scala index 722620a5..bbdbd098 100644 --- a/scala/stepA_mal.scala +++ b/scala/stepA_mal.scala @@ -208,9 +208,6 @@ object stepA_mal { 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("(def! inc (fn* [x] (+ x 1)))") - REP("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))") - REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") if (args.length > 0) { diff --git a/scheme/lib/core.sld b/scheme/lib/core.sld index 4d615b8d..54703824 100644 --- a/scheme/lib/core.sld +++ b/scheme/lib/core.sld @@ -249,7 +249,9 @@ (symbol? . ,(lambda (x) (coerce (mal-instance-of? x 'symbol)))) (symbol . ,(lambda (x) (mal-symbol (string->symbol (mal-value x))))) (keyword? . ,(lambda (x) (coerce (mal-instance-of? x 'keyword)))) - (keyword . ,(lambda (x) (mal-keyword (string->symbol (mal-value x))))) + (keyword . ,(lambda (x) (if (mal-instance-of? x 'keyword) + x + (mal-keyword (string->symbol (mal-value x)))))) (vector? . ,(lambda (x) (coerce (mal-instance-of? x 'vector)))) (vector . ,(lambda args (mal-vector (list->vector args)))) (map? . ,(lambda (x) (coerce (mal-instance-of? x 'map)))) @@ -275,7 +277,7 @@ ((func? x) (let ((func (make-func (func-ast x) (func-params x) (func-env x) (func-fn x)))) - (func-macro?-set! func (func-macro? x)) + (func-macro?-set! func #f) (func-meta-set! func meta) func)) (else diff --git a/scheme/step8_macros.scm b/scheme/step8_macros.scm index c9f177e3..07bc86c7 100644 --- a/scheme/step8_macros.scm +++ b/scheme/step8_macros.scm @@ -171,9 +171,7 @@ (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))))))))") (define (main) diff --git a/scheme/step9_try.scm b/scheme/step9_try.scm index b28e786c..a8d99cde 100644 --- a/scheme/step9_try.scm +++ b/scheme/step9_try.scm @@ -189,9 +189,7 @@ (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))))))))") (define (main) diff --git a/scheme/stepA_mal.scm b/scheme/stepA_mal.scm index 408eed3e..af1f1045 100644 --- a/scheme/stepA_mal.scm +++ b/scheme/stepA_mal.scm @@ -191,11 +191,6 @@ (rep "(def! not (fn* (a) (if a false true)))") (rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") - -(rep "(def! inc (fn* [x] (+ x 1)))") -(rep "(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))") - -(rep "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") (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)))))))") (define (main) diff --git a/skew/step8_macros.sk b/skew/step8_macros.sk index 44565979..3c6307b7 100644 --- a/skew/step8_macros.sk +++ b/skew/step8_macros.sk @@ -153,7 +153,6 @@ def main { RE("(def! not (fn* (a) (if a false true)))") RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") RE("(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)))))))") - RE("(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 argv.count > 0 { RE("(load-file \"" + argv[0] + "\")") diff --git a/skew/step9_try.sk b/skew/step9_try.sk index e9516315..bb93792a 100644 --- a/skew/step9_try.sk +++ b/skew/step9_try.sk @@ -167,7 +167,6 @@ def main { RE("(def! not (fn* (a) (if a false true)))") RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") RE("(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)))))))") - RE("(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 argv.count > 0 { RE("(load-file \"" + argv[0] + "\")") diff --git a/skew/stepA_mal.sk b/skew/stepA_mal.sk index 798aa156..983c3e10 100644 --- a/skew/stepA_mal.sk +++ b/skew/stepA_mal.sk @@ -168,9 +168,6 @@ def main { RE("(def! not (fn* (a) (if a false true)))") RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") RE("(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)))))))") - RE("(def! inc (fn* [x] (+ x 1)))") - RE("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))") - RE("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") if argv.count > 0 { RE("(load-file \"" + argv[0] + "\")") diff --git a/swift/step8_macros.swift b/swift/step8_macros.swift index e0391ced..eb99f0fb 100644 --- a/swift/step8_macros.swift +++ b/swift/step8_macros.swift @@ -583,8 +583,6 @@ func main() { RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", env) RE("(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)))))))", env) - RE("(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))))))))", env) env.set(kSymbolEval, make_builtin({ try! unwrap_args($0) { diff --git a/swift/step9_try.swift b/swift/step9_try.swift index b0b8b314..40973200 100644 --- a/swift/step9_try.swift +++ b/swift/step9_try.swift @@ -616,8 +616,6 @@ func main() { RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", env) RE("(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)))))))", env) - RE("(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))))))))", env) env.set(kSymbolEval, make_builtin({ try! unwrap_args($0) { diff --git a/swift/stepA_mal.swift b/swift/stepA_mal.swift index d1b0adf0..cbdaa79a 100644 --- a/swift/stepA_mal.swift +++ b/swift/stepA_mal.swift @@ -617,10 +617,6 @@ func main() { RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", env) RE("(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)))))))", env) - RE("(def! inc (fn* [x] (+ x 1)))", env) - RE("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))", env) - RE("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) " + - "(let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))", env) env.set(kSymbolEval, make_builtin({ try! unwrap_args($0) { diff --git a/swift3/Sources/step8_macros/main.swift b/swift3/Sources/step8_macros/main.swift index a53483f0..46c3cc3a 100644 --- a/swift3/Sources/step8_macros/main.swift +++ b/swift3/Sources/step8_macros/main.swift @@ -217,7 +217,6 @@ try repl_env.set(MalVal.MalSymbol("*ARGV*"), list(Array(args))) try rep("(def! not (fn* (a) (if a false true)))") try rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") try 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)))))))") -try 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 CommandLine.arguments.count > 1 { diff --git a/swift3/Sources/step9_try/main.swift b/swift3/Sources/step9_try/main.swift index 900dd3e3..7344bba4 100644 --- a/swift3/Sources/step9_try/main.swift +++ b/swift3/Sources/step9_try/main.swift @@ -250,7 +250,6 @@ try repl_env.set(MalVal.MalSymbol("*ARGV*"), list(Array(args))) try rep("(def! not (fn* (a) (if a false true)))") try rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") try 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)))))))") -try 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 CommandLine.arguments.count > 1 { diff --git a/swift3/Sources/stepA_mal/main.swift b/swift3/Sources/stepA_mal/main.swift index b7a51532..55d81547 100644 --- a/swift3/Sources/stepA_mal/main.swift +++ b/swift3/Sources/stepA_mal/main.swift @@ -251,9 +251,6 @@ try rep("(def! *host-language* \"swift\")") try rep("(def! not (fn* (a) (if a false true)))") try rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") try 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)))))))") -try rep("(def! inc (fn* [x] (+ x 1)))") -try rep("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))") -try rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") if CommandLine.arguments.count > 1 { diff --git a/swift4/Sources/step8_macros/main.swift b/swift4/Sources/step8_macros/main.swift index e4bc7ef9..b9d1f48a 100644 --- a/swift4/Sources/step8_macros/main.swift +++ b/swift4/Sources/step8_macros/main.swift @@ -166,7 +166,6 @@ repl_env.set([], forKey: Symbol("*ARGV*")) try rep("(def! not (fn* (a) (if a false true)))", env: repl_env) try rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", env: repl_env) try 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)))))))", env: repl_env) -try 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))))))))", env: repl_env) if CommandLine.argc > 1 { let fileName = CommandLine.arguments[1], diff --git a/swift4/Sources/step9_try/main.swift b/swift4/Sources/step9_try/main.swift index 26624f72..c2bc8ec6 100644 --- a/swift4/Sources/step9_try/main.swift +++ b/swift4/Sources/step9_try/main.swift @@ -178,7 +178,6 @@ repl_env.set([], forKey: Symbol("*ARGV*")) try rep("(def! not (fn* (a) (if a false true)))", env: repl_env) try rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", env: repl_env) try 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)))))))", env: repl_env) -try 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))))))))", env: repl_env) if CommandLine.argc > 1 { let fileName = CommandLine.arguments[1], diff --git a/swift4/Sources/stepA_mal/main.swift b/swift4/Sources/stepA_mal/main.swift index a2cc2b74..a1f9eb51 100644 --- a/swift4/Sources/stepA_mal/main.swift +++ b/swift4/Sources/stepA_mal/main.swift @@ -180,9 +180,6 @@ repl_env.set("Swift4", forKey: Symbol("*host-language*")) try rep("(def! not (fn* (a) (if a false true)))", env: repl_env) try rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", env: repl_env) try 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)))))))", env: repl_env) -try rep("(def! inc (fn* [x] (+ x 1)))", env: repl_env) -try rep("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))", env: repl_env) -try rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))", env: repl_env) if CommandLine.argc > 1 { let fileName = CommandLine.arguments[1], diff --git a/tcl/step8_macros.tcl b/tcl/step8_macros.tcl index 7329d8b4..9ee25aa3 100644 --- a/tcl/step8_macros.tcl +++ b/tcl/step8_macros.tcl @@ -227,7 +227,6 @@ $repl_env set "*ARGV*" [list_new $argv_list] RE "(def! not (fn* (a) (if a false true)))" $repl_env RE "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" $repl_env RE "(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 -RE "(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))))))))" $repl_env fconfigure stdout -translation binary diff --git a/tcl/step9_try.tcl b/tcl/step9_try.tcl index e190f802..dd4ef788 100644 --- a/tcl/step9_try.tcl +++ b/tcl/step9_try.tcl @@ -245,7 +245,6 @@ $repl_env set "*ARGV*" [list_new $argv_list] RE "(def! not (fn* (a) (if a false true)))" $repl_env RE "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" $repl_env RE "(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 -RE "(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))))))))" $repl_env fconfigure stdout -translation binary diff --git a/tcl/stepA_mal.tcl b/tcl/stepA_mal.tcl index 62d4f8e1..71c09c7a 100644 --- a/tcl/stepA_mal.tcl +++ b/tcl/stepA_mal.tcl @@ -249,9 +249,6 @@ RE "(def! *host-language* \"tcl\")" $repl_env RE "(def! not (fn* (a) (if a false true)))" $repl_env RE "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" $repl_env RE "(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 -RE "(def! inc (fn* \[x\] (+ x 1)))" $repl_env -RE "(def! gensym (let* \[counter (atom 0)\] (fn* \[\] (symbol (str \"G__\" (swap! counter inc))))))" $repl_env -RE "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))" $repl_env fconfigure stdout -translation binary diff --git a/tests/lib/alias-hacks.mal b/tests/lib/alias-hacks.mal index c078ad89..906a208f 100644 --- a/tests/lib/alias-hacks.mal +++ b/tests/lib/alias-hacks.mal @@ -1,5 +1,6 @@ ;; Testing alias-hacks.mal -(load-file "../../lib/alias-hacks.mal") +(load-file "../lib/load-file-once.mal") +(load-file-once "../lib/alias-hacks.mal") ;=>nil ;; Testing let @@ -52,4 +53,3 @@ x ;=>3 ((partial str 1 2) 3 4) ;=>"1234" - diff --git a/tests/lib/equality.mal b/tests/lib/equality.mal index 78a0b5d4..52c42b15 100644 --- a/tests/lib/equality.mal +++ b/tests/lib/equality.mal @@ -4,42 +4,42 @@ (load-file "../lib/equality.mal") ;=>nil -;; Testing and2 -(and2) +;; Testing bool-and +(bool-and) ;=>true -(and2 true) +(bool-and true) ;=>true -(and2 false) +(bool-and false) ;=>false -(and2 nil) +(bool-and nil) ;=>false -(and2 1) +(bool-and 1) ;=>true -(and2 1 2) +(bool-and 1 2) ;=>true -(and2 nil (nth () 1)) +(bool-and nil (nth () 1)) ;=>false -;; Testing or2 -(or2) +;; Testing bool-or +(bool-or) ;=>false -(or2 true) +(bool-or true) ;=>true -(or2 false) +(bool-or false) ;=>false -(or2 nil) +(bool-or nil) ;=>false -(or2 1) +(bool-or 1) ;=>true -(or2 1 (nth () 1)) +(bool-or 1 (nth () 1)) ;=>true -(or2 1 2) +(bool-or 1 2) ;=>true -(or2 false nil) +(bool-or false nil) ;=>false ;; Breaking equality. -(def! = (fn* [a b] (and2 (orig= a b) (cond (list? a) (list? b) (vector? a) (vector? b) true true)))) +(def! = (fn* [a b] (bool-and (orig= a b) (cond (list? a) (list? b) (vector? a) (vector? b) true true)))) (= [] ()) ;=>false diff --git a/tests/lib/load-file-once-inc.mal b/tests/lib/load-file-once-inc.mal new file mode 100644 index 00000000..2f912a89 --- /dev/null +++ b/tests/lib/load-file-once-inc.mal @@ -0,0 +1 @@ +(swap! counter (fn* [x] (+ 1 x))) diff --git a/tests/lib/load-file-once.mal b/tests/lib/load-file-once.mal new file mode 100644 index 00000000..65e40a99 --- /dev/null +++ b/tests/lib/load-file-once.mal @@ -0,0 +1,38 @@ +(def! counter (atom 0)) +;=>(atom 0) + +;; The counter is increased by each `load-file`. +(load-file "../tests/lib/load-file-once-inc.mal") +;=>1 +(load-file "../tests/lib/load-file-once-inc.mal") +;=>2 + +;; load-file-once is available +(load-file "../lib/load-file-once.mal") +;=>nil + +;; First import actually calls `load-file`. +(load-file-once "../tests/lib/load-file-once-inc.mal") +;=>3 + +;; Later imports do nothing. +(load-file-once "../tests/lib/load-file-once-inc.mal") +;=>nil +@counter +;=>3 + +;; Loading the module twice does not reset its memory. +(load-file "../lib/load-file-once.mal") +;=>nil +(load-file-once "../tests/lib/load-file-once-inc.mal") +;=>nil +@counter +;=>3 + +;; even if done with itself +(load-file-once "../lib/load-file-once.mal") +;=>nil +(load-file-once "../tests/lib/load-file-once-inc.mal") +;=>nil +@counter +;=>3 diff --git a/tests/lib/memoize.mal b/tests/lib/memoize.mal index 50c31803..60fc43d2 100644 --- a/tests/lib/memoize.mal +++ b/tests/lib/memoize.mal @@ -1,6 +1,6 @@ -(load-file "../tests/computations.mal") -;=>nil -(load-file "../lib/memoize.mal") +(load-file "../lib/load-file-once.mal") +(load-file-once "../tests/computations.mal") +(load-file-once "../lib/memoize.mal") ;=>nil (def! N 32) diff --git a/tests/lib/pprint.mal b/tests/lib/pprint.mal index 1a268f5c..457dd4d6 100644 --- a/tests/lib/pprint.mal +++ b/tests/lib/pprint.mal @@ -1,4 +1,5 @@ -(load-file "../lib/pprint.mal") +(load-file "../lib/load-file-once.mal") +(load-file-once "../lib/pprint.mal") ;=>nil (pprint '(7 8 9 "ten" [11 12 [13 14]] 15 16)) diff --git a/tests/lib/protocols.mal b/tests/lib/protocols.mal index 731b6833..819543d8 100644 --- a/tests/lib/protocols.mal +++ b/tests/lib/protocols.mal @@ -1,4 +1,5 @@ -(load-file "../lib/protocols.mal") +(load-file "../lib/load-file-once.mal") +(load-file-once "../lib/protocols.mal") ;=>nil ;; Testing find-type for normal objects. diff --git a/tests/lib/reducers.mal b/tests/lib/reducers.mal index 6bd4ee4c..9aa242da 100644 --- a/tests/lib/reducers.mal +++ b/tests/lib/reducers.mal @@ -1,4 +1,5 @@ -(load-file "../lib/reducers.mal") +(load-file "../lib/load-file-once.mal") +(load-file-once "../lib/reducers.mal") ;=>nil ;; Testing reduce diff --git a/tests/lib/test_cascade.mal b/tests/lib/test_cascade.mal index b6c2f3c1..95e4632a 100644 --- a/tests/lib/test_cascade.mal +++ b/tests/lib/test_cascade.mal @@ -1,6 +1,25 @@ -(load-file "../lib/test_cascade.mal") +(load-file "../lib/load-file-once.mal") +(load-file-once "../lib/test_cascade.mal") ;=>nil +;; Testing or +(or) +;=>nil +(or 1) +;=>1 +(or 1 2 3 4) +;=>1 +(or false 2) +;=>2 +(or false nil 3) +;=>3 +(or false nil false false nil 4) +;=>4 +(or false nil 3 false nil 4) +;=>3 +(or (or false 4)) +;=>4 + ;; Testing every? (every? first []) ;=>true diff --git a/tests/lib/threading.mal b/tests/lib/threading.mal index 2040ee22..9d3fe96e 100644 --- a/tests/lib/threading.mal +++ b/tests/lib/threading.mal @@ -1,4 +1,5 @@ -(load-file "../lib/threading.mal") +(load-file "../lib/load-file-once.mal") +(load-file-once "../lib/threading.mal") ;=>nil ;; Testing -> macro diff --git a/tests/lib/trivial.mal b/tests/lib/trivial.mal index 6f2f813f..1d9c7c0b 100644 --- a/tests/lib/trivial.mal +++ b/tests/lib/trivial.mal @@ -1,6 +1,9 @@ -(load-file "../lib/trivial.mal") +(load-file "../lib/load-file-once.mal") +(load-file-once "../lib/trivial.mal") ;=>nil +(inc 12) +;=>13 (dec 12) ;=>11 (zero? 12) @@ -9,3 +12,5 @@ ;=>true (identity 12) ;=>12 +(= (gensym) (gensym)) +;=>false diff --git a/tests/perf1.mal b/tests/perf1.mal index e73ed9ad..9d1db7cb 100644 --- a/tests/perf1.mal +++ b/tests/perf1.mal @@ -1,6 +1,7 @@ -(load-file "../lib/threading.mal") ; -> -(load-file "../lib/perf.mal") ; time -(load-file "../lib/test_cascade.mal") ; or +(load-file "../lib/load-file-once.mal") +(load-file-once "../lib/threading.mal") ; -> +(load-file-once "../lib/perf.mal") ; time +(load-file-once "../lib/test_cascade.mal") ; or ;;(prn "Start: basic macros performance test") diff --git a/tests/perf2.mal b/tests/perf2.mal index e2ca4d73..4f0bc6cc 100644 --- a/tests/perf2.mal +++ b/tests/perf2.mal @@ -1,5 +1,6 @@ -(load-file "../tests/computations.mal") ; fib sumdown -(load-file "../lib/perf.mal") ; time +(load-file "../lib/load-file-once.mal") +(load-file-once "../tests/computations.mal") ; fib sumdown +(load-file-once "../lib/perf.mal") ; time ;;(prn "Start: basic math/recursion test") diff --git a/tests/perf3.mal b/tests/perf3.mal index 2efbaf9a..da81f8de 100644 --- a/tests/perf3.mal +++ b/tests/perf3.mal @@ -1,6 +1,7 @@ -(load-file "../lib/threading.mal") ; -> -(load-file "../lib/perf.mal") ; run-fn-for -(load-file "../lib/test_cascade.mal") ; or +(load-file "../lib/load-file-once.mal") +(load-file-once "../lib/threading.mal") ; -> +(load-file-once "../lib/perf.mal") ; run-fn-for +(load-file-once "../lib/test_cascade.mal") ; or ;;(prn "Start: basic macros/atom test") diff --git a/tests/step0_repl.mal b/tests/step0_repl.mal index 2b83a01f..4706a1ae 100644 --- a/tests/step0_repl.mal +++ b/tests/step0_repl.mal @@ -15,3 +15,52 @@ hello mal world hello world abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789 (;:() []{}"'* ;:() []{}"'* ;:() []{}"'*) ;=>hello world abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789 (;:() []{}"'* ;:() []{}"'* ;:() []{}"'*) +;; Non alphanumeric characters +! +;=>! +& +;=>& ++ +;=>+ +, +;=>, +- +;=>- +/ +;=>/ +< +;=>< += +;=>= +> +;=>> +? +;=>? +@ +;=>@ +;;; Behaviour of backslash is not specified enough to test anything in step0. +^ +;=>^ +_ +;=>_ +` +;=>` +~ +;=>~ + +;>>> soft=True +;>>> optional=True +;; ------- Optional Functionality -------------- +;; ------- (Not needed for self-hosting) ------- + +;; Non alphanumeric characters +# +;=># +$ +;=>$ +% +;=>% +. +;=>. +| +;=>| diff --git a/tests/step1_read_print.mal b/tests/step1_read_print.mal index a5ffb1c8..75d2edb2 100644 --- a/tests/step1_read_print.mal +++ b/tests/step1_read_print.mal @@ -80,14 +80,60 @@ false ;=>"abc (with parens)" "abc\"def" ;=>"abc\"def" -;;;"abc\ndef" -;;;;=>"abc\ndef" "" ;=>"" "\\" ;=>"\\" "\\\\\\\\\\\\\\\\\\" ;=>"\\\\\\\\\\\\\\\\\\" +"&" +;=>"&" +"'" +;=>"'" +"(" +;=>"(" +")" +;=>")" +"*" +;=>"*" +"+" +;=>"+" +"," +;=>"," +"-" +;=>"-" +"/" +;=>"/" +":" +;=>":" +";" +;=>";" +"<" +;=>"<" +"=" +;=>"=" +">" +;=>">" +"?" +;=>"?" +"@" +;=>"@" +"[" +;=>"[" +"]" +;=>"]" +"^" +;=>"^" +"_" +;=>"_" +"`" +;=>"`" +"{" +;=>"{" +"}" +;=>"}" +"~" +;=>"~" ;; Testing reader errors (1 2 @@ -128,10 +174,6 @@ false ;=>(splice-unquote (1 2 3)) -;>>> optional=True -;; -;; -------- Optional Functionality -------- - ;; Testing keywords :kw ;=>:kw @@ -173,6 +215,8 @@ false ;/{"a([1-3])" \1 "a(?!\1)([1-3])" \2 "a(?!\1)(?!\2)([1-3])" \3} { :a {:b { :cde 3 } }} ;=>{:a {:b {:cde 3}}} +{"1" 1} +;=>{"1" 1} ({}) ;=>({}) @@ -183,11 +227,62 @@ false 1; comment after expression ;=>1 +;; Testing read of @/deref +@a +;=>(deref a) + +;>>> soft=True +;>>> optional=True +;; +;; -------- Optional Functionality -------- + ;; Testing read of ^/metadata ^{"a" 1} [1 2 3] ;=>(with-meta [1 2 3] {"a" 1}) -;; Testing read of @/deref -@a -;=>(deref a) +;; Non alphanumerice characters in strings +;;; \t is not specified enough to be tested +"\n" +;=>"\n" +"#" +;=>"#" +"$" +;=>"$" +"%" +;=>"%" +"." +;=>"." +"\\" +;=>"\\" +"|" +;=>"|" + +;; Non alphanumeric characters in comments +1;! +;=>1 +1;" +;=>1 +1;# +;=>1 +1;$ +;=>1 +1;% +;=>1 +1;' +;=>1 +1;\ +;=>1 +1;\\ +;=>1 +1;\\\ +;=>1 +1;` +;=>1 +;;; Hopefully less problematic characters +1; &()*+,-./:;<=>?@[]^_{|}~ + +;; FIXME: These tests have no reasons to be optional, but... +;; fantom fails this one +"!" +;=>"!" diff --git a/tests/step2_eval.mal b/tests/step2_eval.mal index 16a3589a..45145924 100644 --- a/tests/step2_eval.mal +++ b/tests/step2_eval.mal @@ -29,9 +29,8 @@ ;=>() ;>>> deferrable=True -;>>> optional=True ;; -;; -------- Deferrable/Optional Functionality -------- +;; -------- Deferrable Functionality -------- ;; Testing evaluation within collection literals [1 2 (+ 1 2)] diff --git a/tests/step3_env.mal b/tests/step3_env.mal index cc8270d8..6711b11a 100644 --- a/tests/step3_env.mal +++ b/tests/step3_env.mal @@ -64,9 +64,8 @@ y ;=>4 ;>>> deferrable=True -;>>> optional=True ;; -;; -------- Deferrable/Optional Functionality -------- +;; -------- Deferrable Functionality -------- ;; Testing let* with vector bindings (let* [z 9] z) diff --git a/tests/step4_if_fn_do.mal b/tests/step4_if_fn_do.mal index c1721f4f..13eb8b49 100644 --- a/tests/step4_if_fn_do.mal +++ b/tests/step4_if_fn_do.mal @@ -127,6 +127,8 @@ ;=>false (= (list) 0) ;=>false +(= (list nil) (list)) +;=>false ;; Testing builtin and user defined functions @@ -195,9 +197,6 @@ a ;=>2 (fib 4) ;=>5 -;;; Too slow for bash, erlang, make and miniMAL -;;;(fib 10) -;;;;=>89 ;; Testing recursive function in environment. @@ -421,9 +420,6 @@ nil ;/\(1 2 abc "\) def ;=>nil -;>>> optional=True -;; -;; -------- Optional Functionality -------- ;; Testing keywords (= :abc :abc) @@ -432,6 +428,8 @@ nil ;=>false (= :abc ":abc") ;=>false +(= (list :abc) (list :abc)) +;=>true ;; Testing vector truthiness (if [] 7 8) @@ -466,6 +464,8 @@ nil ;=>true (= [7 8] [7 8]) ;=>true +(= [:abc] [:abc]) +;=>true (= (list 1 2) [1 2]) ;=>true (= (list 1) []) diff --git a/tests/step6_file.mal b/tests/step6_file.mal index cf1fd2f2..dd3bd661 100644 --- a/tests/step6_file.mal +++ b/tests/step6_file.mal @@ -25,6 +25,10 @@ (slurp "../tests/test.txt") ;=>"A line of text\n" +;;; Load the same file twice. +(slurp "../tests/test.txt") +;=>"A line of text\n" + ;; Testing load-file (load-file "../tests/inc.mal") @@ -97,6 +101,28 @@ (fib 2) ;=>1 +;; Testing `@` reader macro (short for `deref`) +(def! atm (atom 9)) +@atm +;=>9 + +;;; TODO: really a step5 test +;; Testing that vector params not broken by TCO +(def! g (fn* [] 78)) +(g) +;=>78 +(def! g (fn* [a] (+ a 78))) +(g 3) +;=>81 + +;; +;; Testing that *ARGV* exists and is an empty list +(list? *ARGV*) +;=>true +*ARGV* +;=>() + +;>>> soft=True ;>>> optional=True ;; ;; -------- Optional Functionality -------- @@ -115,29 +141,33 @@ mymap ;=>{"a" 1} -;; Testing `@` reader macro (short for `deref`) -(def! atm (atom 9)) -@atm -;=>9 - -;;; TODO: really a step5 test -;; Testing that vector params not broken by TCO -(def! g (fn* [] 78)) -(g) -;=>78 -(def! g (fn* [a] (+ a 78))) -(g 3) -;=>81 - ;; Checking that eval does not use local environments. (def! a 1) ;=>1 (let* (a 2) (eval (read-string "a"))) ;=>1 -;; -;; Testing that *ARGV* exists and is an empty list -(list? *ARGV*) -;=>true -*ARGV* -;=>() +;; Non alphanumeric characters in comments in read-string +(read-string "1;!") +;=>1 +(read-string "1;\"") +;=>1 +(read-string "1;#") +;=>1 +(read-string "1;$") +;=>1 +(read-string "1;%") +;=>1 +(read-string "1;'") +;=>1 +(read-string "1;\\") +;=>1 +(read-string "1;\\\\") +;=>1 +(read-string "1;\\\\\\") +;=>1 +(read-string "1;`") +;=>1 +;;; Hopefully less problematic characters can be checked together +(read-string "1; &()*+,-./:;<=>?@[]^_{|}~") +;=>1 diff --git a/tests/step7_quote.mal b/tests/step7_quote.mal index b36835e5..c1c07f5c 100644 --- a/tests/step7_quote.mal +++ b/tests/step7_quote.mal @@ -119,6 +119,22 @@ b '(1 2 (3 4)) ;=>(1 2 (3 4)) +;; Testing cons and concat with vectors + +(cons [1] [2 3]) +;=>([1] 2 3) +(cons 1 [2 3]) +;=>(1 2 3) +(concat [1 2] (list 3 4) [5 6]) +;=>(1 2 3 4 5 6) +(concat [1 2]) +;=>(1 2) + + +;>>> optional=True +;; +;; -------- Optional Functionality -------- + ;; Testing ` (quasiquote) reader macro `7 ;=>7 @@ -151,22 +167,6 @@ b `(1 ~@c 3) ;=>(1 1 "b" "d" 3) - -;>>> optional=True -;; -;; -------- Optional Functionality -------- - -;; Testing cons and concat with vectors - -(cons [1] [2 3]) -;=>([1] 2 3) -(cons 1 [2 3]) -;=>(1 2 3) -(concat [1 2] (list 3 4) [5 6]) -;=>(1 2 3 4 5 6) -(concat [1 2]) -;=>(1 2) - ;; Testing unquote with vectors (def! a 8) ;=>8 diff --git a/tests/step8_macros.mal b/tests/step8_macros.mal index 59868928..2dcc2c34 100644 --- a/tests/step8_macros.mal +++ b/tests/step8_macros.mal @@ -69,24 +69,6 @@ x ;=>(8 9) -;; Testing or macro -(or) -;=>nil -(or 1) -;=>1 -(or 1 2 3 4) -;=>1 -(or false 2) -;=>2 -(or false nil 3) -;=>3 -(or false nil false false nil 4) -;=>4 -(or false nil 3 false nil 4) -;=>3 -(or (or false 4)) -;=>4 - ;; Testing cond macro (cond) @@ -106,14 +88,10 @@ x ;; Testing EVAL in let* -(let* (x (or nil "yes")) x) +(let* (x (cond false "no" true "yes")) x) ;=>"yes" -;>>> optional=True -;; -;; -------- Optional Functionality -------- - ;; Testing nth, first, rest with vectors (nth [1] 0) @@ -146,10 +124,14 @@ x ;; Testing EVAL in vector let* -(let* [x (or nil "yes")] x) +(let* [x (cond false "no" true "yes")] x) ;=>"yes" ;>>> soft=True +;>>> optional=True +;; +;; ------- Optional Functionality -------------- +;; ------- (Not needed for self-hosting) ------- ;; Test that macros use closures (def! x 2) diff --git a/tests/step9_try.mal b/tests/step9_try.mal index 077c2c44..ce6c9e17 100644 --- a/tests/step9_try.mal +++ b/tests/step9_try.mal @@ -116,9 +116,6 @@ (symbol "abc") ;=>abc -;;;TODO: all implementations should suppport this too -;;;(keyword :abc) -;;;;=>:abc (keyword "abc") ;=>:abc @@ -177,6 +174,7 @@ (map? :abc) ;=>false + ;; ;; Testing hash-maps (hash-map "a" 1) @@ -233,6 +231,9 @@ (keys hm2) ;=>("a") +(keys {"1" 1}) +;=>("1") + ;;; TODO: fix. Clojure returns nil but this breaks mal impl (vals hm1) ;=>() @@ -254,9 +255,6 @@ ;=>{:bcd 234} (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 @@ -291,11 +289,11 @@ ;=>"true \".\" false \".\" nil \".\" :keyw \".\" symb" (def! s (str {:abc "val1" :def "val2"})) -(or (= s "{:abc val1 :def val2}") (= s "{:def val2 :abc val1}")) +(cond (= s "{:abc val1 :def val2}") true (= s "{:def val2 :abc val1}") true) ;=>true (def! p (pr-str {:abc "val1" :def "val2"})) -(or (= p "{:abc \"val1\" :def \"val2\"}") (= p "{:def \"val2\" :abc \"val1\"}")) +(cond (= p "{:abc \"val1\" :def \"val2\"}") true (= p "{:def \"val2\" :abc \"val1\"}") true) ;=>true ;; @@ -374,3 +372,10 @@ (= [] {}) ;=>false +(map? cond) +;=>false + +(keyword :abc) +;=>:abc +(keyword? (first (keys {":abc" 123 ":def" 456}))) +;=>false diff --git a/tests/stepA_mal.mal b/tests/stepA_mal.mal index 58ba909e..ee20474e 100644 --- a/tests/stepA_mal.mal +++ b/tests/stepA_mal.mal @@ -24,6 +24,33 @@ ;; ------- (Needed for self-hosting) ------- ;; +;; +;; Testing hash-map evaluation and atoms (i.e. an env) +(def! e (atom {"+" +})) +(swap! e assoc "-" -) +( (get @e "+") 7 8) +;=>15 +( (get @e "-") 11 8) +;=>3 +(swap! e assoc "foo" (list)) +(get @e "foo") +;=>() +(swap! e assoc "bar" '(1 2 3)) +(get @e "bar") +;=>(1 2 3) + +;; Testing for presence of optional functions +(do (list time-ms string? number? seq conj meta with-meta fn?) nil) +;=>nil + +;; ------------------------------------------------------------------ + +;>>> soft=True +;>>> optional=True +;; +;; ------- Optional Functionality -------------- +;; ------- (Not needed for self-hosting) ------- + ;; Testing metadata on functions ;; @@ -64,7 +91,6 @@ (meta +) ;=>nil - ;; ;; Make sure closures and metadata co-exist (def! gen-plusX (fn* (x) (with-meta (fn* (b) (+ x b)) {"meta" 1}))) @@ -81,33 +107,6 @@ (meta plus8) ;=>{"meta" 1} -;; -;; Testing hash-map evaluation and atoms (i.e. an env) -(def! e (atom {"+" +})) -(swap! e assoc "-" -) -( (get @e "+") 7 8) -;=>15 -( (get @e "-") 11 8) -;=>3 -(swap! e assoc "foo" (list)) -(get @e "foo") -;=>() -(swap! e assoc "bar" '(1 2 3)) -(get @e "bar") -;=>(1 2 3) - -;; Testing for presence of optional functions -(do (list time-ms string? number? seq conj) nil) -;=>nil - -;; ------------------------------------------------------------------ - -;>>> soft=True -;>>> optional=True -;; -;; ------- Optional Functionality -------------- -;; ------- (Not needed for self-hosting) ------- - ;; ;; Testing string? function (string? "") @@ -150,6 +149,8 @@ ;=>false (fn? :+) ;=>false +(fn? ^{"ismacro" true} (fn* () 0)) +;=>true ;; Testing macro? function (macro? cond) @@ -162,6 +163,8 @@ ;=>false (macro? :+) ;=>false +(macro? {}) +;=>false ;; @@ -250,10 +253,6 @@ (with-meta {} {"a" 1}) ;=>{} -;;; Not actually supported by Clojure -;;;(meta (with-meta (atom 7) {"a" 1})) -;;;;=>{"a" 1} - (def! l-wm (with-meta [4 5 6] {"b" 2})) ;=>[4 5 6] (meta l-wm) @@ -274,18 +273,6 @@ (meta +) ;=>nil -;; -;; Testing inc -(inc 12) -;=>13 - -;; -;; Testing gensym and clean or macro -(= (gensym) (gensym)) -;=>false -(let* [or_FIXME 23] (or false (+ or_FIXME 100))) -;=>123 - ;; Loading sumdown from computations.mal (load-file "../tests/computations.mal") @@ -307,3 +294,6 @@ ;=>true (m (+ 1 1)) ;=>false + +(meta (with-meta (atom 7) {"a" 1})) +;=>{"a" 1} diff --git a/ts/step8_macros.ts b/ts/step8_macros.ts index 0c915662..f81fa5b8 100644 --- a/ts/step8_macros.ts +++ b/ts/step8_macros.ts @@ -276,7 +276,6 @@ replEnv.set(MalSymbol.get("*ARGV*"), new MalList([])); 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" && 2 < process.argv.length) { replEnv.set(MalSymbol.get("*ARGV*"), new MalList(process.argv.slice(3).map(s => new MalString(s)))); diff --git a/ts/step9_try.ts b/ts/step9_try.ts index eb22b39b..2074b787 100644 --- a/ts/step9_try.ts +++ b/ts/step9_try.ts @@ -301,7 +301,6 @@ replEnv.set(MalSymbol.get("*ARGV*"), new MalList([])); 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" && 2 < process.argv.length) { replEnv.set(MalSymbol.get("*ARGV*"), new MalList(process.argv.slice(3).map(s => new MalString(s)))); diff --git a/ts/stepA_mal.ts b/ts/stepA_mal.ts index 61e6a15c..c5bbf31c 100644 --- a/ts/stepA_mal.ts +++ b/ts/stepA_mal.ts @@ -302,9 +302,6 @@ rep(`(def! *host-language* "TypeScript")`); 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("(def! inc (fn* [x] (+ x 1)))"); -rep("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))"); -rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))"); if (typeof process !== "undefined" && 2 < process.argv.length) { replEnv.set(MalSymbol.get("*ARGV*"), new MalList(process.argv.slice(3).map(s => new MalString(s)))); diff --git a/vala/step2_eval.vala b/vala/step2_eval.vala index fec15150..f62e8806 100644 --- a/vala/step2_eval.vala +++ b/vala/step2_eval.vala @@ -101,11 +101,12 @@ class Mal.Main : GLib.Object { return result; } if (ast is Mal.Vector) { - var results = new GLib.List(); - for (var iter = (ast as Mal.Vector).iter(); - iter.nonempty(); iter.step()) - results.append(EVAL(iter.deref(), env)); - return new Mal.Vector.from_list(results); + var vec = ast as Mal.Vector; + var result = new Mal.Vector.with_size(vec.length); + var root = new GC.Root(result); (void)root; + for (var i = 0; i < vec.length; i++) + result[i] = EVAL(vec[i], env); + return result; } if (ast is Mal.Hashmap) { var result = new Mal.Hashmap(); diff --git a/vala/step3_env.vala b/vala/step3_env.vala index d15bf4f5..429b5b17 100644 --- a/vala/step3_env.vala +++ b/vala/step3_env.vala @@ -83,11 +83,12 @@ class Mal.Main : GLib.Object { return result; } if (ast is Mal.Vector) { - var results = new GLib.List(); - for (var iter = (ast as Mal.Vector).iter(); - iter.nonempty(); iter.step()) - results.append(EVAL(iter.deref(), env)); - return new Mal.Vector.from_list(results); + var vec = ast as Mal.Vector; + var result = new Mal.Vector.with_size(vec.length); + var root = new GC.Root(result); (void)root; + for (var i = 0; i < vec.length; i++) + result[i] = EVAL(vec[i], env); + return result; } if (ast is Mal.Hashmap) { var result = new Mal.Hashmap(); @@ -101,16 +102,16 @@ class Mal.Main : GLib.Object { } private static Mal.Val define_eval(Mal.Val key, Mal.Val value, - Mal.Env eval_env, Mal.Env def_env) + Mal.Env env) throws Mal.Error { var rootk = new GC.Root(key); (void)rootk; - var roote = new GC.Root(def_env); (void)roote; + var roote = new GC.Root(env); (void)roote; var symkey = key as Mal.Sym; if (symkey == null) throw new Mal.Error.BAD_PARAMS( "let*: expected a symbol to define"); - var val = EVAL(value, eval_env); - def_env.set(symkey, val); + var val = EVAL(value, env); + env.set(symkey, val); return val; } @@ -134,7 +135,7 @@ class Mal.Main : GLib.Object { throw new Mal.Error.BAD_PARAMS( "def!: expected two values"); return define_eval(list.next.data, list.next.next.data, - env, env); + env); case "let*": if (list.length() != 3) throw new Mal.Error.BAD_PARAMS( @@ -150,8 +151,7 @@ class Mal.Main : GLib.Object { throw new Mal.Error.BAD_PARAMS( "let*: expected an even-length list" + " of definitions"); - define_eval(iter.data, iter.next.data, - newenv, newenv); + define_eval(iter.data, iter.next.data, newenv); } } else if (defns is Mal.Vector) { var vec = defns as Mal.Vector; @@ -160,7 +160,7 @@ class Mal.Main : GLib.Object { "let*: expected an even-length vector" + " of definitions"); for (var i = 0; i < vec.length; i += 2) - define_eval(vec[i], vec[i+1], newenv, newenv); + define_eval(vec[i], vec[i+1], newenv); } else { throw new Mal.Error.BAD_PARAMS( "let*: expected a list or vector of definitions"); diff --git a/vala/step4_if_fn_do.vala b/vala/step4_if_fn_do.vala index 21169b03..93d09c3f 100644 --- a/vala/step4_if_fn_do.vala +++ b/vala/step4_if_fn_do.vala @@ -38,11 +38,12 @@ class Mal.Main: GLib.Object { return result; } if (ast is Mal.Vector) { - var results = new GLib.List(); - for (var iter = (ast as Mal.Vector).iter(); - iter.nonempty(); iter.step()) - results.append(EVAL(iter.deref(), env)); - return new Mal.Vector.from_list(results); + var vec = ast as Mal.Vector; + var result = new Mal.Vector.with_size(vec.length); + var root = new GC.Root(result); (void)root; + for (var i = 0; i < vec.length; i++) + result[i] = EVAL(vec[i], env); + return result; } if (ast is Mal.Hashmap) { var result = new Mal.Hashmap(); @@ -56,16 +57,16 @@ class Mal.Main: GLib.Object { } private static Mal.Val define_eval(Mal.Val key, Mal.Val value, - Mal.Env eval_env, Mal.Env def_env) + Mal.Env env) throws Mal.Error { var rootk = new GC.Root(key); (void)rootk; - var roote = new GC.Root(def_env); (void)roote; + var roote = new GC.Root(env); (void)roote; var symkey = key as Mal.Sym; if (symkey == null) throw new Mal.Error.BAD_PARAMS( "let*: expected a symbol to define"); - var val = EVAL(value, eval_env); - def_env.set(symkey, val); + var val = EVAL(value, env); + env.set(symkey, val); return val; } @@ -89,7 +90,7 @@ class Mal.Main: GLib.Object { throw new Mal.Error.BAD_PARAMS( "def!: expected two values"); return define_eval(list.next.data, list.next.next.data, - env, env); + env); case "let*": if (list.length() != 3) throw new Mal.Error.BAD_PARAMS( @@ -105,8 +106,7 @@ class Mal.Main: GLib.Object { throw new Mal.Error.BAD_PARAMS( "let*: expected an even-length list" + " of definitions"); - define_eval(iter.data, iter.next.data, - newenv, newenv); + define_eval(iter.data, iter.next.data, newenv); } } else if (defns is Mal.Vector) { var vec = defns as Mal.Vector; @@ -115,7 +115,7 @@ class Mal.Main: GLib.Object { "let*: expected an even-length vector" + " of definitions"); for (var i = 0; i < vec.length; i += 2) - define_eval(vec[i], vec[i+1], newenv, newenv); + define_eval(vec[i], vec[i+1], newenv); } else { throw new Mal.Error.BAD_PARAMS( "let*: expected a list or vector of definitions"); diff --git a/vala/step5_tco.vala b/vala/step5_tco.vala index 06c144ca..c29a31fd 100644 --- a/vala/step5_tco.vala +++ b/vala/step5_tco.vala @@ -38,11 +38,12 @@ class Mal.Main : GLib.Object { return result; } if (ast is Mal.Vector) { - var results = new GLib.List(); - for (var iter = (ast as Mal.Vector).iter(); - iter.nonempty(); iter.step()) - results.append(EVAL(iter.deref(), env)); - return new Mal.Vector.from_list(results); + var vec = ast as Mal.Vector; + var result = new Mal.Vector.with_size(vec.length); + var root = new GC.Root(result); (void)root; + for (var i = 0; i < vec.length; i++) + result[i] = EVAL(vec[i], env); + return result; } if (ast is Mal.Hashmap) { var result = new Mal.Hashmap(); @@ -56,16 +57,16 @@ class Mal.Main : GLib.Object { } private static Mal.Val define_eval(Mal.Val key, Mal.Val value, - Mal.Env eval_env, Mal.Env def_env) + Mal.Env env) throws Mal.Error { var rootk = new GC.Root(key); (void)rootk; - var roote = new GC.Root(def_env); (void)roote; + var roote = new GC.Root(env); (void)roote; var symkey = key as Mal.Sym; if (symkey == null) throw new Mal.Error.BAD_PARAMS( "let*: expected a symbol to define"); - var val = EVAL(value, eval_env); - def_env.set(symkey, val); + var val = EVAL(value, env); + env.set(symkey, val); return val; } @@ -98,7 +99,7 @@ class Mal.Main : GLib.Object { throw new Mal.Error.BAD_PARAMS( "def!: expected two values"); return define_eval(list.next.data, list.next.next.data, - env, env); + env); case "let*": if (list.length() != 3) throw new Mal.Error.BAD_PARAMS( @@ -114,8 +115,7 @@ class Mal.Main : GLib.Object { throw new Mal.Error.BAD_PARAMS( "let*: expected an even-length list" + " of definitions"); - define_eval(iter.data, iter.next.data, - env, env); + define_eval(iter.data, iter.next.data, env); } } else if (defns is Mal.Vector) { var vec = defns as Mal.Vector; @@ -124,7 +124,7 @@ class Mal.Main : GLib.Object { "let*: expected an even-length vector" + " of definitions"); for (var i = 0; i < vec.length; i += 2) - define_eval(vec[i], vec[i+1], env, env); + define_eval(vec[i], vec[i+1], env); } else { throw new Mal.Error.BAD_PARAMS( "let*: expected a list or vector of definitions"); diff --git a/vala/step6_file.vala b/vala/step6_file.vala index 803cbeab..3bdd83c9 100644 --- a/vala/step6_file.vala +++ b/vala/step6_file.vala @@ -52,11 +52,12 @@ class Mal.Main : GLib.Object { return result; } if (ast is Mal.Vector) { - var results = new GLib.List(); - for (var iter = (ast as Mal.Vector).iter(); - iter.nonempty(); iter.step()) - results.append(EVAL(iter.deref(), env)); - return new Mal.Vector.from_list(results); + var vec = ast as Mal.Vector; + var result = new Mal.Vector.with_size(vec.length); + var root = new GC.Root(result); (void)root; + for (var i = 0; i < vec.length; i++) + result[i] = EVAL(vec[i], env); + return result; } if (ast is Mal.Hashmap) { var result = new Mal.Hashmap(); @@ -70,16 +71,16 @@ class Mal.Main : GLib.Object { } private static Mal.Val define_eval(Mal.Val key, Mal.Val value, - Mal.Env eval_env, Mal.Env def_env) + Mal.Env env) throws Mal.Error { var rootk = new GC.Root(key); (void)rootk; - var roote = new GC.Root(def_env); (void)roote; + var roote = new GC.Root(env); (void)roote; var symkey = key as Mal.Sym; if (symkey == null) throw new Mal.Error.BAD_PARAMS( "let*: expected a symbol to define"); - var val = EVAL(value, eval_env); - def_env.set(symkey, val); + var val = EVAL(value, env); + env.set(symkey, val); return val; } @@ -112,7 +113,7 @@ class Mal.Main : GLib.Object { throw new Mal.Error.BAD_PARAMS( "def!: expected two values"); return define_eval(list.next.data, list.next.next.data, - env, env); + env); case "let*": if (list.length() != 3) throw new Mal.Error.BAD_PARAMS( @@ -128,8 +129,7 @@ class Mal.Main : GLib.Object { throw new Mal.Error.BAD_PARAMS( "let*: expected an even-length list" + " of definitions"); - define_eval(iter.data, iter.next.data, - env, env); + define_eval(iter.data, iter.next.data, env); } } else if (defns is Mal.Vector) { var vec = defns as Mal.Vector; @@ -138,7 +138,7 @@ class Mal.Main : GLib.Object { "let*: expected an even-length vector" + " of definitions"); for (var i = 0; i < vec.length; i += 2) - define_eval(vec[i], vec[i+1], env, env); + define_eval(vec[i], vec[i+1], env); } else { throw new Mal.Error.BAD_PARAMS( "let*: expected a list or vector of definitions"); diff --git a/vala/step7_quote.vala b/vala/step7_quote.vala index 1348385e..4fc745f9 100644 --- a/vala/step7_quote.vala +++ b/vala/step7_quote.vala @@ -52,11 +52,12 @@ class Mal.Main : GLib.Object { return result; } if (ast is Mal.Vector) { - var results = new GLib.List(); - for (var iter = (ast as Mal.Vector).iter(); - iter.nonempty(); iter.step()) - results.append(EVAL(iter.deref(), env)); - return new Mal.Vector.from_list(results); + var vec = ast as Mal.Vector; + var result = new Mal.Vector.with_size(vec.length); + var root = new GC.Root(result); (void)root; + for (var i = 0; i < vec.length; i++) + result[i] = EVAL(vec[i], env); + return result; } if (ast is Mal.Hashmap) { var result = new Mal.Hashmap(); @@ -70,16 +71,16 @@ class Mal.Main : GLib.Object { } private static Mal.Val define_eval(Mal.Val key, Mal.Val value, - Mal.Env eval_env, Mal.Env def_env) + Mal.Env env) throws Mal.Error { var rootk = new GC.Root(key); (void)rootk; - var roote = new GC.Root(def_env); (void)roote; + var roote = new GC.Root(env); (void)roote; var symkey = key as Mal.Sym; if (symkey == null) throw new Mal.Error.BAD_PARAMS( "let*: expected a symbol to define"); - var val = EVAL(value, eval_env); - def_env.set(symkey, val); + var val = EVAL(value, env); + env.set(symkey, val); return val; } @@ -159,7 +160,7 @@ class Mal.Main : GLib.Object { throw new Mal.Error.BAD_PARAMS( "def!: expected two values"); return define_eval(list.next.data, list.next.next.data, - env, env); + env); case "let*": if (list.length() != 3) throw new Mal.Error.BAD_PARAMS( @@ -175,8 +176,7 @@ class Mal.Main : GLib.Object { throw new Mal.Error.BAD_PARAMS( "let*: expected an even-length list" + " of definitions"); - define_eval(iter.data, iter.next.data, - env, env); + define_eval(iter.data, iter.next.data, env); } } else if (defns is Mal.Vector) { var vec = defns as Mal.Vector; @@ -185,11 +185,10 @@ class Mal.Main : GLib.Object { "let*: expected an even-length vector" + " of definitions"); for (var i = 0; i < vec.length; i += 2) - define_eval(vec[i], vec[i+1], env, env); + define_eval(vec[i], vec[i+1], env); } else { throw new Mal.Error.BAD_PARAMS( - "let*: expected a list or vector of "+ - "definitions"); + "let*: expected a list or vector of definitions"); } ast = list.nth(2).data; continue; // tail-call optimisation diff --git a/vala/step8_macros.vala b/vala/step8_macros.vala index 5e909d66..8c0088cb 100644 --- a/vala/step8_macros.vala +++ b/vala/step8_macros.vala @@ -52,11 +52,12 @@ class Mal.Main : GLib.Object { return result; } if (ast is Mal.Vector) { - var results = new GLib.List(); - for (var iter = (ast as Mal.Vector).iter(); - iter.nonempty(); iter.step()) - results.append(EVAL(iter.deref(), env)); - return new Mal.Vector.from_list(results); + var vec = ast as Mal.Vector; + var result = new Mal.Vector.with_size(vec.length); + var root = new GC.Root(result); (void)root; + for (var i = 0; i < vec.length; i++) + result[i] = EVAL(vec[i], env); + return result; } if (ast is Mal.Hashmap) { var result = new Mal.Hashmap(); @@ -70,19 +71,19 @@ class Mal.Main : GLib.Object { } private static Mal.Val define_eval(Mal.Val key, Mal.Val value, - Mal.Env eval_env, Mal.Env def_env, + Mal.Env env, bool is_macro = false) throws Mal.Error { var rootk = new GC.Root(key); (void)rootk; - var roote = new GC.Root(def_env); (void)roote; + var roote = new GC.Root(env); (void)roote; var symkey = key as Mal.Sym; if (symkey == null) throw new Mal.Error.BAD_PARAMS( "let*: expected a symbol to define"); - var val = EVAL(value, eval_env); + var val = EVAL(value, env); if (val is Mal.Function) (val as Mal.Function).is_macro = is_macro; - def_env.set(symkey, val); + env.set(symkey, val); return val; } @@ -192,7 +193,7 @@ class Mal.Main : GLib.Object { throw new Mal.Error.BAD_PARAMS( "def!: expected two values"); return define_eval(list.next.data, list.next.next.data, - env, env, sym.v == "defmacro!"); + env, sym.v == "defmacro!"); case "let*": if (list.length() != 3) throw new Mal.Error.BAD_PARAMS( @@ -208,8 +209,7 @@ class Mal.Main : GLib.Object { throw new Mal.Error.BAD_PARAMS( "let*: expected an even-length list" + " of definitions"); - define_eval(iter.data, iter.next.data, - env, env); + define_eval(iter.data, iter.next.data, env); } } else if (defns is Mal.Vector) { var vec = defns as Mal.Vector; @@ -218,7 +218,7 @@ class Mal.Main : GLib.Object { "let*: expected an even-length vector" + " of definitions"); for (var i = 0; i < vec.length; i += 2) - define_eval(vec[i], vec[i+1], env, env); + define_eval(vec[i], vec[i+1], env); } else { throw new Mal.Error.BAD_PARAMS( "let*: expected a list or vector of definitions"); @@ -337,7 +337,6 @@ class Mal.Main : GLib.Object { setup("(def! not (fn* (a) (if a false true)))", env); setup("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", env); setup("(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)))))))", env); - setup("(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))))))))", env); var ARGV = new GLib.List(); if (args.length > 1) { diff --git a/vala/step9_try.vala b/vala/step9_try.vala index 0c2fcf16..f6d5222d 100644 --- a/vala/step9_try.vala +++ b/vala/step9_try.vala @@ -53,11 +53,12 @@ class Mal.Main : GLib.Object { return result; } if (ast is Mal.Vector) { - var results = new GLib.List(); - for (var iter = (ast as Mal.Vector).iter(); - iter.nonempty(); iter.step()) - results.append(EVAL(iter.deref(), env)); - return new Mal.Vector.from_list(results); + var vec = ast as Mal.Vector; + var result = new Mal.Vector.with_size(vec.length); + var root = new GC.Root(result); (void)root; + for (var i = 0; i < vec.length; i++) + result[i] = EVAL(vec[i], env); + return result; } if (ast is Mal.Hashmap) { var result = new Mal.Hashmap(); @@ -71,19 +72,19 @@ class Mal.Main : GLib.Object { } private static Mal.Val define_eval(Mal.Val key, Mal.Val value, - Mal.Env eval_env, Mal.Env def_env, + Mal.Env env, bool is_macro = false) throws Mal.Error { var rootk = new GC.Root(key); (void)rootk; - var roote = new GC.Root(def_env); (void)roote; + var roote = new GC.Root(env); (void)roote; var symkey = key as Mal.Sym; if (symkey == null) throw new Mal.Error.BAD_PARAMS( "let*: expected a symbol to define"); - var val = EVAL(value, eval_env); + var val = EVAL(value, env); if (val is Mal.Function) (val as Mal.Function).is_macro = is_macro; - def_env.set(symkey, val); + env.set(symkey, val); return val; } @@ -195,7 +196,7 @@ class Mal.Main : GLib.Object { throw new Mal.Error.BAD_PARAMS( "def!: expected two values"); return define_eval(list.next.data, list.next.next.data, - env, env, sym.v == "defmacro!"); + env, sym.v == "defmacro!"); case "let*": if (list.length() != 3) throw new Mal.Error.BAD_PARAMS( @@ -211,8 +212,7 @@ class Mal.Main : GLib.Object { throw new Mal.Error.BAD_PARAMS( "let*: expected an even-length list" + " of definitions"); - define_eval(iter.data, iter.next.data, - env, env); + define_eval(iter.data, iter.next.data, env); } } else if (defns is Mal.Vector) { var vec = defns as Mal.Vector; @@ -221,7 +221,7 @@ class Mal.Main : GLib.Object { "let*: expected an even-length vector" + " of definitions"); for (var i = 0; i < vec.length; i += 2) - define_eval(vec[i], vec[i+1], env, env); + define_eval(vec[i], vec[i+1], env); } else { throw new Mal.Error.BAD_PARAMS( "let*: expected a list or vector of definitions"); @@ -375,7 +375,6 @@ class Mal.Main : GLib.Object { setup("(def! not (fn* (a) (if a false true)))", env); setup("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", env); setup("(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)))))))", env); - setup("(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))))))))", env); var ARGV = new GLib.List(); if (args.length > 1) { diff --git a/vala/stepA_mal.vala b/vala/stepA_mal.vala index d1d105f4..349c6078 100644 --- a/vala/stepA_mal.vala +++ b/vala/stepA_mal.vala @@ -53,11 +53,12 @@ class Mal.Main : GLib.Object { return result; } if (ast is Mal.Vector) { - var results = new GLib.List(); - for (var iter = (ast as Mal.Vector).iter(); - iter.nonempty(); iter.step()) - results.append(EVAL(iter.deref(), env)); - return new Mal.Vector.from_list(results); + var vec = ast as Mal.Vector; + var result = new Mal.Vector.with_size(vec.length); + var root = new GC.Root(result); (void)root; + for (var i = 0; i < vec.length; i++) + result[i] = EVAL(vec[i], env); + return result; } if (ast is Mal.Hashmap) { var result = new Mal.Hashmap(); @@ -71,19 +72,19 @@ class Mal.Main : GLib.Object { } private static Mal.Val define_eval(Mal.Val key, Mal.Val value, - Mal.Env eval_env, Mal.Env def_env, + Mal.Env env, bool is_macro = false) throws Mal.Error { var rootk = new GC.Root(key); (void)rootk; - var roote = new GC.Root(def_env); (void)roote; + var roote = new GC.Root(env); (void)roote; var symkey = key as Mal.Sym; if (symkey == null) throw new Mal.Error.BAD_PARAMS( "let*: expected a symbol to define"); - var val = EVAL(value, eval_env); + var val = EVAL(value, env); if (val is Mal.Function) (val as Mal.Function).is_macro = is_macro; - def_env.set(symkey, val); + env.set(symkey, val); return val; } @@ -195,7 +196,7 @@ class Mal.Main : GLib.Object { throw new Mal.Error.BAD_PARAMS( "def!: expected two values"); return define_eval(list.next.data, list.next.next.data, - env, env, sym.v == "defmacro!"); + env, sym.v == "defmacro!"); case "let*": if (list.length() != 3) throw new Mal.Error.BAD_PARAMS( @@ -211,8 +212,7 @@ class Mal.Main : GLib.Object { throw new Mal.Error.BAD_PARAMS( "let*: expected an even-length list" + " of definitions"); - define_eval(iter.data, iter.next.data, - env, env); + define_eval(iter.data, iter.next.data, env); } } else if (defns is Mal.Vector) { var vec = defns as Mal.Vector; @@ -221,7 +221,7 @@ class Mal.Main : GLib.Object { "let*: expected an even-length vector" + " of definitions"); for (var i = 0; i < vec.length; i += 2) - define_eval(vec[i], vec[i+1], env, env); + define_eval(vec[i], vec[i+1], env); } else { throw new Mal.Error.BAD_PARAMS( "let*: expected a list or vector of definitions"); @@ -376,9 +376,6 @@ class Mal.Main : GLib.Object { setup("(def! not (fn* (a) (if a false true)))", env); setup("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", env); setup("(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)))))))", env); - setup("(def! inc (fn* [x] (+ x 1)))", env); - setup("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))", env); - setup("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))", env); var ARGV = new GLib.List(); if (args.length > 1) { diff --git a/vb/step8_macros.vb b/vb/step8_macros.vb index 1e977aed..32e102d1 100644 --- a/vb/step8_macros.vb +++ b/vb/step8_macros.vb @@ -254,7 +254,6 @@ Namespace Mal 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 args.Length > fileIdx Then REP("(load-file """ & args(fileIdx) & """)") diff --git a/vb/step9_try.vb b/vb/step9_try.vb index 690182db..83bd1bf7 100644 --- a/vb/step9_try.vb +++ b/vb/step9_try.vb @@ -277,7 +277,6 @@ Namespace Mal 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 args.Length > fileIdx Then REP("(load-file """ & args(fileIdx) & """)") diff --git a/vb/stepA_mal.vb b/vb/stepA_mal.vb index cd7f632f..b10b43c2 100644 --- a/vb/stepA_mal.vb +++ b/vb/stepA_mal.vb @@ -278,9 +278,6 @@ Namespace Mal 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("(def! inc (fn* [x] (+ x 1)))") - REP("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str ""G__"" (swap! counter inc))))))") - REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") If args.Length > fileIdx Then REP("(load-file """ & args(fileIdx) & """)") diff --git a/vhdl/step8_macros.vhdl b/vhdl/step8_macros.vhdl index 662b3959..028f8545 100644 --- a/vhdl/step8_macros.vhdl +++ b/vhdl/step8_macros.vhdl @@ -411,7 +411,6 @@ architecture test of step8_macros is RE("(def! not (fn* (a) (if a false true)))", repl_env, dummy_val, err); RE("(def! load-file (fn* (f) (eval (read-string (str " & '"' & "(do " & '"' & " (slurp f) " & '"' & ")" & '"' & ")))))", repl_env, dummy_val, err); RE("(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, dummy_val, err); - RE("(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))))))))", repl_env, dummy_val, err); if program_file /= null then REP("(load-file " & '"' & program_file.all & '"' & ")", repl_env, result, err); diff --git a/vhdl/step9_try.vhdl b/vhdl/step9_try.vhdl index 0e4b2d95..ece0ceab 100644 --- a/vhdl/step9_try.vhdl +++ b/vhdl/step9_try.vhdl @@ -469,7 +469,6 @@ architecture test of step9_try is RE("(def! not (fn* (a) (if a false true)))", repl_env, dummy_val, err); RE("(def! load-file (fn* (f) (eval (read-string (str " & '"' & "(do " & '"' & " (slurp f) " & '"' & ")" & '"' & ")))))", repl_env, dummy_val, err); RE("(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, dummy_val, err); - RE("(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))))))))", repl_env, dummy_val, err); if program_file /= null then REP("(load-file " & '"' & program_file.all & '"' & ")", repl_env, result, err); diff --git a/vhdl/stepA_mal.vhdl b/vhdl/stepA_mal.vhdl index 4006e416..80812c16 100644 --- a/vhdl/stepA_mal.vhdl +++ b/vhdl/stepA_mal.vhdl @@ -470,9 +470,6 @@ architecture test of stepA_mal is RE("(def! not (fn* (a) (if a false true)))", repl_env, dummy_val, err); RE("(def! load-file (fn* (f) (eval (read-string (str " & '"' & "(do " & '"' & " (slurp f) " & '"' & ")" & '"' & ")))))", repl_env, dummy_val, err); RE("(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, dummy_val, err); - RE("(def! inc (fn* [x] (+ x 1)))", repl_env, dummy_val, err); - RE("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str " & '"' & "G__" & '"' & " (swap! counter inc))))))", repl_env, dummy_val, err); - RE("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))", repl_env, dummy_val, err); if program_file /= null then REP("(load-file " & '"' & program_file.all & '"' & ")", repl_env, result, err); diff --git a/vimscript/step8_macros.vim b/vimscript/step8_macros.vim index 96e676ce..c64268df 100644 --- a/vimscript/step8_macros.vim +++ b/vimscript/step8_macros.vim @@ -193,7 +193,6 @@ call repl_env.set("*ARGV*", GetArgvList()) call RE("(def! not (fn* (a) (if a false true)))", repl_env) call RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env) call RE("(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) -call RE("(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))))))))", repl_env) if !empty(argv()) call RE('(load-file "' . argv(0) . '")', repl_env) diff --git a/vimscript/step9_try.vim b/vimscript/step9_try.vim index da0ea899..5f826438 100644 --- a/vimscript/step9_try.vim +++ b/vimscript/step9_try.vim @@ -225,7 +225,6 @@ call repl_env.set("*ARGV*", GetArgvList()) call RE("(def! not (fn* (a) (if a false true)))", repl_env) call RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env) call RE("(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) -call RE("(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))))))))", repl_env) if !empty(argv()) try diff --git a/vimscript/stepA_mal.vim b/vimscript/stepA_mal.vim index bdc00bd2..063a8f71 100644 --- a/vimscript/stepA_mal.vim +++ b/vimscript/stepA_mal.vim @@ -226,9 +226,6 @@ call RE("(def! *host-language* \"vimscript\")", repl_env) call RE("(def! not (fn* (a) (if a false true)))", repl_env) call RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env) call RE("(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) -call RE("(def! inc (fn* [x] (+ x 1)))", repl_env) -call RE("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))", repl_env) -call RE("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))", repl_env) if !empty(argv()) try diff --git a/wasm/step8_macros.wam b/wasm/step8_macros.wam index 6ecaed28..3c5efaa1 100644 --- a/wasm/step8_macros.wam +++ b/wasm/step8_macros.wam @@ -493,7 +493,6 @@ ($RELEASE ($RE "(def! not (fn* (a) (if a false true)))" $repl_env)) ($RELEASE ($RE "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" $repl_env)) ($RELEASE ($RE "(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)) - ($RELEASE ($RE "(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))))))))" $repl_env)) ;; Command line arguments diff --git a/wasm/step9_try.wam b/wasm/step9_try.wam index b9a3084a..b70afb1e 100644 --- a/wasm/step9_try.wam +++ b/wasm/step9_try.wam @@ -540,7 +540,6 @@ ($RELEASE ($RE "(def! not (fn* (a) (if a false true)))" $repl_env)) ($RELEASE ($RE "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" $repl_env)) ($RELEASE ($RE "(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)) - ($RELEASE ($RE "(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))))))))" $repl_env)) ;; Command line arguments diff --git a/wasm/stepA_mal.wam b/wasm/stepA_mal.wam index 5935ef91..3caff191 100644 --- a/wasm/stepA_mal.wam +++ b/wasm/stepA_mal.wam @@ -541,9 +541,6 @@ ($RELEASE ($RE "(def! not (fn* (a) (if a false true)))" $repl_env)) ($RELEASE ($RE "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" $repl_env)) ($RELEASE ($RE "(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)) - ($RELEASE ($RE "(def! inc (fn* [x] (+ x 1)))" $repl_env)) - ($RELEASE ($RE "(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))" $repl_env)) - ($RELEASE ($RE "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (c (gensym)) `(let* (~c ~(first xs)) (if ~c ~c (or ~@(rest xs)))))))))" $repl_env)) ;; Command line arguments (local.set $res ($MAP_LOOP_START (global.get $LIST_T))) diff --git a/yorick/step8_macros.i b/yorick/step8_macros.i index c5c5fb84..cf77d268 100644 --- a/yorick/step8_macros.i +++ b/yorick/step8_macros.i @@ -227,7 +227,6 @@ func main(void) RE, "(def! not (fn* (a) (if a false true)))", repl_env RE, "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env RE, "(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 - RE, "(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))))))))", repl_env if (numberof(command_line_args) > 0) { RE, "(load-file \"" + command_line_args(1) + "\")", repl_env diff --git a/yorick/step9_try.i b/yorick/step9_try.i index bb1c2123..b0899741 100644 --- a/yorick/step9_try.i +++ b/yorick/step9_try.i @@ -241,7 +241,6 @@ func main(void) RE, "(def! not (fn* (a) (if a false true)))", repl_env RE, "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env RE, "(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 - RE, "(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))))))))", repl_env if (numberof(command_line_args) > 0) { RE, "(load-file \"" + command_line_args(1) + "\")", repl_env diff --git a/yorick/stepA_mal.i b/yorick/stepA_mal.i index 6b35a129..ea5c2b32 100644 --- a/yorick/stepA_mal.i +++ b/yorick/stepA_mal.i @@ -242,9 +242,6 @@ func main(void) RE, "(def! not (fn* (a) (if a false true)))", repl_env RE, "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env RE, "(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 - RE, "(def! inc (fn* [x] (+ x 1)))", repl_env - RE, "(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))", repl_env - RE, "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))", repl_env if (numberof(command_line_args) > 0) { RE, "(load-file \"" + command_line_args(1) + "\")", repl_env