tech

[English] [Japanese]

22.2. Linking with Lua

LuneScript can execute Lua code.

However, running Lua code requires some attention.

In the following, I will explain points to note when executing Lua code on LuneScript.

Run Lua code

In LuneScript, Lua code can be executed from within LuneScript by using functions such as _load().

The _load() function has basically the same specifications as lua's load() function.

A sample is shown below.

// @lnsFront: ok
let code = ```
return { val1 = 10, val2 = 20 }
```;
let loaded, err = _load( code, nil );
when! loaded {
   if! let obj = loaded( ## ) {
      forsort val, key in obj@@Map<str,int> {
         print( key, val + 100 ); 
      }
   }
} else {
   print( err );
}

This sample outputs the following values:

val1	110
val2	120

This is the result of enumerating the Lua table returned by Lua code return { val1 = 10, val2 = 20 } with forsort and outputting it with print( key, val + 100 );.

The above sample is almost the same as the LuneScript code below.

// @lnsFront: ok
fn func():Map<str,int> {
   return { "val1":10, "val2":20 };
}
forsort val, key in func() {
   print( key, val + 100 ); 
}

In this way, LuneScript and Lua can work together.

Lua → LuneScript data conversion

When Lua code is executed from LuneScript, the execution result of the Lua code becomes Luaval type.

The Luaval type is the type that holds data in Lua code, and the data in Lua code that corresponds to type T in LuneScript is Luaval<T>.

For example, { val1 = 10, val2 = 20 } in the above sample corresponds to Map<str,int> in LuneScript and becomes Luaval<Map<str,int>> .

Note that when transcompiling from LuneScript to Lua, Luaval<T> is an exact match for T . When transcompiling from LuneScript to Lua, even if you explicitly declare it as Luaval<T> , it will be converted as T internally, so you don't need to be aware of Luaval<T> .

When converting from LuneScript to a language other than Lua (currently convertible to go), Luaval<T> and T are clearly different, so you need to be aware of Luaval<T> when converting to anything other than Lua .

By specifying "–valid-luaval" in the transcompile option, Luaval<T> and T are managed separately even when transcompiling to Lua.

The above example can be rewritten to use func() with an argument of Luaval<Map<str,int>> like this:

// @lnsFront: ok
fn func(map:Luaval<&Map<str,int>>) {
   forsort val, key in map {
      print( key, val + 100 ); 
   }
}
let code = ```
return { val1 = 10, val2 = 20 }
```;
let loaded, err = _load( code, nil );
when! loaded {
   if! let obj = loaded( ## ) {
      func( obj@@Map<str,int> );
   }
} else {
   print( err );
}

Also, as in the above sample, for Luaval type data of Map, you can refer to data in Map by element access using foreach or [] like Map of LuneScript. You can refer to it, but you cannot change it.

interconvertible types

If the type of data in Lua code is:

  • int, real, bool, str
  • above nilable

As for str , there is an overhead proportional to the length of the string.

Luaval type cast

Luaval type casting is restricted.

In the above sample, obj@@Map<str,int> is passed to the func argument as follows.

// @lnsFront: skip   
   if! let obj = loaded( ## ) {
      func( obj@@Map<str,int> );
   }

This is an operation that casts obj to type Map<str,int> , but here the type of obj is of type Luaval<stem> and when we cast it to Map<str,int> , the type after the cast is It becomes a Luaval<Map<str,int>>.

When casting from a certain type T1 to T2 type is possible, if you instruct to cast from Luaval<T1> to T2, the cast result will be Luaval<T2>.

Also, the following casts are not possible:

  • Cast from type T1 to type Luaval<T1>
  • Cast from type Luaval<T1> to type T1

Specifically, you cannot cast from Map<str,int> to Luaval<Map<str,int>> .

However, the stem type can be cast to and from Luaval as an exception.

In addition, it is possible to cast from Luaval type to non-Luaval type by going through stem type as follows,

Luaval => stem => Luaval

The behavior is undefined when casting to a type other than the original.

expandLuavalMap

Treat collection data in Lua code as Luaval .

For example, data of type Map is Luaval<Map> . Map type and Luaval<Map> type data are not compatible, so you cannot assign them.

The following function is provided as a method to expand the Luaval type data of this collection as a LuneScript value.

fn expandLuavalMap( Luaval<stem>! ) : stem!;

Using this function does the following:

// @lnsFront: ok
fn func(map:Luaval<&Map<str,int>>) {
   forsort val, key in map {
      print( key, val + 100 ); 
   }
}
fn func2(map:&Map<str,int>) {
   forsort val, key in map {
      print( key, val + 100 ); 
   }
}
let code = ```
return { val1 = 10, val2 = 20 }
```;
let loaded, err = _load( code, nil );
when! loaded {
   if! let obj = loaded( ## ) {
      func( obj@@Map<str,int> );
      if! let map = expandLuavalMap( obj ) {
         func2( map@@Map<str,int> );
      }
   }
} else {
   print( err );
}

In this sample there is a func() function that enumerates Luaval<&Map<str,int>> and a func2() function that enumerates &Map<str,int> .

Extract Luaval type data in expandLuavalMap() before calling func2() and cast it to Map<str,int> so that Map<str,int> instead of Luaval<Map<str,int>> is processed as

Note that expandLuavalMap() creates a clone of the Luaval type data given as an argument.

Type conversion of Luaval type

A Luaval type that holds a nilable type T! becomes Luaval<T>! It will not be Luaval<T!> .

Also, Immutable of Luaval<T> becomes Luaval<&T> .

Luaval type functions, Luaval type object methods

Function-type Luaval has Luaval type arguments and return values.

For example, the argument proc of the func function in the following sample is From of type Luaval<Process> , the argument of this Form is Luaval<&List<int>> , and the return value is Luaval<&List<int>> .

// @lnsFront: skip
   form Process( val:&List<int> ) : &Map<int>;
   fn func( proc:Luaval<Process> ) {
      let list = proc( [ 1, 2, 3 ] );
   }

LuneScript → Lua data conversion

When passing a LuneScript value to a Lua function, you must pass a value of type Luaval.

However, if an argument to a function in Lua has the following values, it will be of raw type instead of Luava type.

  • int, real, bool, str
  • A collection type such as List whose elements are the above
  • above nilable

Here is a sample:

// @lnsFront: ok
let code = ```
return function( tbl )
   local total = 0
   for key, val in pairs( tbl ) do
      total = total + val
   end
   return total
end
```;
let loaded, err = _load( code, nil );
when! loaded {
   if! let obj = loaded( ## ) {
   let map = { "val1":1, "val2":10 };
      print( (obj@@form)( map ) );  // Lua の関数コール
   }
} else {
   print( err );
}

This sample calls a function that computes the sum of the values of the elements in the Lua table given as arguments.

In this sample, the Map<str,int> type data map is specified as an argument of the Lua function and executed.

At this time, Map<str,int> type data is internally converted to a Lua table.