Eogene

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

using Microsoft.CSharp;
using Microsoft.VisualBasic;

using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.CodeDom.Compiler;
using System.Reflection;
using System.Reflection.Emit;
using System.Text.RegularExpressions;

namespace ToolsUtil
{
        /// <summary>
        /// An evaluator that receives its arguments as key/value pairs.
        /// </summary>
        public interface IDictionaryEvaluator
        {
            object Run(IDictionary arguments);
        }

        /// <summary>
        /// An evaluator that receives its arguments as a list of values, whose position is important.
        /// </summary>
        public interface IListEvaluator
        {
            object Run(IList values);
        }

        /// <summary>
        /// An evaluator that takes no arguments.
        /// </summary>
        public interface IEvaluator
        {
            object Run();
        }

        /// <summary>用于执行C#代码字符串</summary>
        /// Gene
        public abstract class Compilation
        {
            private static string nxbreAssemblyLocation = String.Empty;
            private const string MainForm_DLL = "MainForm.dll";
            private Compilation() { }

            /// <summary>
            /// Performs an immediate evaluation of an expression that takes no argument.
            /// </summary>
            /// <remarks>
            /// The compiled expression is not cached.
            /// </remarks>
            /// <param name="expression">The C# expression to evaluate.</param>
            /// <returns>An object produced by the expression.</returns>
            public static object Evaluate(string expression)
            {
                string code = "class Evaluator:ToolsUtil.IEvaluator { public object Run() {return ("
                                            + PrepareExpression(expression)
                                            + ");}}";

                return ((IEvaluator)LoadCSClass("Evaluator", code, true)).Run();

            }

            /// <summary>
            /// Performs an immediate evaluation of an expression that takes lists for parameter names and values.
            /// </summary>
            /// <remarks>
            /// The compiled expression is not cached.
            /// </remarks>
            /// <param name="expression">The C# expression to evaluate.</param>
            /// <param name="placeHolderRegexpPattern">The regexp used to recoginize the argument placeholders.</param>
            /// <param name="variableNames">The list of argument names.</param>
            /// <param name="values">The list of values.</param>
            /// <returns>An object produced by the expression.</returns>
            public static object Evaluate(string expression, string placeHolderRegexpPattern, IList variableNames, IList values)
            {
                return NewEvaluator(expression, placeHolderRegexpPattern, variableNames, values).Run(values);
            }

            /// <summary>
            /// Instantiates a new evaluator that receives its arguments as a list of values.
            /// </summary>
            /// <param name="expression">The C# expression to compile.</param>
            /// <param name="placeHolderRegexpPattern">The regexp used to recoginize the argument placeholders.</param>
            /// <param name="variableNames">The list of argument names.</param>
            /// <param name="values">The list of values.</param>
            /// <returns>The compiled evaluator.</returns>
            public static IListEvaluator NewEvaluator(string expression, string placeHolderRegexpPattern, IList variableNames, IList values)
            {
                string code = "class Evaluator:ToolsUtil.IListEvaluator { public object Run(System.Collections.IList values) {return ("
                                            + Regex.Replace(PrepareExpression(expression), placeHolderRegexpPattern, new MatchEvaluator(new ListVariableReplacer(variableNames, values).ReplaceListVariable))
                                            + ");}}";

                return (IListEvaluator)LoadCSClass("Evaluator", code, true);
            }

            /// <summary>
            /// Performs an immediate evaluation of an expression that takes key/value pairs as arguments.
            /// </summary>
            /// <remarks>
            /// The compiled expression is not cached.
            /// </remarks>
            /// <param name="expression">The C# expression to evaluate.</param>
            /// <param name="placeHolderRegexpPattern">The regexp used to recoginize the argument placeholders.</param>
            /// <param name="arguments">The key/value pairs of arguments.</param>
            /// <returns>An object produced by the expression.</returns>
            public static object Evaluate(string expression, string placeHolderRegexpPattern, IDictionary arguments)
            {
                return Evaluate(expression, placeHolderRegexpPattern, null, arguments);
            }

            /// <summary>
            /// Performs an immediate evaluation of an expression that takes key/value pairs as arguments.
            /// </summary>
            /// <remarks>
            /// The compiled expression is not cached.
            /// </remarks>
            /// <param name="expression">The C# expression to evaluate.</param>
            /// <param name="placeHolderRegexpPattern">The regexp used to recoginize the argument placeholders.</param>
            /// <param name="numericArgumentPattern">A regexp pattern used to recognize placeholders for arguments whose names are integers and not strings. Use null if not needed.</param>
            /// <param name="arguments">The key/value pairs of arguments.</param>
            /// <returns>An object produced by the expression.</returns>
            public static object Evaluate(string expression, string placeHolderRegexpPattern, string numericArgumentPattern, IDictionary arguments)
            {
                return NewEvaluator(expression, placeHolderRegexpPattern, numericArgumentPattern, arguments).Run(arguments);
            }

            /// <summary>
            /// Instantiates a new evaluator that receives its arguments as key/value pairs.
            /// </summary>
            /// <param name="expression">The C# expression to evaluate.</param>
            /// <param name="placeHolderRegexpPattern">The regexp used to recoginize the argument placeholders.</param>
            /// <param name="arguments">The key/value pairs of arguments.</param>
            /// <returns>The compiled evaluator.</returns>
            public static IDictionaryEvaluator NewEvaluator(string expression, string placeHolderRegexpPattern, IDictionary arguments)
            {
                return NewEvaluator(expression, placeHolderRegexpPattern, null, arguments);
            }

            public static object Evaluate(string expression, IDictionary arguments)
            {
                string code = "class Evaluator:ToolsUtil.IDictionaryEvaluator { public object Run(System.Collections.IDictionary values) {return (" + expression + ");}}";

                return ((IDictionaryEvaluator)LoadCSClass("Evaluator", code, true)).Run(arguments);
            }

            /// <summary>
            /// Instantiates a new evaluator that receives its arguments as key/value pairs.
            /// </summary>
            /// <param name="expression">The C# expression to evaluate.</param>
            /// <param name="placeHolderRegexpPattern">The regexp used to recoginize the argument placeholders.</param>
            /// <param name="numericArgumentPattern">A regexp pattern used to recognize placeholders for arguments whose names are integers and not strings. Use null if not needed.</param>
            /// <param name="arguments">The key/value pairs of arguments.</param>
            /// <returns>The compiled evaluator.</returns>
            public static IDictionaryEvaluator NewEvaluator(string expression, string placeHolderRegexpPattern, string numericArgumentPattern, IDictionary arguments)
            {
                string code = "class Evaluator:ToolsUtil.IDictionaryEvaluator { public object Run(System.Collections.IDictionary values) {return ("
                    + Regex.Replace(PrepareExpression(expression), placeHolderRegexpPattern, new MatchEvaluator(new DictionaryVariableReplacer(arguments, numericArgumentPattern).ReplaceDictionaryVariable))
                                            + ");}}";

                return (IDictionaryEvaluator)LoadCSClass("Evaluator", code, true);
            }

            // ----- INTERNAL MEMBERS -----------------------------------------------
            static CSharpCodeProvider ccp = new CSharpCodeProvider();
            internal static object LoadCSClass(string targetClassName, string source, bool sourceIsString)
            {
                return LoadClass(ccp, targetClassName, source, sourceIsString);
            }

            static VBCodeProvider vbc = new VBCodeProvider();
            internal static object LoadVBClass(string targetClassName, string source, bool sourceIsString)
            {
                return LoadClass(vbc, targetClassName, source, sourceIsString);
            }

            // ----- PRIVATE MEMBERS -----------------------------------------------

            private class ListVariableReplacer
            {
                private readonly IList variableNames;
                private readonly IList values;

                public ListVariableReplacer(IList variableNames, IList values)
                {
                    this.variableNames = variableNames;
                    this.values = values;
                }

                public string ReplaceListVariable(Match m)
                {
                    int variableIndex = variableNames.IndexOf(m.Groups[1].Value);

                    if (variableIndex >= values.Count) throw new Exception("Not enough values to resolve expression: missing index " + variableIndex);

                    return "((" +
                                 values[variableIndex].GetType().FullName +
                                 ")values[" + variableIndex + "])";
                }
            }

 

            private class DictionaryVariableReplacer
            {
                private readonly IDictionary arguments;
                private readonly Regex numericArgumentRegex;

                public DictionaryVariableReplacer(IDictionary arguments, string numericArgumentPattern)
                {
                    this.arguments = arguments;
                    if (numericArgumentPattern != null) numericArgumentRegex = new Regex(numericArgumentPattern);
                    else numericArgumentRegex = null;
                }

                public string ReplaceDictionaryVariable(Match m)
                {
                    if ((numericArgumentRegex != null) && (numericArgumentRegex.IsMatch(m.Groups[0].Value)))
                    {
                        int variableName = Convert.ToInt32(m.Groups[1].Value);

                        if (!arguments.Contains(variableName)) throw new Exception("Not enough arguments to resolve expression: missing " + variableName);

                        return "((" +
                                     arguments[variableName].GetType().FullName +
                                     ")values[" + variableName + "])";
                    }
                    else
                    {
                        string variableName = m.Groups[1].Value;

                        if (!arguments.Contains(variableName)) throw new Exception("Not enough arguments to resolve expression: missing '" + variableName + "'");

                        return "((" +
                                     arguments[variableName].GetType().FullName +
                                     ")values[\"" + variableName + "\"])";
                    }
                }
            }

            private static string PrepareExpression(string expression)
            {
                if (expression.StartsWith("expr:")) return expression.Substring(5);
                else return expression;
            }

            static CompilerParameters compilerParameters;
            ///<remarks>Brendan Ingram has greatly improved this method.</remarks>  
            private static object LoadClass(CodeDomProvider codeProvider, string targetClassName, string source, bool sourceIsString)
            {
                if (compilerParameters == null)
                {
                    compilerParameters = new CompilerParameters();
                    compilerParameters.GenerateExecutable = false;
                    compilerParameters.IncludeDebugInformation = true;
                    compilerParameters.TreatWarningsAsErrors = false;
                    compilerParameters.GenerateInMemory = true;

                    AddReferencedAssembly(compilerParameters, MainFormAssemblyLocation);
                }

                CompilerResults cr;

                if (sourceIsString)
                {
                    cr = codeProvider.CompileAssemblyFromSource(compilerParameters, source);
                }
                else
                {
                    cr = codeProvider.CompileAssemblyFromFile(compilerParameters, source);
                }

                if (cr.Errors.Count != 0)
                {
                    throw new Exception(GetCompilerErrors(cr));
                }

                // Marcin Kielar - zorba128
                // Under some (strange?) conditions, compilation finishes without errors,
                // but assembly cannot be loaded:
                //  - any of Assembly's methods (here - GetTypes) throws exception
                //  - CreateInstance returns null
                try
                {
                    cr.CompiledAssembly.GetTypes();
                }
                catch (Exception e)
                {
                    throw new Exception("Unable to create evaluator class instance - assembly loading problem", e);
                }

                object evaluatorInstance = cr.CompiledAssembly.CreateInstance(targetClassName);

                if (evaluatorInstance == null)
                {
                    throw new Exception("Unable to create evaluator class instance");
                }

                return evaluatorInstance;
            }

            private static void AddReferencedAssembly(CompilerParameters compilerParameters, string assemblyLocation)
            {
                try
                {
                    if ((assemblyLocation != null)
                        && (assemblyLocation != String.Empty)
                        && (!compilerParameters.ReferencedAssemblies.Contains(assemblyLocation)))
                        compilerParameters.ReferencedAssemblies.Add(assemblyLocation);
                }
                catch (Exception e)
                {
                    Console.WriteLine("Error when adding a reference to: '"
                                                        + assemblyLocation
                                                        + "' (Exception message: "
                                                        + e.Message
                                                        + ")");
                }
            }

            private static string MainFormAssemblyLocation
            {
                get
                {
                    lock (nxbreAssemblyLocation)
                    {

                        if (nxbreAssemblyLocation != String.Empty) return nxbreAssemblyLocation;

                        // ------------------------------------------------------------------------------
                        // Find the MainForm.dll assembly and add reference - the assembly could be found
                        // in one of many different locations, so search in all possible locations.
                        // ------------------------------------------------------------------------------

                        // Look in the current directory.
                        nxbreAssemblyLocation = MainForm_DLL;
                        if (File.Exists(nxbreAssemblyLocation)) return nxbreAssemblyLocation;

                        // Look in the app domains base directory.
                        nxbreAssemblyLocation = AppDomain.CurrentDomain.BaseDirectory + MainForm_DLL;
                        if (File.Exists(nxbreAssemblyLocation)) return nxbreAssemblyLocation;

                        // Look in the bin subdirectory of the app domain base directory (ASP.NET)
                        nxbreAssemblyLocation = AppDomain.CurrentDomain.BaseDirectory + @"bin/" + MainForm_DLL;
                        if (File.Exists(nxbreAssemblyLocation)) return nxbreAssemblyLocation;

                        // Try to use assembly from current location
                        nxbreAssemblyLocation = typeof(Compilation).Assembly.Location;
                        if (File.Exists(nxbreAssemblyLocation)) return nxbreAssemblyLocation;

                        throw new Exception(MainForm_DLL + " is impossible to find");
                    }
                }
            }
          
            private static string GetCompilerErrors(CompilerResults cr)
            {
                string errors = "Compiler returned with result code: " +
                                                cr.NativeCompilerReturnValue.ToString() +
                                                ";\n";

                // If errors occurred during compilation, output the compiler output and errors.
                foreach (CompilerError ce in cr.Errors)
                {
                    if (!(ce.IsWarning))
                    {
                        errors += ("CompilerError::" + ce.ToString() + ";\n");
                    }
                }

                return errors;
            }


            public static void main()
            {
                object obj = ToolsUtil.Compilation.Evaluate("5 + 10 - 100>0?1:0");
                IDictionary values = new Hashtable();
                double i = 0;
                while (i < 100)
                {

                    values["a"] = i;
                    //Console.WriteLine(5 + (System.Double)values["a"]);
                    //Console.WriteLine(ToolsUtil.Compilation.Evaluate("5 + (System.Double)values[\"a\"]", values));
                    //Console.WriteLine(Compilation.Evaluate("5 + System.Math.PI + {var:a}", @"\{var:(?<1>[^}]*)\}", values));

                    Console.WriteLine(i + ":" + Compilation.Evaluate("(5 + 10 - 100>0?1:0) + 5 * 20 - 10/2 "));
                    i++;
                }

            }
        }


}

posted on 2010-11-15 13:36  EoGene  阅读(647)  评论(0编辑  收藏  举报