diff options
| author | Joel Martin <github@martintribe.org> | 2014-04-09 21:26:35 -0500 |
|---|---|---|
| committer | Joel Martin <github@martintribe.org> | 2014-04-09 21:26:35 -0500 |
| commit | faee4d12309cec8c90854456fabf4e5e75370518 (patch) | |
| tree | 25f9d4d74f958333976fe5c4bd9327ad86b77a09 | |
| parent | faa20db28295845973b895995c92a70a247f789c (diff) | |
| download | mal-faee4d12309cec8c90854456fabf4e5e75370518.tar.gz mal-faee4d12309cec8c90854456fabf4e5e75370518.zip | |
C#: add stepA_more and core functions.
| -rw-r--r-- | cs/Makefile | 4 | ||||
| -rw-r--r-- | cs/core.cs | 165 | ||||
| -rw-r--r-- | cs/stepA_more.cs | 296 | ||||
| -rw-r--r-- | cs/types.cs | 38 |
4 files changed, 474 insertions, 29 deletions
diff --git a/cs/Makefile b/cs/Makefile index e105786..3236bd6 100644 --- a/cs/Makefile +++ b/cs/Makefile @@ -3,7 +3,7 @@ TESTS = SOURCES = readline.cs types.cs reader.cs printer.cs env.cs core.cs \ - step8_macros.cs + stepA_more.cs OTHER_SOURCES = getline.cs @@ -11,7 +11,7 @@ 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 step7_quote.cs \ - step8_macros.cs + step8_macros.cs stepA_more.cs LIB_SRCS = $(filter-out step%,$(OTHER_SOURCES) $(SOURCES)) @@ -3,8 +3,11 @@ using System.Collections.Generic; using MalVal = Mal.types.MalVal; using MalConstant = Mal.types.MalConstant; using MalInteger = Mal.types.MalInteger; +using MalSymbol = Mal.types.MalSymbol; using MalString = Mal.types.MalString; using MalList = Mal.types.MalList; +using MalVector = Mal.types.MalVector; +using MalHashMap = Mal.types.MalHashMap; using MalFunction = Mal.types.MalFunction; namespace Mal { @@ -13,6 +16,24 @@ namespace Mal { static MalConstant True = Mal.types.True; static MalConstant False = Mal.types.False; + // Errors/Exceptions + static public MalFunction mal_throw = new MalFunction( + a => { throw new Mal.types.MalException(a[0]); }); + + // Scalar functions + static MalFunction nil_Q = new MalFunction( + a => a[0] == Nil ? True : False); + + static MalFunction true_Q = new MalFunction( + a => a[0] == True ? True : False); + + static MalFunction false_Q = new MalFunction( + a => a[0] == False ? True : False); + + static MalFunction symbol_Q = new MalFunction( + a => a[0] is MalSymbol ? True : False); + + // String functions static public MalFunction pr_str = new MalFunction( a => new MalString(printer._pr_str_args(a, " ", true)) ); @@ -32,12 +53,66 @@ namespace Mal { return Nil; } ); - // Sequence functions + // List/Vector functions static public MalFunction list_Q = new MalFunction( a => a[0].GetType() == typeof(MalList) ? True : False); - static MalFunction nth = new MalFunction( - a => ((MalList)a[0])[ ((MalInteger)a[1]).getValue() ]); + static public MalFunction vector_Q = new MalFunction( + a => a[0].GetType() == typeof(MalVector) ? True : False); + + // HashMap functions + static public MalFunction hash_map_Q = new MalFunction( + a => a[0].GetType() == typeof(MalHashMap) ? True : False); + + static MalFunction contains_Q = new MalFunction( + a => { + string key = ((MalString)a[1]).getValue(); + var dict = ((MalHashMap)a[0]).getValue(); + return dict.ContainsKey(key) ? True : False; + }); + + static MalFunction assoc = new MalFunction( + a => { + var new_hm = ((MalHashMap)a[0]).copy(); + return new_hm.assoc_BANG((MalList)a.slice(1)); + }); + + static MalFunction dissoc = new MalFunction( + a => { + var new_hm = ((MalHashMap)a[0]).copy(); + return new_hm.dissoc_BANG((MalList)a.slice(1)); + }); + + static MalFunction get = new MalFunction( + a => { + string key = ((MalString)a[1]).getValue(); + var dict = ((MalHashMap)a[0]).getValue(); + return dict.ContainsKey(key) ? dict[key] : Nil; + }); + + static MalFunction keys = new MalFunction( + a => { + var dict = ((MalHashMap)a[0]).getValue(); + MalList key_lst = new MalList(); + foreach (var key in dict.Keys) { + key_lst.conj_BANG(new MalString(key)); + } + return key_lst; + }); + + static MalFunction vals = new MalFunction( + a => { + var dict = ((MalHashMap)a[0]).getValue(); + MalList val_lst = new MalList(); + foreach (var val in dict.Values) { + val_lst.conj_BANG(val); + } + return val_lst; + }); + + // Sequence functions + static public MalFunction sequential_Q = new MalFunction( + a => a[0] is MalList ? True : False); static MalFunction cons = new MalFunction( a => { @@ -58,6 +133,61 @@ namespace Mal { return (MalVal)new MalList(lst); }); + static MalFunction nth = new MalFunction( + a => ((MalList)a[0])[ ((MalInteger)a[1]).getValue() ]); + + static MalFunction first = new MalFunction( + a => ((MalList)a[0])[0]); + + static MalFunction rest = new MalFunction( + a => ((MalList)a[0]).rest()); + + static MalFunction empty_Q = new MalFunction( + a => ((MalList)a[0]).size() == 0 ? True : False); + + static MalFunction count = new MalFunction( + a => new MalInteger(((MalList)a[0]).size())); + + static MalFunction conj = new MalFunction( + a => { + var src_lst = ((MalList)a[0]).getValue(); + var new_lst = new List<MalVal>(); + new_lst.AddRange(src_lst); + if (a[0] is MalVector) { + for(int i=1; i<a.size(); i++) { + new_lst.Add(a[i]); + } + return new MalVector(new_lst); + } else { + for(int i=1; i<a.size(); i++) { + new_lst.Insert(0, a[i]); + } + return new MalList(new_lst); + } + }); + + + // General list related functions + static MalFunction apply = new MalFunction( + a => { + var f = (MalFunction)a[0]; + var lst = new List<MalVal>(); + lst.AddRange(a.slice(1,a.size()-1).getValue()); + lst.AddRange(((MalList)a[a.size()-1]).getValue()); + return f.apply(new MalList(lst)); + }); + + static MalFunction map = new MalFunction( + a => { + MalFunction f = (MalFunction) a[0]; + var src_lst = ((MalList)a[1]).getValue(); + var new_lst = new List<MalVal>(); + for(int i=0; i<src_lst.Count; i++) { + new_lst.Add(f.apply(new MalList(src_lst[i]))); + } + return new MalList(new_lst); + }); + @@ -65,6 +195,11 @@ namespace Mal { new Dictionary<string, MalVal> { {"=", new MalFunction( a => Mal.types._equal_Q(a[0], a[1]) ? True : False)}, + {"throw", mal_throw}, + {"nil?", nil_Q}, + {"true?", true_Q}, + {"false?", false_Q}, + {"symbol?", symbol_Q}, {"pr-str", pr_str}, {"str", str}, {"prn", prn}, @@ -80,16 +215,28 @@ namespace Mal { {"list", new MalFunction(a => new MalList(a.getValue()))}, {"list?", list_Q}, + {"vector", new MalFunction(a => new MalVector(a.getValue()))}, + {"vector?", vector_Q}, + {"hash-map", new MalFunction(a => new MalHashMap(a))}, + {"map?", hash_map_Q}, + {"contains?", contains_Q}, + {"assoc", assoc}, + {"dissoc", dissoc}, + {"get", get}, + {"keys", keys}, + {"vals", vals}, + {"sequential?", sequential_Q}, {"cons", cons}, {"concat", concat}, {"nth", nth}, - {"first", new MalFunction(a => ((MalList)a[0])[0])}, - {"rest", new MalFunction(a => ((MalList)a[0]).rest())}, - {"empty?", new MalFunction( - a => ((MalList)a[0]).size() == 0 ? True : False)}, - {"count", new MalFunction( - a => new MalInteger(((MalList)a[0]).size()))}, + {"first", first}, + {"rest", rest}, + {"empty?", empty_Q}, + {"count", count}, + {"conj", conj}, + {"apply", apply}, + {"map", map}, }; } } diff --git a/cs/stepA_more.cs b/cs/stepA_more.cs new file mode 100644 index 0000000..97b1e84 --- /dev/null +++ b/cs/stepA_more.cs @@ -0,0 +1,296 @@ +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using Mal; +using MalException = Mal.types.MalException; +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 stepA_more { + // 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())); + } + } + + public static bool is_macro_call(MalVal ast, Env env) { + if (ast is MalList) { + MalVal a0 = ((MalList)ast)[0]; + if (a0 is MalSymbol && + env.find(((MalSymbol)a0).getName()) != null) { + MalVal mac = env.get(((MalSymbol)a0).getName()); + if (mac is MalFunction && + ((MalFunction)mac).isMacro()) { + return true; + } + } + } + return false; + } + + public static MalVal macroexpand(MalVal ast, Env env) { + while (is_macro_call(ast, env)) { + MalSymbol a0 = (MalSymbol)((MalList)ast)[0]; + MalFunction mac = (MalFunction) env.get(a0.getName()); + ast = mac.apply(((MalList)ast).rest()); + } + return ast; + } + + 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 + MalVal expanded = macroexpand(orig_ast, env); + if (!expanded.list_Q()) { return expanded; } + MalList ast = (MalList) expanded; + + 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 "defmacro!": + a1 = ast[1]; + a2 = ast[2]; + res = EVAL(a2, env); + ((MalFunction)res).setMacro(); + env.set(((MalSymbol)a1).getName(), res); + return res; + case "macroexpand": + a1 = ast[1]; + return macroexpand(a1, env); + case "try*": + try { + return EVAL(ast[1], env); + } catch (Exception e) { + if (ast.size() > 2) { + MalVal exc; + a2 = ast[2]; + MalVal a20 = ((MalList)a2)[0]; + if (((MalSymbol)a20).getName() == "catch*") { + if (e is MalException) { + exc = ((MalException)e).getValue(); + } else { + exc = new MalString(e.StackTrace); + } + return EVAL(((MalList)a2)[2], + new Env(env, ((MalList)a2).slice(1,2), + new MalList(exc))); + } + } + throw e; + } + 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 == types.Nil || cond == types.False) { + // eval false slot form + if (ast.size() > 3) { + orig_ast = ast[3]; + } else { + return 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 env.Env(null); + foreach (var entry in core.ns) { + _ref(repl_env, entry.Key, entry.Value); + } + _ref(repl_env, "readline", new MalFunction( + a => { + var line = readline.Readline(((MalString)a[0]).getValue()); + if (line == null) { return types.Nil; } + else { return new MalString(line); } + })); + _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, "(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 (str \"(do \" (slurp f) \")\")))))"); + + int fileIdx = 0; + if (args.Length > 0 && args[0] == "--raw") { + readline.mode = readline.Mode.Raw; + fileIdx = 1; + } + if (args.Length > fileIdx) { + for(int 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) { break; } + } catch (IOException e) { + Console.WriteLine("IOException: " + e.Message); + break; + } + try { + Console.WriteLine(PRINT(RE(repl_env, line))); + } catch (types.MalContinue) { + continue; + } catch (reader.ParseError e) { + Console.WriteLine(e.Message); + continue; + } catch (MalException e) { + Console.WriteLine("Error: " + + printer._pr_str(e.getValue(), false)); + continue; + } catch (Exception e) { + Console.WriteLine("Error: " + e.Message); + Console.WriteLine(e.StackTrace); + continue; + } + } + } + } +} diff --git a/cs/types.cs b/cs/types.cs index cd94f94..642da7a 100644 --- a/cs/types.cs +++ b/cs/types.cs @@ -199,9 +199,11 @@ namespace Mal { } public int size() { return value.Count; } - public MalVal nth(int idx) { return value[idx]; } + public MalVal nth(int idx) { + return value.Count > 0 ? value[idx] : Nil; + } public MalVal this[int idx] { - get { return value[idx]; } + get { return value.Count > 0 ? value[idx] : Nil; } } public MalList rest() { if (size() > 0) { @@ -255,16 +257,12 @@ namespace Mal { } public MalHashMap(MalList lst) { value = new Dictionary<String, MalVal>(); - assoc_BANG(lst.getValue().ToArray()); - } - /* - public MalHashMap(params MalVal[] mvs) { - value = new Dictionary<String, MalVal>(); - assoc_BANG(mvs); + assoc_BANG(lst); } - */ public MalHashMap copy() { - return (MalHashMap)this.MemberwiseClone(); + var new_self = (MalHashMap)this.MemberwiseClone(); + new_self.value = new Dictionary<string, MalVal>(value); + return new_self; } public Dictionary<string, MalVal> getValue() { return value; } @@ -276,15 +274,16 @@ namespace Mal { return "{" + printer.join(value, " ", print_readably) + "}"; } - /* - public Set _entries() { - return value.entrySet(); + public MalHashMap assoc_BANG(MalList lst) { + for (int i=0; i<lst.size(); i+=2) { + value[((MalString)lst[i]).getValue()] = lst[i+1]; + } + return this; } - */ - - public MalHashMap assoc_BANG(params MalVal[] mvs) { - for (int i=0; i<mvs.Length; i+=2) { - value.Add(((MalString)mvs[i]).getValue(), mvs[i+1]); + + public MalHashMap dissoc_BANG(MalList lst) { + for (int i=0; i<lst.size(); i++) { + value.Remove(((MalString)lst[i]).getValue()); } return this; } @@ -306,6 +305,9 @@ namespace Mal { this.env = env; this.fparams = fparams; } + public MalFunction copy() { + return (MalFunction)this.MemberwiseClone(); + } public override string ToString() { if (ast != null) { |
