Skip to content

Commit

Permalink
fix(math): Improve spacing rules on limit-like operators
Browse files Browse the repository at this point in the history
Including another small refactor / clarification on our atom types.
  • Loading branch information
Omikhleia authored and alerque committed Nov 18, 2024
1 parent 56edc14 commit 781f62a
Show file tree
Hide file tree
Showing 6 changed files with 1,306 additions and 2,541 deletions.
16 changes: 7 additions & 9 deletions packages/math/atoms.lua
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
local atomType = {
ordinary = 0,
bigOperator = 1,
bigOperator = 1, -- Misnamed used for TeX's "mathop"
binaryOperator = 2,
relationalOperator = 3,
openingSymbol = 4,
closeSymbol = 5,
punctuationSymbol = 6,
inner = 7,
overSymbol = 8,
underSymbol = 9,
inner = 7, -- Unused for now (used for fractions in The TeXbook)
overSymbol = 8, -- Unused for now (used for overlines etc. in The TeXbook)
underSymbol = 9, -- Unused for now (used for underlines etc. in The TeXbook)
accentSymbol = 10,
radicalSymbol = 11,
vcenter = 12,
botAccentSymbol = 11, -- Unused for now but botaccent is encoded in our dictionary
}

-- Shorthands for atom types, used in the `atom` command option
-- and also in the unicode symbols table / operator dictionary
local atomTypeShort = {
ord = atomType.ordinary,
big = atomType.bigOperator,
op = atomType.bigOperator,
bin = atomType.binaryOperator,
rel = atomType.relationalOperator,
open = atomType.openingSymbol,
Expand All @@ -28,8 +27,7 @@ local atomTypeShort = {
over = atomType.overSymbol,
under = atomType.underSymbol,
accent = atomType.accentSymbol,
radical = atomType.radicalSymbol,
vcenter = atomType.vcenter,
botaccent = atomType.botAccentSymbol,
}

return {
Expand Down
3 changes: 3 additions & 0 deletions packages/math/base-elements.lua
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,9 @@ function elements.stackbox:styleChildren ()
for i = 1, #self.children - 1 do
local v = self.children[i]
local v2 = self.children[i + 1]
-- Handle re-wrapped paired open/close symbols
v = v.is_paired and v.children[#v.children] or v
v2 = v2.is_paired and v2.children[1] or v2
if spacingRules[v.atom] and spacingRules[v.atom][v2.atom] then
local rule = spacingRules[v.atom][v2.atom]
if rule.impossible then
Expand Down
28 changes: 14 additions & 14 deletions packages/math/texlike.lua
Original file line number Diff line number Diff line change
Expand Up @@ -281,8 +281,8 @@ local function isOperatorKind (tree, typeOfAtom)
if tree.command ~= "mo" then
return false
end
-- Case \mo[atom=big]{ops}
-- E.g. \mo[atom=big]{lim}
-- Case \mo[atom=xxx]{ops}
-- E.g. \mo[atom=op]{lim}
if tree.options and tree.options.atom == typeOfAtom then
return true
end
Expand Down Expand Up @@ -391,6 +391,7 @@ local function compileToMathML_aux (_, arg_env, tree)
table.insert(stack, children)
local mrow = {
command = "mrow",
is_paired = true, -- Internal flag to mark this re-wrapped mrow
options = {},
child,
}
Expand Down Expand Up @@ -454,7 +455,7 @@ local function compileToMathML_aux (_, arg_env, tree)
end
tree.options = {}
-- Translate TeX-like sub/superscripts to `munderover` or `msubsup`,
-- depending on whether the base is a big operator
-- depending on whether the base is an operator with moveable limits.
elseif tree.id == "sup" and isMoveableLimits(tree[1]) then
tree.command = "mover"
elseif tree.id == "sub" and isMoveableLimits(tree[1]) then
Expand Down Expand Up @@ -628,20 +629,19 @@ compileToMathML(
\def{dsi}{\mi[mathvariant=double-struck]{#1}}
\def{vec}{\mover[accent=true]{#1}{\rightarrow}}
\def{lim}{\mo[movablelimits=true]{lim}}
% From amsmath:
\def{to}{\mo[atom=bin]{→}}
\def{gcd}{\mo[movablelimits=true]{gcd}}
\def{sup}{\mo[movablelimits=true]{sup}}
\def{inf}{\mo[movablelimits=true]{inf}}
\def{max}{\mo[movablelimits=true]{max}}
\def{min}{\mo[movablelimits=true]{min}}
\def{lim}{\mo[atom=op, movablelimits=true]{lim}}
\def{gcd}{\mo[atom=op, movablelimits=true]{gcd}}
\def{sup}{\mo[atom=op, movablelimits=true]{sup}}
\def{inf}{\mo[atom=op, movablelimits=true]{inf}}
\def{max}{\mo[atom=op, movablelimits=true]{max}}
\def{min}{\mo[atom=op, movablelimits=true]{min}}
% Those use U+202F NARROW NO-BREAK SPACE in their names
\def{limsup}{\mo[movablelimits=true]{lim sup}}
\def{liminf}{\mo[movablelimits=true]{lim inf}}
\def{projlim}{\mo[movablelimits=true]{proj lim}}
\def{injlim}{\mo[movablelimits=true]{inj lim}}
\def{limsup}{\mo[atom=op, movablelimits=true]{lim sup}}
\def{liminf}{\mo[atom=op, movablelimits=true]{lim inf}}
\def{projlim}{\mo[atom=op, movablelimits=true]{proj lim}}
\def{injlim}{\mo[atom=op, movablelimits=true]{inj lim}}
% Standard spaces gleaned from plain TeX
\def{thinspace}{\mspace[width=thin]}
Expand Down
29 changes: 15 additions & 14 deletions packages/math/tools/unicode-xml-to-sile.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -49,26 +49,26 @@
<xsl:choose>
<xsl:when test="$combclass = '220'">botaccent</xsl:when>
<xsl:when test="$combclass = '230'">accent</xsl:when>
<xsl:otherwise>ord</xsl:otherwise><!-- assuming atomType.ordinary -->
<xsl:otherwise>ord</xsl:otherwise><!-- assuming ordinary -->
</xsl:choose>
</xsl:when>
<xsl:when test="$class = 'F'">ord</xsl:when><!-- Fence = mathfence = atomType.ordinary -->
<xsl:when test="$class = 'G'">ord</xsl:when><!-- Glyph Part = assuming atomType.ordinary -->
<xsl:when test="$class = 'F'">ord</xsl:when><!-- Fence = assiming ordinary -->
<xsl:when test="$class = 'G'">ord</xsl:when><!-- Glyph Part = assuming ordinary -->
<xsl:when test="$class = 'L'"><!-- Large -->
<xsl:choose>
<!-- SILE uses the atom for spacing currently (ignoring lspace and rspace) -->
<!-- HACK: integral signs are NOTconsidered as big for spacing purpose -->
<!-- HACK: integral signs are NOT considered as mathop for spacing purpose -->
<xsl:when test="contains($description,'INTEGRAL')">ord</xsl:when>
<xsl:otherwise>big</xsl:otherwise><!-- mathop = atomType.bigOperator -->
<xsl:otherwise>op</xsl:otherwise><!-- mathop = atomType.bigOperator -->
</xsl:choose>
</xsl:when>
<xsl:when test="$class = 'O'">open</xsl:when><!-- Opening = mathopen = atomType.openingSymbol -->
<xsl:when test="$class = 'P'">punct</xsl:when><!-- Punctuation = mathpunct = atomType.punctuationSymbol -->
<xsl:when test="$class = 'R'">rel</xsl:when><!-- Relation = mathrel = atomType.relationalOperator -->
<xsl:when test="$class = 'S'">ord</xsl:when><!-- Space = assuming atomType.ordinary -->
<xsl:when test="$class = 'U'">ord</xsl:when><!-- Unary = mathord = atomType.ordinary -->
<xsl:when test="$class = 'V'">bin</xsl:when><!-- Vary = assume mathbin = atomType.binaryOperator -->
<xsl:otherwise>ord</xsl:otherwise><!-- assuming atomType.ordinary if not specified -->
<xsl:when test="$class = 'O'">open</xsl:when><!-- Opening -->
<xsl:when test="$class = 'P'">punct</xsl:when><!-- Punctuation -->
<xsl:when test="$class = 'R'">rel</xsl:when><!-- Relation -->
<xsl:when test="$class = 'S'">ord</xsl:when><!-- Space = assuming ordinary -->
<xsl:when test="$class = 'U'">ord</xsl:when><!-- Unary = assuming ordinary -->
<xsl:when test="$class = 'V'">bin</xsl:when><!-- Vary = assume binary and let the logic decide later -->
<xsl:otherwise>ord</xsl:otherwise><!-- assuming ordinary if not specified -->
</xsl:choose>
</xsl:template>

Expand Down Expand Up @@ -129,8 +129,9 @@ local function addSymbol (str, shortatom, mathlatex, _, ops)
op.forms = {}
for _, v in pairs(ops) do
if v.form then
v.lspace = SILE.types.length(v.lspace and ("%smu"):format(v.lspace) or "0mu")
v.rspace = SILE.types.length(v.rspace and ("%smu"):format(v.rspace) or "0mu")
-- NOTE: At this point the mu unit is not yet defined, so keep it as a string.
v.lspace = v.lspace and ("%smu"):format(v.lspace) or "0mu"
v.rspace = v.rspace and ("%smu"):format(v.rspace) or "0mu"
op.forms[v.form] = v
else
SU.warn("No form for operator " .. str .. " (operator dictionary is probably incomplete)")
Expand Down
7 changes: 4 additions & 3 deletions packages/math/typesetter.lua
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,11 @@ function ConvertMathML (_, content)
if content.command == "math" or content.command == "mathml" then -- toplevel
return b.stackbox("H", convertChildren(content))
elseif content.command == "mrow" then
return b.stackbox("H", convertChildren(content))
local ret = b.stackbox("H", convertChildren(content))
-- Internal property to keep tracks or paired open/close in TeX-like syntax
ret.is_paired = content.is_paired
return ret
elseif content.command == "mphantom" then
-- MathML's standard mphantom corresponds to TeX's \phantom only.
-- Let's support a special attribute "h" or "v" for TeX-like \hphantom or \vphantom.
local special = content.options.special
return b.phantom(convertChildren(content), special)
elseif content.command == "mi" then
Expand Down
Loading

0 comments on commit 781f62a

Please sign in to comment.