mirror of https://github.com/ArduPilot/ardupilot
340 lines
14 KiB
C#
340 lines
14 KiB
C#
|
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 string runCode(string code)
|
|||
|
{
|
|||
|
string answer = "";
|
|||
|
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
static ICodeCompiler CreateCompiler()
|
|||
|
{
|
|||
|
//Create an instance of the C# compiler
|
|||
|
CodeDomProvider codeProvider = null;
|
|||
|
codeProvider = new CSharpCodeProvider();
|
|||
|
ICodeCompiler compiler = codeProvider.CreateCompiler();
|
|||
|
return compiler;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Creawte parameters for compiling
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
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("System.Windows.Forms.dll");
|
|||
|
|
|||
|
//add any aditional references needed
|
|||
|
// foreach (string refAssembly in code.References)
|
|||
|
// compilerParams.ReferencedAssemblies.Add(refAssembly);
|
|||
|
|
|||
|
return compilerParams;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Compiles the code from the code string
|
|||
|
/// </summary>
|
|||
|
/// <param name="compiler"></param>
|
|||
|
/// <param name="parms"></param>
|
|||
|
/// <param name="source"></param>
|
|||
|
/// <returns></returns>
|
|||
|
static private CompilerResults CompileCode(ICodeCompiler 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;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Need to change eval string to use .NET Math library
|
|||
|
/// </summary>
|
|||
|
/// <param name="eval">evaluation expression</param>
|
|||
|
/// <returns></returns>
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Compiles the c# into an assembly if there are no syntax errors
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
static private CompilerResults CompileAssembly()
|
|||
|
{
|
|||
|
// create a compiler
|
|||
|
ICodeCompiler 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();
|
|||
|
|
|||
|
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);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Runs the Calculate method in our on-the-fly assembly
|
|||
|
/// </summary>
|
|||
|
/// <param name="results"></param>
|
|||
|
static private 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 "";
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
static CodeMemberField FieldVariable(string fieldName, string typeName, MemberAttributes accessLevel)
|
|||
|
{
|
|||
|
CodeMemberField field = new CodeMemberField(typeName, fieldName);
|
|||
|
field.Attributes = accessLevel;
|
|||
|
return field;
|
|||
|
}
|
|||
|
static CodeMemberField FieldVariable(string fieldName, Type type, MemberAttributes accessLevel)
|
|||
|
{
|
|||
|
CodeMemberField field = new CodeMemberField(type, fieldName);
|
|||
|
field.Attributes = accessLevel;
|
|||
|
return field;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Very simplistic getter/setter properties
|
|||
|
/// </summary>
|
|||
|
/// <param name="propName"></param>
|
|||
|
/// <param name="internalName"></param>
|
|||
|
/// <param name="type"></param>
|
|||
|
/// <returns></returns>
|
|||
|
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();
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Main driving routine for building a class
|
|||
|
/// </summary>
|
|||
|
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"));
|
|||
|
|
|||
|
//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(string), 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(string)));
|
|||
|
|
|||
|
//Our Calculate Method
|
|||
|
CodeMemberMethod myMethod = new CodeMemberMethod();
|
|||
|
myMethod.Name = "Calculate";
|
|||
|
myMethod.ReturnType = new CodeTypeReference(typeof(string));
|
|||
|
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);
|
|||
|
//write code
|
|||
|
myNamespace.Types.Add(classDeclaration);
|
|||
|
generator.GenerateCodeFromNamespace(myNamespace, sw, codeOpts);
|
|||
|
sw.Flush();
|
|||
|
sw.Close();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|