Multilingual REPL

DLR Hosting API is a common API for all DLR languages: IronRuby, IronPython, and others. It abstracts away language specific details and provides API that could be used in a language independent manner. The API is primarily designed to ease usage of dynamic languages from strongly typed .NET languages and to make it easier to write applications with a scriptable object model. Nonetheless we can take advantage of the API in a scripting language as well. For that purpose the most interesting features are management of isolated scripting environments (script runtimes) and cross language code execution. I presented the former in my last post. Let’s check out the latter today.

To run examples below you’ll need to build the latest IronRuby, IronPython and DLR from sources. You can do so using the latest sources from IronRuby GIT repository or DLR CodePlex site:

  • Building from IronRuby GIT repo (mapped locally to C:\Git\ironruby):

    C:\Git\ironruby\Merlin\Main > msbuild Languages\Ruby\Ruby.sln
    C:\Git\ironruby\Merlin\Main > msbuild Languages\IronPython\IronPython.sln

    We’ve recently included IronPython projects to the repository. If you already have the repo set up just pull the latest commit. You can also use rake to build IronRuby, we don’t provide rake file for IronPython though.

  • Building from DLR CodePlex (downloaded and extracted to C:\CodePlex\DLR_Main):

    C:\Codeplex\DLR_Main > msbuild Codeplex-DLR.sln

Both builds build unsigned debug assemblies and place the binaries into bin\Debug directory.

Available Languages

The DLR runtime can host multiple languages at once. Listing all languages that are available is easy:

# github: languages.rb
# load IronRuby library that provides Ruby programs an easy access to the Hosting API:
require 'IronRuby'

# create a new DLR runtime:
runtime = IronRuby.create_runtime
# list all available languages:
runtime.setup.language_setups.each { |ls| puts "#{ls.display_name}: #{ls.names.inspect}" }

The last line enumerates all instances of LanguageSetup class (defined by Hosting API) that are stored in the setup of the runtime. For each such language setup its display name and a list of short names are printed. The short names uniquely identify languages within the runtime. A language can be identified by multiple short names.

C:\Codeplex\DLR_Main\Bin\Debug>ir languages.rb
IronPython 2.6 Alpha: ["IronPython", "Python", "py"]
IronRuby 1.0 Alpha: ["IronRuby", "Ruby", "rb"]
ToyScript: ["ToyScript", "ts"]

Where does this list come from? We have said nothing about IronPython in our script. Does IronRuby know about IronPython? Making IronRuby dependent on all languages it could possibly interact with wouldn’t be a good design. Instead, Hosting API creates and sets up a script runtime according to the current .NET application’s configuration. It is told to do so in IronRuby#create_runtime. The .NET application is ir.exe hence the configuration is loaded from ir.exe.config file that lives next to the ir.exe. Indeed, the list of languages can be found in this file:

  <language names="IronPython;Python;py" extensions=".py" displayName="IronPython 2.6 Alpha"
    type="IronPython.Runtime.PythonContext, IronPython, Version=, Culture=neutral, PublicKeyToken=null" />
  <language names="IronRuby;Ruby;rb" extensions=".rb" displayName="IronRuby 1.0 Alpha"     
    type="IronRuby.Runtime.RubyContext, IronRuby, Version=, Culture=neutral, PublicKeyToken=null" />
  <language names="ToyScript;ts" extensions=".ts"    
    type="ToyScript.ToyLanguageContext, ToyScript, Version=, Culture=neutral, PublicKeyToken=null" />

Whenever you find a DLR language you want to use for scripting your app and don’t have yet you can just add it to the list and you’re all set.

Hosting IronPython in IronRuby

Now that we know how IronPython identifies itself to the runtime, we can load its engine and execute some Python code. Let’s take the simple REPL implementation from the previous post and change it to execute Python code instead of Ruby:

# github: python_repl.rb
require 'IronRuby'
runtime = IronRuby.create_runtime

# ask the runtime for Python engine using one of its simple names:
engine = runtime.get_engine("IronPython")

scope = engine.create_scope

while true  
  print "> "
  code = gets
  break if code.nil?
    result = engine.execute(code, scope)

    # print the Ruby's view of the result:
    p result

    # print the result as Python would display it:
    puts engine.operations.format(result)
  rescue Exception
    puts $!

Let’s play now:

C:\Codeplex\DLR_Main\Bin\Debug>ir python_repl.rb
> def add(a,b): return a + b
> add
<function add at 0x000000000000002B>
> add(1,1)
> add((1, 2), (3, 4))
(1, 2, 3, 4)
> ^Z

As you can see, each language might have a different view on the same object.

To make the REPL really useful we need to support multi-line statements (i.e. wait with code execution until a complete statement is entered). Let’s keep that and more for the next post.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: