-- lua-placeholders.lua -- Copyright 2024 E. Nijenhuis -- -- This work may be distributed and/or modified under the -- conditions of the LaTeX Project Public License, either version 1.3c -- of this license or (at your option) any later version. -- The latest version of this license is in -- http://www.latex-project.org/lppl.txt -- and version 1.3c or later is part of all distributions of LaTeX -- version 2005/12/01 or later. -- -- This work has the LPPL maintenance status ‘maintained’. -- -- The Current Maintainer of this work is E. Nijenhuis. -- -- This work consists of the files lua-placeholders.sty -- lua-placeholders-manual.pdf lua-placeholders.lua -- lua-placeholders-common.lua lua-placeholders-namespace.lua -- lua-placeholders-parser.lua and lua-placeholders-types.lua if not modules then modules = {} end modules.lua_placeholders = { version = "1.0.3", date = "2024/04/02", comment = 'Lua Placeholders — for specifying and inserting document parameters', author = 'Erik Nijenhuis', license = 'free' } local api = { namespaces = {}, parameters = {}, strict = false, toks = { is_set_true = token.create('has@param@true'), is_set_false = token.create('has@param@false'), } } local lua_placeholders = {} local lua_placeholders_mt = { __index = api, __newindex = function() tex.error('Cannot override or set actions for this module...') end } setmetatable(lua_placeholders, lua_placeholders_mt) local lua_placeholders_namespace = require('lua-placeholders-namespace') local load_resource = require('lua-placeholders-parser') local function get_param(key, namespace) namespace = namespace or tex.jobname local _namespace = api.namespaces[namespace] return _namespace and _namespace:param(key) end function api.set_strict() api.strict = true end function api.recipe(path, namespace_name) if namespace_name == '' then namespace_name = nil end local filename, abs_path = lua_placeholders_namespace.parse_filename(path) local raw_recipe = load_resource(abs_path) local name = namespace_name or raw_recipe.namespace or filename local namespace = api.namespaces[name] or lua_placeholders_namespace:new { recipe_file = abs_path, strict = api.strict } if not api.namespaces[name] then api.namespaces[name] = namespace end if raw_recipe.namespace then namespace:load_recipe(raw_recipe.parameters) else namespace:load_recipe(raw_recipe) end tex.print('\\NewHook{namespace/' .. name .. '}') tex.print('\\NewHook{namespace/' .. name .. '/loaded}') tex.print('\\UseOneTimeHook{namespace/' .. name .. '}') texio.write_nl(name) if namespace.payload_file and not namespace.payload_loaded then local raw_payload = load_resource(namespace.payload_file) if raw_payload.namespace then namespace:load_payload(raw_payload.parameters) else namespace:load_payload(raw_payload) end tex.print('\\UseOneTimeHook{namespace/' .. name .. '/loaded}') end end function api.payload(path, namespace_name) if namespace_name == '' then namespace_name = nil end local filename, abs_path = lua_placeholders_namespace.parse_filename(path) local raw_payload = load_resource(abs_path) local name = namespace_name or raw_payload.namespace or filename local namespace = api.namespaces[name] or lua_placeholders_namespace:new { payload_file = abs_path, strict = api.strict } if not api.namespaces[name] then api.namespaces[name] = namespace end if namespace.recipe_loaded then if raw_payload.namespace then namespace:load_payload(raw_payload.parameters) else namespace:load_payload(raw_payload) end tex.print('\\UseOneTimeHook{namespace/' .. name .. '/loaded}') end end function api.param_object(key, namespace) return get_param(key, namespace) end function api.param(key, namespace) local param = get_param(key, namespace) if param then param:print_val() elseif api.strict then tex.error('Error: Parameter not set "' .. key .. '" in namespace "' .. namespace .. '".') else tex.sprint(lua_placeholders_toks.unknown_format, '{', key, '}') end end function api.handle_param_is_set(key, namespace) local param = get_param(key, namespace) if param and param:is_set() then tex.sprint(token.create('has@param@true')) else tex.sprint(token.create('has@param@false')) end end function api.field(object_key, field, namespace) local param = get_param(object_key, namespace) if param then local object = param.fields or param.default or {} local f = object[field] if f then f:print_val() else tex.sprint(lua_placeholders_toks.unknown_format, '{', field, '}') end else tex.error('No such object', object_key) end end function api.with_object(object_key, namespace) local object = get_param(object_key, namespace) for key, param in pairs(object.fields) do local val = param:val() if val then token.set_macro(key, param:val() .. '\\xspace') else token.set_macro(key, '\\paramplaceholder{' .. (param.placeholder or key) .. '}\\xspace') end end end function api.for_item(list_key, namespace, csname) local param = get_param(list_key, namespace) local list = param:val() if #list > 0 then if token.is_defined(csname) then local tok = token.create(csname) for _, item in ipairs(list) do if param.values then tex.sprint(tok, '{', item:val(), '}') else tex.sprint(tok, '{', lua_placeholders_toks.placeholder_format, '{', item:val(), '}}') end end else tex.error('No such command ', csname or 'nil') end end end function api.with_rows(key, namespace, csname) local param = get_param(key, namespace) if token.is_defined(csname) then local row_content = token.get_macro(csname) if param then if param.values or api.strict then if #param.values > 0 then for _, row in ipairs(param.values) do local format = row_content for col_key, cell in pairs(row) do format = format:gsub('\\' .. col_key, cell:val()) end tex.print(format) end end elseif param.columns then texio.write_nl("Warning: no values set for " .. param.key) local format = row_content for col_key, col in pairs(param.columns) do if col.default ~= nil then format = format:gsub('\\' .. col_key, col:val()) else format = format:gsub('\\' .. col_key, '{\\paramplaceholder{' .. (col.placeholder or col_key) .. '}}') end end tex.print(format) else tex.error('No values either columns available') end else tex.error('Error: no such parameter') end else tex.error('Error: no such command: ', csname or 'nil') end end return lua_placeholders