From 15c77ff99b9f82faa3c2195a72865a8ec7f40bcd Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 25 Aug 2024 00:38:25 +0200 Subject: [PATCH] Updated Lua error handling. --- src/dm_config.f90 | 76 +++++++++++++++++++++----------------- src/dm_lua.f90 | 91 +++++++++++++++++++++++++++++++++++----------- test/dmtestlua.f90 | 26 ++++++------- 3 files changed, 124 insertions(+), 69 deletions(-) diff --git a/src/dm_config.f90 b/src/dm_config.f90 index 298bdce..32d22a2 100644 --- a/src/dm_config.f90 +++ b/src/dm_config.f90 @@ -6,6 +6,7 @@ module dm_config use :: dm_id use :: dm_kind use :: dm_lua + use :: dm_string implicit none (type, external) private @@ -69,43 +70,51 @@ integer function dm_config_open(config, path, name, geocom) result(rc) geocom_ = .false. if (present(geocom)) geocom_ = geocom - open_block: block - rc = E_INVALID - if (len_trim(path) == 0) exit open_block + rc = E_INVALID + if (len_trim(path) == 0) then + call dm_error_out(rc, 'missing path to configuration file') + return + end if - rc = E_NOT_FOUND - if (.not. dm_file_exists(path)) exit open_block + rc = E_NOT_FOUND + if (.not. dm_file_exists(path)) then + call dm_error_out(rc, 'configuration file ' // trim(path) // ' not found') + return + end if + lua_block: block ! Initialise Lua interpreter. rc = dm_lua_init(config%lua, libs=.true.) - if (dm_is_error(rc)) exit open_block + if (dm_is_error(rc)) exit lua_block ! Register DMPACK API for Lua. rc = dm_lua_api_register(config%lua) - if (dm_is_error(rc)) exit open_block + if (dm_is_error(rc)) exit lua_block ! Register GeoCOM API for Lua. if (geocom_) then rc = dm_lua_geocom_register(config%lua, procedures=.true., errors=.true.) - if (dm_is_error(rc)) exit open_block + if (dm_is_error(rc)) exit lua_block end if ! Load and evaluate Lua script. rc = dm_lua_open(config%lua, path, eval=.true.) - if (dm_is_error(rc)) exit open_block + if (dm_is_error(rc)) exit lua_block ! Load Lua table onto stack. - if (present(name)) then - rc = E_INVALID - if (len_trim(name) == 0) exit open_block - rc = dm_lua_table(config%lua, name) - end if - - rc = config_error(rc, name) - end block open_block + if (.not. dm_string_is_present(name)) exit lua_block + rc = dm_lua_table(config%lua, name) + end block lua_block if (dm_is_ok(rc)) return - call dm_error_out(rc, 'failed to read configuration ' // trim(name) // ' from file ' // path) + + if (rc >= E_LUA .and. rc <= E_LUA_FILE) then + call dm_error_out(rc, dm_lua_error_string(config%lua)) + else if (present(name)) then + call dm_error_out(rc, 'failed to read configuration ' // trim(name) // ' from file ' // path) + else + call dm_error_out(rc, 'failed to read configuration from file ' // path) + end if end function dm_config_open integer function dm_config_size(config) result(n) @@ -143,20 +152,19 @@ integer function config_error(error, param) result(rc) character(len=*), intent(in), optional :: param !! Lua table name. rc = E_NONE - if (error == E_NONE) return + if (error == E_NONE) return if (error == E_EMPTY) return rc = E_CONFIG - if (present(param)) then + if (dm_string_is_present(param)) then call dm_error_out(error, 'invalid parameter ' // trim(param) // ' in configuration') - return + else + call dm_error_out(error, 'invalid parameter in configuration', extra=.true.) end if - - call dm_error_out(error, 'invalid parameter in configuration') end function config_error integer function config_get_array_int32(config, name, values) result(rc) - !! Returns configuration values as 4-byte integer array. + !! Returns configuration values as 4-byte integer array in `values`. type(config_type), intent(inout) :: config !! Config type. character(len=*), intent(in) :: name !! Setting name. integer(kind=i4), allocatable, intent(out) :: values(:) !! Setting values. @@ -166,7 +174,7 @@ integer function config_get_array_int32(config, name, values) result(rc) end function config_get_array_int32 integer function config_get_array_int64(config, name, values) result(rc) - !! Returns configuration values as 8-byte integer array. + !! Returns configuration values as 8-byte integer array in `values`. type(config_type), intent(inout) :: config !! Config type. character(len=*), intent(in) :: name !! Setting name. integer(kind=i8), allocatable, intent(out) :: values(:) !! Setting values. @@ -176,7 +184,7 @@ integer function config_get_array_int64(config, name, values) result(rc) end function config_get_array_int64 integer function config_get_int32(config, name, value) result(rc) - !! Returns configuration value as 4-byte integer. + !! Returns configuration value as 4-byte integer in `value`. type(config_type), intent(inout) :: config !! Config type. character(len=*), intent(in) :: name !! Setting name. integer(kind=i4), intent(inout) :: value !! Setting value. @@ -186,7 +194,7 @@ integer function config_get_int32(config, name, value) result(rc) end function config_get_int32 integer function config_get_int64(config, name, value) result(rc) - !! Returns configuration value as 8-byte integer. + !! Returns configuration value as 8-byte integer in `value`. type(config_type), intent(inout) :: config !! Config type. character(len=*), intent(in) :: name !! Setting name. integer(kind=i8), intent(inout) :: value !! Setting value. @@ -196,7 +204,7 @@ integer function config_get_int64(config, name, value) result(rc) end function config_get_int64 integer function config_get_job_list(config, name, value, field) result(rc) - !! Returns configuration value as job list. + !! Returns configuration value as job list in `value`. use :: dm_job type(config_type), intent(inout) :: config !! Config type. @@ -214,7 +222,7 @@ integer function config_get_job_list(config, name, value, field) result(rc) end function config_get_job_list integer function config_get_logical(config, name, value) result(rc) - !! Returns configuration value as logical (if 0 or 1). + !! Returns configuration value as logical in `value` (if 0 or 1). type(config_type), intent(inout) :: config !! Config type. character(len=*), intent(in) :: name !! Setting name. logical, intent(inout) :: value !! Setting value. @@ -224,7 +232,7 @@ integer function config_get_logical(config, name, value) result(rc) end function config_get_logical integer function config_get_real32(config, name, value) result(rc) - !! Returns configuration value as 4-byte real. + !! Returns configuration value as 4-byte real in `value`. type(config_type), intent(inout) :: config !! Config type. character(len=*), intent(in) :: name !! Setting name. real(kind=r4), intent(inout) :: value !! Setting value. @@ -236,7 +244,7 @@ integer function config_get_real32(config, name, value) result(rc) end function config_get_real32 integer function config_get_real64(config, name, value) result(rc) - !! Returns configuration value as 8-byte real. + !! Returns configuration value as 8-byte real in `value`. type(config_type), intent(inout) :: config !! Config type. character(len=*), intent(in) :: name !! Setting name. real(kind=r8), intent(inout) :: value !! Setting value. @@ -246,7 +254,7 @@ integer function config_get_real64(config, name, value) result(rc) end function config_get_real64 integer function config_get_report(config, name, value, field) result(rc) - !! Returns configuration value as report. + !! Returns configuration value as report in `value`. use :: dm_report type(config_type), intent(inout) :: config !! Config type. @@ -273,8 +281,8 @@ integer function config_get_stack(config, name) result(rc) end function config_get_stack integer function config_get_string(config, name, value) result(rc) - !! Returns configuration value as character string. The string is - !! unescaped by default (`\\` is converted to `\`). + !! Returns configuration value as character string in `value`. The + !! string is unescaped by default (`\\` is converted to `\`). type(config_type), intent(inout) :: config !! Config type. character(len=*), intent(in) :: name !! Setting name. character(len=*), intent(inout) :: value !! Setting value. diff --git a/src/dm_lua.f90 b/src/dm_lua.f90 index 9bc2d85..223338a 100644 --- a/src/dm_lua.f90 +++ b/src/dm_lua.f90 @@ -104,21 +104,21 @@ end function dm_lua_callback public :: dm_lua_destroy public :: dm_lua_dump_stack public :: dm_lua_error + public :: dm_lua_error_string public :: dm_lua_escape public :: dm_lua_eval public :: dm_lua_exec public :: dm_lua_field public :: dm_lua_from public :: dm_lua_get - public :: dm_lua_read public :: dm_lua_init public :: dm_lua_is_function public :: dm_lua_is_nil public :: dm_lua_is_opened public :: dm_lua_is_table - public :: dm_lua_last_error public :: dm_lua_open public :: dm_lua_pop + public :: dm_lua_read public :: dm_lua_register public :: dm_lua_set public :: dm_lua_table @@ -182,6 +182,8 @@ integer function dm_lua_call(lua, nargs, nresults) result(rc) integer, intent(in) :: nargs !! Number of arguments. integer, intent(in) :: nresults !! Number of results. + rc = E_NULL + if (.not. c_associated(lua%ptr)) return rc = dm_lua_error(lua_pcall(lua%ptr, nargs, nresults, 0)) end function dm_lua_call @@ -209,6 +211,22 @@ integer function dm_lua_error(lua_error) result(rc) end select end function dm_lua_error + function dm_lua_error_string(lua) result(str) + !! Returns last error message as allocatable character string or an + !! empty string if no message is available. + type(lua_state_type), intent(inout) :: lua !! Lua type. + character(len=:), allocatable :: str !! Last error message. + + lua_block: block + if (.not. c_associated(lua%ptr)) exit lua_block + if (lua_isstring(lua%ptr, -1) == 0) exit lua_block + str = lua_tostring(lua%ptr, -1) + return + end block lua_block + + if (.not. allocated(str)) str = '' + end function dm_lua_error_string + function dm_lua_escape(str) result(res) !! Escapes passed character string by replacing each occurance of `\` !! with `\\`. @@ -233,6 +251,8 @@ integer function dm_lua_eval(lua, command) result(rc) type(lua_state_type), intent(inout) :: lua !! Lua type. character(len=*), intent(in) :: command !! Lua command to evaluate. + rc = E_NULL + if (.not. c_associated(lua%ptr)) return rc = dm_lua_error(lual_dostring(lua%ptr, command)) end function dm_lua_eval @@ -241,12 +261,14 @@ integer function dm_lua_exec(lua, file_path) result(rc) type(lua_state_type), intent(inout) :: lua !! Lua type. character(len=*), intent(in) :: file_path !! Path to Lua script file. + rc = E_NULL + if (.not. c_associated(lua%ptr)) return rc = dm_lua_error(lual_dofile(lua%ptr, trim(file_path))) end function dm_lua_exec integer function dm_lua_init(lua, libs) result(rc) !! Initialises Lua interpreter and opens libraries, unless `libs` is - !! `.false.`. Returns `E_INVALID` if the Lua pointer is already + !! `.false.`. Returns `E_EXIST` if the Lua pointer is already !! associated, and `E_LUA` if one of the Lua calls failed. type(lua_state_type), intent(inout) :: lua !! Lua type. logical, intent(in), optional :: libs !! Open Lua libraries. @@ -256,21 +278,23 @@ integer function dm_lua_init(lua, libs) result(rc) libs_ = .true. if (present(libs)) libs_ = libs - rc = E_INVALID + rc = E_EXIST if (c_associated(lua%ptr)) return rc = E_LUA lua%ptr = lual_newstate() if (.not. c_associated(lua%ptr)) return - if (libs_) call lual_openlibs(lua%ptr) rc = E_NONE + if (libs_) call lual_openlibs(lua%ptr) end function dm_lua_init logical function dm_lua_is_function(lua) result(is_function) !! Returns `.true.` if element on top of stack is of type function. type(lua_state_type), intent(inout) :: lua !! Lua type. + is_function = .false. + if (.not. c_associated(lua%ptr)) return is_function = (lua_isfunction(lua%ptr, -1) == 1) end function dm_lua_is_function @@ -278,6 +302,8 @@ logical function dm_lua_is_nil(lua) result(is_nil) !! Returns `.true.` if element on top of stack is nil. type(lua_state_type), intent(inout) :: lua !! Lua type. + is_nil = .true. + if (.not. c_associated(lua%ptr)) return is_nil = (lua_isnil(lua%ptr, -1) == 1) end function dm_lua_is_nil @@ -292,31 +318,26 @@ logical function dm_lua_is_table(lua) result(is_table) !! Returns `.true.` if element on top of stack is of type table. type(lua_state_type), intent(inout) :: lua !! Lua type. + is_table = .false. + if (.not. c_associated(lua%ptr)) return is_table = (lua_istable(lua%ptr, -1) == 1) end function dm_lua_is_table - function dm_lua_last_error(lua) result(str) - !! Returns last error message as allocatable character string. - type(lua_state_type), intent(inout) :: lua !! Lua type. - character(len=:), allocatable :: str !! Last error message. - - if (lua_isstring(lua%ptr, -1) == 1) then - str = lua_tostring(lua%ptr, -1) - return - end if - - str = '' - end function dm_lua_last_error - integer function dm_lua_open(lua, file_path, eval) result(rc) - !! Opens Lua script and executes it by default. + !! Opens Lua script and executes it by default. The function returns the + !! following error codes: + !! + !! * `E_LUA` on internal Lua error. + !! * `E_NOT_FOUND` if the file could not be found. + !! * `E_NULL` if the Lua interpreter is not initialised. + !! type(lua_state_type), intent(inout) :: lua !! Lua type. character(len=*), intent(in) :: file_path !! Path to Lua script. logical, intent(in), optional :: eval !! Evaluate script once. logical :: eval_ - rc = E_INVALID + rc = E_NULL if (.not. c_associated(lua%ptr)) return rc = E_NOT_FOUND @@ -334,13 +355,22 @@ integer function dm_lua_open(lua, file_path, eval) result(rc) end function dm_lua_open integer function dm_lua_table(lua, name, n) result(rc) - !! Loads global table of given name. + !! Loads global table of given name. The function returns the following + !! error codes: + !! + !! * `E_NOT_FOUND` if the name does not exist. + !! * `E_NULL` if the Lua interpreter is not initialised. + !! * `E_TYPE` if variable on stack is not a table. + !! type(lua_state_type), intent(inout) :: lua !! Lua type. character(len=*), intent(in) :: name !! Name of table. integer, intent(out), optional :: n !! Number of elements in table. if (present(n)) n = 0 + rc = E_NULL + if (.not. c_associated(lua%ptr)) return + rc = E_NOT_FOUND if (lua_getglobal(lua%ptr, trim(name)) == LUA_TNIL) return @@ -355,9 +385,11 @@ integer function dm_lua_table(lua, name, n) result(rc) end function dm_lua_table integer function dm_lua_table_size(lua) result(n) - !! Returns size of table on stack. + !! Returns size of table on stack. Returns `-1` on error. type(lua_state_type), intent(inout) :: lua !! Lua type. + n = -1 + if (.not. c_associated(lua%ptr)) return n = int(lua_rawlen(lua%ptr, -1), kind=i4) end function dm_lua_table_size @@ -366,6 +398,8 @@ integer(kind=i4) function dm_lua_to_int32(lua, idx) result(value) type(lua_state_type), intent(inout) :: lua !! Lua type. integer, intent(in) :: idx !! Stack index. + value = 0_i4 + if (.not. c_associated(lua%ptr)) return value = int(lua_tointeger(lua%ptr, idx), kind=i4) end function dm_lua_to_int32 @@ -374,6 +408,8 @@ integer(kind=i8) function dm_lua_to_int64(lua, idx) result(value) type(lua_state_type), intent(inout) :: lua !! Lua type. integer, intent(in) :: idx !! Stack index. + value = 0_i8 + if (.not. c_associated(lua%ptr)) return value = lua_tointeger(lua%ptr, idx) end function dm_lua_to_int64 @@ -382,6 +418,8 @@ logical function dm_lua_to_logical(lua, idx) result(value) type(lua_state_type), intent(inout) :: lua !! Lua type. integer, intent(in) :: idx !! Stack index. + value = .false. + if (.not. c_associated(lua%ptr)) return value = lua_toboolean(lua%ptr, idx) end function dm_lua_to_logical @@ -390,6 +428,8 @@ real(kind=r4) function dm_lua_to_real32(lua, idx) result(value) type(lua_state_type), intent(inout) :: lua !! Lua type. integer, intent(in) :: idx !! Stack index. + value = 0.0_r4 + if (.not. c_associated(lua%ptr)) return value = real(lua_tonumber(lua%ptr, idx), kind=r4) end function dm_lua_to_real32 @@ -398,6 +438,8 @@ real(kind=r8) function dm_lua_to_real64(lua, idx) result(value) type(lua_state_type), intent(inout) :: lua !! Lua type. integer, intent(in) :: idx !! Stack index. + value = 0.0_r8 + if (.not. c_associated(lua%ptr)) return value = lua_tonumber(lua%ptr, idx) end function dm_lua_to_real64 @@ -407,6 +449,11 @@ function dm_lua_to_string(lua, idx) result(value) integer, intent(in) :: idx !! Stack index. character(len=:), allocatable :: value !! String value. + if (.not. c_associated(lua%ptr)) then + value = '' + return + end if + value = lua_tostring(lua%ptr, idx) end function dm_lua_to_string diff --git a/test/dmtestlua.f90 b/test/dmtestlua.f90 index 23ea5e3..2bb1660 100644 --- a/test/dmtestlua.f90 +++ b/test/dmtestlua.f90 @@ -34,8 +34,8 @@ program dmtestlua contains logical function test01() result(stat) !! Reads Lua global variables from file. - character(len=*), parameter :: FOO = 'bar' - integer, parameter :: VALUE = 420 + character(len=*), parameter :: FOO = 'bar' + integer, parameter :: VALUE = 420 character(len=32) :: str integer :: rc @@ -81,7 +81,7 @@ logical function test01() result(stat) call dm_lua_destroy(lua) if (dm_is_error(rc)) then - call dm_error_out(rc, dm_lua_last_error(lua)) + call dm_error_out(rc, dm_lua_error_string(lua)) return end if @@ -180,7 +180,7 @@ logical function test03() result(stat) call dm_observ_out(observ) print '(72("."))' - call dm_error_out(rc, dm_lua_last_error(lua)) + call dm_error_out(rc, dm_lua_error_string(lua)) call dm_lua_destroy(lua) if (dm_is_error(rc)) return @@ -223,7 +223,7 @@ logical function test04() result(stat) call dm_lua_dump_stack(lua) end block test_block - call dm_error_out(rc, dm_lua_last_error(lua)) + call dm_error_out(rc, dm_lua_error_string(lua)) call dm_lua_destroy(lua) print *, 'Validating observations ...' @@ -275,7 +275,7 @@ logical function test05() result(stat) if (dm_is_error(rc)) exit test_block end block test_block - call dm_error_out(rc, dm_lua_last_error(lua)) + call dm_error_out(rc, dm_lua_error_string(lua)) call dm_lua_destroy(lua) print *, 'Validating jobs ...' @@ -330,7 +330,7 @@ logical function test06() result(stat) rc = E_NONE end block test_block - call dm_error_out(rc, dm_lua_last_error(lua)) + call dm_error_out(rc, dm_lua_error_string(lua)) call dm_lua_destroy(lua) if (dm_is_error(rc)) return @@ -366,7 +366,7 @@ logical function test07() result(stat) rc = E_NONE end block test_block - call dm_error_out(rc, dm_lua_last_error(lua)) + call dm_error_out(rc, dm_lua_error_string(lua)) call dm_lua_destroy(lua) if (dm_is_error(rc)) return @@ -422,7 +422,7 @@ logical function test08() result(stat) rc = E_NONE end block test_block - call dm_error_out(rc, dm_lua_last_error(lua)) + call dm_error_out(rc, dm_lua_error_string(lua)) call dm_lua_destroy(lua) if (dm_is_error(rc)) return @@ -476,7 +476,7 @@ logical function test09() result(stat) rc = E_NONE end block test_block - call dm_error_out(rc, 'Lua error: ' // dm_lua_last_error(lua)) + call dm_error_out(rc, 'Lua error: ' // dm_lua_error_string(lua)) call dm_lua_destroy(lua) if (dm_is_error(rc)) return @@ -523,7 +523,7 @@ logical function test10() result(stat) rc = E_NONE end block test_block - call dm_error_out(rc, 'Lua error: ' // dm_lua_last_error(lua)) + call dm_error_out(rc, 'Lua error: ' // dm_lua_error_string(lua)) call dm_lua_destroy(lua) if (dm_is_error(rc)) return @@ -570,7 +570,7 @@ logical function test11() result(stat) rc = E_NONE end block test_block - call dm_error_out(rc, dm_lua_last_error(lua)) + call dm_error_out(rc, dm_lua_error_string(lua)) call dm_lua_destroy(lua) call dm_error_out(rc) @@ -611,7 +611,7 @@ logical function test12() result(stat) call dm_lua_destroy(lua) if (dm_is_error(rc)) then - call dm_error_out(rc, dm_lua_last_error(lua)) + call dm_error_out(rc, dm_lua_error_string(lua)) return end if