aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Martin <github@martintribe.org>2014-10-09 23:48:47 -0500
committerJoel Martin <github@martintribe.org>2015-01-06 21:57:24 -0600
commit01c9731649a7ed97fad0bdeac9cb75b7323c0ad6 (patch)
treed6d00e574c489b206ddf78adc12c3d535bab7440
parent1771ab50b87c745181e4e30f94b63e3f23d33dac (diff)
downloadmal-01c9731649a7ed97fad0bdeac9cb75b7323c0ad6.tar.gz
mal-01c9731649a7ed97fad0bdeac9cb75b7323c0ad6.zip
All: swap step9,A. Fixes for bash, C, perl.
step9_interop -> stepA_interop stepA_more -> step9_try C: fix glib headers bash: behavior change of declare -A and pattern replacement. perl: squelch new 5.18 warnings related to switch/given statement. Also, include some in-progress interop related files.
-rw-r--r--.gitignore9
-rw-r--r--Makefile12
-rw-r--r--README.md12
-rw-r--r--bash/Makefile2
-rwxr-xr-xbash/step9_try.sh (renamed from bash/step9_interop.sh)22
-rwxr-xr-xbash/stepA_interop.sh (renamed from bash/stepA_more.sh)0
-rw-r--r--bash/tests/stepA_interop.mal (renamed from bash/tests/step9_interop.mal)0
-rw-r--r--bash/types.sh6
-rw-r--r--c/Makefile4
-rw-r--r--c/reader.c5
-rw-r--r--c/step9_try.c (renamed from c/step9_interop.c)26
-rw-r--r--c/stepA_interop.c (renamed from c/stepA_more.c)0
-rw-r--r--c/tests/stepA_interop.mal (renamed from c/tests/step9_interop.mal)0
-rw-r--r--clojure/Makefile2
-rw-r--r--clojure/project.clj4
-rw-r--r--clojure/src/step9_try.clj (renamed from clojure/src/step9_interop.clj)24
-rw-r--r--clojure/src/stepA_interop.clj (renamed from clojure/src/stepA_more.clj)2
-rw-r--r--clojure/tests/stepA_interop.mal (renamed from clojure/tests/step9_interop.mal)0
-rw-r--r--cs/Makefile4
-rw-r--r--cs/interop.cs66
-rw-r--r--cs/step9_try.cs (renamed from cs/stepA_more.cs)2
-rw-r--r--cs/stepA_interop.cs288
-rw-r--r--docs/TODO10
-rw-r--r--go/Makefile4
-rw-r--r--go/src/step9_try/step9_try.go (renamed from go/src/stepA_more/stepA_more.go)0
-rw-r--r--go/src/stepA_interop/stepA_interop.go306
-rw-r--r--java/Makefile2
-rw-r--r--java/src/main/java/mal/step9_try.java (renamed from java/src/main/java/mal/stepA_more.java)2
-rw-r--r--java/src/main/java/mal/stepA_interop.java302
-rw-r--r--js/Makefile2
-rw-r--r--js/step9_try.js (renamed from js/step9_interop.js)19
-rw-r--r--js/stepA_interop.js (renamed from js/stepA_more.js)0
-rw-r--r--js/tests/stepA_interop.mal (renamed from js/tests/step9_interop.mal)0
-rw-r--r--make/Makefile4
-rw-r--r--make/step9_try.mk (renamed from make/step9_interop.mk)22
-rw-r--r--make/stepA_interop.mk (renamed from make/stepA_more.mk)0
-rw-r--r--make/tests/stepA_interop.mal (renamed from make/tests/step9_interop.mal)0
-rw-r--r--mal/Makefile2
-rw-r--r--mal/step9_try.mal (renamed from mal/stepA_more.mal)0
-rw-r--r--mal/stepA_interop.mal181
-rw-r--r--perl/Makefile2
-rw-r--r--perl/interop.pm1
-rw-r--r--perl/printer.pm1
-rw-r--r--perl/reader.pm1
-rw-r--r--perl/step1_read_print.pl1
-rw-r--r--perl/step2_eval.pl1
-rw-r--r--perl/step3_env.pl1
-rw-r--r--perl/step4_if_fn_do.pl1
-rw-r--r--perl/step5_tco.pl1
-rw-r--r--perl/step6_file.pl1
-rw-r--r--perl/step7_quote.pl1
-rw-r--r--perl/step8_macros.pl1
-rw-r--r--perl/step9_try.pl (renamed from perl/step9_interop.pl)34
-rw-r--r--perl/stepA_interop.pl (renamed from perl/stepA_more.pl)1
-rw-r--r--perl/tests/stepA_interop.mal (renamed from perl/tests/step9_interop.mal)0
-rw-r--r--perl/types.pm1
-rw-r--r--php/Makefile2
-rw-r--r--php/step9_try.php (renamed from php/step9_interop.php)22
-rw-r--r--php/stepA_interop.php (renamed from php/stepA_more.php)0
-rw-r--r--php/tests/stepA_interop.mal25
-rw-r--r--ps/Makefile2
-rw-r--r--ps/interop.ps21
-rw-r--r--ps/step9_try.ps (renamed from ps/step9_interop.ps)52
-rw-r--r--ps/stepA_interop.ps (renamed from ps/stepA_more.ps)0
-rw-r--r--python/Makefile2
-rw-r--r--python/step9_try.py (renamed from python/step9_interop.py)19
-rw-r--r--python/stepA_interop.py (renamed from python/stepA_more.py)0
-rw-r--r--ruby/Makefile2
-rw-r--r--ruby/step9_try.rb (renamed from ruby/stepA_more.rb)0
-rw-r--r--ruby/stepA_interop.rb (renamed from ruby/step9_interop.rb)0
-rw-r--r--ruby/tests/stepA_interop.mal27
-rw-r--r--tests/step1_read_print.mal4
-rw-r--r--tests/step9_try.mal (renamed from tests/stepA_more.mal)0
73 files changed, 1478 insertions, 95 deletions
diff --git a/.gitignore b/.gitignore
index 691f079..cded525 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,14 +16,15 @@ c/step5_tco
c/step6_file
c/step7_quote
c/step8_macros
-c/step9_interop
-c/stepA_more
+c/step9_try
+c/stepA_interop
cs/*.exe
cs/*.dll
cs/*.mdb
clojure/target
clojure/.lein-repl-history
go/step*
+go/mal
java/target/
java/dependency-reduced-pom.xml
rust/step0_repl
@@ -35,5 +36,5 @@ rust/step5_tco
rust/step6_file
rust/step7_quote
rust/step8_macros
-rust/step9_interop
-rust/stepA_more
+rust/step9_try
+rust/stepA_interop
diff --git a/Makefile b/Makefile
index 97a8e0b..6c1bf12 100644
--- a/Makefile
+++ b/Makefile
@@ -21,8 +21,8 @@ step5 = step5_tco
step6 = step6_file
step7 = step7_quote
step8 = step8_macros
-step9 = step9_interop
-stepA = stepA_more
+step9 = step9_try
+stepA = stepA_interop
EXCLUDE_TESTS += test^bash^step5 # no stack exhaustion or completion
EXCLUDE_TESTS += test^c^step5 # segfault
@@ -34,9 +34,9 @@ EXCLUDE_TESTS += test^php^step5 # test completes, even at 100,000
EXCLUDE_TESTS += test^ruby^step5 # test completes, even at 100,000
# interop tests now implemented yet
-EXCLUDE_TESTS += test^cs^step9 test^java^step9 test^mal^step9 \
- test^mal^step0 test^php^step9 test^ps^step9 \
- test^python^step9 test^ruby^step9
+EXCLUDE_TESTS += test^cs^stepA test^java^stepA test^mal^stepA \
+ test^mal^step0 test^php^stepA test^ps^stepA \
+ test^python^stepA test^ruby^stepA
EXCLUDE_PERFS = perf^mal # TODO: fix this
@@ -73,7 +73,7 @@ make_RUNSTEP = make -f ../$(2) $(3)
mal_RUNSTEP = $(call $(MAL_IMPL)_RUNSTEP,$(1),$(call $(MAL_IMPL)_STEP_TO_PROG,stepA),../$(2),") #"
perl_RUNSTEP = perl ../$(2) $(3)
php_RUNSTEP = php ../$(2) $(3)
-ps_RUNSTEP = $(4)gs -q -dNODISPLAY -- ../$(2) $(3)$(4)
+ps_RUNSTEP = $(4)gs -q -I./ -dNODISPLAY -- ../$(2) $(3)$(4)
python_RUNSTEP = $(PYTHON) ../$(2) $(3)
ruby_RUNSTEP = ruby ../$(2) $(3)
diff --git a/README.md b/README.md
index 44fb631..f6d756a 100644
--- a/README.md
+++ b/README.md
@@ -57,8 +57,8 @@ bash stepX_YYY.sh
### C
-The C implementation of mal requires the following libraries: glib,
-libffi6 and either the libedit or GNU readline library.
+The C implementation of mal requires the following libraries (lib and
+header packages): glib, libffi6 and either the libedit or GNU readline library.
```
cd c
@@ -97,6 +97,8 @@ make
### Java 1.7
+The Java implementation of mal requires maven2 to build.
+
```
cd java
mvn compile
@@ -145,6 +147,9 @@ perl stepX_YYY.pl
### PHP 5.3
+The PHP implementation of mal requires the php command line interface
+to run.
+
```
cd php
php stepX_YYY.php
@@ -152,6 +157,9 @@ php stepX_YYY.php
### Postscript Level 2/3
+The Postscript implementation of mal requires ghostscript to run. It
+has been tested with ghostscript 9.10.
+
```
cd ps
gs -q -dNODISPLAY -I./ stepX_YYY.ps
diff --git a/bash/Makefile b/bash/Makefile
index e171f69..df663b5 100644
--- a/bash/Makefile
+++ b/bash/Makefile
@@ -1,5 +1,5 @@
SOURCES_BASE = types.sh reader.sh printer.sh
-SOURCES_LISP = env.sh core.sh stepA_more.sh
+SOURCES_LISP = env.sh core.sh stepA_interop.sh
SOURCES = $(SOURCES_BASE) $(SOURCES_LISP)
all: mal.sh
diff --git a/bash/step9_interop.sh b/bash/step9_try.sh
index ec8d6eb..db0b5a7 100755
--- a/bash/step9_interop.sh
+++ b/bash/step9_try.sh
@@ -157,14 +157,20 @@ EVAL () {
macroexpand)
MACROEXPAND "${a1}" "${env}"
return ;;
- sh*) MACROEXPAND "${a1}" "${env}"
+ try*) MACROEXPAND "${a1}" "${env}"
EVAL "${r}" "${env}"
- local output=""
- local line=""
- while read line; do
- output="${output}${line}\n"
- done < <(eval ${ANON["${r}"]})
- _string "${output%\\n}"
+ [[ -z "${__ERROR}" ]] && return
+ _nth "${a2}" 0; local a20="${r}"
+ if [ "${ANON["${a20}"]}" == "catch__STAR__" ]; then
+ _nth "${a2}" 1; local a21="${r}"
+ _nth "${a2}" 2; local a22="${r}"
+ _list "${a21}"; local binds="${r}"
+ ENV "${env}" "${binds}" "${__ERROR}"
+ local try_env="${r}"
+ __ERROR=
+ MACROEXPAND "${a22}" "${try_env}"
+ EVAL "${r}" "${try_env}"
+ fi # if no catch* clause, just propagate __ERROR
return ;;
do) _count "${ast}"
_slice "${ast}" 1 $(( ${r} - 2 ))
@@ -245,6 +251,7 @@ for _arg in "${@:2}"; do _string "${_arg}"; _conj! "${argv}" "${r}"; done
ENV_SET "${REPL_ENV}" "__STAR__ARGV__STAR__" "${argv}";
# core.mal: defined using the language itself
+REP "(def! *host-language* \"bash\")"
REP "(def! not (fn* (a) (if a false true)))"
REP "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"
REP "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"
@@ -257,6 +264,7 @@ if [[ "${1}" ]]; then
fi
# repl loop
+REP "(println (str \"Mal [\" *host-language* \"]\"))"
while true; do
READLINE "user> " || exit "$?"
[[ "${r}" ]] && REP "${r}" && echo "${r}"
diff --git a/bash/stepA_more.sh b/bash/stepA_interop.sh
index 2422643..2422643 100755
--- a/bash/stepA_more.sh
+++ b/bash/stepA_interop.sh
diff --git a/bash/tests/step9_interop.mal b/bash/tests/stepA_interop.mal
index bf3eabd..bf3eabd 100644
--- a/bash/tests/step9_interop.mal
+++ b/bash/tests/stepA_interop.mal
diff --git a/bash/types.sh b/bash/types.sh
index 6781492..5cdc14a 100644
--- a/bash/types.sh
+++ b/bash/types.sh
@@ -104,7 +104,7 @@ _false? () { [[ ${1} =~ ^fals_ ]]; }
_symbol () {
__new_obj_hash_code
r="symb_${r}"
- ANON["${r}"]="${1//$'\*'/__STAR__}"
+ ANON["${r}"]="${1//\*/__STAR__}"
}
_symbol? () { [[ ${1} =~ ^symb_ ]]; }
@@ -124,7 +124,7 @@ _number? () { [[ ${1} =~ ^numb_ ]]; }
_string () {
__new_obj_hash_code
r="strn_${r}"
- ANON["${r}"]="${1//$'\*'/__STAR__}"
+ ANON["${r}"]="${1//\*/__STAR__}"
}
_string? () { [[ ${1} =~ ^strn_ ]]; }
@@ -173,7 +173,7 @@ _hash_map () {
__new_obj_hash_code
local name="hmap_${r}"
local obj="${__obj_magic}_${name}"
- declare -A -g ${obj}
+ declare -A -g ${obj}; eval "${obj}=()"
ANON["${name}"]="${obj}"
while [[ "${1}" ]]; do
diff --git a/c/Makefile b/c/Makefile
index e18d81f..81b384c 100644
--- a/c/Makefile
+++ b/c/Makefile
@@ -9,7 +9,7 @@ TESTS =
SOURCES_BASE = readline.h readline.c types.h types.c \
reader.h reader.c printer.h printer.c \
interop.h interop.c
-SOURCES_LISP = env.c core.h core.c stepA_more.c
+SOURCES_LISP = env.c core.h core.c stepA_interop.c
SOURCES = $(SOURCES_BASE) $(SOURCES_LISP)
@@ -17,7 +17,7 @@ SOURCES = $(SOURCES_BASE) $(SOURCES_LISP)
SRCS = step0_repl.c step1_read_print.c step2_eval.c step3_env.c \
step4_if_fn_do.c step5_tco.c step6_file.c step7_quote.c \
- step8_macros.c step9_interop.c stepA_more.c
+ step8_macros.c step9_try.c stepA_interop.c
OBJS = $(SRCS:%.c=%.o)
BINS = $(OBJS:%.o=%)
OTHER_OBJS = types.o readline.o reader.o printer.o env.o core.o interop.o
diff --git a/c/reader.c b/c/reader.c
index dbb7335..d9b75b7 100644
--- a/c/reader.c
+++ b/c/reader.c
@@ -2,8 +2,9 @@
#include <stdio.h>
#include <string.h>
-#include <glib/gregex.h>
-#include <glib-object.h>
+//#include <glib/gregex.h>
+//#include <glib-object.h>
+#include <glib.h>
#include "types.h"
#include "reader.h"
diff --git a/c/step9_interop.c b/c/step9_try.c
index 6ba594e..395a7f0 100644
--- a/c/step9_interop.c
+++ b/c/step9_try.c
@@ -181,10 +181,26 @@ MalVal *EVAL(MalVal *ast, Env *env) {
MalVal *a1 = _nth(ast, 1);
return macroexpand(a1, env);
} else if ((a0->type & MAL_SYMBOL) &&
- strcmp(".", a0->val.string) == 0) {
- //g_print("eval apply .\n");
- MalVal *el = eval_ast(_slice(ast, 1, _count(ast)), env);
- return invoke_native(el);
+ strcmp("try*", a0->val.string) == 0) {
+ //g_print("eval apply try*\n");
+ MalVal *a1 = _nth(ast, 1);
+ MalVal *a2 = _nth(ast, 2);
+ MalVal *res = EVAL(a1, env);
+ if (!mal_error) { return res; }
+ MalVal *a20 = _nth(a2, 0);
+ if (strcmp("catch*", a20->val.string) == 0) {
+ MalVal *a21 = _nth(a2, 1);
+ MalVal *a22 = _nth(a2, 2);
+ Env *catch_env = new_env(env,
+ _listX(1, a21),
+ _listX(1, mal_error));
+ //malval_free(mal_error);
+ mal_error = NULL;
+ res = EVAL(a22, catch_env);
+ return res;
+ } else {
+ return &mal_nil;
+ }
} else if ((a0->type & MAL_SYMBOL) &&
strcmp("do", a0->val.string) == 0) {
//g_print("eval apply do\n");
@@ -287,6 +303,7 @@ void init_repl_env(int argc, char *argv[]) {
env_set(repl_env, "*ARGV*", _argv);
// core.mal: defined using the language itself
+ RE(repl_env, "", "(def! *host-language* \"c\")");
RE(repl_env, "", "(def! not (fn* (a) (if a false true)))");
RE(repl_env, "",
"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))");
@@ -311,6 +328,7 @@ int main(int argc, char *argv[])
}
// repl loop
+ RE(repl_env, "", "(println (str \"Mal [\" *host-language* \"]\"))");
for(;;) {
exp = RE(repl_env, prompt, NULL);
if (mal_error && strcmp("EOF", mal_error->val.string) == 0) {
diff --git a/c/stepA_more.c b/c/stepA_interop.c
index b4b7431..b4b7431 100644
--- a/c/stepA_more.c
+++ b/c/stepA_interop.c
diff --git a/c/tests/step9_interop.mal b/c/tests/stepA_interop.mal
index 657e3e7..657e3e7 100644
--- a/c/tests/step9_interop.mal
+++ b/c/tests/stepA_interop.mal
diff --git a/clojure/Makefile b/clojure/Makefile
index 2ddfbcc..6d227a2 100644
--- a/clojure/Makefile
+++ b/clojure/Makefile
@@ -1,5 +1,5 @@
SOURCES_BASE = src/readline.clj src/reader.clj src/printer.clj
-SOURCES_LISP = src/env.clj src/core.clj src/stepA_more.clj
+SOURCES_LISP = src/env.clj src/core.clj src/stepA_interop.clj
SOURCES = $(SOURCES_BASE) $(SOURCES_LISP)
all:
diff --git a/clojure/project.clj b/clojure/project.clj
index 4e7a15f..3d7ff2e 100644
--- a/clojure/project.clj
+++ b/clojure/project.clj
@@ -18,8 +18,8 @@
:step6 {:main step6-file}
:step7 {:main step7-quote}
:step8 {:main step8-macros}
- :step9 {:main step9-interop}
- :stepA {:main stepA-more}}
+ :step9 {:main step9-try}
+ :stepA {:main stepA-interop}}
:main stepA-more)
diff --git a/clojure/src/step9_interop.clj b/clojure/src/step9_try.clj
index c4d67e5..4990d75 100644
--- a/clojure/src/step9_interop.clj
+++ b/clojure/src/step9_try.clj
@@ -1,4 +1,4 @@
-(ns step9-interop
+(ns step9-try
(:refer-clojure :exclude [macroexpand])
(:require [clojure.repl]
[readline]
@@ -94,9 +94,20 @@
'macroexpand
(macroexpand a1 env)
- 'clj*
- (eval (reader/read-string a1))
-
+ 'try*
+ (if (= 'catch* (nth a2 0))
+ (try
+ (EVAL a1 env)
+ (catch clojure.lang.ExceptionInfo ei
+ (EVAL (nth a2 2) (env/env env
+ [(nth a2 1)]
+ [(:data (ex-data ei))])))
+ (catch Throwable t
+ (EVAL (nth a2 2) (env/env env
+ [(nth a2 1)]
+ [(.getMessage t)]))))
+ (EVAL a1 env))
+
'do
(do (eval-ast (->> ast (drop-last) (drop 1)) env)
(recur (last ast) env))
@@ -141,6 +152,7 @@
(env/env-set repl-env '*ARGV* ())
;; 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)))))))")
@@ -161,4 +173,6 @@
(env/env-set repl-env '*ARGV* (rest args))
(if args
(rep (str "(load-file \"" (first args) "\")"))
- (repl-loop)))
+ (do
+ (rep "(println (str \"Mal [\" *host-language* \"]\"))")
+ (repl-loop))))
diff --git a/clojure/src/stepA_more.clj b/clojure/src/stepA_interop.clj
index fc7451f..6ed9964 100644
--- a/clojure/src/stepA_more.clj
+++ b/clojure/src/stepA_interop.clj
@@ -1,4 +1,4 @@
-(ns stepA-more
+(ns stepA-interop
(:refer-clojure :exclude [macroexpand])
(:require [clojure.repl]
[readline]
diff --git a/clojure/tests/step9_interop.mal b/clojure/tests/stepA_interop.mal
index b323222..b323222 100644
--- a/clojure/tests/step9_interop.mal
+++ b/clojure/tests/stepA_interop.mal
diff --git a/cs/Makefile b/cs/Makefile
index b7eb023..1fd1f7a 100644
--- a/cs/Makefile
+++ b/cs/Makefile
@@ -5,7 +5,7 @@ DEBUG =
TESTS =
SOURCES_BASE = readline.cs types.cs reader.cs printer.cs
-SOURCES_LISP = env.cs core.cs stepA_more.cs
+SOURCES_LISP = env.cs core.cs stepA_interop.cs
SOURCES = $(SOURCES_BASE) $(SOURCES_LISP)
OTHER_SOURCES = getline.cs
@@ -14,7 +14,7 @@ OTHER_SOURCES = getline.cs
SRCS = step0_repl.cs step1_read_print.cs step2_eval.cs step3_env.cs \
step4_if_fn_do.cs step5_tco.cs step6_file.cs step7_quote.cs \
- step8_macros.cs stepA_more.cs
+ step8_macros.cs step9_try.cs stepA_interop.cs
LIB_SRCS = $(filter-out step%,$(OTHER_SOURCES) $(SOURCES))
diff --git a/cs/interop.cs b/cs/interop.cs
new file mode 100644
index 0000000..e383280
--- /dev/null
+++ b/cs/interop.cs
@@ -0,0 +1,66 @@
+using System;
+using System.CodeDom.Compiler;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.CSharp;
+
+public static class EvalProvider
+{
+ public static Func<T, TResult> CreateEvalMethod<T, TResult>(string code, string[] usingStatements = null, string[] assemblies = null)
+ {
+ Type returnType = typeof(TResult);
+ Type inputType = typeof(T);
+
+ var includeUsings = new HashSet<string>(new[] { "System" });
+ includeUsings.Add(returnType.Namespace);
+ includeUsings.Add(inputType.Namespace);
+ if (usingStatements != null)
+ foreach (var usingStatement in usingStatements)
+ includeUsings.Add(usingStatement);
+
+ using (CSharpCodeProvider compiler = new CSharpCodeProvider())
+ {
+ var name = "F" + Guid.NewGuid().ToString().Replace("-", string.Empty);
+ var includeAssemblies = new HashSet<string>(new[] { "system.dll" });
+ if (assemblies != null)
+ foreach (var assembly in assemblies)
+ includeAssemblies.Add(assembly);
+
+ var parameters = new CompilerParameters(includeAssemblies.ToArray())
+ {
+ GenerateInMemory = true
+ };
+
+ string source = string.Format(@"
+{0}
+namespace {1}
+{{
+ public static class EvalClass
+ {{
+ public static {2} Eval({3} arg)
+ {{
+ {4}
+ }}
+ }}
+}}", GetUsing(includeUsings), name, returnType.Name, inputType.Name, code);
+
+ var compilerResult = compiler.CompileAssemblyFromSource(parameters, source);
+ var compiledAssembly = compilerResult.CompiledAssembly;
+ var type = compiledAssembly.GetType(string.Format("{0}.EvalClass", name));
+ var method = type.GetMethod("Eval");
+ return (Func<T, TResult>)Delegate.CreateDelegate(typeof(Func<T, TResult>), method);
+ }
+ }
+
+ private static string GetUsing(HashSet<string> usingStatements)
+ {
+ StringBuilder result = new StringBuilder();
+ foreach (string usingStatement in usingStatements)
+ {
+ result.AppendLine(string.Format("using {0};", usingStatement));
+ }
+ return result.ToString();
+ }
+}
+
diff --git a/cs/stepA_more.cs b/cs/step9_try.cs
index 486c344..ed3417c 100644
--- a/cs/stepA_more.cs
+++ b/cs/step9_try.cs
@@ -14,7 +14,7 @@ using MalFunction = Mal.types.MalFunction;
using Env = Mal.env.Env;
namespace Mal {
- class stepA_more {
+ class step9_try {
// read
static MalVal READ(string str) {
return reader.read_str(str);
diff --git a/cs/stepA_interop.cs b/cs/stepA_interop.cs
new file mode 100644
index 0000000..632d18d
--- /dev/null
+++ b/cs/stepA_interop.cs
@@ -0,0 +1,288 @@
+using System;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using Mal;
+using MalVal = Mal.types.MalVal;
+using MalString = Mal.types.MalString;
+using MalSymbol = Mal.types.MalSymbol;
+using MalInteger = Mal.types.MalInteger;
+using MalList = Mal.types.MalList;
+using MalVector = Mal.types.MalVector;
+using MalHashMap = Mal.types.MalHashMap;
+using MalFunction = Mal.types.MalFunction;
+using Env = Mal.env.Env;
+
+namespace Mal {
+ class stepA_interop {
+ // read
+ static MalVal READ(string str) {
+ return reader.read_str(str);
+ }
+
+ // eval
+ public static bool is_pair(MalVal x) {
+ return x is MalList && ((MalList)x).size() > 0;
+ }
+
+ public static MalVal quasiquote(MalVal ast) {
+ if (!is_pair(ast)) {
+ return new MalList(new MalSymbol("quote"), ast);
+ } else {
+ MalVal a0 = ((MalList)ast)[0];
+ if ((a0 is MalSymbol) &&
+ (((MalSymbol)a0).getName() == "unquote")) {
+ return ((MalList)ast)[1];
+ } else if (is_pair(a0)) {
+ MalVal a00 = ((MalList)a0)[0];
+ if ((a00 is MalSymbol) &&
+ (((MalSymbol)a00).getName() == "splice-unquote")) {
+ return new MalList(new MalSymbol("concat"),
+ ((MalList)a0)[1],
+ quasiquote(((MalList)ast).rest()));
+ }
+ }
+ return new MalList(new MalSymbol("cons"),
+ quasiquote(a0),
+ quasiquote(((MalList)ast).rest()));
+ }
+ }
+
+ public static bool is_macro_call(MalVal ast, Env env) {
+ if (ast is MalList) {
+ MalVal a0 = ((MalList)ast)[0];
+ if (a0 is MalSymbol &&
+ env.find(((MalSymbol)a0).getName()) != null) {
+ MalVal mac = env.get(((MalSymbol)a0).getName());
+ if (mac is MalFunction &&
+ ((MalFunction)mac).isMacro()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public static MalVal macroexpand(MalVal ast, Env env) {
+ while (is_macro_call(ast, env)) {
+ MalSymbol a0 = (MalSymbol)((MalList)ast)[0];
+ MalFunction mac = (MalFunction) env.get(a0.getName());
+ ast = mac.apply(((MalList)ast).rest());
+ }
+ return ast;
+ }
+
+ static MalVal eval_ast(MalVal ast, Env env) {
+ if (ast is MalSymbol) {
+ MalSymbol sym = (MalSymbol)ast;
+ return env.get(sym.getName());
+ } else if (ast is MalList) {
+ MalList old_lst = (MalList)ast;
+ MalList new_lst = ast.list_Q() ? new MalList()
+ : (MalList)new MalVector();
+ foreach (MalVal mv in old_lst.getValue()) {
+ new_lst.conj_BANG(EVAL(mv, env));
+ }
+ return new_lst;
+ } else if (ast is MalHashMap) {
+ var new_dict = new Dictionary<string, MalVal>();
+ foreach (var entry in ((MalHashMap)ast).getValue()) {
+ new_dict.Add(entry.Key, EVAL((MalVal)entry.Value, env));
+ }
+ return new MalHashMap(new_dict);
+ } else {
+ return ast;
+ }
+ }
+
+
+ static MalVal EVAL(MalVal orig_ast, Env env) {
+ MalVal a0, a1, a2, res;
+ MalList el;
+
+ while (true) {
+
+ //System.out.println("EVAL: " + printer._pr_str(orig_ast, true));
+ if (!orig_ast.list_Q()) {
+ return eval_ast(orig_ast, env);
+ }
+
+ // apply list
+ MalVal expanded = macroexpand(orig_ast, env);
+ if (!expanded.list_Q()) { return expanded; }
+ MalList ast = (MalList) expanded;
+
+ if (ast.size() == 0) { return ast; }
+ a0 = ast[0];
+
+ String a0sym = a0 is MalSymbol ? ((MalSymbol)a0).getName()
+ : "__<*fn*>__";
+
+ switch (a0sym) {
+ case "def!":
+ a1 = ast[1];
+ a2 = ast[2];
+ res = EVAL(a2, env);
+ env.set(((MalSymbol)a1).getName(), res);
+ return res;
+ case "let*":
+ a1 = ast[1];
+ a2 = ast[2];
+ MalSymbol key;
+ MalVal val;
+ Env let_env = new Env(env);
+ for(int i=0; i<((MalList)a1).size(); i+=2) {
+ key = (MalSymbol)((MalList)a1)[i];
+ val = ((MalList)a1)[i+1];
+ let_env.set(key.getName(), EVAL(val, let_env));
+ }
+ orig_ast = a2;
+ env = let_env;
+ break;
+ case "quote":
+ return ast[1];
+ case "quasiquote":
+ orig_ast = quasiquote(ast[1]);
+ break;
+ case "defmacro!":
+ a1 = ast[1];
+ a2 = ast[2];
+ res = EVAL(a2, env);
+ ((MalFunction)res).setMacro();
+ env.set(((MalSymbol)a1).getName(), res);
+ return res;
+ case "macroexpand":
+ a1 = ast[1];
+ return macroexpand(a1, env);
+ case "try*":
+ try {
+ return EVAL(ast[1], env);
+ } catch (Exception e) {
+ if (ast.size() > 2) {
+ MalVal exc;
+ a2 = ast[2];
+ MalVal a20 = ((MalList)a2)[0];
+ if (((MalSymbol)a20).getName() == "catch*") {
+ if (e is Mal.types.MalException) {
+ exc = ((Mal.types.MalException)e).getValue();
+ } else {
+ exc = new MalString(e.StackTrace);
+ }
+ return EVAL(((MalList)a2)[2],
+ new Env(env, ((MalList)a2).slice(1,2),
+ new MalList(exc)));
+ }
+ }
+ throw e;
+ }
+ case "do":
+ eval_ast(ast.slice(1, ast.size()-1), env);
+ orig_ast = ast[ast.size()-1];
+ break;
+ case "if":
+ a1 = ast[1];
+ MalVal cond = EVAL(a1, env);
+ if (cond == Mal.types.Nil || cond == Mal.types.False) {
+ // eval false slot form
+ if (ast.size() > 3) {
+ orig_ast = ast[3];
+ } else {
+ return Mal.types.Nil;
+ }
+ } else {
+ // eval true slot form
+ orig_ast = ast[2];
+ }
+ break;
+ case "fn*":
+ MalList a1f = (MalList)ast[1];
+ MalVal a2f = ast[2];
+ Env cur_env = env;
+ return new MalFunction(a2f, env, a1f,
+ args => EVAL(a2f, new Env(cur_env, a1f, args)) );
+ default:
+ el = (MalList)eval_ast(ast, env);
+ var f = (MalFunction)el[0];
+ MalVal fnast = f.getAst();
+ if (fnast != null) {
+ orig_ast = fnast;
+ env = f.genEnv(el.rest());
+ } else {
+ return f.apply(el.rest());
+ }
+ break;
+ }
+
+ }
+ }
+
+ // print
+ static string PRINT(MalVal exp) {
+ return printer._pr_str(exp, true);
+ }
+
+ // repl
+ static MalVal RE(Env env, string str) {
+ return EVAL(READ(str), env);
+ }
+
+ static void Main(string[] args) {
+ string prompt = "user> ";
+
+ // core.cs: defined using C#
+ var repl_env = new env.Env(null);
+ foreach (var entry in core.ns) {
+ repl_env.set(entry.Key, entry.Value);
+ }
+ repl_env.set("eval", new MalFunction(a => EVAL(a[0], repl_env)));
+ MalList _argv = new MalList();
+ for (int i=1; i < args.Length; i++) {
+ _argv.conj_BANG(new MalString(args[i]));
+ }
+ repl_env.set("*ARGV*", _argv);
+
+ // core.mal: defined using the language itself
+ RE(repl_env, "(def! *host-language* \"c#\")");
+ RE(repl_env, "(def! not (fn* (a) (if a false true)))");
+ RE(repl_env, "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))");
+ 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 fileIdx = 0;
+ if (args.Length > 0 && args[0] == "--raw") {
+ Mal.readline.mode = Mal.readline.Mode.Raw;
+ fileIdx = 1;
+ }
+ if (args.Length > fileIdx) {
+ RE(repl_env, "(load-file \"" + args[fileIdx] + "\")");
+ return;
+ }
+
+ // repl loop
+ RE(repl_env, "(println (str \"Mal [\" *host-language* \"]\"))");
+ while (true) {
+ string line;
+ try {
+ line = Mal.readline.Readline(prompt);
+ if (line == null) { break; }
+ } catch (IOException e) {
+ Console.WriteLine("IOException: " + e.Message);
+ break;
+ }
+ try {
+ Console.WriteLine(PRINT(RE(repl_env, line)));
+ } catch (Mal.types.MalContinue) {
+ continue;
+ } catch (Mal.types.MalException e) {
+ Console.WriteLine("Error: " +
+ printer._pr_str(e.getValue(), false));
+ continue;
+ } catch (Exception e) {
+ Console.WriteLine("Error: " + e.Message);
+ Console.WriteLine(e.StackTrace);
+ continue;
+ }
+ }
+ }
+ }
+}
diff --git a/docs/TODO b/docs/TODO
index 2464293..6f0b2d0 100644
--- a/docs/TODO
+++ b/docs/TODO
@@ -6,7 +6,7 @@ All:
- keyword type
- gensym reader inside quasiquote
- - per impl tests for step5_tco, step9_interop (if possible)
+ - per impl tests for step5_tco (if possible)
- regular expression matching in runtest
- Print full exception when test gets EOF from expect
@@ -17,6 +17,8 @@ All:
- Move try* to step6
- Remove macros from mal
+ - Implement/fix interop: C#, Java, Mal, PHP, Postscript, Ruby
+
---------------------------------------------
Bash:
@@ -28,7 +30,6 @@ C:
- GC
C#:
- - step9_interop
Clojure:
@@ -37,7 +38,6 @@ Go:
https://gobyexample.com/variadic-functions
Java:
- - step9_interop
- Use gradle instead of mvn
http://blog.paralleluniverse.co/2014/05/01/modern-java/
@@ -51,7 +51,6 @@ Make:
Mal:
- line numbers in errors
- step5_tco
- - step9_interop
Perl:
- fix metadata on native functions
@@ -62,7 +61,8 @@ Postscript:
- add negative numbers
Python:
- - error: python ../python/stepA_more.py ../mal/stepA_more.mal ../mal/stepA_more.mal
+ - error: python ../python/stepA_interop.py ../mal/stepA_interop.mal ../mal/stepA_interop.mal
+ - interop tests
Ruby:
diff --git a/go/Makefile b/go/Makefile
index 18faa4f..4b32a0b 100644
--- a/go/Makefile
+++ b/go/Makefile
@@ -5,14 +5,14 @@ export GOPATH := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
SOURCES_BASE = src/types/types.go src/readline/readline.go \
src/reader/reader.go src/printer/printer.go \
src/env/env.go src/core/core.go
-SOURCES_LISP = src/stepA_more/stepA_more.go
+SOURCES_LISP = src/stepA_interop/stepA_interop.go
SOURCES = $(SOURCES_BASE) $(SOURCES_LISP)
#####################
SRCS = step0_repl.go step1_read_print.go step2_eval.go step3_env.go \
step4_if_fn_do.go step5_tco.go step6_file.go step7_quote.go \
- step8_macros.go stepA_more.go
+ step8_macros.go step9_try.go stepA_interop.go
BINS = $(SRCS:%.go=%)
#####################
diff --git a/go/src/stepA_more/stepA_more.go b/go/src/step9_try/step9_try.go
index 322ee36..322ee36 100644
--- a/go/src/stepA_more/stepA_more.go
+++ b/go/src/step9_try/step9_try.go
diff --git a/go/src/stepA_interop/stepA_interop.go b/go/src/stepA_interop/stepA_interop.go
new file mode 100644
index 0000000..322ee36
--- /dev/null
+++ b/go/src/stepA_interop/stepA_interop.go
@@ -0,0 +1,306 @@
+package main
+
+import (
+ "fmt"
+ "strings"
+ "errors"
+ "os"
+)
+
+import (
+ "readline"
+ . "types"
+ "reader"
+ "printer"
+ . "env"
+ "core"
+)
+
+// read
+func READ(str string) (MalType, error) {
+ return reader.Read_str(str)
+}
+
+// eval
+func is_pair(x MalType) bool {
+ slc, e := GetSlice(x)
+ if e != nil { return false }
+ return len(slc) > 0
+}
+
+func quasiquote(ast MalType) MalType {
+ if !is_pair(ast) {
+ return List{[]MalType{Symbol{"quote"}, ast},nil}
+ } else {
+ slc, _ := GetSlice(ast)
+ a0 := slc[0]
+ if Symbol_Q(a0) && (a0.(Symbol).Val == "unquote") {
+ return slc[1]
+ } else if is_pair(a0) {
+ slc0, _ := GetSlice(a0)
+ a00 := slc0[0]
+ if Symbol_Q(a00) && (a00.(Symbol).Val == "splice-unquote") {
+ return List{[]MalType{Symbol{"concat"},
+ slc0[1],
+ quasiquote(List{slc[1:],nil})},nil}
+ }
+ }
+ return List{[]MalType{Symbol{"cons"},
+ quasiquote(a0),
+ quasiquote(List{slc[1:],nil})},nil}
+ }
+}
+
+func is_macro_call(ast MalType, env EnvType) bool {
+ if List_Q(ast) {
+ slc, _ := GetSlice(ast)
+ a0 := slc[0]
+ if Symbol_Q(a0) && env.Find(a0.(Symbol).Val) != nil {
+ mac, e := env.Get(a0.(Symbol).Val)
+ if e != nil { return false }
+ if MalFunc_Q(mac) {
+ return mac.(MalFunc).GetMacro()
+ }
+ }
+ }
+ return false
+}
+
+func macroexpand(ast MalType, env EnvType) (MalType, error) {
+ var mac MalType
+ var e error
+ for ; is_macro_call(ast, env) ; {
+ slc, _ := GetSlice(ast)
+ a0 := slc[0]
+ mac, e = env.Get(a0.(Symbol).Val); if e != nil { return nil, e }
+ fn := mac.(MalFunc)
+ ast, e = Apply(fn, slc[1:]); if e != nil { return nil, e }
+ }
+ return ast, nil
+}
+
+func eval_ast(ast MalType, env EnvType) (MalType, error) {
+ //fmt.Printf("eval_ast: %#v\n", ast)
+ if Symbol_Q(ast) {
+ return env.Get(ast.(Symbol).Val)
+ } else if List_Q(ast) {
+ lst := []MalType{}
+ for _, a := range ast.(List).Val {
+ exp, e := EVAL(a, env)
+ if e != nil { return nil, e }
+ lst = append(lst, exp)
+ }
+ return List{lst,nil}, nil
+ } else if Vector_Q(ast) {
+ lst := []MalType{}
+ for _, a := range ast.(Vector).Val {
+ exp, e := EVAL(a, env)
+ if e != nil { return nil, e }
+ lst = append(lst, exp)
+ }
+ return Vector{lst,nil}, nil
+ } else if HashMap_Q(ast) {
+ m := ast.(HashMap)
+ new_hm := HashMap{map[string]MalType{},nil}
+ for k, v := range m.Val {
+ ke, e1 := EVAL(k, env)
+ if e1 != nil { return nil, e1 }
+ if _, ok := ke.(string); !ok {
+ return nil, errors.New("non string hash-map key")
+ }
+ kv, e2 := EVAL(v, env)
+ if e2 != nil { return nil, e2 }
+ new_hm.Val[ke.(string)] = kv
+ }
+ return new_hm, nil
+ } else {
+ return ast, nil
+ }
+}
+
+func EVAL(ast MalType, env EnvType) (MalType, error) {
+ var e error
+ for {
+
+ //fmt.Printf("EVAL: %v\n", printer.Pr_str(ast, true))
+ switch ast.(type) {
+ case List: // continue
+ default: return eval_ast(ast, env)
+ }
+
+ // apply list
+ ast, e = macroexpand(ast, env); if e != nil { return nil, e }
+ if (!List_Q(ast)) { return ast, nil }
+
+ a0 := ast.(List).Val[0]
+ var a1 MalType = nil; var a2 MalType = nil
+ switch len(ast.(List).Val) {
+ case 1:
+ a1 = nil; a2 = nil
+ case 2:
+ a1 = ast.(List).Val[1]; a2 = nil
+ default:
+ a1 = ast.(List).Val[1]; a2 = ast.(List).Val[2]
+ }
+ a0sym := "__<*fn*>__"
+ if Symbol_Q(a0) { a0sym = a0.(Symbol).Val }
+ switch a0sym {
+ case "def!":
+ res, e := EVAL(a2, env)
+ if e != nil { return nil, e }
+ return env.Set(a1.(Symbol).Val, res), nil
+ case "let*":
+ let_env, e := NewEnv(env, nil, nil)
+ if e != nil { return nil, e }
+ arr1, e := GetSlice(a1)
+ if e != nil { return nil, e }
+ for i := 0; i < len(arr1); i+=2 {
+ if !Symbol_Q(arr1[i]) {
+ return nil, errors.New("non-symbol bind value")
+ }
+ exp, e := EVAL(arr1[i+1], let_env)
+ if e != nil { return nil, e }
+ let_env.Set(arr1[i].(Symbol).Val, exp)
+ }
+ ast = a2
+ env = let_env
+ case "quote":
+ return a1, nil
+ case "quasiquote":
+ ast = quasiquote(a1)
+ case "defmacro!":
+ fn, e := EVAL(a2, env)
+ fn = fn.(MalFunc).SetMacro()
+ if e != nil { return nil, e }
+ return env.Set(a1.(Symbol).Val, fn), nil
+ case "macroexpand":
+ return macroexpand(a1, env)
+ case "try*":
+ var exc MalType
+ exp, e := EVAL(a1, env)
+ if e == nil {
+ return exp, nil
+ } else {
+ if a2 != nil && List_Q(a2) {
+ a2s, _ := GetSlice(a2)
+ if Symbol_Q(a2s[0]) && (a2s[0].(Symbol).Val == "catch*") {
+ switch e.(type) {
+ case MalError: exc = e.(MalError).Obj
+ default: exc = e.Error()
+ }
+ binds := NewList(a2s[1])
+ new_env, e := NewEnv(env, binds, NewList(exc))
+ if e != nil { return nil, e }
+ exp, e = EVAL(a2s[2], new_env)
+ if e == nil { return exp, nil }
+ }
+ }
+ return nil, e
+ }
+ case "do":
+ lst := ast.(List).Val
+ _, e := eval_ast(List{lst[1:len(lst)-1],nil}, env)
+ if e != nil { return nil, e }
+ if len(lst) == 1 { return nil, nil }
+ ast = lst[len(lst)-1]
+ case "if":
+ cond, e := EVAL(a1, env)
+ if e != nil { return nil, e }
+ if cond == nil || cond == false {
+ if len(ast.(List).Val) >= 4 {
+ ast = ast.(List).Val[3]
+ } else {
+ return nil, nil
+ }
+ } else {
+ ast = a2
+ }
+ case "fn*":
+ fn := MalFunc{EVAL, a2, env, a1, false, NewEnv, nil}
+ return fn, nil
+ default:
+ el, e := eval_ast(ast, env)
+ if e != nil { return nil, e }
+ f := el.(List).Val[0]
+ if MalFunc_Q(f) {
+ fn := f.(MalFunc)
+ ast = fn.Exp
+ env, e = NewEnv(fn.Env, fn.Params, List{el.(List).Val[1:],nil})
+ if e != nil { return nil, e }
+ } else {
+ fn, ok := f.(Func)
+ if !ok { return nil, errors.New("attempt to call non-function") }
+ return fn.Fn(el.(List).Val[1:])
+ }
+ }
+
+ } // TCO loop
+}
+
+// print
+func PRINT(exp MalType) (string, error) {
+ return printer.Pr_str(exp, true), nil
+}
+
+
+var repl_env, _ = NewEnv(nil, nil, nil)
+
+// repl
+func rep(str string) (MalType, error) {
+ var exp MalType
+ var res string
+ var e error
+ if exp, e = READ(str); e != nil { return nil, e }
+ if exp, e = EVAL(exp, repl_env); e != nil { return nil, e }
+ if res, e = PRINT(exp); e != nil { return nil, e }
+ return res, nil
+}
+
+func main() {
+ // core.go: defined using go
+ for k, v := range core.NS {
+ repl_env.Set(k, Func{v.(func([]MalType)(MalType,error)),nil})
+ }
+ repl_env.Set("eval", Func{func(a []MalType) (MalType, error) {
+ return EVAL(a[0], repl_env) },nil})
+ repl_env.Set("*ARGV*", List{})
+
+ // core.mal: defined using the language itself
+ rep("(def! *host-language* \"go\")")
+ rep("(def! not (fn* (a) (if a false true)))")
+ rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))")
+ rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))")
+ 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 {
+ args := make([]MalType, 0, len(os.Args)-2)
+ for _,a := range os.Args[2:] {
+ args = append(args, a)
+ }
+ repl_env.Set("*ARGV*", List{args,nil})
+ if _,e := rep("(load-file \"" + os.Args[1] + "\")"); e != nil {
+ fmt.Printf("Error: %v\n", e)
+ os.Exit(1)
+ }
+ os.Exit(0)
+ }
+
+ // repl loop
+ rep("(println (str \"Mal [\" *host-language* \"]\"))")
+ for {
+ text, err := readline.Readline("user> ")
+ text = strings.TrimRight(text, "\n");
+ if (err != nil) {
+ return
+ }
+ var out MalType
+ var e error
+ if out, e = rep(text); e != nil {
+ if e.Error() == "<empty line>" { continue }
+ fmt.Printf("Error: %v\n", e)
+ continue
+ }
+ fmt.Printf("%v\n", out)
+ }
+}
diff --git a/java/Makefile b/java/Makefile
index 2e168c4..eff6e6f 100644
--- a/java/Makefile
+++ b/java/Makefile
@@ -5,7 +5,7 @@ TESTS =
SOURCES_BASE = src/main/java/mal/readline.java src/main/java/mal/types.java \
src/main/java/mal/reader.java src/main/java/mal/printer.java
SOURCES_LISP = src/main/java/mal/env.java src/main/java/mal/core.java \
- src/main/java/mal/stepA_more.java
+ src/main/java/mal/stepA_interop.java
SOURCES = $(SOURCES_BASE) $(SOURCES_LISP)
#.PHONY: stats tests $(TESTS)
diff --git a/java/src/main/java/mal/stepA_more.java b/java/src/main/java/mal/step9_try.java
index 7e869e6..c3b0e9f 100644
--- a/java/src/main/java/mal/stepA_more.java
+++ b/java/src/main/java/mal/step9_try.java
@@ -15,7 +15,7 @@ import mal.printer;
import mal.env.Env;
import mal.core;
-public class stepA_more {
+public class step9_try {
// read
public static MalVal READ(String str) throws MalThrowable {
return reader.read_str(str);
diff --git a/java/src/main/java/mal/stepA_interop.java b/java/src/main/java/mal/stepA_interop.java
new file mode 100644
index 0000000..75c0402
--- /dev/null
+++ b/java/src/main/java/mal/stepA_interop.java
@@ -0,0 +1,302 @@
+package mal;
+
+import java.io.IOException;
+
+import java.io.StringWriter;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Iterator;
+import mal.types.*;
+import mal.readline;
+import mal.reader;
+import mal.printer;
+import mal.env.Env;
+import mal.core;
+
+public class stepA_interop {
+ // read
+ public static MalVal READ(String str) throws MalThrowable {
+ return reader.read_str(str);
+ }
+
+ // eval
+ public static Boolean is_pair(MalVal x) {
+ return x instanceof MalList && ((MalList)x).size() > 0;
+ }
+
+ public static MalVal quasiquote(MalVal ast) {
+ if (!is_pair(ast)) {
+ return new MalList(new MalSymbol("quote"), ast);
+ } else {
+ MalVal a0 = ((MalList)ast).nth(0);
+ if ((a0 instanceof MalSymbol) &&
+ (((MalSymbol)a0).getName() == "unquote")) {
+ return ((MalList)ast).nth(1);
+ } else if (is_pair(a0)) {
+ MalVal a00 = ((MalList)a0).nth(0);
+ if ((a00 instanceof MalSymbol) &&
+ (((MalSymbol)a00).getName() == "splice-unquote")) {
+ return new MalList(new MalSymbol("concat"),
+ ((MalList)a0).nth(1),
+ quasiquote(((MalList)ast).rest()));
+ }
+ }
+ return new MalList(new MalSymbol("cons"),
+ quasiquote(a0),
+ quasiquote(((MalList)ast).rest()));
+ }
+ }
+
+ public static Boolean is_macro_call(MalVal ast, Env env)
+ throws MalThrowable {
+ if (ast instanceof MalList) {
+ MalVal a0 = ((MalList)ast).nth(0);
+ if (a0 instanceof MalSymbol &&
+ env.find(((MalSymbol)a0).getName()) != null) {
+ MalVal mac = env.get(((MalSymbol)a0).getName());
+ if (mac instanceof MalFunction &&
+ ((MalFunction)mac).isMacro()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public static MalVal macroexpand(MalVal ast, Env env)
+ throws MalThrowable {
+ while (is_macro_call(ast, env)) {
+ MalSymbol a0 = (MalSymbol)((MalList)ast).nth(0);
+ MalFunction mac = (MalFunction) env.get(a0.getName());
+ ast = mac.apply(((MalList)ast).rest());
+ }
+ return ast;
+ }
+
+ public static MalVal eval_ast(MalVal ast, Env env) throws MalThrowable {
+ if (ast instanceof MalSymbol) {
+ MalSymbol sym = (MalSymbol)ast;
+ return env.get(sym.getName());
+ } else if (ast instanceof MalList) {
+ MalList old_lst = (MalList)ast;
+ MalList new_lst = ast.list_Q() ? new MalList()
+ : (MalList)new MalVector();
+ for (MalVal mv : (List<MalVal>)old_lst.value) {
+ new_lst.conj_BANG(EVAL(mv, env));
+ }
+ return new_lst;
+ } else if (ast instanceof MalHashMap) {
+ MalHashMap new_hm = new MalHashMap();
+ Iterator it = ((MalHashMap)ast).value.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry entry = (Map.Entry)it.next();
+ new_hm.value.put(entry.getKey(), EVAL((MalVal)entry.getValue(), env));
+ }
+ return new_hm;
+ } else {
+ return ast;
+ }
+ }
+
+ public static MalVal EVAL(MalVal orig_ast, Env env) throws MalThrowable {
+ MalVal a0, a1,a2, a3, res;
+ MalList el;
+
+ while (true) {
+
+ //System.out.println("EVAL: " + printer._pr_str(orig_ast, true));
+ if (!orig_ast.list_Q()) {
+ return eval_ast(orig_ast, env);
+ }
+
+ // apply list
+ MalVal expanded = macroexpand(orig_ast, env);
+ if (!expanded.list_Q()) { return expanded; }
+ MalList ast = (MalList) expanded;
+ if (ast.size() == 0) { return ast; }
+ a0 = ast.nth(0);
+ String a0sym = a0 instanceof MalSymbol ? ((MalSymbol)a0).getName()
+ : "__<*fn*>__";
+ switch (a0sym) {
+ case "def!":
+ a1 = ast.nth(1);
+ a2 = ast.nth(2);
+ res = EVAL(a2, env);
+ env.set(((MalSymbol)a1).getName(), res);
+ return res;
+ case "let*":
+ a1 = ast.nth(1);
+ a2 = ast.nth(2);
+ MalSymbol key;
+ MalVal val;
+ Env let_env = new Env(env);
+ for(int i=0; i<((MalList)a1).size(); i+=2) {
+ key = (MalSymbol)((MalList)a1).nth(i);
+ val = ((MalList)a1).nth(i+1);
+ let_env.set(key.getName(), EVAL(val, let_env));
+ }
+ orig_ast = a2;
+ env = let_env;
+ break;
+ case "quote":
+ return ast.nth(1);
+ case "quasiquote":
+ orig_ast = quasiquote(ast.nth(1));
+ break;
+ case "defmacro!":
+ a1 = ast.nth(1);
+ a2 = ast.nth(2);
+ res = EVAL(a2, env);
+ ((MalFunction)res).setMacro();
+ env.set(((MalSymbol)a1).getName(), res);
+ return res;
+ case "macroexpand":
+ a1 = ast.nth(1);
+ return macroexpand(a1, env);
+ case "try*":
+ try {
+ return EVAL(ast.nth(1), env);
+ } catch (Throwable t) {
+ if (ast.size() > 2) {
+ MalVal exc;
+ a2 = ast.nth(2);
+ MalVal a20 = ((MalList)a2).nth(0);
+ if (((MalSymbol)a20).getName().equals("catch*")) {
+ if (t instanceof MalException) {
+ exc = ((MalException)t).getValue();
+ } else {
+ StringWriter sw = new StringWriter();
+ t.printStackTrace(new PrintWriter(sw));
+ String tstr = sw.toString();
+ exc = new MalString(t.getMessage() + ": " + tstr);
+ }
+ return EVAL(((MalList)a2).nth(2),
+ new Env(env, ((MalList)a2).slice(1,2),
+ new MalList(exc)));
+ }
+ }
+ throw t;
+ }
+ case "do":
+ eval_ast(ast.slice(1, ast.size()-1), env);
+ orig_ast = ast.nth(ast.size()-1);
+ break;
+ case "if":
+ a1 = ast.nth(1);
+ MalVal cond = EVAL(a1, env);
+ if (cond == types.Nil || cond == types.False) {
+ // eval false slot form
+ if (ast.size() > 3) {
+ orig_ast = ast.nth(3);
+ } else {
+ return types.Nil;
+ }
+ } else {
+ // eval true slot form
+ orig_ast = ast.nth(2);
+ }
+ break;
+ case "fn*":
+ final MalList a1f = (MalList)ast.nth(1);
+ final MalVal a2f = ast.nth(2);
+ final Env cur_env = env;
+ return new MalFunction (a2f, (mal.env.Env)env, a1f) {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return EVAL(a2f, new Env(cur_env, a1f, args));
+ }
+ };
+ default:
+ el = (MalList)eval_ast(ast, env);
+ MalFunction f = (MalFunction)el.nth(0);
+ MalVal fnast = f.getAst();
+ if (fnast != null) {
+ orig_ast = fnast;
+ env = f.genEnv(el.slice(1));
+ } else {
+ return f.apply(el.rest());
+ }
+ }
+
+ }
+ }
+
+ // print
+ public static String PRINT(MalVal exp) {
+ return printer._pr_str(exp, true);
+ }
+
+ // repl
+ public static MalVal RE(Env env, String str) throws MalThrowable {
+ return EVAL(READ(str), env);
+ }
+
+ public static void main(String[] args) throws MalThrowable {
+ String prompt = "user> ";
+
+ final Env repl_env = new Env(null);
+
+ // core.java: defined using Java
+ for (String key : core.ns.keySet()) {
+ repl_env.set(key, core.ns.get(key));
+ }
+ repl_env.set("eval", new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return EVAL(args.nth(0), repl_env);
+ }
+ });
+ MalList _argv = new MalList();
+ for (Integer i=1; i < args.length; i++) {
+ _argv.conj_BANG(new MalString(args[i]));
+ }
+ repl_env.set("*ARGV*", _argv);
+
+
+ // core.mal: defined using the language itself
+ RE(repl_env, "(def! *host-language* \"java\")");
+ RE(repl_env, "(def! not (fn* (a) (if a false true)))");
+ RE(repl_env, "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))");
+ RE(repl_env, "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))");
+ 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")) {
+ readline.mode = readline.Mode.JAVA;
+ fileIdx = 1;
+ }
+ if (args.length > fileIdx) {
+ RE(repl_env, "(load-file \"" + args[fileIdx] + "\")");
+ return;
+ }
+
+ // repl loop
+ RE(repl_env, "(println (str \"Mal [\" *host-language* \"]\"))");
+ while (true) {
+ String line;
+ try {
+ line = readline.readline(prompt);
+ if (line == null) { continue; }
+ } catch (readline.EOFException e) {
+ break;
+ } catch (IOException e) {
+ System.out.println("IOException: " + e.getMessage());
+ break;
+ }
+ try {
+ System.out.println(PRINT(RE(repl_env, line)));
+ } catch (MalContinue e) {
+ continue;
+ } catch (MalException e) {
+ System.out.println("Error: " + printer._pr_str(e.getValue(), false));
+ continue;
+ } catch (MalThrowable t) {
+ System.out.println("Error: " + t.getMessage());
+ continue;
+ } catch (Throwable t) {
+ System.out.println("Uncaught " + t + ": " + t.getMessage());
+ continue;
+ }
+ }
+ }
+}
diff --git a/js/Makefile b/js/Makefile
index 454e481..09ed5a4 100644
--- a/js/Makefile
+++ b/js/Makefile
@@ -2,7 +2,7 @@
TESTS = tests/types.js tests/reader.js
SOURCES_BASE = node_readline.js types.js reader.js printer.js
-SOURCES_LISP = env.js core.js stepA_more.js
+SOURCES_LISP = env.js core.js stepA_interop.js
SOURCES = $(SOURCES_BASE) $(SOURCES_LISP)
WEB_SOURCES = $(SOURCES:node_readline.js=jq_readline.js)
diff --git a/js/step9_interop.js b/js/step9_try.js
index 89d2ca4..ff02f72 100644
--- a/js/step9_interop.js
+++ b/js/step9_try.js
@@ -104,12 +104,17 @@ function _EVAL(ast, env) {
return env.set(a1, func);
case 'macroexpand':
return macroexpand(a1, env);
- case "js*":
- return eval(a1.toString());
- case ".":
- var el = eval_ast(ast.slice(2), env),
- f = eval(a1.toString());
- return f.apply(f, el);
+ case "try*":
+ try {
+ return EVAL(a1, env);
+ } catch (exc) {
+ if (a2 && a2[0].value === "catch*") {
+ if (exc instanceof Error) { exc = exc.message; }
+ return EVAL(a2[2], new Env(env, [a2[1]], [exc]));
+ } else {
+ throw exc;
+ }
+ }
case "do":
eval_ast(ast.slice(1, -1), env);
ast = ast[ast.length-1];
@@ -157,6 +162,7 @@ repl_env.set('eval', function(ast) { return EVAL(ast, repl_env); });
repl_env.set('*ARGV*', []);
// core.mal: defined using the language itself
+rep("(def! *host-language* \"javascript\")")
rep("(def! not (fn* (a) (if a false true)))");
rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))");
rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))");
@@ -171,6 +177,7 @@ if (typeof process !== 'undefined' && process.argv.length > 2) {
// repl loop
if (typeof require !== 'undefined' && require.main === module) {
// Synchronous node.js commandline mode
+ rep("(println (str \"Mal [\" *host-language* \"]\"))");
while (true) {
var line = readline.readline("user> ");
if (line === null) { break; }
diff --git a/js/stepA_more.js b/js/stepA_interop.js
index 0955b7f..0955b7f 100644
--- a/js/stepA_more.js
+++ b/js/stepA_interop.js
diff --git a/js/tests/step9_interop.mal b/js/tests/stepA_interop.mal
index f785292..f785292 100644
--- a/js/tests/step9_interop.mal
+++ b/js/tests/stepA_interop.mal
diff --git a/make/Makefile b/make/Makefile
index 52a7a7d..bd19020 100644
--- a/make/Makefile
+++ b/make/Makefile
@@ -1,8 +1,8 @@
-TESTS = tests/types.mk tests/reader.mk tests/step9_interop.mk
+TESTS = tests/types.mk tests/reader.mk tests/stepA_interop.mk
SOURCES_BASE = util.mk readline.mk gmsl.mk types.mk reader.mk printer.mk
-SOURCES_LISP = env.mk core.mk stepA_more.mk
+SOURCES_LISP = env.mk core.mk stepA_interop.mk
SOURCES = $(SOURCES_BASE) $(SOURCES_LISP)
mal.mk: $(SOURCES)
diff --git a/make/step9_interop.mk b/make/step9_try.mk
index 984740a..4c5b8c1 100644
--- a/make/step9_interop.mk
+++ b/make/step9_try.mk
@@ -99,10 +99,21 @@ $(if $(__ERROR),,\
$(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),)))),\
$(if $(call _EQ,macroexpand,$($(a0)_value)),\
$(call MACROEXPAND,$(call _nth,$(1),1),$(2)),\
- $(if $(call _EQ,make*,$($(a0)_value)),\
+ $(if $(call _EQ,try*,$($(a0)_value)),\
$(foreach a1,$(call _nth,$(1),1),\
- $(and $(EVAL_DEBUG),$(info make*: $$(eval __result := $(call str_decode,$(value $(a1)_value)))))\
- $(eval __result := $(call str_decode,$(value $(a1)_value)))$(call _string,$(__result))),\
+ $(foreach res,$(call EVAL,$(a1),$(2)),\
+ $(if $(__ERROR),\
+ $(foreach a2,$(call _nth,$(1),2),\
+ $(foreach a20,$(call _nth,$(a2),0),\
+ $(if $(call _EQ,catch*,$($(a20)_value)),\
+ $(foreach a21,$(call _nth,$(a2),1),\
+ $(foreach a22,$(call _nth,$(a2),2),\
+ $(foreach binds,$(call _list,$(a21)),\
+ $(foreach catch_env,$(call ENV,$(2),$(binds),$(__ERROR)),\
+ $(eval __ERROR :=)\
+ $(call EVAL,$(a22),$(catch_env)))))),\
+ $(res)))),\
+ $(res)))),\
$(if $(call _EQ,do,$($(a0)_value)),\
$(call slast,$(call EVAL_AST,$(call srest,$(1)),$(2))),\
$(if $(call _EQ,if,$($(a0)_value)),\
@@ -154,6 +165,7 @@ _argv := $(call _list)
REPL_ENV := $(call ENV_SET,$(REPL_ENV),*ARGV*,$(_argv))
# 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))))))) ))
@@ -167,7 +179,9 @@ $(if $(MAKECMDGOALS),\
$(eval INTERACTIVE :=),)
# repl loop
-$(if $(strip $(INTERACTIVE)),$(call REPL))
+$(if $(strip $(INTERACTIVE)),\
+ $(call do,$(call REP, (println (str "Mal [" *host-language* "]")) )) \
+ $(call REPL))
.PHONY: none $(MAKECMDGOALS)
none $(MAKECMDGOALS):
diff --git a/make/stepA_more.mk b/make/stepA_interop.mk
index 050366c..050366c 100644
--- a/make/stepA_more.mk
+++ b/make/stepA_interop.mk
diff --git a/make/tests/step9_interop.mal b/make/tests/stepA_interop.mal
index 9b1a2f9..9b1a2f9 100644
--- a/make/tests/step9_interop.mal
+++ b/make/tests/stepA_interop.mal
diff --git a/mal/Makefile b/mal/Makefile
index 7e45ad4..1990b6b 100644
--- a/mal/Makefile
+++ b/mal/Makefile
@@ -2,7 +2,7 @@
TESTS =
SOURCES_BASE =
-SOURCES_LISP = env.mal core.mal stepA_more.mal
+SOURCES_LISP = env.mal core.mal stepA_interop.mal
SOURCES = $(SOURCES_BASE) $(SOURCES_LISP)
#.PHONY: stats tests $(TESTS)
diff --git a/mal/stepA_more.mal b/mal/step9_try.mal
index 3e2a258..3e2a258 100644
--- a/mal/stepA_more.mal
+++ b/mal/step9_try.mal
diff --git a/mal/stepA_interop.mal b/mal/stepA_interop.mal
new file mode 100644
index 0000000..3e2a258
--- /dev/null
+++ b/mal/stepA_interop.mal
@@ -0,0 +1,181 @@
+(load-file "../mal/env.mal")
+(load-file "../mal/core.mal")
+
+;; read
+(def! READ (fn* [strng]
+ (read-string strng)))
+
+
+;; eval
+(def! is-pair (fn* [x]
+ (if (sequential? x)
+ (if (> (count x) 0)
+ true))))
+
+(def! QUASIQUOTE (fn* [ast]
+ (cond
+ (not (is-pair ast))
+ (list 'quote ast)
+
+ (= 'unquote (first ast))
+ (nth ast 1)
+
+ (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))))))
+
+(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)))))))))
+
+(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)))
+
+(def! eval-ast (fn* [ast env] (do
+ ;;(do (prn "eval-ast" ast "/" (keys env)) )
+ (cond
+ (symbol? ast) (env-get env ast)
+
+ (list? ast) (map (fn* [exp] (EVAL exp env)) ast)
+
+ (vector? ast) (apply vector (map (fn* [exp] (EVAL exp env)) ast))
+
+ (map? ast) (apply hash-map
+ (apply concat
+ (map (fn* [k] [k (EVAL (get ast k) env)])
+ (keys ast))))
+
+ "else" ast))))
+
+(def! LET (fn* [env args]
+ (if (> (count args) 0)
+ (do
+ (env-set env (nth args 0) (EVAL (nth args 1) env))
+ (LET env (rest (rest args)))))))
+
+(def! EVAL (fn* [ast env] (do
+ ;;(do (prn "EVAL" ast "/" (keys @env)) )
+ (if (not (list? ast))
+ (eval-ast ast env)
+
+ ;; apply list
+ (let* [ast (MACROEXPAND ast env)]
+ (if (not (list? ast))
+ ast
+
+ (let* [a0 (first ast)]
+ (cond
+ (= '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)))
+
+ (= 'quote a0)
+ (nth ast 1)
+
+ (= 'quasiquote a0)
+ (let* [a1 (nth ast 1)]
+ (EVAL (QUASIQUOTE a1) 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)
+ (let* [a1 (nth ast 1)]
+ (MACROEXPAND a1 env))
+
+ (= 'try* a0)
+ (if (= 'catch* (nth (nth ast 2) 0))
+ (try*
+ (EVAL (nth ast 1) env)
+ (catch* exc
+ (EVAL (nth (nth ast 2) 2)
+ (new-env env
+ [(nth (nth ast 2)1)]
+ [exc]))))
+ (EVAL (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)))
+
+ (= '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))))))))))
+
+
+;; print
+(def! PRINT (fn* [exp] (pr-str exp)))
+
+;; repl
+(def! repl-env (new-env))
+(def! rep (fn* [strng]
+ (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)
+(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
+(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 "(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! -main (fn* [& args]
+ (if (> (count args) 0)
+ (rep (str "(load-file \"" (first args) "\")"))
+ (do
+ (rep "(println (str \"Mal [\" *host-language* \"]\"))")
+ (repl-loop)))))
+(apply -main *ARGV*)
diff --git a/perl/Makefile b/perl/Makefile
index 772bba7..d8e95a0 100644
--- a/perl/Makefile
+++ b/perl/Makefile
@@ -2,7 +2,7 @@ TESTS =
SOURCES_BASE = readline.pm types.pm reader.pm printer.pm \
interop.pm
-SOURCES_LISP = env.pm core.pm stepA_more.pl
+SOURCES_LISP = env.pm core.pm stepA_interop.pl
SOURCES = $(SOURCES_BASE) $(SOURCES_LISP)
#all: mal.pl
diff --git a/perl/interop.pm b/perl/interop.pm
index 44657ec..ffa379f 100644
--- a/perl/interop.pm
+++ b/perl/interop.pm
@@ -1,6 +1,7 @@
package interop;
use strict;
use warnings FATAL => qw(all);
+no if $] >= 5.018, warnings => "experimental::smartmatch";
use feature qw(switch);
use Exporter 'import';
our @EXPORT_OK = qw( pl_to_mal );
diff --git a/perl/printer.pm b/perl/printer.pm
index e31bed5..9ce6707 100644
--- a/perl/printer.pm
+++ b/perl/printer.pm
@@ -1,6 +1,7 @@
package printer;
use strict;
use warnings FATAL => qw(all);
+no if $] >= 5.018, warnings => "experimental::smartmatch";
use feature qw(switch);
use Exporter 'import';
our @EXPORT_OK = qw( _pr_str );
diff --git a/perl/reader.pm b/perl/reader.pm
index e49d5a1..cd4c565 100644
--- a/perl/reader.pm
+++ b/perl/reader.pm
@@ -2,6 +2,7 @@ package reader;
use feature qw(switch);
use strict;
use warnings FATAL => qw(all);
+no if $] >= 5.018, warnings => "experimental::smartmatch";
use Exporter 'import';
our @EXPORT_OK = qw( read_str );
diff --git a/perl/step1_read_print.pl b/perl/step1_read_print.pl
index 14675b9..8288336 100644
--- a/perl/step1_read_print.pl
+++ b/perl/step1_read_print.pl
@@ -1,5 +1,6 @@
use strict;
use warnings FATAL => qw(all);
+no if $] >= 5.018, warnings => "experimental::smartmatch";
use File::Basename;
use lib dirname (__FILE__);
use readline qw(mal_readline);
diff --git a/perl/step2_eval.pl b/perl/step2_eval.pl
index 0cbd697..c3759a5 100644
--- a/perl/step2_eval.pl
+++ b/perl/step2_eval.pl
@@ -1,5 +1,6 @@
use strict;
use warnings FATAL => qw(all);
+no if $] >= 5.018, warnings => "experimental::smartmatch";
use File::Basename;
use lib dirname (__FILE__);
use readline qw(mal_readline);
diff --git a/perl/step3_env.pl b/perl/step3_env.pl
index 9a7efa4..f63443d 100644
--- a/perl/step3_env.pl
+++ b/perl/step3_env.pl
@@ -1,5 +1,6 @@
use strict;
use warnings FATAL => qw(all);
+no if $] >= 5.018, warnings => "experimental::smartmatch";
use File::Basename;
use lib dirname (__FILE__);
use readline qw(mal_readline);
diff --git a/perl/step4_if_fn_do.pl b/perl/step4_if_fn_do.pl
index 4a4a8d5..abf0c67 100644
--- a/perl/step4_if_fn_do.pl
+++ b/perl/step4_if_fn_do.pl
@@ -1,5 +1,6 @@
use strict;
use warnings FATAL => qw(all);
+no if $] >= 5.018, warnings => "experimental::smartmatch";
use File::Basename;
use lib dirname (__FILE__);
use readline qw(mal_readline);
diff --git a/perl/step5_tco.pl b/perl/step5_tco.pl
index a225d79..60dc13a 100644
--- a/perl/step5_tco.pl
+++ b/perl/step5_tco.pl
@@ -1,5 +1,6 @@
use strict;
use warnings FATAL => qw(all);
+no if $] >= 5.018, warnings => "experimental::smartmatch";
use File::Basename;
use lib dirname (__FILE__);
use readline qw(mal_readline);
diff --git a/perl/step6_file.pl b/perl/step6_file.pl
index 86ac3f3..a95197a 100644
--- a/perl/step6_file.pl
+++ b/perl/step6_file.pl
@@ -1,5 +1,6 @@
use strict;
use warnings FATAL => qw(all);
+no if $] >= 5.018, warnings => "experimental::smartmatch";
use File::Basename;
use lib dirname (__FILE__);
use readline qw(mal_readline);
diff --git a/perl/step7_quote.pl b/perl/step7_quote.pl
index 2e08c1a..5ce9199 100644
--- a/perl/step7_quote.pl
+++ b/perl/step7_quote.pl
@@ -1,5 +1,6 @@
use strict;
use warnings FATAL => qw(all);
+no if $] >= 5.018, warnings => "experimental::smartmatch";
use File::Basename;
use lib dirname (__FILE__);
use readline qw(mal_readline);
diff --git a/perl/step8_macros.pl b/perl/step8_macros.pl
index d723651..d95e032 100644
--- a/perl/step8_macros.pl
+++ b/perl/step8_macros.pl
@@ -1,5 +1,6 @@
use strict;
use warnings FATAL => qw(all);
+no if $] >= 5.018, warnings => "experimental::smartmatch";
use File::Basename;
use lib dirname (__FILE__);
use readline qw(mal_readline);
diff --git a/perl/step9_interop.pl b/perl/step9_try.pl
index 4f53b6a..5862ef1 100644
--- a/perl/step9_interop.pl
+++ b/perl/step9_try.pl
@@ -1,5 +1,6 @@
use strict;
use warnings FATAL => qw(all);
+no if $] >= 5.018, warnings => "experimental::smartmatch";
use File::Basename;
use lib dirname (__FILE__);
use readline qw(mal_readline);
@@ -137,8 +138,32 @@ sub EVAL {
when (/^macroexpand$/) {
return macroexpand($a1, $env);
}
- when (/^pl\*$/) {
- return pl_to_mal(eval(${$a1}));
+ when (/^try\*$/) {
+ do {
+ local $@;
+ my $ret;
+ eval {
+ use autodie; # always "throw" errors
+ $ret = EVAL($a1, $env);
+ 1;
+ } or do {
+ my $err = $@;
+ if ($a2 && ${$a2->nth(0)} eq "catch\*") {
+ my $exc;
+ if (ref $err) {
+ $exc = $err;
+ } else {
+ $exc = String->new(substr $err, 0, -1);
+ }
+ return EVAL($a2->nth(2), Env->new($env,
+ List->new([$a2->nth(1)]),
+ List->new([$exc])));
+ } else {
+ die $err;
+ }
+ };
+ return $ret;
+ };
}
when (/^do$/) {
eval_ast($ast->slice(1, $#{$ast->{val}}-1), $env);
@@ -193,13 +218,18 @@ my @_argv = map {String->new($_)} @ARGV[1..$#ARGV];
$repl_env->set('*ARGV*', List->new(\@_argv));
# core.mal: defined using the language itself
+REP("(def! *host-language* \"javascript\")");
REP("(def! not (fn* (a) (if a false true)))");
REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))");
+REP("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))");
+REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))");
+
if (scalar(@ARGV) > 0) {
REP("(load-file \"" . $ARGV[0] . "\")");
exit 0;
}
+REP("(println (str \"Mal [\" *host-language* \"]\"))");
while (1) {
my $line = mal_readline("user> ");
if (! defined $line) { last; }
diff --git a/perl/stepA_more.pl b/perl/stepA_interop.pl
index 3703c47..0605d57 100644
--- a/perl/stepA_more.pl
+++ b/perl/stepA_interop.pl
@@ -1,5 +1,6 @@
use strict;
use warnings FATAL => qw(all);
+no if $] >= 5.018, warnings => "experimental::smartmatch";
use File::Basename;
use lib dirname (__FILE__);
use readline qw(mal_readline);
diff --git a/perl/tests/step9_interop.mal b/perl/tests/stepA_interop.mal
index 1335be4..1335be4 100644
--- a/perl/tests/step9_interop.mal
+++ b/perl/tests/stepA_interop.mal
diff --git a/perl/types.pm b/perl/types.pm
index e551e11..356f8c6 100644
--- a/perl/types.pm
+++ b/perl/types.pm
@@ -1,6 +1,7 @@
package types;
use strict;
use warnings FATAL => qw(all);
+no if $] >= 5.018, warnings => "experimental::smartmatch";
use feature qw(switch);
use Exporter 'import';
our @EXPORT_OK = qw(_sequential_Q _equal_Q _clone
diff --git a/php/Makefile b/php/Makefile
index 9b91421..d9fd2d4 100644
--- a/php/Makefile
+++ b/php/Makefile
@@ -2,7 +2,7 @@
TESTS =
SOURCES_BASE = readline.php types.php reader.php printer.php
-SOURCES_LISP = env.php core.php stepA_more.php
+SOURCES_LISP = env.php core.php stepA_interop.php
SOURCES = $(SOURCES_BASE) $(SOURCES_LISP)
.PHONY: stats tests $(TESTS)
diff --git a/php/step9_interop.php b/php/step9_try.php
index d4a59c7..9343dea 100644
--- a/php/step9_interop.php
+++ b/php/step9_try.php
@@ -108,8 +108,24 @@ function MAL_EVAL($ast, $env) {
return $env->set($ast[1]->value, $func);
case "macroexpand":
return macroexpand($ast[1], $env);
- case "php*":
- return eval($ast[1]);
+ case "try*":
+ $a1 = $ast[1];
+ $a2 = $ast[2];
+ if ($a2[0]->value === "catch*") {
+ try {
+ return MAL_EVAL($a1, $env);
+ } catch (Error $e) {
+ $catch_env = new Env($env, array($a2[1]),
+ array($e->obj));
+ return MAL_EVAL($a2[2], $catch_env);
+ } catch (Exception $e) {
+ $catch_env = new Env($env, array($a2[1]),
+ array($e->getMessage()));
+ return MAL_EVAL($a2[2], $catch_env);
+ }
+ } else {
+ return MAL_EVAL($a1, $env);
+ }
case "do":
eval_ast($ast->slice(1, -1), $env);
$ast = $ast[count($ast)-1];
@@ -168,6 +184,7 @@ for ($i=2; $i < count($argv); $i++) {
$repl_env->set('*ARGV*', $_argv);
// core.mal: defined using the language itself
+rep("(def! *host-language* \"php\")");
rep("(def! not (fn* (a) (if a false true)))");
rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))");
rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))");
@@ -179,6 +196,7 @@ if (count($argv) > 1) {
}
// repl loop
+rep("(println (str \"Mal [\" *host-language* \"]\"))");
do {
try {
$line = mal_readline("user> ");
diff --git a/php/stepA_more.php b/php/stepA_interop.php
index f38656f..f38656f 100644
--- a/php/stepA_more.php
+++ b/php/stepA_interop.php
diff --git a/php/tests/stepA_interop.mal b/php/tests/stepA_interop.mal
new file mode 100644
index 0000000..15f8a94
--- /dev/null
+++ b/php/tests/stepA_interop.mal
@@ -0,0 +1,25 @@
+;; Testing basic php interop
+
+(php* "return 7;")
+;=>7
+
+(php* "return '7';")
+;=>"7"
+
+(php* "return array(7,8,9);")
+;=>(7 8 9)
+
+(php* "return array(\"abc\" => 789);")
+;=>{"abc" 789}
+
+(php* "print \"hello\n\";")
+; hello
+;=>nil
+
+(php* "global $foo; $foo=8;")
+(php* "global $foo; return $foo;")
+;=>8
+
+(php* "global $f; $f = function($v) { return 1+$v; };")
+(php* "global $f; return array_map($f, array(1,2,3));")
+;=>(2 3 4)
diff --git a/ps/Makefile b/ps/Makefile
index 43b5b70..26056a6 100644
--- a/ps/Makefile
+++ b/ps/Makefile
@@ -2,7 +2,7 @@
TESTS =
SOURCES_BASE = types.ps reader.ps printer.ps
-SOURCES_LISP = env.ps core.ps stepA_more.ps
+SOURCES_LISP = env.ps core.ps stepA_interop.ps
SOURCES = $(SOURCES_BASE) $(SOURCES_LISP)
.PHONY: stats tests $(TESTS)
diff --git a/ps/interop.ps b/ps/interop.ps
new file mode 100644
index 0000000..fb3b88d
--- /dev/null
+++ b/ps/interop.ps
@@ -0,0 +1,21 @@
+% ps_val -> ps2mal -> mal_val
+/ps2mal {
+ % convert a PS value to a Mal value (recursively)
+ [ exch
+ { %forall returned values
+ dup ==
+ dup type /arraytype eq {
+ (here1\n) print
+ _list_from_array
+ }{ dup type /dicttype eq {
+ (here2\n) print
+ _hash_map_from_dict
+ }{
+ (here3\n) print
+ % no-op
+ } ifelse } ifelse
+ } forall
+ ]
+ (here4\n) print
+} def
+
diff --git a/ps/step9_interop.ps b/ps/step9_try.ps
index 7f90d20..9c5c240 100644
--- a/ps/step9_interop.ps
+++ b/ps/step9_try.ps
@@ -144,20 +144,6 @@ end } def
env exch a1 exch env_set % def! it
}{ /macroexpand a0 eq { %if defmacro!
ast 1 _nth env macroexpand
- }{ /ps* a0 eq { %if ps*
- count /stackcnt exch def
- ast 1 _nth
- {
- token not { exit } if
- exch
- } loop
- exec
- count stackcnt gt { % if new operands on stack
- % return an list of new operands
- count stackcnt sub array astore
- }{
- null % return nil
- } ifelse
}{ /do a0 eq { %if do
ast _count 2 gt { %if ast has more than 2 elements
ast 1 ast _count 2 sub _slice env eval_ast pop
@@ -165,6 +151,42 @@ end } def
ast ast _count 1 sub _nth % last ast becomes new ast
env
/loop? true def % loop
+ }{ /try* a0 eq { %if try*
+ { %try
+ countdictstack /dictcnt exch def
+ count /stackcnt exch def
+ ast 1 _nth env EVAL
+ } stopped { %catch
+ % clean up the dictionary stack
+ 1 1 countdictstack dictcnt sub { %foreach added dict
+ %(popping dict\n) print
+ pop end % pop idx and pop dict
+ %(new ast: ) print ast true _pr_str print (\n) print
+ } for
+ % clean up the operand stack
+ count 1 exch 1 exch stackcnt sub { %foreach added operand
+ %(op stack: ) print pstack
+ pop pop % pop idx and operand
+ %(popped op stack\n) print pstack
+ } for
+ % get error data and reset $error dict
+ /errdata get_error_data def
+ $error /newerror false put
+ $error /errorinfo null put
+
+ ast _count 3 lt { %if no third (catch*) form
+ errdata throw
+ } if
+ ast 2 _nth 0 _nth (catch*) eq not { %if third form not catch*
+ (No catch* in throw form) _throw
+ } if
+ ast 2 _nth 2 _nth
+ env
+ ast 2 _nth 1 _nth 1 _list
+ errdata 1 _list
+ env_new
+ EVAL
+ } if
}{ /if a0 eq { %if if
/a1 ast 1 _nth def
/cond a1 env EVAL def
@@ -222,6 +244,7 @@ core_ns { _function _ref } forall
(*ARGV*) [ ] _list_from_array _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
@@ -238,6 +261,7 @@ userdict /ARGUMENTS known { %if command line arguments
} if
% repl loop
+(\(println \(str "Mal [" *host-language* "]"\)\)) RE pop
{ %loop
(user> ) _readline
not { exit } if % exit if EOF
diff --git a/ps/stepA_more.ps b/ps/stepA_interop.ps
index a24ee28..a24ee28 100644
--- a/ps/stepA_more.ps
+++ b/ps/stepA_interop.ps
diff --git a/python/Makefile b/python/Makefile
index b461db3..6e51430 100644
--- a/python/Makefile
+++ b/python/Makefile
@@ -3,7 +3,7 @@ TESTS =
SOURCES_BASE = mal_readline.py mal_types.py reader.py printer.py
-SOURCES_LISP = env.py core.py stepA_more.py
+SOURCES_LISP = env.py core.py stepA_interop.py
SOURCES = $(SOURCES_BASE) $(SOURCES_LISP)
diff --git a/python/step9_interop.py b/python/step9_try.py
index 7cacf1f..65da08c 100644
--- a/python/step9_interop.py
+++ b/python/step9_try.py
@@ -97,12 +97,17 @@ def EVAL(ast, env):
else:
exec(compile(ast[1], '', 'single') in globals())
return None
- elif "py*" == a0:
- return eval(ast[1])
- elif "." == a0:
- el = eval_ast(ast[2:], env)
- f = eval(ast[1])
- return f(*el)
+ elif "try*" == a0:
+ a1, a2 = ast[1], ast[2]
+ if a2[0] == "catch*":
+ try:
+ return EVAL(a1, env);
+ except Exception as exc:
+ exc = exc.args[0]
+ catch_env = Env(env, [a2[1]], [exc])
+ return EVAL(a2[2], catch_env)
+ else:
+ return EVAL(a1, env);
elif "do" == a0:
eval_ast(ast[1:-1], env)
ast = ast[-1]
@@ -143,6 +148,7 @@ repl_env.set('eval', lambda ast: EVAL(ast, repl_env))
repl_env.set('*ARGV*', types._list(*sys.argv[2:]))
# core.mal: defined using the language itself
+REP("(def! *host-language* \"python\")")
REP("(def! not (fn* (a) (if a false true)))")
REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))")
REP("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))")
@@ -153,6 +159,7 @@ if len(sys.argv) >= 2:
sys.exit(0)
# repl loop
+REP("(println (str \"Mal [\" *host-language* \"]\"))")
while True:
try:
line = mal_readline.readline("user> ")
diff --git a/python/stepA_more.py b/python/stepA_interop.py
index 723f0ed..723f0ed 100644
--- a/python/stepA_more.py
+++ b/python/stepA_interop.py
diff --git a/ruby/Makefile b/ruby/Makefile
index 71ab92c..f9792f5 100644
--- a/ruby/Makefile
+++ b/ruby/Makefile
@@ -1,7 +1,7 @@
TESTS =
SOURCES_BASE = mal_readline.rb types.rb reader.rb printer.rb
-SOURCES_LISP = env.rb core.rb stepA_more.rb
+SOURCES_LISP = env.rb core.rb stepA_interop.rb
SOURCES = $(SOURCES_BASE) $(SOURCES_LISP)
#all: mal.rb
diff --git a/ruby/stepA_more.rb b/ruby/step9_try.rb
index 6123293..6123293 100644
--- a/ruby/stepA_more.rb
+++ b/ruby/step9_try.rb
diff --git a/ruby/step9_interop.rb b/ruby/stepA_interop.rb
index 6d2cbe2..6d2cbe2 100644
--- a/ruby/step9_interop.rb
+++ b/ruby/stepA_interop.rb
diff --git a/ruby/tests/stepA_interop.mal b/ruby/tests/stepA_interop.mal
new file mode 100644
index 0000000..2d7efb8
--- /dev/null
+++ b/ruby/tests/stepA_interop.mal
@@ -0,0 +1,27 @@
+;; Testing basic ruby interop
+
+(rb* "7")
+;=>7
+
+(rb* "'7'")
+;=>"7"
+
+(rb* "[7,8,9]")
+;=>(7 8 9)
+
+(rb* "{\"abc\" => 789}")
+;=>{"abc" 789}
+
+(rb* "print 'hello\n'")
+; hello
+;=>nil
+
+(rb* "$foo=8;")
+(rb* "$foo")
+;=>8
+
+(rb* "['a','b','c'].map{|x| 'X'+x+'Y'}.join(' ')")
+;=>"XaY XbY XcY"
+
+(rb* "[1,2,3].map{|x| 1+x}")
+;=>(2 3 4)
diff --git a/tests/step1_read_print.mal b/tests/step1_read_print.mal
index 2b7c269..43e7931 100644
--- a/tests/step1_read_print.mal
+++ b/tests/step1_read_print.mal
@@ -51,6 +51,10 @@ abc-def
;=>(+ 1 (+ 2 3))
( + 1 (+ 2 3 ) )
;=>(+ 1 (+ 2 3))
+(* 1 2)
+;=>(* 1 2)
+(** 1 2)
+;=>(** 1 2)
;; Testing read of vectors
[+ 1 2]
diff --git a/tests/stepA_more.mal b/tests/step9_try.mal
index 7b7dac5..7b7dac5 100644
--- a/tests/stepA_more.mal
+++ b/tests/step9_try.mal