diff options
| author | Joel Martin <github@martintribe.org> | 2014-04-06 23:58:14 -0500 |
|---|---|---|
| committer | Joel Martin <github@martintribe.org> | 2014-04-06 23:58:14 -0500 |
| commit | b18969c0b8d47d67d4b73b5b20742a0bc3179e72 (patch) | |
| tree | 5c3eb2c1545857801ff5e64504bdd2a2ee49aac0 /cs | |
| parent | b56c49a12dad20c55fbf07775b536c9d656af313 (diff) | |
| download | mal-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/Makefile | 12 | ||||
| -rw-r--r-- | cs/core.cs | 33 | ||||
| -rw-r--r-- | cs/env.cs | 55 | ||||
| -rw-r--r-- | cs/step2_eval.cs | 16 | ||||
| -rw-r--r-- | cs/step3_env.cs | 140 | ||||
| -rw-r--r-- | cs/types.cs | 26 |
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 { |
