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 (MIT)
Limited ISO C binding interface to Lua and library for using Lua scripts as configuration files in Fortran applications.
f2k3-lua (MIT)
Fortran 2003 interface to Lua for loading configuration files.
FortLua (MIT)
Fork of AOTUS, mainly to read and write Lua configuration files.
fortran-lua (ISC)
Generic Lua 5.3 interfaces for Fortran 2003, for calling Lua from Fortran and vice versa.
luaf (MIT)
An incomplete set of Fortran 2003 bindings to Lua 5.1.

Lua has a rich eco-system that may help to extent the scope of Fortran applications.

Calling Lua from Fortran

Using the fortran-lua interface bindings, we can call arbitrary Lua routines. In the following basic example, a Fortran application will invoke 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 example function:

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

    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.3 and the static library libfortran-lua53.a:

$ gfortran10 -I/usr/local/include/lua53/ -L/usr/local/lib/lua/5.3/ \
  -o example example.f90 libfortran-lua53.a -llua-5.3
$ ./example
Hello, from Lua!

Calling Fortran from Lua

The Lua 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 Fortran routines available. For real interoperability, the Lua stack pointer passed to the Fortran routine can be altered with the provided ISO C binding interfaces of fortran-lua53.

! 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)
        !! Utility function to register the Fortran routine `hello()`.
        type(c_ptr), intent(in), value :: l
        integer(kind=c_int)            :: luaopen_fortran

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

    subroutine hello(l) bind(c)
        !! The Fortran routine callable from Lua.
        type(c_ptr), intent(in), value :: l

        print '(a)', 'Hello, from Fortran!'
    end subroutine hello
end module fortran

The Lua stack pointer has to be passed by value (attribute value for dummy argument l). Make sure to compile the Lua bindings lua.f90 with flag -fPIC:

$ gfortran10 -fPIC -c src/lua.f90

The example Fortran module fortran.f90 and the static bindings library libfortran-lua53.a will be linked in the target shared library fortran.so:

$ gfortran10 -I/usr/local/include/lua53/ -L/usr/local/lib/lua/5.3/ \
  -shared -fPIC -o fortran.so src/fortran.f90 libfortran-lua53.a -llua-5.3

The workspace directory now contains the following files:

Our Lua program hello.lua then simply imports the shared library fortran.so and calls the subroutine hello():

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

Executing the Lua program outputs the greeting from Fortran:

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

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

References