diff options
| author | Joel Martin <github@martintribe.org> | 2014-10-09 23:48:47 -0500 |
|---|---|---|
| committer | Joel Martin <github@martintribe.org> | 2015-01-06 21:57:24 -0600 |
| commit | 01c9731649a7ed97fad0bdeac9cb75b7323c0ad6 (patch) | |
| tree | d6d00e574c489b206ddf78adc12c3d535bab7440 /cs | |
| parent | 1771ab50b87c745181e4e30f94b63e3f23d33dac (diff) | |
| download | mal-01c9731649a7ed97fad0bdeac9cb75b7323c0ad6.tar.gz mal-01c9731649a7ed97fad0bdeac9cb75b7323c0ad6.zip | |
All: swap step9,A. Fixes for bash, C, perl.
step9_interop -> stepA_interop
stepA_more -> step9_try
C: fix glib headers
bash: behavior change of declare -A and pattern replacement.
perl: squelch new 5.18 warnings related to switch/given statement.
Also, include some in-progress interop related files.
Diffstat (limited to 'cs')
| -rw-r--r-- | cs/Makefile | 4 | ||||
| -rw-r--r-- | cs/interop.cs | 66 | ||||
| -rw-r--r-- | cs/step9_try.cs (renamed from cs/stepA_more.cs) | 2 | ||||
| -rw-r--r-- | cs/stepA_interop.cs | 288 |
4 files changed, 357 insertions, 3 deletions
diff --git a/cs/Makefile b/cs/Makefile index b7eb023..1fd1f7a 100644 --- a/cs/Makefile +++ b/cs/Makefile @@ -5,7 +5,7 @@ DEBUG = TESTS = SOURCES_BASE = readline.cs types.cs reader.cs printer.cs -SOURCES_LISP = env.cs core.cs stepA_more.cs +SOURCES_LISP = env.cs core.cs stepA_interop.cs SOURCES = $(SOURCES_BASE) $(SOURCES_LISP) OTHER_SOURCES = getline.cs @@ -14,7 +14,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 stepA_more.cs + step8_macros.cs step9_try.cs stepA_interop.cs LIB_SRCS = $(filter-out step%,$(OTHER_SOURCES) $(SOURCES)) diff --git a/cs/interop.cs b/cs/interop.cs new file mode 100644 index 0000000..e383280 --- /dev/null +++ b/cs/interop.cs @@ -0,0 +1,66 @@ +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.CSharp; + +public static class EvalProvider +{ + public static Func<T, TResult> CreateEvalMethod<T, TResult>(string code, string[] usingStatements = null, string[] assemblies = null) + { + Type returnType = typeof(TResult); + Type inputType = typeof(T); + + var includeUsings = new HashSet<string>(new[] { "System" }); + includeUsings.Add(returnType.Namespace); + includeUsings.Add(inputType.Namespace); + if (usingStatements != null) + foreach (var usingStatement in usingStatements) + includeUsings.Add(usingStatement); + + using (CSharpCodeProvider compiler = new CSharpCodeProvider()) + { + var name = "F" + Guid.NewGuid().ToString().Replace("-", string.Empty); + var includeAssemblies = new HashSet<string>(new[] { "system.dll" }); + if (assemblies != null) + foreach (var assembly in assemblies) + includeAssemblies.Add(assembly); + + var parameters = new CompilerParameters(includeAssemblies.ToArray()) + { + GenerateInMemory = true + }; + + string source = string.Format(@" +{0} +namespace {1} +{{ + public static class EvalClass + {{ + public static {2} Eval({3} arg) + {{ + {4} + }} + }} +}}", GetUsing(includeUsings), name, returnType.Name, inputType.Name, code); + + var compilerResult = compiler.CompileAssemblyFromSource(parameters, source); + var compiledAssembly = compilerResult.CompiledAssembly; + var type = compiledAssembly.GetType(string.Format("{0}.EvalClass", name)); + var method = type.GetMethod("Eval"); + return (Func<T, TResult>)Delegate.CreateDelegate(typeof(Func<T, TResult>), method); + } + } + + private static string GetUsing(HashSet<string> usingStatements) + { + StringBuilder result = new StringBuilder(); + foreach (string usingStatement in usingStatements) + { + result.AppendLine(string.Format("using {0};", usingStatement)); + } + return result.ToString(); + } +} + diff --git a/cs/stepA_more.cs b/cs/step9_try.cs index 486c344..ed3417c 100644 --- a/cs/stepA_more.cs +++ b/cs/step9_try.cs @@ -14,7 +14,7 @@ using MalFunction = Mal.types.MalFunction; using Env = Mal.env.Env; namespace Mal { - class stepA_more { + class step9_try { // read static MalVal READ(string str) { return reader.read_str(str); diff --git a/cs/stepA_interop.cs b/cs/stepA_interop.cs new file mode 100644 index 0000000..632d18d --- /dev/null +++ b/cs/stepA_interop.cs @@ -0,0 +1,288 @@ +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using Mal; +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_interop { + // 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)); + } + orig_ast = a2; + env = let_env; + break; + case "quote": + return ast[1]; + case "quasiquote": + orig_ast = quasiquote(ast[1]); + break; + 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 Mal.types.MalException) { + exc = ((Mal.types.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 == Mal.types.Nil || cond == Mal.types.False) { + // eval false slot form + if (ast.size() > 3) { + orig_ast = ast[3]; + } else { + return Mal.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); + } + + static void Main(string[] args) { + string prompt = "user> "; + + // core.cs: defined using C# + var repl_env = new env.Env(null); + foreach (var entry in core.ns) { + repl_env.set(entry.Key, entry.Value); + } + repl_env.set("eval", new MalFunction(a => EVAL(a[0], repl_env))); + MalList _argv = new MalList(); + for (int i=1; i < args.Length; i++) { + _argv.conj_BANG(new MalString(args[i])); + } + repl_env.set("*ARGV*", _argv); + + // core.mal: defined using the language itself + RE(repl_env, "(def! *host-language* \"c#\")"); + RE(repl_env, "(def! not (fn* (a) (if a false true)))"); + RE(repl_env, "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); + 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))))))))"); + + int fileIdx = 0; + if (args.Length > 0 && args[0] == "--raw") { + Mal.readline.mode = Mal.readline.Mode.Raw; + fileIdx = 1; + } + if (args.Length > fileIdx) { + RE(repl_env, "(load-file \"" + args[fileIdx] + "\")"); + return; + } + + // repl loop + RE(repl_env, "(println (str \"Mal [\" *host-language* \"]\"))"); + 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.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; + } + } + } + } +} |
