aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Martin <github@martintribe.org>2014-04-07 21:33:29 -0500
committerJoel Martin <github@martintribe.org>2014-04-07 21:33:29 -0500
commit96115d4f2e8cf7335a7d6bb8b62e0f0bbfac0ab5 (patch)
tree9d0e73e3bcaf87dd2c55c5d874f4581a8fbbbcfd
parentafdf531eba459a7a7b6505b037dbe48a363c2c79 (diff)
downloadmal-96115d4f2e8cf7335a7d6bb8b62e0f0bbfac0ab5.tar.gz
mal-96115d4f2e8cf7335a7d6bb8b62e0f0bbfac0ab5.zip
C#: add step5_tco
-rw-r--r--cs/Makefile4
-rw-r--r--cs/step5_tco.cs178
-rw-r--r--cs/types.cs36
-rw-r--r--docs/step_notes.txt3
4 files changed, 213 insertions, 8 deletions
diff --git a/cs/Makefile b/cs/Makefile
index 61835cf..76b79cf 100644
--- a/cs/Makefile
+++ b/cs/Makefile
@@ -3,14 +3,14 @@
TESTS =
SOURCES = readline.cs types.cs reader.cs printer.cs env.cs core.cs \
- step4_if_fn_do.cs
+ step5_tco.cs
OTHER_SOURCES = getline.cs
#####################
SRCS = step0_repl.cs step1_read_print.cs step2_eval.cs step3_env.cs \
- step4_if_fn_do.cs
+ step4_if_fn_do.cs step5_tco.cs
LIB_SRCS = $(filter-out step%,$(OTHER_SOURCES) $(SOURCES))
diff --git a/cs/step5_tco.cs b/cs/step5_tco.cs
new file mode 100644
index 0000000..18b2fc9
--- /dev/null
+++ b/cs/step5_tco.cs
@@ -0,0 +1,178 @@
+using System;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using Mal;
+using MalVal = Mal.types.MalVal;
+using MalSymbol = Mal.types.MalSymbol;
+using MalInteger = Mal.types.MalInteger;
+using MalList = Mal.types.MalList;
+using MalVector = Mal.types.MalVector;
+using MalHashMap = Mal.types.MalHashMap;
+using MalFunction = Mal.types.MalFunction;
+using Env = Mal.env.Env;
+
+namespace Mal {
+ class step4_if_fn_do {
+ // read
+ static MalVal READ(string str) {
+ return reader.read_str(str);
+ }
+
+ // eval
+ static MalVal eval_ast(MalVal ast, Env env) {
+ if (ast is MalSymbol) {
+ MalSymbol sym = (MalSymbol)ast;
+ return env.get(sym.getName());
+ } else if (ast is MalList) {
+ MalList old_lst = (MalList)ast;
+ MalList new_lst = ast.list_Q() ? new MalList()
+ : (MalList)new MalVector();
+ foreach (MalVal mv in old_lst.getValue()) {
+ new_lst.conj_BANG(EVAL(mv, env));
+ }
+ return new_lst;
+ } else if (ast is MalHashMap) {
+ var new_dict = new Dictionary<string, MalVal>();
+ foreach (var entry in ((MalHashMap)ast).getValue()) {
+ new_dict.Add(entry.Key, EVAL((MalVal)entry.Value, env));
+ }
+ return new MalHashMap(new_dict);
+ } else {
+ return ast;
+ }
+ }
+
+
+ static MalVal EVAL(MalVal orig_ast, Env env) {
+ MalVal a0, a1, a2, res;
+ MalList el;
+
+ while (true) {
+
+ //System.out.println("EVAL: " + printer._pr_str(orig_ast, true));
+ if (!orig_ast.list_Q()) {
+ return eval_ast(orig_ast, env);
+ }
+
+ // apply list
+ MalList ast = (MalList)orig_ast;
+ if (ast.size() == 0) { return ast; }
+ a0 = ast.nth(0);
+
+ String a0sym = a0 is 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 == Mal.types.Nil || cond == Mal.types.False) {
+ // eval false slot form
+ if (ast.size() > 3) {
+ orig_ast = ast[3];
+ } else {
+ return Mal.types.Nil;
+ }
+ } else {
+ // eval true slot form
+ orig_ast = ast[2];
+ }
+ break;
+ case "fn*":
+ MalList a1f = (MalList)ast.nth(1);
+ MalVal a2f = ast.nth(2);
+ Env cur_env = env;
+ return new MalFunction(a2f, env, a1f,
+ args => EVAL(a2f, new Env(cur_env, a1f, args)) );
+ default:
+ el = (MalList)eval_ast(ast, env);
+ var f = (MalFunction)el.nth(0);
+ MalVal fnast = f.getAst();
+ if (fnast != null) {
+ orig_ast = fnast;
+ env = f.genEnv(el.rest());
+ } else {
+ return f.apply(el.rest());
+ }
+ break;
+ }
+
+ }
+ }
+
+ // print
+ static string PRINT(MalVal exp) {
+ return printer._pr_str(exp, true);
+ }
+
+ // REPL
+ static MalVal RE(Env env, string str) {
+ return EVAL(READ(str), env);
+ }
+ public static Env _ref(Env env, string name, MalVal mv) {
+ return env.set(name, mv);
+ }
+
+ static void Main(string[] args) {
+ string prompt = "user> ";
+
+ var repl_env = new Mal.env.Env(null);
+ foreach (var entry in Mal.core.ns) {
+ _ref(repl_env, entry.Key, entry.Value);
+ }
+
+ RE(repl_env, "(def! not (fn* (a) (if a false true)))");
+
+ if (args.Length > 0 && args[0] == "--raw") {
+ Mal.readline.mode = Mal.readline.Mode.Raw;
+ }
+ while (true) {
+ string line;
+ try {
+ line = Mal.readline.Readline(prompt);
+ if (line == null) { break; }
+ } catch (IOException e) {
+ Console.WriteLine("IOException: " + e.Message);
+ break;
+ }
+ try {
+ Console.WriteLine(PRINT(RE(repl_env, line)));
+ } catch (Mal.types.MalContinue) {
+ continue;
+ } catch (Mal.reader.ParseError e) {
+ Console.WriteLine(e.Message);
+ continue;
+ } catch (Mal.types.MalException e) {
+ Console.WriteLine("Error: " + e.getValue());
+ continue;
+ } catch (Exception e) {
+ Console.WriteLine("Error: " + e.Message);
+ continue;
+ }
+ }
+ }
+ }
+}
diff --git a/cs/types.cs b/cs/types.cs
index ae9d4bf..87a159b 100644
--- a/cs/types.cs
+++ b/cs/types.cs
@@ -67,7 +67,7 @@ namespace Mal {
public abstract class MalVal {
// Default is just to call regular toString()
public virtual string ToString(bool print_readably) {
- return "<unknown>";
+ return this.ToString();
}
public virtual bool list_Q() { return false; }
}
@@ -290,13 +290,39 @@ namespace Mal {
}
public class MalFunction : MalVal {
- Func<MalList, MalVal> value = null;
- public MalFunction(Func<MalList, MalVal> f) {
- value = f;
+ Func<MalList, MalVal> fn = null;
+ MalVal ast = null;
+ Mal.env.Env env = null;
+ MalList fparams;
+ public MalFunction(Func<MalList, MalVal> fn) {
+ this.fn = fn;
+ }
+ public MalFunction(MalVal ast, Mal.env.Env env, MalList fparams,
+ Func<MalList, MalVal> fn) {
+ this.fn = fn;
+ this.ast = ast;
+ this.env = env;
+ this.fparams = fparams;
+ }
+
+ public override string ToString() {
+ if (ast != null) {
+ return "<fn* " + Mal.printer._pr_str(fparams,true) +
+ " " + Mal.printer._pr_str(ast, true) + ">";
+ } else {
+ return "<builtin_function " + fn.ToString() + ">";
+ }
}
public MalVal apply(MalList args) {
- return value(args);
+ return fn(args);
+ }
+
+ public MalVal getAst() { return ast; }
+ public Mal.env.Env getEnv() { return env; }
+ public MalList getFParams() { return fparams; }
+ public Mal.env.Env genEnv(MalList args) {
+ return new Mal.env.Env(env, fparams, args);
}
}
}
diff --git a/docs/step_notes.txt b/docs/step_notes.txt
index c596a33..120da6e 100644
--- a/docs/step_notes.txt
+++ b/docs/step_notes.txt
@@ -130,7 +130,8 @@ Step Notes:
- while loop around whole thing
- cases where we directly return result of EVAL, instead set
ast and env to what would be put in the EVAL, then loop.
- - for apply case, set env to new Env based on properties
+ - do, if, "apply"
+ - for "apply" case, set env to new Env based on properties
on the function
- step6_file