luametalatex/luametalatex-callbacks.lua

70 lines
2.8 KiB
Lua

-- Now overwrite the callback functionality. Our system is based on the ssumption there there are
-- no unknown callback names, just callbacks very unlikely to ever be called. That doesn't lead to
-- good error checking, but we expect this to be overwritten by LaTeX anyway.
--
-- There are four callback types on this level:
-- 1. luametalatex defined callbacks. They are not real engine callbacks, the code using them is
-- responsible for their potential non-existance.
-- 2. Engine callbacks not defined by us. They are simply passed on to the engine. All engine
-- callbacks are set to this by default.
-- 3. Engine callbacks with a provided default. There is a luametalatex implementation, but it
-- can be overwritten by the user. If the user disabled their implementation, so provided
-- default is restored.
-- 4. Engine callbacks with mandatory code. The luametalatex implementation can not be overwitten
-- by the user, but a luametalatex-defined callback is added with the same name.
--
-- A callback has type 1 or type 4 if is_user_callback is true. If it has type 4, is_user_callback
-- has to be set manually and in addition, an implementation if the system callback is registered.
--
-- A callback has type 3, if is_user_callback is false and system_callbacks is defined.
local callback_known = callback.known
local callback_find = callback.find
local callback_register = callback.register
local rawset = rawset
local system_callbacks = {}
local is_user_callback = setmetatable({}, {
__index = function(t, name)
local is_user = not callback_known(name)
t[name] = is_user
return is_user
end,
})
local callbacks = setmetatable({
__freeze = function(name, fixed)
-- Convert from type 2 to type 3 or 4. This function will be deleted before user code runs.
assert(not is_user_callback[name], 'Not a system callback')
assert(not system_callbacks[name], 'Already frozen')
is_user_callback[name] = fixed and true or false
system_callbacks[name] = callback_find(name)
assert(system_callbacks[name], 'Attempt to freeze undefined callback')
end,
}, {
__index = function(cbs, name)
if is_user_callback[name] then
-- Avoid repetitive lookups
rawset(cbs, name, false)
return false
end
return callback_find(name)
end,
__newindex = function(cbs, name, new)
if is_user_callback[name] then
-- Avoid repetitive lookups
rawset(cbs, name, new or false)
return
end
return callback_register(name, new or system_callbacks[name])
end,
})
function callback.register(name, new)
callbacks[name] = new
end
-- The and ... or construction makes sure that even in raw mode, non-engine callbacks are found
function callback.find(name, raw)
return raw and callback_find(name) or callbacks[name]
end
return callbacks