C#: add step2_eval.
authorJoel Martin <github@martintribe.org>
Mon, 7 Apr 2014 03:38:26 +0000 (22:38 -0500)
committerJoel Martin <github@martintribe.org>
Mon, 7 Apr 2014 03:38:26 +0000 (22:38 -0500)
.gitignore
cs/Makefile
cs/step2_eval.cs [new file with mode: 0644]
cs/types.cs

index 76ce4fd..19ba294 100644 (file)
@@ -19,6 +19,7 @@ c/step9_interop
 c/stepA_more
 cs/*.exe
 cs/*.dll
+cs/*.mdb
 clojure/target
 clojure/.lein-repl-history
 java/target/
index 0028096..7a56e5d 100644 (file)
@@ -3,7 +3,7 @@
 TESTS =
 
 SOURCES = readline.cs types.cs reader.cs printer.cs \
-         step0_repl.cs step1_read_print.cs
+         step0_repl.cs step1_read_print.cs step2_eval.cs
 OTHER_SOURCES = getline.cs
 
 #####################
diff --git a/cs/step2_eval.cs b/cs/step2_eval.cs
new file mode 100644 (file)
index 0000000..fa01901
--- /dev/null
@@ -0,0 +1,146 @@
+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;
+
+namespace Mal {
+    class step1_repl {
+        // read
+        static MalVal READ(string str) {
+            return reader.read_str(str);
+        }
+
+        // eval
+        static MalVal eval_ast(MalVal ast, Dictionary<string, MalVal> env) {
+            if (ast is MalSymbol) {
+                MalSymbol sym = (MalSymbol)ast;
+                return (MalVal)env[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, Dictionary<string, MalVal> env) {
+            MalVal a0;
+            //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);
+            if (!(a0 is MalSymbol)) {
+                throw new Mal.types.MalError("attempt to apply on non-symbol '"
+                        + Mal.printer._pr_str(a0,true) + "'");
+            }
+            MalVal args = eval_ast(ast.rest(), env);
+            MalSymbol fsym = (MalSymbol)a0;
+            var f = (MalFunction)env[fsym.getName()];
+            if (f == null) {
+                throw new Mal.types.MalError("'" + fsym.getName() + "' not found");
+            }
+            return f.apply((MalList)args);
+
+        }
+
+        // print
+        static string PRINT(MalVal exp) {
+            return printer._pr_str(exp, true);
+        }
+
+        // REPL
+        static MalVal RE(Dictionary<string, MalVal> env, string str) {
+            return EVAL(READ(str), env);
+        }
+
+        class plus : MalFunction {
+            public override MalVal apply(MalList args) {
+                return ((MalInteger)args.nth(0)).add(
+                        ((MalInteger)args.nth(1)));
+            }
+        }
+        class minus : MalFunction {
+            public override MalVal apply(MalList args) {
+                return ((MalInteger)args.nth(0)).subtract(
+                        ((MalInteger)args.nth(1)));
+            }
+        }
+        class multiply : MalFunction {
+            public override MalVal apply(MalList args) {
+                return ((MalInteger)args.nth(0)).multiply(
+                        ((MalInteger)args.nth(1)));
+            }
+        }
+        class divide : MalFunction {
+            public override MalVal apply(MalList args) {
+                return ((MalInteger)args.nth(0)).divide(
+                        ((MalInteger)args.nth(1)));
+            }
+        }
+
+
+        static void Main(string[] args) {
+            string prompt = "user> ";
+            
+            var repl_env = new Dictionary<string, MalVal> {
+                { "+", new plus() },
+                { "-", new minus() },
+                { "*", new multiply() },
+                { "/", new divide() },
+            };
+            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.types.MalError e) {
+                    Console.WriteLine("Error: " + e.Message);
+                    continue;
+                } catch (Mal.reader.ParseError e) {
+                    Console.WriteLine(e.Message);
+                    continue;
+                } catch (Exception e) {
+                    Console.WriteLine("Error: " + e.Message);
+                    continue;
+                }
+            }
+        }
+    }
+}
index 1f2e797..7cbbd53 100644 (file)
@@ -16,7 +16,10 @@ namespace Mal {
 
         public abstract class MalVal {
             // Default is just to call regular toString()
-            public abstract string ToString(bool print_readably);
+            public virtual string ToString(bool print_readably) {
+                return "<unknown>";
+            }
+            public virtual bool list_Q() { return false; }
         }
 
         public class MalConstant : MalVal {
@@ -127,6 +130,7 @@ namespace Mal {
             }
 
             public List<MalVal> getValue() { return value; }
+            public override bool list_Q() { return true; }
 
             public override string ToString() {
                 return start + printer.join(value, " ", true) + end;
@@ -141,6 +145,16 @@ namespace Mal {
                 }
                 return this;
             }
+
+            public int size() { return value.Count; }
+            public MalVal nth(int idx) { return value[idx]; }
+            public MalVal rest() {
+                if (size() > 0) {
+                    return new MalList(value.GetRange(1, value.Count-1));
+                } else {
+                    return new MalList();
+                }
+            }
         }
 
         public class MalVector : MalList {
@@ -164,6 +178,7 @@ namespace Mal {
                 return (MalVector)this.MemberwiseClone();
             }
 
+            public override bool list_Q() { return false; }
         }
 
         public class MalHashMap : MalVal {
@@ -185,6 +200,8 @@ namespace Mal {
                 return (MalHashMap)this.MemberwiseClone();
             }
 
+            public Dictionary<string, MalVal> getValue() { return value; }
+
             public override string ToString() {
                 return "{" + printer.join(value, " ", true) + "}";
             }
@@ -206,5 +223,9 @@ namespace Mal {
             }
         }
 
+        public abstract class MalFunction : MalVal {
+            public abstract MalVal apply(MalList args);
+        }
+
     }
 }