DLR Hosting in Silverlight
As you probably know, DLR is the dynamic language runtime that provides a common platform for dynamic languages and scripting in .NET. Their two main languages, IronPython and IronRuby, are available to develop your programs and also to be hosted in your programs. DLR hosting means that the users of your program can use scripting in any DLR language, for example to automate your program or to programmatically access the domain model of your application.
I was thinking about adding a capability to plot function graphs like y = cos(x) to my Live Geometry app, so I thought of hosting the DLR in Silverlight to compile and evaluate mathematical expressions.
Fortunately, DLR readily supports this scenario. And fortunately, Tomáš Matoušek, a developer on our IronRuby Team (part of Visual Studio Managed Languages), sits right around the corner from my office and was kind enough to provide great help when I had questions. Big thanks and kudos to Tomáš!
So, to host the DLR in Silverlight, here's the file that I added to my project (you can view my full source code here: https://dynamicgeometry.codeplex.com/SourceControl/ListDownloadableCommits.aspx).
All you need to do is to set up a script runtime, get your language's engine (here we use Python), create a scope for your variables and you're ready to evaluate, execute and compile!
using System;
using DynamicGeometry;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
using Microsoft.Scripting.Silverlight;
namespace SilverlightDG
{
public class DLR : ExpressionCompiler
{
ScriptRuntime runtime;
ScriptEngine engine;
ScriptScope scope;
public DLR()
{
var setup = new ScriptRuntimeSetup();
setup.HostType = typeof(BrowserScriptHost);
setup.LanguageSetups.Add(Python.CreateLanguageSetup(null));
runtime = new ScriptRuntime(setup);
engine = runtime.GetEngine("Python");
scope = engine.CreateScope();
engine.ImportModule("math");
}
public override Func<double, double> Compile(string expression)
{
var source = engine.CreateScriptSourceFromString(
string.Format(@"
from math import *
def y(x):
return {0}
func = y
", expression),
Microsoft.Scripting.SourceCodeKind.File);
CompiledCode code = source.Compile();
code.Execute(scope);
var func = scope.GetVariable<Func<double, double>>("func");
return func;
}
}
}
ExpressionCompiler is my own abstract class that I defined in the DynamicGeometry assembly:
using System;
namespace DynamicGeometry
{
public abstract class ExpressionCompiler
{
public abstract Func<double, double> Compile(string expression);
public static ExpressionCompiler Singleton { get; set; }
}
}
As you see, the service that I need from the DLR is to implement the Compile method, that compiles an expression down to a callable function delegate, which I can then use to evaluate a function at a point.
Finally, just register the DLR as an implementation for my ExpressionCompiler:
ExpressionCompiler.Singleton = new DLR();
And we're ready to go.
Let's go back to the DLR.cs and I'll comment a little more on what's going on. Essentially, to host the DLR you'd need 3 things:
ScriptRuntime runtime;
ScriptEngine engine;
ScriptScope scope;
Runtime is your "world". You load a language-specific engine (like PythonEngine) into the runtime. To create a runtime with a language, one way is to use:
var setup = new ScriptRuntimeSetup();
setup.HostType = typeof(BrowserScriptHost);
setup.LanguageSetups.Add(Python.CreateLanguageSetup(null));
runtime = new ScriptRuntime(setup);
This will work fine in Silverlight, because we use a browser-specific BrowserScriptHost, which does not use the file system. One problem that I had is that I was trying to directly call:
runtime = Python.CreateRuntime();
Which didn't work because it used the default script host (which tried to access the file system) and not the BrowserScriptHost. After you have the runtime, you can get the engine and create a scope in that engine:
engine = runtime.GetEngine("Python");
scope = engine.CreateScope();
Now you're ready to do things like:
var five = engine.Execute("2 + 3", scope);
You can go up to the first code example to see how I declared a function in Python, and converted it to a C# callable Func<double, double> delegate.
Finally, here's the working application (which you can also find at https://geometry.osenkov.com). Press the y = f(x) toolbar button, enter sin(x) and press the Plot button:
Comments
Anonymous
March 28, 2009
PingBack from http://www.alvinashcraft.com/2009/03/28/dew-drop-march-28-2009/Anonymous
April 01, 2009
To avoid doing all that ScriptRuntimeSetup mumbo-jumbo, you can use the bits from http://sdlsdk.codeplex.com (soon to be merged back into DLR codeplex and other distribution channels) and use: var setup = DynamicApplication.CreateRuntimeSetup()Anonymous
April 03, 2009
what about F#? Can we get it into the DLR as wellAnonymous
April 03, 2009
Jan: yes, you can host the DLR in F# and you can call F# from DLR. You can ask the F# guys for details: http://lorgonblog.spaces.live.com - Brian McNamara, F# Dev http://blogs.msdn.com/timng/ - Tim Ng, F# Dev Lead http://blogs.msdn.com/jomo_fisher/ - Jomo Fisher, F# Dev http://blogs.msdn.com/chrsmith/ - Chris Smith, F# QAAnonymous
May 19, 2009
I need JScript Managed in DLR. What should I do? marcelo8690@hotmail.comAnonymous
May 19, 2009
Hi Marcelo, contact someone from the DLR team: http://www.tkachenko.com/blog/archives/000747.htmlAnonymous
October 31, 2009
Wouldn't it be more lightweight to use either ilcalc, flee, or ncalc (all are open sources in codeplex) to evaluate math expressions?Anonymous
October 31, 2009
Hey Justin, please see here: http://blogs.msdn.com/kirillosenkov/archive/2009/10/31/irony.aspx :) KirillAnonymous
January 13, 2011
Hi all Please Let me Know why we use DLR any particular benefit paython over C# statis lang.