Click here to Skip to main content
15,879,613 members
Articles / Programming Languages / C#

NanoScript - A Lightweight Scripting Engine for .NET

Rate me:
Please Sign up or sign in to vote.
4.83/5 (44 votes)
29 Sep 2009CPOL2 min read 74.6K   1.4K   148   21
A very small and compact scripting solution for .NET

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:

Screenshot1.PNG

Screenshot2.PNG

Screenshot3.PNG

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:

C#
private void compileAndRunToolStripMenuItem_Click(object sender, EventArgs e)
{
    isCodeCompiled = false;
    // create a new scripting context
    script = new ScriptEngine();
    // reference the required assemblies
    script.References("System.Drawing.dll");
    script.References("System.Windows.Forms.dll");
    // make namespaces visible
    script.Using("System");
    script.Using("System.Drawing");
    script.Using("System.Windows");
    script.Using("System.Windows.Forms");

    // export the functions DrawSprite and DrawSprites to the scripting context
    script.SetMemberFunction( this, "DrawSprite");
    script.SetMemberFunction( this, "DrawSpriteS");
    // set the code stored in textBox1
    script.SetCode(this.textBox1.Text);

    try
    {
        // try to compile
       script.Compile();
    }
    catch (NanoScript.ScriptEngine.ScriptingContextException ex)
    {
        // present the compiling errors in a MessageBox
        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:

C#
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.

C#
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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Austria Austria
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

Comments and Discussions

 
QuestionMethods using ref, is not working Pin
shy0522-Jul-14 23:18
shy0522-Jul-14 23:18 
QuestionFunktionsüberlagerungen Pin
filmee2430-Nov-12 22:46
filmee2430-Nov-12 22:46 
QuestionClasses Pin
filmee2429-Nov-12 8:01
filmee2429-Nov-12 8:01 
GeneralMy vote of 5 Pin
Aamer Alduais5-Aug-12 21:21
Aamer Alduais5-Aug-12 21:21 
GeneralMy vote of 4 Pin
jfriedman14-Mar-12 1:30
jfriedman14-Mar-12 1:30 
Questionsame error Pin
YuraKatz2-Jun-10 20:50
YuraKatz2-Jun-10 20:50 
GeneralGreat! Pin
Dr.Luiji30-Sep-09 21:14
professionalDr.Luiji30-Sep-09 21:14 
GeneralUsing from Silverlight application Pin
AndrusM29-Sep-09 7:31
AndrusM29-Sep-09 7:31 
GeneralRe: Using from Silverlight application Pin
Zimmermann Stephan29-Sep-09 8:29
Zimmermann Stephan29-Sep-09 8:29 
GeneralRe: Using from Silverlight application Pin
AndrusM30-Sep-09 2:00
AndrusM30-Sep-09 2:00 
GeneralCS-Script notes [modified] PinPopular
Oleg Shilo25-Sep-09 14:06
Oleg Shilo25-Sep-09 14:06 
GeneralSources of NanoScript.dlll Pin
wvd_vegt24-Sep-09 0:16
professionalwvd_vegt24-Sep-09 0:16 
GeneralRe: Sources of NanoScript.dlll Pin
Zimmermann Stephan24-Sep-09 4:13
Zimmermann Stephan24-Sep-09 4:13 
QuestionAppDomain unloaded Pin
jcarper23-Sep-09 4:50
jcarper23-Sep-09 4:50 
AnswerRe: AppDomain unloaded Pin
Zimmermann Stephan23-Sep-09 5:56
Zimmermann Stephan23-Sep-09 5:56 
GeneralRe: AppDomain unloaded Pin
Sacha Barber23-Sep-09 10:29
Sacha Barber23-Sep-09 10:29 
GeneralJint Pin
norbert_barbosa23-Sep-09 4:25
norbert_barbosa23-Sep-09 4:25 
GeneralCS-Script Pin
Brad Bruce23-Sep-09 2:11
Brad Bruce23-Sep-09 2:11 
GeneralRe: CS-Script Pin
Zimmermann Stephan23-Sep-09 5:41
Zimmermann Stephan23-Sep-09 5:41 
GeneralRe: CS-Script Pin
Tommy Carlier23-Sep-09 20:31
Tommy Carlier23-Sep-09 20:31 
GeneralRe: CS-Script Pin
Oleg Shilo25-Sep-09 13:23
Oleg Shilo25-Sep-09 13:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.