Skip to content

Commit 960dc3f

Browse files
OmikhleiaDidier Willis
authored andcommitted
feat(math): Support TeX-like left..right delimiter syntax
1 parent 72faad5 commit 960dc3f

2 files changed

Lines changed: 83 additions & 1 deletion

File tree

packages/math/init.lua

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,18 @@ To keep them small, you should put braces around the expression:
256256
\Vert v \Vert = \sqrt{x^2 + y^2} \text{ vs. } {\Vert v \Vert} = \sqrt{x^2 + y^2}
257257
\end{math}
258258
259+
Alternatively, you can use the \code{\\left} and \code{\\right} commands to automatically adjust the size of the delimiters to the inner content.
260+
Since SILE does it automatically for paired delimiters, it only really useful if you took a TeX formula using these commands and want to keep it as is, or if you want to use delimiters that are not paired in an obvious way.
261+
In this construct, the period is also supported for a null delimiter, as with TeX.
262+
263+
\begin[type=autodoc:codeblock]{raw}
264+
\left\rangle \frac{\zeta}{2} \right\langle \quad \left\}\frac{\zeta}{2} \right.
265+
\end{raw}
266+
267+
\begin[mode=display]{math}
268+
\left\rangle \frac{\zeta}{2} \right\langle \quad \left\}\frac{\zeta}{2} \right.
269+
\end{math}
270+
259271
\noindent To print a brace in a formula, you need to escape it with a backslash.
260272
261273
\paragraph{Token kinds}

packages/math/texlike.lua

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,21 @@ local mathGrammar = function (_ENV)
3030
digit^1 -- Integer (digits only, ex: 123)
3131
) / tostring
3232
local pos_natural = R("19") * digit^0 / tonumber
33+
34+
-- \left and \right delimiters = The TeXbook p. 148.
35+
-- Characters with a delcode in TeX: The TeXbook p. 341
36+
-- These are for use in \left...\right pairs.
37+
-- We add the period (null delimiter) from p. 149-150.
38+
-- We don't include the backslash here and handle it just after.
39+
local delcode = S"([</|)]>."
40+
-- Left/right is followed by a delimiter with delcode, or a command.
41+
-- We use the delcode or backslash as terminator: commands such as
42+
-- \rightarrow must still be allowed.
43+
local leftright = function (s) return P(s) * (delcode + P"\\") end
44+
3345
local ctrl_word = R("AZ", "az")^1
3446
local ctrl_symbol = P(1) - S"{}\\"
35-
local ctrl_sequence_name = C(ctrl_word + ctrl_symbol) / 1
47+
local ctrl_sequence_name = C(ctrl_word + ctrl_symbol) - leftright("left") - leftright("right") / 1
3648
local comment = (
3749
P"%" *
3850
P(1-eol)^0 *
@@ -56,7 +68,57 @@ local mathGrammar = function (_ENV)
5668
local group = P"{" * V"mathlist" * (P"}" + E("`}` expected"))
5769
-- Simple amsmath-like \text command (no embedded math)
5870
local textgroup = P"{" * C((1-P"}")^1) * (P"}" + E("`}` expected"))
71+
-- TeX \left...\right group
72+
local delim =
73+
-- Delimiter with delcode
74+
C(delcode) / function (d)
75+
if d ~= "." then
76+
return {
77+
id = "atom",
78+
d
79+
}
80+
end
81+
return nil
82+
end
83+
-- Delimiter as escaped \{ or \}
84+
+ P"\\" * C(S"{}") / function (d)
85+
return {
86+
id = "atom",
87+
d
88+
}
89+
end
90+
-- Delimiter as command ex. \langle
91+
+ P"\\" * C(ctrl_sequence_name) / 1 / function (cmd)
92+
return {
93+
id = "command",
94+
command = cmd
95+
}
96+
end
97+
98+
local leftrightgroup = P"\\left" * delim * V"mathlist" * P"\\right" * delim
99+
/ function (left, subformula, right)
100+
if not left and not right then
101+
-- No delimiters, return the subformula as-is
102+
return subformula
103+
end
104+
-- Rewrap the subformula in a flagged mathlist
105+
local mrow = {
106+
id = "mathlist",
107+
options = {},
108+
is_paired_explicit = true, -- Internal flag
109+
subformula
110+
}
111+
if left then
112+
table.insert(mrow, 1, left)
113+
end
114+
if right then
115+
table.insert(mrow, right)
116+
end
117+
return mrow
118+
end
119+
59120
local element_no_infix =
121+
leftrightgroup + -- Important: before command
60122
V"def" +
61123
V"text" + -- Important: before command
62124
V"command" +
@@ -412,6 +474,14 @@ local function compileToMathML_aux (_, arg_env, tree)
412474
else
413475
tree.command = "mrow"
414476
end
477+
elseif tree.is_paired_explicit then
478+
-- We already did the re-wrapping of open/close delimiters in the parser
479+
-- via \left...\right, doing it would not harm but would add an extra mrow,
480+
-- which we can avoid directly to keep the tree minimal.
481+
-- N.B. We could have used the same flag, but it's easier to debug this way.
482+
tree.is_paired = true
483+
tree.is_paired_explicit = nil
484+
tree.command = "mrow"
415485
else
416486
-- Re-wrap content from opening to closing operator in an implicit mrow,
417487
-- so stretchy operators apply to the correct span of content.

0 commit comments

Comments
 (0)