Skip to content

Commit 781f62a

Browse files
Omikhleiaalerque
authored andcommitted
fix(math): Improve spacing rules on limit-like operators
Including another small refactor / clarification on our atom types.
1 parent 56edc14 commit 781f62a

6 files changed

Lines changed: 1306 additions & 2541 deletions

File tree

packages/math/atoms.lua

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,23 @@
11
local atomType = {
22
ordinary = 0,
3-
bigOperator = 1,
3+
bigOperator = 1, -- Misnamed used for TeX's "mathop"
44
binaryOperator = 2,
55
relationalOperator = 3,
66
openingSymbol = 4,
77
closeSymbol = 5,
88
punctuationSymbol = 6,
9-
inner = 7,
10-
overSymbol = 8,
11-
underSymbol = 9,
9+
inner = 7, -- Unused for now (used for fractions in The TeXbook)
10+
overSymbol = 8, -- Unused for now (used for overlines etc. in The TeXbook)
11+
underSymbol = 9, -- Unused for now (used for underlines etc. in The TeXbook)
1212
accentSymbol = 10,
13-
radicalSymbol = 11,
14-
vcenter = 12,
13+
botAccentSymbol = 11, -- Unused for now but botaccent is encoded in our dictionary
1514
}
1615

1716
-- Shorthands for atom types, used in the `atom` command option
1817
-- and also in the unicode symbols table / operator dictionary
1918
local atomTypeShort = {
2019
ord = atomType.ordinary,
21-
big = atomType.bigOperator,
20+
op = atomType.bigOperator,
2221
bin = atomType.binaryOperator,
2322
rel = atomType.relationalOperator,
2423
open = atomType.openingSymbol,
@@ -28,8 +27,7 @@ local atomTypeShort = {
2827
over = atomType.overSymbol,
2928
under = atomType.underSymbol,
3029
accent = atomType.accentSymbol,
31-
radical = atomType.radicalSymbol,
32-
vcenter = atomType.vcenter,
30+
botaccent = atomType.botAccentSymbol,
3331
}
3432

3533
return {

packages/math/base-elements.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,9 @@ function elements.stackbox:styleChildren ()
437437
for i = 1, #self.children - 1 do
438438
local v = self.children[i]
439439
local v2 = self.children[i + 1]
440+
-- Handle re-wrapped paired open/close symbols
441+
v = v.is_paired and v.children[#v.children] or v
442+
v2 = v2.is_paired and v2.children[1] or v2
440443
if spacingRules[v.atom] and spacingRules[v.atom][v2.atom] then
441444
local rule = spacingRules[v.atom][v2.atom]
442445
if rule.impossible then

packages/math/texlike.lua

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -281,8 +281,8 @@ local function isOperatorKind (tree, typeOfAtom)
281281
if tree.command ~= "mo" then
282282
return false
283283
end
284-
-- Case \mo[atom=big]{ops}
285-
-- E.g. \mo[atom=big]{lim}
284+
-- Case \mo[atom=xxx]{ops}
285+
-- E.g. \mo[atom=op]{lim}
286286
if tree.options and tree.options.atom == typeOfAtom then
287287
return true
288288
end
@@ -391,6 +391,7 @@ local function compileToMathML_aux (_, arg_env, tree)
391391
table.insert(stack, children)
392392
local mrow = {
393393
command = "mrow",
394+
is_paired = true, -- Internal flag to mark this re-wrapped mrow
394395
options = {},
395396
child,
396397
}
@@ -454,7 +455,7 @@ local function compileToMathML_aux (_, arg_env, tree)
454455
end
455456
tree.options = {}
456457
-- Translate TeX-like sub/superscripts to `munderover` or `msubsup`,
457-
-- depending on whether the base is a big operator
458+
-- depending on whether the base is an operator with moveable limits.
458459
elseif tree.id == "sup" and isMoveableLimits(tree[1]) then
459460
tree.command = "mover"
460461
elseif tree.id == "sub" and isMoveableLimits(tree[1]) then
@@ -628,20 +629,19 @@ compileToMathML(
628629
\def{dsi}{\mi[mathvariant=double-struck]{#1}}
629630
\def{vec}{\mover[accent=true]{#1}{\rightarrow}}
630631
631-
\def{lim}{\mo[movablelimits=true]{lim}}
632-
633632
% From amsmath:
634633
\def{to}{\mo[atom=bin]{→}}
635-
\def{gcd}{\mo[movablelimits=true]{gcd}}
636-
\def{sup}{\mo[movablelimits=true]{sup}}
637-
\def{inf}{\mo[movablelimits=true]{inf}}
638-
\def{max}{\mo[movablelimits=true]{max}}
639-
\def{min}{\mo[movablelimits=true]{min}}
634+
\def{lim}{\mo[atom=op, movablelimits=true]{lim}}
635+
\def{gcd}{\mo[atom=op, movablelimits=true]{gcd}}
636+
\def{sup}{\mo[atom=op, movablelimits=true]{sup}}
637+
\def{inf}{\mo[atom=op, movablelimits=true]{inf}}
638+
\def{max}{\mo[atom=op, movablelimits=true]{max}}
639+
\def{min}{\mo[atom=op, movablelimits=true]{min}}
640640
% Those use U+202F NARROW NO-BREAK SPACE in their names
641-
\def{limsup}{\mo[movablelimits=true]{lim sup}}
642-
\def{liminf}{\mo[movablelimits=true]{lim inf}}
643-
\def{projlim}{\mo[movablelimits=true]{proj lim}}
644-
\def{injlim}{\mo[movablelimits=true]{inj lim}}
641+
\def{limsup}{\mo[atom=op, movablelimits=true]{lim sup}}
642+
\def{liminf}{\mo[atom=op, movablelimits=true]{lim inf}}
643+
\def{projlim}{\mo[atom=op, movablelimits=true]{proj lim}}
644+
\def{injlim}{\mo[atom=op, movablelimits=true]{inj lim}}
645645
646646
% Standard spaces gleaned from plain TeX
647647
\def{thinspace}{\mspace[width=thin]}

packages/math/tools/unicode-xml-to-sile.xsl

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -49,26 +49,26 @@
4949
<xsl:choose>
5050
<xsl:when test="$combclass = '220'">botaccent</xsl:when>
5151
<xsl:when test="$combclass = '230'">accent</xsl:when>
52-
<xsl:otherwise>ord</xsl:otherwise><!-- assuming atomType.ordinary -->
52+
<xsl:otherwise>ord</xsl:otherwise><!-- assuming ordinary -->
5353
</xsl:choose>
5454
</xsl:when>
55-
<xsl:when test="$class = 'F'">ord</xsl:when><!-- Fence = mathfence = atomType.ordinary -->
56-
<xsl:when test="$class = 'G'">ord</xsl:when><!-- Glyph Part = assuming atomType.ordinary -->
55+
<xsl:when test="$class = 'F'">ord</xsl:when><!-- Fence = assiming ordinary -->
56+
<xsl:when test="$class = 'G'">ord</xsl:when><!-- Glyph Part = assuming ordinary -->
5757
<xsl:when test="$class = 'L'"><!-- Large -->
5858
<xsl:choose>
5959
<!-- SILE uses the atom for spacing currently (ignoring lspace and rspace) -->
60-
<!-- HACK: integral signs are NOTconsidered as big for spacing purpose -->
60+
<!-- HACK: integral signs are NOT considered as mathop for spacing purpose -->
6161
<xsl:when test="contains($description,'INTEGRAL')">ord</xsl:when>
62-
<xsl:otherwise>big</xsl:otherwise><!-- mathop = atomType.bigOperator -->
62+
<xsl:otherwise>op</xsl:otherwise><!-- mathop = atomType.bigOperator -->
6363
</xsl:choose>
6464
</xsl:when>
65-
<xsl:when test="$class = 'O'">open</xsl:when><!-- Opening = mathopen = atomType.openingSymbol -->
66-
<xsl:when test="$class = 'P'">punct</xsl:when><!-- Punctuation = mathpunct = atomType.punctuationSymbol -->
67-
<xsl:when test="$class = 'R'">rel</xsl:when><!-- Relation = mathrel = atomType.relationalOperator -->
68-
<xsl:when test="$class = 'S'">ord</xsl:when><!-- Space = assuming atomType.ordinary -->
69-
<xsl:when test="$class = 'U'">ord</xsl:when><!-- Unary = mathord = atomType.ordinary -->
70-
<xsl:when test="$class = 'V'">bin</xsl:when><!-- Vary = assume mathbin = atomType.binaryOperator -->
71-
<xsl:otherwise>ord</xsl:otherwise><!-- assuming atomType.ordinary if not specified -->
65+
<xsl:when test="$class = 'O'">open</xsl:when><!-- Opening -->
66+
<xsl:when test="$class = 'P'">punct</xsl:when><!-- Punctuation -->
67+
<xsl:when test="$class = 'R'">rel</xsl:when><!-- Relation -->
68+
<xsl:when test="$class = 'S'">ord</xsl:when><!-- Space = assuming ordinary -->
69+
<xsl:when test="$class = 'U'">ord</xsl:when><!-- Unary = assuming ordinary -->
70+
<xsl:when test="$class = 'V'">bin</xsl:when><!-- Vary = assume binary and let the logic decide later -->
71+
<xsl:otherwise>ord</xsl:otherwise><!-- assuming ordinary if not specified -->
7272
</xsl:choose>
7373
</xsl:template>
7474

@@ -129,8 +129,9 @@ local function addSymbol (str, shortatom, mathlatex, _, ops)
129129
op.forms = {}
130130
for _, v in pairs(ops) do
131131
if v.form then
132-
v.lspace = SILE.types.length(v.lspace and ("%smu"):format(v.lspace) or "0mu")
133-
v.rspace = SILE.types.length(v.rspace and ("%smu"):format(v.rspace) or "0mu")
132+
-- NOTE: At this point the mu unit is not yet defined, so keep it as a string.
133+
v.lspace = v.lspace and ("%smu"):format(v.lspace) or "0mu"
134+
v.rspace = v.rspace and ("%smu"):format(v.rspace) or "0mu"
134135
op.forms[v.form] = v
135136
else
136137
SU.warn("No form for operator " .. str .. " (operator dictionary is probably incomplete)")

packages/math/typesetter.lua

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,11 @@ function ConvertMathML (_, content)
113113
if content.command == "math" or content.command == "mathml" then -- toplevel
114114
return b.stackbox("H", convertChildren(content))
115115
elseif content.command == "mrow" then
116-
return b.stackbox("H", convertChildren(content))
116+
local ret = b.stackbox("H", convertChildren(content))
117+
-- Internal property to keep tracks or paired open/close in TeX-like syntax
118+
ret.is_paired = content.is_paired
119+
return ret
117120
elseif content.command == "mphantom" then
118-
-- MathML's standard mphantom corresponds to TeX's \phantom only.
119-
-- Let's support a special attribute "h" or "v" for TeX-like \hphantom or \vphantom.
120121
local special = content.options.special
121122
return b.phantom(convertChildren(content), special)
122123
elseif content.command == "mi" then

0 commit comments

Comments
 (0)