-- 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