aboutsummaryrefslogtreecommitdiff
path: root/cs
diff options
context:
space:
mode:
authorJoel Martin <github@martintribe.org>2014-04-06 23:58:14 -0500
committerJoel Martin <github@martintribe.org>2014-04-06 23:58:14 -0500
commitb18969c0b8d47d67d4b73b5b20742a0bc3179e72 (patch)
tree5c3eb2c1545857801ff5e64504bdd2a2ee49aac0 /cs
parentb56c49a12dad20c55fbf07775b536c9d656af313 (diff)
downloadmal-b18969c0b8d47d67d4b73b5b20742a0bc3179e72.tar.gz
mal-b18969c0b8d47d67d4b73b5b20742a0bc3179e72.zip
CS: add step3_env
Also, make Makefile more closely match the C Makefile.
Diffstat (limited to 'cs')
-rw-r--r--cs/Makefile12
-rw-r--r--cs/core.cs33
-rw-r--r--cs/env.cs55
-rw-r--r--cs/step2_eval.cs16
-rw-r--r--cs/step3_env.cs140
-rw-r--r--cs/types.cs26
6 files changed, 266 insertions, 16 deletions
diff --git a/cs/Makefile b/cs/Makefile
index 7a56e5d..bf8a8ef 100644
--- a/cs/Makefile
+++ b/cs/Makefile
@@ -2,20 +2,22 @@
TESTS =
-SOURCES = readline.cs types.cs reader.cs printer.cs \
- step0_repl.cs step1_read_print.cs step2_eval.cs
+SOURCES = readline.cs types.cs reader.cs printer.cs env.cs core.cs \
+ step3_env.cs
+
OTHER_SOURCES = getline.cs
#####################
-FLAGS = -debug+
+SRCS = step0_repl.cs step1_read_print.cs step2_eval.cs step3_env.cs
-OBJS =
LIB_SRCS = $(filter-out step%,$(OTHER_SOURCES) $(SOURCES))
+FLAGS = -debug+
+
#####################
-all: mal.exe $(patsubst %.cs,%.exe,$(filter step%,$(SOURCES)))
+all: mal.exe $(patsubst %.cs,%.exe,$(SRCS))
mal.exe: $(patsubst %.cs,%.exe,$(word $(words $(SOURCES)),$(SOURCES)))
cp $< $@
diff --git a/cs/core.cs b/cs/core.cs
new file mode 100644
index 0000000..fad4cce
--- /dev/null
+++ b/cs/core.cs
@@ -0,0 +1,33 @@
+using MalVal = Mal.types.MalVal;
+using MalInteger = Mal.types.MalInteger;
+using MalList = Mal.types.MalList;
+using MalFunction = Mal.types.MalFunction;
+
+namespace Mal {
+ public class core {
+ public class plus : MalFunction {
+ public override MalVal apply(MalList args) {
+ return ((MalInteger)args.nth(0)).add(
+ ((MalInteger)args.nth(1)));
+ }
+ }
+ public class minus : MalFunction {
+ public override MalVal apply(MalList args) {
+ return ((MalInteger)args.nth(0)).subtract(
+ ((MalInteger)args.nth(1)));
+ }
+ }
+ public class multiply : MalFunction {
+ public override MalVal apply(MalList args) {
+ return ((MalInteger)args.nth(0)).multiply(
+ ((MalInteger)args.nth(1)));
+ }
+ }
+ public class divide : MalFunction {
+ public override MalVal apply(MalList args) {
+ return ((MalInteger)args.nth(0)).divide(
+ ((MalInteger)args.nth(1)));
+ }
+ }
+ }
+}
diff --git a/cs/env.cs b/cs/env.cs
new file mode 100644
index 0000000..cb5318f
--- /dev/null
+++ b/cs/env.cs
@@ -0,0 +1,55 @@
+using System.Collections.Generic;
+using Mal;
+using MalVal = Mal.types.MalVal;
+using MalSymbol = Mal.types.MalSymbol;
+using MalList = Mal.types.MalList;
+
+namespace Mal {
+ public class env {
+ public class Env {
+ Env outer = null;
+ Dictionary<string, MalVal> data = new Dictionary<string, MalVal>();
+
+ public Env(Env outer) {
+ this.outer = outer;
+ }
+ public Env(Env outer, MalList binds, MalList exprs) {
+ this.outer = outer;
+ for (int i=0; i<binds.size(); i++) {
+ string sym = ((MalSymbol)binds.nth(i)).getName();
+ if (sym == "&") {
+ data[((MalSymbol)binds.nth(i+1)).getName()] = exprs.slice(i);
+ break;
+ } else {
+ data[sym] = exprs.nth(i);
+ }
+ }
+ }
+
+ public Env find(string key) {
+ if (data.ContainsKey(key)) {
+ return this;
+ } else if (outer != null) {
+ return outer.find(key);
+ } else {
+ return null;
+ }
+ }
+
+ public MalVal get(string key) {
+ Env e = find(key);
+ if (e == null) {
+ throw new Mal.types.MalException(
+ "'" + key + "' not found");
+ } else {
+ return e.data[key];
+ }
+ }
+
+ public Env set(string key, MalVal value) {
+ data[key] = value;
+ return this;
+ }
+ }
+ }
+}
diff --git a/cs/step2_eval.cs b/cs/step2_eval.cs
index fa01901..5b49916 100644
--- a/cs/step2_eval.cs
+++ b/cs/step2_eval.cs
@@ -58,13 +58,9 @@ namespace Mal {
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);
+ var el = (MalList)eval_ast(ast, env);
+ var f = (MalFunction)el.nth(0);
+ return f.apply(el.rest());
}
@@ -130,12 +126,12 @@ namespace Mal {
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 (Mal.types.MalError e) {
+ Console.WriteLine("Error: " + e.Message);
+ continue;
} catch (Exception e) {
Console.WriteLine("Error: " + e.Message);
continue;
diff --git a/cs/step3_env.cs b/cs/step3_env.cs
new file mode 100644
index 0000000..0fbbda7
--- /dev/null
+++ b/cs/step3_env.cs
@@ -0,0 +1,140 @@
+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 step1_repl {
+ // 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;
+ //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) + "'");
+ }
+
+ switch (((MalSymbol)a0).getName()) {
+ 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);
+ default:
+ var el = (MalList)eval_ast(ast, env);
+ var f = (MalFunction)el.nth(0);
+ return f.apply(el.rest());
+ }
+ }
+
+ // 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);
+ _ref(repl_env, "+", new Mal.core.plus() );
+ _ref(repl_env, "-", new Mal.core.minus() );
+ _ref(repl_env, "*", new Mal.core.multiply() );
+ _ref(repl_env, "/", new Mal.core.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.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 7cbbd53..8f6755f 100644
--- a/cs/types.cs
+++ b/cs/types.cs
@@ -13,6 +13,18 @@ namespace Mal {
}
public class MalContinue : MalThrowable { }
+ // Thrown by throw function
+ public class MalException : MalThrowable {
+ MalVal value;
+ public MalException(MalVal value) {
+ this.value = value;
+ }
+ public MalException(string value) {
+ this.value = new MalString(value);
+ }
+ public MalVal getValue() { return value; }
+ }
+
public abstract class MalVal {
// Default is just to call regular toString()
@@ -148,13 +160,20 @@ namespace Mal {
public int size() { return value.Count; }
public MalVal nth(int idx) { return value[idx]; }
- public MalVal rest() {
+ public MalList rest() {
if (size() > 0) {
return new MalList(value.GetRange(1, value.Count-1));
} else {
return new MalList();
}
}
+ public virtual MalList slice(int start) {
+ return new MalList(value.GetRange(start, value.Count-start));
+ }
+ public virtual MalList slice(int start, int end) {
+ return new MalList(value.GetRange(start, end-start));
+ }
+
}
public class MalVector : MalList {
@@ -179,6 +198,11 @@ namespace Mal {
}
public override bool list_Q() { return false; }
+
+ public override MalList slice(int start, int end) {
+ var val = this.getValue();
+ return new MalVector(val.GetRange(start, val.Count-start));
+ }
}
public class MalHashMap : MalVal {