diff options
author | Karl Berry <karl@freefriends.org> | 2024-01-12 21:45:17 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2024-01-12 21:45:17 +0000 |
commit | 92923b59b9d97aed633e13ae96acde1946ee7af5 (patch) | |
tree | 2555be7ab687a7b516f6d2dd1036160ee70553fe /Master/texmf-dist/scripts | |
parent | 8aeda3e1019bc7b0493c228b2c9487b0f3636964 (diff) |
lua-placeholders (12jan24)
git-svn-id: svn://tug.org/texlive/trunk@69404 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/scripts')
5 files changed, 707 insertions, 0 deletions
diff --git a/Master/texmf-dist/scripts/lua-placeholders/lua-placeholders-common.lua b/Master/texmf-dist/scripts/lua-placeholders/lua-placeholders-common.lua new file mode 100644 index 00000000000..e68a5484e84 --- /dev/null +++ b/Master/texmf-dist/scripts/lua-placeholders/lua-placeholders-common.lua @@ -0,0 +1,35 @@ +-- lua-placeholders-common.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 + +function table.copy(t) + local u = { } + for k, v in pairs(t) do + u[k] = v + end + return setmetatable(u, getmetatable(t)) +end + +lua_placeholders_toks = { + new_bool = token.create('provideboolean'), + set_bool = token.create('setboolean'), + list_conj = token.create('paramlistconjunction'), + placeholder_format = token.create('paramplaceholder'), + unknown_format = token.create('paramnotfound') +} diff --git a/Master/texmf-dist/scripts/lua-placeholders/lua-placeholders-namespace.lua b/Master/texmf-dist/scripts/lua-placeholders/lua-placeholders-namespace.lua new file mode 100644 index 00000000000..fb84447a251 --- /dev/null +++ b/Master/texmf-dist/scripts/lua-placeholders/lua-placeholders-namespace.lua @@ -0,0 +1,94 @@ +-- lua-placeholders-namespace.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 + +require('lua-placeholders-types') + +local namespace = { + strict = false, + recipe_file = nil, + recipe_loaded = false, + payload_file = nil, + payload_loaded = false +} + +function namespace.parse_filename(path) + local abs_path = kpse.find_file(path) + local _, _, name = abs_path:find('/?%w*/*(%w+)%.%w+') + return name, abs_path +end + +function namespace:new(_o) + local o = { + recipe_file = _o.recipe_file, + payload_file = _o.payload_file, + strict = _o.strict, + values = {} + } + setmetatable(o, self) + self.__index = self + return o +end + +function namespace:load_recipe(params) + for key, opts in pairs(params) do + local param = base_param.define(key, opts) + if param then + self.values[key] = param + end + end + self.recipe_loaded = true +end + +function namespace:load_payload(values) + if self.recipe_loaded then + if values then + for key, value in pairs(values) do + if self.values[key] then + local param = self.values[key] + param:load(key, value) + else + texio.write_nl('Warning: passed an unknown key ' .. key) + end + texio.write_nl('Key' .. key) + end + else + texio.write_nl('Warning: Payload file was empty') + end + self.payload_loaded = true + end +end + +function namespace:param(key) + if not self.recipe_loaded then + tex.error('Error: Recipe was not loaded yet...') + return nil + end + if not self.payload_loaded then + if self.strict then + tex.error('Error: Payload was not loaded yet...') + return nil + else + texio.write_nl('Warning: Payload was not loaded yet...') + end + end + return self.values[key] +end + +return namespace diff --git a/Master/texmf-dist/scripts/lua-placeholders/lua-placeholders-parser.lua b/Master/texmf-dist/scripts/lua-placeholders/lua-placeholders-parser.lua new file mode 100644 index 00000000000..1fa43227a48 --- /dev/null +++ b/Master/texmf-dist/scripts/lua-placeholders/lua-placeholders-parser.lua @@ -0,0 +1,88 @@ +-- lua-placeholders-parser.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 + +local LUA_VERSION = string.sub(_VERSION, 5, -1) + +yaml_supported = false + +-- Check if LUA_PATH is set +local current_path = os.getenv('LUA_PATH') +if current_path then + texio.write_nl('Info: LUA path setup up correctly. Great job!') +else + -- Set the LUA_PATH and LUA_CPATH using 'luarocks -lua-version <LuaLaTeX version> path' + texio.write_nl('Warning: No LUA_PATH set. Looking for LuaRocks installation...') + local handle = io.popen('luarocks --lua-version ' .. LUA_VERSION .. ' path') + if handle then + local buffer = handle:read('*a') + if handle:close() then + texio.write_nl('Info: luarocks command executed successfully') + local lua_path, lua_search_count = string.gsub(buffer, ".*LUA_PATH='([^']*)'.*", "%1") + local lua_cpath, clua_search_count = string.gsub(buffer, ".*LUA_CPATH='([^']*)'.*", "%1") + if lua_search_count > 0 then + texio.write_nl('Info: Setting LUA_PATH from LuaRocks', lua_path) + package.path = lua_path + end + if clua_search_count > 0 then + texio.write_nl('Info: Setting LUA_CPATH from LuaRocks', lua_cpath) + package.cpath = lua_cpath + end + else + texio.write_nl('Error: couldn\'t find LuaRocks installation') + texio.write_nl("Info: LUA PATH:\n\t" .. string.gsub(package.path, ';', '\n\t') .. '\n\n') + end + else + tex.error('Error: could not open a shell. Is shell-escape turned on?') + end +end +texio.write_nl('\n') + +-- For falling back to JSON +require('lualibs') + +-- Require YAML configuration files +-- Make sure to have the apt package lua-yaml installed +local status, yaml = pcall(require, 'lyaml') +if status then + yaml_supported = true +else + texio.write_nl('Warning: No YAML support.') + texio.write_nl(yaml) + texio.write_nl('Info: Falling back to JSON.') +end + +return function(filename) + local _, _, ext = string.match(filename, "(.-)([^\\]-([^\\%.]+))$") + local file = io.open(filename, "rb") + if not file then + error('File ' .. filename .. ' doesn\'t exist...') + end + local raw = file:read "*a" + file:close() + if ext == 'json' then + return utilities.json.tolua(raw) + else + if yaml_supported then + return yaml.load(raw) + else + tex.error('Error: no YAML support!') + end + end +end diff --git a/Master/texmf-dist/scripts/lua-placeholders/lua-placeholders-types.lua b/Master/texmf-dist/scripts/lua-placeholders/lua-placeholders-types.lua new file mode 100644 index 00000000000..faa637c8f78 --- /dev/null +++ b/Master/texmf-dist/scripts/lua-placeholders/lua-placeholders-types.lua @@ -0,0 +1,273 @@ +-- lua-placeholders-types.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 + +require('lua-placeholders-common') + +function table.copy(t) + local u = { } + for k, v in pairs(t) do + u[k] = v + end + return setmetatable(u, getmetatable(t)) +end + +base_param = {} +function base_param:new(o) + o = o or {} + setmetatable(o, self) + self.__index = self + return o +end + +function base_param:is_set() + return self and ((self.values or self.fields or self.value) ~= nil) +end + +function base_param:val() + return self.value or self.values or self.default +end + +function base_param:to_upper() + local val = self:val() + if type(val) == 'string' then + return val:upper() + elseif type(self.placeholder) == 'string' then + return '[' .. self.placeholder:upper() .. ']' + end +end + +function base_param:print_val() + local value = self:val() + if value ~= nil then + tex.write(value) + else + tex.sprint(lua_placeholders_toks.placeholder_format, '{', self.placeholder or self.key, '}') + end +end + +bool_param = base_param:new{ + type = 'bool' +} + +function bool_param:new(key, _o) + local o = { + key = key, + default = _o.default + } + setmetatable(o, self) + self.__index = self + tex.sprint(lua_placeholders_toks.new_bool, '{', o.key, '}') + return o +end + +function bool_param:val() + local value + if self.value ~= nil then + value = tostring(self.value) + elseif self.default ~= nil then + value = tostring(self.default) + else + value = 'false' + end + return value +end + +function bool_param:set_bool(key) + tex.sprint(lua_placeholders_toks.set_bool, '{', key, '}{', self:val(), '}') +end + +str_param = base_param:new{ + type = 'string' +} + +function str_param:new(key, _o) + local o = { + key = key, + placeholder = _o.placeholder + } + setmetatable(o, self) + self.__index = self + return o +end + +number_param = base_param:new{ + type = 'number' +} + +function number_param:new(key, _o) + local o = { + key = key, + placeholder = _o.placeholder, + default = _o.default + } + setmetatable(o, self) + self.__index = self + return o +end + +function number_param:val() + if self.value or self.default then + return tex.number(self.value or self.default) + end +end + +function number_param:print_num() + local val = self:val() + if val then + tex.print('\\numprint{' .. val .. '}') + else + tex.sprint(lua_placeholders_toks.placeholder_format, '{', self.placeholder or self.key, '}') + end +end + +list_param = base_param:new{ + type = 'list' +} + +function list_param:new(key, _o) + local o = { + key = key, + item_type = base_param.define('list_item', { type = (_o["item type"] or 'string') }), + default = {} + } + if _o.default then + for _, default_val in ipairs(_o.default) do + local v = table.copy(o.item_type) + v:load('list_item', default_val) + table.insert(o.default, v) + end + end + setmetatable(o, self) + self.__index = self + return o +end + +function list_param:val() + return self.values or self.default or {} +end + +function list_param:print_val() + local list = self:val() + if #list > 0 then + if not self.values then + tex.sprint(lua_placeholders_toks.placeholder_format, '{') + end + tex.sprint(list[1]:val()) + for i = 2, #list do + tex.sprint(lua_placeholders_toks.list_conj, list[i]:val()) + end + if not self.values then + tex.sprint('}') + end + end +end + +object_param = base_param:new{ + type = 'object' +} + +function object_param:new(key, _o) + local o = { + key = key, + fields = {}, + default = _o.default + } + for _key, field in pairs(_o.fields) do + o.fields[_key] = base_param.define(_key, field) + end + setmetatable(o, self) + self.__index = self + return o +end + +table_param = base_param:new{ + type = 'table' +} + +function table_param:new(key, _o) + local o = { + key = key, + columns = {} + } + for col_key, col in pairs(_o.columns) do + o.columns[col_key] = base_param.define(col_key, col) + end + setmetatable(o, self) + self.__index = self + return o +end + +function base_param.define(key, o) + if o.type then + if o.type == 'bool' then + return bool_param:new(key, o) + elseif o.type == 'string' then + return str_param:new(key, o) + elseif o.type == 'number' then + return number_param:new(key, o) + elseif o.type == 'list' then + return list_param:new(key, o) + elseif o.type == 'object' then + return object_param:new(key, o) + elseif o.type == 'table' then + return table_param:new(key, o) + else + texio.write_nl('Warning: no such parameter type ' .. o.type) + end + else + error('ERROR: parameter must have a "type" field') + end +end + +function base_param:load(key, value) + if self.type == 'list' then + self.values = {} + for _, val in ipairs(value) do + local param = table.copy(self.item_type) + param:load('list-item', val) + table.insert(self.values, param) + end + elseif self.type == 'table' then + self.values = {} + for _, row_vals in ipairs(value) do + local row = {} + for col_key, col in pairs(self.columns) do + local cell = table.copy(col) + cell:load(col.key, row_vals[col_key]) + row[col_key] = cell + end + table.insert(self.values, row) + end + elseif self.type == 'object' then + for field_key, field in pairs(self.fields) do + local field_val = value[field_key] + if field_val then + field:load(field_key, field_val) + else + texio.write_nl('Warning: Passed unknown field to object', field_key) + end + end + else + self.value = value + end + if self.type == 'bool' then + self:set_bool(key) + end +end diff --git a/Master/texmf-dist/scripts/lua-placeholders/lua-placeholders.lua b/Master/texmf-dist/scripts/lua-placeholders/lua-placeholders.lua new file mode 100644 index 00000000000..255b4d1f26a --- /dev/null +++ b/Master/texmf-dist/scripts/lua-placeholders/lua-placeholders.lua @@ -0,0 +1,217 @@ +-- 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 = "0.1.0", + date = "2024/01/12", + comment = 'Extended LaTeX Parameter Interface — 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('\\UseOneTimeHook{namespace/' .. 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 + format = format:gsub('\\' .. col_key, '{\\paramplaceholder{' .. (col.placeholder or col_key) .. '}}') + 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 |