mirror of
https://github.com/kanaka/mal.git
synced 2024-09-20 10:07:45 +03:00
C#: add stepA_more and core functions.
This commit is contained in:
parent
faa20db282
commit
faee4d1230
@ -3,7 +3,7 @@
|
||||
TESTS =
|
||||
|
||||
SOURCES = readline.cs types.cs reader.cs printer.cs env.cs core.cs \
|
||||
step8_macros.cs
|
||||
stepA_more.cs
|
||||
|
||||
OTHER_SOURCES = getline.cs
|
||||
|
||||
@ -11,7 +11,7 @@ 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 step7_quote.cs \
|
||||
step8_macros.cs
|
||||
step8_macros.cs stepA_more.cs
|
||||
|
||||
LIB_SRCS = $(filter-out step%,$(OTHER_SOURCES) $(SOURCES))
|
||||
|
||||
|
165
cs/core.cs
165
cs/core.cs
@ -3,8 +3,11 @@ using System.Collections.Generic;
|
||||
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 {
|
||||
@ -13,6 +16,24 @@ 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)) );
|
||||
@ -32,12 +53,66 @@ namespace Mal {
|
||||
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 => {
|
||||
@ -58,6 +133,61 @@ namespace Mal {
|
||||
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);
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
@ -65,6 +195,11 @@ namespace Mal {
|
||||
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},
|
||||
@ -80,16 +215,28 @@ namespace Mal {
|
||||
|
||||
{"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},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
296
cs/stepA_more.cs
Normal file
296
cs/stepA_more.cs
Normal file
@ -0,0 +1,296 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
38
cs/types.cs
38
cs/types.cs
@ -199,9 +199,11 @@ namespace Mal {
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -255,16 +257,12 @@ namespace Mal {
|
||||
}
|
||||
public MalHashMap(MalList lst) {
|
||||
value = new Dictionary<String, MalVal>();
|
||||
assoc_BANG(lst.getValue().ToArray());
|
||||
assoc_BANG(lst);
|
||||
}
|
||||
/*
|
||||
public MalHashMap(params MalVal[] mvs) {
|
||||
value = new Dictionary<String, MalVal>();
|
||||
assoc_BANG(mvs);
|
||||
}
|
||||
*/
|
||||
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; }
|
||||
@ -276,15 +274,16 @@ namespace Mal {
|
||||
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;
|
||||
}
|
||||
@ -306,6 +305,9 @@ namespace Mal {
|
||||
this.env = env;
|
||||
this.fparams = fparams;
|
||||
}
|
||||
public MalFunction copy() {
|
||||
return (MalFunction)this.MemberwiseClone();
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
if (ast != null) {
|
||||
|
Loading…
Reference in New Issue
Block a user