PageRenderTime 41ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/System.Xronos/Compiler.cs

https://bitbucket.org/stefanrusek/xronos
C# | 406 lines | 316 code | 62 blank | 28 comment | 99 complexity | ed6d934b5cfc23b9b7125009f06aefa0 MD5 | raw file
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) 2008 Stefan Rusek and Benjamin Pollack
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining a copy
  6. * of this software and associated documentation files (the "Software"), to deal
  7. * in the Software without restriction, including without limitation the rights
  8. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. * copies of the Software, and to permit persons to whom the Software is
  10. * furnished to do so, subject to the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be included in
  13. * all copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. * THE SOFTWARE.
  22. *
  23. * ***************************************************************************/
  24. using System;
  25. using System.Collections.Generic;
  26. using System.Linq;
  27. using System.Text;
  28. using Microsoft.Linq.Expressions;
  29. using System.Xronos.Language;
  30. using System.Xronos.Builtins;
  31. using System.Xronos.Scripting;
  32. using Microsoft.Scripting.Ast;
  33. using System.Xronos.Scripting.Binders;
  34. namespace System.Xronos
  35. {
  36. public class VarNotFoundException : Exception
  37. {
  38. public VarNotFoundException(Symbol sym, IPersistentMap meta)
  39. : base("Unable to resolve symbol: " + sym + " in this context" + GetMetaData(meta))
  40. {
  41. }
  42. private static string GetMetaData(IPersistentMap meta)
  43. {
  44. string lineinfo = null;
  45. int? line = (int?)RT.Get(meta, RT.Line);
  46. if (line.HasValue)
  47. lineinfo = "Line " + line;
  48. string fileinfo = null;
  49. string filename = (string)RT.Get(meta, RT.Filename);
  50. if (filename != null)
  51. fileinfo = "File " + filename;
  52. if (fileinfo != null || lineinfo != null)
  53. return string.Format(": ({0}{1}{2})", lineinfo, lineinfo != null && fileinfo != null ? " " : "", fileinfo);
  54. return "";
  55. }
  56. }
  57. public class Compiler
  58. {
  59. public static LambdaExpression Compile(object code, XronosContext context)
  60. {
  61. Statistics.CompilerTime.Start();
  62. try
  63. {
  64. var c = new Compiler();
  65. return Expression.Lambda(c.CompileToExpression(code, new Scope(context)));
  66. }
  67. finally
  68. {
  69. Statistics.CompilerTime.Stop();
  70. }
  71. }
  72. public static Func<TResult> Compile<TResult>(object code, XronosContext context)
  73. {
  74. Statistics.CompilerTime.Start();
  75. try
  76. {
  77. var c = new Compiler();
  78. var expr = c.CompileToExpression(code, new Scope(context));
  79. expr = Expression.Convert(expr, typeof(TResult));
  80. var lambda = Expression.Lambda<Func<TResult>>(expr);
  81. return lambda.Compile();
  82. }
  83. finally
  84. {
  85. Statistics.CompilerTime.Stop();
  86. }
  87. }
  88. public Expression CompileToCallExpression(ISequence cons, Scope scope)
  89. {
  90. var first = cons.first();
  91. Expression result = null;
  92. var symFirst = first as Symbol;
  93. if (symFirst != null)
  94. {
  95. var sf = SpecialForms.Lookup(symFirst);
  96. if (sf != null)
  97. result = FunctionHelper.Invoke<Expression>(sf, this, cons.rest(), scope);
  98. }
  99. if (result == null)
  100. {
  101. var args = CompileArguments(cons, scope);
  102. result = Expression.Dynamic(BinderFactory.CachedInvoke(args.Length - 1), typeof(object), args);
  103. }
  104. return CompileMetaData(RT.meta(symFirst), result);
  105. }
  106. private Expression CompileMetaData(IPersistentMap meta, Expression expr)
  107. {
  108. if (meta == null) return expr;
  109. object filename = RT.Filename.Invoke(meta);
  110. if (filename == null) return expr;
  111. int? line = (int?)RT.Line.Invoke(meta);
  112. int? @char = (int?)RT.Char.Invoke(meta);
  113. int? lineend = (int?)RT.LineEnd.Invoke(meta);
  114. int? charend = (int?)RT.CharEnd.Invoke(meta);
  115. return Expression.Block(
  116. Expression.DebugInfo(
  117. Expression.SymbolDocument((string)RT.Filename.Invoke(meta)),
  118. line.Value, @char.Value, lineend.Value, charend.Value),
  119. expr);
  120. }
  121. public Expression[] CompileArguments(ISequence cons, Scope scope)
  122. {
  123. List<Expression> result = new List<Expression>();
  124. while (cons != null)
  125. {
  126. var arg = CompileToExpression(cons.first(), scope);
  127. if (arg.Type != typeof(object))
  128. arg = Expression.Convert(arg, typeof(object));
  129. result.Add(arg);
  130. cons = cons.rest();
  131. }
  132. return result.ToArray();
  133. }
  134. public Expression CompileToExpression(object form, Scope scope)
  135. {
  136. object originalForm = form;
  137. form = MacroExpand(form);
  138. if (form == null)
  139. return Expression.Constant(null);
  140. var type = form.GetType();
  141. if (type.IsValueType
  142. || type == typeof(string)
  143. || type == typeof(Keyword))
  144. return Expression.Constant(form);
  145. var call = form as ISequence;
  146. if (call != null)
  147. {
  148. if (RT.Count(call) == 0)
  149. return Expression.Constant(PersistentList.Empty);
  150. else
  151. return CompileToCallExpression(call, scope);
  152. }
  153. if (typeof(IPersistentCollection).IsAssignableFrom(type)
  154. || typeof(IPersistentMap).IsAssignableFrom(type)
  155. || typeof(IPersistentMap).IsAssignableFrom(type))
  156. return CompileToList(form, scope);
  157. var sym = (Symbol)form;
  158. if (sym != null)
  159. {
  160. var exp = scope[sym];
  161. if (exp != null)
  162. return exp;
  163. }
  164. var varOrType = LookupVarOrType(null, sym);
  165. if (varOrType == null)
  166. throw new VarNotFoundException(sym, RT.meta(form));
  167. Expression expr = Expression.Constant(varOrType);
  168. var var = varOrType as VarBase;
  169. if (var != null)
  170. {
  171. if (var.isMacro())
  172. throw new CompilerException("Can't take value of a macro: " + var);
  173. expr = Utils.SimpleCallHelper(expr, expr.Type.GetMethod("get"));
  174. }
  175. return expr;
  176. }
  177. private Expression CompileToList(object form, Scope scope)
  178. {
  179. IPersistentMap map = form as IPersistentMap;
  180. ISequence seq = null;
  181. string call = "list";
  182. if (map != null)
  183. {
  184. call = "hashmap";
  185. seq = SpecialMacros.SyntaxQuoteReader.FlattenMap(map);
  186. }
  187. else
  188. {
  189. if (form is IPersistentVector) call = "vector";
  190. seq = RT.seq(form);
  191. }
  192. return CompileToExpression(new Cons(Symbol.Create("xronos", call), seq), scope);
  193. }
  194. internal ConstantExpression CompileToVar(Symbol name, bool create)
  195. {
  196. VarBase var;
  197. var = LookupExistingVar(name);
  198. if (var == null)
  199. {
  200. if (!create)
  201. throw new VarNotFoundException(name, RT.meta(name));
  202. var = CreateNewVar(name);
  203. }
  204. if (create && var.Namespace != Namespace.CurrentNs.get())
  205. throw new CompilerException("Name conflict, can't def " + name + " because namespace: " + Namespace.CurrentNs.get().Name + " refer to: " + var);
  206. return Expression.Constant(var);
  207. }
  208. private static VarBase CreateNewVar(Symbol name)
  209. {
  210. IPersistentMap meta = name.meta();
  211. Namespace ns;
  212. if (name.Namespace != null)
  213. ns = Namespace.find(Symbol.Create(name.meta(), name.Namespace));
  214. else
  215. ns = Namespace.CurrentNs.get();
  216. name = Symbol.Create(name.meta(), name.Name);
  217. Symbol typeName = (Symbol)VarBase.kType.Invoke(meta);
  218. if (typeName == null)
  219. return ns.intern<object>(name);
  220. else
  221. return MakeVarCreater(typeName)(ns, name);
  222. }
  223. private static VarBase LookupExistingVar(Symbol name)
  224. {
  225. if (name.Namespace == null)
  226. name = Symbol.Create(name.meta(), Namespace.CurrentNs.get().Name.Name, name.Name);
  227. return VarBase.find(name);
  228. }
  229. static Dictionary<Symbol, Func<Namespace, Symbol, VarBase>> varCreaterCache = new Dictionary<Symbol, Func<Namespace, Symbol, VarBase>>();
  230. private static Func<Namespace, Symbol, VarBase> MakeVarCreater(Symbol typeName)
  231. {
  232. Func<Namespace, Symbol, VarBase> func;
  233. if (varCreaterCache.TryGetValue(typeName, out func))
  234. return func;
  235. Type t = FindType(typeName);
  236. ParameterExpression pNs = Expression.Parameter(typeof(Namespace), "ns");
  237. ParameterExpression pName = Expression.Parameter(typeof(Symbol), "name");
  238. func = Expression.Lambda<Func<Namespace, Symbol, VarBase>>(Utils.SimpleCallHelper(pNs, typeof(Namespace).GetMethod("intern").MakeGenericMethod(t), pName), pNs, pName).Compile();
  239. varCreaterCache[typeName] = func;
  240. return func;
  241. }
  242. public static Object MacroExpand(Object x)
  243. {
  244. Statistics.MacroExpandTime.Start();
  245. try
  246. {
  247. Object result = x;
  248. do
  249. {
  250. x = result;
  251. result = MacroExpand1(x);
  252. } while (x != result);
  253. return result;
  254. }
  255. finally
  256. {
  257. Statistics.MacroExpandTime.Stop();
  258. }
  259. }
  260. public static Object MacroExpand1(Object x)
  261. {
  262. Symbol op = null;
  263. var form = x as ISequence;
  264. if (form != null)
  265. op = form.first() as Symbol;
  266. if (op != null)
  267. {
  268. if (SpecialForms.IsSpecialForm(op))
  269. return x;
  270. //macro expansion
  271. var v = LookupMacro(op);
  272. if (v != null)
  273. return FunctionHelper.Invoke<object>(v, form.rest());
  274. // (.. target (args)+)
  275. if (op.Namespace == null && op.Name == "..")
  276. {
  277. var target = form.rest().first();
  278. var calls = form.rest().rest();
  279. do
  280. {
  281. target = RT.List(SpecialForms.Dot, target, calls.first());
  282. calls = calls.rest();
  283. } while (calls != null);
  284. return target;
  285. }
  286. // (.member target args+)
  287. if (op.Namespace == null && op.Name[0] == '.' && op.Name.Length > 1)
  288. {
  289. var target = form.rest().first();
  290. var args = RT.Cons(Symbol.Create(op.meta(), op.Name.Substring(1)), form.rest().rest());
  291. return RT.List(SpecialForms.Dot, target, args);
  292. }
  293. // (type. args+)
  294. if (op.Namespace == null && op.Name[op.Name.Length - 1] == '.' && op.Name.Length > 1)
  295. {
  296. var type = Symbol.Create(op.meta(), op.Name.Substring(0, op.Name.Length - 1));
  297. return RT.Cons(SpecialForms.New, RT.Cons(type, form.rest()));
  298. }
  299. // TODO: class/math
  300. }
  301. return x;
  302. }
  303. private static object LookupMacro(Symbol op)
  304. {
  305. var var = LookupVarOrType(null, op) as VarBase;
  306. if (var == null || !var.isMacro())
  307. return null;
  308. return var.GetValue();
  309. }
  310. private static object LookupVarOrType(Namespace ns, Symbol op)
  311. {
  312. var cns = Namespace.CurrentNs;
  313. ns = ns ?? Namespace.CurrentNs.get();
  314. if (op.Namespace != null)
  315. {
  316. ns = Namespace.find(Symbol.Create(op.meta(), op.Namespace));
  317. op = Symbol.Create(op.meta(), op.Name);
  318. }
  319. if (ns == null)
  320. return null;
  321. return ns.getMapping().valAt(op, null);
  322. }
  323. public static object Resolve(Namespace ns, Symbol name)
  324. {
  325. return LookupVarOrType(ns, name) ?? FindTypeInReferencedAssemblies(name);
  326. }
  327. internal static Type FindType(Symbol sname)
  328. {
  329. return LookupVarOrType(null, sname) as Type ?? FindTypeInReferencedAssemblies(sname);
  330. }
  331. private static Type FindTypeInReferencedAssemblies(Symbol sname)
  332. {
  333. var name = sname.Name;
  334. var t = Type.GetType(name, false);
  335. if (t == null)
  336. foreach (var item in AppDomain.CurrentDomain.GetAssemblies())
  337. {
  338. t = item.GetType(name, false);
  339. if (t != null) break;
  340. }
  341. return t;
  342. }
  343. }
  344. }