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 {
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)) );
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 => {
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);
+ });
+
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},
{"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},
};
}
}
--- /dev/null
+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;
+ }
+ }
+ }
+ }
+}
}
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) {
}
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; }
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;
}
this.env = env;
this.fparams = fparams;
}
+ public MalFunction copy() {
+ return (MalFunction)this.MemberwiseClone();
+ }
public override string ToString() {
if (ast != null) {