Introduction
During the last few days, I was thinking about possibilities to have parts of a .NET application scripted.
I had a look at existing solutions but in the end I created something very simple on my own, based on the built-in .NET compiler.
Nanoscript is the result of my work.
It allows me to:
- Write scripted functions which can be called from the application
- Define new classes inside the script which can be instantiated and used inside the script
- Define global variables which can be used inside and outside of the script
- Export static and member functions to the scripting context and call them from a script
I have added two sample applications to the solution. The first one is a small command-line based application. It just does some elementary tests. You can have a look at it for better understanding. The second one is a window based application, drawing Lissajous curves:
My scripting engine is held very simple because I did not need more functionality than it has now. Basically it just compiles a CS file that is generated on the fly and generates an assembly containing one class that encapsulates all the scripted functions. NanoScript does not make use of an own Application Domain. The generated code will be executed in the current application domain. NanoScript cannot shell execute CS files. It is a simple library. My purpose was to have the possibility to script parts of let's say a simple game. If you Google, you will find some other excellent solutions with much more functionality.
Using the Code
Using Nanoscript is very simple. Just add a reference to the assembly NanoScript.dll and use the namespace NanoScript
. The sample application uses the following function to compile the code that is stored inside a textbox
:
private void compileAndRunToolStripMenuItem_Click(object sender, EventArgs e)
{
isCodeCompiled = false;
script = new ScriptEngine();
script.References("System.Drawing.dll");
script.References("System.Windows.Forms.dll");
script.Using("System");
script.Using("System.Drawing");
script.Using("System.Windows");
script.Using("System.Windows.Forms");
script.SetMemberFunction( this, "DrawSprite");
script.SetMemberFunction( this, "DrawSpriteS");
script.SetCode(this.textBox1.Text);
try
{
script.Compile();
}
catch (NanoScript.ScriptEngine.ScriptingContextException ex)
{
StringBuilder b = new StringBuilder();
List<ScriptEngine.ScriptingContextException.CompileError> cc = ex.errors;
b.AppendLine("There were errors:");
foreach (ScriptEngine.ScriptingContextException.CompileError err in cc)
{
b.AppendLine("Error " + err.errNumber + " at line " +
err.line + " col " + err.column +
" : \"" + err.text+"\"" );
b.AppendLine("code: " + err.codeSnippet);
b.AppendLine();
}
MessageBox.Show(b.ToString(),"Compiling Error(s)");
return;
}
isCodeCompiled = true;
}
The functions DrawSprite
and DrawSpriteS
look like this:
public void DrawSprite(String name, double posx, double posy)
{
if (name == "sprite1")
{
g_mgnd.DrawImage(sprite1, new Point((int)posx, (int)posy));
}
}
public void DrawSpriteS(String name, double posx, double posy, double w, double h)
{
if (name == "sprite1")
{
g_mgnd.DrawImage(sprite1, (float)posx, (float)posy, (float)w, (float)h);
}
}
Now, we can write scripts that draw sprites in our application. I added 7 sample functions that render different Lissajous figures. Selecting Example 1 to 7 fills in the appropriate code into the code text box.
Hitting "Compile and Run" compiles the script, and runs it in the application's main loop which calls the function "main
" in the scripting context 50 times per second.
private void MainFunc()
{
while(true)
{
if (isCodeCompiled)
{
g_mgnd.FillRectangle(Brushes.Black, mgndBounds);
script.Execute("main");
UpdatePicBox();
}
Thread.Sleep(20);
}
}
Of course, this is just a very simple example of how scripting can be used. I just wanted to create a visual example of how scripting can be used in a very simple way. It shows how functions defined in the application can be used inside the script. Theoretically, there are no limits to what you can do using a script engine. My next step will be to integrate this engine into my fractal renderer, adding the functionality to add new fractal types without having to rebuild.
History
- 23.09.2009: Initial version
- 26.09.2009: Removed
TempFileCollection
. Now no temporary files are created when a script is compiled.
I have started programming at the age of 13 on the commodore 64.
Ever since then I have been programming on many systems in many languages.
During the last 12 years I have been working as professional programmer in different companies and different areas.
Now I am working as freelancer programmer / consultant