Click here to Skip to main content
Click here to Skip to main content

NanoScript - A Lightweight Scripting Engine for .NET

By , 29 Sep 2009
Rate this:
Please Sign up or sign in to vote.

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:

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:

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.

License

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

About the Author

Zimmermann Stephan
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

 
QuestionFunktionsüberlagerungen Pinmemberfilmee2430-Nov-12 22:46 
QuestionClasses Pinmemberfilmee2429-Nov-12 8:01 
GeneralMy vote of 5 PinmemberAamer Alduais5-Aug-12 21:21 
GeneralMy vote of 4 Pinmemberjfriedman14-Mar-12 1:30 
Questionsame error Pinmemberbestmeat2-Jun-10 20:50 
GeneralGreat! PinmemberDr.Luiji30-Sep-09 21:14 
GeneralUsing from Silverlight application PinmemberAndrusM29-Sep-09 7:31 
This code uses complier method which is not available in Silverlight.
 
How to run scripts in silverlight application ?
 
Andrus

GeneralRe: Using from Silverlight application Pinmemberzimstep29-Sep-09 8:29 
GeneralRe: Using from Silverlight application PinmemberAndrusM30-Sep-09 2:00 
GeneralCS-Script notes [modified] PinmemberOleg Shilo25-Sep-09 14:06 
GeneralSources of NanoScript.dlll Pinmemberwvd_vegt24-Sep-09 0:16 
GeneralRe: Sources of NanoScript.dlll Pinmemberzimstep24-Sep-09 4:13 
QuestionAppDomain unloaded Pinmemberjcarper23-Sep-09 4:50 
AnswerRe: AppDomain unloaded Pinmemberzimstep23-Sep-09 5:56 
GeneralRe: AppDomain unloaded PinmvpSacha Barber23-Sep-09 10:29 
GeneralJint Pinmembernorbert_barbosa23-Sep-09 4:25 
GeneralCS-Script PinmemberBrad Bruce23-Sep-09 2:11 
GeneralRe: CS-Script Pinmemberzimstep23-Sep-09 5:41 
GeneralRe: CS-Script PinmemberTommy Carlier23-Sep-09 20:31 
GeneralRe: CS-Script PinmemberOleg Shilo25-Sep-09 13:23 

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

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

| Advertise | Privacy | Mobile
Web04 | 2.8.140415.2 | Last Updated 29 Sep 2009
Article Copyright 2009 by Zimmermann Stephan
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid