aboutsummaryrefslogtreecommitdiff
path: root/java
diff options
context:
space:
mode:
authorJoel Martin <github@martintribe.org>2014-03-24 16:32:24 -0500
committerJoel Martin <github@martintribe.org>2014-03-24 16:32:24 -0500
commit3169070063b2cb877200117ebb384269d73bcb93 (patch)
tree23de3db1ea5c37afd21a45b6ed7771f56a08c0c4 /java
downloadmal-3169070063b2cb877200117ebb384269d73bcb93.tar.gz
mal-3169070063b2cb877200117ebb384269d73bcb93.zip
Current state of mal for Clojure West lighting talk.
Diffstat (limited to 'java')
-rw-r--r--java/Makefile19
-rw-r--r--java/pom.xml81
-rw-r--r--java/src/main/java/mal/reader.java147
-rw-r--r--java/src/main/java/mal/readline.java101
-rw-r--r--java/src/main/java/mal/step0_repl.java48
-rw-r--r--java/src/main/java/mal/step1_read_print.java60
-rw-r--r--java/src/main/java/mal/step2_eval.java140
-rw-r--r--java/src/main/java/mal/step3_env.java137
-rw-r--r--java/src/main/java/mal/step4_if_fn_do.java163
-rw-r--r--java/src/main/java/mal/step5_tco.java174
-rw-r--r--java/src/main/java/mal/step6_file.java216
-rw-r--r--java/src/main/java/mal/step7_quote.java247
-rw-r--r--java/src/main/java/mal/step8_macros.java285
-rw-r--r--java/src/main/java/mal/stepA_more.java333
-rw-r--r--java/src/main/java/mal/types.java882
15 files changed, 3033 insertions, 0 deletions
diff --git a/java/Makefile b/java/Makefile
new file mode 100644
index 0000000..ccbd6c5
--- /dev/null
+++ b/java/Makefile
@@ -0,0 +1,19 @@
+
+TESTS =
+
+
+SOURCES = src/main/java/mal/types.java src/main/java/mal/readline.java \
+ src/main/java/mal/reader.java src/main/java/mal/Env.java \
+ src/main/java/mal/step5_tco.java
+
+#.PHONY: stats tests $(TESTS)
+.PHONY: stats
+
+stats: $(SOURCES)
+ @wc $^
+
+#tests: $(TESTS)
+#
+#$(TESTS):
+# @echo "Running $@"; \
+# python $@ || exit 1; \
diff --git a/java/pom.xml b/java/pom.xml
new file mode 100644
index 0000000..2f0a4fd
--- /dev/null
+++ b/java/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.martintribe</groupId>
+ <artifactId>mal</artifactId>
+ <packaging>jar</packaging>
+ <version>0.0.1</version>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>16.0.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.3</version>
+ </dependency>
+ <dependency>
+ <groupId>net.java.dev.jna</groupId>
+ <artifactId>jna</artifactId>
+ <version>4.0.0</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.7</source>
+ <target>1.7</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>1.2.1</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>java</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <!--
+ <mainClass>mal.stepA_more</mainClass>
+ <arguments>
+ <argument>foo</argument>
+ <argument>bar</argument>
+ </arguments>
+ -->
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <version>1.7.1</version>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ <configuration>
+ <transformers>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+ <mainClass>mal.stepA_more</mainClass>
+ </transformer>
+ </transformers>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/java/src/main/java/mal/reader.java b/java/src/main/java/mal/reader.java
new file mode 100644
index 0000000..6bae506
--- /dev/null
+++ b/java/src/main/java/mal/reader.java
@@ -0,0 +1,147 @@
+package mal;
+
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.apache.commons.lang3.StringEscapeUtils;
+import mal.types.*;
+
+public class reader {
+ public static class ParseError extends MalThrowable {
+ public ParseError(String msg) {
+ super(msg);
+ }
+ }
+
+ public static class Reader {
+ ArrayList<String> tokens;
+ Integer position;
+ public Reader(ArrayList<String> t) {
+ tokens = t;
+ position = 0;
+ }
+
+ public String peek() {
+ if (position >= tokens.size()) {
+ return null;
+ } else {
+ return tokens.get(position);
+ }
+ }
+ public String next() {
+ return tokens.get(position++);
+ }
+ }
+
+ public static ArrayList<String> tokenize(String str) {
+ ArrayList<String> tokens = new ArrayList<String>();
+ Pattern pattern = Pattern.compile("[\\s ,]*(~@|[\\[\\]{}()'`~@]|\"(?:[\\\\].|[^\\\\\"])*\"|;.*|[^\\s \\[\\]{}()'\"`~@,;]*)");
+ Matcher matcher = pattern.matcher(str);
+ while (matcher.find()) {
+ String token = matcher.group(1);
+ if (token != null &&
+ !token.equals("") &&
+ !(token.charAt(0) == ';')) {
+ tokens.add(token);
+ }
+ }
+ return tokens;
+ }
+
+ public static MalVal read_atom(Reader rdr)
+ throws ParseError {
+ String token = rdr.next();
+ Pattern pattern = Pattern.compile("(^-?[0-9]+$)|(^-?[0-9][0-9.]*$)|(^nil$)|(^true$)|(^false$)|^\"(.*)\"$|(^[^\"]*$)");
+ Matcher matcher = pattern.matcher(token);
+ if (!matcher.find()) {
+ throw new ParseError("unrecognized token '" + token + "'");
+ }
+ if (matcher.group(1) != null) {
+ return new MalInteger(Integer.parseInt(matcher.group(1)));
+ } else if (matcher.group(3) != null) {
+ return types.Nil;
+ } else if (matcher.group(4) != null) {
+ return types.True;
+ } else if (matcher.group(5) != null) {
+ return types.False;
+ } else if (matcher.group(6) != null) {
+ return new MalString(StringEscapeUtils.unescapeJson(matcher.group(6)));
+ } else if (matcher.group(7) != null) {
+ return new MalSymbol(matcher.group(7));
+ } else {
+ throw new ParseError("unrecognized '" + matcher.group(0) + "'");
+ }
+ }
+
+ public static MalVal read_list(Reader rdr, MalList lst, char start, char end)
+ throws MalContinue, ParseError {
+ String token = rdr.next();
+ if (token.charAt(0) != start) {
+ throw new ParseError("expected '" + start + "'");
+ }
+
+ while ((token = rdr.peek()) != null && token.charAt(0) != end) {
+ lst.conj_BANG(read_form(rdr));
+ }
+
+ if (token == null) {
+ throw new ParseError("expected '" + end + "', got EOF");
+ }
+ rdr.next();
+
+ return lst;
+ }
+
+ public static MalVal read_hash_map(Reader rdr)
+ throws MalContinue, ParseError {
+ MalList lst = (MalList)read_list(rdr, new MalList(), '{', '}');
+ return new MalHashMap(lst);
+ }
+
+ public static MalVal read_form(Reader rdr)
+ throws MalContinue, ParseError {
+ String token = rdr.peek();
+ if (token == null) { throw new MalContinue(); }
+ MalVal form;
+
+ switch (token.charAt(0)) {
+ case '\'': rdr.next();
+ return new MalList(new MalSymbol("quote"),
+ read_form(rdr));
+ case '`': rdr.next();
+ return new MalList(new MalSymbol("quasiquote"),
+ read_form(rdr));
+ case '~':
+ if (token.equals("~")) {
+ rdr.next();
+ return new MalList(new MalSymbol("unquote"),
+ read_form(rdr));
+ } else {
+ rdr.next();
+ return new MalList(new MalSymbol("splice-unquote"),
+ read_form(rdr));
+ }
+ case '^': rdr.next();
+ MalVal meta = read_form(rdr);
+ return new MalList(new MalSymbol("with-meta"),
+ read_form(rdr),
+ meta);
+ case '@': rdr.next();
+ return new MalList(new MalSymbol("deref"),
+ read_form(rdr));
+ case '(': form = read_list(rdr, new MalList(), '(' , ')'); break;
+ case ')': throw new ParseError("unexpected ')'");
+ case '[': form = read_list(rdr, new MalVector(), '[' , ']'); break;
+ case ']': throw new ParseError("unexpected ']'");
+ case '{': form = read_hash_map(rdr); break;
+ case '}': throw new ParseError("unexpected '}'");
+ default: form = read_atom(rdr);
+ }
+ return form;
+ }
+
+ public static MalVal read_str(String str)
+ throws MalContinue, ParseError {
+ return read_form(new Reader(tokenize(str)));
+ }
+}
diff --git a/java/src/main/java/mal/readline.java b/java/src/main/java/mal/readline.java
new file mode 100644
index 0000000..1705f39
--- /dev/null
+++ b/java/src/main/java/mal/readline.java
@@ -0,0 +1,101 @@
+package mal;
+
+import java.io.IOException;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+
+import java.io.File;
+import com.google.common.io.Files;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+import com.sun.jna.Platform;
+
+class readline {
+ public enum Mode { JNA, JAVA }
+ static Mode mode = Mode.JNA;
+
+ static String HISTORY_FILE = "/home/joelm/.mal-history";
+ static Boolean historyLoaded = false;
+
+ public static class EOFException extends Exception {
+ }
+
+ public interface RLLibrary extends Library {
+ // Select a library to use.
+ // WARNING: GNU readline is GPL.
+
+ // GNU readline (GPL)
+ RLLibrary INSTANCE = (RLLibrary)
+ Native.loadLibrary("readline", RLLibrary.class);
+ // Libedit (BSD)
+// RLLibrary INSTANCE = (RLLibrary)
+// Native.loadLibrary("edit", RLLibrary.class);
+
+ String readline(String prompt);
+ void add_history(String line);
+ }
+
+ public static void loadHistory(String filename) {
+ File file = new File(filename);
+ try {
+ List<String> lines = Files.readLines(file,
+ StandardCharsets.UTF_8);
+ for (String line : lines) {
+ RLLibrary.INSTANCE.add_history(line);
+ }
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+
+ public static void appendHistory(String filename, String line) {
+ try {
+ BufferedWriter w;
+ w = new BufferedWriter(new FileWriter(filename, true));
+ w.append(line + "\n");
+ w.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+
+ public static String jna_readline(String prompt)
+ throws EOFException, IOException {
+ if (!historyLoaded) {
+ loadHistory(HISTORY_FILE);
+ }
+ String line = RLLibrary.INSTANCE.readline(prompt);
+ if (line == null) {
+ throw new EOFException();
+ }
+ RLLibrary.INSTANCE.add_history(line);
+ appendHistory(HISTORY_FILE, line);
+ return line;
+ }
+
+ // Just java readline (no history, or line editing)
+ public static String java_readline(String prompt)
+ throws EOFException, IOException {
+ System.out.print(prompt);
+ BufferedReader buffer=new BufferedReader(new InputStreamReader(System.in));
+ String line=buffer.readLine();
+ if (line == null) {
+ throw new EOFException();
+ }
+ return line;
+ }
+
+ public static String readline(String prompt)
+ throws EOFException, IOException {
+ if (mode == Mode.JNA) {
+ return jna_readline(prompt);
+ } else {
+ return java_readline(prompt);
+ }
+ }
+}
diff --git a/java/src/main/java/mal/step0_repl.java b/java/src/main/java/mal/step0_repl.java
new file mode 100644
index 0000000..9095aca
--- /dev/null
+++ b/java/src/main/java/mal/step0_repl.java
@@ -0,0 +1,48 @@
+package mal;
+
+import java.io.IOException;
+
+import mal.readline;
+
+public class step0_repl {
+ // read
+ public static String READ(String str) {
+ return str;
+ }
+
+ // eval
+ public static String EVAL(String ast, String env) {
+ return ast;
+ }
+
+ // print
+ public static String PRINT(String exp) {
+ return exp;
+ }
+
+ // REPL
+ public static String RE(String env, String str) {
+ return EVAL(READ(str), env);
+ }
+
+ public static void main(String[] args) {
+ String prompt = "user> ";
+
+ if (args[0].equals("--raw")) {
+ readline.mode = readline.Mode.JAVA;
+ }
+ 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;
+ }
+ System.out.println(PRINT(RE(null, line)));
+ }
+ }
+}
diff --git a/java/src/main/java/mal/step1_read_print.java b/java/src/main/java/mal/step1_read_print.java
new file mode 100644
index 0000000..447afc5
--- /dev/null
+++ b/java/src/main/java/mal/step1_read_print.java
@@ -0,0 +1,60 @@
+package mal;
+
+import java.io.IOException;
+
+import mal.types.*;
+import mal.readline;
+import mal.reader;
+
+public class step1_read_print {
+ // read
+ public static MalVal READ(String str) throws MalThrowable {
+ return reader.read_str(str);
+ }
+
+ // eval
+ public static MalVal EVAL(MalVal ast, String env) {
+ return ast;
+ }
+
+ // print
+ public static String PRINT(MalVal exp) {
+ return types._pr_str(exp, true);
+ }
+
+ // REPL
+ public static MalVal RE(String env, String str) throws MalThrowable {
+ return EVAL(READ(str), env);
+ }
+
+ public static void main(String[] args) throws MalThrowable {
+ String prompt = "user> ";
+
+ if (args[0].equals("--raw")) {
+ readline.mode = readline.Mode.JAVA;
+ }
+ 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(null, line)));
+ } catch (MalContinue e) {
+ continue;
+ } catch (MalError e) {
+ System.out.println("Error: " + e.getMessage());
+ continue;
+ } catch (reader.ParseError e) {
+ System.out.println(e.getMessage());
+ continue;
+ }
+ }
+ }
+}
diff --git a/java/src/main/java/mal/step2_eval.java b/java/src/main/java/mal/step2_eval.java
new file mode 100644
index 0000000..e1b30a9
--- /dev/null
+++ b/java/src/main/java/mal/step2_eval.java
@@ -0,0 +1,140 @@
+package mal;
+
+import java.io.IOException;
+
+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;
+
+public class step2_eval {
+ // read
+ public static MalVal READ(String str) throws MalThrowable {
+ return reader.read_str(str);
+ }
+
+ // eval
+ public static MalVal eval_ast(MalVal ast, HashMap env) throws MalThrowable {
+ if (ast instanceof MalSymbol) {
+ MalSymbol sym = (MalSymbol)ast;
+ return (MalVal)env.get(sym.getName());
+ } else if (ast instanceof MalList) {
+ MalList old_lst = (MalList)ast;
+ MalList new_lst = types._list_Q(ast) ? 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, HashMap env) throws MalThrowable {
+ MalVal a0;
+ //System.out.println("EVAL: " + types._pr_str(orig_ast, true));
+ if (!(types._list_Q(orig_ast))) {
+ return eval_ast(orig_ast, env);
+ }
+
+ // apply list
+ MalList ast = (MalList)orig_ast;
+ if (ast.size() == 0) { return ast; }
+ a0 = ast.nth(0);
+ if (!(a0 instanceof MalSymbol)) {
+ throw new MalError("attempt to apply on non-symbol '"
+ + types._pr_str(a0,true) + "'");
+ }
+ MalVal args = eval_ast(types._rest(ast), env);
+ MalSymbol fsym = (MalSymbol)a0;
+ ILambda f = (ILambda)env.get(fsym.getName());
+ return f.apply((MalList)args);
+ }
+
+ // print
+ public static String PRINT(MalVal exp) {
+ return types._pr_str(exp, true);
+ }
+
+ // REPL
+ public static MalVal RE(HashMap env, String str) throws MalThrowable {
+ return EVAL(READ(str), env);
+ }
+
+ static interface ILambda {
+ public MalVal apply(MalList args);
+ }
+ static class plus implements ILambda {
+ public MalVal apply(MalList args) {
+ return ((MalInteger)args.nth(0)).add(
+ ((MalInteger)args.nth(1)));
+ }
+ }
+ static class minus implements ILambda {
+ public MalVal apply(MalList args) {
+ return ((MalInteger)args.nth(0)).subtract(
+ ((MalInteger)args.nth(1)));
+ }
+ }
+ static class multiply implements ILambda {
+ public MalVal apply(MalList args) {
+ return ((MalInteger)args.nth(0)).multiply(
+ ((MalInteger)args.nth(1)));
+ }
+ }
+ static class divide implements ILambda {
+ public MalVal apply(MalList args) {
+ return ((MalInteger)args.nth(0)).divide(
+ ((MalInteger)args.nth(1)));
+ }
+ }
+
+ public static void main(String[] args) throws MalThrowable {
+ String prompt = "user> ";
+
+ HashMap repl_env = new HashMap();
+ repl_env.put("+", new plus());
+ repl_env.put("-", new minus());
+ repl_env.put("*", new multiply());
+ repl_env.put("/", new divide());
+
+ if (args[0].equals("--raw")) {
+ readline.mode = readline.Mode.JAVA;
+ }
+ 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 (MalError e) {
+ System.out.println("Error: " + e.getMessage());
+ continue;
+ } catch (reader.ParseError e) {
+ System.out.println(e.getMessage());
+ continue;
+ }
+ }
+ }
+}
diff --git a/java/src/main/java/mal/step3_env.java b/java/src/main/java/mal/step3_env.java
new file mode 100644
index 0000000..867dba1
--- /dev/null
+++ b/java/src/main/java/mal/step3_env.java
@@ -0,0 +1,137 @@
+package mal;
+
+import java.io.IOException;
+
+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;
+
+public class step3_env {
+ // read
+ public static MalVal READ(String str) throws MalThrowable {
+ return reader.read_str(str);
+ }
+
+ // eval
+ public static MalVal eval_ast(MalVal ast, Env env) throws MalThrowable {
+ if (ast instanceof MalSymbol) {
+ MalSymbol sym = (MalSymbol)ast;
+ return env.get(sym.getName());
+ } else if (ast instanceof MalList) {
+ MalList old_lst = (MalList)ast;
+ MalList new_lst = types._list_Q(ast) ? 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, res;
+ //System.out.println("EVAL: " + types._pr_str(orig_ast, true));
+ if (!(types._list_Q(orig_ast))) {
+ return eval_ast(orig_ast, env);
+ }
+
+ // apply list
+ MalList ast = (MalList)orig_ast;
+ if (ast.size() == 0) { return ast; }
+ a0 = ast.nth(0);
+ if (!(a0 instanceof MalSymbol)) {
+ throw new MalError("attempt to apply on non-symbol '"
+ + types._pr_str(a0,true) + "'");
+ }
+
+ switch (((MalSymbol)a0).getName()) {
+ 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));
+ }
+ return EVAL(a2, let_env);
+ default:
+ MalVal args = eval_ast(types._rest(ast), env);
+ MalSymbol fsym = (MalSymbol)a0;
+ ILambda f = (ILambda)env.get(fsym.getName());
+ return f.apply((MalList)args);
+ }
+ }
+
+ // print
+ public static String PRINT(MalVal exp) {
+ return types._pr_str(exp, true);
+ }
+
+ // REPL
+ public static MalVal RE(Env env, String str) throws MalThrowable {
+ return EVAL(READ(str), env);
+ }
+ public static Env _ref(Env env, String name, MalVal mv) {
+ return env.set(name, mv);
+ }
+
+ public static void main(String[] args) throws MalThrowable {
+ String prompt = "user> ";
+
+ Env repl_env = new Env(null);
+ _ref(repl_env, "+", types.add);
+ _ref(repl_env, "-", types.subtract);
+ _ref(repl_env, "*", types.multiply);
+ _ref(repl_env, "/", types.divide);
+
+ if (args[0].equals("--raw")) {
+ readline.mode = readline.Mode.JAVA;
+ }
+ 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 (MalError e) {
+ System.out.println("Error: " + e.getMessage());
+ continue;
+ } catch (reader.ParseError e) {
+ System.out.println(e.getMessage());
+ continue;
+ }
+ }
+ }
+}
diff --git a/java/src/main/java/mal/step4_if_fn_do.java b/java/src/main/java/mal/step4_if_fn_do.java
new file mode 100644
index 0000000..7501b50
--- /dev/null
+++ b/java/src/main/java/mal/step4_if_fn_do.java
@@ -0,0 +1,163 @@
+package mal;
+
+import java.io.IOException;
+
+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;
+
+public class step4_if_fn_do {
+ // read
+ public static MalVal READ(String str) throws MalThrowable {
+ return reader.read_str(str);
+ }
+
+ // eval
+ public static MalVal eval_ast(MalVal ast, Env env) throws MalThrowable {
+ if (ast instanceof MalSymbol) {
+ MalSymbol sym = (MalSymbol)ast;
+ return env.get(sym.getName());
+ } else if (ast instanceof MalList) {
+ MalList old_lst = (MalList)ast;
+ MalList new_lst = types._list_Q(ast) ? 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;
+ //System.out.println("EVAL: " + types._pr_str(orig_ast, true));
+ if (!(types._list_Q(orig_ast))) {
+ return eval_ast(orig_ast, env);
+ }
+
+ // apply list
+ MalList ast = (MalList)orig_ast;
+ 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));
+ }
+ return EVAL(a2, let_env);
+ case "do":
+ el = (MalList)eval_ast(types._rest(ast), env);
+ return el.nth(el.size()-1);
+ 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) {
+ a3 = ast.nth(3);
+ return EVAL(a3, env);
+ } else {
+ return types.Nil;
+ }
+ } else {
+ // eval true slot form
+ a2 = ast.nth(2);
+ return EVAL(a2, env);
+ }
+ case "fn*":
+ final MalList a1f = (MalList)ast.nth(1);
+ final MalVal a2f = ast.nth(2);
+ final Env cur_env = env;
+ return new MalFunction () {
+ 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);
+ return f.apply(types._rest(el));
+ }
+ }
+
+ // print
+ public static String PRINT(MalVal exp) {
+ return types._pr_str(exp, true);
+ }
+
+ // REPL
+ public static MalVal RE(Env env, String str) throws MalThrowable {
+ return EVAL(READ(str), env);
+ }
+ public static Env _ref(Env env, String name, MalVal mv) {
+ return env.set(name, mv);
+ }
+
+ public static void main(String[] args) throws MalThrowable {
+ String prompt = "user> ";
+
+ Env repl_env = new Env(null);
+ for (String key : types.types_ns.keySet()) {
+ _ref(repl_env, key, types.types_ns.get(key));
+ }
+
+ RE(repl_env, "(def! not (fn* (a) (if a false true)))");
+
+ if (args[0].equals("--raw")) {
+ readline.mode = readline.Mode.JAVA;
+ }
+ 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 (reader.ParseError e) {
+ System.out.println(e.getMessage());
+ continue;
+ } catch (MalThrowable t) {
+ System.out.println("Error: " + t.getMessage());
+ continue;
+ }
+ }
+ }
+}
diff --git a/java/src/main/java/mal/step5_tco.java b/java/src/main/java/mal/step5_tco.java
new file mode 100644
index 0000000..41b295b
--- /dev/null
+++ b/java/src/main/java/mal/step5_tco.java
@@ -0,0 +1,174 @@
+package mal;
+
+import java.io.IOException;
+
+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;
+
+public class step5_tco {
+ // read
+ public static MalVal READ(String str) throws MalThrowable {
+ return reader.read_str(str);
+ }
+
+ // eval
+ public static MalVal eval_ast(MalVal ast, Env env) throws MalThrowable {
+ if (ast instanceof MalSymbol) {
+ MalSymbol sym = (MalSymbol)ast;
+ return env.get(sym.getName());
+ } else if (ast instanceof MalList) {
+ MalList old_lst = (MalList)ast;
+ MalList new_lst = types._list_Q(ast) ? 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 a1,a2, a3, res;
+ MalList el;
+
+ while (true) {
+
+ //System.out.println("EVAL: " + types._pr_str(orig_ast, true));
+ if (!(types._list_Q(orig_ast))) {
+ return eval_ast(orig_ast, env);
+ }
+
+ // apply list
+ MalList ast = (MalList)orig_ast;
+ if (ast.size() == 0) { return ast; }
+ MalVal 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));
+ }
+ return EVAL(a2, let_env);
+ 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.types.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 = new Env(f.getEnv(), f.getParams(), el.slice(1));
+ } else {
+ return f.apply(types._rest(el));
+ }
+ }
+
+ }
+ }
+
+ // print
+ public static String PRINT(MalVal exp) {
+ return types._pr_str(exp, true);
+ }
+
+ // REPL
+ public static MalVal RE(Env env, String str) throws MalThrowable {
+ return EVAL(READ(str), env);
+ }
+ public static Env _ref(Env env, String name, MalVal mv) {
+ return env.set(name, mv);
+ }
+
+ public static void main(String[] args) throws MalThrowable {
+ String prompt = "user> ";
+
+ Env repl_env = new Env(null);
+ for (String key : types.types_ns.keySet()) {
+ _ref(repl_env, key, types.types_ns.get(key));
+ }
+
+ RE(repl_env, "(def! not (fn* (a) (if a false true)))");
+
+ if (args[0].equals("--raw")) {
+ readline.mode = readline.Mode.JAVA;
+ }
+ 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 (reader.ParseError e) {
+ System.out.println(e.getMessage());
+ continue;
+ } catch (MalThrowable t) {
+ System.out.println("Error: " + t.getMessage());
+ continue;
+ }
+ }
+ }
+}
diff --git a/java/src/main/java/mal/step6_file.java b/java/src/main/java/mal/step6_file.java
new file mode 100644
index 0000000..95bfd1c
--- /dev/null
+++ b/java/src/main/java/mal/step6_file.java
@@ -0,0 +1,216 @@
+package mal;
+
+import java.io.IOException;
+import java.io.FileNotFoundException;
+
+import java.util.Scanner;
+import java.io.File;
+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;
+
+public class step6_file {
+ // read
+ public static MalVal READ(String str) throws MalThrowable {
+ return reader.read_str(str);
+ }
+
+ // eval
+ public static MalVal eval_ast(MalVal ast, Env env) throws MalThrowable {
+ if (ast instanceof MalSymbol) {
+ MalSymbol sym = (MalSymbol)ast;
+ return env.get(sym.getName());
+ } else if (ast instanceof MalList) {
+ MalList old_lst = (MalList)ast;
+ MalList new_lst = types._list_Q(ast) ? 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 a1,a2, a3, res;
+ MalList el;
+
+ while (true) {
+
+ //System.out.println("EVAL: " + types._pr_str(orig_ast, true));
+ if (!(types._list_Q(orig_ast))) {
+ return eval_ast(orig_ast, env);
+ }
+
+ // apply list
+ MalList ast = (MalList)orig_ast;
+ if (ast.size() == 0) { return ast; }
+ MalVal 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));
+ }
+ return EVAL(a2, let_env);
+ 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.types.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 = new Env(f.getEnv(), f.getParams(), el.slice(1));
+ } else {
+ return f.apply(types._rest(el));
+ }
+ }
+
+ }
+ }
+
+ // print
+ public static String PRINT(MalVal exp) {
+ return types._pr_str(exp, true);
+ }
+
+ // REPL
+ public static MalVal RE(Env env, String str) throws MalThrowable {
+ return EVAL(READ(str), env);
+ }
+ public static Env _ref(Env env, String name, MalVal mv) {
+ return env.set(name, mv);
+ }
+ public static String slurp(String fname) throws MalThrowable {
+ try {
+ return new Scanner(new File(fname))
+ .useDelimiter("\\Z").next();
+ } catch (FileNotFoundException e) {
+ throw new MalError(e.getMessage());
+ }
+ }
+
+ public static void main(String[] args) throws MalThrowable {
+ String prompt = "user> ";
+
+ final Env repl_env = new Env(null);
+ for (String key : types.types_ns.keySet()) {
+ _ref(repl_env, key, types.types_ns.get(key));
+ }
+ _ref(repl_env, "read-string", new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return reader.read_str(((MalString)args.nth(0)).getValue());
+ }
+ });
+ _ref(repl_env, "eval", new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return EVAL(args.nth(0), repl_env);
+ }
+ });
+ _ref(repl_env, "slurp", new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ String fname = ((MalString)args.nth(0)).getValue();
+ return new MalString(slurp(fname));
+ }
+ });
+ _ref(repl_env, "slurp-do", new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ String fname = ((MalString)args.nth(0)).getValue();
+ return new MalString("(do " + slurp(fname) + ")");
+ }
+ });
+
+ RE(repl_env, "(def! not (fn* (a) (if a false true)))");
+ RE(repl_env, "(def! load-file (fn* (f) (eval (read-string (slurp-do f)))))");
+
+ Integer fileIdx = 0;
+ if (args.length > 0 && args[0].equals("--raw")) {
+ readline.mode = readline.Mode.JAVA;
+ fileIdx = 1;
+ }
+ if (args.length > fileIdx) {
+ for(Integer i=fileIdx; i<args.length; i++) {
+ RE(repl_env, "(load-file \"" + args[i] + "\")");
+ }
+ return;
+ }
+ 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 (reader.ParseError e) {
+ System.out.println(e.getMessage());
+ continue;
+ } catch (MalThrowable t) {
+ System.out.println("Error: " + t.getMessage());
+ continue;
+ }
+ }
+ }
+}
diff --git a/java/src/main/java/mal/step7_quote.java b/java/src/main/java/mal/step7_quote.java
new file mode 100644
index 0000000..49f395e
--- /dev/null
+++ b/java/src/main/java/mal/step7_quote.java
@@ -0,0 +1,247 @@
+package mal;
+
+import java.io.IOException;
+import java.io.FileNotFoundException;
+
+import java.util.Scanner;
+import java.io.File;
+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;
+
+public class step7_quote {
+ // 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(types._rest((MalList)ast)));
+ }
+ }
+ return new MalList(new MalSymbol("cons"),
+ quasiquote(a0),
+ quasiquote(types._rest((MalList)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 = types._list_Q(ast) ? 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 a1,a2, a3, res;
+ MalList el;
+
+ while (true) {
+
+ //System.out.println("EVAL: " + types._pr_str(orig_ast, true));
+ if (!(types._list_Q(orig_ast))) {
+ return eval_ast(orig_ast, env);
+ }
+
+ // apply list
+ MalList ast = (MalList)orig_ast;
+ if (ast.size() == 0) { return ast; }
+ MalVal 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));
+ }
+ return EVAL(a2, let_env);
+ case "quote":
+ return ast.nth(1);
+ case "quasiquote":
+ return EVAL(quasiquote(ast.nth(1)), env);
+ 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.types.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 = new Env(f.getEnv(), f.getParams(), el.slice(1));
+ } else {
+ return f.apply(types._rest(el));
+ }
+ }
+
+ }
+ }
+
+ // print
+ public static String PRINT(MalVal exp) {
+ return types._pr_str(exp, true);
+ }
+
+ // REPL
+ public static MalVal RE(Env env, String str) throws MalThrowable {
+ return EVAL(READ(str), env);
+ }
+ public static Env _ref(Env env, String name, MalVal mv) {
+ return env.set(name, mv);
+ }
+ public static String slurp(String fname) throws MalThrowable {
+ try {
+ return new Scanner(new File(fname))
+ .useDelimiter("\\Z").next();
+ } catch (FileNotFoundException e) {
+ throw new MalError(e.getMessage());
+ }
+ }
+
+ public static void main(String[] args) throws MalThrowable {
+ String prompt = "user> ";
+
+ final Env repl_env = new Env(null);
+ for (String key : types.types_ns.keySet()) {
+ _ref(repl_env, key, types.types_ns.get(key));
+ }
+ _ref(repl_env, "read-string", new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return reader.read_str(((MalString)args.nth(0)).getValue());
+ }
+ });
+ _ref(repl_env, "eval", new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return EVAL(args.nth(0), repl_env);
+ }
+ });
+ _ref(repl_env, "slurp", new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ String fname = ((MalString)args.nth(0)).getValue();
+ return new MalString(slurp(fname));
+ }
+ });
+ _ref(repl_env, "slurp-do", new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ String fname = ((MalString)args.nth(0)).getValue();
+ return new MalString("(do " + slurp(fname) + ")");
+ }
+ });
+
+ RE(repl_env, "(def! not (fn* (a) (if a false true)))");
+ RE(repl_env, "(def! load-file (fn* (f) (eval (read-string (slurp-do f)))))");
+
+ Integer fileIdx = 0;
+ if (args.length > 0 && args[0].equals("--raw")) {
+ readline.mode = readline.Mode.JAVA;
+ fileIdx = 1;
+ }
+ if (args.length > fileIdx) {
+ for(Integer i=fileIdx; i<args.length; i++) {
+ RE(repl_env, "(load-file \"" + args[i] + "\")");
+ }
+ return;
+ }
+ 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 (reader.ParseError e) {
+ System.out.println(e.getMessage());
+ continue;
+ } catch (MalThrowable t) {
+ System.out.println("Error: " + t.getMessage());
+ continue;
+ }
+ }
+ }
+}
diff --git a/java/src/main/java/mal/step8_macros.java b/java/src/main/java/mal/step8_macros.java
new file mode 100644
index 0000000..c632987
--- /dev/null
+++ b/java/src/main/java/mal/step8_macros.java
@@ -0,0 +1,285 @@
+package mal;
+
+import java.io.IOException;
+import java.io.FileNotFoundException;
+
+import java.util.Scanner;
+import java.io.File;
+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;
+
+public class step8_macros {
+ // 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(types._rest((MalList)ast)));
+ }
+ }
+ return new MalList(new MalSymbol("cons"),
+ quasiquote(a0),
+ quasiquote(types._rest((MalList)ast)));
+ }
+ }
+
+ 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(types._rest((MalList)ast));
+ }
+ 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 = types._list_Q(ast) ? 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 a1,a2, a3, res;
+ MalList el;
+
+ while (true) {
+
+ //System.out.println("EVAL: " + types._pr_str(orig_ast, true));
+ if (!(types._list_Q(orig_ast))) {
+ return eval_ast(orig_ast, env);
+ }
+
+ // apply list
+ MalVal expanded = macroexpand(orig_ast, env);
+ if (!types._list_Q(expanded)) { return expanded; }
+ MalList ast = (MalList) expanded;
+ if (ast.size() == 0) { return ast; }
+ MalVal 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));
+ }
+ return EVAL(a2, let_env);
+ case "quote":
+ return ast.nth(1);
+ case "quasiquote":
+ return EVAL(quasiquote(ast.nth(1)), env);
+ 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 "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.types.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 = new Env(f.getEnv(), f.getParams(), el.slice(1));
+ } else {
+ return f.apply(types._rest(el));
+ }
+ }
+
+ }
+ }
+
+ // print
+ public static String PRINT(MalVal exp) {
+ return types._pr_str(exp, true);
+ }
+
+ // REPL
+ public static MalVal RE(Env env, String str) throws MalThrowable {
+ return EVAL(READ(str), env);
+ }
+ public static Env _ref(Env env, String name, MalVal mv) {
+ return env.set(name, mv);
+ }
+ public static String slurp(String fname) throws MalThrowable {
+ try {
+ return new Scanner(new File(fname))
+ .useDelimiter("\\Z").next();
+ } catch (FileNotFoundException e) {
+ throw new MalError(e.getMessage());
+ }
+ }
+
+ public static void main(String[] args) throws MalThrowable {
+ String prompt = "user> ";
+
+ final Env repl_env = new Env(null);
+ for (String key : types.types_ns.keySet()) {
+ _ref(repl_env, key, types.types_ns.get(key));
+ }
+ _ref(repl_env, "read-string", new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return reader.read_str(((MalString)args.nth(0)).getValue());
+ }
+ });
+ _ref(repl_env, "eval", new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return EVAL(args.nth(0), repl_env);
+ }
+ });
+ _ref(repl_env, "slurp", new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ String fname = ((MalString)args.nth(0)).getValue();
+ return new MalString(slurp(fname));
+ }
+ });
+ _ref(repl_env, "slurp-do", new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ String fname = ((MalString)args.nth(0)).getValue();
+ return new MalString("(do " + slurp(fname) + ")");
+ }
+ });
+
+ RE(repl_env, "(def! not (fn* (a) (if a false true)))");
+ RE(repl_env, "(def! load-file (fn* (f) (eval (read-string (slurp-do f)))))");
+
+ Integer fileIdx = 0;
+ if (args.length > 0 && args[0].equals("--raw")) {
+ readline.mode = readline.Mode.JAVA;
+ fileIdx = 1;
+ }
+ if (args.length > fileIdx) {
+ for(Integer i=fileIdx; i<args.length; i++) {
+ RE(repl_env, "(load-file \"" + args[i] + "\")");
+ }
+ return;
+ }
+ 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 (reader.ParseError e) {
+ System.out.println(e.getMessage());
+ continue;
+ } catch (MalThrowable t) {
+ System.out.println("Error: " + t.getMessage());
+ continue;
+ }
+ }
+ }
+}
diff --git a/java/src/main/java/mal/stepA_more.java b/java/src/main/java/mal/stepA_more.java
new file mode 100644
index 0000000..ff09aff
--- /dev/null
+++ b/java/src/main/java/mal/stepA_more.java
@@ -0,0 +1,333 @@
+package mal;
+
+import java.io.IOException;
+import java.io.FileNotFoundException;
+
+import java.util.Scanner;
+import java.io.File;
+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;
+
+public class stepA_more {
+ // 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(types._rest((MalList)ast)));
+ }
+ }
+ return new MalList(new MalSymbol("cons"),
+ quasiquote(a0),
+ quasiquote(types._rest((MalList)ast)));
+ }
+ }
+
+ 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(types._rest((MalList)ast));
+ }
+ 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 = types._list_Q(ast) ? 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 a1,a2, a3, res;
+ MalList el;
+
+ while (true) {
+
+ //System.out.println("EVAL: " + types._pr_str(orig_ast, true));
+ if (!(types._list_Q(orig_ast))) {
+ return eval_ast(orig_ast, env);
+ }
+
+ // apply list
+ MalVal expanded = macroexpand(orig_ast, env);
+ if (!types._list_Q(expanded)) { return expanded; }
+ MalList ast = (MalList) expanded;
+ if (ast.size() == 0) { return ast; }
+ MalVal 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));
+ }
+ return EVAL(a2, let_env);
+ case "quote":
+ return ast.nth(1);
+ case "quasiquote":
+ return EVAL(quasiquote(ast.nth(1)), env);
+ 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.types.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 = new Env(f.getEnv(), f.getParams(), el.slice(1));
+ } else {
+ return f.apply(types._rest(el));
+ }
+ }
+
+ }
+ }
+
+ // print
+ public static String PRINT(MalVal exp) {
+ return types._pr_str(exp, true);
+ }
+
+ // REPL
+ public static MalVal RE(Env env, String str) throws MalThrowable {
+ return EVAL(READ(str), env);
+ }
+ public static Env _ref(Env env, String name, MalVal mv) {
+ return env.set(name, mv);
+ }
+ public static String slurp(String fname) throws MalThrowable {
+ try {
+ return new Scanner(new File(fname))
+ .useDelimiter("\\Z").next();
+ } catch (FileNotFoundException e) {
+ throw new MalError(e.getMessage());
+ }
+ }
+
+ public static void main(String[] args) throws MalThrowable {
+ String prompt = "user> ";
+
+ final Env repl_env = new Env(null);
+ for (String key : types.types_ns.keySet()) {
+ _ref(repl_env, key, types.types_ns.get(key));
+ }
+ _ref(repl_env, "readline", new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ String prompt = ((MalString)args.nth(0)).getValue();
+ try {
+ return new MalString(readline.readline(prompt));
+ } catch (IOException e) {
+ throw new MalException(new MalString(e.getMessage()));
+ } catch (readline.EOFException e) {
+ throw new MalException(new MalString(e.getMessage()));
+ }
+ }
+ });
+ _ref(repl_env, "read-string", new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ try {
+ return reader.read_str(((MalString)args.nth(0)).getValue());
+ } catch (MalContinue c) {
+ return types.Nil;
+ }
+ }
+ });
+ _ref(repl_env, "eval", new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return EVAL(args.nth(0), repl_env);
+ }
+ });
+ _ref(repl_env, "slurp", new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ String fname = ((MalString)args.nth(0)).getValue();
+ return new MalString(slurp(fname));
+ }
+ });
+ _ref(repl_env, "slurp-do", new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ String fname = ((MalString)args.nth(0)).getValue();
+ return new MalString("(do " + slurp(fname) + ")");
+ }
+ });
+
+ RE(repl_env, "(def! not (fn* (a) (if a false true)))");
+ 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))))))))");
+
+ RE(repl_env, "(def! load-file (fn* (f) (eval (read-string (slurp-do f)))))");
+
+ Integer fileIdx = 0;
+ if (args.length > 0 && args[0].equals("--raw")) {
+ readline.mode = readline.Mode.JAVA;
+ fileIdx = 1;
+ }
+ if (args.length > fileIdx) {
+ for(Integer i=fileIdx; i<args.length; i++) {
+ RE(repl_env, "(load-file \"" + args[i] + "\")");
+ }
+ return;
+ }
+ 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 (reader.ParseError e) {
+ System.out.println(e.getMessage());
+ continue;
+ } catch (MalException e) {
+ System.out.println("Error: " + types._pr_str(e.getValue(), false));
+ continue;
+ } catch (MalThrowable t) {
+ System.out.println("Error: " + t.getMessage());
+ continue;
+ }
+ }
+ }
+}
diff --git a/java/src/main/java/mal/types.java b/java/src/main/java/mal/types.java
new file mode 100644
index 0000000..1e9bb34
--- /dev/null
+++ b/java/src/main/java/mal/types.java
@@ -0,0 +1,882 @@
+package mal;
+
+import java.util.List;
+import java.util.ArrayList;
+import com.google.common.base.Joiner;
+import java.util.Set;
+import java.util.Map;
+import java.util.HashMap;
+import com.google.common.collect.ImmutableMap;
+import org.apache.commons.lang3.StringEscapeUtils;
+
+public class types {
+ //
+ // Exceptions/Errors
+ //
+ public static class MalThrowable extends Exception {
+ public MalThrowable() { }
+ public MalThrowable(String msg) { super(msg); }
+ }
+ public static class MalError extends MalThrowable {
+ public MalError(String msg) { super(msg); }
+ }
+ public static class MalContinue extends MalThrowable { }
+
+ // Thrown by throw function
+ public static class MalException extends MalThrowable {
+ MalVal value;
+ public MalException(MalVal value) {
+ this.value = value;
+ }
+ public MalException(String value) {
+ this.value = new MalString(value);
+ }
+ public MalVal getValue() { return value; }
+ }
+
+
+ //
+ // Mal boxed types
+ //
+ abstract public static class MalVal {
+ MalVal meta = Nil;
+ abstract public MalVal copy() throws MalThrowable;
+
+ // Default is just to call regular toString()
+ public String toString(Boolean print_readably) {
+ return this.toString();
+ }
+ public MalVal getMeta() { return meta; }
+ public void setMeta(MalVal m) { meta = m; }
+ }
+ public static class MalConstant extends MalVal {
+ String value;
+ public MalConstant(String name) { value = name; }
+ public MalConstant copy() throws MalThrowable { return this; }
+
+ public String toString() { return value; }
+ }
+ static MalConstant Nil = new MalConstant("nil");
+ static MalConstant True = new MalConstant("true");
+ static MalConstant False = new MalConstant("false");
+
+ public static class MalInteger extends MalVal {
+ Integer value;
+ public MalInteger(Integer v) { value = v; }
+ public MalInteger copy() throws MalThrowable { return this; }
+
+ public Integer getValue() { return value; }
+ @Override public String toString() {
+ return value.toString();
+ }
+ public MalInteger add(MalInteger other) {
+ return new MalInteger(value + other.getValue());
+ }
+ public MalInteger subtract(MalInteger other) {
+ return new MalInteger(value - other.getValue());
+ }
+ public MalInteger multiply(MalInteger other) {
+ return new MalInteger(value * other.getValue());
+ }
+ public MalInteger divide(MalInteger other) {
+ return new MalInteger(value / other.getValue());
+ }
+ public MalConstant lt(MalInteger other) {
+ return (value < other.getValue()) ? True : False;
+ }
+ public MalConstant lte(MalInteger other) {
+ return (value <= other.getValue()) ? True : False;
+ }
+ public MalConstant gt(MalInteger other) {
+ return (value > other.getValue()) ? True : False;
+ }
+ public MalConstant gte(MalInteger other) {
+ return (value >= other.getValue()) ? True : False;
+ }
+ }
+
+ public static class MalSymbol extends MalVal {
+ String value;
+ public MalSymbol(String v) { value = v; }
+ public MalSymbol copy() throws MalThrowable { return this; }
+
+ public String getName() { return value; }
+ @Override public String toString() {
+ return value;
+ }
+ }
+
+ public static class MalString extends MalVal {
+ String value;
+ public MalString(String v) { value = v; }
+ public MalString copy() throws MalThrowable { return this; }
+
+ public String getValue() { return value; }
+ @Override public String toString() {
+ return "\"" + value + "\"";
+ }
+ public String toString(Boolean print_readably) {
+ if (print_readably) {
+ return "\"" + StringEscapeUtils.escapeJson(value) + "\"";
+ } else {
+ return value;
+ }
+ }
+ }
+
+ public static class MalList extends MalVal {
+ String start = "(", end = ")";
+ List value;
+ public MalList(List val) {
+ value = val;
+ }
+ public MalList(MalVal... mvs) {
+ value = new ArrayList<MalVal>();
+ conj_BANG(mvs);
+ }
+ public MalList copy() throws MalThrowable {
+ MalList new_ml = new MalList();
+ new_ml.value.addAll(value);
+ new_ml.meta = meta;
+ return new_ml;
+ }
+
+ String _join(String delim, Boolean print_readably) {
+ ArrayList<String> strs = new ArrayList<String>();
+ for (MalVal mv : (List<MalVal>)value) {
+ strs.add(mv.toString(print_readably));
+ }
+ return Joiner.on(delim).join(strs);
+ }
+ @Override public String toString() {
+ return start + _join(" ", true) + end;
+ }
+ public String toString(Boolean print_readably) {
+ return start + _join(" ", print_readably) + end;
+ }
+
+ public MalList conj_BANG(MalVal... mvs) {
+ for (MalVal mv : mvs) {
+ value.add(mv);
+ }
+ return this;
+ }
+
+ public Integer size() {
+ return value.size();
+ }
+
+ public MalVal nth(Integer idx) {
+ return (MalVal)value.get(idx);
+ }
+
+ public MalList slice(Integer start, Integer end) {
+ return new MalList(value.subList(start, end));
+ }
+ public MalList slice(Integer start) {
+ return slice(start, value.size());
+ }
+ }
+
+ public static class MalVector extends MalList {
+ // Same implementation except for instantiation methods
+ public MalVector(List val) {
+ value = val;
+ start = "[";
+ end = "]";
+ }
+ public MalVector(MalVal... mvs) {
+ super(mvs);
+ start = "[";
+ end = "]";
+ }
+ public MalVector copy() throws MalThrowable {
+ MalVector new_mv = new MalVector();
+ new_mv.value.addAll(value);
+ new_mv.meta = meta;
+ return new_mv;
+ }
+
+ public MalVector slice(Integer start, Integer end) {
+ return new MalVector(value.subList(start, end));
+ }
+ }
+
+ public static class MalHashMap extends MalVal {
+ Map value;
+ public MalHashMap(Map val) {
+ value = val;
+ }
+ public MalHashMap(MalList lst) {
+ value = new HashMap<String, MalVal>();
+ assoc_BANG(lst);
+ }
+ public MalHashMap(MalVal... mvs) {
+ value = new HashMap<String, MalVal>();
+ assoc_BANG(mvs);
+ }
+ public MalHashMap copy() throws MalThrowable {
+ Map<String,MalVal> shallowCopy = new HashMap<String,MalVal>();
+ shallowCopy.putAll(value);
+ MalHashMap new_hm = new MalHashMap(shallowCopy);
+ new_hm.meta = meta;
+ return new_hm;
+ }
+
+ String _join(Boolean print_readably) {
+ ArrayList<String> strs = new ArrayList<String>();
+ for (Map.Entry<String, MalVal> entry :
+ ((Map<String,MalVal>)value).entrySet()) {
+ if (print_readably) {
+ strs.add("\"" + entry.getKey().toString() + "\"");
+ } else {
+ strs.add(entry.getKey().toString());
+ }
+ strs.add(entry.getValue().toString(print_readably));
+ }
+ return Joiner.on(" ").join(strs);
+ }
+ @Override public String toString() {
+ return "{" + _join(true) + "}";
+ }
+ public String toString(Boolean print_readably) {
+ return "{" + _join(print_readably) + "}";
+ }
+
+ public Set _entries() {
+ return value.entrySet();
+ }
+
+ public MalHashMap assoc_BANG(MalVal... mvs) {
+ for (Integer i=0; i<mvs.length; i+=2) {
+ value.put(((MalSymbol)mvs[i]).getName(),
+ mvs[i+1]);
+ }
+ return this;
+ }
+
+ public MalHashMap assoc_BANG(MalList lst) {
+ for (Integer i=0; i<lst.value.size(); i+=2) {
+ value.put(((MalString)lst.nth(i)).getValue(),
+ lst.nth(i+1));
+ }
+ return this;
+ }
+
+ public MalHashMap dissoc_BANG(MalList lst) {
+ for (Integer i=0; i<lst.value.size(); i++) {
+ value.remove(((MalString)lst.nth(i)).getValue());
+ }
+ return this;
+ }
+
+ public Integer size() {
+ return value.size();
+ }
+ }
+
+ public static class MalAtom extends MalVal {
+ MalVal value;
+ public MalAtom(MalVal value) { this.value = value; }
+ public MalAtom copy() throws MalThrowable { return new MalAtom(value); }
+ @Override public String toString() {
+ return "(atom " + _pr_str(value, true) + ")";
+ }
+ public String toString(Boolean print_readably) {
+ return "(atom " + _pr_str(value, print_readably) + ")";
+ }
+ }
+
+ public static interface ILambda {
+ public MalVal apply(MalList args) throws MalThrowable;
+ }
+
+ public static abstract class MalFunction extends MalVal
+ implements ILambda, java.lang.Cloneable {
+ public MalVal ast = null;
+ public Env env = null;
+ public MalList params = null;
+ public Boolean macro = false;
+ public MalFunction() { }
+ public MalFunction(MalVal ast, Env env, MalList params) {
+ this.ast = ast;
+ this.env = env;
+ this.params = params;
+ }
+ public MalFunction copy() throws MalThrowable {
+ try {
+ // WARNING: clone() is broken:
+ // http://www.artima.com/intv/bloch13.html
+ // However, this doesn't work:
+ // MalFunction new_mf = this.getClass().newInstance();
+ // So for now it's clone.
+ MalFunction new_mf = (MalFunction) this.clone();
+ new_mf.ast = ast;
+ new_mf.env = env;
+ new_mf.params = params;
+ new_mf.macro = macro;
+ return new_mf;
+ } catch (Throwable t) {
+ // not much we can do
+ t.printStackTrace();
+ throw new MalError("Could not copy MalFunction: " + this);
+ }
+ }
+
+ public MalVal getAst() { return ast; }
+ public Env getEnv() { return env; }
+ public MalList getParams() { return params; }
+ public Boolean isMacro() { return macro; }
+ public void setMacro() { macro = true; }
+ }
+
+
+ //
+ // General functions
+ //
+ public static String _pr_str(MalVal mv, Boolean print_readably) {
+ return mv.toString(print_readably);
+ }
+
+ public static String _pr_str_args(MalList args, String sep, Boolean print_readably) {
+ return args._join(sep, print_readably);
+ }
+
+ static MalFunction pr_str = new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return new MalString(_pr_str_args(args, " ", true));
+ }
+ };
+
+ static MalFunction str = new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return new MalString(_pr_str_args(args, "", false));
+ }
+ };
+
+ static MalFunction prn = new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ System.out.println(_pr_str_args(args, " ", true));
+ return Nil;
+ }
+ };
+
+ static MalFunction println = new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ System.out.println(_pr_str_args(args, " ", false));
+ return Nil;
+ }
+ };
+
+
+ static MalFunction meta = new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return args.nth(0).getMeta();
+ }
+ };
+
+ static MalFunction with_meta = new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ MalVal new_mv = ((MalVal)args.nth(0)).copy();
+ new_mv.setMeta(args.nth(1));
+ return new_mv;
+ }
+ };
+
+
+ public static Boolean _equal_Q(MalVal a, MalVal b) {
+ Class ota = a.getClass(), otb = b.getClass();
+ if (!((ota == otb) ||
+ (a instanceof MalList && b instanceof MalList))) {
+ return false;
+ } else {
+ if (a instanceof MalInteger) {
+ return ((MalInteger)a).getValue() ==
+ ((MalInteger)b).getValue();
+ } else if (a instanceof MalSymbol) {
+ return ((MalSymbol)a).getName().equals(
+ ((MalSymbol)b).getName());
+ } else if (a instanceof MalString) {
+ return ((MalString)a).getValue().equals(
+ ((MalString)b).getValue());
+ } else if (a instanceof MalList) {
+ if (((MalList)a).size() != ((MalList)b).size()) {
+ return false;
+ }
+ for (Integer i=0; i<((MalList)a).size(); i++) {
+ if (! _equal_Q(((MalList)a).nth(i),
+ ((MalList)b).nth(i))) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return a == b;
+ }
+ }
+ }
+
+ static MalFunction equal_Q = new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return _equal_Q(args.nth(0), args.nth(1)) ? True : False;
+ }
+ };
+
+
+ //
+ // Constants operations
+ //
+ static MalFunction symbol_Q = new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return args.nth(0) instanceof MalSymbol ? True : False;
+ }
+ };
+
+ static MalFunction nil_Q = new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return args.nth(0) == Nil ? True : False;
+ }
+ };
+
+ static MalFunction true_Q = new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return args.nth(0) == True ? True : False;
+ }
+ };
+
+ static MalFunction false_Q = new MalFunction() {
+ public MalVal apply(MalList args) throws MalThrowable {
+ return args.nth(0) == False ? True : False;
+ }
+ };
+
+
+ //
+ // Number operations
+ //
+ static MalFunction add = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return ((MalInteger)a.nth(0)).add((MalInteger)a.nth(1));
+ }
+ };
+ static MalFunction subtract = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return ((MalInteger)a.nth(0)).subtract((MalInteger)a.nth(1));
+ }
+ };
+ static MalFunction multiply = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return ((MalInteger)a.nth(0)).multiply((MalInteger)a.nth(1));
+ }
+ };
+ static MalFunction divide = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return ((MalInteger)a.nth(0)).divide((MalInteger)a.nth(1));
+ }
+ };
+
+ static MalFunction lt = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return ((MalInteger)a.nth(0)).lt((MalInteger)a.nth(1));
+ }
+ };
+ static MalFunction lte = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return ((MalInteger)a.nth(0)).lte((MalInteger)a.nth(1));
+ }
+ };
+ static MalFunction gt = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return ((MalInteger)a.nth(0)).gt((MalInteger)a.nth(1));
+ }
+ };
+ static MalFunction gte = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return ((MalInteger)a.nth(0)).gte((MalInteger)a.nth(1));
+ }
+ };
+
+ //
+ // Errors/Exceptions
+ //
+ static MalFunction mal_throw = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ throw new MalException(a.nth(0));
+ }
+ };
+
+ //
+ // List operations
+ //
+ static MalFunction new_list = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return new MalList(a.value);
+ }
+ };
+
+ static public Boolean _list_Q(MalVal mv) {
+ return mv.getClass().equals(MalList.class);
+ }
+ static MalFunction list_Q = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return _list_Q(a.nth(0)) ? True : False;
+ }
+ };
+
+ //
+ // Vector operations
+ //
+ static MalFunction new_vector = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return new MalVector(a.value);
+ }
+ };
+
+ static public Boolean _vector_Q(MalVal mv) {
+ return mv.getClass().equals(MalVector.class);
+ }
+ static MalFunction vector_Q = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return _vector_Q(a.nth(0)) ? True : False;
+ }
+ };
+
+ //
+ // Hash map operations
+ //
+ static MalFunction new_hash_map = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return new MalHashMap(a);
+ }
+ };
+
+ static MalFunction hash_map_Q = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return a.nth(0) instanceof MalHashMap ? True : False;
+ }
+ };
+
+ static MalFunction contains_Q = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ String key = ((MalString)a.nth(1)).getValue();
+ MalHashMap mhm = (MalHashMap)a.nth(0);
+ HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;
+ return hm.containsKey(key) ? True : False;
+ }
+ };
+
+ static MalFunction assoc = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ MalHashMap mhm = (MalHashMap)a.nth(0);
+ HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;
+ MalHashMap new_mhm = new MalHashMap((Map)hm.clone());
+ new_mhm.assoc_BANG((MalList)a.slice(1));
+ return new_mhm;
+ }
+ };
+
+ static MalFunction dissoc = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ MalHashMap mhm = (MalHashMap)a.nth(0);
+ HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;
+ MalHashMap new_mhm = new MalHashMap((Map)hm.clone());
+ new_mhm.dissoc_BANG((MalList)a.slice(1));
+ return new_mhm;
+ }
+ };
+
+ static MalFunction get = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ String key = ((MalString)a.nth(1)).getValue();
+ MalHashMap mhm = (MalHashMap)a.nth(0);
+ HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;
+ if (hm.containsKey(key)) {
+ return hm.get(key);
+ } else {
+ return Nil;
+ }
+ }
+ };
+
+ static MalFunction keys = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ MalHashMap mhm = (MalHashMap)a.nth(0);
+ HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;
+ MalList key_lst = new MalList();
+ for (String key : hm.keySet()) {
+ key_lst.conj_BANG(new MalString(key));
+ }
+ return key_lst;
+ }
+ };
+
+ static MalFunction vals = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ MalHashMap mhm = (MalHashMap)a.nth(0);
+ HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;
+ //return new ArrayList<MalVal>(((HashMap<String,MalVal>)hm).values());
+ MalList val_lst = new MalList();
+ for (MalVal val : hm.values()) {
+ val_lst.conj_BANG(val);
+ }
+ return val_lst;
+ }
+ };
+
+
+ //
+ // Atoms
+ //
+ static MalFunction new_atom = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return new MalAtom(a.nth(0));
+ }
+ };
+
+ static MalFunction atom_Q = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return a.nth(0) instanceof MalAtom ? True : False;
+ }
+ };
+
+ static MalFunction deref = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return ((MalAtom)a.nth(0)).value;
+ }
+ };
+
+ static MalFunction reset_BANG = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return ((MalAtom)a.nth(0)).value = a.nth(1);
+ }
+ };
+
+ static MalFunction swap_BANG = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ MalAtom atm = (MalAtom)a.nth(0);
+ MalFunction f = (MalFunction)a.nth(1);
+ MalList new_args = new MalList();
+ new_args.value.addAll(((MalList)a.slice(2)).value);
+ new_args.value.add(0, atm.value);
+ atm.value = f.apply(new_args);
+ return atm.value;
+ }
+ };
+
+
+
+
+ //
+ // Sequence operations
+ //
+ static MalFunction sequential_Q = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return a.nth(0) instanceof MalList ? True : False;
+ }
+ };
+
+ static MalFunction count = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ return new MalInteger(((MalList)a.nth(0)).size());
+ }
+ };
+
+ static MalFunction empty_Q = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ MalVal exp = a.nth(0);
+ if (exp == Nil || (exp instanceof MalList &&
+ ((MalList)exp).size() == 0)) {
+ return True;
+ } else {
+ return False;
+ }
+ }
+ };
+
+ static MalFunction cons = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ MalList lst = new MalList();
+ lst.value.addAll(((MalList)a.nth(1)).value);
+ lst.value.add(0, a.nth(0));
+ return (MalVal) lst;
+ }
+ };
+
+ static MalFunction concat = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ if (a.size() == 0) { return new MalList(); }
+ MalList lst = new MalList();
+ lst.value.addAll(((MalList)a.nth(0)).value);
+ for(Integer i=1; i<a.size(); i++) {
+ lst.value.addAll(((MalList)a.nth(i)).value);
+ }
+ return (MalVal) lst;
+ }
+ };
+
+ static MalFunction conj = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ MalList lst = new MalList();
+ lst.value.addAll(((MalList)a.nth(0)).value);
+ for(Integer i=1; i<a.size(); i++) {
+ lst.value.add(a.nth(i));
+ }
+ return (MalVal) lst;
+ }
+ };
+
+ static MalFunction first = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ MalList ml = ((MalList)a.nth(0));
+ return ml.size() > 0 ? ml.nth(0) : Nil;
+ }
+ };
+
+ static MalList _rest (MalList ml) {
+ if (ml.size() > 0) {
+ return new MalList(ml.value.subList(1, ml.value.size()));
+ } else {
+ return new MalList();
+ }
+ }
+
+ static MalFunction rest = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ MalList ml = ((MalList)a.nth(0));
+ return _rest(ml);
+ }
+ };
+
+ static MalFunction nth = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ Integer idx = ((MalInteger)a.nth(1)).getValue();
+ return ((MalList)a.nth(0)).nth(idx);
+ }
+ };
+
+ // General list related functions
+ static MalFunction apply = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ MalFunction f = (MalFunction)a.nth(0);
+ MalList args = a.slice(1,a.size()-1);
+ args.value.addAll( ((MalList)a.nth(a.size()-1)).value);
+ return f.apply(args);
+ }
+ };
+
+ static MalFunction map = new MalFunction() {
+ public MalVal apply(MalList a) throws MalThrowable {
+ MalFunction f = (MalFunction) a.nth(0);
+ MalList src_lst = (MalList) a.nth(1);
+ MalList new_lst = new MalList();
+ for(Integer i=0; i<src_lst.size(); i++) {
+ new_lst.value.add(
+ f.apply(new MalList(src_lst.nth(i))));
+ }
+ return new_lst;
+ }
+ };
+
+
+
+ //
+ // Env implementation
+ //
+ public static class Env {
+ Env outer = null;
+ HashMap<String,MalVal> data = new HashMap<String,MalVal>();
+
+ public Env(Env outer) {
+ this.outer = outer;
+ }
+ public Env(Env outer, MalList binds, MalList exprs) {
+ this.outer = outer;
+ for (Integer i=0; i<binds.size(); i++) {
+ String sym = ((MalSymbol)binds.nth(i)).getName();
+ if (sym.equals("&")) {
+ data.put(((MalSymbol)binds.nth(i+1)).getName(),
+ exprs.slice(i));
+ break;
+ } else {
+ data.put(sym, exprs.nth(i));
+ }
+ }
+ }
+
+ public Env find(String key) {
+ if (data.containsKey(key)) {
+ return this;
+ } else if (outer != null) {
+ return outer.find(key);
+ } else {
+ return null;
+ }
+ }
+
+ public MalVal get(String key) throws MalThrowable {
+ Env e = find(key);
+ if (e == null) {
+ throw new MalException("'" + key + "' not found");
+ } else {
+ return e.data.get(key);
+ }
+ }
+
+ public Env set(String key, MalVal value) {
+ data.put(key, value);
+ return this;
+ }
+ }
+
+ // types_ns is namespace of type functions
+ static Map<String, MalVal> types_ns = ImmutableMap.<String, MalVal>builder()
+ .put("pr-str", pr_str)
+ .put("str", str)
+ .put("prn", prn)
+ .put("println", println)
+ .put("meta", meta)
+ .put("with-meta", with_meta)
+ .put("=", equal_Q)
+ .put("symbol?", symbol_Q)
+ .put("nil?", nil_Q)
+ .put("true?", true_Q)
+ .put("false?", false_Q)
+ .put("<", lt)
+ .put("<=", lte)
+ .put(">", gt)
+ .put(">=", gte)
+ .put("+", add)
+ .put("-", subtract)
+ .put("*", multiply)
+ .put("/", divide)
+ .put("throw", mal_throw)
+ .put("list", new_list)
+ .put("list?", list_Q)
+ .put("vector", new_vector)
+ .put("vector?", vector_Q)
+ .put("hash-map", new_hash_map)
+ .put("map?", hash_map_Q)
+ .put("assoc", assoc)
+ .put("dissoc", dissoc)
+ .put("contains?", contains_Q)
+ .put("get", get)
+ .put("keys", keys)
+ .put("vals", vals)
+ .put("atom", new_atom)
+ .put("atom?", atom_Q)
+ .put("deref", deref)
+ .put("reset!", reset_BANG)
+ .put("swap!", swap_BANG)
+ .put("sequential?", sequential_Q)
+ .put("cons", cons)
+ .put("concat", concat)
+ .put("conj", conj)
+ .put("first", first)
+ .put("rest", rest)
+ .put("nth", nth)
+ .put("count", count)
+ .put("empty?", empty_Q)
+ .put("apply", apply)
+ .put("map", map)
+ .build();
+}