C#: add step7_quote
authorJoel Martin <github@martintribe.org>
Tue, 8 Apr 2014 03:40:30 +0000 (22:40 -0500)
committerJoel Martin <github@martintribe.org>
Tue, 8 Apr 2014 03:40:30 +0000 (22:40 -0500)
cs/Makefile
cs/core.cs
cs/reader.cs
cs/step7_quote.cs [new file with mode: 0644]
cs/types.cs

index 3194c73..012c503 100644 (file)
@@ -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))
 
index ae8fe1b..0ebc3ef 100644 (file)
@@ -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<MalVal>();
+                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<MalVal>();
+                lst.AddRange(((MalList)a[0]).getValue());
+                for(int i=1; i<a.size(); i++) {
+                    lst.AddRange(((MalList)a[i]).getValue());
+                }
+                return (MalVal)new MalList(lst);
+            });
+
+
 
 
         static public Dictionary<string, MalVal> 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()))},
         };
     }
 }
index a6d22c5..a8d09c6 100644 (file)
@@ -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 (file)
index 0000000..61f638f
--- /dev/null
@@ -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<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[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<args.Length; i++) {
+                    RE(repl_env, "(load-file \"" + args[i] + "\")");
+                }
+                return;
+            }
+            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);
+                    Console.WriteLine(e.StackTrace);
+                    continue;
+                }
+            }
+        }
+    }
+}
index 87a159b..442a2b2 100644 (file)
@@ -173,6 +173,7 @@ namespace Mal {
                 value = val;
             }
             public MalList(params MalVal[] mvs) {
+                value = new List<MalVal>();
                 conj_BANG(mvs);
             }