-- inner-parens-to-brackets.lua -- A Pandoc Lua filter that converts nested parentheses to brackets -- (and conversely). -- Copyright 2024 Bastien Dumont (bastien.dumont [at] posteo.net) -- This file is under the MIT License: see LICENSE for more details local previously_signalled = {} local function signal_if_new(id, msg) if not previously_signalled[id] then io.stdout:write('INFO: ' .. msg .. '\n') previously_signalled[id] = true end end function Para(para) local whole_para = pandoc.utils.stringify(para) local parens_trace = {} local i_str = 0 local isolate_parens = { -- In case there are several level of parentheses in the same Str object -- (could theoretically happen due to non-breaking spaces). -- Also simplifies check_and_replace() below. Str = function(str) local text = str.text if string.match(text, '[][()]') then local substrings = {} for a, b, c in string.gmatch(text, '([^][()]*)([][()]?)([^][()]*)') do for _, sub in ipairs({ a, b, c }) do if sub ~= '' then table.insert(substrings, pandoc.Str(sub)) end end end return substrings end end } local check_and_replace = { traverse = 'topdown', Str = function(str) i_str = i_str + 1 local text = str.text if text == '(' then if #parens_trace > 0 then if parens_trace[#parens_trace] == '(' then str.text = '[' signal_if_new(whole_para .. i_str, 'Replacing a left parenthesis with a bracket ' .. 'in the following item: ' .. whole_para) end end table.insert(parens_trace, str.text) elseif text == ')' then if #parens_trace > 0 then if parens_trace[#parens_trace] == '[' then str.text = ']' signal_if_new(whole_para .. i_str, 'Replacing a right parenthesis with a bracket ' .. 'in the following item: ' .. whole_para) end end table.remove(parens_trace) elseif text == '[' then if #parens_trace > 0 then if parens_trace[#parens_trace] == '[' then str.text = '(' signal_if_new(whole_para .. i_str, 'Replacing a left bracket with a parenthesis ' .. 'in the following item: ' .. whole_para) end end table.insert(parens_trace, str.text) elseif text == ']' then if #parens_trace > 0 then if parens_trace[#parens_trace] == '(' then str.text = ')' signal_if_new(whole_para .. i_str, 'Replacing a right bracket with a parenthesis ' .. 'in the following item: ' .. whole_para) end end table.remove(parens_trace) end return str end } local processed_para = para:walk(isolate_parens):walk(check_and_replace) if #parens_trace > 0 then io.stdout:write('WARNING: Unbalanced parentheses or brackets found in ' .. whole_para .. '\n') end return processed_para end