diff options
Diffstat (limited to 'macros/luatex/generic/luakeys/luakeys.lua')
-rw-r--r-- | macros/luatex/generic/luakeys/luakeys.lua | 511 |
1 files changed, 338 insertions, 173 deletions
diff --git a/macros/luatex/generic/luakeys/luakeys.lua b/macros/luatex/generic/luakeys/luakeys.lua index ae689d02c1..3221fc3efd 100644 --- a/macros/luatex/generic/luakeys/luakeys.lua +++ b/macros/luatex/generic/luakeys/luakeys.lua @@ -36,13 +36,59 @@ if not token then } end +--- Merge two tables in the first specified table. +--- The `merge_tables` function copies all keys from the `source` table +--- to a target table. It returns the modified target table. +--- +---@see https://stackoverflow.com/a/1283608/10193818 +--- +---@param target table +---@param source table +--- +---@return table target The modified target table. +local function merge_tables(target, source) + for key, value in pairs(source) do + if type(value) == 'table' then + if type(target[key] or false) == 'table' then + merge_tables(target[key] or {}, source[key] or {}) + elseif target[key] == nil then + target[key] = value + end + elseif target[key] == nil then + target[key] = value + end + end + return target +end + +---Clone a table. +--- +---@see http://lua-users.org/wiki/CopyTable +--- +---@param orig table +--- +---@return table +local function clone_table(orig) + local orig_type = type(orig) + local copy + if orig_type == 'table' then + copy = {} + for orig_key, orig_value in next, orig, nil do + copy[clone_table(orig_key)] = clone_table(orig_value) + end + setmetatable(copy, clone_table(getmetatable(orig))) + else -- number, string, boolean, etc + copy = orig + end + return copy +end + local utils = { --- Get the size of an array like table `{ 'one', 'two', 'three' }` = 3. - -- - -- @tparam table value A table or any input. - -- - -- @treturn number The size of the array like table. 0 if the input is - -- no table or the table is empty. + --- + ---@param value table # A table or any input. + --- + ---@return number # The size of the array like table. 0 if the input is no table or the table is empty. get_array_size = function(value) local count = 0 if type(value) == 'table' then @@ -53,12 +99,11 @@ local utils = { return count end, - --- Get the size of a table `{ one = 'one', 'two', 'three' }` = 3. - -- - -- @tparam table value A table or any input. - -- - -- @treturn number The size of the array like table. 0 if the input is - -- no table or the table is empty. + ---Get the size of a table `{ one = 'one', 'two', 'three' }` = 3. + --- + ---@param value table|any # A table or any input. + --- + ---@return number # The size of the array like table. 0 if the input is no table or the table is empty. get_table_size = function(value) local count = 0 if type(value) == 'table' then @@ -69,6 +114,10 @@ local utils = { return count end, + merge_tables = merge_tables, + + clone_table = clone_table, + remove_from_array = function(array, element) for index, value in pairs(array) do if element == value then @@ -77,58 +126,95 @@ local utils = { end end end, -} ---- https://stackoverflow.com/a/1283608/10193818 -local function merge_tables(target, t2) - for k, v in pairs(t2) do - if type(v) == 'table' then - if type(target[k] or false) == 'table' then - merge_tables(target[k] or {}, t2[k] or {}) - elseif target[k] == nil then - target[k] = v + ---Scan for an optional argument. + --- + ---@param initial_delimiter? string # The character that marks the beginning of an optional argument (by default `[`). + ---@param end_delimiter? string # The character that marks the end of an optional argument (by default `]`). + --- + ---@return string|nil # The string that was enclosed by the delimiters. The delimiters themselves are not returned. + scan_oarg = function(initial_delimiter, end_delimiter) + if initial_delimiter == nil then + initial_delimiter = '[' + end + + if end_delimiter == nil then + end_delimiter = ']' + end + + local function convert_token(t) + if t.index ~= nil then + return utf8.char(t.index) + else + return '\\' .. t.csname end - elseif target[k] == nil then - target[k] = v end - end - return target -end ---- http://lua-users.org/wiki/CopyTable -local function clone_table(orig) - local orig_type = type(orig) - local copy - if orig_type == 'table' then - copy = {} - for orig_key, orig_value in next, orig, nil do - copy[clone_table(orig_key)] = clone_table(orig_value) + local function get_next_char() + local t = token.get_next() + return convert_token(t), t end - setmetatable(copy, clone_table(getmetatable(orig))) - else -- number, string, boolean, etc - copy = orig - end - return copy -end ---- This table stores all allowed option keys. -local all_options = { - convert_dimensions = false, - debug = false, - default = true, - defaults = false, - defs = false, - format_keys = false, - hooks = {}, - naked_as_value = false, - no_error = false, - postprocess = false, - preprocess = false, - unpack = true, + local char, t = get_next_char() + if char == initial_delimiter then + local oarg = {} + char = get_next_char() + while char ~= end_delimiter do + table.insert(oarg, char) + char = get_next_char() + end + return table.concat(oarg, '') + else + token.put_next(t) + end + end, +} + +local namespace = { + opts = { + convert_dimensions = false, + debug = false, + default = true, + defaults = false, + defs = false, + format_keys = false, + hooks = {}, + naked_as_value = false, + no_error = false, + unpack = true, + }, + + hooks = { + kv_string = true, + keys_before_opts = true, + result_before_opts = true, + keys_before_def = true, + result_before_def = true, + keys = true, + result = true, + }, + + attrs = { + alias = true, + always_present = true, + choices = true, + data_type = true, + default = true, + exclusive_group = true, + l3_tl_set = true, + macro = true, + match = true, + name = true, + opposite_keys = true, + pick = true, + process = true, + required = true, + sub_keys = true, + }, } --- The default options. -local default_options = clone_table(all_options) +local default_options = clone_table(namespace.opts) local function throw_error(message) if type(tex.error) == 'function' then @@ -144,16 +230,15 @@ local l3_code_cctab = 10 -- @section --- The function `render(tbl)` reverses the function --- `parse(kv_string)`. It takes a Lua table and converts this table --- into a key-value string. The resulting string usually has a --- different order as the input table. In Lua only tables with --- 1-based consecutive integer keys (a.k.a. array tables) can be --- parsed in order. --- --- @tparam table result A table to be converted into a key-value string. --- --- @treturn string A key-value string that can be passed to a TeX --- macro. +--- `parse(kv_string)`. It takes a Lua table and converts this table +--- into a key-value string. The resulting string usually has a +--- different order as the input table. In Lua only tables with +--- 1-based consecutive integer keys (a.k.a. array tables) can be +--- parsed in order. +--- +---@param result table # A table to be converted into a key-value string. +--- +---@return string # A key-value string that can be passed to a TeX macro. local function render(result) local function render_inner(result) local output = {} @@ -183,20 +268,19 @@ local function render(result) end --- The function `stringify(tbl, for_tex)` converts a Lua table into a --- printable string. Stringify a table means to convert the table into --- a string. This function is used to realize the `debug` function. --- `stringify(tbl, true)` (`for_tex = true`) generates a string which --- can be embeded into TeX documents. The macro `\luakeysdebug{}` uses --- this option. `stringify(tbl, false)` or `stringify(tbl)` generate a --- string suitable for the terminal. --- --- @tparam table result A table to stringify. --- --- @tparam boolean for_tex Stringify the table into a text string that --- can be embeded inside a TeX document via tex.print(). Curly braces --- and whites spaces are escaped. --- --- https://stackoverflow.com/a/54593224/10193818 +--- printable string. Stringify a table means to convert the table into +--- a string. This function is used to realize the `debug` function. +--- `stringify(tbl, true)` (`for_tex = true`) generates a string which +--- can be embeded into TeX documents. The macro `\luakeysdebug{}` uses +--- this option. `stringify(tbl, false)` or `stringify(tbl)` generate a +--- string suitable for the terminal. +--- +---@see https://stackoverflow.com/a/54593224/10193818 +--- +---@param result table # A table to stringify. +---@param for_tex? boolean # Stringify the table into a text string that can be embeded inside a TeX document via tex.print(). Curly braces and whites spaces are escaped. +--- +---@return string local function stringify(result, for_tex) local line_break, start_bracket, end_bracket, indent @@ -242,7 +326,8 @@ local function stringify(result, for_tex) add(0, stringify_inner(value, depth + 1)) add(depth, end_bracket .. ','); else - add(depth, key .. ' = ' .. start_bracket .. end_bracket .. ',') + add(depth, + key .. ' = ' .. start_bracket .. end_bracket .. ',') end else if (type(value) == 'string') then @@ -259,19 +344,17 @@ local function stringify(result, for_tex) return table.concat(output, line_break) end - return - start_bracket .. line_break .. stringify_inner(result, 1) .. line_break .. - end_bracket + return start_bracket .. line_break .. stringify_inner(result, 1) .. + line_break .. end_bracket end ---- The function `debug(tbl)` pretty prints a Lua table to standard +--- The function `debug(result)` pretty prints a Lua table to standard -- output (stdout). It is a utility function that can be used to -- debug and inspect the resulting Lua table of the function -- `parse`. You have to compile your TeX document in a console to -- see the terminal output. -- --- @tparam table result A table to be printed to standard output for --- debugging purposes. +---@param result table # A table to be printed to standard output for debugging purposes. local function debug(result) print('\n' .. stringify(result, false)) end @@ -280,19 +363,23 @@ end -- @section --- Generate the PEG parser using Lpeg. --- --- Explanations of some LPeg notation forms: --- --- * `patt ^ 0` = `expression *` --- * `patt ^ 1` = `expression +` --- * `patt ^ -1` = `expression ?` --- * `patt1 * patt2` = `expression1 expression2`: Sequence --- * `patt1 + patt2` = `expression1 / expression2`: Ordered choice --- --- * [TUGboat article: Parsing complex data formats in LuaTEX with LPEG](https://tug.org/TUGboat/tb40-2/tb125menke-Patterndf) --- --- @treturn userdata The parser. -local function generate_parser(initial_rule, convert_dimensions) +--- +--- Explanations of some LPeg notation forms: +--- +--- * `patt ^ 0` = `expression *` +--- * `patt ^ 1` = `expression +` +--- * `patt ^ -1` = `expression ?` +--- * `patt1 * patt2` = `expression1 expression2`: Sequence +--- * `patt1 + patt2` = `expression1 / expression2`: Ordered choice +--- +--- * [TUGboat article: Parsing complex data formats in LuaTEX with LPEG](https://tug.or-g/TUGboat/tb40-2/tb125menke-Patterndf) +--- +---@param initial_rule string # The name of the first rule of the grammar table passed to the `lpeg.P(attern)` function (e. g. `list`, `number`). +---@param convert_dimensions? boolean # Whether the dimensions should be converted to scaled points (by default `false`). +--- +---@return userdata # The parser. +local function generate_parser(initial_rule, + convert_dimensions) if convert_dimensions == nil then convert_dimensions = false end @@ -315,7 +402,17 @@ local function generate_parser(initial_rule, convert_dimensions) return white_space ^ 0 * Pattern(match) * white_space ^ 0 end + --- Convert a dimension to an normalized dimension string or an + --- integer in the scaled points format. + --- + ---@param input string + --- + ---@return integer|string # A dimension as an integer or a dimension string. local capture_dimension = function(input) + -- Remove all whitespaces + input = input:gsub('%s+', '') + -- Convert the unit string into lowercase. + input = input:lower() if convert_dimensions then return tex.sp(input) else @@ -325,27 +422,27 @@ local function generate_parser(initial_rule, convert_dimensions) --- Add values to a table in two modes: -- - -- # Key value pair + -- Key-value pair: -- - -- If arg1 and arg2 are not nil, then arg1 is the key and arg2 is the + -- If `arg1` and `arg2` are not nil, then `arg1` is the key and `arg2` is the -- value of a new table entry. -- - -- # Index value + -- Indexed value: -- - -- If arg2 is nil, then arg1 is the value and is added as an indexed + -- If `arg2` is nil, then `arg1` is the value and is added as an indexed -- (by an integer) value. -- - -- @tparam table table - -- @tparam mixed arg1 - -- @tparam mixed arg2 - -- - -- @treturn table - local add_to_table = function(table, arg1, arg2) + ---@param result table # The result table to which an additional key-value pair or value should to be added + ---@param arg1 any # The key or the value. + ---@param arg2? any # Always the value. + --- + ---@return table # The result table to which an additional key-value pair or value has been added. + local add_to_table = function(result, arg1, arg2) if arg2 == nil then - local index = #table + 1 - return rawset(table, index, arg1) + local index = #result + 1 + return rawset(result, index, arg1) else - return rawset(table, arg1, arg2) + return rawset(result, arg1, arg2) end end @@ -438,6 +535,7 @@ local function generate_parser(initial_rule, convert_dimensions) -- 'bp' / 'BP' / 'cc' / etc. -- https://raw.githubusercontent.com/latex3/lualibs/master/lualibs-util-dim.lua + -- https://github.com/TeX-Live/luatex/blob/51db1985f5500dafd2393aa2e403fefa57d3cb76/source/texk/web2c/luatexdir/lua/ltexlib.c#L434-L625 unit = Pattern('bp') + Pattern('BP') + Pattern('cc') + Pattern('CC') + @@ -447,10 +545,12 @@ local function generate_parser(initial_rule, convert_dimensions) Pattern('ex') + Pattern('EX') + Pattern('in') + Pattern('IN') + Pattern('mm') + Pattern('MM') + + Pattern('mu') + Pattern('MU') + Pattern('nc') + Pattern('NC') + Pattern('nd') + Pattern('ND') + Pattern('pc') + Pattern('PC') + Pattern('pt') + Pattern('PT') + + Pattern('px') + Pattern('PX') + Pattern('sp') + Pattern('SP'), -- '"' ('\"' / !'"')* '"' @@ -483,7 +583,8 @@ local function visit_tree(tree, callback_func) callback_func) for key, value in pairs(current) do if type(value) == 'table' then - value = visit_tree_recursive(tree, value, {}, depth + 1, callback_func) + value = visit_tree_recursive(tree, value, {}, depth + 1, + callback_func) end key, value = callback_func(key, value, depth, current, tree) @@ -514,7 +615,7 @@ local is = { return true end local parser = generate_parser('boolean_only', false) - local result = parser:match(value) + local result = parser:match(tostring(value)) return result ~= nil end, @@ -523,7 +624,7 @@ local is = { return false end local parser = generate_parser('dimension_only', false) - local result = parser:match(value) + local result = parser:match(tostring(value)) return result ~= nil end, @@ -543,7 +644,7 @@ local is = { return true end local parser = generate_parser('number_only', false) - local result = parser:match(value) + local result = parser:match(tostring(value)) return result ~= nil end, @@ -555,14 +656,13 @@ local is = { --- Apply the key-value-pair definitions (defs) on an input table in a --- recursive fashion. --- ----@param defs table A table containing all definitions. ----@param opts table The parse options table. ----@param input table The current input table. ----@param output table The current output table. ----@param unknown table Always the root unknown table. ----@param key_path table An array of key names leading to the current ----@param input_root table The root input table ---- input and output table. +---@param defs table # A table containing all definitions. +---@param opts table # The parse options table. +---@param input table # The current input table. +---@param output table # The current output table. +---@param unknown table # Always the root unknown table. +---@param key_path table # An array of key names leading to the current +---@param input_root table # The root input table input and output table. local function apply_definitions(defs, opts, input, @@ -583,7 +683,7 @@ local function apply_definitions(defs, return new_key_path end - local function set_default_value(def) + local function get_default_value(def) if def.default ~= nil then return def.default elseif opts ~= nil and opts.default ~= nil then @@ -599,7 +699,7 @@ local function apply_definitions(defs, return value -- naked keys: values with integer keys elseif utils.remove_from_array(input, search_key) ~= nil then - return set_default_value(def) + return get_default_value(def) end end @@ -634,7 +734,7 @@ local function apply_definitions(defs, always_present = function(value, key, def) if value == nil and def.always_present then - return set_default_value(def) + return get_default_value(def) end end, @@ -692,9 +792,10 @@ local function apply_definitions(defs, throw_error('Unknown data type: ' .. def.data_type) end if converted == nil then - throw_error('The value “' .. value .. '” of the key “' .. key .. - '” could not be converted into the data type “' .. - def.data_type .. '”!') + throw_error( + 'The value “' .. value .. '” of the key “' .. key .. + '” could not be converted into the data type “' .. + def.data_type .. '”!') else return converted end @@ -723,7 +824,8 @@ local function apply_definitions(defs, return end if def.l3_tl_set ~= nil then - tex.print(l3_code_cctab, '\\tl_set:Nn \\g_' .. def.l3_tl_set .. '_tl') + tex.print(l3_code_cctab, + '\\tl_set:Nn \\g_' .. def.l3_tl_set .. '_tl') tex.print('{' .. value .. '}') end end, @@ -747,8 +849,9 @@ local function apply_definitions(defs, end local match = string.match(value, def.match) if match == nil then - throw_error('The value “' .. value .. '” of the key “' .. key .. - '” does not match “' .. def.match .. '”!') + throw_error( + 'The value “' .. value .. '” of the key “' .. key .. + '” does not match “' .. def.match .. '”!') else return match end @@ -780,6 +883,41 @@ local function apply_definitions(defs, end end, + pick = function(value, key, def) + if def.pick then + if type(def.pick) == 'string' and is[def.pick] == nil then + throw_error( + 'Wrong setting. Allowed settings for the attribute “def.pick” are: true, boolean, dimension, integer, number, string. Got “' .. + tostring(def.pick) .. '”.') + end + + if value ~= nil then + return value + end + for i, v in pairs(input) do + -- We can not use ipairs here. `ipairs(t)` iterates up to the + -- first absent index. Values are deleted from the `input` + -- table. + if type(i) == 'number' then + local picked_value = nil + -- Pick a value by type: boolean, dimension, integer, number, string + if is[def.pick] ~= nil then + if is[def.pick](v) then + picked_value = v + end + -- Pick every value + elseif v ~= nil then + picked_value = v + end + if picked_value ~= nil then + input[i] = nil + return picked_value + end + end + end + end + end, + required = function(value, key, def) if def.required ~= nil and def.required and value == nil then throw_error(string.format('Missing required key “%s”!', key)) @@ -797,8 +935,8 @@ local function apply_definitions(defs, elseif type(value) == 'table' then v = value end - v = apply_definitions(def.sub_keys, opts, v, output[key], unknown, - add_to_key_path(key_path, key), input_root) + v = apply_definitions(def.sub_keys, opts, v, output[key], + unknown, add_to_key_path(key_path, key), input_root) if utils.get_table_size(v) > 0 then return v end @@ -821,23 +959,33 @@ local function apply_definitions(defs, end for index, def in pairs(defs) do - --- Find key and def + -- Find key and def local key - if type(def) == 'table' and def.name == nil and type(index) == 'string' then + -- `{ key1 = { }, key2 = { } }` + if type(def) == 'table' and def.name == nil and type(index) == + 'string' then key = index + -- `{ { name = 'key1' }, { name = 'key2' } }` elseif type(def) == 'table' and def.name ~= nil then key = def.name + -- Definitions as strings in an array: `{ 'key1', 'key2' }` elseif type(index) == 'number' and type(def) == 'string' then key = def - def = { default = true } + def = { default = get_default_value({}) } end if type(def) ~= 'table' then - throw_error('Key definition must be a table') + throw_error('Key definition must be a table!') + end + + for attr, _ in pairs(def) do + if namespace.attrs[attr] == nil then + throw_error('Unknown definition attribute: ' .. tostring(attr)) + end end if key == nil then - throw_error('key name couldn’t be detected!') + throw_error('Key name couldn’t be detected!') end local value = find_value(key, def) @@ -845,6 +993,7 @@ local function apply_definitions(defs, for _, def_opt in ipairs({ 'alias', 'opposite_keys', + 'pick', 'always_present', 'required', 'data_type', @@ -888,14 +1037,14 @@ end --- Parse a LaTeX/TeX style key-value string into a Lua table. --- ----@param kv_string string A string in the TeX/LaTeX style key-value format as described above. ----@param opts table A table containing the settings: +---@param kv_string string # A string in the TeX/LaTeX style key-value format as described above. +---@param opts table # A table containing the settings: --- `convert_dimensions`, `unpack`, `naked_as_value`, `converter`, --- `debug`, `preprocess`, `postprocess`. -- ----@return table result The final result of all individual parsing and normalization steps. ----@return table unknown A table with unknown, undefinied key-value pairs. ----@return table raw The unprocessed, raw result of the LPeg parser. +---@return table result # The final result of all individual parsing and normalization steps. +---@return table unknown # A table with unknown, undefinied key-value pairs. +---@return table raw # The unprocessed, raw result of the LPeg parser. local function parse(kv_string, opts) if kv_string == nil then return {} @@ -903,21 +1052,21 @@ local function parse(kv_string, opts) --- Normalize the parse options. --- - --- @param opts table Options in a raw format. The table may be empty or some keys are not set. + ---@param opts table # Options in a raw format. The table may be empty or some keys are not set. --- - --- @return table + ---@return table local function normalize_opts(opts) if type(opts) ~= 'table' then opts = {} end for key, _ in pairs(opts) do - if all_options[key] == nil then + if namespace.opts[key] == nil then throw_error('Unknown parse option: ' .. tostring(key) .. '!') end end local old_opts = opts opts = {} - for name, _ in pairs(all_options) do + for name, _ in pairs(namespace.opts) do if old_opts[name] ~= nil then opts[name] = old_opts[name] else @@ -925,18 +1074,8 @@ local function parse(kv_string, opts) end end - local hooks = { - kv_string = true, - keys_before_opts = true, - result_before_opts = true, - keys_before_def = true, - result_before_def = true, - keys = true, - result = true, - } - for hook in pairs(opts.hooks) do - if hooks[hook] == nil then + if namespace.hooks[hook] == nil then throw_error('Unknown hook: ' .. tostring(hook) .. '!') end end @@ -983,8 +1122,8 @@ local function parse(kv_string, opts) -- tasks are performed on the raw input table coming directly from -- the PEG parser: -- - --- @param result table The raw input table coming directly from the PEG parser - --- @param opts table Some options. + ---@param result table # The raw input table coming directly from the PEG parser + ---@param opts table # Some options. local function apply_opts(result, opts) local callbacks = { unpack = function(key, value) @@ -1012,7 +1151,8 @@ local function parse(kv_string, opts) elseif style == 'upper' then key = key:upper() else - throw_error('Unknown style to format keys: ' .. tostring(style) .. + throw_error('Unknown style to format keys: ' .. + tostring(style) .. ' Allowed styles are: lower, snake, upper') end end @@ -1031,8 +1171,9 @@ local function parse(kv_string, opts) if opts.format_keys then if type(opts.format_keys) ~= 'table' then - throw_error('The option “format_keys” has to be a table not ' .. - type(opts.format_keys)) + throw_error( + 'The option “format_keys” has to be a table not ' .. + type(opts.format_keys)) end result = visit_tree(result, callbacks.format_key) end @@ -1045,8 +1186,8 @@ local function parse(kv_string, opts) local unknown = nil if type(opts.defs) == 'table' then apply_hooks('before_defs') - result, unknown = apply_definitions(opts.defs, opts, result, {}, {}, {}, - clone_table(result)) + result, unknown = apply_definitions(opts.defs, opts, result, {}, {}, + {}, clone_table(result)) end apply_hooks() @@ -1077,6 +1218,26 @@ local result_store = {} -- @section local export = { + version = { 0, 7, 0 }, + + namespace = namespace, + + ---This function is used in the documentation. + --- + ---@param from string # A key in the namespace table, either `opts`, `hook` or `attrs`. + print_names = function(from) + local names = {} + for name in pairs(namespace[from]) do + table.insert(names, name) + end + table.sort(names) + tex.print(table.concat(names, ', ')) + end, + + print_default = function(from, name) + tex.print(tostring(namespace[from][name])) + end, + --- @see default_options opts = default_options, @@ -1089,7 +1250,10 @@ local export = { define = function(defs, opts) return function(kv_string, inner_opts) local options - if inner_opts ~= nil then + + if inner_opts ~= nil and opts ~= nil then + options = merge_tables(opts, inner_opts) + elseif inner_opts ~= nil then options = inner_opts elseif opts ~= nil then options = opts @@ -1116,11 +1280,9 @@ local export = { -- Therefore, it is not necessary to pollute the global namespace to -- store results for the later usage. -- - -- @tparam string identifier The identifier under which the result is - -- saved. + ---@param identifier string # The identifier under which the result is saved. -- - -- @tparam table result A result to be stored and that was created by - -- the key-value parser. + ---@param result table # A result to be stored and that was created by the key-value parser. save = function(identifier, result) result_store[identifier] = result end, @@ -1128,8 +1290,9 @@ local export = { --- The function `get(identifier): table` retrieves a saved result -- from the result store. -- - -- @tparam string identifier The identifier under which the result was - -- saved. + ---@param identifier string # The identifier under which the result was saved. + --- + ---@return table get = function(identifier) -- if result_store[identifier] == nil then -- throw_error('No stored result was found for the identifier \'' .. identifier .. '\'') @@ -1138,6 +1301,8 @@ local export = { end, is = is, + + utils = utils, } return export |