diff options
| author | Joel Martin <github@martintribe.org> | 2014-03-24 16:32:24 -0500 |
|---|---|---|
| committer | Joel Martin <github@martintribe.org> | 2014-03-24 16:32:24 -0500 |
| commit | 3169070063b2cb877200117ebb384269d73bcb93 (patch) | |
| tree | 23de3db1ea5c37afd21a45b6ed7771f56a08c0c4 /java | |
| download | mal-3169070063b2cb877200117ebb384269d73bcb93.tar.gz mal-3169070063b2cb877200117ebb384269d73bcb93.zip | |
Current state of mal for Clojure West lighting talk.
Diffstat (limited to 'java')
| -rw-r--r-- | java/Makefile | 19 | ||||
| -rw-r--r-- | java/pom.xml | 81 | ||||
| -rw-r--r-- | java/src/main/java/mal/reader.java | 147 | ||||
| -rw-r--r-- | java/src/main/java/mal/readline.java | 101 | ||||
| -rw-r--r-- | java/src/main/java/mal/step0_repl.java | 48 | ||||
| -rw-r--r-- | java/src/main/java/mal/step1_read_print.java | 60 | ||||
| -rw-r--r-- | java/src/main/java/mal/step2_eval.java | 140 | ||||
| -rw-r--r-- | java/src/main/java/mal/step3_env.java | 137 | ||||
| -rw-r--r-- | java/src/main/java/mal/step4_if_fn_do.java | 163 | ||||
| -rw-r--r-- | java/src/main/java/mal/step5_tco.java | 174 | ||||
| -rw-r--r-- | java/src/main/java/mal/step6_file.java | 216 | ||||
| -rw-r--r-- | java/src/main/java/mal/step7_quote.java | 247 | ||||
| -rw-r--r-- | java/src/main/java/mal/step8_macros.java | 285 | ||||
| -rw-r--r-- | java/src/main/java/mal/stepA_more.java | 333 | ||||
| -rw-r--r-- | java/src/main/java/mal/types.java | 882 |
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(); +} |
