First time DLR language writer, how do I get items from the scope?

Apr 24, 2009 at 1:37 PM
Edited Apr 24, 2009 at 2:23 PM
I can't figure out how to get access to variables that are registered in the ScriptScope using the latest DLR.

I have a ScriptScope where I add a new delegate:

fGlobals.SetVariable("write", lDel);

which is a delegate with an array of object parameter that simply writes all items to the console.

In my generator for Indentifier tokens I'm delegating all variables to the global scope.

Where fScopeParameter is the first parameter for the lambda and indeed contains that.

return Expression.Dynamic(fLanguage.CreateGetMemberBinder(name, false), typeof(Object), fScopeParam);

Obviously, this doesn't work, using the "UseCodeContext" & CodeContext variable gives me the same, both triggers an "Operation not Implemented" exception.

I'm just trying to figure this out for the first time and obvious am missing some steps, I tried looking at the IronPython & IronRuby ones which are too complex for me to understand, the ToyScript doesn't seem to allow uisng scope this way at all. I'm new at this, so I'm probably missing some steps, what do I need to do to get the "write" function working, or at least return "write" as a variable to me?

The "Creating your own DLR Language" tutorial uses Ast.Read(SymbolID) but that doesn't seem to exist anymore.

Apr 24, 2009 at 3:00 PM
First of all, you shouldn't be using CodeContext as it is IronPython specific class that we've haven't managed to remove from DLR just yet.
To plug your language into hosting API you need to implement a subclass of LanguageContext and override CompileSourceCode method. There you call your parser and create an Expression<Func<Scope, object>>. This means that your top-level code method takes a single parameter of type Scope and returns an object. Inside this method you can simply access the scope as any other .NET object.

Next step is to implement a subclass of ScriptCode that you wrap the resulting DLR AST (an Expression<Func<Scope, object>>) into. ScriptScope is an abstract class that has 2 Run methods to override. One takes a scope the other doesn't. You can implement the latter one so that it calls the former with a 'new Scope()'. The former overload is used for invocation of your code against a scope. You can compile the expression tree Expression<Func<Scope, object>> into a delegate, call it with the scope parameter and return back its value. You might want to cache the compiled delegate on your ScriptCode subclass.

That should be it.
Apr 24, 2009 at 3:09 PM
Edited Apr 24, 2009 at 3:09 PM

I;ve dropped the CodeContext, part and am using the the DlrMainCallTarget (will change to a Func<Scope, object> in a while), my main concern was part about getting a variable from a ScriptScope. When I pass a variable from the top scope (inside the hosting part of my test app), how do I get my language to create an expression that tries to fetch values from that, like the old "Ast.Read()" api did. I can't find any way of going from an identifier in my script to get it to call TryLookupGlobal() at runtime. Do I have to create a custom CallSiteBinder that uses the lamba's first parameter and manually calls it's TryGet* methods or is there an easy way of finding external identifiers?
Apr 24, 2009 at 3:19 PM
Having Scope instance in hand in your top-level code you can just call its methods using Expression.Call(scopeParameter, method info, parameters).
Apr 24, 2009 at 3:27 PM
Edited Apr 24, 2009 at 3:28 PM

yeah, so the way to get the variable "write" I register with:

ScriptScope fGlobals = ....;
fGlobals.SetVariable("write", lDel);

In the Host of the script engine, the AST expression I should use is something like:

Expression lTempVariable = .... ;// some var
return Expression.Block(
  Expression.Call(fScopeParameter, 'TryLookupName', Utils.Constant(SymbolTable.StringToId(aMyIdentifier), lTempVariable)),

Or is there an easier way of dealing with lookups ?

note that since I'm completely new to the DLR, it's quite possible that I my whole approach with external things in the script engine is wrong.
Apr 27, 2009 at 9:55 AM
Ended up with :
  var lSymCall = Expression.Call(typeof(SymbolTable).GetMethod("StringToId"), Expression.Constant(name));
  return Expression.Call(fScopeParam, "LookupName", new Type[]{}, fLanguageContextParam, lSymCall);

Which works great, thanks for the help.