[latex3-commits] [git/LaTeX3-latex3-latex2e] callback_rules: Rule based callback management (325a6695)
Marcel Fabian Krüger
tex at 2krueger.de
Wed May 18 23:53:16 CEST 2022
Repository : https://github.com/latex3/latex2e
On branch : callback_rules
Link : https://github.com/latex3/latex2e/commit/325a669560ad44bcf8e62dbd037203d5ab4ed8f2
>---------------------------------------------------------------
commit 325a669560ad44bcf8e62dbd037203d5ab4ed8f2
Author: Marcel Fabian Krüger <tex at 2krueger.de>
Date: Mon May 16 20:00:21 2022 +0200
Rule based callback management
>---------------------------------------------------------------
325a669560ad44bcf8e62dbd037203d5ab4ed8f2
base/ltluatex.dtx | 181 +++++++++++++++++++--
...tlb-latexrelease-rollback-2021-06-01.luatex.tlg | 2 +-
...tlb-latexrelease-rollback-2021-11-15.luatex.tlg | 2 +-
base/testfiles/tlb-ltluatex-001.luatex.tlg | 2 +-
base/testfiles/tlb-mathcallbacks.luatex.tlg | 6 +-
5 files changed, 174 insertions(+), 19 deletions(-)
diff --git a/base/ltluatex.dtx b/base/ltluatex.dtx
index 18623fd6..20b55fcc 100644
--- a/base/ltluatex.dtx
+++ b/base/ltluatex.dtx
@@ -1288,8 +1288,83 @@ luatexbase.new_luafunction = new_luafunction
% actual function as |func| and the identifying description as |description|.
% Only callbacks with a non-empty list of functions have an entry in this
% list.
-% \begin{macrocode}
-local callbacklist = callbacklist or { }
+%
+% Actually there are two tables: |realcallbacklist| directly contains the entries
+% as described above while |callbacklist| only directly contains the already sorted
+% entries. Other entries can be queried through |callbacklist| too which triggers a
+% resort.
+%
+% Additionally |callbackrules| describes the ordering constraints: It contains two
+% element tables with the descriptions of the constrained callback implementations.
+% It can additionally contain a |type| entry indicating the kind of rule. A missing
+% value indicates a normal ordering contraint.
+% \begin{macrocode}
+local realcallbacklist = {}
+local callbackrules = {}
+local callbacklist = setmetatable({}, {
+ __index = function(t, name)
+ local list = realcallbacklist[name]
+ local rules = callbackrules[name]
+ if list and rules then
+ local meta = {}
+ for i, entry in ipairs(list) do
+ local t = {value = entry, count = 0, pos = i}
+ meta[entry.description], list[i] = t, t
+ end
+ local count = #list
+ local pos = count
+ for i, rule in ipairs(rules) do
+ local rule = rules[i]
+ local pre, post = meta[rule[1]], meta[rule[2]]
+ if pre and post then
+ if rule.type then
+ if not rule.hidden then
+ assert(rule.type == 'incompatible-warning' and luatexbase_warning
+ or rule.type == 'incompatible-error' and luatexbase_error)(
+ "Incompatible functions \"" .. rule[1] .. "\" and \"" .. rule[2]
+ .. "\" specified for callback \"" .. name .. "\".")
+ rule.hidden = true
+ end
+ else
+ local post_count = post.count
+ post.count = post_count+1
+ if post_count == 0 then
+ local post_pos = post.pos
+ if post_pos ~= pos then
+ list[post_pos] = list[pos]
+ end
+ list[pos] = nil
+ pos = pos - 1
+ end
+ pre[#pre+1] = post
+ end
+ end
+ end
+ for i=1, count do -- The actual sort begins
+ local current = list[i]
+ if current then
+ for j, cur in ipairs(current) do
+ local count = cur.count
+ if count == 1 then
+ pos = pos + 1
+ list[pos] = cur
+ else
+ cur.count = count - 1
+ end
+ end
+ list[i] = current.value
+ else
+ -- Cycle occured. TODO: Show cycle for debugging
+ -- list[i] = ...
+ error'Cycle occured'
+ end
+ end
+ end
+ realcallbacklist[name] = list
+ t[name] = list
+ return list
+ end
+})
% \end{macrocode}
%
% Numerical codes for callback types, and name-to-value association (the
@@ -1733,10 +1808,10 @@ local function add_to_callback(name, func, description)
% Then test if this callback is already in use. If not, initialise its list
% and register the proper handler.
% \begin{macrocode}
- local l = callbacklist[name]
+ local l = realcallbacklist[name]
if l == nil then
l = { }
- callbacklist[name] = l
+ realcallbacklist[name] = l
% \end{macrocode}
% If it is not a user defined callback use the primitive callback register.
% \begin{macrocode}
@@ -1752,7 +1827,6 @@ local function add_to_callback(name, func, description)
func = func,
description = description,
}
- local priority = #l + 1
if callbacktypes[name] == exclusive then
if #l == 1 then
luatexbase_error(
@@ -1760,19 +1834,68 @@ local function add_to_callback(name, func, description)
name .. "'")
end
end
- table.insert(l, priority, f)
+ table.insert(l, f)
+ callbacklist[name] = nil
% \end{macrocode}
% Keep user informed.
% \begin{macrocode}
luatexbase_log(
- "Inserting `" .. description .. "' at position "
- .. priority .. " in `" .. name .. "'."
+ "Inserting `" .. description .. "' in `" .. name .. "'."
)
end
luatexbase.add_to_callback = add_to_callback
% \end{macrocode}
% \end{macro}
%
+% \begin{macro}{declare_callback_rule}
+% Add an ordering constraint between two callback implementations
+% \begin{macrocode}
+local function declare_callback_rule(name, desc1, desc2, relation)
+ if not callbacktypes[name] or
+ not desc1 or not desc2 or
+ desc1 == "" or desc2 == "" then
+ luatexbase_error(
+ "Unable to create ordering constraint. "
+ .. "Correct usage:\n"
+ .. "declare_callback_rule(<callback>, <description_a>, <description_b>)"
+ )
+ end
+ if relation == 'before' or not relation then
+ relation = nil
+ elseif relation == 'after' then
+ desc2, desc1 = desc1, desc2
+ relation = nil
+ elseif relation == 'incompatible-warning' or relation == 'incompatible-error' then
+ elseif relation == 'unrelated' then
+ else
+ luatexbase_error(
+ "Unknown relation type in declare_callback_rule"
+ )
+ end
+ callbacklist[name] = nil
+ local rules = callbackrules[name]
+ if rules then
+ for i, rule in ipairs(rules) do
+ if rule[1] == desc1 and rule[2] == desc2 or rule[1] == desc2 and rule[2] == desc1 then
+ if relation == 'unrelated' then
+ table.remove(rules, i)
+ else
+ rule[1], rule[2], rule.type = desc1, desc2, relation
+ end
+ return
+ end
+ end
+ if relation ~= 'unrelated' then
+ rules[#rules + 1] = {desc1, desc2, type = relation}
+ end
+ elseif relation ~= 'unrelated' then
+ callbackrules[name] = {{desc1, desc2, type = relation}}
+ end
+end
+luatexbase.declare_callback_rule = declare_callback_rule
+% \end{macrocode}
+% \end{macro}
+%
% \begin{macro}{remove_from_callback}
% \changes{v1.0a}{2015/09/24}{Function added}
% \changes{v1.0k}{2015/12/02}{adjust initialization of cb local (PHG)}
@@ -1794,7 +1917,7 @@ local function remove_from_callback(name, description)
.. "remove_from_callback(<callback>, <description>)"
)
end
- local l = callbacklist[name]
+ local l = realcallbacklist[name]
if not l then
luatexbase_error(
"No callback list for `" .. name .. "'\n")
@@ -1822,6 +1945,7 @@ local function remove_from_callback(name, description)
"Removing `" .. description .. "' from `" .. name .. "'."
)
if #l == 0 then
+ realcallbacklist[name] = nil
callbacklist[name] = nil
if user_callbacks_defaults[name] == nil then
callback_register(name, nil)
@@ -1841,12 +1965,12 @@ luatexbase.remove_from_callback = remove_from_callback
local function in_callback(name, description)
if not name
or name == ""
- or not callbacklist[name]
+ or not realcallbacklist[name]
or not callbacktypes[name]
or not description then
return false
end
- for _, i in pairs(callbacklist[name]) do
+ for _, i in pairs(realcallbacklist[name]) do
if i.description == description then
return true
end
@@ -1863,7 +1987,7 @@ luatexbase.in_callback = in_callback
% this functionality.
% \begin{macrocode}
local function disable_callback(name)
- if(callbacklist[name] == nil) then
+ if(realcallbacklist[name] == nil) then
callback_register(name, false)
else
luatexbase_error("Callback list for " .. name .. " not empty")
@@ -1877,12 +2001,13 @@ luatexbase.disable_callback = disable_callback
% \changes{v1.0a}{2015/09/24}{Function added}
% \changes{v1.0h}{2015/11/27}{Match test in in-callback latex/4445}
% List the descriptions of functions registered for the given callback.
+% This will sort the list if necessary.
% \begin{macrocode}
local function callback_descriptions (name)
local d = {}
if not name
or name == ""
- or not callbacklist[name]
+ or not realcallbacklist[name]
or not callbacktypes[name]
then
return d
@@ -1936,6 +2061,36 @@ callback_register("mlist_to_hlist", function(head, display_type, need_penalties)
end)
% \end{macrocode}
% \end{macro}
+%
+% And finally some patching for the luatexbase priority argument:
+% \begin{macrocode}
+local func = luatexbase.new_luafunction'ConstrainCallbacks at PatchLuatexbase'
+token.set_lua('ConstrainCallbacks at PatchLuatexbase', func, 'protected')
+lua.get_functions_table()[func] = function()
+ luatexbase.base_add_to_callback = add_to_callback
+ function luatexbase.add_to_callback(name,fun,description,priority)
+ if not priority then
+ return add_to_callback(name, fun, description)
+ end
+ local descriptions = luatexbase.callback_descriptions(name)
+% if not priority then
+% priority = #descriptions + 1
+% end
+ if luatexbase.callbacktypes[name] == 3 then
+ if priority == 1 and #descriptions==1 then
+ luatexbase.module_warning("luatexbase",
+ "resetting exclusive callback: " .. name)
+ luatexbase.reset_callback(name)
+ end
+ else
+ for i, desc in ipairs(descriptions) do
+ declare_callback_rule(name, desc, description, i < priority and 'before' or 'after')
+ end
+ end
+ return add_to_callback(name,fun,description)
+ end
+end
+% \end{macrocode}
% \endgroup
%
% \begin{macrocode}
diff --git a/base/testfiles/tlb-latexrelease-rollback-2021-06-01.luatex.tlg b/base/testfiles/tlb-latexrelease-rollback-2021-06-01.luatex.tlg
index c0ef4498..4cbe7bf5 100644
--- a/base/testfiles/tlb-latexrelease-rollback-2021-06-01.luatex.tlg
+++ b/base/testfiles/tlb-latexrelease-rollback-2021-06-01.luatex.tlg
@@ -717,7 +717,7 @@ Already applied: [....-..-..] Start of XeTeX class allocator on input line ....
Already applied: [....-..-..] Start of XeTeX class allocator on input line ....
Removing `tracingstacklevels' from `input_level_string'.
Applying: [....-..-..] Lua trace_stack_levels function on input line ....
-Inserting `tracingstacklevels' at position 1 in `input_level_string'.
+Inserting `tracingstacklevels' in `input_level_string'.
Already applied: [....-..-..] Lua trace_stack_levels function on input line ....
Applying: [....-..-..] XeTeX character classes on input line ....
Already applied: [....-..-..] XeTeX character classes on input line ....
diff --git a/base/testfiles/tlb-latexrelease-rollback-2021-11-15.luatex.tlg b/base/testfiles/tlb-latexrelease-rollback-2021-11-15.luatex.tlg
index 9f30e524..ced0e099 100644
--- a/base/testfiles/tlb-latexrelease-rollback-2021-11-15.luatex.tlg
+++ b/base/testfiles/tlb-latexrelease-rollback-2021-11-15.luatex.tlg
@@ -717,7 +717,7 @@ Already applied: [....-..-..] Start of XeTeX class allocator on input line ....
Already applied: [....-..-..] Start of XeTeX class allocator on input line ....
Removing `tracingstacklevels' from `input_level_string'.
Applying: [....-..-..] Lua trace_stack_levels function on input line ....
-Inserting `tracingstacklevels' at position 1 in `input_level_string'.
+Inserting `tracingstacklevels' in `input_level_string'.
Already applied: [....-..-..] Lua trace_stack_levels function on input line ....
Applying: [....-..-..] XeTeX character classes on input line ....
Already applied: [....-..-..] XeTeX character classes on input line ....
diff --git a/base/testfiles/tlb-ltluatex-001.luatex.tlg b/base/testfiles/tlb-ltluatex-001.luatex.tlg
index 53c7483d..9e4f3289 100644
--- a/base/testfiles/tlb-ltluatex-001.luatex.tlg
+++ b/base/testfiles/tlb-ltluatex-001.luatex.tlg
@@ -7,7 +7,7 @@ stack traceback:
^^I[C]: in function 'error'
^^I./ltluatex.lua:110: in upvalue 'module_error'
^^I./ltluatex.lua:117: in upvalue 'luatexbase_error'
-^^I./ltluatex.lua:428: in field 'create_callback'
+^^I./ltluatex.lua:493: in field 'create_callback'
^^I[\directlua]:1: in main chunk.
l. ...}
The lua interpreter ran into a problem, so the
diff --git a/base/testfiles/tlb-mathcallbacks.luatex.tlg b/base/testfiles/tlb-mathcallbacks.luatex.tlg
index 1c373bbf..8690520c 100644
--- a/base/testfiles/tlb-mathcallbacks.luatex.tlg
+++ b/base/testfiles/tlb-mathcallbacks.luatex.tlg
@@ -1,16 +1,16 @@
This is a generated file for the LaTeX2e validation system.
Don't change this file in any respect.
-Inserting `test' at position 1 in `pre_mlist_to_hlist_filter'.
+Inserting `test' in `pre_mlist_to_hlist_filter'.
LaTeX Font Info: External font `cmex10' loaded for size
(Font) <7> on input line ....
LaTeX Font Info: External font `cmex10' loaded for size
(Font) <5> on input line ....
pre_mlist_to_hlist_filter called
-Inserting `test' at position 1 in `mlist_to_hlist'.
+Inserting `test' in `mlist_to_hlist'.
pre_mlist_to_hlist_filter called
mlist_to_hlist called
Removing `test' from `mlist_to_hlist'.
pre_mlist_to_hlist_filter called
-Inserting `test' at position 1 in `mlist_to_hlist'.
+Inserting `test' in `mlist_to_hlist'.
pre_mlist_to_hlist_filter called
new mlist_to_hlist called
More information about the latex3-commits
mailing list.