Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Luaj: How to Import or Require a Lua Function Library

Tags:

java

lua

luaj

In the Java LuaJ library I would like to know how to require or import a lua script of functions in another lua script called by a lua closure through Java. For example this does not work:

public static LuaValue runInputStreamLua(InputStream inputStream) throws Exception {
    Prototype luaScriptPrototype = LuaC.instance.compile(inputStream, "");
    Globals luaScriptStandardGlobals = JsePlatform.standardGlobals();
    luaScriptStandardGlobals.loadfile("mycoolmathfunctions.lua");
    LuaClosure luaClosure = new LuaClosure(luaScriptPrototype, luaScriptStandardGlobals);
    return luaClosure.call();
}

And the input stream here refers to the contents of another lua:

import 'mycoolmathfunctions'
-- or maybe require mycoolmathfunctions ?

return sum({1, 2, 3})
-- or maybe mycoolmathfunctions.sum({1, 2, 3}) ?

How do I do this?

like image 265
David Williams Avatar asked Sep 16 '15 00:09

David Williams


People also ask

How require works in lua?

Lua offers a higher-level function to load and run libraries, called require . Roughly, require does the same job as dofile , but with two important differences. First, require searches for the file in a path; second, require controls whether a file has already been run to avoid duplicating the work.

Does lua work with Java?

Lua can be used with primary programming languages like C, C++, Java, C#, etc.. and other scripting languages like Perl and Ruby, further extending its usability.

What does Dofile do in Lua?

The dofile builtin function can be used to execute a chunk of code stored in a file. In the following example, we have two files main. lua and sayit.


1 Answers

In the Java LuaJ library I would like to know how to require or import a lua script of functions in another lua script called by a lua closure through Java.

You can place your Lua libraries as resources in your Java packages. Then on your lua script that requires another lua script, you require them relative to your package path.

Here's an example:

enter image description here

Here's our import-me.lua:

-- make our sample module table global
my_imported = {}

function my_imported.printHello()
    print "Hello!"
end

return my_imported

Which is then imported in our sample-that-imports.lua:

require "com.example.import-me"

my_imported.printHello()

Then we run our sample-that-imports.lua in our SampleMain Java class:

package com.example;
...
public class SampleMain {

    public static void main(String[] args) {
        Globals globals = JsePlatform.standardGlobals();

        // Again, we load the lua scripts relative to our package path
        LuaValue chunk = globals.loadfile("com/example/sample-that-imports.lua");
        chunk.call();

        // We could even use our imported library here
        chunk = globals.load("my_imported.printHello()");
        chunk.call();
    }
}

Now to answer your other problems,

For example this does not work…

I've noticed on your Java code that you've assumed that calling loadfile() would automatically run your lua script. Furthermore, you've assumed that loadfile() is used for loading your lua modules. However, this isn't how it is supposed to be used.

Your loadfile() should be able to return a LuaValue that you need to call() to run the script itself. You could even safely cast it to a LuaClosure because this is what loadfile() actually returns.

To fix your Java code above, you can use this,

public static LuaValue runInputStreamLua(InputStream inputStream) throws Exception {
    Prototype luaScriptPrototype = LuaC.instance.compile(inputStream, "");
    Globals globals = JsePlatform.standardGlobals();
    LuaClosure luaClosure = new LuaClosure(luaScriptPrototype, globals);
    return luaClosure.call();
}

I will assume in the above code that you are already using require in the InputStream (containing a lua script) that you have passed with the above method. If not then, you can do the following changes:

public static LuaValue runInputStreamLua(InputStream inputStream) throws Exception {
    Prototype luaScriptPrototype = LuaC.instance.compile(inputStream, "");
    Globals globals = JsePlatform.standardGlobals();

    LuaValue chunk = globals.load("require 'com.example.import-me';");
    chunk.call();

    LuaClosure luaClosure = new LuaClosure(luaScriptPrototype, globals);
    return luaClosure.call();
}

In the above changes, I am assuming that your lua module (in our example import-me.lua) automatically creates a global space for itself (in our example, the my_imported table). If not, you could do this final touch:

...
LuaValue chunk = globals.load("my_imported = require 'com.example.import-me';");
...


You also should reuse your Globals (returned by JsePlatform.standardGlobals()) unless you really want to create a new Globals table every time you call your method. Furthermore, if you really don't need an InputStream, and simply wants to load the file itself from its file path (or resource path in your Java package path), you can simplify everything into this:

public static LuaValue runLuaFile(Globals globals, String luafile) {
    return globals.loadfile(luafile).call();
}

Or to ensure that our lua module is always imported (or has been require'd) by our lua script,

public static LuaValue runLuaFile(Globals globals, String luafile) {
    LuaValue chunk = globals.load("require 'com.example.import-me';");
    chunk.call();
    chunk = globals.loadfile(luafile);
    return chunk.call();
}

Again, you must specify the complete resource path for our lua file. Here's a sample Java snippet using our simplified method above:

Globals globals = JsePlatform.standardGlobals();
runLuaFile(globals, "com/example/sample-that-imports.lua");

I hope this helps!


Edit:

You've mentioned in the comments that you need to import lua modules from InputStreams. There are 2 ways to achieve that:

  1. The first one is to load and run the lua modules that you need, like simple lua scripts – and if case that the lua modules that you need are only compatible with lua's require mechanism, you'll have a lot of problems to face.
  2. The second, easiest, and most efficient way is to simply load the module, place it inside the lua table package.preload, mapped with a key as its name (to be used by require).

We'll use the second method above, as this is exactly what lua's require mechanism really intends. Here's how to implement it using LuaJ:

public static void preloadLuaModule(Globals globals, String modname, InputStream is) {
    LuaValue module = globals.load(is, modname, "bt", globals);
    globals.get("package").get("preload").set(modname, module);
}

The above utility method pre-loads an InputStream to be used by require. Here's an example usage:

Somewhere in the beginning of everything, we initialize stuffs:

...
preloadLuaModule(globals, "sample_module", sampleModuleInputStream);
...

And our sampleModuleInputStream above is a lua module with the following content:

-- make our sample module table global
sample_module = {}

function sample_module.printHi()
    print "Hi!"
end

return sample_module

Then we could simply use require "sample_module" anywhere we like, be it in a Lua script or in Java using LuaJ:

globals.get("require").call("sample_module");
like image 67
Jason Sparc Avatar answered Oct 11 '22 17:10

Jason Sparc