/System.Xronos/Compiler.cs
C# | 406 lines | 316 code | 62 blank | 28 comment | 99 complexity | ed6d934b5cfc23b9b7125009f06aefa0 MD5 | raw file
- /* ****************************************************************************
- *
- * Copyright (c) 2008 Stefan Rusek and Benjamin Pollack
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * ***************************************************************************/
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using Microsoft.Linq.Expressions;
- using System.Xronos.Language;
- using System.Xronos.Builtins;
- using System.Xronos.Scripting;
- using Microsoft.Scripting.Ast;
- using System.Xronos.Scripting.Binders;
-
- namespace System.Xronos
- {
- public class VarNotFoundException : Exception
- {
- public VarNotFoundException(Symbol sym, IPersistentMap meta)
- : base("Unable to resolve symbol: " + sym + " in this context" + GetMetaData(meta))
- {
- }
-
- private static string GetMetaData(IPersistentMap meta)
- {
- string lineinfo = null;
- int? line = (int?)RT.Get(meta, RT.Line);
- if (line.HasValue)
- lineinfo = "Line " + line;
-
- string fileinfo = null;
- string filename = (string)RT.Get(meta, RT.Filename);
- if (filename != null)
- fileinfo = "File " + filename;
-
- if (fileinfo != null || lineinfo != null)
- return string.Format(": ({0}{1}{2})", lineinfo, lineinfo != null && fileinfo != null ? " " : "", fileinfo);
- return "";
- }
- }
-
- public class Compiler
- {
- public static LambdaExpression Compile(object code, XronosContext context)
- {
- Statistics.CompilerTime.Start();
- try
- {
- var c = new Compiler();
- return Expression.Lambda(c.CompileToExpression(code, new Scope(context)));
- }
- finally
- {
- Statistics.CompilerTime.Stop();
- }
- }
-
- public static Func<TResult> Compile<TResult>(object code, XronosContext context)
- {
- Statistics.CompilerTime.Start();
- try
- {
- var c = new Compiler();
- var expr = c.CompileToExpression(code, new Scope(context));
- expr = Expression.Convert(expr, typeof(TResult));
- var lambda = Expression.Lambda<Func<TResult>>(expr);
- return lambda.Compile();
- }
- finally
- {
- Statistics.CompilerTime.Stop();
- }
- }
-
- public Expression CompileToCallExpression(ISequence cons, Scope scope)
- {
- var first = cons.first();
- Expression result = null;
-
- var symFirst = first as Symbol;
- if (symFirst != null)
- {
- var sf = SpecialForms.Lookup(symFirst);
- if (sf != null)
- result = FunctionHelper.Invoke<Expression>(sf, this, cons.rest(), scope);
- }
-
- if (result == null)
- {
- var args = CompileArguments(cons, scope);
- result = Expression.Dynamic(BinderFactory.CachedInvoke(args.Length - 1), typeof(object), args);
- }
-
- return CompileMetaData(RT.meta(symFirst), result);
- }
-
- private Expression CompileMetaData(IPersistentMap meta, Expression expr)
- {
- if (meta == null) return expr;
- object filename = RT.Filename.Invoke(meta);
- if (filename == null) return expr;
-
- int? line = (int?)RT.Line.Invoke(meta);
- int? @char = (int?)RT.Char.Invoke(meta);
- int? lineend = (int?)RT.LineEnd.Invoke(meta);
- int? charend = (int?)RT.CharEnd.Invoke(meta);
-
- return Expression.Block(
- Expression.DebugInfo(
- Expression.SymbolDocument((string)RT.Filename.Invoke(meta)),
- line.Value, @char.Value, lineend.Value, charend.Value),
- expr);
- }
-
- public Expression[] CompileArguments(ISequence cons, Scope scope)
- {
- List<Expression> result = new List<Expression>();
- while (cons != null)
- {
- var arg = CompileToExpression(cons.first(), scope);
- if (arg.Type != typeof(object))
- arg = Expression.Convert(arg, typeof(object));
- result.Add(arg);
- cons = cons.rest();
- }
- return result.ToArray();
- }
-
- public Expression CompileToExpression(object form, Scope scope)
- {
- object originalForm = form;
- form = MacroExpand(form);
-
- if (form == null)
- return Expression.Constant(null);
-
- var type = form.GetType();
- if (type.IsValueType
- || type == typeof(string)
- || type == typeof(Keyword))
- return Expression.Constant(form);
-
- var call = form as ISequence;
- if (call != null)
- {
- if (RT.Count(call) == 0)
- return Expression.Constant(PersistentList.Empty);
- else
- return CompileToCallExpression(call, scope);
- }
-
- if (typeof(IPersistentCollection).IsAssignableFrom(type)
- || typeof(IPersistentMap).IsAssignableFrom(type)
- || typeof(IPersistentMap).IsAssignableFrom(type))
- return CompileToList(form, scope);
-
-
- var sym = (Symbol)form;
- if (sym != null)
- {
- var exp = scope[sym];
- if (exp != null)
- return exp;
- }
-
- var varOrType = LookupVarOrType(null, sym);
- if (varOrType == null)
- throw new VarNotFoundException(sym, RT.meta(form));
- Expression expr = Expression.Constant(varOrType);
-
- var var = varOrType as VarBase;
- if (var != null)
- {
- if (var.isMacro())
- throw new CompilerException("Can't take value of a macro: " + var);
- expr = Utils.SimpleCallHelper(expr, expr.Type.GetMethod("get"));
- }
-
- return expr;
- }
-
- private Expression CompileToList(object form, Scope scope)
- {
- IPersistentMap map = form as IPersistentMap;
- ISequence seq = null;
- string call = "list";
- if (map != null)
- {
- call = "hashmap";
- seq = SpecialMacros.SyntaxQuoteReader.FlattenMap(map);
- }
- else
- {
- if (form is IPersistentVector) call = "vector";
- seq = RT.seq(form);
- }
-
- return CompileToExpression(new Cons(Symbol.Create("xronos", call), seq), scope);
- }
-
- internal ConstantExpression CompileToVar(Symbol name, bool create)
- {
- VarBase var;
-
- var = LookupExistingVar(name);
- if (var == null)
- {
- if (!create)
- throw new VarNotFoundException(name, RT.meta(name));
-
- var = CreateNewVar(name);
- }
-
- if (create && var.Namespace != Namespace.CurrentNs.get())
- throw new CompilerException("Name conflict, can't def " + name + " because namespace: " + Namespace.CurrentNs.get().Name + " refer to: " + var);
-
- return Expression.Constant(var);
- }
-
- private static VarBase CreateNewVar(Symbol name)
- {
- IPersistentMap meta = name.meta();
-
- Namespace ns;
- if (name.Namespace != null)
- ns = Namespace.find(Symbol.Create(name.meta(), name.Namespace));
- else
- ns = Namespace.CurrentNs.get();
-
- name = Symbol.Create(name.meta(), name.Name);
-
- Symbol typeName = (Symbol)VarBase.kType.Invoke(meta);
- if (typeName == null)
- return ns.intern<object>(name);
- else
- return MakeVarCreater(typeName)(ns, name);
- }
-
- private static VarBase LookupExistingVar(Symbol name)
- {
- if (name.Namespace == null)
- name = Symbol.Create(name.meta(), Namespace.CurrentNs.get().Name.Name, name.Name);
- return VarBase.find(name);
- }
-
- static Dictionary<Symbol, Func<Namespace, Symbol, VarBase>> varCreaterCache = new Dictionary<Symbol, Func<Namespace, Symbol, VarBase>>();
- private static Func<Namespace, Symbol, VarBase> MakeVarCreater(Symbol typeName)
- {
- Func<Namespace, Symbol, VarBase> func;
- if (varCreaterCache.TryGetValue(typeName, out func))
- return func;
-
- Type t = FindType(typeName);
-
- ParameterExpression pNs = Expression.Parameter(typeof(Namespace), "ns");
- ParameterExpression pName = Expression.Parameter(typeof(Symbol), "name");
-
- func = Expression.Lambda<Func<Namespace, Symbol, VarBase>>(Utils.SimpleCallHelper(pNs, typeof(Namespace).GetMethod("intern").MakeGenericMethod(t), pName), pNs, pName).Compile();
- varCreaterCache[typeName] = func;
-
- return func;
- }
-
- public static Object MacroExpand(Object x)
- {
- Statistics.MacroExpandTime.Start();
- try
- {
- Object result = x;
- do
- {
- x = result;
- result = MacroExpand1(x);
- } while (x != result);
- return result;
- }
- finally
- {
- Statistics.MacroExpandTime.Stop();
- }
- }
-
- public static Object MacroExpand1(Object x)
- {
- Symbol op = null;
-
- var form = x as ISequence;
- if (form != null)
- op = form.first() as Symbol;
-
- if (op != null)
- {
- if (SpecialForms.IsSpecialForm(op))
- return x;
-
- //macro expansion
- var v = LookupMacro(op);
- if (v != null)
- return FunctionHelper.Invoke<object>(v, form.rest());
-
- // (.. target (args)+)
- if (op.Namespace == null && op.Name == "..")
- {
- var target = form.rest().first();
- var calls = form.rest().rest();
-
- do
- {
- target = RT.List(SpecialForms.Dot, target, calls.first());
- calls = calls.rest();
- } while (calls != null);
-
- return target;
- }
-
- // (.member target args+)
- if (op.Namespace == null && op.Name[0] == '.' && op.Name.Length > 1)
- {
- var target = form.rest().first();
- var args = RT.Cons(Symbol.Create(op.meta(), op.Name.Substring(1)), form.rest().rest());
- return RT.List(SpecialForms.Dot, target, args);
- }
-
- // (type. args+)
- if (op.Namespace == null && op.Name[op.Name.Length - 1] == '.' && op.Name.Length > 1)
- {
- var type = Symbol.Create(op.meta(), op.Name.Substring(0, op.Name.Length - 1));
- return RT.Cons(SpecialForms.New, RT.Cons(type, form.rest()));
- }
-
- // TODO: class/math
-
- }
- return x;
- }
-
- private static object LookupMacro(Symbol op)
- {
- var var = LookupVarOrType(null, op) as VarBase;
- if (var == null || !var.isMacro())
- return null;
-
- return var.GetValue();
- }
-
- private static object LookupVarOrType(Namespace ns, Symbol op)
- {
- var cns = Namespace.CurrentNs;
-
- ns = ns ?? Namespace.CurrentNs.get();
- if (op.Namespace != null)
- {
- ns = Namespace.find(Symbol.Create(op.meta(), op.Namespace));
- op = Symbol.Create(op.meta(), op.Name);
- }
-
- if (ns == null)
- return null;
-
- return ns.getMapping().valAt(op, null);
- }
-
- public static object Resolve(Namespace ns, Symbol name)
- {
- return LookupVarOrType(ns, name) ?? FindTypeInReferencedAssemblies(name);
- }
-
- internal static Type FindType(Symbol sname)
- {
- return LookupVarOrType(null, sname) as Type ?? FindTypeInReferencedAssemblies(sname);
- }
-
- private static Type FindTypeInReferencedAssemblies(Symbol sname)
- {
- var name = sname.Name;
- var t = Type.GetType(name, false);
- if (t == null)
- foreach (var item in AppDomain.CurrentDomain.GetAssemblies())
- {
- t = item.GetType(name, false);
- if (t != null) break;
- }
- return t;
- }
- }
- }