Using Lua with .NET, Java or Objective-C?

Typically, the need is for a compact scripting language that can be easily embedded in a larger application.

With the VM platforms, there are two approaches: one is to bind the Lua C library with JNI or P/Invoke, and the other is to run Lua entirely in 'managed code' If you are familiar with these platforms, Lua provides an easy way to explore the available libraries and prototype user interfaces. The quest for extra batteries then becomes the question 'Can I do it in C# or Java?'.

6.2.1 Lua with Java

LuaJava allows Lua scripts to run within Java. The Lua interpreter (plus some binding code) is loaded using JNI.

Here is an example script:

sys = luajava.bindClass("java.lang.System")
print ( sys:currentTimeMillis() )

(Note that there is no require 'luajava' needed because we are running within LuaJava.)

newInstance creates a new instance of an object of a named class.

strTk = luajava.newInstance("java.util.StringTokenizer",
 "a,b,c,d", ",")
while strTk:hasMoreTokens() do
 print(strTk:nextToken())
end

Which can also be expressed as

StringTokenizer = luajava.bindClass("java.util.StringTokenizer")
strTk = luajava.new(StringTokenizer,"a,b,c,d", ",")
...

LuaJavaUtils is a little set of utilities to make using LuaJava easier. The java.import module provides an import function which works like the Java statement of this name.

require 'java.import'
import 'java.util.*'
strTk = StringTokenizer("a,b,c,d", ",")
...

This module modifies the global Lua environment so that undeclared globals are first looked up in the import list, and made into class instances if possible; the call metamethod is also overloaded for these instances so we can say Class() rather than luajava.new(Class).

There are also a few little Java classes to get around a limitation of LuaJava, which is that one cannot inherit from full classes.

-- a simple Swing application.
require 'java.import'
import 'java.awt.*'
import 'java.awt.event.*'
import 'javax.swing.*'
import 'java.awt.image.*'
import 'javax.imageio.*'
import 'java.io.*'
-- this is for PaintPanel and Painter...
import 'org.penlight.luajava.utils.*'
bi = ImageIO:read(File(arg[1] or "bld.jpg"))
w = bi:getWidth(nil);
h = bi:getHeight(nil);
painter = proxy("Painter", {
 painter = function(g)
 g:drawImage(bi, 0, 0, nil)
 end
})
f = JFrame("Image")
f:setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
canv = PaintPanel(painter)
canv:setPreferredSize(Dimension(w,h))
f:add(JScrollPane(canv))
f:pack()
f:setVisible(true)

LuaJava only supports Java 1.4, so nifty features like generics and variable argument lists are not supported. Also, currently error handling is a little primitive.

Kahlua is a project to completely implement the Lua engine in Java. The target platform is the reduced runtime J2ME found in mobile phones; the main example is a little Lua interpreter which can run on your phone.

Another option is luaj which targets both J2ME and J2SE and "unique direct lua-to-java-bytecode compiling".

6.2.2 Lua with .NET

LuaInterface is a binding of Lua to .NET; up to version 1.5.3 using P/Invoke, since version 2.0 fully managed code. The 1.5.3 version is more friendly to the more casual user of .NET (say a developer wishing to use a WinForms interface for Lua) and so this is the version included in Lua for Windows.

A console "Hello, World!" using LuaInterface:

require 'luanet'
luanet.load_assembly "System"
Console = luanet.import_type "System.Console"
Math = luanet.import_type "System.Math"
Console.WriteLine("sqrt(2) is {0}",Math.Sqrt(2))

A basic Windows.Forms application:

require 'luanet'
luanet.load_assembly "System.Windows.Forms"
Form = luanet.import_type "System.Windows.Forms.Form"
form = Form()
form.Text = "Hello, World!"
form:ShowDialog()

Since all types need to be explicitly loaded, this can get a bit tedious. A helper library CLRPackage (code ) written in pure Lua can reduce the amount of typing by providing an import facility:

require 'CLRPackage'
import "System"
Console.WriteLine("sqrt(2) is {0}",Math.Sqrt(2))

Windows.Forms applications become more straightforward:

require 'CLRPackage'
import "System.Windows.Forms"
import "System.Drawing"
form = Form()
form.Text = "Hello, World!"
button = Button()
button.Text = "Click Me!"
button.Location = Point(20,20)
button.Click:Add(function()
 MessageBox.Show("We wuz clicked!",arg[0],MessageBoxButtons.OK)
end)
form.Controls:Add(button)
form:ShowDialog()

GUI toolkits tend to split into two camps; those that use absolute positioning (like Point(20,20) above) and those which believe that this is Evil and use widget-packing schemes (like Java and GTK). Here is an alternative way to construct forms:

-- autoform.wlua require "CLRForm"
tbl = {
 x = 2.3,
 y = 10.2,
 z = "two",
 t = -1.0,
 file = "c:\\lang\\lua\\ilua.lua",
 outfile = "",
 res = true,
}
form = AutoVarDialog { Text = "Test AutoVar", Object = tbl;
 "First variable:","x", Range(0,4),
 "Second Variable:","y",
 "Domain name:","z", {"one","two","three"; Editable=true},
 "Blonheim's Little Adjustment:","t",
 "Input File:","file",FileIn "Lua (*.lua)|C# (*.cs)",
 "Output File:","outfile",FileOut "Text (*.txt)",
 "Make a Note?","res",
}
if form:ShowDialogOK() then
 print(tbl.x,tbl.z,tbl.res,tbl.file)
end

This produces a capable little dialog for editing the table tbl's fields. This style is more often seen with bindings to relational database tables, but fits well with dynamically typed and discoverable data such as a Lua table.

6.2.3 Lua with Objective-C

LuaCocoa lets Lua interoperate with Objective-C. There are some cool examples in the documentation.



Back