summaryrefslogtreecommitdiff
path: root/macros/luatex/generic/luakeys/luakeys.lua
diff options
context:
space:
mode:
Diffstat (limited to 'macros/luatex/generic/luakeys/luakeys.lua')
-rw-r--r--macros/luatex/generic/luakeys/luakeys.lua483
1 files changed, 315 insertions, 168 deletions
diff --git a/macros/luatex/generic/luakeys/luakeys.lua b/macros/luatex/generic/luakeys/luakeys.lua
index c5f51fcfad..c5ccf6b01d 100644
--- a/macros/luatex/generic/luakeys/luakeys.lua
+++ b/macros/luatex/generic/luakeys/luakeys.lua
@@ -50,8 +50,89 @@ if not tex then
end
end
---- A table to store parsed key-value results.
-local result_store = {}
+--- Option handling
+-- @section
+
+--- This table stores all allowed option keys.
+local option_keys = {
+ 'convert_dimensions',
+ 'unpack_single_array_values',
+ 'standalone_as_true',
+ 'converter',
+ 'case_insensitive_keys'
+}
+
+--- The default options.
+local default_options = {
+ convert_dimensions = true,
+ unpack_single_array_values = true,
+ standalone_as_true = false,
+}
+
+local function throw_error(message)
+ if type(tex.error) == 'function' then
+ tex.error(message)
+ else
+ error(message)
+ end
+end
+
+--- Convert a key so that it can be written as a table field without
+-- quotes and square brackets (for example `one 2` becomes `one_2`).
+-- The key can then reference values from a table using dot notation.
+-- (`table["one 2"]` becomes `table.one_2`).
+--
+-- @tparam string key The key to be converted.
+--
+-- @treturn string The converted key.
+local function luafy_key(key)
+ return key:gsub('[^%w]+', '_')
+end
+
+--- Convert all keys in a table to strings containig only alphanumeric
+-- characters and underscores.
+--
+-- @param raw_options Some raw options.
+--
+-- @treturn table Returns always a table. If the input value is not a
+-- an empty table is returned.
+local function luafy_options(raw_options)
+ if type(raw_options) ~= 'table' then
+ raw_options = {}
+ end
+ local options = {}
+ for key, value in pairs(raw_options) do
+ options[luafy_key(key)] = value
+ end
+ return options
+end
+
+--- All option keys can be written with underscores or with spaces as
+-- separators.
+-- For the LaTeX version of the macro
+-- `\luakeysdebug[options]{kv-string}`.
+--
+-- @tparam table options_raw Options in a raw format. The table may be
+-- empty or some keys are not set.
+--
+-- @treturn table
+local function normalize_parse_options (options_raw)
+ options_raw = luafy_options(options_raw)
+ local options = {}
+
+ for _, option_name in ipairs(option_keys) do
+ if options_raw[option_name] ~= nil then
+ options[option_name] = options_raw[option_name]
+ else
+ options[option_name] = default_options[option_name]
+ end
+ end
+
+ return options
+end
+
+--- Parser / Lpeg related
+-- @section
--- Generate the PEG parser using Lpeg.
--
@@ -244,27 +325,55 @@ local function get_table_size(value)
end
--- Unpack a single valued array table like `{ 'one' }` into `one` or
--- `{ 1 }` into `into`.
+-- `{ 1 }` into `1`.
--
-- @treturn If the value is a array like table with one non table typed
-- value in it, the unpacked value, else the unchanged input.
-local function unpack_single_valued_array_table(value)
+local function unpack_single_valued_array_table(value, options)
if
type(value) == 'table' and
get_array_size(value) == 1 and
get_table_size(value) == 1 and
type(value[1]) ~= 'table'
then
- return value[1]
+ if type(value[1]) == 'string' and options.standalone_as_true then
+ return value
+ else
+ return value[1]
+ end
end
return value
end
---- This normalization tasks are performed on the raw input table coming
--- directly from the PEG parser:
+local function visit_parse_tree(parse_tree, callback_func)
+ if type(parse_tree) ~= 'table' then
+ throw_error('Parse tree has to be a table')
+ end
+ local function visit_parse_tree_recursive(root_table, current_table, result, depth, callback_func)
+ for key, value in pairs(current_table) do
+ if type(value) == 'table' then
+ value = visit_parse_tree_recursive(root_table, value, {}, depth + 1, callback_func)
+ end
+
+ key, value = callback_func(key, value, depth, current_table, root_table)
+
+ if key ~= nil and value ~= nil then
+ result[key] = value
+ end
+ end
+ if next(result) ~= nil then
+ return result
+ end
+ end
+
+ return visit_parse_tree_recursive(parse_tree, parse_tree, {}, 1, callback_func)
+end
+
+--- Normalize the result tables of the LPeg parser. This normalization
+-- tasks are performed on the raw input table coming directly from the
+-- PEG parser:
--
--- 1. Trim all strings: ` text \n` into `text`
--- 2. Unpack all single valued array like tables: `{ 'text' }` into
+-- * Unpack all single valued array like tables: `{ 'text' }` into
-- `text`
--
-- @tparam table raw The raw input table coming directly from the PEG
@@ -278,7 +387,7 @@ local function normalize(raw, options)
local function normalize_recursive(raw, result, options)
for key, value in pairs(raw) do
if options.unpack_single_array_values then
- value = unpack_single_valued_array_table(value)
+ value = unpack_single_valued_array_table(value, options)
end
if type(value) == 'table' then
result[key] = normalize_recursive(value, {}, options)
@@ -288,7 +397,132 @@ local function normalize(raw, options)
end
return result
end
- return normalize_recursive(raw, {}, options)
+ raw = normalize_recursive(raw, {}, options)
+
+ if options.standalone_as_true then
+ raw = visit_parse_tree(raw, function (key, value)
+ if type(key) == 'number' and type(value) == 'string' then
+ return value, true
+ end
+ return key, value
+ end)
+ end
+
+ if options.case_insensitive_keys then
+ raw = visit_parse_tree(raw, function (key, value)
+ if type(key) == 'string' then
+ return key:lower(), value
+ end
+ return key, value
+ end)
+ end
+
+ return raw
+end
+
+--- Parse a LaTeX/TeX style key-value string into a Lua table. With
+-- this function you should be able to parse key-value strings like
+-- this example:
+--
+-- show,
+-- hide,
+-- key with spaces = String without quotes,
+-- string="String with double quotes: ,{}=",
+-- dimension = 1cm,
+-- number = -1.2,
+-- list = {one,two,three},
+-- key value list = {one=one,two=two,three=three},
+-- nested key = {
+-- nested key 2= {
+-- key = value,
+-- },
+-- },
+--
+-- The string above results in this Lua table:
+--
+-- {
+-- 'show',
+-- 'hide',
+-- ['key with spaces'] = 'String without quotes',
+-- string = 'String with double quotes: ,{}=',
+-- dimension = 1864679,
+-- number = -1.2,
+-- list = {'one', 'two', 'three'},
+-- key value list = {
+-- one = 'one',
+-- three = 'three',
+-- two = 'two'
+-- },
+-- ['nested key'] = {
+-- ['nested key 2'] = {
+-- key = 'value'
+-- }
+-- },
+-- }
+--
+-- @tparam string kv_string A string in the TeX/LaTeX style key-value
+-- format as described above.
+--
+-- @tparam table options A table containing
+-- settings: `convert_dimensions`, `unpack_single_array_values`, `standalone_as_true`, `converter`
+--
+-- @treturn table A hopefully properly parsed table you can do
+-- something useful with.
+local function parse (kv_string, options)
+ if kv_string == nil then
+ return {}
+ end
+ options = normalize_parse_options(options)
+
+ local parser = generate_parser(options)
+ local parse_tree = parser:match(kv_string)
+
+ if options.converter ~= nil and type(options.converter) == 'function' then
+ parse_tree = visit_parse_tree(parse_tree, options.converter)
+ end
+ return normalize(parse_tree, options)
+end
+
+--- Convert back to strings
+-- @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 tbl A table to be converted into a key-value string.
+--
+-- @treturn string A key-value string that can be passed to a TeX
+-- macro.
+local function render (tbl)
+ local function render_inner(tbl)
+ local output = {}
+ local function add(text)
+ table.insert(output, text)
+ end
+ for key, value in pairs(tbl) do
+ if (key and type(key) == 'string') then
+ if (type(value) == 'table') then
+ if (next(value)) then
+ add(key .. '={')
+ add(render_inner(value))
+ add('},')
+ else
+ add(key .. '={},')
+ end
+ else
+ add(key .. '=' .. tostring(value) .. ',')
+ end
+ else
+ add(tostring(value) .. ',')
+ end
+ end
+ return table.concat(output)
+ end
+ return render_inner(tbl)
end
--- The function `stringify(tbl, for_tex)` converts a Lua table into a
@@ -371,171 +605,84 @@ local function stringify(input, for_tex)
return start_bracket .. line_break .. stringify_inner(input, 1) .. line_break .. end_bracket
end
---- For the LaTeX version of the macro
--- `\luakeysdebug[options]{kv-string}`.
+--- The function `pretty_print(tbl)` 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 options_raw Options in a raw format. The table may be
--- empty or some keys are not set.
---
--- @treturn table
-local function normalize_parse_options (options_raw)
- if options_raw == nil then
- options_raw = {}
- end
- local options = {}
-
- if options_raw['unpack single array values'] ~= nil then
- options['unpack_single_array_values'] = options_raw['unpack single array values']
- end
+-- @tparam table tbl A table to be printed to standard output for
+-- debugging purposes.
+local function pretty_print(tbl)
+ print(stringify(tbl, false))
+end
- if options_raw['convert dimensions'] ~= nil then
- options['convert_dimensions'] = options_raw['convert dimensions']
- end
+--- Store results
+-- @section
- if options.convert_dimensions == nil then
- options.convert_dimensions = true
- end
+--- A table to store parsed key-value results.
+local result_store = {}
- if options.unpack_single_array_values == nil then
- options.unpack_single_array_values = true
- end
+--- The function `save(identifier, result): void` saves a result (a
+-- table from a previous run of `parse`) under an identifier.
+-- 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.
+--
+-- @tparam table result A result to be stored and that was created by
+-- the key-value parser.
+local function save(identifier, result)
+ result_store[identifier] = result
+end
- return options
+--- The function `get(identifier): table` retrieves a saved result
+-- from the result store.
+--
+-- @tparam string identifier The identifier under which the result was
+-- saved.
+local function get(identifier)
+ -- if result_store[identifier] == nil then
+ -- throw_error('No stored result was found for the identifier \'' .. identifier .. '\'')
+ -- end
+ return result_store[identifier]
end
-return {
+--- Exports
+-- @section
+
+local export = {
+ --- @see default_options
+ default_options = default_options,
+
+ --- @see stringify
stringify = stringify,
- --- Parse a LaTeX/TeX style key-value string into a Lua table. With
- -- this function you should be able to parse key-value strings like
- -- this example:
- --
- -- show,
- -- hide,
- -- key with spaces = String without quotes,
- -- string="String with double quotes: ,{}=",
- -- dimension = 1cm,
- -- number = -1.2,
- -- list = {one,two,three},
- -- key value list = {one=one,two=two,three=three},
- -- nested key = {
- -- nested key 2= {
- -- key = value,
- -- },
- -- },
- --
- -- The string above results in this Lua table:
- --
- -- {
- -- 'show',
- -- 'hide',
- -- ['key with spaces'] = 'String without quotes',
- -- string = 'String with double quotes: ,{}=',
- -- dimension = 1864679,
- -- number = -1.2,
- -- list = {'one', 'two', 'three'},
- -- key value list = {
- -- one = 'one',
- -- three = 'three',
- -- two = 'two'
- -- },
- -- ['nested key'] = {
- -- ['nested key 2'] = {
- -- key = 'value'
- -- }
- -- },
- -- }
- --
- -- @tparam string kv_string A string in the TeX/LaTeX style key-value
- -- format as described above.
- --
- -- @tparam table options A table containing
- -- settings: `convert_dimensions` `unpack_single_array_values`
- --
- -- @treturn table A hopefully properly parsed table you can do
- -- something useful with.
- parse = function (kv_string, options)
- if kv_string == nil then
- return {}
- end
- options = normalize_parse_options(options)
-
- local parser = generate_parser(options)
- return normalize(parser:match(kv_string), options)
- end,
-
- --- 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 tbl A table to be converted into a key-value string.
- --
- -- @treturn string A key-value string that can be passed to a TeX
- -- macro.
- render = function (tbl)
- local function render_inner(tbl)
- local output = {}
- local function add(text)
- table.insert(output, text)
- end
- for key, value in pairs(tbl) do
- if (key and type(key) == 'string') then
- if (type(value) == 'table') then
- if (next(value)) then
- add(key .. '={')
- add(render_inner(value))
- add('},')
- else
- add(key .. '={},')
- end
- else
- add(key .. '=' .. tostring(value) .. ',')
- end
- else
- add(tostring(value) .. ',')
- end
- end
- return table.concat(output)
- end
- return render_inner(tbl)
- end,
-
- --- The function `print(tbl)` 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 tbl A table to be printed to standard output for
- -- debugging purposes.
- print = function(tbl)
- print(stringify(tbl, false))
- end,
-
- --- The function `save(identifier, result): void` saves a result (a
- -- table from a previous run of `parse`) under an identifier.
- -- 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.
- --
- -- @tparam table result A result to be stored and that was created by
- -- the key-value parser.
- save = function(identifier, result)
- result_store[identifier] = result
- end,
-
- --- The function `get(identifier): table` retrieves a saved result
- -- from the result store.
- --
- -- @tparam string identifier The identifier under which the result was
- -- saved.
- get = function(identifier)
- return result_store[identifier]
- end,
+ --- @see parse
+ parse = parse,
+
+ --- @see render
+ render = render,
+
+ --- @see pretty_print
+ print = pretty_print,
+ --- @see save
+ save = save,
+
+ --- @see get
+ get = get,
}
+
+-- http://olivinelabs.com/busted/#private
+if _TEST then
+ export.luafy_key = luafy_key
+ export.luafy_options = luafy_options
+ export.normalize = normalize
+ export.normalize_parse_options = normalize_parse_options
+ export.unpack_single_valued_array_table = unpack_single_valued_array_table
+ export.visit_parse_tree = visit_parse_tree
+end
+
+return export