In this post I’m going to show how the O2’s FluentSharp APIs can be used to create valid C# code that can then be compiled and executed.
There is already a huge amount of C# AST APIs and extension methods in the FluentSharp.REPL which we will use to dynamically create a C# script (i.e. we are going to create an AST object which will then create a C# source code file)
Here are some of the APIs we are going to use:
Since the output of our script is going to be another C# script, the best way to develop it is to use the O2 Platform's Util - REPL Script a Code Editor.h2 script (see the Creating a REPL editor that is linked to a Code editor post for details on how this GUI was created), which can be found here:
and looks like this:
(the C# REPL editor on the left that has the code editor on the right passed as a parameter):
To start with the AST manipulation/creation we are going to create an CompilationUnit object with one AST using statement.
Note how the code editor on the right contains the source code representation of the CompilationUnit object (which is created by the CompilationUnit's csharpCode() extension method). You can't see it on this screenshot, but that C# code generation happens in real-time (i.e. less than 1s to compile, execute and show :) )
The FluenSharp REPL extension methods make it easy to add a class:
… and a method:
… and a method’s body:
… and the method’s return type + return value
… and adding a variable:
… and invoking a method
What is really nice about the last script is that it actually compiles and runs :)
To see it in action, lets save the current script as a *.cs file and open it in the code editor:
where we can compile the source code:
and
execute it:
Note: the previous script used the FluentSharp alert method to show the MessageBox, here is the same result using the .NET MessageBox static method Show:
To handle cases where we need to use the generated AST scripts from the command line (like in an CI environment) here is how to create an exe instead:
Where the script above will save the C# and Exe files into a temp folder:
with the exe’s execution looking like this:
Scripts created during this post
1) First CompilationUnit example
var compilationUnit = new CompilationUnit();
compilationUnit.add_Using("System.AAAA");
var csharpCode = compilationUnit.csharpCode();
codeEditor.set_Text(csharpCode,".cs");return compilationUnit;
//using ICSharpCode.NRefactory.Ast
using O2.API.AST.ExtensionMethods.CSharp
1: var compilationUnit = new CompilationUnit();
3: compilationUnit.add_Using("System.Windows.Forms");
4:
5: var aClass = compilationUnit.add_Type("AClass");
6: var aMethod = aClass.add_Method("AMethod");
7: var body = aMethod.add_Body();
8: aMethod.setReturnType("void");
9:
10: body.add_Variable("message","bar");
11: body.add_Invocation("foo","alert");
12:
13: body.add_Return("A value");
14: var csharpCode = compilationUnit.csharpCode();
15:
16: //codeEditor.set_Text(csharpCode,".cs");
17: codeEditor.open(csharpCode.saveWithExtension(".cs"));
18:
19: return "ok";
20:
21: //using ICSharpCode.NRefactory.Ast
22: //using O2.API.AST.ExtensionMethods.CSharp
3) Script that uses MessageBox’s Show static method
1: var compilationUnit = new CompilationUnit();
2: compilationUnit.add_Using("System.Windows.Forms");
3: compilationUnit.add_Using("System");
4:
5: var aClass = compilationUnit.add_Type("AClass");
6: var aMethod = aClass.add_Method("AMethod")
7: .setReturnType("void");
8:
9: var body = aMethod.add_Body();
10: body.add_Variable("message","hello world");
11: body.add_Invocation("MessageBox","Show",
12: "message".identifier());
13:
14: var csharpCode = compilationUnit.csharpCode();
15:
16: //codeEditor.set_Text(csharpCode,".cs");
17: var tempFile = csharpCode.saveWithExtension(".cs");
18: codeEditor.open(tempFile)
19: .compileCSSharpFile();
20:
21:
22: return "ok";
24: //using ICSharpCode.NRefactory.Ast
25: //using O2.API.AST.ExtensionMethods.CSharp
1: var compilationUnit = new CompilationUnit();
2: compilationUnit.add_Using("System.Windows.Forms");
3: compilationUnit.add_Using("System");
4:
5: var aClass = compilationUnit.add_Type("AClass");
6: var aMethod = aClass.add_Method("Main")
7: .setReturnType("void")
8: .public_Static();
9:
10:
11: var body = aMethod.add_Body();
12: body.add_Variable("message","hello world (in MessageBox)");
13: body.add_Invocation("Console","Write", "Hello world (in Console)");
14: body.add_Invocation("MessageBox","Show",
15: "message".identifier());
16:
17: var csharpCode = compilationUnit.csharpCode();
18: csharpCode = csharpCode.replace("Main()", "Main(string[] parameters)");
19:
20: var tempDir = "_fromAstToExe".tempDir(false);
21: var tempFile = tempDir.pathCombine("AClass.cs");
22:
23: csharpCode.saveAs(tempFile);
24:
25: var exe = tempFile.compileToExe("AClass", tempDir);
26: return exe;
27:
28: //exe.startProcess();
29:
30: return "ok";
31: //using ICSharpCode.NRefactory.Ast
32: //using O2.API.AST.ExtensionMethods.CSharp
33: //O2File:_Extra_methods_To_Add_to_Main_CodeBase.cs