From 5a159ae75ae4a9b302f2a5a1c826fbc872981368 Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Mon, 7 Apr 2014 22:40:30 -0500 Subject: [PATCH] C#: add step7_quote --- cs/Makefile | 4 +- cs/core.cs | 27 +++++- cs/reader.cs | 30 ++++-- cs/step7_quote.cs | 228 ++++++++++++++++++++++++++++++++++++++++++++++ cs/types.cs | 1 + 5 files changed, 279 insertions(+), 11 deletions(-) create mode 100644 cs/step7_quote.cs diff --git a/cs/Makefile b/cs/Makefile index 3194c735..012c503b 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 \ - step6_file.cs + step7_quote.cs OTHER_SOURCES = getline.cs ##################### SRCS = step0_repl.cs step1_read_print.cs step2_eval.cs step3_env.cs \ - step4_if_fn_do.cs step5_tco.cs step6_file.cs + step4_if_fn_do.cs step5_tco.cs step6_file.cs step7_quote.cs LIB_SRCS = $(filter-out step%,$(OTHER_SOURCES) $(SOURCES)) diff --git a/cs/core.cs b/cs/core.cs index ae8fe1b7..0ebc3ef5 100644 --- a/cs/core.cs +++ b/cs/core.cs @@ -36,6 +36,26 @@ namespace Mal { static public MalFunction list_Q = new MalFunction( a => a[0].GetType() == typeof(MalList) ? True : False); + static MalFunction cons = new MalFunction( + a => { + var lst = new List(); + lst.Add(a[0]); + lst.AddRange(((MalList)a[1]).getValue()); + return (MalVal)new MalList(lst); + }); + + static MalFunction concat = new MalFunction( + a => { + if (a.size() == 0) { return new MalList(); } + var lst = new List(); + lst.AddRange(((MalList)a[0]).getValue()); + for(int i=1; i ns = @@ -57,12 +77,15 @@ namespace Mal { {"list", new MalFunction(a => new MalList(a.getValue()))}, {"list?", list_Q}, + + {"cons", cons}, + {"concat", concat}, {"first", new MalFunction(a => ((MalList)a[0]).nth(0))}, {"rest", new MalFunction(a => ((MalList)a[0]).rest())}, - {"count", new MalFunction( - a => new MalInteger(((MalList)a[0]).size()))}, {"empty?", new MalFunction( a => ((MalList)a[0]).size() == 0 ? True : False)}, + {"count", new MalFunction( + a => new MalInteger(((MalList)a[0]).size()))}, }; } } diff --git a/cs/reader.cs b/cs/reader.cs index a6d22c5f..a8d09c6b 100644 --- a/cs/reader.cs +++ b/cs/reader.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Text.RegularExpressions; using Mal; using MalVal = Mal.types.MalVal; +using MalSymbol = Mal.types.MalSymbol; using MalList = Mal.types.MalList; using MalVector = Mal.types.MalVector; using MalHashMap = Mal.types.MalHashMap; @@ -109,13 +110,28 @@ namespace Mal { if (token == null) { throw new MalContinue(); } MalVal form = null; - switch (token[0]) { - 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 '}'"); + switch (token) { + 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 "~": + rdr.next(); + return new MalList(new MalSymbol("unquote"), + read_form(rdr)); + case "~@": + rdr.next(); + return new MalList(new MalSymbol("splice-unquote"), + 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); break; } return form; diff --git a/cs/step7_quote.cs b/cs/step7_quote.cs new file mode 100644 index 00000000..61f638f7 --- /dev/null +++ b/cs/step7_quote.cs @@ -0,0 +1,228 @@ +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using Mal; +using MalVal = Mal.types.MalVal; +using MalString = Mal.types.MalString; +using MalSymbol = Mal.types.MalSymbol; +using MalInteger = Mal.types.MalInteger; +using MalList = Mal.types.MalList; +using MalVector = Mal.types.MalVector; +using MalHashMap = Mal.types.MalHashMap; +using MalFunction = Mal.types.MalFunction; +using Env = Mal.env.Env; + +namespace Mal { + class step4_if_fn_do { + // read + static MalVal READ(string str) { + return reader.read_str(str); + } + + // eval + public static bool is_pair(MalVal x) { + return x is MalList && ((MalList)x).size() > 0; + } + + public static MalVal quasiquote(MalVal ast) { + if (!is_pair(ast)) { + return new MalList(new MalSymbol("quote"), ast); + } else { + MalVal a0 = ((MalList)ast)[0]; + if ((a0 is MalSymbol) && + (((MalSymbol)a0).getName() == "unquote")) { + return ((MalList)ast)[1]; + } else if (is_pair(a0)) { + MalVal a00 = ((MalList)a0)[0]; + if ((a00 is MalSymbol) && + (((MalSymbol)a00).getName() == "splice-unquote")) { + return new MalList(new MalSymbol("concat"), + ((MalList)a0)[1], + quasiquote(((MalList)ast).rest())); + } + } + return new MalList(new MalSymbol("cons"), + quasiquote(a0), + quasiquote(((MalList)ast).rest())); + } + } + + 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(); + 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[0]; + + String a0sym = a0 is MalSymbol ? ((MalSymbol)a0).getName() + : "__<*fn*>__"; + + switch (a0sym) { + case "def!": + a1 = ast[1]; + a2 = ast[2]; + res = EVAL(a2, env); + env.set(((MalSymbol)a1).getName(), res); + return res; + case "let*": + a1 = ast[1]; + a2 = ast[2]; + MalSymbol key; + MalVal val; + Env let_env = new Env(env); + for(int i=0; i<((MalList)a1).size(); i+=2) { + key = (MalSymbol)((MalList)a1)[i]; + val = ((MalList)a1)[i+1]; + let_env.set(key.getName(), EVAL(val, let_env)); + } + return EVAL(a2, let_env); + case "quote": + return ast[1]; + case "quasiquote": + return EVAL(quasiquote(ast[1]), env); + case "do": + eval_ast(ast.slice(1, ast.size()-1), env); + orig_ast = ast[ast.size()-1]; + break; + case "if": + a1 = ast[1]; + MalVal cond = EVAL(a1, env); + if (cond == Mal.types.Nil || cond == Mal.types.False) { + // eval false slot form + if (ast.size() > 3) { + orig_ast = ast[3]; + } else { + return Mal.types.Nil; + } + } else { + // eval true slot form + orig_ast = ast[2]; + } + break; + case "fn*": + MalList a1f = (MalList)ast[1]; + MalVal a2f = ast[2]; + Env cur_env = env; + return new MalFunction(a2f, env, a1f, + args => EVAL(a2f, new Env(cur_env, a1f, args)) ); + default: + el = (MalList)eval_ast(ast, env); + var f = (MalFunction)el[0]; + MalVal fnast = f.getAst(); + if (fnast != null) { + orig_ast = fnast; + env = f.genEnv(el.rest()); + } else { + return f.apply(el.rest()); + } + break; + } + + } + } + + // print + static string PRINT(MalVal exp) { + return printer._pr_str(exp, true); + } + + // REPL + static MalVal RE(Env env, string str) { + return EVAL(READ(str), env); + } + 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); + } + _ref(repl_env, "read-string", new MalFunction( + a => reader.read_str(((MalString)a[0]).getValue()))); + _ref(repl_env, "eval", new MalFunction( + a => EVAL(a[0], repl_env))); + _ref(repl_env, "slurp", new MalFunction( + a => new MalString(File.ReadAllText( + ((MalString)a[0]).getValue())))); + + RE(repl_env, "(def! not (fn* (a) (if a false true)))"); + RE(repl_env, "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); + + int fileIdx = 0; + if (args.Length > 0 && args[0] == "--raw") { + Mal.readline.mode = Mal.readline.Mode.Raw; + fileIdx = 1; + } + if (args.Length > fileIdx) { + for(int i=fileIdx; i(); conj_BANG(mvs); } -- 2.20.1