views:

325

answers:

1

I have inherited a small scripting language and I am attempting to port it to the DLR so that it is a little easier to manage. So far it has been fairly straight forward. I have run into a problem though attempting to dynamically call members of a variable. The current language runs on .NET and uses a parsing loop and reflection to do this, but I was hoping to get away from that. Here is an example of the script language:

string $system1RemoteUri;
string $dbconnection = $config.GetDBConnection ("somedb");
float $minBad = 0.998;
float $minGood = 0.2;

$systen1RemoteURI, $minBad, and $minGood are variables that will be set in the script, along with $dbconnection. However $dbconnection will get its value from a variable passed in called $config. The 4 variables need to be available to the caller, so they are passed into the lambda, initially as null. Here is the generated Lambda IL (debug view):

.Lambda #Lambda1<Delegate6$1>(
    System.String& $$system1RemoteUri,
    System.String& $$dbconnection,
    System.Double& $$minBad,
    System.Double& $$minGood
    System.Object $$config) {
    .Block() {
        $$minBad = 0.998D;
        $$minGood = 0.2D
    }

    //Some assignment similar to...
    //.Dynamic Call GetDBConnection($config, "somedb");
}

What I am trying to figure out is how to use Expression.Dynamic to emit the $config.GetDBConnection("somedb"). From looking at examples in the Sympl libraries I believe the emitted IL should look like: .Dynamic Call GetdbConnection($config, "somedb") but I cant figure out how to actually emit that from Expression.Dynamic.

It seems to want a CallSiteBinder which I cannot create correctly, and I do not understand what the order of parameters is to Expression.Dynamic, as it seems to only want the "member" being invoked, and not the base.

I do not know the runtime type of $config it is just some object which implements a function called GetDBConnection(string). This is not provided by an interface or base class.

Any help would be appreciated.

+1  A: 

You can either turn this into an InvokeMemberBinder or turn "$config.GetDBConnection" into a GetMember and then do an Invoke on the result of that passing $someDb as the argument.

To implement your GetMemberBinder and InvokeMemberBinder you can use the DLR outer-layer DefaultBinder class. In the latest IronPython/IronRuby source code you can just create a new DefaultBinder instance out of thin air. Then in your FallbackGetMember / FallbackInvoke you can call defaultBinder.GetMember(...) and defaultBinder.Call (which should be renamed Invoke). That'll deal with most .NET types for you. Also all objects which implement IDynamicMetaObjectProvider will work with it as well. For other dynamic operations you can use the other methods on the default binder. And if you want to start customizing your overload resolution and binding rules it has lots of knobs you can turn.

Unfortunately the default binder doesn't have an InvokeMemberBinder implementation right now so you're probably better off w/ GetMember/Invoke.

Dino Viehland
I was just coming back around to answer this as I had figured out basically the same thing. What was throwing me was that I was not realizing I needed to pass the base expression as the first parameter to the Set/Get/Invoke and the rest of the parameters after.
GrayWizardx