using System; using System.Text; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using System.CodeDom; using Microsoft.CSharp; using System.CodeDom.Compiler; using System.Reflection; using System.IO; using System.Text.RegularExpressions; namespace ArdupilotMega { static class CodeGen { public static object runCode(string code) { object answer = null; GetMathMemberNames(); // change evaluation string to pick up Math class members string expression = RefineEvaluationString(code); // build the class using codedom BuildClass(expression); // compile the class into an in-memory assembly. // if it doesn't compile, show errors in the window CompilerResults results = CompileAssembly(); Console.WriteLine("...........................\r\n"); Console.WriteLine(_source.ToString()); // if the code compiled okay, // run the code using the new assembly (which is inside the results) if (results != null && results.CompiledAssembly != null) { // run the evaluation function answer = RunCode(results); } else { } return answer; } public static CodeDomProvider CreateCompiler() { //Create an instance of the C# compiler CodeDomProvider codeProvider = CodeDomProvider.CreateProvider("CSharp"); //ICodeCompiler compiler = codeProvider.CreateCompiler(); return codeProvider; } /// /// Creawte parameters for compiling /// /// public static CompilerParameters CreateCompilerParameters() { //add compiler parameters and assembly references CompilerParameters compilerParams = new CompilerParameters(); compilerParams.CompilerOptions = "/target:library /optimize"; compilerParams.GenerateExecutable = false; compilerParams.GenerateInMemory = true; compilerParams.IncludeDebugInformation = false; compilerParams.ReferencedAssemblies.Add("mscorlib.dll"); compilerParams.ReferencedAssemblies.Add("System.dll"); compilerParams.ReferencedAssemblies.Add(Application.ExecutablePath); compilerParams.ReferencedAssemblies.Add(""); //add any aditional references needed // foreach (string refAssembly in code.References) // compilerParams.ReferencedAssemblies.Add(refAssembly); return compilerParams; } /// /// Compiles the code from the code string /// /// /// /// /// public static CompilerResults CompileCode(CodeDomProvider compiler, CompilerParameters parms, string source) { //actually compile the code CompilerResults results = compiler.CompileAssemblyFromSource( parms, source); //Do we have any compiler errors? if (results.Errors.Count > 0) { foreach (CompilerError error in results.Errors) Console.WriteLine("Compile Error:" + error.ErrorText); return null; } return results; } /// /// Need to change eval string to use .NET Math library /// /// evaluation expression /// public static string RefineEvaluationString(string eval) { // look for regular expressions with only letters Regex regularExpression = new Regex("[a-zA-Z_]+"); // track all functions and constants in the evaluation expression we already replaced ArrayList replacelist = new ArrayList(); // find all alpha words inside the evaluation function that are possible functions MatchCollection matches = regularExpression.Matches(eval); foreach (Match m in matches) { // if the word is found in the math member map, add a Math prefix to it bool isContainedInMathLibrary = _mathMembersMap[m.Value.ToUpper()] != null; if (replacelist.Contains(m.Value) == false && isContainedInMathLibrary) { eval = eval.Replace(m.Value, "Math." + _mathMembersMap[m.Value.ToUpper()]); } // we matched it already, so don't allow us to replace it again replacelist.Add(m.Value); } // return the modified evaluation string return eval; } /// /// Compiles the c# into an assembly if there are no syntax errors /// /// public static CompilerResults CompileAssembly() { // create a compiler CodeDomProvider compiler = CreateCompiler(); // get all the compiler parameters CompilerParameters parms = CreateCompilerParameters(); // compile the code into an assembly CompilerResults results = CompileCode(compiler, parms, _source.ToString()); return results; } static ArrayList _mathMembers = new ArrayList(); static Hashtable _mathMembersMap = new Hashtable(); public static void GetMathMemberNames() { // get a reflected assembly of the System assembly Assembly systemAssembly = Assembly.GetAssembly(typeof(System.Math)); try { //cant call the entry method if the assembly is null if (systemAssembly != null) { //Use reflection to get a reference to the Math class Module[] modules = systemAssembly.GetModules(false); Type[] types = modules[0].GetTypes(); //loop through each class that was defined and look for the first occurrance of the Math class foreach (Type type in types) { if (type.Name == "Math") { // get all of the members of the math class and map them to the same member // name in uppercase MemberInfo[] mis = type.GetMembers(); foreach (MemberInfo mi in mis) { _mathMembers.Add(mi.Name); _mathMembersMap[mi.Name.ToUpper()] = mi.Name; } } //if the entry point method does return in Int32, then capture it and return it } //if it got here, then there was no entry point method defined. Tell user about it } } catch (Exception ex) { Console.WriteLine("Error: An exception occurred while executing the script", ex); } } /// /// Runs the Calculate method in our on-the-fly assembly /// /// public static string RunCode(CompilerResults results) { Assembly executingAssembly = results.CompiledAssembly; try { //cant call the entry method if the assembly is null if (executingAssembly != null) { object assemblyInstance = executingAssembly.CreateInstance("ExpressionEvaluator.Calculator"); //Use reflection to call the static Main function Module[] modules = executingAssembly.GetModules(false); Type[] types = modules[0].GetTypes(); //loop through each class that was defined and look for the first occurrance of the entry point method foreach (Type type in types) { MethodInfo[] mis = type.GetMethods(); foreach (MethodInfo mi in mis) { if (mi.Name == "Calculate") { object result = mi.Invoke(assemblyInstance, null); return result.ToString(); } } } } } catch (Exception ex) { Console.WriteLine("Error: An exception occurred while executing the script", ex); } return ""; } public static CodeMemberField FieldVariable(string fieldName, string typeName, MemberAttributes accessLevel) { CodeMemberField field = new CodeMemberField(typeName, fieldName); field.Attributes = accessLevel; return field; } public static CodeMemberField FieldVariable(string fieldName, Type type, MemberAttributes accessLevel) { CodeMemberField field = new CodeMemberField(type, fieldName); field.Attributes = accessLevel; return field; } /// /// Very simplistic getter/setter properties /// /// /// /// /// public static CodeMemberProperty MakeProperty(string propertyName, string internalName, Type type) { CodeMemberProperty myProperty = new CodeMemberProperty(); myProperty.Name = propertyName; myProperty.Comments.Add(new CodeCommentStatement(String.Format("The {0} property is the returned result", propertyName))); myProperty.Attributes = MemberAttributes.Public; myProperty.Type = new CodeTypeReference(type); myProperty.HasGet = true; myProperty.GetStatements.Add( new CodeMethodReturnStatement( new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), internalName))); myProperty.HasSet = true; myProperty.SetStatements.Add( new CodeAssignStatement( new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), internalName), new CodePropertySetValueReferenceExpression())); return myProperty; } static StringBuilder _source = new StringBuilder(); /// /// Main driving routine for building a class /// public static void BuildClass(string expression) { // need a string to put the code into _source = new StringBuilder(); StringWriter sw = new StringWriter(_source); //Declare your provider and generator CSharpCodeProvider codeProvider = new CSharpCodeProvider(); ICodeGenerator generator = codeProvider.CreateGenerator(sw); CodeGeneratorOptions codeOpts = new CodeGeneratorOptions(); CodeNamespace myNamespace = new CodeNamespace("ExpressionEvaluator"); myNamespace.Imports.Add(new CodeNamespaceImport("System")); myNamespace.Imports.Add(new CodeNamespaceImport("System.Windows.Forms")); myNamespace.Imports.Add(new CodeNamespaceImport("ArdupilotMega")); //Build the class declaration and member variables CodeTypeDeclaration classDeclaration = new CodeTypeDeclaration(); classDeclaration.IsClass = true; classDeclaration.Name = "Calculator"; classDeclaration.Attributes = MemberAttributes.Public; classDeclaration.Members.Add(FieldVariable("answer", typeof(object), MemberAttributes.Private)); //default constructor CodeConstructor defaultConstructor = new CodeConstructor(); defaultConstructor.Attributes = MemberAttributes.Public; defaultConstructor.Comments.Add(new CodeCommentStatement("Default Constructor for class", true)); defaultConstructor.Statements.Add(new CodeSnippetStatement("//TODO: implement default constructor")); classDeclaration.Members.Add(defaultConstructor); //property classDeclaration.Members.Add(MakeProperty("Answer", "answer", typeof(object))); //Our Calculate Method /* CodeMemberMethod myMethod = new CodeMemberMethod(); myMethod.Name = "Calculate"; myMethod.ReturnType = new CodeTypeReference(typeof(object)); myMethod.Comments.Add(new CodeCommentStatement("Calculate an expression", true)); myMethod.Attributes = MemberAttributes.Public; myMethod.Statements.Add(new CodeAssignStatement(new CodeSnippetExpression("Object obj"), new CodeSnippetExpression(expression))); //myMethod.Statements.Add(new CodeAssignStatement(new CodeSnippetExpression("Answer"), new CodeSnippetExpression("obj.ToString()"))); myMethod.Statements.Add( new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "Answer"))); classDeclaration.Members.Add(myMethod); */ classDeclaration.Members.Add(FieldVariable("customforusenumber", typeof(double), MemberAttributes.Public)); classDeclaration.Members.Add(FieldVariable("customforuseobject", typeof(object), MemberAttributes.Public)); CodeSnippetTypeMember myMethod = new CodeSnippetTypeMember(); myMethod.Text = expression; classDeclaration.Members.Add(myMethod); //write code myNamespace.Types.Add(classDeclaration); generator.GenerateCodeFromNamespace(myNamespace, sw, codeOpts); sw.Flush(); sw.Close(); Console.Write(sw.ToString()); } } }