aboutsummaryrefslogtreecommitdiff
path: root/cs
diff options
context:
space:
mode:
authorJoel Martin <github@martintribe.org>2014-10-09 23:48:47 -0500
committerJoel Martin <github@martintribe.org>2015-01-06 21:57:24 -0600
commit01c9731649a7ed97fad0bdeac9cb75b7323c0ad6 (patch)
treed6d00e574c489b206ddf78adc12c3d535bab7440 /cs
parent1771ab50b87c745181e4e30f94b63e3f23d33dac (diff)
downloadmal-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/Makefile4
-rw-r--r--cs/interop.cs66
-rw-r--r--cs/step9_try.cs (renamed from cs/stepA_more.cs)2
-rw-r--r--cs/stepA_interop.cs288
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;
+ }
+ }
+ }
+ }
+}