[IronPython] Building via AST

Dino Viehland dinov at microsoft.com
Tue Apr 13 02:49:02 CEST 2010


This might be possible.  If you wrap this all up in a PythonAst object (calling the constructor which takes a CompilerContext), call Bind on it then you should get a LambdaExpression back out.  You can Compile() on that.

But it’s not like this is well traveled territory and this only applies to 2.6.1 (before that the trees weren’t DLR ASTs so they weren’t reducable).  When we do this ourselves we also call the produced delegate and flow in some data.  The delegate is going to want at least a FunctionCode object as an argument but I think you could get away with passing null (at least as long as no exceptions are thrown).  The delegate might also want a CodeContext object as well depending on the compilation mode we end up using (which is based on the CompilerContext you give us).  This you wouldn’t be able to get away w/ passing null.  But you can get one by doing new ModuleContext(new PythonDictionary(), pythonContext).GlobalContext.  The HostingHelpers class can give you a LanguageContext from the ScriptEngine for Python which you can cast to a PythonContext.

Let me know if it works! ☺

From: users-bounces at lists.ironpython.com [mailto:users-bounces at lists.ironpython.com] On Behalf Of Justin Chase
Sent: Monday, April 12, 2010 4:09 PM
To: Discussion of IronPython
Subject: Re: [IronPython] Building via AST

Ok, so at risk of being a nuissance I have one last question because I feel like I'm half way there. I have the following example that seems to work:

# sample.py
from ConsoleApplication4 import IExample

class Example(IExample):
          test = "hello python"
          def Do(self):
                      return self.test

# program.cs
namespace ConsoleApplication4
{
          class Program
          {
                      static void Main(string[] args)
                      {
                                  var runtime = Python.CreateRuntime();
                                  runtime.LoadAssembly(typeof(IExample).Assembly);

                                  dynamic python = runtime.UseFile("sample.py");
                                  IExample example = (IExample)python.Example();

                                  Console.WriteLine(example.Do());
                                  Console.ReadKey(true);
                      }
          }

          public interface IExample
          {
                      string Do();
          }
}

Which prints out "hello python" as expected and is the exact behavior I would like to have. So now, say I did this instead:

# program.cs
namespace ConsoleApplication4
{
          class Program
          {
                      static void Main(string[] args)
                      {
                                  var runtime = Python.CreateRuntime();
                                  runtime.LoadAssembly(typeof(IExample).Assembly);

                                  var import = new IronPython.Compiler.Ast.ImportStatement(
                                              new[] { new ModuleName(new[] { "ConsoleApplication4" }) },
                                              new[] { "IExample" },
                                              false);

                                  var classDefinition = new IronPython.Compiler.Ast.ClassDefinition("Example",
                                              new Expression[] { new NameExpression("IExample") },
                                              new FunctionDefinition(
                                                          "Do",
                                                          new[] { new Parameter("self") },
                                                          new ReturnStatement(new ConstantExpression("hello python!"))));

                                  // TODO: Compile the above AST into something!
                                  dynamic python = null;

                                  IExample example = (IExample)python.Example();
                                  Console.WriteLine(example.Do());

                                  Console.ReadKey(true);
                      }
          }

          public interface IExample
          {
                      string Do();
          }
}

Is there anyway to fill in the line below the TODO such that I will get the exact same behavior as the first example? If you say no this time then I'll finally stop asking you questions (except maybe why not :-P). If you do say no this time, then I might be interested in trying to extend things such that it is possible.

On Mon, Apr 12, 2010 at 5:20 PM, Dino Viehland <dinov at microsoft.com<mailto:dinov at microsoft.com>> wrote:
Yes – there is the TypeGen class but really it’s just a thin wrapper around TypeBuilder w/ some helper APIs.  If we were to implement it today it might just be extension methods instead of a wrapper class.

Unfortunately there is currently no way to go from an expression tree to an instance method.  It’s on the top of the things to fix in the DLR for a future .NET release.  It’s sad that it hasn’t been solved yet but that is the state of the world.  If you really wanted to get crazy you could look at forking the DLR expression tree compiler and adding support ☺

As far as how Python does this – we do generate classes and we do this via our NewTypeMaker class.  But the IL that we create is rather minimal.  We just derive and override every virtual method and have it dispatch either to the method defined in a PythonType type (which is stored as an instance field in the object).  So while it sucks that we need to generate the IL by hand it’s small enough that it’s not a big deal.

From: users-bounces at lists.ironpython.com<mailto:users-bounces at lists.ironpython.com> [mailto:users-bounces at lists.ironpython.com<mailto:users-bounces at lists.ironpython.com>] On Behalf Of Justin Chase
Sent: Monday, April 12, 2010 2:03 PM
To: Discussion of IronPython
Subject: Re: [IronPython] Building via AST

Ok so I'm wondering if I'm perhaps using the wrong words to try to say what I mean. Because when I look into the IronPython source code I see a class called Microsoft.Scripting.Generation.TypeGen (http://ironpython.codeplex.com/SourceControl/changeset/view/65328#1011039). Which appears to be code for generating a .net type contrary to your response. Am I misinterpreting that?

Thanks for your examples below but that is about as far as I have gotten already, what I need is a way to actually generate types and instances members. And I was just looking at the ILGen class and all it's interesting helpers but what I'd really love to find is a way to generate this by just giving it an AST (including "this" references) instead of having to mess around with IL (hasn't this been solved already??).

I know that Python has a "class" construct and that if I compile scripts I can create instances of these objects, does this translate into actual .net types under the hood? Or something else? Can you add attributes or annotations to these classes for example? For my grand finale what I would really like to do is to generate types that implement interfaces and load them via MEF (by means of the Export attribute). Is this just the wrong way to be thinking about this entirely? Or am I just missing something?


On Mon, Apr 12, 2010 at 3:33 PM, Dino Viehland <dinov at microsoft.com<mailto:dinov at microsoft.com>> wrote:
Nope – the DLR doesn’t have any support for building .NET types – dynamic or otherwise.  If you’d like to just build an object which behaves dynamically I’d suggest looking at DynamicObject.  You can just subclass it and override various Try* methods and you’ll have a dynamic object.

If you really do need to do ILGen into a type, and as long as you’re building only static methods, you can use expression trees via Lambda<T>.CompileToMethod (unfortunately currently instance methods are not supported).  Here’s an example of that:

using System;
using System.Linq.Expressions;
using System.Reflection;

class Foo {
    public static void Main(string[] args) {
        var asm = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("foo"), System.Reflection.Emit.AssemblyBuilderAccess.Save);
        var module = asm.DefineDynamicModule("foo.dll");
        var type = module.DefineType("TestType");

        var param1 = Expression.Parameter(typeof(Foox), "arg1");
        var param2 = Expression.Parameter(typeof(Foox), "arg2");
        var method = type.DefineMethod("TestMethod", MethodAttributes.Public | MethodAttributes.Static);

        Expression.Lambda<Func<Foox, Foox, bool>>(
            Expression.Equal(param1, param2),
            new[] { param1, param2 }
        ).CompileToMethod(method);
        type.CreateType();
        asm.Save("foo.dll");
    }
}
enum Foox {
    Bar,
    Baz
}

From: users-bounces at lists.ironpython.com<mailto:users-bounces at lists.ironpython.com> [mailto:users-bounces at lists.ironpython.com<mailto:users-bounces at lists.ironpython.com>] On Behalf Of Justin Chase
Sent: Monday, April 12, 2010 1:21 PM
To: users at lists.ironpython.com<mailto:users at lists.ironpython.com>
Subject: [IronPython] Building via AST

Suppose I would like to build an AST programmatically and compile that into an assembly dynamically (meaning an assembly flagged with RunAndCollect) from C#. How would I do that with IronPython's help? I do not what to author Python code and compile that I would like to just deal directly with the AST.

Currently I have working code where I'm using System.Linq.Expression namespace to build statements and expressions into delegates but what I would like is to leverage the DLR to build dynamic types as well (without having to use ILGenerator preferably). Is this possible today?

--
Justin Chase
http://www.justnbusiness.com

_______________________________________________
Users mailing list
Users at lists.ironpython.com<mailto:Users at lists.ironpython.com>
http://lists.ironpython.com/listinfo.cgi/users-ironpython.com



--
Justin Chase
http://www.justnbusiness.com

_______________________________________________
Users mailing list
Users at lists.ironpython.com<mailto:Users at lists.ironpython.com>
http://lists.ironpython.com/listinfo.cgi/users-ironpython.com



--
Justin Chase
http://www.justnbusiness.com
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/ironpython-users/attachments/20100413/1ec3352d/attachment.html>


More information about the Ironpython-users mailing list