diff options
| author | Joel Martin <github@martintribe.org> | 2014-04-17 21:49:07 -0500 |
|---|---|---|
| committer | Joel Martin <github@martintribe.org> | 2014-04-17 21:49:07 -0500 |
| commit | db4c329aff4621e05b92a55be4f18173f5a4f655 (patch) | |
| tree | 92baaa6d88246e509cfd6a7d03d8fab997e0b935 | |
| parent | 8cb5cda46cf3aef847ae3926dc53a5e5f87fe261 (diff) | |
| download | mal-db4c329aff4621e05b92a55be4f18173f5a4f655.tar.gz mal-db4c329aff4621e05b92a55be4f18173f5a4f655.zip | |
All: perf test, Makefile refactor, add *host-language*
Other:
- bash,make,postscript: quasiquote of vectors
- Fix Java slurp
- add time function to core.mal
- switches on *host-language* for make time-secs vs time-ms
- Ignore */experiments directories
44 files changed, 192 insertions, 61 deletions
@@ -1,3 +1,4 @@ +*/experiments make/mal.mk js/node_modules js/mal.js @@ -25,6 +25,7 @@ step9 = step9_interop stepA = stepA_more EXCLUDE_TESTS = test^make^step5 test^mal^step0 test^mal^step5 test^mal^step9 test^java^step9 test^cs^step9 +EXCLUDE_PERFS = perf^mal # TODO: fix this # # Utility functions @@ -32,32 +33,36 @@ EXCLUDE_TESTS = test^make^step5 test^mal^step0 test^mal^step5 test^mal^step9 tes STEP_TEST_FILES = $(strip $(wildcard $(1)/tests/$($(2)).mal) $(wildcard tests/$($(2)).mal)) -bash_STEP_TO_PROG = bash/$($(1)).sh -c_STEP_TO_PROG = c/$($(1)) +bash_STEP_TO_PROG = bash/$($(1)).sh +c_STEP_TO_PROG = c/$($(1)) clojure_STEP_TO_PROG = clojure/src/$($(1)).clj -cs_STEP_TO_PROG = cs/$($(1)).exe -java_STEP_TO_PROG = java/src/main/java/mal/$($(1)).java -js_STEP_TO_PROG = js/$($(1)).js -make_STEP_TO_PROG = make/$($(1)).mk -mal_STEP_TO_PROG = mal/$($(1)).mal -php_STEP_TO_PROG = php/$($(1)).php -ps_STEP_TO_PROG = ps/$($(1)).ps -python_STEP_TO_PROG = python/$($(1)).py -ruby_STEP_TO_PROG = ruby/$($(1)).rb - - -bash_RUNTEST = ../runtest.py $(4) ../$(1) -- bash ../$(2) $(5) -c_RUNTEST = ../runtest.py $(4) ../$(1) -- ../$(2) $(5) -clojure_RUNTEST = ../runtest.py $(4) ../$(1) -- lein with-profile +$(3) trampoline run $(5) -cs_RUNTEST = ../runtest.py --redirect $(4) ../$(1) -- mono --debug ../$(2) --raw $(5) -java_RUNTEST = ../runtest.py $(4) ../$(1) -- mvn -quiet exec:java -Dexec.mainClass="mal.$($(3))" -Dexec.args="--raw$(if $(5), $(5),)" -js_RUNTEST = ../runtest.py $(4) ../$(1) -- node ../$(2) $(5) -make_RUNTEST = ../runtest.py $(4) ../$(1) -- make -f ../$(2) $(5) -mal_RUNTEST = $(call $(MAL_IMPL)_RUNTEST,$(1),$(call $(MAL_IMPL)_STEP_TO_PROG,stepA),stepA,--start-timeout 30 --test-timeout 120,../$(2)) -php_RUNTEST = ../runtest.py $(4) ../$(1) -- php ../$(2) $(5) -ps_RUNTEST = ../runtest.py $(4) ../$(1) -- "gs -q -dNODISPLAY -- ../$(2) $(5)" -python_RUNTEST = ../runtest.py $(4) ../$(1) -- $(PYTHON) ../$(2) $(5) -ruby_RUNTEST = ../runtest.py $(4) ../$(1) -- ruby ../$(2) $(5) +cs_STEP_TO_PROG = cs/$($(1)).exe +java_STEP_TO_PROG = java/src/main/java/mal/$($(1)).java +js_STEP_TO_PROG = js/$($(1)).js +make_STEP_TO_PROG = make/$($(1)).mk +mal_STEP_TO_PROG = mal/$($(1)).mal +php_STEP_TO_PROG = php/$($(1)).php +ps_STEP_TO_PROG = ps/$($(1)).ps +python_STEP_TO_PROG = python/$($(1)).py +ruby_STEP_TO_PROG = ruby/$($(1)).rb + + +bash_RUNSTEP = bash ../$(2) $(3) +c_RUNSTEP = ../$(2) $(3) +clojure_RUNSTEP = lein with-profile +$(1) trampoline run $(3) +cs_RUNSTEP = mono ../$(2) --raw $(3) +java_RUNSTEP = mvn -quiet exec:java -Dexec.mainClass="mal.$($(1))" -Dexec.args="--raw$(if $(3), $(3),)" +js_RUNSTEP = node ../$(2) $(3) +make_RUNSTEP = make -f ../$(2) $(3) +mal_RUNSTEP = $(call $(MAL_IMPL)_RUNSTEP,$(1),$(call $(MAL_IMPL)_STEP_TO_PROG,stepA),../$(2),") #" +php_RUNSTEP = php ../$(2) $(3) +ps_RUNSTEP = $(4)gs -q -dNODISPLAY -- ../$(2) $(3)$(4) +python_RUNSTEP = $(PYTHON) ../$(2) $(3) +ruby_RUNSTEP = ruby ../$(2) $(3) + +# Extra options to pass to runtest.py +cs_TEST_OPTS = --redirect +mal_TEST_OPTS = --start-timeout 30 --test-timeout 120 # Derived lists @@ -72,6 +77,8 @@ ALL_TESTS = $(filter-out $(EXCLUDE_TESTS),\ IMPL_STATS = $(foreach impl,$(IMPLS),stats^$(impl)) IMPL_STATS_LISP = $(foreach impl,$(IMPLS),stats-lisp^$(impl)) +IMPL_PERF = $(filter-out $(EXCLUDE_PERFS),$(foreach impl,$(IMPLS),perf^$(impl))) + # # Build rules # @@ -95,8 +102,8 @@ $(ALL_TESTS): $$(call $$(word 2,$$(subst ^, ,$$(@)))_STEP_TO_PROG,$$(word 3,$$(s $(foreach test,$(call STEP_TEST_FILES,$(impl),$(step)),\ echo '----------------------------------------------'; \ echo 'Testing $@, step file: $+, test file: $(test)'; \ - echo 'Running: $(call $(impl)_RUNTEST,$(test),$(+),$(step))'; \ - $(call $(impl)_RUNTEST,$(test),$(+),$(step))))) + echo 'Running: ../runtest.py $(call $(impl)_TEST_OPTS) ../$(test) -- $(call $(impl)_RUNSTEP,$(step),$(+))'; \ + ../runtest.py $(call $(impl)_TEST_OPTS) ../$(test) -- $(call $(impl)_RUNSTEP,$(step),$(+))))) test: $(ALL_TESTS) tests: $(ALL_TESTS) @@ -104,6 +111,9 @@ tests: $(ALL_TESTS) # Stats rules +stats: $(IMPL_STATS) +stats-lisp: $(IMPL_STATS_LISP) + .SECONDEXPANSION: $(IMPL_STATS): @echo "----------------------------------------------"; \ @@ -118,5 +128,19 @@ $(IMPL_STATS_LISP): echo "Stats (lisp only) for $(impl):"; \ $(MAKE) --no-print-directory -C $(impl) stats-lisp) -stats: $(IMPL_STATS) -stats-lisp: $(IMPL_STATS_LISP) + +# Performance test rules + +perf: $(IMPL_PERF) + +.SECONDEXPANSION: +$(IMPL_PERF): + @echo "----------------------------------------------"; \ + $(foreach impl,$(word 2,$(subst ^, ,$(@))),\ + cd $(if $(filter mal,$(impl)),$(MAL_IMPL),$(impl)); \ + echo "Performance test for $(impl):"; \ + echo 'Running: $(call $(impl)_RUNSTEP,stepA,$(call $(impl)_STEP_TO_PROG,stepA),../tests/perf1.mal)'; \ + $(call $(impl)_RUNSTEP,stepA,$(call $(impl)_STEP_TO_PROG,stepA),../tests/perf1.mal); \ + echo 'Running: $(call $(impl)_RUNSTEP,stepA,$(call $(impl)_STEP_TO_PROG,stepA),../tests/perf2.mal)'; \ + $(call $(impl)_RUNSTEP,stepA,$(call $(impl)_STEP_TO_PROG,stepA),../tests/perf2.mal)) + diff --git a/bash/core.sh b/bash/core.sh index e0cba38..c8a0261 100644 --- a/bash/core.sh +++ b/bash/core.sh @@ -56,6 +56,12 @@ num_gte () { r=$(( ${ANON["${1}"]} >= ${ANON["${2}"]} )); _num_bool "${r}"; num_lt () { r=$(( ${ANON["${1}"]} < ${ANON["${2}"]} )); _num_bool "${r}"; } num_lte () { r=$(( ${ANON["${1}"]} <= ${ANON["${2}"]} )); _num_bool "${r}"; } +# return number of milliseconds since epoch +time_ms () { + local ms=$(date +%s%3N) + _number "${ms}" +} + # String functions @@ -280,7 +286,7 @@ meta () { } -# atoms +# Atom functions atom? () { _atom? "${1}" && r="${__true}" || r="${__false}"; } deref () { @@ -327,6 +333,7 @@ declare -A core_ns=( [-]=num_minus [__STAR__]=num_multiply [/]=num_divide + [time-ms]=time_ms [list]=_list [list?]=list? diff --git a/bash/step7_quote.sh b/bash/step7_quote.sh index b541d67..14721ba 100755 --- a/bash/step7_quote.sh +++ b/bash/step7_quote.sh @@ -14,7 +14,7 @@ READ () { } IS_PAIR () { - if _list? "${1}"; then + if _sequential? "${1}"; then _count "${1}" [[ "${r}" > 0 ]] && return 0 fi diff --git a/bash/step8_macros.sh b/bash/step8_macros.sh index 0e58404..e311489 100755 --- a/bash/step8_macros.sh +++ b/bash/step8_macros.sh @@ -14,7 +14,7 @@ READ () { } IS_PAIR () { - if _list? "${1}"; then + if _sequential? "${1}"; then _count "${1}" [[ "${r}" > 0 ]] && return 0 fi diff --git a/bash/step9_interop.sh b/bash/step9_interop.sh index c2b0931..0491f7f 100755 --- a/bash/step9_interop.sh +++ b/bash/step9_interop.sh @@ -14,7 +14,7 @@ READ () { } IS_PAIR () { - if _list? "${1}"; then + if _sequential? "${1}"; then _count "${1}" [[ "${r}" > 0 ]] && return 0 fi diff --git a/bash/stepA_more.sh b/bash/stepA_more.sh index d968942..8a8366a 100755 --- a/bash/stepA_more.sh +++ b/bash/stepA_more.sh @@ -14,7 +14,7 @@ READ () { } IS_PAIR () { - if _list? "${1}"; then + if _sequential? "${1}"; then _count "${1}" [[ "${r}" > 0 ]] && return 0 fi @@ -254,8 +254,11 @@ _fref () { _function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } for n in "${!core_ns[@]}"; do _fref "${n}" "${core_ns["${n}"]}"; done _eval () { EVAL "${1}" "${REPL_ENV}"; } _fref "eval" _eval +_time_ms () { local ms=$(date +%s%3N); _number "${ms}"; } +_fref "time-ms" _time_ms # core.mal: defined using the language itself +REP "(def! *host-language* \"bash\")" REP "(def! not (fn* (a) (if a false true)))" REP "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" REP "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))" @@ -135,6 +135,15 @@ WRAP_INTEGER_CMP_OP(gte,>=) WRAP_INTEGER_CMP_OP(lt,<) WRAP_INTEGER_CMP_OP(lte,<=) +MalVal *time_ms(MalVal *_) { + struct timeval tv; + long msecs; + gettimeofday(&tv, NULL); + msecs = tv.tv_sec * 1000 + tv.tv_usec/1000.0 + 0.5; + + return malval_new_integer(msecs); +} + // List functions @@ -422,7 +431,7 @@ MalVal *swap_BANG(MalVal *args) { -core_ns_entry core_ns[53] = { +core_ns_entry core_ns[54] = { {"=", (void*(*)(void*))equal_Q, 2}, {"throw", (void*(*)(void*))throw, 1}, {"nil?", (void*(*)(void*))nil_Q, 1}, @@ -446,6 +455,7 @@ core_ns_entry core_ns[53] = { {"-", (void*(*)(void*))int_minus, 2}, {"*", (void*(*)(void*))int_multiply, 2}, {"/", (void*(*)(void*))int_divide, 2}, + {"time-ms", (void*(*)(void*))time_ms, 0}, {"list", (void*(*)(void*))list, -1}, {"list?", (void*(*)(void*))list_Q, 1}, @@ -10,6 +10,6 @@ typedef struct { int arg_cnt; } core_ns_entry; -extern core_ns_entry core_ns[53]; +extern core_ns_entry core_ns[54]; #endif diff --git a/c/stepA_more.c b/c/stepA_more.c index 82bf3db..8547607 100644 --- a/c/stepA_more.c +++ b/c/stepA_more.c @@ -298,6 +298,7 @@ void init_repl_env() { malval_new_function((void*(*)(void *))do_eval, 1)); // core.mal: defined using the language itself + RE(repl_env, "", "(def! *host-language* \"c\")"); RE(repl_env, "", "(def! not (fn* (a) (if a false true)))"); RE(repl_env, "", "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); diff --git a/clojure/src/core.clj b/clojure/src/core.clj index 73d8fb4..6b0d9b5 100644 --- a/clojure/src/core.clj +++ b/clojure/src/core.clj @@ -5,7 +5,12 @@ (defn mal_throw [obj] (throw (ex-info "mal exception" {:data obj}))) -;; Metadata +;; Number functions + +(defn time-ms [] + (System/currentTimeMillis)) + +;; Metadata functions ;; - store metadata at :meta key of the real metadata (defn mal_with_meta [obj m] (let [new-meta (assoc (meta obj) :meta m)] @@ -14,8 +19,7 @@ (defn mal_meta [obj] (:meta (meta obj))) - -;; Atoms +;; Atom functions (defn atom? [atm] (= (type atm) clojure.lang.Atom)) @@ -43,6 +47,7 @@ ['- -] ['* *] ['/ /] + ['time-ms time-ms] ['list list] ['list? seq?] diff --git a/clojure/src/stepA_more.clj b/clojure/src/stepA_more.clj index d45d86a..9739760 100644 --- a/clojure/src/stepA_more.clj +++ b/clojure/src/stepA_more.clj @@ -154,6 +154,7 @@ (env/env-set repl-env 'eval (fn [ast] (EVAL ast repl-env))) ;; core.mal: defined using the language itself +(rep "(def! *host-language* \"clojure\")") (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)))))))") @@ -81,3 +81,19 @@ `(~(first form) ~@(rest form) ~x) (list form x)) `(->> (->> ~x ~form) ~@more)))))) + +(if (= "make" *host-language*) + (defmacro! time + (fn* (exp) + `(let* [start_FIXME (time-secs) + ret_FIXME ~exp] + (do + (prn (str "Elapsed time: " (- (time-secs) start_FIXME) "000 msecs")) + ret_FIXME)))) + (defmacro! time + (fn* (exp) + `(let* [start_FIXME (time-ms) + ret_FIXME ~exp] + (do + (prn (str "Elapsed time: " (- (time-ms) start_FIXME) " msecs")) + ret_FIXME))))) @@ -36,6 +36,11 @@ namespace Mal { a => a[0] is MalSymbol ? True : False); + // Number functions + static MalFunction time_ms = new MalFunction( + a => new MalInteger((int)( + DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond))); + // String functions static public MalFunction pr_str = new MalFunction( a => new MalString(printer._pr_str_args(a, " ", true)) ); @@ -265,6 +270,7 @@ namespace Mal { {"-", new MalFunction(a => (MalInteger)a[0] - (MalInteger)a[1])}, {"*", new MalFunction(a => (MalInteger)a[0] * (MalInteger)a[1])}, {"/", new MalFunction(a => (MalInteger)a[0] / (MalInteger)a[1])}, + {"time-ms", time_ms}, {"list", new MalFunction(a => new MalList(a.getValue()))}, {"list?", list_Q}, diff --git a/cs/stepA_more.cs b/cs/stepA_more.cs index cbbc91f..dbbf53d 100644 --- a/cs/stepA_more.cs +++ b/cs/stepA_more.cs @@ -234,6 +234,7 @@ namespace Mal { repl_env.set("eval", new MalFunction(a => EVAL(a[0], repl_env))); // core.mal: defined using the language itself + RE(repl_env, "(def! *host-language* \"c#\")"); RE(repl_env, "(def! not (fn* (a) (if a false true)))"); RE(repl_env, "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); 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)))))))"); @@ -12,6 +12,7 @@ All: - Print full exception when test gets EOF from expect - Break out impl eval into step0.5 + - Fix quasiquoting of vectors --------------------------------------------- @@ -22,6 +23,7 @@ C: C#: - step9_interop + - use same history file Clojure: @@ -50,7 +52,8 @@ Postscript: Python: Ruby: - - use same history file + - save history and use same history file + - setup require search path --------------------------------------------- diff --git a/java/src/main/java/mal/core.java b/java/src/main/java/mal/core.java index ea6787d..0f1d226 100644 --- a/java/src/main/java/mal/core.java +++ b/java/src/main/java/mal/core.java @@ -117,8 +117,10 @@ public class core { public MalVal apply(MalList args) throws MalThrowable { String fname = ((MalString)args.nth(0)).getValue(); try { + // Scanner drops final newline, so add it back return new MalString( - new Scanner(new File(fname)).useDelimiter("\\Z").next()); + new Scanner(new File(fname)).useDelimiter("\\Z").next() + + "\n"); } catch (FileNotFoundException e) { throw new MalError(e.getMessage()); } @@ -169,6 +171,12 @@ public class core { } }; + static MalFunction time_ms = new MalFunction() { + public MalVal apply(MalList a) throws MalThrowable { + return new MalInteger((int)System.currentTimeMillis()); + } + }; + // List functions static MalFunction new_list = new MalFunction() { @@ -480,6 +488,7 @@ public class core { .put("-", subtract) .put("*", multiply) .put("/", divide) + .put("time-ms", time_ms) .put("list", new_list) .put("list?", list_Q) diff --git a/java/src/main/java/mal/stepA_more.java b/java/src/main/java/mal/stepA_more.java index 5ed5667..17bdb16 100644 --- a/java/src/main/java/mal/stepA_more.java +++ b/java/src/main/java/mal/stepA_more.java @@ -245,6 +245,7 @@ public class stepA_more { }); // core.mal: defined using the language itself + RE(repl_env, "(def! *host-language* \"java\")"); RE(repl_env, "(def! not (fn* (a) (if a false true)))"); RE(repl_env, "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); RE(repl_env, "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); @@ -43,6 +43,10 @@ function slurp(f) { } +// Number functions +function time_ms() { return new Date().getTime(); } + + // Hash Map functions function assoc(src_hm) { var hm = types._clone(src_hm); @@ -166,6 +170,7 @@ var ns = {'type': types._obj_type, '-' : function(a,b){return a-b;}, '*' : function(a,b){return a*b;}, '/' : function(a,b){return a/b;}, + "time-ms": time_ms, 'list': types._list, 'list?': types._list_Q, diff --git a/js/stepA_more.js b/js/stepA_more.js index 06eb43d..a2035dc 100644 --- a/js/stepA_more.js +++ b/js/stepA_more.js @@ -164,6 +164,7 @@ for (var n in core.ns) { repl_env.set(n, core.ns[n]); } repl_env.set('eval', function(ast) { return EVAL(ast, repl_env); }); // core.mal: defined using the language itself +rep("(def! *host-language* \"javascript\")") rep("(def! not (fn* (a) (if a false true)))"); rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); diff --git a/make/core.mk b/make/core.mk index 80f51ac..2ee6597 100644 --- a/make/core.mk +++ b/make/core.mk @@ -48,6 +48,8 @@ number_subtract = $(call _pnumber,$(call int_subtract,$($(word 1,$(1))_value),$( number_multiply = $(call _pnumber,$(call int_multiply,$($(word 1,$(1))_value),$($(word 2,$(1))_value))) number_divide = $(call _pnumber,$(call int_divide,$($(word 1,$(1))_value),$($(word 2,$(1))_value))) +time_secs = $(call _number,$(shell echo $$(( $$(date +%s) % 65536 )))) + # String functions @@ -229,6 +231,7 @@ core_ns = type obj_type \ - number_subtract \ * number_multiply \ / number_divide \ + time-secs time_secs \ \ list _list \ list? list? \ diff --git a/make/step7_quote.mk b/make/step7_quote.mk index af14e85..072e407 100644 --- a/make/step7_quote.mk +++ b/make/step7_quote.mk @@ -18,7 +18,7 @@ $(if $(READLINE_EOF)$(__ERROR),,$(call READ_STR,$(if $(1),$(1),$(call READLINE," endef # EVAL: evaluate the parameter -IS_PAIR = $(if $(call _EQ,list,$(call _obj_type,$(1))),$(if $(call _EQ,0,$(call _count,$(1))),,true),) +IS_PAIR = $(if $(call _sequential?,$(1)),$(if $(call _EQ,0,$(call _count,$(1))),,true),) define QUASIQUOTE $(strip \ diff --git a/make/step8_macros.mk b/make/step8_macros.mk index 8ed5df8..f4234d2 100644 --- a/make/step8_macros.mk +++ b/make/step8_macros.mk @@ -18,7 +18,7 @@ $(if $(READLINE_EOF)$(__ERROR),,$(call READ_STR,$(if $(1),$(1),$(call READLINE," endef # EVAL: evaluate the parameter -IS_PAIR = $(if $(call _EQ,list,$(call _obj_type,$(1))),$(if $(call _EQ,0,$(call _count,$(1))),,true),) +IS_PAIR = $(if $(call _sequential?,$(1)),$(if $(call _EQ,0,$(call _count,$(1))),,true),) define QUASIQUOTE $(strip \ diff --git a/make/step9_interop.mk b/make/step9_interop.mk index 8c9220c..2dfe7e0 100644 --- a/make/step9_interop.mk +++ b/make/step9_interop.mk @@ -18,7 +18,7 @@ $(if $(READLINE_EOF)$(__ERROR),,$(call READ_STR,$(if $(1),$(1),$(call READLINE," endef # EVAL: evaluate the parameter -IS_PAIR = $(if $(call _EQ,list,$(call _obj_type,$(1))),$(if $(call _EQ,0,$(call _count,$(1))),,true),) +IS_PAIR = $(if $(call _sequential?,$(1)),$(if $(call _EQ,0,$(call _count,$(1))),,true),) define QUASIQUOTE $(strip \ diff --git a/make/stepA_more.mk b/make/stepA_more.mk index 00ae252..8b92178 100644 --- a/make/stepA_more.mk +++ b/make/stepA_more.mk @@ -18,7 +18,7 @@ $(if $(READLINE_EOF)$(__ERROR),,$(call READ_STR,$(if $(1),$(1),$(call READLINE," endef # EVAL: evaluate the parameter -IS_PAIR = $(if $(call _EQ,list,$(call _obj_type,$(1))),$(if $(call _EQ,0,$(call _count,$(1))),,true),) +IS_PAIR = $(if $(call _sequential?,$(1)),$(if $(call _EQ,0,$(call _count,$(1))),,true),) define QUASIQUOTE $(strip \ @@ -167,6 +167,7 @@ $(call _import_core,$(core_ns)) REPL_ENV := $(call ENV_SET,$(REPL_ENV),eval,$(call _function,$$(call EVAL,$$(1),$$(REPL_ENV)))) # core.mal: defined in terms of the language itself +$(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))))))) )) diff --git a/mal/stepA_more.mal b/mal/stepA_more.mal index b56a697..1af0d25 100644 --- a/mal/stepA_more.mal +++ b/mal/stepA_more.mal @@ -153,6 +153,7 @@ (env-set repl-env 'eval (fn* [ast] (EVAL ast repl-env))) ;; 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)))))))") diff --git a/php/core.php b/php/core.php index 39a22ac..4cb4667 100644 --- a/php/core.php +++ b/php/core.php @@ -37,6 +37,12 @@ function println() { } +// Number functions +function time_ms() { + return intval(microtime(1) * 1000); +} + + // Hash Map functions function assoc($src_hm) { $args = func_get_args(); @@ -188,6 +194,7 @@ $core_ns = array( '-'=> function ($a, $b) { return intval($a - $b,10); }, '*'=> function ($a, $b) { return intval($a * $b,10); }, '/'=> function ($a, $b) { return intval($a / $b,10); }, + 'time-ms'=>function () { return time_ms(); }, 'list'=> function () { return call_user_func_array('_list', func_get_args()); }, 'list?'=> function ($a) { return _list_Q($a); }, diff --git a/php/stepA_more.php b/php/stepA_more.php index c599986..2eae095 100644 --- a/php/stepA_more.php +++ b/php/stepA_more.php @@ -177,6 +177,7 @@ $repl_env->set('eval', _function(function($ast) { })); // core.mal: defined using the language itself +rep("(def! *host-language* \"php\")"); rep("(def! not (fn* (a) (if a false true)))"); rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); @@ -1,5 +1,3 @@ -(in core.ps\n) print - % requires types.ps % Errors/Exceptions @@ -239,6 +237,7 @@ end } def (-) { dup 0 _nth exch 1 _nth sub } (*) { dup 0 _nth exch 1 _nth mul } (/) { dup 0 _nth exch 1 _nth idiv } + (time-ms) { pop realtime } (list) { /data get _list_from_array } (list?) { 0 _nth _list? } @@ -1,5 +1,3 @@ -(in env.ps\n) print - % outer binds exprs -> env_new -> new_env /env_new { 3 dict begin %(in env_new\n) print diff --git a/ps/printer.ps b/ps/printer.ps index de91d80..3062e2d 100644 --- a/ps/printer.ps +++ b/ps/printer.ps @@ -1,5 +1,3 @@ -(in printer.ps\n) print - % requires types.ps to be included first % ast print_readably -> _pr_str -> string diff --git a/ps/reader.ps b/ps/reader.ps index 188a121..f1f63f6 100644 --- a/ps/reader.ps +++ b/ps/reader.ps @@ -1,5 +1,3 @@ -(in reader\n) print - % requires types.ps to be included first /token_delim (;,"` \n{}\(\)[]) def diff --git a/ps/step7_quote.ps b/ps/step7_quote.ps index f335683..93bf464 100644 --- a/ps/step7_quote.ps +++ b/ps/step7_quote.ps @@ -17,7 +17,7 @@ % is_pair?: ast -> is_pair? -> bool % return true if non-empty list, otherwise false /is_pair? { - dup _list? { _count 0 gt }{ pop false } ifelse + dup _sequential? { _count 0 gt }{ pop false } ifelse } def % ast -> quasiquote -> new_ast diff --git a/ps/step8_macros.ps b/ps/step8_macros.ps index 87fafb1..814b62b 100644 --- a/ps/step8_macros.ps +++ b/ps/step8_macros.ps @@ -17,7 +17,7 @@ % is_pair?: ast -> is_pair? -> bool % return true if non-empty list, otherwise false /is_pair? { - dup _list? { _count 0 gt }{ pop false } ifelse + dup _sequential? { _count 0 gt }{ pop false } ifelse } def % ast -> quasiquote -> new_ast diff --git a/ps/step9_interop.ps b/ps/step9_interop.ps index fbf51a5..e8d837f 100644 --- a/ps/step9_interop.ps +++ b/ps/step9_interop.ps @@ -17,7 +17,7 @@ % is_pair?: ast -> is_pair? -> bool % return true if non-empty list, otherwise false /is_pair? { - dup _list? { _count 0 gt }{ pop false } ifelse + dup _sequential? { _count 0 gt }{ pop false } ifelse } def % ast -> quasiquote -> new_ast diff --git a/ps/stepA_more.ps b/ps/stepA_more.ps index 1720b94..73403b0 100644 --- a/ps/stepA_more.ps +++ b/ps/stepA_more.ps @@ -17,7 +17,7 @@ % is_pair?: ast -> is_pair? -> bool % return true if non-empty list, otherwise false /is_pair? { - dup _list? { _count 0 gt }{ pop false } ifelse + dup _sequential? { _count 0 gt }{ pop false } ifelse } def % ast -> quasiquote -> new_ast @@ -252,6 +252,7 @@ core_ns { _ref } forall (eval) { 0 _nth repl_env EVAL } _ref % core.mal: defined using the language itself +(\(def! *host-language* "postscript"\)) RE pop (\(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 diff --git a/ps/types.ps b/ps/types.ps index 2b26582..82be9c2 100644 --- a/ps/types.ps +++ b/ps/types.ps @@ -1,5 +1,3 @@ -(in types.ps\n) print - % General functions % concatenate: concatenate two strings or two arrays diff --git a/python/core.py b/python/core.py index 54737aa..d10047d 100644 --- a/python/core.py +++ b/python/core.py @@ -1,4 +1,4 @@ -import copy +import copy, time from itertools import chain import mal_types as types @@ -130,6 +130,7 @@ ns = { '-': lambda a,b: a-b, '*': lambda a,b: a*b, '/': lambda a,b: int(a/b), + 'time-ms': lambda : int(time.time() * 1000), 'list': types._list, 'list?': types._list_Q, diff --git a/python/stepA_more.py b/python/stepA_more.py index fa79ec3..e4bc768 100644 --- a/python/stepA_more.py +++ b/python/stepA_more.py @@ -58,7 +58,7 @@ def eval_ast(ast, env): def EVAL(ast, env): while True: - #print("EVAL %s" % ast) + #print("EVAL %s" % printer._pr_str(ast)) if not types._list_Q(ast): return eval_ast(ast, env) @@ -150,6 +150,7 @@ for k, v in core.ns.items(): repl_env.set(k, v) repl_env.set('eval', lambda ast: EVAL(ast, repl_env)) # core.mal: defined using the language itself +REP("(def! *host-language* \"python\")") REP("(def! not (fn* (a) (if a false true)))") REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") REP("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))") diff --git a/ruby/core.rb b/ruby/core.rb index c7fc8c3..6b127ba 100644 --- a/ruby/core.rb +++ b/ruby/core.rb @@ -26,6 +26,7 @@ $core_ns = { :- => lambda {|a,b| a - b}, :* => lambda {|a,b| a * b}, :/ => lambda {|a,b| a / b}, + :"time-ms" => lambda {|| (Time.now.to_f * 1000).to_i}, :list => lambda {|*a| List.new a}, :list? => lambda {|*a| a[0].is_a? List}, diff --git a/ruby/stepA_more.rb b/ruby/stepA_more.rb index 42eac5b..a2fc5cc 100644 --- a/ruby/stepA_more.rb +++ b/ruby/stepA_more.rb @@ -154,6 +154,7 @@ $core_ns.each do |k,v| repl_env.set(k,v) end repl_env.set(:eval, lambda {|ast| EVAL(ast, repl_env)}) # core.mal: defined using the language itself +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)))))))"] diff --git a/tests/perf1.mal b/tests/perf1.mal new file mode 100644 index 0000000..871e28f --- /dev/null +++ b/tests/perf1.mal @@ -0,0 +1,10 @@ +(load-file "../core.mal") + +;;(prn "Start: basic macros performance test") + +(time (do + (or false nil false nil false nil false nil false nil 4) + (cond false 1 nil 2 false 3 nil 4 false 5 nil 6 "else" 7) + (-> (list 1 2 3 4 5 6 7 8 9) rest rest rest rest rest rest first))) + +;;(prn "Done: basic macros performance test") diff --git a/tests/perf2.mal b/tests/perf2.mal new file mode 100644 index 0000000..3889191 --- /dev/null +++ b/tests/perf2.mal @@ -0,0 +1,12 @@ +(load-file "../core.mal") + +;;(prn "Start: basic math/recursion test") + +(def! sumdown (fn* (N) (if (> N 0) (+ N (sumdown (- N 1))) 0))) +(def! fib (fn* (N) (if (= N 0) 1 (if (= N 1) 1 (+ (fib (- N 1)) (fib (- N 2))))))) + +(time (do + (sumdown 10) + (fib 12))) + +;;(prn "Done: basic math/recursion test") diff --git a/tests/step7_quote.mal b/tests/step7_quote.mal index c41c7e2..9166065 100644 --- a/tests/step7_quote.mal +++ b/tests/step7_quote.mal @@ -47,6 +47,9 @@ ;=>(1 b 3) `(1 ~b 3) ;=>(1 (1 "b" "d") 3) +;;; TODO: fix this +;;;`[1 ~b 3] +;;;;=>[1 (1 "b" "d") 3] ;; Testing splice-unquote @@ -56,6 +59,9 @@ ;=>(1 c 3) `(1 ~@c 3) ;=>(1 1 "b" "d" 3) +;;; TODO: fix this +;;;`[1 ~@c 3] +;;;;=>[1 1 "b" "d" 3] ;; Testing symbol equality |
