@@ -260,23 +260,36 @@ local compileToStr = function (argEnv, mathlist)
260260 end
261261end
262262
263- local function isBigOperator (tree )
263+ local function isOperatorKind (tree , typeOfAtom , typeOfSymbol )
264+ if not tree then
265+ return false -- safeguard
266+ end
264267 if tree .command ~= " mo" then
265268 return false
266269 end
267270 -- Case \mo[atom=big]{ops}
268271 -- E.g. \mo[atom=big]{lim}
269- if tree .options and tree .options .atom == " big " then
272+ if tree .options and tree .options .atom == typeOfAtom then
270273 return true
271274 end
272- -- Case \mo{ops} where ops is registered as big operator (unicode-symbols)
275+ -- Case \mo{ops} where ops is registered with the resquested type
273276 -- E.g. \mo{∑) or \sum
274- if tree [1 ] and symbolDefaults [tree [1 ]] and symbolDefaults [tree [1 ]].atom == atomType . bigOperator then
277+ if tree [1 ] and symbolDefaults [tree [1 ]] and symbolDefaults [tree [1 ]].atom == typeOfSymbol then
275278 return true
276279 end
277280 return false
278281end
279282
283+ local function isBigOperator (tree )
284+ return isOperatorKind (tree , " big" , atomType .bigOperator )
285+ end
286+ local function isCloseOperator (tree )
287+ return isOperatorKind (tree , " close" , atomType .closeSymbol )
288+ end
289+ local function isOpeningOperator (tree )
290+ return isOperatorKind (tree , " open" , atomType .openingSymbol )
291+ end
292+
280293local function compileToMathML_aux (_ , arg_env , tree )
281294 if type (tree ) == " string" then
282295 return tree
@@ -323,12 +336,56 @@ local function compileToMathML_aux (_, arg_env, tree)
323336 -- Turn mathlist into `mrow` except if it has exactly one `mtr` or `mtd`
324337 -- child.
325338 -- Note that `def`s have already been compiled away at this point.
326- if # tree == 1 and (tree [1 ].command == " mtr" or tree [1 ].command == " mtd" ) then
327- return tree [1 ]
339+ if # tree == 1 then
340+ if tree [1 ].command == " mtr" or tree [1 ].command == " mtd" then
341+ return tree [1 ]
342+ else
343+ tree .command = " mrow"
344+ end
328345 else
346+ -- Re-wrap content from opening to closing operator in an implicit mrow,
347+ -- so stretchy operators apply to the correct span of content.
348+ local children = {}
349+ local stack = {}
350+ for _ , child in ipairs (tree ) do
351+ if isOpeningOperator (child ) then
352+ table.insert (stack , children )
353+ local mrow = {
354+ command = " mrow" ,
355+ options = {},
356+ child ,
357+ }
358+ table.insert (children , mrow )
359+ children = mrow
360+ elseif isCloseOperator (child ) then
361+ table.insert (children , child )
362+ if # stack > 0 then
363+ children = table.remove (stack )
364+ end
365+ elseif
366+ (child .command == " msubsup" or child .command == " msub" or child .command == " msup" )
367+ and isCloseOperator (child [1 ]) -- child[1] is the base
368+ then
369+ if # stack > 0 then
370+ -- Special case for closing operator with sub/superscript:
371+ -- (....)^i must be interpreted as {(....)}^i, not as (...{)}^i
372+ -- Push the closing operator into the mrow
373+ table.insert (children , child [1 ])
374+ -- Move the mrow into the msubsup, replacing the closing operator
375+ child [1 ] = children
376+ -- And insert the msubsup into the parent
377+ children = table.remove (stack )
378+ children [# children ] = child
379+ else
380+ table.insert (children , child )
381+ end
382+ else
383+ table.insert (children , child )
384+ end
385+ end
386+ tree = # stack > 0 and stack [1 ] or children
329387 tree .command = " mrow"
330388 end
331- tree .command = " mrow"
332389 elseif tree .id == " atom" then
333390 local codepoints = {}
334391 for _ , cp in luautf8 .codes (tree [1 ]) do
0 commit comments