Lua

Lua is an imperative programming language first released in 1993 that is often embedded into other applications. The cross-platform Lua interpreter is available for most operating systems. Using the Lua API, programming languages with C interoperatibility interfaces can call Lua routines or be called from Lua.

For Fortran, several interface bindings exists that mostly focus on loading Lua-based configuration files:

AOTUS
Limited ISO C binding interface to Lua and library for using Lua scripts as configuration files in Fortran applications (MIT).
f2k3-lua
Fortran 2003 interface to Lua for loading configuration files (MIT).
flook
Abstraction layer for Lua-based configuration files (MPL-2.0).
FortLua
Fork of AOTUS, mainly to read and write Lua configuration files (MIT).
fortran-lua53
Generic Lua 5.3 interfaces for Fortran 2008, for calling Lua from Fortran and vice versa (ISC).
fortran-lua54
Lua 5.4 interfaces for Fortran 2008 (ISC).
luaf
An incomplete set of Fortran 2003 bindings to Lua 5.1 (MIT).

Lua provides a rich eco-system that may help to extend the scope of Fortran projects.

fortran-lua54

Lua can either be compiled from source or installed as a package. On FreeBSD, run:

# pkg install lang/lua54

On Linux, additional development headers may be required. Clone the GitHub repository and build the interface bindings fortran-lua54 with xmake:

$ git clone https://github.com/interkosmos/fortran-lua54
$ cd fortran-lua54/
$ xmake

Or, run the Fortran Package Manager:

$ fpm build --profile release

Alternatively, we may compile and archive the library manually:

$ gfortran13 -fPIC -c src/lua.f90
$ ar rcs libfortran-lua54.a lua.o

Link your Fortran application with libfortran-lua54.a and the Lua 5.4 library (statically or shared) .

Calling Lua from Fortran

Using the fortran-lua54 interface bindings, we can call arbitrary Lua routines. In the following basic example, a Fortran application will invoke the function hello() in hello.lua:

-- hello.lua
function hello()
   print("Hello, from Lua!")
end

In Fortran, we create a new Lua state, load the standard libraries, and execute the Lua function hello():

! example.f90
program main
    use, intrinsic :: iso_c_binding
    use :: lua
    implicit none
    type(c_ptr) :: l  ! Lua pointer.
    integer     :: rc ! Return code.

    l = lual_newstate()              ! Get Lua state.
    call lual_openlibs(l)            ! Open standard libraries.

    rc = lual_dofile(l, 'hello.lua') ! Load and execute Lua program from file.
    rc = lua_getglobal(l, 'hello')   ! Load function `hello()` onto stack.
    rc = lua_pcall(l, 0, 0, 0)       ! Call function from stack (0 arguments, 0 return values).

    call lua_close(l)
end program main

Link the program against Lua 5.4 and the static library libfortran-lua54.a:

$ gfortran13 -I/usr/local/include/lua54 -L/usr/local/lib/lua/5.4 -o example \
  example.f90 libfortran-lua54.a -llua-5.4

The include and library search paths may differ on Linux. Alternatively, we can run pkg-config(1) to return the correct compiler and linker flags:

$ gfortran13 `pkg-config --cflags lua-5.4` -o example example.f90 libfortran-lua54.a \
  `pkg-config --libs lua-5.4`

The program outputs a greeting from Lua:

$ ./example
Hello, from Lua!

Calling Fortran from Lua

The interface bindings not only let us call Lua from Fortran but also Fortran from Lua. The Fortran procedures we want to access must be registered with lua_register() first.

In this particular example, only the subroutine hello() is exported that prints a friendly greeting to stdout. The function luaopen_fortran() will be called by Lua automatically to make the registered Fortran function available. The postfix fortran in the function name must match the name of the shared library. If the name of the library would be, for instance, libfortran.so, the function must be named luaopen_libfortran() instead.

! fortran.f90
module fortran
    use, intrinsic :: iso_c_binding, only: c_int, c_funloc, c_ptr
    use :: lua
    implicit none

    public :: luaopen_fortran   ! Module registration function.
    public :: hello             ! Routine callable from Lua.
contains
    function luaopen_fortran(l) bind(c) result(n)
        !! Utility function to register the Fortran routine `hello()`.
        type(c_ptr), intent(in), value :: l !! Lua pointer.
        integer(kind=c_int)            :: n !! Always 1.

        call lua_register(l, &              ! Lua pointer.
                          'hello', &        ! Name of the Fortran routine.
                          c_funloc(hello))  ! Function pointer to the Fortran routine.
        n = 1
    end function luaopen_fortran

    function hello(l) bind(c) result(n)
        !! The Fortran routine callable from Lua.
        type(c_ptr), intent(in), value :: l !! Lua pointer.
        integer(kind=c_int)            :: n !! Number of results.

        print '(a)', 'Hello, from Fortran!'
        n = 0
    end function hello
end module fortran

The Lua stack pointer l has to be passed by value. The example Fortran module fortran.f90 and the static library libfortran-lua54.a will be linked into the shared library fortran.so:

$ gfortran13 -I./include -I/usr/local/include/lua54 -L/usr/local/lib/lua/5.4 -fPIC -shared \
  -o fortran.so src/fortran.f90 lib/libfortran-lua54.a -llua-5.4

Linking with the help of pkg-config(1):

$ gfortran13 -I./include `pkg-config --cflags lua-5.4` -fPIC -shared \
  -o fortran.so src/fortran.f90 lib/libfortran-lua54.a `pkg-config --libs lua-5.4`

The project workspace directory then contains:

The Lua script hello.lua has to import the shared library fortran.so with the require() statement first, and then calls the registered Fortran function hello():

-- hello.lua
require("fortran")
hello()

The library must be located in the Lua search path. Executing the script outputs a greeting from Fortran:

$ lua54 ./hello.lua
Hello, from Fortran!

This approach is not platform-independent without recompiling the shared library fortran.so for each targeted system.

References