Related articles in this series:

Flutter thermal update and dynamic UI generation

Lua 15 Minutes quick Start (PART 1)

Lua 15 minutes quick Start (II)

Lua and C language call each other

Lua C API

The Lua C API refers primarily to a series of C functions (which may also be macro functions) that begin with Lua _.

Lua is positioned as a powerful, efficient, lightweight, embeddable scripting language. For easy embedding into other Host environments, the Lua core is implemented as a Library, and other applications can easily access script execution capabilities by simply linking to Lua libraries using the LUA-provided APIS.

Lua 3.1 introduced the lua_State structure, which encapsulates interpreter state, and Lua 4.0 introduced the concept of a virtual stack, but the lua_State structure is still an implementation detail. Users only need to create lua_State instances using the lua_newState () function. Other functions operate on lua_State instances.

In addition to the basic functions that begin with lua_, Lua provides about 60 helper functions that begin with luaL_. Auxiliary functions are completely wrapped on top of basic functions to provide simpler and more convenient operations.

Lua State is the most core concept of Lua API. All API functions operate around Lua State, and Lua State encapsulates a basic virtual stack (which can be called Lua stack). Lua stack is the bridge between the host language (C for Lua official virtual machines, Dart for LuaDardo libraries) and Lua, and a large portion of the Lua API functions are dedicated to handling Lua stacks.

inLuaLuaDardoThe library is basically compatible with most of the Lua C API, with only the Dart language specification adaptation in naming. Therefore, familiarity with the Lua C API is necessary to learn how Dart and Lua call each other. For details on the Lua C API, please read the previous blog postLua and C language Intercall.

Note, LuaDardo library base API interface, please checkLuaBasicAPIClass, it basically corresponds tolua.hInterfaces in files; Auxiliary API interface, viewLuaAuxLibClass, correspondingluaxlib.hFile interface.

Lua stack

There are a few caveats to Lua stack manipulation:

  • In the Lua API, the stack index starts at 1
  • The stack index can be negative. A positive index is called an absolute index and increases from 1 (bottom of the stack), while a negative index is called a relative index and decreases from -1 (top of the stack). Lua API functions internally convert relative indexes to absolute indexes
  • If the stack size is N, the top index of the stack is top (0 < top <= n). Is located in[1, top]The index in the closed interval isA Valid index, located in the[1, n]The index in the closed interval isAcceptable index. If you want to write values to the stack, you must provide a valid index, otherwise errors and even crashes can occur. If only the value is read from the stack, an acceptable index can be provided; For an invalid acceptable index, the behavior is equivalent to storing a nil value there.

Common basic LuaLuaDardo API:

  • int getTop(): returns the top of the stack index
  • int absIndex(int idx): Converts an index to an absolute index
  • bool checkStack(int n): Checks the stack free space to see if n values can still be pushed in without overflow. (Since the Dart language stack grows automatically, in LuaDardo this method always returnstrue)
  • void pop(int n): pops n values from the top of the stack
  • void copy(int fromIdx, int toIdx): copies a value from one location to another
  • void pushValue(int idx): pushes the value at the specified index to the top of the stack
  • void replace(int idx): pops the top value of the stack and writes it to the specified location
  • void insert(int idx): pops the top value of the stack and inserts it at the specified position
  • void remove(int idx): Deletes the value at the specified index, and then moves all values above that value down one place
  • void rotate(int idx, int n)Will:[idx, top]The value in the index interval is rotated n positions towards the top of the stack
  • void setTop(int idx): sets the top of stack index to the specified value. If the specified value is less than the current top of the stack index, it is equivalent to a pop-up operation (specifying a value of 0 is equivalent to clearing the stack). If the specified value is greater than the current top of the stack index, it is equivalent to pushing multiplenilvalue

Here are a few examples

After calling replace(2), the top value of the stack is popped and written to index 2. Before and after stack comparison:

After the insert(2) call, the top value of the stack is popped and inserted into index 2, and the values originally at indexes 2, 3, and 4 are moved up one place, respectively. Before and after stack comparison:

Rotate (2,1) rotates the four values at indexes 2, 3, 4, and 5 one position toward the top of the stack. Before and after stack comparison:

  • String typeName(LuaType tp): converts the given Lua type to the corresponding string representation
  • LuaType type(int idx): Returns an invalid index based on the type of value returned by the indexLuaType.luaNone
  • isXX: provides a range ofisIs a method that starts with a prefix and is used to determine whether the value at a given index is of that type
  • bool toBoolean(int idx): Retrieves a Boolean value from the specified index. If the value is not of a Boolean type, a cast is performed. onlyfalseandnilIt means false, everything else means true
  • double toNumber(int idx)double toNumberX(int idx): Retrieves a number from the specified index. If the value is not numeric, a type conversion is required. The difference is that if the value is not a numeric type and cannot be converted to a numeric type, the former simply returns 0, while the latter returns if the conversion failsnull
  • int toInteger(int idx)andint toIntegerX(int idx): Retrieves an integer value from the specified index. If the value is not an integer, a type conversion is required
  • String toStr(int idx): Retrieves a value from the specified index, or returns the string if the value is a string. If the value is a number, it is converted to a string and returned on failurenull. (Note, the original C API in this interface writingtostring , conflicts with the Dart class method nametoStr)

In addition, there are a number of common methods that start with a push prefix to push a value of a specific type onto the stack. Basically similar to the C API, see Lua and C calls to each other article

Creating the runtime

LuaState state = LuaState.newState();
// Load the standard library
state.openLibs();
// Load Lua code
state.loadString("print('hello')");
state.call(0.0);
Copy the code

The Dart adjustable Lua

Get variables

-- test.lua
a = 100
b = 120
Copy the code

The dart code

LuaState ls = LuaState.newState();
ls.openLibs();
ls.doFile("test.lua");

/ / a into the stack
ls.getGlobal("a");
if(ls.isNumber(- 1)) {var a = ls.toNumber(- 1);
    print("a=$a");
}
/ / b into the stack
ls.getGlobal("b");
if(ls.isNumber(- 1)) {var b = ls.toNumber(- 1);
    print("b=$b");
}
Copy the code

Dart obtains the luA global table

-- test.lua
mytable = {k1 = 1, k2 = 2.34, k3 = "test"}
Copy the code

The dart code

    ls.getGlobal("mytable");
    // Press a key name into the lua table
    ls.pushString("k1");
    // Pop up the key name at the top of the stack, get the corresponding key value, and push the result to the top of the stack
    ls.getTable(2 -);

    if(ls.isInteger(- 1)) {// get the value of key k1
      var k1 = ls.toInteger(- 1);
    }

    // Repeat the above process
    ls.pushString("k2");
    ls.getTable(2 -);
    
    if(ls.isNumber(- 1)) {var k2 = ls.toNumber(- 1);
    }
Copy the code

In addition, the getField method is provided to obtain the key value of the table directly.

    ls.getGlobal("mytable");
    ls.getField(- 1."k1");
    if(ls.isInteger(- 1)) {var k1 = ls.toInteger(- 1);
    }
Copy the code

Call the Lua function

-- test.lua

function myFunc(a)
    print("myFunc run")
end
Copy the code

The dart code

ls.doFile("test.lua");

ls.getGlobal("myFunc");
if(ls.isFunction(- 1)){
    ls.pCall(0.0.0);
}
Copy the code

The pCall method takes three arguments. The first argument represents the number of arguments to the called Lua function, and the second argument represents the number of return values to the called Lua function. Lua functions support multiple return values, so there may be multiple return values.

Note that the Lua function being called is pushed and its arguments are pushed from left to right. The function and all its arguments are popped from the stack when executed. The return value of the function will be pushed from left to right when the function returns.

Lua adjustable Dart

Get variables

The dart code

/ / value into the stack
ls.pushString("Zhang");
// Set the variable name
ls.setGlobal("name");
Copy the code

Lua code

Get the global variable name
print(name) - zhang SAN
Copy the code

Lua gets the global table defined in Dart

The dart code

    // Create a table and push it onto the stack
    ls.newTable();
    // Press a key
    ls.pushString("name");
    // Press the corresponding value of key. Note that the index of the table in the stack is now -3
    ls.pushString("Bruce");
    // Set the above key-value pair to table and pop up the key and value.
    ls.setTable(- 3);
    // Set the variable name for table and pop table
    ls.setGlobal("person");
Copy the code

Lua code

Person = {name="Bruce"}
print(person.name)
Copy the code

There are two more methods for manipulating tables:

  • rawSet(idx): the equivalent ofsetTable(idx), but it is faster to do only one direct access (without triggering meta-methods).
  • rawGet(idx)As above, equivalent togetTable(idx)

Calling the Dart function

Here, we use Dart’s random number generator as an example to write a wrapper function that wraps the nextInt method. The prototype of the method is int nextInt(int Max), and the return value is a random integer between 0 and Max.

import 'package:lua_dardo/lua.dart';
import 'dart:math';

// wrap Function to match Function signature: int Function(LuaState ls)
int randomInt(LuaState ls) {
  int max = ls.checkInteger(1);
  ls.pop(1);

  var random = Random();
  var randVal = random.nextInt(max);
  ls.pushInteger(randVal);
  return 1;
}

void main(List<String> arguments) {
  LuaState state = LuaState.newState();
  state.openLibs();

  state.pushDartFunction(randomInt);
  state.setGlobal('randomInt');

  // Execute a Lua script to test the randomInt function
  state.loadString(''' rand_val = randomInt(10) print('random value is '.. rand_val) ''');
  state.call(0.0);
}
Copy the code

Note that the above example is just a simple demonstration, where pushDartFunction registers a global function randomInt that is available globally on the Lua side when the runtime load is complete. This is not desirable in practical development because there are two main problems. First, there is the potential for function naming conflicts, and second, there is the potential for global scope chaos and performance degradation when you need to write a lot of extensions to Lua with Dart. For example, if we wanted to write Lua wrappers for the interface to Flutter, we might need to register hundreds of functions at once. Registering them all in a global scope is obviously not desirable, which is not conducive to modular management of luA-side code.

The solution to the above problem is also very simple, imitate the implementation of Lua standard library, first according to the different module to create different tables, and then register the function in the table, Lua table is a kind of scope. For details, see Flutter_lua_dardo


Follow the public account: the path of programming from 0 to 1

Or follow a blogger’s video school