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++;
}
}
}
}