diff options
| author | Joel Martin <github@martintribe.org> | 2014-04-07 21:33:29 -0500 |
|---|---|---|
| committer | Joel Martin <github@martintribe.org> | 2014-04-07 21:33:29 -0500 |
| commit | 96115d4f2e8cf7335a7d6bb8b62e0f0bbfac0ab5 (patch) | |
| tree | 9d0e73e3bcaf87dd2c55c5d874f4581a8fbbbcfd | |
| parent | afdf531eba459a7a7b6505b037dbe48a363c2c79 (diff) | |
| download | mal-96115d4f2e8cf7335a7d6bb8b62e0f0bbfac0ab5.tar.gz mal-96115d4f2e8cf7335a7d6bb8b62e0f0bbfac0ab5.zip | |
C#: add step5_tco
| -rw-r--r-- | cs/Makefile | 4 | ||||
| -rw-r--r-- | cs/step5_tco.cs | 178 | ||||
| -rw-r--r-- | cs/types.cs | 36 | ||||
| -rw-r--r-- | docs/step_notes.txt | 3 |
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 |
