-- version = 1.0, 2026-05-19 local hasglyph = node.has_glyph local traverse_id = node.traverse_id local math_code = node.id("math") local keyval = require('luakeyval') local scan_choice = keyval.choices local scan_bool = keyval.bool local process_keys = keyval.process local scan_string = token.scan_string local scan_int = token.scan_int local scan_keyword = token.scan_keyword local enabled = true local bidi = require('unibidi-lua') local directiondata = bidi.directions local mirrordata = bidi.mirrors local brackettypedata = bidi.brackettypes local setbaselevel = bidi.setbaselevel local process = bidi.node.reorder local bidiset = bidi.set bidiset("remove","controls") local setdata = function(k,v) local tbl, scanner if k == "setdir" then tbl = directiondata scanner = scan_string elseif k == "setmirror" then tbl = mirrordata scanner = scan_int else tbl = brackettypedata scanner = scan_string end local from = scan_int() local to = from if scan_keyword("-") then to = scan_int() end local data = scanner() for cp=from,to do tbl[cp] = data end end local messages = { error1 = "unibidi-lua: wrong syntax in \\unibidilua", value_forbidden = "unibidi-lua: the %s key does not accept a value", value_rquired = "unibidi-lua: the %s key require a value", } local keys = { enable = { scanner = scan_bool, default = true }, fences = { scanner = scan_bool, default = true }, nsm = { scanner = scan_bool, default = true }, mirror = { scanner = scan_bool, default = true }, ["remove"] = {scanner = scan_choice, args = {"none","controls","full"}}, baselevel = { scanner = scan_string }, mirrorchar = { scanner = scan_string }, startlevel = { scanner = scan_string }, setdir = { scanner = function() return true end, func = setdata }, setmirror = { scanner = function() return true end, func = setdata }, setbracket = { scanner = function() return true end, func = setdata }, } local function interface() local saved_endlinechar = tex.endlinechar tex.endlinechar = 32 local vals = process_keys(keys,messages) tex.endlinechar = saved_endlinechar if vals.enable ~= nil then enabled = vals.enable end if vals.fences ~= nil then bidiset("fences",vals.fences) end if vals.remove ~= nil then bidiset("remove",vals.remove) end if vals.nsm ~= nil then bidiset("nsm",vals.nsm) end if vals.mirror ~= nil then bidiset("mirror", vals.mirror) end if vals.baselevel then local func, err = load("return " .. vals.baselevel) if func then bidiset("baselevel",func()) else tex.error("unibidi-lua: error in baselevel", {err}) end end if vals.mirrorchar then local func, err = load("return " .. vals.mirrorchar) if func then bidiset("mirrorchar", func()) else tex.error("unibidi-lua: error in mirrorchar", {err}) end end if vals.startlevel then local func, err = load("return " .. vals.startlevel) if func then bidiset("startlevel", func()) else tex.error("unibidi-lua: error in startlevel", {err}) end end end do if token.is_defined('unibidilua') then texio.write_nl('log', "unibidi-lua: redefining \\unibidilua") end local function_table = lua.get_functions_table() local luafnalloc = luatexbase and luatexbase.new_luafunction and luatexbase.new_luafunction('unibidilua') or #function_table + 1 token.set_lua('unibidilua', luafnalloc) function_table[luafnalloc] = interface end -- Just in case we check for balanced math nodes, otherwise the current implementation, -- which ignores nodes inside math, will have nonsense output. -- I'm not sure how a list can have unbalanced math nodes, -- but breqn manages to do that. local function balance_math(head) local stack = 0 for n, sb in traverse_id(math_code,head) do stack = stack + 1-2*sb if stack < 0 then return false end end if stack ~= 0 then return false end return true end local function guarded_process(head,where,direction) if not (hasglyph(head) and balance_math(head) and enabled) then return true end return process(head,direction,where) end return { reorder = guarded_process }