forked from ArduPilot/MissionPlanner
-
Notifications
You must be signed in to change notification settings - Fork 0
/
CodeGen.cs
368 lines (310 loc) · 14.7 KB
/
CodeGen.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
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 MissionPlanner
{
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;
}
/// <summary>
/// Creawte parameters for compiling
/// </summary>
/// <returns></returns>
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("netstandard.dll");
foreach (var assembly in Assembly.GetExecutingAssembly().GetReferencedAssemblies())
{
var ass = Assembly.ReflectionOnlyLoad(assembly.FullName);
var loc = ass.Location;
var file = Path.GetFileName(loc);
if(!compilerParams.ReferencedAssemblies.Contains(file))
compilerParams.ReferencedAssemblies.Add(file);
}
compilerParams.ReferencedAssemblies.Add("");
//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>
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;
}
/// <summary>
/// Need to change eval string to use .NET Math library
/// </summary>
/// <param name="eval">evaluation expression</param>
/// <returns></returns>
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;
}
/// <summary>
/// Compiles the c# into an assembly if there are no syntax errors
/// </summary>
/// <returns></returns>
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 {0}", ex);
}
}
/// <summary>
/// Runs the Calculate method in our on-the-fly assembly
/// </summary>
/// <param name="results"></param>
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 {0}", 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;
}
/// <summary>
/// Very simplistic getter/setter properties
/// </summary>
/// <param name="propName"></param>
/// <param name="internalName"></param>
/// <param name="type"></param>
/// <returns></returns>
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();
/// <summary>
/// Main driving routine for building a class
/// </summary>
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("MissionPlanner.Utilities"));
myNamespace.Imports.Add(new CodeNamespaceImport("MissionPlanner"));
//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());
}
}
}