Hello Lua!

Haxe 3.3 introduces a powerful and flexible new Lua target. Here's how to get started with it.

Article by Justin Donaldson on 2016-04-28.

Comments

The Lua target is now available in the Haxe Foundation repo, and in Haxe nightlies.

Naturally, you will also need a Lua installation. Version 5.2 is preferred. For Linux based systems, you can use your favorite package installer. For Windows, try the LuaDist project.

Once you have an up to date Haxe compiler and Lua installation, setting Lua as a build target is relatively straightforward, simply use the "-lua" directive in your build hxml. E.g.

#build.hxml
-lua out.lua
-main Example.hx
...

However, there's a few things to know and do before you get started with your new Lua project:

LuaJIT

There is additional support for LuaJIT, Lua's speedy jit compiler. When compiled with the -D luajit flag, the following externs become available:

  1. lua.FFI
  2. lua.Jit

These classes grant access to the low level FFI and methods provided by the Luajit runtime.

Installing Dependencies

Lua is the only target that requires dependencies to be installed for the Haxe standard library. The reason for this is that Lua itself has an extremely minimalistic standard library which is missing support for regex and other useful low level functionality.

You can install these dependencies manually, or using a Lua package manager such as Luarocks or LuaDist. Currently, lrexlib-pcre and lfs are required.

If you don't need file system or regular expression support, you don't need to install the dependency. If you are working on a Lua instance that does not allow package installation (e.g. a plugin for a commercial app or game), then you still have options: You can use Lua patterns instead of regular expressions. For simple file based IO, you can use the standard lua.Io extern if your Lua instance provides it.

Supported Versions

Lua 5.2 passes all haxe unit tests as part of the continuous integration suite, and is the primary development version. LuaJIT should also work, although it is not tested through continuous integration yet. Lua 5.3 only has partial support, since this version has deprecated the bit library that is required for 5.2. You can get this version working by compiling it with Lua 5.2 compatibility support.

Lua Magic

Lua has Haxe "magic" in the form of the \\lua\\("code") method, which permits arbitrary inline lua code to be inserted inline.

untyped __lua__("print('hello from raw lua code')");

LuaRequire

Lua has a "require" metadata keyword which is used for importing modules (defined locally or provided through a package manager like Luarocks). This is useful for defining externs for third party Lua libraries. Here's a snippet of the extern for lfs:

package lua.lib.lfs;
@:luaRequire("lfs")
extern class Lfs {
  public static function chdir(path : String) : Bool;
  ...
}

IncludeFile

In some cases it is useful to include a small script of lua code, and make it available for your Haxe code to use elsewhere. Small files can be included this way with the haxe.macro.Compiler.includeFile() method.

//Example.hx
class Example {
    public static function main(){
       untyped _hx_example();
    }
    public static function __init__(){
        haxe.macro.Compiler.includeFile("example.lua");
    }
}
--[[ example.lua --]]
_hx_example = function()
  print("hi from a plain lua function");
end

Note that the included files are inlined at the beginning of the generated lua output, in an order that matches the import declarations for the classes in which they are included. It's generally a good idea to include such files inside an __init__ method, and then only expose the functionality from the lua script in the api for the class.

Also note that there is no conflict detection for lua variables and functions included and exposed in this manner. Be careful not to overwrite anything important!

Export

In some cases it is useful to export haxe methods and classes outside of the haxe context. This can be done by attaching the @:export(...) metadata to any class or field. This will expose the respective datastructure in the global (_G) table. You may also change the name of the exported datastructure in the resulting global table:

//Example.hx
@:expose
class Example {
    public static function sayHi(){
        trace("Hi!");
    }
}
class Foo{
    @:expose("foobar")
    public static function bar(){}
}

It's also worth mentioning that the lua output will return the global metatable as a return value. So, it's possible to use a sandbox mechanism (setfenv or _ENV) to set the _G table for the haxe generated code. The haxe code will populate the new _G table with the exported values, and return this table back to the caller of require().

Next Steps

The Haxe Lua target is still under active development, and code generation may change in upcoming versions. If you have suggestions, bug reports, or general questions on the Haxe Lua target we are happy to hear them! Please reach out to us on the Haxe google group.

Avatar for Justin Donaldson

By Justin Donaldson

Published 2016-04-28